diff --git a/kcms/lookandfeel/kcm.cpp b/kcms/lookandfeel/kcm.cpp index 6cc785cbb..bf2159335 100644 --- a/kcms/lookandfeel/kcm.cpp +++ b/kcms/lookandfeel/kcm.cpp @@ -1,817 +1,825 @@ /* This file is part of the KDE Project Copyright (c) 2014 Marco Martin Copyright (c) 2014 Vishesh Handa This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kcm.h" #include "../krdb/krdb.h" #include "../cursortheme/xcursor/xcursortheme.h" #include "config-kcm.h" #include "config-workspace.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_XFIXES # include #endif KCMLookandFeel::KCMLookandFeel(QObject* parent, const QVariantList& args) : KQuickAddons::ConfigModule(parent, args) , m_config(QStringLiteral("kdeglobals")) , m_configGroup(m_config.group("KDE")) , m_applyColors(true) , m_applyWidgetStyle(true) , m_applyIcons(true) , m_applyPlasmaTheme(true) , m_applyCursors(true) , m_applyWindowSwitcher(true) , m_applyDesktopSwitcher(true) , m_resetDefaultLayout(false) , m_applyWindowDecoration(true) { //This flag seems to be needed in order for QQuickWidget to work //see https://bugreports.qt-project.org/browse/QTBUG-40765 //also, it seems to work only if set in the kcm, not in the systemsettings' main qApp->setAttribute(Qt::AA_DontCreateNativeWidgetSiblings); qmlRegisterType(); qmlRegisterType(); KAboutData* about = new KAboutData(QStringLiteral("kcm_lookandfeel"), i18n("Configure Look and Feel details"), QStringLiteral("0.1"), QString(), KAboutLicense::LGPL); about->addAuthor(i18n("Marco Martin"), QString(), QStringLiteral("mart@kde.org")); setAboutData(about); setButtons(Apply | Default); m_model = new QStandardItemModel(this); QHash roles = m_model->roleNames(); roles[PluginNameRole] = "pluginName"; + roles[DescriptionRole] = "description"; roles[ScreenhotRole] = "screenshot"; roles[FullScreenPreviewRole] = "fullScreenPreview"; roles[HasSplashRole] = "hasSplash"; roles[HasLockScreenRole] = "hasLockScreen"; roles[HasRunCommandRole] = "hasRunCommand"; roles[HasLogoutRole] = "hasLogout"; roles[HasColorsRole] = "hasColors"; roles[HasWidgetStyleRole] = "hasWidgetStyle"; roles[HasIconsRole] = "hasIcons"; roles[HasPlasmaThemeRole] = "hasPlasmaTheme"; roles[HasCursorsRole] = "hasCursors"; roles[HasWindowSwitcherRole] = "hasWindowSwitcher"; roles[HasDesktopSwitcherRole] = "hasDesktopSwitcher"; m_model->setItemRoleNames(roles); + loadModel(); } KCMLookandFeel::~KCMLookandFeel() { } void KCMLookandFeel::getNewStuff() { if (!m_newStuffDialog) { m_newStuffDialog = new KNS3::DownloadDialog( QLatin1String("lookandfeel.knsrc") ); m_newStuffDialog.data()->setWindowTitle(i18n("Download New Look And Feel Packages")); - connect(m_newStuffDialog.data(), &KNS3::DownloadDialog::accepted, this, &KCMLookandFeel::load); + connect(m_newStuffDialog.data(), &KNS3::DownloadDialog::accepted, this, &KCMLookandFeel::loadModel); } m_newStuffDialog.data()->show(); } QStandardItemModel *KCMLookandFeel::lookAndFeelModel() { return m_model; } QString KCMLookandFeel::selectedPlugin() const { return m_selectedPlugin; } void KCMLookandFeel::setSelectedPlugin(const QString &plugin) { if (m_selectedPlugin == plugin) { return; } const bool firstTime = m_selectedPlugin.isNull(); m_selectedPlugin = plugin; emit selectedPluginChanged(); emit selectedPluginIndexChanged(); if (!firstTime) { setNeedsSave(true); } } int KCMLookandFeel::selectedPluginIndex() const { for (int i = 0; i < m_model->rowCount(); ++i) { if (m_model->data(m_model->index(i, 0), PluginNameRole).toString() == m_selectedPlugin) { return i; } } return -1; } QList KCMLookandFeel::availablePackages(const QStringList &components) { QList packages; QStringList paths; const QStringList dataPaths = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation); paths.reserve(dataPaths.count()); for (const QString &path : dataPaths) { QDir dir(path + QStringLiteral("/plasma/look-and-feel")); paths << dir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot); } for (const QString &path : paths) { Plasma::Package pkg = Plasma::PluginLoader::self()->loadPackage(QStringLiteral("Plasma/LookAndFeel")); pkg.setPath(path); pkg.setFallbackPackage(Plasma::Package()); if (components.isEmpty()) { packages << pkg; } else { for (const auto &component : components) { if (!pkg.filePath(component.toUtf8()).isEmpty()) { packages << pkg; break; } } } } return packages; } -void KCMLookandFeel::load() +void KCMLookandFeel::loadModel() { - m_package = Plasma::PluginLoader::self()->loadPackage(QStringLiteral("Plasma/LookAndFeel")); - KConfigGroup cg(KSharedConfig::openConfig(QStringLiteral("kdeglobals")), "KDE"); - const QString packageName = cg.readEntry("LookAndFeelPackage", QString()); - if (!packageName.isEmpty()) { - m_package.setPath(packageName); - } - - if (!m_package.metadata().isValid()) { - return; - } - - setSelectedPlugin(m_package.metadata().pluginName()); - m_model->clear(); const QList pkgs = availablePackages({"defaults", "layouts"}); for (const Plasma::Package &pkg : pkgs) { if (!pkg.metadata().isValid()) { continue; } QStandardItem* row = new QStandardItem(pkg.metadata().name()); row->setData(pkg.metadata().pluginName(), PluginNameRole); + row->setData(pkg.metadata().comment(), DescriptionRole); row->setData(pkg.filePath("preview"), ScreenhotRole); row->setData(pkg.filePath("fullscreenpreview"), FullScreenPreviewRole); //What the package provides row->setData(!pkg.filePath("splashmainscript").isEmpty(), HasSplashRole); row->setData(!pkg.filePath("lockscreenmainscript").isEmpty(), HasLockScreenRole); row->setData(!pkg.filePath("runcommandmainscript").isEmpty(), HasRunCommandRole); row->setData(!pkg.filePath("logoutmainscript").isEmpty(), HasLogoutRole); if (!pkg.filePath("defaults").isEmpty()) { KSharedConfigPtr conf = KSharedConfig::openConfig(pkg.filePath("defaults")); KConfigGroup cg(conf, "kdeglobals"); cg = KConfigGroup(&cg, "General"); bool hasColors = !cg.readEntry("ColorScheme", QString()).isEmpty(); if (!hasColors) { hasColors = !pkg.filePath("colors").isEmpty(); } row->setData(hasColors, HasColorsRole); cg = KConfigGroup(&cg, "KDE"); row->setData(!cg.readEntry("widgetStyle", QString()).isEmpty(), HasWidgetStyleRole); cg = KConfigGroup(conf, "kdeglobals"); cg = KConfigGroup(&cg, "Icons"); row->setData(!cg.readEntry("Theme", QString()).isEmpty(), HasIconsRole); cg = KConfigGroup(conf, "kdeglobals"); cg = KConfigGroup(&cg, "Theme"); row->setData(!cg.readEntry("name", QString()).isEmpty(), HasPlasmaThemeRole); cg = KConfigGroup(conf, "kcminputrc"); cg = KConfigGroup(&cg, "Mouse"); row->setData(!cg.readEntry("cursorTheme", QString()).isEmpty(), HasCursorsRole); cg = KConfigGroup(conf, "kwinrc"); cg = KConfigGroup(&cg, "WindowSwitcher"); row->setData(!cg.readEntry("LayoutName", QString()).isEmpty(), HasWindowSwitcherRole); cg = KConfigGroup(conf, "kwinrc"); cg = KConfigGroup(&cg, "DesktopSwitcher"); row->setData(!cg.readEntry("LayoutName", QString()).isEmpty(), HasDesktopSwitcherRole); } m_model->appendRow(row); } + emit selectedPluginIndexChanged(); +} + +void KCMLookandFeel::load() +{ + m_package = Plasma::PluginLoader::self()->loadPackage(QStringLiteral("Plasma/LookAndFeel")); + KConfigGroup cg(KSharedConfig::openConfig(QStringLiteral("kdeglobals")), "KDE"); + const QString packageName = cg.readEntry("LookAndFeelPackage", QString()); + if (!packageName.isEmpty()) { + m_package.setPath(packageName); + } + + if (!m_package.metadata().isValid()) { + return; + } + + setSelectedPlugin(m_package.metadata().pluginName()); + setNeedsSave(false); } void KCMLookandFeel::save() { Plasma::Package package = Plasma::PluginLoader::self()->loadPackage(QStringLiteral("Plasma/LookAndFeel")); package.setPath(m_selectedPlugin); if (!package.isValid()) { return; } m_configGroup.writeEntry("LookAndFeelPackage", m_selectedPlugin); if (m_resetDefaultLayout) { QDBusMessage message = QDBusMessage::createMethodCall(QStringLiteral("org.kde.plasmashell"), QStringLiteral("/PlasmaShell"), QStringLiteral("org.kde.PlasmaShell"), QStringLiteral("loadLookAndFeelDefaultLayout")); QList args; args << m_selectedPlugin; message.setArguments(args); QDBusConnection::sessionBus().call(message, QDBus::NoBlock); } if (!package.filePath("defaults").isEmpty()) { KSharedConfigPtr conf = KSharedConfig::openConfig(package.filePath("defaults")); KConfigGroup cg(conf, "kdeglobals"); cg = KConfigGroup(&cg, "KDE"); if (m_applyWidgetStyle) { setWidgetStyle(cg.readEntry("widgetStyle", QString())); } if (m_applyColors) { QString colorsFile = package.filePath("colors"); KConfigGroup cg(conf, "kdeglobals"); cg = KConfigGroup(&cg, "General"); QString colorScheme = cg.readEntry("ColorScheme", QString()); if (!colorsFile.isEmpty()) { if (!colorScheme.isEmpty()) { setColors(colorScheme, colorsFile); } else { setColors(package.metadata().name(), colorsFile); } } else if (!colorScheme.isEmpty()) { colorScheme.remove('\''); // So Foo's does not become FooS QRegExp fixer(QStringLiteral("[\\W,.-]+(.?)")); int offset; while ((offset = fixer.indexIn(colorScheme)) >= 0) { colorScheme.replace(offset, fixer.matchedLength(), fixer.cap(1).toUpper()); } colorScheme.replace(0, 1, colorScheme.at(0).toUpper()); //NOTE: why this loop trough all the scheme files? //the scheme theme name is an heuristic, there is no plugin metadata whatsoever. //is based on the file name stripped from weird characters or the //eventual id- prefix store.kde.org puts, so we can just find a //theme that ends as the specified name bool schemeFound = false; const QStringList schemeDirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("color-schemes"), QStandardPaths::LocateDirectory); for (const QString &dir : schemeDirs) { const QStringList fileNames = QDir(dir).entryList(QStringList()<emitChange(KGlobalSettings::StyleChanged); } void KCMLookandFeel::setColors(const QString &scheme, const QString &colorFile) { if (scheme.isEmpty() && colorFile.isEmpty()) { return; } KConfigGroup configGroup(&m_config, "General"); configGroup.writeEntry("ColorScheme", scheme); KSharedConfigPtr conf = KSharedConfig::openConfig(colorFile, KSharedConfig::CascadeConfig); foreach (const QString &grp, conf->groupList()) { KConfigGroup cg(conf, grp); KConfigGroup cg2(&m_config, grp); cg.copyTo(&cg2); } configGroup.sync(); KGlobalSettings::self()->emitChange(KGlobalSettings::PaletteChanged); } void KCMLookandFeel::setIcons(const QString &theme) { if (theme.isEmpty()) { return; } KConfigGroup cg(&m_config, "Icons"); cg.writeEntry("Theme", theme); cg.sync(); for (int i=0; i < KIconLoader::LastGroup; i++) { KIconLoader::emitChange(KIconLoader::Group(i)); } } void KCMLookandFeel::setPlasmaTheme(const QString &theme) { if (theme.isEmpty()) { return; } KConfig config(QStringLiteral("plasmarc")); KConfigGroup cg(&config, "Theme"); cg.writeEntry("name", theme); cg.sync(); } void KCMLookandFeel::setCursorTheme(const QString themeName) { //TODO: use pieces of cursor kcm when moved to plasma-desktop if (themeName.isEmpty()) { return; } KConfig config(QStringLiteral("kcminputrc")); KConfigGroup cg(&config, "Mouse"); cg.writeEntry("cursorTheme", themeName); cg.sync(); // Require the Xcursor version that shipped with X11R6.9 or greater, since // in previous versions the Xfixes code wasn't enabled due to a bug in the // build system (freedesktop bug #975). #if HAVE_XFIXES && XFIXES_MAJOR >= 2 && XCURSOR_LIB_VERSION >= 10105 const int cursorSize = cg.readEntry("cursorSize", 0); QDir themeDir = cursorThemeDir(themeName, 0); if (!themeDir.exists()) { return; } XCursorTheme theme(themeDir); if (!CursorTheme::haveXfixes()) { return; } // Set up the proper launch environment for newly started apps OrgKdeKLauncherInterface klauncher(QStringLiteral("org.kde.klauncher5"), QStringLiteral("/KLauncher"), QDBusConnection::sessionBus()); klauncher.setLaunchEnv(QStringLiteral("XCURSOR_THEME"), themeName); // Update the Xcursor X resources runRdb(0); // Notify all applications that the cursor theme has changed KGlobalSettings::self()->emitChange(KGlobalSettings::CursorChanged); // Reload the standard cursors QStringList names; // Qt cursors names << QStringLiteral("left_ptr") << QStringLiteral("up_arrow") << QStringLiteral("cross") << QStringLiteral("wait") << QStringLiteral("left_ptr_watch") << QStringLiteral("ibeam") << QStringLiteral("size_ver") << QStringLiteral("size_hor") << QStringLiteral("size_bdiag") << QStringLiteral("size_fdiag") << QStringLiteral("size_all") << QStringLiteral("split_v") << QStringLiteral("split_h") << QStringLiteral("pointing_hand") << QStringLiteral("openhand") << QStringLiteral("closedhand") << QStringLiteral("forbidden") << QStringLiteral("whats_this") << QStringLiteral("copy") << QStringLiteral("move") << QStringLiteral("link"); // X core cursors names << QStringLiteral("X_cursor") << QStringLiteral("right_ptr") << QStringLiteral("hand1") << QStringLiteral("hand2") << QStringLiteral("watch") << QStringLiteral("xterm") << QStringLiteral("crosshair") << QStringLiteral("left_ptr_watch") << QStringLiteral("center_ptr") << QStringLiteral("sb_h_double_arrow") << QStringLiteral("sb_v_double_arrow") << QStringLiteral("fleur") << QStringLiteral("top_left_corner") << QStringLiteral("top_side") << QStringLiteral("top_right_corner") << QStringLiteral("right_side") << QStringLiteral("bottom_right_corner") << QStringLiteral("bottom_side") << QStringLiteral("bottom_left_corner") << QStringLiteral("left_side") << QStringLiteral("question_arrow") << QStringLiteral("pirate"); foreach (const QString &name, names) { XFixesChangeCursorByName(QX11Info::display(), theme.loadCursor(name, cursorSize), QFile::encodeName(name)); } #else KMessageBox::information(this, i18n("You have to restart the Plasma session for these changes to take effect."), i18n("Cursor Settings Changed"), "CursorSettingsChanged"); #endif } QDir KCMLookandFeel::cursorThemeDir(const QString &theme, const int depth) { // Prevent infinite recursion if (depth > 10) { return QDir(); } // Search each icon theme directory for 'theme' foreach (const QString &baseDir, cursorSearchPaths()) { QDir dir(baseDir); if (!dir.exists() || !dir.cd(theme)) { continue; } // If there's a cursors subdir, we'll assume this is a cursor theme if (dir.exists(QStringLiteral("cursors"))) { return dir; } // If the theme doesn't have an index.theme file, it can't inherit any themes. if (!dir.exists(QStringLiteral("index.theme"))) { continue; } // Open the index.theme file, so we can get the list of inherited themes KConfig config(dir.path() + "/index.theme", KConfig::NoGlobals); KConfigGroup cg(&config, "Icon Theme"); // Recurse through the list of inherited themes, to check if one of them // is a cursor theme. QStringList inherits = cg.readEntry("Inherits", QStringList()); foreach (const QString &inherit, inherits) { // Avoid possible DoS if (inherit == theme) { continue; } if (cursorThemeDir(inherit, depth + 1).exists()) { return dir; } } } return QDir(); } const QStringList KCMLookandFeel::cursorSearchPaths() { if (!m_cursorSearchPaths.isEmpty()) return m_cursorSearchPaths; #if XCURSOR_LIB_MAJOR == 1 && XCURSOR_LIB_MINOR < 1 // These are the default paths Xcursor will scan for cursor themes QString path("~/.icons:/usr/share/icons:/usr/share/pixmaps:/usr/X11R6/lib/X11/icons"); // If XCURSOR_PATH is set, use that instead of the default path char *xcursorPath = std::getenv("XCURSOR_PATH"); if (xcursorPath) path = xcursorPath; #else // Get the search path from Xcursor QString path = XcursorLibraryPath(); #endif // Separate the paths m_cursorSearchPaths = path.split(QLatin1Char(':'), QString::SkipEmptyParts); // Remove duplicates QMutableStringListIterator i(m_cursorSearchPaths); while (i.hasNext()) { const QString path = i.next(); QMutableStringListIterator j(i); while (j.hasNext()) if (j.next() == path) j.remove(); } // Expand all occurrences of ~/ to the home dir m_cursorSearchPaths.replaceInStrings(QRegExp(QStringLiteral("^~\\/")), QDir::home().path() + QLatin1Char('/')); return m_cursorSearchPaths; } void KCMLookandFeel::setSplashScreen(const QString &theme) { if (theme.isEmpty()) { return; } KConfig config(QStringLiteral("ksplashrc")); KConfigGroup cg(&config, "KSplash"); cg.writeEntry("Theme", theme); //TODO: a way to set none as spash in the l&f cg.writeEntry("Engine", "KSplashQML"); cg.sync(); } void KCMLookandFeel::setLockScreen(const QString &theme) { if (theme.isEmpty()) { return; } KConfig config(QStringLiteral("kscreenlockerrc")); KConfigGroup cg(&config, "Greeter"); cg.writeEntry("Theme", theme); cg.sync(); } void KCMLookandFeel::setWindowSwitcher(const QString &theme) { if (theme.isEmpty()) { return; } KConfig config(QStringLiteral("kwinrc")); KConfigGroup cg(&config, "TabBox"); cg.writeEntry("LayoutName", theme); cg.sync(); } void KCMLookandFeel::setDesktopSwitcher(const QString &theme) { if (theme.isEmpty()) { return; } KConfig config(QStringLiteral("kwinrc")); KConfigGroup cg(&config, "TabBox"); cg.writeEntry("DesktopLayout", theme); cg.writeEntry("DesktopListLayout", theme); cg.sync(); } void KCMLookandFeel::setWindowDecoration(const QString &library, const QString &theme) { if (library.isEmpty()) { return; } KConfig config(QStringLiteral("kwinrc")); KConfigGroup cg(&config, "org.kde.kdecoration2"); cg.writeEntry("library", library); cg.writeEntry("theme", theme); cg.sync(); // Reload KWin. QDBusMessage message = QDBusMessage::createSignal(QStringLiteral("/KWin"), QStringLiteral("org.kde.KWin"), QStringLiteral("reloadConfig")); QDBusConnection::sessionBus().send(message); } void KCMLookandFeel::setApplyColors(bool apply) { if (m_applyColors == apply) { return; } m_applyColors = apply; emit applyColorsChanged(); } bool KCMLookandFeel::applyColors() const { return m_applyColors; } void KCMLookandFeel::setApplyWidgetStyle(bool apply) { if (m_applyWidgetStyle == apply) { return; } m_applyWidgetStyle = apply; emit applyWidgetStyleChanged(); } bool KCMLookandFeel::applyWidgetStyle() const { return m_applyWidgetStyle; } void KCMLookandFeel::setApplyIcons(bool apply) { if (m_applyIcons == apply) { return; } m_applyIcons = apply; emit applyIconsChanged(); } bool KCMLookandFeel::applyIcons() const { return m_applyIcons; } void KCMLookandFeel::setApplyPlasmaTheme(bool apply) { if (m_applyPlasmaTheme == apply) { return; } m_applyPlasmaTheme = apply; emit applyPlasmaThemeChanged(); } bool KCMLookandFeel::applyPlasmaTheme() const { return m_applyPlasmaTheme; } void KCMLookandFeel::setApplyWindowSwitcher(bool apply) { if (m_applyWindowSwitcher == apply) { return; } m_applyWindowSwitcher = apply; emit applyWindowSwitcherChanged(); } bool KCMLookandFeel::applyWindowSwitcher() const { return m_applyWindowSwitcher; } void KCMLookandFeel::setApplyDesktopSwitcher(bool apply) { if (m_applyDesktopSwitcher == apply) { return; } m_applyDesktopSwitcher = apply; emit applyDesktopSwitcherChanged(); } bool KCMLookandFeel::applyDesktopSwitcher() const { return m_applyDesktopSwitcher; } void KCMLookandFeel::setResetDefaultLayout(bool reset) { if (m_resetDefaultLayout == reset) { return; } m_resetDefaultLayout = reset; emit resetDefaultLayoutChanged(); if (reset) { setNeedsSave(true); } } bool KCMLookandFeel::resetDefaultLayout() const { return m_resetDefaultLayout; } diff --git a/kcms/lookandfeel/kcm.h b/kcms/lookandfeel/kcm.h index 911534171..9483a905f 100644 --- a/kcms/lookandfeel/kcm.h +++ b/kcms/lookandfeel/kcm.h @@ -1,150 +1,153 @@ /* Copyright (c) 2014 Marco Martin Copyright (c) 2014 Vishesh Handa This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _KCM_SEARCH_H #define _KCM_SEARCH_H #include #include #include #include #include #include #include class QQuickView; class QStandardItemModel; class KCMLookandFeel : public KQuickAddons::ConfigModule { Q_OBJECT Q_PROPERTY(QStandardItemModel *lookAndFeelModel READ lookAndFeelModel CONSTANT) Q_PROPERTY(QString selectedPlugin READ selectedPlugin WRITE setSelectedPlugin NOTIFY selectedPluginChanged) Q_PROPERTY(int selectedPluginIndex READ selectedPluginIndex NOTIFY selectedPluginIndexChanged) Q_PROPERTY(bool applyColors READ applyColors WRITE setApplyColors NOTIFY applyColorsChanged) Q_PROPERTY(bool applyWidgetStyle READ applyWidgetStyle WRITE setApplyWidgetStyle NOTIFY applyWidgetStyleChanged) Q_PROPERTY(bool applyIcons READ applyIcons WRITE setApplyIcons NOTIFY applyIconsChanged) Q_PROPERTY(bool applyPlasmaTheme READ applyPlasmaTheme WRITE setApplyPlasmaTheme NOTIFY applyPlasmaThemeChanged) Q_PROPERTY(bool applyWindowSwitcher READ applyWindowSwitcher WRITE setApplyWindowSwitcher NOTIFY applyWindowSwitcherChanged) Q_PROPERTY(bool applyDesktopSwitcher READ applyDesktopSwitcher WRITE setApplyDesktopSwitcher NOTIFY applyDesktopSwitcherChanged) Q_PROPERTY(bool resetDefaultLayout READ resetDefaultLayout WRITE setResetDefaultLayout NOTIFY resetDefaultLayoutChanged) public: enum Roles { PluginNameRole = Qt::UserRole +1, ScreenhotRole, FullScreenPreviewRole, + DescriptionRole, HasSplashRole, HasLockScreenRole, HasRunCommandRole, HasLogoutRole, HasColorsRole, HasWidgetStyleRole, HasIconsRole, HasPlasmaThemeRole, HasCursorsRole, HasWindowSwitcherRole, HasDesktopSwitcherRole }; KCMLookandFeel(QObject* parent, const QVariantList& args); ~KCMLookandFeel(); //List only packages which provide at least one of the components QList availablePackages(const QStringList &components); QStandardItemModel *lookAndFeelModel(); QString selectedPlugin() const; void setSelectedPlugin(const QString &plugin); int selectedPluginIndex() const; //Setters of the various theme pieces void setWidgetStyle(const QString &style); void setColors(const QString &scheme, const QString &colorFile); void setIcons(const QString &theme); void setPlasmaTheme(const QString &theme); void setCursorTheme(const QString theme); void setSplashScreen(const QString &theme); void setLockScreen(const QString &theme); void setWindowSwitcher(const QString &theme); void setDesktopSwitcher(const QString &theme); void setWindowDecoration(const QString &library, const QString &theme); void setApplyColors(bool apply); bool applyColors() const; void setApplyWidgetStyle(bool apply); bool applyWidgetStyle() const; void setApplyIcons(bool apply); bool applyIcons() const; void setApplyPlasmaTheme(bool apply); bool applyPlasmaTheme() const; void setApplyWindowSwitcher(bool apply); bool applyWindowSwitcher() const; void setApplyDesktopSwitcher(bool apply); bool applyDesktopSwitcher() const; bool resetDefaultLayout() const; void setResetDefaultLayout(bool reset); + void loadModel(); + Q_INVOKABLE void getNewStuff(); public Q_SLOTS: void load() override; void save() override; void defaults() override; Q_SIGNALS: void selectedPluginChanged(); void applyColorsChanged(); void applyWidgetStyleChanged(); void applyIconsChanged(); void applyPlasmaThemeChanged(); void applyWindowSwitcherChanged(); void applyDesktopSwitcherChanged(); void resetDefaultLayoutChanged(); void selectedPluginIndexChanged(); private: QDir cursorThemeDir(const QString &theme, const int depth); const QStringList cursorSearchPaths(); QStandardItemModel *m_model; Plasma::Package m_package; QString m_selectedPlugin; QStringList m_cursorSearchPaths; QPointer m_newStuffDialog; KConfig m_config; KConfigGroup m_configGroup; bool m_applyColors : 1; bool m_applyWidgetStyle : 1; bool m_applyIcons : 1; bool m_applyPlasmaTheme : 1; bool m_applyCursors : 1; bool m_applyWindowSwitcher : 1; bool m_applyDesktopSwitcher : 1; bool m_resetDefaultLayout : 1; bool m_applyWindowDecoration : 1; }; #endif diff --git a/kcms/lookandfeel/package/contents/ui/main.qml b/kcms/lookandfeel/package/contents/ui/main.qml index 47e4c785e..8b39c6442 100644 --- a/kcms/lookandfeel/package/contents/ui/main.qml +++ b/kcms/lookandfeel/package/contents/ui/main.qml @@ -1,246 +1,104 @@ /* - Copyright (c) 2014 Marco Martin + Copyright (c) 2018 Marco Martin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import QtQuick 2.1 +import QtQuick 2.6 import QtQuick.Layouts 1.1 import QtQuick.Window 2.2 -import QtQuick.Controls 1.0 as QtControls -import org.kde.kquickcontrolsaddons 2.0 -import QtQuick.Controls.Private 1.0 -import org.kde.kirigami 2.0 -import org.kde.plasma.components 2.0 as PlasmaComponents -import org.kde.kcm 1.0 +import QtQuick.Controls 2.3 as QtControls +import org.kde.kirigami 2.4 as Kirigami +import org.kde.kcm 1.1 as KCM -Item { - id: root - implicitWidth: Units.gridUnit * 20 - implicitHeight: Units.gridUnit * 20 +KCM.GridViewKCM { + KCM.ConfigModule.quickHelp: i18n("This module lets you configure the look and feel theme.") - ConfigModule.quickHelp: i18n("This module lets you configure the look of the whole workspace with some ready to go presets.") + view.model: kcm.lookAndFeelModel + view.currentIndex: kcm.selectedPluginIndex + view.delegate: KCM.GridDelegate { + id: delegate - // HACK QtQuick Controls 1 Button does not update its styleOption palette when it changes, since QQC1 is basically - // unmaintained and we're eventually moving to QQC2 and this is the first impression the user gets when playing with - // look and feel feature, we destroy and re-create the GHNS button when its color would change to avoid this. - SystemPalette { - id: syspal - onButtonChanged: { - ghnsButtonLoader.active = false; - ghnsButtonLoader.active = true; - } - } + text: model.display + toolTip: model.description - ColumnLayout { - anchors.fill: parent - QtControls.Label { - text: i18nd("kcm_lookandfeel", "Select an overall theme for your workspace (including plasma theme, color scheme, mouse cursor, window and desktop switcher, splash screen, lock screen etc.)") - wrapMode: Text.WordWrap - Layout.fillWidth: true + thumbnailAvailable: model.screenshot + thumbnail: Image { + anchors.fill: parent + source: model.screenshot || "" } - QtControls.ScrollView { - Layout.fillWidth: true - Layout.fillHeight: true - GridView { - id: grid - model: kcm.lookAndFeelModel - cellWidth: Math.floor(root.width / Math.max(Math.floor(root.width / (Units.gridUnit*12)), 3)) - Units.gridUnit - cellHeight: cellWidth / 1.6 - - onCountChanged: { - grid.currentIndex = kcm.selectedPluginIndex; - grid.positionViewAtIndex(grid.currentIndex, GridView.Visible) - } - delegate: Item { - width: grid.cellWidth - height: grid.cellHeight - - Rectangle { - anchors { - fill: parent - margins: Units.smallSpacing - } - Connections { - target: kcm - onSelectedPluginChanged: { - if (kcm.selectedPlugin == model.pluginName) { - grid.currentIndex = index - } - } - } - QIconItem { - id: icon - anchors.centerIn: parent - width: Units.iconSizes.large - height: width - icon: "view-preview" - } - Image { - anchors { - fill: parent - margins: Units.smallSpacing * 2 - } - source: model.screenshot - - Rectangle { - anchors { - left: parent.left - right: parent.right - bottom: parent.bottom - } - height: childrenRect.height - gradient: Gradient { - GradientStop { - position: 0.0 - color: "transparent" - } - GradientStop { - position: 1.0 - color: Qt.rgba(0, 0, 0, 0.5) - } - } - QtControls.Label { - anchors { - horizontalCenter: parent.horizontalCenter - } - color: "white" - text: model.display - } - } - } - Rectangle { - opacity: grid.currentIndex == index ? 1.0 : 0 - anchors.fill: parent - border.width: Units.smallSpacing * 2 - border.color: syspal.highlight - color: "transparent" - Behavior on opacity { - PropertyAnimation { - duration: Units.longDuration - easing.type: Easing.OutQuad - } - } - } - MouseArea { - anchors.fill: parent - hoverEnabled: true - onClicked: { - grid.currentIndex = index - kcm.selectedPlugin = model.pluginName - resetCheckbox.checked = false; - } - Timer { - interval: 1000 // FIXME TODO: Use platform value for tooltip activation delay. - - running: parent.containsMouse && !parent.pressedButtons - - onTriggered: { - Tooltip.showText(parent, Qt.point(parent.mouseX, parent.mouseY), model.display); - } - } - PlasmaComponents.ToolButton { - anchors { - top: parent.top - right: parent.right - margins: Units.smallSpacing - } - visible: model.fullScreenPreview != "" - iconSource: "media-playback-start" - tooltip: i18n("Show Preview") - flat: false - onClicked: { - previewWindow.url = model.fullScreenPreview; - previewWindow.showFullScreen(); - } - opacity: parent.containsMouse ? 1 : 0 - Behavior on opacity { - PropertyAnimation { - duration: Units.longDuration - easing.type: Easing.OutQuad - } - } - } - } - } + actions: [ + Kirigami.Action { + visible: model.fullScreenPreview != "" + iconName: "media-playback-start" + tooltip: i18n("Test Splashscreen") + onTriggered: { + previewWindow.url = model.fullScreenPreview; + previewWindow.showFullScreen(); } } + ] + onClicked: { + kcm.selectedPlugin = model.pluginName; + view.forceActiveFocus(); + resetCheckbox.checked = false; } - QtControls.Label { - text: i18nd("kcm_lookandfeel", "Warning: your Plasma Desktop layout will be lost and reset to the default layout provided by the selected theme.") - visible: resetCheckbox.checked - wrapMode: Text.WordWrap - Layout.fillWidth: true + } + + footer: RowLayout { + QtControls.CheckBox { + id: resetCheckbox + checked: kcm.resetDefaultLayout + text: i18n("Use Desktop Layout from theme") + onCheckedChanged: kcm.resetDefaultLayout = checked; } - Connections { - target: kcm - onNeedsSaveChanged: { - if (!needsSave) { - resetCheckbox.checked = false; - } - } + Item { + Layout.fillWidth: true } - RowLayout { - QtControls.CheckBox { - id: resetCheckbox - checked: kcm.resetDefaultLayout - text: i18n("Use Desktop Layout from theme") - onCheckedChanged: kcm.resetDefaultLayout = checked; - } - Item { - Layout.fillWidth: true - } - Loader { - id: ghnsButtonLoader - anchors.right: parent.right - - sourceComponent: QtControls.Button { - text: i18n("Get New Looks...") - iconName: "get-hot-new-stuff" - onClicked: kcm.getNewStuff(); - } - } + QtControls.Button { + text: i18n("Get New Looks...") + icon.name: "get-hot-new-stuff" + onClicked: kcm.getNewStuff(); } } - Window { id: previewWindow property alias url: previewImage.source color: Qt.rgba(0, 0, 0, 0.7) MouseArea { anchors.fill: parent Image { id: previewImage anchors.centerIn: parent fillMode: Image.PreserveAspectFit width: Math.min(parent.width, sourceSize.width) height: Math.min(parent.height, sourceSize.height) } onClicked: previewWindow.visible = false; QtControls.ToolButton { anchors { top: parent.top right: parent.right } - iconName: "window-close" + icon.name: "window-close" onClicked: previewWindow.visible = false; } - QtControls.Action { - onTriggered: previewWindow.visible = false; - shortcut: "Esc" + Shortcut { + onActivated: previewWindow.visible = false; + sequence: "Esc" } } } }