flutter_desktop: check remote menu, mid commit
Signed-off-by: fufesou <shuanglongchen@yeah.net>
This commit is contained in:
parent
f1bbe9ca5e
commit
21b277ea3f
@ -1,7 +1,6 @@
|
||||
import 'package:contextmenu/contextmenu.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_hbb/utils/multi_window_manager.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '../../common.dart';
|
||||
@ -11,6 +10,7 @@ import '../../models/peer_model.dart';
|
||||
import '../../models/platform_model.dart';
|
||||
import './material_mod_popup_menu.dart' as mod_menu;
|
||||
import './popup_menu.dart';
|
||||
import './utils.dart';
|
||||
|
||||
class _PopupMenuTheme {
|
||||
static const Color commonColor = MyTheme.accent;
|
||||
@ -32,7 +32,7 @@ class _PeerCard extends StatefulWidget {
|
||||
final Function(BuildContext, String) connect;
|
||||
final PopupMenuEntryBuilder popupMenuEntryBuilder;
|
||||
|
||||
_PeerCard(
|
||||
const _PeerCard(
|
||||
{required this.peer,
|
||||
required this.alias,
|
||||
required this.connect,
|
||||
@ -317,7 +317,7 @@ abstract class BasePeerCard extends StatelessWidget {
|
||||
return _PeerCard(
|
||||
peer: peer,
|
||||
alias: alias,
|
||||
connect: (BuildContext context, String id) => _connect(context, id),
|
||||
connect: (BuildContext context, String id) => connect(context, id),
|
||||
popupMenuEntryBuilder: _buildPopupMenuEntry,
|
||||
);
|
||||
}
|
||||
@ -337,31 +337,6 @@ abstract class BasePeerCard extends StatelessWidget {
|
||||
@protected
|
||||
Future<List<MenuEntryBase<String>>> _buildMenuItems(BuildContext context);
|
||||
|
||||
/// Connect to a peer with [id].
|
||||
/// If [isFileTransfer], starts a session only for file transfer.
|
||||
/// If [isTcpTunneling], starts a session only for tcp tunneling.
|
||||
/// If [isRDP], starts a session only for rdp.
|
||||
void _connect(BuildContext context, String id,
|
||||
{bool isFileTransfer = false,
|
||||
bool isTcpTunneling = false,
|
||||
bool isRDP = false}) async {
|
||||
if (id == '') return;
|
||||
id = id.replaceAll(' ', '');
|
||||
assert(!(isFileTransfer && isTcpTunneling && isRDP),
|
||||
"more than one connect type");
|
||||
if (isFileTransfer) {
|
||||
await rustDeskWinManager.newFileTransfer(id);
|
||||
} else if (isTcpTunneling || isRDP) {
|
||||
await rustDeskWinManager.newPortForward(id, isRDP);
|
||||
} else {
|
||||
await rustDeskWinManager.newRemoteDesktop(id);
|
||||
}
|
||||
FocusScopeNode currentFocus = FocusScope.of(context);
|
||||
if (!currentFocus.hasPrimaryFocus) {
|
||||
currentFocus.unfocus();
|
||||
}
|
||||
}
|
||||
|
||||
MenuEntryBase<String> _connectCommonAction(
|
||||
BuildContext context, String id, String title,
|
||||
{bool isFileTransfer = false,
|
||||
@ -373,7 +348,7 @@ abstract class BasePeerCard extends StatelessWidget {
|
||||
style: style,
|
||||
),
|
||||
proc: () {
|
||||
_connect(
|
||||
connect(
|
||||
context,
|
||||
peer.id,
|
||||
isFileTransfer: isFileTransfer,
|
||||
@ -434,7 +409,7 @@ abstract class BasePeerCard extends StatelessWidget {
|
||||
],
|
||||
)),
|
||||
proc: () {
|
||||
_connect(context, id, isRDP: true);
|
||||
connect(context, id, isRDP: true);
|
||||
},
|
||||
dismissOnClicked: true,
|
||||
);
|
||||
|
@ -11,6 +11,7 @@ import '../../models/platform_model.dart';
|
||||
import '../../common/shared_state.dart';
|
||||
import './popup_menu.dart';
|
||||
import './material_mod_popup_menu.dart' as mod_menu;
|
||||
import './utils.dart';
|
||||
|
||||
class _MenubarTheme {
|
||||
static const Color commonColor = MyTheme.accent;
|
||||
@ -225,7 +226,7 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
||||
),
|
||||
tooltip: translate('Control Actions'),
|
||||
position: mod_menu.PopupMenuPosition.under,
|
||||
itemBuilder: (BuildContext context) => _getControlMenu()
|
||||
itemBuilder: (BuildContext context) => _getControlMenu(context)
|
||||
.map((entry) => entry.build(
|
||||
context,
|
||||
const MenuConfig(
|
||||
@ -297,66 +298,76 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
||||
);
|
||||
}
|
||||
|
||||
List<MenuEntryBase<String>> _getControlMenu() {
|
||||
List<MenuEntryBase<String>> _getControlMenu(BuildContext context) {
|
||||
final pi = widget.ffi.ffiModel.pi;
|
||||
final perms = widget.ffi.ffiModel.permissions;
|
||||
|
||||
final List<MenuEntryBase<String>> displayMenu = [];
|
||||
|
||||
if (pi.version.isNotEmpty) {
|
||||
displayMenu.add(MenuEntryButton<String>(
|
||||
childBuilder: (TextStyle? style) => Text(
|
||||
translate('Refresh'),
|
||||
style: style,
|
||||
),
|
||||
proc: () {
|
||||
bind.sessionRefresh(id: widget.id);
|
||||
},
|
||||
dismissOnClicked: true,
|
||||
));
|
||||
}
|
||||
displayMenu.add(MenuEntryButton<String>(
|
||||
childBuilder: (TextStyle? style) => Text(
|
||||
displayMenu.addAll([
|
||||
MenuEntryButton<String>(
|
||||
childBuilder: (TextStyle? style) => Row(
|
||||
children: [
|
||||
Text(
|
||||
translate('OS Password'),
|
||||
style: style,
|
||||
),
|
||||
Expanded(
|
||||
child: Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: IconButton(
|
||||
padding: EdgeInsets.zero,
|
||||
icon: const Icon(Icons.edit),
|
||||
onPressed: () => showSetOSPassword(
|
||||
widget.id, false, widget.ffi.dialogManager),
|
||||
),
|
||||
))
|
||||
],
|
||||
),
|
||||
proc: () {
|
||||
showSetOSPassword(widget.id, false, widget.ffi.dialogManager);
|
||||
},
|
||||
dismissOnClicked: true,
|
||||
));
|
||||
|
||||
if (!isWebDesktop) {
|
||||
if (perms['keyboard'] != false && perms['clipboard'] != false) {
|
||||
displayMenu.add(MenuEntryButton<String>(
|
||||
),
|
||||
MenuEntryButton<String>(
|
||||
childBuilder: (TextStyle? style) => Text(
|
||||
translate('Paste'),
|
||||
translate('Transfer File'),
|
||||
style: style,
|
||||
),
|
||||
proc: () {
|
||||
() async {
|
||||
ClipboardData? data =
|
||||
await Clipboard.getData(Clipboard.kTextPlain);
|
||||
if (data != null && data.text != null) {
|
||||
bind.sessionInputString(id: widget.id, value: data.text ?? "");
|
||||
}
|
||||
}();
|
||||
connect(context, widget.id, isFileTransfer: true);
|
||||
},
|
||||
dismissOnClicked: true,
|
||||
));
|
||||
}
|
||||
|
||||
displayMenu.add(MenuEntryButton<String>(
|
||||
),
|
||||
MenuEntryButton<String>(
|
||||
childBuilder: (TextStyle? style) => Text(
|
||||
translate('Reset canvas'),
|
||||
translate('TCP Tunneling'),
|
||||
style: style,
|
||||
),
|
||||
proc: () {
|
||||
widget.ffi.cursorModel.reset();
|
||||
connect(context, widget.id, isTcpTunneling: true);
|
||||
},
|
||||
dismissOnClicked: true,
|
||||
));
|
||||
}
|
||||
),
|
||||
]);
|
||||
|
||||
// {handler.get_audit_server() && <li #note>{translate('Note')}</li>}
|
||||
final auditServer = bind.sessionGetAuditServerSync(id: widget.id);
|
||||
//if (auditServer.isNotEmpty) {
|
||||
displayMenu.add(
|
||||
MenuEntryButton<String>(
|
||||
childBuilder: (TextStyle? style) => Text(
|
||||
translate('Note'),
|
||||
style: style,
|
||||
),
|
||||
proc: () {
|
||||
showAuditDialog(widget.id, widget.ffi.dialogManager);
|
||||
},
|
||||
dismissOnClicked: true,
|
||||
),
|
||||
);
|
||||
//}
|
||||
|
||||
displayMenu.add(MenuEntryDivider());
|
||||
|
||||
if (perms['keyboard'] != false) {
|
||||
if (pi.platform == 'Linux' || pi.sasEnabled) {
|
||||
@ -371,7 +382,24 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
||||
dismissOnClicked: true,
|
||||
));
|
||||
}
|
||||
}
|
||||
if (gFFI.ffiModel.permissions["restart"] != false &&
|
||||
(pi.platform == "Linux" ||
|
||||
pi.platform == "Windows" ||
|
||||
pi.platform == "Mac OS")) {
|
||||
displayMenu.add(MenuEntryButton<String>(
|
||||
childBuilder: (TextStyle? style) => Text(
|
||||
translate('Restart Remote Device'),
|
||||
style: style,
|
||||
),
|
||||
proc: () {
|
||||
showRestartRemoteDevice(pi, widget.id, gFFI.dialogManager);
|
||||
},
|
||||
dismissOnClicked: true,
|
||||
));
|
||||
}
|
||||
|
||||
if (perms['keyboard'] != false) {
|
||||
displayMenu.add(MenuEntryButton<String>(
|
||||
childBuilder: (TextStyle? style) => Text(
|
||||
translate('Insert Lock'),
|
||||
@ -402,17 +430,46 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
||||
}
|
||||
}
|
||||
|
||||
if (gFFI.ffiModel.permissions["restart"] != false &&
|
||||
(pi.platform == "Linux" ||
|
||||
pi.platform == "Windows" ||
|
||||
pi.platform == "Mac OS")) {
|
||||
if (pi.version.isNotEmpty) {
|
||||
displayMenu.add(MenuEntryButton<String>(
|
||||
childBuilder: (TextStyle? style) => Text(
|
||||
translate('Restart Remote Device'),
|
||||
translate('Refresh'),
|
||||
style: style,
|
||||
),
|
||||
proc: () {
|
||||
showRestartRemoteDevice(pi, widget.id, gFFI.dialogManager);
|
||||
bind.sessionRefresh(id: widget.id);
|
||||
},
|
||||
dismissOnClicked: true,
|
||||
));
|
||||
}
|
||||
|
||||
if (!isWebDesktop) {
|
||||
// if (perms['keyboard'] != false && perms['clipboard'] != false) {
|
||||
// displayMenu.add(MenuEntryButton<String>(
|
||||
// childBuilder: (TextStyle? style) => Text(
|
||||
// translate('Paste'),
|
||||
// style: style,
|
||||
// ),
|
||||
// proc: () {
|
||||
// () async {
|
||||
// ClipboardData? data =
|
||||
// await Clipboard.getData(Clipboard.kTextPlain);
|
||||
// if (data != null && data.text != null) {
|
||||
// bind.sessionInputString(id: widget.id, value: data.text ?? "");
|
||||
// }
|
||||
// }();
|
||||
// },
|
||||
// dismissOnClicked: true,
|
||||
// ));
|
||||
// }
|
||||
|
||||
displayMenu.add(MenuEntryButton<String>(
|
||||
childBuilder: (TextStyle? style) => Text(
|
||||
translate('Reset canvas'),
|
||||
style: style,
|
||||
),
|
||||
proc: () {
|
||||
widget.ffi.cursorModel.reset();
|
||||
},
|
||||
dismissOnClicked: true,
|
||||
));
|
||||
@ -684,3 +741,76 @@ void showSetOSPassword(
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
void showAuditDialog(String id, dialogManager) async {
|
||||
final controller = TextEditingController();
|
||||
dialogManager.show((setState, close) {
|
||||
submit() {
|
||||
var text = controller.text.trim();
|
||||
if (text != "") {
|
||||
bind.sessionSendNote(id: id, note: text);
|
||||
}
|
||||
close();
|
||||
}
|
||||
|
||||
late final focusNode = FocusNode(
|
||||
onKey: (FocusNode node, RawKeyEvent evt) {
|
||||
if (evt.logicalKey.keyLabel == 'Enter') {
|
||||
if (evt is RawKeyDownEvent) {
|
||||
int pos = controller.selection.base.offset;
|
||||
controller.text =
|
||||
'${controller.text.substring(0, pos)}\n${controller.text.substring(pos)}';
|
||||
controller.selection =
|
||||
TextSelection.fromPosition(TextPosition(offset: pos + 1));
|
||||
}
|
||||
return KeyEventResult.handled;
|
||||
}
|
||||
if (evt.logicalKey.keyLabel == 'Esc') {
|
||||
if (evt is RawKeyDownEvent) {
|
||||
close();
|
||||
}
|
||||
return KeyEventResult.handled;
|
||||
} else {
|
||||
return KeyEventResult.ignored;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return CustomAlertDialog(
|
||||
title: Text(translate('Note')),
|
||||
content: SizedBox(
|
||||
width: 250,
|
||||
height: 120,
|
||||
child: TextField(
|
||||
autofocus: true,
|
||||
keyboardType: TextInputType.multiline,
|
||||
textInputAction: TextInputAction.newline,
|
||||
decoration: const InputDecoration.collapsed(
|
||||
hintText: "input note here",
|
||||
),
|
||||
// inputFormatters: [
|
||||
// LengthLimitingTextInputFormatter(16),
|
||||
// // FilteringTextInputFormatter(RegExp(r"[a-zA-z][a-zA-z0-9\_]*"), allow: true)
|
||||
// ],
|
||||
maxLines: null,
|
||||
maxLength: 256,
|
||||
controller: controller,
|
||||
focusNode: focusNode,
|
||||
)),
|
||||
actions: [
|
||||
TextButton(
|
||||
style: flatButtonStyle,
|
||||
onPressed: close,
|
||||
child: Text(translate('Cancel')),
|
||||
),
|
||||
TextButton(
|
||||
style: flatButtonStyle,
|
||||
onPressed: submit,
|
||||
child: Text(translate('OK')),
|
||||
),
|
||||
],
|
||||
onSubmit: submit,
|
||||
onCancel: close,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
28
flutter/lib/desktop/widgets/utils.dart
Normal file
28
flutter/lib/desktop/widgets/utils.dart
Normal file
@ -0,0 +1,28 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hbb/utils/multi_window_manager.dart';
|
||||
|
||||
/// Connect to a peer with [id].
|
||||
/// If [isFileTransfer], starts a session only for file transfer.
|
||||
/// If [isTcpTunneling], starts a session only for tcp tunneling.
|
||||
/// If [isRDP], starts a session only for rdp.
|
||||
void connect(BuildContext context, String id,
|
||||
{bool isFileTransfer = false,
|
||||
bool isTcpTunneling = false,
|
||||
bool isRDP = false}) async {
|
||||
if (id == '') return;
|
||||
id = id.replaceAll(' ', '');
|
||||
assert(!(isFileTransfer && isTcpTunneling && isRDP),
|
||||
"more than one connect type");
|
||||
|
||||
FocusScopeNode currentFocus = FocusScope.of(context);
|
||||
if (isFileTransfer) {
|
||||
await rustDeskWinManager.newFileTransfer(id);
|
||||
} else if (isTcpTunneling || isRDP) {
|
||||
await rustDeskWinManager.newPortForward(id, isRDP);
|
||||
} else {
|
||||
await rustDeskWinManager.newRemoteDesktop(id);
|
||||
}
|
||||
if (!currentFocus.hasPrimaryFocus) {
|
||||
currentFocus.unfocus();
|
||||
}
|
||||
}
|
@ -17,15 +17,14 @@ use crate::flutter::{self, SESSIONS};
|
||||
use crate::start_server;
|
||||
use crate::ui_interface;
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
use crate::ui_interface::change_id;
|
||||
use crate::ui_interface::{
|
||||
check_mouse_time, check_super_user_permission, discover, forget_password, get_api_server,
|
||||
get_app_name, get_async_job_status, get_connect_status, get_fav, get_id, get_lan_peers,
|
||||
get_langs, get_license, get_local_option, get_mouse_time, get_option, get_options, get_peer,
|
||||
get_peer_option, get_socks, get_sound_inputs, get_uuid, get_version, has_hwcodec,
|
||||
has_rendezvous_service, post_request, send_to_cm, set_local_option, set_option, set_options,
|
||||
set_peer_option, set_permanent_password, set_socks, store_fav, test_if_valid_server,
|
||||
update_temporary_password, using_public_server,
|
||||
change_id, check_mouse_time, check_super_user_permission, discover, forget_password,
|
||||
get_api_server, get_app_name, get_async_job_status, get_connect_status, get_fav, get_id,
|
||||
get_lan_peers, get_langs, get_license, get_local_option, get_mouse_time, get_option,
|
||||
get_options, get_peer, get_peer_option, get_socks, get_sound_inputs, get_uuid, get_version,
|
||||
has_hwcodec, has_rendezvous_service, post_request, send_to_cm, set_local_option, set_option,
|
||||
set_options, set_peer_option, set_permanent_password, set_socks, store_fav,
|
||||
test_if_valid_server, update_temporary_password, using_public_server,
|
||||
};
|
||||
use crate::{
|
||||
client::file_trait::FileManager,
|
||||
@ -810,6 +809,21 @@ pub fn session_restart_remote_device(id: String) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn session_get_audit_server_sync(id: String) -> SyncReturn<String> {
|
||||
let res = if let Some(session) = SESSIONS.read().unwrap().get(&id) {
|
||||
session.get_audit_server()
|
||||
} else {
|
||||
"".to_owned()
|
||||
};
|
||||
SyncReturn(res)
|
||||
}
|
||||
|
||||
pub fn session_send_note(id: String, note: String) {
|
||||
if let Some(session) = SESSIONS.read().unwrap().get(&id) {
|
||||
session.send_note(note)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main_set_home_dir(home: String) {
|
||||
*config::APP_HOME_DIR.write().unwrap() = home;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user