Introduce dark/light mode preference (#12224)

This requires Qt 6.8 and only works on Win and Mac.
This commit is contained in:
Juergen Spitzmueller 2024-11-10 10:42:20 +01:00
parent 25cc10a04c
commit 7bf14813d7
9 changed files with 125 additions and 29 deletions

View File

@ -7,6 +7,10 @@
!!!The following pref variables were added in 2.5: !!!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 were changed in 2.5:
!!!The following pref variables are obsoleted in 2.5: !!!The following pref variables are obsoleted in 2.5:

View File

@ -1982,7 +1982,7 @@ if __name__ == '__main__':
lyx_check_config = True lyx_check_config = True
lyx_kpsewhich = True lyx_kpsewhich = True
outfile = 'lyxrc.defaults' outfile = 'lyxrc.defaults'
lyxrc_fileformat = 38 lyxrc_fileformat = 39
rc_entries = '' rc_entries = ''
lyx_keep_temps = False lyx_keep_temps = False
version_suffix = '' version_suffix = ''

View File

@ -170,6 +170,10 @@
# Add option to configure ui style # Add option to configure ui style
# No conversion necessary. # 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 # NOTE: The format should also be updated in LYXRC.cpp and
# in configure.py (search for lyxrc_fileformat). # in configure.py (search for lyxrc_fileformat).
@ -558,5 +562,6 @@ conversions = [
[ 35, [add_dark_color]], [ 35, [add_dark_color]],
[ 36, [add_spellcheck_default]], [ 36, [add_spellcheck_default]],
[ 37, [remove_fullscreen_widthlimit]], [ 37, [remove_fullscreen_widthlimit]],
[ 38, []] [ 38, []],
[ 39, []]
] ]

View File

@ -60,7 +60,7 @@ namespace {
// The format should also be updated in configure.py, and conversion code // The format should also be updated in configure.py, and conversion code
// should be added to prefs2prefs_prefs.py. // 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! // when adding something to this array keep it sorted!
LexerKeyword lyxrcTags[] = { LexerKeyword lyxrcTags[] = {
{ "\\accept_compound", LyXRC::RC_ACCEPT_COMPOUND }, { "\\accept_compound", LyXRC::RC_ACCEPT_COMPOUND },
@ -81,6 +81,7 @@ LexerKeyword lyxrcTags[] = {
{ "\\citation_search_pattern", LyXRC::RC_CITATION_SEARCH_PATTERN }, { "\\citation_search_pattern", LyXRC::RC_CITATION_SEARCH_PATTERN },
{ "\\citation_search_view", LyXRC::RC_CITATION_SEARCH_VIEW }, { "\\citation_search_view", LyXRC::RC_CITATION_SEARCH_VIEW },
{ "\\close_buffer_with_last_view", LyXRC::RC_CLOSE_BUFFER_WITH_LAST_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_cursor_text", LyXRC::RC_COMPLETION_CURSOR_TEXT },
{ "\\completion_inline_delay", LyXRC::RC_COMPLETION_INLINE_DELAY }, { "\\completion_inline_delay", LyXRC::RC_COMPLETION_INLINE_DELAY },
{ "\\completion_inline_dots", LyXRC::RC_COMPLETION_INLINE_DOTS }, { "\\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(); citation_search_view = lexrc.getString();
break; break;
case RC_COLOR_SCHEME:
if (lexrc.next())
color_scheme = lexrc.getString();
break;
case RC_CT_ADDITIONS_UNDERLINED: case RC_CT_ADDITIONS_UNDERLINED:
lexrc >> ct_additions_underlined; lexrc >> ct_additions_underlined;
break; break;
@ -1704,6 +1710,15 @@ void LyXRC::write(ostream & os, bool ignore_system_lyxrc, string const & name) c
if (tag != RC_LAST) if (tag != RC_LAST)
break; break;
// fall through // 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: case RC_CT_ADDITIONS_UNDERLINED:
if (ignore_system_lyxrc || if (ignore_system_lyxrc ||
ct_additions_underlined 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_PATTERN:
case LyXRC::RC_CITATION_SEARCH_VIEW: case LyXRC::RC_CITATION_SEARCH_VIEW:
case LyXRC::RC_CHECKLASTFILES: case LyXRC::RC_CHECKLASTFILES:
case LyXRC::RC_COLOR_SCHEME:
case LyXRC::RC_COMPLETION_CURSOR_TEXT: case LyXRC::RC_COMPLETION_CURSOR_TEXT:
case LyXRC::RC_COMPLETION_INLINE_DELAY: case LyXRC::RC_COMPLETION_INLINE_DELAY:
case LyXRC::RC_COMPLETION_INLINE_DOTS: 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."); str = _("Show a small box around a Math Macro with the macro name when the cursor is inside.");
break; break;
case LyXRC::RC_COLOR_SCHEME:
str = _("Possibility to enforce a particular color scheme (system|dark|light)");
break;
case RC_DEFFILE: case RC_DEFFILE:
str = _("Command definition file. Can either specify an absolute path, or LyX will look in its global and local commands/ directories."); str = _("Command definition file. Can either specify an absolute path, or LyX will look in its global and local commands/ directories.");
break; break;

View File

@ -58,6 +58,7 @@ public:
RC_CITATION_SEARCH, RC_CITATION_SEARCH,
RC_CITATION_SEARCH_PATTERN, RC_CITATION_SEARCH_PATTERN,
RC_CITATION_SEARCH_VIEW, RC_CITATION_SEARCH_VIEW,
RC_COLOR_SCHEME,
RC_COMPLETION_CURSOR_TEXT, RC_COMPLETION_CURSOR_TEXT,
RC_COMPLETION_INLINE_DELAY, RC_COMPLETION_INLINE_DELAY,
RC_COMPLETION_INLINE_MATH, RC_COMPLETION_INLINE_MATH,
@ -550,6 +551,8 @@ public:
std::string forward_search_dvi; std::string forward_search_dvi;
/// ///
std::string forward_search_pdf; std::string forward_search_pdf;
/// specifiy color scheme (system|dark|light)
std::string color_scheme;
/// ///
int export_overwrite = NO_FILES; int export_overwrite = NO_FILES;
/// Default decimal point when aligning table columns on decimal /// Default decimal point when aligning table columns on decimal

View File

@ -116,6 +116,9 @@
#include <QSortFilterProxyModel> #include <QSortFilterProxyModel>
#include <QStandardItemModel> #include <QStandardItemModel>
#include <QStyle> #include <QStyle>
#if (QT_VERSION >= QT_VERSION_CHECK(6, 8, 0))
#include <QStyleHints>
#endif
#include <QSvgRenderer> #include <QSvgRenderer>
#include <QTimer> #include <QTimer>
#include <QTranslator> #include <QTranslator>
@ -1256,6 +1259,13 @@ void Application::applyPrefs()
{ {
if (lyxrc.ui_style != "default") if (lyxrc.ui_style != "default")
lyx::frontend::GuiApplication::setStyle(toqstr(lyxrc.ui_style)); 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 FuncStatus GuiApplication::getStatus(FuncRequest const & cmd) const

View File

@ -64,6 +64,9 @@
#include <QSpinBox> #include <QSpinBox>
#include <QString> #include <QString>
#include <QStyleFactory> #include <QStyleFactory>
#if (defined(Q_OS_WIN) || defined(Q_CYGWIN_WIN) || defined(Q_OS_MAC)) && QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
#include <QStyleHints>
#endif
#include <QTreeWidget> #include <QTreeWidget>
#include <QTreeWidgetItem> #include <QTreeWidgetItem>
#include <QValidator> #include <QValidator>
@ -2523,6 +2526,10 @@ PrefUserInterface::PrefUserInterface(GuiPreferences * form)
this, SIGNAL(changed())); this, SIGNAL(changed()));
connect(uiStyleCO, SIGNAL(activated(int)), connect(uiStyleCO, SIGNAL(activated(int)),
this, SIGNAL(changed())); 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()), connect(useSystemThemeIconsCB, SIGNAL(clicked()),
this, SIGNAL(changed())); this, SIGNAL(changed()));
connect(lastfilesSB, SIGNAL(valueChanged(int)), connect(lastfilesSB, SIGNAL(valueChanged(int)),
@ -2545,6 +2552,15 @@ PrefUserInterface::PrefUserInterface(GuiPreferences * form)
iconSetCO->addItem(qt_("Classic"), "classic"); iconSetCO->addItem(qt_("Classic"), "classic");
iconSetCO->addItem(qt_("Oxygen"), "oxygen"); 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")); uiStyleCO->addItem(qt_("Default"), toqstr("default"));
for (auto const & style : QStyleFactory::keys()) for (auto const & style : QStyleFactory::keys())
uiStyleCO->addItem(style, style.toLower()); uiStyleCO->addItem(style, style.toLower());
@ -2575,6 +2591,19 @@ void PrefUserInterface::applyRC(LyXRC & rc) const
else else
frontend::GuiApplication::setStyle(uistyle); 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.ui_file = internal_path(fromqstr(uiFileED->text()));
rc.use_system_theme_icons = useSystemThemeIconsCB->isChecked(); rc.use_system_theme_icons = useSystemThemeIconsCB->isChecked();
@ -2607,6 +2636,12 @@ void PrefUserInterface::updateRC(LyXRC const & rc)
toggleToolbarsCB->setChecked(rc.full_screen_toolbars); toggleToolbarsCB->setChecked(rc.full_screen_toolbars);
toggleTabbarCB->setChecked(rc.full_screen_tabbar); toggleTabbarCB->setChecked(rc.full_screen_tabbar);
toggleMenubarCB->setChecked(rc.full_screen_menubar); 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
} }

View File

@ -1792,10 +1792,12 @@ bool GuiView::event(QEvent * e)
// dark/light mode runtime switch support // dark/light mode runtime switch support
#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) #if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
case QEvent::ThemeChange: { case QEvent::ThemeChange: {
if (lyxrc.color_scheme != "dark" && lyxrc.color_scheme != "light") {
guiApp->setPalette(guiApp->style()->standardPalette()); guiApp->setPalette(guiApp->style()->standardPalette());
// We need to update metrics here to avoid a crash (#12786) // We need to update metrics here to avoid a crash (#12786)
theBufferList().changed(true); theBufferList().changed(true);
refillToolbars(); refillToolbars();
}
return QMainWindow::event(e); return QMainWindow::event(e);
} }
#else #else

View File

@ -42,23 +42,22 @@
</item> </item>
<item row="0" column="0"> <item row="0" column="0">
<layout class="QGridLayout" name="gridLayout" columnstretch="0,1,0"> <layout class="QGridLayout" name="gridLayout" columnstretch="0,1,0">
<item row="2" column="1">
<widget class="QComboBox" name="uiStyleCO">
<property name="toolTip">
<string>You can set a custom style here. Note that only certain styles may support dark mode, e.g. fusion on Windows.</string>
</property>
</widget>
</item>
<item row="2" column="2">
<layout class="QHBoxLayout" name="horizontalLayout_2"/>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="uiFileED"/>
</item>
<item row="1" column="2"> <item row="1" column="2">
<layout class="QHBoxLayout" name="horizontalLayout"/> <layout class="QHBoxLayout" name="horizontalLayout"/>
</item> </item>
<item row="0" column="2">
<widget class="QPushButton" name="uiFilePB">
<property name="text">
<string>Bro&amp;wse...</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="iconSetCO">
<property name="toolTip">
<string>The icon set to use. Warning: normal size of icons may be wrong until you save the preferences and restart LyX.</string>
</property>
</widget>
</item>
<item row="1" column="0"> <item row="1" column="0">
<widget class="QLabel" name="iconSetLA"> <widget class="QLabel" name="iconSetLA">
<property name="text"> <property name="text">
@ -79,8 +78,12 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="0" column="1"> <item row="1" column="1">
<widget class="QLineEdit" name="uiFileED"/> <widget class="QComboBox" name="iconSetCO">
<property name="toolTip">
<string>The icon set to use. Warning: normal size of icons may be wrong until you save the preferences and restart LyX.</string>
</property>
</widget>
</item> </item>
<item row="2" column="0"> <item row="2" column="0">
<widget class="QLabel" name="uiStyleLA"> <widget class="QLabel" name="uiStyleLA">
@ -92,15 +95,29 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="1"> <item row="0" column="2">
<widget class="QComboBox" name="uiStyleCO"> <widget class="QPushButton" name="uiFilePB">
<property name="toolTip"> <property name="text">
<string>You can set a custom style here. Note that only certain styles may support dark mode, e.g. fusion on Windows.</string> <string>Bro&amp;wse...</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="2"> <item row="3" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_2"/> <widget class="QComboBox" name="colorSchemeCO">
<property name="toolTip">
<string>You can override the system's color scheme here if the selected style supports multiple schemes.</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="colorSchemeLA">
<property name="text">
<string>C&amp;olor scheme:</string>
</property>
<property name="buddy">
<cstring>colorSchemeCO</cstring>
</property>
</widget>
</item> </item>
</layout> </layout>
</item> </item>