2022-08-30 16:50:25 +08:00
import ' dart:async ' ;
2023-02-03 17:08:40 +08:00
import ' dart:io ' ;
2022-08-06 17:08:48 +08:00
import ' dart:math ' ;
2022-11-03 21:58:25 +08:00
import ' dart:ui ' as ui ;
2022-08-06 17:08:48 +08:00
2023-02-03 17:08:40 +08:00
import ' package:bot_toast/bot_toast.dart ' ;
2022-08-18 11:07:53 +08:00
import ' package:desktop_multi_window/desktop_multi_window.dart ' ;
2022-10-20 22:10:26 +08:00
import ' package:flutter/gestures.dart ' ;
2022-09-04 11:03:16 +08:00
import ' package:flutter/material.dart ' hide TabBarTheme ;
2022-08-06 17:08:48 +08:00
import ' package:flutter_hbb/common.dart ' ;
2023-02-03 17:08:40 +08:00
import ' package:flutter_hbb/common/shared_state.dart ' ;
2022-08-06 17:08:48 +08:00
import ' package:flutter_hbb/consts.dart ' ;
2022-08-18 11:07:53 +08:00
import ' package:flutter_hbb/main.dart ' ;
2022-08-30 16:50:25 +08:00
import ' package:flutter_hbb/models/platform_model.dart ' ;
2022-11-01 17:01:43 +08:00
import ' package:flutter_hbb/models/state_model.dart ' ;
2023-02-03 17:08:40 +08:00
import ' package:flutter_svg/flutter_svg.dart ' ;
2022-08-06 17:08:48 +08:00
import ' package:get/get.dart ' ;
2022-10-20 22:10:26 +08:00
import ' package:get/get_rx/src/rx_workers/utils/debouncer.dart ' ;
2022-08-18 10:54:09 +08:00
import ' package:scroll_pos/scroll_pos.dart ' ;
2022-08-30 16:45:47 +08:00
import ' package:window_manager/window_manager.dart ' ;
2022-08-06 17:08:48 +08:00
2022-08-24 21:09:18 +08:00
import ' ../../utils/multi_window_manager.dart ' ;
2022-08-06 17:08:48 +08:00
const double _kTabBarHeight = kDesktopRemoteTabBarHeight ;
const double _kIconSize = 18 ;
const double _kDividerIndent = 10 ;
2022-08-20 19:57:16 +08:00
const double _kActionIconSize = 12 ;
2022-08-06 17:08:48 +08:00
2022-08-11 16:03:04 +08:00
class TabInfo {
2023-06-07 20:31:54 +08:00
final String key ; // Notice: cm use client_id.toString() as key
2022-08-24 20:12:04 +08:00
final String label ;
final IconData ? selectedIcon ;
final IconData ? unselectedIcon ;
2022-09-08 21:03:20 +08:00
final bool closable ;
2022-09-08 19:26:55 +08:00
final VoidCallback ? onTabCloseButton ;
2022-10-26 22:50:36 +08:00
final VoidCallback ? onTap ;
2022-08-24 20:12:04 +08:00
final Widget page ;
2022-08-11 16:03:04 +08:00
2022-08-18 10:54:09 +08:00
TabInfo (
2022-08-22 20:18:31 +08:00
{ required this . key ,
required this . label ,
this . selectedIcon ,
this . unselectedIcon ,
2022-08-24 20:12:04 +08:00
this . closable = true ,
2022-09-08 19:26:55 +08:00
this . onTabCloseButton ,
2022-10-26 22:50:36 +08:00
this . onTap ,
2022-08-24 20:12:04 +08:00
required this . page } ) ;
2022-08-11 16:03:04 +08:00
}
2022-08-30 16:50:25 +08:00
enum DesktopTabType {
main ,
cm ,
remoteScreen ,
fileTransfer ,
portForward ,
2023-03-01 14:18:46 +08:00
install ,
2022-08-30 16:50:25 +08:00
}
2022-08-24 20:17:51 +08:00
class DesktopTabState {
2022-08-24 20:12:04 +08:00
final List < TabInfo > tabs = [ ] ;
2022-08-18 10:54:09 +08:00
final ScrollPosController scrollController =
ScrollPosController ( itemCount: 0 ) ;
2022-08-24 20:12:04 +08:00
final PageController pageController = PageController ( ) ;
int selected = 0 ;
2022-08-06 17:08:48 +08:00
2022-10-26 22:50:36 +08:00
TabInfo get selectedTabInfo = > tabs [ selected ] ;
2022-08-24 20:17:51 +08:00
DesktopTabState ( ) {
2022-08-18 10:54:09 +08:00
scrollController . itemCount = tabs . length ;
}
2022-08-24 20:12:04 +08:00
}
2022-08-06 17:08:48 +08:00
2022-11-03 21:58:25 +08:00
CancelFunc showRightMenu ( ToastBuilder builder ,
{ BuildContext ? context , Offset ? target } ) {
return BotToast . showAttachedWidget (
target: target ,
targetContext: context ,
verticalOffset: 0 ,
horizontalOffset: 0 ,
duration: Duration ( seconds: 4 ) ,
animationDuration: Duration ( milliseconds: 0 ) ,
animationReverseDuration: Duration ( milliseconds: 0 ) ,
preferDirection: PreferDirection . rightTop ,
ignoreContentClick: false ,
onlyOne: true ,
allowClick: true ,
enableSafeArea: true ,
backgroundColor: Color ( 0x00000000 ) ,
attachedBuilder: builder ,
) ;
}
2022-08-24 20:17:51 +08:00
class DesktopTabController {
final state = DesktopTabState ( ) . obs ;
2022-09-01 21:18:53 +08:00
final DesktopTabType tabType ;
2022-08-11 18:08:35 +08:00
2022-08-24 20:56:42 +08:00
/// index, key
2022-10-27 10:56:14 +08:00
Function ( int , String ) ? onRemoved ;
2023-06-07 20:31:54 +08:00
Function ( String ) ? onSelected ;
2022-08-24 21:52:21 +08:00
2022-10-27 10:56:14 +08:00
DesktopTabController (
{ required this . tabType , this . onRemoved , this . onSelected } ) ;
2022-09-01 21:18:53 +08:00
2022-10-08 17:27:30 +08:00
int get length = > state . value . tabs . length ;
2022-10-26 20:39:28 +08:00
void add ( TabInfo tab ) {
2022-08-23 15:25:18 +08:00
if ( ! isDesktop ) return ;
2022-08-24 20:12:04 +08:00
final index = state . value . tabs . indexWhere ( ( e ) = > e . key = = tab . key ) ;
int toIndex ;
2022-08-11 18:08:35 +08:00
if ( index > = 0 ) {
2022-08-24 20:12:04 +08:00
toIndex = index ;
2022-08-11 18:08:35 +08:00
} else {
2022-08-24 20:12:04 +08:00
state . update ( ( val ) {
val ! . tabs . add ( tab ) ;
} ) ;
2022-08-30 14:43:57 +08:00
state . value . scrollController . itemCount = state . value . tabs . length ;
2022-08-24 20:12:04 +08:00
toIndex = state . value . tabs . length - 1 ;
assert ( toIndex > = 0 ) ;
2022-08-11 18:08:35 +08:00
}
2022-08-22 20:18:31 +08:00
try {
2023-06-07 20:31:54 +08:00
// tabPage has not been initialized, call `onSelected` at the end of initState
jumpTo ( toIndex , callOnSelected: false ) ;
2022-08-22 20:18:31 +08:00
} catch ( e ) {
// call before binding controller will throw
2022-08-24 20:12:04 +08:00
debugPrint ( " Failed to jumpTo: $ e " ) ;
2022-08-22 20:18:31 +08:00
}
}
2022-08-24 20:12:04 +08:00
void remove ( int index ) {
2022-08-23 15:25:18 +08:00
if ( ! isDesktop ) return ;
2022-08-24 20:12:04 +08:00
final len = state . value . tabs . length ;
2022-08-24 20:56:42 +08:00
if ( index < 0 | | index > len - 1 ) return ;
final key = state . value . tabs [ index ] . key ;
2022-08-24 20:12:04 +08:00
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 ) ;
2022-08-22 20:18:31 +08:00
}
2022-08-24 20:12:04 +08:00
state . value . tabs . removeAt ( index ) ;
state . value . scrollController . itemCount = state . value . tabs . length ;
jumpTo ( toIndex ) ;
2022-10-27 10:56:14 +08:00
onRemoved ? . call ( index , key ) ;
2022-08-22 20:18:31 +08:00
}
2023-06-07 20:31:54 +08:00
/// For addTab, tabPage has not been initialized, set [callOnSelected] to false,
/// and call [onSelected] at the end of initState
void jumpTo ( int index , { bool callOnSelected = true } ) {
2022-09-01 21:18:53 +08:00
if ( ! isDesktop | | index < 0 ) return ;
2022-08-24 20:12:04 +08:00
state . update ( ( val ) {
val ! . selected = index ;
2022-10-26 20:02:20 +08:00
Future . delayed ( Duration ( milliseconds: 100 ) , ( ( ) {
2022-08-30 14:43:57 +08:00
if ( val . pageController . hasClients ) {
val . pageController . jumpToPage ( index ) ;
}
2022-10-26 20:02:20 +08:00
val . scrollController . itemCount = val . tabs . length ;
2022-08-30 14:43:57 +08:00
if ( val . scrollController . hasClients & &
2022-09-01 21:18:53 +08:00
val . scrollController . itemCount > index ) {
2022-10-20 22:10:26 +08:00
val . scrollController
. scrollToItem ( index , center: false , animate: true ) ;
2022-08-30 14:43:57 +08:00
}
} ) ) ;
2022-08-24 20:12:04 +08:00
} ) ;
2023-06-07 20:31:54 +08:00
if ( callOnSelected ) {
if ( state . value . tabs . length > index ) {
final key = state . value . tabs [ index ] . key ;
onSelected ? . call ( key ) ;
}
2022-09-01 21:18:53 +08:00
}
2022-08-11 18:08:35 +08:00
}
2022-08-24 20:56:42 +08:00
void closeBy ( String ? key ) {
if ( ! isDesktop ) return ;
2022-10-27 10:56:14 +08:00
assert ( onRemoved ! = null ) ;
2022-08-24 20:56:42 +08:00
if ( key = = null ) {
if ( state . value . selected < state . value . tabs . length ) {
remove ( state . value . selected ) ;
}
} else {
2022-09-08 19:26:55 +08:00
final index = state . value . tabs . indexWhere ( ( tab ) = > tab . key = = key ) ;
remove ( index ) ;
2022-08-24 20:56:42 +08:00
}
}
2022-08-30 20:48:03 +08:00
void clear ( ) {
state . value . tabs . clear ( ) ;
state . refresh ( ) ;
}
2023-07-06 09:40:03 +08:00
Widget ? widget ( String key ) {
return state . value . tabs . firstWhereOrNull ( ( tab ) = > tab . key = = key ) ? . page ;
}
2022-08-24 20:12:04 +08:00
}
2022-08-22 20:18:31 +08:00
2022-08-29 18:48:12 +08:00
class TabThemeConf {
double iconSize ;
2022-10-24 16:44:43 +08:00
2022-09-04 11:03:16 +08:00
TabThemeConf ( { required this . iconSize } ) ;
2022-08-29 18:48:12 +08:00
}
typedef TabBuilder = Widget Function (
String key , Widget icon , Widget label , TabThemeConf themeConf ) ;
2022-11-03 21:58:25 +08:00
typedef TabMenuBuilder = Widget Function ( String key ) ;
2022-08-29 18:48:12 +08:00
typedef LabelGetter = Rx < String > Function ( String key ) ;
2022-10-20 22:56:23 +08:00
/// [_lastClickTime], help to handle double click
2022-11-01 19:56:46 +08:00
int _lastClickTime =
2022-11-02 11:32:30 +08:00
DateTime . now ( ) . millisecondsSinceEpoch - bind . getDoubleClickTime ( ) - 1000 ;
2022-10-20 22:56:23 +08:00
2023-06-23 17:05:48 +08:00
// ignore: must_be_immutable
2022-08-24 20:12:04 +08:00
class DesktopTab extends StatelessWidget {
final bool showLogo ;
final bool showTitle ;
final bool showMinimize ;
final bool showMaximize ;
final bool showClose ;
final Widget Function ( Widget pageView ) ? pageViewBuilder ;
2022-11-03 21:58:25 +08:00
// Right click tab menu
final TabMenuBuilder ? tabMenuBuilder ;
2022-08-24 20:12:04 +08:00
final Widget ? tail ;
2022-09-09 19:29:19 +08:00
final Future < bool > Function ( ) ? onWindowCloseButton ;
2022-08-29 18:48:12 +08:00
final TabBuilder ? tabBuilder ;
final LabelGetter ? labelGetter ;
2022-10-20 22:10:26 +08:00
final double ? maxLabelWidth ;
2022-10-20 22:56:23 +08:00
final Color ? selectedTabBackgroundColor ;
final Color ? unSelectedTabBackgroundColor ;
2023-07-08 17:55:55 +08:00
final Color ? selectedBorderColor ;
2022-08-24 20:12:04 +08:00
2022-08-24 20:17:51 +08:00
final DesktopTabController controller ;
2022-10-24 16:44:43 +08:00
2022-08-30 20:48:03 +08:00
Rx < DesktopTabState > get state = > controller . state ;
2022-10-17 18:37:00 +08:00
final isMaximized = false . obs ;
2022-10-20 22:10:26 +08:00
final _scrollDebounce = Debouncer ( delay: Duration ( milliseconds: 50 ) ) ;
2022-09-01 21:18:53 +08:00
late final DesktopTabType tabType ;
late final bool isMainWindow ;
2022-08-24 20:12:04 +08:00
2022-09-01 21:18:53 +08:00
DesktopTab ( {
Key ? key ,
2022-08-29 18:48:12 +08:00
required this . controller ,
this . showLogo = true ,
2023-02-10 15:05:35 +08:00
this . showTitle = false ,
2022-08-29 18:48:12 +08:00
this . showMinimize = true ,
this . showMaximize = true ,
this . showClose = true ,
this . pageViewBuilder ,
2022-11-03 21:58:25 +08:00
this . tabMenuBuilder ,
2022-08-29 18:48:12 +08:00
this . tail ,
2022-09-08 19:26:55 +08:00
this . onWindowCloseButton ,
2022-08-29 18:48:12 +08:00
this . tabBuilder ,
this . labelGetter ,
2022-10-20 22:10:26 +08:00
this . maxLabelWidth ,
2022-10-20 22:56:23 +08:00
this . selectedTabBackgroundColor ,
this . unSelectedTabBackgroundColor ,
2023-07-08 17:55:55 +08:00
this . selectedBorderColor ,
2022-09-01 21:18:53 +08:00
} ) : super ( key: key ) {
tabType = controller . tabType ;
2023-03-01 14:18:46 +08:00
isMainWindow = tabType = = DesktopTabType . main | |
tabType = = DesktopTabType . cm | |
tabType = = DesktopTabType . install ;
2022-09-01 21:18:53 +08:00
}
2022-08-24 20:12:04 +08:00
2022-11-12 22:33:10 +08:00
static RxString labelGetterAlias ( String peerId ) {
final opt = ' alias ' ;
PeerStringOption . init ( peerId , opt , ( ) {
final alias = bind . mainGetPeerOptionSync ( id: peerId , key: opt ) ;
return alias . isEmpty ? peerId : alias ;
} ) ;
return PeerStringOption . find ( peerId , opt ) ;
}
2022-08-24 20:12:04 +08:00
@ override
Widget build ( BuildContext context ) {
return Column ( children: [
2022-11-01 17:01:43 +08:00
Obx ( ( ) = > Offstage (
2022-11-30 13:56:02 +08:00
offstage: ! stateGlobal . showTabBar . isTrue | |
( kUseCompatibleUiMode & & isHideSingleItem ( ) ) ,
2022-09-04 11:03:16 +08:00
child: SizedBox (
2022-08-24 21:20:50 +08:00
height: _kTabBarHeight ,
child: Column (
children: [
2022-09-04 11:03:16 +08:00
SizedBox (
2022-08-24 21:20:50 +08:00
height: _kTabBarHeight - 1 ,
child: _buildBar ( ) ,
) ,
2022-09-04 11:03:16 +08:00
const Divider (
2022-08-24 21:20:50 +08:00
height: 1 ,
) ,
] ,
2022-08-24 20:12:04 +08:00
) ,
2022-11-01 17:01:43 +08:00
) ) ) ,
2022-08-24 20:12:04 +08:00
Expanded (
child: pageViewBuilder ! = null
? pageViewBuilder ! ( _buildPageView ( ) )
: _buildPageView ( ) )
] ) ;
}
2022-08-30 16:50:25 +08:00
Widget _buildBlock ( { required Widget child } ) {
if ( tabType ! = DesktopTabType . main ) {
return child ;
}
2023-07-05 17:35:37 +08:00
return buildRemoteBlock (
child: child ,
use: ( ) async {
var access_mode = await bind . mainGetOption ( key: ' access-mode ' ) ;
var option = option2bool (
' allow-remote-config-modification ' ,
await bind . mainGetOption (
key: ' allow-remote-config-modification ' ) ) ;
return access_mode = = ' view ' | | ( access_mode . isEmpty & & ! option ) ;
} ) ;
2022-08-30 16:50:25 +08:00
}
2023-02-08 09:11:53 +08:00
List < Widget > _tabWidgets = [ ] ;
2022-08-24 20:12:04 +08:00
Widget _buildPageView ( ) {
2022-08-30 16:50:25 +08:00
return _buildBlock (
child: Obx ( ( ) = > PageView (
controller: state . value . pageController ,
2022-12-24 14:29:32 +08:00
physics: NeverScrollableScrollPhysics ( ) ,
2023-02-08 09:11:53 +08:00
children: ( ) {
/// to-do refactor, separate connection state and UI state for remote session.
/// [workaround] PageView children need an immutable list, after it has been passed into PageView
final tabLen = state . value . tabs . length ;
if ( tabLen = = _tabWidgets . length ) {
return _tabWidgets ;
} else if ( _tabWidgets . isNotEmpty & &
tabLen = = _tabWidgets . length + 1 ) {
/// On add. Use the previous list(pointer) to prevent item's state init twice.
/// *[_tabWidgets.isNotEmpty] means TabsWindow(remote_tab_page or file_manager_tab_page) opened before, but was hidden. In this case, we have to reload, otherwise the child can't be built.
_tabWidgets . add ( state . value . tabs . last . page ) ;
return _tabWidgets ;
} else {
/// On remove or change. Use new list(pointer) to reload list children so that items loading order is normal.
/// the Widgets in list must enable [AutomaticKeepAliveClientMixin]
final newList = state . value . tabs . map ( ( v ) = > v . page ) . toList ( ) ;
_tabWidgets = newList ;
return newList ;
}
} ( ) ) ) ) ;
2022-08-24 20:12:04 +08:00
}
2022-11-30 13:56:02 +08:00
/// Check whether to show ListView
///
/// Conditions:
/// - hide single item when only has one item (home) on [DesktopTabPage].
bool isHideSingleItem ( ) {
return state . value . tabs . length = = 1 & &
2023-03-01 14:18:46 +08:00
( controller . tabType = = DesktopTabType . main | |
controller . tabType = = DesktopTabType . install ) ;
2022-11-30 13:56:02 +08:00
}
2022-08-24 20:12:04 +08:00
Widget _buildBar ( ) {
return Row (
2022-10-20 22:10:26 +08:00
mainAxisAlignment: MainAxisAlignment . spaceBetween ,
2022-08-24 20:12:04 +08:00
children: [
2022-10-20 22:10:26 +08:00
Expanded (
child: GestureDetector (
// custom double tap handler
onTap: showMaximize
? ( ) {
final current = DateTime . now ( ) . millisecondsSinceEpoch ;
final elapsed = current - _lastClickTime ;
_lastClickTime = current ;
2022-11-02 11:32:30 +08:00
if ( elapsed < bind . getDoubleClickTime ( ) ) {
2022-10-20 22:10:26 +08:00
// onDoubleTap
toggleMaximize ( isMainWindow )
. then ( ( value ) = > isMaximized . value = value ) ;
}
}
2022-10-17 18:37:00 +08:00
: null ,
2022-10-14 19:44:57 +08:00
onPanStart: ( _ ) = > startDragging ( isMainWindow ) ,
2022-10-20 22:10:26 +08:00
child: Row (
children: [
Offstage (
offstage: ! Platform . isMacOS ,
child: const SizedBox (
width: 78 ,
) ) ,
2022-11-30 13:56:02 +08:00
Offstage (
2023-01-13 16:06:38 +08:00
offstage: kUseCompatibleUiMode | | Platform . isMacOS ,
2022-11-30 13:56:02 +08:00
child: Row ( children: [
Offstage (
offstage: ! showLogo ,
child: SvgPicture . asset (
' assets/logo.svg ' ,
width: 16 ,
height: 16 ,
) ) ,
Offstage (
offstage: ! showTitle ,
child: const Text (
" RustDesk " ,
style: TextStyle ( fontSize: 13 ) ,
) . marginOnly ( left: 2 ) )
] ) . marginOnly (
left: 5 ,
right: 10 ,
) ,
2022-10-20 22:10:26 +08:00
) ,
Expanded (
child: Listener (
// handle mouse wheel
onPointerSignal: ( e ) {
if ( e is PointerScrollEvent ) {
final sc =
controller . state . value . scrollController ;
if ( ! sc . canScroll ) return ;
_scrollDebounce . call ( ( ) {
sc . animateTo ( sc . offset + e . scrollDelta . dy ,
duration: Duration ( milliseconds: 200 ) ,
curve: Curves . ease ) ;
} ) ;
}
} ,
child: _ListView (
2023-07-08 17:55:55 +08:00
controller: controller ,
tabBuilder: tabBuilder ,
tabMenuBuilder: tabMenuBuilder ,
labelGetter: labelGetter ,
maxLabelWidth: maxLabelWidth ,
selectedTabBackgroundColor:
selectedTabBackgroundColor ,
unSelectedTabBackgroundColor:
unSelectedTabBackgroundColor ,
selectedBorderColor: selectedBorderColor ,
) ) ) ,
2022-10-20 22:10:26 +08:00
] ,
) ) ) ,
2022-11-30 13:56:02 +08:00
// hide simulated action buttons when we in compatible ui mode, because of reusing system title bar.
2022-11-30 14:05:49 +08:00
WindowActionPanel (
isMainWindow: isMainWindow ,
tabType: tabType ,
state: state ,
tail: tail ,
isMaximized: isMaximized ,
showMinimize: showMinimize ,
showMaximize: showMaximize ,
showClose: showClose ,
onClose: onWindowCloseButton ,
2022-08-24 20:12:04 +08:00
)
] ,
) ;
2022-08-22 20:18:31 +08:00
}
2022-08-06 17:08:48 +08:00
}
2022-10-14 09:48:33 +08:00
class WindowActionPanel extends StatefulWidget {
2022-10-14 19:44:57 +08:00
final bool isMainWindow ;
2022-09-01 12:07:05 +08:00
final DesktopTabType tabType ;
final Rx < DesktopTabState > state ;
2022-10-17 18:37:00 +08:00
final RxBool isMaximized ;
2022-08-18 11:07:53 +08:00
2022-08-22 20:18:31 +08:00
final bool showMinimize ;
final bool showMaximize ;
final bool showClose ;
2022-10-14 19:44:57 +08:00
final Widget ? tail ;
2022-09-09 19:29:19 +08:00
final Future < bool > Function ( ) ? onClose ;
2022-08-22 20:18:31 +08:00
2022-08-18 11:07:53 +08:00
const WindowActionPanel (
2022-08-22 20:18:31 +08:00
{ Key ? key ,
2022-10-14 19:44:57 +08:00
required this . isMainWindow ,
2022-09-01 12:07:05 +08:00
required this . tabType ,
required this . state ,
2022-10-17 18:37:00 +08:00
required this . isMaximized ,
2022-10-14 19:44:57 +08:00
this . tail ,
2022-08-22 20:18:31 +08:00
this . showMinimize = true ,
this . showMaximize = true ,
2022-08-30 20:48:03 +08:00
this . showClose = true ,
this . onClose } )
2022-08-18 11:07:53 +08:00
: super ( key: key ) ;
2022-10-14 09:48:33 +08:00
@ override
State < StatefulWidget > createState ( ) {
return WindowActionPanelState ( ) ;
}
}
class WindowActionPanelState extends State < WindowActionPanel >
with MultiWindowListener , WindowListener {
2023-07-26 00:40:03 +08:00
final _saveFrameDebounce = Debouncer ( delay: Duration ( seconds: 1 ) ) ;
2022-10-14 09:48:33 +08:00
@ override
void initState ( ) {
super . initState ( ) ;
DesktopMultiWindow . addListener ( this ) ;
windowManager . addListener ( this ) ;
2022-10-17 18:37:00 +08:00
Future . delayed ( Duration ( milliseconds: 500 ) , ( ) {
if ( widget . isMainWindow ) {
windowManager . isMaximized ( ) . then ( ( maximized ) {
if ( widget . isMaximized . value ! = maximized ) {
WidgetsBinding . instance . addPostFrameCallback (
( _ ) = > setState ( ( ) = > widget . isMaximized . value = maximized ) ) ;
}
} ) ;
} else {
2023-01-23 22:07:50 +08:00
final wc = WindowController . fromWindowId ( kWindowId ! ) ;
2022-10-17 18:37:00 +08:00
wc . isMaximized ( ) . then ( ( maximized ) {
debugPrint ( " isMaximized $ maximized " ) ;
if ( widget . isMaximized . value ! = maximized ) {
WidgetsBinding . instance . addPostFrameCallback (
( _ ) = > setState ( ( ) = > widget . isMaximized . value = maximized ) ) ;
}
} ) ;
}
} ) ;
2022-10-14 09:48:33 +08:00
}
@ override
void dispose ( ) {
DesktopMultiWindow . removeListener ( this ) ;
windowManager . removeListener ( this ) ;
super . dispose ( ) ;
}
2023-02-27 12:01:22 +08:00
void _setMaximize ( bool maximize ) {
2023-03-01 14:18:46 +08:00
stateGlobal . setMaximize ( maximize ) ;
2023-07-27 08:03:18 +08:00
_saveFrameDebounce . call ( _saveFrame ) ;
2023-03-01 14:18:46 +08:00
setState ( ( ) { } ) ;
2023-02-27 12:01:22 +08:00
}
2022-10-14 09:48:33 +08:00
@ override
void onWindowMaximize ( ) {
// catch maximize from system
2022-10-17 18:37:00 +08:00
if ( ! widget . isMaximized . value ) {
widget . isMaximized . value = true ;
2022-10-14 09:48:33 +08:00
}
2023-02-27 12:01:22 +08:00
_setMaximize ( true ) ;
2022-10-14 09:48:33 +08:00
super . onWindowMaximize ( ) ;
}
@ override
void onWindowUnmaximize ( ) {
// catch unmaximize from system
2022-10-17 18:37:00 +08:00
if ( widget . isMaximized . value ) {
widget . isMaximized . value = false ;
2022-10-14 09:48:33 +08:00
}
2023-02-27 12:01:22 +08:00
_setMaximize ( false ) ;
2022-10-14 09:48:33 +08:00
super . onWindowUnmaximize ( ) ;
}
2023-07-26 00:40:03 +08:00
_saveFrame ( ) async {
if ( widget . tabType = = DesktopTabType . main ) {
await saveWindowPosition ( WindowType . Main ) ;
} else if ( kWindowType ! = null & & kWindowId ! = null ) {
await saveWindowPosition ( kWindowType ! , windowId: kWindowId ) ;
}
}
@ override
void onWindowMoved ( ) {
_saveFrameDebounce . call ( _saveFrame ) ;
super . onWindowMoved ( ) ;
}
@ override
void onWindowResized ( ) {
_saveFrameDebounce . call ( _saveFrame ) ;
super . onWindowMoved ( ) ;
}
2022-11-06 17:39:19 +08:00
@ override
void onWindowClose ( ) async {
// hide window on close
if ( widget . isMainWindow ) {
2023-02-03 17:08:40 +08:00
if ( rustDeskWinManager . getActiveWindows ( ) . contains ( kMainWindowId ) ) {
await rustDeskWinManager . unregisterActiveWindow ( kMainWindowId ) ;
}
2023-02-22 09:04:43 +08:00
// macOS specific workaround, the window is not hiding when in fullscreen.
2023-02-21 21:39:32 +08:00
if ( Platform . isMacOS & & await windowManager . isFullScreen ( ) ) {
await windowManager . setFullScreen ( false ) ;
await Future . delayed ( Duration ( seconds: 1 ) ) ;
}
2022-11-06 17:39:19 +08:00
await windowManager . hide ( ) ;
} else {
2023-01-07 12:40:29 +08:00
// it's safe to hide the subwindow
2023-02-21 21:39:32 +08:00
final controller = WindowController . fromWindowId ( kWindowId ! ) ;
if ( Platform . isMacOS & & await controller . isFullScreen ( ) ) {
await controller . setFullscreen ( false ) ;
await Future . delayed ( Duration ( seconds: 1 ) ) ;
}
await controller . hide ( ) ;
2023-01-07 12:40:29 +08:00
await Future . wait ( [
rustDeskWinManager
2023-01-23 22:07:50 +08:00
. call ( WindowType . Main , kWindowEventHide , { " id " : kWindowId ! } ) ,
2023-01-07 12:40:29 +08:00
widget . onClose ? . call ( ) ? ? Future . microtask ( ( ) = > null )
] ) ;
2022-11-06 17:39:19 +08:00
}
super . onWindowClose ( ) ;
}
2022-08-18 11:07:53 +08:00
@ override
Widget build ( BuildContext context ) {
2022-10-20 22:10:26 +08:00
return Row (
mainAxisAlignment: MainAxisAlignment . end ,
2022-08-18 11:07:53 +08:00
children: [
2022-10-14 19:44:57 +08:00
Offstage ( offstage: widget . tail = = null , child: widget . tail ) ,
2022-08-22 20:18:31 +08:00
Offstage (
2022-11-30 14:05:49 +08:00
offstage: kUseCompatibleUiMode ,
child: Row (
children: [
Offstage (
2023-01-13 16:06:38 +08:00
offstage: ! widget . showMinimize | | Platform . isMacOS ,
2022-11-30 14:05:49 +08:00
child: ActionIcon (
2023-03-11 19:14:47 +08:00
message: ' Minimize ' ,
2022-11-30 14:05:49 +08:00
icon: IconFont . min ,
onTap: ( ) {
if ( widget . isMainWindow ) {
windowManager . minimize ( ) ;
} else {
2023-01-23 22:07:50 +08:00
WindowController . fromWindowId ( kWindowId ! ) . minimize ( ) ;
2022-11-30 14:05:49 +08:00
}
} ,
isClose: false ,
) ) ,
Offstage (
2023-01-13 16:06:38 +08:00
offstage: ! widget . showMaximize | | Platform . isMacOS ,
2022-11-30 14:05:49 +08:00
child: Obx ( ( ) = > ActionIcon (
message:
2023-03-11 19:14:47 +08:00
widget . isMaximized . value ? ' Restore ' : ' Maximize ' ,
2022-11-30 14:05:49 +08:00
icon: widget . isMaximized . value
? IconFont . restore
: IconFont . max ,
onTap: _toggleMaximize ,
isClose: false ,
) ) ) ,
Offstage (
2023-01-13 16:06:38 +08:00
offstage: ! widget . showClose | | Platform . isMacOS ,
2022-11-30 14:05:49 +08:00
child: ActionIcon (
2023-03-11 19:14:47 +08:00
message: ' Close ' ,
2022-11-30 14:05:49 +08:00
icon: IconFont . close ,
onTap: ( ) async {
final res = await widget . onClose ? . call ( ) ? ? true ;
if ( res ) {
// hide for all window
// note: the main window can be restored by tray icon
Future . delayed ( Duration . zero , ( ) async {
if ( widget . isMainWindow ) {
await windowManager . close ( ) ;
} else {
2023-01-23 22:07:50 +08:00
await WindowController . fromWindowId ( kWindowId ! )
2022-11-30 14:05:49 +08:00
. close ( ) ;
}
} ) ;
}
} ,
isClose: true ,
) )
] ,
) ,
) ,
2022-08-18 11:07:53 +08:00
] ,
2022-10-20 22:10:26 +08:00
) ;
2022-10-14 19:44:57 +08:00
}
void _toggleMaximize ( ) {
toggleMaximize ( widget . isMainWindow ) . then ( ( maximize ) {
2022-10-17 18:37:00 +08:00
if ( widget . isMaximized . value ! = maximize ) {
// update state for sub window, wc.unmaximize/maximize() will not invoke onWindowMaximize/Unmaximize
widget . isMaximized . value = maximize ;
2022-10-14 19:44:57 +08:00
}
} ) ;
}
}
void startDragging ( bool isMainWindow ) {
if ( isMainWindow ) {
windowManager . startDragging ( ) ;
} else {
2023-01-23 22:07:50 +08:00
WindowController . fromWindowId ( kWindowId ! ) . startDragging ( ) ;
2022-10-14 19:44:57 +08:00
}
}
/// return true -> window will be maximize
/// return false -> window will be unmaximize
Future < bool > toggleMaximize ( bool isMainWindow ) async {
if ( isMainWindow ) {
if ( await windowManager . isMaximized ( ) ) {
windowManager . unmaximize ( ) ;
return false ;
} else {
windowManager . maximize ( ) ;
return true ;
}
} else {
2023-01-23 22:07:50 +08:00
final wc = WindowController . fromWindowId ( kWindowId ! ) ;
2022-10-14 19:44:57 +08:00
if ( await wc . isMaximized ( ) ) {
wc . unmaximize ( ) ;
return false ;
} else {
wc . maximize ( ) ;
return true ;
}
2022-08-18 11:07:53 +08:00
}
2022-09-09 19:29:19 +08:00
}
2022-09-01 12:07:05 +08:00
2022-09-09 19:29:19 +08:00
Future < bool > closeConfirmDialog ( ) async {
2022-10-13 20:19:05 +08:00
var confirm = true ;
2023-05-08 12:34:19 +08:00
final res = await gFFI . dialogManager . show < bool > ( ( setState , close , context ) {
2022-10-13 20:19:05 +08:00
submit ( ) {
final opt = " enable-confirm-closing-tabs " ;
String value = bool2option ( opt , confirm ) ;
bind . mainSetOption ( key: opt , value: value ) ;
close ( true ) ;
}
2022-09-09 19:29:19 +08:00
return CustomAlertDialog (
title: Row ( children: [
const Icon ( Icons . warning_amber_sharp ,
color: Colors . redAccent , size: 28 ) ,
const SizedBox ( width: 10 ) ,
Text ( translate ( " Warning " ) ) ,
] ) ,
2022-10-13 20:19:05 +08:00
content: Column (
mainAxisAlignment: MainAxisAlignment . start ,
crossAxisAlignment: CrossAxisAlignment . start ,
children: [
Text ( translate ( " Disconnect all devices? " ) ) ,
CheckboxListTile (
contentPadding: const EdgeInsets . all ( 0 ) ,
dense: true ,
controlAffinity: ListTileControlAffinity . leading ,
title: Text (
translate ( " Confirm before closing multiple tabs " ) ,
) ,
value: confirm ,
onChanged: ( v ) {
if ( v = = null ) return ;
setState ( ( ) = > confirm = v ) ;
} ,
)
2022-10-24 16:44:43 +08:00
] ) ,
// confirm checkbox
2022-09-09 19:29:19 +08:00
actions: [
2023-01-15 19:46:16 +08:00
dialogButton ( " Cancel " , onPressed: close , isOutline: true ) ,
dialogButton ( " OK " , onPressed: submit ) ,
2022-09-09 19:29:19 +08:00
] ,
onSubmit: submit ,
onCancel: close ,
) ;
} ) ;
return res = = true ;
2022-08-18 11:07:53 +08:00
}
2022-08-18 10:54:09 +08:00
class _ListView extends StatelessWidget {
2022-08-24 20:17:51 +08:00
final DesktopTabController controller ;
2022-08-29 18:48:12 +08:00
final TabBuilder ? tabBuilder ;
2022-11-03 21:58:25 +08:00
final TabMenuBuilder ? tabMenuBuilder ;
2022-08-29 18:48:12 +08:00
final LabelGetter ? labelGetter ;
2022-10-20 22:10:26 +08:00
final double ? maxLabelWidth ;
2022-10-20 22:56:23 +08:00
final Color ? selectedTabBackgroundColor ;
2023-07-08 17:55:55 +08:00
final Color ? selectedBorderColor ;
2022-10-20 22:56:23 +08:00
final Color ? unSelectedTabBackgroundColor ;
2022-08-29 18:48:12 +08:00
2022-08-30 20:48:03 +08:00
Rx < DesktopTabState > get state = > controller . state ;
2022-08-18 10:54:09 +08:00
2022-11-03 21:58:25 +08:00
const _ListView ( {
required this . controller ,
this . tabBuilder ,
this . tabMenuBuilder ,
this . labelGetter ,
this . maxLabelWidth ,
this . selectedTabBackgroundColor ,
this . unSelectedTabBackgroundColor ,
2023-07-08 17:55:55 +08:00
this . selectedBorderColor ,
2022-11-03 21:58:25 +08:00
} ) ;
2022-08-18 10:54:09 +08:00
2022-09-23 11:01:33 +08:00
/// Check whether to show ListView
///
/// Conditions:
/// - hide single item when only has one item (home) on [DesktopTabPage].
bool isHideSingleItem ( ) {
return state . value . tabs . length = = 1 & &
2023-03-01 14:18:46 +08:00
controller . tabType = = DesktopTabType . main | |
controller . tabType = = DesktopTabType . install ;
2022-09-23 11:01:33 +08:00
}
2022-08-18 10:54:09 +08:00
@ override
Widget build ( BuildContext context ) {
2022-08-24 20:12:04 +08:00
return Obx ( ( ) = > ListView (
controller: state . value . scrollController ,
scrollDirection: Axis . horizontal ,
shrinkWrap: true ,
2022-09-04 11:03:16 +08:00
physics: const BouncingScrollPhysics ( ) ,
2022-09-23 11:01:33 +08:00
children: isHideSingleItem ( )
? List . empty ( )
: state . value . tabs . asMap ( ) . entries . map ( ( e ) {
final index = e . key ;
final tab = e . value ;
return _Tab (
2022-10-26 22:50:36 +08:00
key: ValueKey ( tab . key ) ,
2022-09-23 11:01:33 +08:00
index: index ,
2022-10-26 22:50:36 +08:00
tabInfoKey: tab . key ,
2022-09-23 11:01:33 +08:00
label: labelGetter = = null
? Rx < String > ( tab . label )
: labelGetter ! ( tab . label ) ,
selectedIcon: tab . selectedIcon ,
unselectedIcon: tab . unselectedIcon ,
closable: tab . closable ,
selected: state . value . selected ,
onClose: ( ) {
if ( tab . onTabCloseButton ! = null ) {
tab . onTabCloseButton ! ( ) ;
} else {
controller . remove ( index ) ;
}
2022-08-29 18:48:12 +08:00
} ,
2022-10-26 22:50:36 +08:00
onTap: ( ) {
controller . jumpTo ( index ) ;
tab . onTap ? . call ( ) ;
} ,
tabBuilder: tabBuilder ,
2022-11-03 21:58:25 +08:00
tabMenuBuilder: tabMenuBuilder ,
2022-10-20 22:10:26 +08:00
maxLabelWidth: maxLabelWidth ,
2023-02-08 09:11:53 +08:00
selectedTabBackgroundColor: selectedTabBackgroundColor ? ?
MyTheme . tabbar ( context ) . selectedTabBackgroundColor ,
2022-10-20 22:56:23 +08:00
unSelectedTabBackgroundColor: unSelectedTabBackgroundColor ,
2023-07-08 17:55:55 +08:00
selectedBorderColor: selectedBorderColor ,
2022-09-23 11:01:33 +08:00
) ;
} ) . toList ( ) ) ) ;
2022-08-18 10:54:09 +08:00
}
}
2022-08-31 19:23:32 +08:00
class _Tab extends StatefulWidget {
2022-09-04 11:03:16 +08:00
final int index ;
2022-10-26 22:50:36 +08:00
final String tabInfoKey ;
2022-09-04 11:03:16 +08:00
final Rx < String > label ;
final IconData ? selectedIcon ;
final IconData ? unselectedIcon ;
final bool closable ;
final int selected ;
final Function ( ) onClose ;
2022-10-26 22:50:36 +08:00
final Function ( ) onTap ;
2022-10-20 22:56:23 +08:00
final TabBuilder ? tabBuilder ;
2022-11-03 21:58:25 +08:00
final TabMenuBuilder ? tabMenuBuilder ;
2022-10-20 22:10:26 +08:00
final double ? maxLabelWidth ;
2022-10-20 22:56:23 +08:00
final Color ? selectedTabBackgroundColor ;
final Color ? unSelectedTabBackgroundColor ;
2023-07-08 17:55:55 +08:00
final Color ? selectedBorderColor ;
2022-08-06 17:08:48 +08:00
2022-09-04 11:03:16 +08:00
const _Tab ( {
Key ? key ,
required this . index ,
2022-10-26 22:50:36 +08:00
required this . tabInfoKey ,
2022-09-04 11:03:16 +08:00
required this . label ,
this . selectedIcon ,
this . unselectedIcon ,
this . tabBuilder ,
2022-11-03 21:58:25 +08:00
this . tabMenuBuilder ,
2022-09-04 11:03:16 +08:00
required this . closable ,
required this . selected ,
required this . onClose ,
2022-10-26 22:50:36 +08:00
required this . onTap ,
2022-10-20 22:10:26 +08:00
this . maxLabelWidth ,
2022-10-20 22:56:23 +08:00
this . selectedTabBackgroundColor ,
this . unSelectedTabBackgroundColor ,
2023-07-08 17:55:55 +08:00
this . selectedBorderColor ,
2022-09-04 11:03:16 +08:00
} ) : super ( key: key ) ;
2022-08-06 17:08:48 +08:00
2022-08-31 19:23:32 +08:00
@ override
State < _Tab > createState ( ) = > _TabState ( ) ;
}
class _TabState extends State < _Tab > with RestorationMixin {
final RestorableBool restoreHover = RestorableBool ( false ) ;
2022-08-29 18:48:12 +08:00
Widget _buildTabContent ( ) {
2022-08-31 19:23:32 +08:00
bool showIcon =
widget . selectedIcon ! = null & & widget . unselectedIcon ! = null ;
bool isSelected = widget . index = = widget . selected ;
2022-08-29 18:48:12 +08:00
final icon = Offstage (
offstage: ! showIcon ,
child: Icon (
2022-08-31 19:23:32 +08:00
isSelected ? widget . selectedIcon : widget . unselectedIcon ,
2022-08-29 18:48:12 +08:00
size: _kIconSize ,
color: isSelected
2022-09-04 11:03:16 +08:00
? MyTheme . tabbar ( context ) . selectedTabIconColor
: MyTheme . tabbar ( context ) . unSelectedTabIconColor ,
2022-08-29 18:48:12 +08:00
) . paddingOnly ( right: 5 ) ) ;
final labelWidget = Obx ( ( ) {
2022-10-20 22:10:26 +08:00
return ConstrainedBox (
constraints: BoxConstraints ( maxWidth: widget . maxLabelWidth ? ? 200 ) ,
child: Text (
2023-03-24 17:39:31 +08:00
translate ( widget . label . value ) ,
2022-10-20 22:10:26 +08:00
textAlign: TextAlign . center ,
style: TextStyle (
color: isSelected
? MyTheme . tabbar ( context ) . selectedTextColor
: MyTheme . tabbar ( context ) . unSelectedTextColor ) ,
overflow: TextOverflow . ellipsis ,
) ) ;
2022-08-29 18:48:12 +08:00
} ) ;
2022-11-03 21:58:25 +08:00
Widget getWidgetWithBuilder ( ) {
if ( widget . tabBuilder = = null ) {
return Row (
mainAxisAlignment: MainAxisAlignment . center ,
children: [
icon ,
labelWidget ,
] ,
) ;
} else {
return widget . tabBuilder ! (
widget . tabInfoKey ,
2022-08-29 18:48:12 +08:00
icon ,
labelWidget ,
2022-11-03 21:58:25 +08:00
TabThemeConf ( iconSize: _kIconSize ) ,
) ;
}
2022-08-29 18:48:12 +08:00
}
2022-11-03 21:58:25 +08:00
return Listener (
onPointerDown: ( e ) {
if ( e . kind ! = ui . PointerDeviceKind . mouse ) {
return ;
}
if ( e . buttons = = 2 ) {
if ( widget . tabMenuBuilder ! = null ) {
showRightMenu (
( cacel ) {
return widget . tabMenuBuilder ! ( widget . tabInfoKey ) ;
} ,
target: e . position ,
) ;
}
}
} ,
child: getWidgetWithBuilder ( ) ,
) ;
2022-08-29 18:48:12 +08:00
}
2022-08-06 17:08:48 +08:00
@ override
Widget build ( BuildContext context ) {
2022-08-31 19:23:32 +08:00
bool isSelected = widget . index = = widget . selected ;
bool showDivider =
widget . index ! = widget . selected - 1 & & widget . index ! = widget . selected ;
RxBool hover = restoreHover . value . obs ;
2022-08-20 19:57:16 +08:00
return Ink (
child: InkWell (
2022-08-31 19:23:32 +08:00
onHover: ( value ) {
hover . value = value ;
restoreHover . value = value ;
} ,
2022-10-26 22:50:36 +08:00
onTap: ( ) = > widget . onTap ( ) ,
2022-10-20 22:56:23 +08:00
child: Container (
2023-07-08 17:55:55 +08:00
decoration: isSelected & & widget . selectedBorderColor ! = null
? BoxDecoration (
border: Border (
bottom: BorderSide (
color: widget . selectedBorderColor ! ,
width: 1 ,
) ,
) ,
)
: null ,
child: Container (
color: isSelected
? widget . selectedTabBackgroundColor
: widget . unSelectedTabBackgroundColor ,
child: Row (
children: [
SizedBox (
height: _kTabBarHeight ,
child: Row (
crossAxisAlignment: CrossAxisAlignment . center ,
children: [
_buildTabContent ( ) ,
Obx ( ( ( ) = > _CloseButton (
visible: hover . value & & widget . closable ,
tabSelected: isSelected ,
onClose: ( ) = > widget . onClose ( ) ,
) ) )
] ) ) . paddingOnly ( left: 10 , right: 5 ) ,
Offstage (
offstage: ! showDivider ,
child: VerticalDivider (
width: 1 ,
indent: _kDividerIndent ,
endIndent: _kDividerIndent ,
color: MyTheme . tabbar ( context ) . dividerColor ,
) ,
)
] ,
) ,
) ) ,
2022-08-20 19:57:16 +08:00
) ,
2022-08-06 17:08:48 +08:00
) ;
}
2022-08-31 19:23:32 +08:00
@ override
String ? get restorationId = > " _Tab ${ widget . label . value } " ;
@ override
void restoreState ( RestorationBucket ? oldBucket , bool initialRestore ) {
registerForRestoration ( restoreHover , ' restoreHover ' ) ;
}
2022-08-06 17:08:48 +08:00
}
class _CloseButton extends StatelessWidget {
2023-01-09 15:30:39 +08:00
final bool visible ;
2022-08-09 22:35:29 +08:00
final bool tabSelected ;
2022-08-06 17:08:48 +08:00
final Function onClose ;
2022-09-04 11:03:16 +08:00
const _CloseButton ( {
2022-08-06 17:08:48 +08:00
Key ? key ,
2023-01-09 15:30:39 +08:00
required this . visible ,
2022-08-09 22:35:29 +08:00
required this . tabSelected ,
2022-08-06 17:08:48 +08:00
required this . onClose ,
} ) : super ( key: key ) ;
@ override
Widget build ( BuildContext context ) {
2022-08-11 16:03:04 +08:00
return SizedBox (
width: _kIconSize ,
child: Offstage (
2023-01-09 15:30:39 +08:00
offstage: ! visible ,
2022-08-11 16:03:04 +08:00
child: InkWell (
2023-01-29 23:30:49 +08:00
hoverColor: MyTheme . tabbar ( context ) . closeHoverColor ,
2023-02-05 17:29:54 +08:00
customBorder: const CircleBorder ( ) ,
2022-08-11 16:03:04 +08:00
onTap: ( ) = > onClose ( ) ,
child: Icon (
Icons . close ,
size: _kIconSize ,
color: tabSelected
2022-09-04 11:03:16 +08:00
? MyTheme . tabbar ( context ) . selectedIconColor
: MyTheme . tabbar ( context ) . unSelectedIconColor ,
2022-08-11 16:03:04 +08:00
) ,
) ,
2023-01-29 23:30:49 +08:00
) ) . paddingOnly ( left: 10 ) ;
2022-08-06 17:08:48 +08:00
}
}
2022-10-24 16:44:43 +08:00
class ActionIcon extends StatefulWidget {
2022-10-20 22:22:02 +08:00
final String ? message ;
2022-08-20 19:57:16 +08:00
final IconData icon ;
final Function ( ) onTap ;
2022-09-04 11:03:16 +08:00
final bool isClose ;
2022-10-18 22:56:36 +08:00
final double iconSize ;
final double boxSize ;
2022-10-24 16:44:43 +08:00
2022-10-14 22:50:13 +08:00
const ActionIcon (
{ Key ? key ,
2022-10-20 22:22:02 +08:00
this . message ,
2022-10-14 22:50:13 +08:00
required this . icon ,
required this . onTap ,
2022-10-18 22:56:36 +08:00
this . isClose = false ,
this . iconSize = _kActionIconSize ,
this . boxSize = _kTabBarHeight - 1 } )
2022-10-14 22:50:13 +08:00
: super ( key: key ) ;
2022-08-20 19:57:16 +08:00
2022-10-24 16:44:43 +08:00
@ override
State < ActionIcon > createState ( ) = > _ActionIconState ( ) ;
}
class _ActionIconState extends State < ActionIcon > {
var hover = false . obs ;
@ override
void initState ( ) {
super . initState ( ) ;
hover . value = false ;
}
2022-08-20 19:57:16 +08:00
@ override
Widget build ( BuildContext context ) {
2022-10-24 16:44:43 +08:00
return Tooltip (
message: widget . message ! = null ? translate ( widget . message ! ) : " " ,
waitDuration: const Duration ( seconds: 1 ) ,
child: Obx (
( ) = > InkWell (
hoverColor: widget . isClose
? const Color . fromARGB ( 255 , 196 , 43 , 28 )
: MyTheme . tabbar ( context ) . hoverColor ,
onHover: ( value ) = > hover . value = value ,
onTap: widget . onTap ,
child: SizedBox (
height: widget . boxSize ,
width: widget . boxSize ,
child: Icon (
widget . icon ,
color: hover . value & & widget . isClose
? Colors . white
: MyTheme . tabbar ( context ) . unSelectedIconColor ,
size: widget . iconSize ,
2022-08-20 19:57:16 +08:00
) ,
) ,
2022-10-24 16:44:43 +08:00
) ,
) ,
) ;
2022-08-20 19:57:16 +08:00
}
}
2022-08-24 21:09:18 +08:00
class AddButton extends StatelessWidget {
2022-09-04 11:03:16 +08:00
const AddButton ( {
2022-08-24 21:09:18 +08:00
Key ? key ,
} ) : super ( key: key ) ;
@ override
Widget build ( BuildContext context ) {
return ActionIcon (
2023-03-11 19:14:47 +08:00
message: ' New Connection ' ,
2022-08-24 21:09:18 +08:00
icon: IconFont . add ,
2022-12-01 13:52:12 +08:00
onTap: ( ) = > rustDeskWinManager . call (
WindowType . Main , kWindowMainWindowOnTop , " " ) ,
2022-09-04 11:03:16 +08:00
isClose: false ) ;
2022-08-24 21:09:18 +08:00
}
}
2022-09-04 11:03:16 +08:00
class TabbarTheme extends ThemeExtension < TabbarTheme > {
final Color ? selectedTabIconColor ;
final Color ? unSelectedTabIconColor ;
final Color ? selectedTextColor ;
final Color ? unSelectedTextColor ;
final Color ? selectedIconColor ;
final Color ? unSelectedIconColor ;
final Color ? dividerColor ;
final Color ? hoverColor ;
2023-01-29 23:30:49 +08:00
final Color ? closeHoverColor ;
final Color ? selectedTabBackgroundColor ;
2022-09-04 11:03:16 +08:00
const TabbarTheme (
{ required this . selectedTabIconColor ,
required this . unSelectedTabIconColor ,
required this . selectedTextColor ,
required this . unSelectedTextColor ,
required this . selectedIconColor ,
required this . unSelectedIconColor ,
required this . dividerColor ,
2023-01-29 23:30:49 +08:00
required this . hoverColor ,
required this . closeHoverColor ,
required this . selectedTabBackgroundColor } ) ;
2022-09-04 11:03:16 +08:00
static const light = TabbarTheme (
selectedTabIconColor: MyTheme . accent ,
unSelectedTabIconColor: Color . fromARGB ( 255 , 162 , 203 , 241 ) ,
2023-01-29 23:30:49 +08:00
selectedTextColor: Colors . black ,
unSelectedTextColor: Color . fromARGB ( 255 , 112 , 112 , 112 ) ,
2022-09-04 11:03:16 +08:00
selectedIconColor: Color . fromARGB ( 255 , 26 , 26 , 26 ) ,
unSelectedIconColor: Color . fromARGB ( 255 , 96 , 96 , 96 ) ,
dividerColor: Color . fromARGB ( 255 , 238 , 238 , 238 ) ,
2023-07-09 14:26:46 +08:00
hoverColor: Colors . white54 ,
closeHoverColor: Colors . white ,
selectedTabBackgroundColor: Colors . white54 ) ;
2022-09-04 11:03:16 +08:00
static const dark = TabbarTheme (
selectedTabIconColor: MyTheme . accent ,
unSelectedTabIconColor: Color . fromARGB ( 255 , 30 , 65 , 98 ) ,
2023-07-09 14:26:46 +08:00
selectedTextColor: Colors . white ,
2023-01-29 23:30:49 +08:00
unSelectedTextColor: Color . fromARGB ( 255 , 192 , 192 , 192 ) ,
selectedIconColor: Color . fromARGB ( 255 , 192 , 192 , 192 ) ,
2022-09-04 11:03:16 +08:00
unSelectedIconColor: Color . fromARGB ( 255 , 255 , 255 , 255 ) ,
dividerColor: Color . fromARGB ( 255 , 64 , 64 , 64 ) ,
2023-01-29 23:30:49 +08:00
hoverColor: Colors . black26 ,
closeHoverColor: Colors . black ,
selectedTabBackgroundColor: Colors . black26 ) ;
2022-09-04 11:03:16 +08:00
@ override
ThemeExtension < TabbarTheme > copyWith ( {
Color ? selectedTabIconColor ,
Color ? unSelectedTabIconColor ,
Color ? selectedTextColor ,
Color ? unSelectedTextColor ,
Color ? selectedIconColor ,
Color ? unSelectedIconColor ,
Color ? dividerColor ,
Color ? hoverColor ,
2023-01-29 23:30:49 +08:00
Color ? closeHoverColor ,
Color ? selectedTabBackgroundColor ,
2022-09-04 11:03:16 +08:00
} ) {
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 ,
2023-01-29 23:30:49 +08:00
closeHoverColor: closeHoverColor ? ? this . closeHoverColor ,
2023-02-08 09:11:53 +08:00
selectedTabBackgroundColor:
selectedTabBackgroundColor ? ? this . selectedTabBackgroundColor ,
2022-09-04 11:03:16 +08:00
) ;
}
@ override
ThemeExtension < TabbarTheme > lerp (
ThemeExtension < TabbarTheme > ? 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 ) ,
2023-01-29 23:30:49 +08:00
closeHoverColor: Color . lerp ( closeHoverColor , other . closeHoverColor , t ) ,
2023-02-08 09:11:53 +08:00
selectedTabBackgroundColor: Color . lerp (
selectedTabBackgroundColor , other . selectedTabBackgroundColor , t ) ,
2022-09-04 11:03:16 +08:00
) ;
}
static color ( BuildContext context ) {
return Theme . of ( context ) . extension < ColorThemeExtension > ( ) ! ;
}
2022-08-09 22:35:29 +08:00
}