mirror of
https://github.com/weyne85/rustdesk.git
synced 2025-10-29 17:00:05 +00:00
remove menu from desktop home page
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
// main window right pane
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
|
||||
@@ -5,29 +5,14 @@ import 'package:flutter/material.dart' hide MenuItem;
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_hbb/common.dart';
|
||||
import 'package:flutter_hbb/desktop/pages/connection_page.dart';
|
||||
import 'package:flutter_hbb/desktop/pages/desktop_setting_page.dart';
|
||||
import 'package:flutter_hbb/desktop/widgets/popup_menu.dart';
|
||||
import 'package:flutter_hbb/desktop/widgets/material_mod_popup_menu.dart'
|
||||
as mod_menu;
|
||||
import 'package:flutter_hbb/models/platform_model.dart';
|
||||
import 'package:flutter_hbb/models/server_model.dart';
|
||||
import 'package:flutter_hbb/utils/multi_window_manager.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:tray_manager/tray_manager.dart';
|
||||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
|
||||
import '../../common/widgets/dialog.dart';
|
||||
|
||||
class _MenubarTheme {
|
||||
static const Color commonColor = MyTheme.accent;
|
||||
// kMinInteractiveDimension
|
||||
static const double height = 25.0;
|
||||
static const double dividerHeight = 12.0;
|
||||
}
|
||||
|
||||
class DesktopHomePage extends StatefulWidget {
|
||||
const DesktopHomePage({Key? key}) : super(key: key);
|
||||
|
||||
@@ -66,19 +51,19 @@ class _DesktopHomePageState extends State<DesktopHomePage>
|
||||
super.build(context);
|
||||
return Row(
|
||||
children: [
|
||||
buildServerInfo(context),
|
||||
buildLeftPane(context),
|
||||
const VerticalDivider(
|
||||
width: 1,
|
||||
thickness: 1,
|
||||
),
|
||||
Expanded(
|
||||
child: buildServerBoard(context),
|
||||
child: buildRightPane(context),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
buildServerInfo(BuildContext context) {
|
||||
buildLeftPane(BuildContext context) {
|
||||
return ChangeNotifierProvider.value(
|
||||
value: gFFI.serverModel,
|
||||
child: Container(
|
||||
@@ -95,7 +80,7 @@ class _DesktopHomePageState extends State<DesktopHomePage>
|
||||
);
|
||||
}
|
||||
|
||||
buildServerBoard(BuildContext context) {
|
||||
buildRightPane(BuildContext context) {
|
||||
return Container(
|
||||
color: MyTheme.color(context).grayBg,
|
||||
child: ConnectionPage(),
|
||||
@@ -167,93 +152,9 @@ class _DesktopHomePageState extends State<DesktopHomePage>
|
||||
}
|
||||
|
||||
Widget buildPopupMenu(BuildContext context) {
|
||||
var position;
|
||||
RxBool hover = false.obs;
|
||||
return InkWell(
|
||||
onTapDown: (detail) {
|
||||
final x = detail.globalPosition.dx;
|
||||
final y = detail.globalPosition.dy;
|
||||
position = RelativeRect.fromLTRB(x, y, x, y);
|
||||
},
|
||||
onTap: () async {
|
||||
final userName = await gFFI.userModel.getUserName();
|
||||
final enabledInput = await bind.mainGetOption(key: 'enable-audio');
|
||||
final defaultInput = await gFFI.getDefaultAudioInput();
|
||||
var menu = <PopupMenuEntry>[
|
||||
await genEnablePopupMenuItem(
|
||||
translate("Enable Keyboard/Mouse"),
|
||||
'enable-keyboard',
|
||||
),
|
||||
await genEnablePopupMenuItem(
|
||||
translate("Enable Clipboard"),
|
||||
'enable-clipboard',
|
||||
),
|
||||
await genEnablePopupMenuItem(
|
||||
translate("Enable File Transfer"),
|
||||
'enable-file-transfer',
|
||||
),
|
||||
await genEnablePopupMenuItem(
|
||||
translate("Enable TCP Tunneling"),
|
||||
'enable-tunnel',
|
||||
),
|
||||
genAudioInputPopupMenuItem(enabledInput != "N", defaultInput),
|
||||
PopupMenuDivider(),
|
||||
PopupMenuItem(
|
||||
child: Text(translate("ID/Relay Server")),
|
||||
value: 'custom-server',
|
||||
),
|
||||
PopupMenuItem(
|
||||
child: Text(translate("IP Whitelisting")),
|
||||
value: 'whitelist',
|
||||
),
|
||||
PopupMenuItem(
|
||||
child: Text(translate("Socks5 Proxy")),
|
||||
value: 'socks5-proxy',
|
||||
),
|
||||
PopupMenuDivider(),
|
||||
await genEnablePopupMenuItem(
|
||||
translate("Enable Service"),
|
||||
'stop-service',
|
||||
),
|
||||
// TODO: direct server
|
||||
await genEnablePopupMenuItem(
|
||||
translate("Always connected via relay"),
|
||||
'allow-always-relay',
|
||||
),
|
||||
await genEnablePopupMenuItem(
|
||||
translate("Start ID/relay service"),
|
||||
'stop-rendezvous-service',
|
||||
),
|
||||
PopupMenuDivider(),
|
||||
userName.isEmpty
|
||||
? PopupMenuItem(
|
||||
child: Text(translate("Login")),
|
||||
value: 'login',
|
||||
)
|
||||
: PopupMenuItem(
|
||||
child: Text("${translate("Logout")} $userName"),
|
||||
value: 'logout',
|
||||
),
|
||||
PopupMenuItem(
|
||||
child: Text(translate("Change ID")),
|
||||
value: 'change-id',
|
||||
),
|
||||
PopupMenuDivider(),
|
||||
await genEnablePopupMenuItem(
|
||||
translate("Dark Theme"),
|
||||
'allow-darktheme',
|
||||
),
|
||||
PopupMenuItem(
|
||||
child: Text(translate("About")),
|
||||
value: 'about',
|
||||
),
|
||||
];
|
||||
final v =
|
||||
await showMenu(context: context, position: position, items: menu);
|
||||
if (v != null) {
|
||||
onSelectMenu(v);
|
||||
}
|
||||
},
|
||||
onTap: () async {},
|
||||
child: Obx(
|
||||
() => CircleAvatar(
|
||||
radius: 15,
|
||||
@@ -276,6 +177,7 @@ class _DesktopHomePageState extends State<DesktopHomePage>
|
||||
buildPasswordBoard(BuildContext context) {
|
||||
final model = gFFI.serverModel;
|
||||
RxBool refreshHover = false.obs;
|
||||
RxBool editHover = false.obs;
|
||||
return Container(
|
||||
margin: EdgeInsets.only(left: 20.0, right: 16, top: 13, bottom: 13),
|
||||
child: Row(
|
||||
@@ -334,7 +236,19 @@ class _DesktopHomePageState extends State<DesktopHomePage>
|
||||
onTap: () => bind.mainUpdateTemporaryPassword(),
|
||||
onHover: (value) => refreshHover.value = value,
|
||||
),
|
||||
const _PasswordPopupMenu(),
|
||||
InkWell(
|
||||
child: Obx(
|
||||
() => Icon(
|
||||
Icons.edit,
|
||||
color: editHover.value
|
||||
? MyTheme.color(context).text
|
||||
: Color(0xFFDDDDDD),
|
||||
size: 22,
|
||||
).marginOnly(right: 8, bottom: 2),
|
||||
),
|
||||
onTap: () => {},
|
||||
onHover: (value) => editHover.value = value,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
@@ -408,236 +322,6 @@ class _DesktopHomePageState extends State<DesktopHomePage>
|
||||
windowManager.removeListener(this);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void changeTheme(String choice) async {
|
||||
if (choice == "Y") {
|
||||
Get.changeTheme(MyTheme.darkTheme);
|
||||
} else {
|
||||
Get.changeTheme(MyTheme.lightTheme);
|
||||
}
|
||||
Get.find<SharedPreferences>().setString("darkTheme", choice);
|
||||
Get.forceAppUpdate();
|
||||
}
|
||||
|
||||
void onSelectMenu(String key) async {
|
||||
if (key.startsWith('enable-')) {
|
||||
final option = await bind.mainGetOption(key: key);
|
||||
bind.mainSetOption(key: key, value: option == "N" ? "" : "N");
|
||||
} else if (key.startsWith('allow-')) {
|
||||
final option = await bind.mainGetOption(key: key);
|
||||
final choice = option == "Y" ? "" : "Y";
|
||||
bind.mainSetOption(key: key, value: choice);
|
||||
if (key == "allow-darktheme") changeTheme(choice);
|
||||
} else if (key == "stop-service") {
|
||||
final option = await bind.mainGetOption(key: key);
|
||||
bind.mainSetOption(key: key, value: option == "Y" ? "" : "Y");
|
||||
} else if (key == "change-id") {
|
||||
changeIdDialog();
|
||||
} else if (key == "custom-server") {
|
||||
changeServer();
|
||||
} else if (key == "whitelist") {
|
||||
changeWhiteList();
|
||||
} else if (key == "socks5-proxy") {
|
||||
changeSocks5Proxy();
|
||||
} else if (key == "about") {
|
||||
about();
|
||||
} else if (key == "logout") {
|
||||
logOut();
|
||||
} else if (key == "login") {
|
||||
login();
|
||||
}
|
||||
}
|
||||
|
||||
Future<PopupMenuItem<String>> genEnablePopupMenuItem(
|
||||
String label, String key) async {
|
||||
final v = await bind.mainGetOption(key: key);
|
||||
bool enable;
|
||||
if (key == "stop-service") {
|
||||
enable = v != "Y";
|
||||
} else if (key.startsWith("allow-")) {
|
||||
enable = v == "Y";
|
||||
} else {
|
||||
enable = v != "N";
|
||||
}
|
||||
|
||||
return PopupMenuItem(
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(Icons.check,
|
||||
color: enable ? null : MyTheme.accent.withAlpha(00)),
|
||||
Text(
|
||||
label,
|
||||
style: genTextStyle(enable),
|
||||
),
|
||||
],
|
||||
),
|
||||
value: key,
|
||||
);
|
||||
}
|
||||
|
||||
TextStyle genTextStyle(bool isPositive) {
|
||||
return isPositive
|
||||
? TextStyle()
|
||||
: TextStyle(
|
||||
color: Colors.redAccent, decoration: TextDecoration.lineThrough);
|
||||
}
|
||||
|
||||
PopupMenuItem<String> genAudioInputPopupMenuItem(
|
||||
bool enableInput, String defaultAudioInput) {
|
||||
final defaultInput = defaultAudioInput.obs;
|
||||
final enabled = enableInput.obs;
|
||||
|
||||
return PopupMenuItem(
|
||||
child: FutureBuilder<List<String>>(
|
||||
future: gFFI.getAudioInputs(),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
final inputs = snapshot.data!.toList();
|
||||
if (Platform.isWindows) {
|
||||
inputs.insert(0, translate("System Sound"));
|
||||
}
|
||||
var inputList = inputs
|
||||
.map((e) => PopupMenuItem(
|
||||
child: Row(
|
||||
children: [
|
||||
Obx(() => Offstage(
|
||||
offstage: defaultInput.value != e,
|
||||
child: Icon(Icons.check))),
|
||||
Expanded(
|
||||
child: Tooltip(
|
||||
message: e,
|
||||
child: Text(
|
||||
"$e",
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
))),
|
||||
],
|
||||
),
|
||||
value: e,
|
||||
))
|
||||
.toList();
|
||||
inputList.insert(
|
||||
0,
|
||||
PopupMenuItem(
|
||||
child: Row(
|
||||
children: [
|
||||
Obx(() => Offstage(
|
||||
offstage: enabled.value, child: Icon(Icons.check))),
|
||||
Expanded(child: Text(translate("Mute"))),
|
||||
],
|
||||
),
|
||||
value: "Mute",
|
||||
));
|
||||
return PopupMenuButton<String>(
|
||||
padding: EdgeInsets.zero,
|
||||
child: Container(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(translate("Audio Input"))),
|
||||
itemBuilder: (context) => inputList,
|
||||
onSelected: (dev) async {
|
||||
if (dev == "Mute") {
|
||||
await bind.mainSetOption(
|
||||
key: 'enable-audio', value: enabled.value ? '' : 'N');
|
||||
enabled.value =
|
||||
await bind.mainGetOption(key: 'enable-audio') != 'N';
|
||||
} else if (dev != await gFFI.getDefaultAudioInput()) {
|
||||
gFFI.setDefaultAudioInput(dev);
|
||||
defaultInput.value = dev;
|
||||
}
|
||||
},
|
||||
);
|
||||
} else {
|
||||
return Text("...");
|
||||
}
|
||||
},
|
||||
),
|
||||
value: 'audio-input',
|
||||
);
|
||||
}
|
||||
|
||||
void about() async {
|
||||
final appName = await bind.mainGetAppName();
|
||||
final license = await bind.mainGetLicense();
|
||||
final version = await bind.mainGetVersion();
|
||||
const linkStyle = TextStyle(decoration: TextDecoration.underline);
|
||||
gFFI.dialogManager.show((setState, close) {
|
||||
return CustomAlertDialog(
|
||||
title: Text("About $appName"),
|
||||
content: ConstrainedBox(
|
||||
constraints: const BoxConstraints(minWidth: 500),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const SizedBox(
|
||||
height: 8.0,
|
||||
),
|
||||
Text("Version: $version").marginSymmetric(vertical: 4.0),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
launchUrlString("https://rustdesk.com/privacy");
|
||||
},
|
||||
child: const Text(
|
||||
"Privacy Statement",
|
||||
style: linkStyle,
|
||||
).marginSymmetric(vertical: 4.0)),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
launchUrlString("https://rustdesk.com");
|
||||
},
|
||||
child: const Text(
|
||||
"Website",
|
||||
style: linkStyle,
|
||||
).marginSymmetric(vertical: 4.0)),
|
||||
Container(
|
||||
decoration: const BoxDecoration(color: Color(0xFF2c8cff)),
|
||||
padding:
|
||||
const EdgeInsets.symmetric(vertical: 24, horizontal: 8),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Copyright © 2022 Purslane Ltd.\n$license",
|
||||
style: const TextStyle(color: Colors.white),
|
||||
),
|
||||
const Text(
|
||||
"Made with heart in this chaotic world!",
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w800,
|
||||
color: Colors.white),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
).marginSymmetric(vertical: 4.0)
|
||||
],
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(onPressed: close, child: Text(translate("OK"))),
|
||||
],
|
||||
onSubmit: close,
|
||||
onCancel: close,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
void login() {
|
||||
loginDialog().then((success) {
|
||||
if (success) {
|
||||
// refresh frame
|
||||
setState(() {});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void logOut() {
|
||||
gFFI.userModel.logOut().then((_) => {setState(() {})});
|
||||
}
|
||||
}
|
||||
|
||||
/// common login dialog for desktop
|
||||
@@ -874,120 +558,3 @@ void setPasswordDialog() async {
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
class _PasswordPopupMenu extends StatefulWidget {
|
||||
const _PasswordPopupMenu({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<_PasswordPopupMenu> createState() => _PasswordPopupMenuState();
|
||||
}
|
||||
|
||||
class _PasswordPopupMenuState extends State<_PasswordPopupMenu> {
|
||||
final RxBool _tempEnabled = true.obs;
|
||||
final RxBool _permEnabled = true.obs;
|
||||
|
||||
List<MenuEntryBase<String>> _buildMenus() {
|
||||
return <MenuEntryBase<String>>[
|
||||
MenuEntryRadios<String>(
|
||||
text: translate('Password type'),
|
||||
optionsGetter: () => [
|
||||
MenuEntryRadioOption(
|
||||
text: translate('Use temporary password'),
|
||||
value: kUseTemporaryPassword),
|
||||
MenuEntryRadioOption(
|
||||
text: translate('Use permanent password'),
|
||||
value: kUsePermanentPassword),
|
||||
MenuEntryRadioOption(
|
||||
text: translate('Use both passwords'),
|
||||
value: kUseBothPasswords),
|
||||
],
|
||||
curOptionGetter: () async {
|
||||
return gFFI.serverModel.verificationMethod;
|
||||
},
|
||||
optionSetter: (String oldValue, String newValue) async {
|
||||
await bind.mainSetOption(
|
||||
key: "verification-method", value: newValue);
|
||||
await gFFI.serverModel.updatePasswordModel();
|
||||
setState(() {
|
||||
_tempEnabled.value =
|
||||
gFFI.serverModel.verificationMethod != kUsePermanentPassword;
|
||||
_permEnabled.value =
|
||||
gFFI.serverModel.verificationMethod != kUseTemporaryPassword;
|
||||
});
|
||||
}),
|
||||
MenuEntryDivider(),
|
||||
MenuEntryButton<String>(
|
||||
enabled: _permEnabled,
|
||||
childBuilder: (TextStyle? style) => Text(
|
||||
translate('Set permanent password'),
|
||||
style: style,
|
||||
),
|
||||
proc: () {
|
||||
setPasswordDialog();
|
||||
},
|
||||
dismissOnClicked: true,
|
||||
),
|
||||
MenuEntrySubMenu(
|
||||
enabled: _tempEnabled,
|
||||
text: translate('Set temporary password length'),
|
||||
entries: [
|
||||
MenuEntryRadios<String>(
|
||||
enabled: _tempEnabled,
|
||||
text: translate(''),
|
||||
optionsGetter: () => [
|
||||
MenuEntryRadioOption(
|
||||
text: translate('6'),
|
||||
value: '6',
|
||||
enabled: _tempEnabled,
|
||||
),
|
||||
MenuEntryRadioOption(
|
||||
text: translate('8'),
|
||||
value: '8',
|
||||
enabled: _tempEnabled,
|
||||
),
|
||||
MenuEntryRadioOption(
|
||||
text: translate('10'),
|
||||
value: '10',
|
||||
enabled: _tempEnabled,
|
||||
),
|
||||
],
|
||||
curOptionGetter: () async {
|
||||
return gFFI.serverModel.temporaryPasswordLength;
|
||||
},
|
||||
optionSetter: (String oldValue, String newValue) async {
|
||||
if (oldValue != newValue) {
|
||||
await gFFI.serverModel.setTemporaryPasswordLength(newValue);
|
||||
await gFFI.serverModel.updatePasswordModel();
|
||||
}
|
||||
}),
|
||||
])
|
||||
];
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final editHover = false.obs;
|
||||
return mod_menu.PopupMenuButton(
|
||||
padding: EdgeInsets.zero,
|
||||
onHover: (v) => editHover.value = v,
|
||||
tooltip: translate(''),
|
||||
position: mod_menu.PopupMenuPosition.overSide,
|
||||
itemBuilder: (BuildContext context) => _buildMenus()
|
||||
.map((entry) => entry.build(
|
||||
context,
|
||||
const MenuConfig(
|
||||
commonColor: _MenubarTheme.commonColor,
|
||||
height: _MenubarTheme.height,
|
||||
dividerHeight: _MenubarTheme.dividerHeight,
|
||||
)))
|
||||
.expand((i) => i)
|
||||
.toList(),
|
||||
child: Obx(() => Icon(Icons.edit,
|
||||
size: 22,
|
||||
color: editHover.value
|
||||
? MyTheme.color(context).text
|
||||
: const Color(0xFFDDDDDD))
|
||||
.marginOnly(bottom: 2)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user