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 }