Warning, /graphics/krita/3rdparty/ext_qt/0045-Android-Add-support-for-changing-cursor.patch is written in an unsupported language. File is not indexed.
0001 From 67a74e329b4cd803f4470de0302d73f5dac9c9a3 Mon Sep 17 00:00:00 2001 0002 From: Sharaf Zaman <shzam@sdf.org> 0003 Date: Tue, 21 Sep 2021 10:14:23 +0000 0004 Subject: [PATCH 45/46] Android: Add support for changing cursor 0005 0006 --- 0007 src/android/jar/jar.pro | 1 + 0008 .../qtproject/qt5/android/QtPointerIcon.java | 129 ++++++++++++++++++ 0009 .../org/qtproject/qt5/android/QtSurface.java | 11 +- 0010 .../qtproject/qt5/android/SAFFileManager.java | 4 +- 0011 src/plugins/platforms/android/android.pro | 6 +- 0012 .../android/qandroidplatformcursor.cpp | 70 ++++++++++ 0013 .../android/qandroidplatformcursor.h | 22 +++ 0014 .../android/qandroidplatformscreen.cpp | 5 +- 0015 .../android/qandroidplatformscreen.h | 3 + 0016 9 files changed, 242 insertions(+), 9 deletions(-) 0017 create mode 100644 src/android/jar/src/org/qtproject/qt5/android/QtPointerIcon.java 0018 create mode 100644 src/plugins/platforms/android/qandroidplatformcursor.cpp 0019 create mode 100644 src/plugins/platforms/android/qandroidplatformcursor.h 0020 0021 diff --git a/src/android/jar/jar.pro b/src/android/jar/jar.pro 0022 index 6a610c511d..ccd0f21dde 100644 0023 --- a/src/android/jar/jar.pro 0024 +++ b/src/android/jar/jar.pro 0025 @@ -25,6 +25,7 @@ JAVASOURCES += \ 0026 $$PATHPREFIX/EditContextView.java \ 0027 $$PATHPREFIX/EditPopupMenu.java \ 0028 $$PATHPREFIX/CursorHandle.java \ 0029 + $$PATHPREFIX/QtPointerIcon.java \ 0030 $$PATHPREFIX/QtThread.java 0031 0032 # install 0033 diff --git a/src/android/jar/src/org/qtproject/qt5/android/QtPointerIcon.java b/src/android/jar/src/org/qtproject/qt5/android/QtPointerIcon.java 0034 new file mode 100644 0035 index 0000000000..723f0f5992 0036 --- /dev/null 0037 +++ b/src/android/jar/src/org/qtproject/qt5/android/QtPointerIcon.java 0038 @@ -0,0 +1,129 @@ 0039 +package org.qtproject.qt5.android; 0040 + 0041 +import android.app.Activity; 0042 +import android.graphics.Bitmap; 0043 +import android.graphics.BitmapFactory; 0044 +import android.os.Build; 0045 +import android.util.Log; 0046 +import android.util.LruCache; 0047 +import android.view.PointerIcon; 0048 + 0049 +@SuppressWarnings("unused") 0050 +class CursorShape { 0051 + public static final int ArrowCursor = 0; 0052 + public static final int UpArrowCursor = 1; 0053 + public static final int CrossCursor = 2; 0054 + public static final int WaitCursor = 3; 0055 + public static final int IBeamCursor = 4; 0056 + public static final int SizeVerCursor = 5; 0057 + public static final int SizeHorCursor = 6; 0058 + public static final int SizeBDiagCursor = 7; 0059 + public static final int SizeFDiagCursor = 8; 0060 + public static final int SizeAllCursor = 9; 0061 + public static final int BlankCursor = 10; 0062 + public static final int SplitVCursor = 11; 0063 + public static final int SplitHCursor = 12; 0064 + public static final int PointingHandCursor = 13; 0065 + public static final int ForbiddenCursor = 14; 0066 + public static final int WhatsThisCursor = 15; 0067 + public static final int BusyCursor = 16; 0068 + public static final int OpenHandCursor = 17; 0069 + public static final int ClosedHandCursor = 18; 0070 + public static final int DragCopyCursor = 19; 0071 + public static final int DragMoveCursor = 20; 0072 + public static final int DragLinkCursor = 21; 0073 + public static final int LastCursor = DragLinkCursor; 0074 + public static final int BitmapCursor = 24; 0075 + public static final int CustomCursor = 5; 0076 + 0077 + public static PointerIcon getPointerIconQt(int type) { 0078 + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { 0079 + return null; 0080 + } 0081 + switch (type) { 0082 + case ArrowCursor: 0083 + return PointerIcon.getSystemIcon(QtNative.getContext(), PointerIcon.TYPE_ARROW); 0084 + case CrossCursor: 0085 + return PointerIcon.getSystemIcon(QtNative.getContext(), PointerIcon.TYPE_CROSSHAIR); 0086 + case WaitCursor: 0087 + return PointerIcon.getSystemIcon(QtNative.getContext(), PointerIcon.TYPE_WAIT); 0088 + case BlankCursor: 0089 + return PointerIcon.getSystemIcon(QtNative.getContext(), PointerIcon.TYPE_NULL); 0090 + case IBeamCursor: 0091 + return PointerIcon.getSystemIcon(QtNative.getContext(), PointerIcon.TYPE_TEXT); 0092 + case SizeBDiagCursor: 0093 + return PointerIcon.getSystemIcon(QtNative.getContext(), PointerIcon.TYPE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW); 0094 + case SizeFDiagCursor: 0095 + return PointerIcon.getSystemIcon(QtNative.getContext(), PointerIcon.TYPE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW); 0096 + case SplitVCursor: 0097 + case SizeVerCursor: 0098 + return PointerIcon.getSystemIcon(QtNative.getContext(), PointerIcon.TYPE_VERTICAL_DOUBLE_ARROW); 0099 + case SplitHCursor: 0100 + case SizeHorCursor: 0101 + return PointerIcon.getSystemIcon(QtNative.getContext(), PointerIcon.TYPE_HORIZONTAL_DOUBLE_ARROW); 0102 + case PointingHandCursor: 0103 + return PointerIcon.getSystemIcon(QtNative.getContext(), PointerIcon.TYPE_HAND); 0104 + case ForbiddenCursor: 0105 + return PointerIcon.getSystemIcon(QtNative.getContext(), PointerIcon.TYPE_NO_DROP); 0106 + case OpenHandCursor: 0107 + return PointerIcon.getSystemIcon(QtNative.getContext(), PointerIcon.TYPE_GRAB); 0108 + case ClosedHandCursor: 0109 + return PointerIcon.getSystemIcon(QtNative.getContext(), PointerIcon.TYPE_GRABBING); 0110 + case DragMoveCursor: 0111 + case DragCopyCursor: 0112 + return PointerIcon.getSystemIcon(QtNative.getContext(), PointerIcon.TYPE_COPY); 0113 + default: 0114 + return PointerIcon.getSystemIcon(QtNative.getContext(), PointerIcon.TYPE_DEFAULT); 0115 + } 0116 + } 0117 +} 0118 + 0119 +@SuppressWarnings("UnusedDeclaration") 0120 +public class QtPointerIcon { 0121 + 0122 + private static final String TAG = "QtPointerIcon"; 0123 + private static QtPointerIcon sQtPointer; 0124 + // we cache on Java side, passing data over Jni can be expensive 0125 + private final LruCache<Long, PointerIcon> iconCache = new LruCache<>(10); 0126 + private PointerIcon icon; 0127 + 0128 + public static QtPointerIcon instance() { 0129 + if (sQtPointer == null) { 0130 + sQtPointer = new QtPointerIcon(); 0131 + } 0132 + return sQtPointer; 0133 + } 0134 + 0135 + public PointerIcon getIcon() { 0136 + return icon; 0137 + } 0138 + 0139 + public void setIcon(int type) { 0140 + // TODO(sh_zam): setPointerIcon? 0141 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 0142 + icon = CursorShape.getPointerIconQt(type); 0143 + } 0144 + } 0145 + 0146 + public void setCachedBitmapIcon(long cacheKey) { 0147 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 0148 + icon = iconCache.get(cacheKey); 0149 + } 0150 + } 0151 + 0152 + public void setBitmapIcon(byte[] colors, int w, int h, int hX, int hY, long cacheKey) { 0153 + Bitmap bitmap = BitmapFactory.decodeByteArray(colors, 0, colors.length); 0154 + if (bitmap == null) { 0155 + Log.e(TAG, "PointerIcon bitmap is null!"); 0156 + return; 0157 + } 0158 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 0159 + icon = PointerIcon.create(bitmap, hX, hY); 0160 + iconCache.put(cacheKey, icon); 0161 + } 0162 + } 0163 + 0164 + public boolean existsInCache(long key) { 0165 + return iconCache.get(key) != null; 0166 + } 0167 +} 0168 diff --git a/src/android/jar/src/org/qtproject/qt5/android/QtSurface.java b/src/android/jar/src/org/qtproject/qt5/android/QtSurface.java 0169 index 723a783c6b..8b9f581ef0 100644 0170 --- a/src/android/jar/src/org/qtproject/qt5/android/QtSurface.java 0171 +++ b/src/android/jar/src/org/qtproject/qt5/android/QtSurface.java 0172 @@ -40,15 +40,13 @@ 0173 0174 package org.qtproject.qt5.android; 0175 0176 -import android.app.Activity; 0177 import android.content.Context; 0178 import android.graphics.PixelFormat; 0179 +import android.view.MotionEvent; 0180 +import android.view.PointerIcon; 0181 import android.view.SurfaceHolder; 0182 import android.view.SurfaceView; 0183 0184 -import java.lang.reflect.Constructor; 0185 -import java.lang.reflect.Method; 0186 - 0187 public class QtSurface extends SurfaceView implements SurfaceHolder.Callback 0188 { 0189 private Object m_accessibilityDelegate = null; 0190 @@ -87,4 +85,9 @@ public class QtSurface extends SurfaceView implements SurfaceHolder.Callback 0191 { 0192 QtNative.setSurface(getId(), null, 0, 0); 0193 } 0194 + 0195 + @Override 0196 + public PointerIcon onResolvePointerIcon(MotionEvent event, int pointerIndex) { 0197 + return QtPointerIcon.instance().getIcon(); 0198 + } 0199 } 0200 diff --git a/src/android/jar/src/org/qtproject/qt5/android/SAFFileManager.java b/src/android/jar/src/org/qtproject/qt5/android/SAFFileManager.java 0201 index f4f097efcf..df5868d67a 100644 0202 --- a/src/android/jar/src/org/qtproject/qt5/android/SAFFileManager.java 0203 +++ b/src/android/jar/src/org/qtproject/qt5/android/SAFFileManager.java 0204 @@ -671,11 +671,11 @@ public class SAFFileManager { 0205 } 0206 0207 while (cursor.moveToNext()) { 0208 - final String docId = cursor.getString(1); 0209 + final String docId = SAFUtils.getColumnValStringOrNull(cursor, DocumentsContract.Document.COLUMN_DOCUMENT_ID); 0210 final Uri fileUri = DocumentsContract.buildDocumentUriUsingTree(documentTreeUri, docId); 0211 cachedDocumentFiles.add(new CachedDocumentFile(mCtx, 0212 SAFUtils.getColumnValStringOrNull(cursor, DocumentsContract.Document.COLUMN_DISPLAY_NAME), 0213 - SAFUtils.getColumnValStringOrNull(cursor, DocumentsContract.Document.COLUMN_DOCUMENT_ID), 0214 + docId, 0215 SAFUtils.getColumnValStringOrNull(cursor, DocumentsContract.Document.COLUMN_MIME_TYPE), 0216 SAFUtils.getColumnValIntegerOrDefault(cursor, DocumentsContract.Document.COLUMN_SIZE, -1), 0217 fileUri)); 0218 diff --git a/src/plugins/platforms/android/android.pro b/src/plugins/platforms/android/android.pro 0219 index 8f62cb4c1e..92a7572563 100644 0220 --- a/src/plugins/platforms/android/android.pro 0221 +++ b/src/plugins/platforms/android/android.pro 0222 @@ -49,7 +49,8 @@ SOURCES += $$PWD/androidplatformplugin.cpp \ 0223 $$PWD/qandroideventdispatcher.cpp \ 0224 $$PWD/qandroidplatformoffscreensurface.cpp \ 0225 $$PWD/qandroidplatformfiledialoghelper.cpp \ 0226 - $$PWD/qandroidplatformwindowmanager.cpp 0227 + $$PWD/qandroidplatformwindowmanager.cpp \ 0228 + $$PWD/qandroidplatformcursor.cpp 0229 0230 HEADERS += $$PWD/qandroidplatformintegration.h \ 0231 $$PWD/androidcontentfileengine.h \ 0232 @@ -81,7 +82,8 @@ HEADERS += $$PWD/qandroidplatformintegration.h \ 0233 $$PWD/qandroideventdispatcher.h \ 0234 $$PWD/qandroidplatformoffscreensurface.h \ 0235 $$PWD/qandroidplatformfiledialoghelper.h \ 0236 - $$PWD/qandroidplatformwindowmanager.h 0237 + $$PWD/qandroidplatformwindowmanager.h \ 0238 + $$PWD/qandroidplatformcursor.h 0239 0240 qtConfig(android-style-assets): SOURCES += $$PWD/extract.cpp 0241 else: SOURCES += $$PWD/extract-dummy.cpp 0242 diff --git a/src/plugins/platforms/android/qandroidplatformcursor.cpp b/src/plugins/platforms/android/qandroidplatformcursor.cpp 0243 new file mode 100644 0244 index 0000000000..025093066e 0245 --- /dev/null 0246 +++ b/src/plugins/platforms/android/qandroidplatformcursor.cpp 0247 @@ -0,0 +1,70 @@ 0248 +#include "qandroidplatformcursor.h" 0249 +#include "qandroidplatformscreen.h" 0250 +#include <QBitmap> 0251 +#include <QBuffer> 0252 +#include <QPixmap> 0253 +#include <QtCore/private/qjnihelpers_p.h> 0254 +#include <androidjnimain.h> 0255 +#include <private/qguiapplication_p.h> 0256 +#include <private/qhighdpiscaling_p.h> 0257 +#include <private/qjni_p.h> 0258 +#include <qpa/qplatformwindow.h> 0259 + 0260 +QAndroidPlatformCursor::QAndroidPlatformCursor(QAndroidPlatformScreen *screen) 0261 + : m_qtPointer(QJNIObjectPrivate::callStaticObjectMethod( 0262 + "org/qtproject/qt5/android/QtPointerIcon", "instance", 0263 + "()Lorg/qtproject/qt5/android/QtPointerIcon;")) 0264 + , m_screen(screen) 0265 +{ 0266 +} 0267 + 0268 +void QAndroidPlatformCursor::changeCursor(QCursor *cursor, QWindow *window) 0269 +{ 0270 + if (!window || !window->handle()) { 0271 + return; 0272 + } 0273 + 0274 + if (cursor) { 0275 + if (cursor->shape() == Qt::BitmapCursor) { 0276 + QImage bitmap = cursor->bitmap()->toImage(); 0277 + qint64 key = cursor->bitmap()->cacheKey(); 0278 + bool exists = m_qtPointer.callMethod<jboolean>("existsInCache", "(J)Z", key); 0279 + if (exists) { 0280 + m_qtPointer.callMethod<void>("setCachedBitmapIcon", "(J)V", key); 0281 + return; 0282 + } 0283 + 0284 + QImage mask = cursor->mask()->toImage().convertToFormat(QImage::Format_Mono); 0285 + mask.invertPixels(); 0286 + bitmap.setAlphaChannel(mask); 0287 + 0288 + QByteArray bytes; 0289 + QBuffer buffer(&bytes); 0290 + buffer.open(QIODevice::WriteOnly); 0291 + bitmap.save(&buffer, "PNG"); 0292 + 0293 + jbyte *pixels = (jbyte *)bytes.data(); 0294 + 0295 + QJNIEnvironmentPrivate env; 0296 + // we don't need to release the memory since we don't pin it 0297 + jbyteArray array = env->NewByteArray(bytes.size()); 0298 + env->SetByteArrayRegion(array, 0, bytes.size(), pixels); 0299 + 0300 + m_qtPointer.callMethod<void>( 0301 + "setBitmapIcon", "([BIIIIJ)V", array, bitmap.width(), bitmap.height(), 0302 + cursor->hotSpot().x(), cursor->hotSpot().y(), key); 0303 + } else { 0304 + m_qtPointer.callMethod<void>("setIcon", "(I)V", 0305 + static_cast<int>(cursor->shape())); 0306 + } 0307 + } else { 0308 + m_qtPointer.callMethod<void>("setIcon", "(I)V", 0309 + static_cast<int>(Qt::BlankCursor)); 0310 + } 0311 +} 0312 + 0313 +QPoint QAndroidPlatformCursor::pos() const 0314 +{ 0315 + return QHighDpi::toNativePixels(QGuiApplicationPrivate::lastCursorPosition.toPoint(), 0316 + m_screen->screen()); 0317 +} 0318 diff --git a/src/plugins/platforms/android/qandroidplatformcursor.h b/src/plugins/platforms/android/qandroidplatformcursor.h 0319 new file mode 100644 0320 index 0000000000..81a3225f32 0321 --- /dev/null 0322 +++ b/src/plugins/platforms/android/qandroidplatformcursor.h 0323 @@ -0,0 +1,22 @@ 0324 +#ifndef __QANDROIDPLATFORMCURSOR_H_ 0325 +#define __QANDROIDPLATFORMCURSOR_H_ 0326 + 0327 +#include <private/qjni_p.h> 0328 +#include <qpa/qplatformcursor.h> 0329 + 0330 +class QAndroidPlatformScreen; 0331 + 0332 +class QAndroidPlatformCursor : public QPlatformCursor 0333 +{ 0334 +public: 0335 + explicit QAndroidPlatformCursor(QAndroidPlatformScreen *screen); 0336 + 0337 + void changeCursor(QCursor *windowCursor, QWindow *window) override; 0338 + QPoint pos() const override; 0339 + 0340 +private: 0341 + QJNIObjectPrivate m_qtPointer; 0342 + QAndroidPlatformScreen *m_screen; 0343 +}; 0344 + 0345 +#endif // __QANDROIDPLATFORMCURSOR_H_ 0346 diff --git a/src/plugins/platforms/android/qandroidplatformscreen.cpp b/src/plugins/platforms/android/qandroidplatformscreen.cpp 0347 index 61ab6f58c7..891c2c372b 100644 0348 --- a/src/plugins/platforms/android/qandroidplatformscreen.cpp 0349 +++ b/src/plugins/platforms/android/qandroidplatformscreen.cpp 0350 @@ -51,6 +51,7 @@ 0351 #include "androidjnimenu.h" 0352 #include "androiddeadlockprotector.h" 0353 #include "qandroidplatformwindowmanager.h" 0354 +#include "qandroidplatformcursor.h" 0355 0356 #include <android/bitmap.h> 0357 #include <android/native_window_jni.h> 0358 @@ -89,7 +90,9 @@ private: 0359 #endif 0360 0361 QAndroidPlatformScreen::QAndroidPlatformScreen() 0362 - : QObject(), QPlatformScreen() 0363 + : QObject() 0364 + , QPlatformScreen() 0365 + , m_cursor(new QAndroidPlatformCursor(this)) 0366 { 0367 m_availableGeometry = QRect(0, 0, QAndroidPlatformIntegration::m_defaultGeometryWidth, QAndroidPlatformIntegration::m_defaultGeometryHeight); 0368 m_size = QSize(QAndroidPlatformIntegration::m_defaultScreenWidth, QAndroidPlatformIntegration::m_defaultScreenHeight); 0369 diff --git a/src/plugins/platforms/android/qandroidplatformscreen.h b/src/plugins/platforms/android/qandroidplatformscreen.h 0370 index 878e33546a..1d489d77a4 100644 0371 --- a/src/plugins/platforms/android/qandroidplatformscreen.h 0372 +++ b/src/plugins/platforms/android/qandroidplatformscreen.h 0373 @@ -85,6 +85,8 @@ public: 0374 void topWindowChanged(QWindow *w); 0375 int rasterSurfaces(); 0376 0377 + QPlatformCursor *cursor() const override { return m_cursor.data(); } 0378 + 0379 public slots: 0380 void setDirty(const QRect &rect); 0381 void setPhysicalSize(const QSize &size); 0382 @@ -124,6 +126,7 @@ private: 0383 QWaitCondition m_surfaceWaitCondition; 0384 QSize m_size; 0385 QMap<int, QAndroidPlatformWindowManager *> m_windowManagers; 0386 + QScopedPointer<QPlatformCursor> m_cursor; 0387 }; 0388 0389 QT_END_NAMESPACE 0390 -- 0391 2.33.0 0392