Skip to content

Commit fde0257

Browse files
committed
implement basic support for adding repositories
1 parent bd10f6d commit fde0257

File tree

10 files changed

+460
-193
lines changed

10 files changed

+460
-193
lines changed

autobuild.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ cd ui
2929
../qt5build/linux/bin/uic -o packageitem.h PackageItem.ui
3030
../qt5build/linux/bin/uic -o errordialog.h ErrorDialog.ui
3131
../qt5build/linux/bin/uic -o packageinfo.h PackageInfo.ui
32+
../qt5build/linux/bin/uic -o addrepository.h AddRepository.ui
3233
cd ..
3334

3435
# Build application dependencies if not present

main.cpp

Lines changed: 45 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include <iostream>
33
#include <utility>
44
#include <vector>
5+
#include <deque>
56
#include <filesystem>
67
#include <thread>
78
#include <chrono>
@@ -11,12 +12,10 @@
1112
#include <QApplication>
1213
#include <QCoreApplication>
1314
#include <QFontDatabase>
14-
#include <QThread>
1515
#include <QObject>
1616
#include <QDialog>
1717
#include "ui/mainwindow.h"
18-
#include "ui/packageitem.h"
19-
#include "ui/packageinfo.h"
18+
#include "ui/addrepository.h"
2019

2120
// Project globals
2221
#include "globals.h"
@@ -28,10 +27,30 @@
2827
#include "tools/package.h"
2928
#include "tools/repo.h"
3029

31-
const char *repositoryURLs[] = {
30+
std::deque<std::string> repositoryURLs = {
3231
"https://p2r3.github.io/spplice-repo/index.json"
3332
};
3433

34+
void addRepository (const std::string url, QVBoxLayout *container) {
35+
36+
// Fetch the repository packages
37+
std::vector<const ToolsPackage::PackageData*> repository = ToolsRepo::fetchRepository(url);
38+
39+
// Keep track of added package count to order them properly
40+
int currPackage = 0;
41+
42+
for (const ToolsPackage::PackageData *package : repository) {
43+
// Create PackageItem widget from PackageData
44+
QWidget *item = ToolsPackage::createPackageItem(package);
45+
// Add the item to the package list container
46+
container->insertWidget(currPackage, item);
47+
currPackage ++;
48+
// Sleep for a few milliseconds on each package to reduce strain on the network
49+
std::this_thread::sleep_for(std::chrono::milliseconds(5));
50+
}
51+
52+
}
53+
3554
int main (int argc, char *argv[]) {
3655

3756
try {
@@ -58,126 +77,37 @@ int main (int argc, char *argv[]) {
5877
// Initialize CURL
5978
ToolsCURL::init();
6079

61-
// Create a vector containing all packages from all repositories
62-
std::vector<const ToolsPackage::PackageData*> allPackages;
63-
// Fetch packages from each repository
64-
for (auto url : repositoryURLs) {
65-
std::vector<const ToolsPackage::PackageData*> repository = ToolsRepo::fetchRepository(url);
66-
allPackages.insert(allPackages.end(), repository.begin(), repository.end());
67-
}
68-
69-
// Generate a PackageItem for each package
70-
for (const auto &package : allPackages) {
71-
72-
// Create the package item widget
73-
QWidget *item = new QWidget;
74-
Ui::PackageItem itemUI;
75-
itemUI.setupUi(item);
76-
77-
// Set the title and description
78-
itemUI.PackageTitle->setText(QString::fromStdString(package->title));
79-
itemUI.PackageDescription->setText(QString::fromStdString(package->description));
80-
81-
// Connect the install button
82-
QPushButton *installButton = itemUI.PackageInstallButton;
83-
QObject::connect(installButton, &QPushButton::clicked, [installButton, &package]() {
84-
85-
// Create a thread for asynchronous installation
86-
PackageItemWorker *worker = new PackageItemWorker;
87-
QThread *workerThread = new QThread;
88-
worker->moveToThread(workerThread);
89-
90-
// Connect the task of installing the package to the worker
91-
QObject::connect(workerThread, &QThread::started, worker, [worker, &package]() {
92-
QMetaObject::invokeMethod(worker, "installPackage", Q_ARG(const ToolsPackage::PackageData*, package));
93-
});
94-
95-
// Update the button text based on the installation state
96-
QObject::connect(worker, &PackageItemWorker::installStateUpdate, installButton, [installButton]() {
97-
switch (SPPLICE_INSTALL_STATE) {
98-
case 0:
99-
installButton->setText("Install");
100-
installButton->setStyleSheet("");
101-
break;
102-
case 1:
103-
installButton->setText("Installing...");
104-
installButton->setStyleSheet("color: #faa81a;");
105-
break;
106-
case 2:
107-
installButton->setText("Installed");
108-
installButton->setStyleSheet("color: #faa81a;");
109-
break;
110-
}
111-
});
112-
113-
// Clean up the thread once it's done
114-
QObject::connect(worker, &PackageItemWorker::packageIconReady, workerThread, &QThread::quit);
115-
QObject::connect(worker, &PackageItemWorker::packageIconReady, worker, &PackageItemWorker::deleteLater);
116-
QObject::connect(workerThread, &QThread::finished, workerThread, &QThread::deleteLater);
117-
118-
// Start the worker thread
119-
workerThread->start();
80+
QVBoxLayout *packageContainer = windowUI.PackageListLayout;
12081

121-
});
82+
// Connect the "Add Repository" button
83+
QObject::connect(windowUI.TitleButtonR, &QPushButton::clicked, [packageContainer]() {
12284

123-
// Add the item to the package list container
124-
windowUI.PackageListLayout->addWidget(item);
85+
// Create new repository entry dialog
86+
QDialog *dialog = new QDialog;
87+
Ui::RepoDialog dialogUI;
88+
dialogUI.setupUi(dialog);
12589

126-
// Start a new worker thread for asynchronous icon fetching
127-
PackageItemWorker *worker = new PackageItemWorker;
128-
QThread *workerThread = new QThread;
129-
worker->moveToThread(workerThread);
90+
QLineEdit *urlTextBox = dialogUI.RepoURL;
13091

131-
// Connect the task of fetching the icon to the worker
132-
QSize iconSize = itemUI.PackageIcon->size();
133-
QObject::connect(workerThread, &QThread::started, worker, [worker, &package, iconSize]() {
134-
QMetaObject::invokeMethod(worker, "getPackageIcon", Q_ARG(const ToolsPackage::PackageData*, package), Q_ARG(QSize, iconSize));
92+
// Connect the "OK" button
93+
QObject::connect(dialogUI.DialogButton, &QPushButton::clicked, [packageContainer, urlTextBox, dialog]() {
94+
addRepository(urlTextBox->text().toStdString(), packageContainer);
95+
dialog->hide();
13596
});
136-
QObject::connect(worker, &PackageItemWorker::packageIconResult, itemUI.PackageIcon, &QLabel::setPixmap);
137-
138-
// Clean up the thread once it's done
139-
QObject::connect(worker, &PackageItemWorker::packageIconReady, workerThread, &QThread::quit);
140-
QObject::connect(worker, &PackageItemWorker::packageIconReady, worker, &PackageItemWorker::deleteLater);
141-
QObject::connect(workerThread, &QThread::finished, workerThread, &QThread::deleteLater);
142-
143-
// Start the worker thread
144-
workerThread->start();
145-
146-
// Connect the "Read more" button
147-
QObject::connect(itemUI.PackageInfoButton, &QPushButton::clicked, [&package]() {
148-
149-
QDialog *dialog = new QDialog;
150-
Ui::PackageInfo dialogUI;
151-
dialogUI.setupUi(dialog);
152-
153-
// Set text data (title, author, description)
154-
dialogUI.PackageTitle->setText(QString::fromStdString(package->title));
155-
dialogUI.PackageAuthor->setText(QString::fromStdString("By " + package->author));
156-
dialogUI.PackageDescription->setText(QString::fromStdString(package->description));
157-
158-
// Set the icon - assume the image has already been downloaded
159-
size_t imageURLHash = std::hash<std::string>{}(package->icon);
160-
std::filesystem::path imagePath = TEMP_DIR / std::to_string(imageURLHash);
161-
162-
QSize iconSize = dialogUI.PackageIcon->size();
163-
164-
#ifndef TARGET_WINDOWS
165-
QPixmap iconPixmap = QPixmap(QString::fromStdString(imagePath.string())).scaled(iconSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
166-
#else
167-
QPixmap iconPixmap = QPixmap(QString::fromStdWString(imagePath.wstring())).scaled(iconSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
168-
#endif
169-
170-
QPixmap iconRoundedPixmap = ToolsQT::getRoundedPixmap(iconPixmap, 10);
171-
dialogUI.PackageIcon->setPixmap(iconRoundedPixmap);
172-
173-
dialog->setWindowTitle(QString::fromStdString("Details for " + package->title));
174-
dialog->exec();
17597

98+
// Connect the event of pressing return
99+
QObject::connect(urlTextBox, &QLineEdit::returnPressed, [packageContainer, urlTextBox, dialog]() {
100+
addRepository(urlTextBox->text().toStdString(), packageContainer);
101+
dialog->hide();
176102
});
177103

178-
// Sleep for a few milliseconds on each package to reduce strain on the network
179-
std::this_thread::sleep_for(std::chrono::milliseconds(5));
104+
dialog->exec();
105+
106+
});
180107

108+
// Fetch packages from each repository
109+
for (const std::string &url : repositoryURLs) {
110+
addRepository(url, packageContainer);
181111
}
182112

183113
// Clean up CURL on program termination

resources.qrc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,7 @@
33
<file>fonts/Quicksand-Light.ttf</file>
44
<file>fonts/Quicksand-Regular.ttf</file>
55
<file>fonts/Quicksand-Medium.ttf</file>
6+
<file>resources/settings.png</file>
7+
<file>resources/add.png</file>
68
</qresource>
79
</RCC>

resources/add.png

5.9 KB
Loading

resources/settings.png

12 KB
Loading

tools/package.cpp

Lines changed: 126 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,17 @@
66
#include <string>
77
#include <functional>
88
#include <QPixmap>
9+
#include <QWidget>
10+
#include <QThread>
11+
#include <QObject>
12+
#include <QDialog>
913
#include <QJsonObject>
1014
#include <QJsonArray>
1115
#include <QJsonValue>
1216

17+
#include "../ui/packageitem.h"
18+
#include "../ui/packageinfo.h"
19+
1320
#include "../globals.h"
1421
#include "curl.h" // ToolsCURL
1522
#include "qt.h" // ToolsQT
@@ -20,16 +27,27 @@
2027

2128
// Constructs the PackageData instance from a JSON object
2229
ToolsPackage::PackageData::PackageData (QJsonObject package) {
30+
31+
// These properties should be available even in old repositories
2332
this->title = package["title"].toString().toStdString();
2433
this->author = package["author"].toString().toStdString();
2534
this->description = package["description"].toString().toStdString();
26-
this->version = package["version"].toString().toStdString();
2735
this->file = package["file"].toString().toStdString();
2836
this->icon = package["icon"].toString().toStdString();
29-
QJsonArray args = package["args"].toArray();
30-
for (const QJsonValue &arg : args) {
31-
this->args.push_back(arg.toString().toStdString());
37+
38+
// The version and args properties were introduced in version 3
39+
// Use a placeholder for version if not provided
40+
if (package.contains("version")) this->version = "1.0.0";
41+
else this->version = package["version"].toString().toStdString();
42+
43+
// Leave args blank if not provided
44+
if (package.contains("args")) {
45+
QJsonArray args = package["args"].toArray();
46+
for (const QJsonValue &arg : args) {
47+
this->args.push_back(arg.toString().toStdString());
48+
}
3249
}
50+
3351
}
3452

3553
// Checks if the given file exists and is up-to-date
@@ -127,7 +145,7 @@ void PackageItemWorker::installPackage (const ToolsPackage::PackageData *package
127145
#ifndef TARGET_WINDOWS
128146
std::pair<bool, std::string> installationResult = ToolsInstall::installPackageFile(filePath, package->args);
129147
#else
130-
std::pair<bool, std::wstring> installationResult = ToolsInstall::installPackageFile(filePath, package->args);
148+
std::pair<bool, std::wstring> installationResult = ToolsInstall::installPackageFile(filePath, package.args);
131149
#endif
132150

133151
// If installation failed, display error and exit early
@@ -165,3 +183,106 @@ void PackageItemWorker::installPackage (const ToolsPackage::PackageData *package
165183
emit installStateUpdate();
166184

167185
}
186+
187+
QWidget* ToolsPackage::createPackageItem (const ToolsPackage::PackageData *package) {
188+
189+
// Create the package item widget
190+
QWidget *item = new QWidget;
191+
Ui::PackageItem itemUI;
192+
itemUI.setupUi(item);
193+
194+
// Set the title and description
195+
itemUI.PackageTitle->setText(QString::fromStdString(package->title));
196+
itemUI.PackageDescription->setText(QString::fromStdString(package->description));
197+
198+
// Connect the install button
199+
QPushButton *installButton = itemUI.PackageInstallButton;
200+
QObject::connect(installButton, &QPushButton::clicked, [installButton, package]() {
201+
202+
// Create a thread for asynchronous installation
203+
PackageItemWorker *worker = new PackageItemWorker;
204+
QThread *workerThread = new QThread;
205+
worker->moveToThread(workerThread);
206+
207+
// Connect the task of installing the package to the worker
208+
QObject::connect(workerThread, &QThread::started, worker, [worker, package]() {
209+
QMetaObject::invokeMethod(worker, "installPackage", Q_ARG(const ToolsPackage::PackageData*, package));
210+
});
211+
212+
// Update the button text based on the installation state
213+
QObject::connect(worker, &PackageItemWorker::installStateUpdate, installButton, [installButton]() {
214+
switch (SPPLICE_INSTALL_STATE) {
215+
case 0:
216+
installButton->setText("Install");
217+
installButton->setStyleSheet("");
218+
break;
219+
case 1:
220+
installButton->setText("Installing...");
221+
installButton->setStyleSheet("color: #faa81a;");
222+
break;
223+
case 2:
224+
installButton->setText("Installed");
225+
installButton->setStyleSheet("color: #faa81a;");
226+
break;
227+
}
228+
});
229+
230+
// Clean up the thread once it's done
231+
QObject::connect(worker, &PackageItemWorker::packageIconReady, workerThread, &QThread::quit);
232+
QObject::connect(worker, &PackageItemWorker::packageIconReady, worker, &PackageItemWorker::deleteLater);
233+
QObject::connect(workerThread, &QThread::finished, workerThread, &QThread::deleteLater);
234+
235+
// Start the worker thread
236+
workerThread->start();
237+
238+
});
239+
240+
// Start a new worker thread for asynchronous icon fetching
241+
PackageItemWorker *worker = new PackageItemWorker;
242+
QThread *workerThread = new QThread;
243+
worker->moveToThread(workerThread);
244+
245+
// Connect the task of fetching the icon to the worker
246+
QSize iconSize = itemUI.PackageIcon->size();
247+
QObject::connect(workerThread, &QThread::started, worker, [worker, package, iconSize]() {
248+
QMetaObject::invokeMethod(worker, "getPackageIcon", Q_ARG(const ToolsPackage::PackageData*, package), Q_ARG(QSize, iconSize));
249+
});
250+
QObject::connect(worker, &PackageItemWorker::packageIconResult, itemUI.PackageIcon, &QLabel::setPixmap);
251+
252+
// Clean up the thread once it's done
253+
QObject::connect(worker, &PackageItemWorker::packageIconReady, workerThread, &QThread::quit);
254+
QObject::connect(worker, &PackageItemWorker::packageIconReady, worker, &PackageItemWorker::deleteLater);
255+
QObject::connect(workerThread, &QThread::finished, workerThread, &QThread::deleteLater);
256+
257+
// Start the worker thread
258+
workerThread->start();
259+
260+
// Connect the "Read more" button
261+
QObject::connect(itemUI.PackageInfoButton, &QPushButton::clicked, [package]() {
262+
263+
QDialog *dialog = new QDialog;
264+
Ui::PackageInfo dialogUI;
265+
dialogUI.setupUi(dialog);
266+
267+
// Set text data (title, author, description)
268+
dialogUI.PackageTitle->setText(QString::fromStdString(package->title));
269+
dialogUI.PackageAuthor->setText(QString::fromStdString("By " + package->author));
270+
dialogUI.PackageDescription->setText(QString::fromStdString(package->description));
271+
272+
// Set the icon - assume the image has already been downloaded
273+
size_t imageURLHash = std::hash<std::string>{}(package->icon);
274+
std::filesystem::path imagePath = TEMP_DIR / std::to_string(imageURLHash);
275+
276+
QSize iconSize = dialogUI.PackageIcon->size();
277+
QPixmap iconPixmap = ToolsQT::getPixmapFromPath(imagePath, iconSize);
278+
QPixmap iconRoundedPixmap = ToolsQT::getRoundedPixmap(iconPixmap, 10);
279+
dialogUI.PackageIcon->setPixmap(iconRoundedPixmap);
280+
281+
dialog->setWindowTitle(QString::fromStdString("Details for " + package->title));
282+
dialog->exec();
283+
284+
});
285+
286+
return item;
287+
288+
}

0 commit comments

Comments
 (0)