From cc51afe99e6ad3da47347805e47216c8a6eeca08 Mon Sep 17 00:00:00 2001 From: Yoyo Date: Tue, 16 Jun 2026 10:57:30 +0800 Subject: [PATCH] Fix Win32 system menu behavior in extended client area and fullscreen --- .../Avalonia.Win32/WindowImpl.AppWndProc.cs | 7 ++++-- .../WindowImpl.CustomCaptionProc.cs | 25 +++++++++++-------- src/Windows/Avalonia.Win32/WindowImpl.cs | 6 +++++ 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs b/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs index 6cdc3198f4..cb19db2eeb 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs @@ -235,14 +235,14 @@ namespace Avalonia.Win32 break; case WindowsMessage.WM_SYSCOMMAND: - if ((SysCommands)wParam == SysCommands.SC_KEYMENU && + if (GetSysCommand(wParam) == SysCommands.SC_KEYMENU && ToInt32(lParam) == (int)VirtualKeyStates.VK_SPACE) // Open system menu. { break; } // Disable system handling of Alt/F10 menu keys. - if ((SysCommands)wParam == SysCommands.SC_KEYMENU && HighWord(ToInt32(lParam)) <= 0) + if (GetSysCommand(wParam) == SysCommands.SC_KEYMENU && HighWord(ToInt32(lParam)) <= 0) return IntPtr.Zero; break; @@ -1424,6 +1424,9 @@ namespace Avalonia.Win32 private static int HighWord(int param) => param >> 16; + private static SysCommands GetSysCommand(IntPtr wParam) + => (SysCommands)(ToInt32(wParam) & 0xfff0); + private Point DipFromLParam(IntPtr lParam) { return new Point((short)(ToInt32(lParam) & 0xffff), (short)(ToInt32(lParam) >> 16)) / RenderScaling; diff --git a/src/Windows/Avalonia.Win32/WindowImpl.CustomCaptionProc.cs b/src/Windows/Avalonia.Win32/WindowImpl.CustomCaptionProc.cs index 9aec6bc304..8993e6e2bb 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.CustomCaptionProc.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.CustomCaptionProc.cs @@ -123,7 +123,7 @@ namespace Avalonia.Win32 break; case WindowsMessage.WM_NCRBUTTONUP - when (HitTestValues)ToInt32(wParam) == HitTestValues.HTCAPTION: + when (HitTestValues)ToInt32(wParam) == HitTestValues.HTCAPTION && IsCaptionHitForSystemMenu(hWnd, lParam): ShowSystemMenu(PointFromLParam(lParam)); callDwp = false; return IntPtr.Zero; @@ -301,19 +301,17 @@ namespace Avalonia.Win32 private bool IsCaptionHitForSystemMenu(IntPtr hWnd, IntPtr lParam) { - if (!_isClientAreaExtended || WindowState == WindowState.FullScreen) + if (!_isClientAreaExtended) { return false; } - if (HitTestNCA(hWnd, IntPtr.Zero, lParam) != HitTestValues.HTCAPTION) + if (HitTestNCA(hWnd, IntPtr.Zero, lParam) is not HitTestValues.HTCAPTION and not HitTestValues.HTNOWHERE) { return false; } - return HitTestVisual(lParam) - is HitTestValues.HTNOWHERE - or HitTestValues.HTCAPTION; + return HitTestVisual(lParam) is HitTestValues.HTCAPTION or HitTestValues.HTNOWHERE; } private void ShowSystemMenu(PixelPoint screenPoint) @@ -324,7 +322,7 @@ namespace Avalonia.Win32 return; } - // SetForegroundWindow(_hwnd); + SetForegroundWindow(_hwnd); SendMessage(_hwnd, (int)WindowsMessage.WM_INITMENU, menu, IntPtr.Zero); UpdateSystemMenu(menu); @@ -341,7 +339,14 @@ namespace Avalonia.Win32 if (command != 0) { - SendMessage(_hwnd, (int)WindowsMessage.WM_SYSCOMMAND, (IntPtr)command, MakeLParam(screenPoint)); + if ((SysCommands)command == SysCommands.SC_RESTORE && WindowState == WindowState.FullScreen) + { + WindowState = WindowState.Normal; + } + else + { + SendMessage(_hwnd, (int)WindowsMessage.WM_SYSCOMMAND, (IntPtr)command, MakeLParam(screenPoint)); + } } } @@ -353,8 +358,8 @@ namespace Avalonia.Win32 var isFullScreen = state == WindowState.FullScreen; var isNormal = state == WindowState.Normal; - SetSystemMenuItemEnabled(menu, SysCommands.SC_RESTORE, isMinimized || isMaximized); - SetSystemMenuItemEnabled(menu, SysCommands.SC_MOVE, isNormal); + SetSystemMenuItemEnabled(menu, SysCommands.SC_RESTORE, isMinimized || isMaximized || isFullScreen); + SetSystemMenuItemEnabled(menu, SysCommands.SC_MOVE, isNormal || isFullScreen); SetSystemMenuItemEnabled(menu, SysCommands.SC_SIZE, isNormal && _windowProperties.IsResizable); SetSystemMenuItemEnabled(menu, SysCommands.SC_MINIMIZE, !isMinimized && !isFullScreen && _windowProperties.IsMinimizable); SetSystemMenuItemEnabled(menu, SysCommands.SC_MAXIMIZE, !isMaximized && !isFullScreen && _windowProperties.IsMaximizable); diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 4801ebdb2d..3fcff88b8e 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -1102,6 +1102,12 @@ namespace Avalonia.Win32 _savedWindowInfo.Style = current; _savedWindowInfo.ExStyle = currentEx; + if (current.HasAllFlags(WindowStyles.WS_SYSMENU)) + { + // Create the system menu copy before fullscreen removes WS_SYSMENU. + GetSystemMenu(_hwnd, false); + } + // Set new window style and size. SetStyle(current & ~WindowStyles.WS_OVERLAPPEDWINDOW, false); SetExtendedStyle(currentEx & ~(WindowStyles.WS_EX_DLGMODALFRAME | WindowStyles.WS_EX_WINDOWEDGE | WindowStyles.WS_EX_CLIENTEDGE | WindowStyles.WS_EX_STATICEDGE), false);