diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index 59e3bf891..9c3a5724f 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -14,7 +14,7 @@ import 'package:get/get.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:window_manager/window_manager.dart'; -import 'mobile/widgets/overlay.dart'; +import 'common/widgets/overlay.dart'; import 'models/model.dart'; import 'models/platform_model.dart'; diff --git a/flutter/lib/mobile/widgets/overlay.dart b/flutter/lib/common/widgets/overlay.dart similarity index 71% rename from flutter/lib/mobile/widgets/overlay.dart rename to flutter/lib/common/widgets/overlay.dart index b8fd8f653..0e0a6ce2d 100644 --- a/flutter/lib/mobile/widgets/overlay.dart +++ b/flutter/lib/common/widgets/overlay.dart @@ -1,15 +1,18 @@ import 'package:flutter/material.dart'; import 'package:flutter_hbb/common.dart'; +import '../../desktop/widgets/tabbar_widget.dart'; +import '../../mobile/pages/chat_page.dart'; import '../../models/chat_model.dart'; -import '../pages/chat_page.dart'; class DraggableChatWindow extends StatelessWidget { - DraggableChatWindow( - {this.position = Offset.zero, + const DraggableChatWindow( + {Key? key, + this.position = Offset.zero, required this.width, required this.height, - required this.chatModel}); + required this.chatModel}) + : super(key: key); final Offset position; final double width; @@ -30,46 +33,84 @@ class DraggableChatWindow extends StatelessWidget { resizeToAvoidBottomInset: false, appBar: CustomAppBar( onPanUpdate: onPanUpdate, - appBar: Container( - color: MyTheme.accent50, - height: 50, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Padding( - padding: EdgeInsets.symmetric(horizontal: 15), - child: Text( - translate("Chat"), - style: TextStyle( - color: Colors.white, - fontFamily: 'WorkSans', - fontWeight: FontWeight.bold, - fontSize: 20), - )), - Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - IconButton( - onPressed: () { - chatModel.hideChatWindowOverlay(); - }, - icon: Icon(Icons.keyboard_arrow_down)), - IconButton( - onPressed: () { - chatModel.hideChatWindowOverlay(); - chatModel.hideChatIconOverlay(); - }, - icon: Icon(Icons.close)) - ], - ) - ], - ), - ), + appBar: isDesktop + ? _buildDesktopAppBar() + : _buildMobileAppBar(), ), body: ChatPage(chatModel: chatModel), ); }); } + + Widget _buildMobileAppBar() { + return Container( + color: MyTheme.accent50, + height: 50, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 15), + child: Text( + translate("Chat"), + style: const TextStyle( + color: Colors.white, + fontFamily: 'WorkSans', + fontWeight: FontWeight.bold, + fontSize: 20), + )), + Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + IconButton( + onPressed: () { + chatModel.hideChatWindowOverlay(); + }, + icon: const Icon(Icons.keyboard_arrow_down)), + IconButton( + onPressed: () { + chatModel.hideChatWindowOverlay(); + chatModel.hideChatIconOverlay(); + }, + icon: const Icon(Icons.close)) + ], + ) + ], + ), + ); + } + + Widget _buildDesktopAppBar() { + return Container( + color: MyTheme.accent50, + height: 35, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 15), + child: Text( + translate("Chat"), + style: const TextStyle( + color: Colors.white, + fontFamily: 'WorkSans', + fontWeight: FontWeight.bold), + )), + Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + ActionIcon( + message: 'Close', + icon: IconFont.close, + onTap: chatModel.hideChatWindowOverlay, + isClose: true, + ) + ], + ) + ], + ), + ); + } } class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { @@ -86,7 +127,7 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { } @override - Size get preferredSize => new Size.fromHeight(kToolbarHeight); + Size get preferredSize => const Size.fromHeight(kToolbarHeight); } /// floating buttons of back/home/recent actions for android @@ -161,13 +202,15 @@ class DraggableMobileActions extends StatelessWidget { } class Draggable extends StatefulWidget { - Draggable( - {this.checkKeyboard = false, + const Draggable( + {Key? key, + this.checkKeyboard = false, this.checkScreenSize = false, this.position = Offset.zero, required this.width, required this.height, - required this.builder}); + required this.builder}) + : super(key: key); final bool checkKeyboard; final bool checkScreenSize; @@ -224,7 +267,6 @@ class _DraggableState extends State { final bottomHeight = MediaQuery.of(context).viewInsets.bottom; final currentVisible = bottomHeight != 0; - debugPrint(bottomHeight.toString() + currentVisible.toString()); // save if (!_keyboardVisible && currentVisible) { _saveHeight = _position.dy; diff --git a/flutter/lib/desktop/pages/file_manager_tab_page.dart b/flutter/lib/desktop/pages/file_manager_tab_page.dart index 42c50b927..de874b42d 100644 --- a/flutter/lib/desktop/pages/file_manager_tab_page.dart +++ b/flutter/lib/desktop/pages/file_manager_tab_page.dart @@ -75,9 +75,7 @@ class _FileManagerTabPageState extends State { backgroundColor: MyTheme.color(context).bg, body: DesktopTab( controller: tabController, - onWindowCloseButton: () { - tabController.clear(); - }, + onWindowCloseButton: handleWindowCloseButton, tail: const AddButton().paddingOnly(left: 10), )), ), @@ -103,4 +101,21 @@ class _FileManagerTabPageState extends State { tabController.closeBy(peerId); } } + + Future handleWindowCloseButton() async { + final connLength = tabController.state.value.tabs.length; + if (connLength < 1) { + return true; + } else if (connLength == 1) { + final currentConn = tabController.state.value.tabs[0]; + handleTabCloseButton(currentConn.key); + return false; + } else { + final res = await closeConfirmDialog(); + if (res) { + tabController.clear(); + } + return res; + } + } } diff --git a/flutter/lib/desktop/pages/port_forward_tab_page.dart b/flutter/lib/desktop/pages/port_forward_tab_page.dart index d078af458..e4aac06fe 100644 --- a/flutter/lib/desktop/pages/port_forward_tab_page.dart +++ b/flutter/lib/desktop/pages/port_forward_tab_page.dart @@ -83,8 +83,9 @@ class _PortForwardTabPageState extends State { backgroundColor: MyTheme.color(context).bg, body: DesktopTab( controller: tabController, - onWindowCloseButton: () { + onWindowCloseButton: () async { tabController.clear(); + return true; }, tail: AddButton().paddingOnly(left: 10), )), diff --git a/flutter/lib/desktop/pages/remote_tab_page.dart b/flutter/lib/desktop/pages/remote_tab_page.dart index e5caccd71..2f87c51fb 100644 --- a/flutter/lib/desktop/pages/remote_tab_page.dart +++ b/flutter/lib/desktop/pages/remote_tab_page.dart @@ -97,9 +97,7 @@ class _ConnectionTabPageState extends State { body: DesktopTab( controller: tabController, showTabBar: fullscreen.isFalse, - onWindowCloseButton: () { - tabController.clear(); - }, + onWindowCloseButton: handleWindowCloseButton, tail: AddButton().paddingOnly(left: 10), pageViewBuilder: (pageView) { WindowController.fromWindowId(windowId()) @@ -167,4 +165,21 @@ class _ConnectionTabPageState extends State { tabController.closeBy(peerId); } } + + Future handleWindowCloseButton() async { + final connLength = tabController.state.value.tabs.length; + if (connLength < 1) { + return true; + } else if (connLength == 1) { + final currentConn = tabController.state.value.tabs[0]; + handleTabCloseButton(currentConn.key); + return false; + } else { + final res = await closeConfirmDialog(); + if (res) { + tabController.clear(); + } + return res; + } + } } diff --git a/flutter/lib/desktop/widgets/tabbar_widget.dart b/flutter/lib/desktop/widgets/tabbar_widget.dart index fb7989108..dc9bdccb8 100644 --- a/flutter/lib/desktop/widgets/tabbar_widget.dart +++ b/flutter/lib/desktop/widgets/tabbar_widget.dart @@ -182,7 +182,7 @@ class DesktopTab extends StatelessWidget { final bool showClose; final Widget Function(Widget pageView)? pageViewBuilder; final Widget? tail; - final VoidCallback? onWindowCloseButton; + final Future Function()? onWindowCloseButton; final TabBuilder? tabBuilder; final LabelGetter? labelGetter; @@ -355,7 +355,7 @@ class WindowActionPanel extends StatelessWidget { final bool showMinimize; final bool showMaximize; final bool showClose; - final VoidCallback? onClose; + final Future Function()? onClose; const WindowActionPanel( {Key? key, @@ -433,7 +433,8 @@ class WindowActionPanel extends StatelessWidget { message: 'Close', icon: IconFont.close, onTap: () async { - action() { + final res = await onClose?.call() ?? true; + if (res) { if (mainTab) { windowManager.close(); } else { @@ -442,14 +443,6 @@ class WindowActionPanel extends StatelessWidget { WindowController.fromWindowId(windowId!).hide(); }); } - onClose?.call(); - } - - if (tabType != DesktopTabType.main && - state.value.tabs.length > 1) { - closeConfirmDialog(action); - } else { - action(); } }, isClose: true, @@ -457,30 +450,28 @@ class WindowActionPanel extends StatelessWidget { ], ); } +} - closeConfirmDialog(Function() callback) async { - final res = await gFFI.dialogManager.show((setState, close) { - submit() => close(true); - return CustomAlertDialog( - title: Row(children: [ - const Icon(Icons.warning_amber_sharp, - color: Colors.redAccent, size: 28), - const SizedBox(width: 10), - Text(translate("Warning")), - ]), - content: Text(translate("Disconnect all devices?")), - actions: [ - TextButton(onPressed: close, child: Text(translate("Cancel"))), - ElevatedButton(onPressed: submit, child: Text(translate("OK"))), - ], - onSubmit: submit, - onCancel: close, - ); - }); - if (res == true) { - callback(); - } - } +Future closeConfirmDialog() async { + final res = await gFFI.dialogManager.show((setState, close) { + submit() => close(true); + return CustomAlertDialog( + title: Row(children: [ + const Icon(Icons.warning_amber_sharp, + color: Colors.redAccent, size: 28), + const SizedBox(width: 10), + Text(translate("Warning")), + ]), + content: Text(translate("Disconnect all devices?")), + actions: [ + TextButton(onPressed: close, child: Text(translate("Cancel"))), + ElevatedButton(onPressed: submit, child: Text(translate("OK"))), + ], + onSubmit: submit, + onCancel: close, + ); + }); + return res == true; } // ignore: must_be_immutable diff --git a/flutter/lib/models/chat_model.dart b/flutter/lib/models/chat_model.dart index 4bdf5826a..e9952db1e 100644 --- a/flutter/lib/models/chat_model.dart +++ b/flutter/lib/models/chat_model.dart @@ -4,8 +4,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_hbb/models/platform_model.dart'; import 'package:window_manager/window_manager.dart'; -import '../../mobile/widgets/overlay.dart'; import '../common.dart'; +import '../common/widgets/overlay.dart'; import 'model.dart'; class MessageBody { @@ -128,7 +128,10 @@ class ChatModel with ChangeNotifier { if (overlayState == null) return; final overlay = OverlayEntry(builder: (context) { return DraggableChatWindow( - position: Offset(20, 80), width: 250, height: 350, chatModel: this); + position: const Offset(20, 80), + width: 250, + height: 350, + chatModel: this); }); overlayState.insert(overlay); chatWindowOverlayEntry = overlay;