fix: unable to close on fullscreen (#8690)
Signed-off-by: fufesou <linlong1266@gmail.com>
This commit is contained in:
parent
1d59a7fe5f
commit
888e993534
@ -230,8 +230,7 @@ typedef LabelGetter = Rx<String> Function(String key);
|
|||||||
int _lastClickTime =
|
int _lastClickTime =
|
||||||
DateTime.now().millisecondsSinceEpoch - bind.getDoubleClickTime() - 1000;
|
DateTime.now().millisecondsSinceEpoch - bind.getDoubleClickTime() - 1000;
|
||||||
|
|
||||||
// ignore: must_be_immutable
|
class DesktopTab extends StatefulWidget {
|
||||||
class DesktopTab extends StatelessWidget {
|
|
||||||
final bool showLogo;
|
final bool showLogo;
|
||||||
final bool showTitle;
|
final bool showTitle;
|
||||||
final bool showMinimize;
|
final bool showMinimize;
|
||||||
@ -252,12 +251,8 @@ class DesktopTab extends StatelessWidget {
|
|||||||
|
|
||||||
final DesktopTabController controller;
|
final DesktopTabController controller;
|
||||||
|
|
||||||
Rx<DesktopTabState> get state => controller.state;
|
|
||||||
final _scrollDebounce = Debouncer(delay: Duration(milliseconds: 50));
|
final _scrollDebounce = Debouncer(delay: Duration(milliseconds: 50));
|
||||||
|
|
||||||
late final DesktopTabType tabType;
|
|
||||||
late final bool isMainWindow;
|
|
||||||
|
|
||||||
final RxList<String> invisibleTabKeys = RxList.empty();
|
final RxList<String> invisibleTabKeys = RxList.empty();
|
||||||
|
|
||||||
DesktopTab({
|
DesktopTab({
|
||||||
@ -279,18 +274,232 @@ class DesktopTab extends StatelessWidget {
|
|||||||
this.unSelectedTabBackgroundColor,
|
this.unSelectedTabBackgroundColor,
|
||||||
this.selectedBorderColor,
|
this.selectedBorderColor,
|
||||||
this.blockTab,
|
this.blockTab,
|
||||||
}) : super(key: key) {
|
}) : super(key: key);
|
||||||
tabType = controller.tabType;
|
|
||||||
isMainWindow = tabType == DesktopTabType.main ||
|
|
||||||
tabType == DesktopTabType.cm ||
|
|
||||||
tabType == DesktopTabType.install;
|
|
||||||
}
|
|
||||||
|
|
||||||
static RxString tablabelGetter(String peerId) {
|
static RxString tablabelGetter(String peerId) {
|
||||||
final alias = bind.mainGetPeerOptionSync(id: peerId, key: 'alias');
|
final alias = bind.mainGetPeerOptionSync(id: peerId, key: 'alias');
|
||||||
return RxString(getDesktopTabLabel(peerId, alias));
|
return RxString(getDesktopTabLabel(peerId, alias));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<DesktopTab> createState() {
|
||||||
|
return _DesktopTabState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ignore: must_be_immutable
|
||||||
|
class _DesktopTabState extends State<DesktopTab>
|
||||||
|
with MultiWindowListener, WindowListener {
|
||||||
|
final _saveFrameDebounce = Debouncer(delay: Duration(seconds: 1));
|
||||||
|
Timer? _macOSCheckRestoreTimer;
|
||||||
|
int _macOSCheckRestoreCounter = 0;
|
||||||
|
|
||||||
|
bool get showLogo => widget.showLogo;
|
||||||
|
bool get showTitle => widget.showTitle;
|
||||||
|
bool get showMinimize => widget.showMinimize;
|
||||||
|
bool get showMaximize => widget.showMaximize;
|
||||||
|
bool get showClose => widget.showClose;
|
||||||
|
Widget Function(Widget pageView)? get pageViewBuilder =>
|
||||||
|
widget.pageViewBuilder;
|
||||||
|
TabMenuBuilder? get tabMenuBuilder => widget.tabMenuBuilder;
|
||||||
|
Widget? get tail => widget.tail;
|
||||||
|
Future<bool> Function()? get onWindowCloseButton =>
|
||||||
|
widget.onWindowCloseButton;
|
||||||
|
TabBuilder? get tabBuilder => widget.tabBuilder;
|
||||||
|
LabelGetter? get labelGetter => widget.labelGetter;
|
||||||
|
double? get maxLabelWidth => widget.maxLabelWidth;
|
||||||
|
Color? get selectedTabBackgroundColor => widget.selectedTabBackgroundColor;
|
||||||
|
Color? get unSelectedTabBackgroundColor =>
|
||||||
|
widget.unSelectedTabBackgroundColor;
|
||||||
|
Color? get selectedBorderColor => widget.selectedBorderColor;
|
||||||
|
RxBool? get blockTab => widget.blockTab;
|
||||||
|
DesktopTabController get controller => widget.controller;
|
||||||
|
RxList<String> get invisibleTabKeys => widget.invisibleTabKeys;
|
||||||
|
Debouncer get _scrollDebounce => widget._scrollDebounce;
|
||||||
|
|
||||||
|
Rx<DesktopTabState> get state => controller.state;
|
||||||
|
|
||||||
|
DesktopTabType get tabType => controller.tabType;
|
||||||
|
bool get isMainWindow =>
|
||||||
|
tabType == DesktopTabType.main ||
|
||||||
|
tabType == DesktopTabType.cm ||
|
||||||
|
tabType == DesktopTabType.install;
|
||||||
|
|
||||||
|
_DesktopTabState() : super();
|
||||||
|
|
||||||
|
static RxString tablabelGetter(String peerId) {
|
||||||
|
final alias = bind.mainGetPeerOptionSync(id: peerId, key: 'alias');
|
||||||
|
return RxString(getDesktopTabLabel(peerId, alias));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
DesktopMultiWindow.addListener(this);
|
||||||
|
windowManager.addListener(this);
|
||||||
|
|
||||||
|
Future.delayed(Duration(milliseconds: 500), () {
|
||||||
|
if (isMainWindow) {
|
||||||
|
windowManager.isMaximized().then((maximized) {
|
||||||
|
if (stateGlobal.isMaximized.value != maximized) {
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback(
|
||||||
|
(_) => setState(() => stateGlobal.setMaximized(maximized)));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
final wc = WindowController.fromWindowId(kWindowId!);
|
||||||
|
wc.isMaximized().then((maximized) {
|
||||||
|
debugPrint("isMaximized $maximized");
|
||||||
|
if (stateGlobal.isMaximized.value != maximized) {
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback(
|
||||||
|
(_) => setState(() => stateGlobal.setMaximized(maximized)));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
DesktopMultiWindow.removeListener(this);
|
||||||
|
windowManager.removeListener(this);
|
||||||
|
_macOSCheckRestoreTimer?.cancel();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _setMaximized(bool maximize) {
|
||||||
|
stateGlobal.setMaximized(maximize);
|
||||||
|
_saveFrameDebounce.call(_saveFrame);
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onWindowFocus() {
|
||||||
|
stateGlobal.isFocused.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onWindowBlur() {
|
||||||
|
stateGlobal.isFocused.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onWindowMinimize() {
|
||||||
|
stateGlobal.setMinimized(true);
|
||||||
|
stateGlobal.setMaximized(false);
|
||||||
|
super.onWindowMinimize();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onWindowMaximize() {
|
||||||
|
stateGlobal.setMinimized(false);
|
||||||
|
_setMaximized(true);
|
||||||
|
super.onWindowMaximize();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onWindowUnmaximize() {
|
||||||
|
stateGlobal.setMinimized(false);
|
||||||
|
_setMaximized(false);
|
||||||
|
super.onWindowUnmaximize();
|
||||||
|
}
|
||||||
|
|
||||||
|
_saveFrame() async {
|
||||||
|
if (tabType == DesktopTabType.main) {
|
||||||
|
await saveWindowPosition(WindowType.Main);
|
||||||
|
} else if (kWindowType != null && kWindowId != null) {
|
||||||
|
await saveWindowPosition(kWindowType!, windowId: kWindowId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onWindowMoved() {
|
||||||
|
_saveFrameDebounce.call(_saveFrame);
|
||||||
|
super.onWindowMoved();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onWindowResized() {
|
||||||
|
_saveFrameDebounce.call(_saveFrame);
|
||||||
|
super.onWindowMoved();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onWindowClose() async {
|
||||||
|
mainWindowClose() async => await windowManager.hide();
|
||||||
|
notMainWindowClose(WindowController windowController) async {
|
||||||
|
if (controller.length != 0) {
|
||||||
|
debugPrint("close not empty multiwindow from taskbar");
|
||||||
|
if (isWindows) {
|
||||||
|
await windowController.show();
|
||||||
|
await windowController.focus();
|
||||||
|
final res = await onWindowCloseButton?.call() ?? true;
|
||||||
|
if (!res) return;
|
||||||
|
}
|
||||||
|
controller.clear();
|
||||||
|
}
|
||||||
|
await windowController.hide();
|
||||||
|
await rustDeskWinManager
|
||||||
|
.call(WindowType.Main, kWindowEventHide, {"id": kWindowId!});
|
||||||
|
}
|
||||||
|
|
||||||
|
macOSWindowClose(
|
||||||
|
Future<bool> Function() checkFullscreen,
|
||||||
|
Future<void> Function() closeFunc,
|
||||||
|
) async {
|
||||||
|
_macOSCheckRestoreCounter = 0;
|
||||||
|
_macOSCheckRestoreTimer =
|
||||||
|
Timer.periodic(Duration(milliseconds: 30), (timer) async {
|
||||||
|
_macOSCheckRestoreCounter++;
|
||||||
|
if (!await checkFullscreen() || _macOSCheckRestoreCounter >= 30) {
|
||||||
|
_macOSCheckRestoreTimer?.cancel();
|
||||||
|
_macOSCheckRestoreTimer = null;
|
||||||
|
Timer(Duration(milliseconds: 700), () async => await closeFunc());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// hide window on close
|
||||||
|
if (isMainWindow) {
|
||||||
|
if (rustDeskWinManager.getActiveWindows().contains(kMainWindowId)) {
|
||||||
|
await rustDeskWinManager.unregisterActiveWindow(kMainWindowId);
|
||||||
|
}
|
||||||
|
// macOS specific workaround, the window is not hiding when in fullscreen.
|
||||||
|
if (isMacOS && await windowManager.isFullScreen()) {
|
||||||
|
await windowManager.setFullScreen(false);
|
||||||
|
await macOSWindowClose(
|
||||||
|
() async => await windowManager.isFullScreen(),
|
||||||
|
mainWindowClose,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
await mainWindowClose();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// it's safe to hide the subwindow
|
||||||
|
final controller = WindowController.fromWindowId(kWindowId!);
|
||||||
|
if (isMacOS) {
|
||||||
|
// onWindowClose() maybe called multiple times because of loopCloseWindow() in remote_tab_page.dart.
|
||||||
|
// use ??= to make sure the value is set on first call.
|
||||||
|
|
||||||
|
if (await onWindowCloseButton?.call() ?? true) {
|
||||||
|
if (await controller.isFullScreen()) {
|
||||||
|
await controller.setFullscreen(false);
|
||||||
|
stateGlobal.setFullscreen(false, procWnd: false);
|
||||||
|
await macOSWindowClose(
|
||||||
|
() async => await controller.isFullScreen(),
|
||||||
|
() async => await notMainWindowClose(controller),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
await notMainWindowClose(controller);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
await notMainWindowClose(controller);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
super.onWindowClose();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Column(children: [
|
return Column(children: [
|
||||||
@ -468,7 +677,6 @@ class DesktopTab extends StatelessWidget {
|
|||||||
// hide simulated action buttons when we in compatible ui mode, because of reusing system title bar.
|
// hide simulated action buttons when we in compatible ui mode, because of reusing system title bar.
|
||||||
WindowActionPanel(
|
WindowActionPanel(
|
||||||
isMainWindow: isMainWindow,
|
isMainWindow: isMainWindow,
|
||||||
tabType: tabType,
|
|
||||||
state: state,
|
state: state,
|
||||||
tabController: controller,
|
tabController: controller,
|
||||||
invisibleTabKeys: invisibleTabKeys,
|
invisibleTabKeys: invisibleTabKeys,
|
||||||
@ -486,7 +694,6 @@ class DesktopTab extends StatelessWidget {
|
|||||||
|
|
||||||
class WindowActionPanel extends StatefulWidget {
|
class WindowActionPanel extends StatefulWidget {
|
||||||
final bool isMainWindow;
|
final bool isMainWindow;
|
||||||
final DesktopTabType tabType;
|
|
||||||
final Rx<DesktopTabState> state;
|
final Rx<DesktopTabState> state;
|
||||||
final DesktopTabController tabController;
|
final DesktopTabController tabController;
|
||||||
|
|
||||||
@ -502,7 +709,6 @@ class WindowActionPanel extends StatefulWidget {
|
|||||||
const WindowActionPanel(
|
const WindowActionPanel(
|
||||||
{Key? key,
|
{Key? key,
|
||||||
required this.isMainWindow,
|
required this.isMainWindow,
|
||||||
required this.tabType,
|
|
||||||
required this.state,
|
required this.state,
|
||||||
required this.tabController,
|
required this.tabController,
|
||||||
required this.invisibleTabKeys,
|
required this.invisibleTabKeys,
|
||||||
@ -520,180 +726,17 @@ class WindowActionPanel extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class WindowActionPanelState extends State<WindowActionPanel>
|
class WindowActionPanelState extends State<WindowActionPanel> {
|
||||||
with MultiWindowListener, WindowListener {
|
|
||||||
final _saveFrameDebounce = Debouncer(delay: Duration(seconds: 1));
|
|
||||||
Timer? _macOSCheckRestoreTimer;
|
|
||||||
int _macOSCheckRestoreCounter = 0;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
DesktopMultiWindow.addListener(this);
|
|
||||||
windowManager.addListener(this);
|
|
||||||
|
|
||||||
Future.delayed(Duration(milliseconds: 500), () {
|
|
||||||
if (widget.isMainWindow) {
|
|
||||||
windowManager.isMaximized().then((maximized) {
|
|
||||||
if (stateGlobal.isMaximized.value != maximized) {
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback(
|
|
||||||
(_) => setState(() => stateGlobal.setMaximized(maximized)));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
final wc = WindowController.fromWindowId(kWindowId!);
|
|
||||||
wc.isMaximized().then((maximized) {
|
|
||||||
debugPrint("isMaximized $maximized");
|
|
||||||
if (stateGlobal.isMaximized.value != maximized) {
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback(
|
|
||||||
(_) => setState(() => stateGlobal.setMaximized(maximized)));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
DesktopMultiWindow.removeListener(this);
|
|
||||||
windowManager.removeListener(this);
|
|
||||||
_macOSCheckRestoreTimer?.cancel();
|
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _setMaximized(bool maximize) {
|
|
||||||
stateGlobal.setMaximized(maximize);
|
|
||||||
_saveFrameDebounce.call(_saveFrame);
|
|
||||||
setState(() {});
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void onWindowFocus() {
|
|
||||||
stateGlobal.isFocused.value = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void onWindowBlur() {
|
|
||||||
stateGlobal.isFocused.value = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void onWindowMinimize() {
|
|
||||||
stateGlobal.setMinimized(true);
|
|
||||||
stateGlobal.setMaximized(false);
|
|
||||||
super.onWindowMinimize();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void onWindowMaximize() {
|
|
||||||
stateGlobal.setMinimized(false);
|
|
||||||
_setMaximized(true);
|
|
||||||
super.onWindowMaximize();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void onWindowUnmaximize() {
|
|
||||||
stateGlobal.setMinimized(false);
|
|
||||||
_setMaximized(false);
|
|
||||||
super.onWindowUnmaximize();
|
|
||||||
}
|
|
||||||
|
|
||||||
_saveFrame() async {
|
|
||||||
if (widget.tabType == DesktopTabType.main) {
|
|
||||||
await saveWindowPosition(WindowType.Main);
|
|
||||||
} else if (kWindowType != null && kWindowId != null) {
|
|
||||||
await saveWindowPosition(kWindowType!, windowId: kWindowId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void onWindowMoved() {
|
|
||||||
_saveFrameDebounce.call(_saveFrame);
|
|
||||||
super.onWindowMoved();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void onWindowResized() {
|
|
||||||
_saveFrameDebounce.call(_saveFrame);
|
|
||||||
super.onWindowMoved();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void onWindowClose() async {
|
|
||||||
mainWindowClose() async => await windowManager.hide();
|
|
||||||
notMainWindowClose(WindowController controller) async {
|
|
||||||
if (widget.tabController.length != 0) {
|
|
||||||
debugPrint("close not empty multiwindow from taskbar");
|
|
||||||
if (isWindows) {
|
|
||||||
await controller.show();
|
|
||||||
await controller.focus();
|
|
||||||
final res = await widget.onClose?.call() ?? true;
|
|
||||||
if (!res) return;
|
|
||||||
}
|
|
||||||
widget.tabController.clear();
|
|
||||||
}
|
|
||||||
await controller.hide();
|
|
||||||
await rustDeskWinManager
|
|
||||||
.call(WindowType.Main, kWindowEventHide, {"id": kWindowId!});
|
|
||||||
}
|
|
||||||
|
|
||||||
macOSWindowClose(
|
|
||||||
Future<bool> Function() checkFullscreen,
|
|
||||||
Future<void> Function() closeFunc,
|
|
||||||
) async {
|
|
||||||
_macOSCheckRestoreCounter = 0;
|
|
||||||
_macOSCheckRestoreTimer =
|
|
||||||
Timer.periodic(Duration(milliseconds: 30), (timer) async {
|
|
||||||
_macOSCheckRestoreCounter++;
|
|
||||||
if (!await checkFullscreen() || _macOSCheckRestoreCounter >= 30) {
|
|
||||||
_macOSCheckRestoreTimer?.cancel();
|
|
||||||
_macOSCheckRestoreTimer = null;
|
|
||||||
Timer(Duration(milliseconds: 700), () async => await closeFunc());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// hide window on close
|
|
||||||
if (widget.isMainWindow) {
|
|
||||||
if (rustDeskWinManager.getActiveWindows().contains(kMainWindowId)) {
|
|
||||||
await rustDeskWinManager.unregisterActiveWindow(kMainWindowId);
|
|
||||||
}
|
|
||||||
// macOS specific workaround, the window is not hiding when in fullscreen.
|
|
||||||
if (isMacOS && await windowManager.isFullScreen()) {
|
|
||||||
await windowManager.setFullScreen(false);
|
|
||||||
await macOSWindowClose(
|
|
||||||
() async => await windowManager.isFullScreen(),
|
|
||||||
mainWindowClose,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
await mainWindowClose();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// it's safe to hide the subwindow
|
|
||||||
final controller = WindowController.fromWindowId(kWindowId!);
|
|
||||||
if (isMacOS) {
|
|
||||||
// onWindowClose() maybe called multiple times because of loopCloseWindow() in remote_tab_page.dart.
|
|
||||||
// use ??= to make sure the value is set on first call.
|
|
||||||
|
|
||||||
if (await widget.onClose?.call() ?? true) {
|
|
||||||
if (await controller.isFullScreen()) {
|
|
||||||
await controller.setFullscreen(false);
|
|
||||||
stateGlobal.setFullscreen(false, procWnd: false);
|
|
||||||
await macOSWindowClose(
|
|
||||||
() async => await controller.isFullScreen(),
|
|
||||||
() async => await notMainWindowClose(controller),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
await notMainWindowClose(controller);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
await notMainWindowClose(controller);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
super.onWindowClose();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool showTabDowndown() {
|
bool showTabDowndown() {
|
||||||
return widget.tabController.state.value.tabs.length > 1 &&
|
return widget.tabController.state.value.tabs.length > 1 &&
|
||||||
(widget.tabController.tabType == DesktopTabType.remoteScreen ||
|
(widget.tabController.tabType == DesktopTabType.remoteScreen ||
|
||||||
|
Loading…
x
Reference in New Issue
Block a user