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. }