on xenial help sign in
clean patch 2
f0c271dLukáš Tinkl
plugins/Utils/windowstatestorage.cpp+5−4
@@ -76,7 +76,7 @@ WindowStateStorage::WindowState WindowStateStorage::getState(const QString &wind
76 return (WindowState)query.value(QStringLiteral("state")).toInt();
76 return (WindowState)query.value(QStringLiteral("state")).toInt();
77}
77}
78
78
79−void WindowStateStorage::saveGeometry(const QString &windowId, const QRect rect)
79+void WindowStateStorage::saveGeometry(const QString &windowId, const QRect &rect)
80{
80{
81 const QString queryString = QStringLiteral("INSERT OR REPLACE INTO geometry (windowId, x, y, width, height) values ('%1', '%2', '%3', '%4', '%5');")
81 const QString queryString = QStringLiteral("INSERT OR REPLACE INTO geometry (windowId, x, y, width, height) values ('%1', '%2', '%3', '%4', '%5');")
82 .arg(sanitiseString(windowId))
82 .arg(sanitiseString(windowId))
@@ -92,7 +92,7 @@ void WindowStateStorage::saveStage(const QString &appId, int stage)
92{
92{
93 const QString queryString = QStringLiteral("INSERT OR REPLACE INTO stage (appId, stage) values ('%1', '%2');")
93 const QString queryString = QStringLiteral("INSERT OR REPLACE INTO stage (appId, stage) values ('%1', '%2');")
94 .arg(sanitiseString(appId))
94 .arg(sanitiseString(appId))
95− .arg((int)stage);
95+ .arg(stage);
96
96
97 saveValue(queryString);
97 saveValue(queryString);
98}
98}
@@ -123,7 +123,7 @@ void WindowStateStorage::executeAsyncQuery(const QString &queryString)
123 }
123 }
124}
124}
125
125
126−QRect WindowStateStorage::getGeometry(const QString &windowId, const QRect defaultValue) const
126+QRect WindowStateStorage::getGeometry(const QString &windowId, const QRect &defaultValue) const
127{
127{
128 QString queryString = QStringLiteral("SELECT * FROM geometry WHERE windowId = '%1';")
128 QString queryString = QStringLiteral("SELECT * FROM geometry WHERE windowId = '%1';")
129 .arg(sanitiseString(windowId));
129 .arg(sanitiseString(windowId));
@@ -133,7 +133,8 @@ QRect WindowStateStorage::getGeometry(const QString &windowId, const QRect defau
133 if (!query.first()) {
133 if (!query.first()) {
134 return defaultValue;
134 return defaultValue;
135 }
135 }
136− return QRect(query.value(QStringLiteral("x")).toInt(), query.value(QStringLiteral("y")).toInt(), query.value(QStringLiteral("width")).toInt(), query.value(QStringLiteral("height")).toInt());
136+ return QRect(query.value(QStringLiteral("x")).toInt(), query.value(QStringLiteral("y")).toInt(),
137+ query.value(QStringLiteral("width")).toInt(), query.value(QStringLiteral("height")).toInt());
137}
138}
138
139
139void WindowStateStorage::initdb()
140void WindowStateStorage::initdb()
plugins/Utils/windowstatestorage.h+8−3
@@ -32,7 +32,12 @@ public:
32 WindowStateMaximizedLeft = 1 << 4,
32 WindowStateMaximizedLeft = 1 << 4,
33 WindowStateMaximizedRight = 1 << 5,
33 WindowStateMaximizedRight = 1 << 5,
34 WindowStateMaximizedHorizontally = 1 << 6,
34 WindowStateMaximizedHorizontally = 1 << 6,
35− WindowStateMaximizedVertically = 1 << 7
35+ WindowStateMaximizedVertically = 1 << 7,
36+ WindowStateMaximizedTopLeft = 1 << 8,
37+ WindowStateMaximizedTopRight = 1 << 9,
38+ WindowStateMaximizedBottomLeft = 1 << 10,
39+ WindowStateMaximizedBottomRight = 1 << 11,
40+ WindowStateRestored = 1 << 12
36 };
41 };
37 Q_DECLARE_FLAGS(WindowStates, WindowState)
42 Q_DECLARE_FLAGS(WindowStates, WindowState)
38#if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0))
43#if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0))
@@ -45,8 +50,8 @@ public:
45 Q_INVOKABLE void saveState(const QString &windowId, WindowState state);
50 Q_INVOKABLE void saveState(const QString &windowId, WindowState state);
46 Q_INVOKABLE WindowState getState(const QString &windowId, WindowState defaultValue) const;
51 Q_INVOKABLE WindowState getState(const QString &windowId, WindowState defaultValue) const;
47
52
48− Q_INVOKABLE void saveGeometry(const QString &windowId, const QRect rect);
53+ Q_INVOKABLE void saveGeometry(const QString &windowId, const QRect &rect);
49− Q_INVOKABLE QRect getGeometry(const QString &windowId, const QRect defaultValue) const;
54+ Q_INVOKABLE QRect getGeometry(const QString &windowId, const QRect &defaultValue) const;
50
55
51 Q_INVOKABLE void saveStage(const QString &appId, int stage);
56 Q_INVOKABLE void saveStage(const QString &appId, int stage);
52 Q_INVOKABLE int getStage(const QString &appId, int defaultValue) const;
57 Q_INVOKABLE int getStage(const QString &appId, int defaultValue) const;
qml/Components/WindowControlButtons.qml−1
@@ -25,7 +25,6 @@ Row {
25 }
25 }
26
26
27 // to be set from outside
27 // to be set from outside
28− property Item target
29 property bool active: false
28 property bool active: false
30 property bool windowIsMaximized: false
29 property bool windowIsMaximized: false
31 property bool closeButtonShown: true
30 property bool closeButtonShown: true
qml/Stages/DecoratedWindow.qml+20
@@ -52,6 +52,8 @@ FocusScope {
52 readonly property int heightIncrement: !counterRotate ? applicationWindow.heightIncrement : applicationWindow.widthIncrement
52 readonly property int heightIncrement: !counterRotate ? applicationWindow.heightIncrement : applicationWindow.widthIncrement
53
53
54 property alias overlayShown: decoration.overlayShown
54 property alias overlayShown: decoration.overlayShown
55+ property alias stageWidth: decoration.stageWidth
56+ property alias stageHeight: decoration.stageHeight
55
57
56 signal closeClicked()
58 signal closeClicked()
57 signal maximizeClicked()
59 signal maximizeClicked()
@@ -60,6 +62,15 @@ FocusScope {
60 signal minimizeClicked()
62 signal minimizeClicked()
61 signal decorationPressed()
63 signal decorationPressed()
62
64
65+ signal fakeMaximizeAnimationRequested(real progress)
66+ signal fakeMaximizeLeftAnimationRequested(real progress)
67+ signal fakeMaximizeRightAnimationRequested(real progress)
68+ signal fakeMaximizeTopLeftAnimationRequested(real progress)
69+ signal fakeMaximizeTopRightAnimationRequested(real progress)
70+ signal fakeMaximizeBottomLeftAnimationRequested(real progress)
71+ signal fakeMaximizeBottomRightAnimationRequested(real progress)
72+ signal stopFakeAnimation()
73+
63 Rectangle {
74 Rectangle {
64 id: selectionHighlight
75 id: selectionHighlight
65 anchors.fill: parent
76 anchors.fill: parent
@@ -101,6 +112,15 @@ FocusScope {
101 onMaximizeVerticallyClicked: { root.decorationPressed(); root.maximizeVerticallyClicked(); }
112 onMaximizeVerticallyClicked: { root.decorationPressed(); root.maximizeVerticallyClicked(); }
102 onMinimizeClicked: root.minimizeClicked();
113 onMinimizeClicked: root.minimizeClicked();
103 onPressed: root.decorationPressed();
114 onPressed: root.decorationPressed();
115+
116+ onFakeMaximizeAnimationRequested: root.fakeMaximizeAnimationRequested(progress)
117+ onFakeMaximizeLeftAnimationRequested: root.fakeMaximizeLeftAnimationRequested(progress)
118+ onFakeMaximizeRightAnimationRequested: root.fakeMaximizeRightAnimationRequested(progress)
119+ onFakeMaximizeTopLeftAnimationRequested: root.fakeMaximizeTopLeftAnimationRequested(progress)
120+ onFakeMaximizeTopRightAnimationRequested: root.fakeMaximizeTopRightAnimationRequested(progress)
121+ onFakeMaximizeBottomLeftAnimationRequested: root.fakeMaximizeBottomLeftAnimationRequested(progress)
122+ onFakeMaximizeBottomRightAnimationRequested: root.fakeMaximizeBottomRightAnimationRequested(progress)
123+ onStopFakeAnimation: root.stopFakeAnimation()
104 }
124 }
105
125
106 ApplicationWindow {
126 ApplicationWindow {
qml/Stages/DesktopStage.qml+140−67
@@ -68,28 +68,27 @@ AbstractStage {
68 id: maximizeWindowShortcut
68 id: maximizeWindowShortcut
69 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Up
69 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Up
70 onTriggered: priv.focusedAppDelegate.maximize()
70 onTriggered: priv.focusedAppDelegate.maximize()
71− active: priv.focusedAppDelegate !== null
71+ active: priv.focusedAppDelegate !== null && priv.focusedAppDelegate.canBeMaximized
72 }
72 }
73
73
74 GlobalShortcut {
74 GlobalShortcut {
75 id: maximizeWindowLeftShortcut
75 id: maximizeWindowLeftShortcut
76 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Left
76 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Left
77 onTriggered: priv.focusedAppDelegate.maximizeLeft()
77 onTriggered: priv.focusedAppDelegate.maximizeLeft()
78− active: priv.focusedAppDelegate !== null
78+ active: priv.focusedAppDelegate !== null && priv.focusedAppDelegate.canBeMaximizedLeftRight
79 }
79 }
80
80
81 GlobalShortcut {
81 GlobalShortcut {
82 id: maximizeWindowRightShortcut
82 id: maximizeWindowRightShortcut
83 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Right
83 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Right
84 onTriggered: priv.focusedAppDelegate.maximizeRight()
84 onTriggered: priv.focusedAppDelegate.maximizeRight()
85− active: priv.focusedAppDelegate !== null
85+ active: priv.focusedAppDelegate !== null && priv.focusedAppDelegate.canBeMaximizedLeftRight
86 }
86 }
87
87
88 GlobalShortcut {
88 GlobalShortcut {
89 id: minimizeRestoreShortcut
89 id: minimizeRestoreShortcut
90 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Down
90 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Down
91− onTriggered: priv.focusedAppDelegate.maximized || priv.focusedAppDelegate.maximizedLeft || priv.focusedAppDelegate.maximizedRight ||
91+ onTriggered: priv.focusedAppDelegate.anyMaximized
92− priv.focusedAppDelegate.maximizedHorizontally || priv.focusedAppDelegate.maximizedVertically
93 ? priv.focusedAppDelegate.restoreFromMaximized() : priv.focusedAppDelegate.minimize()
92 ? priv.focusedAppDelegate.restoreFromMaximized() : priv.focusedAppDelegate.minimize()
94 active: priv.focusedAppDelegate !== null
93 active: priv.focusedAppDelegate !== null
95 }
94 }
@@ -276,7 +275,7 @@ AbstractStage {
276 value: appDelegate.requestedY -
275 value: appDelegate.requestedY -
277 Math.min(appDelegate.requestedY - PanelState.panelHeight,
276 Math.min(appDelegate.requestedY - PanelState.panelHeight,
278 Math.max(0, priv.virtualKeyboardHeight - (appContainer.height - (appDelegate.requestedY + appDelegate.height))))
277 Math.max(0, priv.virtualKeyboardHeight - (appContainer.height - (appDelegate.requestedY + appDelegate.height))))
279− when: root.oskEnabled && appDelegate.focus && appDelegate.state == "normal"
278+ when: root.oskEnabled && appDelegate.focus && (appDelegate.state == "normal" || appDelegate.state == "restored")
280 && SurfaceManager.inputMethodSurface
279 && SurfaceManager.inputMethodSurface
281 && SurfaceManager.inputMethodSurface.state != Mir.HiddenState
280 && SurfaceManager.inputMethodSurface.state != Mir.HiddenState
282 && SurfaceManager.inputMethodSurface.state != Mir.MinimizedState
281 && SurfaceManager.inputMethodSurface.state != Mir.MinimizedState
@@ -291,7 +290,7 @@ AbstractStage {
291 onShellOrientationAngleChanged: {
290 onShellOrientationAngleChanged: {
292 // at this point decoratedWindow.surfaceOrientationAngle is the old shellOrientationAngle
291 // at this point decoratedWindow.surfaceOrientationAngle is the old shellOrientationAngle
293 if (application && application.rotatesWindowContents) {
292 if (application && application.rotatesWindowContents) {
294− if (state == "normal") {
293+ if (state == "normal" || state == "restored") {
295 var angleDiff = decoratedWindow.surfaceOrientationAngle - shellOrientationAngle;
294 var angleDiff = decoratedWindow.surfaceOrientationAngle - shellOrientationAngle;
296 angleDiff = (360 + angleDiff) % 360;
295 angleDiff = (360 + angleDiff) % 360;
297 if (angleDiff === 90 || angleDiff === 270) {
296 if (angleDiff === 90 || angleDiff === 270) {
@@ -317,14 +316,29 @@ AbstractStage {
317 property int requestedWidth: -1
316 property int requestedWidth: -1
318 property int requestedHeight: -1
317 property int requestedHeight: -1
319
318
320− readonly property bool maximized: windowState & WindowStateStorage.WindowStateMaximized
319+ readonly property bool maximized: windowState === WindowStateStorage.WindowStateMaximized
321− readonly property bool maximizedLeft: windowState & WindowStateStorage.WindowStateMaximizedLeft
320+ readonly property bool maximizedLeft: windowState === WindowStateStorage.WindowStateMaximizedLeft
322− readonly property bool maximizedRight: windowState & WindowStateStorage.WindowStateMaximizedRight
321+ readonly property bool maximizedRight: windowState === WindowStateStorage.WindowStateMaximizedRight
323− readonly property bool maximizedHorizontally: windowState & WindowStateStorage.WindowStateMaximizedHorizontally
322+ readonly property bool maximizedHorizontally: windowState === WindowStateStorage.WindowStateMaximizedHorizontally
324− readonly property bool maximizedVertically: windowState & WindowStateStorage.WindowStateMaximizedVertically
323+ readonly property bool maximizedVertically: windowState === WindowStateStorage.WindowStateMaximizedVertically
324+ readonly property bool maximizedTopLeft: windowState === WindowStateStorage.WindowStateMaximizedTopLeft
325+ readonly property bool maximizedTopRight: windowState === WindowStateStorage.WindowStateMaximizedTopRight
326+ readonly property bool maximizedBottomLeft: windowState === WindowStateStorage.WindowStateMaximizedBottomLeft
327+ readonly property bool maximizedBottomRight: windowState === WindowStateStorage.WindowStateMaximizedBottomRight
328+ readonly property bool anyMaximized: maximized || maximizedLeft || maximizedRight || maximizedHorizontally || maximizedVertically ||
329+ maximizedTopLeft || maximizedTopRight || maximizedBottomLeft || maximizedBottomRight
330+
325 readonly property bool minimized: windowState & WindowStateStorage.WindowStateMinimized
331 readonly property bool minimized: windowState & WindowStateStorage.WindowStateMinimized
326 readonly property alias fullscreen: decoratedWindow.fullscreen
332 readonly property alias fullscreen: decoratedWindow.fullscreen
327
333
334+ readonly property bool canBeMaximized: canBeMaximizedHorizontally && canBeMaximizedVertically
335+ readonly property bool canBeMaximizedLeftRight: (maximumWidth == 0 || maximumWidth >= appContainer.width/2) &&
336+ (maximumHeight == 0 || maximumHeight >= appContainer.height)
337+ readonly property bool canBeCornerMaximized: (maximumWidth == 0 || maximumWidth >= appContainer.width/2) &&
338+ (maximumHeight == 0 || maximumHeight >= appContainer.height/2)
339+ readonly property bool canBeMaximizedHorizontally: maximumWidth == 0 || maximumWidth >= appContainer.width
340+ readonly property bool canBeMaximizedVertically: maximumHeight == 0 || maximumHeight >= appContainer.height
341+
328 property int windowState: WindowStateStorage.WindowStateNormal
342 property int windowState: WindowStateStorage.WindowStateNormal
329 property bool animationsEnabled: true
343 property bool animationsEnabled: true
330 property alias title: decoratedWindow.title
344 property alias title: decoratedWindow.title
@@ -342,7 +356,7 @@ AbstractStage {
342 if (spread.state == "altTab") {
356 if (spread.state == "altTab") {
343 spread.cancel();
357 spread.cancel();
344 }
358 }
345− appDelegate.restore();
359+ appDelegate.restore(true /* animated */, appDelegate.windowState);
346 }
360 }
347 Connections {
361 Connections {
348 target: model.surface
362 target: model.surface
@@ -362,9 +376,6 @@ AbstractStage {
362 }
376 }
363
377
364 onFocusChanged: {
378 onFocusChanged: {
365− if (appRepeater.startingUp)
366− return;
367−
368 if (focus) {
379 if (focus) {
369 // If we're orphan (!parent) it means this stage is no longer the current one
380 // If we're orphan (!parent) it means this stage is no longer the current one
370 // and will be deleted shortly. So we should no longer have a say over the model
381 // and will be deleted shortly. So we should no longer have a say over the model
@@ -450,28 +461,34 @@ AbstractStage {
450 animationsEnabled = (animated === undefined) || animated;
461 animationsEnabled = (animated === undefined) || animated;
451 windowState = WindowStateStorage.WindowStateMaximizedVertically;
462 windowState = WindowStateStorage.WindowStateMaximizedVertically;
452 }
463 }
464+ function maximizeTopLeft(animated) {
465+ animationsEnabled = (animated === undefined) || animated;
466+ windowState = WindowStateStorage.WindowStateMaximizedTopLeft;
467+ }
468+ function maximizeTopRight(animated) {
469+ animationsEnabled = (animated === undefined) || animated;
470+ windowState = WindowStateStorage.WindowStateMaximizedTopRight;
471+ }
472+ function maximizeBottomLeft(animated) {
473+ animationsEnabled = (animated === undefined) || animated;
474+ windowState = WindowStateStorage.WindowStateMaximizedBottomLeft;
475+ }
476+ function maximizeBottomRight(animated) {
477+ animationsEnabled = (animated === undefined) || animated;
478+ windowState = WindowStateStorage.WindowStateMaximizedBottomRight;
479+ }
453 function minimize(animated) {
480 function minimize(animated) {
454 animationsEnabled = (animated === undefined) || animated;
481 animationsEnabled = (animated === undefined) || animated;
455 windowState |= WindowStateStorage.WindowStateMinimized; // add the minimized bit
482 windowState |= WindowStateStorage.WindowStateMinimized; // add the minimized bit
456 }
483 }
457 function restoreFromMaximized(animated) {
484 function restoreFromMaximized(animated) {
458 animationsEnabled = (animated === undefined) || animated;
485 animationsEnabled = (animated === undefined) || animated;
459− windowState = WindowStateStorage.WindowStateNormal;
486+ windowState = WindowStateStorage.WindowStateRestored;
460 }
487 }
461− function restore(animated) {
488+ function restore(animated,state) {
462 animationsEnabled = (animated === undefined) || animated;
489 animationsEnabled = (animated === undefined) || animated;
490+ windowState = state || WindowStateStorage.WindowStateRestored;
463 windowState &= ~WindowStateStorage.WindowStateMinimized; // clear the minimized bit
491 windowState &= ~WindowStateStorage.WindowStateMinimized; // clear the minimized bit
464− if (maximized)
465− maximize();
466− else if (maximizedLeft)
467− maximizeLeft();
468− else if (maximizedRight)
469− maximizeRight();
470− else if (maximizedHorizontally)
471− maximizeHorizontally();
472− else if (maximizedVertically)
473− maximizeVertically();
474−
475 focus = true;
492 focus = true;
476 }
493 }
477
494
@@ -488,9 +505,12 @@ AbstractStage {
488 duration: UbuntuAnimation.SnapDuration
505 duration: UbuntuAnimation.SnapDuration
489 }
506 }
490
507
508+ property real restoredX
509+ property real restoredY
510+
491 states: [
511 states: [
492 State {
512 State {
493− name: "fullscreen"; when: decoratedWindow.fullscreen && !appDelegate.minimized
513+ name: "fullscreen"; when: appDelegate.fullscreen && !appDelegate.minimized
494 PropertyChanges {
514 PropertyChanges {
495 target: appDelegate;
515 target: appDelegate;
496 x: rotation == 0 ? 0 : (parent.width - width) / 2 + (shellOrientationAngle == 90 ? -PanelState.panelHeight : PanelState.panelHeight)
516 x: rotation == 0 ? 0 : (parent.width - width) / 2 + (shellOrientationAngle == 90 ? -PanelState.panelHeight : PanelState.panelHeight)
@@ -509,61 +529,94 @@ AbstractStage {
509 }
529 }
510 },
530 },
511 State {
531 State {
512− name: "maximized"; when: appDelegate.maximized && !appDelegate.minimized
532+ name: "restored";
533+ when: appDelegate.windowState == WindowStateStorage.WindowStateRestored
534+ extend: "normal"
513 PropertyChanges {
535 PropertyChanges {
514 target: appDelegate;
536 target: appDelegate;
515− x: root.leftMargin;
537+ requestedX: restoredX;
516− y: 0;
538+ requestedY: restoredY;
517− visuallyMinimized: false;
518− visuallyMaximized: true
519 }
539 }
540+ },
541+ State {
542+ name: "maximized"; when: appDelegate.maximized && !appDelegate.minimized
520 PropertyChanges {
543 PropertyChanges {
521− target: decoratedWindow
544+ target: appDelegate;
545+ requestedX: root.leftMargin;
546+ requestedY: 0;
522 requestedWidth: appContainer.width - root.leftMargin;
547 requestedWidth: appContainer.width - root.leftMargin;
523 requestedHeight: appContainer.height;
548 requestedHeight: appContainer.height;
549+ visuallyMinimized: false;
550+ visuallyMaximized: true
524 }
551 }
525 },
552 },
526 State {
553 State {
527 name: "maximizedLeft"; when: appDelegate.maximizedLeft && !appDelegate.minimized
554 name: "maximizedLeft"; when: appDelegate.maximizedLeft && !appDelegate.minimized
528 PropertyChanges {
555 PropertyChanges {
529 target: appDelegate
556 target: appDelegate
530− x: root.leftMargin
557+ requestedX: root.leftMargin
531− y: PanelState.panelHeight
558+ requestedY: PanelState.panelHeight
532− }
533− PropertyChanges {
534− target: decoratedWindow
535 requestedWidth: (appContainer.width - root.leftMargin)/2
559 requestedWidth: (appContainer.width - root.leftMargin)/2
536 requestedHeight: appContainer.height - PanelState.panelHeight
560 requestedHeight: appContainer.height - PanelState.panelHeight
537 }
561 }
538 },
562 },
539 State {
563 State {
540 name: "maximizedRight"; when: appDelegate.maximizedRight && !appDelegate.minimized
564 name: "maximizedRight"; when: appDelegate.maximizedRight && !appDelegate.minimized
565+ extend: "maximizedLeft"
541 PropertyChanges {
566 PropertyChanges {
542 target: appDelegate;
567 target: appDelegate;
543− x: (appContainer.width + root.leftMargin)/2
568+ requestedX: (appContainer.width + root.leftMargin)/2
544− y: PanelState.panelHeight
545 }
569 }
570+ },
571+ State {
572+ name: "maximizedTopLeft"; when: appDelegate.maximizedTopLeft && !appDelegate.minimized
546 PropertyChanges {
573 PropertyChanges {
547− target: decoratedWindow
574+ target: appDelegate
575+ requestedX: root.leftMargin
576+ requestedY: PanelState.panelHeight
548 requestedWidth: (appContainer.width - root.leftMargin)/2
577 requestedWidth: (appContainer.width - root.leftMargin)/2
549− requestedHeight: appContainer.height - PanelState.panelHeight
578+ requestedHeight: (appContainer.height - PanelState.panelHeight)/2
579+ }
580+ },
581+ State {
582+ name: "maximizedTopRight"; when: appDelegate.maximizedTopRight && !appDelegate.minimized
583+ extend: "maximizedTopLeft"
584+ PropertyChanges {
585+ target: appDelegate
586+ requestedX: (appContainer.width + root.leftMargin)/2
587+ }
588+ },
589+ State {
590+ name: "maximizedBottomLeft"; when: appDelegate.maximizedBottomLeft && !appDelegate.minimized
591+ PropertyChanges {
592+ target: appDelegate
593+ requestedX: root.leftMargin
594+ requestedY: (appContainer.height + PanelState.panelHeight)/2
595+ requestedWidth: (appContainer.width - root.leftMargin)/2
596+ requestedHeight: appContainer.height/2
597+ }
598+ },
599+ State {
600+ name: "maximizedBottomRight"; when: appDelegate.maximizedBottomRight && !appDelegate.minimized
601+ extend: "maximizedBottomLeft"
602+ PropertyChanges {
603+ target: appDelegate
604+ requestedX: (appContainer.width + root.leftMargin)/2
550 }
605 }
551 },
606 },
552 State {
607 State {
553 name: "maximizedHorizontally"; when: appDelegate.maximizedHorizontally && !appDelegate.minimized
608 name: "maximizedHorizontally"; when: appDelegate.maximizedHorizontally && !appDelegate.minimized
554− PropertyChanges { target: appDelegate; x: root.leftMargin }
609+ PropertyChanges { target: appDelegate; requestedX: root.leftMargin; requestedY: requestedY; requestedWidth: appContainer.width - root.leftMargin }
555− PropertyChanges { target: decoratedWindow; requestedWidth: appContainer.width - root.leftMargin }
556 },
610 },
557 State {
611 State {
558 name: "maximizedVertically"; when: appDelegate.maximizedVertically && !appDelegate.minimized
612 name: "maximizedVertically"; when: appDelegate.maximizedVertically && !appDelegate.minimized
559− PropertyChanges { target: appDelegate; y: PanelState.panelHeight }
613+ PropertyChanges { target: appDelegate; requestedX: requestedX; requestedY: PanelState.panelHeight; requestedHeight: appContainer.height - PanelState.panelHeight }
560− PropertyChanges { target: decoratedWindow; requestedHeight: appContainer.height - PanelState.panelHeight }
561 },
614 },
562 State {
615 State {
563 name: "minimized"; when: appDelegate.minimized
616 name: "minimized"; when: appDelegate.minimized
564 PropertyChanges {
617 PropertyChanges {
565 target: appDelegate;
618 target: appDelegate;
566− x: -appDelegate.width / 2;
619+ requestedX: -appDelegate.width / 2;
567 scale: units.gu(5) / appDelegate.width;
620 scale: units.gu(5) / appDelegate.width;
568 opacity: 0;
621 opacity: 0;
569 visuallyMinimized: true;
622 visuallyMinimized: true;
@@ -573,21 +626,17 @@ AbstractStage {
573 ]
626 ]
574 transitions: [
627 transitions: [
575 Transition {
628 Transition {
576− to: "normal"
629+ to: "normal,restored"
577 enabled: appDelegate.animationsEnabled
630 enabled: appDelegate.animationsEnabled
578 PropertyAction { target: appDelegate; properties: "visuallyMinimized,visuallyMaximized" }
631 PropertyAction { target: appDelegate; properties: "visuallyMinimized,visuallyMaximized" }
579− UbuntuNumberAnimation { target: appDelegate; properties: "x,y,opacity,requestedWidth,requestedHeight,scale"; duration: UbuntuAnimation.FastDuration }
632+ UbuntuNumberAnimation { target: appDelegate; properties: "requestedX,requestedY,opacity,scale,requestedWidth,requestedHeight" }
580− UbuntuNumberAnimation { target: decoratedWindow; properties: "requestedWidth,requestedHeight"; duration: UbuntuAnimation.FastDuration }
581 },
633 },
582 Transition {
634 Transition {
583 to: "minimized"
635 to: "minimized"
584 enabled: appDelegate.animationsEnabled
636 enabled: appDelegate.animationsEnabled
585 PropertyAction { target: appDelegate; property: "visuallyMaximized" }
637 PropertyAction { target: appDelegate; property: "visuallyMaximized" }
586 SequentialAnimation {
638 SequentialAnimation {
587− ParallelAnimation {
588− UbuntuNumberAnimation { target: appDelegate; properties: "x,y,opacity,scale"; duration: UbuntuAnimation.FastDuration }
639+ UbuntuNumberAnimation { target: appDelegate; properties: "requestedX,requestedY,opacity,scale,requestedWidth,requestedHeight" }
589− UbuntuNumberAnimation { target: decoratedWindow; properties: "requestedWidth,requestedHeight"; duration: UbuntuAnimation.FastDuration }
590− }
591 PropertyAction { target: appDelegate; property: "visuallyMinimized" }
640 PropertyAction { target: appDelegate; property: "visuallyMinimized" }
592 ScriptAction {
641 ScriptAction {
593 script: {
642 script: {
@@ -604,10 +653,8 @@ AbstractStage {
604 enabled: appDelegate.animationsEnabled
653 enabled: appDelegate.animationsEnabled
605 PropertyAction { target: appDelegate; property: "visuallyMinimized" }
654 PropertyAction { target: appDelegate; property: "visuallyMinimized" }
606 SequentialAnimation {
655 SequentialAnimation {
607− ParallelAnimation {
656+ ScriptAction { script: { fakeRectangle.stop(); } }
608− UbuntuNumberAnimation { target: appDelegate; properties: "x,y,opacity,scale"; duration: UbuntuAnimation.FastDuration }
657+ UbuntuNumberAnimation { target: appDelegate; properties: "requestedX,requestedY,opacity,scale,requestedWidth,requestedHeight" }
609− UbuntuNumberAnimation { target: decoratedWindow; properties: "requestedWidth,requestedHeight"; duration: UbuntuAnimation.FastDuration }
610− }
611 PropertyAction { target: appDelegate; property: "visuallyMaximized" }
658 PropertyAction { target: appDelegate; property: "visuallyMaximized" }
612 }
659 }
613 }
660 }
@@ -674,27 +721,45 @@ AbstractStage {
674 surface: model.surface
721 surface: model.surface
675 active: appDelegate.focus
722 active: appDelegate.focus
676 focus: true
723 focus: true
677− maximizeButtonShown: (maximumWidth == 0 || maximumWidth >= appContainer.width) &&
724+ maximizeButtonShown: appDelegate.canBeMaximized
678− (maximumHeight == 0 || maximumHeight >= appContainer.height)
679 overlayShown: touchControls.overlayShown
725 overlayShown: touchControls.overlayShown
726+ stageWidth: appContainer.width
727+ stageHeight: appContainer.height
680
728
681 requestedWidth: appDelegate.requestedWidth
729 requestedWidth: appDelegate.requestedWidth
682 requestedHeight: appDelegate.requestedHeight
730 requestedHeight: appDelegate.requestedHeight
683
731
684 onCloseClicked: { appDelegate.close(); }
732 onCloseClicked: { appDelegate.close(); }
685− onMaximizeClicked: appDelegate.maximized || appDelegate.maximizedLeft || appDelegate.maximizedRight
686− || appDelegate.maximizedHorizontally || appDelegate.maximizedVertically
687− ? appDelegate.restoreFromMaximized() : appDelegate.maximize()
733+ onMaximizeClicked: appDelegate.anyMaximized ? appDelegate.restoreFromMaximized() : appDelegate.maximize();
688 onMaximizeHorizontallyClicked: appDelegate.maximizedHorizontally ? appDelegate.restoreFromMaximized() : appDelegate.maximizeHorizontally()
734 onMaximizeHorizontallyClicked: appDelegate.maximizedHorizontally ? appDelegate.restoreFromMaximized() : appDelegate.maximizeHorizontally()
689 onMaximizeVerticallyClicked: appDelegate.maximizedVertically ? appDelegate.restoreFromMaximized() : appDelegate.maximizeVertically()
735 onMaximizeVerticallyClicked: appDelegate.maximizedVertically ? appDelegate.restoreFromMaximized() : appDelegate.maximizeVertically()
690 onMinimizeClicked: appDelegate.minimize()
736 onMinimizeClicked: appDelegate.minimize()
691 onDecorationPressed: { appDelegate.focus = true; }
737 onDecorationPressed: { appDelegate.focus = true; }
738+
739+ onFakeMaximizeAnimationRequested: if (!appDelegate.maximized) fakeRectangle.maximize(progress)
740+ onFakeMaximizeLeftAnimationRequested: if (!appDelegate.maximizedLeft) fakeRectangle.maximizeLeft(progress)
741+ onFakeMaximizeRightAnimationRequested: if (!appDelegate.maximizedRight) fakeRectangle.maximizeRight(progress)
742+ onFakeMaximizeTopLeftAnimationRequested: if (!appDelegate.maximizedTopLeft) fakeRectangle.maximizeTopLeft(progress);
743+ onFakeMaximizeTopRightAnimationRequested: if (!appDelegate.maximizedTopRight) fakeRectangle.maximizeTopRight(progress);
744+ onFakeMaximizeBottomLeftAnimationRequested: if (!appDelegate.maximizedBottomLeft) fakeRectangle.maximizeBottomLeft(progress);
745+ onFakeMaximizeBottomRightAnimationRequested: if (!appDelegate.maximizedBottomRight) fakeRectangle.maximizeBottomRight(progress);
746+ onStopFakeAnimation: fakeRectangle.stop();
692 }
747 }
693
748
694 WindowControlsOverlay {
749 WindowControlsOverlay {
695 id: touchControls
750 id: touchControls
696− anchors.fill: appDelegate
697 target: appDelegate
751 target: appDelegate
752+ stageWidth: appContainer.width
753+ stageHeight: appContainer.height
754+
755+ onFakeMaximizeAnimationRequested: decoratedWindow.fakeMaximizeAnimationRequested(progress)
756+ onFakeMaximizeLeftAnimationRequested: decoratedWindow.fakeMaximizeLeftAnimationRequested(progress)
757+ onFakeMaximizeRightAnimationRequested: decoratedWindow.fakeMaximizeRightAnimationRequested(progress)
758+ onFakeMaximizeTopLeftAnimationRequested: decoratedWindow.fakeMaximizeTopLeftAnimationRequested(progress)
759+ onFakeMaximizeTopRightAnimationRequested: decoratedWindow.fakeMaximizeTopRightAnimationRequested(progress)
760+ onFakeMaximizeBottomLeftAnimationRequested: decoratedWindow.fakeMaximizeBottomLeftAnimationRequested(progress)
761+ onFakeMaximizeBottomRightAnimationRequested: decoratedWindow.fakeMaximizeBottomRightAnimationRequested(progress)
762+ onStopFakeAnimation: decoratedWindow.stopFakeAnimation()
698 }
763 }
699
764
700 WindowedFullscreenPolicy {
765 WindowedFullscreenPolicy {
@@ -706,6 +771,14 @@ AbstractStage {
706 }
771 }
707 }
772 }
708
773
774+ FakeMaximizeDelegate {
775+ id: fakeRectangle
776+ target: priv.focusedAppDelegate
777+ leftMargin: root.leftMargin
778+ appContainerWidth: appContainer.width
779+ appContainerHeight: appContainer.height
780+ }
781+
709 EdgeBarrier {
782 EdgeBarrier {
710 id: edgeBarrier
783 id: edgeBarrier
711
784
qml/Stages/FakeMaximizeDelegate.qmlnew file+186
@@ -0,0 +1,186 @@
1+/*
2+ * Copyright (C) 2016 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 Ubuntu.Components 1.3
19+import "../Components/PanelState"
20+
21+Rectangle {
22+ id: fakeRectangle
23+ visible: opacity > 0 // we go from 0.2 to 0.6
24+ enabled: visible
25+ color: "#ffffff"
26+ border.width: units.dp(1)
27+ border.color: "#99ffffff"
28+ opacity: 0
29+
30+ property int edge: -1 // Item.TransformOrigin
31+ property var target // appDelegate
32+ property int leftMargin
33+ property real appContainerWidth
34+ property real appContainerHeight
35+
36+ QtObject {
37+ id: priv
38+
39+ property real progress: 0
40+
41+ function setup(edge) {
42+ fakeRectangle.x = target.x;
43+ fakeRectangle.y = target.y;
44+ fakeRectangle.width = target.width;
45+ fakeRectangle.height = target.height;
46+ fakeRectangle.edge = edge;
47+ fakeRectangle.transformOrigin = edge;
48+ }
49+
50+ function projectOpacity(progress) {
51+ // progress falls into [0,1], opacity moves in [0.2,0.6]
52+ fakeRectangle.opacity = MathUtils.clampAndProject(progress, 0.0, 1.0, 0.2, 0.6);
53+ }
54+
55+ function projectScale(progress) {
56+ // progress falls into [0,1], scale moves in [1,2] as a hint, when under 30%
57+ fakeRectangle.scale = MathUtils.clampAndProject(progress, 0, 1, 1, 2);
58+ }
59+
60+ function processAnimation(progress, animation) {
61+ if (priv.progress >= 0.3) {
62+ fakeRectangle.scale = 1;
63+ animation.start();
64+ } else if (progress > priv.progress) {
65+ priv.progress += progress;
66+ priv.projectScale(progress);
67+ }
68+ priv.projectOpacity(progress);
69+ }
70+ }
71+
72+ function stop() {
73+ print("!!! Stop")
74+ opacity = 0;
75+ edge = -1;
76+ priv.progress = 0;
77+ }
78+
79+ function maximize(progress) {
80+ if (fakeRectangle.edge != Item.Top) {
81+ priv.setup(Item.Top);
82+ }
83+ priv.processAnimation(progress, fakeMaximizeAnimation);
84+ }
85+
86+ function maximizeLeft(progress) {
87+ if (fakeRectangle.edge != Item.Left) {
88+ priv.setup(Item.Left);
89+ }
90+ priv.processAnimation(progress, fakeMaximizeLeftAnimation);
91+ }
92+
93+ function maximizeRight(progress) {
94+ if (fakeRectangle.edge != Item.Right) {
95+ priv.setup(Item.Right);
96+ }
97+ priv.processAnimation(progress, fakeMaximizeRightAnimation);
98+ }
99+
100+ function maximizeTopLeft(progress) {
101+ if (fakeRectangle.edge != Item.TopLeft) {
102+ priv.setup(Item.TopLeft);
103+ }
104+ priv.processAnimation(progress, fakeMaximizeTopLeftAnimation);
105+ }
106+
107+ function maximizeTopRight(progress) {
108+ if (fakeRectangle.edge != Item.TopRight) {
109+ priv.setup(Item.TopRight);
110+ }
111+ priv.processAnimation(progress, fakeMaximizeTopRightAnimation);
112+ }
113+
114+ function maximizeBottomLeft(progress) {
115+ if (fakeRectangle.edge != Item.BottomLeft) {
116+ priv.setup(Item.BottomLeft);
117+ }
118+ priv.processAnimation(progress, fakeMaximizeBottomLeftAnimation);
119+ }
120+
121+ function maximizeBottomRight(progress) {
122+ if (fakeRectangle.edge != Item.BottomRight) {
123+ priv.setup(Item.BottomRight);
124+ }
125+ priv.processAnimation(progress, fakeMaximizeBottomRightAnimation);
126+ }
127+
128+ Behavior on opacity { UbuntuNumberAnimation { duration: UbuntuAnimation.BriskDuration } }
129+ Behavior on scale { UbuntuNumberAnimation { duration: UbuntuAnimation.BriskDuration } }
130+
131+ ParallelAnimation {
132+ id: fakeMaximizeAnimation
133+ UbuntuNumberAnimation { target: fakeRectangle; properties: "x"; duration: UbuntuAnimation.BriskDuration; to: leftMargin }
134+ UbuntuNumberAnimation { target: fakeRectangle; properties: "y"; duration: UbuntuAnimation.BriskDuration; to: PanelState.panelHeight }
135+ UbuntuNumberAnimation { target: fakeRectangle; properties: "width"; duration: UbuntuAnimation.BriskDuration; to: appContainerWidth - leftMargin }
136+ UbuntuNumberAnimation { target: fakeRectangle; properties: "height"; duration: UbuntuAnimation.BriskDuration; to: appContainerHeight }
137+ }
138+
139+ ParallelAnimation {
140+ id: fakeMaximizeLeftAnimation
141+ UbuntuNumberAnimation { target: fakeRectangle; properties: "x"; duration: UbuntuAnimation.BriskDuration; to: leftMargin }
142+ UbuntuNumberAnimation { target: fakeRectangle; properties: "y"; duration: UbuntuAnimation.BriskDuration; to: PanelState.panelHeight }
143+ UbuntuNumberAnimation { target: fakeRectangle; properties: "width"; duration: UbuntuAnimation.BriskDuration; to: (appContainerWidth - leftMargin)/2 }
144+ UbuntuNumberAnimation { target: fakeRectangle; properties: "height"; duration: UbuntuAnimation.BriskDuration; to: appContainerHeight - PanelState.panelHeight }
145+ }
146+
147+ ParallelAnimation {
148+ id: fakeMaximizeRightAnimation
149+ UbuntuNumberAnimation { target: fakeRectangle; properties: "x"; duration: UbuntuAnimation.BriskDuration; to: (appContainerWidth + leftMargin)/2 }
150+ UbuntuNumberAnimation { target: fakeRectangle; properties: "y"; duration: UbuntuAnimation.BriskDuration; to: PanelState.panelHeight }
151+ UbuntuNumberAnimation { target: fakeRectangle; properties: "width"; duration: UbuntuAnimation.BriskDuration; to: (appContainerWidth - leftMargin)/2 }
152+ UbuntuNumberAnimation { target: fakeRectangle; properties: "height"; duration: UbuntuAnimation.BriskDuration; to: appContainerHeight - PanelState.panelHeight }
153+ }
154+
155+ ParallelAnimation {
156+ id: fakeMaximizeTopLeftAnimation
157+ UbuntuNumberAnimation { target: fakeRectangle; properties: "x"; duration: UbuntuAnimation.BriskDuration; to: leftMargin }
158+ UbuntuNumberAnimation { target: fakeRectangle; properties: "y"; duration: UbuntuAnimation.BriskDuration; to: PanelState.panelHeight }
159+ UbuntuNumberAnimation { target: fakeRectangle; properties: "width"; duration: UbuntuAnimation.BriskDuration; to: (appContainerWidth - leftMargin)/2 }
160+ UbuntuNumberAnimation { target: fakeRectangle; properties: "height"; duration: UbuntuAnimation.BriskDuration; to: (appContainerHeight - PanelState.panelHeight)/2 }
161+ }
162+
163+ ParallelAnimation {
164+ id: fakeMaximizeTopRightAnimation
165+ UbuntuNumberAnimation { target: fakeRectangle; properties: "x"; duration: UbuntuAnimation.BriskDuration; to: (appContainerWidth + leftMargin)/2 }
166+ UbuntuNumberAnimation { target: fakeRectangle; properties: "y"; duration: UbuntuAnimation.BriskDuration; to: PanelState.panelHeight }
167+ UbuntuNumberAnimation { target: fakeRectangle; properties: "width"; duration: UbuntuAnimation.BriskDuration; to: (appContainerWidth - leftMargin)/2 }
168+ UbuntuNumberAnimation { target: fakeRectangle; properties: "height"; duration: UbuntuAnimation.BriskDuration; to: (appContainerHeight - PanelState.panelHeight)/2 }
169+ }
170+
171+ ParallelAnimation {
172+ id: fakeMaximizeBottomLeftAnimation
173+ UbuntuNumberAnimation { target: fakeRectangle; properties: "x"; duration: UbuntuAnimation.BriskDuration; to: leftMargin }
174+ UbuntuNumberAnimation { target: fakeRectangle; properties: "y"; duration: UbuntuAnimation.BriskDuration; to: (appContainerHeight + PanelState.panelHeight)/2 }
175+ UbuntuNumberAnimation { target: fakeRectangle; properties: "width"; duration: UbuntuAnimation.BriskDuration; to: (appContainerWidth - leftMargin)/2 }
176+ UbuntuNumberAnimation { target: fakeRectangle; properties: "height"; duration: UbuntuAnimation.BriskDuration; to: appContainerHeight/2 }
177+ }
178+
179+ ParallelAnimation {
180+ id: fakeMaximizeBottomRightAnimation
181+ UbuntuNumberAnimation { target: fakeRectangle; properties: "x"; duration: UbuntuAnimation.BriskDuration; to: (appContainerWidth + leftMargin)/2 }
182+ UbuntuNumberAnimation { target: fakeRectangle; properties: "y"; duration: UbuntuAnimation.BriskDuration; to: (appContainerHeight + PanelState.panelHeight)/2 }
183+ UbuntuNumberAnimation { target: fakeRectangle; properties: "width"; duration: UbuntuAnimation.BriskDuration; to: (appContainerWidth - leftMargin)/2 }
184+ UbuntuNumberAnimation { target: fakeRectangle; properties: "height"; duration: UbuntuAnimation.BriskDuration; to: appContainerHeight/2 }
185+ }
186+}
qml/Stages/WindowControlsOverlay.qml+158−16
@@ -23,13 +23,25 @@ import "../Components/PanelState"
23Item {
23Item {
24 id: root
24 id: root
25 enabled: target && !target.fullscreen
25 enabled: target && !target.fullscreen
26+ anchors.fill: target
26
27
27 // to be set from outside
28 // to be set from outside
28 property Item target // appDelegate
29 property Item target // appDelegate
30+ property int stageWidth
31+ property int stageHeight
29
32
30 // to be read from outside
33 // to be read from outside
31 readonly property alias overlayShown: overlay.visible
34 readonly property alias overlayShown: overlay.visible
32
35
36+ signal fakeMaximizeAnimationRequested(real progress)
37+ signal fakeMaximizeLeftAnimationRequested(real progress)
38+ signal fakeMaximizeRightAnimationRequested(real progress)
39+ signal fakeMaximizeTopLeftAnimationRequested(real progress)
40+ signal fakeMaximizeTopRightAnimationRequested(real progress)
41+ signal fakeMaximizeBottomLeftAnimationRequested(real progress)
42+ signal fakeMaximizeBottomRightAnimationRequested(real progress)
43+ signal stopFakeAnimation()
44+
33 TouchGestureArea {
45 TouchGestureArea {
34 id: gestureArea
46 id: gestureArea
35 anchors.fill: parent
47 anchors.fill: parent
@@ -79,6 +91,26 @@ Item {
79 property bool dragging
91 property bool dragging
80
92
81 readonly property var resizeArea: root.target && root.target.resizeArea ? root.target.resizeArea : null
93 readonly property var resizeArea: root.target && root.target.resizeArea ? root.target.resizeArea : null
94+ readonly property bool ensureWindow: root.target.state == "normal" || root.target.state == "restored"
95+
96+ readonly property int triggerArea: units.gu(8)
97+ property bool nearLeftEdge: target.maximizedLeft
98+ property bool nearTopEdge: target.maximized
99+ property bool nearRightEdge: target.maximizedRight
100+ property bool nearTopLeftCorner: target.maximizedTopLeft
101+ property bool nearTopRightCorner: target.maximizedTopRight
102+ property bool nearBottomLeftCorner: target.maximizedBottomLeft
103+ property bool nearBottomRightCorner: target.maximizedBottomRight
104+
105+ function resetEdges() {
106+ nearLeftEdge = false;
107+ nearRightEdge = false;
108+ nearTopEdge = false;
109+ nearTopLeftCorner = false;
110+ nearTopRightCorner = false;
111+ nearBottomLeftCorner = false;
112+ nearBottomRightCorner = false;
113+ }
82
114
83 function handlePressedChanged(pressed, mouseX, mouseY) {
115 function handlePressedChanged(pressed, mouseX, mouseY) {
84 if (pressed) {
116 if (pressed) {
@@ -92,12 +124,31 @@ Item {
92 }
124 }
93
125
94 function handlePositionChanged(mouseX, mouseY) {
126 function handlePositionChanged(mouseX, mouseY) {
95− if (priv.dragging) {
96− var pos = mapToItem(root.target.parent, mouseX, mouseY);
127+ var pos = mapToItem(root.target.parent, mouseX, mouseY);
97− root.target.requestedX = Math.round(pos.x - priv.distanceX);
128+ root.target.requestedX = Math.round(pos.x - priv.distanceX);
98− root.target.requestedY = Math.round(Math.max(pos.y - priv.distanceY, PanelState.panelHeight));
129+ root.target.requestedY = Math.round(Math.max(pos.y - priv.distanceY, PanelState.panelHeight));
130+ }
131+
132+ function saveRestoredPos(x, y) {
133+ var pos = mapToItem(root.target, x, y);
134+ target.restoredX = Math.round(pos.x);
135+ target.restoredY = Math.round(pos.y);
99− }
136+ }
137+
138+ // return the progress of mouse pointer movement from 0 to 1 within a corner square of the screen
139+ // 0 -> before the mouse enters the square
140+ // 1 -> mouse is exactly in the very corner
141+ // a is the corner, b is the mouse pos
142+ function progressInCorner(ax, ay, bx, by) {
143+ // distance of two points, a and b, in pixels
144+ var distance = Math.sqrt(Math.pow(bx-ax, 2) + Math.pow(by-ay, 2));
145+ // length of the triggerArea square diagonal
146+ var diagLength = Math.sqrt(2 * priv.triggerArea * priv.triggerArea);
147+ var ratio = 1 - (distance / diagLength);
148+ return bx > 0 && bx <= stageWidth && by > 0 && by <= stageHeight ? ratio : 1; // everything "outside" of our square from the center is 1
100 }
149 }
150+
151+ property real progress: 0
101 }
152 }
102
153
103 // the visual overlay
154 // the visual overlay
@@ -113,8 +164,6 @@ Item {
113 UbuntuNumberAnimation {}
164 UbuntuNumberAnimation {}
114 }
165 }
115
166
116− readonly property bool anyMaximized: target && (target.maximized || target.maximizedLeft || target.maximizedRight)
117−
118 Image {
167 Image {
119 source: "graphics/arrows-centre.png"
168 source: "graphics/arrows-centre.png"
120 width: units.gu(10)
169 width: units.gu(10)
@@ -133,7 +182,98 @@ Item {
133 cursorShape: priv.dragging ? Qt.ClosedHandCursor : (overlay.visible ? Qt.OpenHandCursor : Qt.ArrowCursor)
182 cursorShape: priv.dragging ? Qt.ClosedHandCursor : (overlay.visible ? Qt.OpenHandCursor : Qt.ArrowCursor)
134
183
135 onPressedChanged: priv.handlePressedChanged(pressed, mouseX, mouseY)
184 onPressedChanged: priv.handlePressedChanged(pressed, mouseX, mouseY)
185+ onPositionChanged: {
186+ if (priv.dragging) {
136− onPositionChanged: priv.handlePositionChanged(mouseX, mouseY)
187+ priv.handlePositionChanged(mouseX, mouseY);
188+
189+ var globalPos = mapToItem(null, mouseX, mouseY);
190+ var globalX = globalPos.x;
191+ var globalY = globalPos.y;
192+ if (globalX < priv.triggerArea && globalY < PanelState.panelHeight) { // top left
193+ if (target.canBeCornerMaximized) {
194+ priv.progress = priv.progressInCorner(0, PanelState.panelHeight, globalX, globalY);
195+ priv.resetEdges();
196+ priv.nearTopLeftCorner = true;
197+ root.fakeMaximizeTopLeftAnimationRequested(priv.progress);
198+ }
199+ } else if (globalX > stageWidth - priv.triggerArea && globalY < PanelState.panelHeight) { // top right
200+ if (target.canBeCornerMaximized) {
201+ priv.progress = priv.progressInCorner(stageWidth, PanelState.panelHeight, globalX, globalY);
202+ priv.resetEdges();
203+ priv.nearTopRightCorner = true;
204+ root.fakeMaximizeTopRightAnimationRequested(priv.progress);
205+ }
206+ } else if (globalX < priv.triggerArea && globalY > stageHeight - priv.triggerArea) { // bottom left
207+ if (target.canBeCornerMaximized) {
208+ priv.progress = priv.progressInCorner(0, stageHeight, globalX, globalY);
209+ priv.resetEdges();
210+ priv.nearBottomLeftCorner = true;
211+ root.fakeMaximizeBottomLeftAnimationRequested(priv.progress);
212+ }
213+ } else if (globalX > stageWidth - priv.triggerArea && globalY > stageHeight - priv.triggerArea) { // bottom right
214+ if (target.canBeCornerMaximized) {
215+ priv.progress = priv.progressInCorner(stageWidth, stageHeight, globalX, globalY);
216+ priv.resetEdges();
217+ priv.nearBottomRightCorner = true;
218+ root.fakeMaximizeBottomRightAnimationRequested(priv.progress);
219+ }
220+ } else if (globalX < priv.triggerArea) { // left
221+ if (target.canBeMaximizedLeftRight) {
222+ priv.progress = MathUtils.clampAndProject(globalX, priv.triggerArea, 0, 0, 1);
223+ priv.resetEdges();
224+ priv.nearLeftEdge = true;
225+ root.fakeMaximizeLeftAnimationRequested(priv.progress);
226+ }
227+ } else if (globalX > stageWidth - priv.triggerArea) { // right
228+ if (target.canBeMaximizedLeftRight) {
229+ priv.progress = MathUtils.clampAndProject(globalX, stageWidth - priv.triggerArea, stageWidth, 0, 1);
230+ priv.resetEdges();
231+ priv.nearRightEdge = true;
232+ root.fakeMaximizeRightAnimationRequested(priv.progress);
233+ }
234+ } else if (globalY < PanelState.panelHeight) { // top
235+ if (target.canBeMaximized) {
236+ priv.progress = MathUtils.clampAndProject(globalY, Math.max(PanelState.panelHeight, priv.triggerArea), 0, 0, 1);
237+ priv.resetEdges();
238+ priv.nearTopEdge = true;
239+ root.fakeMaximizeAnimationRequested(priv.progress);
240+ }
241+ } else if (priv.nearLeftEdge || priv.nearRightEdge || priv.nearTopEdge || priv.nearTopLeftCorner || priv.nearTopRightCorner ||
242+ priv.nearBottomLeftCorner || priv.nearBottomRightCorner) {
243+ print("!!! Exited")
244+ priv.progress = 0;
245+ priv.resetEdges();
246+ root.stopFakeAnimation();
247+ } else if (target.anyMaximized) {
248+ priv.progress = 0;
249+ target.restoreFromMaximized();
250+ }
251+ }
252+ }
253+ onReleased: {
254+ print("Mouse released (left/top/right)", priv.nearLeftEdge, priv.nearTopEdge, priv.nearRightEdge)
255+ if (mouse.button == Qt.LeftButton && (target.state == "normal" || target.state == "restored") && priv.progress == 0) {
256+ priv.saveRestoredPos(target.x, target.y);
257+ } else if (priv.progress < 0.3) { // cancel the preview shape if under 30%
258+ priv.progress = 0;
259+ priv.resetEdges();
260+ root.stopFakeAnimation();
261+ } else if (priv.nearLeftEdge) {
262+ target.maximizeLeft();
263+ } else if (priv.nearTopEdge) {
264+ target.maximize();
265+ } else if (priv.nearRightEdge) {
266+ target.maximizeRight();
267+ } else if (priv.nearTopLeftCorner) {
268+ target.maximizeTopLeft();
269+ } else if (priv.nearTopRightCorner) {
270+ target.maximizeTopRight();
271+ } else if (priv.nearBottomLeftCorner) {
272+ target.maximizeBottomLeft();
273+ } else if (priv.nearBottomRightCorner) {
274+ target.maximizeBottomRight();
275+ }
276+ }
137 }
277 }
138
278
139 // dismiss area
279 // dismiss area
@@ -157,7 +297,7 @@ Item {
157 ResizeGrip { // top left
297 ResizeGrip { // top left
158 anchors.horizontalCenter: parent.left
298 anchors.horizontalCenter: parent.left
159 anchors.verticalCenter: parent.top
299 anchors.verticalCenter: parent.top
160− visible: target && !overlay.anyMaximized && !target.maximizedHorizontally && !target.maximizedVertically
300+ visible: priv.ensureWindow || target.maximizedBottomRight
161 resizeTarget: priv.resizeArea
301 resizeTarget: priv.resizeArea
162 }
302 }
163
303
@@ -165,7 +305,7 @@ Item {
165 anchors.horizontalCenter: parent.horizontalCenter
305 anchors.horizontalCenter: parent.horizontalCenter
166 anchors.verticalCenter: parent.top
306 anchors.verticalCenter: parent.top
167 rotation: 45
307 rotation: 45
168− visible: target && !overlay.anyMaximized && !target.maximizedVertically
308+ visible: priv.ensureWindow || target.maximizedHorizontally || target.maximizedBottomLeft || target.maximizedBottomRight
169 resizeTarget: priv.resizeArea
309 resizeTarget: priv.resizeArea
170 }
310 }
171
311
@@ -173,7 +313,7 @@ Item {
173 anchors.horizontalCenter: parent.right
313 anchors.horizontalCenter: parent.right
174 anchors.verticalCenter: parent.top
314 anchors.verticalCenter: parent.top
175 rotation: 90
315 rotation: 90
176− visible: target && !overlay.anyMaximized && !target.maximizedHorizontally && !target.maximizedVertically
316+ visible: priv.ensureWindow || target.maximizedBottomLeft
177 resizeTarget: priv.resizeArea
317 resizeTarget: priv.resizeArea
178 }
318 }
179
319
@@ -181,14 +321,15 @@ Item {
181 anchors.horizontalCenter: parent.right
321 anchors.horizontalCenter: parent.right
182 anchors.verticalCenter: parent.verticalCenter
322 anchors.verticalCenter: parent.verticalCenter
183 rotation: 135
323 rotation: 135
184− visible: target && !target.maximizedRight && !target.maximized && !target.maximizedHorizontally
324+ visible: priv.ensureWindow || target.maximizedVertically || target.maximizedLeft ||
325+ target.maximizedTopLeft || target.maximizedBottomLeft
185 resizeTarget: priv.resizeArea
326 resizeTarget: priv.resizeArea
186 }
327 }
187
328
188 ResizeGrip { // bottom right
329 ResizeGrip { // bottom right
189 anchors.horizontalCenter: parent.right
330 anchors.horizontalCenter: parent.right
190 anchors.verticalCenter: parent.bottom
331 anchors.verticalCenter: parent.bottom
191− visible: target && !overlay.anyMaximized && !target.maximizedHorizontally && !target.maximizedVertically
332+ visible: priv.ensureWindow || target.maximizedTopLeft
192 resizeTarget: priv.resizeArea
333 resizeTarget: priv.resizeArea
193 }
334 }
194
335
@@ -196,7 +337,7 @@ Item {
196 anchors.horizontalCenter: parent.horizontalCenter
337 anchors.horizontalCenter: parent.horizontalCenter
197 anchors.verticalCenter: parent.bottom
338 anchors.verticalCenter: parent.bottom
198 rotation: 45
339 rotation: 45
199− visible: target && !overlay.anyMaximized && !target.maximizedVertically
340+ visible: priv.ensureWindow || target.maximizedHorizontally || target.maximizedTopLeft || target.maximizedTopRight
200 resizeTarget: priv.resizeArea
341 resizeTarget: priv.resizeArea
201 }
342 }
202
343
@@ -204,7 +345,7 @@ Item {
204 anchors.horizontalCenter: parent.left
345 anchors.horizontalCenter: parent.left
205 anchors.verticalCenter: parent.bottom
346 anchors.verticalCenter: parent.bottom
206 rotation: 90
347 rotation: 90
207− visible: target && !overlay.anyMaximized && !target.maximizedHorizontally && !target.maximizedVertically
348+ visible: priv.ensureWindow || target.maximizedTopRight
208 resizeTarget: priv.resizeArea
349 resizeTarget: priv.resizeArea
209 }
350 }
210
351
@@ -212,7 +353,8 @@ Item {
212 anchors.horizontalCenter: parent.left
353 anchors.horizontalCenter: parent.left
213 anchors.verticalCenter: parent.verticalCenter
354 anchors.verticalCenter: parent.verticalCenter
214 rotation: 135
355 rotation: 135
215− visible: target && !target.maximizedLeft && !target.maximized && !target.maximizedHorizontally
356+ visible: priv.ensureWindow || target.maximizedVertically || target.maximizedRight ||
357+ target.maximizedTopRight || target.maximizedBottomRight
216 resizeTarget: priv.resizeArea
358 resizeTarget: priv.resizeArea
217 }
359 }
218 }
360 }
qml/Stages/WindowDecoration.qml+150−8
@@ -24,12 +24,16 @@ MouseArea {
24 id: root
24 id: root
25 clip: true
25 clip: true
26
26
27− property Item target
27+ property Item target // appDelegate
28 property alias title: titleLabel.text
28 property alias title: titleLabel.text
29 property alias maximizeButtonShown: buttons.maximizeButtonShown
29 property alias maximizeButtonShown: buttons.maximizeButtonShown
30 property bool active: false
30 property bool active: false
31− acceptedButtons: Qt.AllButtons // prevent leaking unhandled mouse events
32 property alias overlayShown: buttons.overlayShown
31 property alias overlayShown: buttons.overlayShown
32+ property int stageWidth
33+ property int stageHeight
34+
35+ acceptedButtons: Qt.AllButtons // prevent leaking unhandled mouse events
36+ hoverEnabled: true
33
37
34 signal closeClicked()
38 signal closeClicked()
35 signal minimizeClicked()
39 signal minimizeClicked()
@@ -37,8 +41,18 @@ MouseArea {
37 signal maximizeHorizontallyClicked()
41 signal maximizeHorizontallyClicked()
38 signal maximizeVerticallyClicked()
42 signal maximizeVerticallyClicked()
39
43
44+ signal fakeMaximizeAnimationRequested(real progress)
45+ signal fakeMaximizeLeftAnimationRequested(real progress)
46+ signal fakeMaximizeRightAnimationRequested(real progress)
47+ signal fakeMaximizeTopLeftAnimationRequested(real progress)
48+ signal fakeMaximizeTopRightAnimationRequested(real progress)
49+ signal fakeMaximizeBottomLeftAnimationRequested(real progress)
50+ signal fakeMaximizeBottomRightAnimationRequested(real progress)
51+ signal stopFakeAnimation()
52+
40 onDoubleClicked: {
53 onDoubleClicked: {
54+ priv.resetEdges();
41− if (maximizeButtonShown && mouse.button == Qt.LeftButton) {
55+ if (target.canBeMaximized && mouse.button == Qt.LeftButton) {
42 root.maximizeClicked();
56 root.maximizeClicked();
43 }
57 }
44 }
58 }
@@ -48,6 +62,46 @@ MouseArea {
48 property real distanceX
62 property real distanceX
49 property real distanceY
63 property real distanceY
50 property bool dragging
64 property bool dragging
65+
66+ readonly property int triggerArea: units.gu(3)
67+ property bool nearLeftEdge: target.maximizedLeft
68+ property bool nearTopEdge: target.maximized
69+ property bool nearRightEdge: target.maximizedRight
70+ property bool nearTopLeftCorner: target.maximizedTopLeft
71+ property bool nearTopRightCorner: target.maximizedTopRight
72+ property bool nearBottomLeftCorner: target.maximizedBottomLeft
73+ property bool nearBottomRightCorner: target.maximizedBottomRight
74+
75+ function resetEdges() {
76+ nearLeftEdge = false;
77+ nearRightEdge = false;
78+ nearTopEdge = false;
79+ nearTopLeftCorner = false;
80+ nearTopRightCorner = false;
81+ nearBottomLeftCorner = false;
82+ nearBottomRightCorner = false;
83+ }
84+
85+ function saveRestoredPos(x, y) {
86+ var pos = mapToItem(root.target, x, y);
87+ target.restoredX = Math.round(pos.x);
88+ target.restoredY = Math.round(pos.y);
89+ print("!!! Saved pos", target.restoredX, target.restoredY)
90+ }
91+
92+ // return the progress of mouse pointer movement from 0 to 1 within a corner square of the screen
93+ // 0 -> before the mouse enters the square
94+ // 1 -> mouse is exactly in the very corner
95+ // a is the corner, b is the mouse pos
96+ function progressInCorner(ax, ay, bx, by) {
97+ // distance of two points, a and b, in pixels
98+ var distance = Math.sqrt(Math.pow(bx-ax, 2) + Math.pow(by-ay, 2));
99+ // length of the triggerArea square diagonal
100+ var diagLength = Math.sqrt(2 * priv.triggerArea * priv.triggerArea);
101+ var ratio = 1 - (distance / diagLength);
102+ return bx > 0 && bx <= stageWidth && by > 0 && by <= stageHeight ? ratio : 1; // everything "outside" of our square from the center is 1
103+ }
104+ property real progress: 0
51 }
105 }
52
106
53 onPressedChanged: {
107 onPressedChanged: {
@@ -69,8 +123,97 @@ MouseArea {
69 // Use integer coordinate values to ensure that target is left in a pixel-aligned
123 // Use integer coordinate values to ensure that target is left in a pixel-aligned
70 // position. Mouse movement could have subpixel precision, yielding a fractional
124 // position. Mouse movement could have subpixel precision, yielding a fractional
71 // mouse position.
125 // mouse position.
126+
72 root.target.requestedX = Math.round(pos.x - priv.distanceX);
127 root.target.requestedX = Math.round(pos.x - priv.distanceX);
73 root.target.requestedY = Math.round(Math.max(pos.y - priv.distanceY, PanelState.panelHeight));
128 root.target.requestedY = Math.round(Math.max(pos.y - priv.distanceY, PanelState.panelHeight));
129+
130+ var globalPos = mapToItem(null, mouseX, mouseY);
131+ var globalX = globalPos.x;
132+ var globalY = globalPos.y;
133+ if (globalX < priv.triggerArea && globalY < PanelState.panelHeight) { // top left
134+ if (target.canBeCornerMaximized) {
135+ priv.progress = priv.progressInCorner(0, PanelState.panelHeight, globalX, globalY);
136+ priv.resetEdges();
137+ priv.nearTopLeftCorner = true;
138+ root.fakeMaximizeTopLeftAnimationRequested(priv.progress);
139+ }
140+ } else if (globalX > stageWidth - priv.triggerArea && globalY < PanelState.panelHeight) { // top right
141+ if (target.canBeCornerMaximized) {
142+ priv.progress = priv.progressInCorner(stageWidth, PanelState.panelHeight, globalX, globalY);
143+ priv.resetEdges();
144+ priv.nearTopRightCorner = true;
145+ root.fakeMaximizeTopRightAnimationRequested(priv.progress);
146+ }
147+ } else if (globalX < priv.triggerArea && globalY > stageHeight - priv.triggerArea) { // bottom left
148+ if (target.canBeCornerMaximized) {
149+ priv.progress = priv.progressInCorner(0, stageHeight, globalX, globalY);
150+ priv.resetEdges();
151+ priv.nearBottomLeftCorner = true;
152+ root.fakeMaximizeBottomLeftAnimationRequested(priv.progress);
153+ }
154+ } else if (globalX > stageWidth - priv.triggerArea && globalY > stageHeight - priv.triggerArea) { // bottom right
155+ if (target.canBeCornerMaximized) {
156+ priv.progress = priv.progressInCorner(stageWidth, stageHeight, globalX, globalY);
157+ priv.resetEdges();
158+ priv.nearBottomRightCorner = true;
159+ root.fakeMaximizeBottomRightAnimationRequested(priv.progress);
160+ }
161+ } else if (globalX < priv.triggerArea) { // left
162+ if (target.canBeMaximizedLeftRight) {
163+ priv.progress = MathUtils.clampAndProject(globalX, priv.triggerArea, 0, 0, 1);
164+ priv.resetEdges();
165+ priv.nearLeftEdge = true;
166+ root.fakeMaximizeLeftAnimationRequested(priv.progress);
167+ }
168+ } else if (globalX > stageWidth - priv.triggerArea) { // right
169+ if (target.canBeMaximizedLeftRight) {
170+ priv.progress = MathUtils.clampAndProject(globalX, stageWidth - priv.triggerArea, stageWidth, 0, 1);
171+ priv.resetEdges();
172+ priv.nearRightEdge = true;
173+ root.fakeMaximizeRightAnimationRequested(priv.progress);
174+ }
175+ } else if (globalY < PanelState.panelHeight) { // top
176+ if (target.canBeMaximized) {
177+ priv.progress = MathUtils.clampAndProject(globalY, Math.max(PanelState.panelHeight, priv.triggerArea), 0, 0, 1);
178+ priv.resetEdges();
179+ priv.nearTopEdge = true;
180+ root.fakeMaximizeAnimationRequested(priv.progress);
181+ }
182+ } else if (priv.nearLeftEdge || priv.nearRightEdge || priv.nearTopEdge || priv.nearTopLeftCorner || priv.nearTopRightCorner ||
183+ priv.nearBottomLeftCorner || priv.nearBottomRightCorner) {
184+ print("!!! Exited")
185+ priv.progress = 0;
186+ priv.resetEdges();
187+ root.stopFakeAnimation();
188+ } else if (target.anyMaximized) {
189+ priv.progress = 0;
190+ target.restoreFromMaximized();
191+ }
192+ }
193+ }
194+
195+ onReleased: {
196+ print("Mouse released (left/top/right)", priv.nearLeftEdge, priv.nearTopEdge, priv.nearRightEdge)
197+ if (mouse.button == Qt.LeftButton && (target.state == "normal" || target.state == "restored") && priv.progress == 0) {
198+ priv.saveRestoredPos(target.x, target.y);
199+ } else if (priv.progress < 0.3) { // cancel the preview shape if under 30%
200+ priv.progress = 0;
201+ priv.resetEdges();
202+ root.stopFakeAnimation();
203+ } else if (priv.nearLeftEdge) {
204+ target.maximizeLeft();
205+ } else if (priv.nearTopEdge) {
206+ target.maximize();
207+ } else if (priv.nearRightEdge) {
208+ target.maximizeRight();
209+ } else if (priv.nearTopLeftCorner) {
210+ target.maximizeTopLeft();
211+ } else if (priv.nearTopRightCorner) {
212+ target.maximizeTopRight();
213+ } else if (priv.nearBottomLeftCorner) {
214+ target.maximizeBottomLeft();
215+ } else if (priv.nearBottomRightCorner) {
216+ target.maximizeBottomRight();
74 }
217 }
75 }
218 }
76
219
@@ -105,10 +248,9 @@ MouseArea {
105 onCloseClicked: root.closeClicked();
248 onCloseClicked: root.closeClicked();
106 onMinimizeClicked: root.minimizeClicked();
249 onMinimizeClicked: root.minimizeClicked();
107 onMaximizeClicked: root.maximizeClicked();
250 onMaximizeClicked: root.maximizeClicked();
108− onMaximizeHorizontallyClicked: root.maximizeHorizontallyClicked();
251+ onMaximizeHorizontallyClicked: if (root.target.canBeMaximizedHorizontally) root.maximizeHorizontallyClicked();
109− onMaximizeVerticallyClicked: root.maximizeVerticallyClicked();
252+ onMaximizeVerticallyClicked: if (root.target.canBeMaximizedVertically) root.maximizeVerticallyClicked();
110 closeButtonShown: root.target.application.appId !== "unity8-dash"
253 closeButtonShown: root.target.application.appId !== "unity8-dash"
111− target: root.target
112 }
254 }
113
255
114 Label {
256 Label {
@@ -122,9 +264,9 @@ MouseArea {
122 font.weight: root.active ? Font.Light : Font.Medium
264 font.weight: root.active ? Font.Light : Font.Medium
123 elide: Text.ElideRight
265 elide: Text.ElideRight
124 opacity: overlayShown ? 0 : 1
266 opacity: overlayShown ? 0 : 1
125− visible: opacity == 1
267+ visible: opacity != 0
126 Behavior on opacity {
268 Behavior on opacity {
127− OpacityAnimator { duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing }
269+ UbuntuNumberAnimation {}
128 }
270 }
129 }
271 }
130 }
272 }
qml/Stages/WindowResizeArea.qml+11−24
@@ -54,7 +54,7 @@ MouseArea {
54 property int normalHeight: 0
54 property int normalHeight: 0
55
55
56 function updateNormalGeometry() {
56 function updateNormalGeometry() {
57− if (root.target.state == "normal") {
57+ if (root.target.state == "normal" || root.target.state == "restored") {
58 normalX = root.target.requestedX
58 normalX = root.target.requestedX
59 normalY = root.target.requestedY
59 normalY = root.target.requestedY
60 normalWidth = root.target.width
60 normalWidth = root.target.width
@@ -83,34 +83,21 @@ MouseArea {
83 target.requestedY = Qt.binding(function() { return Math.max(Math.min(windowGeometry.y, root.screenHeight - target.requestedHeight), PanelState.panelHeight); });
83 target.requestedY = Qt.binding(function() { return Math.max(Math.min(windowGeometry.y, root.screenHeight - target.requestedHeight), PanelState.panelHeight); });
84
84
85 var windowState = windowStateStorage.getState(root.windowId, WindowStateStorage.WindowStateNormal)
85 var windowState = windowStateStorage.getState(root.windowId, WindowStateStorage.WindowStateNormal)
86− switch (windowState) {
86+ target.restore(false /* animated */, windowState);
87− case WindowStateStorage.WindowStateNormal:
88− break;
89− case WindowStateStorage.WindowStateMaximized:
90− target.maximize(false);
91− break;
92− case WindowStateStorage.WindowStateMaximizedLeft:
93− target.maximizeLeft(false);
94− break;
95− case WindowStateStorage.WindowStateMaximizedRight:
96− target.maximizeRight(false);
97− break;
98− case WindowStateStorage.WindowStateMaximizedHorizontally:
99− target.maximizeHorizontally(false);
100− break;
101− case WindowStateStorage.WindowStateMaximizedVertically:
102− target.maximizeVertically(false);
103− break;
104− default:
105− console.warn("Unsupported window state");
106− break;
107− }
108
87
109 priv.updateNormalGeometry();
88 priv.updateNormalGeometry();
89+
90+ // initialize the x/y to restore to
91+ target.restoredX = priv.normalX;
92+ target.restoredY = priv.normalY;
110 }
93 }
111
94
112 function saveWindowState() {
95 function saveWindowState() {
96+ var state = target.windowState;
97+ if (state === WindowStateStorage.WindowStateRestored) {
98+ state = WindowStateStorage.WindowStateNormal;
99+ }
113− windowStateStorage.saveState(root.windowId, target.windowState & ~WindowStateStorage.WindowStateMinimized); // clear the minimized bit when saving
100+ windowStateStorage.saveState(root.windowId, state & ~WindowStateStorage.WindowStateMinimized); // clear the minimized bit when saving
114 windowStateStorage.saveGeometry(root.windowId, Qt.rect(priv.normalX, priv.normalY, priv.normalWidth, priv.normalHeight));
101 windowStateStorage.saveGeometry(root.windowId, Qt.rect(priv.normalX, priv.normalY, priv.normalWidth, priv.normalHeight));
115 }
102 }
116
103
tests/mocks/Utils/windowstatestorage.cpp+2−2
@@ -41,7 +41,7 @@ void WindowStateStorage::saveGeometry(const QString &windowId, const QRect &rect
41 m_geometry[windowId] = rect;
41 m_geometry[windowId] = rect;
42}
42}
43
43
44−QRect WindowStateStorage::getGeometry(const QString &windowId, const QRect &defaultValue)
44+QRect WindowStateStorage::getGeometry(const QString &windowId, const QRect &defaultValue) const
45{
45{
46 if (!m_geometry.contains(windowId)) return defaultValue;
46 if (!m_geometry.contains(windowId)) return defaultValue;
47 return m_geometry.value(windowId).toRect();
47 return m_geometry.value(windowId).toRect();
@@ -70,7 +70,7 @@ void WindowStateStorage::saveState(const QString &windowId, WindowState state)
70 m_state[windowId] = state;
70 m_state[windowId] = state;
71}
71}
72
72
73−WindowStateStorage::WindowState WindowStateStorage::getState(const QString &windowId, WindowStateStorage::WindowState defaultValue)
73+WindowStateStorage::WindowState WindowStateStorage::getState(const QString &windowId, WindowStateStorage::WindowState defaultValue) const
74{
74{
75 if (!m_state.contains(windowId)) return defaultValue;
75 if (!m_state.contains(windowId)) return defaultValue;
76 return m_state.value(windowId);
76 return m_state.value(windowId);
tests/mocks/Utils/windowstatestorage.h+8−3
@@ -32,7 +32,12 @@ public:
32 WindowStateMaximizedLeft = 1 << 4,
32 WindowStateMaximizedLeft = 1 << 4,
33 WindowStateMaximizedRight = 1 << 5,
33 WindowStateMaximizedRight = 1 << 5,
34 WindowStateMaximizedHorizontally = 1 << 6,
34 WindowStateMaximizedHorizontally = 1 << 6,
35− WindowStateMaximizedVertically = 1 << 7
35+ WindowStateMaximizedVertically = 1 << 7,
36+ WindowStateMaximizedTopLeft = 1 << 8,
37+ WindowStateMaximizedTopRight = 1 << 9,
38+ WindowStateMaximizedBottomLeft = 1 << 10,
39+ WindowStateMaximizedBottomRight = 1 << 11,
40+ WindowStateRestored = 1 << 12
36 };
41 };
37 Q_DECLARE_FLAGS(WindowStates, WindowState)
42 Q_DECLARE_FLAGS(WindowStates, WindowState)
38#if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0))
43#if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0))
@@ -42,10 +47,10 @@ public:
42 WindowStateStorage(QObject *parent = 0);
47 WindowStateStorage(QObject *parent = 0);
43
48
44 Q_INVOKABLE void saveState(const QString &windowId, WindowState state);
49 Q_INVOKABLE void saveState(const QString &windowId, WindowState state);
45− Q_INVOKABLE WindowState getState(const QString &windowId, WindowState defaultValue);
50+ Q_INVOKABLE WindowState getState(const QString &windowId, WindowState defaultValue) const;
46
51
47 Q_INVOKABLE void saveGeometry(const QString &windowId, const QRect &rect);
52 Q_INVOKABLE void saveGeometry(const QString &windowId, const QRect &rect);
48− Q_INVOKABLE QRect getGeometry(const QString &windowId, const QRect &defaultValue);
53+ Q_INVOKABLE QRect getGeometry(const QString &windowId, const QRect &defaultValue) const;
49
54
50 Q_INVOKABLE void saveStage(const QString &appId, int stage);
55 Q_INVOKABLE void saveStage(const QString &appId, int stage);
51 Q_INVOKABLE int getStage(const QString &appId, int defaultValue) const;
56 Q_INVOKABLE int getStage(const QString &appId, int defaultValue) const;
tests/qmltests/Stages/tst_DesktopStage.qml+2−2
@@ -487,7 +487,7 @@ Item {
487
487
488 // click again to restore
488 // click again to restore
489 mouseClick(dialerMaximizeButton);
489 mouseClick(dialerMaximizeButton);
490− tryCompare(dialerDelegate, "windowState", WindowStateStorage.WindowStateNormal);
490+ tryCompare(dialerDelegate, "windowState", WindowStateStorage.WindowStateRestored);
491 }
491 }
492
492
493 function test_windowMaximizeVertically() {
493 function test_windowMaximizeVertically() {
@@ -502,7 +502,7 @@ Item {
502
502
503 // click again to restore
503 // click again to restore
504 mouseClick(dialerMaximizeButton);
504 mouseClick(dialerMaximizeButton);
505− tryCompare(dialerDelegate, "windowState", WindowStateStorage.WindowStateNormal);
505+ tryCompare(dialerDelegate, "windowState", WindowStateStorage.WindowStateRestored);
506 }
506 }
507
507
508 function test_smashCursorKeys() {
508 function test_smashCursorKeys() {
tests/qmltests/Stages/tst_WindowResizeArea.qml+9−1
@@ -60,9 +60,12 @@ Item {
60 property real heightIncrement: 0
60 property real heightIncrement: 0
61
61
62 property int windowState: WindowStateStorage.WindowStateNormal
62 property int windowState: WindowStateStorage.WindowStateNormal
63+ property real restoredX
64+ property real restoredY
63
65
64 states: [
66 states: [
65 State { name: "normal"; when: windowState == WindowStateStorage.WindowStateNormal },
67 State { name: "normal"; when: windowState == WindowStateStorage.WindowStateNormal },
68+ State { name: "restored"; when: windowState == WindowStateStorage.WindowStateRestored },
66 State { name: "maximized"; when: windowState == WindowStateStorage.WindowStateMaximized }
69 State { name: "maximized"; when: windowState == WindowStateStorage.WindowStateMaximized }
67 ]
70 ]
68
71
@@ -71,7 +74,12 @@ Item {
71 }
74 }
72
75
73 function restoreFromMaximized() {
76 function restoreFromMaximized() {
74− windowState = WindowStateStorage.WindowStateNormal
77+ windowState = WindowStateStorage.WindowStateRestored
78+ }
79+
80+ function restore(animated,state) {
81+ windowState = state || WindowStateStorage.WindowStateRestored;
82+ windowState &= ~WindowStateStorage.WindowStateMinimized; // clear the minimized bit
75 }
83 }
76
84
77 WindowResizeArea {
85 WindowResizeArea {