Merge lp:~nick-dedekind/unity8/desktop-app-focus into lp:unity8

Proposed by Nick Dedekind on 2015-04-15
Status: Merged
Approved by: Daniel d'Andrada on 2015-05-06
Approved revision: 1657
Merged at revision: 1778
Proposed branch: lp:~nick-dedekind/unity8/desktop-app-focus
Merge into: lp:unity8
Diff against target: 1407 lines (+864/-180)
24 files modified
qml/Shell.qml (+0/-1)
qml/Stages/DecoratedWindow.qml (+1/-0)
qml/Stages/DesktopStage.qml (+20/-14)
qml/Stages/PhoneStage.qml (+4/-4)
qml/Stages/TabletStage.qml (+10/-11)
tests/mocks/CMakeLists.txt (+1/-0)
tests/mocks/GSettings.1.0/fake_gsettings.h (+2/-2)
tests/mocks/GSettings.1.0/plugin.cpp (+6/-0)
tests/mocks/Unity/Application/ApplicationManager.cpp (+1/-1)
tests/mocks/Utils/CMakeLists.txt (+39/-0)
tests/mocks/Utils/HomeKeyWatcher.qml (+21/-0)
tests/mocks/Utils/Style.js (+37/-0)
tests/mocks/Utils/Utils.qmltypes (+175/-0)
tests/mocks/Utils/constants.cpp (+23/-0)
tests/mocks/Utils/constants.h (+43/-0)
tests/mocks/Utils/plugin.cpp (+73/-0)
tests/mocks/Utils/plugin.h (+33/-0)
tests/mocks/Utils/qmldir (+5/-0)
tests/mocks/Utils/windowstatestorage.cpp (+48/-0)
tests/mocks/Utils/windowstatestorage.h (+38/-0)
tests/qmltests/Stages/ApplicationCheckBox.qml (+49/-20)
tests/qmltests/Stages/tst_DesktopStage.qml (+90/-80)
tests/qmltests/tst_Shell.qml (+144/-47)
tests/utils/modules/Unity/Test/MouseTouchEmulationCheckbox.qml (+1/-0)
To merge this branch: bzr merge lp:~nick-dedekind/unity8/desktop-app-focus
Reviewer Review Type Date Requested Status
Daniel d'Andrada (community) 2015-04-15 Approve on 2015-05-06
PS Jenkins bot continuous-integration Needs Fixing on 2015-05-06
Michael Zanetti (community) Needs Information on 2015-04-15
Review via email: mp+256287@code.launchpad.net

Commit Message

Fixed desktop stage app focus.

Description of the Change

Fixed desktop stage app focus.
Added mock for Utils plugin so WindowStateStorage positiong can be set up for testing and so it does not backend to SQL.

 * Are there any related MPs required for this MP to build/function as expected? Please list.
No

 * Did you perform an exploratory manual test run of your code change and any related functionality?
Yes

 * 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.
Daniel d'Andrada (dandrader) wrote :

I think this has been fixed already.

Why are you removing tests like test_tappingOnWindowChangesFocusedApp?

review: Needs Information
Daniel d'Andrada (dandrader) wrote :

> I think this has been fixed already.

I'm talking more specifically about revision 1688, "DesktopStage - fix focus switch when user taps on window"

Michael Zanetti (mzanetti) wrote :

One inline comment. otherwise looks good to me and I'm using this branch already in lp:~mzanetti/unity8/desktop-spread

Michael Zanetti (mzanetti) :
review: Needs Information
Nick Dedekind (nick-dedekind) wrote :

> > I think this has been fixed already.
>
> I'm talking more specifically about revision 1688, "DesktopStage - fix focus
> switch when user taps on window"

It was fixed for the tap, but not for the open. When you open a new app on desktop stage it doesn't get focus.

1650. By Nick Dedekind on 2015-04-20

re-added test for click on app window

Nick Dedekind (nick-dedekind) wrote :

> I think this has been fixed already.
>
> Why are you removing tests like test_tappingOnWindowChangesFocusedApp?

If you revert the change in DesktopStage and run the tests, you will see the failure.

1651. By Nick Dedekind on 2015-04-20

replaced mouseClick with tap for touch

1652. By Nick Dedekind on 2015-04-20

test rename

Nick Dedekind (nick-dedekind) wrote :

> One inline comment. otherwise looks good to me and I'm using this branch
> already in lp:~mzanetti/unity8/desktop-spread

No, I mistook it for the same as the decoration test I added.

Daniel d'Andrada (dandrader) wrote :

You should not break focus encapsulation. The DesktopStage delegate item shouldn't directly focus a child item of its DecoratedWindow. It's an implementation detail it doesn't need to know about. Furthermore appDelegate.focusWindow() is bypassing the focus scope of its DecoratedWindow, which is wrong. That's why you needed to call forceActiveFocus() in there (which will recursivelly set focus=true on all its parents).

The commit below solves these problems:

http://bazaar.launchpad.net/~dandrader/unity8/desktop-app-focus/revision/1653

review: Needs Fixing
1653. By Nick Dedekind on 2015-04-28

don't break focus encasulation

Nick Dedekind (nick-dedekind) wrote :

> You should not break focus encapsulation. The DesktopStage delegate item
> shouldn't directly focus a child item of its DecoratedWindow. It's an
> implementation detail it doesn't need to know about. Furthermore
> appDelegate.focusWindow() is bypassing the focus scope of its DecoratedWindow,
> which is wrong. That's why you needed to call forceActiveFocus() in there
> (which will recursivelly set focus=true on all its parents).
>
> The commit below solves these problems:
>
> http://bazaar.launchpad.net/~dandrader/unity8/desktop-app-focus/revision/1653

Ok, makes sense. I've committed your changes to my branch.

Daniel d'Andrada (dandrader) wrote :

Looks mostly good. Only some minor issues left

In tests/mocks/Utils/plugin.cpp
--------------------------------

"""
+ * Copyright (C) 2012 Canonical, Ltd.
"""

Please update the copyright year.

"""
// local
#include "inputwatcher.h"
#include "qlimitproxymodelqml.h"
#include "unitysortfilterproxymodelqml.h"
#include "relativetimeformatter.h"
#include "timeformatter.h"
#include "unitymenumodelpaths.h"
#include "windowkeysfilter.h"
#include "easingcurve.h"
#include "windowstatestorage.h"
#include "constants.h"
"""

Most of those are not local but come from the real plugin, in a different directory.
So it's worth noting it by grouping them separately and including with <foo.h> instead of "foo.h".

In tests/mocks/Utils/plugin.h
--------------------------------

"""
 * Copyright (C) 2012 Canonical, Ltd.
"""

Please update the copyright year.

In tests/qmltests/Stages/tst_DesktopStage.qml
---------------------------------------------

"""
831- * Copyright (C) 2015 Canonical, Ltd.
832+ * Copyright (C) 2013,2014 Canonical, Ltd.
"""

Bogus change. It should be just 2015.

"""
1151 function cleanup() {
1152+ mouseEmulation.checked = true;
1153 tryCompare(shell, "enabled", true); // make sure greeter didn't leave us in
"""

I don't see why the automated tests have to care about whether this control is checked or not. It has no effect over them.

"""
1132+ Column {
1133+ anchors { left: parent.left; right: parent.right }
1134+ spacing: units.gu(1)
1135+
1136+ Label { text: "Applications"; font.bold: true }
1137+
1138+ Repeater {
1139+ id: appRepeater
1140+ model: ApplicationManager.availableApplications
1141+ ApplicationCheckBox {
1142+ appId: modelData
1143+ }
1144+ }
"""

This is nice but now they don't fit all in the window at the same time, you have to enlarge the window to see the bottom ones. It's time to put the controls column (I mean, all controls, not just these, see the darkgrey Rectangle with id:controls) into a vertical Flickable.

review: Needs Fixing
1654. By Nick Dedekind on 2015-05-01

fixed more focus issues

1655. By Nick Dedekind on 2015-05-01

review comments

1656. By Nick Dedekind on 2015-05-01

cleanup focus

Nick Dedekind (nick-dedekind) wrote :

Focus was still broken when you switch to desktop mode.
Reproduce:
1) make tryShell
2) Select 'Desktop' form factor
3) Log in

Result:
Dash should be focused, but is't
Starting other apps doesn't resolve focus until we click on one.

------------------------
In fact, it's broken when you switch between all form factors. Looks like we're not picking up changes when we have app focus, but no delegates have been created yet.

Fixed now, and added test for focus on form factor switching.

Daniel d'Andrada (dandrader) wrote :

"""
file:///tmp/buildd/unity8-8.02+15.04.20150423bzr1657pkg0vivid830/tests/qmltests/tst_Shell.qml:129:17: Type Shell unavailable
                     Shell {
                     ^
file:///tmp/buildd/unity8-8.02+15.04.20150423bzr1657pkg0vivid830/qml/Shell.qml:182:5: HomeKeyWatcher is not a type
         HomeKeyWatcher {
         ^
"""

Please merge trunk. Your mock Utils plugin is outdated.

review: Needs Fixing
Daniel d'Andrada (dandrader) wrote :

Just noticed an issue with the tst_Shell tests: you're not testing stage switches (eg from TabletStage to DesktopStage) but reloading the entire Shell.qml. This is not what happens when you connect a tablet to a mouse and a keyboard.

Form factor and usage mode are two separate things.

This commit fixes it:
http://bazaar.launchpad.net/~dandrader/unity8/desktop-app-focus/revision/1657

PS: I did it myself as I've work depending on this branch and I didn't want to get blocked.

review: Needs Fixing
Daniel d'Andrada (dandrader) wrote :

> """
> file:///tmp/buildd/unity8-8.02+15.04.20150423bzr1657pkg0vivid830/tests/qmltest
> s/tst_Shell.qml:129:17: Type Shell unavailable
> Shell {
> ^
> file:///tmp/buildd/unity8-8.02+15.04.20150423bzr1657pkg0vivid830/qml/Shell.qml
> :182:5: HomeKeyWatcher is not a type
> HomeKeyWatcher {
> ^
> """
>
> Please merge trunk. Your mock Utils plugin is outdated.

And the fix:
http://bazaar.launchpad.net/~dandrader/unity8/desktop-app-focus/revision/1659

Nick Dedekind (nick-dedekind) wrote :

> Just noticed an issue with the tst_Shell tests: you're not testing stage
> switches (eg from TabletStage to DesktopStage) but reloading the entire
> Shell.qml. This is not what happens when you connect a tablet to a mouse and a
> keyboard.
>
> Form factor and usage mode are two separate things.
>
> This commit fixes it:
> http://bazaar.launchpad.net/~dandrader/unity8/desktop-app-focus/revision/1657
>
> PS: I did it myself as I've work depending on this branch and I didn't want to
> get blocked.

Good enough for me. Can we make a note to rename the Stages. I don't like the idea of having a Tablet & Desktop stage, when they're actually representing modes ([split]staged/windowed) rather than senarios (tablet/desktop).

1657. By Nick Dedekind on 2015-05-06

merged with ~dandrader/unity8/desktop-app-focus

Michael Zanetti (mzanetti) wrote :

> Good enough for me. Can we make a note to rename the Stages. I don't like the
> idea of having a Tablet & Desktop stage, when they're actually representing
> modes ([split]staged/windowed) rather than senarios (tablet/desktop).

Yeah... Actually I think the long term plan is to get rid of them. In the desktop-spread branch I think I figured a way to be able to do all the stages in one codebase by just replacing the maths. So don't get started with renaming everything right now.

Daniel d'Andrada (dandrader) wrote :

* Did you perform an exploratory manual test run of the code change and any related functionality?
Yes.

* Did CI run pass? If not, please explain why.
Just the usual autopilot failures.

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

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'qml/Shell.qml'
2--- qml/Shell.qml 2015-04-22 13:18:06 +0000
3+++ qml/Shell.qml 2015-05-06 11:43:33 +0000
4@@ -110,7 +110,6 @@
5
6 GSettings {
7 id: backgroundSettings
8- objectName: "backgroundSettings"
9 schema.id: "org.gnome.desktop.background"
10 }
11
12
13=== modified file 'qml/Stages/DecoratedWindow.qml'
14--- qml/Stages/DecoratedWindow.qml 2015-03-13 19:01:32 +0000
15+++ qml/Stages/DecoratedWindow.qml 2015-05-06 11:43:33 +0000
16@@ -42,6 +42,7 @@
17
18 WindowDecoration {
19 id: decoration
20+ objectName: application ? "appWindowDecoration_" + application.appId : "appWindowDecoration_null"
21 anchors { left: parent.left; top: parent.top; right: parent.right }
22 height: units.gu(3)
23 title: model.name
24
25=== modified file 'qml/Stages/DesktopStage.qml'
26--- qml/Stages/DesktopStage.qml 2015-03-13 19:18:35 +0000
27+++ qml/Stages/DesktopStage.qml 2015-05-06 11:43:33 +0000
28@@ -20,17 +20,14 @@
29 import Ubuntu.Components 1.1
30 import Unity.Application 0.1
31 import "../Components/PanelState"
32-import Utils 0.1
33
34-FocusScope {
35+Rectangle {
36 id: root
37
38 anchors.fill: parent
39
40 property alias background: wallpaper.source
41
42- property var windowStateStorage: WindowStateStorage
43-
44 CrossFadeImage {
45 id: wallpaper
46 anchors.fill: parent
47@@ -58,7 +55,16 @@
48 id: priv
49
50 readonly property string focusedAppId: ApplicationManager.focusedApplicationId
51- readonly property var focusedAppDelegate: focusedAppId ? appRepeater.itemAt(indexOf(focusedAppId)) : null
52+ readonly property var focusedAppDelegate: {
53+ var index = indexOf(focusedAppId);
54+ return index >= 0 && index < appRepeater.count ? appRepeater.itemAt(index) : null
55+ }
56+
57+ onFocusedAppDelegateChanged: {
58+ if (focusedAppDelegate) {
59+ focusedAppDelegate.focus = true;
60+ }
61+ }
62
63 function indexOf(appId) {
64 for (var i = 0; i < ApplicationManager.count; i++) {
65@@ -89,13 +95,19 @@
66 id: appRepeater
67 model: ApplicationManager
68
69- delegate: Item {
70+ delegate: FocusScope {
71 id: appDelegate
72 z: ApplicationManager.count - index
73 y: units.gu(3)
74 width: units.gu(60)
75 height: units.gu(50)
76
77+ onFocusChanged: {
78+ if (focus) {
79+ ApplicationManager.requestFocusApplication(model.appId);
80+ }
81+ }
82+
83 readonly property int minWidth: units.gu(10)
84 readonly property int minHeight: units.gu(10)
85
86@@ -119,14 +131,13 @@
87 ]
88
89 WindowMoveResizeArea {
90- windowStateStorage: root.windowStateStorage
91 target: appDelegate
92 minWidth: appDelegate.minWidth
93 minHeight: appDelegate.minHeight
94 resizeHandleWidth: units.gu(0.5)
95 windowId: model.appId // FIXME: Change this to point to windowId once we have such a thing
96
97- onPressed: decoratedWindow.focus = true;
98+ onPressed: appDelegate.focus = true;
99 }
100
101 DecoratedWindow {
102@@ -135,12 +146,7 @@
103 anchors.fill: parent
104 application: ApplicationManager.get(index)
105 active: ApplicationManager.focusedApplicationId === model.appId
106-
107- onFocusChanged: {
108- if (focus) {
109- ApplicationManager.requestFocusApplication(model.appId);
110- }
111- }
112+ focus: true
113
114 onClose: ApplicationManager.stopApplication(model.appId)
115 onMaximize: appDelegate.state = (appDelegate.state == "maximized" ? "normal" : "maximized")
116
117=== modified file 'qml/Stages/PhoneStage.qml'
118--- qml/Stages/PhoneStage.qml 2015-02-18 18:29:03 +0000
119+++ qml/Stages/PhoneStage.qml 2015-05-06 11:43:33 +0000
120@@ -117,14 +117,14 @@
121
122 readonly property int firstSpreadIndex: root.focusFirstApp ? 1 : 0
123 property string focusedAppId: applicationManager.focusedApplicationId
124- property var focusedApplication: applicationManager.findApplication(focusedAppId)
125- property var focusedAppDelegate: null
126+ readonly property var focusedAppDelegate: {
127+ var index = indexOf(focusedAppId);
128+ return index >= 0 && index < spreadRepeater.count ? spreadRepeater.itemAt(index) : null
129+ }
130
131 property real oldInverseProgress: 0
132 property bool animateX: false
133
134- onFocusedAppIdChanged: focusedAppDelegate = spreadRepeater.itemAt(0);
135-
136 onFocusedAppDelegateChanged: {
137 if (focusedAppDelegate) {
138 focusedAppDelegate.focus = true;
139
140=== modified file 'qml/Stages/TabletStage.qml'
141--- qml/Stages/TabletStage.qml 2015-02-02 16:28:03 +0000
142+++ qml/Stages/TabletStage.qml 2015-05-06 11:43:33 +0000
143@@ -53,6 +53,11 @@
144 id: priv
145
146 property string focusedAppId: ApplicationManager.focusedApplicationId
147+ readonly property var focusedAppDelegate: {
148+ var index = indexOf(focusedAppId);
149+ return index >= 0 && index < spreadRepeater.count ? spreadRepeater.itemAt(index) : null
150+ }
151+
152 property string oldFocusedAppId: ""
153
154 property string mainStageAppId
155@@ -76,19 +81,11 @@
156
157 appId0 = ApplicationManager.count >= 1 ? ApplicationManager.get(0).appId : "";
158 appId1 = ApplicationManager.count > 1 ? ApplicationManager.get(1).appId : "";
159-
160- // Update the QML focus accordingly
161- updateSpreadDelegateFocus();
162 }
163
164- function updateSpreadDelegateFocus() {
165- if (priv.focusedAppId) {
166- var focusedAppIndex = priv.indexOf(priv.focusedAppId);
167- if (focusedAppIndex !== -1) {
168- spreadRepeater.itemAt(focusedAppIndex).focus = true;
169- } else {
170- console.warn("TabletStage: Failed to find the SpreadDelegate for appID=" + priv.focusedAppId);
171- }
172+ onFocusedAppDelegateChanged: {
173+ if (focusedAppDelegate) {
174+ focusedAppDelegate.focus = true;
175 }
176 }
177
178@@ -496,6 +493,8 @@
179
180 delegate: TransformedTabletSpreadDelegate {
181 id: spreadTile
182+ objectName: model.appId ? "tabletSpreadDelegate_" + model.appId
183+ : "tabletSpreadDelegate_null";
184 height: spreadView.height
185 width: model.stage == ApplicationInfoInterface.MainStage ? spreadView.width : spreadView.sideStageWidth
186 active: model.appId == priv.mainStageAppId || model.appId == priv.sideStageAppId
187
188=== modified file 'tests/mocks/CMakeLists.txt'
189--- tests/mocks/CMakeLists.txt 2015-03-12 13:59:07 +0000
190+++ tests/mocks/CMakeLists.txt 2015-05-06 11:43:33 +0000
191@@ -41,6 +41,7 @@
192 add_subdirectory(Unity)
193 add_subdirectory(QtMultimedia)
194 add_subdirectory(Wizard)
195+add_subdirectory(Utils)
196
197 install(
198 DIRECTORY data
199
200=== modified file 'tests/mocks/GSettings.1.0/fake_gsettings.h'
201--- tests/mocks/GSettings.1.0/fake_gsettings.h 2015-03-23 18:19:12 +0000
202+++ tests/mocks/GSettings.1.0/fake_gsettings.h 2015-05-06 11:43:33 +0000
203@@ -78,10 +78,10 @@
204 ~GSettingsControllerQml();
205
206 QString pictureUri() const;
207- void setPictureUri(const QString &str);
208+ Q_INVOKABLE void setPictureUri(const QString &str);
209
210 QString usageMode() const;
211- void setUsageMode(const QString &usageMode);
212+ Q_INVOKABLE void setUsageMode(const QString &usageMode);
213
214 Q_SIGNALS:
215 void pictureUriChanged(const QString&);
216
217=== modified file 'tests/mocks/GSettings.1.0/plugin.cpp'
218--- tests/mocks/GSettings.1.0/plugin.cpp 2015-03-23 18:19:12 +0000
219+++ tests/mocks/GSettings.1.0/plugin.cpp 2015-05-06 11:43:33 +0000
220@@ -19,8 +19,14 @@
221
222 #include <QtQml/qqml.h>
223
224+static QObject* controllerProvider(QQmlEngine* /* engine */, QJSEngine* /* scriptEngine */)
225+{
226+ return GSettingsControllerQml::instance();
227+}
228+
229 void FakeGSettingsQmlPlugin::registerTypes(const char *uri)
230 {
231+ qmlRegisterSingletonType<GSettingsControllerQml>(uri, 1, 0, "GSettingsController", controllerProvider);
232 qmlRegisterType<GSettingsQml>(uri, 1, 0, "GSettings");
233 qmlRegisterUncreatableType<GSettingsSchemaQml>(uri, 1, 0, "GSettingsSchema",
234 "GSettingsSchema can only be used inside of a GSettings component");
235
236=== modified file 'tests/mocks/Unity/Application/ApplicationManager.cpp'
237--- tests/mocks/Unity/Application/ApplicationManager.cpp 2015-03-10 14:25:12 +0000
238+++ tests/mocks/Unity/Application/ApplicationManager.cpp 2015-05-06 11:43:33 +0000
239@@ -143,7 +143,7 @@
240 }
241
242 void ApplicationManager::add(ApplicationInfo *application) {
243- if (!application) {
244+ if (!application || m_runningApplications.contains(application)) {
245 return;
246 }
247
248
249=== added directory 'tests/mocks/Utils'
250=== added file 'tests/mocks/Utils/CMakeLists.txt'
251--- tests/mocks/Utils/CMakeLists.txt 1970-01-01 00:00:00 +0000
252+++ tests/mocks/Utils/CMakeLists.txt 2015-05-06 11:43:33 +0000
253@@ -0,0 +1,39 @@
254+pkg_search_module(GIO REQUIRED gio-2.0)
255+
256+include_directories(
257+ ${CMAKE_CURRENT_SOURCE_DIR}
258+ ${CMAKE_CURRENT_BINARY_DIR}
259+ ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
260+ ${GIO_INCLUDE_DIRS}
261+ ${CMAKE_SOURCE_DIR}/plugins/Utils
262+)
263+
264+set(QMLPLUGIN_SRC
265+ ${CMAKE_SOURCE_DIR}/plugins/Utils/inputwatcher.cpp
266+ ${CMAKE_SOURCE_DIR}/plugins/Utils/qlimitproxymodelqml.cpp
267+ ${CMAKE_SOURCE_DIR}/plugins/Utils/unitysortfilterproxymodelqml.cpp
268+ ${CMAKE_SOURCE_DIR}/plugins/Utils/relativetimeformatter.cpp
269+ ${CMAKE_SOURCE_DIR}/plugins/Utils/timeformatter.cpp
270+ ${CMAKE_SOURCE_DIR}/plugins/Utils/unitymenumodelpaths.cpp
271+ ${CMAKE_SOURCE_DIR}/plugins/Utils/windowkeysfilter.cpp
272+ ${CMAKE_SOURCE_DIR}/plugins/Utils/easingcurve.cpp
273+ constants.cpp
274+ plugin.cpp
275+ windowstatestorage.cpp
276+ )
277+
278+add_library(FakeUtils-qml SHARED
279+ ${QMLPLUGIN_SRC}
280+ )
281+
282+target_link_libraries(FakeUtils-qml ${GIO_LDFLAGS})
283+
284+# Because this is an internal support library, we want
285+# to expose all symbols in it. Consider changing this
286+# either to a static library or just using the
287+# files directly in targets.
288+set_target_properties(FakeUtils-qml PROPERTIES COMPILE_FLAGS -fvisibility=default)
289+
290+qt5_use_modules(FakeUtils-qml Qml Quick DBus Network Gui)
291+
292+add_unity8_mock(Utils 0.1 Utils TARGETS FakeUtils-qml)
293
294=== added file 'tests/mocks/Utils/HomeKeyWatcher.qml'
295--- tests/mocks/Utils/HomeKeyWatcher.qml 1970-01-01 00:00:00 +0000
296+++ tests/mocks/Utils/HomeKeyWatcher.qml 2015-05-06 11:43:33 +0000
297@@ -0,0 +1,21 @@
298+/*
299+ * Copyright (C) 2015 Canonical, Ltd.
300+ *
301+ * This program is free software; you can redistribute it and/or modify
302+ * it under the terms of the GNU General Public License as published by
303+ * the Free Software Foundation; version 3.
304+ *
305+ * This program is distributed in the hope that it will be useful,
306+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
307+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
308+ * GNU General Public License for more details.
309+ *
310+ * You should have received a copy of the GNU General Public License
311+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
312+ */
313+
314+import QtQuick 2.0
315+
316+QtObject {
317+ signal activated()
318+}
319
320=== added file 'tests/mocks/Utils/Style.js'
321--- tests/mocks/Utils/Style.js 1970-01-01 00:00:00 +0000
322+++ tests/mocks/Utils/Style.js 2015-05-06 11:43:33 +0000
323@@ -0,0 +1,37 @@
324+/*
325+ * Copyright (C) 2014 Canonical, Ltd.
326+ *
327+ * This program is free software; you can redistribute it and/or modify
328+ * it under the terms of the GNU General Public License as published by
329+ * the Free Software Foundation; version 3.
330+ *
331+ * This program is distributed in the hope that it will be useful,
332+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
333+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
334+ * GNU General Public License for more details.
335+ *
336+ * You should have received a copy of the GNU General Public License
337+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
338+ */
339+
340+.pragma library
341+
342+/*! \brief Calculate average luminance of the passed colors
343+
344+ \note If not fully opaque, luminance is dependant on blending.
345+ */
346+function luminance() {
347+ var sum = 0;
348+ // TODO this was originally
349+ // for (var k in arguments) {
350+ // but for some unkown reason was causing crashes in testDash/testDashContent
351+ // investigate when we have some time
352+ for (var k = 0; k < arguments.length; ++k) {
353+ // only way to convert string to color
354+ var c = Qt.lighter(arguments[k], 1.0);
355+
356+ sum += 0.2126 * c.r + 0.7152 * c.g + 0.0722 * c.b;
357+ }
358+
359+ return sum / arguments.length;
360+}
361
362=== added file 'tests/mocks/Utils/Utils.qmltypes'
363--- tests/mocks/Utils/Utils.qmltypes 1970-01-01 00:00:00 +0000
364+++ tests/mocks/Utils/Utils.qmltypes 2015-05-06 11:43:33 +0000
365@@ -0,0 +1,175 @@
366+import QtQuick.tooling 1.1
367+
368+// This file describes the plugin-supplied types contained in the library.
369+// It is used for QML tooling purposes only.
370+//
371+// This file was auto-generated by:
372+// 'qmlplugindump -notrelocatable Utils 0.1 plugins'
373+
374+Module {
375+ Component {
376+ name: "EasingCurve"
377+ prototype: "QObject"
378+ exports: ["Utils/EasingCurve 0.1"]
379+ exportMetaObjectRevisions: [0]
380+ Property { name: "type"; type: "QEasingCurve::Type" }
381+ Property { name: "period"; type: "double" }
382+ Property { name: "progress"; type: "double" }
383+ Property { name: "value"; type: "double"; isReadonly: true }
384+ }
385+ Component {
386+ name: "QAbstractProxyModel"
387+ prototype: "QAbstractItemModel"
388+ Property { name: "sourceModel"; type: "QAbstractItemModel"; isPointer: true }
389+ }
390+ Component { name: "QIdentityProxyModel"; prototype: "QAbstractProxyModel" }
391+ Component {
392+ name: "QLimitProxyModelQML"
393+ prototype: "QIdentityProxyModel"
394+ exports: ["Utils/LimitProxyModel 0.1"]
395+ exportMetaObjectRevisions: [0]
396+ Property { name: "model"; type: "QAbstractItemModel"; isPointer: true }
397+ Property { name: "limit"; type: "int" }
398+ Property { name: "count"; type: "int"; isReadonly: true }
399+ Signal { name: "totalCountChanged" }
400+ }
401+ Component {
402+ name: "QSortFilterProxyModel"
403+ prototype: "QAbstractProxyModel"
404+ Property { name: "filterRegExp"; type: "QRegExp" }
405+ Property { name: "filterKeyColumn"; type: "int" }
406+ Property { name: "dynamicSortFilter"; type: "bool" }
407+ Property { name: "filterCaseSensitivity"; type: "Qt::CaseSensitivity" }
408+ Property { name: "sortCaseSensitivity"; type: "Qt::CaseSensitivity" }
409+ Property { name: "isSortLocaleAware"; type: "bool" }
410+ Property { name: "sortRole"; type: "int" }
411+ Property { name: "filterRole"; type: "int" }
412+ Method {
413+ name: "setFilterRegExp"
414+ Parameter { name: "pattern"; type: "string" }
415+ }
416+ Method {
417+ name: "setFilterWildcard"
418+ Parameter { name: "pattern"; type: "string" }
419+ }
420+ Method {
421+ name: "setFilterFixedString"
422+ Parameter { name: "pattern"; type: "string" }
423+ }
424+ Method { name: "clear" }
425+ Method { name: "invalidate" }
426+ }
427+ Component {
428+ name: "RelativeTimeFormatter"
429+ prototype: "TimeFormatter"
430+ exports: ["Utils/RelativeTimeFormatter 0.1"]
431+ exportMetaObjectRevisions: [0]
432+ }
433+ Component {
434+ name: "TimeFormatter"
435+ prototype: "QObject"
436+ exports: ["Utils/GDateTimeFormatter 0.1", "Utils/TimeFormatter 0.1"]
437+ exportMetaObjectRevisions: [0, 0]
438+ Property { name: "format"; type: "string" }
439+ Property { name: "timeString"; type: "string"; isReadonly: true }
440+ Property { name: "time"; type: "qlonglong" }
441+ Signal {
442+ name: "formatChanged"
443+ Parameter { name: "format"; type: "string" }
444+ }
445+ Signal {
446+ name: "timeStringChanged"
447+ Parameter { name: "timeString"; type: "string" }
448+ }
449+ Signal {
450+ name: "timeChanged"
451+ Parameter { name: "time"; type: "qlonglong" }
452+ }
453+ }
454+ Component {
455+ name: "UnityMenuModelPaths"
456+ prototype: "QObject"
457+ exports: ["Utils/UnityMenuModelPaths 0.1"]
458+ exportMetaObjectRevisions: [0]
459+ Property { name: "source"; type: "QVariant" }
460+ Property { name: "busName"; type: "QByteArray"; isReadonly: true }
461+ Property { name: "actions"; type: "QVariantMap"; isReadonly: true }
462+ Property { name: "menuObjectPath"; type: "QByteArray"; isReadonly: true }
463+ Property { name: "busNameHint"; type: "QByteArray" }
464+ Property { name: "actionsHint"; type: "QByteArray" }
465+ Property { name: "menuObjectPathHint"; type: "QByteArray" }
466+ }
467+ Component {
468+ name: "UnitySortFilterProxyModelQML"
469+ prototype: "QSortFilterProxyModel"
470+ exports: ["Utils/UnitySortFilterProxyModel 0.1"]
471+ exportMetaObjectRevisions: [0]
472+ Property { name: "model"; type: "QAbstractItemModel"; isPointer: true }
473+ Property { name: "totalCount"; type: "int"; isReadonly: true }
474+ Property { name: "count"; type: "int"; isReadonly: true }
475+ Property { name: "invertMatch"; type: "bool" }
476+ Signal {
477+ name: "invertMatchChanged"
478+ Parameter { type: "bool" }
479+ }
480+ Method {
481+ name: "get"
482+ type: "QVariantMap"
483+ Parameter { name: "row"; type: "int" }
484+ }
485+ Method {
486+ name: "data"
487+ type: "QVariant"
488+ Parameter { name: "row"; type: "int" }
489+ Parameter { name: "role"; type: "int" }
490+ }
491+ Method { name: "count"; type: "int" }
492+ Method {
493+ name: "findFirst"
494+ type: "int"
495+ Parameter { name: "role"; type: "int" }
496+ Parameter { name: "value"; type: "QVariant" }
497+ }
498+ Method {
499+ name: "mapRowToSource"
500+ type: "int"
501+ Parameter { name: "row"; type: "int" }
502+ }
503+ Method {
504+ name: "mapFromSource"
505+ type: "int"
506+ Parameter { name: "row"; type: "int" }
507+ }
508+ Method {
509+ name: "mapToSource"
510+ type: "int"
511+ Parameter { name: "row"; type: "int" }
512+ }
513+ }
514+ Component {
515+ name: "WindowKeysFilter"
516+ defaultProperty: "data"
517+ prototype: "QQuickItem"
518+ exports: ["Utils/WindowKeysFilter 0.1"]
519+ exportMetaObjectRevisions: [0]
520+ }
521+ Component {
522+ name: "WindowStateStorage"
523+ prototype: "QObject"
524+ exports: ["Utils/WindowStateStorage 0.1"]
525+ isCreatable: false
526+ isSingleton: true
527+ exportMetaObjectRevisions: [0]
528+ Method {
529+ name: "saveGeometry"
530+ Parameter { name: "windowId"; type: "string" }
531+ Parameter { name: "rect"; type: "QRect" }
532+ }
533+ Method {
534+ name: "getGeometry"
535+ type: "QRect"
536+ Parameter { name: "windowId"; type: "string" }
537+ Parameter { name: "defaultValue"; type: "QRect" }
538+ }
539+ }
540+}
541
542=== added file 'tests/mocks/Utils/constants.cpp'
543--- tests/mocks/Utils/constants.cpp 1970-01-01 00:00:00 +0000
544+++ tests/mocks/Utils/constants.cpp 2015-05-06 11:43:33 +0000
545@@ -0,0 +1,23 @@
546+/*
547+ * Copyright 2015 Canonical Ltd.
548+ *
549+ * This program is free software; you can redistribute it and/or modify
550+ * it under the terms of the GNU Lesser General Public License as published by
551+ * the Free Software Foundation; version 3.
552+ *
553+ * This program is distributed in the hope that it will be useful,
554+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
555+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
556+ * GNU Lesser General Public License for more details.
557+ *
558+ * You should have received a copy of the GNU Lesser General Public License
559+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
560+*/
561+
562+#include "constants.h"
563+
564+Constants::Constants(QObject *parent)
565+ : QObject(parent)
566+{
567+ m_indicatorValueTimeout = 5000;
568+}
569
570=== added file 'tests/mocks/Utils/constants.h'
571--- tests/mocks/Utils/constants.h 1970-01-01 00:00:00 +0000
572+++ tests/mocks/Utils/constants.h 2015-05-06 11:43:33 +0000
573@@ -0,0 +1,43 @@
574+/*
575+ * Copyright 2015 Canonical Ltd.
576+ *
577+ * This program is free software; you can redistribute it and/or modify
578+ * it under the terms of the GNU Lesser General Public License as published by
579+ * the Free Software Foundation; version 3.
580+ *
581+ * This program is distributed in the hope that it will be useful,
582+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
583+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
584+ * GNU Lesser General Public License for more details.
585+ *
586+ * You should have received a copy of the GNU Lesser General Public License
587+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
588+*/
589+
590+#ifndef CONSTANTS_H
591+#define CONSTANTS_H
592+
593+#include <QObject>
594+
595+/**
596+ * @brief The Constants class
597+ *
598+ * This singleton class exposes contants to Qml
599+ *
600+ */
601+
602+class Constants: public QObject
603+{
604+ Q_OBJECT
605+ Q_PROPERTY(int indicatorValueTimeout READ indicatorValueTimeout CONSTANT)
606+
607+public:
608+ Constants(QObject *parent = 0);
609+
610+ int indicatorValueTimeout() const { return m_indicatorValueTimeout; }
611+
612+private:
613+ int m_indicatorValueTimeout;
614+};
615+
616+#endif
617
618=== added file 'tests/mocks/Utils/plugin.cpp'
619--- tests/mocks/Utils/plugin.cpp 1970-01-01 00:00:00 +0000
620+++ tests/mocks/Utils/plugin.cpp 2015-05-06 11:43:33 +0000
621@@ -0,0 +1,73 @@
622+/*
623+ * Copyright (C) 2015 Canonical, Ltd.
624+ *
625+ * This program is free software; you can redistribute it and/or modify
626+ * it under the terms of the GNU General Public License as published by
627+ * the Free Software Foundation; version 3.
628+ *
629+ * This program is distributed in the hope that it will be useful,
630+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
631+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
632+ * GNU General Public License for more details.
633+ *
634+ * You should have received a copy of the GNU General Public License
635+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
636+ */
637+
638+// Qt
639+#include <QtQml/qqml.h>
640+#include <QDBusConnection>
641+#include <QQmlContext>
642+#include <QtQuick/QQuickWindow>
643+#include <QDebug>
644+
645+// local
646+#include "plugin.h"
647+#include "windowstatestorage.h"
648+#include "constants.h"
649+
650+// plugin
651+#include <inputwatcher.h>
652+#include <qlimitproxymodelqml.h>
653+#include <unitysortfilterproxymodelqml.h>
654+#include <relativetimeformatter.h>
655+#include <timeformatter.h>
656+#include <unitymenumodelpaths.h>
657+#include <windowkeysfilter.h>
658+#include <easingcurve.h>
659+
660+static QObject *createWindowStateStorage(QQmlEngine *engine, QJSEngine *scriptEngine)
661+{
662+ Q_UNUSED(engine)
663+ Q_UNUSED(scriptEngine)
664+ return new WindowStateStorage();
665+}
666+
667+static QObject *createConstants(QQmlEngine *engine, QJSEngine *scriptEngine)
668+{
669+ Q_UNUSED(engine)
670+ Q_UNUSED(scriptEngine)
671+ return new Constants();
672+}
673+
674+void FakeUtilsPlugin::registerTypes(const char *uri)
675+{
676+ Q_ASSERT(uri == QLatin1String("Utils"));;
677+ qmlRegisterType<QAbstractItemModel>();
678+ qmlRegisterType<QLimitProxyModelQML>(uri, 0, 1, "LimitProxyModel");
679+ qmlRegisterType<UnitySortFilterProxyModelQML>(uri, 0, 1, "UnitySortFilterProxyModel");
680+ qmlRegisterType<UnityMenuModelPaths>(uri, 0, 1, "UnityMenuModelPaths");
681+ qmlRegisterType<TimeFormatter>(uri, 0, 1, "TimeFormatter");
682+ qmlRegisterType<WindowKeysFilter>(uri, 0, 1, "WindowKeysFilter");
683+ qmlRegisterType<GDateTimeFormatter>(uri, 0, 1, "GDateTimeFormatter");
684+ qmlRegisterType<EasingCurve>(uri, 0, 1, "EasingCurve");
685+ qmlRegisterType<RelativeTimeFormatter>(uri, 0, 1, "RelativeTimeFormatter");
686+ qmlRegisterSingletonType<WindowStateStorage>(uri, 0, 1, "WindowStateStorage", createWindowStateStorage);
687+ qmlRegisterType<InputWatcher>(uri, 0, 1, "InputWatcher");
688+ qmlRegisterSingletonType<Constants>(uri, 0, 1, "Constants", createConstants);
689+}
690+
691+void FakeUtilsPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
692+{
693+ QQmlExtensionPlugin::initializeEngine(engine, uri);
694+}
695
696=== added file 'tests/mocks/Utils/plugin.h'
697--- tests/mocks/Utils/plugin.h 1970-01-01 00:00:00 +0000
698+++ tests/mocks/Utils/plugin.h 2015-05-06 11:43:33 +0000
699@@ -0,0 +1,33 @@
700+/*
701+ * Copyright (C) 2015 Canonical, Ltd.
702+ *
703+ * This program is free software; you can redistribute it and/or modify
704+ * it under the terms of the GNU General Public License as published by
705+ * the Free Software Foundation; version 3.
706+ *
707+ * This program is distributed in the hope that it will be useful,
708+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
709+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
710+ * GNU General Public License for more details.
711+ *
712+ * You should have received a copy of the GNU General Public License
713+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
714+ */
715+
716+#ifndef FAKE_UTILS_PLUGIN_H
717+#define FAKE_UTILS_PLUGIN_H
718+
719+#include <QtQml/QQmlEngine>
720+#include <QtQml/QQmlExtensionPlugin>
721+
722+class FakeUtilsPlugin : public QQmlExtensionPlugin
723+{
724+ Q_OBJECT
725+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
726+
727+public:
728+ void registerTypes(const char *uri);
729+ void initializeEngine(QQmlEngine *engine, const char *uri);
730+};
731+
732+#endif
733
734=== added file 'tests/mocks/Utils/qmldir'
735--- tests/mocks/Utils/qmldir 1970-01-01 00:00:00 +0000
736+++ tests/mocks/Utils/qmldir 2015-05-06 11:43:33 +0000
737@@ -0,0 +1,5 @@
738+module Utils
739+plugin FakeUtils-qml
740+typeinfo Utils.qmltypes
741+Style 0.1 Style.js
742+HomeKeyWatcher 0.1 HomeKeyWatcher.qml
743
744=== added file 'tests/mocks/Utils/windowstatestorage.cpp'
745--- tests/mocks/Utils/windowstatestorage.cpp 1970-01-01 00:00:00 +0000
746+++ tests/mocks/Utils/windowstatestorage.cpp 2015-05-06 11:43:33 +0000
747@@ -0,0 +1,48 @@
748+/*
749+ * Copyright 2015 Canonical Ltd.
750+ *
751+ * This program is free software; you can redistribute it and/or modify
752+ * it under the terms of the GNU Lesser General Public License as published by
753+ * the Free Software Foundation; version 3.
754+ *
755+ * This program is distributed in the hope that it will be useful,
756+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
757+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
758+ * GNU Lesser General Public License for more details.
759+ *
760+ * You should have received a copy of the GNU Lesser General Public License
761+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
762+ */
763+
764+#include "windowstatestorage.h"
765+
766+#include <QRect>
767+
768+WindowStateStorage::WindowStateStorage(QObject *parent):
769+ QObject(parent)
770+{
771+}
772+
773+void WindowStateStorage::setGeometry(const QVariantMap& geometry)
774+{
775+ if (geometry != m_geometry) {
776+ m_geometry = geometry;
777+ Q_EMIT geometryChanged(m_geometry);
778+ }
779+}
780+
781+QVariantMap WindowStateStorage::geometry() const
782+{
783+ return m_geometry;
784+}
785+
786+void WindowStateStorage::saveGeometry(const QString &windowId, const QRect &rect)
787+{
788+ m_geometry[windowId] = rect;
789+}
790+
791+QRect WindowStateStorage::getGeometry(const QString &windowId, const QRect &defaultValue)
792+{
793+ if (!m_geometry.contains(windowId)) return defaultValue;
794+ return m_geometry.value(windowId).toRect();
795+}
796
797=== added file 'tests/mocks/Utils/windowstatestorage.h'
798--- tests/mocks/Utils/windowstatestorage.h 1970-01-01 00:00:00 +0000
799+++ tests/mocks/Utils/windowstatestorage.h 2015-05-06 11:43:33 +0000
800@@ -0,0 +1,38 @@
801+/*
802+ * Copyright 2015 Canonical Ltd.
803+ *
804+ * This program is free software; you can redistribute it and/or modify
805+ * it under the terms of the GNU Lesser General Public License as published by
806+ * the Free Software Foundation; version 3.
807+ *
808+ * This program is distributed in the hope that it will be useful,
809+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
810+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
811+ * GNU Lesser General Public License for more details.
812+ *
813+ * You should have received a copy of the GNU Lesser General Public License
814+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
815+ */
816+
817+#include <QObject>
818+#include <QVariantMap>
819+
820+class WindowStateStorage: public QObject
821+{
822+ Q_OBJECT
823+ Q_PROPERTY(QVariantMap geometry READ geometry WRITE setGeometry NOTIFY geometryChanged)
824+public:
825+ WindowStateStorage(QObject *parent = 0);
826+
827+ Q_INVOKABLE void saveGeometry(const QString &windowId, const QRect &rect);
828+ Q_INVOKABLE QRect getGeometry(const QString &windowId, const QRect &defaultValue);
829+
830+Q_SIGNALS:
831+ void geometryChanged(const QVariantMap& geometry);
832+
833+private:
834+ void setGeometry(const QVariantMap& geometry);
835+ QVariantMap geometry() const;
836+
837+ QVariantMap m_geometry;
838+};
839
840=== modified file 'tests/qmltests/Stages/ApplicationCheckBox.qml'
841--- tests/qmltests/Stages/ApplicationCheckBox.qml 2015-03-16 14:11:09 +0000
842+++ tests/qmltests/Stages/ApplicationCheckBox.qml 2015-05-06 11:43:33 +0000
843@@ -22,27 +22,67 @@
844 RowLayout {
845 id: root
846 property string appId
847- property alias checked: checkbox.checked
848+ property bool checked: false
849
850 enabled: appId !== "unity8-dash"
851
852+ onCheckedChanged: {
853+ if (d.bindGuard) { return; }
854+ d.bindGuard = true;
855+
856+ if (checked) {
857+ ApplicationManager.startApplication(root.appId);
858+ } else {
859+ ApplicationManager.stopApplication(root.appId);
860+ }
861+ d.bindGuard = false;
862+ }
863+
864+ QtObject {
865+ id: d
866+ property bool bindGuard: false
867+ property var application: null
868+ Component.onCompleted: {
869+ application = ApplicationManager.findApplication(root.appId);
870+ }
871+ }
872+
873+ Connections {
874+ target: ApplicationManager
875+ onCountChanged: {
876+ d.application = ApplicationManager.findApplication(root.appId);
877+ }
878+ }
879+
880 Layout.fillWidth: true
881 CheckBox {
882 id: checkbox
883 checked: false
884 activeFocusOnPress: false
885- property bool noop: false
886- onCheckedChanged: {
887- if (noop) {
888- return;
889- }
890+
891+ onTriggered: {
892+ if (d.bindGuard) { return; }
893+ d.bindGuard = true;
894+
895 if (checked) {
896- var application = ApplicationManager.startApplication(root.appId);
897- appConnections.target = application;
898+ ApplicationManager.startApplication(root.appId);
899 } else {
900- appConnections.target = null;
901 ApplicationManager.stopApplication(root.appId);
902 }
903+ d.bindGuard = false;
904+ }
905+ onCheckedChanged: {
906+ if (d.bindGuard) { return; }
907+ d.bindGuard = true;
908+
909+ root.checked = checked;
910+
911+ d.bindGuard = false;
912+ }
913+ Binding {
914+ target: checkbox
915+ property: "checked"
916+ value: d.application != null
917 }
918 }
919 Label {
920@@ -50,15 +90,4 @@
921 color: "white"
922 anchors.verticalCenter: parent.verticalCenter
923 }
924-
925- Connections {
926- id: appConnections
927- onStateChanged: {
928- if (target.state == ApplicationInfoInterface.Stopped) {
929- checkbox.noop = true;
930- checkbox.checked = false;
931- checkbox.noop = false;
932- }
933- }
934- }
935 }
936
937=== modified file 'tests/qmltests/Stages/tst_DesktopStage.qml'
938--- tests/qmltests/Stages/tst_DesktopStage.qml 2015-03-16 14:11:09 +0000
939+++ tests/qmltests/Stages/tst_DesktopStage.qml 2015-05-06 11:43:33 +0000
940@@ -20,6 +20,7 @@
941 import Ubuntu.Components.ListItems 1.0 as ListItem
942 import Unity.Application 0.1
943 import Unity.Test 0.1
944+import Utils 0.1
945
946 import "../../../qml/Stages"
947
948@@ -29,31 +30,18 @@
949 width: desktopStageLoader.width + controls.width
950 height: desktopStageLoader.height
951
952- QtObject {
953- id: fakeWindowStateStorage
954-
955- property var storedGeom: [
956- ["unity8-dash", Qt.rect(units.gu(2), units.gu(2), units.gu(50), units.gu(50))],
957- ["webbrowser-app", Qt.rect(units.gu(60), units.gu(2), units.gu(50), units.gu(50))]
958- ]
959-
960- function saveGeometry(windowId, geometry) {
961- for (var i = 0; i < storedGeom.length; ++i) {
962- if (storedGeom[i][0] === windowId) {
963- storedGeom[i][1] = geometry;
964- return;
965- }
966- }
967- // if new
968- storedGeom[storedGeom.length] = [windowId, geometry];
969- }
970- function getGeometry(windowId, defaultGeometry) {
971- for (var i = 0; i < storedGeom.length; ++i) {
972- if (storedGeom[i][0] === windowId) {
973- return storedGeom[i][1];
974- }
975- }
976- return defaultGeometry;
977+ Binding {
978+ target: MouseTouchAdaptor
979+ property: "enabled"
980+ value: false
981+ }
982+
983+ Component.onCompleted: {
984+ // ensures apps which are tested decorations are in view.
985+ WindowStateStorage.geometry = {
986+ 'unity8-dash': Qt.rect(0, units.gu(3), units.gu(50), units.gu(40)),
987+ 'dialer-app': Qt.rect(units.gu(51), units.gu(3), units.gu(50), units.gu(40)),
988+ 'camera-app': Qt.rect(0, units.gu(44), units.gu(50), units.gu(40)),
989 }
990 }
991
992@@ -70,7 +58,6 @@
993 sourceComponent: Component {
994 DesktopStage {
995 anchors.fill: parent
996- windowStateStorage: fakeWindowStateStorage
997 Component.onDestruction: {
998 desktopStageLoader.itemDestroyed = true;
999 }
1000@@ -108,9 +95,6 @@
1001
1002 property Item desktopStage: desktopStageLoader.status === Loader.Ready ? desktopStageLoader.item : null
1003
1004- function init() {
1005- }
1006-
1007 function cleanup() {
1008 desktopStageLoader.itemDestroyed = false;
1009 desktopStageLoader.active = false;
1010@@ -145,57 +129,83 @@
1011 tryCompare(appWindowStates, "state", "surface");
1012 }
1013
1014- function rectsIntersect(aLocal, bLocal) {
1015-
1016- var a = aLocal.mapToItem(null, 0, 0, aLocal.width, aLocal.height);
1017- var b = bLocal.mapToItem(null, 0, 0, bLocal.width, bLocal.height);
1018-
1019- return !((a.y+a.height) < b.y
1020- || a.y > (b.y+b.height)
1021- || (a.x+a.width) < b.x
1022- || a.x > (b.x+b.width));
1023- }
1024-
1025- function test_tappingOnWindowChangesFocusedApp() {
1026- ApplicationManager.startApplication("webbrowser-app");
1027- waitUntilAppSurfaceShowsUp("webbrowser-app");
1028-
1029- var webbrowserWindow = findChild(desktopStage, "appWindow_webbrowser-app");
1030- verify(webbrowserWindow);
1031- var dashWindow = findChild(desktopStage, "appWindow_unity8-dash");
1032- verify(dashWindow);
1033-
1034- // some sanity check
1035- compare(rectsIntersect(dashWindow, webbrowserWindow), false);
1036-
1037- tap(dashWindow);
1038- compare(dashWindow.application.session.surface.activeFocus, true);
1039-
1040- tap(webbrowserWindow);
1041- compare(webbrowserWindow.application.session.surface.activeFocus, true);
1042- }
1043-
1044- function test_tappingOnWindowTitleChangesFocusedApp() {
1045- ApplicationManager.startApplication("webbrowser-app");
1046- waitUntilAppSurfaceShowsUp("webbrowser-app");
1047-
1048- var webbrowserWindow = findChild(desktopStage, "decoratedWindow_webbrowser-app");
1049- verify(webbrowserWindow);
1050- var webbrowserWindowTitle = findChild(webbrowserWindow, "windowDecorationTitle");
1051- verify(webbrowserWindowTitle);
1052- var dashWindow = findChild(desktopStage, "decoratedWindow_unity8-dash");
1053- verify(dashWindow);
1054- var dashWindowTitle = findChild(dashWindow, "windowDecorationTitle");
1055- verify(dashWindowTitle);
1056-
1057- // some sanity check
1058- compare(rectsIntersect(dashWindow, webbrowserWindow), false);
1059-
1060- tap(dashWindowTitle);
1061- compare(dashWindow.application.session.surface.activeFocus, true);
1062-
1063- tap(webbrowserWindowTitle);
1064- compare(webbrowserWindow.application.session.surface.activeFocus, true);
1065+ function startApplication(appId) {
1066+ var app = ApplicationManager.findApplication(appId);
1067+ if (!app) {
1068+ app = ApplicationManager.startApplication(appId);
1069+ }
1070+ verify(app);
1071+ waitUntilAppSurfaceShowsUp(appId);
1072+ verify(app.session.surface);
1073+ return app;
1074+ }
1075+
1076+ function test_appFocusSwitch_data() {
1077+ return [
1078+ {tag: "dash", apps: [ "unity8-dash", "dialer-app", "camera-app" ], focusfrom: 0, focusTo: 1 },
1079+ {tag: "dash", apps: [ "unity8-dash", "dialer-app", "camera-app" ], focusfrom: 1, focusTo: 0 },
1080+ ]
1081+ }
1082+
1083+ function test_appFocusSwitch(data) {
1084+ var i;
1085+ for (i = 0; i < data.apps.length; i++) {
1086+ startApplication(data.apps[i]);
1087+ }
1088+
1089+ ApplicationManager.requestFocusApplication(data.apps[data.focusfrom]);
1090+ tryCompare(ApplicationManager.findApplication(data.apps[data.focusfrom]).session.surface, "activeFocus", true);
1091+
1092+ ApplicationManager.requestFocusApplication(data.apps[data.focusTo]);
1093+ tryCompare(ApplicationManager.findApplication(data.apps[data.focusTo]).session.surface, "activeFocus", true);
1094+ }
1095+
1096+ function test_tappingOnWindowChangesFocusedApp_data() {
1097+ return [
1098+ {tag: "dash", apps: [ "unity8-dash", "dialer-app", "camera-app" ], focusfrom: 0, focusTo: 1 },
1099+ {tag: "dash", apps: [ "unity8-dash", "dialer-app", "camera-app" ], focusfrom: 1, focusTo: 0 },
1100+ ]
1101+ }
1102+
1103+ function test_tappingOnWindowChangesFocusedApp(data) {
1104+ var i;
1105+ for (i = 0; i < data.apps.length; i++) {
1106+ startApplication(data.apps[i]);
1107+ }
1108+
1109+ var fromAppWindow = findChild(desktopStage, "appWindow_" + data.apps[data.focusfrom]);
1110+ verify(fromAppWindow);
1111+ tap(fromAppWindow);
1112+ compare(fromAppWindow.application.session.surface.activeFocus, true);
1113+
1114+ var toAppWindow = findChild(desktopStage, "appWindow_" + data.apps[data.focusTo]);
1115+ verify(toAppWindow);
1116+ tap(toAppWindow);
1117+ compare(toAppWindow.application.session.surface.activeFocus, true);
1118+ }
1119+
1120+ function test_tappingOnDecorationFocusesApplication_data() {
1121+ return [
1122+ {tag: "dash", apps: [ "unity8-dash", "dialer-app", "camera-app" ], focusfrom: 0, focusTo: 1 },
1123+ {tag: "dash", apps: [ "unity8-dash", "dialer-app", "camera-app" ], focusfrom: 1, focusTo: 0 },
1124+ ]
1125+ }
1126+
1127+ function test_tappingOnDecorationFocusesApplication(data) {
1128+ var i;
1129+ for (i = 0; i < data.apps.length; i++) {
1130+ startApplication(data.apps[i]);
1131+ }
1132+
1133+ var fromAppDecoration = findChild(desktopStage, "appWindowDecoration_" + data.apps[data.focusfrom]);
1134+ verify(fromAppDecoration);
1135+ tap(fromAppDecoration);
1136+ tryCompare(ApplicationManager.findApplication(data.apps[data.focusfrom]).session.surface, "activeFocus", true);
1137+
1138+ var toAppDecoration = findChild(desktopStage, "appWindowDecoration_" + data.apps[data.focusTo]);
1139+ verify(toAppDecoration);
1140+ tap(toAppDecoration);
1141+ tryCompare(ApplicationManager.findApplication(data.apps[data.focusTo]).session.surface, "activeFocus", true);
1142 }
1143 }
1144 }
1145
1146=== modified file 'tests/qmltests/tst_Shell.qml'
1147--- tests/qmltests/tst_Shell.qml 2015-04-03 14:23:02 +0000
1148+++ tests/qmltests/tst_Shell.qml 2015-05-06 11:43:33 +0000
1149@@ -34,6 +34,7 @@
1150 import Wizard 0.1 as Wizard
1151
1152 import "../../qml"
1153+import "Stages"
1154
1155 Rectangle {
1156 id: root
1157@@ -109,19 +110,25 @@
1158 }
1159 }
1160
1161- Rectangle {
1162+ Flickable {
1163 id: controls
1164- color: "darkgrey"
1165+ contentHeight: controlRect.height
1166+
1167+ anchors.top: root.top
1168+ anchors.bottom: root.bottom
1169+ anchors.right: root.right
1170 width: units.gu(30)
1171- anchors.top: root.top
1172- anchors.bottom: root.bottom
1173- anchors.right: root.right
1174-
1175- Column {
1176- anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
1177- spacing: units.gu(1)
1178- Row {
1179- anchors { left: parent.left; right: parent.right }
1180+
1181+ Rectangle {
1182+ id: controlRect
1183+ anchors { left: parent.left; right: parent.right }
1184+ color: "darkgrey"
1185+ height: childrenRect.height + units.gu(2)
1186+
1187+ Column {
1188+ anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
1189+ spacing: units.gu(1)
1190+
1191 Button {
1192 text: "Show Greeter"
1193 activeFocusOnPress: false
1194@@ -135,30 +142,54 @@
1195 }
1196 }
1197 }
1198- }
1199- ListItem.ItemSelector {
1200- anchors { left: parent.left; right: parent.right }
1201- activeFocusOnPress: false
1202- text: "LightDM mock mode"
1203- model: ["single", "single-passphrase", "single-pin", "full"]
1204- onSelectedIndexChanged: {
1205- shellLoader.active = false;
1206- LightDM.Greeter.mockMode = model[selectedIndex];
1207- LightDM.Users.mockMode = model[selectedIndex];
1208- shellLoader.active = true;
1209- }
1210- }
1211- ListItem.ItemSelector {
1212- anchors { left: parent.left; right: parent.right }
1213- activeFocusOnPress: false
1214- text: "Size"
1215- model: ["phone", "tablet"]
1216- onSelectedIndexChanged: {
1217- shellLoader.active = false;
1218- shellLoader.state = model[selectedIndex];
1219- shellLoader.active = true;
1220- }
1221- MouseTouchEmulationCheckbox { color: "white" }
1222+ ListItem.ItemSelector {
1223+ anchors { left: parent.left; right: parent.right }
1224+ activeFocusOnPress: false
1225+ text: "LightDM mock mode"
1226+ model: ["single", "single-passphrase", "single-pin", "full"]
1227+ onSelectedIndexChanged: {
1228+ shellLoader.active = false;
1229+ LightDM.Greeter.mockMode = model[selectedIndex];
1230+ LightDM.Users.mockMode = model[selectedIndex];
1231+ shellLoader.active = true;
1232+ }
1233+ }
1234+ ListItem.ItemSelector {
1235+ anchors { left: parent.left; right: parent.right }
1236+ activeFocusOnPress: false
1237+ text: "Size"
1238+ model: ["phone", "tablet"]
1239+ onSelectedIndexChanged: {
1240+ shellLoader.active = false;
1241+ shellLoader.state = model[selectedIndex];
1242+ shellLoader.active = true;
1243+ }
1244+ }
1245+ ListItem.ItemSelector {
1246+ id: usageModeSelector
1247+ anchors { left: parent.left; right: parent.right }
1248+ activeFocusOnPress: false
1249+ text: "Usage mode"
1250+ model: ["Staged", "Windowed"]
1251+ onSelectedIndexChanged: {
1252+ GSettingsController.setUsageMode(model[selectedIndex]);
1253+ }
1254+ }
1255+ MouseTouchEmulationCheckbox {
1256+ id: mouseEmulation
1257+ checked: true
1258+ color: "white"
1259+ }
1260+
1261+ Label { text: "Applications"; font.bold: true }
1262+
1263+ Repeater {
1264+ id: appRepeater
1265+ model: ApplicationManager.availableApplications
1266+ ApplicationCheckBox {
1267+ appId: modelData
1268+ }
1269+ }
1270 }
1271 }
1272 }
1273@@ -239,6 +270,7 @@
1274 function cleanup() {
1275 tryCompare(shell, "enabled", true); // make sure greeter didn't leave us in disabled state
1276 tearDown();
1277+ GSettingsController.setUsageMode("Staged");
1278 }
1279
1280 function loadShell(formFactor) {
1281@@ -685,22 +717,57 @@
1282
1283 function test_launchedAppHasActiveFocus_data() {
1284 return [
1285- {tag:"phone", formFactor:"phone"},
1286- {tag:"tablet", formFactor:"tablet"},
1287- ];
1288+ {tag: "phone", formFactor: "phone", usageMode: "Staged"},
1289+ {tag: "tablet", formFactor: "tablet", usageMode: "Staged"},
1290+ {tag: "desktop", formFactor: "tablet", usageMode: "Windowed"}
1291+ ]
1292 }
1293
1294 function test_launchedAppHasActiveFocus(data) {
1295+ GSettingsController.setUsageMode(data.usageMode);
1296 loadShell(data.formFactor);
1297 swipeAwayGreeter();
1298
1299- var dialerApp = ApplicationManager.startApplication("webbrowser-app");
1300- verify(dialerApp);
1301- waitUntilAppSurfaceShowsUp("webbrowser-app")
1302-
1303- verify(dialerApp.session.surface);
1304-
1305- tryCompare(dialerApp.session.surface, "activeFocus", true);
1306+ var webApp = ApplicationManager.startApplication("webbrowser-app");
1307+ verify(webApp);
1308+ waitUntilAppSurfaceShowsUp("webbrowser-app")
1309+
1310+ verify(webApp.session.surface);
1311+
1312+ tryCompare(webApp.session.surface, "activeFocus", true);
1313+ }
1314+
1315+ function test_launchedAppKeepsActiveFocusOnUsageModeChange() {
1316+ loadShell("tablet");
1317+ swipeAwayGreeter();
1318+
1319+ var webApp = ApplicationManager.startApplication("webbrowser-app");
1320+ verify(webApp);
1321+ waitUntilAppSurfaceShowsUp("webbrowser-app")
1322+
1323+ verify(webApp.session.surface);
1324+
1325+ tryCompare(webApp.session.surface, "activeFocus", true);
1326+
1327+ GSettingsController.setUsageMode("Windowed");
1328+
1329+ // check that the desktop stage and window have been loaded
1330+ {
1331+ var desktopWindow = findChild(shell, "decoratedWindow_webbrowser-app");
1332+ verify(desktopWindow);
1333+ }
1334+
1335+ tryCompare(webApp.session.surface, "activeFocus", true);
1336+
1337+ GSettingsController.setUsageMode("Staged");
1338+
1339+ // check that the tablet stage and app surface delegate have been loaded
1340+ {
1341+ var desktopWindow = findChild(shell, "tabletSpreadDelegate_webbrowser-app");
1342+ verify(desktopWindow);
1343+ }
1344+
1345+ tryCompare(webApp.session.surface, "activeFocus", true);
1346 }
1347
1348 function waitUntilAppSurfaceShowsUp(appId) {
1349@@ -1126,8 +1193,7 @@
1350 loadShell("phone");
1351 swipeAwayGreeter();
1352 AccountsService.backgroundFile = data.accounts;
1353- var backgroundSettings = findInvisibleChild(shell, "backgroundSettings");
1354- backgroundSettings.pictureUri = data.gsettings;
1355+ GSettingsController.setPictureUri(data.gsettings);
1356
1357 if (data.output === "defaultBackground") {
1358 tryCompare(shell, "background", shell.defaultBackground);
1359@@ -1184,5 +1250,36 @@
1360 tryCompare(passwordInput, "focus", true)
1361 }
1362 }
1363+
1364+ function test_stageLoader_data() {
1365+ return [
1366+ {tag: "phone", source: "Stages/PhoneStage.qml", formFactor: "phone", usageMode: "Staged"},
1367+ {tag: "tablet", source: "Stages/TabletStage.qml", formFactor: "tablet", usageMode: "Staged"},
1368+ {tag: "desktop", source: "Stages/DesktopStage.qml", formFactor: "tablet", usageMode: "Windowed"}
1369+ ]
1370+ }
1371+
1372+ function test_stageLoader(data) {
1373+ GSettingsController.setUsageMode(data.usageMode);
1374+ loadShell(data.formFactor);
1375+ var stageLoader = findChild(shell, "applicationsDisplayLoader");
1376+ verify(String(stageLoader.source).indexOf(data.source) >= 0);
1377+ }
1378+
1379+ function test_launcherInverted_data() {
1380+ return [
1381+ {tag: "phone", formFactor: "phone", usageMode: "Staged", launcherInverted: true},
1382+ {tag: "tablet", formFactor: "tablet", usageMode: "Staged", launcherInverted: true},
1383+ {tag: "desktop", formFactor: "tablet", usageMode: "Windowed", launcherInverted: false}
1384+ ]
1385+ }
1386+
1387+ function test_launcherInverted(data) {
1388+ GSettingsController.setUsageMode(data.usageMode);
1389+ loadShell(data.formFactor);
1390+
1391+ var launcher = findChild(shell, "launcher");
1392+ compare(launcher.inverted, data.launcherInverted);
1393+ }
1394 }
1395 }
1396
1397=== modified file 'tests/utils/modules/Unity/Test/MouseTouchEmulationCheckbox.qml'
1398--- tests/utils/modules/Unity/Test/MouseTouchEmulationCheckbox.qml 2015-03-12 15:03:01 +0000
1399+++ tests/utils/modules/Unity/Test/MouseTouchEmulationCheckbox.qml 2015-05-06 11:43:33 +0000
1400@@ -22,6 +22,7 @@
1401 RowLayout {
1402 id: root
1403 property alias color: label.color
1404+ property alias checked: checkbox.checked
1405
1406 Binding {
1407 target: MouseTouchAdaptor

Subscribers

People subscribed via source and target branches