on xenial help sign in
implement swipe to close in spread
commita5c39f1825869f9e8680d450e0d38b73d31c9473 authorMichael Zanetti <michael.zanetti@canonical.com> dateWed 25 Jun 2014, 13:13 (12 years ago) parent316ca48
3 files changed+173−64
qml/Stages/PhoneStage.qml+34−7 qml/Stages/SpreadDelegate.qml+122−54 qml/Stages/TabletStage.qml+17−3
qml/Stages/PhoneStage.qml+34−7
@@ -117,8 +117,7 @@ Item {
117117 id: spreadView
118118 objectName: "spreadView"
119119 anchors.fill: parent
120−// visible: spreadDragArea.status == DirectionalDragArea.Recognized || phase > 1 || snapAnimation.running
121− interactive: spreadDragArea.status == DirectionalDragArea.Recognized || phase > 1 || snapAnimation.running
120+ interactive: (spreadDragArea.status == DirectionalDragArea.Recognized || phase > 1) && draggedIndex == -1
122121 contentWidth: spreadRow.width - shift
123122 contentX: -shift
124123
@@ -127,8 +126,8 @@ Item {
127126 // that, the beginning of the gesture starts with a negative value for contentX
128127 // so the flickable wants to pull it into the view already. "shift" tunes the
129128 // distance where to "lock" the content.
130− property real shift: width / 2
131− property real shiftedContentX: contentX + shift
129+ readonly property real shift: width / 2
130+ readonly property real shiftedContentX: contentX + shift
132131
133132 property int tileDistance: width / 4
134133
@@ -152,6 +151,8 @@ Item {
152151 property int phase: 0
153152
154153 property int selectedIndex: -1
154+ property int draggedIndex: -1
155+ property int closingIndex: -1
155156
156157 onShiftedContentXChanged: {
157158 switch (phase) {
@@ -221,6 +222,15 @@ Item {
221222 // tileDistance * app count (with a minimum of 3 apps, in order to also allow moving 1 and 2 apps a bit)
222223 // + some constant value (still scales with the screen width) which looks good and somewhat fills the screen
223224 width: Math.max(3, ApplicationManager.count) * spreadView.tileDistance + (spreadView.width - spreadView.tileDistance) * 1.5
225+ Behavior on width {
226+ enabled: spreadView.closingIndex >= 0
227+ UbuntuNumberAnimation {}
228+ }
229+ onWidthChanged: {
230+ if (spreadView.closingIndex >= 0) {
231+ spreadView.contentX = Math.min(spreadView.contentX, width - spreadView.width - spreadView.shift)
232+ }
233+ }
224234
225235 x: spreadView.contentX
226236
@@ -244,15 +254,26 @@ Item {
244254 maximizedAppTopMargin: root.maximizedAppTopMargin
245255 dropShadow: spreadView.shiftedContentX > 0 || spreadDragArea.status == DirectionalDragArea.Undecided
246256
247− z: index
257+ z: behavioredIndex
248258 x: index == 0 ? 0 : spreadView.width + (index - 1) * spreadView.tileDistance
259+ property real behavioredIndex: index
260+ Behavior on behavioredIndex {
261+ enabled: spreadView.closingIndex >= 0
262+ UbuntuNumberAnimation {
263+ onRunningChanged: {
264+ if (!running) {
265+ spreadView.closingIndex = -1
266+ }
267+ }
268+ }
269+ }
249270
250271 // Each tile has a different progress value running from 0 to 1.
251272 // A progress value of 0 means the tile is at the right edge. 1 means the tile has reched the left edge.
252273 progress: {
253− var tileProgress = (spreadView.shiftedContentX - index * spreadView.tileDistance) / spreadView.width;
274+ var tileProgress = (spreadView.shiftedContentX - behavioredIndex * spreadView.tileDistance) / spreadView.width;
254275 // Tile 1 needs to move directly from the beginning...
255− if (index == 1 && spreadView.phase < 2) {
276+ if (behavioredIndex == 1 && spreadView.phase < 2) {
256277 tileProgress += spreadView.tileDistance / spreadView.width;
257278 }
258279 return tileProgress;
@@ -288,6 +309,12 @@ Item {
288309 }
289310 }
290311 }
312+
313+ onClosed: {
314+ spreadView.draggedIndex = -1
315+ spreadView.closingIndex = index
316+ ApplicationManager.stopApplication(ApplicationManager.get(index).appId)
317+ }
291318 }
292319 }
293320 }
qml/Stages/SpreadDelegate.qml+122−54
@@ -18,39 +18,41 @@
1818
1919import QtQuick 2.0
2020import Mir.Application 0.1
21−import Ubuntu.Components 1.0
21+import Ubuntu.Components 1.1
2222import "../Components"
2323
2424Item {
2525 id: root
2626
27− signal clicked()
2827 property bool interactive: true
29− property real maximizedAppTopMargin
30− property bool isFullscreen: surface !== null && surface.anchors.topMargin == 0
3128 property bool dropShadow: true
29+ property real maximizedAppTopMargin
3230
33− // FIXME: This really should be invisible to QML code.
34− // e.g. Create a SurfaceItem {} in C++ which we just use without any imperative hacks.
35− readonly property var surface: model.surface
36− onSurfaceChanged: { print("SURFACE!!", model.surface)
37− if (surface) {
38− if (!priv.appHasCreatedASurface) {
39− surface.visible = false; // hide until splash screen removed
40− priv.appHasCreatedASurface = true;
41− }
31+ readonly property bool isFullscreen: surface !== null && surfaceContainer.surface.anchors.topMargin == 0
4232
43− surface.parent = root;
44− surface.anchors.fill = root;
45− priv.checkFullscreen(surface);
46− surface.z = 1;
47− }
48− }
33+ signal clicked()
34+ signal closed()
4935
50− QtObject {
51− id: priv
36+ Item {
37+ id: surfaceContainer
38+ anchors.fill: parent
39+ readonly property var surface: model.surface
5240 property bool appHasCreatedASurface: false
5341
42+ onSurfaceChanged: { print("SURFACE!!", model.surface)
43+ if (surface) {
44+ if (!appHasCreatedASurface) {
45+ surface.visible = false; // hide until splash screen removed
46+ appHasCreatedASurface = true;
47+ }
48+
49+ surface.parent = surfaceContainer;
50+ surface.anchors.fill = surfaceContainer;
51+ checkFullscreen(surface);
52+ surface.z = 1;
53+ }
54+ }
55+
5456 function checkFullscreen(surface) {
5557 if (surface.state === MirSurfaceItem.Fullscreen) {
5658 surface.anchors.topMargin = 0;
@@ -63,43 +65,58 @@ Item {
6365 surface.visible = true;
6466 splashLoader.source = "";
6567 }
66− }
6768
68− Timer { //FIXME - need to delay removing splash screen to allow surface resize to complete
69− id: surfaceRevealDelay
70− interval: 100
71− onTriggered: priv.revealSurface()
72− }
69+ Binding {
70+ target: surface
71+ property: "enabled"
72+ value: root.interactive
73+ }
7374
74− Binding {
75− target: surface
76− property: "enabled"
77− value: root.interactive
78− }
75+ Timer { //FIXME - need to delay removing splash screen to allow surface resize to complete
76+ id: surfaceRevealDelay
77+ interval: 100
78+ onTriggered: surfaceContainer.revealSurface()
79+ }
7980
80− Connections {
81− target: surface
82− onStateChanged: priv.checkFullscreen(surface);
81+ Connections {
82+ target: surface
83+ onStateChanged: checkFullscreen(surface);
84+ }
85+
86+ BorderImage {
87+ id: dropShadowImage
88+ anchors.fill: parent
89+ anchors.margins: -units.gu(2)
90+ source: "graphics/dropshadow.png"
91+ border { left: 50; right: 50; top: 50; bottom: 50 }
92+ opacity: root.dropShadow ? .4 : 0
93+ Behavior on opacity { UbuntuNumberAnimation {} }
94+ }
95+
96+ transform: Translate {
97+ y: dragArea.distance
98+ }
8399 }
84100
101+
85102 StateGroup {
86103 id: appSurfaceState
87104 states: [
88105 State {
89106 name: "noSurfaceYet"
90− when: !priv.appHasCreatedASurface
107+ when: !surfaceContainer.appHasCreatedASurface
91108 StateChangeScript {
92− script: {splashLoader.setSource("Splash.qml", { "name": model.name, "image": model.icon }); }
109+ script: { splashLoader.setSource("Splash.qml", { "name": model.name, "image": model.icon }); }
93110 }
94111 },
95112 State {
96113 name: "hasSurface"
97− when: priv.appHasCreatedASurface && (root.surface !== null)
114+ when: surfaceContainer.appHasCreatedASurface && (surfaceContainer.surface !== null)
98115 StateChangeScript { script: { surfaceRevealDelay.start(); } }
99116 },
100117 State {
101118 name: "surfaceLostButAppStillAlive"
102− when: priv.appHasCreatedASurface && (root.surface === null)
119+ when: surfaceContainer.appHasCreatedASurface && (surfaceContainer.surface === null)
103120 // TODO - use app snapshot
104121 }
105122 ]
@@ -108,25 +125,76 @@ Item {
108125
109126 Loader {
110127 id: splashLoader
111− anchors.fill: parent
112− }
113−
114− BorderImage {
115− id: dropShadowImage
116− anchors.fill: surface
117− anchors.margins: -units.gu(2)
118− source: "graphics/dropshadow.png"
119− border { left: 50; right: 50; top: 50; bottom: 50 }
120− opacity: root.dropShadow ? .4 : 0
121− Behavior on opacity { UbuntuNumberAnimation {} }
128+ anchors.fill: surfaceContainer
122129 }
123130
124− MouseArea {
131+ DraggingArea {
132+ id: dragArea
125133 anchors.fill: parent
126− z: 2
127134 enabled: !root.interactive
128− onClicked: {
129− root.clicked()
135+
136+ property bool moving: false
137+ property real distance: 0
138+
139+ onMovingChanged: {
140+ spreadView.draggedIndex = moving ? index : -1
141+ }
142+
143+ onDragValueChanged: {
144+ moving = moving || Math.abs(dragValue) > units.gu(1)
145+ if (moving && dragging) {
146+ distance = dragValue;
147+ }
148+ }
149+
150+ onDragEnd: {
151+ if (!moving) {
152+ root.clicked();
153+ return;
154+ }
155+
156+ if ((dragVelocity < -600 && distance < -units.gu(4)) || distance < -root.height / 2) {
157+ animation.animate("up")
158+ } else if ((dragVelocity > 600 && distance > units.gu(4)) || distance > root.height / 2) {
159+ animation.animate("down")
160+ } else {
161+ animation.animate("center")
162+ }
163+ }
164+
165+ UbuntuNumberAnimation {
166+ id: animation
167+ target: dragArea
168+ property: "distance"
169+ property bool requestClose: false
170+
171+ function animate(direction) {
172+ animation.from = dragArea.distance
173+ switch (direction) {
174+ case "up":
175+ animation.to = -root.height
176+ requestClose = true;
177+ break;
178+ case "down":
179+ animation.to = root.height
180+ requestClose = true;
181+ break;
182+ default:
183+ animation.to = 0
184+ }
185+ animation.start();
186+ }
187+
188+ onRunningChanged: {
189+ if (!running) {
190+ dragArea.moving = false;
191+ dragArea.distance = 0;
192+ if (requestClose) {
193+ root.closed()
194+ }
195+ }
196+ }
130197 }
131198 }
199+
132200}
qml/Stages/TabletStage.qml+17−3
@@ -135,7 +135,7 @@ Item {
135135 id: spreadView
136136 anchors.fill: parent
137137 contentWidth: spreadRow.width
138− interactive: phase == 2
138+ interactive: (spreadDragArea.status == DirectionalDragArea.Recognized || phase > 1) && draggedIndex == -1
139139
140140 property int tileDistance: units.gu(20)
141141 property int sideStageWidth: units.gu(40)
@@ -157,6 +157,8 @@ Item {
157157
158158 property int selectedIndex: -1
159159 onSelectedIndexChanged: print("selected index changed", selectedIndex)
160+ property int draggedIndex: -1
161+ property int closingIndex: -1
160162
161163 property bool sideStageDragging: sideStageDragHandle.dragging
162164 property real sideStageDragProgress: sideStageDragHandle.progress
@@ -430,11 +432,17 @@ Item {
430432 maximizedAppTopMargin: root.maximizedAppTopMargin
431433 dropShadow: spreadView.contentX > 0 || spreadDragArea.status == DirectionalDragArea.Undecided
432434
435+ property real behavioredZIndex: zIndex
436+ Behavior on behavioredZIndex {
437+ enabled: spreadView.closingIndex >= 0
438+ UbuntuNumberAnimation {}
439+ }
440+
433441 progress: {
434− var tileProgress = (spreadView.contentX - zIndex * spreadView.tileDistance) / spreadView.width;
442+ var tileProgress = (spreadView.contentX - behavioredZIndex * spreadView.tileDistance) / spreadView.width;
435443 // Some tiles (nextInStack, active) need to move directly from the beginning, normalize progress to immediately start at 0
436444 if ((index == spreadView.nextInStack && spreadView.phase < 2) || (active && spreadView.phase < 1)) {
437− tileProgress += zIndex * spreadView.tileDistance / spreadView.width;
445+ tileProgress += behavioredZIndex * spreadView.tileDistance / spreadView.width;
438446 }
439447 return tileProgress;
440448 }
@@ -462,6 +470,12 @@ Item {
462470 }
463471 }
464472
473+ onClosed: {
474+ spreadView.draggedIndex = -1
475+ spreadView.closingIndex = index
476+ ApplicationManager.stopApplication(ApplicationManager.get(index).appId)
477+ }
478+
465479 EasingCurve {
466480 id: snappingCurve
467481 type: EasingCurve.Linear