diff --git a/gui/CMakeLists.txt b/gui/CMakeLists.txt index e6d0da8..e68eea4 100644 --- a/gui/CMakeLists.txt +++ b/gui/CMakeLists.txt @@ -37,6 +37,7 @@ add_library(velox-gui-lib STATIC src/tray/TrayIcon.cpp src/widgets/DropTargetWidget.cpp src/util/ThemeManager.cpp + src/util/IconTheme.cpp src/util/UiThreadWatchdog.cpp src/mainwindow/CategoryPanel.cpp src/mainwindow/MainWindow.cpp @@ -50,6 +51,18 @@ qt_add_resources(velox-gui-lib "theme" FILES resources/qss/idm-like.qss resources/qss/dark.qss ) +# gui/resources/icons/LICENSE: Adwaita-derived (GNOME Project), LGPL-3 or CC-BY-SA-3/4. +# light/dark are the same glyphs recoloured for each toolbar skin (IconTheme picks +# between them the same way ThemeManager picks QSS); app/ is the fixed full-colour +# window/tray glyph. +file(GLOB VELOX_GUI_ICONS_LIGHT resources/icons/light/*.svg) +file(GLOB VELOX_GUI_ICONS_DARK resources/icons/dark/*.svg) +qt_add_resources(velox-gui-lib "icons" + PREFIX "/icons" + BASE "resources/icons" + FILES ${VELOX_GUI_ICONS_LIGHT} ${VELOX_GUI_ICONS_DARK} resources/icons/app/velox.svg +) + target_include_directories(velox-gui-lib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src) target_compile_features(velox-gui-lib PUBLIC cxx_std_23) target_compile_options(velox-gui-lib PRIVATE -Wall -Wextra -Wpedantic -Werror) diff --git a/gui/docs/pkg-qa-requests-m1.md b/gui/docs/pkg-qa-requests-m1.md index f4acf3b..65c4c45 100644 --- a/gui/docs/pkg-qa-requests-m1.md +++ b/gui/docs/pkg-qa-requests-m1.md @@ -219,3 +219,35 @@ Worth making explicit anyway, since relying on undocumented target leakage is fr No apt change needed either way — `qt6-base-dev` (already in `APT_GUI`) ships `QtDBus`'s headers directly (verified live: `dpkg -L qt6-base-dev | grep -i dbus` lists the whole `QtDBus/` include tree). + +--- + +## R5 — real icon set has landed; stop shipping the placeholder, here's the install path + +`gui/resources/icons/.gitkeep` is gone. There's a real, licensed icon set now (Adwaita, +GNOME Project, LGPL-3-or-CC-BY-SA — full text and per-file provenance in +`gui/resources/icons/LICENSE`), embedded into `velox-gui` via `qt_add_resources` in +`gui/CMakeLists.txt` (`:/icons/{light,dark}/.svg` for toolbar/menu actions, +`:/icons/app/velox.svg` for the window and tray icon). Nothing needs installing +separately for the binary itself — the SVGs are baked into the Qt resource system at +build time, not read from disk at runtime. + +What that means for whatever `.deb` postinst / desktop-entry work PKG is doing: + +* **Stop bundling a placeholder icon file in the test `.deb`.** There's no longer a gap + to fill — `velox-gui`'s own binary already carries its icon. +* **App icon for the `.desktop` file / desktop menu entry**, if PKG is generating one: + the source SVG is `gui/resources/icons/app/velox.svg` (full colour, unmodified Adwaita + `folder-download` glyph, same in light and dark). If the packaging pipeline wants a + filesystem-installed icon (standard XDG icon-theme layout, e.g. + `/usr/share/icons/hicolor/scalable/apps/velox.svg`, referenced from the `.desktop` + file's `Icon=velox` key) rather than relying on the binary's embedded window icon for + the taskbar/launcher, that single file is the one to install — no rasterization needed, + it's already a clean scalable SVG. GUI has no packaging-stage code to add this itself + (CLAUDE.md §1: `packaging/` is PKG's lane), so filing the path here rather than reaching + across. +* Licence compliance for the `.deb`: `gui/resources/icons/LICENSE` should ship somewhere + discoverable from the package (e.g. copied into `debian/copyright` or + `/usr/share/doc/velox/copyright` alongside whatever other third-party attributions PKG + already tracks) — it documents the Adwaita provenance and dual LGPL-3/CC-BY-SA license + per file, which needs to be preserved wherever the binary that embeds these SVGs ships. diff --git a/gui/resources/icons/.gitkeep b/gui/resources/icons/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/gui/resources/icons/LICENSE b/gui/resources/icons/LICENSE new file mode 100644 index 0000000..5d38613 --- /dev/null +++ b/gui/resources/icons/LICENSE @@ -0,0 +1,66 @@ +Icon set for Velox Download Manager +==================================== + +Source: GNOME Adwaita icon theme (`adwaita-icon-theme`), version bundled with +Debian/Ubuntu, downloaded from . +This is the same "compatibly-licensed set" path called out in +docs/03-gui-spec.md's legal note (Papirus/Breeze-derived is the other option; we used +what was locally available and equally suitable) and gui/docs/agents/AGENT-GUI.md. + +Nothing in this directory is IDM's artwork. Files below are recolored (fill colour only — +geometry untouched) derivatives of Adwaita SVGs, laid out in IDM's toolbar positions per +docs/03-gui-spec.md §1, so the *positions* rhyme with IDM's muscle memory while the +*pictures* are GNOME's. + +Copyright +--------- +(c) 2002-2014 The GNOME Project and Adwaita icon theme contributors, including: +Ulisse Perusin, Riccardo Buzzotta, Josef Vybíral, Hylke Bons, Ricardo González, +Lapo Calamandrei, Rodney Dawes, Luca Ferretti, Tuomas Kuosmanen, Andreas Nilsson, +Jakub Steiner, Claire Alexander, Darren Wilson, and other contributors. + +License +------- +Dual-licensed, at the recipient's option: + - GNU Lesser General Public License v3.0 (LGPL-3.0-only), or + - Creative Commons Attribution-Share Alike 3.0 or 4.0 (CC-BY-SA-3.0 / CC-BY-SA-4.0) + +Full text: https://www.gnu.org/licenses/lgpl-3.0.html + https://creativecommons.org/licenses/by-sa/4.0/ + +Attribution: "GNOME Project" (https://www.gnome.org), per the upstream copyright file's +own attribution note. + +Provenance and modifications +----------------------------- +All files trace back to `/usr/share/icons/Adwaita/{symbolic,scalable}/...` from the +`adwaita-icon-theme` Debian package. Only the `fill` colour was changed (geometry, paths +and viewBox are untouched) to produce a light-toolbar and a dark-toolbar variant of each +symbolic glyph: + + light/*.svg fill #2e2e2e — used when the app is in the idm-like (light) theme + dark/*.svg fill #eeeeec — used when the app is in the dark theme + app/velox.svg unmodified — full-colour "folder-download" glyph, used for the + window icon, the tray icon, and the .desktop icon + + Adwaita source file -> our name + ----------------------------------------------------------------------------- + symbolic/actions/list-add-symbolic.svg -> add-url.svg + symbolic/actions/media-playback-start-symbolic.svg -> resume.svg + symbolic/actions/media-playback-pause-symbolic.svg -> pause.svg + symbolic/actions/media-playback-stop-symbolic.svg -> stop-all.svg + symbolic/actions/edit-delete-symbolic.svg -> delete.svg + symbolic/actions/edit-clear-all-symbolic.svg -> delete-completed.svg + symbolic/actions/appointment-new-symbolic.svg -> scheduler.svg + symbolic/categories/preferences-system-symbolic.svg -> options.svg + symbolic/legacy/web-browser-symbolic.svg -> grabber.svg + symbolic/actions/document-properties-symbolic.svg -> properties.svg + symbolic/actions/insert-link-symbolic.svg -> batch.svg + scalable/places/folder-download.svg -> app/velox.svg (unmodified) + +Toolbar/menu -> icon mapping (docs/03-gui-spec.md §1 toolbar order) +--------------------------------------------------------------------- +Add URL -> add-url · Resume -> resume · Pause -> pause · Stop All -> stop-all · +Delete -> delete · Delete Completed -> delete-completed · Scheduler -> scheduler · +Options -> options · Grabber -> grabber. Properties and Batch (menu-only, not on the +default toolbar) use properties and batch respectively. diff --git a/gui/resources/icons/app/velox.svg b/gui/resources/icons/app/velox.svg new file mode 100644 index 0000000..f707afa --- /dev/null +++ b/gui/resources/icons/app/velox.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/gui/resources/icons/dark/add-url.svg b/gui/resources/icons/dark/add-url.svg new file mode 100644 index 0000000..9ccb50a --- /dev/null +++ b/gui/resources/icons/dark/add-url.svg @@ -0,0 +1,4 @@ + + + + diff --git a/gui/resources/icons/dark/batch.svg b/gui/resources/icons/dark/batch.svg new file mode 100644 index 0000000..5fee28b --- /dev/null +++ b/gui/resources/icons/dark/batch.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/gui/resources/icons/dark/delete-completed.svg b/gui/resources/icons/dark/delete-completed.svg new file mode 100644 index 0000000..ce88e98 --- /dev/null +++ b/gui/resources/icons/dark/delete-completed.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/gui/resources/icons/dark/delete.svg b/gui/resources/icons/dark/delete.svg new file mode 100644 index 0000000..1d91e05 --- /dev/null +++ b/gui/resources/icons/dark/delete.svg @@ -0,0 +1,4 @@ + + + + diff --git a/gui/resources/icons/dark/grabber.svg b/gui/resources/icons/dark/grabber.svg new file mode 100644 index 0000000..5ff7c6e --- /dev/null +++ b/gui/resources/icons/dark/grabber.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gui/resources/icons/dark/options.svg b/gui/resources/icons/dark/options.svg new file mode 100644 index 0000000..f115852 --- /dev/null +++ b/gui/resources/icons/dark/options.svg @@ -0,0 +1,4 @@ + + + + diff --git a/gui/resources/icons/dark/pause.svg b/gui/resources/icons/dark/pause.svg new file mode 100644 index 0000000..6c1a7a8 --- /dev/null +++ b/gui/resources/icons/dark/pause.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/gui/resources/icons/dark/properties.svg b/gui/resources/icons/dark/properties.svg new file mode 100644 index 0000000..ad96e8d --- /dev/null +++ b/gui/resources/icons/dark/properties.svg @@ -0,0 +1,4 @@ + + + + diff --git a/gui/resources/icons/dark/resume.svg b/gui/resources/icons/dark/resume.svg new file mode 100644 index 0000000..155ab8b --- /dev/null +++ b/gui/resources/icons/dark/resume.svg @@ -0,0 +1,4 @@ + + + + diff --git a/gui/resources/icons/dark/scheduler.svg b/gui/resources/icons/dark/scheduler.svg new file mode 100644 index 0000000..e773863 --- /dev/null +++ b/gui/resources/icons/dark/scheduler.svg @@ -0,0 +1,4 @@ + + + + diff --git a/gui/resources/icons/dark/stop-all.svg b/gui/resources/icons/dark/stop-all.svg new file mode 100644 index 0000000..610a7c9 --- /dev/null +++ b/gui/resources/icons/dark/stop-all.svg @@ -0,0 +1,4 @@ + + + + diff --git a/gui/resources/icons/light/add-url.svg b/gui/resources/icons/light/add-url.svg new file mode 100644 index 0000000..5f24bee --- /dev/null +++ b/gui/resources/icons/light/add-url.svg @@ -0,0 +1,4 @@ + + + + diff --git a/gui/resources/icons/light/batch.svg b/gui/resources/icons/light/batch.svg new file mode 100644 index 0000000..319c55c --- /dev/null +++ b/gui/resources/icons/light/batch.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/gui/resources/icons/light/delete-completed.svg b/gui/resources/icons/light/delete-completed.svg new file mode 100644 index 0000000..b377a3a --- /dev/null +++ b/gui/resources/icons/light/delete-completed.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/gui/resources/icons/light/delete.svg b/gui/resources/icons/light/delete.svg new file mode 100644 index 0000000..f949609 --- /dev/null +++ b/gui/resources/icons/light/delete.svg @@ -0,0 +1,4 @@ + + + + diff --git a/gui/resources/icons/light/grabber.svg b/gui/resources/icons/light/grabber.svg new file mode 100644 index 0000000..8150cbf --- /dev/null +++ b/gui/resources/icons/light/grabber.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/gui/resources/icons/light/options.svg b/gui/resources/icons/light/options.svg new file mode 100644 index 0000000..1d3b9a3 --- /dev/null +++ b/gui/resources/icons/light/options.svg @@ -0,0 +1,4 @@ + + + + diff --git a/gui/resources/icons/light/pause.svg b/gui/resources/icons/light/pause.svg new file mode 100644 index 0000000..9f09eae --- /dev/null +++ b/gui/resources/icons/light/pause.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/gui/resources/icons/light/properties.svg b/gui/resources/icons/light/properties.svg new file mode 100644 index 0000000..0305ee5 --- /dev/null +++ b/gui/resources/icons/light/properties.svg @@ -0,0 +1,4 @@ + + + + diff --git a/gui/resources/icons/light/resume.svg b/gui/resources/icons/light/resume.svg new file mode 100644 index 0000000..68c8558 --- /dev/null +++ b/gui/resources/icons/light/resume.svg @@ -0,0 +1,4 @@ + + + + diff --git a/gui/resources/icons/light/scheduler.svg b/gui/resources/icons/light/scheduler.svg new file mode 100644 index 0000000..81f2095 --- /dev/null +++ b/gui/resources/icons/light/scheduler.svg @@ -0,0 +1,4 @@ + + + + diff --git a/gui/resources/icons/light/stop-all.svg b/gui/resources/icons/light/stop-all.svg new file mode 100644 index 0000000..e79dfc9 --- /dev/null +++ b/gui/resources/icons/light/stop-all.svg @@ -0,0 +1,4 @@ + + + + diff --git a/gui/src/mainwindow/MainWindow.cpp b/gui/src/mainwindow/MainWindow.cpp index 940854d..2897664 100644 --- a/gui/src/mainwindow/MainWindow.cpp +++ b/gui/src/mainwindow/MainWindow.cpp @@ -37,6 +37,7 @@ #include "rpc/RpcClient.hpp" #include "tray/TrayIcon.hpp" #include "util/Format.hpp" +#include "util/IconTheme.hpp" #include "util/Theme.hpp" #include "widgets/DropTargetWidget.hpp" #include "widgets/ProgressDelegate.hpp" @@ -67,8 +68,12 @@ MainWindow::MainWindow(rpc::RpcClient *client, QWidget *parent) offlineBanner_(new QWidget(this)), countsTimer_(new QTimer(this)) { setWindowTitle(tr("Velox Download Manager")); + setWindowIcon(IconTheme::appIcon()); resize(1040, 600); + iconTheme_ = new IconTheme(this); + connect(iconTheme_, &IconTheme::iconsChanged, this, &MainWindow::applyIcons); + proxy_->setSourceModel(model_); proxy_->setSortCaseSensitivity(Qt::CaseInsensitive); @@ -116,6 +121,7 @@ MainWindow::MainWindow(rpc::RpcClient *client, QWidget *parent) setCentralWidget(central); buildActions(); + applyIcons(); buildMenus(); buildToolBar(); buildTray(); @@ -205,6 +211,24 @@ void MainWindow::buildActions() { connect(actShow_, &QAction::triggered, this, &MainWindow::showAndRaise); } +// gui/resources/icons/LICENSE: toolbar-position -> icon-stem mapping. Re-run on +// IconTheme::iconsChanged so a live light/dark switch swaps every action's glyph, the +// same way ThemeManager swaps the QSS on the same signal. +void MainWindow::applyIcons() { + actAddUrl_->setIcon(IconTheme::icon(QStringLiteral("add-url"))); + actResume_->setIcon(IconTheme::icon(QStringLiteral("resume"))); + actPause_->setIcon(IconTheme::icon(QStringLiteral("pause"))); + actStop_->setIcon(IconTheme::icon(QStringLiteral("stop-all"))); + actRemove_->setIcon(IconTheme::icon(QStringLiteral("delete"))); + actResumeAll_->setIcon(IconTheme::icon(QStringLiteral("resume"))); + actPauseAll_->setIcon(IconTheme::icon(QStringLiteral("pause"))); + actProperties_->setIcon(IconTheme::icon(QStringLiteral("properties"))); + actOptions_->setIcon(IconTheme::icon(QStringLiteral("options"))); + actScheduler_->setIcon(IconTheme::icon(QStringLiteral("scheduler"))); + actBatch_->setIcon(IconTheme::icon(QStringLiteral("batch"))); + actGrabber_->setIcon(IconTheme::icon(QStringLiteral("grabber"))); +} + void MainWindow::buildMenus() { QMenu *tasks = menuBar()->addMenu(tr("&Tasks")); tasks->addAction(actAddUrl_); diff --git a/gui/src/mainwindow/MainWindow.hpp b/gui/src/mainwindow/MainWindow.hpp index d8f0f6b..b3a917e 100644 --- a/gui/src/mainwindow/MainWindow.hpp +++ b/gui/src/mainwindow/MainWindow.hpp @@ -27,6 +27,7 @@ class DownloadTableModel; class CategoryPanel; class TrayIcon; class DropTargetWidget; +class IconTheme; #ifdef VELOX_GUI_HAVE_DBUS class GlobalShortcut; #endif @@ -69,6 +70,7 @@ class MainWindow : public QMainWindow { void showAndRaise(); void onSettingsChanged(const QJsonObject ¶ms); void onUrlDropped(const QString &url); + void applyIcons(); private: void buildActions(); @@ -107,6 +109,7 @@ class MainWindow : public QMainWindow { QJsonArray categoriesCache_; QJsonArray queuesCache_; + IconTheme *iconTheme_ = nullptr; TrayIcon *trayIcon_ = nullptr; DropTargetWidget *dropTarget_ = nullptr; bool minimizeToTrayEnabled_ = false; diff --git a/gui/src/tray/TrayIcon.cpp b/gui/src/tray/TrayIcon.cpp index 74dadad..d11dda9 100644 --- a/gui/src/tray/TrayIcon.cpp +++ b/gui/src/tray/TrayIcon.cpp @@ -1,29 +1,26 @@ #include "tray/TrayIcon.hpp" #include -#include +#include #include #include #include -#include #include #include "dialogs/SpeedLimiterDialog.hpp" #include "rpc/Protocol.hpp" #include "rpc/RpcClient.hpp" +#include "util/IconTheme.hpp" namespace velox::gui { TrayIcon::TrayIcon(rpc::RpcClient *client, QAction *actShow, QAction *actAddUrl, QAction *actResumeAll, QAction *actPauseAll, QWidget *parent) : QSystemTrayIcon(parent), client_(client) { - // No icon set has been designed yet (that's its own build-order step); a standard - // style icon is a placeholder, not a shipped asset. - if (auto *w = qobject_cast(parent)) { - setIcon(w->style()->standardIcon(QStyle::SP_ArrowDown)); - } else { - setIcon(qApp->style()->standardIcon(QStyle::SP_ArrowDown)); - } + // gui/resources/icons/LICENSE — same full-colour glyph as the window icon; the tray + // icon is fixed (not theme-picked) since Adwaita's folder-download already reads + // fine against both light and dark shell backgrounds. + setIcon(IconTheme::appIcon()); setToolTip(QCoreApplication::translate("velox::gui::TrayIcon", "Velox Download Manager")); auto *menu = new QMenu(parent); diff --git a/gui/src/util/IconTheme.cpp b/gui/src/util/IconTheme.cpp new file mode 100644 index 0000000..a665b57 --- /dev/null +++ b/gui/src/util/IconTheme.cpp @@ -0,0 +1,27 @@ +#include "util/IconTheme.hpp" + +#include +#include + +namespace velox::gui { + +IconTheme::IconTheme(QObject *parent) : QObject(parent) { + connect(QGuiApplication::styleHints(), &QStyleHints::colorSchemeChanged, this, + &IconTheme::onColorSchemeChanged); +} + +QIcon IconTheme::icon(const QString &name) { + const bool dark = QGuiApplication::styleHints()->colorScheme() == Qt::ColorScheme::Dark; + return QIcon(QStringLiteral(":/icons/%1/%2.svg") + .arg(dark ? QStringLiteral("dark") : QStringLiteral("light"), name)); +} + +QIcon IconTheme::appIcon() { + return QIcon(QStringLiteral(":/icons/app/velox.svg")); +} + +void IconTheme::onColorSchemeChanged() { + emit iconsChanged(); +} + +} // namespace velox::gui diff --git a/gui/src/util/IconTheme.hpp b/gui/src/util/IconTheme.hpp new file mode 100644 index 0000000..f090933 --- /dev/null +++ b/gui/src/util/IconTheme.hpp @@ -0,0 +1,34 @@ +// Resolves action icons to the light/dark SVG variant matching the current +// QStyleHints::colorScheme(), same trigger ThemeManager uses for the QSS skin. Lane GUI. +// docs/03-gui-spec.md §1/§7, gui/resources/icons/LICENSE. + +#pragma once + +#include +#include +#include + +namespace velox::gui { + +class IconTheme : public QObject { + Q_OBJECT + + public: + explicit IconTheme(QObject *parent = nullptr); + + /// `name` is a stem under gui/resources/icons/{light,dark}/, e.g. "add-url". + static QIcon icon(const QString &name); + + /// The fixed full-colour glyph (window icon, tray icon) — same in both themes. + static QIcon appIcon(); + + signals: + /// Fired on a live light/dark switch; callers holding an icon(name) result should + /// re-fetch and re-apply it. + void iconsChanged(); + + private slots: + void onColorSchemeChanged(); +}; + +} // namespace velox::gui