on xenial help sign in
Merge from trunk
commit54de2db06e5b7eb864fb5db13bd4aca6fc97a3ca authorMichael Terry <michael.terry@canonical.com> dateFri 20 Nov 2015, 08:46 (11 years ago) parent8e8ec8eb03c9e3
89 files changed+2260−1442
data/CMakeLists.txt+6 data/unity8-dash.desktop.in+1−1 data/unity8-dash.png data/unity8-dash.svg+326 debian/changelog+32 plugins/AccountsService/AccountsService.cpp+3−3 plugins/AccountsService/AccountsServiceDBusAdaptor.cpp+1−22 plugins/AccountsService/AccountsServiceDBusAdaptor.h−12 plugins/Cursor/MousePointer.cpp+1 plugins/Dash/horizontaljournal.cpp+3−1 plugins/Dash/organicgrid.cpp+3−1 plugins/Dash/verticaljournal.cpp+3−1 plugins/Greeter/Unity/Launcher/launchermodelas.cpp+19−4 plugins/Greeter/Unity/Launcher/launchermodelas.h+2 plugins/Unity/InputInfo/CMakeLists.txt+1−1 plugins/Unity/InputInfo/linux/qinputdeviceinfo_linux.cpp+131−83 plugins/Unity/InputInfo/linux/qinputdeviceinfo_linux_p.h+13−6 plugins/Unity/InputInfo/plugin.cpp+2−2 plugins/Unity/InputInfo/qdeclarativeinputdeviceinfo.cpp−143 tests/mocks/Unity/InputInfo/mockqdeclarativeinputdeviceinfo.cpp → plugins/Unity/InputInfo/qdeclarativeinputdevicemodel.cpp+77−54 plugins/Unity/InputInfo/qdeclarativeinputdeviceinfo_p.h → plugins/Unity/InputInfo/qdeclarativeinputdevicemodel_p.h+27−11 plugins/Unity/InputInfo/qinputinfo.cpp+129−18 plugins/Unity/InputInfo/qinputinfo.h+56−56 plugins/Unity/Session/dbusunitysessionservice.cpp+64−36 plugins/Utils/CMakeLists.txt+2 plugins/Utils/applicationsfiltermodel.cpp+101 plugins/Utils/applicationsfiltermodel.h+69 plugins/Utils/plugin.cpp+2 po/am.po+4−4 po/unity8.pot+67−138 qml/Components/Dialogs.qml+29−2 qml/Components/ModeSwitchWarningDialog.qml+95 qml/Components/PanelState/PanelState.qml+2 qml/Components/ShellDialog.qml+1−1 qml/Components/UnityInputInfo/UnityInputInfo.qml−93 qml/Components/UnityInputInfo/qmldir−1 qml/Dash/CardTool.qml+8−8 qml/Dash/graphics/phone/screenshots/libreoffice@12.png qml/OrientedShell.qml+16−7 qml/Panel/Panel.qml+24−1 qml/Stages/DecoratedWindow.qml+3−1 qml/Stages/DesktopSpread.qml+1−1 qml/Stages/DesktopSpreadDelegate.qml+2−2 qml/Stages/DesktopStage.qml+165−39 qml/Stages/WindowDecoration.qml+8−3 qml/Stages/WindowResizeArea.qml+7−4 src/CachingNetworkManagerFactory.cpp+1 tests/mocks/Unity/Application/ApplicationManager.cpp+1 tests/mocks/Unity/CMakeLists.txt+1−1 tests/mocks/Unity/InputInfo/CMakeLists.txt+7−2 tests/mocks/Unity/InputInfo/mockcontroller.cpp+35 tests/mocks/Unity/InputInfo/mockcontroller.h+34 tests/mocks/Unity/InputInfo/mockqdeclarativeinputdeviceinfo_p.h−94 tests/mocks/Unity/InputInfo/mockqinputinfo.cpp−124 tests/mocks/Unity/InputInfo/mockqinputinfo.h−139 tests/mocks/Unity/InputInfo/plugin.cpp+17−3 tests/mocks/Unity/InputInfo/qinputdeviceinfo_mock.cpp+52 tests/mocks/Unity/InputInfo/qinputdeviceinfo_mock_p.h+58 tests/mocks/Unity/Launcher/MockLauncherModel.cpp+3 tests/mocks/Unity/fake_categories.cpp−5 tests/mocks/Unity/fake_categories.h−1 tests/mocks/Unity/fake_scope.cpp+5 tests/mocks/Unity/fake_scope.h+2 tests/mocks/Unity/fake_scopesoverview.cpp−10 tests/mocks/Unity/fake_scopesoverview.h−2 tests/mocks/Utils/CMakeLists.txt+2 tests/mocks/Utils/plugin.cpp+2 tests/plugins/AccountsService/AccountsServer.cpp+11−5 tests/plugins/AccountsService/AccountsServer.h+1−6 tests/plugins/AccountsService/PropertiesServer.cpp+9 tests/plugins/AccountsService/client.cpp+6−8 tests/plugins/AccountsService/server.cpp+1−7 tests/plugins/Greeter/Unity/Launcher/AccountsServiceDBusAdaptor.cpp−48 tests/plugins/Greeter/Unity/Launcher/AccountsServiceDBusAdaptor.h−53 tests/plugins/Greeter/Unity/Launcher/CMakeLists.txt+8−2 tests/plugins/Greeter/Unity/Launcher/launchermodelastest.cpp+81−16 tests/plugins/Unity/Launcher/AccountsServiceDBusAdaptor.cpp−42 tests/plugins/Unity/Launcher/AccountsServiceDBusAdaptor.h−47 tests/plugins/Unity/Launcher/CMakeLists.txt+8−2 tests/plugins/Unity/Launcher/launchermodeltest.cpp+23−1 tests/qmltests/CMakeLists.txt+1 tests/qmltests/Components/tst_ModeSwitchWarningDialog.qml+75 tests/qmltests/Dash/tst_CardTool.qml+6−6 tests/qmltests/Notifications/tst_Notifications.qml+5−6 tests/qmltests/Notifications/tst_SwipeToAct.qml+5−6 tests/qmltests/Stages/tst_DesktopStage.qml+139−32 tests/qmltests/Stages/tst_WindowResizeArea.qml+43 tests/qmltests/tst_OrientedShell.qml+19−10 tests/qmltests/tst_Shell.qml+93−4
data/CMakeLists.txt+6
@@ -11,6 +11,12 @@ install(FILES
1111 DESTINATION ${CMAKE_INSTALL_DATADIR}/applications
1212 )
1313
14+# install dash icon
15+install(FILES
16+ ${CMAKE_CURRENT_SOURCE_DIR}/${DASH_APP}.png
17+ DESTINATION ${SHELL_APP_DIR}/Dash/
18+ )
19+
1420add_custom_target(pkgversion ALL COMMAND dpkg-parsechangelog -l${CMAKE_SOURCE_DIR}/debian/changelog --show-field version > ${CMAKE_CURRENT_BINARY_DIR}/version)
1521
1622install(FILES com.canonical.Unity8.gschema.xml DESTINATION ${CMAKE_INSTALL_DATADIR}/glib-2.0/schemas)
data/unity8-dash.desktop.in+1−1
@@ -4,7 +4,7 @@ Name=Scopes
44Comment=An interface for using the Scopes system
55Exec=@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_BINDIR@/unity8-dash
66Terminal=false
7−Icon=
7+Icon=@CMAKE_INSTALL_PREFIX@/@SHELL_APP_DIR@/Dash/unity8-dash.png
88NoDisplay=true
99X-Ubuntu-Touch=true
1010X-Ubuntu-Supported-Orientations=primary
data/unity8-dash.pngnew file

Binary file not shown.

data/unity8-dash.svgnew file+326
@@ -0,0 +1,326 @@
1+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2+<!-- Created with Inkscape (http://www.inkscape.org/) -->
3+
4+<svg
5+ xmlns:dc="http://purl.org/dc/elements/1.1/"
6+ xmlns:cc="http://creativecommons.org/ns#"
7+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
8+ xmlns:svg="http://www.w3.org/2000/svg"
9+ xmlns="http://www.w3.org/2000/svg"
10+ xmlns:xlink="http://www.w3.org/1999/xlink"
11+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
12+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
13+ viewBox="0 0 512 512"
14+ style="enable-background:new"
15+ inkscape:export-ydpi="180"
16+ inkscape:export-xdpi="180"
17+ inkscape:export-filename="app_shape02.png"
18+ sodipodi:docname="apps-scope.svg"
19+ inkscape:version="0.91+devel r"
20+ version="1.1"
21+ id="svg3336"
22+ height="512"
23+ width="512">
24+ <sodipodi:namedview
25+ inkscape:snap-global="false"
26+ fit-margin-bottom="18"
27+ fit-margin-right="0"
28+ fit-margin-left="0"
29+ fit-margin-top="18"
30+ inkscape:snap-grids="true"
31+ inkscape:guide-bbox="true"
32+ showguides="false"
33+ inkscape:snap-center="true"
34+ inkscape:snap-object-midpoints="true"
35+ inkscape:snap-midpoints="true"
36+ inkscape:snap-smooth-nodes="true"
37+ inkscape:snap-intersection-paths="true"
38+ inkscape:object-nodes="true"
39+ inkscape:object-paths="true"
40+ inkscape:snap-bbox-midpoints="true"
41+ inkscape:snap-bbox-edge-midpoints="true"
42+ inkscape:bbox-nodes="true"
43+ inkscape:bbox-paths="true"
44+ inkscape:snap-bbox="true"
45+ showborder="true"
46+ inkscape:showpageshadow="false"
47+ inkscape:snap-page="true"
48+ inkscape:window-maximized="1"
49+ inkscape:window-y="24"
50+ inkscape:window-x="0"
51+ inkscape:window-height="1029"
52+ inkscape:window-width="1920"
53+ showgrid="false"
54+ inkscape:current-layer="layer3"
55+ inkscape:document-units="px"
56+ inkscape:cy="204.04409"
57+ inkscape:cx="-384.5809"
58+ inkscape:zoom="0.64"
59+ inkscape:pageshadow="2"
60+ inkscape:pageopacity="0"
61+ borderopacity="1.0"
62+ bordercolor="#666666"
63+ pagecolor="#4f4f4f"
64+ id="base">
65+ <inkscape:grid
66+ type="xygrid"
67+ id="grid811"
68+ originx="0"
69+ originy="15.999999"
70+ visible="true"
71+ enabled="true" />
72+ <sodipodi:guide
73+ orientation="1,0"
74+ position="104,176"
75+ id="guide819" />
76+ <sodipodi:guide
77+ orientation="1,0"
78+ position="120,160"
79+ id="guide821" />
80+ <sodipodi:guide
81+ orientation="1,0"
82+ position="392,144"
83+ id="guide823" />
84+ <sodipodi:guide
85+ orientation="1,0"
86+ position="408,144"
87+ id="guide825" />
88+ <sodipodi:guide
89+ orientation="0,1"
90+ position="328,408"
91+ id="guide827" />
92+ <sodipodi:guide
93+ orientation="0,1"
94+ position="336,392"
95+ id="guide829" />
96+ <sodipodi:guide
97+ orientation="0,1"
98+ position="176,120"
99+ id="guide831" />
100+ <sodipodi:guide
101+ orientation="0,1"
102+ position="216,104"
103+ id="guide833" />
104+ <inkscape:grid
105+ type="axonomgrid"
106+ id="grid835"
107+ units="px"
108+ empspacing="60"
109+ gridanglex="42.9"
110+ gridanglez="42.9"
111+ spacingy="3.522px"
112+ enabled="false"
113+ originx="0"
114+ originy="16.999996" />
115+ <sodipodi:guide
116+ orientation="0,1"
117+ position="344,376"
118+ id="guide4126" />
119+ <sodipodi:guide
120+ orientation="0,1"
121+ position="160,136"
122+ id="guide4128" />
123+ <sodipodi:guide
124+ orientation="1,0"
125+ position="136,96"
126+ id="guide4130" />
127+ <sodipodi:guide
128+ orientation="1,0"
129+ position="376,136"
130+ id="guide4134" />
131+ <sodipodi:guide
132+ orientation="-0.70710678,0.70710678"
133+ position="544,544"
134+ id="guide4136" />
135+ <sodipodi:guide
136+ orientation="1,0"
137+ position="256,-32.000001"
138+ id="guide4138" />
139+ <sodipodi:guide
140+ orientation="0,1"
141+ position="544,256"
142+ id="guide4140" />
143+ <sodipodi:guide
144+ orientation="0.70710678,0.70710678"
145+ position="544,-32.000001"
146+ id="guide4142" />
147+ <sodipodi:guide
148+ orientation="0,1"
149+ position="248,15.999999"
150+ id="guide4152" />
151+ <sodipodi:guide
152+ orientation="0,1"
153+ position="336,496"
154+ id="guide4154" />
155+ <sodipodi:guide
156+ position="344,96"
157+ orientation="1,0"
158+ id="guide4361" />
159+ <sodipodi:guide
160+ position="576,168"
161+ orientation="0,1"
162+ id="guide4363" />
163+ <sodipodi:guide
164+ position="168,192"
165+ orientation="1,0"
166+ id="guide4229" />
167+ <sodipodi:guide
168+ position="312,344"
169+ orientation="0,1"
170+ id="guide4233" />
171+ <sodipodi:guide
172+ position="180.48,-96"
173+ orientation="0,1"
174+ id="guide4235" />
175+ </sodipodi:namedview>
176+ <defs
177+ id="defs3338">
178+ <linearGradient
179+ inkscape:collect="always"
180+ id="linearGradient4203">
181+ <stop
182+ style="stop-color:#ea5420;stop-opacity:1"
183+ offset="0"
184+ id="stop4205" />
185+ <stop
186+ style="stop-color:#f37e40;stop-opacity:1"
187+ offset="1"
188+ id="stop4207" />
189+ </linearGradient>
190+ <clipPath
191+ clipPathUnits="userSpaceOnUse"
192+ id="clipPath4755">
193+ <path
194+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.24899998;fill:#5e2c73;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
195+ d="M 360,50 594,284 V 215.89844 C 594,70.73665 573.2426,50 427.94531,50 Z"
196+ id="path4757"
197+ inkscape:connector-curvature="0" />
198+ </clipPath>
199+ <clipPath
200+ clipPathUnits="userSpaceOnUse"
201+ id="clipPath4767">
202+ <path
203+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#f6f6f6;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
204+ d="M 543.8125,454.1875 312.18945,685.81055 367.94531,681 C 512.70468,668.50896 526.55393,654.72881 539,510.10156 Z"
205+ id="path4769"
206+ inkscape:connector-curvature="0" />
207+ </clipPath>
208+ <linearGradient
209+ inkscape:collect="always"
210+ xlink:href="#linearGradient4203"
211+ id="linearGradient4209"
212+ x1="167.5"
213+ y1="17.25"
214+ x2="343.25"
215+ y2="491"
216+ gradientUnits="userSpaceOnUse" />
217+ <filter
218+ inkscape:collect="always"
219+ style="color-interpolation-filters:sRGB"
220+ id="filter4220"
221+ x="-0.012"
222+ width="1.024"
223+ y="-0.012"
224+ height="1.024">
225+ <feGaussianBlur
226+ inkscape:collect="always"
227+ stdDeviation="1.4390401"
228+ id="feGaussianBlur4222" />
229+ </filter>
230+ <linearGradient
231+ inkscape:collect="always"
232+ xlink:href="#linearGradient4226"
233+ id="linearGradient4232"
234+ x1="255.99998"
235+ y1="-255.99998"
236+ x2="405.35773"
237+ y2="-255.99998"
238+ gradientUnits="userSpaceOnUse"
239+ spreadMethod="reflect" />
240+ <linearGradient
241+ inkscape:collect="always"
242+ id="linearGradient4226">
243+ <stop
244+ style="stop-color:#a73812;stop-opacity:1"
245+ offset="0"
246+ id="stop4228" />
247+ <stop
248+ style="stop-color:#973310;stop-opacity:0"
249+ offset="1"
250+ id="stop4230" />
251+ </linearGradient>
252+ <filter
253+ inkscape:collect="always"
254+ style="color-interpolation-filters:sRGB"
255+ id="filter4234"
256+ x="-0.096000001"
257+ width="1.192"
258+ y="-0.096000001"
259+ height="1.192">
260+ <feGaussianBlur
261+ inkscape:collect="always"
262+ stdDeviation="11.512321"
263+ id="feGaussianBlur4236" />
264+ </filter>
265+ </defs>
266+ <metadata
267+ id="metadata3341">
268+ <rdf:RDF>
269+ <cc:Work
270+ rdf:about="">
271+ <dc:format>image/svg+xml</dc:format>
272+ <dc:type
273+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
274+ <dc:title></dc:title>
275+ </cc:Work>
276+ </rdf:RDF>
277+ </metadata>
278+ <g
279+ inkscape:groupmode="layer"
280+ id="layer3"
281+ inkscape:label="Background">
282+ <path
283+ style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:url(#linearGradient4209);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;enable-background:accumulate"
284+ d="M 0 0 L 0 183.89844 L 0 328.10156 L 0 512 L 512 512 L 512 328.10156 L 512 183.89844 L 512 0 L 0 0 z "
285+ id="rect4158" />
286+ <path
287+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
288+ d="M 0 256 L 0 328.10156 L 0 462.53516 C 0 479.69677 0.052846476 496.03174 0.13085938 512 L 511.86914 512 C 511.94715 496.03174 512 479.69677 512 462.53516 L 512 256 L 0 256 z "
289+ id="rect4286" />
290+ </g>
291+ <g
292+ inkscape:groupmode="layer"
293+ id="layer1"
294+ inkscape:label="Pictogram"
295+ style="display:inline"
296+ transform="translate(-32,-32)">
297+ <circle
298+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:0.4;fill:#d04616;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:7.99999952;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;filter:url(#filter4220);enable-background:accumulate"
299+ id="circle4218"
300+ cx="258"
301+ cy="-256"
302+ r="143.90401"
303+ transform="rotate(90,-7.1132638e-7,31.999996)" />
304+ <circle
305+ transform="rotate(90,-7.1132638e-7,31.999996)"
306+ r="143.90401"
307+ cy="-256"
308+ cx="258"
309+ id="circle4224"
310+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:url(#linearGradient4232);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:7.99999952;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;filter:url(#filter4234);enable-background:accumulate" />
311+ <circle
312+ transform="rotate(90)"
313+ r="143.90401"
314+ cy="-288"
315+ cx="288"
316+ id="circle4206"
317+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:7.99999952;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate" />
318+ <path
319+ style="display:inline;fill:#ea5420;fill-opacity:1;stroke-width:2.9398191;enable-background:new"
320+ id="path4258"
321+ d="m 193.9963,269.54024 c -10.19878,0 -18.46137,8.2634 -18.46137,18.46139 0,10.19309 8.26259,18.45568 18.46137,18.45568 10.19311,0 18.45569,-8.26259 18.45569,-18.45568 0,-10.19799 -8.26258,-18.46139 -18.45569,-18.46139 z m 131.78525,83.88906 c -8.82832,5.09898 -11.85536,16.38129 -6.75557,25.20475 5.0933,8.82834 16.37561,11.85455 25.20393,6.75556 8.82752,-5.0933 11.85457,-16.37561 6.75559,-25.20475 -5.0933,-8.82263 -16.38212,-11.84887 -25.20395,-6.75556 z m -91.69319,-65.42767 c 0,-18.24028 9.05999,-34.35498 22.92309,-44.11321 l -13.49406,-22.6012 c -16.14883,10.79461 -28.16435,27.28971 -33.15766,46.60863 5.82648,4.75434 9.55584,11.99194 9.55584,20.10497 0,8.10813 -3.72936,15.34572 -9.55584,20.10006 4.98681,19.32463 17.00232,35.82055 33.15766,46.61514 l 13.49406,-22.60689 c -13.8631,-9.75253 -22.92309,-25.86641 -22.92309,-44.1075 z m 53.91207,-53.91776 c 28.16514,0 51.27274,21.59491 53.69828,49.13495 l 26.30048,-0.38772 c -1.29405,-20.33173 -10.17604,-38.58421 -23.82372,-52.00594 -7.01814,2.65149 -15.12464,2.2459 -22.13139,-1.79314 -7.01894,-4.0504 -11.41969,-10.88319 -12.62271,-18.30044 -6.8214,-1.88254 -14.00046,-2.91323 -21.42257,-2.91323 -12.75925,0 -24.82271,2.99695 -35.53359,8.29836 l 12.82509,22.98243 c 6.89944,-3.21075 14.5963,-5.01526 22.71013,-5.01526 z m 0,107.82982 c -8.11383,0 -15.81069,-1.80533 -22.70931,-5.01528 l -12.82511,22.98163 c 10.71088,5.3079 22.77434,8.30568 35.53442,8.30568 7.42211,0 14.60118,-1.0315 21.42177,-2.91975 1.20301,-7.41724 5.60537,-14.24352 12.6227,-18.30125 7.01161,-4.04555 15.11326,-4.44382 22.13137,-1.79232 13.64769,-13.42174 22.52968,-31.67421 23.82372,-52.00595 l -26.30696,-0.38772 c -2.41986,27.54738 -25.52665,49.13496 -53.6926,49.13496 z m 37.77461,-119.35113 c 8.82834,5.09818 20.11145,2.07845 25.20475,-6.74987 5.099,-8.82833 2.07928,-20.11145 -6.74988,-25.21126 -8.82832,-5.0933 -20.11063,-2.06705 -25.21125,6.76127 -5.09247,8.82345 -2.06625,20.10657 6.75638,25.19986 z"
322+ inkscape:connector-curvature="0"
323+ inkscape:transform-center-x="16.232534"
324+ inkscape:transform-center-y="-0.0024900663" />
325+ </g>
326+</svg>
debian/changelog+32
@@ -1,3 +1,35 @@
1+unity8 (8.11+16.04.20151112.1-0ubuntu1) xenial; urgency=medium
2+
3+ [ Albert Astals Cid ]
4+ * Make cardWidth and cardHeight real
5+ * Reset instead of qFatal when removing things from the middle (LP:
6+ #1238979)
7+ * Warn we're using only the cache when not connected to the interwebs
8+
9+ [ Andrea Cimitan ]
10+ * Add sharing widget to zoomable image and video playback
11+
12+ [ Daniel d'Andrada ]
13+ * Cursor: properly initialize hotspot position (LP: #1510407)
14+ * Update GSettings mock in tst_OrientedShell
15+
16+ [ Lukáš Tinkl ]
17+ * Restore windows when activating from the spread, maintain a focus
18+ stack
19+
20+ [ Michael Terry ]
21+ * Make a few DBus calls asynchronous, for a smoother UX.
22+
23+ [ Michael Zanetti ]
24+ * Add a warning dialog when disconnecting the external monitor.
25+ * added icon for the dash (LP: #1488146)
26+ * prevent windows to be moved under the panel (LP: #1438465)
27+ * update inputinfo api to the latest upstream snapshot
28+ * use UbuntuNumberAnimations instead of linear ones for window state
29+ transitions (LP: #1497097)
30+
31+ -- Michael Zanetti <michael.zanetti@canonical.com> Thu, 12 Nov 2015 20:41:02 +0000
32+
133unity8 (8.11+16.04.20151104-0ubuntu1) xenial; urgency=medium
234
335 [ Albert Astals Cid ]
plugins/AccountsService/AccountsService.cpp+3−3
@@ -76,7 +76,7 @@ void AccountsService::setDemoEdges(bool demoEdges)
7676{
7777 if (m_demoEdges != demoEdges) {
7878 m_demoEdges = demoEdges;
79− m_service->setUserProperty(m_user, QStringLiteral("com.canonical.unity.AccountsService"), QStringLiteral("demo-edges"), demoEdges);
79+ m_service->setUserPropertyAsync(m_user, QStringLiteral("com.canonical.unity.AccountsService"), QStringLiteral("demo-edges"), demoEdges);
8080
8181 Q_EMIT demoEdgesChanged();
8282 }
@@ -131,7 +131,7 @@ void AccountsService::setHereEnabled(bool enabled)
131131{
132132 if (m_hereEnabled != enabled) {
133133 m_hereEnabled = enabled;
134− m_service->setUserProperty(m_user, QStringLiteral("com.ubuntu.location.providers.here.AccountsService"), QStringLiteral("LicenseAccepted"), enabled);
134+ m_service->setUserPropertyAsync(m_user, QStringLiteral("com.ubuntu.location.providers.here.AccountsService"), QStringLiteral("LicenseAccepted"), enabled);
135135
136136 Q_EMIT hereEnabledChanged();
137137 }
@@ -449,7 +449,7 @@ void AccountsService::setFailedLogins(uint failedLogins)
449449{
450450 if (m_failedLogins != failedLogins) {
451451 m_failedLogins = failedLogins;
452− m_service->setUserProperty(m_user, QStringLiteral("com.canonical.unity.AccountsService.Private"), QStringLiteral("FailedLogins"), failedLogins);
452+ m_service->setUserPropertyAsync(m_user, QStringLiteral("com.canonical.unity.AccountsService.Private"), QStringLiteral("FailedLogins"), failedLogins);
453453
454454 Q_EMIT failedLoginsChanged();
455455 }
plugins/AccountsService/AccountsServiceDBusAdaptor.cpp+1−22
@@ -37,18 +37,6 @@ AccountsServiceDBusAdaptor::AccountsServiceDBusAdaptor(QObject* parent)
3737 connection, this);
3838}
3939
40−QVariant AccountsServiceDBusAdaptor::getUserProperty(const QString &user, const QString &interface, const QString &property)
41−{
42− QDBusInterface *iface = getUserInterface(user);
43− if (iface != nullptr && iface->isValid()) {
44− QDBusReply<QVariant> answer = iface->call(QStringLiteral("Get"), interface, property);
45− if (answer.isValid()) {
46− return answer;
47− }
48− }
49− return QVariant();
50−}
51−
5240QDBusPendingReply<QVariant> AccountsServiceDBusAdaptor::getUserPropertyAsync(const QString &user, const QString &interface, const QString &property)
5341{
5442 QDBusInterface *iface = getUserInterface(user);
@@ -58,15 +46,6 @@ QDBusPendingReply<QVariant> AccountsServiceDBusAdaptor::getUserPropertyAsync(con
5846 return QDBusPendingReply<QVariant>(QDBusMessage::createError(QDBusError::Other, QStringLiteral("Invalid Interface")));
5947}
6048
61−void AccountsServiceDBusAdaptor::setUserProperty(const QString &user, const QString &interface, const QString &property, const QVariant &value)
62−{
63− QDBusInterface *iface = getUserInterface(user);
64− if (iface != nullptr && iface->isValid()) {
65− // The value needs to be carefully wrapped
66− iface->call(QStringLiteral("Set"), interface, property, QVariant::fromValue(QDBusVariant(value)));
67− }
68−}
69−
7049QDBusPendingCall AccountsServiceDBusAdaptor::setUserPropertyAsync(const QString &user, const QString &interface, const QString &property, const QVariant &value)
7150{
7251 QDBusInterface *iface = getUserInterface(user);
@@ -116,7 +95,7 @@ QDBusInterface *AccountsServiceDBusAdaptor::getUserInterface(const QString &user
11695{
11796 QDBusInterface *iface = m_users.value(user);
11897 if (iface == nullptr && m_accountsManager->isValid()) {
119− QDBusReply<QDBusObjectPath> answer = m_accountsManager->asyncCall(QStringLiteral("FindUserByName"), user);
98+ QDBusReply<QDBusObjectPath> answer = m_accountsManager->call(QStringLiteral("FindUserByName"), user);
12099 if (answer.isValid()) {
121100 const QString path = answer.value().path();
122101
plugins/AccountsService/AccountsServiceDBusAdaptor.h−12
@@ -35,19 +35,7 @@ public:
3535 explicit AccountsServiceDBusAdaptor(QObject *parent = 0);
3636 ~AccountsServiceDBusAdaptor() = default;
3737
38− Q_INVOKABLE QVariant getUserProperty(const QString &user, const QString &interface, const QString &property);
3938 Q_INVOKABLE QDBusPendingReply<QVariant> getUserPropertyAsync(const QString &user, const QString &interface, const QString &property);
40−
41− template <typename T>
42− inline T getUserProperty(const QString &user, const QString &interface, const QString &property) {
43− const QVariant variant = getUserProperty(user, interface, property);
44− if (variant.isValid() && variant.canConvert<QDBusArgument>()) {
45− return qdbus_cast<T>(variant.value<QDBusArgument>());
46− }
47− return T();
48− }
49−
50− Q_INVOKABLE void setUserProperty(const QString &user, const QString &interface, const QString &property, const QVariant &value);
5139 Q_INVOKABLE QDBusPendingCall setUserPropertyAsync(const QString &user, const QString &interface, const QString &property, const QVariant &value);
5240
5341Q_SIGNALS:
plugins/Cursor/MousePointer.cpp+1
@@ -32,6 +32,7 @@ MousePointer::MousePointer(QQuickItem *parent)
3232 , m_hotspotX(0)
3333 , m_hotspotY(0)
3434{
35+ updateHotspot();
3536}
3637
3738void MousePointer::handleMouseEvent(ulong timestamp, QPointF movement, Qt::MouseButtons buttons,
plugins/Dash/horizontaljournal.cpp+3−1
@@ -195,7 +195,9 @@ void HorizontalJournal::processModelRemoves(const QVector<QQmlChangeSet::Change>
195195 m_lastInRowIndexPosition.remove(indexToRemove);
196196 } else {
197197 if (indexToRemove < lastIndex) {
198− qFatal("HorizontalJournal only supports removal from the end of the model");
198+ qDebug() << "HorizontalJournal only supports removal from the end of the model, resetting instead";
199+ cleanupExistingItems();
200+ break;
199201 } else {
200202 setImplicitHeightDirty();
201203 }
plugins/Dash/organicgrid.cpp+3−1
@@ -246,7 +246,9 @@ void OrganicGrid::processModelRemoves(const QVector<QQmlChangeSet::Change> &remo
246246 releaseItem(m_visibleItems.takeLast());
247247 } else {
248248 if (indexToRemove < lastIndex) {
249− qFatal("OrganicGrid only supports removal from the end of the model");
249+ qDebug() << "OrganicGrid only supports removal from the end of the model, resetting instead";
250+ cleanupExistingItems();
251+ break;
250252 }
251253 }
252254 }
plugins/Dash/verticaljournal.cpp+3−1
@@ -261,7 +261,9 @@ void VerticalJournal::processModelRemoves(const QVector<QQmlChangeSet::Change> &
261261 }
262262 if (!found) {
263263 if (indexToRemove < lastCreatedIndex) {
264− qFatal("VerticalJournal only supports removal from the end of the model");
264+ qDebug() << "VerticalJournal only supports removal from the end of the model, resetting instead";
265+ cleanupExistingItems();
266+ break;
265267 } else {
266268 setImplicitHeightDirty();
267269 }
plugins/Greeter/Unity/Launcher/launchermodelas.cpp+19−4
@@ -194,15 +194,30 @@ int LauncherModel::findApplication(const QString &appId)
194194 return -1;
195195}
196196
197−typedef QList<QVariantMap> VariantMapList;
198197void LauncherModel::refresh()
199198{
200− QList<QVariantMap> items;
199+ if (!m_accounts || m_user.isEmpty()) {
200+ refreshWithItems(QList<QVariantMap>());
201+ } else {
202+ QDBusPendingCall pendingCall = m_accounts->getUserPropertyAsync(m_user, QStringLiteral("com.canonical.unity.AccountsService"), QStringLiteral("LauncherItems"));
203+ QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall, this);
204+ connect(watcher, &QDBusPendingCallWatcher::finished,
205+ this, [this](QDBusPendingCallWatcher* watcher) {
206+
207+ QDBusPendingReply<QVariant> reply = *watcher;
208+ watcher->deleteLater();
209+ if (reply.isError()) {
210+ qWarning() << "Failed to refresh LauncherItems" << reply.error().message();
211+ return;
212+ }
201213
202− if (m_accounts && !m_user.isEmpty()) {
203− items = m_accounts->getUserProperty<VariantMapList>(m_user, QStringLiteral("com.canonical.unity.AccountsService"), QStringLiteral("LauncherItems"));
214+ refreshWithItems(qdbus_cast<QList<QVariantMap>>(reply.value().value<QDBusArgument>()));
215+ });
204216 }
217+}
205218
219+void LauncherModel::refreshWithItems(const QList<QVariantMap> &items)
220+{
206221 // First walk through all the existing items and see if we need to remove something
207222 QList<LauncherItem*> toBeRemoved;
208223
plugins/Greeter/Unity/Launcher/launchermodelas.h+2
@@ -65,6 +65,8 @@ private Q_SLOTS:
6565 void propertiesChanged(const QString &user, const QString &interface, const QStringList &changed);
6666
6767private:
68+ void refreshWithItems(const QList<QVariantMap> &items);
69+
6870 QString m_user;
6971 QList<LauncherItem*> m_list;
7072 AccountsServiceDBusAdaptor *m_accounts;
plugins/Unity/InputInfo/CMakeLists.txt+1−1
@@ -21,7 +21,7 @@ include_directories(
2121set(InputInfo_SOURCES
2222 plugin.cpp
2323 qinputinfo.cpp
24− qdeclarativeinputdeviceinfo.cpp
24+ qdeclarativeinputdevicemodel.cpp
2525 linux/qinputdeviceinfo_linux.cpp
2626)
2727
plugins/Unity/InputInfo/linux/qinputdeviceinfo_linux.cpp+131−83
@@ -47,39 +47,46 @@
4747#include <QDebug>
4848#include <QSocketNotifier>
4949#include <QTimer>
50+#include <QDir>
5051
51−QInputDeviceInfoPrivate::QInputDeviceInfoPrivate(QObject *parent) :
52− QObject(parent)
53− , udev(0)
52+QInputDeviceManagerPrivate::QInputDeviceManagerPrivate(QObject *parent) :
53+ QObject(parent),
54+ currentFilter(QInputDevice::Unknown),
55+ udevice(0)
5456{
55− QTimer::singleShot(250, this, &QInputDeviceInfoPrivate::init);
57+ QTimer::singleShot(250,this,SLOT(init()));
5658}
5759
58−void QInputDeviceInfoPrivate::init()
60+QInputDeviceManagerPrivate::~QInputDeviceManagerPrivate()
5961{
60− if (!udev)
61− udev = udev_new();
62+ udev_unref(udevice);
63+ udev_monitor_unref(udevMonitor);
64+}
65+
66+void QInputDeviceManagerPrivate::init()
67+{
68+ if (!udevice)
69+ udevice = udev_new();
6270
63− struct udev_list_entry *devices, *dev_list_entry;
64− struct udev_device *dev;
71+ udev_list_entry *devices;
72+ udev_list_entry *dev_list_entry;
73+ udev_device *dev;
6574
6675 QString subsystem = QStringLiteral("input");
6776 struct udev_enumerate *enumerate = 0;
6877
69− if (udev) {
78+ if (udevice) {
7079
71− udevMonitor = udev_monitor_new_from_netlink(udev, "udev");
80+ udevMonitor = udev_monitor_new_from_netlink(udevice, "udev");
7281 udev_monitor_filter_add_match_subsystem_devtype(udevMonitor, subsystem.toLatin1(), NULL);
73− enumerate = udev_enumerate_new(udev);
82+ enumerate = udev_enumerate_new(udevice);
7483 udev_enumerate_add_match_subsystem(enumerate, subsystem.toLatin1());
7584
76−
7785 udev_monitor_enable_receiving(udevMonitor);
7886 notifierFd = udev_monitor_get_fd(udevMonitor);
7987
8088 notifier = new QSocketNotifier(notifierFd, QSocketNotifier::Read, this);
81− connect(notifier, &QSocketNotifier::activated, this, &QInputDeviceInfoPrivate::onUDevChanges);
82−
89+ connect(notifier, SIGNAL(activated(int)), this, SLOT(onUDevChanges()));
8390
8491 udev_enumerate_scan_devices(enumerate);
8592 devices = udev_enumerate_get_list_entry(enumerate);
@@ -88,78 +95,80 @@ void QInputDeviceInfoPrivate::init()
8895 const char *path;
8996 path = udev_list_entry_get_name(dev_list_entry);
9097
91− dev = udev_device_new_from_syspath(udev, path);
92−
93− QString eventPath = QString::fromLatin1(udev_device_get_sysname(dev));
94−
98+ dev = udev_device_new_from_syspath(udevice, path);
9599 if (qstrcmp(udev_device_get_subsystem(dev), "input") == 0 ) {
96−
97− if (eventPath.contains(QStringLiteral("event"))) {
98− eventPath.prepend(QStringLiteral("/dev/input/"));
99−
100− QInputDevice *iDevice = addDevice(eventPath);
101− if (!iDevice)
102− continue;
103−
104− iDevice->setTypes(getInputTypes(dev));
105−
106− if (iDevice->switches().count() > 0 && iDevice->buttons().count() == 0)
107− iDevice->setTypes(iDevice->types() | QInputDeviceInfo::Switch);
108−
109− if (iDevice->buttons().count() > 0 && iDevice->types() == QInputDeviceInfo::Unknown)
110− iDevice->setTypes(iDevice->types() | QInputDeviceInfo::Button);
111−
112− deviceList.append(iDevice);
113− deviceMap.insert(eventPath,iDevice);
114− Q_EMIT newDevice(eventPath);
115−
100+ QInputDevice *iDevice = addDevice(dev);
101+ if (iDevice && !iDevice->devicePath().isEmpty()) {
102+ deviceMap.insert(iDevice->devicePath(),iDevice);
116103 }
117104 }
105+ udev_device_unref(dev);
118106 }
119107 udev_enumerate_unref(enumerate);
120108 }
109+ // udev_unref(udevice);
110+ Q_FOREACH (const QString &devicePath, deviceMap.keys()) {
111+ Q_EMIT deviceAdded(devicePath);
112+ }
121113 Q_EMIT ready();
122114}
123115
124−QInputDeviceInfo::InputTypes QInputDeviceInfoPrivate::getInputTypes( struct udev_device *dev)
116+QInputDevice::InputTypeFlags QInputDeviceManagerPrivate::getInputTypeFlags(struct udev_device *dev)
125117{
126− QInputDeviceInfo::InputTypes types = QInputDeviceInfo::Unknown;
127− if (qstrcmp(udev_device_get_property_value(dev, "ID_INPUT_KEYBOARD"), "1") == 0 )
128− types |= QInputDeviceInfo::Keyboard;
129−
130− if (qstrcmp(udev_device_get_property_value(dev, "ID_INPUT_MOUSE"), "1") == 0)
131− types |= QInputDeviceInfo::Mouse;
132−
133− if (qstrcmp(udev_device_get_property_value(dev, "ID_INPUT_TOUCHPAD"), "1") == 0)
134− types |= QInputDeviceInfo::TouchPad;
135−
118+ QInputDevice::InputTypeFlags flags = QInputDevice::Unknown;
119+ if (qstrcmp(udev_device_get_property_value(dev, "ID_INPUT_KEY"), "1") == 0 ) {
120+ flags |= QInputDevice::Button;
121+ }
122+ if (qstrcmp(udev_device_get_property_value(dev, "ID_INPUT_MOUSE"), "1") == 0) {
123+ flags |= QInputDevice::Mouse;
124+ }
125+ if (qstrcmp(udev_device_get_property_value(dev, "ID_INPUT_TOUCHPAD"), "1") == 0) {
126+ flags |= QInputDevice::TouchPad;
127+ }
136128 if (qstrcmp(udev_device_get_property_value(dev, "ID_INPUT_TOUCHSCREEN"), "1") == 0
137− || qstrcmp(udev_device_get_property_value(dev, "ID_INPUT_TABLET"), "1") == 0)
138− types |= QInputDeviceInfo::TouchScreen;
129+ || qstrcmp(udev_device_get_property_value(dev, "ID_INPUT_TABLET"), "1") == 0) {
130+ flags |= QInputDevice::TouchScreen;
131+ }
132+ if (qstrcmp(udev_device_get_property_value(dev, "ID_INPUT_KEYBOARD"), "1") == 0 ) {
133+ flags |= QInputDevice::Keyboard;
134+ }
135+ if (!QString::fromLatin1(udev_device_get_property_value(dev, "SW")).isEmpty()) {
136+ flags |= QInputDevice::Switch;
137+ }
139138
140− return types;
139+ return flags;
141140}
142141
143−QInputDevice *QInputDeviceInfoPrivate::addDevice(const QString &path)
142+QInputDevice *QInputDeviceManagerPrivate::addDevice(struct udev_device *udev)
144143{
145− QInputDevice *inputDevice = new QInputDevice(this);
146− inputDevice->setDevicePath(path);
144+ QString eventPath = QString::fromLatin1(udev_device_get_sysname(udev));
147145
146+ if (eventPath.contains(QStringLiteral("event")))
147+ eventPath.prepend(QStringLiteral("/dev/input/"));
148+
149+ if (deviceMap.contains(eventPath)) {
150+ return Q_NULLPTR;
151+ }
148152 struct libevdev *dev = NULL;
149153 int fd;
150154 int rc = 1;
151− fd = open(path.toLatin1(), O_RDONLY|O_NONBLOCK);
155+ QInputDevice *inputDevice;
156+ inputDevice = addUdevDevice(udev);
157+ if (!inputDevice) {
158+ return Q_NULLPTR;
159+ }
160+ eventPath = inputDevice->devicePath();
152161
162+ fd = open(eventPath.toLatin1(), O_RDONLY|O_NONBLOCK);
153163 if (fd == -1) {
154164 return inputDevice;
155165 }
156166 rc = libevdev_new_from_fd(fd, &dev);
157167 if (rc < 0) {
158168 qWarning() << "Failed to init libevdev ("<< strerror(-rc) << ")";
159− return inputDevice;
169+ return Q_NULLPTR;
160170 }
161171
162− inputDevice->setName(QString::fromLatin1(libevdev_get_name(dev)));
163172 for (int i = 0; i < EV_MAX; i++) {
164173 if (i == EV_KEY || i == EV_SW || i == EV_REL
165174 || i == EV_REL || i == EV_ABS) {
@@ -183,54 +192,93 @@ QInputDevice *QInputDeviceInfoPrivate::addDevice(const QString &path)
183192 }
184193 }
185194 }
195+
186196 return inputDevice;
187197}
188198
189−void QInputDeviceInfoPrivate::removeDevice(const QString &path)
199+void QInputDeviceManagerPrivate::addDetails(struct udev_device *)
200+{
201+}
202+
203+void QInputDeviceManagerPrivate::removeDevice(const QString &path)
190204{
191− for (int i = 0; i < deviceList.size(); ++i) {
192− if (deviceList.at(i)->devicePath() == path) {
193− delete deviceList.takeAt(i);
194− deviceMap.remove(path);
195− Q_EMIT deviceRemoved(path);
205+ // this path is not a full evdev path
206+ Q_FOREACH (const QString devicePath, deviceMap.keys()) {
207+ if (devicePath.contains(path)) {
208+ deviceMap.remove(devicePath);
209+ Q_EMIT deviceRemoved(devicePath);
196210 }
197211 }
198212}
199213
200−void QInputDeviceInfoPrivate::onUDevChanges()
214+QInputDevice *QInputDeviceManagerPrivate::addUdevDevice(struct udev_device *udev)
201215{
202− struct udev_device *dev = udev_monitor_receive_device(udevMonitor);
216+ QInputDevice *iDevice;
217+
218+ struct udev_list_entry *list;
219+ struct udev_list_entry *node;
220+
221+ list = udev_device_get_properties_list_entry (udev);
222+ QString syspath = QString::fromLatin1(udev_device_get_syspath(udev));
223+ QDir sysdir(syspath);
224+
225+ QStringList infoList = sysdir.entryList(QStringList() << QStringLiteral("event*"),QDir::Dirs);
226+
227+ if (infoList.count() > 0) {
228+ QString token = infoList.at(0);
229+
230+ token.prepend(QStringLiteral("/dev/input/"));
231+ iDevice = new QInputDevice(this);
232+ iDevice->setDevicePath(token);
233+ } else {
234+ return Q_NULLPTR;
235+ }
236+ udev_list_entry_foreach (node, list) {
237+
238+ QString key = QString::fromLatin1(udev_list_entry_get_name(node));
239+ QString value = QString::fromLatin1(udev_list_entry_get_value(node));
240+
241+ if (key == QStringLiteral("NAME")) {
242+ iDevice->setName(value.remove(QStringLiteral("\"")));
243+ }
244+ }
245+ iDevice->setType(getInputTypeFlags(udev));
246+ return iDevice;
247+}
248+
249+void QInputDeviceManagerPrivate::onUDevChanges()
250+{
251+ if (!udevMonitor)
252+ return;
253+
254+ udev_device *dev = udev_monitor_receive_device(udevMonitor);
255+
203256 if (dev) {
204257 if (qstrcmp(udev_device_get_subsystem(dev), "input") == 0 ) {
205258 QString eventPath = QString::fromLatin1(udev_device_get_sysname(dev));
206259
207− if (eventPath.contains(QStringLiteral("input")))
208− return;
209−
210260 QString action = QString::fromStdString(udev_device_get_action(dev));
211261
212262 if (!eventPath.contains(QStringLiteral("/dev/input/")))
213263 eventPath.prepend(QStringLiteral("/dev/input/"));
214264
215265 if (action == QStringLiteral("add")) {
216−
217− QInputDevice *iDevice = addDevice(eventPath);
218− if (!iDevice)
219− return;
220−
221− iDevice->setTypes(getInputTypes(dev));
266+ if (deviceMap.contains(eventPath)){
222267 udev_device_unref(dev);
268+ return;
269+ }
223270
224− if (iDevice->switches().count() > 0 && iDevice->buttons().count() == 0)
225− iDevice->setTypes(iDevice->types() | QInputDeviceInfo::Switch);
226−
227− if (iDevice->buttons().count() > 0 && iDevice->types() == QInputDeviceInfo::Unknown)
228− iDevice->setTypes(iDevice->types() | QInputDeviceInfo::Button);
271+ QInputDevice *iDevice = addDevice(dev);
272+ if (!iDevice) {
273+ delete iDevice;
274+ return;
275+ }
276+ iDevice->setType(getInputTypeFlags(dev));
277+ udev_device_unref(dev);
229278
230− deviceList.append(iDevice);
231279 deviceMap.insert(eventPath,iDevice);
232280
233− Q_EMIT newDevice(eventPath);
281+ Q_EMIT deviceAdded(eventPath);
234282
235283 } else if (action == QStringLiteral("remove")) {
236284 removeDevice(eventPath);
plugins/Unity/InputInfo/linux/qinputdeviceinfo_linux_p.h+13−6
@@ -58,30 +58,37 @@ public:
5858 QList <int> switches;
5959 QList <int> relativeAxis;
6060 QList <int> absoluteAxis;
61− QInputDeviceInfo::InputTypes types;
61+ QInputDevice::InputTypeFlags type;
6262};
6363
64−class QInputDeviceInfoPrivate : public QObject
64+class QInputDeviceManagerPrivate : public QObject
6565{
6666 Q_OBJECT
6767public:
68− explicit QInputDeviceInfoPrivate(QObject *parent = 0);
68+ explicit QInputDeviceManagerPrivate(QObject *parent = 0);
69+ ~QInputDeviceManagerPrivate();
6970 QVector <QInputDevice *> deviceList;
7071 QMap <QString, QInputDevice *> deviceMap;
72+ static QInputDeviceManagerPrivate * instance();
73+ QInputDevice::InputType currentFilter;
7174
7275Q_SIGNALS:
73− void newDevice(const QString &);
76+ void deviceAdded(const QString &);
7477 void deviceRemoved(const QString &);
7578 void ready();
7679
7780private:
78− struct udev *udev;
81+ QInputDevice *addDevice(struct udev_device *udev);
82+ QInputDevice *addUdevDevice(struct udev_device *);
83+
7984 QInputDevice *addDevice(const QString &path);
8085 void removeDevice(const QString &path);
8186 QSocketNotifier *notifier;
8287 int notifierFd;
8388 struct udev_monitor *udevMonitor;
84− QInputDeviceInfo::InputTypes getInputTypes( struct udev_device *);
89+ QInputDevice::InputTypeFlags getInputTypeFlags(struct udev_device *);
90+ struct udev *udevice;
91+ void addDetails(struct udev_device *);
8592
8693private Q_SLOTS:
8794 void onUDevChanges();
plugins/Unity/InputInfo/plugin.cpp+2−2
@@ -21,12 +21,12 @@
2121#include "plugin.h"
2222
2323// local
24−#include "qdeclarativeinputdeviceinfo_p.h"
24+#include "qdeclarativeinputdevicemodel_p.h"
2525
2626void InputInfoPlugin::registerTypes(const char *uri)
2727{
2828 int major = 0;
2929 int minor = 1;
30− qmlRegisterType<QDeclarativeInputDeviceInfo>(uri, major, minor, "InputDeviceInfo");
30+ qmlRegisterType<QDeclarativeInputDeviceModel>(uri, major, minor, "InputDeviceModel");
3131 qmlRegisterType<QInputDevice>(uri, major, minor, "InputInfo");
3232}
plugins/Unity/InputInfo/qdeclarativeinputdeviceinfo.cppdeleted−143
@@ -1,143 +0,0 @@
1−/****************************************************************************
2−**
3−** Copyright (C) 2015 Jolla.
4−** Contact: http://www.qt-project.org/legal
5−**
6−** This file is part of the QtSystems module of the Qt Toolkit.
7−**
8−** $QT_BEGIN_LICENSE:LGPL$
9−** Commercial License Usage
10−** Licensees holding valid commercial Qt licenses may use this file in
11−** accordance with the commercial license agreement provided with the
12−** Software or, alternatively, in accordance with the terms contained in
13−** a written agreement between you and Digia. For licensing terms and
14−** conditions see http://qt.digia.com/licensing. For further information
15−** use the contact form at http://qt.digia.com/contact-us.
16−**
17−** GNU Lesser General Public License Usage
18−** Alternatively, this file may be used under the terms of the GNU Lesser
19−** General Public License version 2.1 as published by the Free Software
20−** Foundation and appearing in the file LICENSE.LGPL included in the
21−** packaging of this file. Please review the following information to
22−** ensure the GNU Lesser General Public License version 2.1 requirements
23−** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24−**
25−** In addition, as a special exception, Digia gives you certain additional
26−** rights. These rights are described in the Digia Qt LGPL Exception
27−** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28−**
29−** GNU General Public License Usage
30−** Alternatively, this file may be used under the terms of the GNU
31−** General Public License version 3.0 as published by the Free Software
32−** Foundation and appearing in the file LICENSE.GPL included in the
33−** packaging of this file. Please review the following information to
34−** ensure the GNU General Public License version 3.0 requirements will be
35−** met: http://www.gnu.org/copyleft/gpl.html.
36−**
37−**
38−** $QT_END_LICENSE$
39−**
40−****************************************************************************/
41−#include "qdeclarativeinputdeviceinfo_p.h"
42−
43−QDeclarativeInputDeviceInfo::QDeclarativeInputDeviceInfo(QObject *parent) :
44− QAbstractListModel(parent),
45− deviceInfo(new QInputDeviceInfo)
46−{
47− connect(deviceInfo, &QInputDeviceInfo::ready, this, &QDeclarativeInputDeviceInfo::updateDeviceList);
48− connect(deviceInfo, &QInputDeviceInfo::deviceAdded,this,&QDeclarativeInputDeviceInfo::addedDevice);
49− connect(deviceInfo, &QInputDeviceInfo::deviceRemoved,this,&QDeclarativeInputDeviceInfo::removedDevice);
50−}
51−
52−QDeclarativeInputDeviceInfo::~QDeclarativeInputDeviceInfo()
53−{
54− delete deviceInfo;
55−}
56−
57−QVariant QDeclarativeInputDeviceInfo::data(const QModelIndex &index, int role) const
58−{
59− switch (role) {
60− case ServiceRole:
61− return QVariant::fromValue(static_cast<QObject *>(inputDevices.value(index.row())));
62− }
63−
64− return QVariant();
65−}
66−
67−int QDeclarativeInputDeviceInfo::rowCount(const QModelIndex &parent) const
68−{
69− Q_UNUSED(parent);
70−
71− return inputDevices.count();
72−}
73−
74−int QDeclarativeInputDeviceInfo::indexOf(const QString &devicePath) const
75−{
76− int idx(-1);
77− Q_FOREACH (QInputDevice *device, inputDevices) {
78− idx++;
79− if (device->devicePath() == devicePath) return idx;
80− }
81−
82− return -1;
83−}
84−
85−QInputDevice *QDeclarativeInputDeviceInfo::get(int index) const
86−{
87− if (index < 0 || index > inputDevices.count())
88− return 0;
89− return inputDevices.value(index);
90−}
91−
92−void QDeclarativeInputDeviceInfo::updateDeviceList()
93−{
94− QVector <QInputDevice *> newDevices = deviceInfo->deviceList();
95−
96− int numNew = newDevices.count();
97−
98− for (int i = 0; i < numNew; i++) {
99− int j = inputDevices.indexOf(newDevices.value(i));
100− if (j == -1) {
101− // not found -> remove from list
102− beginInsertRows(QModelIndex(), i, i);
103− inputDevices.insert(i, newDevices.value(i));
104− endInsertRows();
105− } else if (i != j) {
106− // changed its position -> move it
107− QInputDevice* device = inputDevices.value(j);
108− beginMoveRows(QModelIndex(), j, j, QModelIndex(), i);
109− inputDevices.remove(j);
110− inputDevices.insert(i, device);
111− endMoveRows();
112− } else {
113− QModelIndex changedIndex(this->index(j, 0, QModelIndex()));
114− Q_EMIT dataChanged(changedIndex, changedIndex);
115− }
116− }
117−
118− int numOld = inputDevices.count();
119− if (numOld > numNew) {
120− beginRemoveRows(QModelIndex(), numNew, numOld - 1);
121− inputDevices.remove(numNew, numOld - numNew);
122− endRemoveRows();
123− }
124−}
125−
126−void QDeclarativeInputDeviceInfo::addedDevice(const QString &devicePath)
127−{
128− updateDeviceList();
129− Q_EMIT newDevice(devicePath);
130−}
131−
132−void QDeclarativeInputDeviceInfo::removedDevice(const QString &devicePath)
133−{
134− updateDeviceList();
135− Q_EMIT deviceRemoved(devicePath);
136−}
137−
138−QHash<int, QByteArray> QDeclarativeInputDeviceInfo::roleNames() const
139−{
140− QHash<int, QByteArray> roles;
141− roles.insert(ServiceRole, "service");
142− return roles;
143−}
tests/mocks/Unity/InputInfo/mockqdeclarativeinputdeviceinfo.cpp → plugins/Unity/InputInfo/qdeclarativeinputdevicemodel.cpprenamed+77−54
@@ -1,7 +1,6 @@
11/****************************************************************************
22**
33** Copyright (C) 2015 Jolla.
4−** Copyright (C) 2015 Canonical Ltd.
54** Contact: http://www.qt-project.org/legal
65**
76** This file is part of the QtSystems module of the Qt Toolkit.
@@ -39,39 +38,64 @@
3938** $QT_END_LICENSE$
4039**
4140****************************************************************************/
42−#include "mockqdeclarativeinputdeviceinfo_p.h"
41+#include "qdeclarativeinputdevicemodel_p.h"
42+#include "qinputinfo.h"
4343
44−QDeclarativeInputDeviceInfo::QDeclarativeInputDeviceInfo(QObject *parent) :
44+QDeclarativeInputDeviceModel::QDeclarativeInputDeviceModel(QObject *parent) :
4545 QAbstractListModel(parent),
46− deviceInfo(new QInputDeviceInfo)
46+ deviceInfo(new QInputDeviceManager),
47+ currentFilter(QInputDevice::Unknown)
4748{
48− connect(deviceInfo, &QInputDeviceInfo::deviceAdded,this,&QDeclarativeInputDeviceInfo::addedDevice);
49− connect(deviceInfo, &QInputDeviceInfo::deviceRemoved,this,&QDeclarativeInputDeviceInfo::removedDevice);
49+ connect(deviceInfo,SIGNAL(ready()),this,SLOT(updateDeviceList()));
50+ connect(deviceInfo, &QInputDeviceManager::deviceAdded,this,&QDeclarativeInputDeviceModel::addedDevice);
51+ connect(deviceInfo, &QInputDeviceManager::deviceRemoved,this,&QDeclarativeInputDeviceModel::removedDevice);
5052}
5153
52−QDeclarativeInputDeviceInfo::~QDeclarativeInputDeviceInfo()
54+QDeclarativeInputDeviceModel::~QDeclarativeInputDeviceModel()
5355{
5456 delete deviceInfo;
5557}
5658
57−QVariant QDeclarativeInputDeviceInfo::data(const QModelIndex &index, int role) const
59+QVariant QDeclarativeInputDeviceModel::data(const QModelIndex &index, int role) const
5860{
5961 switch (role) {
6062 case ServiceRole:
6163 return QVariant::fromValue(static_cast<QObject *>(inputDevices.value(index.row())));
62− }
64+ break;
65+ case NameRole:
66+ return QVariant::fromValue(static_cast<QString>(inputDevices.value(index.row())->name()));
67+ break;
68+ case DevicePathRole:
69+ return QVariant::fromValue(static_cast<QString>(inputDevices.value(index.row())->devicePath()));
70+ break;
71+ case ButtonsRole:
72+ return QVariant::fromValue(static_cast<QList <int> >(inputDevices.value(index.row())->buttons()));
73+ break;
74+ case SwitchesRole:
75+ return QVariant::fromValue(static_cast<QList <int> >(inputDevices.value(index.row())->switches()));
76+ break;
77+ case RelativeAxisRole:
78+ return QVariant::fromValue(static_cast<QList <int> >(inputDevices.value(index.row())->relativeAxis()));
79+ break;
80+ case AbsoluteAxisRole:
81+ return QVariant::fromValue(static_cast<QList <int> >(inputDevices.value(index.row())->absoluteAxis()));
82+ break;
83+ case TypesRole:
84+ return QVariant::fromValue(static_cast<int>(inputDevices.value(index.row())->type()));
85+ break;
86+ };
6387
6488 return QVariant();
6589}
6690
67−int QDeclarativeInputDeviceInfo::rowCount(const QModelIndex &parent) const
91+int QDeclarativeInputDeviceModel::rowCount(const QModelIndex &parent) const
6892{
6993 Q_UNUSED(parent);
7094
7195 return inputDevices.count();
7296}
7397
74−int QDeclarativeInputDeviceInfo::indexOf(const QString &devicePath) const
98+int QDeclarativeInputDeviceModel::indexOf(const QString &devicePath) const
7599{
76100 int idx(-1);
77101 Q_FOREACH (QInputDevice *device, inputDevices) {
@@ -82,56 +106,27 @@ int QDeclarativeInputDeviceInfo::indexOf(const QString &devicePath) const
82106 return -1;
83107}
84108
85−QInputDevice *QDeclarativeInputDeviceInfo::get(int index) const
109+QInputDevice *QDeclarativeInputDeviceModel::get(int index) const
86110{
87111 if (index < 0 || index > inputDevices.count())
88112 return 0;
89113 return inputDevices.value(index);
90114}
91115
92−void QDeclarativeInputDeviceInfo::addMockMouse()
93−{
94− deviceInfo->addMockDevice(QInputDeviceInfo::Mouse);
95−}
96−
97−void QDeclarativeInputDeviceInfo::addMockKeyboard()
98−{
99− deviceInfo->addMockDevice(QInputDeviceInfo::Keyboard);
100−}
101−
102−void QDeclarativeInputDeviceInfo::removeMockMouse()
116+void QDeclarativeInputDeviceModel::updateDeviceList()
103117{
104− for (int i = 0; i < inputDevices.count(); i++) {
105− if (inputDevices.at(i)->types().testFlag(QInputDeviceInfo::Mouse)) {
106− deviceInfo->removeMockDevice(i);
107− return;
108− }
109− }
110−}
111−
112−void QDeclarativeInputDeviceInfo::removeMockKeyboard()
113−{
114− for (int i = 0; i < inputDevices.count(); i++) {
115− if (inputDevices.at(i)->types().testFlag(QInputDeviceInfo::Keyboard)) {
116− deviceInfo->removeMockDevice(i);
117− return;
118− }
119− }
120−}
121−
122−void QDeclarativeInputDeviceInfo::updateDeviceList()
123−{
124− QVector <QInputDevice *> newDevices = deviceInfo->deviceList();
118+ QVector <QInputDevice *> newDevices = deviceInfo->deviceListOfType(currentFilter);
125119
126120 int numNew = newDevices.count();
127121
128122 for (int i = 0; i < numNew; i++) {
129123 int j = inputDevices.indexOf(newDevices.value(i));
124+
130125 if (j == -1) {
131− // not found -> remove from list
132126 beginInsertRows(QModelIndex(), i, i);
133127 inputDevices.insert(i, newDevices.value(i));
134128 endInsertRows();
129+ Q_EMIT countChanged();
135130 } else if (i != j) {
136131 // changed its position -> move it
137132 QInputDevice* device = inputDevices.value(j);
@@ -139,10 +134,10 @@ void QDeclarativeInputDeviceInfo::updateDeviceList()
139134 inputDevices.remove(j);
140135 inputDevices.insert(i, device);
141136 endMoveRows();
142− } else {
143− QModelIndex changedIndex(this->index(j, 0, QModelIndex()));
144− Q_EMIT dataChanged(changedIndex, changedIndex);
145− }
137+ Q_EMIT countChanged();
138+ } //else {
139+ QModelIndex changedIndex(this->index(j, 0, QModelIndex()));
140+ Q_EMIT dataChanged(changedIndex, changedIndex);
146141 }
147142
148143 int numOld = inputDevices.count();
@@ -150,24 +145,52 @@ void QDeclarativeInputDeviceInfo::updateDeviceList()
150145 beginRemoveRows(QModelIndex(), numNew, numOld - 1);
151146 inputDevices.remove(numNew, numOld - numNew);
152147 endRemoveRows();
148+ Q_EMIT countChanged();
153149 }
154150}
155151
156−void QDeclarativeInputDeviceInfo::addedDevice(const QString &devicePath)
152+void QDeclarativeInputDeviceModel::addedDevice(const QString &devicePath)
157153{
158154 updateDeviceList();
159− Q_EMIT newDevice(devicePath);
155+ Q_EMIT deviceAdded(devicePath);
160156}
161157
162−void QDeclarativeInputDeviceInfo::removedDevice(const QString &devicePath)
158+void QDeclarativeInputDeviceModel::removedDevice(const QString &devicePath)
163159{
164160 updateDeviceList();
165161 Q_EMIT deviceRemoved(devicePath);
166162}
167163
168−QHash<int, QByteArray> QDeclarativeInputDeviceInfo::roleNames() const
164+QHash<int,QByteArray> QDeclarativeInputDeviceModel::roleNames() const
169165{
170166 QHash<int, QByteArray> roles;
171− roles.insert(ServiceRole, "service");
167+ roles[NameRole] = "name";
168+ roles[DevicePathRole] = "devicePath";
169+ roles[ButtonsRole] = "buttons";
170+ roles[SwitchesRole] = "switches";
171+ roles[RelativeAxisRole] = "rAxis";
172+ roles[AbsoluteAxisRole] = "aAxis";
173+ roles[TypesRole] = "types";
172174 return roles;
173175}
176+
177+/*
178+ * Returns the currently set device filter.
179+ * */
180+QInputDevice::InputType QDeclarativeInputDeviceModel::deviceFilter()
181+{
182+ return currentFilter;
183+}
184+
185+/*
186+ * Sets the current input device filter to filter.
187+ * */
188+void QDeclarativeInputDeviceModel::setDeviceFilter(QInputDevice::InputType filter)
189+{
190+ if (filter != currentFilter) {
191+ deviceInfo->setDeviceFilter(filter);
192+ currentFilter = filter;
193+ updateDeviceList();
194+ Q_EMIT deviceFilterChanged(filter);
195+ }
196+}
plugins/Unity/InputInfo/qdeclarativeinputdeviceinfo_p.h → plugins/Unity/InputInfo/qdeclarativeinputdevicemodel_p.hrenamed+27−11
@@ -39,48 +39,64 @@
3939**
4040****************************************************************************/
4141
42−#ifndef QDECLARATIVEINPUTDEVICEINFO_H
43−#define QDECLARATIVEINPUTDEVICEINFO_H
42+#ifndef QDECLARATIVEINPUTDEVICEMODEL_H
43+#define QDECLARATIVEINPUTDEVICEMODEL_H
4444
4545#include <QObject>
4646#include <QAbstractListModel>
4747#include "qinputinfo.h"
4848
49−class QDeclarativeInputDeviceInfo : public QAbstractListModel
49+class QDeclarativeInputDeviceModel : public QAbstractListModel
5050{
5151 Q_OBJECT
52− Q_DISABLE_COPY(QDeclarativeInputDeviceInfo)
52+ Q_DISABLE_COPY(QDeclarativeInputDeviceModel)
53+ Q_PROPERTY(QInputDevice::InputType deviceFilter READ deviceFilter WRITE setDeviceFilter NOTIFY deviceFilterChanged)
54+
55+ Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
5356
5457public:
5558 enum ItemRoles {
56− ServiceRole = Qt::UserRole + 1
59+ ServiceRole = Qt::UserRole + 1,
60+ NameRole,
61+ DevicePathRole,
62+ ButtonsRole,
63+ SwitchesRole,
64+ RelativeAxisRole,
65+ AbsoluteAxisRole,
66+ TypesRole
5767 };
5868
59− explicit QDeclarativeInputDeviceInfo(QObject *parent = 0);
60− virtual ~QDeclarativeInputDeviceInfo();
69+ explicit QDeclarativeInputDeviceModel(QObject *parent = 0);
70+ virtual ~QDeclarativeInputDeviceModel();
6171
6272 QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
6373 int rowCount(const QModelIndex &parent = QModelIndex()) const;
6474
65− QHash<int, QByteArray> roleNames() const;
75+ void setDeviceFilter(QInputDevice::InputType filter);
76+ QInputDevice::InputType deviceFilter();
6677
6778 Q_INVOKABLE int indexOf(const QString &devicePath) const;
6879
6980 Q_INVOKABLE QInputDevice *get(int index) const;
81+ QHash<int, QByteArray> roleNames() const;
7082
7183Q_SIGNALS:
72− void newDevice(const QString &devicePath);
84+ void deviceAdded(const QString &devicePath);
7385 void deviceRemoved(const QString &devicePath);
86+ void deviceFilterChanged(const QInputDevice::InputType filter);
87+ void countChanged();
7488
7589public Q_SLOTS:
7690 void updateDeviceList();
7791private:
78− QInputDeviceInfo *deviceInfo;
92+ QInputDeviceManager *deviceInfo;
7993 QVector<QInputDevice *> inputDevices;
94+ QInputDevice::InputType currentFilter;
95+
8096private Q_SLOTS:
8197 void addedDevice(const QString &);
8298 void removedDevice(const QString &path);
8399
84100};
85101
86−#endif // QDECLARATIVEINPUTDEVICEINFO_H
102+#endif // QDECLARATIVEINPUTDEVICEMODEL_H
plugins/Unity/InputInfo/qinputinfo.cpp+129−18
@@ -41,16 +41,25 @@
4141
4242#include "qinputinfo.h"
4343
44−#if defined(Q_OS_LINUX)
44+#if defined(UNITY_MOCKS)
45+#include "qinputdeviceinfo_mock_p.h"
46+#elif defined(Q_OS_LINUX)
4547#include "linux/qinputdeviceinfo_linux_p.h"
4648#endif
4749
50+Q_GLOBAL_STATIC(QInputDeviceManagerPrivate, inputDeviceManagerPrivate)
51+
4852QT_BEGIN_NAMESPACE
4953
54+QInputDeviceManagerPrivate * QInputDeviceManagerPrivate::instance()
55+{
56+ QInputDeviceManagerPrivate *priv = inputDeviceManagerPrivate();
57+ return priv;
58+}
5059
5160QInputDevicePrivate::QInputDevicePrivate(QObject *parent) :
5261 QObject(parent),
53− types(QInputDeviceInfo::Unknown)
62+ type(QInputDevice::Unknown)
5463{
5564}
5665
@@ -60,100 +69,202 @@ QInputDevice::QInputDevice(QObject *parent) :
6069{
6170}
6271
72+/*
73+ * Returns the name of this input device.
74+ */
6375QString QInputDevice::name() const
6476{
6577 return d_ptr->name;
6678}
6779
80+/*
81+ * Sets the name of this input device to \b name.
82+ */
6883void QInputDevice::setName(const QString &name)
6984{
7085 d_ptr->name = name;
7186}
7287
88+/*
89+ * Returns the device path of this device.
90+ */
7391QString QInputDevice::devicePath() const
7492{
7593 return d_ptr->devicePath;
7694}
7795
96+/*
97+ * Sets the device ppath of this device to /b path.
98+ */
7899void QInputDevice::setDevicePath(const QString &path)
79100{
80101 d_ptr->devicePath = path;
81102}
82103
104+/*
105+ * Returns the number of buttons this device has.
106+ */
83107QList <int> QInputDevice::buttons() const
84108{
85109 return d_ptr->buttons;
86110}
87111
112+/*
113+ * Adds a button
114+ */
88115void QInputDevice::addButton(int buttonCode)
89116{
90117 d_ptr->buttons.append(buttonCode);
91118}
92119
120+/*
121+ * Returns the number of switch of this device.
122+ */
93123QList <int> QInputDevice::switches() const
94124{
95125 return d_ptr->switches;
96126}
97127
128+/*
129+ * Adds a switch
130+ */
98131void QInputDevice::addSwitch(int switchCode)
99132{
100133 d_ptr->switches.append(switchCode);
101134}
102135
136+/*
137+ * Returns a list of the relative axis of this device
138+ */
103139QList <int> QInputDevice::relativeAxis() const
104140{
105141 return d_ptr->relativeAxis;
106142}
107143
144+/*
145+ */
108146void QInputDevice::addRelativeAxis(int axisCode)
109147{
110148 d_ptr->relativeAxis.append(axisCode);
111149}
112150
151+/*
152+ * Returns a list of the absolute axis of this device
153+ */
113154QList <int> QInputDevice::absoluteAxis() const
114155{
115156 return d_ptr->absoluteAxis;
116157}
117158
159+/*
160+ */
118161void QInputDevice::addAbsoluteAxis(int axisCode)
119162{
120163 d_ptr->absoluteAxis.append(axisCode);
121164}
122165
123−QInputDeviceInfo::InputTypes QInputDevice::types()
166+/*
167+ * Returns a QInputDevice::InputTypeFlags of all the types of types.
168+ */
169+QInputDevice::InputTypeFlags QInputDevice::type() const
124170{
125− return d_ptr->types;
171+ return d_ptr->type;
126172}
127173
128−void QInputDevice::setTypes(QInputDeviceInfo::InputTypes types)
174+/*
175+ */
176+void QInputDevice::setType(QInputDevice::InputTypeFlags type) //? setTypes?
129177{
130− d_ptr->types = types;
178+ d_ptr->type = type;
131179}
132180
133−
134−QInputDeviceInfo::QInputDeviceInfo(QObject *parent) :
181+QInputDeviceManager::QInputDeviceManager(QObject *parent) :
135182 QObject(parent),
136− d_ptr(new QInputDeviceInfoPrivate(this))
183+ d_ptr(inputDeviceManagerPrivate)
137184{
138− connect(d_ptr, &QInputDeviceInfoPrivate::newDevice,this,&QInputDeviceInfo::addedDevice);
139− connect(d_ptr, &QInputDeviceInfoPrivate::deviceRemoved,this,&QInputDeviceInfo::deviceRemoved);
185+ connect(d_ptr, &QInputDeviceManagerPrivate::deviceAdded,this,&QInputDeviceManager::addedDevice);
186+ connect(d_ptr, &QInputDeviceManagerPrivate::deviceRemoved,this,&QInputDeviceManager::deviceRemoved);
140187
141− connect(d_ptr, &QInputDeviceInfoPrivate::ready, this, &QInputDeviceInfo::ready);
188+ connect(d_ptr,SIGNAL(ready()),this,SIGNAL(ready()));
142189}
143190
144−QVector <QInputDevice *> QInputDeviceInfo::deviceList()
191+/*
192+ * Returns a QMap of known input devices.
193+ */
194+QMap <QString, QInputDevice *> QInputDeviceManager::deviceMap()
145195{
146− return d_ptr->deviceList;
196+ return d_ptr->deviceMap;
147197}
148198
149−QMap <QString, QInputDevice *> QInputDeviceInfo::deviceMap()
199+/*
200+ */
201+void QInputDeviceManager::addedDevice(const QString & devicePath)
150202{
151− return d_ptr->deviceMap;
203+ Q_EMIT deviceAdded(devicePath);
152204}
153205
154−void QInputDeviceInfo::addedDevice(const QString & devicePath)
206+/*
207+ * Returns a QVector of InputDevices of type filter
208+ * */
209+QVector <QInputDevice *> QInputDeviceManager::deviceListOfType(QInputDevice::InputType filter)
155210{
156− Q_EMIT deviceAdded(devicePath);
211+ QVector <QInputDevice *> dList;
212+ QMapIterator<QString, QInputDevice *> i(d_ptr->deviceMap);
213+ while (i.hasNext()) {
214+ i.next();
215+ if (i.value()->type().testFlag(filter) || filter == QInputDevice::Unknown) {
216+ dList.append(i.value());
217+ }
218+ }
219+ return dList;
220+}
221+
222+/*
223+ * Returns the number of input devices with the currently set QInputDevice::InputType filter.
224+ * If no device filter has been set, returns number of all available input devices.
225+ * If filter has not been set, returns all available input devices
226+ */
227+int QInputDeviceManager::deviceCount() const
228+{
229+ return deviceCount(static_cast< QInputDevice::InputType >(d_ptr->currentFilter));
230+}
231+
232+/*
233+ * Returns the number of input devices of the type filter.
234+ */
235+int QInputDeviceManager::deviceCount(const QInputDevice::InputType filter) const
236+{
237+ int dList = 0;
238+ QMapIterator<QString, QInputDevice *> i(d_ptr->deviceMap);
239+ while (i.hasNext()) {
240+ i.next();
241+// qDebug() << i.value()->name() << i.value()->devicePath();
242+// qDebug() << i.value()->type() << i.value()->type().testFlag(filter);
243+
244+ if (i.value()->type().testFlag(filter)) {
245+ dList++;
246+ }
247+ }
248+ return dList;
249+}
250+
251+/*
252+ * Returns the currently set device filter.
253+ * */
254+QInputDevice::InputType QInputDeviceManager::deviceFilter()
255+{
256+ return d_ptr->currentFilter;
257+}
258+
259+/*
260+ * Sets the current input device filter to filter.
261+ * */
262+void QInputDeviceManager::setDeviceFilter(QInputDevice::InputType filter)
263+{
264+ if (filter != d_ptr->currentFilter) {
265+ d_ptr->currentFilter = filter;
266+ Q_EMIT deviceFilterChanged(filter);
267+ }
157268}
158269
159270QT_END_NAMESPACE
plugins/Unity/InputInfo/qinputinfo.h+56−56
@@ -48,15 +48,19 @@
4848#include <QSocketNotifier>
4949#include <QDebug>
5050
51−class QInputDeviceInfoPrivate;
51+class QInputDeviceManagerPrivate;
5252class QInputDevicePrivate;
5353class QInputDevice;
5454
55−class QInputDeviceInfoPrivate;
56−class QInputDeviceInfo : public QObject
55+class QInputDeviceManager;
56+
57+class QInputDevice : public QObject
5758{
5859 Q_OBJECT
59− Q_PROPERTY(int deviceCount READ deviceCount)
60+ Q_ENUMS(InputType)
61+ Q_FLAGS(InputType InputTypeFlags)
62+ friend class QInputDeviceManagerPrivate;
63+
6064public:
6165
6266 enum InputType {
@@ -69,67 +73,19 @@ public:
6973 Switch = 32
7074 };
7175 Q_ENUMS(InputType)
72− Q_FLAGS(InputTypes)
73− Q_DECLARE_FLAGS(InputTypes, InputType)
74−
75− explicit QInputDeviceInfo(QObject *parent = 0);
76−
77− Q_INVOKABLE QVector <QInputDevice *> deviceList();
78−
79− Q_INVOKABLE QMap <QString, QInputDevice *> deviceMap();
80− int deviceCount() { return deviceList().count(); }
81−Q_SIGNALS:
82−
83− void deviceAdded(const QString & devicePath);
84− void deviceRemoved(const QString & devicePath);
85−
86− void ready();
87−
88−public Q_SLOTS:
89− void addedDevice(const QString & devicePath);
90−
91−private:
92− Q_DISABLE_COPY(QInputDeviceInfo)
93−#if !defined(QT_SIMULATOR)
94− QInputDeviceInfoPrivate *const d_ptr;
95− Q_DECLARE_PRIVATE(QInputDeviceInfo)
96−#endif
97−};
98−
99−class QInputDevice : public QObject
100−{
101− friend class QInputDeviceInfoPrivate;
102− Q_OBJECT
103− Q_ENUMS(InputType)
104− Q_FLAGS(InputTypes)
105− Q_PROPERTY(QString name READ name NOTIFY nameChanged)
106− Q_PROPERTY(QString devicePath READ devicePath NOTIFY devicePathChanged)
107− Q_PROPERTY(QList <int> buttons READ buttons NOTIFY buttonsChanged)
108− Q_PROPERTY(QList <int> switches READ switches NOTIFY switchesChanged)
109− Q_PROPERTY(QList <int> relativeAxis READ relativeAxis NOTIFY relativeAxisChanged)
110− Q_PROPERTY(QList <int> absoluteAxis READ absoluteAxis NOTIFY absoluteAxisChanged)
111− Q_PROPERTY(QInputDeviceInfo::InputTypes types READ types NOTIFY typesChanged)
76+ Q_DECLARE_FLAGS(InputTypeFlags, InputType)
11277
113−public:
11478 explicit QInputDevice(QObject *parent = 0);
115−
11679 QString name() const;
11780 QString devicePath() const;
11881 QList <int> buttons() const; //keys event code
11982 QList <int> switches() const;
12083 QList <int> relativeAxis() const;
12184 QList <int> absoluteAxis() const;
85+ QInputDevice::InputTypeFlags type() const;
12286
123− QInputDeviceInfo::InputTypes types();
124−Q_SIGNALS:
125− void nameChanged();
126− void devicePathChanged();
127− void buttonsChanged();
128− void switchesChanged();
129− void relativeAxisChanged();
130− void absoluteAxisChanged();
131− void typesChanged();
13287private:
88+
13389 QInputDevicePrivate *d_ptr;
13490 void setName(const QString &);
13591 void setDevicePath(const QString &);
@@ -137,7 +93,51 @@ private:
13793 void addSwitch(int);
13894 void addRelativeAxis(int);
13995 void addAbsoluteAxis(int);
140− void setTypes(QInputDeviceInfo::InputTypes);
96+ void setType(QInputDevice::InputTypeFlags flags);
97+
98+};
99+
100+Q_DECLARE_METATYPE(QInputDevice::InputType)
101+Q_DECLARE_METATYPE(QInputDevice::InputTypeFlags)
102+
103+class QInputDeviceManagerPrivate;
104+
105+class QInputDeviceManager : public QObject
106+{
107+ Q_OBJECT
108+ Q_PROPERTY(int deviceCount READ deviceCount NOTIFY deviceCountChanged)
109+ Q_PROPERTY(QInputDevice::InputType deviceFilter READ deviceFilter WRITE setDeviceFilter NOTIFY deviceFilterChanged)
110+public:
111+
112+ explicit QInputDeviceManager(QObject *parent = 0);
113+
114+ int deviceCount() const;
115+ int deviceCount(const QInputDevice::InputType filter) const;
116+
117+ void setDeviceFilter(QInputDevice::InputType filter);
118+ QInputDevice::InputType deviceFilter();
119+
120+ QMap <QString, QInputDevice *> deviceMap();
121+ Q_INVOKABLE QVector <QInputDevice *> deviceListOfType(QInputDevice::InputType filter);
122+
123+Q_SIGNALS:
124+
125+ void deviceAdded(const QString & devicePath);
126+ void deviceRemoved(const QString & devicePath);
127+
128+ void ready();
129+ void deviceCountChanged(int count);
130+ void deviceFilterChanged(const QInputDevice::InputType filter);
131+
132+public Q_SLOTS:
133+ void addedDevice(const QString & devicePath);
134+
135+private:
136+ Q_DISABLE_COPY(QInputDeviceManager)
137+#if !defined(QT_SIMULATOR)
138+ QInputDeviceManagerPrivate *const d_ptr;
139+ Q_DECLARE_PRIVATE(QInputDeviceManager)
140+#endif
141141};
142142
143143#endif // QINPUTINFO_H
plugins/Unity/Session/dbusunitysessionservice.cpp+64−36
@@ -61,7 +61,7 @@ public:
6161 QStringLiteral("GetSessionByPID"));
6262 msg << (quint32) getpid();
6363
64− QDBusReply<QDBusObjectPath> reply = QDBusConnection::systemBus().asyncCall(msg);
64+ QDBusReply<QDBusObjectPath> reply = QDBusConnection::systemBus().call(msg);
6565 if (reply.isValid()) {
6666 logindSessionPath = reply.value().path();
6767
@@ -92,20 +92,28 @@ public:
9292 msg << "Unity"; // who
9393 msg << "Unity8 handles power events"; // why
9494 msg << "block"; // mode
95− QDBusReply<QDBusUnixFileDescriptor> desc = QDBusConnection::systemBus().asyncCall(msg);
9695
97− if (desc.isValid()) {
98− m_systemdInhibitFd = desc.value();
99− } else {
100− qWarning() << "failed to inhibit systemd powersave handling";
101− }
96+ QDBusPendingCall pendingCall = QDBusConnection::systemBus().asyncCall(msg);
97+ QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall, this);
98+ connect(watcher, &QDBusPendingCallWatcher::finished,
99+ this, [this](QDBusPendingCallWatcher* watcher) {
100+
101+ QDBusPendingReply<QDBusUnixFileDescriptor> reply = *watcher;
102+ watcher->deleteLater();
103+ if (reply.isError()) {
104+ qWarning() << "Failed to inhibit systemd powersave handling" << reply.error().message();
105+ return;
106+ }
107+
108+ m_systemdInhibitFd = reply.value();
109+ });
102110 }
103111
104112
105113 bool checkLogin1Call(const QString &method) const
106114 {
107115 QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE, LOGIN1_PATH, LOGIN1_IFACE, method);
108− QDBusReply<QString> reply = QDBusConnection::systemBus().asyncCall(msg);
116+ QDBusReply<QString> reply = QDBusConnection::systemBus().call(msg);
109117 return reply.isValid() && (reply == QStringLiteral("yes") || reply == QStringLiteral("challenge"));
110118 }
111119
@@ -119,6 +127,21 @@ public:
119127 QDBusConnection::systemBus().asyncCall(msg);
120128 }
121129
130+ void setActive(bool active)
131+ {
132+ isSessionActive = active;
133+
134+ Q_EMIT screensaverActiveChanged(!isSessionActive);
135+
136+ if (isSessionActive) {
137+ screensaverActiveTimer.invalidate();
138+ setIdleHint(false);
139+ } else {
140+ screensaverActiveTimer.start();
141+ setIdleHint(true);
142+ }
143+ }
144+
122145 void checkActive()
123146 {
124147 if (logindSessionPath.isEmpty()) {
@@ -133,12 +156,20 @@ public:
133156 msg << LOGIN1_SESSION_IFACE;
134157 msg << ACTIVE_KEY;
135158
136− QDBusReply<QVariant> reply = QDBusConnection::systemBus().asyncCall(msg);
137− if (reply.isValid()) {
138− isSessionActive = reply.value().toBool();
139− } else {
140− qWarning() << "Failed to get Active property" << reply.error().message();
141− }
159+ QDBusPendingCall pendingCall = QDBusConnection::systemBus().asyncCall(msg);
160+ QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall, this);
161+ connect(watcher, &QDBusPendingCallWatcher::finished,
162+ this, [this](QDBusPendingCallWatcher* watcher) {
163+
164+ QDBusPendingReply<QVariant> reply = *watcher;
165+ watcher->deleteLater();
166+ if (reply.isError()) {
167+ qWarning() << "Failed to get Active property" << reply.error().message();
168+ return;
169+ }
170+
171+ setActive(reply.value().toBool());
172+ });
142173 }
143174
144175 quint32 screensaverActiveTime() const
@@ -159,7 +190,7 @@ public:
159190 msg << LOGIN1_SESSION_IFACE;
160191 msg << IDLE_SINCE_KEY;
161192
162− QDBusReply<QVariant> reply = QDBusConnection::systemBus().asyncCall(msg);
193+ QDBusReply<QVariant> reply = QDBusConnection::systemBus().call(msg);
163194 if (reply.isValid()) {
164195 return reply.value().value<quint64>();
165196 } else {
@@ -184,22 +215,10 @@ private Q_SLOTS:
184215 {
185216 Q_UNUSED(iface)
186217
187− if (changedProps.contains(ACTIVE_KEY) || invalidatedProps.contains(ACTIVE_KEY)) {
188− if (changedProps.value(ACTIVE_KEY).isValid()) {
189− isSessionActive = changedProps.value(ACTIVE_KEY).toBool();
190− } else {
191− checkActive();
192− }
193−
194− Q_EMIT screensaverActiveChanged(!isSessionActive);
195−
196− if (isSessionActive) {
197− screensaverActiveTimer.invalidate();
198− setIdleHint(false);
199− } else {
200− screensaverActiveTimer.start();
201− setIdleHint(true);
202− }
218+ if (changedProps.contains(ACTIVE_KEY)) {
219+ setActive(changedProps.value(ACTIVE_KEY).toBool());
220+ } else if (invalidatedProps.contains(ACTIVE_KEY)) {
221+ checkActive();
203222 }
204223 }
205224
@@ -325,13 +344,22 @@ void DBusUnitySessionService::Lock()
325344 sessionPath,
326345 QStringLiteral("org.freedesktop.DisplayManager.Session"),
327346 QStringLiteral("Lock"));
328− QDBusReply<void> reply = QDBusConnection::systemBus().asyncCall(msg);
329− if (!reply.isValid()) {
330− qWarning() << "Lock call failed" << reply.error().message();
331− } else {
347+
348+ QDBusPendingCall pendingCall = QDBusConnection::systemBus().asyncCall(msg);
349+ QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall, this);
350+ connect(watcher, &QDBusPendingCallWatcher::finished,
351+ this, [this](QDBusPendingCallWatcher* watcher) {
352+
353+ QDBusPendingReply<QVariant> reply = *watcher;
354+ watcher->deleteLater();
355+ if (reply.isError()) {
356+ qWarning() << "Lock call failed" << reply.error().message();
357+ return;
358+ }
359+
332360 // emit Locked when the call succeeds
333361 Q_EMIT Locked();
334− }
362+ });
335363}
336364
337365bool DBusUnitySessionService::IsLocked() const
plugins/Utils/CMakeLists.txt+2
@@ -10,6 +10,8 @@ include_directories(
1010
1111set(QMLPLUGIN_SRC
1212 activefocuslogger.cpp
13+ ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h
14+ applicationsfiltermodel.cpp
1315 constants.cpp
1416 HomeKeyWatcher.cpp
1517 inputwatcher.cpp
plugins/Utils/applicationsfiltermodel.cppnew file+101
@@ -0,0 +1,101 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+// unity-api
18+#include <unity/shell/application/ApplicationManagerInterface.h>
19+#include <unity/shell/application/ApplicationInfoInterface.h>
20+
21+#include "applicationsfiltermodel.h"
22+
23+using namespace unity::shell::application;
24+
25+ApplicationsFilterModel::ApplicationsFilterModel(QObject *parent):
26+ QSortFilterProxyModel(parent),
27+ m_appModel(nullptr),
28+ m_filterTouchApps(false),
29+ m_filterLegacyApps(false)
30+{
31+}
32+
33+ApplicationManagerInterface *ApplicationsFilterModel::applicationsModel() const
34+{
35+ return m_appModel;
36+}
37+
38+void ApplicationsFilterModel::setApplicationsModel(ApplicationManagerInterface *applicationsModel)
39+{
40+ if (m_appModel != applicationsModel) {
41+ if (m_appModel) {
42+ disconnect(m_appModel, &ApplicationManagerInterface::countChanged, this, &ApplicationsFilterModel::countChanged);
43+ }
44+ m_appModel = applicationsModel;
45+ setSourceModel(m_appModel);
46+ Q_EMIT applicationsModelChanged();
47+ connect(m_appModel, &ApplicationManagerInterface::countChanged, this, &ApplicationsFilterModel::countChanged);
48+ }
49+}
50+
51+bool ApplicationsFilterModel::filterTouchApps() const
52+{
53+ return m_filterTouchApps;
54+}
55+
56+void ApplicationsFilterModel::setFilterTouchApps(bool filterTouchApps)
57+{
58+ if (m_filterTouchApps != filterTouchApps) {
59+ m_filterTouchApps = filterTouchApps;
60+ Q_EMIT filterTouchAppsChanged();
61+
62+ invalidateFilter();
63+ Q_EMIT countChanged();
64+ }
65+}
66+
67+bool ApplicationsFilterModel::filterLegacyApps() const
68+{
69+ return m_filterLegacyApps;
70+}
71+
72+void ApplicationsFilterModel::setFilterLegacyApps(bool filterLegacyApps)
73+{
74+ if (m_filterLegacyApps != filterLegacyApps) {
75+ m_filterLegacyApps = filterLegacyApps;
76+ Q_EMIT filterLegacyAppsChanged();
77+
78+ invalidateFilter();
79+ Q_EMIT countChanged();
80+ }
81+}
82+
83+bool ApplicationsFilterModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
84+{
85+ Q_UNUSED(source_parent);
86+
87+ ApplicationInfoInterface *app = m_appModel->get(source_row);
88+ Q_ASSERT(app);
89+ if (m_filterLegacyApps && !app->isTouchApp()) {
90+ return false;
91+ }
92+ if (m_filterTouchApps && app->isTouchApp()) {
93+ return false;
94+ }
95+ return true;
96+}
97+
98+ApplicationInfoInterface *ApplicationsFilterModel::get(int index) const
99+{
100+ return m_appModel->get(mapToSource(this->index(index, 0)).row());
101+}
plugins/Utils/applicationsfiltermodel.hnew file+69
@@ -0,0 +1,69 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef APPLICATIONSFILTERMODEL_H
18+#define APPLICATIONSFILTERMODEL_H
19+
20+#include <QSortFilterProxyModel>
21+
22+namespace unity {
23+namespace shell {
24+namespace application {
25+class ApplicationManagerInterface;
26+class ApplicationInfoInterface;
27+}
28+}
29+}
30+using namespace unity::shell::application;
31+
32+class ApplicationsFilterModel: public QSortFilterProxyModel
33+{
34+ Q_OBJECT
35+
36+ Q_PROPERTY(unity::shell::application::ApplicationManagerInterface* applicationsModel READ applicationsModel WRITE setApplicationsModel NOTIFY applicationsModelChanged)
37+ Q_PROPERTY(bool filterTouchApps READ filterTouchApps WRITE setFilterTouchApps NOTIFY filterTouchAppsChanged)
38+ Q_PROPERTY(bool filterLegacyApps READ filterLegacyApps WRITE setFilterLegacyApps NOTIFY filterLegacyAppsChanged)
39+
40+ Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
41+public:
42+ ApplicationsFilterModel(QObject *parent = 0);
43+
44+ ApplicationManagerInterface* applicationsModel() const;
45+ void setApplicationsModel(ApplicationManagerInterface* applicationsModel);
46+
47+ bool filterTouchApps() const;
48+ void setFilterTouchApps(bool filterTouchApps);
49+
50+ bool filterLegacyApps() const;
51+ void setFilterLegacyApps(bool filterLegacyApps);
52+
53+ bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
54+
55+ Q_INVOKABLE unity::shell::application::ApplicationInfoInterface* get(int index) const;
56+
57+Q_SIGNALS:
58+ void applicationsModelChanged();
59+ void filterTouchAppsChanged();
60+ void filterLegacyAppsChanged();
61+ void countChanged();
62+
63+private:
64+ ApplicationManagerInterface* m_appModel;
65+ bool m_filterTouchApps;
66+ bool m_filterLegacyApps;
67+};
68+
69+#endif
plugins/Utils/plugin.cpp+2
@@ -35,6 +35,7 @@
3535#include "windowstatestorage.h"
3636#include "constants.h"
3737#include "timezoneFormatter.h"
38+#include "applicationsfiltermodel.h"
3839
3940static QObject *createWindowStateStorage(QQmlEngine *engine, QJSEngine *scriptEngine)
4041{
@@ -66,6 +67,7 @@ void UtilsPlugin::registerTypes(const char *uri)
6667 qmlRegisterSingletonType<TimezoneFormatter>(uri, 0, 1, "TimezoneFormatter",
6768 [](QQmlEngine*, QJSEngine*) -> QObject* { return new TimezoneFormatter; });
6869 qmlRegisterType<ActiveFocusLogger>(uri, 0, 1, "ActiveFocusLogger");
70+ qmlRegisterType<ApplicationsFilterModel>(uri, 0, 1, "ApplicationsFilterModel");
6971}
7072
7173void UtilsPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
po/am.po+4−4
@@ -8,15 +8,15 @@ msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
1010"POT-Creation-Date: 2015-10-19 14:34+0100\n"
11−"PO-Revision-Date: 2015-07-04 14:46+0000\n"
11+"PO-Revision-Date: 2015-11-19 19:27+0000\n"
1212"Last-Translator: samson <Unknown>\n"
1313"Language-Team: Amharic <am@li.org>\n"
1414"MIME-Version: 1.0\n"
1515"Content-Type: text/plain; charset=UTF-8\n"
1616"Content-Transfer-Encoding: 8bit\n"
1717"Plural-Forms: nplurals=2; plural=n > 1;\n"
18−"X-Launchpad-Export-Date: 2015-11-07 05:12+0000\n"
19−"X-Generator: Launchpad (build 17838)\n"
18+"X-Launchpad-Export-Date: 2015-11-20 05:31+0000\n"
19+"X-Generator: Launchpad (build 17850)\n"
2020
2121#: plugins/IntegratedLightDM/Greeter.cpp:112
2222msgid "Password: "
@@ -290,7 +290,7 @@ msgstr "ግምገማ መጨመሪያ"
290290
291291#: qml/Dash/Previews/PreviewSharing.qml:51
292292msgid "Preview Share Item"
293−msgstr ""
293+msgstr "የሚካፈሉትን እቃ ቅድመ እይታ"
294294
295295#: qml/Dash/PullToRefreshScopeStyle.qml:55
296296msgid "Pull to refresh…"
po/unity8.pot+67−138
@@ -8,7 +8,7 @@ msgid ""
88msgstr ""
99"Project-Id-Version: unity8\n"
1010"Report-Msgid-Bugs-To: \n"
11−"POT-Creation-Date: 2015-10-19 14:34+0100\n"
11+"POT-Creation-Date: 2015-11-04 15:02+0000\n"
1212"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
1313"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
1414"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -35,86 +35,6 @@ msgstr ""
3535msgid "Unpin shortcut"
3636msgstr ""
3737
38−#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
39−#. This format string is used for showing, on a 12-hour clock, times that happen yesterday.
40−#. (\u2003 is a unicode em space which is slightly wider than a normal space.)
41−#. en_US example: "Yesterday\u2003%l:%M %p" --> "Yesterday 1:00 PM"
42−#: plugins/Utils/relativetimeformatter.cpp:157
43−msgid "Yesterday %l:%M %p"
44−msgstr ""
45−
46−#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
47−#. This format string is used for showing, on a 12-hour clock, times that happened today.
48−#. en_US example: "%l:%M %p" --> "1:00 PM"
49−#: plugins/Utils/relativetimeformatter.cpp:164
50−msgid "%l:%M %p"
51−msgstr ""
52−
53−#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
54−#. This format string is used for showing, on a 12-hour clock, events/appointments that happen tomorrow.
55−#. (\u2003 is a unicode em space which is slightly wider than a normal space.)
56−#. en_US example: "Tomorrow\u2003%l:%M %p" --> "Tomorrow 1:00 PM"
57−#: plugins/Utils/relativetimeformatter.cpp:172
58−msgid "Tomorrow %l:%M %p"
59−msgstr ""
60−
61−#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
62−#. This format string is used for showing, on a 12-hour clock, times that happened in the last week.
63−#. (\u2003 is a unicode em space which is slightly wider than a normal space.)
64−#. en_US example: "%a\u2003%l:%M %p" --> "Fri 1:00 PM"
65−#: plugins/Utils/relativetimeformatter.cpp:181
66−msgid "%a %l:%M %p"
67−msgstr ""
68−
69−#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
70−#. This format string is used for showing, on a 12-hour clock, times that happened before a week from now.
71−#. (\u2003 is a unicode em space which is slightly wider than a normal space.)
72−#. en_US example: "%a %b %d\u2003%l:%M %p" --> "Fri Oct 31 1:00 PM"
73−#. en_GB example: "%a %d %b\u2003%l:%M %p" --> "Fri 31 Oct 1:00 PM"
74−#: plugins/Utils/relativetimeformatter.cpp:190
75−msgid "%a %d %b %l:%M %p"
76−msgstr ""
77−
78−#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
79−#. This format string is used for showing, on a 24-hour clock, times that happen yesterday.
80−#. (\u2003 is a unicode em space which is slightly wider than a normal space.)
81−#. en_US example: "Yesterday\u2003%l:%M %p" --> "Yesterday 13:00"
82−#: plugins/Utils/relativetimeformatter.cpp:201
83−msgid "Yesterday %H:%M"
84−msgstr ""
85−
86−#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
87−#. This format string is used for showing, on a 24-hour clock, times that happened today.
88−#. en_US example: "%H:%M" --> "13:00"
89−#: plugins/Utils/relativetimeformatter.cpp:208
90−msgid "%H:%M"
91−msgstr ""
92−
93−#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
94−#. This format string is used for showing, on a 24-hour clock, events/appointments that happen tomorrow.
95−#. (\u2003 is a unicode em space which is slightly wider than a normal space.)
96−#. en_US example: "Tomorrow\u2003%l:%M %p" --> "Tomorrow 13:00"
97−#: plugins/Utils/relativetimeformatter.cpp:216
98−msgid "Tomorrow %H:%M"
99−msgstr ""
100−
101−#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
102−#. This format string is used for showing, on a 24-hour clock, times that happened in the last week.
103−#. (\u2003 is a unicode em space which is slightly wider than a normal space.)
104−#. en_US example: "%a\u2003%H:%M" --> "Fri 13:00"
105−#: plugins/Utils/relativetimeformatter.cpp:225
106−msgid "%a %H:%M"
107−msgstr ""
108−
109−#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
110−#. This format string is used for showing, on a 24-hour clock, times that happened before a week from now.
111−#. (\u2003 is a unicode em space which is slightly wider than a normal space.)
112−#. en_US example: "%a %b %d\u2003%H:%M" --> "Fri Oct 31 13:00"
113−#. en_GB example: "%a %d %b\u2003%H:%M" --> "Fri 31 Oct 13:00"
114−#: plugins/Utils/relativetimeformatter.cpp:234
115−msgid "%a %d %b %H:%M"
116−msgstr ""
117−
11838#: qml/Components/DelayedLockscreen.qml:49
11939msgid "Device Locked"
12040msgstr ""
@@ -134,83 +54,83 @@ msgid_plural "Please wait %1 minutes and then try again…"
13454msgstr[0] ""
13555msgstr[1] ""
13656
137−#: qml/Components/Dialogs.qml:115
57+#: qml/Components/Dialogs.qml:116
13858msgctxt "Title: Lock/Log out dialog"
13959msgid "Log out"
14060msgstr ""
14161
142−#: qml/Components/Dialogs.qml:116
62+#: qml/Components/Dialogs.qml:117
14363msgid "Are you sure you want to log out?"
14464msgstr ""
14565
146−#: qml/Components/Dialogs.qml:118
66+#: qml/Components/Dialogs.qml:119
14767msgctxt "Button: Lock the system"
14868msgid "Lock"
14969msgstr ""
15070
151−#: qml/Components/Dialogs.qml:125
71+#: qml/Components/Dialogs.qml:126
15272msgctxt "Button: Log out from the system"
15373msgid "Log Out"
15474msgstr ""
15575
156−#: qml/Components/Dialogs.qml:132 qml/Components/Dialogs.qml:165
157−#: qml/Components/Dialogs.qml:224
76+#: qml/Components/Dialogs.qml:133 qml/Components/Dialogs.qml:166
77+#: qml/Components/Dialogs.qml:225
15878msgid "Cancel"
15979msgstr ""
16080
161−#: qml/Components/Dialogs.qml:144
81+#: qml/Components/Dialogs.qml:145
16282msgctxt "Title: Reboot/Shut down dialog"
16383msgid "Shut down"
16484msgstr ""
16585
166−#: qml/Components/Dialogs.qml:145
86+#: qml/Components/Dialogs.qml:146
16787msgid "Are you sure you want to shut down?"
16888msgstr ""
16989
170−#: qml/Components/Dialogs.qml:147
90+#: qml/Components/Dialogs.qml:148
17191msgctxt "Button: Reboot the system"
17292msgid "Reboot"
17393msgstr ""
17494
175−#: qml/Components/Dialogs.qml:156
95+#: qml/Components/Dialogs.qml:157
17696msgctxt "Button: Shut down the system"
17797msgid "Shut down"
17898msgstr ""
17999
180−#: qml/Components/Dialogs.qml:178
100+#: qml/Components/Dialogs.qml:179
181101msgctxt "Title: Reboot dialog"
182102msgid "Reboot"
183103msgstr ""
184104
185−#: qml/Components/Dialogs.qml:179
105+#: qml/Components/Dialogs.qml:180
186106msgid "Are you sure you want to reboot?"
187107msgstr ""
188108
189−#: qml/Components/Dialogs.qml:181
109+#: qml/Components/Dialogs.qml:182
190110msgid "No"
191111msgstr ""
192112
193−#: qml/Components/Dialogs.qml:188
113+#: qml/Components/Dialogs.qml:189
194114msgid "Yes"
195115msgstr ""
196116
197−#: qml/Components/Dialogs.qml:203
117+#: qml/Components/Dialogs.qml:204
198118msgctxt "Title: Power off/Restart dialog"
199119msgid "Power"
200120msgstr ""
201121
202−#: qml/Components/Dialogs.qml:204
122+#: qml/Components/Dialogs.qml:205
203123msgid ""
204124"Are you sure you would like\n"
205125"to power off?"
206126msgstr ""
207127
208−#: qml/Components/Dialogs.qml:206
128+#: qml/Components/Dialogs.qml:207
209129msgctxt "Button: Power off the system"
210130msgid "Power off"
211131msgstr ""
212132
213−#: qml/Components/Dialogs.qml:215
133+#: qml/Components/Dialogs.qml:216
214134msgctxt "Button: Restart the system"
215135msgid "Restart"
216136msgstr ""
@@ -227,42 +147,42 @@ msgstr ""
227147msgid "OK"
228148msgstr ""
229149
230−#: qml/Dash/GenericScopeView.qml:569 qml/Dash/GenericScopeView.qml:727
231−msgid "See less"
232−msgstr ""
233−
234−#: qml/Dash/GenericScopeView.qml:569
235−msgid "See all"
236−msgstr ""
237−
238−#: qml/Dash/GenericScopeView.qml:631
239−msgctxt "Label: Hint for dash search line edit"
240−msgid "Search"
241−msgstr ""
242−
243−#: qml/Dash/PageHeader.qml:269
150+#: qml/Dash/DashPageHeader.qml:265
244151msgctxt "Button: Open the Ubuntu Store"
245152msgid "Store"
246153msgstr ""
247154
248−#: qml/Dash/PageHeader.qml:276
155+#: qml/Dash/DashPageHeader.qml:272
249156msgctxt "Button: Start a search in the current dash scope"
250157msgid "Search"
251158msgstr ""
252159
253−#: qml/Dash/PageHeader.qml:286
160+#: qml/Dash/DashPageHeader.qml:282
254161msgctxt "Button: Show the current dash scope settings"
255162msgid "Settings"
256163msgstr ""
257164
258−#: qml/Dash/PageHeader.qml:293
165+#: qml/Dash/DashPageHeader.qml:289
259166msgid "Remove from Favorites"
260167msgstr ""
261168
262−#: qml/Dash/PageHeader.qml:293
169+#: qml/Dash/DashPageHeader.qml:289
263170msgid "Add to Favorites"
264171msgstr ""
265172
173+#: qml/Dash/GenericScopeView.qml:569 qml/Dash/GenericScopeView.qml:727
174+msgid "See less"
175+msgstr ""
176+
177+#: qml/Dash/GenericScopeView.qml:569
178+msgid "See all"
179+msgstr ""
180+
181+#: qml/Dash/GenericScopeView.qml:631
182+msgctxt "Label: Hint for dash search line edit"
183+msgid "Search"
184+msgstr ""
185+
266186#: qml/Dash/Previews/PreviewActionCombo.qml:35
267187msgid "More..."
268188msgstr ""
@@ -273,8 +193,6 @@ msgstr ""
273193
274194#: qml/Dash/Previews/PreviewCommentInput.qml:68
275195#: qml/Dash/Previews/PreviewRatingInput.qml:174
276−#: qml/Panel/Indicators/MessageMenuItemFactory.qml:148
277−#: qml/Panel/Indicators/MessageMenuItemFactory.qml:205
278196msgid "Send"
279197msgstr ""
280198
@@ -314,6 +232,10 @@ msgstr ""
314232msgid "Also installed"
315233msgstr ""
316234
235+#: qml/DisabledScreenNotice.qml:42
236+msgid "Your device is now connected to an external display."
237+msgstr ""
238+
317239#: qml/Greeter/CoverPage.qml:107
318240msgid "Unlock"
319241msgstr ""
@@ -381,7 +303,7 @@ msgstr ""
381303msgid "Sorry, incorrect passcode"
382304msgstr ""
383305
384−#: qml/Notifications/NotificationMenuItemFactory.qml:128
306+#: qml/Notifications/NotificationMenuItemFactory.qml:129
385307msgid "Show password"
386308msgstr ""
387309
@@ -393,71 +315,78 @@ msgstr ""
393315msgid "Conference"
394316msgstr ""
395317
396−#: qml/Panel/Indicators/MenuItemFactory.qml:727
318+#: qml/Panel/Indicators/MenuItemFactory.qml:731
397319msgid "Nothing is playing"
398320msgstr ""
399321
400−#: qml/Panel/Indicators/MenuItemFactory.qml:856
322+#: qml/Panel/Indicators/MenuItemFactory.qml:860
401323#, qt-format
402324msgid "%1 hour"
403325msgid_plural "%1 hours"
404326msgstr[0] ""
405327msgstr[1] ""
406328
407−#: qml/Panel/Indicators/MenuItemFactory.qml:860
329+#: qml/Panel/Indicators/MenuItemFactory.qml:864
408330#, qt-format
409331msgid "%1 minute"
410332msgid_plural "%1 minutes"
411333msgstr[0] ""
412334msgstr[1] ""
413335
414−#: qml/Panel/Indicators/MenuItemFactory.qml:865
336+#: qml/Panel/Indicators/MenuItemFactory.qml:869
415337#, qt-format
416338msgid "%1 second"
417339msgid_plural "%1 seconds"
418340msgstr[0] ""
419341msgstr[1] ""
420342
421−#: qml/Panel/Indicators/MenuItemFactory.qml:868
343+#: qml/Panel/Indicators/MenuItemFactory.qml:872
422344msgid "0 seconds"
423345msgstr ""
424346
425347#. Translators: String like "1 hour, 2 minutes, 3 seconds remaining"
426−#: qml/Panel/Indicators/MenuItemFactory.qml:870
348+#: qml/Panel/Indicators/MenuItemFactory.qml:874
427349#, qt-format
428350msgid "%1 remaining"
429351msgstr ""
430352
431−#: qml/Panel/Indicators/MenuItemFactory.qml:876
353+#: qml/Panel/Indicators/MenuItemFactory.qml:880
432354msgid "In queue…"
433355msgstr ""
434356
435−#: qml/Panel/Indicators/MenuItemFactory.qml:880
357+#: qml/Panel/Indicators/MenuItemFactory.qml:884
436358msgid "Downloading"
437359msgstr ""
438360
439−#: qml/Panel/Indicators/MenuItemFactory.qml:882
361+#: qml/Panel/Indicators/MenuItemFactory.qml:886
440362msgid "Paused, tap to resume"
441363msgstr ""
442364
443−#: qml/Panel/Indicators/MenuItemFactory.qml:884
365+#: qml/Panel/Indicators/MenuItemFactory.qml:888
444366msgid "Canceled"
445367msgstr ""
446368
447−#: qml/Panel/Indicators/MenuItemFactory.qml:886
369+#: qml/Panel/Indicators/MenuItemFactory.qml:890
448370msgid "Finished"
449371msgstr ""
450372
451−#: qml/Panel/Indicators/MenuItemFactory.qml:888
373+#: qml/Panel/Indicators/MenuItemFactory.qml:892
452374msgid "Failed, tap to retry"
453375msgstr ""
454376
455−#: qml/Panel/Indicators/MessageMenuItemFactory.qml:149
377+#: qml/Panel/Indicators/MessageMenuItemFactory.qml:155
378+#: qml/Panel/Indicators/MessageMenuItemFactory.qml:214
379+msgctxt "Button: Send a reply message"
380+msgid "Send"
381+msgstr ""
382+
383+#: qml/Panel/Indicators/MessageMenuItemFactory.qml:156
456384msgctxt "Label: Hint in message indicator line edit"
457385msgid "Reply"
458386msgstr ""
459387
460−#: qml/Panel/Indicators/MessageMenuItemFactory.qml:204
388+#: qml/Panel/Indicators/MessageMenuItemFactory.qml:213
389+msgctxt "Button: Call back on phone"
461390msgid "Call back"
462391msgstr ""
463392
@@ -541,7 +470,7 @@ msgstr ""
541470
542471#: qml/Wizard/Pages/10-welcome.qml:88 qml/Wizard/Pages/30-passwd-type.qml:128
543472#: qml/Wizard/Pages/40-wifi.qml:215 qml/Wizard/Pages/50-location.qml:131
544−#: qml/Wizard/Pages/60-reporting.qml:50 qml/Wizard/Pages/passwd-confirm.qml:82
473+#: qml/Wizard/Pages/60-reporting.qml:60 qml/Wizard/Pages/passwd-confirm.qml:82
545474#: qml/Wizard/Pages/passwd-set.qml:90
546475msgid "Continue"
547476msgstr ""
@@ -639,17 +568,17 @@ msgstr ""
639568msgid "You can change your mind later in <b>System Settings</b>."
640569msgstr ""
641570
642−#: qml/Wizard/Pages/60-reporting.qml:24
571+#: qml/Wizard/Pages/60-reporting.qml:26
643572msgid "Improving your experience"
644573msgstr ""
645574
646−#: qml/Wizard/Pages/60-reporting.qml:36
575+#: qml/Wizard/Pages/60-reporting.qml:46
647576msgid ""
648577"Your phone is set up to automatically report errors to Canonical and its "
649578"partners, the makers of the operating system."
650579msgstr ""
651580
652−#: qml/Wizard/Pages/60-reporting.qml:43
581+#: qml/Wizard/Pages/60-reporting.qml:53
653582msgid ""
654583"This can be disabled in <b>System Settings</b> under <b>Security &amp; "
655584"Privacy</b>"
qml/Components/Dialogs.qml+29−2
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2015 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -21,6 +21,7 @@ import Unity.Session 0.1
2121import GlobalShortcut 1.0
2222import Ubuntu.Components 1.3
2323import Unity.Platform 1.0
24+import Utils 0.1
2425import "../Greeter"
2526
2627Item {
@@ -45,6 +46,31 @@ Item {
4546 d.showPowerDialog();
4647 }
4748
49+ onUsageScenarioChanged: {
50+ if (usageScenario != "desktop" && legacyAppsModel.count > 0 && !d.modeSwitchWarningPopup) {
51+ var comp = Qt.createComponent(Qt.resolvedUrl("ModeSwitchWarningDialog.qml"))
52+ d.modeSwitchWarningPopup = comp.createObject(root, {model: legacyAppsModel});
53+ d.modeSwitchWarningPopup.forceClose.connect(function() {
54+ while (legacyAppsModel.count > 0) {
55+ ApplicationManager.stopApplication(legacyAppsModel.get(0).appId);
56+ }
57+ d.modeSwitchWarningPopup.hide();
58+ d.modeSwitchWarningPopup.destroy();
59+ d.modeSwitchWarningPopup = null;
60+ })
61+ } else if (usageScenario == "desktop" && d.modeSwitchWarningPopup) {
62+ d.modeSwitchWarningPopup.hide();
63+ d.modeSwitchWarningPopup.destroy();
64+ d.modeSwitchWarningPopup = null;
65+ }
66+ }
67+
68+ ApplicationsFilterModel {
69+ id: legacyAppsModel
70+ applicationsModel: ApplicationManager
71+ filterTouchApps: true
72+ }
73+
4874 GlobalShortcut { // reboot/shutdown dialog
4975 shortcut: Qt.Key_PowerDown
5076 active: Platform.isPC
@@ -91,6 +117,8 @@ Item {
91117 id: d // private stuff
92118 objectName: "dialogsPrivate"
93119
120+ property var modeSwitchWarningPopup: null
121+
94122 function showPowerDialog() {
95123 if (!dialogLoader.active) {
96124 dialogLoader.sourceComponent = powerDialogComponent;
@@ -270,5 +298,4 @@ Item {
270298 unitySessionService.endSession();
271299 }
272300 }
273−
274301}
qml/Components/ModeSwitchWarningDialog.qmlnew file+95
@@ -0,0 +1,95 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.4
18+import QtQuick.Layouts 1.1
19+import Ubuntu.Components 1.3
20+import Ubuntu.Components.ListItems 1.3
21+import Ubuntu.Components.Popups 1.3
22+
23+ShellDialog {
24+ id: root
25+ objectName: "modeSwitchWarningDialog"
26+
27+ property alias model: appRepeater.model
28+
29+ signal forceClose();
30+
31+ Label {
32+ text: i18n.tr("Apps may have unsaved data:")
33+ fontSize: "large"
34+ color: "#5D5D5D"
35+ }
36+
37+ Repeater {
38+ id: appRepeater
39+ RowLayout {
40+ spacing: units.gu(2)
41+ Image {
42+ Layout.preferredHeight: units.gu(2)
43+ Layout.preferredWidth: units.gu(2)
44+ source: model.icon
45+ sourceSize.width: width
46+ sourceSize.height: height
47+ }
48+ Label {
49+ Layout.fillWidth: true
50+ text: model.name
51+ color: "#888888"
52+ }
53+ }
54+ }
55+
56+ Label {
57+ text: i18n.tr("Re-dock, save your work and close these apps to continue.")
58+ wrapMode: Text.WordWrap
59+ color: "#888888"
60+ }
61+
62+ Label {
63+ text: i18n.tr("Or force close now (unsaved data will be lost).")
64+ wrapMode: Text.WordWrap
65+ color: "#888888"
66+ }
67+
68+ ThinDivider {}
69+
70+ RowLayout {
71+ Label {
72+ objectName: "reconnectLabel"
73+ Layout.fillWidth: true
74+ property bool clicked: false
75+ property string notClickedText: i18n.tr("OK, I will reconnect")
76+ property string clickedText: i18n.tr("Reconnect now!")
77+ text: clicked ? clickedText : notClickedText
78+ color: "#333333"
79+
80+ MouseArea {
81+ anchors.fill: parent
82+ onClicked: parent.clicked = true;
83+ }
84+ }
85+
86+ Button {
87+ objectName: "forceCloseButton"
88+ text: i18n.tr("Close all")
89+ color: UbuntuColors.red
90+ onClicked: {
91+ root.forceClose();
92+ }
93+ }
94+ }
95+}
qml/Components/PanelState/PanelState.qml+2
@@ -23,6 +23,8 @@ QtObject {
2323 property string title: ""
2424 property bool buttonsVisible: false
2525
26+ property int panelHeight: 0
27+
2628 signal close()
2729 signal minimize()
2830 signal maximize()
qml/Components/ShellDialog.qml+1−1
@@ -31,7 +31,7 @@ Dialog {
3131 // NB: PopupBase, Dialog's superclass, will check for the existence of this property
3232 property bool reparentToRootItem: false
3333
34− onVisibleChanged: { if (!visible) { dialogLoader.active = false; } }
34+ onVisibleChanged: { if (!visible && dialogLoader) { dialogLoader.active = false; } }
3535
3636 Keys.onEscapePressed: hide()
3737
qml/Components/UnityInputInfo/UnityInputInfo.qmldeleted−93
@@ -1,93 +0,0 @@
1−/*
2− * Copyright 2015 Canonical Ltd.
3− *
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU Lesser General Public License as published by
6− * the Free Software Foundation; version 3.
7− *
8− * This program is distributed in the hope that it will be useful,
9− * but WITHOUT ANY WARRANTY; without even the implied warranty of
10− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU Lesser General Public License for more details.
12− *
13− * You should have received a copy of the GNU Lesser General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− */
16−
17−pragma Singleton
18−
19−import QtQuick 2.4
20−import Unity.InputInfo 0.1
21−// Workaround https://bugs.launchpad.net/ubuntu/+source/unity8/+bug/1473471
22−import Ubuntu.Components 1.3
23−
24−Item {
25− id: root
26− readonly property alias mice: priv.miceCount
27− readonly property alias keyboards: priv.keyboardCount
28−
29− property alias inputInfo: inputInfo
30−
31− QtObject {
32− id: priv
33−
34− property var mice: []
35− property var keyboards: []
36−
37− property int miceCount: 0
38− property int keyboardCount: 0
39−
40− function addMouse(devicePath) {
41− mice.push(devicePath);
42− miceCount++;
43− }
44−
45− function addKeyboard(devicePath) {
46− keyboards.push(devicePath);
47− keyboardCount++;
48− }
49−
50− function removeDevice(devicePath) {
51− for (var i = 0; i < priv.mice.length; i++) {
52− if (priv.mice[i] == devicePath) {
53− priv.mice.splice(i, 1);
54− priv.miceCount--;
55− }
56− }
57− for (var i = 0; i < priv.keyboards.length; i++) {
58− if (priv.keyboards[i] == devicePath) {
59− priv.keyboards.splice(i, 1);
60− priv.keyboardCount--;
61− }
62− }
63− }
64− }
65−
66− InputDeviceInfo {
67− id: inputInfo
68− objectName: "inputDeviceInfo"
69−
70− onNewDevice: {
71− var device = inputInfo.get(inputInfo.indexOf(devicePath));
72− if (device === null) {
73− return;
74− }
75−
76− var hasMouse = (device.types & InputInfo.Mouse) == InputInfo.Mouse
77− var hasTouchpad = (device.types & InputInfo.TouchPad) == InputInfo.TouchPad
78− var hasKeyboard = (device.types & InputInfo.Keyboard) == InputInfo.Keyboard
79−
80− if (hasMouse || hasTouchpad) {
81− priv.addMouse(devicePath);
82− } else if (hasKeyboard) {
83− // Only accepting keyboards that do not claim to be a mouse too
84− // This will be a bit buggy for real hybrid devices, but doesn't
85− // fall for Microsoft mice that claim to be Keyboards too.
86− priv.addKeyboard(devicePath)
87− }
88− }
89− onDeviceRemoved: {
90− priv.removeDevice(devicePath)
91− }
92− }
93−}
qml/Components/UnityInputInfo/qmldirdeleted−1
@@ -1 +0,0 @@
1−singleton UnityInputInfo 1.0 UnityInputInfo.qml
qml/Dash/CardTool.qml+8−8
@@ -78,9 +78,9 @@ Item {
7878 /*!
7979 type:real \brief Width to be enforced on the card in this configuration.
8080
81− If undefined, should use implicit width of the actual card.
81+ If -1, should use implicit width of the actual card.
8282 */
83− property var cardWidth: {
83+ property real cardWidth: {
8484 switch (categoryLayout) {
8585 case "grid":
8686 case "vertical-journal":
@@ -106,16 +106,16 @@ Item {
106106 case "organic-grid":
107107 case "journal":
108108 default:
109− return undefined;
109+ return -1;
110110 }
111111 }
112112
113113 /*!
114114 type:real \brief Height to be enforced on the card in this configuration.
115115
116− If undefined, should use implicit height of the actual card.
116+ If -1, should use implicit height of the actual card.
117117 */
118− readonly property var cardHeight: {
118+ readonly property real cardHeight: {
119119 switch (categoryLayout) {
120120 case "journal":
121121 if (template["card-size"] >= 12 && template["card-size"] <= 38) return units.gu(template["card-size"]);
@@ -129,7 +129,7 @@ Item {
129129 case "organic-grid":
130130 case "vertical-journal":
131131 default:
132− return undefined;
132+ return -1;
133133 }
134134 }
135135
@@ -221,8 +221,8 @@ Item {
221221 item.objectName = "cardToolCard";
222222 item.asynchronous = false;
223223 item.components = Qt.binding(function() { return cardTool.components; });
224− item.width = Qt.binding(function() { return cardTool.cardWidth || item.implicitWidth; });
225− item.height = Qt.binding(function() { return cardTool.cardHeight || item.implicitHeight; });
224+ item.width = Qt.binding(function() { return cardTool.cardWidth !== -1 ? cardTool.cardWidth : item.implicitWidth; });
225+ item.height = Qt.binding(function() { return cardTool.cardHeight !== -1 ? cardTool.cardHeight : item.implicitHeight; });
226226 }
227227 Connections {
228228 target: cardTool
qml/Dash/graphics/phone/screenshots/libreoffice@12.pngnew file

Binary file not shown.

qml/OrientedShell.qml+16−7
@@ -16,13 +16,12 @@
1616
1717import QtQuick 2.4
1818import QtQuick.Window 2.2
19+import Unity.InputInfo 0.1
1920import Unity.Session 0.1
2021import GSettings 1.0
2122import "Components"
22−import "Components/UnityInputInfo"
2323import "Rotation"
24−// Backup Workaround https://bugs.launchpad.net/ubuntu/+source/unity8/+bug/1473471
25−// in case we remove the UnityInputInfo import
24+// Workaround https://bugs.launchpad.net/ubuntu/+source/unity8/+bug/1473471
2625import Ubuntu.Components 1.3
2726
2827Rectangle {
@@ -64,6 +63,16 @@ Rectangle {
6463 property bool orientationLocked: OrientationLock.enabled
6564 property var orientationLock: OrientationLock
6665
66+ InputDeviceModel {
67+ id: miceModel
68+ deviceFilter: InputInfo.Mouse
69+ }
70+
71+ InputDeviceModel {
72+ id: keyboardsModel
73+ deviceFilter: InputInfo.Keyboard
74+ }
75+
6776 property int orientation
6877 onPhysicalOrientationChanged: {
6978 if (!orientationLocked) {
@@ -83,7 +92,7 @@ Rectangle {
8392 }
8493 // We need to manually update this on startup as the binding
8594 // below doesn't seem to have any effect at that stage
86− oskSettings.stayHidden = UnityInputInfo.keyboards > 0
95+ oskSettings.stayHidden = keyboardsModel.count > 0
8796 oskSettings.disableHeight = shell.usageScenario == "desktop"
8897 }
8998
@@ -96,7 +105,7 @@ Rectangle {
96105 Binding {
97106 target: oskSettings
98107 property: "stayHidden"
99− value: UnityInputInfo.keyboards > 0
108+ value: keyboardsModel.count > 0
100109 }
101110
102111 Binding {
@@ -174,7 +183,7 @@ Rectangle {
174183 nativeWidth: root.width
175184 nativeHeight: root.height
176185 mode: applicationArguments.mode
177− hasMouse: UnityInputInfo.mice > deviceConfiguration.ignoredMice
186+ hasMouse: miceModel.count > deviceConfiguration.ignoredMice
178187
179188 // TODO: Factor in the connected input devices (eg: physical keyboard, mouse, touchscreen),
180189 // what's the output device (eg: big TV, desktop monitor, phone display), etc.
@@ -188,7 +197,7 @@ Rectangle {
188197 return "tablet";
189198 }
190199 } else { // automatic
191− if (UnityInputInfo.mice > deviceConfiguration.ignoredMice) {
200+ if (miceModel.count > deviceConfiguration.ignoredMice) {
192201 return "desktop";
193202 } else {
194203 return deviceConfiguration.category;
qml/Panel/Panel.qml+24−1
@@ -32,6 +32,12 @@ Item {
3232
3333 opacity: fullscreenMode && indicators.fullyClosed ? 0.0 : 1.0
3434
35+ Binding {
36+ target: PanelState
37+ property: "panelHeight"
38+ value: root.panelHeight
39+ }
40+
3541 Rectangle {
3642 id: darkenedArea
3743 property real darkenedOpacity: 0.6
@@ -137,7 +143,7 @@ Item {
137143 }
138144
139145 shown: false
140− width: root.width - (windowControlButtons.visible ? windowControlButtons.width : 0)
146+ width: root.width - (windowControlButtons.visible ? windowControlButtons.width + titleLabel.width : 0)
141147 minimizedPanelHeight: units.gu(3)
142148 expandedPanelHeight: units.gu(7)
143149 openedHeight: root.height - indicatorOrangeLine.height
@@ -178,6 +184,23 @@ Item {
178184 onMaximize: PanelState.maximize()
179185 }
180186
187+ Label {
188+ id: titleLabel
189+ objectName: "windowDecorationTitle"
190+ anchors {
191+ left: windowControlButtons.right
192+ top: parent.top
193+ margins: units.gu(0.7)
194+ }
195+ color: "#DFDBD2"
196+ height: windowControlButtons.height
197+ visible: windowControlButtons.visible
198+ verticalAlignment: Text.AlignVCenter
199+ fontSize: "small"
200+ font.bold: true
201+ text: PanelState.title
202+ }
203+
181204 PanelSeparatorLine {
182205 id: indicatorOrangeLine
183206 anchors {
qml/Stages/DecoratedWindow.qml+3−1
@@ -26,6 +26,7 @@ FocusScope {
2626 property alias window: applicationWindow
2727 property alias application: applicationWindow.application
2828 property alias active: decoration.active
29+ property alias title: decoration.title
2930
3031 property bool decorationShown: true
3132 property bool highlightShown: false
@@ -66,7 +67,8 @@ FocusScope {
6667 objectName: application ? "appWindowDecoration_" + application.appId : "appWindowDecoration_null"
6768 anchors { left: parent.left; top: parent.top; right: parent.right }
6869 height: units.gu(3)
69− title: window.title !== "" ? window.title : model.name
70+ width: root.width
71+ title: window.title !== "" ? window.title : ""
7072 onClose: root.close();
7173 onMaximize: root.maximize();
7274 onMinimize: root.minimize();
qml/Stages/DesktopSpread.qml+1−1
@@ -87,7 +87,7 @@ FocusScope {
8787 function focusSelected() {
8888 if (spreadRepeater.highlightedIndex != -1) {
8989 var application = ApplicationManager.get(spreadRepeater.highlightedIndex);
90− ApplicationManager.focusApplication(application.appId);
90+ ApplicationManager.requestFocusApplication(application.appId);
9191 }
9292 }
9393
qml/Stages/DesktopSpreadDelegate.qml+2−2
@@ -29,8 +29,8 @@ Item {
2929 property bool highlightShown: false
3030 property real shadowOpacity: 1
3131
32− property int windowWidth: application.session && application.session.surface ? application.session.surface.size.width : 0
33− property int windowHeight: application.session && application.session.surface ? application.session.surface.size.height : 0
32+ property int windowWidth: application && application.session && application.session.surface ? application.session.surface.size.width : 0
33+ property int windowHeight: application && application.session && application.session.surface ? application.session.surface.size.height : 0
3434
3535 state: "normal"
3636 states: [
qml/Stages/DesktopStage.qml+165−39
@@ -20,11 +20,11 @@ import QtQuick 2.4
2020import QtQuick.Layouts 1.1
2121import Ubuntu.Components 1.3
2222import Unity.Application 0.1
23−import "../Components"
2423import "../Components/PanelState"
2524import "../Components"
2625import Utils 0.1
2726import Ubuntu.Gestures 0.1
27+import GlobalShortcut 1.0
2828
2929AbstractStage {
3030 id: root
@@ -45,21 +45,72 @@ AbstractStage {
4545 spread.state = "";
4646 }
4747
48− ApplicationManager.requestFocusApplication(appId)
48+ ApplicationManager.focusApplication(appId);
49+ }
50+
51+ onApplicationRemoved: {
52+ priv.focusNext();
4953 }
5054
5155 onFocusRequested: {
5256 var appIndex = priv.indexOf(appId);
5357 var appDelegate = appRepeater.itemAt(appIndex);
54− appDelegate.minimized = false;
55− ApplicationManager.focusApplication(appId)
58+ appDelegate.restoreFromMinimized();
5659
5760 if (spread.state == "altTab") {
58− spread.cancel()
61+ spread.cancel();
5962 }
6063 }
6164 }
6265
66+ GlobalShortcut {
67+ id: closeWindowShortcut
68+ shortcut: Qt.AltModifier|Qt.Key_F4
69+ onTriggered: ApplicationManager.stopApplication(priv.focusedAppId)
70+ active: priv.focusedAppId !== ""
71+ }
72+
73+ GlobalShortcut {
74+ id: showSpreadShortcut
75+ shortcut: Qt.MetaModifier|Qt.Key_W
76+ onTriggered: spread.state = "altTab"
77+ }
78+
79+ GlobalShortcut {
80+ id: minimizeAllShortcut
81+ shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_D
82+ onTriggered: priv.minimizeAllWindows()
83+ }
84+
85+ GlobalShortcut {
86+ id: maximizeWindowShortcut
87+ shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Up
88+ onTriggered: priv.focusedAppDelegate.maximize()
89+ active: priv.focusedAppDelegate !== null
90+ }
91+
92+ GlobalShortcut {
93+ id: maximizeWindowLeftShortcut
94+ shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Left
95+ onTriggered: priv.focusedAppDelegate.maximizeLeft()
96+ active: priv.focusedAppDelegate !== null
97+ }
98+
99+ GlobalShortcut {
100+ id: maximizeWindowRightShortcut
101+ shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Right
102+ onTriggered: priv.focusedAppDelegate.maximizeRight()
103+ active: priv.focusedAppDelegate !== null
104+ }
105+
106+ GlobalShortcut {
107+ id: minimizeRestoreShortcut
108+ shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Down
109+ onTriggered: priv.focusedAppDelegate.maximized || priv.focusedAppDelegate.maximizedLeft || priv.focusedAppDelegate.maximizedRight
110+ ? priv.focusedAppDelegate.restore() : priv.focusedAppDelegate.minimize(true)
111+ active: priv.focusedAppDelegate !== null
112+ }
113+
63114 QtObject {
64115 id: priv
65116
@@ -93,6 +144,28 @@ AbstractStage {
93144 }
94145 return -1;
95146 }
147+
148+ function minimizeAllWindows() {
149+ for (var i = 0; i < appRepeater.count; i++) {
150+ var appDelegate = appRepeater.itemAt(i);
151+ if (appDelegate && !appDelegate.minimized) {
152+ appDelegate.minimize(false); // minimize but don't switch focus
153+ }
154+ }
155+
156+ ApplicationManager.unfocusCurrentApplication(); // no app should have focus at this point
157+ }
158+
159+ function focusNext() {
160+ ApplicationManager.unfocusCurrentApplication();
161+ for (var i = 0; i < appRepeater.count; i++) {
162+ var appDelegate = appRepeater.itemAt(i);
163+ if (appDelegate && !appDelegate.minimized) {
164+ ApplicationManager.focusApplication(appDelegate.appId);
165+ return;
166+ }
167+ }
168+ }
96169 }
97170
98171 Connections {
@@ -100,15 +173,23 @@ AbstractStage {
100173 onClose: {
101174 ApplicationManager.stopApplication(ApplicationManager.focusedApplicationId)
102175 }
103− onMinimize: appRepeater.itemAt(0).minimize();
104− onMaximize: appRepeater.itemAt(0).unmaximize();
176+ onMinimize: appRepeater.itemAt(0).minimize(true);
177+ onMaximize: appRepeater.itemAt(0).restore();
105178 }
106179
107180 Binding {
108181 target: PanelState
109182 property: "buttonsVisible"
110− value: priv.focusedAppDelegate !== null && priv.focusedAppDelegate.state === "maximized"
183+ value: priv.focusedAppDelegate !== null && priv.focusedAppDelegate.maximized
184+ }
185+
186+ Binding {
187+ target: PanelState
188+ property: "title"
189+ value: priv.focusedAppDelegate !== null && priv.focusedAppDelegate.title
190+ when: priv.focusedAppDelegate && priv.focusedAppDelegate.maximized
111191 }
192+
112193 Component.onDestruction: PanelState.buttonsVisible = false;
113194
114195 FocusScope {
@@ -135,23 +216,34 @@ AbstractStage {
135216
136217 delegate: FocusScope {
137218 id: appDelegate
138− objectName: "stageDelegate_" + model.appId
219+ objectName: "appDelegate_" + appId
139220 z: ApplicationManager.count - index
140221 y: units.gu(3)
141222 width: units.gu(60)
142223 height: units.gu(50)
143− focus: model.appId === priv.focusedAppId
224+ focus: appId === priv.focusedAppId
225+
226+ QtObject {
227+ id: appDelegatePrivate
228+ property bool maximized: false
229+ property bool maximizedLeft: false
230+ property bool maximizedRight: false
231+ property bool minimized: false
232+ }
233+ readonly property alias maximized: appDelegatePrivate.maximized
234+ readonly property alias maximizedLeft: appDelegatePrivate.maximizedLeft
235+ readonly property alias maximizedRight: appDelegatePrivate.maximizedRight
236+ readonly property alias minimized: appDelegatePrivate.minimized
144237
145− property bool maximized: false
146− property bool minimized: false
238+ readonly property string appId: model.appId
147239 property bool animationsEnabled: true
148−
240+ property alias title: decoratedWindow.title
149241 property bool visuallyMaximized: false
150242 property bool visuallyMinimized: false
151243
152244 onFocusChanged: {
153− if (focus && ApplicationManager.focusedApplicationId !== model.appId) {
154− ApplicationManager.focusApplication(model.appId);
245+ if (focus && ApplicationManager.focusedApplicationId !== appId) {
246+ ApplicationManager.focusApplication(appId);
155247 }
156248 }
157249
@@ -176,24 +268,52 @@ AbstractStage {
176268
177269 function maximize(animated) {
178270 animationsEnabled = (animated === undefined) || animated;
179− minimized = false;
180− maximized = true;
271+ appDelegatePrivate.minimized = false;
272+ appDelegatePrivate.maximized = true;
273+ appDelegatePrivate.maximizedLeft = false;
274+ appDelegatePrivate.maximizedRight = false;
275+ }
276+ function maximizeLeft() {
277+ appDelegatePrivate.minimized = false;
278+ appDelegatePrivate.maximized = false;
279+ appDelegatePrivate.maximizedLeft = true;
280+ appDelegatePrivate.maximizedRight = false;
281+ }
282+ function maximizeRight() {
283+ appDelegatePrivate.minimized = false;
284+ appDelegatePrivate.maximized = false;
285+ appDelegatePrivate.maximizedLeft = false;
286+ appDelegatePrivate.maximizedRight = true;
181287 }
182288 function minimize(animated) {
183289 animationsEnabled = (animated === undefined) || animated;
184− maximized = false;
185− minimized = true;
290+ appDelegatePrivate.maximized = false;
291+ appDelegatePrivate.minimized = true;
292+ }
293+ function restore(animated) {
294+ animationsEnabled = (animated === undefined) || animated;
295+ appDelegatePrivate.minimized = false;
296+ appDelegatePrivate.maximized = false;
297+ appDelegatePrivate.maximizedLeft = false;
298+ appDelegatePrivate.maximizedRight = false;
186299 }
187− function unmaximize(animated) {
300+ function restoreFromMinimized(animated) {
188301 animationsEnabled = (animated === undefined) || animated;
189− minimized = false;
190− maximized = false;
302+ appDelegatePrivate.minimized = false;
303+ if (maximized)
304+ maximize();
305+ else if (maximizedLeft)
306+ maximizeLeft();
307+ else if (maximizedRight)
308+ maximizeRight();
309+ ApplicationManager.focusApplication(appId);
191310 }
192311
193312 states: [
194313 State {
195314 name: "normal";
196315 when: !appDelegate.maximized && !appDelegate.minimized
316+ && !appDelegate.maximizedLeft && !appDelegate.maximizedRight
197317 PropertyChanges {
198318 target: appDelegate;
199319 visuallyMinimized: false;
@@ -210,6 +330,14 @@ AbstractStage {
210330 visuallyMaximized: true
211331 }
212332 },
333+ State {
334+ name: "maximizedLeft"; when: appDelegate.maximizedLeft && !appDelegate.minimized
335+ PropertyChanges { target: appDelegate; x: 0; y: units.gu(3); width: root.width/2; height: root.height - units.gu(3) }
336+ },
337+ State {
338+ name: "maximizedRight"; when: appDelegate.maximizedRight && !appDelegate.minimized
339+ PropertyChanges { target: appDelegate; x: root.width/2; y: units.gu(3); width: root.width/2; height: root.height - units.gu(3) }
340+ },
213341 State {
214342 name: "minimized"; when: appDelegate.minimized
215343 PropertyChanges {
@@ -227,14 +355,14 @@ AbstractStage {
227355 to: "normal"
228356 enabled: appDelegate.animationsEnabled
229357 PropertyAction { target: appDelegate; properties: "visuallyMinimized,visuallyMaximized" }
230− PropertyAnimation { target: appDelegate; properties: "x,y,opacity,width,height,scale,opacity" }
358+ UbuntuNumberAnimation { target: appDelegate; properties: "x,y,opacity,width,height,scale"; duration: UbuntuAnimation.FastDuration }
231359 },
232360 Transition {
233361 to: "maximized"
234362 enabled: appDelegate.animationsEnabled
235363 PropertyAction { target: appDelegate; property: "visuallyMinimized" }
236364 SequentialAnimation {
237− PropertyAnimation { target: appDelegate; properties: "x,y,opacity,width,height,scale,opacity" }
365+ UbuntuNumberAnimation { target: appDelegate; properties: "x,y,opacity,width,height,scale"; duration: UbuntuAnimation.FastDuration }
238366 PropertyAction { target: appDelegate; property: "visuallyMaximized" }
239367 }
240368 },
@@ -243,20 +371,15 @@ AbstractStage {
243371 enabled: appDelegate.animationsEnabled
244372 PropertyAction { target: appDelegate; property: "visuallyMaximized" }
245373 SequentialAnimation {
246− PropertyAnimation { target: appDelegate; properties: "x,y,opacity,width,height,scale,opacity" }
374+ UbuntuNumberAnimation { target: appDelegate; properties: "x,y,opacity,width,height,scale"; duration: UbuntuAnimation.FastDuration }
247375 PropertyAction { target: appDelegate; property: "visuallyMinimized" }
248− }
249− },
250− Transition {
251− from: ""
252− to: "altTab"
253− PropertyAction { target: appDelegate; properties: "y,angle,z,itemScale,itemScaleOriginY" }
254− PropertyAction { target: decoratedWindow; properties: "anchors.topMargin" }
255− PropertyAnimation {
256− target: appDelegate; properties: "x"
257− from: root.width
258− duration: rightEdgePushArea.containsMouse ? UbuntuAnimation.FastDuration :0
259− easing: UbuntuAnimation.StandardEasing
376+ ScriptAction {
377+ script: {
378+ if (appDelegate.animationsEnabled && state === "minimized" ) {
379+ priv.focusNext();
380+ }
381+ }
382+ }
260383 }
261384 }
262385 ]
@@ -276,6 +399,8 @@ AbstractStage {
276399 minHeight: units.gu(10)
277400 borderThickness: units.gu(2)
278401 windowId: model.appId // FIXME: Change this to point to windowId once we have such a thing
402+ screenWidth: root.width
403+ screenHeight: root.height
279404
280405 onPressed: { ApplicationManager.focusApplication(model.appId) }
281406 }
@@ -292,8 +417,9 @@ AbstractStage {
292417 focus: true
293418
294419 onClose: ApplicationManager.stopApplication(model.appId)
295− onMaximize: appDelegate.maximized ? appDelegate.unmaximize() : appDelegate.maximize()
296− onMinimize: appDelegate.minimize()
420+ onMaximize: appDelegate.maximized || appDelegate.maximizedLeft || appDelegate.maximizedRight
421+ ? appDelegate.restore() : appDelegate.maximize()
422+ onMinimize: appDelegate.minimize(true)
297423 onDecorationPressed: { ApplicationManager.focusApplication(model.appId) }
298424 }
299425 }
qml/Stages/WindowDecoration.qml+8−3
@@ -18,6 +18,7 @@ import QtQuick 2.4
1818import Unity.Application 0.1 // For Mir singleton
1919import Ubuntu.Components 1.3
2020import "../Components"
21+import "../Components/PanelState"
2122
2223MouseArea {
2324 id: root
@@ -45,17 +46,18 @@ MouseArea {
4546 priv.distanceX = pos.x;
4647 priv.distanceY = pos.y;
4748 priv.dragging = true;
48− Mir.cursorName = "grabbing";
4949 } else {
5050 priv.dragging = false;
5151 Mir.cursorName = "";
5252 }
5353 }
54+
5455 onPositionChanged: {
5556 if (priv.dragging) {
57+ Mir.cursorName = "grabbing";
5658 var pos = mapToItem(root.target.parent, mouseX, mouseY);
5759 root.target.x = pos.x - priv.distanceX;
58− root.target.y = pos.y - priv.distanceY;
60+ root.target.y = Math.max(pos.y - priv.distanceY, PanelState.panelHeight);
5961 }
6062 }
6163
@@ -70,11 +72,12 @@ MouseArea {
7072 }
7173
7274 Row {
73− anchors { left: parent.left; top: parent.top; bottom: parent.bottom; margins: units.gu(0.7) }
75+ anchors { fill: parent; margins: units.gu(0.7) }
7476 spacing: units.gu(1)
7577 opacity: root.active ? 1 : 0.5
7678
7779 WindowControlButtons {
80+ id: buttons
7881 height: parent.height
7982 onClose: root.close();
8083 onMinimize: root.minimize();
@@ -86,9 +89,11 @@ MouseArea {
8689 objectName: "windowDecorationTitle"
8790 color: "#DFDBD2"
8891 height: parent.height
92+ width: parent.width - buttons.width - parent.anchors.rightMargin - parent.anchors.leftMargin
8993 verticalAlignment: Text.AlignVCenter
9094 fontSize: "small"
9195 font.bold: true
96+ elide: Text.ElideRight
9297 }
9398 }
9499}
qml/Stages/WindowResizeArea.qml+7−4
@@ -18,6 +18,7 @@ import QtQuick 2.4
1818import Ubuntu.Components 1.3
1919import Utils 0.1
2020import Unity.Application 0.1 // for Mir.cursorName
21+import "../Components/PanelState"
2122
2223MouseArea {
2324 id: root
@@ -35,6 +36,8 @@ MouseArea {
3536 property int borderThickness: 0
3637 property int minWidth: 0
3738 property int minHeight: 0
39+ property int screenWidth: 0
40+ property int screenHeight: 0
3841
3942 QtObject {
4043 id: priv
@@ -66,10 +69,10 @@ MouseArea {
6669 Component.onCompleted: {
6770 var windowGeometry = windowStateStorage.getGeometry(root.windowId, Qt.rect(target.x, target.y, target.width, target.height))
6871 if (windowGeometry !== undefined) {
69− target.x = windowGeometry.x
70− target.y = windowGeometry.y
71− target.width = windowGeometry.width
72− target.height = windowGeometry.height
72+ target.width = Math.min(windowGeometry.width, root.screenWidth)
73+ target.height = Math.min(windowGeometry.height, root.screenHeight - PanelState.panelHeight)
74+ target.x = Math.max(Math.min(windowGeometry.x, root.screenWidth - target.width), 0)
75+ target.y = Math.max(Math.min(windowGeometry.y, root.screenHeight - target.height), PanelState.panelHeight)
7376 }
7477 var windowState = windowStateStorage.getState(root.windowId, WindowStateStorage.WindowStateNormal)
7578 if (windowState === WindowStateStorage.WindowStateMaximized) {
src/CachingNetworkManagerFactory.cpp+1
@@ -30,6 +30,7 @@ CachingNetworkAccessManager::CachingNetworkAccessManager(QObject *parent)
3030QNetworkReply* CachingNetworkAccessManager::createRequest(Operation op, const QNetworkRequest &request, QIODevice *outgoingData)
3131{
3232 if (m_networkingStatus->status() != ubuntu::connectivity::NetworkingStatus::Status::Online) {
33+ qDebug() << "Not connected to the internet. Request for" << request.url().toString() << "will be served only from the cache.";
3334 QNetworkRequest req(request);
3435 req.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysCache);
3536 return QNetworkAccessManager::createRequest(op, req, outgoingData);
tests/mocks/Unity/Application/ApplicationManager.cpp+1
@@ -465,6 +465,7 @@ void ApplicationManager::buildListOfAvailableApplications()
465465 application->setAppId("libreoffice");
466466 application->setName("LibreOffice");
467467 application->setIconId("libreoffice");
468+ application->setScreenshotId("libreoffice");
468469 application->setIsTouchApp(false);
469470 m_availableApplications.append(application);
470471}
tests/mocks/Unity/CMakeLists.txt+1−1
@@ -7,7 +7,7 @@ add_subdirectory(Notifications)
77add_subdirectory(DashCommunicator)
88
99pkg_search_module(GOBJECT gobject-2.0 REQUIRED)
10−pkg_check_modules(SCOPES_API REQUIRED unity-shell-scopes=7)
10+pkg_check_modules(SCOPES_API REQUIRED unity-shell-scopes=8)
1111
1212include_directories(
1313 ${CMAKE_CURRENT_BINARY_DIR}
tests/mocks/Unity/InputInfo/CMakeLists.txt+7−2
@@ -9,12 +9,17 @@ find_package(Qt5Quick REQUIRED)
99include_directories(
1010 ${CMAKE_CURRENT_SOURCE_DIR}
1111 ${CMAKE_CURRENT_BINARY_DIR}
12+ ${CMAKE_SOURCE_DIR}/plugins/Unity/InputInfo/
1213)
1314
15+add_definitions(-DUNITY_MOCKS)
16+
1417set(MockInputInfo_SOURCES
1518 plugin.cpp
16− mockqinputinfo.cpp
17− mockqdeclarativeinputdeviceinfo.cpp
19+ qinputdeviceinfo_mock.cpp
20+ mockcontroller.cpp
21+ ${CMAKE_SOURCE_DIR}/plugins/Unity/InputInfo/qinputinfo.cpp
22+ ${CMAKE_SOURCE_DIR}/plugins/Unity/InputInfo/qdeclarativeinputdevicemodel.cpp
1823)
1924
2025add_library(MockInputInfo SHARED
tests/mocks/Unity/InputInfo/mockcontroller.cppnew file+35
@@ -0,0 +1,35 @@
1+/*
2+ * Copyright 2015 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "mockcontroller.h"
18+
19+#include "qinputdeviceinfo_mock_p.h"
20+
21+MockController::MockController(QObject *parent):
22+ QObject(parent)
23+{
24+
25+}
26+
27+QInputDevice *MockController::addMockDevice(const QString &devicePath, QInputDevice::InputType type)
28+{
29+ return QInputDeviceManagerPrivate::instance()->addMockDevice(devicePath, type);
30+}
31+
32+void MockController::removeDevice(const QString &devicePath)
33+{
34+ QInputDeviceManagerPrivate::instance()->removeDevice(devicePath);
35+}
tests/mocks/Unity/InputInfo/mockcontroller.hnew file+34
@@ -0,0 +1,34 @@
1+/*
2+ * Copyright 2015 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef MOCKCONTROLLER_H
18+#define MOCKCONTROLLER_H
19+
20+#include <QObject>
21+#include "qinputinfo.h"
22+
23+class MockController: public QObject
24+{
25+ Q_OBJECT
26+public:
27+ MockController(QObject *parent = 0);
28+ ~MockController() = default;
29+
30+ Q_INVOKABLE QInputDevice* addMockDevice(const QString &devicePath, QInputDevice::InputType type);
31+ Q_INVOKABLE void removeDevice(const QString &devicePath);
32+};
33+
34+#endif
tests/mocks/Unity/InputInfo/mockqdeclarativeinputdeviceinfo_p.hdeleted−94
@@ -1,94 +0,0 @@
1−/****************************************************************************
2−**
3−** Copyright (C) 2015 Jolla.
4−** Copyright (C) 2015 Canoncal Ltd.
5−** Contact: http://www.qt-project.org/legal
6−**
7−** This file is part of the QtSystems module of the Qt Toolkit.
8−**
9−** $QT_BEGIN_LICENSE:LGPL$
10−** Commercial License Usage
11−** Licensees holding valid commercial Qt licenses may use this file in
12−** accordance with the commercial license agreement provided with the
13−** Software or, alternatively, in accordance with the terms contained in
14−** a written agreement between you and Digia. For licensing terms and
15−** conditions see http://qt.digia.com/licensing. For further information
16−** use the contact form at http://qt.digia.com/contact-us.
17−**
18−** GNU Lesser General Public License Usage
19−** Alternatively, this file may be used under the terms of the GNU Lesser
20−** General Public License version 2.1 as published by the Free Software
21−** Foundation and appearing in the file LICENSE.LGPL included in the
22−** packaging of this file. Please review the following information to
23−** ensure the GNU Lesser General Public License version 2.1 requirements
24−** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25−**
26−** In addition, as a special exception, Digia gives you certain additional
27−** rights. These rights are described in the Digia Qt LGPL Exception
28−** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29−**
30−** GNU General Public License Usage
31−** Alternatively, this file may be used under the terms of the GNU
32−** General Public License version 3.0 as published by the Free Software
33−** Foundation and appearing in the file LICENSE.GPL included in the
34−** packaging of this file. Please review the following information to
35−** ensure the GNU General Public License version 3.0 requirements will be
36−** met: http://www.gnu.org/copyleft/gpl.html.
37−**
38−**
39−** $QT_END_LICENSE$
40−**
41−****************************************************************************/
42−
43−#ifndef QDECLARATIVEINPUTDEVICEINFO_H
44−#define QDECLARATIVEINPUTDEVICEINFO_H
45−
46−#include <QObject>
47−#include <QAbstractListModel>
48−#include "mockqinputinfo.h"
49−
50−class QDeclarativeInputDeviceInfo : public QAbstractListModel
51−{
52− Q_OBJECT
53− Q_DISABLE_COPY(QDeclarativeInputDeviceInfo)
54−
55−public:
56− enum ItemRoles {
57− ServiceRole = Qt::UserRole + 1
58− };
59−
60− explicit QDeclarativeInputDeviceInfo(QObject *parent = 0);
61− virtual ~QDeclarativeInputDeviceInfo();
62−
63− QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
64− int rowCount(const QModelIndex &parent = QModelIndex()) const;
65−
66− QHash<int, QByteArray> roleNames() const;
67−
68− Q_INVOKABLE int indexOf(const QString &devicePath) const;
69−
70− Q_INVOKABLE QInputDevice *get(int index) const;
71−
72− // For testing
73− Q_INVOKABLE void addMockMouse();
74− Q_INVOKABLE void addMockKeyboard();
75− Q_INVOKABLE void removeMockMouse();
76− Q_INVOKABLE void removeMockKeyboard();
77−
78−Q_SIGNALS:
79− void newDevice(const QString &devicePath);
80− void deviceRemoved(const QString &devicePath);
81−
82−public Q_SLOTS:
83− void updateDeviceList();
84−
85−private:
86− QInputDeviceInfo *deviceInfo;
87− QVector<QInputDevice *> inputDevices;
88−private Q_SLOTS:
89− void addedDevice(const QString &);
90− void removedDevice(const QString &path);
91−
92−};
93−
94−#endif // QDECLARATIVEINPUTDEVICEINFO_H
tests/mocks/Unity/InputInfo/mockqinputinfo.cppdeleted−124
@@ -1,124 +0,0 @@
1−/****************************************************************************
2−**
3−** Copyright (C) 2015 Canonical, Ltd. and/or its subsidiary(-ies).
4−** Contact: http://www.qt-project.org/legal
5−**
6−** This file is part of the QtSystems module of the Qt Toolkit.
7−**
8−** $QT_BEGIN_LICENSE:LGPL$
9−** Commercial License Usage
10−** Licensees holding valid commercial Qt licenses may use this file in
11−** accordance with the commercial license agreement provided with the
12−** Software or, alternatively, in accordance with the terms contained in
13−** a written agreement between you and Digia. For licensing terms and
14−** conditions see http://qt.digia.com/licensing. For further information
15−** use the contact form at http://qt.digia.com/contact-us.
16−**
17−** GNU Lesser General Public License Usage
18−** Alternatively, this file may be used under the terms of the GNU Lesser
19−** General Public License version 2.1 as published by the Free Software
20−** Foundation and appearing in the file LICENSE.LGPL included in the
21−** packaging of this file. Please review the following information to
22−** ensure the GNU Lesser General Public License version 2.1 requirements
23−** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24−**
25−** In addition, as a special exception, Digia gives you certain additional
26−** rights. These rights are described in the Digia Qt LGPL Exception
27−** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28−**
29−** GNU General Public License Usage
30−** Alternatively, this file may be used under the terms of the GNU
31−** General Public License version 3.0 as published by the Free Software
32−** Foundation and appearing in the file LICENSE.GPL included in the
33−** packaging of this file. Please review the following information to
34−** ensure the GNU General Public License version 3.0 requirements will be
35−** met: http://www.gnu.org/copyleft/gpl.html.
36−**
37−**
38−** $QT_END_LICENSE$
39−**
40−****************************************************************************/
41−
42−#include "mockqinputinfo.h"
43−
44−
45−QInputDevice::QInputDevice(QObject *parent) :
46− QObject(parent)
47−{
48−}
49−
50−QString QInputDevice::name() const
51−{
52− return m_name;
53−}
54−
55−void QInputDevice::setName(const QString &name)
56−{
57− m_name = name;
58−}
59−
60−QString QInputDevice::devicePath() const
61−{
62− return m_devicePath;
63−}
64−
65−void QInputDevice::setDevicePath(const QString &path)
66−{
67− m_devicePath = path;
68−}
69−
70−QList <int> QInputDevice::buttons() const
71−{
72− return {1, 2 ,3};
73−}
74−
75−QList <int> QInputDevice::switches() const
76−{
77− return {};
78−}
79−
80−QList <int> QInputDevice::relativeAxis() const
81−{
82− return {};
83−}
84−
85−QList <int> QInputDevice::absoluteAxis() const
86−{
87− return {};
88−}
89−
90−QInputDeviceInfo::InputTypes QInputDevice::types()
91−{
92− return m_types;
93−}
94−
95−void QInputDevice::setTypes(QInputDeviceInfo::InputTypes types)
96−{
97− m_types = types;
98−}
99−
100−QInputDeviceInfo::QInputDeviceInfo(QObject *parent) :
101− QObject(parent)
102−{
103−}
104−
105−QVector <QInputDevice *> QInputDeviceInfo::deviceList()
106−{
107− return m_list;
108−}
109−
110−void QInputDeviceInfo::removeMockDevice(int index)
111−{
112− QInputDevice *device = m_list.takeAt(index);
113− Q_EMIT deviceRemoved(device->devicePath());
114− device->deleteLater();
115−}
116−
117−void QInputDeviceInfo::addMockDevice(QInputDeviceInfo::InputType inputType)
118−{
119− QInputDevice *device = new QInputDevice(this);
120− device->setDevicePath("/mock/device/" + QString::number(m_counter++));
121− device->setTypes({inputType});
122− m_list.append(device);
123− Q_EMIT deviceAdded(device->devicePath());
124−}
tests/mocks/Unity/InputInfo/mockqinputinfo.hdeleted−139
@@ -1,139 +0,0 @@
1−/****************************************************************************
2−**
3−** Copyright (C) 2015 Canonical, Ltd. and/or its subsidiary(-ies).
4−** Contact: http://www.qt-project.org/legal
5−**
6−** This file is part of the QtSystems module of the Qt Toolkit.
7−**
8−** $QT_BEGIN_LICENSE:LGPL$
9−** Commercial License Usage
10−** Licensees holding valid commercial Qt licenses may use this file in
11−** accordance with the commercial license agreement provided with the
12−** Software or, alternatively, in accordance with the terms contained in
13−** a written agreement between you and Digia. For licensing terms and
14−** conditions see http://qt.digia.com/licensing. For further information
15−** use the contact form at http://qt.digia.com/contact-us.
16−**
17−** GNU Lesser General Public License Usage
18−** Alternatively, this file may be used under the terms of the GNU Lesser
19−** General Public License version 2.1 as published by the Free Software
20−** Foundation and appearing in the file LICENSE.LGPL included in the
21−** packaging of this file. Please review the following information to
22−** ensure the GNU Lesser General Public License version 2.1 requirements
23−** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24−**
25−** In addition, as a special exception, Digia gives you certain additional
26−** rights. These rights are described in the Digia Qt LGPL Exception
27−** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28−**
29−** GNU General Public License Usage
30−** Alternatively, this file may be used under the terms of the GNU
31−** General Public License version 3.0 as published by the Free Software
32−** Foundation and appearing in the file LICENSE.GPL included in the
33−** packaging of this file. Please review the following information to
34−** ensure the GNU General Public License version 3.0 requirements will be
35−** met: http://www.gnu.org/copyleft/gpl.html.
36−**
37−**
38−** $QT_END_LICENSE$
39−**
40−****************************************************************************/
41−
42−#ifndef MOCKQINPUTINFO_H
43−#define MOCKQINPUTINFO_H
44−
45−#include <QObject>
46−#include <QVector>
47−#include <QMap>
48−#include <QSocketNotifier>
49−#include <QDebug>
50−
51−class QInputDevicePrivate;
52−class QInputDevice;
53−
54−class QInputDeviceInfoPrivate;
55−class QInputDeviceInfo : public QObject
56−{
57− Q_OBJECT
58− Q_PROPERTY(int deviceCount READ deviceCount)
59−public:
60−
61− enum InputType {
62− Unknown = 0,
63− Button = 1,
64− Mouse = 2,
65− TouchPad = 4,
66− TouchScreen = 8,
67− Keyboard = 16,
68− Switch = 32
69− };
70− Q_ENUMS(InputType)
71− Q_FLAGS(InputTypes)
72− Q_DECLARE_FLAGS(InputTypes, InputType)
73−
74− explicit QInputDeviceInfo(QObject *parent = 0);
75−
76− Q_INVOKABLE QVector <QInputDevice *> deviceList();
77−
78− int deviceCount() { return deviceList().count(); }
79−
80− Q_INVOKABLE void addMockDevice(InputType inputType);
81− Q_INVOKABLE void removeMockDevice(int index);
82−
83−Q_SIGNALS:
84−
85− void deviceAdded(const QString & devicePath);
86− void deviceRemoved(const QString & devicePath);
87−
88−private:
89− QVector<QInputDevice*> m_list;
90−
91− int m_counter = 0;
92−};
93−
94−class QInputDevice : public QObject
95−{
96− friend class QInputDeviceInfoPrivate;
97− Q_OBJECT
98− Q_ENUMS(InputType)
99− Q_FLAGS(InputTypes)
100− Q_PROPERTY(QString name READ name NOTIFY nameChanged)
101− Q_PROPERTY(QString devicePath READ devicePath NOTIFY devicePathChanged)
102− Q_PROPERTY(QList <int> buttons READ buttons NOTIFY buttonsChanged)
103− Q_PROPERTY(QList <int> switches READ switches NOTIFY switchesChanged)
104− Q_PROPERTY(QList <int> relativeAxis READ relativeAxis NOTIFY relativeAxisChanged)
105− Q_PROPERTY(QList <int> absoluteAxis READ absoluteAxis NOTIFY absoluteAxisChanged)
106− Q_PROPERTY(QInputDeviceInfo::InputTypes types READ types NOTIFY typesChanged)
107−
108−public:
109− explicit QInputDevice(QObject *parent = 0);
110−
111− QString name() const;
112− QString devicePath() const;
113− QList <int> buttons() const; //keys event code
114− QList <int> switches() const;
115− QList <int> relativeAxis() const;
116− QList <int> absoluteAxis() const;
117−
118− QInputDeviceInfo::InputTypes types();
119−Q_SIGNALS:
120− void nameChanged();
121− void devicePathChanged();
122− void buttonsChanged();
123− void switchesChanged();
124− void relativeAxisChanged();
125− void absoluteAxisChanged();
126− void typesChanged();
127−private:
128− void setName(const QString &name);
129− void setTypes(QInputDeviceInfo::InputTypes types);
130− void setDevicePath(const QString &path);
131−
132− QString m_name;
133− QInputDeviceInfo::InputTypes m_types;
134− QString m_devicePath;
135−
136− friend class QInputDeviceInfo;
137−};
138−
139−#endif // MOCKQINPUTINFO_H
tests/mocks/Unity/InputInfo/plugin.cpp+17−3
@@ -15,18 +15,32 @@
1515 */
1616
1717// Qt
18−#include <QtQml/qqml.h>
18+#include <QtQml>
1919
2020// self
2121#include "plugin.h"
2222
2323// local
24−#include "mockqdeclarativeinputdeviceinfo_p.h"
24+#include "qdeclarativeinputdevicemodel_p.h"
25+#include "mockcontroller.h"
26+
27+static QObject *backendProvider(QQmlEngine *engine, QJSEngine *scriptEngine)
28+{
29+ Q_UNUSED(engine)
30+ Q_UNUSED(scriptEngine)
31+ return new MockController(engine);
32+}
2533
2634void InputInfoPlugin::registerTypes(const char *uri)
2735{
2836 int major = 0;
2937 int minor = 1;
30− qmlRegisterType<QDeclarativeInputDeviceInfo>(uri, major, minor, "InputDeviceInfo");
38+ qmlRegisterType<QDeclarativeInputDeviceModel>(uri, major, minor, "InputDeviceModel");
3139 qmlRegisterType<QInputDevice>(uri, major, minor, "InputInfo");
40+
41+ // We can't register the MockInputDeviceBackend directly because QML wants to delete singletons on its own
42+ // Given that MockInputDeviceBackend is a Q_GLOBAL_STATIC it will also be cleaned up by other Qt internals
43+ // This leads to a double-free on shutdown. So let's add a proxy to control the MockBackend through QML:
44+ // MockController
45+ qmlRegisterSingletonType<MockController>(uri, major, minor, "MockInputDeviceBackend", backendProvider);
3246}
tests/mocks/Unity/InputInfo/qinputdeviceinfo_mock.cppnew file+52
@@ -0,0 +1,52 @@
1+/*
2+ * Copyright 2015 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "qinputdeviceinfo_mock_p.h"
18+
19+#include <QTimer>
20+#include <QDebug>
21+
22+QInputDeviceManagerPrivate::QInputDeviceManagerPrivate(QObject *parent) :
23+ QObject(parent),
24+ currentFilter(QInputDevice::Unknown)
25+{
26+ QTimer::singleShot(1, SIGNAL(ready()));
27+}
28+
29+QInputDeviceManagerPrivate::~QInputDeviceManagerPrivate()
30+{
31+}
32+
33+QInputDevice *QInputDeviceManagerPrivate::addMockDevice(const QString &devicePath, QInputDevice::InputType type)
34+{
35+ QInputDevice *inputDevice = new QInputDevice(this);
36+ inputDevice->setDevicePath(devicePath);
37+ inputDevice->setName("Mock Device " + devicePath);
38+ inputDevice->setType(type);
39+ deviceMap.insert(devicePath, inputDevice);
40+ Q_EMIT deviceAdded(devicePath);
41+ return inputDevice;
42+}
43+
44+void QInputDeviceManagerPrivate::removeDevice(const QString &path)
45+{
46+ Q_FOREACH (const QString devicePath, deviceMap.keys()) {
47+ if (devicePath.contains(path)) {
48+ deviceMap.remove(devicePath);
49+ Q_EMIT deviceRemoved(devicePath);
50+ }
51+ }
52+}
tests/mocks/Unity/InputInfo/qinputdeviceinfo_mock_p.hnew file+58
@@ -0,0 +1,58 @@
1+/*
2+ * Copyright 2015 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef QINPUTDEVICEINFO_MOCK_H
18+#define QINPUTDEVICEINFO_MOCK_H
19+
20+#include <QObject>
21+#include "qinputinfo.h"
22+
23+class QInputDevicePrivate : public QObject
24+{
25+ Q_OBJECT
26+public:
27+ explicit QInputDevicePrivate(QObject *parent = 0);
28+
29+ QString name;
30+ QString devicePath;
31+ QList <int> buttons; //keys
32+ QList <int> switches;
33+ QList <int> relativeAxis;
34+ QList <int> absoluteAxis;
35+ QInputDevice::InputTypeFlags type;
36+};
37+
38+class QInputDeviceManagerPrivate : public QObject
39+{
40+ Q_OBJECT
41+public:
42+ explicit QInputDeviceManagerPrivate(QObject *parent = 0);
43+ ~QInputDeviceManagerPrivate();
44+ QVector <QInputDevice *> deviceList;
45+ QMap <QString, QInputDevice *> deviceMap;
46+ static QInputDeviceManagerPrivate * instance();
47+ QInputDevice::InputType currentFilter;
48+
49+ Q_INVOKABLE QInputDevice* addMockDevice(const QString &devicePath, QInputDevice::InputType type);
50+ Q_INVOKABLE void removeDevice(const QString &devicePath);
51+
52+Q_SIGNALS:
53+ void deviceAdded(const QString &);
54+ void deviceRemoved(const QString &);
55+ void ready();
56+};
57+
58+#endif // QINPUTDEVICEINFO_MOCK_H
tests/mocks/Unity/Launcher/MockLauncherModel.cpp+3
@@ -75,6 +75,9 @@ MockLauncherModel::MockLauncherModel(QObject* parent): LauncherModelInterface(pa
7575 item = new MockLauncherItem("calendar-app", "/usr/share/applications/calendar-app.desktop","Calendar", "calendar", this);
7676 item->setPinned(true);
7777 m_list.append(item);
78+ item = new MockLauncherItem("libreoffice", "/usr/share/applications/libreoffice.desktop","Libre Office", "libreoffice", this);
79+ item->setPinned(true);
80+ m_list.append(item);
7881}
7982
8083MockLauncherModel::~MockLauncherModel()
tests/mocks/Unity/fake_categories.cpp−5
@@ -32,11 +32,6 @@ int Categories::rowCount(const QModelIndex& /*parent*/) const
3232 return m_category_count;
3333}
3434
35−void Categories::addSpecialCategory(QString const& /*categoryId*/, QString const& /*name*/, QString const& /*icon*/, QString const& /*rawTemplate*/, QObject* /*countObject*/)
36−{
37− qFatal("Using un-implemented Categories::addSpecialCategory");
38−}
39−
4035bool Categories::overrideCategoryJson(QString const& /* categoryId */, QString const& /* json */)
4136{
4237 qFatal("Using un-implemented Categories::overrideCategoryJson");
tests/mocks/Unity/fake_categories.h−1
@@ -34,7 +34,6 @@ public:
3434 QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
3535 int rowCount(const QModelIndex& parent = QModelIndex()) const override;
3636
37− Q_INVOKABLE void addSpecialCategory(QString const& categoryId, QString const& name, QString const& icon, QString const& rawTemplate, QObject* countObject) override;
3837 Q_INVOKABLE bool overrideCategoryJson(QString const& categoryId, QString const& json) override;
3938
4039 Q_INVOKABLE QVariant data(int row, int role) const;
tests/mocks/Unity/fake_scope.cpp+5
@@ -271,6 +271,11 @@ void Scope::refresh()
271271 Q_EMIT refreshed();
272272}
273273
274+void Scope::activateAction(QVariant const& /*result*/, QString const& /*categoryId*/, QString const& /*actionId*/)
275+{
276+ qFatal("Using Scope::activateAction");
277+}
278+
274279unity::shell::scopes::NavigationInterface* Scope::getNavigation(const QString& id)
275280{
276281 if (id.isEmpty())
tests/mocks/Unity/fake_scope.h+2
@@ -79,6 +79,8 @@ public:
7979
8080 Q_INVOKABLE void refresh() override;
8181
82+ Q_INVOKABLE virtual void activateAction(QVariant const& result, QString const& categoryId, QString const& actionId) override;
83+
8284Q_SIGNALS:
8385 // These are not in the Interface, here for testing benefits
8486 void refreshed();
tests/mocks/Unity/fake_scopesoverview.cpp−10
@@ -74,11 +74,6 @@ int ScopesOverviewCategories::rowCount(const QModelIndex& /*parent*/) const
7474 return 2;
7575}
7676
77−void ScopesOverviewCategories::addSpecialCategory(QString const&, QString const&, QString const&, QString const&, QObject*)
78−{
79− qFatal("Using un-implemented ScopesOverviewCategories::addSpecialCategory");
80−}
81−
8277bool ScopesOverviewCategories::overrideCategoryJson(QString const& /* categoryId */, QString const& /* json */)
8378{
8479 qFatal("Using un-implemented ScopesOverviewCategories::overrideCategoryJson");
@@ -175,11 +170,6 @@ int ScopesOverviewSearchCategories::rowCount(const QModelIndex& /*parent*/) cons
175170 return 2;
176171}
177172
178−void ScopesOverviewSearchCategories::addSpecialCategory(QString const&, QString const&, QString const&, QString const&, QObject*)
179−{
180− qFatal("Using un-implemented ScopesOverviewSearchCategories::addSpecialCategory");
181−}
182−
183173bool ScopesOverviewSearchCategories::overrideCategoryJson(QString const& /* categoryId */, QString const& /* json */)
184174{
185175 qFatal("Using un-implemented ScopesOverviewSearchCategories::overrideCategoryJson");
tests/mocks/Unity/fake_scopesoverview.h−2
@@ -53,7 +53,6 @@ public:
5353 QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
5454 int rowCount(const QModelIndex& parent = QModelIndex()) const override;
5555
56− Q_INVOKABLE void addSpecialCategory(QString const& categoryId, QString const& name, QString const& icon, QString const& rawTemplate, QObject* countObject) override;
5756 Q_INVOKABLE bool overrideCategoryJson(QString const& categoryId, QString const& json) override;
5857
5958 // This is implementation detail
@@ -76,7 +75,6 @@ public:
7675 QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
7776 int rowCount(const QModelIndex& parent = QModelIndex()) const override;
7877
79− Q_INVOKABLE void addSpecialCategory(QString const& categoryId, QString const& name, QString const& icon, QString const& rawTemplate, QObject* countObject) override;
8078 Q_INVOKABLE bool overrideCategoryJson(QString const& categoryId, QString const& json) override;
8179
8280private:
tests/mocks/Utils/CMakeLists.txt+2
@@ -18,6 +18,8 @@ set(QMLPLUGIN_SRC
1818 ${CMAKE_SOURCE_DIR}/plugins/Utils/windowscreenshotprovider.cpp
1919 ${CMAKE_SOURCE_DIR}/plugins/Utils/easingcurve.cpp
2020 ${CMAKE_SOURCE_DIR}/plugins/Utils/inputwatcher.cpp
21+ ${CMAKE_SOURCE_DIR}/plugins/Utils/applicationsfiltermodel.cpp
22+ ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h
2123 constants.cpp
2224 plugin.cpp
2325 windowstatestorage.cpp
tests/mocks/Utils/plugin.cpp+2
@@ -35,6 +35,7 @@
3535#include <windowinputfilter.h>
3636#include <windowscreenshotprovider.h>
3737#include <easingcurve.h>
38+#include <applicationsfiltermodel.h>
3839
3940static QObject *createWindowStateStorage(QQmlEngine *engine, QJSEngine *scriptEngine)
4041{
@@ -63,6 +64,7 @@ void FakeUtilsPlugin::registerTypes(const char *uri)
6364 qmlRegisterType<InputWatcher>(uri, 0, 1, "InputWatcher");
6465 qmlRegisterSingletonType<Constants>(uri, 0, 1, "Constants", createConstants);
6566 qmlRegisterType<ActiveFocusLogger>(uri, 0, 1, "ActiveFocusLogger");
67+ qmlRegisterType<ApplicationsFilterModel>(uri, 0, 1, "ApplicationsFilterModel");
6668}
6769
6870void FakeUtilsPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
tests/plugins/AccountsService/AccountsServer.cpp+11−5
@@ -18,13 +18,14 @@
1818 */
1919
2020#include "AccountsServer.h"
21+#include "AccountsUserAdaptor.h"
22+#include "PropertiesAdaptor.h"
2123#include "PropertiesServer.h"
2224#include <QDBusConnection>
2325#include <QDebug>
2426
25−AccountsServer::AccountsServer(PropertiesServer* propServer, QObject *parent)
27+AccountsServer::AccountsServer(QObject *parent)
2628 : QObject(parent)
27− , m_propServer(propServer)
2829{
2930}
3031
@@ -36,16 +37,21 @@ QDBusObjectPath AccountsServer::FindUserByName(const QString &user)
3637bool AccountsServer::AddUser(const QString &user)
3738{
3839 QString path(QString("/%1").arg(user));
39− if (QDBusConnection::sessionBus().objectRegisteredAt(path) == m_propServer)
40+ if (QDBusConnection::sessionBus().objectRegisteredAt(path) != nullptr)
4041 return true;
41− return QDBusConnection::sessionBus().registerObject(path, m_propServer);
42+
43+ auto props = new PropertiesServer(this);
44+ new PropertiesAdaptor(props);
45+ new AccountsUserAdaptor(props);
46+ return QDBusConnection::sessionBus().registerObject(path, props);
4247}
4348
4449bool AccountsServer::RemoveUser(const QString &user)
4550{
4651 QString path(QString("/%1").arg(user));
47− if (QDBusConnection::sessionBus().objectRegisteredAt(path) != m_propServer)
52+ if (QDBusConnection::sessionBus().objectRegisteredAt(path) == nullptr)
4853 return false;
54+
4955 QDBusConnection::sessionBus().unregisterObject(path);
5056 return true;
5157}
tests/plugins/AccountsService/AccountsServer.h+1−6
@@ -25,14 +25,12 @@
2525#include <QObject>
2626#include <QString>
2727
28−class PropertiesServer;
29−
3028class AccountsServer: public QObject, protected QDBusContext
3129{
3230 Q_OBJECT
3331
3432public:
35− explicit AccountsServer(PropertiesServer *propServer, QObject *parent = 0);
33+ explicit AccountsServer(QObject *parent = 0);
3634
3735public Q_SLOTS:
3836
@@ -41,9 +39,6 @@ public Q_SLOTS:
4139 // mock only.
4240 bool AddUser(const QString &user);
4341 bool RemoveUser(const QString &user);
44−
45−private:
46− PropertiesServer* m_propServer;
4742};
4843
4944#endif
tests/plugins/AccountsService/PropertiesServer.cpp+9
@@ -23,10 +23,12 @@
2323// Qt
2424#include <QDebug>
2525#include <QDBusMessage>
26+#include <QDBusMetaType>
2627
2728PropertiesServer::PropertiesServer(QObject *parent)
2829 : QObject(parent)
2930{
31+ qDBusRegisterMetaType<QList<QVariantMap>>();
3032 Reset();
3133}
3234
@@ -47,6 +49,12 @@ void PropertiesServer::Set(const QString &interface, const QString &property, co
4749 if (m_properties[interface].contains(property)) {
4850 QVariant& oldValue = m_properties[interface][property];
4951 if (oldValue != newValue) {
52+ // complex types have an extra layer of wrapping via QDBusArgument
53+ if (interface == QStringLiteral("com.canonical.unity.AccountsService") &&
54+ property == QStringLiteral("LauncherItems")) {
55+ newValue = QVariant::fromValue(qdbus_cast<QList<QVariantMap>>(newValue.value<QDBusArgument>()));
56+ }
57+
5058 oldValue = newValue;
5159
5260 // Special case for Background file.
@@ -72,6 +80,7 @@ void PropertiesServer::Reset()
7280{
7381 m_properties["com.canonical.unity.AccountsService"]["demo-edges"] = false;
7482 m_properties["com.canonical.unity.AccountsService"]["DemoEdgesCompleted"] = QStringList();
83+ m_properties["com.canonical.unity.AccountsService"]["LauncherItems"] = QVariant::fromValue(QList<QVariantMap>());
7584 m_properties["com.canonical.unity.AccountsService.Private"]["FailedLogins"] = 0;
7685 m_properties["com.ubuntu.touch.AccountsService.SecurityPrivacy"]["StatsWelcomeScreen"] = true;
7786 m_properties["com.ubuntu.AccountsService.SecurityPrivacy"]["EnableLauncherWhileLocked"] = true;
tests/plugins/AccountsService/client.cpp+6−8
@@ -57,8 +57,6 @@ private Q_SLOTS:
5757 m_userInterface = new QDBusInterface("org.freedesktop.Accounts",
5858 QString("/%1").arg(QTest::currentTestFunction()),
5959 "org.freedesktop.DBus.Properties", QDBusConnection::sessionBus(), this);
60− QDBusReply<void> resetReply = m_userInterface->call("Reset");
61− QVERIFY(resetReply.isValid());
6260
6361 QVERIFY(QObject::connect(m_userInterface, SIGNAL(PropertiesChanged(QString, QVariantMap, QStringList)),
6462 this, SIGNAL(propertiesChanged(QString, QVariantMap, QStringList))));
@@ -79,17 +77,17 @@ private Q_SLOTS:
7977 {
8078 // Test various invalid calls
8179 AccountsServiceDBusAdaptor session;
82− QCOMPARE(session.getUserProperty("NOPE", "com.canonical.unity.AccountsService", "demo-edges"), QVariant());
83− QCOMPARE(session.getUserProperty(QTest::currentTestFunction(), "com.canonical.unity.AccountsService", "NOPE"), QVariant());
80+ QCOMPARE(session.getUserPropertyAsync("NOPE", "com.canonical.unity.AccountsService", "demo-edges").value(), QVariant());
81+ QCOMPARE(session.getUserPropertyAsync(QTest::currentTestFunction(), "com.canonical.unity.AccountsService", "NOPE").value(), QVariant());
8482 }
8583
8684 void testGetSetServiceDBusAdaptor()
8785 {
8886 AccountsServiceDBusAdaptor session;
89− session.setUserProperty(QTest::currentTestFunction(), "com.canonical.unity.AccountsService", "demo-edges", QVariant(true));
90− QCOMPARE(session.getUserProperty(QTest::currentTestFunction(), "com.canonical.unity.AccountsService", "demo-edges"), QVariant(true));
91− session.setUserProperty(QTest::currentTestFunction(), "com.canonical.unity.AccountsService", "demo-edges", QVariant(false));
92− QCOMPARE(session.getUserProperty(QTest::currentTestFunction(), "com.canonical.unity.AccountsService", "demo-edges"), QVariant(false));
87+ session.setUserPropertyAsync(QTest::currentTestFunction(), "com.canonical.unity.AccountsService", "demo-edges", QVariant(true)).waitForFinished();
88+ QCOMPARE(session.getUserPropertyAsync(QTest::currentTestFunction(), "com.canonical.unity.AccountsService", "demo-edges").value(), QVariant(true));
89+ session.setUserPropertyAsync(QTest::currentTestFunction(), "com.canonical.unity.AccountsService", "demo-edges", QVariant(false)).waitForFinished();
90+ QCOMPARE(session.getUserPropertyAsync(QTest::currentTestFunction(), "com.canonical.unity.AccountsService", "demo-edges").value(), QVariant(false));
9391 }
9492
9593 void testGetSetService()
tests/plugins/AccountsService/server.cpp+1−7
@@ -19,11 +19,9 @@
1919
2020#include "AccountsAdaptor.h"
2121#include "AccountsServer.h"
22−#include "PropertiesAdaptor.h"
2322#include "PropertiesServer.h"
2423#include "SecurityPrivacyAdaptor.h"
2524#include "LocationAdaptor.h"
26−#include "AccountsUserAdaptor.h"
2725#include "AccountsPrivateAdaptor.h"
2826#include <QCoreApplication>
2927
@@ -31,11 +29,7 @@ int main(int argc, char *argv[])
3129{
3230 QCoreApplication a(argc, argv);
3331
34− auto props = new PropertiesServer(&a);
35− new PropertiesAdaptor(props);
36− new AccountsUserAdaptor(props);
37−
38− auto accounts = new AccountsServer(props, &a);
32+ auto accounts = new AccountsServer(&a);
3933 new AccountsAdaptor(accounts);
4034 new SecurityPrivacyAdaptor(accounts);
4135 new LocationAdaptor(accounts);
tests/plugins/Greeter/Unity/Launcher/AccountsServiceDBusAdaptor.cppdeleted−48
@@ -1,48 +0,0 @@
1−/*
2− * Copyright (C) 2014-2015 Canonical, Ltd.
3− *
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU General Public License as published by
6− * the Free Software Foundation; version 3.
7− *
8− * This program is distributed in the hope that it will be useful,
9− * but WITHOUT ANY WARRANTY; without even the implied warranty of
10− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
12− *
13− * You should have received a copy of the GNU General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− */
16−
17−#include "AccountsServiceDBusAdaptor.h"
18−#include <QDebug>
19−#include <QDBusArgument>
20−#include <QDBusMessage>
21−#include <QDBusMetaType>
22−
23−AccountsServiceDBusAdaptor::AccountsServiceDBusAdaptor(QObject* parent)
24− : QObject(parent)
25−{
26− qDBusRegisterMetaType<QList<QVariantMap>>();
27−}
28−
29−QVariant AccountsServiceDBusAdaptor::getUserProperty(const QString &user, const QString &interface, const QString &property)
30−{
31− Q_UNUSED(interface)
32− Q_UNUSED(property) // We only fake one property here (LauncherItems)
33−
34− QDBusMessage msg;
35− QVariant v = QVariant::fromValue(mockProperties.value(user));
36− QDBusVariant dv(v);
37− QVariant packed = QVariant::fromValue(dv);
38− msg << packed;
39−
40− return packed.value<QDBusArgument>().asVariant();
41−}
42−
43−void AccountsServiceDBusAdaptor::simulatePropertyChange(const QString &user, const QString &property, const QVariant &value)
44−{
45− Q_ASSERT(property == "LauncherItems");
46− mockProperties[user] = value.value<QList<QVariantMap>>();
47− Q_EMIT propertiesChanged(user, "com.canonical.unity.AccountsService", QStringList() << property);
48−}
tests/plugins/Greeter/Unity/Launcher/AccountsServiceDBusAdaptor.hdeleted−53
@@ -1,53 +0,0 @@
1−/*
2− * Copyright (C) 2014-2015 Canonical, Ltd.
3− *
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU General Public License as published by
6− * the Free Software Foundation; version 3.
7− *
8− * This program is distributed in the hope that it will be useful,
9− * but WITHOUT ANY WARRANTY; without even the implied warranty of
10− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
12− *
13− * You should have received a copy of the GNU General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− */
16−
17−#ifndef UNITY_ACCOUNTSSERVICEDBUSADAPTOR_H
18−#define UNITY_ACCOUNTSSERVICEDBUSADAPTOR_H
19−
20−#include <QMap>
21−#include <QObject>
22−#include <QString>
23−#include <QHash>
24−#include <QDBusArgument>
25−
26−extern QHash<QString, QList<QVariantMap>> mockProperties;
27−
28−class AccountsServiceDBusAdaptor: public QObject
29−{
30− Q_OBJECT
31−
32−public:
33− explicit AccountsServiceDBusAdaptor(QObject *parent = 0);
34−
35− Q_INVOKABLE QVariant getUserProperty(const QString &user, const QString &interface, const QString &property);
36− template <typename T>
37− inline T getUserProperty(const QString &user, const QString &interface, const QString &property) {
38− Q_UNUSED(interface)
39− Q_ASSERT(property == "LauncherItems");
40− T ret = mockProperties[user];
41− return ret;
42− }
43−
44−Q_SIGNALS:
45− void propertiesChanged(const QString &user, const QString &interface, const QStringList &changed);
46−
47−private:
48− void simulatePropertyChange(const QString &user, const QString &property, const QVariant &value);
49−
50− friend class LauncherModelASTest;
51−};
52−
53−#endif
tests/plugins/Greeter/Unity/Launcher/CMakeLists.txt+8−2
@@ -3,6 +3,7 @@ pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=7)
33include_directories(
44 ${CMAKE_CURRENT_SOURCE_DIR}
55 ${CMAKE_CURRENT_BINARY_DIR}
6+ ${CMAKE_SOURCE_DIR}/plugins/AccountsService
67 ${CMAKE_SOURCE_DIR}/plugins/Greeter/Unity/Launcher
78 ${libunity8-private_SOURCE_DIR}
89 )
@@ -14,7 +15,7 @@ add_definitions(-DLAUNCHER_TESTING)
1415### LauncherModelASTest
1516add_executable(launchermodelastestExec
1617 launchermodelastest.cpp
17− AccountsServiceDBusAdaptor.cpp
18+ ${CMAKE_SOURCE_DIR}/plugins/AccountsService/AccountsServiceDBusAdaptor.cpp
1819 ${CMAKE_SOURCE_DIR}/plugins/Greeter/Unity/Launcher/launchermodelas.cpp
1920 ${CMAKE_SOURCE_DIR}/plugins/Greeter/Unity/Launcher/launcheritem.cpp
2021 ${CMAKE_SOURCE_DIR}/plugins/Greeter/Unity/Launcher/quicklistmodel.cpp
@@ -29,10 +30,15 @@ target_link_libraries(launchermodelastestExec
2930 unity8-private
3031 ${GSETTINGS_QT_LDFLAGS}
3132 )
33+add_dependencies(launchermodelastestExec mock-server)
3234qt5_use_modules(launchermodelastestExec Test Core DBus Gui)
3335add_unity8_unittest(LauncherModelAS dbus-test-runner
3436 ARG_PREFIX "--parameter"
35− ARGS --task $<TARGET_FILE:launchermodelastestExec>
37+ ARGS
38+ --task $<TARGET_FILE:mock-server>
39+ --ignore-return
40+ --task $<TARGET_FILE:launchermodelastestExec>
41+ --wait-for org.freedesktop.Accounts
3642)
3743
3844# copy .desktop files into build directory for shadow builds
tests/plugins/Greeter/Unity/Launcher/launchermodelastest.cpp+81−16
@@ -22,9 +22,12 @@
2222#include "launchermodelas.h"
2323#include "AccountsServiceDBusAdaptor.h"
2424
25+#include <QDBusInterface>
26+#include <QDBusMetaType>
27+#include <QDBusReply>
28+#include <QSignalSpy>
2529#include <QtTest>
2630
27−QHash<QString, QList<QVariantMap>> mockProperties;
2831
2932class LauncherModelASTest : public QObject
3033{
@@ -32,9 +35,15 @@ class LauncherModelASTest : public QObject
3235
3336private:
3437 LauncherModel *launcherModel;
38+ QStringList users;
39+ QHash<QString, QList<QVariantMap>> mockProperties;
3540
3641private Q_SLOTS:
3742
43+ void initTestCase() {
44+ qDBusRegisterMetaType<QList<QVariantMap>>();
45+ }
46+
3847 void init() {
3948 // Prepare some fake list
4049 QList<QVariantMap> list;
@@ -50,7 +59,63 @@ private Q_SLOTS:
5059 item["id"] = "appId2";
5160 item["name"] = "Item 2";
5261 list.append(item);
53− mockProperties["user1"] = list;
62+ setASLauncherItems("user1", list);
63+ }
64+
65+ void cleanup() {
66+ Q_FOREACH(QString user, users) {
67+ QDBusInterface accountsInterface(QStringLiteral("org.freedesktop.Accounts"),
68+ QStringLiteral("/org/freedesktop/Accounts"),
69+ QStringLiteral("org.freedesktop.Accounts"));
70+ QDBusReply<bool> removeReply = accountsInterface.call(QStringLiteral("RemoveUser"),
71+ user);
72+ QVERIFY(removeReply.isValid());
73+ QCOMPARE(removeReply.value(), true);
74+ }
75+
76+ users.clear();
77+ mockProperties.clear();
78+ }
79+
80+ void addASUser(const QString &user)
81+ {
82+ QDBusInterface accountsInterface(QStringLiteral("org.freedesktop.Accounts"),
83+ QStringLiteral("/org/freedesktop/Accounts"),
84+ QStringLiteral("org.freedesktop.Accounts"));
85+ QDBusReply<bool> addReply = accountsInterface.call(QStringLiteral("AddUser"),
86+ user);
87+ QVERIFY(addReply.isValid());
88+ QCOMPARE(addReply.value(), true);
89+
90+ users << user;
91+ mockProperties[user] = QList<QVariantMap>();
92+ }
93+
94+ void setASLauncherItems(const QString &user, const QList<QVariantMap> &items)
95+ {
96+ if (!users.contains(user)) {
97+ addASUser(user);
98+ }
99+
100+ QDBusInterface userInterface(QStringLiteral("org.freedesktop.Accounts"),
101+ QString("/%1").arg(user),
102+ QStringLiteral("org.freedesktop.DBus.Properties"));
103+
104+ QVariant wrapped = QVariant::fromValue(QDBusVariant(QVariant::fromValue(items)));
105+ QDBusReply<void> setReply = userInterface.call(QStringLiteral("Set"),
106+ QStringLiteral("com.canonical.unity.AccountsService"),
107+ QStringLiteral("LauncherItems"),
108+ wrapped);
109+ QVERIFY(setReply.isValid());
110+
111+ // Record what we set for comparison's sake
112+ mockProperties[user] = items;
113+ }
114+
115+ void setUser(LauncherModel *model, const QString &user) {
116+ QSignalSpy spy(model, &QAbstractItemModel::rowsInserted);
117+ model->setUser(user);
118+ QTRY_COMPARE(spy.count(), mockProperties[user].count());
54119 }
55120
56121 bool isInSync(LauncherModel *model, const QList<QVariantMap> &properties) {
@@ -81,7 +146,7 @@ private Q_SLOTS:
81146 // We didn't set a user yet. model should be empty
82147 QCOMPARE(model->rowCount(), 0);
83148
84− model->setUser("user1");
149+ setUser(model, "user1");
85150
86151 QCOMPARE(isInSync(model, mockProperties["user1"]), true);
87152 model->deleteLater();
@@ -102,7 +167,7 @@ private Q_SLOTS:
102167 QFETCH(bool, inSync);
103168
104169 LauncherModel* model = new LauncherModel(this);
105− model->setUser(modelUser);
170+ setUser(model, modelUser);
106171
107172 int oldCount = mockProperties[modelUser].count();
108173 QCOMPARE(model->rowCount(), oldCount);
@@ -117,17 +182,17 @@ private Q_SLOTS:
117182 newEntry.insert("countVisible", false);
118183 newEntry.insert("pinned", true);
119184 newList.append(newEntry);
120− model->m_accounts->simulatePropertyChange(changedUser, "LauncherItems", QVariant::fromValue(newList));
185+ setASLauncherItems(changedUser, newList);
121186
122− QCOMPARE(isInSync(model, mockProperties[modelUser]), true);
123− QCOMPARE(isInSync(model, mockProperties[changedUser]), inSync);
187+ QTRY_COMPARE(isInSync(model, mockProperties[modelUser]), true);
188+ QTRY_COMPARE(isInSync(model, mockProperties[changedUser]), inSync);
124189
125190 model->deleteLater();
126191 }
127192
128193 void testUpdateCount() {
129194 LauncherModel* model = new LauncherModel(this);
130− model->setUser("user1");
195+ setUser(model, "user1");
131196
132197 QCOMPARE(model->get(0)->countVisible(), false);
133198 QCOMPARE(model->get(0)->count(), 0);
@@ -137,34 +202,34 @@ private Q_SLOTS:
137202 entry["countVisible"] = true;
138203 entry["count"] = 55;
139204 newList[0] = entry;
140− model->m_accounts->simulatePropertyChange("user1", "LauncherItems", QVariant::fromValue(newList));
205+ setASLauncherItems("user1", newList);
141206
142− QCOMPARE(isInSync(model, mockProperties["user1"]), true);
207+ QTRY_COMPARE(isInSync(model, mockProperties["user1"]), true);
143208
144209 model->deleteLater();
145210 }
146211
147212 void testOnlyPinned() {
148213 LauncherModel *model = new LauncherModel(this);
149− model->setUser("user1");
214+ setUser(model, "user1");
150215 model->setOnlyPinned(true);
151216
152− QCOMPARE(isInSync(model, mockProperties["user1"]), true);
217+ QTRY_COMPARE(isInSync(model, mockProperties["user1"]), true);
153218
154219 // Let's unpin one item
155220 QList<QVariantMap> newList = mockProperties["user1"];
156221 QVariantMap entry = newList.at(0);
157222 entry["pinned"] = false;
158223 newList[0] = entry;
159− model->m_accounts->simulatePropertyChange("user1", "LauncherItems", QVariant::fromValue(newList));
160− QCOMPARE(isInSync(model, mockProperties["user1"]), true);
224+ setASLauncherItems("user1", newList);
225+ QTRY_COMPARE(isInSync(model, mockProperties["user1"]), true);
161226
162227 // Now toggle onlyPinned and make sure the model keeps up
163228 model->setOnlyPinned(false);
164− QCOMPARE(isInSync(model, mockProperties["user1"]), true);
229+ QTRY_COMPARE(isInSync(model, mockProperties["user1"]), true);
165230
166231 model->setOnlyPinned(true);
167− QCOMPARE(isInSync(model, mockProperties["user1"]), true);
232+ QTRY_COMPARE(isInSync(model, mockProperties["user1"]), true);
168233 }
169234};
170235
tests/plugins/Unity/Launcher/AccountsServiceDBusAdaptor.cppdeleted−42
@@ -1,42 +0,0 @@
1−/*
2− * Copyright (C) 2014-2015 Canonical, Ltd.
3− *
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU General Public License as published by
6− * the Free Software Foundation; version 3.
7− *
8− * This program is distributed in the hope that it will be useful,
9− * but WITHOUT ANY WARRANTY; without even the implied warranty of
10− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
12− *
13− * You should have received a copy of the GNU General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− */
16−
17−#include "AccountsServiceDBusAdaptor.h"
18−#include <QDebug>
19−
20−AccountsServiceDBusAdaptor::AccountsServiceDBusAdaptor(QObject* parent)
21− : QObject(parent)
22−{
23−}
24−
25−QVariant AccountsServiceDBusAdaptor::getUserProperty(const QString &user, const QString &interface, const QString &property)
26−{
27− Q_UNUSED(user)
28− Q_UNUSED(interface)
29− return m_properties.value(property);
30−}
31−
32−void AccountsServiceDBusAdaptor::setUserProperty(const QString &user, const QString &interface, const QString &property, const QVariant &value)
33−{
34− Q_UNUSED(user)
35− Q_UNUSED(interface)
36− m_properties[property] = value;
37−}
38−
39−void AccountsServiceDBusAdaptor::setUserPropertyAsync(const QString &user, const QString &interface, const QString &property, const QVariant &value)
40−{
41− setUserProperty(user, interface, property, value);
42−}
tests/plugins/Unity/Launcher/AccountsServiceDBusAdaptor.hdeleted−47
@@ -1,47 +0,0 @@
1−/*
2− * Copyright (C) 2014-2015 Canonical, Ltd.
3− *
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU General Public License as published by
6− * the Free Software Foundation; version 3.
7− *
8− * This program is distributed in the hope that it will be useful,
9− * but WITHOUT ANY WARRANTY; without even the implied warranty of
10− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
12− *
13− * You should have received a copy of the GNU General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− */
16−
17−#ifndef UNITY_ACCOUNTSSERVICEDBUSADAPTOR_H
18−#define UNITY_ACCOUNTSSERVICEDBUSADAPTOR_H
19−
20−#include <QMap>
21−#include <QObject>
22−#include <QString>
23−#include <QHash>
24−
25−class AccountsServiceDBusAdaptor: public QObject
26−{
27− Q_OBJECT
28−
29−public:
30− explicit AccountsServiceDBusAdaptor(QObject *parent = 0);
31−
32− Q_INVOKABLE QVariant getUserProperty(const QString &user, const QString &interface, const QString &property);
33− Q_INVOKABLE void setUserProperty(const QString &user, const QString &interface, const QString &property, const QVariant &value);
34− Q_INVOKABLE void setUserPropertyAsync(const QString &user, const QString &interface, const QString &property, const QVariant &value);
35−
36−Q_SIGNALS:
37− void propertiesChanged(const QString &user, const QString &interface, const QStringList &changed);
38− void maybeChanged(const QString &user); // Standard properties might have changed
39−
40−private:
41− void simulatePropertyChange(const QString &property, const QVariant &value);
42−
43−private:
44− QHash<QString, QVariant> m_properties;
45−};
46−
47−#endif
tests/plugins/Unity/Launcher/CMakeLists.txt+8−2
@@ -4,6 +4,7 @@ pkg_check_modules(LAUNCHER_API REQUIRED unity-shell-launcher=7)
44include_directories(
55 ${CMAKE_CURRENT_SOURCE_DIR}
66 ${CMAKE_CURRENT_BINARY_DIR}
7+ ${CMAKE_SOURCE_DIR}/plugins/AccountsService
78 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher
89 ${GSETTINGS_QT_INCLUDE_DIRS}
910 ${libunity8-private_SOURCE_DIR}
@@ -17,7 +18,7 @@ add_definitions(-DLAUNCHER_TESTING)
1718add_executable(launchermodeltestExec
1819 launchermodeltest.cpp
1920 gsettings.cpp
20− AccountsServiceDBusAdaptor.cpp
21+ ${CMAKE_SOURCE_DIR}/plugins/AccountsService/AccountsServiceDBusAdaptor.cpp
2122 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/launchermodel.cpp
2223 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/asadapter.cpp
2324 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/launcheritem.cpp
@@ -35,11 +36,16 @@ target_link_libraries(launchermodeltestExec
3536 unity8-private
3637 ${GSETTINGS_QT_LDFLAGS}
3738 )
39+add_dependencies(launchermodeltestExec mock-server)
3840qt5_use_modules(launchermodeltestExec Test Core DBus Xml Gui)
3941
4042add_unity8_unittest(LauncherModel dbus-test-runner
4143 ARG_PREFIX "--parameter"
42− ARGS --task $<TARGET_FILE:launchermodeltestExec>
44+ ARGS
45+ --task $<TARGET_FILE:mock-server>
46+ --ignore-return
47+ --task $<TARGET_FILE:launchermodeltestExec>
48+ --wait-for org.freedesktop.Accounts
4349)
4450
4551# copy .desktop files into build directory for shadow builds
tests/plugins/Unity/Launcher/launchermodeltest.cpp+23−1
@@ -31,6 +31,7 @@
3131#include <QtTest>
3232#include <QDBusInterface>
3333#include <QDBusReply>
34+#include <QDBusMetaType>
3435#include <QDomDocument>
3536
3637// This is a mock, specifically to test the LauncherModel
@@ -136,12 +137,17 @@ private:
136137
137138 QList<QVariantMap> getASConfig() {
138139 AccountsServiceDBusAdaptor *as = launcherModel->m_asAdapter->m_accounts;
139− return as->getUserProperty("", "", "LauncherItems").value<QList<QVariantMap>>();
140+ QDBusReply<QVariant> reply = as->getUserPropertyAsync(QString(qgetenv("USER")),
141+ "com.canonical.unity.AccountsService",
142+ "LauncherItems");
143+ return qdbus_cast<QList<QVariantMap>>(reply.value().value<QDBusArgument>());
140144 }
141145
142146private Q_SLOTS:
143147
144148 void initTestCase() {
149+ qDBusRegisterMetaType<QList<QVariantMap>>();
150+
145151 launcherModel = new LauncherModel(this);
146152 QCoreApplication::processEvents(); // to let the model register on DBus
147153 QCOMPARE(launcherModel->rowCount(QModelIndex()), 0);
@@ -152,6 +158,14 @@ private Q_SLOTS:
152158
153159 // Adding 2 apps to the mock appmanager. Both should appear in the launcher.
154160 void init() {
161+ QDBusInterface accountsInterface(QStringLiteral("org.freedesktop.Accounts"),
162+ QStringLiteral("/org/freedesktop/Accounts"),
163+ QStringLiteral("org.freedesktop.Accounts"));
164+ QDBusReply<bool> addReply = accountsInterface.call(QStringLiteral("AddUser"),
165+ QString(qgetenv("USER")));
166+ QVERIFY(addReply.isValid());
167+ QCOMPARE(addReply.value(), true);
168+
155169 appManager->addApplication(new MockApp("abs-icon"));
156170 QCOMPARE(launcherModel->rowCount(QModelIndex()), 1);
157171
@@ -169,6 +183,14 @@ private Q_SLOTS:
169183 while (launcherModel->rowCount(QModelIndex()) > 0) {
170184 launcherModel->requestRemove(launcherModel->get(0)->appId());
171185 }
186+
187+ QDBusInterface accountsInterface(QStringLiteral("org.freedesktop.Accounts"),
188+ QStringLiteral("/org/freedesktop/Accounts"),
189+ QStringLiteral("org.freedesktop.Accounts"));
190+ QDBusReply<bool> removeReply = accountsInterface.call(QStringLiteral("RemoveUser"),
191+ QString(qgetenv("USER")));
192+ QVERIFY(removeReply.isValid());
193+ QCOMPARE(removeReply.value(), true);
172194 }
173195
174196 void testMove() {
tests/qmltests/CMakeLists.txt+1
@@ -11,6 +11,7 @@ add_unity8_qmltest(Components Dialogs LIGHTDM)
1111add_unity8_qmltest(Components DraggingArea)
1212add_unity8_qmltest(Components LazyImage)
1313add_unity8_qmltest(Components Lockscreen)
14+add_unity8_qmltest(Components ModeSwitchWarningDialog)
1415add_unity8_qmlunittest(Components PhysicalKeysMapper)
1516add_unity8_qmltest(Components Rating)
1617add_unity8_qmltest(Components ResponsiveGridView)
tests/qmltests/Components/tst_ModeSwitchWarningDialog.qmlnew file+75
@@ -0,0 +1,75 @@
1+/*
2+ * Copyright 2015 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.4
18+import QtTest 1.0
19+import "../../../qml/Components"
20+import Unity.Test 0.1 as UT
21+import Ubuntu.Components 1.3
22+import Ubuntu.Components.Popups 1.3
23+
24+Rectangle {
25+ id: root
26+ width: units.gu(40)
27+ height: units.gu(70)
28+ color: "lightgrey"
29+
30+ ListModel {
31+ id: fakeModel
32+ ListElement { icon: "../graphics/applicationIcons/facebook.png"; name: "Facebook" }
33+ ListElement { icon: "../graphics/applicationIcons/gmail.png"; name: "Mail" }
34+ }
35+
36+ function openWarning() {
37+ return PopupUtils.open(Qt.resolvedUrl("../../../qml/Components/ModeSwitchWarningDialog.qml"), root, { model: fakeModel });
38+ }
39+
40+ Button {
41+ anchors.centerIn: parent
42+ text: "trigger warning"
43+ onClicked: {
44+ openWarning();
45+ }
46+ }
47+
48+ SignalSpy {
49+ id: spy
50+ signalName: "forceClose"
51+ }
52+
53+ UT.UnityTestCase {
54+ name: "ModeSwitchWarning"
55+ when: windowShown
56+
57+ function test_labelChangesOnClick() {
58+ var popup = openWarning();
59+ var reconnectLabel = findChild(popup, "reconnectLabel");
60+ tryCompare(reconnectLabel, "text", reconnectLabel.notClickedText)
61+ mouseClick(reconnectLabel, reconnectLabel.width / 2, reconnectLabel.height / 2)
62+ tryCompare(reconnectLabel, "text", reconnectLabel.clickedText)
63+ }
64+
65+ function test_closeButtonEmitsSignal() {
66+ var popup = openWarning();
67+ spy.target = popup;
68+ spy.clear();
69+ var closeButton = findChild(popup, "forceCloseButton");
70+ mouseClick(closeButton, closeButton.width / 2, closeButton.height / 2)
71+ tryCompare(spy, "count", 1)
72+
73+ }
74+ }
75+}
tests/qmltests/Dash/tst_CardTool.qml+6−6
@@ -107,8 +107,8 @@ Rectangle {
107107 Repeater {
108108 model: [
109109 { label: "View width", value: cardTool.viewWidth && cardTool.viewWidth / units.gu(1) },
110− { label: "Card width", value: cardTool.cardWidth && cardTool.cardWidth / units.gu(1) },
111− { label: "Card height", value: cardTool.cardHeight && cardTool.cardHeight / units.gu(1) },
110+ { label: "Card width", value: cardTool.cardWidth !== -1 ? cardTool.cardWidth / units.gu(1) : undefined },
111+ { label: "Card height", value: cardTool.cardHeight !== -1 ? cardTool.cardHeight / units.gu(1) : undefined },
112112 ]
113113
114114 delegate: Row {
@@ -265,10 +265,10 @@ Rectangle {
265265 { tag: "Small", width: units.gu(12), height: function() { return internalCard ? internalCard.height : 0 }, index: 7 },
266266 { tag: "Large", width: units.gu(38), height: function() { return internalCard ? internalCard.height : 0 }, index: 8 },
267267 { tag: "Horizontal", width: units.gu(38), height: function() { return internalCard ? internalCard.height : 0 }, index: 9 },
268− { tag: "OrganicGrid", width: undefined, height: undefined, index: 0, layout_index: 1},
269− { tag: "Journal", width: undefined, height: units.gu(20), size: 20, index: 0, layout_index: 2 },
270− { tag: "OversizedJournal", width: undefined, height: units.gu(18.5), size: 40, index: 0, layout_index: 2 },
271− { tag: "VerticalJournal", width: units.gu(18), height: undefined, index: 0, layout_index: 3 },
268+ { tag: "OrganicGrid", width: -1, height: -1, index: 0, layout_index: 1},
269+ { tag: "Journal", width: -1, height: units.gu(20), size: 20, index: 0, layout_index: 2 },
270+ { tag: "OversizedJournal", width: -1, height: units.gu(18.5), size: 40, index: 0, layout_index: 2 },
271+ { tag: "VerticalJournal", width: units.gu(18), height: -1, index: 0, layout_index: 3 },
272272 { tag: "SmallCarousel", width: units.gu(18), height: units.gu(18), viewWidth: 30, index: 0, layout_index: 4},
273273 { tag: "MediumCarousel", width: units.gu(22), height: units.gu(22), viewWidth: 84, index: 0, layout_index: 4},
274274 { tag: "LargeCarousel", width: units.gu(26), height: units.gu(26), viewWidth: 140, index: 0, layout_index: 4},
tests/qmltests/Notifications/tst_Notifications.qml+5−6
@@ -19,7 +19,6 @@ import QtQuick.Layouts 1.1
1919import QtTest 1.0
2020import ".."
2121import "../../../qml/Notifications"
22−import "../../../qml/Components/UnityInputInfo"
2322import Ubuntu.Components 1.3
2423import Unity.Test 0.1
2524import Unity.Notifications 1.0
@@ -188,7 +187,7 @@ Item {
188187
189188 anchors.fill: parent
190189 model: mockModel
191− hasMouse: UnityInputInfo.mice > 0 // for testing, we add/remove only a mock mouse
190+ hasMouse: false
192191 }
193192 }
194193
@@ -264,9 +263,9 @@ Item {
264263 id: fakeMouseCB
265264 onClicked: {
266265 if (checked) {
267− UnityInputInfo.inputInfo.addMockMouse();
266+ notifications.hasMouse = true;
268267 } else {
269− UnityInputInfo.inputInfo.removeMockMouse();
268+ notifications.hasMouse = false;
270269 }
271270 }
272271 }
@@ -695,7 +694,7 @@ Item {
695694 }
696695
697696 function test_clickToClose_data() { // reuse the data
698− UnityInputInfo.inputInfo.addMockMouse();
697+ notifications.hasMouse = true;
699698 return test_NotificationRenderer_data();
700699 }
701700
@@ -726,7 +725,7 @@ Item {
726725 }
727726
728727 function cleanupTestCase() {
729− UnityInputInfo.inputInfo.removeMockMouse();
728+ notifications.hasMouse = false;
730729 }
731730 }
732731 }
tests/qmltests/Notifications/tst_SwipeToAct.qml+5−6
@@ -19,7 +19,6 @@ import QtQuick.Layouts 1.1
1919import QtTest 1.0
2020import ".."
2121import "../../../qml/Notifications"
22−import "../../../qml/Components/UnityInputInfo"
2322import Ubuntu.Components 1.3
2423import Unity.Test 0.1
2524import Unity.Notifications 1.0
@@ -112,7 +111,7 @@ Item {
112111
113112 anchors.fill: parent
114113 model: mockModel
115− hasMouse: UnityInputInfo.mice > 0 // for testing, we add/remove only a mock mouse
114+ hasMouse: false
116115 }
117116 }
118117
@@ -152,9 +151,9 @@ Item {
152151 id: fakeMouseCB
153152 onClicked: {
154153 if (checked) {
155− UnityInputInfo.inputInfo.addMockMouse();
154+ notifications.hasMouse = true;
156155 } else {
157− UnityInputInfo.inputInfo.removeMockMouse();
156+ notifications.hasMouse = false;
158157 }
159158 }
160159 }
@@ -301,7 +300,7 @@ Item {
301300 }
302301
303302 // add a mock mouse, test clicking the left/right buttons
304− UnityInputInfo.inputInfo.addMockMouse();
303+ notifications.hasMouse = true;
305304 var leftButton = findChild(swipeButton, "leftButton");
306305 var rightButton = findChild(swipeButton, "rightButton");
307306
@@ -316,7 +315,7 @@ Item {
316315 compare(actionSpy.signalArguments[0][0], data.actions.data(1, ActionModel.RoleActionId), "got wrong id for negative action");
317316 actionSpy.clear();
318317 }
319− UnityInputInfo.inputInfo.removeMockMouse();
318+ notifications.hasMouse = false;
320319 }
321320 }
322321 }
tests/qmltests/Stages/tst_DesktopStage.qml+139−32
@@ -60,14 +60,10 @@ Item {
6060
6161 focus: true
6262
63− property bool itemDestroyed: false
6463 sourceComponent: Component {
6564 DesktopStage {
6665 color: "darkblue"
6766 anchors.fill: parent
68− Component.onDestruction: {
69− desktopStageLoader.itemDestroyed = true;
70− }
7167 orientations: Orientations {}
7268 }
7369 }
@@ -103,16 +99,10 @@ Item {
10399 property Item desktopStage: desktopStageLoader.status === Loader.Ready ? desktopStageLoader.item : null
104100
105101 function cleanup() {
106− desktopStageLoader.itemDestroyed = false;
107102 desktopStageLoader.active = false;
108103
109104 tryCompare(desktopStageLoader, "status", Loader.Null);
110105 tryCompare(desktopStageLoader, "item", null);
111− // Loader.status might be Loader.Null and Loader.item might be null but the Loader
112− // actually took place. Likely because Loader waits until the next event loop
113− // iteration to do its work. So to ensure the reload, we will wait until the
114− // Shell instance gets destroyed.
115− tryCompare(desktopStageLoader, "itemDestroyed", true);
116106
117107 killAllRunningApps();
118108
@@ -155,10 +145,7 @@ Item {
155145 }
156146
157147 function test_appFocusSwitch(data) {
158− var i;
159− for (i = 0; i < data.apps.length; i++) {
160− startApplication(data.apps[i]);
161− }
148+ data.apps.forEach(startApplication);
162149
163150 ApplicationManager.requestFocusApplication(data.apps[data.focusfrom]);
164151 tryCompare(ApplicationManager.findApplication(data.apps[data.focusfrom]).session.surface, "activeFocus", true);
@@ -175,10 +162,7 @@ Item {
175162 }
176163
177164 function test_tappingOnWindowChangesFocusedApp(data) {
178− var i;
179− for (i = 0; i < data.apps.length; i++) {
180− startApplication(data.apps[i]);
181− }
165+ data.apps.forEach(startApplication);
182166 var fromAppId = data.apps[data.focusfrom];
183167 var toAppId = data.apps[data.focusTo]
184168
@@ -195,18 +179,37 @@ Item {
195179 compare(ApplicationManager.focusedApplicationId, toAppId);
196180 }
197181
182+ function test_clickingOnWindowChangesFocusedApp_data() {
183+ return test_tappingOnWindowChangesFocusedApp_data(); // reuse test data
184+ }
185+
186+ function test_clickingOnWindowChangesFocusedApp(data) {
187+ data.apps.forEach(startApplication);
188+ var fromAppId = data.apps[data.focusfrom];
189+ var toAppId = data.apps[data.focusTo]
190+
191+ var fromAppWindow = findChild(desktopStage, "appWindow_" + fromAppId);
192+ verify(fromAppWindow);
193+ mouseClick(fromAppWindow);
194+ compare(fromAppWindow.application.session.surface.activeFocus, true);
195+ compare(ApplicationManager.focusedApplicationId, fromAppId);
196+
197+ var toAppWindow = findChild(desktopStage, "appWindow_" + toAppId);
198+ verify(toAppWindow);
199+ mouseClick(toAppWindow);
200+ compare(toAppWindow.application.session.surface.activeFocus, true);
201+ compare(ApplicationManager.focusedApplicationId, toAppId);
202+ }
203+
198204 function test_tappingOnDecorationFocusesApplication_data() {
199205 return [
200− {tag: "dash", apps: [ "unity8-dash", "dialer-app", "camera-app" ], focusfrom: 0, focusTo: 1 },
201− {tag: "dash", apps: [ "unity8-dash", "dialer-app", "camera-app" ], focusfrom: 1, focusTo: 0 },
206+ {tag: "dash to dialer", apps: [ "unity8-dash", "dialer-app", "camera-app" ], focusfrom: 0, focusTo: 1 },
207+ {tag: "dialer to dash", apps: [ "unity8-dash", "dialer-app", "camera-app" ], focusfrom: 1, focusTo: 0 },
202208 ]
203209 }
204210
205211 function test_tappingOnDecorationFocusesApplication(data) {
206− var i;
207− for (i = 0; i < data.apps.length; i++) {
208− startApplication(data.apps[i]);
209− }
212+ data.apps.forEach(startApplication);
210213
211214 var fromAppDecoration = findChild(desktopStage, "appWindowDecoration_" + data.apps[data.focusfrom]);
212215 verify(fromAppDecoration);
@@ -219,10 +222,114 @@ Item {
219222 tryCompare(ApplicationManager.findApplication(data.apps[data.focusTo]).session.surface, "activeFocus", true);
220223 }
221224
225+ function test_clickingOnDecorationFocusesApplication_data() {
226+ return test_tappingOnDecorationFocusesApplication_data(); // reuse test data
227+ }
228+
229+ function test_clickingOnDecorationFocusesApplication(data) {
230+ data.apps.forEach(startApplication);
231+
232+ var fromAppDecoration = findChild(desktopStage, "appWindowDecoration_" + data.apps[data.focusfrom]);
233+ verify(fromAppDecoration);
234+ mouseClick(fromAppDecoration);
235+ tryCompare(ApplicationManager.findApplication(data.apps[data.focusfrom]).session.surface, "activeFocus", true);
236+
237+ var toAppDecoration = findChild(desktopStage, "appWindowDecoration_" + data.apps[data.focusTo]);
238+ verify(toAppDecoration);
239+ mouseClick(toAppDecoration);
240+ tryCompare(ApplicationManager.findApplication(data.apps[data.focusTo]).session.surface, "activeFocus", true);
241+ }
242+
243+ function test_windowMaximize() {
244+ var apps = ["unity8-dash", "dialer-app", "camera-app"];
245+ apps.forEach(startApplication);
246+ var appName = "dialer-app";
247+ var appDelegate = findChild(desktopStage, "appDelegate_" + appName);
248+ verify(appDelegate);
249+ ApplicationManager.focusApplication(appName);
250+ keyClick(Qt.Key_Up, Qt.MetaModifier|Qt.ControlModifier); // Ctrl+Super+Up shortcut to maximize
251+ tryCompare(appDelegate, "maximized", true);
252+ tryCompare(appDelegate, "minimized", false);
253+ }
254+
255+ function test_windowMaximizeLeft() {
256+ var apps = ["unity8-dash", "dialer-app", "camera-app"];
257+ apps.forEach(startApplication);
258+ var appName = "dialer-app";
259+ var appDelegate = findChild(desktopStage, "appDelegate_" + appName);
260+ verify(appDelegate);
261+ ApplicationManager.focusApplication(appName);
262+ keyClick(Qt.Key_Left, Qt.MetaModifier|Qt.ControlModifier); // Ctrl+Super+Left shortcut to maximizeLeft
263+ tryCompare(appDelegate, "maximized", false);
264+ tryCompare(appDelegate, "minimized", false);
265+ tryCompare(appDelegate, "maximizedLeft", true);
266+ tryCompare(appDelegate, "maximizedRight", false);
267+ }
268+
269+ function test_windowMaximizeRight() {
270+ var apps = ["unity8-dash", "dialer-app", "camera-app"];
271+ apps.forEach(startApplication);
272+ var appName = "dialer-app";
273+ var appDelegate = findChild(desktopStage, "appDelegate_" + appName);
274+ verify(appDelegate);
275+ ApplicationManager.focusApplication(appName);
276+ keyClick(Qt.Key_Right, Qt.MetaModifier|Qt.ControlModifier); // Ctrl+Super+Right shortcut to maximizeRight
277+ tryCompare(appDelegate, "maximized", false);
278+ tryCompare(appDelegate, "minimized", false);
279+ tryCompare(appDelegate, "maximizedLeft", false);
280+ tryCompare(appDelegate, "maximizedRight", true);
281+ }
282+
283+ function test_windowMinimize() {
284+ var apps = ["unity8-dash", "dialer-app", "camera-app"];
285+ apps.forEach(startApplication);
286+ var appName = "dialer-app";
287+ var appDelegate = findChild(desktopStage, "appDelegate_" + appName);
288+ verify(appDelegate);
289+ ApplicationManager.focusApplication(appName);
290+ keyClick(Qt.Key_Down, Qt.MetaModifier|Qt.ControlModifier); // Ctrl+Super+Down shortcut to minimize
291+ tryCompare(appDelegate, "maximized", false);
292+ tryCompare(appDelegate, "minimized", true);
293+ verify(ApplicationManager.focusedApplicationId != ""); // verify we don't lose focus when minimizing an app
294+ }
295+
296+ function test_windowMinimizeAll() {
297+ var apps = ["unity8-dash", "dialer-app", "camera-app"];
298+ apps.forEach(startApplication);
299+ verify(ApplicationManager.count == 3);
300+ keyClick(Qt.Key_D, Qt.MetaModifier|Qt.ControlModifier); // Ctrl+Super+D shortcut to minimize all
301+ tryCompare(ApplicationManager, "focusedApplicationId", ""); // verify no app is focused
302+ }
303+
304+ function test_windowClose() {
305+ var apps = ["unity8-dash", "dialer-app", "camera-app"];
306+ apps.forEach(startApplication);
307+ verify(ApplicationManager.count == 3);
308+ var appName = "dialer-app";
309+ var appDelegate = findChild(desktopStage, "appDelegate_" + appName);
310+ verify(appDelegate);
311+ ApplicationManager.focusApplication(appName);
312+ keyClick(Qt.Key_F4, Qt.AltModifier); // Alt+F4 shortcut to close
313+ verify(ApplicationManager.count == 2); // verify the app is gone
314+ verify(ApplicationManager.findApplication(appName) === null); // and it's not in running apps
315+ }
316+
317+ function test_smashCursorKeys() {
318+ var apps = ["unity8-dash", "dialer-app", "camera-app"];
319+ apps.forEach(startApplication);
320+ verify(ApplicationManager.count == 3);
321+ keyClick(Qt.Key_D, Qt.MetaModifier|Qt.ControlModifier); // Ctrl+Super+D shortcut to minimize all
322+ tryCompare(ApplicationManager, "focusedApplicationId", ""); // verify no app is focused
323+
324+ // now try pressing all 4 arrow keys + ctrl + meta
325+ keyClick(Qt.Key_Up | Qt.Key_Down | Qt.Key_Left | Qt.Key_Right, Qt.MetaModifier|Qt.ControlModifier); // smash it!!!
326+ tryCompare(ApplicationManager, "focusedApplicationId", ""); // verify still no app is focused
327+ }
328+
222329 function test_minimizeApplicationHidesSurface() {
223330 var dashApp = startApplication("unity8-dash");
224331
225− var dashDelegate = findChild(desktopStage, "stageDelegate_unity8-dash");
332+ var dashDelegate = findChild(desktopStage, "appDelegate_unity8-dash");
226333 verify(dashDelegate);
227334
228335 findChild(dashDelegate, "decoratedWindow").minimize();
@@ -234,11 +341,11 @@ Item {
234341 var dialerApp = startApplication("dialer-app");
235342 var cameraApp = startApplication("camera-app");
236343
237− var dashDelegate = findChild(desktopStage, "stageDelegate_unity8-dash");
344+ var dashDelegate = findChild(desktopStage, "appDelegate_unity8-dash");
238345 verify(dashDelegate);
239− var dialerDelegate = findChild(desktopStage, "stageDelegate_dialer-app");
346+ var dialerDelegate = findChild(desktopStage, "appDelegate_dialer-app");
240347 verify(dialerDelegate);
241− var cameraDelegate = findChild(desktopStage, "stageDelegate_camera-app");
348+ var cameraDelegate = findChild(desktopStage, "appDelegate_camera-app");
242349 verify(cameraDelegate);
243350
244351 // maximize
@@ -256,20 +363,20 @@ Item {
256363
257364 function test_applicationsBecomeVisibleWhenOccludingAppRemoved() {
258365 var dashApp = startApplication("unity8-dash");
259− var dashDelegate = findChild(desktopStage, "stageDelegate_unity8-dash");
366+ var dashDelegate = findChild(desktopStage, "appDelegate_unity8-dash");
260367 verify(dashDelegate);
261368
262369 var dialerApp = startApplication("dialer-app");
263− var dialerDelegate = findChild(desktopStage, "stageDelegate_dialer-app");
370+ var dialerDelegate = findChild(desktopStage, "appDelegate_dialer-app");
264371 verify(dialerDelegate);
265372
266373 var cameraApp = startApplication("camera-app");
267− var cameraDelegate = findChild(desktopStage, "stageDelegate_camera-app");
374+ var cameraDelegate = findChild(desktopStage, "appDelegate_camera-app");
268375 verify(cameraDelegate);
269376 findChild(dialerDelegate, "decoratedWindow").maximize();
270377
271378 var galleryApp = startApplication("gallery-app");
272− var galleryDelegate = findChild(desktopStage, "stageDelegate_gallery-app");
379+ var galleryDelegate = findChild(desktopStage, "appDelegate_gallery-app");
273380 verify(galleryDelegate);
274381 findChild(galleryDelegate, "decoratedWindow").maximize();
275382
tests/qmltests/Stages/tst_WindowResizeArea.qml+43
@@ -19,6 +19,7 @@ import QtQuick.Layouts 1.1
1919import QtTest 1.0
2020import Unity.Test 0.1
2121import ".."
22+import "../../../qml/Components/PanelState"
2223import "../../../qml/Stages"
2324import Ubuntu.Components 1.3
2425import Ubuntu.Components.ListItems 1.3 as ListItem
@@ -31,6 +32,12 @@ Item {
3132
3233 property var fakeWindow: windowLoader.item
3334
35+ Binding {
36+ target: PanelState
37+ property: "panelHeight"
38+ value: units.gu(2)
39+ }
40+
3441 Component {
3542 id: fakeWindowComponent
3643
@@ -57,6 +64,8 @@ Item {
5764 minWidth: units.gu(15)
5865 minHeight: units.gu(10)
5966 windowId: "test-window-id"
67+ screenWidth: root.width
68+ screenHeight: root.height
6069 }
6170
6271 Rectangle {
@@ -228,5 +237,39 @@ Item {
228237 // clean up
229238 fakeWindow.state = "normal"
230239 }
240+
241+
242+ function test_restoreMovesIntoBounds_data() {
243+ return [
244+ {tag: "left off", x: -units.gu(5), y: units.gu(5), w: units.gu(10), h: units.gu(10)},
245+ {tag: "top off", x: units.gu(5), y: -units.gu(5), w: units.gu(10), h: units.gu(10)},
246+ {tag: "right off", x: root.width - units.gu(5), y: units.gu(5), w: units.gu(10), h: units.gu(10)},
247+ {tag: "bottom off", x: units.gu(5), y: root.height - units.gu(5), w: units.gu(10), h: units.gu(10)},
248+ {tag: "width too large", x: units.gu(5), y: units.gu(5), w: root.width * 2, h: units.gu(10)},
249+ {tag: "height too large", x: units.gu(5), y: units.gu(5), w: units.gu(10), h: root.height * 2}
250+ ]
251+ }
252+
253+ function test_restoreMovesIntoBounds(data) {
254+ fakeWindow.x = data.x;
255+ fakeWindow.y = data.y;
256+ fakeWindow.width = data.w;
257+ fakeWindow.height = data.h;
258+ waitForRendering(root);
259+
260+ // This will destroy the window and recreate it
261+ windowLoader.active = false;
262+ waitForRendering(root);
263+ windowLoader.active = true;
264+ waitForRendering(root)
265+
266+ // Make sure it's again where we left it in normal state before destroying
267+ compare(fakeWindow.x >= 0, true)
268+ compare(fakeWindow.y >= PanelState.panelHeight, true)
269+ compare(fakeWindow.x + fakeWindow.width <= root.width, true)
270+ compare(fakeWindow.y + fakeWindow.height <= root.height, true)
271+
272+ waitForRendering(root)
273+ }
231274 }
232275}
tests/qmltests/tst_OrientedShell.qml+19−10
@@ -26,7 +26,6 @@ import Powerd 0.1
2626import Unity.InputInfo 0.1
2727
2828import "../../qml"
29−import "../../qml/Components/UnityInputInfo"
3029
3130Rectangle {
3231 id: root
@@ -52,7 +51,17 @@ Rectangle {
5251
5352 QtObject{
5453 id: mockOskSettings
55− property bool stayHidden: false;
54+ property bool stayHidden: false
55+ property bool disableHeight: false
56+ }
57+
58+ InputDeviceModel {
59+ id: miceModel
60+ deviceFilter: InputInfo.Mouse
61+ }
62+ InputDeviceModel {
63+ id: keyboardsModel
64+ deviceFilter: InputInfo.Keyboard
5665 }
5766
5867 property int physicalOrientation0
@@ -328,14 +337,14 @@ Rectangle {
328337 text: "Add mouse"
329338 activeFocusOnPress: false
330339 onClicked: {
331− UnityInputInfo.inputInfo.addMockMouse()
340+ MockInputDeviceBackend.addMockDevice("/mouse" + miceModel.count, InputInfo.Mouse)
332341 }
333342 }
334343 Button {
335344 text: "Remove mouse"
336345 activeFocusOnPress: false
337346 onClicked: {
338− UnityInputInfo.inputInfo.removeMockMouse()
347+ MockInputDeviceBackend.removeDevice("/mouse" + (miceModel.count - 1))
339348 }
340349 }
341350 }
@@ -344,14 +353,14 @@ Rectangle {
344353 text: "Add kbd"
345354 activeFocusOnPress: false
346355 onClicked: {
347− UnityInputInfo.inputInfo.addMockKeyboard()
356+ MockInputDeviceBackend.addMockDevice("/kbd" + keyboardsModel.count, InputInfo.Keyboard)
348357 }
349358 }
350359 Button {
351360 activeFocusOnPress: false
352361 text: "Remove kbd"
353362 onClicked: {
354− UnityInputInfo.inputInfo.removeMockKeyboard()
363+ MockInputDeviceBackend.removeDevice("/kbd" + (keyboardsModel.count - 1))
355364 }
356365 }
357366 }
@@ -976,19 +985,19 @@ Rectangle {
976985 tryCompare(shell, "usageScenario", "phone");
977986 tryCompare(mockOskSettings, "stayHidden", false);
978987
979− UnityInputInfo.inputInfo.addMockKeyboard();
988+ MockInputDeviceBackend.addMockDevice("/kbd0", InputInfo.Keyboard);
980989 tryCompare(shell, "usageScenario", "phone");
981990 tryCompare(mockOskSettings, "stayHidden", true);
982991
983− UnityInputInfo.inputInfo.addMockMouse();
992+ MockInputDeviceBackend.addMockDevice("/mouse0", InputInfo.Mouse);
984993 tryCompare(shell, "usageScenario", "desktop");
985994 tryCompare(mockOskSettings, "stayHidden", true);
986995
987− UnityInputInfo.inputInfo.removeMockKeyboard();
996+ MockInputDeviceBackend.removeDevice("/kbd0");
988997 tryCompare(shell, "usageScenario", "desktop");
989998 tryCompare(mockOskSettings, "stayHidden", false);
990999
991− UnityInputInfo.inputInfo.removeMockMouse();
1000+ MockInputDeviceBackend.removeDevice("/mouse0");
9921001 tryCompare(shell, "usageScenario", "phone");
9931002 tryCompare(mockOskSettings, "stayHidden", false);
9941003 }
tests/qmltests/tst_Shell.qml+93−4
@@ -313,6 +313,7 @@ Rectangle {
313313 }
314314
315315 function cleanup() {
316+ waitForRendering(shell);
316317 mouseEmulation.checked = true;
317318 tryCompare(shell, "enabled", true); // make sure greeter didn't leave us in disabled state
318319 tearDown();
@@ -344,9 +345,9 @@ Rectangle {
344345 var app2 = ApplicationManager.startApplication("webbrowser-app")
345346 var app3 = ApplicationManager.startApplication("camera-app")
346347 var app4 = ApplicationManager.startApplication("facebook-webapp")
347− var app5 = ApplicationManager.startApplication("calendar-app")
348+ var app5 = ApplicationManager.startApplication("camera-app")
348349 var app6 = ApplicationManager.startApplication("gallery-app")
349− var app7 = ApplicationManager.startApplication("camera-app")
350+ var app7 = ApplicationManager.startApplication("calendar-app")
350351 waitUntilAppWindowIsFullyLoaded(app7);
351352 }
352353
@@ -1710,11 +1711,11 @@ Rectangle {
17101711 keyPress(Qt.Key_Control)
17111712 keyClick(Qt.Key_Tab);
17121713
1713−
17141714 tryCompare(desktopSpread, "state", "altTab")
17151715
1716− mouseMove(shell, 0, 0);
1716+ mouseMove(shell, 0, shell.height / 2);
17171717 tryCompare(launcher, "state", "visibleTemporary")
1718+ waitForRendering(shell)
17181719
17191720 mouseClick(bfb, bfb.width / 2, bfb.height / 2)
17201721 tryCompare(launcher, "state", "")
@@ -1809,6 +1810,37 @@ Rectangle {
18091810 tryCompare(panelButtons, "visible", true);
18101811 }
18111812
1813+ function test_cantMoveWindowUnderPanel() {
1814+ loadDesktopShellWithApps();
1815+ var appRepeater = findChild(shell, "appRepeater")
1816+ var appDelegate = appRepeater.itemAt(0);
1817+
1818+ mousePress(appDelegate, appDelegate.width / 2, units.gu(1))
1819+ mouseMove(appDelegate, appDelegate.width / 2, -units.gu(100))
1820+
1821+ compare(appDelegate.y >= PanelState.panelHeight, true);
1822+ }
1823+
1824+ function test_restoreWindowStateFixesIfUnderPanel() {
1825+ loadDesktopShellWithApps();
1826+ var appRepeater = findChild(shell, "appRepeater")
1827+ var appId = ApplicationManager.get(0).appId;
1828+ var appDelegate = appRepeater.itemAt(0);
1829+
1830+ // Move it under the panel programmatically (might happen later with an alt+drag)
1831+ appDelegate.y = -units.gu(10)
1832+
1833+ ApplicationManager.stopApplication(appId)
1834+ ApplicationManager.startApplication(appId)
1835+ waitForRendering(shell)
1836+
1837+ // Make sure the newly started one is at index 0 again
1838+ tryCompare(ApplicationManager.get(0), "appId", appId);
1839+
1840+ appDelegate = appRepeater.itemAt(0);
1841+ compare(appDelegate.y >= PanelState.panelHeight, true);
1842+ }
1843+
18121844 function test_lifecyclePolicyForNonTouchApp_data() {
18131845 return [
18141846 {tag: "phone", formFactor: "phone", usageScenario: "phone"},
@@ -1853,5 +1885,62 @@ Rectangle {
18531885 compare(app1.session.surface.activeFocus, false);
18541886 compare(app1.requestedState, ApplicationInfoInterface.RequestedRunning);
18551887 }
1888+
1889+ function test_switchToStagedForcesLegacyAppClosing_data() {
1890+ return [
1891+ {tag: "forceClose", replug: false },
1892+ {tag: "replug", replug: true }
1893+ ];
1894+ }
1895+
1896+ function test_switchToStagedForcesLegacyAppClosing(data) {
1897+ loadShell("desktop")
1898+ shell.usageScenario = "desktop"
1899+ waitForRendering(shell);
1900+
1901+ ApplicationManager.startApplication("camera-app")
1902+
1903+ shell.usageScenario = "phone"
1904+ waitForRendering(shell);
1905+
1906+ // No legacy app running yet... Popup must *not* show.
1907+ var popup = findChild(root, "modeSwitchWarningDialog");
1908+ compare(popup, null);
1909+
1910+ shell.usageScenario = "desktop"
1911+ waitForRendering(shell);
1912+
1913+ // Now start a legacy app
1914+ ApplicationManager.startApplication("libreoffice")
1915+
1916+ shell.usageScenario = "phone"
1917+ waitForRendering(shell);
1918+
1919+ // The popup must appear now
1920+ popup = findChild(root, "modeSwitchWarningDialog");
1921+ compare(popup !== null, true);
1922+
1923+ if (data.replug) {
1924+ shell.usageScenario = "desktop"
1925+ waitForRendering(shell);
1926+
1927+ } else {
1928+ var forceCloseButton = findChild(popup, "forceCloseButton");
1929+ mouseClick(forceCloseButton, forceCloseButton.width / 2, forceCloseButton.height / 2);
1930+ waitForRendering(root);
1931+ }
1932+
1933+ // Popup must be gone now
1934+ popup = findChild(root, "modeSwitchWarningDialog");
1935+ compare(popup === null, true);
1936+
1937+ if (data.replug) {
1938+ // Libreoffice must still be running
1939+ compare(ApplicationManager.findApplication("libreoffice") !== null, true);
1940+ } else {
1941+ // Libreoffice must be gone now
1942+ compare(ApplicationManager.findApplication("libreoffice") === null, true);
1943+ }
1944+ }
18561945 }
18571946}