Warning, /plasma/plasma-desktop/applets/kickoff/package/contents/ui/FullRepresentation.qml is written in an unsupported language. File is not indexed.
0001 /* 0002 SPDX-FileCopyrightText: 2011 Martin Gräßlin <mgraesslin@kde.org> 0003 SPDX-FileCopyrightText: 2012 Gregor Taetzner <gregor@freenet.de> 0004 SPDX-FileCopyrightText: 2012 Marco Martin <mart@kde.org> 0005 SPDX-FileCopyrightText: 2013 2014 David Edmundson <davidedmundson@kde.org> 0006 SPDX-FileCopyrightText: 2014 Sebastian Kügler <sebas@kde.org> 0007 SPDX-FileCopyrightText: 2021 Mikel Johnson <mikel5764@gmail.com> 0008 SPDX-FileCopyrightText: 2021 Noah Davis <noahadvs@gmail.com> 0009 0010 SPDX-License-Identifier: GPL-2.0-or-later 0011 */ 0012 import QtQuick 2.15 0013 import QtQuick.Templates 2.15 as T 0014 import QtQuick.Layouts 1.15 0015 import QtQml 2.15 0016 import org.kde.plasma.plasmoid 2.0 0017 import org.kde.kirigami 2.20 as Kirigami 0018 import org.kde.plasma.extras 2.0 as PlasmaExtras 0019 0020 EmptyPage { 0021 id: root 0022 0023 // kickoff is Kickoff.qml 0024 leftPadding: -kickoff.backgroundMetrics.leftPadding 0025 rightPadding: -kickoff.backgroundMetrics.rightPadding 0026 topPadding: 0 0027 bottomPadding: -kickoff.backgroundMetrics.bottomPadding 0028 readonly property var appletInterface: kickoff 0029 0030 Layout.minimumWidth: implicitWidth 0031 Layout.maximumWidth: Kirigami.Units.gridUnit * 80 0032 Layout.minimumHeight: implicitHeight 0033 Layout.maximumHeight: Kirigami.Units.gridUnit * 40 0034 Layout.preferredWidth: Math.max(implicitWidth, width) 0035 Layout.preferredHeight: Math.max(implicitHeight, height) 0036 0037 property alias normalPage: normalPage 0038 property bool blockingHoverFocus: false 0039 0040 /* NOTE: Important things to know about keyboard input handling: 0041 * 0042 * - Key events are passed up to parent items until the end is reached. 0043 * Be mindful of this when using `Keys.forwardTo`. 0044 * 0045 * - Keys defaults to BeforeItem while KeyNavigation defaults to AfterItem. 0046 * 0047 * - When Keys and KeyNavigation are using the same priority, it seems like 0048 * the one declared first in the QML file gets priority over the other. 0049 * 0050 * - Except for Keys.onPressed, all Keys.on*Pressed signals automatically 0051 * set `event.accepted = true`. 0052 * 0053 * - If you do `item.forceActiveFocus()` and `item` is a focus scope, the 0054 * children of `item` won't necessarily get focus. It seems like 0055 * `forceActiveFocus()` is better for forcing a specific thing to be focused 0056 * while KeyNavigation is better at passing focus down to children of the 0057 * thing you want to focus when dealing with focus scopes. 0058 * 0059 * - KeyNavigation uses BacktabFocusReason (TabFocusReason if mirrored) for left, 0060 * TabFocusReason (BacktabFocusReason if mirrored) for right, 0061 * BacktabFocusReason for up and TabFocusReason for down. 0062 * 0063 * - KeyNavigation does not seem to respect dynamic changes to focus chain 0064 * rules in the reverse direction, which can lead to confusing results. 0065 * It is therefore safer to use Keys for items whose position in the Tab 0066 * order must be changed on demand. (Tested with Qt 5.15.8 on X11.) 0067 */ 0068 0069 header: Header { 0070 id: header 0071 preferredNameAndIconWidth: normalPage.preferredSideBarWidth 0072 Binding { 0073 target: kickoff 0074 property: "header" 0075 value: header 0076 restoreMode: Binding.RestoreBinding 0077 } 0078 } 0079 0080 contentItem: VerticalStackView { 0081 id: contentItemStackView 0082 focus: true 0083 movementTransitionsEnabled: true 0084 // Not using a component to prevent it from being destroyed 0085 initialItem: NormalPage { 0086 id: normalPage 0087 objectName: "normalPage" 0088 } 0089 0090 Component { 0091 id: searchViewComponent 0092 KickoffListView { 0093 id: searchView 0094 objectName: "searchView" 0095 mainContentView: true 0096 // Forces the function be re-run every time runnerModel.count changes. 0097 // This is absolutely necessary to make the search view work reliably. 0098 model: kickoff.runnerModel.count ? kickoff.runnerModel.modelForRow(0) : null 0099 delegate: KickoffListDelegate { 0100 width: view.availableWidth 0101 isSearchResult: true 0102 } 0103 activeFocusOnTab: true 0104 property var interceptedPosition: null 0105 Keys.onTabPressed: event => { 0106 kickoff.firstHeaderItem.forceActiveFocus(Qt.TabFocusReason); 0107 } 0108 Keys.onBacktabPressed: event => { 0109 kickoff.lastHeaderItem.forceActiveFocus(Qt.BacktabFocusReason); 0110 } 0111 T.StackView.onActivated: { 0112 kickoff.sideBar = null 0113 kickoff.contentArea = searchView 0114 } 0115 0116 Connections { 0117 target: blockHoverFocusHandler 0118 enabled: blockHoverFocusHandler.enabled && !searchView.interceptedPosition 0119 function onPointChanged() { 0120 searchView.interceptedPosition = blockHoverFocusHandler.point.position 0121 } 0122 } 0123 0124 Connections { 0125 target: blockHoverFocusHandler 0126 enabled: blockHoverFocusHandler.enabled && searchView.interceptedPosition && root.blockingHoverFocus 0127 function onPointChanged() { 0128 if (blockHoverFocusHandler.point.position === searchView.interceptedPosition) { 0129 return; 0130 } 0131 root.blockingHoverFocus = false 0132 } 0133 } 0134 0135 HoverHandler { 0136 id: blockHoverFocusHandler 0137 enabled: !contentItemStackView.busy && (!searchView.interceptedPosition || root.blockingHoverFocus) 0138 } 0139 0140 Loader { 0141 anchors.centerIn: searchView.view 0142 width: searchView.view.width - (Kirigami.Units.gridUnit * 4) 0143 0144 active: searchView.view.count === 0 0145 visible: active 0146 asynchronous: true 0147 0148 sourceComponent: PlasmaExtras.PlaceholderMessage { 0149 id: emptyHint 0150 0151 iconName: "edit-none" 0152 opacity: 0 0153 text: i18nc("@info:status", "No matches") 0154 0155 Connections { 0156 target: kickoff.runnerModel 0157 function onQueryFinished() { 0158 showAnimation.restart() 0159 } 0160 } 0161 0162 NumberAnimation { 0163 id: showAnimation 0164 duration: Kirigami.Units.longDuration 0165 easing.type: Easing.OutCubic 0166 property: "opacity" 0167 target: emptyHint 0168 to: 1 0169 } 0170 } 0171 } 0172 } 0173 } 0174 0175 Keys.priority: Keys.AfterItem 0176 // This is here rather than root because events are implicitly forwarded 0177 // to parent items. Don't want to send multiple events to searchField. 0178 Keys.forwardTo: kickoff.searchField 0179 0180 Connections { 0181 target: root.header 0182 function onSearchTextChanged() { 0183 if (root.header.searchText.length === 0 && contentItemStackView.currentItem.objectName !== "normalPage") { 0184 root.blockingHoverFocus = false 0185 contentItemStackView.reverseTransitions = true 0186 contentItemStackView.replace(normalPage) 0187 } else if (root.header.searchText.length > 0) { 0188 if (contentItemStackView.currentItem.objectName !== "searchView") { 0189 contentItemStackView.reverseTransitions = false 0190 contentItemStackView.replace(searchViewComponent) 0191 } else { 0192 root.blockingHoverFocus = true 0193 contentItemStackView.contentItem.interceptedPosition = null 0194 contentItemStackView.contentItem.currentIndex = 0 0195 } 0196 } 0197 } 0198 } 0199 } 0200 0201 Component.onCompleted: { 0202 rootModel.refresh(); 0203 } 0204 }