Skip to content

Add a dark mode toggle - #47

Open
ascensionra wants to merge 1 commit into
secondfry:masterfrom
ascensionra:add-dark-mode
Open

ascensionra wants to merge 1 commit into
secondfry:masterfrom
ascensionra:add-dark-mode

Conversation

@ascensionra

Copy link
Copy Markdown

Summary

Adds a View > Dark Mode checkable menu action that switches the whole app to a dark Qt stylesheet, persisted across restarts via QSettings alongside the other preferences.

Context

The app only ever rendered in the OS's light/native widget style. A few widgets also hardcode light-theme colors directly (status message text, the four security-priority spinbox backgrounds, and the path-table row highlighting), so a naive global stylesheet would leave those specific widgets unreadable (e.g. black status text on a dark background).

Approach

  • New src/shortcircuit/view/theme.py holds the dark QSS and an apply_theme(app, dark) helper. It's hand-written (not uic-generated), unlike the other view/ modules.
  • On Windows, Qt stylesheets don't fully restyle some native-style controls (combo box/spin box arrows, checkboxes), so enabling dark mode also switches the base QStyle to Fusion, which is fully stylesheet-driven, and restores the original style when toggled back off. The original style name is stashed as a dynamic Qt property on the QApplication object (app.setProperty(...)) rather than module-level Python state, so it survives across calls without a global.
  • The three hardcoded-color widgets are made theme-aware individually instead of through the global stylesheet, because a widget's own setStyleSheet() call takes precedence over an ancestor/application stylesheet for the same widget in Qt's cascade, regardless of selector specificity:
    • _label_message (status/path messages) picks theme-appropriate green/red/neutral instead of hardcoded green/red/black.
    • The four priority spinboxes (colors set directly in the uic-generated gui_main.py) get explicit light/dark variants applied on toggle.
    • Path-table row text is now pinned to black, since the row backgrounds are intentionally fixed light pastels for security-class coding in both themes.
  • Menu bar already existed but was empty, so the View menu is a clean addition — no .ui regeneration needed.

Trade-offs

  • The dark palette is a single hand-tuned QSS, not derived from the OS theme — it won't auto-follow a Windows "switch with system" dark/light setting, only the explicit menu toggle.
  • I didn't touch the few spots that yapf would also reformat elsewhere in app.py (e.g. MappersDialog.__init__), since they're pre-existing and unrelated to this change — kept the diff scoped to dark mode.

Testing

  • yapf --diff and pylint (project configs) are clean on both changed files (10.00/10 on the new module).
  • Ran the real app (Python 3.10 + PySide2 5.15.2.1, per requirements.txt) headlessly, toggled dark mode at runtime, and ran an actual pathfind (Jita → Amarr) to populate the results table — confirmed menu, buttons, inputs, table, status bar, and the three previously-hardcoded-color widgets all render correctly and stay legible in both themes.
  • Built a standalone Windows executable via the existing build_win_installer.bat / PyInstaller spec to confirm the change doesn't break packaging.

Risks

Low — additive UI feature behind an opt-in toggle, defaults to off, no changes to pathfinding/networking logic.

Short Circuit only ever rendered with the OS's light/native widget
style. Several widgets also hardcode light-theme-only colors (status
message text, the four security-priority spinbox backgrounds, and the
path-table row highlighting), so simply applying a dark QApplication
stylesheet on top would leave those specific widgets unreadable
(e.g. black status text on a dark background).

This adds a View > Dark Mode checkable menu action that applies a
QSS dark theme app-wide, persisted via QSettings like the other
preferences. On Windows, Qt style sheets don't fully restyle some
native-style controls (combo box/spin box arrows, checkboxes), so
enabling dark mode also switches the base QStyle to Fusion, which is
stylesheet-driven, and restores the original style when toggled off.

The hardcoded-color widgets are made theme-aware individually rather
than through the global stylesheet: a widget's own setStyleSheet()
call takes precedence over an ancestor/application stylesheet for the
same widget in Qt's cascade, regardless of selector specificity, so
the four priority spinboxes (whose colors are set directly in the
generated gui_main.py from the .ui file) needed explicit light/dark
variants rather than relying on inheritance. The path-table row colors
are intentionally fixed light pastels for security-class coding in
both themes, so their text is now pinned to black instead of
inheriting the theme's (light) default foreground color.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants