diff --git a/src/app/appsettingsmanager.h b/src/app/appsettingsmanager.h index 074fb0cf85f76174fa27e4c0fc75304c361c8c8d..b0f17939c7357b93d85d2fd0a52430966742a0bd 100644 --- a/src/app/appsettingsmanager.h +++ b/src/app/appsettingsmanager.h @@ -45,6 +45,7 @@ extern const QString defaultDownloadPath; X(DisplayHyperlinkPreviews, true) \ X(EnableExperimentalSwarm, false) \ X(EnableDarkTheme, false) \ + X(AppTheme, "System") \ X(BaseZoom, 1.0) \ X(ParticipantsSide, false) \ X(HideSelf, false) \ diff --git a/src/app/constant/JamiStrings.qml b/src/app/constant/JamiStrings.qml index 32af891a0b8419283571ab281763231e5b6b30e6..adf297180718282ebbc37599834509c652263dd4 100644 --- a/src/app/constant/JamiStrings.qml +++ b/src/app/constant/JamiStrings.qml @@ -422,9 +422,11 @@ Item { // SystemSettings property string system: qsTr("System") + property string dark: qsTr("Dark") + property string light: qsTr("Light") property string selectFolder: qsTr("Select a folder") property string enableNotifications: qsTr("Enable notifications") - property string enableDarkTheme: qsTr("Enable dark theme") + property string applicationTheme: qsTr("Application theme") property string enableDesktopNotifications: qsTr("Enable desktop notifications") property string keepMinimized: qsTr("Keep minimized on close") property string tipRunStartup: qsTr("Run application on system startup") diff --git a/src/app/constant/JamiTheme.qml b/src/app/constant/JamiTheme.qml index 5a1cb4a73bc91a4aefce1a8173438f8e0973bbb9..f1b41da91a7af44fc1140701ddb9811a6fafdb41 100644 --- a/src/app/constant/JamiTheme.qml +++ b/src/app/constant/JamiTheme.qml @@ -25,7 +25,7 @@ import net.jami.Adapters 1.1 import net.jami.Enums 1.1 Item { - property bool darkTheme: UtilsAdapter.getAppValue(Settings.EnableDarkTheme) + property bool darkTheme: UtilsAdapter.useApplicationTheme() Connections { target: UtilsAdapter @@ -33,6 +33,10 @@ Item { function onChangeFontSize() { baseZoom = UtilsAdapter.getAppValue(Settings.BaseZoom) } + + function onAppThemeChanged() { + darkTheme = UtilsAdapter.useApplicationTheme() + } } // Jami theme colors diff --git a/src/app/settingsview/components/SystemSettings.qml b/src/app/settingsview/components/SystemSettings.qml index ec520c4340e5d4a24f0de20821d36a8bfdf46b3e..80ffcc4926b2b47d69da257ad8162ee9e889e877 100644 --- a/src/app/settingsview/components/SystemSettings.qml +++ b/src/app/settingsview/components/SystemSettings.qml @@ -64,21 +64,53 @@ ColumnLayout { verticalAlignment: Text.AlignVCenter } - ToggleSwitch { - id: darkThemeCheckBox + + + SettingsComboBox { + id: themeComboBoxSettings + Layout.fillWidth: true Layout.leftMargin: JamiTheme.preferredMarginSize - checked: UtilsAdapter.getAppValue(Settings.EnableDarkTheme) - - labelText: JamiStrings.enableDarkTheme + labelText: JamiStrings.applicationTheme fontPointSize: JamiTheme.settingsFontSize - tooltipText: JamiStrings.enableDarkTheme + comboModel: ListModel { + Component.onCompleted: { + append({ textDisplay: JamiStrings.dark }) + append({ textDisplay: JamiStrings.light }) + if (UtilsAdapter.hasNativeDarkTheme()) + append({ textDisplay: JamiStrings.system }) + } + } + widthOfComboBox: itemWidth + tipText: JamiStrings.selectAudioInputDevice + role: "textDisplay" + + modelIndex: { + if (UtilsAdapter.hasNativeDarkTheme()) { + var theme = UtilsAdapter.getAppValue(Settings.Key.AppTheme) + if (theme === "Dark") { + return 0 + } else if (theme === "Light") { + return 1 + } + return 2 + } + return UtilsAdapter.getAppValue(Settings.Key.EnableDarkTheme) ? 0 : 1 + } - onSwitchToggled: { - JamiTheme.setTheme(checked) - UtilsAdapter.setAppValue(Settings.Key.EnableDarkTheme, checked) + onActivated: { + if (UtilsAdapter.hasNativeDarkTheme()) { + if (modelIndex === 0) + UtilsAdapter.setAppValue(Settings.Key.AppTheme, "Dark") + else if (modelIndex === 1) + UtilsAdapter.setAppValue(Settings.Key.AppTheme, "Light") + else if (modelIndex === 2) + UtilsAdapter.setAppValue(Settings.Key.AppTheme, "System") + } else { + UtilsAdapter.setAppValue(Settings.Key.EnableDarkTheme, modelIndex === 0) + } } } diff --git a/src/app/utilsadapter.cpp b/src/app/utilsadapter.cpp index 6a21e1c16c551436834fccb1badd10254d70251d..d27c0f7095a797bb6b09f7787b17330ebc7ef8c3 100644 --- a/src/app/utilsadapter.cpp +++ b/src/app/utilsadapter.cpp @@ -375,6 +375,8 @@ UtilsAdapter::setAppValue(const Settings::Key key, const QVariant& value) Q_EMIT changeFontSize(); else if (key == Settings::Key::ShowChatviewHorizontally) Q_EMIT chatviewPositionChanged(); + else if (key == Settings::Key::AppTheme) + Q_EMIT appThemeChanged(); } QString @@ -584,3 +586,72 @@ UtilsAdapter::luma(const QColor& color) const return (0.2126 * color.red() + 0.7152 * color.green() + 0.0722 * color.blue()) < 153 /* .6 * 256 */; } + +#if __has_include(<gio/gio.h>) +void +settingsCallback(GSettings* self, gchar* key, gpointer user_data) +{ + QString keyString = key; + if (keyString == "color-scheme" || keyString == "gtk-theme") { + Q_EMIT((UtilsAdapter*) (user_data))->appThemeChanged(); + } +} +#endif + +bool +UtilsAdapter::isSystemThemeDark() +{ +#if __has_include(<gio/gio.h>) + if (!settings) { + settings = g_settings_new("org.gnome.desktop.interface"); + if (!settings) + return false; + g_signal_connect(settings, "changed", G_CALLBACK(settingsCallback), this); + } + if (!schema) { + g_object_get(settings, "settings-schema", &schema, nullptr); + if (!schema) + return false; + } + std::vector<std::string> keys = {"gtk-color-scheme", "color-scheme", "gtk-theme"}; + auto** gtk_keys = g_settings_schema_list_keys(schema); + for (const auto& key : keys) { + auto hasKey = false; + for (int i = 0; gtk_keys[i]; i++) { + if (key == gtk_keys[i]) { + hasKey = true; + break; + } + } + if (hasKey) { + if (auto* valueCstr = g_settings_get_string(settings, key.c_str())) { + QString value = valueCstr; + if (!value.isEmpty()) { + return value.contains("dark", Qt::CaseInsensitive) + || value.contains("black", Qt::CaseInsensitive); + } + } + } + } + return false; +#else + qWarning("System theme detection is not implemented"); + return false; +#endif +} + +bool +UtilsAdapter::useApplicationTheme() +{ + if (hasNativeDarkTheme()) { + QString theme = getAppValue(Settings::Key::AppTheme).toString(); + if (theme == "Dark") + return true; + else if (theme == "Light") + return false; + return isSystemThemeDark(); + } + bool enableDark = getAppValue(Settings::Key::EnableDarkTheme).toBool(); + setAppValue(Settings::Key::AppTheme, enableDark ? "Dark" : "Light"); + return enableDark; +} \ No newline at end of file diff --git a/src/app/utilsadapter.h b/src/app/utilsadapter.h index 9d7af80a844f391124b06ee7b7c6026fa0d2502e..b1c659e79a224cdf295ae8629c1b26b5608a4fcb 100644 --- a/src/app/utilsadapter.h +++ b/src/app/utilsadapter.h @@ -29,6 +29,10 @@ #include "appsettingsmanager.h" #include "qtutils.h" +#if __has_include(<gio/gio.h>) +#include <gio/gio.h> +#endif + class QClipboard; class SystemTray; @@ -107,12 +111,22 @@ public: const QString& convId, const QString& uri); Q_INVOKABLE bool luma(const QColor& color) const; + Q_INVOKABLE bool useApplicationTheme(); + Q_INVOKABLE bool hasNativeDarkTheme() const + { +#if __has_include(<gio/gio.h>) + return true; +#else + return false; +#endif + } Q_SIGNALS: void debugMessageReceived(const QString& message); void showExperimentalSwarm(); void changeFontSize(); void chatviewPositionChanged(); + void appThemeChanged(); private: QClipboard* clipboard_; @@ -121,5 +135,11 @@ private: QMetaObject::Connection debugMessageReceivedConnection_; QString getDefaultRecordPath() const; + + bool isSystemThemeDark(); +#if __has_include(<gio/gio.h>) + GSettings* settings {nullptr}; + GSettingsSchema* schema {nullptr}; +#endif }; Q_DECLARE_METATYPE(UtilsAdapter*)