Adwaita-derived (GNOME Project, LGPL-3-or-CC-BY-SA), recoloured per light/dark
skin and laid out in IDM's toolbar positions per docs/03-gui-spec.md §1.
Licence and per-file provenance recorded in gui/resources/icons/LICENSE.
New IconTheme mirrors ThemeManager's colorSchemeChanged hook so a live
light/dark switch swaps every action's glyph, not just the QSS. Toolbar/menu
actions, the tray icon and the window icon all now use it instead of the
SP_ArrowDown placeholder. Install-path notes for PKG filed as R5 in
gui/docs/pkg-qa-requests-m1.md.
Also rebases lane/gui onto main (13 commits, no gui/ conflicts).
Verified: full build clean, all 13 gui ctest targets pass (including
gui_no_download_logic), unhappy-path DoD gate PASS against mockd
(--drop-connection).
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Continues the build order past Options/Scheduler/Speed Limiter/Batch/
Grabber/tray.
- DropTargetWidget: frameless always-on-top drop target (docs/03-gui-spec.md
§5), position persisted, accepts a dropped http(s) URL or link text and
opens FileInfoDialog directly (skipping Add URL, since the URL is already
known). Shown/hidden from general.showDropTarget, live via
event.settings.changed, same pattern MainWindow already used for
general.minimizeToTray.
- Clipboard, explicit path #2 (docs/06-risks-and-spikes.md R2):
GlobalShortcut wraps org.freedesktop.portal.GlobalShortcuts
(CreateSession -> BindShortcuts -> Activated), triggering the same Add URL
flow. Guarded end-to-end on `if(TARGET Qt6::DBus)` / VELOX_GUI_HAVE_DBUS
so a build without the component degrades to "feature skipped," not
broken (gui/docs/pkg-qa-requests-m1.md R4). Best-effort by design per the
risk doc: fails silent, never advertised.
Verified live against the real portal (a real Wayland session, not just
offscreen): `CreateSession` refuses every caller with "An app id is
required" — reproduced identically via a bare `busctl` call with no Qt
involved at all, so this is the portal requiring a sandboxed caller
identity, not something fixable from an unconfined process. Recorded as
a partial Spike S2 answer in docs/06-risks-and-spikes.md: this explicit
path likely doesn't work for Velox as a traditionally-packaged app on
stock GNOME, only if/when it ships confined. Also fixed a real leak this
verification caught: QDBusInterface's introspection cache reads as a
LeakSanitizer leak the first time anything touches D-Bus (tst_rtl went
red under ASan) — switched to QDBusMessage::createMethodCall, which
needs no introspection.
- Theming (docs/03-gui-spec.md §7): gui/resources/qss/{idm-like,dark}.qss,
each with a documented palette block up top (QSS itself has no variable
syntax), applied by ThemeManager and kept live via
QStyleHints::colorSchemeChanged. util/Theme.hpp gives the handful of
inline C++ styles (status dot, offline banner, the eleven identical
error-label styles across dialogs) named constants instead of a twelfth
copy of the same hex.
- UiThreadWatchdog: the M1 DoD's 200 ms debug-build watchdog. A background
std::thread pings the UI thread every 50 ms via a queued invokeMethod and
warns once (not per-poll) if a ping goes unanswered past 200 ms; no
QThread, no Qt event loop of its own, so the watchdog itself can never be
what blocks the thread it watches. No-op in a release build. Proven both
ways in tst_uithreadwatchdog: fires on a genuinely blocked UI thread
(synchronous sleep, no processEvents) and stays silent on a responsive
one.
Full non-conformance suite (55 tests across every lane, `ctest -LE
conformance`) passes clean at this point, including the whole gui label
under ASan+UBSan.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01NSCdCWFXBTSBBK3MzWtJiC
Wires up the three dialogs from build order step 5 (docs/03-gui-spec.md
§§2-3) and the MainWindow slots that were declared but never implemented:
- AddUrlDialog: clipboard prefill is the explicit path docs/06 R2 calls
for (no passive monitoring, not advertised).
- FileInfoDialog: async download.probe never blocks the UI; ends by
calling download.add itself (Now / Later / Add to Queue). buildSpec()
is a pure static so the optional-field-omission logic is unit-testable
without touching a widget.
- ProgressDialog: non-modal, WA_DeleteOnClose, driven by the taskProgress/
taskStateChanged signals RpcClient already re-broadcasts; download.get
seeds state once for a dialog opened mid-transfer. Hosts SegmentBarsWidget
and SpeedGraphWidget.
MainWindow: openAddUrlDialog/openPropertiesForSelection/showTableContextMenu
now have bodies; category.list/queue.list responses are cached so File Info
can populate its category combo and queue menu without a second round trip.
The row context menu covers what already exists (Resume/Pause/Stop/Delete/
Properties) and deliberately leaves out Open/Open With/Move-Rename/
Redownload/Add to Queue — those need dialogs later build-order steps haven't
reached yet.
util/Format.hpp: pulled the bytes/rate/eta formatting out of MainWindow and
DownloadTableModel once the dialogs wanted the same strings a third time.
Verified end-to-end against a running mockd (category.list/queue.list,
download.probe, download.add, download.get, and live event.task.progress/
event.task.state) under ASan+UBSan: all three dialogs render correctly
against real fixture data and the flow runs clean with no leaks or
sanitizer reports.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01NSCdCWFXBTSBBK3MzWtJiC