Filtering and launching

This commit is contained in:
Javier Feliz 2025-08-28 13:02:00 -04:00
parent 81f9b5e32c
commit d725a54bd8
3 changed files with 84 additions and 5 deletions

View File

@ -1,5 +1,7 @@
#include "AppListModel.hpp"
#include <QDebug>
#include <QProcess>
#include <QRegularExpression>
AppListModel::AppListModel(QObject *parent)
: QAbstractListModel(parent)
@ -10,15 +12,16 @@ AppListModel::AppListModel(QObject *parent)
int AppListModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return m_apps ? static_cast<int>(m_apps->size()) : 0;
return static_cast<int>(m_filteredIndexes.size());
}
QVariant AppListModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid() || !m_apps || index.row() >= static_cast<int>(m_apps->size()))
if (!index.isValid() || !m_apps || index.row() >= static_cast<int>(m_filteredIndexes.size()))
return QVariant();
const dmenu::DesktopEntry &app = (*m_apps)[index.row()];
int appIndex = m_filteredIndexes[index.row()];
const dmenu::DesktopEntry &app = (*m_apps)[appIndex];
switch (role) {
case NameRole:
@ -45,5 +48,61 @@ void AppListModel::loadApps()
{
beginResetModel();
m_apps = dmenu::get_dmenu_app_data();
updateFilteredApps();
endResetModel();
}
void AppListModel::launchApp(int index)
{
if (!m_apps || index < 0 || index >= static_cast<int>(m_filteredIndexes.size()))
return;
int appIndex = m_filteredIndexes[index];
const dmenu::DesktopEntry &app = (*m_apps)[appIndex];
// Parse exec command (remove %f, %u, %F, %U field codes if present)
QString command = QString::fromStdString(app.exec);
QRegularExpression fieldCodes("%[fuFU]");
command = command.replace(fieldCodes, "").trimmed();
qDebug() << "Launching:" << command;
// Use nohup and redirect output to /dev/null for proper detachment
QString detachedCommand = QString("nohup %1 >/dev/null 2>&1 &").arg(command);
QProcess::startDetached("/bin/sh", QStringList() << "-c" << detachedCommand);
}
QString AppListModel::searchText() const
{
return m_searchText;
}
void AppListModel::setSearchText(const QString &searchText)
{
if (m_searchText == searchText)
return;
m_searchText = searchText;
emit searchTextChanged();
beginResetModel();
updateFilteredApps();
endResetModel();
}
void AppListModel::updateFilteredApps()
{
m_filteredIndexes.clear();
if (!m_apps)
return;
for (size_t i = 0; i < m_apps->size(); ++i) {
const dmenu::DesktopEntry &app = (*m_apps)[i];
if (m_searchText.isEmpty() ||
QString::fromStdString(app.name).contains(m_searchText, Qt::CaseInsensitive)) {
m_filteredIndexes.push_back(static_cast<int>(i));
}
}
}

View File

@ -10,6 +10,7 @@
class AppListModel : public QAbstractListModel
{
Q_OBJECT
Q_PROPERTY(QString searchText READ searchText WRITE setSearchText NOTIFY searchTextChanged)
public:
enum AppRoles {
@ -25,7 +26,18 @@ public:
QHash<int, QByteArray> roleNames() const override;
Q_INVOKABLE void loadApps();
Q_INVOKABLE void launchApp(int index);
QString searchText() const;
void setSearchText(const QString &searchText);
signals:
void searchTextChanged();
private:
void updateFilteredApps();
dmenu::DEVec m_apps;
std::vector<int> m_filteredIndexes;
QString m_searchText;
};

View File

@ -40,12 +40,19 @@ ApplicationWindow {
placeholderText: "Type to search applications..."
selectByMouse: true
focus: true
text: appModel.searchText
onTextChanged: {
appModel.searchText = text
listView.currentIndex = 0
}
Keys.onDownPressed: listView.incrementCurrentIndex()
Keys.onUpPressed: listView.decrementCurrentIndex()
Keys.onReturnPressed: {
if (listView.currentItem) {
console.log("Selected:", appModel.data(appModel.index(listView.currentIndex, 0), Qt.UserRole + 2))
appModel.launchApp(listView.currentIndex)
Qt.quit()
}
}
}
@ -105,7 +112,8 @@ ApplicationWindow {
onClicked: {
listView.currentIndex = index
console.log("Clicked:", model.exec)
appModel.launchApp(index)
Qt.quit()
}
}
}