diff --git a/src/helper.cpp b/src/helper.cpp --- a/src/helper.cpp +++ b/src/helper.cpp @@ -45,7 +45,6 @@ reply.setErrorDescription(i18n("No theme specified in helper parameters.")); return reply; } - qWarning()<<"KAUTH HELPER CALLED SAVE WITH" << theme; { KConfigGroup cg(KSharedConfig::openConfig(QStringLiteral(PLYMOUTH_CONFIG_PATH)), "Daemon"); @@ -181,7 +180,6 @@ if (themearchive.isEmpty()) { return ActionReply::BackendError; } - qWarning()<<"KAUTH HELPER CALLED INSTALL WITH" << themearchive; QDir basedir(QStringLiteral(PLYMOUTH_THEMES_DIR)); if (!basedir.exists()) { @@ -292,7 +290,6 @@ qWarning()<<"No theme specified."; return ActionReply::BackendError; } - qWarning()<<"KAUTH HELPER CALLED UNINSTALL WITH" << theme; QDir dir(QStringLiteral(PLYMOUTH_THEMES_DIR)); if (!dir.exists()) { diff --git a/src/kcm.h b/src/kcm.h --- a/src/kcm.h +++ b/src/kcm.h @@ -27,18 +27,21 @@ #include -class QQuickView; +class QQuickItem; class QStandardItemModel; class KCMPlymouth : public KQuickAddons::ConfigModule { Q_OBJECT Q_PROPERTY(QStandardItemModel *themesModel READ themesModel CONSTANT) Q_PROPERTY(QString selectedPlugin READ selectedPlugin WRITE setSelectedPlugin NOTIFY selectedPluginChanged) + Q_PROPERTY(int selectedPluginIndex READ selectedPluginIndex NOTIFY selectedPluginIndexChanged) + Q_PROPERTY(bool busy READ busy WRITE setBusy NOTIFY busyChanged) public: enum Roles { - PluginNameRole = Qt::UserRole +1, + DescriptionRole = Qt::UserRole + 1, + PluginNameRole, ScreenhotRole, UninstallableRole }; @@ -50,8 +53,13 @@ QString selectedPlugin() const; void setSelectedPlugin(const QString &plugin); - Q_INVOKABLE int selectedPluginIndex() const; - Q_INVOKABLE void getNewStuff(); + int selectedPluginIndex() const; + + bool busy() const; + void setBusy(const bool &busy); + + Q_INVOKABLE void reloadModel(); + Q_INVOKABLE void getNewStuff(QQuickItem *ctx); Q_INVOKABLE void uninstall(const QString &plugin); public Q_SLOTS: @@ -61,10 +69,17 @@ Q_SIGNALS: void selectedPluginChanged(); + void selectedPluginIndexChanged(); + + void busyChanged(); + + void showSuccessMessage(const QString &message); + void showErrorMessage(const QString &message); private: QStandardItemModel *m_model; QString m_selectedPlugin; + bool m_busy = false; QPointer m_newStuffDialog; }; diff --git a/src/kcm.cpp b/src/kcm.cpp --- a/src/kcm.cpp +++ b/src/kcm.cpp @@ -29,15 +29,13 @@ #include #include #include -#include -#include #include -#include +#include +#include #include #include -#include #include #include @@ -53,24 +51,23 @@ KCMPlymouth::KCMPlymouth(QObject* parent, const QVariantList& args) : KQuickAddons::ConfigModule(parent, args) { - //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_plymouth"), i18n("Configure Plymouth Splash Screen"), + KAboutData* about = new KAboutData(QStringLiteral("kcm_plymouth"), i18n("Boot Splash Screen"), QStringLiteral(PLYMOUTH_KCM_VERSION), QString(), KAboutLicense::LGPL); about->addAuthor(i18n("Marco Martin"), QString(), QStringLiteral("mart@kde.org")); setAboutData(about); - setButtons(Apply | Default); + setButtons(Apply); m_model = new QStandardItemModel(this); - QHash roles = m_model->roleNames(); - roles[PluginNameRole] = "pluginName"; - roles[ScreenhotRole] = "screenshot"; - roles[UninstallableRole] = "uninstallable"; - m_model->setItemRoleNames(roles); + + m_model->setItemRoleNames({ + {Qt::DisplayRole, QByteArrayLiteral("display")}, + {DescriptionRole, QByteArrayLiteral("description")}, + {PluginNameRole, QByteArrayLiteral("pluginName")}, + {ScreenhotRole, QByteArrayLiteral("screenshot")}, + {UninstallableRole, QByteArrayLiteral("uninstallable")} + }); //setAuthActionName("org.kde.kcontrol.kcmplymouth.save"); //setNeedsAuthorization(true); @@ -81,12 +78,64 @@ delete m_newStuffDialog.data(); } -void KCMPlymouth::getNewStuff() +void KCMPlymouth::reloadModel() +{ + m_model->clear(); + + QDir dir(QStringLiteral(PLYMOUTH_THEMES_DIR)); + if (!dir.exists()) { + return; + } + + KConfigGroup installedCg(KSharedConfig::openConfig(QStringLiteral("kplymouththemeinstallerrc")), "DownloadedThemes"); + + dir.setFilter(QDir::NoDotAndDotDot|QDir::Dirs); + + const auto list = dir.entryInfoList(); + for (const QFileInfo &fileInfo : list) { + const QString pluginName = fileInfo.fileName(); + QDir themeDir(fileInfo.filePath()); + + KConfig file(themeDir.filePath(pluginName + QLatin1String(".plymouth")), KConfig::SimpleConfig); + KConfigGroup grp = file.group("Plymouth Theme"); + + QString displayName = grp.readEntry("Name", QString()); + if (displayName.isEmpty()) { + displayName = pluginName; + } + + QStandardItem *row = new QStandardItem(displayName); + row->setData(pluginName, PluginNameRole); + row->setData(grp.readEntry("Description", QString()), DescriptionRole); + row->setData(installedCg.entryMap().contains(fileInfo.fileName()), UninstallableRole); + + //the theme has a preview + if (QFile::exists(themeDir.path() + QStringLiteral("/preview.png"))) { + row->setData(QString(themeDir.path() + QStringLiteral("/preview.png")), ScreenhotRole); + //fetch it downloaded from kns + } else { + const QString fileName = installedCg.readEntry(fileInfo.fileName(), QString()); + if (fileName.isEmpty()) { + row->setData(QString(), ScreenhotRole); + } else { + row->setData(fileName + QStringLiteral(".png"), ScreenhotRole); + } + } + + m_model->appendRow(row); + } + + emit selectedPluginIndexChanged(); +} + +void KCMPlymouth::getNewStuff(QQuickItem *ctx) { if (!m_newStuffDialog) { m_newStuffDialog = new KNS3::DownloadDialog( QLatin1String("plymouth.knsrc") ); - m_newStuffDialog.data()->setWindowTitle(i18n("Download New Splash Screens")); - connect(m_newStuffDialog.data(), &KNS3::DownloadDialog::accepted, this, &KCMPlymouth::load); + m_newStuffDialog->setWindowTitle(i18n("Download New Boot Splash Screens")); + m_newStuffDialog->setWindowModality(Qt::WindowModal); + m_newStuffDialog->winId(); // so it creates the windowHandle(); + connect(m_newStuffDialog.data(), &KNS3::DownloadDialog::accepted, this, &KCMPlymouth::reloadModel); connect(m_newStuffDialog.data(), &KNS3::DownloadDialog::finished, m_newStuffDialog.data(), &KNS3::DownloadDialog::deleteLater); connect(m_newStuffDialog->engine(), &KNSCore::Engine::signalEntryChanged, this, [=](const KNSCore::EntryInternal &entry){ @@ -97,7 +146,12 @@ KIO::file_copy(QUrl(entry.previewUrl(KNSCore::EntryInternal::PreviewBig1)), QUrl::fromLocalFile(QString(entry.installedFiles().first() + QStringLiteral(".png"))), -1, KIO::Overwrite | KIO::HideProgressInfo); }); } - m_newStuffDialog.data()->show(); + + if (ctx && ctx->window()) { + m_newStuffDialog->windowHandle()->setTransientParent(ctx->window()); + } + + m_newStuffDialog->show(); } QStandardItemModel *KCMPlymouth::themesModel() @@ -116,13 +170,26 @@ return; } - const bool firstTime = m_selectedPlugin.isNull(); m_selectedPlugin = plugin; emit selectedPluginChanged(); + emit selectedPluginIndexChanged(); + + setNeedsSave(true); +} - if (!firstTime) { - setNeedsSave(true); +bool KCMPlymouth::busy() const +{ + return m_busy; +} + +void KCMPlymouth::setBusy(const bool &busy) +{ + if (m_busy == busy) { + return; } + + m_busy = busy; + emit busyChanged(); } int KCMPlymouth::selectedPluginIndex() const @@ -137,45 +204,19 @@ void KCMPlymouth::load() { - m_model->clear(); - QDir dir(QStringLiteral(PLYMOUTH_THEMES_DIR)); - if (!dir.exists()) { - return; - } + reloadModel(); KConfigGroup cg(KSharedConfig::openConfig(QStringLiteral(PLYMOUTH_CONFIG_PATH)), "Daemon"); - m_selectedPlugin = cg.readEntry("Theme"); - KConfigGroup installedCg(KSharedConfig::openConfig(QStringLiteral("kplymouththemeinstallerrc")), "DownloadedThemes"); - dir.setFilter(QDir::NoDotAndDotDot|QDir::Dirs); - QFileInfoList list = dir.entryInfoList(); - for (int i = 0; i < list.size(); ++i) { - QFileInfo fileInfo = list.at(i); - QStandardItem* row = new QStandardItem(fileInfo.fileName()); - row->setData(fileInfo.fileName(), PluginNameRole); - row->setData(installedCg.entryMap().contains(fileInfo.fileName()), UninstallableRole); - QDir themeDir(fileInfo.filePath()); - //the theme has a preview - if (QFile::exists(themeDir.path() + QStringLiteral("/preview.png"))) { - row->setData(QString(themeDir.path() + QStringLiteral("/preview.png")), ScreenhotRole); - //fetch it downloaded from kns - } else { - const QString fileName = installedCg.readEntry(fileInfo.fileName(), QString()); - if (fileName.isEmpty()) { - row->setData(QString(), ScreenhotRole); - } else { - row->setData(fileName + QStringLiteral(".png"), ScreenhotRole); - } - } + setSelectedPlugin(cg.readEntry("Theme")); - m_model->appendRow(row); - } setNeedsSave(false); } void KCMPlymouth::save() { + setBusy(true); QVariantMap helperargs; helperargs[QStringLiteral("theme")] = m_selectedPlugin; @@ -188,8 +229,12 @@ KAuth::ExecuteJob *job = action.execute(); bool rc = job->exec(); if (!rc) { - KMessageBox::error(nullptr, i18n("Unable to authenticate/execute the action: %1, %2", job->error(), job->errorString())); + if (job->error() == KAuth::ActionReply::UserCancelledError) { + emit showErrorMessage(i18n("Unable to authenticate/execute the action: %1 (%2)", job->error(), job->errorString())); + } + load(); } + setBusy(false); } void KCMPlymouth::uninstall(const QString &plugin) @@ -205,10 +250,11 @@ KAuth::ExecuteJob *job = action.execute(); bool rc = job->exec(); if (!rc) { - KMessageBox::error(nullptr, i18n("Unable to authenticate/execute the action: %1, %2", job->error(), job->errorString())); + emit showErrorMessage(i18n("Unable to authenticate/execute the action: %1 (%2)", job->error(), job->errorString())); } else { KConfigGroup installedCg(KSharedConfig::openConfig(QStringLiteral("kplymouththemeinstallerrc")), "DownloadedThemes"); installedCg.deleteEntry(plugin); + emit showSuccessMessage(i18n("Theme uninstalled successfully.")); load(); } } diff --git a/src/kcm_plymouth.desktop b/src/kcm_plymouth.desktop --- a/src/kcm_plymouth.desktop +++ b/src/kcm_plymouth.desktop @@ -11,7 +11,7 @@ X-KDE-System-Settings-Parent-Category=session X-KDE-Weight=80 -Name=Boot Splash +Name=Boot Splash Screen Name[ca]=Pantalla de presentació d'inici Name[ca@valencia]=Pantalla de presentació d'inici Name[cs]=Startovací obrazovka @@ -48,7 +48,7 @@ Name[x-test]=xxBoot Splashxx Name[zh_CN]=启动画面 Name[zh_TW]=啟動畫面 -Comment=Plymouth Splash Screen +Comment=Choose Plymouth boot splash screen Comment[ca]=Pantalla de presentació del Plymouth Comment[ca@valencia]=Pantalla de presentació del Plymouth Comment[cs]=Úvodní obrazovka Plymouth @@ -85,7 +85,7 @@ Comment[x-test]=xxPlymouth Splash Screenxx Comment[zh_CN]=Plymouth 闪屏 Comment[zh_TW]=Plymouth 啟動畫面 -X-KDE-Keywords=plymouth,splash,splash screen +X-KDE-Keywords=plymouth,splash,splash screen,boot splash X-KDE-Keywords[ca]=plymouth,splash,pantalla de presentació X-KDE-Keywords[ca@valencia]=plymouth,splash,pantalla de presentació X-KDE-Keywords[da]=plymouth,splash,opstartsskærm diff --git a/src/package/contents/ui/main.qml b/src/package/contents/ui/main.qml --- a/src/package/contents/ui/main.qml +++ b/src/package/contents/ui/main.qml @@ -20,167 +20,80 @@ 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 +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 choose the Plymouth boot splash screen.") - ConfigModule.quickHelp: i18nd("kcm_plymouth", "This module lets you configure the look of the whole workspace with some ready to go presets.") + view.model: kcm.themesModel + view.currentIndex: kcm.selectedPluginIndex + view.enabled: !kcm.busy - SystemPalette {id: syspal} + view.delegate: KCM.GridDelegate { + id: delegate - ColumnLayout { - anchors.fill: parent - QtControls.Label { - text: i18nd("kcm_plymouth", "Select a global splash screen for the system") - wrapMode: Text.WordWrap - Layout.fillWidth: true + text: model.display + toolTip: model.description + + thumbnailAvailable: !!model.screenshot + thumbnail: Image { + anchors.fill: parent + source: model.screenshot + sourceSize: Qt.size(delegate.GridView.view.cellWidth * Screen.devicePixelRatio, + delegate.GridView.view.cellHeight * Screen.devicePixelRatio) } - QtControls.ScrollView { - Layout.fillWidth: true - Layout.fillHeight: true - GridView { - id: grid - model: kcm.themesModel - 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 + actions: [ + Kirigami.Action { + iconName: "edit-delete" + tooltip: i18n("Uninstall") + enabled: model.uninstallable + onTriggered: kcm.uninstall(model.pluginName) + } + ] + onClicked: { + kcm.selectedPlugin = model.pluginName; + view.forceActiveFocus(); + } + } - 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" - visible: image.status != Image.Ready - } - Image { - id: image - anchors { - fill: parent - margins: units.smallSpacing * 2 - } - source: model.screenshot + footer: ColumnLayout { + Kirigami.InlineMessage { + id: infoLabel + Layout.fillWidth: true + showCloseButton: true + } - 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 - } - Timer { - interval: 1000 // FIXME TODO: Use platform value for tooltip activation delay. + QtControls.ProgressBar { + id: progressBar + Layout.fillWidth: true + visible: kcm.busy + indeterminate: true + } - running: parent.containsMouse && !parent.pressedButtons + QtControls.Button { + id: getNewButton + Layout.alignment: Qt.AlignRight + text: i18n("Get New Boot Splash Screens...") + icon.name: "get-hot-new-stuff" + enabled: !kcm.busy + onClicked: kcm.getNewStuff(this) + } + } - 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.uninstallable - iconSource: "list-remove" - tooltip: i18nd("kcm_plymouth", "Uninstall") - flat: false - onClicked: { - kcm.uninstall(model.pluginName); - } - opacity: parent.containsMouse ? 1 : 0 - Behavior on opacity { - PropertyAnimation { - duration: units.longDuration - easing.type: Easing.OutQuad - } - } - } - } - } - } - } + Connections { + target: kcm + onShowSuccessMessage: { + infoLabel.type = Kirigami.MessageType.Positive; + infoLabel.text = message; + infoLabel.visible = true; } - RowLayout { - Item { - Layout.fillWidth: true - } - QtControls.Button { - anchors.right: parent.right - text: i18nd("kcm_plymouth", "Get New Boot Splash Screens...") - iconName: "get-hot-new-stuff" - onClicked: kcm.getNewStuff(); - } + onShowErrorMessage: { + infoLabel.type = Kirigami.MessageType.Error; + infoLabel.text = message; + infoLabel.visible = true; } } }