on xenial help sign in
Add support for low shell chrome. Fixes: #1535397

Approved by: Gerry Boland

commit52892bc0725b565ff7d03051ae598460298e042e authorMichael Zanetti <michael.zanetti@canonical.com> dateWed 23 Mar 2016, 09:49 (11 years ago) parent8a1d6e7a9caa24
20 files changed+367−28
qml/Components/InputMethod.qml+4−1 qml/Shell.qml+5−1 qml/Stages/AbstractStage.qml+2 qml/Stages/DesktopStage.qml+71−12 qml/Stages/PhoneStage.qml+10 qml/Stages/StagedFullscreenPolicy.qml+58 qml/Stages/TabletStage.qml+9 qml/Stages/WindowResizeArea.qml+2−2 qml/Stages/WindowedFullscreenPolicy.qml+41 tests/mocks/Unity/Application/ApplicationInfo.cpp+21−3 tests/mocks/Unity/Application/ApplicationInfo.h+5−1 tests/mocks/Unity/Application/ApplicationManager.cpp+6−4 tests/mocks/Unity/Application/MirSurface.cpp+16 tests/mocks/Unity/Application/MirSurface.h+5−3 tests/mocks/Unity/Application/MirSurfaceItem.cpp+9 tests/mocks/Unity/Application/MirSurfaceItem.h+1 tests/mocks/Unity/Application/Session.cpp+26−1 tests/mocks/Unity/Application/Session.h+7 tests/qmltests/Stages/tst_WindowResizeArea.qml+7 tests/qmltests/tst_Shell.qml+62
qml/Components/InputMethod.qml+4−1
@@ -51,7 +51,10 @@ Item {
5151 }
5252
5353 state: {
54− if (surfaceItem.surface && surfaceItem.surfaceState != Mir.MinimizedState && root.enabled) {
54+ if (surfaceItem.surface &&
55+ surfaceItem.surfaceState != Mir.HiddenState &&
56+ surfaceItem.surfaceState != Mir.MinimizedState &&
57+ root.enabled) {
5558 return "shown";
5659 } else {
5760 return "hidden";
qml/Shell.qml+5−1
@@ -246,7 +246,7 @@ Item {
246246 property string usageScenario: shell.usageScenario === "phone" || greeter.hasLockedApp
247247 ? "phone"
248248 : shell.usageScenario
249− source: {
249+ readonly property string qmlComponent: {
250250 if(shell.mode === "greeter") {
251251 return "Stages/ShimStage.qml"
252252 } else if (applicationsDisplayLoader.usageScenario === "phone") {
@@ -257,6 +257,10 @@ Item {
257257 return "Stages/DesktopStage.qml";
258258 }
259259 }
260+ onQmlComponentChanged: {
261+ if (item) item.stageAboutToBeUnloaded();
262+ source = qmlComponent;
263+ }
260264
261265 property bool interactive: tutorial.spreadEnabled
262266 && (!greeter || !greeter.shown)
qml/Stages/AbstractStage.qml+2
@@ -51,6 +51,8 @@ Rectangle {
5151 | Qt.InvertedPortraitOrientation
5252 | Qt.InvertedLandscapeOrientation
5353
54+ signal stageAboutToBeUnloaded
55+
5456 // Shared code for use in stage implementations
5557 GSettings {
5658 id: lifecycleExceptions
qml/Stages/DesktopStage.qml+71−12
@@ -255,8 +255,8 @@ AbstractStage {
255255 focus: appId === priv.focusedAppId
256256 width: decoratedWindow.width
257257 height: decoratedWindow.height
258− property alias requestedWidth: decoratedWindow.requestedWidth
259− property alias requestedHeight: decoratedWindow.requestedHeight
258+ property int requestedWidth: -1
259+ property int requestedHeight: -1
260260 property alias minimumWidth: decoratedWindow.minimumWidth
261261 property alias minimumHeight: decoratedWindow.minimumHeight
262262 property alias maximumWidth: decoratedWindow.maximumWidth
@@ -369,8 +369,10 @@ AbstractStage {
369369 name: "fullscreen"; when: decoratedWindow.fullscreen
370370 PropertyChanges {
371371 target: appDelegate;
372− x: 0; y: -PanelState.panelHeight
373− requestedWidth: appContainer.width; requestedHeight: appContainer.height;
372+ x: 0;
373+ y: -PanelState.panelHeight
374+ requestedWidth: appContainer.width;
375+ requestedHeight: appContainer.height;
374376 }
375377 },
376378 State {
@@ -387,21 +389,42 @@ AbstractStage {
387389 name: "maximized"; when: appDelegate.maximized && !appDelegate.minimized
388390 PropertyChanges {
389391 target: appDelegate;
390− x: root.leftMargin; y: 0;
391− requestedWidth: appContainer.width - root.leftMargin; requestedHeight: appContainer.height;
392+ x: root.leftMargin;
393+ y: 0;
392394 visuallyMinimized: false;
393395 visuallyMaximized: true
394396 }
397+ PropertyChanges {
398+ target: decoratedWindow
399+ requestedWidth: appContainer.width - root.leftMargin;
400+ requestedHeight: appContainer.height;
401+ }
395402 },
396403 State {
397404 name: "maximizedLeft"; when: appDelegate.maximizedLeft && !appDelegate.minimized
398− PropertyChanges { target: appDelegate; x: root.leftMargin; y: PanelState.panelHeight;
399− requestedWidth: (appContainer.width - root.leftMargin)/2; requestedHeight: appContainer.height - PanelState.panelHeight }
405+ PropertyChanges {
406+ target: appDelegate
407+ x: root.leftMargin
408+ y: PanelState.panelHeight
409+ }
410+ PropertyChanges {
411+ target: decoratedWindow
412+ requestedWidth: (appContainer.width - root.leftMargin)/2
413+ requestedHeight: appContainer.height - PanelState.panelHeight
414+ }
400415 },
401416 State {
402417 name: "maximizedRight"; when: appDelegate.maximizedRight && !appDelegate.minimized
403− PropertyChanges { target: appDelegate; x: (appContainer.width + root.leftMargin)/2; y: PanelState.panelHeight;
404− requestedWidth: (appContainer.width - root.leftMargin)/2; requestedHeight: appContainer.height - PanelState.panelHeight }
418+ PropertyChanges {
419+ target: appDelegate;
420+ x: (appContainer.width + root.leftMargin)/2
421+ y: PanelState.panelHeight
422+ }
423+ PropertyChanges {
424+ target: decoratedWindow
425+ requestedWidth: (appContainer.width - root.leftMargin)/2
426+ requestedHeight: appContainer.height - PanelState.panelHeight
427+ }
405428 },
406429 State {
407430 name: "minimized"; when: appDelegate.minimized
@@ -421,13 +444,17 @@ AbstractStage {
421444 enabled: appDelegate.animationsEnabled
422445 PropertyAction { target: appDelegate; properties: "visuallyMinimized,visuallyMaximized" }
423446 UbuntuNumberAnimation { target: appDelegate; properties: "x,y,opacity,requestedWidth,requestedHeight,scale"; duration: UbuntuAnimation.FastDuration }
447+ UbuntuNumberAnimation { target: decoratedWindow; properties: "requestedWidth,requestedHeight"; duration: UbuntuAnimation.FastDuration }
424448 },
425449 Transition {
426450 to: "minimized"
427451 enabled: appDelegate.animationsEnabled
428452 PropertyAction { target: appDelegate; property: "visuallyMaximized" }
429453 SequentialAnimation {
430− UbuntuNumberAnimation { target: appDelegate; properties: "x,y,opacity,requestedWidth,requestedHeight,scale"; duration: UbuntuAnimation.FastDuration }
454+ ParallelAnimation {
455+ UbuntuNumberAnimation { target: appDelegate; properties: "x,y,opacity,scale"; duration: UbuntuAnimation.FastDuration }
456+ UbuntuNumberAnimation { target: decoratedWindow; properties: "requestedWidth,requestedHeight"; duration: UbuntuAnimation.FastDuration }
457+ }
431458 PropertyAction { target: appDelegate; property: "visuallyMinimized" }
432459 ScriptAction {
433460 script: {
@@ -443,7 +470,10 @@ AbstractStage {
443470 enabled: appDelegate.animationsEnabled
444471 PropertyAction { target: appDelegate; property: "visuallyMinimized" }
445472 SequentialAnimation {
446− UbuntuNumberAnimation { target: appDelegate; properties: "x,y,opacity,requestedWidth,requestedHeight,scale"; duration: UbuntuAnimation.FastDuration }
473+ ParallelAnimation {
474+ UbuntuNumberAnimation { target: appDelegate; properties: "x,y,opacity,scale"; duration: UbuntuAnimation.FastDuration }
475+ UbuntuNumberAnimation { target: decoratedWindow; properties: "requestedWidth,requestedHeight"; duration: UbuntuAnimation.FastDuration }
476+ }
447477 PropertyAction { target: appDelegate; property: "visuallyMaximized" }
448478 }
449479 }
@@ -458,6 +488,7 @@ AbstractStage {
458488 }
459489
460490 WindowResizeArea {
491+ id: resizeArea
461492 objectName: "windowResizeArea"
462493 target: appDelegate
463494 minWidth: units.gu(10)
@@ -469,6 +500,25 @@ AbstractStage {
469500 leftMargin: root.leftMargin
470501
471502 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+ }
472522 }
473523
474524 DecoratedWindow {
@@ -480,12 +530,21 @@ AbstractStage {
480530 active: ApplicationManager.focusedApplicationId === model.appId
481531 focus: true
482532
533+ requestedWidth: appDelegate.requestedWidth
534+ requestedHeight: appDelegate.requestedHeight
535+
483536 onClose: ApplicationManager.stopApplication(model.appId)
484537 onMaximize: appDelegate.maximized || appDelegate.maximizedLeft || appDelegate.maximizedRight
485538 ? appDelegate.restoreFromMaximized() : appDelegate.maximize()
486539 onMinimize: appDelegate.minimize()
487540 onDecorationPressed: { ApplicationManager.focusApplication(model.appId) }
488541 }
542+
543+ WindowedFullscreenPolicy {
544+ id: fullscreenPolicy
545+ active: true
546+ application: decoratedWindow.application
547+ }
489548 }
490549 }
491550 }
qml/Stages/PhoneStage.qml+10
@@ -629,6 +629,16 @@ AbstractStage {
629629 property: "focusedAppOrientationChangesEnabled"
630630 value: orientationChangesEnabled
631631 }
632+
633+ StagedFullscreenPolicy {
634+ id: fullscreenPolicy
635+ application: appDelegate.application
636+ }
637+
638+ Connections {
639+ target: root
640+ onStageAboutToBeUnloaded: fullscreenPolicy.active = false
641+ }
632642 }
633643 }
634644 }
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 {
947947 period: (spreadView.positionMarker2 - spreadView.positionMarker1) / 3
948948 progress: spreadTile.progress - spreadView.positionMarker1
949949 }
950+
951+ StagedFullscreenPolicy {
952+ id: fullscreenPolicy
953+ application: spreadTile.application
954+ }
955+ Connections {
956+ target: root
957+ onStageAboutToBeUnloaded: fullscreenPolicy.active = false
958+ }
950959 }
951960 }
952961 }
qml/Stages/WindowResizeArea.qml+2−2
@@ -69,7 +69,7 @@ MouseArea {
6969 onHeightChanged: priv.updateNormalGeometry();
7070 }
7171
72− Component.onCompleted: {
72+ function loadWindowState() {
7373 var windowGeometry = windowStateStorage.getGeometry(root.windowId,
7474 Qt.rect(target.x, target.y, defaultWidth, defaultHeight));
7575
@@ -87,7 +87,7 @@ MouseArea {
8787 priv.updateNormalGeometry();
8888 }
8989
90− Component.onDestruction: {
90+ function saveWindowState() {
9191 windowStateStorage.saveState(root.windowId, target.state == "maximized" ? WindowStateStorage.WindowStateMaximized : WindowStateStorage.WindowStateNormal)
9292 windowStateStorage.saveGeometry(root.windowId, Qt.rect(priv.normalX, priv.normalY, priv.normalWidth, priv.normalHeight))
9393 }
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)
4444 , m_isTouchApp(true)
4545 , m_exemptFromLifecycle(false)
4646 , m_manualSurfaceCreation(false)
47+ , m_shellChrome(Mir::NormalChrome)
4748{
4849}
4950
@@ -63,6 +64,7 @@ ApplicationInfo::ApplicationInfo(QObject *parent)
6364 , m_isTouchApp(true)
6465 , m_exemptFromLifecycle(false)
6566 , m_manualSurfaceCreation(false)
67+ , m_shellChrome(Mir::NormalChrome)
6668{
6769}
6870
@@ -102,9 +104,11 @@ void ApplicationInfo::setSession(Session* session)
102104 if (m_session) {
103105 m_session->setApplication(this);
104106 m_session->setParent(this);
107+ m_session->setFullscreen(m_fullscreen);
105108 SessionManager::singleton()->registerSession(m_session);
106109 connect(m_session, &Session::surfaceAdded,
107110 this, &ApplicationInfo::onSessionSurfaceAdded);
111+ connect(m_session, &Session::fullscreenChanged, this, &ApplicationInfo::fullscreenChanged);
108112
109113 if (!m_manualSurfaceCreation) {
110114 QTimer::singleShot(500, m_session, &Session::createSurface);
@@ -191,12 +195,17 @@ void ApplicationInfo::setFocused(bool value)
191195
192196void ApplicationInfo::setFullscreen(bool value)
193197{
194− if (value != m_fullscreen) {
195− m_fullscreen = value;
196− Q_EMIT fullscreenChanged(value);
198+ m_fullscreen = value;
199+ if (m_session) {
200+ m_session->setFullscreen(value);
197201 }
198202}
199203
204+bool ApplicationInfo::fullscreen() const
205+{
206+ return m_session ? m_session->fullscreen() : m_fullscreen;
207+}
208+
200209void ApplicationInfo::setManualSurfaceCreation(bool value)
201210{
202211 if (value != m_manualSurfaceCreation) {
@@ -262,6 +271,7 @@ void ApplicationInfo::onSessionSurfaceAdded(MirSurface* surface)
262271 } else {
263272 setState(Suspended);
264273 }
274+ surface->setShellChrome(m_shellChrome);
265275 }
266276}
267277
@@ -291,3 +301,11 @@ void ApplicationInfo::setInitialSurfaceSize(const QSize &size)
291301 Q_EMIT initialSurfaceSizeChanged(m_initialSurfaceSize);
292302 }
293303}
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;
2424
2525// unity-api
2626#include <unity/shell/application/ApplicationInfoInterface.h>
27+#include <unity/shell/application/Mir.h>
2728
2829using namespace unity::shell::application;
2930
@@ -78,7 +79,7 @@ public:
7879 QString screenshot() const { return m_screenshotFileName; }
7980
8081 void setFullscreen(bool value);
81− bool fullscreen() const { return m_fullscreen; }
82+ bool fullscreen() const;
8283
8384 Qt::ScreenOrientations supportedOrientations() const override;
8485 void setSupportedOrientations(Qt::ScreenOrientations orientations);
@@ -97,6 +98,8 @@ public:
9798
9899 QSize initialSurfaceSize() const override;
99100 void setInitialSurfaceSize(const QSize &size) override;
101+
102+ Q_INVOKABLE void setShellChrome(Mir::ShellChrome shellChrome);
100103public:
101104 void setSession(Session* session);
102105 Session* session() const { return m_session; }
@@ -134,6 +137,7 @@ private:
134137 QSize m_initialSurfaceSize;
135138
136139 bool m_manualSurfaceCreation;
140+ Mir::ShellChrome m_shellChrome;
137141};
138142
139143Q_DECLARE_METATYPE(ApplicationInfo*)
tests/mocks/Unity/Application/ApplicationManager.cpp+6−4
@@ -328,6 +328,7 @@ void ApplicationManager::buildListOfAvailableApplications()
328328 application->setName("Camera");
329329 application->setScreenshotId("camera");
330330 application->setIconId("camera");
331+ application->setShellChrome(Mir::LowChrome);
331332 application->setFullscreen(true);
332333 application->setSupportedOrientations(Qt::PortraitOrientation
333334 | Qt::LandscapeOrientation
@@ -341,7 +342,8 @@ void ApplicationManager::buildListOfAvailableApplications()
341342 application->setName("Gallery");
342343 application->setScreenshotId("gallery");
343344 application->setIconId("gallery");
344− application->setFullscreen(true);
345+ application->setShellChrome(Mir::LowChrome);
346+ application->setStage(ApplicationInfo::MainStage);
345347 m_availableApplications.append(application);
346348
347349 application = new ApplicationInfo(this);
@@ -353,7 +355,7 @@ void ApplicationManager::buildListOfAvailableApplications()
353355
354356 application = new ApplicationInfo(this);
355357 application->setAppId("webbrowser-app");
356− application->setFullscreen(true);
358+ application->setShellChrome(Mir::LowChrome);
357359 application->setName("Browser");
358360 application->setScreenshotId("browser");
359361 application->setIconId("browser");
@@ -378,7 +380,7 @@ void ApplicationManager::buildListOfAvailableApplications()
378380 application->setName("GMail");
379381 application->setIconId("gmail");
380382 application->setScreenshotId("gmail-webapp.svg");
381− application->setFullscreen(false);
383+ application->setStage(ApplicationInfo::MainStage);
382384 application->setSupportedOrientations(Qt::PortraitOrientation
383385 | Qt::LandscapeOrientation
384386 | Qt::InvertedPortraitOrientation
@@ -390,7 +392,7 @@ void ApplicationManager::buildListOfAvailableApplications()
390392 application->setName("Music");
391393 application->setIconId("soundcloud");
392394 application->setScreenshotId("music");
393− application->setFullscreen(false);
395+ application->setStage(ApplicationInfo::MainStage);
394396 application->setSupportedOrientations(Qt::PortraitOrientation
395397 | Qt::LandscapeOrientation
396398 | Qt::InvertedPortraitOrientation
tests/mocks/Unity/Application/MirSurface.cpp+16
@@ -38,6 +38,7 @@ MirSurface::MirSurface(const QString& name,
3838 , m_width(-1)
3939 , m_height(-1)
4040 , m_slowToResize(false)
41+ , m_shellChrome(Mir::NormalChrome)
4142{
4243// qDebug() << "MirSurface::MirSurface() " << name;
4344 QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership);
@@ -134,6 +135,21 @@ void MirSurface::setOrientationAngle(Mir::OrientationAngle angle)
134135}
135136
136137
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+
137153
138154void MirSurface::registerView(qintptr viewId)
139155{
tests/mocks/Unity/Application/MirSurface.h+5−3
@@ -74,10 +74,13 @@ public:
7474 int widthIncrement() const override { return m_widthIncrement; }
7575 int heightIncrement() const override { return m_heightIncrement; }
7676
77+ Mir::ShellChrome shellChrome() const override;
78+
7779 ////
7880 // API for tests
7981
8082 Q_INVOKABLE void setLive(bool live);
83+ Q_INVOKABLE void setShellChrome(Mir::ShellChrome shellChrome);
8184
8285 void registerView(qintptr viewId);
8386 void unregisterView(qintptr viewId);
@@ -109,9 +112,6 @@ public:
109112 void setActiveFocus(bool);
110113
111114Q_SIGNALS:
112− void stateChanged(Mir::State);
113− void liveChanged(bool live);
114− void orientationAngleChanged(Mir::OrientationAngle angle);
115115 void widthChanged();
116116 void heightChanged();
117117 void slowToResizeChanged();
@@ -152,6 +152,8 @@ private:
152152 QSize m_delayedResize;
153153 QSize m_pendingResize;
154154
155+ Mir::ShellChrome m_shellChrome;
156+
155157 struct View {
156158 bool visible;
157159 };
tests/mocks/Unity/Application/MirSurfaceItem.cpp+9
@@ -100,6 +100,15 @@ bool MirSurfaceItem::live() const
100100 }
101101}
102102
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+
103112Mir::OrientationAngle MirSurfaceItem::orientationAngle() const
104113{
105114 if (m_qmlSurface) {
tests/mocks/Unity/Application/MirSurfaceItem.h+1
@@ -47,6 +47,7 @@ public:
4747 Mir::Type type() const override;
4848 QString name() const override;
4949 bool live() const override;
50+ Mir::ShellChrome shellChrome() const override;
5051
5152 Mir::State surfaceState() const override;
5253 void setSurfaceState(Mir::State) override {}
tests/mocks/Unity/Application/Session.cpp+26−1
@@ -34,6 +34,7 @@ Session::Session(const QString &name,
3434 , m_surface(nullptr)
3535 , m_parentSession(nullptr)
3636 , m_children(new SessionModel(this))
37+ , m_fullscreen(false)
3738{
3839// qDebug() << "Session::Session() " << this->name();
3940
@@ -71,6 +72,25 @@ void Session::release()
7172 deleteLater();
7273}
7374
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+
7494void Session::setApplication(ApplicationInfo* application)
7595{
7696 if (m_application == application)
@@ -82,15 +102,18 @@ void Session::setApplication(ApplicationInfo* application)
82102
83103void Session::appendSurface(MirSurface* surface)
84104{
85− // qDebug() << "Session::appendSurface - session=" << name() << "surface=" << surface;
105+ qDebug() << "Session::appendSurface - session=" << name() << "surface=" << surface;
86106
87107 m_surfaces.insert(m_surfaces.rowCount(), surface);
88108
109+ connect(surface, &MirSurfaceInterface::stateChanged, this, &Session::updateFullscreenProperty);
89110 connect(surface, &QObject::destroyed,
90111 this, [this, surface]() { this->removeSurface(surface); });
91112
92113 Q_EMIT lastSurfaceChanged(surface);
93114 Q_EMIT surfaceAdded(surface);
115+
116+ updateFullscreenProperty();
94117}
95118
96119void Session::removeSurface(MirSurface* surface)
@@ -99,6 +122,8 @@ void Session::removeSurface(MirSurface* surface)
99122 if (m_surfaces.contains(surface)) {
100123 m_surfaces.remove(surface);
101124 }
125+
126+ updateFullscreenProperty();
102127}
103128
104129void Session::setScreenshot(const QUrl& screenshot)
tests/mocks/Unity/Application/Session.h+7
@@ -47,6 +47,7 @@ public:
4747 //getters
4848 QString name() const { return m_name; }
4949 bool live() const { return m_live; }
50+ bool fullscreen() const { return m_fullscreen; }
5051 ApplicationInfo* application() const { return m_application; }
5152 MirSurface *lastSurface() const;
5253 ObjectListModel<MirSurface>* surfaces() const;
@@ -57,6 +58,7 @@ public:
5758 void removeSurface(MirSurface* surface);
5859 void setScreenshot(const QUrl& m_screenshot);
5960 void setLive(bool live);
61+ void setFullscreen(bool fullscreen);
6062
6163 Q_INVOKABLE void addChildSession(Session* session);
6264 void insertChildSession(uint index, Session* session);
@@ -68,6 +70,7 @@ Q_SIGNALS:
6870 void liveChanged(bool live);
6971 void surfaceAdded(MirSurface *surface);
7072 void lastSurfaceChanged(MirSurface *surface);
73+ void fullscreenChanged(bool fullscreen);
7174
7275 // internal mock use
7376 void deregister();
@@ -75,6 +78,9 @@ Q_SIGNALS:
7578public Q_SLOTS:
7679 Q_INVOKABLE void createSurface();
7780
81+private Q_SLOTS:
82+ void updateFullscreenProperty();
83+
7884private:
7985 SessionModel* childSessions() const;
8086 void setParentSession(Session* session);
@@ -87,6 +93,7 @@ private:
8793 Session* m_parentSession;
8894 SessionModel* m_children;
8995 ObjectListModel<MirSurface> m_surfaces;
96+ bool m_fullscreen;
9097
9198 friend class ApplicationTestInterface;
9299};
tests/qmltests/Stages/tst_WindowResizeArea.qml+7
@@ -72,6 +72,13 @@ Item {
7272 windowId: "test-window-id"
7373 screenWidth: root.width
7474 screenHeight: root.height
75+
76+ Component.onCompleted: {
77+ loadWindowState();
78+ }
79+ Component.onDestruction: {
80+ saveWindowState();
81+ }
7582 }
7683
7784 Rectangle {
tests/qmltests/tst_Shell.qml+62
@@ -131,6 +131,9 @@ Rectangle {
131131 anchors.right: root.right
132132 width: units.gu(30)
133133
134+ property var focusedApp: ApplicationManager.findApplication(ApplicationManager.focusedApplicationId)
135+ property var focusedSurface: focusedApp && focusedApp.session ? focusedApp.session.lastSurface : null
136+
134137 Rectangle {
135138 id: controlRect
136139 anchors { left: parent.left; right: parent.right }
@@ -263,6 +266,65 @@ Rectangle {
263266 appId: modelData
264267 }
265268 }
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+
266328 }
267329 }
268330 }