on xenial help sign in
improve launcher flicking bahavior
1af7070Michael Zanetti
Launcher/LauncherDelegate.qml−1
@@ -65,7 +65,6 @@ Item {
65 onClicked: root.clicked()
65 onClicked: root.clicked()
66 onCanceled: root.released()
66 onCanceled: root.released()
67 preventStealing: false
67 preventStealing: false
68− enabled: root.angle < 5
69
68
70 onPressAndHold: {
69 onPressAndHold: {
71 root.state = "moving"
70 root.state = "moving"
Launcher/LauncherPanel.qml+26−14
@@ -100,6 +100,7 @@ Item {
100 height: parent.height - dashItem.height - parent.spacing*2
100 height: parent.height - dashItem.height - parent.spacing*2
101 ListView {
101 ListView {
102 id: launcherListView
102 id: launcherListView
103+ objectName: "launcherListView"
103 anchors.fill: parent
104 anchors.fill: parent
104 anchors.topMargin: -itemSize
105 anchors.topMargin: -itemSize
105 anchors.bottomMargin: -itemSize
106 anchors.bottomMargin: -itemSize
@@ -116,15 +117,7 @@ Item {
116 // The height of the area where icons start getting folded
117 // The height of the area where icons start getting folded
117 property int foldingAreaHeight: itemSize * 0.75
118 property int foldingAreaHeight: itemSize * 0.75
118 property int itemSize: width
119 property int itemSize: width
119−
120+ property int clickFlickSpeed: units.gu(60)
120− Component.onCompleted: {
121− // This is needed because snapping would partially fold
122− // the first item on initialisation. This can only be
123− // overridden by mouse interactions - which is what flick()
124− // simulates. Setting contentY to 0 would not work because
125− // of this.
126− flick(0, units.gu(20))
127− }
128
121
129 delegate: LauncherDelegate {
122 delegate: LauncherDelegate {
130 id: launcherDelegate
123 id: launcherDelegate
@@ -139,7 +132,26 @@ Item {
139 maxAngle: 60
132 maxAngle: 60
140
133
141 onClicked: {
134 onClicked: {
135+ // First/last item do the scrolling at more than 12 degrees
136+ if (index == 0 || index == launcherListView.count -1) {
137+ if (angle > 12) {
138+ launcherListView.flick(0, -launcherListView.clickFlickSpeed);
139+ } else if (angle < -12) {
140+ launcherListView.flick(0, launcherListView.clickFlickSpeed);
141+ } else {
142+ root.applicationSelected(launcherModel.get(index).desktopFile);
143+ }
144+ return;
145+ }
146+
147+ // the rest launches apps up to an angle of 30 degrees
148+ if (angle > 30) {
149+ launcherListView.flick(0, -launcherListView.clickFlickSpeed);
150+ } else if (angle < -30) {
151+ launcherListView.flick(0, launcherListView.clickFlickSpeed);
152+ } else {
142− root.applicationSelected(launcherModel.get(index).desktopFile);
153+ root.applicationSelected(launcherModel.get(index).desktopFile);
154+ }
143 }
155 }
144 }
156 }
145
157
@@ -151,9 +163,9 @@ Item {
151 top: parent.top
163 top: parent.top
152 topMargin: launcherListView.topMargin
164 topMargin: launcherListView.topMargin
153 }
165 }
154− height: launcherListView.itemSize
166+ height: launcherListView.itemSize / 2
155 enabled: launcherListView.contentY > -launcherListView.topMargin
167 enabled: launcherListView.contentY > -launcherListView.topMargin
156− onClicked: launcherListView.flick(0, units.gu(40))
168+ onClicked: launcherListView.flick(0, launcherListView.clickFlickSpeed)
157 }
169 }
158
170
159 MouseArea {
171 MouseArea {
@@ -164,9 +176,9 @@ Item {
164 bottom: parent.bottom
176 bottom: parent.bottom
165 bottomMargin: launcherListView.bottomMargin
177 bottomMargin: launcherListView.bottomMargin
166 }
178 }
167− height: launcherListView.itemSize
179+ height: launcherListView.itemSize / 2
168 enabled: launcherListView.contentHeight - launcherListView.height - launcherListView.contentY > -launcherListView.bottomMargin
180 enabled: launcherListView.contentHeight - launcherListView.height - launcherListView.contentY > -launcherListView.bottomMargin
169− onClicked: launcherListView.flick(0, -units.gu(40))
181+ onClicked: launcherListView.flick(0, -launcherListView.clickFlickSpeed)
170 }
182 }
171 }
183 }
172 }
184 }
tests/qmltests/Launcher/tst_Launcher.qml+114−34
@@ -23,20 +23,21 @@ import "../../../Launcher"
23/* Nothing is shown at first. If you drag from left edge you will bring up the
23/* Nothing is shown at first. If you drag from left edge you will bring up the
24 launcher. */
24 launcher. */
25Item {
25Item {
26+ id: root
26 width: units.gu(50)
27 width: units.gu(50)
27− height: units.gu(81)
28+ height: units.gu(55)
28
29
29 Launcher {
30 Launcher {
30 id: launcher
31 id: launcher
31 x: 0
32 x: 0
32 y: 0
33 y: 0
33 width: units.gu(40)
34 width: units.gu(40)
34− height: units.gu(81)
35+ height: parent.height
35
36
36− property string latestApplicationSelected
37+ property string lastSelectedApplication
37
38
38 onLauncherApplicationSelected: {
39 onLauncherApplicationSelected: {
39− latestApplicationSelected = desktopFile
40+ lastSelectedApplication = desktopFile
40 }
41 }
41
42
42 property int dashItemSelected_count: 0
43 property int dashItemSelected_count: 0
@@ -57,10 +58,34 @@ Item {
57 }
58 }
58 }
59 }
59
60
61+ UT.UnityTestCase {
62+ id: revealer
63+
64+ function dragLauncherIntoView() {
65+ var startX = launcher.dragAreaWidth/2;
66+ var startY = launcher.height/2;
67+ touchFlick(launcher,
68+ startX, startY,
69+ startX+units.gu(8), startY);
70+
71+ var panel = findChild(launcher, "launcherPanel");
72+ verify(panel != undefined);
73+
74+ // wait until it gets fully extended
75+ tryCompare(panel, "x", 0);
76+ tryCompare(launcher, "state", "visible");
77+ }
78+
79+ function waitUntilLauncherDisappears() {
80+ var panel = findChild(launcher, "launcherPanel");
81+ tryCompare(panel, "x", -panel.width, 1000);
82+ }
83+ }
84+
60 UT.UnityTestCase {
85 UT.UnityTestCase {
61 id: testCase
86 id: testCase
62 name: "Launcher"
87 name: "Launcher"
63− when: windowShown
88+ when: windowShown && initTestCase.completed
64
89
65 // Drag from the left edge of the screen rightwards and check that the launcher
90 // Drag from the left edge of the screen rightwards and check that the launcher
66 // appears (as if being dragged by the finger/pointer)
91 // appears (as if being dragged by the finger/pointer)
@@ -71,7 +96,7 @@ Item {
71 // it starts out hidden just left of the left launcher edge
96 // it starts out hidden just left of the left launcher edge
72 compare(panel.x, -panel.width)
97 compare(panel.x, -panel.width)
73
98
74− dragLauncherIntoView()
99+ revealer.dragLauncherIntoView()
75
100
76 // tapping on the center of the screen should dismiss the launcher
101 // tapping on the center of the screen should dismiss the launcher
77 mouseClick(launcher, launcher.width/2, launcher.height/2)
102 mouseClick(launcher, launcher.width/2, launcher.height/2)
@@ -85,20 +110,25 @@ Item {
85 corresponding desktop file. E.g. clicking on phone icon should yield
110 corresponding desktop file. E.g. clicking on phone icon should yield
86 launcherApplicationSelected("[...]phone-app.desktop") */
111 launcherApplicationSelected("[...]phone-app.desktop") */
87 function test_clickingOnAppIconCausesSignalEmission() {
112 function test_clickingOnAppIconCausesSignalEmission() {
88− launcher.latestApplicationSelected = ""
113+ launcher.lastSelectedApplication = ""
89
114
90− dragLauncherIntoView()
115+ revealer.dragLauncherIntoView()
116+
117+ var listView = findChild(launcher, "launcherListView");
118+ listView.flick(0, units.gu(500));
119+ tryCompare(listView, "flicking", false);
91
120
92 var appIcon = findChild(launcher, "launcherDelegate0")
121 var appIcon = findChild(launcher, "launcherDelegate0")
122+
93 verify(appIcon != undefined)
123 verify(appIcon != undefined)
94
124
95 mouseClick(appIcon, appIcon.width/2, appIcon.height/2)
125 mouseClick(appIcon, appIcon.width/2, appIcon.height/2)
96
126
97− tryCompare(launcher, "latestApplicationSelected",
127+ tryCompare(launcher, "lastSelectedApplication",
98 "/usr/share/applications/phone-app.desktop")
128 "/usr/share/applications/phone-app.desktop")
99
129
100 // Tapping on an application icon also dismisses the launcher
130 // Tapping on an application icon also dismisses the launcher
101− waitUntilLauncherDisappears()
131+ revealer.waitUntilLauncherDisappears()
102 }
132 }
103
133
104 /* If I click on the dash icon on the launcher
134 /* If I click on the dash icon on the launcher
@@ -106,7 +136,7 @@ Item {
106 function test_clickingOnDashIconCausesSignalEmission() {
136 function test_clickingOnDashIconCausesSignalEmission() {
107 launcher.dashItemSelected_count = 0
137 launcher.dashItemSelected_count = 0
108
138
109− dragLauncherIntoView()
139+ revealer.dragLauncherIntoView()
110
140
111 var dashIcon = findChild(launcher, "dashItem")
141 var dashIcon = findChild(launcher, "dashItem")
112 verify(dashIcon != undefined)
142 verify(dashIcon != undefined)
@@ -116,30 +146,9 @@ Item {
116 tryCompare(launcher, "dashItemSelected_count", 1)
146 tryCompare(launcher, "dashItemSelected_count", 1)
117
147
118 // Tapping on the dash icon also dismisses the launcher
148 // Tapping on the dash icon also dismisses the launcher
119− waitUntilLauncherDisappears()
120− }
121−
122− function dragLauncherIntoView() {
123− var startX = launcher.dragAreaWidth/2
124− var startY = launcher.height/2
125− touchFlick(launcher,
126− startX, startY,
127− startX+units.gu(8), startY)
128−
129− var panel = findChild(launcher, "launcherPanel")
130− verify(panel != undefined)
131−
132− // wait until it gets fully extended
133− tryCompare(panel, "x", 0)
134− tryCompare(launcher, "state", "visible")
135− }
136−
137− function waitUntilLauncherDisappears() {
149+ revealer.waitUntilLauncherDisappears()
138− var panel = findChild(launcher, "launcherPanel")
139− tryCompare(panel, "x", -panel.width, 1000)
140 }
150 }
141
151
142−
143 function test_teaseLauncher_data() {
152 function test_teaseLauncher_data() {
144 return [
153 return [
145 {tag: "available", available: true},
154 {tag: "available", available: true},
@@ -164,7 +173,78 @@ Item {
164 wait(100)
173 wait(100)
165 compare(launcher.maxPanelX, -launcher.panelWidth, "Launcher moved even if it shouldn't")
174 compare(launcher.maxPanelX, -launcher.panelWidth, "Launcher moved even if it shouldn't")
166 }
175 }
167− waitUntilLauncherDisappears();
176+ revealer.waitUntilLauncherDisappears();
177+ launcher.available = true;
178+ }
179+ }
180+
181+ UT.UnityTestCase {
182+ id: clickFlickTestCase
183+ when: windowShown && testCase.completed
184+
185+ function test_clickFlick_data() {
186+ var listView = findChild(launcher, "launcherListView");
187+ return [
188+ {tag: "unfolded top", flickSpeed: units.gu(200), clickY: listView.topMargin + units.gu(2), expectFlick: false},
189+ {tag: "folded top", flickSpeed: -units.gu(200), clickY: listView.topMargin + units.gu(2), expectFlick: true},
190+ {tag: "unfolded bottom", flickSpeed: -units.gu(200), clickY: listView.height - listView.topMargin - units.gu(1), expectFlick: false},
191+ {tag: "folded bottom", flickSpeed: units.gu(200), clickY: listView.height - listView.topMargin - units.gu(1), expectFlick: true},
192+ ];
193+ }
194+
195+ function test_clickFlick(data) {
196+ launcher.lastSelectedApplication = "";
197+ revealer.dragLauncherIntoView();
198+ var listView = findChild(launcher, "launcherListView");
199+
200+ listView.flick(0, data.flickSpeed);
201+ tryCompare(listView, "flicking", false);
202+
203+ var oldY = listView.contentY;
204+
205+ mouseClick(listView, listView.width / 2, data.clickY);
206+ tryCompare(listView, "flicking", false);
207+
208+ if (data.expectFlick) {
209+ verify(listView.contentY != oldY);
210+ compare(launcher.lastSelectedApplication, "", "Launcher app clicked signal emitted even though it should only flick");
211+ } else {
212+ verify(launcher.lastSelectedApplication != "");
213+ compare(listView.contentY, oldY, "Launcher was flicked even though it should only launch an app");
214+ }
215+
216+ // Restore position on top
217+ listView.flick(0, units.gu(200));
218+ tryCompare(listView, "flicking", false)
219+ // Click somewhere in the empty space to make it hide in case it isn't
220+ mouseClick(launcher, launcher.width - units.gu(1), units.gu(1));
221+ revealer.waitUntilLauncherDisappears();
222+ }
223+ }
224+
225+ UT.UnityTestCase {
226+ id: initTestCase
227+ name: "LauncherInit"
228+ when: windowShown
229+
230+ /*
231+ * FIXME: There is a bug in ListView which makes it snap to an item
232+ * instead of the edge at startup. Enable this test once our patch for
233+ * ListView has landed upstream.
234+ * https://bugreports.qt-project.org/browse/QTBUG-32251
235+
236+ function test_initFirstUnfolded() {
237+
238+ // Make sure noone changed the height of the window. The issue this test case
239+ // is verifying only happens on certain heights of the Launcher
240+ compare(root.height, units.gu(55));
241+
242+ var listView = findChild(launcher, "launcherListView");
243+ compare(listView.contentY, -listView.topMargin, "Launcher did not start up with first item unfolded");
244+
245+ // Now do check that snapping is in fact enabled
246+ compare(listView.snapMode, ListView.SnapToItem, "Snapping is not enabled");
168 }
247 }
248+ */
169 }
249 }
170}
250}