Skip to content

Commit d5218cf

Browse files
committed
Saving & reading app list, use of paths / names for icon
1 parent 035f2c1 commit d5218cf

File tree

12 files changed

+508
-64
lines changed

12 files changed

+508
-64
lines changed

Include/RunAsGPU/Shared/Shared.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,6 @@
55

66
std::filesystem::path GetExecutablePath();
77

8+
std::string TrimString(const std::string& s);
9+
810
#endif //RUNASGPU_SHARED_HPP
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[
2+
{
3+
"name": "AppName",
4+
"desc": "Description here",
5+
"exec": "/usr/bin/false",
6+
"icon": null
7+
},
8+
{
9+
"name": "Firefox",
10+
"desc": "Web Browser",
11+
"exec": "/usr/bin/firefox",
12+
"icon": "firefox"
13+
}
14+
]

Sources/AppGUI/UI/AppSelector.cpp

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
#include "AppSelector.hpp"
2+
3+
#include "Model/AppListModel.hpp"
4+
#include "Model/AppListDelegate.hpp"
5+
6+
#include <cstdlib>
7+
#include <filesystem>
8+
#include <fstream>
9+
#include <sstream>
10+
#include <iostream>
11+
#include <algorithm>
12+
13+
#include <RunAsGPU/Shared/Shared.hpp>
14+
15+
#include <QFileInfo>
16+
#include <QDir>
17+
#include <QLineEdit>
18+
#include <QSortFilterProxyModel>
19+
#include <QStandardItemModel>
20+
21+
namespace fs = std::filesystem;
22+
23+
std::vector<Application> ListInstalledApps() {
24+
std::vector<Application> appList;
25+
std::vector<std::string> searchPaths;
26+
27+
const char *xdgDirs = getenv("XDG_DATA_DIRS");
28+
if (xdgDirs) {
29+
std::istringstream ss(xdgDirs);
30+
std::string path;
31+
32+
while (std::getline(ss, path, ':'))
33+
searchPaths.push_back(path + "/applications");
34+
} else
35+
searchPaths = {"/usr/share/applications", "/usr/local/share/applications"};
36+
37+
const char *homeDir = getenv("HOME");
38+
if (homeDir)
39+
searchPaths.push_back(std::string(homeDir) + "/.local/share/applications");
40+
41+
for (const auto &path: searchPaths) {
42+
if (!fs::exists(path))
43+
continue;
44+
45+
if (!fs::is_directory(path))
46+
continue;
47+
48+
for (const auto &entry: fs::directory_iterator(path)) {
49+
if (entry.path().extension() != ".desktop")
50+
continue;
51+
52+
QString name, description, execPath, iconStr;
53+
std::ifstream file(entry.path());
54+
if (!file.is_open())
55+
continue;
56+
57+
std::map<std::string, std::string> desktopEntries;
58+
std::string line;
59+
60+
bool inDesktopEntry = false;
61+
while (std::getline(file, line)) {
62+
line = TrimString(line);
63+
64+
if (line.empty())
65+
continue;
66+
67+
if (line.front() == '[' && line.back() == ']') {
68+
inDesktopEntry = true;
69+
continue;
70+
}
71+
72+
if (inDesktopEntry) {
73+
size_t pos = line.find('=');
74+
if (pos == std::string::npos)
75+
continue;
76+
77+
std::string key = line.substr(0, pos);
78+
std::string value = line.substr(pos + 1);
79+
desktopEntries[key] = value;
80+
}
81+
}
82+
83+
if (desktopEntries.find("Name") != desktopEntries.end())
84+
name = QString::fromStdString(desktopEntries["Name"]);
85+
if (desktopEntries.find("Comment") != desktopEntries.end())
86+
description = QString::fromStdString(desktopEntries["Comment"]);
87+
if (desktopEntries.find("Exec") != desktopEntries.end())
88+
execPath = QString::fromStdString(desktopEntries["Exec"]);
89+
if (desktopEntries.find("Icon") != desktopEntries.end())
90+
iconStr = QString::fromStdString(desktopEntries["Icon"]);
91+
92+
Application app{name, description, execPath, iconStr};
93+
appList.push_back(app);
94+
}
95+
}
96+
97+
return appList;
98+
}
99+
100+
std::vector<Application> appList,
101+
filteredApps;
102+
103+
std::vector<QString> qSelectedApps;
104+
105+
std::vector<Application> Ui::selectedItems;
106+
107+
AppListModel *modelAppList;
108+
AppListDelegate *delegateAppList;
109+
110+
void Ui_AppSelector::performLogic() const {
111+
modelAppList = new AppListModel(listInstalledApps);
112+
delegateAppList = new AppListDelegate(listInstalledApps);
113+
114+
self->setFixedSize(self->size());
115+
116+
listInstalledApps->setModel(modelAppList);
117+
listInstalledApps->setItemDelegate(delegateAppList);
118+
listInstalledApps->setViewMode(QListView::ListMode);
119+
listInstalledApps->setUniformItemSizes(true);
120+
listInstalledApps->setSpacing(5);
121+
listInstalledApps->setSelectionMode(QAbstractItemView::MultiSelection);
122+
listInstalledApps->setEditTriggers(QAbstractItemView::NoEditTriggers);
123+
listInstalledApps->setFocusPolicy(Qt::StrongFocus);
124+
125+
appList = ListInstalledApps();
126+
std::sort(appList.begin(), appList.end(), [](const Application &a, const Application &b) {
127+
return a.name.compare(b.name, Qt::CaseInsensitive) < 0;
128+
});
129+
130+
for (const auto &app: appList) {
131+
if (app.execPath.isEmpty())
132+
continue;
133+
134+
modelAppList->addItem(app);
135+
}
136+
137+
QObject::connect(listInstalledApps->selectionModel(), &QItemSelectionModel::selectionChanged, self,
138+
[&](const QItemSelection &selected, const QItemSelection &deselected) {
139+
for (const QModelIndex &index: selected.indexes()) {
140+
QString selectedName = modelAppList->data(index, Qt::DisplayRole).toString();
141+
142+
auto it = std::find_if(qSelectedApps.begin(), qSelectedApps.end(),
143+
[&](const QString &appName) {
144+
return appName == selectedName;
145+
});
146+
147+
if (it == qSelectedApps.end()) {
148+
auto appIt = std::find_if(appList.begin(), appList.end(), [&](const Application &app) {
149+
return app.name == selectedName;
150+
});
151+
152+
if (appIt != appList.end())
153+
qSelectedApps.push_back(selectedName);
154+
}
155+
}
156+
157+
for (const QModelIndex &index: deselected.indexes()) {
158+
QString deselectedName = modelAppList->data(index, Qt::DisplayRole).toString();
159+
160+
qSelectedApps.erase(std::remove_if(qSelectedApps.begin(), qSelectedApps.end(),
161+
[&](const QString &appName) {
162+
return appName == deselectedName;
163+
}), qSelectedApps.end());
164+
}
165+
});
166+
167+
QObject::connect(filterBox, &QLineEdit::textChanged, self, [&](const QString &text) {
168+
QItemSelectionModel *selectionModel = listInstalledApps->selectionModel();
169+
170+
filteredApps.clear();
171+
for (const auto &app: appList) {
172+
if (app.name.contains(text, Qt::CaseInsensitive))
173+
filteredApps.push_back(app);
174+
}
175+
176+
modelAppList->clear();
177+
for (const auto &app: filteredApps)
178+
modelAppList->addItem(app);
179+
180+
// restore selections
181+
for (int i = 0; i < modelAppList->rowCount(); i++) {
182+
QModelIndex index = modelAppList->index(i, 0);
183+
QString appName = modelAppList->data(index, Qt::DisplayRole).toString();
184+
if (std::find(qSelectedApps.begin(), qSelectedApps.end(), appName) != qSelectedApps.end())
185+
selectionModel->select(index, QItemSelectionModel::Select);
186+
}
187+
});
188+
189+
QObject::connect(buttonBox, &QDialogButtonBox::accepted, self, [&]() {
190+
Ui::selectedItems.clear();
191+
192+
for (const QString &index: qSelectedApps) {
193+
auto it = std::find_if(appList.begin(), appList.end(), [&](const Application &app) {
194+
return app.name == index;
195+
});
196+
197+
if (it != appList.end()) {
198+
Ui::selectedItems.push_back(*it);
199+
}
200+
}
201+
202+
listInstalledApps->selectionModel()->clearSelection();
203+
204+
self->accept();
205+
});
206+
207+
QObject::connect(buttonBox, &QDialogButtonBox::rejected, self, [&]() {
208+
self->reject();
209+
});
210+
}

Sources/AppGUI/UI/AppSelector.hpp

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,37 @@
1-
#ifndef AppSelector_HPP
2-
#define AppSelector_HPP
1+
#ifndef APPSELECTOR_HPP
2+
#define APPSELECTOR_HPP
33

44
#include <QtCore/QVariant>
55
#include <QtWidgets/QAbstractButton>
66
#include <QtWidgets/QApplication>
77
#include <QtWidgets/QDialog>
88
#include <QtWidgets/QDialogButtonBox>
99
#include <QtWidgets/QLabel>
10+
#include <QtWidgets/QLineEdit>
1011
#include <QtWidgets/QListView>
12+
#include "Model/AppListModel.hpp"
1113

1214
QT_BEGIN_NAMESPACE
1315

1416
class Ui_AppSelector
1517
{
1618
public:
19+
QDialog *self;
1720
QDialogButtonBox *buttonBox;
1821
QListView *listInstalledApps;
1922
QLabel *label0;
23+
QLineEdit *filterBox;
24+
QLabel *label1;
2025

2126
void setupUi(QDialog *AppSelector)
2227
{
2328
if (AppSelector->objectName().isEmpty())
2429
AppSelector->setObjectName("AppSelector");
25-
AppSelector->resize(590, 385);
30+
AppSelector->resize(590, 433);
31+
self = AppSelector;
2632
buttonBox = new QDialogButtonBox(AppSelector);
2733
buttonBox->setObjectName("buttonBox");
28-
buttonBox->setGeometry(QRect(230, 340, 341, 32));
34+
buttonBox->setGeometry(QRect(240, 390, 341, 32));
2935
buttonBox->setOrientation(Qt::Orientation::Horizontal);
3036
buttonBox->setStandardButtons(QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok);
3137
listInstalledApps = new QListView(AppSelector);
@@ -34,26 +40,38 @@ class Ui_AppSelector
3440
label0 = new QLabel(AppSelector);
3541
label0->setObjectName("label0");
3642
label0->setGeometry(QRect(10, 10, 151, 18));
43+
filterBox = new QLineEdit(AppSelector);
44+
filterBox->setObjectName("filterBox");
45+
filterBox->setGeometry(QRect(50, 330, 531, 32));
46+
label1 = new QLabel(AppSelector);
47+
label1->setObjectName("label1");
48+
label1->setGeometry(QRect(10, 335, 58, 18));
3749

3850
retranslateUi(AppSelector);
39-
QObject::connect(buttonBox, &QDialogButtonBox::accepted, AppSelector, qOverload<>(&QDialog::accept));
40-
QObject::connect(buttonBox, &QDialogButtonBox::rejected, AppSelector, qOverload<>(&QDialog::reject));
4151

4252
QMetaObject::connectSlotsByName(AppSelector);
53+
54+
performLogic();
4355
} // setupUi
4456

4557
void retranslateUi(QDialog *AppSelector)
4658
{
4759
AppSelector->setWindowTitle(QCoreApplication::translate("AppSelector", "Select Application", nullptr));
4860
label0->setText(QCoreApplication::translate("AppSelector", "Installed applications:", nullptr));
61+
filterBox->setPlaceholderText(QCoreApplication::translate("AppSelector", "Application Name", nullptr));
62+
label1->setText(QCoreApplication::translate("AppSelector", "Filter:", nullptr));
4963
} // retranslateUi
5064

65+
void performLogic() const;
66+
5167
};
5268

5369
namespace Ui {
70+
extern std::vector<Application> selectedItems;
71+
5472
class AppSelector: public Ui_AppSelector {};
5573
} // namespace Ui
5674

5775
QT_END_NAMESPACE
5876

59-
#endif // AppSelector_HPP
77+
#endif // APPSELECTOR_HPP

Sources/AppGUI/UI/AppSelector.ui

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<x>0</x>
88
<y>0</y>
99
<width>590</width>
10-
<height>385</height>
10+
<height>433</height>
1111
</rect>
1212
</property>
1313
<property name="windowTitle">
@@ -16,8 +16,8 @@
1616
<widget class="QDialogButtonBox" name="buttonBox">
1717
<property name="geometry">
1818
<rect>
19-
<x>230</x>
20-
<y>340</y>
19+
<x>240</x>
20+
<y>390</y>
2121
<width>341</width>
2222
<height>32</height>
2323
</rect>
@@ -52,6 +52,32 @@
5252
<string>Installed applications:</string>
5353
</property>
5454
</widget>
55+
<widget class="QLabel" name="label1">
56+
<property name="geometry">
57+
<rect>
58+
<x>10</x>
59+
<y>335</y>
60+
<width>58</width>
61+
<height>18</height>
62+
</rect>
63+
</property>
64+
<property name="text">
65+
<string>Filter:</string>
66+
</property>
67+
</widget>
68+
<widget class="QLineEdit" name="filterBox">
69+
<property name="geometry">
70+
<rect>
71+
<x>50</x>
72+
<y>330</y>
73+
<width>531</width>
74+
<height>32</height>
75+
</rect>
76+
</property>
77+
<property name="placeholderText">
78+
<string>Application Name</string>
79+
</property>
80+
</widget>
5581
</widget>
5682
<resources/>
5783
<connections>

0 commit comments

Comments
 (0)