From b6b1dfd6f8871a78ede3b36cb64f1a894d181f98 Mon Sep 17 00:00:00 2001 From: Gustl22 Date: Sat, 26 Jul 2025 08:51:50 +0200 Subject: [PATCH 1/2] fix: Send Fullscreen Event on Windows --- .../integration_test/window_manager_test.dart | 236 +++++++++++------- .../windows/window_manager_plugin.cpp | 2 +- 2 files changed, 152 insertions(+), 86 deletions(-) diff --git a/packages/window_manager/example/integration_test/window_manager_test.dart b/packages/window_manager/example/integration_test/window_manager_test.dart index 5901027e..2b2f7334 100644 --- a/packages/window_manager/example/integration_test/window_manager_test.dart +++ b/packages/window_manager/example/integration_test/window_manager_test.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:io'; import 'dart:ui'; @@ -19,110 +20,175 @@ Future main() async { }, ); - testWidgets('getBounds', (tester) async { - expect( - await windowManager.getBounds(), - isA().having((r) => r.size, 'size', const Size(640, 480)), + group('Getters', () { + testWidgets('getBounds', (tester) async { + expect( + await windowManager.getBounds(), + isA().having((r) => r.size, 'size', const Size(640, 480)), + ); + }); + + testWidgets( + 'isAlwaysOnBottom', + (tester) async { + expect(await windowManager.isAlwaysOnBottom(), isFalse); + }, + skip: Platform.isMacOS || Platform.isWindows, ); - }); - - testWidgets( - 'isAlwaysOnBottom', - (tester) async { - expect(await windowManager.isAlwaysOnBottom(), isFalse); - }, - skip: Platform.isMacOS || Platform.isWindows, - ); - testWidgets('isAlwaysOnTop', (tester) async { - expect(await windowManager.isAlwaysOnTop(), isFalse); - }); + testWidgets('isAlwaysOnTop', (tester) async { + expect(await windowManager.isAlwaysOnTop(), isFalse); + }); - testWidgets('isClosable', (tester) async { - expect(await windowManager.isClosable(), isTrue); - }); + testWidgets('isClosable', (tester) async { + expect(await windowManager.isClosable(), isTrue); + }); - testWidgets('isFocused', (tester) async { - expect(await windowManager.isFocused(), isTrue); - }); + testWidgets('isFocused', (tester) async { + expect(await windowManager.isFocused(), isTrue); + }); - testWidgets('isFullScreen', (tester) async { - expect(await windowManager.isFullScreen(), isFalse); - }); + testWidgets('isFullScreen', (tester) async { + expect(await windowManager.isFullScreen(), isFalse); + }); - testWidgets( - 'hasShadow', - (tester) async { - expect(await windowManager.hasShadow(), isTrue); - }, - skip: Platform.isLinux, - ); + testWidgets( + 'hasShadow', + (tester) async { + expect(await windowManager.hasShadow(), isTrue); + }, + skip: Platform.isLinux, + ); - testWidgets('isMaximizable', (tester) async { - expect(await windowManager.isMaximizable(), isTrue); - }); + testWidgets('isMaximizable', (tester) async { + expect(await windowManager.isMaximizable(), isTrue); + }); - testWidgets('isMaximized', (tester) async { - expect(await windowManager.isMaximized(), isFalse); - }); + testWidgets('isMaximized', (tester) async { + expect(await windowManager.isMaximized(), isFalse); + }); - testWidgets( - 'isMinimizable', - (tester) async { - expect(await windowManager.isMinimizable(), isTrue); - }, - skip: Platform.isMacOS, - ); + testWidgets( + 'isMinimizable', + (tester) async { + expect(await windowManager.isMinimizable(), isTrue); + }, + skip: Platform.isMacOS, + ); - testWidgets('isMinimized', (tester) async { - expect(await windowManager.isMinimized(), isFalse); - }); + testWidgets('isMinimized', (tester) async { + expect(await windowManager.isMinimized(), isFalse); + }); - testWidgets( - 'isMovable', - (tester) async { - expect(await windowManager.isMovable(), isTrue); - }, - skip: Platform.isLinux || Platform.isWindows, - ); + testWidgets( + 'isMovable', + (tester) async { + expect(await windowManager.isMovable(), isTrue); + }, + skip: Platform.isLinux || Platform.isWindows, + ); - testWidgets('getOpacity', (tester) async { - expect(await windowManager.getOpacity(), 1.0); - }); + testWidgets('getOpacity', (tester) async { + expect(await windowManager.getOpacity(), 1.0); + }); - testWidgets('getPosition', (tester) async { - expect(await windowManager.getPosition(), isA()); - }); + testWidgets('getPosition', (tester) async { + expect(await windowManager.getPosition(), isA()); + }); - testWidgets('isPreventClose', (tester) async { - expect(await windowManager.isPreventClose(), isFalse); - }); + testWidgets('isPreventClose', (tester) async { + expect(await windowManager.isPreventClose(), isFalse); + }); - testWidgets('isResizable', (tester) async { - expect(await windowManager.isResizable(), isTrue); - }); + testWidgets('isResizable', (tester) async { + expect(await windowManager.isResizable(), isTrue); + }); - testWidgets('getSize', (tester) async { - expect(await windowManager.getSize(), const Size(640, 480)); - }); + testWidgets('getSize', (tester) async { + expect(await windowManager.getSize(), const Size(640, 480)); + }); - testWidgets( - 'isSkipTaskbar', - (tester) async { - expect(await windowManager.isSkipTaskbar(), isFalse); - }, - skip: Platform.isWindows, - ); + testWidgets( + 'isSkipTaskbar', + (tester) async { + expect(await windowManager.isSkipTaskbar(), isFalse); + }, + skip: Platform.isWindows, + ); - testWidgets('getTitle', (tester) async { - expect(await windowManager.getTitle(), 'window_manager_test'); + testWidgets('getTitle', (tester) async { + expect(await windowManager.getTitle(), 'window_manager_test'); + }); + + testWidgets('getTitleBarHeight', (tester) async { + expect(await windowManager.getTitleBarHeight(), isNonNegative); + }); + + testWidgets('isVisible', (tester) async { + expect(await windowManager.isVisible(), isTrue); + }); + }); + + group('Setters and Listeners', () { + late StreamController streamController; + late WindowListener windowListener; + + setUp(() async { + streamController = StreamController(); + windowListener = WindowListenerImpl(streamController: streamController); + windowManager.addListener(windowListener); + }); + + tearDown(() async { + windowManager.removeListener(windowListener); + await streamController.close(); + }); + + testWidgets('minimize & restore', (tester) async { + await windowManager.minimize(); + await windowManager.restore(); + expect( + streamController.stream, + emitsInOrder([ + emitsThrough('minimize'), + emitsThrough('restore'), + ]), + ); + }); + + testWidgets('maximize & unmaximize', (tester) async { + await windowManager.maximize(); + await windowManager.unmaximize(); + expect( + streamController.stream, + emitsInOrder([ + emitsThrough('maximize'), + emitsThrough('unmaximize'), + ]), + ); + }); + + testWidgets('setFullscreen true & false', (tester) async { + await windowManager.setFullScreen(true); + await windowManager.setFullScreen(false); + expect( + streamController.stream, + emitsInOrder([ + emitsThrough('enter-full-screen'), + emitsThrough('leave-full-screen'), + ]), + ); + }); }); +} - testWidgets('getTitleBarHeight', (tester) async { - expect(await windowManager.getTitleBarHeight(), isNonNegative); - }); +class WindowListenerImpl with WindowListener { + WindowListenerImpl({required this.streamController}); - testWidgets('isVisible', (tester) async { - expect(await windowManager.isVisible(), isTrue); - }); + final StreamController streamController; + + @override + void onWindowEvent(String eventName) { + streamController.add(eventName); + } } diff --git a/packages/window_manager/windows/window_manager_plugin.cpp b/packages/window_manager/windows/window_manager_plugin.cpp index ba7720ae..0f871859 100644 --- a/packages/window_manager/windows/window_manager_plugin.cpp +++ b/packages/window_manager/windows/window_manager_plugin.cpp @@ -289,7 +289,7 @@ std::optional WindowManagerPlugin::HandleWindowProc(HWND hWnd, rect->bottom = bottom; } } else if (message == WM_SIZE) { - if (window_manager->IsFullScreen() && wParam == SIZE_MAXIMIZED && + if (window_manager->IsFullScreen() && window_manager->last_state != STATE_FULLSCREEN_ENTERED) { _EmitEvent("enter-full-screen"); window_manager->last_state = STATE_FULLSCREEN_ENTERED; From 5f7858139537d09d5591519bd944294aab02833d Mon Sep 17 00:00:00 2001 From: Gustl22 Date: Fri, 1 Aug 2025 09:36:42 +0200 Subject: [PATCH 2/2] windows: Further adaptations --- packages/window_manager/windows/window_manager.cpp | 1 + packages/window_manager/windows/window_manager_plugin.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/window_manager/windows/window_manager.cpp b/packages/window_manager/windows/window_manager.cpp index 1d19490f..017671f1 100644 --- a/packages/window_manager/windows/window_manager.cpp +++ b/packages/window_manager/windows/window_manager.cpp @@ -598,6 +598,7 @@ void WindowManager::SetFullScreen(const flutter::EncodableMap& args) { ::GetWindowPlacement(mainWindow, &placement); ::GetMonitorInfo( ::MonitorFromWindow(mainWindow, MONITOR_DEFAULTTONEAREST), &monitor); + // TODO: Check how frame can be restored after leaving fullscreen, when always enable frameless if (!g_maximized_before_fullscreen) { SetAsFrameless(); } diff --git a/packages/window_manager/windows/window_manager_plugin.cpp b/packages/window_manager/windows/window_manager_plugin.cpp index 0f871859..878ccd48 100644 --- a/packages/window_manager/windows/window_manager_plugin.cpp +++ b/packages/window_manager/windows/window_manager_plugin.cpp @@ -293,7 +293,7 @@ std::optional WindowManagerPlugin::HandleWindowProc(HWND hWnd, window_manager->last_state != STATE_FULLSCREEN_ENTERED) { _EmitEvent("enter-full-screen"); window_manager->last_state = STATE_FULLSCREEN_ENTERED; - } else if (!window_manager->IsFullScreen() && wParam == SIZE_RESTORED && + } else if (!window_manager->IsFullScreen() && window_manager->last_state == STATE_FULLSCREEN_ENTERED) { window_manager->ForceChildRefresh(); _EmitEvent("leave-full-screen");