mirror of
https://github.com/rustdesk/rustdesk.git
synced 2024-11-29 08:19:05 +08:00
Merge pull request #1346 from Heap-Hop/refactor_desktop_tab
refactor: flutter_desktop DesktopTab
This commit is contained in:
commit
06064ee9f5
@ -206,7 +206,8 @@ closeConnection({String? id}) {
|
||||
if (isAndroid || isIOS) {
|
||||
Navigator.popUntil(globalKey.currentContext!, ModalRoute.withName("/"));
|
||||
} else {
|
||||
DesktopTabBar.close(id);
|
||||
final controller = Get.find<DesktopTabController>();
|
||||
controller.closeBy(id);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -21,28 +21,35 @@ class ConnectionTabPage extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
||||
// refactor List<int> when using multi-tab
|
||||
// this singleton is only for test
|
||||
RxList<TabInfo> tabs = RxList<TabInfo>.empty(growable: true);
|
||||
final tabController = Get.put(DesktopTabController());
|
||||
static final Rx<String> _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<Widget>.empty(growable: true);
|
||||
|
||||
_ConnectionTabPageState(Map<String, dynamic> 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,
|
||||
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 +58,23 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
||||
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<FFI>(tag: tag);
|
||||
});
|
||||
@ -74,49 +86,35 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
||||
|
||||
@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: 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;
|
||||
},
|
||||
))),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void onRemoveId(String id) {
|
||||
ffi(id).close();
|
||||
if (tabs.length == 0) {
|
||||
if (tabController.state.value.tabs.length == 0) {
|
||||
WindowController.fromWindowId(windowId()).close();
|
||||
}
|
||||
}
|
||||
|
@ -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<DesktopTabPage> {
|
||||
late RxList<TabInfo> tabs;
|
||||
final tabController = DesktopTabController();
|
||||
|
||||
@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);
|
||||
tabController.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: tabController,
|
||||
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));
|
||||
tabController.add(TabInfo(
|
||||
key: kTabLabelSettingPage,
|
||||
label: kTabLabelSettingPage,
|
||||
selectedIcon: Icons.build_sharp,
|
||||
unselectedIcon: Icons.build_outlined,
|
||||
page: DesktopSettingPage()));
|
||||
}
|
||||
}
|
||||
|
@ -20,25 +20,26 @@ class FileManagerTabPage extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _FileManagerTabPageState extends State<FileManagerTabPage> {
|
||||
// refactor List<int> when using multi-tab
|
||||
// this singleton is only for test
|
||||
RxList<TabInfo> tabs = List<TabInfo>.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<String, dynamic> 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<FileManagerTabPage> {
|
||||
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<FFI>(tag: tag);
|
||||
});
|
||||
@ -70,43 +69,29 @@ class _FileManagerTabPageState extends State<FileManagerTabPage> {
|
||||
|
||||
@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();
|
||||
}
|
||||
}
|
||||
|
@ -79,6 +79,8 @@ class ConnectionManagerState extends State<ConnectionManager> {
|
||||
@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<ConnectionManager> {
|
||||
),
|
||||
],
|
||||
)
|
||||
: 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<ChatModel>(
|
||||
builder: (_, model, child) => model.isShowChatPage
|
||||
? Expanded(child: Scaffold(body: ChatPage()))
|
||||
: Offstage())
|
||||
]),
|
||||
)
|
||||
],
|
||||
);
|
||||
]));
|
||||
}
|
||||
|
||||
Widget buildTitleBar(Widget middle) {
|
||||
@ -156,23 +140,6 @@ class ConnectionManagerState extends State<ConnectionManager> {
|
||||
);
|
||||
}
|
||||
|
||||
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<ConnectionManager> {
|
||||
}
|
||||
}
|
||||
|
||||
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<ServerModel>(builder: (_, model, child) {
|
||||
return client.authorized
|
||||
? buildAuthorized(context)
|
||||
: buildUnAuthorized(context);
|
||||
});
|
||||
}
|
||||
|
||||
buildAuthorized(BuildContext context) {
|
||||
|
@ -5,221 +5,239 @@ 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';
|
||||
|
||||
import '../../utils/multi_window_manager.dart';
|
||||
|
||||
const double _kTabBarHeight = kDesktopRemoteTabBarHeight;
|
||||
const double _kIconSize = 18;
|
||||
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<TabInfo> 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 DesktopTabState {
|
||||
final List<TabInfo> tabs = [];
|
||||
final ScrollPosController scrollController =
|
||||
ScrollPosController(itemCount: 0);
|
||||
static final Rx<PageController> controller = PageController().obs;
|
||||
static final Rx<int> selected = 0.obs;
|
||||
static final _tabBarListViewKey = GlobalKey();
|
||||
final PageController pageController = PageController();
|
||||
int selected = 0;
|
||||
|
||||
DesktopTabBar(
|
||||
{Key? key,
|
||||
required this.tabs,
|
||||
DesktopTabState() {
|
||||
scrollController.itemCount = tabs.length;
|
||||
}
|
||||
}
|
||||
|
||||
class DesktopTabController {
|
||||
final state = DesktopTabState().obs;
|
||||
|
||||
/// 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);
|
||||
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;
|
||||
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) {
|
||||
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);
|
||||
onRemove?.call(index, key);
|
||||
}
|
||||
|
||||
void jumpTo(int index) {
|
||||
state.update((val) {
|
||||
val!.selected = index;
|
||||
val.pageController.jumpToPage(index);
|
||||
val.scrollController.scrollToItem(index, center: true, animate: true);
|
||||
});
|
||||
onSelected?.call(index);
|
||||
}
|
||||
|
||||
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 {
|
||||
final Function(String)? onTabClose;
|
||||
final TarBarTheme theme;
|
||||
final bool isMainWindow;
|
||||
final bool showTabBar;
|
||||
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 DesktopTabController 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.showTabBar = true,
|
||||
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(
|
||||
return Column(children: [
|
||||
Offstage(
|
||||
offstage: !showTabBar,
|
||||
child: Container(
|
||||
height: _kTabBarHeight,
|
||||
child: Column(
|
||||
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),
|
||||
),
|
||||
],
|
||||
),
|
||||
Container(
|
||||
height: _kTabBarHeight - 1,
|
||||
child: _buildBar(),
|
||||
),
|
||||
Offstage(
|
||||
offstage: onAddSetting == null,
|
||||
child: _ActionIcon(
|
||||
message: 'Settings',
|
||||
icon: IconFont.menu,
|
||||
theme: _theme,
|
||||
onTap: () => onAddSetting?.call(),
|
||||
is_close: false,
|
||||
),
|
||||
Divider(
|
||||
height: 1,
|
||||
thickness: 1,
|
||||
),
|
||||
WindowActionPanel(
|
||||
mainTab: mainTab,
|
||||
theme: _theme,
|
||||
showMinimize: showMinimize,
|
||||
showMaximize: showMaximize,
|
||||
showClose: showClose,
|
||||
)
|
||||
],
|
||||
),
|
||||
)),
|
||||
Expanded(
|
||||
child: pageViewBuilder != null
|
||||
? pageViewBuilder!(_buildPageView())
|
||||
: _buildPageView())
|
||||
]);
|
||||
}
|
||||
|
||||
Widget _buildPageView() {
|
||||
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,
|
||||
)),
|
||||
),
|
||||
],
|
||||
),
|
||||
Divider(
|
||||
height: 1,
|
||||
thickness: 1,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Offstage(offstage: tail == null, child: tail),
|
||||
WindowActionPanel(
|
||||
mainTab: isMainWindow,
|
||||
theme: theme,
|
||||
showMinimize: showMinimize,
|
||||
showMaximize: showMaximize,
|
||||
showClose: showClose,
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
static onAdd(RxList<TabInfo> 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<TabInfo> 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<TabInfo> 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 +258,7 @@ class WindowActionPanel extends StatelessWidget {
|
||||
children: [
|
||||
Offstage(
|
||||
offstage: !showMinimize,
|
||||
child: _ActionIcon(
|
||||
child: ActionIcon(
|
||||
message: 'Minimize',
|
||||
icon: IconFont.min,
|
||||
theme: theme,
|
||||
@ -269,7 +287,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 +315,7 @@ class WindowActionPanel extends StatelessWidget {
|
||||
})),
|
||||
Offstage(
|
||||
offstage: !showClose,
|
||||
child: _ActionIcon(
|
||||
child: ActionIcon(
|
||||
message: 'Close',
|
||||
icon: IconFont.close,
|
||||
theme: theme,
|
||||
@ -317,69 +335,37 @@ class WindowActionPanel extends StatelessWidget {
|
||||
|
||||
// ignore: must_be_immutable
|
||||
class _ListView extends StatelessWidget {
|
||||
final Rx<PageController> controller;
|
||||
final ScrollPosController scrollController;
|
||||
final RxList<TabInfo> tabInfos;
|
||||
final Rx<int> selected;
|
||||
final DesktopTabController controller;
|
||||
late final Rx<DesktopTabState> 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 +379,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,
|
||||
@ -473,31 +459,11 @@ class _Tab extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class _AddButton extends StatelessWidget {
|
||||
late final _Theme 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;
|
||||
final Function onClose;
|
||||
late final _Theme theme;
|
||||
late final TarBarTheme theme;
|
||||
|
||||
_CloseButton({
|
||||
Key? key,
|
||||
@ -528,13 +494,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 +534,54 @@ 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 AddButton extends StatelessWidget {
|
||||
late final TarBarTheme theme;
|
||||
|
||||
_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);
|
||||
}
|
||||
AddButton({
|
||||
Key? key,
|
||||
required this.theme,
|
||||
}) : super(key: key);
|
||||
|
||||
_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;
|
||||
@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;
|
||||
final Color selectedTextColor;
|
||||
final Color unSelectedTextColor;
|
||||
final Color selectedIconColor;
|
||||
final Color unSelectedIconColor;
|
||||
final Color dividerColor;
|
||||
final Color hoverColor;
|
||||
|
||||
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
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -200,6 +200,7 @@ class ChatModel with ChangeNotifier {
|
||||
if (!_isShowChatPage) {
|
||||
toggleCMChatPage(id);
|
||||
}
|
||||
_ffi.target?.serverModel.jumpTo(id);
|
||||
|
||||
late final chatUser;
|
||||
if (id == clientModeID) {
|
||||
|
@ -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<TabInfo> tabs = RxList<TabInfo>.empty(growable: true);
|
||||
final tabController = DesktopTabController();
|
||||
|
||||
List<Client> _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);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user