diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index 4a0a0dc82..309ae9892 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -155,7 +155,7 @@ class MyTheme { brightness: Brightness.light, primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, - tabBarTheme: TabBarTheme( + tabBarTheme: const TabBarTheme( labelColor: Colors.black87, ), splashColor: Colors.transparent, @@ -163,13 +163,14 @@ class MyTheme { ).copyWith( extensions: >[ ColorThemeExtension.light, + TabbarTheme.light, ], ); static ThemeData darkTheme = ThemeData( brightness: Brightness.dark, primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, - tabBarTheme: TabBarTheme( + tabBarTheme: const TabBarTheme( labelColor: Colors.white70, ), splashColor: Colors.transparent, @@ -177,12 +178,17 @@ class MyTheme { ).copyWith( extensions: >[ ColorThemeExtension.dark, + TabbarTheme.dark, ], ); static ColorThemeExtension color(BuildContext context) { return Theme.of(context).extension()!; } + + static TabbarTheme tabbar(BuildContext context) { + return Theme.of(context).extension()!; + } } bool isDarkTheme() { diff --git a/flutter/lib/desktop/pages/connection_tab_page.dart b/flutter/lib/desktop/pages/connection_tab_page.dart index d9bc86fe2..8f5350792 100644 --- a/flutter/lib/desktop/pages/connection_tab_page.dart +++ b/flutter/lib/desktop/pages/connection_tab_page.dart @@ -83,7 +83,6 @@ class _ConnectionTabPageState extends State { @override Widget build(BuildContext context) { - final theme = isDarkTheme() ? TarBarTheme.dark() : TarBarTheme.light(); final RxBool fullscreen = Get.find(tag: 'fullscreen'); return Obx(() => SubWindowDragToResizeArea( resizeEdgeSize: fullscreen.value ? 1.0 : 8.0, @@ -95,14 +94,11 @@ class _ConnectionTabPageState extends State { backgroundColor: MyTheme.color(context).bg, body: Obx(() => DesktopTab( controller: tabController, - theme: theme, showTabBar: fullscreen.isFalse, onClose: () { tabController.clear(); }, - tail: AddButton( - theme: theme, - ).paddingOnly(left: 10), + tail: AddButton().paddingOnly(left: 10), pageViewBuilder: (pageView) { WindowController.fromWindowId(windowId()) .setFullscreen(fullscreen.isTrue); diff --git a/flutter/lib/desktop/pages/desktop_tab_page.dart b/flutter/lib/desktop/pages/desktop_tab_page.dart index 0546f0503..87082284b 100644 --- a/flutter/lib/desktop/pages/desktop_tab_page.dart +++ b/flutter/lib/desktop/pages/desktop_tab_page.dart @@ -48,17 +48,11 @@ class _DesktopTabPageState extends State { backgroundColor: MyTheme.color(context).bg, body: DesktopTab( controller: tabController, - theme: dark - ? const TarBarTheme.dark() - : const TarBarTheme.light(), tail: ActionIcon( message: 'Settings', icon: IconFont.menu, - theme: dark - ? const TarBarTheme.dark() - : const TarBarTheme.light(), onTap: onAddSetting, - is_close: false, + isClose: false, ), )); }) diff --git a/flutter/lib/desktop/pages/file_manager_tab_page.dart b/flutter/lib/desktop/pages/file_manager_tab_page.dart index add5eed9f..18ea039a7 100644 --- a/flutter/lib/desktop/pages/file_manager_tab_page.dart +++ b/flutter/lib/desktop/pages/file_manager_tab_page.dart @@ -62,8 +62,6 @@ class _FileManagerTabPageState extends State { @override Widget build(BuildContext context) { - final theme = - isDarkTheme() ? const TarBarTheme.dark() : const TarBarTheme.light(); return SubWindowDragToResizeArea( windowId: windowId(), child: Container( @@ -73,13 +71,10 @@ class _FileManagerTabPageState extends State { backgroundColor: MyTheme.color(context).bg, body: DesktopTab( controller: tabController, - theme: theme, onClose: () { tabController.clear(); }, - tail: AddButton( - theme: theme, - ).paddingOnly(left: 10), + tail: AddButton().paddingOnly(left: 10), )), ), ); diff --git a/flutter/lib/desktop/pages/port_forward_tab_page.dart b/flutter/lib/desktop/pages/port_forward_tab_page.dart index 2340a4ca1..e0384b614 100644 --- a/flutter/lib/desktop/pages/port_forward_tab_page.dart +++ b/flutter/lib/desktop/pages/port_forward_tab_page.dart @@ -69,7 +69,6 @@ class _PortForwardTabPageState extends State { @override Widget build(BuildContext context) { - final theme = isDarkTheme() ? TarBarTheme.dark() : TarBarTheme.light(); return SubWindowDragToResizeArea( windowId: windowId(), child: Container( @@ -79,13 +78,10 @@ class _PortForwardTabPageState extends State { backgroundColor: MyTheme.color(context).bg, body: DesktopTab( controller: tabController, - theme: theme, onClose: () { tabController.clear(); }, - tail: AddButton( - theme: theme, - ).paddingOnly(left: 10), + tail: AddButton().paddingOnly(left: 10), )), ), ); diff --git a/flutter/lib/desktop/pages/server_page.dart b/flutter/lib/desktop/pages/server_page.dart index f64adfca2..ac2fb7caa 100644 --- a/flutter/lib/desktop/pages/server_page.dart +++ b/flutter/lib/desktop/pages/server_page.dart @@ -118,7 +118,6 @@ class ConnectionManagerState extends State { ], ) : DesktopTab( - theme: isDarkTheme() ? TarBarTheme.dark() : TarBarTheme.light(), showTitle: false, showMaximize: false, showMinimize: true, diff --git a/flutter/lib/desktop/widgets/tabbar_widget.dart b/flutter/lib/desktop/widgets/tabbar_widget.dart index 3c2b28ab0..5daa1aeb6 100644 --- a/flutter/lib/desktop/widgets/tabbar_widget.dart +++ b/flutter/lib/desktop/widgets/tabbar_widget.dart @@ -3,7 +3,7 @@ import 'dart:async'; import 'dart:math'; import 'package:desktop_multi_window/desktop_multi_window.dart'; -import 'package:flutter/material.dart'; +import 'package:flutter/material.dart' hide TabBarTheme; import 'package:flutter_hbb/common.dart'; import 'package:flutter_hbb/consts.dart'; import 'package:flutter_hbb/main.dart'; @@ -158,8 +158,7 @@ class DesktopTabController { class TabThemeConf { double iconSize; - TarBarTheme theme; - TabThemeConf({required this.iconSize, required this.theme}); + TabThemeConf({required this.iconSize}); } typedef TabBuilder = Widget Function( @@ -168,7 +167,6 @@ typedef LabelGetter = Rx Function(String key); class DesktopTab extends StatelessWidget { final Function(String)? onTabClose; - final TarBarTheme theme; final bool showTabBar; final bool showLogo; final bool showTitle; @@ -189,7 +187,6 @@ class DesktopTab extends StatelessWidget { DesktopTab({ Key? key, required this.controller, - this.theme = const TarBarTheme.light(), this.onTabClose, this.showTabBar = true, this.showLogo = true, @@ -213,15 +210,15 @@ class DesktopTab extends StatelessWidget { return Column(children: [ Offstage( offstage: !showTabBar, - child: Container( + child: SizedBox( height: _kTabBarHeight, child: Column( children: [ - Container( + SizedBox( height: _kTabBarHeight - 1, child: _buildBar(), ), - Divider( + const Divider( height: 1, thickness: 1, ), @@ -300,7 +297,7 @@ class DesktopTab extends StatelessWidget { )), Offstage( offstage: !showTitle, - child: Text( + child: const Text( "RustDesk", style: TextStyle(fontSize: 13), ).marginOnly(left: 2)) @@ -321,7 +318,6 @@ class DesktopTab extends StatelessWidget { child: _ListView( controller: controller, onTabClose: onTabClose, - theme: theme, tabBuilder: tabBuilder, labelGetter: labelGetter, )), @@ -334,7 +330,6 @@ class DesktopTab extends StatelessWidget { mainTab: isMainWindow, tabType: tabType, state: state, - theme: theme, showMinimize: showMinimize, showMaximize: showMaximize, showClose: showClose, @@ -349,7 +344,6 @@ class WindowActionPanel extends StatelessWidget { final bool mainTab; final DesktopTabType tabType; final Rx state; - final TarBarTheme theme; final bool showMinimize; final bool showMaximize; @@ -361,7 +355,6 @@ class WindowActionPanel extends StatelessWidget { required this.mainTab, required this.tabType, required this.state, - required this.theme, this.showMinimize = true, this.showMaximize = true, this.showClose = true, @@ -377,7 +370,6 @@ class WindowActionPanel extends StatelessWidget { child: ActionIcon( message: 'Minimize', icon: IconFont.min, - theme: theme, onTap: () { if (mainTab) { windowManager.minimize(); @@ -385,31 +377,30 @@ class WindowActionPanel extends StatelessWidget { WindowController.fromWindowId(windowId!).minimize(); } }, - is_close: false, + isClose: false, )), // TODO: drag makes window restore Offstage( offstage: !showMaximize, child: FutureBuilder(builder: (context, snapshot) { - RxBool is_maximized = false.obs; + RxBool isMaximized = false.obs; if (mainTab) { windowManager.isMaximized().then((maximized) { - is_maximized.value = maximized; + isMaximized.value = maximized; }); } else { final wc = WindowController.fromWindowId(windowId!); wc.isMaximized().then((maximized) { - is_maximized.value = maximized; + isMaximized.value = maximized; }); } return Obx( () => ActionIcon( - message: is_maximized.value ? "Restore" : "Maximize", - icon: is_maximized.value ? IconFont.restore : IconFont.max, - theme: theme, + message: isMaximized.value ? "Restore" : "Maximize", + icon: isMaximized.value ? IconFont.restore : IconFont.max, onTap: () { if (mainTab) { - if (is_maximized.value) { + if (isMaximized.value) { windowManager.unmaximize(); } else { windowManager.maximize(); @@ -417,15 +408,15 @@ class WindowActionPanel extends StatelessWidget { } else { // TODO: subwindow is maximized but first query result is not maximized. final wc = WindowController.fromWindowId(windowId!); - if (is_maximized.value) { + if (isMaximized.value) { wc.unmaximize(); } else { wc.maximize(); } } - is_maximized.value = !is_maximized.value; + isMaximized.value = !isMaximized.value; }, - is_close: false, + isClose: false, ), ); })), @@ -434,7 +425,6 @@ class WindowActionPanel extends StatelessWidget { child: ActionIcon( message: 'Close', icon: IconFont.close, - theme: theme, onTap: () async { action() { if (mainTab) { @@ -455,7 +445,7 @@ class WindowActionPanel extends StatelessWidget { action(); } }, - is_close: true, + isClose: true, )), ], ); @@ -490,17 +480,15 @@ class WindowActionPanel extends StatelessWidget { class _ListView extends StatelessWidget { final DesktopTabController controller; final Function(String key)? onTabClose; - final TarBarTheme theme; final TabBuilder? tabBuilder; final LabelGetter? labelGetter; Rx get state => controller.state; - _ListView( + const _ListView( {required this.controller, required this.onTabClose, - required this.theme, this.tabBuilder, this.labelGetter}); @@ -510,7 +498,7 @@ class _ListView extends StatelessWidget { controller: state.value.scrollController, scrollDirection: Axis.horizontal, shrinkWrap: true, - physics: BouncingScrollPhysics(), + physics: const BouncingScrollPhysics(), children: state.value.tabs.asMap().entries.map((e) { final index = e.key; final tab = e.value; @@ -525,7 +513,6 @@ class _ListView extends StatelessWidget { selected: state.value.selected, onClose: () => controller.remove(index), onSelected: () => controller.jumpTo(index), - theme: theme, tabBuilder: tabBuilder == null ? null : (Widget icon, Widget labelWidget, TabThemeConf themeConf) { @@ -542,31 +529,29 @@ class _ListView extends StatelessWidget { } class _Tab extends StatefulWidget { - late final int index; - late final Rx label; - late final IconData? selectedIcon; - late final IconData? unselectedIcon; - late final bool closable; - late final int selected; - late final Function() onClose; - late final Function() onSelected; - late final TarBarTheme theme; + final int index; + final Rx label; + final IconData? selectedIcon; + final IconData? unselectedIcon; + final bool closable; + final int selected; + final Function() onClose; + final Function() onSelected; final Widget Function(Widget icon, Widget label, TabThemeConf themeConf)? tabBuilder; - _Tab( - {Key? key, - required this.index, - required this.label, - this.selectedIcon, - this.unselectedIcon, - this.tabBuilder, - required this.closable, - required this.selected, - required this.onClose, - required this.onSelected, - required this.theme}) - : super(key: key); + const _Tab({ + Key? key, + required this.index, + required this.label, + this.selectedIcon, + this.unselectedIcon, + this.tabBuilder, + required this.closable, + required this.selected, + required this.onClose, + required this.onSelected, + }) : super(key: key); @override State<_Tab> createState() => _TabState(); @@ -586,8 +571,8 @@ class _TabState extends State<_Tab> with RestorationMixin { isSelected ? widget.selectedIcon : widget.unselectedIcon, size: _kIconSize, color: isSelected - ? widget.theme.selectedtabIconColor - : widget.theme.unSelectedtabIconColor, + ? MyTheme.tabbar(context).selectedTabIconColor + : MyTheme.tabbar(context).unSelectedTabIconColor, ).paddingOnly(right: 5)); final labelWidget = Obx(() { return Text( @@ -595,8 +580,8 @@ class _TabState extends State<_Tab> with RestorationMixin { textAlign: TextAlign.center, style: TextStyle( color: isSelected - ? widget.theme.selectedTextColor - : widget.theme.unSelectedTextColor), + ? MyTheme.tabbar(context).selectedTextColor + : MyTheme.tabbar(context).unSelectedTextColor), ); }); @@ -609,8 +594,8 @@ class _TabState extends State<_Tab> with RestorationMixin { ], ); } else { - return widget.tabBuilder!(icon, labelWidget, - TabThemeConf(iconSize: _kIconSize, theme: widget.theme)); + return widget.tabBuilder!( + icon, labelWidget, TabThemeConf(iconSize: _kIconSize)); } } @@ -639,7 +624,6 @@ class _TabState extends State<_Tab> with RestorationMixin { visiable: hover.value && widget.closable, tabSelected: isSelected, onClose: () => widget.onClose(), - theme: widget.theme, ))) ])).paddingSymmetric(horizontal: 10), Offstage( @@ -648,7 +632,7 @@ class _TabState extends State<_Tab> with RestorationMixin { width: 1, indent: _kDividerIndent, endIndent: _kDividerIndent, - color: widget.theme.dividerColor, + color: MyTheme.tabbar(context).dividerColor, thickness: 1, ), ) @@ -671,14 +655,12 @@ class _CloseButton extends StatelessWidget { final bool visiable; final bool tabSelected; final Function onClose; - late final TarBarTheme theme; - _CloseButton({ + const _CloseButton({ Key? key, required this.visiable, required this.tabSelected, required this.onClose, - required this.theme, }) : super(key: key); @override @@ -694,8 +676,8 @@ class _CloseButton extends StatelessWidget { Icons.close, size: _kIconSize, color: tabSelected - ? theme.selectedIconColor - : theme.unSelectedIconColor, + ? MyTheme.tabbar(context).selectedIconColor + : MyTheme.tabbar(context).unSelectedIconColor, ), ), )).paddingOnly(left: 5); @@ -705,16 +687,14 @@ class _CloseButton extends StatelessWidget { class ActionIcon extends StatelessWidget { final String message; final IconData icon; - final TarBarTheme theme; final Function() onTap; - final bool is_close; + final bool isClose; const ActionIcon({ Key? key, required this.message, required this.icon, - required this.theme, required this.onTap, - required this.is_close, + required this.isClose, }) : super(key: key); @override @@ -722,34 +702,32 @@ class ActionIcon extends StatelessWidget { RxBool hover = false.obs; return Obx(() => Tooltip( message: translate(message), - waitDuration: Duration(seconds: 1), + waitDuration: const Duration(seconds: 1), child: InkWell( - hoverColor: - is_close ? Color.fromARGB(255, 196, 43, 28) : theme.hoverColor, + hoverColor: isClose + ? const Color.fromARGB(255, 196, 43, 28) + : MyTheme.tabbar(context).hoverColor, onHover: (value) => hover.value = value, - child: Container( + onTap: onTap, + child: SizedBox( height: _kTabBarHeight - 1, width: _kTabBarHeight - 1, child: Icon( icon, - color: hover.value && is_close + color: hover.value && isClose ? Colors.white - : theme.unSelectedIconColor, + : MyTheme.tabbar(context).unSelectedIconColor, size: _kActionIconSize, ), ), - onTap: onTap, ), )); } } class AddButton extends StatelessWidget { - late final TarBarTheme theme; - - AddButton({ + const AddButton({ Key? key, - required this.theme, }) : super(key: key); @override @@ -757,41 +735,101 @@ class AddButton extends StatelessWidget { return ActionIcon( message: 'New Connection', icon: IconFont.add, - theme: theme, onTap: () => rustDeskWinManager.call(WindowType.Main, "main_window_on_top", ""), - is_close: false); + isClose: 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; +class TabbarTheme extends ThemeExtension { + final Color? selectedTabIconColor; + final Color? unSelectedTabIconColor; + 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 TabbarTheme( + {required this.selectedTabIconColor, + required this.unSelectedTabIconColor, + required this.selectedTextColor, + required this.unSelectedTextColor, + required this.selectedIconColor, + required this.unSelectedIconColor, + required this.dividerColor, + required this.hoverColor}); - 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; + static const light = TabbarTheme( + selectedTabIconColor: MyTheme.accent, + unSelectedTabIconColor: Color.fromARGB(255, 162, 203, 241), + 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: Color.fromARGB(51, 158, 158, 158)); + + static const dark = TabbarTheme( + selectedTabIconColor: MyTheme.accent, + unSelectedTabIconColor: Color.fromARGB(255, 30, 65, 98), + 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 + ThemeExtension copyWith({ + Color? selectedTabIconColor, + Color? unSelectedTabIconColor, + Color? selectedTextColor, + Color? unSelectedTextColor, + Color? selectedIconColor, + Color? unSelectedIconColor, + Color? dividerColor, + Color? hoverColor, + }) { + return TabbarTheme( + selectedTabIconColor: selectedTabIconColor ?? this.selectedTabIconColor, + unSelectedTabIconColor: + unSelectedTabIconColor ?? this.unSelectedTabIconColor, + selectedTextColor: selectedTextColor ?? this.selectedTextColor, + unSelectedTextColor: unSelectedTextColor ?? this.unSelectedTextColor, + selectedIconColor: selectedIconColor ?? this.selectedIconColor, + unSelectedIconColor: unSelectedIconColor ?? this.unSelectedIconColor, + dividerColor: dividerColor ?? this.dividerColor, + hoverColor: hoverColor ?? this.hoverColor, + ); + } + + @override + ThemeExtension lerp( + ThemeExtension? other, double t) { + if (other is! TabbarTheme) { + return this; + } + return TabbarTheme( + selectedTabIconColor: + Color.lerp(selectedTabIconColor, other.selectedTabIconColor, t), + unSelectedTabIconColor: + Color.lerp(unSelectedTabIconColor, other.unSelectedTabIconColor, t), + selectedTextColor: + Color.lerp(selectedTextColor, other.selectedTextColor, t), + unSelectedTextColor: + Color.lerp(unSelectedTextColor, other.unSelectedTextColor, t), + selectedIconColor: + Color.lerp(selectedIconColor, other.selectedIconColor, t), + unSelectedIconColor: + Color.lerp(unSelectedIconColor, other.unSelectedIconColor, t), + dividerColor: Color.lerp(dividerColor, other.dividerColor, t), + hoverColor: Color.lerp(hoverColor, other.hoverColor, t), + ); + } + + static color(BuildContext context) { + return Theme.of(context).extension()!; + } }