@@ -77,7 +77,6 @@ Rectangle {
7777 var appIndex = priv.indexOf(appId);
7878 var appDelegate = appRepeater.itemAt(appIndex);
7979 appDelegate.minimized = false;
80− appDelegate.focus = true;
8180 ApplicationManager.focusApplication(appId);
8281 }
8382 }
@@ -91,12 +90,6 @@ Rectangle {
9190 return index >= 0 && index < appRepeater.count ? appRepeater.itemAt(index) : null
9291 }
9392
94− onFocusedAppDelegateChanged: {
95− if (focusedAppDelegate) {
96− focusedAppDelegate.focus = true;
97− }
98− }
99−
10093 function indexOf(appId) {
10194 for (var i = 0; i < ApplicationManager.count; i++) {
10295 if (ApplicationManager.get(i).appId == appId) {
@@ -133,23 +126,28 @@ Rectangle {
133126 id: appContainer
134127 objectName: "appContainer"
135128 anchors.fill: parent
129+ focus: true
136130
137131 Keys.onPressed: {
138132 switch (event.key) {
139133 case Qt.Key_Left:
140134 case Qt.Key_Backtab:
141135 selectPrevious(event.isAutoRepeat)
136+ event.accepted = true;
142137 break;
143138 case Qt.Key_Right:
144139 case Qt.Key_Tab:
145140 selectNext(event.isAutoRepeat)
141+ event.accepted = true;
146142 break;
147143 case Qt.Key_Escape:
148144 appRepeater.highlightedIndex = -1
145+ event.accepted = true;
149146 case Qt.Key_Enter:
150147 case Qt.Key_Return:
151148 case Qt.Key_Space:
152149 root.state = ""
150+ event.accepted = true;
153151 }
154152 }
155153
@@ -180,7 +178,8 @@ Rectangle {
180178
181179 function focusSelected() {
182180 if (appRepeater.highlightedIndex != -1) {
183− appRepeater.itemAt(appRepeater.highlightedIndex).focus = true;
181+ var application = ApplicationManager.get(appRepeater.highlightedIndex);
182+ ApplicationManager.focusApplication(application.appId);
184183 }
185184 }
186185
@@ -192,6 +191,15 @@ Rectangle {
192191 property int highlightedIndex: -1
193192 property int closingIndex: -1
194193
194+ function indexOf(delegateItem) {
195+ for (var i = 0; i < appRepeater.count; i++) {
196+ if (appRepeater.itemAt(i) === delegateItem) {
197+ return i;
198+ }
199+ }
200+ return -1;
201+ }
202+
195203 delegate: FocusScope {
196204 id: appDelegate
197205 z: ApplicationManager.count - index
@@ -199,23 +207,19 @@ Rectangle {
199207 width: units.gu(60)
200208 height: units.gu(50)
201209
210+ property int windowWidth: 0
211+ property int windowHeight: 0
212+ // We don't want to resize the actual application when we're transforming things for the spread only
213+ onWidthChanged: if (appDelegate.state !== "altTab") windowWidth = width
214+ onHeightChanged: if (appDelegate.state !== "altTab") windowHeight = height
215+
202216 readonly property int minWidth: units.gu(10)
203217 readonly property int minHeight: units.gu(10)
204218
205219 property bool maximized: false
206220 property bool minimized: false
207221
208− onFocusChanged: {
209− if (focus && ApplicationManager.focusedApplicationId !== model.appId) {
210− ApplicationManager.requestFocusApplication(model.appId);
211− decoratedWindow.forceActiveFocus();
212− }
213− }
214− Component.onCompleted: {
215− if (ApplicationManager.focusedApplicationId == model.appId) {
216− decoratedWindow.forceActiveFocus();
217− }
218− }
222+ focus: priv.focusedAppId === model.appId && root.state !== "altTab"
219223
220224 Binding {
221225 target: ApplicationManager.get(index)
@@ -266,7 +270,9 @@ Rectangle {
266270 PropertyChanges {
267271 target: appDelegate
268272 x: spreadMaths.animatedX
269− y: spreadMaths.animatedY + (appDelegate.height - decoratedWindow.height)
273+ y: spreadMaths.animatedY + (appDelegate.height - decoratedWindow.height) - units.gu(2)
274+ width: spreadMaths.spreadHeight
275+ height: spreadMaths.sceneHeight
270276 angle: spreadMaths.animatedAngle
271277 itemScale: spreadMaths.scale
272278 itemScaleOriginY: decoratedWindow.height / 2;
@@ -281,6 +287,7 @@ Rectangle {
281287 width: spreadMaths.spreadHeight
282288 height: spreadMaths.spreadHeight
283289 shadowOpacity: spreadMaths.shadowOpacity
290+ anchors.topMargin: units.gu(2)
284291 }
285292 PropertyChanges {
286293 target: tileInfo
@@ -302,6 +309,18 @@ Rectangle {
302309 from: "maximized,minimized,normal,"
303310 to: "maximized,minimized,normal,"
304311 PropertyAnimation { target: appDelegate; properties: "x,y,opacity,width,height,scale" }
312+ },
313+ Transition {
314+ from: ""
315+ to: "altTab"
316+ PropertyAction { target: appDelegate; properties: "y,angle,z,itemScale,itemScaleOriginY" }
317+ PropertyAction { target: decoratedWindow; properties: "anchors.topMargin" }
318+ PropertyAnimation {
319+ target: appDelegate; properties: "x"
320+ from: root.width
321+ duration: rightEdgePushArea.containsMouse ? UbuntuAnimation.FastDuration :0
322+ easing: UbuntuAnimation.StandardEasing
323+ }
305324 }
306325 ]
307326 property real angle: 0
@@ -326,7 +345,7 @@ Rectangle {
326345 resizeHandleWidth: units.gu(2)
327346 windowId: model.appId // FIXME: Change this to point to windowId once we have such a thing
328347
329− onPressed: appDelegate.focus = true;
348+ onPressed: ApplicationManager.focusApplication(model.appId);
330349 }
331350
332351 DecoratedWindow {
@@ -334,11 +353,11 @@ Rectangle {
334353 objectName: "decoratedWindow"
335354 anchors.left: appDelegate.left
336355 anchors.top: appDelegate.top
337− windowWidth: appDelegate.width
338− windowHeight: appDelegate.height
356+ windowWidth: appDelegate.windowWidth
357+ windowHeight: appDelegate.windowHeight
339358 application: ApplicationManager.get(index)
340359 active: ApplicationManager.focusedApplicationId === model.appId
341− focus: false
360+ focus: true
342361
343362 onClose: ApplicationManager.stopApplication(model.appId)
344363 onMaximize: appDelegate.maximize()
@@ -363,31 +382,6 @@ Rectangle {
363382 anchors.fill: parent
364383 anchors.margins: -units.gu(2)
365384 enabled: false
366− hoverEnabled: enabled
367−
368− // There is a bug in MouseArea where containsMouse doesn't
369− // return to false if the MouseArea is disabled while
370− // containing the mouse. Let's manage the property our own.
371− property bool upperThirdContainsMouse: false
372− onContainsMouseChanged: evaluateContainsMouse()
373− onMouseYChanged: evaluateContainsMouse()
374− function evaluateContainsMouse() {
375− if (containsMouse) {
376− appRepeater.highlightedIndex = index
377− }
378−
379− if (containsMouse && mouseY < height / 3) {
380− spreadSelectArea.upperThirdContainsMouse = true
381− } else {
382− spreadSelectArea.upperThirdContainsMouse = false;
383− }
384− }
385− onEnabledChanged: {
386− if (!enabled) {
387− spreadSelectArea.upperThirdContainsMouse = false
388− }
389− }
390−
391385 onClicked: {
392386 root.state = ""
393387 }
@@ -396,24 +390,28 @@ Rectangle {
396390
397391 Image {
398392 id: closeImage
399− anchors { left: parent.left; top: parent.top; leftMargin: -height / 2; topMargin: -height / 2 + spreadMaths.closeIconOffset }
393+ anchors { left: parent.left; top: parent.top; leftMargin: -height / 2; topMargin: -height / 2 + spreadMaths.closeIconOffset + units.gu(2) }
400394 source: "graphics/window-close.svg"
401− visible: spreadSelectArea.upperThirdContainsMouse
395+ readonly property var mousePos: hoverMouseArea.mapToItem(appDelegate, hoverMouseArea.mouseX, hoverMouseArea.mouseY)
396+ visible: index == appRepeater.highlightedIndex
397+ && mousePos.y < (decoratedWindow.height / 3)
398+ && mousePos.y > -units.gu(4)
399+ && mousePos.x > -units.gu(4)
400+ && mousePos.x < (decoratedWindow.width * 2 / 3)
402401 height: units.gu(1.5)
403402 width: height
404403 sourceSize.width: width
405404 sourceSize.height: height
406− }
407405
408− MouseArea {
409− id: closeMouseArea
410− objectName: "closeMouseArea"
411− anchors.fill: closeImage
412− anchors.margins: -units.gu(2)
413− enabled: spreadSelectArea.upperThirdContainsMouse
414− onClicked: {
415− appRepeater.closingIndex = index;
416− ApplicationManager.stopApplication(model.appId)
406+ MouseArea {
407+ id: closeMouseArea
408+ objectName: "closeMouseArea"
409+ anchors.fill: closeImage
410+ anchors.margins: -units.gu(2)
411+ onClicked: {
412+ appRepeater.closingIndex = index;
413+ ApplicationManager.stopApplication(model.appId)
414+ }
417415 }
418416 }
419417
@@ -462,9 +460,58 @@ Rectangle {
462460 }
463461 }
464462
463+ MouseArea {
464+ id: hoverMouseArea
465+ anchors.fill: appContainer
466+ propagateComposedEvents: true
467+ hoverEnabled: true
468+ enabled: false
469+
470+ property int scrollAreaWidth: root.width / 3
471+ property bool progressiveScrollingEnabled: false
472+
473+ onMouseXChanged: {
474+ mouse.accepted = false
475+
476+ // Find the hovered item and mark it active
477+ var mapped = mapToItem(appContainer, hoverMouseArea.mouseX, hoverMouseArea.mouseY)
478+ var itemUnder = appContainer.childAt(mapped.x, mapped.y)
479+ if (itemUnder) {
480+ mapped = mapToItem(itemUnder, hoverMouseArea.mouseX, hoverMouseArea.mouseY)
481+ var delegateChild = itemUnder.childAt(mapped.x, mapped.y)
482+ if (delegateChild.objectName === "decoratedWindow" || delegateChild.objectName === "tileInfo") {
483+ appRepeater.highlightedIndex = appRepeater.indexOf(itemUnder)
484+ }
485+ }
486+
487+ if (spreadFlickable.contentWidth > spreadFlickable.minContentWidth) {
488+ var margins = spreadFlickable.width * 0.05;
489+
490+ if (!progressiveScrollingEnabled && mouseX < spreadFlickable.width - scrollAreaWidth) {
491+ progressiveScrollingEnabled = true
492+ }
493+
494+ // do we need to scroll?
495+ if (mouseX < scrollAreaWidth) {
496+ var progress = Math.min(1, (scrollAreaWidth + margins - mouseX) / (scrollAreaWidth - margins));
497+ var contentX = (1 - progress) * (spreadFlickable.contentWidth - spreadFlickable.width)
498+ spreadFlickable.contentX = Math.max(0, Math.min(spreadFlickable.contentX, contentX))
499+ }
500+ if (mouseX > spreadFlickable.width - scrollAreaWidth && progressiveScrollingEnabled) {
501+ var progress = Math.min(1, (mouseX - (spreadFlickable.width - scrollAreaWidth)) / (scrollAreaWidth - margins))
502+ var contentX = progress * (spreadFlickable.contentWidth - spreadFlickable.width)
503+ spreadFlickable.contentX = Math.min(spreadFlickable.contentWidth - spreadFlickable.width, Math.max(spreadFlickable.contentX, contentX))
504+ }
505+ }
506+ }
507+ onPressed: mouse.accepted = false
508+ }
509+
465510 FloatingFlickable {
466511 id: spreadFlickable
512+ objectName: "spreadFlickable"
467513 anchors.fill: parent
514+ property int minContentWidth: 6 * Math.min(height / 4, width / 5)
468515 contentWidth: Math.max(6, ApplicationManager.count) * Math.min(height / 4, width / 5)
469516 enabled: false
470517
@@ -579,10 +626,10 @@ Rectangle {
579626 State {
580627 name: "altTab"; when: root.altTabPressed
581628 PropertyChanges { target: workspaceSelector; visible: true }
582− PropertyChanges { target: spreadFlickable; enabled: true }
629+ PropertyChanges { target: spreadFlickable; enabled: spreadFlickable.contentWidth > spreadFlickable.minContentWidth }
583630 PropertyChanges { target: currentSelectedLabel; visible: true }
584631 PropertyChanges { target: spreadBackground; visible: true }
585− PropertyChanges { target: appContainer; focus: true }
632+ PropertyChanges { target: hoverMouseArea; enabled: true }
586633 }
587634 ]
588635 signal updateWorkspaces();
@@ -592,6 +639,7 @@ Rectangle {
592639 from: "*"
593640 to: "altTab"
594641 SequentialAnimation {
642+ PropertyAction { target: hoverMouseArea; property: "progressiveScrollingEnabled"; value: false }
595643 PropertyAction { target: appRepeater; property: "highlightedIndex"; value: Math.min(ApplicationManager.count - 1, 1) }
596644 PauseAnimation { duration: 50 }
597645 PropertyAction { target: workspaceSelector; property: "visible" }
@@ -616,19 +664,20 @@ Rectangle {
616664 ]
617665
618666 MouseArea {
619− anchors {
620− top: parent.top
621− right: parent.right
622− bottom: parent.bottom
623− }
624− // TODO: Make this a push to edge thing like the launcher when we can,
625− // for now, yes, we want 1 pixel, regardless of the scaling
626− width: 1
627− hoverEnabled: true
628− onContainsMouseChanged: {
629− if (containsMouse) {
630− root.state = "altTab"
631− }
632− }
667+ id: rightEdgePushArea
668+ anchors {
669+ top: parent.top
670+ right: parent.right
671+ bottom: parent.bottom
672+ }
673+ // TODO: Make this a push to edge thing like the launcher when we can,
674+ // for now, yes, we want 1 pixel, regardless of the scaling
675+ width: 1
676+ hoverEnabled: true
677+ onContainsMouseChanged: {
678+ if (containsMouse) {
679+ root.state = "altTab";
680+ }
681+ }
633682 }
634683}