on xenial help sign in
Support animated cursors
commit81b01b164e77dff0d62d7fa3d24fbd0adb3f2435 authorDaniel d'Andrada <daniel.dandrada@canonical.com> dateThu 19 May 2016, 16:44 (10 years ago) parent5824fd2
19 files changed+465−143
CMakeLists.txt+1−1 debian/control+2−2 plugins/Cursor/CMakeLists.txt+2−1 plugins/Cursor/Cursor.qml+21−5 plugins/Cursor/Cursor.qmltypes−78 plugins/Cursor/CursorImageInfo.cpp+106 plugins/Cursor/CursorImageInfo.h+76 plugins/Cursor/CursorImageProvider.cpp+50−21 plugins/Cursor/CursorImageProvider.h+12−4 plugins/Cursor/MousePointer.cpp−19 plugins/Cursor/MousePointer.h−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 tests/plugins/Cursor/TextEntry.qml+34 tests/plugins/Cursor/tst_Cursor.qml+144
CMakeLists.txt+1−1
@@ -57,7 +57,7 @@ find_package(Qt5DBus 5.4 REQUIRED)
5757find_package(Qt5Concurrent 5.4 REQUIRED)
5858find_package(Qt5Sql 5.4 REQUIRED)
5959
60−pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=16)
60+pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=17)
6161pkg_check_modules(GEONAMES REQUIRED geonames>=0.2)
6262pkg_check_modules(GIO REQUIRED gio-2.0>=2.32)
6363pkg_check_modules(GLIB REQUIRED glib-2.0>=2.32)
debian/control+2−2
@@ -30,7 +30,7 @@ Build-Depends: cmake,
3030 libqt5xmlpatterns5-dev,
3131 libsystemsettings-dev,
3232 libudev-dev,
33− libunity-api-dev (>= 7.112),
33+ libunity-api-dev (>= 7.113),
3434 libusermetricsoutput1-dev,
3535# Need those X11 libs touch emulation from mouse events in manual QML tests on a X11 desktop
3636 libx11-dev[!armhf],
@@ -133,7 +133,7 @@ Depends: qml-module-qtquick-layouts,
133133 qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 1.3.1845) | qtdeclarative5-ubuntu-ui-toolkit-plugin-gles (>= 1.3.1845),
134134 qtdeclarative5-unity-notifications-plugin (>= 0.1.2) | unity-notifications-impl,
135135 ubuntu-thumbnailer-impl-0,
136− unity-application-impl-16,
136+ unity-application-impl-17,
137137 unity-notifications-impl-3,
138138 unity-plugin-scopes | unity-scopes-impl,
139139 unity-scopes-impl-12,
plugins/Cursor/CMakeLists.txt+2−1
@@ -8,8 +8,9 @@ include_directories(
88
99set(QMLPLUGIN_SRC
1010 plugin.cpp
11− MousePointer.cpp
11+ CursorImageInfo.cpp
1212 CursorImageProvider.cpp
13+ MousePointer.cpp
1314 # We need to run moc on this header
1415 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirMousePointerInterface.h
1516 )
plugins/Cursor/Cursor.qml+21−5
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2015 Canonical, Ltd.
2+ * Copyright (C) 2015-2016 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -15,14 +15,30 @@
1515 */
1616
1717import QtQuick 2.4
18−import Cursor 1.0 // For MousePointer
18+import Cursor 1.1
1919
2020MousePointer {
2121 id: mousePointer
2222
23− Image {
24− x: -mousePointer.hotspotX
25− y: -mousePointer.hotspotY
23+ CursorImageInfo {
24+ id: imageInfo
25+ themeName: mousePointer.themeName
26+ cursorName: mousePointer.cursorName
27+ }
28+
29+ AnimatedSprite {
30+ x: -imageInfo.hotspot.x
31+ y: -imageInfo.hotspot.y
2632 source: "image://cursor/" + mousePointer.themeName + "/" + mousePointer.cursorName
33+
34+ interpolate: false
35+
36+ width: imageInfo.frameWidth
37+ height: imageInfo.frameHeight
38+
39+ frameCount: imageInfo.frameCount
40+ frameDuration: imageInfo.frameDuration
41+ frameWidth: imageInfo.frameWidth
42+ frameHeight: imageInfo.frameHeight
2743 }
2844}
plugins/Cursor/Cursor.qmltypesdeleted−78
@@ -1,78 +0,0 @@
1−import QtQuick.tooling 1.1
2−
3−// This file describes the plugin-supplied types contained in the library.
4−// It is used for QML tooling purposes only.
5−//
6−// This file was auto-generated by:
7−// 'qmlplugindump -notrelocatable Cursor 1.0 plugins'
8−
9−Module {
10− Component {
11− name: "MirMousePointerInterface"
12− defaultProperty: "data"
13− prototype: "QQuickItem"
14− Property { name: "cursorName"; type: "string"; isReadonly: true }
15− Property { name: "themeName"; type: "string"; isReadonly: true }
16− Property { name: "hotspotX"; type: "double"; isReadonly: true }
17− Property { name: "hotspotY"; type: "double"; isReadonly: true }
18− Signal {
19− name: "cursorNameChanged"
20− Parameter { name: "name"; type: "string" }
21− }
22− Signal {
23− name: "themeNameChanged"
24− Parameter { name: "name"; type: "string" }
25− }
26− Signal {
27− name: "hotspotXChanged"
28− Parameter { name: "value"; type: "double" }
29− }
30− Signal {
31− name: "hotspotYChanged"
32− Parameter { name: "value"; type: "double" }
33− }
34− Method {
35− name: "handleMouseEvent"
36− Parameter { name: "timestamp"; type: "ulong" }
37− Parameter { name: "movement"; type: "QPointF" }
38− Parameter { name: "buttons"; type: "Qt::MouseButtons" }
39− Parameter { name: "modifiers"; type: "Qt::KeyboardModifiers" }
40− }
41− Method {
42− name: "handleWheelEvent"
43− Parameter { name: "timestamp"; type: "ulong" }
44− Parameter { name: "angleDelta"; type: "QPoint" }
45− Parameter { name: "modifiers"; type: "Qt::KeyboardModifiers" }
46− }
47− }
48− Component {
49− name: "MousePointer"
50− defaultProperty: "data"
51− prototype: "MirMousePointerInterface"
52− exports: ["Cursor/MousePointer 1.0"]
53− exportMetaObjectRevisions: [0]
54− Signal {
55− name: "pushedLeftBoundary"
56− Parameter { name: "amount"; type: "double" }
57− Parameter { name: "buttons"; type: "Qt::MouseButtons" }
58− }
59− Signal {
60− name: "pushedRightBoundary"
61− Parameter { name: "amount"; type: "double" }
62− Parameter { name: "buttons"; type: "Qt::MouseButtons" }
63− }
64− Method {
65− name: "handleMouseEvent"
66− Parameter { name: "timestamp"; type: "ulong" }
67− Parameter { name: "movement"; type: "QPointF" }
68− Parameter { name: "buttons"; type: "Qt::MouseButtons" }
69− Parameter { name: "modifiers"; type: "Qt::KeyboardModifiers" }
70− }
71− Method {
72− name: "handleWheelEvent"
73− Parameter { name: "timestamp"; type: "ulong" }
74− Parameter { name: "angleDelta"; type: "QPoint" }
75− Parameter { name: "modifiers"; type: "Qt::KeyboardModifiers" }
76− }
77− }
78−}
plugins/Cursor/CursorImageInfo.cppnew file+106
@@ -0,0 +1,106 @@
1+/*
2+ * Copyright (C) 2016 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 "CursorImageInfo.h"
18+
19+CursorImageInfo::CursorImageInfo(QObject *parent)
20+ : QObject(parent)
21+{
22+ m_updateTimer.setInterval(0);
23+ m_updateTimer.setSingleShot(true);
24+ connect(&m_updateTimer, &QTimer::timeout, this, &CursorImageInfo::update);
25+}
26+
27+void CursorImageInfo::setCursorName(const QString &cursorName)
28+{
29+ if (cursorName != m_cursorName) {
30+ m_cursorName = cursorName;
31+ Q_EMIT cursorNameChanged();
32+ scheduleUpdate();
33+ }
34+}
35+
36+void CursorImageInfo::setThemeName(const QString &themeName)
37+{
38+ if (m_themeName != themeName) {
39+ m_themeName = themeName;
40+ Q_EMIT themeNameChanged();
41+ scheduleUpdate();
42+ }
43+}
44+
45+void CursorImageInfo::scheduleUpdate()
46+{
47+ if (!m_updateTimer.isActive()) {
48+ m_updateTimer.start();
49+ }
50+}
51+
52+void CursorImageInfo::update()
53+{
54+ m_cursorImage = CursorImageProvider::instance()->fetchCursor(m_themeName, m_cursorName);
55+
56+ Q_EMIT hotspotChanged();
57+ Q_EMIT frameWidthChanged();
58+ Q_EMIT frameHeightChanged();
59+ Q_EMIT frameCountChanged();
60+ Q_EMIT frameDurationChanged();
61+}
62+
63+QPoint CursorImageInfo::hotspot() const
64+{
65+ if (m_cursorImage) {
66+ return m_cursorImage->hotspot;
67+ } else {
68+ return QPoint();
69+ }
70+}
71+
72+qreal CursorImageInfo::frameWidth() const
73+{
74+ if (m_cursorImage) {
75+ return m_cursorImage->frameWidth;
76+ } else {
77+ return 0;
78+ }
79+}
80+
81+qreal CursorImageInfo::frameHeight() const
82+{
83+ if (m_cursorImage) {
84+ return m_cursorImage->frameHeight;
85+ } else {
86+ return 0;
87+ }
88+}
89+
90+int CursorImageInfo::frameCount() const
91+{
92+ if (m_cursorImage) {
93+ return m_cursorImage->frameCount;
94+ } else {
95+ return 0;
96+ }
97+}
98+
99+int CursorImageInfo::frameDuration() const
100+{
101+ if (m_cursorImage) {
102+ return m_cursorImage->frameDuration;
103+ } else {
104+ return 0;
105+ }
106+}
plugins/Cursor/CursorImageInfo.hnew file+76
@@ -0,0 +1,76 @@
1+/*
2+ * Copyright (C) 2016 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 CURSOR_IMAGE_INFO_H
18+#define CURSOR_IMAGE_INFO_H
19+
20+#include "CursorImageProvider.h"
21+
22+#include <QObject>
23+#include <QString>
24+#include <QTimer>
25+
26+class CursorImageInfo : public QObject
27+{
28+ Q_OBJECT
29+
30+ Q_PROPERTY(QString themeName READ themeName WRITE setThemeName NOTIFY themeNameChanged)
31+ Q_PROPERTY(QString cursorName READ cursorName WRITE setCursorName NOTIFY cursorNameChanged)
32+
33+ Q_PROPERTY(QPoint hotspot READ hotspot NOTIFY hotspotChanged)
34+ Q_PROPERTY(qreal frameWidth READ frameWidth NOTIFY frameWidthChanged)
35+ Q_PROPERTY(qreal frameHeight READ frameHeight NOTIFY frameHeightChanged)
36+ Q_PROPERTY(int frameCount READ frameCount NOTIFY frameCountChanged)
37+ Q_PROPERTY(int frameDuration READ frameDuration NOTIFY frameDurationChanged)
38+
39+public:
40+ CursorImageInfo(QObject *parent = nullptr);
41+
42+ QString themeName() const { return m_themeName; }
43+ void setThemeName(const QString &);
44+
45+ QString cursorName() const { return m_cursorName; }
46+ void setCursorName(const QString &);
47+
48+ QPoint hotspot() const;
49+ qreal frameWidth() const;
50+ qreal frameHeight() const;
51+ int frameCount() const;
52+ int frameDuration() const;
53+
54+Q_SIGNALS:
55+ void themeNameChanged();
56+ void cursorNameChanged();
57+ void hotspotChanged();
58+ void frameWidthChanged();
59+ void frameHeightChanged();
60+ void frameCountChanged();
61+ void frameDurationChanged();
62+
63+private Q_SLOTS:
64+ void update();
65+
66+private:
67+ void scheduleUpdate();
68+ QTimer m_updateTimer;
69+
70+ QString m_themeName;
71+ QString m_cursorName;
72+
73+ CursorImage *m_cursorImage{nullptr};
74+};
75+
76+#endif // CURSOR_IMAGE_INFO_H
plugins/Cursor/CursorImageProvider.cpp+50−21
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2015 Canonical, Ltd.
2+ * Copyright (C) 2015-2016 Canonical, Ltd.
33 *
44 * This program is free software: you can redistribute it and/or modify it under
55 * the terms of the GNU Lesser General Public License version 3, as published by
@@ -46,6 +46,9 @@ BuiltInCursorImage::BuiltInCursorImage()
4646 qimage.fill(Qt::transparent);
4747 QPainter imagePainter(&qimage);
4848
49+ frameWidth = qimage.width();
50+ frameHeight = qimage.height();
51+
4952 QSvgRenderer *svgRenderer = new QSvgRenderer(QByteArray(svgString));
5053 svgRenderer->render(&imagePainter);
5154 delete svgRenderer;
@@ -59,6 +62,8 @@ BlankCursorImage::BlankCursorImage()
5962{
6063 qimage = QImage(1, 1, QImage::Format_ARGB32);
6164 qimage.fill(Qt::transparent);
65+ frameWidth = qimage.width();
66+ frameHeight = qimage.height();
6267}
6368
6469/////
@@ -69,40 +74,74 @@ CustomCursorImage::CustomCursorImage(const QCursor &cursor)
6974{
7075 qimage = cursor.pixmap().toImage();
7176 hotspot = cursor.hotSpot();
77+ frameWidth = qimage.width();
78+ frameHeight = qimage.height();
7279}
7380
7481/////
7582// XCursorImage
7683
7784XCursorImage::XCursorImage(const QString &theme, const QString &file)
78− : xcursorImages(nullptr)
7985{
8086 // TODO: Consider grid unit value
8187 // Hardcoding to a medium size for now
8288 int preferredCursorHeightPx = 32;
8389
84− xcursorImages = XcursorLibraryLoadImages(QFile::encodeName(file), QFile::encodeName(theme),
90+ XcursorImages *xcursorImages = XcursorLibraryLoadImages(QFile::encodeName(file), QFile::encodeName(theme),
8591 preferredCursorHeightPx);
86− if (!xcursorImages) {
92+ if (!xcursorImages || xcursorImages->nimage == 0) {
8793 return;
8894 }
8995
90− // Just take the first one. It will have multiple images in case of an animated cursor.
91− // TODO: Support animated cursors
92− if ( xcursorImages->nimage > 0) {
93− XcursorImage *xcursorImage = xcursorImages->images[0];
96+ frameCount = xcursorImages->nimage;
9497
95− qimage = QImage((uchar*)xcursorImage->pixels,
96− xcursorImage->width, xcursorImage->height, QImage::Format_ARGB32);
98+ for (int i = 0; i < xcursorImages->nimage; ++i) {
99+ XcursorImage *xcursorImage = xcursorImages->images[0];
100+ if (frameWidth < (int)xcursorImage->width) {
101+ frameWidth = xcursorImage->width;
102+ }
103+ if (frameHeight < (int)xcursorImage->height) {
104+ frameHeight = xcursorImage->height;
105+ }
106+ if (i == 0) {
107+ frameDuration = (int)xcursorImage->delay;
108+ } else {
109+ if (frameDuration != (int)xcursorImage->delay) {
110+ qWarning().nospace() << "CursorImageProvider: XCursorImage("<<theme<<","<<file<<") has"
111+ " varying delays in its animation. Animation won't look right.";
112+ }
113+ }
114+ }
97115
116+ {
117+ // Assume that the hotspot position does not animate
118+ XcursorImage *xcursorImage = xcursorImages->images[0];
98119 hotspot.setX(xcursorImage->xhot);
99120 hotspot.setY(xcursorImage->yhot);
100121 }
122+
123+ // Build the sprite as a single row of frames
124+ qimage = QImage(frameWidth*frameCount, frameHeight, QImage::Format_ARGB32);
125+ qimage.fill(Qt::transparent);
126+
127+ {
128+ QPainter painter(&qimage);
129+
130+ for (int i = 0; i < xcursorImages->nimage; ++i) {
131+ XcursorImage *xcursorImage = xcursorImages->images[i];
132+
133+ auto frameImage = QImage((uchar*)xcursorImage->pixels,
134+ xcursorImage->width, xcursorImage->height, QImage::Format_ARGB32);
135+
136+ painter.drawImage(QPoint(i*frameWidth, 0), frameImage);
137+ }
138+ }
139+
140+ XcursorImagesDestroy(xcursorImages);
101141}
102142
103143XCursorImage::~XCursorImage()
104144{
105− XcursorImagesDestroy(xcursorImages);
106145}
107146
108147/////
@@ -205,16 +244,6 @@ QImage CursorImageProvider::requestImage(const QString &cursorThemeAndName, QSiz
205244 return cursorImage->qimage;
206245}
207246
208−QPoint CursorImageProvider::hotspot(const QString &themeName, const QString &cursorName)
209−{
210− CursorImage *cursorImage = fetchCursor(themeName, cursorName);
211− if (cursorImage) {
212− return cursorImage->hotspot;
213− } else {
214− return QPoint(0,0);
215− }
216−}
217−
218247CursorImage *CursorImageProvider::fetchCursor(const QString &cursorThemeAndName)
219248{
220249 QString themeName;
plugins/Cursor/CursorImageProvider.h+12−4
@@ -31,15 +31,23 @@ public:
3131 virtual ~CursorImage() {}
3232
3333 QImage qimage;
34+
35+ // TODO: consider if there's a need to animate the hotspot
36+ // ie, if there's a need to make it an array of points, one for each frame.
37+ // Maybe no single xcursor (or at least the ones we know of or use)
38+ // vary its hotspot position through its animation.
3439 QPoint hotspot;
40+
41+ int frameWidth{0};
42+ int frameHeight{0};
43+ int frameCount{1};
44+ int frameDuration{40};
3545};
3646
3747class XCursorImage : public CursorImage {
3848public:
3949 XCursorImage(const QString &theme, const QString &file);
4050 virtual ~XCursorImage();
41−
42− XcursorImages *xcursorImages;
4351};
4452
4553class BuiltInCursorImage : public CursorImage {
@@ -68,16 +76,16 @@ public:
6876
6977 QImage requestImage(const QString &cursorName, QSize *size, const QSize &requestedSize) override;
7078
71− QPoint hotspot(const QString &themeName, const QString &cursorName);
79+ CursorImage *fetchCursor(const QString &themeName, const QString &cursorName);
7280
7381 void setCustomCursor(const QCursor &customCursor);
7482
7583private:
7684 CursorImage *fetchCursor(const QString &cursorThemeAndName);
77− CursorImage *fetchCursor(const QString &themeName, const QString &cursorName);
7885 CursorImage *fetchCursorHelper(const QString &themeName, const QString &cursorName);
7986
8087 // themeName -> (cursorName -> cursorImage)
88+ // TODO: discard old, unused, cursors
8189 QMap<QString, QMap<QString, CursorImage*> > m_cursors;
8290
8391 QScopedPointer<CursorImage> m_builtInCursorImage;
plugins/Cursor/MousePointer.cpp−19
@@ -29,10 +29,7 @@ MousePointer::MousePointer(QQuickItem *parent)
2929 : MirMousePointerInterface(parent)
3030 , m_cursorName(QStringLiteral("left_ptr"))
3131 , m_themeName(QStringLiteral("default"))
32− , m_hotspotX(0)
33− , m_hotspotY(0)
3432{
35− updateHotspot();
3633}
3734
3835void MousePointer::handleMouseEvent(ulong timestamp, QPointF movement, Qt::MouseButtons buttons,
@@ -115,22 +112,6 @@ void MousePointer::setCursorName(const QString &cursorName)
115112 if (cursorName != m_cursorName) {
116113 m_cursorName = cursorName;
117114 Q_EMIT cursorNameChanged(m_cursorName);
118− updateHotspot();
119− }
120−}
121−
122−void MousePointer::updateHotspot()
123−{
124− QPoint newHotspot = CursorImageProvider::instance()->hotspot(m_themeName, m_cursorName);
125−
126− if (m_hotspotX != newHotspot.x()) {
127− m_hotspotX = newHotspot.x();
128− Q_EMIT hotspotXChanged(m_hotspotX);
129− }
130−
131− if (m_hotspotY != newHotspot.y()) {
132− m_hotspotY = newHotspot.y();
133− Q_EMIT hotspotYChanged(m_hotspotY);
134115 }
135116}
136117
plugins/Cursor/MousePointer.h−6
@@ -35,9 +35,6 @@ public:
3535 void setThemeName(const QString &themeName) override;
3636 QString themeName() const override { return m_themeName; }
3737
38− qreal hotspotX() const override { return m_hotspotX; }
39− qreal hotspotY() const override { return m_hotspotY; }
40−
4138 void setCustomCursor(const QCursor &) override;
4239
4340public Q_SLOTS:
@@ -55,13 +52,10 @@ protected:
5552
5653private:
5754 void registerWindow(QWindow *window);
58− void updateHotspot();
5955
6056 QPointer<QWindow> m_registeredWindow;
6157 QString m_cursorName;
6258 QString m_themeName;
63− int m_hotspotX;
64− int m_hotspotY;
6559};
6660
6761#endif // MOUSEPOINTER_H
plugins/Cursor/plugin.cpp+4−2
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2015 Canonical, Ltd.
2+ * Copyright (C) 2015-2016 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -22,13 +22,15 @@
2222#include "plugin.h"
2323
2424// local
25+#include "CursorImageInfo.h"
2526#include "CursorImageProvider.h"
2627#include "MousePointer.h"
2728
2829void CursorPlugin::registerTypes(const char *uri)
2930{
3031 Q_ASSERT(uri == QLatin1String("Cursor"));
31− qmlRegisterType<MousePointer>(uri, 1, 0, "MousePointer");
32+ qmlRegisterType<CursorImageInfo>(uri, 1, 1, "CursorImageInfo");
33+ qmlRegisterType<MousePointer>(uri, 1, 1, "MousePointer");
3234}
3335
3436void CursorPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
plugins/Cursor/qmldir+1−1
@@ -1,3 +1,3 @@
11module Cursor
22plugin Cursor-qml
3−Cursor 1.0 Cursor.qml
3+Cursor 1.1 Cursor.qml
qml/Shell.qml+1−1
@@ -41,7 +41,7 @@ import Unity.Notifications 1.0 as NotificationBackend
4141import Unity.Session 0.1
4242import Unity.DashCommunicator 0.1
4343import Unity.Indicators 0.1 as Indicators
44−import Cursor 1.0
44+import Cursor 1.1
4545import WindowManager 0.1
4646
4747
tests/mocks/Cursor/qmldir+1−1
@@ -1,2 +1,2 @@
11module Cursor
2−Cursor 1.0 Cursor.qml
2+Cursor 1.1 Cursor.qml
tests/plugins/CMakeLists.txt+2−1
@@ -1,8 +1,9 @@
11add_subdirectory(AccountsService)
2+add_subdirectory(Cursor)
3+add_subdirectory(Dash)
24add_subdirectory(GlobalShortcut)
35add_subdirectory(Greeter)
46add_subdirectory(IntegratedLightDM)
5−add_subdirectory(Dash)
67add_subdirectory(Ubuntu)
78add_subdirectory(Unity)
89add_subdirectory(Utils)
tests/plugins/Cursor/CMakeLists.txtnew file+8
@@ -0,0 +1,8 @@
1+include(QmlTest)
2+
3+set(UNITY_IMPORT_PATHS
4+ ${CMAKE_BINARY_DIR}/tests/utils/modules
5+ ${UNITY_PLUGINPATH}
6+)
7+
8+add_manual_qml_test(. Cursor IMPORT_PATHS ${UNITY_IMPORT_PATHS})
tests/plugins/Cursor/TextEntry.qmlnew file+34
@@ -0,0 +1,34 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it under
5+ * the terms of the GNU Lesser General Public License version 3, as published by
6+ * the Free Software Foundation.
7+ *
8+ * This program is distributed in the hope that it will be useful, but WITHOUT
9+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11+ * 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+import QtQuick 2.4
18+
19+Column {
20+ property alias name: entryText.text
21+ property alias value: textInput.text
22+ Text { id: entryText }
23+ Rectangle {
24+ color: "white"
25+ width: parent.width - 20
26+ height: 40
27+ TextInput {
28+ id: textInput
29+ anchors.fill: parent
30+ verticalAlignment: TextInput.AlignVCenter
31+ selectByMouse: true
32+ }
33+ }
34+}
tests/plugins/Cursor/tst_Cursor.qmlnew file+144
@@ -0,0 +1,144 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it under
5+ * the terms of the GNU Lesser General Public License version 3, as published by
6+ * the Free Software Foundation.
7+ *
8+ * This program is distributed in the hope that it will be useful, but WITHOUT
9+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11+ * 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+
18+import QtQuick 2.4
19+import Cursor 1.1
20+
21+Rectangle {
22+ id: root
23+ color: "blue"
24+ width: 400
25+ height: 600
26+
27+ property string themeName: "default"
28+ property string cursorName: "left_ptr"
29+
30+ CursorImageInfo {
31+ id: imageInfo
32+ themeName: root.themeName
33+ cursorName: root.cursorName
34+ }
35+
36+ Item {
37+ id: cursor
38+ x: (200 - animatedSprite.width) / 2
39+ y: (root.height - animatedSprite.height) / 2
40+
41+ AnimatedSprite {
42+ id: animatedSprite
43+
44+ x: -imageInfo.hotspot.x
45+ y: -imageInfo.hotspot.y
46+ source: "image://cursor/" + root.themeName + "/" + root.cursorName
47+
48+ interpolate: false
49+
50+ width: imageInfo.frameWidth
51+ height: imageInfo.frameHeight
52+
53+ frameCount: imageInfo.frameCount
54+ frameDuration: imageInfo.frameDuration
55+ frameWidth: imageInfo.frameWidth
56+ frameHeight: imageInfo.frameHeight
57+ }
58+ }
59+
60+ Rectangle {
61+ id: hotspotCrossH
62+ color: "red"
63+ width: 200
64+ height: 1
65+ anchors.top: cursor.top
66+ opacity: 0.6
67+ }
68+ Rectangle {
69+ id: hotspotCrossV
70+ color: "red"
71+ width: 1
72+ anchors.left: cursor.left
73+ anchors.top: parent.top
74+ anchors.bottom: parent.bottom
75+ opacity: 0.6
76+ }
77+ MouseArea {
78+ anchors.top: parent.top
79+ anchors.bottom: parent.bottom
80+ anchors.left: parent.left
81+ anchors.right: controls.left
82+ onClicked: {
83+ if (hotspotCrossH.visible) {
84+ hotspotCrossH.visible = false;
85+ hotspotCrossV.visible = false;
86+ } else {
87+ hotspotCrossH.visible = true;
88+ hotspotCrossV.visible = true;
89+ }
90+ }
91+ }
92+
93+ Rectangle {
94+ id: controls
95+ color: "lightgrey"
96+ anchors.right: parent.right
97+ anchors.top: parent.top
98+ anchors.bottom: parent.bottom
99+ width: root.width - 200
100+
101+ Column {
102+ anchors.fill: parent
103+ anchors.margins: 10
104+
105+ TextEntry { id: themeNameEntry; name: "themeName"; value: "default" }
106+
107+ Item {width: 10; height: 20}
108+
109+ TextEntry { id: cursorNameEntry; name: "cursorName"; value: "left_ptr" }
110+
111+ Item {width: 10; height: 40}
112+
113+ Rectangle {
114+ color: applyMouseArea.pressed ? "green" : "lightslategray"
115+ width: parent.width - 20
116+ height: 40
117+ Text { anchors.centerIn: parent; text: "Apply" }
118+ MouseArea {
119+ id: applyMouseArea
120+ anchors.fill: parent
121+ onClicked: {
122+ root.themeName = themeNameEntry.value;
123+ root.cursorName = cursorNameEntry.value;
124+ }
125+ }
126+ }
127+
128+ Item {width: 10; height: 10}
129+ Rectangle {
130+ color: "black"
131+ height: 2
132+ anchors.left: parent.left
133+ anchors.right: parent.right
134+ }
135+ Item {width: 10; height: 10}
136+
137+ Text { text: "frameWidth: " + imageInfo.frameWidth }
138+ Text { text: "frameHeight: " + imageInfo.frameHeight }
139+ Text { text: "frameCount: " + imageInfo.frameCount }
140+ Text { text: "frameDuration: " + imageInfo.frameDuration }
141+ Text { text: "currentFrame: " + animatedSprite.currentFrame }
142+ }
143+ }
144+}