on xenial help sign in
move windows around with Alt + LMB
commitd47a3907440391ee1228d01a19924e1b248307c3 authorLukáš Tinkl <lukas.tinkl@canonical.com> dateMon 5 Sep 2016, 12:09 (10 years ago) parentf8a0e67
7 files changed+123−13
plugins/Utils/WindowInputMonitor.cpp+28−5 plugins/Utils/WindowInputMonitor.h+9−4 qml/Shell.qml+15 qml/Stages/DecoratedWindow.qml+2−1 qml/Stages/DesktopStage.qml+48−1 qml/Stages/TabletStage.qml+17−1 tests/mocks/Utils/WindowInputMonitor.qml+4−1
plugins/Utils/WindowInputMonitor.cpp+28−5
@@ -56,14 +56,13 @@ bool WindowInputMonitor::eventFilter(QObject *watched, QEvent *event)
5656 Q_ASSERT(watched == static_cast<QObject*>(m_filteredWindow.data()));
5757 Q_UNUSED(watched);
5858
59− update(event);
60−
61− // We're only monitoring, never filtering out events
62− return false;
59+ return update(event);
6360}
6461
65−void WindowInputMonitor::update(QEvent *event)
62+bool WindowInputMonitor::update(QEvent *event)
6663{
64+ bool result = false; // by default, just monitor
65+
6766 if (event->type() == QEvent::KeyPress) {
6867 QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
6968
@@ -81,6 +80,11 @@ void WindowInputMonitor::update(QEvent *event)
8180 if (keyEvent->key() == m_pressedHomeKey) {
8281 m_pressedHomeKey = 0;
8382 }
83+ if (m_movingWindow && keyEvent->modifiers() == Qt::AltModifier) {
84+ m_movingWindow = false;
85+ result = true;
86+ Q_EMIT windowMoveEnded();
87+ }
8488
8589 } else if (event->type() == QEvent::TouchBegin) {
8690
@@ -98,7 +102,26 @@ void WindowInputMonitor::update(QEvent *event)
98102 const QPointF pos = touchEv->touchPoints().last().screenPos();
99103 Q_EMIT touchEnded(pos);
100104 }
105+ } else if (event->type() == QEvent::MouseButtonPress) {
106+ QMouseEvent * mouseEv = static_cast<QMouseEvent *>(event);
107+ if (mouseEv && mouseEv->button() == Qt::LeftButton && mouseEv->modifiers() == Qt::AltModifier) {
108+ m_movingWindow = true;
109+ result = true;
110+ Q_EMIT windowMoveRequested(mouseEv->screenPos());
111+ }
112+ } else if (event->type() == QEvent::MouseMove && m_movingWindow) {
113+ QMouseEvent * mouseEv = static_cast<QMouseEvent *>(event);
114+ if (mouseEv) {
115+ result = true;
116+ Q_EMIT windowMoveUpdated(mouseEv->screenPos());
117+ }
118+ } else if (event->type() == QEvent::MouseButtonRelease && m_movingWindow) {
119+ m_movingWindow = false;
120+ result = true;
121+ Q_EMIT windowMoveEnded();
101122 }
123+
124+ return result;
102125}
103126
104127void WindowInputMonitor::setupFilterOnWindow(QQuickWindow *window)
plugins/Utils/WindowInputMonitor.h+9−4
@@ -29,25 +29,25 @@
2929 * key presses.
3030 *
3131 * Additionally, this class monitors for generic touch events on the screen, to
32− * help with hiding/revealing the mouse pointer.
32+ * help with hiding/revealing the mouse pointer and moving window around with Alt+LMB
3333 */
3434class WindowInputMonitor : public QQuickItem
3535{
3636 Q_OBJECT
3737public:
3838
39− WindowInputMonitor(QQuickItem *parent = 0);
39+ WindowInputMonitor(QQuickItem *parent = nullptr);
4040
4141 // for testing
4242 WindowInputMonitor(UnityUtil::AbstractTimer *timer,
4343 UnityUtil::AbstractElapsedTimer *elapsedTimer,
44− QQuickItem *parent = 0);
44+ QQuickItem *parent = nullptr);
4545
4646 virtual ~WindowInputMonitor();
4747
4848 bool eventFilter(QObject *watched, QEvent *event) override;
4949
50− void update(QEvent *event);
50+ bool update(QEvent *event);
5151
5252 const qint64 msecsWithoutTouches = 150;
5353
@@ -77,6 +77,10 @@ Q_SIGNALS:
7777 */
7878 void touchEnded(const QPointF &pos);
7979
80+ void windowMoveRequested(const QPointF &scenePos);
81+ void windowMoveUpdated(const QPointF &scenePos);
82+ void windowMoveEnded();
83+
8084private Q_SLOTS:
8185 void setupFilterOnWindow(QQuickWindow *window);
8286 void emitActivatedIfNoTouchesAround();
@@ -92,6 +96,7 @@ private:
9296 // Which Qt::Key from m_homeKeys is currently pressed.
9397 // 0 if none
9498 int m_pressedHomeKey{0};
99+ bool m_movingWindow{false};
95100};
96101
97102#endif // UNITY_WINDOWINPUTMONITOR_H
qml/Shell.qml+15
@@ -260,6 +260,21 @@ StyledItem {
260260 cursor.y = mappedCoords.y;
261261 cursor.mouseNeverMoved = false;
262262 }
263+ onWindowMoveRequested: {
264+ if (applicationsDisplayLoader.item && applicationsDisplayLoader.item.windowMoveRequested) {
265+ applicationsDisplayLoader.item.windowMoveRequested(scenePos);
266+ }
267+ }
268+ onWindowMoveUpdated: {
269+ if (applicationsDisplayLoader.item && applicationsDisplayLoader.item.windowMoveUpdated) {
270+ applicationsDisplayLoader.item.windowMoveUpdated(scenePos);
271+ }
272+ }
273+ onWindowMoveEnded: {
274+ if (applicationsDisplayLoader.item && applicationsDisplayLoader.item.windowMoveEnded) {
275+ applicationsDisplayLoader.item.windowMoveEnded();
276+ }
277+ }
263278 }
264279
265280 GSettings {
qml/Stages/DecoratedWindow.qml+2−1
@@ -55,6 +55,7 @@ FocusScope {
5555 property alias stageWidth: moveHandler.stageWidth
5656 property alias stageHeight: moveHandler.stageHeight
5757 readonly property alias dragging: moveHandler.dragging
58+ readonly property alias moveHandler: moveHandler
5859
5960 readonly property Item clientAreaItem: applicationWindow
6061
@@ -118,7 +119,7 @@ FocusScope {
118119
119120 MoveHandler {
120121 id: moveHandler
121− anchors.fill: decoration
122+ anchors.fill: parent
122123 objectName: "moveHandler"
123124 target: root.parent
124125 buttonsWidth: decoration.buttonsWidth
qml/Stages/DesktopStage.qml+48−1
@@ -42,6 +42,38 @@ AbstractStage {
4242 }
4343 }
4444
45+ // Alt+LMB handlers
46+ function windowMoveRequested(scenePos) {
47+ var candidate;
48+ if (priv.focusedAppDelegate && priv.focusedAppDelegate.contains(mapToItem(priv.focusedAppDelegate, scenePos.x, scenePos.y))) {
49+ candidate = priv.focusedAppDelegate;
50+ } else {
51+ candidate = appContainer.childAt(scenePos.x, scenePos.y);
52+ }
53+
54+ if (candidate && candidate.moveHandler) {
55+ print("!!! Window move requested", scenePos);
56+ var localPos = mapToItem(candidate, scenePos.x, scenePos.y)
57+ candidate.focus = true;
58+ candidate.moveHandler.handlePressedChanged(true, Qt.LeftButton, localPos.x, localPos.y);
59+ }
60+ }
61+ function windowMoveUpdated(scenePos) {
62+ if (priv.focusedAppDelegate) {
63+ print("!!! Window move updated", scenePos);
64+ var localPos = mapToItem(priv.focusedAppDelegate, scenePos.x, scenePos.y)
65+ priv.focusedAppDelegate.moveHandler.handlePositionChanged(localPos);
66+ }
67+ }
68+ function windowMoveEnded() {
69+ if (priv.focusedAppDelegate) {
70+ print("!!! Window move ended");
71+ priv.focusedAppDelegate.moveHandler.handlePressedChanged(false, Qt.LeftButton);
72+ priv.focusedAppDelegate.moveHandler.handleReleased();
73+ fakeRectangle.commit();
74+ }
75+ }
76+
4577 // Used by TutorialRight
4678 property bool spreadShown: spread.state == "altTab"
4779
@@ -248,6 +280,20 @@ AbstractStage {
248280 source: root.background
249281 }
250282
283+ Timer { // fix a race condition between TopLevelSurfaceRepeater and app (usually dash) starting up; set a focusedAppDelegate
284+ running: !appRepeater.startingUp
285+ interval: 1
286+ onTriggered: {
287+ for (var i = 0; i < appRepeater.count; i++) {
288+ var appDelegate = appRepeater.itemAt(i);
289+ if (appDelegate && appDelegate.focus) {
290+ priv.focusedAppDelegate = appDelegate;
291+ break;
292+ }
293+ }
294+ }
295+ }
296+
251297 TopLevelSurfaceRepeater {
252298 id: appRepeater
253299 model: topLevelSurfaceList
@@ -350,7 +396,8 @@ AbstractStage {
350396 readonly property var surface: model.surface
351397 readonly property alias resizeArea: resizeArea
352398 readonly property alias focusedSurface: decoratedWindow.focusedSurface
353− readonly property bool dragging: touchControls.overlayShown ? touchControls.dragging : decoratedWindow.dragging
399+ readonly property bool dragging: moveHandler.dragging
400+ readonly property alias moveHandler: decoratedWindow.moveHandler // for Alt+LMB dragging
354401
355402 readonly property bool isDash: model.application.appId == "unity8-dash"
356403 readonly property alias clientAreaItem: decoratedWindow.clientAreaItem
qml/Stages/TabletStage.qml+17−1
@@ -699,6 +699,20 @@ AbstractStage {
699699 }
700700 }
701701
702+ Timer { // fix a race condition between TopLevelSurfaceRepeater and app (usually dash) starting up; set a focusedAppDelegate
703+ running: !spreadRepeater.startingUp
704+ interval: 1
705+ onTriggered: {
706+ for (var i = 0; i < spreadRepeater.count; i++) {
707+ var appDelegate = spreadRepeater.itemAt(i);
708+ if (appDelegate && appDelegate.focus) {
709+ priv.focusedAppDelegate = appDelegate;
710+ break;
711+ }
712+ }
713+ }
714+ }
715+
702716 TopLevelSurfaceRepeater {
703717 id: spreadRepeater
704718 objectName: "spreadRepeater"
@@ -763,7 +777,9 @@ AbstractStage {
763777 onFocusChanged: {
764778 if (focus && !spreadRepeater.startingUp) {
765779 priv.focusedAppDelegate = spreadTile;
766− root.topLevelSurfaceList.raiseId(model.id);
780+ if (root.parent) {
781+ root.topLevelSurfaceList.raiseId(model.id);
782+ }
767783 }
768784 if (focus && priv.sideStageEnabled && stage === ApplicationInfoInterface.SideStage) {
769785 sideStage.show();
tests/mocks/Utils/WindowInputMonitor.qml+4−1
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2015 Canonical, Ltd.
2+ * Copyright (C) 2015-2016 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -20,4 +20,7 @@ QtObject {
2020 signal homeKeyActivated()
2121 signal touchBegun()
2222 signal touchEnded(point pos)
23+ signal windowMoveRequested(point scenePos)
24+ signal windowMoveUpdated(point scenePos)
25+ signal windowMoveEnded()
2326}