effects/desktopgrid: Restore position correctly when dropping a window

This required a bit of a magic on the WindowHeap side to store and
restore global position of a WindowHeapDelegates' window thumbnails.

An additional property bool animationEnabled on a delegate level enables
the heap to restore position without playing unneeded initial animation,
just like the heap itself.

Windows that are being dragged or already returning form a drop are
positioned higher than others on a z-stack.

BUG: 453995
This commit is contained in:
ivan tkachenko 2022-09-13 01:32:35 +03:00
parent 9b282672a2
commit b01ea99c01
No known key found for this signature in database
GPG key ID: AF72731B7C654CB3
4 changed files with 109 additions and 8 deletions

View file

@ -1,6 +1,7 @@
/*
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-FileCopyrightText: 2022 Marco Martin <mart@kde.org>
SPDX-FileCopyrightText: 2022 ivan tkachenko <me@ratijas.tk>
SPDX-License-Identifier: GPL-2.0-or-later
*/
@ -17,6 +18,8 @@ FocusScope {
required property QtObject clientModel
required property QtObject desktop
/** @type {{int: rect}} */
required property var dndManagerStore
readonly property bool dragActive: heap.dragActive || dragHandler.active || xAnim.running || yAnim.running
property real panelOpacity: 1
focus: true
@ -115,6 +118,7 @@ FocusScope {
animationEnabled: container.animationEnabled
organized: container.organized
layout.mode: effect.layout
dndManagerStore: desktopView.dndManagerStore
model: KWinComponents.ClientFilterModel {
activity: KWinComponents.Workspace.currentActivity
desktop: desktopView.desktop

View file

@ -1,6 +1,7 @@
/*
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-FileCopyrightText: 2022 Marco Martin <mart@kde.org>
SPDX-FileCopyrightText: 2022 ivan tkachenko <me@ratijas.tk>
SPDX-License-Identifier: GPL-2.0-or-later
*/
@ -23,6 +24,11 @@ Rectangle {
property bool animationEnabled: false
property bool organized: false
/** Shared Drag&Drop store to keep track of DND state across desktops.
* @type {{client.internalId: rect}}
*/
property var dndManagerStore: ({})
color: "black"
function start() {
@ -261,6 +267,7 @@ Rectangle {
height: container.height
clientModel: stackModel
dndManagerStore: container.dndManagerStore
Rectangle {
anchors.fill: parent
color: "transparent"

View file

@ -1,5 +1,6 @@
/*
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-FileCopyrightText: 2022 ivan tkachenko <me@ratijas.tk>
SPDX-License-Identifier: GPL-2.0-or-later
*/
@ -49,6 +50,13 @@ FocusScope {
activated();
}
/** @type {{client.internalId: rect}} */
property var dndManagerStore: ({})
function saveDND(key: int, rect: rect) {
dndManagerStore[key] = rect;
}
KWinComponents.WindowThumbnailItem {
id: otherScreenThumbnail
z: 2
@ -109,6 +117,22 @@ FocusScope {
Repeater {
id: windowsRepeater
onItemAdded: (index, item) => {
// restore/reparent from drop
var key = item.client.internalId;
if (key in heap.dndManagerStore) {
expoLayout.forceLayout();
var oldGlobalRect = heap.dndManagerStore[key];
item.restoreDND(oldGlobalRect);
delete heap.dndManagerStore[key];
} else if (heap.effectiveOrganized) {
// New window has opened in the middle of a running effect.
// Make sure it is positioned before enabling its animations.
expoLayout.forceLayout();
}
item.animationEnabled = true;
}
delegate: WindowHeapDelegate {
windowHeap: heap
}

View file

@ -1,5 +1,6 @@
/*
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-FileCopyrightText: 2022 ivan tkachenko <me@ratijas.tk>
SPDX-License-Identifier: GPL-2.0-or-later
*/
@ -39,6 +40,9 @@ Item {
// Show a text label under this thumbnail
property bool windowTitleVisible: true
// Same as for window heap
property bool animationEnabled: false
//scale up and down the whole thumbnail without affecting layouting
property real targetScale: 1.0
@ -73,11 +77,15 @@ Item {
}
visible: opacity > 0
z: activeDragHandler.active ? 1000
z: (activeDragHandler.active || returning.running) ? 1000
: client.stackingOrder * (presentOnCurrentDesktop ? 1 : 0.001)
function restoreDND(oldGlobalRect: rect) {
thumbSource.restoreDND(oldGlobalRect);
}
component TweenBehavior : Behavior {
enabled: thumb.state !== "partial" && thumb.windowHeap.animationEnabled && !thumb.activeDragHandler.active
enabled: thumb.state !== "partial" && thumb.windowHeap.animationEnabled && thumb.animationEnabled && !thumb.activeDragHandler.active
NumberAnimation {
duration: thumb.windowHeap.animationDuration
easing.type: Easing.OutCubic
@ -92,9 +100,7 @@ Item {
KWinComponents.WindowThumbnailItem {
id: thumbSource
wId: thumb.client.internalId
state: thumb.activeDragHandler.active ? "drag" : "normal"
Drag.active: thumb.activeDragHandler.active
Drag.proposedAction: Qt.MoveAction
Drag.supportedActions: Qt.MoveAction
Drag.source: thumb.client
@ -105,6 +111,23 @@ Item {
onXChanged: effect.checkItemDraggedOutOfScreen(thumbSource)
onYChanged: effect.checkItemDraggedOutOfScreen(thumbSource)
state: "normal"
function saveDND() {
const oldGlobalRect = mapToItem(null, 0, 0, width, height);
thumb.windowHeap.saveDND(thumb.client.internalId, oldGlobalRect);
}
function restoreDND(oldGlobalRect: rect) {
state = "reparenting";
const newGlobalRect = mapFromItem(null, oldGlobalRect);
x = newGlobalRect.x;
y = newGlobalRect.y;
width = newGlobalRect.width;
height = newGlobalRect.height;
state = "normal";
}
states: [
State {
name: "normal"
@ -116,6 +139,14 @@ Item {
height: thumb.height
}
},
State {
name: "pressed"
PropertyChanges {
target: thumbSource
width: thumb.width
height: thumb.height
}
},
State {
name: "drag"
PropertyChanges {
@ -124,17 +155,25 @@ Item {
thumb.activeDragHandler.centroid.position.x
y: -thumb.activeDragHandler.centroid.pressPosition.y * thumb.targetScale +
thumb.activeDragHandler.centroid.position.y
width: cell.width * thumb.targetScale
height: cell.height * thumb.targetScale
width: thumb.width * thumb.targetScale
height: thumb.height * thumb.targetScale
}
},
State {
name: "reparenting"
PropertyChanges {
target: thumbSource
}
}
]
transitions: Transition {
id: returning
from: "drag,reparenting"
to: "normal"
enabled: thumb.windowHeap.animationEnabled
NumberAnimation {
duration: thumb.windowHeap.animationDuration
properties: "x, y, width, height, opacity"
properties: "x, y, width, height"
easing.type: Easing.OutCubic
}
}
@ -316,25 +355,52 @@ Item {
onTapped: {
thumb.windowHeap.windowClicked(thumb.client, eventPoint)
}
onPressedChanged: {
if (pressed) {
var saved = Qt.point(thumbSource.x, thumbSource.y);
thumbSource.Drag.active = true;
thumbSource.state = "pressed";
thumbSource.x = saved.x;
thumbSource.y = saved.y;
} else if (!thumb.activeDragHandler.active) {
thumbSource.Drag.active = false;
thumbSource.state = "normal";
}
}
}
component DragManager : DragHandler {
target: null
dragThreshold: 0
grabPermissions: PointerHandler.CanTakeOverFromAnything
// This does not work when moving pointer fast and pressing along the way
// See also QTBUG-105903, QTBUG-105904
// enabled: thumbSource.state !== "normal"
onActiveChanged: {
thumb.windowHeap.dragActive = active;
if (active) {
thumb.activeDragHandler = this;
thumbSource.state = "drag";
} else {
thumbSource.saveDND();
var action = thumbSource.Drag.drop();
if (action === Qt.MoveAction) {
// this whole component is in the process of being destroyed due to drop onto
// This whole component is in the process of being destroyed due to drop onto
// another virtual desktop (not another screen).
if (typeof thumbSource !== "undefined") {
// Except the case when it was dropped on the same desktop which it's already on, so let's return to normal state anyway.
thumbSource.state = "normal";
}
return;
}
var globalPos = targetScreen.mapToGlobal(centroid.scenePosition);
effect.checkItemDroppedOutOfScreen(globalPos, thumbSource);
// else, return to normal without reparenting
thumbSource.state = "normal";
}
}
}