Merge lp:~unity-team/unity8/externalMonitor into lp:unity8

Proposed by Michał Sawicz on 2015-10-08
Status: Merged
Approved by: Gerry Boland on 2015-10-21
Approved revision: 2007
Merged at revision: 2008
Proposed branch: lp:~unity-team/unity8/externalMonitor
Merge into: lp:unity8
Prerequisite: lp:~unity-team/unity8/mousePointer
Diff against target: 1322 lines (+748/-169)
29 files modified
debian/unity8.install (+1/-0)
qml/Components/WallpaperResolver.qml (+63/-0)
qml/DeviceConfiguration.qml (+1/-0)
qml/DisabledScreenNotice.qml (+49/-0)
qml/Notifications/Notification.qml (+2/-0)
qml/Notifications/NotificationMenuItemFactory.qml (+2/-1)
qml/Notifications/Notifications.qml (+2/-0)
qml/OrientedShell.qml (+10/-1)
qml/Rotation/RotationStates.qml (+2/-2)
qml/Shell.qml (+9/-32)
src/ApplicationArguments.h (+10/-2)
src/CMakeLists.txt (+3/-0)
src/SecondaryWindow.cpp (+31/-0)
src/SecondaryWindow.h (+30/-0)
src/ShellApplication.cpp (+197/-0)
src/ShellApplication.h (+55/-0)
src/ShellView.cpp (+59/-0)
src/ShellView.h (+34/-0)
src/main.cpp (+4/-101)
tests/autopilot/unity8/fixture_setup.py (+1/-1)
tests/autopilot/unity8/greeter/tests/__init__.py (+1/-1)
tests/autopilot/unity8/launcher.py (+1/-1)
tests/autopilot/unity8/shell/__init__.py (+1/-1)
tests/autopilot/unity8/shell/tests/__init__.py (+1/-1)
tests/qmltests/CMakeLists.txt (+2/-0)
tests/qmltests/Components/tst_WallpaperResolver.qml (+89/-0)
tests/qmltests/tst_DisabledScreenNotice.qml (+37/-0)
tests/qmltests/tst_OrientedShell.qml (+51/-1)
tests/qmltests/tst_Shell.qml (+0/-24)
To merge this branch: bzr merge lp:~unity-team/unity8/externalMonitor
Reviewer Review Type Date Requested Status
Gerry Boland 2015-10-08 Approve on 2015-10-21
PS Jenkins bot continuous-integration 2015-10-08 Needs Fixing on 2015-10-20
Michał Sawicz Pending
Lukáš Tinkl 2015-10-08 Pending
Review via email: mp+273829@code.launchpad.net

This proposal supersedes a proposal from 2015-10-05.

Commit Message

Initial multi-monitor support

When an external screen appears, move the shell to it

Description of the Change

Works best on a laptop.

Known issues:
- Input events come at very irregular intervals (if at all) when an external monitor is connected to a phone or tablet. Works fine on a laptop connected to an external monitor.

* Are there any related MPs required for this MP to build/function as expected? Please list.
https://code.launchpad.net/~unity-team/qtmir/multimonitorNext/+merge/274516

* Did you perform an exploratory manual test run of your code change and any related functionality?
Yes. Works fine on laptops/desktops. On phones you can get mixed results due to known Mir bugs.

* Did you make sure that your branch does not contain spurious tags?
Yes

* If you changed the packaging (debian), did you subscribe the ubuntu-unity team to this MP?
N/A

* If you changed the UI, has there been a design review?
N/A

To post a comment you must log in.
Michał Sawicz (saviq) wrote : Posted in a previous version of this proposal

I tried this on a Nexus 7, here's my results:
- with my monitor it takes a significant amount of time to switch (like 30-40s) when the device seems locked up, the external screen switches modes back and forth
- only settled down with my TV cold-plugged, hotplugging worked after that, too
- external and internal screens are swapped (I get the splash screen over HDMI)
- touch doesn't work on the internal screen, where the UI is displayed
- splash screen doesn't fill the whole screen (resolution is reported correctly)
- if I can see correctly, the whole shell is reinitialized when switching, that might explain some of the delay

Trying on Nexus 4 now, will report back soon.

review: Needs Fixing
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal

> I tried this on a Nexus 7, here's my results:
> - with my monitor it takes a significant amount of time to switch (like
> 30-40s) when the device seems locked up, the external screen switches modes
> back and forth
> - only settled down with my TV cold-plugged, hotplugging worked after that,
> too
> - external and internal screens are swapped (I get the splash screen over
> HDMI)
> - touch doesn't work on the internal screen, where the UI is displayed
> - splash screen doesn't fill the whole screen (resolution is reported
> correctly)
> - if I can see correctly, the whole shell is reinitialized when switching,
> that might explain some of the delay
>
> Trying on Nexus 4 now, will report back soon.

You likely missing https://code.launchpad.net/~dandrader/qtmir/multimonitorNext/+merge/273303

Gerry Boland (gerboland) wrote : Posted in a previous version of this proposal

Note also, current Mir has multimonitor stability & functionality issues. USC is crashy on hot plug, and brings down unity8 with it. Touch input broken is also Mir issue.

Gerry Boland (gerboland) wrote : Posted in a previous version of this proposal

"SecondaryDisplay" not a name I like. Qt uses the term: "Screen", never "Display". Also it's more than a secondary screen, it has actual purpose. How about "DisabledScreenSplash" ? It's just inevitable we'll rename it when we do more advanced MM stuff.

"BackgroundResolver" - since the term "background" is heavily loaded, could this be renamed to "BackgroundImageResolver" or "WallpaperResolver"?

Did design really want the user's personal wallpaper to be used as background on the 'secondary' screen?

+++ qml/Components/BackgroundResolver.qml
+ /*
+ Users should set their UI width here.
+ */
+ property real width
Shell.qml prefers using "//" for multiline comments. Dunno if there's a standard.
What uses "width" in here?

+++ src/SecondaryWindow.cpp
+ setTitle(QStringLiteral("Unity8 Shell - Secundary Screen"));
typo
Am surprised you not inheriting QQuickWindow, but if that's all you need, great!

review: Needs Fixing
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal

On 05/10/2015 11:46, Gerry Boland wrote:
> Review: Needs Fixing
>
> "SecondaryDisplay" not a name I like. Qt uses the term: "Screen", never "Display". Also it's more than a secondary screen, it has actual purpose. How about "DisabledScreenSplash" ? It's just inevitable we'll rename it when we do more advanced MM stuff.

Renamed to DisabledScreenNotice.qml

> "BackgroundResolver" - since the term "background" is heavily loaded, could this be renamed to "BackgroundImageResolver" or "WallpaperResolver"?

Renamed to WallpaperResolver

> Did design really want the user's personal wallpaper to be used as background on the 'secondary' screen?

This didn't go through design at all. I don't see what's the problem
with using the background image there. This is just a placeholder for
the first iteration of multimonitor support anyway.

>
> +++ qml/Components/BackgroundResolver.qml
> + /*
> + Users should set their UI width here.
> + */
> + property real width
> Shell.qml prefers using "//" for multiline comments. Dunno if there's a standard.
> What uses "width" in here?

Done.

>
>
> +++ src/SecondaryWindow.cpp
> + setTitle(QStringLiteral("Unity8 Shell - Secundary Screen"));
> typo
> Am surprised you not inheriting QQuickWindow, but if that's all you need, great!
>

QQuickView inherits QQuickWindow.

Lukáš Tinkl (lukas-kde) wrote : Posted in a previous version of this proposal

+ property var _asImageTester: Image {
49 + id: asImageTester
50 + source: "Adas"//AccountsService.backgroundFile != undefined && AccountsService.backgroundFile.length > 0 ? AccountsService.backgroundFile : ""

Looks like a typo here

review: Needs Fixing
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal

On 07/10/2015 07:46, Lukáš Tinkl wrote:
> Review: Needs Fixing
>
> + property var _asImageTester: Image {
> 49 + id: asImageTester
> 50 + source: "Adas"//AccountsService.backgroundFile != undefined && AccountsService.backgroundFile.length > 0 ? AccountsService.backgroundFile : ""
>
> Looks like a typo here

Fixed. Thanks.

Gerry Boland (gerboland) wrote : Posted in a previous version of this proposal

> > Did design really want the user's personal wallpaper to be used as
> background on the 'secondary' screen?
>
> This didn't go through design at all. I don't see what's the problem
> with using the background image there. This is just a placeholder for
> the first iteration of multimonitor support anyway.

Fair enough

> >
> > +++ src/SecondaryWindow.cpp
> > + setTitle(QStringLiteral("Unity8 Shell - Secundary Screen"));
> > typo
> > Am surprised you not inheriting QQuickWindow, but if that's all you need,
> great!
> >
> QQuickView inherits QQuickWindow.
Ah, I always think it is the other way around.

Lukáš Tinkl (lukas-kde) wrote : Posted in a previous version of this proposal

Text conflict in CMakeLists.txt

review: Needs Fixing
Michał Sawicz (saviq) wrote : Posted in a previous version of this proposal

W dniu 08.10.2015 o 14:38, Lukáš Tinkl pisze:
> Text conflict in CMakeLists.txt
Fixed (resubmitted on top of the right branch and merged).

Gerry Boland (gerboland) wrote :

No further code complaints. Testing in silo22, this is working as well as it can.

review: Approve
Gerry Boland (gerboland) wrote :

m_qmlEngine is not being deleted anywhere, I think is reason for hang.

Found a bug on desktop - for single monitor case, keyboard events not being delivered to any QWindow. You need to call
m_shellView->requestActivate();
for it.

review: Needs Fixing
2007. By Daniel d'Andrada on 2015-10-20

Some more deletions

Daniel d'Andrada (dandrader) wrote :

On 20/10/2015 14:02, Gerry Boland wrote:
> Review: Needs Fixing
>
> m_qmlEngine is not being deleted anywhere, I think is reason for hang.

Done.

>
> Found a bug on desktop - for single monitor case, keyboard events not being delivered to any QWindow. You need to call
> m_shellView->requestActivate();
> for it.

Can't reproduce it on my laptop. m_shellView->showFullScreen()
internally calls m_shellView->requestActivate(). Or is that after
connecting and disconnecting a monitor? Need more info...

Daniel d'Andrada (dandrader) wrote :

> On 20/10/2015 14:02, Gerry Boland wrote:
> > Review: Needs Fixing
> >
> > m_qmlEngine is not being deleted anywhere, I think is reason for hang.
>
> Done.
>
> >
> > Found a bug on desktop - for single monitor case, keyboard events not being
> delivered to any QWindow. You need to call
> > m_shellView->requestActivate();
> > for it.
>
> Can't reproduce it on my laptop. m_shellView->showFullScreen()
> internally calls m_shellView->requestActivate(). Or is that after
> connecting and disconnecting a monitor? Need more info...

Used lp:~unity-team/unity8/req-445 and lp:~unity-team/qtmir/req-445 and still can't reproduce it on my laptop.

Michał Sawicz (saviq) wrote :

> > On 20/10/2015 14:02, Gerry Boland wrote:
> > > Found a bug on desktop - for single monitor case, keyboard events not
> being
> > delivered to any QWindow. You need to call
> > > m_shellView->requestActivate();
> > > for it.
> >
> > Can't reproduce it on my laptop. m_shellView->showFullScreen()
> > internally calls m_shellView->requestActivate(). Or is that after
> > connecting and disconnecting a monitor? Need more info...
>
> Used lp:~unity-team/unity8/req-445 and lp:~unity-team/qtmir/req-445 and still
> can't reproduce it on my laptop.

Me either, silo 22 works quite fine here.

Gerry Boland (gerboland) wrote :

Yep, I must have had old package or something. All good for me now.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'debian/unity8.install'
2--- debian/unity8.install 2015-10-01 13:10:32 +0000
3+++ debian/unity8.install 2015-10-20 16:45:46 +0000
4@@ -12,6 +12,7 @@
5 usr/share/unity8/Rotation
6 usr/share/unity8/DeviceConfiguration.qml
7 usr/share/unity8/OrientedShell.qml
8+usr/share/unity8/DisabledScreenNotice.qml
9 usr/share/unity8/Shell.qml
10 usr/share/unity8/Stages
11 usr/share/unity8/Tutorial
12
13=== added file 'qml/Components/WallpaperResolver.qml'
14--- qml/Components/WallpaperResolver.qml 1970-01-01 00:00:00 +0000
15+++ qml/Components/WallpaperResolver.qml 2015-10-20 16:45:46 +0000
16@@ -0,0 +1,63 @@
17+/*
18+ * Copyright (C) 2015 Canonical, Ltd.
19+ *
20+ * This program is free software; you can redistribute it and/or modify
21+ * it under the terms of the GNU General Public License as published by
22+ * the Free Software Foundation; version 3.
23+ *
24+ * This program is distributed in the hope that it will be useful,
25+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
26+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27+ * GNU General Public License for more details.
28+ *
29+ * You should have received a copy of the GNU General Public License
30+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
31+ */
32+
33+import QtQuick 2.4
34+import AccountsService 0.1
35+import GSettings 1.0
36+import Ubuntu.Components 1.3
37+
38+/*
39+ Defines the background URL based on several factors, such as:
40+ - default, fallback, background
41+ - Background set in AccountSettings, if any
42+ - Background set in GSettings, if any
43+ */
44+QtObject {
45+ // Users should set their UI width here.
46+ property real width
47+
48+ property url defaultBackground: Qt.resolvedUrl(width >= units.gu(60) ? "../graphics/tablet_background.jpg"
49+ : "../graphics/phone_background.jpg")
50+
51+ // That's the property users of this component are going to consume.
52+ readonly property url background: asImageTester.status == Image.Ready ? asImageTester.source
53+ : gsImageTester.status == Image.Ready ? gsImageTester.source : defaultBackground
54+
55+ // This is a dummy image to detect if the custom AS set wallpaper loads successfully.
56+ property var _asImageTester: Image {
57+ id: asImageTester
58+ source: AccountsService.backgroundFile != undefined && AccountsService.backgroundFile.length > 0 ? AccountsService.backgroundFile : ""
59+ height: 0
60+ width: 0
61+ sourceSize.height: 0
62+ sourceSize.width: 0
63+ }
64+
65+ // This is a dummy image to detect if the custom GSettings set wallpaper loads successfully.
66+ property var _gsImageTester: Image {
67+ id: gsImageTester
68+ source: backgroundSettings.pictureUri && backgroundSettings.pictureUri.length > 0 ? backgroundSettings.pictureUri : ""
69+ height: 0
70+ width: 0
71+ sourceSize.height: 0
72+ sourceSize.width: 0
73+ }
74+
75+ property var _gsettings: GSettings {
76+ id: backgroundSettings
77+ schema.id: "org.gnome.desktop.background"
78+ }
79+}
80
81=== modified file 'qml/DeviceConfiguration.qml'
82--- qml/DeviceConfiguration.qml 2015-07-01 17:52:34 +0000
83+++ qml/DeviceConfiguration.qml 2015-10-20 16:45:46 +0000
84@@ -86,6 +86,7 @@
85 PropertyChanges {
86 target: root
87 category: "desktop"
88+ supportedOrientations: root.useNativeOrientation
89 }
90 }
91 ]
92
93=== added file 'qml/DisabledScreenNotice.qml'
94--- qml/DisabledScreenNotice.qml 1970-01-01 00:00:00 +0000
95+++ qml/DisabledScreenNotice.qml 2015-10-20 16:45:46 +0000
96@@ -0,0 +1,49 @@
97+/*
98+ * Copyright (C) 2015 Canonical, Ltd.
99+ *
100+ * This program is free software; you can redistribute it and/or modify
101+ * it under the terms of the GNU General Public License as published by
102+ * the Free Software Foundation; version 3.
103+ *
104+ * This program is distributed in the hope that it will be useful,
105+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
106+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
107+ * GNU General Public License for more details.
108+ *
109+ * You should have received a copy of the GNU General Public License
110+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
111+ */
112+
113+import QtQuick 2.4
114+import Ubuntu.Components 1.3
115+import "Components"
116+
117+Image {
118+ id: root
119+
120+ WallpaperResolver {
121+ width: root.width
122+ id: wallpaperResolver
123+ }
124+
125+ source: wallpaperResolver.background
126+
127+ UbuntuShape {
128+ anchors.fill: text
129+ anchors.margins: -units.gu(2)
130+ backgroundColor: "black"
131+ opacity: 0.4
132+ }
133+
134+ Label {
135+ id: text
136+ anchors.centerIn: parent
137+ width: parent.width / 2
138+ text: i18n.tr("Your device is now connected to an external display.")
139+ color: "white"
140+ horizontalAlignment: Text.AlignHCenter
141+ verticalAlignment: Text.AlignVCenter
142+ fontSize: "x-large"
143+ wrapMode: Text.Wrap
144+ }
145+}
146
147=== modified file 'qml/Notifications/Notification.qml'
148--- qml/Notifications/Notification.qml 2015-10-08 19:30:59 +0000
149+++ qml/Notifications/Notification.qml 2015-10-20 16:45:46 +0000
150@@ -50,6 +50,7 @@
151 readonly property real contentSpacing: units.gu(2)
152 readonly property bool canBeClosed: type === Notification.Ephemeral
153 property bool hasMouse
154+ property url background: ""
155
156 objectName: "background"
157 implicitHeight: type !== Notification.PlaceHolder ? (fullscreen ? maxHeight : outterColumn.height - shapedBack.anchors.topMargin + contentSpacing * 2) : 0
158@@ -401,6 +402,7 @@
159 menuData: model
160 menuIndex: index
161 maxHeight: notification.maxHeight
162+ background: notification.background
163
164 onLoaded: {
165 notification.fullscreen = Qt.binding(function() { return fullscreen; });
166
167=== modified file 'qml/Notifications/NotificationMenuItemFactory.qml'
168--- qml/Notifications/NotificationMenuItemFactory.qml 2014-11-17 13:46:56 +0000
169+++ qml/Notifications/NotificationMenuItemFactory.qml 2015-10-20 16:45:46 +0000
170@@ -30,6 +30,7 @@
171 property int menuIndex : -1
172 property int maxHeight
173 readonly property bool fullscreen: menuData.type === "com.canonical.snapdecision.pinlock"
174+ property url background: ""
175
176 signal accepted()
177
178@@ -149,7 +150,7 @@
179 infoText: notification.summary
180 errorText: errorAction.valid ? errorAction.state : ""
181 retryText: notification.body
182- background: shell.background
183+ background: menuFactory.background
184 darkenBackground: 0.4
185
186 onEntered: {
187
188=== modified file 'qml/Notifications/Notifications.qml'
189--- qml/Notifications/Notifications.qml 2015-09-22 14:23:44 +0000
190+++ qml/Notifications/Notifications.qml 2015-10-20 16:45:46 +0000
191@@ -29,6 +29,7 @@
192 property real margin
193 property bool useModal: snapDecisionProxyModel.count > 0
194 property bool hasMouse
195+ property url background: ""
196
197 UnitySortFilterProxyModel {
198 id: snapDecisionProxyModel
199@@ -60,6 +61,7 @@
200 maxHeight: notificationList.height
201 margins: notificationList.margin
202 hasMouse: notificationList.hasMouse
203+ background: notificationList.background
204
205 // make sure there's no opacity-difference between the several
206 // elements in a notification
207
208=== modified file 'qml/OrientedShell.qml'
209--- qml/OrientedShell.qml 2015-09-22 14:23:44 +0000
210+++ qml/OrientedShell.qml 2015-10-20 16:45:46 +0000
211@@ -76,6 +76,12 @@
212 oskSettings.disableHeight = shell.usageScenario == "desktop"
213 }
214
215+ // we must rotate to a supported orientation regardless of shell's preference
216+ property bool orientationChangesEnabled:
217+ (orientation & supportedOrientations) === 0 ? true
218+ : shell.orientationChangesEnabled
219+
220+
221 Binding {
222 target: oskSettings
223 property: "stayHidden"
224@@ -89,7 +95,10 @@
225 }
226
227 readonly property int supportedOrientations: shell.supportedOrientations
228- & deviceConfiguration.supportedOrientations
229+ & (deviceConfiguration.supportedOrientations == deviceConfiguration.useNativeOrientation
230+ ? nativeOrientation
231+ : deviceConfiguration.supportedOrientations)
232+
233 property int acceptedOrientationAngle: {
234 if (orientation & supportedOrientations) {
235 return Screen.angleBetween(nativeOrientation, orientation);
236
237=== modified file 'qml/Rotation/RotationStates.qml'
238--- qml/Rotation/RotationStates.qml 2015-05-11 14:36:03 +0000
239+++ qml/Rotation/RotationStates.qml 2015-10-20 16:45:46 +0000
240@@ -83,7 +83,7 @@
241 }
242
243 function tryUpdateState() {
244- if (d.transitioning || (!d.startingUp && !root.shell.orientationChangesEnabled)) {
245+ if (d.transitioning || (!d.startingUp && !root.orientedShell.orientationChangesEnabled)) {
246 return;
247 }
248
249@@ -95,7 +95,7 @@
250 }
251
252 property Connections shellConnections: Connections {
253- target: root.shell
254+ target: root.orientedShell
255 onOrientationChangesEnabledChanged: {
256 d.tryUpdateState();
257 }
258
259=== modified file 'qml/Shell.qml'
260--- qml/Shell.qml 2015-10-20 16:45:46 +0000
261+++ qml/Shell.qml 2015-10-20 16:45:46 +0000
262@@ -17,7 +17,6 @@
263 import QtQuick 2.0
264 import QtQuick.Window 2.0
265 import AccountsService 0.1
266-import GSettings 1.0
267 import Unity.Application 0.1
268 import Ubuntu.Components 0.1
269 import Ubuntu.Components.Popups 1.0
270@@ -108,9 +107,11 @@
271 enabled: greeter && !greeter.waiting
272
273 property real edgeSize: units.gu(2)
274- property url defaultBackground: Qt.resolvedUrl(shell.width >= units.gu(60) ? "graphics/tablet_background.jpg" : "graphics/phone_background.jpg")
275- property url background: asImageTester.status == Image.Ready ? asImageTester.source
276- : gsImageTester.status == Image.Ready ? gsImageTester.source : defaultBackground
277+
278+ WallpaperResolver {
279+ id: wallpaperResolver
280+ width: shell.width
281+ }
282
283 readonly property alias greeter: greeterLoader.item
284
285@@ -131,31 +132,6 @@
286 shell.activateApplication(app);
287 }
288
289- // This is a dummy image to detect if the custom AS set wallpaper loads successfully.
290- Image {
291- id: asImageTester
292- source: AccountsService.backgroundFile != undefined && AccountsService.backgroundFile.length > 0 ? AccountsService.backgroundFile : ""
293- height: 0
294- width: 0
295- sourceSize.height: 0
296- sourceSize.width: 0
297- }
298-
299- GSettings {
300- id: backgroundSettings
301- schema.id: "org.gnome.desktop.background"
302- }
303-
304- // This is a dummy image to detect if the custom GSettings set wallpaper loads successfully.
305- Image {
306- id: gsImageTester
307- source: backgroundSettings.pictureUri && backgroundSettings.pictureUri.length > 0 ? backgroundSettings.pictureUri : ""
308- height: 0
309- width: 0
310- sourceSize.height: 0
311- sourceSize.width: 0
312- }
313-
314 Binding {
315 target: LauncherModel
316 property: "applicationManager"
317@@ -323,7 +299,7 @@
318 Binding {
319 target: applicationsDisplayLoader.item
320 property: "background"
321- value: shell.background
322+ value: wallpaperResolver.background
323 }
324 Binding {
325 target: applicationsDisplayLoader.item
326@@ -428,7 +404,7 @@
327 tabletMode: shell.usageScenario != "phone"
328 launcherOffset: launcher.progress
329 forcedUnlock: tutorial.running
330- background: shell.background
331+ background: wallpaperResolver.background
332
333 // avoid overlapping with Launcher's edge drag area
334 // FIXME: Fix TouchRegistry & friends and remove this workaround
335@@ -603,7 +579,7 @@
336 id: wizard
337 objectName: "wizard"
338 anchors.fill: parent
339- background: shell.background
340+ background: wallpaperResolver.background
341
342 function unlockWhenDoneWithWizard() {
343 if (!active) {
344@@ -634,6 +610,7 @@
345 model: NotificationBackend.Model
346 margin: units.gu(1)
347 hasMouse: shell.hasMouse
348+ background: wallpaperResolver.background
349
350 y: topmostIsFullscreen ? 0 : panel.panelHeight
351 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
352
353=== modified file 'src/ApplicationArguments.h'
354--- src/ApplicationArguments.h 2015-09-14 09:11:08 +0000
355+++ src/ApplicationArguments.h 2015-10-20 16:45:46 +0000
356@@ -26,17 +26,25 @@
357 class ApplicationArguments : public QObject
358 {
359 Q_OBJECT
360- Q_PROPERTY(QString deviceName READ deviceName CONSTANT)
361+ Q_PROPERTY(QString deviceName READ deviceName NOTIFY deviceNameChanged)
362 Q_PROPERTY(QString mode READ mode CONSTANT)
363 public:
364 ApplicationArguments(QObject *parent = nullptr);
365
366- void setDeviceName(const QString &deviceName) { m_deviceName = deviceName; }
367+ void setDeviceName(const QString &deviceName) {
368+ if (deviceName != m_deviceName) {
369+ m_deviceName = deviceName;
370+ Q_EMIT deviceNameChanged(m_deviceName);
371+ }
372+ }
373 QString deviceName() const { return m_deviceName; }
374
375 void setMode(const QString &mode) { m_mode = mode; }
376 QString mode() const { return m_mode; }
377
378+Q_SIGNALS:
379+ void deviceNameChanged(const QString&);
380+
381 private:
382 QString m_deviceName;
383 QString m_mode;
384
385=== modified file 'src/CMakeLists.txt'
386--- src/CMakeLists.txt 2015-06-24 11:41:09 +0000
387+++ src/CMakeLists.txt 2015-10-20 16:45:46 +0000
388@@ -21,6 +21,9 @@
389 main.cpp
390 MouseTouchAdaptor.cpp
391 CachingNetworkManagerFactory.cpp
392+ SecondaryWindow.cpp
393+ ShellApplication.cpp
394+ ShellView.cpp
395 UnityCommandLineParser.cpp
396 ${QML_FILES} # This is to make qml and image files appear in the IDE's project tree
397 )
398
399=== added file 'src/SecondaryWindow.cpp'
400--- src/SecondaryWindow.cpp 1970-01-01 00:00:00 +0000
401+++ src/SecondaryWindow.cpp 2015-10-20 16:45:46 +0000
402@@ -0,0 +1,31 @@
403+/*
404+ * Copyright (C) 2015 Canonical, Ltd.
405+ *
406+ * This program is free software; you can redistribute it and/or modify
407+ * it under the terms of the GNU General Public License as published by
408+ * the Free Software Foundation; version 3.
409+ *
410+ * This program is distributed in the hope that it will be useful,
411+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
412+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
413+ * GNU General Public License for more details.
414+ *
415+ * You should have received a copy of the GNU General Public License
416+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
417+ */
418+
419+#include "SecondaryWindow.h"
420+
421+// local
422+#include <paths.h>
423+
424+SecondaryWindow::SecondaryWindow(QQmlEngine *engine)
425+ : QQuickView(engine, nullptr)
426+{
427+ setResizeMode(QQuickView::SizeRootObjectToView);
428+ setColor("black");
429+ setTitle(QStringLiteral("Unity8 Shell - Secondary Screen"));
430+
431+ QUrl source(::qmlDirectory() + "/DisabledScreenNotice.qml");
432+ setSource(source);
433+}
434
435=== added file 'src/SecondaryWindow.h'
436--- src/SecondaryWindow.h 1970-01-01 00:00:00 +0000
437+++ src/SecondaryWindow.h 2015-10-20 16:45:46 +0000
438@@ -0,0 +1,30 @@
439+/*
440+ * Copyright (C) 2015 Canonical, Ltd.
441+ *
442+ * This program is free software; you can redistribute it and/or modify
443+ * it under the terms of the GNU General Public License as published by
444+ * the Free Software Foundation; version 3.
445+ *
446+ * This program is distributed in the hope that it will be useful,
447+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
448+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
449+ * GNU General Public License for more details.
450+ *
451+ * You should have received a copy of the GNU General Public License
452+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
453+ */
454+
455+#ifndef UNITY_SECONDARY_WINDOW_H
456+#define UNITY_SECONDARY_WINDOW_H
457+
458+#include <QQuickView>
459+
460+class SecondaryWindow : public QQuickView
461+{
462+ Q_OBJECT
463+
464+public:
465+ SecondaryWindow(QQmlEngine *engine);
466+};
467+
468+#endif // UNITY_SECONDARY_WINDOW_H
469
470=== added file 'src/ShellApplication.cpp'
471--- src/ShellApplication.cpp 1970-01-01 00:00:00 +0000
472+++ src/ShellApplication.cpp 2015-10-20 16:45:46 +0000
473@@ -0,0 +1,197 @@
474+/*
475+ * Copyright (C) 2015 Canonical, Ltd.
476+ *
477+ * This program is free software; you can redistribute it and/or modify
478+ * it under the terms of the GNU General Public License as published by
479+ * the Free Software Foundation; version 3.
480+ *
481+ * This program is distributed in the hope that it will be useful,
482+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
483+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
484+ * GNU General Public License for more details.
485+ *
486+ * You should have received a copy of the GNU General Public License
487+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
488+ */
489+
490+#include "ShellApplication.h"
491+
492+// Qt
493+#include <QLibrary>
494+#include <QScreen>
495+
496+#include <libintl.h>
497+
498+// libandroid-properties
499+#include <hybris/properties/properties.h>
500+
501+// local
502+#include <paths.h>
503+#include "CachingNetworkManagerFactory.h"
504+#include "MouseTouchAdaptor.h"
505+#include "UnityCommandLineParser.h"
506+
507+ShellApplication::ShellApplication(int & argc, char ** argv, bool isMirServer)
508+ : QGuiApplication(argc, argv)
509+ , m_shellView(nullptr)
510+ , m_secondaryWindow(nullptr)
511+ , m_mouseTouchAdaptor(nullptr)
512+ , m_qmlEngine(nullptr)
513+{
514+
515+ setApplicationName(QStringLiteral("unity8"));
516+
517+ connect(this, &QGuiApplication::screenAdded, this, &ShellApplication::onScreenAdded);
518+
519+ setupQmlEngine(isMirServer);
520+
521+ UnityCommandLineParser parser(*this);
522+
523+ if (!parser.deviceName().isEmpty()) {
524+ m_deviceName = parser.deviceName();
525+ } else {
526+ char buffer[200];
527+ property_get("ro.product.device", buffer /* value */, "desktop" /* default_value*/);
528+ m_deviceName = QString(buffer);
529+ }
530+ m_qmlArgs.setDeviceName(m_deviceName);
531+
532+ m_qmlArgs.setMode(parser.mode());
533+
534+ // The testability driver is only loaded by QApplication but not by QGuiApplication.
535+ // However, QApplication depends on QWidget which would add some unneeded overhead => Let's load the testability driver on our own.
536+ if (parser.hasTestability() || getenv("QT_LOAD_TESTABILITY")) {
537+ QLibrary testLib(QStringLiteral("qttestability"));
538+ if (testLib.load()) {
539+ typedef void (*TasInitialize)(void);
540+ TasInitialize initFunction = (TasInitialize)testLib.resolve("qt_testability_init");
541+ if (initFunction) {
542+ initFunction();
543+ } else {
544+ qCritical("Library qttestability resolve failed!");
545+ }
546+ } else {
547+ qCritical("Library qttestability load failed!");
548+ }
549+ }
550+
551+ bindtextdomain("unity8", translationDirectory().toUtf8().data());
552+ textdomain("unity8");
553+
554+ m_shellView = new ShellView(m_qmlEngine, &m_qmlArgs);
555+
556+ if (parser.windowGeometry().isValid()) {
557+ m_shellView->setWidth(parser.windowGeometry().width());
558+ m_shellView->setHeight(parser.windowGeometry().height());
559+ }
560+
561+ if (parser.hasFrameless()) {
562+ m_shellView->setFlags(Qt::FramelessWindowHint);
563+ }
564+
565+ // You will need this if you want to interact with touch-only components using a mouse
566+ // Needed only when manually testing on a desktop.
567+ if (parser.hasMouseToTouch()) {
568+ m_mouseTouchAdaptor = MouseTouchAdaptor::instance();
569+ }
570+
571+
572+ // Some hard-coded policy for now.
573+ // NB: We don't support more than two screens at the moment
574+ //
575+ // TODO: Support an arbitrary number of screens and different policies
576+ // (eg cloned desktop, several desktops, etc)
577+ if (isMirServer && screens().count() == 2) {
578+ m_shellView->setScreen(screens().at(1));
579+ m_qmlArgs.setDeviceName("desktop");
580+
581+ m_secondaryWindow = new SecondaryWindow(m_qmlEngine);
582+ m_secondaryWindow->setScreen(screens().at(0));
583+ // QWindow::showFullScreen() also calls QWindow::requestActivate() and we don't want that!
584+ m_secondaryWindow->setWindowState(Qt::WindowFullScreen);
585+ m_secondaryWindow->setVisible(true);
586+ }
587+
588+ if (isMirServer || parser.hasFullscreen()) {
589+ m_shellView->showFullScreen();
590+ } else {
591+ m_shellView->show();
592+ }
593+}
594+
595+ShellApplication::~ShellApplication()
596+{
597+ destroyResources();
598+}
599+
600+void ShellApplication::destroyResources()
601+{
602+ // Deletion order is important. Don't use QScopedPointers and the like
603+ // Otherwise the process will hang on shutdown (bug somewhere I guess).
604+ delete m_shellView;
605+ m_shellView = nullptr;
606+
607+ delete m_secondaryWindow;
608+ m_secondaryWindow = nullptr;
609+
610+ delete m_mouseTouchAdaptor;
611+ m_mouseTouchAdaptor = nullptr;
612+
613+ delete m_qmlEngine;
614+ m_qmlEngine = nullptr;
615+}
616+
617+void ShellApplication::setupQmlEngine(bool isMirServer)
618+{
619+ m_qmlEngine = new QQmlEngine(this);
620+
621+ m_qmlEngine->setBaseUrl(QUrl::fromLocalFile(::qmlDirectory()));
622+
623+ prependImportPaths(m_qmlEngine, ::overrideImportPaths());
624+ if (!isMirServer) {
625+ prependImportPaths(m_qmlEngine, ::nonMirImportPaths());
626+ }
627+ appendImportPaths(m_qmlEngine, ::fallbackImportPaths());
628+
629+ m_qmlEngine->setNetworkAccessManagerFactory(new CachingNetworkManagerFactory);
630+
631+ QObject::connect(m_qmlEngine, &QQmlEngine::quit, this, &QGuiApplication::quit);
632+}
633+
634+void ShellApplication::onScreenAdded(QScreen * /*screen*/)
635+{
636+ // TODO: Support an arbitrary number of screens and different policies
637+ // (eg cloned desktop, several desktops, etc)
638+ if (screens().count() == 2) {
639+ m_shellView->setScreen(screens().at(1));
640+ m_qmlArgs.setDeviceName("desktop");
641+ // Changing the QScreen where a QWindow is drawn makes it also lose focus (besides having
642+ // its backing QPlatformWindow recreated). So lets refocus it.
643+ m_shellView->requestActivate();
644+
645+ m_secondaryWindow = new SecondaryWindow(m_qmlEngine);
646+ m_secondaryWindow->setScreen(screens().at(0));
647+
648+ // QWindow::showFullScreen() also calls QWindow::requestActivate() and we don't want that!
649+ m_secondaryWindow->setWindowState(Qt::WindowFullScreen);
650+ m_secondaryWindow->setVisible(true);
651+ }
652+}
653+
654+void ShellApplication::onScreenAboutToBeRemoved(QScreen *screen)
655+{
656+ // TODO: Support an arbitrary number of screens and different policies
657+ // (eg cloned desktop, several desktops, etc)
658+ if (screen == m_shellView->screen()) {
659+ Q_ASSERT(screens().count() > 1);
660+ Q_ASSERT(screens().at(0) != screen);
661+ Q_ASSERT(m_secondaryWindow);
662+ delete m_secondaryWindow;
663+ m_secondaryWindow = nullptr;
664+ m_shellView->setScreen(screens().first());
665+ m_qmlArgs.setDeviceName(m_deviceName);
666+ // Changing the QScreen where a QWindow is drawn makes it also lose focus (besides having
667+ // its backing QPlatformWindow recreated). So lets refocus it.
668+ m_shellView->requestActivate();
669+ }
670+}
671
672=== added file 'src/ShellApplication.h'
673--- src/ShellApplication.h 1970-01-01 00:00:00 +0000
674+++ src/ShellApplication.h 2015-10-20 16:45:46 +0000
675@@ -0,0 +1,55 @@
676+/*
677+ * Copyright (C) 2015 Canonical, Ltd.
678+ *
679+ * This program is free software; you can redistribute it and/or modify
680+ * it under the terms of the GNU General Public License as published by
681+ * the Free Software Foundation; version 3.
682+ *
683+ * This program is distributed in the hope that it will be useful,
684+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
685+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
686+ * GNU General Public License for more details.
687+ *
688+ * You should have received a copy of the GNU General Public License
689+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
690+ */
691+
692+#ifndef SHELLAPPLICATION_H
693+#define SHELLAPPLICATION_H
694+
695+#include <QGuiApplication>
696+#include <QQmlEngine>
697+#include <QQuickView>
698+#include <QScopedPointer>
699+
700+#include "ApplicationArguments.h"
701+#include "MouseTouchAdaptor.h"
702+#include "SecondaryWindow.h"
703+#include "ShellView.h"
704+
705+class ShellApplication : public QGuiApplication
706+{
707+ Q_OBJECT
708+public:
709+ ShellApplication(int & argc, char ** argv, bool isMirServer);
710+ virtual ~ShellApplication();
711+
712+ void destroyResources();
713+public Q_SLOTS:
714+ // called by qtmir
715+ void onScreenAboutToBeRemoved(QScreen *screen);
716+
717+private Q_SLOTS:
718+ void onScreenAdded(QScreen*);
719+
720+private:
721+ void setupQmlEngine(bool isMirServer);
722+ QString m_deviceName;
723+ ApplicationArguments m_qmlArgs;
724+ ShellView *m_shellView;
725+ SecondaryWindow *m_secondaryWindow;
726+ MouseTouchAdaptor *m_mouseTouchAdaptor;
727+ QQmlEngine *m_qmlEngine;
728+};
729+
730+#endif // SHELLAPPLICATION_H
731
732=== added file 'src/ShellView.cpp'
733--- src/ShellView.cpp 1970-01-01 00:00:00 +0000
734+++ src/ShellView.cpp 2015-10-20 16:45:46 +0000
735@@ -0,0 +1,59 @@
736+/*
737+ * Copyright (C) 2015 Canonical, Ltd.
738+ *
739+ * This program is free software; you can redistribute it and/or modify
740+ * it under the terms of the GNU General Public License as published by
741+ * the Free Software Foundation; version 3.
742+ *
743+ * This program is distributed in the hope that it will be useful,
744+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
745+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
746+ * GNU General Public License for more details.
747+ *
748+ * You should have received a copy of the GNU General Public License
749+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
750+ */
751+
752+#include "ShellView.h"
753+
754+// Qt
755+#include <QQmlContext>
756+#include <QQuickItem>
757+
758+// local
759+#include <paths.h>
760+
761+ShellView::ShellView(QQmlEngine *engine, QObject *qmlArgs)
762+ : QQuickView(engine, nullptr)
763+{
764+ setResizeMode(QQuickView::SizeRootObjectToView);
765+ setColor("black");
766+ setTitle(QStringLiteral("Unity8"));
767+
768+ rootContext()->setContextProperty(QStringLiteral("applicationArguments"), qmlArgs);
769+
770+ QUrl source(::qmlDirectory() + "/OrientedShell.qml");
771+ setSource(source);
772+
773+ connect(this, &QWindow::widthChanged, this, &ShellView::onWidthChanged);
774+ connect(this, &QWindow::heightChanged, this, &ShellView::onHeightChanged);
775+}
776+
777+void ShellView::onWidthChanged(int w)
778+{
779+ // For good measure in case SizeRootObjectToView doesn't fulfill its promise.
780+ //
781+ // There's at least one situation that's know to leave the root object with an outdated size.
782+ // (really looks like Qt bug)
783+ // Happens when starting unity8 with an external monitor already connected.
784+ // The QResizeEvent we get still has the size of the first screen and since the resize move is triggered
785+ // from the resize event handler, the root item doesn't get resized.
786+ // TODO: Confirm the Qt bug and submit a patch upstream
787+ rootObject()->setWidth(w);
788+}
789+
790+void ShellView::onHeightChanged(int h)
791+{
792+ // See comment in ShellView::onWidthChanged()
793+ rootObject()->setHeight(h);
794+}
795
796=== added file 'src/ShellView.h'
797--- src/ShellView.h 1970-01-01 00:00:00 +0000
798+++ src/ShellView.h 2015-10-20 16:45:46 +0000
799@@ -0,0 +1,34 @@
800+/*
801+ * Copyright (C) 2015 Canonical, Ltd.
802+ *
803+ * This program is free software; you can redistribute it and/or modify
804+ * it under the terms of the GNU General Public License as published by
805+ * the Free Software Foundation; version 3.
806+ *
807+ * This program is distributed in the hope that it will be useful,
808+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
809+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
810+ * GNU General Public License for more details.
811+ *
812+ * You should have received a copy of the GNU General Public License
813+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
814+ */
815+
816+#ifndef UNITY_SHELL_VIEW_H
817+#define UNITY_SHELL_VIEW_H
818+
819+#include <QQuickView>
820+
821+class ShellView : public QQuickView
822+{
823+ Q_OBJECT
824+
825+public:
826+ ShellView(QQmlEngine *engine, QObject *qmlArgs);
827+
828+private Q_SLOTS:
829+ void onWidthChanged(int);
830+ void onHeightChanged(int);
831+};
832+
833+#endif // UNITY_SHELL_VIEW_H
834
835=== modified file 'src/main.cpp'
836--- src/main.cpp 2015-09-23 15:14:01 +0000
837+++ src/main.cpp 2015-10-20 16:45:46 +0000
838@@ -14,26 +14,8 @@
839 * along with this program. If not, see <http://www.gnu.org/licenses/>.
840 */
841
842-// Qt
843-#include <QCommandLineParser>
844-#include <QtQuick/QQuickView>
845-#include <QtGui/QGuiApplication>
846-#include <QtQml/QQmlEngine>
847-#include <QtQml/QQmlContext>
848-#include <QLibrary>
849-#include <QDebug>
850-#include <csignal>
851-#include <libintl.h>
852-
853-// libandroid-properties
854-#include <hybris/properties/properties.h>
855-
856 // local
857-#include <paths.h>
858-#include "MouseTouchAdaptor.h"
859-#include "ApplicationArguments.h"
860-#include "CachingNetworkManagerFactory.h"
861-#include "UnityCommandLineParser.h"
862+#include "ShellApplication.h"
863
864 int main(int argc, const char *argv[])
865 {
866@@ -43,91 +25,12 @@
867 isMirServer = true;
868 }
869
870- QGuiApplication::setApplicationName(QStringLiteral("unity8"));
871- QGuiApplication *application;
872-
873- application = new QGuiApplication(argc, (char**)argv);
874-
875- UnityCommandLineParser parser(*application);
876-
877- ApplicationArguments qmlArgs;
878-
879- if (!parser.deviceName().isEmpty()) {
880- qmlArgs.setDeviceName(parser.deviceName());
881- } else {
882- char buffer[200];
883- property_get("ro.product.device", buffer /* value */, "desktop" /* default_value*/);
884- qmlArgs.setDeviceName(QString(buffer));
885- }
886-
887- qmlArgs.setMode(parser.mode());
888-
889- // The testability driver is only loaded by QApplication but not by QGuiApplication.
890- // However, QApplication depends on QWidget which would add some unneeded overhead => Let's load the testability driver on our own.
891- if (parser.hasTestability() || getenv("QT_LOAD_TESTABILITY")) {
892- QLibrary testLib(QStringLiteral("qttestability"));
893- if (testLib.load()) {
894- typedef void (*TasInitialize)(void);
895- TasInitialize initFunction = (TasInitialize)testLib.resolve("qt_testability_init");
896- if (initFunction) {
897- initFunction();
898- } else {
899- qCritical("Library qttestability resolve failed!");
900- }
901- } else {
902- qCritical("Library qttestability load failed!");
903- }
904- }
905-
906- bindtextdomain("unity8", translationDirectory().toUtf8().data());
907- textdomain("unity8");
908-
909- QQuickView* view = new QQuickView();
910- view->setResizeMode(QQuickView::SizeRootObjectToView);
911- view->setColor("black");
912- view->setTitle(QStringLiteral("Unity8 Shell"));
913-
914- if (parser.windowGeometry().isValid()) {
915- view->setWidth(parser.windowGeometry().width());
916- view->setHeight(parser.windowGeometry().height());
917- }
918-
919- view->engine()->setBaseUrl(QUrl::fromLocalFile(::qmlDirectory()));
920- view->rootContext()->setContextProperty(QStringLiteral("applicationArguments"), &qmlArgs);
921- if (parser.hasFrameless()) {
922- view->setFlags(Qt::FramelessWindowHint);
923- }
924-
925- // You will need this if you want to interact with touch-only components using a mouse
926- // Needed only when manually testing on a desktop.
927- MouseTouchAdaptor *mouseTouchAdaptor = 0;
928- if (parser.hasMouseToTouch()) {
929- mouseTouchAdaptor = MouseTouchAdaptor::instance();
930- }
931-
932- QUrl source(::qmlDirectory() + "/OrientedShell.qml");
933- prependImportPaths(view->engine(), ::overrideImportPaths());
934- if (!isMirServer) {
935- prependImportPaths(view->engine(), ::nonMirImportPaths());
936- }
937- appendImportPaths(view->engine(), ::fallbackImportPaths());
938-
939- CachingNetworkManagerFactory *managerFactory = new CachingNetworkManagerFactory();
940- view->engine()->setNetworkAccessManagerFactory(managerFactory);
941-
942- view->setSource(source);
943- QObject::connect(view->engine(), &QQmlEngine::quit, application, &QGuiApplication::quit);
944-
945- if (isMirServer || parser.hasFullscreen()) {
946- view->showFullScreen();
947- } else {
948- view->show();
949- }
950+ ShellApplication *application = new ShellApplication(argc, (char**)argv, isMirServer);
951
952 int result = application->exec();
953
954- delete view;
955- delete mouseTouchAdaptor;
956+ application->destroyResources();
957+
958 delete application;
959
960 return result;
961
962=== modified file 'tests/autopilot/unity8/fixture_setup.py'
963--- tests/autopilot/unity8/fixture_setup.py 2015-09-21 12:50:10 +0000
964+++ tests/autopilot/unity8/fixture_setup.py 2015-10-20 16:45:46 +0000
965@@ -133,7 +133,7 @@
966 args = [binary_arg] + env_args
967 self.unity_proxy = process_helpers.restart_unity_with_testability(
968 *args)
969- self.main_win = self.unity_proxy.select_single(shell.QQuickView)
970+ self.main_win = self.unity_proxy.select_single(shell.ShellView)
971
972 def _create_sensors(self):
973 # Wait for unity to start running.
974
975=== modified file 'tests/autopilot/unity8/greeter/tests/__init__.py'
976--- tests/autopilot/unity8/greeter/tests/__init__.py 2015-04-29 19:21:18 +0000
977+++ tests/autopilot/unity8/greeter/tests/__init__.py 2015-10-20 16:45:46 +0000
978@@ -24,5 +24,5 @@
979 class GreeterTestCase(UnityTestCase):
980 def get_shell(self, unity_proxy):
981 main_window = (
982- unity_proxy.select_single(shell.QQuickView))
983+ unity_proxy.select_single(shell.ShellView))
984 return main_window.select_single('Shell')
985
986=== modified file 'tests/autopilot/unity8/launcher.py'
987--- tests/autopilot/unity8/launcher.py 2015-04-29 19:21:18 +0000
988+++ tests/autopilot/unity8/launcher.py 2015-10-20 16:45:46 +0000
989@@ -42,7 +42,7 @@
990 logger.debug('The launcher is already opened.')
991
992 def _swipe_to_show_launcher(self):
993- view = self.get_root_instance().select_single('QQuickView')
994+ view = self.get_root_instance().select_single('ShellView')
995 start_y = stop_y = view.y + view.height // 2
996
997 start_x = view.x + 1
998
999=== modified file 'tests/autopilot/unity8/shell/__init__.py'
1000--- tests/autopilot/unity8/shell/__init__.py 2015-06-22 15:47:34 +0000
1001+++ tests/autopilot/unity8/shell/__init__.py 2015-10-20 16:45:46 +0000
1002@@ -93,7 +93,7 @@
1003 return _urgency_enums.get(urgency.upper())
1004
1005
1006-class QQuickView(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
1007+class ShellView(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
1008 """An helper class that makes it easy to interact with the shell"""
1009
1010 def get_greeter(self):
1011
1012=== modified file 'tests/autopilot/unity8/shell/tests/__init__.py'
1013--- tests/autopilot/unity8/shell/tests/__init__.py 2015-09-21 12:50:10 +0000
1014+++ tests/autopilot/unity8/shell/tests/__init__.py 2015-10-20 16:45:46 +0000
1015@@ -297,7 +297,7 @@
1016
1017 @property
1018 def main_window(self):
1019- return self._proxy.select_single(shell.QQuickView)
1020+ return self._proxy.select_single(shell.ShellView)
1021
1022
1023 class DashBaseTestCase(AutopilotTestCase):
1024
1025=== modified file 'tests/qmltests/CMakeLists.txt'
1026--- tests/qmltests/CMakeLists.txt 2015-10-20 16:45:46 +0000
1027+++ tests/qmltests/CMakeLists.txt 2015-10-20 16:45:46 +0000
1028@@ -2,6 +2,7 @@
1029 add_subdirectory(Components)
1030
1031 add_unity8_qmltest(. OrientedShell LIGHTDM)
1032+add_unity8_qmltest(. DisabledScreenNotice)
1033 add_unity8_qmltest(. Shell LIGHTDM)
1034 add_unity8_qmltest(. ShellWithPin LIGHTDM)
1035 add_unity8_qmltest(Components Background)
1036@@ -15,6 +16,7 @@
1037 add_unity8_qmltest(Components ResponsiveGridView)
1038 add_unity8_qmltest(Components ResponsiveVerticalJournal)
1039 add_unity8_qmltest(Components Showable)
1040+add_unity8_qmltest(Components WallpaperResolver)
1041 add_unity8_qmltest(Components ZoomableImage)
1042 add_unity8_qmltest(Dash Dash)
1043 add_unity8_qmltest(Dash DashContent)
1044
1045=== added file 'tests/qmltests/Components/tst_WallpaperResolver.qml'
1046--- tests/qmltests/Components/tst_WallpaperResolver.qml 1970-01-01 00:00:00 +0000
1047+++ tests/qmltests/Components/tst_WallpaperResolver.qml 2015-10-20 16:45:46 +0000
1048@@ -0,0 +1,89 @@
1049+/*
1050+ * Copyright (C) 2015 Canonical, Ltd.
1051+ *
1052+ * This program is free software; you can redistribute it and/or modify
1053+ * it under the terms of the GNU General Public License as published by
1054+ * the Free Software Foundation; version 3.
1055+ *
1056+ * This program is distributed in the hope that it will be useful,
1057+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1058+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1059+ * GNU General Public License for more details.
1060+ *
1061+ * You should have received a copy of the GNU General Public License
1062+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1063+ */
1064+
1065+import QtQuick 2.4
1066+import QtTest 1.0
1067+import Ubuntu.Components 1.3
1068+import Unity.Test 0.1
1069+import AccountsService 0.1
1070+import GSettings 1.0
1071+
1072+import "../../../qml/Components"
1073+
1074+
1075+Image {
1076+ width: units.gu(70)
1077+ height: units.gu(70)
1078+
1079+ source: wallpaperResolver.background
1080+
1081+ WallpaperResolver {
1082+ id: wallpaperResolver
1083+ width: units.gu(70)
1084+ }
1085+
1086+ UnityTestCase {
1087+ id: testCase
1088+ name: "WallpaperResolver"
1089+ when: windowShown
1090+
1091+ function test_background_data() {
1092+ return [
1093+ {tag: "color",
1094+ accounts: Qt.resolvedUrl("data:image/svg+xml,<svg><rect width='100%' height='100%' fill='#dd4814'/></svg>"),
1095+ gsettings: "",
1096+ output: Qt.resolvedUrl("data:image/svg+xml,<svg><rect width='100%' height='100%' fill='#dd4814'/></svg>")},
1097+
1098+ {tag: "empty", accounts: "", gsettings: "", output: "defaultBackground"},
1099+
1100+ {tag: "as-specified",
1101+ accounts: Qt.resolvedUrl("../../data/unity/backgrounds/blue.png"),
1102+ gsettings: "",
1103+ output: Qt.resolvedUrl("../../data/unity/backgrounds/blue.png")},
1104+
1105+ {tag: "gs-specified",
1106+ accounts: "",
1107+ gsettings: Qt.resolvedUrl("../../data/unity/backgrounds/red.png"),
1108+ output: Qt.resolvedUrl("../../data/unity/backgrounds/red.png")},
1109+
1110+ {tag: "both-specified",
1111+ accounts: Qt.resolvedUrl("../../data/unity/backgrounds/blue.png"),
1112+ gsettings: Qt.resolvedUrl("../../data/unity/backgrounds/red.png"),
1113+ output: Qt.resolvedUrl("../../data/unity/backgrounds/blue.png")},
1114+
1115+ {tag: "invalid-as",
1116+ accounts: Qt.resolvedUrl("../../data/unity/backgrounds/nope.png"),
1117+ gsettings: Qt.resolvedUrl("../../data/unity/backgrounds/red.png"),
1118+ output: Qt.resolvedUrl("../../data/unity/backgrounds/red.png")},
1119+
1120+ {tag: "invalid-both",
1121+ accounts: Qt.resolvedUrl("../../data/unity/backgrounds/nope.png"),
1122+ gsettings: Qt.resolvedUrl("../../data/unity/backgrounds/stillnope.png"),
1123+ output: "defaultBackground"},
1124+ ]
1125+ }
1126+ function test_background(data) {
1127+ AccountsService.backgroundFile = data.accounts;
1128+ GSettingsController.setPictureUri(data.gsettings);
1129+
1130+ if (data.output === "defaultBackground") {
1131+ tryCompare(wallpaperResolver, "background", wallpaperResolver.defaultBackground);
1132+ } else {
1133+ tryCompare(wallpaperResolver, "background", data.output);
1134+ }
1135+ }
1136+ }
1137+}
1138
1139=== added file 'tests/qmltests/tst_DisabledScreenNotice.qml'
1140--- tests/qmltests/tst_DisabledScreenNotice.qml 1970-01-01 00:00:00 +0000
1141+++ tests/qmltests/tst_DisabledScreenNotice.qml 2015-10-20 16:45:46 +0000
1142@@ -0,0 +1,37 @@
1143+/*
1144+ * Copyright (C) 2015 Canonical, Ltd.
1145+ *
1146+ * This program is free software; you can redistribute it and/or modify
1147+ * it under the terms of the GNU General Public License as published by
1148+ * the Free Software Foundation; version 3.
1149+ *
1150+ * This program is distributed in the hope that it will be useful,
1151+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1152+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1153+ * GNU General Public License for more details.
1154+ *
1155+ * You should have received a copy of the GNU General Public License
1156+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1157+ */
1158+
1159+import QtQuick 2.4
1160+import QtTest 1.0
1161+import Unity.Test 0.1
1162+import "../../qml"
1163+
1164+
1165+Item {
1166+ id: root
1167+ width: units.gu(70)
1168+ height: units.gu(70)
1169+
1170+ DisabledScreenNotice {
1171+ anchors.fill: parent
1172+ }
1173+
1174+ UnityTestCase {
1175+ id: testCase
1176+ name: "DisabledScreenNotice"
1177+ when: windowShown
1178+ }
1179+}
1180
1181=== modified file 'tests/qmltests/tst_OrientedShell.qml'
1182--- tests/qmltests/tst_OrientedShell.qml 2015-10-20 16:45:46 +0000
1183+++ tests/qmltests/tst_OrientedShell.qml 2015-10-20 16:45:46 +0000
1184@@ -110,6 +110,22 @@
1185 physicalOrientation180: Qt.LandscapeOrientation
1186 primaryOrientationAngle: 90
1187 }
1188+ },
1189+ State {
1190+ name: "desktop"
1191+ PropertyChanges {
1192+ target: orientedShellLoader
1193+ width: units.gu(100)
1194+ height: units.gu(56)
1195+ }
1196+ PropertyChanges {
1197+ target: root
1198+ physicalOrientation270: Qt.InvertedPortraitOrientation
1199+ physicalOrientation0: Qt.LandscapeOrientation
1200+ physicalOrientation90: Qt.PortraitOrientation
1201+ physicalOrientation180: Qt.InvertedLandscapeOrientation
1202+ primaryOrientationAngle: 0
1203+ }
1204 }
1205 ]
1206
1207@@ -250,7 +266,7 @@
1208 anchors { left: parent.left; right: parent.right }
1209 activeFocusOnPress: false
1210 text: "Device Name"
1211- model: ["mako", "manta", "flo"]
1212+ model: ["mako", "manta", "flo", "desktop"]
1213 onSelectedIndexChanged: {
1214 testCase.tearDown();
1215 applicationArguments.deviceName = model[selectedIndex];
1216@@ -310,12 +326,14 @@
1217 Row {
1218 Button {
1219 text: "Add mouse"
1220+ activeFocusOnPress: false
1221 onClicked: {
1222 UnityInputInfo.inputInfo.addMockMouse()
1223 }
1224 }
1225 Button {
1226 text: "Remove mouse"
1227+ activeFocusOnPress: false
1228 onClicked: {
1229 UnityInputInfo.inputInfo.removeMockMouse()
1230 }
1231@@ -324,17 +342,37 @@
1232 Row {
1233 Button {
1234 text: "Add kbd"
1235+ activeFocusOnPress: false
1236 onClicked: {
1237 UnityInputInfo.inputInfo.addMockKeyboard()
1238 }
1239 }
1240 Button {
1241+ activeFocusOnPress: false
1242 text: "Remove kbd"
1243 onClicked: {
1244 UnityInputInfo.inputInfo.removeMockKeyboard()
1245 }
1246 }
1247 }
1248+
1249+ // Simulates what happens when the shell is moved to an external monitor and back
1250+ Button {
1251+ id: moveToFromMonitorButton
1252+ text: applicationArguments.deviceName === "desktop" ? "Move to " + prevDevName + " screen" : "Move to desktop screen"
1253+ activeFocusOnPress: false
1254+ property string prevDevName: "mako"
1255+ onClicked: {
1256+ usageModeSelector.selectedIndex = 2; // "Automatic"
1257+
1258+ if (applicationArguments.deviceName === "desktop") {
1259+ applicationArguments.deviceName = prevDevName;
1260+ } else {
1261+ prevDevName = applicationArguments.deviceName;
1262+ applicationArguments.deviceName = "desktop"
1263+ }
1264+ }
1265+ }
1266 }
1267 }
1268
1269@@ -1054,6 +1092,18 @@
1270 compare(signalSpy.count, 0);
1271 }
1272
1273+ function test_moveToExternalMonitor() {
1274+ loadShell("flo");
1275+
1276+ compare(orientedShell.orientation, Qt.InvertedLandscapeOrientation);
1277+ compare(shell.transformRotationAngle, 90);
1278+
1279+ moveToFromMonitorButton.clicked();
1280+
1281+ tryCompare(orientedShell, "orientation", Qt.LandscapeOrientation);
1282+ tryCompare(shell, "transformRotationAngle" , 0);
1283+ }
1284+
1285 // angle - rotation angle in degrees clockwise, relative to the primary orientation.
1286 function rotateTo(angle) {
1287 switch (angle) {
1288
1289=== modified file 'tests/qmltests/tst_Shell.qml'
1290--- tests/qmltests/tst_Shell.qml 2015-09-18 12:05:32 +0000
1291+++ tests/qmltests/tst_Shell.qml 2015-10-20 16:45:46 +0000
1292@@ -1240,30 +1240,6 @@
1293 tryCompare(launcherPanel, "x", -launcherPanel.width);
1294 }
1295
1296- function test_background_data() {
1297- return [
1298- {tag: "color", accounts: Qt.resolvedUrl("data:image/svg+xml,<svg><rect width='100%' height='100%' fill='#dd4814'/></svg>"), gsettings: "", output: Qt.resolvedUrl("data:image/svg+xml,<svg><rect width='100%' height='100%' fill='#dd4814'/></svg>")},
1299- {tag: "empty", accounts: "", gsettings: "", output: "defaultBackground"},
1300- {tag: "as-specified", accounts: Qt.resolvedUrl("../data/unity/backgrounds/blue.png"), gsettings: "", output: Qt.resolvedUrl("../data/unity/backgrounds/blue.png")},
1301- {tag: "gs-specified", accounts: "", gsettings: Qt.resolvedUrl("../data/unity/backgrounds/red.png"), output: Qt.resolvedUrl("../data/unity/backgrounds/red.png")},
1302- {tag: "both-specified", accounts: Qt.resolvedUrl("../data/unity/backgrounds/blue.png"), gsettings: Qt.resolvedUrl("../data/unity/backgrounds/red.png"), output: Qt.resolvedUrl("../data/unity/backgrounds/blue.png")},
1303- {tag: "invalid-as", accounts: Qt.resolvedUrl("../data/unity/backgrounds/nope.png"), gsettings: Qt.resolvedUrl("../data/unity/backgrounds/red.png"), output: Qt.resolvedUrl("../data/unity/backgrounds/red.png")},
1304- {tag: "invalid-both", accounts: Qt.resolvedUrl("../data/unity/backgrounds/nope.png"), gsettings: Qt.resolvedUrl("../data/unity/backgrounds/stillnope.png"), output: "defaultBackground"},
1305- ]
1306- }
1307- function test_background(data) {
1308- loadShell("phone");
1309- swipeAwayGreeter();
1310- AccountsService.backgroundFile = data.accounts;
1311- GSettingsController.setPictureUri(data.gsettings);
1312-
1313- if (data.output === "defaultBackground") {
1314- tryCompare(shell, "background", shell.defaultBackground);
1315- } else {
1316- tryCompare(shell, "background", data.output);
1317- }
1318- }
1319-
1320 function test_tabletLogin_data() {
1321 return [
1322 {tag: "auth error", user: "auth-error", loggedIn: false, password: ""},

Subscribers

People subscribed via source and target branches