on xenial help sign in
Restore windows when activating from the spread, maintain a focus stack
d3df971Lukáš Tinkl
qml/Panel/Panel.qml+18−1
@@ -137,7 +137,7 @@ Item {
137 }
137 }
138
138
139 shown: false
139 shown: false
140− width: root.width - (windowControlButtons.visible ? windowControlButtons.width : 0)
140+ width: root.width - (windowControlButtons.visible ? windowControlButtons.width + titleLabel.width : 0)
141 minimizedPanelHeight: units.gu(3)
141 minimizedPanelHeight: units.gu(3)
142 expandedPanelHeight: units.gu(7)
142 expandedPanelHeight: units.gu(7)
143 openedHeight: root.height - indicatorOrangeLine.height
143 openedHeight: root.height - indicatorOrangeLine.height
@@ -178,6 +178,23 @@ Item {
178 onMaximize: PanelState.maximize()
178 onMaximize: PanelState.maximize()
179 }
179 }
180
180
181+ Label {
182+ id: titleLabel
183+ objectName: "windowDecorationTitle"
184+ anchors {
185+ left: windowControlButtons.right
186+ top: parent.top
187+ margins: units.gu(0.7)
188+ }
189+ color: "#DFDBD2"
190+ height: windowControlButtons.height
191+ visible: windowControlButtons.visible
192+ verticalAlignment: Text.AlignVCenter
193+ fontSize: "small"
194+ font.bold: true
195+ text: PanelState.title
196+ }
197+
181 PanelSeparatorLine {
198 PanelSeparatorLine {
182 id: indicatorOrangeLine
199 id: indicatorOrangeLine
183 anchors {
200 anchors {
qml/Stages/DecoratedWindow.qml+3−1
@@ -26,6 +26,7 @@ FocusScope {
26 property alias window: applicationWindow
26 property alias window: applicationWindow
27 property alias application: applicationWindow.application
27 property alias application: applicationWindow.application
28 property alias active: decoration.active
28 property alias active: decoration.active
29+ property alias title: decoration.title
29
30
30 property bool decorationShown: true
31 property bool decorationShown: true
31 property bool highlightShown: false
32 property bool highlightShown: false
@@ -66,7 +67,8 @@ FocusScope {
66 objectName: application ? "appWindowDecoration_" + application.appId : "appWindowDecoration_null"
67 objectName: application ? "appWindowDecoration_" + application.appId : "appWindowDecoration_null"
67 anchors { left: parent.left; top: parent.top; right: parent.right }
68 anchors { left: parent.left; top: parent.top; right: parent.right }
68 height: units.gu(3)
69 height: units.gu(3)
70+ width: root.width
69− title: window.title !== "" ? window.title : model.name
71+ title: window.title !== "" ? window.title : ""
70 onClose: root.close();
72 onClose: root.close();
71 onMaximize: root.maximize();
73 onMaximize: root.maximize();
72 onMinimize: root.minimize();
74 onMinimize: root.minimize();
qml/Stages/DesktopSpread.qml+1−1
@@ -87,7 +87,7 @@ FocusScope {
87 function focusSelected() {
87 function focusSelected() {
88 if (spreadRepeater.highlightedIndex != -1) {
88 if (spreadRepeater.highlightedIndex != -1) {
89 var application = ApplicationManager.get(spreadRepeater.highlightedIndex);
89 var application = ApplicationManager.get(spreadRepeater.highlightedIndex);
90− ApplicationManager.focusApplication(application.appId);
90+ ApplicationManager.requestFocusApplication(application.appId);
91 }
91 }
92 }
92 }
93
93
qml/Stages/DesktopSpreadDelegate.qml+2−2
@@ -29,8 +29,8 @@ Item {
29 property bool highlightShown: false
29 property bool highlightShown: false
30 property real shadowOpacity: 1
30 property real shadowOpacity: 1
31
31
32− property int windowWidth: application.session && application.session.surface ? application.session.surface.size.width : 0
32+ property int windowWidth: application && 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
33+ property int windowHeight: application && application.session && application.session.surface ? application.session.surface.size.height : 0
34
34
35 state: "normal"
35 state: "normal"
36 states: [
36 states: [
qml/Stages/DesktopStage.qml+147−33
@@ -20,11 +20,11 @@ import QtQuick 2.4
20import QtQuick.Layouts 1.1
20import QtQuick.Layouts 1.1
21import Ubuntu.Components 1.3
21import Ubuntu.Components 1.3
22import Unity.Application 0.1
22import Unity.Application 0.1
23−import "../Components"
24import "../Components/PanelState"
23import "../Components/PanelState"
25import "../Components"
24import "../Components"
26import Utils 0.1
25import Utils 0.1
27import Ubuntu.Gestures 0.1
26import Ubuntu.Gestures 0.1
27+import GlobalShortcut 1.0
28
28
29AbstractStage {
29AbstractStage {
30 id: root
30 id: root
@@ -45,21 +45,72 @@ AbstractStage {
45 spread.state = "";
45 spread.state = "";
46 }
46 }
47
47
48− ApplicationManager.requestFocusApplication(appId)
48+ ApplicationManager.focusApplication(appId);
49+ }
50+
51+ onApplicationRemoved: {
52+ priv.focusNext();
49 }
53 }
50
54
51 onFocusRequested: {
55 onFocusRequested: {
52 var appIndex = priv.indexOf(appId);
56 var appIndex = priv.indexOf(appId);
53 var appDelegate = appRepeater.itemAt(appIndex);
57 var appDelegate = appRepeater.itemAt(appIndex);
54− appDelegate.minimized = false;
58+ appDelegate.restoreFromMinimized();
55− ApplicationManager.focusApplication(appId)
56
59
57 if (spread.state == "altTab") {
60 if (spread.state == "altTab") {
58− spread.cancel()
61+ spread.cancel();
59 }
62 }
60 }
63 }
61 }
64 }
62
65
66+ GlobalShortcut {
67+ id: closeWindowShortcut
68+ shortcut: Qt.AltModifier|Qt.Key_F4
69+ onTriggered: ApplicationManager.stopApplication(priv.focusedAppId)
70+ active: priv.focusedAppId !== ""
71+ }
72+
73+ GlobalShortcut {
74+ id: showSpreadShortcut
75+ shortcut: Qt.MetaModifier|Qt.Key_W
76+ onTriggered: spread.state = "altTab"
77+ }
78+
79+ GlobalShortcut {
80+ id: minimizeAllShortcut
81+ shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_D
82+ onTriggered: priv.minimizeAllWindows()
83+ }
84+
85+ GlobalShortcut {
86+ id: maximizeWindowShortcut
87+ shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Up
88+ onTriggered: priv.focusedAppDelegate.maximize()
89+ active: priv.focusedAppDelegate !== null
90+ }
91+
92+ GlobalShortcut {
93+ id: maximizeWindowLeftShortcut
94+ shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Left
95+ onTriggered: priv.focusedAppDelegate.maximizeLeft()
96+ active: priv.focusedAppDelegate !== null
97+ }
98+
99+ GlobalShortcut {
100+ id: maximizeWindowRightShortcut
101+ shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Right
102+ onTriggered: priv.focusedAppDelegate.maximizeRight()
103+ active: priv.focusedAppDelegate !== null
104+ }
105+
106+ GlobalShortcut {
107+ id: minimizeRestoreShortcut
108+ shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Down
109+ onTriggered: priv.focusedAppDelegate.maximized || priv.focusedAppDelegate.maximizedLeft || priv.focusedAppDelegate.maximizedRight
110+ ? priv.focusedAppDelegate.restore() : priv.focusedAppDelegate.minimize(true)
111+ active: priv.focusedAppDelegate !== null
112+ }
113+
63 QtObject {
114 QtObject {
64 id: priv
115 id: priv
65
116
@@ -93,6 +144,28 @@ AbstractStage {
93 }
144 }
94 return -1;
145 return -1;
95 }
146 }
147+
148+ function minimizeAllWindows() {
149+ for (var i = 0; i < appRepeater.count; i++) {
150+ var appDelegate = appRepeater.itemAt(i);
151+ if (appDelegate && !appDelegate.minimized) {
152+ appDelegate.minimize(false); // minimize but don't switch focus
153+ }
154+ }
155+
156+ ApplicationManager.unfocusCurrentApplication(); // no app should have focus at this point
157+ }
158+
159+ function focusNext() {
160+ ApplicationManager.unfocusCurrentApplication();
161+ for (var i = 0; i < appRepeater.count; i++) {
162+ var appDelegate = appRepeater.itemAt(i);
163+ if (appDelegate && !appDelegate.minimized) {
164+ ApplicationManager.focusApplication(appDelegate.appId);
165+ return;
166+ }
167+ }
168+ }
96 }
169 }
97
170
98 Connections {
171 Connections {
@@ -100,15 +173,23 @@ AbstractStage {
100 onClose: {
173 onClose: {
101 ApplicationManager.stopApplication(ApplicationManager.focusedApplicationId)
174 ApplicationManager.stopApplication(ApplicationManager.focusedApplicationId)
102 }
175 }
103− onMinimize: appRepeater.itemAt(0).minimize();
176+ onMinimize: appRepeater.itemAt(0).minimize(true);
104− onMaximize: appRepeater.itemAt(0).unmaximize();
177+ onMaximize: appRepeater.itemAt(0).restore();
105 }
178 }
106
179
107 Binding {
180 Binding {
108 target: PanelState
181 target: PanelState
109 property: "buttonsVisible"
182 property: "buttonsVisible"
110− value: priv.focusedAppDelegate !== null && priv.focusedAppDelegate.state === "maximized"
183+ value: priv.focusedAppDelegate !== null && priv.focusedAppDelegate.maximized
111 }
184 }
185+
186+ Binding {
187+ target: PanelState
188+ property: "title"
189+ value: priv.focusedAppDelegate !== null && priv.focusedAppDelegate.title
190+ when: priv.focusedAppDelegate && priv.focusedAppDelegate.maximized
191+ }
192+
112 Component.onDestruction: PanelState.buttonsVisible = false;
193 Component.onDestruction: PanelState.buttonsVisible = false;
113
194
114 FocusScope {
195 FocusScope {
@@ -135,23 +216,26 @@ AbstractStage {
135
216
136 delegate: FocusScope {
217 delegate: FocusScope {
137 id: appDelegate
218 id: appDelegate
138− objectName: "stageDelegate_" + model.appId
219+ objectName: "appDelegate_" + appId
139 z: ApplicationManager.count - index
220 z: ApplicationManager.count - index
140 y: units.gu(3)
221 y: units.gu(3)
141 width: units.gu(60)
222 width: units.gu(60)
142 height: units.gu(50)
223 height: units.gu(50)
143− focus: model.appId === priv.focusedAppId
224+ focus: appId === priv.focusedAppId
144
225
145 property bool maximized: false
226 property bool maximized: false
227+ property bool maximizedLeft: false
228+ property bool maximizedRight: false
146 property bool minimized: false
229 property bool minimized: false
230+ readonly property string appId: model.appId
147 property bool animationsEnabled: true
231 property bool animationsEnabled: true
148−
232+ property alias title: decoratedWindow.title
149 property bool visuallyMaximized: false
233 property bool visuallyMaximized: false
150 property bool visuallyMinimized: false
234 property bool visuallyMinimized: false
151
235
152 onFocusChanged: {
236 onFocusChanged: {
153− if (focus && ApplicationManager.focusedApplicationId !== model.appId) {
237+ if (focus && ApplicationManager.focusedApplicationId !== appId) {
154− ApplicationManager.focusApplication(model.appId);
238+ ApplicationManager.focusApplication(appId);
155 }
239 }
156 }
240 }
157
241
@@ -163,8 +247,6 @@ AbstractStage {
163 (priv.foregroundMaximizedAppIdIndex === -1 || priv.foregroundMaximizedAppIdIndex >= index) ||
247 (priv.foregroundMaximizedAppIdIndex === -1 || priv.foregroundMaximizedAppIdIndex >= index) ||
164 (spread.state == "altTab" && index === spread.highlightedIndex)
248 (spread.state == "altTab" && index === spread.highlightedIndex)
165
249
166− onVisibleChanged: console.log("VISIBLE", model.appId, visible)
167−
168 Binding {
250 Binding {
169 target: ApplicationManager.get(index)
251 target: ApplicationManager.get(index)
170 property: "requestedState"
252 property: "requestedState"
@@ -180,22 +262,50 @@ AbstractStage {
180 animationsEnabled = (animated === undefined) || animated;
262 animationsEnabled = (animated === undefined) || animated;
181 minimized = false;
263 minimized = false;
182 maximized = true;
264 maximized = true;
265+ maximizedLeft = false;
266+ maximizedRight = false;
267+ }
268+ function maximizeLeft() {
269+ minimized = false;
270+ maximized = false;
271+ maximizedLeft = true;
272+ maximizedRight = false;
273+ }
274+ function maximizeRight() {
275+ minimized = false;
276+ maximized = false;
277+ maximizedLeft = false;
278+ maximizedRight = true;
183 }
279 }
184 function minimize(animated) {
280 function minimize(animated) {
185 animationsEnabled = (animated === undefined) || animated;
281 animationsEnabled = (animated === undefined) || animated;
186 maximized = false;
282 maximized = false;
187 minimized = true;
283 minimized = true;
188 }
284 }
189− function unmaximize(animated) {
285+ function restore(animated) {
190 animationsEnabled = (animated === undefined) || animated;
286 animationsEnabled = (animated === undefined) || animated;
191 minimized = false;
287 minimized = false;
192 maximized = false;
288 maximized = false;
289+ maximizedLeft = false;
290+ maximizedRight = false;
291+ }
292+ function restoreFromMinimized(animated) {
293+ animationsEnabled = (animated === undefined) || animated;
294+ minimized = false;
295+ if (maximized)
296+ maximize();
297+ else if (maximizedLeft)
298+ maximizeLeft();
299+ else if (maximizedRight)
300+ maximizeRight();
301+ ApplicationManager.focusApplication(appId);
193 }
302 }
194
303
195 states: [
304 states: [
196 State {
305 State {
197 name: "normal";
306 name: "normal";
198 when: !appDelegate.maximized && !appDelegate.minimized
307 when: !appDelegate.maximized && !appDelegate.minimized
308+ && !appDelegate.maximizedLeft && !appDelegate.maximizedRight
199 PropertyChanges {
309 PropertyChanges {
200 target: appDelegate;
310 target: appDelegate;
201 visuallyMinimized: false;
311 visuallyMinimized: false;
@@ -212,6 +322,14 @@ AbstractStage {
212 visuallyMaximized: true
322 visuallyMaximized: true
213 }
323 }
214 },
324 },
325+ State {
326+ name: "maximizedLeft"; when: appDelegate.maximizedLeft && !appDelegate.minimized
327+ PropertyChanges { target: appDelegate; x: 0; y: units.gu(3); width: root.width/2; height: root.height - units.gu(3) }
328+ },
329+ State {
330+ name: "maximizedRight"; when: appDelegate.maximizedRight && !appDelegate.minimized
331+ PropertyChanges { target: appDelegate; x: root.width/2; y: units.gu(3); width: root.width/2; height: root.height - units.gu(3) }
332+ },
215 State {
333 State {
216 name: "minimized"; when: appDelegate.minimized
334 name: "minimized"; when: appDelegate.minimized
217 PropertyChanges {
335 PropertyChanges {
@@ -229,14 +347,14 @@ AbstractStage {
229 to: "normal"
347 to: "normal"
230 enabled: appDelegate.animationsEnabled
348 enabled: appDelegate.animationsEnabled
231 PropertyAction { target: appDelegate; properties: "visuallyMinimized,visuallyMaximized" }
349 PropertyAction { target: appDelegate; properties: "visuallyMinimized,visuallyMaximized" }
232− PropertyAnimation { target: appDelegate; properties: "x,y,opacity,width,height,scale,opacity" }
350+ PropertyAnimation { target: appDelegate; properties: "x,y,opacity,width,height,scale" }
233 },
351 },
234 Transition {
352 Transition {
235 to: "maximized"
353 to: "maximized"
236 enabled: appDelegate.animationsEnabled
354 enabled: appDelegate.animationsEnabled
237 PropertyAction { target: appDelegate; property: "visuallyMinimized" }
355 PropertyAction { target: appDelegate; property: "visuallyMinimized" }
238 SequentialAnimation {
356 SequentialAnimation {
239− PropertyAnimation { target: appDelegate; properties: "x,y,opacity,width,height,scale,opacity" }
357+ PropertyAnimation { target: appDelegate; properties: "x,y,opacity,width,height,scale" }
240 PropertyAction { target: appDelegate; property: "visuallyMaximized" }
358 PropertyAction { target: appDelegate; property: "visuallyMaximized" }
241 }
359 }
242 },
360 },
@@ -245,20 +363,15 @@ AbstractStage {
245 enabled: appDelegate.animationsEnabled
363 enabled: appDelegate.animationsEnabled
246 PropertyAction { target: appDelegate; property: "visuallyMaximized" }
364 PropertyAction { target: appDelegate; property: "visuallyMaximized" }
247 SequentialAnimation {
365 SequentialAnimation {
248− PropertyAnimation { target: appDelegate; properties: "x,y,opacity,width,height,scale,opacity" }
366+ PropertyAnimation { target: appDelegate; properties: "x,y,opacity,width,height,scale" }
249 PropertyAction { target: appDelegate; property: "visuallyMinimized" }
367 PropertyAction { target: appDelegate; property: "visuallyMinimized" }
250− }
368+ ScriptAction {
251− },
369+ script: {
252− Transition {
370+ if (appDelegate.animationsEnabled && state === "minimized" ) {
253− from: ""
371+ priv.focusNext();
254− to: "altTab"
372+ }
255− PropertyAction { target: appDelegate; properties: "y,angle,z,itemScale,itemScaleOriginY" }
373+ }
256− PropertyAction { target: decoratedWindow; properties: "anchors.topMargin" }
374+ }
257− PropertyAnimation {
258− target: appDelegate; properties: "x"
259− from: root.width
260− duration: rightEdgePushArea.containsMouse ? UbuntuAnimation.FastDuration :0
261− easing: UbuntuAnimation.StandardEasing
262 }
375 }
263 }
376 }
264 ]
377 ]
@@ -294,8 +407,9 @@ AbstractStage {
294 focus: true
407 focus: true
295
408
296 onClose: ApplicationManager.stopApplication(model.appId)
409 onClose: ApplicationManager.stopApplication(model.appId)
410+ onMaximize: appDelegate.maximized || appDelegate.maximizedLeft || appDelegate.maximizedRight
297− onMaximize: appDelegate.maximized ? appDelegate.unmaximize() : appDelegate.maximize()
411+ ? appDelegate.restore() : appDelegate.maximize()
298− onMinimize: appDelegate.minimize()
412+ onMinimize: appDelegate.minimize(true)
299 onDecorationPressed: { ApplicationManager.focusApplication(model.appId) }
413 onDecorationPressed: { ApplicationManager.focusApplication(model.appId) }
300 }
414 }
301 }
415 }
qml/Stages/WindowDecoration.qml+6−2
@@ -45,14 +45,15 @@ MouseArea {
45 priv.distanceX = pos.x;
45 priv.distanceX = pos.x;
46 priv.distanceY = pos.y;
46 priv.distanceY = pos.y;
47 priv.dragging = true;
47 priv.dragging = true;
48− Mir.cursorName = "grabbing";
49 } else {
48 } else {
50 priv.dragging = false;
49 priv.dragging = false;
51 Mir.cursorName = "";
50 Mir.cursorName = "";
52 }
51 }
53 }
52 }
53+
54 onPositionChanged: {
54 onPositionChanged: {
55 if (priv.dragging) {
55 if (priv.dragging) {
56+ Mir.cursorName = "grabbing";
56 var pos = mapToItem(root.target.parent, mouseX, mouseY);
57 var pos = mapToItem(root.target.parent, mouseX, mouseY);
57 root.target.x = pos.x - priv.distanceX;
58 root.target.x = pos.x - priv.distanceX;
58 root.target.y = pos.y - priv.distanceY;
59 root.target.y = pos.y - priv.distanceY;
@@ -70,11 +71,12 @@ MouseArea {
70 }
71 }
71
72
72 Row {
73 Row {
73− anchors { left: parent.left; top: parent.top; bottom: parent.bottom; margins: units.gu(0.7) }
74+ anchors { fill: parent; margins: units.gu(0.7) }
74 spacing: units.gu(1)
75 spacing: units.gu(1)
75 opacity: root.active ? 1 : 0.5
76 opacity: root.active ? 1 : 0.5
76
77
77 WindowControlButtons {
78 WindowControlButtons {
79+ id: buttons
78 height: parent.height
80 height: parent.height
79 onClose: root.close();
81 onClose: root.close();
80 onMinimize: root.minimize();
82 onMinimize: root.minimize();
@@ -86,9 +88,11 @@ MouseArea {
86 objectName: "windowDecorationTitle"
88 objectName: "windowDecorationTitle"
87 color: "#DFDBD2"
89 color: "#DFDBD2"
88 height: parent.height
90 height: parent.height
91+ width: parent.width - buttons.width - parent.anchors.rightMargin - parent.anchors.leftMargin
89 verticalAlignment: Text.AlignVCenter
92 verticalAlignment: Text.AlignVCenter
90 fontSize: "small"
93 fontSize: "small"
91 font.bold: true
94 font.bold: true
95+ elide: Text.ElideRight
92 }
96 }
93 }
97 }
94}
98}
tests/qmltests/Stages/tst_DesktopStage.qml+139−32
@@ -60,14 +60,10 @@ Item {
60
60
61 focus: true
61 focus: true
62
62
63− property bool itemDestroyed: false
64 sourceComponent: Component {
63 sourceComponent: Component {
65 DesktopStage {
64 DesktopStage {
66 color: "darkblue"
65 color: "darkblue"
67 anchors.fill: parent
66 anchors.fill: parent
68− Component.onDestruction: {
69− desktopStageLoader.itemDestroyed = true;
70− }
71 orientations: Orientations {}
67 orientations: Orientations {}
72 }
68 }
73 }
69 }
@@ -103,16 +99,10 @@ Item {
103 property Item desktopStage: desktopStageLoader.status === Loader.Ready ? desktopStageLoader.item : null
99 property Item desktopStage: desktopStageLoader.status === Loader.Ready ? desktopStageLoader.item : null
104
100
105 function cleanup() {
101 function cleanup() {
106− desktopStageLoader.itemDestroyed = false;
107 desktopStageLoader.active = false;
102 desktopStageLoader.active = false;
108
103
109 tryCompare(desktopStageLoader, "status", Loader.Null);
104 tryCompare(desktopStageLoader, "status", Loader.Null);
110 tryCompare(desktopStageLoader, "item", null);
105 tryCompare(desktopStageLoader, "item", null);
111− // Loader.status might be Loader.Null and Loader.item might be null but the Loader
112− // actually took place. Likely because Loader waits until the next event loop
113− // iteration to do its work. So to ensure the reload, we will wait until the
114− // Shell instance gets destroyed.
115− tryCompare(desktopStageLoader, "itemDestroyed", true);
116
106
117 killAllRunningApps();
107 killAllRunningApps();
118
108
@@ -155,10 +145,7 @@ Item {
155 }
145 }
156
146
157 function test_appFocusSwitch(data) {
147 function test_appFocusSwitch(data) {
158− var i;
159− for (i = 0; i < data.apps.length; i++) {
160− startApplication(data.apps[i]);
148+ data.apps.forEach(startApplication);
161− }
162
149
163 ApplicationManager.requestFocusApplication(data.apps[data.focusfrom]);
150 ApplicationManager.requestFocusApplication(data.apps[data.focusfrom]);
164 tryCompare(ApplicationManager.findApplication(data.apps[data.focusfrom]).session.surface, "activeFocus", true);
151 tryCompare(ApplicationManager.findApplication(data.apps[data.focusfrom]).session.surface, "activeFocus", true);
@@ -175,10 +162,7 @@ Item {
175 }
162 }
176
163
177 function test_tappingOnWindowChangesFocusedApp(data) {
164 function test_tappingOnWindowChangesFocusedApp(data) {
178− var i;
179− for (i = 0; i < data.apps.length; i++) {
180− startApplication(data.apps[i]);
165+ data.apps.forEach(startApplication);
181− }
182 var fromAppId = data.apps[data.focusfrom];
166 var fromAppId = data.apps[data.focusfrom];
183 var toAppId = data.apps[data.focusTo]
167 var toAppId = data.apps[data.focusTo]
184
168
@@ -195,18 +179,37 @@ Item {
195 compare(ApplicationManager.focusedApplicationId, toAppId);
179 compare(ApplicationManager.focusedApplicationId, toAppId);
196 }
180 }
197
181
182+ function test_clickingOnWindowChangesFocusedApp_data() {
183+ return test_tappingOnWindowChangesFocusedApp_data(); // reuse test data
184+ }
185+
186+ function test_clickingOnWindowChangesFocusedApp(data) {
187+ data.apps.forEach(startApplication);
188+ var fromAppId = data.apps[data.focusfrom];
189+ var toAppId = data.apps[data.focusTo]
190+
191+ var fromAppWindow = findChild(desktopStage, "appWindow_" + fromAppId);
192+ verify(fromAppWindow);
193+ mouseClick(fromAppWindow);
194+ compare(fromAppWindow.application.session.surface.activeFocus, true);
195+ compare(ApplicationManager.focusedApplicationId, fromAppId);
196+
197+ var toAppWindow = findChild(desktopStage, "appWindow_" + toAppId);
198+ verify(toAppWindow);
199+ mouseClick(toAppWindow);
200+ compare(toAppWindow.application.session.surface.activeFocus, true);
201+ compare(ApplicationManager.focusedApplicationId, toAppId);
202+ }
203+
198 function test_tappingOnDecorationFocusesApplication_data() {
204 function test_tappingOnDecorationFocusesApplication_data() {
199 return [
205 return [
200− {tag: "dash", apps: [ "unity8-dash", "dialer-app", "camera-app" ], focusfrom: 0, focusTo: 1 },
206+ {tag: "dash to dialer", apps: [ "unity8-dash", "dialer-app", "camera-app" ], focusfrom: 0, focusTo: 1 },
201− {tag: "dash", apps: [ "unity8-dash", "dialer-app", "camera-app" ], focusfrom: 1, focusTo: 0 },
207+ {tag: "dialer to dash", apps: [ "unity8-dash", "dialer-app", "camera-app" ], focusfrom: 1, focusTo: 0 },
202 ]
208 ]
203 }
209 }
204
210
205 function test_tappingOnDecorationFocusesApplication(data) {
211 function test_tappingOnDecorationFocusesApplication(data) {
206− var i;
207− for (i = 0; i < data.apps.length; i++) {
208− startApplication(data.apps[i]);
212+ data.apps.forEach(startApplication);
209− }
210
213
211 var fromAppDecoration = findChild(desktopStage, "appWindowDecoration_" + data.apps[data.focusfrom]);
214 var fromAppDecoration = findChild(desktopStage, "appWindowDecoration_" + data.apps[data.focusfrom]);
212 verify(fromAppDecoration);
215 verify(fromAppDecoration);
@@ -219,10 +222,114 @@ Item {
219 tryCompare(ApplicationManager.findApplication(data.apps[data.focusTo]).session.surface, "activeFocus", true);
222 tryCompare(ApplicationManager.findApplication(data.apps[data.focusTo]).session.surface, "activeFocus", true);
220 }
223 }
221
224
225+ function test_clickingOnDecorationFocusesApplication_data() {
226+ return test_tappingOnDecorationFocusesApplication_data(); // reuse test data
227+ }
228+
229+ function test_clickingOnDecorationFocusesApplication(data) {
230+ data.apps.forEach(startApplication);
231+
232+ var fromAppDecoration = findChild(desktopStage, "appWindowDecoration_" + data.apps[data.focusfrom]);
233+ verify(fromAppDecoration);
234+ mouseClick(fromAppDecoration);
235+ tryCompare(ApplicationManager.findApplication(data.apps[data.focusfrom]).session.surface, "activeFocus", true);
236+
237+ var toAppDecoration = findChild(desktopStage, "appWindowDecoration_" + data.apps[data.focusTo]);
238+ verify(toAppDecoration);
239+ mouseClick(toAppDecoration);
240+ tryCompare(ApplicationManager.findApplication(data.apps[data.focusTo]).session.surface, "activeFocus", true);
241+ }
242+
243+ function test_windowMaximize() {
244+ var apps = ["unity8-dash", "dialer-app", "camera-app"];
245+ apps.forEach(startApplication);
246+ var appName = "dialer-app";
247+ var appDelegate = findChild(desktopStage, "appDelegate_" + appName);
248+ verify(appDelegate);
249+ ApplicationManager.focusApplication(appName);
250+ keyClick(Qt.Key_Up, Qt.MetaModifier|Qt.ControlModifier); // Ctrl+Super+Up shortcut to maximize
251+ tryCompare(appDelegate, "maximized", true);
252+ tryCompare(appDelegate, "minimized", false);
253+ }
254+
255+ function test_windowMaximizeLeft() {
256+ var apps = ["unity8-dash", "dialer-app", "camera-app"];
257+ apps.forEach(startApplication);
258+ var appName = "dialer-app";
259+ var appDelegate = findChild(desktopStage, "appDelegate_" + appName);
260+ verify(appDelegate);
261+ ApplicationManager.focusApplication(appName);
262+ keyClick(Qt.Key_Left, Qt.MetaModifier|Qt.ControlModifier); // Ctrl+Super+Left shortcut to maximizeLeft
263+ tryCompare(appDelegate, "maximized", false);
264+ tryCompare(appDelegate, "minimized", false);
265+ tryCompare(appDelegate, "maximizedLeft", true);
266+ tryCompare(appDelegate, "maximizedRight", false);
267+ }
268+
269+ function test_windowMaximizeRight() {
270+ var apps = ["unity8-dash", "dialer-app", "camera-app"];
271+ apps.forEach(startApplication);
272+ var appName = "dialer-app";
273+ var appDelegate = findChild(desktopStage, "appDelegate_" + appName);
274+ verify(appDelegate);
275+ ApplicationManager.focusApplication(appName);
276+ keyClick(Qt.Key_Right, Qt.MetaModifier|Qt.ControlModifier); // Ctrl+Super+Right shortcut to maximizeRight
277+ tryCompare(appDelegate, "maximized", false);
278+ tryCompare(appDelegate, "minimized", false);
279+ tryCompare(appDelegate, "maximizedLeft", false);
280+ tryCompare(appDelegate, "maximizedRight", true);
281+ }
282+
283+ function test_windowMinimize() {
284+ var apps = ["unity8-dash", "dialer-app", "camera-app"];
285+ apps.forEach(startApplication);
286+ var appName = "dialer-app";
287+ var appDelegate = findChild(desktopStage, "appDelegate_" + appName);
288+ verify(appDelegate);
289+ ApplicationManager.focusApplication(appName);
290+ keyClick(Qt.Key_Down, Qt.MetaModifier|Qt.ControlModifier); // Ctrl+Super+Down shortcut to minimize
291+ tryCompare(appDelegate, "maximized", false);
292+ tryCompare(appDelegate, "minimized", true);
293+ verify(ApplicationManager.focusedApplicationId != ""); // verify we don't lose focus when minimizing an app
294+ }
295+
296+ function test_windowMinimizeAll() {
297+ var apps = ["unity8-dash", "dialer-app", "camera-app"];
298+ apps.forEach(startApplication);
299+ verify(ApplicationManager.count == 3);
300+ keyClick(Qt.Key_D, Qt.MetaModifier|Qt.ControlModifier); // Ctrl+Super+D shortcut to minimize all
301+ tryCompare(ApplicationManager, "focusedApplicationId", ""); // verify no app is focused
302+ }
303+
304+ function test_windowClose() {
305+ var apps = ["unity8-dash", "dialer-app", "camera-app"];
306+ apps.forEach(startApplication);
307+ verify(ApplicationManager.count == 3);
308+ var appName = "dialer-app";
309+ var appDelegate = findChild(desktopStage, "appDelegate_" + appName);
310+ verify(appDelegate);
311+ ApplicationManager.focusApplication(appName);
312+ keyClick(Qt.Key_F4, Qt.AltModifier); // Alt+F4 shortcut to close
313+ verify(ApplicationManager.count == 2); // verify the app is gone
314+ verify(ApplicationManager.findApplication(appName) === null); // and it's not in running apps
315+ }
316+
317+ function test_smashCursorKeys() {
318+ var apps = ["unity8-dash", "dialer-app", "camera-app"];
319+ apps.forEach(startApplication);
320+ verify(ApplicationManager.count == 3);
321+ keyClick(Qt.Key_D, Qt.MetaModifier|Qt.ControlModifier); // Ctrl+Super+D shortcut to minimize all
322+ tryCompare(ApplicationManager, "focusedApplicationId", ""); // verify no app is focused
323+
324+ // now try pressing all 4 arrow keys + ctrl + meta
325+ keyClick(Qt.Key_Up | Qt.Key_Down | Qt.Key_Left | Qt.Key_Right, Qt.MetaModifier|Qt.ControlModifier); // smash it!!!
326+ tryCompare(ApplicationManager, "focusedApplicationId", ""); // verify still no app is focused
327+ }
328+
222 function test_minimizeApplicationHidesSurface() {
329 function test_minimizeApplicationHidesSurface() {
223 var dashApp = startApplication("unity8-dash");
330 var dashApp = startApplication("unity8-dash");
224
331
225− var dashDelegate = findChild(desktopStage, "stageDelegate_unity8-dash");
332+ var dashDelegate = findChild(desktopStage, "appDelegate_unity8-dash");
226 verify(dashDelegate);
333 verify(dashDelegate);
227
334
228 findChild(dashDelegate, "decoratedWindow").minimize();
335 findChild(dashDelegate, "decoratedWindow").minimize();
@@ -234,11 +341,11 @@ Item {
234 var dialerApp = startApplication("dialer-app");
341 var dialerApp = startApplication("dialer-app");
235 var cameraApp = startApplication("camera-app");
342 var cameraApp = startApplication("camera-app");
236
343
237− var dashDelegate = findChild(desktopStage, "stageDelegate_unity8-dash");
344+ var dashDelegate = findChild(desktopStage, "appDelegate_unity8-dash");
238 verify(dashDelegate);
345 verify(dashDelegate);
239− var dialerDelegate = findChild(desktopStage, "stageDelegate_dialer-app");
346+ var dialerDelegate = findChild(desktopStage, "appDelegate_dialer-app");
240 verify(dialerDelegate);
347 verify(dialerDelegate);
241− var cameraDelegate = findChild(desktopStage, "stageDelegate_camera-app");
348+ var cameraDelegate = findChild(desktopStage, "appDelegate_camera-app");
242 verify(cameraDelegate);
349 verify(cameraDelegate);
243
350
244 // maximize
351 // maximize
@@ -256,20 +363,20 @@ Item {
256
363
257 function test_applicationsBecomeVisibleWhenOccludingAppRemoved() {
364 function test_applicationsBecomeVisibleWhenOccludingAppRemoved() {
258 var dashApp = startApplication("unity8-dash");
365 var dashApp = startApplication("unity8-dash");
259− var dashDelegate = findChild(desktopStage, "stageDelegate_unity8-dash");
366+ var dashDelegate = findChild(desktopStage, "appDelegate_unity8-dash");
260 verify(dashDelegate);
367 verify(dashDelegate);
261
368
262 var dialerApp = startApplication("dialer-app");
369 var dialerApp = startApplication("dialer-app");
263− var dialerDelegate = findChild(desktopStage, "stageDelegate_dialer-app");
370+ var dialerDelegate = findChild(desktopStage, "appDelegate_dialer-app");
264 verify(dialerDelegate);
371 verify(dialerDelegate);
265
372
266 var cameraApp = startApplication("camera-app");
373 var cameraApp = startApplication("camera-app");
267− var cameraDelegate = findChild(desktopStage, "stageDelegate_camera-app");
374+ var cameraDelegate = findChild(desktopStage, "appDelegate_camera-app");
268 verify(cameraDelegate);
375 verify(cameraDelegate);
269 findChild(dialerDelegate, "decoratedWindow").maximize();
376 findChild(dialerDelegate, "decoratedWindow").maximize();
270
377
271 var galleryApp = startApplication("gallery-app");
378 var galleryApp = startApplication("gallery-app");
272− var galleryDelegate = findChild(desktopStage, "stageDelegate_gallery-app");
379+ var galleryDelegate = findChild(desktopStage, "appDelegate_gallery-app");
273 verify(galleryDelegate);
380 verify(galleryDelegate);
274 findChild(galleryDelegate, "decoratedWindow").maximize();
381 findChild(galleryDelegate, "decoratedWindow").maximize();
275
382