on xenial help sign in
Cursor: make its size grid unit based
280e594Daniel d'Andrada
plugins/Cursor/Cursor.qml+5−2
@@ -25,6 +25,7 @@ MousePointer {
25 id: imageInfo
25 id: imageInfo
26 themeName: mousePointer.themeName
26 themeName: mousePointer.themeName
27 cursorName: mousePointer.cursorName
27 cursorName: mousePointer.cursorName
28+ cursorHeight: mousePointer.height
28 }
29 }
29
30
30 Loader {
31 Loader {
@@ -32,7 +33,7 @@ MousePointer {
32 sourceComponent: AnimatedSprite {
33 sourceComponent: AnimatedSprite {
33 x: -imageInfo.hotspot.x
34 x: -imageInfo.hotspot.x
34 y: -imageInfo.hotspot.y
35 y: -imageInfo.hotspot.y
35− source: "image://cursor/" + mousePointer.themeName + "/" + mousePointer.cursorName
36+ source: imageInfo.imageSource
36
37
37 interpolate: false
38 interpolate: false
38
39
@@ -53,7 +54,9 @@ MousePointer {
53 sourceComponent: Image {
54 sourceComponent: Image {
54 x: -imageInfo.hotspot.x
55 x: -imageInfo.hotspot.x
55 y: -imageInfo.hotspot.y
56 y: -imageInfo.hotspot.y
56− source: "image://cursor/" + mousePointer.themeName + "/" + mousePointer.cursorName
57+ source: imageInfo.imageSource
58+ width: sourceSize.width
59+ height: sourceSize.height
57 }
60 }
58 }
61 }
59}
62}
plugins/Cursor/CursorImageInfo.cpp+21−1
@@ -28,6 +28,15 @@ void CursorImageInfo::setCursorName(const QString &cursorName)
28 update();
28 update();
29 Q_EMIT cursorNameChanged();
29 Q_EMIT cursorNameChanged();
30 }
30 }
31+
32+}
33+void CursorImageInfo::setCursorHeight(qreal cursorHeight)
34+{
35+ if (cursorHeight != m_cursorHeight) {
36+ m_cursorHeight = cursorHeight;
37+ update();
38+ Q_EMIT cursorHeightChanged();
39+ }
31}
40}
32
41
33void CursorImageInfo::setThemeName(const QString &themeName)
42void CursorImageInfo::setThemeName(const QString &themeName)
@@ -41,13 +50,14 @@ void CursorImageInfo::setThemeName(const QString &themeName)
41
50
42void CursorImageInfo::update()
51void CursorImageInfo::update()
43{
52{
44− m_cursorImage = CursorImageProvider::instance()->fetchCursor(m_themeName, m_cursorName);
53+ m_cursorImage = CursorImageProvider::instance()->fetchCursor(m_themeName, m_cursorName, (int) m_cursorHeight);
45
54
46 Q_EMIT hotspotChanged();
55 Q_EMIT hotspotChanged();
47 Q_EMIT frameWidthChanged();
56 Q_EMIT frameWidthChanged();
48 Q_EMIT frameHeightChanged();
57 Q_EMIT frameHeightChanged();
49 Q_EMIT frameCountChanged();
58 Q_EMIT frameCountChanged();
50 Q_EMIT frameDurationChanged();
59 Q_EMIT frameDurationChanged();
60+ Q_EMIT imageSourceChanged();
51}
61}
52
62
53QPoint CursorImageInfo::hotspot() const
63QPoint CursorImageInfo::hotspot() const
@@ -94,3 +104,13 @@ int CursorImageInfo::frameDuration() const
94 return 0;
104 return 0;
95 }
105 }
96}
106}
107+
108+QUrl CursorImageInfo::imageSource() const
109+{
110+ auto urlString = QString("image://cursor/%1/%2/%3")
111+ .arg(m_themeName)
112+ .arg(m_cursorName)
113+ .arg(m_cursorHeight);
114+
115+ return QUrl(urlString);
116+}
plugins/Cursor/CursorImageInfo.h+10
@@ -21,6 +21,7 @@
21
21
22#include <QObject>
22#include <QObject>
23#include <QString>
23#include <QString>
24+#include <QUrl>
24
25
25class CursorImageInfo : public QObject
26class CursorImageInfo : public QObject
26{
27{
@@ -28,12 +29,14 @@ class CursorImageInfo : public QObject
28
29
29 Q_PROPERTY(QString themeName READ themeName WRITE setThemeName NOTIFY themeNameChanged)
30 Q_PROPERTY(QString themeName READ themeName WRITE setThemeName NOTIFY themeNameChanged)
30 Q_PROPERTY(QString cursorName READ cursorName WRITE setCursorName NOTIFY cursorNameChanged)
31 Q_PROPERTY(QString cursorName READ cursorName WRITE setCursorName NOTIFY cursorNameChanged)
32+ Q_PROPERTY(qreal cursorHeight READ cursorHeight WRITE setCursorHeight NOTIFY cursorHeightChanged)
31
33
32 Q_PROPERTY(QPoint hotspot READ hotspot NOTIFY hotspotChanged)
34 Q_PROPERTY(QPoint hotspot READ hotspot NOTIFY hotspotChanged)
33 Q_PROPERTY(qreal frameWidth READ frameWidth NOTIFY frameWidthChanged)
35 Q_PROPERTY(qreal frameWidth READ frameWidth NOTIFY frameWidthChanged)
34 Q_PROPERTY(qreal frameHeight READ frameHeight NOTIFY frameHeightChanged)
36 Q_PROPERTY(qreal frameHeight READ frameHeight NOTIFY frameHeightChanged)
35 Q_PROPERTY(int frameCount READ frameCount NOTIFY frameCountChanged)
37 Q_PROPERTY(int frameCount READ frameCount NOTIFY frameCountChanged)
36 Q_PROPERTY(int frameDuration READ frameDuration NOTIFY frameDurationChanged)
38 Q_PROPERTY(int frameDuration READ frameDuration NOTIFY frameDurationChanged)
39+ Q_PROPERTY(QUrl imageSource READ imageSource NOTIFY imageSourceChanged)
37
40
38public:
41public:
39 CursorImageInfo(QObject *parent = nullptr);
42 CursorImageInfo(QObject *parent = nullptr);
@@ -44,20 +47,26 @@ public:
44 QString cursorName() const { return m_cursorName; }
47 QString cursorName() const { return m_cursorName; }
45 void setCursorName(const QString &);
48 void setCursorName(const QString &);
46
49
50+ qreal cursorHeight() const { return m_cursorHeight; }
51+ void setCursorHeight(qreal);
52+
47 QPoint hotspot() const;
53 QPoint hotspot() const;
48 qreal frameWidth() const;
54 qreal frameWidth() const;
49 qreal frameHeight() const;
55 qreal frameHeight() const;
50 int frameCount() const;
56 int frameCount() const;
51 int frameDuration() const;
57 int frameDuration() const;
58+ QUrl imageSource() const;
52
59
53Q_SIGNALS:
60Q_SIGNALS:
54 void themeNameChanged();
61 void themeNameChanged();
55 void cursorNameChanged();
62 void cursorNameChanged();
63+ void cursorHeightChanged();
56 void hotspotChanged();
64 void hotspotChanged();
57 void frameWidthChanged();
65 void frameWidthChanged();
58 void frameHeightChanged();
66 void frameHeightChanged();
59 void frameCountChanged();
67 void frameCountChanged();
60 void frameDurationChanged();
68 void frameDurationChanged();
69+ void imageSourceChanged();
61
70
62private Q_SLOTS:
71private Q_SLOTS:
63 void update();
72 void update();
@@ -65,6 +74,7 @@ private Q_SLOTS:
65private:
74private:
66 QString m_themeName;
75 QString m_themeName;
67 QString m_cursorName;
76 QString m_cursorName;
77+ qreal m_cursorHeight{0};
68
78
69 CursorImage *m_cursorImage{nullptr};
79 CursorImage *m_cursorImage{nullptr};
70};
80};
plugins/Cursor/CursorImageProvider.cpp+33−21
@@ -27,7 +27,7 @@ CursorImageProvider *CursorImageProvider::m_instance = nullptr;
27/////
27/////
28// BuiltInCursorImage
28// BuiltInCursorImage
29
29
30−BuiltInCursorImage::BuiltInCursorImage()
30+BuiltInCursorImage::BuiltInCursorImage(int cursorHeight)
31{
31{
32 const char *svgString =
32 const char *svgString =
33 "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"
33 "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>"
@@ -42,12 +42,14 @@ BuiltInCursorImage::BuiltInCursorImage()
42 " d=\"M 20.504,50.94931 460.42533,518.14486 266.47603,515.61948 366.48114,719.16522 274.05218,770.68296 172.53185,559.56112 20.504,716.13476 Z\" />"
42 " d=\"M 20.504,50.94931 460.42533,518.14486 266.47603,515.61948 366.48114,719.16522 274.05218,770.68296 172.53185,559.56112 20.504,716.13476 Z\" />"
43 "</svg>";
43 "</svg>";
44
44
45+ // NB: Original image dimension is 20x32. Ensure aspect ratio is kept
45− qimage = QImage(20, 32, QImage::Format_ARGB32);
46+ qimage = QImage((20./32.)*cursorHeight, cursorHeight, QImage::Format_ARGB32);
46 qimage.fill(Qt::transparent);
47 qimage.fill(Qt::transparent);
47 QPainter imagePainter(&qimage);
48 QPainter imagePainter(&qimage);
48
49
49 frameWidth = qimage.width();
50 frameWidth = qimage.width();
50 frameHeight = qimage.height();
51 frameHeight = qimage.height();
52+ requestedHeight = cursorHeight;
51
53
52 QSvgRenderer *svgRenderer = new QSvgRenderer(QByteArray(svgString));
54 QSvgRenderer *svgRenderer = new QSvgRenderer(QByteArray(svgString));
53 svgRenderer->render(&imagePainter);
55 svgRenderer->render(&imagePainter);
@@ -81,11 +83,9 @@ CustomCursorImage::CustomCursorImage(const QCursor &cursor)
81/////
83/////
82// XCursorImage
84// XCursorImage
83
85
84−XCursorImage::XCursorImage(const QString &theme, const QString &file)
86+XCursorImage::XCursorImage(const QString &theme, const QString &file, int preferredCursorHeightPx)
85{
87{
86− // TODO: Consider grid unit value
87− // Hardcoding to a medium size for now
88− int preferredCursorHeightPx = 32;
88+ requestedHeight = preferredCursorHeightPx;
89
89
90 XcursorImages *xcursorImages = XcursorLibraryLoadImages(QFile::encodeName(file), QFile::encodeName(theme),
90 XcursorImages *xcursorImages = XcursorLibraryLoadImages(QFile::encodeName(file), QFile::encodeName(theme),
91 preferredCursorHeightPx);
91 preferredCursorHeightPx);
@@ -96,7 +96,7 @@ XCursorImage::XCursorImage(const QString &theme, const QString &file)
96 frameCount = xcursorImages->nimage;
96 frameCount = xcursorImages->nimage;
97
97
98 for (int i = 0; i < xcursorImages->nimage; ++i) {
98 for (int i = 0; i < xcursorImages->nimage; ++i) {
99− XcursorImage *xcursorImage = xcursorImages->images[0];
99+ XcursorImage *xcursorImage = xcursorImages->images[i];
100 if (frameWidth < (int)xcursorImage->width) {
100 if (frameWidth < (int)xcursorImage->width) {
101 frameWidth = xcursorImage->width;
101 frameWidth = xcursorImage->width;
102 }
102 }
@@ -235,34 +235,43 @@ CursorImageProvider::~CursorImageProvider()
235 m_instance = nullptr;
235 m_instance = nullptr;
236}
236}
237
237
238−QImage CursorImageProvider::requestImage(const QString &cursorThemeAndName, QSize *size, const QSize & /*requestedSize*/)
238+QImage CursorImageProvider::requestImage(const QString &cursorThemeAndNameAndHeight, QSize *size, const QSize & /*requestedSize*/)
239{
239{
240− CursorImage *cursorImage = fetchCursor(cursorThemeAndName);
240+ CursorImage *cursorImage = fetchCursor(cursorThemeAndNameAndHeight);
241 size->setWidth(cursorImage->qimage.width());
241 size->setWidth(cursorImage->qimage.width());
242 size->setHeight(cursorImage->qimage.height());
242 size->setHeight(cursorImage->qimage.height());
243
243
244 return cursorImage->qimage;
244 return cursorImage->qimage;
245}
245}
246
246
247−CursorImage *CursorImageProvider::fetchCursor(const QString &cursorThemeAndName)
247+CursorImage *CursorImageProvider::fetchCursor(const QString &cursorThemeAndNameAndHeight)
248{
248{
249 QString themeName;
249 QString themeName;
250 QString cursorName;
250 QString cursorName;
251+ int cursorHeight;
251 {
252 {
252− QStringList themeAndNameList = cursorThemeAndName.split('/');
253+ QStringList themeAndNameList = cursorThemeAndNameAndHeight.split('/');
253− if (themeAndNameList.size() != 2) {
254+ if (themeAndNameList.size() != 3) {
254 return nullptr;
255 return nullptr;
255 }
256 }
256 themeName = themeAndNameList[0];
257 themeName = themeAndNameList[0];
257 cursorName = themeAndNameList[1];
258 cursorName = themeAndNameList[1];
259+
260+ bool ok;
261+ cursorHeight = themeAndNameList[2].toInt(&ok);
262+ if (!ok) {
263+ cursorHeight = 32;
264+ qWarning().nospace() << "CursorImageProvider: invalid cursor height ("<<themeAndNameList[2]<<")."
265+ " Falling back to "<<cursorHeight<<" pixels";
266+ }
258 }
267 }
259
268
260− return fetchCursor(themeName, cursorName);
269+ return fetchCursor(themeName, cursorName, cursorHeight);
261}
270}
262
271
263−CursorImage *CursorImageProvider::fetchCursor(const QString &themeName, const QString &cursorName)
272+CursorImage *CursorImageProvider::fetchCursor(const QString &themeName, const QString &cursorName, int cursorHeight)
264{
273{
265− CursorImage *cursorImage = fetchCursorHelper(themeName, cursorName);
274+ CursorImage *cursorImage = fetchCursorHelper(themeName, cursorName, cursorHeight);
266
275
267 // Try some fallbacks
276 // Try some fallbacks
268 if (cursorImage->qimage.isNull()) {
277 if (cursorImage->qimage.isNull()) {
@@ -271,7 +280,7 @@ CursorImage *CursorImageProvider::fetchCursor(const QString &themeName, const QS
271 int i = 0;
280 int i = 0;
272 while (cursorImage->qimage.isNull() && i < fallbackNames.count()) {
281 while (cursorImage->qimage.isNull() && i < fallbackNames.count()) {
273 qDebug().nospace() << "CursorImageProvider: "<< cursorName <<" not found, trying " << fallbackNames.at(i);
282 qDebug().nospace() << "CursorImageProvider: "<< cursorName <<" not found, trying " << fallbackNames.at(i);
274− cursorImage = fetchCursorHelper(themeName, fallbackNames.at(i));
283+ cursorImage = fetchCursorHelper(themeName, fallbackNames.at(i), cursorHeight);
275 ++i;
284 ++i;
276 }
285 }
277 }
286 }
@@ -281,14 +290,14 @@ CursorImage *CursorImageProvider::fetchCursor(const QString &themeName, const QS
281 if (cursorImage->qimage.isNull() && cursorName != QLatin1String("left_ptr")) {
290 if (cursorImage->qimage.isNull() && cursorName != QLatin1String("left_ptr")) {
282 qDebug() << "CursorImageProvider:" << cursorName
291 qDebug() << "CursorImageProvider:" << cursorName
283 << "not found (nor its fallbacks, if any). Going for \"left_ptr\" as a last resort.";
292 << "not found (nor its fallbacks, if any). Going for \"left_ptr\" as a last resort.";
284− cursorImage = fetchCursorHelper(themeName, QStringLiteral("left_ptr"));
293+ cursorImage = fetchCursorHelper(themeName, QStringLiteral("left_ptr"), cursorHeight);
285 }
294 }
286
295
287 if (cursorImage->qimage.isNull()) {
296 if (cursorImage->qimage.isNull()) {
288 // finally, go for the built-in cursor
297 // finally, go for the built-in cursor
289 qWarning() << "CursorImageProvider: couldn't find any cursors. Using the built-in one";
298 qWarning() << "CursorImageProvider: couldn't find any cursors. Using the built-in one";
290− if (!m_builtInCursorImage) {
299+ if (!m_builtInCursorImage || m_builtInCursorImage->requestedHeight != cursorHeight) {
291− m_builtInCursorImage.reset(new BuiltInCursorImage);
300+ m_builtInCursorImage.reset(new BuiltInCursorImage(cursorHeight));
292 }
301 }
293 cursorImage = m_builtInCursorImage.data();
302 cursorImage = m_builtInCursorImage.data();
294 }
303 }
@@ -296,7 +305,7 @@ CursorImage *CursorImageProvider::fetchCursor(const QString &themeName, const QS
296 return cursorImage;
305 return cursorImage;
297}
306}
298
307
299−CursorImage *CursorImageProvider::fetchCursorHelper(const QString &themeName, const QString &cursorName)
308+CursorImage *CursorImageProvider::fetchCursorHelper(const QString &themeName, const QString &cursorName, int cursorHeight)
300{
309{
301 if (cursorName == QLatin1String("blank")) {
310 if (cursorName == QLatin1String("blank")) {
302 return &m_blankCursorImage;
311 return &m_blankCursorImage;
@@ -306,7 +315,10 @@ CursorImage *CursorImageProvider::fetchCursorHelper(const QString &themeName, co
306 QMap<QString, CursorImage*> &themeCursors = m_cursors[themeName];
315 QMap<QString, CursorImage*> &themeCursors = m_cursors[themeName];
307
316
308 if (!themeCursors.contains(cursorName)) {
317 if (!themeCursors.contains(cursorName)) {
309− themeCursors[cursorName] = new XCursorImage(themeName, cursorName);
318+ themeCursors[cursorName] = new XCursorImage(themeName, cursorName, cursorHeight);
319+ } else if (themeCursors[cursorName]->requestedHeight != cursorHeight) {
320+ delete themeCursors.take(cursorName);
321+ themeCursors[cursorName] = new XCursorImage(themeName, cursorName, cursorHeight);
310 }
322 }
311
323
312 return themeCursors[cursorName];
324 return themeCursors[cursorName];
plugins/Cursor/CursorImageProvider.h+9−6
@@ -42,17 +42,20 @@ public:
42 int frameHeight{0};
42 int frameHeight{0};
43 int frameCount{1};
43 int frameCount{1};
44 int frameDuration{40};
44 int frameDuration{40};
45+
46+ // Requested height when creating this cursor.
47+ int requestedHeight{0};
45};
48};
46
49
47class XCursorImage : public CursorImage {
50class XCursorImage : public CursorImage {
48public:
51public:
49− XCursorImage(const QString &theme, const QString &file);
52+ XCursorImage(const QString &theme, const QString &file, int preferredCursorHeightPx);
50 virtual ~XCursorImage();
53 virtual ~XCursorImage();
51};
54};
52
55
53class BuiltInCursorImage : public CursorImage {
56class BuiltInCursorImage : public CursorImage {
54public:
57public:
55− BuiltInCursorImage();
58+ BuiltInCursorImage(int cursorHeight);
56};
59};
57
60
58class BlankCursorImage : public CursorImage {
61class BlankCursorImage : public CursorImage {
@@ -74,15 +77,15 @@ public:
74 static CursorImageProvider *instance() { return m_instance; }
77 static CursorImageProvider *instance() { return m_instance; }
75
78
76
79
77− QImage requestImage(const QString &cursorName, QSize *size, const QSize &requestedSize) override;
80+ QImage requestImage(const QString &cursorThemeAndNameAndHeight, QSize *size, const QSize &requestedSize) override;
78
81
79− CursorImage *fetchCursor(const QString &themeName, const QString &cursorName);
82+ CursorImage *fetchCursor(const QString &themeName, const QString &cursorName, int cursorHeight);
80
83
81 void setCustomCursor(const QCursor &customCursor);
84 void setCustomCursor(const QCursor &customCursor);
82
85
83private:
86private:
84− CursorImage *fetchCursor(const QString &cursorThemeAndName);
87+ CursorImage *fetchCursor(const QString &cursorThemeAndNameAndHeight);
85− CursorImage *fetchCursorHelper(const QString &themeName, const QString &cursorName);
88+ CursorImage *fetchCursorHelper(const QString &themeName, const QString &cursorName, int cursorHeight);
86
89
87 // themeName -> (cursorName -> cursorImage)
90 // themeName -> (cursorName -> cursorImage)
88 // TODO: discard old, unused, cursors
91 // TODO: discard old, unused, cursors
qml/Shell.qml+2
@@ -775,6 +775,8 @@ StyledItem {
775 when: cursor.mouseNeverMoved && cursor.visible
775 when: cursor.mouseNeverMoved && cursor.visible
776 }
776 }
777
777
778+ height: units.gu(3)
779+
778 onPushedLeftBoundary: {
780 onPushedLeftBoundary: {
779 if (buttons === Qt.NoButton) {
781 if (buttons === Qt.NoButton) {
780 launcher.pushEdge(amount);
782 launcher.pushEdge(amount);
tests/plugins/Cursor/tst_Cursor.qml+8−1
@@ -26,11 +26,13 @@ Rectangle {
26
26
27 property string themeName: "default"
27 property string themeName: "default"
28 property string cursorName: "left_ptr"
28 property string cursorName: "left_ptr"
29+ property string cursorHeight: "32"
29
30
30 CursorImageInfo {
31 CursorImageInfo {
31 id: imageInfo
32 id: imageInfo
32 themeName: root.themeName
33 themeName: root.themeName
33 cursorName: root.cursorName
34 cursorName: root.cursorName
35+ cursorHeight: root.cursorHeight
34 }
36 }
35
37
36 Item {
38 Item {
@@ -43,7 +45,7 @@ Rectangle {
43
45
44 x: -imageInfo.hotspot.x
46 x: -imageInfo.hotspot.x
45 y: -imageInfo.hotspot.y
47 y: -imageInfo.hotspot.y
46− source: "image://cursor/" + root.themeName + "/" + root.cursorName
48+ source: "image://cursor/" + root.themeName + "/" + root.cursorName + "/" + root.cursorHeight
47
49
48 interpolate: false
50 interpolate: false
49
51
@@ -110,6 +112,10 @@ Rectangle {
110
112
111 Item {width: 10; height: 40}
113 Item {width: 10; height: 40}
112
114
115+ TextEntry { id: cursorHeightEntry; name: "cursorHeight"; value: "32" }
116+
117+ Item {width: 10; height: 40}
118+
113 Rectangle {
119 Rectangle {
114 color: applyMouseArea.pressed ? "green" : "lightslategray"
120 color: applyMouseArea.pressed ? "green" : "lightslategray"
115 width: parent.width - 20
121 width: parent.width - 20
@@ -121,6 +127,7 @@ Rectangle {
121 onClicked: {
127 onClicked: {
122 root.themeName = themeNameEntry.value;
128 root.themeName = themeNameEntry.value;
123 root.cursorName = cursorNameEntry.value;
129 root.cursorName = cursorNameEntry.value;
130+ root.cursorHeight = cursorHeightEntry.value;
124 }
131 }
125 }
132 }
126 }
133 }