From 09c80bc585d7138eac86051d71f3938aa013c180 Mon Sep 17 00:00:00 2001 From: csf Date: Thu, 11 Aug 2022 10:19:12 +0800 Subject: [PATCH] update desktop and mobile chat message --- flutter/lib/desktop/pages/remote_page.dart | 61 ++++++------- flutter/lib/mobile/pages/chat_page.dart | 10 ++- flutter/lib/mobile/pages/home_page.dart | 5 +- flutter/lib/mobile/pages/remote_page.dart | 6 +- flutter/lib/mobile/widgets/overlay.dart | 96 +++------------------ flutter/lib/models/chat_model.dart | 99 ++++++++++++++++++++++ 6 files changed, 151 insertions(+), 126 deletions(-) diff --git a/flutter/lib/desktop/pages/remote_page.dart b/flutter/lib/desktop/pages/remote_page.dart index da7a317a8..fc94d5be8 100644 --- a/flutter/lib/desktop/pages/remote_page.dart +++ b/flutter/lib/desktop/pages/remote_page.dart @@ -115,11 +115,6 @@ class _RemotePageState extends State if (v < 100) { SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []); - // [pi.version.isNotEmpty] -> check ready or not,avoid login without soft-keyboard - if (chatWindowOverlayEntry == null && - _ffi.ffiModel.pi.version.isNotEmpty) { - _ffi.invokeMethod("enable_soft_keyboard", false); - } } }); } @@ -266,9 +261,10 @@ class _RemotePageState extends State body: Overlay( initialEntries: [ OverlayEntry(builder: (context) { + _ffi.chatModel.setOverlayState(Overlay.of(context)); return Container( color: Colors.black, - child: getBodyForDesktopWithListener(keyboard)); + child: getRawPointerAndKeyBody(getBodyForDesktop(keyboard))); }) ], )); @@ -290,8 +286,8 @@ class _RemotePageState extends State ChangeNotifierProvider.value(value: _ffi.cursorModel), ChangeNotifierProvider.value(value: _ffi.canvasModel), ], - child: getRawPointerAndKeyBody(Consumer( - builder: (context, ffiModel, _child) => buildBody(ffiModel))))); + child: Consumer( + builder: (context, ffiModel, _child) => buildBody(ffiModel)))); } Widget getRawPointerAndKeyBody(Widget child) { @@ -467,7 +463,7 @@ class _RemotePageState extends State onPressed: () { _ffi.chatModel .changeCurrentID(ChatModel.clientModeID); - toggleChatOverlay(); + _ffi.chatModel.toggleChatOverlay(); }, ) ]) + @@ -502,11 +498,27 @@ class _RemotePageState extends State /// DoubleFiner -> right click /// HoldDrag -> left drag - Widget getBodyForDesktopWithListener(bool keyboard) { + Widget getBodyForDesktop(bool keyboard) { var paints = [ - ImagePaint( - id: widget.id, - ) + MouseRegion( + onEnter: (evt) { + bind.hostStopSystemKeyPropagate(stopped: false); + }, + onExit: (evt) { + bind.hostStopSystemKeyPropagate(stopped: true); + }, + child: Container( + color: MyTheme.canvasColor, + child: LayoutBuilder(builder: (context, constraints) { + Future.delayed(Duration.zero, () { + Provider.of(context, listen: false) + .updateViewStyle(); + }); + return ImagePaint( + id: widget.id, + ); + }), + )) ]; final cursor = bind.getSessionToggleOptionSync( id: widget.id, arg: 'show-remote-cursor'); @@ -516,26 +528,9 @@ class _RemotePageState extends State )); } paints.add(getHelpTools()); - - return MouseRegion( - onEnter: (evt) { - bind.hostStopSystemKeyPropagate(stopped: false); - }, - onExit: (evt) { - bind.hostStopSystemKeyPropagate(stopped: true); - }, - child: Container( - color: MyTheme.canvasColor, - child: LayoutBuilder(builder: (context, constraints) { - Future.delayed(Duration.zero, () { - Provider.of(context, listen: false) - .updateViewStyle(); - }); - return Stack( - children: paints, - ); - }), - )); + return Stack( + children: paints, + ); } int lastMouseDownButtons = 0; diff --git a/flutter/lib/mobile/pages/chat_page.dart b/flutter/lib/mobile/pages/chat_page.dart index a49a02bb4..0bc4c2a25 100644 --- a/flutter/lib/mobile/pages/chat_page.dart +++ b/flutter/lib/mobile/pages/chat_page.dart @@ -4,10 +4,15 @@ import 'package:flutter_hbb/common.dart'; import 'package:flutter_hbb/models/chat_model.dart'; import 'package:provider/provider.dart'; -import '../../models/model.dart'; import 'home_page.dart'; class ChatPage extends StatelessWidget implements PageShape { + late final ChatModel chatModel; + + ChatPage({ChatModel? chatModel}) { + this.chatModel = chatModel ?? gFFI.chatModel; + } + @override final title = translate("Chat"); @@ -19,6 +24,7 @@ class ChatPage extends StatelessWidget implements PageShape { PopupMenuButton( icon: Icon(Icons.group), itemBuilder: (context) { + // only mobile need [appBarActions], just bind gFFI.chatModel final chatModel = gFFI.chatModel; return chatModel.messages.entries.map((entry) { final id = entry.key; @@ -37,7 +43,7 @@ class ChatPage extends StatelessWidget implements PageShape { @override Widget build(BuildContext context) { return ChangeNotifierProvider.value( - value: gFFI.chatModel, + value: chatModel, child: Container( color: MyTheme.grayBg, child: Consumer(builder: (context, chatModel, child) { diff --git a/flutter/lib/mobile/pages/home_page.dart b/flutter/lib/mobile/pages/home_page.dart index 6bf0be2c7..05a6d6b51 100644 --- a/flutter/lib/mobile/pages/home_page.dart +++ b/flutter/lib/mobile/pages/home_page.dart @@ -3,7 +3,6 @@ import 'package:flutter_hbb/mobile/pages/chat_page.dart'; import 'package:flutter_hbb/mobile/pages/server_page.dart'; import 'package:flutter_hbb/mobile/pages/settings_page.dart'; import '../../common.dart'; -import '../widgets/overlay.dart'; import 'connection_page.dart'; abstract class PageShape extends Widget { @@ -79,8 +78,8 @@ class _HomePageState extends State { onTap: (index) => setState(() { // close chat overlay when go chat page if (index == 1 && _selectedIndex != index) { - hideChatIconOverlay(); - hideChatWindowOverlay(); + gFFI.chatModel.hideChatIconOverlay(); + gFFI.chatModel.hideChatWindowOverlay(); } _selectedIndex = index; }), diff --git a/flutter/lib/mobile/pages/remote_page.dart b/flutter/lib/mobile/pages/remote_page.dart index 9b938a1ce..69bf11de0 100644 --- a/flutter/lib/mobile/pages/remote_page.dart +++ b/flutter/lib/mobile/pages/remote_page.dart @@ -96,8 +96,8 @@ class _RemotePageState extends State { if (v < 100) { SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []); - // [pi.version.isNotEmpty] -> check ready or not,avoid login without soft-keyboard - if (chatWindowOverlayEntry == null && + // [pi.version.isNotEmpty] -> check ready or not, avoid login without soft-keyboard + if (gFFI.chatModel.chatWindowOverlayEntry == null && gFFI.ffiModel.pi.version.isNotEmpty) { gFFI.invokeMethod("enable_soft_keyboard", false); } @@ -453,7 +453,7 @@ class _RemotePageState extends State { onPressed: () { gFFI.chatModel .changeCurrentID(ChatModel.clientModeID); - toggleChatOverlay(); + gFFI.chatModel.toggleChatOverlay(); }, ) ]) + diff --git a/flutter/lib/mobile/widgets/overlay.dart b/flutter/lib/mobile/widgets/overlay.dart index 362f62974..976d9bb73 100644 --- a/flutter/lib/mobile/widgets/overlay.dart +++ b/flutter/lib/mobile/widgets/overlay.dart @@ -1,22 +1,23 @@ -import 'package:draggable_float_widget/draggable_float_widget.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hbb/common.dart'; +import '../../models/chat_model.dart'; import '../../models/model.dart'; import '../pages/chat_page.dart'; -OverlayEntry? chatIconOverlayEntry; -OverlayEntry? chatWindowOverlayEntry; - OverlayEntry? mobileActionsOverlayEntry; class DraggableChatWindow extends StatelessWidget { DraggableChatWindow( - {this.position = Offset.zero, required this.width, required this.height}); + {this.position = Offset.zero, + required this.width, + required this.height, + required this.chatModel}); final Offset position; final double width; final double height; + final ChatModel chatModel; @override Widget build(BuildContext context) { @@ -27,7 +28,7 @@ class DraggableChatWindow extends StatelessWidget { height: height, builder: (_, onPanUpdate) { return isIOS - ? ChatPage() + ? ChatPage(chatModel: chatModel) : Scaffold( resizeToAvoidBottomInset: false, appBar: CustomAppBar( @@ -53,13 +54,13 @@ class DraggableChatWindow extends StatelessWidget { children: [ IconButton( onPressed: () { - hideChatWindowOverlay(); + chatModel.hideChatWindowOverlay(); }, icon: Icon(Icons.keyboard_arrow_down)), IconButton( onPressed: () { - hideChatWindowOverlay(); - hideChatIconOverlay(); + chatModel.hideChatWindowOverlay(); + chatModel.hideChatIconOverlay(); }, icon: Icon(Icons.close)) ], @@ -68,7 +69,7 @@ class DraggableChatWindow extends StatelessWidget { ), ), ), - body: ChatPage(), + body: ChatPage(chatModel: chatModel), ); }); } @@ -91,81 +92,6 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { Size get preferredSize => new Size.fromHeight(kToolbarHeight); } -showChatIconOverlay({Offset offset = const Offset(200, 50)}) { - if (chatIconOverlayEntry != null) { - chatIconOverlayEntry!.remove(); - } - if (globalKey.currentState == null || globalKey.currentState!.overlay == null) - return; - final bar = navigationBarKey.currentWidget; - if (bar != null) { - if ((bar as BottomNavigationBar).currentIndex == 1) { - return; - } - } - final globalOverlayState = globalKey.currentState!.overlay!; - - final overlay = OverlayEntry(builder: (context) { - return DraggableFloatWidget( - config: DraggableFloatWidgetBaseConfig( - initPositionYInTop: false, - initPositionYMarginBorder: 100, - borderTopContainTopBar: true, - ), - child: FloatingActionButton( - onPressed: () { - if (chatWindowOverlayEntry == null) { - showChatWindowOverlay(); - } else { - hideChatWindowOverlay(); - } - }, - child: Icon(Icons.message))); - }); - globalOverlayState.insert(overlay); - chatIconOverlayEntry = overlay; -} - -hideChatIconOverlay() { - if (chatIconOverlayEntry != null) { - chatIconOverlayEntry!.remove(); - chatIconOverlayEntry = null; - } -} - -showChatWindowOverlay() { - if (chatWindowOverlayEntry != null) return; - if (globalKey.currentState == null || globalKey.currentState!.overlay == null) - return; - final globalOverlayState = globalKey.currentState!.overlay!; - - final overlay = OverlayEntry(builder: (context) { - return DraggableChatWindow( - position: Offset(20, 80), width: 250, height: 350); - }); - globalOverlayState.insert(overlay); - chatWindowOverlayEntry = overlay; -} - -hideChatWindowOverlay() { - if (chatWindowOverlayEntry != null) { - chatWindowOverlayEntry!.remove(); - chatWindowOverlayEntry = null; - return; - } -} - -toggleChatOverlay() { - if (chatIconOverlayEntry == null || chatWindowOverlayEntry == null) { - gFFI.invokeMethod("enable_soft_keyboard", true); - showChatIconOverlay(); - showChatWindowOverlay(); - } else { - hideChatIconOverlay(); - hideChatWindowOverlay(); - } -} - /// floating buttons of back/home/recent actions for android class DraggableMobileActions extends StatelessWidget { DraggableMobileActions( diff --git a/flutter/lib/models/chat_model.dart b/flutter/lib/models/chat_model.dart index 52f00aa01..9b9f70756 100644 --- a/flutter/lib/models/chat_model.dart +++ b/flutter/lib/models/chat_model.dart @@ -1,8 +1,10 @@ import 'package:dash_chat_2/dash_chat_2.dart'; +import 'package:draggable_float_widget/draggable_float_widget.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hbb/models/platform_model.dart'; import '../../mobile/widgets/overlay.dart'; +import '../common.dart'; import 'model.dart'; class MessageBody { @@ -22,6 +24,14 @@ class MessageBody { class ChatModel with ChangeNotifier { static final clientModeID = -1; + /// _overlayState: + /// Desktop: store session overlay by using [setOverlayState]. + /// Mobile: always null, use global overlay. + /// see [_getOverlayState] in [showChatIconOverlay] or [showChatWindowOverlay] + OverlayState? _overlayState; + OverlayEntry? chatIconOverlayEntry; + OverlayEntry? chatWindowOverlayEntry; + final ChatUser me = ChatUser( id: "", firstName: "Me", @@ -51,6 +61,94 @@ class ChatModel with ChangeNotifier { } } + setOverlayState(OverlayState? os) { + _overlayState = os; + } + + OverlayState? _getOverlayState() { + if (_overlayState == null) { + if (globalKey.currentState == null || + globalKey.currentState!.overlay == null) return null; + return globalKey.currentState!.overlay; + } else { + return _overlayState; + } + } + + showChatIconOverlay({Offset offset = const Offset(200, 50)}) { + if (chatIconOverlayEntry != null) { + chatIconOverlayEntry!.remove(); + } + // mobile check navigationBar + final bar = navigationBarKey.currentWidget; + if (bar != null) { + if ((bar as BottomNavigationBar).currentIndex == 1) { + return; + } + } + + final overlayState = _getOverlayState(); + if (overlayState == null) return; + + final overlay = OverlayEntry(builder: (context) { + return DraggableFloatWidget( + config: DraggableFloatWidgetBaseConfig( + initPositionYInTop: false, + initPositionYMarginBorder: 100, + borderTopContainTopBar: true, + ), + child: FloatingActionButton( + onPressed: () { + if (chatWindowOverlayEntry == null) { + showChatWindowOverlay(); + } else { + hideChatWindowOverlay(); + } + }, + child: Icon(Icons.message))); + }); + overlayState.insert(overlay); + chatIconOverlayEntry = overlay; + } + + hideChatIconOverlay() { + if (chatIconOverlayEntry != null) { + chatIconOverlayEntry!.remove(); + chatIconOverlayEntry = null; + } + } + + showChatWindowOverlay() { + if (chatWindowOverlayEntry != null) return; + final overlayState = _getOverlayState(); + if (overlayState == null) return; + final overlay = OverlayEntry(builder: (context) { + return DraggableChatWindow( + position: Offset(20, 80), width: 250, height: 350, chatModel: this); + }); + overlayState.insert(overlay); + chatWindowOverlayEntry = overlay; + } + + hideChatWindowOverlay() { + if (chatWindowOverlayEntry != null) { + chatWindowOverlayEntry!.remove(); + chatWindowOverlayEntry = null; + return; + } + } + + toggleChatOverlay() { + if (chatIconOverlayEntry == null || chatWindowOverlayEntry == null) { + gFFI.invokeMethod("enable_soft_keyboard", true); + showChatIconOverlay(); + showChatWindowOverlay(); + } else { + hideChatIconOverlay(); + hideChatWindowOverlay(); + } + } + changeCurrentID(int id) { if (_messages.containsKey(id)) { _currentID = id; @@ -117,6 +215,7 @@ class ChatModel with ChangeNotifier { close() { hideChatIconOverlay(); hideChatWindowOverlay(); + _overlayState = null; notifyListeners(); }