From d9755abbc2cd9df14580535967732211726ad145 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 20 Apr 2023 18:10:06 +0800 Subject: [PATCH 1/3] tmp commit Signed-off-by: fufesou --- flutter/lib/models/model.dart | 5 ++-- flutter/lib/plugin/common.dart | 1 + flutter/lib/plugin/desc.dart | 5 ++++ flutter/lib/plugin/model.dart | 28 +++++++++++++++++ flutter/lib/plugin/reloader.dart | 29 ------------------ flutter/lib/plugin/widget.dart | 30 +++++++++++++++++++ .../widgets/remote/toolbar/display.dart | 27 +++++++++++++++++ 7 files changed, 94 insertions(+), 31 deletions(-) create mode 100644 flutter/lib/plugin/common.dart create mode 100644 flutter/lib/plugin/model.dart delete mode 100644 flutter/lib/plugin/reloader.dart create mode 100644 flutter/lib/plugin/widgets/remote/toolbar/display.dart diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index b9a2a7598..44ca71912 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -17,7 +17,8 @@ import 'package:flutter_hbb/models/server_model.dart'; import 'package:flutter_hbb/models/user_model.dart'; import 'package:flutter_hbb/models/state_model.dart'; import 'package:flutter_hbb/plugin/event.dart'; -import 'package:flutter_hbb/plugin/reloader.dart'; +import 'package:flutter_hbb/plugin/desc.dart'; +import 'package:flutter_hbb/plugin/widget.dart'; import 'package:flutter_hbb/common/shared_state.dart'; import 'package:tuple/tuple.dart'; import 'package:image/image.dart' as img2; @@ -229,7 +230,7 @@ class FfiModel with ChangeNotifier { } else if (name == "fingerprint") { FingerprintState.find(peerId).value = evt['fingerprint'] ?? ''; } else if (name == "plugin_desc") { - handleReloading(evt, peerId); + updateDesc(evt); } else if (name == "plugin_event") { handlePluginEvent( evt, peerId, (Map e) => handleMsgBox(e, peerId)); diff --git a/flutter/lib/plugin/common.dart b/flutter/lib/plugin/common.dart new file mode 100644 index 000000000..5cbc0f871 --- /dev/null +++ b/flutter/lib/plugin/common.dart @@ -0,0 +1 @@ +typedef PluginId = String; diff --git a/flutter/lib/plugin/desc.dart b/flutter/lib/plugin/desc.dart index c6b04d11c..6be712697 100644 --- a/flutter/lib/plugin/desc.dart +++ b/flutter/lib/plugin/desc.dart @@ -38,9 +38,14 @@ class UiType { : button = json['t'] == 'Button' ? UiButton.fromJson(json['c']) : null, checkbox = json['t'] != 'Checkbox' ? UiCheckbox.fromJson(json['c']) : null; + + bool get isValid => button != null || checkbox != null; } class Location { + // location key: + // host|main|settings|display|others + // client|remote|toolbar|display HashMap ui; Location(this.ui); diff --git a/flutter/lib/plugin/model.dart b/flutter/lib/plugin/model.dart new file mode 100644 index 000000000..824992e20 --- /dev/null +++ b/flutter/lib/plugin/model.dart @@ -0,0 +1,28 @@ +import 'package:flutter/material.dart'; +import './common.dart'; +import './desc.dart'; + +// ui location +// host|main|settings|display|others +// client|remote|toolbar|display + +final Map> locationModels = {}; + +class LocationModel with ChangeNotifier { + final List uiList = []; + + void add(UiType ui) { + uiList.add(ui); + notifyListeners(); + } +} + +void addLocation(PluginId id, String location, UiType ui) { + if (!locationModels.containsKey(id)) { + locationModels[id] = {}; + } + if (!locationModels[id]!.containsKey(location)) { + locationModels[id]![location] = LocationModel(); + } + locationModels[id]![location]!.add(ui); +} diff --git a/flutter/lib/plugin/reloader.dart b/flutter/lib/plugin/reloader.dart deleted file mode 100644 index 1b1641f87..000000000 --- a/flutter/lib/plugin/reloader.dart +++ /dev/null @@ -1,29 +0,0 @@ -void handleReloading(Map evt, String peer) { - // location - // host|main|settings|display|others - // client|remote|toolbar|display - // - // ui - // { - // "t": "Button", - // "c": { - // "key": "key", - // "text": "text", - // "icon": "icon", - // "tooltip": "tooltip", - // "action": "action" - // } - // } - // - // { - // "t": "Checkbox", - // "c": { - // "key": "key", - // "text": "text", - // "tooltip": "tooltip", - // "action": "action" - // } - // } - // - -} diff --git a/flutter/lib/plugin/widget.dart b/flutter/lib/plugin/widget.dart index dbdfbbadd..a99e25e4e 100644 --- a/flutter/lib/plugin/widget.dart +++ b/flutter/lib/plugin/widget.dart @@ -1,4 +1,6 @@ import 'package:flutter/material.dart'; +import './desc.dart'; +import './model.dart'; final Map pluginWidgets = {}; @@ -14,4 +16,32 @@ class PluginWidget { required this.location, required this.widget, }); + + // static Widget createButton(UiButton btn) {} + + // static Widget createCheckbox(UiCheckbox chk) {} + + // // ui location + // // host|main|settings|display|others + // // client|remote|toolbar|display + // static Widget? create(String id, String locatin, UiType ui) { + // if (ui.button != null) { + // return createButton(ui.button!); + // } else if (ui.checkbox != null) { + // return createCheckbox(ui.checkbox!); + // } else { + // return null; + // } + // } +} + +void handleReloading(Map evt, String peer) { + if (evt['id'] == null || evt['location'] == null) { + return; + } + final ui = UiType.fromJson(evt); + if (!ui.isValid) { + return; + } + addLocation(evt['id']!, evt['location']!, ui); } diff --git a/flutter/lib/plugin/widgets/remote/toolbar/display.dart b/flutter/lib/plugin/widgets/remote/toolbar/display.dart new file mode 100644 index 000000000..1f7cc359e --- /dev/null +++ b/flutter/lib/plugin/widgets/remote/toolbar/display.dart @@ -0,0 +1,27 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +import '../../../model.dart'; + +class Display extends StatelessWidget { + final String peerId; + final LocationModel locationModel; + + Display({ + Key? key, + required this.peerId, + required this.locationModel, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return ChangeNotifierProvider.value( + value: locationModel, + child: Consumer(builder: (context, model, child) { + return Column( + children: [], + ); + }), + ); + } +} From 9a08e0bed4ccc9f40afc802567b4c6f01b981be2 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 20 Apr 2023 20:57:47 +0800 Subject: [PATCH 2/3] add ui event Signed-off-by: fufesou --- .../lib/desktop/widgets/remote_toolbar.dart | 43 ++++----- flutter/lib/plugin/common.dart | 42 ++++++++ flutter/lib/plugin/desc.dart | 96 ++++++++++++------- flutter/lib/plugin/model.dart | 2 + flutter/lib/plugin/widget.dart | 3 - .../widgets/remote/toolbar/display.dart | 85 +++++++++++++++- src/flutter_ffi.rs | 48 ++++++++++ src/plugin/config.rs | 4 +- src/plugin/mod.rs | 2 + 9 files changed, 262 insertions(+), 63 deletions(-) diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index f8a38c830..f44f62514 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -1,5 +1,4 @@ import 'dart:convert'; -import 'dart:io'; import 'dart:ui' as ui; import 'package:flutter/material.dart'; @@ -641,7 +640,7 @@ class _ControlMenu extends StatelessWidget { if (e.divider) { return Divider(); } else { - return _MenuItemButton( + return MenuButton( child: e.child, onPressed: e.onPressed, ffi: ffi, @@ -711,7 +710,7 @@ class _DisplayMenuState extends State<_DisplayMenu> { if (!visible) return Offstage(); return Column( children: [ - _MenuItemButton( + MenuButton( child: Text(translate('Adjust Window')), onPressed: _doAdjustWindow, ffi: widget.ffi), @@ -828,7 +827,7 @@ class _DisplayMenuState extends State<_DisplayMenu> { final v = data as List>; return Column(children: [ ...v - .map((e) => _RadioMenuButton( + .map((e) => RdoMenuButton( value: e.value, groupValue: e.groupValue, onChanged: e.onChanged, @@ -858,14 +857,14 @@ class _DisplayMenuState extends State<_DisplayMenu> { final enabled = widget.ffi.canvasModel.imageOverflow.value; return Column(children: [ - _RadioMenuButton( + RdoMenuButton( child: Text(translate('ScrollAuto')), value: kRemoteScrollStyleAuto, groupValue: groupValue, onChanged: enabled ? (value) => onChange(value) : null, ffi: widget.ffi, ), - _RadioMenuButton( + RdoMenuButton( child: Text(translate('Scrollbar')), value: kRemoteScrollStyleBar, groupValue: groupValue, @@ -886,7 +885,7 @@ class _DisplayMenuState extends State<_DisplayMenu> { ffi: widget.ffi, child: Text(translate('Image Quality')), menuChildren: v - .map((e) => _RadioMenuButton( + .map((e) => RdoMenuButton( value: e.value, groupValue: e.groupValue, onChanged: e.onChanged, @@ -908,7 +907,7 @@ class _DisplayMenuState extends State<_DisplayMenu> { ffi: widget.ffi, child: Text(translate('Codec')), menuChildren: v - .map((e) => _RadioMenuButton( + .map((e) => RdoMenuButton( value: e.value, groupValue: e.groupValue, onChanged: e.onChanged, @@ -948,7 +947,7 @@ class _DisplayMenuState extends State<_DisplayMenu> { return _SubmenuButton( ffi: widget.ffi, menuChildren: resolutions - .map((e) => _RadioMenuButton( + .map((e) => RdoMenuButton( value: '${e.width}x${e.height}', groupValue: groupValue, onChanged: onChanged, @@ -966,7 +965,7 @@ class _DisplayMenuState extends State<_DisplayMenu> { if (v.isEmpty) return Offstage(); return Column( children: v - .map((e) => _CheckboxMenuButton( + .map((e) => CkbMenuButton( value: e.value, onChanged: e.onChanged, child: e.child, @@ -1026,7 +1025,7 @@ class _KeyboardMenu extends StatelessWidget { KeyboardModeMenu(key: _kKeyMapMode, menu: 'Map mode'), KeyboardModeMenu(key: _kKeyTranslateMode, menu: 'Translate mode'), ]; - List<_RadioMenuButton> list = []; + List list = []; final enabled = !ffi.ffiModel.viewOnly; onChanged(String? value) async { if (value == null) return; @@ -1049,7 +1048,7 @@ class _KeyboardMenu extends StatelessWidget { if (mode.key == _kKeyTranslateMode) { text = '$text beta'; } - list.add(_RadioMenuButton( + list.add(RdoMenuButton( child: Text(text), value: mode.key, groupValue: groupValue, @@ -1069,7 +1068,7 @@ class _KeyboardMenu extends StatelessWidget { return Column( children: [ Divider(), - _MenuItemButton( + MenuButton( child: Text( '${translate('Local keyboard type')}: ${KBLayoutType.value}'), trailingIcon: const Icon(Icons.settings), @@ -1085,7 +1084,7 @@ class _KeyboardMenu extends StatelessWidget { view_mode() { final ffiModel = ffi.ffiModel; final enabled = version_cmp(pi.version, '1.2.0') >= 0 && ffiModel.keyboard; - return _CheckboxMenuButton( + return CkbMenuButton( value: ffiModel.viewOnly, onChanged: enabled ? (value) async { @@ -1129,7 +1128,7 @@ class _ChatMenuState extends State<_ChatMenu> { } textChat() { - return _MenuItemButton( + return MenuButton( child: Text(translate('Text chat')), ffi: widget.ffi, onPressed: () { @@ -1148,7 +1147,7 @@ class _ChatMenuState extends State<_ChatMenu> { } voiceCall() { - return _MenuItemButton( + return MenuButton( child: Text(translate('Voice call')), ffi: widget.ffi, onPressed: () => bind.sessionRequestVoiceCall(id: widget.id), @@ -1403,12 +1402,12 @@ class _SubmenuButton extends StatelessWidget { } } -class _MenuItemButton extends StatelessWidget { +class MenuButton extends StatelessWidget { final VoidCallback? onPressed; final Widget? trailingIcon; final Widget? child; final FFI ffi; - _MenuItemButton( + MenuButton( {Key? key, this.onPressed, this.trailingIcon, @@ -1431,12 +1430,12 @@ class _MenuItemButton extends StatelessWidget { } } -class _CheckboxMenuButton extends StatelessWidget { +class CkbMenuButton extends StatelessWidget { final bool? value; final ValueChanged? onChanged; final Widget? child; final FFI ffi; - const _CheckboxMenuButton( + const CkbMenuButton( {Key? key, required this.value, required this.onChanged, @@ -1460,13 +1459,13 @@ class _CheckboxMenuButton extends StatelessWidget { } } -class _RadioMenuButton extends StatelessWidget { +class RdoMenuButton extends StatelessWidget { final T value; final T? groupValue; final ValueChanged? onChanged; final Widget? child; final FFI ffi; - const _RadioMenuButton( + const RdoMenuButton( {Key? key, required this.value, required this.groupValue, diff --git a/flutter/lib/plugin/common.dart b/flutter/lib/plugin/common.dart index 5cbc0f871..8395e2a81 100644 --- a/flutter/lib/plugin/common.dart +++ b/flutter/lib/plugin/common.dart @@ -1 +1,43 @@ +import 'dart:convert'; + typedef PluginId = String; + +class MsgFromUi { + String remotePeerId; + String localPeerId; + String id; + String name; + String location; + String key; + String value; + String action; + + MsgFromUi({ + required this.remotePeerId, + required this.localPeerId, + required this.id, + required this.name, + required this.location, + required this.key, + required this.value, + required this.action, + }); + + Map toJson() { + return { + 'remote_peer_id': remotePeerId, + 'local_peer_id': localPeerId, + 'id': id, + 'name': name, + 'location': location, + 'key': key, + 'value': value, + 'action': action, + }; + } + + @override + String toString() { + return jsonEncode(toJson()); + } +} diff --git a/flutter/lib/plugin/desc.dart b/flutter/lib/plugin/desc.dart index 6be712697..8c0de4625 100644 --- a/flutter/lib/plugin/desc.dart +++ b/flutter/lib/plugin/desc.dart @@ -1,45 +1,58 @@ import 'dart:collection'; -class UiButton { - String key; - String text; - String icon; - String tooltip; - String action; - - UiButton(this.key, this.text, this.icon, this.tooltip, this.action); - UiButton.fromJson(Map json) - : key = json['key'] ?? '', - text = json['text'] ?? '', - icon = json['icon'] ?? '', - tooltip = json['tooltip'] ?? '', - action = json['action'] ?? ''; -} - -class UiCheckbox { - String key; - String text; - String tooltip; - String action; - - UiCheckbox(this.key, this.text, this.tooltip, this.action); - UiCheckbox.fromJson(Map json) - : key = json['key'] ?? '', - text = json['text'] ?? '', - tooltip = json['tooltip'] ?? '', - action = json['action'] ?? ''; -} +const String kValueTrue = '1'; +const String kValueFalse = '0'; class UiType { - UiButton? button; - UiCheckbox? checkbox; + String key; + String text; + String tooltip; + String action; + + UiType(this.key, this.text, this.tooltip, this.action); UiType.fromJson(Map json) - : button = json['t'] == 'Button' ? UiButton.fromJson(json['c']) : null, - checkbox = - json['t'] != 'Checkbox' ? UiCheckbox.fromJson(json['c']) : null; + : key = json['key'] ?? '', + text = json['text'] ?? '', + tooltip = json['tooltip'] ?? '', + action = json['action'] ?? ''; - bool get isValid => button != null || checkbox != null; + static UiType? create(Map json) { + if (json['t'] == 'Button') { + return UiButton.fromJson(json['c']); + } else if (json['t'] == 'Checkbox') { + return UiCheckbox.fromJson(json['c']); + } else { + return null; + } + } +} + +class UiButton extends UiType { + String icon; + + UiButton( + {required String key, + required String text, + required this.icon, + required String tooltip, + required String action}) + : super(key, text, tooltip, action); + + UiButton.fromJson(Map json) + : icon = json['icon'] ?? '', + super.fromJson(json); +} + +class UiCheckbox extends UiType { + UiCheckbox( + {required String key, + required String text, + required String tooltip, + required String action}) + : super(key, text, tooltip, action); + + UiCheckbox.fromJson(Map json) : super.fromJson(json); } class Location { @@ -49,6 +62,14 @@ class Location { HashMap ui; Location(this.ui); + Location.fromJson(Map json) : ui = HashMap() { + json.forEach((key, value) { + var ui = UiType.create(value); + if (ui != null) { + this.ui[ui.key] = ui; + } + }); + } } class ConfigItem { @@ -63,6 +84,11 @@ class ConfigItem { value = json['value'] ?? '', description = json['description'] ?? '', defaultValue = json['default'] ?? ''; + + static String get trueValue => kValueTrue; + static String get falseValue => kValueFalse; + static bool isTrue(String value) => value == kValueTrue; + static bool isFalse(String value) => value == kValueFalse; } class Config { diff --git a/flutter/lib/plugin/model.dart b/flutter/lib/plugin/model.dart index 824992e20..ac1b74828 100644 --- a/flutter/lib/plugin/model.dart +++ b/flutter/lib/plugin/model.dart @@ -15,6 +15,8 @@ class LocationModel with ChangeNotifier { uiList.add(ui); notifyListeners(); } + + bool get isEmpty => uiList.isEmpty; } void addLocation(PluginId id, String location, UiType ui) { diff --git a/flutter/lib/plugin/widget.dart b/flutter/lib/plugin/widget.dart index a99e25e4e..fc110077c 100644 --- a/flutter/lib/plugin/widget.dart +++ b/flutter/lib/plugin/widget.dart @@ -40,8 +40,5 @@ void handleReloading(Map evt, String peer) { return; } final ui = UiType.fromJson(evt); - if (!ui.isValid) { - return; - } addLocation(evt['id']!, evt['location']!, ui); } diff --git a/flutter/lib/plugin/widgets/remote/toolbar/display.dart b/flutter/lib/plugin/widgets/remote/toolbar/display.dart index 1f7cc359e..c5fa13e67 100644 --- a/flutter/lib/plugin/widgets/remote/toolbar/display.dart +++ b/flutter/lib/plugin/widgets/remote/toolbar/display.dart @@ -1,27 +1,110 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_hbb/models/model.dart'; import 'package:provider/provider.dart'; +import 'package:flutter_hbb/desktop/widgets/remote_toolbar.dart'; +import 'package:flutter_hbb/models/platform_model.dart'; +import '../../../desc.dart'; import '../../../model.dart'; +import '../../../common.dart'; class Display extends StatelessWidget { + final PluginId pluginId; final String peerId; + final FFI ffi; + final String location; final LocationModel locationModel; Display({ Key? key, + required this.pluginId, required this.peerId, + required this.ffi, + required this.location, required this.locationModel, }) : super(key: key); + bool get isEmpty => locationModel.isEmpty; + @override Widget build(BuildContext context) { return ChangeNotifierProvider.value( value: locationModel, child: Consumer(builder: (context, model, child) { return Column( - children: [], + children: locationModel.uiList.map((ui) => _buildItem(ui)).toList(), ); }), ); } + + Widget _buildItem(UiType ui) { + switch (ui.runtimeType) { + case UiButton: + return _buildMenuButton(ui as UiButton); + case UiCheckbox: + return _buildCheckboxMenuButton(ui as UiCheckbox); + default: + return Container(); + } + } + + Uint8List _makeEvent( + String localPeerId, + String key, { + bool? v, + }) { + final event = MsgFromUi( + remotePeerId: peerId, + localPeerId: localPeerId, + id: pluginId, + name: getDesc(pluginId)?.name ?? '', + location: location, + key: key, + value: + v != null ? (v ? ConfigItem.trueValue : ConfigItem.falseValue) : '', + action: '', + ); + return Uint8List.fromList(event.toString().codeUnits); + } + + Widget _buildMenuButton(UiButton ui) { + return MenuButton( + onPressed: () { + () async { + final localPeerId = await bind.mainGetMyId(); + bind.pluginEvent( + id: pluginId, + event: _makeEvent(localPeerId, ui.key), + ); + }(); + }, + // to-do: rustdesk translate or plugin translate ? + child: Text(ui.text), + ffi: ffi, + ); + } + + Widget _buildCheckboxMenuButton(UiCheckbox ui) { + final v = + bind.pluginGetSessionOption(id: pluginId, peer: peerId, key: ui.key); + return CkbMenuButton( + value: ConfigItem.isTrue(v), + onChanged: (v) { + if (v != null) { + () async { + final localPeerId = await bind.mainGetMyId(); + bind.pluginEvent( + id: pluginId, + event: _makeEvent(localPeerId, ui.key, v: v), + ); + }(); + } + }, + // to-do: rustdesk translate or plugin translate ? + child: Text(ui.text), + ffi: ffi, + ); + } } diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index 5f72431ef..62db17151 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -1405,6 +1405,54 @@ pub fn plugin_event(_id: String, _event: Vec) { } } +#[inline] +pub fn plugin_get_session_option(_id: String, _peer: String, _key: String) -> SyncReturn { + #[cfg(feature = "plugin_framework")] + #[cfg(not(any(target_os = "android", target_os = "ios")))] + { + return SyncReturn(crate::plugin::PeerConfig::get(&_id, &_peer, &_key)); + } + #[cfg(any( + not(feature = "plugin_framework"), + target_os = "android", + target_os = "ios" + ))] + return SyncReturn("".to_owned()); +} + +#[inline] +pub fn plugin_set_session_option(_id: String, _peer: String, _key: String, _value: String) { + #[cfg(feature = "plugin_framework")] + #[cfg(not(any(target_os = "android", target_os = "ios")))] + { + crate::plugin::PeerConfig::set(&_id, &_peer, &_key, &_value); + } +} + +#[inline] +pub fn plugin_get_local_option(_id: String, _key: String) -> SyncReturn { + #[cfg(feature = "plugin_framework")] + #[cfg(not(any(target_os = "android", target_os = "ios")))] + { + allow_err!(crate::plugin::LocalConfig::get(&_id, &key)); + } + #[cfg(any( + not(feature = "plugin_framework"), + target_os = "android", + target_os = "ios" + ))] + return SyncReturn("".to_owned()); +} + +#[inline] +pub fn plugin_set_local_option(_id: String, _key: String, _value: String) { + #[cfg(feature = "plugin_framework")] + #[cfg(not(any(target_os = "android", target_os = "ios")))] + { + crate::plugin::LocalConfig::set(&_id, &_key, &_value); + } +} + #[cfg(target_os = "android")] pub mod server_side { use hbb_common::{config, log}; diff --git a/src/plugin/config.rs b/src/plugin/config.rs index 7a9228425..d86053649 100644 --- a/src/plugin/config.rs +++ b/src/plugin/config.rs @@ -15,9 +15,9 @@ lazy_static::lazy_static! { } #[derive(Debug, Default, Serialize, Deserialize)] -struct LocalConfig(HashMap); +pub struct LocalConfig(HashMap); #[derive(Debug, Default, Serialize, Deserialize)] -struct PeerConfig(HashMap); +pub struct PeerConfig(HashMap); type PeersConfig = HashMap; #[inline] diff --git a/src/plugin/mod.rs b/src/plugin/mod.rs index 2f16f0325..c7ff2c4af 100644 --- a/src/plugin/mod.rs +++ b/src/plugin/mod.rs @@ -12,6 +12,8 @@ pub use plugins::{ reload_plugin, unload_plugin, }; +pub use config::{LocalConfig, PeerConfig}; + #[inline] fn cstr_to_string(cstr: *const c_char) -> ResultType { Ok(String::from_utf8(unsafe { From 1b303b7b27f38e89185f4a8428c1aac73e7a4a96 Mon Sep 17 00:00:00 2001 From: fufesou Date: Thu, 20 Apr 2023 22:53:43 +0800 Subject: [PATCH 3/3] plugin_framework, ui tmp Signed-off-by: fufesou --- flutter/lib/{ => desktop}/plugin/common.dart | 6 ++ flutter/lib/{ => desktop}/plugin/desc.dart | 0 flutter/lib/{ => desktop}/plugin/event.dart | 0 flutter/lib/desktop/plugin/model.dart | 44 ++++++++++ .../plugin/widget.dart} | 83 +++++++++++++++++-- .../lib/desktop/widgets/remote_toolbar.dart | 11 ++- flutter/lib/models/model.dart | 6 +- flutter/lib/plugin/model.dart | 30 ------- flutter/lib/plugin/widget.dart | 44 ---------- src/flutter_ffi.rs | 14 ++-- src/plugin/desc.rs | 2 +- 11 files changed, 147 insertions(+), 93 deletions(-) rename flutter/lib/{ => desktop}/plugin/common.dart (80%) rename flutter/lib/{ => desktop}/plugin/desc.dart (100%) rename flutter/lib/{ => desktop}/plugin/event.dart (100%) create mode 100644 flutter/lib/desktop/plugin/model.dart rename flutter/lib/{plugin/widgets/remote/toolbar/display.dart => desktop/plugin/widget.dart} (57%) delete mode 100644 flutter/lib/plugin/model.dart delete mode 100644 flutter/lib/plugin/widget.dart diff --git a/flutter/lib/plugin/common.dart b/flutter/lib/desktop/plugin/common.dart similarity index 80% rename from flutter/lib/plugin/common.dart rename to flutter/lib/desktop/plugin/common.dart index 8395e2a81..b1b3dbfa0 100644 --- a/flutter/lib/plugin/common.dart +++ b/flutter/lib/desktop/plugin/common.dart @@ -2,6 +2,12 @@ import 'dart:convert'; typedef PluginId = String; +// ui location +const String kLocationHostMainDisplayOthers = + 'host|main|settings|display|others'; +const String kLocationClientRemoteToolbarDisplay = + 'client|remote|toolbar|display'; + class MsgFromUi { String remotePeerId; String localPeerId; diff --git a/flutter/lib/plugin/desc.dart b/flutter/lib/desktop/plugin/desc.dart similarity index 100% rename from flutter/lib/plugin/desc.dart rename to flutter/lib/desktop/plugin/desc.dart diff --git a/flutter/lib/plugin/event.dart b/flutter/lib/desktop/plugin/event.dart similarity index 100% rename from flutter/lib/plugin/event.dart rename to flutter/lib/desktop/plugin/event.dart diff --git a/flutter/lib/desktop/plugin/model.dart b/flutter/lib/desktop/plugin/model.dart new file mode 100644 index 000000000..a823844aa --- /dev/null +++ b/flutter/lib/desktop/plugin/model.dart @@ -0,0 +1,44 @@ +import 'package:flutter/material.dart'; +import './common.dart'; +import './desc.dart'; + +final Map locationModels = {}; + +class PluginModel with ChangeNotifier { + final List uiList = []; + + void add(UiType ui) { + uiList.add(ui); + notifyListeners(); + } + + bool get isEmpty => uiList.isEmpty; +} + +class LocationModel with ChangeNotifier { + final Map pluginModels = {}; + + void add(PluginId id, UiType ui) { + if (pluginModels[id] != null) { + pluginModels[id]!.add(ui); + } else { + var model = PluginModel(); + model.add(ui); + pluginModels[id] = model; + notifyListeners(); + } + } + + bool get isEmpty => pluginModels.isEmpty; +} + +void addLocationUi(String location, PluginId id, UiType ui) { + locationModels[location]?.add(id, ui); +} + +LocationModel addLocation(String location) { + if (locationModels[location] == null) { + locationModels[location] = LocationModel(); + } + return locationModels[location]!; +} diff --git a/flutter/lib/plugin/widgets/remote/toolbar/display.dart b/flutter/lib/desktop/plugin/widget.dart similarity index 57% rename from flutter/lib/plugin/widgets/remote/toolbar/display.dart rename to flutter/lib/desktop/plugin/widget.dart index c5fa13e67..3f7b413ea 100644 --- a/flutter/lib/plugin/widgets/remote/toolbar/display.dart +++ b/flutter/lib/desktop/plugin/widget.dart @@ -5,20 +5,18 @@ import 'package:provider/provider.dart'; import 'package:flutter_hbb/desktop/widgets/remote_toolbar.dart'; import 'package:flutter_hbb/models/platform_model.dart'; -import '../../../desc.dart'; -import '../../../model.dart'; -import '../../../common.dart'; +import './desc.dart'; +import './model.dart'; +import './common.dart'; -class Display extends StatelessWidget { - final PluginId pluginId; +class LocationItem extends StatelessWidget { final String peerId; final FFI ffi; final String location; final LocationModel locationModel; - Display({ + LocationItem({ Key? key, - required this.pluginId, required this.peerId, required this.ffi, required this.location, @@ -27,18 +25,71 @@ class Display extends StatelessWidget { bool get isEmpty => locationModel.isEmpty; + static LocationItem createLocationItem( + String peerId, FFI ffi, String location) { + final model = addLocation(location); + return LocationItem( + peerId: peerId, + ffi: ffi, + location: location, + locationModel: model, + ); + } + @override Widget build(BuildContext context) { return ChangeNotifierProvider.value( value: locationModel, child: Consumer(builder: (context, model, child) { return Column( - children: locationModel.uiList.map((ui) => _buildItem(ui)).toList(), + children: model.pluginModels.entries + .map((entry) => _buildPluginItem(entry.key, entry.value)) + .toList(), ); }), ); } + Widget _buildPluginItem(PluginId id, PluginModel model) => PluginItem( + pluginId: id, + peerId: peerId, + ffi: ffi, + location: location, + pluginModel: model, + ); +} + +class PluginItem extends StatelessWidget { + final PluginId pluginId; + final String peerId; + final FFI ffi; + final String location; + final PluginModel pluginModel; + + PluginItem({ + Key? key, + required this.pluginId, + required this.peerId, + required this.ffi, + required this.location, + required this.pluginModel, + }) : super(key: key); + + bool get isEmpty => pluginModel.isEmpty; + + @override + Widget build(BuildContext context) { + return ChangeNotifierProvider.value( + value: pluginModel, + child: Consumer(builder: (context, model, child) { + return Column( + children: model.uiList.map((ui) => _buildItem(ui)).toList(), + ); + }), + ); + } + + // to-do: add plugin icon and tooltip Widget _buildItem(UiType ui) { switch (ui.runtimeType) { case UiButton: @@ -80,7 +131,9 @@ class Display extends StatelessWidget { ); }(); }, - // to-do: rustdesk translate or plugin translate ? + trailingIcon: Icon( + IconData(int.parse(ui.icon, radix: 16), fontFamily: 'MaterialIcons')), + // to-do: RustDesk translate or plugin translate ? child: Text(ui.text), ffi: ffi, ); @@ -89,6 +142,10 @@ class Display extends StatelessWidget { Widget _buildCheckboxMenuButton(UiCheckbox ui) { final v = bind.pluginGetSessionOption(id: pluginId, peer: peerId, key: ui.key); + if (v == null) { + // session or plugin not found + return Container(); + } return CkbMenuButton( value: ConfigItem.isTrue(v), onChanged: (v) { @@ -108,3 +165,11 @@ class Display extends StatelessWidget { ); } } + +void handleReloading(Map evt, String peer) { + if (evt['id'] == null || evt['location'] == null) { + return; + } + final ui = UiType.fromJson(evt); + addLocationUi(evt['location']!, evt['id']!, ui); +} diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index f44f62514..f4895c785 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -8,6 +8,8 @@ import 'package:flutter_hbb/models/chat_model.dart'; import 'package:flutter_hbb/models/state_model.dart'; import 'package:flutter_hbb/consts.dart'; import 'package:flutter_hbb/utils/multi_window_manager.dart'; +import 'package:flutter_hbb/desktop/plugin/widget.dart'; +import 'package:flutter_hbb/desktop/plugin/common.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:get/get.dart'; import 'package:provider/provider.dart'; @@ -655,13 +657,19 @@ class _DisplayMenu extends StatefulWidget { final FFI ffi; final MenubarState state; final Function(bool) setFullscreen; + final LocationItem pluginItem; _DisplayMenu( {Key? key, required this.id, required this.ffi, required this.state, required this.setFullscreen}) - : super(key: key); + : pluginItem = LocationItem.createLocationItem( + id, + ffi, + kLocationClientRemoteToolbarDisplay, + ), + super(key: key); @override State<_DisplayMenu> createState() => _DisplayMenuState(); @@ -699,6 +707,7 @@ class _DisplayMenuState extends State<_DisplayMenu> { resolutions(), Divider(), toggles(), + widget.pluginItem, ]); } diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 44ca71912..406783c2e 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -16,9 +16,9 @@ import 'package:flutter_hbb/models/peer_tab_model.dart'; import 'package:flutter_hbb/models/server_model.dart'; import 'package:flutter_hbb/models/user_model.dart'; import 'package:flutter_hbb/models/state_model.dart'; -import 'package:flutter_hbb/plugin/event.dart'; -import 'package:flutter_hbb/plugin/desc.dart'; -import 'package:flutter_hbb/plugin/widget.dart'; +import 'package:flutter_hbb/desktop/plugin/event.dart'; +import 'package:flutter_hbb/desktop/plugin/desc.dart'; +import 'package:flutter_hbb/desktop/plugin/widget.dart'; import 'package:flutter_hbb/common/shared_state.dart'; import 'package:tuple/tuple.dart'; import 'package:image/image.dart' as img2; diff --git a/flutter/lib/plugin/model.dart b/flutter/lib/plugin/model.dart deleted file mode 100644 index ac1b74828..000000000 --- a/flutter/lib/plugin/model.dart +++ /dev/null @@ -1,30 +0,0 @@ -import 'package:flutter/material.dart'; -import './common.dart'; -import './desc.dart'; - -// ui location -// host|main|settings|display|others -// client|remote|toolbar|display - -final Map> locationModels = {}; - -class LocationModel with ChangeNotifier { - final List uiList = []; - - void add(UiType ui) { - uiList.add(ui); - notifyListeners(); - } - - bool get isEmpty => uiList.isEmpty; -} - -void addLocation(PluginId id, String location, UiType ui) { - if (!locationModels.containsKey(id)) { - locationModels[id] = {}; - } - if (!locationModels[id]!.containsKey(location)) { - locationModels[id]![location] = LocationModel(); - } - locationModels[id]![location]!.add(ui); -} diff --git a/flutter/lib/plugin/widget.dart b/flutter/lib/plugin/widget.dart deleted file mode 100644 index fc110077c..000000000 --- a/flutter/lib/plugin/widget.dart +++ /dev/null @@ -1,44 +0,0 @@ -import 'package:flutter/material.dart'; -import './desc.dart'; -import './model.dart'; - -final Map pluginWidgets = {}; - -class PluginWidget { - final String id; - final String name; - final String location; - final Widget widget; - - PluginWidget({ - required this.id, - required this.name, - required this.location, - required this.widget, - }); - - // static Widget createButton(UiButton btn) {} - - // static Widget createCheckbox(UiCheckbox chk) {} - - // // ui location - // // host|main|settings|display|others - // // client|remote|toolbar|display - // static Widget? create(String id, String locatin, UiType ui) { - // if (ui.button != null) { - // return createButton(ui.button!); - // } else if (ui.checkbox != null) { - // return createCheckbox(ui.checkbox!); - // } else { - // return null; - // } - // } -} - -void handleReloading(Map evt, String peer) { - if (evt['id'] == null || evt['location'] == null) { - return; - } - final ui = UiType.fromJson(evt); - addLocation(evt['id']!, evt['location']!, ui); -} diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index 62db17151..b5cc669ab 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -1406,7 +1406,7 @@ pub fn plugin_event(_id: String, _event: Vec) { } #[inline] -pub fn plugin_get_session_option(_id: String, _peer: String, _key: String) -> SyncReturn { +pub fn plugin_get_session_option(_id: String, _peer: String, _key: String) -> SyncReturn> { #[cfg(feature = "plugin_framework")] #[cfg(not(any(target_os = "android", target_os = "ios")))] { @@ -1417,7 +1417,9 @@ pub fn plugin_get_session_option(_id: String, _peer: String, _key: String) -> Sy target_os = "android", target_os = "ios" ))] - return SyncReturn("".to_owned()); + { + return SyncReturn(None); + } } #[inline] @@ -1430,18 +1432,20 @@ pub fn plugin_set_session_option(_id: String, _peer: String, _key: String, _valu } #[inline] -pub fn plugin_get_local_option(_id: String, _key: String) -> SyncReturn { +pub fn plugin_get_local_option(_id: String, _key: String) -> SyncReturn> { #[cfg(feature = "plugin_framework")] #[cfg(not(any(target_os = "android", target_os = "ios")))] { - allow_err!(crate::plugin::LocalConfig::get(&_id, &key)); + return SyncReturn(crate::plugin::LocalConfig::get(&_id, &_key)); } #[cfg(any( not(feature = "plugin_framework"), target_os = "android", target_os = "ios" ))] - return SyncReturn("".to_owned()); + { + return SyncReturn(None); + } } #[inline] diff --git a/src/plugin/desc.rs b/src/plugin/desc.rs index bc094abf9..94a137570 100644 --- a/src/plugin/desc.rs +++ b/src/plugin/desc.rs @@ -8,7 +8,7 @@ use std::ffi::{c_char, CStr}; pub struct UiButton { key: String, text: String, - icon: String, + icon: String, // icon can be int in flutter, but string in other ui framework. And it is flexible to use string. tooltip: String, action: String, // The action to be triggered when the button is clicked. }