Skip to content

Commit 5e16a0b

Browse files
authored
Merge pull request unoplatform#21758 from ramezgerges/win32_maximized_initial_frame
fix(win32): generate an initial frame with the correct size when starting up as maximized
2 parents 35976a0 + 48aec2e commit 5e16a0b

File tree

2 files changed

+28
-17
lines changed

2 files changed

+28
-17
lines changed

src/Uno.UI.Runtime.Skia.Win32.Support/NativeMethods.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ WM_NCHITTEST
7878
WM_NCMOUSEMOVE
7979
WM_NCLBUTTONDOWN
8080
WM_NCLBUTTONUP
81+
WM_NCPAINT
8182
WM_NCPOINTERUPDATE
8283
WM_NCPOINTERDOWN
8384
WM_NCPOINTERUP

src/Uno.UI.Runtime.Skia.Win32/UI/Xaml/Window/Win32WindowWrapper.cs

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.IO;
77
using System.Runtime.CompilerServices;
88
using System.Runtime.InteropServices;
9+
using System.Threading;
910
using Microsoft.UI.Input;
1011
using Microsoft.UI.Windowing;
1112
using Microsoft.UI.Xaml;
@@ -29,6 +30,7 @@
2930
using Windows.Win32.Graphics.Gdi;
3031
using Windows.Win32.UI.HiDpi;
3132
using Windows.Win32.UI.WindowsAndMessaging;
33+
using Uno.UI.Dispatching;
3234
using Point = System.Drawing.Point;
3335

3436
namespace Uno.UI.Runtime.Skia.Win32;
@@ -208,6 +210,15 @@ private unsafe LRESULT WndProcInner(HWND hwnd, uint msg, WPARAM wParam, LPARAM l
208210

209211
switch (msg)
210212
{
213+
case PInvoke.WM_NCPAINT:
214+
// see the comment in the WM_ERASEBKGND handler
215+
if (WasShown && _beforeFirstEraseBkgnd && (_pendingState is OverlappedPresenterState.Maximized || Window?.AppWindow.Presenter is FullScreenPresenter))
216+
{
217+
OnWindowSizeOrLocationChanged(); // In case the window size has changed but WM_SIZE is not fired yet. This happens specifically if the window is starting maximized using _pendingState
218+
XamlRoot!.VisualTree.RootElement.UpdateLayout(); // relayout in response to the new window size
219+
(XamlRoot?.Content?.Visual.CompositionTarget as CompositionTarget)?.OnRenderFrameOpportunity(); // force an early render
220+
}
221+
break;
211222
case PInvoke.WM_ACTIVATE:
212223
OnWmActivate(wParam);
213224
return new LRESULT(0);
@@ -253,11 +264,15 @@ private unsafe LRESULT WndProcInner(HWND hwnd, uint msg, WPARAM wParam, LPARAM l
253264
if (_beforeFirstEraseBkgnd)
254265
{
255266
// Without drawing on the first WM_ERASEBKGND, we get an initial white frame
256-
// Note that we don't call OnRenderFrameOpportunity here, but before showing
257-
// the window in ShowCore. The problem is that any minor delay will cause
258-
// a split-second white flash, so we're keeping the "time to blit" to a
259-
// minimum by "rendering" before the window is shown and only drawing when
260-
// receiving the first WM_ERASEBKGND
267+
// Note that we don't call OnRenderFrameOpportunity here, but in ShowCore right before
268+
// showing the window or in WM_NCPAINT which is the first message received after showing
269+
// the window in ShowCore and after a possible window size change because the window was
270+
// shown in a maximized/fullscreen state.
271+
// The problem is that any minor delay will cause a split-second white flash, so we're keeping
272+
// the "time to blit" to a minimum by "rendering" asap and only "drawing" when
273+
// receiving the first WM_ERASEBKGND. Even then, there is still a race between our drawing a frame
274+
// and the next screen refresh and while in most cases we are able to win the race and not get this
275+
// split second of "whiteness", it's not a guarantee, especially on a slower device.
261276
_beforeFirstEraseBkgnd = false;
262277
// The render timer might already be running. This is fine. The CompositionTarget
263278
// contract allows calling OnNativePlatformFrameRequested multiple times.
@@ -466,20 +481,14 @@ protected internal override void Activate()
466481

467482
protected override void ShowCore()
468483
{
469-
// see the comment in WndProc's WM_ERASEBKGND handling
470-
if (_beforeFirstEraseBkgnd)
471-
{
472-
(XamlRoot?.Content?.Visual.CompositionTarget as CompositionTarget)?.OnRenderFrameOpportunity();
473-
}
474-
475484
if (Window?.AppWindow.Presenter is FullScreenPresenter)
476485
{
477486
// The window takes a split second to be rerendered with the fullscreen window size but
478487
// no fix has been found for this yet.
479488
SetWindowStyle(WINDOW_STYLE.WS_DLGFRAME, false);
480489
_ = PInvoke.ShowWindow(_hwnd, SHOW_WINDOW_CMD.SW_MAXIMIZE);
481490
}
482-
else if (Window?.AppWindow.Presenter is OverlappedPresenter overlappedPresenter)
491+
else if (Window?.AppWindow.Presenter is OverlappedPresenter)
483492
{
484493
switch (_pendingState)
485494
{
@@ -489,17 +498,18 @@ protected override void ShowCore()
489498
case OverlappedPresenterState.Minimized:
490499
_ = PInvoke.ShowWindow(_hwnd, SHOW_WINDOW_CMD.SW_MINIMIZE);
491500
break;
492-
case OverlappedPresenterState.Restored:
493-
_ = PInvoke.ShowWindow(_hwnd, SHOW_WINDOW_CMD.SW_RESTORE);
494-
break;
495501
default:
496-
_ = PInvoke.ShowWindow(_hwnd, SHOW_WINDOW_CMD.SW_SHOWDEFAULT);
502+
// see the comment in the WM_ERASEBKGND handler
503+
OnWindowSizeOrLocationChanged(); // In case the window size has changed but WM_SIZE is not fired yet. This happens specifically if the window is starting maximized using _pendingState
504+
XamlRoot!.VisualTree.RootElement.UpdateLayout(); // relayout in response to the new window size
505+
(XamlRoot?.Content?.Visual.CompositionTarget as CompositionTarget)?.OnRenderFrameOpportunity(); // force an early render
506+
PInvoke.ShowWindow(_hwnd, SHOW_WINDOW_CMD.SW_SHOWDEFAULT);
497507
break;
498508
}
499509
}
500510
else
501511
{
502-
PInvoke.ShowWindow(_hwnd, SHOW_WINDOW_CMD.SW_SHOWDEFAULT);
512+
throw new InvalidOperationException("Unsupported Window Presenter.");
503513
}
504514
}
505515

0 commit comments

Comments
 (0)