on xenial help sign in
Cursor: make its size grid unit based
ef73617Daniel d'Andrada
plugins/Cursor/Cursor.qml+11−2
@@ -25,6 +25,13 @@ 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
29+ }
30+
31+ QtObject {
32+ id: d
33+ readonly property url imageSource: "image://cursor/" + mousePointer.themeName + "/" + mousePointer.cursorName
34+ + "/" + mousePointer.height
28 }
35 }
29
36
30 Loader {
37 Loader {
@@ -32,7 +39,7 @@ MousePointer {
32 sourceComponent: AnimatedSprite {
39 sourceComponent: AnimatedSprite {
33 x: -imageInfo.hotspot.x
40 x: -imageInfo.hotspot.x
34 y: -imageInfo.hotspot.y
41 y: -imageInfo.hotspot.y
35− source: "image://cursor/" + mousePointer.themeName + "/" + mousePointer.cursorName
42+ source: d.imageSource
36
43
37 interpolate: false
44 interpolate: false
38
45
@@ -53,7 +60,9 @@ MousePointer {
53 sourceComponent: Image {
60 sourceComponent: Image {
54 x: -imageInfo.hotspot.x
61 x: -imageInfo.hotspot.x
55 y: -imageInfo.hotspot.y
62 y: -imageInfo.hotspot.y
56− source: "image://cursor/" + mousePointer.themeName + "/" + mousePointer.cursorName
63+ source: d.imageSource
64+ width: sourceSize.width
65+ height: sourceSize.height
57 }
66 }
58 }
67 }
59}
68}
plugins/Cursor/CursorImageInfo.cpp+10−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,7 +50,7 @@ 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();
plugins/Cursor/CursorImageInfo.h+6
@@ -28,6 +28,7 @@ class CursorImageInfo : public QObject
28
28
29 Q_PROPERTY(QString themeName READ themeName WRITE setThemeName NOTIFY themeNameChanged)
29 Q_PROPERTY(QString themeName READ themeName WRITE setThemeName NOTIFY themeNameChanged)
30 Q_PROPERTY(QString cursorName READ cursorName WRITE setCursorName NOTIFY cursorNameChanged)
30 Q_PROPERTY(QString cursorName READ cursorName WRITE setCursorName NOTIFY cursorNameChanged)
31+ Q_PROPERTY(qreal cursorHeight READ cursorHeight WRITE setCursorHeight NOTIFY cursorHeightChanged)
31
32
32 Q_PROPERTY(QPoint hotspot READ hotspot NOTIFY hotspotChanged)
33 Q_PROPERTY(QPoint hotspot READ hotspot NOTIFY hotspotChanged)
33 Q_PROPERTY(qreal frameWidth READ frameWidth NOTIFY frameWidthChanged)
34 Q_PROPERTY(qreal frameWidth READ frameWidth NOTIFY frameWidthChanged)
@@ -44,6 +45,9 @@ public:
44 QString cursorName() const { return m_cursorName; }
45 QString cursorName() const { return m_cursorName; }
45 void setCursorName(const QString &);
46 void setCursorName(const QString &);
46
47
48+ qreal cursorHeight() const { return m_cursorHeight; }
49+ void setCursorHeight(qreal);
50+
47 QPoint hotspot() const;
51 QPoint hotspot() const;
48 qreal frameWidth() const;
52 qreal frameWidth() const;
49 qreal frameHeight() const;
53 qreal frameHeight() const;
@@ -53,6 +57,7 @@ public:
53Q_SIGNALS:
57Q_SIGNALS:
54 void themeNameChanged();
58 void themeNameChanged();
55 void cursorNameChanged();
59 void cursorNameChanged();
60+ void cursorHeightChanged();
56 void hotspotChanged();
61 void hotspotChanged();
57 void frameWidthChanged();
62 void frameWidthChanged();
58 void frameHeightChanged();
63 void frameHeightChanged();
@@ -65,6 +70,7 @@ private Q_SLOTS:
65private:
70private:
66 QString m_themeName;
71 QString m_themeName;
67 QString m_cursorName;
72 QString m_cursorName;
73+ qreal m_cursorHeight{0};
68
74
69 CursorImage *m_cursorImage{nullptr};
75 CursorImage *m_cursorImage{nullptr};
70};
76};
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 }