From b8f7e85c0bd1f9f9d05ee9c9366acd2c7defd497 Mon Sep 17 00:00:00 2001 From: Kingtous Date: Thu, 18 Aug 2022 11:07:53 +0800 Subject: [PATCH 1/2] feat: main window custom bar & drag Signed-off-by: Kingtous --- flutter/lib/desktop/pages/server_page.dart | 88 ++++++++-- .../lib/desktop/widgets/tabbar_widget.dart | 157 ++++++++++++++---- flutter/lib/main.dart | 32 +++- flutter/pubspec.yaml | 5 +- 4 files changed, 223 insertions(+), 59 deletions(-) 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 From 3cc67bf581c098cc0822c19f1c9105c254097755 Mon Sep 17 00:00:00 2001 From: Kingtous Date: Thu, 18 Aug 2022 17:25:47 +0800 Subject: [PATCH 2/2] feat: sub window custom title bar & functions Signed-off-by: Kingtous --- .../lib/desktop/widgets/tabbar_widget.dart | 29 ++++++++++++------- flutter/lib/main.dart | 3 +- flutter/pubspec.lock | 4 +-- flutter/pubspec.yaml | 3 +- 4 files changed, 25 insertions(+), 14 deletions(-) diff --git a/flutter/lib/desktop/widgets/tabbar_widget.dart b/flutter/lib/desktop/widgets/tabbar_widget.dart index 32504f6a8..4a2581705 100644 --- a/flutter/lib/desktop/widgets/tabbar_widget.dart +++ b/flutter/lib/desktop/widgets/tabbar_widget.dart @@ -84,6 +84,9 @@ class DesktopTabBar extends StatelessWidget { onPanStart: (_) { if (mainTab) { windowManager.startDragging(); + } else { + WindowController.fromWindowId(windowId!) + .startDragging(); } }, child: Obx(() => TabBar( @@ -201,16 +204,15 @@ class WindowActionPanel extends StatelessWidget { child: Icon( Icons.minimize, color: color, - ), + ).paddingSymmetric(horizontal: 5), onTap: () { if (mainTab) { windowManager.minimize(); } else { - // TODO - // WindowController.fromWindowId(windowId!).close(); + WindowController.fromWindowId(windowId!).minimize(); } }, - ).paddingOnly(right: 10), + ), ), Tooltip( message: translate("Maximize"), @@ -218,7 +220,8 @@ class WindowActionPanel extends StatelessWidget { child: Icon( Icons.rectangle_outlined, color: color, - ), + size: 20, + ).paddingSymmetric(horizontal: 5), onTap: () { if (mainTab) { windowManager.isMaximized().then((maximized) { @@ -229,11 +232,17 @@ class WindowActionPanel extends StatelessWidget { } }); } else { - // TODO - // WindowController.fromWindowId(windowId!).(); + final wc = WindowController.fromWindowId(windowId!); + wc.isMaximized().then((maximized) { + if (maximized) { + wc.unmaximize(); + } else { + wc.maximize(); + } + }); } }, - ).paddingOnly(right: 10), + ), ), Tooltip( message: translate("Close"), @@ -241,7 +250,7 @@ class WindowActionPanel extends StatelessWidget { child: Icon( Icons.close, color: color, - ), + ).paddingSymmetric(horizontal: 5), onTap: () { if (mainTab) { windowManager.close(); @@ -249,7 +258,7 @@ class WindowActionPanel extends StatelessWidget { WindowController.fromWindowId(windowId!).close(); } }, - ).paddingOnly(right: 10), + ), ) ], ); diff --git a/flutter/lib/main.dart b/flutter/lib/main.dart index d41e89116..960bfb667 100644 --- a/flutter/lib/main.dart +++ b/flutter/lib/main.dart @@ -1,5 +1,6 @@ import 'dart:convert'; +import 'package:desktop_multi_window/desktop_multi_window.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hbb/desktop/pages/desktop_tab_page.dart'; import 'package:flutter_hbb/desktop/pages/server_page.dart'; @@ -32,6 +33,7 @@ Future main(List args) async { // main window if (args.isNotEmpty && args.first == 'multi_window') { windowId = int.parse(args[1]); + WindowController.fromWindowId(windowId!).showTitleBar(false); final argument = args[2].isEmpty ? Map() : jsonDecode(args[2]) as Map; @@ -134,7 +136,6 @@ void runConnectionManagerScreen() async { await windowManager.focus(); }) ]); - ; runApp(GetMaterialApp( debugShowCheckedModeBanner: false, theme: getCurrentTheme(), diff --git a/flutter/pubspec.lock b/flutter/pubspec.lock index f16f9516b..679322df3 100644 --- a/flutter/pubspec.lock +++ b/flutter/pubspec.lock @@ -250,8 +250,8 @@ packages: dependency: "direct main" description: path: "." - ref: "2b1176d53f195cc55e8d37151bb3d9f6bd52fad3" - resolved-ref: "2b1176d53f195cc55e8d37151bb3d9f6bd52fad3" + ref: bf670217de03f4866177a9793284f4db99271c51 + resolved-ref: bf670217de03f4866177a9793284f4db99271c51 url: "https://github.com/Kingtous/rustdesk_desktop_multi_window" source: git version: "0.1.0" diff --git a/flutter/pubspec.yaml b/flutter/pubspec.yaml index d5856167a..f25d5e341 100644 --- a/flutter/pubspec.yaml +++ b/flutter/pubspec.yaml @@ -63,9 +63,10 @@ dependencies: url: https://github.com/Kingtous/rustdesk_window_manager ref: 028a7f6 desktop_multi_window: + # path: ../../rustdesk_desktop_multi_window git: url: https://github.com/Kingtous/rustdesk_desktop_multi_window - ref: 2b1176d53f195cc55e8d37151bb3d9f6bd52fad3 + ref: bf670217de03f4866177a9793284f4db99271c51 freezed_annotation: ^2.0.3 tray_manager: git: