| 
									
										
										
										
											2022-05-29 17:19:50 +08:00
										 |  |  |  | import 'dart:async'; | 
					
						
							| 
									
										
										
										
											2022-09-16 20:31:01 +08:00
										 |  |  |  | import 'dart:convert'; | 
					
						
							| 
									
										
										
										
											2022-05-29 17:19:50 +08:00
										 |  |  |  | import 'dart:ui' as ui; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-17 01:22:14 +08:00
										 |  |  |  | import 'package:flutter/material.dart'; | 
					
						
							| 
									
										
										
										
											2022-05-29 17:19:50 +08:00
										 |  |  |  | import 'package:flutter/services.dart'; | 
					
						
							| 
									
										
										
										
											2022-11-24 11:19:16 +08:00
										 |  |  |  | import 'package:flutter_hbb/consts.dart'; | 
					
						
							| 
									
										
										
										
											2022-05-24 23:33:00 +08:00
										 |  |  |  | import 'package:flutter_hbb/mobile/widgets/gesture_help.dart'; | 
					
						
							| 
									
										
										
										
											2022-05-29 17:19:50 +08:00
										 |  |  |  | import 'package:flutter_hbb/models/chat_model.dart'; | 
					
						
							| 
									
										
										
										
											2022-09-27 22:16:27 +08:00
										 |  |  |  | import 'package:get/get_state_manager/src/rx_flutter/rx_obx_widget.dart'; | 
					
						
							| 
									
										
										
										
											2020-11-18 23:15:59 +08:00
										 |  |  |  | import 'package:provider/provider.dart'; | 
					
						
							| 
									
										
										
										
											2020-11-23 13:03:51 +08:00
										 |  |  |  | import 'package:wakelock/wakelock.dart'; | 
					
						
							| 
									
										
										
										
											2022-05-29 17:19:50 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-24 23:33:00 +08:00
										 |  |  |  | import '../../common.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-09-27 20:35:02 +08:00
										 |  |  |  | import '../../models/input_model.dart'; | 
					
						
							| 
									
										
										
										
											2022-05-24 23:33:00 +08:00
										 |  |  |  | import '../../models/model.dart'; | 
					
						
							| 
									
										
										
										
											2022-08-05 20:29:43 +08:00
										 |  |  |  | import '../../models/platform_model.dart'; | 
					
						
							| 
									
										
										
										
											2022-03-07 22:54:34 +08:00
										 |  |  |  | import '../widgets/dialog.dart'; | 
					
						
							| 
									
										
										
										
											2022-05-29 17:19:50 +08:00
										 |  |  |  | import '../widgets/gestures.dart'; | 
					
						
							| 
									
										
										
										
											2020-11-17 01:22:14 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-28 18:06:27 +08:00
										 |  |  |  | final initText = '\1' * 1024; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-17 01:22:14 +08:00
										 |  |  |  | class RemotePage extends StatefulWidget { | 
					
						
							| 
									
										
										
										
											2022-02-17 15:22:14 +08:00
										 |  |  |  |   RemotePage({Key? key, required this.id}) : super(key: key); | 
					
						
							| 
									
										
										
										
											2020-11-17 01:22:14 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   final String id; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   @override | 
					
						
							| 
									
										
										
										
											2022-09-27 22:16:27 +08:00
										 |  |  |  |   State<RemotePage> createState() => _RemotePageState(); | 
					
						
							| 
									
										
										
										
											2020-11-17 01:22:14 +08:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | class _RemotePageState extends State<RemotePage> { | 
					
						
							| 
									
										
										
										
											2022-02-17 15:22:14 +08:00
										 |  |  |  |   Timer? _interval; | 
					
						
							|  |  |  |  |   Timer? _timer; | 
					
						
							| 
									
										
										
										
											2022-05-23 16:02:37 +08:00
										 |  |  |  |   bool _showBar = !isWebDesktop; | 
					
						
							| 
									
										
										
										
											2020-11-20 17:20:42 +08:00
										 |  |  |  |   double _bottom = 0; | 
					
						
							| 
									
										
										
										
											2020-11-25 16:28:46 +08:00
										 |  |  |  |   String _value = ''; | 
					
						
							| 
									
										
										
										
											2020-11-23 23:18:42 +08:00
										 |  |  |  |   double _scale = 1; | 
					
						
							| 
									
										
										
										
											2022-05-12 20:05:59 +08:00
										 |  |  |  |   double _mouseScrollIntegral = 0; // mouse scroll speed controller
 | 
					
						
							| 
									
										
										
										
											2022-06-02 17:16:23 +08:00
										 |  |  |  |   Orientation? _currentOrientation; | 
					
						
							| 
									
										
										
										
											2022-02-17 18:00:44 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-24 10:44:44 +08:00
										 |  |  |  |   var _more = true; | 
					
						
							| 
									
										
										
										
											2020-11-25 16:28:46 +08:00
										 |  |  |  |   var _fn = false; | 
					
						
							| 
									
										
										
										
											2022-04-25 18:25:25 +08:00
										 |  |  |  |   final FocusNode _mobileFocusNode = FocusNode(); | 
					
						
							|  |  |  |  |   final FocusNode _physicalFocusNode = FocusNode(); | 
					
						
							| 
									
										
										
										
											2022-05-11 17:06:40 +08:00
										 |  |  |  |   var _showEdit = false; // use soft keyboard
 | 
					
						
							| 
									
										
										
										
											2020-11-17 12:08:31 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-27 22:16:27 +08:00
										 |  |  |  |   InputModel get inputModel => gFFI.inputModel; | 
					
						
							| 
									
										
										
										
											2022-09-27 20:35:02 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-17 11:12:55 +08:00
										 |  |  |  |   @override | 
					
						
							|  |  |  |  |   void initState() { | 
					
						
							|  |  |  |  |     super.initState(); | 
					
						
							| 
									
										
										
										
											2022-09-27 20:35:02 +08:00
										 |  |  |  |     gFFI.start(widget.id); | 
					
						
							| 
									
										
										
										
											2022-05-26 18:25:16 +08:00
										 |  |  |  |     WidgetsBinding.instance.addPostFrameCallback((_) { | 
					
						
							| 
									
										
										
										
											2022-02-24 18:24:52 +08:00
										 |  |  |  |       SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []); | 
					
						
							| 
									
										
										
										
											2022-08-12 18:42:02 +08:00
										 |  |  |  |       gFFI.dialogManager | 
					
						
							| 
									
										
										
										
											2022-08-16 21:27:21 +08:00
										 |  |  |  |           .showLoading(translate('Connecting...'), onCancel: closeConnection); | 
					
						
							| 
									
										
										
										
											2020-11-17 12:08:31 +08:00
										 |  |  |  |       _interval = | 
					
						
							|  |  |  |  |           Timer.periodic(Duration(milliseconds: 30), (timer) => interval()); | 
					
						
							|  |  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2020-11-23 12:00:56 +08:00
										 |  |  |  |     Wakelock.enable(); | 
					
						
							| 
									
										
										
										
											2022-04-25 18:25:25 +08:00
										 |  |  |  |     _physicalFocusNode.requestFocus(); | 
					
						
							| 
									
										
										
										
											2022-06-13 21:07:26 +08:00
										 |  |  |  |     gFFI.ffiModel.updateEventListener(widget.id); | 
					
						
							| 
									
										
										
										
											2022-09-27 20:35:02 +08:00
										 |  |  |  |     gFFI.inputModel.listenToMouse(true); | 
					
						
							| 
									
										
										
										
											2022-08-15 20:26:20 +08:00
										 |  |  |  |     gFFI.qualityMonitorModel.checkShowQualityMonitor(widget.id); | 
					
						
							| 
									
										
										
										
											2020-11-17 12:08:31 +08:00
										 |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   @override | 
					
						
							|  |  |  |  |   void dispose() { | 
					
						
							| 
									
										
										
										
											2022-09-08 22:18:02 +08:00
										 |  |  |  |     gFFI.dialogManager.hideMobileActionsOverlay(); | 
					
						
							| 
									
										
										
										
											2022-09-27 20:35:02 +08:00
										 |  |  |  |     gFFI.inputModel.listenToMouse(false); | 
					
						
							| 
									
										
										
										
											2022-06-13 21:07:26 +08:00
										 |  |  |  |     gFFI.invokeMethod("enable_soft_keyboard", true); | 
					
						
							| 
									
										
										
										
											2022-04-25 18:25:25 +08:00
										 |  |  |  |     _mobileFocusNode.dispose(); | 
					
						
							|  |  |  |  |     _physicalFocusNode.dispose(); | 
					
						
							| 
									
										
										
										
											2022-06-13 21:07:26 +08:00
										 |  |  |  |     gFFI.close(); | 
					
						
							| 
									
										
										
										
											2022-02-17 15:22:14 +08:00
										 |  |  |  |     _interval?.cancel(); | 
					
						
							| 
									
										
										
										
											2020-11-27 16:06:35 +08:00
										 |  |  |  |     _timer?.cancel(); | 
					
						
							| 
									
										
										
										
											2022-08-12 18:42:02 +08:00
										 |  |  |  |     gFFI.dialogManager.dismissAll(); | 
					
						
							| 
									
										
										
										
											2022-02-24 18:24:52 +08:00
										 |  |  |  |     SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, | 
					
						
							|  |  |  |  |         overlays: SystemUiOverlay.values); | 
					
						
							| 
									
										
										
										
											2020-11-23 12:00:56 +08:00
										 |  |  |  |     Wakelock.disable(); | 
					
						
							| 
									
										
										
										
											2021-08-18 02:01:03 +08:00
										 |  |  |  |     super.dispose(); | 
					
						
							| 
									
										
										
										
											2020-11-17 12:08:31 +08:00
										 |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-25 16:28:46 +08:00
										 |  |  |  |   void resetTool() { | 
					
						
							| 
									
										
										
										
											2022-09-27 20:35:02 +08:00
										 |  |  |  |     inputModel.resetModifiers(); | 
					
						
							| 
									
										
										
										
											2020-11-25 16:28:46 +08:00
										 |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-01 01:35:01 +08:00
										 |  |  |  |   bool isKeyboardShown() { | 
					
						
							|  |  |  |  |     return _bottom >= 100; | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-24 18:24:52 +08:00
										 |  |  |  |   // crash on web before widget initiated.
 | 
					
						
							| 
									
										
										
										
											2022-01-26 19:00:23 +08:00
										 |  |  |  |   void intervalUnsafe() { | 
					
						
							| 
									
										
										
										
											2020-11-20 17:20:42 +08:00
										 |  |  |  |     var v = MediaQuery.of(context).viewInsets.bottom; | 
					
						
							|  |  |  |  |     if (v != _bottom) { | 
					
						
							| 
									
										
										
										
											2020-11-25 16:28:46 +08:00
										 |  |  |  |       resetTool(); | 
					
						
							| 
									
										
										
										
											2020-11-20 17:20:42 +08:00
										 |  |  |  |       setState(() { | 
					
						
							|  |  |  |  |         _bottom = v; | 
					
						
							| 
									
										
										
										
											2020-11-25 16:28:46 +08:00
										 |  |  |  |         if (v < 100) { | 
					
						
							| 
									
										
										
										
											2022-02-24 18:24:52 +08:00
										 |  |  |  |           SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, | 
					
						
							|  |  |  |  |               overlays: []); | 
					
						
							| 
									
										
										
										
											2022-08-11 10:19:12 +08:00
										 |  |  |  |           // [pi.version.isNotEmpty] -> check ready or not, avoid login without soft-keyboard
 | 
					
						
							|  |  |  |  |           if (gFFI.chatModel.chatWindowOverlayEntry == null && | 
					
						
							| 
									
										
										
										
											2022-06-13 21:07:26 +08:00
										 |  |  |  |               gFFI.ffiModel.pi.version.isNotEmpty) { | 
					
						
							|  |  |  |  |             gFFI.invokeMethod("enable_soft_keyboard", false); | 
					
						
							| 
									
										
										
										
											2022-04-28 22:44:54 +08:00
										 |  |  |  |           } | 
					
						
							| 
									
										
										
										
											2020-11-20 17:20:42 +08:00
										 |  |  |  |         } | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-11-19 21:59:49 +08:00
										 |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-26 19:00:23 +08:00
										 |  |  |  |   void interval() { | 
					
						
							|  |  |  |  |     try { | 
					
						
							|  |  |  |  |       intervalUnsafe(); | 
					
						
							|  |  |  |  |     } catch (e) {} | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-25 18:25:25 +08:00
										 |  |  |  |   // handle mobile virtual keyboard
 | 
					
						
							| 
									
										
										
										
											2022-09-27 23:05:11 +08:00
										 |  |  |  |   void handleSoftKeyboardInput(String newValue) { | 
					
						
							| 
									
										
										
										
											2021-08-17 22:57:35 +08:00
										 |  |  |  |     var oldValue = _value; | 
					
						
							|  |  |  |  |     _value = newValue; | 
					
						
							| 
									
										
										
										
											2022-01-25 18:13:11 +08:00
										 |  |  |  |     if (isIOS) { | 
					
						
							| 
									
										
										
										
											2021-08-17 22:57:35 +08:00
										 |  |  |  |       var i = newValue.length - 1; | 
					
						
							|  |  |  |  |       for (; i >= 0 && newValue[i] != '\1'; --i) {} | 
					
						
							|  |  |  |  |       var j = oldValue.length - 1; | 
					
						
							|  |  |  |  |       for (; j >= 0 && oldValue[j] != '\1'; --j) {} | 
					
						
							|  |  |  |  |       if (i < j) j = i; | 
					
						
							|  |  |  |  |       newValue = newValue.substring(j + 1); | 
					
						
							|  |  |  |  |       oldValue = oldValue.substring(j + 1); | 
					
						
							|  |  |  |  |       var common = 0; | 
					
						
							|  |  |  |  |       for (; | 
					
						
							| 
									
										
										
										
											2022-08-02 13:10:09 +08:00
										 |  |  |  |           common < oldValue.length && | 
					
						
							|  |  |  |  |               common < newValue.length && | 
					
						
							|  |  |  |  |               newValue[common] == oldValue[common]; | 
					
						
							|  |  |  |  |           ++common) {} | 
					
						
							| 
									
										
										
										
											2021-08-17 22:57:35 +08:00
										 |  |  |  |       for (i = 0; i < oldValue.length - common; ++i) { | 
					
						
							| 
									
										
										
										
											2022-09-27 20:35:02 +08:00
										 |  |  |  |         inputModel.inputKey('VK_BACK'); | 
					
						
							| 
									
										
										
										
											2021-08-17 22:57:35 +08:00
										 |  |  |  |       } | 
					
						
							|  |  |  |  |       if (newValue.length > common) { | 
					
						
							|  |  |  |  |         var s = newValue.substring(common); | 
					
						
							|  |  |  |  |         if (s.length > 1) { | 
					
						
							| 
									
										
										
										
											2022-08-05 20:29:43 +08:00
										 |  |  |  |           bind.sessionInputString(id: widget.id, value: s); | 
					
						
							| 
									
										
										
										
											2021-08-17 22:57:35 +08:00
										 |  |  |  |         } else { | 
					
						
							|  |  |  |  |           inputChar(s); | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  |       return; | 
					
						
							|  |  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-09-27 22:16:27 +08:00
										 |  |  |  |     if (oldValue.isNotEmpty && | 
					
						
							|  |  |  |  |         newValue.isNotEmpty && | 
					
						
							| 
									
										
										
										
											2021-08-17 22:57:35 +08:00
										 |  |  |  |         oldValue[0] == '\1' && | 
					
						
							|  |  |  |  |         newValue[0] != '\1') { | 
					
						
							| 
									
										
										
										
											2020-11-28 18:06:27 +08:00
										 |  |  |  |       // clipboard
 | 
					
						
							| 
									
										
										
										
											2021-08-17 22:57:35 +08:00
										 |  |  |  |       oldValue = ''; | 
					
						
							| 
									
										
										
										
											2020-11-28 18:06:27 +08:00
										 |  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-09-01 01:48:41 +08:00
										 |  |  |  |     if (newValue.length == oldValue.length) { | 
					
						
							|  |  |  |  |       // ?
 | 
					
						
							|  |  |  |  |     } else if (newValue.length < oldValue.length) { | 
					
						
							| 
									
										
										
										
											2020-11-28 18:06:27 +08:00
										 |  |  |  |       final char = 'VK_BACK'; | 
					
						
							| 
									
										
										
										
											2022-09-27 20:35:02 +08:00
										 |  |  |  |       inputModel.inputKey(char); | 
					
						
							| 
									
										
										
										
											2020-11-28 18:06:27 +08:00
										 |  |  |  |     } else { | 
					
						
							| 
									
										
										
										
											2021-08-17 22:57:35 +08:00
										 |  |  |  |       final content = newValue.substring(oldValue.length); | 
					
						
							| 
									
										
										
										
											2020-11-28 18:06:27 +08:00
										 |  |  |  |       if (content.length > 1) { | 
					
						
							| 
									
										
										
										
											2021-08-17 22:57:35 +08:00
										 |  |  |  |         if (oldValue != '' && | 
					
						
							| 
									
										
										
										
											2020-12-22 16:00:10 +08:00
										 |  |  |  |             content.length == 2 && | 
					
						
							|  |  |  |  |             (content == '""' || | 
					
						
							|  |  |  |  |                 content == '()' || | 
					
						
							|  |  |  |  |                 content == '[]' || | 
					
						
							|  |  |  |  |                 content == '<>' || | 
					
						
							|  |  |  |  |                 content == "{}" || | 
					
						
							|  |  |  |  |                 content == '”“' || | 
					
						
							|  |  |  |  |                 content == '《》' || | 
					
						
							|  |  |  |  |                 content == '()' || | 
					
						
							|  |  |  |  |                 content == '【】')) { | 
					
						
							| 
									
										
										
										
											2020-12-22 16:07:48 +08:00
										 |  |  |  |           // can not only input content[0], because when input ], [ are also auo insert, which cause ] never be input
 | 
					
						
							| 
									
										
										
										
											2022-08-05 20:29:43 +08:00
										 |  |  |  |           bind.sessionInputString(id: widget.id, value: content); | 
					
						
							| 
									
										
										
										
											2020-12-22 16:00:10 +08:00
										 |  |  |  |           openKeyboard(); | 
					
						
							|  |  |  |  |           return; | 
					
						
							|  |  |  |  |         } | 
					
						
							| 
									
										
										
										
											2022-08-05 20:29:43 +08:00
										 |  |  |  |         bind.sessionInputString(id: widget.id, value: content); | 
					
						
							| 
									
										
										
										
											2020-11-28 18:06:27 +08:00
										 |  |  |  |       } else { | 
					
						
							| 
									
										
										
										
											2021-08-17 22:57:35 +08:00
										 |  |  |  |         inputChar(content); | 
					
						
							| 
									
										
										
										
											2020-11-28 18:06:27 +08:00
										 |  |  |  |       } | 
					
						
							|  |  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-08-17 22:57:35 +08:00
										 |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   void inputChar(String char) { | 
					
						
							|  |  |  |  |     if (char == '\n') { | 
					
						
							|  |  |  |  |       char = 'VK_RETURN'; | 
					
						
							|  |  |  |  |     } else if (char == ' ') { | 
					
						
							|  |  |  |  |       char = 'VK_SPACE'; | 
					
						
							|  |  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-09-27 20:35:02 +08:00
										 |  |  |  |     inputModel.inputKey(char); | 
					
						
							| 
									
										
										
										
											2020-11-28 18:06:27 +08:00
										 |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   void openKeyboard() { | 
					
						
							| 
									
										
										
										
											2022-06-13 21:07:26 +08:00
										 |  |  |  |     gFFI.invokeMethod("enable_soft_keyboard", true); | 
					
						
							| 
									
										
										
										
											2020-11-28 18:06:27 +08:00
										 |  |  |  |     // destroy first, so that our _value trick can work
 | 
					
						
							| 
									
										
										
										
											2020-12-22 15:26:35 +08:00
										 |  |  |  |     _value = initText; | 
					
						
							| 
									
										
										
										
											2021-09-01 01:35:01 +08:00
										 |  |  |  |     setState(() => _showEdit = false); | 
					
						
							| 
									
										
										
										
											2020-11-28 18:06:27 +08:00
										 |  |  |  |     _timer?.cancel(); | 
					
						
							|  |  |  |  |     _timer = Timer(Duration(milliseconds: 30), () { | 
					
						
							|  |  |  |  |       // show now, and sleep a while to requestFocus to
 | 
					
						
							|  |  |  |  |       // make sure edit ready, so that keyboard wont show/hide/show/hide happen
 | 
					
						
							| 
									
										
										
										
											2021-09-01 01:35:01 +08:00
										 |  |  |  |       setState(() => _showEdit = true); | 
					
						
							| 
									
										
										
										
											2020-11-28 18:06:27 +08:00
										 |  |  |  |       _timer?.cancel(); | 
					
						
							|  |  |  |  |       _timer = Timer(Duration(milliseconds: 30), () { | 
					
						
							| 
									
										
										
										
											2022-02-24 18:24:52 +08:00
										 |  |  |  |         SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, | 
					
						
							|  |  |  |  |             overlays: SystemUiOverlay.values); | 
					
						
							| 
									
										
										
										
											2022-04-25 18:25:25 +08:00
										 |  |  |  |         _mobileFocusNode.requestFocus(); | 
					
						
							| 
									
										
										
										
											2020-11-28 18:06:27 +08:00
										 |  |  |  |       }); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-17 01:22:14 +08:00
										 |  |  |  |   @override | 
					
						
							|  |  |  |  |   Widget build(BuildContext context) { | 
					
						
							| 
									
										
										
										
											2020-11-29 14:28:07 +08:00
										 |  |  |  |     final pi = Provider.of<FfiModel>(context).pi; | 
					
						
							| 
									
										
										
										
											2021-09-01 01:35:01 +08:00
										 |  |  |  |     final hideKeyboard = isKeyboardShown() && _showEdit; | 
					
						
							| 
									
										
										
										
											2021-08-17 15:07:01 +08:00
										 |  |  |  |     final showActionButton = !_showBar || hideKeyboard; | 
					
						
							| 
									
										
										
										
											2022-06-13 21:07:26 +08:00
										 |  |  |  |     final keyboard = gFFI.ffiModel.permissions['keyboard'] != false; | 
					
						
							| 
									
										
										
										
											2022-04-26 21:21:08 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-22 18:29:04 +08:00
										 |  |  |  |     return WillPopScope( | 
					
						
							| 
									
										
										
										
											2020-11-29 14:19:01 +08:00
										 |  |  |  |       onWillPop: () async { | 
					
						
							| 
									
										
										
										
											2022-11-15 16:49:55 +08:00
										 |  |  |  |         clientClose(widget.id, gFFI.dialogManager); | 
					
						
							| 
									
										
										
										
											2020-11-29 14:19:01 +08:00
										 |  |  |  |         return false; | 
					
						
							|  |  |  |  |       }, | 
					
						
							| 
									
										
										
										
											2022-09-27 22:16:27 +08:00
										 |  |  |  |       child: getRawPointerAndKeyBody(Scaffold( | 
					
						
							|  |  |  |  |           // resizeToAvoidBottomInset: true,
 | 
					
						
							|  |  |  |  |           floatingActionButton: !showActionButton | 
					
						
							|  |  |  |  |               ? null | 
					
						
							|  |  |  |  |               : FloatingActionButton( | 
					
						
							|  |  |  |  |                   mini: !hideKeyboard, | 
					
						
							|  |  |  |  |                   child: Icon( | 
					
						
							|  |  |  |  |                       hideKeyboard ? Icons.expand_more : Icons.expand_less), | 
					
						
							|  |  |  |  |                   backgroundColor: MyTheme.accent, | 
					
						
							|  |  |  |  |                   onPressed: () { | 
					
						
							|  |  |  |  |                     setState(() { | 
					
						
							|  |  |  |  |                       if (hideKeyboard) { | 
					
						
							|  |  |  |  |                         _showEdit = false; | 
					
						
							|  |  |  |  |                         gFFI.invokeMethod("enable_soft_keyboard", false); | 
					
						
							|  |  |  |  |                         _mobileFocusNode.unfocus(); | 
					
						
							|  |  |  |  |                         _physicalFocusNode.requestFocus(); | 
					
						
							|  |  |  |  |                       } else { | 
					
						
							|  |  |  |  |                         _showBar = !_showBar; | 
					
						
							|  |  |  |  |                       } | 
					
						
							|  |  |  |  |                     }); | 
					
						
							|  |  |  |  |                   }), | 
					
						
							|  |  |  |  |           bottomNavigationBar: _showBar && pi.displays.isNotEmpty | 
					
						
							|  |  |  |  |               ? getBottomAppBar(keyboard) | 
					
						
							|  |  |  |  |               : null, | 
					
						
							|  |  |  |  |           body: Overlay( | 
					
						
							|  |  |  |  |             initialEntries: [ | 
					
						
							|  |  |  |  |               OverlayEntry(builder: (context) { | 
					
						
							|  |  |  |  |                 return Container( | 
					
						
							|  |  |  |  |                     color: Colors.black, | 
					
						
							|  |  |  |  |                     child: isWebDesktop | 
					
						
							|  |  |  |  |                         ? getBodyForDesktopWithListener(keyboard) | 
					
						
							|  |  |  |  |                         : SafeArea(child: | 
					
						
							|  |  |  |  |                             OrientationBuilder(builder: (ctx, orientation) { | 
					
						
							|  |  |  |  |                             if (_currentOrientation != orientation) { | 
					
						
							|  |  |  |  |                               Timer(const Duration(milliseconds: 200), () { | 
					
						
							|  |  |  |  |                                 gFFI.dialogManager | 
					
						
							|  |  |  |  |                                     .resetMobileActionsOverlay(ffi: gFFI); | 
					
						
							|  |  |  |  |                                 _currentOrientation = orientation; | 
					
						
							|  |  |  |  |                                 gFFI.canvasModel.updateViewStyle(); | 
					
						
							|  |  |  |  |                               }); | 
					
						
							|  |  |  |  |                             } | 
					
						
							|  |  |  |  |                             return Obx(() => Container( | 
					
						
							|  |  |  |  |                                 color: MyTheme.canvasColor, | 
					
						
							|  |  |  |  |                                 child: inputModel.isPhysicalMouse.value | 
					
						
							|  |  |  |  |                                     ? getBodyForMobile() | 
					
						
							|  |  |  |  |                                     : getBodyForMobileWithGesture())); | 
					
						
							|  |  |  |  |                           }))); | 
					
						
							|  |  |  |  |               }) | 
					
						
							|  |  |  |  |             ], | 
					
						
							|  |  |  |  |           ))), | 
					
						
							| 
									
										
										
										
											2020-11-29 14:19:01 +08:00
										 |  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2020-11-20 16:37:48 +08:00
										 |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-27 22:16:27 +08:00
										 |  |  |  |   Widget getRawPointerAndKeyBody(Widget child) { | 
					
						
							|  |  |  |  |     final keyboard = gFFI.ffiModel.permissions['keyboard'] != false; | 
					
						
							|  |  |  |  |     return RawPointerMouseRegion( | 
					
						
							|  |  |  |  |         cursor: keyboard ? SystemMouseCursors.none : MouseCursor.defer, | 
					
						
							|  |  |  |  |         inputModel: inputModel, | 
					
						
							|  |  |  |  |         child: RawKeyFocusScope( | 
					
						
							|  |  |  |  |             focusNode: _physicalFocusNode, | 
					
						
							|  |  |  |  |             inputModel: inputModel, | 
					
						
							|  |  |  |  |             child: child)); | 
					
						
							| 
									
										
										
										
											2022-04-25 18:25:25 +08:00
										 |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-28 22:44:54 +08:00
										 |  |  |  |   Widget getBottomAppBar(bool keyboard) { | 
					
						
							| 
									
										
										
										
											2022-02-02 01:20:14 +08:00
										 |  |  |  |     return BottomAppBar( | 
					
						
							|  |  |  |  |       elevation: 10, | 
					
						
							|  |  |  |  |       color: MyTheme.accent, | 
					
						
							|  |  |  |  |       child: Row( | 
					
						
							|  |  |  |  |         mainAxisSize: MainAxisSize.max, | 
					
						
							|  |  |  |  |         mainAxisAlignment: MainAxisAlignment.spaceBetween, | 
					
						
							|  |  |  |  |         children: <Widget>[ | 
					
						
							|  |  |  |  |           Row( | 
					
						
							|  |  |  |  |               children: <Widget>[ | 
					
						
							| 
									
										
										
										
											2022-08-02 13:10:09 +08:00
										 |  |  |  |                     IconButton( | 
					
						
							|  |  |  |  |                       color: Colors.white, | 
					
						
							|  |  |  |  |                       icon: Icon(Icons.clear), | 
					
						
							|  |  |  |  |                       onPressed: () { | 
					
						
							| 
									
										
										
										
											2022-11-15 16:49:55 +08:00
										 |  |  |  |                         clientClose(widget.id, gFFI.dialogManager); | 
					
						
							| 
									
										
										
										
											2022-08-02 13:10:09 +08:00
										 |  |  |  |                       }, | 
					
						
							|  |  |  |  |                     ) | 
					
						
							|  |  |  |  |                   ] + | 
					
						
							| 
									
										
										
										
											2022-02-02 01:20:14 +08:00
										 |  |  |  |                   <Widget>[ | 
					
						
							|  |  |  |  |                     IconButton( | 
					
						
							|  |  |  |  |                       color: Colors.white, | 
					
						
							|  |  |  |  |                       icon: Icon(Icons.tv), | 
					
						
							|  |  |  |  |                       onPressed: () { | 
					
						
							|  |  |  |  |                         setState(() => _showEdit = false); | 
					
						
							| 
									
										
										
										
											2022-09-29 18:56:47 +08:00
										 |  |  |  |                         showOptions(context, widget.id, gFFI.dialogManager); | 
					
						
							| 
									
										
										
										
											2022-02-02 01:20:14 +08:00
										 |  |  |  |                       }, | 
					
						
							|  |  |  |  |                     ) | 
					
						
							|  |  |  |  |                   ] + | 
					
						
							| 
									
										
										
										
											2022-05-23 16:02:37 +08:00
										 |  |  |  |                   (isWebDesktop | 
					
						
							| 
									
										
										
										
											2022-02-02 01:20:14 +08:00
										 |  |  |  |                       ? [] | 
					
						
							| 
									
										
										
										
											2022-06-13 21:07:26 +08:00
										 |  |  |  |                       : gFFI.ffiModel.isPeerAndroid | 
					
						
							|  |  |  |  |                           ? [ | 
					
						
							|  |  |  |  |                               IconButton( | 
					
						
							|  |  |  |  |                                 color: Colors.white, | 
					
						
							| 
									
										
										
										
											2022-09-08 22:18:02 +08:00
										 |  |  |  |                                 icon: const Icon(Icons.build), | 
					
						
							|  |  |  |  |                                 onPressed: () => gFFI.dialogManager | 
					
						
							|  |  |  |  |                                     .toggleMobileActionsOverlay(ffi: gFFI), | 
					
						
							| 
									
										
										
										
											2022-06-13 21:07:26 +08:00
										 |  |  |  |                               ) | 
					
						
							|  |  |  |  |                             ] | 
					
						
							|  |  |  |  |                           : [ | 
					
						
							|  |  |  |  |                               IconButton( | 
					
						
							|  |  |  |  |                                   color: Colors.white, | 
					
						
							|  |  |  |  |                                   icon: Icon(Icons.keyboard), | 
					
						
							|  |  |  |  |                                   onPressed: openKeyboard), | 
					
						
							|  |  |  |  |                               IconButton( | 
					
						
							|  |  |  |  |                                 color: Colors.white, | 
					
						
							|  |  |  |  |                                 icon: Icon(gFFI.ffiModel.touchMode | 
					
						
							|  |  |  |  |                                     ? Icons.touch_app | 
					
						
							|  |  |  |  |                                     : Icons.mouse), | 
					
						
							|  |  |  |  |                                 onPressed: changeTouchMode, | 
					
						
							|  |  |  |  |                               ), | 
					
						
							| 
									
										
										
										
											2022-08-02 13:10:09 +08:00
										 |  |  |  |                             ]) + | 
					
						
							| 
									
										
										
										
											2022-03-28 00:36:53 +08:00
										 |  |  |  |                   (isWeb | 
					
						
							|  |  |  |  |                       ? [] | 
					
						
							|  |  |  |  |                       : <Widget>[ | 
					
						
							| 
									
										
										
										
											2022-08-02 13:10:09 +08:00
										 |  |  |  |                           IconButton( | 
					
						
							|  |  |  |  |                             color: Colors.white, | 
					
						
							|  |  |  |  |                             icon: Icon(Icons.message), | 
					
						
							|  |  |  |  |                             onPressed: () { | 
					
						
							| 
									
										
										
										
											2022-06-13 21:07:26 +08:00
										 |  |  |  |                               gFFI.chatModel | 
					
						
							|  |  |  |  |                                   .changeCurrentID(ChatModel.clientModeID); | 
					
						
							| 
									
										
										
										
											2022-08-11 10:19:12 +08:00
										 |  |  |  |                               gFFI.chatModel.toggleChatOverlay(); | 
					
						
							| 
									
										
										
										
											2022-06-13 21:07:26 +08:00
										 |  |  |  |                             }, | 
					
						
							| 
									
										
										
										
											2022-08-02 13:10:09 +08:00
										 |  |  |  |                           ) | 
					
						
							|  |  |  |  |                         ]) + | 
					
						
							| 
									
										
										
										
											2022-03-28 00:36:53 +08:00
										 |  |  |  |                   [ | 
					
						
							| 
									
										
										
										
											2022-02-02 01:20:14 +08:00
										 |  |  |  |                     IconButton( | 
					
						
							|  |  |  |  |                       color: Colors.white, | 
					
						
							|  |  |  |  |                       icon: Icon(Icons.more_vert), | 
					
						
							|  |  |  |  |                       onPressed: () { | 
					
						
							|  |  |  |  |                         setState(() => _showEdit = false); | 
					
						
							| 
									
										
										
										
											2022-08-05 20:29:43 +08:00
										 |  |  |  |                         showActions(widget.id); | 
					
						
							| 
									
										
										
										
											2022-02-02 01:20:14 +08:00
										 |  |  |  |                       }, | 
					
						
							|  |  |  |  |                     ), | 
					
						
							|  |  |  |  |                   ]), | 
					
						
							|  |  |  |  |           IconButton( | 
					
						
							|  |  |  |  |               color: Colors.white, | 
					
						
							|  |  |  |  |               icon: Icon(Icons.expand_more), | 
					
						
							|  |  |  |  |               onPressed: () { | 
					
						
							|  |  |  |  |                 setState(() => _showBar = !_showBar); | 
					
						
							|  |  |  |  |               }), | 
					
						
							|  |  |  |  |         ], | 
					
						
							|  |  |  |  |       ), | 
					
						
							|  |  |  |  |     ); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-17 18:00:44 +08:00
										 |  |  |  |   /// touchMode only:
 | 
					
						
							|  |  |  |  |   ///   LongPress -> right click
 | 
					
						
							|  |  |  |  |   ///   OneFingerPan -> start/end -> left down start/end
 | 
					
						
							|  |  |  |  |   ///   onDoubleTapDown -> move to
 | 
					
						
							|  |  |  |  |   ///   onLongPressDown => move to
 | 
					
						
							|  |  |  |  |   ///
 | 
					
						
							|  |  |  |  |   /// mouseMode only:
 | 
					
						
							|  |  |  |  |   ///   DoubleFiner -> right click
 | 
					
						
							|  |  |  |  |   ///   HoldDrag -> left drag
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-02 21:24:49 +08:00
										 |  |  |  |   Offset _cacheLongPressPosition = Offset(0, 0); | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |  |   Widget getBodyForMobileWithGesture() { | 
					
						
							| 
									
										
										
										
											2022-06-13 21:07:26 +08:00
										 |  |  |  |     final touchMode = gFFI.ffiModel.touchMode; | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |  |     return getMixinGestureDetector( | 
					
						
							|  |  |  |  |         child: getBodyForMobile(), | 
					
						
							|  |  |  |  |         onTapUp: (d) { | 
					
						
							| 
									
										
										
										
											2022-04-28 22:44:54 +08:00
										 |  |  |  |           if (touchMode) { | 
					
						
							| 
									
										
										
										
											2022-09-27 20:35:02 +08:00
										 |  |  |  |             gFFI.cursorModel.move(d.localPosition.dx, d.localPosition.dy); | 
					
						
							|  |  |  |  |             inputModel.tap(MouseButtons.left); | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |  |           } else { | 
					
						
							| 
									
										
										
										
											2022-09-27 20:35:02 +08:00
										 |  |  |  |             inputModel.tap(MouseButtons.left); | 
					
						
							| 
									
										
										
										
											2022-02-17 18:00:44 +08:00
										 |  |  |  |           } | 
					
						
							|  |  |  |  |         }, | 
					
						
							| 
									
										
										
										
											2022-02-23 21:32:33 +08:00
										 |  |  |  |         onDoubleTapDown: (d) { | 
					
						
							| 
									
										
										
										
											2022-04-28 22:44:54 +08:00
										 |  |  |  |           if (touchMode) { | 
					
						
							| 
									
										
										
										
											2022-06-13 21:07:26 +08:00
										 |  |  |  |             gFFI.cursorModel.move(d.localPosition.dx, d.localPosition.dy); | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |  |           } | 
					
						
							|  |  |  |  |         }, | 
					
						
							|  |  |  |  |         onDoubleTap: () { | 
					
						
							| 
									
										
										
										
											2022-09-27 20:35:02 +08:00
										 |  |  |  |           inputModel.tap(MouseButtons.left); | 
					
						
							|  |  |  |  |           inputModel.tap(MouseButtons.left); | 
					
						
							| 
									
										
										
										
											2022-02-17 18:00:44 +08:00
										 |  |  |  |         }, | 
					
						
							| 
									
										
										
										
											2022-02-23 21:32:33 +08:00
										 |  |  |  |         onLongPressDown: (d) { | 
					
						
							| 
									
										
										
										
											2022-04-28 22:44:54 +08:00
										 |  |  |  |           if (touchMode) { | 
					
						
							| 
									
										
										
										
											2022-06-13 21:07:26 +08:00
										 |  |  |  |             gFFI.cursorModel.move(d.localPosition.dx, d.localPosition.dy); | 
					
						
							| 
									
										
										
										
											2022-07-02 21:24:49 +08:00
										 |  |  |  |             _cacheLongPressPosition = d.localPosition; | 
					
						
							| 
									
										
										
										
											2022-02-17 18:00:44 +08:00
										 |  |  |  |           } | 
					
						
							|  |  |  |  |         }, | 
					
						
							|  |  |  |  |         onLongPress: () { | 
					
						
							| 
									
										
										
										
											2022-07-02 21:24:49 +08:00
										 |  |  |  |           if (touchMode) { | 
					
						
							| 
									
										
										
										
											2022-08-01 14:33:08 +08:00
										 |  |  |  |             gFFI.cursorModel | 
					
						
							| 
									
										
										
										
											2022-07-02 21:24:49 +08:00
										 |  |  |  |                 .move(_cacheLongPressPosition.dx, _cacheLongPressPosition.dy); | 
					
						
							|  |  |  |  |           } | 
					
						
							| 
									
										
										
										
											2022-09-27 20:35:02 +08:00
										 |  |  |  |           inputModel.tap(MouseButtons.right); | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |  |         }, | 
					
						
							|  |  |  |  |         onDoubleFinerTap: (d) { | 
					
						
							| 
									
										
										
										
											2022-04-28 22:44:54 +08:00
										 |  |  |  |           if (!touchMode) { | 
					
						
							| 
									
										
										
										
											2022-09-27 20:35:02 +08:00
										 |  |  |  |             inputModel.tap(MouseButtons.right); | 
					
						
							| 
									
										
										
										
											2022-02-17 18:00:44 +08:00
										 |  |  |  |           } | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |  |         }, | 
					
						
							|  |  |  |  |         onHoldDragStart: (d) { | 
					
						
							| 
									
										
										
										
											2022-04-28 22:44:54 +08:00
										 |  |  |  |           if (!touchMode) { | 
					
						
							| 
									
										
										
										
											2022-09-27 20:35:02 +08:00
										 |  |  |  |             inputModel.sendMouse('down', MouseButtons.left); | 
					
						
							| 
									
										
										
										
											2022-02-17 18:00:44 +08:00
										 |  |  |  |           } | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |  |         }, | 
					
						
							|  |  |  |  |         onHoldDragUpdate: (d) { | 
					
						
							| 
									
										
										
										
											2022-04-28 22:44:54 +08:00
										 |  |  |  |           if (!touchMode) { | 
					
						
							| 
									
										
										
										
											2022-06-13 21:07:26 +08:00
										 |  |  |  |             gFFI.cursorModel.updatePan(d.delta.dx, d.delta.dy, touchMode); | 
					
						
							| 
									
										
										
										
											2022-02-17 18:00:44 +08:00
										 |  |  |  |           } | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |  |         }, | 
					
						
							| 
									
										
										
										
											2022-04-26 21:21:08 +08:00
										 |  |  |  |         onHoldDragEnd: (_) { | 
					
						
							| 
									
										
										
										
											2022-04-28 22:44:54 +08:00
										 |  |  |  |           if (!touchMode) { | 
					
						
							| 
									
										
										
										
											2022-09-27 20:35:02 +08:00
										 |  |  |  |             inputModel.sendMouse('up', MouseButtons.left); | 
					
						
							| 
									
										
										
										
											2022-02-17 18:00:44 +08:00
										 |  |  |  |           } | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |  |         }, | 
					
						
							|  |  |  |  |         onOneFingerPanStart: (d) { | 
					
						
							| 
									
										
										
										
											2022-04-28 22:44:54 +08:00
										 |  |  |  |           if (touchMode) { | 
					
						
							| 
									
										
										
										
											2022-06-13 21:07:26 +08:00
										 |  |  |  |             gFFI.cursorModel.move(d.localPosition.dx, d.localPosition.dy); | 
					
						
							| 
									
										
										
										
											2022-09-27 20:35:02 +08:00
										 |  |  |  |             inputModel.sendMouse('down', MouseButtons.left); | 
					
						
							| 
									
										
										
										
											2022-07-02 21:24:49 +08:00
										 |  |  |  |           } else { | 
					
						
							| 
									
										
										
										
											2022-09-29 18:56:47 +08:00
										 |  |  |  |             final offset = gFFI.cursorModel.offset; | 
					
						
							|  |  |  |  |             final cursorX = offset.dx; | 
					
						
							|  |  |  |  |             final cursorY = offset.dy; | 
					
						
							| 
									
										
										
										
											2022-07-02 21:24:49 +08:00
										 |  |  |  |             final visible = | 
					
						
							| 
									
										
										
										
											2022-08-01 14:33:08 +08:00
										 |  |  |  |                 gFFI.cursorModel.getVisibleRect().inflate(1); // extend edges
 | 
					
						
							| 
									
										
										
										
											2022-07-02 21:24:49 +08:00
										 |  |  |  |             final size = MediaQueryData.fromWindow(ui.window).size; | 
					
						
							|  |  |  |  |             if (!visible.contains(Offset(cursorX, cursorY))) { | 
					
						
							| 
									
										
										
										
											2022-08-01 14:33:08 +08:00
										 |  |  |  |               gFFI.cursorModel.move(size.width / 2, size.height / 2); | 
					
						
							| 
									
										
										
										
											2022-07-02 21:24:49 +08:00
										 |  |  |  |             } | 
					
						
							| 
									
										
										
										
											2022-02-17 18:00:44 +08:00
										 |  |  |  |           } | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |  |         }, | 
					
						
							|  |  |  |  |         onOneFingerPanUpdate: (d) { | 
					
						
							| 
									
										
										
										
											2022-06-13 21:07:26 +08:00
										 |  |  |  |           gFFI.cursorModel.updatePan(d.delta.dx, d.delta.dy, touchMode); | 
					
						
							| 
									
										
										
										
											2022-02-17 18:00:44 +08:00
										 |  |  |  |         }, | 
					
						
							|  |  |  |  |         onOneFingerPanEnd: (d) { | 
					
						
							| 
									
										
										
										
											2022-04-28 22:44:54 +08:00
										 |  |  |  |           if (touchMode) { | 
					
						
							| 
									
										
										
										
											2022-09-27 20:35:02 +08:00
										 |  |  |  |             inputModel.sendMouse('up', MouseButtons.left); | 
					
						
							| 
									
										
										
										
											2022-02-17 18:00:44 +08:00
										 |  |  |  |           } | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |  |         }, | 
					
						
							| 
									
										
										
										
											2022-05-10 11:27:16 +08:00
										 |  |  |  |         // scale + pan event
 | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |  |         onTwoFingerScaleUpdate: (d) { | 
					
						
							| 
									
										
										
										
											2022-06-13 21:07:26 +08:00
										 |  |  |  |           gFFI.canvasModel.updateScale(d.scale / _scale); | 
					
						
							| 
									
										
										
										
											2022-02-17 15:22:14 +08:00
										 |  |  |  |           _scale = d.scale; | 
					
						
							| 
									
										
										
										
											2022-06-13 21:07:26 +08:00
										 |  |  |  |           gFFI.canvasModel.panX(d.focalPointDelta.dx); | 
					
						
							|  |  |  |  |           gFFI.canvasModel.panY(d.focalPointDelta.dy); | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |  |         }, | 
					
						
							| 
									
										
										
										
											2022-05-10 14:44:47 +08:00
										 |  |  |  |         onTwoFingerScaleEnd: (d) { | 
					
						
							|  |  |  |  |           _scale = 1; | 
					
						
							| 
									
										
										
										
											2022-11-17 18:52:27 +08:00
										 |  |  |  |           bind.sessionSetViewStyle(id: widget.id, value: ""); | 
					
						
							| 
									
										
										
										
											2022-05-10 14:44:47 +08:00
										 |  |  |  |         }, | 
					
						
							| 
									
										
										
										
											2022-06-13 21:07:26 +08:00
										 |  |  |  |         onThreeFingerVerticalDragUpdate: gFFI.ffiModel.isPeerAndroid | 
					
						
							| 
									
										
										
										
											2022-05-10 14:44:47 +08:00
										 |  |  |  |             ? null | 
					
						
							|  |  |  |  |             : (d) { | 
					
						
							| 
									
										
										
										
											2022-06-13 21:07:26 +08:00
										 |  |  |  |                 _mouseScrollIntegral += d.delta.dy / 4; | 
					
						
							|  |  |  |  |                 if (_mouseScrollIntegral > 1) { | 
					
						
							| 
									
										
										
										
											2022-09-27 20:35:02 +08:00
										 |  |  |  |                   inputModel.scroll(1); | 
					
						
							| 
									
										
										
										
											2022-06-13 21:07:26 +08:00
										 |  |  |  |                   _mouseScrollIntegral = 0; | 
					
						
							|  |  |  |  |                 } else if (_mouseScrollIntegral < -1) { | 
					
						
							| 
									
										
										
										
											2022-09-27 20:35:02 +08:00
										 |  |  |  |                   inputModel.scroll(-1); | 
					
						
							| 
									
										
										
										
											2022-06-13 21:07:26 +08:00
										 |  |  |  |                   _mouseScrollIntegral = 0; | 
					
						
							|  |  |  |  |                 } | 
					
						
							|  |  |  |  |               }); | 
					
						
							| 
									
										
										
										
											2022-02-02 01:20:14 +08:00
										 |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-03 00:53:59 +08:00
										 |  |  |  |   Widget getBodyForMobile() { | 
					
						
							|  |  |  |  |     return Container( | 
					
						
							|  |  |  |  |         color: MyTheme.canvasColor, | 
					
						
							| 
									
										
										
										
											2022-11-29 16:36:35 +08:00
										 |  |  |  |         child: Stack(children: () { | 
					
						
							|  |  |  |  |           final paints = [ | 
					
						
							|  |  |  |  |             ImagePaint(), | 
					
						
							|  |  |  |  |             QualityMonitor(gFFI.qualityMonitorModel), | 
					
						
							|  |  |  |  |             getHelpTools(), | 
					
						
							|  |  |  |  |             SizedBox( | 
					
						
							|  |  |  |  |               width: 0, | 
					
						
							|  |  |  |  |               height: 0, | 
					
						
							|  |  |  |  |               child: !_showEdit | 
					
						
							|  |  |  |  |                   ? Container() | 
					
						
							|  |  |  |  |                   : TextFormField( | 
					
						
							|  |  |  |  |                       textInputAction: TextInputAction.newline, | 
					
						
							|  |  |  |  |                       autocorrect: false, | 
					
						
							|  |  |  |  |                       enableSuggestions: false, | 
					
						
							|  |  |  |  |                       autofocus: true, | 
					
						
							|  |  |  |  |                       focusNode: _mobileFocusNode, | 
					
						
							|  |  |  |  |                       maxLines: null, | 
					
						
							|  |  |  |  |                       initialValue: _value, | 
					
						
							|  |  |  |  |                       // trick way to make backspace work always
 | 
					
						
							|  |  |  |  |                       keyboardType: TextInputType.multiline, | 
					
						
							|  |  |  |  |                       onChanged: handleSoftKeyboardInput, | 
					
						
							|  |  |  |  |                     ), | 
					
						
							|  |  |  |  |             ), | 
					
						
							|  |  |  |  |           ]; | 
					
						
							| 
									
										
										
										
											2022-12-31 21:41:16 +08:00
										 |  |  |  |           if (!gFFI.canvasModel.cursorEmbedded) { | 
					
						
							| 
									
										
										
										
											2022-11-29 16:36:35 +08:00
										 |  |  |  |             paints.add(CursorPaint()); | 
					
						
							|  |  |  |  |           } | 
					
						
							|  |  |  |  |           return paints; | 
					
						
							|  |  |  |  |         }())); | 
					
						
							| 
									
										
										
										
											2022-02-03 00:53:59 +08:00
										 |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-25 18:25:25 +08:00
										 |  |  |  |   Widget getBodyForDesktopWithListener(bool keyboard) { | 
					
						
							| 
									
										
										
										
											2022-02-06 16:29:56 +08:00
										 |  |  |  |     var paints = <Widget>[ImagePaint()]; | 
					
						
							| 
									
										
										
										
											2022-12-31 21:41:16 +08:00
										 |  |  |  |     if (!gFFI.canvasModel.cursorEmbedded) { | 
					
						
							| 
									
										
										
										
											2022-11-29 16:36:35 +08:00
										 |  |  |  |       final cursor = bind.sessionGetToggleOptionSync( | 
					
						
							|  |  |  |  |           id: widget.id, arg: 'show-remote-cursor'); | 
					
						
							|  |  |  |  |       if (keyboard || cursor) { | 
					
						
							|  |  |  |  |         paints.add(CursorPaint()); | 
					
						
							|  |  |  |  |       } | 
					
						
							| 
									
										
										
										
											2022-02-06 16:29:56 +08:00
										 |  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-04-25 18:25:25 +08:00
										 |  |  |  |     return Container( | 
					
						
							|  |  |  |  |         color: MyTheme.canvasColor, child: Stack(children: paints)); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-05 20:29:43 +08:00
										 |  |  |  |   void showActions(String id) async { | 
					
						
							| 
									
										
										
										
											2021-08-21 17:18:14 +08:00
										 |  |  |  |     final size = MediaQuery.of(context).size; | 
					
						
							|  |  |  |  |     final x = 120.0; | 
					
						
							|  |  |  |  |     final y = size.height; | 
					
						
							|  |  |  |  |     final more = <PopupMenuItem<String>>[]; | 
					
						
							| 
									
										
										
										
											2022-06-13 21:07:26 +08:00
										 |  |  |  |     final pi = gFFI.ffiModel.pi; | 
					
						
							|  |  |  |  |     final perms = gFFI.ffiModel.permissions; | 
					
						
							| 
									
										
										
										
											2022-02-02 00:46:21 +08:00
										 |  |  |  |     if (pi.version.isNotEmpty) { | 
					
						
							| 
									
										
										
										
											2021-08-21 17:18:14 +08:00
										 |  |  |  |       more.add(PopupMenuItem<String>( | 
					
						
							|  |  |  |  |           child: Text(translate('Refresh')), value: 'refresh')); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |     more.add(PopupMenuItem<String>( | 
					
						
							|  |  |  |  |         child: Row( | 
					
						
							|  |  |  |  |             children: ([ | 
					
						
							| 
									
										
										
										
											2022-08-04 17:24:02 +08:00
										 |  |  |  |           Text(translate('OS Password')), | 
					
						
							| 
									
										
										
										
											2022-06-13 21:07:26 +08:00
										 |  |  |  |           TextButton( | 
					
						
							|  |  |  |  |             style: flatButtonStyle, | 
					
						
							|  |  |  |  |             onPressed: () { | 
					
						
							| 
									
										
										
										
											2022-08-12 18:42:02 +08:00
										 |  |  |  |               showSetOSPassword(id, false, gFFI.dialogManager); | 
					
						
							| 
									
										
										
										
											2022-06-13 21:07:26 +08:00
										 |  |  |  |             }, | 
					
						
							|  |  |  |  |             child: Icon(Icons.edit, color: MyTheme.accent), | 
					
						
							|  |  |  |  |           ) | 
					
						
							|  |  |  |  |         ])), | 
					
						
							| 
									
										
										
										
											2021-08-21 17:18:14 +08:00
										 |  |  |  |         value: 'enter_os_password')); | 
					
						
							| 
									
										
										
										
											2022-05-23 16:02:37 +08:00
										 |  |  |  |     if (!isWebDesktop) { | 
					
						
							| 
									
										
										
										
											2022-02-02 00:46:21 +08:00
										 |  |  |  |       if (perms['keyboard'] != false && perms['clipboard'] != false) { | 
					
						
							|  |  |  |  |         more.add(PopupMenuItem<String>( | 
					
						
							|  |  |  |  |             child: Text(translate('Paste')), value: 'paste')); | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  |       more.add(PopupMenuItem<String>( | 
					
						
							|  |  |  |  |           child: Text(translate('Reset canvas')), value: 'reset_canvas')); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |     if (perms['keyboard'] != false) { | 
					
						
							| 
									
										
										
										
											2022-09-27 22:56:18 +08:00
										 |  |  |  |       more.add(PopupMenuItem<String>( | 
					
						
							|  |  |  |  |           child: Text(translate('Physical Keyboard Input Mode')), | 
					
						
							|  |  |  |  |           value: 'input-mode')); | 
					
						
							| 
									
										
										
										
											2022-02-02 00:46:21 +08:00
										 |  |  |  |       if (pi.platform == 'Linux' || pi.sasEnabled) { | 
					
						
							|  |  |  |  |         more.add(PopupMenuItem<String>( | 
					
						
							| 
									
										
										
										
											2022-09-27 22:16:27 +08:00
										 |  |  |  |             child: Text('${translate('Insert')} Ctrl + Alt + Del'), | 
					
						
							| 
									
										
										
										
											2022-02-02 00:46:21 +08:00
										 |  |  |  |             value: 'cad')); | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  |       more.add(PopupMenuItem<String>( | 
					
						
							|  |  |  |  |           child: Text(translate('Insert Lock')), value: 'lock')); | 
					
						
							|  |  |  |  |       if (pi.platform == 'Windows' && | 
					
						
							| 
									
										
										
										
											2022-08-16 15:22:57 +08:00
										 |  |  |  |           await bind.sessionGetToggleOption(id: id, arg: 'privacy-mode') != | 
					
						
							| 
									
										
										
										
											2022-08-05 20:29:43 +08:00
										 |  |  |  |               true) { | 
					
						
							| 
									
										
										
										
											2022-02-02 00:46:21 +08:00
										 |  |  |  |         more.add(PopupMenuItem<String>( | 
					
						
							| 
									
										
										
										
											2022-09-27 22:16:27 +08:00
										 |  |  |  |             child: Text(translate( | 
					
						
							|  |  |  |  |                 '${gFFI.ffiModel.inputBlocked ? 'Unb' : 'B'}lock user input')), | 
					
						
							| 
									
										
										
										
											2022-02-02 00:46:21 +08:00
										 |  |  |  |             value: 'block-input')); | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-10-25 11:27:34 +09:00
										 |  |  |  |     if (perms["restart"] != false && | 
					
						
							| 
									
										
										
										
											2022-08-04 17:24:02 +08:00
										 |  |  |  |         (pi.platform == "Linux" || | 
					
						
							|  |  |  |  |             pi.platform == "Windows" || | 
					
						
							|  |  |  |  |             pi.platform == "Mac OS")) { | 
					
						
							|  |  |  |  |       more.add(PopupMenuItem<String>( | 
					
						
							|  |  |  |  |           child: Text(translate('Restart Remote Device')), value: 'restart')); | 
					
						
							|  |  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-10-25 11:27:34 +09:00
										 |  |  |  |     // Currently only support VP9
 | 
					
						
							|  |  |  |  |     if (gFFI.recordingModel.start || | 
					
						
							|  |  |  |  |         (perms["recording"] != false && | 
					
						
							|  |  |  |  |             gFFI.qualityMonitorModel.data.codecFormat == "VP9")) { | 
					
						
							|  |  |  |  |       more.add(PopupMenuItem<String>( | 
					
						
							|  |  |  |  |           child: Row( | 
					
						
							|  |  |  |  |             children: [ | 
					
						
							|  |  |  |  |               Text(translate(gFFI.recordingModel.start | 
					
						
							|  |  |  |  |                   ? 'Stop session recording' | 
					
						
							|  |  |  |  |                   : 'Start session recording')), | 
					
						
							|  |  |  |  |               Padding( | 
					
						
							|  |  |  |  |                 padding: EdgeInsets.only(left: 12), | 
					
						
							|  |  |  |  |                 child: Icon( | 
					
						
							|  |  |  |  |                     gFFI.recordingModel.start | 
					
						
							|  |  |  |  |                         ? Icons.pause_circle_filled | 
					
						
							|  |  |  |  |                         : Icons.videocam_outlined, | 
					
						
							|  |  |  |  |                     color: MyTheme.accent), | 
					
						
							|  |  |  |  |               ) | 
					
						
							|  |  |  |  |             ], | 
					
						
							|  |  |  |  |           ), | 
					
						
							|  |  |  |  |           value: 'record')); | 
					
						
							|  |  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-08-02 13:10:09 +08:00
										 |  |  |  |     () async { | 
					
						
							| 
									
										
										
										
											2021-08-21 17:18:14 +08:00
										 |  |  |  |       var value = await showMenu( | 
					
						
							|  |  |  |  |         context: context, | 
					
						
							|  |  |  |  |         position: RelativeRect.fromLTRB(x, y, x, y), | 
					
						
							| 
									
										
										
										
											2022-02-02 00:46:21 +08:00
										 |  |  |  |         items: more, | 
					
						
							| 
									
										
										
										
											2021-08-21 17:18:14 +08:00
										 |  |  |  |         elevation: 8, | 
					
						
							|  |  |  |  |       ); | 
					
						
							|  |  |  |  |       if (value == 'cad') { | 
					
						
							| 
									
										
										
										
											2022-08-05 20:29:43 +08:00
										 |  |  |  |         bind.sessionCtrlAltDel(id: widget.id); | 
					
						
							| 
									
										
										
										
											2022-09-27 22:56:18 +08:00
										 |  |  |  |       } else if (value == 'input-mode') { | 
					
						
							|  |  |  |  |         changePhysicalKeyboardInputMode(); | 
					
						
							| 
									
										
										
										
											2021-08-21 17:18:14 +08:00
										 |  |  |  |       } else if (value == 'lock') { | 
					
						
							| 
									
										
										
										
											2022-08-05 20:29:43 +08:00
										 |  |  |  |         bind.sessionLockScreen(id: widget.id); | 
					
						
							| 
									
										
										
										
											2022-02-02 00:46:21 +08:00
										 |  |  |  |       } else if (value == 'block-input') { | 
					
						
							| 
									
										
										
										
											2022-08-05 20:29:43 +08:00
										 |  |  |  |         bind.sessionToggleOption( | 
					
						
							|  |  |  |  |             id: widget.id, | 
					
						
							| 
									
										
										
										
											2022-09-27 22:16:27 +08:00
										 |  |  |  |             value: '${gFFI.ffiModel.inputBlocked ? 'un' : ''}block-input'); | 
					
						
							| 
									
										
										
										
											2022-06-13 21:07:26 +08:00
										 |  |  |  |         gFFI.ffiModel.inputBlocked = !gFFI.ffiModel.inputBlocked; | 
					
						
							| 
									
										
										
										
											2021-08-21 17:18:14 +08:00
										 |  |  |  |       } else if (value == 'refresh') { | 
					
						
							| 
									
										
										
										
											2022-08-05 20:29:43 +08:00
										 |  |  |  |         bind.sessionRefresh(id: widget.id); | 
					
						
							| 
									
										
										
										
											2021-08-21 17:18:14 +08:00
										 |  |  |  |       } else if (value == 'paste') { | 
					
						
							| 
									
										
										
										
											2022-08-02 13:10:09 +08:00
										 |  |  |  |         () async { | 
					
						
							| 
									
										
										
										
											2022-02-17 15:22:14 +08:00
										 |  |  |  |           ClipboardData? data = await Clipboard.getData(Clipboard.kTextPlain); | 
					
						
							|  |  |  |  |           if (data != null && data.text != null) { | 
					
						
							| 
									
										
										
										
											2022-08-05 20:29:43 +08:00
										 |  |  |  |             bind.sessionInputString(id: widget.id, value: data.text ?? ""); | 
					
						
							| 
									
										
										
										
											2021-08-21 17:18:14 +08:00
										 |  |  |  |           } | 
					
						
							|  |  |  |  |         }(); | 
					
						
							|  |  |  |  |       } else if (value == 'enter_os_password') { | 
					
						
							| 
									
										
										
										
											2022-08-13 15:08:17 +08:00
										 |  |  |  |         // FIXME:
 | 
					
						
							|  |  |  |  |         // null means no session of id
 | 
					
						
							|  |  |  |  |         // empty string means no password
 | 
					
						
							| 
									
										
										
										
											2022-11-24 11:19:16 +08:00
										 |  |  |  |         var password = await bind.sessionGetOption(id: id, arg: 'os-password'); | 
					
						
							| 
									
										
										
										
											2022-08-05 20:29:43 +08:00
										 |  |  |  |         if (password != null) { | 
					
						
							|  |  |  |  |           bind.sessionInputOsPassword(id: widget.id, value: password); | 
					
						
							| 
									
										
										
										
											2021-08-21 17:18:14 +08:00
										 |  |  |  |         } else { | 
					
						
							| 
									
										
										
										
											2022-08-12 18:42:02 +08:00
										 |  |  |  |           showSetOSPassword(id, true, gFFI.dialogManager); | 
					
						
							| 
									
										
										
										
											2021-08-21 17:18:14 +08:00
										 |  |  |  |         } | 
					
						
							|  |  |  |  |       } else if (value == 'reset_canvas') { | 
					
						
							| 
									
										
										
										
											2022-06-13 21:07:26 +08:00
										 |  |  |  |         gFFI.cursorModel.reset(); | 
					
						
							| 
									
										
										
										
											2022-08-04 17:24:02 +08:00
										 |  |  |  |       } else if (value == 'restart') { | 
					
						
							| 
									
										
										
										
											2022-08-12 18:42:02 +08:00
										 |  |  |  |         showRestartRemoteDevice(pi, widget.id, gFFI.dialogManager); | 
					
						
							| 
									
										
										
										
											2022-10-25 11:27:34 +09:00
										 |  |  |  |       } else if (value == 'record') { | 
					
						
							|  |  |  |  |         gFFI.recordingModel.toggle(); | 
					
						
							| 
									
										
										
										
											2021-08-21 17:18:14 +08:00
										 |  |  |  |       } | 
					
						
							|  |  |  |  |     }(); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-24 15:59:03 +08:00
										 |  |  |  |   void changeTouchMode() { | 
					
						
							|  |  |  |  |     setState(() => _showEdit = false); | 
					
						
							|  |  |  |  |     showModalBottomSheet( | 
					
						
							| 
									
										
										
										
											2022-09-23 16:31:50 +08:00
										 |  |  |  |         // backgroundColor: MyTheme.grayBg,
 | 
					
						
							| 
									
										
										
										
											2022-02-24 15:59:03 +08:00
										 |  |  |  |         isScrollControlled: true, | 
					
						
							|  |  |  |  |         context: context, | 
					
						
							|  |  |  |  |         shape: const RoundedRectangleBorder( | 
					
						
							|  |  |  |  |             borderRadius: BorderRadius.vertical(top: Radius.circular(5))), | 
					
						
							|  |  |  |  |         builder: (context) => DraggableScrollableSheet( | 
					
						
							|  |  |  |  |             expand: false, | 
					
						
							|  |  |  |  |             builder: (context, scrollController) { | 
					
						
							|  |  |  |  |               return SingleChildScrollView( | 
					
						
							| 
									
										
										
										
											2022-09-12 11:23:45 +08:00
										 |  |  |  |                   controller: ScrollController(), | 
					
						
							| 
									
										
										
										
											2022-02-24 15:59:03 +08:00
										 |  |  |  |                   padding: EdgeInsets.symmetric(vertical: 10), | 
					
						
							|  |  |  |  |                   child: GestureHelp( | 
					
						
							| 
									
										
										
										
											2022-06-13 21:07:26 +08:00
										 |  |  |  |                       touchMode: gFFI.ffiModel.touchMode, | 
					
						
							| 
									
										
										
										
											2022-02-24 15:59:03 +08:00
										 |  |  |  |                       onTouchModeChange: (t) { | 
					
						
							| 
									
										
										
										
											2022-06-13 21:07:26 +08:00
										 |  |  |  |                         gFFI.ffiModel.toggleTouchMode(); | 
					
						
							|  |  |  |  |                         final v = gFFI.ffiModel.touchMode ? 'Y' : ''; | 
					
						
							| 
									
										
										
										
											2022-08-05 20:29:43 +08:00
										 |  |  |  |                         bind.sessionPeerOption( | 
					
						
							|  |  |  |  |                             id: widget.id, name: "touch", value: v); | 
					
						
							| 
									
										
										
										
											2022-02-24 15:59:03 +08:00
										 |  |  |  |                       })); | 
					
						
							|  |  |  |  |             })); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-27 22:56:18 +08:00
										 |  |  |  |   void changePhysicalKeyboardInputMode() async { | 
					
						
							| 
									
										
										
										
											2022-11-15 23:09:29 -08:00
										 |  |  |  |     var current = await bind.sessionGetKeyboardMode(id: widget.id) ?? "legacy"; | 
					
						
							| 
									
										
										
										
											2022-09-27 22:56:18 +08:00
										 |  |  |  |     gFFI.dialogManager.show((setState, close) { | 
					
						
							|  |  |  |  |       void setMode(String? v) async { | 
					
						
							| 
									
										
										
										
											2022-11-15 23:09:29 -08:00
										 |  |  |  |         await bind.sessionPeerOption( | 
					
						
							| 
									
										
										
										
											2022-12-20 23:55:54 +09:00
										 |  |  |  |             id: widget.id, name: "keyboard-mode", value: v ?? ""); | 
					
						
							| 
									
										
										
										
											2022-09-27 22:56:18 +08:00
										 |  |  |  |         setState(() => current = v ?? ''); | 
					
						
							|  |  |  |  |         Future.delayed(Duration(milliseconds: 300), close); | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       return CustomAlertDialog( | 
					
						
							|  |  |  |  |           title: Text(translate('Physical Keyboard Input Mode')), | 
					
						
							|  |  |  |  |           content: Column(mainAxisSize: MainAxisSize.min, children: [ | 
					
						
							|  |  |  |  |             getRadio('Legacy mode', 'legacy', current, setMode, | 
					
						
							|  |  |  |  |                 contentPadding: EdgeInsets.zero), | 
					
						
							|  |  |  |  |             getRadio('Map mode', 'map', current, setMode, | 
					
						
							|  |  |  |  |                 contentPadding: EdgeInsets.zero), | 
					
						
							|  |  |  |  |           ])); | 
					
						
							|  |  |  |  |     }, clickMaskDismiss: true); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-25 13:03:48 +08:00
										 |  |  |  |   Widget getHelpTools() { | 
					
						
							| 
									
										
										
										
											2021-09-01 01:35:01 +08:00
										 |  |  |  |     final keyboard = isKeyboardShown(); | 
					
						
							| 
									
										
										
										
											2022-02-24 15:59:03 +08:00
										 |  |  |  |     if (!keyboard) { | 
					
						
							| 
									
										
										
										
											2020-11-25 13:03:48 +08:00
										 |  |  |  |       return SizedBox(); | 
					
						
							|  |  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-08-17 15:07:01 +08:00
										 |  |  |  |     final size = MediaQuery.of(context).size; | 
					
						
							| 
									
										
										
										
											2022-09-27 20:35:02 +08:00
										 |  |  |  |     wrap(String text, void Function() onPressed, | 
					
						
							| 
									
										
										
										
											2022-02-17 15:22:14 +08:00
										 |  |  |  |         [bool? active, IconData? icon]) { | 
					
						
							| 
									
										
										
										
											2021-08-02 22:21:23 +08:00
										 |  |  |  |       return TextButton( | 
					
						
							|  |  |  |  |           style: TextButton.styleFrom( | 
					
						
							|  |  |  |  |             minimumSize: Size(0, 0), | 
					
						
							| 
									
										
										
										
											2022-02-16 23:08:23 +08:00
										 |  |  |  |             padding: EdgeInsets.symmetric(vertical: 10, horizontal: 9.75), | 
					
						
							|  |  |  |  |             //adds padding inside the button
 | 
					
						
							|  |  |  |  |             tapTargetSize: MaterialTapTargetSize.shrinkWrap, | 
					
						
							|  |  |  |  |             //limits the touch area to the button area
 | 
					
						
							| 
									
										
										
										
											2021-08-02 22:21:23 +08:00
										 |  |  |  |             shape: RoundedRectangleBorder( | 
					
						
							|  |  |  |  |               borderRadius: BorderRadius.circular(5.0), | 
					
						
							|  |  |  |  |             ), | 
					
						
							|  |  |  |  |             backgroundColor: active == true ? MyTheme.accent80 : null, | 
					
						
							|  |  |  |  |           ), | 
					
						
							|  |  |  |  |           child: icon != null | 
					
						
							|  |  |  |  |               ? Icon(icon, size: 17, color: Colors.white) | 
					
						
							|  |  |  |  |               : Text(translate(text), | 
					
						
							| 
									
										
										
										
											2022-06-13 21:07:26 +08:00
										 |  |  |  |                   style: TextStyle(color: Colors.white, fontSize: 11)), | 
					
						
							| 
									
										
										
										
											2021-08-02 22:21:23 +08:00
										 |  |  |  |           onPressed: onPressed); | 
					
						
							| 
									
										
										
										
											2022-09-27 20:35:02 +08:00
										 |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-13 21:07:26 +08:00
										 |  |  |  |     final pi = gFFI.ffiModel.pi; | 
					
						
							| 
									
										
										
										
											2021-08-11 00:22:47 +08:00
										 |  |  |  |     final isMac = pi.platform == "Mac OS"; | 
					
						
							| 
									
										
										
										
											2020-11-25 16:28:46 +08:00
										 |  |  |  |     final modifiers = <Widget>[ | 
					
						
							| 
									
										
										
										
											2021-08-17 15:07:01 +08:00
										 |  |  |  |       wrap('Ctrl ', () { | 
					
						
							| 
									
										
										
										
											2022-09-27 20:35:02 +08:00
										 |  |  |  |         setState(() => inputModel.ctrl = !inputModel.ctrl); | 
					
						
							|  |  |  |  |       }, inputModel.ctrl), | 
					
						
							| 
									
										
										
										
											2021-08-17 15:07:01 +08:00
										 |  |  |  |       wrap(' Alt ', () { | 
					
						
							| 
									
										
										
										
											2022-09-27 20:35:02 +08:00
										 |  |  |  |         setState(() => inputModel.alt = !inputModel.alt); | 
					
						
							|  |  |  |  |       }, inputModel.alt), | 
					
						
							| 
									
										
										
										
											2020-11-25 16:28:46 +08:00
										 |  |  |  |       wrap('Shift', () { | 
					
						
							| 
									
										
										
										
											2022-09-27 20:35:02 +08:00
										 |  |  |  |         setState(() => inputModel.shift = !inputModel.shift); | 
					
						
							|  |  |  |  |       }, inputModel.shift), | 
					
						
							| 
									
										
										
										
											2021-08-17 15:07:01 +08:00
										 |  |  |  |       wrap(isMac ? ' Cmd ' : ' Win ', () { | 
					
						
							| 
									
										
										
										
											2022-09-27 20:35:02 +08:00
										 |  |  |  |         setState(() => inputModel.command = !inputModel.command); | 
					
						
							|  |  |  |  |       }, inputModel.command), | 
					
						
							| 
									
										
										
										
											2020-11-25 16:28:46 +08:00
										 |  |  |  |     ]; | 
					
						
							|  |  |  |  |     final keys = <Widget>[ | 
					
						
							|  |  |  |  |       wrap( | 
					
						
							| 
									
										
										
										
											2021-08-17 15:07:01 +08:00
										 |  |  |  |           ' Fn ', | 
					
						
							| 
									
										
										
										
											2022-08-02 13:10:09 +08:00
										 |  |  |  |           () => setState( | 
					
						
							| 
									
										
										
										
											2020-11-25 16:28:46 +08:00
										 |  |  |  |                 () { | 
					
						
							| 
									
										
										
										
											2022-08-02 13:10:09 +08:00
										 |  |  |  |                   _fn = !_fn; | 
					
						
							|  |  |  |  |                   if (_fn) { | 
					
						
							|  |  |  |  |                     _more = false; | 
					
						
							|  |  |  |  |                   } | 
					
						
							|  |  |  |  |                 }, | 
					
						
							|  |  |  |  |               ), | 
					
						
							| 
									
										
										
										
											2020-11-25 16:28:46 +08:00
										 |  |  |  |           _fn), | 
					
						
							|  |  |  |  |       wrap( | 
					
						
							| 
									
										
										
										
											2021-08-17 15:07:01 +08:00
										 |  |  |  |           ' ... ', | 
					
						
							| 
									
										
										
										
											2022-08-02 13:10:09 +08:00
										 |  |  |  |           () => setState( | 
					
						
							| 
									
										
										
										
											2020-11-25 16:28:46 +08:00
										 |  |  |  |                 () { | 
					
						
							| 
									
										
										
										
											2022-08-02 13:10:09 +08:00
										 |  |  |  |                   _more = !_more; | 
					
						
							|  |  |  |  |                   if (_more) { | 
					
						
							|  |  |  |  |                     _fn = false; | 
					
						
							|  |  |  |  |                   } | 
					
						
							|  |  |  |  |                 }, | 
					
						
							|  |  |  |  |               ), | 
					
						
							| 
									
										
										
										
											2020-11-25 16:28:46 +08:00
										 |  |  |  |           _more), | 
					
						
							|  |  |  |  |     ]; | 
					
						
							|  |  |  |  |     final fn = <Widget>[ | 
					
						
							|  |  |  |  |       SizedBox(width: 9999), | 
					
						
							|  |  |  |  |     ]; | 
					
						
							|  |  |  |  |     for (var i = 1; i <= 12; ++i) { | 
					
						
							| 
									
										
										
										
											2022-09-27 22:16:27 +08:00
										 |  |  |  |       final name = 'F$i'; | 
					
						
							| 
									
										
										
										
											2020-11-25 16:28:46 +08:00
										 |  |  |  |       fn.add(wrap(name, () { | 
					
						
							| 
									
										
										
										
											2022-09-27 22:16:27 +08:00
										 |  |  |  |         inputModel.inputKey('VK_$name'); | 
					
						
							| 
									
										
										
										
											2020-11-25 16:28:46 +08:00
										 |  |  |  |       })); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |     final more = <Widget>[ | 
					
						
							|  |  |  |  |       SizedBox(width: 9999), | 
					
						
							|  |  |  |  |       wrap('Esc', () { | 
					
						
							| 
									
										
										
										
											2022-09-27 20:35:02 +08:00
										 |  |  |  |         inputModel.inputKey('VK_ESCAPE'); | 
					
						
							| 
									
										
										
										
											2020-11-25 16:28:46 +08:00
										 |  |  |  |       }), | 
					
						
							|  |  |  |  |       wrap('Tab', () { | 
					
						
							| 
									
										
										
										
											2022-09-27 20:35:02 +08:00
										 |  |  |  |         inputModel.inputKey('VK_TAB'); | 
					
						
							| 
									
										
										
										
											2020-11-25 16:28:46 +08:00
										 |  |  |  |       }), | 
					
						
							|  |  |  |  |       wrap('Home', () { | 
					
						
							| 
									
										
										
										
											2022-09-27 20:35:02 +08:00
										 |  |  |  |         inputModel.inputKey('VK_HOME'); | 
					
						
							| 
									
										
										
										
											2020-11-25 16:28:46 +08:00
										 |  |  |  |       }), | 
					
						
							|  |  |  |  |       wrap('End', () { | 
					
						
							| 
									
										
										
										
											2022-09-27 20:35:02 +08:00
										 |  |  |  |         inputModel.inputKey('VK_END'); | 
					
						
							| 
									
										
										
										
											2020-11-25 16:28:46 +08:00
										 |  |  |  |       }), | 
					
						
							|  |  |  |  |       wrap('Del', () { | 
					
						
							| 
									
										
										
										
											2022-09-27 20:35:02 +08:00
										 |  |  |  |         inputModel.inputKey('VK_DELETE'); | 
					
						
							| 
									
										
										
										
											2020-11-25 16:28:46 +08:00
										 |  |  |  |       }), | 
					
						
							| 
									
										
										
										
											2020-11-27 02:14:27 +08:00
										 |  |  |  |       wrap('PgUp', () { | 
					
						
							| 
									
										
										
										
											2022-09-27 20:35:02 +08:00
										 |  |  |  |         inputModel.inputKey('VK_PRIOR'); | 
					
						
							| 
									
										
										
										
											2020-11-25 16:28:46 +08:00
										 |  |  |  |       }), | 
					
						
							| 
									
										
										
										
											2021-08-17 15:07:01 +08:00
										 |  |  |  |       wrap('PgDn', () { | 
					
						
							| 
									
										
										
										
											2022-09-27 20:35:02 +08:00
										 |  |  |  |         inputModel.inputKey('VK_NEXT'); | 
					
						
							| 
									
										
										
										
											2020-11-25 16:28:46 +08:00
										 |  |  |  |       }), | 
					
						
							| 
									
										
										
										
											2020-12-24 10:44:44 +08:00
										 |  |  |  |       SizedBox(width: 9999), | 
					
						
							|  |  |  |  |       wrap('', () { | 
					
						
							| 
									
										
										
										
											2022-09-27 20:35:02 +08:00
										 |  |  |  |         inputModel.inputKey('VK_LEFT'); | 
					
						
							| 
									
										
										
										
											2020-12-24 10:44:44 +08:00
										 |  |  |  |       }, false, Icons.keyboard_arrow_left), | 
					
						
							|  |  |  |  |       wrap('', () { | 
					
						
							| 
									
										
										
										
											2022-09-27 20:35:02 +08:00
										 |  |  |  |         inputModel.inputKey('VK_UP'); | 
					
						
							| 
									
										
										
										
											2020-12-24 10:44:44 +08:00
										 |  |  |  |       }, false, Icons.keyboard_arrow_up), | 
					
						
							|  |  |  |  |       wrap('', () { | 
					
						
							| 
									
										
										
										
											2022-09-27 20:35:02 +08:00
										 |  |  |  |         inputModel.inputKey('VK_DOWN'); | 
					
						
							| 
									
										
										
										
											2020-12-24 10:44:44 +08:00
										 |  |  |  |       }, false, Icons.keyboard_arrow_down), | 
					
						
							|  |  |  |  |       wrap('', () { | 
					
						
							| 
									
										
										
										
											2022-09-27 20:35:02 +08:00
										 |  |  |  |         inputModel.inputKey('VK_RIGHT'); | 
					
						
							| 
									
										
										
										
											2020-12-24 10:44:44 +08:00
										 |  |  |  |       }, false, Icons.keyboard_arrow_right), | 
					
						
							| 
									
										
										
										
											2021-08-11 00:22:47 +08:00
										 |  |  |  |       wrap(isMac ? 'Cmd+C' : 'Ctrl+C', () { | 
					
						
							|  |  |  |  |         sendPrompt(isMac, 'VK_C'); | 
					
						
							| 
									
										
										
										
											2020-12-24 10:44:44 +08:00
										 |  |  |  |       }), | 
					
						
							| 
									
										
										
										
											2021-08-11 00:22:47 +08:00
										 |  |  |  |       wrap(isMac ? 'Cmd+V' : 'Ctrl+V', () { | 
					
						
							|  |  |  |  |         sendPrompt(isMac, 'VK_V'); | 
					
						
							| 
									
										
										
										
											2021-08-06 22:45:45 +08:00
										 |  |  |  |       }), | 
					
						
							| 
									
										
										
										
											2021-08-11 00:22:47 +08:00
										 |  |  |  |       wrap(isMac ? 'Cmd+S' : 'Ctrl+S', () { | 
					
						
							|  |  |  |  |         sendPrompt(isMac, 'VK_S'); | 
					
						
							| 
									
										
										
										
											2020-12-24 10:44:44 +08:00
										 |  |  |  |       }), | 
					
						
							| 
									
										
										
										
											2020-11-25 16:28:46 +08:00
										 |  |  |  |     ]; | 
					
						
							| 
									
										
										
										
											2021-08-17 15:07:01 +08:00
										 |  |  |  |     final space = size.width > 320 ? 4.0 : 2.0; | 
					
						
							| 
									
										
										
										
											2020-11-25 16:28:46 +08:00
										 |  |  |  |     return Container( | 
					
						
							| 
									
										
										
										
											2020-11-27 02:14:27 +08:00
										 |  |  |  |         color: Color(0xAA000000), | 
					
						
							| 
									
										
										
										
											2020-11-25 16:28:46 +08:00
										 |  |  |  |         padding: EdgeInsets.only( | 
					
						
							| 
									
										
										
										
											2021-08-17 15:07:01 +08:00
										 |  |  |  |             top: keyboard ? 24 : 4, left: 0, right: 0, bottom: 8), | 
					
						
							| 
									
										
										
										
											2020-11-25 16:28:46 +08:00
										 |  |  |  |         child: Wrap( | 
					
						
							| 
									
										
										
										
											2021-08-17 15:07:01 +08:00
										 |  |  |  |           spacing: space, | 
					
						
							|  |  |  |  |           runSpacing: space, | 
					
						
							| 
									
										
										
										
											2020-11-25 16:28:46 +08:00
										 |  |  |  |           children: <Widget>[SizedBox(width: 9999)] + | 
					
						
							|  |  |  |  |               (keyboard | 
					
						
							| 
									
										
										
										
											2020-12-24 10:44:44 +08:00
										 |  |  |  |                   ? modifiers + keys + (_fn ? fn : []) + (_more ? more : []) | 
					
						
							| 
									
										
										
										
											2022-02-17 18:00:44 +08:00
										 |  |  |  |                   : modifiers), | 
					
						
							| 
									
										
										
										
											2020-11-25 16:28:46 +08:00
										 |  |  |  |         )); | 
					
						
							| 
									
										
										
										
											2020-11-25 13:03:48 +08:00
										 |  |  |  |   } | 
					
						
							| 
									
										
										
										
											2020-11-17 11:12:55 +08:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-18 23:15:59 +08:00
										 |  |  |  | class ImagePaint extends StatelessWidget { | 
					
						
							|  |  |  |  |   @override | 
					
						
							|  |  |  |  |   Widget build(BuildContext context) { | 
					
						
							|  |  |  |  |     final m = Provider.of<ImageModel>(context); | 
					
						
							| 
									
										
										
										
											2020-11-23 23:18:42 +08:00
										 |  |  |  |     final c = Provider.of<CanvasModel>(context); | 
					
						
							| 
									
										
										
										
											2022-06-13 21:07:26 +08:00
										 |  |  |  |     final adjust = gFFI.cursorModel.adjustForKeyboard(); | 
					
						
							| 
									
										
										
										
											2020-11-23 23:18:42 +08:00
										 |  |  |  |     var s = c.scale; | 
					
						
							| 
									
										
										
										
											2020-11-18 23:15:59 +08:00
										 |  |  |  |     return CustomPaint( | 
					
						
							| 
									
										
										
										
											2022-09-27 22:16:27 +08:00
										 |  |  |  |       painter: ImagePainter( | 
					
						
							| 
									
										
										
										
											2020-11-25 11:20:40 +08:00
										 |  |  |  |           image: m.image, x: c.x / s, y: (c.y - adjust) / s, scale: s), | 
					
						
							| 
									
										
										
										
											2020-11-19 18:22:06 +08:00
										 |  |  |  |     ); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | class CursorPaint extends StatelessWidget { | 
					
						
							|  |  |  |  |   @override | 
					
						
							|  |  |  |  |   Widget build(BuildContext context) { | 
					
						
							|  |  |  |  |     final m = Provider.of<CursorModel>(context); | 
					
						
							| 
									
										
										
										
											2020-11-23 23:18:42 +08:00
										 |  |  |  |     final c = Provider.of<CanvasModel>(context); | 
					
						
							| 
									
										
										
										
											2022-06-13 21:07:26 +08:00
										 |  |  |  |     final adjust = gFFI.cursorModel.adjustForKeyboard(); | 
					
						
							| 
									
										
										
										
											2020-11-23 23:18:42 +08:00
										 |  |  |  |     var s = c.scale; | 
					
						
							| 
									
										
										
										
											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
										 |  |  |  |       } | 
					
						
							|  |  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-11-19 18:22:06 +08:00
										 |  |  |  |     return CustomPaint( | 
					
						
							| 
									
										
										
										
											2022-09-27 22:16:27 +08:00
										 |  |  |  |       painter: ImagePainter( | 
					
						
							| 
									
										
										
										
											2022-11-22 21:34:53 +08:00
										 |  |  |  |           image: m.image ?? preDefaultCursor.image, | 
					
						
							| 
									
										
										
										
											2022-11-13 19:35:59 -08:00
										 |  |  |  |           x: m.x * s - hotx * s + c.x, | 
					
						
							|  |  |  |  |           y: m.y * s - hoty * s + c.y - adjust, | 
					
						
							| 
									
										
										
										
											2020-11-23 23:18:42 +08:00
										 |  |  |  |           scale: 1), | 
					
						
							| 
									
										
										
										
											2020-11-18 23:15:59 +08:00
										 |  |  |  |     ); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | class ImagePainter extends CustomPainter { | 
					
						
							|  |  |  |  |   ImagePainter({ | 
					
						
							| 
									
										
										
										
											2022-02-17 15:22:14 +08:00
										 |  |  |  |     required this.image, | 
					
						
							|  |  |  |  |     required this.x, | 
					
						
							|  |  |  |  |     required this.y, | 
					
						
							|  |  |  |  |     required this.scale, | 
					
						
							| 
									
										
										
										
											2020-11-17 11:12:55 +08:00
										 |  |  |  |   }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-17 15:22:14 +08:00
										 |  |  |  |   ui.Image? image; | 
					
						
							| 
									
										
										
										
											2020-11-19 18:22:06 +08:00
										 |  |  |  |   double x; | 
					
						
							|  |  |  |  |   double y; | 
					
						
							| 
									
										
										
										
											2020-11-23 23:18:42 +08:00
										 |  |  |  |   double scale; | 
					
						
							| 
									
										
										
										
											2020-11-17 11:12:55 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   @override | 
					
						
							|  |  |  |  |   void paint(Canvas canvas, Size size) { | 
					
						
							| 
									
										
										
										
											2020-11-17 12:08:31 +08:00
										 |  |  |  |     if (image == null) return; | 
					
						
							| 
									
										
										
										
											2020-11-23 23:18:42 +08:00
										 |  |  |  |     canvas.scale(scale, scale); | 
					
						
							| 
									
										
										
										
											2022-09-27 22:16:27 +08:00
										 |  |  |  |     canvas.drawImage(image!, Offset(x, y), Paint()); | 
					
						
							| 
									
										
										
										
											2020-11-17 11:12:55 +08:00
										 |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   @override | 
					
						
							|  |  |  |  |   bool shouldRepaint(CustomPainter oldDelegate) { | 
					
						
							| 
									
										
										
										
											2020-11-17 12:08:31 +08:00
										 |  |  |  |     return oldDelegate != this; | 
					
						
							| 
									
										
										
										
											2020-11-17 01:22:14 +08:00
										 |  |  |  |   } | 
					
						
							|  |  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-11-19 21:59:49 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-29 18:56:47 +08:00
										 |  |  |  | void showOptions( | 
					
						
							|  |  |  |  |     BuildContext context, String id, OverlayDialogManager dialogManager) async { | 
					
						
							| 
									
										
										
										
											2022-11-24 11:19:16 +08:00
										 |  |  |  |   String quality = | 
					
						
							|  |  |  |  |       await bind.sessionGetImageQuality(id: id) ?? kRemoteImageQualityBalanced; | 
					
						
							|  |  |  |  |   if (quality == '') quality = kRemoteImageQualityBalanced; | 
					
						
							| 
									
										
										
										
											2022-09-16 20:31:01 +08:00
										 |  |  |  |   String codec = | 
					
						
							|  |  |  |  |       await bind.sessionGetOption(id: id, arg: 'codec-preference') ?? 'auto'; | 
					
						
							|  |  |  |  |   if (codec == '') codec = 'auto'; | 
					
						
							| 
									
										
										
										
											2022-11-24 11:19:16 +08:00
										 |  |  |  |   String viewStyle = await bind.sessionGetViewStyle(id: id) ?? ''; | 
					
						
							| 
									
										
										
										
											2022-09-16 20:31:01 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-25 23:52:58 +08:00
										 |  |  |  |   var displays = <Widget>[]; | 
					
						
							| 
									
										
										
										
											2022-06-13 21:07:26 +08:00
										 |  |  |  |   final pi = gFFI.ffiModel.pi; | 
					
						
							|  |  |  |  |   final image = gFFI.ffiModel.getConnectionImage(); | 
					
						
							| 
									
										
										
										
											2022-09-16 20:31:01 +08:00
										 |  |  |  |   if (image != null) { | 
					
						
							| 
									
										
										
										
											2020-11-29 00:13:55 +08:00
										 |  |  |  |     displays.add(Padding(padding: const EdgeInsets.only(top: 8), child: image)); | 
					
						
							| 
									
										
										
										
											2022-09-16 20:31:01 +08:00
										 |  |  |  |   } | 
					
						
							| 
									
										
										
										
											2020-11-25 23:52:58 +08:00
										 |  |  |  |   if (pi.displays.length > 1) { | 
					
						
							|  |  |  |  |     final cur = pi.currentDisplay; | 
					
						
							|  |  |  |  |     final children = <Widget>[]; | 
					
						
							| 
									
										
										
										
											2022-09-16 20:31:01 +08:00
										 |  |  |  |     for (var i = 0; i < pi.displays.length; ++i) { | 
					
						
							| 
									
										
										
										
											2020-11-25 23:52:58 +08:00
										 |  |  |  |       children.add(InkWell( | 
					
						
							|  |  |  |  |           onTap: () { | 
					
						
							|  |  |  |  |             if (i == cur) return; | 
					
						
							| 
									
										
										
										
											2022-08-05 20:29:43 +08:00
										 |  |  |  |             bind.sessionSwitchDisplay(id: id, value: i); | 
					
						
							| 
									
										
										
										
											2022-08-12 18:42:02 +08:00
										 |  |  |  |             gFFI.dialogManager.dismissAll(); | 
					
						
							| 
									
										
										
										
											2020-11-25 23:52:58 +08:00
										 |  |  |  |           }, | 
					
						
							|  |  |  |  |           child: Ink( | 
					
						
							|  |  |  |  |               width: 40, | 
					
						
							|  |  |  |  |               height: 40, | 
					
						
							|  |  |  |  |               decoration: BoxDecoration( | 
					
						
							| 
									
										
										
										
											2022-09-29 18:56:47 +08:00
										 |  |  |  |                   border: Border.all(color: Theme.of(context).hintColor), | 
					
						
							|  |  |  |  |                   borderRadius: BorderRadius.circular(2), | 
					
						
							|  |  |  |  |                   color: i == cur | 
					
						
							|  |  |  |  |                       ? Theme.of(context).toggleableActiveColor.withOpacity(0.6) | 
					
						
							|  |  |  |  |                       : null), | 
					
						
							| 
									
										
										
										
											2020-11-25 23:52:58 +08:00
										 |  |  |  |               child: Center( | 
					
						
							|  |  |  |  |                   child: Text((i + 1).toString(), | 
					
						
							|  |  |  |  |                       style: TextStyle( | 
					
						
							| 
									
										
										
										
											2022-09-29 18:56:47 +08:00
										 |  |  |  |                           color: i == cur ? Colors.white : Colors.black87, | 
					
						
							|  |  |  |  |                           fontWeight: FontWeight.bold)))))); | 
					
						
							| 
									
										
										
										
											2022-09-16 20:31:01 +08:00
										 |  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-11-25 23:52:58 +08:00
										 |  |  |  |     displays.add(Padding( | 
					
						
							|  |  |  |  |         padding: const EdgeInsets.only(top: 8), | 
					
						
							|  |  |  |  |         child: Wrap( | 
					
						
							|  |  |  |  |           alignment: WrapAlignment.center, | 
					
						
							|  |  |  |  |           spacing: 8, | 
					
						
							|  |  |  |  |           children: children, | 
					
						
							|  |  |  |  |         ))); | 
					
						
							| 
									
										
										
										
											2020-11-29 00:13:55 +08:00
										 |  |  |  |   } | 
					
						
							|  |  |  |  |   if (displays.isNotEmpty) { | 
					
						
							| 
									
										
										
										
											2022-09-16 20:31:01 +08:00
										 |  |  |  |     displays.add(const Divider(color: MyTheme.border)); | 
					
						
							| 
									
										
										
										
											2020-11-25 23:52:58 +08:00
										 |  |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-06-13 21:07:26 +08:00
										 |  |  |  |   final perms = gFFI.ffiModel.permissions; | 
					
						
							| 
									
										
										
										
											2022-09-16 20:31:01 +08:00
										 |  |  |  |   final hasHwcodec = bind.mainHasHwcodec(); | 
					
						
							|  |  |  |  |   final List<bool> codecs = []; | 
					
						
							|  |  |  |  |   if (hasHwcodec) { | 
					
						
							|  |  |  |  |     try { | 
					
						
							|  |  |  |  |       final Map codecsJson = | 
					
						
							|  |  |  |  |           jsonDecode(await bind.sessionSupportedHwcodec(id: id)); | 
					
						
							|  |  |  |  |       final h264 = codecsJson['h264'] ?? false; | 
					
						
							|  |  |  |  |       final h265 = codecsJson['h265'] ?? false; | 
					
						
							|  |  |  |  |       codecs.add(h264); | 
					
						
							|  |  |  |  |       codecs.add(h265); | 
					
						
							| 
									
										
										
										
											2022-12-20 23:55:54 +09:00
										 |  |  |  |     } catch (e) { | 
					
						
							|  |  |  |  |       debugPrint("Show Codec Preference err=$e"); | 
					
						
							|  |  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-09-16 20:31:01 +08:00
										 |  |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-03-13 00:32:44 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-12 18:42:02 +08:00
										 |  |  |  |   dialogManager.show((setState, close) { | 
					
						
							| 
									
										
										
										
											2020-11-28 15:24:44 +08:00
										 |  |  |  |     final more = <Widget>[]; | 
					
						
							| 
									
										
										
										
											2022-02-02 00:46:21 +08:00
										 |  |  |  |     if (perms['audio'] != false) { | 
					
						
							| 
									
										
										
										
											2022-08-05 20:29:43 +08:00
										 |  |  |  |       more.add(getToggle(id, setState, 'disable-audio', 'Mute')); | 
					
						
							| 
									
										
										
										
											2020-11-28 13:34:59 +08:00
										 |  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-02-02 00:46:21 +08:00
										 |  |  |  |     if (perms['keyboard'] != false) { | 
					
						
							| 
									
										
										
										
											2022-09-16 20:31:01 +08:00
										 |  |  |  |       if (perms['clipboard'] != false) { | 
					
						
							| 
									
										
										
										
											2022-08-05 20:29:43 +08:00
										 |  |  |  |         more.add( | 
					
						
							|  |  |  |  |             getToggle(id, setState, 'disable-clipboard', 'Disable clipboard')); | 
					
						
							| 
									
										
										
										
											2022-09-16 20:31:01 +08:00
										 |  |  |  |       } | 
					
						
							| 
									
										
										
										
											2022-02-02 00:46:21 +08:00
										 |  |  |  |       more.add(getToggle( | 
					
						
							| 
									
										
										
										
											2022-08-05 20:29:43 +08:00
										 |  |  |  |           id, setState, 'lock-after-session-end', 'Lock after session end')); | 
					
						
							| 
									
										
										
										
											2022-02-02 00:46:21 +08:00
										 |  |  |  |       if (pi.platform == 'Windows') { | 
					
						
							| 
									
										
										
										
											2022-08-05 20:29:43 +08:00
										 |  |  |  |         more.add(getToggle(id, setState, 'privacy-mode', 'Privacy mode')); | 
					
						
							| 
									
										
										
										
											2022-02-02 00:46:21 +08:00
										 |  |  |  |       } | 
					
						
							| 
									
										
										
										
											2020-11-30 16:05:23 +08:00
										 |  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-09-16 20:31:01 +08:00
										 |  |  |  |     setQuality(String? value) { | 
					
						
							| 
									
										
										
										
											2022-02-17 18:00:44 +08:00
										 |  |  |  |       if (value == null) return; | 
					
						
							| 
									
										
										
										
											2022-02-02 00:46:21 +08:00
										 |  |  |  |       setState(() { | 
					
						
							|  |  |  |  |         quality = value; | 
					
						
							| 
									
										
										
										
											2022-08-05 20:29:43 +08:00
										 |  |  |  |         bind.sessionSetImageQuality(id: id, value: value); | 
					
						
							| 
									
										
										
										
											2022-02-02 00:46:21 +08:00
										 |  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2022-09-16 20:31:01 +08:00
										 |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     setViewStyle(String? value) { | 
					
						
							| 
									
										
										
										
											2022-02-17 18:00:44 +08:00
										 |  |  |  |       if (value == null) return; | 
					
						
							| 
									
										
										
										
											2022-02-02 00:46:21 +08:00
										 |  |  |  |       setState(() { | 
					
						
							|  |  |  |  |         viewStyle = value; | 
					
						
							| 
									
										
										
										
											2022-09-16 20:31:01 +08:00
										 |  |  |  |         bind | 
					
						
							| 
									
										
										
										
											2022-11-17 18:52:27 +08:00
										 |  |  |  |             .sessionSetViewStyle(id: id, value: value) | 
					
						
							| 
									
										
										
										
											2022-09-16 20:31:01 +08:00
										 |  |  |  |             .then((_) => gFFI.canvasModel.updateViewStyle()); | 
					
						
							| 
									
										
										
										
											2022-02-02 00:46:21 +08:00
										 |  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2022-09-16 20:31:01 +08:00
										 |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     setCodec(String? value) { | 
					
						
							|  |  |  |  |       if (value == null) return; | 
					
						
							|  |  |  |  |       setState(() { | 
					
						
							|  |  |  |  |         codec = value; | 
					
						
							|  |  |  |  |         bind | 
					
						
							|  |  |  |  |             .sessionPeerOption(id: id, name: "codec-preference", value: value) | 
					
						
							|  |  |  |  |             .then((_) => bind.sessionChangePreferCodec(id: id)); | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     final radios = [ | 
					
						
							| 
									
										
										
										
											2022-11-24 11:19:16 +08:00
										 |  |  |  |       getRadio( | 
					
						
							|  |  |  |  |           'Scale original', kRemoteViewStyleOriginal, viewStyle, setViewStyle), | 
					
						
							|  |  |  |  |       getRadio( | 
					
						
							|  |  |  |  |           'Scale adaptive', kRemoteViewStyleAdaptive, viewStyle, setViewStyle), | 
					
						
							| 
									
										
										
										
											2022-09-16 20:31:01 +08:00
										 |  |  |  |       const Divider(color: MyTheme.border), | 
					
						
							| 
									
										
										
										
											2022-11-24 11:19:16 +08:00
										 |  |  |  |       getRadio( | 
					
						
							|  |  |  |  |           'Good image quality', kRemoteImageQualityBest, quality, setQuality), | 
					
						
							|  |  |  |  |       getRadio('Balanced', kRemoteImageQualityBalanced, quality, setQuality), | 
					
						
							|  |  |  |  |       getRadio('Optimize reaction time', kRemoteImageQualityLow, quality, | 
					
						
							|  |  |  |  |           setQuality), | 
					
						
							| 
									
										
										
										
											2022-09-16 20:31:01 +08:00
										 |  |  |  |       const Divider(color: MyTheme.border) | 
					
						
							|  |  |  |  |     ]; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     if (hasHwcodec && codecs.length == 2 && (codecs[0] || codecs[1])) { | 
					
						
							|  |  |  |  |       radios.addAll([ | 
					
						
							|  |  |  |  |         getRadio(translate('Auto'), 'auto', codec, setCodec), | 
					
						
							|  |  |  |  |         getRadio('VP9', 'vp9', codec, setCodec), | 
					
						
							|  |  |  |  |       ]); | 
					
						
							|  |  |  |  |       if (codecs[0]) { | 
					
						
							|  |  |  |  |         radios.add(getRadio('H264', 'h264', codec, setCodec)); | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  |       if (codecs[1]) { | 
					
						
							|  |  |  |  |         radios.add(getRadio('H265', 'h265', codec, setCodec)); | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  |       radios.add(const Divider(color: MyTheme.border)); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     final toggles = [ | 
					
						
							|  |  |  |  |       getToggle(id, setState, 'show-quality-monitor', 'Show quality monitor'), | 
					
						
							|  |  |  |  |     ]; | 
					
						
							| 
									
										
										
										
											2022-12-31 21:41:16 +08:00
										 |  |  |  |     if (!gFFI.canvasModel.cursorEmbedded) { | 
					
						
							| 
									
										
										
										
											2022-11-29 16:36:35 +08:00
										 |  |  |  |       toggles.insert(0, | 
					
						
							|  |  |  |  |           getToggle(id, setState, 'show-remote-cursor', 'Show remote cursor')); | 
					
						
							|  |  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-09-16 20:31:01 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-13 00:32:44 +08:00
										 |  |  |  |     return CustomAlertDialog( | 
					
						
							| 
									
										
										
										
											2022-03-13 23:07:52 +08:00
										 |  |  |  |       content: Column( | 
					
						
							|  |  |  |  |           mainAxisSize: MainAxisSize.min, | 
					
						
							| 
									
										
										
										
											2022-09-16 20:31:01 +08:00
										 |  |  |  |           children: displays + radios + toggles + more), | 
					
						
							| 
									
										
										
										
											2022-03-13 23:07:52 +08:00
										 |  |  |  |       contentPadding: 0, | 
					
						
							| 
									
										
										
										
											2022-03-13 00:32:44 +08:00
										 |  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2022-04-21 10:02:47 +08:00
										 |  |  |  |   }, clickMaskDismiss: true, backDismiss: true); | 
					
						
							| 
									
										
										
										
											2022-08-04 17:24:02 +08:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-12 18:42:02 +08:00
										 |  |  |  | void showSetOSPassword( | 
					
						
							|  |  |  |  |     String id, bool login, OverlayDialogManager dialogManager) async { | 
					
						
							| 
									
										
										
										
											2020-12-21 19:05:31 +08:00
										 |  |  |  |   final controller = TextEditingController(); | 
					
						
							| 
									
										
										
										
											2022-08-16 15:22:57 +08:00
										 |  |  |  |   var password = await bind.sessionGetOption(id: id, arg: "os-password") ?? ""; | 
					
						
							|  |  |  |  |   var autoLogin = await bind.sessionGetOption(id: id, arg: "auto-login") != ""; | 
					
						
							| 
									
										
										
										
											2020-12-21 19:05:31 +08:00
										 |  |  |  |   controller.text = password; | 
					
						
							| 
									
										
										
										
											2022-08-12 18:42:02 +08:00
										 |  |  |  |   dialogManager.show((setState, close) { | 
					
						
							| 
									
										
										
										
											2022-03-13 00:32:44 +08:00
										 |  |  |  |     return CustomAlertDialog( | 
					
						
							|  |  |  |  |         title: Text(translate('OS Password')), | 
					
						
							|  |  |  |  |         content: Column(mainAxisSize: MainAxisSize.min, children: [ | 
					
						
							| 
									
										
										
										
											2022-02-28 16:11:21 +08:00
										 |  |  |  |           PasswordWidget(controller: controller), | 
					
						
							|  |  |  |  |           CheckboxListTile( | 
					
						
							|  |  |  |  |             contentPadding: const EdgeInsets.all(0), | 
					
						
							|  |  |  |  |             dense: true, | 
					
						
							|  |  |  |  |             controlAffinity: ListTileControlAffinity.leading, | 
					
						
							|  |  |  |  |             title: Text( | 
					
						
							|  |  |  |  |               translate('Auto Login'), | 
					
						
							|  |  |  |  |             ), | 
					
						
							|  |  |  |  |             value: autoLogin, | 
					
						
							|  |  |  |  |             onChanged: (v) { | 
					
						
							|  |  |  |  |               if (v == null) return; | 
					
						
							|  |  |  |  |               setState(() => autoLogin = v); | 
					
						
							|  |  |  |  |             }, | 
					
						
							|  |  |  |  |           ), | 
					
						
							|  |  |  |  |         ]), | 
					
						
							| 
									
										
										
										
											2022-03-13 00:32:44 +08:00
										 |  |  |  |         actions: [ | 
					
						
							| 
									
										
										
										
											2022-02-28 16:11:21 +08:00
										 |  |  |  |           TextButton( | 
					
						
							|  |  |  |  |             style: flatButtonStyle, | 
					
						
							|  |  |  |  |             onPressed: () { | 
					
						
							| 
									
										
										
										
											2022-03-13 23:07:52 +08:00
										 |  |  |  |               close(); | 
					
						
							| 
									
										
										
										
											2022-02-28 16:11:21 +08:00
										 |  |  |  |             }, | 
					
						
							|  |  |  |  |             child: Text(translate('Cancel')), | 
					
						
							|  |  |  |  |           ), | 
					
						
							|  |  |  |  |           TextButton( | 
					
						
							|  |  |  |  |             style: flatButtonStyle, | 
					
						
							|  |  |  |  |             onPressed: () { | 
					
						
							|  |  |  |  |               var text = controller.text.trim(); | 
					
						
							| 
									
										
										
										
											2022-08-05 20:29:43 +08:00
										 |  |  |  |               bind.sessionPeerOption(id: id, name: "os-password", value: text); | 
					
						
							|  |  |  |  |               bind.sessionPeerOption( | 
					
						
							|  |  |  |  |                   id: id, name: "auto-login", value: autoLogin ? 'Y' : ''); | 
					
						
							| 
									
										
										
										
											2022-02-28 16:11:21 +08:00
										 |  |  |  |               if (text != "" && login) { | 
					
						
							| 
									
										
										
										
											2022-08-05 20:29:43 +08:00
										 |  |  |  |                 bind.sessionInputOsPassword(id: id, value: text); | 
					
						
							| 
									
										
										
										
											2022-02-28 16:11:21 +08:00
										 |  |  |  |               } | 
					
						
							| 
									
										
										
										
											2022-03-13 23:07:52 +08:00
										 |  |  |  |               close(); | 
					
						
							| 
									
										
										
										
											2022-02-28 16:11:21 +08:00
										 |  |  |  |             }, | 
					
						
							|  |  |  |  |             child: Text(translate('OK')), | 
					
						
							|  |  |  |  |           ), | 
					
						
							| 
									
										
										
										
											2022-03-13 00:32:44 +08:00
										 |  |  |  |         ]); | 
					
						
							|  |  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2020-12-21 19:05:31 +08:00
										 |  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-08-11 00:22:47 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | void sendPrompt(bool isMac, String key) { | 
					
						
							| 
									
										
										
										
											2022-09-27 20:35:02 +08:00
										 |  |  |  |   final old = isMac ? gFFI.inputModel.command : gFFI.inputModel.ctrl; | 
					
						
							| 
									
										
										
										
											2021-08-11 00:22:47 +08:00
										 |  |  |  |   if (isMac) { | 
					
						
							| 
									
										
										
										
											2022-09-27 20:35:02 +08:00
										 |  |  |  |     gFFI.inputModel.command = true; | 
					
						
							| 
									
										
										
										
											2021-08-11 00:22:47 +08:00
										 |  |  |  |   } else { | 
					
						
							| 
									
										
										
										
											2022-09-27 20:35:02 +08:00
										 |  |  |  |     gFFI.inputModel.ctrl = true; | 
					
						
							| 
									
										
										
										
											2021-08-11 00:22:47 +08:00
										 |  |  |  |   } | 
					
						
							| 
									
										
										
										
											2022-09-27 20:35:02 +08:00
										 |  |  |  |   gFFI.inputModel.inputKey(key); | 
					
						
							| 
									
										
										
										
											2021-08-11 00:22:47 +08:00
										 |  |  |  |   if (isMac) { | 
					
						
							| 
									
										
										
										
											2022-09-27 20:35:02 +08:00
										 |  |  |  |     gFFI.inputModel.command = old; | 
					
						
							| 
									
										
										
										
											2021-08-11 00:22:47 +08:00
										 |  |  |  |   } else { | 
					
						
							| 
									
										
										
										
											2022-09-27 20:35:02 +08:00
										 |  |  |  |     gFFI.inputModel.ctrl = old; | 
					
						
							| 
									
										
										
										
											2021-08-11 00:22:47 +08:00
										 |  |  |  |   } | 
					
						
							|  |  |  |  | } |