on xenial help sign in
rebase on top of lp:~lukas-kde/unity8/horizVertWindowMaximize
cf5a47dLukáš 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 restore()
31+ signal restoreClicked()
32 signal focusMaximizedApp()
32 signal focusMaximizedApp()
33}
33}
qml/Components/WindowControlButtons.qml+17−6
@@ -33,9 +33,11 @@ Row {
33
33
34 readonly property bool overlayShown: TouchControlsState.overlayShown && target && target.surface === TouchControlsState.surface
34 readonly property bool overlayShown: TouchControlsState.overlayShown && target && target.surface === TouchControlsState.surface
35
35
36− signal close()
36+ signal closeClicked()
37− signal minimize()
37+ signal minimizeClicked()
38− signal maximize()
38+ signal maximizeClicked()
39+ signal maximizeVerticallyClicked()
40+ signal maximizeHorizontallyClicked()
39
41
40 MouseArea {
42 MouseArea {
41 id: closeWindowButton
43 id: closeWindowButton
@@ -43,7 +45,7 @@ Row {
43 hoverEnabled: true
45 hoverEnabled: true
44 height: parent.height
46 height: parent.height
45 width: height
47 width: height
46− onClicked: root.close()
48+ onClicked: root.closeClicked()
47 visible: root.closeButtonShown
49 visible: root.closeButtonShown
48
50
49 Rectangle {
51 Rectangle {
@@ -66,7 +68,7 @@ Row {
66 hoverEnabled: true
68 hoverEnabled: true
67 height: parent.height
69 height: parent.height
68 width: height
70 width: height
69− onClicked: root.minimize()
71+ onClicked: root.minimizeClicked()
70
72
71 Rectangle {
73 Rectangle {
72 anchors.fill: parent
74 anchors.fill: 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.fill: parent
105 anchors.fill: parent
qml/Panel/Panel.qml+5−4
@@ -110,7 +110,7 @@ Item {
110 height: indicators.minimizedPanelHeight
110 height: indicators.minimizedPanelHeight
111 hoverEnabled: true
111 hoverEnabled: true
112 onClicked: callHint.visible ? callHint.showLiveCall() : PanelState.focusMaximizedApp()
112 onClicked: callHint.visible ? callHint.showLiveCall() : PanelState.focusMaximizedApp()
113− onDoubleClicked: PanelState.restore()
113+ onDoubleClicked: PanelState.restoreClicked()
114
114
115 // WindowControlButtons inside the mouse area, otherwise QML doesn't grok nested hover events :/
115 // WindowControlButtons inside the mouse area, otherwise QML doesn't grok nested hover events :/
116 // cf. https://bugreports.qt.io/browse/QTBUG-32909
116 // cf. https://bugreports.qt.io/browse/QTBUG-32909
@@ -125,15 +125,16 @@ Item {
125 bottomMargin: units.gu(0.5)
125 bottomMargin: units.gu(0.5)
126 }
126 }
127 height: indicators.minimizedPanelHeight - anchors.topMargin - anchors.bottomMargin
127 height: indicators.minimizedPanelHeight - anchors.topMargin - anchors.bottomMargin
128+
128 visible: ((PanelState.buttonsVisible && parent.containsMouse) ||
129 visible: ((PanelState.buttonsVisible && parent.containsMouse) ||
129 (TouchControlsState.overlayShown && TouchControlsState.buttonsShownInPanel))
130 (TouchControlsState.overlayShown && TouchControlsState.buttonsShownInPanel))
130 && !root.locked && !callHint.visible
131 && !root.locked && !callHint.visible
131 active: PanelState.buttonsVisible || TouchControlsState.overlayShown
132 active: PanelState.buttonsVisible || TouchControlsState.overlayShown
132 windowIsMaximized: true
133 windowIsMaximized: true
134+ onCloseClicked: PanelState.closeClicked()
135+ onMinimizeClicked: PanelState.minimizeClicked()
136+ onMaximizeClicked: PanelState.restoreClicked()
133 closeButtonShown: PanelState.closeButtonShown
137 closeButtonShown: PanelState.closeButtonShown
134− onClose: PanelState.close()
135− onMinimize: PanelState.minimize()
136− onMaximize: PanelState.restore()
137 }
138 }
138 }
139 }
139
140
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
@@ -92,7 +92,8 @@ AbstractStage {
92 GlobalShortcut {
92 GlobalShortcut {
93 id: minimizeRestoreShortcut
93 id: minimizeRestoreShortcut
94 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Down
94 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Down
95− onTriggered: priv.focusedAppDelegate.maximized || priv.focusedAppDelegate.maximizedLeft || priv.focusedAppDelegate.maximizedRight
95+ onTriggered: priv.focusedAppDelegate.maximized || priv.focusedAppDelegate.maximizedLeft || priv.focusedAppDelegate.maximizedRight ||
96+ priv.focusedAppDelegate.maximizedHorizontally || priv.focusedAppDelegate.maximizedVertically
96 ? priv.focusedAppDelegate.restoreFromMaximized() : priv.focusedAppDelegate.minimize()
97 ? priv.focusedAppDelegate.restoreFromMaximized() : priv.focusedAppDelegate.minimize()
97 active: priv.focusedAppDelegate !== null
98 active: priv.focusedAppDelegate !== null
98 }
99 }
@@ -161,9 +162,9 @@ AbstractStage {
161
162
162 Connections {
163 Connections {
163 target: PanelState
164 target: PanelState
164− onClose: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.close(); } }
165+ onCloseClicked: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.close(); } }
165− onMinimize: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.minimize(); } }
166+ onMinimizeClicked: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.minimize(); } }
166− onRestore: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.restoreFromMaximized(); } }
167+ onRestoreClicked: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.restoreFromMaximized(); } }
167 onFocusMaximizedApp: {
168 onFocusMaximizedApp: {
168 if (priv.foregroundMaximizedAppDelegate) {
169 if (priv.foregroundMaximizedAppDelegate) {
169 priv.foregroundMaximizedAppDelegate.focus = true;
170 priv.foregroundMaximizedAppDelegate.focus = true;
@@ -291,19 +292,16 @@ AbstractStage {
291 property alias widthIncrement: decoratedWindow.widthIncrement
292 property alias widthIncrement: decoratedWindow.widthIncrement
292 property alias heightIncrement: decoratedWindow.heightIncrement
293 property alias heightIncrement: decoratedWindow.heightIncrement
293
294
294− QtObject {
295− id: appDelegatePrivate
296− property bool maximized: false
295+ readonly property bool maximized: windowState & WindowStateStorage.WindowStateMaximized
297− property bool maximizedLeft: false
296+ readonly property bool maximizedLeft: windowState & WindowStateStorage.WindowStateMaximizedLeft
298− property bool maximizedRight: false
297+ readonly property bool maximizedRight: windowState & WindowStateStorage.WindowStateMaximizedRight
299− property bool minimized: false
298+ readonly property bool maximizedHorizontally: windowState & WindowStateStorage.WindowStateMaximizedHorizontally
300− }
299+ readonly property bool maximizedVertically: windowState & WindowStateStorage.WindowStateMaximizedVertically
301− readonly property alias maximized: appDelegatePrivate.maximized
300+ readonly property bool minimized: windowState & WindowStateStorage.WindowStateMinimized
302− readonly property alias maximizedLeft: appDelegatePrivate.maximizedLeft
303− readonly property alias maximizedRight: appDelegatePrivate.maximizedRight
304− readonly property alias minimized: appDelegatePrivate.minimized
305 readonly property alias fullscreen: decoratedWindow.fullscreen
301 readonly property alias fullscreen: decoratedWindow.fullscreen
306
302
303+ property int windowState: WindowStateStorage.WindowStateNormal
304+
307 readonly property var application: model.application
305 readonly property var application: model.application
308 property bool animationsEnabled: true
306 property bool animationsEnabled: true
309 property alias title: decoratedWindow.title
307 property alias title: decoratedWindow.title
@@ -402,43 +400,45 @@ AbstractStage {
402
400
403 function maximize(animated) {
401 function maximize(animated) {
404 animationsEnabled = (animated === undefined) || animated;
402 animationsEnabled = (animated === undefined) || animated;
405− appDelegatePrivate.minimized = false;
403+ windowState = WindowStateStorage.WindowStateMaximized;
406− appDelegatePrivate.maximized = true;
404+ }
407− appDelegatePrivate.maximizedLeft = false;
405+ function maximizeLeft(animated) {
408− appDelegatePrivate.maximizedRight = false;
406+ animationsEnabled = (animated === undefined) || animated;
407+ windowState = WindowStateStorage.WindowStateMaximizedLeft;
408+ }
409+ function maximizeRight(animated) {
410+ animationsEnabled = (animated === undefined) || animated;
411+ windowState = WindowStateStorage.WindowStateMaximizedRight;
409 }
412 }
410− function maximizeLeft() {
413+ function maximizeHorizontally(animated) {
411− appDelegatePrivate.minimized = false;
414+ animationsEnabled = (animated === undefined) || animated;
412− appDelegatePrivate.maximized = false;
415+ windowState = WindowStateStorage.WindowStateMaximizedHorizontally;
413− appDelegatePrivate.maximizedLeft = true;
414− appDelegatePrivate.maximizedRight = false;
415 }
416 }
416− function maximizeRight() {
417+ function maximizeVertically(animated) {
417− appDelegatePrivate.minimized = false;
418+ animationsEnabled = (animated === undefined) || animated;
418− appDelegatePrivate.maximized = false;
419+ windowState = WindowStateStorage.WindowStateMaximizedVertically;
419− appDelegatePrivate.maximizedLeft = false;
420− appDelegatePrivate.maximizedRight = true;
421 }
420 }
422 function minimize(animated) {
421 function minimize(animated) {
423 animationsEnabled = (animated === undefined) || animated;
422 animationsEnabled = (animated === undefined) || animated;
424− appDelegatePrivate.minimized = true;
423+ windowState |= WindowStateStorage.WindowStateMinimized; // add the minimized bit
425 }
424 }
426 function restoreFromMaximized(animated) {
425 function restoreFromMaximized(animated) {
427 animationsEnabled = (animated === undefined) || animated;
426 animationsEnabled = (animated === undefined) || animated;
428− appDelegatePrivate.minimized = false;
427+ windowState = WindowStateStorage.WindowStateNormal;
429− appDelegatePrivate.maximized = false;
430− appDelegatePrivate.maximizedLeft = false;
431− appDelegatePrivate.maximizedRight = false;
432 }
428 }
433 function restore(animated) {
429 function restore(animated) {
434 animationsEnabled = (animated === undefined) || animated;
430 animationsEnabled = (animated === undefined) || animated;
435− appDelegatePrivate.minimized = false;
431+ windowState &= ~WindowStateStorage.WindowStateMinimized; // clear the minimized bit
436 if (maximized)
432 if (maximized)
437 maximize();
433 maximize();
438 else if (maximizedLeft)
434 else if (maximizedLeft)
439 maximizeLeft();
435 maximizeLeft();
440 else if (maximizedRight)
436 else if (maximizedRight)
441 maximizeRight();
437 maximizeRight();
438+ else if (maximizedHorizontally)
439+ maximizeHorizontally();
440+ else if (maximizedVertically)
441+ maximizeVertically();
442
442
443 focus = true;
443 focus = true;
444 }
444 }
@@ -469,8 +469,7 @@ AbstractStage {
469 },
469 },
470 State {
470 State {
471 name: "normal";
471 name: "normal";
472− when: !appDelegate.maximized && !appDelegate.minimized
472+ when: appDelegate.windowState == WindowStateStorage.WindowStateNormal
473− && !appDelegate.maximizedLeft && !appDelegate.maximizedRight
474 PropertyChanges {
473 PropertyChanges {
475 target: appDelegate;
474 target: appDelegate;
476 visuallyMinimized: false;
475 visuallyMinimized: false;
@@ -518,13 +517,23 @@ AbstractStage {
518 requestedHeight: appContainer.height - PanelState.panelHeight
517 requestedHeight: appContainer.height - PanelState.panelHeight
519 }
518 }
520 },
519 },
520+ State {
521+ name: "maximizedHorizontally"; when: appDelegate.maximizedHorizontally && !appDelegate.minimized
522+ PropertyChanges { target: appDelegate; x: root.leftMargin }
523+ PropertyChanges { target: decoratedWindow; requestedWidth: appContainer.width - root.leftMargin }
524+ },
525+ State {
526+ name: "maximizedVertically"; when: appDelegate.maximizedVertically && !appDelegate.minimized
527+ PropertyChanges { target: appDelegate; y: PanelState.panelHeight }
528+ PropertyChanges { target: decoratedWindow; requestedHeight: appContainer.height - PanelState.panelHeight }
529+ },
521 State {
530 State {
522 name: "minimized"; when: appDelegate.minimized
531 name: "minimized"; when: appDelegate.minimized
523 PropertyChanges {
532 PropertyChanges {
524 target: appDelegate;
533 target: appDelegate;
525 x: -appDelegate.width / 2;
534 x: -appDelegate.width / 2;
526 scale: units.gu(5) / appDelegate.width;
535 scale: units.gu(5) / appDelegate.width;
527− opacity: 0
536+ opacity: 0;
528 visuallyMinimized: true;
537 visuallyMinimized: true;
529 visuallyMaximized: false
538 visuallyMaximized: false
530 }
539 }
@@ -627,10 +636,13 @@ AbstractStage {
627 requestedWidth: appDelegate.requestedWidth
636 requestedWidth: appDelegate.requestedWidth
628 requestedHeight: appDelegate.requestedHeight
637 requestedHeight: appDelegate.requestedHeight
629
638
630− onClose: { appDelegate.close(); }
639+ onCloseClicked: { appDelegate.close(); }
631− onMaximize: appDelegate.maximized || appDelegate.maximizedLeft || appDelegate.maximizedRight
640+ onMaximizeClicked: appDelegate.maximized || appDelegate.maximizedLeft || appDelegate.maximizedRight
641+ || appDelegate.maximizedHorizontally || appDelegate.maximizedVertically
632− ? appDelegate.restoreFromMaximized() : appDelegate.maximize()
642+ ? appDelegate.restoreFromMaximized() : appDelegate.maximize()
643+ onMaximizeHorizontallyClicked: appDelegate.maximizedHorizontally ? appDelegate.restoreFromMaximized() : appDelegate.maximizeHorizontally()
644+ onMaximizeVerticallyClicked: appDelegate.maximizedVertically ? appDelegate.restoreFromMaximized() : appDelegate.maximizeVertically()
633− onMinimize: appDelegate.minimize()
645+ onMinimizeClicked: appDelegate.minimize()
634 onDecorationPressed: { appDelegate.focus = true; }
646 onDecorationPressed: { appDelegate.focus = true; }
635 }
647 }
636
648
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
@@ -30,11 +30,13 @@ MouseArea {
30 property bool active: false
30 property bool active: false
31 hoverEnabled: true
31 hoverEnabled: true
32
32
33− signal close()
33+ signal closeClicked()
34− signal minimize()
34+ signal minimizeClicked()
35− signal maximize()
35+ signal maximizeClicked()
36+ signal maximizeHorizontallyClicked()
37+ signal maximizeVerticallyClicked()
36
38
37− onDoubleClicked: root.maximize()
39+ onDoubleClicked: root.maximizeClicked()
38
40
39 QtObject {
41 QtObject {
40 id: priv
42 id: priv
@@ -91,11 +93,13 @@ MouseArea {
91 id: buttons
93 id: buttons
92 height: parent.height
94 height: parent.height
93 active: root.active
95 active: root.active
96+ onCloseClicked: root.closeClicked();
97+ onMinimizeClicked: root.minimizeClicked();
98+ onMaximizeClicked: root.maximizeClicked();
99+ onMaximizeHorizontallyClicked: root.maximizeHorizontallyClicked();
100+ onMaximizeVerticallyClicked: root.maximizeVerticallyClicked();
94 closeButtonShown: root.target.application.appId !== "unity8-dash"
101 closeButtonShown: root.target.application.appId !== "unity8-dash"
95 target: root.target
102 target: root.target
96− onClose: root.close();
97− onMinimize: root.minimize();
98− onMaximize: root.maximize();
99 }
103 }
100
104
101 Label {
105 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
@@ -82,15 +82,35 @@ MouseArea {
82 target.y = Qt.binding(function() { return Math.max(Math.min(windowGeometry.y, root.screenHeight - target.requestedHeight), PanelState.panelHeight); });
82 target.y = Qt.binding(function() { return Math.max(Math.min(windowGeometry.y, root.screenHeight - target.requestedHeight), PanelState.panelHeight); });
83
83
84 var windowState = windowStateStorage.getState(root.windowId, WindowStateStorage.WindowStateNormal)
84 var windowState = windowStateStorage.getState(root.windowId, WindowStateStorage.WindowStateNormal)
85− if (windowState === WindowStateStorage.WindowStateMaximized) {
85+ switch (windowState) {
86+ case WindowStateStorage.WindowStateNormal:
87+ break;
88+ case WindowStateStorage.WindowStateMaximized:
86− target.maximize(false)
89+ target.maximize(false);
90+ break;
91+ case WindowStateStorage.WindowStateMaximizedLeft:
92+ target.maximizeLeft(false);
93+ break;
94+ case WindowStateStorage.WindowStateMaximizedRight:
95+ target.maximizeRight(false);
96+ break;
97+ case WindowStateStorage.WindowStateMaximizedHorizontally:
98+ target.maximizeHorizontally(false);
99+ break;
100+ case WindowStateStorage.WindowStateMaximizedVertically:
101+ target.maximizeVertically(false);
102+ break;
103+ default:
104+ console.warn("Unsupported window state");
105+ break;
87 }
106 }
107+
88 priv.updateNormalGeometry();
108 priv.updateNormalGeometry();
89 }
109 }
90
110
91 function saveWindowState() {
111 function saveWindowState() {
92− windowStateStorage.saveState(root.windowId, target.state == "maximized" ? WindowStateStorage.WindowStateMaximized : WindowStateStorage.WindowStateNormal)
112+ windowStateStorage.saveState(root.windowId, target.windowState & ~WindowStateStorage.WindowStateMinimized); // clear the minimized bit when saving
93− windowStateStorage.saveGeometry(root.windowId, Qt.rect(priv.normalX, priv.normalY, priv.normalWidth, priv.normalHeight))
113+ windowStateStorage.saveGeometry(root.windowId, Qt.rect(priv.normalX, priv.normalY, priv.normalWidth, priv.normalHeight));
94 }
114 }
95
115
96 QtObject {
116 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() {