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;