Warning, /plasma/plasma-workspace/krunner/qml/RunCommand.qml is written in an unsupported language. File is not indexed.

0001 /*
0002     SPDX-FileCopyrightText: 2014 Marco Martin <mart@kde.org>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 import QtQuick 2.15
0008 import QtQuick.Layouts 1.1
0009 import QtQuick.Window 2.1
0010 
0011 import org.kde.kquickcontrolsaddons 2.0  // For KCMShell
0012 import org.kde.plasma.core 2.0 as PlasmaCore
0013 import org.kde.plasma.components 3.0 as PlasmaComponents3
0014 import org.kde.plasma.extras 2.0 as PlasmaExtras
0015 import org.kde.milou 0.1 as Milou
0016 
0017 ColumnLayout {
0018     id: root
0019     property string query
0020     property string runner
0021     property bool showHistory: false
0022     property alias runnerManager: results.runnerManager
0023 
0024     LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft
0025     LayoutMirroring.childrenInherit: true
0026 
0027     onQueryChanged: {
0028         queryField.text = query;
0029     }
0030 
0031     Connections {
0032         target: runnerWindow
0033         function onVisibleChanged() {
0034             if (runnerWindow.visible) {
0035                 queryField.forceActiveFocus();
0036                 listView.currentIndex = -1
0037                 if (runnerManager.retainPriorSearch) {
0038                     // If we manually specified a query(D-Bus invocation) we don't want to retain the prior search
0039                     if (!query) {
0040                         queryField.text = runnerManager.priorSearch
0041                         queryField.select(root.query.length, 0)
0042                     }
0043                 }
0044             } else {
0045                 if (runnerManager.retainPriorSearch) {
0046                     runnerManager.priorSearch = root.query
0047                 }
0048                 root.runner = ""
0049                 root.query = ""
0050                 root.showHistory = false
0051             }
0052         }
0053     }
0054 
0055     Connections {
0056         target: root
0057         function onShowHistoryChanged() {
0058             if (showHistory) {
0059                 // we store 50 entries in the history but only show 20 in the UI so it doesn't get too huge
0060                 listView.model = runnerManager.history.slice(0, 20)
0061             } else {
0062                 listView.model = []
0063             }
0064         }
0065     }
0066 
0067     RowLayout {
0068         Layout.alignment: Qt.AlignTop
0069         PlasmaComponents3.ToolButton {
0070             icon.name: "configure"
0071             onClicked: {
0072                 runnerWindow.visible = false
0073                 runnerWindow.displayConfiguration()
0074             }
0075             Accessible.name: i18n("Configure")
0076             Accessible.description: i18n("Configure KRunner Behavior")
0077             visible: KCMShell.authorize("kcm_krunnersettings.desktop").length > 0
0078             PlasmaComponents3.ToolTip {
0079                 text: i18n("Configure KRunner…")
0080             }
0081         }
0082         PlasmaExtras.SearchField {
0083             id: queryField
0084             property bool allowCompletion: false
0085 
0086             Layout.minimumWidth: PlasmaCore.Units.gridUnit * 25
0087             Layout.maximumWidth: PlasmaCore.Units.gridUnit * 25
0088 
0089             activeFocusOnPress: true
0090             placeholderText: results.runnerName ? i18nc("Textfield placeholder text, query specific KRunner plugin",
0091                                                          "Search '%1'…", results.runnerName)
0092                                                 : i18nc("Textfield placeholder text", "Search…")
0093 
0094             PlasmaComponents3.BusyIndicator {
0095                 anchors {
0096                     right: parent.right
0097                     top: parent.top
0098                     bottom: parent.bottom
0099                     margins: PlasmaCore.Units.smallSpacing
0100                     rightMargin: height
0101                 }
0102 
0103                 Timer {
0104                     id: queryTimer
0105                     property bool queryDisplay: false
0106                     running: results.querying
0107                     repeat: true
0108                     onRunningChanged: if (queryDisplay && !running) {
0109                         queryDisplay = false
0110                     }
0111                     onTriggered: if (!queryDisplay) {
0112                         queryDisplay = true
0113                     }
0114                     interval: 500
0115                 }
0116 
0117                 running: queryTimer.queryDisplay
0118             }
0119             function move_up() {
0120                 if (length === 0) {
0121                     root.showHistory = true;
0122                 } else if (results.count > 0) {
0123                     results.decrementCurrentIndex();
0124                 }
0125                 focusCurrentListView()
0126             }
0127 
0128             function move_down() {
0129                 if (length === 0) {
0130                     root.showHistory = true;
0131                 } else if (results.count > 0) {
0132                     results.incrementCurrentIndex();
0133                 }
0134                 focusCurrentListView()
0135             }
0136 
0137             function focusCurrentListView() {
0138                 if (listView.count > 0) {
0139                     listView.forceActiveFocus();
0140                 } else if (results.count > 0) {
0141                     results.forceActiveFocus();
0142                 }
0143             }
0144 
0145             onTextChanged: {
0146                 root.query = queryField.text
0147                 if (allowCompletion && length > 0 && runnerManager.historyEnabled) {
0148                     var oldText = text
0149                     var suggestedText = runnerManager.getHistorySuggestion(text);
0150                     if (suggestedText.length > 0) {
0151                         text = text + suggestedText.substr(oldText.length)
0152                         select(text.length, oldText.length)
0153                     }
0154                 }
0155             }
0156             Keys.onPressed: {
0157                 allowCompletion = (event.key !== Qt.Key_Backspace && event.key !== Qt.Key_Delete)
0158 
0159                 if (event.modifiers & Qt.ControlModifier) {
0160                     if (event.key === Qt.Key_J) {
0161                         move_down()
0162                         event.accepted = true;
0163                     } else if (event.key === Qt.Key_K) {
0164                         move_up()
0165                         event.accepted = true;
0166                     }
0167                 }
0168                 // We only need to handle the Key_End case, the first item is focused by default
0169                 if (event.key === Qt.Key_End && results.count > 0 && cursorPosition === text.length) {
0170                     results.currentIndex = results.count - 1
0171                     event.accepted = true;
0172                     focusCurrentListView()
0173                 }
0174             }
0175             Keys.onUpPressed: move_up()
0176             Keys.onDownPressed: move_down()
0177             function closeOrRun(event) {
0178                 // Close KRunner if no text was typed and enter was pressed, FEATURE: 211225
0179                 if (!root.query) {
0180                     runnerWindow.visible = false
0181                 } else {
0182                     results.runCurrentIndex(event)
0183                 }
0184             }
0185             Keys.onEnterPressed: closeOrRun(event)
0186             Keys.onReturnPressed: closeOrRun(event)
0187 
0188             Keys.onEscapePressed: {
0189                 runnerWindow.visible = false
0190             }
0191 
0192             PlasmaCore.SvgItem {
0193                 anchors {
0194                     right: parent.right
0195                     rightMargin: 6 // from PlasmaStyle TextFieldStyle
0196                     verticalCenter: parent.verticalCenter
0197                 }
0198                 // match clear button
0199                 width: Math.max(parent.height * 0.8, PlasmaCore.Units.iconSizes.small)
0200                 height: width
0201                 svg: PlasmaCore.Svg {
0202                     imagePath: "widgets/arrows"
0203                     colorGroup: PlasmaCore.Theme.ButtonColorGroup
0204                 }
0205                 elementId: "down-arrow"
0206                 visible: queryField.length === 0 && runnerManager.historyEnabled
0207 
0208                 MouseArea {
0209                     anchors.fill: parent
0210                     onPressed: {
0211                         root.showHistory = !root.showHistory
0212                         if (root.showHistory) {
0213                             listView.forceActiveFocus(); // is the history list
0214                         } else {
0215                             queryField.forceActiveFocus();
0216                         }
0217                     }
0218                 }
0219             }
0220         }
0221         PlasmaComponents3.ToolButton {
0222             visible: runnerWindow.helpEnabled
0223             checkable: true
0224             checked: root.query.startsWith("?")
0225             // Reset if out quers starts with "?", otherwise set it to "?"
0226             onClicked: root.query = root.query.startsWith("?") ? "" : "?"
0227             icon.name: "question"
0228             Accessible.name: i18n("Show Usage Help")
0229             Accessible.description: i18n("Show Usage Help")
0230             PlasmaComponents3.ToolTip {
0231                 text: i18n("Show Usage Help")
0232             }
0233         }
0234         PlasmaComponents3.ToolButton {
0235             checkable: true
0236             checked: runnerWindow.pinned
0237             onToggled: runnerWindow.pinned = checked
0238             icon.name: "window-pin"
0239             Accessible.name: i18n("Pin")
0240             Accessible.description: i18n("Pin Search")
0241             PlasmaComponents3.ToolTip {
0242                 text: i18n("Keep Open")
0243             }
0244         }
0245     }
0246 
0247     PlasmaComponents3.ScrollView {
0248         Layout.alignment: Qt.AlignTop
0249         visible: results.count > 0
0250         enabled: visible
0251         Layout.fillWidth: true
0252         Layout.preferredHeight: Math.min(Screen.height, results.contentHeight)
0253         // HACK: workaround for https://bugreports.qt.io/browse/QTBUG-83890
0254         PlasmaComponents3.ScrollBar.horizontal.policy: PlasmaComponents3.ScrollBar.AlwaysOff
0255 
0256         Milou.ResultsView {
0257             id: results
0258             queryString: root.query
0259             runner: root.runner
0260 
0261             Keys.onPressed: {
0262                 var ctrl = event.modifiers & Qt.ControlModifier;
0263                 if (ctrl && event.key === Qt.Key_J) {
0264                     incrementCurrentIndex()
0265                 } else if (ctrl && event.key === Qt.Key_K) {
0266                     decrementCurrentIndex()
0267                 } else if (event.key === Qt.Key_Home) {
0268                     results.currentIndex = 0
0269                 } else if (event.key === Qt.Key_End) {
0270                     results.currentIndex = results.count - 1
0271                 } else if (event.text !== "") {
0272                     // This prevents unprintable control characters from being inserted
0273                     if (!/[\x00-\x1F\x7F]/.test(event.text)) {
0274                         queryField.text += event.text;
0275                     }
0276                     queryField.cursorPosition = queryField.text.length
0277                     queryField.focus = true;
0278                 }
0279             }
0280 
0281             Keys.onEscapePressed: {
0282                 runnerWindow.visible = false
0283             }
0284 
0285             onActivated: {
0286                 if (!runnerWindow.pinned) {
0287                     runnerWindow.visible = false
0288                 }
0289             }
0290 
0291             onUpdateQueryString: {
0292                 queryField.text = text
0293                 queryField.select(cursorPosition, root.query.length)
0294                 queryField.focus = true;
0295             }
0296         }
0297     }
0298 
0299     PlasmaComponents3.ScrollView {
0300         Layout.alignment: Qt.AlignTop
0301         Layout.fillWidth: true
0302         visible: root.query.length === 0 && listView.count > 0
0303         // don't accept keyboard input when not visible so the keys propagate to the other list
0304         enabled: visible
0305         Layout.preferredHeight: Math.min(Screen.height, listView.contentHeight)
0306         // HACK: workaround for https://bugreports.qt.io/browse/QTBUG-83890
0307         PlasmaComponents3.ScrollBar.horizontal.policy: PlasmaComponents3.ScrollBar.AlwaysOff
0308 
0309         ListView {
0310             id: listView // needs this id so the delegate can access it
0311             keyNavigationWraps: true
0312             highlight: PlasmaExtras.Highlight {}
0313             highlightMoveDuration: 0
0314             activeFocusOnTab: true
0315             model: []
0316             reuseItems: true
0317             delegate: Milou.ResultDelegate {
0318                 id: resultDelegate
0319                 width: listView.width
0320                 typeText: index === 0 ? i18n("Recent Queries") : ""
0321                 additionalActions: [{
0322                     icon: "list-remove",
0323                     text: i18n("Remove")
0324                 }]
0325                 Accessible.description: i18n("Recent Queries")
0326             }
0327 
0328             onActiveFocusChanged: {
0329                 if (!activeFocus && currentIndex == listView.count-1) {
0330                     currentIndex = 0;
0331                 }
0332             }
0333             Keys.onReturnPressed: runCurrentIndex(event)
0334             Keys.onEnterPressed: runCurrentIndex(event)
0335             
0336             Keys.onTabPressed: {
0337                 if (currentIndex == listView.count-1) {
0338                     listView.nextItemInFocusChain(true).forceActiveFocus();
0339                 } else {
0340                     incrementCurrentIndex()
0341                 }
0342             }
0343             Keys.onBacktabPressed: {
0344                 if (currentIndex == 0) {
0345                     listView.nextItemInFocusChain(false).forceActiveFocus();
0346                 } else {
0347                     decrementCurrentIndex()
0348                 }
0349             }
0350             Keys.onPressed: {
0351                 var ctrl = event.modifiers & Qt.ControlModifier;
0352                 if (ctrl && event.key === Qt.Key_J) {
0353                     incrementCurrentIndex()
0354                 } else if (ctrl && event.key === Qt.Key_K) {
0355                     decrementCurrentIndex()
0356                 } else if (event.key === Qt.Key_Home) {
0357                     currentIndex = 0
0358                 } else if (event.key === Qt.Key_End) {
0359                     currentIndex = count - 1
0360                 } else if (event.text !== "") {
0361                     // This prevents unprintable control characters from being inserted
0362                     if (event.key == Qt.Key_Escape) {
0363                         root.showHistory = false
0364                     } else if (!/[\x00-\x1F\x7F]/.test(event.text)) {
0365                         queryField.text += event.text;
0366                     }
0367                     queryField.focus = true;
0368                 }
0369             }
0370   
0371             Keys.onUpPressed: decrementCurrentIndex()
0372             Keys.onDownPressed: incrementCurrentIndex()
0373 
0374             function runCurrentIndex(event) {
0375                 var entry = runnerManager.history[currentIndex]
0376                 if (entry) {
0377                     // If user presses Shift+Return to invoke an action, invoke the first runner action
0378                     if (event && event.modifiers === Qt.ShiftModifier
0379                             && currentItem.additionalActions && currentItem.additionalActions.length > 0) {
0380                         runAction(0);
0381                         return
0382                     }
0383 
0384                     queryField.text = entry
0385                     queryField.forceActiveFocus();
0386                 }
0387             }
0388 
0389             function runAction(actionIndex) {
0390                 if (actionIndex === 0) {
0391                     // QStringList changes just reset the model, so we'll remember the index and set it again
0392                     var currentIndex = listView.currentIndex
0393                     runnerManager.removeFromHistory(currentIndex)
0394                     model = runnerManager.history
0395                     listView.currentIndex = currentIndex
0396                 }
0397             }
0398         }
0399 
0400     }
0401 }