diff --git a/applets/systemtray/CMakeLists.txt b/applets/systemtray/CMakeLists.txt index 4b5540b7f..7bc5238ec 100644 --- a/applets/systemtray/CMakeLists.txt +++ b/applets/systemtray/CMakeLists.txt @@ -1,32 +1,32 @@ add_definitions(-DTRANSLATION_DOMAIN=\"plasma_applet_org.kde.plasma.private.systemtray\") plasma_install_package(package org.kde.plasma.private.systemtray) set(systemtray_SRCS systemtray.cpp ) ecm_qt_declare_logging_category(systemtray_SRCS HEADER debug.h IDENTIFIER SYSTEM_TRAY CATEGORY_NAME kde.systemtray DEFAULT_SEVERITY Info) add_library(org.kde.plasma.private.systemtray MODULE ${systemtray_SRCS}) kcoreaddons_desktop_to_json(org.kde.plasma.private.systemtray package/metadata.desktop) target_link_libraries(org.kde.plasma.private.systemtray Qt5::Gui Qt5::Quick - KF5::Plasma Qt5::DBus + KF5::Plasma KF5::IconThemes KF5::XmlGui KF5::I18n) install(TARGETS org.kde.plasma.private.systemtray DESTINATION ${KDE_INSTALL_PLUGINDIR}/plasma/applets) add_subdirectory(container) if(BUILD_TESTING) add_subdirectory(tests) endif() diff --git a/applets/systemtray/package/contents/applet/CompactApplet.qml b/applets/systemtray/package/contents/applet/CompactApplet.qml index b8da65d0f..23557fddd 100644 --- a/applets/systemtray/package/contents/applet/CompactApplet.qml +++ b/applets/systemtray/package/contents/applet/CompactApplet.qml @@ -1,82 +1,79 @@ /* * Copyright 2011 Marco Martin + * Copyright 2019 ivan tkachenko * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; 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 org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.plasmoid 2.0 PlasmaCore.ToolTipArea { id: appletRoot objectName: "org.kde.desktop-CompactApplet" anchors.fill: parent icon: plasmoid.icon mainText: plasmoid.toolTipMainText subText: plasmoid.toolTipSubText - location: if (plasmoid.parent && plasmoid.parent.parent.objectName === "hiddenTasksColumn" && plasmoid.location !== PlasmaCore.Types.LeftEdge) { - return PlasmaCore.Types.RightEdge; - } else { - return plasmoid.location; - } + active: !plasmoid.expanded textFormat: plasmoid.toolTipTextFormat mainItem: plasmoid.toolTipItem ? plasmoid.toolTipItem : null property Item fullRepresentation property Item compactRepresentation Connections { target: plasmoid onContextualActionsAboutToShow: appletRoot.hideToolTip() } Layout.minimumWidth: { switch (plasmoid.formFactor) { case PlasmaCore.Types.Vertical: return 0; case PlasmaCore.Types.Horizontal: return height; default: return units.gridUnit * 3; } } Layout.minimumHeight: { switch (plasmoid.formFactor) { case PlasmaCore.Types.Vertical: return width; case PlasmaCore.Types.Horizontal: return 0; default: return units.gridUnit * 3; } } onCompactRepresentationChanged: { if (compactRepresentation) { compactRepresentation.parent = appletRoot; compactRepresentation.anchors.fill = appletRoot; compactRepresentation.visible = true; } appletRoot.visible = true; } } diff --git a/applets/systemtray/package/contents/ui/ConfigEntries.qml b/applets/systemtray/package/contents/ui/ConfigEntries.qml index 3df4fd721..b60da2438 100644 --- a/applets/systemtray/package/contents/ui/ConfigEntries.qml +++ b/applets/systemtray/package/contents/ui/ConfigEntries.qml @@ -1,266 +1,252 @@ /* * Copyright 2013 Sebastian Kügler * Copyright 2014 Marco Martin + * Copyright 2019 ivan tkachenko * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. */ import QtQuick 2.5 import QtQuick.Controls 1.4 as QQC1 import QtQuick.Controls 2.5 as QQC2 import QtQuick.Layouts 1.3 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents import org.kde.plasma.extras 2.0 as PlasmaExtras import org.kde.kquickcontrolsaddons 2.0 import org.kde.kquickcontrols 2.0 as KQC import org.kde.kirigami 2.5 as Kirigami ColumnLayout { id: iconsPage signal configurationChanged property var cfg_shownItems: [] property var cfg_hiddenItems: [] property alias cfg_showAllItems: showAllCheckBox.checked function saveConfig () { for (var i in tableView.model) { //tableView.model[i].applet.globalShortcut = tableView.model[i].shortcut } } - PlasmaCore.DataSource { - id: statusNotifierSource - engine: "statusnotifieritem" - interval: 0 - onSourceAdded: { - connectSource(source) - } - Component.onCompleted: { - connectedSources = sources - } - } - - PlasmaCore.SortFilterModel { + StatusNotifierItemModel { id: statusNotifierModel - sourceModel: PlasmaCore.DataModel { - dataSource: statusNotifierSource - } } Kirigami.FormLayout { QQC2.CheckBox { id: showAllCheckBox text: i18n("Always show all entries") } QQC2.Button { // just for measurement id: measureButton text: "measureButton" visible: false } // resizeToContents does not take into account the heading QQC2.Label { id: shortcutColumnMeasureLabel text: shortcutColumn.title visible: false } } function retrieveAllItems() { var list = []; for (var i = 0; i < statusNotifierModel.count; ++i) { var item = statusNotifierModel.get(i); list.push({ "index": i, "taskId": item.Id, "name": item.Title, "iconName": item.IconName, "icon": item.Icon }); } var lastIndex = list.length; for (var i = 0; i < plasmoid.applets.length; ++i) { var item = plasmoid.applets[i] list.push({ "index": (i + lastIndex), "applet": item, "taskId": item.pluginName, "name": item.title, "iconName": item.icon, "shortcut": item.globalShortcut }); } list.sort(function(a, b) { return a.name.localeCompare(b.name); }); return list; } // There is no QQC2 version of TableView yet QQC1.TableView { id: tableView Layout.fillWidth: true Layout.fillHeight: true model: retrieveAllItems() horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff flickableItem.boundsBehavior: Flickable.StopAtBounds Component.onCompleted: { visibilityColumn.resizeToContents() shortcutColumn.resizeToContents() } // Taken from QtQuickControls BasicTableViewStyle, just to make its height sensible... rowDelegate: BorderImage { visible: styleData.selected || styleData.alternate source: "image://__tablerow/" + (styleData.alternate ? "alternate_" : "") + (tableView.activeFocus ? "active" : "") height: measureButton.height border.left: 4 ; border.right: 4 } QQC1.TableViewColumn { id: entryColumn width: tableView.viewport.width - visibilityColumn.width - shortcutColumn.width title: i18nc("Name of the system tray entry", "Entry") movable: false resizable: false delegate: RowLayout { Item { // spacer Layout.preferredWidth: 1 Layout.fillHeight: true } QIconItem { width: units.iconSizes.small height: width icon: modelData.iconName || modelData.icon || "" } QQC2.Label { Layout.fillWidth: true text: modelData.name elide: Text.ElideRight wrapMode: Text.NoWrap } } } QQC1.TableViewColumn { id: visibilityColumn title: i18n("Visibility") movable: false resizable: false delegate: QQC2.ComboBox { implicitWidth: Math.round(units.gridUnit * 6.5) // ComboBox sizing is broken enabled: !showAllCheckBox.checked currentIndex: { if (cfg_shownItems.indexOf(modelData.taskId) != -1) { return 1; } else if (cfg_hiddenItems.indexOf(modelData.taskId) != -1) { return 2; } else { return 0; } } // activated, in contrast to currentIndexChanged, only fires if the user himself changed the value onActivated: { var shownIndex = cfg_shownItems.indexOf(modelData.taskId); var hiddenIndex = cfg_hiddenItems.indexOf(modelData.taskId); switch (index) { case 0: { if (shownIndex > -1) { cfg_shownItems.splice(shownIndex, 1); } if (hiddenIndex > -1) { cfg_hiddenItems.splice(hiddenIndex, 1); } break; } case 1: { if (shownIndex === -1) { cfg_shownItems.push(modelData.taskId); } if (hiddenIndex > -1) { cfg_hiddenItems.splice(hiddenIndex, 1); } break; } case 2: { if (shownIndex > -1) { cfg_shownItems.splice(shownIndex, 1); } if (hiddenIndex === -1) { cfg_hiddenItems.push(modelData.taskId); } break; } } iconsPage.configurationChanged(); } model: [i18n("Auto"), i18n("Shown"), i18n("Hidden")] } } QQC1.TableViewColumn { id: shortcutColumn title: i18n("Keyboard Shortcut") // FIXME doesn't fit movable: false resizable: false // this Item wrapper prevents TableView from ripping apart the two KeySequenceItem buttons delegate: Item { implicitWidth: Math.max(shortcutColumnMeasureLabel.width, keySequenceItem.width) + 10 height: keySequenceItem.height KQC.KeySequenceItem { id: keySequenceItem anchors.right: parent.right keySequence: modelData.shortcut // only Plasmoids have that visible: modelData.hasOwnProperty("shortcut") onKeySequenceChanged: { if (keySequence !== modelData.shortcut) { // both SNIs and plasmoids are listed in the same TableView // but they come from two separate models, so we need to subtract // the SNI model count to get the actual plasmoid index var index = modelData.index - statusNotifierModel.count plasmoid.applets[index].globalShortcut = keySequence iconsPage.configurationChanged() } shortcutColumn.resizeToContents() } } } } } } diff --git a/applets/systemtray/package/contents/ui/ExpandedRepresentation.qml b/applets/systemtray/package/contents/ui/ExpandedRepresentation.qml index 94e576dde..35ac10090 100644 --- a/applets/systemtray/package/contents/ui/ExpandedRepresentation.qml +++ b/applets/systemtray/package/contents/ui/ExpandedRepresentation.qml @@ -1,144 +1,142 @@ /* * Copyright 2016 Marco Martin + * Copyright 2019 ivan tkachenko * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; 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 org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents import org.kde.plasma.extras 2.0 as PlasmaExtras Item { id: expandedRepresentation - //set width/height to avoid an useless Dialog resize - width: Layout.minimumWidth - height: Layout.minimumHeight Layout.minimumWidth: units.gridUnit * 24 Layout.minimumHeight: units.gridUnit * 21 Layout.preferredWidth: Layout.minimumWidth Layout.preferredHeight: Layout.minimumHeight * 1.5 property alias activeApplet: container.activeApplet property alias hiddenLayout: hiddenItemsView.layout PlasmaComponents.ToolButton { id: pinButton anchors.right: parent.right width: Math.round(units.gridUnit * 1.25) height: width checkable: true checked: plasmoid.configuration.pin onCheckedChanged: plasmoid.configuration.pin = checked iconSource: "window-pin" z: 2 tooltip: i18n("Keep Open") } PlasmaExtras.Heading { id: heading level: 1 anchors { left: parent.left top: parent.top right: parent.right topMargin: hiddenItemsView.visible ? units.smallSpacing : 0 leftMargin: { //Menu mode if (!activeApplet && hiddenItemsView.visible) { return units.smallSpacing; //applet open, sidebar } else if (activeApplet && hiddenItemsView.visible) { return hiddenItemsView.iconColumnWidth + units.largeSpacing; //applet open, no sidebar } else { return 0; } } } visible: activeApplet text: activeApplet ? activeApplet.title : "" MouseArea { anchors.fill: parent onClicked: { if (activeApplet) { activeApplet.expanded = false; dialog.visible = true; } } } } PlasmaExtras.Heading { id: noAppletHeading level: 1 anchors { left: parent.left top: parent.top right: parent.right topMargin: hiddenItemsView.visible ? units.smallSpacing : 0 leftMargin: units.smallSpacing } text: i18n("Status and Notifications") visible: !heading.visible } PlasmaCore.SvgItem { anchors { left: parent.left leftMargin: hiddenLayout.width top: parent.top bottom: parent.bottom margins: -units.gridUnit } visible: hiddenItemsView.visible && activeApplet width: lineSvg.elementSize("vertical-line").width elementId: "vertical-line" svg: PlasmaCore.Svg { id: lineSvg; imagePath: "widgets/line" } } HiddenItemsView { id: hiddenItemsView anchors { left: parent.left top: noAppletHeading.bottom topMargin: units.smallSpacing bottom: parent.bottom } } PlasmoidPopupsContainer { id: container anchors { left: parent.left right: parent.right top: heading.bottom bottom: parent.bottom leftMargin: hiddenItemsView.visible ? hiddenItemsView.iconColumnWidth + units.largeSpacing : 0 } } } diff --git a/applets/systemtray/package/contents/ui/HiddenItemsView.qml b/applets/systemtray/package/contents/ui/HiddenItemsView.qml index ea1f386c5..eb5088c65 100644 --- a/applets/systemtray/package/contents/ui/HiddenItemsView.qml +++ b/applets/systemtray/package/contents/ui/HiddenItemsView.qml @@ -1,73 +1,75 @@ /* * Copyright 2016 Marco Martin + * Copyright 2019 ivan tkachenko * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; 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 org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents import org.kde.plasma.extras 2.0 as PlasmaExtras PlasmaExtras.ScrollArea { id: hiddenTasksView - visible: !activeApplet || activeApplet.parent.parent == hiddenTasksColumn - width: activeApplet ? iconColumnWidth : parent.width + visible: !root.activeApplet || root.activeAppletContainer === hiddenTasksColumn + width: root.activeApplet ? iconColumnWidth : parent.width property alias layout: hiddenTasksColumn //Useful to align stuff to the column of icons, both in expanded and shrink modes property int iconColumnWidth: root.hiddenItemSize + highlight.marginHints.left + highlight.marginHints.right horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff - verticalScrollBarPolicy: activeApplet ? Qt.ScrollBarAlwaysOff : Qt.ScrollBarAsNeeded + verticalScrollBarPolicy: root.activeApplet ? Qt.ScrollBarAlwaysOff : Qt.ScrollBarAsNeeded Flickable { + anchors.fill: parent contentWidth: width contentHeight: hiddenTasksColumn.height MouseArea { - width: parent.width + width: hiddenTasksView.width height: hiddenTasksColumn.height drag.filterChildren: true hoverEnabled: true onExited: hiddenTasksColumn.hoveredItem = null; CurrentItemHighLight { - target: root.activeApplet && root.activeApplet.parent.parent == hiddenTasksColumn ? root.activeApplet.parent : null + target: root.activeAppletContainer === hiddenTasksColumn ? root.activeAppletItem : null location: PlasmaCore.Types.LeftEdge } PlasmaComponents.Highlight { id: highlight visible: hiddenTasksColumn.hoveredItem != null && !root.activeApplet y: hiddenTasksColumn.hoveredItem ? hiddenTasksColumn.hoveredItem.y : 0 width: hiddenTasksColumn.hoveredItem ? hiddenTasksColumn.hoveredItem.width : 0 height: hiddenTasksColumn.hoveredItem ? hiddenTasksColumn.hoveredItem.height : 0 } - Column { + ColumnLayout { id: hiddenTasksColumn spacing: units.smallSpacing - width: parent.width + width: hiddenTasksView.width property Item hoveredItem property alias marginHints: highlight.marginHints objectName: "hiddenTasksColumn" } } } } diff --git a/applets/systemtray/package/contents/ui/PlasmoidPopupsContainer.qml b/applets/systemtray/package/contents/ui/PlasmoidPopupsContainer.qml index 381a5455e..1ca58eff1 100644 --- a/applets/systemtray/package/contents/ui/PlasmoidPopupsContainer.qml +++ b/applets/systemtray/package/contents/ui/PlasmoidPopupsContainer.qml @@ -1,104 +1,104 @@ /* * Copyright 2015 Marco Martin + * Copyright 2019 ivan tkachenko * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.4 import QtQuick.Layouts 1.1 import QtQuick.Controls 1.4 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents StackView { id: mainStack clip: true focus: true Layout.minimumWidth: units.gridUnit * 12 Layout.minimumHeight: units.gridUnit * 12 property Item activeApplet onActiveAppletChanged: { - if (activeApplet != null) { + if (activeApplet) { //reset any potential anchor activeApplet.fullRepresentationItem.anchors.left = undefined; activeApplet.fullRepresentationItem.anchors.top = undefined; activeApplet.fullRepresentationItem.anchors.right = undefined; activeApplet.fullRepresentationItem.anchors.bottom = undefined; activeApplet.fullRepresentationItem.anchors.centerIn = undefined; activeApplet.fullRepresentationItem.anchors.fill = undefined; - mainStack.replace({item: activeApplet.fullRepresentationItem, immediate: !dialog.visible, properties: {focus: true}}); } else { mainStack.replace(emptyPage); } } Connections { target: plasmoid onAppletRemoved: { - if (applet == root.activeApplet) { + if (applet === root.activeApplet) { mainStack.clear() } } } //used to animate away to nothing Item { id: emptyPage } delegate: StackViewDelegate { function transitionFinished(properties) { properties.exitItem.opacity = 1 } replaceTransition: StackViewTransition { ParallelAnimation { PropertyAnimation { target: enterItem property: "x" from: enterItem.width to: 0 duration: units.longDuration } PropertyAnimation { target: enterItem property: "opacity" from: 0 to: 1 duration: units.longDuration } } ParallelAnimation { PropertyAnimation { target: exitItem property: "x" from: 0 to: -exitItem.width duration: units.longDuration } PropertyAnimation { target: exitItem property: "opacity" from: 1 to: 0 duration: units.longDuration } } } } } diff --git a/applets/systemtray/package/contents/ui/StatusNotifierItemModel.qml b/applets/systemtray/package/contents/ui/StatusNotifierItemModel.qml new file mode 100644 index 000000000..c83903d35 --- /dev/null +++ b/applets/systemtray/package/contents/ui/StatusNotifierItemModel.qml @@ -0,0 +1,39 @@ +/* + * Copyright 2019 ivan tkachenko + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. + */ + + +import QtQuick 2.0 + +import org.kde.plasma.core 2.0 as PlasmaCore + +PlasmaCore.SortFilterModel { + property alias source: statusNotifierSource + sourceModel: PlasmaCore.DataModel { + dataSource: PlasmaCore.DataSource { + id: statusNotifierSource + engine: "statusnotifieritem" + interval: 0 + onSourceAdded: { + connectSource(source) + } + Component.onCompleted: { + connectedSources = sources + } + } + } +} diff --git a/applets/systemtray/package/contents/ui/items/AbstractItem.qml b/applets/systemtray/package/contents/ui/items/AbstractItem.qml index 7489bc20a..9d40e35d9 100644 --- a/applets/systemtray/package/contents/ui/items/AbstractItem.qml +++ b/applets/systemtray/package/contents/ui/items/AbstractItem.qml @@ -1,143 +1,167 @@ /* * Copyright 2016 Marco Martin + * Copyright 2019 ivan tkachenko * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; 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.12 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents PlasmaCore.ToolTipArea { id: abstractItem + objectName: "abstractItem" - height: effectiveItemSize + marginHints.top + marginHints.bottom - width: labelVisible ? parent.width : effectiveItemSize + marginHints.left + marginHints.right + Layout.fillHeight: !(labelVisible || vertical) + Layout.fillWidth: labelVisible || vertical + Layout.preferredHeight: effectiveItemSize + marginHints.top + marginHints.bottom + Layout.preferredWidth: labelVisible ? -1 : effectiveItemSize + marginHints.left + marginHints.right property real effectiveItemSize: hidden ? root.hiddenItemSize : root.itemSize property string itemId property string category property alias text: label.text - property bool hidden: parent.objectName == "hiddenTasksColumn" + property bool hidden: parent.objectName === "hiddenTasksColumn" property QtObject marginHints: parent.marginHints - property bool labelVisible: abstractItem.hidden && !root.activeApplet + property alias labelVisible: label.visible + property Item iconItemContainer: iconItemContainer property Item iconItem - //PlasmaCore.Types.ItemStatus - property int status + property int /* PlasmaCore.Types.ItemStatus */ status property QtObject model signal clicked(var mouse) signal wheel(var wheel) signal contextMenu(var mouse) property bool forcedHidden: plasmoid.configuration.hiddenItems.indexOf(itemId) !== -1 property bool forcedShown: plasmoid.configuration.showAllItems || plasmoid.configuration.shownItems.indexOf(itemId) !== -1 property bool categoryShown: shownCategories.indexOf(category) !== -1; readonly property int effectiveStatus: { if (!categoryShown || status === PlasmaCore.Types.HiddenStatus) { return PlasmaCore.Types.HiddenStatus } else if (forcedShown || (!forcedHidden && status !== PlasmaCore.Types.PassiveStatus)) { return PlasmaCore.Types.ActiveStatus } else { return PlasmaCore.Types.PassiveStatus } } - /* subclasses need to assign to this tiiltip properties + RowLayout { + anchors.fill: parent + spacing: 0 + + Item { + id: iconItemContainer + property real size: Math.min(parent.width - marginHints.left - marginHints.right, + parent.height - marginHints.top - marginHints.bottom) + Layout.preferredWidth: abstractItem.effectiveItemSize + Layout.preferredHeight: abstractItem.effectiveItemSize + Layout.leftMargin: labelVisible ? marginHints.left : 0 + Layout.rightMargin: labelVisible ? marginHints.right : 0 + Layout.alignment: Qt.AlignVCenter | (labelVisible ? Qt.AlignLeft : Qt.AlignHCenter) + } + PlasmaComponents.Label { + id: label + Layout.fillWidth: true + + opacity: visible ? 1 : 0 + visible: abstractItem.hidden && !root.activeApplet + Behavior on opacity { + NumberAnimation { + duration: units.longDuration + easing.type: Easing.InOutQuad + } + } + } + } + + /* subclasses need to assign to this tooltip properties mainText: subText: - icon: + icon: */ location: { - if (abstractItem.parent && abstractItem.parent.objectName === "hiddenTasksColumn") { + if (hidden) { if (LayoutMirroring.enabled && plasmoid.location !== PlasmaCore.Types.RightEdge) { return PlasmaCore.Types.LeftEdge; } else if (plasmoid.location !== PlasmaCore.Types.LeftEdge) { return PlasmaCore.Types.RightEdge; } } return plasmoid.location; } //BEGIN CONNECTIONS onEffectiveStatusChanged: updateItemVisibility(abstractItem); onContainsMouseChanged: { if (hidden && containsMouse) { root.hiddenLayout.hoveredItem = abstractItem } } Component.onCompleted: updateItemVisibility(abstractItem); //dangerous but needed due how repeater reparents onParentChanged: updateItemVisibility(abstractItem); + onIconItemChanged: { + if (iconItem) { + iconItem.parent = iconItemContainer; + iconItem.anchors.fill = iconItemContainer; + } + } + //END CONNECTIONS PulseAnimation { targetItem: iconItem running: (abstractItem.status === PlasmaCore.Types.NeedsAttentionStatus || abstractItem.status === PlasmaCore.Types.RequiresAttentionStatus ) && units.longDuration > 0 } MouseArea { id: mouseArea - anchors.fill: abstractItem + anchors.fill: parent hoverEnabled: true drag.filterChildren: true acceptedButtons: Qt.LeftButton | Qt.MiddleButton | Qt.RightButton onClicked: abstractItem.clicked(mouse) onPressed: { abstractItem.hideToolTip() if (mouse.button === Qt.RightButton) { abstractItem.contextMenu(mouse) } } onPressAndHold: { abstractItem.contextMenu(mouse) } onWheel: { abstractItem.wheel(wheel); //Don't accept the event in order to make the scrolling by mouse wheel working //for the parent scrollview this icon is in. wheel.accepted = false; } } - - PlasmaComponents.Label { - id: label - anchors { - left: parent.left - leftMargin: iconItem ? iconItem.width + units.smallSpacing : 0 - verticalCenter: parent.verticalCenter - } - opacity: labelVisible ? 1 : 0 - visible: abstractItem.hidden - Behavior on opacity { - NumberAnimation { - duration: units.longDuration - easing.type: Easing.InOutQuad - } - } - } } diff --git a/applets/systemtray/package/contents/ui/items/PlasmoidItem.qml b/applets/systemtray/package/contents/ui/items/PlasmoidItem.qml index 4d9b26235..931fd664f 100644 --- a/applets/systemtray/package/contents/ui/items/PlasmoidItem.qml +++ b/applets/systemtray/package/contents/ui/items/PlasmoidItem.qml @@ -1,83 +1,85 @@ /* * Copyright 2015 Marco Martin + * Copyright 2019 ivan tkachenko * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.1 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents +// Container for Plasma applet with expandable section, like Bluetooth, Volume, Networks etc. AbstractItem { id: plasmoidContainer property Item applet - iconItem: applet - text: applet ? applet.title : "" + text: applet ? applet.title : "" + iconItem: applet itemId: applet ? applet.pluginName : "" category: applet ? plasmoid.nativeInterface.plasmoidCategory(applet) : "UnknownCategory" mainText: applet ? applet.toolTipMainText : "" subText: applet ? applet.toolTipSubText : "" icon: applet ? applet.icon : "" mainItem: applet && applet.toolTipItem ? applet.toolTipItem : null textFormat: applet ? applet.toolTipTextFormat : "" status: applet ? applet.status : PlasmaCore.Types.UnknownStatus active: root.activeApplet !== applet onClicked: { if (applet && mouse.button === Qt.LeftButton) { - applet.expanded = true; + applet.expanded = !applet.expanded; } } onContextMenu: { if (applet) { plasmoid.nativeInterface.showPlasmoidMenu(applet, 0, plasmoidContainer.hidden ? applet.height : 0); } } onHeightChanged: { if (applet) { applet.width = height } } onAppletChanged: { if (!applet) { plasmoidContainer.destroy(); print("applet destroyed") } } Connections { target: applet onExpandedChanged: { if (expanded) { var oldApplet = root.activeApplet; root.activeApplet = applet; if (oldApplet) { oldApplet.expanded = false; } dialog.visible = true; } else if (root.activeApplet === applet) { - if (!applet.parent.hidden) { + if (!hidden) { dialog.visible = false; } //if not expanded we don't have an active applet anymore root.activeApplet = null; } } } } diff --git a/applets/systemtray/package/contents/ui/items/StatusNotifierItem.qml b/applets/systemtray/package/contents/ui/items/StatusNotifierItem.qml index bf9261b21..ab9140457 100644 --- a/applets/systemtray/package/contents/ui/items/StatusNotifierItem.qml +++ b/applets/systemtray/package/contents/ui/items/StatusNotifierItem.qml @@ -1,129 +1,120 @@ /* * Copyright 2016 Marco Martin + * Copyright 2019 ivan tkachenko * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.1 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents AbstractItem { id: taskIcon itemId: Id text: Title mainText: ToolTipTitle != "" ? ToolTipTitle : Title subText: ToolTipSubTitle icon: ToolTipIcon != "" ? ToolTipIcon : Icon ? Icon : IconName textFormat: Text.AutoText category: Category status: { switch (Status) { case "Active": return PlasmaCore.Types.ActiveStatus; case "NeedsAttention": return PlasmaCore.Types.NeedsAttentionStatus; //just assume passive default: return PlasmaCore.Types.PassiveStatus; } } - iconItem: iconItem - - PlasmaCore.IconItem { - id: iconItem + iconItem: PlasmaCore.IconItem { source: Icon ? Icon : IconName - width: Math.min(parent.width, parent.height) - height: width active: taskIcon.containsMouse - - anchors { - left: parent.left - verticalCenter: parent.verticalCenter - } } onContextMenu: { openContextMenu(plasmoid.nativeInterface.popupPosition(taskIcon, mouse.x, mouse.y)) } onClicked: { var pos = plasmoid.nativeInterface.popupPosition(taskIcon, mouse.x, mouse.y); switch (mouse.button) { case Qt.LeftButton: { - var service = statusNotifierSource.serviceForSource(DataEngineSource); + var service = statusNotifierModel.source.serviceForSource(DataEngineSource); var operation = service.operationDescription("Activate"); operation.x = pos.x; operation.y = pos.y; var job = service.startOperationCall(operation); job.finished.connect(function () { if (!job.result) { // On error try to invoke the context menu. // Workaround primarily for apps using libappindicator. openContextMenu(pos); } }); break; } case Qt.RightButton: openContextMenu(pos); break; case Qt.MiddleButton: - var service = statusNotifierSource.serviceForSource(DataEngineSource); + var service = statusNotifierModel.source.serviceForSource(DataEngineSource); var operation = service.operationDescription("SecondaryActivate"); operation.x = pos.x; operation.y = pos.y; service.startOperationCall(operation); break; } } function openContextMenu(pos) { - var service = statusNotifierSource.serviceForSource(DataEngineSource); + var service = statusNotifierModel.source.serviceForSource(DataEngineSource); var operation = service.operationDescription("ContextMenu"); operation.x = pos.x; operation.y = pos.y; var job = service.startOperationCall(operation); job.finished.connect(function () { plasmoid.nativeInterface.showStatusNotifierContextMenu(job, taskIcon); }); } onWheel: { //don't send activateVertScroll with a delta of 0, some clients seem to break (kmix) if (wheel.angleDelta.y !== 0) { - var service = statusNotifierSource.serviceForSource(DataEngineSource); + var service = statusNotifierModel.source.serviceForSource(DataEngineSource); var operation = service.operationDescription("Scroll"); operation.delta =wheel.angleDelta.y; operation.direction = "Vertical"; service.startOperationCall(operation); } if (wheel.angleDelta.x !== 0) { - var service = statusNotifierSource.serviceForSource(DataEngineSource); + var service = statusNotifierModel.source.serviceForSource(DataEngineSource); var operation = service.operationDescription("Scroll"); operation.delta =wheel.angleDelta.x; operation.direction = "Horizontal"; service.startOperationCall(operation); } } } diff --git a/applets/systemtray/package/contents/ui/main.qml b/applets/systemtray/package/contents/ui/main.qml index dfa5be650..43c3af7f2 100644 --- a/applets/systemtray/package/contents/ui/main.qml +++ b/applets/systemtray/package/contents/ui/main.qml @@ -1,388 +1,371 @@ /* * Copyright 2011 Marco Martin + * Copyright 2019 ivan tkachenko * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.5 import QtQuick.Layouts 1.1 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.plasmoid 2.0 import org.kde.draganddrop 2.0 as DnD import org.kde.kirigami 2.5 as Kirigami import "items" MouseArea { id: root - Layout.minimumWidth: vertical ? units.iconSizes.small : tasksRow.implicitWidth + (expander.visible ? expander.implicitWidth : 0) + units.smallSpacing - - Layout.minimumHeight: vertical ? tasksRow.implicitHeight + (expander.visible ? expander.implicitHeight : 0) + units.smallSpacing : units.smallSpacing - - Layout.preferredHeight: Layout.minimumHeight + Layout.fillWidth: vertical + Layout.fillHeight: !vertical + Layout.minimumWidth: vertical ? 0 : mainLayout.implicitWidth + Layout.minimumHeight: !vertical ? 0 : mainLayout.implicitHeight LayoutMirroring.enabled: !vertical && Qt.application.layoutDirection === Qt.RightToLeft LayoutMirroring.childrenInherit: true property var iconSizes: ["small", "smallMedium", "medium", "large", "huge", "enormous"]; property int iconSize: plasmoid.configuration.iconSize + (Kirigami.Settings.tabletMode ? 1 : 0) property bool vertical: plasmoid.formFactor === PlasmaCore.Types.Vertical readonly property int itemSize: units.roundToIconSize(Math.min(Math.min(width, height), units.iconSizes[iconSizes[Math.min(iconSizes.length-1, iconSize)]])) property int hiddenItemSize: units.iconSizes.smallMedium property alias expanded: dialog.visible property Item activeApplet + property Item activeAppletItem: findParentNamed(activeApplet, "abstractItem") + property Item activeAppletContainer: activeAppletItem ? activeAppletItem.parent : null + property int status: dialog.visible ? PlasmaCore.Types.RequiresAttentionStatus : PlasmaCore.Types.PassiveStatus - property alias visibleLayout: tasksRow + property alias visibleLayout: tasksLayout property alias hiddenLayout: expandedRepresentation.hiddenLayout property alias statusNotifierModel: statusNotifierModel // workaround https://bugreports.qt.io/browse/QTBUG-71238 / https://bugreports.qt.io/browse/QTBUG-72004 property Component plasmoidItemComponent: Qt.createComponent("items/PlasmoidItem.qml") Plasmoid.onExpandedChanged: { if (!plasmoid.expanded) { dialog.visible = plasmoid.expanded; + root.activeApplet = null; } } + // Shouldn't it be part of Qt? + function findParentNamed(object, objectName) { + if (object) { + while (object = object.parent) { + if (object.objectName === objectName) { + return object; + } + } + } + return null; + } + function updateItemVisibility(item) { switch (item.effectiveStatus) { case PlasmaCore.Types.HiddenStatus: - if (item.parent === invisibleEntriesContainer) { - return; + if (item.parent !== invisibleEntriesContainer) { + item.parent = invisibleEntriesContainer; } - - item.parent = invisibleEntriesContainer; break; case PlasmaCore.Types.ActiveStatus: if (visibleLayout.children.length === 0) { item.parent = visibleLayout; //notifications is always the first } else if (visibleLayout.children[0].itemId === "org.kde.plasma.notifications" && item.itemId !== "org.kde.plasma.notifications") { plasmoid.nativeInterface.reorderItemAfter(item, visibleLayout.children[0]); } else if (visibleLayout.children[0] !== item) { plasmoid.nativeInterface.reorderItemBefore(item, visibleLayout.children[0]); } break; case PlasmaCore.Types.PassiveStatus: if (hiddenLayout.children.length === 0) { item.parent = hiddenLayout; //notifications is always the first } else if (hiddenLayout.children[0].itemId === "org.kde.plasma.notifications" && item.itemId !== "org.kde.plasma.notifications") { plasmoid.nativeInterface.reorderItemAfter(item, hiddenLayout.children[0]); } else if (hiddenLayout.children[0] !== item) { plasmoid.nativeInterface.reorderItemBefore(item, hiddenLayout.children[0]); } - item.x = 0; break; } } onWheel: { // Don't propagate unhandled wheel events wheel.accepted = true; } Containment.onAppletAdded: { //Allow the plasmoid expander to know in what window it will be - var plasmoidContainer = plasmoidItemComponent.createObject(invisibleEntriesContainer, {"x": x, "y": y, "applet": applet}); - - applet.parent = plasmoidContainer - applet.anchors.left = plasmoidContainer.left - applet.anchors.top = plasmoidContainer.top - applet.anchors.bottom = plasmoidContainer.bottom - applet.width = plasmoidContainer.height + var plasmoidContainer = plasmoidItemComponent.createObject(invisibleEntriesContainer, {"applet": applet}); applet.visible = true plasmoidContainer.visible = true - + //This is to make preloading effective, minimizes the scene changes if (applet.fullRepresentationItem) { applet.fullRepresentationItem.width = expandedRepresentation.width applet.fullRepresentationItem.width = expandedRepresentation.height applet.fullRepresentationItem.parent = preloadedStorage; } else { applet.fullRepresentationItemChanged.connect(function() { applet.fullRepresentationItem.width = expandedRepresentation.width applet.fullRepresentationItem.width = expandedRepresentation.height applet.fullRepresentationItem.parent = preloadedStorage; }); } } //being there forces the items to fully load, and they will be reparented in the popup one by one, this item is *never* visible Item { id: preloadedStorage visible: false } Containment.onAppletRemoved: { } Connections { target: plasmoid onUserConfiguringChanged: { if (plasmoid.userConfiguring) { dialog.visible = false } } } - Connections { + Connections { target: plasmoid.configuration onExtraItemsChanged: plasmoid.nativeInterface.allowedPlasmoids = plasmoid.configuration.extraItems } Component.onCompleted: { //script, don't bind plasmoid.nativeInterface.allowedPlasmoids = initializePlasmoidList(); } function initializePlasmoidList() { var newKnownItems = []; var newExtraItems = []; //NOTE:why this? otherwise the interpreter will execute plasmoid.nativeInterface.defaultPlasmoids() on //every access of defaults[], resulting in a very slow iteration var defaults = []; //defaults = defaults.concat(plasmoid.nativeInterface.defaultPlasmoids); defaults = plasmoid.nativeInterface.defaultPlasmoids.slice() var candidate; //Add every plasmoid that is both not enabled explicitly and not already known for (var i = 0; i < defaults.length; ++i) { candidate = defaults[i]; if (plasmoid.configuration.knownItems.indexOf(candidate) === -1) { newKnownItems.push(candidate); if (plasmoid.configuration.extraItems.indexOf(candidate) === -1) { newExtraItems.push(candidate); } } } if (newExtraItems.length > 0) { plasmoid.configuration.extraItems = plasmoid.configuration.extraItems.slice().concat(newExtraItems); } if (newKnownItems.length > 0) { plasmoid.configuration.knownItems = plasmoid.configuration.knownItems.slice().concat(newKnownItems); } return plasmoid.configuration.extraItems; } - PlasmaCore.DataSource { - id: statusNotifierSource - engine: "statusnotifieritem" - interval: 0 - onSourceAdded: { - connectSource(source) - } - Component.onCompleted: { - connectedSources = sources - } - } - - //due to the magic of property bindings this function will be //re-executed all the times a setting changes property var shownCategories: { var array = []; if (plasmoid.configuration.applicationStatusShown) { array.push("ApplicationStatus"); } if (plasmoid.configuration.communicationsShown) { array.push("Communications"); } if (plasmoid.configuration.systemServicesShown) { array.push("SystemServices"); } if (plasmoid.configuration.hardwareControlShown) { array.push("Hardware"); } if (plasmoid.configuration.miscellaneousShown) { array.push("UnknownCategory"); } //nothing? make a regexp that matches nothing if (array.length === 0) { array.push("$^") } return array; } - PlasmaCore.SortFilterModel { - id: statusNotifierModel - sourceModel: PlasmaCore.DataModel { - dataSource: statusNotifierSource - } + StatusNotifierItemModel { + id: statusNotifierModel } //This is a dump for items we don't want to be seen or as an incubation, when they are //created as a nursery before going in their final place Item { id: invisibleEntriesContainer visible: false Repeater { id: tasksRepeater model: statusNotifierModel delegate: StatusNotifierItem {} } //NOTE: this exists mostly for not causing reference errors property QtObject marginHints: QtObject { property int left: 0 property int top: 0 property int right: 0 property int bottom: 0 } } CurrentItemHighLight { - visualParent: tasksRow - target: root.activeApplet && root.activeApplet.parent.parent == tasksRow ? root.activeApplet.parent : root + visualParent: mainLayout + + target: root.activeAppletContainer === tasksLayout ? root.activeAppletItem : root location: plasmoid.location } DnD.DropArea { anchors.fill: parent preventStealing: true; /** Extracts the name of the system tray applet in the drag data if present * otherwise returns null*/ function systemTrayAppletName(event) { if (event.mimeData.formats.indexOf("text/x-plasmoidservicename") < 0) { return null; } var plasmoidId = event.mimeData.getDataAsByteArray("text/x-plasmoidservicename"); if (!plasmoid.nativeInterface.isSystemTrayApplet(plasmoidId)) { return null; } return plasmoidId; } onDragEnter: { if (!systemTrayAppletName(event)) { event.ignore(); } } onDrop: { var plasmoidId = systemTrayAppletName(event); if (!plasmoidId) { event.ignore(); return; } if (plasmoid.configuration.extraItems.indexOf(plasmoidId) < 0) { var extraItems = plasmoid.configuration.extraItems; extraItems.push(plasmoidId); plasmoid.configuration.extraItems = extraItems; } } } - //Main Layout - Flow { - id: tasksRow - spacing: 0 - height: parent.height - (vertical && expander.visible ? expander.height : 0) - width: parent.width - (vertical || !expander.visible ? 0 : expander.width) - property string skipItems - flow: vertical ? Flow.LeftToRight : Flow.TopToBottom - //To make it look centered - y: Math.round(height/2 - childrenRect.height/2) - x: (expander.visible && LayoutMirroring.enabled ? expander.width : 0) + Math.round(width/2 - childrenRect.width/2) + // Main layout + GridLayout { + id: mainLayout + rowSpacing: 0 + columnSpacing: 0 + anchors.fill: parent - //Do spacing with margins, to correctly compute the number of lines - property QtObject marginHints: QtObject { - property int left: Math.round(units.smallSpacing / 2) - property int top: Math.round(units.smallSpacing / 2) - property int right: Math.round(units.smallSpacing / 2) - property int bottom: Math.round(units.smallSpacing / 2) - } + flow: vertical ? GridLayout.TopToBottom : GridLayout.LeftToRight + + GridLayout { + id: tasksLayout + + rowSpacing: 0 + columnSpacing: 0 + Layout.fillWidth: true + Layout.fillHeight: true - //add doesn't seem to work used in conjunction with stackBefore/stackAfter - /*add: Transition { - NumberAnimation { - property: "scale" - from: 0 - to: 1 - easing.type: Easing.InQuad - duration: units.longDuration + flow: vertical ? GridLayout.TopToBottom : GridLayout.LeftToRight + rows: vertical ? Math.round(children.length / columns) + : Math.max(1, Math.floor(root.height / (itemSize + marginHints.top + marginHints.bottom))) + columns: !vertical ? Math.round(children.length / rows) + : Math.max(1, Math.floor(root.width / (itemSize + marginHints.left + marginHints.right))) + + // Do spacing with margins, to correctly compute the number of lines + property QtObject marginHints: QtObject { + property int left: Math.round(units.smallSpacing / 2) + property int top: Math.round(units.smallSpacing / 2) + property int right: Math.round(units.smallSpacing / 2) + property int bottom: Math.round(units.smallSpacing / 2) } } - move: Transition { - NumberAnimation { - properties: "x,y" - easing.type: Easing.InQuad - duration: units.longDuration - } - }*/ - } - ExpanderArrow { - id: expander - anchors { - fill: parent - leftMargin: vertical ? 0 : parent.width - implicitWidth - topMargin: vertical ? parent.height - implicitHeight : 0 + ExpanderArrow { + id: expander + Layout.fillWidth: vertical + Layout.fillHeight: !vertical } } - //Main popup + // Main popup PlasmaCore.Dialog { id: dialog visualParent: root flags: Qt.WindowStaysOnTopHint location: plasmoid.location hideOnWindowDeactivate: !plasmoid.configuration.pin onVisibleChanged: { if (!visible) { plasmoid.status = PlasmaCore.Types.PassiveStatus; if (root.activeApplet) { root.activeApplet.expanded = false; } } else { plasmoid.status = PlasmaCore.Types.RequiresAttentionStatus; } plasmoid.expanded = visible; } mainItem: ExpandedRepresentation { id: expandedRepresentation Keys.onEscapePressed: { root.expanded = false; } activeApplet: root.activeApplet LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft LayoutMirroring.childrenInherit: true } } }