Merge lp:~mardy/unity-2d/spread-a11y-geometry into lp:unity-2d
- spread-a11y-geometry
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | 798 |
Proposed branch: | lp:~mardy/unity-2d/spread-a11y-geometry |
Merge into: | lp:unity-2d |
Diff against target: |
692 lines (+256/-152) 9 files modified
libunity-2d-private/Unity2d/GnomeBackground.qml (+4/-2) libunity-2d-private/src/screeninfo.cpp (+7/-0) libunity-2d-private/src/screeninfo.h (+3/-0) places/app/dashdeclarativeview.cpp (+2/-17) places/app/dashdeclarativeview.h (+0/-7) spread/Windows.qml (+20/-8) spread/Workspace.qml (+133/-83) spread/Workspaces.qml (+86/-34) spread/app/spreadview.cpp (+1/-1) |
To merge this branch: | bzr merge lp:~mardy/unity-2d/spread-a11y-geometry |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Michał Sawicz | functional | Needs Fixing | |
Lohith D Shivamurthy | Pending | ||
Florian Boucault | functional | Pending | |
Review via email: mp+80358@code.launchpad.net |
This proposal supersedes a proposal from 2011-10-21.
Commit message
Description of the change
[spread] Allow keyboard navigation between workspaces
Turn the Workspace repeater into a GridView, which takes care of giving the
keyboard focus to the proper child element.
This branch is based on lp:~mardy/unity-2d/geometry, and doesn't exhibit the issue of having the desktop background jumping to the left when the exit animation has been completed.
Florian Boucault (fboucault) wrote : Posted in a previous version of this proposal | # |
Florian Boucault (fboucault) wrote : Posted in a previous version of this proposal | # |
You did not actually implement accessibility in the workspace switcher, which is completely fine. The bug report might be confusing in that respect. Bottom line: do we have a separate bug report for adding a11y?
Florian Boucault (fboucault) wrote : Posted in a previous version of this proposal | # |
I see the issue I raised earlier was raised by Michaŀ in https:/
That was your answer:
"The first issue with the mouse hover could be avoided, but I chose it this way because I think it's more intuitive than requiring to click outside -- and indeed, if you zoomed into a workspace and then move the mouse out of it, you probably want to dismiss it (and if not, it's easy enough to bring it back to zoomed state). Let me know if I should change this."
I'll consult with design now.
Florian Boucault (fboucault) wrote : Posted in a previous version of this proposal | # |
btw, which one is the one that is going to be merged? this MR or that one: https:/
Florian Boucault (fboucault) wrote : Posted in a previous version of this proposal | # |
> I see the issue I raised earlier was raised by Michaŀ in
> https:/
> That was your answer:
> "The first issue with the mouse hover could be avoided, but I chose it this
> way because I think it's more intuitive than requiring to click outside -- and
> indeed, if you zoomed into a workspace and then move the mouse out of it, you
> probably want to dismiss it (and if not, it's easy enough to bring it back to
> zoomed state). Let me know if I should change this."
>
> I'll consult with design now.
Design, in the person of John Lea, spotted at least one issue with that automatic zoom on hover: when the user reaches for the launcher the zoomed workspace is automatically unzoomed, which is confusing.
Alberto Mardegan (mardy) wrote : Posted in a previous version of this proposal | # |
> btw, which one is the one that is going to be merged? this MR or that one:
> https:/
This one. I'll cancel the old one.
I'll fix the unzooming on mouse movement; about the a11y, you are right, this is about keyboard navigation only. I'm not aware of any existing bug.
Alberto Mardegan (mardy) wrote : Posted in a previous version of this proposal | # |
Updated the code. Now the zoomed view is not exited with just a mouse hover.
Michał Sawicz (saviq) wrote : | # |
One more thing:
1. Invoke spread;
2. Zoom to a workspace with 2+ windows;
3. Click on another workspace;
Expected:
Current workspace unzooms, clicked workspace gets focus and highlight;
Current result:
Highlight shows up where current workspace should be, current workspace unzooms and remains focused;
Alberto Mardegan (mardy) wrote : | # |
> One more thing:
> 1. Invoke spread;
> 2. Zoom to a workspace with 2+ windows;
> 3. Click on another workspace;
Fixed, MR updated.
Lohith D Shivamurthy (dyams) wrote : | # |
There are issues I observed:
1) Invoke the Spread.
2) Now mouse hover over any other workspace in the spread.
3) The any other workspace is highlighted.
4) Now Press Spread shortcut, Super+S.
Expected:
The highlighted workspace(mouse hovered) should be brought to front.
Actual:
The previously zoomed workspace in brought to front again, ignored the current choice.
-------
Other issue is :
1) Have at least 2+ windows in two different workspaces
2) Invoke spread
3) Now click on a workspace which contains at least two windows
3) workspace comes front with window selection. (75% Zoomed)
4) Press Spread shortcut, Super+S key
Expected:
The spread should come back to its previous state again where all workspaces are shown in the Grid.
Actual:
Some other workspace is brought to front completely.
Alberto Mardegan (mardy) wrote : | # |
Sorry, I have to step out of this, as I don't have time for fixing the issues raised.
Gerry Boland (gerboland) wrote : | # |
Unit tests not passing just yet, so merge will fail. Wait for my fixes then merge.
Lohith D Shivamurthy (dyams) wrote : | # |
There is merge conflict in places/
mardy: would it be possible for you to resolve this? Please
It would be very helpful, if you could do so.
rebase your branch with current trunk. You have to edit only two lines.
Thank you.
Lohith D Shivamurthy (dyams) wrote : | # |
This branch is moved to new location.
Please follow the new branch here
https:/
Thank you
Preview Diff
1 | === modified file 'libunity-2d-private/Unity2d/GnomeBackground.qml' |
2 | --- libunity-2d-private/Unity2d/GnomeBackground.qml 2011-10-21 08:07:49 +0000 |
3 | +++ libunity-2d-private/Unity2d/GnomeBackground.qml 2011-11-02 20:30:31 +0000 |
4 | @@ -24,6 +24,8 @@ |
5 | Item { |
6 | property string overlay_color |
7 | property real overlay_alpha |
8 | + property int offsetX: -1 |
9 | + property int offsetY: -1 |
10 | |
11 | /* Avoid redraw at rendering */ |
12 | CacheEffect { |
13 | @@ -86,8 +88,8 @@ |
14 | /* by default, place the background on top of the desktop background, |
15 | no matter where the DeclarativeView or the parent object are placed. |
16 | */ |
17 | - x: parent.mapFromItem(null, -declarativeView.globalPosition.x, 0).x |
18 | - y: parent.mapFromItem(null, 0, -declarativeView.globalPosition.y).y |
19 | + x: offsetX != -1 ? offsetX : parent.mapFromItem(null, -declarativeView.globalPosition.x, 0).x |
20 | + y: offsetY != -1 ? offsetY : parent.mapFromItem(null, 0, -declarativeView.globalPosition.y).y |
21 | |
22 | /* Possible modes are: |
23 | - "wallpaper" |
24 | |
25 | === modified file 'libunity-2d-private/src/screeninfo.cpp' |
26 | --- libunity-2d-private/src/screeninfo.cpp 2011-10-03 11:13:44 +0000 |
27 | +++ libunity-2d-private/src/screeninfo.cpp 2011-11-02 20:30:31 +0000 |
28 | @@ -71,6 +71,12 @@ |
29 | |
30 | QRect ScreenInfo::availableGeometry() const |
31 | { |
32 | + int screen = QX11Info::appScreen(); |
33 | + return QApplication::desktop()->availableGeometry(screen); |
34 | +} |
35 | + |
36 | +QRect ScreenInfo::panelsFreeGeometry() const |
37 | +{ |
38 | /* We cannot just return the system's availableGeometry(), because that |
39 | * doesn't consider the Launcher, if it's set to auto-hide. */ |
40 | int screen = QX11Info::appScreen(); |
41 | @@ -105,6 +111,7 @@ |
42 | { |
43 | if (screen == QX11Info::appScreen()) { |
44 | Q_EMIT availableGeometryChanged(availableGeometry()); |
45 | + Q_EMIT panelsFreeGeometryChanged(panelsFreeGeometry()); |
46 | } |
47 | } |
48 | |
49 | |
50 | === modified file 'libunity-2d-private/src/screeninfo.h' |
51 | --- libunity-2d-private/src/screeninfo.h 2011-07-29 13:49:34 +0000 |
52 | +++ libunity-2d-private/src/screeninfo.h 2011-11-02 20:30:31 +0000 |
53 | @@ -21,6 +21,7 @@ |
54 | Q_PROPERTY(unsigned int activeWindow READ activeWindow NOTIFY activeWindowChanged) |
55 | Q_PROPERTY(QRect geometry READ geometry NOTIFY geometryChanged) |
56 | Q_PROPERTY(QRect availableGeometry READ availableGeometry NOTIFY availableGeometryChanged) |
57 | + Q_PROPERTY(QRect panelsFreeGeometry READ panelsFreeGeometry NOTIFY panelsFreeGeometryChanged) |
58 | Q_PROPERTY(bool isCompositingManagerRunning READ isCompositingManagerRunning |
59 | NOTIFY isCompositingManagerRunningChanged) |
60 | |
61 | @@ -36,6 +37,7 @@ |
62 | WorkspacesInfo *workspaces() { return &m_workspacesInfo; } |
63 | unsigned int activeWindow() const { return m_activeWindow; } |
64 | QRect availableGeometry() const; |
65 | + QRect panelsFreeGeometry() const; |
66 | QRect geometry() const; |
67 | bool isCompositingManagerRunning() const; |
68 | |
69 | @@ -43,6 +45,7 @@ |
70 | void activeWindowChanged(unsigned int activeWindow); |
71 | void geometryChanged(QRect geometry); |
72 | void availableGeometryChanged(QRect availableGeometry); |
73 | + void panelsFreeGeometryChanged(QRect panelsFreeGeometry); |
74 | void workspacesChanged(WorkspacesInfo *workspaces); |
75 | void isCompositingManagerRunningChanged(bool); |
76 | |
77 | |
78 | === modified file 'places/app/dashdeclarativeview.cpp' |
79 | --- places/app/dashdeclarativeview.cpp 2011-10-03 11:13:44 +0000 |
80 | +++ places/app/dashdeclarativeview.cpp 2011-11-02 20:30:31 +0000 |
81 | @@ -59,7 +59,6 @@ |
82 | setTransparentBackground(QX11Info::isCompositingManagerRunning()); |
83 | |
84 | QDesktopWidget* desktop = QApplication::desktop(); |
85 | - connect(desktop, SIGNAL(resized(int)), SIGNAL(screenGeometryChanged())); |
86 | connect(desktop, SIGNAL(resized(int)), SIGNAL(updateDashModeDependingOnScreenGeometry())); |
87 | connect(desktop, SIGNAL(workAreaResized(int)), SLOT(onWorkAreaResized(int))); |
88 | |
89 | @@ -74,7 +73,6 @@ |
90 | } |
91 | |
92 | updateSize(); |
93 | - availableGeometryChanged(); |
94 | } |
95 | |
96 | |
97 | @@ -112,7 +110,7 @@ |
98 | void |
99 | DashDeclarativeView::fitToAvailableSpace() |
100 | { |
101 | - QRect rect = availableGeometry(); |
102 | + QRect rect = m_screenInfo->panelsFreeGeometry(); |
103 | move(rect.topLeft()); |
104 | setFixedSize(rect.size()); |
105 | } |
106 | @@ -120,7 +118,7 @@ |
107 | void |
108 | DashDeclarativeView::resizeToDesktopModeSize() |
109 | { |
110 | - QRect rect = availableGeometry(); |
111 | + QRect rect = m_screenInfo->panelsFreeGeometry(); |
112 | int screenRight = rect.right(); |
113 | |
114 | rect.setWidth(qMin(DASH_DESKTOP_WIDTH, rect.width())); |
115 | @@ -261,19 +259,6 @@ |
116 | setActive(true); |
117 | } |
118 | |
119 | -const QRect |
120 | -DashDeclarativeView::screenGeometry() const |
121 | -{ |
122 | - QDesktopWidget* desktop = QApplication::desktop(); |
123 | - return desktop->screenGeometry(this); |
124 | -} |
125 | - |
126 | -QRect |
127 | -DashDeclarativeView::availableGeometry() const |
128 | -{ |
129 | - return m_screenInfo->availableGeometry(); |
130 | -} |
131 | - |
132 | void |
133 | DashDeclarativeView::keyPressEvent(QKeyEvent* event) |
134 | { |
135 | |
136 | === modified file 'places/app/dashdeclarativeview.h' |
137 | --- places/app/dashdeclarativeview.h 2011-10-03 11:13:44 +0000 |
138 | +++ places/app/dashdeclarativeview.h 2011-11-02 20:30:31 +0000 |
139 | @@ -32,8 +32,6 @@ |
140 | Q_PROPERTY(bool expanded READ expanded WRITE setExpanded NOTIFY expandedChanged) |
141 | Q_PROPERTY(DashMode dashMode READ dashMode WRITE setDashMode NOTIFY dashModeChanged) |
142 | Q_PROPERTY(QString activeLens READ activeLens WRITE setActiveLens NOTIFY activeLensChanged) |
143 | - Q_PROPERTY(QRect screenGeometry READ screenGeometry NOTIFY screenGeometryChanged) |
144 | - Q_PROPERTY(QRect availableGeometry READ availableGeometry NOTIFY availableGeometryChanged) |
145 | |
146 | public: |
147 | enum DashMode { |
148 | @@ -46,8 +44,6 @@ |
149 | bool active() const; |
150 | DashMode dashMode() const; |
151 | const QString& activeLens() const; |
152 | - const QRect screenGeometry() const; |
153 | - QRect availableGeometry() const; |
154 | bool expanded() const; |
155 | |
156 | /* setters */ |
157 | @@ -67,9 +63,6 @@ |
158 | void expandedChanged(bool); |
159 | void activeLensChanged(const QString&); |
160 | |
161 | - void screenGeometryChanged(); |
162 | - void availableGeometryChanged(); |
163 | - |
164 | protected: |
165 | void resizeEvent(QResizeEvent*); |
166 | virtual void showEvent(QShowEvent *event); |
167 | |
168 | === modified file 'spread/Windows.qml' |
169 | --- spread/Windows.qml 2011-08-09 13:33:24 +0000 |
170 | +++ spread/Windows.qml 2011-11-02 20:30:31 +0000 |
171 | @@ -32,22 +32,23 @@ |
172 | |
173 | The context property called control is the initiator of the entire spread process, and |
174 | is triggered by D-Bus calls on the C++ side. |
175 | - |
176 | - The ScreenInfo's property availableGeometry represents the available space on the screen (i.e. |
177 | - screen minus launcher, panels, etc.). |
178 | */ |
179 | |
180 | GridView { |
181 | id: windows |
182 | |
183 | signal clicked |
184 | + signal entered |
185 | signal windowActivated(variant window) |
186 | |
187 | + keyNavigationWraps: state == "zoomed" |
188 | + |
189 | MouseArea { |
190 | anchors.fill: parent |
191 | onClicked: windows.clicked() |
192 | /* Eating all mouse events so that they are not passed beneath the workspace */ |
193 | hoverEnabled: true |
194 | + onEntered: windows.entered() |
195 | } |
196 | |
197 | /* This proxy model takes care of removing all windows that are not on |
198 | @@ -79,7 +80,12 @@ |
199 | cellWidth: Math.floor(width / columns) |
200 | cellHeight: height / rows |
201 | |
202 | - model: filteredByApplication |
203 | + /* Set the model only when the component is ready; otherwise, the |
204 | + * initialization gets somehow messed up and the "columns" and "rows" |
205 | + * variables are set to those of the first workspace. */ |
206 | + Component.onCompleted: { |
207 | + model = filteredByApplication |
208 | + } |
209 | |
210 | delegate: |
211 | Item { |
212 | @@ -93,6 +99,11 @@ |
213 | itemHeight: window.size.height |
214 | parent: windows |
215 | } |
216 | + /* We are not using GridView.isCurrentItem because it mysteriously |
217 | + * returns "false" the first time the spread is activated. Couldn't |
218 | + * reproduce the same behaviour with simpler test cases. |
219 | + */ |
220 | + focus: GridView.view.currentIndex == index |
221 | |
222 | /* Workaround http://bugreports.qt.nokia.com/browse/QTBUG-15642 where onAdd is not called for the first item */ |
223 | //GridView.onAdd: |
224 | @@ -106,8 +117,10 @@ |
225 | switch (event.key) { |
226 | case Qt.Key_Enter: |
227 | case Qt.Key_Return: |
228 | + { |
229 | windows.windowActivated(spreadWindow) |
230 | event.accepted = true |
231 | + } |
232 | } |
233 | } |
234 | |
235 | @@ -121,7 +134,8 @@ |
236 | |
237 | onEntered: { |
238 | windows.currentIndex = index |
239 | - cell.forceActiveFocus() |
240 | + /* Make sure the workspace is notified as well */ |
241 | + windows.entered() |
242 | } |
243 | |
244 | onClicked: windows.windowActivated(spreadWindow) |
245 | @@ -141,14 +155,12 @@ |
246 | Behavior on height { enabled: spreadWindow.animateFollow; NumberAnimation { duration: Utils.transitionDuration; easing.type: Easing.InOutQuad } } |
247 | |
248 | windowInfo: window |
249 | - state: windows.state |
250 | + state: windows.state == "screen" ? "screen" : "spread" |
251 | states: [ |
252 | State { |
253 | name: "screen" |
254 | PropertyChanges { |
255 | target: spreadWindow |
256 | - /* Note that we subtract the availableGeometry x and y since window.location is |
257 | - expressed in global screen coordinates. */ |
258 | x: window.position.x - declarativeView.globalPosition.x |
259 | y: window.position.y - declarativeView.globalPosition.y |
260 | width: window.size.width |
261 | |
262 | === modified file 'spread/Workspace.qml' |
263 | --- spread/Workspace.qml 2011-08-10 00:10:39 +0000 |
264 | +++ spread/Workspace.qml 2011-11-02 20:30:31 +0000 |
265 | @@ -23,100 +23,150 @@ |
266 | FocusScope { |
267 | id: workspace |
268 | |
269 | - transformOrigin: Item.TopLeft |
270 | - |
271 | property real unzoomedScale |
272 | - property int unzoomedX |
273 | - property int unzoomedY |
274 | property real zoomedScale |
275 | property int zoomedX |
276 | property int zoomedY |
277 | + property alias windowCount: windows.count |
278 | |
279 | + property int screenOriginX |
280 | + property int screenOriginY |
281 | signal clicked |
282 | - |
283 | - GnomeBackground { |
284 | - anchors.fill: parent |
285 | - overlay_color: "black" |
286 | - overlay_alpha: 0 |
287 | - |
288 | - clip: true |
289 | - cached: false |
290 | - } |
291 | - |
292 | - Windows { |
293 | - state: workspace.state == "screen" ? "screen" : "spread" |
294 | - anchors.fill: parent |
295 | - focus: true |
296 | - onClicked: workspace.clicked() |
297 | - onWindowActivated: { |
298 | - if (workspace.state != "zoomed") { |
299 | - workspace.clicked() |
300 | - } else { |
301 | - /* Hack to make sure the window is on top of the others during the |
302 | - outro animation */ |
303 | - window.z = 9999 |
304 | - switcher.activateWindow(window.windowInfo) |
305 | - } |
306 | - } |
307 | - } |
308 | - |
309 | - states: [ |
310 | - State { |
311 | - name: "unzoomed" |
312 | - PropertyChanges { |
313 | - target: workspace |
314 | - scale: unzoomedScale |
315 | - x: unzoomedX |
316 | - y: unzoomedY |
317 | - z: 0 |
318 | - } |
319 | - }, |
320 | - State { |
321 | - name: "zoomed" |
322 | - PropertyChanges { |
323 | - target: workspace |
324 | - scale: zoomedScale |
325 | - x: zoomedX |
326 | - y: zoomedY |
327 | - z: 2 |
328 | - } |
329 | - }, |
330 | - State { |
331 | - name: "screen" |
332 | - PropertyChanges { |
333 | - target: workspace |
334 | - scale: 1.0 |
335 | - x: 0 |
336 | - y: 0 |
337 | - z: 2 |
338 | - } |
339 | - } |
340 | - ] |
341 | - |
342 | - transitions: [ |
343 | - Transition { |
344 | - NumberAnimation { |
345 | - target: workspace |
346 | - properties: "x,y,scale" |
347 | - duration: Utils.transitionDuration |
348 | - easing.type: Easing.InOutQuad |
349 | - } |
350 | - }, |
351 | - Transition { |
352 | - to: "unzoomed" |
353 | - SequentialAnimation { |
354 | - /* When going to default state put the workspace underneath the |
355 | - workspace in zoomed state but not on the same plane as the |
356 | - workspaces also in the default state until the end of the transition. */ |
357 | - PropertyAction { property: "z"; value: 1 } |
358 | + signal entered |
359 | + |
360 | + onStateChanged: { |
361 | + screenOriginX = mapFromItem(switcher, 0, 0).x |
362 | + screenOriginY = mapFromItem(switcher, 0, 0).y |
363 | + } |
364 | + |
365 | + Item { |
366 | + id: workspaceWindow |
367 | + |
368 | + transformOrigin: Item.TopLeft |
369 | + width: switcher.width |
370 | + height: switcher.height |
371 | + state: parent.state |
372 | + |
373 | + GnomeBackground { |
374 | + anchors.fill: parent |
375 | + overlay_color: "black" |
376 | + overlay_alpha: 0 |
377 | + |
378 | + clip: true |
379 | + cached: false |
380 | + offsetX: -screen.panelsFreeGeometry.x |
381 | + offsetY: -screen.panelsFreeGeometry.y |
382 | + } |
383 | + |
384 | + Windows { |
385 | + id: windows |
386 | + state: workspace.state |
387 | + anchors.fill: parent |
388 | + focus: true |
389 | + onClicked: workspace.clicked() |
390 | + onEntered: workspace.entered() |
391 | + onWindowActivated: { |
392 | + if (workspace.state != "zoomed") { |
393 | + workspace.clicked() |
394 | + } else { |
395 | + /* Hack to make sure the window is on top of the others during the |
396 | + outro animation */ |
397 | + window.z = 9999 |
398 | + switcher.activateWindow(window.windowInfo) |
399 | + } |
400 | + } |
401 | + } |
402 | + |
403 | + states: [ |
404 | + State { |
405 | + name: "unzoomed" |
406 | + PropertyChanges { |
407 | + target: workspaceWindow |
408 | + scale: unzoomedScale |
409 | + x: switcher.spacing / 2 |
410 | + y: switcher.spacing / 2 |
411 | + } |
412 | + PropertyChanges { |
413 | + target: workspace |
414 | + z: 0 |
415 | + } |
416 | + }, |
417 | + State { |
418 | + name: "zoomed" |
419 | + PropertyChanges { |
420 | + target: workspaceWindow |
421 | + scale: zoomedScale |
422 | + x: zoomedX + workspace.screenOriginX |
423 | + y: zoomedY + workspace.screenOriginY |
424 | + } |
425 | + PropertyChanges { |
426 | + target: workspace |
427 | + z: 2 |
428 | + } |
429 | + }, |
430 | + State { |
431 | + name: "screen" |
432 | + PropertyChanges { |
433 | + target: workspaceWindow |
434 | + scale: 1.0 |
435 | + x: workspace.screenOriginX |
436 | + y: workspace.screenOriginY |
437 | + } |
438 | + PropertyChanges { |
439 | + target: workspace |
440 | + z: 2 |
441 | + } |
442 | + } |
443 | + ] |
444 | + |
445 | + transitions: [ |
446 | + Transition { |
447 | + /* Disable animations when coming from the base state */ |
448 | + from: "" |
449 | + }, |
450 | + Transition { |
451 | NumberAnimation { |
452 | - target: workspace |
453 | + target: workspaceWindow |
454 | properties: "x,y,scale" |
455 | duration: Utils.transitionDuration |
456 | easing.type: Easing.InOutQuad |
457 | } |
458 | - PropertyAction { property: "z" } |
459 | + }, |
460 | + Transition { |
461 | + to: "unzoomed" |
462 | + SequentialAnimation { |
463 | + /* When going to default state put the workspace underneath the |
464 | + workspace in zoomed state but not on the same plane as the |
465 | + workspaces also in the default state until the end of the transition. */ |
466 | + PropertyAction { target: workspace; property: "z"; value: 1 } |
467 | + NumberAnimation { |
468 | + target: workspaceWindow |
469 | + properties: "x,y,scale" |
470 | + duration: Utils.transitionDuration |
471 | + easing.type: Easing.InOutQuad |
472 | + } |
473 | + PropertyAction { target: workspace; property: "z" } |
474 | + } |
475 | + } |
476 | + ] |
477 | + } |
478 | + |
479 | + Keys.onPressed: { |
480 | + switch (event.key) { |
481 | + case Qt.Key_Enter: |
482 | + case Qt.Key_Return: |
483 | + { |
484 | + clicked() |
485 | + event.accepted = true |
486 | } |
487 | } |
488 | - ] |
489 | + } |
490 | + |
491 | + function setFocusOnFirstWindow() { |
492 | + windows.currentIndex = 0 |
493 | + } |
494 | + |
495 | + function setFocusOnLastWindow() { |
496 | + windows.currentIndex = windows.count - 1 |
497 | + } |
498 | } |
499 | |
500 | === modified file 'spread/Workspaces.qml' |
501 | --- spread/Workspaces.qml 2011-10-18 12:19:18 +0000 |
502 | +++ spread/Workspaces.qml 2011-11-02 20:30:31 +0000 |
503 | @@ -29,28 +29,35 @@ |
504 | property int rows: screen.workspaces.rows |
505 | |
506 | property int margin: 35 |
507 | - property int spacing: 5 |
508 | + property int spacing: 4 |
509 | |
510 | /* Effective area available for laying out the workspaces after considering |
511 | inter-workspace spaces */ |
512 | - property int availableWidth: switcher.width - ((columns - 1) * spacing) |
513 | - property int availableHeight: switcher.height - ((rows - 1) * spacing) |
514 | - |
515 | - /* Scale of each workspace when laid out in the switcher grid |
516 | - Note that all scale calculations are done using the desktop's available |
517 | - geometry as this is the "natural" (initial) size of every workspace. |
518 | - |
519 | - FIXME: this seems to be broken in the case of 10 workspaces and 4x4 layout. |
520 | - it does only display a 3x3 grid for some reason. |
521 | - */ |
522 | - property bool isLayoutHorizontal: (columns * screen.availableGeometry.width) > |
523 | - (rows * screen.availableGeometry.height) |
524 | - property real cellScale: (isLayoutHorizontal) ? (availableWidth / columns / switcher.width) : |
525 | - (availableHeight / rows / switcher.height) |
526 | + property int availableWidth: switcher.width - (columns * spacing) |
527 | + property int availableHeight: switcher.height - (rows * spacing) |
528 | + |
529 | + property int maxCellWidth: Math.floor(availableWidth / columns) |
530 | + property int maxCellHeight: Math.floor(availableHeight / rows) |
531 | + /* Depending on the aspect ratio of the final workspaces layout, we will |
532 | + have either vertical or horizontal margins. That is, we will either: |
533 | + - use maxCellWidth as base cell width and compute the height based on the |
534 | + screen ratio, or |
535 | + - use maxCellHeight for the base cell height and compute the width based |
536 | + on the screen ratio. |
537 | + To figure out which way to go, compute the other size and see how things |
538 | + would fit inside the screen. */ |
539 | + property int computedCellHeight: maxCellWidth * switcher.height / switcher.width |
540 | + property int computedCellWidth: maxCellHeight * switcher.width / switcher.height |
541 | + |
542 | + property bool useWidth: (computedCellHeight + spacing) * rows <= switcher.height |
543 | + |
544 | + property int cellWidth: useWidth ? maxCellWidth : computedCellWidth |
545 | + property int cellHeight: useWidth ? computedCellHeight : maxCellHeight |
546 | + property real cellScale: cellWidth / switcher.width |
547 | |
548 | /* Scale of a workspace when the user zooms on it (fills most of the switcher, leaving a margin to see |
549 | the corners of the other workspaces below it) */ |
550 | - property bool isDesktopHorizontal: screen.availableGeometry.width > screen.availableGeometry.height |
551 | + property bool isDesktopHorizontal: screen.panelsFreeGeometry.width > screen.panelsFreeGeometry.height |
552 | property real zoomedScale: (isDesktopHorizontal) ? ((width - 2*margin) / switcher.width) : |
553 | ((height - 2*margin) / switcher.height) |
554 | |
555 | @@ -71,34 +78,62 @@ |
556 | /* Group all Workspace elements into a single Item to help workspaceByNumber |
557 | iterate over less items than it would need to if the Repeater was adding children |
558 | to the switcher itself. */ |
559 | - Repeater { |
560 | + GridView { |
561 | id: workspaces |
562 | + anchors.centerIn: parent |
563 | + |
564 | + width: cellWidth * columns |
565 | + height: cellHeight * rows |
566 | |
567 | model: screen.workspaces.count |
568 | + cellWidth: parent.cellWidth + spacing |
569 | + cellHeight: parent.cellHeight + spacing |
570 | + keyNavigationWraps: true |
571 | + property string windowFocus |
572 | + Keys.onPressed: { |
573 | + if (event.key == Qt.Key_Left || event.key == Qt.Key_Up) { |
574 | + windowFocus = "last" |
575 | + } else if (event.key == Qt.Key_Right || event.key == Qt.Key_Down) { |
576 | + windowFocus = "first" |
577 | + } |
578 | + } |
579 | + |
580 | + highlight: Rectangle { |
581 | + color: "orange" |
582 | + x: workspaces.currentItem.x |
583 | + y: workspaces.currentItem.y |
584 | + z: -1 |
585 | + width: workspaces.cellWidth |
586 | + height: workspaces.cellHeight |
587 | + visible: workspaces.currentItem.state == "unzoomed" |
588 | + } |
589 | + highlightFollowsCurrentItem: false |
590 | + |
591 | delegate: Workspace { |
592 | id: workspace |
593 | |
594 | - /* FIXME: This is ok right now since we ignore screen.orientation and |
595 | - screen.startingCorner, but we should respect them eventually */ |
596 | property int workspaceNumber: index |
597 | - property int row: Math.floor(index / columns) |
598 | - property int column: index % columns |
599 | - |
600 | - width: switcher.width |
601 | - height: switcher.height |
602 | - |
603 | - /* Organize the workspaces in a grid in 'unzoomed' state */ |
604 | - unzoomedX: column * (switcher.width * cellScale) + (column * switcher.spacing) |
605 | - unzoomedY: row * (switcher.height * cellScale) + (row * switcher.spacing) |
606 | + |
607 | + width: workspaces.cellWidth |
608 | + height: workspaces.cellHeight |
609 | + |
610 | unzoomedScale: switcher.cellScale |
611 | |
612 | /* Center the workspace in 'zoomed' state */ |
613 | - zoomedX: (switcher.width - width*zoomedScale) / 2 |
614 | - zoomedY: (switcher.height - height*zoomedScale) / 2 |
615 | + zoomedX: switcher.width * (1 - zoomedScale) / 2 |
616 | + zoomedY: switcher.height * (1 - zoomedScale) / 2 |
617 | zoomedScale: switcher.zoomedScale |
618 | |
619 | - focus: zoomedWorkspace == workspaceNumber |
620 | - |
621 | + Connections { |
622 | + target: workspaces |
623 | + onCurrentIndexChanged: { |
624 | + if (workspaces.windowFocus == "first") { |
625 | + setFocusOnFirstWindow() |
626 | + } else if (workspaces.windowFocus == "last") { |
627 | + setFocusOnLastWindow() |
628 | + } |
629 | + } |
630 | + } |
631 | state: { |
632 | if (initial) { |
633 | if (screen.workspaces.current == workspaceNumber) { |
634 | @@ -115,16 +150,32 @@ |
635 | } |
636 | } |
637 | |
638 | + onEntered: { |
639 | + if (zoomedWorkspace == -1) { |
640 | + workspaces.currentIndex = index |
641 | + } |
642 | + } |
643 | + |
644 | onClicked: { |
645 | if (zoomedWorkspace == workspaceNumber) { |
646 | activateWorkspace(workspaceNumber) |
647 | } else if (zoomedWorkspace == -1) { |
648 | - zoomedWorkspace = workspaceNumber |
649 | + if (windowCount <= 1) { |
650 | + activateWorkspace(workspaceNumber) |
651 | + } else { |
652 | + workspaces.currentIndex = index |
653 | + zoomedWorkspace = workspaceNumber |
654 | + } |
655 | } else { |
656 | + workspaces.currentIndex = index |
657 | zoomedWorkspace = -1 |
658 | } |
659 | } |
660 | } |
661 | + |
662 | + onCurrentIndexChanged: { |
663 | + zoomedWorkspace = -1 |
664 | + } |
665 | } |
666 | |
667 | /* FIXME: bad naming. Ideas: screenModeActivated, initialState */ |
668 | @@ -166,9 +217,10 @@ |
669 | |
670 | spreadView.show() |
671 | spreadView.forceActivateWindow() |
672 | + workspaces.currentIndex = screen.workspaces.current |
673 | /* This is necessary otherwise we don't get keypresses until the user does a |
674 | mouse over on a window */ |
675 | - switcher.forceActiveFocus() |
676 | + workspaces.forceActiveFocus() |
677 | initial = false |
678 | } |
679 | |
680 | |
681 | === modified file 'spread/app/spreadview.cpp' |
682 | --- spread/app/spreadview.cpp 2011-10-03 11:13:44 +0000 |
683 | +++ spread/app/spreadview.cpp 2011-11-02 20:30:31 +0000 |
684 | @@ -38,7 +38,7 @@ |
685 | |
686 | if(screen == current_screen) |
687 | { |
688 | - QRect geometry = m_screenInfo->availableGeometry(); |
689 | + QRect geometry = m_screenInfo->panelsFreeGeometry(); |
690 | setGeometry(geometry); |
691 | setFixedSize(geometry.size()); |
692 | } |
Quite nice functionally. Though I did spot one issue: when a workspace is zoomed and you hover an unzoomed workspace, the zoomed workspace is instantly unzoomed and the one hovered becomes focus.