custom_cursor.dart 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. import 'dart:convert';
  2. import 'dart:js' as js;
  3. import 'package:flutter/foundation.dart';
  4. import 'package:flutter/services.dart';
  5. import 'package:flutter_hbb/models/model.dart' as model;
  6. class CursorData {
  7. final String key;
  8. final String url;
  9. final double hotX;
  10. final double hotY;
  11. final int width;
  12. final int height;
  13. CursorData({
  14. required this.key,
  15. required this.url,
  16. required this.hotX,
  17. required this.hotY,
  18. required this.width,
  19. required this.height,
  20. });
  21. }
  22. /// The cursor manager
  23. class CursorManager {
  24. final Map<String, CursorData> _cursors = <String, CursorData>{};
  25. String latestKey = '';
  26. CursorManager._();
  27. static CursorManager instance = CursorManager._();
  28. Future<void> registerCursor(CursorData data) async {
  29. _cursors[data.key] = data;
  30. }
  31. Future<void> deleteCursor(String key) async {
  32. _cursors.remove(key);
  33. }
  34. Future<void> setSystemCursor(String key) async {
  35. if (latestKey == key) {
  36. return;
  37. }
  38. latestKey = key;
  39. final CursorData? cursorData = _cursors[key];
  40. if (cursorData != null) {
  41. js.context.callMethod('setByName', [
  42. 'cursor',
  43. jsonEncode({
  44. 'url': cursorData.url,
  45. 'hotx': cursorData.hotX.toInt(),
  46. 'hoty': cursorData.hotY.toInt(),
  47. })
  48. ]);
  49. }
  50. }
  51. Future<void> resetSystemCursor() async {
  52. latestKey = '';
  53. js.context.callMethod('setByName', ['cursor', 'auto']);
  54. }
  55. }
  56. class FlutterCustomMemoryImageCursor extends MouseCursor {
  57. final String key;
  58. const FlutterCustomMemoryImageCursor({required this.key});
  59. @override
  60. MouseCursorSession createSession(int device) =>
  61. _FlutterCustomMemoryImageCursorSession(this, device);
  62. @override
  63. String get debugDescription =>
  64. objectRuntimeType(this, 'FlutterCustomMemoryImageCursor');
  65. }
  66. class _FlutterCustomMemoryImageCursorSession extends MouseCursorSession {
  67. _FlutterCustomMemoryImageCursorSession(
  68. FlutterCustomMemoryImageCursor cursor, int device)
  69. : super(cursor, device);
  70. @override
  71. FlutterCustomMemoryImageCursor get cursor =>
  72. super.cursor as FlutterCustomMemoryImageCursor;
  73. @override
  74. Future<void> activate() async {
  75. await CursorManager.instance.setSystemCursor(cursor.key);
  76. }
  77. @override
  78. void dispose() {}
  79. }
  80. deleteCustomCursor(String key) => CursorManager.instance.deleteCursor(key);
  81. resetSystemCursor() => CursorManager.instance.resetSystemCursor();
  82. MouseCursor buildCursorOfCache(
  83. model.CursorModel cursor, double scale, model.CursorData? cache) {
  84. if (cache == null) {
  85. return MouseCursor.defer;
  86. } else {
  87. final key = cache.updateGetKey(scale);
  88. if (!cursor.cachedKeys.contains(key)) {
  89. // data should be checked here, because it may be changed after `updateGetKey()`
  90. final data = cache.data;
  91. if (data == null) {
  92. return MouseCursor.defer;
  93. }
  94. debugPrint(
  95. "Register custom cursor with key $key (${cache.hotx},${cache.hoty})");
  96. CursorManager.instance.registerCursor(CursorData(
  97. key: key,
  98. url: 'data:image/rgba;base64,${base64Encode(data)}',
  99. width: (cache.width * cache.scale).toInt(),
  100. height: (cache.height * cache.scale).toInt(),
  101. hotX: cache.hotx,
  102. hotY: cache.hoty));
  103. cursor.addKey(key);
  104. }
  105. return FlutterCustomMemoryImageCursor(key: key);
  106. }
  107. }