Merge pull request #1470 from fufesou/flutter_cursors_cache
Flutter cursors cache
This commit is contained in:
commit
48998ded2e
flutter
lib
pubspec.yaml@ -568,13 +568,16 @@ class ImagePaint extends StatelessWidget {
|
|||||||
cursor: (cursorOverImage.isTrue && keyboardEnabled.isTrue)
|
cursor: (cursorOverImage.isTrue && keyboardEnabled.isTrue)
|
||||||
? (remoteCursorMoved.isTrue
|
? (remoteCursorMoved.isTrue
|
||||||
? SystemMouseCursors.none
|
? SystemMouseCursors.none
|
||||||
: (cursor.pngData != null
|
: (cursor.cacheLinux != null
|
||||||
? FlutterCustomMemoryImageCursor(
|
? FlutterCustomMemoryImageCursor(
|
||||||
pixbuf: cursor.pngData!,
|
pixbuf: cursor.cacheLinux!.data,
|
||||||
hotx: cursor.hotx,
|
key: cursor.cacheLinux!.key,
|
||||||
hoty: cursor.hoty,
|
hotx: cursor.cacheLinux!.hotx,
|
||||||
imageWidth: (cursor.image!.width * s).toInt(),
|
hoty: cursor.cacheLinux!.hoty,
|
||||||
imageHeight: (cursor.image!.height * s).toInt(),
|
imageWidth:
|
||||||
|
(cursor.cacheLinux!.width * s).toInt(),
|
||||||
|
imageHeight:
|
||||||
|
(cursor.cacheLinux!.height * s).toInt(),
|
||||||
)
|
)
|
||||||
: MouseCursor.defer))
|
: MouseCursor.defer))
|
||||||
: MouseCursor.defer,
|
: MouseCursor.defer,
|
||||||
|
@ -8,7 +8,7 @@ import './material_mod_popup_menu.dart' as mod_menu;
|
|||||||
|
|
||||||
// https://stackoverflow.com/questions/68318314/flutter-popup-menu-inside-popup-menu
|
// https://stackoverflow.com/questions/68318314/flutter-popup-menu-inside-popup-menu
|
||||||
class PopupMenuChildrenItem<T> extends mod_menu.PopupMenuEntry<T> {
|
class PopupMenuChildrenItem<T> extends mod_menu.PopupMenuEntry<T> {
|
||||||
const PopupMenuChildrenItem({
|
PopupMenuChildrenItem({
|
||||||
key,
|
key,
|
||||||
this.height = kMinInteractiveDimension,
|
this.height = kMinInteractiveDimension,
|
||||||
this.padding,
|
this.padding,
|
||||||
@ -43,6 +43,16 @@ class PopupMenuChildrenItem<T> extends mod_menu.PopupMenuEntry<T> {
|
|||||||
|
|
||||||
class MyPopupMenuItemState<T, W extends PopupMenuChildrenItem<T>>
|
class MyPopupMenuItemState<T, W extends PopupMenuChildrenItem<T>>
|
||||||
extends State<W> {
|
extends State<W> {
|
||||||
|
RxBool enabled = true.obs;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
if (widget.enabled != null) {
|
||||||
|
enabled.value = widget.enabled!.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
void handleTap(T value) {
|
void handleTap(T value) {
|
||||||
widget.onTap?.call();
|
widget.onTap?.call();
|
||||||
@ -56,9 +66,8 @@ class MyPopupMenuItemState<T, W extends PopupMenuChildrenItem<T>>
|
|||||||
TextStyle style = widget.textStyle ??
|
TextStyle style = widget.textStyle ??
|
||||||
popupMenuTheme.textStyle ??
|
popupMenuTheme.textStyle ??
|
||||||
theme.textTheme.subtitle1!;
|
theme.textTheme.subtitle1!;
|
||||||
return Obx(() {
|
return Obx(() => mod_menu.PopupMenuButton<T>(
|
||||||
return mod_menu.PopupMenuButton<T>(
|
enabled: enabled.value,
|
||||||
enabled: widget.enabled != null ? widget.enabled!.value : true,
|
|
||||||
position: widget.position,
|
position: widget.position,
|
||||||
offset: widget.offset,
|
offset: widget.offset,
|
||||||
onSelected: handleTap,
|
onSelected: handleTap,
|
||||||
@ -75,8 +84,7 @@ class MyPopupMenuItemState<T, W extends PopupMenuChildrenItem<T>>
|
|||||||
child: widget.child,
|
child: widget.child,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
));
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -342,7 +350,7 @@ typedef SwitchSetter = Future<void> Function(bool);
|
|||||||
|
|
||||||
abstract class MenuEntrySwitchBase<T> extends MenuEntryBase<T> {
|
abstract class MenuEntrySwitchBase<T> extends MenuEntryBase<T> {
|
||||||
final String text;
|
final String text;
|
||||||
final Rx<TextStyle>? textStyle;
|
Rx<TextStyle>? textStyle;
|
||||||
|
|
||||||
MenuEntrySwitchBase({
|
MenuEntrySwitchBase({
|
||||||
required this.text,
|
required this.text,
|
||||||
@ -357,6 +365,11 @@ abstract class MenuEntrySwitchBase<T> extends MenuEntryBase<T> {
|
|||||||
@override
|
@override
|
||||||
List<mod_menu.PopupMenuEntry<T>> build(
|
List<mod_menu.PopupMenuEntry<T>> build(
|
||||||
BuildContext context, MenuConfig conf) {
|
BuildContext context, MenuConfig conf) {
|
||||||
|
textStyle ??= const TextStyle(
|
||||||
|
color: Colors.black,
|
||||||
|
fontSize: MenuConfig.fontSize,
|
||||||
|
fontWeight: FontWeight.normal)
|
||||||
|
.obs;
|
||||||
return [
|
return [
|
||||||
mod_menu.PopupMenuItem(
|
mod_menu.PopupMenuItem(
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
@ -366,23 +379,10 @@ abstract class MenuEntrySwitchBase<T> extends MenuEntryBase<T> {
|
|||||||
alignment: AlignmentDirectional.centerStart,
|
alignment: AlignmentDirectional.centerStart,
|
||||||
height: conf.height,
|
height: conf.height,
|
||||||
child: Row(children: [
|
child: Row(children: [
|
||||||
() {
|
Obx(() => Text(
|
||||||
if (textStyle != null) {
|
|
||||||
final style = textStyle!;
|
|
||||||
return Obx(() => Text(
|
|
||||||
text,
|
text,
|
||||||
style: style.value,
|
style: textStyle!.value,
|
||||||
));
|
)),
|
||||||
} else {
|
|
||||||
return Text(
|
|
||||||
text,
|
|
||||||
style: const TextStyle(
|
|
||||||
color: Colors.black,
|
|
||||||
fontSize: MenuConfig.fontSize,
|
|
||||||
fontWeight: FontWeight.normal),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}(),
|
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Align(
|
child: Align(
|
||||||
alignment: Alignment.centerRight,
|
alignment: Alignment.centerRight,
|
||||||
@ -485,6 +485,7 @@ class MenuEntrySubMenu<T> extends MenuEntryBase<T> {
|
|||||||
@override
|
@override
|
||||||
List<mod_menu.PopupMenuEntry<T>> build(
|
List<mod_menu.PopupMenuEntry<T>> build(
|
||||||
BuildContext context, MenuConfig conf) {
|
BuildContext context, MenuConfig conf) {
|
||||||
|
super.enabled ??= true.obs;
|
||||||
return [
|
return [
|
||||||
PopupMenuChildrenItem(
|
PopupMenuChildrenItem(
|
||||||
enabled: super.enabled,
|
enabled: super.enabled,
|
||||||
@ -500,9 +501,7 @@ class MenuEntrySubMenu<T> extends MenuEntryBase<T> {
|
|||||||
Obx(() => Text(
|
Obx(() => Text(
|
||||||
text,
|
text,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: (super.enabled != null ? super.enabled!.value : true)
|
color: super.enabled!.value ? Colors.black : Colors.grey,
|
||||||
? Colors.black
|
|
||||||
: Colors.grey,
|
|
||||||
fontSize: MenuConfig.fontSize,
|
fontSize: MenuConfig.fontSize,
|
||||||
fontWeight: FontWeight.normal),
|
fontWeight: FontWeight.normal),
|
||||||
)),
|
)),
|
||||||
@ -511,9 +510,7 @@ class MenuEntrySubMenu<T> extends MenuEntryBase<T> {
|
|||||||
alignment: Alignment.centerRight,
|
alignment: Alignment.centerRight,
|
||||||
child: Obx(() => Icon(
|
child: Obx(() => Icon(
|
||||||
Icons.keyboard_arrow_right,
|
Icons.keyboard_arrow_right,
|
||||||
color: (super.enabled != null ? super.enabled!.value : true)
|
color: super.enabled!.value ? conf.commonColor : Colors.grey,
|
||||||
? conf.commonColor
|
|
||||||
: Colors.grey,
|
|
||||||
)),
|
)),
|
||||||
))
|
))
|
||||||
]),
|
]),
|
||||||
@ -537,11 +534,6 @@ class MenuEntryButton<T> extends MenuEntryBase<T> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildChild(BuildContext context, MenuConfig conf) {
|
Widget _buildChild(BuildContext context, MenuConfig conf) {
|
||||||
return Obx(() {
|
|
||||||
bool enabled = true;
|
|
||||||
if (super.enabled != null) {
|
|
||||||
enabled = super.enabled!.value;
|
|
||||||
}
|
|
||||||
const enabledStyle = TextStyle(
|
const enabledStyle = TextStyle(
|
||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
fontSize: MenuConfig.fontSize,
|
fontSize: MenuConfig.fontSize,
|
||||||
@ -550,8 +542,9 @@ class MenuEntryButton<T> extends MenuEntryBase<T> {
|
|||||||
color: Colors.grey,
|
color: Colors.grey,
|
||||||
fontSize: MenuConfig.fontSize,
|
fontSize: MenuConfig.fontSize,
|
||||||
fontWeight: FontWeight.normal);
|
fontWeight: FontWeight.normal);
|
||||||
return TextButton(
|
super.enabled ??= true.obs;
|
||||||
onPressed: enabled
|
return Obx(() => TextButton(
|
||||||
|
onPressed: super.enabled!.value
|
||||||
? () {
|
? () {
|
||||||
if (super.dismissOnClicked && Navigator.canPop(context)) {
|
if (super.dismissOnClicked && Navigator.canPop(context)) {
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
@ -562,10 +555,10 @@ class MenuEntryButton<T> extends MenuEntryBase<T> {
|
|||||||
child: Container(
|
child: Container(
|
||||||
alignment: AlignmentDirectional.centerStart,
|
alignment: AlignmentDirectional.centerStart,
|
||||||
constraints: BoxConstraints(minHeight: conf.height),
|
constraints: BoxConstraints(minHeight: conf.height),
|
||||||
child: childBuilder(enabled ? enabledStyle : disabledStyle),
|
child: childBuilder(
|
||||||
|
super.enabled!.value ? enabledStyle : disabledStyle),
|
||||||
),
|
),
|
||||||
);
|
));
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -573,7 +566,6 @@ class MenuEntryButton<T> extends MenuEntryBase<T> {
|
|||||||
BuildContext context, MenuConfig conf) {
|
BuildContext context, MenuConfig conf) {
|
||||||
return [
|
return [
|
||||||
mod_menu.PopupMenuItem(
|
mod_menu.PopupMenuItem(
|
||||||
enabled: super.enabled != null ? super.enabled!.value : true,
|
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
height: conf.height,
|
height: conf.height,
|
||||||
child: _buildChild(context, conf),
|
child: _buildChild(context, conf),
|
||||||
|
@ -611,7 +611,7 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
|||||||
MenuEntryRadioOption(text: translate('Map mode'), value: 'map'),
|
MenuEntryRadioOption(text: translate('Map mode'), value: 'map'),
|
||||||
],
|
],
|
||||||
curOptionGetter: () async {
|
curOptionGetter: () async {
|
||||||
return await bind.sessionGetKeyboardName(id: widget.id) ?? 'legacy';
|
return await bind.sessionGetKeyboardName(id: widget.id);
|
||||||
},
|
},
|
||||||
optionSetter: (String oldValue, String newValue) async {
|
optionSetter: (String oldValue, String newValue) async {
|
||||||
await bind.sessionSetKeyboardMode(
|
await bind.sessionSetKeyboardMode(
|
||||||
|
@ -17,6 +17,7 @@ import 'package:flutter_hbb/models/server_model.dart';
|
|||||||
import 'package:flutter_hbb/models/user_model.dart';
|
import 'package:flutter_hbb/models/user_model.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
import 'package:tuple/tuple.dart';
|
||||||
|
import 'package:flutter_custom_cursor/flutter_custom_cursor.dart';
|
||||||
|
|
||||||
import '../common.dart';
|
import '../common.dart';
|
||||||
import '../common/shared_state.dart';
|
import '../common/shared_state.dart';
|
||||||
@ -351,7 +352,7 @@ class ImageModel with ChangeNotifier {
|
|||||||
// my throw exception, because the listener maybe already dispose
|
// my throw exception, because the listener maybe already dispose
|
||||||
update(image, tabBarHeight);
|
update(image, tabBarHeight);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('update image: $e');
|
debugPrint('update image: $e');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -594,11 +595,36 @@ class CanvasModel with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// data for cursor
|
||||||
|
class CursorData {
|
||||||
|
final String peerId;
|
||||||
|
final int id;
|
||||||
|
final Uint8List? data;
|
||||||
|
final double hotx;
|
||||||
|
final double hoty;
|
||||||
|
final int width;
|
||||||
|
final int height;
|
||||||
|
late String key;
|
||||||
|
|
||||||
|
CursorData({
|
||||||
|
required this.peerId,
|
||||||
|
required this.id,
|
||||||
|
required this.data,
|
||||||
|
required this.hotx,
|
||||||
|
required this.hoty,
|
||||||
|
required this.width,
|
||||||
|
required this.height,
|
||||||
|
}) {
|
||||||
|
key =
|
||||||
|
'${peerId}_${id}_${(hotx * 10e6).round().toInt()}_${(hoty * 10e6).round().toInt()}_${width}_$height';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class CursorModel with ChangeNotifier {
|
class CursorModel with ChangeNotifier {
|
||||||
ui.Image? _image;
|
ui.Image? _image;
|
||||||
final _images = <int, Tuple3<ui.Image, double, double>>{};
|
final _images = <int, Tuple3<ui.Image, double, double>>{};
|
||||||
Uint8List? _pngData;
|
CursorData? _cacheLinux;
|
||||||
final _pngs = <int, Uint8List?>{};
|
final _cacheMapLinux = <int, CursorData>{};
|
||||||
double _x = -10000;
|
double _x = -10000;
|
||||||
double _y = -10000;
|
double _y = -10000;
|
||||||
double _hotx = 0;
|
double _hotx = 0;
|
||||||
@ -609,7 +635,7 @@ class CursorModel with ChangeNotifier {
|
|||||||
WeakReference<FFI> parent;
|
WeakReference<FFI> parent;
|
||||||
|
|
||||||
ui.Image? get image => _image;
|
ui.Image? get image => _image;
|
||||||
Uint8List? get pngData => _pngData;
|
CursorData? get cacheLinux => _cacheLinux;
|
||||||
|
|
||||||
double get x => _x - _displayOriginX;
|
double get x => _x - _displayOriginX;
|
||||||
|
|
||||||
@ -623,6 +649,9 @@ class CursorModel with ChangeNotifier {
|
|||||||
|
|
||||||
CursorModel(this.parent);
|
CursorModel(this.parent);
|
||||||
|
|
||||||
|
List<String> get cachedKeysLinux =>
|
||||||
|
_cacheMapLinux.values.map((v) => v.key).toList();
|
||||||
|
|
||||||
// remote physical display coordinate
|
// remote physical display coordinate
|
||||||
Rect getVisibleRect() {
|
Rect getVisibleRect() {
|
||||||
final size = MediaQueryData.fromWindow(ui.window).size;
|
final size = MediaQueryData.fromWindow(ui.window).size;
|
||||||
@ -763,13 +792,7 @@ class CursorModel with ChangeNotifier {
|
|||||||
if (parent.target?.id != pid) return;
|
if (parent.target?.id != pid) return;
|
||||||
_image = image;
|
_image = image;
|
||||||
_images[id] = Tuple3(image, _hotx, _hoty);
|
_images[id] = Tuple3(image, _hotx, _hoty);
|
||||||
final data = await image.toByteData(format: ImageByteFormat.png);
|
_updateCacheLinux(image, id, width, height);
|
||||||
if (data != null) {
|
|
||||||
_pngData = data.buffer.asUint8List();
|
|
||||||
} else {
|
|
||||||
_pngData = null;
|
|
||||||
}
|
|
||||||
_pngs[id] = _pngData;
|
|
||||||
try {
|
try {
|
||||||
// my throw exception, because the listener maybe already dispose
|
// my throw exception, because the listener maybe already dispose
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
@ -780,8 +803,28 @@ class CursorModel with ChangeNotifier {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _updateCacheLinux(ui.Image image, int id, int w, int h) async {
|
||||||
|
final data = await image.toByteData(format: ImageByteFormat.png);
|
||||||
|
late Uint8List? dataLinux;
|
||||||
|
if (data != null) {
|
||||||
|
dataLinux = data.buffer.asUint8List();
|
||||||
|
} else {
|
||||||
|
dataLinux = null;
|
||||||
|
}
|
||||||
|
_cacheLinux = CursorData(
|
||||||
|
peerId: this.id,
|
||||||
|
data: dataLinux,
|
||||||
|
id: id,
|
||||||
|
hotx: _hotx,
|
||||||
|
hoty: _hoty,
|
||||||
|
width: w,
|
||||||
|
height: h,
|
||||||
|
);
|
||||||
|
_cacheMapLinux[id] = _cacheLinux!;
|
||||||
|
}
|
||||||
|
|
||||||
void updateCursorId(Map<String, dynamic> evt) {
|
void updateCursorId(Map<String, dynamic> evt) {
|
||||||
_pngData = _pngs[int.parse(evt['id'])];
|
_cacheLinux = _cacheMapLinux[int.parse(evt['id'])];
|
||||||
final tmp = _images[int.parse(evt['id'])];
|
final tmp = _images[int.parse(evt['id'])];
|
||||||
if (tmp != null) {
|
if (tmp != null) {
|
||||||
_image = tmp.item1;
|
_image = tmp.item1;
|
||||||
@ -828,6 +871,17 @@ class CursorModel with ChangeNotifier {
|
|||||||
_x = -10000;
|
_x = -10000;
|
||||||
_image = null;
|
_image = null;
|
||||||
_images.clear();
|
_images.clear();
|
||||||
|
|
||||||
|
_clearCacheLinux();
|
||||||
|
_cacheLinux = null;
|
||||||
|
_cacheMapLinux.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _clearCacheLinux() {
|
||||||
|
final cachedKeys = [...cachedKeysLinux];
|
||||||
|
for (var key in cachedKeys) {
|
||||||
|
customCursorController.freeCache(key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -871,7 +925,9 @@ class QualityMonitorModel with ChangeNotifier {
|
|||||||
_data.codecFormat = evt['codec_format'];
|
_data.codecFormat = evt['codec_format'];
|
||||||
}
|
}
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
} catch (e) {}
|
} catch (e) {
|
||||||
|
//
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1230,7 +1286,7 @@ class FFI {
|
|||||||
Future<Map<String, String>> getHttpHeaders() async {
|
Future<Map<String, String>> getHttpHeaders() async {
|
||||||
return {
|
return {
|
||||||
'Authorization':
|
'Authorization':
|
||||||
'Bearer ' + await bind.mainGetLocalOption(key: 'access_token')
|
'Bearer ${await bind.mainGetLocalOption(key: 'access_token')}'
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -71,7 +71,7 @@ dependencies:
|
|||||||
flutter_custom_cursor:
|
flutter_custom_cursor:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/Kingtous/rustdesk_flutter_custom_cursor
|
url: https://github.com/Kingtous/rustdesk_flutter_custom_cursor
|
||||||
ref: 7fe78c139c711bafbae52d924e9caf18bd193e28
|
ref: 9021e21de36c84edf01d5034f38eda580463163b
|
||||||
get: ^4.6.5
|
get: ^4.6.5
|
||||||
visibility_detector: ^0.3.3
|
visibility_detector: ^0.3.3
|
||||||
contextmenu: ^3.0.0
|
contextmenu: ^3.0.0
|
||||||
|
Loading…
x
Reference in New Issue
Block a user