Merge pull request #1511 from fufesou/flutter_desktop_new_remote_menu_4

Flutter desktop new remote menu 4
This commit is contained in:
RustDesk 2022-09-13 22:33:15 +08:00 committed by GitHub
commit 406be63ffd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 175 additions and 116 deletions

View File

@ -330,18 +330,6 @@ class _DesktopHomePageState extends State<DesktopHomePage>
onHover: (value) => refreshHover.value = value, onHover: (value) => refreshHover.value = value,
), ),
const _PasswordPopupMenu(), const _PasswordPopupMenu(),
// FutureBuilder<Widget>(
// future: buildPasswordPopupMenu(context),
// builder: (context, snapshot) {
// if (snapshot.hasError) {
// print("${snapshot.error}");
// }
// if (snapshot.hasData) {
// return snapshot.data!;
// } else {
// return Offstage();
// }
// })
], ],
), ),
], ],
@ -353,92 +341,6 @@ class _DesktopHomePageState extends State<DesktopHomePage>
); );
} }
Future<Widget> buildPasswordPopupMenu(BuildContext context) async {
var position;
RxBool editHover = 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 {
var method = (String text, String value) => PopupMenuItem(
child: Row(
children: [
Offstage(
offstage: gFFI.serverModel.verificationMethod != value,
child: Icon(Icons.check)),
Text(
text,
),
],
),
onTap: () => gFFI.serverModel.setVerificationMethod(value),
);
final temporary_enabled =
gFFI.serverModel.verificationMethod != kUsePermanentPassword;
var menu = <PopupMenuEntry>[
method(translate("Use temporary password"), kUseTemporaryPassword),
method(translate("Use permanent password"), kUsePermanentPassword),
method(translate("Use both passwords"), kUseBothPasswords),
PopupMenuDivider(),
PopupMenuItem(
child: Text(translate("Set permanent password")),
value: 'set-permanent-password',
enabled: gFFI.serverModel.verificationMethod !=
kUseTemporaryPassword),
PopupMenuItem(
child: PopupMenuButton(
padding: EdgeInsets.zero,
child: Text(
translate("Set temporary password length"),
),
itemBuilder: (context) => ["6", "8", "10"]
.map((e) => PopupMenuItem(
child: Row(
children: [
Offstage(
offstage: gFFI.serverModel
.temporaryPasswordLength !=
e,
child: Icon(Icons.check)),
Text(
e,
),
],
),
onTap: () {
if (gFFI.serverModel.temporaryPasswordLength !=
e) {
() async {
await gFFI.serverModel
.setTemporaryPasswordLength(e);
await bind.mainUpdateTemporaryPassword();
}();
}
},
))
.toList(),
enabled: temporary_enabled,
),
enabled: temporary_enabled),
];
final v =
await showMenu(context: context, position: position, items: menu);
if (v == "set-permanent-password") {
setPasswordDialog();
}
},
onHover: (value) => editHover.value = value,
child: Obx(() => Icon(Icons.edit,
size: 22,
color: editHover.value
? MyTheme.color(context).text
: Color(0xFFDDDDDD))
.marginOnly(bottom: 2)));
}
buildTip(BuildContext context) { buildTip(BuildContext context) {
return Padding( return Padding(
padding: padding:
@ -469,7 +371,7 @@ class _DesktopHomePageState extends State<DesktopHomePage>
@override @override
void onTrayMenuItemClick(MenuItem menuItem) { void onTrayMenuItemClick(MenuItem menuItem) {
print("click ${menuItem.key}"); print('click ${menuItem.key}');
switch (menuItem.key) { switch (menuItem.key) {
case "quit": case "quit":
exit(0); exit(0);

View File

@ -50,6 +50,8 @@ class _RemotePageState extends State<RemotePage>
var _isPhysicalMouse = false; var _isPhysicalMouse = false;
var _imageFocused = false; var _imageFocused = false;
final _onEnterOrLeaveImage = <Function(bool)>[];
late FFI _ffi; late FFI _ffi;
void _updateTabBarHeight() { void _updateTabBarHeight() {
@ -421,11 +423,17 @@ class _RemotePageState extends State<RemotePage>
_physicalFocusNode.requestFocus(); _physicalFocusNode.requestFocus();
} }
_cursorOverImage.value = true; _cursorOverImage.value = true;
for (var f in _onEnterOrLeaveImage) {
f(true);
}
_ffi.enterOrLeave(true); _ffi.enterOrLeave(true);
} }
void leaveView(PointerExitEvent evt) { void leaveView(PointerExitEvent evt) {
_cursorOverImage.value = false; _cursorOverImage.value = false;
for (var f in _onEnterOrLeaveImage) {
f(false);
}
_ffi.enterOrLeave(false); _ffi.enterOrLeave(false);
} }
@ -469,6 +477,7 @@ class _RemotePageState extends State<RemotePage>
paints.add(RemoteMenubar( paints.add(RemoteMenubar(
id: widget.id, id: widget.id,
ffi: _ffi, ffi: _ffi,
onEnterOrLeaveImage: _onEnterOrLeaveImage,
)); ));
return Stack( return Stack(
children: paints, children: paints,
@ -597,8 +606,8 @@ class ImagePaint extends StatelessWidget {
return FlutterCustomMemoryImageCursor( return FlutterCustomMemoryImageCursor(
pixbuf: cacheLinux.data, pixbuf: cacheLinux.data,
key: key, key: key,
hotx: 0.0, hotx: cacheLinux.hotx,
hoty: 0.0, hoty: cacheLinux.hoty,
imageWidth: (cacheLinux.width * scale).toInt(), imageWidth: (cacheLinux.width * scale).toInt(),
imageHeight: (cacheLinux.height * scale).toInt(), imageHeight: (cacheLinux.height * scale).toInt(),
); );

View File

@ -1,4 +1,5 @@
import 'dart:io'; import 'dart:io';
import 'dart:math' as math;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
@ -25,11 +26,13 @@ class _MenubarTheme {
class RemoteMenubar extends StatefulWidget { class RemoteMenubar extends StatefulWidget {
final String id; final String id;
final FFI ffi; final FFI ffi;
final List<Function(bool)> onEnterOrLeaveImage;
const RemoteMenubar({ const RemoteMenubar({
Key? key, Key? key,
required this.id, required this.id,
required this.ffi, required this.ffi,
required this.onEnterOrLeaveImage,
}) : super(key: key); }) : super(key: key);
@override @override
@ -39,12 +42,38 @@ class RemoteMenubar extends StatefulWidget {
class _RemoteMenubarState extends State<RemoteMenubar> { class _RemoteMenubarState extends State<RemoteMenubar> {
final RxBool _show = false.obs; final RxBool _show = false.obs;
final Rx<Color> _hideColor = Colors.white12.obs; final Rx<Color> _hideColor = Colors.white12.obs;
final _rxHideReplay = rxdart.ReplaySubject<int>();
final _pinMenubar = false.obs;
bool _isCursorOverImage = false;
bool get isFullscreen => Get.find<RxBool>(tag: 'fullscreen').isTrue; bool get isFullscreen => Get.find<RxBool>(tag: 'fullscreen').isTrue;
void setFullscreen(bool v) { void _setFullscreen(bool v) {
Get.find<RxBool>(tag: 'fullscreen').value = v; Get.find<RxBool>(tag: 'fullscreen').value = v;
} }
@override
void initState() {
super.initState();
widget.onEnterOrLeaveImage.add((enter) {
if (enter) {
_rxHideReplay.add(0);
_isCursorOverImage = true;
} else {
_isCursorOverImage = false;
}
});
_rxHideReplay
.throttleTime(const Duration(milliseconds: 5000),
trailing: true, leading: false)
.listen((int v) {
if (_pinMenubar.isFalse && _show.isTrue && _isCursorOverImage) {
_show.value = false;
}
});
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Align( return Align(
@ -76,6 +105,7 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
Widget _buildMenubar(BuildContext context) { Widget _buildMenubar(BuildContext context) {
final List<Widget> menubarItems = []; final List<Widget> menubarItems = [];
if (!isWebDesktop) { if (!isWebDesktop) {
menubarItems.add(_buildPinMenubar(context));
menubarItems.add(_buildFullscreen(context)); menubarItems.add(_buildFullscreen(context));
if (widget.ffi.ffiModel.isPeerAndroid) { if (widget.ffi.ffiModel.isPeerAndroid) {
menubarItems.add(IconButton( menubarItems.add(IconButton(
@ -111,11 +141,29 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
])); ]));
} }
Widget _buildPinMenubar(BuildContext context) {
return Obx(() => IconButton(
tooltip:
translate(_pinMenubar.isTrue ? 'Unpin menubar' : 'Pin menubar'),
onPressed: () {
_pinMenubar.value = !_pinMenubar.value;
},
icon: Obx(() => Transform.rotate(
angle: _pinMenubar.isTrue ? math.pi / 4 : 0,
child: Icon(
Icons.push_pin,
color: _pinMenubar.isTrue
? _MenubarTheme.commonColor
: Colors.grey,
))),
));
}
Widget _buildFullscreen(BuildContext context) { Widget _buildFullscreen(BuildContext context) {
return IconButton( return IconButton(
tooltip: translate(isFullscreen ? 'Exit Fullscreen' : 'Fullscreen'), tooltip: translate(isFullscreen ? 'Exit Fullscreen' : 'Fullscreen'),
onPressed: () { onPressed: () {
setFullscreen(!isFullscreen); _setFullscreen(!isFullscreen);
}, },
icon: Obx(() => isFullscreen icon: Obx(() => isFullscreen
? const Icon( ? const Icon(
@ -250,7 +298,6 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
), ),
tooltip: translate('Display Settings'), tooltip: translate('Display Settings'),
position: mod_menu.PopupMenuPosition.under, position: mod_menu.PopupMenuPosition.under,
onSelected: (String item) {},
itemBuilder: (BuildContext context) => _getDisplayMenu() itemBuilder: (BuildContext context) => _getDisplayMenu()
.map((entry) => entry.build( .map((entry) => entry.build(
context, context,
@ -273,7 +320,6 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
), ),
tooltip: translate('Keyboard Settings'), tooltip: translate('Keyboard Settings'),
position: mod_menu.PopupMenuPosition.under, position: mod_menu.PopupMenuPosition.under,
onSelected: (String item) {},
itemBuilder: (BuildContext context) => _getKeyboardMenu() itemBuilder: (BuildContext context) => _getKeyboardMenu()
.map((entry) => entry.build( .map((entry) => entry.build(
context, context,

View File

@ -409,6 +409,56 @@ enum ScrollStyle {
scrollauto, scrollauto,
} }
class ViewStyle {
final String style;
final double width;
final double height;
final int displayWidth;
final int displayHeight;
ViewStyle({
this.style = '',
this.width = 0.0,
this.height = 0.0,
this.displayWidth = 0,
this.displayHeight = 0,
});
static int _double2Int(double v) => (v * 100).round().toInt();
@override
bool operator ==(Object other) =>
other is ViewStyle &&
other.runtimeType == runtimeType &&
_innerEqual(other);
bool _innerEqual(ViewStyle other) {
return style == other.style &&
ViewStyle._double2Int(other.width) == ViewStyle._double2Int(width) &&
ViewStyle._double2Int(other.height) == ViewStyle._double2Int(height) &&
other.displayWidth == displayWidth &&
other.displayHeight == displayHeight;
}
@override
int get hashCode => Object.hash(
style,
ViewStyle._double2Int(width),
ViewStyle._double2Int(height),
displayWidth,
displayHeight,
).hashCode;
double get scale {
double s = 1.0;
if (style == 'adaptive') {
final s1 = width / displayWidth;
final s2 = height / displayHeight;
s = s1 < s2 ? s1 : s2;
}
return s;
}
}
class CanvasModel with ChangeNotifier { class CanvasModel with ChangeNotifier {
// image offset of canvas // image offset of canvas
double _x = 0; double _x = 0;
@ -425,7 +475,7 @@ class CanvasModel with ChangeNotifier {
// scroll offset y percent // scroll offset y percent
double _scrollY = 0.0; double _scrollY = 0.0;
ScrollStyle _scrollStyle = ScrollStyle.scrollauto; ScrollStyle _scrollStyle = ScrollStyle.scrollauto;
String? _viewStyle; ViewStyle _lastViewStyle = ViewStyle();
WeakReference<FFI> parent; WeakReference<FFI> parent;
@ -446,19 +496,27 @@ class CanvasModel with ChangeNotifier {
updateViewStyle() async { updateViewStyle() async {
final style = await bind.sessionGetOption(id: id, arg: 'view-style'); final style = await bind.sessionGetOption(id: id, arg: 'view-style');
if (style == null || _viewStyle == style) { if (style == null) {
return; return;
} }
final sizeWidth = size.width;
_scale = 1.0; final sizeHeight = size.height;
if (style == 'adaptive') { final displayWidth = getDisplayWidth();
final s1 = size.width / getDisplayWidth(); final displayHeight = getDisplayHeight();
final s2 = size.height / getDisplayHeight(); final viewStyle = ViewStyle(
_scale = s1 < s2 ? s1 : s2; style: style,
width: sizeWidth,
height: sizeHeight,
displayWidth: displayWidth,
displayHeight: displayHeight,
);
if (_lastViewStyle == viewStyle) {
return;
} }
_viewStyle = style; _lastViewStyle = viewStyle;
_x = (size.width - getDisplayWidth() * _scale) / 2; _scale = viewStyle.scale;
_y = (size.height - getDisplayHeight() * _scale) / 2; _x = (sizeWidth - displayWidth * _scale) / 2;
_y = (sizeHeight - displayHeight * _scale) / 2;
notifyListeners(); notifyListeners();
} }

View File

@ -344,5 +344,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Use IP Whitelisting", "只允许白名单上的IP访问"), ("Use IP Whitelisting", "只允许白名单上的IP访问"),
("Network", "网络"), ("Network", "网络"),
("Enable RDP", "允许RDP访问"), ("Enable RDP", "允许RDP访问"),
("Pin menubar", "固定菜单栏"),
("Unpin menubar", "取消固定菜单栏"),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -344,5 +344,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Use IP Whitelisting", ""), ("Use IP Whitelisting", ""),
("Network", ""), ("Network", ""),
("Enable RDP", ""), ("Enable RDP", ""),
("Pin menubar", "Připnout panel nabídek"),
("Unpin menubar", "Odepnout panel nabídek"),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -344,5 +344,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Use IP Whitelisting", ""), ("Use IP Whitelisting", ""),
("Network", ""), ("Network", ""),
("Enable RDP", ""), ("Enable RDP", ""),
("Pin menubar", "Fastgør menulinjen"),
("Unpin menubar", "Frigør menulinjen"),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -344,5 +344,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Use IP Whitelisting", ""), ("Use IP Whitelisting", ""),
("Network", ""), ("Network", ""),
("Enable RDP", ""), ("Enable RDP", ""),
("Pin menubar", "Pin-Menüleiste"),
("Unpin menubar", "Menüleiste lösen"),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -344,5 +344,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Use IP Whitelisting", ""), ("Use IP Whitelisting", ""),
("Network", ""), ("Network", ""),
("Enable RDP", ""), ("Enable RDP", ""),
("Pin menubar", "Alpingla menubreto"),
("Unpin menubar", "Malfiksi menubreton"),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -357,5 +357,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Use IP Whitelisting", ""), ("Use IP Whitelisting", ""),
("Network", ""), ("Network", ""),
("Enable RDP", ""), ("Enable RDP", ""),
("Pin menubar", "Pin barra de menú"),
("Unpin menubar", "Desbloquear barra de menú"),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -344,5 +344,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Use IP Whitelisting", ""), ("Use IP Whitelisting", ""),
("Network", ""), ("Network", ""),
("Enable RDP", ""), ("Enable RDP", ""),
("Pin menubar", "Épingler la barre de menus"),
("Unpin menubar", "Détacher la barre de menu"),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -344,5 +344,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Use IP Whitelisting", ""), ("Use IP Whitelisting", ""),
("Network", ""), ("Network", ""),
("Enable RDP", ""), ("Enable RDP", ""),
("Pin menubar", "Menüsor rögzítése"),
("Unpin menubar", "Menüsor rögzítésének feloldása"),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -357,5 +357,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Use IP Whitelisting", ""), ("Use IP Whitelisting", ""),
("Network", ""), ("Network", ""),
("Enable RDP", ""), ("Enable RDP", ""),
("Pin menubar", "Pin menubar"),
("Unpin menubar", "Unpin menubar"),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -343,5 +343,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Use IP Whitelisting", ""), ("Use IP Whitelisting", ""),
("Network", ""), ("Network", ""),
("Enable RDP", ""), ("Enable RDP", ""),
("Pin menubar", "Blocca la barra dei menu"),
("Unpin menubar", "Sblocca la barra dei menu"),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -341,5 +341,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Use IP Whitelisting", ""), ("Use IP Whitelisting", ""),
("Network", ""), ("Network", ""),
("Enable RDP", ""), ("Enable RDP", ""),
("Pin menubar", "メニューバーを固定する"),
("Unpin menubar", "メニューバーのピン留めを外す"),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -338,5 +338,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Use IP Whitelisting", ""), ("Use IP Whitelisting", ""),
("Network", ""), ("Network", ""),
("Enable RDP", ""), ("Enable RDP", ""),
("Pin menubar", "핀 메뉴 바"),
("Unpin menubar", "메뉴 모음 고정 해제"),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -319,5 +319,7 @@ lazy_static::lazy_static! {
("Insecure Connection", "Қатерлі Қосылым"), ("Insecure Connection", "Қатерлі Қосылым"),
("Scale original", "Scale original"), ("Scale original", "Scale original"),
("Scale adaptive", "Scale adaptive"), ("Scale adaptive", "Scale adaptive"),
("Pin menubar", "Мәзір жолағын бекіту"),
("Unpin menubar", "Мәзір жолағын босату"),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -342,5 +342,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Use IP Whitelisting", ""), ("Use IP Whitelisting", ""),
("Network", ""), ("Network", ""),
("Enable RDP", ""), ("Enable RDP", ""),
("Pin menubar", "Przypnij pasek menu"),
("Unpin menubar", "Odepnij pasek menu"),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -338,5 +338,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Use IP Whitelisting", ""), ("Use IP Whitelisting", ""),
("Network", ""), ("Network", ""),
("Enable RDP", ""), ("Enable RDP", ""),
("Pin menubar", "Fixar barra de menu"),
("Unpin menubar", "Desenganxa la barra de menús"),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -344,5 +344,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Use IP Whitelisting", ""), ("Use IP Whitelisting", ""),
("Network", ""), ("Network", ""),
("Enable RDP", ""), ("Enable RDP", ""),
("Pin menubar", ""),
("Unpin menubar", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -344,5 +344,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Use IP Whitelisting", ""), ("Use IP Whitelisting", ""),
("Network", ""), ("Network", ""),
("Enable RDP", ""), ("Enable RDP", ""),
("Pin menubar", "Закрепить строку меню"),
("Unpin menubar", "Открепить строку меню"),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -344,5 +344,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Use IP Whitelisting", ""), ("Use IP Whitelisting", ""),
("Network", ""), ("Network", ""),
("Enable RDP", ""), ("Enable RDP", ""),
("Pin menubar", "Pripnúť panel s ponukami"),
("Unpin menubar", "Uvoľniť panel s ponukami"),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -344,5 +344,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Use IP Whitelisting", ""), ("Use IP Whitelisting", ""),
("Network", ""), ("Network", ""),
("Enable RDP", ""), ("Enable RDP", ""),
("Pin menubar", ""),
("Unpin menubar", ""),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -357,5 +357,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Use IP Whitelisting", ""), ("Use IP Whitelisting", ""),
("Network", ""), ("Network", ""),
("Enable RDP", ""), ("Enable RDP", ""),
("Pin menubar", "Menü çubuğunu sabitle"),
("Unpin menubar", "Menü çubuğunun sabitlemesini kaldır"),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -344,5 +344,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Use IP Whitelisting", "只允許白名單上的IP訪問"), ("Use IP Whitelisting", "只允許白名單上的IP訪問"),
("Network", "網絡"), ("Network", "網絡"),
("Enable RDP", "允許RDP訪問"), ("Enable RDP", "允許RDP訪問"),
("Pin menubar", "固定菜單欄"),
("Unpin menubar", "取消固定菜單欄"),
].iter().cloned().collect(); ].iter().cloned().collect();
} }

View File

@ -344,5 +344,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Use IP Whitelisting", ""), ("Use IP Whitelisting", ""),
("Network", ""), ("Network", ""),
("Enable RDP", ""), ("Enable RDP", ""),
("Pin menubar", "Ghim thanh menu"),
("Unpin menubar", "Bỏ ghim thanh menu"),
].iter().cloned().collect(); ].iter().cloned().collect();
} }