Warning, /plasma/plasma-desktop/applets/kickoff/package/contents/ui/AbstractKickoffItemDelegate.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: 2014 Sebastian Kügler <sebas@kde.org>
0005     SPDX-FileCopyrightText: 2015-2018 Eike Hein <hein@kde.org>
0006     SPDX-FileCopyrightText: 2021 Mikel Johnson <mikel5764@gmail.com>
0007     SPDX-FileCopyrightText: 2021 Noah Davis <noahadvs@gmail.com>
0008     SPDX-FileCopyrightText: 2022 Nate Graham <nate@kde.org>
0009 
0010     SPDX-License-Identifier: GPL-2.0-or-later
0011 */
0012 import QtQuick 2.15
0013 import QtQml 2.15
0014 import QtQuick.Layouts 1.15
0015 import QtQuick.Templates 2.15 as T
0016 import org.kde.plasma.core as PlasmaCore
0017 import org.kde.plasma.components 3.0 as PC3
0018 import org.kde.kirigami 2.20 as Kirigami
0019 import "code/tools.js" as Tools
0020 import org.kde.plasma.plasmoid 2.0
0021 
0022 T.ItemDelegate {
0023     id: root
0024 
0025     // model properties
0026     required property var model
0027     required property int index
0028     required property url url
0029     required property var decoration
0030     required property string description
0031 
0032     readonly property Flickable view: ListView.view ?? GridView.view
0033     property bool isCategoryListItem: false
0034     readonly property bool hasActionList: model && (model.favoriteId !== null || ("hasActionList" in model && model.hasActionList === true))
0035     property bool isSearchResult: false
0036 
0037     readonly property bool isSeparator: model && (model.isSeparator === true)
0038     property int separatorHeight: KickoffSingleton.lineSvg.horLineHeight + (2 * Kirigami.Units.smallSpacing)
0039     property int itemHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, implicitContentHeight + topPadding + bottomPadding)
0040 
0041     readonly property bool dragEnabled: enabled && !isCategoryListItem
0042         && Plasmoid.immutability !== PlasmaCore.Types.SystemImmutable
0043 
0044     readonly property alias mouseArea: mouseArea
0045 
0046     readonly property bool iconAndLabelsShouldlookSelected: isPressed && !isCategoryListItem
0047 
0048     property bool labelTruncated: false
0049     property bool descriptionTruncated: false
0050     property bool descriptionVisible: true
0051 
0052     property Item dragIconItem: null
0053 
0054     // pressed: is read-only and we're not using it here because we have fancy
0055     // custom mouse handling
0056     readonly property bool isPressed: mouseArea.pressed
0057 
0058     function openActionMenu(x = undefined, y = undefined) {
0059         if (!hasActionList) { return; }
0060 
0061         let actions = model.actionList;
0062         const favoriteActions = Tools.createFavoriteActions(
0063             i18n, //i18n() function callback
0064             view.model.favoritesModel,
0065             model.favoriteId,
0066         );
0067         if (favoriteActions) {
0068             if (actions && actions.length > 0) {
0069                 actions.push({ "type": "separator" }, ...favoriteActions);
0070             } else {
0071                 actions = favoriteActions;
0072             }
0073         }
0074 
0075         if (actions && actions.length > 0) {
0076             ActionMenu.plasmoid = kickoff;
0077             ActionMenu.menu.visualParent = root;
0078             ActionMenu.actionList = actions;
0079             if (x !== undefined && y !== undefined) {
0080                 ActionMenu.menu.open(x, y);
0081             } else {
0082                 ActionMenu.menu.openRelative();
0083             }
0084         }
0085     }
0086 
0087     // The default Z value for delegates is 1. The default Z value for the section delegate is 2.
0088     // The highlight gets a value of 3 while the drag is active and then goes back to the default value of 0.
0089     z: Drag.active ? 4 : 1
0090 
0091     implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
0092                             implicitContentWidth + leftPadding + rightPadding)
0093     implicitHeight: isSeparator ? separatorHeight : itemHeight
0094 
0095     spacing: KickoffSingleton.fontMetrics.descent
0096 
0097     enabled: !isSeparator && !model.disabled
0098     hoverEnabled: false
0099 
0100     text: model.name ?? model.displayWrapped ?? model.display
0101     Accessible.role: Accessible.ListItem
0102     Accessible.description: root.description !== root.text ? root.description : ""
0103     Accessible.onPressAction: {
0104         root.forceActiveFocus() // trigger is focus guarded
0105         action.trigger()
0106     }
0107 
0108     // Using an action so that it can be replaced or manually triggered
0109     // using `model` () instead of `root.model` leads to errors about
0110     // `model` not having the trigger() function
0111     action: T.Action {
0112         onTriggered: {
0113             // Unless we're showing search results, eat the activation if we
0114             // don't have focus, to prevent the return/enter key from
0115             // inappropriately activating unfocused items
0116             if (!root.activeFocus && !root.isSearchResult) {
0117                 return;
0118             }
0119             view.currentIndex = index
0120             // if successfully triggered, close popup
0121             if (view.model.trigger && view.model.trigger(index, "", null)) {
0122                 if (kickoff.hideOnWindowDeactivate) {
0123                     kickoff.expanded = false;
0124                 }
0125             }
0126         }
0127     }
0128 
0129     Drag.active: mouseArea.drag.active
0130     Drag.dragType: Drag.Automatic
0131     Drag.mimeData: { "text/uri-list" : root.url }
0132     Drag.onDragFinished: Drag.imageSource = ""
0133 
0134     MouseArea {
0135         id: mouseArea
0136         property bool dragEnabled: false
0137         parent: root
0138         anchors.fill: parent
0139         anchors.margins: 1
0140         // Flickable margins are not mirrored, so disable layout mirroring
0141         LayoutMirroring.enabled: false
0142         // Only for ListView since extending margins for GridView is hard
0143         anchors.leftMargin: root.view instanceof ListView ? -root.view.leftMargin : anchors.margins
0144         anchors.rightMargin: root.view instanceof ListView ? -root.view.rightMargin : anchors.margins
0145         hoverEnabled: root.view
0146             // When the movedWithWheel condition is broken, this ensures that
0147             // onEntered is called again without moving the mouse.
0148             && !root.view.movedWithWheel
0149             // Fix VerticalStackView animation causing view currentIndex
0150             // to change while delegates are moving under the mouse cursor
0151             && kickoff.fullRepresentationItem && !kickoff.fullRepresentationItem.contentItem.busy && !kickoff.fullRepresentationItem.blockingHoverFocus
0152         acceptedButtons: Qt.LeftButton | Qt.RightButton
0153         drag {
0154             axis: Drag.XAndYAxis
0155             target: root.dragEnabled && mouseArea.dragEnabled ? dragItem : undefined
0156         }
0157         // Using this Item fixes drag and drop causing delegates
0158         // to reset to a 0 X position and overlapping each other.
0159         Item { id: dragItem }
0160 
0161         onEntered: {
0162             // When the movedWithKeyboard condition is broken, we do not want to
0163             // select the hovered item without moving the mouse.
0164             if (root.view.movedWithKeyboard) {
0165                 return
0166             }
0167             // Don't highlight separators.
0168             if (root.isSeparator) {
0169                 return;
0170             }
0171 
0172             // forceActiveFocus() touches multiple items, so check for
0173             // activeFocus first to be more efficient.
0174             if (!root.activeFocus) {
0175                 root.forceActiveFocus(Qt.MouseFocusReason)
0176             }
0177             // No need to check currentIndex first because it's
0178             // built into QQuickListView::setCurrentIndex() already
0179             root.view.currentIndex = index
0180         }
0181         onPressed: mouse => {
0182             // Select and focus on press to improve responsiveness and touch feedback
0183             view.currentIndex = index
0184             root.forceActiveFocus(Qt.MouseFocusReason)
0185 
0186             // Only enable drag and drop with a mouse.
0187             // We don't have a good way to handle it and drag scrolling with touch.
0188             mouseArea.dragEnabled = mouse.source === Qt.MouseEventNotSynthesized
0189 
0190             // We normally try to open right click menus on press like Qt Widgets
0191             if (mouse.button === Qt.RightButton) {
0192                 root.openActionMenu(mouseX, mouseY)
0193             } else if (mouseArea.dragEnabled && mouse.button === Qt.LeftButton
0194                 && root.dragEnabled && root.dragIconItem && root.Drag.imageSource.toString() === ""
0195             ) {
0196                 root.dragIconItem.grabToImage(result => {
0197                     root.Drag.imageSource = result.url
0198                 })
0199             }
0200         }
0201         onClicked: mouse => {
0202             if (mouse.button === Qt.LeftButton) {
0203                 root.action.trigger()
0204             }
0205         }
0206         // MouseEvents for pressAndHold use Qt.MouseEventSynthesizedByQt for mouse.source,
0207         // which makes checking mouse.source for whether or not touch input is used useless.
0208         onPressAndHold: mouse => {
0209             if (mouse.button === Qt.LeftButton) {
0210                 root.openActionMenu(mouseX, mouseY)
0211             }
0212         }
0213     }
0214 
0215     PC3.ToolTip.text: {
0216         if (root.labelTruncated && root.descriptionTruncated) {
0217             return `${text} (${description})`
0218         } else if (root.descriptionTruncated || !root.descriptionVisible) {
0219             return description
0220         }
0221         return ""
0222     }
0223     PC3.ToolTip.visible: mouseArea.containsMouse && PC3.ToolTip.text.length > 0
0224     PC3.ToolTip.delay: Kirigami.Units.toolTipDelay
0225 
0226     background: null
0227 }