From 39a1545e94af91c39915e73cd33fc2d85af8122b Mon Sep 17 00:00:00 2001 From: 21pages Date: Thu, 1 Sep 2022 12:07:05 +0800 Subject: [PATCH 1/2] add close confirmation dialog Signed-off-by: 21pages --- flutter/lib/desktop/pages/server_page.dart | 38 +++++++------ .../lib/desktop/widgets/tabbar_widget.dart | 55 ++++++++++++++++--- 2 files changed, 67 insertions(+), 26 deletions(-) diff --git a/flutter/lib/desktop/pages/server_page.dart b/flutter/lib/desktop/pages/server_page.dart index e7922403b..b4573297a 100644 --- a/flutter/lib/desktop/pages/server_page.dart +++ b/flutter/lib/desktop/pages/server_page.dart @@ -48,22 +48,25 @@ class _DesktopServerPageState extends State ], child: Consumer( builder: (context, serverModel, child) => Container( - decoration: BoxDecoration( - border: - Border.all(color: MyTheme.color(context).border!)), - child: Scaffold( - backgroundColor: MyTheme.color(context).bg, - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Expanded(child: ConnectionManager()), - SizedBox.fromSize(size: Size(0, 15.0)), - ], - ), - ), - ), - ))); + decoration: BoxDecoration( + border: Border.all(color: MyTheme.color(context).border!)), + child: Scaffold( + backgroundColor: MyTheme.color(context).bg, + body: Overlay(initialEntries: [ + OverlayEntry(builder: (context) { + gFFI.dialogManager.setOverlayState(Overlay.of(context)); + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Expanded(child: ConnectionManager()), + SizedBox.fromSize(size: Size(0, 15.0)), + ], + ), + ); + }) + ]), + )))); } @override @@ -109,7 +112,8 @@ class ConnectionManagerState extends State { theme: isDarkTheme() ? TarBarTheme.dark() : TarBarTheme.light(), showTitle: false, showMaximize: false, - showMinimize: false, + showMinimize: true, + showClose: true, controller: serverModel.tabController, tabType: DesktopTabType.cm, pageViewBuilder: (pageView) => Row(children: [ diff --git a/flutter/lib/desktop/widgets/tabbar_widget.dart b/flutter/lib/desktop/widgets/tabbar_widget.dart index 38e724bad..755d6946c 100644 --- a/flutter/lib/desktop/widgets/tabbar_widget.dart +++ b/flutter/lib/desktop/widgets/tabbar_widget.dart @@ -314,6 +314,8 @@ class DesktopTab extends StatelessWidget { Offstage(offstage: tail == null, child: tail), WindowActionPanel( mainTab: isMainWindow, + tabType: tabType, + state: state, theme: theme, showMinimize: showMinimize, showMaximize: showMaximize, @@ -327,6 +329,8 @@ class DesktopTab extends StatelessWidget { class WindowActionPanel extends StatelessWidget { final bool mainTab; + final DesktopTabType tabType; + final Rx state; final TarBarTheme theme; final bool showMinimize; @@ -337,6 +341,8 @@ class WindowActionPanel extends StatelessWidget { const WindowActionPanel( {Key? key, required this.mainTab, + required this.tabType, + required this.state, required this.theme, this.showMinimize = true, this.showMaximize = true, @@ -411,22 +417,53 @@ class WindowActionPanel extends StatelessWidget { message: 'Close', icon: IconFont.close, theme: theme, - onTap: () { - if (mainTab) { - windowManager.close(); - } else { - // only hide for multi window, not close - Future.delayed(Duration.zero, () { - WindowController.fromWindowId(windowId!).hide(); - }); + onTap: () async { + action() { + if (mainTab) { + windowManager.close(); + } else { + // only hide for multi window, not close + Future.delayed(Duration.zero, () { + WindowController.fromWindowId(windowId!).hide(); + }); + } + onClose?.call(); + } + + if (tabType != DesktopTabType.main && + state.value.tabs.length > 1) { + closeConfirmDialog(action); + } else { + action(); } - onClose?.call(); }, is_close: true, )), ], ); } + + closeConfirmDialog(Function() callback) async { + final res = await gFFI.dialogManager + .show((setState, close) => CustomAlertDialog( + title: Row(children: [ + Icon(Icons.warning_amber_sharp, + color: Colors.redAccent, size: 28), + SizedBox(width: 10), + Text(translate("Warning")), + ]), + content: Text(translate("Disconnect all devices?")), + actions: [ + TextButton( + onPressed: () => close(), child: Text(translate("Cancel"))), + ElevatedButton( + onPressed: () => close(true), child: Text(translate("OK"))), + ], + )); + if (res == true) { + callback(); + } + } } // ignore: must_be_immutable From f6bc448cec584021a686cf16afca97a2db29df00 Mon Sep 17 00:00:00 2001 From: 21pages Date: Thu, 1 Sep 2022 21:18:53 +0800 Subject: [PATCH 2/2] adjust cm display behavior Signed-off-by: 21pages --- .../desktop/pages/connection_tab_page.dart | 5 ++- .../lib/desktop/pages/desktop_tab_page.dart | 3 +- .../desktop/pages/file_manager_tab_page.dart | 3 +- .../desktop/pages/port_forward_tab_page.dart | 5 ++- flutter/lib/desktop/pages/server_page.dart | 16 ++++++-- .../lib/desktop/widgets/tabbar_widget.dart | 38 +++++++++++++----- flutter/lib/main.dart | 1 + flutter/lib/mobile/pages/chat_page.dart | 1 + flutter/lib/models/chat_model.dart | 11 ++++- flutter/lib/models/server_model.dart | 40 ++++++++++--------- 10 files changed, 83 insertions(+), 40 deletions(-) diff --git a/flutter/lib/desktop/pages/connection_tab_page.dart b/flutter/lib/desktop/pages/connection_tab_page.dart index 5687c5c7e..d9bc86fe2 100644 --- a/flutter/lib/desktop/pages/connection_tab_page.dart +++ b/flutter/lib/desktop/pages/connection_tab_page.dart @@ -20,7 +20,8 @@ class ConnectionTabPage extends StatefulWidget { } class _ConnectionTabPageState extends State { - final tabController = Get.put(DesktopTabController()); + final tabController = + Get.put(DesktopTabController(tabType: DesktopTabType.remoteScreen)); static const IconData selectedIcon = Icons.desktop_windows_sharp; static const IconData unselectedIcon = Icons.desktop_windows_outlined; @@ -60,6 +61,7 @@ class _ConnectionTabPageState extends State { if (call.method == "new_remote_desktop") { final args = jsonDecode(call.arguments); final id = args['id']; + ConnectionTypeState.init(id); window_on_top(windowId()); ConnectionTypeState.init(id); tabController.add(TabInfo( @@ -94,7 +96,6 @@ class _ConnectionTabPageState extends State { body: Obx(() => DesktopTab( controller: tabController, theme: theme, - tabType: DesktopTabType.remoteScreen, showTabBar: fullscreen.isFalse, onClose: () { tabController.clear(); diff --git a/flutter/lib/desktop/pages/desktop_tab_page.dart b/flutter/lib/desktop/pages/desktop_tab_page.dart index 57ee43e14..874a71dcf 100644 --- a/flutter/lib/desktop/pages/desktop_tab_page.dart +++ b/flutter/lib/desktop/pages/desktop_tab_page.dart @@ -15,7 +15,7 @@ class DesktopTabPage extends StatefulWidget { } class _DesktopTabPageState extends State { - final tabController = DesktopTabController(); + final tabController = DesktopTabController(tabType: DesktopTabType.main); @override void initState() { @@ -46,7 +46,6 @@ class _DesktopTabPageState extends State { body: DesktopTab( controller: tabController, theme: dark ? TarBarTheme.dark() : TarBarTheme.light(), - tabType: DesktopTabType.main, tail: ActionIcon( message: 'Settings', icon: IconFont.menu, diff --git a/flutter/lib/desktop/pages/file_manager_tab_page.dart b/flutter/lib/desktop/pages/file_manager_tab_page.dart index 6c8b58a30..add5eed9f 100644 --- a/flutter/lib/desktop/pages/file_manager_tab_page.dart +++ b/flutter/lib/desktop/pages/file_manager_tab_page.dart @@ -25,7 +25,7 @@ class _FileManagerTabPageState extends State { static final IconData unselectedIcon = Icons.file_copy_outlined; _FileManagerTabPageState(Map params) { - Get.put(DesktopTabController()); + Get.put(DesktopTabController(tabType: DesktopTabType.fileTransfer)); tabController.add(TabInfo( key: params['id'], label: params['id'], @@ -74,7 +74,6 @@ class _FileManagerTabPageState extends State { body: DesktopTab( controller: tabController, theme: theme, - tabType: DesktopTabType.fileTransfer, onClose: () { tabController.clear(); }, diff --git a/flutter/lib/desktop/pages/port_forward_tab_page.dart b/flutter/lib/desktop/pages/port_forward_tab_page.dart index 1e2c8e2bc..2340a4ca1 100644 --- a/flutter/lib/desktop/pages/port_forward_tab_page.dart +++ b/flutter/lib/desktop/pages/port_forward_tab_page.dart @@ -18,7 +18,7 @@ class PortForwardTabPage extends StatefulWidget { } class _PortForwardTabPageState extends State { - final tabController = Get.put(DesktopTabController()); + late final DesktopTabController tabController; late final bool isRDP; static const IconData selectedIcon = Icons.forward_sharp; @@ -26,6 +26,8 @@ class _PortForwardTabPageState extends State { _PortForwardTabPageState(Map params) { isRDP = params['isRDP']; + tabController = Get.put(DesktopTabController( + tabType: isRDP ? DesktopTabType.rdp : DesktopTabType.portForward)); tabController.add(TabInfo( key: params['id'], label: params['id'], @@ -78,7 +80,6 @@ class _PortForwardTabPageState extends State { body: DesktopTab( controller: tabController, theme: theme, - tabType: isRDP ? DesktopTabType.rdp : DesktopTabType.portForward, onClose: () { tabController.clear(); }, diff --git a/flutter/lib/desktop/pages/server_page.dart b/flutter/lib/desktop/pages/server_page.dart index b4573297a..f64adfca2 100644 --- a/flutter/lib/desktop/pages/server_page.dart +++ b/flutter/lib/desktop/pages/server_page.dart @@ -19,10 +19,12 @@ class DesktopServerPage extends StatefulWidget { class _DesktopServerPageState extends State with WindowListener, AutomaticKeepAliveClientMixin { + final tabController = gFFI.serverModel.tabController; @override void initState() { gFFI.ffiModel.updateEventListener(""); windowManager.addListener(this); + tabController.onRemove = (_, id) => onRemoveId(id); super.initState(); } @@ -39,6 +41,13 @@ class _DesktopServerPageState extends State super.onWindowClose(); } + void onRemoveId(String id) { + if (tabController.state.value.tabs.isEmpty) { + windowManager.close(); + } + } + + @override Widget build(BuildContext context) { super.build(context); return MultiProvider( @@ -115,7 +124,6 @@ class ConnectionManagerState extends State { showMinimize: true, showClose: true, controller: serverModel.tabController, - tabType: DesktopTabType.cm, pageViewBuilder: (pageView) => Row(children: [ Expanded(child: pageView), Consumer( @@ -454,8 +462,10 @@ class _CmControlPanel extends StatelessWidget { decoration: BoxDecoration( color: MyTheme.accent, borderRadius: BorderRadius.circular(10)), child: InkWell( - onTap: () => - checkClickTime(client.id, () => handleAccept(context)), + onTap: () => checkClickTime(client.id, () { + handleAccept(context); + windowManager.minimize(); + }), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ diff --git a/flutter/lib/desktop/widgets/tabbar_widget.dart b/flutter/lib/desktop/widgets/tabbar_widget.dart index 755d6946c..1a19dd833 100644 --- a/flutter/lib/desktop/widgets/tabbar_widget.dart +++ b/flutter/lib/desktop/widgets/tabbar_widget.dart @@ -59,13 +59,15 @@ class DesktopTabState { class DesktopTabController { final state = DesktopTabState().obs; + final DesktopTabType tabType; /// index, key Function(int, String)? onRemove; - Function(int)? onSelected; - void add(TabInfo tab) { + DesktopTabController({required this.tabType}); + + void add(TabInfo tab, {bool authorized = false}) { if (!isDesktop) return; final index = state.value.tabs.indexWhere((e) => e.key == tab.key); int toIndex; @@ -79,6 +81,16 @@ class DesktopTabController { toIndex = state.value.tabs.length - 1; assert(toIndex >= 0); } + if (tabType == DesktopTabType.cm) { + Future.delayed(Duration.zero, () async { + window_on_top(null); + }); + if (authorized) { + Future.delayed(const Duration(seconds: 3), () { + windowManager.minimize(); + }); + } + } try { jumpTo(toIndex); } catch (e) { @@ -106,6 +118,7 @@ class DesktopTabController { } void jumpTo(int index) { + if (!isDesktop || index < 0) return; state.update((val) { val!.selected = index; Future.delayed(Duration.zero, (() { @@ -114,12 +127,14 @@ class DesktopTabController { } if (val.scrollController.hasClients && val.scrollController.canScroll && - val.scrollController.itemCount >= index) { + val.scrollController.itemCount > index) { val.scrollController.scrollToItem(index, center: true, animate: true); } })); }); - onSelected?.call(index); + if (state.value.tabs.length > index) { + onSelected?.call(index); + } } void closeBy(String? key) { @@ -154,8 +169,6 @@ typedef LabelGetter = Rx Function(String key); class DesktopTab extends StatelessWidget { final Function(String)? onTabClose; final TarBarTheme theme; - final DesktopTabType tabType; - final bool isMainWindow; final bool showTabBar; final bool showLogo; final bool showTitle; @@ -170,10 +183,12 @@ class DesktopTab extends StatelessWidget { final DesktopTabController controller; Rx get state => controller.state; + late final DesktopTabType tabType; + late final bool isMainWindow; - const DesktopTab({ + DesktopTab({ + Key? key, required this.controller, - required this.tabType, this.theme = const TarBarTheme.light(), this.onTabClose, this.showTabBar = true, @@ -187,8 +202,11 @@ class DesktopTab extends StatelessWidget { this.onClose, this.tabBuilder, this.labelGetter, - }) : isMainWindow = - tabType == DesktopTabType.main || tabType == DesktopTabType.cm; + }) : super(key: key) { + tabType = controller.tabType; + isMainWindow = + tabType == DesktopTabType.main || tabType == DesktopTabType.cm; + } @override Widget build(BuildContext context) { diff --git a/flutter/lib/main.dart b/flutter/lib/main.dart index e1c254942..2f1d0680f 100644 --- a/flutter/lib/main.dart +++ b/flutter/lib/main.dart @@ -165,6 +165,7 @@ void runConnectionManagerScreen() async { await windowManager.setAlignment(Alignment.topRight); await windowManager.show(); await windowManager.focus(); + await windowManager.setAlignment(Alignment.topRight); // ensure }) ]); runApp(GetMaterialApp( diff --git a/flutter/lib/mobile/pages/chat_page.dart b/flutter/lib/mobile/pages/chat_page.dart index 738f34e89..b265f6995 100644 --- a/flutter/lib/mobile/pages/chat_page.dart +++ b/flutter/lib/mobile/pages/chat_page.dart @@ -59,6 +59,7 @@ class ChatPage extends StatelessWidget implements PageShape { messages: chatModel .messages[chatModel.currentID]?.chatMessages ?? [], + inputOptions: const InputOptions(sendOnEnter: true), messageOptions: MessageOptions( showOtherUsersAvatar: false, showTime: true, diff --git a/flutter/lib/models/chat_model.dart b/flutter/lib/models/chat_model.dart index de949c782..a9c791ef7 100644 --- a/flutter/lib/models/chat_model.dart +++ b/flutter/lib/models/chat_model.dart @@ -209,10 +209,19 @@ class ChatModel with ChangeNotifier { id: await bind.mainGetLastRemoteId(), ); } else { - final client = _ffi.target?.serverModel.clients[id]; + final client = _ffi.target?.serverModel.clients + .firstWhere((client) => client.id == id); if (client == null) { return debugPrint("Failed to receive msg,user doesn't exist"); } + if (isDesktop) { + window_on_top(null); + var index = _ffi.target?.serverModel.clients + .indexWhere((client) => client.id == id); + if (index != null && index >= 0) { + gFFI.serverModel.tabController.jumpTo(index); + } + } chatUser = ChatUser(id: client.peerId, firstName: client.name); } diff --git a/flutter/lib/models/server_model.dart b/flutter/lib/models/server_model.dart index fa7f15e54..31c579f83 100644 --- a/flutter/lib/models/server_model.dart +++ b/flutter/lib/models/server_model.dart @@ -32,7 +32,7 @@ class ServerModel with ChangeNotifier { late final TextEditingController _serverId; final _serverPasswd = TextEditingController(text: ""); - final tabController = DesktopTabController(); + final tabController = DesktopTabController(tabType: DesktopTabType.cm); List _clients = []; @@ -347,20 +347,18 @@ class ServerModel with ChangeNotifier { var res = await bind.mainGetClientsState(); try { final List clientsJson = jsonDecode(res); - if (isDesktop && clientsJson.isEmpty && _clients.isNotEmpty) { - // exit cm when >1 peers to no peers - exit(0); - } _clients.clear(); tabController.state.value.tabs.clear(); for (var clientJson in clientsJson) { final client = Client.fromJson(clientJson); _clients.add(client); - tabController.add(TabInfo( - key: client.id.toString(), - label: client.name, - closable: false, - page: Desktop.buildConnectionCard(client))); + tabController.add( + TabInfo( + key: client.id.toString(), + label: client.name, + closable: false, + page: Desktop.buildConnectionCard(client)), + authorized: client.authorized); } notifyListeners(); } catch (e) { @@ -471,14 +469,18 @@ class ServerModel with ChangeNotifier { } else { _clients[index].authorized = true; } - tabController.add(TabInfo( - key: client.id.toString(), - label: client.name, - closable: false, - page: Desktop.buildConnectionCard(client))); + tabController.add( + TabInfo( + key: client.id.toString(), + label: client.name, + closable: false, + page: Desktop.buildConnectionCard(client)), + authorized: true); scrollToBottom(); notifyListeners(); - } catch (e) {} + } catch (e) { + debugPrint("onClientAuthorized:$e"); + } } void onClientRemove(Map evt) { @@ -486,8 +488,10 @@ class ServerModel with ChangeNotifier { final id = int.parse(evt['id'] as String); if (_clients.any((c) => c.id == id)) { final index = _clients.indexWhere((client) => client.id == id); - _clients.removeAt(index); - tabController.remove(index); + if (index >= 0) { + _clients.removeAt(index); + tabController.remove(index); + } parent.target?.dialogManager.dismissByTag(getLoginDialogTag(id)); parent.target?.invokeMethod("cancel_notification", id); }