/*SPDX-FileCopyrightText: 2011 Martin GrΓ€Γlin <mgraesslin@kde.org>SPDX-FileCopyrightText: 2012 Gregor Taetzner <gregor@freenet.de>SPDX-FileCopyrightText: 2012 Marco Martin <mart@kde.org>SPDX-FileCopyrightText: 2013 2014 David Edmundson <davidedmundson@kde.org>SPDX-FileCopyrightText: 2014 Sebastian KΓΌgler <sebas@kde.org>SPDX-FileCopyrightText: 2021 Mikel Johnson <mikel5764@gmail.com>SPDX-FileCopyrightText: 2021 Noah Davis <noahadvs@gmail.com>SPDX-License-Identifier: GPL-2.0-or-later*/pragma ComponentBehavior: Boundimport QtQuickimport QtQuick.Templates as Timport QtQuick.Layoutsimport org.kde.plasma.plasmoidimport org.kde.plasma.private.kicker as Kickerimport org.kde.kirigami as Kirigamiimport org.kde.plasma.extras as PlasmaExtrasEmptyPage {id: root// kickoff is Kickoff.qmlleftPadding: -kickoff.backgroundMetrics.leftPaddingrightPadding: -kickoff.backgroundMetrics.rightPaddingtopPadding: 0bottomPadding: -kickoff.backgroundMetrics.bottomPaddingreadonly property var appletInterface: kickoffLayout.minimumWidth: implicitWidthLayout.minimumHeight: {const stackView = normalPage.contentItem as HorizontalStackViewconst basePage = stackView.currentItem as BasePageconst contentAreaItem = basePage.contentAreaItem as Itemreturn root.calculateTotalHeightForContentHeight(normalPage.calculateTotalHeightForContentHeight(contentAreaItem.implicitHeight))}property alias normalPage: normalPageproperty bool blockingHoverFocus: trueproperty var interceptedPosition: null/* NOTE: Important things to know about keyboard input handling:** - Key events are passed up to parent items until the end is reached.* Be mindful of this when using `Keys.forwardTo`.** - Keys defaults to BeforeItem while KeyNavigation defaults to AfterItem.** - When Keys and KeyNavigation are using the same priority, it seems like* the one declared first in the QML file gets priority over the other.** - Except for Keys.onPressed, all Keys.on*Pressed signals automatically* set `event.accepted = true`.** - If you do `item.forceActiveFocus()` and `item` is a focus scope, the* children of `item` won't necessarily get focus. It seems like* `forceActiveFocus()` is better for forcing a specific thing to be focused* while KeyNavigation is better at passing focus down to children of the* thing you want to focus when dealing with focus scopes.** - KeyNavigation uses BacktabFocusReason (TabFocusReason if mirrored) for left,* TabFocusReason (BacktabFocusReason if mirrored) for right,* BacktabFocusReason for up and TabFocusReason for down.** - KeyNavigation does not seem to respect dynamic changes to focus chain* rules in the reverse direction, which can lead to confusing results.* It is therefore safer to use Keys for items whose position in the Tab* order must be changed on demand. (Tested with Qt 5.15.8 on X11.)*/header: Header {id: headerpreferredNameAndIconWidth: normalPage.preferredSideBarWidthBinding {target: kickoffproperty: "header"value: headerrestoreMode: Binding.RestoreBinding}}contentItem: VerticalStackView {id: contentItemStackViewfocus: truemovementTransitionsEnabled: trueimplicitHeight: normalPage.implicitHeight + topPadding + bottomPaddingimplicitWidth: normalPage.implicitWidth + leftPadding + rightPadding// Not using a component to prevent it from being destroyedinitialItem: NormalPage {id: normalPageobjectName: "normalPage"}Component {id: searchViewComponentKickoffListView {id: searchViewobjectName: "searchView"mainContentView: true// Forces the function be re-run every time runnerModel.count changes.// This is absolutely necessary to make the search view work reliably.model: kickoff.runnerModel.count ? kickoff.runnerModel.modelForRow(0) : nulldelegate: KickoffListDelegate {width: view.availableWidthisSearchResult: true}section.property: "group"activeFocusOnTab: trueKeys.onTabPressed: event => {kickoff.firstHeaderItem.forceActiveFocus(Qt.TabFocusReason);}Keys.onBacktabPressed: event => {kickoff.lastHeaderItem.forceActiveFocus(Qt.BacktabFocusReason);}Keys.onUpPressed: event => {kickoff.searchField.forceActiveFocus(Qt.BacktabFocusReason)}T.StackView.onStatusChanged: {if (T.StackView.status === T.StackView.Activating || T.StackView.status === T.StackView.Activating) {kickoff.sideBar = nullkickoff.contentArea = searchView}}Loader {anchors.centerIn: searchView.viewwidth: searchView.view.width - (Kirigami.Units.gridUnit * 4)active: searchView.view.count === 0visible: activeasynchronous: truesourceComponent: PlasmaExtras.PlaceholderMessage {id: emptyHinticonName: "edit-none"opacity: 0text: i18nc("@info:status", "No matches")Connections {target: kickoff.runnerModelfunction onQueryFinished() {showAnimation.restart()}}NumberAnimation {id: showAnimationduration: Kirigami.Units.longDurationeasing.type: Easing.OutCubicproperty: "opacity"target: emptyHintto: 1}}}}}Connections {target: kickofffunction onExpandedChanged() {if (!kickoff.expanded) {root.blockingHoverFocus = trueroot.interceptedPosition = null}}}Connections {target: blockHoverFocusHandlerenabled: blockHoverFocusHandler.enabled && !root.interceptedPositionfunction onPointChanged() {root.interceptedPosition = blockHoverFocusHandler.point.position}}Connections {target: blockHoverFocusHandlerenabled: blockHoverFocusHandler.enabled && root.interceptedPosition && root.blockingHoverFocusfunction onPointChanged() {if (blockHoverFocusHandler.point.position === root.interceptedPosition) {return;}root.blockingHoverFocus = false}}HoverHandler {id: blockHoverFocusHandlerenabled: !contentItemStackView.busy && (!root.interceptedPosition || root.blockingHoverFocus)}Keys.priority: Keys.AfterItem// This is here rather than root because events are implicitly forwarded// to parent items. Don't want to send multiple events to searchField.Keys.forwardTo: kickoff.searchFieldConnections {target: root.headerfunction onSearchTextChanged() {if ((root.header as Header).searchText.length === 0 &&contentItemStackView.currentItem.objectName !== "normalPage") {contentItemStackView.reverseTransitions = truecontentItemStackView.replace(normalPage)} else if ((root.header as Header).searchText.length > 0) {if (contentItemStackView.currentItem.objectName !== "searchView") {contentItemStackView.reverseTransitions = falsecontentItemStackView.replace(searchViewComponent)} else {contentItemStackView.contentItem.currentIndex = 0}}root.blockingHoverFocus = trueroot.interceptedPosition = null}}}Loader {active: !!kickoff.dragSource.sourceItemanchors.fill: parentsourceComponent: DropArea {id: favoriteRemoveDropArea// should be "as AbstractKickoffItemDelegate", but the type system gets confused when changing view style at runtimereadonly property Item draggedItem: kickoff.dragSource.sourceItemonEntered: event => {if (draggedItem?.view.model instanceof Kicker.KAStatsFavoritesModel) {event.accept (Qt.MoveAction)draggedItem.removalPlaceholderActive = true} else {event.accepted = false}}onDropped: event => {if (draggedItem && kickoff.rootModel.favoritesModel.isFavorite(draggedItem.model.favoriteId) && draggedItem.view.model instanceof Kicker.KAStatsFavoritesModel) {kickoff.rootModel.favoritesModel.removeFavorite(draggedItem.model.favoriteId);event.accept(Qt.MoveAction)} else {draggedItem.removalPlaceholderActive = falseevent.accepted = false}}onExited: {if (draggedItem) {draggedItem.removalPlaceholderActive = false}}}}Component.onCompleted: {rootModel.refresh();}}