Warning, /plasma/milou/lib/qml/ResultsView.qml is written in an unsupported language. File is not indexed.

0001 /*
0002  * This file is part of the KDE Milou Project
0003  * SPDX-FileCopyrightText: 2013-2014 Vishesh Handa <me@vhanda.in>
0004  *
0005  * SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0006  *
0007  */
0008 
0009 import QtQuick 2.1
0010 
0011 import org.kde.plasma.components 2.0 as PlasmaComponents // for Highlight
0012 import org.kde.plasma.core 2.0 as PlasmaCore
0013 import org.kde.milou 0.3 as Milou
0014 
0015 ListView {
0016     id: listView
0017     property alias queryString: resultModel.queryString
0018     property alias runner: resultModel.runner
0019     property alias runnerManager: resultModel.runnerManager
0020 
0021     property alias runnerName: resultModel.runnerName
0022     property alias runnerIcon: resultModel.runnerIcon
0023     property alias querying: resultModel.querying
0024     property alias limit: resultModel.limit
0025     property bool reversed
0026     signal activated
0027     signal updateQueryString(string text, int cursorPosition)
0028 
0029     // NOTE this also flips increment/decrementCurrentIndex (Bug 360789)
0030     verticalLayoutDirection: reversed ? ListView.BottomToTop : ListView.TopToBottom
0031     keyNavigationWraps: true
0032     highlight: PlasmaComponents.Highlight {}
0033     highlightMoveDuration: 0
0034     activeFocusOnTab: true
0035     Accessible.role: Accessible.List
0036 
0037     section {
0038         criteria: ViewSection.FullString
0039         property: "category"
0040     }
0041 
0042     // This is used to keep track if the user has pressed enter before
0043     // the first result has been shown, in the case the first result should
0044     // be run when the model is populated
0045     property bool runAutomatically
0046 
0047     // This is used to disable mouse selection if the user interacts only with keyboard
0048     property bool moved: false
0049     property point savedMousePosition: Milou.MouseHelper.globalMousePosition()
0050     function mouseMovedGlobally() {
0051         return savedMousePosition != Milou.MouseHelper.globalMousePosition();
0052     }
0053 
0054     Milou.DragHelper {
0055         id: dragHelper
0056         dragIconSize: units.iconSizes.medium
0057     }
0058 
0059     model: Milou.ResultsModel {
0060         id: resultModel
0061         limit: 15
0062         onQueryStringChangeRequested: {
0063             listView.updateQueryString(queryString, pos)
0064         }
0065         Component.onCompleted: {
0066             if (typeof runnerWindow !== "undefined") {
0067                 runnerWindow.runnerManager = runnerManager
0068             }
0069         }
0070         onQueryStringChanged: () => {
0071             resetView();
0072             // Do not run the results automatically, if the query changed since we pressed enter
0073             // BUG: 459859
0074             runAutomatically = false;
0075         }
0076         onModelReset: resetView()
0077 
0078         onRowsInserted: {
0079             // Keep the selection at the top as items inserted to the beginning will shift it downwards
0080             // ListView will update its view after this signal is processed and then our callLater will set it back
0081             if (listView.currentIndex === 0) {
0082                 Qt.callLater(function() {
0083                     listView.currentIndex = 0;
0084                 });
0085             }
0086 
0087             if (runAutomatically) {
0088                 // This needs to be delayed as running a result may close the window and clear the query
0089                 // having us reset the model whilst in the middle of processing the insertion.
0090                 // The proxy model chain that comes after us really doesn't like this.
0091                 Qt.callLater(function() {
0092                     resultModel.run(resultModel.index(0, 0));
0093                     listView.activated();
0094                 });
0095 
0096                 runAutomatically = false;
0097             }
0098         }
0099 
0100         function resetView() {
0101             listView.currentIndex = 0;
0102             listView.moved = false;
0103             listView.savedMousePosition = Milou.MouseHelper.globalMousePosition();
0104         }
0105     }
0106 
0107     delegate: ResultDelegate {
0108         id: resultDelegate
0109         width: listView.width
0110         reversed: listView.reversed
0111     }
0112 
0113     //
0114     // vHanda: Ideally this should have gotten handled in the delagte's onReturnPressed
0115     // code, but the ListView doesn't seem forward keyboard events to the delgate when
0116     // it is not in activeFocus. Even manually adding Keys.forwardTo: resultDelegate
0117     // doesn't make any difference!
0118     Keys.onReturnPressed: runCurrentIndex(event);
0119     Keys.onEnterPressed: runCurrentIndex(event);
0120 
0121     function runCurrentIndex(event) {
0122         if (!currentItem) {
0123             runAutomatically = true
0124             return;
0125         } else {
0126             // If user presses Shift+Return to invoke an action, invoke the first runner action
0127             if (event && event.modifiers === Qt.ShiftModifier
0128                     && currentItem.additionalActions && currentItem.additionalActions.length > 0) {
0129                 runAction(0)
0130                 return
0131             }
0132 
0133             if (currentItem.activeAction > -1) {
0134                 runAction(currentItem.activeAction)
0135                 return
0136             }
0137 
0138             if (resultModel.run(resultModel.index(currentIndex, 0))) {
0139                 activated()
0140             }
0141             runAutomatically = false
0142         }
0143     }
0144 
0145     function runAction(index) {
0146         if (resultModel.runAction(resultModel.index(currentIndex, 0), index)) {
0147             activated()
0148         }
0149     }
0150 
0151     onActiveFocusChanged: {
0152         if (!activeFocus && currentIndex == listView.count-1) {
0153             currentIndex = 0;
0154         }
0155     }
0156 
0157     Keys.onTabPressed: {
0158         if (!currentItem || !currentItem.activateNextAction()) {
0159             if (reversed) {
0160                 if (currentIndex == 0) {
0161                     listView.nextItemInFocusChain(false).forceActiveFocus();
0162                     return;
0163                 }
0164                 decrementCurrentIndex()
0165             } else {
0166                 if (currentIndex == listView.count-1) {
0167                     listView.nextItemInFocusChain(true).forceActiveFocus();
0168                     return;
0169                 }
0170                 incrementCurrentIndex()
0171             }
0172         }
0173     }
0174     Keys.onBacktabPressed: {
0175         if (!currentItem || !currentItem.activatePreviousAction()) {
0176             if (reversed) {
0177                 if (currentIndex == listView.count-1) {
0178                     listView.nextItemInFocusChain(true).forceActiveFocus();
0179                     return;
0180                 }
0181                 incrementCurrentIndex()
0182             } else {
0183                 if (currentIndex == 0) {
0184                     listView.nextItemInFocusChain(false).forceActiveFocus();
0185                     return;
0186                 }
0187                 decrementCurrentIndex()
0188             }
0189 
0190             // activate previous action cannot know whether we want to back tab from an action
0191             // to the main result or back tab from another search result, so we explicitly highlight
0192             // the last action here to provide a consistent navigation experience
0193             if (currentItem) {
0194                 currentItem.activateLastAction()
0195             }
0196         }
0197     }
0198     Keys.onUpPressed: reversed ? incrementCurrentIndex() : decrementCurrentIndex();
0199     Keys.onDownPressed: reversed ? decrementCurrentIndex() : incrementCurrentIndex();
0200 
0201     boundsBehavior: Flickable.StopAtBounds
0202 
0203     function loadSettings() {
0204         resultModel.loadSettings()
0205     }
0206 
0207     function setQueryString(queryString) {
0208         resultModel.queryString = queryString
0209         runAutomatically = false
0210     }
0211 }