Warning, /plasma/plasma-desktop/applets/kicker/package/contents/ui/ItemGridView.qml is written in an unsupported language. File is not indexed.

0001 /*
0002     SPDX-FileCopyrightText: 2015 Eike Hein <hein@kde.org>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 import QtQuick 2.15
0008 
0009 import org.kde.kquickcontrolsaddons 2.0
0010 import org.kde.ksvg 1.0 as KSvg
0011 import org.kde.plasma.components as PlasmaComponents
0012 import org.kde.plasma.extras 2.0 as PlasmaExtras
0013 import org.kde.kirigami 2.20 as Kirigami
0014 
0015 FocusScope {
0016     id: itemGrid
0017 
0018     signal keyNavLeft
0019     signal keyNavRight
0020     signal keyNavUp
0021     signal keyNavDown
0022 
0023     signal itemActivated(int index, string actionId, string argument)
0024 
0025     property bool dragEnabled: true
0026     property bool dropEnabled: false
0027     property bool showLabels: true
0028 
0029     property alias currentIndex: gridView.currentIndex
0030     property alias currentItem: gridView.currentItem
0031     property alias contentItem: gridView.contentItem
0032     property alias count: gridView.count
0033     property alias model: gridView.model
0034 
0035     property alias cellWidth: gridView.cellWidth
0036     property alias cellHeight: gridView.cellHeight
0037     property alias iconSize: gridView.iconSize
0038 
0039     property var horizontalScrollBarPolicy: PlasmaComponents.ScrollBar.AlwaysOff
0040     property var verticalScrollBarPolicy: PlasmaComponents.ScrollBar.AsNeeded
0041 
0042     onDropEnabledChanged: {
0043         if (!dropEnabled && "dropPlaceHolderIndex" in model) {
0044             model.dropPlaceHolderIndex = -1;
0045         }
0046     }
0047 
0048     onFocusChanged: {
0049         if (!focus && !root.keyEventProxy.activeFocus) {
0050             currentIndex = -1;
0051         }
0052     }
0053 
0054     function currentRow() {
0055         if (currentIndex === -1) {
0056             return -1;
0057         }
0058 
0059         return Math.floor(currentIndex / Math.floor(width / itemGrid.cellWidth));
0060     }
0061 
0062     function currentCol() {
0063         if (currentIndex === -1) {
0064             return -1;
0065         }
0066 
0067         return currentIndex - (currentRow() * Math.floor(width / itemGrid.cellWidth));
0068     }
0069 
0070     function lastRow() {
0071         var columns = Math.floor(width / itemGrid.cellWidth);
0072         return Math.ceil(count / columns) - 1;
0073     }
0074 
0075     function tryActivate(row, col) {
0076         if (count) {
0077             var columns = Math.floor(width / itemGrid.cellWidth);
0078             var rows = Math.ceil(count / columns);
0079             row = Math.min(row, rows - 1);
0080             col = Math.min(col, columns - 1);
0081             currentIndex = Math.min(row ? ((Math.max(1, row) * columns) + col)
0082                 : col,
0083                 count - 1);
0084 
0085             focus = true;
0086         }
0087     }
0088 
0089     function forceLayout() {
0090         gridView.forceLayout();
0091     }
0092 
0093     ActionMenu {
0094         id: actionMenu
0095 
0096         onActionClicked: {
0097             visualParent.actionTriggered(actionId, actionArgument);
0098         }
0099     }
0100 
0101     DropArea {
0102         id: dropArea
0103 
0104         anchors.fill: parent
0105 
0106         onPositionChanged: event => {
0107             if (!itemGrid.dropEnabled || gridView.animating || !kicker.dragSource) {
0108                 return;
0109             }
0110 
0111             var x = Math.max(0, event.x - (width % itemGrid.cellWidth));
0112             var cPos = mapToItem(gridView.contentItem, x, event.y);
0113             var item = gridView.itemAt(cPos.x, cPos.y);
0114 
0115             if (item) {
0116                 if (kicker.dragSource.parent === gridView.contentItem) {
0117                     if (item !== kicker.dragSource) {
0118                         item.GridView.view.model.moveRow(dragSource.itemIndex, item.itemIndex);
0119                     }
0120                 } else if (kicker.dragSource.GridView.view.model.favoritesModel === itemGrid.model
0121                     && !itemGrid.model.isFavorite(kicker.dragSource.favoriteId)) {
0122                     var hasPlaceholder = (itemGrid.model.dropPlaceholderIndex !== -1);
0123 
0124                     itemGrid.model.dropPlaceholderIndex = item.itemIndex;
0125 
0126                     if (!hasPlaceholder) {
0127                         gridView.currentIndex = (item.itemIndex - 1);
0128                     }
0129                 }
0130             } else if (kicker.dragSource.parent !== gridView.contentItem
0131                 && kicker.dragSource.GridView.view.model.favoritesModel === itemGrid.model
0132                 && !itemGrid.model.isFavorite(kicker.dragSource.favoriteId)) {
0133                     var hasPlaceholder = (itemGrid.model.dropPlaceholderIndex !== -1);
0134 
0135                     itemGrid.model.dropPlaceholderIndex = hasPlaceholder ? itemGrid.model.count - 1 : itemGrid.model.count;
0136 
0137                     if (!hasPlaceholder) {
0138                         gridView.currentIndex = (itemGrid.model.count - 1);
0139                     }
0140             } else {
0141                 itemGrid.model.dropPlaceholderIndex = -1;
0142                 gridView.currentIndex = -1;
0143             }
0144         }
0145 
0146         onExited: {
0147             if ("dropPlaceholderIndex" in itemGrid.model) {
0148                 itemGrid.model.dropPlaceholderIndex = -1;
0149                 gridView.currentIndex = -1;
0150             }
0151         }
0152 
0153         onDropped: {
0154             if (kicker.dragSource && kicker.dragSource.parent !== gridView.contentItem && kicker.dragSource.GridView.view.model.favoritesModel === itemGrid.model) {
0155                 itemGrid.model.addFavorite(kicker.dragSource.favoriteId, itemGrid.model.dropPlaceholderIndex);
0156                 gridView.currentIndex = -1;
0157             }
0158         }
0159 
0160         Timer {
0161             id: resetAnimationDurationTimer
0162 
0163             interval: 120
0164             repeat: false
0165 
0166             onTriggered: {
0167                 gridView.animationDuration = interval - 20;
0168             }
0169         }
0170 
0171         PlasmaComponents.ScrollView {
0172             id: scrollArea
0173 
0174             anchors.fill: parent
0175 
0176             focus: true
0177 
0178             PlasmaComponents.ScrollBar.horizontal.policy: itemGrid.horizontalScrollBarPolicy
0179 
0180             GridView {
0181                 id: gridView
0182 
0183                 signal itemContainsMouseChanged(bool containsMouse)
0184 
0185                 property int iconSize: Kirigami.Units.iconSizes.huge
0186 
0187                 property bool animating: false
0188                 property int animationDuration: itemGrid.dropEnabled ? resetAnimationDurationTimer.interval : 0
0189 
0190                 focus: true
0191 
0192                 currentIndex: -1
0193 
0194                 move: Transition {
0195                     enabled: itemGrid.dropEnabled
0196 
0197                     SequentialAnimation {
0198                         PropertyAction { target: gridView; property: "animating"; value: true }
0199 
0200                         NumberAnimation {
0201                             duration: gridView.animationDuration
0202                             properties: "x, y"
0203                             easing.type: Easing.OutQuad
0204                         }
0205 
0206                         PropertyAction { target: gridView; property: "animating"; value: false }
0207                     }
0208                 }
0209 
0210                 moveDisplaced: Transition {
0211                     enabled: itemGrid.dropEnabled
0212 
0213                     SequentialAnimation {
0214                         PropertyAction { target: gridView; property: "animating"; value: true }
0215 
0216                         NumberAnimation {
0217                             duration: gridView.animationDuration
0218                             properties: "x, y"
0219                             easing.type: Easing.OutQuad
0220                         }
0221 
0222                         PropertyAction { target: gridView; property: "animating"; value: false }
0223                     }
0224                 }
0225 
0226                 keyNavigationWraps: false
0227                 boundsBehavior: Flickable.StopAtBounds
0228 
0229                 delegate: ItemGridDelegate {
0230                     showLabel: itemGrid.showLabels
0231                 }
0232 
0233                 highlight: Item {
0234                     property bool isDropPlaceHolder: "dropPlaceholderIndex" in itemGrid.model && itemGrid.currentIndex === itemGrid.model.dropPlaceholderIndex
0235 
0236                     PlasmaExtras.Highlight {
0237                         visible: gridView.currentItem && !isDropPlaceHolder
0238                         hovered: true
0239                         pressed: hoverArea.pressed
0240 
0241                         anchors.fill: parent
0242                     }
0243 
0244                     KSvg.FrameSvgItem {
0245                         visible: gridView.currentItem && isDropPlaceHolder
0246 
0247                         anchors.fill: parent
0248 
0249                         imagePath: "widgets/viewitem"
0250                         prefix: "selected"
0251 
0252                         opacity: 0.5
0253 
0254                         Kirigami.Icon {
0255                             anchors {
0256                                 right: parent.right
0257                                 rightMargin: parent.margins.right
0258                                 bottom: parent.bottom
0259                                 bottomMargin: parent.margins.bottom
0260                             }
0261 
0262                             width: Kirigami.Units.iconSizes.smallMedium
0263                             height: width
0264 
0265                             source: "list-add"
0266                             active: false
0267                         }
0268                     }
0269                 }
0270 
0271                 highlightFollowsCurrentItem: true
0272                 highlightMoveDuration: 0
0273 
0274                 onCurrentIndexChanged: {
0275                     if (currentIndex !== -1) {
0276                         hoverArea.hoverEnabled = false
0277                         focus = true;
0278                     }
0279                 }
0280 
0281                 onCountChanged: {
0282                     animationDuration = 0;
0283                     resetAnimationDurationTimer.start();
0284                 }
0285 
0286                 onModelChanged: {
0287                     currentIndex = -1;
0288                 }
0289 
0290                 Keys.onLeftPressed: event => {
0291                     if (itemGrid.currentCol() !== 0) {
0292                         event.accepted = true;
0293                         moveCurrentIndexLeft();
0294                     } else {
0295                         itemGrid.keyNavLeft();
0296                     }
0297                 }
0298 
0299                 Keys.onRightPressed: event => {
0300                     var columns = Math.floor(width / cellWidth);
0301 
0302                     if (itemGrid.currentCol() !== columns - 1 && currentIndex !== count -1) {
0303                         event.accepted = true;
0304                         moveCurrentIndexRight();
0305                     } else {
0306                         itemGrid.keyNavRight();
0307                     }
0308                 }
0309 
0310                 Keys.onUpPressed: event => {
0311                     if (itemGrid.currentRow() !== 0) {
0312                         event.accepted = true;
0313                         moveCurrentIndexUp();
0314                         positionViewAtIndex(currentIndex, GridView.Contain);
0315                     } else {
0316                         itemGrid.keyNavUp();
0317                     }
0318                 }
0319 
0320                 Keys.onDownPressed: event => {
0321                     if (itemGrid.currentRow() < itemGrid.lastRow()) {
0322                         // Fix moveCurrentIndexDown()'s lack of proper spatial nav down
0323                         // into partial columns.
0324                         event.accepted = true;
0325                         var columns = Math.floor(width / cellWidth);
0326                         var newIndex = currentIndex + columns;
0327                         currentIndex = Math.min(newIndex, count - 1);
0328                         positionViewAtIndex(currentIndex, GridView.Contain);
0329                     } else {
0330                         itemGrid.keyNavDown();
0331                     }
0332                 }
0333 
0334                 onItemContainsMouseChanged: containsMouse => {
0335                     if (!containsMouse) {
0336                         if (!actionMenu.opened) {
0337                             gridView.currentIndex = -1;
0338                         }
0339 
0340                         hoverArea.pressX = -1;
0341                         hoverArea.pressY = -1;
0342                         hoverArea.lastX = -1;
0343                         hoverArea.lastY = -1;
0344                         hoverArea.pressedItem = null;
0345                         hoverArea.hoverEnabled = true;
0346                     }
0347                 }
0348             }
0349         }
0350 
0351         MouseArea {
0352             id: hoverArea
0353 
0354             anchors.fill: parent
0355 
0356             property int pressX: -1
0357             property int pressY: -1
0358             property int lastX: -1
0359             property int lastY: -1
0360             property Item pressedItem: null
0361 
0362             acceptedButtons: Qt.LeftButton | Qt.RightButton
0363 
0364             hoverEnabled: true
0365 
0366             function updatePositionProperties(x, y) {
0367                 // Prevent hover event synthesis in QQuickWindow interfering
0368                 // with keyboard navigation by ignoring repeated events with
0369                 // identical coordinates. As the work done here would be re-
0370                 // dundant in any case, these are safe to ignore.
0371                 if (lastX === x && lastY === y) {
0372                     return;
0373                 }
0374 
0375                 lastX = x;
0376                 lastY = y;
0377 
0378                 var cPos = mapToItem(gridView.contentItem, x, y);
0379                 var item = gridView.itemAt(cPos.x, cPos.y);
0380 
0381                 if (!item) {
0382                     gridView.currentIndex = -1;
0383                     pressedItem = null;
0384                 } else {
0385                     itemGrid.focus = (item.itemIndex !== -1)
0386                     gridView.currentIndex = item.itemIndex;
0387                 }
0388 
0389                 return item;
0390             }
0391 
0392             onPressed: mouse => {
0393                 mouse.accepted = true;
0394 
0395                 updatePositionProperties(mouse.x, mouse.y);
0396 
0397                 pressX = mouse.x;
0398                 pressY = mouse.y;
0399 
0400                 if (mouse.button === Qt.RightButton) {
0401                     if (gridView.currentItem) {
0402                         if (gridView.currentItem.hasActionList) {
0403                             var mapped = mapToItem(gridView.currentItem, mouse.x, mouse.y);
0404                             gridView.currentItem.openActionMenu(mapped.x, mapped.y);
0405                         }
0406                     } else {
0407                         var mapped = mapToItem(rootItem, mouse.x, mouse.y);
0408                         contextMenu.open(mapped.x, mapped.y);
0409                     }
0410                 } else {
0411                     pressedItem = gridView.currentItem;
0412                 }
0413             }
0414 
0415             onReleased: mouse => {
0416                 mouse.accepted = true;
0417                 updatePositionProperties(mouse.x, mouse.y);
0418 
0419                 if (!dragHelper.dragging) {
0420                     if (pressedItem) {
0421                         if ("trigger" in gridView.model) {
0422                             gridView.model.trigger(pressedItem.itemIndex, "", null);
0423                             root.toggle();
0424                         }
0425 
0426                         itemGrid.itemActivated(pressedItem.itemIndex, "", null);
0427                     } else if (mouse.button === Qt.LeftButton) {
0428                         root.toggle();
0429                     }
0430                 }
0431 
0432                 pressX = pressY = -1;
0433                 pressedItem = null;
0434             }
0435 
0436             onPositionChanged: mouse => {
0437                 var item = pressedItem? pressedItem : updatePositionProperties(mouse.x, mouse.y);
0438 
0439                 if (gridView.currentIndex !== -1) {
0440                     if (itemGrid.dragEnabled && pressX !== -1 && dragHelper.isDrag(pressX, pressY, mouse.x, mouse.y)) {
0441                         if ("pluginName" in item.m) {
0442                             dragHelper.startDrag(kicker, item.url, item.icon,
0443                             "text/x-plasmoidservicename", item.m.pluginName);
0444                         } else {
0445                             dragHelper.startDrag(kicker, item.url, item.icon);
0446                         }
0447 
0448                         kicker.dragSource = item;
0449 
0450                         pressX = -1;
0451                         pressY = -1;
0452                     }
0453                 }
0454             }
0455         }
0456     }
0457 }