Commit 3d0beae8 authored by Marco Martin's avatar Marco Martin
Browse files

Drag windows between screens in WindowHeap

Allow dragging windows between screens, using a placeholder thumbnail item to
render part of the thumbnail on the other screen.

BUG: 283333
BUG: 453996
BUG: 448566
parent 6abce152
Pipeline #186019 passed with stage
in 16 minutes and 22 seconds
......@@ -34,6 +34,19 @@ DropArea {
drag.source.desktop = desktopView.desktop.x11DesktopNumber;
}
}
Connections {
target: effect
function onItemDroppedOutOfScreen(globalPos, item, screen) {
if (screen != targetScreen) {
return;
}
const pos = screen.mapFromGlobal(globalPos);
if (!desktopView.contains(desktopView.mapFromItem(null, pos.x, pos.y))) {
return;
}
item.client.desktop = desktopView.desktop.x11DesktopNumber;
}
}
Repeater {
model: KWinComponents.ClientFilterModel {
activity: KWinComponents.Workspace.currentActivity
......
......@@ -5,6 +5,7 @@
*/
import QtQuick 2.12
import QtQuick.Window 2.12
import org.kde.kirigami 2.12 as Kirigami
import org.kde.kwin 3.0 as KWinComponents
import org.kde.kwin.private.effects 1.0
......@@ -29,7 +30,6 @@ FocusScope {
property int selectedIndex: -1
property int animationDuration: PlasmaCore.Units.longDuration
property bool animationEnabled: false
property bool dragEnabled: true
property bool absolutePositioning: true
property real padding: 0
property var showOnly: []
......@@ -49,6 +49,57 @@ FocusScope {
activated();
}
KWinComponents.WindowThumbnailItem {
id: otherScreenThumbnail
z: 2
property KWinComponents.WindowThumbnailItem cloneOf
visible: false
wId: cloneOf ? cloneOf.wId : null
width: cloneOf ? cloneOf.width : 0
height: cloneOf ? cloneOf.height : 0
onCloneOfChanged: {
if (!cloneOf) {
visible = false;
}
}
}
Connections {
target: effect
function onItemDraggedOutOfScreen(item, screens) {
let found = false;
// don't put a proxy for item's own screen
if (screens.length === 0 || item.screen === targetScreen) {
otherScreenThumbnail.visible = false;
return;
}
for (let i in screens) {
if (targetScreen === screens[i]) {
found = true;
let globalPos = item.screen.mapToGlobal(item.mapToItem(null, 0,0));
let heapRelativePos = targetScreen.mapFromGlobal(globalPos);
heapRelativePos = heap.mapFromItem(null, heapRelativePos.x, heapRelativePos.y);
otherScreenThumbnail.cloneOf = item
otherScreenThumbnail.x = heapRelativePos.x;
otherScreenThumbnail.y = heapRelativePos.y;
otherScreenThumbnail.visible = true;
}
}
if (!found) {
otherScreenThumbnail.visible = false;
}
}
function onItemDroppedOutOfScreen(pos, item, screen) {
if (screen === targetScreen) {
// To actually move we neeed a screen number rather than an EffectScreen
KWinComponents.Workspace.sendClientToScreen(item.client, KWinComponents.Workspace.screenAt(pos));
}
}
}
ExpoLayout {
id: expoLayout
x: heap.padding
......@@ -119,6 +170,8 @@ FocusScope {
id: thumbSource
wId: thumb.client.internalId
state: thumb.activeDragHandler.active ? "drag" : "normal"
readonly property QtObject screen: targetScreen
readonly property QtObject client: thumb.client
Drag.active: thumb.activeDragHandler.active
Drag.source: thumb.client
......@@ -126,6 +179,9 @@ FocusScope {
thumb.activeDragHandler.centroid.pressPosition.x * thumb.activeDragHandler.targetScale,
thumb.activeDragHandler.centroid.pressPosition.y * thumb.activeDragHandler.targetScale)
onXChanged: effect.checkItemDraggedOutOfScreen(thumbSource)
onYChanged: effect.checkItemDraggedOutOfScreen(thumbSource)
states: [
State {
name: "normal"
......@@ -340,7 +396,7 @@ FocusScope {
component DragManager : DragHandler {
id: dragHandler
enabled: heap.dragEnabled || heap.supportsCloseWindows
enabled: heap.supportsCloseWindows
target: null
readonly property double targetScale: {
......@@ -362,6 +418,8 @@ FocusScope {
thumb.activeDragHandler = dragHandler;
} else {
thumbSource.Drag.drop();
let globalPos = targetScreen.mapToGlobal(centroid.scenePosition);
effect.checkItemDroppedOutOfScreen(globalPos, thumbSource);
}
}
}
......@@ -370,7 +428,6 @@ FocusScope {
id: dragHandler
readonly property double targetOpacity: 1
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad | PointerDevice.Stylus
enabled: heap.dragEnabled
}
DragManager {
id: touchDragHandler
......
......@@ -105,7 +105,6 @@ Item {
padding: PlasmaCore.Units.largeSpacing
animationDuration: container.animationDuration
animationEnabled: container.animationEnabled
dragEnabled: false
organized: container.organized
showOnly: container.effect.mode === WindowView.ModeWindowClass ? "activeClass" : selectedIds
layout: effect.layout
......
......@@ -808,6 +808,16 @@ EffectScreen::EffectScreen(QObject *parent)
{
}
QPointF EffectScreen::mapToGlobal(const QPointF &pos) const
{
return pos + geometry().topLeft();
}
QPointF EffectScreen::mapFromGlobal(const QPointF &pos) const
{
return pos - geometry().topLeft();
}
//****************************************
// EffectWindow
//****************************************
......
......@@ -1980,6 +1980,9 @@ public:
*/
virtual QRect geometry() const = 0;
Q_INVOKABLE QPointF mapToGlobal(const QPointF &pos) const;
Q_INVOKABLE QPointF mapFromGlobal(const QPointF &pos) const;
/**
* Returns the screen's refresh rate in milli-hertz.
*/
......
......@@ -22,6 +22,7 @@ public:
{
return effect->d.data();
}
bool isItemOnScreen(QQuickItem *item, EffectScreen *screen);
SharedQmlEngine::Ptr qmlEngine;
QScopedPointer<QQmlComponent> qmlComponent;
......@@ -33,6 +34,25 @@ public:
EffectScreen *paintedScreen = nullptr;
};
bool QuickSceneEffectPrivate::isItemOnScreen(QQuickItem *item, EffectScreen *screen)
{
if (!item || !screen || !views.contains(screen)) {
return false;
}
auto *view = views[screen];
auto *rootItem = view->rootItem();
auto candidate = item->parentItem();
// Is there a more efficient way?
while (candidate) {
if (candidate == rootItem) {
return true;
}
candidate = candidate->parentItem();
}
return false;
}
QuickSceneView::QuickSceneView(QuickSceneEffect *effect, EffectScreen *screen)
: OffscreenQuickView(effect, QuickSceneEffectPrivate::get(effect)->dummyWindow.data())
, m_effect(effect)
......@@ -113,6 +133,35 @@ bool QuickSceneEffect::supported()
return effects->compositingType() == OpenGLCompositing;
}
void QuickSceneEffect::checkItemDraggedOutOfScreen(QQuickItem *item)
{
const QRectF globalGeom = QRectF(item->mapToGlobal(QPointF(0, 0)), QSizeF(item->width(), item->height()));
QList<EffectScreen *> screens;
for (auto it = d->views.constBegin(); it != d->views.constEnd(); ++it) {
if (!d->isItemOnScreen(item, it.key()) && it.key()->geometry().intersects(globalGeom.toRect())) {
screens << it.key();
}
}
Q_EMIT itemDraggedOutOfScreen(item, screens);
}
void QuickSceneEffect::checkItemDroppedOutOfScreen(const QPointF &globalPos, QQuickItem *item)
{
EffectScreen *screen = nullptr;
for (auto it = d->views.constBegin(); it != d->views.constEnd(); ++it) {
if (!d->isItemOnScreen(item, it.key()) && it.key()->geometry().contains(globalPos.toPoint())) {
screen = it.key();
break;
}
}
if (screen) {
Q_EMIT itemDroppedOutOfScreen(globalPos, item, screen);
}
}
bool QuickSceneEffect::eventFilter(QObject *watched, QEvent *event)
{
if (event->type() == QEvent::CursorChange) {
......
......@@ -123,6 +123,13 @@ public:
static bool supported();
Q_INVOKABLE void checkItemDraggedOutOfScreen(QQuickItem *item);
Q_INVOKABLE void checkItemDroppedOutOfScreen(const QPointF &globalPos, QQuickItem *item);
Q_SIGNALS:
void itemDraggedOutOfScreen(QQuickItem *item, QList<EffectScreen *> screens);
void itemDroppedOutOfScreen(const QPointF &globalPos, QQuickItem *item, EffectScreen *screen);
protected:
/**
* Reimplement this function to provide your initial properties for the scene view
......
......@@ -404,6 +404,11 @@ int WorkspaceWrapper::numScreens() const
return screens()->count();
}
int WorkspaceWrapper::screenAt(const QPointF &pos) const
{
return kwinApp()->platform()->enabledOutputs().indexOf(kwinApp()->platform()->outputAt(pos.toPoint()));
}
int WorkspaceWrapper::activeScreen() const
{
return kwinApp()->platform()->enabledOutputs().indexOf(workspace()->activeOutput());
......
......@@ -233,6 +233,8 @@ public:
VirtualDesktop *currentVirtualDesktop() const;
void setCurrentVirtualDesktop(VirtualDesktop *desktop);
Q_INVOKABLE int screenAt(const QPointF &pos) const;
/**
* Returns the geometry a Client can use with the specified option.
* This method should be preferred over other methods providing screen sizes as the
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment