mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-11-04 00:33:12 +00:00
refactor: unify request approval menus and naming
This commit is contained in:
1 parent
417dcdf7bb
commit
c43443b1a1
67 files changed
+535
-559
No files matched your search
@@ -1,75 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../desktop/widgets/material_mod_popup_menu.dart' as mod_menu;
|
||||
import '../../desktop/widgets/popup_menu.dart';
|
||||
|
||||
class LoginMethodMenu extends MenuEntryBase<String> {
|
||||
final MenuEntryButton<String> defaultEntry;
|
||||
final List<MenuEntryButton<String>> methods;
|
||||
|
||||
LoginMethodMenu({required this.defaultEntry, required this.methods});
|
||||
|
||||
@override
|
||||
List<mod_menu.PopupMenuEntry<String>> build(
|
||||
BuildContext context, MenuConfig conf) {
|
||||
return [
|
||||
_LoginMethodMenuItem(
|
||||
height: conf.height,
|
||||
child: IntrinsicHeight(
|
||||
child:
|
||||
Row(crossAxisAlignment: CrossAxisAlignment.stretch, children: [
|
||||
Expanded(
|
||||
child: MenuEntryButton<String>(
|
||||
padding: defaultEntry.padding,
|
||||
enabled: defaultEntry.enabled,
|
||||
dismissOnClicked: true,
|
||||
proc: defaultEntry.proc,
|
||||
childBuilder: defaultEntry.childBuilder,
|
||||
).build(context, conf).single),
|
||||
PopupMenuChildrenItem<String>(
|
||||
height: conf.height,
|
||||
padding: EdgeInsets.zero,
|
||||
enabled: defaultEntry.enabled,
|
||||
itemBuilder: (submenuContext) => methods
|
||||
.expand((entry) => MenuEntryButton<String>(
|
||||
childBuilder: entry.childBuilder,
|
||||
proc: entry.proc,
|
||||
padding: entry.padding,
|
||||
enabled: entry.enabled,
|
||||
dismissOnClicked: true,
|
||||
dismissCallback: () {
|
||||
if (Navigator.canPop(context)) Navigator.pop(context);
|
||||
},
|
||||
).build(submenuContext, conf))
|
||||
.toList(),
|
||||
child: SizedBox(
|
||||
width: 56,
|
||||
child: Icon(Icons.keyboard_arrow_right, color: conf.commonColor),
|
||||
),
|
||||
),
|
||||
])),
|
||||
)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// PopupMenuItem adds a TextButton around its child; these actions need separate hit targets.
|
||||
class _LoginMethodMenuItem extends mod_menu.PopupMenuEntry<String> {
|
||||
const _LoginMethodMenuItem({required this.height, required this.child});
|
||||
|
||||
final Widget child;
|
||||
|
||||
@override
|
||||
final double height;
|
||||
|
||||
@override
|
||||
bool represents(String? value) => false;
|
||||
|
||||
@override
|
||||
State<_LoginMethodMenuItem> createState() => _LoginMethodMenuItemState();
|
||||
}
|
||||
|
||||
class _LoginMethodMenuItemState extends State<_LoginMethodMenuItem> {
|
||||
@override
|
||||
Widget build(BuildContext context) => widget.child;
|
||||
}
|
||||
@@ -15,7 +15,7 @@ import '../../models/peer_model.dart';
|
||||
import '../../models/platform_model.dart';
|
||||
import '../../desktop/widgets/material_mod_popup_menu.dart' as mod_menu;
|
||||
import '../../desktop/widgets/popup_menu.dart';
|
||||
import 'login_method_menu.dart';
|
||||
import 'request_approval_menu.dart';
|
||||
import 'dart:math' as math;
|
||||
|
||||
typedef PopupMenuEntryBuilder = Future<List<mod_menu.PopupMenuEntry<String>>>
|
||||
@@ -573,7 +573,36 @@ abstract class BasePeerCard extends StatelessWidget {
|
||||
@protected
|
||||
Future<List<MenuEntryBase<String>>> _buildMenuItems(BuildContext context);
|
||||
|
||||
MenuEntryBase<String> _connectCommonAction(
|
||||
@protected
|
||||
Future<List<MenuEntryBase<String>>> _buildConnectionMenuItems(
|
||||
BuildContext context) async {
|
||||
final List<MenuEntryBase<String>> menuItems = [
|
||||
_connectAction(context),
|
||||
_transferFileAction(context),
|
||||
_viewCameraAction(context),
|
||||
_terminalAction(context),
|
||||
];
|
||||
|
||||
if (peer.platform == kPeerPlatformWindows) {
|
||||
menuItems.add(_terminalRunAsAdminAction(context));
|
||||
}
|
||||
|
||||
if (isDesktop && peer.platform != kPeerPlatformAndroid) {
|
||||
menuItems.add(_tcpTunnelingAction(context));
|
||||
}
|
||||
if (isWindows && peer.platform == kPeerPlatformWindows) {
|
||||
menuItems.add(_rdpAction(context, peer.id));
|
||||
}
|
||||
menuItems.add(MenuEntryDivider<String>());
|
||||
menuItems.add(_requestApprovalAction(context));
|
||||
// menuItems.add(await _openNewConnInOptAction(peer.id));
|
||||
if (!isWeb) {
|
||||
menuItems.add(await _forceAlwaysRelayAction(peer.id));
|
||||
}
|
||||
return menuItems;
|
||||
}
|
||||
|
||||
MenuEntryButton<String> _connectCommonAction(
|
||||
BuildContext context,
|
||||
String title, {
|
||||
bool isFileTransfer = false,
|
||||
@@ -582,16 +611,10 @@ abstract class BasePeerCard extends StatelessWidget {
|
||||
bool isRDP = false,
|
||||
bool isTerminal = false,
|
||||
bool isTerminalRunAsAdmin = false,
|
||||
MenuEntryButton<String>? defaultEntry,
|
||||
String loginMethod = '',
|
||||
}) {
|
||||
action(String text, String loginMethod) => MenuEntryButton<String>(
|
||||
childBuilder: (TextStyle? style) => loginMethod == 'consent'
|
||||
? Tooltip(
|
||||
message: translate(
|
||||
'Skip saved passwords and ask the remote user to accept the connection.'),
|
||||
child: Text(text, style: style),
|
||||
)
|
||||
: Text(text, style: style),
|
||||
return MenuEntryButton<String>(
|
||||
childBuilder: (TextStyle? style) => Text(title, style: style),
|
||||
proc: () {
|
||||
if (isTerminalRunAsAdmin) {
|
||||
setEnvTerminalAdmin();
|
||||
@@ -611,10 +634,44 @@ abstract class BasePeerCard extends StatelessWidget {
|
||||
padding: menuPadding,
|
||||
dismissOnClicked: true,
|
||||
);
|
||||
return LoginMethodMenu(
|
||||
defaultEntry: defaultEntry ?? action(title, ''),
|
||||
methods: [
|
||||
action(translate('Without password'), 'consent'),
|
||||
}
|
||||
|
||||
MenuEntryBase<String> _requestApprovalAction(BuildContext context) {
|
||||
MenuEntryButton<String> action(String title,
|
||||
{bool isFileTransfer = false,
|
||||
bool isViewCamera = false,
|
||||
bool isTcpTunneling = false,
|
||||
bool isRDP = false,
|
||||
bool isTerminal = false,
|
||||
bool isTerminalRunAsAdmin = false}) =>
|
||||
_connectCommonAction(
|
||||
context,
|
||||
isTerminal || isTerminalRunAsAdmin
|
||||
? '${translate(title)} (beta)'
|
||||
: translate(title),
|
||||
isFileTransfer: isFileTransfer,
|
||||
isViewCamera: isViewCamera,
|
||||
isTcpTunneling: isTcpTunneling,
|
||||
isRDP: isRDP,
|
||||
isTerminal: isTerminal,
|
||||
isTerminalRunAsAdmin: isTerminalRunAsAdmin,
|
||||
loginMethod: 'request-approval',
|
||||
);
|
||||
|
||||
return RequestApprovalMenu(
|
||||
title: translate('Request approval'),
|
||||
padding: menuPadding,
|
||||
entries: [
|
||||
action('Connect'),
|
||||
action('Transfer file', isFileTransfer: true),
|
||||
action('View camera', isViewCamera: true),
|
||||
action('Terminal', isTerminal: true),
|
||||
if (peer.platform == kPeerPlatformWindows)
|
||||
action('Terminal (Run as administrator)', isTerminalRunAsAdmin: true),
|
||||
if (isDesktop && peer.platform != kPeerPlatformAndroid)
|
||||
action('TCP tunneling', isTcpTunneling: true),
|
||||
if (isWindows && peer.platform == kPeerPlatformWindows)
|
||||
action('RDP', isRDP: true),
|
||||
],
|
||||
);
|
||||
}
|
||||
@@ -676,7 +733,7 @@ abstract class BasePeerCard extends StatelessWidget {
|
||||
|
||||
@protected
|
||||
MenuEntryBase<String> _rdpAction(BuildContext context, String id) {
|
||||
final action = MenuEntryButton<String>(
|
||||
return MenuEntryButton<String>(
|
||||
childBuilder: (TextStyle? style) => Container(
|
||||
alignment: AlignmentDirectional.center,
|
||||
height: CustomPopupMenuTheme.height,
|
||||
@@ -710,8 +767,6 @@ abstract class BasePeerCard extends StatelessWidget {
|
||||
padding: menuPadding,
|
||||
dismissOnClicked: true,
|
||||
);
|
||||
return _connectCommonAction(context, translate('RDP'),
|
||||
isRDP: true, defaultEntry: action);
|
||||
}
|
||||
|
||||
@protected
|
||||
@@ -1014,33 +1069,13 @@ class RecentPeerCard extends BasePeerCard {
|
||||
@override
|
||||
Future<List<MenuEntryBase<String>>> _buildMenuItems(
|
||||
BuildContext context) async {
|
||||
final List<MenuEntryBase<String>> menuItems = [
|
||||
_connectAction(context),
|
||||
_transferFileAction(context),
|
||||
_viewCameraAction(context),
|
||||
_terminalAction(context),
|
||||
];
|
||||
|
||||
if (peer.platform == kPeerPlatformWindows) {
|
||||
menuItems.add(_terminalRunAsAdminAction(context));
|
||||
}
|
||||
|
||||
final List favs = (await bind.mainGetFav()).toList();
|
||||
|
||||
if (isDesktop && peer.platform != kPeerPlatformAndroid) {
|
||||
menuItems.add(_tcpTunnelingAction(context));
|
||||
}
|
||||
// menuItems.add(await _openNewConnInOptAction(peer.id));
|
||||
if (!isWeb) {
|
||||
menuItems.add(await _forceAlwaysRelayAction(peer.id));
|
||||
}
|
||||
if (isWindows && peer.platform == kPeerPlatformWindows) {
|
||||
menuItems.add(_rdpAction(context, peer.id));
|
||||
}
|
||||
final menuItems = await _buildConnectionMenuItems(context);
|
||||
menuItems.add(MenuEntryDivider());
|
||||
if (isWindows) {
|
||||
menuItems.add(_createShortCutAction(peer.id));
|
||||
}
|
||||
menuItems.add(MenuEntryDivider());
|
||||
if (isMobile || isDesktop || isWebDesktop) {
|
||||
menuItems.add(_renameAction(peer.id));
|
||||
}
|
||||
@@ -1079,31 +1114,11 @@ class FavoritePeerCard extends BasePeerCard {
|
||||
@override
|
||||
Future<List<MenuEntryBase<String>>> _buildMenuItems(
|
||||
BuildContext context) async {
|
||||
final List<MenuEntryBase<String>> menuItems = [
|
||||
_connectAction(context),
|
||||
_transferFileAction(context),
|
||||
_viewCameraAction(context),
|
||||
_terminalAction(context),
|
||||
];
|
||||
|
||||
if (peer.platform == kPeerPlatformWindows) {
|
||||
menuItems.add(_terminalRunAsAdminAction(context));
|
||||
}
|
||||
|
||||
if (isDesktop && peer.platform != kPeerPlatformAndroid) {
|
||||
menuItems.add(_tcpTunnelingAction(context));
|
||||
}
|
||||
// menuItems.add(await _openNewConnInOptAction(peer.id));
|
||||
if (!isWeb) {
|
||||
menuItems.add(await _forceAlwaysRelayAction(peer.id));
|
||||
}
|
||||
if (isWindows && peer.platform == kPeerPlatformWindows) {
|
||||
menuItems.add(_rdpAction(context, peer.id));
|
||||
}
|
||||
final menuItems = await _buildConnectionMenuItems(context);
|
||||
menuItems.add(MenuEntryDivider());
|
||||
if (isWindows) {
|
||||
menuItems.add(_createShortCutAction(peer.id));
|
||||
}
|
||||
menuItems.add(MenuEntryDivider());
|
||||
if (isMobile || isDesktop || isWebDesktop) {
|
||||
menuItems.add(_renameAction(peer.id));
|
||||
}
|
||||
@@ -1139,29 +1154,10 @@ class DiscoveredPeerCard extends BasePeerCard {
|
||||
@override
|
||||
Future<List<MenuEntryBase<String>>> _buildMenuItems(
|
||||
BuildContext context) async {
|
||||
final List<MenuEntryBase<String>> menuItems = [
|
||||
_connectAction(context),
|
||||
_transferFileAction(context),
|
||||
_viewCameraAction(context),
|
||||
_terminalAction(context),
|
||||
];
|
||||
|
||||
if (peer.platform == kPeerPlatformWindows) {
|
||||
menuItems.add(_terminalRunAsAdminAction(context));
|
||||
}
|
||||
|
||||
final List favs = (await bind.mainGetFav()).toList();
|
||||
|
||||
if (isDesktop && peer.platform != kPeerPlatformAndroid) {
|
||||
menuItems.add(_tcpTunnelingAction(context));
|
||||
}
|
||||
// menuItems.add(await _openNewConnInOptAction(peer.id));
|
||||
if (!isWeb) {
|
||||
menuItems.add(await _forceAlwaysRelayAction(peer.id));
|
||||
}
|
||||
if (isWindows && peer.platform == kPeerPlatformWindows) {
|
||||
menuItems.add(_rdpAction(context, peer.id));
|
||||
}
|
||||
final menuItems = await _buildConnectionMenuItems(context);
|
||||
menuItems.add(MenuEntryDivider());
|
||||
menuItems.add(_wolAction(peer.id));
|
||||
if (isWindows) {
|
||||
menuItems.add(_createShortCutAction(peer.id));
|
||||
@@ -1198,32 +1194,12 @@ class AddressBookPeerCard extends BasePeerCard {
|
||||
@override
|
||||
Future<List<MenuEntryBase<String>>> _buildMenuItems(
|
||||
BuildContext context) async {
|
||||
final List<MenuEntryBase<String>> menuItems = [
|
||||
_connectAction(context),
|
||||
_transferFileAction(context),
|
||||
_viewCameraAction(context),
|
||||
_terminalAction(context),
|
||||
];
|
||||
|
||||
if (peer.platform == kPeerPlatformWindows) {
|
||||
menuItems.add(_terminalRunAsAdminAction(context));
|
||||
}
|
||||
|
||||
if (isDesktop && peer.platform != kPeerPlatformAndroid) {
|
||||
menuItems.add(_tcpTunnelingAction(context));
|
||||
}
|
||||
// menuItems.add(await _openNewConnInOptAction(peer.id));
|
||||
if (!isWeb) {
|
||||
menuItems.add(await _forceAlwaysRelayAction(peer.id));
|
||||
}
|
||||
if (isWindows && peer.platform == kPeerPlatformWindows) {
|
||||
menuItems.add(_rdpAction(context, peer.id));
|
||||
}
|
||||
final menuItems = await _buildConnectionMenuItems(context);
|
||||
menuItems.add(MenuEntryDivider());
|
||||
if (isWindows) {
|
||||
menuItems.add(_createShortCutAction(peer.id));
|
||||
}
|
||||
if (gFFI.abModel.current.canWrite()) {
|
||||
menuItems.add(MenuEntryDivider());
|
||||
if (isMobile || isDesktop || isWebDesktop) {
|
||||
menuItems.add(_renameAction(peer.id));
|
||||
}
|
||||
@@ -1355,26 +1331,9 @@ class MyGroupPeerCard extends BasePeerCard {
|
||||
@override
|
||||
Future<List<MenuEntryBase<String>>> _buildMenuItems(
|
||||
BuildContext context) async {
|
||||
final List<MenuEntryBase<String>> menuItems = [
|
||||
_connectAction(context),
|
||||
_transferFileAction(context),
|
||||
_viewCameraAction(context),
|
||||
_terminalAction(context),
|
||||
];
|
||||
|
||||
if (peer.platform == kPeerPlatformWindows) {
|
||||
menuItems.add(_terminalRunAsAdminAction(context));
|
||||
}
|
||||
|
||||
if (isDesktop && peer.platform != kPeerPlatformAndroid) {
|
||||
menuItems.add(_tcpTunnelingAction(context));
|
||||
}
|
||||
// menuItems.add(await _openNewConnInOptAction(peer.id));
|
||||
if (!isWeb) {
|
||||
menuItems.add(await _forceAlwaysRelayAction(peer.id));
|
||||
}
|
||||
if (isWindows && peer.platform == kPeerPlatformWindows) {
|
||||
menuItems.add(_rdpAction(context, peer.id));
|
||||
final menuItems = await _buildConnectionMenuItems(context);
|
||||
if (isWindows || gFFI.userModel.userName.isNotEmpty) {
|
||||
menuItems.add(MenuEntryDivider());
|
||||
}
|
||||
if (isWindows) {
|
||||
menuItems.add(_createShortCutAction(peer.id));
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../desktop/widgets/material_mod_popup_menu.dart' as mod_menu;
|
||||
import '../../desktop/widgets/popup_menu.dart';
|
||||
|
||||
class RequestApprovalMenu extends MenuEntryBase<String> {
|
||||
final String title;
|
||||
final List<MenuEntryButton<String>> entries;
|
||||
final EdgeInsets? padding;
|
||||
|
||||
RequestApprovalMenu(
|
||||
{required this.title, required this.entries, this.padding});
|
||||
|
||||
@override
|
||||
List<mod_menu.PopupMenuEntry<String>> build(
|
||||
BuildContext context, MenuConfig conf) {
|
||||
final theme = Theme.of(context);
|
||||
final buttonPadding =
|
||||
(TextButtonTheme.of(context).style?.padding?.resolve({}) ??
|
||||
ButtonStyleButton.scaledPadding(
|
||||
theme.useMaterial3
|
||||
? const EdgeInsets.symmetric(horizontal: 12, vertical: 8)
|
||||
: const EdgeInsets.all(8),
|
||||
const EdgeInsets.symmetric(horizontal: 8),
|
||||
const EdgeInsets.symmetric(horizontal: 4),
|
||||
MediaQuery.textScalerOf(context)
|
||||
.scale(theme.textTheme.labelLarge?.fontSize ?? 14) /
|
||||
14,
|
||||
))
|
||||
.resolve(Directionality.of(context));
|
||||
final checkboxTheme = CheckboxTheme.of(context);
|
||||
final checkboxWidth =
|
||||
((checkboxTheme.materialTapTargetSize ?? theme.materialTapTargetSize) ==
|
||||
MaterialTapTargetSize.padded
|
||||
? kMinInteractiveDimension
|
||||
: kMinInteractiveDimension - 8) +
|
||||
(checkboxTheme.visualDensity ?? theme.visualDensity)
|
||||
.baseSizeAdjustment
|
||||
.dx;
|
||||
final arrowInset = (checkboxWidth - (IconTheme.of(context).size ?? 24)) / 2;
|
||||
return MenuEntrySubMenu<String>(
|
||||
text: title,
|
||||
dismissParentOnCanceled: true,
|
||||
// Normal entries contain two TextButtons; the submenu adds a leading gap.
|
||||
padding: (padding ?? EdgeInsets.zero)
|
||||
.add(EdgeInsets.only(
|
||||
left: buttonPadding.left * 2,
|
||||
right: buttonPadding.right * 2 + arrowInset))
|
||||
.subtract(
|
||||
const EdgeInsetsDirectional.only(start: MenuConfig.midPadding))
|
||||
.resolve(Directionality.of(context)),
|
||||
entries: entries
|
||||
.map((entry) => MenuEntryButton<String>(
|
||||
childBuilder: entry.childBuilder,
|
||||
proc: entry.proc,
|
||||
padding: entry.padding,
|
||||
enabled: entry.enabled,
|
||||
dismissOnClicked: true,
|
||||
dismissCallback: () {
|
||||
if (Navigator.canPop(context)) Navigator.pop(context);
|
||||
},
|
||||
))
|
||||
.toList(),
|
||||
).build(context, conf);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user