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

0001 /*
0002     SPDX-FileCopyrightText: 2013-2014 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.Controls
0009 
0010 import org.kde.kquickcontrolsaddons 2.0
0011 import org.kde.plasma.extras 2.0 as PlasmaExtras
0012 import org.kde.kirigami 2.20 as Kirigami
0013 
0014 FocusScope {
0015     id: itemList
0016 
0017     property real minimumWidth: Kirigami.Units.gridUnit * 14
0018     property real maximumWidth: minimumWidth * 2
0019 
0020     width: minimumWidth
0021     height: listView.contentHeight
0022 
0023     signal exited
0024     signal keyNavigationAtListEnd
0025     signal appendSearchText(string text)
0026 
0027     property Item focusParent: null
0028     property QtObject dialog: null
0029     property QtObject childDialog: null
0030     property bool iconsEnabled: false
0031     property int itemHeight: Math.ceil((Math.max(Kirigami.Units.iconSizes.sizeForLabels, Kirigami.Units.iconSizes.small)
0032         + Math.max(highlightItemSvg.margins.top + highlightItemSvg.margins.bottom,
0033         listItemSvg.margins.top + listItemSvg.margins.bottom)) / 2) * 2
0034     property int separatorHeight: model.sorted === true ? 0 : lineSvg.horLineHeight + (2 * Kirigami.Units.smallSpacing)
0035 
0036     property alias currentIndex: listView.currentIndex
0037     property alias currentItem: listView.currentItem
0038     property alias keyNavigationWraps: listView.keyNavigationWraps
0039     property alias showChildDialogs: listView.showChildDialogs
0040     property alias model: listView.model
0041     property alias count: listView.count
0042     property alias containsMouse: listener.containsMouse
0043     property alias resetOnExitDelay: resetIndexTimer.interval
0044 
0045     onFocusParentChanged: {
0046         appendSearchText.connect(focusParent.appendSearchText);
0047     }
0048 
0049     Timer {
0050         id: dialogSpawnTimer
0051 
0052         property bool focusOnSpawn: false
0053 
0054         interval: 70
0055         repeat: false
0056 
0057         onTriggered: {
0058             if (!kicker.expanded || model === undefined || currentIndex == -1) {
0059                 return;
0060             }
0061 
0062             if (itemList.childDialog != null) {
0063                 itemList.childDialog.delayedDestroy();
0064             }
0065 
0066             // Gets reenabled after the dialog spawn causes a focus-in on the dialog window.
0067             kicker.hideOnWindowDeactivate = false;
0068 
0069             itemList.childDialog = itemListDialogComponent.createObject(itemList);
0070             itemList.childDialog.focusParent = itemList;
0071             itemList.childDialog.visualParent = listView.currentItem;
0072             itemList.childDialog.model = model.modelForRow(listView.currentIndex);
0073             itemList.childDialog.visible = true;
0074 
0075             windowSystem.forceActive(itemList.childDialog.mainItem);
0076             itemList.childDialog.mainItem.focus = true;
0077 
0078             if (focusOnSpawn) {
0079                 itemList.childDialog.mainItem.showChildDialogs = false;
0080                 itemList.childDialog.mainItem.currentIndex = 0;
0081                 itemList.childDialog.mainItem.showChildDialogs = true;
0082             }
0083         }
0084     }
0085 
0086     Timer {
0087         id: resetIndexTimer
0088 
0089         interval: (dialog != null) ? 50 : 150
0090         repeat: false
0091 
0092         onTriggered: {
0093             if (focus && (!itemList.childDialog || !itemList.childDialog.mainItem.containsMouse)) {
0094                 currentIndex = -1;
0095                 itemList.exited();
0096             }
0097         }
0098     }
0099 
0100     MouseEventListener {
0101         id: listener
0102 
0103         anchors.fill: parent
0104 
0105         hoverEnabled: true
0106 
0107         onContainsMouseChanged: {
0108             listView.eligibleWidth = listView.width;
0109 
0110             if (containsMouse) {
0111                 resetIndexTimer.stop();
0112                 itemList.forceActiveFocus();
0113             } else if ((!itemList.childDialog || !dialog)
0114                 && (!currentItem || !currentItem.menu.opened)) {
0115                 resetIndexTimer.start();
0116             }
0117         }
0118 
0119         ScrollView {
0120             anchors.fill: parent
0121 
0122             focus: true
0123 
0124             ListView {
0125                 id: listView
0126 
0127                 property bool showChildDialogs: true
0128                 property int eligibleWidth: width
0129 
0130                 currentIndex: -1
0131 
0132                 boundsBehavior: Flickable.StopAtBounds
0133                 snapMode: ListView.SnapToItem
0134                 spacing: 0
0135                 keyNavigationWraps: (dialog != null)
0136 
0137                 delegate: ItemListDelegate {
0138                     onFullTextWidthChanged: {
0139                         if (fullTextWidth > itemList.width) itemList.width = Math.min(fullTextWidth, itemList.maximumWidth);
0140                     }
0141                 }
0142 
0143                 highlight: PlasmaExtras.Highlight {
0144                     visible: !listView.currentItem.isSeparator
0145                     pressed: listView.currentItem && listView.currentItem.pressed && !listView.currentItem.hasChildren
0146                     active:  listView.currentItem && listView.currentItem.hovered
0147                 }
0148 
0149                 highlightMoveDuration: 0
0150 
0151                 onCountChanged: {
0152                     currentIndex = -1;
0153                 }
0154 
0155                 onCurrentIndexChanged: {
0156                     if (currentIndex != -1) {
0157                         if (itemList.childDialog) {
0158                             if (currentItem && currentItem.hasChildren) {
0159                                 itemList.childDialog.mainItem.width = itemList.minimumWidth;
0160                                 itemList.childDialog.model = model.modelForRow(currentIndex);
0161                                 itemList.childDialog.visualParent = listView.currentItem;
0162                             } else {
0163                                 itemList.childDialog.delayedDestroy();
0164                             }
0165 
0166                             return;
0167                         }
0168 
0169                         if (currentItem == null || !currentItem.hasChildren || !kicker.expanded) {
0170                             dialogSpawnTimer.stop();
0171 
0172                             return;
0173                         }
0174 
0175                         if (showChildDialogs) {
0176                             dialogSpawnTimer.focusOnSpawn = false;
0177                             dialogSpawnTimer.restart();
0178                         }
0179                     } else if (itemList.childDialog != null) {
0180                         itemList.childDialog.delayedDestroy();
0181                         itemList.childDialog = null;
0182                     }
0183                 }
0184 
0185                 onCurrentItemChanged: {
0186                     if (currentItem) {
0187                         currentItem.menu.closed.connect(resetIndexTimer.restart);
0188                     }
0189                 }
0190             }
0191 
0192             Keys.onPressed: event => {
0193                 if (event.key === Qt.Key_Up) {
0194                     event.accepted = true;
0195 
0196                     if (!keyNavigationWraps && currentIndex == 0) {
0197                         itemList.keyNavigationAtListEnd();
0198 
0199                         return;
0200                     }
0201 
0202                     showChildDialogs = false;
0203                     listView.decrementCurrentIndex();
0204 
0205                     if (currentItem.isSeparator) {
0206                         listView.decrementCurrentIndex();
0207                     }
0208 
0209                     showChildDialogs = true;
0210                 } else if (event.key === Qt.Key_Down) {
0211                     event.accepted = true;
0212 
0213                     if (!keyNavigationWraps && currentIndex == count - 1) {
0214                         itemList.keyNavigationAtListEnd();
0215 
0216                         return;
0217                     }
0218 
0219                     showChildDialogs = false;
0220                     listView.incrementCurrentIndex();
0221 
0222                     if (currentItem.isSeparator) {
0223                         listView.incrementCurrentIndex();
0224                     }
0225 
0226                     showChildDialogs = true;
0227                 } else if ((event.key === Qt.Key_Right || event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && itemList.childDialog != null) {
0228                     windowSystem.forceActive(itemList.childDialog.mainItem);
0229                     itemList.childDialog.mainItem.focus = true;
0230                     itemList.childDialog.mainItem.currentIndex = 0;
0231                 } else if ((event.key === Qt.Key_Right || event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && itemList.childDialog == null
0232                     && currentItem != null && currentItem.hasChildren) {
0233                     dialogSpawnTimer.focusOnSpawn = true;
0234                     dialogSpawnTimer.restart();
0235                 } else if (event.key === Qt.Key_Left && dialog != null) {
0236                     dialog.destroy();
0237                 } else if (event.key === Qt.Key_Escape) {
0238                     kicker.expanded = false;
0239                 } else if (event.key === Qt.Key_Tab) {
0240                     //do nothing, and skip appending text
0241                 } else if (event.text !== "") {
0242                     if (/[\x00-\x1F\x7F]/.test(event.text)) {
0243                         // We still want to focus it
0244                         appendSearchText("");
0245                     } else {
0246                         appendSearchText(event.text);
0247                     }
0248                 }
0249             }
0250         }
0251     }
0252 
0253     Component.onCompleted: {
0254         windowSystem.monitorWindowFocus(itemList);
0255 
0256         if (dialog == null) {
0257             appendSearchText.connect(root.appendSearchText);
0258         }
0259     }
0260 }