diff --git a/kcms/desktoptheme/kcm.cpp b/kcms/desktoptheme/kcm.cpp index 989e33730..478ee0e37 100644 --- a/kcms/desktoptheme/kcm.cpp +++ b/kcms/desktoptheme/kcm.cpp @@ -1,274 +1,284 @@ /* This file is part of the KDE Project Copyright (c) 2014 Marco Martin Copyright (c) 2014 Vishesh Handa Copyright (c) 2016 David Rosca 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 #include #include #include #include #include #include #include #include #include #include #include #include Q_LOGGING_CATEGORY(KCM_DESKTOP_THEME, "kcm_desktoptheme") K_PLUGIN_FACTORY_WITH_JSON(KCMDesktopThemeFactory, "kcm_desktoptheme.json", registerPlugin();) KCMDesktopTheme::KCMDesktopTheme(QObject *parent, const QVariantList &args) : KQuickAddons::ConfigModule(parent, args) , m_defaultTheme(new Plasma::Theme(this)) { //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(); KAboutData* about = new KAboutData(QStringLiteral("kcm_desktoptheme"), i18n("Configure Desktop Theme"), QStringLiteral("0.1"), QString(), KAboutLicense::LGPL); about->addAuthor(i18n("David Rosca"), QString(), QStringLiteral("nowrep@gmail.com")); setAboutData(about); setButtons(Apply | Default | Help); m_model = new QStandardItemModel(this); QHash roles = m_model->roleNames(); roles[PluginNameRole] = QByteArrayLiteral("pluginName"); roles[ThemeNameRole] = QByteArrayLiteral("themeName"); roles[IsLocalRole] = QByteArrayLiteral("isLocal"); m_model->setItemRoleNames(roles); } KCMDesktopTheme::~KCMDesktopTheme() { delete m_defaultTheme; } QStandardItemModel *KCMDesktopTheme::desktopThemeModel() const { return m_model; } QString KCMDesktopTheme::selectedPlugin() const { return m_selectedPlugin; } void KCMDesktopTheme::setSelectedPlugin(const QString &plugin) { if (m_selectedPlugin == plugin) { return; } m_selectedPlugin = plugin; Q_EMIT selectedPluginChanged(m_selectedPlugin); updateNeedsSave(); } void KCMDesktopTheme::getNewThemes() { KNS3::DownloadDialog *dialog = new KNS3::DownloadDialog(QStringLiteral("plasma-themes.knsrc")); dialog->open(); connect(dialog, &QDialog::accepted, this, [this, dialog]() { if (!dialog->changedEntries().isEmpty()) { load(); delete dialog; } }); } void KCMDesktopTheme::installThemeFromFile(const QUrl &file) { qCDebug(KCM_DESKTOP_THEME) << "Installing ... " << file; QString program = QStringLiteral("plasmapkg2"); QStringList arguments; arguments << QStringLiteral("-t") << QStringLiteral("theme") << QStringLiteral("-i") << file.toLocalFile(); qCDebug(KCM_DESKTOP_THEME) << program << arguments.join(QStringLiteral(" ")); QProcess *myProcess = new QProcess(this); connect(myProcess, static_cast(&QProcess::finished), this, [this](int exitCode, QProcess::ExitStatus exitStatus) { Q_UNUSED(exitStatus); if (exitCode == 0) { qCDebug(KCM_DESKTOP_THEME) << "Theme installed successfully :)"; load(); Q_EMIT showInfoMessage(i18n("Theme installed successfully.")); } else { qCWarning(KCM_DESKTOP_THEME) << "Theme installation failed." << exitCode; Q_EMIT showInfoMessage(i18n("Theme installation failed.")); } }); connect(myProcess, static_cast(&QProcess::error), this, [this](QProcess::ProcessError e) { qCWarning(KCM_DESKTOP_THEME) << "Theme installation failed: " << e; Q_EMIT showInfoMessage(i18n("Theme installation failed.")); }); myProcess->start(program, arguments); } void KCMDesktopTheme::removeTheme(const QString &name) { Q_ASSERT(!m_pendingRemoval.contains(name)); Q_ASSERT(!m_model->findItems(name).isEmpty()); m_pendingRemoval.append(name); m_model->removeRow(m_model->findItems(name).at(0)->row()); updateNeedsSave(); } void KCMDesktopTheme::applyPlasmaTheme(QQuickItem *item, const QString &themeName) { if (!item) { return; } Plasma::Theme *theme = m_themes[themeName]; if (!theme) { theme = new Plasma::Theme(themeName, this); m_themes[themeName] = theme; } Q_FOREACH (Plasma::Svg *svg, item->findChildren()) { svg->setTheme(theme); svg->setUsingRenderingCache(false); } } +Q_INVOKABLE int KCMDesktopTheme::indexOf(const QString &themeName) const +{ + for (int i = 0; i < m_model->rowCount(); ++i) { + if (m_model->data(m_model->index(i, 0), PluginNameRole).toString() == themeName) { + return i; + } + } + return -1; +} + void KCMDesktopTheme::load() { m_pendingRemoval.clear(); // Get all desktop themes QStringList themes; const QStringList &packs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("plasma/desktoptheme"), QStandardPaths::LocateDirectory); Q_FOREACH (const QString &ppath, packs) { const QDir cd(ppath); const QStringList &entries = cd.entryList(QDir::Dirs | QDir::Hidden | QDir::NoDotAndDotDot); Q_FOREACH (const QString &pack, entries) { const QString _metadata = ppath + QLatin1Char('/') + pack + QStringLiteral("/metadata.desktop"); if (QFile::exists(_metadata)) { themes << _metadata; } } } m_model->clear(); Q_FOREACH (const QString &theme, themes) { int themeSepIndex = theme.lastIndexOf('/', -1); const QString themeRoot = theme.left(themeSepIndex); int themeNameSepIndex = themeRoot.lastIndexOf('/', -1); const QString packageName = themeRoot.right(themeRoot.length() - themeNameSepIndex - 1); KDesktopFile df(theme); if (df.noDisplay()) { continue; } QString name = df.readName(); if (name.isEmpty()) { name = packageName; } const bool isLocal = QFileInfo(theme).isWritable(); if (m_model->findItems(packageName).isEmpty()) { QStandardItem *item = new QStandardItem; item->setText(packageName); item->setData(packageName, PluginNameRole); item->setData(name, ThemeNameRole); item->setData(isLocal, IsLocalRole); m_model->appendRow(item); } } KConfigGroup cg(KSharedConfig::openConfig(QStringLiteral("plasmarc")), "Theme"); setSelectedPlugin(cg.readEntry("name", m_defaultTheme->themeName())); updateNeedsSave(); } void KCMDesktopTheme::save() { if (m_defaultTheme->themeName() == m_selectedPlugin) { return; } m_defaultTheme->setThemeName(m_selectedPlugin); removeThemes(); updateNeedsSave(); } void KCMDesktopTheme::defaults() { setSelectedPlugin(QStringLiteral("default")); } void KCMDesktopTheme::updateNeedsSave() { setNeedsSave(!m_pendingRemoval.isEmpty() || m_selectedPlugin != m_defaultTheme->themeName()); } void KCMDesktopTheme::removeThemes() { const QString program = QStringLiteral("plasmapkg2"); Q_FOREACH (const QString &name, m_pendingRemoval) { const QStringList arguments = {QStringLiteral("-t"), QStringLiteral("theme"), QStringLiteral("-r"), name}; qCDebug(KCM_DESKTOP_THEME) << program << arguments.join(QStringLiteral(" ")); QProcess *process = new QProcess(this); connect(process, static_cast(&QProcess::finished), this, [this, process](int exitCode, QProcess::ExitStatus exitStatus) { Q_UNUSED(exitStatus); if (exitCode == 0) { qCDebug(KCM_DESKTOP_THEME) << "Theme removed successfully :)"; load(); } else { qCWarning(KCM_DESKTOP_THEME) << "Theme removal failed." << exitCode; Q_EMIT showInfoMessage(i18n("Theme removal failed.")); } process->deleteLater(); }); connect(process, static_cast(&QProcess::error), this, [this, process](QProcess::ProcessError e) { qCWarning(KCM_DESKTOP_THEME) << "Theme removal failed: " << e; Q_EMIT showInfoMessage(i18n("Theme removal failed.")); process->deleteLater(); }); process->start(program, arguments); } } #include "kcm.moc" diff --git a/kcms/desktoptheme/kcm.h b/kcms/desktoptheme/kcm.h index 2a2cd9266..999878262 100644 --- a/kcms/desktoptheme/kcm.h +++ b/kcms/desktoptheme/kcm.h @@ -1,83 +1,85 @@ /* Copyright (c) 2014 Marco Martin Copyright (c) 2014 Vishesh Handa Copyright (c) 2016 David Rosca 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_DESKTOPTHEME_H #define _KCM_DESKTOPTHEME_H #include namespace Plasma { class Svg; class Theme; } class QStandardItemModel; class KCMDesktopTheme : public KQuickAddons::ConfigModule { Q_OBJECT Q_PROPERTY(QStandardItemModel *desktopThemeModel READ desktopThemeModel CONSTANT) Q_PROPERTY(QString selectedPlugin READ selectedPlugin WRITE setSelectedPlugin NOTIFY selectedPluginChanged) public: enum Roles { PluginNameRole = Qt::UserRole + 1, ThemeNameRole, IsLocalRole }; Q_ENUM(Roles) KCMDesktopTheme(QObject *parent, const QVariantList &args); ~KCMDesktopTheme(); QStandardItemModel *desktopThemeModel() const; QString selectedPlugin() const; void setSelectedPlugin(const QString &plugin); Q_INVOKABLE void getNewThemes(); Q_INVOKABLE void installThemeFromFile(const QUrl &file); Q_INVOKABLE void removeTheme(const QString &name); Q_INVOKABLE void applyPlasmaTheme(QQuickItem *item, const QString &themeName); + Q_INVOKABLE int indexOf(const QString &themeName) const; + Q_SIGNALS: void selectedPluginChanged(const QString &plugin); void showInfoMessage(const QString &infoMessage); public Q_SLOTS: void load(); void save(); void defaults(); private: void removeThemes(); void updateNeedsSave(); QStandardItemModel *m_model; QString m_selectedPlugin; QStringList m_pendingRemoval; Plasma::Theme *m_defaultTheme; QHash m_themes; }; Q_DECLARE_LOGGING_CATEGORY(KCM_DESKTOP_THEME) #endif // _KCM_DESKTOPTHEME_H diff --git a/kcms/desktoptheme/package/contents/ui/main.qml b/kcms/desktoptheme/package/contents/ui/main.qml index d22853aa1..3e28804e4 100644 --- a/kcms/desktoptheme/package/contents/ui/main.qml +++ b/kcms/desktoptheme/package/contents/ui/main.qml @@ -1,221 +1,220 @@ /* Copyright (c) 2014 Marco Martin Copyright (c) 2016 David Rosca 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.Layouts 1.1 import QtQuick.Dialogs 1.0 import QtQuick.Controls.Private 1.0 import QtQuick.Controls 1.0 as QtControls import org.kde.kcm 1.0 import org.kde.plasma.core 2.0 // for units Item { implicitWidth: units.gridUnit * 20 implicitHeight: units.gridUnit * 20 ConfigModule.quickHelp: i18n("This module lets you configure the desktop theme.") SystemPalette { id: syspal } ColumnLayout { anchors.fill: parent QtControls.ScrollView { Layout.fillWidth: true Layout.fillHeight: true verticalScrollBarPolicy: Qt.ScrollBarAlwaysOn GridView { id: grid model: kcm.desktopThemeModel cellWidth: Math.floor(grid.width / Math.max(Math.floor(grid.width / (units.gridUnit * 12)), 3)) cellHeight: cellWidth / 1.6 + onCountChanged: { + grid.currentIndex = kcm.indexOf(kcm.selectedPlugin); + grid.positionViewAtIndex(grid.currentIndex, GridView.Visible) + } + delegate: Item { property bool isLocal : model.isLocal property string pluginName : model.pluginName width: grid.cellWidth height: grid.cellHeight Rectangle { anchors { fill: parent margins: units.smallSpacing } Connections { target: kcm onSelectedPluginChanged: { if (kcm.selectedPlugin == model.pluginName) { makeCurrentTimer.pendingIndex = index } } } Component.onCompleted: { if (kcm.selectedPlugin == model.pluginName) { makeCurrentTimer.pendingIndex = index } } Item { anchors { fill: parent margins: units.smallSpacing * 2 } ThemePreview { id: preview anchors { top: parent.top left: parent.left right: parent.right bottom: label.top } themeName: model.pluginName } QtControls.Label { id: label anchors { bottom: parent.bottom horizontalCenter: parent.horizontalCenter leftMargin: units.smallSpacing * 2 rightMargin: units.smallSpacing * 2 } height: paintedHeight width: parent.width color: "black" text: model.themeName elide: Text.ElideRight horizontalAlignment: Text.AlignHCenter } } 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 } Timer { interval: 1000 running: parent.containsMouse && !parent.pressedButtons onTriggered: { Tooltip.showText(parent, Qt.point(parent.mouseX, parent.mouseY), model.themeName); } } } } } Timer { id: makeCurrentTimer interval: 100 repeat: false property int pendingIndex onPendingIndexChanged: makeCurrentTimer.restart() onTriggered: { grid.currentIndex = pendingIndex } } } } RowLayout { QtControls.Button { text: i18n("Get new Theme") iconName: "get-hot-new-stuff" onClicked: kcm.getNewThemes() } QtControls.Button { text: i18n("Install from File") iconName: "document-import" onClicked: fileDialogLoader.active = true; } QtControls.Button { text: i18n("Remove Theme") iconName: "edit-delete" enabled: grid.currentItem && grid.currentItem.isLocal onClicked: { kcm.removeTheme(grid.currentItem.pluginName); - updateSelectedPluginTimer.restart(); + kcm.selectedPlugin = grid.currentItem.pluginName } } Item { Layout.fillWidth: true } QtControls.Label { id: infoLabel } } } Connections { target: kcm onShowInfoMessage: { infoLabel.text = infoMessage; hideInfoMessageTimer.restart(); } } - Timer { - id: updateSelectedPluginTimer - interval: 100 - onTriggered: kcm.selectedPlugin = grid.currentItem.pluginName - } - Timer { id: hideInfoMessageTimer interval: 20 * 1000 onTriggered: { infoLabel.text = "" } } Loader { id: fileDialogLoader active: false sourceComponent: FileDialog { visible: true title: i18n("Open Theme") folder: shortcuts.home nameFilters: [ i18n("Theme Files (*.zip *.tar.gz *.tar.bz2)") ] onAccepted: { kcm.installThemeFromFile(fileUrls[0]) fileDialogLoader.active = false } onRejected: { fileDialogLoader.active = false } } } } diff --git a/kcms/lookandfeel/kcm.cpp b/kcms/lookandfeel/kcm.cpp index eec3bc78a..a8ceca200 100644 --- a/kcms/lookandfeel/kcm.cpp +++ b/kcms/lookandfeel/kcm.cpp @@ -1,719 +1,730 @@ /* 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 #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 K_PLUGIN_FACTORY_WITH_JSON(KCMLookandFeelFactory, "kcm_lookandfeel.json", registerPlugin();) 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) { //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[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); } 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); } 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 QString &component) { QList packages; QStringList paths; const QStringList dataPaths = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation); for (const QString &path : dataPaths) { QDir dir(path + "/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 (component.isEmpty() || !pkg.filePath(component.toUtf8()).isEmpty()) { packages << pkg; } } return packages; } 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()); m_model->clear(); const QList pkgs = availablePackages(); 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.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); } } 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); QDBusMessage message; if (m_resetDefaultLayout) { 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()); QString src = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "color-schemes/" + colorScheme + ".colors"); setColors(colorScheme, src); } } if (m_applyIcons) { cg = KConfigGroup(conf, "kdeglobals"); cg = KConfigGroup(&cg, "Icons"); setIcons(cg.readEntry("Theme", QString())); } if (m_applyPlasmaTheme) { cg = KConfigGroup(conf, "plasmarc"); cg = KConfigGroup(&cg, "Theme"); setPlasmaTheme(cg.readEntry("name", QString())); } if (m_applyCursors) { cg = KConfigGroup(conf, "kcminputrc"); cg = KConfigGroup(&cg, "Mouse"); setCursorTheme(cg.readEntry("cursorTheme", QString())); } if (m_applyWindowSwitcher) { cg = KConfigGroup(conf, "kwinrc"); cg = KConfigGroup(&cg, "WindowSwitcher"); setWindowSwitcher(cg.readEntry("LayoutName", QString())); } if (m_applyDesktopSwitcher) { cg = KConfigGroup(conf, "kwinrc"); cg = KConfigGroup(&cg, "DesktopSwitcher"); setDesktopSwitcher(cg.readEntry("LayoutName", QString())); } } //TODO: option to enable/disable apply? they don't seem required by UI design setSplashScreen(m_selectedPlugin); setLockScreen(m_selectedPlugin); m_configGroup.sync(); runRdb(KRdbExportQtColors | KRdbExportGtkTheme | KRdbExportColors | KRdbExportQtSettings | KRdbExportXftSettings); } void KCMLookandFeel::defaults() { if (!m_package.metadata().isValid()) { return; } setSelectedPlugin(m_package.metadata().pluginName()); } void KCMLookandFeel::setWidgetStyle(const QString &style) { if (style.isEmpty()) { return; } m_configGroup.writeEntry("widgetStyle", style); m_configGroup.sync(); //FIXME: changing style on the fly breaks QQuickWidgets KGlobalSettings::self()->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); configGroup.sync(); KSharedConfigPtr conf = KSharedConfig::openConfig(colorFile); foreach (const QString &grp, conf->groupList()) { KConfigGroup cg(conf, grp); KConfigGroup cg2(&m_config, grp); cg.copyTo(&cg2); } 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 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, 0), QFile::encodeName(name)); } #else KMessageBox::information(this, i18n("You have to restart KDE for cursor 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(':', 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() + '/'); 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(); // Reload KWin. QDBusMessage message = QDBusMessage::createSignal(QStringLiteral("/KWin"), QStringLiteral("org.kde.KWin"), QStringLiteral("reloadConfig")); QDBusConnection::sessionBus().send(message); } 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(); // 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; } #include "kcm.moc" diff --git a/kcms/lookandfeel/kcm.h b/kcms/lookandfeel/kcm.h index 7ec47cc29..16df7fde7 100644 --- a/kcms/lookandfeel/kcm.h +++ b/kcms/lookandfeel/kcm.h @@ -1,143 +1,147 @@ /* 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, HasSplashRole, HasLockScreenRole, HasRunCommandRole, HasLogoutRole, HasColorsRole, HasWidgetStyleRole, HasIconsRole, HasPlasmaThemeRole, HasCursorsRole, HasWindowSwitcherRole, HasDesktopSwitcherRole }; KCMLookandFeel(QObject* parent, const QVariantList& args); ~KCMLookandFeel(); QList availablePackages(const QString &component = QString()); 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 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); Q_INVOKABLE void getNewStuff(); public Q_SLOTS: void load(); void save(); void defaults(); 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; }; #endif diff --git a/kcms/lookandfeel/package/contents/ui/main.qml b/kcms/lookandfeel/package/contents/ui/main.qml index b0182b6a1..d644f7d22 100644 --- a/kcms/lookandfeel/package/contents/ui/main.qml +++ b/kcms/lookandfeel/package/contents/ui/main.qml @@ -1,243 +1,233 @@ /* Copyright (c) 2014 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.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 //We need units from it import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents import org.kde.kcm 1.0 Item { implicitWidth: units.gridUnit * 20 implicitHeight: units.gridUnit * 20 ConfigModule.quickHelp: i18n("This module lets you configure the look of the whole workspace with some ready to go presets.") SystemPalette {id: syspal} 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 } QtControls.ScrollView { Layout.fillWidth: true Layout.fillHeight: true GridView { id: grid model: kcm.lookAndFeelModel cellWidth: Math.floor(grid.width / Math.max(Math.floor(grid.width / (units.gridUnit*12)), 3)) 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) { - makeCurrentTimer.pendingIndex = index + grid.currentIndex = index } } } - Component.onCompleted: { - if (kcm.selectedPlugin == model.pluginName) { - makeCurrentTimer.pendingIndex = 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("Test Splashscreen") 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 } } } } } } - Timer { - id: makeCurrentTimer - interval: 100 - repeat: false - property int pendingIndex - onPendingIndexChanged: makeCurrentTimer.restart() - onTriggered: { - grid.currentIndex = pendingIndex - } - } } } 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 } Connections { target: kcm onNeedsSaveChanged: { if (!needsSave) { resetCheckbox.checked = false; } } } RowLayout { QtControls.CheckBox { id: resetCheckbox checked: kcm.resetDefaultLayout text: i18n("Use Desktop Layout from theme") onCheckedChanged: kcm.resetDefaultLayout = checked; } Item { Layout.fillWidth: true } QtControls.Button { anchors.right: parent.right text: i18n("Get New Looks...") iconName: "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" onClicked: previewWindow.visible = false; } QtControls.Action { onTriggered: previewWindow.visible = false; shortcut: "Esc" } } } }