@@ -20,6 +20,7 @@ import QtQuick 2.4
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
@@ -41,6 +42,8 @@ Rectangle {
4142 property bool beingResized: false
4243 property bool keepDashRunning: true
4344 property bool suspended: false
45+ property alias background: wallpaper.source
46+ property alias altTabPressed: spread.altTabPressed
4447
4548 // functions to be called from outside
4649 function updateFocusedAppOrientation() { /* TODO */ }
@@ -53,21 +56,11 @@ Rectangle {
5356 property int mainAppWindowOrientationAngle: 0
5457 readonly property bool orientationChangesEnabled: false
5558
56− property alias background: wallpaper.source
57− property bool altTabPressed: false
58−
59− CrossFadeImage {
60− id: wallpaper
61− anchors.fill: parent
62− sourceSize { height: root.height; width: root.width }
63− fillMode: Image.PreserveAspectCrop
64− }
65−
6659 Connections {
6760 target: ApplicationManager
6861 onApplicationAdded: {
69− if (root.state == "altTab") {
70− root.state = "";
62+ if (spread.state == "altTab") {
63+ spread.state = "";
7164 }
7265
7366 ApplicationManager.requestFocusApplication(appId)
@@ -77,7 +70,11 @@ Rectangle {
7770 var appIndex = priv.indexOf(appId);
7871 var appDelegate = appRepeater.itemAt(appIndex);
7972 appDelegate.minimized = false;
80− appDelegate.focus = true;
73+ ApplicationManager.focusApplication(appId)
74+
75+ if (spread.state == "altTab") {
76+ spread.cancel()
77+ }
8178 }
8279 }
8380
@@ -115,72 +112,17 @@ Rectangle {
115112 value: priv.focusedAppDelegate !== null && priv.focusedAppDelegate.state === "maximized"
116113 }
117114
118− Rectangle {
119− id: spreadBackground
120− anchors.fill: parent
121− color: "#55000000"
122− visible: false
123− }
124−
125115 FocusScope {
126116 id: appContainer
127117 objectName: "appContainer"
128118 anchors.fill: parent
129− focus: true
130−
131− Keys.onPressed: {
132− switch (event.key) {
133− case Qt.Key_Left:
134− case Qt.Key_Backtab:
135− selectPrevious(event.isAutoRepeat)
136− event.accepted = true;
137− break;
138− case Qt.Key_Right:
139− case Qt.Key_Tab:
140− selectNext(event.isAutoRepeat)
141− event.accepted = true;
142− break;
143− case Qt.Key_Escape:
144− appRepeater.highlightedIndex = -1
145− event.accepted = true;
146− case Qt.Key_Enter:
147− case Qt.Key_Return:
148− case Qt.Key_Space:
149− root.state = ""
150− event.accepted = true;
151− }
152− }
153−
154− function selectNext(isAutoRepeat) {
155− if (isAutoRepeat && appRepeater.highlightedIndex >= ApplicationManager.count -1) {
156− return; // AutoRepeat is not allowed to wrap around
157− }
158−
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) {
162− spreadFlickable.snapTo(newContentX)
163− }
164− }
119+ focus: spread.state !== "altTab"
165120
166− function selectPrevious(isAutoRepeat) {
167− if (isAutoRepeat && appRepeater.highlightedIndex == 0) {
168− return; // AutoRepeat is not allowed to wrap around
169− }
170−
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));
174− if (spreadFlickable.contentX > newContentX || newIndex == ApplicationManager.count -1) {
175− spreadFlickable.snapTo(newContentX)
176− }
177− }
178−
179− function focusSelected() {
180− if (appRepeater.highlightedIndex != -1) {
181− var application = ApplicationManager.get(appRepeater.highlightedIndex);
182− ApplicationManager.requestFocusApplication(application.appId)
183− }
121+ CrossFadeImage {
122+ id: wallpaper
123+ anchors.fill: parent
124+ sourceSize { height: root.height; width: root.width }
125+ fillMode: Image.PreserveAspectCrop
184126 }
185127
186128 Repeater {
@@ -188,30 +130,13 @@ Rectangle {
188130 model: ApplicationManager
189131 objectName: "appRepeater"
190132
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−
203133 delegate: FocusScope {
204134 id: appDelegate
205135 z: ApplicationManager.count - index
206136 y: units.gu(3)
207137 width: units.gu(60)
208138 height: units.gu(50)
209−
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
139+ focus: model.appId === priv.focusedAppId
215140
216141 readonly property int minWidth: units.gu(10)
217142 readonly property int minHeight: units.gu(10)
@@ -225,13 +150,6 @@ Rectangle {
225150 }
226151 }
227152
228− Component.onCompleted: {
229− // Focus the top-most or AppMan-focused application on start up.
230− if (ApplicationManager.focusedApplicationId === model.appId && !focus) {
231− focus = true;
232− }
233− }
234−
235153 Binding {
236154 target: ApplicationManager.get(index)
237155 property: "requestedState"
@@ -256,63 +174,17 @@ Rectangle {
256174 maximized = false;
257175 }
258176
259− Behavior on x {
260− id: closeBehavior
261− enabled: appRepeater.closingIndex >= 0
262− UbuntuNumberAnimation {
263− onRunningChanged: if (!running) appRepeater.closingIndex = -1
264− }
265− }
266−
267177 states: [
268178 State {
269− name: "normal"; when: !appDelegate.maximized && !appDelegate.minimized && root.state !== "altTab"
179+ name: "normal"; when: !appDelegate.maximized && !appDelegate.minimized
270180 },
271181 State {
272− name: "maximized"; when: appDelegate.maximized && (root.state !== "altTab" || (root.state == "altTab" && !root.workspacesUpdated))
182+ name: "maximized"; when: appDelegate.maximized
273183 PropertyChanges { target: appDelegate; x: 0; y: 0; width: root.width; height: root.height }
274184 },
275185 State {
276− name: "minimized"; when: appDelegate.minimized && (root.state !== "altTab" || (root.state == "altTab" && !root.workspacesUpdated))
186+ name: "minimized"; when: appDelegate.minimized
277187 PropertyChanges { target: appDelegate; x: -appDelegate.width / 2; scale: units.gu(5) / appDelegate.width; opacity: 0 }
278− },
279− State {
280− name: "altTab"; when: root.state == "altTab" && root.workspacesUpdated
281− PropertyChanges {
282− target: appDelegate
283− x: spreadMaths.animatedX
284− y: spreadMaths.animatedY + (appDelegate.height - decoratedWindow.height) - units.gu(2)
285− width: spreadMaths.spreadHeight
286− height: spreadMaths.sceneHeight
287− angle: spreadMaths.animatedAngle
288− itemScale: spreadMaths.scale
289− itemScaleOriginY: decoratedWindow.height / 2;
290− z: index
291− visible: spreadMaths.itemVisible
292− }
293− PropertyChanges {
294− target: decoratedWindow
295− decorationShown: false
296− highlightShown: index == appRepeater.highlightedIndex
297− state: "transformed"
298− width: spreadMaths.spreadHeight
299− height: spreadMaths.spreadHeight
300− shadowOpacity: spreadMaths.shadowOpacity
301− anchors.topMargin: units.gu(2)
302− }
303− PropertyChanges {
304− target: tileInfo
305− visible: true
306− opacity: spreadMaths.tileInfoOpacity
307− }
308− PropertyChanges {
309− target: spreadSelectArea
310− enabled: true
311− }
312− PropertyChanges {
313− target: windowMoveResizeArea
314− enabled: false
315− }
316188 }
317189 ]
318190 transitions: [
@@ -334,18 +206,13 @@ Rectangle {
334206 }
335207 }
336208 ]
337− property real angle: 0
338− property real itemScale: 1
339− property int itemScaleOriginX: 0
340− property int itemScaleOriginY: 0
341−
342− SpreadMaths {
343− id: spreadMaths
344− flickable: spreadFlickable
345− itemIndex: index
346− totalItems: Math.max(6, ApplicationManager.count)
347− sceneHeight: root.height
348− itemHeight: appDelegate.height
209+
210+ Binding {
211+ id: previewBinding
212+ target: appDelegate
213+ property: "z"
214+ value: ApplicationManager.count + 1
215+ when: index == spread.highlightedIndex && blurLayer.ready
349216 }
350217
351218 WindowMoveResizeArea {
@@ -356,7 +223,7 @@ Rectangle {
356223 resizeHandleWidth: units.gu(2)
357224 windowId: model.appId // FIXME: Change this to point to windowId once we have such a thing
358225
359− onPressed: { appDelegate.focus = true; }
226+ onPressed: { ApplicationManager.focusApplication(model.appId) }
360227 }
361228
362229 DecoratedWindow {
@@ -364,8 +231,8 @@ Rectangle {
364231 objectName: "decoratedWindow"
365232 anchors.left: appDelegate.left
366233 anchors.top: appDelegate.top
367− windowWidth: appDelegate.windowWidth
368− windowHeight: appDelegate.windowHeight
234+ width: appDelegate.width
235+ height: appDelegate.height
369236 application: ApplicationManager.get(index)
370237 active: ApplicationManager.focusedApplicationId === model.appId
371238 focus: true
@@ -373,324 +240,37 @@ Rectangle {
373240 onClose: ApplicationManager.stopApplication(model.appId)
374241 onMaximize: appDelegate.maximize()
375242 onMinimize: appDelegate.minimize()
376−
377− transform: [
378− Scale {
379− origin.x: itemScaleOriginX
380− origin.y: itemScaleOriginY
381− xScale: itemScale
382− yScale: itemScale
383− },
384− Rotation {
385− origin { x: 0; y: (decoratedWindow.height - (decoratedWindow.height * itemScale / 2)) }
386− axis { x: 0; y: 1; z: 0 }
387− angle: appDelegate.angle
388− }
389− ]
390−
391− MouseArea {
392− id: spreadSelectArea
393− anchors.fill: parent
394− anchors.margins: -units.gu(2)
395− enabled: false
396− onClicked: {
397− appRepeater.highlightedIndex = index;
398− root.state = ""
399− }
400− }
401− }
402−
403− Image {
404− id: closeImage
405− anchors { left: parent.left; top: parent.top; leftMargin: -height / 2; topMargin: -height / 2 + spreadMaths.closeIconOffset + units.gu(2) }
406− source: "graphics/window-close.svg"
407− readonly property var mousePos: hoverMouseArea.mapToItem(appDelegate, hoverMouseArea.mouseX, hoverMouseArea.mouseY)
408− visible: index == appRepeater.highlightedIndex
409− && mousePos.y < (decoratedWindow.height / 3)
410− && mousePos.y > -units.gu(4)
411− && mousePos.x > -units.gu(4)
412− && mousePos.x < (decoratedWindow.width * 2 / 3)
413− height: units.gu(1.5)
414− width: height
415− sourceSize.width: width
416− sourceSize.height: height
417−
418− MouseArea {
419− id: closeMouseArea
420− objectName: "closeMouseArea"
421− anchors.fill: closeImage
422− anchors.margins: -units.gu(2)
423− onClicked: {
424− appRepeater.closingIndex = index;
425− ApplicationManager.stopApplication(model.appId)
426− }
427− }
428− }
429−
430− MouseArea {
431− id: tileInfo
432− objectName: "tileInfo"
433− anchors { left: parent.left; top: decoratedWindow.bottom; topMargin: units.gu(5) }
434− width: units.gu(30)
435− height: titleInfoColumn.height
436− visible: false
437− hoverEnabled: true
438−
439− onContainsMouseChanged: {
440− if (containsMouse) {
441− appRepeater.highlightedIndex = index
442− }
443− }
444−
445− onClicked: {
446− root.state = ""
447− }
448−
449− ColumnLayout {
450− id: titleInfoColumn
451− anchors { left: parent.left; top: parent.top; right: parent.right }
452− spacing: units.gu(1)
453−
454− UbuntuShape {
455− Layout.preferredHeight: Math.min(units.gu(6), root.height * .05)
456− Layout.preferredWidth: height * 8 / 7.6
457− image: Image {
458− anchors.fill: parent
459− source: model.icon
460− }
461− }
462− Label {
463− Layout.fillWidth: true
464− Layout.preferredHeight: units.gu(6)
465− text: model.name
466− wrapMode: Text.WordWrap
467− maximumLineCount: 2
468− }
469− }
470243 }
471244 }
472245 }
473246 }
474247
475− MouseArea {
476− id: hoverMouseArea
477− anchors.fill: appContainer
478− propagateComposedEvents: true
479− hoverEnabled: true
480− enabled: false
481−
482− property int scrollAreaWidth: root.width / 3
483− property bool progressiveScrollingEnabled: false
484−
485− onMouseXChanged: {
486− mouse.accepted = false
487− if (hoverMouseArea.pressed) return;
488−
489− // Find the hovered item and mark it active
490− var mapped = mapToItem(appContainer, hoverMouseArea.mouseX, hoverMouseArea.mouseY)
491− var itemUnder = appContainer.childAt(mapped.x, mapped.y)
492− if (itemUnder) {
493− mapped = mapToItem(itemUnder, hoverMouseArea.mouseX, hoverMouseArea.mouseY)
494− var delegateChild = itemUnder.childAt(mapped.x, mapped.y)
495− if (delegateChild.objectName === "decoratedWindow" || delegateChild.objectName === "tileInfo") {
496− appRepeater.highlightedIndex = appRepeater.indexOf(itemUnder)
497− }
498− }
499−
500− if (spreadFlickable.contentWidth > spreadFlickable.minContentWidth) {
501− var margins = spreadFlickable.width * 0.05;
502−
503− if (!progressiveScrollingEnabled && mouseX < spreadFlickable.width - scrollAreaWidth) {
504− progressiveScrollingEnabled = true
505− }
506−
507− // do we need to scroll?
508− if (mouseX < scrollAreaWidth) {
509− var progress = Math.min(1, (scrollAreaWidth + margins - mouseX) / (scrollAreaWidth - margins));
510− var contentX = (1 - progress) * (spreadFlickable.contentWidth - spreadFlickable.width)
511− spreadFlickable.contentX = Math.max(0, Math.min(spreadFlickable.contentX, contentX))
512− }
513− if (mouseX > spreadFlickable.width - scrollAreaWidth && progressiveScrollingEnabled) {
514− var progress = Math.min(1, (mouseX - (spreadFlickable.width - scrollAreaWidth)) / (scrollAreaWidth - margins))
515− var contentX = progress * (spreadFlickable.contentWidth - spreadFlickable.width)
516− spreadFlickable.contentX = Math.min(spreadFlickable.contentWidth - spreadFlickable.width, Math.max(spreadFlickable.contentX, contentX))
517− }
518− }
519− }
520− onPressed: mouse.accepted = false
521− }
522−
523− FloatingFlickable {
524− id: spreadFlickable
525− objectName: "spreadFlickable"
248+ BlurLayer {
249+ id: blurLayer
526250 anchors.fill: parent
527− property int minContentWidth: 6 * Math.min(height / 4, width / 5)
528− contentWidth: Math.max(6, ApplicationManager.count) * Math.min(height / 4, width / 5)
529− enabled: false
530−
531− function snapTo(contentX) {
532− snapAnimation.stop();
533− snapAnimation.to = contentX
534− snapAnimation.start();
535− }
536−
537− UbuntuNumberAnimation {
538− id: snapAnimation
539− target: spreadFlickable
540− property: "contentX"
541− }
542− }
543−
544− Item {
545− id: workspaceSelector
546− anchors {
547− left: parent.left
548− top: parent.top
549− right: parent.right
550− topMargin: units.gu(3.5) // TODO: should be root.panelHeight
551− }
552− height: root.height * 0.25
251+ source: appContainer
553252 visible: false
554−
555− RowLayout {
556− anchors.fill: parent
557− spacing: units.gu(1)
558− Item { Layout.fillWidth: true }
559− Repeater {
560− model: 1 // TODO: will be a workspacemodel in the future
561− Item {
562− Layout.fillHeight: true
563− Layout.preferredWidth: ((height - units.gu(6)) * root.width / root.height)
564− Image {
565− source: root.background
566− anchors {
567− left: parent.left
568− right: parent.right
569− verticalCenter: parent.verticalCenter
570− }
571− height: parent.height * 0.75
572−
573− // FIXME: This is temporary until we can have multiple Items per surface
574− ShaderEffect {
575− anchors.fill: parent
576−
577− property var source: ShaderEffectSource {
578− id: shaderEffectSource
579− live: false
580− sourceItem: appContainer
581− Connections { target: root; onUpdateWorkspaces: shaderEffectSource.scheduleUpdate() }
582− }
583−
584− fragmentShader: "
585− varying highp vec2 qt_TexCoord0;
586− uniform sampler2D source;
587− void main(void)
588− {
589− highp vec4 sourceColor = texture2D(source, qt_TexCoord0);
590− gl_FragColor = sourceColor;
591− }"
592− }
593− }
594−
595− // TODO: This is the bar for the currently selected workspace
596− // Enable this once the workspace stuff is implemented
597−// Rectangle {
598−// anchors { left: parent.left; right: parent.right; bottom: parent.bottom }
599−// height: units.dp(2)
600−// color: UbuntuColors.orange
601−// visible: index == 0 // TODO: should be active workspace index
602−// }
603− }
604−
605− }
606− // TODO: This is the "new workspace" button. Enable this once workspaces are implemented
607−// Item {
608−// Layout.fillHeight: true
609−// Layout.preferredWidth: ((height - units.gu(6)) * root.width / root.height)
610−// Rectangle {
611−// anchors {
612−// left: parent.left
613−// right: parent.right
614−// verticalCenter: parent.verticalCenter
615−// }
616−// height: parent.height * 0.75
617−// color: "#22ffffff"
618−
619−// Label {
620−// anchors.centerIn: parent
621−// font.pixelSize: parent.height / 2
622−// text: "+"
623−// }
624−// }
625−// }
626− Item { Layout.fillWidth: true }
627− }
628253 }
629254
630− Label {
631− id: currentSelectedLabel
632− anchors { bottom: parent.bottom; bottomMargin: root.height * 0.625; horizontalCenter: parent.horizontalCenter }
633− text: appRepeater.highlightedIndex >= 0 ? ApplicationManager.get(appRepeater.highlightedIndex).name : ""
255+ Rectangle {
256+ id: spreadBackground
257+ anchors.fill: parent
258+ color: "#55000000"
634259 visible: false
635− fontSize: "large"
636260 }
637261
638− states: [
639− State {
640− name: "altTab"; when: root.altTabPressed
641− PropertyChanges { target: workspaceSelector; visible: true }
642− PropertyChanges { target: spreadFlickable; enabled: spreadFlickable.contentWidth > spreadFlickable.minContentWidth }
643− PropertyChanges { target: currentSelectedLabel; visible: true }
644− PropertyChanges { target: spreadBackground; visible: true }
645− PropertyChanges { target: hoverMouseArea; enabled: true }
646− }
647− ]
648− signal updateWorkspaces();
649− property bool workspacesUpdated: false
650− transitions: [
651− Transition {
652− from: "*"
653− to: "altTab"
654− SequentialAnimation {
655− PropertyAction { target: hoverMouseArea; property: "progressiveScrollingEnabled"; value: false }
656− PropertyAction { target: appRepeater; property: "highlightedIndex"; value: Math.min(ApplicationManager.count - 1, 1) }
657− PauseAnimation { duration: 50 }
658− PropertyAction { target: workspaceSelector; property: "visible" }
659− ScriptAction { script: root.updateWorkspaces() }
660− // FIXME: Updating of shaderEffectSource take a bit of time. This is temporary until we can paint multiple items per surface
661− PauseAnimation { duration: 10 }
662− PropertyAction { target: root; property: "workspacesUpdated"; value: true }
663− PropertyAction { target: spreadFlickable; property: "visible" }
664− PropertyAction { targets: [currentSelectedLabel,spreadBackground]; property: "visible" }
665− PropertyAction { target: spreadFlickable; property: "contentX"; value: 0 }
666− }
667− },
668− Transition {
669− from: "*"
670− to: "*"
671− PropertyAnimation { property: "opacity" }
672− PropertyAction { target: root; property: "workspacesUpdated"; value: false }
673− ScriptAction { script: { appContainer.focusSelected() } }
674− PropertyAction { target: appRepeater; property: "highlightedIndex"; value: -1 }
675− }
676−
677− ]
678−
679262 MouseArea {
680− id: rightEdgePushArea
681− anchors {
682− top: parent.top
683− right: parent.right
684− bottom: parent.bottom
685− }
686− // TODO: Make this a push to edge thing like the launcher when we can,
687− // for now, yes, we want 1 pixel, regardless of the scaling
688− width: 1
689− hoverEnabled: true
690− onContainsMouseChanged: {
691− if (containsMouse) {
692− root.state = "altTab";
693− }
694− }
263+ id: eventEater
264+ anchors.fill: parent
265+ visible: spreadBackground.visible
266+ enabled: visible
267+ }
268+
269+ DesktopSpread {
270+ id: spread
271+ objectName: "spread"
272+ anchors.fill: parent
273+ workspace: appContainer
274+ focus: state == "altTab"
695275 }
696276}