From 78c79a0e8d3cab86c7d30a6ec5927e58e861ca59 Mon Sep 17 00:00:00 2001 From: csf Date: Wed, 24 Aug 2022 20:12:04 +0800 Subject: [PATCH 1/6] refactor tabbar_widget.dart and impl for desktop_tab_page.dart --- .../lib/desktop/pages/desktop_tab_page.dart | 69 +-- .../lib/desktop/widgets/tabbar_widget.dart | 519 +++++++++--------- 2 files changed, 273 insertions(+), 315 deletions(-) diff --git a/flutter/lib/desktop/pages/desktop_tab_page.dart b/flutter/lib/desktop/pages/desktop_tab_page.dart index 141b7ca0e..5cc86f0ca 100644 --- a/flutter/lib/desktop/pages/desktop_tab_page.dart +++ b/flutter/lib/desktop/pages/desktop_tab_page.dart @@ -4,7 +4,6 @@ import 'package:flutter_hbb/consts.dart'; import 'package:flutter_hbb/desktop/pages/desktop_home_page.dart'; import 'package:flutter_hbb/desktop/pages/desktop_setting_page.dart'; import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart'; -import 'package:get/get.dart'; import 'package:window_manager/window_manager.dart'; class DesktopTabPage extends StatefulWidget { @@ -15,65 +14,51 @@ class DesktopTabPage extends StatefulWidget { } class _DesktopTabPageState extends State { - late RxList tabs; + final tabBarController = DesktopTabBarController(); @override void initState() { super.initState(); - tabs = RxList.from([ - TabInfo( - key: kTabLabelHomePage, - label: kTabLabelHomePage, - selectedIcon: Icons.home_sharp, - unselectedIcon: Icons.home_outlined, - closable: false) - ], growable: true); + tabBarController.state.value.tabs.add(TabInfo( + key: kTabLabelHomePage, + label: kTabLabelHomePage, + selectedIcon: Icons.home_sharp, + unselectedIcon: Icons.home_outlined, + closable: false, + page: DesktopHomePage())); } @override Widget build(BuildContext context) { + final dark = isDarkTheme(); return DragToResizeArea( child: Container( decoration: BoxDecoration( border: Border.all(color: MyTheme.color(context).border!)), child: Scaffold( - backgroundColor: MyTheme.color(context).bg, - body: Column( - children: [ - DesktopTabBar( - tabs: tabs, - dark: isDarkTheme(), - mainTab: true, - onAddSetting: onAddSetting, + backgroundColor: MyTheme.color(context).bg, + body: DesktopTab( + controller: tabBarController, + theme: dark ? TarBarTheme.dark() : TarBarTheme.light(), + isMainWindow: true, + tail: ActionIcon( + message: 'Settings', + icon: IconFont.menu, + theme: dark ? TarBarTheme.dark() : TarBarTheme.light(), + onTap: onAddSetting, + is_close: false, ), - Obx((() => Expanded( - child: PageView( - controller: DesktopTabBar.controller.value, - children: tabs.map((tab) { - switch (tab.label) { - case kTabLabelHomePage: - return DesktopHomePage(key: ValueKey(tab.label)); - case kTabLabelSettingPage: - return DesktopSettingPage(key: ValueKey(tab.label)); - default: - return Container(); - } - }).toList()), - ))), - ], - ), - ), + )), ), ); } void onAddSetting() { - DesktopTabBar.onAdd( - tabs, - TabInfo( - key: kTabLabelSettingPage, - label: kTabLabelSettingPage, - selectedIcon: Icons.build_sharp, - unselectedIcon: Icons.build_outlined)); + tabBarController.add(TabInfo( + key: kTabLabelSettingPage, + label: kTabLabelSettingPage, + selectedIcon: Icons.build_sharp, + unselectedIcon: Icons.build_outlined, + page: DesktopSettingPage())); } } diff --git a/flutter/lib/desktop/widgets/tabbar_widget.dart b/flutter/lib/desktop/widgets/tabbar_widget.dart index 7198a1c3c..7544c6ef0 100644 --- a/flutter/lib/desktop/widgets/tabbar_widget.dart +++ b/flutter/lib/desktop/widgets/tabbar_widget.dart @@ -16,210 +16,216 @@ const double _kDividerIndent = 10; const double _kActionIconSize = 12; class TabInfo { - late final String key; - late final String label; - late final IconData? selectedIcon; - late final IconData? unselectedIcon; - late final bool closable; + final String key; + final String label; + final IconData? selectedIcon; + final IconData? unselectedIcon; + final bool closable; + final Widget page; TabInfo( {required this.key, required this.label, this.selectedIcon, this.unselectedIcon, - this.closable = true}); + this.closable = true, + required this.page}); } -class DesktopTabBar extends StatelessWidget { - late final RxList tabs; - late final Function(String)? onTabClose; - late final bool dark; - late final _Theme _theme; - late final bool mainTab; - late final bool showLogo; - late final bool showTitle; - late final bool showMinimize; - late final bool showMaximize; - late final bool showClose; - late final void Function()? onAddSetting; - late final void Function(int)? onSelected; +class DesktopTabBarState { + final List tabs = []; final ScrollPosController scrollController = ScrollPosController(itemCount: 0); - static final Rx controller = PageController().obs; - static final Rx selected = 0.obs; - static final _tabBarListViewKey = GlobalKey(); + final PageController pageController = PageController(); + int selected = 0; - DesktopTabBar( - {Key? key, - required this.tabs, + DesktopTabBarState() { + scrollController.itemCount = tabs.length; + // TODO test + // WidgetsBinding.instance.addPostFrameCallback((_) { + // scrollController.scrollToItem(selected, + // center: true, animate: true); + // }); + } +} + +class DesktopTabBarController { + final state = DesktopTabBarState().obs; + + void add(TabInfo tab) { + if (!isDesktop) return; + final index = state.value.tabs.indexWhere((e) => e.key == tab.key); + int toIndex; + if (index >= 0) { + toIndex = index; + } else { + state.update((val) { + val!.tabs.add(tab); + }); + toIndex = state.value.tabs.length - 1; + assert(toIndex >= 0); + } + try { + jumpTo(toIndex); + } catch (e) { + // call before binding controller will throw + debugPrint("Failed to jumpTo: $e"); + } + } + + void remove(int index) { + if (!isDesktop) return; + if (index < 0) return; + final len = state.value.tabs.length; + final currentSelected = state.value.selected; + int toIndex = 0; + if (index == len - 1) { + toIndex = max(0, currentSelected - 1); + } else if (index < len - 1 && index < currentSelected) { + toIndex = max(0, currentSelected - 1); + } + state.value.tabs.removeAt(index); + state.value.scrollController.itemCount = state.value.tabs.length; + jumpTo(toIndex); + } + + void jumpTo(int index) { + state.update((val) { + val!.selected = index; + val.pageController.jumpToPage(index); + val.scrollController.scrollToItem(index, center: true, animate: true); + }); + + // onSelected callback + } +} + +class DesktopTab extends StatelessWidget { + final Function(String)? onTabClose; + final TarBarTheme theme; + final bool isMainWindow; + final bool showLogo; + final bool showTitle; + final bool showMinimize; + final bool showMaximize; + final bool showClose; + final Widget Function(Widget pageView)? pageViewBuilder; + final Widget? tail; + + final DesktopTabBarController controller; + late final state = controller.state; + + DesktopTab( + {required this.controller, + required this.isMainWindow, + this.theme = const TarBarTheme.light(), this.onTabClose, - required this.dark, - required this.mainTab, - this.onAddSetting, - this.onSelected, this.showLogo = true, this.showTitle = true, this.showMinimize = true, this.showMaximize = true, - this.showClose = true}) - : _theme = dark ? _Theme.dark() : _Theme.light(), - super(key: key) { - scrollController.itemCount = tabs.length; - WidgetsBinding.instance.addPostFrameCallback((_) { - scrollController.scrollToItem(selected.value, - center: true, animate: true); - }); - } + this.showClose = true, + this.pageViewBuilder, + this.tail}); @override Widget build(BuildContext context) { - return Container( - height: _kTabBarHeight, - child: Column( - children: [ - Container( - height: _kTabBarHeight - 1, - child: Row( - children: [ - Expanded( - child: Row( - children: [ - Row(children: [ - Offstage( - offstage: !showLogo, - child: Image.asset( - 'assets/logo.ico', - width: 20, - height: 20, - )), - Offstage( - offstage: !showTitle, - child: Text( - "RustDesk", - style: TextStyle(fontSize: 13), - ).marginOnly(left: 2)) - ]).marginOnly( - left: 5, - right: 10, - ), - Expanded( - child: GestureDetector( - onPanStart: (_) { - if (mainTab) { - windowManager.startDragging(); - } else { - WindowController.fromWindowId(windowId!) - .startDragging(); - } - }, - child: _ListView( - key: _tabBarListViewKey, - controller: controller, - scrollController: scrollController, - tabInfos: tabs, - selected: selected, - onTabClose: onTabClose, - theme: _theme, - onSelected: onSelected)), - ), - Offstage( - offstage: mainTab, - child: _AddButton( - theme: _theme, - ).paddingOnly(left: 10), - ), - ], - ), - ), - Offstage( - offstage: onAddSetting == null, - child: _ActionIcon( - message: 'Settings', - icon: IconFont.menu, - theme: _theme, - onTap: () => onAddSetting?.call(), - is_close: false, - ), - ), - WindowActionPanel( - mainTab: mainTab, - theme: _theme, - showMinimize: showMinimize, - showMaximize: showMaximize, - showClose: showClose, - ) - ], + return Column(children: [ + Container( + height: _kTabBarHeight, + child: Column( + children: [ + Container( + height: _kTabBarHeight - 1, + child: _buildBar(), ), - ), - Divider( - height: 1, - thickness: 1, - ), - ], + Divider( + height: 1, + thickness: 1, + ), + ], + ), ), + Expanded( + child: pageViewBuilder != null + ? pageViewBuilder!(_buildPageView()) + : _buildPageView()) + ]); + } + + Widget _buildPageView() { + debugPrint("_buildPageView: ${state.value.tabs.length}"); + return Obx(() => PageView( + controller: state.value.pageController, + children: + state.value.tabs.map((tab) => tab.page).toList(growable: false))); + } + + Widget _buildBar() { + return Row( + children: [ + Expanded( + child: Row( + children: [ + Row(children: [ + Offstage( + offstage: !showLogo, + child: Image.asset( + 'assets/logo.ico', + width: 20, + height: 20, + )), + Offstage( + offstage: !showTitle, + child: Text( + "RustDesk", + style: TextStyle(fontSize: 13), + ).marginOnly(left: 2)) + ]).marginOnly( + left: 5, + right: 10, + ), + Expanded( + child: GestureDetector( + onPanStart: (_) { + if (isMainWindow) { + windowManager.startDragging(); + } else { + WindowController.fromWindowId(windowId!) + .startDragging(); + } + }, + child: _ListView( + controller: controller, + onTabClose: onTabClose, + theme: theme, + )), + ), + Offstage( + offstage: isMainWindow, + child: _AddButton( + theme: theme, + ).paddingOnly(left: 10), + ), + ], + ), + ), + Offstage(offstage: tail == null, child: tail), + WindowActionPanel( + mainTab: isMainWindow, + theme: theme, + showMinimize: showMinimize, + showMaximize: showMaximize, + showClose: showClose, + ) + ], ); } - - static onAdd(RxList tabs, TabInfo tab) { - if (!isDesktop) return; - int index = tabs.indexWhere((e) => e.key == tab.key); - if (index >= 0) { - selected.value = index; - } else { - tabs.add(tab); - selected.value = tabs.length - 1; - assert(selected.value >= 0); - } - try { - controller.value.jumpToPage(selected.value); - } catch (e) { - // call before binding controller will throw - debugPrint("Failed to jumpToPage: $e"); - } - } - - static remove(RxList tabs, int index) { - if (!isDesktop) return; - if (index < 0) return; - if (index == tabs.length - 1) { - selected.value = max(0, selected.value - 1); - } else if (index < tabs.length - 1 && index < selected.value) { - selected.value = max(0, selected.value - 1); - } - tabs.removeAt(index); - controller.value.jumpToPage(selected.value); - } - - static void jumpTo(RxList tabs, int index) { - if (!isDesktop) return; - if (index < 0 || index >= tabs.length) return; - selected.value = index; - controller.value.jumpToPage(selected.value); - } - - static void close(String? key) { - if (!isDesktop) return; - final tabBar = _tabBarListViewKey.currentWidget as _ListView?; - if (tabBar == null) return; - final tabs = tabBar.tabs; - if (key == null) { - if (tabBar.selected.value < tabs.length) { - tabs[tabBar.selected.value].onClose(); - } - } else { - for (final tab in tabs) { - if (tab.key == key) { - tab.onClose(); - break; - } - } - } - } } class WindowActionPanel extends StatelessWidget { final bool mainTab; - final _Theme theme; + final TarBarTheme theme; final bool showMinimize; final bool showMaximize; @@ -240,7 +246,7 @@ class WindowActionPanel extends StatelessWidget { children: [ Offstage( offstage: !showMinimize, - child: _ActionIcon( + child: ActionIcon( message: 'Minimize', icon: IconFont.min, theme: theme, @@ -269,7 +275,7 @@ class WindowActionPanel extends StatelessWidget { }); } return Obx( - () => _ActionIcon( + () => ActionIcon( message: is_maximized.value ? "Restore" : "Maximize", icon: is_maximized.value ? IconFont.restore : IconFont.max, theme: theme, @@ -297,7 +303,7 @@ class WindowActionPanel extends StatelessWidget { })), Offstage( offstage: !showClose, - child: _ActionIcon( + child: ActionIcon( message: 'Close', icon: IconFont.close, theme: theme, @@ -317,69 +323,37 @@ class WindowActionPanel extends StatelessWidget { // ignore: must_be_immutable class _ListView extends StatelessWidget { - final Rx controller; - final ScrollPosController scrollController; - final RxList tabInfos; - final Rx selected; + final DesktopTabBarController controller; + late final Rx state; final Function(String key)? onTabClose; - final _Theme _theme; - late List<_Tab> tabs; - late final void Function(int)? onSelected; + final TarBarTheme theme; _ListView( - {Key? key, - required this.controller, - required this.scrollController, - required this.tabInfos, - required this.selected, - required this.onTabClose, - required _Theme theme, - this.onSelected}) - : _theme = theme, - super(key: key); + {required this.controller, required this.onTabClose, required this.theme}) + : this.state = controller.state; @override Widget build(BuildContext context) { - return Obx(() { - tabs = tabInfos.asMap().entries.map((e) { - int index = e.key; - return _Tab( - index: index, - label: e.value.label, - selectedIcon: e.value.selectedIcon, - unselectedIcon: e.value.unselectedIcon, - closable: e.value.closable, - selected: selected.value, - onClose: () { - tabInfos.removeWhere((tab) => tab.key == e.value.key); - onTabClose?.call(e.value.key); - if (index <= selected.value) { - selected.value = max(0, selected.value - 1); - } - assert(tabInfos.length == 0 || selected.value < tabInfos.length); - scrollController.itemCount = tabInfos.length; - if (tabInfos.length > 0) { - scrollController.scrollToItem(selected.value, - center: true, animate: true); - controller.value.jumpToPage(selected.value); - } - }, - onSelected: () { - selected.value = index; - scrollController.scrollToItem(index, center: true, animate: true); - controller.value.jumpToPage(index); - onSelected?.call(selected.value); - }, - theme: _theme, - ); - }).toList(); - return ListView( - controller: scrollController, - scrollDirection: Axis.horizontal, - shrinkWrap: true, - physics: BouncingScrollPhysics(), - children: tabs); - }); + return Obx(() => ListView( + controller: state.value.scrollController, + scrollDirection: Axis.horizontal, + shrinkWrap: true, + physics: BouncingScrollPhysics(), + children: state.value.tabs.asMap().entries.map((e) { + final index = e.key; + final tab = e.value; + return _Tab( + index: index, + label: tab.label, + selectedIcon: tab.selectedIcon, + unselectedIcon: tab.unselectedIcon, + closable: tab.closable, + selected: state.value.selected, + onClose: () => controller.remove(index), + onSelected: () => controller.jumpTo(index), + theme: theme, + ); + }).toList())); } } @@ -393,7 +367,7 @@ class _Tab extends StatelessWidget { late final Function() onClose; late final Function() onSelected; final RxBool _hover = false.obs; - late final _Theme theme; + late final TarBarTheme theme; _Tab( {Key? key, @@ -474,7 +448,7 @@ class _Tab extends StatelessWidget { } class _AddButton extends StatelessWidget { - late final _Theme theme; + late final TarBarTheme theme; _AddButton({ Key? key, @@ -483,7 +457,7 @@ class _AddButton extends StatelessWidget { @override Widget build(BuildContext context) { - return _ActionIcon( + return ActionIcon( message: 'New Connection', icon: IconFont.add, theme: theme, @@ -497,7 +471,7 @@ class _CloseButton extends StatelessWidget { final bool visiable; final bool tabSelected; final Function onClose; - late final _Theme theme; + late final TarBarTheme theme; _CloseButton({ Key? key, @@ -528,13 +502,13 @@ class _CloseButton extends StatelessWidget { } } -class _ActionIcon extends StatelessWidget { +class ActionIcon extends StatelessWidget { final String message; final IconData icon; - final _Theme theme; + final TarBarTheme theme; final Function() onTap; final bool is_close; - const _ActionIcon({ + const ActionIcon({ Key? key, required this.message, required this.icon, @@ -568,35 +542,34 @@ class _ActionIcon extends StatelessWidget { } } -class _Theme { - late Color unSelectedtabIconColor; - late Color selectedtabIconColor; - late Color selectedTextColor; - late Color unSelectedTextColor; - late Color selectedIconColor; - late Color unSelectedIconColor; - late Color dividerColor; - late Color hoverColor; +class TarBarTheme { + final Color unSelectedtabIconColor; + final Color selectedtabIconColor; + final Color selectedTextColor; + final Color unSelectedTextColor; + final Color selectedIconColor; + final Color unSelectedIconColor; + final Color dividerColor; + final Color hoverColor; - _Theme.light() { - unSelectedtabIconColor = Color.fromARGB(255, 162, 203, 241); - selectedtabIconColor = MyTheme.accent; - selectedTextColor = Color.fromARGB(255, 26, 26, 26); - unSelectedTextColor = Color.fromARGB(255, 96, 96, 96); - selectedIconColor = Color.fromARGB(255, 26, 26, 26); - unSelectedIconColor = Color.fromARGB(255, 96, 96, 96); - dividerColor = Color.fromARGB(255, 238, 238, 238); - hoverColor = Colors.grey.withOpacity(0.2); - } + const TarBarTheme.light() + : unSelectedtabIconColor = const Color.fromARGB(255, 162, 203, 241), + selectedtabIconColor = MyTheme.accent, + selectedTextColor = const Color.fromARGB(255, 26, 26, 26), + unSelectedTextColor = const Color.fromARGB(255, 96, 96, 96), + selectedIconColor = const Color.fromARGB(255, 26, 26, 26), + unSelectedIconColor = const Color.fromARGB(255, 96, 96, 96), + dividerColor = const Color.fromARGB(255, 238, 238, 238), + hoverColor = const Color.fromARGB( + 51, 158, 158, 158); // Colors.grey; //0xFF9E9E9E - _Theme.dark() { - unSelectedtabIconColor = Color.fromARGB(255, 30, 65, 98); - selectedtabIconColor = MyTheme.accent; - selectedTextColor = Color.fromARGB(255, 255, 255, 255); - unSelectedTextColor = Color.fromARGB(255, 207, 207, 207); - selectedIconColor = Color.fromARGB(255, 215, 215, 215); - unSelectedIconColor = Color.fromARGB(255, 255, 255, 255); - dividerColor = Color.fromARGB(255, 64, 64, 64); - hoverColor = Colors.black26; - } + const TarBarTheme.dark() + : unSelectedtabIconColor = const Color.fromARGB(255, 30, 65, 98), + selectedtabIconColor = MyTheme.accent, + selectedTextColor = const Color.fromARGB(255, 255, 255, 255), + unSelectedTextColor = const Color.fromARGB(255, 207, 207, 207), + selectedIconColor = const Color.fromARGB(255, 215, 215, 215), + unSelectedIconColor = const Color.fromARGB(255, 255, 255, 255), + dividerColor = const Color.fromARGB(255, 64, 64, 64), + hoverColor = Colors.black26; } From 66b145912684f86ab08f6f2e4a5095357c2d9ecf Mon Sep 17 00:00:00 2001 From: csf Date: Wed, 24 Aug 2022 20:17:51 +0800 Subject: [PATCH 2/6] rename tabbar -> tab --- flutter/lib/desktop/pages/desktop_tab_page.dart | 8 ++++---- flutter/lib/desktop/widgets/tabbar_widget.dart | 14 +++++++------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/flutter/lib/desktop/pages/desktop_tab_page.dart b/flutter/lib/desktop/pages/desktop_tab_page.dart index 5cc86f0ca..2504c699f 100644 --- a/flutter/lib/desktop/pages/desktop_tab_page.dart +++ b/flutter/lib/desktop/pages/desktop_tab_page.dart @@ -14,12 +14,12 @@ class DesktopTabPage extends StatefulWidget { } class _DesktopTabPageState extends State { - final tabBarController = DesktopTabBarController(); + final tabController = DesktopTabController(); @override void initState() { super.initState(); - tabBarController.state.value.tabs.add(TabInfo( + tabController.state.value.tabs.add(TabInfo( key: kTabLabelHomePage, label: kTabLabelHomePage, selectedIcon: Icons.home_sharp, @@ -38,7 +38,7 @@ class _DesktopTabPageState extends State { child: Scaffold( backgroundColor: MyTheme.color(context).bg, body: DesktopTab( - controller: tabBarController, + controller: tabController, theme: dark ? TarBarTheme.dark() : TarBarTheme.light(), isMainWindow: true, tail: ActionIcon( @@ -54,7 +54,7 @@ class _DesktopTabPageState extends State { } void onAddSetting() { - tabBarController.add(TabInfo( + tabController.add(TabInfo( key: kTabLabelSettingPage, label: kTabLabelSettingPage, selectedIcon: Icons.build_sharp, diff --git a/flutter/lib/desktop/widgets/tabbar_widget.dart b/flutter/lib/desktop/widgets/tabbar_widget.dart index 7544c6ef0..77757dd04 100644 --- a/flutter/lib/desktop/widgets/tabbar_widget.dart +++ b/flutter/lib/desktop/widgets/tabbar_widget.dart @@ -32,14 +32,14 @@ class TabInfo { required this.page}); } -class DesktopTabBarState { +class DesktopTabState { final List tabs = []; final ScrollPosController scrollController = ScrollPosController(itemCount: 0); final PageController pageController = PageController(); int selected = 0; - DesktopTabBarState() { + DesktopTabState() { scrollController.itemCount = tabs.length; // TODO test // WidgetsBinding.instance.addPostFrameCallback((_) { @@ -49,8 +49,8 @@ class DesktopTabBarState { } } -class DesktopTabBarController { - final state = DesktopTabBarState().obs; +class DesktopTabController { + final state = DesktopTabState().obs; void add(TabInfo tab) { if (!isDesktop) return; @@ -112,7 +112,7 @@ class DesktopTab extends StatelessWidget { final Widget Function(Widget pageView)? pageViewBuilder; final Widget? tail; - final DesktopTabBarController controller; + final DesktopTabController controller; late final state = controller.state; DesktopTab( @@ -323,8 +323,8 @@ class WindowActionPanel extends StatelessWidget { // ignore: must_be_immutable class _ListView extends StatelessWidget { - final DesktopTabBarController controller; - late final Rx state; + final DesktopTabController controller; + late final Rx state; final Function(String key)? onTabClose; final TarBarTheme theme; From cc3c725f389a786ee79a66de50455e3997840f17 Mon Sep 17 00:00:00 2001 From: csf Date: Wed, 24 Aug 2022 20:56:42 +0800 Subject: [PATCH 3/6] refactor DesktopTab impl for connection_tab_page.dart --- flutter/lib/common.dart | 3 +- .../desktop/pages/connection_tab_page.dart | 111 ++++++++++-------- .../lib/desktop/widgets/tabbar_widget.dart | 47 +++----- 3 files changed, 83 insertions(+), 78 deletions(-) diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index ece2ec797..9944d6884 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -206,7 +206,8 @@ closeConnection({String? id}) { if (isAndroid || isIOS) { Navigator.popUntil(globalKey.currentContext!, ModalRoute.withName("/")); } else { - DesktopTabBar.close(id); + final controller = Get.find(); + controller.closeBy(id); } } diff --git a/flutter/lib/desktop/pages/connection_tab_page.dart b/flutter/lib/desktop/pages/connection_tab_page.dart index 407feddea..8f9d4f349 100644 --- a/flutter/lib/desktop/pages/connection_tab_page.dart +++ b/flutter/lib/desktop/pages/connection_tab_page.dart @@ -21,28 +21,36 @@ class ConnectionTabPage extends StatefulWidget { } class _ConnectionTabPageState extends State { - // refactor List when using multi-tab - // this singleton is only for test - RxList tabs = RxList.empty(growable: true); + final tabController = Get.put(DesktopTabController()); static final Rx _fullscreenID = "".obs; - final IconData selectedIcon = Icons.desktop_windows_sharp; - final IconData unselectedIcon = Icons.desktop_windows_outlined; + static final IconData selectedIcon = Icons.desktop_windows_sharp; + static final IconData unselectedIcon = Icons.desktop_windows_outlined; var connectionMap = RxList.empty(growable: true); _ConnectionTabPageState(Map params) { if (params['id'] != null) { - tabs.add(TabInfo( + tabController.state.value.tabs.add(TabInfo( key: params['id'], label: params['id'], selectedIcon: selectedIcon, - unselectedIcon: unselectedIcon)); + unselectedIcon: unselectedIcon, + closable: false, + page: RemotePage( + id: params['id'], + tabBarHeight: + _fullscreenID.value.isNotEmpty ? 0 : kDesktopRemoteTabBarHeight, + fullscreenID: _fullscreenID, + ))); } } @override void initState() { super.initState(); + + tabController.onRemove = (_, id) => onRemoveId(id); + rustDeskWinManager.setMethodHandler((call, fromWindowId) async { print( "call ${call.method} with args ${call.arguments} from window ${fromWindowId}"); @@ -51,18 +59,23 @@ class _ConnectionTabPageState extends State { final args = jsonDecode(call.arguments); final id = args['id']; window_on_top(windowId()); - DesktopTabBar.onAdd( - tabs, - TabInfo( - key: id, - label: id, - selectedIcon: selectedIcon, - unselectedIcon: unselectedIcon)); + tabController.add(TabInfo( + key: id, + label: id, + selectedIcon: selectedIcon, + unselectedIcon: unselectedIcon, + closable: false, + page: RemotePage( + id: id, + tabBarHeight: _fullscreenID.value.isNotEmpty + ? 0 + : kDesktopRemoteTabBarHeight, + fullscreenID: _fullscreenID, + ))); } else if (call.method == "onDestroy") { - print( - "executing onDestroy hook, closing ${tabs.map((tab) => tab.label).toList()}"); - tabs.forEach((tab) { - final tag = '${tab.label}'; + tabController.state.value.tabs.forEach((tab) { + print("executing onDestroy hook, closing ${tab.label}}"); + final tag = tab.label; ffi(tag).close().then((_) { Get.delete(tag: tag); }); @@ -74,49 +87,29 @@ class _ConnectionTabPageState extends State { @override Widget build(BuildContext context) { + final theme = isDarkTheme() ? TarBarTheme.dark() : TarBarTheme.light(); return SubWindowDragToResizeArea( windowId: windowId(), child: Container( decoration: BoxDecoration( border: Border.all(color: MyTheme.color(context).border!)), child: Scaffold( - backgroundColor: MyTheme.color(context).bg, - body: Column( - children: [ - Obx(() => Visibility( - visible: _fullscreenID.value.isEmpty, - child: DesktopTabBar( - tabs: tabs, - onTabClose: onRemoveId, - dark: isDarkTheme(), - mainTab: false, - ))), - Expanded(child: Obx(() { - WindowController.fromWindowId(windowId()) - .setFullscreen(_fullscreenID.value.isNotEmpty); - return PageView( - controller: DesktopTabBar.controller.value, - children: tabs - .map((tab) => RemotePage( - key: ValueKey(tab.label), - id: tab.label, - tabBarHeight: _fullscreenID.value.isNotEmpty - ? 0 - : kDesktopRemoteTabBarHeight, - fullscreenID: _fullscreenID, - )) //RemotePage(key: ValueKey(e), id: e)) - .toList()); - })), - ], - ), - ), + backgroundColor: MyTheme.color(context).bg, + body: DesktopTab( + controller: tabController, + theme: theme, + isMainWindow: false, + tail: AddButton( + theme: theme, + ).paddingOnly(left: 10), + )), ), ); } void onRemoveId(String id) { ffi(id).close(); - if (tabs.length == 0) { + if (tabController.state.value.tabs.length == 0) { WindowController.fromWindowId(windowId()).close(); } } @@ -125,3 +118,23 @@ class _ConnectionTabPageState extends State { return widget.params["windowId"]; } } + +class AddButton extends StatelessWidget { + late final TarBarTheme theme; + + AddButton({ + Key? key, + required this.theme, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return ActionIcon( + message: 'New Connection', + icon: IconFont.add, + theme: theme, + onTap: () => + rustDeskWinManager.call(WindowType.Main, "main_window_on_top", ""), + is_close: false); + } +} diff --git a/flutter/lib/desktop/widgets/tabbar_widget.dart b/flutter/lib/desktop/widgets/tabbar_widget.dart index 77757dd04..48116b374 100644 --- a/flutter/lib/desktop/widgets/tabbar_widget.dart +++ b/flutter/lib/desktop/widgets/tabbar_widget.dart @@ -5,7 +5,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_hbb/common.dart'; import 'package:flutter_hbb/consts.dart'; import 'package:flutter_hbb/main.dart'; -import 'package:flutter_hbb/utils/multi_window_manager.dart'; import 'package:get/get.dart'; import 'package:window_manager/window_manager.dart'; import 'package:scroll_pos/scroll_pos.dart'; @@ -52,6 +51,9 @@ class DesktopTabState { class DesktopTabController { final state = DesktopTabState().obs; + /// index, key + Function(int, String)? onRemove; + void add(TabInfo tab) { if (!isDesktop) return; final index = state.value.tabs.indexWhere((e) => e.key == tab.key); @@ -75,8 +77,9 @@ class DesktopTabController { void remove(int index) { if (!isDesktop) return; - if (index < 0) return; final len = state.value.tabs.length; + if (index < 0 || index > len - 1) return; + final key = state.value.tabs[index].key; final currentSelected = state.value.selected; int toIndex = 0; if (index == len - 1) { @@ -87,6 +90,7 @@ class DesktopTabController { state.value.tabs.removeAt(index); state.value.scrollController.itemCount = state.value.tabs.length; jumpTo(toIndex); + onRemove?.call(index, key); } void jumpTo(int index) { @@ -98,6 +102,19 @@ class DesktopTabController { // onSelected callback } + + void closeBy(String? key) { + if (!isDesktop) return; + assert(onRemove != null); + if (key == null) { + if (state.value.selected < state.value.tabs.length) { + remove(state.value.selected); + } + } else { + state.value.tabs.indexWhere((tab) => tab.key == key); + remove(state.value.selected); + } + } } class DesktopTab extends StatelessWidget { @@ -201,12 +218,6 @@ class DesktopTab extends StatelessWidget { theme: theme, )), ), - Offstage( - offstage: isMainWindow, - child: _AddButton( - theme: theme, - ).paddingOnly(left: 10), - ), ], ), ), @@ -447,26 +458,6 @@ class _Tab extends StatelessWidget { } } -class _AddButton extends StatelessWidget { - late final TarBarTheme theme; - - _AddButton({ - Key? key, - required this.theme, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - return ActionIcon( - message: 'New Connection', - icon: IconFont.add, - theme: theme, - onTap: () => - rustDeskWinManager.call(WindowType.Main, "main_window_on_top", ""), - is_close: false); - } -} - class _CloseButton extends StatelessWidget { final bool visiable; final bool tabSelected; From 4f4ac672287f34253b4e88a0c465bb24e98e3d19 Mon Sep 17 00:00:00 2001 From: csf Date: Wed, 24 Aug 2022 21:09:18 +0800 Subject: [PATCH 4/6] refactor DesktopTab impl for file_manager_tab_page.dart --- .../desktop/pages/connection_tab_page.dart | 21 ----- .../desktop/pages/file_manager_tab_page.dart | 81 ++++++++----------- .../lib/desktop/widgets/tabbar_widget.dart | 27 +++++-- 3 files changed, 55 insertions(+), 74 deletions(-) diff --git a/flutter/lib/desktop/pages/connection_tab_page.dart b/flutter/lib/desktop/pages/connection_tab_page.dart index 8f9d4f349..cf221c4d0 100644 --- a/flutter/lib/desktop/pages/connection_tab_page.dart +++ b/flutter/lib/desktop/pages/connection_tab_page.dart @@ -35,7 +35,6 @@ class _ConnectionTabPageState extends State { label: params['id'], selectedIcon: selectedIcon, unselectedIcon: unselectedIcon, - closable: false, page: RemotePage( id: params['id'], tabBarHeight: @@ -118,23 +117,3 @@ class _ConnectionTabPageState extends State { return widget.params["windowId"]; } } - -class AddButton extends StatelessWidget { - late final TarBarTheme theme; - - AddButton({ - Key? key, - required this.theme, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - return ActionIcon( - message: 'New Connection', - icon: IconFont.add, - theme: theme, - onTap: () => - rustDeskWinManager.call(WindowType.Main, "main_window_on_top", ""), - is_close: false); - } -} diff --git a/flutter/lib/desktop/pages/file_manager_tab_page.dart b/flutter/lib/desktop/pages/file_manager_tab_page.dart index 78f0842ad..7ae8e36b3 100644 --- a/flutter/lib/desktop/pages/file_manager_tab_page.dart +++ b/flutter/lib/desktop/pages/file_manager_tab_page.dart @@ -20,25 +20,26 @@ class FileManagerTabPage extends StatefulWidget { } class _FileManagerTabPageState extends State { - // refactor List when using multi-tab - // this singleton is only for test - RxList tabs = List.empty(growable: true).obs; - final IconData selectedIcon = Icons.file_copy_sharp; - final IconData unselectedIcon = Icons.file_copy_outlined; + final tabController = Get.put(DesktopTabController()); + + static final IconData selectedIcon = Icons.file_copy_sharp; + static final IconData unselectedIcon = Icons.file_copy_outlined; _FileManagerTabPageState(Map params) { - if (params['id'] != null) { - tabs.add(TabInfo( - key: params['id'], - label: params['id'], - selectedIcon: selectedIcon, - unselectedIcon: unselectedIcon)); - } + tabController.state.value.tabs.add(TabInfo( + key: params['id'], + label: params['id'], + selectedIcon: selectedIcon, + unselectedIcon: unselectedIcon, + page: FileManagerPage(id: params['id']))); } @override void initState() { super.initState(); + + tabController.onRemove = (_, id) => onRemoveId(id); + rustDeskWinManager.setMethodHandler((call, fromWindowId) async { print( "call ${call.method} with args ${call.arguments} from window ${fromWindowId}"); @@ -47,18 +48,16 @@ class _FileManagerTabPageState extends State { final args = jsonDecode(call.arguments); final id = args['id']; window_on_top(windowId()); - DesktopTabBar.onAdd( - tabs, - TabInfo( - key: id, - label: id, - selectedIcon: selectedIcon, - unselectedIcon: unselectedIcon)); + tabController.add(TabInfo( + key: id, + label: id, + selectedIcon: selectedIcon, + unselectedIcon: unselectedIcon, + page: FileManagerPage(id: id))); } else if (call.method == "onDestroy") { - print( - "executing onDestroy hook, closing ${tabs.map((tab) => tab.label).toList()}"); - tabs.forEach((tab) { - final tag = 'ft_${tab.label}'; + tabController.state.value.tabs.forEach((tab) { + print("executing onDestroy hook, closing ${tab.label}}"); + final tag = tab.label; ffi(tag).close().then((_) { Get.delete(tag: tag); }); @@ -70,43 +69,29 @@ class _FileManagerTabPageState extends State { @override Widget build(BuildContext context) { + final theme = isDarkTheme() ? TarBarTheme.dark() : TarBarTheme.light(); return SubWindowDragToResizeArea( windowId: windowId(), child: Container( decoration: BoxDecoration( border: Border.all(color: MyTheme.color(context).border!)), child: Scaffold( - backgroundColor: MyTheme.color(context).bg, - body: Column( - children: [ - DesktopTabBar( - tabs: tabs, - onTabClose: onRemoveId, - dark: isDarkTheme(), - mainTab: false, - ), - Expanded( - child: Obx( - () => PageView( - controller: DesktopTabBar.controller.value, - children: tabs - .map((tab) => FileManagerPage( - key: ValueKey(tab.label), - id: tab - .label)) //RemotePage(key: ValueKey(e), id: e)) - .toList()), - ), - ) - ], - ), - ), + backgroundColor: MyTheme.color(context).bg, + body: DesktopTab( + controller: tabController, + theme: theme, + isMainWindow: false, + tail: AddButton( + theme: theme, + ).paddingOnly(left: 10), + )), ), ); } void onRemoveId(String id) { ffi("ft_$id").close(); - if (tabs.length == 0) { + if (tabController.state.value.tabs.length == 0) { WindowController.fromWindowId(windowId()).close(); } } diff --git a/flutter/lib/desktop/widgets/tabbar_widget.dart b/flutter/lib/desktop/widgets/tabbar_widget.dart index 48116b374..8aa8377c6 100644 --- a/flutter/lib/desktop/widgets/tabbar_widget.dart +++ b/flutter/lib/desktop/widgets/tabbar_widget.dart @@ -9,6 +9,8 @@ import 'package:get/get.dart'; import 'package:window_manager/window_manager.dart'; import 'package:scroll_pos/scroll_pos.dart'; +import '../../utils/multi_window_manager.dart'; + const double _kTabBarHeight = kDesktopRemoteTabBarHeight; const double _kIconSize = 18; const double _kDividerIndent = 10; @@ -40,11 +42,6 @@ class DesktopTabState { DesktopTabState() { scrollController.itemCount = tabs.length; - // TODO test - // WidgetsBinding.instance.addPostFrameCallback((_) { - // scrollController.scrollToItem(selected, - // center: true, animate: true); - // }); } } @@ -533,6 +530,26 @@ class ActionIcon extends StatelessWidget { } } +class AddButton extends StatelessWidget { + late final TarBarTheme theme; + + AddButton({ + Key? key, + required this.theme, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return ActionIcon( + message: 'New Connection', + icon: IconFont.add, + theme: theme, + onTap: () => + rustDeskWinManager.call(WindowType.Main, "main_window_on_top", ""), + is_close: false); + } +} + class TarBarTheme { final Color unSelectedtabIconColor; final Color selectedtabIconColor; From 67b40b2cc7bc97be82034cfc81454a05fa300d75 Mon Sep 17 00:00:00 2001 From: csf Date: Wed, 24 Aug 2022 21:20:50 +0800 Subject: [PATCH 5/6] fix full screen --- .../desktop/pages/connection_tab_page.dart | 22 ++++++++----- .../lib/desktop/widgets/tabbar_widget.dart | 32 +++++++++++-------- 2 files changed, 32 insertions(+), 22 deletions(-) diff --git a/flutter/lib/desktop/pages/connection_tab_page.dart b/flutter/lib/desktop/pages/connection_tab_page.dart index cf221c4d0..66f342919 100644 --- a/flutter/lib/desktop/pages/connection_tab_page.dart +++ b/flutter/lib/desktop/pages/connection_tab_page.dart @@ -94,14 +94,20 @@ class _ConnectionTabPageState extends State { border: Border.all(color: MyTheme.color(context).border!)), child: Scaffold( backgroundColor: MyTheme.color(context).bg, - body: DesktopTab( - controller: tabController, - theme: theme, - isMainWindow: false, - tail: AddButton( - theme: theme, - ).paddingOnly(left: 10), - )), + body: Obx(() => DesktopTab( + controller: tabController, + theme: theme, + isMainWindow: false, + showTabBar: _fullscreenID.value.isEmpty, + tail: AddButton( + theme: theme, + ).paddingOnly(left: 10), + pageViewBuilder: (pageView) { + WindowController.fromWindowId(windowId()) + .setFullscreen(_fullscreenID.value.isNotEmpty); + return pageView; + }, + ))), ), ); } diff --git a/flutter/lib/desktop/widgets/tabbar_widget.dart b/flutter/lib/desktop/widgets/tabbar_widget.dart index 8aa8377c6..afac932ec 100644 --- a/flutter/lib/desktop/widgets/tabbar_widget.dart +++ b/flutter/lib/desktop/widgets/tabbar_widget.dart @@ -118,6 +118,7 @@ class DesktopTab extends StatelessWidget { final Function(String)? onTabClose; final TarBarTheme theme; final bool isMainWindow; + final bool showTabBar; final bool showLogo; final bool showTitle; final bool showMinimize; @@ -134,6 +135,7 @@ class DesktopTab extends StatelessWidget { required this.isMainWindow, this.theme = const TarBarTheme.light(), this.onTabClose, + this.showTabBar = true, this.showLogo = true, this.showTitle = true, this.showMinimize = true, @@ -145,21 +147,23 @@ class DesktopTab extends StatelessWidget { @override Widget build(BuildContext context) { return Column(children: [ - Container( - height: _kTabBarHeight, - child: Column( - children: [ - Container( - height: _kTabBarHeight - 1, - child: _buildBar(), + Offstage( + offstage: !showTabBar, + child: Container( + height: _kTabBarHeight, + child: Column( + children: [ + Container( + height: _kTabBarHeight - 1, + child: _buildBar(), + ), + Divider( + height: 1, + thickness: 1, + ), + ], ), - Divider( - height: 1, - thickness: 1, - ), - ], - ), - ), + )), Expanded( child: pageViewBuilder != null ? pageViewBuilder!(_buildPageView()) From e78d44935a35643495f1b21db13cc7b27d7eacfc Mon Sep 17 00:00:00 2001 From: csf Date: Wed, 24 Aug 2022 21:52:21 +0800 Subject: [PATCH 6/6] refactor DesktopTab impl for cm --- flutter/lib/desktop/pages/server_page.dart | 82 ++++++++----------- .../lib/desktop/widgets/tabbar_widget.dart | 6 +- flutter/lib/models/chat_model.dart | 1 + flutter/lib/models/server_model.dart | 46 ++++++----- 4 files changed, 64 insertions(+), 71 deletions(-) diff --git a/flutter/lib/desktop/pages/server_page.dart b/flutter/lib/desktop/pages/server_page.dart index bfcc28382..d96efc710 100644 --- a/flutter/lib/desktop/pages/server_page.dart +++ b/flutter/lib/desktop/pages/server_page.dart @@ -79,6 +79,8 @@ class ConnectionManagerState extends State { @override void initState() { gFFI.serverModel.updateClientState(); + gFFI.serverModel.tabController.onSelected = (index) => + gFFI.chatModel.changeCurrentID(gFFI.serverModel.clients[index].id); // test // gFFI.serverModel.clients.forEach((client) { // DesktopTabBar.onAdd( @@ -103,38 +105,20 @@ class ConnectionManagerState extends State { ), ], ) - : Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SizedBox( - height: kTextTabBarHeight, - child: Obx(() => DesktopTabBar( - dark: isDarkTheme(), - mainTab: true, - tabs: serverModel.tabs, - showTitle: false, - showMaximize: false, - showMinimize: false, - onSelected: (index) => gFFI.chatModel - .changeCurrentID(serverModel.clients[index].id), - )), - ), - Expanded( - child: Row(children: [ - Expanded( - child: PageView( - controller: DesktopTabBar.controller.value, - children: serverModel.clients - .map((client) => buildConnectionCard(client)) - .toList(growable: false))), + : DesktopTab( + theme: isDarkTheme() ? TarBarTheme.dark() : TarBarTheme.light(), + showTitle: false, + showMaximize: false, + showMinimize: false, + controller: serverModel.tabController, + isMainWindow: true, + pageViewBuilder: (pageView) => Row(children: [ + Expanded(child: pageView), Consumer( builder: (_, model, child) => model.isShowChatPage ? Expanded(child: Scaffold(body: ChatPage())) : Offstage()) - ]), - ) - ], - ); + ])); } Widget buildTitleBar(Widget middle) { @@ -156,23 +140,6 @@ class ConnectionManagerState extends State { ); } - Widget buildConnectionCard(Client client) { - return Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - key: ValueKey(client.id), - children: [ - _CmHeader(client: client), - client.isFileTransfer ? Offstage() : _PrivilegeBoard(client: client), - Expanded( - child: Align( - alignment: Alignment.bottomCenter, - child: _CmControlPanel(client: client), - )) - ], - ).paddingSymmetric(vertical: 8.0, horizontal: 8.0); - } - Widget buildTab(Client client) { return Tab( child: Row( @@ -191,6 +158,23 @@ class ConnectionManagerState extends State { } } +Widget buildConnectionCard(Client client) { + return Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + key: ValueKey(client.id), + children: [ + _CmHeader(client: client), + client.isFileTransfer ? Offstage() : _PrivilegeBoard(client: client), + Expanded( + child: Align( + alignment: Alignment.bottomCenter, + child: _CmControlPanel(client: client), + )) + ], + ).paddingSymmetric(vertical: 8.0, horizontal: 8.0); +} + class _AppIcon extends StatelessWidget { const _AppIcon({Key? key}) : super(key: key); @@ -421,9 +405,11 @@ class _CmControlPanel extends StatelessWidget { @override Widget build(BuildContext context) { - return client.authorized - ? buildAuthorized(context) - : buildUnAuthorized(context); + return Consumer(builder: (_, model, child) { + return client.authorized + ? buildAuthorized(context) + : buildUnAuthorized(context); + }); } buildAuthorized(BuildContext context) { diff --git a/flutter/lib/desktop/widgets/tabbar_widget.dart b/flutter/lib/desktop/widgets/tabbar_widget.dart index afac932ec..3b88deae6 100644 --- a/flutter/lib/desktop/widgets/tabbar_widget.dart +++ b/flutter/lib/desktop/widgets/tabbar_widget.dart @@ -51,6 +51,8 @@ class DesktopTabController { /// index, key Function(int, String)? onRemove; + Function(int)? onSelected; + void add(TabInfo tab) { if (!isDesktop) return; final index = state.value.tabs.indexWhere((e) => e.key == tab.key); @@ -96,8 +98,7 @@ class DesktopTabController { val.pageController.jumpToPage(index); val.scrollController.scrollToItem(index, center: true, animate: true); }); - - // onSelected callback + onSelected?.call(index); } void closeBy(String? key) { @@ -172,7 +173,6 @@ class DesktopTab extends StatelessWidget { } Widget _buildPageView() { - debugPrint("_buildPageView: ${state.value.tabs.length}"); return Obx(() => PageView( controller: state.value.pageController, children: diff --git a/flutter/lib/models/chat_model.dart b/flutter/lib/models/chat_model.dart index a42b10ee2..de949c782 100644 --- a/flutter/lib/models/chat_model.dart +++ b/flutter/lib/models/chat_model.dart @@ -200,6 +200,7 @@ class ChatModel with ChangeNotifier { if (!_isShowChatPage) { toggleCMChatPage(id); } + _ffi.target?.serverModel.jumpTo(id); late final chatUser; if (id == clientModeID) { diff --git a/flutter/lib/models/server_model.dart b/flutter/lib/models/server_model.dart index dec13f245..fa7f15e54 100644 --- a/flutter/lib/models/server_model.dart +++ b/flutter/lib/models/server_model.dart @@ -4,10 +4,10 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_hbb/models/platform_model.dart'; -import 'package:get/get_rx/src/rx_types/rx_types.dart'; import 'package:wakelock/wakelock.dart'; import '../common.dart'; +import '../desktop/pages/server_page.dart' as Desktop; import '../desktop/widgets/tabbar_widget.dart'; import '../mobile/pages/server_page.dart'; import 'model.dart'; @@ -32,7 +32,7 @@ class ServerModel with ChangeNotifier { late final TextEditingController _serverId; final _serverPasswd = TextEditingController(text: ""); - RxList tabs = RxList.empty(growable: true); + final tabController = DesktopTabController(); List _clients = []; @@ -352,16 +352,15 @@ class ServerModel with ChangeNotifier { exit(0); } _clients.clear(); - tabs.clear(); + tabController.state.value.tabs.clear(); for (var clientJson in clientsJson) { final client = Client.fromJson(clientJson); _clients.add(client); - DesktopTabBar.onAdd( - tabs, - TabInfo( - key: client.id.toString(), - label: client.name, - closable: false)); + tabController.add(TabInfo( + key: client.id.toString(), + label: client.name, + closable: false, + page: Desktop.buildConnectionCard(client))); } notifyListeners(); } catch (e) { @@ -376,10 +375,11 @@ class ServerModel with ChangeNotifier { return; } _clients.add(client); - DesktopTabBar.onAdd( - tabs, - TabInfo( - key: client.id.toString(), label: client.name, closable: false)); + tabController.add(TabInfo( + key: client.id.toString(), + label: client.name, + closable: false, + page: Desktop.buildConnectionCard(client))); scrollToBottom(); notifyListeners(); if (isAndroid) showLoginDialog(client); @@ -456,7 +456,7 @@ class ServerModel with ChangeNotifier { bind.cmLoginRes(connId: client.id, res: res); parent.target?.invokeMethod("cancel_notification", client.id); final index = _clients.indexOf(client); - DesktopTabBar.remove(tabs, index); + tabController.remove(index); _clients.remove(client); } } @@ -471,10 +471,11 @@ class ServerModel with ChangeNotifier { } else { _clients[index].authorized = true; } - DesktopTabBar.onAdd( - tabs, - TabInfo( - key: client.id.toString(), label: client.name, closable: false)); + tabController.add(TabInfo( + key: client.id.toString(), + label: client.name, + closable: false, + page: Desktop.buildConnectionCard(client))); scrollToBottom(); notifyListeners(); } catch (e) {} @@ -486,7 +487,7 @@ class ServerModel with ChangeNotifier { if (_clients.any((c) => c.id == id)) { final index = _clients.indexWhere((client) => client.id == id); _clients.removeAt(index); - DesktopTabBar.remove(tabs, index); + tabController.remove(index); parent.target?.dialogManager.dismissByTag(getLoginDialogTag(id)); parent.target?.invokeMethod("cancel_notification", id); } @@ -501,7 +502,12 @@ class ServerModel with ChangeNotifier { bind.cmCloseConnection(connId: client.id); }); _clients.clear(); - tabs.clear(); + tabController.state.value.tabs.clear(); + } + + void jumpTo(int id) { + final index = _clients.indexWhere((client) => client.id == id); + tabController.jumpTo(index); } }