From 7d80492a1268ec021bd66a012e67d62738378025 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 29 Apr 2020 22:25:11 -0300 Subject: [PATCH 01/47] Add Fullscreen to WindowState enum. --- src/Avalonia.Controls/WindowState.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Avalonia.Controls/WindowState.cs b/src/Avalonia.Controls/WindowState.cs index 4ed30e726e..777b52dc11 100644 --- a/src/Avalonia.Controls/WindowState.cs +++ b/src/Avalonia.Controls/WindowState.cs @@ -19,5 +19,10 @@ namespace Avalonia.Controls /// The window is maximized. /// Maximized, + + /// + /// The window is fullscreen. + /// + FullScreen, } } From 02fa37772871df8958dc89829fc4d78688a35afe Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 29 Apr 2020 22:26:08 -0300 Subject: [PATCH 02/47] add missing window style. --- src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs index cbfa1abfb7..5c364c4038 100644 --- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs +++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs @@ -460,6 +460,7 @@ namespace Avalonia.Win32.Interop WS_SIZEFRAME = 0x40000, WS_SYSMENU = 0x80000, WS_TABSTOP = 0x10000, + WS_THICKFRAME = 0x40000, WS_VISIBLE = 0x10000000, WS_VSCROLL = 0x200000, WS_EX_DLGMODALFRAME = 0x00000001, From dd776df3c7a52b38e5f8bfefecc36cbdf1d54e36 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 29 Apr 2020 22:27:03 -0300 Subject: [PATCH 03/47] add extension method to convert between RECT and PixelRect. --- src/Windows/Avalonia.Win32/Win32TypeExtensions.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 src/Windows/Avalonia.Win32/Win32TypeExtensions.cs diff --git a/src/Windows/Avalonia.Win32/Win32TypeExtensions.cs b/src/Windows/Avalonia.Win32/Win32TypeExtensions.cs new file mode 100644 index 0000000000..0e9daf3137 --- /dev/null +++ b/src/Windows/Avalonia.Win32/Win32TypeExtensions.cs @@ -0,0 +1,13 @@ +using static Avalonia.Win32.Interop.UnmanagedMethods; + +namespace Avalonia.Win32 +{ + public static class Win32TypeExtensions + { + internal static PixelRect ToPixelRect(this RECT rect) + { + return new PixelRect(rect.left, rect.top, rect.right - rect.left, + rect.bottom - rect.top); + } + } +} From 05a2a648712436f3b4c514938fd50323502e0698 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 29 Apr 2020 22:29:06 -0300 Subject: [PATCH 04/47] use extension method in screens api. --- src/Windows/Avalonia.Win32/ScreenImpl.cs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/Windows/Avalonia.Win32/ScreenImpl.cs b/src/Windows/Avalonia.Win32/ScreenImpl.cs index 963042b249..442794f0f0 100644 --- a/src/Windows/Avalonia.Win32/ScreenImpl.cs +++ b/src/Windows/Avalonia.Win32/ScreenImpl.cs @@ -8,7 +8,7 @@ namespace Avalonia.Win32 { public class ScreenImpl : IScreenImpl { - public int ScreenCount + public int ScreenCount { get => GetSystemMetrics(SystemMetric.SM_CMONITORS); } @@ -33,7 +33,7 @@ namespace Avalonia.Win32 var shcore = LoadLibrary("shcore.dll"); var method = GetProcAddress(shcore, nameof(GetDpiForMonitor)); if (method != IntPtr.Zero) - { + { GetDpiForMonitor(monitor, MONITOR_DPI_TYPE.MDT_EFFECTIVE_DPI, out var x, out _); dpi = (double)x; } @@ -51,11 +51,8 @@ namespace Avalonia.Win32 RECT bounds = monitorInfo.rcMonitor; RECT workingArea = monitorInfo.rcWork; - PixelRect avaloniaBounds = new PixelRect(bounds.left, bounds.top, bounds.right - bounds.left, - bounds.bottom - bounds.top); - PixelRect avaloniaWorkArea = - new PixelRect(workingArea.left, workingArea.top, workingArea.right - workingArea.left, - workingArea.bottom - workingArea.top); + PixelRect avaloniaBounds = bounds.ToPixelRect(); + PixelRect avaloniaWorkArea = workingArea.ToPixelRect(); screens[index] = new WinScreen(dpi / 96.0d, avaloniaBounds, avaloniaWorkArea, monitorInfo.dwFlags == 1, monitor); From 5eee1e04e5d9e009d20139277bedefa735a52a17 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 29 Apr 2020 22:35:37 -0300 Subject: [PATCH 05/47] 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."); } From 26ad865589f7b8ac83d0b34fc5e209e023a50302 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 30 Apr 2020 13:39:56 -0300 Subject: [PATCH 06/47] Add Guids for TaskBarList interop. --- src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs index 5c364c4038..e9f00a0c7f 100644 --- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs +++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs @@ -1643,6 +1643,8 @@ namespace Avalonia.Win32.Interop public static readonly Guid SaveFileDialog = Guid.Parse("C0B4E2F3-BA21-4773-8DBA-335EC946EB8B"); public static readonly Guid IFileDialog = Guid.Parse("42F85136-DB7E-439C-85F1-E4075D135FC8"); public static readonly Guid IShellItem = Guid.Parse("43826D1E-E718-42EE-BC55-A1E261C37BFE"); + public static readonly Guid TaskBarList = Guid.Parse("56FDF344-FD6D-11D0-958A-006097C9A090"); + public static readonly Guid ITaskBarList2 = Guid.Parse("ea1afb91-9e28-4b86-90e9-9e9f8a5eefaf"); } [ComImport(), Guid("42F85136-DB7E-439C-85F1-E4075D135FC8"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] From 1ce8f4f6bad28ac2e523e6b6a097bd040cbbbb6c Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 30 Apr 2020 13:40:45 -0300 Subject: [PATCH 07/47] use helper methods GetStyle / SetStyle. --- src/Windows/Avalonia.Win32/WindowImpl.cs | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 5f88fc3f0c..6bbcb3dc92 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -254,8 +254,8 @@ namespace Avalonia.Win32 // 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); + _savedWindowInfo.Style = GetStyle(); + _savedWindowInfo.ExStyle = GetExtendedStyle(); GetWindowRect(_hwnd, out var windowRect); _savedWindowInfo.WindowRect = windowRect; } @@ -265,12 +265,8 @@ namespace Avalonia.Win32 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))); + SetStyle(_savedWindowInfo.Style & ~(WindowStyles.WS_CAPTION | WindowStyles.WS_THICKFRAME)); + SetExtendedStyle(_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. @@ -288,8 +284,8 @@ namespace Avalonia.Win32 // 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); + SetStyle(_savedWindowInfo.Style); + SetExtendedStyle(_savedWindowInfo.ExStyle); // On restore, resize to the previous saved rect size. var new_rect = _savedWindowInfo.WindowRect.ToPixelRect(); From ecd4cd7bbf4a8eeecd3815c772fddda6fdbb3dc5 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 30 Apr 2020 13:43:11 -0300 Subject: [PATCH 08/47] Add CoCreateInstance overload that gives intptr. --- src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs index e9f00a0c7f..43beb35e45 100644 --- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs +++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs @@ -1147,7 +1147,10 @@ namespace Avalonia.Win32.Interop internal static extern int CoCreateInstance(ref Guid clsid, IntPtr ignore1, int ignore2, ref Guid iid, [MarshalAs(UnmanagedType.IUnknown), Out] out object pUnkOuter); - + [DllImport("ole32.dll", PreserveSig = true)] + internal static extern int CoCreateInstance(ref Guid clsid, + IntPtr ignore1, int ignore2, ref Guid iid, [Out] out IntPtr pUnkOuter); + [DllImport("shell32.dll", CharSet = CharSet.Unicode, SetLastError = true)] internal static extern int SHCreateItemFromParsingName([MarshalAs(UnmanagedType.LPWStr)] string pszPath, IntPtr pbc, ref Guid riid, [MarshalAs(UnmanagedType.Interface)] out IShellItem ppv); From 93e43951dbb58cacca68d78a1ddfade135de250f Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 30 Apr 2020 13:43:43 -0300 Subject: [PATCH 09/47] Add delegates and VTables for ITaskBarList2. --- .../Avalonia.Win32/ITaskBarList2VTable.cs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 src/Windows/Avalonia.Win32/ITaskBarList2VTable.cs diff --git a/src/Windows/Avalonia.Win32/ITaskBarList2VTable.cs b/src/Windows/Avalonia.Win32/ITaskBarList2VTable.cs new file mode 100644 index 0000000000..c187251ee3 --- /dev/null +++ b/src/Windows/Avalonia.Win32/ITaskBarList2VTable.cs @@ -0,0 +1,22 @@ +using System; +using System.Runtime.InteropServices; +using static Avalonia.Win32.Interop.UnmanagedMethods; + +namespace Avalonia.Win32 +{ + delegate void MarkFullscreenWindow(IntPtr This, IntPtr hwnd, [MarshalAs(UnmanagedType.Bool)] bool fullscreen); + delegate HRESULT HrInit(IntPtr This); + + struct ITaskBarList2VTable + { + public IntPtr IUnknown1; + public IntPtr IUnknown2; + public IntPtr IUnknown3; + public IntPtr HrInit; + public IntPtr AddTab; + public IntPtr DeleteTab; + public IntPtr ActivateTab; + public IntPtr SetActiveAlt; + public IntPtr MarkFullscreenWindow; + } +} From cecce99c954be7b7cccd467403ae58500ae171d8 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 30 Apr 2020 13:44:11 -0300 Subject: [PATCH 10/47] SystemDialogs call the correct cocreateinstance overload. --- src/Windows/Avalonia.Win32/SystemDialogImpl.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Windows/Avalonia.Win32/SystemDialogImpl.cs b/src/Windows/Avalonia.Win32/SystemDialogImpl.cs index 8bdd4b7bfa..c6164e0868 100644 --- a/src/Windows/Avalonia.Win32/SystemDialogImpl.cs +++ b/src/Windows/Avalonia.Win32/SystemDialogImpl.cs @@ -24,7 +24,7 @@ namespace Avalonia.Win32 Guid clsid = dialog is OpenFileDialog ? UnmanagedMethods.ShellIds.OpenFileDialog : UnmanagedMethods.ShellIds.SaveFileDialog; Guid iid = UnmanagedMethods.ShellIds.IFileDialog; - UnmanagedMethods.CoCreateInstance(ref clsid, IntPtr.Zero, 1, ref iid, out var unk); + UnmanagedMethods.CoCreateInstance(ref clsid, IntPtr.Zero, 1, ref iid, out object unk); var frm = (UnmanagedMethods.IFileDialog)unk; var openDialog = dialog as OpenFileDialog; @@ -105,9 +105,9 @@ namespace Avalonia.Win32 var hWnd = parent?.PlatformImpl?.Handle?.Handle ?? IntPtr.Zero; Guid clsid = UnmanagedMethods.ShellIds.OpenFileDialog; - Guid iid = UnmanagedMethods.ShellIds.IFileDialog; + Guid iid = UnmanagedMethods.ShellIds.IFileDialog; - UnmanagedMethods.CoCreateInstance(ref clsid, IntPtr.Zero, 1, ref iid, out var unk); + UnmanagedMethods.CoCreateInstance(ref clsid, IntPtr.Zero, 1, ref iid, out object unk); var frm = (UnmanagedMethods.IFileDialog)unk; uint options; frm.GetOptions(out options); From 90f386ffc0468df5e2678a777acb80e6e7b1faf8 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 30 Apr 2020 13:44:37 -0300 Subject: [PATCH 11/47] correctly port MarkFullScreen using CoreRT compatible COM. --- src/Windows/Avalonia.Win32/WindowImpl.cs | 58 ++++++++++++++---------- 1 file changed, 33 insertions(+), 25 deletions(-) diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 6bbcb3dc92..ba7f90eb63 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -46,6 +46,7 @@ namespace Avalonia.Win32 private SavedWindowInfo _savedWindowInfo; private bool _fullScreen; + private IntPtr _taskBarList; #if USE_MANAGED_DRAG private readonly ManagedWindowResizeDragHelper _managedDrag; @@ -218,39 +219,46 @@ namespace Avalonia.Win32 } } - private void MarkFullscreen(bool fullscreen) + /// + /// Ported from https://github.com/chromium/chromium/blob/master/ui/views/win/fullscreen_handler.cc + /// + /// Fullscreen state. + private unsafe void MarkFullscreen(bool fullscreen) { - //if (!task_bar_list_) - //{ - // HRESULT hr = + if (_taskBarList == IntPtr.Zero) + { + Guid clsid = ShellIds.TaskBarList; + Guid iid = ShellIds.ITaskBarList2; + + int result = CoCreateInstance(ref clsid, IntPtr.Zero, 1, ref iid, out _taskBarList); + + if (_taskBarList != IntPtr.Zero) + { + var ptr = (ITaskBarList2VTable**)_taskBarList.ToPointer(); + var hrInit = Marshal.GetDelegateForFunctionPointer((*ptr)->HrInit); - // ::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; - //} + if (hrInit(_taskBarList) != HRESULT.S_OK) + { + _taskBarList = IntPtr.Zero; + } + } + } - //// 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); + if (_taskBarList != IntPtr.Zero) + { + var ptr = (ITaskBarList2VTable**)_taskBarList.ToPointer(); + var markFullscreen = Marshal.GetDelegateForFunctionPointer((*ptr)->MarkFullscreenWindow); + markFullscreen(_taskBarList, _hwnd, fullscreen); + } } + /// + /// Ported from https://github.com/chromium/chromium/blob/master/ui/views/win/fullscreen_handler.cc + /// + /// 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) { From 81c4a1e796c6814edfde812fa1ff34464186ac2e Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 30 Apr 2020 13:46:32 -0300 Subject: [PATCH 12/47] move struct. --- src/Windows/Avalonia.Win32/WindowImpl.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index ba7f90eb63..f3538a9bfe 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -37,13 +37,6 @@ 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; private IntPtr _taskBarList; @@ -833,6 +826,13 @@ namespace Avalonia.Win32 IntPtr EglGlPlatformSurface.IEglWindowGlPlatformSurfaceInfo.Handle => Handle.Handle; + private struct SavedWindowInfo + { + public WindowStyles Style { get; set; } + public WindowStyles ExStyle { get; set; } + public RECT WindowRect { get; set; } + }; + private struct WindowProperties { public bool ShowInTaskbar; From 51ad905a2c3e73e832178222a5586a194add9e1b Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 30 Apr 2020 13:48:42 -0300 Subject: [PATCH 13/47] use WindowProperties to hold fullscreen flag. --- src/Windows/Avalonia.Win32/WindowImpl.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index f3538a9bfe..9553447623 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -38,7 +38,6 @@ namespace Avalonia.Win32 }; private SavedWindowInfo _savedWindowInfo; - private bool _fullScreen; private IntPtr _taskBarList; #if USE_MANAGED_DRAG @@ -253,7 +252,7 @@ namespace Avalonia.Win32 private void SetFullScreen(bool fullscreen) { // Save current window state if not already fullscreen. - if (!_fullScreen) + if (!_windowProperties.IsFullScreen) { _savedWindowInfo.Style = GetStyle(); _savedWindowInfo.ExStyle = GetExtendedStyle(); @@ -261,9 +260,9 @@ namespace Avalonia.Win32 _savedWindowInfo.WindowRect = windowRect; } - _fullScreen = fullscreen; + _windowProperties.IsFullScreen = fullscreen; - if (_fullScreen) + if (_windowProperties.IsFullScreen) { // Set new window style and size. SetStyle(_savedWindowInfo.Style & ~(WindowStyles.WS_CAPTION | WindowStyles.WS_THICKFRAME)); @@ -639,7 +638,7 @@ namespace Avalonia.Win32 switch (state) { case WindowState.Minimized: - if (_fullScreen) + if (_windowProperties.IsFullScreen) { SetFullScreen(false); } @@ -647,7 +646,7 @@ namespace Avalonia.Win32 command = ShowWindowCommand.Minimize; break; case WindowState.Maximized: - if (_fullScreen) + if (_windowProperties.IsFullScreen) { SetFullScreen(false); } @@ -656,7 +655,7 @@ namespace Avalonia.Win32 break; case WindowState.Normal: - if (_fullScreen) + if (_windowProperties.IsFullScreen) { SetFullScreen(false); } @@ -838,6 +837,7 @@ namespace Avalonia.Win32 public bool ShowInTaskbar; public bool IsResizable; public SystemDecorations Decorations; + public bool IsFullScreen; } } } From fe954a0398baf00fd60277841847480f379d4850 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 30 Apr 2020 13:51:01 -0300 Subject: [PATCH 14/47] move private methods. --- src/Windows/Avalonia.Win32/WindowImpl.cs | 174 +++++++++++------------ 1 file changed, 87 insertions(+), 87 deletions(-) diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 9553447623..842bf3dc72 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -211,93 +211,6 @@ namespace Avalonia.Win32 } } - /// - /// Ported from https://github.com/chromium/chromium/blob/master/ui/views/win/fullscreen_handler.cc - /// - /// Fullscreen state. - private unsafe void MarkFullscreen(bool fullscreen) - { - if (_taskBarList == IntPtr.Zero) - { - Guid clsid = ShellIds.TaskBarList; - Guid iid = ShellIds.ITaskBarList2; - - int result = CoCreateInstance(ref clsid, IntPtr.Zero, 1, ref iid, out _taskBarList); - - if (_taskBarList != IntPtr.Zero) - { - var ptr = (ITaskBarList2VTable**)_taskBarList.ToPointer(); - - var hrInit = Marshal.GetDelegateForFunctionPointer((*ptr)->HrInit); - - if (hrInit(_taskBarList) != HRESULT.S_OK) - { - _taskBarList = IntPtr.Zero; - } - } - } - - if (_taskBarList != IntPtr.Zero) - { - var ptr = (ITaskBarList2VTable**)_taskBarList.ToPointer(); - var markFullscreen = Marshal.GetDelegateForFunctionPointer((*ptr)->MarkFullscreenWindow); - markFullscreen(_taskBarList, _hwnd, fullscreen); - } - } - - /// - /// Ported from https://github.com/chromium/chromium/blob/master/ui/views/win/fullscreen_handler.cc - /// - /// - private void SetFullScreen(bool fullscreen) - { - // Save current window state if not already fullscreen. - if (!_windowProperties.IsFullScreen) - { - _savedWindowInfo.Style = GetStyle(); - _savedWindowInfo.ExStyle = GetExtendedStyle(); - GetWindowRect(_hwnd, out var windowRect); - _savedWindowInfo.WindowRect = windowRect; - } - - _windowProperties.IsFullScreen = fullscreen; - - if (_windowProperties.IsFullScreen) - { - // Set new window style and size. - SetStyle(_savedWindowInfo.Style & ~(WindowStyles.WS_CAPTION | WindowStyles.WS_THICKFRAME)); - SetExtendedStyle(_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. - SetStyle(_savedWindowInfo.Style); - SetExtendedStyle(_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 @@ -631,6 +544,93 @@ namespace Avalonia.Win32 } } + /// + /// Ported from https://github.com/chromium/chromium/blob/master/ui/views/win/fullscreen_handler.cc + /// + /// Fullscreen state. + private unsafe void MarkFullscreen(bool fullscreen) + { + if (_taskBarList == IntPtr.Zero) + { + Guid clsid = ShellIds.TaskBarList; + Guid iid = ShellIds.ITaskBarList2; + + int result = CoCreateInstance(ref clsid, IntPtr.Zero, 1, ref iid, out _taskBarList); + + if (_taskBarList != IntPtr.Zero) + { + var ptr = (ITaskBarList2VTable**)_taskBarList.ToPointer(); + + var hrInit = Marshal.GetDelegateForFunctionPointer((*ptr)->HrInit); + + if (hrInit(_taskBarList) != HRESULT.S_OK) + { + _taskBarList = IntPtr.Zero; + } + } + } + + if (_taskBarList != IntPtr.Zero) + { + var ptr = (ITaskBarList2VTable**)_taskBarList.ToPointer(); + var markFullscreen = Marshal.GetDelegateForFunctionPointer((*ptr)->MarkFullscreenWindow); + markFullscreen(_taskBarList, _hwnd, fullscreen); + } + } + + /// + /// Ported from https://github.com/chromium/chromium/blob/master/ui/views/win/fullscreen_handler.cc + /// + /// + private void SetFullScreen(bool fullscreen) + { + // Save current window state if not already fullscreen. + if (!_windowProperties.IsFullScreen) + { + _savedWindowInfo.Style = GetStyle(); + _savedWindowInfo.ExStyle = GetExtendedStyle(); + GetWindowRect(_hwnd, out var windowRect); + _savedWindowInfo.WindowRect = windowRect; + } + + _windowProperties.IsFullScreen = fullscreen; + + if (_windowProperties.IsFullScreen) + { + // Set new window style and size. + SetStyle(_savedWindowInfo.Style & ~(WindowStyles.WS_CAPTION | WindowStyles.WS_THICKFRAME)); + SetExtendedStyle(_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. + SetStyle(_savedWindowInfo.Style); + SetExtendedStyle(_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); + } + private void ShowWindow(WindowState state) { ShowWindowCommand command; From a41162dce6fd27794cde937c57c0785f2d5d0d38 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 30 Apr 2020 14:05:06 -0300 Subject: [PATCH 15/47] use UpdateWindowProperties to MarkFullScreen. --- src/Windows/Avalonia.Win32/WindowImpl.cs | 47 ++++++++++++------------ 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 842bf3dc72..c2f8eb3dbf 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -580,23 +580,19 @@ namespace Avalonia.Win32 /// /// Ported from https://github.com/chromium/chromium/blob/master/ui/views/win/fullscreen_handler.cc + /// Method must only be called from inside UpdateWindowProperties. /// /// private void SetFullScreen(bool fullscreen) { - // Save current window state if not already fullscreen. - if (!_windowProperties.IsFullScreen) + if (fullscreen) { + // Save current window state if not already fullscreen. _savedWindowInfo.Style = GetStyle(); _savedWindowInfo.ExStyle = GetExtendedStyle(); GetWindowRect(_hwnd, out var windowRect); _savedWindowInfo.WindowRect = windowRect; - } - - _windowProperties.IsFullScreen = fullscreen; - if (_windowProperties.IsFullScreen) - { // Set new window style and size. SetStyle(_savedWindowInfo.Style & ~(WindowStyles.WS_CAPTION | WindowStyles.WS_THICKFRAME)); SetExtendedStyle(_savedWindowInfo.ExStyle & ~(WindowStyles.WS_EX_DLGMODALFRAME | WindowStyles.WS_EX_WINDOWEDGE | WindowStyles.WS_EX_CLIENTEDGE | WindowStyles.WS_EX_STATICEDGE)); @@ -635,42 +631,40 @@ namespace Avalonia.Win32 { ShowWindowCommand command; + var newWindowProperties = _windowProperties; + switch (state) { case WindowState.Minimized: - if (_windowProperties.IsFullScreen) - { - SetFullScreen(false); - } - + newWindowProperties.IsFullScreen = false; command = ShowWindowCommand.Minimize; break; case WindowState.Maximized: - if (_windowProperties.IsFullScreen) - { - SetFullScreen(false); - } - + newWindowProperties.IsFullScreen = false; command = ShowWindowCommand.Maximize; break; case WindowState.Normal: - if (_windowProperties.IsFullScreen) - { - SetFullScreen(false); - } - + newWindowProperties.IsFullScreen = false; command = ShowWindowCommand.Restore; break; case WindowState.FullScreen: - SetFullScreen(true); - return; + newWindowProperties.IsFullScreen = true; + command = ShowWindowCommand.Maximize; + break; default: throw new ArgumentException("Invalid WindowState."); } + UpdateWindowProperties(newWindowProperties); + + if (newWindowProperties.IsFullScreen) + { + return; + } + UnmanagedMethods.ShowWindow(_hwnd, command); if (state == WindowState.Maximized) @@ -800,6 +794,11 @@ namespace Avalonia.Win32 SetWindowPosFlags.SWP_NOZORDER | SetWindowPosFlags.SWP_NOACTIVATE | SetWindowPosFlags.SWP_FRAMECHANGED); } + + if (oldProperties.IsFullScreen != newProperties.IsFullScreen) + { + SetFullScreen(newProperties.IsFullScreen); + } } #if USE_MANAGED_DRAG From bfa96382f7b7ac617c1f55b14463c8f30a3bc9d2 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 30 Apr 2020 14:06:12 -0300 Subject: [PATCH 16/47] cleaner code. --- src/Windows/Avalonia.Win32/WindowImpl.cs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index c2f8eb3dbf..97ec32cfb5 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -651,8 +651,8 @@ namespace Avalonia.Win32 case WindowState.FullScreen: newWindowProperties.IsFullScreen = true; - command = ShowWindowCommand.Maximize; - break; + UpdateWindowProperties(newWindowProperties); + return; default: throw new ArgumentException("Invalid WindowState."); @@ -660,11 +660,6 @@ namespace Avalonia.Win32 UpdateWindowProperties(newWindowProperties); - if (newWindowProperties.IsFullScreen) - { - return; - } - UnmanagedMethods.ShowWindow(_hwnd, command); if (state == WindowState.Maximized) From c38c7e2d65ddeb84504819412dbe8671725d09fe Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 30 Apr 2020 16:47:15 -0300 Subject: [PATCH 17/47] [OSX] implement FullScreen and fix sync of window states. --- native/Avalonia.Native/inc/avalonia-native.h | 1 + native/Avalonia.Native/src/OSX/window.h | 2 + native/Avalonia.Native/src/OSX/window.mm | 126 ++++++++++++++++--- 3 files changed, 114 insertions(+), 15 deletions(-) diff --git a/native/Avalonia.Native/inc/avalonia-native.h b/native/Avalonia.Native/inc/avalonia-native.h index b10db08adc..b0937d35ba 100644 --- a/native/Avalonia.Native/inc/avalonia-native.h +++ b/native/Avalonia.Native/inc/avalonia-native.h @@ -135,6 +135,7 @@ enum AvnWindowState Normal, Minimized, Maximized, + FullScreen, }; enum AvnStandardCursorType diff --git a/native/Avalonia.Native/src/OSX/window.h b/native/Avalonia.Native/src/OSX/window.h index 505900d584..449751b06e 100644 --- a/native/Avalonia.Native/src/OSX/window.h +++ b/native/Avalonia.Native/src/OSX/window.h @@ -33,6 +33,8 @@ struct INSWindowHolder struct IWindowStateChanged { virtual void WindowStateChanged () = 0; + virtual void StartStateTransition () = 0; + virtual void EndStateTransition () = 0; }; #endif /* window_h */ diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index 109ed63e6f..d7e4680cce 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -408,6 +408,8 @@ private: SystemDecorations _hasDecorations = SystemDecorationsFull; CGRect _lastUndecoratedFrame; AvnWindowState _lastWindowState; + bool _inSetWindowState; + bool _transitioningWindowState; FORWARD_IUNKNOWN() BEGIN_INTERFACE_MAP() @@ -421,6 +423,8 @@ private: ComPtr WindowEvents; WindowImpl(IAvnWindowEvents* events, IAvnGlContext* gl) : WindowBaseImpl(events, gl) { + _transitioningWindowState = false; + _inSetWindowState = false; _lastWindowState = Normal; WindowEvents = events; [Window setCanBecomeKeyAndMain]; @@ -457,11 +461,29 @@ private: } } + void StartStateTransition () override + { + _transitioningWindowState = true; + } + + void EndStateTransition () override + { + _transitioningWindowState = false; + } + void WindowStateChanged () override { - AvnWindowState state; - GetWindowState(&state); - WindowEvents->WindowStateChanged(state); + if(!_inSetWindowState && !_transitioningWindowState) + { + AvnWindowState state; + GetWindowState(&state); + + if(_lastWindowState != state) + { + _lastWindowState = state; + WindowEvents->WindowStateChanged(state); + } + } } bool UndecoratedIsMaximized () @@ -586,6 +608,12 @@ private: return E_POINTER; } + if(([Window styleMask] & NSFullScreenWindowMask) == NSFullScreenWindowMask) + { + *ret = FullScreen; + return S_OK; + } + if([Window isMiniaturized]) { *ret = Minimized; @@ -608,12 +636,25 @@ private: { @autoreleasepool { + if(_lastWindowState == state) + { + return S_OK; + } + + _inSetWindowState = true; + + auto currentState = _lastWindowState; _lastWindowState = state; if(_shown) { switch (state) { case Maximized: + if(currentState == FullScreen) + { + [Window toggleFullScreen:nullptr]; + } + lastPositionSet.X = 0; lastPositionSet.Y = 0; @@ -629,15 +670,29 @@ private: break; case Minimized: + if(currentState == FullScreen) + { + [Window toggleFullScreen:nullptr]; + } + [Window miniaturize:Window]; break; - default: + case FullScreen: + [Window toggleFullScreen:nullptr]; + break; + + case Normal: if([Window isMiniaturized]) { [Window deminiaturize:Window]; } + if(currentState == FullScreen) + { + [Window toggleFullScreen:nullptr]; + } + if(IsZoomed()) { DoZoom(); @@ -646,23 +701,17 @@ private: } } + _inSetWindowState = false; + return S_OK; } } virtual void OnResized () override { - if(_shown) + if(_shown && !_inSetWindowState && !_transitioningWindowState) { - auto windowState = [Window isMiniaturized] ? Minimized - : (IsZoomed() ? Maximized : Normal); - - if (windowState != _lastWindowState) - { - _lastWindowState = windowState; - - WindowEvents->WindowStateChanged(windowState); - } + WindowStateChanged(); } } @@ -1378,7 +1427,54 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent - (void)windowDidResize:(NSNotification *)notification { - _parent->OnResized(); + auto parent = dynamic_cast(_parent.operator->()); + + if(parent != nullptr) + { + parent->WindowStateChanged(); + } +} + +- (void)windowWillExitFullScreen:(NSNotification *)notification +{ + auto parent = dynamic_cast(_parent.operator->()); + + if(parent != nullptr) + { + parent->StartStateTransition(); + } +} + +- (void)windowDidExitFullScreen:(NSNotification *)notification +{ + auto parent = dynamic_cast(_parent.operator->()); + + if(parent != nullptr) + { + parent->EndStateTransition(); + parent->WindowStateChanged(); + } +} + +- (void)windowWillEnterFullScreen:(NSNotification *)notification +{ + auto parent = dynamic_cast(_parent.operator->()); + + if(parent != nullptr) + { + parent->StartStateTransition(); + } +} + +- (void)windowDidEnterFullScreen:(NSNotification *)notification +{ + auto parent = dynamic_cast(_parent.operator->()); + + if(parent != nullptr) + { + parent->EndStateTransition(); + parent->WindowStateChanged(); + } } - (BOOL)windowShouldZoom:(NSWindow *)window toFrame:(NSRect)newFrame From 00ef2fe59000cb1cd5ce1ec381441da8e708da2c Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 30 Apr 2020 16:53:03 -0300 Subject: [PATCH 18/47] deminiturize when entering fullscreen if needed. --- native/Avalonia.Native/src/OSX/window.mm | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index d7e4680cce..349db9f1cd 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -679,6 +679,11 @@ private: break; case FullScreen: + if([Window isMiniaturized]) + { + [Window deminiaturize:Window]; + } + [Window toggleFullScreen:nullptr]; break; From 65caf0341e8abca61ab9d4cd1c8628e573eb0633 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 30 Apr 2020 17:00:08 -0300 Subject: [PATCH 19/47] have a guess a fullscreen on x11. --- src/Avalonia.X11/X11Atoms.cs | 1 + src/Avalonia.X11/X11Window.cs | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/src/Avalonia.X11/X11Atoms.cs b/src/Avalonia.X11/X11Atoms.cs index db74a32b99..523b65c115 100644 --- a/src/Avalonia.X11/X11Atoms.cs +++ b/src/Avalonia.X11/X11Atoms.cs @@ -156,6 +156,7 @@ namespace Avalonia.X11 public readonly IntPtr _NET_SYSTEM_TRAY_OPCODE; public readonly IntPtr _NET_WM_STATE_MAXIMIZED_HORZ; public readonly IntPtr _NET_WM_STATE_MAXIMIZED_VERT; + public readonly IntPtr _NET_WM_STATE_FULLSCREEN; public readonly IntPtr _XEMBED; public readonly IntPtr _XEMBED_INFO; public readonly IntPtr _MOTIF_WM_HINTS; diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs index 3a919e2bc4..65b8cf040d 100644 --- a/src/Avalonia.X11/X11Window.cs +++ b/src/Avalonia.X11/X11Window.cs @@ -555,6 +555,11 @@ namespace Avalonia.X11 ChangeWMAtoms(true, _x11.Atoms._NET_WM_STATE_MAXIMIZED_VERT, _x11.Atoms._NET_WM_STATE_MAXIMIZED_HORZ); } + else if (value == WindowState.FullScreen) + { + ChangeWMAtoms(false, _x11.Atoms._NET_WM_STATE_HIDDEN); + ChangeWMAtoms(true, _x11.Atoms._NET_WM_STATE_FULLSCREEN); + } else { ChangeWMAtoms(false, _x11.Atoms._NET_WM_STATE_HIDDEN); @@ -585,6 +590,12 @@ namespace Avalonia.X11 break; } + if(pitems[c] == _x11.Atoms._NET_WM_STATE_FULLSCREEN) + { + state = WindowState.FullScreen; + break; + } + if (pitems[c] == _x11.Atoms._NET_WM_STATE_MAXIMIZED_HORZ || pitems[c] == _x11.Atoms._NET_WM_STATE_MAXIMIZED_VERT) { From 17c2268703df79fef30f3f4c01e7b7ad28276e28 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 30 Apr 2020 17:19:02 -0300 Subject: [PATCH 20/47] add window states demo to control catalog. --- samples/ControlCatalog/MainView.xaml | 5 ++-- samples/ControlCatalog/MainWindow.xaml | 2 +- .../ViewModels/MainWindowViewModel.cs | 27 ++++++++++++++++++- 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/samples/ControlCatalog/MainView.xaml b/samples/ControlCatalog/MainView.xaml index f3f70719e3..e02308b5c6 100644 --- a/samples/ControlCatalog/MainView.xaml +++ b/samples/ControlCatalog/MainView.xaml @@ -59,8 +59,8 @@ - - + + No Decorations Border Only Full Decorations @@ -69,6 +69,7 @@ Light Dark + diff --git a/samples/ControlCatalog/MainWindow.xaml b/samples/ControlCatalog/MainWindow.xaml index bea751ad4c..935db20757 100644 --- a/samples/ControlCatalog/MainWindow.xaml +++ b/samples/ControlCatalog/MainWindow.xaml @@ -7,7 +7,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:vm="clr-namespace:ControlCatalog.ViewModels" xmlns:v="clr-namespace:ControlCatalog.Views" - x:Class="ControlCatalog.MainWindow"> + x:Class="ControlCatalog.MainWindow" WindowState="{Binding WindowState, Mode=TwoWay}"> diff --git a/samples/ControlCatalog/ViewModels/MainWindowViewModel.cs b/samples/ControlCatalog/ViewModels/MainWindowViewModel.cs index b6aa3e92cd..0257b4ce66 100644 --- a/samples/ControlCatalog/ViewModels/MainWindowViewModel.cs +++ b/samples/ControlCatalog/ViewModels/MainWindowViewModel.cs @@ -1,4 +1,5 @@ using System.Reactive; +using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.Notifications; using Avalonia.Dialogs; @@ -11,6 +12,8 @@ namespace ControlCatalog.ViewModels private IManagedNotificationManager _notificationManager; private bool _isMenuItemChecked = true; + private WindowState _windowState; + private WindowState[] _windowStates; public MainWindowViewModel(IManagedNotificationManager notificationManager) { @@ -45,10 +48,32 @@ namespace ControlCatalog.ViewModels (App.Current.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime).Shutdown(); }); - ToggleMenuItemCheckedCommand = ReactiveCommand.Create(() => + ToggleMenuItemCheckedCommand = ReactiveCommand.Create(() => { IsMenuItemChecked = !IsMenuItemChecked; }); + + WindowState = WindowState.Normal; + + WindowStates = new WindowState[] + { + WindowState.Minimized, + WindowState.Normal, + WindowState.Maximized, + WindowState.FullScreen, + }; + } + + public WindowState WindowState + { + get { return _windowState; } + set { this.RaiseAndSetIfChanged(ref _windowState, value); } + } + + public WindowState[] WindowStates + { + get { return _windowStates; } + set { this.RaiseAndSetIfChanged(ref _windowStates, value); } } public IManagedNotificationManager NotificationManager From 9fb30c110bfeceb718ad1f06858c023aedb3b50a Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 30 Apr 2020 19:40:48 -0300 Subject: [PATCH 21/47] ensure WindowStyles can not get corrupted when changed during fullscreen. --- src/Windows/Avalonia.Win32/WindowImpl.cs | 95 ++++++++++++++++++------ 1 file changed, 72 insertions(+), 23 deletions(-) diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 97ec32cfb5..d709c9e65e 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -38,6 +38,7 @@ namespace Avalonia.Win32 }; private SavedWindowInfo _savedWindowInfo; + private bool _isFullScreenActive; private IntPtr _taskBarList; #if USE_MANAGED_DRAG @@ -587,15 +588,12 @@ namespace Avalonia.Win32 { if (fullscreen) { - // Save current window state if not already fullscreen. - _savedWindowInfo.Style = GetStyle(); - _savedWindowInfo.ExStyle = GetExtendedStyle(); GetWindowRect(_hwnd, out var windowRect); _savedWindowInfo.WindowRect = windowRect; // Set new window style and size. - SetStyle(_savedWindowInfo.Style & ~(WindowStyles.WS_CAPTION | WindowStyles.WS_THICKFRAME)); - SetExtendedStyle(_savedWindowInfo.ExStyle & ~(WindowStyles.WS_EX_DLGMODALFRAME | WindowStyles.WS_EX_WINDOWEDGE | WindowStyles.WS_EX_CLIENTEDGE | WindowStyles.WS_EX_STATICEDGE)); + SetStyle(GetStyle() & ~(WindowStyles.WS_CAPTION | WindowStyles.WS_THICKFRAME), false); + SetExtendedStyle(GetExtendedStyle() & ~(WindowStyles.WS_EX_DLGMODALFRAME | WindowStyles.WS_EX_WINDOWEDGE | WindowStyles.WS_EX_CLIENTEDGE | WindowStyles.WS_EX_STATICEDGE), false); // On expand, if we're given a window_rect, grow to it, otherwise do // not resize. @@ -607,14 +605,18 @@ namespace Avalonia.Win32 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); + + _isFullScreenActive = true; } 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. - SetStyle(_savedWindowInfo.Style); - SetExtendedStyle(_savedWindowInfo.ExStyle); + _isFullScreenActive = false; + + SetStyle(_savedWindowInfo.Style, false); + SetExtendedStyle(_savedWindowInfo.ExStyle, false); // On restore, resize to the previous saved rect size. var new_rect = _savedWindowInfo.WindowRect.ToPixelRect(); @@ -622,6 +624,8 @@ namespace Avalonia.Win32 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); + + UpdateWindowProperties(_windowProperties, true); } MarkFullscreen(fullscreen); @@ -693,20 +697,62 @@ namespace Avalonia.Win32 } } - private WindowStyles GetStyle() => (WindowStyles)GetWindowLong(_hwnd, (int)WindowLongParam.GWL_STYLE); + private WindowStyles GetStyle() + { + if (_isFullScreenActive) + { + return _savedWindowInfo.Style; + } + else + { + return (WindowStyles)GetWindowLong(_hwnd, (int)WindowLongParam.GWL_STYLE); + } + } + + private WindowStyles GetExtendedStyle() + { + if (_isFullScreenActive) + { + return _savedWindowInfo.ExStyle; + } + else + { + return (WindowStyles)GetWindowLong(_hwnd, (int)WindowLongParam.GWL_EXSTYLE); + } + } + + private void SetStyle(WindowStyles style, bool save = true) + { + if (save) + { + _savedWindowInfo.Style = style; + } - private WindowStyles GetExtendedStyle() => (WindowStyles)GetWindowLong(_hwnd, (int)WindowLongParam.GWL_EXSTYLE); + if (!_isFullScreenActive) + { + SetWindowLong(_hwnd, (int)WindowLongParam.GWL_STYLE, (uint)style); + } + } - private void SetStyle(WindowStyles style) => SetWindowLong(_hwnd, (int)WindowLongParam.GWL_STYLE, (uint)style); + private void SetExtendedStyle(WindowStyles style, bool save = true) + { + if (save) + { + _savedWindowInfo.ExStyle = style; + } - private void SetExtendedStyle(WindowStyles style) => SetWindowLong(_hwnd, (int)WindowLongParam.GWL_EXSTYLE, (uint)style); + if (!_isFullScreenActive) + { + SetWindowLong(_hwnd, (int)WindowLongParam.GWL_EXSTYLE, (uint)style); + } + } private void UpdateEnabled() { EnableWindow(_hwnd, _disabledBy.Count == 0); } - private void UpdateWindowProperties(WindowProperties newProperties) + private void UpdateWindowProperties(WindowProperties newProperties, bool forceChanges = false) { var oldProperties = _windowProperties; @@ -714,7 +760,7 @@ namespace Avalonia.Win32 // according to the new values already. _windowProperties = newProperties; - if (oldProperties.ShowInTaskbar != newProperties.ShowInTaskbar) + if ((oldProperties.ShowInTaskbar != newProperties.ShowInTaskbar) || forceChanges) { var exStyle = GetExtendedStyle(); @@ -733,7 +779,7 @@ namespace Avalonia.Win32 // Otherwise it will still show in the taskbar. } - if (oldProperties.IsResizable != newProperties.IsResizable) + if ((oldProperties.IsResizable != newProperties.IsResizable) || forceChanges) { var style = GetStyle(); @@ -749,7 +795,7 @@ namespace Avalonia.Win32 SetStyle(style); } - if (oldProperties.Decorations != newProperties.Decorations) + if ((oldProperties.Decorations != newProperties.Decorations) || forceChanges) { var style = GetStyle(); @@ -778,16 +824,19 @@ namespace Avalonia.Win32 SetStyle(style); - var newRect = oldClientRect; - - if (newProperties.Decorations == SystemDecorations.Full) + if (!_isFullScreenActive) { - AdjustWindowRectEx(ref newRect, (uint)style, false, (uint)GetExtendedStyle()); - } + var newRect = oldClientRect; - SetWindowPos(_hwnd, IntPtr.Zero, newRect.left, newRect.top, newRect.Width, newRect.Height, - SetWindowPosFlags.SWP_NOZORDER | SetWindowPosFlags.SWP_NOACTIVATE | - SetWindowPosFlags.SWP_FRAMECHANGED); + if (newProperties.Decorations == SystemDecorations.Full) + { + AdjustWindowRectEx(ref newRect, (uint)style, false, (uint)GetExtendedStyle()); + } + + SetWindowPos(_hwnd, IntPtr.Zero, newRect.left, newRect.top, newRect.Width, newRect.Height, + SetWindowPosFlags.SWP_NOZORDER | SetWindowPosFlags.SWP_NOACTIVATE | + SetWindowPosFlags.SWP_FRAMECHANGED); + } } if (oldProperties.IsFullScreen != newProperties.IsFullScreen) From a77e043262f3da1e9cab2e68e17c31704e893199 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 30 Apr 2020 20:08:32 -0300 Subject: [PATCH 22/47] [OSX] attempt to fix decoration changes when in fs mode. --- src/Avalonia.Native/WindowImpl.cs | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/src/Avalonia.Native/WindowImpl.cs b/src/Avalonia.Native/WindowImpl.cs index 6d6b2c5296..1f284f53ab 100644 --- a/src/Avalonia.Native/WindowImpl.cs +++ b/src/Avalonia.Native/WindowImpl.cs @@ -12,7 +12,10 @@ namespace Avalonia.Native private readonly IAvaloniaNativeFactory _factory; private readonly AvaloniaNativePlatformOptions _opts; private readonly GlPlatformFeature _glFeature; - IAvnWindow _native; + private readonly IAvnWindow _native; + private bool _isFullScreenActive; + private Interop.SystemDecorations _lastDecoration; + internal WindowImpl(IAvaloniaNativeFactory factory, AvaloniaNativePlatformOptions opts, GlPlatformFeature glFeature) : base(opts, glFeature) { @@ -39,7 +42,7 @@ namespace Avalonia.Native bool IAvnWindowEvents.Closing() { - if(_parent.Closing != null) + if (_parent.Closing != null) { return _parent.Closing(); } @@ -65,12 +68,17 @@ namespace Avalonia.Native _native.CanResize = value; } - public void SetSystemDecorations(Controls.SystemDecorations enabled) + public void SetSystemDecorations(Controls.SystemDecorations decorations) { - _native.HasDecorations = (Interop.SystemDecorations)enabled; + _lastDecoration = (Interop.SystemDecorations)decorations; + + if (!_isFullScreenActive) + { + _native.SetHasDecorations(_lastDecoration); + } } - public void SetTitleBarColor (Avalonia.Media.Color color) + public void SetTitleBarColor(Avalonia.Media.Color color) { _native.SetTitleBarColor(new AvnColor { Alpha = color.A, Red = color.R, Green = color.G, Blue = color.B }); } @@ -92,6 +100,17 @@ namespace Avalonia.Native set { _native.SetWindowState((AvnWindowState)value); + + if (value == WindowState.FullScreen) + { + _isFullScreenActive = true; + } + else + { + _isFullScreenActive = false; + + _native.SetHasDecorations(_lastDecoration); + } } } From bce4b8aee6fd1bb86f9522534f4ae0887f2a2cf6 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 1 May 2020 12:45:28 -0300 Subject: [PATCH 23/47] Revert "[OSX] attempt to fix decoration changes when in fs mode." This reverts commit a77e043262f3da1e9cab2e68e17c31704e893199. --- src/Avalonia.Native/WindowImpl.cs | 29 +++++------------------------ 1 file changed, 5 insertions(+), 24 deletions(-) diff --git a/src/Avalonia.Native/WindowImpl.cs b/src/Avalonia.Native/WindowImpl.cs index 1f284f53ab..6d6b2c5296 100644 --- a/src/Avalonia.Native/WindowImpl.cs +++ b/src/Avalonia.Native/WindowImpl.cs @@ -12,10 +12,7 @@ namespace Avalonia.Native private readonly IAvaloniaNativeFactory _factory; private readonly AvaloniaNativePlatformOptions _opts; private readonly GlPlatformFeature _glFeature; - private readonly IAvnWindow _native; - private bool _isFullScreenActive; - private Interop.SystemDecorations _lastDecoration; - + IAvnWindow _native; internal WindowImpl(IAvaloniaNativeFactory factory, AvaloniaNativePlatformOptions opts, GlPlatformFeature glFeature) : base(opts, glFeature) { @@ -42,7 +39,7 @@ namespace Avalonia.Native bool IAvnWindowEvents.Closing() { - if (_parent.Closing != null) + if(_parent.Closing != null) { return _parent.Closing(); } @@ -68,17 +65,12 @@ namespace Avalonia.Native _native.CanResize = value; } - public void SetSystemDecorations(Controls.SystemDecorations decorations) + public void SetSystemDecorations(Controls.SystemDecorations enabled) { - _lastDecoration = (Interop.SystemDecorations)decorations; - - if (!_isFullScreenActive) - { - _native.SetHasDecorations(_lastDecoration); - } + _native.HasDecorations = (Interop.SystemDecorations)enabled; } - public void SetTitleBarColor(Avalonia.Media.Color color) + public void SetTitleBarColor (Avalonia.Media.Color color) { _native.SetTitleBarColor(new AvnColor { Alpha = color.A, Red = color.R, Green = color.G, Blue = color.B }); } @@ -100,17 +92,6 @@ namespace Avalonia.Native set { _native.SetWindowState((AvnWindowState)value); - - if (value == WindowState.FullScreen) - { - _isFullScreenActive = true; - } - else - { - _isFullScreenActive = false; - - _native.SetHasDecorations(_lastDecoration); - } } } From 47f3b2c81067196e7cc38d7394a45fc3bada42fc Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 1 May 2020 12:52:44 -0300 Subject: [PATCH 24/47] rename _hasDecorations to _decorations. --- native/Avalonia.Native/src/OSX/window.mm | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index 349db9f1cd..ea1cf6c768 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -405,7 +405,7 @@ class WindowImpl : public virtual WindowBaseImpl, public virtual IAvnWindow, pub { private: bool _canResize = true; - SystemDecorations _hasDecorations = SystemDecorationsFull; + SystemDecorations _decorations = SystemDecorationsFull; CGRect _lastUndecoratedFrame; AvnWindowState _lastWindowState; bool _inSetWindowState; @@ -493,12 +493,12 @@ private: bool IsZoomed () { - return _hasDecorations != SystemDecorationsNone ? [Window isZoomed] : UndecoratedIsMaximized(); + return _decorations != SystemDecorationsNone ? [Window isZoomed] : UndecoratedIsMaximized(); } void DoZoom() { - switch (_hasDecorations) + switch (_decorations) { case SystemDecorationsNone: if (!UndecoratedIsMaximized()) @@ -530,10 +530,10 @@ private: { @autoreleasepool { - _hasDecorations = value; + _decorations = value; UpdateStyle(); - switch (_hasDecorations) + switch (_decorations) { case SystemDecorationsNone: [Window setHasShadow:NO]; @@ -725,7 +725,7 @@ protected: { unsigned long s = NSWindowStyleMaskBorderless; - switch (_hasDecorations) + switch (_decorations) { case SystemDecorationsNone: break; From cf0568a959a5b565d05fa81a52a0fbfc2358d604 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 1 May 2020 12:57:45 -0300 Subject: [PATCH 25/47] Rename HasDecorations api to SetDecorations api. --- native/Avalonia.Native/inc/avalonia-native.h | 2 +- native/Avalonia.Native/src/OSX/window.mm | 2 +- src/Avalonia.Native/WindowImpl.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/native/Avalonia.Native/inc/avalonia-native.h b/native/Avalonia.Native/inc/avalonia-native.h index b0937d35ba..38d99db5c9 100644 --- a/native/Avalonia.Native/inc/avalonia-native.h +++ b/native/Avalonia.Native/inc/avalonia-native.h @@ -247,7 +247,7 @@ AVNCOM(IAvnWindow, 04) : virtual IAvnWindowBase { virtual HRESULT ShowDialog (IAvnWindow* parent) = 0; virtual HRESULT SetCanResize(bool value) = 0; - virtual HRESULT SetHasDecorations(SystemDecorations value) = 0; + virtual HRESULT SetDecorations(SystemDecorations value) = 0; virtual HRESULT SetTitle (void* utf8Title) = 0; virtual HRESULT SetTitleBarColor (AvnColor color) = 0; virtual HRESULT SetWindowState(AvnWindowState state) = 0; diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index ea1cf6c768..ac66715e78 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -526,7 +526,7 @@ private: } } - virtual HRESULT SetHasDecorations(SystemDecorations value) override + virtual HRESULT SetDecorations(SystemDecorations value) override { @autoreleasepool { diff --git a/src/Avalonia.Native/WindowImpl.cs b/src/Avalonia.Native/WindowImpl.cs index 6d6b2c5296..ec010815f4 100644 --- a/src/Avalonia.Native/WindowImpl.cs +++ b/src/Avalonia.Native/WindowImpl.cs @@ -67,7 +67,7 @@ namespace Avalonia.Native public void SetSystemDecorations(Controls.SystemDecorations enabled) { - _native.HasDecorations = (Interop.SystemDecorations)enabled; + _native.Decorations = (Interop.SystemDecorations)enabled; } public void SetTitleBarColor (Avalonia.Media.Color color) From 7876fea246efb9d327199294b5b16668284c28d5 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 1 May 2020 14:39:21 -0300 Subject: [PATCH 26/47] [OSX] cope with toggling borderstyle when in fs mode. --- native/Avalonia.Native/src/OSX/window.mm | 89 +++++++++++++++++++----- 1 file changed, 72 insertions(+), 17 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index ac66715e78..46648a7998 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -404,9 +404,9 @@ public: class WindowImpl : public virtual WindowBaseImpl, public virtual IAvnWindow, public IWindowStateChanged { private: - bool _canResize = true; - SystemDecorations _decorations = SystemDecorationsFull; - CGRect _lastUndecoratedFrame; + bool _canResize; + bool _fullScreenActive; + SystemDecorations _decorations; AvnWindowState _lastWindowState; bool _inSetWindowState; bool _transitioningWindowState; @@ -423,6 +423,9 @@ private: ComPtr WindowEvents; WindowImpl(IAvnWindowEvents* events, IAvnGlContext* gl) : WindowBaseImpl(events, gl) { + _fullScreenActive = false; + _canResize = true; + _decorations = SystemDecorationsFull; _transitioningWindowState = false; _inSetWindowState = false; _lastWindowState = Normal; @@ -488,12 +491,14 @@ private: bool UndecoratedIsMaximized () { - return CGRectEqualToRect([Window frame], [Window screen].visibleFrame); + auto windowSize = [Window frame]; + auto available = [Window screen].visibleFrame; + return CGRectEqualToRect(windowSize, available); } bool IsZoomed () { - return _decorations != SystemDecorationsNone ? [Window isZoomed] : UndecoratedIsMaximized(); + return _decorations == SystemDecorationsFull ? [Window isZoomed] : UndecoratedIsMaximized(); } void DoZoom() @@ -501,15 +506,11 @@ private: switch (_decorations) { case SystemDecorationsNone: - if (!UndecoratedIsMaximized()) - { - _lastUndecoratedFrame = [Window frame]; - } - - [Window zoom:Window]; + case SystemDecorationsBorderOnly: + [Window setFrame:[Window screen].visibleFrame display:true]; break; - case SystemDecorationsBorderOnly: + case SystemDecorationsFull: [Window performZoom:Window]; break; @@ -530,7 +531,16 @@ private: { @autoreleasepool { + auto currentWindowState = _lastWindowState; _decorations = value; + + if(_fullScreenActive) + { + return S_OK; + } + + auto currentFrame = [Window frame]; + UpdateStyle(); switch (_decorations) @@ -539,12 +549,28 @@ private: [Window setHasShadow:NO]; [Window setTitleVisibility:NSWindowTitleHidden]; [Window setTitlebarAppearsTransparent:YES]; + + if(currentWindowState == Maximized) + { + if(!UndecoratedIsMaximized()) + { + DoZoom(); + } + } break; case SystemDecorationsBorderOnly: [Window setHasShadow:YES]; [Window setTitleVisibility:NSWindowTitleHidden]; [Window setTitlebarAppearsTransparent:YES]; + + if(currentWindowState == Maximized) + { + if(!UndecoratedIsMaximized()) + { + DoZoom(); + } + } break; case SystemDecorationsFull: @@ -552,6 +578,13 @@ private: [Window setTitleVisibility:NSWindowTitleVisible]; [Window setTitlebarAppearsTransparent:NO]; [Window setTitle:_lastTitle]; + + if(currentWindowState == Maximized) + { + auto newFrame = [Window contentRectForFrameRect:[Window frame]].size; + + [View setFrameSize:newFrame]; + } break; } @@ -620,7 +653,7 @@ private: return S_OK; } - if([Window isZoomed]) + if(IsZoomed()) { *ret = Maximized; return S_OK; @@ -632,6 +665,27 @@ private: } } + void EnterFullScreenMode () + { + _fullScreenActive = true; + + [Window setHasShadow:YES]; + [Window setTitleVisibility:NSWindowTitleVisible]; + [Window setTitlebarAppearsTransparent:NO]; + [Window setTitle:_lastTitle]; + + [Window toggleFullScreen:nullptr]; + } + + void ExitFullScreenMode () + { + [Window toggleFullScreen:nullptr]; + + _fullScreenActive = false; + + SetDecorations(_decorations); + } + virtual HRESULT SetWindowState (AvnWindowState state) override { @autoreleasepool @@ -652,7 +706,7 @@ private: case Maximized: if(currentState == FullScreen) { - [Window toggleFullScreen:nullptr]; + ExitFullScreenMode(); } lastPositionSet.X = 0; @@ -672,7 +726,7 @@ private: case Minimized: if(currentState == FullScreen) { - [Window toggleFullScreen:nullptr]; + ExitFullScreenMode(); } [Window miniaturize:Window]; @@ -684,7 +738,7 @@ private: [Window deminiaturize:Window]; } - [Window toggleFullScreen:nullptr]; + EnterFullScreenMode(); break; case Normal: @@ -695,7 +749,7 @@ private: if(currentState == FullScreen) { - [Window toggleFullScreen:nullptr]; + ExitFullScreenMode(); } if(IsZoomed()) @@ -728,6 +782,7 @@ protected: switch (_decorations) { case SystemDecorationsNone: + s = s | NSWindowStyleMaskFullSizeContentView; break; case SystemDecorationsBorderOnly: From aadc2effe0af8cf938aa6ff0e9d79ec340bb0182 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 1 May 2020 14:47:36 -0300 Subject: [PATCH 27/47] borderless windows will close. --- native/Avalonia.Native/src/OSX/window.mm | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index 46648a7998..8a1367486d 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -1273,6 +1273,20 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent } } +- (void)performClose:(id)sender +{ + if([[self delegate] respondsToSelector:@selector(windowShouldClose:)]) + { + if(![[self delegate] windowShouldClose:self]) return; + } + else if([self respondsToSelector:@selector(windowShouldClose:)]) + { + if(![self windowShouldClose:self]) return; + } + + [self close]; +} + - (void)pollModalSession:(nonnull NSModalSession)session { auto response = [NSApp runModalSession:session]; From 66d3f44cab5bb9382482e8c02f74bec758304749 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 1 May 2020 16:31:50 -0300 Subject: [PATCH 28/47] borderless windows can change state. --- native/Avalonia.Native/src/OSX/window.mm | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index 75efb3e090..844e550fea 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -409,6 +409,7 @@ private: SystemDecorations _decorations; AvnWindowState _lastWindowState; bool _inSetWindowState; + NSRect _preZoomSize; bool _transitioningWindowState; FORWARD_IUNKNOWN() @@ -706,6 +707,11 @@ private: auto currentState = _lastWindowState; _lastWindowState = state; + if(currentState == Normal) + { + _preZoomSize = [Window frame]; + } + if(_shown) { switch (state) { @@ -760,7 +766,18 @@ private: if(IsZoomed()) { - DoZoom(); + if(_decorations == SystemDecorationsFull) + { + DoZoom(); + } + else + { + [Window setFrame:_preZoomSize display:true]; + auto newFrame = [Window contentRectForFrameRect:[Window frame]].size; + + [View setFrameSize:newFrame]; + } + } break; } From b8e150e6f5a33e411aed2c4dd30176b409c60230 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 1 May 2020 17:11:15 -0300 Subject: [PATCH 29/47] [OSX] borderless windows can be minimized. --- native/Avalonia.Native/src/OSX/window.mm | 29 ++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index 844e550fea..0016b8dfc3 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -391,7 +391,7 @@ protected: void UpdateStyle() { - [Window setStyleMask:GetStyle()]; + [Window setStyleMask: GetStyle()]; } public: @@ -436,6 +436,20 @@ private: [Window setTabbingMode:NSWindowTabbingModeDisallowed]; } + void HideOrShowTrafficLights () + { + for (id subview in Window.contentView.superview.subviews) { + if ([subview isKindOfClass:NSClassFromString(@"NSTitlebarContainerView")]) { + NSView *titlebarView = [subview subviews][0]; + for (id button in titlebarView.subviews) { + if ([button isKindOfClass:[NSButton class]]) { + [button setHidden: (_decorations != SystemDecorationsFull)]; + } + } + } + } + } + virtual HRESULT Show () override { @autoreleasepool @@ -447,6 +461,8 @@ private: WindowBaseImpl::Show(); + HideOrShowTrafficLights(); + return SetWindowState(_lastWindowState); } } @@ -467,6 +483,8 @@ private: [cparent->Window addChildWindow:Window ordered:NSWindowAbove]; WindowBaseImpl::Show(); + HideOrShowTrafficLights(); + return S_OK; } } @@ -549,6 +567,8 @@ private: auto currentFrame = [Window frame]; UpdateStyle(); + + HideOrShowTrafficLights(); switch (_decorations) { @@ -741,6 +761,7 @@ private: ExitFullScreenMode(); } + [Window miniaturize:Window]; break; @@ -805,20 +826,20 @@ protected: switch (_decorations) { case SystemDecorationsNone: - s = s | NSWindowStyleMaskFullSizeContentView; + s = s | NSWindowStyleMaskFullSizeContentView | NSWindowStyleMaskMiniaturizable; break; case SystemDecorationsBorderOnly: - s = s | NSWindowStyleMaskTitled | NSWindowStyleMaskFullSizeContentView; + s = s | NSWindowStyleMaskTitled | NSWindowStyleMaskFullSizeContentView | NSWindowStyleMaskMiniaturizable; break; case SystemDecorationsFull: s = s | NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskBorderless; + if(_canResize) { s = s | NSWindowStyleMaskResizable; } - break; } From d19f117b4f3dab091e51ec4effb438063a7d651d Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 1 May 2020 17:29:00 -0300 Subject: [PATCH 30/47] [OSX] any borderstyle can enter fullscreen mode. --- native/Avalonia.Native/src/OSX/window.mm | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index 0016b8dfc3..50e04d9def 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -668,7 +668,7 @@ private: return E_POINTER; } - if(([Window styleMask] & NSFullScreenWindowMask) == NSFullScreenWindowMask) + if(([Window styleMask] & NSWindowStyleMaskFullScreen) == NSWindowStyleMaskFullScreen) { *ret = FullScreen; return S_OK; @@ -701,6 +701,8 @@ private: [Window setTitlebarAppearsTransparent:NO]; [Window setTitle:_lastTitle]; + [Window setStyleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskResizable]; + [Window toggleFullScreen:nullptr]; } From 24dd2b4459980dd379652485d9b005dec4f39483 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 1 May 2020 18:04:26 -0300 Subject: [PATCH 31/47] [OSX] borderless window can transistion from FullScreen to Maximised correctly. --- native/Avalonia.Native/src/OSX/window.h | 2 ++ native/Avalonia.Native/src/OSX/window.mm | 17 +++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/native/Avalonia.Native/src/OSX/window.h b/native/Avalonia.Native/src/OSX/window.h index 14fa054cbf..ec8fe9e6ee 100644 --- a/native/Avalonia.Native/src/OSX/window.h +++ b/native/Avalonia.Native/src/OSX/window.h @@ -37,6 +37,8 @@ struct IWindowStateChanged virtual void WindowStateChanged () = 0; virtual void StartStateTransition () = 0; virtual void EndStateTransition () = 0; + virtual SystemDecorations Decorations () = 0; + virtual AvnWindowState WindowState () = 0; }; #endif /* window_h */ diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index 50e04d9def..f8dc07063e 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -499,6 +499,16 @@ private: _transitioningWindowState = false; } + SystemDecorations Decorations () override + { + return _decorations; + } + + AvnWindowState WindowState () override + { + return _lastWindowState; + } + void WindowStateChanged () override { if(!_inSetWindowState && !_transitioningWindowState) @@ -1587,6 +1597,13 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent if(parent != nullptr) { parent->EndStateTransition(); + + if(parent->Decorations() != SystemDecorationsFull && parent->WindowState() == Maximized) + { + NSRect screenRect = [[self screen] visibleFrame]; + [self setFrame:screenRect display:YES]; + } + parent->WindowStateChanged(); } } From b429943023adc615236c569bab484f48d1926db9 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 1 May 2020 18:20:25 -0300 Subject: [PATCH 32/47] [OSX] correctly transition from fullscreen to minimized. --- native/Avalonia.Native/src/OSX/window.mm | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm index f8dc07063e..091219fc72 100644 --- a/native/Avalonia.Native/src/OSX/window.mm +++ b/native/Avalonia.Native/src/OSX/window.mm @@ -772,9 +772,10 @@ private: { ExitFullScreenMode(); } - - - [Window miniaturize:Window]; + else + { + [Window miniaturize:Window]; + } break; case FullScreen: @@ -1604,6 +1605,11 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent [self setFrame:screenRect display:YES]; } + if(parent->WindowState() == Minimized) + { + [self miniaturize:nullptr]; + } + parent->WindowStateChanged(); } } From cd2b26bc949d3a6f347d4dd2e2d27b502423ae73 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Fri, 1 May 2020 20:09:36 +0200 Subject: [PATCH 33/47] Add extra methods to LogicalExtensions and optimize a few methods as well. --- .../Platform/DefaultMenuInteractionHandler.cs | 2 +- .../ManagedFileChooser.xaml.cs | 2 +- .../LogicalTree/LogicalExtensions.cs | 93 ++++++++++++++++++- .../VisualTree/VisualExtensions.cs | 14 ++- 4 files changed, 105 insertions(+), 6 deletions(-) diff --git a/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs b/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs index 3715bc52a4..8ad622ba4a 100644 --- a/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs +++ b/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs @@ -392,7 +392,7 @@ namespace Avalonia.Controls.Platform { var control = e.Source as ILogical; - if (!Menu.IsLogicalParentOf(control)) + if (!Menu.IsLogicalAncestorOf(control)) { Menu.Close(); } diff --git a/src/Avalonia.Dialogs/ManagedFileChooser.xaml.cs b/src/Avalonia.Dialogs/ManagedFileChooser.xaml.cs index b967b40c0d..616f260397 100644 --- a/src/Avalonia.Dialogs/ManagedFileChooser.xaml.cs +++ b/src/Avalonia.Dialogs/ManagedFileChooser.xaml.cs @@ -34,7 +34,7 @@ namespace Avalonia.Dialogs return; } - var isQuickLink = _quickLinksRoot.IsLogicalParentOf(e.Source as Control); + var isQuickLink = _quickLinksRoot.IsLogicalAncestorOf(e.Source as Control); if (e.ClickCount == 2 || isQuickLink) { if (model.ItemType == ManagedFileChooserItemType.File) diff --git a/src/Avalonia.Styling/LogicalTree/LogicalExtensions.cs b/src/Avalonia.Styling/LogicalTree/LogicalExtensions.cs index 1003375978..bd05796578 100644 --- a/src/Avalonia.Styling/LogicalTree/LogicalExtensions.cs +++ b/src/Avalonia.Styling/LogicalTree/LogicalExtensions.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; namespace Avalonia.LogicalTree { @@ -29,6 +28,35 @@ namespace Avalonia.LogicalTree } } + /// + /// Finds first ancestor of given type. + /// + /// Ancestor type. + /// The logical. + /// If given logical should be included in search. + /// First ancestor of given type. + public static T FindLogicalAncestorOfType(this ILogical logical, bool includeSelf = false) where T : class + { + if (logical is null) + { + return null; + } + + ILogical parent = includeSelf ? logical : logical.LogicalParent; + + while (parent != null) + { + if (parent is T result) + { + return result; + } + + parent = parent.LogicalParent; + } + + return null; + } + public static IEnumerable GetLogicalChildren(this ILogical logical) { return logical.LogicalChildren; @@ -57,6 +85,28 @@ namespace Avalonia.LogicalTree } } + /// + /// Finds first descendant of given type. + /// + /// Descendant type. + /// The logical. + /// If given logical should be included in search. + /// First descendant of given type. + public static T FindLogicalDescendantOfType(this ILogical logical, bool includeSelf = false) where T : class + { + if (logical is null) + { + return null; + } + + if (includeSelf && logical is T result) + { + return result; + } + + return FindDescendantOfTypeCore(logical); + } + public static ILogical GetLogicalParent(this ILogical logical) { return logical.LogicalParent; @@ -80,9 +130,46 @@ namespace Avalonia.LogicalTree } } - public static bool IsLogicalParentOf(this ILogical logical, ILogical target) + public static bool IsLogicalAncestorOf(this ILogical logical, ILogical target) { - return target.GetLogicalAncestors().Any(x => x == logical); + ILogical current = target?.LogicalParent; + + while (current != null) + { + if (current == logical) + { + return true; + } + + current = current.LogicalParent; + } + + return false; + } + + private static T FindDescendantOfTypeCore(ILogical logical) where T : class + { + var logicalChildren = logical.LogicalChildren; + var logicalChildrenCount = logicalChildren.Count; + + for (var i = 0; i < logicalChildrenCount; i++) + { + ILogical child = logicalChildren[i]; + + if (child is T result) + { + return result; + } + + var childResult = FindDescendantOfTypeCore(child); + + if (!(childResult is null)) + { + return childResult; + } + } + + return null; } } } diff --git a/src/Avalonia.Visuals/VisualTree/VisualExtensions.cs b/src/Avalonia.Visuals/VisualTree/VisualExtensions.cs index 4b3e757e7a..636aa6836e 100644 --- a/src/Avalonia.Visuals/VisualTree/VisualExtensions.cs +++ b/src/Avalonia.Visuals/VisualTree/VisualExtensions.cs @@ -377,7 +377,19 @@ namespace Avalonia.VisualTree /// public static bool IsVisualAncestorOf(this IVisual visual, IVisual target) { - return target.GetVisualAncestors().Any(x => x == visual); + IVisual current = target; + + while (current != null) + { + if (current == visual) + { + return true; + } + + current = current.VisualParent; + } + + return false; } public static IEnumerable SortByZIndex(this IEnumerable elements) From f3dfb5d18cfa9ff375c0bd378fc9a5d990ca8c55 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Sat, 2 May 2020 12:31:47 +0200 Subject: [PATCH 34/47] Fix broken visual ancestor. --- src/Avalonia.Visuals/VisualTree/VisualExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Visuals/VisualTree/VisualExtensions.cs b/src/Avalonia.Visuals/VisualTree/VisualExtensions.cs index 636aa6836e..8c4004efdc 100644 --- a/src/Avalonia.Visuals/VisualTree/VisualExtensions.cs +++ b/src/Avalonia.Visuals/VisualTree/VisualExtensions.cs @@ -377,7 +377,7 @@ namespace Avalonia.VisualTree /// public static bool IsVisualAncestorOf(this IVisual visual, IVisual target) { - IVisual current = target; + IVisual current = target?.VisualParent; while (current != null) { From c9d1685f04a777e864480693626069fca2ec9b4f Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Sat, 2 May 2020 12:55:49 +0200 Subject: [PATCH 35/47] Add comments copied from VisualExtensions. --- .../LogicalTree/LogicalExtensions.cs | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/src/Avalonia.Styling/LogicalTree/LogicalExtensions.cs b/src/Avalonia.Styling/LogicalTree/LogicalExtensions.cs index bd05796578..458ab0fce2 100644 --- a/src/Avalonia.Styling/LogicalTree/LogicalExtensions.cs +++ b/src/Avalonia.Styling/LogicalTree/LogicalExtensions.cs @@ -3,8 +3,16 @@ using System.Collections.Generic; namespace Avalonia.LogicalTree { + /// + /// Provides extension methods for working with the logical tree. + /// public static class LogicalExtensions { + /// + /// Enumerates the ancestors of an in the logical tree. + /// + /// The logical. + /// The logical's ancestors. public static IEnumerable GetLogicalAncestors(this ILogical logical) { Contract.Requires(logical != null); @@ -18,6 +26,11 @@ namespace Avalonia.LogicalTree } } + /// + /// Enumerates an and its ancestors in the logical tree. + /// + /// The logical. + /// The logical and its ancestors. public static IEnumerable GetSelfAndLogicalAncestors(this ILogical logical) { yield return logical; @@ -57,11 +70,21 @@ namespace Avalonia.LogicalTree return null; } + /// + /// Enumerates the children of an in the logical tree. + /// + /// The logical. + /// The logical children. public static IEnumerable GetLogicalChildren(this ILogical logical) { return logical.LogicalChildren; } + /// + /// Enumerates the descendants of an in the logical tree. + /// + /// The logical. + /// The logical's ancestors. public static IEnumerable GetLogicalDescendants(this ILogical logical) { foreach (ILogical child in logical.LogicalChildren) @@ -75,6 +98,11 @@ namespace Avalonia.LogicalTree } } + /// + /// Enumerates an and its descendants in the logical tree. + /// + /// The logical. + /// The logical and its ancestors. public static IEnumerable GetSelfAndLogicalDescendants(this ILogical logical) { yield return logical; @@ -107,16 +135,34 @@ namespace Avalonia.LogicalTree return FindDescendantOfTypeCore(logical); } + /// + /// Gets the logical parent of an . + /// + /// The logical. + /// The parent, or null if the logical is unparented. public static ILogical GetLogicalParent(this ILogical logical) { return logical.LogicalParent; } + /// + /// Gets the logical parent of an . + /// + /// The type of the logical parent. + /// The logical. + /// + /// The parent, or null if the logical is unparented or its parent is not of type . + /// public static T GetLogicalParent(this ILogical logical) where T : class { return logical.LogicalParent as T; } + /// + /// Enumerates the siblings of an in the logical tree. + /// + /// The logical. + /// The logical siblings. public static IEnumerable GetLogicalSiblings(this ILogical logical) { ILogical parent = logical.LogicalParent; @@ -130,6 +176,15 @@ namespace Avalonia.LogicalTree } } + /// + /// Tests whether an is an ancestor of another logical. + /// + /// The logical. + /// The potential descendant. + /// + /// True if is an ancestor of ; + /// otherwise false. + /// public static bool IsLogicalAncestorOf(this ILogical logical, ILogical target) { ILogical current = target?.LogicalParent; From 34913150914c706ead9bb3a52334929c5d81a247 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Tue, 5 May 2020 17:56:02 +0300 Subject: [PATCH 36/47] [X11] Fixed reverting back from fullscreen --- src/Avalonia.X11/X11Window.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs index 65b8cf040d..1d5ce0dc8c 100644 --- a/src/Avalonia.X11/X11Window.cs +++ b/src/Avalonia.X11/X11Window.cs @@ -552,6 +552,7 @@ namespace Avalonia.X11 else if (value == WindowState.Maximized) { ChangeWMAtoms(false, _x11.Atoms._NET_WM_STATE_HIDDEN); + ChangeWMAtoms(false, _x11.Atoms._NET_WM_STATE_FULLSCREEN); ChangeWMAtoms(true, _x11.Atoms._NET_WM_STATE_MAXIMIZED_VERT, _x11.Atoms._NET_WM_STATE_MAXIMIZED_HORZ); } @@ -559,10 +560,13 @@ namespace Avalonia.X11 { ChangeWMAtoms(false, _x11.Atoms._NET_WM_STATE_HIDDEN); ChangeWMAtoms(true, _x11.Atoms._NET_WM_STATE_FULLSCREEN); + ChangeWMAtoms(false, _x11.Atoms._NET_WM_STATE_MAXIMIZED_VERT, + _x11.Atoms._NET_WM_STATE_MAXIMIZED_HORZ); } else { ChangeWMAtoms(false, _x11.Atoms._NET_WM_STATE_HIDDEN); + ChangeWMAtoms(false, _x11.Atoms._NET_WM_STATE_FULLSCREEN); ChangeWMAtoms(false, _x11.Atoms._NET_WM_STATE_MAXIMIZED_VERT, _x11.Atoms._NET_WM_STATE_MAXIMIZED_HORZ); } @@ -1063,7 +1067,7 @@ namespace Avalonia.X11 void ChangeWMAtoms(bool enable, params IntPtr[] atoms) { - if (atoms.Length < 1 || atoms.Length > 4) + if (atoms.Length != 1 && atoms.Length != 2) throw new ArgumentException(); if (!_mapped) From bd3d45d1ceaf894f14e16f5ddd7cb93edb09e719 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Tue, 5 May 2020 17:58:05 +0300 Subject: [PATCH 37/47] [X11] Make BorderOnly to behave the same way as None, since it doesn't work anyway --- src/Avalonia.X11/X11Window.cs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs index 1d5ce0dc8c..c82aabd4c9 100644 --- a/src/Avalonia.X11/X11Window.cs +++ b/src/Avalonia.X11/X11Window.cs @@ -220,14 +220,10 @@ namespace Avalonia.X11 var decorations = MotifDecorations.Menu | MotifDecorations.Title | MotifDecorations.Border | MotifDecorations.Maximize | MotifDecorations.Minimize | MotifDecorations.ResizeH; - if (_popup || _systemDecorations == SystemDecorations.None) - { + if (_popup + || _systemDecorations == SystemDecorations.None + || _systemDecorations == SystemDecorations.BorderOnly) decorations = 0; - } - else if (_systemDecorations == SystemDecorations.BorderOnly) - { - decorations = MotifDecorations.Border; - } if (!_canResize || _systemDecorations == SystemDecorations.BorderOnly) { From 1c4a56a406f56a3d287c4915319b57f7dd251315 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Tue, 5 May 2020 13:08:10 -0300 Subject: [PATCH 38/47] ensure style is always up to date before entering fs. --- src/Windows/Avalonia.Win32/WindowImpl.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index d709c9e65e..15261fe1c4 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -591,9 +591,15 @@ namespace Avalonia.Win32 GetWindowRect(_hwnd, out var windowRect); _savedWindowInfo.WindowRect = windowRect; + var current = GetStyle(); + var currentEx = GetExtendedStyle(); + + _savedWindowInfo.Style = current; + _savedWindowInfo.ExStyle = currentEx; + // Set new window style and size. - SetStyle(GetStyle() & ~(WindowStyles.WS_CAPTION | WindowStyles.WS_THICKFRAME), false); - SetExtendedStyle(GetExtendedStyle() & ~(WindowStyles.WS_EX_DLGMODALFRAME | WindowStyles.WS_EX_WINDOWEDGE | WindowStyles.WS_EX_CLIENTEDGE | WindowStyles.WS_EX_STATICEDGE), false); + SetStyle(current & ~(WindowStyles.WS_CAPTION | WindowStyles.WS_THICKFRAME), false); + SetExtendedStyle(currentEx & ~(WindowStyles.WS_EX_DLGMODALFRAME | WindowStyles.WS_EX_WINDOWEDGE | WindowStyles.WS_EX_CLIENTEDGE | WindowStyles.WS_EX_STATICEDGE), false); // On expand, if we're given a window_rect, grow to it, otherwise do // not resize. From 172de036be36360e996f2d2e5b201bde8b122b76 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Tue, 5 May 2020 14:15:21 -0300 Subject: [PATCH 39/47] fix glitch with window decorations transitioning from fullscreen to maximized. --- src/Windows/Avalonia.Win32/WindowImpl.cs | 34 ++++++++++++------------ 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 15261fe1c4..549a568fa2 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -801,6 +801,11 @@ namespace Avalonia.Win32 SetStyle(style); } + if (oldProperties.IsFullScreen != newProperties.IsFullScreen) + { + SetFullScreen(newProperties.IsFullScreen); + } + if ((oldProperties.Decorations != newProperties.Decorations) || forceChanges) { var style = GetStyle(); @@ -816,22 +821,22 @@ namespace Avalonia.Win32 style &= ~fullDecorationFlags; } - var margins = new MARGINS - { - cyBottomHeight = newProperties.Decorations == SystemDecorations.BorderOnly ? 1 : 0 - }; - - DwmExtendFrameIntoClientArea(_hwnd, ref margins); - - GetClientRect(_hwnd, out var oldClientRect); - var oldClientRectOrigin = new POINT(); - ClientToScreen(_hwnd, ref oldClientRectOrigin); - oldClientRect.Offset(oldClientRectOrigin); - SetStyle(style); if (!_isFullScreenActive) { + var margins = new MARGINS + { + cyBottomHeight = newProperties.Decorations == SystemDecorations.BorderOnly ? 1 : 0 + }; + + DwmExtendFrameIntoClientArea(_hwnd, ref margins); + + GetClientRect(_hwnd, out var oldClientRect); + var oldClientRectOrigin = new POINT(); + ClientToScreen(_hwnd, ref oldClientRectOrigin); + oldClientRect.Offset(oldClientRectOrigin); + var newRect = oldClientRect; if (newProperties.Decorations == SystemDecorations.Full) @@ -844,11 +849,6 @@ namespace Avalonia.Win32 SetWindowPosFlags.SWP_FRAMECHANGED); } } - - if (oldProperties.IsFullScreen != newProperties.IsFullScreen) - { - SetFullScreen(newProperties.IsFullScreen); - } } #if USE_MANAGED_DRAG From 0e12f984193e16363da8648db81b5ebe42e874e7 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Tue, 5 May 2020 17:00:15 -0300 Subject: [PATCH 40/47] move TaskbarList interop into UnmanagedMethods and a wrapper class. --- .../Avalonia.Win32/ITaskBarList2VTable.cs | 22 -------- .../Avalonia.Win32/Interop/TaskBarList.cs | 55 +++++++++++++++++++ .../Interop/UnmanagedMethods.cs | 16 ++++++ src/Windows/Avalonia.Win32/WindowImpl.cs | 37 +------------ 4 files changed, 72 insertions(+), 58 deletions(-) delete mode 100644 src/Windows/Avalonia.Win32/ITaskBarList2VTable.cs create mode 100644 src/Windows/Avalonia.Win32/Interop/TaskBarList.cs diff --git a/src/Windows/Avalonia.Win32/ITaskBarList2VTable.cs b/src/Windows/Avalonia.Win32/ITaskBarList2VTable.cs deleted file mode 100644 index c187251ee3..0000000000 --- a/src/Windows/Avalonia.Win32/ITaskBarList2VTable.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using System.Runtime.InteropServices; -using static Avalonia.Win32.Interop.UnmanagedMethods; - -namespace Avalonia.Win32 -{ - delegate void MarkFullscreenWindow(IntPtr This, IntPtr hwnd, [MarshalAs(UnmanagedType.Bool)] bool fullscreen); - delegate HRESULT HrInit(IntPtr This); - - struct ITaskBarList2VTable - { - public IntPtr IUnknown1; - public IntPtr IUnknown2; - public IntPtr IUnknown3; - public IntPtr HrInit; - public IntPtr AddTab; - public IntPtr DeleteTab; - public IntPtr ActivateTab; - public IntPtr SetActiveAlt; - public IntPtr MarkFullscreenWindow; - } -} diff --git a/src/Windows/Avalonia.Win32/Interop/TaskBarList.cs b/src/Windows/Avalonia.Win32/Interop/TaskBarList.cs new file mode 100644 index 0000000000..1b01ebbe7f --- /dev/null +++ b/src/Windows/Avalonia.Win32/Interop/TaskBarList.cs @@ -0,0 +1,55 @@ +using System; +using System.Runtime.InteropServices; +using static Avalonia.Win32.Interop.UnmanagedMethods; + +namespace Avalonia.Win32.Interop +{ + internal class TaskBarList + { + private static IntPtr s_taskBarList; + private static HrInit s_hrInitDelegate; + private static MarkFullscreenWindow s_markFullscreenWindowDelegate; + + /// + /// Ported from https://github.com/chromium/chromium/blob/master/ui/views/win/fullscreen_handler.cc + /// + /// Fullscreen state. + public static unsafe void MarkFullscreen(IntPtr hwnd, bool fullscreen) + { + if (s_taskBarList == IntPtr.Zero) + { + Guid clsid = ShellIds.TaskBarList; + Guid iid = ShellIds.ITaskBarList2; + + int result = CoCreateInstance(ref clsid, IntPtr.Zero, 1, ref iid, out s_taskBarList); + + if (s_taskBarList != IntPtr.Zero) + { + var ptr = (ITaskBarList2VTable**)s_taskBarList.ToPointer(); + + if (s_hrInitDelegate is null) + { + s_hrInitDelegate = Marshal.GetDelegateForFunctionPointer((*ptr)->HrInit); + } + + if (s_hrInitDelegate(s_taskBarList) != HRESULT.S_OK) + { + s_taskBarList = IntPtr.Zero; + } + } + } + + if (s_taskBarList != IntPtr.Zero) + { + var ptr = (ITaskBarList2VTable**)s_taskBarList.ToPointer(); + + if (s_markFullscreenWindowDelegate is null) + { + s_markFullscreenWindowDelegate = Marshal.GetDelegateForFunctionPointer((*ptr)->MarkFullscreenWindow); + } + + s_markFullscreenWindowDelegate(s_taskBarList, hwnd, fullscreen); + } + } + } +} diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs index 43beb35e45..5601ccbafe 100644 --- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs +++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs @@ -1880,6 +1880,22 @@ namespace Avalonia.Win32.Interop [MarshalAs(UnmanagedType.LPWStr)] public string pszSpec; } + + public delegate void MarkFullscreenWindow(IntPtr This, IntPtr hwnd, [MarshalAs(UnmanagedType.Bool)] bool fullscreen); + public delegate HRESULT HrInit(IntPtr This); + + public struct ITaskBarList2VTable + { + public IntPtr IUnknown1; + public IntPtr IUnknown2; + public IntPtr IUnknown3; + public IntPtr HrInit; + public IntPtr AddTab; + public IntPtr DeleteTab; + public IntPtr ActivateTab; + public IntPtr SetActiveAlt; + public IntPtr MarkFullscreenWindow; + } } [Flags] diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 549a568fa2..51d60cc970 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -39,7 +39,6 @@ namespace Avalonia.Win32 private SavedWindowInfo _savedWindowInfo; private bool _isFullScreenActive; - private IntPtr _taskBarList; #if USE_MANAGED_DRAG private readonly ManagedWindowResizeDragHelper _managedDrag; @@ -545,40 +544,6 @@ namespace Avalonia.Win32 } } - /// - /// Ported from https://github.com/chromium/chromium/blob/master/ui/views/win/fullscreen_handler.cc - /// - /// Fullscreen state. - private unsafe void MarkFullscreen(bool fullscreen) - { - if (_taskBarList == IntPtr.Zero) - { - Guid clsid = ShellIds.TaskBarList; - Guid iid = ShellIds.ITaskBarList2; - - int result = CoCreateInstance(ref clsid, IntPtr.Zero, 1, ref iid, out _taskBarList); - - if (_taskBarList != IntPtr.Zero) - { - var ptr = (ITaskBarList2VTable**)_taskBarList.ToPointer(); - - var hrInit = Marshal.GetDelegateForFunctionPointer((*ptr)->HrInit); - - if (hrInit(_taskBarList) != HRESULT.S_OK) - { - _taskBarList = IntPtr.Zero; - } - } - } - - if (_taskBarList != IntPtr.Zero) - { - var ptr = (ITaskBarList2VTable**)_taskBarList.ToPointer(); - var markFullscreen = Marshal.GetDelegateForFunctionPointer((*ptr)->MarkFullscreenWindow); - markFullscreen(_taskBarList, _hwnd, fullscreen); - } - } - /// /// Ported from https://github.com/chromium/chromium/blob/master/ui/views/win/fullscreen_handler.cc /// Method must only be called from inside UpdateWindowProperties. @@ -634,7 +599,7 @@ namespace Avalonia.Win32 UpdateWindowProperties(_windowProperties, true); } - MarkFullscreen(fullscreen); + TaskBarList.MarkFullscreen(_hwnd, fullscreen); } private void ShowWindow(WindowState state) From 4d6166e3cc3975f53724d276016b4fc9986c1921 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Tue, 5 May 2020 17:01:19 -0300 Subject: [PATCH 41/47] make class internal. --- src/Windows/Avalonia.Win32/Win32TypeExtensions.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Windows/Avalonia.Win32/Win32TypeExtensions.cs b/src/Windows/Avalonia.Win32/Win32TypeExtensions.cs index 0e9daf3137..8193611f6d 100644 --- a/src/Windows/Avalonia.Win32/Win32TypeExtensions.cs +++ b/src/Windows/Avalonia.Win32/Win32TypeExtensions.cs @@ -2,9 +2,9 @@ namespace Avalonia.Win32 { - public static class Win32TypeExtensions + internal static class Win32TypeExtensions { - internal static PixelRect ToPixelRect(this RECT rect) + public static PixelRect ToPixelRect(this RECT rect) { return new PixelRect(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top); From 0608ad6008d2032311cc1be03405bfb33f9ab537 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Tue, 5 May 2020 17:52:09 -0300 Subject: [PATCH 42/47] prevent restoring window styles from overwriting windowstates. --- src/Windows/Avalonia.Win32/WindowImpl.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 51d60cc970..a32bc7d9b3 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -586,7 +586,8 @@ namespace Avalonia.Win32 // repainted. Better-looking methods welcome. _isFullScreenActive = false; - SetStyle(_savedWindowInfo.Style, false); + var windowStates = GetWindowStateStyles(); + SetStyle((_savedWindowInfo.Style & ~WindowStateMask) | windowStates, false); SetExtendedStyle(_savedWindowInfo.ExStyle, false); // On restore, resize to the previous saved rect size. @@ -668,6 +669,13 @@ namespace Avalonia.Win32 } } + private const WindowStyles WindowStateMask = (WindowStyles.WS_MAXIMIZE | WindowStyles.WS_MINIMIZE); + + private WindowStyles GetWindowStateStyles () + { + return GetStyle() & WindowStateMask; + } + private WindowStyles GetStyle() { if (_isFullScreenActive) From 0cae901e2e0d7d4b8306c7d91eea1932acbe2739 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Tue, 5 May 2020 17:56:33 -0300 Subject: [PATCH 43/47] move const declaration --- src/Windows/Avalonia.Win32/WindowImpl.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index a32bc7d9b3..d82e9f64f1 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -44,7 +44,7 @@ namespace Avalonia.Win32 private readonly ManagedWindowResizeDragHelper _managedDrag; #endif - + private const WindowStyles WindowStateMask = (WindowStyles.WS_MAXIMIZE | WindowStyles.WS_MINIMIZE); private readonly List _disabledBy; private readonly TouchDevice _touchDevice; private readonly MouseDevice _mouseDevice; @@ -667,9 +667,7 @@ namespace Avalonia.Win32 SetWindowPos(_hwnd, WindowPosZOrder.HWND_NOTOPMOST, x, y, cx, cy, SetWindowPosFlags.SWP_SHOWWINDOW); } } - } - - private const WindowStyles WindowStateMask = (WindowStyles.WS_MAXIMIZE | WindowStyles.WS_MINIMIZE); + } private WindowStyles GetWindowStateStyles () { From 20754f9291efd990ab6bc42afcf08e33314d46a3 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Wed, 6 May 2020 00:08:19 +0300 Subject: [PATCH 44/47] [X11] Remove BorderOnly support for now since it doesn't work anyway --- src/Avalonia.X11/X11Window.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs index c82aabd4c9..026a1457a3 100644 --- a/src/Avalonia.X11/X11Window.cs +++ b/src/Avalonia.X11/X11Window.cs @@ -221,11 +221,10 @@ namespace Avalonia.X11 MotifDecorations.Maximize | MotifDecorations.Minimize | MotifDecorations.ResizeH; if (_popup - || _systemDecorations == SystemDecorations.None - || _systemDecorations == SystemDecorations.BorderOnly) + || _systemDecorations == SystemDecorations.None) decorations = 0; - if (!_canResize || _systemDecorations == SystemDecorations.BorderOnly) + if (!_canResize) { functions &= ~(MotifFunctions.Resize | MotifFunctions.Maximize); decorations &= ~(MotifDecorations.Maximize | MotifDecorations.ResizeH); @@ -248,7 +247,7 @@ namespace Avalonia.X11 var min = _minMaxSize.minSize; var max = _minMaxSize.maxSize; - if (!_canResize || _systemDecorations == SystemDecorations.BorderOnly) + if (!_canResize) max = min = _realSize; if (preResize.HasValue) @@ -821,7 +820,7 @@ namespace Avalonia.X11 public void SetSystemDecorations(SystemDecorations enabled) { - _systemDecorations = enabled; + _systemDecorations = enabled == SystemDecorations.Full ? SystemDecorations.Full : SystemDecorations.None; UpdateMotifHints(); UpdateSizeHints(null); } From 33dca75d4fc521a12e9f4f1696e61bd8dd8e6e02 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 6 May 2020 13:53:49 -0300 Subject: [PATCH 45/47] Allow user to set GPU VMem limit. --- src/Skia/Avalonia.Skia/PlatformRenderInterface.cs | 10 +++++++++- src/Skia/Avalonia.Skia/SkiaOptions.cs | 9 +++++++++ src/Skia/Avalonia.Skia/SkiaPlatform.cs | 2 +- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs index 46872f903e..0518e25286 100644 --- a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs +++ b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs @@ -18,7 +18,7 @@ namespace Avalonia.Skia private GRContext GrContext { get; } - public PlatformRenderInterface(ICustomSkiaGpu customSkiaGpu) + public PlatformRenderInterface(ICustomSkiaGpu customSkiaGpu, long maxResourceBytes) { if (customSkiaGpu != null) { @@ -26,6 +26,10 @@ namespace Avalonia.Skia GrContext = _customSkiaGpu.GrContext; + GrContext.GetResourceCacheLimits(out var maxResources, out _); + + GrContext.SetResourceCacheLimits(maxResources, maxResourceBytes); + return; } @@ -39,6 +43,10 @@ namespace Avalonia.Skia : GRGlInterface.AssembleGlesInterface((_, proc) => display.GlInterface.GetProcAddress(proc))) { GrContext = GRContext.Create(GRBackend.OpenGL, iface); + + GrContext.GetResourceCacheLimits(out var maxResources, out _); + + GrContext.SetResourceCacheLimits(maxResources, maxResourceBytes); } display.ClearContext(); } diff --git a/src/Skia/Avalonia.Skia/SkiaOptions.cs b/src/Skia/Avalonia.Skia/SkiaOptions.cs index f4b8fe3c1d..b180466c4e 100644 --- a/src/Skia/Avalonia.Skia/SkiaOptions.cs +++ b/src/Skia/Avalonia.Skia/SkiaOptions.cs @@ -8,9 +8,18 @@ namespace Avalonia /// public class SkiaOptions { + public SkiaOptions() + { + MaxGpuResourceSizeBytes = 512000000; + } /// /// Custom gpu factory to use. Can be used to customize behavior of Skia renderer. /// public Func CustomGpuFactory { get; set; } + + /// + /// The maximum number of bytes for video memory to store textures and resources. + /// + public long MaxGpuResourceSizeBytes { get; set; } } } diff --git a/src/Skia/Avalonia.Skia/SkiaPlatform.cs b/src/Skia/Avalonia.Skia/SkiaPlatform.cs index b4b340d24d..9a5725e06f 100644 --- a/src/Skia/Avalonia.Skia/SkiaPlatform.cs +++ b/src/Skia/Avalonia.Skia/SkiaPlatform.cs @@ -18,7 +18,7 @@ namespace Avalonia.Skia public static void Initialize(SkiaOptions options) { var customGpu = options.CustomGpuFactory?.Invoke(); - var renderInterface = new PlatformRenderInterface(customGpu); + var renderInterface = new PlatformRenderInterface(customGpu, options.MaxGpuResourceSizeBytes); AvaloniaLocator.CurrentMutable .Bind().ToConstant(renderInterface) From 4033dff3daf3b0ae2641491bfee79685604713d3 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 6 May 2020 14:07:13 -0300 Subject: [PATCH 46/47] add default argument. --- src/Skia/Avalonia.Skia/PlatformRenderInterface.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs index 0518e25286..f6d77e1045 100644 --- a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs +++ b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs @@ -18,7 +18,7 @@ namespace Avalonia.Skia private GRContext GrContext { get; } - public PlatformRenderInterface(ICustomSkiaGpu customSkiaGpu, long maxResourceBytes) + public PlatformRenderInterface(ICustomSkiaGpu customSkiaGpu, long maxResourceBytes = 100000000) { if (customSkiaGpu != null) { From e91238965a5f5a26864259338bb9c9187a34672d Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 6 May 2020 17:38:03 -0300 Subject: [PATCH 47/47] 100mb default gpu resource graphics memory --- src/Skia/Avalonia.Skia/PlatformRenderInterface.cs | 2 +- src/Skia/Avalonia.Skia/SkiaOptions.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs index f6d77e1045..d277267d5d 100644 --- a/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs +++ b/src/Skia/Avalonia.Skia/PlatformRenderInterface.cs @@ -18,7 +18,7 @@ namespace Avalonia.Skia private GRContext GrContext { get; } - public PlatformRenderInterface(ICustomSkiaGpu customSkiaGpu, long maxResourceBytes = 100000000) + public PlatformRenderInterface(ICustomSkiaGpu customSkiaGpu, long maxResourceBytes = 100663296) { if (customSkiaGpu != null) { diff --git a/src/Skia/Avalonia.Skia/SkiaOptions.cs b/src/Skia/Avalonia.Skia/SkiaOptions.cs index b180466c4e..2d5e5990e6 100644 --- a/src/Skia/Avalonia.Skia/SkiaOptions.cs +++ b/src/Skia/Avalonia.Skia/SkiaOptions.cs @@ -10,7 +10,7 @@ namespace Avalonia { public SkiaOptions() { - MaxGpuResourceSizeBytes = 512000000; + MaxGpuResourceSizeBytes = 100663296; // Value taken from skia. } /// /// Custom gpu factory to use. Can be used to customize behavior of Skia renderer.