Merge pull request #1247 from Heap-Hop/flutter_desktop

Update desktop and mobile chat message
This commit is contained in:
RustDesk 2022-08-11 10:23:57 +08:00 committed by GitHub
commit 0529e33434
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 151 additions and 126 deletions

View File

@ -115,11 +115,6 @@ class _RemotePageState extends State<RemotePage>
if (v < 100) { if (v < 100) {
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual,
overlays: []); 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<RemotePage>
body: Overlay( body: Overlay(
initialEntries: [ initialEntries: [
OverlayEntry(builder: (context) { OverlayEntry(builder: (context) {
_ffi.chatModel.setOverlayState(Overlay.of(context));
return Container( return Container(
color: Colors.black, color: Colors.black,
child: getBodyForDesktopWithListener(keyboard)); child: getRawPointerAndKeyBody(getBodyForDesktop(keyboard)));
}) })
], ],
)); ));
@ -290,8 +286,8 @@ class _RemotePageState extends State<RemotePage>
ChangeNotifierProvider.value(value: _ffi.cursorModel), ChangeNotifierProvider.value(value: _ffi.cursorModel),
ChangeNotifierProvider.value(value: _ffi.canvasModel), ChangeNotifierProvider.value(value: _ffi.canvasModel),
], ],
child: getRawPointerAndKeyBody(Consumer<FfiModel>( child: Consumer<FfiModel>(
builder: (context, ffiModel, _child) => buildBody(ffiModel))))); builder: (context, ffiModel, _child) => buildBody(ffiModel))));
} }
Widget getRawPointerAndKeyBody(Widget child) { Widget getRawPointerAndKeyBody(Widget child) {
@ -467,7 +463,7 @@ class _RemotePageState extends State<RemotePage>
onPressed: () { onPressed: () {
_ffi.chatModel _ffi.chatModel
.changeCurrentID(ChatModel.clientModeID); .changeCurrentID(ChatModel.clientModeID);
toggleChatOverlay(); _ffi.chatModel.toggleChatOverlay();
}, },
) )
]) + ]) +
@ -502,11 +498,27 @@ class _RemotePageState extends State<RemotePage>
/// DoubleFiner -> right click /// DoubleFiner -> right click
/// HoldDrag -> left drag /// HoldDrag -> left drag
Widget getBodyForDesktopWithListener(bool keyboard) { Widget getBodyForDesktop(bool keyboard) {
var paints = <Widget>[ var paints = <Widget>[
ImagePaint( MouseRegion(
id: widget.id, 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<CanvasModel>(context, listen: false)
.updateViewStyle();
});
return ImagePaint(
id: widget.id,
);
}),
))
]; ];
final cursor = bind.getSessionToggleOptionSync( final cursor = bind.getSessionToggleOptionSync(
id: widget.id, arg: 'show-remote-cursor'); id: widget.id, arg: 'show-remote-cursor');
@ -516,26 +528,9 @@ class _RemotePageState extends State<RemotePage>
)); ));
} }
paints.add(getHelpTools()); paints.add(getHelpTools());
return Stack(
return MouseRegion( children: paints,
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<CanvasModel>(context, listen: false)
.updateViewStyle();
});
return Stack(
children: paints,
);
}),
));
} }
int lastMouseDownButtons = 0; int lastMouseDownButtons = 0;

View File

@ -4,10 +4,15 @@ import 'package:flutter_hbb/common.dart';
import 'package:flutter_hbb/models/chat_model.dart'; import 'package:flutter_hbb/models/chat_model.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../../models/model.dart';
import 'home_page.dart'; import 'home_page.dart';
class ChatPage extends StatelessWidget implements PageShape { class ChatPage extends StatelessWidget implements PageShape {
late final ChatModel chatModel;
ChatPage({ChatModel? chatModel}) {
this.chatModel = chatModel ?? gFFI.chatModel;
}
@override @override
final title = translate("Chat"); final title = translate("Chat");
@ -19,6 +24,7 @@ class ChatPage extends StatelessWidget implements PageShape {
PopupMenuButton<int>( PopupMenuButton<int>(
icon: Icon(Icons.group), icon: Icon(Icons.group),
itemBuilder: (context) { itemBuilder: (context) {
// only mobile need [appBarActions], just bind gFFI.chatModel
final chatModel = gFFI.chatModel; final chatModel = gFFI.chatModel;
return chatModel.messages.entries.map((entry) { return chatModel.messages.entries.map((entry) {
final id = entry.key; final id = entry.key;
@ -37,7 +43,7 @@ class ChatPage extends StatelessWidget implements PageShape {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ChangeNotifierProvider.value( return ChangeNotifierProvider.value(
value: gFFI.chatModel, value: chatModel,
child: Container( child: Container(
color: MyTheme.grayBg, color: MyTheme.grayBg,
child: Consumer<ChatModel>(builder: (context, chatModel, child) { child: Consumer<ChatModel>(builder: (context, chatModel, child) {

View File

@ -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/server_page.dart';
import 'package:flutter_hbb/mobile/pages/settings_page.dart'; import 'package:flutter_hbb/mobile/pages/settings_page.dart';
import '../../common.dart'; import '../../common.dart';
import '../widgets/overlay.dart';
import 'connection_page.dart'; import 'connection_page.dart';
abstract class PageShape extends Widget { abstract class PageShape extends Widget {
@ -79,8 +78,8 @@ class _HomePageState extends State<HomePage> {
onTap: (index) => setState(() { onTap: (index) => setState(() {
// close chat overlay when go chat page // close chat overlay when go chat page
if (index == 1 && _selectedIndex != index) { if (index == 1 && _selectedIndex != index) {
hideChatIconOverlay(); gFFI.chatModel.hideChatIconOverlay();
hideChatWindowOverlay(); gFFI.chatModel.hideChatWindowOverlay();
} }
_selectedIndex = index; _selectedIndex = index;
}), }),

View File

@ -96,8 +96,8 @@ class _RemotePageState extends State<RemotePage> {
if (v < 100) { if (v < 100) {
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual,
overlays: []); overlays: []);
// [pi.version.isNotEmpty] -> check ready or not,avoid login without soft-keyboard // [pi.version.isNotEmpty] -> check ready or not, avoid login without soft-keyboard
if (chatWindowOverlayEntry == null && if (gFFI.chatModel.chatWindowOverlayEntry == null &&
gFFI.ffiModel.pi.version.isNotEmpty) { gFFI.ffiModel.pi.version.isNotEmpty) {
gFFI.invokeMethod("enable_soft_keyboard", false); gFFI.invokeMethod("enable_soft_keyboard", false);
} }
@ -453,7 +453,7 @@ class _RemotePageState extends State<RemotePage> {
onPressed: () { onPressed: () {
gFFI.chatModel gFFI.chatModel
.changeCurrentID(ChatModel.clientModeID); .changeCurrentID(ChatModel.clientModeID);
toggleChatOverlay(); gFFI.chatModel.toggleChatOverlay();
}, },
) )
]) + ]) +

View File

@ -1,22 +1,23 @@
import 'package:draggable_float_widget/draggable_float_widget.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hbb/common.dart'; import 'package:flutter_hbb/common.dart';
import '../../models/chat_model.dart';
import '../../models/model.dart'; import '../../models/model.dart';
import '../pages/chat_page.dart'; import '../pages/chat_page.dart';
OverlayEntry? chatIconOverlayEntry;
OverlayEntry? chatWindowOverlayEntry;
OverlayEntry? mobileActionsOverlayEntry; OverlayEntry? mobileActionsOverlayEntry;
class DraggableChatWindow extends StatelessWidget { class DraggableChatWindow extends StatelessWidget {
DraggableChatWindow( 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 Offset position;
final double width; final double width;
final double height; final double height;
final ChatModel chatModel;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -27,7 +28,7 @@ class DraggableChatWindow extends StatelessWidget {
height: height, height: height,
builder: (_, onPanUpdate) { builder: (_, onPanUpdate) {
return isIOS return isIOS
? ChatPage() ? ChatPage(chatModel: chatModel)
: Scaffold( : Scaffold(
resizeToAvoidBottomInset: false, resizeToAvoidBottomInset: false,
appBar: CustomAppBar( appBar: CustomAppBar(
@ -53,13 +54,13 @@ class DraggableChatWindow extends StatelessWidget {
children: [ children: [
IconButton( IconButton(
onPressed: () { onPressed: () {
hideChatWindowOverlay(); chatModel.hideChatWindowOverlay();
}, },
icon: Icon(Icons.keyboard_arrow_down)), icon: Icon(Icons.keyboard_arrow_down)),
IconButton( IconButton(
onPressed: () { onPressed: () {
hideChatWindowOverlay(); chatModel.hideChatWindowOverlay();
hideChatIconOverlay(); chatModel.hideChatIconOverlay();
}, },
icon: Icon(Icons.close)) 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); 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 /// floating buttons of back/home/recent actions for android
class DraggableMobileActions extends StatelessWidget { class DraggableMobileActions extends StatelessWidget {
DraggableMobileActions( DraggableMobileActions(

View File

@ -1,8 +1,10 @@
import 'package:dash_chat_2/dash_chat_2.dart'; 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/material.dart';
import 'package:flutter_hbb/models/platform_model.dart'; import 'package:flutter_hbb/models/platform_model.dart';
import '../../mobile/widgets/overlay.dart'; import '../../mobile/widgets/overlay.dart';
import '../common.dart';
import 'model.dart'; import 'model.dart';
class MessageBody { class MessageBody {
@ -22,6 +24,14 @@ class MessageBody {
class ChatModel with ChangeNotifier { class ChatModel with ChangeNotifier {
static final clientModeID = -1; 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( final ChatUser me = ChatUser(
id: "", id: "",
firstName: "Me", 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) { changeCurrentID(int id) {
if (_messages.containsKey(id)) { if (_messages.containsKey(id)) {
_currentID = id; _currentID = id;
@ -117,6 +215,7 @@ class ChatModel with ChangeNotifier {
close() { close() {
hideChatIconOverlay(); hideChatIconOverlay();
hideChatWindowOverlay(); hideChatWindowOverlay();
_overlayState = null;
notifyListeners(); notifyListeners();
} }