1. update DesktopTabBar for cm.

2. refactor server_model clients map -> list.
3. update tab changing events.
This commit is contained in:
csf
2022-08-22 20:18:31 +08:00
parent b33d1f216f
commit 14b8140e45
8 changed files with 325 additions and 202 deletions

View File

@@ -14,36 +14,19 @@ const double _kTabBarHeight = kDesktopRemoteTabBarHeight;
const double _kIconSize = 18;
const double _kDividerIndent = 10;
const double _kActionIconSize = 12;
final _tabBarKey = GlobalKey();
void closeTab(String? id) {
final tabBar = _tabBarKey.currentWidget as _ListView?;
if (tabBar == null) return;
final tabs = tabBar.tabs;
if (id == null) {
if (tabBar.selected.value < tabs.length) {
tabs[tabBar.selected.value].onClose();
}
} else {
for (final tab in tabs) {
if (tab.label == id) {
tab.onClose();
break;
}
}
}
}
class TabInfo {
late final String key;
late final String label;
late final IconData selectedIcon;
late final IconData unselectedIcon;
late final IconData? selectedIcon;
late final IconData? unselectedIcon;
late final bool closable;
TabInfo(
{required this.label,
required this.selectedIcon,
required this.unselectedIcon,
{required this.key,
required this.label,
this.selectedIcon,
this.unselectedIcon,
this.closable = true});
}
@@ -53,20 +36,33 @@ class DesktopTabBar extends StatelessWidget {
late final bool dark;
late final _Theme _theme;
late final bool mainTab;
late final Function()? onAddSetting;
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;
final ScrollPosController scrollController =
ScrollPosController(itemCount: 0);
static final Rx<PageController> controller = PageController().obs;
static final Rx<int> selected = 0.obs;
static final _tabBarListViewKey = GlobalKey();
DesktopTabBar({
Key? key,
required this.tabs,
this.onTabClose,
required this.dark,
required this.mainTab,
this.onAddSetting,
}) : _theme = dark ? _Theme.dark() : _Theme.light(),
DesktopTabBar(
{Key? key,
required this.tabs,
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((_) {
@@ -88,22 +84,23 @@ class DesktopTabBar extends StatelessWidget {
Expanded(
child: Row(
children: [
Offstage(
offstage: !mainTab,
child: Row(children: [
Image.asset(
'assets/logo.ico',
width: 20,
height: 20,
),
Text(
"RustDesk",
style: TextStyle(fontSize: 13),
).marginOnly(left: 2),
]).marginOnly(
left: 5,
right: 10,
),
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(
@@ -116,13 +113,14 @@ class DesktopTabBar extends StatelessWidget {
}
},
child: _ListView(
key: _tabBarKey,
key: _tabBarListViewKey,
controller: controller,
scrollController: scrollController,
tabInfos: tabs,
selected: selected,
onTabClose: onTabClose,
theme: _theme)),
theme: _theme,
onSelected: onSelected)),
),
Offstage(
offstage: mainTab,
@@ -146,6 +144,9 @@ class DesktopTabBar extends StatelessWidget {
WindowActionPanel(
mainTab: mainTab,
theme: _theme,
showMinimize: showMinimize,
showMaximize: showMaximize,
showClose: showClose,
)
],
),
@@ -160,7 +161,7 @@ class DesktopTabBar extends StatelessWidget {
}
static onAdd(RxList<TabInfo> tabs, TabInfo tab) {
int index = tabs.indexWhere((e) => e.label == tab.label);
int index = tabs.indexWhere((e) => e.key == tab.key);
if (index >= 0) {
selected.value = index;
} else {
@@ -168,86 +169,148 @@ class DesktopTabBar extends StatelessWidget {
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 (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 (index < 0 || index >= tabs.length) return;
selected.value = index;
controller.value.jumpToPage(selected.value);
}
static void close(String? key) {
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 bool showMinimize;
final bool showMaximize;
final bool showClose;
const WindowActionPanel(
{Key? key, required this.mainTab, required this.theme})
{Key? key,
required this.mainTab,
required this.theme,
this.showMinimize = true,
this.showMaximize = true,
this.showClose = true})
: super(key: key);
@override
Widget build(BuildContext context) {
return Row(
children: [
_ActionIcon(
message: 'Minimize',
icon: IconFont.min,
theme: theme,
onTap: () {
if (mainTab) {
windowManager.minimize();
} else {
WindowController.fromWindowId(windowId!).minimize();
}
},
is_close: false,
),
FutureBuilder(builder: (context, snapshot) {
RxBool is_maximized = false.obs;
if (mainTab) {
windowManager.isMaximized().then((maximized) {
is_maximized.value = maximized;
});
} else {
final wc = WindowController.fromWindowId(windowId!);
wc.isMaximized().then((maximized) {
is_maximized.value = maximized;
});
}
return Obx(
() => _ActionIcon(
message: is_maximized.value ? "Restore" : "Maximize",
icon: is_maximized.value ? IconFont.restore : IconFont.max,
Offstage(
offstage: !showMinimize,
child: _ActionIcon(
message: 'Minimize',
icon: IconFont.min,
theme: theme,
onTap: () {
if (mainTab) {
if (is_maximized.value) {
windowManager.unmaximize();
} else {
windowManager.maximize();
}
windowManager.minimize();
} else {
final wc = WindowController.fromWindowId(windowId!);
if (is_maximized.value) {
wc.unmaximize();
} else {
wc.maximize();
}
WindowController.fromWindowId(windowId!).minimize();
}
is_maximized.value = !is_maximized.value;
},
is_close: false,
),
);
}),
_ActionIcon(
message: 'Close',
icon: IconFont.close,
theme: theme,
onTap: () {
if (mainTab) {
windowManager.close();
} else {
WindowController.fromWindowId(windowId!).close();
}
},
is_close: true,
),
)),
Offstage(
offstage: !showMaximize,
child: FutureBuilder(builder: (context, snapshot) {
RxBool is_maximized = false.obs;
if (mainTab) {
windowManager.isMaximized().then((maximized) {
is_maximized.value = maximized;
});
} else {
final wc = WindowController.fromWindowId(windowId!);
wc.isMaximized().then((maximized) {
is_maximized.value = maximized;
});
}
return Obx(
() => _ActionIcon(
message: is_maximized.value ? "Restore" : "Maximize",
icon: is_maximized.value ? IconFont.restore : IconFont.max,
theme: theme,
onTap: () {
if (mainTab) {
if (is_maximized.value) {
windowManager.unmaximize();
} else {
WindowController.fromWindowId(windowId!).minimize();
}
} else {
final wc = WindowController.fromWindowId(windowId!);
if (is_maximized.value) {
wc.unmaximize();
} else {
final wc = WindowController.fromWindowId(windowId!);
wc.isMaximized().then((maximized) {
if (maximized) {
wc.unmaximize();
} else {
wc.maximize();
}
});
}
}
is_maximized.value = !is_maximized.value;
},
is_close: false,
),
);
})),
Offstage(
offstage: !showClose,
child: _ActionIcon(
message: 'Close',
icon: IconFont.close,
theme: theme,
onTap: () {
if (mainTab) {
windowManager.close();
} else {
WindowController.fromWindowId(windowId!).close();
}
},
is_close: true,
)),
],
);
}
@@ -259,19 +322,21 @@ class _ListView extends StatelessWidget {
final ScrollPosController scrollController;
final RxList<TabInfo> tabInfos;
final Rx<int> selected;
final Function(String label)? onTabClose;
final Function(String key)? onTabClose;
final _Theme _theme;
late List<_Tab> tabs;
late final void Function(int)? onSelected;
_ListView({
Key? key,
required this.controller,
required this.scrollController,
required this.tabInfos,
required this.selected,
required this.onTabClose,
required _Theme theme,
}) : _theme = 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);
@override
@@ -279,17 +344,16 @@ class _ListView extends StatelessWidget {
return Obx(() {
tabs = tabInfos.asMap().entries.map((e) {
int index = e.key;
String label = e.value.label;
return _Tab(
index: index,
label: label,
label: e.value.label,
selectedIcon: e.value.selectedIcon,
unselectedIcon: e.value.unselectedIcon,
closable: e.value.closable,
selected: selected.value,
onClose: () {
tabInfos.removeWhere((tab) => tab.label == label);
onTabClose?.call(label);
tabInfos.removeWhere((tab) => tab.key == e.value.key);
onTabClose?.call(e.value.key);
if (index <= selected.value) {
selected.value = max(0, selected.value - 1);
}
@@ -305,6 +369,7 @@ class _ListView extends StatelessWidget {
selected.value = index;
scrollController.scrollToItem(index, center: true, animate: true);
controller.value.jumpToPage(index);
onSelected?.call(selected.value);
},
theme: _theme,
);
@@ -322,8 +387,8 @@ class _ListView extends StatelessWidget {
class _Tab extends StatelessWidget {
late final int index;
late final String label;
late final IconData selectedIcon;
late final IconData unselectedIcon;
late final IconData? selectedIcon;
late final IconData? unselectedIcon;
late final bool closable;
late final int selected;
late final Function() onClose;
@@ -335,8 +400,8 @@ class _Tab extends StatelessWidget {
{Key? key,
required this.index,
required this.label,
required this.selectedIcon,
required this.unselectedIcon,
this.selectedIcon,
this.unselectedIcon,
required this.closable,
required this.selected,
required this.onClose,
@@ -346,6 +411,7 @@ class _Tab extends StatelessWidget {
@override
Widget build(BuildContext context) {
bool show_icon = selectedIcon != null && unselectedIcon != null;
bool is_selected = index == selected;
bool show_divider = index != selected - 1 && index != selected;
return Ink(
@@ -362,13 +428,15 @@ class _Tab extends StatelessWidget {
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
is_selected ? selectedIcon : unselectedIcon,
size: _kIconSize,
color: is_selected
? theme.selectedtabIconColor
: theme.unSelectedtabIconColor,
).paddingOnly(right: 5),
Offstage(
offstage: !show_icon,
child: Icon(
is_selected ? selectedIcon : unselectedIcon,
size: _kIconSize,
color: is_selected
? theme.selectedtabIconColor
: theme.unSelectedtabIconColor,
).paddingOnly(right: 5)),
Text(
translate(label),
textAlign: TextAlign.center,