Warning, /plasma/plasma-workspace/applets/systemtray/package/contents/ui/main.qml is written in an unsupported language. File is not indexed.

0001 /*
0002     SPDX-FileCopyrightText: 2011 Marco Martin <mart@kde.org>
0003     SPDX-FileCopyrightText: 2020 Konrad Materka <materka@gmail.com>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 import QtQuick 2.5
0009 import QtQuick.Layouts 1.1
0010 import QtQuick.Window 2.15
0011 import org.kde.plasma.core as PlasmaCore
0012 import org.kde.ksvg 1.0 as KSvg
0013 import org.kde.plasma.plasmoid 2.0
0014 import org.kde.draganddrop 2.0 as DnD
0015 import org.kde.kirigami 2.5 as Kirigami // For Settings.tabletMode
0016 import org.kde.kitemmodels 1.0 as KItemModels
0017 
0018 import "items"
0019 
0020 ContainmentItem {
0021     id: root
0022 
0023     readonly property bool vertical: Plasmoid.formFactor === PlasmaCore.Types.Vertical
0024 
0025     Layout.minimumWidth: vertical ? Kirigami.Units.iconSizes.small : mainLayout.implicitWidth + Kirigami.Units.smallSpacing
0026     Layout.minimumHeight: vertical ? mainLayout.implicitHeight + Kirigami.Units.smallSpacing : Kirigami.Units.iconSizes.small
0027 
0028     LayoutMirroring.enabled: !vertical && Qt.application.layoutDirection === Qt.RightToLeft
0029     LayoutMirroring.childrenInherit: true
0030 
0031     readonly property alias systemTrayState: systemTrayState
0032     readonly property alias itemSize: tasksGrid.itemSize
0033     readonly property alias visibleLayout: tasksGrid
0034     readonly property alias hiddenLayout: expandedRepresentation.hiddenLayout
0035     readonly property bool oneRowOrColumn: tasksGrid.rowsOrColumns === 1
0036 
0037     MouseArea {
0038         anchors.fill: parent
0039 
0040         onWheel: {
0041             // Don't propagate unhandled wheel events
0042             wheel.accepted = true;
0043         }
0044 
0045         SystemTrayState {
0046             id: systemTrayState
0047         }
0048 
0049         //being there forces the items to fully load, and they will be reparented in the popup one by one, this item is *never* visible
0050         Item {
0051             id: preloadedStorage
0052             visible: false
0053         }
0054 
0055         CurrentItemHighLight {
0056             location: Plasmoid.location
0057             parent: root
0058         }
0059 
0060         DnD.DropArea {
0061             anchors.fill: parent
0062 
0063             preventStealing: true
0064 
0065             /** Extracts the name of the system tray applet in the drag data if present
0066             * otherwise returns null*/
0067             function systemTrayAppletName(event) {
0068                 if (event.mimeData.formats.indexOf("text/x-plasmoidservicename") < 0) {
0069                     return null;
0070                 }
0071                 const plasmoidId = event.mimeData.getDataAsByteArray("text/x-plasmoidservicename");
0072 
0073                 if (!Plasmoid.isSystemTrayApplet(plasmoidId)) {
0074                     return null;
0075                 }
0076                 return plasmoidId;
0077             }
0078 
0079             onDragEnter: {
0080                 if (!systemTrayAppletName(event)) {
0081                     event.ignore();
0082                 }
0083             }
0084 
0085             onDrop: {
0086                 const plasmoidId = systemTrayAppletName(event);
0087                 if (!plasmoidId) {
0088                     event.ignore();
0089                     return;
0090                 }
0091 
0092                 if (Plasmoid.configuration.extraItems.indexOf(plasmoidId) < 0) {
0093                     const extraItems = Plasmoid.configuration.extraItems;
0094                     extraItems.push(plasmoidId);
0095                     Plasmoid.configuration.extraItems = extraItems;
0096                 }
0097             }
0098         }
0099 
0100 
0101         //Main Layout
0102         GridLayout {
0103             id: mainLayout
0104 
0105             rowSpacing: 0
0106             columnSpacing: 0
0107             anchors.fill: parent
0108 
0109             flow: vertical ? GridLayout.TopToBottom : GridLayout.LeftToRight
0110 
0111             GridView {
0112                 id: tasksGrid
0113 
0114                 Layout.alignment: Qt.AlignCenter
0115 
0116                 interactive: false //disable features we don't need
0117                 flow: vertical ? GridView.LeftToRight : GridView.TopToBottom
0118 
0119                 // The icon size to display when not using the auto-scaling setting
0120                 readonly property int smallIconSize: Kirigami.Units.iconSizes.smallMedium
0121 
0122                 // Automatically use autoSize setting when in tablet mode, if it's
0123                 // not already being used
0124                 readonly property bool autoSize: Plasmoid.configuration.scaleIconsToFit || Kirigami.Settings.tabletMode
0125 
0126                 readonly property int gridThickness: root.vertical ? root.width : root.height
0127                 // Should change to 2 rows/columns on a 56px panel (in standard DPI)
0128                 readonly property int rowsOrColumns: autoSize ? 1 : Math.max(1, Math.min(count, Math.floor(gridThickness / (smallIconSize + Kirigami.Units.smallSpacing))))
0129 
0130                 // Add margins only if the panel is larger than a small icon (to avoid large gaps between tiny icons)
0131                 readonly property int cellSpacing: Kirigami.Units.smallSpacing * (Kirigami.Settings.tabletMode ? 6 : Plasmoid.configuration.iconSpacing)
0132                 readonly property int smallSizeCellLength: gridThickness < smallIconSize ? smallIconSize : smallIconSize + cellSpacing
0133 
0134                 cellHeight: {
0135                     if (root.vertical) {
0136                         return autoSize ? itemSize + (gridThickness < itemSize ? 0 : cellSpacing) : smallSizeCellLength
0137                     } else {
0138                         return autoSize ? root.height : Math.floor(root.height / rowsOrColumns)
0139                     }
0140                 }
0141                 cellWidth: {
0142                     if (root.vertical) {
0143                         return autoSize ? root.width : Math.floor(root.width / rowsOrColumns)
0144                     } else {
0145                         return autoSize ? itemSize + (gridThickness < itemSize ? 0 : cellSpacing) : smallSizeCellLength
0146                     }
0147                 }
0148 
0149                 //depending on the form factor, we are calculating only one dimension, second is always the same as root/parent
0150                 implicitHeight: root.vertical ? cellHeight * Math.ceil(count / rowsOrColumns) : root.height
0151                 implicitWidth: !root.vertical ? cellWidth * Math.ceil(count / rowsOrColumns) : root.width
0152 
0153                 readonly property int itemSize: {
0154                     if (autoSize) {
0155                         return Kirigami.Units.iconSizes.roundedIconSize(Math.min(Math.min(root.width, root.height) / rowsOrColumns, Kirigami.Units.iconSizes.enormous))
0156                     } else {
0157                         return smallIconSize
0158                     }
0159                 }
0160 
0161                 model: KItemModels.KSortFilterProxyModel {
0162                     sourceModel: Plasmoid.systemTrayModel
0163                     filterRoleName: "effectiveStatus"
0164                     filterRowCallback: (sourceRow, sourceParent) => {
0165                         let value = sourceModel.data(sourceModel.index(sourceRow, 0, sourceParent), filterRole);
0166                         return value === PlasmaCore.Types.ActiveStatus;
0167                     }
0168                 }
0169 
0170                 delegate: ItemLoader {
0171                     id: delegate
0172 
0173                     width: tasksGrid.cellWidth
0174                     height: tasksGrid.cellHeight
0175                     minLabelHeight: 0
0176 
0177                     // We need to recalculate the stacking order of the z values due to how keyboard navigation works
0178                     // the tab order depends exclusively from this, so we redo it as the position in the list
0179                     // ensuring tab navigation focuses the expected items
0180                     Component.onCompleted: {
0181                         let item = tasksGrid.itemAtIndex(index - 1);
0182                         if (item) {
0183                             Plasmoid.stackItemBefore(delegate, item)
0184                         } else {
0185                             item = tasksGrid.itemAtIndex(index + 1);
0186                         }
0187                         if (item) {
0188                             Plasmoid.stackItemAfter(delegate, item)
0189                         }
0190                     }
0191                 }
0192 
0193                 add: Transition {
0194                     enabled: itemSize > 0 && Component.status == Component.Ready
0195 
0196                     NumberAnimation {
0197                         property: "scale"
0198                         from: 0
0199                         to: 1
0200                         easing.type: Easing.InOutQuad
0201                         duration: Kirigami.Units.longDuration
0202                     }
0203                 }
0204 
0205                 displaced: Transition {
0206                     //ensure scale value returns to 1.0
0207                     //https://doc.qt.io/qt-5/qml-qtquick-viewtransition.html#handling-interrupted-animations
0208                     enabled: Component.status == Component.Ready
0209                     NumberAnimation {
0210                         property: "scale"
0211                         to: 1
0212                         easing.type: Easing.InOutQuad
0213                         duration: Kirigami.Units.longDuration
0214                     }
0215                 }
0216 
0217                 move: Transition {
0218                     NumberAnimation {
0219                         properties: "x,y"
0220                         easing.type: Easing.InOutQuad
0221                         duration: Kirigami.Units.longDuration
0222                     }
0223                 }
0224             }
0225 
0226             ExpanderArrow {
0227                 id: expander
0228                 Layout.fillWidth: vertical
0229                 Layout.fillHeight: !vertical
0230                 Layout.alignment: vertical ? Qt.AlignVCenter : Qt.AlignHCenter
0231                 iconSize: tasksGrid.itemSize
0232                 visible: root.hiddenLayout.itemCount > 0
0233             }
0234         }
0235 
0236         Timer {
0237             id: expandedSync
0238             interval: 100
0239             onTriggered: systemTrayState.expanded = dialog.visible;
0240         }
0241 
0242         //Main popup
0243         PlasmaCore.AppletPopup {
0244             id: dialog
0245             objectName: "popupWindow"
0246             visualParent: root
0247             popupDirection: switch (Plasmoid.location) {
0248                 case PlasmaCore.Types.TopEdge:
0249                     return Qt.BottomEdge
0250                 case PlasmaCore.Types.LeftEdge:
0251                     return Qt.RightEdge
0252                 case PlasmaCore.Types.RightEdge:
0253                     return Qt.LeftEdge
0254                 default:
0255                     return Qt.TopEdge
0256             }
0257             margin: (Plasmoid.containmentDisplayHints & PlasmaCore.Types.ContainmentPrefersFloatingApplets) ? Kirigami.Units.largeSpacing : 0
0258 
0259             floating: Plasmoid.location == PlasmaCore.Desktop
0260 
0261             removeBorderStrategy: Plasmoid.location === PlasmaCore.Types.Floating
0262             ? PlasmaCore.AppletPopup.AtScreenEdges
0263             : PlasmaCore.AppletPopup.AtScreenEdges | PlasmaCore.AppletPopup.AtPanelEdges
0264 
0265 
0266             hideOnWindowDeactivate: !Plasmoid.configuration.pin
0267             visible: systemTrayState.expanded
0268             appletInterface: root
0269 
0270             backgroundHints: (Plasmoid.containmentDisplayHints & PlasmaCore.Types.ContainmentPrefersOpaqueBackground) ? PlasmaCore.AppletPopup.SolidBackground : PlasmaCore.AppletPopup.StandardBackground
0271 
0272             onVisibleChanged: {
0273                 if (!visible) {
0274                     expandedSync.restart();
0275                 } else {
0276                     if (expandedRepresentation.plasmoidContainer.visible) {
0277                         expandedRepresentation.plasmoidContainer.forceActiveFocus();
0278                     } else if (expandedRepresentation.hiddenLayout.visible) {
0279                         expandedRepresentation.hiddenLayout.forceActiveFocus();
0280                     }
0281                 }
0282             }
0283             mainItem: ExpandedRepresentation {
0284                 id: expandedRepresentation
0285 
0286                 Keys.onEscapePressed: {
0287                     systemTrayState.expanded = false
0288                 }
0289 
0290                 // Draws a line between the applet dialog and the panel
0291                 KSvg.SvgItem {
0292                     id: separator
0293                     // Only draw for popups of panel applets, not desktop applets
0294                     visible: [PlasmaCore.Types.TopEdge, PlasmaCore.Types.LeftEdge, PlasmaCore.Types.RightEdge, PlasmaCore.Types.BottomEdge]
0295                         .includes(Plasmoid.location) && !dialog.margin
0296                     anchors {
0297                         topMargin: -dialog.topMargin
0298                         leftMargin: -dialog.leftMargin
0299                         rightMargin: -dialog.rightMargin
0300                         bottomMargin: -dialog.bottomMargin
0301                     }
0302                     z: 999 /* Draw the line on top of the applet */
0303                     elementId: (Plasmoid.location === PlasmaCore.Types.TopEdge || Plasmoid.location === PlasmaCore.Types.BottomEdge) ? "horizontal-line" : "vertical-line"
0304                     imagePath: "widgets/line"
0305                     // Use states instead of bindings to work around https://bugreports.qt.io/browse/QTBUG-120464
0306                     states: [
0307                         State {
0308                             when: Plasmoid.location === PlasmaCore.Types.TopEdge
0309                             AnchorChanges {
0310                                 target: separator
0311                                 anchors {
0312                                     top: separator.parent.top
0313                                     left: separator.parent.left
0314                                     right: separator.parent.right
0315                                 }
0316                             }
0317                             PropertyChanges {
0318                                 target: separator
0319                                 height: 1
0320                             }
0321                         },
0322                         State {
0323                             when: Plasmoid.location === PlasmaCore.Types.LeftEdge
0324                             AnchorChanges {
0325                                 target: separator
0326                                 anchors {
0327                                     left: separator.parent.left
0328                                     top: separator.parent.top
0329                                     bottom: separator.parent.bottom
0330                                 }
0331                             }
0332                             PropertyChanges {
0333                                 target: separator
0334                                 width: 1
0335                             }
0336                         },
0337                         State {
0338                             when: Plasmoid.location === PlasmaCore.Types.RightEdge
0339                             AnchorChanges {
0340                                 target: separator
0341                                 anchors {
0342                                     top: separator.parent.top
0343                                     right: separator.parent.right
0344                                     bottom: separator.parent.bottom
0345                                 }
0346                             }
0347                             PropertyChanges {
0348                                 target: separator
0349                                 width: 1
0350                             }
0351                         },
0352                         State {
0353                             when: Plasmoid.location === PlasmaCore.Types.BottomEdge
0354                             AnchorChanges {
0355                                 target: separator
0356                                 anchors {
0357                                     left: separator.parent.left
0358                                     right: separator.parent.right
0359                                     bottom: separator.parent.bottom
0360                                 }
0361                             }
0362                             PropertyChanges {
0363                                 target: separator
0364                                 height: 1
0365                             }
0366                         }
0367                     ]
0368                 }
0369 
0370                 LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft
0371                 LayoutMirroring.childrenInherit: true
0372             }
0373         }
0374     }
0375 }