Merge lp:~dandrader/unity8/animatedCursors into lp:unity8
- animatedCursors
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Lukáš Tinkl |
Approved revision: | no longer in the source branch. |
Merged at revision: | 2439 |
Proposed branch: | lp:~dandrader/unity8/animatedCursors |
Merge into: | lp:unity8 |
Diff against target: |
894 lines (+467/-145) 19 files modified
CMakeLists.txt (+1/-1) debian/control (+2/-2) plugins/Cursor/CMakeLists.txt (+2/-1) plugins/Cursor/Cursor.qml (+21/-5) plugins/Cursor/Cursor.qmltypes (+0/-78) plugins/Cursor/CursorImageInfo.cpp (+106/-0) plugins/Cursor/CursorImageInfo.h (+76/-0) plugins/Cursor/CursorImageProvider.cpp (+52/-23) plugins/Cursor/CursorImageProvider.h (+12/-4) plugins/Cursor/MousePointer.cpp (+0/-19) plugins/Cursor/MousePointer.h (+0/-6) plugins/Cursor/plugin.cpp (+4/-2) plugins/Cursor/qmldir (+1/-1) qml/Shell.qml (+1/-1) tests/mocks/Cursor/qmldir (+1/-1) tests/plugins/CMakeLists.txt (+2/-1) tests/plugins/Cursor/CMakeLists.txt (+8/-0) tests/plugins/Cursor/TextEntry.qml (+34/-0) tests/plugins/Cursor/tst_Cursor.qml (+144/-0) |
To merge this branch: | bzr merge lp:~dandrader/unity8/animatedCursors |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Unity8 CI Bot | continuous-integration | Needs Fixing | |
Lukáš Tinkl (community) | Approve | ||
Michael Zanetti | Pending | ||
Review via email: mp+293628@code.launchpad.net |
Commit message
Support animated cursors
Description of the change
* Are there any related MPs required for this MP to build/function as expected? Please list.
https:/
https:/
* Did you perform an exploratory manual test run of your code change and any related functionality?
Yes.
For testing you have two options:
1 - Run "make tryCursor". Then type "watch" in the cursorName field and hit the "Apply" button.
2 - Run the demo app:
- bzr branch lp:~dandrader/+junk/animatedDemos
- cd animatedDemos/
- qmake && make
- cd ..
- qmlscene CursorShapes.qml -I . --desktop_
- hover mouse over the "Wait" or "Busy" boxes as they are the only ones with animated cursors.
* If you changed the packaging (debian), did you subscribe the ubuntu-unity team to this MP?
Not applicable
* If you changed the UI, has there been a design review?
Not applicable
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
- 2382. By CI Train Bot Account
-
Resync trunk.
- 2383. By Andrea Cimitan
-
Add social actions to cards
Approved by: Josh Arenson - 2384. By Albert Astals Cid
-
Pass the category correctly
Approved by: Pawel Stolowski
- 2385. By Albert Astals Cid
-
Make the previewActions react to the actions model changing
i.e. use a binding instead of a direct assingment Fixes: #1485887
Approved by: Andrea Cimitan - 2386. By Albert Astals Cid
-
Update the icon also to the original one if that's what the scope wants
Approved by: Andrea Cimitan
- 2387. By Albert Astals Cid
-
Reload Preview Zoomable Image source if it gets updated
Approved by: Michael Zanetti
- 2388. By CI Train Bot Account
-
Make the previewActions react to the actions model changing
i.e. use a binding instead of a direct assingment Fixes: #1485887
Approved by: Andrea Cimitan - 2389. By CI Train Bot Account
-
Update the icon also to the original one if that's what the scope wants
Approved by: Andrea Cimitan
- 2390. By CI Train Bot Account
-
Reload Preview Zoomable Image source if it gets updated
Approved by: Michael Zanetti
- 2391. By Albert Astals Cid
-
Expandable Filter Widget
Approved by: Andrea Cimitan, Pawel Stolowski - 2392. By CI Train Bot Account
-
Releasing 8.12+16.
04.20160504. 2-0ubuntu1
Albert Astals Cid (aacid) wrote : | # |
Text conflict in tests/plugins/
1 conflicts encountered.
Daniel d'Andrada (dandrader) wrote : | # |
On 05/05/2016 04:29, Albert Astals Cid wrote:
> Text conflict in tests/plugins/
> 1 conflicts encountered.
Fixed, thanks.
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2384
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
- 2393. By CI Train Bot Account
-
Resync trunk.
- 2394. By Launchpad Translations on behalf of unity-team
-
Launchpad automatic translations update.
Lukáš Tinkl (lukas-kde) wrote : | # |
Please regenerate the Cursor.qmltypes file, it still contains the hotspot stuff
Lukáš Tinkl (lukas-kde) wrote : | # |
Arguably, you should probably bump the Cursor plugin version to 1.1 (in qmldir and plugin.cpp) as this is not backwards compatible.
Daniel d'Andrada (dandrader) wrote : | # |
On 09/05/2016 14:05, Lukáš Tinkl wrote:
> Review: Needs Fixing
>
> Please regenerate the Cursor.qmltypes file, it still contains the hotspot stuff
Nuked the file.
Daniel d'Andrada (dandrader) wrote : | # |
On 09/05/2016 14:06, Lukáš Tinkl wrote:
> Review: Needs Fixing
>
> Arguably, you should probably bump the Cursor plugin version to 1.1 (in qmldir and plugin.cpp) as this is not backwards compatible.
Not convinced of the usefulness of that but did it anyway.
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2385
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Lukáš Tinkl (lukas-kde) wrote : | # |
Approving, tested this and works fine, all issues addressed
* Did you perform an exploratory manual test run of the code change and any related functionality?
Yes
* Did CI run pass? If not, please explain why.
No, due to unity-api version bump
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2386
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
- 2395. By Launchpad Translations on behalf of unity-team
-
Launchpad automatic translations update.
- 2396. By Launchpad Translations on behalf of unity-team
-
Launchpad automatic translations update.
- 2397. By Daniel d'Andrada
-
Move prompt surfaces from MirSurface to Application
prompt surfaces can show up even before an application creates its first surface
- 2398. By CI Train Bot Account
-
Releasing 8.12+16.
04.20160518. 1-0ubuntu1 - 2399. By Daniel d'Andrada
-
Support animated cursors
Preview Diff
1 | === modified file 'CMakeLists.txt' |
2 | --- CMakeLists.txt 2016-05-18 11:17:20 +0000 |
3 | +++ CMakeLists.txt 2016-05-19 19:45:00 +0000 |
4 | @@ -57,7 +57,7 @@ |
5 | find_package(Qt5Concurrent 5.4 REQUIRED) |
6 | find_package(Qt5Sql 5.4 REQUIRED) |
7 | |
8 | -pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=16) |
9 | +pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=17) |
10 | pkg_check_modules(GEONAMES REQUIRED geonames>=0.2) |
11 | pkg_check_modules(GIO REQUIRED gio-2.0>=2.32) |
12 | pkg_check_modules(GLIB REQUIRED glib-2.0>=2.32) |
13 | |
14 | === modified file 'debian/control' |
15 | --- debian/control 2016-05-17 19:42:10 +0000 |
16 | +++ debian/control 2016-05-19 19:45:00 +0000 |
17 | @@ -30,7 +30,7 @@ |
18 | libqt5xmlpatterns5-dev, |
19 | libsystemsettings-dev, |
20 | libudev-dev, |
21 | - libunity-api-dev (>= 7.112), |
22 | + libunity-api-dev (>= 7.113), |
23 | libusermetricsoutput1-dev, |
24 | # Need those X11 libs touch emulation from mouse events in manual QML tests on a X11 desktop |
25 | libx11-dev[!armhf], |
26 | @@ -133,7 +133,7 @@ |
27 | qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 1.3.1845) | qtdeclarative5-ubuntu-ui-toolkit-plugin-gles (>= 1.3.1845), |
28 | qtdeclarative5-unity-notifications-plugin (>= 0.1.2) | unity-notifications-impl, |
29 | ubuntu-thumbnailer-impl-0, |
30 | - unity-application-impl-16, |
31 | + unity-application-impl-17, |
32 | unity-notifications-impl-3, |
33 | unity-plugin-scopes | unity-scopes-impl, |
34 | unity-scopes-impl-12, |
35 | |
36 | === modified file 'plugins/Cursor/CMakeLists.txt' |
37 | --- plugins/Cursor/CMakeLists.txt 2015-09-29 13:45:05 +0000 |
38 | +++ plugins/Cursor/CMakeLists.txt 2016-05-19 19:45:00 +0000 |
39 | @@ -8,8 +8,9 @@ |
40 | |
41 | set(QMLPLUGIN_SRC |
42 | plugin.cpp |
43 | + CursorImageInfo.cpp |
44 | + CursorImageProvider.cpp |
45 | MousePointer.cpp |
46 | - CursorImageProvider.cpp |
47 | # We need to run moc on this header |
48 | ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirMousePointerInterface.h |
49 | ) |
50 | |
51 | === modified file 'plugins/Cursor/Cursor.qml' |
52 | --- plugins/Cursor/Cursor.qml 2015-12-03 15:00:04 +0000 |
53 | +++ plugins/Cursor/Cursor.qml 2016-05-19 19:45:00 +0000 |
54 | @@ -1,5 +1,5 @@ |
55 | /* |
56 | - * Copyright (C) 2015 Canonical, Ltd. |
57 | + * Copyright (C) 2015-2016 Canonical, Ltd. |
58 | * |
59 | * This program is free software; you can redistribute it and/or modify |
60 | * it under the terms of the GNU General Public License as published by |
61 | @@ -15,14 +15,30 @@ |
62 | */ |
63 | |
64 | import QtQuick 2.4 |
65 | -import Cursor 1.0 // For MousePointer |
66 | +import Cursor 1.1 |
67 | |
68 | MousePointer { |
69 | id: mousePointer |
70 | |
71 | - Image { |
72 | - x: -mousePointer.hotspotX |
73 | - y: -mousePointer.hotspotY |
74 | + CursorImageInfo { |
75 | + id: imageInfo |
76 | + themeName: mousePointer.themeName |
77 | + cursorName: mousePointer.cursorName |
78 | + } |
79 | + |
80 | + AnimatedSprite { |
81 | + x: -imageInfo.hotspot.x |
82 | + y: -imageInfo.hotspot.y |
83 | source: "image://cursor/" + mousePointer.themeName + "/" + mousePointer.cursorName |
84 | + |
85 | + interpolate: false |
86 | + |
87 | + width: imageInfo.frameWidth |
88 | + height: imageInfo.frameHeight |
89 | + |
90 | + frameCount: imageInfo.frameCount |
91 | + frameDuration: imageInfo.frameDuration |
92 | + frameWidth: imageInfo.frameWidth |
93 | + frameHeight: imageInfo.frameHeight |
94 | } |
95 | } |
96 | |
97 | === removed file 'plugins/Cursor/Cursor.qmltypes' |
98 | --- plugins/Cursor/Cursor.qmltypes 2016-03-11 20:18:12 +0000 |
99 | +++ plugins/Cursor/Cursor.qmltypes 1970-01-01 00:00:00 +0000 |
100 | @@ -1,78 +0,0 @@ |
101 | -import QtQuick.tooling 1.1 |
102 | - |
103 | -// This file describes the plugin-supplied types contained in the library. |
104 | -// It is used for QML tooling purposes only. |
105 | -// |
106 | -// This file was auto-generated by: |
107 | -// 'qmlplugindump -notrelocatable Cursor 1.0 plugins' |
108 | - |
109 | -Module { |
110 | - Component { |
111 | - name: "MirMousePointerInterface" |
112 | - defaultProperty: "data" |
113 | - prototype: "QQuickItem" |
114 | - Property { name: "cursorName"; type: "string"; isReadonly: true } |
115 | - Property { name: "themeName"; type: "string"; isReadonly: true } |
116 | - Property { name: "hotspotX"; type: "double"; isReadonly: true } |
117 | - Property { name: "hotspotY"; type: "double"; isReadonly: true } |
118 | - Signal { |
119 | - name: "cursorNameChanged" |
120 | - Parameter { name: "name"; type: "string" } |
121 | - } |
122 | - Signal { |
123 | - name: "themeNameChanged" |
124 | - Parameter { name: "name"; type: "string" } |
125 | - } |
126 | - Signal { |
127 | - name: "hotspotXChanged" |
128 | - Parameter { name: "value"; type: "double" } |
129 | - } |
130 | - Signal { |
131 | - name: "hotspotYChanged" |
132 | - Parameter { name: "value"; type: "double" } |
133 | - } |
134 | - Method { |
135 | - name: "handleMouseEvent" |
136 | - Parameter { name: "timestamp"; type: "ulong" } |
137 | - Parameter { name: "movement"; type: "QPointF" } |
138 | - Parameter { name: "buttons"; type: "Qt::MouseButtons" } |
139 | - Parameter { name: "modifiers"; type: "Qt::KeyboardModifiers" } |
140 | - } |
141 | - Method { |
142 | - name: "handleWheelEvent" |
143 | - Parameter { name: "timestamp"; type: "ulong" } |
144 | - Parameter { name: "angleDelta"; type: "QPoint" } |
145 | - Parameter { name: "modifiers"; type: "Qt::KeyboardModifiers" } |
146 | - } |
147 | - } |
148 | - Component { |
149 | - name: "MousePointer" |
150 | - defaultProperty: "data" |
151 | - prototype: "MirMousePointerInterface" |
152 | - exports: ["Cursor/MousePointer 1.0"] |
153 | - exportMetaObjectRevisions: [0] |
154 | - Signal { |
155 | - name: "pushedLeftBoundary" |
156 | - Parameter { name: "amount"; type: "double" } |
157 | - Parameter { name: "buttons"; type: "Qt::MouseButtons" } |
158 | - } |
159 | - Signal { |
160 | - name: "pushedRightBoundary" |
161 | - Parameter { name: "amount"; type: "double" } |
162 | - Parameter { name: "buttons"; type: "Qt::MouseButtons" } |
163 | - } |
164 | - Method { |
165 | - name: "handleMouseEvent" |
166 | - Parameter { name: "timestamp"; type: "ulong" } |
167 | - Parameter { name: "movement"; type: "QPointF" } |
168 | - Parameter { name: "buttons"; type: "Qt::MouseButtons" } |
169 | - Parameter { name: "modifiers"; type: "Qt::KeyboardModifiers" } |
170 | - } |
171 | - Method { |
172 | - name: "handleWheelEvent" |
173 | - Parameter { name: "timestamp"; type: "ulong" } |
174 | - Parameter { name: "angleDelta"; type: "QPoint" } |
175 | - Parameter { name: "modifiers"; type: "Qt::KeyboardModifiers" } |
176 | - } |
177 | - } |
178 | -} |
179 | |
180 | === added file 'plugins/Cursor/CursorImageInfo.cpp' |
181 | --- plugins/Cursor/CursorImageInfo.cpp 1970-01-01 00:00:00 +0000 |
182 | +++ plugins/Cursor/CursorImageInfo.cpp 2016-05-19 19:45:00 +0000 |
183 | @@ -0,0 +1,106 @@ |
184 | +/* |
185 | + * Copyright (C) 2016 Canonical, Ltd. |
186 | + * |
187 | + * This program is free software; you can redistribute it and/or modify |
188 | + * it under the terms of the GNU General Public License as published by |
189 | + * the Free Software Foundation; version 3. |
190 | + * |
191 | + * This program is distributed in the hope that it will be useful, |
192 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
193 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
194 | + * GNU General Public License for more details. |
195 | + * |
196 | + * You should have received a copy of the GNU General Public License |
197 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
198 | + */ |
199 | + |
200 | +#include "CursorImageInfo.h" |
201 | + |
202 | +CursorImageInfo::CursorImageInfo(QObject *parent) |
203 | + : QObject(parent) |
204 | +{ |
205 | + m_updateTimer.setInterval(0); |
206 | + m_updateTimer.setSingleShot(true); |
207 | + connect(&m_updateTimer, &QTimer::timeout, this, &CursorImageInfo::update); |
208 | +} |
209 | + |
210 | +void CursorImageInfo::setCursorName(const QString &cursorName) |
211 | +{ |
212 | + if (cursorName != m_cursorName) { |
213 | + m_cursorName = cursorName; |
214 | + Q_EMIT cursorNameChanged(); |
215 | + scheduleUpdate(); |
216 | + } |
217 | +} |
218 | + |
219 | +void CursorImageInfo::setThemeName(const QString &themeName) |
220 | +{ |
221 | + if (m_themeName != themeName) { |
222 | + m_themeName = themeName; |
223 | + Q_EMIT themeNameChanged(); |
224 | + scheduleUpdate(); |
225 | + } |
226 | +} |
227 | + |
228 | +void CursorImageInfo::scheduleUpdate() |
229 | +{ |
230 | + if (!m_updateTimer.isActive()) { |
231 | + m_updateTimer.start(); |
232 | + } |
233 | +} |
234 | + |
235 | +void CursorImageInfo::update() |
236 | +{ |
237 | + m_cursorImage = CursorImageProvider::instance()->fetchCursor(m_themeName, m_cursorName); |
238 | + |
239 | + Q_EMIT hotspotChanged(); |
240 | + Q_EMIT frameWidthChanged(); |
241 | + Q_EMIT frameHeightChanged(); |
242 | + Q_EMIT frameCountChanged(); |
243 | + Q_EMIT frameDurationChanged(); |
244 | +} |
245 | + |
246 | +QPoint CursorImageInfo::hotspot() const |
247 | +{ |
248 | + if (m_cursorImage) { |
249 | + return m_cursorImage->hotspot; |
250 | + } else { |
251 | + return QPoint(); |
252 | + } |
253 | +} |
254 | + |
255 | +qreal CursorImageInfo::frameWidth() const |
256 | +{ |
257 | + if (m_cursorImage) { |
258 | + return m_cursorImage->frameWidth; |
259 | + } else { |
260 | + return 0; |
261 | + } |
262 | +} |
263 | + |
264 | +qreal CursorImageInfo::frameHeight() const |
265 | +{ |
266 | + if (m_cursorImage) { |
267 | + return m_cursorImage->frameHeight; |
268 | + } else { |
269 | + return 0; |
270 | + } |
271 | +} |
272 | + |
273 | +int CursorImageInfo::frameCount() const |
274 | +{ |
275 | + if (m_cursorImage) { |
276 | + return m_cursorImage->frameCount; |
277 | + } else { |
278 | + return 0; |
279 | + } |
280 | +} |
281 | + |
282 | +int CursorImageInfo::frameDuration() const |
283 | +{ |
284 | + if (m_cursorImage) { |
285 | + return m_cursorImage->frameDuration; |
286 | + } else { |
287 | + return 0; |
288 | + } |
289 | +} |
290 | |
291 | === added file 'plugins/Cursor/CursorImageInfo.h' |
292 | --- plugins/Cursor/CursorImageInfo.h 1970-01-01 00:00:00 +0000 |
293 | +++ plugins/Cursor/CursorImageInfo.h 2016-05-19 19:45:00 +0000 |
294 | @@ -0,0 +1,76 @@ |
295 | +/* |
296 | + * Copyright (C) 2016 Canonical, Ltd. |
297 | + * |
298 | + * This program is free software; you can redistribute it and/or modify |
299 | + * it under the terms of the GNU General Public License as published by |
300 | + * the Free Software Foundation; version 3. |
301 | + * |
302 | + * This program is distributed in the hope that it will be useful, |
303 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
304 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
305 | + * GNU General Public License for more details. |
306 | + * |
307 | + * You should have received a copy of the GNU General Public License |
308 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
309 | + */ |
310 | + |
311 | +#ifndef CURSOR_IMAGE_INFO_H |
312 | +#define CURSOR_IMAGE_INFO_H |
313 | + |
314 | +#include "CursorImageProvider.h" |
315 | + |
316 | +#include <QObject> |
317 | +#include <QString> |
318 | +#include <QTimer> |
319 | + |
320 | +class CursorImageInfo : public QObject |
321 | +{ |
322 | + Q_OBJECT |
323 | + |
324 | + Q_PROPERTY(QString themeName READ themeName WRITE setThemeName NOTIFY themeNameChanged) |
325 | + Q_PROPERTY(QString cursorName READ cursorName WRITE setCursorName NOTIFY cursorNameChanged) |
326 | + |
327 | + Q_PROPERTY(QPoint hotspot READ hotspot NOTIFY hotspotChanged) |
328 | + Q_PROPERTY(qreal frameWidth READ frameWidth NOTIFY frameWidthChanged) |
329 | + Q_PROPERTY(qreal frameHeight READ frameHeight NOTIFY frameHeightChanged) |
330 | + Q_PROPERTY(int frameCount READ frameCount NOTIFY frameCountChanged) |
331 | + Q_PROPERTY(int frameDuration READ frameDuration NOTIFY frameDurationChanged) |
332 | + |
333 | +public: |
334 | + CursorImageInfo(QObject *parent = nullptr); |
335 | + |
336 | + QString themeName() const { return m_themeName; } |
337 | + void setThemeName(const QString &); |
338 | + |
339 | + QString cursorName() const { return m_cursorName; } |
340 | + void setCursorName(const QString &); |
341 | + |
342 | + QPoint hotspot() const; |
343 | + qreal frameWidth() const; |
344 | + qreal frameHeight() const; |
345 | + int frameCount() const; |
346 | + int frameDuration() const; |
347 | + |
348 | +Q_SIGNALS: |
349 | + void themeNameChanged(); |
350 | + void cursorNameChanged(); |
351 | + void hotspotChanged(); |
352 | + void frameWidthChanged(); |
353 | + void frameHeightChanged(); |
354 | + void frameCountChanged(); |
355 | + void frameDurationChanged(); |
356 | + |
357 | +private Q_SLOTS: |
358 | + void update(); |
359 | + |
360 | +private: |
361 | + void scheduleUpdate(); |
362 | + QTimer m_updateTimer; |
363 | + |
364 | + QString m_themeName; |
365 | + QString m_cursorName; |
366 | + |
367 | + CursorImage *m_cursorImage{nullptr}; |
368 | +}; |
369 | + |
370 | +#endif // CURSOR_IMAGE_INFO_H |
371 | |
372 | === modified file 'plugins/Cursor/CursorImageProvider.cpp' |
373 | --- plugins/Cursor/CursorImageProvider.cpp 2015-11-30 17:38:20 +0000 |
374 | +++ plugins/Cursor/CursorImageProvider.cpp 2016-05-19 19:45:00 +0000 |
375 | @@ -1,5 +1,5 @@ |
376 | /* |
377 | - * Copyright (C) 2015 Canonical, Ltd. |
378 | + * Copyright (C) 2015-2016 Canonical, Ltd. |
379 | * |
380 | * This program is free software: you can redistribute it and/or modify it under |
381 | * the terms of the GNU Lesser General Public License version 3, as published by |
382 | @@ -46,6 +46,9 @@ |
383 | qimage.fill(Qt::transparent); |
384 | QPainter imagePainter(&qimage); |
385 | |
386 | + frameWidth = qimage.width(); |
387 | + frameHeight = qimage.height(); |
388 | + |
389 | QSvgRenderer *svgRenderer = new QSvgRenderer(QByteArray(svgString)); |
390 | svgRenderer->render(&imagePainter); |
391 | delete svgRenderer; |
392 | @@ -59,6 +62,8 @@ |
393 | { |
394 | qimage = QImage(1, 1, QImage::Format_ARGB32); |
395 | qimage.fill(Qt::transparent); |
396 | + frameWidth = qimage.width(); |
397 | + frameHeight = qimage.height(); |
398 | } |
399 | |
400 | ///// |
401 | @@ -69,40 +74,74 @@ |
402 | { |
403 | qimage = cursor.pixmap().toImage(); |
404 | hotspot = cursor.hotSpot(); |
405 | + frameWidth = qimage.width(); |
406 | + frameHeight = qimage.height(); |
407 | } |
408 | |
409 | ///// |
410 | // XCursorImage |
411 | |
412 | XCursorImage::XCursorImage(const QString &theme, const QString &file) |
413 | - : xcursorImages(nullptr) |
414 | { |
415 | // TODO: Consider grid unit value |
416 | // Hardcoding to a medium size for now |
417 | int preferredCursorHeightPx = 32; |
418 | |
419 | - xcursorImages = XcursorLibraryLoadImages(QFile::encodeName(file), QFile::encodeName(theme), |
420 | + XcursorImages *xcursorImages = XcursorLibraryLoadImages(QFile::encodeName(file), QFile::encodeName(theme), |
421 | preferredCursorHeightPx); |
422 | - if (!xcursorImages) { |
423 | + if (!xcursorImages || xcursorImages->nimage == 0) { |
424 | return; |
425 | } |
426 | |
427 | - // Just take the first one. It will have multiple images in case of an animated cursor. |
428 | - // TODO: Support animated cursors |
429 | - if ( xcursorImages->nimage > 0) { |
430 | - XcursorImage *xcursorImage = xcursorImages->images[0]; |
431 | - |
432 | - qimage = QImage((uchar*)xcursorImage->pixels, |
433 | - xcursorImage->width, xcursorImage->height, QImage::Format_ARGB32); |
434 | - |
435 | + frameCount = xcursorImages->nimage; |
436 | + |
437 | + for (int i = 0; i < xcursorImages->nimage; ++i) { |
438 | + XcursorImage *xcursorImage = xcursorImages->images[0]; |
439 | + if (frameWidth < (int)xcursorImage->width) { |
440 | + frameWidth = xcursorImage->width; |
441 | + } |
442 | + if (frameHeight < (int)xcursorImage->height) { |
443 | + frameHeight = xcursorImage->height; |
444 | + } |
445 | + if (i == 0) { |
446 | + frameDuration = (int)xcursorImage->delay; |
447 | + } else { |
448 | + if (frameDuration != (int)xcursorImage->delay) { |
449 | + qWarning().nospace() << "CursorImageProvider: XCursorImage("<<theme<<","<<file<<") has" |
450 | + " varying delays in its animation. Animation won't look right."; |
451 | + } |
452 | + } |
453 | + } |
454 | + |
455 | + { |
456 | + // Assume that the hotspot position does not animate |
457 | + XcursorImage *xcursorImage = xcursorImages->images[0]; |
458 | hotspot.setX(xcursorImage->xhot); |
459 | hotspot.setY(xcursorImage->yhot); |
460 | } |
461 | + |
462 | + // Build the sprite as a single row of frames |
463 | + qimage = QImage(frameWidth*frameCount, frameHeight, QImage::Format_ARGB32); |
464 | + qimage.fill(Qt::transparent); |
465 | + |
466 | + { |
467 | + QPainter painter(&qimage); |
468 | + |
469 | + for (int i = 0; i < xcursorImages->nimage; ++i) { |
470 | + XcursorImage *xcursorImage = xcursorImages->images[i]; |
471 | + |
472 | + auto frameImage = QImage((uchar*)xcursorImage->pixels, |
473 | + xcursorImage->width, xcursorImage->height, QImage::Format_ARGB32); |
474 | + |
475 | + painter.drawImage(QPoint(i*frameWidth, 0), frameImage); |
476 | + } |
477 | + } |
478 | + |
479 | + XcursorImagesDestroy(xcursorImages); |
480 | } |
481 | |
482 | XCursorImage::~XCursorImage() |
483 | { |
484 | - XcursorImagesDestroy(xcursorImages); |
485 | } |
486 | |
487 | ///// |
488 | @@ -205,16 +244,6 @@ |
489 | return cursorImage->qimage; |
490 | } |
491 | |
492 | -QPoint CursorImageProvider::hotspot(const QString &themeName, const QString &cursorName) |
493 | -{ |
494 | - CursorImage *cursorImage = fetchCursor(themeName, cursorName); |
495 | - if (cursorImage) { |
496 | - return cursorImage->hotspot; |
497 | - } else { |
498 | - return QPoint(0,0); |
499 | - } |
500 | -} |
501 | - |
502 | CursorImage *CursorImageProvider::fetchCursor(const QString &cursorThemeAndName) |
503 | { |
504 | QString themeName; |
505 | |
506 | === modified file 'plugins/Cursor/CursorImageProvider.h' |
507 | --- plugins/Cursor/CursorImageProvider.h 2015-11-16 14:27:31 +0000 |
508 | +++ plugins/Cursor/CursorImageProvider.h 2016-05-19 19:45:00 +0000 |
509 | @@ -31,15 +31,23 @@ |
510 | virtual ~CursorImage() {} |
511 | |
512 | QImage qimage; |
513 | + |
514 | + // TODO: consider if there's a need to animate the hotspot |
515 | + // ie, if there's a need to make it an array of points, one for each frame. |
516 | + // Maybe no single xcursor (or at least the ones we know of or use) |
517 | + // vary its hotspot position through its animation. |
518 | QPoint hotspot; |
519 | + |
520 | + int frameWidth{0}; |
521 | + int frameHeight{0}; |
522 | + int frameCount{1}; |
523 | + int frameDuration{40}; |
524 | }; |
525 | |
526 | class XCursorImage : public CursorImage { |
527 | public: |
528 | XCursorImage(const QString &theme, const QString &file); |
529 | virtual ~XCursorImage(); |
530 | - |
531 | - XcursorImages *xcursorImages; |
532 | }; |
533 | |
534 | class BuiltInCursorImage : public CursorImage { |
535 | @@ -68,16 +76,16 @@ |
536 | |
537 | QImage requestImage(const QString &cursorName, QSize *size, const QSize &requestedSize) override; |
538 | |
539 | - QPoint hotspot(const QString &themeName, const QString &cursorName); |
540 | + CursorImage *fetchCursor(const QString &themeName, const QString &cursorName); |
541 | |
542 | void setCustomCursor(const QCursor &customCursor); |
543 | |
544 | private: |
545 | CursorImage *fetchCursor(const QString &cursorThemeAndName); |
546 | - CursorImage *fetchCursor(const QString &themeName, const QString &cursorName); |
547 | CursorImage *fetchCursorHelper(const QString &themeName, const QString &cursorName); |
548 | |
549 | // themeName -> (cursorName -> cursorImage) |
550 | + // TODO: discard old, unused, cursors |
551 | QMap<QString, QMap<QString, CursorImage*> > m_cursors; |
552 | |
553 | QScopedPointer<CursorImage> m_builtInCursorImage; |
554 | |
555 | === modified file 'plugins/Cursor/MousePointer.cpp' |
556 | --- plugins/Cursor/MousePointer.cpp 2015-12-01 21:54:18 +0000 |
557 | +++ plugins/Cursor/MousePointer.cpp 2016-05-19 19:45:00 +0000 |
558 | @@ -29,10 +29,7 @@ |
559 | : MirMousePointerInterface(parent) |
560 | , m_cursorName(QStringLiteral("left_ptr")) |
561 | , m_themeName(QStringLiteral("default")) |
562 | - , m_hotspotX(0) |
563 | - , m_hotspotY(0) |
564 | { |
565 | - updateHotspot(); |
566 | } |
567 | |
568 | void MousePointer::handleMouseEvent(ulong timestamp, QPointF movement, Qt::MouseButtons buttons, |
569 | @@ -115,22 +112,6 @@ |
570 | if (cursorName != m_cursorName) { |
571 | m_cursorName = cursorName; |
572 | Q_EMIT cursorNameChanged(m_cursorName); |
573 | - updateHotspot(); |
574 | - } |
575 | -} |
576 | - |
577 | -void MousePointer::updateHotspot() |
578 | -{ |
579 | - QPoint newHotspot = CursorImageProvider::instance()->hotspot(m_themeName, m_cursorName); |
580 | - |
581 | - if (m_hotspotX != newHotspot.x()) { |
582 | - m_hotspotX = newHotspot.x(); |
583 | - Q_EMIT hotspotXChanged(m_hotspotX); |
584 | - } |
585 | - |
586 | - if (m_hotspotY != newHotspot.y()) { |
587 | - m_hotspotY = newHotspot.y(); |
588 | - Q_EMIT hotspotYChanged(m_hotspotY); |
589 | } |
590 | } |
591 | |
592 | |
593 | === modified file 'plugins/Cursor/MousePointer.h' |
594 | --- plugins/Cursor/MousePointer.h 2015-12-01 21:40:13 +0000 |
595 | +++ plugins/Cursor/MousePointer.h 2016-05-19 19:45:00 +0000 |
596 | @@ -35,9 +35,6 @@ |
597 | void setThemeName(const QString &themeName) override; |
598 | QString themeName() const override { return m_themeName; } |
599 | |
600 | - qreal hotspotX() const override { return m_hotspotX; } |
601 | - qreal hotspotY() const override { return m_hotspotY; } |
602 | - |
603 | void setCustomCursor(const QCursor &) override; |
604 | |
605 | public Q_SLOTS: |
606 | @@ -55,13 +52,10 @@ |
607 | |
608 | private: |
609 | void registerWindow(QWindow *window); |
610 | - void updateHotspot(); |
611 | |
612 | QPointer<QWindow> m_registeredWindow; |
613 | QString m_cursorName; |
614 | QString m_themeName; |
615 | - int m_hotspotX; |
616 | - int m_hotspotY; |
617 | }; |
618 | |
619 | #endif // MOUSEPOINTER_H |
620 | |
621 | === modified file 'plugins/Cursor/plugin.cpp' |
622 | --- plugins/Cursor/plugin.cpp 2015-11-20 15:01:39 +0000 |
623 | +++ plugins/Cursor/plugin.cpp 2016-05-19 19:45:00 +0000 |
624 | @@ -1,5 +1,5 @@ |
625 | /* |
626 | - * Copyright (C) 2015 Canonical, Ltd. |
627 | + * Copyright (C) 2015-2016 Canonical, Ltd. |
628 | * |
629 | * This program is free software; you can redistribute it and/or modify |
630 | * it under the terms of the GNU General Public License as published by |
631 | @@ -22,13 +22,15 @@ |
632 | #include "plugin.h" |
633 | |
634 | // local |
635 | +#include "CursorImageInfo.h" |
636 | #include "CursorImageProvider.h" |
637 | #include "MousePointer.h" |
638 | |
639 | void CursorPlugin::registerTypes(const char *uri) |
640 | { |
641 | Q_ASSERT(uri == QLatin1String("Cursor")); |
642 | - qmlRegisterType<MousePointer>(uri, 1, 0, "MousePointer"); |
643 | + qmlRegisterType<CursorImageInfo>(uri, 1, 1, "CursorImageInfo"); |
644 | + qmlRegisterType<MousePointer>(uri, 1, 1, "MousePointer"); |
645 | } |
646 | |
647 | void CursorPlugin::initializeEngine(QQmlEngine *engine, const char *uri) |
648 | |
649 | === modified file 'plugins/Cursor/qmldir' |
650 | --- plugins/Cursor/qmldir 2015-09-29 13:45:05 +0000 |
651 | +++ plugins/Cursor/qmldir 2016-05-19 19:45:00 +0000 |
652 | @@ -1,3 +1,3 @@ |
653 | module Cursor |
654 | plugin Cursor-qml |
655 | -Cursor 1.0 Cursor.qml |
656 | +Cursor 1.1 Cursor.qml |
657 | |
658 | === modified file 'qml/Shell.qml' |
659 | --- qml/Shell.qml 2016-04-29 20:06:54 +0000 |
660 | +++ qml/Shell.qml 2016-05-19 19:45:00 +0000 |
661 | @@ -41,7 +41,7 @@ |
662 | import Unity.Session 0.1 |
663 | import Unity.DashCommunicator 0.1 |
664 | import Unity.Indicators 0.1 as Indicators |
665 | -import Cursor 1.0 |
666 | +import Cursor 1.1 |
667 | import WindowManager 0.1 |
668 | |
669 | |
670 | |
671 | === modified file 'tests/mocks/Cursor/qmldir' |
672 | --- tests/mocks/Cursor/qmldir 2015-09-29 13:45:05 +0000 |
673 | +++ tests/mocks/Cursor/qmldir 2016-05-19 19:45:00 +0000 |
674 | @@ -1,2 +1,2 @@ |
675 | module Cursor |
676 | -Cursor 1.0 Cursor.qml |
677 | +Cursor 1.1 Cursor.qml |
678 | |
679 | === modified file 'tests/plugins/CMakeLists.txt' |
680 | --- tests/plugins/CMakeLists.txt 2016-04-28 12:06:22 +0000 |
681 | +++ tests/plugins/CMakeLists.txt 2016-05-19 19:45:00 +0000 |
682 | @@ -1,8 +1,9 @@ |
683 | add_subdirectory(AccountsService) |
684 | +add_subdirectory(Cursor) |
685 | +add_subdirectory(Dash) |
686 | add_subdirectory(GlobalShortcut) |
687 | add_subdirectory(Greeter) |
688 | add_subdirectory(IntegratedLightDM) |
689 | -add_subdirectory(Dash) |
690 | add_subdirectory(Ubuntu) |
691 | add_subdirectory(Unity) |
692 | add_subdirectory(Utils) |
693 | |
694 | === added directory 'tests/plugins/Cursor' |
695 | === added file 'tests/plugins/Cursor/CMakeLists.txt' |
696 | --- tests/plugins/Cursor/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
697 | +++ tests/plugins/Cursor/CMakeLists.txt 2016-05-19 19:45:00 +0000 |
698 | @@ -0,0 +1,8 @@ |
699 | +include(QmlTest) |
700 | + |
701 | +set(UNITY_IMPORT_PATHS |
702 | + ${CMAKE_BINARY_DIR}/tests/utils/modules |
703 | + ${UNITY_PLUGINPATH} |
704 | +) |
705 | + |
706 | +add_manual_qml_test(. Cursor IMPORT_PATHS ${UNITY_IMPORT_PATHS}) |
707 | |
708 | === added file 'tests/plugins/Cursor/TextEntry.qml' |
709 | --- tests/plugins/Cursor/TextEntry.qml 1970-01-01 00:00:00 +0000 |
710 | +++ tests/plugins/Cursor/TextEntry.qml 2016-05-19 19:45:00 +0000 |
711 | @@ -0,0 +1,34 @@ |
712 | +/* |
713 | + * Copyright (C) 2016 Canonical, Ltd. |
714 | + * |
715 | + * This program is free software: you can redistribute it and/or modify it under |
716 | + * the terms of the GNU Lesser General Public License version 3, as published by |
717 | + * the Free Software Foundation. |
718 | + * |
719 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
720 | + * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
721 | + * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
722 | + * Lesser General Public License for more details. |
723 | + * |
724 | + * You should have received a copy of the GNU Lesser General Public License |
725 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
726 | + */ |
727 | + |
728 | +import QtQuick 2.4 |
729 | + |
730 | +Column { |
731 | + property alias name: entryText.text |
732 | + property alias value: textInput.text |
733 | + Text { id: entryText } |
734 | + Rectangle { |
735 | + color: "white" |
736 | + width: parent.width - 20 |
737 | + height: 40 |
738 | + TextInput { |
739 | + id: textInput |
740 | + anchors.fill: parent |
741 | + verticalAlignment: TextInput.AlignVCenter |
742 | + selectByMouse: true |
743 | + } |
744 | + } |
745 | +} |
746 | |
747 | === added file 'tests/plugins/Cursor/tst_Cursor.qml' |
748 | --- tests/plugins/Cursor/tst_Cursor.qml 1970-01-01 00:00:00 +0000 |
749 | +++ tests/plugins/Cursor/tst_Cursor.qml 2016-05-19 19:45:00 +0000 |
750 | @@ -0,0 +1,144 @@ |
751 | +/* |
752 | + * Copyright (C) 2016 Canonical, Ltd. |
753 | + * |
754 | + * This program is free software: you can redistribute it and/or modify it under |
755 | + * the terms of the GNU Lesser General Public License version 3, as published by |
756 | + * the Free Software Foundation. |
757 | + * |
758 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
759 | + * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
760 | + * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
761 | + * Lesser General Public License for more details. |
762 | + * |
763 | + * You should have received a copy of the GNU Lesser General Public License |
764 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
765 | + */ |
766 | + |
767 | + |
768 | +import QtQuick 2.4 |
769 | +import Cursor 1.1 |
770 | + |
771 | +Rectangle { |
772 | + id: root |
773 | + color: "blue" |
774 | + width: 400 |
775 | + height: 600 |
776 | + |
777 | + property string themeName: "default" |
778 | + property string cursorName: "left_ptr" |
779 | + |
780 | + CursorImageInfo { |
781 | + id: imageInfo |
782 | + themeName: root.themeName |
783 | + cursorName: root.cursorName |
784 | + } |
785 | + |
786 | + Item { |
787 | + id: cursor |
788 | + x: (200 - animatedSprite.width) / 2 |
789 | + y: (root.height - animatedSprite.height) / 2 |
790 | + |
791 | + AnimatedSprite { |
792 | + id: animatedSprite |
793 | + |
794 | + x: -imageInfo.hotspot.x |
795 | + y: -imageInfo.hotspot.y |
796 | + source: "image://cursor/" + root.themeName + "/" + root.cursorName |
797 | + |
798 | + interpolate: false |
799 | + |
800 | + width: imageInfo.frameWidth |
801 | + height: imageInfo.frameHeight |
802 | + |
803 | + frameCount: imageInfo.frameCount |
804 | + frameDuration: imageInfo.frameDuration |
805 | + frameWidth: imageInfo.frameWidth |
806 | + frameHeight: imageInfo.frameHeight |
807 | + } |
808 | + } |
809 | + |
810 | + Rectangle { |
811 | + id: hotspotCrossH |
812 | + color: "red" |
813 | + width: 200 |
814 | + height: 1 |
815 | + anchors.top: cursor.top |
816 | + opacity: 0.6 |
817 | + } |
818 | + Rectangle { |
819 | + id: hotspotCrossV |
820 | + color: "red" |
821 | + width: 1 |
822 | + anchors.left: cursor.left |
823 | + anchors.top: parent.top |
824 | + anchors.bottom: parent.bottom |
825 | + opacity: 0.6 |
826 | + } |
827 | + MouseArea { |
828 | + anchors.top: parent.top |
829 | + anchors.bottom: parent.bottom |
830 | + anchors.left: parent.left |
831 | + anchors.right: controls.left |
832 | + onClicked: { |
833 | + if (hotspotCrossH.visible) { |
834 | + hotspotCrossH.visible = false; |
835 | + hotspotCrossV.visible = false; |
836 | + } else { |
837 | + hotspotCrossH.visible = true; |
838 | + hotspotCrossV.visible = true; |
839 | + } |
840 | + } |
841 | + } |
842 | + |
843 | + Rectangle { |
844 | + id: controls |
845 | + color: "lightgrey" |
846 | + anchors.right: parent.right |
847 | + anchors.top: parent.top |
848 | + anchors.bottom: parent.bottom |
849 | + width: root.width - 200 |
850 | + |
851 | + Column { |
852 | + anchors.fill: parent |
853 | + anchors.margins: 10 |
854 | + |
855 | + TextEntry { id: themeNameEntry; name: "themeName"; value: "default" } |
856 | + |
857 | + Item {width: 10; height: 20} |
858 | + |
859 | + TextEntry { id: cursorNameEntry; name: "cursorName"; value: "left_ptr" } |
860 | + |
861 | + Item {width: 10; height: 40} |
862 | + |
863 | + Rectangle { |
864 | + color: applyMouseArea.pressed ? "green" : "lightslategray" |
865 | + width: parent.width - 20 |
866 | + height: 40 |
867 | + Text { anchors.centerIn: parent; text: "Apply" } |
868 | + MouseArea { |
869 | + id: applyMouseArea |
870 | + anchors.fill: parent |
871 | + onClicked: { |
872 | + root.themeName = themeNameEntry.value; |
873 | + root.cursorName = cursorNameEntry.value; |
874 | + } |
875 | + } |
876 | + } |
877 | + |
878 | + Item {width: 10; height: 10} |
879 | + Rectangle { |
880 | + color: "black" |
881 | + height: 2 |
882 | + anchors.left: parent.left |
883 | + anchors.right: parent.right |
884 | + } |
885 | + Item {width: 10; height: 10} |
886 | + |
887 | + Text { text: "frameWidth: " + imageInfo.frameWidth } |
888 | + Text { text: "frameHeight: " + imageInfo.frameHeight } |
889 | + Text { text: "frameCount: " + imageInfo.frameCount } |
890 | + Text { text: "frameDuration: " + imageInfo.frameDuration } |
891 | + Text { text: "currentFrame: " + animatedSprite.currentFrame } |
892 | + } |
893 | + } |
894 | +} |
FAILED: Continuous integration, rev:2374 /unity8- jenkins. ubuntu. com/job/ lp-unity8- ci/1113/ /unity8- jenkins. ubuntu. com/job/ build-0- fetch/1491 /unity8- jenkins. ubuntu. com/job/ build-1- sourcepkg/ release= vivid+overlay/ 1456 /unity8- jenkins. ubuntu. com/job/ build-1- sourcepkg/ release= xenial+ overlay/ 1456 /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=amd64, release= vivid+overlay/ 1456/console /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=amd64, release= xenial+ overlay/ 1456/console /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=armhf, release= vivid+overlay/ 1456/console /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=armhf, release= xenial+ overlay/ 1456/console /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=i386, release= vivid+overlay/ 1456/console /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=i386, release= xenial+ overlay/ 1456/console
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild: /unity8- jenkins. ubuntu. com/job/ lp-unity8- ci/1113/ rebuild
https:/