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: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
||||||
SPDX-FileCopyrightText: 2022 Marco Martin <mart@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
|
SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
*/
|
*/
|
||||||
|
@ -17,6 +18,8 @@ FocusScope {
|
||||||
|
|
||||||
required property QtObject clientModel
|
required property QtObject clientModel
|
||||||
required property QtObject desktop
|
required property QtObject desktop
|
||||||
|
/** @type {{int: rect}} */
|
||||||
|
required property var dndManagerStore
|
||||||
readonly property bool dragActive: heap.dragActive || dragHandler.active || xAnim.running || yAnim.running
|
readonly property bool dragActive: heap.dragActive || dragHandler.active || xAnim.running || yAnim.running
|
||||||
property real panelOpacity: 1
|
property real panelOpacity: 1
|
||||||
focus: true
|
focus: true
|
||||||
|
@ -115,6 +118,7 @@ FocusScope {
|
||||||
animationEnabled: container.animationEnabled
|
animationEnabled: container.animationEnabled
|
||||||
organized: container.organized
|
organized: container.organized
|
||||||
layout.mode: effect.layout
|
layout.mode: effect.layout
|
||||||
|
dndManagerStore: desktopView.dndManagerStore
|
||||||
model: KWinComponents.ClientFilterModel {
|
model: KWinComponents.ClientFilterModel {
|
||||||
activity: KWinComponents.Workspace.currentActivity
|
activity: KWinComponents.Workspace.currentActivity
|
||||||
desktop: desktopView.desktop
|
desktop: desktopView.desktop
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
/*
|
/*
|
||||||
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
||||||
SPDX-FileCopyrightText: 2022 Marco Martin <mart@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
|
SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
*/
|
*/
|
||||||
|
@ -23,6 +24,11 @@ Rectangle {
|
||||||
property bool animationEnabled: false
|
property bool animationEnabled: false
|
||||||
property bool organized: 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"
|
color: "black"
|
||||||
|
|
||||||
function start() {
|
function start() {
|
||||||
|
@ -261,6 +267,7 @@ Rectangle {
|
||||||
height: container.height
|
height: container.height
|
||||||
|
|
||||||
clientModel: stackModel
|
clientModel: stackModel
|
||||||
|
dndManagerStore: container.dndManagerStore
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
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
|
SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
*/
|
*/
|
||||||
|
@ -49,6 +50,13 @@ FocusScope {
|
||||||
activated();
|
activated();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @type {{client.internalId: rect}} */
|
||||||
|
property var dndManagerStore: ({})
|
||||||
|
|
||||||
|
function saveDND(key: int, rect: rect) {
|
||||||
|
dndManagerStore[key] = rect;
|
||||||
|
}
|
||||||
|
|
||||||
KWinComponents.WindowThumbnailItem {
|
KWinComponents.WindowThumbnailItem {
|
||||||
id: otherScreenThumbnail
|
id: otherScreenThumbnail
|
||||||
z: 2
|
z: 2
|
||||||
|
@ -109,6 +117,22 @@ FocusScope {
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
id: windowsRepeater
|
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 {
|
delegate: WindowHeapDelegate {
|
||||||
windowHeap: heap
|
windowHeap: heap
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
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
|
SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
*/
|
*/
|
||||||
|
@ -39,6 +40,9 @@ Item {
|
||||||
// Show a text label under this thumbnail
|
// Show a text label under this thumbnail
|
||||||
property bool windowTitleVisible: true
|
property bool windowTitleVisible: true
|
||||||
|
|
||||||
|
// Same as for window heap
|
||||||
|
property bool animationEnabled: false
|
||||||
|
|
||||||
//scale up and down the whole thumbnail without affecting layouting
|
//scale up and down the whole thumbnail without affecting layouting
|
||||||
property real targetScale: 1.0
|
property real targetScale: 1.0
|
||||||
|
|
||||||
|
@ -73,11 +77,15 @@ Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
visible: opacity > 0
|
visible: opacity > 0
|
||||||
z: activeDragHandler.active ? 1000
|
z: (activeDragHandler.active || returning.running) ? 1000
|
||||||
: client.stackingOrder * (presentOnCurrentDesktop ? 1 : 0.001)
|
: client.stackingOrder * (presentOnCurrentDesktop ? 1 : 0.001)
|
||||||
|
|
||||||
|
function restoreDND(oldGlobalRect: rect) {
|
||||||
|
thumbSource.restoreDND(oldGlobalRect);
|
||||||
|
}
|
||||||
|
|
||||||
component TweenBehavior : Behavior {
|
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 {
|
NumberAnimation {
|
||||||
duration: thumb.windowHeap.animationDuration
|
duration: thumb.windowHeap.animationDuration
|
||||||
easing.type: Easing.OutCubic
|
easing.type: Easing.OutCubic
|
||||||
|
@ -92,9 +100,7 @@ Item {
|
||||||
KWinComponents.WindowThumbnailItem {
|
KWinComponents.WindowThumbnailItem {
|
||||||
id: thumbSource
|
id: thumbSource
|
||||||
wId: thumb.client.internalId
|
wId: thumb.client.internalId
|
||||||
state: thumb.activeDragHandler.active ? "drag" : "normal"
|
|
||||||
|
|
||||||
Drag.active: thumb.activeDragHandler.active
|
|
||||||
Drag.proposedAction: Qt.MoveAction
|
Drag.proposedAction: Qt.MoveAction
|
||||||
Drag.supportedActions: Qt.MoveAction
|
Drag.supportedActions: Qt.MoveAction
|
||||||
Drag.source: thumb.client
|
Drag.source: thumb.client
|
||||||
|
@ -105,6 +111,23 @@ Item {
|
||||||
onXChanged: effect.checkItemDraggedOutOfScreen(thumbSource)
|
onXChanged: effect.checkItemDraggedOutOfScreen(thumbSource)
|
||||||
onYChanged: 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: [
|
states: [
|
||||||
State {
|
State {
|
||||||
name: "normal"
|
name: "normal"
|
||||||
|
@ -116,6 +139,14 @@ Item {
|
||||||
height: thumb.height
|
height: thumb.height
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
State {
|
||||||
|
name: "pressed"
|
||||||
|
PropertyChanges {
|
||||||
|
target: thumbSource
|
||||||
|
width: thumb.width
|
||||||
|
height: thumb.height
|
||||||
|
}
|
||||||
|
},
|
||||||
State {
|
State {
|
||||||
name: "drag"
|
name: "drag"
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
|
@ -124,17 +155,25 @@ Item {
|
||||||
thumb.activeDragHandler.centroid.position.x
|
thumb.activeDragHandler.centroid.position.x
|
||||||
y: -thumb.activeDragHandler.centroid.pressPosition.y * thumb.targetScale +
|
y: -thumb.activeDragHandler.centroid.pressPosition.y * thumb.targetScale +
|
||||||
thumb.activeDragHandler.centroid.position.y
|
thumb.activeDragHandler.centroid.position.y
|
||||||
width: cell.width * thumb.targetScale
|
width: thumb.width * thumb.targetScale
|
||||||
height: cell.height * thumb.targetScale
|
height: thumb.height * thumb.targetScale
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: "reparenting"
|
||||||
|
PropertyChanges {
|
||||||
|
target: thumbSource
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
transitions: Transition {
|
transitions: Transition {
|
||||||
|
id: returning
|
||||||
|
from: "drag,reparenting"
|
||||||
to: "normal"
|
to: "normal"
|
||||||
enabled: thumb.windowHeap.animationEnabled
|
enabled: thumb.windowHeap.animationEnabled
|
||||||
NumberAnimation {
|
NumberAnimation {
|
||||||
duration: thumb.windowHeap.animationDuration
|
duration: thumb.windowHeap.animationDuration
|
||||||
properties: "x, y, width, height, opacity"
|
properties: "x, y, width, height"
|
||||||
easing.type: Easing.OutCubic
|
easing.type: Easing.OutCubic
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -316,25 +355,52 @@ Item {
|
||||||
onTapped: {
|
onTapped: {
|
||||||
thumb.windowHeap.windowClicked(thumb.client, eventPoint)
|
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 {
|
component DragManager : DragHandler {
|
||||||
target: null
|
target: null
|
||||||
|
dragThreshold: 0
|
||||||
grabPermissions: PointerHandler.CanTakeOverFromAnything
|
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: {
|
onActiveChanged: {
|
||||||
thumb.windowHeap.dragActive = active;
|
thumb.windowHeap.dragActive = active;
|
||||||
if (active) {
|
if (active) {
|
||||||
thumb.activeDragHandler = this;
|
thumb.activeDragHandler = this;
|
||||||
|
thumbSource.state = "drag";
|
||||||
} else {
|
} else {
|
||||||
|
thumbSource.saveDND();
|
||||||
|
|
||||||
var action = thumbSource.Drag.drop();
|
var action = thumbSource.Drag.drop();
|
||||||
if (action === Qt.MoveAction) {
|
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).
|
// 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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var globalPos = targetScreen.mapToGlobal(centroid.scenePosition);
|
var globalPos = targetScreen.mapToGlobal(centroid.scenePosition);
|
||||||
effect.checkItemDroppedOutOfScreen(globalPos, thumbSource);
|
effect.checkItemDroppedOutOfScreen(globalPos, thumbSource);
|
||||||
|
|
||||||
|
// else, return to normal without reparenting
|
||||||
|
thumbSource.state = "normal";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue