diff --git a/lib/model.dart b/lib/model.dart index b6eca7642..687b113f3 100644 --- a/lib/model.dart +++ b/lib/model.dart @@ -200,10 +200,14 @@ class ImageModel with ChangeNotifier { void update(ui.Image image) { if (_image == null && image != null) { - 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); + if (isDesktop) { + FFI.canvasModel.updateViewStyle(); + } else { + 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); + } initializeCursorAndCanvas(); } _image = image; @@ -240,6 +244,29 @@ class CanvasModel with ChangeNotifier { double get y => _y; double get scale => _scale; + void updateViewStyle() { + final s = FFI.getByName('peer_option', 'view-style'); + 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') { + final s = s1 < s2 ? s1 : s2; + if (s < 1) { + _scale = s; + } + } else if (s == 'stretch') { + final s = s1 > s2 ? s1 : s2; + if (s > 1) { + _scale = s; + } + } else { + _scale = 1; + } + _x = (size.width - FFI.ffiModel.display.width * _scale) / 2; + _y = (size.height - FFI.ffiModel.display.height * _scale) / 2; + notifyListeners(); + } + void update(double x, double y, double scale) { _x = x; _y = y; @@ -258,8 +285,12 @@ class CanvasModel with ChangeNotifier { } void resetOffset() { - _x = 0; - _y = 0; + if (isDesktop) { + updateViewStyle(); + } else { + _x = 0; + _y = 0; + } notifyListeners(); } @@ -624,6 +655,18 @@ class FFI { static Future getVersion() async { return await PlatformFFI.getVersion(); } + + static handleMouse(Map evt) { + // + } + + static listenToMouse(bool yesOrNo) { + if (yesOrNo) { + PlatformFFI.startDesktopWebListener(handleMouse); + } else { + PlatformFFI.stopDesktopWebListener(); + } + } } class Peer { diff --git a/lib/native_model.dart b/lib/native_model.dart index 2c9df78b3..42242b340 100644 --- a/lib/native_model.dart +++ b/lib/native_model.dart @@ -101,6 +101,10 @@ class PlatformFFI { print(e); } } + + static void startDesktopWebListener( + Function(Map) handleMouse) {} + static void stopDesktopWebListener() {} } final localeName = Platform.localeName; diff --git a/lib/remote_page.dart b/lib/remote_page.dart index 63cfb92d4..bd632b2ef 100644 --- a/lib/remote_page.dart +++ b/lib/remote_page.dart @@ -242,6 +242,7 @@ class _RemotePageState extends State { return false; }, child: Scaffold( + // resizeToAvoidBottomInset: true, floatingActionButton: !showActionButton ? null : FloatingActionButton( @@ -264,8 +265,8 @@ class _RemotePageState extends State { child: Container( color: Colors.black, child: isDesktop - ? getBodyForDesktop() - : SafeArea(child: getBodyForMobile())), + ? getBodyForDesktopWithListener() + : SafeArea(child: getBodyForMobileWithGuesture())), )), ); } @@ -344,7 +345,7 @@ class _RemotePageState extends State { ); } - Widget getBodyForMobile() { + Widget getBodyForMobileWithGuesture() { return GestureDetector( onLongPress: () { if (_drag || _scroll) return; @@ -406,36 +407,47 @@ class _RemotePageState extends State { } } }, - child: Container( - color: MyTheme.canvasColor, - child: Stack(children: [ - ImagePaint(), - CursorPaint(), - getHelpTools(), - SizedBox( - width: 0, - height: 0, - child: !_showEdit - ? Container() - : TextFormField( - textInputAction: TextInputAction.newline, - autocorrect: false, - enableSuggestions: false, - focusNode: _focusNode, - maxLines: null, - initialValue: - _value, // trick way to make backspace work always - keyboardType: TextInputType.multiline, - onChanged: handleInput, - ), - ), - ]))); + child: getBodyForMobile()); } - Widget getBodyForDesktop() { - return GestureDetector( - onTapDown: (details) => {}, - onTapUp: (details) => {}, + Widget getBodyForMobile() { + return Container( + color: MyTheme.canvasColor, + child: Stack(children: [ + ImagePaint(), + CursorPaint(), + getHelpTools(), + SizedBox( + width: 0, + height: 0, + child: !_showEdit + ? Container() + : TextFormField( + textInputAction: TextInputAction.newline, + autocorrect: false, + enableSuggestions: false, + focusNode: _focusNode, + maxLines: null, + initialValue: + _value, // trick way to make backspace work always + keyboardType: TextInputType.multiline, + onChanged: handleInput, + ), + ), + ])); + } + + Widget getBodyForDesktopWithListener() { + print(FFI.ffiModel.display.width); + return MouseRegion( + onEnter: (event) { + print('enter'); + FFI.listenToMouse(true); + }, + onExit: (event) { + print('exit'); + FFI.listenToMouse(false); + }, child: Container( color: MyTheme.canvasColor, child: Stack(children: [ImagePaint(), CursorPaint()]))); @@ -932,6 +944,7 @@ void showOptions(BuildContext context) { viewStyle = value; FFI.setByName( 'peer_option', '{"name": "view-style", "value": "$value"}'); + FFI.canvasModel.updateViewStyle(); }); }; return Tuple3( diff --git a/lib/web_model.dart b/lib/web_model.dart index 807a42ab9..156196312 100644 --- a/lib/web_model.dart +++ b/lib/web_model.dart @@ -1,6 +1,10 @@ import 'dart:typed_data'; import 'dart:js' as js; import 'common.dart'; +import 'dart:html'; +import 'dart:async'; + +final List> mouselisteners = []; class PlatformFFI { static void clearRgbaFrame() {} @@ -22,10 +26,47 @@ class PlatformFFI { } static Future init() async { + window.document.onContextMenu.listen((evt) => evt.preventDefault()); isWeb = true; isDesktop = !js.context.callMethod('isMobile'); js.context.callMethod('init'); } + + // MouseRegion onHover not work for mouse move when right button down + static void startDesktopWebListener( + Function(Map) handleMouse) { + // document.body.getElementsByTagName('flt-glass-pane')[0].style.cursor = 'none'; + mouselisteners.add(window.document.onMouseMove + .listen((evt) => handleMouse(getEvent(evt)))); + mouselisteners.add(window.document.onMouseDown + .listen((evt) => handleMouse(getEvent(evt)))); + mouselisteners.add( + window.document.onMouseUp.listen((evt) => handleMouse(getEvent(evt)))); + mouselisteners.add(window.document.onMouseWheel.listen((evt) => {})); + } + + static void stopDesktopWebListener() { + mouselisteners.forEach((l) { + l.cancel(); + }); + mouselisteners.clear(); + } } -final localeName = js.context.callMethod('getLanguage') as String; +Map getEvent(MouseEvent evt) { + // https://github.com/novnc/noVNC/blob/679b45fa3b453c7cf32f4b4455f4814818ecf161/core/rfb.js + // https://developer.mozilla.org/zh-CN/docs/Web/API/Element/mousedown_event + final out = {}; + out['type'] = evt.type; + out['x'] = evt.client.x; + out['y'] = evt.client.y; + out['ctrl'] = evt.ctrlKey; + out['shift'] = evt.shiftKey; + out['alt'] = evt.altKey; + out['meta'] = evt.metaKey; + out['buttons'] = evt + .buttons; // left button: 1, right button: 2, middle button: 4, 1 | 2 = 3 (left + right) + return out; +} + +final localeName = window.navigator.language;