Merge lp:~lukas-kde/unity8/dynamicLauncherIcons into lp:unity8

Proposed by Lukáš Tinkl
Status: Superseded
Proposed branch: lp:~lukas-kde/unity8/dynamicLauncherIcons
Merge into: lp:unity8
Prerequisite: lp:~mzanetti/unity8/launcher-sizing
Diff against target: 2539 lines (+1202/-374) (has conflicts)
17 files modified
data/com.canonical.Unity8.gschema.xml (+11/-0)
plugins/Unity/Launcher/launcheritem.h (+1/-1)
plugins/Unity/Launcher/launchermodel.cpp (+13/-4)
plugins/Unity/Launcher/launchermodel.h (+1/-1)
qml/Components/PhysicalKeysMapper.qml (+24/-0)
qml/Launcher/Launcher.qml (+143/-11)
qml/Launcher/LauncherDelegate.qml (+54/-20)
qml/Launcher/LauncherPanel.qml (+122/-51)
qml/Launcher/graphics/launcher-app-focus-ring.svg (+12/-0)
qml/Shell.qml (+51/-1)
qml/Stages/AbstractStage.qml (+2/-0)
qml/Stages/DesktopStage.qml (+237/-231)
tests/mocks/GSettings.1.0/fake_gsettings.cpp (+64/-0)
tests/mocks/GSettings.1.0/fake_gsettings.h (+18/-0)
tests/mocks/Unity/Launcher/MockLauncherModel.cpp (+2/-0)
tests/qmltests/Launcher/tst_Launcher.qml (+244/-18)
tests/qmltests/tst_Shell.qml (+203/-36)
Text conflict in tests/qmltests/tst_Shell.qml
To merge this branch: bzr merge lp:~lukas-kde/unity8/dynamicLauncherIcons
Reviewer Review Type Date Requested Status
Michael Zanetti (community) Needs Fixing
PS Jenkins bot (community) continuous-integration Needs Fixing
Unity8 CI Bot continuous-integration Needs Fixing
Review via email: mp+285463@code.launchpad.net

This proposal has been superseded by a proposal from 2016-03-01.

Commit message

Watch for launcher item icon changes

Description of the change

Watch for launcher item icon changes

Set a QFSFileWatcher on icons present in the launcher, and notify the views when the actual contents of those icons changes on disk.

Fixes #1543290 - Icons of apps pinned in the launcher don't update if app got updated and has a new icon

Checklist:

* Are there any related MPs required for this MP to build/function as expected? Please list.

Yes, lp:~mzanetti/unity8/launcher-sizing

* Did you perform an exploratory manual test run of your code change and any related functionality?

Yes

* Did you make sure that your branch does not contain spurious tags?

Yes

* If you changed the packaging (debian), did you subscribe the ubuntu-unity team to this MP?

Yes

* If you changed the UI, has there been a design review?

N/A

To post a comment you must log in.
Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Michael Zanetti (mzanetti) wrote :

hmm... We already have a file system watcher for launcher stuff... Check out data/unity8-filewatcher.conf

The reason for not using a QFileSystemWatcher directly for that was there is a limited number of watches that a process can set up. Can we update the icons on that UpdateLauncher call too?

review: Needs Fixing
Revision history for this message
Lukáš Tinkl (lukas-kde) wrote :

The existing watcher is observing changes in the desktop files only (applications being added/removed) using:

data/unity8-filewatcher.conf:

start on (file FILE=/home/phablet/.local/share/applications/*.desktop) or (file FILE=/usr/share/applications/*.desktop)

Here we have to watch icons' actual contents changes which is doable only from the model where we have the path to the icon (the .desktop file doesn't have the full icon path).

Revision history for this message
Michael Zanetti (mzanetti) wrote :

I don't think an app icon can change without it's desktop file being touched too. (Except, obviously, if the user replaces the file manually in the file system). But during app updates the .desktop file is always overwritten (even if sometimes with the same content) which then would always call the UpdateLauncher D-Bus method.

If possible, I'd like to try hooking into that mechanism instead of setting up more FSWatchers.

Revision history for this message
Lukáš Tinkl (lukas-kde) wrote :

OK done, reused the the existing upstart watcher. I have to reset the whole model when the refresh() method is called tho... otherwise QML won't notice the change and won't redraw the icon because the "icon" property stays the same (unlike its contents).

Revision history for this message
Michael Zanetti (mzanetti) wrote :

resetting the model will mess up scrolling positions, it would destroy opened quicklists and what not.

Which I understand that some sort of hack will be needed, I'd suggest something easier for the view. Perhaps like this?

QString tmp = m_icon;
m_icon.clear();
emit dataChanged({RoleIcon});
m_icon = tmp;
emit dataChanged({RoleIcon});

review: Needs Information
Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Lukáš Tinkl (lukas-kde) wrote :

Done, still works if I simulate the model reset by setting the icon twice

Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
2178. By Launchpad Translations on behalf of unity-team

Launchpad automatic translations update.

2179. By Ted Gould

Start the indicators after Unity8 starts instead of before
Approved by: Michael Terry, Michał Sawicz

2180. By Daniel d'Andrada

Window Size Hints
Approved by: Michał Sawicz

2181. By Nick Dedekind

Removed unused panel resources
Approved by: Albert Astals Cid

2182. By Nick Dedekind

Added support for secondary indicator actions Fixes: #1398888
Approved by: Lukáš Tinkl

2183. By Albert Astals Cid

We don't use the unity-scope-scopes anymore
 Fixes: #1439172
Approved by: Unity8 CI Bot, Michał Sawicz

2184. By Daniel d'Andrada

Set initial surface size

Removes flicker on start up in desktop mode Fixes: #1532974
Approved by: Nick Dedekind

2185. By Florian Boucault

Splash screen: delay showing the ActivityIndicator by 2 seconds.
Approved by: John Lea, Daniel d'Andrada

2186. By Albert Astals Cid

Forward the makesurevisible signal from the inner preview widget

Otherwise the OSK can cover the text field which is not what we want
Approved by: Andrea Cimitan

2187. By Andrea Cimitan

only request a bigger image if zoomable widget data property is true, otherwise just leave the screen size Fixes: #1536814
Approved by: Albert Astals Cid

2188. By Michael Zanetti

Don't eat mouse events in the edgebarrer
Approved by: Albert Astals Cid

2189. By Albert Astals Cid

Make fallback also be fallback when the image is empty and not only on error
 Fixes: #1531913
Approved by: Andrea Cimitan

2190. By Albert Astals Cid

Do not use components in card creator generated code

We can resolve it on compile time
Approved by: Andrea Cimitan

2191. By Albert Astals Cid

Visual improvements to the dash audio play cards

Approved by: Andrea Cimitan

2192. By Albert Astals Cid

Make the artShapeSize assignment be updated when using dynamic grid units

By using a binding
Approved by: Gerry Boland, Unity8 CI Bot, Andrea Cimitan

2193. By Nick Dedekind

Remove zombie if they're not animated. Fixes: #1534541
Approved by: Albert Astals Cid, Marcus Tomlinson

2194. By Lukáš Tinkl

Replace the word "phone" with a more generic term "device" in the welcome wizard Fixes: #1294732, #1543527
Approved by: Andrea Cimitan

2195. By Michael Terry

Have the tablet greeter set appropriate OSK hints if the user has a passcode instead of a passphrase. Fixes: #1544173
Approved by: Josh Arenson

2196. By Michał Sawicz

Bump unity-api dependencies
Approved by: Michael Terry

2197. By CI Train Bot Account

Releasing 8.11+16.04.20160212-0ubuntu1

2198. By CI Train Bot Account

Update translation template

2199. By CI Train Bot Account

Resync trunk.

2200. By Albert Astals Cid

Make the audio previews create a playlist
 Fixes: #1540477
Approved by: Andrea Cimitan

2201. By CI Train Bot Account

Releasing 8.11+16.04.20160216.1-0ubuntu1

2202. By CI Train Bot Account

Update translation template

2203. By CI Train Bot Account

Resync trunk.

2204. By Launchpad Translations on behalf of unity-team

Launchpad automatic translations update.

2205. By Launchpad Translations on behalf of unity-team

Launchpad automatic translations update.

2206. By Launchpad Translations on behalf of unity-team

Launchpad automatic translations update.

2207. By Michał Sawicz

Add autopilot as DEP-8 tests

2208. By Launchpad Translations on behalf of unity-team

Launchpad automatic translations update.

Revision history for this message
Michael Zanetti (mzanetti) wrote :

two small inline comments

Revision history for this message
Michael Zanetti (mzanetti) wrote :

something's wrong with merging... has some conflicts

review: Needs Fixing
2209. By Lukáš Tinkl

Watch for launcher item icon changes

2210. By Lukáš Tinkl

no cache, more fun

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'data/com.canonical.Unity8.gschema.xml'
2--- data/com.canonical.Unity8.gschema.xml 2015-11-24 17:44:18 +0000
3+++ data/com.canonical.Unity8.gschema.xml 2016-03-01 13:59:38 +0000
4@@ -27,6 +27,17 @@
5 <summary>Maximum push needed to overcome edge barrier</summary>
6 <description>How much you have to push (in grid units) the mouse against an edge barrier when sensibility is 1.</description>
7 </key>
8+ <key type="b" name="autohide-launcher">
9+ <default>false</default>
10+ <summary>Autohide the launcher</summary>
11+ <description>This will only be applied in windowed mode. In staged mode, the launcher will always hide.</description>
12+ </key>
13+ <key type="u" name="launcher-width">
14+ <default>8</default>
15+ <range min="6" max="12"/>
16+ <summary>Width of the launcher in grid units.</summary>
17+ <description>Changes the width of the launcher in all usage modes.</description>
18+ </key>
19 </schema>
20
21 <schema path="/com/canonical/unity8/greeter/" id="com.canonical.Unity8.Greeter" gettext-domain="unity8">
22
23=== modified file 'plugins/Unity/Launcher/launcheritem.h'
24--- plugins/Unity/Launcher/launcheritem.h 2015-07-23 14:13:57 +0000
25+++ plugins/Unity/Launcher/launcheritem.h 2016-03-01 13:59:38 +0000
26@@ -32,7 +32,7 @@
27 {
28 Q_OBJECT
29 public:
30- LauncherItem(const QString &appId, const QString &name, const QString &icon, QObject *parent = 0);
31+ LauncherItem(const QString &appId, const QString &name, const QString &icon, QObject *parent = nullptr);
32
33 QString appId() const override;
34 QString name() const override;
35
36=== modified file 'plugins/Unity/Launcher/launchermodel.cpp'
37--- plugins/Unity/Launcher/launchermodel.cpp 2015-09-14 09:11:08 +0000
38+++ plugins/Unity/Launcher/launchermodel.cpp 2016-03-01 13:59:38 +0000
39@@ -103,7 +103,7 @@
40 LauncherItem *item = m_list.at(index);
41 if (!item->focused()) {
42 item->setAlerting(alerting);
43- Q_EMIT dataChanged(modelIndex, modelIndex, QVector<int>() << RoleAlerting);
44+ Q_EMIT dataChanged(modelIndex, modelIndex, {RoleAlerting});
45 }
46 }
47 }
48@@ -414,10 +414,19 @@
49 } else {
50 int idx = m_list.indexOf(item);
51 item->setName(desktopFile.displayName());
52- item->setIcon(desktopFile.icon());
53 item->setPinned(item->pinned()); // update pinned text if needed
54 item->setRunning(item->running());
55- Q_EMIT dataChanged(index(idx), index(idx), {RoleName, RoleIcon, RoleRunning});
56+ Q_EMIT dataChanged(index(idx), index(idx), {RoleName, RoleRunning});
57+
58+ const QString oldIcon = item->icon();
59+ if (oldIcon == desktopFile.icon()) { // same icon file, perhaps different contents, simulate changing the icon name to force reload
60+ item->setIcon(QString());
61+ Q_EMIT dataChanged(index(idx), index(idx), {RoleIcon});
62+ }
63+
64+ // now set the icon for real
65+ item->setIcon(desktopFile.icon());
66+ Q_EMIT dataChanged(index(idx), index(idx), {RoleIcon});
67 }
68 }
69
70@@ -489,7 +498,7 @@
71 if (idx >= 0) {
72 LauncherItem *item = m_list.at(idx);
73 setAlerting(item->appId(), true);
74- Q_EMIT dataChanged(index(idx), index(idx), QVector<int>() << RoleAlerting);
75+ Q_EMIT dataChanged(index(idx), index(idx), {RoleAlerting});
76 }
77 }
78
79
80=== modified file 'plugins/Unity/Launcher/launchermodel.h'
81--- plugins/Unity/Launcher/launchermodel.h 2015-07-29 12:32:57 +0000
82+++ plugins/Unity/Launcher/launchermodel.h 2016-03-01 13:59:38 +0000
83@@ -38,7 +38,7 @@
84 Q_OBJECT
85
86 public:
87- LauncherModel(QObject *parent = 0);
88+ LauncherModel(QObject *parent = nullptr);
89 ~LauncherModel();
90
91 int rowCount(const QModelIndex &parent = QModelIndex()) const override;
92
93=== modified file 'qml/Components/PhysicalKeysMapper.qml'
94--- qml/Components/PhysicalKeysMapper.qml 2016-01-13 18:43:34 +0000
95+++ qml/Components/PhysicalKeysMapper.qml 2016-03-01 13:59:38 +0000
96@@ -42,12 +42,15 @@
97 signal screenshotTriggered;
98
99 readonly property bool altTabPressed: d.altTabPressed
100+ readonly property bool superPressed: d.superPressed
101+ readonly property bool superTabPressed: d.superTabPressed
102
103 property int powerKeyLongPressTime: 2000
104
105 // For testing. If running windowed (e.g. tryShell), Alt+Tab is taken by the
106 // running desktop, set this to true to use Ctrl+Tab instead.
107 property bool controlInsteadOfAlt: false
108+ property bool controlInsteadOfSuper: false
109
110 QtObject {
111 id: d
112@@ -59,6 +62,9 @@
113 property bool altPressed: false
114 property bool altTabPressed: false
115
116+ property bool superPressed: false
117+ property bool superTabPressed: false
118+
119 property var powerButtonPressStart: 0
120
121 // We need to eat ALT presses until we know what they're for (Alt+Tab or going to the app?)
122@@ -119,11 +125,23 @@
123 event.accepted = true;
124 d.altPressInjected = false;
125 }
126+
127+ // Adding MetaModifier here because that's what keyboards do. Pressing Super_L actually gives
128+ // Super_L + MetaModifier. This helps to make sure we only invoke superPressed if no other
129+ // Modifier is pressed too.
130+ } else if (((event.key == Qt.Key_Super_L || event.key == Qt.Key_Super_R) && event.modifiers === Qt.MetaModifier)
131+ || (root.controlInsteadOfSuper && event.key == Qt.Key_Control)
132+ ) {
133+ d.superPressed = true;
134 } else if (event.key == Qt.Key_Tab) {
135 if (d.altPressed && !d.altTabPressed) {
136 d.altTabPressed = true;
137 event.accepted = true;
138 }
139+ if (d.superPressed && !d.superTabPressed) {
140+ d.superTabPressed = true;
141+ event.accepted = true;
142+ }
143 }
144 }
145
146@@ -154,6 +172,12 @@
147 if (d.altTabPressed) {
148 event.accepted = true;
149 }
150+ } else if (event.key == Qt.Key_Super_L || event.key == Qt.Key_Super_R || (root.controlInsteadOfSuper && event.key == Qt.Key_Control)) {
151+ d.superPressed = false;
152+ if (d.superTabPressed) {
153+ d.superTabPressed = false;
154+ event.accepted = true;
155+ }
156 }
157 }
158 }
159
160=== modified file 'qml/Launcher/Launcher.qml'
161--- qml/Launcher/Launcher.qml 2016-01-19 15:26:15 +0000
162+++ qml/Launcher/Launcher.qml 2016-03-01 13:59:38 +0000
163@@ -19,21 +19,26 @@
164 import Ubuntu.Components 1.3
165 import Ubuntu.Gestures 0.1
166 import Unity.Launcher 0.1
167+import GlobalShortcut 1.0
168
169-Item {
170+FocusScope {
171 id: root
172
173 property bool autohideEnabled: false
174+ property bool lockedVisible: false
175 property bool available: true // can be used to disable all interactions
176 property alias inverted: panel.inverted
177 property bool shadeBackground: true // can be used to disable background shade when launcher is visible
178
179- property int panelWidth: units.gu(8)
180+ property int panelWidth: units.gu(10)
181 property int dragAreaWidth: units.gu(1)
182 property int minimizeDistance: units.gu(26)
183 property real progress: dragArea.dragging && dragArea.touchX > panelWidth ?
184 (width * (dragArea.touchX-panelWidth) / (width - panelWidth)) : 0
185
186+ property bool superPressed: false
187+ property bool superTabPressed: false
188+
189 readonly property bool dragging: dragArea.dragging
190 readonly property real dragDistance: dragArea.dragging ? dragArea.touchX : 0
191 readonly property real visibleWidth: panel.width + panel.x
192@@ -57,12 +62,55 @@
193 }
194 }
195
196+ onSuperPressedChanged: {
197+ if (superPressed) {
198+ superPressTimer.start();
199+ superLongPressTimer.start();
200+ } else {
201+ superPressTimer.stop();
202+ superLongPressTimer.stop();
203+ launcher.switchToNextState("");
204+ panel.shortcutHintsShown = false;
205+ }
206+ }
207+
208+ onSuperTabPressedChanged: {
209+ if (superTabPressed) {
210+ switchToNextState("visible")
211+ panel.highlightIndex = -1;
212+ root.focus = true;
213+ superPressTimer.stop();
214+ superLongPressTimer.stop();
215+ } else {
216+ if (panel.highlightIndex == -1) {
217+ showDashHome();
218+ } else if (panel.highlightIndex >= 0){
219+ launcherApplicationSelected(LauncherModel.get(panel.highlightIndex).appId);
220+ }
221+ panel.highlightIndex = -2;
222+ switchToNextState("");
223+ root.focus = false;
224+ }
225+ }
226+
227+ onLockedVisibleChanged: {
228+ if (lockedVisible && state == "") {
229+ panel.dismissTimer.stop();
230+ fadeOutAnimation.stop();
231+ switchToNextState("visible")
232+ } else if (!lockedVisible && state == "visible") {
233+ hide();
234+ }
235+ }
236+
237 function hide() {
238 switchToNextState("")
239 }
240
241 function fadeOut() {
242- fadeOutAnimation.start();
243+ if (!root.lockedVisible) {
244+ fadeOutAnimation.start();
245+ }
246 }
247
248 function switchToNextState(state) {
249@@ -90,6 +138,76 @@
250 }
251 }
252
253+ function openForKeyboardNavigation() {
254+ panel.highlightIndex = -1; // The BFB
255+ root.focus = true;
256+ switchToNextState("visible")
257+ }
258+
259+ Keys.onPressed: {
260+ switch (event.key) {
261+ case Qt.Key_Backtab:
262+ panel.highlightPrevious();
263+ event.accepted = true;
264+ break;
265+ case Qt.Key_Up:
266+ if (root.inverted) {
267+ panel.highlightNext()
268+ } else {
269+ panel.highlightPrevious();
270+ }
271+ event.accepted = true;
272+ break;
273+ case Qt.Key_Tab:
274+ panel.highlightNext();
275+ event.accepted = true;
276+ break;
277+ case Qt.Key_Down:
278+ if (root.inverted) {
279+ panel.highlightPrevious();
280+ } else {
281+ panel.highlightNext();
282+ }
283+ event.accepted = true;
284+ break;
285+ case Qt.Key_Right:
286+ panel.openQuicklist(panel.highlightIndex)
287+ event.accepted = true;
288+ break;
289+ case Qt.Key_Escape:
290+ panel.highlightIndex = -2
291+ // Falling through intentionally
292+ case Qt.Key_Enter:
293+ case Qt.Key_Return:
294+ case Qt.Key_Space:
295+ if (panel.highlightIndex == -1) {
296+ showDashHome();
297+ } else if (panel.highlightIndex >= 0) {
298+ launcherApplicationSelected(LauncherModel.get(panel.highlightIndex).appId);
299+ }
300+ root.hide();
301+ event.accepted = true;
302+ root.focus = false;
303+ }
304+ }
305+
306+ Timer {
307+ id: superPressTimer
308+ interval: 200
309+ onTriggered: {
310+ switchToNextState("visible")
311+ }
312+ }
313+
314+ Timer {
315+ id: superLongPressTimer
316+ interval: 1000
317+ onTriggered: {
318+ switchToNextState("visible")
319+ panel.shortcutHintsShown = true;
320+ }
321+ }
322+
323 Timer {
324 id: teaseTimer
325 interval: mode == "teasing" ? 200 : 300
326@@ -106,6 +224,13 @@
327 interval: 1
328 property string nextState: ""
329 onTriggered: {
330+ if (root.lockedVisible && nextState == "") {
331+ // Due to binding updates when switching between modes
332+ // it could happen that our request to show will be overwritten
333+ // with a hide request. Rewrite it when we know hiding is not allowed.
334+ nextState = "visible"
335+ }
336+
337 // switching to an intermediate state here to make sure all the
338 // values are restored, even if we were already in the target state
339 root.state = "tmp"
340@@ -151,7 +276,7 @@
341
342 MouseArea {
343 id: launcherDragArea
344- enabled: root.available && (root.state == "visible" || root.state == "visibleTemporary")
345+ enabled: root.available && (root.state == "visible" || root.state == "visibleTemporary") && !root.lockedVisible
346 anchors.fill: panel
347 anchors.rightMargin: -units.gu(2)
348 drag {
349@@ -172,9 +297,10 @@
350 InverseMouseArea {
351 id: closeMouseArea
352 anchors.fill: panel
353- enabled: root.shadeBackground && root.state == "visible"
354+ enabled: root.shadeBackground && root.state == "visible" && (!root.lockedVisible || panel.highlightIndex >= -1)
355 visible: enabled
356 onPressed: {
357+ panel.highlightIndex = -2
358 root.hide();
359 }
360 }
361@@ -183,7 +309,7 @@
362 id: backgroundShade
363 anchors.fill: parent
364 color: "black"
365- opacity: root.shadeBackground && root.state == "visible" ? 0.6 : 0
366+ opacity: root.shadeBackground && root.state == "visible" && !root.lockedVisible ? 0.6 : 0
367
368 Behavior on opacity { NumberAnimation { duration: UbuntuAnimation.BriskDuration } }
369 }
370@@ -227,11 +353,11 @@
371 Connections {
372 target: panel.dismissTimer
373 onTriggered: {
374- if (root.autohideEnabled) {
375+ if (root.autohideEnabled && !root.lockedVisible) {
376 if (!panel.preventHiding) {
377- root.state = ""
378+ root.hide();
379 } else {
380- panel.dismissTimer.restart()
381+ dismissTimer.restart()
382 }
383 }
384 }
385@@ -240,11 +366,11 @@
386 property bool animate: true
387
388 onApplicationSelected: {
389- root.state = ""
390+ root.hide();
391 launcherApplicationSelected(appId)
392 }
393 onShowDashHome: {
394- root.state = ""
395+ root.hide();
396 root.showDashHome();
397 }
398
399@@ -254,6 +380,12 @@
400 }
401 }
402
403+ onKbdNavigationCancelled: {
404+ panel.highlightIndex = -2;
405+ root.hide();
406+ root.focus = false;
407+ }
408+
409 Behavior on x {
410 enabled: !dragArea.dragging && !launcherDragArea.drag.active && panel.animate;
411 NumberAnimation {
412
413=== modified file 'qml/Launcher/LauncherDelegate.qml'
414--- qml/Launcher/LauncherDelegate.qml 2015-11-19 16:55:31 +0000
415+++ qml/Launcher/LauncherDelegate.qml 2016-03-01 13:59:38 +0000
416@@ -20,6 +20,7 @@
417 Item {
418 id: root
419
420+ property int itemIndex: 0
421 property string iconName
422 property int count: 0
423 property bool countVisible: false
424@@ -29,10 +30,12 @@
425 property real maxAngle: 0
426 property bool inverted: false
427 property bool alerting: false
428- readonly property alias wiggling: wiggleAnim.running
429+ property bool highlighted: false
430+ property bool shortcutHintShown: false
431
432 readonly property int effectiveHeight: Math.cos(angle * Math.PI / 180) * itemHeight
433 readonly property real foldedHeight: Math.cos(maxAngle * Math.PI / 180) * itemHeight
434+ readonly property alias wiggling: wiggleAnim.running
435
436 property int itemWidth
437 property int itemHeight
438@@ -121,14 +124,25 @@
439
440 Item {
441 id: iconItem
442- width: parent.itemWidth + units.gu(1)
443+ width: root.width
444 height: parent.itemHeight + units.gu(1)
445 anchors.centerIn: parent
446
447+ Image {
448+ objectName: "focusRing"
449+ anchors.centerIn: iconShape
450+ height: width * 15 / 16
451+ width: iconShape.width + units.gu(1)
452+ source: "graphics/launcher-app-focus-ring.svg"
453+ sourceSize.width: width
454+ sourceSize.height: height
455+ visible: root.highlighted
456+ }
457+
458 ProportionalShape {
459 id: iconShape
460 anchors.centerIn: parent
461- width: parent.width - units.gu(2)
462+ width: root.itemWidth
463 aspect: UbuntuShape.DropShadow
464 source: Image {
465 id: iconImage
466@@ -144,7 +158,8 @@
467 anchors {
468 right: parent.right
469 bottom: parent.bottom
470- margins: units.dp(3)
471+ rightMargin: (iconItem.width - root.itemWidth) / 2 - units.dp(2)
472+ margins: units.dp(5)
473 }
474 width: Math.min(root.itemWidth, Math.max(units.gu(2), countLabel.implicitWidth + units.gu(1)))
475 height: units.gu(2)
476@@ -172,16 +187,11 @@
477 id: progressOverlay
478 objectName: "progressOverlay"
479
480- anchors {
481- left: iconItem.left
482- right: iconItem.right
483- verticalCenter: parent.verticalCenter
484- leftMargin: units.gu(1.5)
485- rightMargin: units.gu(1.5)
486- }
487+ anchors.centerIn: parent
488+ width: root.itemWidth * .8
489 height: units.gu(1)
490 visible: root.progress > -1
491- color: UbuntuColors.darkGrey
492+ backgroundColor: UbuntuColors.darkGrey
493 borderSource: "none"
494
495 Item {
496@@ -199,32 +209,56 @@
497 top: parent.top
498 bottom: parent.bottom
499 }
500- color: "white"
501+ backgroundColor: "white"
502 borderSource: "none"
503 width: progressOverlay.width
504 }
505 }
506 }
507
508- Image {
509- objectName: "runningHighlight"
510+ Column {
511 anchors {
512 left: parent.left
513 verticalCenter: parent.verticalCenter
514 }
515- visible: root.itemRunning
516- rotation: 180
517- source: "graphics/focused_app_arrow.png"
518+ spacing: units.gu(.5)
519+ Repeater {
520+ model: 1 // TODO: This should be "Math.min(3, app.surfaceCount)" once we have multiple surfaces
521+ Rectangle {
522+ objectName: "runningHighlight" + index
523+ width: units.gu(0.25)
524+ height: units.gu(.5)
525+ color: "white"
526+ visible: root.itemRunning
527+ }
528+ }
529 }
530
531- Image {
532+ Rectangle {
533 objectName: "focusedHighlight"
534 anchors {
535 right: parent.right
536 verticalCenter: parent.verticalCenter
537 }
538+ width: units.gu(0.25)
539+ height: units.gu(.5)
540+ color: "white"
541 visible: root.itemFocused
542- source: "graphics/focused_app_arrow.png"
543+ }
544+
545+ Rectangle {
546+ objectName: "shortcutHint"
547+ anchors.centerIn: parent
548+ width: units.gu(3)
549+ height: width
550+ color: "#E0292929"
551+ visible: root.shortcutHintShown
552+ Label {
553+ anchors.centerIn: parent
554+ text: (itemIndex + 1) % 10
555+ color: "white"
556+ font.weight: Font.DemiBold
557+ }
558 }
559 }
560
561
562=== modified file 'qml/Launcher/LauncherPanel.qml'
563--- qml/Launcher/LauncherPanel.qml 2016-01-11 17:38:19 +0000
564+++ qml/Launcher/LauncherPanel.qml 2016-03-01 13:59:38 +0000
565@@ -19,12 +19,13 @@
566 import Ubuntu.Components.ListItems 1.3 as ListItems
567 import Unity.Launcher 0.1
568 import Ubuntu.Components.Popups 1.3
569+import GlobalShortcut 1.0
570 import "../Components/ListItems"
571 import "../Components/"
572
573 Rectangle {
574 id: root
575- color: "#B2000000"
576+ color: "#E0292929"
577
578 rotation: inverted ? 180 : 0
579
580@@ -33,11 +34,13 @@
581 property bool dragging: false
582 property bool moving: launcherListView.moving || launcherListView.flicking
583 property bool preventHiding: moving || dndArea.draggedIndex >= 0 || quickList.state === "open" || dndArea.pressed
584- || mouseEventEater.containsMouse || dashItem.hovered
585- property int highlightIndex: -1
586+ || mouseEventEater.containsMouse || dashItem.hovered
587+ property int highlightIndex: -2
588+ property bool shortcutHintsShown: false
589
590 signal applicationSelected(string appId)
591 signal showDashHome()
592+ signal kbdNavigationCancelled()
593
594 onXChanged: {
595 if (quickList.state == "open") {
596@@ -45,6 +48,26 @@
597 }
598 }
599
600+ function highlightNext() {
601+ highlightIndex++;
602+ if (highlightIndex >= launcherListView.count) {
603+ highlightIndex = -1;
604+ }
605+ launcherListView.moveToIndex(Math.max(highlightIndex, 0));
606+ }
607+ function highlightPrevious() {
608+ highlightIndex--;
609+ if (highlightIndex <= -2) {
610+ highlightIndex = launcherListView.count - 1;
611+ }
612+ launcherListView.moveToIndex(Math.max(highlightIndex, 0));
613+ }
614+ function openQuicklist(index) {
615+ quickList.open(index);
616+ quickList.selectedIndex = 0;
617+ quickList.focus = true;
618+ }
619+
620 MouseArea {
621 id: mouseEventEater
622 anchors.fill: parent
623@@ -57,24 +80,16 @@
624 fill: parent
625 }
626
627- Item {
628+ Rectangle {
629 objectName: "buttonShowDashHome"
630 width: parent.width
631- height: units.gu(7)
632- clip: true
633-
634- UbuntuShape {
635- anchors {
636- fill: parent
637- topMargin: -units.gu(2)
638- }
639- aspect: UbuntuShape.Flat
640- backgroundColor: UbuntuColors.orange
641- }
642+ height: width * .9
643+ color: UbuntuColors.orange
644+ readonly property bool highlighted: root.highlightIndex == -1;
645
646 Image {
647 objectName: "dashItem"
648- width: units.gu(5)
649+ width: parent.width * .6
650 height: width
651 anchors.centerIn: parent
652 source: "graphics/home.png"
653@@ -85,6 +100,14 @@
654 anchors.fill: parent
655 onClicked: root.showDashHome()
656 }
657+ Rectangle {
658+ objectName: "bfbFocusHighlight"
659+ anchors.fill: parent
660+ border.color: "white"
661+ border.width: units.dp(1)
662+ color: "transparent"
663+ visible: parent.highlighted
664+ }
665 }
666
667 Item {
668@@ -102,10 +125,8 @@
669 objectName: "launcherListView"
670 anchors {
671 fill: parent
672- topMargin: -extensionSize + units.gu(0.5)
673- bottomMargin: -extensionSize + units.gu(1)
674- leftMargin: units.gu(0.5)
675- rightMargin: units.gu(0.5)
676+ topMargin: -extensionSize + width * .15
677+ bottomMargin: -extensionSize + width * .15
678 }
679 topMargin: extensionSize
680 bottomMargin: extensionSize
681@@ -140,11 +161,11 @@
682 }
683
684 // The height of the area where icons start getting folded
685- property int foldingStartHeight: units.gu(6.5)
686+ property int foldingStartHeight: itemHeight
687 // The height of the area where the items reach the final folding angle
688 property int foldingStopHeight: foldingStartHeight - itemHeight - spacing
689- property int itemWidth: units.gu(7)
690- property int itemHeight: units.gu(6.5)
691+ property int itemWidth: width * .75
692+ property int itemHeight: itemWidth * 15 / 16 + units.gu(1)
693 property int clickFlickSpeed: units.gu(60)
694 property int draggedIndex: dndArea.draggedIndex
695 property real realContentY: contentY - originY + topMargin
696@@ -172,12 +193,24 @@
697
698 UbuntuNumberAnimation {
699 id: moveAnimation
700+ objectName: "moveAnimation"
701 target: launcherListView
702 property: "contentY"
703 function moveTo(contentY) {
704 from = launcherListView.contentY;
705 to = contentY;
706- start();
707+ restart();
708+ }
709+ }
710+ function moveToIndex(index) {
711+ var totalItemHeight = launcherListView.itemHeight + launcherListView.spacing
712+ var itemPosition = index * totalItemHeight;
713+ var height = launcherListView.height - launcherListView.topMargin - launcherListView.bottomMargin
714+ var distanceToEnd = index == 0 || index == launcherListView.count - 1 ? 0 : totalItemHeight
715+ if (itemPosition + totalItemHeight + distanceToEnd > launcherListView.contentY + launcherListView.originY + launcherListView.topMargin + height) {
716+ moveAnimation.moveTo(itemPosition + launcherListView.itemHeight - launcherListView.topMargin - height + distanceToEnd - launcherListView.originY);
717+ } else if (itemPosition - distanceToEnd < launcherListView.contentY - launcherListView.originY + launcherListView.topMargin) {
718+ moveAnimation.moveTo(itemPosition - distanceToEnd - launcherListView.topMargin + launcherListView.originY);
719 }
720 }
721
722@@ -192,9 +225,10 @@
723 // the right app when running autopilot tests for
724 // multiple apps.
725 readonly property string appId: model.appId
726+ itemIndex: index
727 itemHeight: launcherListView.itemHeight
728 itemWidth: launcherListView.itemWidth
729- width: itemWidth
730+ width: parent.width
731 height: itemHeight
732 iconName: model.icon
733 count: model.count
734@@ -204,6 +238,8 @@
735 itemFocused: model.focused
736 inverted: root.inverted
737 alerting: model.alerting
738+ highlighted: root.highlightIndex == index
739+ shortcutHintShown: root.shortcutHintsShown && index <= 9
740 z: -Math.abs(offset)
741 maxAngle: 55
742 property bool dragging: false
743@@ -241,14 +277,7 @@
744 onAlertingChanged: {
745 if(alerting) {
746 if (!dragging && (launcherListView.peekingIndex === -1 || launcher.visibleWidth > 0)) {
747- var itemPosition = index * launcherListView.itemHeight;
748- var height = launcherListView.height - launcherListView.topMargin - launcherListView.bottomMargin
749- var distanceToEnd = index == 0 || index == launcherListView.count - 1 ? 0 : launcherListView.itemHeight
750- if (itemPosition + launcherListView.itemHeight + distanceToEnd > launcherListView.contentY + launcherListView.topMargin + height) {
751- moveAnimation.moveTo(itemPosition + launcherListView.itemHeight - launcherListView.topMargin - height + distanceToEnd);
752- } else if (itemPosition - distanceToEnd < launcherListView.contentY + launcherListView.topMargin) {
753- moveAnimation.moveTo(itemPosition - distanceToEnd - launcherListView.topMargin);
754- }
755+ launcherListView.moveToIndex(index)
756 if (!dragging && launcher.state !== "visible") {
757 peekingAnimation.start()
758 }
759@@ -402,10 +431,7 @@
760
761 if (mouse.button & Qt.RightButton) { // context menu
762 // Opening QuickList
763- quickList.item = clickedItem;
764- quickList.model = launcherListView.model.get(index).quickList;
765- quickList.appId = launcherListView.model.get(index).appId;
766- quickList.state = "open";
767+ quickList.open(index);
768 return;
769 }
770
771@@ -413,10 +439,8 @@
772
773 // First/last item do the scrolling at more than 12 degrees
774 if (index == 0 || index == launcherListView.count - 1) {
775- if (clickedItem.angle > 12) {
776- launcherListView.flick(0, -launcherListView.clickFlickSpeed);
777- } else if (clickedItem.angle < -12) {
778- launcherListView.flick(0, launcherListView.clickFlickSpeed);
779+ if (clickedItem.angle > 12 || clickedItem.angle < -12) {
780+ launcherListView.moveToIndex(index);
781 } else {
782 root.applicationSelected(LauncherModel.get(index).appId);
783 }
784@@ -424,10 +448,8 @@
785 }
786
787 // the rest launches apps up to an angle of 30 degrees
788- if (clickedItem.angle > 30) {
789- launcherListView.flick(0, -launcherListView.clickFlickSpeed);
790- } else if (clickedItem.angle < -30) {
791- launcherListView.flick(0, launcherListView.clickFlickSpeed);
792+ if (clickedItem.angle > 30 || clickedItem.angle < -30) {
793+ launcherListView.moveToIndex(index);
794 } else {
795 root.applicationSelected(LauncherModel.get(index).appId);
796 }
797@@ -481,11 +503,7 @@
798
799 draggedIndex = Math.floor((mouse.y + launcherListView.realContentY) / launcherListView.realItemHeight);
800
801- // Opening QuickList
802- quickList.item = selectedItem;
803- quickList.model = launcherListView.model.get(draggedIndex).quickList;
804- quickList.appId = launcherListView.model.get(draggedIndex).appId;
805- quickList.state = "open";
806+ quickList.open(draggedIndex)
807
808 launcherListView.interactive = false
809
810@@ -644,7 +662,9 @@
811 enabled: quickList.state == "open" || pressed
812
813 onClicked: {
814- quickList.state = ""
815+ quickList.state = "";
816+ quickList.focus = false;
817+ root.kbdNavigationCancelled();
818 }
819
820 // Forward for dragging to work when quickList is open
821@@ -693,12 +713,60 @@
822 property var model
823 property string appId
824 property var item
825+ property int selectedIndex: -1
826+
827+ Keys.onPressed: {
828+ switch (event.key) {
829+ case Qt.Key_Down:
830+ selectedIndex++;
831+ if (selectedIndex >= popoverRepeater.count) {
832+ selectedIndex = 0;
833+ }
834+ event.accepted = true;
835+ break;
836+ case Qt.Key_Up:
837+ selectedIndex--;
838+ if (selectedIndex < 0) {
839+ selectedIndex = popoverRepeater.count - 1;
840+ }
841+ event.accepted = true;
842+ break;
843+ case Qt.Key_Left:
844+ case Qt.Key_Escape:
845+ quickList.selectedIndex = -1;
846+ quickList.focus = false;
847+ quickList.state = ""
848+ event.accepted = true;
849+ break;
850+ case Qt.Key_Enter:
851+ case Qt.Key_Return:
852+ case Qt.Key_Space:
853+ if (quickList.selectedIndex >= 0) {
854+ LauncherModel.quickListActionInvoked(quickList.appId, quickList.selectedIndex)
855+ }
856+ quickList.selectedIndex = -1;
857+ quickList.focus = false;
858+ quickList.state = ""
859+ root.kbdNavigationCancelled();
860+ event.accepted = true;
861+ break;
862+ }
863+ }
864
865 // internal
866 property int itemCenter: item ? root.mapFromItem(quickList.item).y + (item.height / 2) + quickList.item.offset : units.gu(1)
867 property int offset: itemCenter + (height/2) + units.gu(1) > parent.height ? -itemCenter - (height/2) - units.gu(1) + parent.height :
868 itemCenter - (height/2) < units.gu(1) ? (height/2) - itemCenter + units.gu(1) : 0
869
870+ function open(index) {
871+ var itemPosition = index * launcherListView.itemHeight;
872+ var height = launcherListView.height - launcherListView.topMargin - launcherListView.bottomMargin
873+ item = launcherListView.itemAt(launcherListView.width / 2, itemPosition + launcherListView.itemHeight / 2);
874+ quickList.model = launcherListView.model.get(index).quickList;
875+ quickList.appId = launcherListView.model.get(index).appId;
876+ quickList.state = "open";
877+ }
878+
879 Column {
880 id: quickListColumn
881 width: parent.width
882@@ -712,6 +780,7 @@
883 objectName: "quickListEntry" + index
884 text: (model.clickable ? "" : "<b>") + model.label + (model.clickable ? "" : "</b>")
885 highlightWhenPressed: model.clickable
886+ selected: index === quickList.selectedIndex
887
888 // FIXME: This is a workaround for the theme not being context sensitive. I.e. the
889 // ListItems don't know that they are sitting in a themed Popover where the color
890@@ -727,6 +796,8 @@
891 // Unsetting model to prevent showing changing entries during fading out
892 // that may happen because of triggering an action.
893 LauncherModel.quickListActionInvoked(quickList.appId, index);
894+ quickList.focus = false;
895+ root.kbdNavigationCancelled();
896 quickList.model = undefined;
897 }
898 }
899
900=== added file 'qml/Launcher/graphics/launcher-app-focus-ring.svg'
901--- qml/Launcher/graphics/launcher-app-focus-ring.svg 1970-01-01 00:00:00 +0000
902+++ qml/Launcher/graphics/launcher-app-focus-ring.svg 2016-03-01 13:59:38 +0000
903@@ -0,0 +1,12 @@
904+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
905+<svg width="172px" height="163px" viewBox="0 0 172 163" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
906+ <!-- Generator: Sketch 3.4.4 (17249) - http://www.bohemiancoding.com/sketch -->
907+ <title>Shape</title>
908+ <desc>Created with Sketch.</desc>
909+ <defs></defs>
910+ <g id="•-Launcher" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
911+ <g id="Artboard-9" sketch:type="MSArtboardGroup" transform="translate(-163.000000, -1436.000000)" fill="#E95420">
912+ <path d="M221.983432,1440 L221.983432,1440 C195.6127,1440 184.708233,1442.4723 177.107949,1450.10734 C169.476819,1457.77336 167,1468.79245 167,1495.3481 L167,1538.9019 C167,1565.45755 169.476819,1576.47664 177.107949,1584.14266 C184.708233,1591.7777 195.6127,1594.25 221.983432,1594.25 L276.016868,1594.25 C302.387595,1594.25 313.291998,1591.77771 320.892221,1584.14264 C328.523252,1576.47663 331,1565.45769 331,1538.9019 L331,1495.3481 C331,1468.79231 328.523252,1457.77337 320.892221,1450.10736 C313.291998,1442.47229 302.387595,1440 276.016868,1440 L221.983432,1440 Z M221.983432,1436 L276.016868,1436 C302.345315,1436 314.848953,1438.36655 323.727108,1447.2854 C332.633306,1456.23243 335,1468.85167 335,1495.3481 L335,1538.9019 C335,1565.39833 332.633306,1578.01757 323.727108,1586.9646 C314.848953,1595.88345 302.345315,1598.25 276.016868,1598.25 L221.983432,1598.25 C195.654985,1598.25 183.151291,1595.88345 174.273077,1586.96463 C165.366772,1578.0176 163,1565.39822 163,1538.9019 L163,1495.3481 C163,1468.85178 165.366772,1456.2324 174.273077,1447.28537 C183.151291,1438.36655 195.654985,1436 221.983432,1436 L221.983432,1436 Z" id="Shape" sketch:type="MSShapeGroup"></path>
913+ </g>
914+ </g>
915+</svg>
916\ No newline at end of file
917
918=== modified file 'qml/Shell.qml'
919--- qml/Shell.qml 2016-01-28 18:25:14 +0000
920+++ qml/Shell.qml 2016-03-01 13:59:38 +0000
921@@ -25,6 +25,7 @@
922 import Unity.Connectivity 0.1
923 import Unity.Launcher 0.1
924 import GlobalShortcut 1.0 // has to be before Utils, because of WindowKeysFilter
925+import GSettings 1.0
926 import Utils 0.1
927 import Powerd 0.1
928 import SessionBroadcast 0.1
929@@ -187,6 +188,11 @@
930 }
931 }
932
933+ GSettings {
934+ id: settings
935+ schema.id: "com.canonical.Unity8"
936+ }
937+
938 Item {
939 id: stages
940 objectName: "stages"
941@@ -343,6 +349,11 @@
942 property: "altTabPressed"
943 value: physicalKeysMapper.altTabPressed
944 }
945+ Binding {
946+ target: applicationsDisplayLoader.item
947+ property: "leftMargin"
948+ value: launcher.lockedVisible ? launcher.panelWidth: 0
949+ }
950 }
951
952 Tutorial {
953@@ -373,7 +384,11 @@
954 InputMethod {
955 id: inputMethod
956 objectName: "inputMethod"
957- anchors { fill: parent; topMargin: panel.panelHeight }
958+ anchors {
959+ fill: parent
960+ topMargin: panel.panelHeight
961+ leftMargin: launcher.lockedVisible ? launcher.panelWidth : 0
962+ }
963 z: notifications.useModal || panel.indicators.shown || wizard.active ? overlay.z + 1 : overlay.z - 1
964 }
965
966@@ -557,6 +572,10 @@
967 && !greeter.hasLockedApp
968 inverted: shell.usageScenario !== "desktop"
969 shadeBackground: !tutorial.running
970+ superPressed: physicalKeysMapper.superPressed
971+ superTabPressed: physicalKeysMapper.superTabPressed
972+ panelWidth: units.gu(settings.launcherWidth)
973+ lockedVisible: shell.usageScenario == "desktop" && !settings.autohideLauncher && !panel.fullscreenMode
974
975 onShowDashHome: showHome()
976 onDash: showDash()
977@@ -576,6 +595,37 @@
978 panel.indicators.hide()
979 }
980 }
981+ onFocusChanged: {
982+ if (!focus) {
983+ applicationsDisplayLoader.focus = true;
984+ }
985+ }
986+
987+ GlobalShortcut {
988+ shortcut: Qt.AltModifier | Qt.Key_F1
989+ onTriggered: {
990+ launcher.openForKeyboardNavigation();
991+ }
992+ }
993+ GlobalShortcut {
994+ shortcut: Qt.MetaModifier | Qt.Key_0
995+ onTriggered: {
996+ if (LauncherModel.get(9)) {
997+ activateApplication(LauncherModel.get(9).appId);
998+ }
999+ }
1000+ }
1001+ Repeater {
1002+ model: 9
1003+ GlobalShortcut {
1004+ shortcut: Qt.MetaModifier | (Qt.Key_1 + index)
1005+ onTriggered: {
1006+ if (LauncherModel.get(index)) {
1007+ activateApplication(LauncherModel.get(index).appId);
1008+ }
1009+ }
1010+ }
1011+ }
1012 }
1013
1014 Wizard {
1015
1016=== modified file 'qml/Stages/AbstractStage.qml'
1017--- qml/Stages/AbstractStage.qml 2016-01-14 13:03:20 +0000
1018+++ qml/Stages/AbstractStage.qml 2016-03-01 13:59:38 +0000
1019@@ -39,6 +39,8 @@
1020 property int shellOrientationAngle
1021 property bool spreadEnabled: true // If false, animations and right edge will be disabled
1022 property bool suspended
1023+ // A Stage should paint a wallpaper etc over its full size but not use the margins for window placement
1024+ property int leftMargin: 0
1025
1026 // To be read from outside
1027 property var mainApp: null
1028
1029=== modified file 'qml/Stages/DesktopStage.qml'
1030--- qml/Stages/DesktopStage.qml 2016-02-03 14:00:47 +0000
1031+++ qml/Stages/DesktopStage.qml 2016-03-01 13:59:38 +0000
1032@@ -224,11 +224,9 @@
1033 PanelState.dropShadow = false;
1034 }
1035
1036- FocusScope {
1037- id: appContainer
1038- objectName: "appContainer"
1039+ Item {
1040+ id: stageContainer
1041 anchors.fill: parent
1042- focus: spread.state !== "altTab"
1043
1044 CrossFadeImage {
1045 id: wallpaper
1046@@ -238,236 +236,244 @@
1047 fillMode: Image.PreserveAspectCrop
1048 }
1049
1050- Repeater {
1051- id: appRepeater
1052- model: ApplicationManager
1053- objectName: "appRepeater"
1054-
1055- delegate: FocusScope {
1056- id: appDelegate
1057- objectName: "appDelegate_" + appId
1058- // z might be overriden in some cases by effects, but we need z ordering
1059- // to calculate occlusion detection
1060- property int normalZ: ApplicationManager.count - index
1061- z: normalZ
1062- y: PanelState.panelHeight
1063- focus: appId === priv.focusedAppId
1064- width: decoratedWindow.width
1065- height: decoratedWindow.height
1066- property alias requestedWidth: decoratedWindow.requestedWidth
1067- property alias requestedHeight: decoratedWindow.requestedHeight
1068- property alias minimumWidth: decoratedWindow.minimumWidth
1069- property alias minimumHeight: decoratedWindow.minimumHeight
1070- property alias maximumWidth: decoratedWindow.maximumWidth
1071- property alias maximumHeight: decoratedWindow.maximumHeight
1072- property alias widthIncrement: decoratedWindow.widthIncrement
1073- property alias heightIncrement: decoratedWindow.heightIncrement
1074-
1075- QtObject {
1076- id: appDelegatePrivate
1077- property bool maximized: false
1078- property bool maximizedLeft: false
1079- property bool maximizedRight: false
1080- property bool minimized: false
1081- }
1082- readonly property alias maximized: appDelegatePrivate.maximized
1083- readonly property alias maximizedLeft: appDelegatePrivate.maximizedLeft
1084- readonly property alias maximizedRight: appDelegatePrivate.maximizedRight
1085- readonly property alias minimized: appDelegatePrivate.minimized
1086-
1087- readonly property string appId: model.appId
1088- property bool animationsEnabled: true
1089- property alias title: decoratedWindow.title
1090- readonly property string appName: model.name
1091- property bool visuallyMaximized: false
1092- property bool visuallyMinimized: false
1093-
1094- onFocusChanged: {
1095- if (focus && ApplicationManager.focusedApplicationId !== appId) {
1096+ FocusScope {
1097+ id: appContainer
1098+ objectName: "appContainer"
1099+ anchors.fill: parent
1100+ anchors.leftMargin: root.leftMargin
1101+ focus: spread.state !== "altTab"
1102+
1103+ Repeater {
1104+ id: appRepeater
1105+ model: ApplicationManager
1106+ objectName: "appRepeater"
1107+
1108+ delegate: FocusScope {
1109+ id: appDelegate
1110+ objectName: "appDelegate_" + appId
1111+ // z might be overriden in some cases by effects, but we need z ordering
1112+ // to calculate occlusion detection
1113+ property int normalZ: ApplicationManager.count - index
1114+ z: normalZ
1115+ y: PanelState.panelHeight
1116+ focus: appId === priv.focusedAppId
1117+ width: decoratedWindow.width
1118+ height: decoratedWindow.height
1119+ property alias requestedWidth: decoratedWindow.requestedWidth
1120+ property alias requestedHeight: decoratedWindow.requestedHeight
1121+ property alias minimumWidth: decoratedWindow.minimumWidth
1122+ property alias minimumHeight: decoratedWindow.minimumHeight
1123+ property alias maximumWidth: decoratedWindow.maximumWidth
1124+ property alias maximumHeight: decoratedWindow.maximumHeight
1125+ property alias widthIncrement: decoratedWindow.widthIncrement
1126+ property alias heightIncrement: decoratedWindow.heightIncrement
1127+
1128+ QtObject {
1129+ id: appDelegatePrivate
1130+ property bool maximized: false
1131+ property bool maximizedLeft: false
1132+ property bool maximizedRight: false
1133+ property bool minimized: false
1134+ }
1135+ readonly property alias maximized: appDelegatePrivate.maximized
1136+ readonly property alias maximizedLeft: appDelegatePrivate.maximizedLeft
1137+ readonly property alias maximizedRight: appDelegatePrivate.maximizedRight
1138+ readonly property alias minimized: appDelegatePrivate.minimized
1139+
1140+ readonly property string appId: model.appId
1141+ property bool animationsEnabled: true
1142+ property alias title: decoratedWindow.title
1143+ readonly property string appName: model.name
1144+ property bool visuallyMaximized: false
1145+ property bool visuallyMinimized: false
1146+
1147+ onFocusChanged: {
1148+ if (focus && ApplicationManager.focusedApplicationId !== appId) {
1149+ ApplicationManager.focusApplication(appId);
1150+ }
1151+ }
1152+
1153+ onVisuallyMaximizedChanged: priv.updateForegroundMaximizedApp()
1154+
1155+ visible: !visuallyMinimized &&
1156+ !greeter.fullyShown &&
1157+ (priv.foregroundMaximizedAppZ === -1 || priv.foregroundMaximizedAppZ <= z) ||
1158+ (spread.state == "altTab" && index === spread.highlightedIndex)
1159+
1160+ Binding {
1161+ target: ApplicationManager.get(index)
1162+ property: "requestedState"
1163+ // TODO: figure out some lifecycle policy, like suspending minimized apps
1164+ // if running on a tablet or something.
1165+ // TODO: If the device has a dozen suspended apps because it was running
1166+ // in staged mode, when it switches to Windowed mode it will suddenly
1167+ // resume all those apps at once. We might want to avoid that.
1168+ value: ApplicationInfoInterface.RequestedRunning // Always running for now
1169+ }
1170+
1171+ function maximize(animated) {
1172+ animationsEnabled = (animated === undefined) || animated;
1173+ appDelegatePrivate.minimized = false;
1174+ appDelegatePrivate.maximized = true;
1175+ appDelegatePrivate.maximizedLeft = false;
1176+ appDelegatePrivate.maximizedRight = false;
1177+ }
1178+ function maximizeLeft() {
1179+ appDelegatePrivate.minimized = false;
1180+ appDelegatePrivate.maximized = false;
1181+ appDelegatePrivate.maximizedLeft = true;
1182+ appDelegatePrivate.maximizedRight = false;
1183+ }
1184+ function maximizeRight() {
1185+ appDelegatePrivate.minimized = false;
1186+ appDelegatePrivate.maximized = false;
1187+ appDelegatePrivate.maximizedLeft = false;
1188+ appDelegatePrivate.maximizedRight = true;
1189+ }
1190+ function minimize(animated) {
1191+ animationsEnabled = (animated === undefined) || animated;
1192+ appDelegatePrivate.minimized = true;
1193+ }
1194+ function restoreFromMaximized(animated) {
1195+ animationsEnabled = (animated === undefined) || animated;
1196+ appDelegatePrivate.minimized = false;
1197+ appDelegatePrivate.maximized = false;
1198+ appDelegatePrivate.maximizedLeft = false;
1199+ appDelegatePrivate.maximizedRight = false;
1200+ }
1201+ function restore(animated) {
1202+ animationsEnabled = (animated === undefined) || animated;
1203+ appDelegatePrivate.minimized = false;
1204+ if (maximized)
1205+ maximize();
1206+ else if (maximizedLeft)
1207+ maximizeLeft();
1208+ else if (maximizedRight)
1209+ maximizeRight();
1210 ApplicationManager.focusApplication(appId);
1211 }
1212- }
1213-
1214- onVisuallyMaximizedChanged: priv.updateForegroundMaximizedApp()
1215-
1216- visible: !visuallyMinimized &&
1217- !greeter.fullyShown &&
1218- (priv.foregroundMaximizedAppZ === -1 || priv.foregroundMaximizedAppZ <= z) ||
1219- (spread.state == "altTab" && index === spread.highlightedIndex)
1220-
1221- Binding {
1222- target: ApplicationManager.get(index)
1223- property: "requestedState"
1224- // TODO: figure out some lifecycle policy, like suspending minimized apps
1225- // if running on a tablet or something.
1226- // TODO: If the device has a dozen suspended apps because it was running
1227- // in staged mode, when it switches to Windowed mode it will suddenly
1228- // resume all those apps at once. We might want to avoid that.
1229- value: ApplicationInfoInterface.RequestedRunning // Always running for now
1230- }
1231-
1232- function maximize(animated) {
1233- animationsEnabled = (animated === undefined) || animated;
1234- appDelegatePrivate.minimized = false;
1235- appDelegatePrivate.maximized = true;
1236- appDelegatePrivate.maximizedLeft = false;
1237- appDelegatePrivate.maximizedRight = false;
1238- }
1239- function maximizeLeft() {
1240- appDelegatePrivate.minimized = false;
1241- appDelegatePrivate.maximized = false;
1242- appDelegatePrivate.maximizedLeft = true;
1243- appDelegatePrivate.maximizedRight = false;
1244- }
1245- function maximizeRight() {
1246- appDelegatePrivate.minimized = false;
1247- appDelegatePrivate.maximized = false;
1248- appDelegatePrivate.maximizedLeft = false;
1249- appDelegatePrivate.maximizedRight = true;
1250- }
1251- function minimize(animated) {
1252- animationsEnabled = (animated === undefined) || animated;
1253- appDelegatePrivate.minimized = true;
1254- }
1255- function restoreFromMaximized(animated) {
1256- animationsEnabled = (animated === undefined) || animated;
1257- appDelegatePrivate.minimized = false;
1258- appDelegatePrivate.maximized = false;
1259- appDelegatePrivate.maximizedLeft = false;
1260- appDelegatePrivate.maximizedRight = false;
1261- }
1262- function restore(animated) {
1263- animationsEnabled = (animated === undefined) || animated;
1264- appDelegatePrivate.minimized = false;
1265- if (maximized)
1266- maximize();
1267- else if (maximizedLeft)
1268- maximizeLeft();
1269- else if (maximizedRight)
1270- maximizeRight();
1271- ApplicationManager.focusApplication(appId);
1272- }
1273-
1274- states: [
1275- State {
1276- name: "fullscreen"; when: decoratedWindow.fullscreen
1277- extend: "maximized"
1278- PropertyChanges {
1279- target: appDelegate;
1280- y: -PanelState.panelHeight
1281- }
1282- },
1283- State {
1284- name: "normal";
1285- when: !appDelegate.maximized && !appDelegate.minimized
1286- && !appDelegate.maximizedLeft && !appDelegate.maximizedRight
1287- PropertyChanges {
1288- target: appDelegate;
1289- visuallyMinimized: false;
1290- visuallyMaximized: false
1291- }
1292- },
1293- State {
1294- name: "maximized"; when: appDelegate.maximized && !appDelegate.minimized
1295- PropertyChanges {
1296- target: appDelegate;
1297- x: 0; y: 0;
1298- requestedWidth: root.width; requestedHeight: root.height;
1299- visuallyMinimized: false;
1300- visuallyMaximized: true
1301- }
1302- },
1303- State {
1304- name: "maximizedLeft"; when: appDelegate.maximizedLeft && !appDelegate.minimized
1305- PropertyChanges { target: appDelegate; x: 0; y: PanelState.panelHeight;
1306- requestedWidth: root.width/2; requestedHeight: root.height - PanelState.panelHeight }
1307- },
1308- State {
1309- name: "maximizedRight"; when: appDelegate.maximizedRight && !appDelegate.minimized
1310- PropertyChanges { target: appDelegate; x: root.width/2; y: PanelState.panelHeight;
1311- requestedWidth: root.width/2; requestedHeight: root.height - PanelState.panelHeight }
1312- },
1313- State {
1314- name: "minimized"; when: appDelegate.minimized
1315- PropertyChanges {
1316- target: appDelegate;
1317- x: -appDelegate.width / 2;
1318- scale: units.gu(5) / appDelegate.width;
1319- opacity: 0
1320- visuallyMinimized: true;
1321- visuallyMaximized: false
1322- }
1323- }
1324- ]
1325- transitions: [
1326- Transition {
1327- to: "normal"
1328- enabled: appDelegate.animationsEnabled
1329- PropertyAction { target: appDelegate; properties: "visuallyMinimized,visuallyMaximized" }
1330- UbuntuNumberAnimation { target: appDelegate; properties: "x,y,opacity,requestedWidth,requestedHeight,scale"; duration: UbuntuAnimation.FastDuration }
1331- },
1332- Transition {
1333- to: "minimized"
1334- enabled: appDelegate.animationsEnabled
1335- PropertyAction { target: appDelegate; property: "visuallyMaximized" }
1336- SequentialAnimation {
1337+
1338+ states: [
1339+ State {
1340+ name: "fullscreen"; when: decoratedWindow.fullscreen
1341+ extend: "maximized"
1342+ PropertyChanges {
1343+ target: appDelegate;
1344+ y: -PanelState.panelHeight
1345+ }
1346+ },
1347+ State {
1348+ name: "normal";
1349+ when: !appDelegate.maximized && !appDelegate.minimized
1350+ && !appDelegate.maximizedLeft && !appDelegate.maximizedRight
1351+ PropertyChanges {
1352+ target: appDelegate;
1353+ visuallyMinimized: false;
1354+ visuallyMaximized: false
1355+ }
1356+ },
1357+ State {
1358+ name: "maximized"; when: appDelegate.maximized && !appDelegate.minimized
1359+ PropertyChanges {
1360+ target: appDelegate;
1361+ x: 0; y: 0;
1362+ requestedWidth: appContainer.width; requestedHeight: appContainer.height;
1363+ visuallyMinimized: false;
1364+ visuallyMaximized: true
1365+ }
1366+ },
1367+ State {
1368+ name: "maximizedLeft"; when: appDelegate.maximizedLeft && !appDelegate.minimized
1369+ PropertyChanges { target: appDelegate; x: 0; y: PanelState.panelHeight;
1370+ requestedWidth: appContainer.width/2; requestedHeight: appContainer.height - PanelState.panelHeight }
1371+ },
1372+ State {
1373+ name: "maximizedRight"; when: appDelegate.maximizedRight && !appDelegate.minimized
1374+ PropertyChanges { target: appDelegate; x: appContainer.width/2; y: PanelState.panelHeight;
1375+ requestedWidth: appContainer.width/2; requestedHeight: appContainer.height - PanelState.panelHeight }
1376+ },
1377+ State {
1378+ name: "minimized"; when: appDelegate.minimized
1379+ PropertyChanges {
1380+ target: appDelegate;
1381+ x: -appDelegate.width / 2;
1382+ scale: units.gu(5) / appDelegate.width;
1383+ opacity: 0
1384+ visuallyMinimized: true;
1385+ visuallyMaximized: false
1386+ }
1387+ }
1388+ ]
1389+ transitions: [
1390+ Transition {
1391+ to: "normal"
1392+ enabled: appDelegate.animationsEnabled
1393+ PropertyAction { target: appDelegate; properties: "visuallyMinimized,visuallyMaximized" }
1394 UbuntuNumberAnimation { target: appDelegate; properties: "x,y,opacity,requestedWidth,requestedHeight,scale"; duration: UbuntuAnimation.FastDuration }
1395- PropertyAction { target: appDelegate; property: "visuallyMinimized" }
1396- ScriptAction {
1397- script: {
1398- if (appDelegate.minimized) {
1399- priv.focusNext();
1400+ },
1401+ Transition {
1402+ to: "minimized"
1403+ enabled: appDelegate.animationsEnabled
1404+ PropertyAction { target: appDelegate; property: "visuallyMaximized" }
1405+ SequentialAnimation {
1406+ UbuntuNumberAnimation { target: appDelegate; properties: "x,y,opacity,requestedWidth,requestedHeight,scale"; duration: UbuntuAnimation.FastDuration }
1407+ PropertyAction { target: appDelegate; property: "visuallyMinimized" }
1408+ ScriptAction {
1409+ script: {
1410+ if (appDelegate.minimized) {
1411+ priv.focusNext();
1412+ }
1413 }
1414 }
1415 }
1416- }
1417- },
1418- Transition {
1419- to: "*" //maximized and fullscreen
1420- enabled: appDelegate.animationsEnabled
1421- PropertyAction { target: appDelegate; property: "visuallyMinimized" }
1422- SequentialAnimation {
1423- UbuntuNumberAnimation { target: appDelegate; properties: "x,y,opacity,requestedWidth,requestedHeight,scale"; duration: UbuntuAnimation.FastDuration }
1424- PropertyAction { target: appDelegate; property: "visuallyMaximized" }
1425- }
1426- }
1427- ]
1428-
1429- Binding {
1430- id: previewBinding
1431- target: appDelegate
1432- property: "z"
1433- value: ApplicationManager.count + 1
1434- when: index == spread.highlightedIndex && blurLayer.ready
1435- }
1436-
1437- WindowResizeArea {
1438- objectName: "windowResizeArea"
1439- target: appDelegate
1440- minWidth: units.gu(10)
1441- minHeight: units.gu(10)
1442- borderThickness: units.gu(2)
1443- windowId: model.appId // FIXME: Change this to point to windowId once we have such a thing
1444- screenWidth: root.width
1445- screenHeight: root.height
1446-
1447- onPressed: { ApplicationManager.focusApplication(model.appId) }
1448- }
1449-
1450- DecoratedWindow {
1451- id: decoratedWindow
1452- objectName: "decoratedWindow"
1453- anchors.left: appDelegate.left
1454- anchors.top: appDelegate.top
1455- application: ApplicationManager.get(index)
1456- active: ApplicationManager.focusedApplicationId === model.appId
1457- focus: true
1458-
1459- onClose: ApplicationManager.stopApplication(model.appId)
1460- onMaximize: appDelegate.maximized || appDelegate.maximizedLeft || appDelegate.maximizedRight
1461- ? appDelegate.restoreFromMaximized() : appDelegate.maximize()
1462- onMinimize: appDelegate.minimize()
1463- onDecorationPressed: { ApplicationManager.focusApplication(model.appId) }
1464+ },
1465+ Transition {
1466+ to: "*" //maximized and fullscreen
1467+ enabled: appDelegate.animationsEnabled
1468+ PropertyAction { target: appDelegate; property: "visuallyMinimized" }
1469+ SequentialAnimation {
1470+ UbuntuNumberAnimation { target: appDelegate; properties: "x,y,opacity,requestedWidth,requestedHeight,scale"; duration: UbuntuAnimation.FastDuration }
1471+ PropertyAction { target: appDelegate; property: "visuallyMaximized" }
1472+ }
1473+ }
1474+ ]
1475+
1476+ Binding {
1477+ id: previewBinding
1478+ target: appDelegate
1479+ property: "z"
1480+ value: ApplicationManager.count + 1
1481+ when: index == spread.highlightedIndex && blurLayer.ready
1482+ }
1483+
1484+ WindowResizeArea {
1485+ objectName: "windowResizeArea"
1486+ target: appDelegate
1487+ minWidth: units.gu(10)
1488+ minHeight: units.gu(10)
1489+ borderThickness: units.gu(2)
1490+ windowId: model.appId // FIXME: Change this to point to windowId once we have such a thing
1491+ screenWidth: appContainer.width
1492+ screenHeight: appContainer.height
1493+
1494+ onPressed: { ApplicationManager.focusApplication(model.appId) }
1495+ }
1496+
1497+ DecoratedWindow {
1498+ id: decoratedWindow
1499+ objectName: "decoratedWindow"
1500+ anchors.left: appDelegate.left
1501+ anchors.top: appDelegate.top
1502+ application: ApplicationManager.get(index)
1503+ active: ApplicationManager.focusedApplicationId === model.appId
1504+ focus: true
1505+
1506+ onClose: ApplicationManager.stopApplication(model.appId)
1507+ onMaximize: appDelegate.maximized || appDelegate.maximizedLeft || appDelegate.maximizedRight
1508+ ? appDelegate.restoreFromMaximized() : appDelegate.maximize()
1509+ onMinimize: appDelegate.minimize()
1510+ onDecorationPressed: { ApplicationManager.focusApplication(model.appId) }
1511+ }
1512 }
1513 }
1514 }
1515@@ -475,8 +481,8 @@
1516
1517 BlurLayer {
1518 id: blurLayer
1519- anchors.fill: parent
1520- source: appContainer
1521+ anchors.fill: stageContainer
1522+ source: stageContainer
1523 visible: false
1524 }
1525
1526@@ -527,7 +533,7 @@
1527 DesktopSpread {
1528 id: spread
1529 objectName: "spread"
1530- anchors.fill: parent
1531+ anchors.fill: stageContainer
1532 workspace: appContainer
1533 focus: state == "altTab"
1534 altTabPressed: root.altTabPressed
1535
1536=== modified file 'tests/mocks/GSettings.1.0/fake_gsettings.cpp'
1537--- tests/mocks/GSettings.1.0/fake_gsettings.cpp 2015-09-29 20:19:56 +0000
1538+++ tests/mocks/GSettings.1.0/fake_gsettings.cpp 2016-03-01 13:59:38 +0000
1539@@ -22,6 +22,8 @@
1540
1541 GSettingsControllerQml::GSettingsControllerQml()
1542 : m_usageMode("Staged")
1543+ , m_autohideLauncher(false)
1544+ , m_launcherWidth(8)
1545 {
1546 }
1547
1548@@ -88,6 +90,32 @@
1549 }
1550 }
1551
1552+bool GSettingsControllerQml::autohideLauncher() const
1553+{
1554+ return m_autohideLauncher;
1555+}
1556+
1557+void GSettingsControllerQml::setAutohideLauncher(bool autohideLauncher)
1558+{
1559+ if (m_autohideLauncher != autohideLauncher) {
1560+ m_autohideLauncher = autohideLauncher;
1561+ Q_EMIT autohideLauncherChanged(autohideLauncher);
1562+ }
1563+}
1564+
1565+int GSettingsControllerQml::launcherWidth() const
1566+{
1567+ return m_launcherWidth;
1568+}
1569+
1570+void GSettingsControllerQml::setLauncherWidth(int launcherWidth)
1571+{
1572+ if (m_launcherWidth != launcherWidth) {
1573+ m_launcherWidth = launcherWidth;
1574+ Q_EMIT launcherWidthChanged(launcherWidth);
1575+ }
1576+}
1577+
1578 GSettingsSchemaQml::GSettingsSchemaQml(QObject *parent): QObject(parent) {
1579 }
1580
1581@@ -129,6 +157,10 @@
1582 this, &GSettingsQml::lockedOutTimeChanged);
1583 connect(GSettingsControllerQml::instance(), &GSettingsControllerQml::lifecycleExemptAppidsChanged,
1584 this, &GSettingsQml::lifecycleExemptAppidsChanged);
1585+ connect(GSettingsControllerQml::instance(), &GSettingsControllerQml::autohideLauncherChanged,
1586+ this, &GSettingsQml::autohideLauncherChanged);
1587+ connect(GSettingsControllerQml::instance(), &GSettingsControllerQml::launcherWidthChanged,
1588+ this, &GSettingsQml::launcherWidthChanged);
1589 }
1590
1591 GSettingsSchemaQml * GSettingsQml::schema() const {
1592@@ -192,9 +224,41 @@
1593 }
1594 }
1595
1596+bool GSettingsQml::autohideLauncher() const
1597+{
1598+ if (m_schema->id() == "com.canonical.Unity8") {
1599+ return GSettingsControllerQml::instance()->autohideLauncher();
1600+ } else {
1601+ return false;
1602+ }
1603+}
1604+
1605+int GSettingsQml::launcherWidth() const
1606+{
1607+ if (m_schema->id() == "com.canonical.Unity8") {
1608+ return GSettingsControllerQml::instance()->launcherWidth();
1609+ } else {
1610+ return false;
1611+ }
1612+}
1613+
1614 void GSettingsQml::setLifecycleExemptAppids(const QStringList &appIds)
1615 {
1616 if (m_schema->id() == "com.canonical.qtmir") {
1617 GSettingsControllerQml::instance()->setLifecycleExemptAppids(appIds);
1618 }
1619 }
1620+
1621+void GSettingsQml::setAutohideLauncher(bool autohideLauncher)
1622+{
1623+ if (m_schema->id() == "com.canonical.Unity8") {
1624+ GSettingsControllerQml::instance()->setAutohideLauncher(autohideLauncher);
1625+ }
1626+}
1627+
1628+void GSettingsQml::setLauncherWidth(int launcherWidth)
1629+{
1630+ if (m_schema->id() == "com.canonical.Unity8") {
1631+ GSettingsControllerQml::instance()->setLauncherWidth(launcherWidth);
1632+ }
1633+}
1634
1635=== modified file 'tests/mocks/GSettings.1.0/fake_gsettings.h'
1636--- tests/mocks/GSettings.1.0/fake_gsettings.h 2015-09-29 20:19:56 +0000
1637+++ tests/mocks/GSettings.1.0/fake_gsettings.h 2016-03-01 13:59:38 +0000
1638@@ -50,6 +50,8 @@
1639 Q_PROPERTY(QString usageMode READ usageMode WRITE setUsageMode NOTIFY usageModeChanged)
1640 Q_PROPERTY(qint64 lockedOutTime READ lockedOutTime WRITE setLockedOutTime NOTIFY lockedOutTimeChanged)
1641 Q_PROPERTY(QStringList lifecycleExemptAppids READ lifecycleExemptAppids WRITE setLifecycleExemptAppids NOTIFY lifecycleExemptAppidsChanged)
1642+ Q_PROPERTY(bool autohideLauncher READ autohideLauncher WRITE setAutohideLauncher NOTIFY autohideLauncherChanged)
1643+ Q_PROPERTY(int launcherWidth READ launcherWidth WRITE setLauncherWidth NOTIFY launcherWidthChanged)
1644
1645 public:
1646 GSettingsQml(QObject *parent = nullptr);
1647@@ -59,11 +61,15 @@
1648 QString usageMode() const;
1649 qint64 lockedOutTime() const;
1650 QStringList lifecycleExemptAppids() const;
1651+ bool autohideLauncher() const;
1652+ int launcherWidth() const;
1653
1654 void setPictureUri(const QString &str);
1655 void setUsageMode(const QString &usageMode);
1656 void setLockedOutTime(qint64 timestamp);
1657 void setLifecycleExemptAppids(const QStringList &appIds);
1658+ void setAutohideLauncher(bool autohideLauncher);
1659+ void setLauncherWidth(int launcherWidth);
1660
1661 Q_SIGNALS:
1662 void schemaChanged();
1663@@ -71,6 +77,8 @@
1664 void usageModeChanged(const QString&);
1665 void lockedOutTimeChanged(qint64);
1666 void lifecycleExemptAppidsChanged(const QStringList &);
1667+ void autohideLauncherChanged(bool);
1668+ void launcherWidthChanged(int launcherWidth);
1669
1670 private:
1671 GSettingsSchemaQml* m_schema;
1672@@ -98,11 +106,19 @@
1673 QStringList lifecycleExemptAppids() const;
1674 Q_INVOKABLE void setLifecycleExemptAppids(const QStringList &appIds);
1675
1676+ bool autohideLauncher() const;
1677+ Q_INVOKABLE void setAutohideLauncher(bool autohideLauncher);
1678+
1679+ int launcherWidth() const;
1680+ Q_INVOKABLE void setLauncherWidth(int launcherWidth);
1681+
1682 Q_SIGNALS:
1683 void pictureUriChanged(const QString&);
1684 void usageModeChanged(const QString&);
1685 void lockedOutTimeChanged(qint64 timestamp);
1686 void lifecycleExemptAppidsChanged(const QStringList&);
1687+ void autohideLauncherChanged(bool autohideLauncher);
1688+ void launcherWidthChanged(int launcherWidth);
1689
1690 private:
1691 GSettingsControllerQml();
1692@@ -111,6 +127,8 @@
1693 QString m_usageMode;
1694 qint64 m_lockedOutTime;
1695 QStringList m_lifecycleExemptAppids;
1696+ bool m_autohideLauncher;
1697+ int m_launcherWidth;
1698
1699 static GSettingsControllerQml* s_controllerInstance;
1700 QList<GSettingsQml *> m_registeredGSettings;
1701
1702=== modified file 'tests/mocks/Unity/Launcher/MockLauncherModel.cpp'
1703--- tests/mocks/Unity/Launcher/MockLauncherModel.cpp 2015-11-04 11:29:16 +0000
1704+++ tests/mocks/Unity/Launcher/MockLauncherModel.cpp 2016-03-01 13:59:38 +0000
1705@@ -25,6 +25,7 @@
1706 MockLauncherItem *item = new MockLauncherItem("dialer-app", "/usr/share/applications/dialer-app.desktop", "Dialer", "dialer-app", this);
1707 item->setProgress(0);
1708 item->setPinned(true);
1709+ item->setRunning(true);
1710 item->setFocused(true);
1711 m_list.append(item);
1712 item = new MockLauncherItem("camera-app", "/usr/share/applications/camera-app.desktop", "Camera", "camera", this);
1713@@ -34,6 +35,7 @@
1714 item = new MockLauncherItem("gallery-app", "/usr/share/applications/gallery-app.desktop", "Gallery", "gallery", this);
1715 item->setProgress(50);
1716 item->setCountVisible(true);
1717+ item->setRunning(true);
1718 item->setAlerting(false);
1719 m_list.append(item);
1720 item = new MockLauncherItem("music-app", "/usr/share/applications/music-app.desktop", "Music", "soundcloud", this);
1721
1722=== modified file 'tests/qmltests/Launcher/tst_Launcher.qml'
1723--- tests/qmltests/Launcher/tst_Launcher.qml 2016-01-19 15:36:15 +0000
1724+++ tests/qmltests/Launcher/tst_Launcher.qml 2016-03-01 13:59:38 +0000
1725@@ -28,12 +28,13 @@
1726 launcher. */
1727 Item {
1728 id: root
1729- width: units.gu(50)
1730- height: units.gu(55)
1731+ width: units.gu(140)
1732+ height: units.gu(70)
1733
1734 Loader {
1735 id: launcherLoader
1736 anchors.fill: parent
1737+ focus: true
1738 property bool itemDestroyed: false
1739 sourceComponent: Component {
1740 Launcher {
1741@@ -68,6 +69,7 @@
1742
1743 Component.onCompleted: {
1744 launcherLoader.itemDestroyed = false;
1745+ launcherLoader.focus = true
1746 edgeBarrierControls.target = testCase.findChild(this, "edgeBarrierController");
1747 }
1748 Component.onDestruction: {
1749@@ -77,11 +79,40 @@
1750 }
1751 }
1752
1753+ Binding {
1754+ target: launcherLoader.item
1755+ property: "lockedVisible"
1756+ value: lockedVisibleCheckBox.checked
1757+ }
1758+ Binding {
1759+ target: launcherLoader.item
1760+ property: "panelWidth"
1761+ value: units.gu(Math.round(widthSlider.value))
1762+ }
1763+
1764 ColumnLayout {
1765 anchors { bottom: parent.bottom; right: parent.right; margins: units.gu(1) }
1766 spacing: units.gu(1)
1767 width: childrenRect.width
1768
1769+ RowLayout {
1770+ CheckBox {
1771+ id: lockedVisibleCheckBox
1772+ checked: false
1773+ }
1774+ Label {
1775+ text: "Launcher always visible"
1776+ }
1777+ }
1778+
1779+ Slider {
1780+ id: widthSlider
1781+ Layout.fillWidth: true
1782+ minimumValue: 6
1783+ maximumValue: 12
1784+ value: 10
1785+ }
1786+
1787 MouseTouchEmulationCheckbox {}
1788
1789 EdgeBarrierControls {
1790@@ -102,6 +133,15 @@
1791 Layout.fillWidth: true
1792 }
1793
1794+ Button {
1795+ text: "open for kbd navigation"
1796+ onClicked: {
1797+ launcherLoader.item.openForKeyboardNavigation()
1798+ launcherLoader.item.forceActiveFocus();// = true
1799+ }
1800+ Layout.fillWidth: true
1801+ }
1802+
1803 Row {
1804 spacing: units.gu(1)
1805
1806@@ -206,10 +246,6 @@
1807 // growing while populating it with icons etc.
1808 tryCompare(listView, "flicking", false);
1809
1810- // Make sure noone changed the height of the window. The issue this test case
1811- // is verifying only happens on certain heights of the Launcher
1812- compare(root.height, units.gu(55));
1813-
1814 compare(listView.contentY, -listView.topMargin, "Launcher did not start up with first item unfolded");
1815
1816 // Now do check that snapping is in fact enabled
1817@@ -266,13 +302,32 @@
1818
1819 function positionLauncherListAtBeginning() {
1820 var listView = testCase.findChild(launcherLoader.item, "launcherListView");
1821- listView.contentY = -listView.topMargin;
1822+ var moveAnimation = findInvisibleChild(listView, "moveAnimation")
1823+
1824+ listView.moveToIndex(0);
1825+
1826+ waitForRendering(listView);
1827+ tryCompare(moveAnimation, "running", false);
1828 }
1829 function positionLauncherListAtEnd() {
1830 var listView = testCase.findChild(launcherLoader.item, "launcherListView");
1831- if ((listView.contentHeight + listView.topMargin + listView.bottomMargin) > listView.height) {
1832- listView.contentY = listView.topMargin + listView.contentHeight
1833- - listView.height;
1834+ var moveAnimation = findInvisibleChild(listView, "moveAnimation")
1835+
1836+ listView.moveToIndex(listView.count -1);
1837+
1838+ waitForRendering(listView);
1839+ tryCompare(moveAnimation, "running", false);
1840+ }
1841+
1842+ function assertFocusOnIndex(index) {
1843+ var launcherListView = findChild(launcher, "launcherListView");
1844+ var bfbFocusHighlight = findChild(launcher, "bfbFocusHighlight");
1845+
1846+ waitForRendering(launcher);
1847+ compare(bfbFocusHighlight.visible, index === -1);
1848+ for (var i = 0; i < launcherListView.count; i++) {
1849+ var focusRing = findChild(findChild(launcher, "launcherDelegate" + i), "focusRing")
1850+ compare(focusRing.visible, index === i);
1851 }
1852 }
1853
1854@@ -290,10 +345,10 @@
1855 dragLauncherIntoView()
1856
1857 // tapping on the center of the screen should dismiss the launcher
1858- mouseClick(launcher)
1859+ mouseClick(launcher, panel.width + units.gu(5), launcher.height / 2)
1860
1861 // should eventually get fully retracted (hidden)
1862- tryCompare(panel, "x", -launcher.panelWidth, 1000)
1863+ tryCompare(panel, "x", -launcher.panelWidth, 2000)
1864 }
1865
1866 /* If I click on the icon of an application on the launcher
1867@@ -377,6 +432,7 @@
1868 wait(100)
1869 compare(launcher.maxPanelX, -launcher.panelWidth, "Launcher moved even if it shouldn't")
1870 }
1871+
1872 waitUntilLauncherDisappears();
1873 launcher.available = true;
1874 }
1875@@ -400,6 +456,8 @@
1876 dragLauncherIntoView();
1877 var launcherListView = findChild(launcher, "launcherListView");
1878 for (var i = 0; i < launcherListView.count; ++i) {
1879+ launcherListView.moveToIndex(i);
1880+ waitForRendering(launcherListView);
1881 var delegate = findChild(launcherListView, "launcherDelegate" + i)
1882 compare(findChild(delegate, "countEmblem").visible, LauncherModel.get(i).countVisible)
1883 // Intentionally allow type coercion (string/number)
1884@@ -421,7 +479,7 @@
1885 var launcherListView = findChild(launcher, "launcherListView");
1886 for (var i = 0; i < launcherListView.count; ++i) {
1887 var delegate = findChild(launcherListView, "launcherDelegate" + i)
1888- compare(findChild(delegate, "runningHighlight").visible, LauncherModel.get(i).running)
1889+ compare(findChild(delegate, "runningHighlight0").visible, LauncherModel.get(i).running)
1890 }
1891 }
1892
1893@@ -460,6 +518,7 @@
1894 launcher.lastSelectedApplication = "";
1895 dragLauncherIntoView();
1896 var listView = findChild(launcher, "launcherListView");
1897+ var moveAnimation = findInvisibleChild(listView, "moveAnimation")
1898
1899 // flicking is unreliable. sometimes it works, sometimes the
1900 // list view moves just a tiny bit or not at all, making tests fail.
1901@@ -470,12 +529,14 @@
1902 } else {
1903 positionLauncherListAtEnd();
1904 }
1905- tryCompare(listView, "flicking", false);
1906-
1907 var oldY = listView.contentY;
1908
1909 mouseClick(listView, listView.width / 2, data.clickY);
1910- tryCompare(listView, "flicking", false);
1911+
1912+ if (data.expectFlick) {
1913+ tryCompare(moveAnimation, "running", true);
1914+ }
1915+ tryCompare(moveAnimation, "running", false);
1916
1917 if (data.expectFlick) {
1918 verify(listView.contentY != oldY);
1919@@ -764,14 +825,15 @@
1920 function test_launcher_dismiss() {
1921 dragLauncherIntoView();
1922 verify(launcher.state == "visible");
1923- mouseClick(root);
1924+
1925+ mouseClick(root, root.width / 2, units.gu(1));
1926 waitUntilLauncherDisappears();
1927 verify(launcher.state == "");
1928
1929 // and repeat, as a test for regression in lpbug#1531339
1930 dragLauncherIntoView();
1931 verify(launcher.state == "visible");
1932- mouseClick(root);
1933+ mouseClick(root, root.width / 2, units.gu(1));
1934 waitUntilLauncherDisappears();
1935 verify(launcher.state == "");
1936 }
1937@@ -1044,5 +1106,169 @@
1938 LauncherModel.setCountVisible(LauncherModel.get(1).appId, 0)
1939 LauncherModel.setCount(LauncherModel.get(1).appId, oldCount)
1940 }
1941+
1942+ function test_longpressSuperKeyShowsHints() {
1943+ var shortCutHint0 = findChild(findChild(launcher, "launcherDelegate0"), "shortcutHint");
1944+
1945+ tryCompare(shortCutHint0, "visible", false);
1946+
1947+ launcher.superPressed = true;
1948+ tryCompare(launcher, "state", "visible");
1949+ tryCompare(shortCutHint0, "visible", true);
1950+
1951+ launcher.superPressed = false;
1952+ tryCompare(launcher, "state", "");
1953+ tryCompare(shortCutHint0, "visible", false);
1954+ }
1955+
1956+ function test_keyboardNavigation() {
1957+ var bfbFocusHighlight = findChild(launcher, "bfbFocusHighlight");
1958+ var quickList = findChild(launcher, "quickList");
1959+ var launcherPanel = findChild(launcher, "launcherPanel");
1960+ var launcherListView = findChild(launcher, "launcherListView");
1961+ var last = launcherListView.count - 1;
1962+
1963+ compare(bfbFocusHighlight.visible, false);
1964+
1965+ launcher.openForKeyboardNavigation();
1966+ tryCompare(launcherPanel, "x", 0);
1967+ waitForRendering(launcher);
1968+
1969+ assertFocusOnIndex(-1);
1970+
1971+ // Down should go down
1972+ keyClick(Qt.Key_Down);
1973+ assertFocusOnIndex(0);
1974+
1975+ // Tab should go down
1976+ keyClick(Qt.Key_Tab);
1977+ assertFocusOnIndex(1);
1978+
1979+ // Up should go up
1980+ keyClick(Qt.Key_Up);
1981+ assertFocusOnIndex(0);
1982+
1983+ // Backtab should go up
1984+ keyClick(Qt.Key_Backtab);
1985+ assertFocusOnIndex(-1); // BFB
1986+
1987+ // The list should wrap around
1988+ keyClick(Qt.Key_Up);
1989+ assertFocusOnIndex(last);
1990+
1991+ keyClick(Qt.Key_Down);
1992+ waitForRendering(launcher);
1993+ keyClick(Qt.Key_Down);
1994+ assertFocusOnIndex(0); // Back to Top
1995+
1996+ // Right opens the quicklist
1997+ keyClick(Qt.Key_Right);
1998+ assertFocusOnIndex(0); // Navigating the quicklist... the launcher focus should not move
1999+ tryCompare(quickList, "visible", true);
2000+ tryCompare(quickList, "selectedIndex", 0)
2001+
2002+ // Down should move down the quicklist
2003+ keyClick(Qt.Key_Down);
2004+ tryCompare(quickList, "selectedIndex", 1)
2005+
2006+ // The quicklist should wrap around too
2007+ keyClick(Qt.Key_Down);
2008+ keyClick(Qt.Key_Down);
2009+ keyClick(Qt.Key_Down);
2010+ tryCompare(quickList, "selectedIndex", 0)
2011+
2012+ // Left gets us back to the launcher
2013+ keyClick(Qt.Key_Left);
2014+ assertFocusOnIndex(0);
2015+ tryCompare(quickList, "visible", false);
2016+
2017+ // Launcher navigation should still work
2018+ // Go bar to top by wrapping around
2019+ keyClick(Qt.Key_Down);
2020+ assertFocusOnIndex(1);
2021+ }
2022+
2023+ function test_selectQuicklistItemByKeyboard() {
2024+ launcher.openForKeyboardNavigation();
2025+ waitForRendering(launcher);
2026+
2027+ signalSpy.clear();
2028+ signalSpy.signalName = "quickListTriggered"
2029+
2030+ keyClick(Qt.Key_Down); // Down to launcher item 0
2031+ keyClick(Qt.Key_Down); // Down to launcher item 1
2032+ keyClick(Qt.Key_Right); // Into quicklist
2033+ keyClick(Qt.Key_Down); // Down to quicklist item 1
2034+ keyClick(Qt.Key_Down); // Down to quicklist item 2
2035+ keyClick(Qt.Key_Enter); // Trigger it
2036+
2037+ compare(signalSpy.count, 1, "Quicklist signal wasn't triggered")
2038+ compare(signalSpy.signalArguments[0][0], LauncherModel.get(1).appId)
2039+ compare(signalSpy.signalArguments[0][1], 2)
2040+ assertFocusOnIndex(-2);
2041+ }
2042+
2043+ function test_hideNotWorkingWhenLockedOut_data() {
2044+ return [
2045+ {tag: "locked visible", locked: true},
2046+ {tag: "no locked visible", locked: false},
2047+ ]
2048+ }
2049+
2050+ function test_hideNotWorkingWhenLockedOut(data) {
2051+ launcher.lockedVisible = data.locked;
2052+ if (data.locked) {
2053+ tryCompare(launcher, "state", "visible");
2054+ } else {
2055+ tryCompare(launcher, "state", "");
2056+ }
2057+
2058+ launcher.hide();
2059+ waitForRendering(launcher);
2060+ if (data.locked) {
2061+ verify(launcher.state == "visible");
2062+ } else {
2063+ verify(launcher.state == "");
2064+ }
2065+ }
2066+
2067+ function test_cancelKbdNavigationWitMouse_data() {
2068+ return [
2069+ {tag: "locked out - no quicklist", autohide: false, withQuickList: false },
2070+ {tag: "locked out - with quicklist", autohide: false, withQuickList: true },
2071+ {tag: "autohide - no quicklist", autohide: true, withQuickList: false },
2072+ {tag: "autohide - with quicklist", autohide: true, withQuickList: true },
2073+ ]
2074+ }
2075+
2076+ function test_cancelKbdNavigationWitMouse(data) {
2077+ launcher.autohideEnabled = data.autohide;
2078+ launcher.openForKeyboardNavigation();
2079+ waitForRendering(launcher);
2080+
2081+ var launcherPanel = findChild(launcher, "launcherPanel");
2082+ tryCompare(launcherPanel, "x", 0);
2083+
2084+ var quickList = findChild(launcher, "quickList");
2085+
2086+ keyClick(Qt.Key_Down); // Down to launcher item 0
2087+ keyClick(Qt.Key_Down); // Down to launcher item 1
2088+
2089+ if (data.withQuickList) {
2090+ keyClick(Qt.Key_Right); // Into quicklist
2091+ tryCompare(quickList, "visible", true)
2092+ }
2093+ waitForRendering(launcher)
2094+
2095+ mouseClick(root);
2096+
2097+ if (data.autohide) {
2098+ tryCompare(launcher, "state", "");
2099+ } else {
2100+ tryCompare(launcher, "state", "visible");
2101+ }
2102+
2103+ assertFocusOnIndex(-2);
2104+ }
2105 }
2106 }
2107
2108=== modified file 'tests/qmltests/tst_Shell.qml'
2109--- tests/qmltests/tst_Shell.qml 2016-02-12 00:11:28 +0000
2110+++ tests/qmltests/tst_Shell.qml 2016-03-01 13:59:38 +0000
2111@@ -26,6 +26,7 @@
2112 import Unity.Connectivity 0.1
2113 import Unity.Indicators 0.1
2114 import Unity.Notifications 1.0
2115+import Unity.Launcher 0.1
2116 import Unity.Test 0.1
2117 import Powerd 0.1
2118 import Wizard 0.1 as Wizard
2119@@ -121,10 +122,6 @@
2120 Component.onDestruction: {
2121 shellLoader.itemDestroyed = true;
2122 }
2123- Component.onCompleted: {
2124- var keyMapper = testCase.findChild(__shell, "physicalKeysMapper");
2125- keyMapper.controlInsteadOfAlt = true;
2126- }
2127 }
2128 }
2129 }
2130@@ -213,6 +210,31 @@
2131 checked: true
2132 color: "white"
2133 }
2134+ ListItem.ItemSelector {
2135+ id: ctrlModifier
2136+ anchors { left: parent.left; right: parent.right }
2137+ activeFocusOnPress: false
2138+ text: "Ctrl key as"
2139+ model: ["Ctrl", "Alt", "Super"]
2140+ onSelectedIndexChanged: {
2141+ var keyMapper = testCase.findChild(shellContainer, "physicalKeysMapper");
2142+ keyMapper.controlInsteadOfAlt = selectedIndex == 1;
2143+ keyMapper.controlInsteadOfSuper = selectedIndex == 2;
2144+ }
2145+ }
2146+
2147+ Row {
2148+ anchors { left: parent.left; right: parent.right }
2149+ CheckBox {
2150+ id: autohideLauncherCheckbox
2151+ onCheckedChanged: {
2152+ GSettingsController.setAutohideLauncher(checked)
2153+ }
2154+ }
2155+ Label {
2156+ text: "Autohide launcher"
2157+ }
2158+ }
2159
2160 Label { text: "Applications"; font.bold: true }
2161
2162@@ -912,6 +934,7 @@
2163 function dragLauncherIntoView() {
2164 var launcher = findChild(shell, "launcher");
2165 var launcherPanel = findChild(launcher, "launcherPanel");
2166+ waitForRendering(launcher);
2167 verify(launcherPanel.x = - launcherPanel.width);
2168
2169 var touchStartX = 2;
2170@@ -1441,7 +1464,7 @@
2171
2172 // Do a quick alt-tab and see if focus changes
2173 tryCompare(app3.session.lastSurface, "activeFocus", true)
2174- keyClick(Qt.Key_Tab, Qt.ControlModifier)
2175+ keyClick(Qt.Key_Tab, Qt.AltModifier)
2176 tryCompare(app2.session.lastSurface, "activeFocus", true)
2177
2178 var desktopSpread = findChild(shell, "spread")
2179@@ -1449,12 +1472,12 @@
2180 tryCompare(desktopSpread, "state", "")
2181
2182 // Just press Alt, make sure the spread comes up
2183- keyPress(Qt.Key_Control);
2184+ keyPress(Qt.Key_Alt);
2185 keyClick(Qt.Key_Tab);
2186 tryCompare(desktopSpread, "state", "altTab")
2187
2188 // Release control, check if spread disappears
2189- keyRelease(Qt.Key_Control)
2190+ keyRelease(Qt.Key_Alt)
2191 tryCompare(desktopSpread, "state", "")
2192
2193 // Focus should have switched back now
2194@@ -1482,7 +1505,7 @@
2195 tryCompare(desktopSpread, "state", "")
2196
2197 // Just press Alt, make sure the spread comes up
2198- keyPress(Qt.Key_Control);
2199+ keyPress(Qt.Key_Alt);
2200 keyClick(Qt.Key_Tab);
2201 tryCompare(desktopSpread, "state", "altTab")
2202 tryCompare(spreadRepeater, "highlightedIndex", 1)
2203@@ -1503,7 +1526,7 @@
2204 tryCompare(spreadRepeater, "highlightedIndex", 0)
2205
2206 // Release control, check if spread disappears
2207- keyRelease(Qt.Key_Control)
2208+ keyRelease(Qt.Key_Alt)
2209 tryCompare(desktopSpread, "state", "")
2210
2211 // Make sure that after wrapping around once, we have the same one focused as at the beginning
2212@@ -1516,7 +1539,7 @@
2213 var spreadRepeater = findInvisibleChild(shell, "spreadRepeater");
2214 verify(spreadRepeater !== null);
2215
2216- keyPress(Qt.Key_Control)
2217+ keyPress(Qt.Key_Alt)
2218 keyClick(Qt.Key_Tab);
2219 tryCompare(spreadRepeater, "highlightedIndex", 1);
2220
2221@@ -1538,7 +1561,7 @@
2222 keyClick(Qt.Key_Backtab);
2223 tryCompare(spreadRepeater, "highlightedIndex", 1);
2224
2225- keyRelease(Qt.Key_Control);
2226+ keyRelease(Qt.Key_Alt);
2227 }
2228
2229 function test_highlightFollowsMouse() {
2230@@ -1547,7 +1570,7 @@
2231 var spreadRepeater = findInvisibleChild(shell, "spreadRepeater");
2232 verify(spreadRepeater !== null);
2233
2234- keyPress(Qt.Key_Control)
2235+ keyPress(Qt.Key_Alt)
2236 keyClick(Qt.Key_Tab);
2237
2238 tryCompare(spreadRepeater, "highlightedIndex", 1);
2239@@ -1566,7 +1589,7 @@
2240
2241 verify(y < 4000);
2242
2243- keyRelease(Qt.Key_Control);
2244+ keyRelease(Qt.Key_Alt);
2245 }
2246
2247 function test_closeFromSpread() {
2248@@ -1575,7 +1598,7 @@
2249 var spreadRepeater = findInvisibleChild(shell, "spreadRepeater");
2250 verify(spreadRepeater !== null);
2251
2252- keyPress(Qt.Key_Control)
2253+ keyPress(Qt.Key_Alt)
2254 keyClick(Qt.Key_Tab);
2255
2256 appRemovedSpy.clear();
2257@@ -1602,7 +1625,7 @@
2258 tryCompare(appRemovedSpy, "count", 1)
2259 compare(appRemovedSpy.signalArguments[0][0], closedAppId);
2260
2261- keyRelease(Qt.Key_Control);
2262+ keyRelease(Qt.Key_Alt);
2263 }
2264
2265 function test_selectFromSpreadWithMouse_data() {
2266@@ -1622,7 +1645,7 @@
2267 var spreadRepeater = findInvisibleChild(shell, "spreadRepeater");
2268 verify(spreadRepeater !== null);
2269
2270- keyPress(Qt.Key_Control)
2271+ keyPress(Qt.Key_Alt)
2272 keyClick(Qt.Key_Tab);
2273
2274 var focusAppId = ApplicationManager.get(2).appId;
2275@@ -1649,7 +1672,7 @@
2276 tryCompare(stage, "state", "");
2277 tryCompare(ApplicationManager, "focusedApplicationId", focusAppId);
2278
2279- keyRelease(Qt.Key_Control);
2280+ keyRelease(Qt.Key_Alt);
2281 }
2282
2283 function test_progressiveAutoScrolling() {
2284@@ -1658,7 +1681,7 @@
2285 var appRepeater = findInvisibleChild(shell, "appRepeater");
2286 verify(appRepeater !== null);
2287
2288- keyPress(Qt.Key_Control)
2289+ keyPress(Qt.Key_Alt)
2290 keyClick(Qt.Key_Tab);
2291
2292 var spreadFlickable = findChild(shell, "spreadFlickable")
2293@@ -1669,7 +1692,7 @@
2294 var x = 0;
2295 var y = shell.height * .5
2296 mouseMove(shell, x, y)
2297- while (x <= spreadFlickable.width) {
2298+ while (x <= shell.width) {
2299 x+=10;
2300 mouseMove(shell, x, y)
2301 wait(0); // spin the loop so bindings get evaluated
2302@@ -1684,7 +1707,7 @@
2303 }
2304 tryCompare(spreadFlickable, "contentX", 0);
2305
2306- keyRelease(Qt.Key_Control);
2307+ keyRelease(Qt.Key_Alt);
2308 }
2309
2310 // This makes sure the hoverMouseArea is set to invisible AND disabled
2311@@ -1696,13 +1719,13 @@
2312 tryCompare(hoverMouseArea, "enabled", false)
2313 tryCompare(hoverMouseArea, "visible", false)
2314
2315- keyPress(Qt.Key_Control)
2316+ keyPress(Qt.Key_Alt)
2317 keyClick(Qt.Key_Tab);
2318
2319 tryCompare(hoverMouseArea, "enabled", true)
2320 tryCompare(hoverMouseArea, "visible", true)
2321
2322- keyRelease(Qt.Key_Control)
2323+ keyRelease(Qt.Key_Alt)
2324
2325 tryCompare(hoverMouseArea, "enabled", false)
2326 tryCompare(hoverMouseArea, "visible", false)
2327@@ -1719,7 +1742,7 @@
2328 var appRepeater = findInvisibleChild(shell, "appRepeater");
2329 verify(appRepeater !== null);
2330
2331- keyPress(Qt.Key_Control)
2332+ keyPress(Qt.Key_Alt)
2333 keyClick(Qt.Key_Tab);
2334
2335 tryCompare(spreadRepeater, "highlightedIndex", 1);
2336@@ -1740,33 +1763,46 @@
2337
2338 verify(y < 4000);
2339
2340- keyRelease(Qt.Key_Control);
2341- }
2342-
2343- function test_focusAppFromLauncherExitsSpread() {
2344+ keyRelease(Qt.Key_Alt);
2345+ }
2346+
2347+ function test_focusAppFromLauncherExitsSpread_data() {
2348+ return [
2349+ {tag: "autohide launcher", launcherLocked: false },
2350+ {tag: "locked launcher", launcherLocked: true }
2351+ ]
2352+ }
2353+
2354+ function test_focusAppFromLauncherExitsSpread(data) {
2355 loadDesktopShellWithApps()
2356-
2357+ var launcher = findChild(shell, "launcher");
2358 var desktopSpread = findChild(shell, "spread");
2359- var launcher = findChild(shell, "launcher");
2360 var bfb = findChild(launcher, "buttonShowDashHome");
2361
2362- keyPress(Qt.Key_Control)
2363+ GSettingsController.setAutohideLauncher(!data.launcherLocked);
2364+ waitForRendering(shell);
2365+
2366+ keyPress(Qt.Key_Alt)
2367 keyClick(Qt.Key_Tab);
2368
2369 tryCompare(desktopSpread, "state", "altTab")
2370
2371- revealLauncherByEdgePushWithMouse();
2372- tryCompare(launcher, "x", 0);
2373- mouseMove(bfb, bfb.width / 2, bfb.height / 2)
2374- waitForRendering(shell)
2375+ if (!data.launcherLocked) {
2376+ revealLauncherByEdgePushWithMouse();
2377+ tryCompare(launcher, "x", 0);
2378+ mouseMove(bfb, bfb.width / 2, bfb.height / 2)
2379+ waitForRendering(shell)
2380+ }
2381
2382 mouseClick(bfb, bfb.width / 2, bfb.height / 2)
2383- tryCompare(launcher, "state", "")
2384+ if (!data.launcherLocked) {
2385+ tryCompare(launcher, "state", "")
2386+ }
2387 tryCompare(desktopSpread, "state", "")
2388
2389 tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash")
2390
2391- keyRelease(Qt.Key_Control);
2392+ keyRelease(Qt.Key_Alt);
2393 }
2394
2395 // regression test for http://pad.lv/1443319
2396@@ -1988,6 +2024,7 @@
2397 compare(ApplicationManager.findApplication("libreoffice") === null, true);
2398 }
2399 }
2400+<<<<<<< TREE
2401
2402 function test_inputEventsOnEdgesEndUpInAppSurface_data() {
2403 return [
2404@@ -2030,5 +2067,135 @@
2405 compare(topmostSurfaceItem.touchPressCount, 2);
2406 compare(topmostSurfaceItem.touchReleaseCount, 2);
2407 }
2408+=======
2409+
2410+ function test_superTabToCycleLauncher_data() {
2411+ return [
2412+ {tag: "autohide launcher", launcherLocked: false},
2413+ {tag: "locked launcher", launcherLocked: true}
2414+ ]
2415+ }
2416+
2417+ function test_superTabToCycleLauncher(data) {
2418+ loadShell("desktop");
2419+ shell.usageScenario = "desktop";
2420+ GSettingsController.setAutohideLauncher(!data.launcherLocked);
2421+ waitForRendering(shell);
2422+
2423+ var launcher = findChild(shell, "launcher");
2424+ var launcherPanel = findChild(launcher, "launcherPanel");
2425+ var firstAppInLauncher = LauncherModel.get(0).appId;
2426+
2427+ compare(launcher.state, data.launcherLocked ? "visible": "");
2428+ compare(launcherPanel.highlightIndex, -2);
2429+ compare(ApplicationManager.focusedApplicationId, "unity8-dash");
2430+
2431+ wait(2000)
2432+ // Use Super + Tab Tab to cycle to the first entry in the launcher
2433+ keyPress(Qt.Key_Super_L, Qt.MetaModifier);
2434+ keyClick(Qt.Key_Tab);
2435+ tryCompare(launcher, "state", "visible");
2436+ tryCompare(launcherPanel, "highlightIndex", -1);
2437+ keyClick(Qt.Key_Tab);
2438+ tryCompare(launcherPanel, "highlightIndex", 0);
2439+ keyRelease(Qt.Key_Super_L, Qt.MetaModifier);
2440+ tryCompare(launcher, "state", data.launcherLocked ? "visible" : "");
2441+ tryCompare(launcherPanel, "highlightIndex", -2);
2442+ tryCompare(ApplicationManager, "focusedApplicationId", firstAppInLauncher);
2443+
2444+ // Now go back to the dash
2445+ keyPress(Qt.Key_Super_L, Qt.MetaModifier);
2446+ keyClick(Qt.Key_Tab);
2447+ tryCompare(launcher, "state", "visible");
2448+ tryCompare(launcherPanel, "highlightIndex", -1);
2449+ keyRelease(Qt.Key_Super_L, Qt.MetaModifier);
2450+ tryCompare(launcher, "state", data.launcherLocked ? "visible" : "");
2451+ tryCompare(launcherPanel, "highlightIndex", -2);
2452+ tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
2453+ }
2454+
2455+ function test_longpressSuperOpensLauncher() {
2456+ loadShell("desktop");
2457+ var launcher = findChild(shell, "launcher");
2458+ var shortcutHint = findChild(findChild(launcher, "launcherDelegate0"), "shortcutHint")
2459+
2460+ compare(launcher.state, "");
2461+ keyPress(Qt.Key_Super_L, Qt.MetaModifier);
2462+ tryCompare(launcher, "state", "visible");
2463+ tryCompare(shortcutHint, "visible", true);
2464+
2465+ keyRelease(Qt.Key_Super_L, Qt.MetaModifier);
2466+ tryCompare(launcher, "state", "");
2467+ tryCompare(shortcutHint, "visible", false);
2468+ }
2469+
2470+ function test_metaNumberLaunchesFromLauncher_data() {
2471+ return [
2472+ {tag: "Meta+1", key: Qt.Key_1, index: 0},
2473+ {tag: "Meta+2", key: Qt.Key_2, index: 1},
2474+ {tag: "Meta+4", key: Qt.Key_5, index: 4},
2475+ {tag: "Meta+0", key: Qt.Key_0, index: 9},
2476+ ]
2477+ }
2478+
2479+ function test_metaNumberLaunchesFromLauncher(data) {
2480+ loadShell("desktop");
2481+ var launcher = findChild(shell, "launcher");
2482+ var appId = LauncherModel.get(data.index).appId;
2483+ waitForRendering(shell);
2484+
2485+ keyClick(data.key, Qt.MetaModifier);
2486+ tryCompare(ApplicationManager, "focusedApplicationId", appId);
2487+ }
2488+
2489+ function test_altF1OpensLauncherForKeyboardNavigation() {
2490+ loadShell("desktop");
2491+ waitForRendering(shell);
2492+ var launcher = findChild(shell, "launcher");
2493+
2494+ keyClick(Qt.Key_F1, Qt.AltModifier);
2495+ tryCompare(launcher, "state", "visible");
2496+ tryCompare(launcher, "focus", true)
2497+ }
2498+
2499+ function test_lockedOutLauncherShrinksStage() {
2500+ loadShell("desktop");
2501+ shell.usageScenario = "desktop";
2502+ waitForRendering(shell);
2503+
2504+ var appContainer = findChild(shell, "appContainer");
2505+ var launcher = findChild(shell, "launcher");
2506+
2507+ GSettingsController.setAutohideLauncher(true);
2508+ waitForRendering(shell)
2509+ var hiddenSize = appContainer.width;
2510+
2511+ GSettingsController.setAutohideLauncher(false);
2512+ waitForRendering(shell)
2513+ var shownSize = appContainer.width;
2514+
2515+ compare(shownSize + launcher.panelWidth, hiddenSize);
2516+ }
2517+
2518+ function test_fullscreenAppHidesLockedOutLauncher() {
2519+ loadShell("desktop");
2520+ shell.usageScenario = "desktop";
2521+ waitForRendering(shell);
2522+
2523+ var appContainer = findChild(shell, "appContainer");
2524+ var launcher = findChild(shell, "launcher");
2525+ var launcherPanel = findChild(launcher, "launcherPanel");
2526+
2527+ GSettingsController.setAutohideLauncher(false);
2528+ waitForRendering(shell)
2529+
2530+ tryCompare(appContainer, "width", shell.width - launcherPanel.width);
2531+
2532+ var cameraApp = ApplicationManager.startApplication("camera-app");
2533+ waitUntilAppWindowIsFullyLoaded(cameraApp);
2534+
2535+ tryCompare(appContainer, "width", shell.width);
2536+ }
2537+>>>>>>> MERGE-SOURCE
2538 }
2539 }

Subscribers

People subscribed via source and target branches