Merge lp:~dandrader/unity8/animatedCursors into lp:unity8

Proposed by Daniel d'Andrada
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
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://code.launchpad.net/~dandrader/unity-api/removeHotspot/+merge/293627
https://code.launchpad.net/~dandrader/qtmir/removeHotspot/+merge/293629

* 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/CustomCursor
- qmake && make
- cd ..
- qmlscene CursorShapes.qml -I . --desktop_file_hint=unity8
- 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

To post a comment you must log in.
Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote :
review: Needs Fixing (continuous-integration)
lp:~dandrader/unity8/animatedCursors updated
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

Revision history for this message
Albert Astals Cid (aacid) wrote :

Text conflict in tests/plugins/CMakeLists.txt
1 conflicts encountered.

Revision history for this message
Daniel d'Andrada (dandrader) wrote :

On 05/05/2016 04:29, Albert Astals Cid wrote:
> Text conflict in tests/plugins/CMakeLists.txt
> 1 conflicts encountered.
Fixed, thanks.

Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote :
review: Needs Fixing (continuous-integration)
lp:~dandrader/unity8/animatedCursors updated
2393. By CI Train Bot Account

Resync trunk.

2394. By Launchpad Translations on behalf of unity-team

Launchpad automatic translations update.

Revision history for this message
Lukáš Tinkl (lukas-kde) wrote :

Please regenerate the Cursor.qmltypes file, it still contains the hotspot stuff

review: Needs Fixing
Revision history for this message
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.

review: Needs Fixing
Revision history for this message
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.

Revision history for this message
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.

Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
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

review: Approve
Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote :
review: Needs Fixing (continuous-integration)
lp:~dandrader/unity8/animatedCursors updated
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

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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+}

Subscribers

People subscribed via source and target branches