on xenial help sign in
Add support for low shell chrome. Fixes: #1535397
52892bcMichael Zanetti
qml/Components/InputMethod.qml+4−1
@@ -51,7 +51,10 @@ Item {
51 }
51 }
52
52
53 state: {
53 state: {
54+ if (surfaceItem.surface &&
55+ surfaceItem.surfaceState != Mir.HiddenState &&
54− if (surfaceItem.surface && surfaceItem.surfaceState != Mir.MinimizedState && root.enabled) {
56+ surfaceItem.surfaceState != Mir.MinimizedState &&
57+ root.enabled) {
55 return "shown";
58 return "shown";
56 } else {
59 } else {
57 return "hidden";
60 return "hidden";
qml/Shell.qml+5−1
@@ -246,7 +246,7 @@ Item {
246 property string usageScenario: shell.usageScenario === "phone" || greeter.hasLockedApp
246 property string usageScenario: shell.usageScenario === "phone" || greeter.hasLockedApp
247 ? "phone"
247 ? "phone"
248 : shell.usageScenario
248 : shell.usageScenario
249− source: {
249+ readonly property string qmlComponent: {
250 if(shell.mode === "greeter") {
250 if(shell.mode === "greeter") {
251 return "Stages/ShimStage.qml"
251 return "Stages/ShimStage.qml"
252 } else if (applicationsDisplayLoader.usageScenario === "phone") {
252 } else if (applicationsDisplayLoader.usageScenario === "phone") {
@@ -257,6 +257,10 @@ Item {
257 return "Stages/DesktopStage.qml";
257 return "Stages/DesktopStage.qml";
258 }
258 }
259 }
259 }
260+ onQmlComponentChanged: {
261+ if (item) item.stageAboutToBeUnloaded();
262+ source = qmlComponent;
263+ }
260
264
261 property bool interactive: tutorial.spreadEnabled
265 property bool interactive: tutorial.spreadEnabled
262 && (!greeter || !greeter.shown)
266 && (!greeter || !greeter.shown)
qml/Stages/AbstractStage.qml+2
@@ -51,6 +51,8 @@ Rectangle {
51 | Qt.InvertedPortraitOrientation
51 | Qt.InvertedPortraitOrientation
52 | Qt.InvertedLandscapeOrientation
52 | Qt.InvertedLandscapeOrientation
53
53
54+ signal stageAboutToBeUnloaded
55+
54 // Shared code for use in stage implementations
56 // Shared code for use in stage implementations
55 GSettings {
57 GSettings {
56 id: lifecycleExceptions
58 id: lifecycleExceptions
qml/Stages/DesktopStage.qml+71−12
@@ -255,8 +255,8 @@ AbstractStage {
255 focus: appId === priv.focusedAppId
255 focus: appId === priv.focusedAppId
256 width: decoratedWindow.width
256 width: decoratedWindow.width
257 height: decoratedWindow.height
257 height: decoratedWindow.height
258− property alias requestedWidth: decoratedWindow.requestedWidth
258+ property int requestedWidth: -1
259− property alias requestedHeight: decoratedWindow.requestedHeight
259+ property int requestedHeight: -1
260 property alias minimumWidth: decoratedWindow.minimumWidth
260 property alias minimumWidth: decoratedWindow.minimumWidth
261 property alias minimumHeight: decoratedWindow.minimumHeight
261 property alias minimumHeight: decoratedWindow.minimumHeight
262 property alias maximumWidth: decoratedWindow.maximumWidth
262 property alias maximumWidth: decoratedWindow.maximumWidth
@@ -369,8 +369,10 @@ AbstractStage {
369 name: "fullscreen"; when: decoratedWindow.fullscreen
369 name: "fullscreen"; when: decoratedWindow.fullscreen
370 PropertyChanges {
370 PropertyChanges {
371 target: appDelegate;
371 target: appDelegate;
372+ x: 0;
372− x: 0; y: -PanelState.panelHeight
373+ y: -PanelState.panelHeight
374+ requestedWidth: appContainer.width;
373− requestedWidth: appContainer.width; requestedHeight: appContainer.height;
375+ requestedHeight: appContainer.height;
374 }
376 }
375 },
377 },
376 State {
378 State {
@@ -387,21 +389,42 @@ AbstractStage {
387 name: "maximized"; when: appDelegate.maximized && !appDelegate.minimized
389 name: "maximized"; when: appDelegate.maximized && !appDelegate.minimized
388 PropertyChanges {
390 PropertyChanges {
389 target: appDelegate;
391 target: appDelegate;
390− x: root.leftMargin; y: 0;
392+ x: root.leftMargin;
391− requestedWidth: appContainer.width - root.leftMargin; requestedHeight: appContainer.height;
393+ y: 0;
392 visuallyMinimized: false;
394 visuallyMinimized: false;
393 visuallyMaximized: true
395 visuallyMaximized: true
394 }
396 }
397+ PropertyChanges {
398+ target: decoratedWindow
399+ requestedWidth: appContainer.width - root.leftMargin;
400+ requestedHeight: appContainer.height;
401+ }
395 },
402 },
396 State {
403 State {
397 name: "maximizedLeft"; when: appDelegate.maximizedLeft && !appDelegate.minimized
404 name: "maximizedLeft"; when: appDelegate.maximizedLeft && !appDelegate.minimized
405+ PropertyChanges {
406+ target: appDelegate
407+ x: root.leftMargin
398− PropertyChanges { target: appDelegate; x: root.leftMargin; y: PanelState.panelHeight;
408+ y: PanelState.panelHeight
409+ }
410+ PropertyChanges {
411+ target: decoratedWindow
412+ requestedWidth: (appContainer.width - root.leftMargin)/2
399− requestedWidth: (appContainer.width - root.leftMargin)/2; requestedHeight: appContainer.height - PanelState.panelHeight }
413+ requestedHeight: appContainer.height - PanelState.panelHeight
414+ }
400 },
415 },
401 State {
416 State {
402 name: "maximizedRight"; when: appDelegate.maximizedRight && !appDelegate.minimized
417 name: "maximizedRight"; when: appDelegate.maximizedRight && !appDelegate.minimized
418+ PropertyChanges {
419+ target: appDelegate;
403− PropertyChanges { target: appDelegate; x: (appContainer.width + root.leftMargin)/2; y: PanelState.panelHeight;
420+ x: (appContainer.width + root.leftMargin)/2
421+ y: PanelState.panelHeight
422+ }
423+ PropertyChanges {
424+ target: decoratedWindow
425+ requestedWidth: (appContainer.width - root.leftMargin)/2
404− requestedWidth: (appContainer.width - root.leftMargin)/2; requestedHeight: appContainer.height - PanelState.panelHeight }
426+ requestedHeight: appContainer.height - PanelState.panelHeight
427+ }
405 },
428 },
406 State {
429 State {
407 name: "minimized"; when: appDelegate.minimized
430 name: "minimized"; when: appDelegate.minimized
@@ -421,13 +444,17 @@ AbstractStage {
421 enabled: appDelegate.animationsEnabled
444 enabled: appDelegate.animationsEnabled
422 PropertyAction { target: appDelegate; properties: "visuallyMinimized,visuallyMaximized" }
445 PropertyAction { target: appDelegate; properties: "visuallyMinimized,visuallyMaximized" }
423 UbuntuNumberAnimation { target: appDelegate; properties: "x,y,opacity,requestedWidth,requestedHeight,scale"; duration: UbuntuAnimation.FastDuration }
446 UbuntuNumberAnimation { target: appDelegate; properties: "x,y,opacity,requestedWidth,requestedHeight,scale"; duration: UbuntuAnimation.FastDuration }
447+ UbuntuNumberAnimation { target: decoratedWindow; properties: "requestedWidth,requestedHeight"; duration: UbuntuAnimation.FastDuration }
424 },
448 },
425 Transition {
449 Transition {
426 to: "minimized"
450 to: "minimized"
427 enabled: appDelegate.animationsEnabled
451 enabled: appDelegate.animationsEnabled
428 PropertyAction { target: appDelegate; property: "visuallyMaximized" }
452 PropertyAction { target: appDelegate; property: "visuallyMaximized" }
429 SequentialAnimation {
453 SequentialAnimation {
454+ ParallelAnimation {
455+ UbuntuNumberAnimation { target: appDelegate; properties: "x,y,opacity,scale"; duration: UbuntuAnimation.FastDuration }
430− UbuntuNumberAnimation { target: appDelegate; properties: "x,y,opacity,requestedWidth,requestedHeight,scale"; duration: UbuntuAnimation.FastDuration }
456+ UbuntuNumberAnimation { target: decoratedWindow; properties: "requestedWidth,requestedHeight"; duration: UbuntuAnimation.FastDuration }
457+ }
431 PropertyAction { target: appDelegate; property: "visuallyMinimized" }
458 PropertyAction { target: appDelegate; property: "visuallyMinimized" }
432 ScriptAction {
459 ScriptAction {
433 script: {
460 script: {
@@ -443,7 +470,10 @@ AbstractStage {
443 enabled: appDelegate.animationsEnabled
470 enabled: appDelegate.animationsEnabled
444 PropertyAction { target: appDelegate; property: "visuallyMinimized" }
471 PropertyAction { target: appDelegate; property: "visuallyMinimized" }
445 SequentialAnimation {
472 SequentialAnimation {
473+ ParallelAnimation {
474+ UbuntuNumberAnimation { target: appDelegate; properties: "x,y,opacity,scale"; duration: UbuntuAnimation.FastDuration }
446− UbuntuNumberAnimation { target: appDelegate; properties: "x,y,opacity,requestedWidth,requestedHeight,scale"; duration: UbuntuAnimation.FastDuration }
475+ UbuntuNumberAnimation { target: decoratedWindow; properties: "requestedWidth,requestedHeight"; duration: UbuntuAnimation.FastDuration }
476+ }
447 PropertyAction { target: appDelegate; property: "visuallyMaximized" }
477 PropertyAction { target: appDelegate; property: "visuallyMaximized" }
448 }
478 }
449 }
479 }
@@ -458,6 +488,7 @@ AbstractStage {
458 }
488 }
459
489
460 WindowResizeArea {
490 WindowResizeArea {
491+ id: resizeArea
461 objectName: "windowResizeArea"
492 objectName: "windowResizeArea"
462 target: appDelegate
493 target: appDelegate
463 minWidth: units.gu(10)
494 minWidth: units.gu(10)
@@ -469,6 +500,25 @@ AbstractStage {
469 leftMargin: root.leftMargin
500 leftMargin: root.leftMargin
470
501
471 onPressed: { ApplicationManager.focusApplication(model.appId) }
502 onPressed: { ApplicationManager.focusApplication(model.appId) }
503+
504+ Component.onCompleted: {
505+ loadWindowState();
506+ }
507+
508+ property bool saveStateOnDestruction: true
509+ Connections {
510+ target: root
511+ onStageAboutToBeUnloaded: {
512+ resizeArea.saveWindowState();
513+ resizeArea.saveStateOnDestruction = false;
514+ fullscreenPolicy.active = false;
515+ }
516+ }
517+ Component.onDestruction: {
518+ if (saveStateOnDestruction) {
519+ saveWindowState();
520+ }
521+ }
472 }
522 }
473
523
474 DecoratedWindow {
524 DecoratedWindow {
@@ -480,12 +530,21 @@ AbstractStage {
480 active: ApplicationManager.focusedApplicationId === model.appId
530 active: ApplicationManager.focusedApplicationId === model.appId
481 focus: true
531 focus: true
482
532
533+ requestedWidth: appDelegate.requestedWidth
534+ requestedHeight: appDelegate.requestedHeight
535+
483 onClose: ApplicationManager.stopApplication(model.appId)
536 onClose: ApplicationManager.stopApplication(model.appId)
484 onMaximize: appDelegate.maximized || appDelegate.maximizedLeft || appDelegate.maximizedRight
537 onMaximize: appDelegate.maximized || appDelegate.maximizedLeft || appDelegate.maximizedRight
485 ? appDelegate.restoreFromMaximized() : appDelegate.maximize()
538 ? appDelegate.restoreFromMaximized() : appDelegate.maximize()
486 onMinimize: appDelegate.minimize()
539 onMinimize: appDelegate.minimize()
487 onDecorationPressed: { ApplicationManager.focusApplication(model.appId) }
540 onDecorationPressed: { ApplicationManager.focusApplication(model.appId) }
488 }
541 }
542+
543+ WindowedFullscreenPolicy {
544+ id: fullscreenPolicy
545+ active: true
546+ application: decoratedWindow.application
547+ }
489 }
548 }
490 }
549 }
491 }
550 }
qml/Stages/PhoneStage.qml+10
@@ -629,6 +629,16 @@ AbstractStage {
629 property: "focusedAppOrientationChangesEnabled"
629 property: "focusedAppOrientationChangesEnabled"
630 value: orientationChangesEnabled
630 value: orientationChangesEnabled
631 }
631 }
632+
633+ StagedFullscreenPolicy {
634+ id: fullscreenPolicy
635+ application: appDelegate.application
636+ }
637+
638+ Connections {
639+ target: root
640+ onStageAboutToBeUnloaded: fullscreenPolicy.active = false
641+ }
632 }
642 }
633 }
643 }
634 }
644 }
qml/Stages/StagedFullscreenPolicy.qmlnew file+58
@@ -0,0 +1,58 @@
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 Unity.Application 0.1
19+
20+// This component will change the state of the surface based on the surface
21+// state and shell chrome.
22+//
23+// Chrome changed to LowChrome -> server sets client window state to "fullscreen"
24+// Chrome changed to NormalChrome -> server sets client window to "restored" state.
25+// Chrome set and state change to restored -> server RESETS client window state to "fullscreen"
26+// Chrome not set and state change to restored -> client window stays "restored"
27+// Chrome not set and state change to fulscreen -> client window stays "fullscreen"
28+QtObject {
29+ property bool active: true
30+ property QtObject application: null
31+
32+ readonly property var lastSurface: application && application.session ?
33+ application.session.lastSurface : null
34+ onLastSurfaceChanged: {
35+ if (!active || !lastSurface) return;
36+ if (lastSurface.shellChrome === Mir.LowChrome) {
37+ lastSurface.state = Mir.FullscreenState;
38+ }
39+ }
40+
41+ property var _connections: Connections {
42+ target: lastSurface
43+ onShellChromeChanged: {
44+ if (!active || !lastSurface) return;
45+ if (lastSurface.shellChrome === Mir.LowChrome) {
46+ lastSurface.state = Mir.FullscreenState;
47+ } else {
48+ lastSurface.state = Mir.RestoredState;
49+ }
50+ }
51+ onStateChanged: {
52+ if (!active) return;
53+ if (lastSurface.state === Mir.RestoredState && lastSurface.shellChrome === Mir.LowChrome) {
54+ lastSurface.state = Mir.FullscreenState;
55+ }
56+ }
57+ }
58+}
qml/Stages/TabletStage.qml+9
@@ -947,6 +947,15 @@ AbstractStage {
947 period: (spreadView.positionMarker2 - spreadView.positionMarker1) / 3
947 period: (spreadView.positionMarker2 - spreadView.positionMarker1) / 3
948 progress: spreadTile.progress - spreadView.positionMarker1
948 progress: spreadTile.progress - spreadView.positionMarker1
949 }
949 }
950+
951+ StagedFullscreenPolicy {
952+ id: fullscreenPolicy
953+ application: spreadTile.application
954+ }
955+ Connections {
956+ target: root
957+ onStageAboutToBeUnloaded: fullscreenPolicy.active = false
958+ }
950 }
959 }
951 }
960 }
952 }
961 }
qml/Stages/WindowResizeArea.qml+2−2
@@ -69,7 +69,7 @@ MouseArea {
69 onHeightChanged: priv.updateNormalGeometry();
69 onHeightChanged: priv.updateNormalGeometry();
70 }
70 }
71
71
72− Component.onCompleted: {
72+ function loadWindowState() {
73 var windowGeometry = windowStateStorage.getGeometry(root.windowId,
73 var windowGeometry = windowStateStorage.getGeometry(root.windowId,
74 Qt.rect(target.x, target.y, defaultWidth, defaultHeight));
74 Qt.rect(target.x, target.y, defaultWidth, defaultHeight));
75
75
@@ -87,7 +87,7 @@ MouseArea {
87 priv.updateNormalGeometry();
87 priv.updateNormalGeometry();
88 }
88 }
89
89
90− Component.onDestruction: {
90+ function saveWindowState() {
91 windowStateStorage.saveState(root.windowId, target.state == "maximized" ? WindowStateStorage.WindowStateMaximized : WindowStateStorage.WindowStateNormal)
91 windowStateStorage.saveState(root.windowId, target.state == "maximized" ? WindowStateStorage.WindowStateMaximized : WindowStateStorage.WindowStateNormal)
92 windowStateStorage.saveGeometry(root.windowId, Qt.rect(priv.normalX, priv.normalY, priv.normalWidth, priv.normalHeight))
92 windowStateStorage.saveGeometry(root.windowId, Qt.rect(priv.normalX, priv.normalY, priv.normalWidth, priv.normalHeight))
93 }
93 }
qml/Stages/WindowedFullscreenPolicy.qmlnew file+41
@@ -0,0 +1,41 @@
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 QtQml 2.2
18+import Unity.Application 0.1
19+
20+// This component will change the state of the surface when the stage is loaded.
21+//
22+// On first surface load; if the surface is set to low chrome & fullscreen, the
23+// state of the window is returned to restored.
24+QtObject {
25+ property bool active: true
26+ property QtObject application: null
27+
28+ readonly property var lastSurface: application && application.session ?
29+ application.session.lastSurface : null
30+ property bool _firstTimeSurface: true
31+
32+ onLastSurfaceChanged: {
33+ if (!active || !lastSurface) return;
34+ if (!_firstTimeSurface) return;
35+ _firstTimeSurface = false;
36+
37+ if (lastSurface.state === Mir.FullscreenState && lastSurface.shellChrome === Mir.LowChrome) {
38+ lastSurface.state = Mir.RestoredState;
39+ }
40+ }
41+}
tests/mocks/Unity/Application/ApplicationInfo.cpp+21−3
@@ -44,6 +44,7 @@ ApplicationInfo::ApplicationInfo(const QString &appId, QObject *parent)
44 , m_isTouchApp(true)
44 , m_isTouchApp(true)
45 , m_exemptFromLifecycle(false)
45 , m_exemptFromLifecycle(false)
46 , m_manualSurfaceCreation(false)
46 , m_manualSurfaceCreation(false)
47+ , m_shellChrome(Mir::NormalChrome)
47{
48{
48}
49}
49
50
@@ -63,6 +64,7 @@ ApplicationInfo::ApplicationInfo(QObject *parent)
63 , m_isTouchApp(true)
64 , m_isTouchApp(true)
64 , m_exemptFromLifecycle(false)
65 , m_exemptFromLifecycle(false)
65 , m_manualSurfaceCreation(false)
66 , m_manualSurfaceCreation(false)
67+ , m_shellChrome(Mir::NormalChrome)
66{
68{
67}
69}
68
70
@@ -102,9 +104,11 @@ void ApplicationInfo::setSession(Session* session)
102 if (m_session) {
104 if (m_session) {
103 m_session->setApplication(this);
105 m_session->setApplication(this);
104 m_session->setParent(this);
106 m_session->setParent(this);
107+ m_session->setFullscreen(m_fullscreen);
105 SessionManager::singleton()->registerSession(m_session);
108 SessionManager::singleton()->registerSession(m_session);
106 connect(m_session, &Session::surfaceAdded,
109 connect(m_session, &Session::surfaceAdded,
107 this, &ApplicationInfo::onSessionSurfaceAdded);
110 this, &ApplicationInfo::onSessionSurfaceAdded);
111+ connect(m_session, &Session::fullscreenChanged, this, &ApplicationInfo::fullscreenChanged);
108
112
109 if (!m_manualSurfaceCreation) {
113 if (!m_manualSurfaceCreation) {
110 QTimer::singleShot(500, m_session, &Session::createSurface);
114 QTimer::singleShot(500, m_session, &Session::createSurface);
@@ -191,12 +195,17 @@ void ApplicationInfo::setFocused(bool value)
191
195
192void ApplicationInfo::setFullscreen(bool value)
196void ApplicationInfo::setFullscreen(bool value)
193{
197{
194− if (value != m_fullscreen) {
198+ m_fullscreen = value;
195− m_fullscreen = value;
199+ if (m_session) {
196− Q_EMIT fullscreenChanged(value);
200+ m_session->setFullscreen(value);
197 }
201 }
198}
202}
199
203
204+bool ApplicationInfo::fullscreen() const
205+{
206+ return m_session ? m_session->fullscreen() : m_fullscreen;
207+}
208+
200void ApplicationInfo::setManualSurfaceCreation(bool value)
209void ApplicationInfo::setManualSurfaceCreation(bool value)
201{
210{
202 if (value != m_manualSurfaceCreation) {
211 if (value != m_manualSurfaceCreation) {
@@ -262,6 +271,7 @@ void ApplicationInfo::onSessionSurfaceAdded(MirSurface* surface)
262 } else {
271 } else {
263 setState(Suspended);
272 setState(Suspended);
264 }
273 }
274+ surface->setShellChrome(m_shellChrome);
265 }
275 }
266}
276}
267
277
@@ -291,3 +301,11 @@ void ApplicationInfo::setInitialSurfaceSize(const QSize &size)
291 Q_EMIT initialSurfaceSizeChanged(m_initialSurfaceSize);
301 Q_EMIT initialSurfaceSizeChanged(m_initialSurfaceSize);
292 }
302 }
293}
303}
304+
305+void ApplicationInfo::setShellChrome(Mir::ShellChrome shellChrome)
306+{
307+ m_shellChrome = shellChrome;
308+ if (m_session && m_session->lastSurface()) {
309+ m_session->lastSurface()->setShellChrome(shellChrome);
310+ }
311+}
tests/mocks/Unity/Application/ApplicationInfo.h+5−1
@@ -24,6 +24,7 @@ class Session;
24
24
25// unity-api
25// unity-api
26#include <unity/shell/application/ApplicationInfoInterface.h>
26#include <unity/shell/application/ApplicationInfoInterface.h>
27+#include <unity/shell/application/Mir.h>
27
28
28using namespace unity::shell::application;
29using namespace unity::shell::application;
29
30
@@ -78,7 +79,7 @@ public:
78 QString screenshot() const { return m_screenshotFileName; }
79 QString screenshot() const { return m_screenshotFileName; }
79
80
80 void setFullscreen(bool value);
81 void setFullscreen(bool value);
81− bool fullscreen() const { return m_fullscreen; }
82+ bool fullscreen() const;
82
83
83 Qt::ScreenOrientations supportedOrientations() const override;
84 Qt::ScreenOrientations supportedOrientations() const override;
84 void setSupportedOrientations(Qt::ScreenOrientations orientations);
85 void setSupportedOrientations(Qt::ScreenOrientations orientations);
@@ -97,6 +98,8 @@ public:
97
98
98 QSize initialSurfaceSize() const override;
99 QSize initialSurfaceSize() const override;
99 void setInitialSurfaceSize(const QSize &size) override;
100 void setInitialSurfaceSize(const QSize &size) override;
101+
102+ Q_INVOKABLE void setShellChrome(Mir::ShellChrome shellChrome);
100public:
103public:
101 void setSession(Session* session);
104 void setSession(Session* session);
102 Session* session() const { return m_session; }
105 Session* session() const { return m_session; }
@@ -134,6 +137,7 @@ private:
134 QSize m_initialSurfaceSize;
137 QSize m_initialSurfaceSize;
135
138
136 bool m_manualSurfaceCreation;
139 bool m_manualSurfaceCreation;
140+ Mir::ShellChrome m_shellChrome;
137};
141};
138
142
139Q_DECLARE_METATYPE(ApplicationInfo*)
143Q_DECLARE_METATYPE(ApplicationInfo*)
tests/mocks/Unity/Application/ApplicationManager.cpp+6−4
@@ -328,6 +328,7 @@ void ApplicationManager::buildListOfAvailableApplications()
328 application->setName("Camera");
328 application->setName("Camera");
329 application->setScreenshotId("camera");
329 application->setScreenshotId("camera");
330 application->setIconId("camera");
330 application->setIconId("camera");
331+ application->setShellChrome(Mir::LowChrome);
331 application->setFullscreen(true);
332 application->setFullscreen(true);
332 application->setSupportedOrientations(Qt::PortraitOrientation
333 application->setSupportedOrientations(Qt::PortraitOrientation
333 | Qt::LandscapeOrientation
334 | Qt::LandscapeOrientation
@@ -341,7 +342,8 @@ void ApplicationManager::buildListOfAvailableApplications()
341 application->setName("Gallery");
342 application->setName("Gallery");
342 application->setScreenshotId("gallery");
343 application->setScreenshotId("gallery");
343 application->setIconId("gallery");
344 application->setIconId("gallery");
344− application->setFullscreen(true);
345+ application->setShellChrome(Mir::LowChrome);
346+ application->setStage(ApplicationInfo::MainStage);
345 m_availableApplications.append(application);
347 m_availableApplications.append(application);
346
348
347 application = new ApplicationInfo(this);
349 application = new ApplicationInfo(this);
@@ -353,7 +355,7 @@ void ApplicationManager::buildListOfAvailableApplications()
353
355
354 application = new ApplicationInfo(this);
356 application = new ApplicationInfo(this);
355 application->setAppId("webbrowser-app");
357 application->setAppId("webbrowser-app");
356− application->setFullscreen(true);
358+ application->setShellChrome(Mir::LowChrome);
357 application->setName("Browser");
359 application->setName("Browser");
358 application->setScreenshotId("browser");
360 application->setScreenshotId("browser");
359 application->setIconId("browser");
361 application->setIconId("browser");
@@ -378,7 +380,7 @@ void ApplicationManager::buildListOfAvailableApplications()
378 application->setName("GMail");
380 application->setName("GMail");
379 application->setIconId("gmail");
381 application->setIconId("gmail");
380 application->setScreenshotId("gmail-webapp.svg");
382 application->setScreenshotId("gmail-webapp.svg");
381− application->setFullscreen(false);
383+ application->setStage(ApplicationInfo::MainStage);
382 application->setSupportedOrientations(Qt::PortraitOrientation
384 application->setSupportedOrientations(Qt::PortraitOrientation
383 | Qt::LandscapeOrientation
385 | Qt::LandscapeOrientation
384 | Qt::InvertedPortraitOrientation
386 | Qt::InvertedPortraitOrientation
@@ -390,7 +392,7 @@ void ApplicationManager::buildListOfAvailableApplications()
390 application->setName("Music");
392 application->setName("Music");
391 application->setIconId("soundcloud");
393 application->setIconId("soundcloud");
392 application->setScreenshotId("music");
394 application->setScreenshotId("music");
393− application->setFullscreen(false);
395+ application->setStage(ApplicationInfo::MainStage);
394 application->setSupportedOrientations(Qt::PortraitOrientation
396 application->setSupportedOrientations(Qt::PortraitOrientation
395 | Qt::LandscapeOrientation
397 | Qt::LandscapeOrientation
396 | Qt::InvertedPortraitOrientation
398 | Qt::InvertedPortraitOrientation
tests/mocks/Unity/Application/MirSurface.cpp+16
@@ -38,6 +38,7 @@ MirSurface::MirSurface(const QString& name,
38 , m_width(-1)
38 , m_width(-1)
39 , m_height(-1)
39 , m_height(-1)
40 , m_slowToResize(false)
40 , m_slowToResize(false)
41+ , m_shellChrome(Mir::NormalChrome)
41{
42{
42// qDebug() << "MirSurface::MirSurface() " << name;
43// qDebug() << "MirSurface::MirSurface() " << name;
43 QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership);
44 QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership);
@@ -134,6 +135,21 @@ void MirSurface::setOrientationAngle(Mir::OrientationAngle angle)
134}
135}
135
136
136
137
138+Mir::ShellChrome MirSurface::shellChrome() const
139+{
140+ return m_shellChrome;
141+}
142+
143+void MirSurface::setShellChrome(Mir::ShellChrome shellChrome)
144+{
145+ if (shellChrome == m_shellChrome)
146+ return;
147+
148+ m_shellChrome = shellChrome;
149+ Q_EMIT shellChromeChanged(shellChrome);
150+}
151+
152+
137
153
138void MirSurface::registerView(qintptr viewId)
154void MirSurface::registerView(qintptr viewId)
139{
155{
tests/mocks/Unity/Application/MirSurface.h+5−3
@@ -74,10 +74,13 @@ public:
74 int widthIncrement() const override { return m_widthIncrement; }
74 int widthIncrement() const override { return m_widthIncrement; }
75 int heightIncrement() const override { return m_heightIncrement; }
75 int heightIncrement() const override { return m_heightIncrement; }
76
76
77+ Mir::ShellChrome shellChrome() const override;
78+
77 ////
79 ////
78 // API for tests
80 // API for tests
79
81
80 Q_INVOKABLE void setLive(bool live);
82 Q_INVOKABLE void setLive(bool live);
83+ Q_INVOKABLE void setShellChrome(Mir::ShellChrome shellChrome);
81
84
82 void registerView(qintptr viewId);
85 void registerView(qintptr viewId);
83 void unregisterView(qintptr viewId);
86 void unregisterView(qintptr viewId);
@@ -109,9 +112,6 @@ public:
109 void setActiveFocus(bool);
112 void setActiveFocus(bool);
110
113
111Q_SIGNALS:
114Q_SIGNALS:
112− void stateChanged(Mir::State);
113− void liveChanged(bool live);
114− void orientationAngleChanged(Mir::OrientationAngle angle);
115 void widthChanged();
115 void widthChanged();
116 void heightChanged();
116 void heightChanged();
117 void slowToResizeChanged();
117 void slowToResizeChanged();
@@ -152,6 +152,8 @@ private:
152 QSize m_delayedResize;
152 QSize m_delayedResize;
153 QSize m_pendingResize;
153 QSize m_pendingResize;
154
154
155+ Mir::ShellChrome m_shellChrome;
156+
155 struct View {
157 struct View {
156 bool visible;
158 bool visible;
157 };
159 };
tests/mocks/Unity/Application/MirSurfaceItem.cpp+9
@@ -100,6 +100,15 @@ bool MirSurfaceItem::live() const
100 }
100 }
101}
101}
102
102
103+Mir::ShellChrome MirSurfaceItem::shellChrome() const
104+{
105+ if (m_qmlSurface) {
106+ return m_qmlSurface->shellChrome();
107+ } else {
108+ return Mir::NormalChrome;
109+ }
110+}
111+
103Mir::OrientationAngle MirSurfaceItem::orientationAngle() const
112Mir::OrientationAngle MirSurfaceItem::orientationAngle() const
104{
113{
105 if (m_qmlSurface) {
114 if (m_qmlSurface) {
tests/mocks/Unity/Application/MirSurfaceItem.h+1
@@ -47,6 +47,7 @@ public:
47 Mir::Type type() const override;
47 Mir::Type type() const override;
48 QString name() const override;
48 QString name() const override;
49 bool live() const override;
49 bool live() const override;
50+ Mir::ShellChrome shellChrome() const override;
50
51
51 Mir::State surfaceState() const override;
52 Mir::State surfaceState() const override;
52 void setSurfaceState(Mir::State) override {}
53 void setSurfaceState(Mir::State) override {}
tests/mocks/Unity/Application/Session.cpp+26−1
@@ -34,6 +34,7 @@ Session::Session(const QString &name,
34 , m_surface(nullptr)
34 , m_surface(nullptr)
35 , m_parentSession(nullptr)
35 , m_parentSession(nullptr)
36 , m_children(new SessionModel(this))
36 , m_children(new SessionModel(this))
37+ , m_fullscreen(false)
37{
38{
38// qDebug() << "Session::Session() " << this->name();
39// qDebug() << "Session::Session() " << this->name();
39
40
@@ -71,6 +72,25 @@ void Session::release()
71 deleteLater();
72 deleteLater();
72}
73}
73
74
75+void Session::updateFullscreenProperty()
76+{
77+ if (m_surfaces.rowCount() > 0) {
78+ // TODO: Figure out something better
79+ setFullscreen(lastSurface()->state() == Mir::FullscreenState);
80+ } else {
81+ // Keep the current value of the fullscreen property until we get a new
82+ // surface
83+ }
84+}
85+
86+void Session::setFullscreen(bool fullscreen)
87+{
88+ if (m_fullscreen != fullscreen) {
89+ m_fullscreen = fullscreen;
90+ Q_EMIT fullscreenChanged(m_fullscreen);
91+ }
92+}
93+
74void Session::setApplication(ApplicationInfo* application)
94void Session::setApplication(ApplicationInfo* application)
75{
95{
76 if (m_application == application)
96 if (m_application == application)
@@ -82,15 +102,18 @@ void Session::setApplication(ApplicationInfo* application)
82
102
83void Session::appendSurface(MirSurface* surface)
103void Session::appendSurface(MirSurface* surface)
84{
104{
85− // qDebug() << "Session::appendSurface - session=" << name() << "surface=" << surface;
105+ qDebug() << "Session::appendSurface - session=" << name() << "surface=" << surface;
86
106
87 m_surfaces.insert(m_surfaces.rowCount(), surface);
107 m_surfaces.insert(m_surfaces.rowCount(), surface);
88
108
109+ connect(surface, &MirSurfaceInterface::stateChanged, this, &Session::updateFullscreenProperty);
89 connect(surface, &QObject::destroyed,
110 connect(surface, &QObject::destroyed,
90 this, [this, surface]() { this->removeSurface(surface); });
111 this, [this, surface]() { this->removeSurface(surface); });
91
112
92 Q_EMIT lastSurfaceChanged(surface);
113 Q_EMIT lastSurfaceChanged(surface);
93 Q_EMIT surfaceAdded(surface);
114 Q_EMIT surfaceAdded(surface);
115+
116+ updateFullscreenProperty();
94}
117}
95
118
96void Session::removeSurface(MirSurface* surface)
119void Session::removeSurface(MirSurface* surface)
@@ -99,6 +122,8 @@ void Session::removeSurface(MirSurface* surface)
99 if (m_surfaces.contains(surface)) {
122 if (m_surfaces.contains(surface)) {
100 m_surfaces.remove(surface);
123 m_surfaces.remove(surface);
101 }
124 }
125+
126+ updateFullscreenProperty();
102}
127}
103
128
104void Session::setScreenshot(const QUrl& screenshot)
129void Session::setScreenshot(const QUrl& screenshot)
tests/mocks/Unity/Application/Session.h+7
@@ -47,6 +47,7 @@ public:
47 //getters
47 //getters
48 QString name() const { return m_name; }
48 QString name() const { return m_name; }
49 bool live() const { return m_live; }
49 bool live() const { return m_live; }
50+ bool fullscreen() const { return m_fullscreen; }
50 ApplicationInfo* application() const { return m_application; }
51 ApplicationInfo* application() const { return m_application; }
51 MirSurface *lastSurface() const;
52 MirSurface *lastSurface() const;
52 ObjectListModel<MirSurface>* surfaces() const;
53 ObjectListModel<MirSurface>* surfaces() const;
@@ -57,6 +58,7 @@ public:
57 void removeSurface(MirSurface* surface);
58 void removeSurface(MirSurface* surface);
58 void setScreenshot(const QUrl& m_screenshot);
59 void setScreenshot(const QUrl& m_screenshot);
59 void setLive(bool live);
60 void setLive(bool live);
61+ void setFullscreen(bool fullscreen);
60
62
61 Q_INVOKABLE void addChildSession(Session* session);
63 Q_INVOKABLE void addChildSession(Session* session);
62 void insertChildSession(uint index, Session* session);
64 void insertChildSession(uint index, Session* session);
@@ -68,6 +70,7 @@ Q_SIGNALS:
68 void liveChanged(bool live);
70 void liveChanged(bool live);
69 void surfaceAdded(MirSurface *surface);
71 void surfaceAdded(MirSurface *surface);
70 void lastSurfaceChanged(MirSurface *surface);
72 void lastSurfaceChanged(MirSurface *surface);
73+ void fullscreenChanged(bool fullscreen);
71
74
72 // internal mock use
75 // internal mock use
73 void deregister();
76 void deregister();
@@ -75,6 +78,9 @@ Q_SIGNALS:
75public Q_SLOTS:
78public Q_SLOTS:
76 Q_INVOKABLE void createSurface();
79 Q_INVOKABLE void createSurface();
77
80
81+private Q_SLOTS:
82+ void updateFullscreenProperty();
83+
78private:
84private:
79 SessionModel* childSessions() const;
85 SessionModel* childSessions() const;
80 void setParentSession(Session* session);
86 void setParentSession(Session* session);
@@ -87,6 +93,7 @@ private:
87 Session* m_parentSession;
93 Session* m_parentSession;
88 SessionModel* m_children;
94 SessionModel* m_children;
89 ObjectListModel<MirSurface> m_surfaces;
95 ObjectListModel<MirSurface> m_surfaces;
96+ bool m_fullscreen;
90
97
91 friend class ApplicationTestInterface;
98 friend class ApplicationTestInterface;
92};
99};
tests/qmltests/Stages/tst_WindowResizeArea.qml+7
@@ -72,6 +72,13 @@ Item {
72 windowId: "test-window-id"
72 windowId: "test-window-id"
73 screenWidth: root.width
73 screenWidth: root.width
74 screenHeight: root.height
74 screenHeight: root.height
75+
76+ Component.onCompleted: {
77+ loadWindowState();
78+ }
79+ Component.onDestruction: {
80+ saveWindowState();
81+ }
75 }
82 }
76
83
77 Rectangle {
84 Rectangle {
tests/qmltests/tst_Shell.qml+62
@@ -131,6 +131,9 @@ Rectangle {
131 anchors.right: root.right
131 anchors.right: root.right
132 width: units.gu(30)
132 width: units.gu(30)
133
133
134+ property var focusedApp: ApplicationManager.findApplication(ApplicationManager.focusedApplicationId)
135+ property var focusedSurface: focusedApp && focusedApp.session ? focusedApp.session.lastSurface : null
136+
134 Rectangle {
137 Rectangle {
135 id: controlRect
138 id: controlRect
136 anchors { left: parent.left; right: parent.right }
139 anchors { left: parent.left; right: parent.right }
@@ -263,6 +266,65 @@ Rectangle {
263 appId: modelData
266 appId: modelData
264 }
267 }
265 }
268 }
269+
270+ Label { text: "Focused Application"; font.bold: true }
271+
272+ Row {
273+ CheckBox {
274+ id: fullscreeAppCheck
275+
276+ onTriggered: {
277+ if (!controls.focusedSurface) return;
278+ if (controls.focusedSurface.state == Mir.FullscreenState) {
279+ controls.focusedSurface.state = Mir.RestoredState;
280+ } else {
281+ controls.focusedSurface.state = Mir.FullscreenState;
282+ }
283+ }
284+
285+ Binding {
286+ target: fullscreeAppCheck
287+ when: controls.focusedSurface
288+ property: "checked"
289+ value: {
290+ if (!controls.focusedSurface) return false;
291+ return controls.focusedSurface.state === Mir.FullscreenState
292+ }
293+ }
294+ }
295+ Label {
296+ text: "Fullscreen"
297+ }
298+ }
299+
300+ Row {
301+ CheckBox {
302+ id: chromeAppCheck
303+
304+ onTriggered: {
305+ if (!controls.focusedSurface) return;
306+ if (controls.focusedSurface.shellChrome == Mir.LowChrome) {
307+ controls.focusedSurface.setShellChrome(Mir.NormalChrome);
308+ } else {
309+ controls.focusedSurface.setShellChrome(Mir.LowChrome);
310+ }
311+ }
312+
313+ Binding {
314+ target: chromeAppCheck
315+ when: controls.focusedSurface !== null
316+ property: "checked"
317+ value: {
318+ if (!controls.focusedSurface) return false;
319+ controls.focusedSurface.shellChrome === Mir.LowChrome
320+ }
321+ }
322+ }
323+ Label {
324+ text: "Low Chrome"
325+ }
326+ }
327+
266 }
328 }
267 }
329 }
268 }
330 }