on xenial help sign in
WIP: MousePointer based edge push
commit50158058f9188f0e52adee53cae2d2fcd70551c7 authorLukáš Tinkl <lukas.tinkl@canonical.com> dateThu 25 Aug 2016, 18:31 (10 years ago) parenta404f6c
8 files changed+196−98
plugins/Cursor/MousePointer.cpp+5 plugins/Cursor/MousePointer.h+1 qml/Shell.qml+31−7 qml/Stages/DecoratedWindow.qml+7−1 qml/Stages/DesktopStage.qml+5−1 qml/Stages/FakeMaximizeDelegate.qml+82−26 qml/Stages/MoveHandler.qml+64−63 tests/mocks/Cursor/Cursor.qml+1
plugins/Cursor/MousePointer.cpp+5
@@ -73,6 +73,8 @@ void MousePointer::handleMouseEvent(ulong timestamp, QPointF movement, Qt::Mouse
7373 Q_EMIT pushedRightBoundary(newX - (sceneWidth - 1), buttons);
7474 } else if (newY < m_panelHeight) { // top edge
7575 Q_EMIT pushedTopBoundary(qAbs(newY), buttons);
76+ } else if (Q_LIKELY(newX >= 0 && newX < sceneWidth && newY >= 0 && newY < sceneHeight)) { // normal pos, not pushing
77+ Q_EMIT pushStopped();
7678 }
7779
7880 setX(qBound(0.0, newX, sceneWidth - 1));
@@ -98,6 +100,9 @@ void MousePointer::itemChange(ItemChange change, const ItemChangeData &value)
98100{
99101 if (change == ItemSceneChange) {
100102 registerWindow(value.window);
103+ if (value.window) {
104+ m_panelHeight = QQmlProperty::read(this, "panelHeight").toInt(); // when we move e.g. to a different screen (with possible different DGU)
105+ }
101106 }
102107}
103108
plugins/Cursor/MousePointer.h+1
@@ -51,6 +51,7 @@ Q_SIGNALS:
5151 void pushedTopRightCorner(qreal amount, Qt::MouseButtons buttons);
5252 void pushedBottomLeftCorner(qreal amount, Qt::MouseButtons buttons);
5353 void pushedBottomRightCorner(qreal amount, Qt::MouseButtons buttons);
54+ void pushStopped();
5455 void mouseMoved();
5556
5657protected:
qml/Shell.qml+31−7
@@ -778,35 +778,59 @@ StyledItem {
778778
779779 height: units.gu(3)
780780
781+ readonly property var previewRectangle: applicationsDisplayLoader.item && applicationsDisplayLoader.item.previewRectangle &&
782+ applicationsDisplayLoader.item.previewRectangle.targetDragging ?
783+ applicationsDisplayLoader.item.previewRectangle : null
784+
781785 onPushedLeftBoundary: {
782− print("!!! Left edge:", amount, buttons)
783786 if (buttons === Qt.NoButton) {
784787 launcher.pushEdge(amount);
788+ } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
789+ previewRectangle.maximizeLeft(amount);
785790 }
786791 }
787792
788793 onPushedRightBoundary: {
789− print("!!! Right edge:", amount, buttons)
790794 if (buttons === Qt.NoButton && applicationsDisplayLoader.item
791795 && applicationsDisplayLoader.item.pushRightEdge) {
792796 applicationsDisplayLoader.item.pushRightEdge(amount);
797+ } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
798+ previewRectangle.maximizeRight(amount);
793799 }
794800 }
795801
796802 onPushedTopBoundary: {
797− print("!!! Top edge:", amount, buttons)
803+ if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximized) {
804+ previewRectangle.maximize(amount);
805+ }
798806 }
799807 onPushedTopLeftCorner: {
800− print("!!! Top left corner:", amount, buttons)
808+ if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
809+ previewRectangle.maximizeTopLeft(amount);
810+ }
801811 }
802812 onPushedTopRightCorner: {
803− print("!!! Top right corner:", amount, buttons)
813+ if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
814+ previewRectangle.maximizeTopRight(amount);
815+ }
804816 }
805817 onPushedBottomLeftCorner: {
806− print("!!! Bottom left corner:", amount, buttons)
818+ if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
819+ previewRectangle.maximizeBottomLeft(amount);
820+ }
807821 }
808822 onPushedBottomRightCorner: {
809− print("!!! Bottom right corner:", amount, buttons)
823+ if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
824+ previewRectangle.maximizeBottomRight(amount);
825+ }
826+ }
827+ onPushStopped: {
828+ if (previewRectangle) {
829+ if (previewRectangle.progress > 0 && previewRectangle.progress < 0.1) {
830+ print("!!! Stopping at", previewRectangle.progress)
831+ previewRectangle.stop();
832+ }
833+ }
810834 }
811835
812836 onMouseMoved: {
qml/Stages/DecoratedWindow.qml+7−1
@@ -72,6 +72,8 @@ FocusScope {
7272 signal fakeMaximizeBottomRightAnimationRequested(real progress)
7373 signal stopFakeAnimation()
7474
75+ signal shouldCommitSnapWindow()
76+
7577 Rectangle {
7678 id: selectionHighlight
7779 anchors.fill: parent
@@ -116,7 +118,11 @@ FocusScope {
116118
117119 onPressedChanged: moveHandler.handlePressedChanged(pressed, pressedButtons, mouseX, mouseY)
118120 onPositionChanged: moveHandler.handlePositionChanged(mouse)
119− onReleased: moveHandler.handleReleased(mouse)
121+ onReleased: {
122+ print("!!! Mouse released on decoration")
123+ root.shouldCommitSnapWindow();
124+ moveHandler.handleReleased(mouse);
125+ }
120126 }
121127
122128 MoveHandler {
qml/Stages/DesktopStage.qml+5−1
@@ -45,6 +45,9 @@ AbstractStage {
4545 // Used by TutorialRight
4646 property bool spreadShown: spread.state == "altTab"
4747
48+ // used by the snap windows (edge maximize) feature
49+ readonly property alias previewRectangle: fakeRectangle
50+
4851 mainApp: priv.focusedAppDelegate ? priv.focusedAppDelegate.application : null
4952
5053 // application windows never rotate independently
@@ -662,9 +665,9 @@ AbstractStage {
662665 enabled: appDelegate.animationsEnabled
663666 PropertyAction { target: appDelegate; property: "visuallyMinimized" }
664667 SequentialAnimation {
665− ScriptAction { script: { fakeRectangle.stop(); } }
666668 UbuntuNumberAnimation { target: appDelegate; properties: "requestedX,requestedY,opacity,scale,requestedWidth,requestedHeight" }
667669 PropertyAction { target: appDelegate; property: "visuallyMaximized" }
670+ ScriptAction { script: { fakeRectangle.stop(); } }
668671 }
669672 }
670673 ]
@@ -744,6 +747,7 @@ AbstractStage {
744747 onMaximizeVerticallyClicked: appDelegate.maximizedVertically ? appDelegate.restoreFromMaximized() : appDelegate.maximizeVertically()
745748 onMinimizeClicked: appDelegate.minimize()
746749 onDecorationPressed: { appDelegate.focus = true; }
750+ onShouldCommitSnapWindow: fakeRectangle.commit();
747751
748752 onFakeMaximizeAnimationRequested: if (!appDelegate.maximized) fakeRectangle.maximize(progress)
749753 onFakeMaximizeLeftAnimationRequested: if (!appDelegate.maximizedLeft) fakeRectangle.maximizeLeft(progress)
qml/Stages/FakeMaximizeDelegate.qml+82−26
@@ -16,11 +16,12 @@
1616
1717import QtQuick 2.4
1818import Ubuntu.Components 1.3
19+import Utils 0.1 // For EdgeBarrierSettings
1920import "../Components/PanelState"
2021
2122Rectangle {
2223 id: fakeRectangle
23− visible: opacity > 0 // we go from 0.2 to 0.6
24+ visible: opacity > 0 && target && !target.anyMaximized // we go from 0.2 to 0.6
2425 enabled: visible
2526 color: "#ffffff"
2627 border.width: units.dp(1)
@@ -33,12 +34,43 @@ Rectangle {
3334 property real appContainerWidth
3435 property real appContainerHeight
3536
37+ readonly property bool targetDragging: target && target.moveHandler.dragging
38+
39+ // Edge push progress
40+ // Value range is [0.0, 1.0]
41+ readonly property real progress: Math.min(Math.max(0.0, priv.accumulatedPush / EdgeBarrierSettings.pushThreshold), 1.0)
42+ onProgressChanged: {
43+ print("!!! Progress:", progress)
44+ }
45+
3646 QtObject {
3747 id: priv
3848
39− property real progress: 0
49+ property real accumulatedPush: 0
50+
51+ function push(amount) {
52+ if (accumulatedPush === EdgeBarrierSettings.pushThreshold) {
53+ // NO-OP
54+ print("!!! Accumulated push already at threshold")
55+ return;
56+ }
57+
58+ if (accumulatedPush + amount > EdgeBarrierSettings.pushThreshold) {
59+ accumulatedPush = EdgeBarrierSettings.pushThreshold;
60+ } else {
61+ accumulatedPush += amount;
62+ }
63+
64+ if (accumulatedPush === EdgeBarrierSettings.pushThreshold) {
65+ print("!!! Reached push progress 1, maximizing")
66+ commit();
67+ }
68+ }
4069
4170 function setup(edge) {
71+ if (edge !== fakeRectangle.edge) {
72+ stop(); // a different edge, start anew
73+ }
4274 fakeRectangle.x = target.x;
4375 fakeRectangle.y = target.y;
4476 fakeRectangle.width = target.width;
@@ -47,25 +79,49 @@ Rectangle {
4779 fakeRectangle.transformOrigin = edge;
4880 }
4981
50− function projectOpacity(progress) {
82+ function projectOpacity() {
5183 // progress falls into [0,1], opacity moves in [0.2,0.6]
5284 fakeRectangle.opacity = MathUtils.clampAndProject(progress, 0.0, 1.0, 0.2, 0.6);
5385 }
5486
55− function projectScale(progress) {
56− // progress falls into [0,1], scale moves in [1,2] as a hint, when under 30%
87+ function projectScale() {
88+ // progress falls into [0,1], scale moves in [1,2] as a hint, when under 10%
5789 fakeRectangle.scale = MathUtils.clampAndProject(progress, 0, 1, 1, 2);
5890 }
5991
60− function processAnimation(progress, animation) {
61− if (priv.progress >= 0.3) {
92+ function processAnimation(amount, animation) {
93+ priv.push(amount);
94+
95+ if (progress > 0.1) { // above 10% we start the full preview animation
6296 fakeRectangle.scale = 1;
6397 animation.start();
64− } else if (progress > priv.progress) {
65− priv.progress += progress;
66− priv.projectScale(progress);
98+ } else {
99+ priv.projectScale();
100+ }
101+
102+ priv.projectOpacity();
103+ }
104+ }
105+
106+ function commit() {
107+ if (progress > 0.1 && edge != -1) {
108+ print("!!! Committing edge", edge, ", progress:", progress);
109+ target.moveHandler.handlePressedChanged(false, Qt.LeftButton, 0, 0); // cancel the drag
110+ if (edge == Item.Top) {
111+ target.maximize();
112+ } else if (edge == Item.Left) {
113+ target.maximizeLeft();
114+ } else if (edge == Item.Right) {
115+ target.maximizeRight();
116+ } else if (edge == Item.TopLeft) {
117+ target.maximizeTopLeft();
118+ } else if (edge == Item.TopRight) {
119+ target.maximizeTopRight();
120+ } else if (edge == Item.BottomLeft) {
121+ target.maximizeBottomLeft();
122+ } else if (edge == Item.BottomRight) {
123+ target.maximizeBottomRight();
67124 }
68− priv.projectOpacity(progress);
69125 }
70126 }
71127
@@ -73,56 +129,56 @@ Rectangle {
73129 print("!!! Stop")
74130 opacity = 0;
75131 edge = -1;
76− priv.progress = 0;
132+ priv.accumulatedPush = 0;
77133 }
78134
79− function maximize(progress) {
135+ function maximize(amount) {
80136 if (fakeRectangle.edge != Item.Top) {
81137 priv.setup(Item.Top);
82138 }
83− priv.processAnimation(progress, fakeMaximizeAnimation);
139+ priv.processAnimation(amount, fakeMaximizeAnimation);
84140 }
85141
86− function maximizeLeft(progress) {
142+ function maximizeLeft(amount) {
87143 if (fakeRectangle.edge != Item.Left) {
88144 priv.setup(Item.Left);
89145 }
90− priv.processAnimation(progress, fakeMaximizeLeftAnimation);
146+ priv.processAnimation(amount, fakeMaximizeLeftAnimation);
91147 }
92148
93− function maximizeRight(progress) {
149+ function maximizeRight(amount) {
94150 if (fakeRectangle.edge != Item.Right) {
95151 priv.setup(Item.Right);
96152 }
97− priv.processAnimation(progress, fakeMaximizeRightAnimation);
153+ priv.processAnimation(amount, fakeMaximizeRightAnimation);
98154 }
99155
100− function maximizeTopLeft(progress) {
156+ function maximizeTopLeft(amount) {
101157 if (fakeRectangle.edge != Item.TopLeft) {
102158 priv.setup(Item.TopLeft);
103159 }
104− priv.processAnimation(progress, fakeMaximizeTopLeftAnimation);
160+ priv.processAnimation(amount, fakeMaximizeTopLeftAnimation);
105161 }
106162
107− function maximizeTopRight(progress) {
163+ function maximizeTopRight(amount) {
108164 if (fakeRectangle.edge != Item.TopRight) {
109165 priv.setup(Item.TopRight);
110166 }
111− priv.processAnimation(progress, fakeMaximizeTopRightAnimation);
167+ priv.processAnimation(amount, fakeMaximizeTopRightAnimation);
112168 }
113169
114− function maximizeBottomLeft(progress) {
170+ function maximizeBottomLeft(amount) {
115171 if (fakeRectangle.edge != Item.BottomLeft) {
116172 priv.setup(Item.BottomLeft);
117173 }
118− priv.processAnimation(progress, fakeMaximizeBottomLeftAnimation);
174+ priv.processAnimation(amount, fakeMaximizeBottomLeftAnimation);
119175 }
120176
121− function maximizeBottomRight(progress) {
177+ function maximizeBottomRight(amount) {
122178 if (fakeRectangle.edge != Item.BottomRight) {
123179 priv.setup(Item.BottomRight);
124180 }
125− priv.processAnimation(progress, fakeMaximizeBottomRightAnimation);
181+ priv.processAnimation(amount, fakeMaximizeBottomRightAnimation);
126182 }
127183
128184 Behavior on opacity { UbuntuNumberAnimation { duration: UbuntuAnimation.BriskDuration } }
qml/Stages/MoveHandler.qml+64−63
@@ -29,7 +29,7 @@ Item {
2929 property int stageHeight
3030 property real buttonsWidth: 0
3131
32− readonly property alias dragging: priv.dragging
32+ property alias dragging: priv.dragging
3333
3434 signal fakeMaximizeAnimationRequested(real progress)
3535 signal fakeMaximizeLeftAnimationRequested(real progress)
@@ -104,6 +104,7 @@ Item {
104104 Mir.cursorName = "grabbing";
105105
106106 if (target.anyMaximized) { // restore from maximized when dragging away from edges/corners
107+ print("!!! Restoring maximized window after a drag away")
107108 priv.progress = 0;
108109 target.restore(false, WindowStateStorage.WindowStateNormal);
109110 }
@@ -115,75 +116,75 @@ Item {
115116 target.requestedX = Math.round(pos.x - priv.distanceX);
116117 target.requestedY = Math.round(Math.max(pos.y - priv.distanceY, PanelState.panelHeight));
117118
118− var globalPos = mapToItem(null, mouse.x, mouse.y);
119− var globalX = globalPos.x;
120− var globalY = globalPos.y;
121− if (globalX < priv.triggerArea && globalY < PanelState.panelHeight) { // top left
122− if (target.canBeCornerMaximized) {
123− priv.progress = priv.progressInCorner(0, PanelState.panelHeight, globalX, globalY);
124− priv.resetEdges();
125− priv.nearTopLeftCorner = true;
126− root.fakeMaximizeTopLeftAnimationRequested(priv.progress);
127− }
128− } else if (globalX > stageWidth - priv.triggerArea && globalY < PanelState.panelHeight) { // top right
129− if (target.canBeCornerMaximized) {
130− priv.progress = priv.progressInCorner(stageWidth, PanelState.panelHeight, globalX, globalY);
131− priv.resetEdges();
132− priv.nearTopRightCorner = true;
133− root.fakeMaximizeTopRightAnimationRequested(priv.progress);
134− }
135− } else if (globalX < priv.triggerArea && globalY > stageHeight - priv.triggerArea) { // bottom left
136− if (target.canBeCornerMaximized) {
137− priv.progress = priv.progressInCorner(0, stageHeight, globalX, globalY);
138− priv.resetEdges();
139− priv.nearBottomLeftCorner = true;
140− root.fakeMaximizeBottomLeftAnimationRequested(priv.progress);
141− }
142− } else if (globalX > stageWidth - priv.triggerArea && globalY > stageHeight - priv.triggerArea) { // bottom right
143− if (target.canBeCornerMaximized) {
144− priv.progress = priv.progressInCorner(stageWidth, stageHeight, globalX, globalY);
145− priv.resetEdges();
146− priv.nearBottomRightCorner = true;
147− root.fakeMaximizeBottomRightAnimationRequested(priv.progress);
148− }
149− } else if (globalX < priv.triggerArea) { // left
150− if (target.canBeMaximizedLeftRight) {
151− priv.progress = MathUtils.clampAndProject(globalX, priv.triggerArea, 0, 0, 1);
152− priv.resetEdges();
153− priv.nearLeftEdge = true;
154− root.fakeMaximizeLeftAnimationRequested(priv.progress);
155− }
156− } else if (globalX > stageWidth - priv.triggerArea) { // right
157− if (target.canBeMaximizedLeftRight) {
158− priv.progress = MathUtils.clampAndProject(globalX, stageWidth - priv.triggerArea, stageWidth, 0, 1);
159− priv.resetEdges();
160− priv.nearRightEdge = true;
161− root.fakeMaximizeRightAnimationRequested(priv.progress);
162− }
163− } else if (globalY < PanelState.panelHeight) { // top
164− if (target.canBeMaximized) {
165− priv.progress = MathUtils.clampAndProject(globalY, Math.max(PanelState.panelHeight, priv.triggerArea), 0, 0, 1);
166− priv.resetEdges();
167− priv.nearTopEdge = true;
168− root.fakeMaximizeAnimationRequested(priv.progress);
169− }
170− } else if (priv.nearLeftEdge || priv.nearRightEdge || priv.nearTopEdge || priv.nearTopLeftCorner || priv.nearTopRightCorner ||
171− priv.nearBottomLeftCorner || priv.nearBottomRightCorner) {
172− print("!!! Exited")
173− priv.progress = 0;
174− priv.resetEdges();
175− root.stopFakeAnimation();
176− }
119+// var globalPos = mapToItem(null, mouse.x, mouse.y);
120+// var globalX = globalPos.x;
121+// var globalY = globalPos.y;
122+// if (globalX < priv.triggerArea && globalY < PanelState.panelHeight) { // top left
123+// if (target.canBeCornerMaximized) {
124+// priv.progress = priv.progressInCorner(0, PanelState.panelHeight, globalX, globalY);
125+// priv.resetEdges();
126+// priv.nearTopLeftCorner = true;
127+// root.fakeMaximizeTopLeftAnimationRequested(priv.progress);
128+// }
129+// } else if (globalX > stageWidth - priv.triggerArea && globalY < PanelState.panelHeight) { // top right
130+// if (target.canBeCornerMaximized) {
131+// priv.progress = priv.progressInCorner(stageWidth, PanelState.panelHeight, globalX, globalY);
132+// priv.resetEdges();
133+// priv.nearTopRightCorner = true;
134+// root.fakeMaximizeTopRightAnimationRequested(priv.progress);
135+// }
136+// } else if (globalX < priv.triggerArea && globalY > stageHeight - priv.triggerArea) { // bottom left
137+// if (target.canBeCornerMaximized) {
138+// priv.progress = priv.progressInCorner(0, stageHeight, globalX, globalY);
139+// priv.resetEdges();
140+// priv.nearBottomLeftCorner = true;
141+// root.fakeMaximizeBottomLeftAnimationRequested(priv.progress);
142+// }
143+// } else if (globalX > stageWidth - priv.triggerArea && globalY > stageHeight - priv.triggerArea) { // bottom right
144+// if (target.canBeCornerMaximized) {
145+// priv.progress = priv.progressInCorner(stageWidth, stageHeight, globalX, globalY);
146+// priv.resetEdges();
147+// priv.nearBottomRightCorner = true;
148+// root.fakeMaximizeBottomRightAnimationRequested(priv.progress);
149+// }
150+// } else if (globalX < priv.triggerArea) { // left
151+// if (target.canBeMaximizedLeftRight) {
152+// priv.progress = MathUtils.clampAndProject(globalX, priv.triggerArea, 0, 0, 1);
153+// priv.resetEdges();
154+// priv.nearLeftEdge = true;
155+// root.fakeMaximizeLeftAnimationRequested(priv.progress);
156+// }
157+// } else if (globalX > stageWidth - priv.triggerArea) { // right
158+// if (target.canBeMaximizedLeftRight) {
159+// priv.progress = MathUtils.clampAndProject(globalX, stageWidth - priv.triggerArea, stageWidth, 0, 1);
160+// priv.resetEdges();
161+// priv.nearRightEdge = true;
162+// root.fakeMaximizeRightAnimationRequested(priv.progress);
163+// }
164+// } else if (globalY < PanelState.panelHeight) { // top
165+// if (target.canBeMaximized) {
166+// priv.progress = MathUtils.clampAndProject(globalY, Math.max(PanelState.panelHeight, priv.triggerArea), 0, 0, 1);
167+// priv.resetEdges();
168+// priv.nearTopEdge = true;
169+// root.fakeMaximizeAnimationRequested(priv.progress);
170+// }
171+// } else if (priv.nearLeftEdge || priv.nearRightEdge || priv.nearTopEdge || priv.nearTopLeftCorner || priv.nearTopRightCorner ||
172+// priv.nearBottomLeftCorner || priv.nearBottomRightCorner) {
173+// print("!!! Exited")
174+// priv.progress = 0;
175+// priv.resetEdges();
176+// root.stopFakeAnimation();
177+// }
177178 }
178179 }
179180
180181 function handleReleased(mouse) {
181− print("Mouse released (left/top/right)", priv.nearLeftEdge, priv.nearTopEdge, priv.nearRightEdge)
182182 if (mouse.button == Qt.LeftButton && (target.state == "normal" || target.state == "restored") && priv.progress == 0) {
183183 // save the x/y to restore to
184+ print("!!! Saving restoredX/Y")
184185 target.restoredX = target.x;
185186 target.restoredY = target.y;
186− } else if (priv.progress < 0.3) { // cancel the preview shape if under 30%
187+ } /*else if (priv.progress < 0.3) { // cancel the preview shape if under 30%
187188 priv.progress = 0;
188189 priv.resetEdges();
189190 root.stopFakeAnimation();
@@ -208,6 +209,6 @@ Item {
208209 } else if (priv.nearBottomRightCorner) {
209210 target.maximizeBottomRight();
210211 priv.resetEdges();
211− }
212+ }*/
212213 }
213214}
tests/mocks/Cursor/Cursor.qml+1
@@ -26,5 +26,6 @@ Item {
2626 signal pushedTopRightCorner(real amount, int buttons)
2727 signal pushedBottomLeftCorner(real amount, int buttons)
2828 signal pushedBottomRightCorner(real amount, int buttons)
29+ signal pushStopped()
2930 signal mouseMoved()
3031}