on xenial help sign in
allow navigating the staged spreads with keyboard too
ea5f0a6Michael Zanetti
qml/Stages/PhoneStage.qml+47−6
@@ -44,6 +44,33 @@ AbstractStage {
44 }
44 }
45 }
45 }
46
46
47+ onAltTabPressedChanged: {
48+ if (!spreadEnabled || !altTabEnabled) {
49+ return;
50+ }
51+ if (altTabPressed) {
52+ spreadView.snapToSpread();
53+ priv.highlightIndex = Math.min(spreadRepeater.count - 1, 1);
54+ } else {
55+ spreadView.snapTo(priv.highlightIndex)
56+ }
57+ }
58+
59+ FocusScope {
60+ focus: root.altTabPressed
61+
62+ Keys.onPressed: {
63+ switch (event.key) {
64+ case Qt.Key_Tab:
65+ priv.highlightIndex = (priv.highlightIndex + 1) % spreadRepeater.count
66+ break;
67+ case Qt.Key_Backtab:
68+ priv.highlightIndex = (priv.highlightIndex + spreadRepeater.count - 1) % spreadRepeater.count
69+ break;
70+ }
71+ }
72+ }
73+
47 // Functions to be called from outside
74 // Functions to be called from outside
48 function updateFocusedAppOrientation() {
75 function updateFocusedAppOrientation() {
49 if (spreadRepeater.count > 0) {
76 if (spreadRepeater.count > 0) {
@@ -186,6 +213,7 @@ AbstractStage {
186
213
187 property real oldInverseProgress: 0
214 property real oldInverseProgress: 0
188 property bool animateX: false
215 property bool animateX: false
216+ property int highlightIndex: 0
189
217
190 onFocusedAppDelegateChanged: {
218 onFocusedAppDelegateChanged: {
191 if (focusedAppDelegate) {
219 if (focusedAppDelegate) {
@@ -220,6 +248,10 @@ AbstractStage {
220 spreadView.phase = 0;
248 spreadView.phase = 0;
221 spreadView.contentX = -spreadView.shift;
249 spreadView.contentX = -spreadView.shift;
222 }
250 }
251+
252+ onHighlightIndexChanged: {
253+ spreadView.contentX = highlightIndex * spreadView.contentWidth / (spreadRepeater.count + 2)
254+ }
223 }
255 }
224 Timer {
256 Timer {
225 id: fullyShowingFocusedAppUpdateTimer
257 id: fullyShowingFocusedAppUpdateTimer
@@ -287,6 +319,11 @@ AbstractStage {
287 }
319 }
288 }
320 }
289
321
322+ Behavior on contentX {
323+ enabled: root.altTabPressed
324+ UbuntuNumberAnimation {}
325+ }
326+
290 onShiftedContentXChanged: {
327 onShiftedContentXChanged: {
291 if (root.beingResized) {
328 if (root.beingResized) {
292 // Flickabe.contentX wiggles during resizes. Don't react to it.
329 // Flickabe.contentX wiggles during resizes. Don't react to it.
@@ -325,12 +362,17 @@ AbstractStage {
325 } else if (shiftedContentX < positionMarker3 * width) {
362 } else if (shiftedContentX < positionMarker3 * width) {
326 snapTo(1);
363 snapTo(1);
327 } else if (phase < 2){
364 } else if (phase < 2){
328− // Add 1 pixel to make sure we definitely hit positionMarker4 even with rounding errors of the animation.
365+ snapToSpread();
329− snapAnimation.targetContentX = width * positionMarker4 + 1 - shift;
330− snapAnimation.start();
331 root.opened();
366 root.opened();
332 }
367 }
333 }
368 }
369+
370+ function snapToSpread() {
371+ // Add 1 pixel to make sure we definitely hit positionMarker4 even with rounding errors of the animation.
372+ snapAnimation.targetContentX = (root.width * spreadView.positionMarker4) + 1 - spreadView.shift;
373+ snapAnimation.start();
374+ }
375+
334 function snapTo(index) {
376 function snapTo(index) {
335 if (!root.altTabEnabled) {
377 if (!root.altTabEnabled) {
336 // Reset to start instead
378 // Reset to start instead
@@ -433,6 +475,7 @@ AbstractStage {
433 maximizedAppTopMargin: root.maximizedAppTopMargin
475 maximizedAppTopMargin: root.maximizedAppTopMargin
434 dropShadow: spreadView.active || priv.focusedAppDelegateIsDislocated
476 dropShadow: spreadView.active || priv.focusedAppDelegateIsDislocated
435 focusFirstApp: root.focusFirstApp
477 focusFirstApp: root.focusFirstApp
478+ highlightShown: root.altTabPressed && index === priv.highlightIndex
436
479
437 readonly property bool isDash: model.appId == "unity8-dash"
480 readonly property bool isDash: model.appId == "unity8-dash"
438
481
@@ -665,9 +708,7 @@ AbstractStage {
665 edge: Qt.RightEdge
708 edge: Qt.RightEdge
666
709
667 onPassed: {
710 onPassed: {
668− // Add 1 pixel to make sure we definitely hit positionMarker4 even with rounding errors of the animation.
711+ spreadView.snapToSpread();
669− snapAnimation.targetContentX = (root.width * spreadView.positionMarker4) + 1 - spreadView.shift;
670− snapAnimation.start();
671 }
712 }
672 material: Component {
713 material: Component {
673 Item {
714 Item {
qml/Stages/SpreadDelegate.qml+20
@@ -43,6 +43,7 @@ FocusScope {
43 property int shellOrientationAngle
43 property int shellOrientationAngle
44 property int shellOrientation
44 property int shellOrientation
45 property QtObject orientations
45 property QtObject orientations
46+ property bool highlightShown: false
46
47
47 function matchShellOrientation() {
48 function matchShellOrientation() {
48 if (!root.application)
49 if (!root.application)
@@ -261,6 +262,25 @@ FocusScope {
261 Behavior on opacity { UbuntuNumberAnimation {} }
262 Behavior on opacity { UbuntuNumberAnimation {} }
262 }
263 }
263
264
265+ Rectangle {
266+ id: selectionHighlight
267+ objectName: "selectionHighlight"
268+ anchors.fill: appWindow
269+ anchors.margins: -units.gu(1)
270+ color: "white"
271+ opacity: root.highlightShown ? 0.15 : 0
272+ antialiasing: true
273+ visible: opacity > 0
274+ }
275+
276+ Rectangle {
277+ anchors { left: selectionHighlight.left; right: selectionHighlight.right; bottom: selectionHighlight.bottom; }
278+ height: units.dp(2)
279+ color: UbuntuColors.orange
280+ visible: root.highlightShown
281+ antialiasing: true
282+ }
283+
264 ApplicationWindow {
284 ApplicationWindow {
265 id: appWindow
285 id: appWindow
266 objectName: application ? "appWindow_" + application.appId : "appWindow_null"
286 objectName: application ? "appWindow_" + application.appId : "appWindow_null"
qml/Stages/TabletStage.qml+53−6
@@ -108,6 +108,38 @@ AbstractStage {
108 priv.oldInverseProgress = inverseProgress;
108 priv.oldInverseProgress = inverseProgress;
109 }
109 }
110
110
111+ onAltTabPressedChanged: {
112+ if (!spreadEnabled) {
113+ return;
114+ }
115+ if (altTabPressed) {
116+ priv.highlightIndex = Math.min(spreadRepeater.count - 1, 1);
117+ spreadView.snapToSpread();
118+ } else {
119+ for (var i = 0; i < spreadRepeater.count; i++) {
120+ if (spreadRepeater.itemAt(i).zIndex === priv.highlightIndex) {
121+ spreadView.snapTo(i);
122+ return;
123+ }
124+ }
125+ }
126+ }
127+
128+ FocusScope {
129+ focus: root.altTabPressed
130+
131+ Keys.onPressed: {
132+ switch (event.key) {
133+ case Qt.Key_Tab:
134+ priv.highlightIndex = (priv.highlightIndex + 1) % spreadRepeater.count
135+ break;
136+ case Qt.Key_Backtab:
137+ priv.highlightIndex = (priv.highlightIndex + spreadRepeater.count - 1) % spreadRepeater.count
138+ break;
139+ }
140+ }
141+ }
142+
111 QtObject {
143 QtObject {
112 id: priv
144 id: priv
113 objectName: "stagesPriv"
145 objectName: "stagesPriv"
@@ -136,6 +168,8 @@ AbstractStage {
136
168
137 property int oldInverseProgress: 0
169 property int oldInverseProgress: 0
138
170
171+ property int highlightIndex: 0
172+
139 onFocusedAppIdChanged: {
173 onFocusedAppIdChanged: {
140 if (priv.focusedAppId.length > 0) {
174 if (priv.focusedAppId.length > 0) {
141 var focusedApp = ApplicationManager.findApplication(focusedAppId);
175 var focusedApp = ApplicationManager.findApplication(focusedAppId);
@@ -191,6 +225,10 @@ AbstractStage {
191 }
225 }
192 return oneWayFlick;
226 return oneWayFlick;
193 }
227 }
228+
229+ onHighlightIndexChanged: {
230+ spreadView.contentX = highlightIndex * spreadView.contentWidth / (spreadRepeater.count + 2)
231+ }
194 }
232 }
195
233
196 Connections {
234 Connections {
@@ -397,11 +435,16 @@ AbstractStage {
397 } else if (shiftedContentX < phase1Width) {
435 } else if (shiftedContentX < phase1Width) {
398 snapTo(1);
436 snapTo(1);
399 } else {
437 } else {
400− // Add 1 pixel to make sure we definitely hit positionMarker4 even with rounding errors of the animation.
438+ snapToSpread();
401− snapAnimation.targetContentX = spreadView.width * spreadView.positionMarker4 + 1 - shift;
402− snapAnimation.start();
403 }
439 }
404 }
440 }
441+
442+ function snapToSpread() {
443+ // Add 1 pixel to make sure we definitely hit positionMarker4 even with rounding errors of the animation.
444+ snapAnimation.targetContentX = (spreadView.width * spreadView.positionMarker4) + 1 - shift;
445+ snapAnimation.start();
446+ }
447+
405 function snapTo(index) {
448 function snapTo(index) {
406 spreadView.selectedIndex = index;
449 spreadView.selectedIndex = index;
407 snapAnimation.targetContentX = -shift;
450 snapAnimation.targetContentX = -shift;
@@ -487,6 +530,11 @@ AbstractStage {
487 }
530 }
488 }
531 }
489
532
533+ Behavior on contentX {
534+ enabled: root.altTabPressed
535+ UbuntuNumberAnimation {}
536+ }
537+
490 MouseArea {
538 MouseArea {
491 id: spreadRow
539 id: spreadRow
492 x: spreadView.contentX
540 x: spreadView.contentX
@@ -617,6 +665,7 @@ AbstractStage {
617 dragOffset: !isDash && model.appId == priv.mainStageAppId && root.inverseProgress > 0 && spreadView.phase === 0 ? root.inverseProgress : 0
665 dragOffset: !isDash && model.appId == priv.mainStageAppId && root.inverseProgress > 0 && spreadView.phase === 0 ? root.inverseProgress : 0
618 application: ApplicationManager.get(index)
666 application: ApplicationManager.get(index)
619 closeable: !isDash
667 closeable: !isDash
668+ highlightShown: root.altTabPressed && priv.highlightIndex == zIndex
620
669
621 readonly property bool wantsMainStage: model.stage == ApplicationInfoInterface.MainStage
670 readonly property bool wantsMainStage: model.stage == ApplicationInfoInterface.MainStage
622
671
@@ -819,9 +868,7 @@ AbstractStage {
819 edge: Qt.RightEdge
868 edge: Qt.RightEdge
820
869
821 onPassed: {
870 onPassed: {
822− // Add 1 pixel to make sure we definitely hit positionMarker4 even with rounding errors of the animation.
871+ spreadView.snapToSpread();
823− snapAnimation.targetContentX = (root.width * spreadView.positionMarker4) + 1 - spreadView.shift;
824− snapAnimation.start();
825 }
872 }
826 material: Component {
873 material: Component {
827 Item {
874 Item {
tests/qmltests/Stages/tst_SpreadDelegate.qml+7
@@ -308,5 +308,12 @@ Rectangle {
308 tryCompare(closeAnimation, "running", false);
308 tryCompare(closeAnimation, "running", false);
309 }
309 }
310
310
311+ function test_showHighLight() {
312+ loadWithGalleryApp.clicked();
313+ var highlightRect = findChild(spreadDelegateLoader.item, "selectionHighlight")
314+ tryCompare(highlightRect, "visible", false)
315+ spreadDelegateLoader.item.highlightShown = true;
316+ tryCompare(highlightRect, "visible", true)
317+ }
311 }
318 }
312}
319}
tests/qmltests/tst_Shell.qml+12−12
@@ -1424,9 +1424,17 @@ Rectangle {
1424 tryCompare(galleryApp, "requestedState", ApplicationInfoInterface.RequestedRunning);
1424 tryCompare(galleryApp, "requestedState", ApplicationInfoInterface.RequestedRunning);
1425 }
1425 }
1426
1426
1427+ function test_altTabSwitchesFocus_data() {
1428+ return [
1429+ { tag: "windowed", shellType: "desktop" },
1430+ { tag: "staged", shellType: "phone" },
1431+ { tag: "sidestaged", shellType: "tablet" }
1432+ ];
1433+ }
1434+
1427− function test_altTabSwitchesFocus() {
1435+ function test_altTabSwitchesFocus(data) {
1428− loadShell("desktop");
1436+ loadShell(data.shellType);
1429− shell.usageScenario = "desktop"
1437+ shell.usageScenario = data.shellType;
1430 waitForRendering(root)
1438 waitForRendering(root)
1431
1439
1432 var desktopStage = findChild(shell, "stage");
1440 var desktopStage = findChild(shell, "stage");
@@ -1444,18 +1452,10 @@ Rectangle {
1444 keyClick(Qt.Key_Tab, Qt.ControlModifier)
1452 keyClick(Qt.Key_Tab, Qt.ControlModifier)
1445 tryCompare(app2.session.lastSurface, "activeFocus", true)
1453 tryCompare(app2.session.lastSurface, "activeFocus", true)
1446
1454
1447− var desktopSpread = findChild(shell, "spread")
1455+ // Press Alt+Tab
1448−
1449− tryCompare(desktopSpread, "state", "")
1450−
1451− // Just press Alt, make sure the spread comes up
1452 keyPress(Qt.Key_Control);
1456 keyPress(Qt.Key_Control);
1453 keyClick(Qt.Key_Tab);
1457 keyClick(Qt.Key_Tab);
1454− tryCompare(desktopSpread, "state", "altTab")
1455−
1456− // Release control, check if spread disappears
1457 keyRelease(Qt.Key_Control)
1458 keyRelease(Qt.Key_Control)
1458− tryCompare(desktopSpread, "state", "")
1459
1459
1460 // Focus should have switched back now
1460 // Focus should have switched back now
1461 tryCompare(app3.session.lastSurface, "activeFocus", true)
1461 tryCompare(app3.session.lastSurface, "activeFocus", true)