Add dark mode (System / Light / Dark theme setting)#1040
Open
aniro wants to merge 6 commits intodail8859:masterfrom
Open
Add dark mode (System / Light / Dark theme setting)#1040aniro wants to merge 6 commits intodail8859:masterfrom
aniro wants to merge 6 commits intodail8859:masterfrom
Conversation
Implements a clean dark mode that drives all theming from a single ApplicationSettings::darkMode boolean. Live toggle: no restart required. The Lua side defines a `theme` table that is rebuilt from a `dark_mode` global injected by C++. SetStyle() sets STYLE_DEFAULT from theme and calls StyleClearAll, then auto-translates canonical light-mode colors (black text, white background) to theme equivalents per-style. Language `.lua` files therefore work in either mode without modification: the vast majority specify black on white, which gets converted, while deliberate syntax colors (blue keywords, green comments, etc.) pass through unchanged. EditorManager::applyEditorTheme() handles all Scintilla UI surfaces (margins, caret line, fold markers, line numbers, brace match) for both modes. NotepadNextApplication::refreshEditorTheme() updates the Lua theme and re-applies styles to every open editor when the setting changes. For Qt widgets, MainWindow::applyStyleSheet() switches between Fusion-based light and dark QPalettes and loads either npp.css or the new npp-dark.css for ADS dock tabs and the QuickFindWidget. No third-party stylesheet dependency. LuaConsoleDock takes ApplicationSettings and reapplies its lexer styles on darkModeChanged so the console's Lua syntax highlighting tracks the theme. A "Dark mode" checkbox is added to the GUI section of Preferences.
Lua SetStyle() calls StyleClearAll on language load, which resets STYLE_LINENUMBER, STYLE_BRACELIGHT, STYLE_BRACEBAD, and STYLE_INDENTGUIDE to STYLE_DEFAULT values, wiping the colors set by applyEditorTheme. Split applyEditorTheme into two methods: the full theme (including STYLE_DEFAULT + StyleClearAll) and a separate applyEditorNamedStyles for the colors that need re-applying after each language load. NotepadNextApplication::setEditorLanguage calls applyEditorNamedStyles after the Lua SetLanguage call so line numbers and brace match retain their themed colors regardless of which language is active.
Qt 6.5+ has Fusion::standardPalette() honor QStyleHints::colorScheme(), so on systems where the user has selected dark mode at the OS level (GNOME, KDE, etc.) Fusion::standardPalette() returns dark colors. Setting that as the application palette in light mode left menu bar, toolbar, and dock tabs dark while only the editor and stylesheet-targeted widgets switched to light. Define an explicit light palette parallel to the existing dark one so the palette is fully driven by the user-selected DarkMode setting and ignores the system color scheme. Verified visually: menu bar, toolbar, and ADS tabs render light when DarkMode=false and dark when DarkMode=true, regardless of the GNOME theme.
Replace the DarkMode boolean with a Theme enum (System / Light / Dark, default System). System mode reads QGuiApplication::styleHints()->colorScheme() and listens for colorSchemeChanged so the app follows the desktop environment's light/dark preference, including live changes (e.g. GNOME night-mode auto-switching at sunset). Light and Dark remain explicit overrides for users who want their editor independent of the desktop theme. ApplicationSettings exposes a single effectiveDarkMode() helper plus an effectiveDarkModeChanged(bool) signal that fires on either theme change or system color-scheme change while in System mode. All consumers (EditorManager, NotepadNextApplication, MainWindow, LuaConsoleDock) call effectiveDarkMode() and connect to that signal, so the resolution-to-dark/light logic stays in one place. Preferences gets a "Theme" combo box with three options replacing the previous checkbox.
In System theme mode, applyStyleSheet now applies QApplication::style()->standardPalette() instead of our hardcoded Fusion palette. Qt 6.5+ Fusion already adapts standardPalette to QStyleHints::colorScheme(), so this lets the Qt widgets follow the desktop environment's palette rather than imposing our own interpretation of "dark" or "light". Light and Dark explicit modes still use the hardcoded palettes since the user has chosen to override the system, and Qt's standardPalette would leak through the system color scheme there. Scintilla editor surfaces still resolve through effectiveDarkMode() since they cannot read the Qt palette directly.
Stop forcing QApplication::setStyle(Fusion) globally and skip QApplication::setPalette() entirely when the user has selected System theme. Qt 6.5+ already syncs the palette with QStyleHints::colorScheme() and the platform integration picks the right style on its own; re-applying both on every applyStyleSheet call only re-emits paletteChange events and overrides legitimate platform choices. Light and Dark explicit modes still apply their hardcoded Fusion-derived palettes since the user has chosen to override the system there.
Author
This was referenced May 5, 2026
Closed
Closed
Closed
Closed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Summary
Adds dark mode to NotepadNext via a tristate System / Light / Dark theme setting (default System). When set to System, the application follows the desktop environment's color scheme via `QStyleHints::colorScheme()` and updates live when the system preference changes. Light and Dark are explicit overrides for users who want their editor independent of the rest of the desktop.
This is an alternative take on #411 — written from scratch against current master rather than the older qmake codebase that PR was built on, and avoiding the third-party stylesheet dependency. #411 can be closed if this one is preferred.
Design
A single `ApplicationSettings::theme` enum drives everything. `effectiveDarkMode()` resolves System against the current `QStyleHints::colorScheme()`. A single `effectiveDarkModeChanged(bool)` signal fires on either user-initiated theme changes or system color-scheme changes (when in System mode), and every consumer connects to that one signal.
Theming layers:
Qt widgets. `MainWindow::applyStyleSheet()` switches between Qt's natural palette (System mode — left untouched, since Qt 6.5+ already follows the desktop) and explicit Fusion-derived palettes (Light / Dark mode). Targeted CSS in `stylesheets/npp.css` (existing, light) and the new `stylesheets/npp-dark.css` (dark) covers ADS dock tabs, status bar, and `QuickFindWidget`. No third-party stylesheet dependency.
Scintilla editor surfaces. `EditorManager::applyEditorTheme()` sets fold markers, element colors (caret line, selection inactive, whitespace, fold line), fold margin colors, and `STYLE_DEFAULT`. A separate `applyEditorNamedStyles()` sets `STYLE_LINENUMBER`, `STYLE_BRACELIGHT`, `STYLE_BRACEBAD`, `STYLE_INDENTGUIDE`. The split is needed because Lua's `SetStyle` calls `StyleClearAll` on every language load, which would otherwise wipe these named styles.
Syntax highlighting. `init.lua` builds a `theme` table from a `dark_mode` global injected by C++. `SetStyle()` writes `STYLE_DEFAULT` from the theme, calls `StyleClearAll`, then auto-translates canonical light-mode colors per language style: `style.fgColor == light_fg → theme.default_fg` and `style.bgColor == light_bg → theme.default_bg`. The vast majority of language `.lua` files specify black on white and get auto-converted; deliberate syntax colors (blue keywords, green comments, gray strings, orange numbers) pass through unchanged and remain readable on a dark background. No language definition files were edited.
`LuaConsoleDock`. Now takes `ApplicationSettings*`, applies theme-aware Lua lexer styles, and reapplies them on `effectiveDarkModeChanged`.
Preferences UI. New "Theme" combo box (Follow system / Light / Dark) in the GUI tab.
Files
Verification done
Things explicitly NOT addressed
Test plan