on xenial help sign in
Implement maximizing windows horizontally/vertically
760bfdfLukáš Tinkl
plugins/Utils/windowstatestorage.h+13−3
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright 2015 Canonical Ltd.
2+ * Copyright 2015-2016 Canonical Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
5 * it under the terms of the GNU Lesser General Public License as published by
@@ -25,9 +25,19 @@ class WindowStateStorage: public QObject
25 Q_ENUMS(WindowState)
25 Q_ENUMS(WindowState)
26public:
26public:
27 enum WindowState {
27 enum WindowState {
28− WindowStateNormal,
28+ WindowStateNormal = 1 << 0,
29− WindowStateMaximized
29+ WindowStateMaximized = 1 << 1,
30+ WindowStateMinimized = 1 << 2,
31+ WindowStateFullscreen = 1 << 3,
32+ WindowStateMaximizedLeft = 1 << 4,
33+ WindowStateMaximizedRight = 1 << 5,
34+ WindowStateMaximizedHorizontally = 1 << 6,
35+ WindowStateMaximizedVertically = 1 << 7
30 };
36 };
37+ Q_DECLARE_FLAGS(WindowStates, WindowState)
38+#if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0))
39+ Q_FLAG(WindowStates)
40+#endif
31
41
32 WindowStateStorage(QObject *parent = 0);
42 WindowStateStorage(QObject *parent = 0);
33 virtual ~WindowStateStorage();
43 virtual ~WindowStateStorage();
qml/Components/PanelState/PanelState.qml+3−3
@@ -26,8 +26,8 @@ QtObject {
26 property bool dropShadow: false
26 property bool dropShadow: false
27 property int panelHeight: 0
27 property int panelHeight: 0
28
28
29− signal close()
29+ signal closeClicked()
30− signal minimize()
30+ signal minimizeClicked()
31− signal maximize()
31+ signal restoreClicked()
32 signal focusMaximizedApp()
32 signal focusMaximizedApp()
33}
33}
qml/Components/WindowControlButtons.qml+17−6
@@ -25,9 +25,11 @@ Row {
25 property bool active: false
25 property bool active: false
26 property bool closeButtonShown: true
26 property bool closeButtonShown: true
27
27
28− signal close()
28+ signal closeClicked()
29− signal minimize()
29+ signal minimizeClicked()
30− signal maximize()
30+ signal maximizeClicked()
31+ signal maximizeVerticallyClicked()
32+ signal maximizeHorizontallyClicked()
31
33
32 MouseArea {
34 MouseArea {
33 id: closeWindowButton
35 id: closeWindowButton
@@ -35,7 +37,7 @@ Row {
35 hoverEnabled: true
37 hoverEnabled: true
36 height: parent.height
38 height: parent.height
37 width: height
39 width: height
38− onClicked: root.close()
40+ onClicked: root.closeClicked()
39 visible: root.closeButtonShown
41 visible: root.closeButtonShown
40
42
41 Rectangle {
43 Rectangle {
@@ -62,7 +64,7 @@ Row {
62 hoverEnabled: true
64 hoverEnabled: true
63 height: parent.height
65 height: parent.height
64 width: height
66 width: height
65− onClicked: root.minimize()
67+ onClicked: root.minimizeClicked()
66
68
67 Rectangle {
69 Rectangle {
68 anchors.centerIn: parent
70 anchors.centerIn: parent
@@ -88,7 +90,16 @@ Row {
88 hoverEnabled: true
90 hoverEnabled: true
89 height: parent.height
91 height: parent.height
90 width: height
92 width: height
93+ acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
91− onClicked: root.maximize()
94+ onClicked: {
95+ if (mouse.button == Qt.LeftButton) {
96+ root.maximizeClicked();
97+ } else if (mouse.button == Qt.RightButton) {
98+ root.maximizeHorizontallyClicked();
99+ } else if (mouse.button == Qt.MiddleButton) {
100+ root.maximizeVerticallyClicked();
101+ }
102+ }
92
103
93 Rectangle {
104 Rectangle {
94 anchors.centerIn: parent
105 anchors.centerIn: parent
qml/Panel/Panel.qml+4−4
@@ -101,7 +101,7 @@ Item {
101 height: indicators.minimizedPanelHeight
101 height: indicators.minimizedPanelHeight
102 hoverEnabled: true
102 hoverEnabled: true
103 onClicked: callHint.visible ? callHint.showLiveCall() : PanelState.focusMaximizedApp()
103 onClicked: callHint.visible ? callHint.showLiveCall() : PanelState.focusMaximizedApp()
104− onDoubleClicked: PanelState.maximize()
104+ onDoubleClicked: PanelState.restoreClicked()
105
105
106 property bool mouseWasPressed: false
106 property bool mouseWasPressed: false
107 onPressed: mouseWasPressed = containsPress
107 onPressed: mouseWasPressed = containsPress
@@ -127,10 +127,10 @@ Item {
127 height: indicators.minimizedPanelHeight - anchors.topMargin - anchors.bottomMargin
127 height: indicators.minimizedPanelHeight - anchors.topMargin - anchors.bottomMargin
128 visible: PanelState.buttonsVisible && parent.containsMouse && !root.locked && !callHint.visible
128 visible: PanelState.buttonsVisible && parent.containsMouse && !root.locked && !callHint.visible
129 active: PanelState.buttonsVisible
129 active: PanelState.buttonsVisible
130+ onCloseClicked: PanelState.closeClicked()
131+ onMinimizeClicked: PanelState.minimizeClicked()
132+ onMaximizeClicked: PanelState.restoreClicked()
130 closeButtonShown: PanelState.closeButtonShown
133 closeButtonShown: PanelState.closeButtonShown
131− onClose: PanelState.close()
132− onMinimize: PanelState.minimize()
133− onMaximize: PanelState.maximize()
134 }
134 }
135 }
135 }
136
136
qml/Stages/DecoratedWindow.qml+10−6
@@ -46,9 +46,11 @@ FocusScope {
46 property alias widthIncrement: applicationWindow.widthIncrement
46 property alias widthIncrement: applicationWindow.widthIncrement
47 property alias heightIncrement: applicationWindow.heightIncrement
47 property alias heightIncrement: applicationWindow.heightIncrement
48
48
49− signal close()
49+ signal closeClicked()
50− signal maximize()
50+ signal maximizeClicked()
51− signal minimize()
51+ signal maximizeHorizontallyClicked()
52+ signal maximizeVerticallyClicked()
53+ signal minimizeClicked()
52 signal decorationPressed()
54 signal decorationPressed()
53
55
54 Rectangle {
56 Rectangle {
@@ -86,9 +88,11 @@ FocusScope {
86 title: applicationWindow.title
88 title: applicationWindow.title
87 visible: root.decorationShown
89 visible: root.decorationShown
88
90
89− onClose: root.close();
91+ onCloseClicked: root.closeClicked();
92+ onMaximizeClicked: { root.decorationPressed(); root.maximizeClicked(); }
93+ onMaximizeHorizontallyClicked: { root.decorationPressed(); root.maximizeHorizontallyClicked(); }
90− onMaximize: { root.decorationPressed(); root.maximize(); }
94+ onMaximizeVerticallyClicked: { root.decorationPressed(); root.maximizeVerticallyClicked(); }
91− onMinimize: root.minimize();
95+ onMinimizeClicked: root.minimizeClicked();
92 onPressed: root.decorationPressed();
96 onPressed: root.decorationPressed();
93 }
97 }
94
98
qml/Stages/DesktopStage.qml+54−42
@@ -90,7 +90,8 @@ AbstractStage {
90 GlobalShortcut {
90 GlobalShortcut {
91 id: minimizeRestoreShortcut
91 id: minimizeRestoreShortcut
92 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Down
92 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Down
93− onTriggered: priv.focusedAppDelegate.maximized || priv.focusedAppDelegate.maximizedLeft || priv.focusedAppDelegate.maximizedRight
93+ onTriggered: priv.focusedAppDelegate.maximized || priv.focusedAppDelegate.maximizedLeft || priv.focusedAppDelegate.maximizedRight ||
94+ priv.focusedAppDelegate.maximizedHorizontally || priv.focusedAppDelegate.maximizedVertically
94 ? priv.focusedAppDelegate.restoreFromMaximized() : priv.focusedAppDelegate.minimize()
95 ? priv.focusedAppDelegate.restoreFromMaximized() : priv.focusedAppDelegate.minimize()
95 active: priv.focusedAppDelegate !== null
96 active: priv.focusedAppDelegate !== null
96 }
97 }
@@ -159,9 +160,9 @@ AbstractStage {
159
160
160 Connections {
161 Connections {
161 target: PanelState
162 target: PanelState
162− onClose: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.close(); } }
163+ onCloseClicked: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.close(); } }
163− onMinimize: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.minimize(); } }
164+ onMinimizeClicked: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.minimize(); } }
164− onMaximize: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.restoreFromMaximized(); } }
165+ onRestoreClicked: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.restoreFromMaximized(); } }
165 onFocusMaximizedApp: {
166 onFocusMaximizedApp: {
166 if (priv.foregroundMaximizedAppDelegate) {
167 if (priv.foregroundMaximizedAppDelegate) {
167 priv.foregroundMaximizedAppDelegate.focus = true;
168 priv.foregroundMaximizedAppDelegate.focus = true;
@@ -276,19 +277,16 @@ AbstractStage {
276 property alias widthIncrement: decoratedWindow.widthIncrement
277 property alias widthIncrement: decoratedWindow.widthIncrement
277 property alias heightIncrement: decoratedWindow.heightIncrement
278 property alias heightIncrement: decoratedWindow.heightIncrement
278
279
279− QtObject {
280− id: appDelegatePrivate
281− property bool maximized: false
280+ readonly property bool maximized: windowState & WindowStateStorage.WindowStateMaximized
282− property bool maximizedLeft: false
281+ readonly property bool maximizedLeft: windowState & WindowStateStorage.WindowStateMaximizedLeft
283− property bool maximizedRight: false
282+ readonly property bool maximizedRight: windowState & WindowStateStorage.WindowStateMaximizedRight
284− property bool minimized: false
283+ readonly property bool maximizedHorizontally: windowState & WindowStateStorage.WindowStateMaximizedHorizontally
285− }
284+ readonly property bool maximizedVertically: windowState & WindowStateStorage.WindowStateMaximizedVertically
286− readonly property alias maximized: appDelegatePrivate.maximized
285+ readonly property bool minimized: windowState & WindowStateStorage.WindowStateMinimized
287− readonly property alias maximizedLeft: appDelegatePrivate.maximizedLeft
288− readonly property alias maximizedRight: appDelegatePrivate.maximizedRight
289− readonly property alias minimized: appDelegatePrivate.minimized
290 readonly property alias fullscreen: decoratedWindow.fullscreen
286 readonly property alias fullscreen: decoratedWindow.fullscreen
291
287
288+ property int windowState: WindowStateStorage.WindowStateNormal
289+
292 readonly property var application: model.application
290 readonly property var application: model.application
293 property bool animationsEnabled: true
291 property bool animationsEnabled: true
294 property alias title: decoratedWindow.title
292 property alias title: decoratedWindow.title
@@ -386,43 +384,45 @@ AbstractStage {
386
384
387 function maximize(animated) {
385 function maximize(animated) {
388 animationsEnabled = (animated === undefined) || animated;
386 animationsEnabled = (animated === undefined) || animated;
389− appDelegatePrivate.minimized = false;
387+ windowState = WindowStateStorage.WindowStateMaximized;
390− appDelegatePrivate.maximized = true;
388+ }
391− appDelegatePrivate.maximizedLeft = false;
389+ function maximizeLeft(animated) {
392− appDelegatePrivate.maximizedRight = false;
390+ animationsEnabled = (animated === undefined) || animated;
391+ windowState = WindowStateStorage.WindowStateMaximizedLeft;
392+ }
393+ function maximizeRight(animated) {
394+ animationsEnabled = (animated === undefined) || animated;
395+ windowState = WindowStateStorage.WindowStateMaximizedRight;
393 }
396 }
394− function maximizeLeft() {
397+ function maximizeHorizontally(animated) {
395− appDelegatePrivate.minimized = false;
398+ animationsEnabled = (animated === undefined) || animated;
396− appDelegatePrivate.maximized = false;
399+ windowState = WindowStateStorage.WindowStateMaximizedHorizontally;
397− appDelegatePrivate.maximizedLeft = true;
398− appDelegatePrivate.maximizedRight = false;
399 }
400 }
400− function maximizeRight() {
401+ function maximizeVertically(animated) {
401− appDelegatePrivate.minimized = false;
402+ animationsEnabled = (animated === undefined) || animated;
402− appDelegatePrivate.maximized = false;
403+ windowState = WindowStateStorage.WindowStateMaximizedVertically;
403− appDelegatePrivate.maximizedLeft = false;
404− appDelegatePrivate.maximizedRight = true;
405 }
404 }
406 function minimize(animated) {
405 function minimize(animated) {
407 animationsEnabled = (animated === undefined) || animated;
406 animationsEnabled = (animated === undefined) || animated;
408− appDelegatePrivate.minimized = true;
407+ windowState |= WindowStateStorage.WindowStateMinimized; // add the minimized bit
409 }
408 }
410 function restoreFromMaximized(animated) {
409 function restoreFromMaximized(animated) {
411 animationsEnabled = (animated === undefined) || animated;
410 animationsEnabled = (animated === undefined) || animated;
412− appDelegatePrivate.minimized = false;
411+ windowState = WindowStateStorage.WindowStateNormal;
413− appDelegatePrivate.maximized = false;
414− appDelegatePrivate.maximizedLeft = false;
415− appDelegatePrivate.maximizedRight = false;
416 }
412 }
417 function restore(animated) {
413 function restore(animated) {
418 animationsEnabled = (animated === undefined) || animated;
414 animationsEnabled = (animated === undefined) || animated;
419− appDelegatePrivate.minimized = false;
415+ windowState &= ~WindowStateStorage.WindowStateMinimized; // clear the minimized bit
420 if (maximized)
416 if (maximized)
421 maximize();
417 maximize();
422 else if (maximizedLeft)
418 else if (maximizedLeft)
423 maximizeLeft();
419 maximizeLeft();
424 else if (maximizedRight)
420 else if (maximizedRight)
425 maximizeRight();
421 maximizeRight();
422+ else if (maximizedHorizontally)
423+ maximizeHorizontally();
424+ else if (maximizedVertically)
425+ maximizeVertically();
426
426
427 focus = true;
427 focus = true;
428 }
428 }
@@ -453,8 +453,7 @@ AbstractStage {
453 },
453 },
454 State {
454 State {
455 name: "normal";
455 name: "normal";
456− when: !appDelegate.maximized && !appDelegate.minimized
456+ when: appDelegate.windowState == WindowStateStorage.WindowStateNormal
457− && !appDelegate.maximizedLeft && !appDelegate.maximizedRight
458 PropertyChanges {
457 PropertyChanges {
459 target: appDelegate;
458 target: appDelegate;
460 visuallyMinimized: false;
459 visuallyMinimized: false;
@@ -502,13 +501,23 @@ AbstractStage {
502 requestedHeight: appContainer.height - PanelState.panelHeight
501 requestedHeight: appContainer.height - PanelState.panelHeight
503 }
502 }
504 },
503 },
504+ State {
505+ name: "maximizedHorizontally"; when: appDelegate.maximizedHorizontally && !appDelegate.minimized
506+ PropertyChanges { target: appDelegate; x: root.leftMargin }
507+ PropertyChanges { target: decoratedWindow; requestedWidth: appContainer.width - root.leftMargin }
508+ },
509+ State {
510+ name: "maximizedVertically"; when: appDelegate.maximizedVertically && !appDelegate.minimized
511+ PropertyChanges { target: appDelegate; y: PanelState.panelHeight }
512+ PropertyChanges { target: decoratedWindow; requestedHeight: appContainer.height - PanelState.panelHeight }
513+ },
505 State {
514 State {
506 name: "minimized"; when: appDelegate.minimized
515 name: "minimized"; when: appDelegate.minimized
507 PropertyChanges {
516 PropertyChanges {
508 target: appDelegate;
517 target: appDelegate;
509 x: -appDelegate.width / 2;
518 x: -appDelegate.width / 2;
510 scale: units.gu(5) / appDelegate.width;
519 scale: units.gu(5) / appDelegate.width;
511− opacity: 0
520+ opacity: 0;
512 visuallyMinimized: true;
521 visuallyMinimized: true;
513 visuallyMaximized: false
522 visuallyMaximized: false
514 }
523 }
@@ -611,10 +620,13 @@ AbstractStage {
611 requestedWidth: appDelegate.requestedWidth
620 requestedWidth: appDelegate.requestedWidth
612 requestedHeight: appDelegate.requestedHeight
621 requestedHeight: appDelegate.requestedHeight
613
622
614− onClose: { appDelegate.close(); }
623+ onCloseClicked: { appDelegate.close(); }
615− onMaximize: appDelegate.maximized || appDelegate.maximizedLeft || appDelegate.maximizedRight
624+ onMaximizeClicked: appDelegate.maximized || appDelegate.maximizedLeft || appDelegate.maximizedRight
625+ || appDelegate.maximizedHorizontally || appDelegate.maximizedVertically
616− ? appDelegate.restoreFromMaximized() : appDelegate.maximize()
626+ ? appDelegate.restoreFromMaximized() : appDelegate.maximize()
627+ onMaximizeHorizontallyClicked: appDelegate.maximizedHorizontally ? appDelegate.restoreFromMaximized() : appDelegate.maximizeHorizontally()
628+ onMaximizeVerticallyClicked: appDelegate.maximizedVertically ? appDelegate.restoreFromMaximized() : appDelegate.maximizeVertically()
617− onMinimize: appDelegate.minimize()
629+ onMinimizeClicked: appDelegate.minimize()
618 onDecorationPressed: { appDelegate.focus = true; }
630 onDecorationPressed: { appDelegate.focus = true; }
619 }
631 }
620
632
qml/Stages/WindowDecoration.qml+12−8
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2014-2015 Canonical, Ltd.
2+ * Copyright (C) 2014-2016 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
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
5 * it under the terms of the GNU General Public License as published by
@@ -29,11 +29,13 @@ MouseArea {
29 property bool active: false
29 property bool active: false
30 hoverEnabled: true
30 hoverEnabled: true
31
31
32− signal close()
32+ signal closeClicked()
33− signal minimize()
33+ signal minimizeClicked()
34− signal maximize()
34+ signal maximizeClicked()
35+ signal maximizeHorizontallyClicked()
36+ signal maximizeVerticallyClicked()
35
37
36− onDoubleClicked: root.maximize()
38+ onDoubleClicked: root.maximizeClicked()
37
39
38 QtObject {
40 QtObject {
39 id: priv
41 id: priv
@@ -84,10 +86,12 @@ MouseArea {
84 id: buttons
86 id: buttons
85 height: parent.height
87 height: parent.height
86 active: root.active
88 active: root.active
89+ onCloseClicked: root.closeClicked();
90+ onMinimizeClicked: root.minimizeClicked();
91+ onMaximizeClicked: root.maximizeClicked();
92+ onMaximizeHorizontallyClicked: root.maximizeHorizontallyClicked();
93+ onMaximizeVerticallyClicked: root.maximizeVerticallyClicked();
87 closeButtonShown: root.target.application.appId !== "unity8-dash"
94 closeButtonShown: root.target.application.appId !== "unity8-dash"
88− onClose: root.close();
89− onMinimize: root.minimize();
90− onMaximize: root.maximize();
91 }
95 }
92
96
93 Label {
97 Label {
qml/Stages/WindowResizeArea.qml+25−5
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2014-2015 Canonical, Ltd.
2+ * Copyright (C) 2014-2016 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
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
5 * it under the terms of the GNU General Public License as published by
@@ -81,15 +81,35 @@ MouseArea {
81 target.y = Qt.binding(function() { return Math.max(Math.min(windowGeometry.y, root.screenHeight - target.requestedHeight), PanelState.panelHeight); });
81 target.y = Qt.binding(function() { return Math.max(Math.min(windowGeometry.y, root.screenHeight - target.requestedHeight), PanelState.panelHeight); });
82
82
83 var windowState = windowStateStorage.getState(root.windowId, WindowStateStorage.WindowStateNormal)
83 var windowState = windowStateStorage.getState(root.windowId, WindowStateStorage.WindowStateNormal)
84− if (windowState === WindowStateStorage.WindowStateMaximized) {
84+ switch (windowState) {
85+ case WindowStateStorage.WindowStateNormal:
86+ break;
87+ case WindowStateStorage.WindowStateMaximized:
85− target.maximize(false)
88+ target.maximize(false);
89+ break;
90+ case WindowStateStorage.WindowStateMaximizedLeft:
91+ target.maximizeLeft(false);
92+ break;
93+ case WindowStateStorage.WindowStateMaximizedRight:
94+ target.maximizeRight(false);
95+ break;
96+ case WindowStateStorage.WindowStateMaximizedHorizontally:
97+ target.maximizeHorizontally(false);
98+ break;
99+ case WindowStateStorage.WindowStateMaximizedVertically:
100+ target.maximizeVertically(false);
101+ break;
102+ default:
103+ console.warn("Unsupported window state");
104+ break;
86 }
105 }
106+
87 priv.updateNormalGeometry();
107 priv.updateNormalGeometry();
88 }
108 }
89
109
90 function saveWindowState() {
110 function saveWindowState() {
91− windowStateStorage.saveState(root.windowId, target.state == "maximized" ? WindowStateStorage.WindowStateMaximized : WindowStateStorage.WindowStateNormal)
111+ windowStateStorage.saveState(root.windowId, target.windowState & ~WindowStateStorage.WindowStateMinimized); // clear the minimized bit when saving
92− windowStateStorage.saveGeometry(root.windowId, Qt.rect(priv.normalX, priv.normalY, priv.normalWidth, priv.normalHeight))
112+ windowStateStorage.saveGeometry(root.windowId, Qt.rect(priv.normalX, priv.normalY, priv.normalWidth, priv.normalHeight));
93 }
113 }
94
114
95 QtObject {
115 QtObject {
tests/mocks/Utils/windowstatestorage.h+14−3
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright 2015 Canonical Ltd.
2+ * Copyright 2015-2016 Canonical Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
5 * it under the terms of the GNU Lesser General Public License as published by
@@ -25,9 +25,20 @@ class WindowStateStorage: public QObject
25 Q_ENUMS(WindowState)
25 Q_ENUMS(WindowState)
26public:
26public:
27 enum WindowState {
27 enum WindowState {
28− WindowStateNormal,
28+ WindowStateNormal = 1 << 0,
29− WindowStateMaximized
29+ WindowStateMaximized = 1 << 1,
30+ WindowStateMinimized = 1 << 2,
31+ WindowStateFullscreen = 1 << 3,
32+ WindowStateMaximizedLeft = 1 << 4,
33+ WindowStateMaximizedRight = 1 << 5,
34+ WindowStateMaximizedHorizontally = 1 << 6,
35+ WindowStateMaximizedVertically = 1 << 7
30 };
36 };
37+ Q_DECLARE_FLAGS(WindowStates, WindowState)
38+#if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0))
39+ Q_FLAG(WindowStates)
40+#endif
41+
31 WindowStateStorage(QObject *parent = 0);
42 WindowStateStorage(QObject *parent = 0);
32
43
33 Q_INVOKABLE void saveState(const QString &windowId, WindowState state);
44 Q_INVOKABLE void saveState(const QString &windowId, WindowState state);
tests/qmltests/Stages/tst_DesktopStage.qml+31−1
@@ -462,6 +462,36 @@ Item {
462 verify(topSurfaceList.indexForId(dialerSurfaceId) === -1);
462 verify(topSurfaceList.indexForId(dialerSurfaceId) === -1);
463 }
463 }
464
464
465+ function test_windowMaximizeHorizontally() {
466+ var dialerDelegate = startApplication("dialer-app");
467+
468+ var dialerMaximizeButton = findChild(dialerDelegate, "maximizeWindowButton");
469+ verify(dialerMaximizeButton);
470+
471+ // RMB to maximize horizontally
472+ mouseClick(dialerMaximizeButton, dialerMaximizeButton.width/2, dialerMaximizeButton.height/2, Qt.RightButton);
473+ tryCompare(dialerDelegate, "windowState", WindowStateStorage.WindowStateMaximizedHorizontally);
474+
475+ // click again to restore
476+ mouseClick(dialerMaximizeButton);
477+ tryCompare(dialerDelegate, "windowState", WindowStateStorage.WindowStateNormal);
478+ }
479+
480+ function test_windowMaximizeVertically() {
481+ var dialerDelegate = startApplication("dialer-app");
482+
483+ var dialerMaximizeButton = findChild(dialerDelegate, "maximizeWindowButton");
484+ verify(dialerMaximizeButton);
485+
486+ // MMB to maximize vertically
487+ mouseClick(dialerMaximizeButton, dialerMaximizeButton.width/2, dialerMaximizeButton.height/2, Qt.MiddleButton);
488+ tryCompare(dialerDelegate, "windowState", WindowStateStorage.WindowStateMaximizedVertically);
489+
490+ // click again to restore
491+ mouseClick(dialerMaximizeButton);
492+ tryCompare(dialerDelegate, "windowState", WindowStateStorage.WindowStateNormal);
493+ }
494+
465 function test_smashCursorKeys() {
495 function test_smashCursorKeys() {
466 var apps = ["dialer-app", "gmail-webapp"];
496 var apps = ["dialer-app", "gmail-webapp"];
467 apps.forEach(startApplication);
497 apps.forEach(startApplication);
@@ -483,7 +513,7 @@ Item {
483 verify(decoratedWindow);
513 verify(decoratedWindow);
484
514
485 tryCompare(dashSurface, "visible", true);
515 tryCompare(dashSurface, "visible", true);
486− decoratedWindow.minimize();
516+ decoratedWindow.minimizeClicked();
487 tryCompare(dashSurface, "visible", false);
517 tryCompare(dashSurface, "visible", false);
488 }
518 }
489
519
tests/qmltests/Stages/tst_WindowResizeArea.qml+16−5
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright 2014-2015 Canonical Ltd.
2+ * Copyright 2014-2016 Canonical Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
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
5 * it under the terms of the GNU General Public License as published by
@@ -24,6 +24,7 @@ import "../../../qml/Stages"
24import Ubuntu.Components 1.3
24import Ubuntu.Components 1.3
25import Ubuntu.Components.ListItems 1.3 as ListItem
25import Ubuntu.Components.ListItems 1.3 as ListItem
26import Unity.Application 0.1
26import Unity.Application 0.1
27+import Utils 0.1
27
28
28Item {
29Item {
29 id: root
30 id: root
@@ -55,10 +56,20 @@ Item {
55 property real maximumHeight: 0
56 property real maximumHeight: 0
56 property real widthIncrement: 0
57 property real widthIncrement: 0
57 property real heightIncrement: 0
58 property real heightIncrement: 0
58− state: "normal"
59+
60+ property int windowState: WindowStateStorage.WindowStateNormal
61+
62+ states: [
63+ State { name: "normal"; when: windowState == WindowStateStorage.WindowStateNormal },
64+ State { name: "maximized"; when: windowState == WindowStateStorage.WindowStateMaximized }
65+ ]
59
66
60 function maximize() {
67 function maximize() {
61− state = "maximized"
68+ windowState = WindowStateStorage.WindowStateMaximized
69+ }
70+
71+ function restoreFromMaximized() {
72+ windowState = WindowStateStorage.WindowStateNormal
62 }
73 }
63
74
64 WindowResizeArea {
75 WindowResizeArea {
@@ -346,7 +357,7 @@ Item {
346 fakeWindow.y = initialWindowY + moveDelta
357 fakeWindow.y = initialWindowY + moveDelta
347
358
348 // Now change the state to maximized. The window should not keep updating the stored values
359 // Now change the state to maximized. The window should not keep updating the stored values
349− fakeWindow.state = "maximized"
360+ fakeWindow.maximize()
350 fakeWindow.x = 31415 // 0 is too risky to pass the test even when broken
361 fakeWindow.x = 31415 // 0 is too risky to pass the test even when broken
351 fakeWindow.y = 31415
362 fakeWindow.y = 31415
352
363
@@ -363,7 +374,7 @@ Item {
363 tryCompare(fakeWindow, "state", "maximized")
374 tryCompare(fakeWindow, "state", "maximized")
364
375
365 // clean up
376 // clean up
366− fakeWindow.state = "normal"
377+ fakeWindow.restoreFromMaximized()
367 }
378 }
368
379
369 function test_restoreMovesIntoBounds_data() {
380 function test_restoreMovesIntoBounds_data() {