From 6840052033c3e3a3ee1817c10f7bbf492116e1ed Mon Sep 17 00:00:00 2001 From: csf Date: Thu, 17 Feb 2022 18:00:44 +0800 Subject: [PATCH] remove drag/scroll/right btn, update gesture --- lib/gestures.dart | 71 ++++++++++++---- lib/model.dart | 146 +++++++++++++++++++++------------ lib/remote_page.dart | 191 +++++++++++++++---------------------------- 3 files changed, 219 insertions(+), 189 deletions(-) diff --git a/lib/gestures.dart b/lib/gestures.dart index c93042b7c..cb15813c9 100644 --- a/lib/gestures.dart +++ b/lib/gestures.dart @@ -7,7 +7,8 @@ enum CustomTouchGestureState { oneFingerPan, twoFingerScale, twoFingerVerticalDrag, - twoFingerHorizontalDrag + twoFingerHorizontalDrag, + twoFingerPan } const kScaleSlop = kPrecisePointerPanSlop / 10; @@ -43,11 +44,20 @@ class CustomTouchGestureRecognizer extends ScaleGestureRecognizer { GestureDragUpdateCallback? onTwoFingerHorizontalDragUpdate; GestureDragEndCallback? onTwoFingerHorizontalDragEnd; + // twoFingerPan + GestureDragStartCallback? onTwoFingerPanStart; + GestureDragUpdateCallback? onTwoFingerPanUpdate; + GestureDragEndCallback? onTwoFingerPanEnd; + void _init() { debugPrint("CustomTouchGestureRecognizer init"); onStart = (d) { if (d.pointerCount == 1) { _currentState = CustomTouchGestureState.oneFingerPan; + if (onOneFingerPanStart != null) { + onOneFingerPanStart!(DragStartDetails( + localPosition: d.localFocalPoint, globalPosition: d.focalPoint)); + } debugPrint("start pan"); } else if (d.pointerCount == 2) { _currentState = CustomTouchGestureState.none; @@ -84,6 +94,11 @@ class CustomTouchGestureRecognizer extends ScaleGestureRecognizer { onTwoFingerVerticalDragUpdate!(_getDragUpdateDetails(d)); } break; + case CustomTouchGestureState.twoFingerPan: + if (onTwoFingerPanUpdate != null) { + onTwoFingerPanUpdate!(_getDragUpdateDetails(d)); + } + break; default: break; } @@ -118,6 +133,12 @@ class CustomTouchGestureRecognizer extends ScaleGestureRecognizer { onTwoFingerVerticalDragEnd!(_getDragEndDetails(d)); } break; + case CustomTouchGestureState.twoFingerPan: + debugPrint("TwoFingerState.twoFingerPan onEnd"); + if (onTwoFingerPanEnd != null) { + onTwoFingerPanEnd!(_getDragEndDetails(d)); + } + break; default: break; } @@ -154,24 +175,27 @@ class CustomTouchGestureRecognizer extends ScaleGestureRecognizer { if (_sumScale.abs() > kScaleSlop) { debugPrint("start Scale"); _currentState = CustomTouchGestureState.twoFingerScale; - if (onOneFingerPanStart != null) { - onOneFingerPanStart!(_getDragStartDetails(d)); + if (onTwoFingerScaleStart != null) { + onTwoFingerScaleStart!(ScaleStartDetails( + localFocalPoint: d.localFocalPoint, focalPoint: d.focalPoint)); } _reset(); - } else if (_sumHorizontal.abs() > kPrecisePointerPanSlop) { - debugPrint("start Horizontal"); - _currentState = CustomTouchGestureState.twoFingerHorizontalDrag; - if (onTwoFingerHorizontalDragUpdate != null) { - onTwoFingerHorizontalDragUpdate!(_getDragUpdateDetails(d)); - } - _reset(); - } else if (_sumVertical.abs() > kPrecisePointerPanSlop) { + } else if (_sumVertical.abs() > kPrecisePointerPanSlop && + _sumHorizontal.abs() < kPrecisePointerHitSlop) { debugPrint("start Vertical"); if (onTwoFingerVerticalDragStart != null) { _getDragStartDetails(d); } _currentState = CustomTouchGestureState.twoFingerVerticalDrag; _reset(); + } else if ((_sumHorizontal.abs() + _sumVertical.abs()) > + kPrecisePointerPanSlop) { + debugPrint("start TwoFingerPan"); + _currentState = CustomTouchGestureState.twoFingerPan; + if (onTwoFingerPanStart != null) { + onTwoFingerPanStart!(_getDragStartDetails(d)); + } + _reset(); } } @@ -672,7 +696,8 @@ class _TapTracker { void startTrackingPointer(PointerRoute route, Matrix4? transform) { if (!_isTrackingPointer) { _isTrackingPointer = true; - GestureBinding.instance!.pointerRouter.addRoute(pointer, route, transform); + GestureBinding.instance!.pointerRouter + .addRoute(pointer, route, transform); } } @@ -716,17 +741,23 @@ class _CountdownZoned { RawGestureDetector getMixinGestureDetector({ Widget? child, GestureTapUpCallback? onTapUp, + GestureTapDownCallback? onDoubleTapDown, GestureDoubleTapCallback? onDoubleTap, + GestureLongPressDownCallback? onLongPressDown, + GestureLongPressCallback? onLongPress, GestureDragStartCallback? onHoldDragStart, GestureDragUpdateCallback? onHoldDragUpdate, GestureDragCancelCallback? onHoldDragCancel, GestureTapDownCallback? onDoubleFinerTap, GestureDragStartCallback? onOneFingerPanStart, GestureDragUpdateCallback? onOneFingerPanUpdate, + GestureDragEndCallback? onOneFingerPanEnd, GestureScaleUpdateCallback? onTwoFingerScaleUpdate, GestureScaleEndCallback? onTwoFingerScaleEnd, GestureDragUpdateCallback? onTwoFingerHorizontalDragUpdate, GestureDragUpdateCallback? onTwoFingerVerticalDragUpdate, + GestureDragStartCallback? onTwoFingerPanStart, + GestureDragUpdateCallback? onTwoFingerPanUpdate, }) { return RawGestureDetector( child: child, @@ -740,7 +771,16 @@ RawGestureDetector getMixinGestureDetector({ DoubleTapGestureRecognizer: GestureRecognizerFactoryWithHandlers( () => DoubleTapGestureRecognizer(), (instance) { - instance.onDoubleTap = onDoubleTap; + instance + ..onDoubleTapDown = onDoubleTapDown + ..onDoubleTap = onDoubleTap; + }), + LongPressGestureRecognizer: + GestureRecognizerFactoryWithHandlers( + () => LongPressGestureRecognizer(), (instance) { + instance + ..onLongPressDown = onLongPressDown + ..onLongPress = onLongPress; }), // Customized HoldTapMoveGestureRecognizer: @@ -763,10 +803,13 @@ RawGestureDetector getMixinGestureDetector({ instance ..onOneFingerPanStart = onOneFingerPanStart ..onOneFingerPanUpdate = onOneFingerPanUpdate + ..onOneFingerPanEnd = onOneFingerPanEnd ..onTwoFingerScaleUpdate = onTwoFingerScaleUpdate ..onTwoFingerScaleEnd = onTwoFingerScaleEnd ..onTwoFingerHorizontalDragUpdate = onTwoFingerHorizontalDragUpdate - ..onTwoFingerVerticalDragUpdate = onTwoFingerVerticalDragUpdate; + ..onTwoFingerVerticalDragUpdate = onTwoFingerVerticalDragUpdate + ..onTwoFingerPanStart = onTwoFingerPanStart + ..onTwoFingerPanUpdate = onTwoFingerPanUpdate; }) }); } diff --git a/lib/model.dart b/lib/model.dart index a341f0b54..30a6f21c5 100644 --- a/lib/model.dart +++ b/lib/model.dart @@ -42,7 +42,7 @@ class FfiModel with ChangeNotifier { FfiModel() { Translator.call = translate; clear(); - () async { + () async { await PlatformFFI.init(); _initialized = true; print("FFI initialized"); @@ -97,14 +97,13 @@ class FfiModel with ChangeNotifier { _permissions.clear(); } - void update( - String id, + void update(String id, BuildContext context, void Function( - Map evt, - String id, - ) - handleMsgbox) { + Map evt, + String id, + ) + handleMsgbox) { var pos; for (;;) { var evt = FFI.popEvent(); @@ -143,17 +142,17 @@ class FfiModel with ChangeNotifier { final pid = FFI.id; ui.decodeImageFromPixels( rgba, _display.width, _display.height, ui.PixelFormat.bgra8888, - (image) { - PlatformFFI.clearRgbaFrame(); - _decoding = false; - if (FFI.id != pid) return; - try { - // my throw exception, because the listener maybe already dispose - FFI.imageModel.update(image); - } catch (e) { - print('update image: $e'); - } - }); + (image) { + PlatformFFI.clearRgbaFrame(); + _decoding = false; + if (FFI.id != pid) return; + try { + // my throw exception, because the listener maybe already dispose + FFI.imageModel.update(image); + } catch (e) { + print('update image: $e'); + } + }); } } } @@ -210,7 +209,9 @@ class ImageModel with ChangeNotifier { if (isDesktop) { FFI.canvasModel.updateViewStyle(); } else { - final size = MediaQueryData.fromWindow(ui.window).size; + final size = MediaQueryData + .fromWindow(ui.window) + .size; final xscale = size.width / image.width; final yscale = size.height / image.height; FFI.canvasModel.scale = max(xscale, yscale); @@ -223,7 +224,9 @@ class ImageModel with ChangeNotifier { double get maxScale { if (_image == null) return 1.0; - final size = MediaQueryData.fromWindow(ui.window).size; + final size = MediaQueryData + .fromWindow(ui.window) + .size; final xscale = size.width / _image!.width; final yscale = size.height / _image!.height; return max(1.0, max(xscale, yscale)); @@ -231,7 +234,9 @@ class ImageModel with ChangeNotifier { double get minScale { if (_image == null) return 1.0; - final size = MediaQueryData.fromWindow(ui.window).size; + final size = MediaQueryData + .fromWindow(ui.window) + .size; final xscale = size.width / _image!.width; final yscale = size.height / _image!.height; return min(xscale, yscale); @@ -253,7 +258,9 @@ class CanvasModel with ChangeNotifier { void updateViewStyle() { final s = FFI.getByName('peer_option', 'view-style'); - final size = MediaQueryData.fromWindow(ui.window).size; + final size = MediaQueryData + .fromWindow(ui.window) + .size; final s1 = size.width / FFI.ffiModel.display.width; final s2 = size.height / FFI.ffiModel.display.height; if (s == 'shrink') { @@ -282,7 +289,9 @@ class CanvasModel with ChangeNotifier { } void moveDesktopMouse(double x, double y) { - final size = MediaQueryData.fromWindow(ui.window).size; + final size = MediaQueryData + .fromWindow(ui.window) + .size; final dw = FFI.ffiModel.display.width * _scale; final dh = FFI.ffiModel.display.height * _scale; var dxOffset = 0; @@ -298,7 +307,7 @@ class CanvasModel with ChangeNotifier { if (dxOffset != 0 || dyOffset != 0) { notifyListeners(); } - FFI.cursorModel.move(x, y); + FFI.cursorModel.moveLocal(x, y); } set scale(v) { @@ -377,7 +386,9 @@ class CursorModel with ChangeNotifier { // remote physical display coordinate Rect getVisibleRect() { - final size = MediaQueryData.fromWindow(ui.window).size; + final size = MediaQueryData + .fromWindow(ui.window) + .size; final xoffset = FFI.canvasModel.x; final yoffset = FFI.canvasModel.y; final scale = FFI.canvasModel.scale; @@ -397,13 +408,18 @@ class CursorModel with ChangeNotifier { return h - thresh; } - void touch(double x, double y, bool right) { - move(x, y); + void touch(double x, double y, MouseButtons button) { + moveLocal(x, y); FFI.moveMouse(_x, _y); - FFI.tap(right); + FFI.tap(button); } - void move(double x, double y) { + void move(double x, double y){ + moveLocal(x, y); + FFI.moveMouse(_x, _y); + } + + void moveLocal(double x, double y) { final scale = FFI.canvasModel.scale; final xoffset = FFI.canvasModel.x; final yoffset = FFI.canvasModel.y; @@ -420,10 +436,10 @@ class CursorModel with ChangeNotifier { notifyListeners(); } - void updatePan(double dx, double dy, bool touchMode, bool drag) { + void updatePan(double dx, double dy,bool touchMode) { if (FFI.imageModel.image == null) return; if (touchMode) { - if (drag) { + if (true) { final scale = FFI.canvasModel.scale; _x += dx / scale; _y += dy / scale; @@ -508,17 +524,17 @@ class CursorModel with ChangeNotifier { final rgba = Uint8List.fromList(colors.map((s) => s as int).toList()); var pid = FFI.id; ui.decodeImageFromPixels(rgba, width, height, ui.PixelFormat.rgba8888, - (image) { - if (FFI.id != pid) return; - _image = image; - _images[id] = Tuple3(image, _hotx, _hoty); - try { - // my throw exception, because the listener maybe already dispose - notifyListeners(); - } catch (e) { - print('notify cursor: $e'); - } - }); + (image) { + if (FFI.id != pid) return; + _image = image; + _images[id] = Tuple3(image, _hotx, _hoty); + try { + // my throw exception, because the listener maybe already dispose + notifyListeners(); + } catch (e) { + print('notify cursor: $e'); + } + }); } void updateCursorId(Map evt) { @@ -547,8 +563,8 @@ class CursorModel with ChangeNotifier { notifyListeners(); } - void updateDisplayOriginWithCursor( - double x, double y, double xCursor, double yCursor) { + void updateDisplayOriginWithCursor(double x, double y, double xCursor, + double yCursor) { _displayOriginX = x; _displayOriginY = y; _x = xCursor; @@ -655,6 +671,26 @@ class ServerModel with ChangeNotifier { } } +enum MouseButtons { + left, + right, + wheel +} + +extension ToString on MouseButtons{ + String get value { + switch (this) { + case MouseButtons.left: + return "left"; + case MouseButtons.right: + return "right"; + case MouseButtons.wheel: + return "wheel"; + } + } +} + + class FFI { static String id = ""; static var shift = false; @@ -671,9 +707,9 @@ class FFI { return getByName('remote_id'); } - static void tap(bool right) { - sendMouse('down', right ? 'right' : 'left'); - sendMouse('up', right ? 'right' : 'left'); + static void tap(MouseButtons button) { + sendMouse('down', button); + sendMouse('up', button); } static void scroll(double y) { @@ -700,10 +736,10 @@ class FFI { return evt; } - static void sendMouse(String type, String buttons) { + static void sendMouse(String type, MouseButtons button) { if (!ffiModel.keyboard()) return; setByName( - 'send_mouse', json.encode(modify({'type': type, 'buttons': buttons}))); + 'send_mouse', json.encode(modify({'type': type, 'buttons': button.value}))); } static void inputKey(String name) { @@ -725,7 +761,7 @@ class FFI { return peers .map((s) => s as List) .map((s) => - Peer.fromJson(s[0] as String, s[1] as Map)) + Peer.fromJson(s[0] as String, s[1] as Map)) .toList(); } catch (e) { print('peers(): $e'); @@ -761,8 +797,14 @@ class FFI { static void close() { if (FFI.imageModel.image != null && !isDesktop) { - savePreference(id, cursorModel.x, cursorModel.y, canvasModel.x, - canvasModel.y, canvasModel.scale, ffiModel.pi.currentDisplay); + savePreference( + id, + cursorModel.x, + cursorModel.y, + canvasModel.x, + canvasModel.y, + canvasModel.scale, + ffiModel.pi.currentDisplay); } id = ""; setByName('close', ''); diff --git a/lib/remote_page.dart b/lib/remote_page.dart index 1ed6b47b8..44961be23 100644 --- a/lib/remote_page.dart +++ b/lib/remote_page.dart @@ -1,4 +1,3 @@ -import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:flutter/services.dart'; @@ -28,14 +27,9 @@ class _RemotePageState extends State { bool _showBar = !isDesktop; double _bottom = 0; String _value = ''; - double _xOffset = 0; - double _yOffset = 0; - double _yOffset0 = 0; double _scale = 1; bool _mouseTools = false; - var _drag = false; - var _right = false; - var _scroll = false; + var _more = true; var _fn = false; final FocusNode _focusNode = FocusNode(); @@ -72,7 +66,6 @@ class _RemotePageState extends State { } void resetTool() { - _scroll = _drag = _right = false; FFI.resetModifiers(); } @@ -227,9 +220,6 @@ class _RemotePageState extends State { } void resetMouse() { - _drag = false; - _scroll = false; - _right = false; _mouseTools = false; } @@ -322,7 +312,6 @@ class _RemotePageState extends State { setState(() { _mouseTools = !_mouseTools; resetTool(); - if (_mouseTools) _drag = true; }); }, )) @@ -348,112 +337,97 @@ class _RemotePageState extends State { ); } + /// 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 + Widget getBodyForMobileWithGesture() { return getMixinGestureDetector( child: getBodyForMobile(), onTapUp: (d) { - if (_drag || _scroll) return; if (_touchMode) { - FFI.cursorModel - .touch(d.localPosition.dx, d.localPosition.dy, _right); + FFI.cursorModel.touch( + d.localPosition.dx, d.localPosition.dy, MouseButtons.left); } else { - FFI.tap(_right); + FFI.tap(MouseButtons.left); + } + }, + onDoubleTapDown: (d){ + if(_touchMode){ + FFI.cursorModel.move(d.localPosition.dx, d.localPosition.dy); } }, onDoubleTap: () { - if (_drag || _scroll) return; - FFI.tap(_right); - FFI.tap(_right); + FFI.tap(MouseButtons.left); + FFI.tap(MouseButtons.left); + }, + onLongPressDown: (d){ + if (_touchMode){ + FFI.cursorModel.move(d.localPosition.dx, d.localPosition.dy); + } + }, + onLongPress: () { + if (_touchMode) { + FFI.tap(MouseButtons.right); + } }, onDoubleFinerTap: (d) { - if (_drag || _scroll) return; - FFI.tap(true); + if (!_touchMode) { + FFI.tap(MouseButtons.right); + } }, onHoldDragStart: (d) { - FFI.sendMouse('down', 'left'); + if (!_touchMode) { + FFI.sendMouse('down', MouseButtons.left); + } }, onHoldDragUpdate: (d) { - FFI.cursorModel.updatePan(d.delta.dx, d.delta.dy, _touchMode, _drag); + if (!_touchMode) { + FFI.cursorModel.updatePan(d.delta.dx, d.delta.dy, _touchMode); + } }, onHoldDragCancel: () { - FFI.sendMouse('up', 'left'); + if (!_touchMode) { + FFI.sendMouse('up', MouseButtons.left); + } }, onOneFingerPanStart: (d) { - FFI.sendMouse('up', 'left'); + if (_touchMode) { + debugPrint("_touchMode , onOneFingerPanStart"); + FFI.cursorModel.move(d.localPosition.dx, d.localPosition.dy); + FFI.sendMouse('down', MouseButtons.left); + } else { + FFI.sendMouse('up', MouseButtons.left); + } }, onOneFingerPanUpdate: (d) { - FFI.cursorModel.updatePan(d.delta.dx, d.delta.dy, _touchMode, _drag); + FFI.cursorModel.updatePan(d.delta.dx, d.delta.dy, _touchMode); + }, + onOneFingerPanEnd: (d) { + if (_touchMode) { + FFI.sendMouse('up', MouseButtons.left); + } }, onTwoFingerScaleUpdate: (d) { - FFI.canvasModel.updateScale(d.scale/_scale); + FFI.canvasModel.updateScale(d.scale / _scale); _scale = d.scale; + FFI.canvasModel.panX(d.focalPointDelta.dx); + FFI.canvasModel.panY(d.focalPointDelta.dy); }, - onTwoFingerScaleEnd: (d)=>_scale = 1, + onTwoFingerScaleEnd: (d) => _scale = 1, onTwoFingerVerticalDragUpdate: (d) { FFI.scroll(d.delta.dy / 2); + }, + onTwoFingerPanUpdate: (d) { + FFI.canvasModel.panX(d.delta.dx); + FFI.canvasModel.panY(d.delta.dy); }); - return GestureDetector( - onLongPress: () { - if (_drag || _scroll) return; - // make right click and real left long click both work - // should add "long press = right click" option? - FFI.sendMouse('down', 'left'); - FFI.tap(true); - }, - onLongPressUp: () { - FFI.sendMouse('up', 'left'); - }, - onTapUp: (details) { - if (_drag || _scroll) return; - if (_touchMode) { - FFI.cursorModel.touch( - details.localPosition.dx, details.localPosition.dy, _right); - } else { - FFI.tap(_right); - } - }, - onScaleStart: (details) { - _scale = 1; - _xOffset = details.focalPoint.dx; - _yOffset = _yOffset0 = details.focalPoint.dy; - if (_drag) { - FFI.sendMouse('down', 'left'); - } - }, - onScaleUpdate: (details) { - var scale = details.scale; - if (scale == 1) { - if (!_scroll) { - var x = details.focalPoint.dx; - var y = details.focalPoint.dy; - var dx = x - _xOffset; - var dy = y - _yOffset; - FFI.cursorModel.updatePan(dx, dy, _touchMode, _drag); - _xOffset = x; - _yOffset = y; - } else { - _xOffset = details.focalPoint.dx; - _yOffset = details.focalPoint.dy; - } - } else if (!_drag && !_scroll) { - FFI.canvasModel.updateScale(scale / _scale); - _scale = scale; - } - }, - onScaleEnd: (details) { - if (_drag) { - FFI.sendMouse('up', 'left'); - setState(resetMouse); - } else if (_scroll) { - var dy = (_yOffset - _yOffset0) / 10; - if (dy.abs() > 0.1) { - if (dy > 0 && dy < 1) dy = 1; - if (dy < 0 && dy > -1) dy = -1; - FFI.scroll(dy); - } - } - }, - child: getBodyForMobile()); } Widget getBodyForMobile() { @@ -637,35 +611,6 @@ class _RemotePageState extends State { style: TextStyle(color: Colors.white, fontSize: 11)), onPressed: onPressed); }; - final mouse = [ - wrap('Drag', () { - setState(() { - _drag = !_drag; - if (_drag) { - _scroll = false; - _right = false; - } - }); - }, _drag), - wrap('Scroll', () { - setState(() { - _scroll = !_scroll; - if (_scroll) { - _drag = false; - _right = false; - } - }); - }, _scroll), - wrap('Right', () { - setState(() { - _right = !_right; - if (_right) { - _scroll = false; - _drag = false; - } - }); - }, _right) - ]; final pi = FFI.ffiModel.pi; final isMac = pi.platform == "Mac OS"; final modifiers = [ @@ -772,7 +717,7 @@ class _RemotePageState extends State { children: [SizedBox(width: 9999)] + (keyboard ? modifiers + keys + (_fn ? fn : []) + (_more ? more : []) - : mouse + modifiers), + : modifiers), )); } } @@ -987,14 +932,14 @@ void showOptions(BuildContext context) { } } var setQuality = (String? value) { - if(value == null) return; + if (value == null) return; setState(() { quality = value; FFI.setByName('image_quality', value); }); }; var setViewStyle = (String? value) { - if(value == null) return; + if (value == null) return; setState(() { viewStyle = value; FFI.setByName( @@ -1050,7 +995,7 @@ void showSetOSPassword(BuildContext context, bool login) { ), value: autoLogin, onChanged: (v) { - if(v==null) return; + if (v == null) return; setState(() => autoLogin = v); }, ),