From d280b6e4aa3c4284941a2dac3d37b78f46a11e62 Mon Sep 17 00:00:00 2001 From: Michail Vourlakos Date: Sun, 31 Mar 2019 14:53:12 +0300 Subject: [PATCH] button to remove locally installed indicators --- app/indicator/factory.cpp | 49 ++++++++++++++++++- app/indicator/factory.h | 10 +++- app/lattedock.notifyrc | 8 +++ app/view/indicator.cpp | 21 +++++++- app/view/indicator.h | 3 ++ declarativeimports/components/ComboBox.qml | 12 +++++ .../components/ComboBoxButton.qml | 8 +++ .../components/ItemDelegate.qml | 42 +++++++++++++--- .../controls/CustomIndicatorButton.qml | 44 +++++++++++++++-- 9 files changed, 182 insertions(+), 15 deletions(-) diff --git a/app/indicator/factory.cpp b/app/indicator/factory.cpp index 1ede16c5e..ba0f01a34 100644 --- a/app/indicator/factory.cpp +++ b/app/indicator/factory.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -75,6 +76,11 @@ Factory::~Factory() m_parentWidget->deleteLater(); } +bool Factory::pluginExists(QString id) const +{ + return m_plugins.contains(id); +} + int Factory::customPluginsCount() { return m_customPluginIds.count(); @@ -90,6 +96,10 @@ QStringList Factory::customPluginNames() return m_customPluginNames; } +QStringList Factory::customLocalPluginIds() +{ + return m_customLocalPluginIds; +} KPluginMetaData Factory::metadata(QString pluginId) { @@ -105,6 +115,7 @@ void Factory::reload() m_plugins.clear(); m_customPluginIds.clear(); m_customPluginNames.clear(); + m_customLocalPluginIds.clear(); for(const auto &path : m_watchedPaths) { QDir standard(path); @@ -129,6 +140,10 @@ void Factory::reload() m_customPluginNames << metadata.name(); } + if (standard.absolutePath().startsWith(QDir::homePath())) { + m_customLocalPluginIds << metadata.pluginId(); + } + QString pluginPath = metadata.fileName().remove("metadata.desktop"); qDebug() << " Indicator Package Loaded ::: " << metadata.name() << " [" << metadata.pluginId() << "]" << " - [" <setIcon(QMessageBox::Warning); + msg->setWindowTitle(i18n("Remove Indicator")); + msg->setText( + i18n("Do you want to remove %0 indicator from your system?").arg(pluginName)); + msg->setStandardButtons(QMessageBox::Yes | QMessageBox::No); + msg->setDefaultButton(QMessageBox::No); + + connect(msg, &QMessageBox::finished, this, [ &, msg, id, pluginName](int result) { + auto showRemovedSucceed = [](QString name) { + auto notification = new KNotification("remove-done", KNotification::CloseOnTimeout); + notification->setText(i18nc("indicator_name, removed success","%0 indicator removed successfully").arg(name)); + notification->sendEvent(); + }; + + if (result == QMessageBox::Yes) { + qDebug() << "Trying to remove indicator :: " << id; + QProcess process; + process.start(QString("kpackagetool5 -r " +id + " -t Latte/Indicator")); + process.waitForFinished(); + showRemovedSucceed(pluginName); + } + }); + + msg->open(); + } +} + void Factory::downloadIndicator() { KNS3::DownloadDialog dialog(QStringLiteral("latte-indicators.knsrc"), m_parentWidget); diff --git a/app/indicator/factory.h b/app/indicator/factory.h index c043b29c3..fde2f86c7 100644 --- a/app/indicator/factory.h +++ b/app/indicator/factory.h @@ -41,15 +41,17 @@ public: Factory(QObject *parent); ~Factory() override; - void reload(); - int customPluginsCount(); QStringList customPluginIds(); QStringList customPluginNames(); + QStringList customLocalPluginIds(); KPluginMetaData metadata(QString pluginId); void downloadIndicator(); + void removeIndicator(QString id); + + bool pluginExists(QString id) const; //! metadata record static bool metadataAreValid(KPluginMetaData &metadata); @@ -61,11 +63,15 @@ public: signals: void customPluginsChanged(); +private: + void reload(); + private: QHash m_plugins; QStringList m_customPluginIds; QStringList m_customPluginNames; + QStringList m_customLocalPluginIds; QStringList m_watchedPaths; diff --git a/app/lattedock.notifyrc b/app/lattedock.notifyrc index d4c8727e1..b1d6e51df 100644 --- a/app/lattedock.notifyrc +++ b/app/lattedock.notifyrc @@ -99,6 +99,14 @@ Name[uk]=Не вдалося експортувати Name[x-test]=xxFailed to exportxx Action=Popup +[Event/remove-done] +Name=Removed successfully +Action=Popup + +[Event/remove-fail] +Name=Failed to remove +Action=Popup + [Event/switch-layout] Name=Switch to layout Name[ca]=Commuta a la disposició diff --git a/app/view/indicator.cpp b/app/view/indicator.cpp index fbaefe916..edc90e5d8 100644 --- a/app/view/indicator.cpp +++ b/app/view/indicator.cpp @@ -52,7 +52,13 @@ Indicator::Indicator(Latte::View *parent) connect(m_view, &Latte::View::latteTasksArePresentChanged, this, &Indicator::latteTasksArePresentChanged); - connect(m_corona->indicatorFactory(), &Latte::Indicator::Factory::customPluginsChanged, this, &Indicator::customPluginsChanged); + connect(m_corona->indicatorFactory(), &Latte::Indicator::Factory::customPluginsChanged, [this]() { + if (!m_corona->indicatorFactory()->pluginExists(m_type)) { + setType("org.kde.latte.default"); + } + + emit customPluginsChanged(); + }); connect(this, &Indicator::pluginChanged, [this]() { if ((m_type != "org.kde.latte.default") && m_type != "org.kde.latte.plasma") { @@ -219,6 +225,11 @@ QStringList Indicator::customPluginNames() const return m_corona->indicatorFactory()->customPluginNames(); } +QStringList Indicator::customLocalPluginIds() const +{ + return m_corona->indicatorFactory()->customLocalPluginIds(); +} + QQmlComponent *Indicator::component() const { return m_component; @@ -398,6 +409,14 @@ void Indicator::downloadIndicator() }); } +void Indicator::removeIndicator(QString pluginId) +{ //! call asynchronously in order to not crash when view settings window + //! loses focus and it closes + QTimer::singleShot(0, [this, pluginId]() { + m_corona->indicatorFactory()->removeIndicator(pluginId); + }); +} + void Indicator::loadConfig() { auto config = m_view->containment()->config().group("Indicator"); diff --git a/app/view/indicator.h b/app/view/indicator.h index 954a5ad81..3e9551dc9 100644 --- a/app/view/indicator.h +++ b/app/view/indicator.h @@ -64,6 +64,7 @@ class Indicator: public QObject Q_PROPERTY(int customPluginsCount READ customPluginsCount NOTIFY customPluginsChanged) Q_PROPERTY(QStringList customPluginIds READ customPluginIds NOTIFY customPluginsChanged) Q_PROPERTY(QStringList customPluginNames READ customPluginNames NOTIFY customPluginsChanged) + Q_PROPERTY(QStringList customLocalPluginIds READ customLocalPluginIds NOTIFY customPluginsChanged) /** * Configuration object: each config key will be a writable property of this object. property bindings work. @@ -102,6 +103,7 @@ public: int customPluginsCount() const; QStringList customPluginIds() const; QStringList customPluginNames() const; + QStringList customLocalPluginIds() const; QObject *configuration() const; QQmlComponent *component() const; @@ -113,6 +115,7 @@ public slots: Q_INVOKABLE void configUiFor(QString type, QQuickItem *parent); Q_INVOKABLE void addIndicator(); Q_INVOKABLE void downloadIndicator(); + Q_INVOKABLE void removeIndicator(QString pluginId); signals: void customPluginsChanged(); diff --git a/declarativeimports/components/ComboBox.qml b/declarativeimports/components/ComboBox.qml index ff8c7d315..afa917a21 100644 --- a/declarativeimports/components/ComboBox.qml +++ b/declarativeimports/components/ComboBox.qml @@ -47,14 +47,22 @@ T.ComboBox { property bool popUpAlignRight: true property int minimumPopUpWidth: 150 property int popUpRelativeX: 0 + property string enabledRole property string iconRole + property string iconToolTipRole + property string iconOnlyWhenHoveredRole + + signal iconClicked(int index); delegate: ItemDelegate { width: control.popup.width enabled: control.enabledRole ? (Array.isArray(control.model) ? modelData[control.enabledRole] : model[control.enabledRole]) : true text: control.textRole ? (Array.isArray(control.model) ? modelData[control.textRole] : model[control.textRole]) : modelData icon: control.iconRole ? (Array.isArray(control.model) ? modelData[control.iconRole] : model[control.iconRole]) : '' + iconToolTip: control.iconToolTipRole ? (Array.isArray(control.model) ? modelData[control.iconToolTipRole] : model[control.iconToolTipRole]) : '' + iconOnlyWhenHovered: control.iconOnlyWhenHoveredRole ? (Array.isArray(control.model) ? modelData[control.iconOnlyWhenHoveredRole] : model[control.iconOnlyWhenHoveredRole]) : '' + highlighted: mouseArea.pressed ? listView.currentIndex == index : control.currentIndex == index blankSpaceForEmptyIcons: control.blankSpaceForEmptyIcons property bool separatorVisible: false @@ -313,6 +321,10 @@ T.ComboBox { LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft LayoutMirroring.childrenInherit: true T.ScrollBar.vertical: Controls.ScrollBar { } + + signal iconClicked(int index); + + onIconClicked: control.iconClicked(index); } background: Rectangle { anchors { diff --git a/declarativeimports/components/ComboBoxButton.qml b/declarativeimports/components/ComboBoxButton.qml index acb3505cd..5209f31d5 100644 --- a/declarativeimports/components/ComboBoxButton.qml +++ b/declarativeimports/components/ComboBoxButton.qml @@ -53,6 +53,10 @@ Rectangle { property string comboBoxEnabledRole: "" property string comboBoxTextRole: "" property string comboBoxIconRole: "" + property string comboBoxIconToolTipRole: "" + property string comboBoxIconOnlyWhenHoveredRole: "" + + signal iconClicked(int index); PlasmaComponents.Button { id: mainButton @@ -86,6 +90,8 @@ Rectangle { enabledRole: comboBoxEnabledRole iconRole: comboBoxIconRole textRole: comboBoxTextRole + iconToolTipRole: comboBoxIconToolTipRole + iconOnlyWhenHoveredRole: comboBoxIconOnlyWhenHoveredRole blankSpaceForEmptyIcons: comboBoxBlankSpaceForEmptyIcons forcePressed: comboBoxForcePressed @@ -95,6 +101,8 @@ Rectangle { (popUpAlignRight ? width : -(root.width - width)) minimumPopUpWidth: Math.max(comboBoxMinimumPopUpWidth, root.width) + + onIconClicked: root.iconClicked(index); } Label{ diff --git a/declarativeimports/components/ItemDelegate.qml b/declarativeimports/components/ItemDelegate.qml index 54e2a13c2..8dfabdaf7 100644 --- a/declarativeimports/components/ItemDelegate.qml +++ b/declarativeimports/components/ItemDelegate.qml @@ -21,6 +21,9 @@ import QtQuick 2.5 import QtQuick.Layouts 1.3 import QtQuick.Templates 2.2 as T import org.kde.plasma.core 2.0 as PlasmaCore + +import org.kde.latte.components 1.0 as LatteComponents + import "private" as Private T.CheckDelegate { @@ -39,7 +42,10 @@ T.CheckDelegate { property bool blankSpaceForEmptyIcons: false property string icon + property string iconToolTip + property bool iconOnlyWhenHovered + readonly property bool isHovered: hovered || iconMouseArea.containsMouse readonly property int margin: 4 contentItem: RowLayout { @@ -48,23 +54,43 @@ T.CheckDelegate { spacing: units.smallSpacing enabled: control.enabled - PlasmaCore.IconItem { + Rectangle { Layout.minimumWidth: parent.height Layout.maximumWidth: parent.height Layout.minimumHeight: parent.height Layout.maximumHeight: parent.height - //height: parent.height - 2*control.margin - //width: parent.height - 2*control.margin - colorGroup: PlasmaCore.Theme.ButtonColorGroup - source: control.icon - visible: icon + visible: icon && (!control.iconOnlyWhenHovered || (control.iconOnlyWhenHovered && control.isHovered)) + color: control.iconToolTip && iconMouseArea.containsMouse ? theme.highlightColor : "transparent" + + PlasmaCore.IconItem { + id: iconElement + anchors.fill: parent + colorGroup: PlasmaCore.Theme.ButtonColorGroup + source: control.icon + } + + LatteComponents.ToolTip{ + parent: iconElement + text: iconToolTip + visible: iconMouseArea.containsMouse + delay: 6 * units.longDuration + } + + MouseArea { + id: iconMouseArea + anchors.fill: parent + hoverEnabled: true + visible: control.iconToolTip + + onClicked: control.ListView.view.iconClicked(index); + } } Rectangle { //blank space when no icon is shown Layout.minimumHeight: parent.height Layout.minimumWidth: parent.height - visible: !icon && control.blankSpaceForEmptyIcons + visible: control.blankSpaceForEmptyIcons && (!icon || (control.iconOnlyWhenHovered && !control.isHovered) ) color: "transparent" } @@ -87,7 +113,7 @@ T.CheckDelegate { opacity: { if (control.highlighted || control.pressed) { return 0.6; - } else if (control.hovered && !control.pressed) { + } else if (control.isHovered && !control.pressed) { return 0.3; } diff --git a/shell/package/contents/controls/CustomIndicatorButton.qml b/shell/package/contents/controls/CustomIndicatorButton.qml index 4c38ec5d3..0e4b0c953 100644 --- a/shell/package/contents/controls/CustomIndicatorButton.qml +++ b/shell/package/contents/controls/CustomIndicatorButton.qml @@ -33,6 +33,8 @@ LatteComponents.ComboBoxButton{ i18n("Download indicator styles from the internet") comboBoxTextRole: "name" comboBoxIconRole: "icon" + comboBoxIconToolTipRole: "iconToolTip" + comboBoxIconOnlyWhenHoveredRole: "iconOnlyWhenHovered" comboBoxBlankSpaceForEmptyIcons: true comboBoxForcePressed: latteView.indicator.type === type comboBoxPopUpAlignRight: Qt.application.layoutDirection !== Qt.RightToLeft @@ -85,6 +87,17 @@ LatteComponents.ComboBoxButton{ custom.updateButtonInformation(); } + + onIconClicked: { + if (index>=0) { + var item = actionsModel.get(index); + var pluginId = item.pluginId; + if (latteView.indicator.customLocalPluginIds.indexOf(pluginId)>=0) { + latteView.indicator.removeIndicator(pluginId); + custom.comboBox.popup.close(); + } + } + } } function updateButtonInformation() { @@ -113,9 +126,22 @@ LatteComponents.ComboBoxButton{ if (latteView.indicator.customPluginsCount > 0) { var pluginIds = latteView.indicator.customPluginIds; var pluginNames = latteView.indicator.customPluginNames; + var localPluginIds = latteView.indicator.customLocalPluginIds; for(var i=0; i=0; + var iconString = canBeRemoved ? 'remove' : ''; + var iconTip = canBeRemoved ? i18n('Remove indicator') : ''; + var iconOnlyForHovered = canBeRemoved ? true : false; + + var element = { + pluginId: pluginIds[i], + name: pluginNames[i], + icon: iconString, + iconToolTip: iconTip, + iconOnlyWhenHovered: iconOnlyForHovered + }; + actionsModel.append(element); } } @@ -141,11 +167,23 @@ LatteComponents.ComboBoxButton{ function appendDefaults() { //! add - var addElement = {pluginId: 'add:', name: i18n('Add Indicator...'), icon: 'list-add'}; + var addElement = { + pluginId: 'add:', + name: i18n('Add Indicator...'), + icon: 'list-add', + iconToolTip: '', + iconOnlyWhenHovered: false + }; actionsModel.append(addElement); //! download - var downloadElement = {pluginId: 'download:', name: i18n('Get New Indicators...'), icon: 'favorites'}; + var downloadElement = { + pluginId: 'download:', + name: i18n('Get New Indicators...'), + icon: 'favorites', + iconToolTip: '', + iconOnlyWhenHovered: false + }; actionsModel.append(downloadElement); }