@@ -20,6 +20,7 @@ import QtQuick 2.3
2020import QtQuick.Layouts 1.1
2121import Ubuntu.Components 1.1
2222import Unity.Application 0.1
23+import "../Components"
2324import "../Components/PanelState"
2425import Utils 0.1
2526import Ubuntu.Gestures 0.1
@@ -56,13 +57,6 @@ Rectangle {
5657 property alias background: wallpaper.source
5758 property bool altTabPressed: false
5859
59− CrossFadeImage {
60− id: wallpaper
61− anchors.fill: parent
62− sourceSize { height: root.height; width: root.width }
63− fillMode: Image.PreserveAspectCrop
64− }
65−
6660 Connections {
6761 target: ApplicationManager
6862 onApplicationAdded: {
@@ -141,8 +135,8 @@ Rectangle {
141135 event.accepted = true;
142136 break;
143137 case Qt.Key_Escape:
144− appRepeater.highlightedIndex = -1
145− event.accepted = true;
138+ spreadRepeater.highlightedIndex = -1
139+ // Falling through intentionally
146140 case Qt.Key_Enter:
147141 case Qt.Key_Return:
148142 case Qt.Key_Space:
@@ -152,54 +146,50 @@ Rectangle {
152146 }
153147
154148 function selectNext(isAutoRepeat) {
155− if (isAutoRepeat && appRepeater.highlightedIndex >= ApplicationManager.count -1) {
149+ print("should select next", isAutoRepeat)
150+ if (isAutoRepeat && spreadRepeater.highlightedIndex >= ApplicationManager.count -1) {
156151 return; // AutoRepeat is not allowed to wrap around
157152 }
158153
159− appRepeater.highlightedIndex = (appRepeater.highlightedIndex + 1) % ApplicationManager.count;
160− var newContentX = ((spreadFlickable.contentWidth) / (ApplicationManager.count + 1)) * Math.max(0, Math.min(ApplicationManager.count - 5, appRepeater.highlightedIndex - 3));
161− if (spreadFlickable.contentX < newContentX || appRepeater.highlightedIndex == 0) {
154+ spreadRepeater.highlightedIndex = (spreadRepeater.highlightedIndex + 1) % ApplicationManager.count;
155+ var newContentX = ((spreadFlickable.contentWidth) / (ApplicationManager.count + 1)) * Math.max(0, Math.min(ApplicationManager.count - 5, spreadRepeater.highlightedIndex - 3));
156+ if (spreadFlickable.contentX < newContentX || spreadRepeater.highlightedIndex == 0) {
162157 spreadFlickable.snapTo(newContentX)
163158 }
164159 }
165160
166161 function selectPrevious(isAutoRepeat) {
167− if (isAutoRepeat && appRepeater.highlightedIndex == 0) {
162+ if (isAutoRepeat && spreadRepeater.highlightedIndex == 0) {
168163 return; // AutoRepeat is not allowed to wrap around
169164 }
170165
171− var newIndex = appRepeater.highlightedIndex - 1 >= 0 ? appRepeater.highlightedIndex - 1 : ApplicationManager.count - 1;
172− appRepeater.highlightedIndex = newIndex;
173− var newContentX = ((spreadFlickable.contentWidth) / (ApplicationManager.count + 1)) * Math.max(0, Math.min(ApplicationManager.count - 5, appRepeater.highlightedIndex - 1));
166+ var newIndex = spreadRepeater.highlightedIndex - 1 >= 0 ? spreadRepeater.highlightedIndex - 1 : ApplicationManager.count - 1;
167+ spreadRepeater.highlightedIndex = newIndex;
168+ var newContentX = ((spreadFlickable.contentWidth) / (ApplicationManager.count + 1)) * Math.max(0, Math.min(ApplicationManager.count - 5, spreadRepeater.highlightedIndex - 1));
174169 if (spreadFlickable.contentX > newContentX || newIndex == ApplicationManager.count -1) {
175170 spreadFlickable.snapTo(newContentX)
176171 }
177172 }
178173
179174 function focusSelected() {
180− if (appRepeater.highlightedIndex != -1) {
181− var application = ApplicationManager.get(appRepeater.highlightedIndex);
175+ if (spreadRepeater.highlightedIndex != -1) {
176+ var application = ApplicationManager.get(spreadRepeater.highlightedIndex);
182177 ApplicationManager.focusApplication(application.appId);
183178 }
184179 }
185180
181+ CrossFadeImage {
182+ id: wallpaper
183+ anchors.fill: parent
184+ sourceSize { height: root.height; width: root.width }
185+ fillMode: Image.PreserveAspectCrop
186+ }
187+
186188 Repeater {
187189 id: appRepeater
188190 model: ApplicationManager
189191 objectName: "appRepeater"
190192
191− property int highlightedIndex: -1
192− property int closingIndex: -1
193−
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−
203193 delegate: FocusScope {
204194 id: appDelegate
205195 z: ApplicationManager.count - index
@@ -207,12 +197,6 @@ Rectangle {
207197 width: units.gu(60)
208198 height: units.gu(50)
209199
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−
216200 readonly property int minWidth: units.gu(10)
217201 readonly property int minHeight: units.gu(10)
218202
@@ -245,63 +229,17 @@ Rectangle {
245229 maximized = false;
246230 }
247231
248− Behavior on x {
249− id: closeBehavior
250− enabled: appRepeater.closingIndex >= 0
251− UbuntuNumberAnimation {
252− onRunningChanged: if (!running) appRepeater.closingIndex = -1
253− }
254− }
255−
256232 states: [
257233 State {
258− name: "normal"; when: !appDelegate.maximized && !appDelegate.minimized && root.state !== "altTab"
234+ name: "normal"; when: !appDelegate.maximized && !appDelegate.minimized
259235 },
260236 State {
261− name: "maximized"; when: appDelegate.maximized && (root.state !== "altTab" || (root.state == "altTab" && !root.workspacesUpdated))
237+ name: "maximized"; when: appDelegate.maximized
262238 PropertyChanges { target: appDelegate; x: 0; y: 0; width: root.width; height: root.height }
263239 },
264240 State {
265− name: "minimized"; when: appDelegate.minimized && (root.state !== "altTab" || (root.state == "altTab" && !root.workspacesUpdated))
241+ name: "minimized"; when: appDelegate.minimized
266242 PropertyChanges { target: appDelegate; x: -appDelegate.width / 2; scale: units.gu(5) / appDelegate.width; opacity: 0 }
267− },
268− State {
269− name: "altTab"; when: root.state == "altTab" && root.workspacesUpdated
270− PropertyChanges {
271− target: appDelegate
272− x: spreadMaths.animatedX
273− y: spreadMaths.animatedY + (appDelegate.height - decoratedWindow.height) - units.gu(2)
274− width: spreadMaths.spreadHeight
275− height: spreadMaths.sceneHeight
276− angle: spreadMaths.animatedAngle
277− itemScale: spreadMaths.scale
278− itemScaleOriginY: decoratedWindow.height / 2;
279− z: index
280− visible: spreadMaths.itemVisible
281− }
282− PropertyChanges {
283− target: decoratedWindow
284− decorationShown: false
285− highlightShown: index == appRepeater.highlightedIndex
286− state: "transformed"
287− width: spreadMaths.spreadHeight
288− height: spreadMaths.spreadHeight
289− shadowOpacity: spreadMaths.shadowOpacity
290− anchors.topMargin: units.gu(2)
291− }
292− PropertyChanges {
293− target: tileInfo
294− visible: true
295− opacity: spreadMaths.tileInfoOpacity
296− }
297− PropertyChanges {
298− target: spreadSelectArea
299− enabled: true
300− }
301− PropertyChanges {
302− target: windowMoveResizeArea
303− enabled: false
304− }
305243 }
306244 ]
307245 transitions: [
@@ -309,32 +247,15 @@ Rectangle {
309247 from: "maximized,minimized,normal,"
310248 to: "maximized,minimized,normal,"
311249 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− }
324250 }
325251 ]
326− property real angle: 0
327− property real itemScale: 1
328− property int itemScaleOriginX: 0
329− property int itemScaleOriginY: 0
330252
331− SpreadMaths {
332− id: spreadMaths
333− flickable: spreadFlickable
334− itemIndex: index
335− totalItems: Math.max(6, ApplicationManager.count)
336− sceneHeight: root.height
337− itemHeight: appDelegate.height
253+ Binding {
254+ id: previewBinding
255+ target: appDelegate
256+ property: "z"
257+ value: ApplicationManager.count + 1
258+ when: index == spreadRepeater.highlightedIndex && blurLayer.ready
338259 }
339260
340261 WindowMoveResizeArea {
@@ -353,8 +274,8 @@ Rectangle {
353274 objectName: "decoratedWindow"
354275 anchors.left: appDelegate.left
355276 anchors.top: appDelegate.top
356− windowWidth: appDelegate.windowWidth
357− windowHeight: appDelegate.windowHeight
277+ width: appDelegate.width
278+ height: appDelegate.height
358279 application: ApplicationManager.get(index)
359280 active: ApplicationManager.focusedApplicationId === model.appId
360281 focus: true
@@ -362,6 +283,66 @@ Rectangle {
362283 onClose: ApplicationManager.stopApplication(model.appId)
363284 onMaximize: appDelegate.maximize()
364285 onMinimize: appDelegate.minimize()
286+ }
287+ }
288+ }
289+ }
290+
291+ BlurLayer {
292+ id: blurLayer
293+ anchors.fill: parent
294+ source: appContainer
295+ visible: false
296+ }
297+
298+ Item {
299+ id: spreadContainer
300+ visible: false
301+ onVisibleChanged: print("container visible changed", visible)
302+
303+ Repeater {
304+ id: spreadRepeater
305+ model: ApplicationManager
306+
307+ property int highlightedIndex: -1
308+ property int closingIndex: -1
309+
310+ function indexOf(delegateItem) {
311+ for (var i = 0; i < spreadRepeater.count; i++) {
312+ if (spreadRepeater.itemAt(i) === delegateItem) {
313+ return i;
314+ }
315+ }
316+ return -1;
317+ }
318+
319+ delegate: Item {
320+ id: spreadDelegate
321+ objectName: "spreadDelegate"
322+ width: units.gu(20)
323+ height: units.gu(20)
324+
325+ property real angle: 0
326+ property real itemScale: 1
327+ property int itemScaleOriginX: 0
328+ property int itemScaleOriginY: 0
329+
330+ Behavior on x {
331+ id: closeBehavior
332+ enabled: spreadRepeater.closingIndex >= 0
333+ UbuntuNumberAnimation {
334+ onRunningChanged: if (!running) spreadRepeater.closingIndex = -1
335+ }
336+ }
337+
338+ DesktopSpreadDelegate {
339+ id: clippedSpreadDelegate
340+ objectName: "clippedSpreadDelegate"
341+ anchors.left: parent.left
342+ anchors.top: parent.top
343+ windowWidth: parent.width
344+ windowHeight: parent.height
345+ application: ApplicationManager.get(index)
365346
366347 transform: [
367348 Scale {
@@ -371,9 +352,9 @@ Rectangle {
371352 yScale: itemScale
372353 },
373354 Rotation {
374− origin { x: 0; y: (decoratedWindow.height - (decoratedWindow.height * itemScale / 2)) }
355+ origin { x: 0; y: (clippedSpreadDelegate.height - (clippedSpreadDelegate.height * itemScale / 2)) }
375356 axis { x: 0; y: 1; z: 0 }
376− angle: appDelegate.angle
357+ angle: spreadDelegate.angle
377358 }
378359 ]
379360
@@ -388,37 +369,69 @@ Rectangle {
388369 }
389370 }
390371
391− Image {
392− id: closeImage
393− anchors { left: parent.left; top: parent.top; leftMargin: -height / 2; topMargin: -height / 2 + spreadMaths.closeIconOffset + units.gu(2) }
394− source: "graphics/window-close.svg"
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)
401− height: units.gu(1.5)
402− width: height
403− sourceSize.width: width
404− sourceSize.height: height
372+ SpreadMaths {
373+ id: spreadMaths
374+ flickable: spreadFlickable
375+ itemIndex: index
376+ totalItems: Math.max(6, ApplicationManager.count)
377+ sceneHeight: root.height
378+ itemHeight: spreadDelegate.height
379+ }
405380
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)
381+ states: [
382+ State {
383+ name: "altTab"; when: root.state == "altTab" && spreadContainer.visible
384+ PropertyChanges {
385+ target: spreadDelegate
386+ x: spreadMaths.animatedX
387+ y: spreadMaths.animatedY + (spreadDelegate.height - clippedSpreadDelegate.height) - units.gu(2)
388+ width: spreadMaths.spreadHeight
389+ height: spreadMaths.sceneHeight
390+ angle: spreadMaths.animatedAngle
391+ itemScale: spreadMaths.scale
392+ itemScaleOriginY: clippedSpreadDelegate.height / 2;
393+ z: index
394+ visible: spreadMaths.itemVisible
395+ }
396+ PropertyChanges {
397+ target: clippedSpreadDelegate
398+ highlightShown: index == spreadRepeater.highlightedIndex
399+ state: "transformed"
400+ width: spreadMaths.spreadHeight
401+ height: spreadMaths.spreadHeight
402+ shadowOpacity: spreadMaths.shadowOpacity
403+ anchors.topMargin: units.gu(2)
404+ }
405+ PropertyChanges {
406+ target: tileInfo
407+ visible: true
408+ opacity: spreadMaths.tileInfoOpacity
409+ }
410+ PropertyChanges {
411+ target: spreadSelectArea
412+ enabled: true
414413 }
415414 }
416− }
415+ ]
416+ transitions: [
417+ Transition {
418+ from: ""
419+ to: "altTab"
420+ PropertyAction { target: spreadDelegate; properties: "y,height,width,angle,z,itemScale,itemScaleOriginY,visible" }
421+ PropertyAction { target: clippedSpreadDelegate; properties: "anchors.topMargin" }
422+ PropertyAnimation {
423+ target: spreadDelegate; properties: "x"
424+ from: root.width
425+ duration: rightEdgePushArea.containsMouse ? UbuntuAnimation.FastDuration :0
426+ easing: UbuntuAnimation.StandardEasing
427+ }
428+ }
429+ ]
417430
418431 MouseArea {
419432 id: tileInfo
420433 objectName: "tileInfo"
421− anchors { left: parent.left; top: decoratedWindow.bottom; topMargin: units.gu(5) }
434+ anchors { left: parent.left; top: clippedSpreadDelegate.bottom; topMargin: units.gu(5) }
422435 width: units.gu(30)
423436 height: titleInfoColumn.height
424437 visible: false
@@ -426,7 +439,7 @@ Rectangle {
426439
427440 onContainsMouseChanged: {
428441 if (containsMouse) {
429− appRepeater.highlightedIndex = index
442+ spreadRepeater.highlightedIndex = index
430443 }
431444 }
432445
@@ -456,10 +469,38 @@ Rectangle {
456469 }
457470 }
458471 }
472+
473+ Image {
474+ id: closeImage
475+ anchors { left: parent.left; top: parent.top; leftMargin: -height / 2; topMargin: -height / 2 + spreadMaths.closeIconOffset + units.gu(2) }
476+ source: "graphics/window-close.svg"
477+ readonly property var mousePos: hoverMouseArea.mapToItem(spreadDelegate, hoverMouseArea.mouseX, hoverMouseArea.mouseY)
478+ visible: index == spreadRepeater.highlightedIndex
479+ && mousePos.y < (clippedSpreadDelegate.height / 3)
480+ && mousePos.y > -units.gu(4)
481+ && mousePos.x > -units.gu(4)
482+ && mousePos.x < (clippedSpreadDelegate.width * 2 / 3)
483+ height: units.gu(1.5)
484+ width: height
485+ sourceSize.width: width
486+ sourceSize.height: height
487+
488+ MouseArea {
489+ id: closeMouseArea
490+ objectName: "closeMouseArea"
491+ anchors.fill: closeImage
492+ anchors.margins: -units.gu(2)
493+ onClicked: {
494+ spreadRepeater.closingIndex = index;
495+ ApplicationManager.stopApplication(model.appId)
496+ }
497+ }
498+ }
459499 }
460500 }
461501 }
462502
503+
463504 MouseArea {
464505 id: hoverMouseArea
465506 anchors.fill: appContainer
@@ -474,13 +515,13 @@ Rectangle {
474515 mouse.accepted = false
475516
476517 // 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)
518+ var mapped = mapToItem(spreadContainer, hoverMouseArea.mouseX, hoverMouseArea.mouseY)
519+ var itemUnder = spreadContainer.childAt(mapped.x, mapped.y)
479520 if (itemUnder) {
480521 mapped = mapToItem(itemUnder, hoverMouseArea.mouseX, hoverMouseArea.mouseY)
481522 var delegateChild = itemUnder.childAt(mapped.x, mapped.y)
482− if (delegateChild.objectName === "decoratedWindow" || delegateChild.objectName === "tileInfo") {
483− appRepeater.highlightedIndex = appRepeater.indexOf(itemUnder)
523+ if (delegateChild && (delegateChild.objectName === "clippedSpreadDelegate" || delegateChild.objectName === "tileInfo")) {
524+ spreadRepeater.highlightedIndex = spreadRepeater.indexOf(itemUnder)
484525 }
485526 }
486527
@@ -534,7 +575,7 @@ Rectangle {
534575 left: parent.left
535576 top: parent.top
536577 right: parent.right
537− topMargin: units.gu(3.5) // TODO: should be root.panelHeight
578+ topMargin: units.gu(3.5)
538579 }
539580 height: root.height * 0.25
540581 visible: false
@@ -557,15 +598,12 @@ Rectangle {
557598 }
558599 height: parent.height * 0.75
559600
560− // FIXME: This is temporary until we can have multiple Items per surface
561601 ShaderEffect {
562602 anchors.fill: parent
563603
564604 property var source: ShaderEffectSource {
565605 id: shaderEffectSource
566− live: false
567606 sourceItem: appContainer
568− Connections { target: root; onUpdateWorkspaces: shaderEffectSource.scheduleUpdate() }
569607 }
570608
571609 fragmentShader: "
@@ -617,7 +655,7 @@ Rectangle {
617655 Label {
618656 id: currentSelectedLabel
619657 anchors { bottom: parent.bottom; bottomMargin: root.height * 0.625; horizontalCenter: parent.horizontalCenter }
620− text: appRepeater.highlightedIndex >= 0 ? ApplicationManager.get(appRepeater.highlightedIndex).name : ""
658+ text: spreadRepeater.highlightedIndex >= 0 ? ApplicationManager.get(spreadRepeater.highlightedIndex).name : ""
621659 visible: false
622660 fontSize: "large"
623661 }
@@ -625,40 +663,39 @@ Rectangle {
625663 states: [
626664 State {
627665 name: "altTab"; when: root.altTabPressed
666+ PropertyChanges { target: blurLayer; saturation: 0.8; blurRadius: 60; visible: true }
628667 PropertyChanges { target: workspaceSelector; visible: true }
668+ PropertyChanges { target: spreadContainer; visible: true }
629669 PropertyChanges { target: spreadFlickable; enabled: spreadFlickable.contentWidth > spreadFlickable.minContentWidth }
630670 PropertyChanges { target: currentSelectedLabel; visible: true }
631671 PropertyChanges { target: spreadBackground; visible: true }
632672 PropertyChanges { target: hoverMouseArea; enabled: true }
633673 }
634674 ]
635− signal updateWorkspaces();
636− property bool workspacesUpdated: false
637675 transitions: [
638676 Transition {
639677 from: "*"
640678 to: "altTab"
641679 SequentialAnimation {
642680 PropertyAction { target: hoverMouseArea; property: "progressiveScrollingEnabled"; value: false }
643− PropertyAction { target: appRepeater; property: "highlightedIndex"; value: Math.min(ApplicationManager.count - 1, 1) }
681+ PropertyAction { target: spreadRepeater; property: "highlightedIndex"; value: Math.min(ApplicationManager.count - 1, 1) }
644682 PauseAnimation { duration: 50 }
645683 PropertyAction { target: workspaceSelector; property: "visible" }
646− ScriptAction { script: root.updateWorkspaces() }
647− // FIXME: Updating of shaderEffectSource take a bit of time. This is temporary until we can paint multiple items per surface
648− PauseAnimation { duration: 10 }
649− PropertyAction { target: root; property: "workspacesUpdated"; value: true }
650− PropertyAction { target: spreadFlickable; property: "visible" }
651− PropertyAction { targets: [currentSelectedLabel,spreadBackground]; property: "visible" }
652− PropertyAction { target: spreadFlickable; property: "contentX"; value: 0 }
684+ PropertyAction { target: spreadContainer; property: "visible" }
685+ ParallelAnimation {
686+ UbuntuNumberAnimation { target: blurLayer; properties: "saturation,blurRadius" }
687+ PropertyAction { target: spreadFlickable; property: "visible" }
688+ PropertyAction { targets: [currentSelectedLabel,spreadBackground]; property: "visible" }
689+ PropertyAction { target: spreadFlickable; property: "contentX"; value: 0 }
690+ }
653691 }
654692 },
655693 Transition {
656694 from: "*"
657695 to: "*"
658696 PropertyAnimation { property: "opacity" }
659− PropertyAction { target: root; property: "workspacesUpdated"; value: false }
660697 ScriptAction { script: { appContainer.focusSelected() } }
661− PropertyAction { target: appRepeater; property: "highlightedIndex"; value: -1 }
698+ PropertyAction { target: spreadRepeater; property: "highlightedIndex"; value: -1 }
662699 }
663700
664701 ]