Warning, /plasma/plasma-desktop/applets/kicker/package/contents/ui/MenuRepresentation.qml is written in an unsupported language. File is not indexed.

0001 /*
0002     SPDX-FileCopyrightText: 2013-2015 Eike Hein <hein@kde.org>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 import QtQuick 2.15
0008 import QtQuick.Layouts 1.15
0009 // Deliberately imported after QtQuick to avoid missing restoreMode property in Binding. Fix in Qt 6.
0010 import QtQml 2.15
0011 
0012 import org.kde.plasma.core as PlasmaCore
0013 import org.kde.ksvg 1.0 as KSvg
0014 import org.kde.plasma.extras 2.0 as PlasmaExtras
0015 import org.kde.kirigami 2.20 as Kirigami
0016 import org.kde.plasma.plasmoid 2.0
0017 
0018 FocusScope {
0019     id: root
0020 
0021     focus: true
0022 
0023     Layout.minimumWidth: (sideBar.width + (sideBar.width ? mainRow.spacing : 0)
0024         + Math.max(searchField.defaultWidth, runnerColumns.width))
0025     Layout.maximumWidth: Math.max(mainRow.width, Layout.minimumWidth); // mainRow.width is constrained by rootList.maximumWidth
0026 
0027     Layout.minimumHeight: Math.max(((rootModel.count - rootModel.separatorCount) * rootList.itemHeight)
0028         + (rootModel.separatorCount * rootList.separatorHeight)
0029         + searchField.height + (2 * Kirigami.Units.smallSpacing), sideBar.margins.top + sideBar.margins.bottom
0030         + favoriteApps.contentHeight + favoriteSystemActions.contentHeight + sidebarSeparator.height
0031         + (4 * Kirigami.Units.smallSpacing))
0032     Layout.maximumHeight: Layout.minimumHeight
0033 
0034     signal appendSearchText(string text)
0035 
0036     function reset() {
0037         kicker.hideOnWindowDeactivate = true;
0038 
0039         rootList.currentIndex = -1;
0040 
0041         searchField.text = "";
0042         searchField.focus = true;
0043     }
0044 
0045     Row {
0046         id: mainRow
0047 
0048         height: parent.height
0049 
0050         spacing: Kirigami.Units.smallSpacing
0051 
0052         LayoutMirroring.enabled: ((Plasmoid.location === PlasmaCore.Types.RightEdge)
0053             || (Qt.application.layoutDirection === Qt.RightToLeft && Plasmoid.location !== PlasmaCore.Types.LeftEdge))
0054 
0055         KSvg.FrameSvgItem {
0056             id: sideBar
0057 
0058             visible: width > 0
0059 
0060             width: (globalFavorites && systemFavorites
0061                 && (globalFavorites.count + systemFavorites.count)
0062                 ? Kirigami.Units.iconSizes.medium + margins.left + margins.right : 0)
0063             height: parent.height
0064 
0065             imagePath: "widgets/frame"
0066             prefix: "plain"
0067 
0068             SideBarSection {
0069                 id: favoriteApps
0070 
0071                 anchors.top: parent.top
0072                 anchors.topMargin: sideBar.margins.top
0073 
0074                 height: (sideBar.height - sideBar.margins.top - sideBar.margins.bottom
0075                     - favoriteSystemActions.height - sidebarSeparator.height - (4 * Kirigami.Units.smallSpacing))
0076 
0077                 model: globalFavorites
0078 
0079                 states: [ State {
0080                     name: "top"
0081                     when: (Plasmoid.location === PlasmaCore.Types.TopEdge)
0082 
0083                     AnchorChanges {
0084                         target: favoriteApps
0085                         anchors.top: undefined
0086                         anchors.bottom: parent.bottom
0087                     }
0088 
0089                     PropertyChanges {
0090                         target: favoriteApps
0091                         anchors.topMargin: undefined
0092                         anchors.bottomMargin: sideBar.margins.bottom
0093                     }
0094                 }]
0095 
0096                 Binding {
0097                     target: globalFavorites
0098                     property: "iconSize"
0099                     value: Kirigami.Units.iconSizes.medium
0100                     restoreMode: Binding.RestoreBinding
0101                 }
0102             }
0103 
0104             KSvg.SvgItem {
0105                 id: sidebarSeparator
0106 
0107                 anchors.bottom: favoriteSystemActions.top
0108                 anchors.bottomMargin: (2 * Kirigami.Units.smallSpacing)
0109                 anchors.horizontalCenter: parent.horizontalCenter
0110 
0111                 width: Kirigami.Units.iconSizes.medium
0112                 height: lineSvg.horLineHeight
0113 
0114                 visible: (favoriteApps.model && favoriteApps.model.count
0115                     && favoriteSystemActions.model && favoriteSystemActions.model.count)
0116 
0117                 svg: lineSvg
0118                 elementId: "horizontal-line"
0119 
0120                 states: [ State {
0121                     name: "top"
0122                     when: (Plasmoid.location === PlasmaCore.Types.TopEdge)
0123 
0124                     AnchorChanges {
0125                         target: sidebarSeparator
0126                         anchors.top: favoriteSystemActions.bottom
0127                         anchors.bottom: undefined
0128 
0129                     }
0130 
0131                     PropertyChanges {
0132                         target: sidebarSeparator
0133                         anchors.topMargin: (2 * Kirigami.Units.smallSpacing)
0134                         anchors.bottomMargin: undefined
0135                     }
0136                 }]
0137             }
0138 
0139             SideBarSection {
0140                 id: favoriteSystemActions
0141 
0142                 anchors.bottom: parent.bottom
0143                 anchors.bottomMargin: sideBar.margins.bottom
0144 
0145                 model: systemFavorites
0146 
0147                 states: [ State {
0148                     name: "top"
0149                     when: (Plasmoid.location === PlasmaCore.Types.TopEdge)
0150 
0151                     AnchorChanges {
0152                         target: favoriteSystemActions
0153                         anchors.top: parent.top
0154                         anchors.bottom: undefined
0155                     }
0156 
0157                     PropertyChanges {
0158                         target: favoriteSystemActions
0159                         anchors.topMargin: sideBar.margins.top
0160                         anchors.bottomMargin: undefined
0161                     }
0162                 }]
0163             }
0164         }
0165 
0166         ItemListView {
0167             id: rootList
0168 
0169             anchors.top: parent.top
0170 
0171             minimumWidth: root.Layout.minimumWidth - sideBar.width - mainRow.spacing
0172             height: ((rootModel.count - rootModel.separatorCount) * itemHeight) + (rootModel.separatorCount * separatorHeight)
0173 
0174             visible: searchField.text === ""
0175 
0176             iconsEnabled: Plasmoid.configuration.showIconsRootLevel
0177 
0178             model: rootModel
0179 
0180             onKeyNavigationAtListEnd: {
0181                 searchField.focus = true;
0182             }
0183 
0184             states: [ State {
0185                 name: "top"
0186                 when: (Plasmoid.location === PlasmaCore.Types.TopEdge)
0187 
0188                 AnchorChanges {
0189                     target: rootList
0190                     anchors.top: parent.top
0191                 }
0192             }]
0193 
0194             Component.onCompleted: {
0195                 rootList.exited.connect(root.reset);
0196             }
0197         }
0198 
0199         Row {
0200             id: runnerColumns
0201 
0202             height: parent.height
0203 
0204             signal focusChanged()
0205 
0206             visible: searchField.text !== "" && runnerModel.count > 0
0207 
0208             Repeater {
0209                 id: runnerColumnsRepeater
0210 
0211                 model: runnerModel
0212 
0213                 delegate: RunnerResultsList {
0214                     id: runnerMatches
0215                     visible: runnerModel.modelForRow(index).count > 0
0216 
0217                     onKeyNavigationAtListEnd: {
0218                         searchField.focus = true;
0219                     }
0220 
0221                     onContainsMouseChanged: {
0222                         if (containsMouse) {
0223                             runnerMatches.focus = true;
0224                         }
0225                     }
0226 
0227                     onFocusChanged: {
0228                         if (focus) {
0229                             runnerColumns.focusChanged();
0230                         }
0231                     }
0232 
0233                     function focusChanged() {
0234                         if (!runnerMatches.focus && runnerMatches.currentIndex != -1) {
0235                             runnerMatches.currentIndex = -1;
0236                         }
0237                     }
0238 
0239                     Keys.onPressed: event => {
0240                         var target = null;
0241 
0242                         if (event.key === Qt.Key_Right) {
0243                             var targets = new Array();
0244 
0245                             for (var i = index + 1; i < runnerModel.count; ++i) {
0246                                 targets[targets.length] = i;
0247                             }
0248 
0249                             for (var i = 0; i < index; ++i) {
0250                                 targets[targets.length] = i;
0251                             }
0252 
0253                             for (var i = 0; i < targets.length; ++i) {
0254                                 if (runnerModel.modelForRow(targets[i]).count) {
0255                                     target = runnerColumnsRepeater.itemAt(targets[i]);
0256                                     break;
0257                                 }
0258                             }
0259                         } else if (event.key === Qt.Key_Left) {
0260                             var targets = new Array();
0261 
0262                             for (var i = index - 1; i >= 0; --i) {
0263                                 targets[targets.length] = i;
0264                             }
0265 
0266                             for (var i = runnerModel.count - 1; i > index; --i) {
0267                                 targets[targets.length] = i;
0268                             }
0269 
0270                             for (var i = 0; i < targets.length; ++i) {
0271                                 if (runnerModel.modelForRow(targets[i]).count) {
0272                                     target = runnerColumnsRepeater.itemAt(targets[i]);
0273                                     break;
0274                                 }
0275                             }
0276                         }
0277 
0278                         if (target) {
0279                             event.accepted = true;
0280                             currentIndex = -1;
0281                             target.currentIndex = 0;
0282                             target.focus = true;
0283                         }
0284                     }
0285 
0286                     Component.onCompleted: {
0287                         runnerColumns.focusChanged.connect(focusChanged);
0288                     }
0289 
0290                     Component.onDestruction: {
0291                         runnerColumns.focusChanged.disconnect(focusChanged);
0292                     }
0293                 }
0294             }
0295         }
0296     }
0297 
0298     PlasmaExtras.SearchField {
0299         id: searchField
0300 
0301         anchors.bottom: mainRow.bottom
0302         anchors.left: parent.left
0303         anchors.leftMargin: sideBar.width + (sideBar.width ? mainRow.spacing : Kirigami.Units.smallSpacing)
0304 
0305         readonly property real defaultWidth: Kirigami.Units.gridUnit * 14
0306         width: (runnerColumnsRepeater.count !== 0 ? runnerColumnsRepeater.itemAt(0).width
0307                                                   : (rootList.visible ? rootList.width : defaultWidth))
0308 
0309         focus: !Kirigami.InputMethod.willShowOnActive
0310 
0311         onTextChanged: {
0312             runnerModel.query = text;
0313         }
0314 
0315         onFocusChanged: {
0316             if (focus) {
0317                 // FIXME: Cleanup arbitration between rootList/runnerCols here and in Keys.
0318                 if (rootList.visible) {
0319                     rootList.currentIndex = -1;
0320                 }
0321 
0322                 if (runnerColumns.visible) {
0323                     runnerColumnsRepeater.itemAt(0).currentIndex = -1;
0324                 }
0325             }
0326         }
0327 
0328         states: [ State {
0329             name: "top"
0330             when: Plasmoid.location === PlasmaCore.Types.TopEdge
0331 
0332             AnchorChanges {
0333                 target: searchField
0334                 anchors.top: undefined
0335                 anchors.bottom: mainRow.bottom
0336                 anchors.left: parent.left
0337                 anchors.right: undefined
0338             }
0339 
0340             PropertyChanges {
0341                 target: searchField
0342                 anchors.leftMargin: sideBar.width + mainRow.spacing + Kirigami.Units.smallSpacing
0343                 anchors.rightMargin: undefined
0344             }
0345         },
0346         State {
0347             name: "right"
0348             when: (Plasmoid.location === PlasmaCore.Types.RightEdge && Qt.application.layoutDirection === Qt.LeftToRight)
0349                 || (Plasmoid.location === PlasmaCore.Types.LeftEdge && Qt.application.layoutDirection === Qt.RightToLeft)
0350 
0351             AnchorChanges {
0352                 target: searchField
0353                 anchors.top: undefined
0354                 anchors.bottom: mainRow.bottom
0355                 anchors.left: undefined
0356                 anchors.right: parent.right
0357             }
0358 
0359             PropertyChanges {
0360                 target: searchField
0361                 anchors.leftMargin: undefined
0362                 anchors.rightMargin: sideBar.width + mainRow.spacing + Kirigami.Units.smallSpacing
0363             }
0364         }]
0365 
0366         Keys.onPressed: event => {
0367             if (event.key === Qt.Key_Up) {
0368                 if (rootList.visible) {
0369                     rootList.showChildDialogs = false;
0370                     rootList.currentIndex = rootList.model.count - 1;
0371                     rootList.forceActiveFocus();
0372                     rootList.showChildDialogs = true;
0373                 }
0374 
0375                 if (runnerColumns.visible) {
0376                     for (var i = 0; i < runnerModel.count; ++i) {
0377                         if (runnerModel.modelForRow(i).count) {
0378                             var targetList = runnerColumnsRepeater.itemAt(i);
0379                             targetList.currentIndex = runnerModel.modelForRow(i).count - 1;
0380                             targetList.forceActiveFocus();
0381                             break;
0382                         }
0383                     }
0384                 }
0385             } else if (event.key === Qt.Key_Down) {
0386                 if (rootList.visible) {
0387                     rootList.showChildDialogs = false;
0388                     rootList.currentIndex = Math.min(1, rootList.count);
0389                     rootList.forceActiveFocus();
0390                     rootList.showChildDialogs = true;
0391                 }
0392 
0393                 if (runnerColumns.visible) {
0394                     for (var i = 0; i < runnerModel.count; ++i) {
0395                         if (runnerModel.modelForRow(i).count) {
0396                             var targetList = runnerColumnsRepeater.itemAt(i);
0397                             targetList.currentIndex = Math.min(1, targetList.count);
0398                             targetList.forceActiveFocus();
0399                             break;
0400                         }
0401                     }
0402                 }
0403             } else if (event.key === Qt.Key_Left && cursorPosition === 0) {
0404                     for (var i = runnerModel.count; i >= 0; --i) {
0405                         if (runnerModel.modelForRow(i).count) {
0406                             var targetList = runnerColumnsRepeater.itemAt(i);
0407                             targetList.currentIndex = 0;
0408                             targetList.forceActiveFocus();
0409                             break;
0410                         }
0411                     }
0412             } else if (event.key === Qt.Key_Right && cursorPosition === length) {
0413                 for (var i = 1; i < runnerModel.count; ++i) {
0414                     if (runnerModel.modelForRow(i).count) {
0415                         var targetList = runnerColumnsRepeater.itemAt(i);
0416                         targetList.currentIndex = 0;
0417                         targetList.forceActiveFocus();
0418                         break;
0419                     }
0420                 }
0421             } else if (event.key === Qt.Key_Enter || event.key === Qt.Key_Return) {
0422                 if (runnerColumns.visible && runnerModel.modelForRow(0).count) {
0423                     runnerModel.modelForRow(0).trigger(0, "", null);
0424                     kicker.expanded = false;
0425                 }
0426             }
0427         }
0428 
0429         function appendText(newText) {
0430             focus = true;
0431             text = text + newText;
0432         }
0433     }
0434 
0435     Keys.onPressed: event => {
0436         if (event.key === Qt.Key_Escape) {
0437             kicker.expanded = false;
0438         }
0439     }
0440 
0441     Component.onCompleted: {
0442         appendSearchText.connect(searchField.appendText);
0443 
0444         kicker.reset.connect(reset);
0445         windowSystem.hidden.connect(reset);
0446 
0447         rootModel.refresh();
0448     }
0449 }