From 7bf14813d7bec0d7e970b579552e9cf28d3c40a0 Mon Sep 17 00:00:00 2001 From: Juergen Spitzmueller Date: Sun, 10 Nov 2024 10:42:20 +0100 Subject: [PATCH] Introduce dark/light mode preference (#12224) This requires Qt 6.8 and only works on Win and Mac. --- lib/RELEASE-NOTES | 4 ++ lib/configure.py | 2 +- lib/scripts/prefs2prefs_prefs.py | 7 +++- src/LyXRC.cpp | 22 ++++++++++- src/LyXRC.h | 3 ++ src/frontends/qt/GuiApplication.cpp | 10 +++++ src/frontends/qt/GuiPrefs.cpp | 35 +++++++++++++++++ src/frontends/qt/GuiView.cpp | 10 +++-- src/frontends/qt/ui/PrefUi.ui | 61 ++++++++++++++++++----------- 9 files changed, 125 insertions(+), 29 deletions(-) diff --git a/lib/RELEASE-NOTES b/lib/RELEASE-NOTES index 03364a06fb..0ca44c7805 100644 --- a/lib/RELEASE-NOTES +++ b/lib/RELEASE-NOTES @@ -7,6 +7,10 @@ !!!The following pref variables were added in 2.5: +- \color_scheme {system,light,dark} allows to override the system-wide + color scheme (i.e., dark or light mode). This requires LyX to be built + against Qt >= 6.8.0. + !!!The following pref variables were changed in 2.5: !!!The following pref variables are obsoleted in 2.5: diff --git a/lib/configure.py b/lib/configure.py index 7c944e72b3..50497eca55 100644 --- a/lib/configure.py +++ b/lib/configure.py @@ -1982,7 +1982,7 @@ if __name__ == '__main__': lyx_check_config = True lyx_kpsewhich = True outfile = 'lyxrc.defaults' - lyxrc_fileformat = 38 + lyxrc_fileformat = 39 rc_entries = '' lyx_keep_temps = False version_suffix = '' diff --git a/lib/scripts/prefs2prefs_prefs.py b/lib/scripts/prefs2prefs_prefs.py index fe7e0a1ff4..5544c6aed2 100644 --- a/lib/scripts/prefs2prefs_prefs.py +++ b/lib/scripts/prefs2prefs_prefs.py @@ -170,6 +170,10 @@ # Add option to configure ui style # No conversion necessary. +# Incremented to format 39, by spitz +# Add \color_scheme {system|light|dark} +# No conversion necessary. + # NOTE: The format should also be updated in LYXRC.cpp and # in configure.py (search for lyxrc_fileformat). @@ -558,5 +562,6 @@ conversions = [ [ 35, [add_dark_color]], [ 36, [add_spellcheck_default]], [ 37, [remove_fullscreen_widthlimit]], - [ 38, []] + [ 38, []], + [ 39, []] ] diff --git a/src/LyXRC.cpp b/src/LyXRC.cpp index 60c7f23814..e130fbf584 100644 --- a/src/LyXRC.cpp +++ b/src/LyXRC.cpp @@ -60,7 +60,7 @@ namespace { // The format should also be updated in configure.py, and conversion code // should be added to prefs2prefs_prefs.py. -static unsigned int const LYXRC_FILEFORMAT = 38; // chillenb: screen_width and screen_limit +static unsigned int const LYXRC_FILEFORMAT = 39; // spitz: \color_scheme {system|light|dark} // when adding something to this array keep it sorted! LexerKeyword lyxrcTags[] = { { "\\accept_compound", LyXRC::RC_ACCEPT_COMPOUND }, @@ -81,6 +81,7 @@ LexerKeyword lyxrcTags[] = { { "\\citation_search_pattern", LyXRC::RC_CITATION_SEARCH_PATTERN }, { "\\citation_search_view", LyXRC::RC_CITATION_SEARCH_VIEW }, { "\\close_buffer_with_last_view", LyXRC::RC_CLOSE_BUFFER_WITH_LAST_VIEW }, + { "\\color_scheme", LyXRC::RC_COLOR_SCHEME }, { "\\completion_cursor_text", LyXRC::RC_COMPLETION_CURSOR_TEXT }, { "\\completion_inline_delay", LyXRC::RC_COMPLETION_INLINE_DELAY }, { "\\completion_inline_dots", LyXRC::RC_COMPLETION_INLINE_DOTS }, @@ -707,6 +708,11 @@ LyXRC::ReturnValues LyXRC::read(Lexer & lexrc, bool check_format) citation_search_view = lexrc.getString(); break; + case RC_COLOR_SCHEME: + if (lexrc.next()) + color_scheme = lexrc.getString(); + break; + case RC_CT_ADDITIONS_UNDERLINED: lexrc >> ct_additions_underlined; break; @@ -1704,6 +1710,15 @@ void LyXRC::write(ostream & os, bool ignore_system_lyxrc, string const & name) c if (tag != RC_LAST) break; // fall through + case RC_COLOR_SCHEME: + if (ignore_system_lyxrc || + color_scheme != system_lyxrc.color_scheme) { + os << "# Set color scheme (system|light|dark)\n"; + os << "\\color_scheme " << color_scheme << '\n'; + } + if (tag != RC_LAST) + break; + // fall through case RC_CT_ADDITIONS_UNDERLINED: if (ignore_system_lyxrc || ct_additions_underlined @@ -2926,6 +2941,7 @@ void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new) case LyXRC::RC_CITATION_SEARCH_PATTERN: case LyXRC::RC_CITATION_SEARCH_VIEW: case LyXRC::RC_CHECKLASTFILES: + case LyXRC::RC_COLOR_SCHEME: case LyXRC::RC_COMPLETION_CURSOR_TEXT: case LyXRC::RC_COMPLETION_INLINE_DELAY: case LyXRC::RC_COMPLETION_INLINE_DOTS: @@ -3210,6 +3226,10 @@ string const LyXRC::getDescription(LyXRCTags tag) str = _("Show a small box around a Math Macro with the macro name when the cursor is inside."); break; + case LyXRC::RC_COLOR_SCHEME: + str = _("Possibility to enforce a particular color scheme (system|dark|light)"); + break; + case RC_DEFFILE: str = _("Command definition file. Can either specify an absolute path, or LyX will look in its global and local commands/ directories."); break; diff --git a/src/LyXRC.h b/src/LyXRC.h index 8c34712d90..ed86e97da9 100644 --- a/src/LyXRC.h +++ b/src/LyXRC.h @@ -58,6 +58,7 @@ public: RC_CITATION_SEARCH, RC_CITATION_SEARCH_PATTERN, RC_CITATION_SEARCH_VIEW, + RC_COLOR_SCHEME, RC_COMPLETION_CURSOR_TEXT, RC_COMPLETION_INLINE_DELAY, RC_COMPLETION_INLINE_MATH, @@ -550,6 +551,8 @@ public: std::string forward_search_dvi; /// std::string forward_search_pdf; + /// specifiy color scheme (system|dark|light) + std::string color_scheme; /// int export_overwrite = NO_FILES; /// Default decimal point when aligning table columns on decimal diff --git a/src/frontends/qt/GuiApplication.cpp b/src/frontends/qt/GuiApplication.cpp index 65ed25013e..019088ea71 100644 --- a/src/frontends/qt/GuiApplication.cpp +++ b/src/frontends/qt/GuiApplication.cpp @@ -116,6 +116,9 @@ #include #include #include +#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)) +#include +#endif #include #include #include @@ -1256,6 +1259,13 @@ void Application::applyPrefs() { if (lyxrc.ui_style != "default") lyx::frontend::GuiApplication::setStyle(toqstr(lyxrc.ui_style)); +#if (defined(Q_OS_WIN) || defined(Q_CYGWIN_WIN) || defined(Q_OS_MAC)) && QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) + // Set color scheme from prefs + if (lyxrc.color_scheme == "dark") + guiApp->styleHints()->setColorScheme(Qt::ColorScheme::Dark); + else if (lyxrc.color_scheme == "light") + guiApp->styleHints()->setColorScheme(Qt::ColorScheme::Light); +#endif } FuncStatus GuiApplication::getStatus(FuncRequest const & cmd) const diff --git a/src/frontends/qt/GuiPrefs.cpp b/src/frontends/qt/GuiPrefs.cpp index 8a25e20451..f94eeeea90 100644 --- a/src/frontends/qt/GuiPrefs.cpp +++ b/src/frontends/qt/GuiPrefs.cpp @@ -64,6 +64,9 @@ #include #include #include +#if (defined(Q_OS_WIN) || defined(Q_CYGWIN_WIN) || defined(Q_OS_MAC)) && QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) +#include +#endif #include #include #include @@ -2523,6 +2526,10 @@ PrefUserInterface::PrefUserInterface(GuiPreferences * form) this, SIGNAL(changed())); connect(uiStyleCO, SIGNAL(activated(int)), this, SIGNAL(changed())); +#if (defined(Q_OS_WIN) || defined(Q_CYGWIN_WIN) || defined(Q_OS_MAC)) && QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) + connect(colorSchemeCO, SIGNAL(activated(int)), + this, SIGNAL(changed())); +#endif connect(useSystemThemeIconsCB, SIGNAL(clicked()), this, SIGNAL(changed())); connect(lastfilesSB, SIGNAL(valueChanged(int)), @@ -2545,6 +2552,15 @@ PrefUserInterface::PrefUserInterface(GuiPreferences * form) iconSetCO->addItem(qt_("Classic"), "classic"); iconSetCO->addItem(qt_("Oxygen"), "oxygen"); +#if (defined(Q_OS_WIN) || defined(Q_CYGWIN_WIN) || defined(Q_OS_MAC)) && QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) + colorSchemeCO->addItem(qt_("System Default"), "system"); + colorSchemeCO->addItem(qt_("Light Mode"), "light"); + colorSchemeCO->addItem(qt_("Dark Mode"), "dark"); +#else + colorSchemeCO->setVisible(false); + colorSchemeLA->setVisible(false); +#endif + uiStyleCO->addItem(qt_("Default"), toqstr("default")); for (auto const & style : QStyleFactory::keys()) uiStyleCO->addItem(style, style.toLower()); @@ -2575,6 +2591,19 @@ void PrefUserInterface::applyRC(LyXRC & rc) const else frontend::GuiApplication::setStyle(uistyle); } +#if (defined(Q_OS_WIN) || defined(Q_CYGWIN_WIN) || defined(Q_OS_MAC)) && QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) + QString const color_scheme = colorSchemeCO->itemData( + colorSchemeCO->currentIndex()).toString(); + if (rc.color_scheme != fromqstr(color_scheme)) { + if (lyxrc.color_scheme == "dark") + guiApp->styleHints()->setColorScheme(Qt::ColorScheme::Dark); + else if (lyxrc.color_scheme == "light") + guiApp->styleHints()->setColorScheme(Qt::ColorScheme::Light); + else + guiApp->styleHints()->unsetColorScheme(); + } + rc.color_scheme = fromqstr(color_scheme); +#endif rc.ui_file = internal_path(fromqstr(uiFileED->text())); rc.use_system_theme_icons = useSystemThemeIconsCB->isChecked(); @@ -2607,6 +2636,12 @@ void PrefUserInterface::updateRC(LyXRC const & rc) toggleToolbarsCB->setChecked(rc.full_screen_toolbars); toggleTabbarCB->setChecked(rc.full_screen_tabbar); toggleMenubarCB->setChecked(rc.full_screen_menubar); +#if (defined(Q_OS_WIN) || defined(Q_CYGWIN_WIN) || defined(Q_OS_MAC)) && QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) + int colorscheme = colorSchemeCO->findData(toqstr(rc.color_scheme)); + if (colorscheme < 0) + colorscheme = 0; + colorSchemeCO->setCurrentIndex(colorscheme); +#endif } diff --git a/src/frontends/qt/GuiView.cpp b/src/frontends/qt/GuiView.cpp index 4163affb52..29a450a8b4 100644 --- a/src/frontends/qt/GuiView.cpp +++ b/src/frontends/qt/GuiView.cpp @@ -1792,10 +1792,12 @@ bool GuiView::event(QEvent * e) // dark/light mode runtime switch support #if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) case QEvent::ThemeChange: { - guiApp->setPalette(guiApp->style()->standardPalette()); - // We need to update metrics here to avoid a crash (#12786) - theBufferList().changed(true); - refillToolbars(); + if (lyxrc.color_scheme != "dark" && lyxrc.color_scheme != "light") { + guiApp->setPalette(guiApp->style()->standardPalette()); + // We need to update metrics here to avoid a crash (#12786) + theBufferList().changed(true); + refillToolbars(); + } return QMainWindow::event(e); } #else diff --git a/src/frontends/qt/ui/PrefUi.ui b/src/frontends/qt/ui/PrefUi.ui index 877d5de4ca..59e40edba8 100644 --- a/src/frontends/qt/ui/PrefUi.ui +++ b/src/frontends/qt/ui/PrefUi.ui @@ -42,23 +42,22 @@ + + + + You can set a custom style here. Note that only certain styles may support dark mode, e.g. fusion on Windows. + + + + + + + + + - - - - Bro&wse... - - - - - - - The icon set to use. Warning: normal size of icons may be wrong until you save the preferences and restart LyX. - - - @@ -79,8 +78,12 @@ - - + + + + The icon set to use. Warning: normal size of icons may be wrong until you save the preferences and restart LyX. + + @@ -92,15 +95,29 @@ - - - - You can set a custom style here. Note that only certain styles may support dark mode, e.g. fusion on Windows. + + + + Bro&wse... - - + + + + You can override the system's color scheme here if the selected style supports multiple schemes. + + + + + + + C&olor scheme: + + + colorSchemeCO + +