shift-shell/containments/homescreens/folio/qml/private/WidgetResizeHandleFrame.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

242 lines
8.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 QtQuick.Controls as QQC2
import Qt5Compat.GraphicalEffects
import org.kde.kirigami as Kirigami
import org.kde.plasma.private.mobileshell as MobileShell
import plasma.applet.org.kde.plasma.mobile.homescreen.folio as Folio
import '../delegate'
Item {
id: root
property Folio.HomeScreen folio
// given by parent:
property real widgetWidth
property real widgetHeight
property real widgetX
property real widgetY
property real widgetTopMargin
property real widgetBottomMargin
property real widgetLeftMargin
property real widgetRightMargin
property int widgetRow
property int widgetColumn
property int widgetGridWidth
property int widgetGridHeight
// filled here, given to parent:
// what the drag intends for the dimensions and position of the widget
property int widgetRowAfterDrag: 0
property int widgetColumnAfterDrag: 0
property int widgetGridWidthAfterDrag: 0
property int widgetGridHeightAfterDrag: 0
property var lockDrag: null
readonly property int resizeAnimationDuration: MobileShell.Motion.duration(MobileShell.Motion.SpatialDefault)
readonly property color resizeAccentColor: Qt.rgba(Kirigami.Theme.highlightColor.r, Kirigami.Theme.highlightColor.g, Kirigami.Theme.highlightColor.b, 0.85)
property alias handleContainer: handleContainer
signal widgetChangeAfterDrag(int widgetRow, int widgetColumn, int widgetGridWidth, int widgetGridHeight)
// solely used here:
property real startDragWidth: 0
property real startDragHeight: 0
property real startX: 0
property real startY: 0
property int startWidgetRow: 0
property int startWidgetColumn: 0
onWidgetWidthChanged: {
if (lockDrag === null) updateDimensions();
}
onWidgetHeightChanged: {
if (lockDrag === null) updateDimensions();
}
onWidgetXChanged: {
if (lockDrag === null) updateDimensions();
}
onWidgetYChanged: {
if (lockDrag === null) updateDimensions();
}
function updateDimensions() {
handleContainer.width = widgetWidth;
handleContainer.height = widgetHeight;
handleContainer.x = widgetX;
handleContainer.y = widgetY;
}
function startDrag() {
startDragWidth = handleContainer.width;
startDragHeight = handleContainer.height;
startX = handleContainer.x;
startY = handleContainer.y;
startWidgetRow = root.widgetRow;
startWidgetColumn = root.widgetColumn;
root.widgetChangeAfterDrag(startWidgetRow, startWidgetColumn, root.widgetGridWidth, root.widgetGridHeight);
}
function snapEdges() {
lockDrag = null;
// snaps the bounds to what we ended up at
widthAnim.to = widgetWidth;
widthAnim.restart();
heightAnim.to = widgetHeight;
heightAnim.restart();
xAnim.to = widgetX;
xAnim.restart();
yAnim.to = widgetY;
yAnim.restart();
}
function pressedHandler(orientation) {
if (root.lockDrag !== orientation) {
root.startDrag();
root.lockDrag = orientation;
}
}
function dragHandler(orientation, leftEdgeDelta, rightEdgeDelta, topEdgeDelta, bottomEdgeDelta) {
if (root.lockDrag === orientation) {
// update the handle container dimensions and position
handleContainer.x = root.startX - leftEdgeDelta;
handleContainer.y = root.startY - topEdgeDelta;
handleContainer.width = root.startDragWidth + rightEdgeDelta + leftEdgeDelta;
handleContainer.height = root.startDragHeight + bottomEdgeDelta + topEdgeDelta;
// update the widget dimensions and position
const columnsMovedRight = Math.round((handleContainer.x - root.startX) / folio.HomeScreenState.pageCellWidth);
const rowsMovedDown = Math.round((handleContainer.y - root.startY) / folio.HomeScreenState.pageCellHeight);
const realWidgetWidth = handleContainer.width + widgetLeftMargin + widgetRightMargin;
const realWidgetHeight = handleContainer.height + widgetTopMargin + widgetBottomMargin;
const widgetRowAfterDrag = startWidgetRow + rowsMovedDown;
const widgetColumnAfterDrag = startWidgetColumn + columnsMovedRight;
const widgetGridWidthAfterDrag = Math.round(realWidgetWidth / folio.HomeScreenState.pageCellWidth);
const widgetGridHeightAfterDrag = Math.round(realWidgetHeight / folio.HomeScreenState.pageCellHeight);
root.widgetChangeAfterDrag(widgetRowAfterDrag, widgetColumnAfterDrag, widgetGridWidthAfterDrag, widgetGridHeightAfterDrag);
}
}
function releaseHandler(orientation) {
if (root.lockDrag === orientation) {
root.snapEdges();
}
}
Item {
id: handleContainer
MobileShell.MotionNumberAnimation on width {
id: widthAnim
type: MobileShell.Motion.SpatialDefault
duration: root.resizeAnimationDuration
}
MobileShell.MotionNumberAnimation on height {
id: heightAnim
type: MobileShell.Motion.SpatialDefault
duration: root.resizeAnimationDuration
}
MobileShell.MotionNumberAnimation on x {
id: xAnim
type: MobileShell.Motion.SpatialDefault
duration: root.resizeAnimationDuration
}
MobileShell.MotionNumberAnimation on y {
id: yAnim
type: MobileShell.Motion.SpatialDefault
duration: root.resizeAnimationDuration
}
}
Rectangle {
id: resizeOutline
color: 'transparent'
border.color: root.resizeAccentColor
radius: Kirigami.Units.cornerRadius
border.width: 1
anchors.fill: handleContainer
anchors.leftMargin: -root.widgetLeftMargin
anchors.rightMargin: -root.widgetRightMargin
anchors.topMargin: -root.widgetTopMargin
anchors.bottomMargin: -root.widgetBottomMargin
}
WidgetResizeHandle {
id: topHandle
orientation: WidgetHandlePosition.TopCenter
x: resizeOutline.x + Math.round(resizeOutline.width / 2) - Math.round(width / 2)
y: resizeOutline.y - Math.round(height / 2)
width: Math.round(Math.max(height, resizeOutline.width * 0.3)) + touchPadding * 2
onPressed: pressedHandler(orientation)
onDragEvent: (leftEdgeDelta, rightEdgeDelta, topEdgeDelta, bottomEdgeDelta) => dragHandler(orientation, leftEdgeDelta, rightEdgeDelta, topEdgeDelta, bottomEdgeDelta)
onReleased: releaseHandler(orientation)
}
WidgetResizeHandle {
id: leftHandle
orientation: WidgetHandlePosition.LeftCenter
x: resizeOutline.x - (width / 2)
y: resizeOutline.y + (resizeOutline.height / 2) - (height / 2)
height: Math.round(Math.max(width, resizeOutline.height * 0.3)) + touchPadding * 2
onPressed: pressedHandler(orientation)
onDragEvent: (leftEdgeDelta, rightEdgeDelta, topEdgeDelta, bottomEdgeDelta) => dragHandler(orientation, leftEdgeDelta, rightEdgeDelta, topEdgeDelta, bottomEdgeDelta)
onReleased: releaseHandler(orientation)
}
WidgetResizeHandle {
id: rightHandle
orientation: WidgetHandlePosition.RightCenter
x: resizeOutline.x + resizeOutline.width - (width / 2)
y: resizeOutline.y + (resizeOutline.height / 2) - (height / 2)
height: Math.round(Math.max(width, resizeOutline.height * 0.3)) + touchPadding * 2
onPressed: pressedHandler(orientation)
onDragEvent: (leftEdgeDelta, rightEdgeDelta, topEdgeDelta, bottomEdgeDelta) => dragHandler(orientation, leftEdgeDelta, rightEdgeDelta, topEdgeDelta, bottomEdgeDelta)
onReleased: releaseHandler(orientation)
}
WidgetResizeHandle {
id: bottomHandle
orientation: WidgetHandlePosition.BottomCenter
x: resizeOutline.x + (resizeOutline.width / 2) - (width / 2)
y: resizeOutline.y + resizeOutline.height - (height / 2)
width: Math.round(Math.max(height, resizeOutline.width * 0.3)) + touchPadding * 2
onPressed: pressedHandler(orientation)
onDragEvent: (leftEdgeDelta, rightEdgeDelta, topEdgeDelta, bottomEdgeDelta) => dragHandler(orientation, leftEdgeDelta, rightEdgeDelta, topEdgeDelta, bottomEdgeDelta)
onReleased: releaseHandler(orientation)
}
}