Warning, /plasma/plasma-mobile/containments/homescreens/halcyon/package/contents/ui/FavoritesGrid.qml is written in an unsupported language. File is not indexed.

0001 // SPDX-FileCopyrightText: 2022 Devin Lin <devin@kde.org>
0002 // SPDX-License-Identifier: LGPL-2.0-or-later
0003 
0004 import QtQuick
0005 import QtQuick.Controls as QQC2
0006 import QtQuick.Layouts
0007 import QtQml.Models
0008 
0009 import org.kde.plasma.components 3.0 as PC3
0010 import org.kde.draganddrop as DragDrop
0011 
0012 import org.kde.kirigami as Kirigami
0013 import org.kde.plasma.private.mobileshell as MobileShell
0014 import org.kde.private.mobile.homescreen.halcyon as Halcyon
0015 
0016 MobileShell.GridView {
0017     id: root
0018     required property var searchWidget
0019     
0020     // don't set anchors.margins since we want everywhere to be draggable
0021     required property bool twoColumn
0022     
0023     signal openConfigureRequested()
0024     signal requestOpenFolder(Halcyon.ApplicationFolder folder)
0025 
0026     // search widget open gesture
0027     property bool openingSearchWidget: false
0028     property bool canOpenSearchWidget: false
0029     property real oldVerticalOvershoot: verticalOvershoot
0030     
0031     onVerticalOvershootChanged: {
0032         if (dragging && canOpenSearchWidget && verticalOvershoot < 0) {
0033             if (!openingSearchWidget) {
0034                 if (oldVerticalOvershoot === 0) {
0035                     openingSearchWidget = true;
0036                     root.searchWidget.startGesture();
0037                 }
0038             } else {
0039                 let offset = -(verticalOvershoot - oldVerticalOvershoot);
0040                 root.searchWidget.updateGestureOffset(-offset);
0041             }
0042         }
0043         oldVerticalOvershoot = verticalOvershoot;
0044     }
0045     onDraggingChanged: {
0046         if (dragging) {
0047             canOpenSearchWidget = root.contentY <= 0;
0048         } else if (!dragging && openingSearchWidget) {
0049             openingSearchWidget = false;
0050             root.searchWidget.endGesture();
0051         }
0052     }
0053 
0054     // open wallpaper menu when held on click
0055     TapHandler {
0056         onLongPressed: root.openConfigureRequested()
0057     }
0058     
0059     header: MobileShell.BaseItem {
0060         topPadding: Math.round(root.height * 0.2)
0061         bottomPadding: Kirigami.Units.gridUnit
0062         // leftPadding: root.leftMargin
0063         // rightPadding: root.rightMargin
0064         implicitWidth: root.width
0065 
0066         background: Rectangle {
0067             color: 'transparent'
0068             TapHandler { onLongPressed: root.openConfigureRequested() } // open wallpaper menu when held on click
0069         }
0070         contentItem: Clock {}
0071     }
0072     
0073     Keys.onReturnPressed: currentItem.appDelegate.launch()
0074     model: DelegateModel {
0075         id: visualModel
0076         model: Halcyon.PinnedModel
0077         
0078         delegate: Item {
0079             id: delegateRoot
0080             property int visualIndex: DelegateModel.itemsIndex
0081             property alias appDelegate: appDelegate
0082 
0083             width: root.cellWidth
0084             height: root.cellHeight
0085             
0086             function moveDragToCurrentPos(from, to) {
0087                 if (from !== to) {
0088                     visualModel.items.move(from, to);
0089                     Halcyon.PinnedModel.moveEntry(from, to);
0090                 }
0091             }
0092             
0093             function topDragEnter(drag) {
0094                 if (transitionAnim.running || appDelegate.drag.active) return; // don't do anything when reordering
0095                     
0096                 let fromIndex = drag.source.visualIndex;
0097                 let delegateVisualIndex = appDelegate.visualIndex;
0098                 let reorderIndex = -1;
0099                 
0100                 if (fromIndex < delegateVisualIndex) { // dragged item from above
0101                     // move to spot above
0102                     reorderIndex = delegateVisualIndex - (root.twoColumn ? 2 : 1);
0103                 } else { // dragged item from below
0104                     // move to current spot
0105                     reorderIndex = delegateVisualIndex;
0106                 }
0107                 
0108                 if (reorderIndex >= 0 && reorderIndex < root.count) {
0109                     delegateRoot.moveDragToCurrentPos(fromIndex, reorderIndex)
0110                 }
0111             }
0112             
0113             function bottomDragEnter(drag) {
0114                 if (transitionAnim.running || appDelegate.drag.active) return; // don't do anything when reordering
0115                 
0116                 let fromIndex = drag.source.visualIndex;
0117                 let delegateVisualIndex = appDelegate.visualIndex;
0118                 let reorderIndex = -1;
0119                 
0120                 if (fromIndex < delegateVisualIndex) { // dragged item from above
0121                     // move to current spot
0122                     reorderIndex = delegateVisualIndex;
0123                 } else { // dragged item from below
0124                     // move to spot below
0125                     reorderIndex = delegateVisualIndex + (root.twoColumn ? 2 : 1);
0126                 }
0127                 
0128                 if (reorderIndex >= 0 && reorderIndex < root.count) {
0129                     delegateRoot.moveDragToCurrentPos(fromIndex, reorderIndex);
0130                 }
0131             }
0132 
0133             // top drop area
0134             DropArea {
0135                 id: topDropArea
0136                 anchors.top: parent.top
0137                 anchors.left: leftDropArea.right
0138                 anchors.right: rightDropArea.left
0139                 height: delegateRoot.height * 0.2
0140                 onEntered: (drag) => delegateRoot.topDragEnter(drag)
0141             }
0142             
0143             // bottom drop area
0144             DropArea {
0145                 id: bottomDropArea
0146                 anchors.bottom: parent.bottom
0147                 anchors.left: leftDropArea.right
0148                 anchors.right: rightDropArea.left
0149                 height: delegateRoot.height * 0.2
0150                 onEntered: (drag) => delegateRoot.bottomDragEnter(drag)
0151             }
0152             
0153             // left drop area
0154             DropArea {
0155                 id: leftDropArea
0156                 anchors.bottom: parent.bottom
0157                 anchors.top: parent.top
0158                 anchors.left: parent.left
0159                 width: root.twoColumn ? Math.max(appDelegate.leftPadding, delegateRoot.width * 0.1) : 0
0160                 onEntered: (drag) => delegateRoot.topDragEnter(drag)
0161             }
0162             
0163             // right drop area
0164             DropArea {
0165                 id: rightDropArea
0166                 anchors.bottom: parent.bottom
0167                 anchors.top: parent.top
0168                 anchors.right: parent.right
0169                 width: root.twoColumn ? Math.max(appDelegate.rightPadding, delegateRoot.width * 0.1) : 0
0170                 onEntered: (drag) => delegateRoot.bottomDragEnter(drag)
0171             }
0172             
0173             // folder drop area
0174             DropArea {
0175                 anchors.top: topDropArea.bottom
0176                 anchors.bottom: bottomDropArea.top
0177                 anchors.left: leftDropArea.right
0178                 anchors.right: rightDropArea.left
0179                 onEntered: (drag) => {
0180                     if (transitionAnim.running || appDelegate.drag.active || drag.source.isFolder) return; // don't do anything when reordering
0181                     folderAnim.to = 1;
0182                     folderAnim.restart();
0183                 }
0184                 onExited: () => {
0185                     folderAnim.to = 0;
0186                     folderAnim.restart();
0187                 }
0188                 onDropped: (drop) => {
0189                     if (transitionAnim.running || appDelegate.drag.active || drag.source.isFolder) return; // don't do anything when reordering
0190                     if (appDelegate.isFolder) {
0191                         Halcyon.PinnedModel.addAppToFolder(drop.source.visualIndex, appDelegate.visualIndex);
0192                     } else {
0193                         Halcyon.PinnedModel.createFolderFromApps(drop.source.visualIndex, appDelegate.visualIndex);
0194                     }
0195                     folderAnim.to = 0;
0196                     folderAnim.restart();
0197                 }
0198                 
0199                 NumberAnimation {
0200                     id: folderAnim
0201                     target: appDelegate
0202                     properties: "dragFolderAnimationProgress"
0203                     duration: 100
0204                 }
0205             }
0206             
0207             // actual visual delegate
0208             FavoritesAppDelegate {
0209                 id: appDelegate
0210                 visualIndex: delegateRoot.visualIndex
0211                 
0212                 isFolder: model.isFolder
0213                 folder: model.folder
0214                 application: model.application
0215                 
0216                 onFolderOpenRequested: root.requestOpenFolder(model.folder)
0217                 
0218                 menuActions: [
0219                     Kirigami.Action {
0220                         icon.name: "emblem-favorite"
0221                         text: i18n("Remove from favourites")
0222                         onTriggered: Halcyon.PinnedModel.removeEntry(model.index)
0223                     }
0224                 ]
0225                 
0226                 implicitWidth: root.cellWidth
0227                 implicitHeight: visible ? root.cellHeight : 0
0228                 
0229                 anchors.horizontalCenter: parent.horizontalCenter
0230                 anchors.verticalCenter: parent.verticalCenter
0231                 
0232                 states: [
0233                     State {
0234                         when: appDelegate.drag.active
0235                         ParentChange {
0236                             target: appDelegate
0237                             parent: root
0238                         }
0239                         
0240                         AnchorChanges {
0241                             target: appDelegate
0242                             anchors.horizontalCenter: undefined
0243                             anchors.verticalCenter: undefined
0244                         }
0245                     }
0246                 ]
0247             }
0248         }
0249     }
0250     
0251     // animations
0252     displaced: Transition {
0253         NumberAnimation {
0254             id: transitionAnim
0255             properties: "x,y"
0256             easing.type: Easing.OutQuad
0257         }
0258     }
0259     
0260     ColumnLayout {
0261         id: placeholder
0262         spacing: Kirigami.Units.gridUnit
0263         visible: root.count == 0
0264         opacity: 0.9
0265         
0266         anchors.fill: parent
0267         anchors.topMargin: Math.round(swipeView.height * 0.2) - (root.contentY - root.originY)
0268         anchors.leftMargin: root.leftMargin
0269         anchors.rightMargin: root.rightMargin
0270         
0271         layer.enabled: true
0272         layer.effect: MobileShell.TextDropShadow {}
0273         
0274         Kirigami.Icon {
0275             id: icon
0276             Layout.alignment: Qt.AlignBottom | Qt.AlignHCenter
0277             implicitWidth: Kirigami.Units.iconSizes.large
0278             implicitHeight: width
0279             source: "arrow-left"
0280             color: "white"
0281         }
0282         
0283         Kirigami.Heading {
0284             Layout.fillWidth: true
0285             Layout.maximumWidth: placeholder.width * 0.75
0286             Layout.alignment: Qt.AlignTop | Qt.AlignHCenter
0287             color: "white"
0288             level: 3
0289             wrapMode: Text.Wrap
0290             horizontalAlignment: Text.AlignHCenter
0291             text: i18n("Add applications to your favourites so they show up here.")
0292         }
0293         
0294         TapHandler {
0295             onLongPressed: root.openConfigureRequested()
0296         }
0297     }
0298 }