diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index 6027fb8de..b991c7a96 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -743,39 +743,3 @@ Future>? matchPeers(String searchText, List peers) async { } return filteredList; } - -class PrivacyModeState { - static String tag(String id) => 'privacy_mode_' + id; - - static void init(String id) { - final RxBool state = false.obs; - Get.put(state, tag: tag(id)); - } - - static void delete(String id) => Get.delete(tag: tag(id)); - static RxBool find(String id) => Get.find(tag: tag(id)); -} - -class BlockInputState { - static String tag(String id) => 'block_input_' + id; - - static void init(String id) { - final RxBool state = false.obs; - Get.put(state, tag: tag(id)); - } - - static void delete(String id) => Get.delete(tag: tag(id)); - static RxBool find(String id) => Get.find(tag: tag(id)); -} - -class CurrentDisplayState { - static String tag(String id) => 'current_display_' + id; - - static void init(String id) { - final RxInt state = RxInt(0); - Get.put(state, tag: tag(id)); - } - - static void delete(String id) => Get.delete(tag: tag(id)); - static RxInt find(String id) => Get.find(tag: tag(id)); -} diff --git a/flutter/lib/common/shared_state.dart b/flutter/lib/common/shared_state.dart new file mode 100644 index 000000000..8ff4e667e --- /dev/null +++ b/flutter/lib/common/shared_state.dart @@ -0,0 +1,73 @@ +import 'package:get/get.dart'; + +import '../consts.dart'; + +class PrivacyModeState { + static String tag(String id) => 'privacy_mode_$id'; + + static void init(String id) { + final RxBool state = false.obs; + Get.put(state, tag: tag(id)); + } + + static void delete(String id) => Get.delete(tag: tag(id)); + static RxBool find(String id) => Get.find(tag: tag(id)); +} + +class BlockInputState { + static String tag(String id) => 'block_input_$id'; + + static void init(String id) { + final RxBool state = false.obs; + Get.put(state, tag: tag(id)); + } + + static void delete(String id) => Get.delete(tag: tag(id)); + static RxBool find(String id) => Get.find(tag: tag(id)); +} + +class CurrentDisplayState { + static String tag(String id) => 'current_display_$id'; + + static void init(String id) { + final RxInt state = RxInt(0); + Get.put(state, tag: tag(id)); + } + + static void delete(String id) => Get.delete(tag: tag(id)); + static RxInt find(String id) => Get.find(tag: tag(id)); +} + +class ConnectionType { + final Rx _secure = kInvalidValueStr.obs; + final Rx _direct = kInvalidValueStr.obs; + + Rx get secure => _secure; + Rx get direct => _direct; + + void setSecure(bool v) { + _secure.value = v ? 'secure' : 'insecure'; + } + + void setDirect(bool v) { + _direct.value = v ? '' : '_relay'; + } + + bool isValid() { + return _secure.value != kInvalidValueStr && + _direct.value != kInvalidValueStr; + } +} + +class ConnectionTypeState { + static String tag(String id) => 'connection_type_$id'; + + static void init(String id) { + final ConnectionType collectionType = ConnectionType(); + Get.put(collectionType, tag: tag(id)); + } + + static void delete(String id) => Get.delete(tag: tag(id)); + static ConnectionType find(String id) => + Get.find(tag: tag(id)); +} diff --git a/flutter/lib/consts.dart b/flutter/lib/consts.dart index 3f0abd43f..6c67e2ab9 100644 --- a/flutter/lib/consts.dart +++ b/flutter/lib/consts.dart @@ -10,3 +10,5 @@ const String kTabLabelSettingPage = "Settings"; const int kDefaultDisplayWidth = 1280; const int kDefaultDisplayHeight = 720; + +const kInvalidValueStr = "InvalidValueStr"; diff --git a/flutter/lib/desktop/pages/connection_tab_page.dart b/flutter/lib/desktop/pages/connection_tab_page.dart index 1d00cdc8a..75471af0e 100644 --- a/flutter/lib/desktop/pages/connection_tab_page.dart +++ b/flutter/lib/desktop/pages/connection_tab_page.dart @@ -3,6 +3,7 @@ import 'dart:convert'; import 'package:desktop_multi_window/desktop_multi_window.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hbb/common.dart'; +import 'package:flutter_hbb/common/shared_state.dart'; import 'package:flutter_hbb/consts.dart'; import 'package:flutter_hbb/desktop/pages/remote_page.dart'; import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart'; @@ -20,8 +21,8 @@ class ConnectionTabPage extends StatefulWidget { class _ConnectionTabPageState extends State { final tabController = Get.put(DesktopTabController()); - static final IconData selectedIcon = Icons.desktop_windows_sharp; - static final IconData unselectedIcon = Icons.desktop_windows_outlined; + static const IconData selectedIcon = Icons.desktop_windows_sharp; + static const IconData unselectedIcon = Icons.desktop_windows_outlined; var connectionMap = RxList.empty(growable: true); @@ -34,7 +35,7 @@ class _ConnectionTabPageState extends State { selectedIcon: selectedIcon, unselectedIcon: unselectedIcon, page: Obx(() => RemotePage( - key: ValueKey(params['id']), + key: ValueKey(params['id']), id: params['id'], tabBarHeight: fullscreen.isTrue ? 0 : kDesktopRemoteTabBarHeight, @@ -88,10 +89,10 @@ class _ConnectionTabPageState extends State { child: Scaffold( backgroundColor: MyTheme.color(context).bg, body: Obx(() => DesktopTab( - controller: tabController, - theme: theme, - isMainWindow: false, - showTabBar: fullscreen.isFalse, + controller: tabController, + theme: theme, + isMainWindow: false, + showTabBar: fullscreen.isFalse, onClose: () { tabController.clear(); }, @@ -103,7 +104,36 @@ class _ConnectionTabPageState extends State { .setFullscreen(fullscreen.isTrue); return pageView; }, - ))), + tabBuilder: (key, icon, label, themeConf) { + final connectionType = ConnectionTypeState.find(key); + if (!connectionType.isValid()) { + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + icon, + label, + ], + ); + } else { + final iconName = + '${connectionType.secure.value}${connectionType.direct.value}'; + final connectionIcon = Image.asset( + 'assets/$iconName.png', + width: themeConf.iconSize, + height: themeConf.iconSize, + color: theme.selectedtabIconColor, + ); + //.paddingOnly(right: 5); + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + icon, + connectionIcon, + label, + ], + ); + } + }))), ), )); } diff --git a/flutter/lib/desktop/pages/remote_page.dart b/flutter/lib/desktop/pages/remote_page.dart index fbba5bafe..14635e5a1 100644 --- a/flutter/lib/desktop/pages/remote_page.dart +++ b/flutter/lib/desktop/pages/remote_page.dart @@ -5,11 +5,9 @@ import 'dart:ui' as ui; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:flutter_hbb/models/chat_model.dart'; import 'package:get/get.dart'; import 'package:provider/provider.dart'; import 'package:wakelock/wakelock.dart'; -import 'package:tuple/tuple.dart'; // import 'package:window_manager/window_manager.dart'; @@ -19,6 +17,8 @@ import '../../mobile/widgets/dialog.dart'; import '../../mobile/widgets/overlay.dart'; import '../../models/model.dart'; import '../../models/platform_model.dart'; +import '../../models/chat_model.dart'; +import '../../common/shared_state.dart'; final initText = '\1' * 1024; @@ -41,7 +41,7 @@ class _RemotePageState extends State Timer? _timer; bool _showBar = !isWebDesktop; String _value = ''; - var _cursorOverImage = false.obs; + final _cursorOverImage = false.obs; final FocusNode _mobileFocusNode = FocusNode(); final FocusNode _physicalFocusNode = FocusNode(); @@ -54,6 +54,20 @@ class _RemotePageState extends State _ffi.canvasModel.tabBarHeight = widget.tabBarHeight; } + void _initStates(String id) { + PrivacyModeState.init(id); + BlockInputState.init(id); + CurrentDisplayState.init(id); + ConnectionTypeState.init(id); + } + + void _removeStates(String id) { + PrivacyModeState.delete(id); + BlockInputState.delete(id); + CurrentDisplayState.delete(id); + ConnectionTypeState.delete(id); + } + @override void initState() { super.initState(); @@ -74,14 +88,12 @@ class _RemotePageState extends State _ffi.listenToMouse(true); _ffi.qualityMonitorModel.checkShowQualityMonitor(widget.id); // WindowManager.instance.addListener(this); - PrivacyModeState.init(widget.id); - BlockInputState.init(widget.id); - CurrentDisplayState.init(widget.id); + _initStates(widget.id); } @override void dispose() { - print("REMOTE PAGE dispose ${widget.id}"); + debugPrint("REMOTE PAGE dispose ${widget.id}"); hideMobileActionsOverlay(); _ffi.listenToMouse(false); _mobileFocusNode.dispose(); @@ -97,9 +109,7 @@ class _RemotePageState extends State // WindowManager.instance.removeListener(this); Get.delete(tag: widget.id); super.dispose(); - PrivacyModeState.delete(widget.id); - BlockInputState.delete(widget.id); - CurrentDisplayState.delete(widget.id); + _removeStates(widget.id); } void resetTool() { diff --git a/flutter/lib/desktop/widgets/popup_menu.dart b/flutter/lib/desktop/widgets/popup_menu.dart index acb8f184c..00f940fdb 100644 --- a/flutter/lib/desktop/widgets/popup_menu.dart +++ b/flutter/lib/desktop/widgets/popup_menu.dart @@ -4,12 +4,10 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:tuple/tuple.dart'; -import './material_mod_popup_menu.dart' as modMenu; - -const kInvalidValueStr = "InvalidValueStr"; +import './material_mod_popup_menu.dart' as mod_menu; // https://stackoverflow.com/questions/68318314/flutter-popup-menu-inside-popup-menu -class PopupMenuChildrenItem extends modMenu.PopupMenuEntry { +class PopupMenuChildrenItem extends mod_menu.PopupMenuEntry { const PopupMenuChildrenItem({ key, this.height = kMinInteractiveDimension, @@ -17,19 +15,19 @@ class PopupMenuChildrenItem extends modMenu.PopupMenuEntry { this.enable = true, this.textStyle, this.onTap, - this.position = modMenu.PopupMenuPosition.overSide, + this.position = mod_menu.PopupMenuPosition.overSide, this.offset = Offset.zero, required this.itemBuilder, required this.child, }) : super(key: key); - final modMenu.PopupMenuPosition position; + final mod_menu.PopupMenuPosition position; final Offset offset; final TextStyle? textStyle; final EdgeInsets? padding; final bool enable; final void Function()? onTap; - final List> Function(BuildContext) itemBuilder; + final List> Function(BuildContext) itemBuilder; final Widget child; @override @@ -59,7 +57,7 @@ class MyPopupMenuItemState> popupMenuTheme.textStyle ?? theme.textTheme.subtitle1!; - return modMenu.PopupMenuButton( + return mod_menu.PopupMenuButton( enabled: widget.enable, position: widget.position, offset: widget.offset, @@ -88,22 +86,26 @@ class MenuConfig { static const iconWidth = 12.0; static const iconHeight = 12.0; - final double secondMenuHeight; + final double height; + final double dividerHeight; final Color commonColor; const MenuConfig( {required this.commonColor, - this.secondMenuHeight = kMinInteractiveDimension}); + this.height = kMinInteractiveDimension, + this.dividerHeight = 16.0}); } abstract class MenuEntryBase { - modMenu.PopupMenuEntry build(BuildContext context, MenuConfig conf); + mod_menu.PopupMenuEntry build(BuildContext context, MenuConfig conf); } class MenuEntryDivider extends MenuEntryBase { @override - modMenu.PopupMenuEntry build(BuildContext context, MenuConfig conf) { - return const modMenu.PopupMenuDivider(); + mod_menu.PopupMenuEntry build(BuildContext context, MenuConfig conf) { + return mod_menu.PopupMenuDivider( + height: conf.dividerHeight, + ); } } @@ -138,14 +140,15 @@ class MenuEntrySubRadios extends MenuEntryBase { } } - modMenu.PopupMenuEntry _buildSecondMenu( + mod_menu.PopupMenuEntry _buildSecondMenu( BuildContext context, MenuConfig conf, Tuple2 opt) { - return modMenu.PopupMenuItem( + return mod_menu.PopupMenuItem( padding: EdgeInsets.zero, + height: conf.height, child: TextButton( child: Container( alignment: AlignmentDirectional.centerStart, - constraints: BoxConstraints(minHeight: conf.secondMenuHeight), + constraints: BoxConstraints(minHeight: conf.height), child: Row( children: [ SizedBox( @@ -156,7 +159,7 @@ class MenuEntrySubRadios extends MenuEntryBase { Icons.check, color: conf.commonColor, ) - : SizedBox.shrink())), + : const SizedBox.shrink())), const SizedBox(width: MenuConfig.midPadding), Text( opt.item1, @@ -178,10 +181,10 @@ class MenuEntrySubRadios extends MenuEntryBase { } @override - modMenu.PopupMenuEntry build(BuildContext context, MenuConfig conf) { + mod_menu.PopupMenuEntry build(BuildContext context, MenuConfig conf) { return PopupMenuChildrenItem( - height: conf.secondMenuHeight, padding: EdgeInsets.zero, + height: conf.height, itemBuilder: (BuildContext context) => options.map((opt) => _buildSecondMenu(context, conf, opt)).toList(), child: Row(children: [ @@ -218,9 +221,10 @@ abstract class MenuEntrySwitchBase extends MenuEntryBase { Future setOption(bool option); @override - modMenu.PopupMenuEntry build(BuildContext context, MenuConfig conf) { - return modMenu.PopupMenuItem( + mod_menu.PopupMenuEntry build(BuildContext context, MenuConfig conf) { + return mod_menu.PopupMenuItem( padding: EdgeInsets.zero, + height: conf.height, child: Obx( () => SwitchListTile( value: curOption.value, @@ -229,7 +233,7 @@ abstract class MenuEntrySwitchBase extends MenuEntryBase { }, title: Container( alignment: AlignmentDirectional.centerStart, - constraints: BoxConstraints(minHeight: conf.secondMenuHeight), + constraints: BoxConstraints(minHeight: conf.height), child: Text( text, style: const TextStyle( @@ -242,7 +246,7 @@ abstract class MenuEntrySwitchBase extends MenuEntryBase { horizontal: VisualDensity.minimumDensity, vertical: VisualDensity.minimumDensity, ), - contentPadding: EdgeInsets.only(left: 8.0), + contentPadding: const EdgeInsets.only(left: 8.0), ), ), ); @@ -303,11 +307,11 @@ class MenuEntrySubMenu extends MenuEntryBase { }); @override - modMenu.PopupMenuEntry build(BuildContext context, MenuConfig conf) { + mod_menu.PopupMenuEntry build(BuildContext context, MenuConfig conf) { return PopupMenuChildrenItem( - height: conf.secondMenuHeight, + height: conf.height, padding: EdgeInsets.zero, - position: modMenu.PopupMenuPosition.overSide, + position: mod_menu.PopupMenuPosition.overSide, itemBuilder: (BuildContext context) => entries.map((entry) => entry.build(context, conf)).toList(), child: Row(children: [ @@ -342,13 +346,14 @@ class MenuEntryButton extends MenuEntryBase { }); @override - modMenu.PopupMenuEntry build(BuildContext context, MenuConfig conf) { - return modMenu.PopupMenuItem( + 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.secondMenuHeight), + constraints: BoxConstraints(minHeight: conf.height), child: childBuilder( const TextStyle( color: Colors.black, @@ -362,14 +367,3 @@ class MenuEntryButton extends MenuEntryBase { ); } } - -class CustomMenu { - final List> entries; - final MenuConfig conf; - - const CustomMenu({required this.entries, required this.conf}); - - List> build(BuildContext context) { - return entries.map((entry) => entry.build(context, conf)).toList(); - } -} diff --git a/flutter/lib/desktop/widgets/remote_menubar.dart b/flutter/lib/desktop/widgets/remote_menubar.dart index 011525ba9..620f5f226 100644 --- a/flutter/lib/desktop/widgets/remote_menubar.dart +++ b/flutter/lib/desktop/widgets/remote_menubar.dart @@ -9,12 +9,15 @@ import '../../mobile/widgets/dialog.dart'; import '../../mobile/widgets/overlay.dart'; import '../../models/model.dart'; import '../../models/platform_model.dart'; +import '../../common/shared_state.dart'; import './popup_menu.dart'; import './material_mod_popup_menu.dart' as mod_menu; class _MenubarTheme { static const Color commonColor = MyTheme.accent; - static const double height = kMinInteractiveDimension; + // kMinInteractiveDimension + static const double height = 24.0; + static const double dividerHeight = 12.0; } class RemoteMenubar extends StatefulWidget { @@ -168,11 +171,9 @@ class _RemoteMenubarState extends State { ), itemBuilder: (BuildContext context) { final List rowChildren = []; - const double selectorScale = 1.3; for (int i = 0; i < pi.displays.length; i++) { - rowChildren.add(Transform.scale( - scale: selectorScale, - child: Stack( + rowChildren.add( + Stack( alignment: Alignment.center, children: [ const Icon( @@ -203,7 +204,7 @@ class _RemoteMenubarState extends State { ) ], ), - )); + ); } return >[ mod_menu.PopupMenuItem( @@ -232,7 +233,8 @@ class _RemoteMenubarState extends State { context, const MenuConfig( commonColor: _MenubarTheme.commonColor, - secondMenuHeight: _MenubarTheme.height, + height: _MenubarTheme.height, + dividerHeight: _MenubarTheme.dividerHeight, ))) .toList(), ); @@ -253,7 +255,8 @@ class _RemoteMenubarState extends State { context, const MenuConfig( commonColor: _MenubarTheme.commonColor, - secondMenuHeight: _MenubarTheme.height, + height: _MenubarTheme.height, + dividerHeight: _MenubarTheme.dividerHeight, ))) .toList(), ); diff --git a/flutter/lib/desktop/widgets/tabbar_widget.dart b/flutter/lib/desktop/widgets/tabbar_widget.dart index 8ef082b49..6e0ce747d 100644 --- a/flutter/lib/desktop/widgets/tabbar_widget.dart +++ b/flutter/lib/desktop/widgets/tabbar_widget.dart @@ -121,6 +121,16 @@ class DesktopTabController { } } +class TabThemeConf { + double iconSize; + TarBarTheme theme; + TabThemeConf({required this.iconSize, required this.theme}); +} + +typedef TabBuilder = Widget Function( + String key, Widget icon, Widget label, TabThemeConf themeConf); +typedef LabelGetter = Rx Function(String key); + class DesktopTab extends StatelessWidget { final Function(String)? onTabClose; final TarBarTheme theme; @@ -134,24 +144,29 @@ class DesktopTab extends StatelessWidget { final Widget Function(Widget pageView)? pageViewBuilder; final Widget? tail; final VoidCallback? onClose; + final TabBuilder? tabBuilder; + final LabelGetter? labelGetter; final DesktopTabController controller; Rx get state => controller.state; - const DesktopTab( - {required this.controller, - required this.isMainWindow, - this.theme = const TarBarTheme.light(), - this.onTabClose, - this.showTabBar = true, - this.showLogo = true, - this.showTitle = true, - this.showMinimize = true, - this.showMaximize = true, - this.showClose = true, - this.pageViewBuilder, - this.tail, - this.onClose}); + const DesktopTab({ + required this.controller, + required this.isMainWindow, + this.theme = const TarBarTheme.light(), + this.onTabClose, + this.showTabBar = true, + this.showLogo = true, + this.showTitle = true, + this.showMinimize = true, + this.showMaximize = true, + this.showClose = true, + this.pageViewBuilder, + this.tail, + this.onClose, + this.tabBuilder, + this.labelGetter, + }); @override Widget build(BuildContext context) { @@ -194,8 +209,10 @@ class DesktopTab extends StatelessWidget { child: Row( children: [ Offstage( - offstage: !Platform.isMacOS, - child: const SizedBox(width: 78,)), + offstage: !Platform.isMacOS, + child: const SizedBox( + width: 78, + )), Row(children: [ Offstage( offstage: !showLogo, @@ -228,6 +245,8 @@ class DesktopTab extends StatelessWidget { controller: controller, onTabClose: onTabClose, theme: theme, + tabBuilder: tabBuilder, + labelGetter: labelGetter, )), ), ], @@ -356,10 +375,18 @@ class _ListView extends StatelessWidget { final DesktopTabController controller; final Function(String key)? onTabClose; final TarBarTheme theme; + + final TabBuilder? tabBuilder; + final LabelGetter? labelGetter; + Rx get state => controller.state; - const _ListView( - {required this.controller, required this.onTabClose, required this.theme}); + _ListView( + {required this.controller, + required this.onTabClose, + required this.theme, + this.tabBuilder, + this.labelGetter}); @override Widget build(BuildContext context) { @@ -373,7 +400,9 @@ class _ListView extends StatelessWidget { final tab = e.value; return _Tab( index: index, - label: tab.label, + label: labelGetter == null + ? Rx(tab.label) + : labelGetter!(tab.label), selectedIcon: tab.selectedIcon, unselectedIcon: tab.unselectedIcon, closable: tab.closable, @@ -381,6 +410,16 @@ class _ListView extends StatelessWidget { onClose: () => controller.remove(index), onSelected: () => controller.jumpTo(index), theme: theme, + tabBuilder: tabBuilder == null + ? null + : (Widget icon, Widget labelWidget, TabThemeConf themeConf) { + return tabBuilder!( + tab.label, + icon, + labelWidget, + themeConf, + ); + }, ); }).toList())); } @@ -388,7 +427,7 @@ class _ListView extends StatelessWidget { class _Tab extends StatelessWidget { late final int index; - late final String label; + late final Rx label; late final IconData? selectedIcon; late final IconData? unselectedIcon; late final bool closable; @@ -397,6 +436,8 @@ class _Tab extends StatelessWidget { late final Function() onSelected; final RxBool _hover = false.obs; late final TarBarTheme theme; + final Widget Function(Widget icon, Widget label, TabThemeConf themeConf)? + tabBuilder; _Tab( {Key? key, @@ -404,6 +445,7 @@ class _Tab extends StatelessWidget { required this.label, this.selectedIcon, this.unselectedIcon, + this.tabBuilder, required this.closable, required this.selected, required this.onClose, @@ -411,11 +453,49 @@ class _Tab extends StatelessWidget { required this.theme}) : super(key: key); + Widget _buildTabContent() { + bool showIcon = selectedIcon != null && unselectedIcon != null; + bool isSelected = index == selected; + + final icon = Offstage( + offstage: !showIcon, + child: Icon( + isSelected ? selectedIcon : unselectedIcon, + size: _kIconSize, + color: isSelected + ? theme.selectedtabIconColor + : theme.unSelectedtabIconColor, + ).paddingOnly(right: 5)); + final labelWidget = Obx(() { + return Text( + translate(label.value), + textAlign: TextAlign.center, + style: TextStyle( + color: isSelected + ? theme.selectedTextColor + : theme.unSelectedTextColor), + ); + }); + + if (tabBuilder == null) { + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + icon, + labelWidget, + ], + ); + } else { + return tabBuilder!( + icon, labelWidget, TabThemeConf(iconSize: _kIconSize, theme: theme)); + } + } + @override Widget build(BuildContext context) { - bool show_icon = selectedIcon != null && unselectedIcon != null; - bool is_selected = index == selected; - bool show_divider = index != selected - 1 && index != selected; + bool showIcon = selectedIcon != null && unselectedIcon != null; + bool isSelected = index == selected; + bool showDivider = index != selected - 1 && index != selected; return Ink( child: InkWell( onHover: (hover) => _hover.value = hover, @@ -427,40 +507,19 @@ class _Tab extends StatelessWidget { child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Offstage( - offstage: !show_icon, - child: Icon( - is_selected ? selectedIcon : unselectedIcon, - size: _kIconSize, - color: is_selected - ? theme.selectedtabIconColor - : theme.unSelectedtabIconColor, - ).paddingOnly(right: 5)), - Text( - translate(label), - textAlign: TextAlign.center, - style: TextStyle( - color: is_selected - ? theme.selectedTextColor - : theme.unSelectedTextColor), - ), - ], - ), + _buildTabContent(), Offstage( offstage: !closable, child: Obx((() => _CloseButton( visiable: _hover.value, - tabSelected: is_selected, + tabSelected: isSelected, onClose: () => onClose(), theme: theme, ))), ) ])).paddingSymmetric(horizontal: 10), Offstage( - offstage: !show_divider, + offstage: !showDivider, child: VerticalDivider( width: 1, indent: _kDividerIndent, diff --git a/flutter/lib/main.dart b/flutter/lib/main.dart index da3b07567..efca88180 100644 --- a/flutter/lib/main.dart +++ b/flutter/lib/main.dart @@ -202,7 +202,7 @@ class App extends StatelessWidget { title: 'RustDesk', theme: getCurrentTheme(), home: isDesktop - ? DesktopTabPage() + ? const DesktopTabPage() : !isAndroid ? WebHomePage() : HomePage(), diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index 70e922bce..4a54ba4e8 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -17,6 +17,7 @@ import 'package:shared_preferences/shared_preferences.dart'; import 'package:tuple/tuple.dart'; import '../common.dart'; +import '../common/shared_state.dart'; import '../mobile/widgets/dialog.dart'; import '../mobile/widgets/overlay.dart'; import 'peer_model.dart'; @@ -96,25 +97,26 @@ class FfiModel with ChangeNotifier { clearPermissions(); } - void setConnectionType(bool secure, bool direct) { + void setConnectionType(String peerId, bool secure, bool direct) { _secure = secure; _direct = direct; + try { + var connectionType = ConnectionTypeState.find(peerId); + connectionType.setSecure(secure); + connectionType.setDirect(direct); + } catch (e) { + // + } } Image? getConnectionImage() { - String? icon; - if (secure == true && direct == true) { - icon = 'secure'; - } else if (secure == false && direct == true) { - icon = 'insecure'; - } else if (secure == false && direct == false) { - icon = 'insecure_relay'; - } else if (secure == true && direct == false) { - icon = 'secure_relay'; + if (secure == null || direct == null) { + return null; + } else { + final icon = + '${secure == true ? "secure" : "insecure"}${direct == true ? "" : "_relay"}'; + return Image.asset('assets/$icon.png', width: 48, height: 48); } - return icon == null - ? null - : Image.asset('assets/$icon.png', width: 48, height: 48); } void clearPermissions() { @@ -130,7 +132,8 @@ class FfiModel with ChangeNotifier { } else if (name == 'peer_info') { handlePeerInfo(evt, peerId); } else if (name == 'connection_ready') { - setConnectionType(evt['secure'] == 'true', evt['direct'] == 'true'); + setConnectionType( + peerId, evt['secure'] == 'true', evt['direct'] == 'true'); } else if (name == 'switch_display') { handleSwitchDisplay(evt); } else if (name == 'cursor_data') { @@ -189,7 +192,7 @@ class FfiModel with ChangeNotifier { handlePeerInfo(evt, peerId); } else if (name == 'connection_ready') { parent.target?.ffiModel.setConnectionType( - evt['secure'] == 'true', evt['direct'] == 'true'); + peerId, evt['secure'] == 'true', evt['direct'] == 'true'); } else if (name == 'switch_display') { handleSwitchDisplay(evt); } else if (name == 'cursor_data') {