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.Controls 2.15 as QQC2 0009 import QtQuick.Layouts 1.1 0010 import QtQuick.Window 2.1 0011 0012 import org.kde.config // KAuthorized 0013 import org.kde.kcmutils // KCMLauncher 0014 import org.kde.plasma.components 3.0 as PlasmaComponents3 0015 import org.kde.plasma.extras 2.0 as PlasmaExtras 0016 import org.kde.milou 0.1 as Milou 0017 import org.kde.krunner.private.view 0018 import org.kde.kirigami 2.20 as Kirigami 0019 0020 ColumnLayout { 0021 id: root 0022 property string query 0023 property string singleRunner 0024 property bool showHistory: false 0025 property string priorSearch: "" 0026 property alias runnerManager: results.runnerManager 0027 0028 LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft 0029 LayoutMirroring.childrenInherit: true 0030 0031 // Spacing needs to be 0 to not make the last spacer item add a fake margin 0032 spacing: 0 0033 0034 Connections { 0035 target: runnerWindow 0036 function onHistoryBehaviorChanged() { 0037 runnerManager.historyEnabled = runnerWindow.historyBehavior !== HistoryBehavior.Disabled 0038 } 0039 function onFavoriteIdsChanged() { 0040 results.model.favoriteIds = runnerWindow.favoriteIds 0041 } 0042 function onActivityChanged(activity) { 0043 runnerManager.setHistoryEnvironmentIdentifier(activity) 0044 } 0045 } 0046 Component.onCompleted: { 0047 runnerManager.historyEnabled = runnerWindow.historyBehavior !== HistoryBehavior.Disabled 0048 results.model.favoriteIds = runnerWindow.favoriteIds 0049 } 0050 0051 onQueryChanged: { 0052 queryField.text = query; 0053 } 0054 0055 Connections { 0056 target: runnerWindow 0057 function onVisibleChanged() { 0058 if (runnerWindow.visible) { 0059 queryField.forceActiveFocus(); 0060 listView.currentIndex = -1 0061 if (runnerWindow.retainPriorSearch) { 0062 // If we manually specified a query(D-Bus invocation) we don't want to retain the prior search 0063 if (!query) { 0064 queryField.text = priorSearch 0065 queryField.select(root.query.length, 0) 0066 } 0067 } 0068 } else { 0069 if (runnerWindow.retainPriorSearch) { 0070 priorSearch = root.query 0071 } 0072 root.singleRunner = "" 0073 root.query = "" 0074 fadedTextCompletion.text = ""; 0075 root.showHistory = false 0076 } 0077 } 0078 } 0079 0080 Connections { 0081 target: root 0082 function onShowHistoryChanged() { 0083 if (showHistory) { 0084 // we store 50 entries in the history but only show 20 in the UI so it doesn't get too huge 0085 listView.model = runnerManager.history.slice(0, 20) 0086 } else { 0087 listView.model = [] 0088 } 0089 } 0090 } 0091 0092 RowLayout { 0093 Layout.alignment: Qt.AlignTop 0094 PlasmaComponents3.ToolButton { 0095 icon.name: "configure" 0096 onClicked: { 0097 runnerWindow.visible = false 0098 KCMLauncher.open("plasma/kcms/desktop/kcm_krunnersettings") 0099 } 0100 Accessible.name: i18n("Configure") 0101 Accessible.description: i18n("Configure KRunner Behavior") 0102 visible: KAuthorized.authorizeControlModule("kcm_krunnersettings") 0103 PlasmaComponents3.ToolTip { 0104 text: i18n("Configure KRunner…") 0105 } 0106 } 0107 PlasmaComponents3.ToolButton { 0108 visible: !!root.singleRunner 0109 icon.name: results.singleRunnerMetaData.iconName 0110 onClicked: () => { 0111 root.singleRunner = "" 0112 root.query = "" 0113 fadedTextCompletion.text = "" 0114 queryField.forceActiveFocus(); 0115 } 0116 checked: true 0117 Accessible.description: i18n("Showing only results from %1", results.singleRunnerMetaData.name) 0118 PlasmaComponents3.ToolTip { 0119 text: parent.Accessible.description 0120 } 0121 } 0122 PlasmaExtras.SearchField { 0123 id: queryField 0124 property bool allowCompletion: false 0125 0126 Layout.minimumWidth: Kirigami.Units.gridUnit * 25 0127 Layout.maximumWidth: Kirigami.Units.gridUnit * 25 0128 0129 activeFocusOnPress: true 0130 placeholderText: results.singleRunner ? i18nc("Textfield placeholder text, query specific KRunner plugin", 0131 "Search '%1'…", results.singleRunnerMetaData.name) 0132 : i18nc("Textfield placeholder text", "Search…") 0133 background.z: -2 // The fadedTextCompletion has -1, so it appears over the background 0134 0135 QQC2.Label { 0136 id: fadedTextCompletion 0137 leftPadding: parent.leftPadding 0138 topPadding: parent.topPadding 0139 rightPadding: parent.rightPadding 0140 bottomPadding: parent.bottomPadding 0141 width: parent.width 0142 height: parent.height 0143 elide: Text.ElideRight 0144 opacity: 0.5 0145 text: "" 0146 textFormat: Text.PlainText 0147 renderType: parent.renderType 0148 color: parent.color 0149 focus: false 0150 z: -1 0151 } 0152 0153 PlasmaComponents3.BusyIndicator { 0154 anchors { 0155 right: parent.right 0156 top: parent.top 0157 bottom: parent.bottom 0158 margins: Kirigami.Units.smallSpacing 0159 rightMargin: height 0160 } 0161 0162 Timer { 0163 id: queryTimer 0164 property bool queryDisplay: false 0165 running: results.querying 0166 repeat: true 0167 onRunningChanged: if (queryDisplay && !running) { 0168 queryDisplay = false 0169 } 0170 onTriggered: if (!queryDisplay) { 0171 queryDisplay = true 0172 } 0173 interval: 500 0174 } 0175 0176 running: queryTimer.queryDisplay 0177 } 0178 function move_up() { 0179 if (length === 0) { 0180 root.showHistory = true; 0181 } else if (results.count > 0) { 0182 results.decrementCurrentIndex(); 0183 } 0184 focusCurrentListView() 0185 } 0186 0187 function move_down() { 0188 if (length === 0) { 0189 root.showHistory = true; 0190 } else if (results.count > 0) { 0191 results.incrementCurrentIndex(); 0192 } 0193 focusCurrentListView() 0194 } 0195 0196 function focusCurrentListView() { 0197 if (listView.count > 0) { 0198 listView.forceActiveFocus(); 0199 } else if (results.count > 0) { 0200 results.forceActiveFocus(); 0201 } 0202 } 0203 0204 onTextChanged: { 0205 root.query = queryField.text 0206 if (!allowCompletion || !root.query ) { // Clear suggestion in case it was disabled or the query is cleared 0207 fadedTextCompletion.text = "" 0208 } else if (runnerWindow.historyBehavior === HistoryBehavior.CompletionSuggestion) { 0209 fadedTextCompletion.text = runnerManager.getHistorySuggestion(text) 0210 } else if (length > 0 && runnerWindow.historyBehavior === HistoryBehavior.ImmediateCompletion) { 0211 var oldText = text 0212 var suggestedText = runnerManager.getHistorySuggestion(text); 0213 if (suggestedText.length > 0) { 0214 text = text + suggestedText.substr(oldText.length) 0215 select(text.length, oldText.length) 0216 } 0217 } 0218 } 0219 Keys.onPressed: event => { 0220 allowCompletion = (event.key !== Qt.Key_Backspace && event.key !== Qt.Key_Delete) 0221 0222 if (event.modifiers & Qt.ControlModifier) { 0223 if (event.key === Qt.Key_J) { 0224 move_down() 0225 event.accepted = true; 0226 } else if (event.key === Qt.Key_K) { 0227 move_up() 0228 event.accepted = true; 0229 } 0230 } 0231 // We only need to handle the Key_End case, the first item is focused by default 0232 if (event.key === Qt.Key_End && results.count > 0 && cursorPosition === text.length) { 0233 results.currentIndex = results.count - 1 0234 event.accepted = true; 0235 focusCurrentListView() 0236 } 0237 if (runnerWindow.historyBehavior === HistoryBehavior.CompletionSuggestion 0238 && fadedTextCompletion.text.length > 0 0239 && cursorPosition === text.length 0240 && event.key === Qt.Key_Right 0241 ) { 0242 queryField.text = fadedTextCompletion.text 0243 fadedTextCompletion.text = "" 0244 } 0245 } 0246 Keys.onTabPressed: event => { 0247 if (runnerWindow.historyBehavior === HistoryBehavior.CompletionSuggestion) { 0248 if (fadedTextCompletion.text && queryField.text !== fadedTextCompletion.text) { 0249 queryField.text = fadedTextCompletion.text 0250 fadedTextCompletion.text = "" 0251 } else { 0252 event.accepted = false 0253 } 0254 } 0255 } 0256 Keys.onUpPressed: move_up() 0257 Keys.onDownPressed: move_down() 0258 function closeOrRun(event) { 0259 // Close KRunner if no text was typed and enter was pressed, FEATURE: 211225 0260 if (!root.query) { 0261 runnerWindow.visible = false 0262 } else { 0263 results.runCurrentIndex(event) 0264 } 0265 } 0266 Keys.onEnterPressed: event => closeOrRun(event) 0267 Keys.onReturnPressed: event => closeOrRun(event) 0268 0269 Keys.onEscapePressed: { 0270 runnerWindow.visible = false 0271 } 0272 0273 Kirigami.Icon { 0274 anchors { 0275 right: parent.right 0276 rightMargin: 6 // from PlasmaStyle TextFieldStyle 0277 verticalCenter: parent.verticalCenter 0278 } 0279 // match clear button 0280 width: Math.max(parent.height * 0.8, Kirigami.Units.iconSizes.small) 0281 height: width 0282 source: "expand" 0283 visible: queryField.length === 0 && runnerManager.historyEnabled 0284 0285 MouseArea { 0286 anchors.fill: parent 0287 onPressed: { 0288 root.showHistory = !root.showHistory 0289 if (root.showHistory) { 0290 listView.forceActiveFocus(); // is the history list 0291 } else { 0292 queryField.forceActiveFocus(); 0293 } 0294 } 0295 } 0296 } 0297 } 0298 0299 PlasmaComponents3.ToolButton { 0300 visible: runnerWindow.helpEnabled 0301 checkable: true 0302 checked: root.query.startsWith("?") 0303 // Reset if out quers starts with "?", otherwise set it to "?" 0304 onClicked: root.query = root.query.startsWith("?") ? "" : "?" 0305 icon.name: "question" 0306 Accessible.name: i18n("Show Usage Help") 0307 Accessible.description: i18n("Show Usage Help") 0308 PlasmaComponents3.ToolTip { 0309 text: i18n("Show Usage Help") 0310 } 0311 } 0312 PlasmaComponents3.ToolButton { 0313 checkable: true 0314 checked: runnerWindow.pinned 0315 onToggled: runnerWindow.pinned = checked 0316 icon.name: "window-pin" 0317 Accessible.name: i18n("Pin") 0318 Accessible.description: i18n("Pin Search") 0319 PlasmaComponents3.ToolTip { 0320 text: i18n("Keep Open") 0321 } 0322 } 0323 } 0324 0325 PlasmaComponents3.ScrollView { 0326 visible: results.count > 0 0327 enabled: visible 0328 Layout.fillWidth: true 0329 Layout.fillHeight: true 0330 Layout.maximumHeight: Math.max(listView.contentHeight, results.contentHeight) 0331 // This replaces the ColumnLayout spacing 0332 Layout.topMargin: Kirigami.Units.smallSpacing 0333 0334 Milou.ResultsView { 0335 id: results 0336 queryString: root.query 0337 singleRunner: root.singleRunner 0338 0339 Keys.onPressed: event => { 0340 var ctrl = event.modifiers & Qt.ControlModifier; 0341 if (ctrl && event.key === Qt.Key_J) { 0342 incrementCurrentIndex() 0343 } else if (ctrl && event.key === Qt.Key_K) { 0344 decrementCurrentIndex() 0345 } else if (event.key === Qt.Key_Home) { 0346 results.currentIndex = 0 0347 } else if (event.key === Qt.Key_End) { 0348 results.currentIndex = results.count - 1 0349 } else if (event.text !== "") { 0350 // This prevents unprintable control characters from being inserted 0351 if (!/[\x00-\x1F\x7F]/.test(event.text)) { 0352 queryField.text += event.text; 0353 } 0354 queryField.cursorPosition = queryField.text.length 0355 queryField.focus = true; 0356 } 0357 } 0358 0359 Keys.onEscapePressed: { 0360 runnerWindow.visible = false 0361 } 0362 0363 onActivated: { 0364 if (!runnerWindow.pinned) { 0365 runnerWindow.visible = false 0366 } 0367 } 0368 0369 onUpdateQueryString: { 0370 queryField.text = text 0371 queryField.select(cursorPosition, root.query.length) 0372 queryField.focus = true; 0373 } 0374 } 0375 } 0376 0377 PlasmaComponents3.ScrollView { 0378 Layout.fillWidth: true 0379 Layout.fillHeight: true 0380 Layout.maximumHeight: listView.contentHeight 0381 // This replaces the ColumnLayout spacing 0382 Layout.topMargin: Kirigami.Units.smallSpacing 0383 visible: root.query.length === 0 && listView.count > 0 0384 // don't accept keyboard input when not visible so the keys propagate to the other list 0385 enabled: visible 0386 0387 ListView { 0388 id: listView // needs this id so the delegate can access it 0389 keyNavigationWraps: true 0390 highlight: PlasmaExtras.Highlight {} 0391 highlightMoveDuration: 0 0392 activeFocusOnTab: true 0393 model: [] 0394 reuseItems: true 0395 delegate: Milou.ResultDelegate { 0396 id: resultDelegate 0397 width: listView.width 0398 typeText: index === 0 ? i18n("Recent Queries") : "" 0399 additionalActions: [{ 0400 icon: "list-remove", 0401 text: i18n("Remove") 0402 }] 0403 Accessible.description: i18n("Recent Queries") 0404 } 0405 0406 onActiveFocusChanged: { 0407 if (!activeFocus && currentIndex == listView.count-1) { 0408 currentIndex = 0; 0409 } 0410 } 0411 Keys.onReturnPressed: runCurrentIndex(event) 0412 Keys.onEnterPressed: runCurrentIndex(event) 0413 0414 Keys.onTabPressed: { 0415 if (currentIndex == listView.count-1) { 0416 listView.nextItemInFocusChain(true).forceActiveFocus(); 0417 } else { 0418 incrementCurrentIndex() 0419 } 0420 } 0421 Keys.onBacktabPressed: { 0422 if (currentIndex == 0) { 0423 listView.nextItemInFocusChain(false).forceActiveFocus(); 0424 } else { 0425 decrementCurrentIndex() 0426 } 0427 } 0428 Keys.onPressed: event => { 0429 var ctrl = event.modifiers & Qt.ControlModifier; 0430 if (ctrl && event.key === Qt.Key_J) { 0431 incrementCurrentIndex() 0432 } else if (ctrl && event.key === Qt.Key_K) { 0433 decrementCurrentIndex() 0434 } else if (event.key === Qt.Key_Home) { 0435 currentIndex = 0 0436 } else if (event.key === Qt.Key_End) { 0437 currentIndex = count - 1 0438 } else if (event.text !== "") { 0439 // This prevents unprintable control characters from being inserted 0440 if (event.key == Qt.Key_Escape) { 0441 root.showHistory = false 0442 } else if (!/[\x00-\x1F\x7F]/.test(event.text)) { 0443 queryField.text += event.text; 0444 } 0445 queryField.focus = true; 0446 } 0447 } 0448 0449 Keys.onUpPressed: decrementCurrentIndex() 0450 Keys.onDownPressed: incrementCurrentIndex() 0451 0452 function runCurrentIndex(event) { 0453 var entry = runnerManager.history[currentIndex] 0454 if (entry) { 0455 // If user presses Shift+Return to invoke an action, invoke the first runner action 0456 if (event && event.modifiers === Qt.ShiftModifier 0457 && currentItem.additionalActions && currentItem.additionalActions.length > 0) { 0458 runAction(0); 0459 return 0460 } 0461 0462 queryField.text = entry 0463 queryField.forceActiveFocus(); 0464 } 0465 } 0466 0467 function runAction(actionIndex) { 0468 if (actionIndex === 0) { 0469 // QStringList changes just reset the model, so we'll remember the index and set it again 0470 var currentIndex = listView.currentIndex 0471 runnerManager.removeFromHistory(currentIndex) 0472 model = runnerManager.history 0473 listView.currentIndex = currentIndex 0474 } 0475 } 0476 } 0477 0478 } 0479 0480 // to align the layout to the top of any pending space in the view 0481 Item { 0482 Layout.fillHeight: true 0483 } 0484 }