flutter_desktop: fix cursor, mid commit

Signed-off-by: fufesou <shuanglongchen@yeah.net>
This commit is contained in:
fufesou 2022-09-03 10:39:33 +08:00
parent f37dc72bbf
commit 11c5364e71
4 changed files with 242 additions and 164 deletions

View File

@ -1,16 +1,26 @@
import 'package:get/get.dart'; import 'package:get/get.dart';
import '../consts.dart'; import '../consts.dart';
import '../models/platform_model.dart';
class PrivacyModeState { class PrivacyModeState {
static String tag(String id) => 'privacy_mode_$id'; static String tag(String id) => 'privacy_mode_$id';
static void init(String id) { static void init(String id) {
final RxBool state = false.obs; final key = tag(id);
Get.put(state, tag: tag(id)); if (!Get.isRegistered(tag: key)) {
final RxBool state = false.obs;
Get.put(state, tag: key);
}
}
static void delete(String id) {
final key = tag(id);
if (Get.isRegistered(tag: key)) {
Get.delete(tag: key);
}
} }
static void delete(String id) => Get.delete(tag: tag(id));
static RxBool find(String id) => Get.find<RxBool>(tag: tag(id)); static RxBool find(String id) => Get.find<RxBool>(tag: tag(id));
} }
@ -18,11 +28,20 @@ class BlockInputState {
static String tag(String id) => 'block_input_$id'; static String tag(String id) => 'block_input_$id';
static void init(String id) { static void init(String id) {
final RxBool state = false.obs; final key = tag(id);
Get.put(state, tag: tag(id)); if (!Get.isRegistered(tag: key)) {
final RxBool state = false.obs;
Get.put(state, tag: key);
}
}
static void delete(String id) {
final key = tag(id);
if (Get.isRegistered(tag: key)) {
Get.delete(tag: key);
}
} }
static void delete(String id) => Get.delete(tag: tag(id));
static RxBool find(String id) => Get.find<RxBool>(tag: tag(id)); static RxBool find(String id) => Get.find<RxBool>(tag: tag(id));
} }
@ -30,11 +49,20 @@ class CurrentDisplayState {
static String tag(String id) => 'current_display_$id'; static String tag(String id) => 'current_display_$id';
static void init(String id) { static void init(String id) {
final RxInt state = RxInt(0); final key = tag(id);
Get.put(state, tag: tag(id)); if (!Get.isRegistered(tag: key)) {
final RxInt state = RxInt(0);
Get.put(state, tag: key);
}
}
static void delete(String id) {
final key = tag(id);
if (Get.isRegistered(tag: key)) {
Get.delete(tag: key);
}
} }
static void delete(String id) => Get.delete(tag: tag(id));
static RxInt find(String id) => Get.find<RxInt>(tag: tag(id)); static RxInt find(String id) => Get.find<RxInt>(tag: tag(id));
} }
@ -85,3 +113,46 @@ class ConnectionTypeState {
static ConnectionType find(String id) => static ConnectionType find(String id) =>
Get.find<ConnectionType>(tag: tag(id)); Get.find<ConnectionType>(tag: tag(id));
} }
class ShowRemoteCursorState {
static String tag(String id) => 'show_remote_cursor_$id';
static void init(String id) {
final key = tag(id);
if (!Get.isRegistered(tag: key)) {
final RxBool state = false.obs;
Get.put(state, tag: key);
}
}
static void delete(String id) {
final key = tag(id);
if (Get.isRegistered(tag: key)) {
Get.delete(tag: key);
}
}
static RxBool find(String id) => Get.find<RxBool>(tag: tag(id));
}
class KeyboardEnabledState {
static String tag(String id) => 'keyboard_enabled_$id';
static void init(String id) {
final key = tag(id);
if (!Get.isRegistered(tag: key)) {
// Server side, default true
final RxBool state = true.obs;
Get.put(state, tag: key);
}
}
static void delete(String id) {
final key = tag(id);
if (Get.isRegistered(tag: key)) {
Get.delete(tag: key);
}
}
static RxBool find(String id) => Get.find<RxBool>(tag: tag(id));
}

View File

@ -40,6 +40,8 @@ class _RemotePageState extends State<RemotePage>
Timer? _timer; Timer? _timer;
String _value = ''; String _value = '';
final _cursorOverImage = false.obs; final _cursorOverImage = false.obs;
late RxBool _showRemoteCursor;
late RxBool _keyboardEnabled;
final FocusNode _mobileFocusNode = FocusNode(); final FocusNode _mobileFocusNode = FocusNode();
final FocusNode _physicalFocusNode = FocusNode(); final FocusNode _physicalFocusNode = FocusNode();
@ -56,17 +58,24 @@ class _RemotePageState extends State<RemotePage>
PrivacyModeState.init(id); PrivacyModeState.init(id);
BlockInputState.init(id); BlockInputState.init(id);
CurrentDisplayState.init(id); CurrentDisplayState.init(id);
KeyboardEnabledState.init(id);
ShowRemoteCursorState.init(id);
_showRemoteCursor = ShowRemoteCursorState.find(id);
_keyboardEnabled = KeyboardEnabledState.find(id);
} }
void _removeStates(String id) { void _removeStates(String id) {
PrivacyModeState.delete(id); PrivacyModeState.delete(id);
BlockInputState.delete(id); BlockInputState.delete(id);
CurrentDisplayState.delete(id); CurrentDisplayState.delete(id);
ShowRemoteCursorState.delete(id);
KeyboardEnabledState.delete(id);
} }
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_initStates(widget.id);
_ffi = FFI(); _ffi = FFI();
_updateTabBarHeight(); _updateTabBarHeight();
Get.put(_ffi, tag: widget.id); Get.put(_ffi, tag: widget.id);
@ -84,7 +93,8 @@ class _RemotePageState extends State<RemotePage>
_ffi.listenToMouse(true); _ffi.listenToMouse(true);
_ffi.qualityMonitorModel.checkShowQualityMonitor(widget.id); _ffi.qualityMonitorModel.checkShowQualityMonitor(widget.id);
// WindowManager.instance.addListener(this); // WindowManager.instance.addListener(this);
_initStates(widget.id); _showRemoteCursor.value = bind.sessionGetToggleOptionSync(
id: widget.id, arg: 'show-remote-cursor');
} }
@override @override
@ -197,8 +207,7 @@ class _RemotePageState extends State<RemotePage>
_ffi.inputKey(label, down: down, press: press ?? false); _ffi.inputKey(label, down: down, press: press ?? false);
} }
Widget buildBody(BuildContext context, FfiModel ffiModel) { Widget buildBody(BuildContext context) {
final keyboard = ffiModel.permissions['keyboard'] != false;
return Scaffold( return Scaffold(
backgroundColor: MyTheme.color(context).bg, backgroundColor: MyTheme.color(context).bg,
body: Overlay( body: Overlay(
@ -208,8 +217,7 @@ class _RemotePageState extends State<RemotePage>
_ffi.dialogManager.setOverlayState(Overlay.of(context)); _ffi.dialogManager.setOverlayState(Overlay.of(context));
return Container( return Container(
color: Colors.black, color: Colors.black,
child: getRawPointerAndKeyBody( child: getRawPointerAndKeyBody(getBodyForDesktop(context)));
getBodyForDesktop(context, keyboard)));
}) })
], ],
)); ));
@ -224,70 +232,61 @@ class _RemotePageState extends State<RemotePage>
clientClose(_ffi.dialogManager); clientClose(_ffi.dialogManager);
return false; return false;
}, },
child: MultiProvider( child: MultiProvider(providers: [
providers: [ ChangeNotifierProvider.value(value: _ffi.ffiModel),
ChangeNotifierProvider.value(value: _ffi.ffiModel), ChangeNotifierProvider.value(value: _ffi.imageModel),
ChangeNotifierProvider.value(value: _ffi.imageModel), ChangeNotifierProvider.value(value: _ffi.cursorModel),
ChangeNotifierProvider.value(value: _ffi.cursorModel), ChangeNotifierProvider.value(value: _ffi.canvasModel),
ChangeNotifierProvider.value(value: _ffi.canvasModel), ], child: buildBody(context)));
],
child: Consumer<FfiModel>(
builder: (context, ffiModel, child) =>
buildBody(context, ffiModel))));
} }
Widget getRawPointerAndKeyBody(Widget child) { Widget getRawPointerAndKeyBody(Widget child) {
return Consumer<FfiModel>( return FocusScope(
builder: (context, FfiModel, _child) => MouseRegion( autofocus: true,
cursor: FfiModel.permissions['keyboard'] != false child: Focus(
? SystemMouseCursors.none autofocus: true,
: MouseCursor.defer, canRequestFocus: true,
child: FocusScope( focusNode: _physicalFocusNode,
autofocus: true, onFocusChange: (bool v) {
child: Focus( _imageFocused = v;
autofocus: true, },
canRequestFocus: true, onKey: (data, e) {
focusNode: _physicalFocusNode, final key = e.logicalKey;
onFocusChange: (bool v) { if (e is RawKeyDownEvent) {
_imageFocused = v; if (e.repeat) {
}, sendRawKey(e, press: true);
onKey: (data, e) { } else {
final key = e.logicalKey; if (e.isAltPressed && !_ffi.alt) {
if (e is RawKeyDownEvent) { _ffi.alt = true;
if (e.repeat) { } else if (e.isControlPressed && !_ffi.ctrl) {
sendRawKey(e, press: true); _ffi.ctrl = true;
} else { } else if (e.isShiftPressed && !_ffi.shift) {
if (e.isAltPressed && !_ffi.alt) { _ffi.shift = true;
_ffi.alt = true; } else if (e.isMetaPressed && !_ffi.command) {
} else if (e.isControlPressed && !_ffi.ctrl) { _ffi.command = true;
_ffi.ctrl = true; }
} else if (e.isShiftPressed && !_ffi.shift) { sendRawKey(e, down: true);
_ffi.shift = true; }
} else if (e.isMetaPressed && !_ffi.command) { }
_ffi.command = true; if (e is RawKeyUpEvent) {
} if (key == LogicalKeyboardKey.altLeft ||
sendRawKey(e, down: true); key == LogicalKeyboardKey.altRight) {
} _ffi.alt = false;
} } else if (key == LogicalKeyboardKey.controlLeft ||
if (e is RawKeyUpEvent) { key == LogicalKeyboardKey.controlRight) {
if (key == LogicalKeyboardKey.altLeft || _ffi.ctrl = false;
key == LogicalKeyboardKey.altRight) { } else if (key == LogicalKeyboardKey.shiftRight ||
_ffi.alt = false; key == LogicalKeyboardKey.shiftLeft) {
} else if (key == LogicalKeyboardKey.controlLeft || _ffi.shift = false;
key == LogicalKeyboardKey.controlRight) { } else if (key == LogicalKeyboardKey.metaLeft ||
_ffi.ctrl = false; key == LogicalKeyboardKey.metaRight) {
} else if (key == LogicalKeyboardKey.shiftRight || _ffi.command = false;
key == LogicalKeyboardKey.shiftLeft) { }
_ffi.shift = false; sendRawKey(e);
} else if (key == LogicalKeyboardKey.metaLeft || }
key == LogicalKeyboardKey.metaRight) { return KeyEventResult.handled;
_ffi.command = false; },
} child: child));
sendRawKey(e);
}
return KeyEventResult.handled;
},
child: child))));
} }
/// touchMode only: /// touchMode only:
@ -382,32 +381,30 @@ class _RemotePageState extends State<RemotePage>
child: child)); child: child));
} }
Widget getBodyForDesktop(BuildContext context, bool keyboard) { Widget getBodyForDesktop(BuildContext context) {
var paints = <Widget>[ var paints = <Widget>[
MouseRegion(onEnter: (evt) { MouseRegion(onEnter: (evt) {
bind.hostStopSystemKeyPropagate(stopped: false); bind.hostStopSystemKeyPropagate(stopped: false);
}, onExit: (evt) { }, onExit: (evt) {
bind.hostStopSystemKeyPropagate(stopped: true); bind.hostStopSystemKeyPropagate(stopped: true);
}, child: Container( }, child: LayoutBuilder(builder: (context, constraints) {
child: LayoutBuilder(builder: (context, constraints) { Future.delayed(Duration.zero, () {
Future.delayed(Duration.zero, () { Provider.of<CanvasModel>(context, listen: false).updateViewStyle();
Provider.of<CanvasModel>(context, listen: false).updateViewStyle(); });
}); return ImagePaint(
return ImagePaint( id: widget.id,
id: widget.id, cursorOverImage: _cursorOverImage,
cursorOverImage: _cursorOverImage, keyboardEnabled: _keyboardEnabled,
listenerBuilder: _buildImageListener, listenerBuilder: _buildImageListener,
); );
}), }))
))
]; ];
final cursor = bind.sessionGetToggleOptionSync(
id: widget.id, arg: 'show-remote-cursor'); paints.add(Obx(() => Visibility(
if (keyboard || cursor) { visible: _keyboardEnabled.isTrue || _showRemoteCursor.isTrue,
paints.add(CursorPaint( child: CursorPaint(
id: widget.id, id: widget.id,
)); ))));
}
paints.add(QualityMonitor(_ffi.qualityMonitorModel)); paints.add(QualityMonitor(_ffi.qualityMonitorModel));
paints.add(RemoteMenubar( paints.add(RemoteMenubar(
id: widget.id, id: widget.id,
@ -447,7 +444,7 @@ class _RemotePageState extends State<RemotePage>
_ffi.canvasModel.updateViewStyle(); _ffi.canvasModel.updateViewStyle();
break; break;
case 'maximize': case 'maximize':
Future.delayed(Duration(milliseconds: 100), () { Future.delayed(const Duration(milliseconds: 100), () {
_ffi.canvasModel.updateViewStyle(); _ffi.canvasModel.updateViewStyle();
}); });
break; break;
@ -461,6 +458,7 @@ class _RemotePageState extends State<RemotePage>
class ImagePaint extends StatelessWidget { class ImagePaint extends StatelessWidget {
final String id; final String id;
final Rx<bool> cursorOverImage; final Rx<bool> cursorOverImage;
final Rx<bool> keyboardEnabled;
final Widget Function(Widget)? listenerBuilder; final Widget Function(Widget)? listenerBuilder;
final ScrollController _horizontal = ScrollController(); final ScrollController _horizontal = ScrollController();
final ScrollController _vertical = ScrollController(); final ScrollController _vertical = ScrollController();
@ -469,6 +467,7 @@ class ImagePaint extends StatelessWidget {
{Key? key, {Key? key,
required this.id, required this.id,
required this.cursorOverImage, required this.cursorOverImage,
required this.keyboardEnabled,
this.listenerBuilder}) this.listenerBuilder})
: super(key: key); : super(key: key);
@ -485,25 +484,26 @@ class ImagePaint extends StatelessWidget {
painter: ImagePainter(image: m.image, x: 0, y: 0, scale: s), painter: ImagePainter(image: m.image, x: 0, y: 0, scale: s),
)); ));
return Center( return Center(
child: NotificationListener<ScrollNotification>( child: NotificationListener<ScrollNotification>(
onNotification: (notification) { onNotification: (notification) {
final percentX = _horizontal.position.extentBefore / final percentX = _horizontal.position.extentBefore /
(_horizontal.position.extentBefore + (_horizontal.position.extentBefore +
_horizontal.position.extentInside + _horizontal.position.extentInside +
_horizontal.position.extentAfter); _horizontal.position.extentAfter);
final percentY = _vertical.position.extentBefore / final percentY = _vertical.position.extentBefore /
(_vertical.position.extentBefore + (_vertical.position.extentBefore +
_vertical.position.extentInside + _vertical.position.extentInside +
_vertical.position.extentAfter); _vertical.position.extentAfter);
c.setScrollPercent(percentX, percentY); c.setScrollPercent(percentX, percentY);
return false; return false;
}, },
child: Obx(() => MouseRegion( child: Obx(() => MouseRegion(
cursor: cursorOverImage.value cursor: (keyboardEnabled.isTrue && cursorOverImage.isTrue)
? SystemMouseCursors.none ? SystemMouseCursors.none
: SystemMouseCursors.basic, : MouseCursor.defer,
child: _buildCrossScrollbar(_buildListener(imageWidget)))), child: _buildCrossScrollbar(_buildListener(imageWidget)))),
)); ),
);
} else { } else {
final imageWidget = SizedBox( final imageWidget = SizedBox(
width: c.size.width, width: c.size.width,
@ -562,13 +562,12 @@ class CursorPaint extends StatelessWidget {
final m = Provider.of<CursorModel>(context); final m = Provider.of<CursorModel>(context);
final c = Provider.of<CanvasModel>(context); final c = Provider.of<CanvasModel>(context);
// final adjust = m.adjustForKeyboard(); // final adjust = m.adjustForKeyboard();
var s = c.scale;
return CustomPaint( return CustomPaint(
painter: ImagePainter( painter: ImagePainter(
image: m.image, image: m.image,
x: m.x * s - m.hotx + c.x, x: m.x - m.hotx + c.x / c.scale,
y: m.y * s - m.hoty + c.y, y: m.y - m.hoty + c.y / c.scale,
scale: 1), scale: c.scale),
); );
} }
} }
@ -620,30 +619,30 @@ class QualityMonitor extends StatelessWidget {
right: 10, right: 10,
child: qualityMonitorModel.show child: qualityMonitorModel.show
? Container( ? Container(
padding: EdgeInsets.all(8), padding: const EdgeInsets.all(8),
color: MyTheme.canvasColor.withAlpha(120), color: MyTheme.canvasColor.withAlpha(120),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
"Speed: ${qualityMonitorModel.data.speed ?? ''}", "Speed: ${qualityMonitorModel.data.speed ?? ''}",
style: TextStyle(color: MyTheme.grayBg), style: const TextStyle(color: MyTheme.grayBg),
), ),
Text( Text(
"FPS: ${qualityMonitorModel.data.fps ?? ''}", "FPS: ${qualityMonitorModel.data.fps ?? ''}",
style: TextStyle(color: MyTheme.grayBg), style: const TextStyle(color: MyTheme.grayBg),
), ),
Text( Text(
"Delay: ${qualityMonitorModel.data.delay ?? ''} ms", "Delay: ${qualityMonitorModel.data.delay ?? ''} ms",
style: TextStyle(color: MyTheme.grayBg), style: const TextStyle(color: MyTheme.grayBg),
), ),
Text( Text(
"Target Bitrate: ${qualityMonitorModel.data.targetBitrate ?? ''}kb", "Target Bitrate: ${qualityMonitorModel.data.targetBitrate ?? ''}kb",
style: TextStyle(color: MyTheme.grayBg), style: const TextStyle(color: MyTheme.grayBg),
), ),
Text( Text(
"Codec: ${qualityMonitorModel.data.codecFormat ?? ''}", "Codec: ${qualityMonitorModel.data.codecFormat ?? ''}",
style: TextStyle(color: MyTheme.grayBg), style: const TextStyle(color: MyTheme.grayBg),
), ),
], ],
), ),

View File

@ -522,16 +522,19 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
} }
}), }),
MenuEntryDivider<String>(), MenuEntryDivider<String>(),
MenuEntrySwitch<String>( () {
text: translate('Show remote cursor'), final state = ShowRemoteCursorState.find(widget.id);
getter: () async { return MenuEntrySwitch2<String>(
return bind.sessionGetToggleOptionSync( text: translate('Show remote cursor'),
id: widget.id, arg: 'show-remote-cursor'); getter: () {
}, return state;
setter: (bool v) async { },
await bind.sessionToggleOption( setter: (bool v) async {
id: widget.id, value: 'show-remote-cursor'); state.value = v;
}), await bind.sessionToggleOption(
id: widget.id, value: 'show-remote-cursor');
});
}(),
MenuEntrySwitch<String>( MenuEntrySwitch<String>(
text: translate('Show quality monitor'), text: translate('Show quality monitor'),
getter: () async { getter: () async {
@ -560,12 +563,12 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
'Lock after session end', 'lock-after-session-end')); 'Lock after session end', 'lock-after-session-end'));
if (pi.platform == 'Windows') { if (pi.platform == 'Windows') {
displayMenu.add(MenuEntrySwitch2<String>( displayMenu.add(MenuEntrySwitch2<String>(
dismissOnClicked: true,
text: translate('Privacy mode'), text: translate('Privacy mode'),
getter: () { getter: () {
return PrivacyModeState.find(widget.id); return PrivacyModeState.find(widget.id);
}, },
setter: (bool v) async { setter: (bool v) async {
Navigator.pop(context);
await bind.sessionToggleOption( await bind.sessionToggleOption(
id: widget.id, value: 'privacy-mode'); id: widget.id, value: 'privacy-mode');
})); }));

View File

@ -32,7 +32,7 @@ class FfiModel with ChangeNotifier {
Display _display = Display(); Display _display = Display();
var _inputBlocked = false; var _inputBlocked = false;
final _permissions = Map<String, bool>(); final _permissions = <String, bool>{};
bool? _secure; bool? _secure;
bool? _direct; bool? _direct;
bool _touchMode = false; bool _touchMode = false;
@ -71,12 +71,13 @@ class FfiModel with ChangeNotifier {
} }
} }
void updatePermission(Map<String, dynamic> evt) { void updatePermission(Map<String, dynamic> evt, String id) {
evt.forEach((k, v) { evt.forEach((k, v) {
if (k == 'name' || k.isEmpty) return; if (k == 'name' || k.isEmpty) return;
_permissions[k] = v == 'true'; _permissions[k] = v == 'true';
}); });
print('$_permissions'); KeyboardEnabledState.find(id).value = _permissions['keyboard'] != false;
debugPrint('$_permissions');
notifyListeners(); notifyListeners();
} }
@ -146,7 +147,7 @@ class FfiModel with ChangeNotifier {
} else if (name == 'clipboard') { } else if (name == 'clipboard') {
Clipboard.setData(ClipboardData(text: evt['content'])); Clipboard.setData(ClipboardData(text: evt['content']));
} else if (name == 'permission') { } else if (name == 'permission') {
parent.target?.ffiModel.updatePermission(evt); parent.target?.ffiModel.updatePermission(evt, peerId);
} else if (name == 'chat_client_mode') { } else if (name == 'chat_client_mode') {
parent.target?.chatModel parent.target?.chatModel
.receive(ChatModel.clientModeID, evt['text'] ?? ""); .receive(ChatModel.clientModeID, evt['text'] ?? "");
@ -185,7 +186,7 @@ class FfiModel with ChangeNotifier {
/// Bind the event listener to receive events from the Rust core. /// Bind the event listener to receive events from the Rust core.
void updateEventListener(String peerId) { void updateEventListener(String peerId) {
final void Function(Map<String, dynamic>) cb = (evt) { cb(evt) {
var name = evt['name']; var name = evt['name'];
if (name == 'msgbox') { if (name == 'msgbox') {
handleMsgBox(evt, peerId); handleMsgBox(evt, peerId);
@ -205,7 +206,7 @@ class FfiModel with ChangeNotifier {
} else if (name == 'clipboard') { } else if (name == 'clipboard') {
Clipboard.setData(ClipboardData(text: evt['content'])); Clipboard.setData(ClipboardData(text: evt['content']));
} else if (name == 'permission') { } else if (name == 'permission') {
parent.target?.ffiModel.updatePermission(evt); parent.target?.ffiModel.updatePermission(evt, peerId);
} else if (name == 'chat_client_mode') { } else if (name == 'chat_client_mode') {
parent.target?.chatModel parent.target?.chatModel
.receive(ChatModel.clientModeID, evt['text'] ?? ""); .receive(ChatModel.clientModeID, evt['text'] ?? "");
@ -239,7 +240,8 @@ class FfiModel with ChangeNotifier {
} else if (name == 'update_privacy_mode') { } else if (name == 'update_privacy_mode') {
updatePrivacyMode(evt, peerId); updatePrivacyMode(evt, peerId);
} }
}; }
platformFFI.setEventCallback(cb); platformFFI.setEventCallback(cb);
} }
@ -321,7 +323,7 @@ class FfiModel with ChangeNotifier {
if (isPeerAndroid) { if (isPeerAndroid) {
_touchMode = true; _touchMode = true;
if (parent.target?.ffiModel.permissions['keyboard'] != false) { if (parent.target?.ffiModel.permissions['keyboard'] != false) {
Timer(Duration(milliseconds: 100), showMobileActionsOverlay); Timer(const Duration(milliseconds: 100), showMobileActionsOverlay);
} }
} else { } else {
_touchMode = _touchMode =
@ -464,15 +466,20 @@ enum ScrollStyle {
} }
class CanvasModel with ChangeNotifier { class CanvasModel with ChangeNotifier {
// image offset of canvas
double _x = 0;
// image offset of canvas
double _y = 0;
// image scale
double _scale = 1.0;
// the tabbar over the image
double tabBarHeight = 0.0;
// TODO multi canvas model
String id = "";
// scroll offset x percent // scroll offset x percent
double _scrollX = 0.0; double _scrollX = 0.0;
// scroll offset y percent // scroll offset y percent
double _scrollY = 0.0; double _scrollY = 0.0;
double _x = 0;
double _y = 0;
double _scale = 1.0;
double _tabBarHeight = 0.0;
String id = ""; // TODO multi canvas model
ScrollStyle _scrollStyle = ScrollStyle.scrollauto; ScrollStyle _scrollStyle = ScrollStyle.scrollauto;
WeakReference<FFI> parent; WeakReference<FFI> parent;
@ -492,9 +499,6 @@ class CanvasModel with ChangeNotifier {
double get scrollX => _scrollX; double get scrollX => _scrollX;
double get scrollY => _scrollY; double get scrollY => _scrollY;
set tabBarHeight(double h) => _tabBarHeight = h;
double get tabBarHeight => _tabBarHeight;
void updateViewStyle() async { void updateViewStyle() async {
final style = await bind.sessionGetOption(id: id, arg: 'view-style'); final style = await bind.sessionGetOption(id: id, arg: 'view-style');
if (style == null) { if (style == null) {
@ -548,12 +552,11 @@ class CanvasModel with ChangeNotifier {
Size get size { Size get size {
final size = MediaQueryData.fromWindow(ui.window).size; final size = MediaQueryData.fromWindow(ui.window).size;
return Size(size.width, size.height - _tabBarHeight); return Size(size.width, size.height - tabBarHeight);
} }
void moveDesktopMouse(double x, double y) { void moveDesktopMouse(double x, double y) {
// On mobile platforms, move the canvas with the cursor. // On mobile platforms, move the canvas with the cursor.
//if (!isDesktop) {
final dw = getDisplayWidth() * _scale; final dw = getDisplayWidth() * _scale;
final dh = getDisplayHeight() * _scale; final dh = getDisplayHeight() * _scale;
var dxOffset = 0; var dxOffset = 0;
@ -579,8 +582,13 @@ class CanvasModel with ChangeNotifier {
if (dxOffset != 0 || dyOffset != 0) { if (dxOffset != 0 || dyOffset != 0) {
notifyListeners(); notifyListeners();
} }
//}
parent.target?.cursorModel.moveLocal(x, y); // If keyboard is not permitted, do not move cursor when mouse is moving.
if (parent.target != null) {
if (parent.target!.ffiModel.keyboard()) {
parent.target!.cursorModel.moveLocal(x, y);
}
}
} }
set scale(v) { set scale(v) {
@ -597,11 +605,8 @@ class CanvasModel with ChangeNotifier {
if (isWebDesktop) { if (isWebDesktop) {
updateViewStyle(); updateViewStyle();
} else { } else {
final size = MediaQueryData.fromWindow(ui.window).size; _x = (size.width - getDisplayWidth() * _scale) / 2;
final canvasWidth = size.width; _y = (size.height - getDisplayHeight() * _scale) / 2;
final canvasHeight = size.height - _tabBarHeight;
_x = (canvasWidth - getDisplayWidth() * _scale) / 2;
_y = (canvasHeight - getDisplayHeight() * _scale) / 2;
} }
notifyListeners(); notifyListeners();
} }
@ -613,7 +618,7 @@ class CanvasModel with ChangeNotifier {
void updateScale(double v) { void updateScale(double v) {
if (parent.target?.imageModel.image == null) return; if (parent.target?.imageModel.image == null) return;
final offset = parent.target?.cursorModel.offset ?? Offset(0, 0); final offset = parent.target?.cursorModel.offset ?? const Offset(0, 0);
var r = parent.target?.cursorModel.getVisibleRect() ?? Rect.zero; var r = parent.target?.cursorModel.getVisibleRect() ?? Rect.zero;
final px0 = (offset.dx - r.left) * _scale; final px0 = (offset.dx - r.left) * _scale;
final py0 = (offset.dy - r.top) * _scale; final py0 = (offset.dy - r.top) * _scale;
@ -640,7 +645,7 @@ class CanvasModel with ChangeNotifier {
class CursorModel with ChangeNotifier { class CursorModel with ChangeNotifier {
ui.Image? _image; ui.Image? _image;
final _images = Map<int, Tuple3<ui.Image, double, double>>(); final _images = <int, Tuple3<ui.Image, double, double>>{};
double _x = -10000; double _x = -10000;
double _y = -10000; double _y = -10000;
double _hotx = 0; double _hotx = 0;
@ -807,7 +812,7 @@ class CursorModel with ChangeNotifier {
// my throw exception, because the listener maybe already dispose // my throw exception, because the listener maybe already dispose
notifyListeners(); notifyListeners();
} catch (e) { } catch (e) {
print('notify cursor: $e'); debugPrint('notify cursor: $e');
} }
}); });
} }