/*SPDX-FileCopyrightText: 2011 Martin GrΓ€Γlin <mgraesslin@kde.org>SPDX-FileCopyrightText: 2012 Gregor Taetzner <gregor@freenet.de>SPDX-FileCopyrightText: 2014 Sebastian KΓΌgler <sebas@kde.org>SPDX-FileCopyrightText: 2015-2018 Eike Hein <hein@kde.org>SPDX-FileCopyrightText: 2021 Mikel Johnson <mikel5764@gmail.com>SPDX-FileCopyrightText: 2021 Noah Davis <noahadvs@gmail.com>SPDX-FileCopyrightText: 2022 Nate Graham <nate@kde.org>SPDX-License-Identifier: GPL-2.0-or-later*/pragma ComponentBehavior: Boundimport QtQuickimport QtQuick.Templates as Timport org.kde.plasma.core as PlasmaCoreimport org.kde.plasma.components as PC3import org.kde.plasma.extras as PlasmaExtrasimport org.kde.kirigami as Kirigamiimport "code/tools.js" as Toolsimport org.kde.plasma.plasmoidT.ItemDelegate {id: rootenum AppNameFormat {NameOnly,GenericNameOnly,NameAndGenericName,GenericNameAndName}// model propertiesrequired property var modelrequired property int indexrequired property url urlrequired property var decorationrequired property string descriptionrequired property bool isMultilineTextreadonly property Flickable view: ListView.view ?? GridView.viewproperty bool isCategoryListItem: falseproperty bool removalPlaceholderActive: falsereadonly property bool hasActionList: model && (model.favoriteId !== null || ("hasActionList" in model && model.hasActionList === true))property bool isSearchResult: falsereadonly property bool isSeparator: model && (model.isSeparator === true)property int separatorHeight: KickoffSingleton.lineSvg.horLineHeight + (2 * Kirigami.Units.smallSpacing)property int itemHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, implicitContentHeight + topPadding + bottomPadding)readonly property bool dragEnabled: enabled && !isCategoryListItem&& Plasmoid.immutability !== PlasmaCore.Types.SystemImmutablereadonly property alias mouseArea: mouseAreareadonly property bool iconAndLabelsShouldlookSelected: down && !isCategoryListItemproperty bool labelTruncated: falseproperty bool descriptionTruncated: falseproperty bool descriptionVisible: trueproperty Item dragIconItem: nulldown: mouseArea.pressed || dragHandler.activefunction openActionMenu(x = undefined, y = undefined) {if (!hasActionList) { return; }let actions = Array.from(model.actionList);const favoriteActions = Tools.createFavoriteActions(i18n, //i18n() function callbackroot.view.model.favoritesModel,model.favoriteId,);if (favoriteActions) {if (actions && actions.length > 0) {// same logic as in kicker's fillActionMenu()// Insert favoriteActions after the "Add to" actions (called// "addLauncherActions" by libkicker)const firstAddToActionIndex = actions.findIndex((action) =>["addToDesktop", "addToTaskManager", "addToPanel"].includes(action?.actionId),);if (firstAddToActionIndex >= 0) {actions.splice(firstAddToActionIndex, 0, ...favoriteActions);} else {// Or at the end (with a separator) if they're not foundactions.push({ type: "separator" }, ...favoriteActions);}} else {actions = favoriteActions;}}if (actions && actions.length > 0) {ActionMenu.plasmoid = kickoff;ActionMenu.menu.visualParent = root;ActionMenu.actionList = actions;if (x !== undefined && y !== undefined) {ActionMenu.menu.open(x, y);} else {ActionMenu.menu.openRelative();}}}// The default Z value for delegates is 1. The default Z value for the section delegate is 2.// The highlight gets a value of 3 while the drag is active and then goes back to the default value of 0.z: Drag.active ? 4 : 1implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,implicitContentWidth + leftPadding + rightPadding)implicitHeight: isSeparator ? separatorHeight : itemHeightspacing: KickoffSingleton.fontMetrics.descentenabled: !isSeparator && !model.disabledhoverEnabled: falsetext: model.compactNameWrapped ?? model.compactName ?? model.displayWrapped ?? model.displayAccessible.role: Accessible.ListItemAccessible.description: root.description !== root.text ? root.description : ""Accessible.onPressAction: {root.forceActiveFocus() // trigger is focus guardedaction.trigger()}// Using an action so that it can be replaced or manually triggered// using `model` () instead of `root.model` leads to errors about// `model` not having the trigger() functionaction: T.Action {onTriggered: {// Unless we're showing search results, eat the activation if we// don't have focus, to prevent the return/enter key from// inappropriately activating unfocused items// Also block activation while dragging.if ((!root.activeFocus && !root.isSearchResult) || dragHandler.active || touchDragHandler.active) {return;}root.view.currentIndex = root.index// if successfully triggered, close popupif (root.view.model.trigger && root.view.model.trigger(root.index, "", null)) {if (kickoff.hideOnWindowDeactivate) {kickoff.expanded = false;}}}}function performDrag(handler: DragHandler): void {if (!handler.active) {kickoff.dragSource.Drag.active = false;kickoff.dragSource.Drag.imageSource = "";kickoff.dragSource.sourceItem = null;return;}root.dragIconItem.grabToImage(result => {if (!handler.active) {return;}kickoff.dragSource.sourceItem = root;kickoff.dragSource.Drag.imageSource = result.url;kickoff.dragSource.Drag.mimeData = {"text/uri-list" : [root.url]};kickoff.dragSource.Drag.active = handler.active;});}DragHandler {id: dragHandleracceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad | PointerDevice.Stylusenabled: root.dragEnabled && root.dragIconItem !== nulltarget: null // Using this Item fixes drag and drop causing delegates to reset to a 0 X position and overlapping each other.onActiveChanged: root.performDrag(this)}DragHandler {id: touchDragHandleracceptedDevices: PointerDevice.TouchScreenenabled: dragHandler.enabledtarget: nullyAxis.enabled: falseonActiveChanged: root.performDrag(this)}MouseArea {id: mouseAreaparent: rootanchors.fill: parentanchors.margins: 1// Flickable margins are not mirrored, so disable layout mirroringLayoutMirroring.enabled: false// Only for ListView since extending margins for GridView is hardanchors.leftMargin: root.view instanceof ListView ? -root.view.leftMargin : anchors.marginsanchors.rightMargin: root.view instanceof ListView ? -root.view.rightMargin : anchors.marginshoverEnabled: root.view// When the movedWithWheel condition is broken, this ensures that// onEntered is called again without moving the mouse.&& !root.view.movedWithWheel// Fix VerticalStackView animation causing view currentIndex// to change while delegates are moving under the mouse cursor&& kickoff.fullRepresentationItem && !kickoff.fullRepresentationItem.contentItem.busy && !kickoff.fullRepresentationItem.blockingHoverFocusacceptedButtons: Qt.LeftButton | Qt.RightButtononEntered: {// - When the movedWithKeyboard condition is broken, we do not want to// select the hovered item without moving the mouse.// - Don't highlight separators.// - Don't switch category items on hover if the setting isn't enabledif (root.view.movedWithKeyboard || root.isSeparator|| (root.isCategoryListItem && !Plasmoid.configuration.switchCategoryOnHover)) {return}// forceActiveFocus() touches multiple items, so check for// activeFocus first to be more efficient. Keep activeFocus// stable if it's on the searchField to avoid unintentional// activation with Space key presses.if (!root.activeFocus && !kickoff.searchField?.activeFocus) {root.forceActiveFocus(Qt.MouseFocusReason)}// No need to check currentIndex first because it's// built into QQuickListView::setCurrentIndex() alreadyroot.view.currentIndex = root.index}onPressed: mouse => {// Select and focus on press to improve responsiveness and touch feedbackroot.view.currentIndex = root.indexroot.forceActiveFocus(Qt.MouseFocusReason)// We normally try to open right click menus on press like Qt Widgetsif (mouse.button === Qt.RightButton) {root.openActionMenu(mouseX, mouseY)}}onClicked: mouse => {if (mouse.button === Qt.LeftButton) {root.action.trigger()}}TapHandler {acceptedDevices: PointerDevice.TouchScreenonLongPressed: root.openActionMenu(point.position.x, point.position.y)}}PC3.ToolTip.text: {if (root.labelTruncated) {return model?.display ?? ""} else if (root.descriptionTruncated || (!root.descriptionVisible && (root.isSearchResult|| Plasmoid.configuration.appNameFormat > 1))) {return description}return ""}PC3.ToolTip.visible: mouseArea.containsMouse && PC3.ToolTip.text.length > 0PC3.ToolTip.delay: Kirigami.Units.toolTipDelaybackground: Loader {active: (root.GridView?.isCurrentItem || root.ListView?.isCurrentItem) ?? falsesourceComponent: PlasmaExtras.Highlight {anchors.fill: parentz: (root.Drag.active ?? false) ? 3 : 0hovered: truepressed: root.down && !root.isCategoryListItemactive: root.view.activeFocus ||(kickoff.contentArea === root && kickoff.searchField.activeFocus)}}}