diff --git a/flutter/lib/desktop/widgets/popup_menu.dart b/flutter/lib/desktop/widgets/popup_menu.dart index 00f940fdb..3d5fdf7f6 100644 --- a/flutter/lib/desktop/widgets/popup_menu.dart +++ b/flutter/lib/desktop/widgets/popup_menu.dart @@ -97,15 +97,18 @@ class MenuConfig { } abstract class MenuEntryBase { - mod_menu.PopupMenuEntry build(BuildContext context, MenuConfig conf); + List> build(BuildContext context, MenuConfig conf); } class MenuEntryDivider extends MenuEntryBase { @override - mod_menu.PopupMenuEntry build(BuildContext context, MenuConfig conf) { - return mod_menu.PopupMenuDivider( - height: conf.dividerHeight, - ); + List> build( + BuildContext context, MenuConfig conf) { + return [ + mod_menu.PopupMenuDivider( + height: conf.dividerHeight, + ) + ]; } } @@ -113,6 +116,85 @@ typedef RadioOptionsGetter = List> Function(); typedef RadioCurOptionGetter = Future Function(); typedef RadioOptionSetter = Future Function(String); +class MenuEntryRadioUtils {} + +class MenuEntryRadios extends MenuEntryBase { + final String text; + final RadioOptionsGetter optionsGetter; + final RadioCurOptionGetter curOptionGetter; + final RadioOptionSetter optionSetter; + final RxString _curOption = "".obs; + + MenuEntryRadios( + {required this.text, + required this.optionsGetter, + required this.curOptionGetter, + required this.optionSetter}) { + () async { + _curOption.value = await curOptionGetter(); + }(); + } + + List> get options => optionsGetter(); + RxString get curOption => _curOption; + setOption(String option) async { + await optionSetter(option); + final opt = await curOptionGetter(); + if (_curOption.value != opt) { + _curOption.value = opt; + } + } + + mod_menu.PopupMenuEntry _buildMenuItem( + BuildContext context, MenuConfig conf, Tuple2 opt) { + return mod_menu.PopupMenuItem( + padding: EdgeInsets.zero, + height: conf.height, + child: TextButton( + child: Container( + alignment: AlignmentDirectional.centerStart, + constraints: BoxConstraints(minHeight: conf.height), + child: Row( + children: [ + Text( + opt.item1, + style: const TextStyle( + color: Colors.black, + fontSize: MenuConfig.fontSize, + fontWeight: FontWeight.normal), + ), + Expanded( + child: Align( + alignment: Alignment.centerRight, + child: SizedBox( + width: 20.0, + height: 20.0, + child: Obx(() => opt.item2 == curOption.value + ? Icon( + Icons.check, + color: conf.commonColor, + ) + : const SizedBox.shrink())), + )), + ], + ), + ), + onPressed: () { + if (opt.item2 != curOption.value) { + setOption(opt.item2); + } + }, + ), + ); + } + + @override + List> build( + BuildContext context, MenuConfig conf) { + return options.map((opt) => _buildMenuItem(context, conf, opt)).toList(); + } +} + class MenuEntrySubRadios extends MenuEntryBase { final String text; final RadioOptionsGetter optionsGetter; @@ -151,23 +233,26 @@ class MenuEntrySubRadios extends MenuEntryBase { constraints: BoxConstraints(minHeight: conf.height), child: Row( children: [ - SizedBox( - width: 20.0, - height: 20.0, - child: Obx(() => opt.item2 == curOption.value - ? Icon( - Icons.check, - color: conf.commonColor, - ) - : const SizedBox.shrink())), - const SizedBox(width: MenuConfig.midPadding), Text( opt.item1, style: const TextStyle( color: Colors.black, fontSize: MenuConfig.fontSize, fontWeight: FontWeight.normal), - ) + ), + Expanded( + child: Align( + alignment: Alignment.centerRight, + child: SizedBox( + width: 20.0, + height: 20.0, + child: Obx(() => opt.item2 == curOption.value + ? Icon( + Icons.check, + color: conf.commonColor, + ) + : const SizedBox.shrink())), + )), ], ), ), @@ -181,31 +266,34 @@ class MenuEntrySubRadios extends MenuEntryBase { } @override - mod_menu.PopupMenuEntry build(BuildContext context, MenuConfig conf) { - return PopupMenuChildrenItem( - padding: EdgeInsets.zero, - height: conf.height, - itemBuilder: (BuildContext context) => - options.map((opt) => _buildSecondMenu(context, conf, opt)).toList(), - child: Row(children: [ - const SizedBox(width: MenuConfig.midPadding), - Text( - text, - style: const TextStyle( - color: Colors.black, - fontSize: MenuConfig.fontSize, - fontWeight: FontWeight.normal), - ), - Expanded( - child: Align( - alignment: Alignment.centerRight, - child: Icon( - Icons.keyboard_arrow_right, - color: conf.commonColor, + List> build( + BuildContext context, MenuConfig conf) { + return [ + PopupMenuChildrenItem( + padding: EdgeInsets.zero, + height: conf.height, + itemBuilder: (BuildContext context) => + options.map((opt) => _buildSecondMenu(context, conf, opt)).toList(), + child: Row(children: [ + const SizedBox(width: MenuConfig.midPadding), + Text( + text, + style: const TextStyle( + color: Colors.black, + fontSize: MenuConfig.fontSize, + fontWeight: FontWeight.normal), ), - )) - ]), - ); + Expanded( + child: Align( + alignment: Alignment.centerRight, + child: Icon( + Icons.keyboard_arrow_right, + color: conf.commonColor, + ), + )) + ]), + ) + ]; } } @@ -221,35 +309,38 @@ abstract class MenuEntrySwitchBase extends MenuEntryBase { Future setOption(bool option); @override - mod_menu.PopupMenuEntry build(BuildContext context, MenuConfig conf) { - return mod_menu.PopupMenuItem( - padding: EdgeInsets.zero, - height: conf.height, - child: Obx( - () => SwitchListTile( - value: curOption.value, - onChanged: (v) { - setOption(v); - }, - title: Container( - alignment: AlignmentDirectional.centerStart, - constraints: BoxConstraints(minHeight: conf.height), - child: Text( - text, - style: const TextStyle( - color: Colors.black, - fontSize: MenuConfig.fontSize, - fontWeight: FontWeight.normal), - )), - dense: true, - visualDensity: const VisualDensity( - horizontal: VisualDensity.minimumDensity, - vertical: VisualDensity.minimumDensity, + List> build( + BuildContext context, MenuConfig conf) { + return [ + mod_menu.PopupMenuItem( + padding: EdgeInsets.zero, + height: conf.height, + child: Obx( + () => SwitchListTile( + value: curOption.value, + onChanged: (v) { + setOption(v); + }, + title: Container( + alignment: AlignmentDirectional.centerStart, + constraints: BoxConstraints(minHeight: conf.height), + child: Text( + text, + style: const TextStyle( + color: Colors.black, + fontSize: MenuConfig.fontSize, + fontWeight: FontWeight.normal), + )), + dense: true, + visualDensity: const VisualDensity( + horizontal: VisualDensity.minimumDensity, + vertical: VisualDensity.minimumDensity, + ), + contentPadding: const EdgeInsets.only(left: 8.0), ), - contentPadding: const EdgeInsets.only(left: 8.0), ), - ), - ); + ) + ]; } } @@ -307,32 +398,37 @@ class MenuEntrySubMenu extends MenuEntryBase { }); @override - mod_menu.PopupMenuEntry build(BuildContext context, MenuConfig conf) { - return PopupMenuChildrenItem( - height: conf.height, - padding: EdgeInsets.zero, - position: mod_menu.PopupMenuPosition.overSide, - itemBuilder: (BuildContext context) => - entries.map((entry) => entry.build(context, conf)).toList(), - child: Row(children: [ - const SizedBox(width: MenuConfig.midPadding), - Text( - text, - style: const TextStyle( - color: Colors.black, - fontSize: MenuConfig.fontSize, - fontWeight: FontWeight.normal), - ), - Expanded( - child: Align( - alignment: Alignment.centerRight, - child: Icon( - Icons.keyboard_arrow_right, - color: conf.commonColor, + List> build( + BuildContext context, MenuConfig conf) { + return [ + PopupMenuChildrenItem( + height: conf.height, + padding: EdgeInsets.zero, + position: mod_menu.PopupMenuPosition.overSide, + itemBuilder: (BuildContext context) => entries + .map((entry) => entry.build(context, conf)) + .expand((i) => i) + .toList(), + child: Row(children: [ + const SizedBox(width: MenuConfig.midPadding), + Text( + text, + style: const TextStyle( + color: Colors.black, + fontSize: MenuConfig.fontSize, + fontWeight: FontWeight.normal), ), - )) - ]), - ); + Expanded( + child: Align( + alignment: Alignment.centerRight, + child: Icon( + Icons.keyboard_arrow_right, + color: conf.commonColor, + ), + )) + ]), + ) + ]; } } @@ -346,24 +442,27 @@ class MenuEntryButton extends MenuEntryBase { }); @override - mod_menu.PopupMenuEntry build(BuildContext context, MenuConfig conf) { - return mod_menu.PopupMenuItem( - padding: EdgeInsets.zero, - height: conf.height, - child: TextButton( - child: Container( - alignment: AlignmentDirectional.centerStart, - constraints: BoxConstraints(minHeight: conf.height), - child: childBuilder( - const TextStyle( - color: Colors.black, - fontSize: MenuConfig.fontSize, - fontWeight: FontWeight.normal), - )), - onPressed: () { - proc(); - }, - ), - ); + List> build( + BuildContext context, MenuConfig conf) { + return [ + mod_menu.PopupMenuItem( + padding: EdgeInsets.zero, + height: conf.height, + child: TextButton( + child: Container( + alignment: AlignmentDirectional.centerStart, + constraints: BoxConstraints(minHeight: conf.height), + child: childBuilder( + const TextStyle( + color: Colors.black, + fontSize: MenuConfig.fontSize, + fontWeight: FontWeight.normal), + )), + onPressed: () { + proc(); + }, + ), + ) + ]; } } diff --git a/flutter/lib/desktop/widgets/remote_menubar.dart b/flutter/lib/desktop/widgets/remote_menubar.dart index 620f5f226..0e931dd71 100644 --- a/flutter/lib/desktop/widgets/remote_menubar.dart +++ b/flutter/lib/desktop/widgets/remote_menubar.dart @@ -236,6 +236,7 @@ class _RemoteMenubarState extends State { height: _MenubarTheme.height, dividerHeight: _MenubarTheme.dividerHeight, ))) + .expand((i) => i) .toList(), ); } @@ -258,6 +259,7 @@ class _RemoteMenubarState extends State { height: _MenubarTheme.height, dividerHeight: _MenubarTheme.dividerHeight, ))) + .expand((i) => i) .toList(), ); } @@ -401,7 +403,7 @@ class _RemoteMenubarState extends State { List> _getDisplayMenu() { final displayMenu = [ - MenuEntrySubRadios( + MenuEntryRadios( text: translate('Ratio'), optionsGetter: () => [ Tuple2(translate('Original'), 'original'), @@ -418,7 +420,8 @@ class _RemoteMenubarState extends State { id: widget.id, name: "view-style", value: v); widget.ffi.canvasModel.updateViewStyle(); }), - MenuEntrySubRadios( + MenuEntryDivider(), + MenuEntryRadios( text: translate('Scroll Style'), optionsGetter: () => [ Tuple2(translate('ScrollAuto'), 'scrollauto'), @@ -434,7 +437,8 @@ class _RemoteMenubarState extends State { id: widget.id, name: "scroll-style", value: v); widget.ffi.canvasModel.updateScrollStyle(); }), - MenuEntrySubRadios( + MenuEntryDivider(), + MenuEntryRadios( text: translate('Image Quality'), optionsGetter: () => [ Tuple2(translate('Good image quality'), 'best'), @@ -451,6 +455,7 @@ class _RemoteMenubarState extends State { optionSetter: (String v) async { await bind.sessionSetImageQuality(id: widget.id, value: v); }), + MenuEntryDivider(), MenuEntrySwitch( text: translate('Show remote cursor'), getter: () async { diff --git a/flutter/pubspec.yaml b/flutter/pubspec.yaml index fc59b8bd3..a35f1c872 100644 --- a/flutter/pubspec.yaml +++ b/flutter/pubspec.yaml @@ -40,10 +40,7 @@ dependencies: url_launcher: ^6.0.9 shared_preferences: ^2.0.6 toggle_switch: ^1.4.0 - dash_chat_2: - git: - url: https://github.com/fufesou/Dash-Chat-2 - ref: feat_maxWidth + dash_chat_2: ^0.0.14 draggable_float_widget: ^0.0.2 settings_ui: ^2.0.2 flutter_breadcrumb: ^1.0.1