Compare commits

...

15 Commits

Author SHA1 Message Date
8a47838d94 EntryProvider: Support broken .desktop files that don't start with [Desktop Entry] 2022-07-15 18:26:02 +02:00
89c9cc573e Add more reasonable screenshots 2021-10-30 16:22:43 +02:00
7c15d7b145 Window: executeConfig: Fix after refactoring for Qt 5.15
Refactoring for 5.15 broke this (d9b0be5063),
so fix it.
2021-05-08 20:54:19 +02:00
d9b0be5063 Fix some Qt deprication warnings 2021-04-29 09:33:57 +02:00
f1b17c0842 Ranking: Prioritizee name before command/binary name
This seems to work better for flatpak apps and should
argueably be more natural.
2021-04-29 09:33:31 +02:00
4315de0e76 EntryPushButton: Hardcode max 256x256 icon size for now
While dirty, in practise, this appears to be working more fine than selecting
the max size. Some of those were too big.

Relevant: #32.
2021-03-19 12:00:40 +01:00
f0387db468 Add a basic ranking of system entries and show most relevant first 2021-03-16 22:29:26 +01:00
6e4f0ecabf Window: filterGridFor: Search button Command too 2021-03-05 20:51:14 +01:00
2df37212dd EntryPushButton: Don't allow moving dynamic buttons 2020-10-19 21:45:33 +02:00
b13815a924 executeConfig: Open terminal if Shift pressed 2020-10-19 21:45:33 +02:00
bb74d156c9 Introduce new button type 'DYNAMIC' for PATH suggestions
Fixes #23
2020-10-19 21:45:33 +02:00
f9038f3098 EntryProvider: Don't use 'new' when throwing exceptions 2020-10-19 21:45:18 +02:00
042b53a0be Window: addToFavourites(): Discard shortcut from system entries 2020-10-19 21:36:59 +02:00
64d3223fb2 EntryProvider: saveUserEntry: Only save key field if it's not empty 2020-10-19 21:30:11 +02:00
adfb065358 Window: keyPressEvent: don't show empty shortcuts 2020-10-10 22:25:40 +02:00
9 changed files with 98 additions and 56 deletions

View File

@ -25,20 +25,22 @@ EntryConfig EntryProvider::readFromDesktopFile(const QString &path)
if(!file.open(QIODevice::ReadOnly | QIODevice::Text)) if(!file.open(QIODevice::ReadOnly | QIODevice::Text))
{ {
// TODO: better exception class // TODO: better exception class
throw new std::runtime_error("Failed to open file"); throw std::runtime_error("Failed to open file");
} }
QTextStream stream(&file); QTextStream stream(&file);
// There should be nothing preceding this group in the desktop entry file but possibly one or more comments. // There should be nothing preceding this group in the desktop entry file but possibly one or more comments.
// https://standards.freedesktop.org/desktop-entry-spec/latest/ar01s03.html#group-header // https://standards.freedesktop.org/desktop-entry-spec/latest/ar01s03.html#group-header
QString firstLine; // Ignore that as there some that violate that in the wild
const QString startSection = "[Desktop Entry]";
QString line;
do do
{ {
firstLine = stream.readLine().trimmed(); line = stream.readLine().trimmed();
} while(!stream.atEnd() && (firstLine.isEmpty() || firstLine[0] == '#')); } while(!stream.atEnd() && line != startSection);
if(firstLine != "[Desktop Entry]") if(line != startSection)
{ {
throw ConfigFormatException(".desktop file does not start with [Desktop Entry]: " + path.toStdString()); throw ConfigFormatException(".desktop file does not contain [Desktop Entry] section: " + path.toStdString());
} }
while(!stream.atEnd()) while(!stream.atEnd())
@ -120,7 +122,7 @@ EntryConfig EntryProvider::readqsrunFile(const QString &path)
if(!file.open(QIODevice::ReadOnly | QIODevice::Text)) if(!file.open(QIODevice::ReadOnly | QIODevice::Text))
{ {
// TODO: better exception class // TODO: better exception class
throw new std::runtime_error("Failed to open file"); throw std::runtime_error("Failed to open file");
} }
QHash<QString, QString> map; QHash<QString, QString> map;
QTextStream stream(&file); QTextStream stream(&file);
@ -131,7 +133,7 @@ EntryConfig EntryProvider::readqsrunFile(const QString &path)
int spacePos = line.indexOf(' '); int spacePos = line.indexOf(' ');
if(spacePos == -1) if(spacePos == -1)
{ {
throw new ConfigFormatException("misformated line in .qsrun config file " + path.toStdString()); throw ConfigFormatException("misformated line in .qsrun config file " + path.toStdString());
} }
QString key = line.mid(0, spacePos); QString key = line.mid(0, spacePos);
@ -139,7 +141,7 @@ EntryConfig EntryProvider::readqsrunFile(const QString &path)
if(key == "" || value == "") if(key == "" || value == "")
{ {
throw new ConfigFormatException("empty key or value in .qsrun config file " + path.toStdString()); throw ConfigFormatException("empty key or value in .qsrun config file " + path.toStdString());
} }
map[key] = value; map[key] = value;
} }
@ -154,7 +156,7 @@ EntryConfig EntryProvider::readqsrunFile(const QString &path)
} }
else else
{ {
throw new ConfigFormatException("Error attempting to read inherited entry"); throw ConfigFormatException("Error attempting to read inherited entry");
} }
} }
QString type = map["type"]; QString type = map["type"];
@ -162,7 +164,7 @@ EntryConfig EntryProvider::readqsrunFile(const QString &path)
{ {
if(type == "system") if(type == "system")
{ {
throw new ConfigFormatException(".qsrun files cannot be designated as system entries " + throw ConfigFormatException(".qsrun files cannot be designated as system entries " +
path.toStdString()); path.toStdString());
} }
else if(type == "inherit") else if(type == "inherit")
@ -175,7 +177,7 @@ EntryConfig EntryProvider::readqsrunFile(const QString &path)
} }
else else
{ {
throw new ConfigFormatException("Invalid value for type provided in file: " + path.toStdString()); throw ConfigFormatException("Invalid value for type provided in file: " + path.toStdString());
} }
} }
else else
@ -301,32 +303,35 @@ void EntryProvider::saveUserEntry(const EntryConfig &config)
throw std::runtime_error("Error: Can not open file for writing"); throw std::runtime_error("Error: Can not open file for writing");
} }
QTextStream outStream(&file); QTextStream outStream(&file);
outStream << "type" << " " << ((config.type == EntryType::USER) ? "user" : "inherit") << endl; outStream << "type" << " " << ((config.type == EntryType::USER) ? "user" : "inherit") << Qt::endl;
if(!config.inherit.isEmpty()) if(!config.inherit.isEmpty())
{ {
outStream << "inherit" << " " << config.inherit << endl; outStream << "inherit" << " " << config.inherit << Qt::endl;
}
outStream << "row" << " " << config.row << Qt::endl;
outStream << "col" << " " << config.col << Qt::endl;
outStream << "hidden" << " " << config.hidden << Qt::endl;
if(!config.key.isEmpty())
{
outStream << "key" << " " << config.key << Qt::endl;
} }
outStream << "row" << " " << config.row << endl;
outStream << "col" << " " << config.col << endl;
outStream << "hidden" << " " << config.hidden << endl;
outStream << "key" << " " << config.key << endl;
if(config.type == EntryType::USER) if(config.type == EntryType::USER)
{ {
if(!config.name.isEmpty()) if(!config.name.isEmpty())
{ {
outStream << "name" << " " << config.name << endl; outStream << "name" << " " << config.name << Qt::endl;
} }
if(!config.command.isEmpty()) if(!config.command.isEmpty())
{ {
outStream << "command" << " " << config.command << endl; outStream << "command" << " " << config.command << Qt::endl;
} }
if(!config.iconPath.isEmpty()) if(!config.iconPath.isEmpty())
{ {
outStream << "icon" << " " << config.iconPath << endl; outStream << "icon" << " " << config.iconPath << Qt::endl;
} }
if(!config.arguments.empty()) if(!config.arguments.empty())
{ {
outStream << "arguments" << " " << config.arguments.join(' ') << endl; outStream << "arguments" << " " << config.arguments.join(' ') << Qt::endl;
} }
} }

View File

@ -19,7 +19,8 @@ enum EntryType
{ {
USER, USER,
INHERIT, INHERIT,
SYSTEM SYSTEM,
DYNAMIC
}; };
class EntryConfig class EntryConfig

View File

@ -33,19 +33,7 @@ EntryPushButton::EntryPushButton(const EntryConfig &config) : QPushButton()
icon = resolveIcon(config.iconPath); icon = resolveIcon(config.iconPath);
} }
this->setIcon(icon); this->setIcon(icon);
if(!icon.availableSizes().isEmpty()) this->setIconSize(QSize{256, 256});
{
auto sizes = icon.availableSizes();
QSize maxSize = sizes.first();
for(QSize &current : sizes)
{
if(current.width() > maxSize.width())
{
maxSize = current;
}
}
this->setIconSize(maxSize);
}
this->config = config; this->config = config;
connect(this, SIGNAL(clicked()), this, SLOT(emitOwnClicked())); connect(this, SIGNAL(clicked()), this, SLOT(emitOwnClicked()));
@ -106,7 +94,7 @@ void EntryPushButton::mousePressEvent(QMouseEvent *event)
{ {
this->userEntryMenu.exec(QCursor::pos()); this->userEntryMenu.exec(QCursor::pos());
} }
else else if(this->config.type == EntryType::SYSTEM)
{ {
this->systemEntryMenu.exec(QCursor::pos()); this->systemEntryMenu.exec(QCursor::pos());
} }
@ -116,7 +104,7 @@ void EntryPushButton::mousePressEvent(QMouseEvent *event)
void EntryPushButton::mouseMoveEvent(QMouseEvent *event) void EntryPushButton::mouseMoveEvent(QMouseEvent *event)
{ {
if(this->config.type == EntryType::SYSTEM) if(this->config.type == EntryType::SYSTEM || this->config.type == EntryType::DYNAMIC)
{ {
return; return;
} }

BIN
screenshots/calc.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

BIN
screenshots/startview.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 KiB

View File

@ -120,11 +120,12 @@ void Window::populateGrid(const QVector<EntryPushButton *> &list)
void Window::executeConfig(const EntryConfig &config) void Window::executeConfig(const EntryConfig &config)
{ {
if(config.isTerminalCommand) if(config.isTerminalCommand || QApplication::keyboardModifiers().testFlag(Qt::ShiftModifier))
{ {
QString cmd = settingsProvider->getTerminalCommand(); QString cmd = settingsProvider->getTerminalCommand();
cmd.replace("%c", config.command); cmd.replace("%c", config.command);
QProcess::startDetached(cmd); QStringList args = QProcess::splitCommand(cmd);
QProcess::startDetached(args[0], args);
} }
else else
{ {
@ -166,6 +167,7 @@ void Window::addToFavourites(const EntryConfig &config)
when we add it to the favourites. the alternative would be to reload the whole config, when we add it to the favourites. the alternative would be to reload the whole config,
but that's probably overkill. */ but that's probably overkill. */
userConfig.update(config); userConfig.update(config);
userConfig.key = "";
userEntryButtons.append(createEntryButton(userConfig)); userEntryButtons.append(createEntryButton(userConfig));
} }
@ -265,6 +267,7 @@ void Window::addPATHSuggestion(const QString &text)
e.row = 0; e.row = 0;
e.command = suggestions[0]; e.command = suggestions[0];
e.iconPath = suggestions[0]; e.iconPath = suggestions[0];
e.type = EntryType::DYNAMIC;
EntryPushButton *button = createEntryButton(e); EntryPushButton *button = createEntryButton(e);
clearGrid(); clearGrid();
grid->addWidget(button, 0, 0); grid->addWidget(button, 0, 0);
@ -342,6 +345,7 @@ void Window::lineEditTextChanged(QString text)
} }
e.command = arguments[0]; e.command = arguments[0];
e.iconPath = "utilities-terminal"; e.iconPath = "utilities-terminal";
e.type = EntryType::DYNAMIC;
EntryPushButton *button = createEntryButton(e); EntryPushButton *button = createEntryButton(e);
clearGrid(); clearGrid();
@ -381,7 +385,10 @@ void Window::keyPressEvent(QKeyEvent *event)
for(EntryPushButton *button : buttonsInGrid) for(EntryPushButton *button : buttonsInGrid)
{ {
button->showShortcut(); if(!button->getEntryConfig().key.isEmpty())
{
button->showShortcut();
}
} }
QKeySequence seq(event->key()); QKeySequence seq(event->key());
@ -397,6 +404,27 @@ void Window::keyPressEvent(QKeyEvent *event)
QWidget::keyPressEvent(event); QWidget::keyPressEvent(event);
} }
int Window::rankConfig(const EntryConfig &config, QString filter) const
{
if(config.name.startsWith(filter, Qt::CaseInsensitive))
{
return 0;
}
else if(config.command.startsWith(filter, Qt::CaseInsensitive))
{
return 1;
}
else if(config.name.contains(filter, Qt::CaseInsensitive))
{
return 2;
}
else if(config.command.contains(filter, Qt::CaseInsensitive))
{
return 3;
}
return -1;
}
void Window::filterGridFor(QString filter) void Window::filterGridFor(QString filter)
{ {
if(filter.length() > 0) if(filter.length() > 0)
@ -405,7 +433,8 @@ void Window::filterGridFor(QString filter)
bool userEntryMatch = false; bool userEntryMatch = false;
for(EntryPushButton *button : this->userEntryButtons) for(EntryPushButton *button : this->userEntryButtons)
{ {
if(button->getName().contains(filter, Qt::CaseInsensitive)) if(button->getName().contains(filter, Qt::CaseInsensitive) ||
button->getCommand().contains(filter, Qt::CaseInsensitive))
{ {
button->setVisible(true); button->setVisible(true);
grid->addWidget(button, button->getRow(), button->getCol()); grid->addWidget(button, button->getRow(), button->getCol());
@ -415,26 +444,38 @@ void Window::filterGridFor(QString filter)
} }
if(!userEntryMatch) if(!userEntryMatch)
{ {
QVector<RankedButton> rankedEntries;
int currow = 0; int currow = 0;
int curcol = 0; int curcol = 0;
int i = 1; int i = 1;
const int MAX_COLS = this->settingsProvider->getMaxCols(); const int MAX_COLS = this->settingsProvider->getMaxCols();
for(EntryPushButton *button : this->systemEntryButtons) for(EntryPushButton *button : this->systemEntryButtons)
{ {
if(button->getName().contains(filter, Qt::CaseInsensitive)) int ranking = rankConfig(button->getEntryConfig(), filter);
if(ranking > -1)
{ {
button->setVisible(true); RankedButton rb;
if(i < 10) rb.button = button;
{ rb.ranking = ranking;
button->setShortcutKey(QString::number(i++)); rankedEntries.append(rb);
} }
grid->addWidget(button, currow, curcol++); }
this->buttonsInGrid.append(button); std::sort(rankedEntries.begin(), rankedEntries.end(),
if(curcol == MAX_COLS) [](const RankedButton &a, const RankedButton &b) -> bool { return a.ranking < b.ranking; });
{ for(RankedButton &rankedButton : rankedEntries)
curcol = 0; {
++currow; EntryPushButton *button = rankedButton.button;
} button->setVisible(true);
if(i < 10)
{
button->setShortcutKey(QString::number(i++));
}
grid->addWidget(button, currow, curcol++);
this->buttonsInGrid.append(button);
if(curcol == MAX_COLS)
{
curcol = 0;
++currow;
} }
} }
} }

View File

@ -37,6 +37,13 @@
#include "calculationengine.h" #include "calculationengine.h"
#include "settingsprovider.h" #include "settingsprovider.h"
class RankedButton
{
public:
EntryPushButton *button = nullptr;
int ranking;
};
class Window : public QWidget class Window : public QWidget
{ {
Q_OBJECT Q_OBJECT
@ -77,7 +84,7 @@ class Window : public QWidget
QStringList generatePATHSuggestions(const QString &text); QStringList generatePATHSuggestions(const QString &text);
void closeWindow(); void closeWindow();
std::pair<int, int> getNextFreeCell(); std::pair<int, int> getNextFreeCell();
int rankConfig(const EntryConfig &config, QString filter) const;
private slots: private slots:
void lineEditReturnPressed(); void lineEditReturnPressed();
void showCalculationResultContextMenu(const QPoint &point); void showCalculationResultContextMenu(const QPoint &point);