| 
									
										
										
										
											2022-05-29 17:19:50 +08:00
										 |  |  | import 'dart:async'; | 
					
						
							|  |  |  | import 'dart:io'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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'; | 
					
						
							| 
									
										
										
										
											2023-10-31 21:10:23 +08:00
										 |  |  | import 'package:wakelock_plus/wakelock_plus.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'; | 
					
						
							| 
									
										
										
										
											2023-03-24 15:21:14 +08:00
										 |  |  | import '../../common/widgets/dialog.dart'; | 
					
						
							| 
									
										
										
										
											2023-11-14 12:11:38 +08:00
										 |  |  | import '../../common/widgets/toolbar.dart'; | 
					
						
							| 
									
										
										
										
											2022-05-29 17:19:50 +08:00
										 |  |  | import '../../models/model.dart'; | 
					
						
							| 
									
										
										
										
											2023-08-01 22:19:38 +08:00
										 |  |  | import '../../models/desktop_render_texture.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'; | 
					
						
							| 
									
										
										
										
											2023-06-07 20:31:54 +08:00
										 |  |  | import '../widgets/tabbar_widget.dart'; | 
					
						
							| 
									
										
										
										
											2022-05-29 17:19:50 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-03 22:03:49 -07:00
										 |  |  | final SimpleWrapper<bool> _firstEnterImage = SimpleWrapper(false); | 
					
						
							| 
									
										
										
										
											2022-09-13 19:10:55 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-08 21:44:54 +08:00
										 |  |  | // Used to skip session close if "move to new window" is clicked.
 | 
					
						
							| 
									
										
										
										
											2023-08-04 01:41:36 +08:00
										 |  |  | final Map<String, bool> closeSessionOnDispose = {}; | 
					
						
							| 
									
										
										
										
											2023-08-03 23:14:40 +08: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, | 
					
						
							| 
									
										
										
										
											2023-08-03 23:14:40 +08:00
										 |  |  |     required this.sessionId, | 
					
						
							| 
									
										
										
										
											2023-08-14 20:40:58 +08:00
										 |  |  |     required this.tabWindowId, | 
					
						
							| 
									
										
										
										
											2023-10-08 21:44:54 +08:00
										 |  |  |     required this.display, | 
					
						
							|  |  |  |     required this.displays, | 
					
						
							| 
									
										
										
										
											2023-03-20 00:16:06 +08:00
										 |  |  |     required this.password, | 
					
						
							| 
									
										
										
										
											2023-06-11 16:32:22 +08:00
										 |  |  |     required this.toolbarState, | 
					
						
							| 
									
										
										
										
											2023-06-07 20:31:54 +08:00
										 |  |  |     required this.tabController, | 
					
						
							| 
									
										
										
										
											2023-01-17 13:28:33 +08:00
										 |  |  |     this.switchUuid, | 
					
						
							| 
									
										
										
										
											2023-02-13 16:40:24 +08:00
										 |  |  |     this.forceRelay, | 
					
						
							| 
									
										
										
										
											2024-03-20 15:05:54 +08:00
										 |  |  |     this.isSharedPassword, | 
					
						
							| 
									
										
										
										
											2022-08-26 23:28:08 +08:00
										 |  |  |   }) : super(key: key); | 
					
						
							| 
									
										
										
										
											2022-05-29 17:19:50 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   final String id; | 
					
						
							| 
									
										
										
										
											2023-08-03 23:14:40 +08:00
										 |  |  |   final SessionID? sessionId; | 
					
						
							| 
									
										
										
										
											2023-08-14 20:40:58 +08:00
										 |  |  |   final int? tabWindowId; | 
					
						
							| 
									
										
										
										
											2023-10-08 21:44:54 +08:00
										 |  |  |   final int? display; | 
					
						
							|  |  |  |   final List<int>? displays; | 
					
						
							| 
									
										
										
										
											2023-03-20 00:16:06 +08:00
										 |  |  |   final String? password; | 
					
						
							| 
									
										
										
										
											2023-06-11 16:32:22 +08:00
										 |  |  |   final ToolbarState toolbarState; | 
					
						
							| 
									
										
										
										
											2023-01-17 13:28:33 +08:00
										 |  |  |   final String? switchUuid; | 
					
						
							| 
									
										
										
										
											2023-02-13 16:40:24 +08:00
										 |  |  |   final bool? forceRelay; | 
					
						
							| 
									
										
										
										
											2024-03-20 15:05:54 +08:00
										 |  |  |   final bool? isSharedPassword; | 
					
						
							| 
									
										
										
										
											2022-11-03 21:58:25 +08:00
										 |  |  |   final SimpleWrapper<State<RemotePage>?> _lastState = SimpleWrapper(null); | 
					
						
							| 
									
										
										
										
											2023-06-07 20:31:54 +08:00
										 |  |  |   final DesktopTabController tabController; | 
					
						
							| 
									
										
										
										
											2022-11-03 21:58:25 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   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; | 
					
						
							| 
									
										
										
										
											2022-05-29 17:19:50 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-26 16:11:08 +08:00
										 |  |  |   var _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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-11 16:32:22 +08:00
										 |  |  |   Function(bool)? _onEnterOrLeaveImage4Toolbar; | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |   SessionID get sessionId => _ffi.sessionId; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-29 18:48:12 +08:00
										 |  |  |   void _initStates(String id) { | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |     initSharedStates(id); | 
					
						
							|  |  |  |     _zoomCursor = PeerBoolOption.find(id, 'zoom-cursor'); | 
					
						
							| 
									
										
										
										
											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); | 
					
						
							| 
									
										
										
										
											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); | 
					
						
							| 
									
										
										
										
											2023-08-03 23:14:40 +08:00
										 |  |  |     _ffi = FFI(widget.sessionId); | 
					
						
							| 
									
										
										
										
											2022-08-12 18:42:02 +08:00
										 |  |  |     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, | 
					
						
							| 
									
										
										
										
											2023-03-20 00:16:06 +08:00
										 |  |  |       password: widget.password, | 
					
						
							| 
									
										
										
										
											2024-03-20 15:05:54 +08:00
										 |  |  |       isSharedPassword: widget.isSharedPassword, | 
					
						
							| 
									
										
										
										
											2023-01-17 13:28:33 +08:00
										 |  |  |       switchUuid: widget.switchUuid, | 
					
						
							| 
									
										
										
										
											2023-02-13 16:40:24 +08:00
										 |  |  |       forceRelay: widget.forceRelay, | 
					
						
							| 
									
										
										
										
											2023-08-14 20:40:58 +08:00
										 |  |  |       tabWindowId: widget.tabWindowId, | 
					
						
							| 
									
										
										
										
											2023-10-08 21:44:54 +08:00
										 |  |  |       display: widget.display, | 
					
						
							|  |  |  |       displays: widget.displays, | 
					
						
							| 
									
										
										
										
											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) { | 
					
						
							| 
									
										
										
										
											2023-10-31 21:10:23 +08:00
										 |  |  |       WakelockPlus.enable(); | 
					
						
							| 
									
										
										
										
											2022-05-29 17:19:50 +08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-08-01 22:19:38 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |     _ffi.ffiModel.updateEventListener(sessionId, widget.id); | 
					
						
							| 
									
										
										
										
											2023-04-23 20:53:51 +08:00
										 |  |  |     bind.pluginSyncUi(syncTo: kAppTypeDesktopRemote); | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |     _ffi.qualityMonitorModel.checkShowQualityMonitor(sessionId); | 
					
						
							| 
									
										
										
										
											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( | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |         sessionId: sessionId, arg: 'show-remote-cursor'); | 
					
						
							|  |  |  |     _zoomCursor.value = bind.sessionGetToggleOptionSync( | 
					
						
							|  |  |  |         sessionId: sessionId, 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-04-04 09:51:41 +08:00
										 |  |  |     _blockableOverlayState.applyFfi(_ffi); | 
					
						
							| 
									
										
										
										
											2023-06-07 20:31:54 +08:00
										 |  |  |     widget.tabController.onSelected?.call(widget.id); | 
					
						
							| 
									
										
										
										
											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; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-04-30 20:05:56 +08:00
										 |  |  |     if (!Platform.isLinux) { | 
					
						
							| 
									
										
										
										
											2023-10-31 21:10:23 +08:00
										 |  |  |       WakelockPlus.enable(); | 
					
						
							| 
									
										
										
										
											2023-04-30 20:05:56 +08:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-01 09:25:06 +08:00
										 |  |  |   // When the window is unminimized, onWindowMaximize or onWindowRestore can be called when the old state was maximized or not.
 | 
					
						
							| 
									
										
										
										
											2023-05-01 09:05:52 +08:00
										 |  |  |   @override | 
					
						
							|  |  |  |   void onWindowMaximize() { | 
					
						
							|  |  |  |     super.onWindowMaximize(); | 
					
						
							|  |  |  |     if (!Platform.isLinux) { | 
					
						
							| 
									
										
										
										
											2023-10-31 21:10:23 +08:00
										 |  |  |       WakelockPlus.enable(); | 
					
						
							| 
									
										
										
										
											2023-05-01 09:05:52 +08:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-30 20:05:56 +08:00
										 |  |  |   @override | 
					
						
							|  |  |  |   void onWindowMinimize() { | 
					
						
							|  |  |  |     super.onWindowMinimize(); | 
					
						
							|  |  |  |     if (!Platform.isLinux) { | 
					
						
							| 
									
										
										
										
											2023-10-31 21:10:23 +08:00
										 |  |  |       WakelockPlus.disable(); | 
					
						
							| 
									
										
										
										
											2023-04-30 20:05:56 +08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-12-27 16:18:28 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-29 17:19:50 +08:00
										 |  |  |   @override | 
					
						
							| 
									
										
										
										
											2023-06-28 17:09:23 +08:00
										 |  |  |   Future<void> dispose() async { | 
					
						
							| 
									
										
										
										
											2023-08-04 01:41:36 +08:00
										 |  |  |     final closeSession = closeSessionOnDispose.remove(widget.id) ?? true; | 
					
						
							| 
									
										
										
										
											2023-08-03 23:14:40 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-30 14:01:12 +08:00
										 |  |  |     // https://github.com/flutter/flutter/issues/64935
 | 
					
						
							|  |  |  |     super.dispose(); | 
					
						
							| 
									
										
										
										
											2023-08-01 22:19:38 +08:00
										 |  |  |     debugPrint("REMOTE PAGE dispose session $sessionId ${widget.id}"); | 
					
						
							| 
									
										
										
										
											2024-01-02 16:58:10 +08:00
										 |  |  |     _ffi.textureModel.onRemotePageDispose(closeSession); | 
					
						
							| 
									
										
										
										
											2022-12-22 19:54:04 +08:00
										 |  |  |     // ensure we leave this session, this is a double check
 | 
					
						
							| 
									
										
										
										
											2023-09-25 00:15:36 +08:00
										 |  |  |     _ffi.inputModel.enterOrLeave(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(); | 
					
						
							| 
									
										
										
										
											2023-08-03 23:14:40 +08:00
										 |  |  |     await _ffi.close(closeSession: closeSession); | 
					
						
							| 
									
										
										
										
											2022-05-29 17:19:50 +08:00
										 |  |  |     _timer?.cancel(); | 
					
						
							| 
									
										
										
										
											2023-06-08 16:42:57 +08:00
										 |  |  |     _ffi.dialogManager.dismissAll(); | 
					
						
							| 
									
										
										
										
											2023-08-03 23:14:40 +08:00
										 |  |  |     if (closeSession) { | 
					
						
							|  |  |  |       await SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, | 
					
						
							|  |  |  |           overlays: SystemUiOverlay.values); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-05-29 17:19:50 +08:00
										 |  |  |     if (!Platform.isLinux) { | 
					
						
							| 
									
										
										
										
											2023-10-31 21:10:23 +08:00
										 |  |  |       await WakelockPlus.disable(); | 
					
						
							| 
									
										
										
										
											2022-05-29 17:19:50 +08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-06-28 17:09:23 +08:00
										 |  |  |     await Get.delete<FFI>(tag: widget.id); | 
					
						
							| 
									
										
										
										
											2023-04-12 09:41:13 +08:00
										 |  |  |     removeSharedStates(widget.id); | 
					
						
							| 
									
										
										
										
											2022-05-29 17:19:50 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-24 12:03:29 +08:00
										 |  |  |   Widget emptyOverlay() => BlockableOverlay( | 
					
						
							|  |  |  |         /// the Overlay key will be set with _blockableOverlayState in BlockableOverlay
 | 
					
						
							|  |  |  |         /// see override build() in [BlockableOverlay]
 | 
					
						
							|  |  |  |         state: _blockableOverlayState, | 
					
						
							|  |  |  |         underlying: Container( | 
					
						
							|  |  |  |           color: Colors.transparent, | 
					
						
							| 
									
										
										
										
											2023-08-23 23:29:15 +08:00
										 |  |  |         ), | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-03 10:39:33 +08:00
										 |  |  |   Widget buildBody(BuildContext context) { | 
					
						
							| 
									
										
										
										
											2023-08-24 14:40:02 +08:00
										 |  |  |     remoteToolbar(BuildContext context) => RemoteToolbar( | 
					
						
							|  |  |  |           id: widget.id, | 
					
						
							|  |  |  |           ffi: _ffi, | 
					
						
							|  |  |  |           state: widget.toolbarState, | 
					
						
							|  |  |  |           onEnterOrLeaveImageSetter: (func) => | 
					
						
							|  |  |  |               _onEnterOrLeaveImage4Toolbar = func, | 
					
						
							|  |  |  |           onEnterOrLeaveImageCleaner: () => _onEnterOrLeaveImage4Toolbar = null, | 
					
						
							| 
									
										
										
										
											2023-10-08 21:44:54 +08:00
										 |  |  |           setRemoteState: setState, | 
					
						
							| 
									
										
										
										
											2023-08-24 14:40:02 +08:00
										 |  |  |         ); | 
					
						
							| 
									
										
										
										
											2023-10-26 16:11:08 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     bodyWidget() { | 
					
						
							|  |  |  |       return Stack( | 
					
						
							| 
									
										
										
										
											2023-08-23 23:29:15 +08:00
										 |  |  |         children: [ | 
					
						
							|  |  |  |           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); | 
					
						
							|  |  |  |                       } | 
					
						
							| 
									
										
										
										
											2023-02-07 00:11:48 +09:00
										 |  |  |                     } | 
					
						
							| 
									
										
										
										
											2023-08-23 23:29:15 +08:00
										 |  |  |                   }, | 
					
						
							|  |  |  |                   inputModel: _ffi.inputModel, | 
					
						
							|  |  |  |                   child: getBodyForDesktop(context))), | 
					
						
							| 
									
										
										
										
											2023-11-14 12:11:38 +08:00
										 |  |  |           Stack( | 
					
						
							|  |  |  |             children: [ | 
					
						
							|  |  |  |               _ffi.ffiModel.pi.isSet.isTrue && | 
					
						
							|  |  |  |                       _ffi.ffiModel.waitForFirstImage.isTrue | 
					
						
							|  |  |  |                   ? emptyOverlay() | 
					
						
							|  |  |  |                   : () { | 
					
						
							|  |  |  |                       _ffi.ffiModel.tryShowAndroidActionsOverlay(); | 
					
						
							|  |  |  |                       return Offstage(); | 
					
						
							|  |  |  |                     }(), | 
					
						
							|  |  |  |               // Use Overlay to enable rebuild every time on menu button click.
 | 
					
						
							|  |  |  |               _ffi.ffiModel.pi.isSet.isTrue | 
					
						
							|  |  |  |                   ? Overlay( | 
					
						
							|  |  |  |                       initialEntries: [OverlayEntry(builder: remoteToolbar)]) | 
					
						
							|  |  |  |                   : remoteToolbar(context), | 
					
						
							|  |  |  |               _ffi.ffiModel.pi.isSet.isFalse ? emptyOverlay() : Offstage(), | 
					
						
							|  |  |  |             ], | 
					
						
							|  |  |  |           ), | 
					
						
							| 
									
										
										
										
											2023-02-07 00:11:48 +09:00
										 |  |  |         ], | 
					
						
							| 
									
										
										
										
											2023-10-26 16:11:08 +08:00
										 |  |  |       ); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return Scaffold( | 
					
						
							|  |  |  |       backgroundColor: Theme.of(context).colorScheme.background, | 
					
						
							|  |  |  |       body: Obx(() { | 
					
						
							|  |  |  |         final imageReady = _ffi.ffiModel.pi.isSet.isTrue && | 
					
						
							|  |  |  |             _ffi.ffiModel.waitForFirstImage.isFalse; | 
					
						
							|  |  |  |         if (imageReady) { | 
					
						
							| 
									
										
										
										
											2023-11-14 12:11:38 +08:00
										 |  |  |           // If the privacy mode(disable physical displays) is switched,
 | 
					
						
							|  |  |  |           // we should not dismiss the dialog immediately.
 | 
					
						
							|  |  |  |           if (DateTime.now().difference(togglePrivacyModeTime) > | 
					
						
							|  |  |  |               const Duration(milliseconds: 3000)) { | 
					
						
							|  |  |  |             // `dismissAll()` is to ensure that the state is clean.
 | 
					
						
							|  |  |  |             // It's ok to call dismissAll() here.
 | 
					
						
							|  |  |  |             _ffi.dialogManager.dismissAll(); | 
					
						
							|  |  |  |             // Recreate the block state to refresh the state.
 | 
					
						
							|  |  |  |             _blockableOverlayState = BlockableOverlayState(); | 
					
						
							|  |  |  |             _blockableOverlayState.applyFfi(_ffi); | 
					
						
							|  |  |  |           } | 
					
						
							| 
									
										
										
										
											2023-10-26 17:03:54 +08:00
										 |  |  |           // Block the whole `bodyWidget()` when dialog shows.
 | 
					
						
							| 
									
										
										
										
											2023-10-26 16:11:08 +08:00
										 |  |  |           return BlockableOverlay( | 
					
						
							|  |  |  |             underlying: bodyWidget(), | 
					
						
							|  |  |  |             state: _blockableOverlayState, | 
					
						
							|  |  |  |           ); | 
					
						
							|  |  |  |         } else { | 
					
						
							| 
									
										
										
										
											2023-10-26 17:03:54 +08:00
										 |  |  |           // `_blockableOverlayState` is not recreated here.
 | 
					
						
							|  |  |  |           // The toolbar's block state won't work properly when reconnecting, but that's okay.
 | 
					
						
							| 
									
										
										
										
											2023-10-26 16:11:08 +08:00
										 |  |  |           return bodyWidget(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }), | 
					
						
							| 
									
										
										
										
											2023-02-07 00:11:48 +09:00
										 |  |  |     ); | 
					
						
							| 
									
										
										
										
											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 { | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |           clientClose(sessionId, _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; | 
					
						
							| 
									
										
										
										
											2023-06-11 16:32:22 +08:00
										 |  |  |     if (_onEnterOrLeaveImage4Toolbar != null) { | 
					
						
							| 
									
										
										
										
											2022-09-13 19:10:55 -07:00
										 |  |  |       try { | 
					
						
							| 
									
										
										
										
											2023-06-11 16:32:22 +08:00
										 |  |  |         _onEnterOrLeaveImage4Toolbar!(true); | 
					
						
							| 
									
										
										
										
											2022-09-13 19:10:55 -07:00
										 |  |  |       } 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(); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2023-09-25 00:15:36 +08:00
										 |  |  |       _ffi.inputModel.enterOrLeave(true); | 
					
						
							| 
									
										
										
										
											2022-12-22 19:54:04 +08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-09-05 06:19:05 -04:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   void leaveView(PointerExitEvent evt) { | 
					
						
							| 
									
										
										
										
											2023-03-28 10:52: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; | 
					
						
							| 
									
										
										
										
											2023-06-11 16:32:22 +08:00
										 |  |  |     if (_onEnterOrLeaveImage4Toolbar != null) { | 
					
						
							| 
									
										
										
										
											2022-09-13 19:10:55 -07:00
										 |  |  |       try { | 
					
						
							| 
									
										
										
										
											2023-06-11 16:32:22 +08:00
										 |  |  |         _onEnterOrLeaveImage4Toolbar!(false); | 
					
						
							| 
									
										
										
										
											2022-09-13 19:10:55 -07:00
										 |  |  |       } catch (e) { | 
					
						
							|  |  |  |         //
 | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2022-09-13 06:59:06 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-12-22 19:54:04 +08:00
										 |  |  |     // See [onWindowBlur].
 | 
					
						
							|  |  |  |     if (!Platform.isWindows) { | 
					
						
							| 
									
										
										
										
											2023-09-25 00:15:36 +08:00
										 |  |  |       _ffi.inputModel.enterOrLeave(false); | 
					
						
							| 
									
										
										
										
											2022-12-22 19:54:04 +08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-09-05 06:19:05 -04:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-17 20:07:55 +08:00
										 |  |  |   Widget _buildRawTouchAndPointerRegion( | 
					
						
							|  |  |  |     Widget child, | 
					
						
							|  |  |  |     PointerEnterEventListener? onEnter, | 
					
						
							|  |  |  |     PointerExitEventListener? onExit, | 
					
						
							|  |  |  |   ) { | 
					
						
							|  |  |  |     return RawTouchGestureDetectorRegion( | 
					
						
							|  |  |  |       child: _buildRawPointerMouseRegion(child, onEnter, onExit), | 
					
						
							|  |  |  |       ffi: _ffi, | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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(); | 
					
						
							|  |  |  |         }); | 
					
						
							| 
									
										
										
										
											2023-10-08 21:44:54 +08:00
										 |  |  |         final peerDisplay = CurrentDisplayState.find(widget.id); | 
					
						
							|  |  |  |         return Obx( | 
					
						
							|  |  |  |           () => _ffi.ffiModel.pi.isSet.isFalse | 
					
						
							|  |  |  |               ? Container(color: Colors.transparent) | 
					
						
							| 
									
										
										
										
											2024-01-02 16:58:10 +08:00
										 |  |  |               : Obx(() { | 
					
						
							|  |  |  |                   _ffi.textureModel.updateCurrentDisplay(peerDisplay.value); | 
					
						
							|  |  |  |                   return ImagePaint( | 
					
						
							| 
									
										
										
										
											2023-10-08 21:44:54 +08:00
										 |  |  |                     id: widget.id, | 
					
						
							|  |  |  |                     zoomCursor: _zoomCursor, | 
					
						
							|  |  |  |                     cursorOverImage: _cursorOverImage, | 
					
						
							|  |  |  |                     keyboardEnabled: _keyboardEnabled, | 
					
						
							|  |  |  |                     remoteCursorMoved: _remoteCursorMoved, | 
					
						
							|  |  |  |                     listenerBuilder: (child) => _buildRawTouchAndPointerRegion( | 
					
						
							|  |  |  |                         child, enterView, leaveView), | 
					
						
							| 
									
										
										
										
											2024-01-02 16:58:10 +08:00
										 |  |  |                     ffi: _ffi, | 
					
						
							|  |  |  |                   ); | 
					
						
							|  |  |  |                 }), | 
					
						
							| 
									
										
										
										
											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, | 
					
						
							| 
									
										
										
										
											2023-07-17 20:07:55 +08:00
										 |  |  |         child: _buildRawTouchAndPointerRegion( | 
					
						
							| 
									
										
										
										
											2023-01-28 21:11:03 +08:00
										 |  |  |             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 { | 
					
						
							| 
									
										
										
										
											2024-01-02 16:58:10 +08:00
										 |  |  |   final FFI ffi; | 
					
						
							| 
									
										
										
										
											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; | 
					
						
							| 
									
										
										
										
											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, | 
					
						
							| 
									
										
										
										
											2024-01-02 16:58:10 +08:00
										 |  |  |       required this.ffi, | 
					
						
							| 
									
										
										
										
											2022-08-13 17:58:24 +08:00
										 |  |  |       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, | 
					
						
							| 
									
										
										
										
											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; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   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; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-02 16:58:10 +08:00
										 |  |  |   @override | 
					
						
							|  |  |  |   void initState() { | 
					
						
							|  |  |  |     super.initState(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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-09-19 23:49:17 +08:00
										 |  |  |     bool isViewAdaptive() => c.viewStyle.style == kRemoteViewStyleAdaptive; | 
					
						
							|  |  |  |     bool isViewOriginal() => c.viewStyle.style == kRemoteViewStyleOriginal; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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
 | 
					
						
							| 
									
										
										
										
											2023-09-19 23:49:17 +08:00
										 |  |  |               if (zoomCursor.value && isViewAdaptive()) { | 
					
						
							| 
									
										
										
										
											2023-02-03 17:02:28 +08:00
										 |  |  |                 cursorScale = s * c.devicePixelRatio; | 
					
						
							|  |  |  |               } | 
					
						
							|  |  |  |             } else { | 
					
						
							| 
									
										
										
										
											2023-09-19 23:49:17 +08:00
										 |  |  |               if (zoomCursor.value || isViewOriginal()) { | 
					
						
							| 
									
										
										
										
											2023-02-03 17:02:28 +08:00
										 |  |  |                 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) { | 
					
						
							| 
									
										
										
										
											2023-10-08 21:44:54 +08:00
										 |  |  |       final paintWidth = c.getDisplayWidth() * s; | 
					
						
							|  |  |  |       final paintHeight = c.getDisplayHeight() * s; | 
					
						
							|  |  |  |       final paintSize = Size(paintWidth, paintHeight); | 
					
						
							|  |  |  |       final paintWidget = useTextureRender | 
					
						
							|  |  |  |           ? _BuildPaintTextureRender( | 
					
						
							|  |  |  |               c, s, Offset.zero, paintSize, isViewOriginal()) | 
					
						
							|  |  |  |           : _buildScrollbarNonTextureRender(m, paintSize, s); | 
					
						
							| 
									
										
										
										
											2022-10-09 17:13:14 +08:00
										 |  |  |       return NotificationListener<ScrollNotification>( | 
					
						
							| 
									
										
										
										
											2023-01-06 18:14:31 +08:00
										 |  |  |           onNotification: (notification) { | 
					
						
							| 
									
										
										
										
											2023-12-04 22:35:14 +08:00
										 |  |  |             c.updateScrollPercent(); | 
					
						
							| 
									
										
										
										
											2023-01-06 18:14:31 +08:00
										 |  |  |             return false; | 
					
						
							|  |  |  |           }, | 
					
						
							|  |  |  |           child: mouseRegion( | 
					
						
							|  |  |  |             child: Obx(() => _buildCrossScrollbarFromLayout( | 
					
						
							| 
									
										
										
										
											2023-12-04 22:35:14 +08:00
										 |  |  |                   context, | 
					
						
							|  |  |  |                   _buildListener(paintWidget), | 
					
						
							|  |  |  |                   c.size, | 
					
						
							|  |  |  |                   paintSize, | 
					
						
							|  |  |  |                   c.scrollHorizontal, | 
					
						
							|  |  |  |                   c.scrollVertical, | 
					
						
							|  |  |  |                 )), | 
					
						
							| 
									
										
										
										
											2023-01-06 18:14:31 +08:00
										 |  |  |           )); | 
					
						
							| 
									
										
										
										
											2022-08-11 00:12:47 +08:00
										 |  |  |     } else { | 
					
						
							| 
									
										
										
										
											2023-02-21 21:56:46 +08:00
										 |  |  |       if (c.size.width > 0 && c.size.height > 0) { | 
					
						
							| 
									
										
										
										
											2023-10-08 21:44:54 +08:00
										 |  |  |         final paintWidget = useTextureRender | 
					
						
							|  |  |  |             ? _BuildPaintTextureRender( | 
					
						
							|  |  |  |                 c, | 
					
						
							|  |  |  |                 s, | 
					
						
							|  |  |  |                 Offset( | 
					
						
							|  |  |  |                   Platform.isLinux ? c.x.toInt().toDouble() : c.x, | 
					
						
							|  |  |  |                   Platform.isLinux ? c.y.toInt().toDouble() : c.y, | 
					
						
							| 
									
										
										
										
											2023-09-19 21:32:50 +08:00
										 |  |  |                 ), | 
					
						
							| 
									
										
										
										
											2023-10-08 21:44:54 +08:00
										 |  |  |                 c.size, | 
					
						
							|  |  |  |                 isViewOriginal()) | 
					
						
							|  |  |  |             : _buildScrollAuthNonTextureRender(m, c, s); | 
					
						
							|  |  |  |         return mouseRegion(child: _buildListener(paintWidget)); | 
					
						
							| 
									
										
										
										
											2023-02-21 21:56:46 +08:00
										 |  |  |       } 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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-08 21:44:54 +08:00
										 |  |  |   Widget _buildScrollbarNonTextureRender( | 
					
						
							|  |  |  |       ImageModel m, Size imageSize, double s) { | 
					
						
							|  |  |  |     return CustomPaint( | 
					
						
							|  |  |  |       size: imageSize, | 
					
						
							|  |  |  |       painter: ImagePainter(image: m.image, x: 0, y: 0, scale: s), | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Widget _buildScrollAuthNonTextureRender( | 
					
						
							|  |  |  |       ImageModel m, CanvasModel c, double s) { | 
					
						
							|  |  |  |     return CustomPaint( | 
					
						
							|  |  |  |       size: Size(c.size.width, c.size.height), | 
					
						
							|  |  |  |       painter: ImagePainter(image: m.image, x: c.x / s, y: c.y / s, scale: s), | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Widget _BuildPaintTextureRender( | 
					
						
							|  |  |  |       CanvasModel c, double s, Offset offset, Size size, bool isViewOriginal) { | 
					
						
							|  |  |  |     final ffiModel = c.parent.target!.ffiModel; | 
					
						
							|  |  |  |     final displays = ffiModel.pi.getCurDisplays(); | 
					
						
							|  |  |  |     final children = <Widget>[]; | 
					
						
							|  |  |  |     final rect = ffiModel.rect; | 
					
						
							|  |  |  |     if (rect == null) { | 
					
						
							|  |  |  |       return Container(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     final curDisplay = ffiModel.pi.currentDisplay; | 
					
						
							|  |  |  |     for (var i = 0; i < displays.length; i++) { | 
					
						
							| 
									
										
										
										
											2024-01-02 16:58:10 +08:00
										 |  |  |       final textureId = widget.ffi.textureModel | 
					
						
							|  |  |  |           .getTextureId(curDisplay == kAllDisplayValue ? i : curDisplay); | 
					
						
							|  |  |  |       if (true) { | 
					
						
							|  |  |  |         // both "textureId.value != -1" and "true" seems ok
 | 
					
						
							| 
									
										
										
										
											2023-10-08 21:44:54 +08:00
										 |  |  |         children.add(Positioned( | 
					
						
							|  |  |  |           left: (displays[i].x - rect.left) * s + offset.dx, | 
					
						
							|  |  |  |           top: (displays[i].y - rect.top) * s + offset.dy, | 
					
						
							|  |  |  |           width: displays[i].width * s, | 
					
						
							|  |  |  |           height: displays[i].height * s, | 
					
						
							|  |  |  |           child: Obx(() => Texture( | 
					
						
							|  |  |  |                 textureId: textureId.value, | 
					
						
							|  |  |  |                 filterQuality: | 
					
						
							|  |  |  |                     isViewOriginal ? FilterQuality.none : FilterQuality.low, | 
					
						
							|  |  |  |               )), | 
					
						
							|  |  |  |         )); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return SizedBox( | 
					
						
							|  |  |  |       width: size.width, | 
					
						
							|  |  |  |       height: size.height, | 
					
						
							|  |  |  |       child: Stack(children: children), | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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)) { | 
					
						
							| 
									
										
										
										
											2023-10-18 22:39:28 +08:00
										 |  |  |         debugPrint( | 
					
						
							|  |  |  |             "Register custom cursor with key $key (${cache.hotx},${cache.hoty})"); | 
					
						
							| 
									
										
										
										
											2022-12-11 14:17:29 +08:00
										 |  |  |         // [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( | 
					
						
							| 
									
										
										
										
											2023-12-04 22:35:14 +08:00
										 |  |  |     BuildContext context, | 
					
						
							|  |  |  |     Widget child, | 
					
						
							|  |  |  |     Size layoutSize, | 
					
						
							|  |  |  |     Size size, | 
					
						
							|  |  |  |     ScrollController horizontal, | 
					
						
							|  |  |  |     ScrollController vertical, | 
					
						
							|  |  |  |   ) { | 
					
						
							| 
									
										
										
										
											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( | 
					
						
							| 
									
										
										
										
											2023-12-04 22:35:14 +08:00
										 |  |  |           controller: horizontal, | 
					
						
							| 
									
										
										
										
											2022-10-09 17:13:14 +08:00
										 |  |  |           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( | 
					
						
							| 
									
										
										
										
											2023-12-04 22:35:14 +08:00
										 |  |  |           controller: vertical, | 
					
						
							| 
									
										
										
										
											2022-10-09 17:13:14 +08:00
										 |  |  |           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( | 
					
						
							| 
									
										
										
										
											2023-12-04 22:35:14 +08:00
										 |  |  |         scrollController: horizontal, | 
					
						
							| 
									
										
										
										
											2022-10-09 17:13:14 +08:00
										 |  |  |         enableCustomMouseWheelScrolling: cursorOverImage.isFalse, | 
					
						
							|  |  |  |         customMouseWheelScrollConfig: scrollConfig, | 
					
						
							|  |  |  |         child: RawScrollbar( | 
					
						
							| 
									
										
										
										
											2023-09-13 21:02:21 +08:00
										 |  |  |           thickness: kScrollbarThickness, | 
					
						
							| 
									
										
										
										
											2022-10-09 17:13:14 +08:00
										 |  |  |           thumbColor: Colors.grey, | 
					
						
							| 
									
										
										
										
											2023-12-04 22:35:14 +08:00
										 |  |  |           controller: horizontal, | 
					
						
							| 
									
										
										
										
											2022-10-09 17:13:14 +08:00
										 |  |  |           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( | 
					
						
							| 
									
										
										
										
											2023-12-04 22:35:14 +08:00
										 |  |  |         scrollController: vertical, | 
					
						
							| 
									
										
										
										
											2022-10-05 00:22:40 +08:00
										 |  |  |         enableCustomMouseWheelScrolling: cursorOverImage.isFalse, | 
					
						
							|  |  |  |         customMouseWheelScrollConfig: scrollConfig, | 
					
						
							| 
									
										
										
										
											2022-10-09 17:13:14 +08:00
										 |  |  |         child: RawScrollbar( | 
					
						
							| 
									
										
										
										
											2023-09-13 21:02:21 +08:00
										 |  |  |           thickness: kScrollbarThickness, | 
					
						
							| 
									
										
										
										
											2022-10-09 17:13:14 +08:00
										 |  |  |           thumbColor: Colors.grey, | 
					
						
							| 
									
										
										
										
											2023-12-04 22:35:14 +08:00
										 |  |  |           controller: vertical, | 
					
						
							| 
									
										
										
										
											2022-10-09 17:13:14 +08:00
										 |  |  |           thumbVisibility: false, | 
					
						
							|  |  |  |           trackVisibility: false, | 
					
						
							|  |  |  |           child: widget, | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-08 21:44:54 +08:00
										 |  |  |     return Container( | 
					
						
							|  |  |  |       child: widget, | 
					
						
							|  |  |  |       width: layoutSize.width, | 
					
						
							|  |  |  |       height: layoutSize.height, | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2022-10-09 17:13:14 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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) { | 
					
						
							| 
									
										
										
										
											2023-10-08 21:44:54 +08:00
										 |  |  |       final rect = c.parent.target!.ffiModel.rect; | 
					
						
							|  |  |  |       if (rect == null) { | 
					
						
							|  |  |  |         // unreachable!
 | 
					
						
							|  |  |  |         debugPrint('unreachable! The displays rect is null.'); | 
					
						
							|  |  |  |         return Container(); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2023-12-04 22:35:14 +08:00
										 |  |  |       if (cx < 0) { | 
					
						
							|  |  |  |         final imageWidth = rect.width * c.scale; | 
					
						
							|  |  |  |         cx = -imageWidth * c.scrollX; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       if (cy < 0) { | 
					
						
							|  |  |  |         final imageHeight = rect.height * c.scale; | 
					
						
							|  |  |  |         cy = -imageHeight * c.scrollY; | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2022-11-26 12:38:12 +08:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     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
										 |  |  |   } | 
					
						
							|  |  |  | } |