| 
									
										
										
										
											2022-05-29 17:19:50 +08:00
										 |  |  | import 'dart:async'; | 
					
						
							|  |  |  | import 'dart:io'; | 
					
						
							|  |  |  | import 'dart:ui' as ui; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-21 10:40:43 +08:00
										 |  |  | import 'package:desktop_multi_window/desktop_multi_window.dart'; | 
					
						
							| 
									
										
										
										
											2022-05-29 17:19:50 +08:00
										 |  |  | import 'package:flutter/material.dart'; | 
					
						
							|  |  |  | import 'package:flutter/services.dart'; | 
					
						
							| 
									
										
										
										
											2022-12-11 14:17:29 +08:00
										 |  |  | import 'package:flutter_custom_cursor/cursor_manager.dart' | 
					
						
							|  |  |  |     as custom_cursor_manager; | 
					
						
							| 
									
										
										
										
											2022-06-13 21:07:26 +08:00
										 |  |  | import 'package:get/get.dart'; | 
					
						
							| 
									
										
										
										
											2022-05-29 17:19:50 +08:00
										 |  |  | import 'package:provider/provider.dart'; | 
					
						
							|  |  |  | import 'package:wakelock/wakelock.dart'; | 
					
						
							| 
									
										
										
										
											2022-09-06 02:08:59 -07:00
										 |  |  | import 'package:flutter_custom_cursor/flutter_custom_cursor.dart'; | 
					
						
							| 
									
										
										
										
											2022-10-05 00:22:40 +08:00
										 |  |  | import 'package:flutter_improved_scrolling/flutter_improved_scrolling.dart'; | 
					
						
							| 
									
										
										
										
											2022-06-27 10:34:57 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-05 00:22:40 +08:00
										 |  |  | import '../../consts.dart'; | 
					
						
							| 
									
										
										
										
											2022-09-27 23:05:11 +08:00
										 |  |  | import '../../common/widgets/overlay.dart'; | 
					
						
							| 
									
										
										
										
											2022-09-27 22:16:27 +08:00
										 |  |  | import '../../common/widgets/remote_input.dart'; | 
					
						
							| 
									
										
										
										
											2022-05-29 17:19:50 +08:00
										 |  |  | import '../../common.dart'; | 
					
						
							|  |  |  | import '../../mobile/widgets/dialog.dart'; | 
					
						
							|  |  |  | import '../../models/model.dart'; | 
					
						
							| 
									
										
										
										
											2022-08-03 22:03:31 +08:00
										 |  |  | import '../../models/platform_model.dart'; | 
					
						
							| 
									
										
										
										
											2022-08-29 18:48:12 +08:00
										 |  |  | import '../../common/shared_state.dart'; | 
					
						
							| 
									
										
										
										
											2023-02-09 23:00:34 +09:00
										 |  |  | import '../../utils/image.dart'; | 
					
						
							| 
									
										
										
										
											2023-03-15 18:31:53 +01:00
										 |  |  | import '../widgets/remote_toolbar.dart'; | 
					
						
							| 
									
										
										
										
											2022-12-27 16:45:13 +08:00
										 |  |  | import '../widgets/kb_layout_type_chooser.dart'; | 
					
						
							| 
									
										
										
										
											2022-05-29 17:19:50 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-13 19:10:55 -07:00
										 |  |  | bool _isCustomCursorInited = false; | 
					
						
							| 
									
										
										
										
											2022-10-03 22:03:49 -07:00
										 |  |  | final SimpleWrapper<bool> _firstEnterImage = SimpleWrapper(false); | 
					
						
							| 
									
										
										
										
											2022-09-13 19:10:55 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-29 17:19:50 +08:00
										 |  |  | class RemotePage extends StatefulWidget { | 
					
						
							| 
									
										
										
										
											2022-11-03 21:58:25 +08:00
										 |  |  |   RemotePage({ | 
					
						
							| 
									
										
										
										
											2022-08-26 23:28:08 +08:00
										 |  |  |     Key? key, | 
					
						
							|  |  |  |     required this.id, | 
					
						
							| 
									
										
										
										
											2022-11-10 14:32:22 +08:00
										 |  |  |     required this.menubarState, | 
					
						
							| 
									
										
										
										
											2023-01-17 13:28:33 +08:00
										 |  |  |     this.switchUuid, | 
					
						
							| 
									
										
										
										
											2023-02-13 16:40:24 +08:00
										 |  |  |     this.forceRelay, | 
					
						
							| 
									
										
										
										
											2022-08-26 23:28:08 +08:00
										 |  |  |   }) : super(key: key); | 
					
						
							| 
									
										
										
										
											2022-05-29 17:19:50 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   final String id; | 
					
						
							| 
									
										
										
										
											2022-11-10 14:32:22 +08:00
										 |  |  |   final MenubarState menubarState; | 
					
						
							| 
									
										
										
										
											2023-01-17 13:28:33 +08:00
										 |  |  |   final String? switchUuid; | 
					
						
							| 
									
										
										
										
											2023-02-13 16:40:24 +08:00
										 |  |  |   final bool? forceRelay; | 
					
						
							| 
									
										
										
										
											2022-11-03 21:58:25 +08:00
										 |  |  |   final SimpleWrapper<State<RemotePage>?> _lastState = SimpleWrapper(null); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   FFI get ffi => (_lastState.value! as _RemotePageState)._ffi; | 
					
						
							| 
									
										
										
										
											2022-05-29 17:19:50 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							| 
									
										
										
										
											2022-11-03 21:58:25 +08:00
										 |  |  |   State<RemotePage> createState() { | 
					
						
							|  |  |  |     final state = _RemotePageState(); | 
					
						
							|  |  |  |     _lastState.value = state; | 
					
						
							|  |  |  |     return state; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-05-29 17:19:50 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-17 22:21:49 +08:00
										 |  |  | class _RemotePageState extends State<RemotePage> | 
					
						
							| 
									
										
										
										
											2022-12-21 10:40:43 +08:00
										 |  |  |     with AutomaticKeepAliveClientMixin, MultiWindowListener { | 
					
						
							| 
									
										
										
										
											2022-05-29 17:19:50 +08:00
										 |  |  |   Timer? _timer; | 
					
						
							| 
									
										
										
										
											2022-09-05 10:18:29 -04:00
										 |  |  |   String keyboardMode = "legacy"; | 
					
						
							| 
									
										
										
										
											2022-12-21 22:39:30 +08:00
										 |  |  |   bool _isWindowBlur = false; | 
					
						
							| 
									
										
										
										
											2022-08-29 18:48:12 +08:00
										 |  |  |   final _cursorOverImage = false.obs; | 
					
						
							| 
									
										
										
										
											2022-09-03 10:39:33 +08:00
										 |  |  |   late RxBool _showRemoteCursor; | 
					
						
							| 
									
										
										
										
											2022-11-16 18:07:58 +08:00
										 |  |  |   late RxBool _zoomCursor; | 
					
						
							| 
									
										
										
										
											2022-09-06 21:20:53 -07:00
										 |  |  |   late RxBool _remoteCursorMoved; | 
					
						
							| 
									
										
										
										
											2022-09-03 10:39:33 +08:00
										 |  |  |   late RxBool _keyboardEnabled; | 
					
						
							| 
									
										
										
										
											2023-02-19 15:25:30 +08:00
										 |  |  |   late RxInt _textureId; | 
					
						
							|  |  |  |   late int _textureKey; | 
					
						
							| 
									
										
										
										
											2023-02-21 23:46:13 +08:00
										 |  |  |   final useTextureRender = bind.mainUseTextureRender(); | 
					
						
							| 
									
										
										
										
											2022-05-29 17:19:50 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-08 22:01:15 +09:00
										 |  |  |   final _blockableOverlayState = BlockableOverlayState(); | 
					
						
							| 
									
										
										
										
											2023-02-07 00:11:48 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-21 10:40:43 +08:00
										 |  |  |   final FocusNode _rawKeyFocusNode = FocusNode(debugLabel: "rawkeyFocusNode"); | 
					
						
							| 
									
										
										
										
											2022-05-29 17:19:50 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-13 19:10:55 -07:00
										 |  |  |   Function(bool)? _onEnterOrLeaveImage4Menubar; | 
					
						
							| 
									
										
										
										
											2022-09-13 06:59:06 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-12 18:42:02 +08:00
										 |  |  |   late FFI _ffi; | 
					
						
							| 
									
										
										
										
											2022-06-13 21:07:26 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-29 18:48:12 +08:00
										 |  |  |   void _initStates(String id) { | 
					
						
							|  |  |  |     PrivacyModeState.init(id); | 
					
						
							|  |  |  |     BlockInputState.init(id); | 
					
						
							|  |  |  |     CurrentDisplayState.init(id); | 
					
						
							| 
									
										
										
										
											2022-09-03 10:39:33 +08:00
										 |  |  |     KeyboardEnabledState.init(id); | 
					
						
							|  |  |  |     ShowRemoteCursorState.init(id); | 
					
						
							| 
									
										
										
										
											2022-09-06 21:20:53 -07:00
										 |  |  |     RemoteCursorMovedState.init(id); | 
					
						
							| 
									
										
										
										
											2022-11-16 18:07:58 +08:00
										 |  |  |     final optZoomCursor = 'zoom-cursor'; | 
					
						
							| 
									
										
										
										
											2022-11-22 22:12:10 +08:00
										 |  |  |     PeerBoolOption.init(id, optZoomCursor, () => false); | 
					
						
							| 
									
										
										
										
											2022-11-16 18:07:58 +08:00
										 |  |  |     _zoomCursor = PeerBoolOption.find(id, optZoomCursor); | 
					
						
							| 
									
										
										
										
											2022-09-03 10:39:33 +08:00
										 |  |  |     _showRemoteCursor = ShowRemoteCursorState.find(id); | 
					
						
							|  |  |  |     _keyboardEnabled = KeyboardEnabledState.find(id); | 
					
						
							| 
									
										
										
										
											2022-09-06 21:20:53 -07:00
										 |  |  |     _remoteCursorMoved = RemoteCursorMovedState.find(id); | 
					
						
							| 
									
										
										
										
											2023-02-19 15:25:30 +08:00
										 |  |  |     _textureKey = newTextureId; | 
					
						
							|  |  |  |     _textureId = RxInt(-1); | 
					
						
							| 
									
										
										
										
											2022-08-29 18:48:12 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void _removeStates(String id) { | 
					
						
							|  |  |  |     PrivacyModeState.delete(id); | 
					
						
							|  |  |  |     BlockInputState.delete(id); | 
					
						
							|  |  |  |     CurrentDisplayState.delete(id); | 
					
						
							| 
									
										
										
										
											2022-09-03 10:39:33 +08:00
										 |  |  |     ShowRemoteCursorState.delete(id); | 
					
						
							|  |  |  |     KeyboardEnabledState.delete(id); | 
					
						
							| 
									
										
										
										
											2022-09-06 21:20:53 -07:00
										 |  |  |     RemoteCursorMovedState.delete(id); | 
					
						
							| 
									
										
										
										
											2022-08-29 18:48:12 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-29 17:19:50 +08:00
										 |  |  |   @override | 
					
						
							|  |  |  |   void initState() { | 
					
						
							|  |  |  |     super.initState(); | 
					
						
							| 
									
										
										
										
											2022-09-03 10:39:33 +08:00
										 |  |  |     _initStates(widget.id); | 
					
						
							| 
									
										
										
										
											2022-08-12 18:42:02 +08:00
										 |  |  |     _ffi = FFI(); | 
					
						
							|  |  |  |     Get.put(_ffi, tag: widget.id); | 
					
						
							| 
									
										
										
										
											2022-12-27 16:45:13 +08:00
										 |  |  |     _ffi.imageModel.addCallbackOnFirstImage((String peerId) { | 
					
						
							|  |  |  |       showKBLayoutTypeChooserIfNeeded( | 
					
						
							|  |  |  |           _ffi.ffiModel.pi.platform, _ffi.dialogManager); | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2023-01-17 13:28:33 +08:00
										 |  |  |     _ffi.start( | 
					
						
							|  |  |  |       widget.id, | 
					
						
							|  |  |  |       switchUuid: widget.switchUuid, | 
					
						
							| 
									
										
										
										
											2023-02-13 16:40:24 +08:00
										 |  |  |       forceRelay: widget.forceRelay, | 
					
						
							| 
									
										
										
										
											2023-01-17 13:28:33 +08:00
										 |  |  |     ); | 
					
						
							| 
									
										
										
										
											2022-05-29 17:19:50 +08:00
										 |  |  |     WidgetsBinding.instance.addPostFrameCallback((_) { | 
					
						
							|  |  |  |       SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []); | 
					
						
							| 
									
										
										
										
											2022-08-12 18:42:02 +08:00
										 |  |  |       _ffi.dialogManager | 
					
						
							| 
									
										
										
										
											2022-08-16 21:27:21 +08:00
										 |  |  |           .showLoading(translate('Connecting...'), onCancel: closeConnection); | 
					
						
							| 
									
										
										
										
											2022-05-29 17:19:50 +08:00
										 |  |  |     }); | 
					
						
							|  |  |  |     if (!Platform.isLinux) { | 
					
						
							|  |  |  |       Wakelock.enable(); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-02-19 15:25:30 +08:00
										 |  |  |     // Register texture.
 | 
					
						
							|  |  |  |     _textureId.value = -1; | 
					
						
							| 
									
										
										
										
											2023-02-23 16:30:12 +08:00
										 |  |  |     if (useTextureRender) { | 
					
						
							|  |  |  |       textureRenderer.createTexture(_textureKey).then((id) async { | 
					
						
							|  |  |  |         debugPrint("id: $id, texture_key: $_textureKey"); | 
					
						
							|  |  |  |         if (id != -1) { | 
					
						
							|  |  |  |           final ptr = await textureRenderer.getTexturePtr(_textureKey); | 
					
						
							|  |  |  |           platformFFI.registerTexture(widget.id, ptr); | 
					
						
							|  |  |  |           _textureId.value = id; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-08-12 18:42:02 +08:00
										 |  |  |     _ffi.ffiModel.updateEventListener(widget.id); | 
					
						
							| 
									
										
										
										
											2022-08-15 20:26:20 +08:00
										 |  |  |     _ffi.qualityMonitorModel.checkShowQualityMonitor(widget.id); | 
					
						
							| 
									
										
										
										
											2022-11-22 22:12:10 +08:00
										 |  |  |     // Session option should be set after models.dart/FFI.start
 | 
					
						
							| 
									
										
										
										
											2023-03-17 11:27:22 +08:00
										 |  |  |     _showRemoteCursor.value = bind.sessionGetToggleOptionSync( | 
					
						
							|  |  |  |         id: widget.id, arg: 'show-remote-cursor'); | 
					
						
							| 
									
										
										
										
											2022-11-22 22:12:10 +08:00
										 |  |  |     _zoomCursor.value = | 
					
						
							|  |  |  |         bind.sessionGetToggleOptionSync(id: widget.id, arg: 'zoom-cursor'); | 
					
						
							| 
									
										
										
										
											2022-12-21 10:40:43 +08:00
										 |  |  |     DesktopMultiWindow.addListener(this); | 
					
						
							| 
									
										
										
										
											2022-12-11 14:17:29 +08:00
										 |  |  |     // if (!_isCustomCursorInited) {
 | 
					
						
							|  |  |  |     //   customCursorController.registerNeedUpdateCursorCallback(
 | 
					
						
							|  |  |  |     //       (String? lastKey, String? currentKey) async {
 | 
					
						
							|  |  |  |     //     if (_firstEnterImage.value) {
 | 
					
						
							|  |  |  |     //       _firstEnterImage.value = false;
 | 
					
						
							|  |  |  |     //       return true;
 | 
					
						
							|  |  |  |     //     }
 | 
					
						
							|  |  |  |     //     return lastKey == null || lastKey != currentKey;
 | 
					
						
							|  |  |  |     //   });
 | 
					
						
							|  |  |  |     //   _isCustomCursorInited = true;
 | 
					
						
							|  |  |  |     // }
 | 
					
						
							| 
									
										
										
										
											2023-02-07 00:11:48 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-08 22:01:15 +09:00
										 |  |  |     _ffi.dialogManager.setOverlayState(_blockableOverlayState); | 
					
						
							|  |  |  |     _ffi.chatModel.setOverlayState(_blockableOverlayState); | 
					
						
							| 
									
										
										
										
											2023-02-07 00:11:48 +09:00
										 |  |  |     // make remote page penetrable automatically, effective for chat over remote
 | 
					
						
							| 
									
										
										
										
											2023-02-08 22:01:15 +09:00
										 |  |  |     _blockableOverlayState.onMiddleBlockedClick = () { | 
					
						
							|  |  |  |       _blockableOverlayState.setMiddleBlocked(false); | 
					
						
							| 
									
										
										
										
											2023-02-07 00:11:48 +09:00
										 |  |  |     }; | 
					
						
							| 
									
										
										
										
											2022-05-29 17:19:50 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-21 10:40:43 +08:00
										 |  |  |   @override | 
					
						
							|  |  |  |   void onWindowBlur() { | 
					
						
							|  |  |  |     super.onWindowBlur(); | 
					
						
							| 
									
										
										
										
											2022-12-22 19:54:04 +08:00
										 |  |  |     // On windows, we use `focus` way to handle keyboard better.
 | 
					
						
							|  |  |  |     // Now on Linux, there's some rdev issues which will break the input.
 | 
					
						
							|  |  |  |     // We disable the `focus` way for non-Windows temporarily.
 | 
					
						
							|  |  |  |     if (Platform.isWindows) { | 
					
						
							|  |  |  |       _isWindowBlur = true; | 
					
						
							|  |  |  |       // unfocus the primary-focus when the whole window is lost focus,
 | 
					
						
							|  |  |  |       // and let OS to handle events instead.
 | 
					
						
							|  |  |  |       _rawKeyFocusNode.unfocus(); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-12-21 10:40:43 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-21 22:39:30 +08:00
										 |  |  |   @override | 
					
						
							|  |  |  |   void onWindowFocus() { | 
					
						
							|  |  |  |     super.onWindowFocus(); | 
					
						
							| 
									
										
										
										
											2022-12-22 19:54:04 +08:00
										 |  |  |     // See [onWindowBlur].
 | 
					
						
							|  |  |  |     if (Platform.isWindows) { | 
					
						
							|  |  |  |       _isWindowBlur = false; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-12-21 22:39:30 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-27 16:18:28 +08:00
										 |  |  |   @override | 
					
						
							|  |  |  |   void onWindowRestore() { | 
					
						
							|  |  |  |     super.onWindowRestore(); | 
					
						
							|  |  |  |     // On windows, we use `onWindowRestore` way to handle window restore from
 | 
					
						
							|  |  |  |     // a minimized state.
 | 
					
						
							|  |  |  |     if (Platform.isWindows) { | 
					
						
							|  |  |  |       _isWindowBlur = false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-29 17:19:50 +08:00
										 |  |  |   @override | 
					
						
							|  |  |  |   void dispose() { | 
					
						
							| 
									
										
										
										
											2022-08-29 18:48:12 +08:00
										 |  |  |     debugPrint("REMOTE PAGE dispose ${widget.id}"); | 
					
						
							| 
									
										
										
										
											2023-02-23 16:30:12 +08:00
										 |  |  |     if (useTextureRender) { | 
					
						
							|  |  |  |       platformFFI.registerTexture(widget.id, 0); | 
					
						
							|  |  |  |       textureRenderer.closeTexture(_textureKey); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-12-22 19:54:04 +08:00
										 |  |  |     // ensure we leave this session, this is a double check
 | 
					
						
							|  |  |  |     bind.sessionEnterOrLeave(id: widget.id, enter: false); | 
					
						
							| 
									
										
										
										
											2022-12-21 10:40:43 +08:00
										 |  |  |     DesktopMultiWindow.removeListener(this); | 
					
						
							| 
									
										
										
										
											2022-09-08 22:18:02 +08:00
										 |  |  |     _ffi.dialogManager.hideMobileActionsOverlay(); | 
					
						
							| 
									
										
										
										
											2022-09-21 16:03:08 +08:00
										 |  |  |     _ffi.recordingModel.onClose(); | 
					
						
							| 
									
										
										
										
											2022-09-13 21:36:38 +08:00
										 |  |  |     _rawKeyFocusNode.dispose(); | 
					
						
							| 
									
										
										
										
											2022-06-13 21:07:26 +08:00
										 |  |  |     _ffi.close(); | 
					
						
							| 
									
										
										
										
											2022-05-29 17:19:50 +08:00
										 |  |  |     _timer?.cancel(); | 
					
						
							| 
									
										
										
										
											2022-08-12 18:42:02 +08:00
										 |  |  |     _ffi.dialogManager.dismissAll(); | 
					
						
							| 
									
										
										
										
											2022-05-29 17:19:50 +08:00
										 |  |  |     SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, | 
					
						
							|  |  |  |         overlays: SystemUiOverlay.values); | 
					
						
							|  |  |  |     if (!Platform.isLinux) { | 
					
						
							|  |  |  |       Wakelock.disable(); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-06-13 21:07:26 +08:00
										 |  |  |     Get.delete<FFI>(tag: widget.id); | 
					
						
							| 
									
										
										
										
											2022-05-29 17:19:50 +08:00
										 |  |  |     super.dispose(); | 
					
						
							| 
									
										
										
										
											2022-08-29 18:48:12 +08:00
										 |  |  |     _removeStates(widget.id); | 
					
						
							| 
									
										
										
										
											2022-05-29 17:19:50 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-03 10:39:33 +08:00
										 |  |  |   Widget buildBody(BuildContext context) { | 
					
						
							| 
									
										
										
										
											2022-08-06 18:48:07 +08:00
										 |  |  |     return Scaffold( | 
					
						
							| 
									
										
										
										
											2023-02-23 16:49:31 +01:00
										 |  |  |       backgroundColor: Theme.of(context).colorScheme.background, | 
					
						
							| 
									
										
										
										
											2023-02-08 22:01:15 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  |       /// the Overlay key will be set with _blockableOverlayState in BlockableOverlay
 | 
					
						
							|  |  |  |       /// see override build() in [BlockableOverlay]
 | 
					
						
							|  |  |  |       body: BlockableOverlay( | 
					
						
							|  |  |  |         state: _blockableOverlayState, | 
					
						
							| 
									
										
										
										
											2023-02-07 00:11:48 +09:00
										 |  |  |         underlying: Container( | 
					
						
							|  |  |  |             color: Colors.black, | 
					
						
							|  |  |  |             child: RawKeyFocusScope( | 
					
						
							|  |  |  |                 focusNode: _rawKeyFocusNode, | 
					
						
							|  |  |  |                 onFocusChange: (bool imageFocused) { | 
					
						
							|  |  |  |                   debugPrint( | 
					
						
							|  |  |  |                       "onFocusChange(window active:${!_isWindowBlur}) $imageFocused"); | 
					
						
							|  |  |  |                   // See [onWindowBlur].
 | 
					
						
							|  |  |  |                   if (Platform.isWindows) { | 
					
						
							|  |  |  |                     if (_isWindowBlur) { | 
					
						
							|  |  |  |                       imageFocused = false; | 
					
						
							|  |  |  |                       Future.delayed(Duration.zero, () { | 
					
						
							|  |  |  |                         _rawKeyFocusNode.unfocus(); | 
					
						
							|  |  |  |                       }); | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                     if (imageFocused) { | 
					
						
							|  |  |  |                       _ffi.inputModel.enterOrLeave(true); | 
					
						
							|  |  |  |                     } else { | 
					
						
							|  |  |  |                       _ffi.inputModel.enterOrLeave(false); | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                   } | 
					
						
							|  |  |  |                 }, | 
					
						
							|  |  |  |                 inputModel: _ffi.inputModel, | 
					
						
							|  |  |  |                 child: getBodyForDesktop(context))), | 
					
						
							|  |  |  |         upperLayer: [ | 
					
						
							|  |  |  |           OverlayEntry( | 
					
						
							|  |  |  |               builder: (context) => RemoteMenubar( | 
					
						
							|  |  |  |                     id: widget.id, | 
					
						
							|  |  |  |                     ffi: _ffi, | 
					
						
							|  |  |  |                     state: widget.menubarState, | 
					
						
							|  |  |  |                     onEnterOrLeaveImageSetter: (func) => | 
					
						
							|  |  |  |                         _onEnterOrLeaveImage4Menubar = func, | 
					
						
							|  |  |  |                     onEnterOrLeaveImageCleaner: () => | 
					
						
							|  |  |  |                         _onEnterOrLeaveImage4Menubar = null, | 
					
						
							|  |  |  |                   )) | 
					
						
							|  |  |  |         ], | 
					
						
							|  |  |  |       ), | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2022-08-06 18:48:07 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-29 17:19:50 +08:00
										 |  |  |   @override | 
					
						
							|  |  |  |   Widget build(BuildContext context) { | 
					
						
							| 
									
										
										
										
											2022-06-17 22:21:49 +08:00
										 |  |  |     super.build(context); | 
					
						
							| 
									
										
										
										
											2022-05-29 17:19:50 +08:00
										 |  |  |     return WillPopScope( | 
					
						
							| 
									
										
										
										
											2022-06-17 22:21:49 +08:00
										 |  |  |         onWillPop: () async { | 
					
						
							| 
									
										
										
										
											2022-11-15 16:49:55 +08:00
										 |  |  |           clientClose(widget.id, _ffi.dialogManager); | 
					
						
							| 
									
										
										
										
											2022-06-17 00:06:49 +08:00
										 |  |  |           return false; | 
					
						
							|  |  |  |         }, | 
					
						
							| 
									
										
										
										
											2022-09-03 10:39:33 +08:00
										 |  |  |         child: MultiProvider(providers: [ | 
					
						
							|  |  |  |           ChangeNotifierProvider.value(value: _ffi.ffiModel), | 
					
						
							|  |  |  |           ChangeNotifierProvider.value(value: _ffi.imageModel), | 
					
						
							|  |  |  |           ChangeNotifierProvider.value(value: _ffi.cursorModel), | 
					
						
							|  |  |  |           ChangeNotifierProvider.value(value: _ffi.canvasModel), | 
					
						
							| 
									
										
										
										
											2022-09-15 17:31:28 +08:00
										 |  |  |           ChangeNotifierProvider.value(value: _ffi.recordingModel), | 
					
						
							| 
									
										
										
										
											2022-09-03 10:39:33 +08:00
										 |  |  |         ], child: buildBody(context))); | 
					
						
							| 
									
										
										
										
											2022-05-29 17:19:50 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-05 06:19:05 -04:00
										 |  |  |   void enterView(PointerEnterEvent evt) { | 
					
						
							|  |  |  |     _cursorOverImage.value = true; | 
					
						
							| 
									
										
										
										
											2022-10-03 22:03:49 -07:00
										 |  |  |     _firstEnterImage.value = true; | 
					
						
							| 
									
										
										
										
											2022-09-13 19:10:55 -07:00
										 |  |  |     if (_onEnterOrLeaveImage4Menubar != null) { | 
					
						
							|  |  |  |       try { | 
					
						
							|  |  |  |         _onEnterOrLeaveImage4Menubar!(true); | 
					
						
							|  |  |  |       } catch (e) { | 
					
						
							|  |  |  |         //
 | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2022-09-13 06:59:06 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-12-22 19:54:04 +08:00
										 |  |  |     // See [onWindowBlur].
 | 
					
						
							|  |  |  |     if (!Platform.isWindows) { | 
					
						
							|  |  |  |       if (!_rawKeyFocusNode.hasFocus) { | 
					
						
							|  |  |  |         _rawKeyFocusNode.requestFocus(); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       bind.sessionEnterOrLeave(id: widget.id, enter: true); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-09-05 06:19:05 -04:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void leaveView(PointerExitEvent evt) { | 
					
						
							| 
									
										
										
										
											2023-03-13 21:03:43 +08:00
										 |  |  |     if (_ffi.ffiModel.keyboard()) { | 
					
						
							| 
									
										
										
										
											2023-03-13 21:05:41 +08:00
										 |  |  |       _ffi.inputModel.tryMoveEdgeOnExit(evt.position); | 
					
						
							| 
									
										
										
										
											2023-03-13 21:03:43 +08:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-05 06:19:05 -04:00
										 |  |  |     _cursorOverImage.value = false; | 
					
						
							| 
									
										
										
										
											2022-10-03 22:03:49 -07:00
										 |  |  |     _firstEnterImage.value = false; | 
					
						
							| 
									
										
										
										
											2022-09-13 19:10:55 -07:00
										 |  |  |     if (_onEnterOrLeaveImage4Menubar != null) { | 
					
						
							|  |  |  |       try { | 
					
						
							|  |  |  |         _onEnterOrLeaveImage4Menubar!(false); | 
					
						
							|  |  |  |       } catch (e) { | 
					
						
							|  |  |  |         //
 | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2022-09-13 06:59:06 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-12-22 19:54:04 +08:00
										 |  |  |     // See [onWindowBlur].
 | 
					
						
							|  |  |  |     if (!Platform.isWindows) { | 
					
						
							|  |  |  |       bind.sessionEnterOrLeave(id: widget.id, enter: false); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-09-05 06:19:05 -04:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-28 21:11:03 +08:00
										 |  |  |   Widget _buildRawPointerMouseRegion( | 
					
						
							|  |  |  |     Widget child, | 
					
						
							|  |  |  |     PointerEnterEventListener? onEnter, | 
					
						
							|  |  |  |     PointerExitEventListener? onExit, | 
					
						
							|  |  |  |   ) { | 
					
						
							|  |  |  |     return RawPointerMouseRegion( | 
					
						
							| 
									
										
										
										
											2023-03-13 21:03:43 +08:00
										 |  |  |       onEnter: onEnter, | 
					
						
							|  |  |  |       onExit: onExit, | 
					
						
							| 
									
										
										
										
											2023-01-28 21:11:03 +08:00
										 |  |  |       onPointerDown: (event) { | 
					
						
							|  |  |  |         // A double check for blur status.
 | 
					
						
							|  |  |  |         // Note: If there's an `onPointerDown` event is triggered, `_isWindowBlur` is expected being false.
 | 
					
						
							|  |  |  |         // Sometimes the system does not send the necessary focus event to flutter. We should manually
 | 
					
						
							|  |  |  |         // handle this inconsistent status by setting `_isWindowBlur` to false. So we can
 | 
					
						
							|  |  |  |         // ensure the grab-key thread is running when our users are clicking the remote canvas.
 | 
					
						
							|  |  |  |         if (_isWindowBlur) { | 
					
						
							|  |  |  |           debugPrint( | 
					
						
							|  |  |  |               "Unexpected status: onPointerDown is triggered while the remote window is in blur status"); | 
					
						
							|  |  |  |           _isWindowBlur = false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (!_rawKeyFocusNode.hasFocus) { | 
					
						
							|  |  |  |           _rawKeyFocusNode.requestFocus(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       inputModel: _ffi.inputModel, | 
					
						
							|  |  |  |       child: child, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-03 10:39:33 +08:00
										 |  |  |   Widget getBodyForDesktop(BuildContext context) { | 
					
						
							| 
									
										
										
										
											2022-06-13 21:07:26 +08:00
										 |  |  |     var paints = <Widget>[ | 
					
						
							| 
									
										
										
										
											2022-08-11 16:03:04 +08:00
										 |  |  |       MouseRegion(onEnter: (evt) { | 
					
						
							|  |  |  |         bind.hostStopSystemKeyPropagate(stopped: false); | 
					
						
							|  |  |  |       }, onExit: (evt) { | 
					
						
							|  |  |  |         bind.hostStopSystemKeyPropagate(stopped: true); | 
					
						
							| 
									
										
										
										
											2022-09-03 10:39:33 +08:00
										 |  |  |       }, child: LayoutBuilder(builder: (context, constraints) { | 
					
						
							|  |  |  |         Future.delayed(Duration.zero, () { | 
					
						
							|  |  |  |           Provider.of<CanvasModel>(context, listen: false).updateViewStyle(); | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |         return ImagePaint( | 
					
						
							|  |  |  |           id: widget.id, | 
					
						
							| 
									
										
										
										
											2022-11-16 18:07:58 +08:00
										 |  |  |           zoomCursor: _zoomCursor, | 
					
						
							| 
									
										
										
										
											2022-09-03 10:39:33 +08:00
										 |  |  |           cursorOverImage: _cursorOverImage, | 
					
						
							|  |  |  |           keyboardEnabled: _keyboardEnabled, | 
					
						
							| 
									
										
										
										
											2022-09-06 21:20:53 -07:00
										 |  |  |           remoteCursorMoved: _remoteCursorMoved, | 
					
						
							| 
									
										
										
										
											2023-02-19 15:25:30 +08:00
										 |  |  |           textureId: _textureId, | 
					
						
							| 
									
										
										
										
											2023-02-21 23:46:13 +08:00
										 |  |  |           useTextureRender: useTextureRender, | 
					
						
							| 
									
										
										
										
											2023-01-28 21:11:03 +08:00
										 |  |  |           listenerBuilder: (child) => | 
					
						
							|  |  |  |               _buildRawPointerMouseRegion(child, enterView, leaveView), | 
					
						
							| 
									
										
										
										
											2022-09-03 10:39:33 +08:00
										 |  |  |         ); | 
					
						
							|  |  |  |       })) | 
					
						
							| 
									
										
										
										
											2022-06-13 21:07:26 +08:00
										 |  |  |     ]; | 
					
						
							| 
									
										
										
										
											2022-09-03 10:39:33 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-31 21:41:16 +08:00
										 |  |  |     if (!_ffi.canvasModel.cursorEmbedded) { | 
					
						
							| 
									
										
										
										
											2022-12-30 16:14:30 +08:00
										 |  |  |       paints.add(Obx(() => Offstage( | 
					
						
							|  |  |  |           offstage: _showRemoteCursor.isFalse || _remoteCursorMoved.isFalse, | 
					
						
							| 
									
										
										
										
											2022-11-29 16:36:35 +08:00
										 |  |  |           child: CursorPaint( | 
					
						
							|  |  |  |             id: widget.id, | 
					
						
							|  |  |  |             zoomCursor: _zoomCursor, | 
					
						
							|  |  |  |           )))); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-01-28 21:11:03 +08:00
										 |  |  |     paints.add( | 
					
						
							|  |  |  |       Positioned( | 
					
						
							|  |  |  |         top: 10, | 
					
						
							|  |  |  |         right: 10, | 
					
						
							|  |  |  |         child: _buildRawPointerMouseRegion( | 
					
						
							|  |  |  |             QualityMonitor(_ffi.qualityMonitorModel), null, null), | 
					
						
							|  |  |  |       ), | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2022-08-11 10:19:12 +08:00
										 |  |  |     return Stack( | 
					
						
							|  |  |  |       children: paints, | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2022-05-29 17:19:50 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-17 22:21:49 +08:00
										 |  |  |   @override | 
					
						
							|  |  |  |   bool get wantKeepAlive => true; | 
					
						
							| 
									
										
										
										
											2022-05-29 17:19:50 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-14 16:16:38 +08:00
										 |  |  | class ImagePaint extends StatefulWidget { | 
					
						
							| 
									
										
										
										
											2022-06-13 21:07:26 +08:00
										 |  |  |   final String id; | 
					
						
							| 
									
										
										
										
											2023-02-02 12:39:39 +08:00
										 |  |  |   final RxBool zoomCursor; | 
					
						
							|  |  |  |   final RxBool cursorOverImage; | 
					
						
							|  |  |  |   final RxBool keyboardEnabled; | 
					
						
							|  |  |  |   final RxBool remoteCursorMoved; | 
					
						
							| 
									
										
										
										
											2023-02-19 15:25:30 +08:00
										 |  |  |   final RxInt textureId; | 
					
						
							| 
									
										
										
										
											2023-02-21 23:46:13 +08:00
										 |  |  |   final bool useTextureRender; | 
					
						
							| 
									
										
										
										
											2022-08-13 17:58:24 +08:00
										 |  |  |   final Widget Function(Widget)? listenerBuilder; | 
					
						
							| 
									
										
										
										
											2022-06-13 21:07:26 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-13 17:58:24 +08:00
										 |  |  |   ImagePaint( | 
					
						
							|  |  |  |       {Key? key, | 
					
						
							|  |  |  |       required this.id, | 
					
						
							| 
									
										
										
										
											2022-11-16 18:07:58 +08:00
										 |  |  |       required this.zoomCursor, | 
					
						
							| 
									
										
										
										
											2022-08-13 17:58:24 +08:00
										 |  |  |       required this.cursorOverImage, | 
					
						
							| 
									
										
										
										
											2022-09-03 10:39:33 +08:00
										 |  |  |       required this.keyboardEnabled, | 
					
						
							| 
									
										
										
										
											2022-09-06 21:20:53 -07:00
										 |  |  |       required this.remoteCursorMoved, | 
					
						
							| 
									
										
										
										
											2023-02-19 15:25:30 +08:00
										 |  |  |       required this.textureId, | 
					
						
							| 
									
										
										
										
											2023-02-21 23:46:13 +08:00
										 |  |  |       required this.useTextureRender, | 
					
						
							| 
									
										
										
										
											2022-09-01 22:36:40 -07:00
										 |  |  |       this.listenerBuilder}) | 
					
						
							| 
									
										
										
										
											2022-08-13 17:58:24 +08:00
										 |  |  |       : super(key: key); | 
					
						
							| 
									
										
										
										
											2022-06-13 21:07:26 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-14 16:16:38 +08:00
										 |  |  |   @override | 
					
						
							|  |  |  |   State<StatefulWidget> createState() => _ImagePaintState(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class _ImagePaintState extends State<ImagePaint> { | 
					
						
							|  |  |  |   bool _lastRemoteCursorMoved = false; | 
					
						
							|  |  |  |   final ScrollController _horizontal = ScrollController(); | 
					
						
							|  |  |  |   final ScrollController _vertical = ScrollController(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   String get id => widget.id; | 
					
						
							| 
									
										
										
										
											2023-02-02 12:39:39 +08:00
										 |  |  |   RxBool get zoomCursor => widget.zoomCursor; | 
					
						
							|  |  |  |   RxBool get cursorOverImage => widget.cursorOverImage; | 
					
						
							|  |  |  |   RxBool get keyboardEnabled => widget.keyboardEnabled; | 
					
						
							|  |  |  |   RxBool get remoteCursorMoved => widget.remoteCursorMoved; | 
					
						
							| 
									
										
										
										
											2022-11-14 16:16:38 +08:00
										 |  |  |   Widget Function(Widget)? get listenerBuilder => widget.listenerBuilder; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-29 17:19:50 +08:00
										 |  |  |   @override | 
					
						
							|  |  |  |   Widget build(BuildContext context) { | 
					
						
							| 
									
										
										
										
											2022-06-17 00:06:49 +08:00
										 |  |  |     final m = Provider.of<ImageModel>(context); | 
					
						
							| 
									
										
										
										
											2022-08-12 20:14:53 +08:00
										 |  |  |     var c = Provider.of<CanvasModel>(context); | 
					
						
							|  |  |  |     final s = c.scale; | 
					
						
							| 
									
										
										
										
											2022-09-08 21:03:20 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-03 17:02:28 +08:00
										 |  |  |     mouseRegion({child}) => Obx(() { | 
					
						
							|  |  |  |           double getCursorScale() { | 
					
						
							|  |  |  |             var c = Provider.of<CanvasModel>(context); | 
					
						
							|  |  |  |             var cursorScale = 1.0; | 
					
						
							|  |  |  |             if (Platform.isWindows) { | 
					
						
							|  |  |  |               // debug win10
 | 
					
						
							|  |  |  |               final isViewAdaptive = | 
					
						
							|  |  |  |                   c.viewStyle.style == kRemoteViewStyleAdaptive; | 
					
						
							|  |  |  |               if (zoomCursor.value && isViewAdaptive) { | 
					
						
							|  |  |  |                 cursorScale = s * c.devicePixelRatio; | 
					
						
							|  |  |  |               } | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |               final isViewOriginal = | 
					
						
							|  |  |  |                   c.viewStyle.style == kRemoteViewStyleOriginal; | 
					
						
							|  |  |  |               if (zoomCursor.value || isViewOriginal) { | 
					
						
							|  |  |  |                 cursorScale = s; | 
					
						
							|  |  |  |               } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             return cursorScale; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           return MouseRegion( | 
					
						
							|  |  |  |               cursor: cursorOverImage.isTrue | 
					
						
							|  |  |  |                   ? c.cursorEmbedded | 
					
						
							|  |  |  |                       ? SystemMouseCursors.none | 
					
						
							|  |  |  |                       : keyboardEnabled.isTrue | 
					
						
							|  |  |  |                           ? (() { | 
					
						
							|  |  |  |                               if (remoteCursorMoved.isTrue) { | 
					
						
							|  |  |  |                                 _lastRemoteCursorMoved = true; | 
					
						
							|  |  |  |                                 return SystemMouseCursors.none; | 
					
						
							|  |  |  |                               } else { | 
					
						
							|  |  |  |                                 if (_lastRemoteCursorMoved) { | 
					
						
							|  |  |  |                                   _lastRemoteCursorMoved = false; | 
					
						
							|  |  |  |                                   _firstEnterImage.value = true; | 
					
						
							|  |  |  |                                 } | 
					
						
							|  |  |  |                                 return _buildCustomCursor( | 
					
						
							|  |  |  |                                     context, getCursorScale()); | 
					
						
							|  |  |  |                               } | 
					
						
							|  |  |  |                             }()) | 
					
						
							|  |  |  |                           : _buildDisabledCursor(context, getCursorScale()) | 
					
						
							|  |  |  |                   : MouseCursor.defer, | 
					
						
							|  |  |  |               onHover: (evt) {}, | 
					
						
							|  |  |  |               child: child); | 
					
						
							|  |  |  |         }); | 
					
						
							| 
									
										
										
										
											2022-09-08 21:03:20 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-06 20:25:18 +08:00
										 |  |  |     if (c.imageOverflow.isTrue && c.scrollStyle == ScrollStyle.scrollbar) { | 
					
						
							|  |  |  |       final imageWidth = c.getDisplayWidth() * s; | 
					
						
							|  |  |  |       final imageHeight = c.getDisplayHeight() * s; | 
					
						
							|  |  |  |       final imageSize = Size(imageWidth, imageHeight); | 
					
						
							| 
									
										
										
										
											2023-02-21 23:46:13 +08:00
										 |  |  |       late final Widget imageWidget; | 
					
						
							|  |  |  |       if (widget.useTextureRender) { | 
					
						
							|  |  |  |         imageWidget = SizedBox( | 
					
						
							|  |  |  |           width: imageWidth, | 
					
						
							|  |  |  |           height: imageHeight, | 
					
						
							|  |  |  |           child: Obx(() => Texture(textureId: widget.textureId.value)), | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         imageWidget = CustomPaint( | 
					
						
							|  |  |  |           size: imageSize, | 
					
						
							|  |  |  |           painter: ImagePainter(image: m.image, x: 0, y: 0, scale: s), | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2022-09-06 02:08:59 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-09 17:13:14 +08:00
										 |  |  |       return NotificationListener<ScrollNotification>( | 
					
						
							| 
									
										
										
										
											2023-01-06 18:14:31 +08:00
										 |  |  |           onNotification: (notification) { | 
					
						
							|  |  |  |             final percentX = _horizontal.hasClients | 
					
						
							|  |  |  |                 ? _horizontal.position.extentBefore / | 
					
						
							|  |  |  |                     (_horizontal.position.extentBefore + | 
					
						
							|  |  |  |                         _horizontal.position.extentInside + | 
					
						
							|  |  |  |                         _horizontal.position.extentAfter) | 
					
						
							|  |  |  |                 : 0.0; | 
					
						
							|  |  |  |             final percentY = _vertical.hasClients | 
					
						
							|  |  |  |                 ? _vertical.position.extentBefore / | 
					
						
							|  |  |  |                     (_vertical.position.extentBefore + | 
					
						
							|  |  |  |                         _vertical.position.extentInside + | 
					
						
							|  |  |  |                         _vertical.position.extentAfter) | 
					
						
							|  |  |  |                 : 0.0; | 
					
						
							|  |  |  |             c.setScrollPercent(percentX, percentY); | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |           }, | 
					
						
							|  |  |  |           child: mouseRegion( | 
					
						
							|  |  |  |             child: Obx(() => _buildCrossScrollbarFromLayout( | 
					
						
							|  |  |  |                 context, _buildListener(imageWidget), c.size, imageSize)), | 
					
						
							|  |  |  |           )); | 
					
						
							| 
									
										
										
										
											2022-08-11 00:12:47 +08:00
										 |  |  |     } else { | 
					
						
							| 
									
										
										
										
											2023-02-21 23:46:13 +08:00
										 |  |  |       late final Widget imageWidget; | 
					
						
							| 
									
										
										
										
											2023-02-21 21:56:46 +08:00
										 |  |  |       if (c.size.width > 0 && c.size.height > 0) { | 
					
						
							| 
									
										
										
										
											2023-02-21 23:46:13 +08:00
										 |  |  |         if (widget.useTextureRender) { | 
					
						
							|  |  |  |           imageWidget = Stack( | 
					
						
							|  |  |  |             children: [ | 
					
						
							|  |  |  |               Positioned( | 
					
						
							|  |  |  |                 left: c.x, | 
					
						
							|  |  |  |                 top: c.y, | 
					
						
							|  |  |  |                 width: c.getDisplayWidth() * s, | 
					
						
							|  |  |  |                 height: c.getDisplayHeight() * s, | 
					
						
							|  |  |  |                 child: Texture(textureId: widget.textureId.value), | 
					
						
							|  |  |  |               ) | 
					
						
							|  |  |  |             ], | 
					
						
							|  |  |  |           ); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |           imageWidget = CustomPaint( | 
					
						
							|  |  |  |             size: Size(c.size.width, c.size.height), | 
					
						
							|  |  |  |             painter: | 
					
						
							|  |  |  |                 ImagePainter(image: m.image, x: c.x / s, y: c.y / s, scale: s), | 
					
						
							|  |  |  |           ); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-02-21 21:56:46 +08:00
										 |  |  |         return mouseRegion(child: _buildListener(imageWidget)); | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         return Container(); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2022-08-11 00:12:47 +08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-05-29 17:19:50 +08:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-08-13 15:08:17 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-22 21:34:53 +08:00
										 |  |  |   MouseCursor _buildCursorOfCache( | 
					
						
							|  |  |  |       CursorModel cursor, double scale, CursorData? cache) { | 
					
						
							| 
									
										
										
										
											2022-10-30 14:38:35 +08:00
										 |  |  |     if (cache == null) { | 
					
						
							| 
									
										
										
										
											2022-09-07 19:52:30 -07:00
										 |  |  |       return MouseCursor.defer; | 
					
						
							|  |  |  |     } else { | 
					
						
							| 
									
										
										
										
											2023-02-02 14:39:58 +08:00
										 |  |  |       final key = cache.updateGetKey(scale); | 
					
						
							| 
									
										
										
										
											2022-12-11 14:17:29 +08:00
										 |  |  |       if (!cursor.cachedKeys.contains(key)) { | 
					
						
							|  |  |  |         debugPrint("Register custom cursor with key $key"); | 
					
						
							|  |  |  |         // [Safety]
 | 
					
						
							|  |  |  |         // It's ok to call async registerCursor in current synchronous context,
 | 
					
						
							|  |  |  |         // because activating the cursor is also an async call and will always
 | 
					
						
							|  |  |  |         // be executed after this.
 | 
					
						
							|  |  |  |         custom_cursor_manager.CursorManager.instance | 
					
						
							|  |  |  |             .registerCursor(custom_cursor_manager.CursorData() | 
					
						
							|  |  |  |               ..buffer = cache.data! | 
					
						
							|  |  |  |               ..height = (cache.height * cache.scale).toInt() | 
					
						
							|  |  |  |               ..width = (cache.width * cache.scale).toInt() | 
					
						
							|  |  |  |               ..hotX = cache.hotx | 
					
						
							|  |  |  |               ..hotY = cache.hoty | 
					
						
							|  |  |  |               ..name = key); | 
					
						
							|  |  |  |         cursor.addKey(key); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       return FlutterCustomMemoryImageCursor(key: key); | 
					
						
							| 
									
										
										
										
											2022-09-07 19:52:30 -07:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-22 21:34:53 +08:00
										 |  |  |   MouseCursor _buildCustomCursor(BuildContext context, double scale) { | 
					
						
							|  |  |  |     final cursor = Provider.of<CursorModel>(context); | 
					
						
							|  |  |  |     final cache = cursor.cache ?? preDefaultCursor.cache; | 
					
						
							|  |  |  |     return _buildCursorOfCache(cursor, scale, cache); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-16 12:29:51 +08:00
										 |  |  |   MouseCursor _buildDisabledCursor(BuildContext context, double scale) { | 
					
						
							|  |  |  |     final cursor = Provider.of<CursorModel>(context); | 
					
						
							| 
									
										
										
										
											2022-11-22 21:34:53 +08:00
										 |  |  |     final cache = preForbiddenCursor.cache; | 
					
						
							|  |  |  |     return _buildCursorOfCache(cursor, scale, cache); | 
					
						
							| 
									
										
										
										
											2022-10-16 12:29:51 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-09 17:13:14 +08:00
										 |  |  |   Widget _buildCrossScrollbarFromLayout( | 
					
						
							|  |  |  |       BuildContext context, Widget child, Size layoutSize, Size size) { | 
					
						
							| 
									
										
										
										
											2022-10-05 00:22:40 +08:00
										 |  |  |     final scrollConfig = CustomMouseWheelScrollConfig( | 
					
						
							|  |  |  |         scrollDuration: kDefaultScrollDuration, | 
					
						
							|  |  |  |         scrollCurve: Curves.linearToEaseOut, | 
					
						
							|  |  |  |         mouseWheelTurnsThrottleTimeMs: | 
					
						
							|  |  |  |             kDefaultMouseWheelThrottleDuration.inMilliseconds, | 
					
						
							|  |  |  |         scrollAmountMultiplier: kDefaultScrollAmountMultiplier); | 
					
						
							| 
									
										
										
										
											2022-10-09 17:13:14 +08:00
										 |  |  |     var widget = child; | 
					
						
							|  |  |  |     if (layoutSize.width < size.width) { | 
					
						
							|  |  |  |       widget = ScrollConfiguration( | 
					
						
							|  |  |  |         behavior: ScrollConfiguration.of(context).copyWith(scrollbars: false), | 
					
						
							|  |  |  |         child: SingleChildScrollView( | 
					
						
							|  |  |  |           controller: _horizontal, | 
					
						
							|  |  |  |           scrollDirection: Axis.horizontal, | 
					
						
							|  |  |  |           physics: cursorOverImage.isTrue | 
					
						
							|  |  |  |               ? const NeverScrollableScrollPhysics() | 
					
						
							|  |  |  |               : null, | 
					
						
							|  |  |  |           child: widget, | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       widget = Row( | 
					
						
							| 
									
										
										
										
											2022-11-30 22:56:22 +08:00
										 |  |  |         children: [ | 
					
						
							|  |  |  |           Container( | 
					
						
							|  |  |  |             width: ((layoutSize.width - size.width) ~/ 2).toDouble(), | 
					
						
							|  |  |  |           ), | 
					
						
							|  |  |  |           widget, | 
					
						
							|  |  |  |         ], | 
					
						
							| 
									
										
										
										
											2022-10-09 17:13:14 +08:00
										 |  |  |       ); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (layoutSize.height < size.height) { | 
					
						
							|  |  |  |       widget = ScrollConfiguration( | 
					
						
							|  |  |  |         behavior: ScrollConfiguration.of(context).copyWith(scrollbars: false), | 
					
						
							|  |  |  |         child: SingleChildScrollView( | 
					
						
							|  |  |  |           controller: _vertical, | 
					
						
							|  |  |  |           physics: cursorOverImage.isTrue | 
					
						
							|  |  |  |               ? const NeverScrollableScrollPhysics() | 
					
						
							|  |  |  |               : null, | 
					
						
							|  |  |  |           child: widget, | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       widget = Column( | 
					
						
							| 
									
										
										
										
											2022-11-30 22:56:22 +08:00
										 |  |  |         children: [ | 
					
						
							|  |  |  |           Container( | 
					
						
							|  |  |  |             height: ((layoutSize.height - size.height) ~/ 2).toDouble(), | 
					
						
							|  |  |  |           ), | 
					
						
							|  |  |  |           widget, | 
					
						
							|  |  |  |         ], | 
					
						
							| 
									
										
										
										
											2022-10-09 17:13:14 +08:00
										 |  |  |       ); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (layoutSize.width < size.width) { | 
					
						
							|  |  |  |       widget = ImprovedScrolling( | 
					
						
							|  |  |  |         scrollController: _horizontal, | 
					
						
							|  |  |  |         enableCustomMouseWheelScrolling: cursorOverImage.isFalse, | 
					
						
							|  |  |  |         customMouseWheelScrollConfig: scrollConfig, | 
					
						
							|  |  |  |         child: RawScrollbar( | 
					
						
							|  |  |  |           thumbColor: Colors.grey, | 
					
						
							|  |  |  |           controller: _horizontal, | 
					
						
							|  |  |  |           thumbVisibility: false, | 
					
						
							|  |  |  |           trackVisibility: false, | 
					
						
							| 
									
										
										
										
											2022-10-10 10:53:10 +08:00
										 |  |  |           notificationPredicate: layoutSize.height < size.height | 
					
						
							|  |  |  |               ? (notification) => notification.depth == 1 | 
					
						
							|  |  |  |               : defaultScrollNotificationPredicate, | 
					
						
							| 
									
										
										
										
											2022-10-09 17:13:14 +08:00
										 |  |  |           child: widget, | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (layoutSize.height < size.height) { | 
					
						
							|  |  |  |       widget = ImprovedScrolling( | 
					
						
							| 
									
										
										
										
											2022-10-05 00:22:40 +08:00
										 |  |  |         scrollController: _vertical, | 
					
						
							|  |  |  |         enableCustomMouseWheelScrolling: cursorOverImage.isFalse, | 
					
						
							|  |  |  |         customMouseWheelScrollConfig: scrollConfig, | 
					
						
							| 
									
										
										
										
											2022-10-09 17:13:14 +08:00
										 |  |  |         child: RawScrollbar( | 
					
						
							|  |  |  |           thumbColor: Colors.grey, | 
					
						
							|  |  |  |           controller: _vertical, | 
					
						
							|  |  |  |           thumbVisibility: false, | 
					
						
							|  |  |  |           trackVisibility: false, | 
					
						
							|  |  |  |           child: widget, | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return widget; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-13 17:58:24 +08:00
										 |  |  |   Widget _buildListener(Widget child) { | 
					
						
							|  |  |  |     if (listenerBuilder != null) { | 
					
						
							|  |  |  |       return listenerBuilder!(child); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       return child; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-08-13 15:08:17 +08:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-05-29 17:19:50 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class CursorPaint extends StatelessWidget { | 
					
						
							| 
									
										
										
										
											2022-06-13 21:07:26 +08:00
										 |  |  |   final String id; | 
					
						
							| 
									
										
										
										
											2022-11-16 18:07:58 +08:00
										 |  |  |   final RxBool zoomCursor; | 
					
						
							| 
									
										
										
										
											2022-06-13 21:07:26 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-16 18:07:58 +08:00
										 |  |  |   const CursorPaint({ | 
					
						
							|  |  |  |     Key? key, | 
					
						
							|  |  |  |     required this.id, | 
					
						
							|  |  |  |     required this.zoomCursor, | 
					
						
							|  |  |  |   }) : super(key: key); | 
					
						
							| 
									
										
										
										
											2022-06-13 21:07:26 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-29 17:19:50 +08:00
										 |  |  |   @override | 
					
						
							|  |  |  |   Widget build(BuildContext context) { | 
					
						
							| 
									
										
										
										
											2022-06-17 00:06:49 +08:00
										 |  |  |     final m = Provider.of<CursorModel>(context); | 
					
						
							|  |  |  |     final c = Provider.of<CanvasModel>(context); | 
					
						
							| 
									
										
										
										
											2022-11-13 23:41:07 +08:00
										 |  |  |     double hotx = m.hotx; | 
					
						
							|  |  |  |     double hoty = m.hoty; | 
					
						
							|  |  |  |     if (m.image == null) { | 
					
						
							| 
									
										
										
										
											2022-11-22 21:34:53 +08:00
										 |  |  |       if (preDefaultCursor.image != null) { | 
					
						
							|  |  |  |         hotx = preDefaultCursor.image!.width / 2; | 
					
						
							|  |  |  |         hoty = preDefaultCursor.image!.height / 2; | 
					
						
							| 
									
										
										
										
											2022-11-13 23:41:07 +08:00
										 |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-11-26 12:38:12 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     double cx = c.x; | 
					
						
							|  |  |  |     double cy = c.y; | 
					
						
							| 
									
										
										
										
											2022-12-30 16:14:30 +08:00
										 |  |  |     if (c.viewStyle.style == kRemoteViewStyleOriginal && | 
					
						
							|  |  |  |         c.scrollStyle == ScrollStyle.scrollbar) { | 
					
						
							| 
									
										
										
										
											2022-11-26 12:38:12 +08:00
										 |  |  |       final d = c.parent.target!.ffiModel.display; | 
					
						
							|  |  |  |       final imageWidth = d.width * c.scale; | 
					
						
							|  |  |  |       final imageHeight = d.height * c.scale; | 
					
						
							|  |  |  |       cx = -imageWidth * c.scrollX; | 
					
						
							|  |  |  |       cy = -imageHeight * c.scrollY; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     double x = (m.x - hotx) * c.scale + cx; | 
					
						
							| 
									
										
										
										
											2022-12-30 16:14:30 +08:00
										 |  |  |     double y = (m.y - hoty) * c.scale + cy; | 
					
						
							| 
									
										
										
										
											2022-11-26 12:38:12 +08:00
										 |  |  |     double scale = 1.0; | 
					
						
							| 
									
										
										
										
											2023-02-02 13:40:13 +08:00
										 |  |  |     final isViewOriginal = c.viewStyle.style == kRemoteViewStyleOriginal; | 
					
						
							|  |  |  |     if (zoomCursor.value || isViewOriginal) { | 
					
						
							| 
									
										
										
										
											2022-11-26 12:38:12 +08:00
										 |  |  |       x = m.x - hotx + cx / c.scale; | 
					
						
							|  |  |  |       y = m.y - hoty + cy / c.scale; | 
					
						
							|  |  |  |       scale = c.scale; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return CustomPaint( | 
					
						
							|  |  |  |       painter: ImagePainter( | 
					
						
							|  |  |  |         image: m.image ?? preDefaultCursor.image, | 
					
						
							|  |  |  |         x: x, | 
					
						
							|  |  |  |         y: y, | 
					
						
							|  |  |  |         scale: scale, | 
					
						
							|  |  |  |       ), | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2022-05-29 17:19:50 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | } |