diff --git a/flutter/lib/desktop/pages/server_page.dart b/flutter/lib/desktop/pages/server_page.dart index c78552143..32130ad2e 100644 --- a/flutter/lib/desktop/pages/server_page.dart +++ b/flutter/lib/desktop/pages/server_page.dart @@ -2,9 +2,9 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; - // import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:provider/provider.dart'; +import 'package:window_manager/window_manager.dart'; import '../../common.dart'; import '../../mobile/pages/home_page.dart'; @@ -143,8 +143,15 @@ class ConnectionManager extends StatelessWidget { // serverModel.clients[0] = Client( // false, false, "Readmi-M21sdfsdf", "123123123", true, false, false); return serverModel.clients.isEmpty - ? Center( - child: Text(translate("Waiting")), + ? Column( + children: [ + buildTitleBar(Offstage()), + Expanded( + child: Center( + child: Text(translate("Waiting")), + ), + ), + ], ) : DefaultTabController( length: serverModel.clients.length, @@ -153,18 +160,37 @@ class ConnectionManager extends StatelessWidget { children: [ SizedBox( height: kTextTabBarHeight, - child: TabBar( - isScrollable: true, - tabs: serverModel.clients.entries - .map((entry) => buildTab(entry)) - .toList(growable: false)), + child: buildTitleBar(TabBar( + isScrollable: true, + tabs: serverModel.clients.entries + .map((entry) => buildTab(entry)) + .toList(growable: false))), + ), + Expanded( + child: TabBarView( + children: serverModel.clients.entries + .map((entry) => buildConnectionCard(entry)) + .toList(growable: false)), + ) + ], + ), + ); + } + + Widget buildTitleBar(Widget middle) { + return GestureDetector( + onPanDown: (d) { + windowManager.startDragging(); + }, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + _AppIcon(), + Expanded(child: middle), + const SizedBox( + width: 4.0, ), - Expanded( - child: TabBarView( - children: serverModel.clients.entries - .map((entry) => buildConnectionCard(entry)) - .toList(growable: false)), - ) + _CloseButton() ], ), ); @@ -204,6 +230,40 @@ class ConnectionManager extends StatelessWidget { } } +class _AppIcon extends StatelessWidget { + const _AppIcon({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + margin: EdgeInsets.symmetric(horizontal: 4.0), + child: Image.asset( + 'assets/logo.ico', + width: 30, + height: 30, + ), + ); + } +} + +class _CloseButton extends StatelessWidget { + const _CloseButton({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Ink( + child: InkWell( + onTap: () { + windowManager.close(); + }, + child: Icon( + Icons.close, + size: 30, + )), + ); + } +} + class _CmHeader extends StatefulWidget { final Client client; diff --git a/flutter/lib/desktop/widgets/tabbar_widget.dart b/flutter/lib/desktop/widgets/tabbar_widget.dart index b7a96271f..32504f6a8 100644 --- a/flutter/lib/desktop/widgets/tabbar_widget.dart +++ b/flutter/lib/desktop/widgets/tabbar_widget.dart @@ -1,10 +1,13 @@ import 'dart:math'; +import 'package:desktop_multi_window/desktop_multi_window.dart'; 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'; const double _kTabBarHeight = kDesktopRemoteTabBarHeight; const double _kIconSize = 18; @@ -76,42 +79,49 @@ class DesktopTabBar extends StatelessWidget { Text("RustDesk").paddingOnly(left: 5), ]).paddingSymmetric(horizontal: 12, vertical: 5), ), - Flexible( - child: Obx(() => TabBar( - key: tabBarKey, - indicatorColor: _theme.indicatorColor, - labelPadding: const EdgeInsets.symmetric( - vertical: 0, horizontal: 0), - isScrollable: true, - indicatorPadding: EdgeInsets.zero, - physics: BouncingScrollPhysics(), - controller: controller.value, - tabs: tabs.asMap().entries.map((e) { - int index = e.key; - String label = e.value.label; + Expanded( + child: GestureDetector( + onPanStart: (_) { + if (mainTab) { + windowManager.startDragging(); + } + }, + child: Obx(() => TabBar( + key: tabBarKey, + indicatorColor: _theme.indicatorColor, + labelPadding: const EdgeInsets.symmetric( + vertical: 0, horizontal: 0), + isScrollable: true, + indicatorPadding: EdgeInsets.zero, + physics: BouncingScrollPhysics(), + controller: controller.value, + tabs: tabs.asMap().entries.map((e) { + int index = e.key; + String label = e.value.label; - return _Tab( - index: index, - label: label, - icon: e.value.icon, - closable: e.value.closable, - selected: selected.value, - onClose: () { - onTabClose(label); - if (index <= selected.value) { - selected.value = max(0, selected.value - 1); - } - controller.value.animateTo(selected.value, - duration: Duration.zero); - }, - onSelected: () { - selected.value = index; - controller.value - .animateTo(index, duration: Duration.zero); - }, - theme: _theme, - ); - }).toList())), + return _Tab( + index: index, + label: label, + icon: e.value.icon, + closable: e.value.closable, + selected: selected.value, + onClose: () { + onTabClose(label); + if (index <= selected.value) { + selected.value = max(0, selected.value - 1); + } + controller.value.animateTo(selected.value, + duration: Duration.zero); + }, + onSelected: () { + selected.value = index; + controller.value + .animateTo(index, duration: Duration.zero); + }, + theme: _theme, + ); + }).toList())), + ), ), Offstage( offstage: mainTab, @@ -134,6 +144,10 @@ class DesktopTabBar extends StatelessWidget { onTap: () => onAddSetting?.call(), ).paddingOnly(right: 10), ), + ), + WindowActionPanel( + mainTab: mainTab, + color: _theme.unSelectedIconColor, ) ], ), @@ -169,6 +183,79 @@ class DesktopTabBar extends StatelessWidget { } } +class WindowActionPanel extends StatelessWidget { + final bool mainTab; + final Color color; + + const WindowActionPanel( + {Key? key, required this.mainTab, required this.color}) + : super(key: key); + + @override + Widget build(BuildContext context) { + return Row( + children: [ + Tooltip( + message: translate("Minimize"), + child: InkWell( + child: Icon( + Icons.minimize, + color: color, + ), + onTap: () { + if (mainTab) { + windowManager.minimize(); + } else { + // TODO + // WindowController.fromWindowId(windowId!).close(); + } + }, + ).paddingOnly(right: 10), + ), + Tooltip( + message: translate("Maximize"), + child: InkWell( + child: Icon( + Icons.rectangle_outlined, + color: color, + ), + onTap: () { + if (mainTab) { + windowManager.isMaximized().then((maximized) { + if (maximized) { + windowManager.unmaximize(); + } else { + windowManager.maximize(); + } + }); + } else { + // TODO + // WindowController.fromWindowId(windowId!).(); + } + }, + ).paddingOnly(right: 10), + ), + Tooltip( + message: translate("Close"), + child: InkWell( + child: Icon( + Icons.close, + color: color, + ), + onTap: () { + if (mainTab) { + windowManager.close(); + } else { + WindowController.fromWindowId(windowId!).close(); + } + }, + ).paddingOnly(right: 10), + ) + ], + ); + } +} + class _Tab extends StatelessWidget { late final int index; late final String label; diff --git a/flutter/lib/main.dart b/flutter/lib/main.dart index ef4ec81c8..d41e89116 100644 --- a/flutter/lib/main.dart +++ b/flutter/lib/main.dart @@ -77,7 +77,14 @@ Future initEnv(String appType) async { } void runMainApp(bool startService) async { - await initEnv(kAppTypeMain); + WindowOptions windowOptions = getHiddenTitleBarWindowOptions(Size(1280, 720)); + await Future.wait([ + initEnv(kAppTypeMain), + windowManager.waitUntilReadyToShow(windowOptions, () async { + await windowManager.show(); + await windowManager.focus(); + }) + ]); if (startService) { // await windowManager.ensureInitialized(); // disable tray @@ -118,13 +125,7 @@ void runFileTransferScreen(Map argument) async { void runConnectionManagerScreen() async { // initialize window - WindowOptions windowOptions = WindowOptions( - size: Size(300, 400), - center: true, - backgroundColor: Colors.transparent, - skipTaskbar: false, - titleBarStyle: TitleBarStyle.normal, - ); + WindowOptions windowOptions = getHiddenTitleBarWindowOptions(Size(300, 400)); await Future.wait([ initEnv(kAppTypeConnectionManager), windowManager.waitUntilReadyToShow(windowOptions, () async { @@ -134,7 +135,20 @@ void runConnectionManagerScreen() async { }) ]); ; - runApp(GetMaterialApp(theme: getCurrentTheme(), home: DesktopServerPage())); + runApp(GetMaterialApp( + debugShowCheckedModeBanner: false, + theme: getCurrentTheme(), + home: DesktopServerPage())); +} + +WindowOptions getHiddenTitleBarWindowOptions(Size size) { + return WindowOptions( + size: size, + center: true, + backgroundColor: Colors.transparent, + skipTaskbar: false, + titleBarStyle: TitleBarStyle.hidden, + ); } class App extends StatelessWidget { diff --git a/flutter/pubspec.yaml b/flutter/pubspec.yaml index caa12313d..d5856167a 100644 --- a/flutter/pubspec.yaml +++ b/flutter/pubspec.yaml @@ -67,7 +67,10 @@ dependencies: url: https://github.com/Kingtous/rustdesk_desktop_multi_window ref: 2b1176d53f195cc55e8d37151bb3d9f6bd52fad3 freezed_annotation: ^2.0.3 - tray_manager: 0.1.7 + tray_manager: + git: + url: https://github.com/Kingtous/rustdesk_tray_manager + ref: 3aa37c86e47ea748e7b5507cbe59f2c54ebdb23a get: ^4.6.5 visibility_detector: ^0.3.3 contextmenu: ^3.0.0