on xenial help sign in
further development on the Desktop Spread
b61b5adMichael Zanetti
plugins/GlobalShortcut/globalshortcut.cpp+1−1
@@ -72,7 +72,7 @@ void GlobalShortcut::keyPressEvent(QKeyEvent * event)
72void GlobalShortcut::setupFilterOnWindow(QQuickWindow *window)
72void GlobalShortcut::setupFilterOnWindow(QQuickWindow *window)
73{
73{
74 if (!window) {
74 if (!window) {
75− qWarning() << Q_FUNC_INFO << "Failed to setup filter on window";
75+// qWarning() << Q_FUNC_INFO << "Failed to setup filter on window";
76 return;
76 return;
77 }
77 }
78
78
qml/Components/BlurLayer.qmlnew file+69
@@ -0,0 +1,69 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.4
18+import QtGraphicalEffects 1.0
19+
20+Item {
21+ id: root
22+
23+ property alias source: shaderEffectSource.sourceItem
24+ property alias saturation: desaturateEffect.saturationValue
25+ property alias blurRadius: fastBlur.radius
26+
27+ readonly property alias ready: shaderEffectSource.ready
28+
29+ ShaderEffect {
30+ id: desaturateEffect
31+ anchors.fill: root
32+
33+ property real saturationValue: 1
34+
35+ property variant source: ShaderEffectSource {
36+ id: shaderEffectSource
37+ hideSource: root.visible
38+ live: !root.visible
39+ property bool ready: false;
40+ onLiveChanged: {
41+ if (!live) {
42+ ready = false;
43+ scheduleUpdate();
44+ }
45+ }
46+ onScheduledUpdateCompleted: ready = true;
47+ }
48+
49+ fragmentShader: "
50+ varying highp vec2 qt_TexCoord0;
51+ uniform sampler2D source;
52+ uniform lowp float saturationValue;
53+ void main(void)
54+ {
55+ highp vec4 sourceColor = texture2D(source, qt_TexCoord0);
56+ highp vec4 scaledColor = sourceColor * vec4(0.3, 0.59, 0.11, 1.0);
57+ lowp float luminance = scaledColor.r + scaledColor.g + scaledColor.b ;
58+ gl_FragColor = mix(vec4(luminance, luminance, luminance, sourceColor.a), sourceColor, saturationValue);
59+ }"
60+ }
61+
62+ FastBlur {
63+ id: fastBlur
64+ anchors.fill: parent
65+ source: desaturateEffect
66+ visible: radius > 0
67+ radius: 0
68+ }
69+}
qml/Stages/ApplicationWindow.qml+3
@@ -20,6 +20,8 @@ import Unity.Application 0.1
20
20
21FocusScope {
21FocusScope {
22 id: root
22 id: root
23+ implicitWidth: sessionContainer.implicitWidth
24+ implicitHeight: sessionContainer.implicitHeight
23
25
24 // to be read from outside
26 // to be read from outside
25 readonly property bool fullscreen: application ? application.fullscreen : false
27 readonly property bool fullscreen: application ? application.fullscreen : false
@@ -29,6 +31,7 @@ FocusScope {
29 // to be set from outside
31 // to be set from outside
30 property QtObject application
32 property QtObject application
31 property int surfaceOrientationAngle
33 property int surfaceOrientationAngle
34+ property alias resizeSurface: sessionContainer.resizeSurface
32
35
33 QtObject {
36 QtObject {
34 id: d
37 id: d
qml/Stages/DecoratedWindow.qml+10−51
@@ -31,37 +31,10 @@ FocusScope {
31 property bool highlightShown: false
31 property bool highlightShown: false
32 property real shadowOpacity: 1
32 property real shadowOpacity: 1
33
33
34− property int windowWidth: width
35− property int windowHeight: height
36−
37 signal close();
34 signal close();
38 signal maximize();
35 signal maximize();
39 signal minimize();
36 signal minimize();
40
37
41− state: "normal"
42− states: [
43− State {
44− name: "normal"
45− PropertyChanges {
46− target: root
47− width: windowWidth
48− height: windowHeight
49− }
50− },
51− State {
52− name: "transformed"
53− PropertyChanges {
54− target: applicationWindow
55− itemScale: Math.max(root.width / root.windowWidth, root.height / root.windowHeight)
56− interactive: false
57− }
58− PropertyChanges {
59− target: clipper
60− clip: true
61− }
62− }
63− ]
64−
65 BorderImage {
38 BorderImage {
66 anchors {
39 anchors {
67 fill: root
40 fill: root
@@ -98,29 +71,15 @@ FocusScope {
98 visible: decorationShown
71 visible: decorationShown
99 }
72 }
100
73
101− Item {
102− id: clipper
103− anchors.fill: parent
104−
105− ApplicationWindow {
74+ ApplicationWindow {
106− id: applicationWindow
75+ id: applicationWindow
107− objectName: application ? "appWindow_" + application.appId : "appWindow_null"
76+ objectName: application ? "appWindow_" + application.appId : "appWindow_null"
108− anchors.top: parent.top
77+ anchors.top: parent.top
109− anchors.topMargin: root.decorationShown ? decoration.height : 0
78+ anchors.topMargin: decoration.height
110− anchors.left: parent.left
79+ anchors.left: parent.left
111− width: root.windowWidth
80+ width: root.width
112− height: root.windowHeight - (root.decorationShown ? decoration.height : 0)
81+ height: root.height - decoration.height
113− interactive: true
82+ interactive: true
114− focus: true
83+ focus: true
115−
116− property real itemScale: 1
117− transform: [
118− Scale {
119− origin.x: 0; origin.y: 0
120− xScale: applicationWindow.itemScale
121− yScale: applicationWindow.itemScale
122− }
123− ]
124− }
125 }
84 }
126}
85}
qml/Stages/DesktopSpread.qmlnew file+537
@@ -0,0 +1,537 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.4
18+import QtQuick.Layouts 1.1
19+import Ubuntu.Components 1.2
20+import Ubuntu.Gestures 0.1
21+import Unity.Application 0.1
22+
23+FocusScope {
24+ id: root
25+
26+ property bool altTabPressed: false
27+ property Item workspace: null
28+
29+ readonly property alias highlightedIndex: spreadRepeater.highlightedIndex
30+
31+ onFocusChanged: {
32+ // When the spread comes active, we want to keep focus to the input handler below
33+ // Make sure nothing inside the ApplicationWindow grabs our focus!
34+ if (focus) {
35+ forceActiveFocus();
36+ }
37+ }
38+
39+ Keys.onPressed: {
40+ switch (event.key) {
41+ case Qt.Key_Left:
42+ case Qt.Key_Backtab:
43+ selectPrevious(event.isAutoRepeat)
44+ event.accepted = true;
45+ break;
46+ case Qt.Key_Right:
47+ case Qt.Key_Tab:
48+ selectNext(event.isAutoRepeat)
49+ event.accepted = true;
50+ break;
51+ case Qt.Key_Escape:
52+ spreadRepeater.highlightedIndex = -1
53+ // Falling through intentionally
54+ case Qt.Key_Enter:
55+ case Qt.Key_Return:
56+ case Qt.Key_Space:
57+ root.state = ""
58+ event.accepted = true;
59+ }
60+ }
61+
62+ function selectNext(isAutoRepeat) {
63+ if (isAutoRepeat && spreadRepeater.highlightedIndex >= ApplicationManager.count -1) {
64+ return; // AutoRepeat is not allowed to wrap around
65+ }
66+
67+ spreadRepeater.highlightedIndex = (spreadRepeater.highlightedIndex + 1) % ApplicationManager.count;
68+ var newContentX = ((spreadFlickable.contentWidth) / (ApplicationManager.count + 1)) * Math.max(0, Math.min(ApplicationManager.count - 5, spreadRepeater.highlightedIndex - 3));
69+ if (spreadFlickable.contentX < newContentX || spreadRepeater.highlightedIndex == 0) {
70+ spreadFlickable.snapTo(newContentX)
71+ }
72+ }
73+
74+ function selectPrevious(isAutoRepeat) {
75+ if (isAutoRepeat && spreadRepeater.highlightedIndex == 0) {
76+ return; // AutoRepeat is not allowed to wrap around
77+ }
78+
79+ var newIndex = spreadRepeater.highlightedIndex - 1 >= 0 ? spreadRepeater.highlightedIndex - 1 : ApplicationManager.count - 1;
80+ spreadRepeater.highlightedIndex = newIndex;
81+ var newContentX = ((spreadFlickable.contentWidth) / (ApplicationManager.count + 1)) * Math.max(0, Math.min(ApplicationManager.count - 5, spreadRepeater.highlightedIndex - 1));
82+ if (spreadFlickable.contentX > newContentX || newIndex == ApplicationManager.count -1) {
83+ spreadFlickable.snapTo(newContentX)
84+ }
85+ }
86+
87+ function focusSelected() {
88+ if (spreadRepeater.highlightedIndex != -1) {
89+ var application = ApplicationManager.get(spreadRepeater.highlightedIndex);
90+ ApplicationManager.focusApplication(application.appId);
91+ }
92+ }
93+
94+ function cancel() {
95+ spreadRepeater.highlightedIndex = -1;
96+ state = ""
97+ }
98+
99+ Item {
100+ id: spreadContainer
101+ objectName: "spreadContainer"
102+ anchors.fill: parent
103+ visible: false
104+
105+ property bool animateIn: false
106+
107+ Repeater {
108+ id: spreadRepeater
109+ objectName: "spreadRepeater"
110+ model: ApplicationManager
111+
112+ property int highlightedIndex: -1
113+ property int closingIndex: -1
114+
115+ function indexOf(delegateItem) {
116+ for (var i = 0; i < spreadRepeater.count; i++) {
117+ if (spreadRepeater.itemAt(i) === delegateItem) {
118+ return i;
119+ }
120+ }
121+ return -1;
122+ }
123+
124+ delegate: Item {
125+ id: spreadDelegate
126+ objectName: "spreadDelegate"
127+ width: units.gu(20)
128+ height: units.gu(20)
129+
130+ property real angle: 0
131+ property real itemScale: 1
132+ property int itemScaleOriginX: 0
133+ property int itemScaleOriginY: 0
134+
135+ Behavior on x {
136+ id: closeBehavior
137+ enabled: spreadRepeater.closingIndex >= 0
138+ UbuntuNumberAnimation {
139+ onRunningChanged: if (!running) spreadRepeater.closingIndex = -1
140+ }
141+ }
142+
143+ DesktopSpreadDelegate {
144+ id: clippedSpreadDelegate
145+ objectName: "clippedSpreadDelegate"
146+ anchors.left: parent.left
147+ anchors.top: parent.top
148+ application: ApplicationManager.get(index)
149+ width: spreadMaths.spreadHeight
150+ height: spreadMaths.spreadHeight
151+
152+ transform: [
153+ Scale {
154+ origin.x: itemScaleOriginX
155+ origin.y: itemScaleOriginY
156+ xScale: itemScale
157+ yScale: itemScale
158+ },
159+ Rotation {
160+ origin { x: 0; y: (clippedSpreadDelegate.height - (clippedSpreadDelegate.height * itemScale / 2)) }
161+ axis { x: 0; y: 1; z: 0 }
162+ angle: spreadDelegate.angle
163+ }
164+ ]
165+
166+ MouseArea {
167+ id: spreadSelectArea
168+ anchors.fill: parent
169+ anchors.margins: -units.gu(2)
170+ enabled: false
171+ onClicked: {
172+ spreadRepeater.highlightedIndex = index;
173+ root.state = "";
174+ }
175+ }
176+ }
177+
178+ SpreadMaths {
179+ id: spreadMaths
180+ flickable: spreadFlickable
181+ itemIndex: index
182+ totalItems: Math.max(6, ApplicationManager.count)
183+ sceneHeight: root.height
184+ itemHeight: spreadDelegate.height
185+ }
186+
187+ states: [
188+ State {
189+ name: "altTab"; when: root.state == "altTab" && spreadContainer.visible
190+ PropertyChanges {
191+ target: spreadDelegate
192+ x: spreadMaths.animatedX
193+ y: spreadMaths.animatedY + (spreadDelegate.height - clippedSpreadDelegate.height) - units.gu(2)
194+ width: spreadMaths.spreadHeight
195+ height: spreadMaths.sceneHeight
196+ angle: spreadMaths.animatedAngle
197+ itemScale: spreadMaths.scale
198+ itemScaleOriginY: clippedSpreadDelegate.height / 2;
199+ z: index
200+ visible: spreadMaths.itemVisible
201+ }
202+ PropertyChanges {
203+ target: clippedSpreadDelegate
204+ highlightShown: index == spreadRepeater.highlightedIndex
205+ state: "transformed"
206+ shadowOpacity: spreadMaths.shadowOpacity
207+ anchors.topMargin: units.gu(2)
208+ }
209+ PropertyChanges {
210+ target: tileInfo
211+ visible: true
212+ opacity: spreadMaths.tileInfoOpacity
213+ }
214+ PropertyChanges {
215+ target: spreadSelectArea
216+ enabled: true
217+ }
218+ }
219+ ]
220+ transitions: [
221+ Transition {
222+ from: ""
223+ to: "altTab"
224+ PropertyAction { target: spreadDelegate; properties: "y,height,width,angle,z,itemScale,itemScaleOriginY,visible" }
225+ PropertyAction { target: clippedSpreadDelegate; properties: "anchors.topMargin" }
226+ PropertyAnimation {
227+ target: spreadDelegate; properties: "x"
228+ from: root.width
229+ duration: spreadContainer.animateIn ? UbuntuAnimation.FastDuration :0
230+ easing: UbuntuAnimation.StandardEasing
231+ }
232+ }
233+ ]
234+
235+ MouseArea {
236+ id: tileInfo
237+ objectName: "tileInfo"
238+ anchors { left: parent.left; top: clippedSpreadDelegate.bottom; topMargin: units.gu(5) }
239+ property int nextItemX: spreadRepeater.count > index + 1 ? spreadRepeater.itemAt(index + 1).x : spreadDelegate.x + units.gu(30)
240+ width: Math.min(units.gu(30), nextItemX - spreadDelegate.x)
241+ height: titleInfoColumn.height
242+ visible: false
243+ hoverEnabled: true
244+
245+ onContainsMouseChanged: {
246+ if (containsMouse) {
247+ spreadRepeater.highlightedIndex = index
248+ }
249+ }
250+
251+ onClicked: {
252+ root.state = ""
253+ }
254+
255+ ColumnLayout {
256+ id: titleInfoColumn
257+ anchors { left: parent.left; top: parent.top; right: parent.right }
258+ spacing: units.gu(1)
259+
260+ UbuntuShape {
261+ Layout.preferredHeight: Math.min(units.gu(6), root.height * .05)
262+ Layout.preferredWidth: height * 8 / 7.6
263+ image: Image {
264+ anchors.fill: parent
265+ source: model.icon
266+ }
267+ }
268+ Label {
269+ Layout.fillWidth: true
270+ Layout.preferredHeight: units.gu(6)
271+ text: model.name
272+ wrapMode: Text.WordWrap
273+ elide: Text.ElideRight
274+ maximumLineCount: 2
275+ }
276+ }
277+ }
278+
279+ Image {
280+ id: closeImage
281+ anchors { left: parent.left; top: parent.top; leftMargin: -height / 2; topMargin: -height / 2 + spreadMaths.closeIconOffset + units.gu(2) }
282+ source: "graphics/window-close.svg"
283+ readonly property var mousePos: hoverMouseArea.mapToItem(spreadDelegate, hoverMouseArea.mouseX, hoverMouseArea.mouseY)
284+ visible: index == spreadRepeater.highlightedIndex
285+ && mousePos.y < (clippedSpreadDelegate.height / 3)
286+ && mousePos.y > -units.gu(4)
287+ && mousePos.x > -units.gu(4)
288+ && mousePos.x < (clippedSpreadDelegate.width * 2 / 3)
289+ height: units.gu(1.5)
290+ width: height
291+ sourceSize.width: width
292+ sourceSize.height: height
293+
294+ MouseArea {
295+ id: closeMouseArea
296+ objectName: "closeMouseArea"
297+ anchors.fill: closeImage
298+ anchors.margins: -units.gu(2)
299+ onClicked: {
300+ spreadRepeater.closingIndex = index;
301+ ApplicationManager.stopApplication(model.appId)
302+ }
303+ }
304+ }
305+ }
306+ }
307+ }
308+
309+
310+ MouseArea {
311+ id: hoverMouseArea
312+ objectName: "hoverMouseArea"
313+ anchors.fill: spreadContainer
314+ propagateComposedEvents: true
315+ hoverEnabled: true
316+ enabled: false
317+ visible: enabled
318+
319+ property int scrollAreaWidth: root.width / 3
320+ property bool progressiveScrollingEnabled: false
321+
322+ onMouseXChanged: {
323+ mouse.accepted = false
324+
325+ if (hoverMouseArea.pressed) {
326+ return;
327+ }
328+
329+ // Find the hovered item and mark it active
330+ var mapped = mapToItem(spreadContainer, hoverMouseArea.mouseX, hoverMouseArea.mouseY)
331+ var itemUnder = spreadContainer.childAt(mapped.x, mapped.y)
332+ if (itemUnder) {
333+ mapped = mapToItem(itemUnder, hoverMouseArea.mouseX, hoverMouseArea.mouseY)
334+ var delegateChild = itemUnder.childAt(mapped.x, mapped.y)
335+ if (delegateChild && (delegateChild.objectName === "clippedSpreadDelegate" || delegateChild.objectName === "tileInfo")) {
336+ spreadRepeater.highlightedIndex = spreadRepeater.indexOf(itemUnder)
337+ }
338+ }
339+
340+ if (spreadFlickable.contentWidth > spreadFlickable.minContentWidth) {
341+ var margins = spreadFlickable.width * 0.05;
342+
343+ if (!progressiveScrollingEnabled && mouseX < spreadFlickable.width - scrollAreaWidth) {
344+ progressiveScrollingEnabled = true
345+ }
346+
347+ // do we need to scroll?
348+ if (mouseX < scrollAreaWidth + margins) {
349+ var progress = Math.min(1, (scrollAreaWidth + margins - mouseX) / (scrollAreaWidth - margins));
350+ var contentX = (1 - progress) * (spreadFlickable.contentWidth - spreadFlickable.width)
351+ spreadFlickable.contentX = Math.max(0, Math.min(spreadFlickable.contentX, contentX))
352+ }
353+ if (mouseX > spreadFlickable.width - scrollAreaWidth && progressiveScrollingEnabled) {
354+ var progress = Math.min(1, (mouseX - (spreadFlickable.width - scrollAreaWidth)) / (scrollAreaWidth - margins))
355+ var contentX = progress * (spreadFlickable.contentWidth - spreadFlickable.width)
356+ spreadFlickable.contentX = Math.min(spreadFlickable.contentWidth - spreadFlickable.width, Math.max(spreadFlickable.contentX, contentX))
357+ }
358+ }
359+ }
360+ onPressed: mouse.accepted = false
361+ }
362+
363+ FloatingFlickable {
364+ id: spreadFlickable
365+ objectName: "spreadFlickable"
366+ anchors.fill: parent
367+ property int minContentWidth: 6 * Math.min(height / 4, width / 5)
368+ contentWidth: Math.max(6, ApplicationManager.count) * Math.min(height / 4, width / 5)
369+ enabled: false
370+
371+ function snapTo(contentX) {
372+ snapAnimation.stop();
373+ snapAnimation.to = contentX
374+ snapAnimation.start();
375+ }
376+
377+ UbuntuNumberAnimation {
378+ id: snapAnimation
379+ target: spreadFlickable
380+ property: "contentX"
381+ }
382+ }
383+
384+ Item {
385+ id: workspaceSelector
386+ anchors {
387+ left: parent.left
388+ top: parent.top
389+ right: parent.right
390+ topMargin: units.gu(3.5)
391+ }
392+ height: root.height * 0.25
393+ visible: false
394+
395+ RowLayout {
396+ anchors.fill: parent
397+ spacing: units.gu(1)
398+ Item { Layout.fillWidth: true }
399+ Repeater {
400+ model: 1 // TODO: will be a workspacemodel in the future
401+ Item {
402+ Layout.fillHeight: true
403+ Layout.preferredWidth: ((height - units.gu(6)) * root.width / root.height)
404+ Image {
405+ source: root.background
406+ anchors {
407+ left: parent.left
408+ right: parent.right
409+ verticalCenter: parent.verticalCenter
410+ }
411+ height: parent.height * 0.75
412+
413+ ShaderEffect {
414+ anchors.fill: parent
415+
416+ property var source: ShaderEffectSource {
417+ id: shaderEffectSource
418+ sourceItem: appContainer
419+ }
420+
421+ fragmentShader: "
422+ varying highp vec2 qt_TexCoord0;
423+ uniform sampler2D source;
424+ void main(void)
425+ {
426+ highp vec4 sourceColor = texture2D(source, qt_TexCoord0);
427+ gl_FragColor = sourceColor;
428+ }"
429+ }
430+ }
431+
432+ // TODO: This is the bar for the currently selected workspace
433+ // Enable this once the workspace stuff is implemented
434+ // Rectangle {
435+ // anchors { left: parent.left; right: parent.right; bottom: parent.bottom }
436+ // height: units.dp(2)
437+ // color: UbuntuColors.orange
438+ // visible: index == 0 // TODO: should be active workspace index
439+ // }
440+ }
441+
442+ }
443+ // TODO: This is the "new workspace" button. Enable this once workspaces are implemented
444+ // Item {
445+ // Layout.fillHeight: true
446+ // Layout.preferredWidth: ((height - units.gu(6)) * root.width / root.height)
447+ // Rectangle {
448+ // anchors {
449+ // left: parent.left
450+ // right: parent.right
451+ // verticalCenter: parent.verticalCenter
452+ // }
453+ // height: parent.height * 0.75
454+ // color: "#22ffffff"
455+
456+ // Label {
457+ // anchors.centerIn: parent
458+ // font.pixelSize: parent.height / 2
459+ // text: "+"
460+ // }
461+ // }
462+ // }
463+ Item { Layout.fillWidth: true }
464+ }
465+ }
466+
467+ Label {
468+ id: currentSelectedLabel
469+ anchors { bottom: parent.bottom; bottomMargin: root.height * 0.625; horizontalCenter: parent.horizontalCenter }
470+ text: spreadRepeater.highlightedIndex >= 0 ? ApplicationManager.get(spreadRepeater.highlightedIndex).name : ""
471+ visible: false
472+ fontSize: "large"
473+ }
474+
475+ states: [
476+ State {
477+ name: "altTab"; when: root.altTabPressed
478+ PropertyChanges { target: blurLayer; saturation: 0.8; blurRadius: 60; visible: true }
479+ PropertyChanges { target: workspaceSelector; visible: true }
480+ PropertyChanges { target: spreadContainer; visible: true }
481+ PropertyChanges { target: spreadFlickable; enabled: spreadFlickable.contentWidth > spreadFlickable.minContentWidth }
482+ PropertyChanges { target: currentSelectedLabel; visible: true }
483+ PropertyChanges { target: spreadBackground; visible: true }
484+ PropertyChanges { target: hoverMouseArea; enabled: true }
485+ }
486+ ]
487+ transitions: [
488+ Transition {
489+ from: "*"
490+ to: "altTab"
491+ SequentialAnimation {
492+ PropertyAction { target: hoverMouseArea; property: "progressiveScrollingEnabled"; value: false }
493+ PropertyAction { target: spreadRepeater; property: "highlightedIndex"; value: Math.min(ApplicationManager.count - 1, 1) }
494+ PauseAnimation { duration: 140 }
495+ PropertyAction { target: workspaceSelector; property: "visible" }
496+ PropertyAction { target: spreadContainer; property: "visible" }
497+ ParallelAnimation {
498+ UbuntuNumberAnimation {
499+ target: blurLayer; properties: "saturation,blurRadius";
500+ duration: spreadContainer.animateIn ? UbuntuAnimation.FastDuration : 0
501+ }
502+ PropertyAction { target: spreadFlickable; property: "visible" }
503+ PropertyAction { targets: [currentSelectedLabel,spreadBackground]; property: "visible" }
504+ PropertyAction { target: spreadFlickable; property: "contentX"; value: 0 }
505+ }
506+ }
507+ },
508+ Transition {
509+ from: "*"
510+ to: "*"
511+ PropertyAnimation { property: "opacity" }
512+ ScriptAction { script: { root.focusSelected() } }
513+ PropertyAction { target: spreadRepeater; property: "highlightedIndex"; value: -1 }
514+ PropertyAction { target: spreadContainer; property: "animateIn"; value: false }
515+ }
516+
517+ ]
518+
519+ MouseArea {
520+ id: rightEdgePushArea
521+ anchors {
522+ top: parent.top
523+ right: parent.right
524+ bottom: parent.bottom
525+ }
526+ // TODO: Make this a push to edge thing like the launcher when we can,
527+ // for now, yes, we want 1 pixel, regardless of the scaling
528+ width: 1
529+ hoverEnabled: true
530+ onContainsMouseChanged: {
531+ if (containsMouse) {
532+ spreadContainer.animateIn = true;
533+ root.state = "altTab";
534+ }
535+ }
536+ }
537+}
qml/Stages/DesktopSpreadDelegate.qmlnew file+111
@@ -0,0 +1,111 @@
1+/*
2+ * Copyright (C) 2014-2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors: Michael Zanetti <michael.zanetti@canonical.com>
17+ */
18+
19+import QtQuick 2.3
20+import Ubuntu.Components 1.1
21+import Unity.Application 0.1
22+
23+Item {
24+ id: root
25+
26+ property alias window: applicationWindow
27+ property alias application: applicationWindow.application
28+
29+ property bool highlightShown: false
30+ property real shadowOpacity: 1
31+
32+ property int windowWidth: application.session && application.session.surface ? application.session.surface.size.width : 0
33+ property int windowHeight: application.session && application.session.surface ? application.session.surface.size.height : 0
34+
35+ state: "normal"
36+ states: [
37+ State {
38+ name: "normal"
39+ PropertyChanges {
40+ target: root
41+ width: windowWidth
42+ height: windowHeight
43+ }
44+ },
45+ State {
46+ name: "transformed"
47+ PropertyChanges {
48+ target: applicationWindow
49+ itemScale: Math.max(root.width / root.windowWidth, root.height / root.windowHeight)
50+ interactive: false
51+ }
52+ PropertyChanges {
53+ target: clipper
54+ clip: true
55+ }
56+ }
57+ ]
58+
59+ BorderImage {
60+ anchors {
61+ fill: root
62+ margins: -units.gu(2)
63+ }
64+ source: "graphics/dropshadow2gu.sci"
65+ opacity: root.shadowOpacity * .3
66+ }
67+
68+ Rectangle {
69+ id: selectionHighlight
70+ anchors.fill: parent
71+ anchors.margins: -units.gu(1)
72+ color: "white"
73+ opacity: highlightShown ? 0.15 : 0
74+ antialiasing: true
75+ }
76+
77+ Rectangle {
78+ anchors { left: selectionHighlight.left; right: selectionHighlight.right; bottom: selectionHighlight.bottom; }
79+ height: units.dp(2)
80+ color: UbuntuColors.orange
81+ visible: root.highlightShown
82+ antialiasing: true
83+ }
84+
85+ Item {
86+ id: clipper
87+ anchors.fill: parent
88+
89+ ApplicationWindow {
90+ id: applicationWindow
91+ objectName: application ? "appWindow_" + application.appId : "appWindow_null"
92+ anchors.top: parent.top
93+ anchors.topMargin: 0
94+ anchors.left: parent.left
95+ width: root.windowWidth
96+ height: root.windowHeight
97+ interactive: false
98+ resizeSurface: false
99+ focus: false
100+
101+ property real itemScale: 1
102+ transform: [
103+ Scale {
104+ origin.x: 0; origin.y: 0
105+ xScale: applicationWindow.itemScale
106+ yScale: applicationWindow.itemScale
107+ }
108+ ]
109+ }
110+ }
111+}
qml/Stages/DesktopStage.qml+49−469
@@ -20,6 +20,7 @@ import QtQuick 2.4
20import QtQuick.Layouts 1.1
20import QtQuick.Layouts 1.1
21import Ubuntu.Components 1.1
21import Ubuntu.Components 1.1
22import Unity.Application 0.1
22import Unity.Application 0.1
23+import "../Components"
23import "../Components/PanelState"
24import "../Components/PanelState"
24import Utils 0.1
25import Utils 0.1
25import Ubuntu.Gestures 0.1
26import Ubuntu.Gestures 0.1
@@ -41,6 +42,8 @@ Rectangle {
41 property bool beingResized: false
42 property bool beingResized: false
42 property bool keepDashRunning: true
43 property bool keepDashRunning: true
43 property bool suspended: false
44 property bool suspended: false
45+ property alias background: wallpaper.source
46+ property alias altTabPressed: spread.altTabPressed
44
47
45 // functions to be called from outside
48 // functions to be called from outside
46 function updateFocusedAppOrientation() { /* TODO */ }
49 function updateFocusedAppOrientation() { /* TODO */ }
@@ -53,21 +56,11 @@ Rectangle {
53 property int mainAppWindowOrientationAngle: 0
56 property int mainAppWindowOrientationAngle: 0
54 readonly property bool orientationChangesEnabled: false
57 readonly property bool orientationChangesEnabled: false
55
58
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−
66 Connections {
59 Connections {
67 target: ApplicationManager
60 target: ApplicationManager
68 onApplicationAdded: {
61 onApplicationAdded: {
69− if (root.state == "altTab") {
62+ if (spread.state == "altTab") {
70− root.state = "";
63+ spread.state = "";
71 }
64 }
72
65
73 ApplicationManager.requestFocusApplication(appId)
66 ApplicationManager.requestFocusApplication(appId)
@@ -77,7 +70,11 @@ Rectangle {
77 var appIndex = priv.indexOf(appId);
70 var appIndex = priv.indexOf(appId);
78 var appDelegate = appRepeater.itemAt(appIndex);
71 var appDelegate = appRepeater.itemAt(appIndex);
79 appDelegate.minimized = false;
72 appDelegate.minimized = false;
80− appDelegate.focus = true;
73+ ApplicationManager.focusApplication(appId)
74+
75+ if (spread.state == "altTab") {
76+ spread.cancel()
77+ }
81 }
78 }
82 }
79 }
83
80
@@ -115,72 +112,17 @@ Rectangle {
115 value: priv.focusedAppDelegate !== null && priv.focusedAppDelegate.state === "maximized"
112 value: priv.focusedAppDelegate !== null && priv.focusedAppDelegate.state === "maximized"
116 }
113 }
117
114
118− Rectangle {
119− id: spreadBackground
120− anchors.fill: parent
121− color: "#55000000"
122− visible: false
123− }
124−
125 FocusScope {
115 FocusScope {
126 id: appContainer
116 id: appContainer
127 objectName: "appContainer"
117 objectName: "appContainer"
128 anchors.fill: parent
118 anchors.fill: parent
129− focus: true
119+ focus: spread.state !== "altTab"
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− }
165
120
166− function selectPrevious(isAutoRepeat) {
121+ CrossFadeImage {
167− if (isAutoRepeat && appRepeater.highlightedIndex == 0) {
122+ id: wallpaper
168− return; // AutoRepeat is not allowed to wrap around
123+ anchors.fill: parent
169− }
124+ sourceSize { height: root.height; width: root.width }
170−
125+ fillMode: Image.PreserveAspectCrop
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− }
184 }
126 }
185
127
186 Repeater {
128 Repeater {
@@ -188,30 +130,13 @@ Rectangle {
188 model: ApplicationManager
130 model: ApplicationManager
189 objectName: "appRepeater"
131 objectName: "appRepeater"
190
132
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−
203 delegate: FocusScope {
133 delegate: FocusScope {
204 id: appDelegate
134 id: appDelegate
205 z: ApplicationManager.count - index
135 z: ApplicationManager.count - index
206 y: units.gu(3)
136 y: units.gu(3)
207 width: units.gu(60)
137 width: units.gu(60)
208 height: units.gu(50)
138 height: units.gu(50)
209−
139+ focus: model.appId === priv.focusedAppId
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
140
216 readonly property int minWidth: units.gu(10)
141 readonly property int minWidth: units.gu(10)
217 readonly property int minHeight: units.gu(10)
142 readonly property int minHeight: units.gu(10)
@@ -225,13 +150,6 @@ Rectangle {
225 }
150 }
226 }
151 }
227
152
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−
235 Binding {
153 Binding {
236 target: ApplicationManager.get(index)
154 target: ApplicationManager.get(index)
237 property: "requestedState"
155 property: "requestedState"
@@ -256,63 +174,17 @@ Rectangle {
256 maximized = false;
174 maximized = false;
257 }
175 }
258
176
259− Behavior on x {
260− id: closeBehavior
261− enabled: appRepeater.closingIndex >= 0
262− UbuntuNumberAnimation {
263− onRunningChanged: if (!running) appRepeater.closingIndex = -1
264− }
265− }
266−
267 states: [
177 states: [
268 State {
178 State {
269− name: "normal"; when: !appDelegate.maximized && !appDelegate.minimized && root.state !== "altTab"
179+ name: "normal"; when: !appDelegate.maximized && !appDelegate.minimized
270 },
180 },
271 State {
181 State {
272− name: "maximized"; when: appDelegate.maximized && (root.state !== "altTab" || (root.state == "altTab" && !root.workspacesUpdated))
182+ name: "maximized"; when: appDelegate.maximized
273 PropertyChanges { target: appDelegate; x: 0; y: 0; width: root.width; height: root.height }
183 PropertyChanges { target: appDelegate; x: 0; y: 0; width: root.width; height: root.height }
274 },
184 },
275 State {
185 State {
276− name: "minimized"; when: appDelegate.minimized && (root.state !== "altTab" || (root.state == "altTab" && !root.workspacesUpdated))
186+ name: "minimized"; when: appDelegate.minimized
277 PropertyChanges { target: appDelegate; x: -appDelegate.width / 2; scale: units.gu(5) / appDelegate.width; opacity: 0 }
187 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− }
316 }
188 }
317 ]
189 ]
318 transitions: [
190 transitions: [
@@ -334,18 +206,13 @@ Rectangle {
334 }
206 }
335 }
207 }
336 ]
208 ]
337− property real angle: 0
209+
338− property real itemScale: 1
210+ Binding {
339− property int itemScaleOriginX: 0
211+ id: previewBinding
340− property int itemScaleOriginY: 0
212+ target: appDelegate
341−
213+ property: "z"
342− SpreadMaths {
343− id: spreadMaths
344− flickable: spreadFlickable
345− itemIndex: index
346− totalItems: Math.max(6, ApplicationManager.count)
214+ value: ApplicationManager.count + 1
347− sceneHeight: root.height
215+ when: index == spread.highlightedIndex && blurLayer.ready
348− itemHeight: appDelegate.height
349 }
216 }
350
217
351 WindowMoveResizeArea {
218 WindowMoveResizeArea {
@@ -356,7 +223,7 @@ Rectangle {
356 resizeHandleWidth: units.gu(2)
223 resizeHandleWidth: units.gu(2)
357 windowId: model.appId // FIXME: Change this to point to windowId once we have such a thing
224 windowId: model.appId // FIXME: Change this to point to windowId once we have such a thing
358
225
359− onPressed: { appDelegate.focus = true; }
226+ onPressed: { ApplicationManager.focusApplication(model.appId) }
360 }
227 }
361
228
362 DecoratedWindow {
229 DecoratedWindow {
@@ -364,8 +231,8 @@ Rectangle {
364 objectName: "decoratedWindow"
231 objectName: "decoratedWindow"
365 anchors.left: appDelegate.left
232 anchors.left: appDelegate.left
366 anchors.top: appDelegate.top
233 anchors.top: appDelegate.top
367− windowWidth: appDelegate.windowWidth
234+ width: appDelegate.width
368− windowHeight: appDelegate.windowHeight
235+ height: appDelegate.height
369 application: ApplicationManager.get(index)
236 application: ApplicationManager.get(index)
370 active: ApplicationManager.focusedApplicationId === model.appId
237 active: ApplicationManager.focusedApplicationId === model.appId
371 focus: true
238 focus: true
@@ -373,324 +240,37 @@ Rectangle {
373 onClose: ApplicationManager.stopApplication(model.appId)
240 onClose: ApplicationManager.stopApplication(model.appId)
374 onMaximize: appDelegate.maximize()
241 onMaximize: appDelegate.maximize()
375 onMinimize: appDelegate.minimize()
242 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− }
470 }
243 }
471 }
244 }
472 }
245 }
473 }
246 }
474
247
475− MouseArea {
248+ BlurLayer {
476− id: hoverMouseArea
249+ id: blurLayer
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"
526 anchors.fill: parent
250 anchors.fill: parent
527− property int minContentWidth: 6 * Math.min(height / 4, width / 5)
251+ source: appContainer
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
553 visible: false
252 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− }
628 }
253 }
629
254
630− Label {
255+ Rectangle {
631− id: currentSelectedLabel
256+ id: spreadBackground
632− anchors { bottom: parent.bottom; bottomMargin: root.height * 0.625; horizontalCenter: parent.horizontalCenter }
257+ anchors.fill: parent
633− text: appRepeater.highlightedIndex >= 0 ? ApplicationManager.get(appRepeater.highlightedIndex).name : ""
258+ color: "#55000000"
634 visible: false
259 visible: false
635− fontSize: "large"
636 }
260 }
637
261
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−
679 MouseArea {
262 MouseArea {
680− id: rightEdgePushArea
263+ id: eventEater
681− anchors {
264+ anchors.fill: parent
682− top: parent.top
265+ visible: spreadBackground.visible
683− right: parent.right
266+ enabled: visible
684− bottom: parent.bottom
685− }
267+ }
686− // TODO: Make this a push to edge thing like the launcher when we can,
268+
687− // for now, yes, we want 1 pixel, regardless of the scaling
269+ DesktopSpread {
688− width: 1
270+ id: spread
689− hoverEnabled: true
271+ objectName: "spread"
690− onContainsMouseChanged: {
272+ anchors.fill: parent
691− if (containsMouse) {
273+ workspace: appContainer
692− root.state = "altTab";
274+ focus: state == "altTab"
693− }
694− }
695 }
275 }
696}
276}
qml/Stages/SessionContainer.qml+3
@@ -20,11 +20,14 @@ import "Animations"
20FocusScope {
20FocusScope {
21 id: root
21 id: root
22 objectName: "sessionContainer"
22 objectName: "sessionContainer"
23+ implicitWidth: _surfaceContainer.implicitWidth
24+ implicitHeight: _surfaceContainer.implicitHeight
23 property QtObject session
25 property QtObject session
24 readonly property var childSessions: session ? session.childSessions : null
26 readonly property var childSessions: session ? session.childSessions : null
25 readonly property alias surface: _surfaceContainer.surface
27 readonly property alias surface: _surfaceContainer.surface
26 property alias interactive: _surfaceContainer.interactive
28 property alias interactive: _surfaceContainer.interactive
27 property alias surfaceOrientationAngle: _surfaceContainer.surfaceOrientationAngle
29 property alias surfaceOrientationAngle: _surfaceContainer.surfaceOrientationAngle
30+ property alias resizeSurface: _surfaceContainer.resizeSurface
28
31
29 readonly property alias surfaceContainer: _surfaceContainer
32 readonly property alias surfaceContainer: _surfaceContainer
30 SurfaceContainer {
33 SurfaceContainer {
qml/Stages/SurfaceContainer.qml+3−2
@@ -28,6 +28,7 @@ FocusScope {
28 property bool hadSurface: false
28 property bool hadSurface: false
29 property bool interactive
29 property bool interactive
30 property int surfaceOrientationAngle: 0
30 property int surfaceOrientationAngle: 0
31+ property bool resizeSurface: true
31
32
32 onSurfaceChanged: {
33 onSurfaceChanged: {
33 if (surface) {
34 if (surface) {
@@ -52,8 +53,8 @@ FocusScope {
52
53
53 consumesInput: true
54 consumesInput: true
54
55
55− surfaceWidth: width
56+ surfaceWidth: root.resizeSurface ? width : -1
56− surfaceHeight: height
57+ surfaceHeight: root.resizeSurface ? height : -1
57
58
58 anchors.fill: root
59 anchors.fill: root
59 enabled: root.interactive
60 enabled: root.interactive
qml/Stages/WindowMoveResizeArea.qml+1−1
@@ -104,6 +104,6 @@ MouseArea {
104 }
104 }
105
105
106 Component.onDestruction: {
106 Component.onDestruction: {
107− windowStateStorage.saveGeometry(root.windowId, Qt.rect(target.x, target.y, target.windowWidth, target.windowHeight))
107+ windowStateStorage.saveGeometry(root.windowId, Qt.rect(target.x, target.y, target.width, target.height))
108 }
108 }
109}
109}
tests/mocks/Unity/Application/MirSurface.cpp+12−5
@@ -36,12 +36,12 @@ MirSurface::MirSurface(const QString& name,
36 , m_width(-1)
36 , m_width(-1)
37 , m_height(-1)
37 , m_height(-1)
38{
38{
39− qDebug() << "MirSurface::MirSurface() " << name;
39+// qDebug() << "MirSurface::MirSurface() " << name;
40}
40}
41
41
42MirSurface::~MirSurface()
42MirSurface::~MirSurface()
43{
43{
44− qDebug() << "MirSurface::~MirSurface() " << name();
44+// qDebug() << "MirSurface::~MirSurface() " << name();
45}
45}
46
46
47QString MirSurface::name() const
47QString MirSurface::name() const
@@ -75,7 +75,7 @@ bool MirSurface::live() const
75
75
76void MirSurface::setLive(bool live)
76void MirSurface::setLive(bool live)
77{
77{
78− qDebug().nospace() << "MirSurface::setLive("<<live<<") " << name();
78+// qDebug().nospace() << "MirSurface::setLive("<<live<<") " << name();
79 if (live == m_live)
79 if (live == m_live)
80 return;
80 return;
81
81
@@ -123,13 +123,13 @@ void MirSurface::setOrientationAngle(Mir::OrientationAngle angle)
123void MirSurface::incrementViewCount()
123void MirSurface::incrementViewCount()
124{
124{
125 ++m_viewCount;
125 ++m_viewCount;
126− qDebug().nospace() << "MirSurface::incrementViewCount() viewCount(after)=" << m_viewCount << " " << name();
126+// qDebug().nospace() << "MirSurface::incrementViewCount() viewCount(after)=" << m_viewCount << " " << name();
127}
127}
128
128
129void MirSurface::decrementViewCount()
129void MirSurface::decrementViewCount()
130{
130{
131 --m_viewCount;
131 --m_viewCount;
132− qDebug().nospace() << "MirSurface::decrementViewCount() viewCount(after)=" << m_viewCount << " " << name();
132+// qDebug().nospace() << "MirSurface::decrementViewCount() viewCount(after)=" << m_viewCount << " " << name();
133
133
134 Q_ASSERT(m_viewCount >= 0);
134 Q_ASSERT(m_viewCount >= 0);
135
135
@@ -170,13 +170,20 @@ int MirSurface::height() const
170
170
171void MirSurface::resize(int width, int height)
171void MirSurface::resize(int width, int height)
172{
172{
173+ bool changed = false;
173 if (width != m_width) {
174 if (width != m_width) {
174 m_width = width;
175 m_width = width;
175 Q_EMIT widthChanged();
176 Q_EMIT widthChanged();
177+ changed = true;
176 }
178 }
177
179
178 if (m_height != height) {
180 if (m_height != height) {
179 m_height = height;
181 m_height = height;
180 Q_EMIT heightChanged();
182 Q_EMIT heightChanged();
183+ changed = true;
184+ }
185+
186+ if (changed) {
187+ Q_EMIT sizeChanged(QSize(width, height));
181 }
188 }
182}
189}
tests/mocks/Unity/Application/MirSurfaceItem.cpp+8−8
@@ -38,7 +38,7 @@ MirSurfaceItem::MirSurfaceItem(QQuickItem *parent)
38 , m_touchPressCount(0)
38 , m_touchPressCount(0)
39 , m_touchReleaseCount(0)
39 , m_touchReleaseCount(0)
40{
40{
41− qDebug() << "MirSurfaceItem::MirSurfaceItem() " << (void*)(this) << name();
41+// qDebug() << "MirSurfaceItem::MirSurfaceItem() " << (void*)(this) << name();
42 setAcceptedMouseButtons(Qt::LeftButton | Qt::MiddleButton | Qt::RightButton |
42 setAcceptedMouseButtons(Qt::LeftButton | Qt::MiddleButton | Qt::RightButton |
43 Qt::ExtraButton1 | Qt::ExtraButton2 | Qt::ExtraButton3 | Qt::ExtraButton4 |
43 Qt::ExtraButton1 | Qt::ExtraButton2 | Qt::ExtraButton3 | Qt::ExtraButton4 |
44 Qt::ExtraButton5 | Qt::ExtraButton6 | Qt::ExtraButton7 | Qt::ExtraButton8 |
44 Qt::ExtraButton5 | Qt::ExtraButton6 | Qt::ExtraButton7 | Qt::ExtraButton8 |
@@ -48,7 +48,7 @@ MirSurfaceItem::MirSurfaceItem(QQuickItem *parent)
48
48
49MirSurfaceItem::~MirSurfaceItem()
49MirSurfaceItem::~MirSurfaceItem()
50{
50{
51− qDebug() << "MirSurfaceItem::~MirSurfaceItem() " << (void*)(this) << name();
51+// qDebug() << "MirSurfaceItem::~MirSurfaceItem() " << (void*)(this) << name();
52 setSurface(nullptr);
52 setSurface(nullptr);
53}
53}
54
54
@@ -136,7 +136,7 @@ void MirSurfaceItem::onComponentStatusChanged(QQmlComponent::Status status)
136
136
137void MirSurfaceItem::createQmlContentItem()
137void MirSurfaceItem::createQmlContentItem()
138{
138{
139− qDebug() << "MirSurfaceItem::createQmlContentItem()";
139+// qDebug() << "MirSurfaceItem::createQmlContentItem()";
140
140
141 m_qmlItem = qobject_cast<QQuickItem*>(m_qmlContentComponent->create());
141 m_qmlItem = qobject_cast<QQuickItem*>(m_qmlContentComponent->create());
142 m_qmlItem->setParentItem(this);
142 m_qmlItem->setParentItem(this);
@@ -178,9 +178,9 @@ void MirSurfaceItem::mouseReleaseEvent(QMouseEvent * event)
178
178
179void MirSurfaceItem::setSurface(MirSurfaceInterface* surface)
179void MirSurfaceItem::setSurface(MirSurfaceInterface* surface)
180{
180{
181− qDebug().nospace() << "MirSurfaceItem::setSurface() this=" << (void*)(this)
181+// qDebug().nospace() << "MirSurfaceItem::setSurface() this=" << (void*)(this)
182− << " name=" << name()
182+// << " name=" << name()
183− << " surface=" << surface;
183+// << " surface=" << surface;
184
184
185 if (m_qmlSurface == surface) {
185 if (m_qmlSurface == surface) {
186 return;
186 return;
@@ -268,7 +268,7 @@ int MirSurfaceItem::surfaceWidth() const
268
268
269void MirSurfaceItem::setSurfaceWidth(int value)
269void MirSurfaceItem::setSurfaceWidth(int value)
270{
270{
271− if (m_surfaceWidth != value) {
271+ if (value != -1 && m_surfaceWidth != value) {
272 m_surfaceWidth = value;
272 m_surfaceWidth = value;
273 Q_EMIT surfaceWidthChanged(m_surfaceWidth);
273 Q_EMIT surfaceWidthChanged(m_surfaceWidth);
274 updateSurfaceSize();
274 updateSurfaceSize();
@@ -282,7 +282,7 @@ int MirSurfaceItem::surfaceHeight() const
282
282
283void MirSurfaceItem::setSurfaceHeight(int value)
283void MirSurfaceItem::setSurfaceHeight(int value)
284{
284{
285− if (m_surfaceHeight != value) {
285+ if (value != -1 && m_surfaceHeight != value) {
286 m_surfaceHeight = value;
286 m_surfaceHeight = value;
287 Q_EMIT surfaceHeightChanged(m_surfaceHeight);
287 Q_EMIT surfaceHeightChanged(m_surfaceHeight);
288 updateSurfaceSize();
288 updateSurfaceSize();
tests/mocks/Unity/Application/Session.cpp+4−4
@@ -35,14 +35,14 @@ Session::Session(const QString &name,
35 , m_parentSession(nullptr)
35 , m_parentSession(nullptr)
36 , m_children(new SessionModel(this))
36 , m_children(new SessionModel(this))
37{
37{
38− qDebug() << "Session::Session() " << this->name();
38+// qDebug() << "Session::Session() " << this->name();
39
39
40 QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership);
40 QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership);
41}
41}
42
42
43Session::~Session()
43Session::~Session()
44{
44{
45− qDebug() << "Session::~Session() " << name();
45+// qDebug() << "Session::~Session() " << name();
46
46
47 QList<Session*> children(m_children->list());
47 QList<Session*> children(m_children->list());
48 for (Session* child : children) {
48 for (Session* child : children) {
@@ -67,7 +67,7 @@ Session::~Session()
67
67
68void Session::release()
68void Session::release()
69{
69{
70− qDebug() << "Session::release " << name();
70+// qDebug() << "Session::release " << name();
71 deleteLater();
71 deleteLater();
72}
72}
73
73
@@ -82,7 +82,7 @@ void Session::setApplication(ApplicationInfo* application)
82
82
83void Session::setSurface(MirSurface* surface)
83void Session::setSurface(MirSurface* surface)
84{
84{
85− qDebug() << "Session::setSurface - session=" << name() << "surface=" << surface;
85+// qDebug() << "Session::setSurface - session=" << name() << "surface=" << surface;
86 if (m_surface == surface)
86 if (m_surface == surface)
87 return;
87 return;
88
88
tests/qmltests/tst_Shell.qml+131−43
@@ -322,8 +322,9 @@ Rectangle {
322
322
323 function loadDesktopShellWithApps() {
323 function loadDesktopShellWithApps() {
324 loadShell("desktop");
324 loadShell("desktop");
325+ waitForRendering(shell)
325 shell.usageScenario = "desktop"
326 shell.usageScenario = "desktop"
326− waitForRendering(root)
327+ waitForRendering(shell)
327 var app1 = ApplicationManager.startApplication("dialer-app")
328 var app1 = ApplicationManager.startApplication("dialer-app")
328 var app2 = ApplicationManager.startApplication("webbrowser-app")
329 var app2 = ApplicationManager.startApplication("webbrowser-app")
329 var app3 = ApplicationManager.startApplication("camera-app")
330 var app3 = ApplicationManager.startApplication("camera-app")
@@ -1407,17 +1408,18 @@ Rectangle {
1407 keyClick(Qt.Key_Tab, Qt.ControlModifier)
1408 keyClick(Qt.Key_Tab, Qt.ControlModifier)
1408 tryCompare(app2.session.surface, "activeFocus", true)
1409 tryCompare(app2.session.surface, "activeFocus", true)
1409
1410
1411+ var desktopSpread = findChild(shell, "spread")
1410
1412
1411− tryCompare(desktopStage, "state", "")
1413+ tryCompare(desktopSpread, "state", "")
1412
1414
1413 // Just press Alt, make sure the spread comes up
1415 // Just press Alt, make sure the spread comes up
1414 keyPress(Qt.Key_Control);
1416 keyPress(Qt.Key_Control);
1415 keyClick(Qt.Key_Tab);
1417 keyClick(Qt.Key_Tab);
1416− tryCompare(desktopStage, "state", "altTab")
1418+ tryCompare(desktopSpread, "state", "altTab")
1417
1419
1418 // Release control, check if spread disappears
1420 // Release control, check if spread disappears
1419 keyRelease(Qt.Key_Control)
1421 keyRelease(Qt.Key_Control)
1420− tryCompare(desktopStage, "state", "")
1422+ tryCompare(desktopSpread, "state", "")
1421
1423
1422 // Focus should have switched back now
1424 // Focus should have switched back now
1423 tryCompare(app3.session.surface, "activeFocus", true)
1425 tryCompare(app3.session.surface, "activeFocus", true)
@@ -1429,41 +1431,44 @@ Rectangle {
1429 var desktopStage = findChild(shell, "stage");
1431 var desktopStage = findChild(shell, "stage");
1430 verify(desktopStage !== null)
1432 verify(desktopStage !== null)
1431
1433
1432− var appContainer = findInvisibleChild(shell, "appContainer")
1434+ var desktopSpread = findChild(shell, "spread");
1433− verify(appContainer !== null)
1435+ verify(desktopSpread !== null)
1434
1436
1437+ var spreadContainer = findInvisibleChild(shell, "spreadContainer")
1438+ verify(spreadContainer !== null)
1439+
1435− var appRepeater = findInvisibleChild(shell, "appRepeater")
1440+ var spreadRepeater = findInvisibleChild(shell, "spreadRepeater")
1436− verify(appRepeater !== null)
1441+ verify(spreadRepeater !== null)
1437
1442
1438 // remember the focused appId
1443 // remember the focused appId
1439 var focused = ApplicationManager.get(ApplicationManager.findApplication(ApplicationManager.focusedApplicationId));
1444 var focused = ApplicationManager.get(ApplicationManager.findApplication(ApplicationManager.focusedApplicationId));
1440
1445
1441− tryCompare(desktopStage, "state", "")
1446+ tryCompare(desktopSpread, "state", "")
1442
1447
1443 // Just press Alt, make sure the spread comes up
1448 // Just press Alt, make sure the spread comes up
1444 keyPress(Qt.Key_Control);
1449 keyPress(Qt.Key_Control);
1445 keyClick(Qt.Key_Tab);
1450 keyClick(Qt.Key_Tab);
1446− tryCompare(desktopStage, "state", "altTab")
1451+ tryCompare(desktopSpread, "state", "altTab")
1447− tryCompare(appRepeater, "highlightedIndex", 1)
1452+ tryCompare(spreadRepeater, "highlightedIndex", 1)
1448 waitForRendering(shell)
1453 waitForRendering(shell)
1449
1454
1450 // Now press and hold Tab, make sure the highlight moves all the way but stops at the last one
1455 // Now press and hold Tab, make sure the highlight moves all the way but stops at the last one
1451 // We can't simulate a pressed key with keyPress() currently, so let's inject the events
1456 // We can't simulate a pressed key with keyPress() currently, so let's inject the events
1452 // at API level. Jump for 10 times, verify that it's still at the last one and didn't wrap around.
1457 // at API level. Jump for 10 times, verify that it's still at the last one and didn't wrap around.
1453 for (var i = 0; i < 10; i++) {
1458 for (var i = 0; i < 10; i++) {
1454− appContainer.selectNext(true); // true == isAutoRepeat
1459+ desktopSpread.selectNext(true); // true == isAutoRepeat
1455 wait(0); // Trigger the event loop to make sure all the things happen
1460 wait(0); // Trigger the event loop to make sure all the things happen
1456 }
1461 }
1457− tryCompare(appRepeater, "highlightedIndex", 6)
1462+ tryCompare(spreadRepeater, "highlightedIndex", 6)
1458
1463
1459 // Now release it once, and verify that it does wrap around with an additional Tab press
1464 // Now release it once, and verify that it does wrap around with an additional Tab press
1460 keyRelease(Qt.Key_Tab);
1465 keyRelease(Qt.Key_Tab);
1461 keyClick(Qt.Key_Tab);
1466 keyClick(Qt.Key_Tab);
1462− tryCompare(appRepeater, "highlightedIndex", 0)
1467+ tryCompare(spreadRepeater, "highlightedIndex", 0)
1463
1468
1464 // Release control, check if spread disappears
1469 // Release control, check if spread disappears
1465 keyRelease(Qt.Key_Control)
1470 keyRelease(Qt.Key_Control)
1466− tryCompare(desktopStage, "state", "")
1471+ tryCompare(desktopSpread, "state", "")
1467
1472
1468 // Make sure that after wrapping around once, we have the same one focused as at the beginning
1473 // Make sure that after wrapping around once, we have the same one focused as at the beginning
1469 tryCompare(focused.session.surface, "activeFocus", true)
1474 tryCompare(focused.session.surface, "activeFocus", true)
@@ -1472,30 +1477,30 @@ Rectangle {
1472 function test_altBackTabNavigation() {
1477 function test_altBackTabNavigation() {
1473 loadDesktopShellWithApps();
1478 loadDesktopShellWithApps();
1474
1479
1475− var appRepeater = findInvisibleChild(shell, "appRepeater");
1480+ var spreadRepeater = findInvisibleChild(shell, "spreadRepeater");
1476− verify(appRepeater !== null);
1481+ verify(spreadRepeater !== null);
1477
1482
1478 keyPress(Qt.Key_Control)
1483 keyPress(Qt.Key_Control)
1479 keyClick(Qt.Key_Tab);
1484 keyClick(Qt.Key_Tab);
1480− tryCompare(appRepeater, "highlightedIndex", 1);
1485+ tryCompare(spreadRepeater, "highlightedIndex", 1);
1481
1486
1482 keyClick(Qt.Key_Tab);
1487 keyClick(Qt.Key_Tab);
1483− tryCompare(appRepeater, "highlightedIndex", 2);
1488+ tryCompare(spreadRepeater, "highlightedIndex", 2);
1484
1489
1485 keyClick(Qt.Key_Tab);
1490 keyClick(Qt.Key_Tab);
1486− tryCompare(appRepeater, "highlightedIndex", 3);
1491+ tryCompare(spreadRepeater, "highlightedIndex", 3);
1487
1492
1488 keyClick(Qt.Key_Tab);
1493 keyClick(Qt.Key_Tab);
1489− tryCompare(appRepeater, "highlightedIndex", 4);
1494+ tryCompare(spreadRepeater, "highlightedIndex", 4);
1490
1495
1491 keyClick(Qt.Key_Backtab);
1496 keyClick(Qt.Key_Backtab);
1492− tryCompare(appRepeater, "highlightedIndex", 3);
1497+ tryCompare(spreadRepeater, "highlightedIndex", 3);
1493
1498
1494 keyClick(Qt.Key_Backtab);
1499 keyClick(Qt.Key_Backtab);
1495− tryCompare(appRepeater, "highlightedIndex", 2);
1500+ tryCompare(spreadRepeater, "highlightedIndex", 2);
1496
1501
1497 keyClick(Qt.Key_Backtab);
1502 keyClick(Qt.Key_Backtab);
1498− tryCompare(appRepeater, "highlightedIndex", 1);
1503+ tryCompare(spreadRepeater, "highlightedIndex", 1);
1499
1504
1500 keyRelease(Qt.Key_Control);
1505 keyRelease(Qt.Key_Control);
1501 }
1506 }
@@ -1503,20 +1508,20 @@ Rectangle {
1503 function test_highlightFollowsMouse() {
1508 function test_highlightFollowsMouse() {
1504 loadDesktopShellWithApps()
1509 loadDesktopShellWithApps()
1505
1510
1506− var appRepeater = findInvisibleChild(shell, "appRepeater");
1511+ var spreadRepeater = findInvisibleChild(shell, "spreadRepeater");
1507− verify(appRepeater !== null);
1512+ verify(spreadRepeater !== null);
1508
1513
1509 keyPress(Qt.Key_Control)
1514 keyPress(Qt.Key_Control)
1510 keyClick(Qt.Key_Tab);
1515 keyClick(Qt.Key_Tab);
1511
1516
1512− tryCompare(appRepeater, "highlightedIndex", 1);
1517+ tryCompare(spreadRepeater, "highlightedIndex", 1);
1513
1518
1514 var x = 0;
1519 var x = 0;
1515 var y = shell.height * .75;
1520 var y = shell.height * .75;
1516 mouseMove(shell, x, y)
1521 mouseMove(shell, x, y)
1517
1522
1518 for (var i = 0; i < 7; i++) {
1523 for (var i = 0; i < 7; i++) {
1519− while (appRepeater.highlightedIndex != i && x <= 4000) {
1524+ while (spreadRepeater.highlightedIndex != i && x <= 4000) {
1520 x+=10;
1525 x+=10;
1521 mouseMove(shell, x, y)
1526 mouseMove(shell, x, y)
1522 wait(0); // spin the loop so bindings get evaluated
1527 wait(0); // spin the loop so bindings get evaluated
@@ -1531,8 +1536,8 @@ Rectangle {
1531 function test_closeFromSpread() {
1536 function test_closeFromSpread() {
1532 loadDesktopShellWithApps()
1537 loadDesktopShellWithApps()
1533
1538
1534− var appRepeater = findInvisibleChild(shell, "appRepeater");
1539+ var spreadRepeater = findInvisibleChild(shell, "spreadRepeater");
1535− verify(appRepeater !== null);
1540+ verify(spreadRepeater !== null);
1536
1541
1537 keyPress(Qt.Key_Control)
1542 keyPress(Qt.Key_Control)
1538 keyClick(Qt.Key_Tab);
1543 keyClick(Qt.Key_Tab);
@@ -1540,14 +1545,14 @@ Rectangle {
1540 appRemovedSpy.clear();
1545 appRemovedSpy.clear();
1541
1546
1542 var closedAppId = ApplicationManager.get(2).appId;
1547 var closedAppId = ApplicationManager.get(2).appId;
1543− var appDelegate2 = appRepeater.itemAt(2);
1548+ var spreadDelegate2 = spreadRepeater.itemAt(2);
1544− var closeMouseArea = findChild(appDelegate2, "closeMouseArea");
1549+ var closeMouseArea = findChild(spreadDelegate2, "closeMouseArea");
1545
1550
1546 // Move the mosue over tile 2 and verify the close button becomes visible
1551 // Move the mosue over tile 2 and verify the close button becomes visible
1547 var x = 0;
1552 var x = 0;
1548 var y = shell.height * .5;
1553 var y = shell.height * .5;
1549 mouseMove(shell, x, y)
1554 mouseMove(shell, x, y)
1550− while (appRepeater.highlightedIndex !== 2 && x <= 4000) {
1555+ while (spreadRepeater.highlightedIndex !== 2 && x <= 4000) {
1551 x+=10;
1556 x+=10;
1552 mouseMove(shell, x, y)
1557 mouseMove(shell, x, y)
1553 wait(0); // spin the loop so bindings get evaluated
1558 wait(0); // spin the loop so bindings get evaluated
@@ -1574,32 +1579,35 @@ Rectangle {
1574 function test_selectFromSpreadWithMouse(data) {
1579 function test_selectFromSpreadWithMouse(data) {
1575 loadDesktopShellWithApps()
1580 loadDesktopShellWithApps()
1576
1581
1582+ var stage = findChild(shell, "stage");
1583+ var spread = findChild(stage, "spread");
1584+ waitForRendering(spread)
1585+
1577− var appRepeater = findInvisibleChild(shell, "appRepeater");
1586+ var spreadRepeater = findInvisibleChild(shell, "spreadRepeater");
1578− verify(appRepeater !== null);
1587+ verify(spreadRepeater !== null);
1579
1588
1580 keyPress(Qt.Key_Control)
1589 keyPress(Qt.Key_Control)
1581 keyClick(Qt.Key_Tab);
1590 keyClick(Qt.Key_Tab);
1582
1591
1583 var focusAppId = ApplicationManager.get(2).appId;
1592 var focusAppId = ApplicationManager.get(2).appId;
1584− var appDelegate2 = appRepeater.itemAt(2);
1593+ var spreadDelegate2 = spreadRepeater.itemAt(2);
1585− var decoratedWindow = findChild(appDelegate2, "decoratedWindow");
1594+ var clippedSpreadDelegate = findChild(spreadDelegate2, "clippedSpreadDelegate");
1586− var stage = findChild(shell, "stage");
1587
1595
1588− tryCompare(stage, "state", "altTab");
1596+ tryCompare(spread, "state", "altTab");
1589
1597
1590 // Move the mouse over tile 2 and verify the highlight becomes visible
1598 // Move the mouse over tile 2 and verify the highlight becomes visible
1591 var x = 0;
1599 var x = 0;
1592 var y = shell.height * (data.tileInfo ? .95 : 0.5)
1600 var y = shell.height * (data.tileInfo ? .95 : 0.5)
1593 mouseMove(shell, x, y)
1601 mouseMove(shell, x, y)
1594− while (appRepeater.highlightedIndex !== 2 && x <= 4000) {
1602+ while (spreadRepeater.highlightedIndex !== 2 && x <= 4000) {
1595 x+=10;
1603 x+=10;
1596 mouseMove(shell, x, y)
1604 mouseMove(shell, x, y)
1597 wait(0); // spin the loop so bindings get evaluated
1605 wait(0); // spin the loop so bindings get evaluated
1598 }
1606 }
1599− tryCompare(decoratedWindow, "highlightShown", true);
1607+ tryCompare(clippedSpreadDelegate, "highlightShown", true);
1600
1608
1601 // Click the tile
1609 // Click the tile
1602− mouseClick(decoratedWindow, decoratedWindow.width / 2, decoratedWindow.height / 2)
1610+ mouseClick(clippedSpreadDelegate, clippedSpreadDelegate.width / 2, clippedSpreadDelegate.height / 2)
1603
1611
1604 // Verify that we left the spread and app2 is the focused one now
1612 // Verify that we left the spread and app2 is the focused one now
1605 tryCompare(stage, "state", "");
1613 tryCompare(stage, "state", "");
@@ -1618,7 +1626,6 @@ Rectangle {
1618 keyClick(Qt.Key_Tab);
1626 keyClick(Qt.Key_Tab);
1619
1627
1620 var spreadFlickable = findChild(shell, "spreadFlickable")
1628 var spreadFlickable = findChild(shell, "spreadFlickable")
1621− print("have flickable", spreadFlickable, spreadFlickable.contentX)
1622
1629
1623 compare(spreadFlickable.contentX, 0);
1630 compare(spreadFlickable.contentX, 0);
1624
1631
@@ -1643,5 +1650,86 @@ Rectangle {
1643
1650
1644 keyRelease(Qt.Key_Control);
1651 keyRelease(Qt.Key_Control);
1645 }
1652 }
1653+
1654+ // This makes sure the hoverMouseArea is set to invisible AND disabled
1655+ // when not needed. Otherwise it'll eat mouse hover events for the rest of the shell/apps
1656+ function test_hoverMouseAreaDisabledAndInvisible() {
1657+ loadDesktopShellWithApps()
1658+
1659+ var hoverMouseArea = findChild(shell, "hoverMouseArea");
1660+ tryCompare(hoverMouseArea, "enabled", false)
1661+ tryCompare(hoverMouseArea, "visible", false)
1662+
1663+ keyPress(Qt.Key_Control)
1664+ keyClick(Qt.Key_Tab);
1665+
1666+ tryCompare(hoverMouseArea, "enabled", true)
1667+ tryCompare(hoverMouseArea, "visible", true)
1668+
1669+ keyRelease(Qt.Key_Control)
1670+
1671+ tryCompare(hoverMouseArea, "enabled", false)
1672+ tryCompare(hoverMouseArea, "visible", false)
1673+ }
1674+
1675+ function test_workspacePreviewsHighlightedApp() {
1676+ loadDesktopShellWithApps()
1677+
1678+ var targetZ = ApplicationManager.count + 1;
1679+
1680+ var spreadRepeater = findInvisibleChild(shell, "spreadRepeater");
1681+ verify(spreadRepeater !== null);
1682+
1683+ var appRepeater = findInvisibleChild(shell, "appRepeater");
1684+ verify(appRepeater !== null);
1685+
1686+ keyPress(Qt.Key_Control)
1687+ keyClick(Qt.Key_Tab);
1688+
1689+ tryCompare(spreadRepeater, "highlightedIndex", 1);
1690+ tryCompare(appRepeater.itemAt(1), "z", targetZ)
1691+
1692+ var x = 0;
1693+ var y = shell.height * .75;
1694+ mouseMove(shell, x, y)
1695+
1696+ for (var i = 0; i < 7; i++) {
1697+ while (spreadRepeater.highlightedIndex != i && x <= 4000) {
1698+ tryCompare(appRepeater.itemAt(spreadRepeater.highlightedIndex), "z", targetZ)
1699+ x+=10;
1700+ mouseMove(shell, x, y)
1701+ wait(0); // spin the loop so bindings get evaluated
1702+ }
1703+ }
1704+
1705+ verify(y < 4000);
1706+
1707+ keyRelease(Qt.Key_Control);
1708+ }
1709+
1710+ function test_focusAppFromLauncherExitsSpread() {
1711+ loadDesktopShellWithApps()
1712+
1713+ var desktopSpread = findChild(shell, "spread");
1714+ var launcher = findChild(shell, "launcher");
1715+ var bfb = findChild(launcher, "buttonShowDashHome");
1716+
1717+ keyPress(Qt.Key_Control)
1718+ keyClick(Qt.Key_Tab);
1719+
1720+
1721+ tryCompare(desktopSpread, "state", "altTab")
1722+
1723+ mouseMove(shell, 0, 0);
1724+ tryCompare(launcher, "state", "visibleTemporary")
1725+
1726+ mouseClick(bfb, bfb.width / 2, bfb.height / 2)
1727+ tryCompare(launcher, "state", "")
1728+ tryCompare(desktopSpread, "state", "")
1729+
1730+ tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash")
1731+
1732+ keyRelease(Qt.Key_Control);
1733+ }
1646 }
1734 }
1647}
1735}