Qt wallet: add a dark mode #49

Closed
opened 2026-11-10 19:06:48 +00:00 by btcbob · 6 comments
Owner

Requested by dokfaux on Dobbscord: the Qt wallet needs a dark mode.

Right now the wallet ignores dark desktop themes, and when the OS palette is dark some of it goes unreadable. The problem is that colors are hardcoded in a handful of places instead of coming from the palette:

  • src/qt/dobbscoin.cpp:547 and src/qt/dobbscoingui.cpp:110 paint the bgtile background image behind the main window and wallet frame
  • src/qt/dobbscoingui.cpp:211 sync progress bar has a fixed #e8e8e8 background
  • src/qt/intro.cpp:213,225 error and free-space labels in #800000, which nearly vanishes on dark backgrounds
  • src/qt/coincontroldialog.cpp:619-621, src/qt/optionsdialog.cpp:252,284 plain color:red
  • styleSheet properties baked into forms/overviewpage.ui (4), sendcoinsdialog.ui (5), intro.ui, optionsdialog.ui

overviewpage.cpp already reads QPalette::Text for the transaction list, so that part is fine.

Proposal

  1. Add a Theme setting in Options > Display: System / Light / Dark. Store it in QSettings and add a -theme= startup arg to match.
  2. Dark = Qt's Fusion style with a dark QPalette. No big stylesheet, so stock widgets pick it up for free.
  3. Swap the hardcoded colors above for palette roles or a small helper that returns a warning/error color that reads on both backgrounds.
  4. Give the background tile a dark variant (or drop it in dark mode).
  5. Check icons: the toolbar and tab icons are dark-on-transparent and need light variants or recoloring at load.
  6. "System" follows the OS on platforms where Qt 5 can detect it (Windows registry AppsUseLightTheme, macOS appearance). Elsewhere it falls back to Light.

Done when

  • Every tab, dialog, and the debug console is readable in Dark on Linux, Windows, and macOS builds
  • Switching theme applies without a restart, or the dialog says a restart is needed
  • Light looks the same as today
Requested by dokfaux on Dobbscord: the Qt wallet needs a dark mode. Right now the wallet ignores dark desktop themes, and when the OS palette is dark some of it goes unreadable. The problem is that colors are hardcoded in a handful of places instead of coming from the palette: - `src/qt/dobbscoin.cpp:547` and `src/qt/dobbscoingui.cpp:110` paint the `bgtile` background image behind the main window and wallet frame - `src/qt/dobbscoingui.cpp:211` sync progress bar has a fixed `#e8e8e8` background - `src/qt/intro.cpp:213,225` error and free-space labels in `#800000`, which nearly vanishes on dark backgrounds - `src/qt/coincontroldialog.cpp:619-621`, `src/qt/optionsdialog.cpp:252,284` plain `color:red` - `styleSheet` properties baked into `forms/overviewpage.ui` (4), `sendcoinsdialog.ui` (5), `intro.ui`, `optionsdialog.ui` `overviewpage.cpp` already reads `QPalette::Text` for the transaction list, so that part is fine. ## Proposal 1. Add a **Theme** setting in Options > Display: System / Light / Dark. Store it in QSettings and add a `-theme=` startup arg to match. 2. Dark = Qt's Fusion style with a dark `QPalette`. No big stylesheet, so stock widgets pick it up for free. 3. Swap the hardcoded colors above for palette roles or a small helper that returns a warning/error color that reads on both backgrounds. 4. Give the background tile a dark variant (or drop it in dark mode). 5. Check icons: the toolbar and tab icons are dark-on-transparent and need light variants or recoloring at load. 6. "System" follows the OS on platforms where Qt 5 can detect it (Windows registry `AppsUseLightTheme`, macOS appearance). Elsewhere it falls back to Light. ## Done when - Every tab, dialog, and the debug console is readable in Dark on Linux, Windows, and macOS builds - Switching theme applies without a restart, or the dialog says a restart is needed - Light looks the same as today
Author
Owner

Scope

Sized against main @ 3f047b7f.

Constraints

  • Depends pins Qt 5.15.19. QStyleHints::colorScheme() is Qt 6.5+, so "System" detection has to be per-platform code (Windows registry, macOS NSApp.effectiveAppearance). Linux falls back to Light unless the desktop palette is already dark.
  • This tree predates Bitcoin Core's PlatformStyle (0.12), which is what upstream uses to recolor icons. We'd backport a slim version of it.

Work items

  1. Theme setting + -theme= arg + Fusion/dark QPalette at startup (dobbscoin.cpp, optionsmodel, optionsdialog.ui). Small.
  2. Hardcoded colors, ~20 sites:
    • guiconstants.h COLOR_*, used by transactiontablemodel.cpp and overviewpage.cpp. COLOR_BLACK at transactiontablemodel.cpp:482 gives black-on-dark text in the history tab. This is the worst one.
    • trafficgraphwidget.cpp already draws on black, so it's fine as is
    • splashscreen.cpp:53,125
    • the setStyleSheet sites already listed above
    • .ui stylesheets: color:#000000 and the #F0D0A0→#F8D488 gradient in overviewpage.ui, #808080, three red
  3. Icons: 46 PNGs across 39 QIcon(":/icons/...") call sites. Backport PlatformStyle::SingleColorIcon to recolor the monochrome ones at load. The colored ones (tx status, connection bars) probably stay as they are. Medium.
  4. bgtile background: add a dark tile or skip it in Dark. Needs art, or just a decision.
  5. QA on Linux, Windows and macOS builds, every tab and dialog.

Estimate: roughly 2–3 focused days, most of it items 2–3 and QA. Item 1 alone (Fusion + dark palette, no icon work) is about half a day and would already be usable, with some dark-on-dark icons. That could ship first as a stopgap.

Open question: does the background tile stay in Dark, or do we drop it?

## Scope Sized against `main` @ 3f047b7f. **Constraints** - Depends pins Qt **5.15.19**. `QStyleHints::colorScheme()` is Qt 6.5+, so "System" detection has to be per-platform code (Windows registry, macOS `NSApp.effectiveAppearance`). Linux falls back to Light unless the desktop palette is already dark. - This tree predates Bitcoin Core's `PlatformStyle` (0.12), which is what upstream uses to recolor icons. We'd backport a slim version of it. **Work items** 1. Theme setting + `-theme=` arg + Fusion/dark `QPalette` at startup (`dobbscoin.cpp`, `optionsmodel`, `optionsdialog.ui`). Small. 2. Hardcoded colors, ~20 sites: - `guiconstants.h` `COLOR_*`, used by `transactiontablemodel.cpp` and `overviewpage.cpp`. **`COLOR_BLACK` at `transactiontablemodel.cpp:482` gives black-on-dark text in the history tab.** This is the worst one. - `trafficgraphwidget.cpp` already draws on black, so it's fine as is - `splashscreen.cpp:53,125` - the `setStyleSheet` sites already listed above - `.ui` stylesheets: `color:#000000` and the `#F0D0A0→#F8D488` gradient in `overviewpage.ui`, `#808080`, three `red` 3. Icons: 46 PNGs across 39 `QIcon(":/icons/...")` call sites. Backport `PlatformStyle::SingleColorIcon` to recolor the monochrome ones at load. The colored ones (tx status, connection bars) probably stay as they are. Medium. 4. `bgtile` background: add a dark tile or skip it in Dark. Needs art, or just a decision. 5. QA on Linux, Windows and macOS builds, every tab and dialog. **Estimate:** roughly 2–3 focused days, most of it items 2–3 and QA. Item 1 alone (Fusion + dark palette, no icon work) is about half a day and would already be usable, with some dark-on-dark icons. That could ship first as a stopgap. **Open question:** does the background tile stay in Dark, or do we drop it?
Author
Owner

Revised: ship a dark image set instead of recoloring

Looking at the actual art, auto-recoloring (PlatformStyle::SingleColorIcon) is wrong for this wallet. Our icons are clip-art with interior linework: Dobbs heads, the golfer, the frog, the spider. A single-color tint turns them into flat white blobs. Hand-made dark variants keep the art.

Needs a dark variant (~21): the black line art and silhouettes
address-book, connect4_16, debugwindow, overview, receive, send, synced, transaction0, transaction2, transaction_conflicted, tx_inout, tx_input, tx_mined, tx_output, unit_bob, unit_mbob, unit_ubob, dobbscoin_testnet, plus the overview art overviewLeftTop, overviewLeftBottom, overviewRight and the bgtile background.

Fine as is: the colored icons (clocks, locks, add/remove/quit, eyes, key, edit/copy/paste, filesave, export, configure, connect0-3 bars, history, bob/dobbscoin coin), splash* (already on black), about, qrcode (keep it black on white so it scans).

Mechanism: put the variants in src/qt/res/icons-dark/ and images-dark/ under the same filenames, and add them to the qrc. A small themedIcon(name) helper returns :/icons-dark/name when Dark is active and that file exists, and falls back to :/icons/name otherwise. The 39 QIcon(":/icons/...") call sites switch to the helper. That's simpler than backporting PlatformStyle, and a missing dark file degrades gracefully. The .ui files reference icons directly, so those get re-set in code or moved to the helper.

Art first pass: for pure black-on-transparent line art, inverting to near-white (#e8e8e8, not pure white) is a scripted starting point. Then hand-touch the Dobbs heads and the overview art so the faces don't read as photo negatives.

This replaces work item 3 above. The estimate shifts from code to art: about 1 day of code, plus however long the art pass takes.

## Revised: ship a dark image set instead of recoloring Looking at the actual art, auto-recoloring (`PlatformStyle::SingleColorIcon`) is wrong for this wallet. Our icons are clip-art with interior linework: Dobbs heads, the golfer, the frog, the spider. A single-color tint turns them into flat white blobs. Hand-made dark variants keep the art. **Needs a dark variant (~21):** the black line art and silhouettes `address-book`, `connect4_16`, `debugwindow`, `overview`, `receive`, `send`, `synced`, `transaction0`, `transaction2`, `transaction_conflicted`, `tx_inout`, `tx_input`, `tx_mined`, `tx_output`, `unit_bob`, `unit_mbob`, `unit_ubob`, `dobbscoin_testnet`, plus the overview art `overviewLeftTop`, `overviewLeftBottom`, `overviewRight` and the `bgtile` background. **Fine as is:** the colored icons (clocks, locks, add/remove/quit, eyes, key, edit/copy/paste, filesave, export, configure, connect0-3 bars, history, `bob`/`dobbscoin` coin), `splash*` (already on black), `about`, `qrcode` (keep it black on white so it scans). **Mechanism:** put the variants in `src/qt/res/icons-dark/` and `images-dark/` under the same filenames, and add them to the qrc. A small `themedIcon(name)` helper returns `:/icons-dark/name` when Dark is active and that file exists, and falls back to `:/icons/name` otherwise. The 39 `QIcon(":/icons/...")` call sites switch to the helper. That's simpler than backporting PlatformStyle, and a missing dark file degrades gracefully. The `.ui` files reference icons directly, so those get re-set in code or moved to the helper. **Art first pass:** for pure black-on-transparent line art, inverting to near-white (`#e8e8e8`, not pure white) is a scripted starting point. Then hand-touch the Dobbs heads and the overview art so the faces don't read as photo negatives. This replaces work item 3 above. The estimate shifts from code to art: about 1 day of code, plus however long the art pass takes.
Author
Owner

Art pushed on branch feat/49-dark-mode-art (6f55433d): 22 dark variants in src/qt/res/icons-dark/ and images-dark/, inverted for the silhouettes and outlined for the Dobbs heads and overview drawings. Not wired into the qrc yet. The theme switch and loader come next on the same branch.

Art pushed on branch `feat/49-dark-mode-art` (6f55433d): 22 dark variants in `src/qt/res/icons-dark/` and `images-dark/`, inverted for the silhouettes and outlined for the Dobbs heads and overview drawings. Not wired into the qrc yet. The theme switch and loader come next on the same branch.
Author
Owner

Theme switch pushed on feat/49-dark-mode-art (b4c219f7).

  • Options > Display > Theme: System / Light / Dark, plus -theme= on the command line. It takes effect after a restart, like Language.
  • Dark uses Fusion with a dark palette. System follows the OS: the registry on Windows, and the platform palette on macOS and Linux. If the desktop is already dark, the native style is kept and only the artwork swaps.
  • All icon call sites and the .ui artwork go through GUIUtil::ThemedIcon()/ThemedResource(), which falls back to the original when a file has no dark variant.
  • Fixed for dark: the sync bar background, the intro error labels, and the debug console's command color.

Built and run on Linux (regtest, Xvfb), Overview tab:

Dark
dark

Light (unchanged)
light

Correction to the scope comment: COLOR_BLACK at transactiontablemodel.cpp:482 is a decoration fallback, not history text, so it was never the readability bug I called it.

Not yet checked: the other tabs and dialogs, Windows, macOS, and the 16px unit heads at real size.

Theme switch pushed on `feat/49-dark-mode-art` (b4c219f7). - Options > Display > **Theme**: System / Light / Dark, plus `-theme=` on the command line. It takes effect after a restart, like Language. - Dark uses Fusion with a dark palette. System follows the OS: the registry on Windows, and the platform palette on macOS and Linux. If the desktop is already dark, the native style is kept and only the artwork swaps. - All icon call sites and the .ui artwork go through `GUIUtil::ThemedIcon()`/`ThemedResource()`, which falls back to the original when a file has no dark variant. - Fixed for dark: the sync bar background, the intro error labels, and the debug console's command color. Built and run on Linux (regtest, Xvfb), Overview tab: Dark ![dark](https://git.subgenius.finance/attachments/e461833b-b891-47f2-89bd-8c480eb68ff6) Light (unchanged) ![light](https://git.subgenius.finance/attachments/67ef904d-4979-4fcc-ba98-8b02d7f082bc) **Correction to the scope comment:** `COLOR_BLACK` at `transactiontablemodel.cpp:482` is a decoration fallback, not history text, so it was never the readability bug I called it. **Not yet checked:** the other tabs and dialogs, Windows, macOS, and the 16px unit heads at real size.
Author
Owner

Dark theme, every tab, from feat/49-dark-mode-art @ 86893c1f. Linux, regtest wallet with 103 mined blocks and one send-to-self, started with -theme=dark.

86893c1f also fixes the Mining page's "Mine (BOB)" checkbox, which was invisible on the near-black tile.

Overview
overview

Send
send

Receive
receive

Mining (checkbox fixed)
mining

Transactions: immature amounts in brackets are dim gray, same as in Light.
transactions

Options > Display: the Theme row reads System because the -theme=dark command-line override wins, and the dialog lists that override underneath.
options

Debug console
console

Not yet checked: Windows, macOS, and the debug window's Information, Network Traffic and Peers tabs.

Dark theme, every tab, from `feat/49-dark-mode-art` @ 86893c1f. Linux, regtest wallet with 103 mined blocks and one send-to-self, started with `-theme=dark`. 86893c1f also fixes the Mining page's "Mine (BOB)" checkbox, which was invisible on the near-black tile. **Overview** ![overview](https://git.subgenius.finance/attachments/1c5e91d2-a624-4a6d-a64a-b1651a70f157) **Send** ![send](https://git.subgenius.finance/attachments/a7d913fc-8242-4ab3-9bba-ab6da5d26bea) **Receive** ![receive](https://git.subgenius.finance/attachments/c00a6f33-edbc-4fe1-bf0e-bde677bdb6f6) **Mining** (checkbox fixed) ![mining](https://git.subgenius.finance/attachments/132cfca5-b701-4982-ab64-a55368e98f66) **Transactions**: immature amounts in brackets are dim gray, same as in Light. ![transactions](https://git.subgenius.finance/attachments/59b6b3e2-c77d-4c8b-90ce-ce9bc590d1e1) **Options > Display**: the Theme row reads System because the `-theme=dark` command-line override wins, and the dialog lists that override underneath. ![options](https://git.subgenius.finance/attachments/bfb8ee08-213e-4f6b-9cf0-e01edabf47ed) **Debug console** ![console](https://git.subgenius.finance/attachments/d6f6a78f-b964-4111-b79e-b13b1263f627) Not yet checked: Windows, macOS, and the debug window's Information, Network Traffic and Peers tabs.
Author
Owner

Windows test build of PR #50 is up as a pre-release: https://git.subgenius.finance/SubGeniusFinance/dobbscoin-source/releases/tag/wallet-darkmode-20261110

Direct download: https://git.subgenius.finance/SubGeniusFinance/dobbscoin-source/releases/download/wallet-darkmode-20261110/dobbscoin-darkmode-20261110-86893c1f-win64.zip
sha256 6262ea24aedebc9046bc5ff647b79670f5c1f334de64b7830cf000bcb1f5c284

Options > Display > Theme, then restart. Please report here:

  • Does System follow your Windows app mode?
  • Is every tab and dialog readable in Dark?
  • Does Light look the same as v0.14.0?

Back up wallet.dat first, as with any test build. No installer: unzip and run dobbscoin-qt.exe. Help > About shows v0.14.0.0-86893c1f.

Windows test build of PR #50 is up as a pre-release: https://git.subgenius.finance/SubGeniusFinance/dobbscoin-source/releases/tag/wallet-darkmode-20261110 Direct download: https://git.subgenius.finance/SubGeniusFinance/dobbscoin-source/releases/download/wallet-darkmode-20261110/dobbscoin-darkmode-20261110-86893c1f-win64.zip sha256 `6262ea24aedebc9046bc5ff647b79670f5c1f334de64b7830cf000bcb1f5c284` Options > Display > Theme, then restart. Please report here: - Does **System** follow your Windows app mode? - Is every tab and dialog readable in **Dark**? - Does **Light** look the same as v0.14.0? Back up `wallet.dat` first, as with any test build. No installer: unzip and run `dobbscoin-qt.exe`. Help > About shows `v0.14.0.0-86893c1f`.
btcbob referenced this issue from a commit 2026-11-11 01:46:09 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
SubGeniusFinance/dobbscoin-source#49
No description provided.