diff --git a/CMakeLists.txt b/CMakeLists.txt index 011540c..359ddc5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,93 +1,92 @@ project(kde-gtk-config) set(PROJECT_VERSION "5.17.80") cmake_minimum_required(VERSION 2.8.12) find_package(ECM 0.0.9 REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules" ${ECM_MODULE_PATH}) find_package(Qt5 REQUIRED NO_MODULE COMPONENTS Widgets Svg Test) find_package(KF5 REQUIRED COMPONENTS I18n KIO ConfigWidgets NewStuff Archive KCMUtils IconThemes DBusAddons) -find_package(X11 REQUIRED) find_package(GTK3 REQUIRED) find_package(GSettingSchemas REQUIRED) include_directories( ${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/src/ui ${GTK3_INCLUDE_DIRS} ) include(ECMSetupVersion) include(ECMInstallIcons) include(ECMMarkAsTest) include(GenerateExportHeader) include(FeatureSummary) include(KDEInstallDirs) include(KDECMakeSettings) include(KDECompilerSettings NO_POLICY_SCOPE) include(KDEClangFormat) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h) # Set KI18n translation domain add_definitions(-DTRANSLATION_DOMAIN=\"kde-gtk-config\") set(kcm_SRCS src/appearancegtk3.cpp src/appearancegtk2.cpp src/appearencegtk.cpp src/abstractappearance.cpp src/thread.cpp src/installer.cpp src/gtkconfigkcmodule.cpp src/dialog_installer.cpp src/dialog_uninstaller.cpp ) ki18n_wrap_ui(kcm_SRCS src/ui/gui.ui src/ui/dialog_installer.ui src/ui/dialog_uninstaller.ui ) add_library(kcm_kdegtkconfig MODULE ${kcm_SRCS}) target_compile_definitions(kcm_kdegtkconfig PRIVATE -DPROJECT_VERSION="${PROJECT_VERSION}" -DQT_NO_SIGNALS_SLOTS_KEYWORDS ) target_link_libraries(kcm_kdegtkconfig ${GIO2_LIBRARY} ${GLIB2_LIBRARY} ${GTK3_LIBRARY} ${GOBJECT2_LIBRARY} Qt5::Svg KF5::ConfigCore KF5::I18n KF5::KIOWidgets KF5::NewStuff KF5::Archive KF5::ConfigWidgets KF5::IconThemes ) kcoreaddons_desktop_to_json(kcm_kdegtkconfig kde-gtk-config.desktop) install(TARGETS kcm_kdegtkconfig DESTINATION ${PLUGIN_INSTALL_DIR}) install(FILES cgctheme.knsrc cgcgtk3.knsrc DESTINATION ${KDE_INSTALL_KNSRCDIR}) install(FILES kde-gtk-config.desktop DESTINATION ${SERVICES_INSTALL_DIR}) add_subdirectory(gtkproxies) add_subdirectory(gtk3proxies) add_subdirectory(icons) add_subdirectory(tests) add_subdirectory(kded) # add clang-format target for all our real source files file(GLOB_RECURSE ALL_CLANG_FORMAT_SOURCE_FILES *.cpp *.h) kde_clang_format(${ALL_CLANG_FORMAT_SOURCE_FILES}) feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/kded/configvalueprovider.cpp b/kded/configvalueprovider.cpp index e77d23e..5e29a7a 100644 --- a/kded/configvalueprovider.cpp +++ b/kded/configvalueprovider.cpp @@ -1,148 +1,164 @@ /* * Copyright (C) 2019 Mikhail Zolotukhin * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include +#include #include #include #include #include #include "configvalueprovider.h" ConfigValueProvider::ConfigValueProvider() : kdeglobalsConfig(KSharedConfig::openConfig(QStringLiteral("kdeglobals"))), inputConfig(KSharedConfig::openConfig(QStringLiteral("kcminputrc"))) { } QString ConfigValueProvider::fontName() const { static const QFont defaultFont(QStringLiteral("Noto Sans"), 10); kdeglobalsConfig->reparseConfiguration(); KConfigGroup configGroup = kdeglobalsConfig->group(QStringLiteral("General")); QString fontAsString = configGroup.readEntry(QStringLiteral("font"), defaultFont.toString()); static QFont font; font.fromString(fontAsString); return font.family() + ' ' + font.styleName() + ' ' + QString::number(font.pointSize()); } QString ConfigValueProvider::iconThemeName() const { KIconTheme *newIconTheme = KIconLoader::global()->theme(); if (newIconTheme) { return newIconTheme->internalName(); } else { return QStringLiteral("breeze"); } } QString ConfigValueProvider::cursorThemeName() const { inputConfig->reparseConfiguration(); KConfigGroup configGroup = inputConfig->group(QStringLiteral("Mouse")); return configGroup.readEntry(QStringLiteral("cursorTheme"), QStringLiteral("breeze_cursors")); } QString ConfigValueProvider::iconsOnButtons() const { kdeglobalsConfig->reparseConfiguration(); KConfigGroup configGroup = kdeglobalsConfig->group(QStringLiteral("KDE")); bool kdeConfigValue = configGroup.readEntry(QStringLiteral("ShowIconsOnPushButtons"), true); if (kdeConfigValue) { return QStringLiteral("1"); } else { return QStringLiteral("0"); } } QString ConfigValueProvider::iconsInMenus() const { kdeglobalsConfig->reparseConfiguration(); KConfigGroup configGroup = kdeglobalsConfig->group(QStringLiteral("KDE")); bool kdeConfigValue = configGroup.readEntry(QStringLiteral("ShowIconsInMenuItems"), true); if (kdeConfigValue) { return QStringLiteral("1"); } else { return QStringLiteral("0"); } } QString ConfigValueProvider::toolbarStyle(ConfigValueProvider::ToolbarStyleNotation notation) const { kdeglobalsConfig->reparseConfiguration(); KConfigGroup configGroup = kdeglobalsConfig->group(QStringLiteral("Toolbar style")); QString kdeConfigValue = configGroup.readEntry(QStringLiteral("ToolButtonStyle"), "TextBesideIcon"); return toolbarStyleInDesiredNotation(kdeConfigValue, notation); } QString ConfigValueProvider::scrollbarBehavior() const { kdeglobalsConfig->reparseConfiguration(); KConfigGroup configGroup = kdeglobalsConfig->group(QStringLiteral("KDE")); bool kdeConfigValue = configGroup.readEntry(QStringLiteral("ScrollbarLeftClickNavigatesByPage"), true); if (kdeConfigValue) { // GTK setting is inverted return QStringLiteral("0"); } else { return QStringLiteral("1"); } } +QString ConfigValueProvider::preferDarkTheme() const +{ + kdeglobalsConfig->reparseConfiguration(); + KConfigGroup colorsConfigGroup = kdeglobalsConfig->group(QStringLiteral("Colors:Window")); + QColor windowBackgroundColor = colorsConfigGroup.readEntry(QStringLiteral("BackgroundNormal"), QColor(239, 240, 241)); + const int windowBackgroundGray = qGray(windowBackgroundColor.rgb()); + + // We use heuristic to determine if current color scheme is dark or not + if (windowBackgroundGray >= 192) { + return QStringLiteral("0"); + } else { + return QStringLiteral("1"); + } +} + QString ConfigValueProvider::toolbarStyleInDesiredNotation(const QString &kdeConfigValue, ConfigValueProvider::ToolbarStyleNotation notation) const { QStringList toolbarStyles {}; if (notation == ToolbarStyleNotation::SettingsIni) { toolbarStyles.append({ QStringLiteral("GTK_TOOLBAR_ICONS"), QStringLiteral("GTK_TOOLBAR_TEXT"), QStringLiteral("GTK_TOOLBAR_BOTH_HORIZ"), QStringLiteral("GTK_TOOLBAR_BOTH") }); } else if (notation == ToolbarStyleNotation::Xsettingsd) { toolbarStyles.append({ QStringLiteral("0"), QStringLiteral("1"), QStringLiteral("3"), QStringLiteral("2") }); } else { toolbarStyles.append({ QStringLiteral("icons"), QStringLiteral("text"), QStringLiteral("both-horiz"), QStringLiteral("both") }); } if (kdeConfigValue == QStringLiteral("NoText")) { return toolbarStyles[0]; } else if (kdeConfigValue == QStringLiteral("TextOnly")) { return toolbarStyles[1]; } else if (kdeConfigValue == QStringLiteral("TextBesideIcon")) { return toolbarStyles[2]; } else { return toolbarStyles[3]; } } diff --git a/kded/configvalueprovider.h b/kded/configvalueprovider.h index 494f14a..d398c3c 100644 --- a/kded/configvalueprovider.h +++ b/kded/configvalueprovider.h @@ -1,52 +1,53 @@ /* * Copyright (C) 2019 Mikhail Zolotukhin * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include class QString; class QFont; class ConfigValueProvider { public: enum class ToolbarStyleNotation { Xsettingsd = 0, SettingsIni, Dconf }; ConfigValueProvider(); QString fontName() const; QString iconThemeName() const; QString cursorThemeName() const; QString iconsOnButtons() const; QString iconsInMenus() const; QString toolbarStyle(ToolbarStyleNotation notation) const; QString scrollbarBehavior() const; + QString preferDarkTheme() const; private: QString toolbarStyleInDesiredNotation(const QString &kdeConfigValue, ToolbarStyleNotation notation) const; KSharedConfigPtr kdeglobalsConfig; KSharedConfigPtr inputConfig; }; diff --git a/kded/gtkconfig.cpp b/kded/gtkconfig.cpp index 344869a..3b4a366 100644 --- a/kded/gtkconfig.cpp +++ b/kded/gtkconfig.cpp @@ -1,145 +1,154 @@ /* * Copyright (C) 2019 Mikhail Zolotukhin * Copyright (C) 2019 Nicolas Fella * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include "gtkconfig.h" #include "configvalueprovider.h" K_PLUGIN_CLASS_WITH_JSON(GtkConfig, "gtkconfig.json") GtkConfig::GtkConfig(QObject *parent, const QVariantList&) : KDEDModule(parent), configValueProvider(new ConfigValueProvider()) { connect(qGuiApp, &QGuiApplication::fontChanged, this, &GtkConfig::setFont); connect(KIconLoader::global(), &KIconLoader::iconChanged, this, &GtkConfig::setIconTheme); QDBusConnection::sessionBus().connect(QString(), QStringLiteral("/KGlobalSettings"), QStringLiteral("org.kde.KGlobalSettings"), QStringLiteral("notifyChange"), this, SLOT(onGlobalSettingsChange(int,int))); applyAllSettings(); } void GtkConfig::setFont() const { const QString configFontName = configValueProvider->fontName(); ConfigEditor::setGtk2ConfigValue(QStringLiteral("gtk-font-name"), configFontName); ConfigEditor::setGtk3ConfigValueDconf(QStringLiteral("font-name"), configFontName); ConfigEditor::setGtk3ConfigValueSettingsIni(QStringLiteral("gtk-font-name"), configFontName); ConfigEditor::setGtk3ConfigValueXSettingsd(QStringLiteral("Gtk/FontName"), configFontName); } void GtkConfig::setIconTheme(int iconGroup) const { if (iconGroup == KIconLoader::Group::Desktop) { // This is needed to update icons only once const QString iconThemeName = configValueProvider->iconThemeName(); ConfigEditor::setGtk2ConfigValue(QStringLiteral("gtk-icon-theme-name"), iconThemeName); ConfigEditor::setGtk3ConfigValueDconf(QStringLiteral("icon-theme"), iconThemeName); ConfigEditor::setGtk3ConfigValueSettingsIni(QStringLiteral("gtk-icon-theme-name"), iconThemeName); ConfigEditor::setGtk3ConfigValueXSettingsd(QStringLiteral("Net/IconThemeName"), iconThemeName); } } void GtkConfig::setCursorTheme() const { const QString cursorThemeName = configValueProvider->cursorThemeName(); ConfigEditor::setGtk2ConfigValue(QStringLiteral("gtk-cursor-theme-name"), cursorThemeName); ConfigEditor::setGtk3ConfigValueDconf(QStringLiteral("cursor-theme"), cursorThemeName); ConfigEditor::setGtk3ConfigValueSettingsIni(QStringLiteral("gtk-cursor-theme-name"), cursorThemeName); ConfigEditor::setGtk3ConfigValueXSettingsd(QStringLiteral("Gtk/CursorThemeName"), cursorThemeName); } void GtkConfig::setIconsOnButtons() const { const QString iconsOnButtonsConfigValue = configValueProvider->iconsOnButtons(); ConfigEditor::setGtk2ConfigValue(QStringLiteral("gtk-button-images"), iconsOnButtonsConfigValue); ConfigEditor::setGtk3ConfigValueSettingsIni(QStringLiteral("gtk-button-images"), iconsOnButtonsConfigValue); ConfigEditor::setGtk3ConfigValueXSettingsd(QStringLiteral("Gtk/ButtonImages"), iconsOnButtonsConfigValue); } void GtkConfig::setIconsInMenus() const { const QString iconsInMenusConfigValue = configValueProvider->iconsInMenus(); ConfigEditor::setGtk2ConfigValue(QStringLiteral("gtk-menu-images"), iconsInMenusConfigValue); ConfigEditor::setGtk3ConfigValueSettingsIni(QStringLiteral("gtk-menu-images"), iconsInMenusConfigValue); ConfigEditor::setGtk3ConfigValueXSettingsd(QStringLiteral("Gtk/MenuImages"), iconsInMenusConfigValue); } void GtkConfig::setToolbarStyle() const { using ToolbarStyleNotation = ConfigValueProvider::ToolbarStyleNotation; QString toolbarStyleSettingsIni = configValueProvider->toolbarStyle(ToolbarStyleNotation::SettingsIni); QString toolbarStyleDConf = configValueProvider->toolbarStyle(ToolbarStyleNotation::Dconf); QString toolbarStyleXSettingsd = configValueProvider->toolbarStyle(ToolbarStyleNotation::Xsettingsd); ConfigEditor::setGtk2ConfigValue(QStringLiteral("gtk-toolbar-style"), toolbarStyleSettingsIni); ConfigEditor::setGtk3ConfigValueDconf(QStringLiteral("toolbar-style"), toolbarStyleDConf); ConfigEditor::setGtk3ConfigValueSettingsIni(QStringLiteral("gtk-toolbar-style"), toolbarStyleSettingsIni); ConfigEditor::setGtk3ConfigValueXSettingsd(QStringLiteral("Gtk/ToolbarStyle"), toolbarStyleXSettingsd); } void GtkConfig::setScrollbarBehavior() const { const QString scrollbarBehavior = configValueProvider->scrollbarBehavior(); ConfigEditor::setGtk2ConfigValue(QStringLiteral("gtk-primary-button-warps-slider"), scrollbarBehavior); ConfigEditor::setGtk3ConfigValueSettingsIni(QStringLiteral("gtk-primary-button-warps-slider"), scrollbarBehavior); ConfigEditor::setGtk3ConfigValueXSettingsd(QStringLiteral("Gtk/PrimaryButtonWarpsSlider"), scrollbarBehavior); } +void GtkConfig::setDarkThemePreference() const +{ + const QString preferDarkTheme = configValueProvider->preferDarkTheme(); + ConfigEditor::setGtk3ConfigValueSettingsIni(QStringLiteral("gtk-application-prefer-dark-theme"), preferDarkTheme); +} + void GtkConfig::applyAllSettings() const { setFont(); setIconTheme(KIconLoader::Group::Desktop); setCursorTheme(); setIconsOnButtons(); setIconsInMenus(); setToolbarStyle(); setScrollbarBehavior(); + setDarkThemePreference(); } void GtkConfig::onGlobalSettingsChange(int settingsChangeType, int arg) const { SettingsChangeType changeType = static_cast(settingsChangeType); SettingsCategory settingsCategory = static_cast(arg); if (changeType == SettingsChangeType::Cursor) { setCursorTheme(); } else if (changeType == SettingsChangeType::Settings && settingsCategory == SettingsCategory::Style) { setIconsOnButtons(); setIconsInMenus(); setToolbarStyle(); } else if (changeType == SettingsChangeType::Settings && settingsCategory == SettingsCategory::Mouse) { setScrollbarBehavior(); + } else if (changeType == SettingsChangeType::Palette) { + setDarkThemePreference(); } } #include "gtkconfig.moc" diff --git a/kded/gtkconfig.h b/kded/gtkconfig.h index 5dff563..8cabfab 100644 --- a/kded/gtkconfig.h +++ b/kded/gtkconfig.h @@ -1,74 +1,75 @@ /* * Copyright (C) 2019 Mikhail Zolotukhin * Copyright (C) 2019 Nicolas Fella * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include #include "configeditor.h" #include "configvalueprovider.h" class Q_DECL_EXPORT GtkConfig : public KDEDModule { Q_OBJECT enum class SettingsChangeType { Palette = 0, Font, Style, Settings, Icon, Cursor, ToolbarStyle, BlockShortcuts, NaturalSorting }; enum class SettingsCategory { Mouse, Completion, Paths, Popupmenu, Qt, Shortcuts, Locale, Style }; public: GtkConfig(QObject *parent, const QVariantList& args); void setFont() const; void setIconTheme(int iconGroup) const; void setCursorTheme() const; void setIconsOnButtons() const; void setIconsInMenus() const; void setToolbarStyle() const; void setScrollbarBehavior() const; + void setDarkThemePreference() const; void applyAllSettings() const; public Q_SLOTS: void onGlobalSettingsChange(int settingsChangeType, int arg) const; private: QScopedPointer configValueProvider; }; diff --git a/src/appearancegtk3.cpp b/src/appearancegtk3.cpp index 3034e96..7d8de22 100644 --- a/src/appearancegtk3.cpp +++ b/src/appearancegtk3.cpp @@ -1,155 +1,138 @@ /* KDE GTK Configuration Module * * Copyright 2011 José Antonio Sanchez Reynaga * Copyright 2011 Aleix Pol Gonzalez * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include #include #include #include #include #include #include -#include #include "appearancegtk3.h" QStringList AppearanceGTK3::installedThemes() const { QFileInfoList availableThemes; for (const QString& themesDir : QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("themes"), QStandardPaths::LocateDirectory)) { QDir root(themesDir); availableThemes += root.entryInfoList(QDir::NoDotAndDotDot | QDir::AllDirs); } // Also show the user-installed themes QDir user(QDir::homePath() + QStringLiteral("/.themes")); availableThemes += user.entryInfoList(QDir::NoDotAndDotDot | QDir::AllDirs); // We just want actual themes QStringList themes; // Check that the theme contains a gtk-3.* subdirectory QStringList gtk3SubdirPattern(QStringLiteral("gtk-3.*")); for (const QFileInfo &it : availableThemes) { QDir themeDir(it.filePath()); if(!themeDir.entryList(gtk3SubdirPattern, QDir::Dirs).isEmpty()) themes += it.filePath(); } return themes; } bool AppearanceGTK3::saveSettings(const KSharedConfig::Ptr& file) const { KConfigGroup group(file, QStringLiteral("Settings")); group.writeEntry(QStringLiteral("gtk-theme-name"), m_settings["theme"]); - group.writeEntry(QStringLiteral("gtk-application-prefer-dark-theme"), m_settings[QStringLiteral("application_prefer_dark_theme")]); const bool sync = group.sync(); Q_ASSERT(sync); return true; } bool AppearanceGTK3::loadSettings(const KSharedConfig::Ptr& file) { KConfigGroup group(file, QStringLiteral("Settings")); if (!file || !group.isValid()) { qWarning() << QStringLiteral("Cannot open the GTK3 config file") << file; return false; } - m_settings = QMap { - {QStringLiteral("application_prefer_dark_theme"), QStringLiteral("false")} - }; + m_settings.clear(); m_settings[QStringLiteral("theme")] = group.readEntry(QStringLiteral("gtk-theme-name")); - m_settings[QStringLiteral("application_prefer_dark_theme")] = group.readEntry(QStringLiteral("gtk-application-prefer-dark-theme")); for(auto it = m_settings.begin(); it != m_settings.end(); ) { if (it.value().isEmpty()) { it = m_settings.erase(it); } else { ++it; } } return true; } QString AppearanceGTK3::configFileName() const { return QStringLiteral("gtk-3.0/settings.ini"); } QString AppearanceGTK3::defaultConfigFile() const { QString root = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); if(root.isEmpty()) { root = QFileInfo(QDir::home(), QStringLiteral(".config")).absoluteFilePath(); } return root + '/' + configFileName(); } -bool AppearanceGTK3::getApplicationPreferDarkTheme() const -{ - return m_settings[QStringLiteral("application_prefer_dark_theme")] == QStringLiteral("1") || m_settings[QStringLiteral("application_prefer_dark_theme")] == QStringLiteral("true"); -} - -void AppearanceGTK3::setApplicationPreferDarkTheme(bool enable) -{ - m_settings[QStringLiteral("application_prefer_dark_theme")] = enable ? QStringLiteral("true") : QStringLiteral("false"); -} - bool AppearanceGTK3::saveSettings(const QString& file) const { auto cfg = KSharedConfig::openConfig(file, KConfig::NoGlobals); return saveSettings(cfg); } bool AppearanceGTK3::loadSettings(const QString& path) { auto cfg = KSharedConfig::openConfig(path, KConfig::NoGlobals); return loadSettings(cfg); } bool AppearanceGTK3::loadSettings() { auto cfg = KSharedConfig::openConfig(configFileName(), KConfig::NoGlobals); return loadSettings(cfg); } bool AppearanceGTK3::saveSettings() const { // FIXME kdebz#387417 // We should maybe use GSettings everywhere in future, but at this moment we // need this to have this configuration available in sandboxed applications which // is only possible through dconf g_autoptr(GSettings) gsettings = g_settings_new("org.gnome.desktop.interface"); g_settings_set_string(gsettings, "gtk-theme", m_settings["theme"].toUtf8().constData()); - g_object_set(gtk_settings_get_default(), "gtk-application-prefer-dark-theme", getApplicationPreferDarkTheme(), nullptr); - auto cfg = KSharedConfig::openConfig(configFileName(), KConfig::NoGlobals); return saveSettings(cfg); } diff --git a/src/appearancegtk3.h b/src/appearancegtk3.h index 3b6fbf5..50fba62 100644 --- a/src/appearancegtk3.h +++ b/src/appearancegtk3.h @@ -1,49 +1,47 @@ /* KDE GTK Configuration Module * * Copyright 2011 José Antonio Sanchez Reynaga * Copyright 2011 Aleix Pol Gonzalez * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef APPEARANCEGTK3_H #define APPEARANCEGTK3_H #include #include "abstractappearance.h" class AppearanceGTK3 : public AbstractAppearance { public: QStringList installedThemes() const override; bool saveSettings() const override; bool loadSettings() override; bool saveSettings(const QString &file) const override; bool loadSettings(const QString &path) override; - bool getApplicationPreferDarkTheme() const; - void setApplicationPreferDarkTheme(bool enable); private: QString defaultConfigFile() const; QString configFileName() const; bool saveSettings(const KSharedConfig::Ptr &file) const; bool loadSettings(const KSharedConfig::Ptr &file); }; #endif // APPEARANCEGTK3_H diff --git a/src/appearencegtk.cpp b/src/appearencegtk.cpp index 7dd8d0a..8808cf2 100644 --- a/src/appearencegtk.cpp +++ b/src/appearencegtk.cpp @@ -1,89 +1,79 @@ /* KDE GTK Configuration Module * * Copyright 2011 José Antonio Sanchez Reynaga * Copyright 2011 Aleix Pol Gonzalez * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include #include "abstractappearance.h" #include "appearencegtk.h" #include "appearancegtk2.h" #include "appearancegtk3.h" AppearenceGTK::AppearenceGTK() { m_app << new AppearanceGTK2; m_app << new AppearanceGTK3; } AppearenceGTK::~AppearenceGTK() { qDeleteAll(m_app); } QString AppearenceGTK::getTheme() const { return gtk2Appearance()->getTheme(); } void AppearenceGTK::setTheme(const QString& name) { return gtk2Appearance()->setTheme(name); } QString AppearenceGTK::getThemeGtk3() const { return gtk3Appearance()->getTheme(); } void AppearenceGTK::setThemeGtk3(const QString& name) { return gtk3Appearance()->setTheme(name); } -bool AppearenceGTK::getApplicationPreferDarkTheme() const -{ - return ((AppearanceGTK3*)gtk3Appearance())->getApplicationPreferDarkTheme(); -} - -void AppearenceGTK::setApplicationPreferDarkTheme(bool enable) -{ - return ((AppearanceGTK3*)gtk3Appearance())->setApplicationPreferDarkTheme(enable); -} - bool AppearenceGTK::loadFileConfig() { bool correct = false; for(AbstractAppearance *app : m_app) { bool c = app->loadSettings(); correct = correct || c; } return correct; } bool AppearenceGTK::saveFileConfig() { bool correct = true; for(AbstractAppearance *app : m_app) { bool c = app->saveSettings(); correct = correct && c; } return correct; } diff --git a/src/appearencegtk.h b/src/appearencegtk.h index e820f23..6eb82c3 100644 --- a/src/appearencegtk.h +++ b/src/appearencegtk.h @@ -1,59 +1,57 @@ /* KDE GTK Configuration Module * * Copyright 2011 José Antonio Sanchez Reynaga * Copyright 2011 Aleix Pol Gonzalez * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef APARIENCIAGTK_H #define APARIENCIAGTK_H #include #include #include class AbstractAppearance; /** * This class is responsible of administrating the GTK themes. It loads the * configurations from the .gtkrc-2.0 file. */ class AppearenceGTK { public: AppearenceGTK(); ~AppearenceGTK(); void setTheme(const QString &); void setThemeGtk3(const QString &theme); - void setApplicationPreferDarkTheme(bool enable); QString getTheme() const; QString getThemeGtk3() const; - bool getApplicationPreferDarkTheme() const; bool loadFileConfig(); bool saveFileConfig(); AbstractAppearance* gtk2Appearance() const { return m_app.first(); } AbstractAppearance* gtk3Appearance() const { return m_app.last(); } private: QVector m_app; }; #endif // APARIENCIAGTK_H diff --git a/src/gtkconfigkcmodule.cpp b/src/gtkconfigkcmodule.cpp index 1c576b5..70fac52 100644 --- a/src/gtkconfigkcmodule.cpp +++ b/src/gtkconfigkcmodule.cpp @@ -1,392 +1,386 @@ /* KDE GTK Configuration Module * * Copyright 2011 José Antonio Sanchez Reynaga * Copyright 2011 Aleix Pol Gonzalez * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "config.h" #include "ui_gui.h" #include "abstractappearance.h" #include "gtkconfigkcmodule.h" K_PLUGIN_FACTORY_WITH_JSON(GTKConfigKCModuleFactory, "kde-gtk-config.json", registerPlugin();) GTKConfigKCModule::GTKConfigKCModule(QWidget* parent, const QVariantList& args ) : KCModule(parent) , ui(new Ui::GUI) , installer(0) , uninstaller(0) , m_saveEnabled(true) { Q_UNUSED(args); KAboutData *acercade = new KAboutData("cgc", i18n("GTK Application Style"), PROJECT_VERSION, QString(), KAboutLicense::LGPL_V3, i18n("Copyright 2011 José Antonio Sánchez Reynaga")); acercade->addAuthor(i18n("José Antonio Sánchez Reynaga (antonioJASR)"),i18n("Main Developer"), "joanzare@gmail.com"); acercade->addAuthor(i18n("Aleix Pol i Gonzalez"), i18n("Feature development. Previews, code refactoring."), "aleixpol@blue-systems.com"); acercade->addCredit(i18n("Manuel Tortosa (manutortosa)"), i18n("Ideas, tester, internationalization")); acercade->addCredit(i18n("Adrián Chaves Fernández (Gallaecio)"), i18n("Internationalization")); setAboutData(acercade); setButtons(KCModule::Default | KCModule::Apply); ui->setupUi(this); appareance = new AppearenceGTK; m_tempGtk2Preview = QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/gtkrc-2.0"; m_tempGtk3Preview = QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/.config/gtk-3.0/settings.ini"; const QIcon previewIcon = QIcon::fromTheme("document-preview"); ui->gtk2Preview->setIcon(previewIcon); ui->gtk3Preview->setIcon(previewIcon); QString gtk2Preview = QStandardPaths::findExecutable("gtk_preview", {CMAKE_INSTALL_FULL_LIBEXECDIR}); QString gtk3Preview = QStandardPaths::findExecutable("gtk3_preview", {CMAKE_INSTALL_FULL_LIBEXECDIR}); m_p2 = new KProcess(this); m_p2->setEnv("GTK2_RC_FILES", m_tempGtk2Preview, true); if(!gtk2Preview.isEmpty()) { *m_p2 << gtk2Preview; connect(m_p2, SIGNAL(finished(int)), this, SLOT(untogglePreview())); } m_p3 = new KProcess(this); m_p3->setEnv("XDG_CONFIG_HOME", QStandardPaths::writableLocation(QStandardPaths::TempLocation)+"/.config"); if(!gtk3Preview.isEmpty()) { *m_p3 << gtk3Preview; connect(m_p3, SIGNAL(finished(int)), this, SLOT(untogglePreview())); } ui->gtk2Preview->setVisible(!gtk2Preview.isEmpty()); ui->gtk3Preview->setVisible(!gtk3Preview.isEmpty()); // UI changes connect(ui->cb_theme, SIGNAL(currentIndexChanged(int)), this, SLOT(appChanged())); connect(ui->cb_theme_gtk3, SIGNAL(currentIndexChanged(int)), this, SLOT(appChanged())); - connect(ui->checkBox_theme_gtk3_prefer_dark, &QAbstractButton::clicked, this, >KConfigKCModule::appChanged); // Preview updates connect(ui->gtk2Preview, &QAbstractButton::clicked, this, >KConfigKCModule::runGtk2IfNecessary); connect(ui->gtk3Preview, &QAbstractButton::clicked, this, >KConfigKCModule::runGtk3IfNecessary); QMenu *m = new QMenu(this); m->addAction(QIcon::fromTheme("get-hot-new-stuff"), i18n("Download GTK2 themes..."), this, >KConfigKCModule::showThemeGHNS); m->addAction(QIcon::fromTheme("get-hot-new-stuff"), i18n("Download GTK3 themes..."), this, >KConfigKCModule::installThemeGTK3GHNS); m->addAction(QIcon::fromTheme("archive-insert"), i18n("Install a local theme..."), this, >KConfigKCModule::showDialogForInstall); m->addAction(QIcon::fromTheme("archive-remove"), i18n("Uninstall a local theme..."), this, >KConfigKCModule::showDialogForUninstall); ui->newThemes->setMenu(m); ui->newThemes->setIcon(QIcon::fromTheme("get-hot-new-stuff")); } GTKConfigKCModule::~GTKConfigKCModule() { m_p2->kill(); m_p3->kill(); QFile::remove(m_tempGtk2Preview); QFile::remove(m_tempGtk3Preview); delete appareance; m_p2->waitForFinished(); m_p3->waitForFinished(); delete ui; } void GTKConfigKCModule::syncUI() { appareance->setThemeGtk3(ui->cb_theme_gtk3->currentText()); appareance->setTheme(ui->cb_theme->currentText()); - appareance->setApplicationPreferDarkTheme(ui->checkBox_theme_gtk3_prefer_dark->isChecked()); } void GTKConfigKCModule::showThemeGHNS() { KNS3::DownloadDialog d("cgctheme.knsrc", this); if(d.exec()) { refreshLists(); } } void GTKConfigKCModule::installThemeGTK3GHNS() { KNS3::DownloadDialog d("cgcgtk3.knsrc", this); if(d.exec()) { refreshLists(); } } void GTKConfigKCModule::refreshLists() { refreshThemesUi(true); } void GTKConfigKCModule::appChanged() { if (m_loading) { return; } savePreviewConfig(); emit changed(true); } void GTKConfigKCModule::savePreviewConfig() { if(!m_saveEnabled || !(ui->gtk2Preview->isChecked() || ui->gtk3Preview->isChecked())) { return; } syncUI(); if(ui->gtk3Preview->isChecked()) { // We don't want to recursively loop between savePreviewConfig and runIfNecessary m_saveEnabled = false; m_p3->kill(); appareance->gtk3Appearance()->saveSettings(m_tempGtk3Preview); // Need to make sure runIfNecessary() to know that it's not running m_p3->waitForFinished(); m_p3->start(); ui->gtk3Preview->setChecked(true); m_saveEnabled = true; } else if(ui->gtk2Preview->isChecked()) { appareance->gtk2Appearance()->saveSettings(m_tempGtk2Preview); } } void GTKConfigKCModule::runGtk2IfNecessary(bool checked) { KProcess* p = m_p2; KProcess* np = m_p3; if (checked) { np->kill(); np->waitForFinished(); savePreviewConfig(); if(p->state() == QProcess::NotRunning) { p->start(); } } else { p->kill(); p->waitForFinished(); } } void GTKConfigKCModule::runGtk3IfNecessary(bool checked) { KProcess* p = m_p3; KProcess* np = m_p2; if (checked) { np->kill(); np->waitForFinished(); savePreviewConfig(); if(p->state() == QProcess::NotRunning) { p->start(); } } else { p->kill(); p->waitForFinished(); } } void GTKConfigKCModule::save() { syncUI(); if(!appareance->saveFileConfig()) { KMessageBox::error(this, i18n("Failed to save configuration.")); } } void setComboItem(QComboBox* combo, const QStringList& texts) { for (const QString &text : texts) { int pos = combo->findText(text); if(pos >= 0) { combo->setCurrentIndex(pos); return; } } } void GTKConfigKCModule::defaults() { refreshThemesUi(false); m_saveEnabled = false; setComboItem(ui->cb_theme, QStringList("oxygen-gtk") << "Clearlooks"); setComboItem(ui->cb_theme_gtk3, QStringList("oxygen-gtk") << "Adwaita"); m_saveEnabled = true; appChanged(); } void GTKConfigKCModule::load() { m_saveEnabled = false; bool someCorrect = appareance->loadFileConfig(); m_loading = true; if(someCorrect) { refreshLists(); } else { defaults(); } m_loading = false; m_saveEnabled = true; } class MyStringListModel : public QAbstractListModel { public: MyStringListModel(const QStringList &texts, QObject* parent) : QAbstractListModel(parent), m_texts(texts) { } QVariant data(const QModelIndex & index, int role) const override { if (role != Qt::DisplayRole || !index.isValid() || index.row()>=m_texts.count()) { return {}; } return m_texts[index.row()]; } int rowCount(const QModelIndex &parent) const override { return parent.isValid() ? 0 : m_texts.count(); } void setStrings(const QSet &list) { const auto current = m_texts.toSet(); const auto oldRows = QSet(current).subtract(list); const auto newRows = QSet(list).subtract(current); if (!newRows.isEmpty()) { beginInsertRows({}, m_texts.count(), m_texts.count() + newRows.count()); m_texts += newRows.toList(); endInsertRows(); } int from = -1; for (const auto &row: oldRows) { for(; from < m_texts.count();) { const auto idx = m_texts.indexOf(row, from); if (idx < 0) { break; } beginRemoveRows({}, idx, idx); m_texts.removeAt(idx); endRemoveRows(); from = idx + 1; } } } private: QStringList m_texts; }; void refreshComboSameCurrentValue(QComboBox* combo, const QString& temp, const QStringList& texts) { const auto model = dynamic_cast(combo->model()); if (!model) { combo->setModel(new MyStringListModel(texts, combo)); } else { model->setStrings(texts.toSet()); } const int idx = combo->findText(temp); combo->setCurrentIndex(qMax(0, idx)); } void GTKConfigKCModule::refreshThemesUi(bool useConfig) { // Theme gtk2 bool wasenabled = m_saveEnabled; m_saveEnabled = false; refreshComboSameCurrentValue(ui->cb_theme, useConfig ? appareance->getTheme() : ui->cb_theme->currentText(), appareance->gtk2Appearance()->installedThemesNames()); // Theme gtk3 refreshComboSameCurrentValue(ui->cb_theme_gtk3, useConfig ? appareance->getThemeGtk3() : ui->cb_theme_gtk3->currentText(), appareance->gtk3Appearance()->installedThemesNames()); - // Dark theme for gtk3 - ui->checkBox_theme_gtk3_prefer_dark->setChecked(appareance->getApplicationPreferDarkTheme()); - - m_saveEnabled = wasenabled; emit changed(true); } void GTKConfigKCModule::showDialogForInstall() { if(!installer) { installer = new DialogInstaller(this); connect(installer, &DialogInstaller::themeInstalled, this, >KConfigKCModule::refreshLists); } installer->exec(); refreshThemesUi(); } void GTKConfigKCModule::showDialogForUninstall() { if(!uninstaller) { uninstaller = new DialogUninstaller(this, appareance); connect(uninstaller, &DialogUninstaller::themeUninstalled, this, >KConfigKCModule::refreshLists); } uninstaller->refreshListsForUninstall(); uninstaller->exec(); refreshThemesUi(); } void GTKConfigKCModule::untogglePreview() { if(sender() == m_p2) { ui->gtk2Preview->setChecked(false); } else { ui->gtk3Preview->setChecked(false); } } #include "gtkconfigkcmodule.moc" diff --git a/src/ui/gui.ui b/src/ui/gui.ui index 45510a7..ca70158 100644 --- a/src/ui/gui.ui +++ b/src/ui/gui.ui @@ -1,168 +1,158 @@ GUI 0 0 900 600 Qt::Horizontal 40 20 GTK2 theme: Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter QComboBox::AdjustToContents Preview Theme true GTK3 theme: Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter QComboBox::AdjustToContents Preview Theme true - - - - Prefer dark theme - - - Qt::Horizontal 40 20 Qt::Vertical 0 0 Qt::Horizontal 0 0 <html><head/><body><p>Here you can install new GTK themes. Even more are available at <a href="http://gnome-look.org"><span style=" text-decoration: underline;">gnome-look.org</span></a></p></body></html> Get New GNOME/GTK Application Styles... .. - - - diff --git a/tests/configsavetest.cpp b/tests/configsavetest.cpp index 37e4d38..7acbc14 100644 --- a/tests/configsavetest.cpp +++ b/tests/configsavetest.cpp @@ -1,71 +1,66 @@ /* * */ #include "configsavetest.h" #include #include #include QTEST_GUILESS_MAIN(ConfigSaveTest); ConfigSaveTest::ConfigSaveTest() { QStandardPaths::setTestModeEnabled(true); } static void fillValues(QScopedPointer& a) { a->setTheme("a"); - - auto a3 = dynamic_cast(a.data()); - if (a3) { - a3->setApplicationPreferDarkTheme(false); - } } void compareAppearances(QScopedPointer& reloaded, QScopedPointer& instance) { QCOMPARE(reloaded->getTheme(), instance->getTheme()); } QByteArray readFile(const QString& path) { QFile f(path); if(!f.open(QFile::ReadOnly | QFile::Text)) return QByteArray(); return f.readAll(); } void ConfigSaveTest::testGtk2() { const QString pathA = QDir::current().absoluteFilePath("test-gtk2") , pathB = QDir::current().absoluteFilePath("testB-gtk2"); QScopedPointer instance(new AppearanceGTK2); fillValues(instance); QVERIFY(instance->saveSettings(pathA)); QScopedPointer reloaded(new AppearanceGTK2); QVERIFY(reloaded->loadSettings(pathA)); compareAppearances(reloaded, instance); QVERIFY(reloaded->saveSettings(pathB)); QCOMPARE(readFile(pathA), readFile(pathB)); } void ConfigSaveTest::testGtk3() { QScopedPointer instance(new AppearanceGTK3); fillValues(instance); const QString pathA = QDir::current().absoluteFilePath("test-gtk3") , pathB = QDir::current().absoluteFilePath("testB-gtk3"); QVERIFY(instance->saveSettings(pathA)); QScopedPointer reloaded(new AppearanceGTK3); QVERIFY(QFile::exists(pathA)); QVERIFY(reloaded->loadSettings(pathA)); compareAppearances(reloaded, instance); QVERIFY(reloaded->saveSettings(pathB)); QCOMPARE(readFile(pathA), readFile(pathB)); }