diff --git a/applets/systemtray/package/contents/applet/CompactApplet.qml b/applets/systemtray/package/contents/applet/CompactApplet.qml index b8da65d0f..6975639cd 100644 --- a/applets/systemtray/package/contents/applet/CompactApplet.qml +++ b/applets/systemtray/package/contents/applet/CompactApplet.qml @@ -1,82 +1,82 @@ /* * Copyright 2011 Marco Martin * * 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) { + location: if (plasmoid.parent && plasmoid.parent.inHiddenLayout && 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 c2175dcbc..33a7f325d 100644 --- a/applets/systemtray/package/contents/ui/ConfigEntries.qml +++ b/applets/systemtray/package/contents/ui/ConfigEntries.qml @@ -1,280 +1,280 @@ /* * Copyright 2013 Sebastian Kügler * Copyright 2014 Marco Martin * Copyright 2019 Konrad Materka * * 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 2.5 as QQC2 import QtQuick.Layouts 1.3 import org.kde.plasma.core 2.1 as PlasmaCore import org.kde.kquickcontrols 2.0 as KQC import org.kde.kirigami 2.10 as Kirigami ColumnLayout { id: iconsPage signal configurationChanged property var cfg_shownItems: [] property var cfg_hiddenItems: [] property var cfg_extraItems: [] property alias cfg_showAllItems: showAllCheckBox.checked QQC2.CheckBox { id: showAllCheckBox text: i18n("Always show all entries") } function categoryName(category) { switch (category) { case "ApplicationStatus": return i18n("Application Status") case "Communications": return i18n("Communications") case "SystemServices": return i18n("System Services") case "Hardware": return i18n("Hardware Control") case "UnknownCategory": default: return i18n("Miscellaneous") } } QQC2.ScrollView { id: scrollView Layout.fillWidth: true Layout.fillHeight: true contentHeight: itemsList.implicitHeight Component.onCompleted: scrollView.background.visible = true property bool scrollBarVisible: QQC2.ScrollBar.vertical && QQC2.ScrollBar.vertical.visible property var scrollBarWidth: scrollBarVisible ? QQC2.ScrollBar.vertical.width : 0 ListView { id: itemsList property var visibilityColumnWidth: units.gridUnit property var keySequenceColumnWidth: units.gridUnit model: PlasmaCore.SortFilterModel { sourceModel: PlasmaCore.SortFilterModel { sourceModel: plasmoid.nativeInterface.systemTrayModel sortRole: "display" sortColumn: 0 isSortLocaleAware: true } sortRole: "category" sortColumn: 0 isSortLocaleAware: true } header: Kirigami.AbstractListItem { hoverEnabled: false RowLayout { Kirigami.Heading { text: i18nc("Name of the system tray entry", "Entry") level: 2 Layout.fillWidth: true } Kirigami.Heading { text: i18n("Visibility") level: 2 Layout.preferredWidth: itemsList.visibilityColumnWidth Component.onCompleted: itemsList.visibilityColumnWidth = Math.max(implicitWidth, itemsList.visibilityColumnWidth) } Kirigami.Heading { text: i18n("Keyboard Shortcut") level: 2 Layout.preferredWidth: itemsList.keySequenceColumnWidth Component.onCompleted: itemsList.keySequenceColumnWidth = Math.max(implicitWidth, itemsList.keySequenceColumnWidth) } } } section { property: "category" delegate: Kirigami.ListSectionHeader { label: categoryName(section) } } delegate: Kirigami.AbstractListItem { highlighted: false hoverEnabled: false property bool isPlasmoid: model.itemType === "Plasmoid" contentItem: RowLayout { RowLayout { Layout.fillWidth: true Kirigami.Icon { width: units.iconSizes.small height: width source: model.decoration } QQC2.Label { Layout.fillWidth: true text: model.display elide: Text.ElideRight wrapMode: Text.NoWrap } } QQC2.ComboBox { id: visibilityComboBox property var contentWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, implicitContentWidth + leftPadding + rightPadding) implicitWidth: Math.max(contentWidth, itemsList.visibilityColumnWidth) Component.onCompleted: itemsList.visibilityColumnWidth = Math.max(implicitWidth, itemsList.visibilityColumnWidth) enabled: (!showAllCheckBox.checked || isPlasmoid) && itemId textRole: "text" model: comboBoxModel() currentIndex: { var value if (cfg_shownItems.indexOf(itemId) !== -1) { value = "shown" } else if (cfg_hiddenItems.indexOf(itemId) !== -1) { value = "hidden" } else if (isPlasmoid && cfg_extraItems.indexOf(itemId) === -1) { value = "disabled" } else { value = "auto" } for (var i = 0; i < model.length; i++) { if (model[i].value === value) { return i } } return 0 } - property var currentValue: model[currentIndex].value + property var myCurrentValue: model[currentIndex].value onActivated: { var shownIndex = cfg_shownItems.indexOf(itemId) var hiddenIndex = cfg_hiddenItems.indexOf(itemId) var extraIndex = cfg_extraItems.indexOf(itemId) - switch (currentValue) { + switch (myCurrentValue) { case "auto": if (shownIndex > -1) { cfg_shownItems.splice(shownIndex, 1) } if (hiddenIndex > -1) { cfg_hiddenItems.splice(hiddenIndex, 1) } if (extraIndex === -1) { cfg_extraItems.push(itemId) } break case "shown": if (shownIndex === -1) { cfg_shownItems.push(itemId) } if (hiddenIndex > -1) { cfg_hiddenItems.splice(hiddenIndex, 1) } if (extraIndex === -1) { cfg_extraItems.push(itemId) } break case "hidden": if (shownIndex > -1) { cfg_shownItems.splice(shownIndex, 1) } if (hiddenIndex === -1) { cfg_hiddenItems.push(itemId) } if (extraIndex === -1) { cfg_extraItems.push(itemId) } break case "disabled": if (extraIndex > -1) { cfg_extraItems.splice(extraIndex, 1) } break } iconsPage.configurationChanged() } function comboBoxModel() { var autoElement = {"value": "auto", "text": i18n("Shown when relevant")} var shownElement = {"value": "shown", "text": i18n("Always shown")} var hiddenElement = {"value": "hidden", "text": i18n("Always hidden")} var disabledElement = {"value": "disabled", "text": i18n("Disabled")} if (showAllCheckBox.checked) { if (isPlasmoid) { return [autoElement, disabledElement] } else { return [shownElement] } } else { if (isPlasmoid) { return [autoElement, shownElement, hiddenElement, disabledElement] } else { return [autoElement, shownElement, hiddenElement] } } } } KQC.KeySequenceItem { id: keySequenceItem Layout.minimumWidth: itemsList.keySequenceColumnWidth Layout.preferredWidth: itemsList.keySequenceColumnWidth Component.onCompleted: itemsList.keySequenceColumnWidth = Math.max(implicitWidth, itemsList.keySequenceColumnWidth) visible: isPlasmoid - enabled: visibilityComboBox.currentValue !== "disabled" + enabled: visibilityComboBox.myCurrentValue !== "disabled" keySequence: model.applet ? model.applet.globalShortcut : "" onKeySequenceChanged: { if (model.applet && keySequence !== model.applet.globalShortcut) { model.applet.globalShortcut = keySequence itemsList.keySequenceColumnWidth = Math.max(implicitWidth, itemsList.keySequenceColumnWidth) } } } // Placeholder for when KeySequenceItem is not visible Item { Layout.minimumWidth: itemsList.keySequenceColumnWidth Layout.maximumWidth: itemsList.keySequenceColumnWidth visible: !keySequenceItem.visible } } } } } } diff --git a/applets/systemtray/package/contents/ui/ExpandedRepresentation.qml b/applets/systemtray/package/contents/ui/ExpandedRepresentation.qml index db9aab1b0..dd8eb3c52 100644 --- a/applets/systemtray/package/contents/ui/ExpandedRepresentation.qml +++ b/applets/systemtray/package/contents/ui/ExpandedRepresentation.qml @@ -1,143 +1,142 @@ /* * Copyright 2016 Marco Martin * * 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.12 import QtQuick.Layouts 1.12 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 3.0 as PlasmaComponents import org.kde.plasma.extras 2.0 as PlasmaExtras ColumnLayout { 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 Layout.maximumWidth: Layout.minimumWidth Layout.maximumHeight: Layout.minimumHeight spacing: 0 // avoid gap between title and content property alias activeApplet: container.activeApplet property alias hiddenLayout: hiddenItemsView.layout RowLayout { PlasmaExtras.Heading { id: heading Layout.fillWidth: true level: 1 Layout.leftMargin: { //Menu mode if (!activeApplet && hiddenItemsView.visible && !LayoutMirroring.enabled) { return units.smallSpacing; //applet open, sidebar } else if (activeApplet && hiddenItemsView.visible && !LayoutMirroring.enabled) { return hiddenItemsView.width + units.largeSpacing; //applet open, no sidebar } else { return 0; } } Layout.rightMargin: { //Menu mode if (!activeApplet && hiddenItemsView.visible && LayoutMirroring.enabled) { return units.smallSpacing; //applet open, sidebar } else if (activeApplet && hiddenItemsView.visible && LayoutMirroring.enabled) { return hiddenItemsView.width + 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 visible: !activeApplet Layout.fillWidth: true level: 1 text: i18n("Status and Notifications") } PlasmaComponents.ToolButton { id: pinButton - Layout.preferredHeight: Math.round(units.gridUnit * 1.25) - Layout.preferredWidth: Layout.preferredHeight + implicitHeight: Math.round(units.gridUnit * 1.25) + implicitWidth: implicitHeight checkable: true checked: plasmoid.configuration.pin onToggled: plasmoid.configuration.pin = checked icon.name: "window-pin" PlasmaComponents.ToolTip { text: i18n("Keep Open") } } } RowLayout { spacing: 0 // must be 0 so that the separator is as close to the indicator as possible HiddenItemsView { id: hiddenItemsView Layout.fillWidth: !activeApplet - Layout.preferredWidth: activeApplet ? iconColumnWidth : -1 Layout.fillHeight: true } PlasmaCore.SvgItem { visible: hiddenItemsView.visible && activeApplet Layout.fillHeight: true Layout.preferredWidth: lineSvg.elementSize("vertical-line").width elementId: "vertical-line" svg: PlasmaCore.Svg { id: lineSvg; imagePath: "widgets/line" } } PlasmoidPopupsContainer { id: container visible: activeApplet Layout.fillWidth: true Layout.fillHeight: true Layout.leftMargin: hiddenItemsView.visible && activeApplet && !LayoutMirroring.enabled ? units.largeSpacing : 0 Layout.rightMargin: hiddenItemsView.visible && activeApplet && LayoutMirroring.enabled ? units.largeSpacing : 0 } } } diff --git a/applets/systemtray/package/contents/ui/HiddenItemsView.qml b/applets/systemtray/package/contents/ui/HiddenItemsView.qml index ea1f386c5..66895d98e 100644 --- a/applets/systemtray/package/contents/ui/HiddenItemsView.qml +++ b/applets/systemtray/package/contents/ui/HiddenItemsView.qml @@ -1,73 +1,71 @@ /* * Copyright 2016 Marco Martin * * 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.activeApplet.parent && root.activeApplet.parent.inHiddenLayout) + implicitWidth: 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 Flickable { contentWidth: width contentHeight: hiddenTasksColumn.height MouseArea { width: parent.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 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 { id: hiddenTasksColumn spacing: units.smallSpacing width: parent.width property Item hoveredItem - property alias marginHints: highlight.marginHints - - objectName: "hiddenTasksColumn" + readonly property int iconItemHeight: root.hiddenItemSize + highlight.marginHints.top + highlight.marginHints.bottom } } } } diff --git a/applets/systemtray/package/contents/ui/PlasmoidPopupsContainer.qml b/applets/systemtray/package/contents/ui/PlasmoidPopupsContainer.qml index f00e178ec..4654de08e 100644 --- a/applets/systemtray/package/contents/ui/PlasmoidPopupsContainer.qml +++ b/applets/systemtray/package/contents/ui/PlasmoidPopupsContainer.qml @@ -1,104 +1,103 @@ /* * Copyright 2015 Marco Martin * * 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 //needed for units import org.kde.plasma.core 2.0 as PlasmaCore StackView { id: mainStack clip: true focus: true Layout.minimumWidth: units.gridUnit * 12 Layout.minimumHeight: units.gridUnit * 12 property Item activeApplet onActiveAppletChanged: { if (activeApplet != null) { //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) { 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/items/AbstractItem.qml b/applets/systemtray/package/contents/ui/items/AbstractItem.qml index 3542cd356..afad7a86f 100644 --- a/applets/systemtray/package/contents/ui/items/AbstractItem.qml +++ b/applets/systemtray/package/contents/ui/items/AbstractItem.qml @@ -1,167 +1,164 @@ /* * Copyright 2016 Marco Martin * * 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.2 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents PlasmaCore.ToolTipArea { id: abstractItem - height: effectiveItemSize + marginHints.top + marginHints.bottom - width: labelVisible ? parent.width : effectiveItemSize + marginHints.left + marginHints.right + height: inVisibleLayout ? visibleLayout.iconSize : hiddenLayout.iconItemHeight + width: inVisibleLayout ? visibleLayout.iconSize : hiddenLayout.width - 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 QtObject marginHints: parent.marginHints - property bool labelVisible: abstractItem.hidden && !root.activeApplet property Item iconItem - //PlasmaCore.Types.ItemStatus - property int status + property int /*PlasmaCore.Types.ItemStatus*/ status + readonly property bool inHiddenLayout: effectiveStatus === PlasmaCore.Types.PassiveStatus + readonly property bool inVisibleLayout: effectiveStatus === PlasmaCore.Types.ActiveStatus property QtObject model signal clicked(var mouse) signal pressed(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 readonly property int effectiveStatus: { if (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 + /* subclasses need to assign to this tooltip properties mainText: subText: - icon: + icon: */ location: { - if (abstractItem.parent && abstractItem.parent.objectName === "hiddenTasksColumn") { + if (inHiddenLayout) { 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 property int creationId // used for item order tie breaking onEffectiveStatusChanged: updateItemVisibility(abstractItem) onCategoryChanged: updateItemVisibility(abstractItem) onTextChanged: updateItemVisibility(abstractItem) Component.onCompleted: { creationId = root.creationIdCounter++ updateItemVisibility(abstractItem) } onContainsMouseChanged: { - if (hidden && containsMouse) { + if (inHiddenLayout && containsMouse) { root.hiddenLayout.hoveredItem = abstractItem } } //END CONNECTIONS PulseAnimation { targetItem: iconItem running: (abstractItem.status === PlasmaCore.Types.NeedsAttentionStatus || abstractItem.status === PlasmaCore.Types.RequiresAttentionStatus ) && units.longDuration > 0 } function activated() { activatedAnimation.start() } SequentialAnimation { id: activatedAnimation loops: 1 ScaleAnimator { target: iconItem from: 1 to: 0.5 duration: units.shortDuration easing.type: Easing.InQuad } ScaleAnimator { target: iconItem from: 0.5 to: 1 duration: units.shortDuration easing.type: Easing.OutQuad } } MouseArea { anchors.fill: abstractItem hoverEnabled: true drag.filterChildren: true acceptedButtons: Qt.LeftButton | Qt.MiddleButton | Qt.RightButton onClicked: abstractItem.clicked(mouse) onPressed: { abstractItem.hideToolTip() abstractItem.pressed(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 + opacity: visible ? 1 : 0 + visible: abstractItem.inHiddenLayout && !root.activeApplet 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 8919eb938..05086a9e4 100644 --- a/applets/systemtray/package/contents/ui/items/PlasmoidItem.qml +++ b/applets/systemtray/package/contents/ui/items/PlasmoidItem.qml @@ -1,90 +1,119 @@ /* * Copyright 2015 Marco Martin * * 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 AbstractItem { id: plasmoidContainer property Item applet iconItem: applet text: applet ? applet.title : "" 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 + Component.onDestruction: { + applet = null + } + onClicked: { if (applet && mouse.button === Qt.LeftButton) { applet.expanded = true; } } onPressed: { if (mouse.button === Qt.RightButton) { plasmoidContainer.contextMenu(mouse); } } onContextMenu: { if (applet) { - plasmoid.nativeInterface.showPlasmoidMenu(applet, 0, plasmoidContainer.hidden ? applet.height : 0); + plasmoid.nativeInterface.showPlasmoidMenu(applet, 0, plasmoidContainer.inHiddenLayout ? applet.height : 0); } } onHeightChanged: { if (applet) { applet.width = height } } + + //This is to make preloading effective, minimizes the scene changes + function preloadFullRepresentationItem(fullRepresentationItem) { + if (fullRepresentationItem && applet.fullRepresentationItem.parent === null) { + fullRepresentationItem.width = expandedRepresentation.width + fullRepresentationItem.width = expandedRepresentation.height + fullRepresentationItem.parent = preloadedStorage; + } + } + onAppletChanged: { + if (applet) { + applet.parent = plasmoidContainer + applet.anchors.left = plasmoidContainer.left + applet.anchors.top = plasmoidContainer.top + applet.anchors.bottom = plasmoidContainer.bottom + applet.width = plasmoidContainer.height + applet.visible = true + plasmoidContainer.visible = true + + preloadFullRepresentationItem(applet.fullRepresentationItem) + } if (!applet) { plasmoidContainer.destroy(); - print("applet destroyed") } } + Connections { target: applet onActivated: plasmoidContainer.activated() onExpandedChanged: { if (expanded) { var oldApplet = root.activeApplet; root.activeApplet = applet; - if (oldApplet) { + if (oldApplet && oldApplet !== applet) { oldApplet.expanded = false; } dialog.visible = true; plasmoidContainer.activated() } else if (root.activeApplet === applet) { - if (!applet.parent.hidden) { + if (!inHiddenLayout) { dialog.visible = false; } //if not expanded we don't have an active applet anymore root.activeApplet = null; } } + + onFullRepresentationItemChanged: { + preloadFullRepresentationItem(fullRepresentationItem) + } } } diff --git a/applets/systemtray/package/contents/ui/items/StatusNotifierItem.qml b/applets/systemtray/package/contents/ui/items/StatusNotifierItem.qml index edc4c8642..109d870aa 100644 --- a/applets/systemtray/package/contents/ui/items/StatusNotifierItem.qml +++ b/applets/systemtray/package/contents/ui/items/StatusNotifierItem.qml @@ -1,141 +1,140 @@ /* * Copyright 2016 Marco Martin * * 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 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 source: { if (taskIcon.status === PlasmaCore.Types.NeedsAttentionStatus) { if (AttentionIcon) { return AttentionIcon } if (AttentionIconName) { return AttentionIconName } } return 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: { + case Qt.LeftButton: var service = statusNotifierSource.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); } }); taskIcon.activated() break; - } case Qt.RightButton: openContextMenu(pos); break; case Qt.MiddleButton: var service = statusNotifierSource.serviceForSource(DataEngineSource); var operation = service.operationDescription("SecondaryActivate"); operation.x = pos.x; operation.y = pos.y; service.startOperationCall(operation); taskIcon.activated() break; } } function openContextMenu(pos) { var service = statusNotifierSource.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 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 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 ec58d7204..722960c22 100644 --- a/applets/systemtray/package/contents/ui/main.qml +++ b/applets/systemtray/package/contents/ui/main.qml @@ -1,399 +1,361 @@ /* * Copyright 2011 Marco Martin * * 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 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 int status: dialog.visible ? PlasmaCore.Types.RequiresAttentionStatus : PlasmaCore.Types.PassiveStatus property alias visibleLayout: tasksRow 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") property int creationIdCounter: 0 Plasmoid.onExpandedChanged: { if (!plasmoid.expanded) { dialog.visible = plasmoid.expanded; } } // temporary hack to fix known broken categories // should go away as soon as fixes are merged readonly property var categoryOverride: { "org.kde.discovernotifier": "SystemServices", "org.kde.plasma.networkmanagement": "Hardware", "org.kde.kdeconnect": "Hardware", "org.kde.plasma.keyboardindicator": "Hardware", "touchpad": "Hardware" } readonly property var categoryOrder: [ "UnknownCategory", "ApplicationStatus", "Communications", "SystemServices", "Hardware" ] function indexForItemCategory(item) { if (item.itemId == "org.kde.plasma.notifications") { return -1 } var i = categoryOrder.indexOf(categoryOverride[item.itemId] || item.category) return i == -1 ? categoryOrder.indexOf("UnknownCategory") : i } // return negative integer if a < b, 0 if a === b, and positive otherwise function compareItems(a, b) { var categoryDiff = indexForItemCategory(a) - indexForItemCategory(b) var textDiff = (categoryDiff != 0 ? categoryDiff : a.text.localeCompare(b.text)) return textDiff != 0 ? textDiff : b.creationId - a.creationId } function moveItemAt(item, container, index) { if (container.children.length == 0) { item.parent = container } else { if (index == container.children.length) { var other = container.children[index - 1] if (item != other) { plasmoid.nativeInterface.reorderItemAfter(item, other) } } else { var other = container.children[index] if (item != other) { plasmoid.nativeInterface.reorderItemBefore(item, other) } } } } function reorderItem(item, container) { var i = 0; while (i < container.children.length && compareItems(container.children[i], item) <= 0) { i++ } moveItemAt(item, container, i) } function updateItemVisibility(item) { switch (item.effectiveStatus) { case PlasmaCore.Types.HiddenStatus: if (item.parent != invisibleEntriesContainer) { item.parent = invisibleEntriesContainer; } break; case PlasmaCore.Types.ActiveStatus: reorderItem(item, visibleLayout) break; case PlasmaCore.Types.PassiveStatus: reorderItem(item, hiddenLayout) 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 - 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 } } PlasmaCore.SortFilterModel { id: statusNotifierModel sourceModel: PlasmaCore.DataModel { dataSource: statusNotifierSource } } //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 + target: root.activeApplet && root.activeApplet.parent && root.activeApplet.parent.inVisibleLayout ? root.activeApplet.parent : 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) - - //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) - } + readonly property var iconSize: root.itemSize + units.smallSpacing //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 } } 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 } } //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 } } }