| 
									
										
										
										
											2022-05-29 17:19:50 +08:00
										 |  |  | import 'dart:async'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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-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-10-05 00:22:40 +08:00
										 |  |  | import 'package:flutter_improved_scrolling/flutter_improved_scrolling.dart'; | 
					
						
							| 
									
										
										
										
											2024-05-08 09:59:05 +08:00
										 |  |  | import 'package:flutter_hbb/models/state_model.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'; | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-29 10:52:32 +08:00
										 |  |  | import 'package:flutter_hbb/native/custom_cursor.dart' | 
					
						
							|  |  |  |     if (dart.library.html) 'package:flutter_hbb/web/custom_cursor.dart'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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-06-11 16:32:22 +08:00
										 |  |  |     required this.toolbarState, | 
					
						
							| 
									
										
										
										
											2024-03-28 11:38:11 +08:00
										 |  |  |     this.sessionId, | 
					
						
							|  |  |  |     this.tabWindowId, | 
					
						
							|  |  |  |     this.password, | 
					
						
							|  |  |  |     this.display, | 
					
						
							|  |  |  |     this.displays, | 
					
						
							|  |  |  |     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, | 
					
						
							| 
									
										
										
										
											2024-07-25 21:52:57 +08:00
										 |  |  |   }) : super(key: key) { | 
					
						
							|  |  |  |     initSharedStates(id); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											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); | 
					
						
							| 
									
										
										
										
											2024-03-28 11:38:11 +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() { | 
					
						
							| 
									
										
										
										
											2024-07-25 10:45:51 +08:00
										 |  |  |     final state = _RemotePageState(id); | 
					
						
							| 
									
										
										
										
											2022-11-03 21:58:25 +08:00
										 |  |  |     _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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-13 21:04:00 +08:00
										 |  |  |   // We need `_instanceIdOnEnterOrLeaveImage4Toolbar` together with `_onEnterOrLeaveImage4Toolbar`
 | 
					
						
							|  |  |  |   // to identify the toolbar instance and its callback function.
 | 
					
						
							|  |  |  |   int? _instanceIdOnEnterOrLeaveImage4Toolbar; | 
					
						
							| 
									
										
										
										
											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; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-25 10:45:51 +08:00
										 |  |  |   _RemotePageState(String id) { | 
					
						
							|  |  |  |     _initStates(id); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-29 18:48:12 +08:00
										 |  |  |   void _initStates(String id) { | 
					
						
							| 
									
										
										
										
											2024-05-18 23:13:54 +08:00
										 |  |  |     _zoomCursor = PeerBoolOption.find(id, kOptionZoomCursor); | 
					
						
							| 
									
										
										
										
											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(); | 
					
						
							| 
									
										
										
										
											2023-08-03 23:14:40 +08:00
										 |  |  |     _ffi = FFI(widget.sessionId); | 
					
						
							| 
									
										
										
										
											2024-06-30 21:24:18 +08:00
										 |  |  |     Get.put<FFI>(_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
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2024-03-24 11:23:06 +08:00
										 |  |  |     if (!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); | 
					
						
							| 
									
										
										
										
											2024-03-28 11:38:11 +08:00
										 |  |  |     if (!isWeb) bind.pluginSyncUi(syncTo: kAppTypeDesktopRemote); | 
					
						
							| 
									
										
										
										
											2023-06-06 07:39:44 +08:00
										 |  |  |     _ffi.qualityMonitorModel.checkShowQualityMonitor(sessionId); | 
					
						
							| 
									
										
										
										
											2024-06-23 11:06:47 +08:00
										 |  |  |     _ffi.dialogManager.loadMobileActionsOverlayVisible(); | 
					
						
							| 
									
										
										
										
											2024-07-24 14:00:49 +08:00
										 |  |  |     WidgetsBinding.instance.addPostFrameCallback((_) { | 
					
						
							|  |  |  |       // Session option should be set after models.dart/FFI.start
 | 
					
						
							|  |  |  |       _showRemoteCursor.value = bind.sessionGetToggleOptionSync( | 
					
						
							|  |  |  |           sessionId: sessionId, arg: 'show-remote-cursor'); | 
					
						
							|  |  |  |       _zoomCursor.value = bind.sessionGetToggleOptionSync( | 
					
						
							|  |  |  |           sessionId: sessionId, arg: kOptionZoomCursor); | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											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); | 
					
						
							| 
									
										
										
										
											2024-07-24 14:00:49 +08:00
										 |  |  |     // Call onSelected in post frame callback, since we cannot guarantee that the callback will not call setState.
 | 
					
						
							|  |  |  |     WidgetsBinding.instance.addPostFrameCallback((_) { | 
					
						
							|  |  |  |       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.
 | 
					
						
							| 
									
										
										
										
											2024-03-24 11:23:06 +08:00
										 |  |  |     if (isWindows) { | 
					
						
							| 
									
										
										
										
											2022-12-22 19:54:04 +08:00
										 |  |  |       _isWindowBlur = true; | 
					
						
							|  |  |  |       // unfocus the primary-focus when the whole window is lost focus,
 | 
					
						
							|  |  |  |       // and let OS to handle events instead.
 | 
					
						
							|  |  |  |       _rawKeyFocusNode.unfocus(); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2024-05-08 09:59:05 +08:00
										 |  |  |     stateGlobal.isFocused.value = false; | 
					
						
							| 
									
										
										
										
											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].
 | 
					
						
							| 
									
										
										
										
											2024-03-24 11:23:06 +08:00
										 |  |  |     if (isWindows) { | 
					
						
							| 
									
										
										
										
											2022-12-22 19:54:04 +08:00
										 |  |  |       _isWindowBlur = false; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2024-05-08 09:59:05 +08:00
										 |  |  |     stateGlobal.isFocused.value = true; | 
					
						
							| 
									
										
										
										
											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.
 | 
					
						
							| 
									
										
										
										
											2024-03-24 11:23:06 +08:00
										 |  |  |     if (isWindows) { | 
					
						
							| 
									
										
										
										
											2022-12-27 16:18:28 +08:00
										 |  |  |       _isWindowBlur = false; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2024-03-24 11:23:06 +08:00
										 |  |  |     if (!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(); | 
					
						
							| 
									
										
										
										
											2024-03-24 11:23:06 +08:00
										 |  |  |     if (!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(); | 
					
						
							| 
									
										
										
										
											2024-03-24 11:23:06 +08:00
										 |  |  |     if (!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
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-21 23:38:27 +08:00
										 |  |  |   @override | 
					
						
							|  |  |  |   void onWindowEnterFullScreen() { | 
					
						
							|  |  |  |     super.onWindowEnterFullScreen(); | 
					
						
							|  |  |  |     if (isMacOS) { | 
					
						
							|  |  |  |       stateGlobal.setFullscreen(true); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   @override | 
					
						
							|  |  |  |   void onWindowLeaveFullScreen() { | 
					
						
							|  |  |  |     super.onWindowLeaveFullScreen(); | 
					
						
							|  |  |  |     if (isMacOS) { | 
					
						
							|  |  |  |       stateGlobal.setFullscreen(false); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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); | 
					
						
							| 
									
										
										
										
											2024-09-11 10:01:03 +08:00
										 |  |  |     if (closeSession) { | 
					
						
							|  |  |  |       // ensure we leave this session, this is a double check
 | 
					
						
							|  |  |  |       _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(); | 
					
						
							| 
									
										
										
										
											2024-05-27 09:27:30 +08:00
										 |  |  |     _ffi.imageModel.disposeImage(); | 
					
						
							|  |  |  |     _ffi.cursorModel.disposeImages(); | 
					
						
							| 
									
										
										
										
											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); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2024-03-24 11:23:06 +08:00
										 |  |  |     if (!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, | 
					
						
							| 
									
										
										
										
											2024-06-13 21:04:00 +08:00
										 |  |  |           onEnterOrLeaveImageSetter: (id, func) { | 
					
						
							|  |  |  |             _instanceIdOnEnterOrLeaveImage4Toolbar = id; | 
					
						
							|  |  |  |             _onEnterOrLeaveImage4Toolbar = func; | 
					
						
							|  |  |  |           }, | 
					
						
							|  |  |  |           onEnterOrLeaveImageCleaner: (id) { | 
					
						
							|  |  |  |             // If _instanceIdOnEnterOrLeaveImage4Toolbar != id
 | 
					
						
							|  |  |  |             // it means `_onEnterOrLeaveImage4Toolbar` is not set or it has been changed to another toolbar.
 | 
					
						
							|  |  |  |             if (_instanceIdOnEnterOrLeaveImage4Toolbar == id) { | 
					
						
							|  |  |  |               _instanceIdOnEnterOrLeaveImage4Toolbar = null; | 
					
						
							|  |  |  |               _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( | 
					
						
							| 
									
										
										
										
											2024-05-27 09:29:31 +08:00
										 |  |  |               color: kColorCanvas, | 
					
						
							| 
									
										
										
										
											2023-08-23 23:29:15 +08:00
										 |  |  |               child: RawKeyFocusScope( | 
					
						
							|  |  |  |                   focusNode: _rawKeyFocusNode, | 
					
						
							|  |  |  |                   onFocusChange: (bool imageFocused) { | 
					
						
							|  |  |  |                     debugPrint( | 
					
						
							|  |  |  |                         "onFocusChange(window active:${!_isWindowBlur}) $imageFocused"); | 
					
						
							|  |  |  |                     // See [onWindowBlur].
 | 
					
						
							| 
									
										
										
										
											2024-03-24 11:23:06 +08:00
										 |  |  |                     if (isWindows) { | 
					
						
							| 
									
										
										
										
											2023-08-23 23:29:15 +08:00
										 |  |  |                       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() | 
					
						
							|  |  |  |                   : () { | 
					
						
							| 
									
										
										
										
											2024-06-01 20:23:58 +08:00
										 |  |  |                       if (!_ffi.ffiModel.isPeerAndroid) { | 
					
						
							|  |  |  |                         return Offstage(); | 
					
						
							|  |  |  |                       } else { | 
					
						
							|  |  |  |                         return Obx(() => Offstage( | 
					
						
							|  |  |  |                               offstage: _ffi.dialogManager | 
					
						
							|  |  |  |                                   .mobileActionsOverlayVisible.isFalse, | 
					
						
							|  |  |  |                               child: Overlay(initialEntries: [ | 
					
						
							|  |  |  |                                 makeMobileActionsOverlayEntry( | 
					
						
							| 
									
										
										
										
											2024-06-23 11:26:15 +08:00
										 |  |  |                                   () => _ffi.dialogManager | 
					
						
							|  |  |  |                                       .setMobileActionsOverlayVisible(false), | 
					
						
							| 
									
										
										
										
											2024-06-01 20:23:58 +08:00
										 |  |  |                                   ffi: _ffi, | 
					
						
							|  |  |  |                                 ) | 
					
						
							|  |  |  |                               ]), | 
					
						
							|  |  |  |                             )); | 
					
						
							|  |  |  |                       } | 
					
						
							| 
									
										
										
										
											2023-11-14 12:11:38 +08:00
										 |  |  |                     }(), | 
					
						
							|  |  |  |               // 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].
 | 
					
						
							| 
									
										
										
										
											2024-03-24 11:23:06 +08:00
										 |  |  |     if (!isWindows) { | 
					
						
							| 
									
										
										
										
											2022-12-22 19:54:04 +08:00
										 |  |  |       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].
 | 
					
						
							| 
									
										
										
										
											2024-03-24 11:23:06 +08:00
										 |  |  |     if (!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) { | 
					
						
							| 
									
										
										
										
											2024-03-28 11:38:11 +08:00
										 |  |  |         if (!isWeb) bind.hostStopSystemKeyPropagate(stopped: false); | 
					
						
							| 
									
										
										
										
											2022-08-11 16:03:04 +08:00
										 |  |  |       }, onExit: (evt) { | 
					
						
							| 
									
										
										
										
											2024-03-28 11:38:11 +08:00
										 |  |  |         if (!isWeb) bind.hostStopSystemKeyPropagate(stopped: true); | 
					
						
							| 
									
										
										
										
											2022-09-03 10:39:33 +08:00
										 |  |  |       }, child: LayoutBuilder(builder: (context, constraints) { | 
					
						
							| 
									
										
										
										
											2024-05-29 08:16:36 +08:00
										 |  |  |         final c = Provider.of<CanvasModel>(context, listen: false); | 
					
						
							|  |  |  |         Future.delayed(Duration.zero, () => c.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(() { | 
					
						
							| 
									
										
										
										
											2024-06-14 00:28:59 +08:00
										 |  |  |                   widget.toolbarState.initShow(sessionId); | 
					
						
							| 
									
										
										
										
											2024-01-02 16:58:10 +08:00
										 |  |  |                   _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) { | 
					
						
							| 
									
										
										
										
											2024-07-13 00:44:08 +08:00
										 |  |  |       paints | 
					
						
							|  |  |  |           .add(Obx(() => _showRemoteCursor.isFalse || _remoteCursorMoved.isFalse | 
					
						
							|  |  |  |               ? Offstage() | 
					
						
							|  |  |  |               : CursorPaint( | 
					
						
							|  |  |  |                   id: widget.id, | 
					
						
							|  |  |  |                   zoomCursor: _zoomCursor, | 
					
						
							|  |  |  |                 ))); | 
					
						
							| 
									
										
										
										
											2022-11-29 16:36:35 +08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											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; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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; | 
					
						
							| 
									
										
										
										
											2024-03-24 11:23:06 +08:00
										 |  |  |             if (isWindows) { | 
					
						
							| 
									
										
										
										
											2023-02-03 17:02:28 +08:00
										 |  |  |               // 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); | 
					
						
							|  |  |  |         }); | 
					
						
							| 
									
										
										
										
											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); | 
					
						
							| 
									
										
										
										
											2024-06-13 18:03:41 +08:00
										 |  |  |       final paintWidget = | 
					
						
							|  |  |  |           m.useTextureRender || widget.ffi.ffiModel.pi.forceTextureRender | 
					
						
							|  |  |  |               ? _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) { | 
					
						
							| 
									
										
										
										
											2024-06-13 18:03:41 +08:00
										 |  |  |         final paintWidget = | 
					
						
							|  |  |  |             m.useTextureRender || widget.ffi.ffiModel.pi.forceTextureRender | 
					
						
							|  |  |  |                 ? _BuildPaintTextureRender( | 
					
						
							|  |  |  |                     c, | 
					
						
							|  |  |  |                     s, | 
					
						
							|  |  |  |                     Offset( | 
					
						
							|  |  |  |                       isLinux ? c.x.toInt().toDouble() : c.x, | 
					
						
							|  |  |  |                       isLinux ? c.y.toInt().toDouble() : c.y, | 
					
						
							|  |  |  |                     ), | 
					
						
							|  |  |  |                     c.size, | 
					
						
							|  |  |  |                     isViewOriginal()) | 
					
						
							|  |  |  |                 : _buildScrollAutoNonTextureRender(m, c, s); | 
					
						
							| 
									
										
										
										
											2023-10-08 21:44:54 +08:00
										 |  |  |         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), | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-13 18:03:41 +08:00
										 |  |  |   Widget _buildScrollAutoNonTextureRender( | 
					
						
							| 
									
										
										
										
											2023-10-08 21:44:54 +08:00
										 |  |  |       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 _buildCustomCursor(BuildContext context, double scale) { | 
					
						
							|  |  |  |     final cursor = Provider.of<CursorModel>(context); | 
					
						
							|  |  |  |     final cache = cursor.cache ?? preDefaultCursor.cache; | 
					
						
							| 
									
										
										
										
											2024-03-29 10:52:32 +08:00
										 |  |  |     return buildCursorOfCache(cursor, scale, cache); | 
					
						
							| 
									
										
										
										
											2022-11-22 21:34:53 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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; | 
					
						
							| 
									
										
										
										
											2024-03-29 10:52:32 +08:00
										 |  |  |     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
										 |  |  |   } | 
					
						
							|  |  |  | } |