Merge pull request #4301 from Kingtous/master

feat: add entry in left panel and dialog callback
This commit is contained in:
RustDesk 2023-05-08 16:12:02 +08:00 committed by GitHub
commit c465c16f77
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
57 changed files with 878 additions and 554 deletions

View File

@ -557,7 +557,7 @@ void window_on_top(int? id) {
}
typedef DialogBuilder = CustomAlertDialog Function(
StateSetter setState, void Function([dynamic]) close);
StateSetter setState, void Function([dynamic]) close, BuildContext context);
class Dialog<T> {
OverlayEntry? entry;
@ -660,7 +660,7 @@ class OverlayDialogManager {
child: StatefulBuilder(builder: (context, setState) {
return Listener(
onPointerUp: (_) => innerClicked = true,
child: builder(setState, close),
child: builder(setState, close, overlayState.context),
);
})));
});
@ -680,7 +680,7 @@ class OverlayDialogManager {
VoidCallback? onCancel}) {
final tag = _tagCount.toString();
_tagCount++;
show((setState, close) {
show((setState, close, context) {
cancel() {
dismissAll();
if (onCancel != null) {
@ -938,7 +938,7 @@ void msgBox(String id, String type, String title, String text, String link,
buttons.insert(0, dialogButton('JumpLink', onPressed: jumplink));
}
dialogManager.show(
(setState, close) => CustomAlertDialog(
(setState, close, context) => CustomAlertDialog(
title: null,
content: SelectionArea(child: msgboxContent(type, title, text)),
actions: buttons,
@ -1011,7 +1011,7 @@ Widget msgboxContent(String type, String title, String text) {
void msgBoxCommon(OverlayDialogManager dialogManager, String title,
Widget content, List<Widget> buttons,
{bool hasCancel = true}) {
dialogManager.show((setState, close) => CustomAlertDialog(
dialogManager.show((setState, close, context) => CustomAlertDialog(
title: Text(
translate(title),
style: TextStyle(fontSize: 21),

View File

@ -224,7 +224,7 @@ class _AddressBookState extends State<AddressBook> {
final style = TextStyle(fontSize: 14.0);
String? errorMsg;
gFFI.dialogManager.show((setState, close) {
gFFI.dialogManager.show((setState, close, context) {
submit() async {
setState(() {
isInProgress = true;
@ -334,7 +334,7 @@ class _AddressBookState extends State<AddressBook> {
var msg = "";
var isInProgress = false;
TextEditingController controller = TextEditingController(text: field);
gFFI.dialogManager.show((setState, close) {
gFFI.dialogManager.show((setState, close, context) {
submit() async {
setState(() {
msg = "";

View File

@ -65,7 +65,7 @@ void changeIdDialog() {
RegexValidationRule('allowed characters', RegExp(r'^\w*$'))
];
gFFI.dialogManager.show((setState, close) {
gFFI.dialogManager.show((setState, close, context) {
submit() async {
debugPrint("onSubmit");
newId = controller.text.trim();
@ -175,7 +175,7 @@ void changeWhiteList({Function()? callback}) async {
var controller = TextEditingController(text: newWhiteListField);
var msg = "";
var isInProgress = false;
gFFI.dialogManager.show((setState, close) {
gFFI.dialogManager.show((setState, close, context) {
return CustomAlertDialog(
title: Text(translate("IP Whitelisting")),
content: Column(
@ -255,7 +255,7 @@ void changeWhiteList({Function()? callback}) async {
Future<String> changeDirectAccessPort(
String currentIP, String currentPort) async {
final controller = TextEditingController(text: currentPort);
await gFFI.dialogManager.show((setState, close) {
await gFFI.dialogManager.show((setState, close, context) {
return CustomAlertDialog(
title: Text(translate("Change Local Port")),
content: Column(
@ -425,7 +425,7 @@ class _PasswordWidgetState extends State<PasswordWidget> {
void wrongPasswordDialog(
String id, OverlayDialogManager dialogManager, type, title, text) {
dialogManager.dismissAll();
dialogManager.show((setState, close) {
dialogManager.show((setState, close, context) {
cancel() {
close();
closeConnection();
@ -498,7 +498,7 @@ _connectDialog(
rememberAccount = await bind.sessionGetRemember(id: id) ?? false;
}
dialogManager.dismissAll();
dialogManager.show((setState, close) {
dialogManager.show((setState, close, context) {
cancel() {
close();
closeConnection();
@ -653,7 +653,7 @@ void showWaitUacDialog(
dialogManager.dismissAll();
dialogManager.show(
tag: '$id-wait-uac',
(setState, close) => CustomAlertDialog(
(setState, close, context) => CustomAlertDialog(
title: null,
content: msgboxContent(type, 'Wait', 'wait_accept_uac_tip'),
));
@ -769,7 +769,7 @@ void showRequestElevationDialog(String id, OverlayDialogManager dialogManager) {
]));
dialogManager.dismissAll();
dialogManager.show(tag: '$id-request-elevation', (setState, close) {
dialogManager.show(tag: '$id-request-elevation', (setState, close, context) {
void submit() {
if (groupValue.value == 'logon') {
if (userController.text.isEmpty) {
@ -813,7 +813,7 @@ void showOnBlockDialog(
dialogManager.existing('$id-request-elevation')) {
return;
}
dialogManager.show(tag: '$id-$type', (setState, close) {
dialogManager.show(tag: '$id-$type', (setState, close, context) {
void submit() {
close();
showRequestElevationDialog(id, dialogManager);
@ -835,7 +835,7 @@ void showOnBlockDialog(
void showElevationError(String id, String type, String title, String text,
OverlayDialogManager dialogManager) {
dialogManager.show(tag: '$id-$type', (setState, close) {
dialogManager.show(tag: '$id-$type', (setState, close, context) {
void submit() {
close();
showRequestElevationDialog(id, dialogManager);
@ -859,7 +859,7 @@ void showElevationError(String id, String type, String title, String text,
void showWaitAcceptDialog(String id, String type, String title, String text,
OverlayDialogManager dialogManager) {
dialogManager.dismissAll();
dialogManager.show((setState, close) {
dialogManager.show((setState, close, context) {
onCancel() {
closeConnection();
}
@ -878,7 +878,7 @@ void showWaitAcceptDialog(String id, String type, String title, String text,
void showRestartRemoteDevice(
PeerInfo pi, String id, OverlayDialogManager dialogManager) async {
final res =
await dialogManager.show<bool>((setState, close) => CustomAlertDialog(
await dialogManager.show<bool>((setState, close, context) => CustomAlertDialog(
title: Row(children: [
Icon(Icons.warning_rounded, color: Colors.redAccent, size: 28),
Flexible(
@ -915,7 +915,7 @@ showSetOSPassword(
var password = await bind.sessionGetOption(id: id, arg: 'os-password') ?? '';
var autoLogin = await bind.sessionGetOption(id: id, arg: 'auto-login') != '';
controller.text = password;
dialogManager.show((setState, close) {
dialogManager.show((setState, close, context) {
submit() {
var text = controller.text.trim();
bind.sessionPeerOption(id: id, name: 'os-password', value: text);
@ -983,7 +983,7 @@ showSetOSAccount(
var password = await bind.sessionGetOption(id: id, arg: 'os-password') ?? '';
usernameController.text = username;
passwdController.text = password;
dialogManager.show((setState, close) {
dialogManager.show((setState, close, context) {
submit() {
final username = usernameController.text.trim();
final password = usernameController.text.trim();
@ -1115,7 +1115,7 @@ showAuditDialog(String id, dialogManager) async {
void showConfirmSwitchSidesDialog(
String id, OverlayDialogManager dialogManager) async {
dialogManager.show((setState, close) {
dialogManager.show((setState, close, context) {
submit() async {
await bind.sessionSwitchSides(id: id);
closeConnection(id: id);

View File

@ -391,7 +391,7 @@ Future<bool?> loginDialog() async {
final autoLogin = true.obs;
final RxString curOP = ''.obs;
final res = await gFFI.dialogManager.show<bool>((setState, close) {
final res = await gFFI.dialogManager.show<bool>((setState, close, context) {
username.addListener(() {
if (usernameMsg != null) {
setState(() => usernameMsg = null);
@ -530,7 +530,7 @@ Future<bool?> verificationCodeDialog(UserPayload? user) async {
final focusNode = FocusNode()..requestFocus();
Timer(Duration(milliseconds: 100), () => focusNode..requestFocus());
final res = await gFFI.dialogManager.show<bool>((setState, close) {
final res = await gFFI.dialogManager.show<bool>((setState, close, context) {
bool validate() {
return code.text.length >= 6;
}

View File

@ -664,7 +664,7 @@ abstract class BasePeerCard extends StatelessWidget {
RxBool isInProgress = false.obs;
String name = await _getAlias(id);
var controller = TextEditingController(text: name);
gFFI.dialogManager.show((setState, close) {
gFFI.dialogManager.show((setState, close, context) {
submit() async {
isInProgress.value = true;
String name = controller.text.trim();
@ -724,7 +724,7 @@ abstract class BasePeerCard extends StatelessWidget {
void _delete(String id, bool isLan, Function reloadFunc) async {
gFFI.dialogManager.show(
(setState, close) {
(setState, close, context) {
submit() async {
if (isLan) {
bind.mainRemoveDiscovered(id: id);
@ -1023,7 +1023,7 @@ class AddressBookPeerCard extends BasePeerCard {
final tags = List.of(gFFI.abModel.tags);
var selectedTag = gFFI.abModel.getPeerTags(id).obs;
gFFI.dialogManager.show((setState, close) {
gFFI.dialogManager.show((setState, close, context) {
submit() async {
setState(() {
isInProgress = true;
@ -1115,7 +1115,7 @@ void _rdpDialog(String id) async {
text: await bind.mainGetPeerOption(id: id, key: 'rdp_password'));
RxBool secure = true.obs;
gFFI.dialogManager.show((setState, close) {
gFFI.dialogManager.show((setState, close, context) {
submit() async {
String port = portController.text.trim();
String username = userController.text;

View File

@ -14,6 +14,7 @@ import 'package:flutter_hbb/desktop/pages/desktop_tab_page.dart';
import 'package:flutter_hbb/desktop/widgets/scroll_wrapper.dart';
import 'package:flutter_hbb/models/platform_model.dart';
import 'package:flutter_hbb/models/server_model.dart';
import 'package:flutter_hbb/plugin/ui_manager.dart';
import 'package:flutter_hbb/utils/multi_window_manager.dart';
import 'package:get/get.dart';
import 'package:provider/provider.dart';
@ -88,6 +89,7 @@ class _DesktopHomePageState extends State<DesktopHomePage>
}
},
),
buildPluginEntry()
],
),
),
@ -572,6 +574,22 @@ class _DesktopHomePageState extends State<DesktopHomePage>
_updateTimer?.cancel();
super.dispose();
}
Widget buildPluginEntry() {
final entries = PluginUiManager.instance.entries.entries;
return Offstage(
offstage: entries.isEmpty,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
...
entries.map((entry) {
return entry.value;
})
],
),
);
}
}
void setPasswordDialog() async {
@ -589,7 +607,7 @@ void setPasswordDialog() async {
MinCharactersValidationRule(8),
];
gFFI.dialogManager.show((setState, close) {
gFFI.dialogManager.show((setState, close, context) {
submit() {
setState(() {
errMsg0 = "";

View File

@ -2016,7 +2016,7 @@ void changeSocks5Proxy() async {
RxBool obscure = true.obs;
var isInProgress = false;
gFFI.dialogManager.show((setState, close) {
gFFI.dialogManager.show((setState, close, context) {
submit() async {
setState(() {
proxyMsg = '';

View File

@ -643,7 +643,7 @@ class _FileManagerViewState extends State<FileManagerView> {
MenuButton(
onPressed: () {
final name = TextEditingController();
_ffi.dialogManager.show((setState, close) {
_ffi.dialogManager.show((setState, close, context) {
submit() {
if (name.value.text.isNotEmpty) {
controller.createDir(PathUtil.join(

View File

@ -293,7 +293,7 @@ class _InstallPageBodyState extends State<_InstallPageBody>
),
];
gFFI.dialogManager.show(
(setState, close) => CustomAlertDialog(
(setState, close, context) => CustomAlertDialog(
title: null,
content: SelectionArea(
child:

View File

@ -204,7 +204,7 @@ showKBLayoutTypeChooser(
String localPlatform,
OverlayDialogManager dialogManager,
) {
dialogManager.show((setState, close) {
dialogManager.show((setState, close, context) {
return CustomAlertDialog(
title:
Text('${translate('Select local keyboard type')} ($localPlatform)'),

View File

@ -687,7 +687,7 @@ Future<bool> toggleMaximize(bool isMainWindow) async {
Future<bool> closeConfirmDialog() async {
var confirm = true;
final res = await gFFI.dialogManager.show<bool>((setState, close) {
final res = await gFFI.dialogManager.show<bool>((setState, close, context) {
submit() {
final opt = "enable-confirm-closing-tabs";
String value = bool2option(opt, confirm);

View File

@ -197,7 +197,7 @@ class _FileManagerPageState extends State<FileManagerPage> {
} else if (v == "folder") {
final name = TextEditingController();
gFFI.dialogManager
.show((setState, close) => CustomAlertDialog(
.show((setState, close, context) => CustomAlertDialog(
title: Text(translate("Create Folder")),
content: Column(
mainAxisSize: MainAxisSize.min,

View File

@ -868,7 +868,7 @@ void showOptions(
List<TToggleMenu> displayToggles =
await toolbarDisplayToggle(context, id, gFFI);
dialogManager.show((setState, close) {
dialogManager.show((setState, close, context) {
var viewStyle =
(viewStyleRadios.isNotEmpty ? viewStyleRadios[0].groupValue : '').obs;
var imageQuality =

View File

@ -318,7 +318,7 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
kRequestIgnoreBatteryOptimizations);
} else {
final res = await gFFI.dialogManager
.show<bool>((setState, close) => CustomAlertDialog(
.show<bool>((setState, close, context) => CustomAlertDialog(
title: Text(translate("Open System Setting")),
content: Text(translate(
"android_open_battery_optimizations_tip")),
@ -505,7 +505,7 @@ void showLanguageSettings(OverlayDialogManager dialogManager) async {
try {
final langs = json.decode(await bind.mainGetLangs()) as List<dynamic>;
var lang = bind.mainGetLocalOption(key: "lang");
dialogManager.show((setState, close) {
dialogManager.show((setState, close, context) {
setLang(v) {
if (lang != v) {
setState(() {
@ -539,7 +539,7 @@ void showLanguageSettings(OverlayDialogManager dialogManager) async {
void showThemeSettings(OverlayDialogManager dialogManager) async {
var themeMode = MyTheme.getThemeModePreference();
dialogManager.show((setState, close) {
dialogManager.show((setState, close, context) {
setTheme(v) {
if (themeMode != v) {
setState(() {
@ -563,7 +563,7 @@ void showThemeSettings(OverlayDialogManager dialogManager) async {
}
void showAbout(OverlayDialogManager dialogManager) {
dialogManager.show((setState, close) {
dialogManager.show((setState, close, context) {
return CustomAlertDialog(
title: Text('${translate('About')} RustDesk'),
content: Wrap(direction: Axis.vertical, spacing: 12, children: [

View File

@ -20,7 +20,7 @@ void setPermanentPasswordDialog(OverlayDialogManager dialogManager) async {
final p1 = TextEditingController(text: pw);
var validateLength = false;
var validateSame = false;
dialogManager.show((setState, close) {
dialogManager.show((setState, close, context) {
submit() async {
close();
dialogManager.showLoading(translate("Waiting"));
@ -111,7 +111,7 @@ void setTemporaryPasswordLengthDialog(
var index = lengths.indexOf(length);
if (index < 0) index = 0;
length = lengths[index];
dialogManager.show((setState, close) {
dialogManager.show((setState, close, context) {
setLength(newValue) {
final oldValue = length;
if (oldValue == newValue) return;
@ -160,7 +160,7 @@ void showServerSettingsWithValue(
String? relayServerMsg;
String? apiServerMsg;
dialogManager.show((setState, close) {
dialogManager.show((setState, close, context) {
Future<bool> validate() async {
if (idCtrl.text != oldCfg.idServer) {
final res = await validateAsync(idCtrl.text);

View File

@ -152,7 +152,7 @@ class FileModel {
String title, String content, bool showCheckbox, bool isIdentical) async {
fileConfirmCheckboxRemember = false;
return await parent.target?.dialogManager.show<bool?>(
(setState, Function(bool? v) close) {
(setState, Function(bool? v) close, context) {
cancel() => close(false);
submit() => close(true);
return CustomAlertDialog(
@ -547,7 +547,7 @@ class FileController {
Future<bool?> showRemoveDialog(
String title, String content, bool showCheckbox) async {
return await dialogManager?.show<bool>((setState, Function(bool v) close) {
return await dialogManager?.show<bool>((setState, Function(bool v) close, context) {
cancel() => close(false);
submit() => close(true);
return CustomAlertDialog(

View File

@ -383,7 +383,7 @@ class FfiModel with ChangeNotifier {
void showRelayHintDialog(String id, String type, String title, String text,
OverlayDialogManager dialogManager) {
dialogManager.show(tag: '$id-$type', (setState, close) {
dialogManager.show(tag: '$id-$type', (setState, close, context) {
onClose() {
closeConnection();
close();

View File

@ -289,7 +289,7 @@ class ServerModel with ChangeNotifier {
toggleService() async {
if (_isStart) {
final res =
await parent.target?.dialogManager.show<bool>((setState, close) {
await parent.target?.dialogManager.show<bool>((setState, close, context) {
submit() => close(true);
return CustomAlertDialog(
title: Row(children: [
@ -312,7 +312,7 @@ class ServerModel with ChangeNotifier {
}
} else {
final res =
await parent.target?.dialogManager.show<bool>((setState, close) {
await parent.target?.dialogManager.show<bool>((setState, close, context) {
submit() => close(true);
return CustomAlertDialog(
title: Row(children: [
@ -481,7 +481,7 @@ class ServerModel with ChangeNotifier {
}
void showLoginDialog(Client client) {
parent.target?.dialogManager.show((setState, close) {
parent.target?.dialogManager.show((setState, close, context) {
cancel() {
sendLoginResponse(client, false);
close();
@ -699,7 +699,7 @@ String getLoginDialogTag(int id) {
}
showInputWarnAlert(FFI ffi) {
ffi.dialogManager.show((setState, close) {
ffi.dialogManager.show((setState, close, context) {
submit() {
AndroidPermissionManager.startAction(kActionAccessibilitySettings);
close();
@ -726,7 +726,7 @@ showInputWarnAlert(FFI ffi) {
}
Future<void> showClientsMayNotBeChangedAlert(FFI? ffi) async {
await ffi?.dialogManager.show((setState, close) {
await ffi?.dialogManager.show((setState, close, context) {
return CustomAlertDialog(
title: Text(translate("Permissions")),
content: Column(

View File

@ -1,5 +1,11 @@
import 'dart:convert';
import 'dart:ffi';
import 'package:ffi/ffi.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hbb/plugin/ui_manager.dart';
import 'package:flutter_hbb/plugin/utils/dialogs.dart';
abstract class NativeHandler {
bool onEvent(Map<String, dynamic> evt);
}
@ -30,6 +36,15 @@ class NativeUiHandler extends NativeHandler {
final cbFuncDart = cbFuncNative.asFunction<OnSelectPeersCallbackDart>();
onSelectPeers(cbFuncDart, userData);
break;
case "register_ui_entry":
int cb = evt['on_tap_cb'];
int userData = evt['user_data'] ?? 0;
String title = evt['title'] ?? "";
final cbFuncNative = Pointer.fromAddress(cb)
.cast<NativeFunction<OnSelectPeersCallback>>();
final cbFuncDart = cbFuncNative.asFunction<OnSelectPeersCallbackDart>();
onRegisterUiEntry(title, cbFuncDart, userData);
break;
default:
return false;
}
@ -37,7 +52,28 @@ class NativeUiHandler extends NativeHandler {
}
void onSelectPeers(OnSelectPeersCallbackDart cb, int userData) async {
// TODO: design a UI interface to pick peers.
cb(0, Pointer.fromAddress(0), 0, Pointer.fromAddress(userData));
showPeerSelectionDialog(onPeersCallback: (peers) {
String json = jsonEncode(<String, dynamic> {
"peers": peers
});
final native = json.toNativeUtf8();
cb(0, native.cast(), native.length, Pointer.fromAddress(userData));
malloc.free(native);
});
}
void onRegisterUiEntry(String title, OnSelectPeersCallbackDart cbFuncDart, int userData) {
Widget widget = InkWell(
child: Container(
height: 25.0,
child: Row(
children: [
Expanded(child: Text(title)),
Icon(Icons.chevron_right_rounded, size: 12.0,)
],
),
),
);
PluginUiManager.instance.registerEntry(title, widget);
}
}

View File

@ -0,0 +1,17 @@
import 'package:flutter/material.dart';
class PluginUiManager {
PluginUiManager._();
static PluginUiManager instance = PluginUiManager._();
Map<String, Widget> entries = <String, Widget>{};
void registerEntry(String key, Widget widget) {
entries[key] = widget;
}
void unregisterEntry(String key) {
entries.remove(key);
}
}

View File

@ -0,0 +1,83 @@
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hbb/common.dart';
import 'package:flutter_hbb/models/platform_model.dart';
import 'package:get/get.dart';
void showPeerSelectionDialog(
{bool singleSelection = false,
required Function(List<String>) onPeersCallback}) {
final peers = bind.mainLoadRecentPeersSync();
if (peers.isEmpty) {
debugPrint("load recent peers sync failed.");
return;
}
Map<String, dynamic> map = jsonDecode(peers);
List<dynamic> peersList = map['peers'] ?? [];
final selected = List<String>.empty(growable: true);
submit() async {
onPeersCallback.call(selected);
}
gFFI.dialogManager.show((setState, close, context) {
return CustomAlertDialog(
title:
Text(translate(singleSelection ? "Select peers" : "Select a peer")),
content: SizedBox(
height: 300.0,
child: ListView.builder(
itemBuilder: (context, index) {
final Map<String, dynamic> peer = peersList[index];
final String platform = peer['platform'] ?? "";
final String id = peer['id'] ?? "";
final String alias = peer['alias'] ?? "";
return GestureDetector(
onTap: () {
setState(() {
if (selected.contains(id)) {
selected.remove(id);
} else {
selected.add(id);
}
});
},
child: Container(
key: ValueKey(index),
height: 50.0,
decoration: BoxDecoration(
color: Theme.of(context).highlightColor,
borderRadius: BorderRadius.circular(12.0)
),
padding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 4.0),
margin: EdgeInsets.symmetric(vertical: 4.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: [
// platform
SizedBox(width: 8.0,),
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
getPlatformImage(platform, size: 34.0),
],
),
SizedBox(width: 8.0,),
// id/alias
Expanded(child: Text(alias.isEmpty ? id : alias)),
],
),
),
);
},
itemCount: peersList.length,
itemExtent: 50.0,
),
),
onSubmit: submit,
);
});
}

View File

@ -751,6 +751,25 @@ pub fn main_load_recent_peers() {
}
}
pub fn main_load_recent_peers_sync() -> SyncReturn<String> {
if !config::APP_DIR.read().unwrap().is_empty() {
let peers: Vec<HashMap<&str, String>> = PeerConfig::peers()
.drain(..)
.map(|(id, _, p)| peer_to_map(id, p))
.collect();
let data = HashMap::from([
("name", "load_recent_peers".to_owned()),
(
"peers",
serde_json::ser::to_string(&peers).unwrap_or("".to_owned()),
),
]);
return SyncReturn(serde_json::ser::to_string(&data).unwrap_or("".to_owned()));
}
SyncReturn("".to_string())
}
pub fn main_load_fav_peers() {
if !config::APP_DIR.read().unwrap().is_empty() {
let favs = get_fav();

View File

@ -498,5 +498,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Fingerprint", ""),
("Copy Fingerprint", ""),
("no fingerprints", ""),
("Select a peer", ""),
("Select peers", ""),
("Plugins", "")
].iter().cloned().collect();
}

View File

@ -498,5 +498,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Fingerprint", "指纹"),
("Copy Fingerprint", "复制指纹"),
("no fingerprints", "没有指纹"),
("Select a peer", "选择一个被控端"),
("Select peers", "选择被控"),
("Plugins", "插件")
].iter().cloned().collect();
}

View File

@ -498,5 +498,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Fingerprint", ""),
("Copy Fingerprint", ""),
("no fingerprints", ""),
("Select a peer", ""),
("Select peers", ""),
("Plugins", "")
].iter().cloned().collect();
}

View File

@ -498,5 +498,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Fingerprint", ""),
("Copy Fingerprint", ""),
("no fingerprints", ""),
("Select a peer", ""),
("Select peers", ""),
("Plugins", "")
].iter().cloned().collect();
}

View File

@ -498,5 +498,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Fingerprint", "Fingerabdruck"),
("Copy Fingerprint", "Fingerabdruck kopieren"),
("no fingerprints", "Keine Fingerabdrücke"),
("Select a peer", ""),
("Select peers", ""),
("Plugins", "")
].iter().cloned().collect();
}

View File

@ -498,5 +498,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Fingerprint", ""),
("Copy Fingerprint", ""),
("no fingerprints", ""),
("Select a peer", ""),
("Select peers", ""),
("Plugins", "")
].iter().cloned().collect();
}

View File

@ -498,5 +498,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Fingerprint", ""),
("Copy Fingerprint", ""),
("no fingerprints", ""),
("Select a peer", ""),
("Select peers", ""),
("Plugins", "")
].iter().cloned().collect();
}

View File

@ -498,5 +498,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Fingerprint", "Huella digital"),
("Copy Fingerprint", "Copiar huella digital"),
("no fingerprints", "sin huellas digitales"),
("Select a peer", ""),
("Select peers", ""),
("Plugins", "")
].iter().cloned().collect();
}

View File

@ -498,5 +498,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Fingerprint", "اثر انگشت"),
("Copy Fingerprint", "کپی کردن اثر انگشت"),
("no fingerprints", "بدون اثر انگشت"),
("Select a peer", ""),
("Select peers", ""),
("Plugins", "")
].iter().cloned().collect();
}

View File

@ -498,5 +498,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Fingerprint", ""),
("Copy Fingerprint", ""),
("no fingerprints", ""),
("Select a peer", ""),
("Select peers", ""),
("Plugins", "")
].iter().cloned().collect();
}

View File

@ -498,5 +498,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Fingerprint", ""),
("Copy Fingerprint", ""),
("no fingerprints", ""),
("Select a peer", ""),
("Select peers", ""),
("Plugins", "")
].iter().cloned().collect();
}

View File

@ -498,5 +498,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Fingerprint", ""),
("Copy Fingerprint", ""),
("no fingerprints", ""),
("Select a peer", ""),
("Select peers", ""),
("Plugins", "")
].iter().cloned().collect();
}

View File

@ -498,5 +498,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Fingerprint", "Firma digitale"),
("Copy Fingerprint", "Copia firma digitale"),
("no fingerprints", "Nessuna firma digitale"),
("Select a peer", ""),
("Select peers", ""),
("Plugins", "")
].iter().cloned().collect();
}

View File

@ -498,5 +498,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Fingerprint", ""),
("Copy Fingerprint", ""),
("no fingerprints", ""),
("Select a peer", ""),
("Select peers", ""),
("Plugins", "")
].iter().cloned().collect();
}

View File

@ -498,5 +498,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Fingerprint", ""),
("Copy Fingerprint", ""),
("no fingerprints", ""),
("Select a peer", ""),
("Select peers", ""),
("Plugins", "")
].iter().cloned().collect();
}

View File

@ -498,5 +498,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Fingerprint", ""),
("Copy Fingerprint", ""),
("no fingerprints", ""),
("Select a peer", ""),
("Select peers", ""),
("Plugins", "")
].iter().cloned().collect();
}

File diff suppressed because it is too large Load Diff

View File

@ -498,5 +498,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Fingerprint", "Vingerafdruk"),
("Copy Fingerprint", "Kopieer Vingerafdruk"),
("no fingerprints", "geen vingerafdrukken"),
("Select a peer", ""),
("Select peers", ""),
("Plugins", "")
].iter().cloned().collect();
}

View File

@ -498,5 +498,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Fingerprint", "Sygnatura"),
("Copy Fingerprint", "Skopiuj sygnaturę"),
("no fingerprints", "brak sygnatur"),
("Select a peer", ""),
("Select peers", ""),
("Plugins", "")
].iter().cloned().collect();
}

View File

@ -498,5 +498,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Fingerprint", ""),
("Copy Fingerprint", ""),
("no fingerprints", ""),
("Select a peer", ""),
("Select peers", ""),
("Plugins", "")
].iter().cloned().collect();
}

View File

@ -498,5 +498,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Fingerprint", ""),
("Copy Fingerprint", ""),
("no fingerprints", ""),
("Select a peer", ""),
("Select peers", ""),
("Plugins", "")
].iter().cloned().collect();
}

View File

@ -498,5 +498,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Fingerprint", ""),
("Copy Fingerprint", ""),
("no fingerprints", ""),
("Select a peer", ""),
("Select peers", ""),
("Plugins", "")
].iter().cloned().collect();
}

View File

@ -498,5 +498,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Fingerprint", "Отпечаток"),
("Copy Fingerprint", "Копировать отпечаток"),
("no fingerprints", "отпечатки отсутствуют"),
("Select a peer", ""),
("Select peers", ""),
("Plugins", "")
].iter().cloned().collect();
}

View File

@ -498,5 +498,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Fingerprint", ""),
("Copy Fingerprint", ""),
("no fingerprints", ""),
("Select a peer", ""),
("Select peers", ""),
("Plugins", "")
].iter().cloned().collect();
}

View File

@ -498,5 +498,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Fingerprint", ""),
("Copy Fingerprint", ""),
("no fingerprints", ""),
("Select a peer", ""),
("Select peers", ""),
("Plugins", "")
].iter().cloned().collect();
}

View File

@ -498,5 +498,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Fingerprint", ""),
("Copy Fingerprint", ""),
("no fingerprints", ""),
("Select a peer", ""),
("Select peers", ""),
("Plugins", "")
].iter().cloned().collect();
}

View File

@ -498,5 +498,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Fingerprint", ""),
("Copy Fingerprint", ""),
("no fingerprints", ""),
("Select a peer", ""),
("Select peers", ""),
("Plugins", "")
].iter().cloned().collect();
}

View File

@ -498,5 +498,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Fingerprint", ""),
("Copy Fingerprint", ""),
("no fingerprints", ""),
("Select a peer", ""),
("Select peers", ""),
("Plugins", "")
].iter().cloned().collect();
}

View File

@ -498,5 +498,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Fingerprint", ""),
("Copy Fingerprint", ""),
("no fingerprints", ""),
("Select a peer", ""),
("Select peers", ""),
("Plugins", "")
].iter().cloned().collect();
}

View File

@ -498,5 +498,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Fingerprint", ""),
("Copy Fingerprint", ""),
("no fingerprints", ""),
("Select a peer", ""),
("Select peers", ""),
("Plugins", "")
].iter().cloned().collect();
}

View File

@ -498,5 +498,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Fingerprint", ""),
("Copy Fingerprint", ""),
("no fingerprints", ""),
("Select a peer", ""),
("Select peers", ""),
("Plugins", "")
].iter().cloned().collect();
}

View File

@ -498,5 +498,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Fingerprint", "指紋"),
("Copy Fingerprint", "複製指紋"),
("no fingerprints", "沒有指紋"),
("Select a peer", ""),
("Select peers", ""),
("Plugins", "")
].iter().cloned().collect();
}

View File

@ -498,5 +498,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Fingerprint", ""),
("Copy Fingerprint", ""),
("no fingerprints", ""),
("Select a peer", ""),
("Select peers", ""),
("Plugins", "")
].iter().cloned().collect();
}

View File

@ -498,5 +498,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Fingerprint", ""),
("Copy Fingerprint", ""),
("no fingerprints", ""),
("Select a peer", ""),
("Select peers", ""),
("Plugins", "")
].iter().cloned().collect();
}

View File

@ -58,6 +58,33 @@ impl PluginNativeHandler for PluginNativeUIHandler {
data: "missing cb field message".as_ptr() as _,
});
}
"register_ui_entry" => {
let title;
if let Some(v) = data.get("title") {
title = v.as_str().unwrap_or("");
} else {
title = "";
}
if let Some(on_tap_cb) = data.get("on_tap_cb") {
if let Some(on_tap_cb) = on_tap_cb.as_u64() {
let user_data = match data.get("user_data") {
Some(user_data) => {
user_data.as_u64().unwrap_or(0)
},
None => 0,
};
self.register_ui_entry(title, on_tap_cb, user_data);
return Some(super::NR {
return_type: 0,
data: std::ptr::null(),
});
}
}
return Some(super::NR {
return_type: -1,
data: "missing cb field message".as_ptr() as _,
});
}
_ => {}
}
None
@ -97,4 +124,26 @@ impl PluginNativeUIHandler {
serde_json::to_string(&param).unwrap_or("".to_string()),
);
}
/// Call with method `register_ui_entry` and the following json:
/// ```
/// {
///
/// "on_tap_cb": 0, // The function address
/// "user_data": 0, // An opaque pointer value passed to the callback.
/// "title": "entry name"
/// }
/// ```
fn register_ui_entry(&self, title: &str, on_tap_cb: u64, user_data: u64) {
let mut param = HashMap::new();
param.insert("name", json!("native_ui"));
param.insert("action", json!("register_ui_entry"));
param.insert("title", json!(title));
param.insert("cb", json!(on_tap_cb));
param.insert("user_data", json!(user_data));
crate::flutter::push_global_event(
APP_TYPE_MAIN,
serde_json::to_string(&param).unwrap_or("".to_string()),
);
}
}