on xenial help sign in
Merge from trunk
54de2dbMichael Terry
data/CMakeLists.txt+6
@@ -11,6 +11,12 @@ install(FILES
11 DESTINATION ${CMAKE_INSTALL_DATADIR}/applications
11 DESTINATION ${CMAKE_INSTALL_DATADIR}/applications
12 )
12 )
13
13
14+# install dash icon
15+install(FILES
16+ ${CMAKE_CURRENT_SOURCE_DIR}/${DASH_APP}.png
17+ DESTINATION ${SHELL_APP_DIR}/Dash/
18+ )
19+
14add_custom_target(pkgversion ALL COMMAND dpkg-parsechangelog -l${CMAKE_SOURCE_DIR}/debian/changelog --show-field version > ${CMAKE_CURRENT_BINARY_DIR}/version)
20add_custom_target(pkgversion ALL COMMAND dpkg-parsechangelog -l${CMAKE_SOURCE_DIR}/debian/changelog --show-field version > ${CMAKE_CURRENT_BINARY_DIR}/version)
15
21
16install(FILES com.canonical.Unity8.gschema.xml DESTINATION ${CMAKE_INSTALL_DATADIR}/glib-2.0/schemas)
22install(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
4Comment=An interface for using the Scopes system
4Comment=An interface for using the Scopes system
5Exec=@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_BINDIR@/unity8-dash
5Exec=@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_BINDIR@/unity8-dash
6Terminal=false
6Terminal=false
7−Icon=
7+Icon=@CMAKE_INSTALL_PREFIX@/@SHELL_APP_DIR@/Dash/unity8-dash.png
8NoDisplay=true
8NoDisplay=true
9X-Ubuntu-Touch=true
9X-Ubuntu-Touch=true
10X-Ubuntu-Supported-Orientations=primary
10X-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+
1unity8 (8.11+16.04.20151104-0ubuntu1) xenial; urgency=medium
33unity8 (8.11+16.04.20151104-0ubuntu1) xenial; urgency=medium
2
34
3 [ Albert Astals Cid ]
35 [ Albert Astals Cid ]
plugins/AccountsService/AccountsService.cpp+3−3
@@ -76,7 +76,7 @@ void AccountsService::setDemoEdges(bool demoEdges)
76{
76{
77 if (m_demoEdges != demoEdges) {
77 if (m_demoEdges != demoEdges) {
78 m_demoEdges = demoEdges;
78 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);
80
80
81 Q_EMIT demoEdgesChanged();
81 Q_EMIT demoEdgesChanged();
82 }
82 }
@@ -131,7 +131,7 @@ void AccountsService::setHereEnabled(bool enabled)
131{
131{
132 if (m_hereEnabled != enabled) {
132 if (m_hereEnabled != enabled) {
133 m_hereEnabled = enabled;
133 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);
135
135
136 Q_EMIT hereEnabledChanged();
136 Q_EMIT hereEnabledChanged();
137 }
137 }
@@ -449,7 +449,7 @@ void AccountsService::setFailedLogins(uint failedLogins)
449{
449{
450 if (m_failedLogins != failedLogins) {
450 if (m_failedLogins != failedLogins) {
451 m_failedLogins = failedLogins;
451 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);
453
453
454 Q_EMIT failedLoginsChanged();
454 Q_EMIT failedLoginsChanged();
455 }
455 }
plugins/AccountsService/AccountsServiceDBusAdaptor.cpp+1−22
@@ -37,18 +37,6 @@ AccountsServiceDBusAdaptor::AccountsServiceDBusAdaptor(QObject* parent)
37 connection, this);
37 connection, this);
38}
38}
39
39
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−
52QDBusPendingReply<QVariant> AccountsServiceDBusAdaptor::getUserPropertyAsync(const QString &user, const QString &interface, const QString &property)
40QDBusPendingReply<QVariant> AccountsServiceDBusAdaptor::getUserPropertyAsync(const QString &user, const QString &interface, const QString &property)
53{
41{
54 QDBusInterface *iface = getUserInterface(user);
42 QDBusInterface *iface = getUserInterface(user);
@@ -58,15 +46,6 @@ QDBusPendingReply<QVariant> AccountsServiceDBusAdaptor::getUserPropertyAsync(con
58 return QDBusPendingReply<QVariant>(QDBusMessage::createError(QDBusError::Other, QStringLiteral("Invalid Interface")));
46 return QDBusPendingReply<QVariant>(QDBusMessage::createError(QDBusError::Other, QStringLiteral("Invalid Interface")));
59}
47}
60
48
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−
70QDBusPendingCall AccountsServiceDBusAdaptor::setUserPropertyAsync(const QString &user, const QString &interface, const QString &property, const QVariant &value)
49QDBusPendingCall AccountsServiceDBusAdaptor::setUserPropertyAsync(const QString &user, const QString &interface, const QString &property, const QVariant &value)
71{
50{
72 QDBusInterface *iface = getUserInterface(user);
51 QDBusInterface *iface = getUserInterface(user);
@@ -116,7 +95,7 @@ QDBusInterface *AccountsServiceDBusAdaptor::getUserInterface(const QString &user
116{
95{
117 QDBusInterface *iface = m_users.value(user);
96 QDBusInterface *iface = m_users.value(user);
118 if (iface == nullptr && m_accountsManager->isValid()) {
97 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);
120 if (answer.isValid()) {
99 if (answer.isValid()) {
121 const QString path = answer.value().path();
100 const QString path = answer.value().path();
122
101
plugins/AccountsService/AccountsServiceDBusAdaptor.h−12
@@ -35,19 +35,7 @@ public:
35 explicit AccountsServiceDBusAdaptor(QObject *parent = 0);
35 explicit AccountsServiceDBusAdaptor(QObject *parent = 0);
36 ~AccountsServiceDBusAdaptor() = default;
36 ~AccountsServiceDBusAdaptor() = default;
37
37
38− Q_INVOKABLE QVariant getUserProperty(const QString &user, const QString &interface, const QString &property);
39 Q_INVOKABLE QDBusPendingReply<QVariant> getUserPropertyAsync(const QString &user, const QString &interface, const QString &property);
38 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);
51 Q_INVOKABLE QDBusPendingCall setUserPropertyAsync(const QString &user, const QString &interface, const QString &property, const QVariant &value);
39 Q_INVOKABLE QDBusPendingCall setUserPropertyAsync(const QString &user, const QString &interface, const QString &property, const QVariant &value);
52
40
53Q_SIGNALS:
41Q_SIGNALS:
plugins/Cursor/MousePointer.cpp+1
@@ -32,6 +32,7 @@ MousePointer::MousePointer(QQuickItem *parent)
32 , m_hotspotX(0)
32 , m_hotspotX(0)
33 , m_hotspotY(0)
33 , m_hotspotY(0)
34{
34{
35+ updateHotspot();
35}
36}
36
37
37void MousePointer::handleMouseEvent(ulong timestamp, QPointF movement, Qt::MouseButtons buttons,
38void 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>
195 m_lastInRowIndexPosition.remove(indexToRemove);
195 m_lastInRowIndexPosition.remove(indexToRemove);
196 } else {
196 } else {
197 if (indexToRemove < lastIndex) {
197 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;
199 } else {
201 } else {
200 setImplicitHeightDirty();
202 setImplicitHeightDirty();
201 }
203 }
plugins/Dash/organicgrid.cpp+3−1
@@ -246,7 +246,9 @@ void OrganicGrid::processModelRemoves(const QVector<QQmlChangeSet::Change> &remo
246 releaseItem(m_visibleItems.takeLast());
246 releaseItem(m_visibleItems.takeLast());
247 } else {
247 } else {
248 if (indexToRemove < lastIndex) {
248 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;
250 }
252 }
251 }
253 }
252 }
254 }
plugins/Dash/verticaljournal.cpp+3−1
@@ -261,7 +261,9 @@ void VerticalJournal::processModelRemoves(const QVector<QQmlChangeSet::Change> &
261 }
261 }
262 if (!found) {
262 if (!found) {
263 if (indexToRemove < lastCreatedIndex) {
263 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;
265 } else {
267 } else {
266 setImplicitHeightDirty();
268 setImplicitHeightDirty();
267 }
269 }
plugins/Greeter/Unity/Launcher/launchermodelas.cpp+19−4
@@ -194,15 +194,30 @@ int LauncherModel::findApplication(const QString &appId)
194 return -1;
194 return -1;
195}
195}
196
196
197−typedef QList<QVariantMap> VariantMapList;
198void LauncherModel::refresh()
197void LauncherModel::refresh()
199{
198{
199+ if (!m_accounts || m_user.isEmpty()) {
200− QList<QVariantMap> items;
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+ }
201
213
202− if (m_accounts && !m_user.isEmpty()) {
214+ refreshWithItems(qdbus_cast<QList<QVariantMap>>(reply.value().value<QDBusArgument>()));
203− items = m_accounts->getUserProperty<VariantMapList>(m_user, QStringLiteral("com.canonical.unity.AccountsService"), QStringLiteral("LauncherItems"));
215+ });
204 }
216 }
217+}
205
218
219+void LauncherModel::refreshWithItems(const QList<QVariantMap> &items)
220+{
206 // First walk through all the existing items and see if we need to remove something
221 // First walk through all the existing items and see if we need to remove something
207 QList<LauncherItem*> toBeRemoved;
222 QList<LauncherItem*> toBeRemoved;
208
223
plugins/Greeter/Unity/Launcher/launchermodelas.h+2
@@ -65,6 +65,8 @@ private Q_SLOTS:
65 void propertiesChanged(const QString &user, const QString &interface, const QStringList &changed);
65 void propertiesChanged(const QString &user, const QString &interface, const QStringList &changed);
66
66
67private:
67private:
68+ void refreshWithItems(const QList<QVariantMap> &items);
69+
68 QString m_user;
70 QString m_user;
69 QList<LauncherItem*> m_list;
71 QList<LauncherItem*> m_list;
70 AccountsServiceDBusAdaptor *m_accounts;
72 AccountsServiceDBusAdaptor *m_accounts;
plugins/Unity/InputInfo/CMakeLists.txt+1−1
@@ -21,7 +21,7 @@ include_directories(
21set(InputInfo_SOURCES
21set(InputInfo_SOURCES
22 plugin.cpp
22 plugin.cpp
23 qinputinfo.cpp
23 qinputinfo.cpp
24− qdeclarativeinputdeviceinfo.cpp
24+ qdeclarativeinputdevicemodel.cpp
25 linux/qinputdeviceinfo_linux.cpp
25 linux/qinputdeviceinfo_linux.cpp
26)
26)
27
27
plugins/Unity/InputInfo/linux/qinputdeviceinfo_linux.cpp+131−83
@@ -47,39 +47,46 @@
47#include <QDebug>
47#include <QDebug>
48#include <QSocketNotifier>
48#include <QSocketNotifier>
49#include <QTimer>
49#include <QTimer>
50+#include <QDir>
50
51
51−QInputDeviceInfoPrivate::QInputDeviceInfoPrivate(QObject *parent) :
52+QInputDeviceManagerPrivate::QInputDeviceManagerPrivate(QObject *parent) :
52− QObject(parent)
53+ QObject(parent),
53− , udev(0)
54+ currentFilter(QInputDevice::Unknown),
55+ udevice(0)
54{
56{
55− QTimer::singleShot(250, this, &QInputDeviceInfoPrivate::init);
57+ QTimer::singleShot(250,this,SLOT(init()));
56}
58}
57
59
58−void QInputDeviceInfoPrivate::init()
60+QInputDeviceManagerPrivate::~QInputDeviceManagerPrivate()
59{
61{
60− if (!udev)
62+ udev_unref(udevice);
63+ udev_monitor_unref(udevMonitor);
64+}
65+
66+void QInputDeviceManagerPrivate::init()
67+{
68+ if (!udevice)
61− udev = udev_new();
69+ udevice = udev_new();
62
70
63− struct udev_list_entry *devices, *dev_list_entry;
71+ udev_list_entry *devices;
72+ udev_list_entry *dev_list_entry;
64− struct udev_device *dev;
73+ udev_device *dev;
65
74
66 QString subsystem = QStringLiteral("input");
75 QString subsystem = QStringLiteral("input");
67 struct udev_enumerate *enumerate = 0;
76 struct udev_enumerate *enumerate = 0;
68
77
69− if (udev) {
78+ if (udevice) {
70
79
71− udevMonitor = udev_monitor_new_from_netlink(udev, "udev");
80+ udevMonitor = udev_monitor_new_from_netlink(udevice, "udev");
72 udev_monitor_filter_add_match_subsystem_devtype(udevMonitor, subsystem.toLatin1(), NULL);
81 udev_monitor_filter_add_match_subsystem_devtype(udevMonitor, subsystem.toLatin1(), NULL);
73− enumerate = udev_enumerate_new(udev);
82+ enumerate = udev_enumerate_new(udevice);
74 udev_enumerate_add_match_subsystem(enumerate, subsystem.toLatin1());
83 udev_enumerate_add_match_subsystem(enumerate, subsystem.toLatin1());
75
84
76−
77 udev_monitor_enable_receiving(udevMonitor);
85 udev_monitor_enable_receiving(udevMonitor);
78 notifierFd = udev_monitor_get_fd(udevMonitor);
86 notifierFd = udev_monitor_get_fd(udevMonitor);
79
87
80 notifier = new QSocketNotifier(notifierFd, QSocketNotifier::Read, this);
88 notifier = new QSocketNotifier(notifierFd, QSocketNotifier::Read, this);
81− connect(notifier, &QSocketNotifier::activated, this, &QInputDeviceInfoPrivate::onUDevChanges);
89+ connect(notifier, SIGNAL(activated(int)), this, SLOT(onUDevChanges()));
82−
83
90
84 udev_enumerate_scan_devices(enumerate);
91 udev_enumerate_scan_devices(enumerate);
85 devices = udev_enumerate_get_list_entry(enumerate);
92 devices = udev_enumerate_get_list_entry(enumerate);
@@ -88,78 +95,80 @@ void QInputDeviceInfoPrivate::init()
88 const char *path;
95 const char *path;
89 path = udev_list_entry_get_name(dev_list_entry);
96 path = udev_list_entry_get_name(dev_list_entry);
90
97
91− dev = udev_device_new_from_syspath(udev, path);
98+ dev = udev_device_new_from_syspath(udevice, path);
92−
93− QString eventPath = QString::fromLatin1(udev_device_get_sysname(dev));
94−
95 if (qstrcmp(udev_device_get_subsystem(dev), "input") == 0 ) {
99 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);
100+ QInputDevice *iDevice = addDevice(dev);
101− if (!iDevice)
101+ if (iDevice && !iDevice->devicePath().isEmpty()) {
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);
102+ deviceMap.insert(iDevice->devicePath(),iDevice);
114− Q_EMIT newDevice(eventPath);
115−
116 }
103 }
117 }
104 }
105+ udev_device_unref(dev);
118 }
106 }
119 udev_enumerate_unref(enumerate);
107 udev_enumerate_unref(enumerate);
120 }
108 }
109+ // udev_unref(udevice);
110+ Q_FOREACH (const QString &devicePath, deviceMap.keys()) {
111+ Q_EMIT deviceAdded(devicePath);
112+ }
121 Q_EMIT ready();
113 Q_EMIT ready();
122}
114}
123
115
124−QInputDeviceInfo::InputTypes QInputDeviceInfoPrivate::getInputTypes( struct udev_device *dev)
116+QInputDevice::InputTypeFlags QInputDeviceManagerPrivate::getInputTypeFlags(struct udev_device *dev)
125{
117{
126− QInputDeviceInfo::InputTypes types = QInputDeviceInfo::Unknown;
118+ QInputDevice::InputTypeFlags flags = QInputDevice::Unknown;
127− if (qstrcmp(udev_device_get_property_value(dev, "ID_INPUT_KEYBOARD"), "1") == 0 )
119+ if (qstrcmp(udev_device_get_property_value(dev, "ID_INPUT_KEY"), "1") == 0 ) {
128− types |= QInputDeviceInfo::Keyboard;
120+ flags |= QInputDevice::Button;
129−
121+ }
130− if (qstrcmp(udev_device_get_property_value(dev, "ID_INPUT_MOUSE"), "1") == 0)
122+ if (qstrcmp(udev_device_get_property_value(dev, "ID_INPUT_MOUSE"), "1") == 0) {
131− types |= QInputDeviceInfo::Mouse;
123+ flags |= QInputDevice::Mouse;
132−
124+ }
133− if (qstrcmp(udev_device_get_property_value(dev, "ID_INPUT_TOUCHPAD"), "1") == 0)
125+ if (qstrcmp(udev_device_get_property_value(dev, "ID_INPUT_TOUCHPAD"), "1") == 0) {
134− types |= QInputDeviceInfo::TouchPad;
126+ flags |= QInputDevice::TouchPad;
135−
127+ }
136 if (qstrcmp(udev_device_get_property_value(dev, "ID_INPUT_TOUCHSCREEN"), "1") == 0
128 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)
129+ || qstrcmp(udev_device_get_property_value(dev, "ID_INPUT_TABLET"), "1") == 0) {
138− types |= QInputDeviceInfo::TouchScreen;
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+ }
139
138
140− return types;
139+ return flags;
141}
140}
142
141
143−QInputDevice *QInputDeviceInfoPrivate::addDevice(const QString &path)
142+QInputDevice *QInputDeviceManagerPrivate::addDevice(struct udev_device *udev)
144{
143{
145− QInputDevice *inputDevice = new QInputDevice(this);
144+ QString eventPath = QString::fromLatin1(udev_device_get_sysname(udev));
146− inputDevice->setDevicePath(path);
147
145
146+ if (eventPath.contains(QStringLiteral("event")))
147+ eventPath.prepend(QStringLiteral("/dev/input/"));
148+
149+ if (deviceMap.contains(eventPath)) {
150+ return Q_NULLPTR;
151+ }
148 struct libevdev *dev = NULL;
152 struct libevdev *dev = NULL;
149 int fd;
153 int fd;
150 int rc = 1;
154 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();
152
161
162+ fd = open(eventPath.toLatin1(), O_RDONLY|O_NONBLOCK);
153 if (fd == -1) {
163 if (fd == -1) {
154 return inputDevice;
164 return inputDevice;
155 }
165 }
156 rc = libevdev_new_from_fd(fd, &dev);
166 rc = libevdev_new_from_fd(fd, &dev);
157 if (rc < 0) {
167 if (rc < 0) {
158 qWarning() << "Failed to init libevdev ("<< strerror(-rc) << ")";
168 qWarning() << "Failed to init libevdev ("<< strerror(-rc) << ")";
159− return inputDevice;
169+ return Q_NULLPTR;
160 }
170 }
161
171
162− inputDevice->setName(QString::fromLatin1(libevdev_get_name(dev)));
163 for (int i = 0; i < EV_MAX; i++) {
172 for (int i = 0; i < EV_MAX; i++) {
164 if (i == EV_KEY || i == EV_SW || i == EV_REL
173 if (i == EV_KEY || i == EV_SW || i == EV_REL
165 || i == EV_REL || i == EV_ABS) {
174 || i == EV_REL || i == EV_ABS) {
@@ -183,54 +192,93 @@ QInputDevice *QInputDeviceInfoPrivate::addDevice(const QString &path)
183 }
192 }
184 }
193 }
185 }
194 }
195+
186 return inputDevice;
196 return inputDevice;
187}
197}
188
198
199+void QInputDeviceManagerPrivate::addDetails(struct udev_device *)
200+{
201+}
202+
189−void QInputDeviceInfoPrivate::removeDevice(const QString &path)
203+void QInputDeviceManagerPrivate::removeDevice(const QString &path)
190{
204{
191− for (int i = 0; i < deviceList.size(); ++i) {
205+ // this path is not a full evdev path
206+ Q_FOREACH (const QString devicePath, deviceMap.keys()) {
192− if (deviceList.at(i)->devicePath() == path) {
207+ if (devicePath.contains(path)) {
193− delete deviceList.takeAt(i);
194− deviceMap.remove(path);
208+ deviceMap.remove(devicePath);
195− Q_EMIT deviceRemoved(path);
209+ Q_EMIT deviceRemoved(devicePath);
196 }
210 }
197 }
211 }
198}
212}
199
213
200−void QInputDeviceInfoPrivate::onUDevChanges()
214+QInputDevice *QInputDeviceManagerPrivate::addUdevDevice(struct udev_device *udev)
201{
215{
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+
202− struct udev_device *dev = udev_monitor_receive_device(udevMonitor);
254+ udev_device *dev = udev_monitor_receive_device(udevMonitor);
255+
203 if (dev) {
256 if (dev) {
204 if (qstrcmp(udev_device_get_subsystem(dev), "input") == 0 ) {
257 if (qstrcmp(udev_device_get_subsystem(dev), "input") == 0 ) {
205 QString eventPath = QString::fromLatin1(udev_device_get_sysname(dev));
258 QString eventPath = QString::fromLatin1(udev_device_get_sysname(dev));
206
259
207− if (eventPath.contains(QStringLiteral("input")))
208− return;
209−
210 QString action = QString::fromStdString(udev_device_get_action(dev));
260 QString action = QString::fromStdString(udev_device_get_action(dev));
211
261
212 if (!eventPath.contains(QStringLiteral("/dev/input/")))
262 if (!eventPath.contains(QStringLiteral("/dev/input/")))
213 eventPath.prepend(QStringLiteral("/dev/input/"));
263 eventPath.prepend(QStringLiteral("/dev/input/"));
214
264
215 if (action == QStringLiteral("add")) {
265 if (action == QStringLiteral("add")) {
216−
266+ if (deviceMap.contains(eventPath)){
217− QInputDevice *iDevice = addDevice(eventPath);
218− if (!iDevice)
219− return;
220−
221− iDevice->setTypes(getInputTypes(dev));
222 udev_device_unref(dev);
267 udev_device_unref(dev);
268+ return;
269+ }
223
270
224− if (iDevice->switches().count() > 0 && iDevice->buttons().count() == 0)
271+ QInputDevice *iDevice = addDevice(dev);
225− iDevice->setTypes(iDevice->types() | QInputDeviceInfo::Switch);
272+ if (!iDevice) {
226−
273+ delete iDevice;
227− if (iDevice->buttons().count() > 0 && iDevice->types() == QInputDeviceInfo::Unknown)
274+ return;
228− iDevice->setTypes(iDevice->types() | QInputDeviceInfo::Button);
275+ }
276+ iDevice->setType(getInputTypeFlags(dev));
277+ udev_device_unref(dev);
229
278
230− deviceList.append(iDevice);
231 deviceMap.insert(eventPath,iDevice);
279 deviceMap.insert(eventPath,iDevice);
232
280
233− Q_EMIT newDevice(eventPath);
281+ Q_EMIT deviceAdded(eventPath);
234
282
235 } else if (action == QStringLiteral("remove")) {
283 } else if (action == QStringLiteral("remove")) {
236 removeDevice(eventPath);
284 removeDevice(eventPath);
plugins/Unity/InputInfo/linux/qinputdeviceinfo_linux_p.h+13−6
@@ -58,30 +58,37 @@ public:
58 QList <int> switches;
58 QList <int> switches;
59 QList <int> relativeAxis;
59 QList <int> relativeAxis;
60 QList <int> absoluteAxis;
60 QList <int> absoluteAxis;
61− QInputDeviceInfo::InputTypes types;
61+ QInputDevice::InputTypeFlags type;
62};
62};
63
63
64−class QInputDeviceInfoPrivate : public QObject
64+class QInputDeviceManagerPrivate : public QObject
65{
65{
66 Q_OBJECT
66 Q_OBJECT
67public:
67public:
68− explicit QInputDeviceInfoPrivate(QObject *parent = 0);
68+ explicit QInputDeviceManagerPrivate(QObject *parent = 0);
69+ ~QInputDeviceManagerPrivate();
69 QVector <QInputDevice *> deviceList;
70 QVector <QInputDevice *> deviceList;
70 QMap <QString, QInputDevice *> deviceMap;
71 QMap <QString, QInputDevice *> deviceMap;
72+ static QInputDeviceManagerPrivate * instance();
73+ QInputDevice::InputType currentFilter;
71
74
72Q_SIGNALS:
75Q_SIGNALS:
73− void newDevice(const QString &);
76+ void deviceAdded(const QString &);
74 void deviceRemoved(const QString &);
77 void deviceRemoved(const QString &);
75 void ready();
78 void ready();
76
79
77private:
80private:
78− struct udev *udev;
81+ QInputDevice *addDevice(struct udev_device *udev);
82+ QInputDevice *addUdevDevice(struct udev_device *);
83+
79 QInputDevice *addDevice(const QString &path);
84 QInputDevice *addDevice(const QString &path);
80 void removeDevice(const QString &path);
85 void removeDevice(const QString &path);
81 QSocketNotifier *notifier;
86 QSocketNotifier *notifier;
82 int notifierFd;
87 int notifierFd;
83 struct udev_monitor *udevMonitor;
88 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 *);
85
92
86private Q_SLOTS:
93private Q_SLOTS:
87 void onUDevChanges();
94 void onUDevChanges();
plugins/Unity/InputInfo/plugin.cpp+2−2
@@ -21,12 +21,12 @@
21#include "plugin.h"
21#include "plugin.h"
22
22
23// local
23// local
24−#include "qdeclarativeinputdeviceinfo_p.h"
24+#include "qdeclarativeinputdevicemodel_p.h"
25
25
26void InputInfoPlugin::registerTypes(const char *uri)
26void InputInfoPlugin::registerTypes(const char *uri)
27{
27{
28 int major = 0;
28 int major = 0;
29 int minor = 1;
29 int minor = 1;
30− qmlRegisterType<QDeclarativeInputDeviceInfo>(uri, major, minor, "InputDeviceInfo");
30+ qmlRegisterType<QDeclarativeInputDeviceModel>(uri, major, minor, "InputDeviceModel");
31 qmlRegisterType<QInputDevice>(uri, major, minor, "InputInfo");
31 qmlRegisterType<QInputDevice>(uri, major, minor, "InputInfo");
32}
32}
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 @@
1/****************************************************************************
1/****************************************************************************
2**
2**
3** Copyright (C) 2015 Jolla.
3** Copyright (C) 2015 Jolla.
4−** Copyright (C) 2015 Canonical Ltd.
5** Contact: http://www.qt-project.org/legal
4** Contact: http://www.qt-project.org/legal
6**
5**
7** This file is part of the QtSystems module of the Qt Toolkit.
6** This file is part of the QtSystems module of the Qt Toolkit.
@@ -39,39 +38,64 @@
39** $QT_END_LICENSE$
38** $QT_END_LICENSE$
40**
39**
41****************************************************************************/
40****************************************************************************/
42−#include "mockqdeclarativeinputdeviceinfo_p.h"
41+#include "qdeclarativeinputdevicemodel_p.h"
42+#include "qinputinfo.h"
43
43
44−QDeclarativeInputDeviceInfo::QDeclarativeInputDeviceInfo(QObject *parent) :
44+QDeclarativeInputDeviceModel::QDeclarativeInputDeviceModel(QObject *parent) :
45 QAbstractListModel(parent),
45 QAbstractListModel(parent),
46− deviceInfo(new QInputDeviceInfo)
46+ deviceInfo(new QInputDeviceManager),
47+ currentFilter(QInputDevice::Unknown)
47{
48{
48− connect(deviceInfo, &QInputDeviceInfo::deviceAdded,this,&QDeclarativeInputDeviceInfo::addedDevice);
49+ connect(deviceInfo,SIGNAL(ready()),this,SLOT(updateDeviceList()));
50+ connect(deviceInfo, &QInputDeviceManager::deviceAdded,this,&QDeclarativeInputDeviceModel::addedDevice);
49− connect(deviceInfo, &QInputDeviceInfo::deviceRemoved,this,&QDeclarativeInputDeviceInfo::removedDevice);
51+ connect(deviceInfo, &QInputDeviceManager::deviceRemoved,this,&QDeclarativeInputDeviceModel::removedDevice);
50}
52}
51
53
52−QDeclarativeInputDeviceInfo::~QDeclarativeInputDeviceInfo()
54+QDeclarativeInputDeviceModel::~QDeclarativeInputDeviceModel()
53{
55{
54 delete deviceInfo;
56 delete deviceInfo;
55}
57}
56
58
57−QVariant QDeclarativeInputDeviceInfo::data(const QModelIndex &index, int role) const
59+QVariant QDeclarativeInputDeviceModel::data(const QModelIndex &index, int role) const
58{
60{
59 switch (role) {
61 switch (role) {
60 case ServiceRole:
62 case ServiceRole:
61 return QVariant::fromValue(static_cast<QObject *>(inputDevices.value(index.row())));
63 return QVariant::fromValue(static_cast<QObject *>(inputDevices.value(index.row())));
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;
62− }
86+ };
63
87
64 return QVariant();
88 return QVariant();
65}
89}
66
90
67−int QDeclarativeInputDeviceInfo::rowCount(const QModelIndex &parent) const
91+int QDeclarativeInputDeviceModel::rowCount(const QModelIndex &parent) const
68{
92{
69 Q_UNUSED(parent);
93 Q_UNUSED(parent);
70
94
71 return inputDevices.count();
95 return inputDevices.count();
72}
96}
73
97
74−int QDeclarativeInputDeviceInfo::indexOf(const QString &devicePath) const
98+int QDeclarativeInputDeviceModel::indexOf(const QString &devicePath) const
75{
99{
76 int idx(-1);
100 int idx(-1);
77 Q_FOREACH (QInputDevice *device, inputDevices) {
101 Q_FOREACH (QInputDevice *device, inputDevices) {
@@ -82,56 +106,27 @@ int QDeclarativeInputDeviceInfo::indexOf(const QString &devicePath) const
82 return -1;
106 return -1;
83}
107}
84
108
85−QInputDevice *QDeclarativeInputDeviceInfo::get(int index) const
109+QInputDevice *QDeclarativeInputDeviceModel::get(int index) const
86{
110{
87 if (index < 0 || index > inputDevices.count())
111 if (index < 0 || index > inputDevices.count())
88 return 0;
112 return 0;
89 return inputDevices.value(index);
113 return inputDevices.value(index);
90}
114}
91
115
92−void QDeclarativeInputDeviceInfo::addMockMouse()
116+void QDeclarativeInputDeviceModel::updateDeviceList()
93−{
94− deviceInfo->addMockDevice(QInputDeviceInfo::Mouse);
95−}
96−
97−void QDeclarativeInputDeviceInfo::addMockKeyboard()
98−{
99− deviceInfo->addMockDevice(QInputDeviceInfo::Keyboard);
100−}
101−
102−void QDeclarativeInputDeviceInfo::removeMockMouse()
103{
117{
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);
125
119
126 int numNew = newDevices.count();
120 int numNew = newDevices.count();
127
121
128 for (int i = 0; i < numNew; i++) {
122 for (int i = 0; i < numNew; i++) {
129 int j = inputDevices.indexOf(newDevices.value(i));
123 int j = inputDevices.indexOf(newDevices.value(i));
124+
130 if (j == -1) {
125 if (j == -1) {
131− // not found -> remove from list
132 beginInsertRows(QModelIndex(), i, i);
126 beginInsertRows(QModelIndex(), i, i);
133 inputDevices.insert(i, newDevices.value(i));
127 inputDevices.insert(i, newDevices.value(i));
134 endInsertRows();
128 endInsertRows();
129+ Q_EMIT countChanged();
135 } else if (i != j) {
130 } else if (i != j) {
136 // changed its position -> move it
131 // changed its position -> move it
137 QInputDevice* device = inputDevices.value(j);
132 QInputDevice* device = inputDevices.value(j);
@@ -139,10 +134,10 @@ void QDeclarativeInputDeviceInfo::updateDeviceList()
139 inputDevices.remove(j);
134 inputDevices.remove(j);
140 inputDevices.insert(i, device);
135 inputDevices.insert(i, device);
141 endMoveRows();
136 endMoveRows();
137+ Q_EMIT countChanged();
142− } else {
138+ } //else {
143− QModelIndex changedIndex(this->index(j, 0, QModelIndex()));
139+ QModelIndex changedIndex(this->index(j, 0, QModelIndex()));
144− Q_EMIT dataChanged(changedIndex, changedIndex);
140+ Q_EMIT dataChanged(changedIndex, changedIndex);
145− }
146 }
141 }
147
142
148 int numOld = inputDevices.count();
143 int numOld = inputDevices.count();
@@ -150,24 +145,52 @@ void QDeclarativeInputDeviceInfo::updateDeviceList()
150 beginRemoveRows(QModelIndex(), numNew, numOld - 1);
145 beginRemoveRows(QModelIndex(), numNew, numOld - 1);
151 inputDevices.remove(numNew, numOld - numNew);
146 inputDevices.remove(numNew, numOld - numNew);
152 endRemoveRows();
147 endRemoveRows();
148+ Q_EMIT countChanged();
153 }
149 }
154}
150}
155
151
156−void QDeclarativeInputDeviceInfo::addedDevice(const QString &devicePath)
152+void QDeclarativeInputDeviceModel::addedDevice(const QString &devicePath)
157{
153{
158 updateDeviceList();
154 updateDeviceList();
159− Q_EMIT newDevice(devicePath);
155+ Q_EMIT deviceAdded(devicePath);
160}
156}
161
157
162−void QDeclarativeInputDeviceInfo::removedDevice(const QString &devicePath)
158+void QDeclarativeInputDeviceModel::removedDevice(const QString &devicePath)
163{
159{
164 updateDeviceList();
160 updateDeviceList();
165 Q_EMIT deviceRemoved(devicePath);
161 Q_EMIT deviceRemoved(devicePath);
166}
162}
167
163
168−QHash<int, QByteArray> QDeclarativeInputDeviceInfo::roleNames() const
164+QHash<int,QByteArray> QDeclarativeInputDeviceModel::roleNames() const
169{
165{
170 QHash<int, QByteArray> roles;
166 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";
172 return roles;
174 return roles;
173}
175}
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 @@
39**
39**
40****************************************************************************/
40****************************************************************************/
41
41
42−#ifndef QDECLARATIVEINPUTDEVICEINFO_H
42+#ifndef QDECLARATIVEINPUTDEVICEMODEL_H
43−#define QDECLARATIVEINPUTDEVICEINFO_H
43+#define QDECLARATIVEINPUTDEVICEMODEL_H
44
44
45#include <QObject>
45#include <QObject>
46#include <QAbstractListModel>
46#include <QAbstractListModel>
47#include "qinputinfo.h"
47#include "qinputinfo.h"
48
48
49−class QDeclarativeInputDeviceInfo : public QAbstractListModel
49+class QDeclarativeInputDeviceModel : public QAbstractListModel
50{
50{
51 Q_OBJECT
51 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)
53
56
54public:
57public:
55 enum ItemRoles {
58 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
57 };
67 };
58
68
59− explicit QDeclarativeInputDeviceInfo(QObject *parent = 0);
69+ explicit QDeclarativeInputDeviceModel(QObject *parent = 0);
60− virtual ~QDeclarativeInputDeviceInfo();
70+ virtual ~QDeclarativeInputDeviceModel();
61
71
62 QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
72 QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
63 int rowCount(const QModelIndex &parent = QModelIndex()) const;
73 int rowCount(const QModelIndex &parent = QModelIndex()) const;
64
74
65− QHash<int, QByteArray> roleNames() const;
75+ void setDeviceFilter(QInputDevice::InputType filter);
76+ QInputDevice::InputType deviceFilter();
66
77
67 Q_INVOKABLE int indexOf(const QString &devicePath) const;
78 Q_INVOKABLE int indexOf(const QString &devicePath) const;
68
79
69 Q_INVOKABLE QInputDevice *get(int index) const;
80 Q_INVOKABLE QInputDevice *get(int index) const;
81+ QHash<int, QByteArray> roleNames() const;
70
82
71Q_SIGNALS:
83Q_SIGNALS:
72− void newDevice(const QString &devicePath);
84+ void deviceAdded(const QString &devicePath);
73 void deviceRemoved(const QString &devicePath);
85 void deviceRemoved(const QString &devicePath);
86+ void deviceFilterChanged(const QInputDevice::InputType filter);
87+ void countChanged();
74
88
75public Q_SLOTS:
89public Q_SLOTS:
76 void updateDeviceList();
90 void updateDeviceList();
77private:
91private:
78− QInputDeviceInfo *deviceInfo;
92+ QInputDeviceManager *deviceInfo;
79 QVector<QInputDevice *> inputDevices;
93 QVector<QInputDevice *> inputDevices;
94+ QInputDevice::InputType currentFilter;
95+
80private Q_SLOTS:
96private Q_SLOTS:
81 void addedDevice(const QString &);
97 void addedDevice(const QString &);
82 void removedDevice(const QString &path);
98 void removedDevice(const QString &path);
83
99
84};
100};
85
101
86−#endif // QDECLARATIVEINPUTDEVICEINFO_H
102+#endif // QDECLARATIVEINPUTDEVICEMODEL_H
plugins/Unity/InputInfo/qinputinfo.cpp+129−18
@@ -41,16 +41,25 @@
41
41
42#include "qinputinfo.h"
42#include "qinputinfo.h"
43
43
44+#if defined(UNITY_MOCKS)
45+#include "qinputdeviceinfo_mock_p.h"
44−#if defined(Q_OS_LINUX)
46+#elif defined(Q_OS_LINUX)
45#include "linux/qinputdeviceinfo_linux_p.h"
47#include "linux/qinputdeviceinfo_linux_p.h"
46#endif
48#endif
47
49
50+Q_GLOBAL_STATIC(QInputDeviceManagerPrivate, inputDeviceManagerPrivate)
51+
48QT_BEGIN_NAMESPACE
52QT_BEGIN_NAMESPACE
49
53
54+QInputDeviceManagerPrivate * QInputDeviceManagerPrivate::instance()
55+{
56+ QInputDeviceManagerPrivate *priv = inputDeviceManagerPrivate();
57+ return priv;
58+}
50
59
51QInputDevicePrivate::QInputDevicePrivate(QObject *parent) :
60QInputDevicePrivate::QInputDevicePrivate(QObject *parent) :
52 QObject(parent),
61 QObject(parent),
53− types(QInputDeviceInfo::Unknown)
62+ type(QInputDevice::Unknown)
54{
63{
55}
64}
56
65
@@ -60,100 +69,202 @@ QInputDevice::QInputDevice(QObject *parent) :
60{
69{
61}
70}
62
71
72+/*
73+ * Returns the name of this input device.
74+ */
63QString QInputDevice::name() const
75QString QInputDevice::name() const
64{
76{
65 return d_ptr->name;
77 return d_ptr->name;
66}
78}
67
79
80+/*
81+ * Sets the name of this input device to \b name.
82+ */
68void QInputDevice::setName(const QString &name)
83void QInputDevice::setName(const QString &name)
69{
84{
70 d_ptr->name = name;
85 d_ptr->name = name;
71}
86}
72
87
88+/*
89+ * Returns the device path of this device.
90+ */
73QString QInputDevice::devicePath() const
91QString QInputDevice::devicePath() const
74{
92{
75 return d_ptr->devicePath;
93 return d_ptr->devicePath;
76}
94}
77
95
96+/*
97+ * Sets the device ppath of this device to /b path.
98+ */
78void QInputDevice::setDevicePath(const QString &path)
99void QInputDevice::setDevicePath(const QString &path)
79{
100{
80 d_ptr->devicePath = path;
101 d_ptr->devicePath = path;
81}
102}
82
103
104+/*
105+ * Returns the number of buttons this device has.
106+ */
83QList <int> QInputDevice::buttons() const
107QList <int> QInputDevice::buttons() const
84{
108{
85 return d_ptr->buttons;
109 return d_ptr->buttons;
86}
110}
87
111
112+/*
113+ * Adds a button
114+ */
88void QInputDevice::addButton(int buttonCode)
115void QInputDevice::addButton(int buttonCode)
89{
116{
90 d_ptr->buttons.append(buttonCode);
117 d_ptr->buttons.append(buttonCode);
91}
118}
92
119
120+/*
121+ * Returns the number of switch of this device.
122+ */
93QList <int> QInputDevice::switches() const
123QList <int> QInputDevice::switches() const
94{
124{
95 return d_ptr->switches;
125 return d_ptr->switches;
96}
126}
97
127
128+/*
129+ * Adds a switch
130+ */
98void QInputDevice::addSwitch(int switchCode)
131void QInputDevice::addSwitch(int switchCode)
99{
132{
100 d_ptr->switches.append(switchCode);
133 d_ptr->switches.append(switchCode);
101}
134}
102
135
136+/*
137+ * Returns a list of the relative axis of this device
138+ */
103QList <int> QInputDevice::relativeAxis() const
139QList <int> QInputDevice::relativeAxis() const
104{
140{
105 return d_ptr->relativeAxis;
141 return d_ptr->relativeAxis;
106}
142}
107
143
144+/*
145+ */
108void QInputDevice::addRelativeAxis(int axisCode)
146void QInputDevice::addRelativeAxis(int axisCode)
109{
147{
110 d_ptr->relativeAxis.append(axisCode);
148 d_ptr->relativeAxis.append(axisCode);
111}
149}
112
150
151+/*
152+ * Returns a list of the absolute axis of this device
153+ */
113QList <int> QInputDevice::absoluteAxis() const
154QList <int> QInputDevice::absoluteAxis() const
114{
155{
115 return d_ptr->absoluteAxis;
156 return d_ptr->absoluteAxis;
116}
157}
117
158
159+/*
160+ */
118void QInputDevice::addAbsoluteAxis(int axisCode)
161void QInputDevice::addAbsoluteAxis(int axisCode)
119{
162{
120 d_ptr->absoluteAxis.append(axisCode);
163 d_ptr->absoluteAxis.append(axisCode);
121}
164}
122
165
123−QInputDeviceInfo::InputTypes QInputDevice::types()
166+/*
167+ * Returns a QInputDevice::InputTypeFlags of all the types of types.
168+ */
169+QInputDevice::InputTypeFlags QInputDevice::type() const
124{
170{
125− return d_ptr->types;
171+ return d_ptr->type;
126}
172}
127
173
128−void QInputDevice::setTypes(QInputDeviceInfo::InputTypes types)
174+/*
175+ */
176+void QInputDevice::setType(QInputDevice::InputTypeFlags type) //? setTypes?
129{
177{
130− d_ptr->types = types;
178+ d_ptr->type = type;
131}
179}
132
180
133−
181+QInputDeviceManager::QInputDeviceManager(QObject *parent) :
134−QInputDeviceInfo::QInputDeviceInfo(QObject *parent) :
135 QObject(parent),
182 QObject(parent),
136− d_ptr(new QInputDeviceInfoPrivate(this))
183+ d_ptr(inputDeviceManagerPrivate)
137{
184{
138− connect(d_ptr, &QInputDeviceInfoPrivate::newDevice,this,&QInputDeviceInfo::addedDevice);
185+ connect(d_ptr, &QInputDeviceManagerPrivate::deviceAdded,this,&QInputDeviceManager::addedDevice);
139− connect(d_ptr, &QInputDeviceInfoPrivate::deviceRemoved,this,&QInputDeviceInfo::deviceRemoved);
186+ connect(d_ptr, &QInputDeviceManagerPrivate::deviceRemoved,this,&QInputDeviceManager::deviceRemoved);
140
187
141− connect(d_ptr, &QInputDeviceInfoPrivate::ready, this, &QInputDeviceInfo::ready);
188+ connect(d_ptr,SIGNAL(ready()),this,SIGNAL(ready()));
142}
189}
143
190
144−QVector <QInputDevice *> QInputDeviceInfo::deviceList()
191+/*
192+ * Returns a QMap of known input devices.
193+ */
194+QMap <QString, QInputDevice *> QInputDeviceManager::deviceMap()
145{
195{
146− return d_ptr->deviceList;
196+ return d_ptr->deviceMap;
147}
197}
148
198
149−QMap <QString, QInputDevice *> QInputDeviceInfo::deviceMap()
199+/*
200+ */
201+void QInputDeviceManager::addedDevice(const QString & devicePath)
150{
202{
151− return d_ptr->deviceMap;
203+ Q_EMIT deviceAdded(devicePath);
152}
204}
153
205
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)
155{
210{
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+ }
157}
268}
158
269
159QT_END_NAMESPACE
270QT_END_NAMESPACE
plugins/Unity/InputInfo/qinputinfo.h+56−56
@@ -48,15 +48,19 @@
48#include <QSocketNotifier>
48#include <QSocketNotifier>
49#include <QDebug>
49#include <QDebug>
50
50
51−class QInputDeviceInfoPrivate;
51+class QInputDeviceManagerPrivate;
52class QInputDevicePrivate;
52class QInputDevicePrivate;
53class QInputDevice;
53class QInputDevice;
54
54
55−class QInputDeviceInfoPrivate;
55+class QInputDeviceManager;
56+
56−class QInputDeviceInfo : public QObject
57+class QInputDevice : public QObject
57{
58{
58 Q_OBJECT
59 Q_OBJECT
59− Q_PROPERTY(int deviceCount READ deviceCount)
60+ Q_ENUMS(InputType)
61+ Q_FLAGS(InputType InputTypeFlags)
62+ friend class QInputDeviceManagerPrivate;
63+
60public:
64public:
61
65
62 enum InputType {
66 enum InputType {
@@ -69,67 +73,19 @@ public:
69 Switch = 32
73 Switch = 32
70 };
74 };
71 Q_ENUMS(InputType)
75 Q_ENUMS(InputType)
72− Q_FLAGS(InputTypes)
73− Q_DECLARE_FLAGS(InputTypes, InputType)
76+ Q_DECLARE_FLAGS(InputTypeFlags, 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)
112
77
113−public:
114 explicit QInputDevice(QObject *parent = 0);
78 explicit QInputDevice(QObject *parent = 0);
115−
116 QString name() const;
79 QString name() const;
117 QString devicePath() const;
80 QString devicePath() const;
118 QList <int> buttons() const; //keys event code
81 QList <int> buttons() const; //keys event code
119 QList <int> switches() const;
82 QList <int> switches() const;
120 QList <int> relativeAxis() const;
83 QList <int> relativeAxis() const;
121 QList <int> absoluteAxis() const;
84 QList <int> absoluteAxis() const;
85+ QInputDevice::InputTypeFlags type() const;
122
86
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();
132private:
87private:
88+
133 QInputDevicePrivate *d_ptr;
89 QInputDevicePrivate *d_ptr;
134 void setName(const QString &);
90 void setName(const QString &);
135 void setDevicePath(const QString &);
91 void setDevicePath(const QString &);
@@ -137,7 +93,51 @@ private:
137 void addSwitch(int);
93 void addSwitch(int);
138 void addRelativeAxis(int);
94 void addRelativeAxis(int);
139 void addAbsoluteAxis(int);
95 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
141};
141};
142
142
143#endif // QINPUTINFO_H
143#endif // QINPUTINFO_H
plugins/Unity/Session/dbusunitysessionservice.cpp+64−36
@@ -61,7 +61,7 @@ public:
61 QStringLiteral("GetSessionByPID"));
61 QStringLiteral("GetSessionByPID"));
62 msg << (quint32) getpid();
62 msg << (quint32) getpid();
63
63
64− QDBusReply<QDBusObjectPath> reply = QDBusConnection::systemBus().asyncCall(msg);
64+ QDBusReply<QDBusObjectPath> reply = QDBusConnection::systemBus().call(msg);
65 if (reply.isValid()) {
65 if (reply.isValid()) {
66 logindSessionPath = reply.value().path();
66 logindSessionPath = reply.value().path();
67
67
@@ -92,20 +92,28 @@ public:
92 msg << "Unity"; // who
92 msg << "Unity"; // who
93 msg << "Unity8 handles power events"; // why
93 msg << "Unity8 handles power events"; // why
94 msg << "block"; // mode
94 msg << "block"; // mode
95− QDBusReply<QDBusUnixFileDescriptor> desc = QDBusConnection::systemBus().asyncCall(msg);
96
95
97− if (desc.isValid()) {
96+ QDBusPendingCall pendingCall = QDBusConnection::systemBus().asyncCall(msg);
98− m_systemdInhibitFd = desc.value();
97+ QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall, this);
99− } else {
98+ connect(watcher, &QDBusPendingCallWatcher::finished,
99+ this, [this](QDBusPendingCallWatcher* watcher) {
100+
101+ QDBusPendingReply<QDBusUnixFileDescriptor> reply = *watcher;
102+ watcher->deleteLater();
103+ if (reply.isError()) {
100− qWarning() << "failed to inhibit systemd powersave handling";
104+ qWarning() << "Failed to inhibit systemd powersave handling" << reply.error().message();
105+ return;
106+ }
107+
108+ m_systemdInhibitFd = reply.value();
101− }
109+ });
102 }
110 }
103
111
104
112
105 bool checkLogin1Call(const QString &method) const
113 bool checkLogin1Call(const QString &method) const
106 {
114 {
107 QDBusMessage msg = QDBusMessage::createMethodCall(LOGIN1_SERVICE, LOGIN1_PATH, LOGIN1_IFACE, method);
115 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);
109 return reply.isValid() && (reply == QStringLiteral("yes") || reply == QStringLiteral("challenge"));
117 return reply.isValid() && (reply == QStringLiteral("yes") || reply == QStringLiteral("challenge"));
110 }
118 }
111
119
@@ -119,6 +127,21 @@ public:
119 QDBusConnection::systemBus().asyncCall(msg);
127 QDBusConnection::systemBus().asyncCall(msg);
120 }
128 }
121
129
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+
122 void checkActive()
145 void checkActive()
123 {
146 {
124 if (logindSessionPath.isEmpty()) {
147 if (logindSessionPath.isEmpty()) {
@@ -133,12 +156,20 @@ public:
133 msg << LOGIN1_SESSION_IFACE;
156 msg << LOGIN1_SESSION_IFACE;
134 msg << ACTIVE_KEY;
157 msg << ACTIVE_KEY;
135
158
136− QDBusReply<QVariant> reply = QDBusConnection::systemBus().asyncCall(msg);
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();
137− if (reply.isValid()) {
166+ if (reply.isError()) {
138− isSessionActive = reply.value().toBool();
139− } else {
140− qWarning() << "Failed to get Active property" << reply.error().message();
167+ qWarning() << "Failed to get Active property" << reply.error().message();
168+ return;
169+ }
170+
171+ setActive(reply.value().toBool());
141− }
172+ });
142 }
173 }
143
174
144 quint32 screensaverActiveTime() const
175 quint32 screensaverActiveTime() const
@@ -159,7 +190,7 @@ public:
159 msg << LOGIN1_SESSION_IFACE;
190 msg << LOGIN1_SESSION_IFACE;
160 msg << IDLE_SINCE_KEY;
191 msg << IDLE_SINCE_KEY;
161
192
162− QDBusReply<QVariant> reply = QDBusConnection::systemBus().asyncCall(msg);
193+ QDBusReply<QVariant> reply = QDBusConnection::systemBus().call(msg);
163 if (reply.isValid()) {
194 if (reply.isValid()) {
164 return reply.value().value<quint64>();
195 return reply.value().value<quint64>();
165 } else {
196 } else {
@@ -184,22 +215,10 @@ private Q_SLOTS:
184 {
215 {
185 Q_UNUSED(iface)
216 Q_UNUSED(iface)
186
217
187− if (changedProps.contains(ACTIVE_KEY) || invalidatedProps.contains(ACTIVE_KEY)) {
218+ if (changedProps.contains(ACTIVE_KEY)) {
188− if (changedProps.value(ACTIVE_KEY).isValid()) {
189− isSessionActive = changedProps.value(ACTIVE_KEY).toBool();
219+ setActive(changedProps.value(ACTIVE_KEY).toBool());
190− } else {
220+ } else if (invalidatedProps.contains(ACTIVE_KEY)) {
191− checkActive();
221+ 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− }
203 }
222 }
204 }
223 }
205
224
@@ -325,13 +344,22 @@ void DBusUnitySessionService::Lock()
325 sessionPath,
344 sessionPath,
326 QStringLiteral("org.freedesktop.DisplayManager.Session"),
345 QStringLiteral("org.freedesktop.DisplayManager.Session"),
327 QStringLiteral("Lock"));
346 QStringLiteral("Lock"));
347+
328− QDBusReply<void> reply = QDBusConnection::systemBus().asyncCall(msg);
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();
329− if (!reply.isValid()) {
355+ if (reply.isError()) {
330− qWarning() << "Lock call failed" << reply.error().message();
356+ qWarning() << "Lock call failed" << reply.error().message();
331− } else {
357+ return;
358+ }
359+
332 // emit Locked when the call succeeds
360 // emit Locked when the call succeeds
333 Q_EMIT Locked();
361 Q_EMIT Locked();
334− }
362+ });
335}
363}
336
364
337bool DBusUnitySessionService::IsLocked() const
365bool DBusUnitySessionService::IsLocked() const
plugins/Utils/CMakeLists.txt+2
@@ -10,6 +10,8 @@ include_directories(
10
10
11set(QMLPLUGIN_SRC
11set(QMLPLUGIN_SRC
12 activefocuslogger.cpp
12 activefocuslogger.cpp
13+ ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h
14+ applicationsfiltermodel.cpp
13 constants.cpp
15 constants.cpp
14 HomeKeyWatcher.cpp
16 HomeKeyWatcher.cpp
15 inputwatcher.cpp
17 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 @@
35#include "windowstatestorage.h"
35#include "windowstatestorage.h"
36#include "constants.h"
36#include "constants.h"
37#include "timezoneFormatter.h"
37#include "timezoneFormatter.h"
38+#include "applicationsfiltermodel.h"
38
39
39static QObject *createWindowStateStorage(QQmlEngine *engine, QJSEngine *scriptEngine)
40static QObject *createWindowStateStorage(QQmlEngine *engine, QJSEngine *scriptEngine)
40{
41{
@@ -66,6 +67,7 @@ void UtilsPlugin::registerTypes(const char *uri)
66 qmlRegisterSingletonType<TimezoneFormatter>(uri, 0, 1, "TimezoneFormatter",
67 qmlRegisterSingletonType<TimezoneFormatter>(uri, 0, 1, "TimezoneFormatter",
67 [](QQmlEngine*, QJSEngine*) -> QObject* { return new TimezoneFormatter; });
68 [](QQmlEngine*, QJSEngine*) -> QObject* { return new TimezoneFormatter; });
68 qmlRegisterType<ActiveFocusLogger>(uri, 0, 1, "ActiveFocusLogger");
69 qmlRegisterType<ActiveFocusLogger>(uri, 0, 1, "ActiveFocusLogger");
70+ qmlRegisterType<ApplicationsFilterModel>(uri, 0, 1, "ApplicationsFilterModel");
69}
71}
70
72
71void UtilsPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
73void UtilsPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
po/am.po+4−4
@@ -8,15 +8,15 @@ msgstr ""
8"Project-Id-Version: unity8\n"
8"Project-Id-Version: unity8\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2015-10-19 14:34+0100\n"
10"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"
12"Last-Translator: samson <Unknown>\n"
12"Last-Translator: samson <Unknown>\n"
13"Language-Team: Amharic <am@li.org>\n"
13"Language-Team: Amharic <am@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n > 1;\n"
17"Plural-Forms: nplurals=2; plural=n > 1;\n"
18−"X-Launchpad-Export-Date: 2015-11-07 05:12+0000\n"
18+"X-Launchpad-Export-Date: 2015-11-20 05:31+0000\n"
19−"X-Generator: Launchpad (build 17838)\n"
19+"X-Generator: Launchpad (build 17850)\n"
20
20
21#: plugins/IntegratedLightDM/Greeter.cpp:112
21#: plugins/IntegratedLightDM/Greeter.cpp:112
22msgid "Password: "
22msgid "Password: "
@@ -290,7 +290,7 @@ msgstr "ግምገማ መጨመሪያ"
290
290
291#: qml/Dash/Previews/PreviewSharing.qml:51
291#: qml/Dash/Previews/PreviewSharing.qml:51
292msgid "Preview Share Item"
292msgid "Preview Share Item"
293−msgstr ""
293+msgstr "የሚካፈሉትን እቃ ቅድመ እይታ"
294
294
295#: qml/Dash/PullToRefreshScopeStyle.qml:55
295#: qml/Dash/PullToRefreshScopeStyle.qml:55
296msgid "Pull to refresh…"
296msgid "Pull to refresh…"
po/unity8.pot+67−138
@@ -8,7 +8,7 @@ msgid ""
8msgstr ""
8msgstr ""
9"Project-Id-Version: unity8\n"
9"Project-Id-Version: unity8\n"
10"Report-Msgid-Bugs-To: \n"
10"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"
12"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
12"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14"Language-Team: LANGUAGE <LL@li.org>\n"
14"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -35,86 +35,6 @@ msgstr ""
35msgid "Unpin shortcut"
35msgid "Unpin shortcut"
36msgstr ""
36msgstr ""
37
37
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−
118#: qml/Components/DelayedLockscreen.qml:49
38#: qml/Components/DelayedLockscreen.qml:49
119msgid "Device Locked"
39msgid "Device Locked"
120msgstr ""
40msgstr ""
@@ -134,83 +54,83 @@ msgid_plural "Please wait %1 minutes and then try again…"
134msgstr[0] ""
54msgstr[0] ""
135msgstr[1] ""
55msgstr[1] ""
136
56
137−#: qml/Components/Dialogs.qml:115
57+#: qml/Components/Dialogs.qml:116
138msgctxt "Title: Lock/Log out dialog"
58msgctxt "Title: Lock/Log out dialog"
139msgid "Log out"
59msgid "Log out"
140msgstr ""
60msgstr ""
141
61
142−#: qml/Components/Dialogs.qml:116
62+#: qml/Components/Dialogs.qml:117
143msgid "Are you sure you want to log out?"
63msgid "Are you sure you want to log out?"
144msgstr ""
64msgstr ""
145
65
146−#: qml/Components/Dialogs.qml:118
66+#: qml/Components/Dialogs.qml:119
147msgctxt "Button: Lock the system"
67msgctxt "Button: Lock the system"
148msgid "Lock"
68msgid "Lock"
149msgstr ""
69msgstr ""
150
70
151−#: qml/Components/Dialogs.qml:125
71+#: qml/Components/Dialogs.qml:126
152msgctxt "Button: Log out from the system"
72msgctxt "Button: Log out from the system"
153msgid "Log Out"
73msgid "Log Out"
154msgstr ""
74msgstr ""
155
75
156−#: qml/Components/Dialogs.qml:132 qml/Components/Dialogs.qml:165
76+#: qml/Components/Dialogs.qml:133 qml/Components/Dialogs.qml:166
157−#: qml/Components/Dialogs.qml:224
77+#: qml/Components/Dialogs.qml:225
158msgid "Cancel"
78msgid "Cancel"
159msgstr ""
79msgstr ""
160
80
161−#: qml/Components/Dialogs.qml:144
81+#: qml/Components/Dialogs.qml:145
162msgctxt "Title: Reboot/Shut down dialog"
82msgctxt "Title: Reboot/Shut down dialog"
163msgid "Shut down"
83msgid "Shut down"
164msgstr ""
84msgstr ""
165
85
166−#: qml/Components/Dialogs.qml:145
86+#: qml/Components/Dialogs.qml:146
167msgid "Are you sure you want to shut down?"
87msgid "Are you sure you want to shut down?"
168msgstr ""
88msgstr ""
169
89
170−#: qml/Components/Dialogs.qml:147
90+#: qml/Components/Dialogs.qml:148
171msgctxt "Button: Reboot the system"
91msgctxt "Button: Reboot the system"
172msgid "Reboot"
92msgid "Reboot"
173msgstr ""
93msgstr ""
174
94
175−#: qml/Components/Dialogs.qml:156
95+#: qml/Components/Dialogs.qml:157
176msgctxt "Button: Shut down the system"
96msgctxt "Button: Shut down the system"
177msgid "Shut down"
97msgid "Shut down"
178msgstr ""
98msgstr ""
179
99
180−#: qml/Components/Dialogs.qml:178
100+#: qml/Components/Dialogs.qml:179
181msgctxt "Title: Reboot dialog"
101msgctxt "Title: Reboot dialog"
182msgid "Reboot"
102msgid "Reboot"
183msgstr ""
103msgstr ""
184
104
185−#: qml/Components/Dialogs.qml:179
105+#: qml/Components/Dialogs.qml:180
186msgid "Are you sure you want to reboot?"
106msgid "Are you sure you want to reboot?"
187msgstr ""
107msgstr ""
188
108
189−#: qml/Components/Dialogs.qml:181
109+#: qml/Components/Dialogs.qml:182
190msgid "No"
110msgid "No"
191msgstr ""
111msgstr ""
192
112
193−#: qml/Components/Dialogs.qml:188
113+#: qml/Components/Dialogs.qml:189
194msgid "Yes"
114msgid "Yes"
195msgstr ""
115msgstr ""
196
116
197−#: qml/Components/Dialogs.qml:203
117+#: qml/Components/Dialogs.qml:204
198msgctxt "Title: Power off/Restart dialog"
118msgctxt "Title: Power off/Restart dialog"
199msgid "Power"
119msgid "Power"
200msgstr ""
120msgstr ""
201
121
202−#: qml/Components/Dialogs.qml:204
122+#: qml/Components/Dialogs.qml:205
203msgid ""
123msgid ""
204"Are you sure you would like\n"
124"Are you sure you would like\n"
205"to power off?"
125"to power off?"
206msgstr ""
126msgstr ""
207
127
208−#: qml/Components/Dialogs.qml:206
128+#: qml/Components/Dialogs.qml:207
209msgctxt "Button: Power off the system"
129msgctxt "Button: Power off the system"
210msgid "Power off"
130msgid "Power off"
211msgstr ""
131msgstr ""
212
132
213−#: qml/Components/Dialogs.qml:215
133+#: qml/Components/Dialogs.qml:216
214msgctxt "Button: Restart the system"
134msgctxt "Button: Restart the system"
215msgid "Restart"
135msgid "Restart"
216msgstr ""
136msgstr ""
@@ -227,42 +147,42 @@ msgstr ""
227msgid "OK"
147msgid "OK"
228msgstr ""
148msgstr ""
229
149
230−#: qml/Dash/GenericScopeView.qml:569 qml/Dash/GenericScopeView.qml:727
150+#: qml/Dash/DashPageHeader.qml:265
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
244msgctxt "Button: Open the Ubuntu Store"
151msgctxt "Button: Open the Ubuntu Store"
245msgid "Store"
152msgid "Store"
246msgstr ""
153msgstr ""
247
154
248−#: qml/Dash/PageHeader.qml:276
155+#: qml/Dash/DashPageHeader.qml:272
249msgctxt "Button: Start a search in the current dash scope"
156msgctxt "Button: Start a search in the current dash scope"
250msgid "Search"
157msgid "Search"
251msgstr ""
158msgstr ""
252
159
253−#: qml/Dash/PageHeader.qml:286
160+#: qml/Dash/DashPageHeader.qml:282
254msgctxt "Button: Show the current dash scope settings"
161msgctxt "Button: Show the current dash scope settings"
255msgid "Settings"
162msgid "Settings"
256msgstr ""
163msgstr ""
257
164
258−#: qml/Dash/PageHeader.qml:293
165+#: qml/Dash/DashPageHeader.qml:289
259msgid "Remove from Favorites"
166msgid "Remove from Favorites"
260msgstr ""
167msgstr ""
261
168
262−#: qml/Dash/PageHeader.qml:293
169+#: qml/Dash/DashPageHeader.qml:289
263msgid "Add to Favorites"
170msgid "Add to Favorites"
264msgstr ""
171msgstr ""
265
172
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+
266#: qml/Dash/Previews/PreviewActionCombo.qml:35
186#: qml/Dash/Previews/PreviewActionCombo.qml:35
267msgid "More..."
187msgid "More..."
268msgstr ""
188msgstr ""
@@ -273,8 +193,6 @@ msgstr ""
273
193
274#: qml/Dash/Previews/PreviewCommentInput.qml:68
194#: qml/Dash/Previews/PreviewCommentInput.qml:68
275#: qml/Dash/Previews/PreviewRatingInput.qml:174
195#: qml/Dash/Previews/PreviewRatingInput.qml:174
276−#: qml/Panel/Indicators/MessageMenuItemFactory.qml:148
277−#: qml/Panel/Indicators/MessageMenuItemFactory.qml:205
278msgid "Send"
196msgid "Send"
279msgstr ""
197msgstr ""
280
198
@@ -314,6 +232,10 @@ msgstr ""
314msgid "Also installed"
232msgid "Also installed"
315msgstr ""
233msgstr ""
316
234
235+#: qml/DisabledScreenNotice.qml:42
236+msgid "Your device is now connected to an external display."
237+msgstr ""
238+
317#: qml/Greeter/CoverPage.qml:107
239#: qml/Greeter/CoverPage.qml:107
318msgid "Unlock"
240msgid "Unlock"
319msgstr ""
241msgstr ""
@@ -381,7 +303,7 @@ msgstr ""
381msgid "Sorry, incorrect passcode"
303msgid "Sorry, incorrect passcode"
382msgstr ""
304msgstr ""
383
305
384−#: qml/Notifications/NotificationMenuItemFactory.qml:128
306+#: qml/Notifications/NotificationMenuItemFactory.qml:129
385msgid "Show password"
307msgid "Show password"
386msgstr ""
308msgstr ""
387
309
@@ -393,71 +315,78 @@ msgstr ""
393msgid "Conference"
315msgid "Conference"
394msgstr ""
316msgstr ""
395
317
396−#: qml/Panel/Indicators/MenuItemFactory.qml:727
318+#: qml/Panel/Indicators/MenuItemFactory.qml:731
397msgid "Nothing is playing"
319msgid "Nothing is playing"
398msgstr ""
320msgstr ""
399
321
400−#: qml/Panel/Indicators/MenuItemFactory.qml:856
322+#: qml/Panel/Indicators/MenuItemFactory.qml:860
401#, qt-format
323#, qt-format
402msgid "%1 hour"
324msgid "%1 hour"
403msgid_plural "%1 hours"
325msgid_plural "%1 hours"
404msgstr[0] ""
326msgstr[0] ""
405msgstr[1] ""
327msgstr[1] ""
406
328
407−#: qml/Panel/Indicators/MenuItemFactory.qml:860
329+#: qml/Panel/Indicators/MenuItemFactory.qml:864
408#, qt-format
330#, qt-format
409msgid "%1 minute"
331msgid "%1 minute"
410msgid_plural "%1 minutes"
332msgid_plural "%1 minutes"
411msgstr[0] ""
333msgstr[0] ""
412msgstr[1] ""
334msgstr[1] ""
413
335
414−#: qml/Panel/Indicators/MenuItemFactory.qml:865
336+#: qml/Panel/Indicators/MenuItemFactory.qml:869
415#, qt-format
337#, qt-format
416msgid "%1 second"
338msgid "%1 second"
417msgid_plural "%1 seconds"
339msgid_plural "%1 seconds"
418msgstr[0] ""
340msgstr[0] ""
419msgstr[1] ""
341msgstr[1] ""
420
342
421−#: qml/Panel/Indicators/MenuItemFactory.qml:868
343+#: qml/Panel/Indicators/MenuItemFactory.qml:872
422msgid "0 seconds"
344msgid "0 seconds"
423msgstr ""
345msgstr ""
424
346
425#. Translators: String like "1 hour, 2 minutes, 3 seconds remaining"
347#. Translators: String like "1 hour, 2 minutes, 3 seconds remaining"
426−#: qml/Panel/Indicators/MenuItemFactory.qml:870
348+#: qml/Panel/Indicators/MenuItemFactory.qml:874
427#, qt-format
349#, qt-format
428msgid "%1 remaining"
350msgid "%1 remaining"
429msgstr ""
351msgstr ""
430
352
431−#: qml/Panel/Indicators/MenuItemFactory.qml:876
353+#: qml/Panel/Indicators/MenuItemFactory.qml:880
432msgid "In queue…"
354msgid "In queue…"
433msgstr ""
355msgstr ""
434
356
435−#: qml/Panel/Indicators/MenuItemFactory.qml:880
357+#: qml/Panel/Indicators/MenuItemFactory.qml:884
436msgid "Downloading"
358msgid "Downloading"
437msgstr ""
359msgstr ""
438
360
439−#: qml/Panel/Indicators/MenuItemFactory.qml:882
361+#: qml/Panel/Indicators/MenuItemFactory.qml:886
440msgid "Paused, tap to resume"
362msgid "Paused, tap to resume"
441msgstr ""
363msgstr ""
442
364
443−#: qml/Panel/Indicators/MenuItemFactory.qml:884
365+#: qml/Panel/Indicators/MenuItemFactory.qml:888
444msgid "Canceled"
366msgid "Canceled"
445msgstr ""
367msgstr ""
446
368
447−#: qml/Panel/Indicators/MenuItemFactory.qml:886
369+#: qml/Panel/Indicators/MenuItemFactory.qml:890
448msgid "Finished"
370msgid "Finished"
449msgstr ""
371msgstr ""
450
372
451−#: qml/Panel/Indicators/MenuItemFactory.qml:888
373+#: qml/Panel/Indicators/MenuItemFactory.qml:892
452msgid "Failed, tap to retry"
374msgid "Failed, tap to retry"
453msgstr ""
375msgstr ""
454
376
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
456msgctxt "Label: Hint in message indicator line edit"
384msgctxt "Label: Hint in message indicator line edit"
457msgid "Reply"
385msgid "Reply"
458msgstr ""
386msgstr ""
459
387
460−#: qml/Panel/Indicators/MessageMenuItemFactory.qml:204
388+#: qml/Panel/Indicators/MessageMenuItemFactory.qml:213
389+msgctxt "Button: Call back on phone"
461msgid "Call back"
390msgid "Call back"
462msgstr ""
391msgstr ""
463
392
@@ -541,7 +470,7 @@ msgstr ""
541
470
542#: qml/Wizard/Pages/10-welcome.qml:88 qml/Wizard/Pages/30-passwd-type.qml:128
471#: qml/Wizard/Pages/10-welcome.qml:88 qml/Wizard/Pages/30-passwd-type.qml:128
543#: qml/Wizard/Pages/40-wifi.qml:215 qml/Wizard/Pages/50-location.qml:131
472#: 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
545#: qml/Wizard/Pages/passwd-set.qml:90
474#: qml/Wizard/Pages/passwd-set.qml:90
546msgid "Continue"
475msgid "Continue"
547msgstr ""
476msgstr ""
@@ -639,17 +568,17 @@ msgstr ""
639msgid "You can change your mind later in <b>System Settings</b>."
568msgid "You can change your mind later in <b>System Settings</b>."
640msgstr ""
569msgstr ""
641
570
642−#: qml/Wizard/Pages/60-reporting.qml:24
571+#: qml/Wizard/Pages/60-reporting.qml:26
643msgid "Improving your experience"
572msgid "Improving your experience"
644msgstr ""
573msgstr ""
645
574
646−#: qml/Wizard/Pages/60-reporting.qml:36
575+#: qml/Wizard/Pages/60-reporting.qml:46
647msgid ""
576msgid ""
648"Your phone is set up to automatically report errors to Canonical and its "
577"Your phone is set up to automatically report errors to Canonical and its "
649"partners, the makers of the operating system."
578"partners, the makers of the operating system."
650msgstr ""
579msgstr ""
651
580
652−#: qml/Wizard/Pages/60-reporting.qml:43
581+#: qml/Wizard/Pages/60-reporting.qml:53
653msgid ""
582msgid ""
654"This can be disabled in <b>System Settings</b> under <b>Security &amp; "
583"This can be disabled in <b>System Settings</b> under <b>Security &amp; "
655"Privacy</b>"
584"Privacy</b>"
qml/Components/Dialogs.qml+29−2
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2015 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
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
5 * it under the terms of the GNU General Public License as published by
@@ -21,6 +21,7 @@ import Unity.Session 0.1
21import GlobalShortcut 1.0
21import GlobalShortcut 1.0
22import Ubuntu.Components 1.3
22import Ubuntu.Components 1.3
23import Unity.Platform 1.0
23import Unity.Platform 1.0
24+import Utils 0.1
24import "../Greeter"
25import "../Greeter"
25
26
26Item {
27Item {
@@ -45,6 +46,31 @@ Item {
45 d.showPowerDialog();
46 d.showPowerDialog();
46 }
47 }
47
48
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+
48 GlobalShortcut { // reboot/shutdown dialog
74 GlobalShortcut { // reboot/shutdown dialog
49 shortcut: Qt.Key_PowerDown
75 shortcut: Qt.Key_PowerDown
50 active: Platform.isPC
76 active: Platform.isPC
@@ -91,6 +117,8 @@ Item {
91 id: d // private stuff
117 id: d // private stuff
92 objectName: "dialogsPrivate"
118 objectName: "dialogsPrivate"
93
119
120+ property var modeSwitchWarningPopup: null
121+
94 function showPowerDialog() {
122 function showPowerDialog() {
95 if (!dialogLoader.active) {
123 if (!dialogLoader.active) {
96 dialogLoader.sourceComponent = powerDialogComponent;
124 dialogLoader.sourceComponent = powerDialogComponent;
@@ -270,5 +298,4 @@ Item {
270 unitySessionService.endSession();
298 unitySessionService.endSession();
271 }
299 }
272 }
300 }
273−
274}
301}
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 {
23 property string title: ""
23 property string title: ""
24 property bool buttonsVisible: false
24 property bool buttonsVisible: false
25
25
26+ property int panelHeight: 0
27+
26 signal close()
28 signal close()
27 signal minimize()
29 signal minimize()
28 signal maximize()
30 signal maximize()
qml/Components/ShellDialog.qml+1−1
@@ -31,7 +31,7 @@ Dialog {
31 // NB: PopupBase, Dialog's superclass, will check for the existence of this property
31 // NB: PopupBase, Dialog's superclass, will check for the existence of this property
32 property bool reparentToRootItem: false
32 property bool reparentToRootItem: false
33
33
34− onVisibleChanged: { if (!visible) { dialogLoader.active = false; } }
34+ onVisibleChanged: { if (!visible && dialogLoader) { dialogLoader.active = false; } }
35
35
36 Keys.onEscapePressed: hide()
36 Keys.onEscapePressed: hide()
37
37
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 {
78 /*!
78 /*!
79 type:real \brief Width to be enforced on the card in this configuration.
79 type:real \brief Width to be enforced on the card in this configuration.
80
80
81− If undefined, should use implicit width of the actual card.
81+ If -1, should use implicit width of the actual card.
82 */
82 */
83− property var cardWidth: {
83+ property real cardWidth: {
84 switch (categoryLayout) {
84 switch (categoryLayout) {
85 case "grid":
85 case "grid":
86 case "vertical-journal":
86 case "vertical-journal":
@@ -106,16 +106,16 @@ Item {
106 case "organic-grid":
106 case "organic-grid":
107 case "journal":
107 case "journal":
108 default:
108 default:
109− return undefined;
109+ return -1;
110 }
110 }
111 }
111 }
112
112
113 /*!
113 /*!
114 type:real \brief Height to be enforced on the card in this configuration.
114 type:real \brief Height to be enforced on the card in this configuration.
115
115
116− If undefined, should use implicit height of the actual card.
116+ If -1, should use implicit height of the actual card.
117 */
117 */
118− readonly property var cardHeight: {
118+ readonly property real cardHeight: {
119 switch (categoryLayout) {
119 switch (categoryLayout) {
120 case "journal":
120 case "journal":
121 if (template["card-size"] >= 12 && template["card-size"] <= 38) return units.gu(template["card-size"]);
121 if (template["card-size"] >= 12 && template["card-size"] <= 38) return units.gu(template["card-size"]);
@@ -129,7 +129,7 @@ Item {
129 case "organic-grid":
129 case "organic-grid":
130 case "vertical-journal":
130 case "vertical-journal":
131 default:
131 default:
132− return undefined;
132+ return -1;
133 }
133 }
134 }
134 }
135
135
@@ -221,8 +221,8 @@ Item {
221 item.objectName = "cardToolCard";
221 item.objectName = "cardToolCard";
222 item.asynchronous = false;
222 item.asynchronous = false;
223 item.components = Qt.binding(function() { return cardTool.components; });
223 item.components = Qt.binding(function() { return cardTool.components; });
224− item.width = Qt.binding(function() { return cardTool.cardWidth || item.implicitWidth; });
224+ item.width = Qt.binding(function() { return cardTool.cardWidth !== -1 ? cardTool.cardWidth : item.implicitWidth; });
225− item.height = Qt.binding(function() { return cardTool.cardHeight || item.implicitHeight; });
225+ item.height = Qt.binding(function() { return cardTool.cardHeight !== -1 ? cardTool.cardHeight : item.implicitHeight; });
226 }
226 }
227 Connections {
227 Connections {
228 target: cardTool
228 target: cardTool
qml/Dash/graphics/phone/screenshots/libreoffice@12.pngnew file

Binary file not shown.

qml/OrientedShell.qml+16−7
@@ -16,13 +16,12 @@
16
16
17import QtQuick 2.4
17import QtQuick 2.4
18import QtQuick.Window 2.2
18import QtQuick.Window 2.2
19+import Unity.InputInfo 0.1
19import Unity.Session 0.1
20import Unity.Session 0.1
20import GSettings 1.0
21import GSettings 1.0
21import "Components"
22import "Components"
22−import "Components/UnityInputInfo"
23import "Rotation"
23import "Rotation"
24−// Backup Workaround https://bugs.launchpad.net/ubuntu/+source/unity8/+bug/1473471
24+// Workaround https://bugs.launchpad.net/ubuntu/+source/unity8/+bug/1473471
25−// in case we remove the UnityInputInfo import
26import Ubuntu.Components 1.3
25import Ubuntu.Components 1.3
27
26
28Rectangle {
27Rectangle {
@@ -64,6 +63,16 @@ Rectangle {
64 property bool orientationLocked: OrientationLock.enabled
63 property bool orientationLocked: OrientationLock.enabled
65 property var orientationLock: OrientationLock
64 property var orientationLock: OrientationLock
66
65
66+ InputDeviceModel {
67+ id: miceModel
68+ deviceFilter: InputInfo.Mouse
69+ }
70+
71+ InputDeviceModel {
72+ id: keyboardsModel
73+ deviceFilter: InputInfo.Keyboard
74+ }
75+
67 property int orientation
76 property int orientation
68 onPhysicalOrientationChanged: {
77 onPhysicalOrientationChanged: {
69 if (!orientationLocked) {
78 if (!orientationLocked) {
@@ -83,7 +92,7 @@ Rectangle {
83 }
92 }
84 // We need to manually update this on startup as the binding
93 // We need to manually update this on startup as the binding
85 // below doesn't seem to have any effect at that stage
94 // below doesn't seem to have any effect at that stage
86− oskSettings.stayHidden = UnityInputInfo.keyboards > 0
95+ oskSettings.stayHidden = keyboardsModel.count > 0
87 oskSettings.disableHeight = shell.usageScenario == "desktop"
96 oskSettings.disableHeight = shell.usageScenario == "desktop"
88 }
97 }
89
98
@@ -96,7 +105,7 @@ Rectangle {
96 Binding {
105 Binding {
97 target: oskSettings
106 target: oskSettings
98 property: "stayHidden"
107 property: "stayHidden"
99− value: UnityInputInfo.keyboards > 0
108+ value: keyboardsModel.count > 0
100 }
109 }
101
110
102 Binding {
111 Binding {
@@ -174,7 +183,7 @@ Rectangle {
174 nativeWidth: root.width
183 nativeWidth: root.width
175 nativeHeight: root.height
184 nativeHeight: root.height
176 mode: applicationArguments.mode
185 mode: applicationArguments.mode
177− hasMouse: UnityInputInfo.mice > deviceConfiguration.ignoredMice
186+ hasMouse: miceModel.count > deviceConfiguration.ignoredMice
178
187
179 // TODO: Factor in the connected input devices (eg: physical keyboard, mouse, touchscreen),
188 // TODO: Factor in the connected input devices (eg: physical keyboard, mouse, touchscreen),
180 // what's the output device (eg: big TV, desktop monitor, phone display), etc.
189 // what's the output device (eg: big TV, desktop monitor, phone display), etc.
@@ -188,7 +197,7 @@ Rectangle {
188 return "tablet";
197 return "tablet";
189 }
198 }
190 } else { // automatic
199 } else { // automatic
191− if (UnityInputInfo.mice > deviceConfiguration.ignoredMice) {
200+ if (miceModel.count > deviceConfiguration.ignoredMice) {
192 return "desktop";
201 return "desktop";
193 } else {
202 } else {
194 return deviceConfiguration.category;
203 return deviceConfiguration.category;
qml/Panel/Panel.qml+24−1
@@ -32,6 +32,12 @@ Item {
32
32
33 opacity: fullscreenMode && indicators.fullyClosed ? 0.0 : 1.0
33 opacity: fullscreenMode && indicators.fullyClosed ? 0.0 : 1.0
34
34
35+ Binding {
36+ target: PanelState
37+ property: "panelHeight"
38+ value: root.panelHeight
39+ }
40+
35 Rectangle {
41 Rectangle {
36 id: darkenedArea
42 id: darkenedArea
37 property real darkenedOpacity: 0.6
43 property real darkenedOpacity: 0.6
@@ -137,7 +143,7 @@ Item {
137 }
143 }
138
144
139 shown: false
145 shown: false
140− width: root.width - (windowControlButtons.visible ? windowControlButtons.width : 0)
146+ width: root.width - (windowControlButtons.visible ? windowControlButtons.width + titleLabel.width : 0)
141 minimizedPanelHeight: units.gu(3)
147 minimizedPanelHeight: units.gu(3)
142 expandedPanelHeight: units.gu(7)
148 expandedPanelHeight: units.gu(7)
143 openedHeight: root.height - indicatorOrangeLine.height
149 openedHeight: root.height - indicatorOrangeLine.height
@@ -178,6 +184,23 @@ Item {
178 onMaximize: PanelState.maximize()
184 onMaximize: PanelState.maximize()
179 }
185 }
180
186
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+
181 PanelSeparatorLine {
204 PanelSeparatorLine {
182 id: indicatorOrangeLine
205 id: indicatorOrangeLine
183 anchors {
206 anchors {
qml/Stages/DecoratedWindow.qml+3−1
@@ -26,6 +26,7 @@ FocusScope {
26 property alias window: applicationWindow
26 property alias window: applicationWindow
27 property alias application: applicationWindow.application
27 property alias application: applicationWindow.application
28 property alias active: decoration.active
28 property alias active: decoration.active
29+ property alias title: decoration.title
29
30
30 property bool decorationShown: true
31 property bool decorationShown: true
31 property bool highlightShown: false
32 property bool highlightShown: false
@@ -66,7 +67,8 @@ FocusScope {
66 objectName: application ? "appWindowDecoration_" + application.appId : "appWindowDecoration_null"
67 objectName: application ? "appWindowDecoration_" + application.appId : "appWindowDecoration_null"
67 anchors { left: parent.left; top: parent.top; right: parent.right }
68 anchors { left: parent.left; top: parent.top; right: parent.right }
68 height: units.gu(3)
69 height: units.gu(3)
70+ width: root.width
69− title: window.title !== "" ? window.title : model.name
71+ title: window.title !== "" ? window.title : ""
70 onClose: root.close();
72 onClose: root.close();
71 onMaximize: root.maximize();
73 onMaximize: root.maximize();
72 onMinimize: root.minimize();
74 onMinimize: root.minimize();
qml/Stages/DesktopSpread.qml+1−1
@@ -87,7 +87,7 @@ FocusScope {
87 function focusSelected() {
87 function focusSelected() {
88 if (spreadRepeater.highlightedIndex != -1) {
88 if (spreadRepeater.highlightedIndex != -1) {
89 var application = ApplicationManager.get(spreadRepeater.highlightedIndex);
89 var application = ApplicationManager.get(spreadRepeater.highlightedIndex);
90− ApplicationManager.focusApplication(application.appId);
90+ ApplicationManager.requestFocusApplication(application.appId);
91 }
91 }
92 }
92 }
93
93
qml/Stages/DesktopSpreadDelegate.qml+2−2
@@ -29,8 +29,8 @@ Item {
29 property bool highlightShown: false
29 property bool highlightShown: false
30 property real shadowOpacity: 1
30 property real shadowOpacity: 1
31
31
32− property int windowWidth: application.session && application.session.surface ? application.session.surface.size.width : 0
32+ property int windowWidth: application && 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
33+ property int windowHeight: application && application.session && application.session.surface ? application.session.surface.size.height : 0
34
34
35 state: "normal"
35 state: "normal"
36 states: [
36 states: [
qml/Stages/DesktopStage.qml+165−39
@@ -20,11 +20,11 @@ import QtQuick 2.4
20import QtQuick.Layouts 1.1
20import QtQuick.Layouts 1.1
21import Ubuntu.Components 1.3
21import Ubuntu.Components 1.3
22import Unity.Application 0.1
22import Unity.Application 0.1
23−import "../Components"
24import "../Components/PanelState"
23import "../Components/PanelState"
25import "../Components"
24import "../Components"
26import Utils 0.1
25import Utils 0.1
27import Ubuntu.Gestures 0.1
26import Ubuntu.Gestures 0.1
27+import GlobalShortcut 1.0
28
28
29AbstractStage {
29AbstractStage {
30 id: root
30 id: root
@@ -45,21 +45,72 @@ AbstractStage {
45 spread.state = "";
45 spread.state = "";
46 }
46 }
47
47
48− ApplicationManager.requestFocusApplication(appId)
48+ ApplicationManager.focusApplication(appId);
49+ }
50+
51+ onApplicationRemoved: {
52+ priv.focusNext();
49 }
53 }
50
54
51 onFocusRequested: {
55 onFocusRequested: {
52 var appIndex = priv.indexOf(appId);
56 var appIndex = priv.indexOf(appId);
53 var appDelegate = appRepeater.itemAt(appIndex);
57 var appDelegate = appRepeater.itemAt(appIndex);
54− appDelegate.minimized = false;
58+ appDelegate.restoreFromMinimized();
55− ApplicationManager.focusApplication(appId)
56
59
57 if (spread.state == "altTab") {
60 if (spread.state == "altTab") {
58− spread.cancel()
61+ spread.cancel();
59 }
62 }
60 }
63 }
61 }
64 }
62
65
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+
63 QtObject {
114 QtObject {
64 id: priv
115 id: priv
65
116
@@ -93,6 +144,28 @@ AbstractStage {
93 }
144 }
94 return -1;
145 return -1;
95 }
146 }
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+ }
96 }
169 }
97
170
98 Connections {
171 Connections {
@@ -100,15 +173,23 @@ AbstractStage {
100 onClose: {
173 onClose: {
101 ApplicationManager.stopApplication(ApplicationManager.focusedApplicationId)
174 ApplicationManager.stopApplication(ApplicationManager.focusedApplicationId)
102 }
175 }
103− onMinimize: appRepeater.itemAt(0).minimize();
176+ onMinimize: appRepeater.itemAt(0).minimize(true);
104− onMaximize: appRepeater.itemAt(0).unmaximize();
177+ onMaximize: appRepeater.itemAt(0).restore();
105 }
178 }
106
179
107 Binding {
180 Binding {
108 target: PanelState
181 target: PanelState
109 property: "buttonsVisible"
182 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
111 }
191 }
192+
112 Component.onDestruction: PanelState.buttonsVisible = false;
193 Component.onDestruction: PanelState.buttonsVisible = false;
113
194
114 FocusScope {
195 FocusScope {
@@ -135,23 +216,34 @@ AbstractStage {
135
216
136 delegate: FocusScope {
217 delegate: FocusScope {
137 id: appDelegate
218 id: appDelegate
138− objectName: "stageDelegate_" + model.appId
219+ objectName: "appDelegate_" + appId
139 z: ApplicationManager.count - index
220 z: ApplicationManager.count - index
140 y: units.gu(3)
221 y: units.gu(3)
141 width: units.gu(60)
222 width: units.gu(60)
142 height: units.gu(50)
223 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
144
237
145− property bool maximized: false
238+ readonly property string appId: model.appId
146− property bool minimized: false
147 property bool animationsEnabled: true
239 property bool animationsEnabled: true
148−
240+ property alias title: decoratedWindow.title
149 property bool visuallyMaximized: false
241 property bool visuallyMaximized: false
150 property bool visuallyMinimized: false
242 property bool visuallyMinimized: false
151
243
152 onFocusChanged: {
244 onFocusChanged: {
153− if (focus && ApplicationManager.focusedApplicationId !== model.appId) {
245+ if (focus && ApplicationManager.focusedApplicationId !== appId) {
154− ApplicationManager.focusApplication(model.appId);
246+ ApplicationManager.focusApplication(appId);
155 }
247 }
156 }
248 }
157
249
@@ -176,24 +268,52 @@ AbstractStage {
176
268
177 function maximize(animated) {
269 function maximize(animated) {
178 animationsEnabled = (animated === undefined) || animated;
270 animationsEnabled = (animated === undefined) || animated;
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() {
179− minimized = false;
283+ appDelegatePrivate.minimized = false;
180− maximized = true;
284+ appDelegatePrivate.maximized = false;
285+ appDelegatePrivate.maximizedLeft = false;
286+ appDelegatePrivate.maximizedRight = true;
181 }
287 }
182 function minimize(animated) {
288 function minimize(animated) {
183 animationsEnabled = (animated === undefined) || animated;
289 animationsEnabled = (animated === undefined) || animated;
184− maximized = false;
290+ appDelegatePrivate.maximized = false;
185− minimized = true;
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;
186 }
299 }
187− function unmaximize(animated) {
300+ function restoreFromMinimized(animated) {
188 animationsEnabled = (animated === undefined) || animated;
301 animationsEnabled = (animated === undefined) || animated;
189− minimized = false;
302+ appDelegatePrivate.minimized = false;
190− maximized = false;
303+ if (maximized)
304+ maximize();
305+ else if (maximizedLeft)
306+ maximizeLeft();
307+ else if (maximizedRight)
308+ maximizeRight();
309+ ApplicationManager.focusApplication(appId);
191 }
310 }
192
311
193 states: [
312 states: [
194 State {
313 State {
195 name: "normal";
314 name: "normal";
196 when: !appDelegate.maximized && !appDelegate.minimized
315 when: !appDelegate.maximized && !appDelegate.minimized
316+ && !appDelegate.maximizedLeft && !appDelegate.maximizedRight
197 PropertyChanges {
317 PropertyChanges {
198 target: appDelegate;
318 target: appDelegate;
199 visuallyMinimized: false;
319 visuallyMinimized: false;
@@ -210,6 +330,14 @@ AbstractStage {
210 visuallyMaximized: true
330 visuallyMaximized: true
211 }
331 }
212 },
332 },
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+ },
213 State {
341 State {
214 name: "minimized"; when: appDelegate.minimized
342 name: "minimized"; when: appDelegate.minimized
215 PropertyChanges {
343 PropertyChanges {
@@ -227,14 +355,14 @@ AbstractStage {
227 to: "normal"
355 to: "normal"
228 enabled: appDelegate.animationsEnabled
356 enabled: appDelegate.animationsEnabled
229 PropertyAction { target: appDelegate; properties: "visuallyMinimized,visuallyMaximized" }
357 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 }
231 },
359 },
232 Transition {
360 Transition {
233 to: "maximized"
361 to: "maximized"
234 enabled: appDelegate.animationsEnabled
362 enabled: appDelegate.animationsEnabled
235 PropertyAction { target: appDelegate; property: "visuallyMinimized" }
363 PropertyAction { target: appDelegate; property: "visuallyMinimized" }
236 SequentialAnimation {
364 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 }
238 PropertyAction { target: appDelegate; property: "visuallyMaximized" }
366 PropertyAction { target: appDelegate; property: "visuallyMaximized" }
239 }
367 }
240 },
368 },
@@ -243,20 +371,15 @@ AbstractStage {
243 enabled: appDelegate.animationsEnabled
371 enabled: appDelegate.animationsEnabled
244 PropertyAction { target: appDelegate; property: "visuallyMaximized" }
372 PropertyAction { target: appDelegate; property: "visuallyMaximized" }
245 SequentialAnimation {
373 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 }
247 PropertyAction { target: appDelegate; property: "visuallyMinimized" }
375 PropertyAction { target: appDelegate; property: "visuallyMinimized" }
248− }
376+ ScriptAction {
249− },
377+ script: {
250− Transition {
378+ if (appDelegate.animationsEnabled && state === "minimized" ) {
251− from: ""
379+ priv.focusNext();
252− to: "altTab"
380+ }
253− PropertyAction { target: appDelegate; properties: "y,angle,z,itemScale,itemScaleOriginY" }
381+ }
254− PropertyAction { target: decoratedWindow; properties: "anchors.topMargin" }
382+ }
255− PropertyAnimation {
256− target: appDelegate; properties: "x"
257− from: root.width
258− duration: rightEdgePushArea.containsMouse ? UbuntuAnimation.FastDuration :0
259− easing: UbuntuAnimation.StandardEasing
260 }
383 }
261 }
384 }
262 ]
385 ]
@@ -276,6 +399,8 @@ AbstractStage {
276 minHeight: units.gu(10)
399 minHeight: units.gu(10)
277 borderThickness: units.gu(2)
400 borderThickness: units.gu(2)
278 windowId: model.appId // FIXME: Change this to point to windowId once we have such a thing
401 windowId: model.appId // FIXME: Change this to point to windowId once we have such a thing
402+ screenWidth: root.width
403+ screenHeight: root.height
279
404
280 onPressed: { ApplicationManager.focusApplication(model.appId) }
405 onPressed: { ApplicationManager.focusApplication(model.appId) }
281 }
406 }
@@ -292,8 +417,9 @@ AbstractStage {
292 focus: true
417 focus: true
293
418
294 onClose: ApplicationManager.stopApplication(model.appId)
419 onClose: ApplicationManager.stopApplication(model.appId)
420+ onMaximize: appDelegate.maximized || appDelegate.maximizedLeft || appDelegate.maximizedRight
295− onMaximize: appDelegate.maximized ? appDelegate.unmaximize() : appDelegate.maximize()
421+ ? appDelegate.restore() : appDelegate.maximize()
296− onMinimize: appDelegate.minimize()
422+ onMinimize: appDelegate.minimize(true)
297 onDecorationPressed: { ApplicationManager.focusApplication(model.appId) }
423 onDecorationPressed: { ApplicationManager.focusApplication(model.appId) }
298 }
424 }
299 }
425 }
qml/Stages/WindowDecoration.qml+8−3
@@ -18,6 +18,7 @@ import QtQuick 2.4
18import Unity.Application 0.1 // For Mir singleton
18import Unity.Application 0.1 // For Mir singleton
19import Ubuntu.Components 1.3
19import Ubuntu.Components 1.3
20import "../Components"
20import "../Components"
21+import "../Components/PanelState"
21
22
22MouseArea {
23MouseArea {
23 id: root
24 id: root
@@ -45,17 +46,18 @@ MouseArea {
45 priv.distanceX = pos.x;
46 priv.distanceX = pos.x;
46 priv.distanceY = pos.y;
47 priv.distanceY = pos.y;
47 priv.dragging = true;
48 priv.dragging = true;
48− Mir.cursorName = "grabbing";
49 } else {
49 } else {
50 priv.dragging = false;
50 priv.dragging = false;
51 Mir.cursorName = "";
51 Mir.cursorName = "";
52 }
52 }
53 }
53 }
54+
54 onPositionChanged: {
55 onPositionChanged: {
55 if (priv.dragging) {
56 if (priv.dragging) {
57+ Mir.cursorName = "grabbing";
56 var pos = mapToItem(root.target.parent, mouseX, mouseY);
58 var pos = mapToItem(root.target.parent, mouseX, mouseY);
57 root.target.x = pos.x - priv.distanceX;
59 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);
59 }
61 }
60 }
62 }
61
63
@@ -70,11 +72,12 @@ MouseArea {
70 }
72 }
71
73
72 Row {
74 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) }
74 spacing: units.gu(1)
76 spacing: units.gu(1)
75 opacity: root.active ? 1 : 0.5
77 opacity: root.active ? 1 : 0.5
76
78
77 WindowControlButtons {
79 WindowControlButtons {
80+ id: buttons
78 height: parent.height
81 height: parent.height
79 onClose: root.close();
82 onClose: root.close();
80 onMinimize: root.minimize();
83 onMinimize: root.minimize();
@@ -86,9 +89,11 @@ MouseArea {
86 objectName: "windowDecorationTitle"
89 objectName: "windowDecorationTitle"
87 color: "#DFDBD2"
90 color: "#DFDBD2"
88 height: parent.height
91 height: parent.height
92+ width: parent.width - buttons.width - parent.anchors.rightMargin - parent.anchors.leftMargin
89 verticalAlignment: Text.AlignVCenter
93 verticalAlignment: Text.AlignVCenter
90 fontSize: "small"
94 fontSize: "small"
91 font.bold: true
95 font.bold: true
96+ elide: Text.ElideRight
92 }
97 }
93 }
98 }
94}
99}
qml/Stages/WindowResizeArea.qml+7−4
@@ -18,6 +18,7 @@ import QtQuick 2.4
18import Ubuntu.Components 1.3
18import Ubuntu.Components 1.3
19import Utils 0.1
19import Utils 0.1
20import Unity.Application 0.1 // for Mir.cursorName
20import Unity.Application 0.1 // for Mir.cursorName
21+import "../Components/PanelState"
21
22
22MouseArea {
23MouseArea {
23 id: root
24 id: root
@@ -35,6 +36,8 @@ MouseArea {
35 property int borderThickness: 0
36 property int borderThickness: 0
36 property int minWidth: 0
37 property int minWidth: 0
37 property int minHeight: 0
38 property int minHeight: 0
39+ property int screenWidth: 0
40+ property int screenHeight: 0
38
41
39 QtObject {
42 QtObject {
40 id: priv
43 id: priv
@@ -66,10 +69,10 @@ MouseArea {
66 Component.onCompleted: {
69 Component.onCompleted: {
67 var windowGeometry = windowStateStorage.getGeometry(root.windowId, Qt.rect(target.x, target.y, target.width, target.height))
70 var windowGeometry = windowStateStorage.getGeometry(root.windowId, Qt.rect(target.x, target.y, target.width, target.height))
68 if (windowGeometry !== undefined) {
71 if (windowGeometry !== undefined) {
69− target.x = windowGeometry.x
72+ target.width = Math.min(windowGeometry.width, root.screenWidth)
70− target.y = windowGeometry.y
73+ target.height = Math.min(windowGeometry.height, root.screenHeight - PanelState.panelHeight)
71− target.width = windowGeometry.width
74+ target.x = Math.max(Math.min(windowGeometry.x, root.screenWidth - target.width), 0)
72− target.height = windowGeometry.height
75+ target.y = Math.max(Math.min(windowGeometry.y, root.screenHeight - target.height), PanelState.panelHeight)
73 }
76 }
74 var windowState = windowStateStorage.getState(root.windowId, WindowStateStorage.WindowStateNormal)
77 var windowState = windowStateStorage.getState(root.windowId, WindowStateStorage.WindowStateNormal)
75 if (windowState === WindowStateStorage.WindowStateMaximized) {
78 if (windowState === WindowStateStorage.WindowStateMaximized) {
src/CachingNetworkManagerFactory.cpp+1
@@ -30,6 +30,7 @@ CachingNetworkAccessManager::CachingNetworkAccessManager(QObject *parent)
30QNetworkReply* CachingNetworkAccessManager::createRequest(Operation op, const QNetworkRequest &request, QIODevice *outgoingData)
30QNetworkReply* CachingNetworkAccessManager::createRequest(Operation op, const QNetworkRequest &request, QIODevice *outgoingData)
31{
31{
32 if (m_networkingStatus->status() != ubuntu::connectivity::NetworkingStatus::Status::Online) {
32 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.";
33 QNetworkRequest req(request);
34 QNetworkRequest req(request);
34 req.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysCache);
35 req.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysCache);
35 return QNetworkAccessManager::createRequest(op, req, outgoingData);
36 return QNetworkAccessManager::createRequest(op, req, outgoingData);
tests/mocks/Unity/Application/ApplicationManager.cpp+1
@@ -465,6 +465,7 @@ void ApplicationManager::buildListOfAvailableApplications()
465 application->setAppId("libreoffice");
465 application->setAppId("libreoffice");
466 application->setName("LibreOffice");
466 application->setName("LibreOffice");
467 application->setIconId("libreoffice");
467 application->setIconId("libreoffice");
468+ application->setScreenshotId("libreoffice");
468 application->setIsTouchApp(false);
469 application->setIsTouchApp(false);
469 m_availableApplications.append(application);
470 m_availableApplications.append(application);
470}
471}
tests/mocks/Unity/CMakeLists.txt+1−1
@@ -7,7 +7,7 @@ add_subdirectory(Notifications)
7add_subdirectory(DashCommunicator)
7add_subdirectory(DashCommunicator)
8
8
9pkg_search_module(GOBJECT gobject-2.0 REQUIRED)
9pkg_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)
11
11
12include_directories(
12include_directories(
13 ${CMAKE_CURRENT_BINARY_DIR}
13 ${CMAKE_CURRENT_BINARY_DIR}
tests/mocks/Unity/InputInfo/CMakeLists.txt+7−2
@@ -9,12 +9,17 @@ find_package(Qt5Quick REQUIRED)
9include_directories(
9include_directories(
10 ${CMAKE_CURRENT_SOURCE_DIR}
10 ${CMAKE_CURRENT_SOURCE_DIR}
11 ${CMAKE_CURRENT_BINARY_DIR}
11 ${CMAKE_CURRENT_BINARY_DIR}
12+ ${CMAKE_SOURCE_DIR}/plugins/Unity/InputInfo/
12)
13)
13
14
15+add_definitions(-DUNITY_MOCKS)
16+
14set(MockInputInfo_SOURCES
17set(MockInputInfo_SOURCES
15 plugin.cpp
18 plugin.cpp
16− mockqinputinfo.cpp
19+ qinputdeviceinfo_mock.cpp
17− mockqdeclarativeinputdeviceinfo.cpp
20+ mockcontroller.cpp
21+ ${CMAKE_SOURCE_DIR}/plugins/Unity/InputInfo/qinputinfo.cpp
22+ ${CMAKE_SOURCE_DIR}/plugins/Unity/InputInfo/qdeclarativeinputdevicemodel.cpp
18)
23)
19
24
20add_library(MockInputInfo SHARED
25add_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 @@
15 */
15 */
16
16
17// Qt
17// Qt
18−#include <QtQml/qqml.h>
18+#include <QtQml>
19
19
20// self
20// self
21#include "plugin.h"
21#include "plugin.h"
22
22
23// local
23// 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+}
25
33
26void InputInfoPlugin::registerTypes(const char *uri)
34void InputInfoPlugin::registerTypes(const char *uri)
27{
35{
28 int major = 0;
36 int major = 0;
29 int minor = 1;
37 int minor = 1;
30− qmlRegisterType<QDeclarativeInputDeviceInfo>(uri, major, minor, "InputDeviceInfo");
38+ qmlRegisterType<QDeclarativeInputDeviceModel>(uri, major, minor, "InputDeviceModel");
31 qmlRegisterType<QInputDevice>(uri, major, minor, "InputInfo");
39 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);
32}
46}
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
75 item = new MockLauncherItem("calendar-app", "/usr/share/applications/calendar-app.desktop","Calendar", "calendar", this);
75 item = new MockLauncherItem("calendar-app", "/usr/share/applications/calendar-app.desktop","Calendar", "calendar", this);
76 item->setPinned(true);
76 item->setPinned(true);
77 m_list.append(item);
77 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);
78}
81}
79
82
80MockLauncherModel::~MockLauncherModel()
83MockLauncherModel::~MockLauncherModel()
tests/mocks/Unity/fake_categories.cpp−5
@@ -32,11 +32,6 @@ int Categories::rowCount(const QModelIndex& /*parent*/) const
32 return m_category_count;
32 return m_category_count;
33}
33}
34
34
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−
40bool Categories::overrideCategoryJson(QString const& /* categoryId */, QString const& /* json */)
35bool Categories::overrideCategoryJson(QString const& /* categoryId */, QString const& /* json */)
41{
36{
42 qFatal("Using un-implemented Categories::overrideCategoryJson");
37 qFatal("Using un-implemented Categories::overrideCategoryJson");
tests/mocks/Unity/fake_categories.h−1
@@ -34,7 +34,6 @@ public:
34 QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
34 QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
35 int rowCount(const QModelIndex& parent = QModelIndex()) const override;
35 int rowCount(const QModelIndex& parent = QModelIndex()) const override;
36
36
37− Q_INVOKABLE void addSpecialCategory(QString const& categoryId, QString const& name, QString const& icon, QString const& rawTemplate, QObject* countObject) override;
38 Q_INVOKABLE bool overrideCategoryJson(QString const& categoryId, QString const& json) override;
37 Q_INVOKABLE bool overrideCategoryJson(QString const& categoryId, QString const& json) override;
39
38
40 Q_INVOKABLE QVariant data(int row, int role) const;
39 Q_INVOKABLE QVariant data(int row, int role) const;
tests/mocks/Unity/fake_scope.cpp+5
@@ -271,6 +271,11 @@ void Scope::refresh()
271 Q_EMIT refreshed();
271 Q_EMIT refreshed();
272}
272}
273
273
274+void Scope::activateAction(QVariant const& /*result*/, QString const& /*categoryId*/, QString const& /*actionId*/)
275+{
276+ qFatal("Using Scope::activateAction");
277+}
278+
274unity::shell::scopes::NavigationInterface* Scope::getNavigation(const QString& id)
279unity::shell::scopes::NavigationInterface* Scope::getNavigation(const QString& id)
275{
280{
276 if (id.isEmpty())
281 if (id.isEmpty())
tests/mocks/Unity/fake_scope.h+2
@@ -79,6 +79,8 @@ public:
79
79
80 Q_INVOKABLE void refresh() override;
80 Q_INVOKABLE void refresh() override;
81
81
82+ Q_INVOKABLE virtual void activateAction(QVariant const& result, QString const& categoryId, QString const& actionId) override;
83+
82Q_SIGNALS:
84Q_SIGNALS:
83 // These are not in the Interface, here for testing benefits
85 // These are not in the Interface, here for testing benefits
84 void refreshed();
86 void refreshed();
tests/mocks/Unity/fake_scopesoverview.cpp−10
@@ -74,11 +74,6 @@ int ScopesOverviewCategories::rowCount(const QModelIndex& /*parent*/) const
74 return 2;
74 return 2;
75}
75}
76
76
77−void ScopesOverviewCategories::addSpecialCategory(QString const&, QString const&, QString const&, QString const&, QObject*)
78−{
79− qFatal("Using un-implemented ScopesOverviewCategories::addSpecialCategory");
80−}
81−
82bool ScopesOverviewCategories::overrideCategoryJson(QString const& /* categoryId */, QString const& /* json */)
77bool ScopesOverviewCategories::overrideCategoryJson(QString const& /* categoryId */, QString const& /* json */)
83{
78{
84 qFatal("Using un-implemented ScopesOverviewCategories::overrideCategoryJson");
79 qFatal("Using un-implemented ScopesOverviewCategories::overrideCategoryJson");
@@ -175,11 +170,6 @@ int ScopesOverviewSearchCategories::rowCount(const QModelIndex& /*parent*/) cons
175 return 2;
170 return 2;
176}
171}
177
172
178−void ScopesOverviewSearchCategories::addSpecialCategory(QString const&, QString const&, QString const&, QString const&, QObject*)
179−{
180− qFatal("Using un-implemented ScopesOverviewSearchCategories::addSpecialCategory");
181−}
182−
183bool ScopesOverviewSearchCategories::overrideCategoryJson(QString const& /* categoryId */, QString const& /* json */)
173bool ScopesOverviewSearchCategories::overrideCategoryJson(QString const& /* categoryId */, QString const& /* json */)
184{
174{
185 qFatal("Using un-implemented ScopesOverviewSearchCategories::overrideCategoryJson");
175 qFatal("Using un-implemented ScopesOverviewSearchCategories::overrideCategoryJson");
tests/mocks/Unity/fake_scopesoverview.h−2
@@ -53,7 +53,6 @@ public:
53 QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
53 QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
54 int rowCount(const QModelIndex& parent = QModelIndex()) const override;
54 int rowCount(const QModelIndex& parent = QModelIndex()) const override;
55
55
56− Q_INVOKABLE void addSpecialCategory(QString const& categoryId, QString const& name, QString const& icon, QString const& rawTemplate, QObject* countObject) override;
57 Q_INVOKABLE bool overrideCategoryJson(QString const& categoryId, QString const& json) override;
56 Q_INVOKABLE bool overrideCategoryJson(QString const& categoryId, QString const& json) override;
58
57
59 // This is implementation detail
58 // This is implementation detail
@@ -76,7 +75,6 @@ public:
76 QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
75 QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
77 int rowCount(const QModelIndex& parent = QModelIndex()) const override;
76 int rowCount(const QModelIndex& parent = QModelIndex()) const override;
78
77
79− Q_INVOKABLE void addSpecialCategory(QString const& categoryId, QString const& name, QString const& icon, QString const& rawTemplate, QObject* countObject) override;
80 Q_INVOKABLE bool overrideCategoryJson(QString const& categoryId, QString const& json) override;
78 Q_INVOKABLE bool overrideCategoryJson(QString const& categoryId, QString const& json) override;
81
79
82private:
80private:
tests/mocks/Utils/CMakeLists.txt+2
@@ -18,6 +18,8 @@ set(QMLPLUGIN_SRC
18 ${CMAKE_SOURCE_DIR}/plugins/Utils/windowscreenshotprovider.cpp
18 ${CMAKE_SOURCE_DIR}/plugins/Utils/windowscreenshotprovider.cpp
19 ${CMAKE_SOURCE_DIR}/plugins/Utils/easingcurve.cpp
19 ${CMAKE_SOURCE_DIR}/plugins/Utils/easingcurve.cpp
20 ${CMAKE_SOURCE_DIR}/plugins/Utils/inputwatcher.cpp
20 ${CMAKE_SOURCE_DIR}/plugins/Utils/inputwatcher.cpp
21+ ${CMAKE_SOURCE_DIR}/plugins/Utils/applicationsfiltermodel.cpp
22+ ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h
21 constants.cpp
23 constants.cpp
22 plugin.cpp
24 plugin.cpp
23 windowstatestorage.cpp
25 windowstatestorage.cpp
tests/mocks/Utils/plugin.cpp+2
@@ -35,6 +35,7 @@
35#include <windowinputfilter.h>
35#include <windowinputfilter.h>
36#include <windowscreenshotprovider.h>
36#include <windowscreenshotprovider.h>
37#include <easingcurve.h>
37#include <easingcurve.h>
38+#include <applicationsfiltermodel.h>
38
39
39static QObject *createWindowStateStorage(QQmlEngine *engine, QJSEngine *scriptEngine)
40static QObject *createWindowStateStorage(QQmlEngine *engine, QJSEngine *scriptEngine)
40{
41{
@@ -63,6 +64,7 @@ void FakeUtilsPlugin::registerTypes(const char *uri)
63 qmlRegisterType<InputWatcher>(uri, 0, 1, "InputWatcher");
64 qmlRegisterType<InputWatcher>(uri, 0, 1, "InputWatcher");
64 qmlRegisterSingletonType<Constants>(uri, 0, 1, "Constants", createConstants);
65 qmlRegisterSingletonType<Constants>(uri, 0, 1, "Constants", createConstants);
65 qmlRegisterType<ActiveFocusLogger>(uri, 0, 1, "ActiveFocusLogger");
66 qmlRegisterType<ActiveFocusLogger>(uri, 0, 1, "ActiveFocusLogger");
67+ qmlRegisterType<ApplicationsFilterModel>(uri, 0, 1, "ApplicationsFilterModel");
66}
68}
67
69
68void FakeUtilsPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
70void FakeUtilsPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
tests/plugins/AccountsService/AccountsServer.cpp+11−5
@@ -18,13 +18,14 @@
18 */
18 */
19
19
20#include "AccountsServer.h"
20#include "AccountsServer.h"
21+#include "AccountsUserAdaptor.h"
22+#include "PropertiesAdaptor.h"
21#include "PropertiesServer.h"
23#include "PropertiesServer.h"
22#include <QDBusConnection>
24#include <QDBusConnection>
23#include <QDebug>
25#include <QDebug>
24
26
25−AccountsServer::AccountsServer(PropertiesServer* propServer, QObject *parent)
27+AccountsServer::AccountsServer(QObject *parent)
26 : QObject(parent)
28 : QObject(parent)
27− , m_propServer(propServer)
28{
29{
29}
30}
30
31
@@ -36,16 +37,21 @@ QDBusObjectPath AccountsServer::FindUserByName(const QString &user)
36bool AccountsServer::AddUser(const QString &user)
37bool AccountsServer::AddUser(const QString &user)
37{
38{
38 QString path(QString("/%1").arg(user));
39 QString path(QString("/%1").arg(user));
39− if (QDBusConnection::sessionBus().objectRegisteredAt(path) == m_propServer)
40+ if (QDBusConnection::sessionBus().objectRegisteredAt(path) != nullptr)
40 return true;
41 return true;
42+
43+ auto props = new PropertiesServer(this);
44+ new PropertiesAdaptor(props);
45+ new AccountsUserAdaptor(props);
41− return QDBusConnection::sessionBus().registerObject(path, m_propServer);
46+ return QDBusConnection::sessionBus().registerObject(path, props);
42}
47}
43
48
44bool AccountsServer::RemoveUser(const QString &user)
49bool AccountsServer::RemoveUser(const QString &user)
45{
50{
46 QString path(QString("/%1").arg(user));
51 QString path(QString("/%1").arg(user));
47− if (QDBusConnection::sessionBus().objectRegisteredAt(path) != m_propServer)
52+ if (QDBusConnection::sessionBus().objectRegisteredAt(path) == nullptr)
48 return false;
53 return false;
54+
49 QDBusConnection::sessionBus().unregisterObject(path);
55 QDBusConnection::sessionBus().unregisterObject(path);
50 return true;
56 return true;
51}
57}
tests/plugins/AccountsService/AccountsServer.h+1−6
@@ -25,14 +25,12 @@
25#include <QObject>
25#include <QObject>
26#include <QString>
26#include <QString>
27
27
28−class PropertiesServer;
29−
30class AccountsServer: public QObject, protected QDBusContext
28class AccountsServer: public QObject, protected QDBusContext
31{
29{
32 Q_OBJECT
30 Q_OBJECT
33
31
34public:
32public:
35− explicit AccountsServer(PropertiesServer *propServer, QObject *parent = 0);
33+ explicit AccountsServer(QObject *parent = 0);
36
34
37public Q_SLOTS:
35public Q_SLOTS:
38
36
@@ -41,9 +39,6 @@ public Q_SLOTS:
41 // mock only.
39 // mock only.
42 bool AddUser(const QString &user);
40 bool AddUser(const QString &user);
43 bool RemoveUser(const QString &user);
41 bool RemoveUser(const QString &user);
44−
45−private:
46− PropertiesServer* m_propServer;
47};
42};
48
43
49#endif
44#endif
tests/plugins/AccountsService/PropertiesServer.cpp+9
@@ -23,10 +23,12 @@
23// Qt
23// Qt
24#include <QDebug>
24#include <QDebug>
25#include <QDBusMessage>
25#include <QDBusMessage>
26+#include <QDBusMetaType>
26
27
27PropertiesServer::PropertiesServer(QObject *parent)
28PropertiesServer::PropertiesServer(QObject *parent)
28 : QObject(parent)
29 : QObject(parent)
29{
30{
31+ qDBusRegisterMetaType<QList<QVariantMap>>();
30 Reset();
32 Reset();
31}
33}
32
34
@@ -47,6 +49,12 @@ void PropertiesServer::Set(const QString &interface, const QString &property, co
47 if (m_properties[interface].contains(property)) {
49 if (m_properties[interface].contains(property)) {
48 QVariant& oldValue = m_properties[interface][property];
50 QVariant& oldValue = m_properties[interface][property];
49 if (oldValue != newValue) {
51 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+
50 oldValue = newValue;
58 oldValue = newValue;
51
59
52 // Special case for Background file.
60 // Special case for Background file.
@@ -72,6 +80,7 @@ void PropertiesServer::Reset()
72{
80{
73 m_properties["com.canonical.unity.AccountsService"]["demo-edges"] = false;
81 m_properties["com.canonical.unity.AccountsService"]["demo-edges"] = false;
74 m_properties["com.canonical.unity.AccountsService"]["DemoEdgesCompleted"] = QStringList();
82 m_properties["com.canonical.unity.AccountsService"]["DemoEdgesCompleted"] = QStringList();
83+ m_properties["com.canonical.unity.AccountsService"]["LauncherItems"] = QVariant::fromValue(QList<QVariantMap>());
75 m_properties["com.canonical.unity.AccountsService.Private"]["FailedLogins"] = 0;
84 m_properties["com.canonical.unity.AccountsService.Private"]["FailedLogins"] = 0;
76 m_properties["com.ubuntu.touch.AccountsService.SecurityPrivacy"]["StatsWelcomeScreen"] = true;
85 m_properties["com.ubuntu.touch.AccountsService.SecurityPrivacy"]["StatsWelcomeScreen"] = true;
77 m_properties["com.ubuntu.AccountsService.SecurityPrivacy"]["EnableLauncherWhileLocked"] = true;
86 m_properties["com.ubuntu.AccountsService.SecurityPrivacy"]["EnableLauncherWhileLocked"] = true;
tests/plugins/AccountsService/client.cpp+6−8
@@ -57,8 +57,6 @@ private Q_SLOTS:
57 m_userInterface = new QDBusInterface("org.freedesktop.Accounts",
57 m_userInterface = new QDBusInterface("org.freedesktop.Accounts",
58 QString("/%1").arg(QTest::currentTestFunction()),
58 QString("/%1").arg(QTest::currentTestFunction()),
59 "org.freedesktop.DBus.Properties", QDBusConnection::sessionBus(), this);
59 "org.freedesktop.DBus.Properties", QDBusConnection::sessionBus(), this);
60− QDBusReply<void> resetReply = m_userInterface->call("Reset");
61− QVERIFY(resetReply.isValid());
62
60
63 QVERIFY(QObject::connect(m_userInterface, SIGNAL(PropertiesChanged(QString, QVariantMap, QStringList)),
61 QVERIFY(QObject::connect(m_userInterface, SIGNAL(PropertiesChanged(QString, QVariantMap, QStringList)),
64 this, SIGNAL(propertiesChanged(QString, QVariantMap, QStringList))));
62 this, SIGNAL(propertiesChanged(QString, QVariantMap, QStringList))));
@@ -79,17 +77,17 @@ private Q_SLOTS:
79 {
77 {
80 // Test various invalid calls
78 // Test various invalid calls
81 AccountsServiceDBusAdaptor session;
79 AccountsServiceDBusAdaptor session;
82− QCOMPARE(session.getUserProperty("NOPE", "com.canonical.unity.AccountsService", "demo-edges"), QVariant());
80+ QCOMPARE(session.getUserPropertyAsync("NOPE", "com.canonical.unity.AccountsService", "demo-edges").value(), QVariant());
83− QCOMPARE(session.getUserProperty(QTest::currentTestFunction(), "com.canonical.unity.AccountsService", "NOPE"), QVariant());
81+ QCOMPARE(session.getUserPropertyAsync(QTest::currentTestFunction(), "com.canonical.unity.AccountsService", "NOPE").value(), QVariant());
84 }
82 }
85
83
86 void testGetSetServiceDBusAdaptor()
84 void testGetSetServiceDBusAdaptor()
87 {
85 {
88 AccountsServiceDBusAdaptor session;
86 AccountsServiceDBusAdaptor session;
89− session.setUserProperty(QTest::currentTestFunction(), "com.canonical.unity.AccountsService", "demo-edges", QVariant(true));
87+ session.setUserPropertyAsync(QTest::currentTestFunction(), "com.canonical.unity.AccountsService", "demo-edges", QVariant(true)).waitForFinished();
90− QCOMPARE(session.getUserProperty(QTest::currentTestFunction(), "com.canonical.unity.AccountsService", "demo-edges"), QVariant(true));
88+ QCOMPARE(session.getUserPropertyAsync(QTest::currentTestFunction(), "com.canonical.unity.AccountsService", "demo-edges").value(), QVariant(true));
91− session.setUserProperty(QTest::currentTestFunction(), "com.canonical.unity.AccountsService", "demo-edges", QVariant(false));
89+ session.setUserPropertyAsync(QTest::currentTestFunction(), "com.canonical.unity.AccountsService", "demo-edges", QVariant(false)).waitForFinished();
92− QCOMPARE(session.getUserProperty(QTest::currentTestFunction(), "com.canonical.unity.AccountsService", "demo-edges"), QVariant(false));
90+ QCOMPARE(session.getUserPropertyAsync(QTest::currentTestFunction(), "com.canonical.unity.AccountsService", "demo-edges").value(), QVariant(false));
93 }
91 }
94
92
95 void testGetSetService()
93 void testGetSetService()
tests/plugins/AccountsService/server.cpp+1−7
@@ -19,11 +19,9 @@
19
19
20#include "AccountsAdaptor.h"
20#include "AccountsAdaptor.h"
21#include "AccountsServer.h"
21#include "AccountsServer.h"
22−#include "PropertiesAdaptor.h"
23#include "PropertiesServer.h"
22#include "PropertiesServer.h"
24#include "SecurityPrivacyAdaptor.h"
23#include "SecurityPrivacyAdaptor.h"
25#include "LocationAdaptor.h"
24#include "LocationAdaptor.h"
26−#include "AccountsUserAdaptor.h"
27#include "AccountsPrivateAdaptor.h"
25#include "AccountsPrivateAdaptor.h"
28#include <QCoreApplication>
26#include <QCoreApplication>
29
27
@@ -31,11 +29,7 @@ int main(int argc, char *argv[])
31{
29{
32 QCoreApplication a(argc, argv);
30 QCoreApplication a(argc, argv);
33
31
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);
39 new AccountsAdaptor(accounts);
33 new AccountsAdaptor(accounts);
40 new SecurityPrivacyAdaptor(accounts);
34 new SecurityPrivacyAdaptor(accounts);
41 new LocationAdaptor(accounts);
35 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)
3include_directories(
3include_directories(
4 ${CMAKE_CURRENT_SOURCE_DIR}
4 ${CMAKE_CURRENT_SOURCE_DIR}
5 ${CMAKE_CURRENT_BINARY_DIR}
5 ${CMAKE_CURRENT_BINARY_DIR}
6+ ${CMAKE_SOURCE_DIR}/plugins/AccountsService
6 ${CMAKE_SOURCE_DIR}/plugins/Greeter/Unity/Launcher
7 ${CMAKE_SOURCE_DIR}/plugins/Greeter/Unity/Launcher
7 ${libunity8-private_SOURCE_DIR}
8 ${libunity8-private_SOURCE_DIR}
8 )
9 )
@@ -14,7 +15,7 @@ add_definitions(-DLAUNCHER_TESTING)
14### LauncherModelASTest
15### LauncherModelASTest
15add_executable(launchermodelastestExec
16add_executable(launchermodelastestExec
16 launchermodelastest.cpp
17 launchermodelastest.cpp
17− AccountsServiceDBusAdaptor.cpp
18+ ${CMAKE_SOURCE_DIR}/plugins/AccountsService/AccountsServiceDBusAdaptor.cpp
18 ${CMAKE_SOURCE_DIR}/plugins/Greeter/Unity/Launcher/launchermodelas.cpp
19 ${CMAKE_SOURCE_DIR}/plugins/Greeter/Unity/Launcher/launchermodelas.cpp
19 ${CMAKE_SOURCE_DIR}/plugins/Greeter/Unity/Launcher/launcheritem.cpp
20 ${CMAKE_SOURCE_DIR}/plugins/Greeter/Unity/Launcher/launcheritem.cpp
20 ${CMAKE_SOURCE_DIR}/plugins/Greeter/Unity/Launcher/quicklistmodel.cpp
21 ${CMAKE_SOURCE_DIR}/plugins/Greeter/Unity/Launcher/quicklistmodel.cpp
@@ -29,10 +30,15 @@ target_link_libraries(launchermodelastestExec
29 unity8-private
30 unity8-private
30 ${GSETTINGS_QT_LDFLAGS}
31 ${GSETTINGS_QT_LDFLAGS}
31 )
32 )
33+add_dependencies(launchermodelastestExec mock-server)
32qt5_use_modules(launchermodelastestExec Test Core DBus Gui)
34qt5_use_modules(launchermodelastestExec Test Core DBus Gui)
33add_unity8_unittest(LauncherModelAS dbus-test-runner
35add_unity8_unittest(LauncherModelAS dbus-test-runner
34 ARG_PREFIX "--parameter"
36 ARG_PREFIX "--parameter"
37+ ARGS
38+ --task $<TARGET_FILE:mock-server>
39+ --ignore-return
35− ARGS --task $<TARGET_FILE:launchermodelastestExec>
40+ --task $<TARGET_FILE:launchermodelastestExec>
41+ --wait-for org.freedesktop.Accounts
36)
42)
37
43
38# copy .desktop files into build directory for shadow builds
44# copy .desktop files into build directory for shadow builds
tests/plugins/Greeter/Unity/Launcher/launchermodelastest.cpp+81−16
@@ -22,9 +22,12 @@
22#include "launchermodelas.h"
22#include "launchermodelas.h"
23#include "AccountsServiceDBusAdaptor.h"
23#include "AccountsServiceDBusAdaptor.h"
24
24
25+#include <QDBusInterface>
26+#include <QDBusMetaType>
27+#include <QDBusReply>
28+#include <QSignalSpy>
25#include <QtTest>
29#include <QtTest>
26
30
27−QHash<QString, QList<QVariantMap>> mockProperties;
28
31
29class LauncherModelASTest : public QObject
32class LauncherModelASTest : public QObject
30{
33{
@@ -32,9 +35,15 @@ class LauncherModelASTest : public QObject
32
35
33private:
36private:
34 LauncherModel *launcherModel;
37 LauncherModel *launcherModel;
38+ QStringList users;
39+ QHash<QString, QList<QVariantMap>> mockProperties;
35
40
36private Q_SLOTS:
41private Q_SLOTS:
37
42
43+ void initTestCase() {
44+ qDBusRegisterMetaType<QList<QVariantMap>>();
45+ }
46+
38 void init() {
47 void init() {
39 // Prepare some fake list
48 // Prepare some fake list
40 QList<QVariantMap> list;
49 QList<QVariantMap> list;
@@ -50,7 +59,63 @@ private Q_SLOTS:
50 item["id"] = "appId2";
59 item["id"] = "appId2";
51 item["name"] = "Item 2";
60 item["name"] = "Item 2";
52 list.append(item);
61 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());
54 }
119 }
55
120
56 bool isInSync(LauncherModel *model, const QList<QVariantMap> &properties) {
121 bool isInSync(LauncherModel *model, const QList<QVariantMap> &properties) {
@@ -81,7 +146,7 @@ private Q_SLOTS:
81 // We didn't set a user yet. model should be empty
146 // We didn't set a user yet. model should be empty
82 QCOMPARE(model->rowCount(), 0);
147 QCOMPARE(model->rowCount(), 0);
83
148
84− model->setUser("user1");
149+ setUser(model, "user1");
85
150
86 QCOMPARE(isInSync(model, mockProperties["user1"]), true);
151 QCOMPARE(isInSync(model, mockProperties["user1"]), true);
87 model->deleteLater();
152 model->deleteLater();
@@ -102,7 +167,7 @@ private Q_SLOTS:
102 QFETCH(bool, inSync);
167 QFETCH(bool, inSync);
103
168
104 LauncherModel* model = new LauncherModel(this);
169 LauncherModel* model = new LauncherModel(this);
105− model->setUser(modelUser);
170+ setUser(model, modelUser);
106
171
107 int oldCount = mockProperties[modelUser].count();
172 int oldCount = mockProperties[modelUser].count();
108 QCOMPARE(model->rowCount(), oldCount);
173 QCOMPARE(model->rowCount(), oldCount);
@@ -117,17 +182,17 @@ private Q_SLOTS:
117 newEntry.insert("countVisible", false);
182 newEntry.insert("countVisible", false);
118 newEntry.insert("pinned", true);
183 newEntry.insert("pinned", true);
119 newList.append(newEntry);
184 newList.append(newEntry);
120− model->m_accounts->simulatePropertyChange(changedUser, "LauncherItems", QVariant::fromValue(newList));
185+ setASLauncherItems(changedUser, newList);
121
186
122− QCOMPARE(isInSync(model, mockProperties[modelUser]), true);
187+ QTRY_COMPARE(isInSync(model, mockProperties[modelUser]), true);
123− QCOMPARE(isInSync(model, mockProperties[changedUser]), inSync);
188+ QTRY_COMPARE(isInSync(model, mockProperties[changedUser]), inSync);
124
189
125 model->deleteLater();
190 model->deleteLater();
126 }
191 }
127
192
128 void testUpdateCount() {
193 void testUpdateCount() {
129 LauncherModel* model = new LauncherModel(this);
194 LauncherModel* model = new LauncherModel(this);
130− model->setUser("user1");
195+ setUser(model, "user1");
131
196
132 QCOMPARE(model->get(0)->countVisible(), false);
197 QCOMPARE(model->get(0)->countVisible(), false);
133 QCOMPARE(model->get(0)->count(), 0);
198 QCOMPARE(model->get(0)->count(), 0);
@@ -137,34 +202,34 @@ private Q_SLOTS:
137 entry["countVisible"] = true;
202 entry["countVisible"] = true;
138 entry["count"] = 55;
203 entry["count"] = 55;
139 newList[0] = entry;
204 newList[0] = entry;
140− model->m_accounts->simulatePropertyChange("user1", "LauncherItems", QVariant::fromValue(newList));
205+ setASLauncherItems("user1", newList);
141
206
142− QCOMPARE(isInSync(model, mockProperties["user1"]), true);
207+ QTRY_COMPARE(isInSync(model, mockProperties["user1"]), true);
143
208
144 model->deleteLater();
209 model->deleteLater();
145 }
210 }
146
211
147 void testOnlyPinned() {
212 void testOnlyPinned() {
148 LauncherModel *model = new LauncherModel(this);
213 LauncherModel *model = new LauncherModel(this);
149− model->setUser("user1");
214+ setUser(model, "user1");
150 model->setOnlyPinned(true);
215 model->setOnlyPinned(true);
151
216
152− QCOMPARE(isInSync(model, mockProperties["user1"]), true);
217+ QTRY_COMPARE(isInSync(model, mockProperties["user1"]), true);
153
218
154 // Let's unpin one item
219 // Let's unpin one item
155 QList<QVariantMap> newList = mockProperties["user1"];
220 QList<QVariantMap> newList = mockProperties["user1"];
156 QVariantMap entry = newList.at(0);
221 QVariantMap entry = newList.at(0);
157 entry["pinned"] = false;
222 entry["pinned"] = false;
158 newList[0] = entry;
223 newList[0] = entry;
159− model->m_accounts->simulatePropertyChange("user1", "LauncherItems", QVariant::fromValue(newList));
224+ setASLauncherItems("user1", newList);
160− QCOMPARE(isInSync(model, mockProperties["user1"]), true);
225+ QTRY_COMPARE(isInSync(model, mockProperties["user1"]), true);
161
226
162 // Now toggle onlyPinned and make sure the model keeps up
227 // Now toggle onlyPinned and make sure the model keeps up
163 model->setOnlyPinned(false);
228 model->setOnlyPinned(false);
164− QCOMPARE(isInSync(model, mockProperties["user1"]), true);
229+ QTRY_COMPARE(isInSync(model, mockProperties["user1"]), true);
165
230
166 model->setOnlyPinned(true);
231 model->setOnlyPinned(true);
167− QCOMPARE(isInSync(model, mockProperties["user1"]), true);
232+ QTRY_COMPARE(isInSync(model, mockProperties["user1"]), true);
168 }
233 }
169};
234};
170
235
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)
4include_directories(
4include_directories(
5 ${CMAKE_CURRENT_SOURCE_DIR}
5 ${CMAKE_CURRENT_SOURCE_DIR}
6 ${CMAKE_CURRENT_BINARY_DIR}
6 ${CMAKE_CURRENT_BINARY_DIR}
7+ ${CMAKE_SOURCE_DIR}/plugins/AccountsService
7 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher
8 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher
8 ${GSETTINGS_QT_INCLUDE_DIRS}
9 ${GSETTINGS_QT_INCLUDE_DIRS}
9 ${libunity8-private_SOURCE_DIR}
10 ${libunity8-private_SOURCE_DIR}
@@ -17,7 +18,7 @@ add_definitions(-DLAUNCHER_TESTING)
17add_executable(launchermodeltestExec
18add_executable(launchermodeltestExec
18 launchermodeltest.cpp
19 launchermodeltest.cpp
19 gsettings.cpp
20 gsettings.cpp
20− AccountsServiceDBusAdaptor.cpp
21+ ${CMAKE_SOURCE_DIR}/plugins/AccountsService/AccountsServiceDBusAdaptor.cpp
21 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/launchermodel.cpp
22 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/launchermodel.cpp
22 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/asadapter.cpp
23 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/asadapter.cpp
23 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/launcheritem.cpp
24 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher/launcheritem.cpp
@@ -35,11 +36,16 @@ target_link_libraries(launchermodeltestExec
35 unity8-private
36 unity8-private
36 ${GSETTINGS_QT_LDFLAGS}
37 ${GSETTINGS_QT_LDFLAGS}
37 )
38 )
39+add_dependencies(launchermodeltestExec mock-server)
38qt5_use_modules(launchermodeltestExec Test Core DBus Xml Gui)
40qt5_use_modules(launchermodeltestExec Test Core DBus Xml Gui)
39
41
40add_unity8_unittest(LauncherModel dbus-test-runner
42add_unity8_unittest(LauncherModel dbus-test-runner
41 ARG_PREFIX "--parameter"
43 ARG_PREFIX "--parameter"
44+ ARGS
45+ --task $<TARGET_FILE:mock-server>
46+ --ignore-return
42− ARGS --task $<TARGET_FILE:launchermodeltestExec>
47+ --task $<TARGET_FILE:launchermodeltestExec>
48+ --wait-for org.freedesktop.Accounts
43)
49)
44
50
45# copy .desktop files into build directory for shadow builds
51# copy .desktop files into build directory for shadow builds
tests/plugins/Unity/Launcher/launchermodeltest.cpp+23−1
@@ -31,6 +31,7 @@
31#include <QtTest>
31#include <QtTest>
32#include <QDBusInterface>
32#include <QDBusInterface>
33#include <QDBusReply>
33#include <QDBusReply>
34+#include <QDBusMetaType>
34#include <QDomDocument>
35#include <QDomDocument>
35
36
36// This is a mock, specifically to test the LauncherModel
37// This is a mock, specifically to test the LauncherModel
@@ -136,12 +137,17 @@ private:
136
137
137 QList<QVariantMap> getASConfig() {
138 QList<QVariantMap> getASConfig() {
138 AccountsServiceDBusAdaptor *as = launcherModel->m_asAdapter->m_accounts;
139 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>());
140 }
144 }
141
145
142private Q_SLOTS:
146private Q_SLOTS:
143
147
144 void initTestCase() {
148 void initTestCase() {
149+ qDBusRegisterMetaType<QList<QVariantMap>>();
150+
145 launcherModel = new LauncherModel(this);
151 launcherModel = new LauncherModel(this);
146 QCoreApplication::processEvents(); // to let the model register on DBus
152 QCoreApplication::processEvents(); // to let the model register on DBus
147 QCOMPARE(launcherModel->rowCount(QModelIndex()), 0);
153 QCOMPARE(launcherModel->rowCount(QModelIndex()), 0);
@@ -152,6 +158,14 @@ private Q_SLOTS:
152
158
153 // Adding 2 apps to the mock appmanager. Both should appear in the launcher.
159 // Adding 2 apps to the mock appmanager. Both should appear in the launcher.
154 void init() {
160 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+
155 appManager->addApplication(new MockApp("abs-icon"));
169 appManager->addApplication(new MockApp("abs-icon"));
156 QCOMPARE(launcherModel->rowCount(QModelIndex()), 1);
170 QCOMPARE(launcherModel->rowCount(QModelIndex()), 1);
157
171
@@ -169,6 +183,14 @@ private Q_SLOTS:
169 while (launcherModel->rowCount(QModelIndex()) > 0) {
183 while (launcherModel->rowCount(QModelIndex()) > 0) {
170 launcherModel->requestRemove(launcherModel->get(0)->appId());
184 launcherModel->requestRemove(launcherModel->get(0)->appId());
171 }
185 }
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);
172 }
194 }
173
195
174 void testMove() {
196 void testMove() {
tests/qmltests/CMakeLists.txt+1
@@ -11,6 +11,7 @@ add_unity8_qmltest(Components Dialogs LIGHTDM)
11add_unity8_qmltest(Components DraggingArea)
11add_unity8_qmltest(Components DraggingArea)
12add_unity8_qmltest(Components LazyImage)
12add_unity8_qmltest(Components LazyImage)
13add_unity8_qmltest(Components Lockscreen)
13add_unity8_qmltest(Components Lockscreen)
14+add_unity8_qmltest(Components ModeSwitchWarningDialog)
14add_unity8_qmlunittest(Components PhysicalKeysMapper)
15add_unity8_qmlunittest(Components PhysicalKeysMapper)
15add_unity8_qmltest(Components Rating)
16add_unity8_qmltest(Components Rating)
16add_unity8_qmltest(Components ResponsiveGridView)
17add_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 {
107 Repeater {
107 Repeater {
108 model: [
108 model: [
109 { label: "View width", value: cardTool.viewWidth && cardTool.viewWidth / units.gu(1) },
109 { label: "View width", value: cardTool.viewWidth && cardTool.viewWidth / units.gu(1) },
110− { label: "Card width", value: cardTool.cardWidth && cardTool.cardWidth / units.gu(1) },
110+ { label: "Card width", value: cardTool.cardWidth !== -1 ? cardTool.cardWidth / units.gu(1) : undefined },
111− { label: "Card height", value: cardTool.cardHeight && cardTool.cardHeight / units.gu(1) },
111+ { label: "Card height", value: cardTool.cardHeight !== -1 ? cardTool.cardHeight / units.gu(1) : undefined },
112 ]
112 ]
113
113
114 delegate: Row {
114 delegate: Row {
@@ -265,10 +265,10 @@ Rectangle {
265 { tag: "Small", width: units.gu(12), height: function() { return internalCard ? internalCard.height : 0 }, index: 7 },
265 { tag: "Small", width: units.gu(12), height: function() { return internalCard ? internalCard.height : 0 }, index: 7 },
266 { tag: "Large", width: units.gu(38), height: function() { return internalCard ? internalCard.height : 0 }, index: 8 },
266 { tag: "Large", width: units.gu(38), height: function() { return internalCard ? internalCard.height : 0 }, index: 8 },
267 { tag: "Horizontal", width: units.gu(38), height: function() { return internalCard ? internalCard.height : 0 }, index: 9 },
267 { 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},
268+ { tag: "OrganicGrid", width: -1, height: -1, index: 0, layout_index: 1},
269− { tag: "Journal", width: undefined, height: units.gu(20), size: 20, index: 0, layout_index: 2 },
269+ { tag: "Journal", width: -1, 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 },
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: undefined, index: 0, layout_index: 3 },
271+ { tag: "VerticalJournal", width: units.gu(18), height: -1, index: 0, layout_index: 3 },
272 { tag: "SmallCarousel", width: units.gu(18), height: units.gu(18), viewWidth: 30, index: 0, layout_index: 4},
272 { tag: "SmallCarousel", width: units.gu(18), height: units.gu(18), viewWidth: 30, index: 0, layout_index: 4},
273 { tag: "MediumCarousel", width: units.gu(22), height: units.gu(22), viewWidth: 84, index: 0, layout_index: 4},
273 { tag: "MediumCarousel", width: units.gu(22), height: units.gu(22), viewWidth: 84, index: 0, layout_index: 4},
274 { tag: "LargeCarousel", width: units.gu(26), height: units.gu(26), viewWidth: 140, index: 0, layout_index: 4},
274 { 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
19import QtTest 1.0
19import QtTest 1.0
20import ".."
20import ".."
21import "../../../qml/Notifications"
21import "../../../qml/Notifications"
22−import "../../../qml/Components/UnityInputInfo"
23import Ubuntu.Components 1.3
22import Ubuntu.Components 1.3
24import Unity.Test 0.1
23import Unity.Test 0.1
25import Unity.Notifications 1.0
24import Unity.Notifications 1.0
@@ -188,7 +187,7 @@ Item {
188
187
189 anchors.fill: parent
188 anchors.fill: parent
190 model: mockModel
189 model: mockModel
191− hasMouse: UnityInputInfo.mice > 0 // for testing, we add/remove only a mock mouse
190+ hasMouse: false
192 }
191 }
193 }
192 }
194
193
@@ -264,9 +263,9 @@ Item {
264 id: fakeMouseCB
263 id: fakeMouseCB
265 onClicked: {
264 onClicked: {
266 if (checked) {
265 if (checked) {
267− UnityInputInfo.inputInfo.addMockMouse();
266+ notifications.hasMouse = true;
268 } else {
267 } else {
269− UnityInputInfo.inputInfo.removeMockMouse();
268+ notifications.hasMouse = false;
270 }
269 }
271 }
270 }
272 }
271 }
@@ -695,7 +694,7 @@ Item {
695 }
694 }
696
695
697 function test_clickToClose_data() { // reuse the data
696 function test_clickToClose_data() { // reuse the data
698− UnityInputInfo.inputInfo.addMockMouse();
697+ notifications.hasMouse = true;
699 return test_NotificationRenderer_data();
698 return test_NotificationRenderer_data();
700 }
699 }
701
700
@@ -726,7 +725,7 @@ Item {
726 }
725 }
727
726
728 function cleanupTestCase() {
727 function cleanupTestCase() {
729− UnityInputInfo.inputInfo.removeMockMouse();
728+ notifications.hasMouse = false;
730 }
729 }
731 }
730 }
732 }
731 }
tests/qmltests/Notifications/tst_SwipeToAct.qml+5−6
@@ -19,7 +19,6 @@ import QtQuick.Layouts 1.1
19import QtTest 1.0
19import QtTest 1.0
20import ".."
20import ".."
21import "../../../qml/Notifications"
21import "../../../qml/Notifications"
22−import "../../../qml/Components/UnityInputInfo"
23import Ubuntu.Components 1.3
22import Ubuntu.Components 1.3
24import Unity.Test 0.1
23import Unity.Test 0.1
25import Unity.Notifications 1.0
24import Unity.Notifications 1.0
@@ -112,7 +111,7 @@ Item {
112
111
113 anchors.fill: parent
112 anchors.fill: parent
114 model: mockModel
113 model: mockModel
115− hasMouse: UnityInputInfo.mice > 0 // for testing, we add/remove only a mock mouse
114+ hasMouse: false
116 }
115 }
117 }
116 }
118
117
@@ -152,9 +151,9 @@ Item {
152 id: fakeMouseCB
151 id: fakeMouseCB
153 onClicked: {
152 onClicked: {
154 if (checked) {
153 if (checked) {
155− UnityInputInfo.inputInfo.addMockMouse();
154+ notifications.hasMouse = true;
156 } else {
155 } else {
157− UnityInputInfo.inputInfo.removeMockMouse();
156+ notifications.hasMouse = false;
158 }
157 }
159 }
158 }
160 }
159 }
@@ -301,7 +300,7 @@ Item {
301 }
300 }
302
301
303 // add a mock mouse, test clicking the left/right buttons
302 // add a mock mouse, test clicking the left/right buttons
304− UnityInputInfo.inputInfo.addMockMouse();
303+ notifications.hasMouse = true;
305 var leftButton = findChild(swipeButton, "leftButton");
304 var leftButton = findChild(swipeButton, "leftButton");
306 var rightButton = findChild(swipeButton, "rightButton");
305 var rightButton = findChild(swipeButton, "rightButton");
307
306
@@ -316,7 +315,7 @@ Item {
316 compare(actionSpy.signalArguments[0][0], data.actions.data(1, ActionModel.RoleActionId), "got wrong id for negative action");
315 compare(actionSpy.signalArguments[0][0], data.actions.data(1, ActionModel.RoleActionId), "got wrong id for negative action");
317 actionSpy.clear();
316 actionSpy.clear();
318 }
317 }
319− UnityInputInfo.inputInfo.removeMockMouse();
318+ notifications.hasMouse = false;
320 }
319 }
321 }
320 }
322 }
321 }
tests/qmltests/Stages/tst_DesktopStage.qml+139−32
@@ -60,14 +60,10 @@ Item {
60
60
61 focus: true
61 focus: true
62
62
63− property bool itemDestroyed: false
64 sourceComponent: Component {
63 sourceComponent: Component {
65 DesktopStage {
64 DesktopStage {
66 color: "darkblue"
65 color: "darkblue"
67 anchors.fill: parent
66 anchors.fill: parent
68− Component.onDestruction: {
69− desktopStageLoader.itemDestroyed = true;
70− }
71 orientations: Orientations {}
67 orientations: Orientations {}
72 }
68 }
73 }
69 }
@@ -103,16 +99,10 @@ Item {
103 property Item desktopStage: desktopStageLoader.status === Loader.Ready ? desktopStageLoader.item : null
99 property Item desktopStage: desktopStageLoader.status === Loader.Ready ? desktopStageLoader.item : null
104
100
105 function cleanup() {
101 function cleanup() {
106− desktopStageLoader.itemDestroyed = false;
107 desktopStageLoader.active = false;
102 desktopStageLoader.active = false;
108
103
109 tryCompare(desktopStageLoader, "status", Loader.Null);
104 tryCompare(desktopStageLoader, "status", Loader.Null);
110 tryCompare(desktopStageLoader, "item", null);
105 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);
116
106
117 killAllRunningApps();
107 killAllRunningApps();
118
108
@@ -155,10 +145,7 @@ Item {
155 }
145 }
156
146
157 function test_appFocusSwitch(data) {
147 function test_appFocusSwitch(data) {
158− var i;
159− for (i = 0; i < data.apps.length; i++) {
160− startApplication(data.apps[i]);
148+ data.apps.forEach(startApplication);
161− }
162
149
163 ApplicationManager.requestFocusApplication(data.apps[data.focusfrom]);
150 ApplicationManager.requestFocusApplication(data.apps[data.focusfrom]);
164 tryCompare(ApplicationManager.findApplication(data.apps[data.focusfrom]).session.surface, "activeFocus", true);
151 tryCompare(ApplicationManager.findApplication(data.apps[data.focusfrom]).session.surface, "activeFocus", true);
@@ -175,10 +162,7 @@ Item {
175 }
162 }
176
163
177 function test_tappingOnWindowChangesFocusedApp(data) {
164 function test_tappingOnWindowChangesFocusedApp(data) {
178− var i;
179− for (i = 0; i < data.apps.length; i++) {
180− startApplication(data.apps[i]);
165+ data.apps.forEach(startApplication);
181− }
182 var fromAppId = data.apps[data.focusfrom];
166 var fromAppId = data.apps[data.focusfrom];
183 var toAppId = data.apps[data.focusTo]
167 var toAppId = data.apps[data.focusTo]
184
168
@@ -195,18 +179,37 @@ Item {
195 compare(ApplicationManager.focusedApplicationId, toAppId);
179 compare(ApplicationManager.focusedApplicationId, toAppId);
196 }
180 }
197
181
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+
198 function test_tappingOnDecorationFocusesApplication_data() {
204 function test_tappingOnDecorationFocusesApplication_data() {
199 return [
205 return [
200− {tag: "dash", apps: [ "unity8-dash", "dialer-app", "camera-app" ], focusfrom: 0, focusTo: 1 },
206+ {tag: "dash to dialer", 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 },
207+ {tag: "dialer to dash", apps: [ "unity8-dash", "dialer-app", "camera-app" ], focusfrom: 1, focusTo: 0 },
202 ]
208 ]
203 }
209 }
204
210
205 function test_tappingOnDecorationFocusesApplication(data) {
211 function test_tappingOnDecorationFocusesApplication(data) {
206− var i;
207− for (i = 0; i < data.apps.length; i++) {
208− startApplication(data.apps[i]);
212+ data.apps.forEach(startApplication);
209− }
210
213
211 var fromAppDecoration = findChild(desktopStage, "appWindowDecoration_" + data.apps[data.focusfrom]);
214 var fromAppDecoration = findChild(desktopStage, "appWindowDecoration_" + data.apps[data.focusfrom]);
212 verify(fromAppDecoration);
215 verify(fromAppDecoration);
@@ -219,10 +222,114 @@ Item {
219 tryCompare(ApplicationManager.findApplication(data.apps[data.focusTo]).session.surface, "activeFocus", true);
222 tryCompare(ApplicationManager.findApplication(data.apps[data.focusTo]).session.surface, "activeFocus", true);
220 }
223 }
221
224
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+
222 function test_minimizeApplicationHidesSurface() {
329 function test_minimizeApplicationHidesSurface() {
223 var dashApp = startApplication("unity8-dash");
330 var dashApp = startApplication("unity8-dash");
224
331
225− var dashDelegate = findChild(desktopStage, "stageDelegate_unity8-dash");
332+ var dashDelegate = findChild(desktopStage, "appDelegate_unity8-dash");
226 verify(dashDelegate);
333 verify(dashDelegate);
227
334
228 findChild(dashDelegate, "decoratedWindow").minimize();
335 findChild(dashDelegate, "decoratedWindow").minimize();
@@ -234,11 +341,11 @@ Item {
234 var dialerApp = startApplication("dialer-app");
341 var dialerApp = startApplication("dialer-app");
235 var cameraApp = startApplication("camera-app");
342 var cameraApp = startApplication("camera-app");
236
343
237− var dashDelegate = findChild(desktopStage, "stageDelegate_unity8-dash");
344+ var dashDelegate = findChild(desktopStage, "appDelegate_unity8-dash");
238 verify(dashDelegate);
345 verify(dashDelegate);
239− var dialerDelegate = findChild(desktopStage, "stageDelegate_dialer-app");
346+ var dialerDelegate = findChild(desktopStage, "appDelegate_dialer-app");
240 verify(dialerDelegate);
347 verify(dialerDelegate);
241− var cameraDelegate = findChild(desktopStage, "stageDelegate_camera-app");
348+ var cameraDelegate = findChild(desktopStage, "appDelegate_camera-app");
242 verify(cameraDelegate);
349 verify(cameraDelegate);
243
350
244 // maximize
351 // maximize
@@ -256,20 +363,20 @@ Item {
256
363
257 function test_applicationsBecomeVisibleWhenOccludingAppRemoved() {
364 function test_applicationsBecomeVisibleWhenOccludingAppRemoved() {
258 var dashApp = startApplication("unity8-dash");
365 var dashApp = startApplication("unity8-dash");
259− var dashDelegate = findChild(desktopStage, "stageDelegate_unity8-dash");
366+ var dashDelegate = findChild(desktopStage, "appDelegate_unity8-dash");
260 verify(dashDelegate);
367 verify(dashDelegate);
261
368
262 var dialerApp = startApplication("dialer-app");
369 var dialerApp = startApplication("dialer-app");
263− var dialerDelegate = findChild(desktopStage, "stageDelegate_dialer-app");
370+ var dialerDelegate = findChild(desktopStage, "appDelegate_dialer-app");
264 verify(dialerDelegate);
371 verify(dialerDelegate);
265
372
266 var cameraApp = startApplication("camera-app");
373 var cameraApp = startApplication("camera-app");
267− var cameraDelegate = findChild(desktopStage, "stageDelegate_camera-app");
374+ var cameraDelegate = findChild(desktopStage, "appDelegate_camera-app");
268 verify(cameraDelegate);
375 verify(cameraDelegate);
269 findChild(dialerDelegate, "decoratedWindow").maximize();
376 findChild(dialerDelegate, "decoratedWindow").maximize();
270
377
271 var galleryApp = startApplication("gallery-app");
378 var galleryApp = startApplication("gallery-app");
272− var galleryDelegate = findChild(desktopStage, "stageDelegate_gallery-app");
379+ var galleryDelegate = findChild(desktopStage, "appDelegate_gallery-app");
273 verify(galleryDelegate);
380 verify(galleryDelegate);
274 findChild(galleryDelegate, "decoratedWindow").maximize();
381 findChild(galleryDelegate, "decoratedWindow").maximize();
275
382
tests/qmltests/Stages/tst_WindowResizeArea.qml+43
@@ -19,6 +19,7 @@ import QtQuick.Layouts 1.1
19import QtTest 1.0
19import QtTest 1.0
20import Unity.Test 0.1
20import Unity.Test 0.1
21import ".."
21import ".."
22+import "../../../qml/Components/PanelState"
22import "../../../qml/Stages"
23import "../../../qml/Stages"
23import Ubuntu.Components 1.3
24import Ubuntu.Components 1.3
24import Ubuntu.Components.ListItems 1.3 as ListItem
25import Ubuntu.Components.ListItems 1.3 as ListItem
@@ -31,6 +32,12 @@ Item {
31
32
32 property var fakeWindow: windowLoader.item
33 property var fakeWindow: windowLoader.item
33
34
35+ Binding {
36+ target: PanelState
37+ property: "panelHeight"
38+ value: units.gu(2)
39+ }
40+
34 Component {
41 Component {
35 id: fakeWindowComponent
42 id: fakeWindowComponent
36
43
@@ -57,6 +64,8 @@ Item {
57 minWidth: units.gu(15)
64 minWidth: units.gu(15)
58 minHeight: units.gu(10)
65 minHeight: units.gu(10)
59 windowId: "test-window-id"
66 windowId: "test-window-id"
67+ screenWidth: root.width
68+ screenHeight: root.height
60 }
69 }
61
70
62 Rectangle {
71 Rectangle {
@@ -228,5 +237,39 @@ Item {
228 // clean up
237 // clean up
229 fakeWindow.state = "normal"
238 fakeWindow.state = "normal"
230 }
239 }
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+ }
231 }
274 }
232}
275}
tests/qmltests/tst_OrientedShell.qml+19−10
@@ -26,7 +26,6 @@ import Powerd 0.1
26import Unity.InputInfo 0.1
26import Unity.InputInfo 0.1
27
27
28import "../../qml"
28import "../../qml"
29−import "../../qml/Components/UnityInputInfo"
30
29
31Rectangle {
30Rectangle {
32 id: root
31 id: root
@@ -52,7 +51,17 @@ Rectangle {
52
51
53 QtObject{
52 QtObject{
54 id: mockOskSettings
53 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
56 }
65 }
57
66
58 property int physicalOrientation0
67 property int physicalOrientation0
@@ -328,14 +337,14 @@ Rectangle {
328 text: "Add mouse"
337 text: "Add mouse"
329 activeFocusOnPress: false
338 activeFocusOnPress: false
330 onClicked: {
339 onClicked: {
331− UnityInputInfo.inputInfo.addMockMouse()
340+ MockInputDeviceBackend.addMockDevice("/mouse" + miceModel.count, InputInfo.Mouse)
332 }
341 }
333 }
342 }
334 Button {
343 Button {
335 text: "Remove mouse"
344 text: "Remove mouse"
336 activeFocusOnPress: false
345 activeFocusOnPress: false
337 onClicked: {
346 onClicked: {
338− UnityInputInfo.inputInfo.removeMockMouse()
347+ MockInputDeviceBackend.removeDevice("/mouse" + (miceModel.count - 1))
339 }
348 }
340 }
349 }
341 }
350 }
@@ -344,14 +353,14 @@ Rectangle {
344 text: "Add kbd"
353 text: "Add kbd"
345 activeFocusOnPress: false
354 activeFocusOnPress: false
346 onClicked: {
355 onClicked: {
347− UnityInputInfo.inputInfo.addMockKeyboard()
356+ MockInputDeviceBackend.addMockDevice("/kbd" + keyboardsModel.count, InputInfo.Keyboard)
348 }
357 }
349 }
358 }
350 Button {
359 Button {
351 activeFocusOnPress: false
360 activeFocusOnPress: false
352 text: "Remove kbd"
361 text: "Remove kbd"
353 onClicked: {
362 onClicked: {
354− UnityInputInfo.inputInfo.removeMockKeyboard()
363+ MockInputDeviceBackend.removeDevice("/kbd" + (keyboardsModel.count - 1))
355 }
364 }
356 }
365 }
357 }
366 }
@@ -976,19 +985,19 @@ Rectangle {
976 tryCompare(shell, "usageScenario", "phone");
985 tryCompare(shell, "usageScenario", "phone");
977 tryCompare(mockOskSettings, "stayHidden", false);
986 tryCompare(mockOskSettings, "stayHidden", false);
978
987
979− UnityInputInfo.inputInfo.addMockKeyboard();
988+ MockInputDeviceBackend.addMockDevice("/kbd0", InputInfo.Keyboard);
980 tryCompare(shell, "usageScenario", "phone");
989 tryCompare(shell, "usageScenario", "phone");
981 tryCompare(mockOskSettings, "stayHidden", true);
990 tryCompare(mockOskSettings, "stayHidden", true);
982
991
983− UnityInputInfo.inputInfo.addMockMouse();
992+ MockInputDeviceBackend.addMockDevice("/mouse0", InputInfo.Mouse);
984 tryCompare(shell, "usageScenario", "desktop");
993 tryCompare(shell, "usageScenario", "desktop");
985 tryCompare(mockOskSettings, "stayHidden", true);
994 tryCompare(mockOskSettings, "stayHidden", true);
986
995
987− UnityInputInfo.inputInfo.removeMockKeyboard();
996+ MockInputDeviceBackend.removeDevice("/kbd0");
988 tryCompare(shell, "usageScenario", "desktop");
997 tryCompare(shell, "usageScenario", "desktop");
989 tryCompare(mockOskSettings, "stayHidden", false);
998 tryCompare(mockOskSettings, "stayHidden", false);
990
999
991− UnityInputInfo.inputInfo.removeMockMouse();
1000+ MockInputDeviceBackend.removeDevice("/mouse0");
992 tryCompare(shell, "usageScenario", "phone");
1001 tryCompare(shell, "usageScenario", "phone");
993 tryCompare(mockOskSettings, "stayHidden", false);
1002 tryCompare(mockOskSettings, "stayHidden", false);
994 }
1003 }
tests/qmltests/tst_Shell.qml+93−4
@@ -313,6 +313,7 @@ Rectangle {
313 }
313 }
314
314
315 function cleanup() {
315 function cleanup() {
316+ waitForRendering(shell);
316 mouseEmulation.checked = true;
317 mouseEmulation.checked = true;
317 tryCompare(shell, "enabled", true); // make sure greeter didn't leave us in disabled state
318 tryCompare(shell, "enabled", true); // make sure greeter didn't leave us in disabled state
318 tearDown();
319 tearDown();
@@ -344,9 +345,9 @@ Rectangle {
344 var app2 = ApplicationManager.startApplication("webbrowser-app")
345 var app2 = ApplicationManager.startApplication("webbrowser-app")
345 var app3 = ApplicationManager.startApplication("camera-app")
346 var app3 = ApplicationManager.startApplication("camera-app")
346 var app4 = ApplicationManager.startApplication("facebook-webapp")
347 var app4 = ApplicationManager.startApplication("facebook-webapp")
347− var app5 = ApplicationManager.startApplication("calendar-app")
348+ var app5 = ApplicationManager.startApplication("camera-app")
348 var app6 = ApplicationManager.startApplication("gallery-app")
349 var app6 = ApplicationManager.startApplication("gallery-app")
349− var app7 = ApplicationManager.startApplication("camera-app")
350+ var app7 = ApplicationManager.startApplication("calendar-app")
350 waitUntilAppWindowIsFullyLoaded(app7);
351 waitUntilAppWindowIsFullyLoaded(app7);
351 }
352 }
352
353
@@ -1710,11 +1711,11 @@ Rectangle {
1710 keyPress(Qt.Key_Control)
1711 keyPress(Qt.Key_Control)
1711 keyClick(Qt.Key_Tab);
1712 keyClick(Qt.Key_Tab);
1712
1713
1713−
1714 tryCompare(desktopSpread, "state", "altTab")
1714 tryCompare(desktopSpread, "state", "altTab")
1715
1715
1716− mouseMove(shell, 0, 0);
1716+ mouseMove(shell, 0, shell.height / 2);
1717 tryCompare(launcher, "state", "visibleTemporary")
1717 tryCompare(launcher, "state", "visibleTemporary")
1718+ waitForRendering(shell)
1718
1719
1719 mouseClick(bfb, bfb.width / 2, bfb.height / 2)
1720 mouseClick(bfb, bfb.width / 2, bfb.height / 2)
1720 tryCompare(launcher, "state", "")
1721 tryCompare(launcher, "state", "")
@@ -1809,6 +1810,37 @@ Rectangle {
1809 tryCompare(panelButtons, "visible", true);
1810 tryCompare(panelButtons, "visible", true);
1810 }
1811 }
1811
1812
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+
1812 function test_lifecyclePolicyForNonTouchApp_data() {
1844 function test_lifecyclePolicyForNonTouchApp_data() {
1813 return [
1845 return [
1814 {tag: "phone", formFactor: "phone", usageScenario: "phone"},
1846 {tag: "phone", formFactor: "phone", usageScenario: "phone"},
@@ -1853,5 +1885,62 @@ Rectangle {
1853 compare(app1.session.surface.activeFocus, false);
1885 compare(app1.session.surface.activeFocus, false);
1854 compare(app1.requestedState, ApplicationInfoInterface.RequestedRunning);
1886 compare(app1.requestedState, ApplicationInfoInterface.RequestedRunning);
1855 }
1887 }
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+ }
1856 }
1945 }
1857}
1946}