You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
365 lines
14 KiB
365 lines
14 KiB
/* |
|
* Copyright 2014 Marco Martin <mart@kde.org> |
|
* |
|
* This program is free software; you can redistribute it and/or |
|
* modify it under the terms of the GNU General Public License as |
|
* published by the Free Software Foundation; either version 2 of |
|
* the License, or (at your option) any later version. |
|
* |
|
* This program is distributed in the hope that it will be useful, |
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
* GNU General Public License for more details. |
|
* |
|
* You should have received a copy of the GNU General Public License |
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
*/ |
|
|
|
import QtQuick 2.6 |
|
import QtQuick.Layouts 1.1 |
|
import QtQuick.Window 2.1 |
|
import org.kde.plasma.core 2.0 as PlasmaCore |
|
import org.kde.plasma.components 2.0 as PlasmaComponents // For Highlight |
|
import org.kde.plasma.components 3.0 as PlasmaComponents3 |
|
import org.kde.plasma.extras 2.0 as PlasmaExtras |
|
import org.kde.milou 0.1 as Milou |
|
|
|
ColumnLayout { |
|
id: root |
|
property string query |
|
property string runner |
|
property bool showHistory: false |
|
property string priorSearch |
|
Layout.minimumHeight: 682 |
|
Layout.maximumHeight: 682 |
|
|
|
LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft |
|
LayoutMirroring.childrenInherit: true |
|
|
|
onQueryChanged: { |
|
queryField.text = query; |
|
} |
|
|
|
Connections { |
|
target: runnerWindow |
|
onVisibleChanged: { |
|
if (runnerWindow.visible) { |
|
queryField.forceActiveFocus(); |
|
listView.currentIndex = -1 |
|
if (runnerWindow.retainPriorSearch) { |
|
// If we manually specified a query(D-Bus invocation) we don't want to retain the prior search |
|
if (!query) { |
|
queryField.text = priorSearch |
|
queryField.select(root.query.length, 0) |
|
} |
|
} |
|
} else { |
|
if (runnerWindow.retainPriorSearch) { |
|
priorSearch = root.query |
|
} |
|
root.runner = "" |
|
root.query = "" |
|
root.showHistory = false |
|
} |
|
} |
|
} |
|
|
|
RowLayout { |
|
Layout.alignment: Qt.AlignTop |
|
PlasmaComponents3.ToolButton { |
|
icon.name: "configure" |
|
visible: false |
|
onClicked: { |
|
runnerWindow.visible = false |
|
runnerWindow.displayConfiguration() |
|
} |
|
Accessible.name: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Configure") |
|
Accessible.description: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Configure Search Plugins") |
|
PlasmaComponents3.ToolTip { |
|
text: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Configure KRunner...") |
|
} |
|
} |
|
PlasmaComponents3.TextField { |
|
id: queryField |
|
property bool allowCompletion: false |
|
|
|
clearButtonShown: true |
|
Layout.minimumWidth: 818 |
|
Layout.maximumWidth: 818 |
|
|
|
activeFocusOnPress: true |
|
placeholderText: results.runnerName ? i18ndc("plasma_lookandfeel_org.kde.lookandfeel", |
|
"Textfield placeholder text, query specific KRunner", |
|
"Search '%1'...", results.runnerName) |
|
: i18ndc("plasma_lookandfeel_org.kde.lookandfeel", |
|
"Textfield placeholder text", "Search...") |
|
|
|
PlasmaComponents3.BusyIndicator { |
|
anchors { |
|
right: parent.right |
|
top: parent.top |
|
bottom: parent.bottom |
|
margins: units.smallSpacing |
|
rightMargin: height |
|
} |
|
|
|
Timer { |
|
id: queryTimer |
|
property bool queryDisplay: false |
|
running: results.querying |
|
repeat: true |
|
onRunningChanged: if (queryDisplay && !running) { |
|
queryDisplay = false |
|
} |
|
onTriggered: if (!queryDisplay) { |
|
queryDisplay = true |
|
} |
|
interval: 500 |
|
} |
|
|
|
running: queryTimer.queryDisplay |
|
} |
|
function move_up() { |
|
if (length === 0) { |
|
root.showHistory = true; |
|
listView.forceActiveFocus(); |
|
} else if (results.count > 0) { |
|
results.forceActiveFocus(); |
|
results.decrementCurrentIndex(); |
|
} |
|
} |
|
|
|
function move_down() { |
|
if (length === 0) { |
|
root.showHistory = true; |
|
listView.forceActiveFocus(); |
|
} else if (results.count > 0) { |
|
results.forceActiveFocus(); |
|
results.incrementCurrentIndex(); |
|
} |
|
} |
|
|
|
onTextChanged: { |
|
root.query = queryField.text |
|
if (allowCompletion && length > 0) { |
|
var history = runnerWindow.history |
|
|
|
// search the first item in the history rather than the shortest matching one |
|
// this way more recently used entries take precedence over older ones (Bug 358985) |
|
for (var i = 0, j = history.length; i < j; ++i) { |
|
var item = history[i] |
|
|
|
if (item.toLowerCase().indexOf(text.toLowerCase()) === 0) { |
|
var oldText = text |
|
text = text + item.substr(oldText.length) |
|
select(text.length, oldText.length) |
|
break |
|
} |
|
} |
|
} |
|
} |
|
Keys.onPressed: { |
|
allowCompletion = (event.key !== Qt.Key_Backspace && event.key !== Qt.Key_Delete) |
|
|
|
if (event.modifiers & Qt.ControlModifier) { |
|
if (event.key === Qt.Key_J) { |
|
move_down() |
|
event.accepted = true; |
|
} else if (event.key === Qt.Key_K) { |
|
move_up() |
|
event.accepted = true; |
|
} |
|
} |
|
} |
|
Keys.onUpPressed: move_up() |
|
Keys.onDownPressed: move_down() |
|
Keys.onEnterPressed: results.runCurrentIndex(event) |
|
Keys.onReturnPressed: results.runCurrentIndex(event) |
|
|
|
Keys.onEscapePressed: { |
|
runnerWindow.visible = false |
|
} |
|
|
|
PlasmaCore.SvgItem { |
|
anchors { |
|
right: parent.right |
|
rightMargin: 6 // from PlasmaStyle TextFieldStyle |
|
verticalCenter: parent.verticalCenter |
|
} |
|
// match clear button |
|
width: Math.max(parent.height * 0.8, units.iconSizes.small) |
|
height: width |
|
svg: PlasmaCore.Svg { |
|
imagePath: "widgets/arrows" |
|
colorGroup: PlasmaCore.Theme.ButtonColorGroup |
|
} |
|
elementId: "down-arrow" |
|
visible: queryField.length === 0 && runnerWindow.history.length > 0 |
|
|
|
MouseArea { |
|
anchors.fill: parent |
|
onPressed: { |
|
root.showHistory = !root.showHistory |
|
if (root.showHistory) { |
|
listView.forceActiveFocus(); // is the history list |
|
} else { |
|
queryField.forceActiveFocus(); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
PlasmaComponents3.ToolButton { |
|
icon.name: "window-close" |
|
visible: false |
|
onClicked: { |
|
runnerWindow.visible = false |
|
} |
|
Accessible.name: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Close") |
|
Accessible.description: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Close Search") |
|
PlasmaComponents3.ToolTip { |
|
text: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Close") |
|
} |
|
} |
|
} |
|
|
|
PlasmaExtras.ScrollArea { |
|
Layout.alignment: Qt.AlignTop |
|
visible: !(historyScrollArea.visible || resultsScrollArea.visible) |
|
enabled: false |
|
Layout.fillWidth: true |
|
Layout.preferredHeight: Screen.height/2; //Math.min(Screen.height, results.contentHeight) |
|
} |
|
|
|
PlasmaExtras.ScrollArea { |
|
id: historyScrollArea |
|
Layout.alignment: Qt.AlignTop |
|
visible: results.count > 0 |
|
enabled: visible |
|
Layout.fillWidth: true |
|
Layout.preferredHeight: Screen.height/2; //Math.min(Screen.height, results.contentHeight) |
|
|
|
Milou.ResultsView { |
|
id: results |
|
queryString: root.query |
|
runner: root.runner |
|
|
|
Keys.onPressed: { |
|
var ctrl = event.modifiers & Qt.ControlModifier; |
|
if (ctrl && event.key === Qt.Key_J) { |
|
incrementCurrentIndex() |
|
} else if (ctrl && event.key === Qt.Key_K) { |
|
decrementCurrentIndex() |
|
} else if (event.text !== "") { |
|
// This prevents unprintable control characters from being inserted |
|
if (!/[\x00-\x1F\x7F]/.test(event.text)) { |
|
queryField.text += event.text; |
|
} |
|
queryField.focus = true; |
|
} |
|
} |
|
|
|
onActivated: { |
|
runnerWindow.addToHistory(queryString) |
|
runnerWindow.visible = false |
|
} |
|
|
|
onUpdateQueryString: { |
|
queryField.text = text |
|
queryField.cursorPosition = cursorPosition |
|
} |
|
} |
|
} |
|
|
|
PlasmaExtras.ScrollArea { |
|
id: resultsScrollArea |
|
Layout.alignment: Qt.AlignTop |
|
Layout.fillWidth: true |
|
visible: root.query.length === 0 && listView.count > 0 |
|
// don't accept keyboard input when not visible so the keys propagate to the other list |
|
enabled: visible |
|
Layout.preferredHeight: Screen.height/2;//Math.min(Screen.height, listView.contentHeight) |
|
|
|
ListView { |
|
id: listView // needs this id so the delegate can access it |
|
keyNavigationWraps: true |
|
highlight: PlasmaComponents.Highlight {} |
|
highlightMoveDuration: 0 |
|
activeFocusOnTab: true |
|
// we store 50 entries in the history but only show 20 in the UI so it doesn't get too huge |
|
model: root.showHistory ? runnerWindow.history.slice(0, 20) : [] |
|
delegate: Milou.ResultDelegate { |
|
id: resultDelegate |
|
width: listView.width |
|
typeText: index === 0 ? i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Recent Queries") : "" |
|
additionalActions: [{ |
|
icon: "list-remove", |
|
text: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Remove") |
|
}] |
|
Accessible.description: i18n("in category recent queries") |
|
} |
|
|
|
onActiveFocusChanged: { |
|
if (!activeFocus && currentIndex == listView.count-1) { |
|
currentIndex = 0; |
|
} |
|
} |
|
Keys.onReturnPressed: runCurrentIndex(event) |
|
Keys.onEnterPressed: runCurrentIndex(event) |
|
|
|
Keys.onTabPressed: { |
|
if (currentIndex == listView.count-1) { |
|
listView.nextItemInFocusChain(true).forceActiveFocus(); |
|
} else { |
|
incrementCurrentIndex() |
|
} |
|
} |
|
Keys.onBacktabPressed: { |
|
if (currentIndex == 0) { |
|
listView.nextItemInFocusChain(false).forceActiveFocus(); |
|
} else { |
|
decrementCurrentIndex() |
|
} |
|
} |
|
Keys.onPressed: { |
|
var ctrl = event.modifiers & Qt.ControlModifier; |
|
if (ctrl && event.key === Qt.Key_J) { |
|
incrementCurrentIndex() |
|
} else if (ctrl && event.key === Qt.Key_K) { |
|
decrementCurrentIndex() |
|
} else if (event.text !== "") { |
|
queryField.text += event.text; |
|
queryField.focus = true; |
|
} |
|
} |
|
|
|
Keys.onUpPressed: decrementCurrentIndex() |
|
Keys.onDownPressed: incrementCurrentIndex() |
|
|
|
function runCurrentIndex(event) { |
|
var entry = runnerWindow.history[currentIndex] |
|
if (entry) { |
|
// If user presses Shift+Return to invoke an action, invoke the first runner action |
|
if (event && event.modifiers === Qt.ShiftModifier |
|
&& currentItem.additionalActions && currentItem.additionalActions.length > 0) { |
|
runAction(0); |
|
return |
|
} |
|
|
|
queryField.text = entry |
|
queryField.forceActiveFocus(); |
|
} |
|
} |
|
|
|
function runAction(actionIndex) { |
|
if (actionIndex === 0) { |
|
// QStringList changes just reset the model, so we'll remember the index and set it again |
|
var currentIndex = listView.currentIndex |
|
runnerWindow.removeFromHistory(currentIndex) |
|
listView.currentIndex = currentIndex |
|
} |
|
} |
|
} |
|
|
|
} |
|
}
|
|
|