From 5eee1e04e5d9e009d20139277bedefa735a52a17 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 29 Apr 2020 22:35:37 -0300 Subject: [PATCH] win32 implementation for fullscreen windowstate. --- src/Windows/Avalonia.Win32/WindowImpl.cs | 118 ++++++++++++++++++++++- 1 file changed, 117 insertions(+), 1 deletion(-) diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index e193c72ef7..5f88fc3f0c 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -37,10 +37,21 @@ namespace Avalonia.Win32 { WindowEdge.West, HitTestValues.HTLEFT } }; + private struct SavedWindowInfo + { + public WindowStyles Style { get; set; } + public WindowStyles ExStyle { get; set; } + public RECT WindowRect { get; set; } + }; + + private SavedWindowInfo _savedWindowInfo; + private bool _fullScreen; + #if USE_MANAGED_DRAG private readonly ManagedWindowResizeDragHelper _managedDrag; #endif + private readonly List _disabledBy; private readonly TouchDevice _touchDevice; private readonly MouseDevice _mouseDevice; @@ -82,7 +93,9 @@ namespace Avalonia.Win32 _windowProperties = new WindowProperties { - ShowInTaskbar = false, IsResizable = true, Decorations = SystemDecorations.Full + ShowInTaskbar = false, + IsResizable = true, + Decorations = SystemDecorations.Full }; _rendererLock = new ManagedDeferredRendererLock(); @@ -205,6 +218,90 @@ namespace Avalonia.Win32 } } + private void MarkFullscreen(bool fullscreen) + { + //if (!task_bar_list_) + //{ + // HRESULT hr = + + + // ::CoCreateInstance(CLSID_TaskbarList, nullptr, CLSCTX_INPROC_SERVER, + // IID_PPV_ARGS(&task_bar_list_)); + // if (SUCCEEDED(hr) && FAILED(task_bar_list_->HrInit())) + // task_bar_list_ = nullptr; + //} + + //// As per MSDN marking the window as fullscreen should ensure that the + //// taskbar is moved to the bottom of the Z-order when the fullscreen window + //// is activated. If the window is not fullscreen, the Shell falls back to + //// heuristics to determine how the window should be treated, which means + //// that it could still consider the window as fullscreen. :( + //if (task_bar_list_) + // task_bar_list_->MarkFullscreenWindow(hwnd_, !!fullscreen); + } + + private void SetFullScreen(bool fullscreen) + { + // Ported from https://github.com/chromium/chromium/blob/master/ui/views/win/fullscreen_handler.cc + //std::unique_ptr visibility; + + // With Aero enabled disabling the visibility causes the window to disappear + // for several frames, which looks worse than doing other updates + // non-atomically. + //if (!ui::win::IsAeroGlassEnabled()) + // visibility = std::make_unique(hwnd_); + + // Save current window state if not already fullscreen. + if (!_fullScreen) + { + _savedWindowInfo.Style = (WindowStyles)GetWindowLong(_hwnd, (int)WindowLongParam.GWL_STYLE); + _savedWindowInfo.ExStyle = (WindowStyles)GetWindowLong(_hwnd, (int)WindowLongParam.GWL_EXSTYLE); + GetWindowRect(_hwnd, out var windowRect); + _savedWindowInfo.WindowRect = windowRect; + } + + _fullScreen = fullscreen; + + if (_fullScreen) + { + // Set new window style and size. + SetWindowLong(_hwnd, (int)WindowLongParam.GWL_STYLE, + (uint)(_savedWindowInfo.Style & ~(WindowStyles.WS_CAPTION | WindowStyles.WS_THICKFRAME))); + SetWindowLong( + _hwnd, (int)WindowLongParam.GWL_EXSTYLE, + (uint)(_savedWindowInfo.ExStyle & ~(WindowStyles.WS_EX_DLGMODALFRAME | WindowStyles.WS_EX_WINDOWEDGE | + WindowStyles.WS_EX_CLIENTEDGE | WindowStyles.WS_EX_STATICEDGE))); + + // On expand, if we're given a window_rect, grow to it, otherwise do + // not resize. + MONITORINFO monitor_info = MONITORINFO.Create(); + GetMonitorInfo(MonitorFromWindow(_hwnd, MONITOR.MONITOR_DEFAULTTONEAREST), ref monitor_info); + + var window_rect = monitor_info.rcMonitor.ToPixelRect(); + + SetWindowPos(_hwnd, IntPtr.Zero, window_rect.X, window_rect.Y, + window_rect.Width, window_rect.Height, + SetWindowPosFlags.SWP_NOZORDER | SetWindowPosFlags.SWP_NOACTIVATE | SetWindowPosFlags.SWP_FRAMECHANGED); + } + else + { + // Reset original window style and size. The multiple window size/moves + // here are ugly, but if SetWindowPos() doesn't redraw, the taskbar won't be + // repainted. Better-looking methods welcome. + SetWindowLong(_hwnd, (int)WindowLongParam.GWL_STYLE, (uint)_savedWindowInfo.Style); + SetWindowLong(_hwnd, (int)WindowLongParam.GWL_EXSTYLE, (uint)_savedWindowInfo.ExStyle); + + // On restore, resize to the previous saved rect size. + var new_rect = _savedWindowInfo.WindowRect.ToPixelRect(); + + SetWindowPos(_hwnd, IntPtr.Zero, new_rect.X, new_rect.Y, new_rect.Width, + new_rect.Height, + SetWindowPosFlags.SWP_NOZORDER | SetWindowPosFlags.SWP_NOACTIVATE | SetWindowPosFlags.SWP_FRAMECHANGED); + } + + MarkFullscreen(fullscreen); + } + public IEnumerable Surfaces => new object[] { Handle, _gl, _framebuffer }; public PixelPoint Position @@ -545,16 +642,35 @@ namespace Avalonia.Win32 switch (state) { case WindowState.Minimized: + if (_fullScreen) + { + SetFullScreen(false); + } + command = ShowWindowCommand.Minimize; break; case WindowState.Maximized: + if (_fullScreen) + { + SetFullScreen(false); + } + command = ShowWindowCommand.Maximize; break; case WindowState.Normal: + if (_fullScreen) + { + SetFullScreen(false); + } + command = ShowWindowCommand.Restore; break; + case WindowState.FullScreen: + SetFullScreen(true); + return; + default: throw new ArgumentException("Invalid WindowState."); }