Merge lp:~mzanetti/unity8/staged-spread-with-kbd into lp:unity8
- staged-spread-with-kbd
- Merge into trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Albert Astals Cid | ||||
Approved revision: | 2210 | ||||
Merged at revision: | 2236 | ||||
Proposed branch: | lp:~mzanetti/unity8/staged-spread-with-kbd | ||||
Merge into: | lp:unity8 | ||||
Prerequisite: | lp:~mzanetti/unity8/mouse-activated-staged-spreads | ||||
Diff against target: |
322 lines (+139/-24) 5 files modified
qml/Stages/PhoneStage.qml (+47/-6) qml/Stages/SpreadDelegate.qml (+20/-0) qml/Stages/TabletStage.qml (+53/-6) tests/qmltests/Stages/tst_SpreadDelegate.qml (+7/-0) tests/qmltests/tst_Shell.qml (+12/-12) |
||||
To merge this branch: | bzr merge lp:~mzanetti/unity8/staged-spread-with-kbd | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Albert Astals Cid (community) | Approve | ||
Unity8 CI Bot | continuous-integration | Needs Fixing | |
Review via email: mp+288281@code.launchpad.net |
Commit message
Allow alt+tabbing in staged mode too
Description of the change
* Are there any related MPs required for this MP to build/function as expected? Please list.
see prereq
* 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?
n/a
* If you changed the UI, has there been a design review?
vesa is testing it
Daniel d'Andrada (dandrader) wrote : | # |
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2212
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
UNSTABLE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2213
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
UNSTABLE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
- 2208. By Michael Zanetti
-
merge trunk
- 2209. By Michael Zanetti
-
allow navigating the staged spreads with keyboard too
- 2210. By Michael Zanetti
-
merge prereq
Albert Astals Cid (aacid) wrote : | # |
* Did you perform an exploratory manual test run of the code change and any related functionality?
Yes
* Did CI run pass? If not, please explain why.
Yes
* Did you make sure that the branch does not contain spurious tags?
Yes
Preview Diff
1 | === modified file 'qml/Stages/PhoneStage.qml' | |||
2 | --- qml/Stages/PhoneStage.qml 2016-03-10 12:03:17 +0000 | |||
3 | +++ qml/Stages/PhoneStage.qml 2016-03-10 12:03:17 +0000 | |||
4 | @@ -44,6 +44,33 @@ | |||
5 | 44 | } | 44 | } |
6 | 45 | } | 45 | } |
7 | 46 | 46 | ||
8 | 47 | onAltTabPressedChanged: { | ||
9 | 48 | if (!spreadEnabled || !altTabEnabled) { | ||
10 | 49 | return; | ||
11 | 50 | } | ||
12 | 51 | if (altTabPressed) { | ||
13 | 52 | spreadView.snapToSpread(); | ||
14 | 53 | priv.highlightIndex = Math.min(spreadRepeater.count - 1, 1); | ||
15 | 54 | } else { | ||
16 | 55 | spreadView.snapTo(priv.highlightIndex) | ||
17 | 56 | } | ||
18 | 57 | } | ||
19 | 58 | |||
20 | 59 | FocusScope { | ||
21 | 60 | focus: root.altTabPressed | ||
22 | 61 | |||
23 | 62 | Keys.onPressed: { | ||
24 | 63 | switch (event.key) { | ||
25 | 64 | case Qt.Key_Tab: | ||
26 | 65 | priv.highlightIndex = (priv.highlightIndex + 1) % spreadRepeater.count | ||
27 | 66 | break; | ||
28 | 67 | case Qt.Key_Backtab: | ||
29 | 68 | priv.highlightIndex = (priv.highlightIndex + spreadRepeater.count - 1) % spreadRepeater.count | ||
30 | 69 | break; | ||
31 | 70 | } | ||
32 | 71 | } | ||
33 | 72 | } | ||
34 | 73 | |||
35 | 47 | // Functions to be called from outside | 74 | // Functions to be called from outside |
36 | 48 | function updateFocusedAppOrientation() { | 75 | function updateFocusedAppOrientation() { |
37 | 49 | if (spreadRepeater.count > 0) { | 76 | if (spreadRepeater.count > 0) { |
38 | @@ -186,6 +213,7 @@ | |||
39 | 186 | 213 | ||
40 | 187 | property real oldInverseProgress: 0 | 214 | property real oldInverseProgress: 0 |
41 | 188 | property bool animateX: false | 215 | property bool animateX: false |
42 | 216 | property int highlightIndex: 0 | ||
43 | 189 | 217 | ||
44 | 190 | onFocusedAppDelegateChanged: { | 218 | onFocusedAppDelegateChanged: { |
45 | 191 | if (focusedAppDelegate) { | 219 | if (focusedAppDelegate) { |
46 | @@ -220,6 +248,10 @@ | |||
47 | 220 | spreadView.phase = 0; | 248 | spreadView.phase = 0; |
48 | 221 | spreadView.contentX = -spreadView.shift; | 249 | spreadView.contentX = -spreadView.shift; |
49 | 222 | } | 250 | } |
50 | 251 | |||
51 | 252 | onHighlightIndexChanged: { | ||
52 | 253 | spreadView.contentX = highlightIndex * spreadView.contentWidth / (spreadRepeater.count + 2) | ||
53 | 254 | } | ||
54 | 223 | } | 255 | } |
55 | 224 | Timer { | 256 | Timer { |
56 | 225 | id: fullyShowingFocusedAppUpdateTimer | 257 | id: fullyShowingFocusedAppUpdateTimer |
57 | @@ -287,6 +319,11 @@ | |||
58 | 287 | } | 319 | } |
59 | 288 | } | 320 | } |
60 | 289 | 321 | ||
61 | 322 | Behavior on contentX { | ||
62 | 323 | enabled: root.altTabPressed | ||
63 | 324 | UbuntuNumberAnimation {} | ||
64 | 325 | } | ||
65 | 326 | |||
66 | 290 | onShiftedContentXChanged: { | 327 | onShiftedContentXChanged: { |
67 | 291 | if (root.beingResized) { | 328 | if (root.beingResized) { |
68 | 292 | // Flickabe.contentX wiggles during resizes. Don't react to it. | 329 | // Flickabe.contentX wiggles during resizes. Don't react to it. |
69 | @@ -325,12 +362,17 @@ | |||
70 | 325 | } else if (shiftedContentX < positionMarker3 * width) { | 362 | } else if (shiftedContentX < positionMarker3 * width) { |
71 | 326 | snapTo(1); | 363 | snapTo(1); |
72 | 327 | } else if (phase < 2){ | 364 | } else if (phase < 2){ |
76 | 328 | // Add 1 pixel to make sure we definitely hit positionMarker4 even with rounding errors of the animation. | 365 | snapToSpread(); |
74 | 329 | snapAnimation.targetContentX = width * positionMarker4 + 1 - shift; | ||
75 | 330 | snapAnimation.start(); | ||
77 | 331 | root.opened(); | 366 | root.opened(); |
78 | 332 | } | 367 | } |
79 | 333 | } | 368 | } |
80 | 369 | |||
81 | 370 | function snapToSpread() { | ||
82 | 371 | // Add 1 pixel to make sure we definitely hit positionMarker4 even with rounding errors of the animation. | ||
83 | 372 | snapAnimation.targetContentX = (root.width * spreadView.positionMarker4) + 1 - spreadView.shift; | ||
84 | 373 | snapAnimation.start(); | ||
85 | 374 | } | ||
86 | 375 | |||
87 | 334 | function snapTo(index) { | 376 | function snapTo(index) { |
88 | 335 | if (!root.altTabEnabled) { | 377 | if (!root.altTabEnabled) { |
89 | 336 | // Reset to start instead | 378 | // Reset to start instead |
90 | @@ -433,6 +475,7 @@ | |||
91 | 433 | maximizedAppTopMargin: root.maximizedAppTopMargin | 475 | maximizedAppTopMargin: root.maximizedAppTopMargin |
92 | 434 | dropShadow: spreadView.active || priv.focusedAppDelegateIsDislocated | 476 | dropShadow: spreadView.active || priv.focusedAppDelegateIsDislocated |
93 | 435 | focusFirstApp: root.focusFirstApp | 477 | focusFirstApp: root.focusFirstApp |
94 | 478 | highlightShown: root.altTabPressed && index === priv.highlightIndex | ||
95 | 436 | 479 | ||
96 | 437 | readonly property bool isDash: model.appId == "unity8-dash" | 480 | readonly property bool isDash: model.appId == "unity8-dash" |
97 | 438 | 481 | ||
98 | @@ -665,9 +708,7 @@ | |||
99 | 665 | edge: Qt.RightEdge | 708 | edge: Qt.RightEdge |
100 | 666 | 709 | ||
101 | 667 | onPassed: { | 710 | onPassed: { |
105 | 668 | // Add 1 pixel to make sure we definitely hit positionMarker4 even with rounding errors of the animation. | 711 | spreadView.snapToSpread(); |
103 | 669 | snapAnimation.targetContentX = (root.width * spreadView.positionMarker4) + 1 - spreadView.shift; | ||
104 | 670 | snapAnimation.start(); | ||
106 | 671 | } | 712 | } |
107 | 672 | material: Component { | 713 | material: Component { |
108 | 673 | Item { | 714 | Item { |
109 | 674 | 715 | ||
110 | === modified file 'qml/Stages/SpreadDelegate.qml' | |||
111 | --- qml/Stages/SpreadDelegate.qml 2015-11-04 14:57:45 +0000 | |||
112 | +++ qml/Stages/SpreadDelegate.qml 2016-03-10 12:03:17 +0000 | |||
113 | @@ -43,6 +43,7 @@ | |||
114 | 43 | property int shellOrientationAngle | 43 | property int shellOrientationAngle |
115 | 44 | property int shellOrientation | 44 | property int shellOrientation |
116 | 45 | property QtObject orientations | 45 | property QtObject orientations |
117 | 46 | property bool highlightShown: false | ||
118 | 46 | 47 | ||
119 | 47 | function matchShellOrientation() { | 48 | function matchShellOrientation() { |
120 | 48 | if (!root.application) | 49 | if (!root.application) |
121 | @@ -261,6 +262,25 @@ | |||
122 | 261 | Behavior on opacity { UbuntuNumberAnimation {} } | 262 | Behavior on opacity { UbuntuNumberAnimation {} } |
123 | 262 | } | 263 | } |
124 | 263 | 264 | ||
125 | 265 | Rectangle { | ||
126 | 266 | id: selectionHighlight | ||
127 | 267 | objectName: "selectionHighlight" | ||
128 | 268 | anchors.fill: appWindow | ||
129 | 269 | anchors.margins: -units.gu(1) | ||
130 | 270 | color: "white" | ||
131 | 271 | opacity: root.highlightShown ? 0.15 : 0 | ||
132 | 272 | antialiasing: true | ||
133 | 273 | visible: opacity > 0 | ||
134 | 274 | } | ||
135 | 275 | |||
136 | 276 | Rectangle { | ||
137 | 277 | anchors { left: selectionHighlight.left; right: selectionHighlight.right; bottom: selectionHighlight.bottom; } | ||
138 | 278 | height: units.dp(2) | ||
139 | 279 | color: UbuntuColors.orange | ||
140 | 280 | visible: root.highlightShown | ||
141 | 281 | antialiasing: true | ||
142 | 282 | } | ||
143 | 283 | |||
144 | 264 | ApplicationWindow { | 284 | ApplicationWindow { |
145 | 265 | id: appWindow | 285 | id: appWindow |
146 | 266 | objectName: application ? "appWindow_" + application.appId : "appWindow_null" | 286 | objectName: application ? "appWindow_" + application.appId : "appWindow_null" |
147 | 267 | 287 | ||
148 | === modified file 'qml/Stages/TabletStage.qml' | |||
149 | --- qml/Stages/TabletStage.qml 2016-03-10 12:03:17 +0000 | |||
150 | +++ qml/Stages/TabletStage.qml 2016-03-10 12:03:17 +0000 | |||
151 | @@ -108,6 +108,38 @@ | |||
152 | 108 | priv.oldInverseProgress = inverseProgress; | 108 | priv.oldInverseProgress = inverseProgress; |
153 | 109 | } | 109 | } |
154 | 110 | 110 | ||
155 | 111 | onAltTabPressedChanged: { | ||
156 | 112 | if (!spreadEnabled) { | ||
157 | 113 | return; | ||
158 | 114 | } | ||
159 | 115 | if (altTabPressed) { | ||
160 | 116 | priv.highlightIndex = Math.min(spreadRepeater.count - 1, 1); | ||
161 | 117 | spreadView.snapToSpread(); | ||
162 | 118 | } else { | ||
163 | 119 | for (var i = 0; i < spreadRepeater.count; i++) { | ||
164 | 120 | if (spreadRepeater.itemAt(i).zIndex === priv.highlightIndex) { | ||
165 | 121 | spreadView.snapTo(i); | ||
166 | 122 | return; | ||
167 | 123 | } | ||
168 | 124 | } | ||
169 | 125 | } | ||
170 | 126 | } | ||
171 | 127 | |||
172 | 128 | FocusScope { | ||
173 | 129 | focus: root.altTabPressed | ||
174 | 130 | |||
175 | 131 | Keys.onPressed: { | ||
176 | 132 | switch (event.key) { | ||
177 | 133 | case Qt.Key_Tab: | ||
178 | 134 | priv.highlightIndex = (priv.highlightIndex + 1) % spreadRepeater.count | ||
179 | 135 | break; | ||
180 | 136 | case Qt.Key_Backtab: | ||
181 | 137 | priv.highlightIndex = (priv.highlightIndex + spreadRepeater.count - 1) % spreadRepeater.count | ||
182 | 138 | break; | ||
183 | 139 | } | ||
184 | 140 | } | ||
185 | 141 | } | ||
186 | 142 | |||
187 | 111 | QtObject { | 143 | QtObject { |
188 | 112 | id: priv | 144 | id: priv |
189 | 113 | objectName: "stagesPriv" | 145 | objectName: "stagesPriv" |
190 | @@ -136,6 +168,8 @@ | |||
191 | 136 | 168 | ||
192 | 137 | property int oldInverseProgress: 0 | 169 | property int oldInverseProgress: 0 |
193 | 138 | 170 | ||
194 | 171 | property int highlightIndex: 0 | ||
195 | 172 | |||
196 | 139 | onFocusedAppIdChanged: { | 173 | onFocusedAppIdChanged: { |
197 | 140 | if (priv.focusedAppId.length > 0) { | 174 | if (priv.focusedAppId.length > 0) { |
198 | 141 | var focusedApp = ApplicationManager.findApplication(focusedAppId); | 175 | var focusedApp = ApplicationManager.findApplication(focusedAppId); |
199 | @@ -191,6 +225,10 @@ | |||
200 | 191 | } | 225 | } |
201 | 192 | return oneWayFlick; | 226 | return oneWayFlick; |
202 | 193 | } | 227 | } |
203 | 228 | |||
204 | 229 | onHighlightIndexChanged: { | ||
205 | 230 | spreadView.contentX = highlightIndex * spreadView.contentWidth / (spreadRepeater.count + 2) | ||
206 | 231 | } | ||
207 | 194 | } | 232 | } |
208 | 195 | 233 | ||
209 | 196 | Connections { | 234 | Connections { |
210 | @@ -397,11 +435,16 @@ | |||
211 | 397 | } else if (shiftedContentX < phase1Width) { | 435 | } else if (shiftedContentX < phase1Width) { |
212 | 398 | snapTo(1); | 436 | snapTo(1); |
213 | 399 | } else { | 437 | } else { |
217 | 400 | // Add 1 pixel to make sure we definitely hit positionMarker4 even with rounding errors of the animation. | 438 | snapToSpread(); |
215 | 401 | snapAnimation.targetContentX = spreadView.width * spreadView.positionMarker4 + 1 - shift; | ||
216 | 402 | snapAnimation.start(); | ||
218 | 403 | } | 439 | } |
219 | 404 | } | 440 | } |
220 | 441 | |||
221 | 442 | function snapToSpread() { | ||
222 | 443 | // Add 1 pixel to make sure we definitely hit positionMarker4 even with rounding errors of the animation. | ||
223 | 444 | snapAnimation.targetContentX = (spreadView.width * spreadView.positionMarker4) + 1 - shift; | ||
224 | 445 | snapAnimation.start(); | ||
225 | 446 | } | ||
226 | 447 | |||
227 | 405 | function snapTo(index) { | 448 | function snapTo(index) { |
228 | 406 | spreadView.selectedIndex = index; | 449 | spreadView.selectedIndex = index; |
229 | 407 | snapAnimation.targetContentX = -shift; | 450 | snapAnimation.targetContentX = -shift; |
230 | @@ -487,6 +530,11 @@ | |||
231 | 487 | } | 530 | } |
232 | 488 | } | 531 | } |
233 | 489 | 532 | ||
234 | 533 | Behavior on contentX { | ||
235 | 534 | enabled: root.altTabPressed | ||
236 | 535 | UbuntuNumberAnimation {} | ||
237 | 536 | } | ||
238 | 537 | |||
239 | 490 | MouseArea { | 538 | MouseArea { |
240 | 491 | id: spreadRow | 539 | id: spreadRow |
241 | 492 | x: spreadView.contentX | 540 | x: spreadView.contentX |
242 | @@ -617,6 +665,7 @@ | |||
243 | 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 |
244 | 618 | application: ApplicationManager.get(index) | 666 | application: ApplicationManager.get(index) |
245 | 619 | closeable: !isDash | 667 | closeable: !isDash |
246 | 668 | highlightShown: root.altTabPressed && priv.highlightIndex == zIndex | ||
247 | 620 | 669 | ||
248 | 621 | readonly property bool wantsMainStage: model.stage == ApplicationInfoInterface.MainStage | 670 | readonly property bool wantsMainStage: model.stage == ApplicationInfoInterface.MainStage |
249 | 622 | 671 | ||
250 | @@ -819,9 +868,7 @@ | |||
251 | 819 | edge: Qt.RightEdge | 868 | edge: Qt.RightEdge |
252 | 820 | 869 | ||
253 | 821 | onPassed: { | 870 | onPassed: { |
257 | 822 | // Add 1 pixel to make sure we definitely hit positionMarker4 even with rounding errors of the animation. | 871 | spreadView.snapToSpread(); |
255 | 823 | snapAnimation.targetContentX = (root.width * spreadView.positionMarker4) + 1 - spreadView.shift; | ||
256 | 824 | snapAnimation.start(); | ||
258 | 825 | } | 872 | } |
259 | 826 | material: Component { | 873 | material: Component { |
260 | 827 | Item { | 874 | Item { |
261 | 828 | 875 | ||
262 | === modified file 'tests/qmltests/Stages/tst_SpreadDelegate.qml' | |||
263 | --- tests/qmltests/Stages/tst_SpreadDelegate.qml 2015-11-04 14:57:45 +0000 | |||
264 | +++ tests/qmltests/Stages/tst_SpreadDelegate.qml 2016-03-10 12:03:17 +0000 | |||
265 | @@ -308,5 +308,12 @@ | |||
266 | 308 | tryCompare(closeAnimation, "running", false); | 308 | tryCompare(closeAnimation, "running", false); |
267 | 309 | } | 309 | } |
268 | 310 | 310 | ||
269 | 311 | function test_showHighLight() { | ||
270 | 312 | loadWithGalleryApp.clicked(); | ||
271 | 313 | var highlightRect = findChild(spreadDelegateLoader.item, "selectionHighlight") | ||
272 | 314 | tryCompare(highlightRect, "visible", false) | ||
273 | 315 | spreadDelegateLoader.item.highlightShown = true; | ||
274 | 316 | tryCompare(highlightRect, "visible", true) | ||
275 | 317 | } | ||
276 | 311 | } | 318 | } |
277 | 312 | } | 319 | } |
278 | 313 | 320 | ||
279 | === modified file 'tests/qmltests/tst_Shell.qml' | |||
280 | --- tests/qmltests/tst_Shell.qml 2016-03-10 12:03:17 +0000 | |||
281 | +++ tests/qmltests/tst_Shell.qml 2016-03-10 12:03:17 +0000 | |||
282 | @@ -1447,9 +1447,17 @@ | |||
283 | 1447 | tryCompare(galleryApp, "requestedState", ApplicationInfoInterface.RequestedRunning); | 1447 | tryCompare(galleryApp, "requestedState", ApplicationInfoInterface.RequestedRunning); |
284 | 1448 | } | 1448 | } |
285 | 1449 | 1449 | ||
289 | 1450 | function test_altTabSwitchesFocus() { | 1450 | function test_altTabSwitchesFocus_data() { |
290 | 1451 | loadShell("desktop"); | 1451 | return [ |
291 | 1452 | shell.usageScenario = "desktop" | 1452 | { tag: "windowed", shellType: "desktop" }, |
292 | 1453 | { tag: "staged", shellType: "phone" }, | ||
293 | 1454 | { tag: "sidestaged", shellType: "tablet" } | ||
294 | 1455 | ]; | ||
295 | 1456 | } | ||
296 | 1457 | |||
297 | 1458 | function test_altTabSwitchesFocus(data) { | ||
298 | 1459 | loadShell(data.shellType); | ||
299 | 1460 | shell.usageScenario = data.shellType; | ||
300 | 1453 | waitForRendering(root) | 1461 | waitForRendering(root) |
301 | 1454 | 1462 | ||
302 | 1455 | var desktopStage = findChild(shell, "stage"); | 1463 | var desktopStage = findChild(shell, "stage"); |
303 | @@ -1467,18 +1475,10 @@ | |||
304 | 1467 | keyClick(Qt.Key_Tab, Qt.AltModifier) | 1475 | keyClick(Qt.Key_Tab, Qt.AltModifier) |
305 | 1468 | tryCompare(app2.session.lastSurface, "activeFocus", true) | 1476 | tryCompare(app2.session.lastSurface, "activeFocus", true) |
306 | 1469 | 1477 | ||
312 | 1470 | var desktopSpread = findChild(shell, "spread") | 1478 | // Press Alt+Tab |
308 | 1471 | |||
309 | 1472 | tryCompare(desktopSpread, "state", "") | ||
310 | 1473 | |||
311 | 1474 | // Just press Alt, make sure the spread comes up | ||
313 | 1475 | keyPress(Qt.Key_Alt); | 1479 | keyPress(Qt.Key_Alt); |
314 | 1476 | keyClick(Qt.Key_Tab); | 1480 | keyClick(Qt.Key_Tab); |
315 | 1477 | tryCompare(desktopSpread, "state", "altTab") | ||
316 | 1478 | |||
317 | 1479 | // Release control, check if spread disappears | ||
318 | 1480 | keyRelease(Qt.Key_Alt) | 1481 | keyRelease(Qt.Key_Alt) |
319 | 1481 | tryCompare(desktopSpread, "state", "") | ||
320 | 1482 | 1482 | ||
321 | 1483 | // Focus should have switched back now | 1483 | // Focus should have switched back now |
322 | 1484 | tryCompare(app3.session.lastSurface, "activeFocus", true) | 1484 | tryCompare(app3.session.lastSurface, "activeFocus", true) |
A checkbox in tst_SpreadDeleg ate.qml to control highlightShown would be nice.