diff --git a/flutter_hbb/lib/main.dart b/flutter_hbb/lib/main.dart index 99767935a..73666ef43 100644 --- a/flutter_hbb/lib/main.dart +++ b/flutter_hbb/lib/main.dart @@ -16,13 +16,15 @@ class App extends StatelessWidget { value: FFI.imageModel, child: ChangeNotifierProvider.value( value: FFI.cursorModel, - child: MaterialApp( - title: 'RustDesk', - theme: ThemeData( - primarySwatch: Colors.blue, - visualDensity: VisualDensity.adaptivePlatformDensity, - ), - home: HomePage(title: 'RustDesk'), - )))); + child: ChangeNotifierProvider.value( + value: FFI.canvasModel, + child: MaterialApp( + title: 'RustDesk', + theme: ThemeData( + primarySwatch: Colors.blue, + visualDensity: VisualDensity.adaptivePlatformDensity, + ), + home: HomePage(title: 'RustDesk'), + ))))); } } diff --git a/flutter_hbb/lib/model.dart b/flutter_hbb/lib/model.dart index 374f366e8..3a3812c41 100644 --- a/flutter_hbb/lib/model.dart +++ b/flutter_hbb/lib/model.dart @@ -6,6 +6,7 @@ import 'dart:convert'; import 'dart:typed_data'; import 'dart:ui' as ui; import 'package:flutter/material.dart'; +import 'package:tuple/tuple.dart'; import 'dart:async'; import 'common.dart'; @@ -47,6 +48,8 @@ class FfiModel with ChangeNotifier { print('$_permissions'); } + bool keyboard() => _permissions['keyboard'] == true; + void clear() { _pi = PeerInfo(); _display = Display(); @@ -151,9 +154,50 @@ class ImageModel with ChangeNotifier { } } +class CanvasModel with ChangeNotifier { + double _x; + double _y; + double _scale; + double _xPan; + double _yPan; + + CanvasModel() { + clear(); + } + + double get x => _x; + double get y => _y; + double get scale => _scale; + + void startPan() { + _xPan = 0; + _yPan = 0; + } + + void updateOffset(double dx, double dy) { + _x += dx; + _y += dy; + notifyListeners(); + } + + void updateScale(double v) { + _scale *= v; + if (_scale > 1) _scale = 1; + notifyListeners(); + } + + void clear() { + _x = 0; + _y = 0; + _scale = 1.0; + _xPan = 0; + _yPan = 0; + } +} + class CursorModel with ChangeNotifier { ui.Image _image; - final _images = Map(); + final _images = Map>(); double _x = -10000; double _y = -10000; double _hotx = 0; @@ -162,8 +206,10 @@ class CursorModel with ChangeNotifier { double _displayOriginY = 0; ui.Image get image => _image; - double get x => _x - _displayOriginX - _hotx; - double get y => _y - _displayOriginY - _hoty; + double get x => _x - _displayOriginX; + double get y => _y - _displayOriginY; + double get hotx => _hotx; + double get hoty => _hoty; void updateCursorData(Map evt) { var id = int.parse(evt['id']); @@ -176,7 +222,7 @@ class CursorModel with ChangeNotifier { ui.decodeImageFromPixels(rgba, width, height, ui.PixelFormat.rgba8888, (image) { _image = image; - _images[id] = image; + _images[id] = Tuple3(image, _hotx, _hoty); try { // my throw exception, because the listener maybe already dispose notifyListeners(); @@ -187,7 +233,9 @@ class CursorModel with ChangeNotifier { void updateCursorId(Map evt) { final tmp = _images[int.parse(evt['id'])]; if (tmp != null) { - _image = tmp; + _image = tmp.item1; + _hotx = tmp.item2; + _hoty = tmp.item3; notifyListeners(); } } @@ -222,6 +270,7 @@ class FFI { static final imageModel = ImageModel(); static final ffiModel = FfiModel(); static final cursorModel = CursorModel(); + static final canvasModel = CanvasModel(); static String getId() { return getByName('remote_id'); @@ -283,6 +332,7 @@ class FFI { FFI.imageModel.update(null); FFI.cursorModel.clear(); FFI.ffiModel.clear(); + FFI.canvasModel.clear(); } static void setByName(String name, [String value = '']) { diff --git a/flutter_hbb/lib/remote_page.dart b/flutter_hbb/lib/remote_page.dart index 5d2d49df4..d8857ba42 100644 --- a/flutter_hbb/lib/remote_page.dart +++ b/flutter_hbb/lib/remote_page.dart @@ -27,6 +27,9 @@ class _RemotePageState extends State { bool _showBar = true; double _bottom = 0; var _scaleMode = false; + double _xOffset = 0; + double _yOffset = 0; + double _scale = 1; final FocusNode _focusNode = FocusNode(); @override @@ -153,8 +156,55 @@ class _RemotePageState extends State { ), ) : null, - body: RawGestureDetector( - gestures: buildGuestures(), + body: GestureDetector( + onLongPressStart: (details) { + var x = details.globalPosition.dx; + var y = details.globalPosition.dy; + print('long press'); + () async { + var value = await showMenu( + context: context, + position: + RelativeRect.fromLTRB(x + 20, y + 20, x + 20, y + 20), + items: [ + PopupMenuItem( + child: Text(_scaleMode ? 'Pan Mode' : 'Scale Mode'), + value: 'mode'), + ], + elevation: 8.0, + ); + if (value == 'mode') { + setState(() => _scaleMode = !_scaleMode); + } + }(); + }, + onDoubleTap: () { + print('double tap'); + }, + onTap: () { + print('tap'); + }, + onScaleStart: (details) { + _scale = 1; + _xOffset = details.focalPoint.dx; + _yOffset = details.focalPoint.dy; + FFI.canvasModel.startPan(); + }, + onScaleUpdate: (details) { + var scale = details.scale; + if (scale == 1) { + var x = details.focalPoint.dx; + var y = details.focalPoint.dy; + var dx = x - _xOffset; + var dy = y - _yOffset; + FFI.canvasModel.updateOffset(dx, dy); + _xOffset = x; + _yOffset = y; + } else { + FFI.canvasModel.updateScale(scale / _scale); + _scale = scale; + } + }, child: FlutterEasyLoading( child: Container( color: MyTheme.canvasColor, @@ -288,8 +338,11 @@ class ImagePaint extends StatelessWidget { @override Widget build(BuildContext context) { final m = Provider.of(context); + final c = Provider.of(context); + var s = c.scale; return CustomPaint( - painter: new ImagePainter(image: m.image, x: 0, y: 0), + painter: + new ImagePainter(image: m.image, x: c.x / s, y: c.y / s, scale: s), ); } } @@ -298,8 +351,14 @@ class CursorPaint extends StatelessWidget { @override Widget build(BuildContext context) { final m = Provider.of(context); + final c = Provider.of(context); + var s = c.scale; return CustomPaint( - painter: new ImagePainter(image: m.image, x: m.x, y: m.y), + painter: new ImagePainter( + image: m.image, + x: m.x * s - m.hotx + c.x, + y: m.y * s - m.hoty + c.y, + scale: 1), ); } } @@ -309,15 +368,18 @@ class ImagePainter extends CustomPainter { this.image, this.x, this.y, + this.scale, }); ui.Image image; double x; double y; + double scale; @override void paint(Canvas canvas, Size size) { if (image == null) return; + canvas.scale(scale, scale); canvas.drawImage(image, new Offset(x, y), new Paint()); }