remove drag/scroll/right btn, update gesture

This commit is contained in:
csf 2022-02-17 18:00:44 +08:00
parent b4ed72435c
commit 6840052033
3 changed files with 219 additions and 189 deletions

View File

@ -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>(
() => DoubleTapGestureRecognizer(), (instance) {
instance.onDoubleTap = onDoubleTap;
instance
..onDoubleTapDown = onDoubleTapDown
..onDoubleTap = onDoubleTap;
}),
LongPressGestureRecognizer:
GestureRecognizerFactoryWithHandlers<LongPressGestureRecognizer>(
() => 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;
})
});
}

View File

@ -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<String, dynamic> evt,
String id,
)
handleMsgbox) {
Map<String, dynamic> 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<String, dynamic> 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<dynamic>)
.map((s) =>
Peer.fromJson(s[0] as String, s[1] as Map<String, dynamic>))
Peer.fromJson(s[0] as String, s[1] as Map<String, dynamic>))
.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', '');

View File

@ -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<RemotePage> {
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<RemotePage> {
}
void resetTool() {
_scroll = _drag = _right = false;
FFI.resetModifiers();
}
@ -227,9 +220,6 @@ class _RemotePageState extends State<RemotePage> {
}
void resetMouse() {
_drag = false;
_scroll = false;
_right = false;
_mouseTools = false;
}
@ -322,7 +312,6 @@ class _RemotePageState extends State<RemotePage> {
setState(() {
_mouseTools = !_mouseTools;
resetTool();
if (_mouseTools) _drag = true;
});
},
))
@ -348,112 +337,97 @@ class _RemotePageState extends State<RemotePage> {
);
}
/// 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<RemotePage> {
style: TextStyle(color: Colors.white, fontSize: 11)),
onPressed: onPressed);
};
final mouse = <Widget>[
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 = <Widget>[
@ -772,7 +717,7 @@ class _RemotePageState extends State<RemotePage> {
children: <Widget>[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);
},
),