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 }