shift-shell/containments/homescreens/folio/qml/DelegateDragItem.qml
Marco Allegretti a37734b74a Move homescreens to shared motion
Use Motion wrappers and state layers across Folio and Halcyon delegates, drawers, folders, resize handles, and dock feedback. Also align Folio edit/drop highlights with theme colors instead of fixed white overlays.
2026-05-21 11:13:36 +02:00

192 lines
6.5 KiB
QML

// SPDX-FileCopyrightText: 2023 Devin Lin <devin@kde.org>
// SPDX-License-Identifier: LGPL-2.0-or-later
import QtQuick
import QtQuick.Layouts
import org.kde.kirigami as Kirigami
import plasma.applet.org.kde.plasma.mobile.homescreen.folio as Folio
import org.kde.plasma.private.mobileshell as MobileShell
import "./delegate"
// Placeholder item that the user sees as they drag app/folder delegates around.
// See WidgetDragItem for the equivalent for widgets.
Item {
id: root
property Folio.HomeScreen folio
property MobileShell.MaskManager maskManager
property Folio.FolioDelegate delegate
width: folio.HomeScreenState.pageCellWidth
height: folio.HomeScreenState.pageCellHeight
readonly property real dropAnimationRunning: dragXAnim.running || dragYAnim.running
readonly property int dropAnimationDuration: MobileShell.Motion.duration(MobileShell.Motion.EffectsDefault)
// ignore widget dragging, that is not handled by this component
readonly property bool isWidgetDrag: folio.HomeScreenState.dragState.dropDelegate && folio.HomeScreenState.dragState.dropDelegate.type === Folio.FolioDelegate.Widget
visible: false
x: folio.HomeScreenState.delegateDragX
y: folio.HomeScreenState.delegateDragY
function setXBinding() {
x = Qt.binding(() => folio.HomeScreenState.delegateDragX);
}
function setYBinding() {
y = Qt.binding(() => folio.HomeScreenState.delegateDragY);
}
// animate drop x
MobileShell.MotionNumberAnimation on x {
id: dragXAnim
type: MobileShell.Motion.EffectsDefault
running: false
duration: root.dropAnimationDuration
onFinished: {
root.visible = false;
root.setXBinding();
}
}
// animate drop y
MobileShell.MotionNumberAnimation on y {
id: dragYAnim
type: MobileShell.Motion.EffectsDefault
running: false
duration: root.dropAnimationDuration
onFinished: {
root.visible = false;
root.setYBinding();
}
}
// animate scale if it's an app being placed into a folder
ScaleAnimator on scale {
id: scaleAnim
to: 0
running: false
duration: root.dropAnimationDuration
easing.type: MobileShell.Motion.easing(MobileShell.Motion.EffectsDefault)
}
Connections {
id: stateWatcher
target: folio.HomeScreenState
property var delegateDroppedOn: null
// reset and show drag item
function onDelegateDragStarted() {
if (isWidgetDrag) {
return;
}
root.scale = 1.0;
root.visible = true;
}
// save the existing delegate at the spot (this is called before the delegate is dropped)
function onDelegateDragDropped() {
if (root.isWidgetDrag) {
return;
}
let dragState = folio.HomeScreenState.dragState;
let dropPosition = dragState.candidateDropPosition;
switch (dropPosition.location) {
case Folio.DelegateDragPosition.Pages:
stateWatcher.delegateDroppedOn = folio.HomeScreenState.getPageDelegateAt(dropPosition.page, dropPosition.pageRow, dropPosition.pageColumn);
break;
case Folio.DelegateDragPosition.Favourites:
stateWatcher.delegateDroppedOn = folio.HomeScreenState.getFavouritesDelegateAt(dropPosition.favouritesPosition);
break;
case Folio.DelegateDragPosition.Folder:
stateWatcher.delegateDroppedOn = null;
break;
}
}
}
Connections {
target: folio.HomeScreenState.dragState
// animate from when the delegate is dropped to its drop position
function onDelegateDroppedAndPlaced() {
if (root.isWidgetDrag) {
return;
}
let dragState = folio.HomeScreenState.dragState;
let dropPosition = dragState.candidateDropPosition;
let pos = null;
switch (dropPosition.location) {
case Folio.DelegateDragPosition.Pages:
pos = folio.HomeScreenState.getPageDelegateScreenPosition(dropPosition.page, dropPosition.pageRow, dropPosition.pageColumn);
break;
case Folio.DelegateDragPosition.Favourites:
pos = folio.HomeScreenState.getFavouritesDelegateScreenPosition(dropPosition.favouritesPosition);
break;
case Folio.DelegateDragPosition.Folder:
pos = folio.HomeScreenState.getFolderDelegateScreenPosition(dropPosition.folderPosition);
break;
}
dragXAnim.to = pos.x;
dragYAnim.to = pos.y;
dragXAnim.restart();
dragYAnim.restart();
if (stateWatcher.delegateDroppedOn &&
stateWatcher.delegateDroppedOn.type != Folio.FolioDelegate.None &&
dragState.dropDelegate.type === Folio.FolioDelegate.Application) {
// scale animation if we are creating, or inserting into a folder
scaleAnim.restart();
}
}
// if the drop has been abandoned, just hide
function onNewDelegateDropAbandoned() {
root.visible = false;
}
}
// simulate an icon delegate
ColumnLayout {
anchors.fill: parent
spacing: 0
// icon
DelegateIconLoader {
id: loader
folio: root.folio
maskManager: root.maskManager
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
Layout.minimumWidth: folio.FolioSettings.delegateIconSize
Layout.minimumHeight: folio.FolioSettings.delegateIconSize
Layout.preferredHeight: Layout.minimumHeight
delegate: root.delegate
layer.enabled: true
layer.effect: DelegateShadow {}
}
// simulate the delegate label for positioning purposes
DelegateLabel {
id: label
opacity: 0
Layout.fillWidth: true
Layout.preferredHeight: folio.HomeScreenState.pageDelegateLabelHeight
Layout.topMargin: folio.HomeScreenState.pageDelegateLabelSpacing
Layout.leftMargin: -parent.anchors.leftMargin + Kirigami.Units.smallSpacing
Layout.rightMargin: -parent.anchors.rightMargin + Kirigami.Units.smallSpacing
}
}
}