From b8a382a0d837fe3116d0d2dfa5a8f18f4806fac4 Mon Sep 17 00:00:00 2001 From: fufesou Date: Fri, 23 Sep 2022 12:20:40 +0800 Subject: [PATCH] flutter_desktop: remove animation & adjust popup menu Signed-off-by: fufesou --- flutter/lib/common.dart | 12 + flutter/lib/common/widgets/address_book.dart | 7 +- flutter/lib/common/widgets/peer_card.dart | 52 +- flutter/lib/common/widgets/peers_view.dart | 12 +- .../lib/desktop/pages/connection_page.dart | 16 +- .../widgets/material_mod_popup_menu.dart | 5 +- flutter/lib/desktop/widgets/popup_menu.dart | 147 +++-- .../lib/desktop/widgets/remote_menubar.dart | 506 ++++++++++-------- 8 files changed, 478 insertions(+), 279 deletions(-) diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index 5cd54a0a8..8f8220db1 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -177,6 +177,12 @@ class MyTheme { ), splashColor: Colors.transparent, highlightColor: Colors.transparent, + splashFactory: isDesktop ? NoSplash.splashFactory : null, + textButtonTheme: isDesktop + ? TextButtonThemeData( + style: ButtonStyle(splashFactory: NoSplash.splashFactory), + ) + : null, ).copyWith( extensions: >[ ColorThemeExtension.light, @@ -192,6 +198,12 @@ class MyTheme { ), splashColor: Colors.transparent, highlightColor: Colors.transparent, + splashFactory: isDesktop ? NoSplash.splashFactory : null, + textButtonTheme: isDesktop + ? TextButtonThemeData( + style: ButtonStyle(splashFactory: NoSplash.splashFactory), + ) + : null, ).copyWith( extensions: >[ ColorThemeExtension.dark, diff --git a/flutter/lib/common/widgets/address_book.dart b/flutter/lib/common/widgets/address_book.dart index 9fac81723..ab5f924dd 100644 --- a/flutter/lib/common/widgets/address_book.dart +++ b/flutter/lib/common/widgets/address_book.dart @@ -11,7 +11,8 @@ import '../../mobile/pages/settings_page.dart'; import '../../models/platform_model.dart'; class AddressBook extends StatefulWidget { - const AddressBook({Key? key}) : super(key: key); + final EdgeInsets? menuPadding; + const AddressBook({Key? key, this.menuPadding}) : super(key: key); @override State createState() { @@ -180,7 +181,9 @@ class _AddressBookState extends State { Expanded( child: Align( alignment: Alignment.topLeft, - child: AddressBookPeersView()), + child: AddressBookPeersView( + menuPadding: widget.menuPadding, + )), ) ], )); diff --git a/flutter/lib/common/widgets/peer_card.dart b/flutter/lib/common/widgets/peer_card.dart index 9c0c997bc..2f6421880 100644 --- a/flutter/lib/common/widgets/peer_card.dart +++ b/flutter/lib/common/widgets/peer_card.dart @@ -14,7 +14,7 @@ import '../../desktop/widgets/popup_menu.dart'; class _PopupMenuTheme { static const Color commonColor = MyTheme.accent; // kMinInteractiveDimension - static const double height = 25.0; + static const double height = 20.0; static const double dividerHeight = 3.0; } @@ -319,8 +319,10 @@ class _PeerCardState extends State<_PeerCard> abstract class BasePeerCard extends StatelessWidget { final Peer peer; + final EdgeInsets? menuPadding; - BasePeerCard({required this.peer, Key? key}) : super(key: key); + BasePeerCard({required this.peer, this.menuPadding, Key? key}) + : super(key: key); @override Widget build(BuildContext context) { @@ -365,6 +367,7 @@ abstract class BasePeerCard extends StatelessWidget { isRDP: isRDP, ); }, + padding: menuPadding, dismissOnClicked: true, ); } @@ -414,17 +417,25 @@ abstract class BasePeerCard extends StatelessWidget { Expanded( child: Align( alignment: Alignment.centerRight, - child: IconButton( - padding: EdgeInsets.zero, - icon: const Icon(Icons.edit), - onPressed: () => _rdpDialog(id), - ), + child: Transform.scale( + scale: 0.8, + child: IconButton( + icon: const Icon(Icons.edit), + padding: EdgeInsets.zero, + onPressed: () { + if (Navigator.canPop(context)) { + Navigator.pop(context); + } + _rdpDialog(id); + }, + )), )) ], )), proc: () { connect(context, id, isRDP: true); }, + padding: menuPadding, dismissOnClicked: true, ); } @@ -439,6 +450,7 @@ abstract class BasePeerCard extends StatelessWidget { proc: () { bind.mainWol(id: id); }, + padding: menuPadding, dismissOnClicked: true, ); } @@ -447,6 +459,7 @@ abstract class BasePeerCard extends StatelessWidget { Future> _forceAlwaysRelayAction(String id) async { const option = 'force-always-relay'; return MenuEntrySwitch( + switchType: SwitchType.scheckbox, text: translate('Always connect via relay'), getter: () async { return (await bind.mainGetPeerOption(id: id, key: option)).isNotEmpty; @@ -461,7 +474,8 @@ abstract class BasePeerCard extends StatelessWidget { } await bind.mainSetPeerOption(id: id, key: option, value: value); }, - dismissOnClicked: false, + padding: menuPadding, + dismissOnClicked: true, ); } @@ -475,6 +489,7 @@ abstract class BasePeerCard extends StatelessWidget { proc: () { _rename(id, isAddressBook); }, + padding: menuPadding, dismissOnClicked: true, ); } @@ -494,6 +509,7 @@ abstract class BasePeerCard extends StatelessWidget { await reloadFunc(); }(); }, + padding: menuPadding, dismissOnClicked: true, ); } @@ -508,6 +524,7 @@ abstract class BasePeerCard extends StatelessWidget { proc: () { bind.mainForgetPassword(id: id); }, + padding: menuPadding, dismissOnClicked: true, ); } @@ -528,6 +545,7 @@ abstract class BasePeerCard extends StatelessWidget { } }(); }, + padding: menuPadding, dismissOnClicked: true, ); } @@ -549,6 +567,7 @@ abstract class BasePeerCard extends StatelessWidget { } }(); }, + padding: menuPadding, dismissOnClicked: true, ); } @@ -616,7 +635,8 @@ abstract class BasePeerCard extends StatelessWidget { } class RecentPeerCard extends BasePeerCard { - RecentPeerCard({required Peer peer, Key? key}) : super(peer: peer, key: key); + RecentPeerCard({required Peer peer, EdgeInsets? menuPadding, Key? key}) + : super(peer: peer, menuPadding: menuPadding, key: key); @override Future>> _buildMenuItems( @@ -645,8 +665,8 @@ class RecentPeerCard extends BasePeerCard { } class FavoritePeerCard extends BasePeerCard { - FavoritePeerCard({required Peer peer, Key? key}) - : super(peer: peer, key: key); + FavoritePeerCard({required Peer peer, EdgeInsets? menuPadding, Key? key}) + : super(peer: peer, menuPadding: menuPadding, key: key); @override Future>> _buildMenuItems( @@ -677,8 +697,8 @@ class FavoritePeerCard extends BasePeerCard { } class DiscoveredPeerCard extends BasePeerCard { - DiscoveredPeerCard({required Peer peer, Key? key}) - : super(peer: peer, key: key); + DiscoveredPeerCard({required Peer peer, EdgeInsets? menuPadding, Key? key}) + : super(peer: peer, menuPadding: menuPadding, key: key); @override Future>> _buildMenuItems( @@ -706,8 +726,8 @@ class DiscoveredPeerCard extends BasePeerCard { } class AddressBookPeerCard extends BasePeerCard { - AddressBookPeerCard({required Peer peer, Key? key}) - : super(peer: peer, key: key); + AddressBookPeerCard({required Peer peer, EdgeInsets? menuPadding, Key? key}) + : super(peer: peer, menuPadding: menuPadding, key: key); @override Future>> _buildMenuItems( @@ -748,6 +768,7 @@ class AddressBookPeerCard extends BasePeerCard { await gFFI.abModel.updateAb(); }(); }, + padding: super.menuPadding, dismissOnClicked: true, ); } @@ -762,6 +783,7 @@ class AddressBookPeerCard extends BasePeerCard { proc: () { _abEditTag(id); }, + padding: super.menuPadding, dismissOnClicked: true, ); } diff --git a/flutter/lib/common/widgets/peers_view.dart b/flutter/lib/common/widgets/peers_view.dart index cf9c4299a..63c29af6d 100644 --- a/flutter/lib/common/widgets/peers_view.dart +++ b/flutter/lib/common/widgets/peers_view.dart @@ -224,7 +224,7 @@ abstract class BasePeersView extends StatelessWidget { } class RecentPeersView extends BasePeersView { - RecentPeersView({Key? key}) + RecentPeersView({Key? key, EdgeInsets? menuPadding}) : super( key: key, name: 'recent peer', @@ -232,6 +232,7 @@ class RecentPeersView extends BasePeersView { offstageFunc: (Peer peer) => false, peerCardBuilder: (Peer peer) => RecentPeerCard( peer: peer, + menuPadding: menuPadding, ), initPeers: [], ); @@ -245,7 +246,7 @@ class RecentPeersView extends BasePeersView { } class FavoritePeersView extends BasePeersView { - FavoritePeersView({Key? key}) + FavoritePeersView({Key? key, EdgeInsets? menuPadding}) : super( key: key, name: 'favorite peer', @@ -253,6 +254,7 @@ class FavoritePeersView extends BasePeersView { offstageFunc: (Peer peer) => false, peerCardBuilder: (Peer peer) => FavoritePeerCard( peer: peer, + menuPadding: menuPadding, ), initPeers: [], ); @@ -266,7 +268,7 @@ class FavoritePeersView extends BasePeersView { } class DiscoveredPeersView extends BasePeersView { - DiscoveredPeersView({Key? key}) + DiscoveredPeersView({Key? key, EdgeInsets? menuPadding}) : super( key: key, name: 'discovered peer', @@ -274,6 +276,7 @@ class DiscoveredPeersView extends BasePeersView { offstageFunc: (Peer peer) => false, peerCardBuilder: (Peer peer) => DiscoveredPeerCard( peer: peer, + menuPadding: menuPadding, ), initPeers: [], ); @@ -287,7 +290,7 @@ class DiscoveredPeersView extends BasePeersView { } class AddressBookPeersView extends BasePeersView { - AddressBookPeersView({Key? key}) + AddressBookPeersView({Key? key, EdgeInsets? menuPadding}) : super( key: key, name: 'address book peer', @@ -296,6 +299,7 @@ class AddressBookPeersView extends BasePeersView { !_hitTag(gFFI.abModel.selectedTags, peer.tags), peerCardBuilder: (Peer peer) => AddressBookPeerCard( peer: peer, + menuPadding: menuPadding, ), initPeers: _loadPeers(), ); diff --git a/flutter/lib/desktop/pages/connection_page.dart b/flutter/lib/desktop/pages/connection_page.dart index 6a8c58f7b..9ff7befd7 100644 --- a/flutter/lib/desktop/pages/connection_page.dart +++ b/flutter/lib/desktop/pages/connection_page.dart @@ -74,10 +74,18 @@ class _ConnectionPageState extends State { translate('Address Book') ], children: [ - RecentPeersView(), - FavoritePeersView(), - DiscoveredPeersView(), - const AddressBook(), + RecentPeersView( + menuPadding: EdgeInsets.only(left: 12.0, right: 3.0), + ), + FavoritePeersView( + menuPadding: EdgeInsets.only(left: 12.0, right: 3.0), + ), + DiscoveredPeersView( + menuPadding: EdgeInsets.only(left: 12.0, right: 3.0), + ), + const AddressBook( + menuPadding: EdgeInsets.only(left: 12.0, right: 3.0), + ), ], )), ], diff --git a/flutter/lib/desktop/widgets/material_mod_popup_menu.dart b/flutter/lib/desktop/widgets/material_mod_popup_menu.dart index 1345f72f1..776a2b756 100644 --- a/flutter/lib/desktop/widgets/material_mod_popup_menu.dart +++ b/flutter/lib/desktop/widgets/material_mod_popup_menu.dart @@ -14,7 +14,8 @@ import 'package:flutter/material.dart'; // void setState(VoidCallback fn) { } // enum Menu { itemOne, itemTwo, itemThree, itemFour } -const Duration _kMenuDuration = Duration(milliseconds: 300); +// const Duration _kMenuDuration = Duration(milliseconds: 300); +const Duration _kMenuDuration = Duration(milliseconds: 0); const double _kMenuCloseIntervalEnd = 2.0 / 3.0; const double _kMenuHorizontalPadding = 16.0; const double _kMenuDividerHeight = 16.0; @@ -22,7 +23,7 @@ const double _kMenuDividerHeight = 16.0; const double _kMenuMinWidth = 2.0 * _kMenuWidthStep; const double _kMenuMaxWidth = double.infinity; // const double _kMenuVerticalPadding = 8.0; -const double _kMenuVerticalPadding = 0.0; +const double _kMenuVerticalPadding = 8.0; const double _kMenuWidthStep = 0.0; //const double _kMenuScreenPadding = 8.0; const double _kMenuScreenPadding = 0.0; diff --git a/flutter/lib/desktop/widgets/popup_menu.dart b/flutter/lib/desktop/widgets/popup_menu.dart index 3814561ee..84fd69b0e 100644 --- a/flutter/lib/desktop/widgets/popup_menu.dart +++ b/flutter/lib/desktop/widgets/popup_menu.dart @@ -78,7 +78,8 @@ class MyPopupMenuItemState> duration: kThemeChangeDuration, child: Container( alignment: AlignmentDirectional.centerStart, - constraints: BoxConstraints(minHeight: widget.height), + constraints: BoxConstraints( + minHeight: widget.height, maxHeight: widget.height), padding: widget.padding ?? const EdgeInsets.symmetric(horizontal: 16), child: widget.child, @@ -156,12 +157,14 @@ class MenuEntryRadios extends MenuEntryBase { final RadioCurOptionGetter curOptionGetter; final RadioOptionSetter optionSetter; final RxString _curOption = "".obs; + final EdgeInsets? padding; MenuEntryRadios({ required this.text, required this.optionsGetter, required this.curOptionGetter, required this.optionSetter, + this.padding, dismissOnClicked = false, RxBool? enabled, }) : super(dismissOnClicked: dismissOnClicked, enabled: enabled) { @@ -189,8 +192,10 @@ class MenuEntryRadios extends MenuEntryBase { height: conf.height, child: TextButton( child: Container( + padding: padding, alignment: AlignmentDirectional.centerStart, - constraints: BoxConstraints(minHeight: conf.height), + constraints: + BoxConstraints(minHeight: conf.height, maxHeight: conf.height), child: Row( children: [ Text( @@ -202,17 +207,22 @@ class MenuEntryRadios extends MenuEntryBase { ), Expanded( child: Align( - alignment: Alignment.centerRight, - child: SizedBox( - width: 20.0, - height: 20.0, - child: Obx(() => opt.value == curOption.value - ? Icon( - Icons.check, - color: conf.commonColor, - ) - : const SizedBox.shrink())), - )), + alignment: Alignment.centerRight, + child: Transform.scale( + scale: MenuConfig.iconScale, + child: Obx(() => opt.value == curOption.value + ? IconButton( + padding: const EdgeInsets.fromLTRB( + 8.0, 0.0, 8.0, 0.0), + hoverColor: Colors.transparent, + focusColor: Colors.transparent, + onPressed: () {}, + icon: Icon( + Icons.check, + color: conf.commonColor, + )) + : const SizedBox.shrink()), + ))), ], ), ), @@ -239,12 +249,14 @@ class MenuEntrySubRadios extends MenuEntryBase { final RadioCurOptionGetter curOptionGetter; final RadioOptionSetter optionSetter; final RxString _curOption = "".obs; + final EdgeInsets? padding; MenuEntrySubRadios({ required this.text, required this.optionsGetter, required this.curOptionGetter, required this.optionSetter, + this.padding, dismissOnClicked = false, RxBool? enabled, }) : super( @@ -275,8 +287,10 @@ class MenuEntrySubRadios extends MenuEntryBase { height: conf.height, child: TextButton( child: Container( + padding: padding, alignment: AlignmentDirectional.centerStart, - constraints: BoxConstraints(minHeight: conf.height), + constraints: + BoxConstraints(minHeight: conf.height, maxHeight: conf.height), child: Row( children: [ Text( @@ -289,14 +303,18 @@ class MenuEntrySubRadios extends MenuEntryBase { Expanded( child: Align( alignment: Alignment.centerRight, - child: SizedBox( - width: 20.0, - height: 20.0, + child: Transform.scale( + scale: MenuConfig.iconScale, child: Obx(() => opt.value == curOption.value - ? Icon( - Icons.check, - color: conf.commonColor, - ) + ? IconButton( + padding: EdgeInsets.zero, + hoverColor: Colors.transparent, + focusColor: Colors.transparent, + onPressed: () {}, + icon: Icon( + Icons.check, + color: conf.commonColor, + )) : const SizedBox.shrink())), )), ], @@ -318,7 +336,7 @@ class MenuEntrySubRadios extends MenuEntryBase { return [ PopupMenuChildrenItem( enabled: super.enabled, - padding: EdgeInsets.zero, + padding: padding, height: conf.height, itemBuilder: (BuildContext context) => options.map((opt) => _buildSecondMenu(context, conf, opt)).toList(), @@ -345,22 +363,31 @@ class MenuEntrySubRadios extends MenuEntryBase { } } +enum SwitchType { + sswitch, + scheckbox, +} + typedef SwitchGetter = Future Function(); typedef SwitchSetter = Future Function(bool); abstract class MenuEntrySwitchBase extends MenuEntryBase { + final SwitchType switchType; final String text; + final EdgeInsets? padding; Rx? textStyle; MenuEntrySwitchBase({ + required this.switchType, required this.text, required dismissOnClicked, this.textStyle, + this.padding, RxBool? enabled, }) : super(dismissOnClicked: dismissOnClicked, enabled: enabled); RxBool get curOption; - Future setOption(bool option); + Future setOption(bool? option); @override List> build( @@ -376,6 +403,7 @@ abstract class MenuEntrySwitchBase extends MenuEntryBase { height: conf.height, child: TextButton( child: Container( + padding: padding, alignment: AlignmentDirectional.centerStart, height: conf.height, child: Row(children: [ @@ -386,16 +414,33 @@ abstract class MenuEntrySwitchBase extends MenuEntryBase { Expanded( child: Align( alignment: Alignment.centerRight, - child: Obx(() => Switch( - value: curOption.value, - onChanged: (v) { - if (super.dismissOnClicked && - Navigator.canPop(context)) { - Navigator.pop(context); - } - setOption(v); - }, - )), + child: Transform.scale( + scale: MenuConfig.iconScale, + child: Obx(() { + if (switchType == SwitchType.sswitch) { + return Switch( + value: curOption.value, + onChanged: (v) { + if (super.dismissOnClicked && + Navigator.canPop(context)) { + Navigator.pop(context); + } + setOption(v); + }, + ); + } else { + return Checkbox( + value: curOption.value, + onChanged: (v) { + if (super.dismissOnClicked && + Navigator.canPop(context)) { + Navigator.pop(context); + } + setOption(v); + }, + ); + } + })), )) ])), onPressed: () { @@ -416,15 +461,19 @@ class MenuEntrySwitch extends MenuEntrySwitchBase { final RxBool _curOption = false.obs; MenuEntrySwitch({ + required SwitchType switchType, required String text, required this.getter, required this.setter, Rx? textStyle, + EdgeInsets? padding, dismissOnClicked = false, RxBool? enabled, }) : super( + switchType: switchType, text: text, textStyle: textStyle, + padding: padding, dismissOnClicked: dismissOnClicked, enabled: enabled, ) { @@ -436,11 +485,13 @@ class MenuEntrySwitch extends MenuEntrySwitchBase { @override RxBool get curOption => _curOption; @override - setOption(bool option) async { - await setter(option); - final opt = await getter(); - if (_curOption.value != opt) { - _curOption.value = opt; + setOption(bool? option) async { + if (option != null) { + await setter(option); + final opt = await getter(); + if (_curOption.value != opt) { + _curOption.value = opt; + } } } } @@ -453,32 +504,40 @@ class MenuEntrySwitch2 extends MenuEntrySwitchBase { final SwitchSetter setter; MenuEntrySwitch2({ + required SwitchType switchType, required String text, required this.getter, required this.setter, Rx? textStyle, + EdgeInsets? padding, dismissOnClicked = false, RxBool? enabled, }) : super( + switchType: switchType, text: text, textStyle: textStyle, + padding: padding, dismissOnClicked: dismissOnClicked); @override RxBool get curOption => getter(); @override - setOption(bool option) async { - await setter(option); + setOption(bool? option) async { + if (option != null) { + await setter(option); + } } } class MenuEntrySubMenu extends MenuEntryBase { final String text; final List> entries; + final EdgeInsets? padding; MenuEntrySubMenu({ required this.text, required this.entries, + this.padding, RxBool? enabled, }) : super(enabled: enabled); @@ -490,7 +549,7 @@ class MenuEntrySubMenu extends MenuEntryBase { PopupMenuChildrenItem( enabled: super.enabled, height: conf.height, - padding: EdgeInsets.zero, + padding: padding, position: mod_menu.PopupMenuPosition.overSide, itemBuilder: (BuildContext context) => entries .map((entry) => entry.build(context, conf)) @@ -522,10 +581,12 @@ class MenuEntrySubMenu extends MenuEntryBase { class MenuEntryButton extends MenuEntryBase { final Widget Function(TextStyle? style) childBuilder; Function() proc; + final EdgeInsets? padding; MenuEntryButton({ required this.childBuilder, required this.proc, + this.padding, dismissOnClicked = false, RxBool? enabled, }) : super( @@ -553,8 +614,10 @@ class MenuEntryButton extends MenuEntryBase { } : null, child: Container( + padding: padding, alignment: AlignmentDirectional.centerStart, - constraints: BoxConstraints(minHeight: conf.height), + constraints: + BoxConstraints(minHeight: conf.height, maxHeight: conf.height), child: childBuilder( super.enabled!.value ? enabledStyle : disabledStyle), ), diff --git a/flutter/lib/desktop/widgets/remote_menubar.dart b/flutter/lib/desktop/widgets/remote_menubar.dart index 070ad217b..67e69a1d9 100644 --- a/flutter/lib/desktop/widgets/remote_menubar.dart +++ b/flutter/lib/desktop/widgets/remote_menubar.dart @@ -19,7 +19,7 @@ import './material_mod_popup_menu.dart' as mod_menu; class _MenubarTheme { static const Color commonColor = MyTheme.accent; // kMinInteractiveDimension - static const double height = 25.0; + static const double height = 20.0; static const double dividerHeight = 12.0; } @@ -367,31 +367,41 @@ class _RemoteMenubarState extends State { List> _getControlMenu(BuildContext context) { final pi = widget.ffi.ffiModel.pi; final perms = widget.ffi.ffiModel.permissions; - + const EdgeInsets padding = EdgeInsets.only(left: 14.0, right: 5.0); final List> displayMenu = []; displayMenu.addAll([ MenuEntryButton( - 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), - ), - )) - ], - ), + childBuilder: (TextStyle? style) => Container( + alignment: AlignmentDirectional.center, + height: _MenubarTheme.height, + child: Row( + children: [ + Text( + translate('OS Password'), + style: style, + ), + Expanded( + child: Align( + alignment: Alignment.centerRight, + child: Transform.scale( + scale: 0.8, + child: IconButton( + padding: EdgeInsets.zero, + icon: const Icon(Icons.edit), + onPressed: () { + if (Navigator.canPop(context)) { + Navigator.pop(context); + } + showSetOSPassword( + widget.id, false, widget.ffi.dialogManager); + })), + )) + ], + )), proc: () { showSetOSPassword(widget.id, false, widget.ffi.dialogManager); }, + padding: padding, dismissOnClicked: true, ), MenuEntryButton( @@ -402,6 +412,7 @@ class _RemoteMenubarState extends State { proc: () { connect(context, widget.id, isFileTransfer: true); }, + padding: padding, dismissOnClicked: true, ), MenuEntryButton( @@ -409,6 +420,7 @@ class _RemoteMenubarState extends State { translate('TCP Tunneling'), style: style, ), + padding: padding, proc: () { connect(context, widget.id, isTcpTunneling: true); }, @@ -427,6 +439,7 @@ class _RemoteMenubarState extends State { proc: () { showAuditDialog(widget.id, widget.ffi.dialogManager); }, + padding: padding, dismissOnClicked: true, ), ); @@ -443,6 +456,7 @@ class _RemoteMenubarState extends State { proc: () { bind.sessionCtrlAltDel(id: widget.id); }, + padding: padding, dismissOnClicked: true, )); } @@ -459,6 +473,7 @@ class _RemoteMenubarState extends State { proc: () { showRestartRemoteDevice(pi, widget.id, gFFI.dialogManager); }, + padding: padding, dismissOnClicked: true, )); } @@ -472,6 +487,7 @@ class _RemoteMenubarState extends State { proc: () { bind.sessionLockScreen(id: widget.id); }, + padding: padding, dismissOnClicked: true, )); @@ -489,6 +505,7 @@ class _RemoteMenubarState extends State { value: '${blockInput.value ? "un" : ""}block-input'); blockInput.value = !blockInput.value; }, + padding: padding, dismissOnClicked: true, )); } @@ -503,6 +520,7 @@ class _RemoteMenubarState extends State { proc: () { bind.sessionRefresh(id: widget.id); }, + padding: padding, dismissOnClicked: true, )); } @@ -523,6 +541,7 @@ class _RemoteMenubarState extends State { // } // }(); // }, + // padding: padding, // dismissOnClicked: true, // )); // } @@ -535,6 +554,7 @@ class _RemoteMenubarState extends State { proc: () { widget.ffi.cursorModel.reset(); }, + padding: padding, dismissOnClicked: true, )); } @@ -543,125 +563,155 @@ class _RemoteMenubarState extends State { } List> _getDisplayMenu(dynamic futureData) { + const EdgeInsets padding = EdgeInsets.only(left: 18.0, right: 8.0); final displayMenu = [ MenuEntryRadios( - text: translate('Ratio'), - optionsGetter: () => [ - MenuEntryRadioOption( - text: translate('Scale original'), value: 'original'), - MenuEntryRadioOption( - text: translate('Scale adaptive'), value: 'adaptive'), - ], - curOptionGetter: () async { - return await bind.sessionGetOption( - id: widget.id, arg: 'view-style') ?? - 'adaptive'; - }, - optionSetter: (String oldValue, String newValue) async { - await bind.sessionPeerOption( - id: widget.id, name: "view-style", value: newValue); - widget.ffi.canvasModel.updateViewStyle(); - }), + text: translate('Ratio'), + optionsGetter: () => [ + MenuEntryRadioOption( + text: translate('Scale original'), + value: 'original', + dismissOnClicked: true, + ), + MenuEntryRadioOption( + text: translate('Scale adaptive'), + value: 'adaptive', + dismissOnClicked: true, + ), + ], + curOptionGetter: () async { + return await bind.sessionGetOption( + id: widget.id, arg: 'view-style') ?? + 'adaptive'; + }, + optionSetter: (String oldValue, String newValue) async { + await bind.sessionPeerOption( + id: widget.id, name: "view-style", value: newValue); + widget.ffi.canvasModel.updateViewStyle(); + }, + padding: padding, + dismissOnClicked: true, + ), MenuEntryDivider(), MenuEntryRadios( - text: translate('Scroll Style'), - optionsGetter: () => [ - MenuEntryRadioOption( - text: translate('ScrollAuto'), value: 'scrollauto'), - MenuEntryRadioOption( - text: translate('Scrollbar'), value: 'scrollbar'), - ], - curOptionGetter: () async { - return await bind.sessionGetOption( - id: widget.id, arg: 'scroll-style') ?? - ''; - }, - optionSetter: (String oldValue, String newValue) async { - await bind.sessionPeerOption( - id: widget.id, name: "scroll-style", value: newValue); - widget.ffi.canvasModel.updateScrollStyle(); - }), + text: translate('Scroll Style'), + optionsGetter: () => [ + MenuEntryRadioOption( + text: translate('ScrollAuto'), + value: 'scrollauto', + dismissOnClicked: true, + ), + MenuEntryRadioOption( + text: translate('Scrollbar'), + value: 'scrollbar', + dismissOnClicked: true, + ), + ], + curOptionGetter: () async { + return await bind.sessionGetOption( + id: widget.id, arg: 'scroll-style') ?? + ''; + }, + optionSetter: (String oldValue, String newValue) async { + await bind.sessionPeerOption( + id: widget.id, name: "scroll-style", value: newValue); + widget.ffi.canvasModel.updateScrollStyle(); + }, + padding: padding, + dismissOnClicked: true, + ), MenuEntryDivider(), MenuEntryRadios( - text: translate('Image Quality'), - optionsGetter: () => [ - MenuEntryRadioOption( - text: translate('Good image quality'), value: 'best'), - MenuEntryRadioOption( - text: translate('Balanced'), value: 'balanced'), - MenuEntryRadioOption( - text: translate('Optimize reaction time'), value: 'low'), - MenuEntryRadioOption( - text: translate('Custom'), - value: 'custom', - dismissOnClicked: true), - ], - curOptionGetter: () async { - String quality = - await bind.sessionGetImageQuality(id: widget.id) ?? 'balanced'; - if (quality == '') quality = 'balanced'; - return quality; - }, - optionSetter: (String oldValue, String newValue) async { - if (oldValue != newValue) { - await bind.sessionSetImageQuality(id: widget.id, value: newValue); - } + text: translate('Image Quality'), + optionsGetter: () => [ + MenuEntryRadioOption( + text: translate('Good image quality'), + value: 'best', + dismissOnClicked: true, + ), + MenuEntryRadioOption( + text: translate('Balanced'), + value: 'balanced', + dismissOnClicked: true, + ), + MenuEntryRadioOption( + text: translate('Optimize reaction time'), + value: 'low', + dismissOnClicked: true, + ), + MenuEntryRadioOption( + text: translate('Custom'), + value: 'custom', + dismissOnClicked: true), + ], + curOptionGetter: () async { + String quality = + await bind.sessionGetImageQuality(id: widget.id) ?? 'balanced'; + if (quality == '') quality = 'balanced'; + return quality; + }, + optionSetter: (String oldValue, String newValue) async { + if (oldValue != newValue) { + await bind.sessionSetImageQuality(id: widget.id, value: newValue); + } - if (newValue == 'custom') { - final btnCancel = msgBoxButton(translate('Close'), () { - widget.ffi.dialogManager.dismissAll(); - }); - final quality = - await bind.sessionGetCustomImageQuality(id: widget.id); - double initValue = quality != null && quality.isNotEmpty - ? quality[0].toDouble() - : 50.0; - const minValue = 10.0; - const maxValue = 100.0; - if (initValue < minValue) { - initValue = minValue; - } - if (initValue > maxValue) { - initValue = maxValue; - } - final RxDouble sliderValue = RxDouble(initValue); - final rxReplay = rxdart.ReplaySubject(); - rxReplay - .throttleTime(const Duration(milliseconds: 1000), - trailing: true, leading: false) - .listen((double v) { - () async { - await bind.sessionSetCustomImageQuality( - id: widget.id, value: v.toInt()); - }(); - }); - final slider = Obx(() { - return Slider( - value: sliderValue.value, - min: minValue, - max: maxValue, - divisions: 90, - onChanged: (double value) { - sliderValue.value = value; - rxReplay.add(value); - }, - ); - }); - final content = Row( - children: [ - slider, - SizedBox( - width: 90, - child: Obx(() => Text( - '${sliderValue.value.round()}% Bitrate', - style: const TextStyle(fontSize: 15), - ))) - ], - ); - msgBoxCommon(widget.ffi.dialogManager, 'Custom Image Quality', - content, [btnCancel]); + if (newValue == 'custom') { + final btnCancel = msgBoxButton(translate('Close'), () { + widget.ffi.dialogManager.dismissAll(); + }); + final quality = + await bind.sessionGetCustomImageQuality(id: widget.id); + double initValue = quality != null && quality.isNotEmpty + ? quality[0].toDouble() + : 50.0; + const minValue = 10.0; + const maxValue = 100.0; + if (initValue < minValue) { + initValue = minValue; } - }), + if (initValue > maxValue) { + initValue = maxValue; + } + final RxDouble sliderValue = RxDouble(initValue); + final rxReplay = rxdart.ReplaySubject(); + rxReplay + .throttleTime(const Duration(milliseconds: 1000), + trailing: true, leading: false) + .listen((double v) { + () async { + await bind.sessionSetCustomImageQuality( + id: widget.id, value: v.toInt()); + }(); + }); + final slider = Obx(() { + return Slider( + value: sliderValue.value, + min: minValue, + max: maxValue, + divisions: 90, + onChanged: (double value) { + sliderValue.value = value; + rxReplay.add(value); + }, + ); + }); + final content = Row( + children: [ + slider, + SizedBox( + width: 90, + child: Obx(() => Text( + '${sliderValue.value.round()}% Bitrate', + style: const TextStyle(fontSize: 15), + ))) + ], + ); + msgBoxCommon(widget.ffi.dialogManager, 'Custom Image Quality', + content, [btnCancel]); + } + }, + padding: padding, + ), MenuEntryDivider(), ]; @@ -677,30 +727,49 @@ class _RemoteMenubarState extends State { } finally {} if (codecs.length == 2 && (codecs[0] || codecs[1])) { displayMenu.add(MenuEntryRadios( - text: translate('Codec Preference'), - optionsGetter: () { - final list = [ - MenuEntryRadioOption(text: translate('Auto'), value: 'auto'), - MenuEntryRadioOption(text: 'VP9', value: 'vp9'), - ]; - if (codecs[0]) { - list.add(MenuEntryRadioOption(text: 'H264', value: 'h264')); - } - if (codecs[1]) { - list.add(MenuEntryRadioOption(text: 'H265', value: 'h265')); - } - return list; - }, - curOptionGetter: () async { - return await bind.sessionGetOption( - id: widget.id, arg: 'codec-preference') ?? - 'auto'; - }, - optionSetter: (String oldValue, String newValue) async { - await bind.sessionPeerOption( - id: widget.id, name: "codec-preference", value: newValue); - bind.sessionChangePreferCodec(id: widget.id); - })); + text: translate('Codec Preference'), + optionsGetter: () { + final list = [ + MenuEntryRadioOption( + text: translate('Auto'), + value: 'auto', + dismissOnClicked: true, + ), + MenuEntryRadioOption( + text: 'VP9', + value: 'vp9', + dismissOnClicked: true, + ), + ]; + if (codecs[0]) { + list.add(MenuEntryRadioOption( + text: 'H264', + value: 'h264', + dismissOnClicked: true, + )); + } + if (codecs[1]) { + list.add(MenuEntryRadioOption( + text: 'H265', + value: 'h265', + dismissOnClicked: true, + )); + } + return list; + }, + curOptionGetter: () async { + return await bind.sessionGetOption( + id: widget.id, arg: 'codec-preference') ?? + 'auto'; + }, + optionSetter: (String oldValue, String newValue) async { + await bind.sessionPeerOption( + id: widget.id, name: "codec-preference", value: newValue); + bind.sessionChangePreferCodec(id: widget.id); + }, + padding: padding, + dismissOnClicked: true, + )); } } @@ -708,62 +777,74 @@ class _RemoteMenubarState extends State { displayMenu.add(() { final state = ShowRemoteCursorState.find(widget.id); return MenuEntrySwitch2( - text: translate('Show remote cursor'), - getter: () { - return state; - }, - setter: (bool v) async { - state.value = v; - await bind.sessionToggleOption( - id: widget.id, value: 'show-remote-cursor'); - }); + switchType: SwitchType.scheckbox, + text: translate('Show remote cursor'), + getter: () { + return state; + }, + setter: (bool v) async { + state.value = v; + await bind.sessionToggleOption( + id: widget.id, value: 'show-remote-cursor'); + }, + padding: padding, + dismissOnClicked: true, + ); }()); /// Show quality monitor displayMenu.add(MenuEntrySwitch( - text: translate('Show quality monitor'), - getter: () async { - return bind.sessionGetToggleOptionSync( - id: widget.id, arg: 'show-quality-monitor'); - }, - setter: (bool v) async { - await bind.sessionToggleOption( - id: widget.id, value: 'show-quality-monitor'); - widget.ffi.qualityMonitorModel.checkShowQualityMonitor(widget.id); - })); + switchType: SwitchType.scheckbox, + text: translate('Show quality monitor'), + getter: () async { + return bind.sessionGetToggleOptionSync( + id: widget.id, arg: 'show-quality-monitor'); + }, + setter: (bool v) async { + await bind.sessionToggleOption( + id: widget.id, value: 'show-quality-monitor'); + widget.ffi.qualityMonitorModel.checkShowQualityMonitor(widget.id); + }, + padding: padding, + dismissOnClicked: true, + )); final perms = widget.ffi.ffiModel.permissions; final pi = widget.ffi.ffiModel.pi; if (perms['audio'] != false) { - displayMenu.add(_createSwitchMenuEntry('Mute', 'disable-audio')); + displayMenu + .add(_createSwitchMenuEntry('Mute', 'disable-audio', padding, true)); } if (Platform.isWindows && pi.platform == 'Windows' && perms['file'] != false) { displayMenu.add(_createSwitchMenuEntry( - 'Allow file copy and paste', 'enable-file-transfer')); + 'Allow file copy and paste', 'enable-file-transfer', padding, true)); } if (perms['keyboard'] != false) { if (perms['clipboard'] != false) { - displayMenu.add( - _createSwitchMenuEntry('Disable clipboard', 'disable-clipboard')); + displayMenu.add(_createSwitchMenuEntry( + 'Disable clipboard', 'disable-clipboard', padding, true)); } displayMenu.add(_createSwitchMenuEntry( - 'Lock after session end', 'lock-after-session-end')); + 'Lock after session end', 'lock-after-session-end', padding, true)); if (pi.platform == 'Windows') { displayMenu.add(MenuEntrySwitch2( - dismissOnClicked: true, - text: translate('Privacy mode'), - getter: () { - return PrivacyModeState.find(widget.id); - }, - setter: (bool v) async { - await bind.sessionToggleOption( - id: widget.id, value: 'privacy-mode'); - })); + switchType: SwitchType.scheckbox, + text: translate('Privacy mode'), + getter: () { + return PrivacyModeState.find(widget.id); + }, + setter: (bool v) async { + await bind.sessionToggleOption( + id: widget.id, value: 'privacy-mode'); + }, + padding: padding, + dismissOnClicked: true, + )); } } return displayMenu; @@ -772,34 +853,39 @@ class _RemoteMenubarState extends State { List> _getKeyboardMenu() { final keyboardMenu = [ MenuEntryRadios( - text: translate('Ratio'), - optionsGetter: () => [ - MenuEntryRadioOption( - text: translate('Legacy mode'), value: 'legacy'), - MenuEntryRadioOption(text: translate('Map mode'), value: 'map'), - ], - curOptionGetter: () async { - return await bind.sessionGetKeyboardName(id: widget.id); - }, - optionSetter: (String oldValue, String newValue) async { - await bind.sessionSetKeyboardMode( - id: widget.id, keyboardMode: newValue); - widget.ffi.canvasModel.updateViewStyle(); - }) + text: translate('Ratio'), + optionsGetter: () => [ + MenuEntryRadioOption(text: translate('Legacy mode'), value: 'legacy'), + MenuEntryRadioOption(text: translate('Map mode'), value: 'map'), + ], + curOptionGetter: () async { + return await bind.sessionGetKeyboardName(id: widget.id); + }, + optionSetter: (String oldValue, String newValue) async { + await bind.sessionSetKeyboardMode( + id: widget.id, keyboardMode: newValue); + widget.ffi.canvasModel.updateViewStyle(); + }, + ) ]; return keyboardMenu; } - MenuEntrySwitch _createSwitchMenuEntry(String text, String option) { + MenuEntrySwitch _createSwitchMenuEntry( + String text, String option, EdgeInsets? padding, bool dismissOnClicked) { return MenuEntrySwitch( - text: translate(text), - getter: () async { - return bind.sessionGetToggleOptionSync(id: widget.id, arg: option); - }, - setter: (bool v) async { - await bind.sessionToggleOption(id: widget.id, value: option); - }); + switchType: SwitchType.scheckbox, + text: translate(text), + getter: () async { + return bind.sessionGetToggleOptionSync(id: widget.id, arg: option); + }, + setter: (bool v) async { + await bind.sessionToggleOption(id: widget.id, value: option); + }, + padding: padding, + dismissOnClicked: dismissOnClicked, + ); } }