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:
parent
9b282672a2
commit
b01ea99c01
4 changed files with 109 additions and 8 deletions
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue