Merge pull request #1292 from Heap-Hop/flutter_desktop

Flutter desktop close connection tab
This commit is contained in:
RustDesk 2022-08-17 15:36:54 +08:00 committed by GitHub
commit cccfb8a736
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 77 additions and 39 deletions

View File

@ -4,6 +4,7 @@ import 'dart:io';
import 'package:desktop_multi_window/desktop_multi_window.dart'; import 'package:desktop_multi_window/desktop_multi_window.dart';
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart';
import 'package:get/instance_manager.dart'; import 'package:get/instance_manager.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:window_manager/window_manager.dart'; import 'package:window_manager/window_manager.dart';
@ -66,11 +67,11 @@ final ButtonStyle flatButtonStyle = TextButton.styleFrom(
), ),
); );
backToHomePage() { closeConnection({String? id}) {
if (isAndroid || isIOS) { if (isAndroid || isIOS) {
Navigator.popUntil(globalKey.currentContext!, ModalRoute.withName("/")); Navigator.popUntil(globalKey.currentContext!, ModalRoute.withName("/"));
} else { } else {
// TODO desktop closeTab(id);
} }
} }
@ -306,7 +307,7 @@ void msgBox(
0, 0,
wrap(translate('OK'), () { wrap(translate('OK'), () {
dialogManager.dismissAll(); dialogManager.dismissAll();
backToHomePage(); closeConnection();
})); }));
} }
if (hasCancel == null) { if (hasCancel == null) {
@ -482,7 +483,7 @@ RadioListTile<T> getRadio<T>(
CheckboxListTile getToggle( CheckboxListTile getToggle(
String id, void Function(void Function()) setState, option, name, String id, void Function(void Function()) setState, option, name,
{FFI? ffi}) { {FFI? ffi}) {
final opt = bind.getSessionToggleOptionSync(id: id, arg: option); final opt = bind.sessionGetToggleOptionSync(id: id, arg: option);
return CheckboxListTile( return CheckboxListTile(
value: opt, value: opt,
onChanged: (v) { onChanged: (v) {

View File

@ -104,6 +104,7 @@ class _ConnectionTabPageState extends State<ConnectionTabPage>
void onRemoveId(String id) { void onRemoveId(String id) {
DesktopTabBar.onClose(this, tabController, tabs, id); DesktopTabBar.onClose(this, tabController, tabs, id);
ffi(id).close();
if (tabs.length == 0) { if (tabs.length == 0) {
WindowController.fromWindowId(windowId()).close(); WindowController.fromWindowId(windowId()).close();
} }

View File

@ -93,6 +93,7 @@ class _FileManagerTabPageState extends State<FileManagerTabPage>
void onRemoveId(String id) { void onRemoveId(String id) {
DesktopTabBar.onClose(this, tabController, tabs, id); DesktopTabBar.onClose(this, tabController, tabs, id);
ffi(id).close();
if (tabs.length == 0) { if (tabs.length == 0) {
WindowController.fromWindowId(windowId()).close(); WindowController.fromWindowId(windowId()).close();
} }

View File

@ -60,7 +60,7 @@ class _RemotePageState extends State<RemotePage>
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []); SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []);
_ffi.dialogManager _ffi.dialogManager
.showLoading(translate('Connecting...'), onCancel: backToHomePage); .showLoading(translate('Connecting...'), onCancel: closeConnection);
}); });
if (!Platform.isLinux) { if (!Platform.isLinux) {
Wakelock.enable(); Wakelock.enable();
@ -490,7 +490,7 @@ class _RemotePageState extends State<RemotePage>
}), }),
)) ))
]; ];
final cursor = bind.getSessionToggleOptionSync( final cursor = bind.sessionGetToggleOptionSync(
id: widget.id, arg: 'show-remote-cursor'); id: widget.id, arg: 'show-remote-cursor');
if (keyboard || cursor) { if (keyboard || cursor) {
paints.add(CursorPaint( paints.add(CursorPaint(
@ -565,7 +565,7 @@ class _RemotePageState extends State<RemotePage>
more.add(PopupMenuItem<String>( more.add(PopupMenuItem<String>(
child: Text(translate('Insert Lock')), value: 'lock')); child: Text(translate('Insert Lock')), value: 'lock'));
if (pi.platform == 'Windows' && if (pi.platform == 'Windows' &&
await bind.getSessionToggleOption(id: id, arg: 'privacy-mode') != await bind.sessionGetToggleOption(id: id, arg: 'privacy-mode') !=
true) { true) {
more.add(PopupMenuItem<String>( more.add(PopupMenuItem<String>(
child: Text(translate( child: Text(translate(
@ -610,7 +610,7 @@ class _RemotePageState extends State<RemotePage>
// TODO icon diff // TODO icon diff
// null means no session of id // null means no session of id
// empty string means no password // empty string means no password
var password = await bind.getSessionOption(id: id, arg: "os-password"); var password = await bind.sessionGetOption(id: id, arg: "os-password");
if (password != null) { if (password != null) {
bind.sessionInputOsPassword(id: widget.id, value: password); bind.sessionInputOsPassword(id: widget.id, value: password);
} else { } else {
@ -837,12 +837,12 @@ class QualityMonitor extends StatelessWidget {
void showOptions(String id) async { void showOptions(String id) async {
final _ffi = ffi(id); final _ffi = ffi(id);
String quality = await bind.getSessionImageQuality(id: id) ?? 'balanced'; String quality = await bind.sessionGetImageQuality(id: id) ?? 'balanced';
if (quality == '') quality = 'balanced'; if (quality == '') quality = 'balanced';
String viewStyle = String viewStyle =
await bind.getSessionOption(id: id, arg: 'view-style') ?? ''; await bind.sessionGetOption(id: id, arg: 'view-style') ?? '';
String scrollStyle = String scrollStyle =
await bind.getSessionOption(id: id, arg: 'scroll-style') ?? ''; await bind.sessionGetOption(id: id, arg: 'scroll-style') ?? '';
var displays = <Widget>[]; var displays = <Widget>[];
final pi = _ffi.ffiModel.pi; final pi = _ffi.ffiModel.pi;
final image = _ffi.ffiModel.getConnectionImage(); final image = _ffi.ffiModel.getConnectionImage();
@ -957,8 +957,8 @@ void showOptions(String id) async {
void showSetOSPassword( void showSetOSPassword(
String id, bool login, OverlayDialogManager dialogManager) async { String id, bool login, OverlayDialogManager dialogManager) async {
final controller = TextEditingController(); final controller = TextEditingController();
var password = await bind.getSessionOption(id: id, arg: "os-password") ?? ""; var password = await bind.sessionGetOption(id: id, arg: "os-password") ?? "";
var autoLogin = await bind.getSessionOption(id: id, arg: "auto-login") != ""; var autoLogin = await bind.sessionGetOption(id: id, arg: "auto-login") != "";
controller.text = password; controller.text = password;
dialogManager.show((setState, close) { dialogManager.show((setState, close) {
return CustomAlertDialog( return CustomAlertDialog(

View File

@ -195,6 +195,17 @@ class _PeerCardState extends State<_PeerCard>
} else if (value == 'file') { } else if (value == 'file') {
_connect(id, isFileTransfer: true); _connect(id, isFileTransfer: true);
} else if (value == 'add-fav') { } else if (value == 'add-fav') {
final favs = (await bind.mainGetFav()).toList();
if (favs.indexOf(id) < 0) {
favs.add(id);
bind.mainStoreFav(favs: favs);
}
} else if (value == 'remove-fav') {
final favs = (await bind.mainGetFav()).toList();
if (favs.remove(id)) {
bind.mainStoreFav(favs: favs);
Get.forceAppUpdate(); // TODO use inner model / state
}
} else if (value == 'connect') { } else if (value == 'connect') {
_connect(id, isFileTransfer: false); _connect(id, isFileTransfer: false);
} else if (value == 'ab-delete') { } else if (value == 'ab-delete') {
@ -425,6 +436,8 @@ class RecentPeerCard extends BasePeerCard {
PopupMenuItem<String>( PopupMenuItem<String>(
child: Text(translate('Unremember Password')), child: Text(translate('Unremember Password')),
value: 'unremember-password'), value: 'unremember-password'),
PopupMenuItem<String>(
child: Text(translate('Add to Favorites')), value: 'add-fav'),
]; ];
} }
} }
@ -469,6 +482,8 @@ class DiscoveredPeerCard extends BasePeerCard {
PopupMenuItem<String>( PopupMenuItem<String>(
child: Text(translate('Unremember Password')), child: Text(translate('Unremember Password')),
value: 'unremember-password'), value: 'unremember-password'),
PopupMenuItem<String>(
child: Text(translate('Add to Favorites')), value: 'add-fav'),
]; ];
} }
} }

View File

@ -10,6 +10,25 @@ const double _kTabBarHeight = kDesktopRemoteTabBarHeight;
const double _kIconSize = 18; const double _kIconSize = 18;
const double _kDividerIndent = 10; const double _kDividerIndent = 10;
const double _kAddIconSize = _kTabBarHeight - 15; const double _kAddIconSize = _kTabBarHeight - 15;
final tabBarKey = GlobalKey();
void closeTab(String? id) {
final tabBar = tabBarKey.currentWidget as TabBar?;
if (tabBar == null) return;
final tabs = tabBar.tabs as List<_Tab>;
if (id == null) {
final current = tabBar.controller?.index;
if (current == null) return;
tabs[current].onClose();
} else {
for (final tab in tabs) {
if (tab.label == id) {
tab.onClose();
break;
}
}
}
}
class TabInfo { class TabInfo {
late final String label; late final String label;
@ -59,6 +78,7 @@ class DesktopTabBar extends StatelessWidget {
), ),
Flexible( Flexible(
child: Obx(() => TabBar( child: Obx(() => TabBar(
key: tabBarKey,
indicatorColor: _theme.indicatorColor, indicatorColor: _theme.indicatorColor,
labelPadding: const EdgeInsets.symmetric( labelPadding: const EdgeInsets.symmetric(
vertical: 0, horizontal: 0), vertical: 0, horizontal: 0),

View File

@ -29,7 +29,7 @@ class _FileManagerPageState extends State<FileManagerPage> {
gFFI.connect(widget.id, isFileTransfer: true); gFFI.connect(widget.id, isFileTransfer: true);
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
gFFI.dialogManager gFFI.dialogManager
.showLoading(translate('Connecting...'), onCancel: backToHomePage); .showLoading(translate('Connecting...'), onCancel: closeConnection);
}); });
gFFI.ffiModel.updateEventListener(widget.id); gFFI.ffiModel.updateEventListener(widget.id);
Wakelock.enable(); Wakelock.enable();

View File

@ -51,7 +51,7 @@ class _RemotePageState extends State<RemotePage> {
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []); SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []);
gFFI.dialogManager gFFI.dialogManager
.showLoading(translate('Connecting...'), onCancel: backToHomePage); .showLoading(translate('Connecting...'), onCancel: closeConnection);
_interval = _interval =
Timer.periodic(Duration(milliseconds: 30), (timer) => interval()); Timer.periodic(Duration(milliseconds: 30), (timer) => interval());
}); });
@ -623,7 +623,7 @@ class _RemotePageState extends State<RemotePage> {
Widget getBodyForDesktopWithListener(bool keyboard) { Widget getBodyForDesktopWithListener(bool keyboard) {
var paints = <Widget>[ImagePaint()]; var paints = <Widget>[ImagePaint()];
final cursor = bind.getSessionToggleOptionSync( final cursor = bind.sessionGetToggleOptionSync(
id: widget.id, arg: 'show-remote-cursor'); id: widget.id, arg: 'show-remote-cursor');
if (keyboard || cursor) { if (keyboard || cursor) {
paints.add(CursorPaint()); paints.add(CursorPaint());
@ -694,7 +694,7 @@ class _RemotePageState extends State<RemotePage> {
more.add(PopupMenuItem<String>( more.add(PopupMenuItem<String>(
child: Text(translate('Insert Lock')), value: 'lock')); child: Text(translate('Insert Lock')), value: 'lock'));
if (pi.platform == 'Windows' && if (pi.platform == 'Windows' &&
await bind.getSessionToggleOption(id: id, arg: 'privacy-mode') != await bind.sessionGetToggleOption(id: id, arg: 'privacy-mode') !=
true) { true) {
more.add(PopupMenuItem<String>( more.add(PopupMenuItem<String>(
child: Text(translate((gFFI.ffiModel.inputBlocked ? 'Unb' : 'B') + child: Text(translate((gFFI.ffiModel.inputBlocked ? 'Unb' : 'B') +
@ -738,7 +738,7 @@ class _RemotePageState extends State<RemotePage> {
// FIXME: // FIXME:
// null means no session of id // null means no session of id
// empty string means no password // empty string means no password
var password = await bind.getSessionOption(id: id, arg: "os-password"); var password = await bind.sessionGetOption(id: id, arg: "os-password");
if (password != null) { if (password != null) {
bind.sessionInputOsPassword(id: widget.id, value: password); bind.sessionInputOsPassword(id: widget.id, value: password);
} else { } else {
@ -1012,10 +1012,10 @@ class QualityMonitor extends StatelessWidget {
} }
void showOptions(String id, OverlayDialogManager dialogManager) async { void showOptions(String id, OverlayDialogManager dialogManager) async {
String quality = await bind.getSessionImageQuality(id: id) ?? 'balanced'; String quality = await bind.sessionGetImageQuality(id: id) ?? 'balanced';
if (quality == '') quality = 'balanced'; if (quality == '') quality = 'balanced';
String viewStyle = String viewStyle =
await bind.getSessionOption(id: id, arg: 'view-style') ?? ''; await bind.sessionGetOption(id: id, arg: 'view-style') ?? '';
var displays = <Widget>[]; var displays = <Widget>[];
final pi = gFFI.ffiModel.pi; final pi = gFFI.ffiModel.pi;
final image = gFFI.ffiModel.getConnectionImage(); final image = gFFI.ffiModel.getConnectionImage();
@ -1113,8 +1113,8 @@ void showOptions(String id, OverlayDialogManager dialogManager) async {
void showSetOSPassword( void showSetOSPassword(
String id, bool login, OverlayDialogManager dialogManager) async { String id, bool login, OverlayDialogManager dialogManager) async {
final controller = TextEditingController(); final controller = TextEditingController();
var password = await bind.getSessionOption(id: id, arg: "os-password") ?? ""; var password = await bind.sessionGetOption(id: id, arg: "os-password") ?? "";
var autoLogin = await bind.getSessionOption(id: id, arg: "auto-login") != ""; var autoLogin = await bind.sessionGetOption(id: id, arg: "auto-login") != "";
controller.text = password; controller.text = password;
dialogManager.show((setState, close) { dialogManager.show((setState, close) {
return CustomAlertDialog( return CustomAlertDialog(

View File

@ -132,7 +132,7 @@ class _ScanPageState extends State<ScanPage> {
} }
void showServerSettingFromQr(String data) async { void showServerSettingFromQr(String data) async {
backToHomePage(); closeConnection();
await controller?.pauseCamera(); await controller?.pauseCamera();
if (!data.startsWith('config=')) { if (!data.startsWith('config=')) {
showToast('Invalid QR code'); showToast('Invalid QR code');

View File

@ -157,7 +157,7 @@ void setTemporaryPasswordLengthDialog(
void enterPasswordDialog(String id, OverlayDialogManager dialogManager) async { void enterPasswordDialog(String id, OverlayDialogManager dialogManager) async {
final controller = TextEditingController(); final controller = TextEditingController();
var remember = await bind.getSessionRemember(id: id) ?? false; var remember = await bind.sessionGetRemember(id: id) ?? false;
dialogManager.dismissAll(); dialogManager.dismissAll();
dialogManager.show((setState, close) { dialogManager.show((setState, close) {
return CustomAlertDialog( return CustomAlertDialog(
@ -184,7 +184,7 @@ void enterPasswordDialog(String id, OverlayDialogManager dialogManager) async {
style: flatButtonStyle, style: flatButtonStyle,
onPressed: () { onPressed: () {
close(); close();
backToHomePage(); closeConnection();
}, },
child: Text(translate('Cancel')), child: Text(translate('Cancel')),
), ),
@ -196,7 +196,7 @@ void enterPasswordDialog(String id, OverlayDialogManager dialogManager) async {
gFFI.login(id, text, remember); gFFI.login(id, text, remember);
close(); close();
dialogManager.showLoading(translate('Logging in...'), dialogManager.showLoading(translate('Logging in...'),
onCancel: backToHomePage); onCancel: closeConnection);
}, },
child: Text(translate('OK')), child: Text(translate('OK')),
), ),
@ -214,7 +214,7 @@ void wrongPasswordDialog(String id, OverlayDialogManager dialogManager) {
style: flatButtonStyle, style: flatButtonStyle,
onPressed: () { onPressed: () {
close(); close();
backToHomePage(); closeConnection();
}, },
child: Text(translate('Cancel')), child: Text(translate('Cancel')),
), ),

View File

@ -287,7 +287,7 @@ class FfiModel with ChangeNotifier {
bind.sessionReconnect(id: id); bind.sessionReconnect(id: id);
clearPermissions(); clearPermissions();
dialogManager.showLoading(translate('Connecting...'), dialogManager.showLoading(translate('Connecting...'),
onCancel: backToHomePage); onCancel: closeConnection);
}); });
_reconnects *= 2; _reconnects *= 2;
} else { } else {
@ -312,7 +312,7 @@ class FfiModel with ChangeNotifier {
} }
} else { } else {
_touchMode = _touchMode =
await bind.getSessionOption(id: peerId, arg: "touch-mode") != ''; await bind.sessionGetOption(id: peerId, arg: "touch-mode") != '';
} }
if (evt['is_file_transfer'] == "true") { if (evt['is_file_transfer'] == "true") {
@ -335,7 +335,7 @@ class FfiModel with ChangeNotifier {
if (displays.length > 0) { if (displays.length > 0) {
parent.target?.dialogManager.showLoading( parent.target?.dialogManager.showLoading(
translate('Connected, waiting for image...'), translate('Connected, waiting for image...'),
onCancel: backToHomePage); onCancel: closeConnection);
_waitForImage = true; _waitForImage = true;
_reconnects = 1; _reconnects = 1;
} }
@ -471,7 +471,7 @@ class CanvasModel with ChangeNotifier {
double get tabBarHeight => _tabBarHeight; double get tabBarHeight => _tabBarHeight;
void updateViewStyle() async { void updateViewStyle() async {
final style = await bind.getSessionOption(id: id, arg: 'view-style'); final style = await bind.sessionGetOption(id: id, arg: 'view-style');
if (style == null) { if (style == null) {
return; return;
} }
@ -517,7 +517,7 @@ class CanvasModel with ChangeNotifier {
} }
updateScrollStyle() async { updateScrollStyle() async {
final style = await bind.getSessionOption(id: id, arg: 'scroll-style'); final style = await bind.sessionGetOption(id: id, arg: 'scroll-style');
if (style == 'scrollbar') { if (style == 'scrollbar') {
_scrollStyle = ScrollStyle.scrollbar; _scrollStyle = ScrollStyle.scrollbar;
_scrollX = 0.0; _scrollX = 0.0;
@ -863,7 +863,7 @@ class QualityMonitorModel with ChangeNotifier {
QualityMonitorData get data => _data; QualityMonitorData get data => _data;
checkShowQualityMonitor(String id) async { checkShowQualityMonitor(String id) async {
final show = await bind.getSessionToggleOption( final show = await bind.sessionGetToggleOption(
id: id, arg: 'show-quality-monitor') == id: id, arg: 'show-quality-monitor') ==
true; true;
if (_show != show) { if (_show != show) {

View File

@ -116,7 +116,7 @@ pub fn session_connect(
Ok(()) Ok(())
} }
pub fn get_session_remember(id: String) -> Option<bool> { pub fn session_get_remember(id: String) -> Option<bool> {
if let Some(session) = SESSIONS.read().unwrap().get(&id) { if let Some(session) = SESSIONS.read().unwrap().get(&id) {
Some(session.get_remember()) Some(session.get_remember())
} else { } else {
@ -124,7 +124,7 @@ pub fn get_session_remember(id: String) -> Option<bool> {
} }
} }
pub fn get_session_toggle_option(id: String, arg: String) -> Option<bool> { pub fn session_get_toggle_option(id: String, arg: String) -> Option<bool> {
if let Some(session) = SESSIONS.read().unwrap().get(&id) { if let Some(session) = SESSIONS.read().unwrap().get(&id) {
Some(session.get_toggle_option(&arg)) Some(session.get_toggle_option(&arg))
} else { } else {
@ -132,12 +132,12 @@ pub fn get_session_toggle_option(id: String, arg: String) -> Option<bool> {
} }
} }
pub fn get_session_toggle_option_sync(id: String, arg: String) -> SyncReturn<bool> { pub fn session_get_toggle_option_sync(id: String, arg: String) -> SyncReturn<bool> {
let res = get_session_toggle_option(id, arg) == Some(true); let res = session_get_toggle_option(id, arg) == Some(true);
SyncReturn(res) SyncReturn(res)
} }
pub fn get_session_image_quality(id: String) -> Option<String> { pub fn session_get_image_quality(id: String) -> Option<String> {
if let Some(session) = SESSIONS.read().unwrap().get(&id) { if let Some(session) = SESSIONS.read().unwrap().get(&id) {
Some(session.get_image_quality()) Some(session.get_image_quality())
} else { } else {
@ -145,7 +145,7 @@ pub fn get_session_image_quality(id: String) -> Option<String> {
} }
} }
pub fn get_session_option(id: String, arg: String) -> Option<String> { pub fn session_get_option(id: String, arg: String) -> Option<String> {
if let Some(session) = SESSIONS.read().unwrap().get(&id) { if let Some(session) = SESSIONS.read().unwrap().get(&id) {
Some(session.get_option(&arg)) Some(session.get_option(&arg))
} else { } else {