From a7ace8f57bad9dfdb153348d85311d65a0a9af17 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Wed, 15 Dec 2021 14:27:42 +0100 Subject: [PATCH 01/12] Add more platform specific screen from X methods. --- src/Avalonia.Controls/Platform/IScreenImpl.cs | 8 +++ src/Avalonia.Controls/Screens.cs | 51 ++++++++++++++----- src/Avalonia.Controls/Window.cs | 2 +- src/Avalonia.DesignerSupport/Remote/Stubs.cs | 15 ++++++ .../HeadlessPlatformStubs.cs | 15 ++++++ src/Avalonia.Native/ScreenImpl.cs | 15 ++++++ src/Avalonia.X11/X11Screens.cs | 15 ++++++ src/Web/Avalonia.Web.Blazor/WinStubs.cs | 15 ++++++ src/Windows/Avalonia.Win32/ScreenImpl.cs | 39 ++++++++++++++ src/Windows/Avalonia.Win32/WinScreen.cs | 6 ++- 10 files changed, 164 insertions(+), 17 deletions(-) diff --git a/src/Avalonia.Controls/Platform/IScreenImpl.cs b/src/Avalonia.Controls/Platform/IScreenImpl.cs index 5bd45057d9..b68391aa52 100644 --- a/src/Avalonia.Controls/Platform/IScreenImpl.cs +++ b/src/Avalonia.Controls/Platform/IScreenImpl.cs @@ -1,5 +1,7 @@ using System.Collections.Generic; +#nullable enable + namespace Avalonia.Platform { public interface IScreenImpl @@ -7,5 +9,11 @@ namespace Avalonia.Platform int ScreenCount { get; } IReadOnlyList AllScreens { get; } + + Screen? ScreenFromWindow(IWindowBaseImpl window); + + Screen? ScreenFromPoint(PixelPoint point); + + Screen? ScreenFromRect(PixelRect rect); } } diff --git a/src/Avalonia.Controls/Screens.cs b/src/Avalonia.Controls/Screens.cs index 8a0a0fa728..786502361c 100644 --- a/src/Avalonia.Controls/Screens.cs +++ b/src/Avalonia.Controls/Screens.cs @@ -20,30 +20,53 @@ namespace Avalonia.Controls _iScreenImpl = iScreenImpl; } - public Screen ScreenFromBounds(PixelRect bounds){ - - Screen currMaxScreen = null; - double maxAreaSize = 0; - foreach (Screen screen in All) + public Screen ScreenFromBounds(PixelRect bounds) + { + Screen currMaxScreen = _iScreenImpl.ScreenFromRect(bounds); + + if (currMaxScreen == null) { - double left = MathUtilities.Clamp(bounds.X, screen.Bounds.X, screen.Bounds.X + screen.Bounds.Width); - double top = MathUtilities.Clamp(bounds.Y, screen.Bounds.Y, screen.Bounds.Y + screen.Bounds.Height); - double right = MathUtilities.Clamp(bounds.X + bounds.Width, screen.Bounds.X, screen.Bounds.X + screen.Bounds.Width); - double bottom = MathUtilities.Clamp(bounds.Y + bounds.Height, screen.Bounds.Y, screen.Bounds.Y + screen.Bounds.Height); - double area = (right - left) * (bottom - top); - if (area > maxAreaSize) + double maxAreaSize = 0; + foreach (Screen screen in All) { - maxAreaSize = area; - currMaxScreen = screen; + double left = MathUtilities.Clamp(bounds.X, screen.Bounds.X, screen.Bounds.X + screen.Bounds.Width); + double top = MathUtilities.Clamp(bounds.Y, screen.Bounds.Y, screen.Bounds.Y + screen.Bounds.Height); + double right = MathUtilities.Clamp(bounds.X + bounds.Width, screen.Bounds.X, screen.Bounds.X + screen.Bounds.Width); + double bottom = MathUtilities.Clamp(bounds.Y + bounds.Height, screen.Bounds.Y, screen.Bounds.Y + screen.Bounds.Height); + double area = (right - left) * (bottom - top); + if (area > maxAreaSize) + { + maxAreaSize = area; + currMaxScreen = screen; + } } } return currMaxScreen; } + public Screen ScreenFromWindow(IWindowBaseImpl window) + { + var screen = _iScreenImpl.ScreenFromWindow(window); + + if (screen == null && window.Position is { } position) + { + screen = ScreenFromPoint(position); + } + + return screen; + } + public Screen ScreenFromPoint(PixelPoint point) { - return All.FirstOrDefault(x => x.Bounds.Contains(point)); + var screen = _iScreenImpl.ScreenFromPoint(point); + + if (screen == null) + { + screen = All.FirstOrDefault(x => x.Bounds.Contains(point)); + } + + return screen; } public Screen ScreenFromVisual(IVisual visual) diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs index 4c94b725ea..ca325229cc 100644 --- a/src/Avalonia.Controls/Window.cs +++ b/src/Avalonia.Controls/Window.cs @@ -863,7 +863,7 @@ namespace Avalonia.Controls if (WindowStartupLocation == WindowStartupLocation.CenterScreen) { - var screen = Screens.ScreenFromPoint(owner?.Position ?? Position); + var screen = Screens.ScreenFromWindow(owner); if (screen != null) { diff --git a/src/Avalonia.DesignerSupport/Remote/Stubs.cs b/src/Avalonia.DesignerSupport/Remote/Stubs.cs index 9dcd4d8e87..b6988a27a6 100644 --- a/src/Avalonia.DesignerSupport/Remote/Stubs.cs +++ b/src/Avalonia.DesignerSupport/Remote/Stubs.cs @@ -236,5 +236,20 @@ namespace Avalonia.DesignerSupport.Remote public IReadOnlyList AllScreens { get; } = new Screen[] { new Screen(1, new PixelRect(0, 0, 4000, 4000), new PixelRect(0, 0, 4000, 4000), true) }; + + public Screen ScreenFromPoint(PixelPoint point) + { + return null; + } + + public Screen ScreenFromRect(PixelRect rect) + { + return null; + } + + public Screen ScreenFromWindow(IWindowBaseImpl window) + { + return null; + } } } diff --git a/src/Avalonia.Headless/HeadlessPlatformStubs.cs b/src/Avalonia.Headless/HeadlessPlatformStubs.cs index 605659d464..9318ae3d59 100644 --- a/src/Avalonia.Headless/HeadlessPlatformStubs.cs +++ b/src/Avalonia.Headless/HeadlessPlatformStubs.cs @@ -203,5 +203,20 @@ namespace Avalonia.Headless new Screen(1, new PixelRect(0, 0, 1920, 1280), new PixelRect(0, 0, 1920, 1280), true), }; + + public Screen ScreenFromPoint(PixelPoint point) + { + return null; + } + + public Screen ScreenFromRect(PixelRect rect) + { + return null; + } + + public Screen ScreenFromWindow(IWindowBaseImpl window) + { + return null; + } } } diff --git a/src/Avalonia.Native/ScreenImpl.cs b/src/Avalonia.Native/ScreenImpl.cs index 7b4a001486..03f9b438b4 100644 --- a/src/Avalonia.Native/ScreenImpl.cs +++ b/src/Avalonia.Native/ScreenImpl.cs @@ -48,5 +48,20 @@ namespace Avalonia.Native _native?.Dispose(); _native = null; } + + public Screen ScreenFromPoint(PixelPoint point) + { + return null; + } + + public Screen ScreenFromRect(PixelRect rect) + { + return null; + } + + public Screen ScreenFromWindow(IWindowBaseImpl window) + { + return null; + } } } diff --git a/src/Avalonia.X11/X11Screens.cs b/src/Avalonia.X11/X11Screens.cs index bf5c74e0e5..906f4af64e 100644 --- a/src/Avalonia.X11/X11Screens.cs +++ b/src/Avalonia.X11/X11Screens.cs @@ -200,6 +200,21 @@ namespace Avalonia.X11 } + public Screen ScreenFromWindow(IWindowBaseImpl window) + { + return null; + } + + public Screen ScreenFromPoint(PixelPoint point) + { + return null; + } + + public Screen ScreenFromRect(PixelRect rect) + { + return null; + } + public int ScreenCount => _impl.Screens.Length; public IReadOnlyList AllScreens => diff --git a/src/Web/Avalonia.Web.Blazor/WinStubs.cs b/src/Web/Avalonia.Web.Blazor/WinStubs.cs index 7b2bff6bfd..17c1bca138 100644 --- a/src/Web/Avalonia.Web.Blazor/WinStubs.cs +++ b/src/Web/Avalonia.Web.Blazor/WinStubs.cs @@ -55,5 +55,20 @@ namespace Avalonia.Web.Blazor public IReadOnlyList AllScreens { get; } = new[] { new Screen(96, new PixelRect(0, 0, 4000, 4000), new PixelRect(0, 0, 4000, 4000), true) }; + + public Screen? ScreenFromPoint(PixelPoint point) + { + return null; + } + + public Screen? ScreenFromRect(PixelRect rect) + { + return null; + } + + public Screen? ScreenFromWindow(IWindowBaseImpl window) + { + return null; + } } } diff --git a/src/Windows/Avalonia.Win32/ScreenImpl.cs b/src/Windows/Avalonia.Win32/ScreenImpl.cs index 442794f0f0..5942208594 100644 --- a/src/Windows/Avalonia.Win32/ScreenImpl.cs +++ b/src/Windows/Avalonia.Win32/ScreenImpl.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using Avalonia.Platform; using Avalonia.Win32.Interop; using static Avalonia.Win32.Interop.UnmanagedMethods; @@ -70,5 +71,43 @@ namespace Avalonia.Win32 { _allScreens = null; } + + public Screen ScreenFromWindow(IWindowBaseImpl window) + { + var handle = window.Handle.Handle; + + var monitor = MonitorFromWindow(handle, MONITOR.MONITOR_DEFAULTTONEAREST); + + return FindScreenByHandle(monitor); + } + + public Screen ScreenFromPoint(PixelPoint point) + { + var monitor = MonitorFromPoint(new POINT + { + X = point.X, + Y = point.Y + }, MONITOR.MONITOR_DEFAULTTONEAREST); + + return FindScreenByHandle(monitor); + } + + public Screen ScreenFromRect(PixelRect rect) + { + var monitor = MonitorFromRect(new RECT + { + left = rect.TopLeft.X, + top = rect.TopLeft.Y, + right = rect.TopRight.X, + bottom = rect.BottomRight.Y + }, MONITOR.MONITOR_DEFAULTTONEAREST); + + return FindScreenByHandle(monitor); + } + + private Screen FindScreenByHandle(IntPtr handle) + { + return AllScreens.Cast().FirstOrDefault(m => m.Handle == handle); + } } } diff --git a/src/Windows/Avalonia.Win32/WinScreen.cs b/src/Windows/Avalonia.Win32/WinScreen.cs index 0cf9fe31db..f103cc3b66 100644 --- a/src/Windows/Avalonia.Win32/WinScreen.cs +++ b/src/Windows/Avalonia.Win32/WinScreen.cs @@ -9,9 +9,11 @@ namespace Avalonia.Win32 public WinScreen(double pixelDensity, PixelRect bounds, PixelRect workingArea, bool primary, IntPtr hMonitor) : base(pixelDensity, bounds, workingArea, primary) { - this._hMonitor = hMonitor; + _hMonitor = hMonitor; } + public IntPtr Handle => _hMonitor; + public override int GetHashCode() { return (int)_hMonitor; @@ -19,7 +21,7 @@ namespace Avalonia.Win32 public override bool Equals(object obj) { - return (obj is WinScreen screen) ? this._hMonitor == screen._hMonitor : base.Equals(obj); + return (obj is WinScreen screen) ? _hMonitor == screen._hMonitor : base.Equals(obj); } } } From 11ebcd176208ff08e47427cf039c901b8c9c6d67 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Wed, 15 Dec 2021 14:36:36 +0100 Subject: [PATCH 02/12] Add fallback for window position. --- src/Avalonia.Controls/Window.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs index ca325229cc..3923787ab5 100644 --- a/src/Avalonia.Controls/Window.cs +++ b/src/Avalonia.Controls/Window.cs @@ -865,6 +865,11 @@ namespace Avalonia.Controls { var screen = Screens.ScreenFromWindow(owner); + if (screen == null) + { + screen = Screens.ScreenFromPoint(Position); + } + if (screen != null) { Position = screen.WorkingArea.CenterRect(rect).Position; From 6986f0d6689ca0c3bd95e70862c660f1c4a64610 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Mon, 3 Jan 2022 12:37:44 +0100 Subject: [PATCH 03/12] Implement screen helper to simply cross platform screens implementation. --- .../Platform/ScreenHelper.cs | 54 ++++++++++++++++ src/Avalonia.Controls/Screens.cs | 64 +++++-------------- src/Avalonia.Controls/Window.cs | 11 +++- src/Avalonia.DesignerSupport/Remote/Stubs.cs | 6 +- .../HeadlessPlatformStubs.cs | 6 +- src/Avalonia.Native/ScreenImpl.cs | 6 +- src/Avalonia.X11/X11Screens.cs | 12 ++-- src/Web/Avalonia.Web.Blazor/WinStubs.cs | 6 +- src/Windows/Avalonia.Win32/ScreenImpl.cs | 6 +- 9 files changed, 100 insertions(+), 71 deletions(-) create mode 100644 src/Avalonia.Controls/Platform/ScreenHelper.cs diff --git a/src/Avalonia.Controls/Platform/ScreenHelper.cs b/src/Avalonia.Controls/Platform/ScreenHelper.cs new file mode 100644 index 0000000000..07affb5ecc --- /dev/null +++ b/src/Avalonia.Controls/Platform/ScreenHelper.cs @@ -0,0 +1,54 @@ +using System.Collections.Generic; +using Avalonia.Utilities; + +#nullable enable + +namespace Avalonia.Platform +{ + public static class ScreenHelper + { + public static Screen? ScreenFromPoint(PixelPoint point, IReadOnlyList screens) + { + foreach (Screen screen in screens) + { + if (screen.Bounds.Contains(point)) + { + return screen; + } + } + + return null; + } + + public static Screen? ScreenFromRect(PixelRect bounds, IReadOnlyList screens) + { + Screen? currMaxScreen = null; + double maxAreaSize = 0; + + foreach (Screen screen in screens) + { + double left = MathUtilities.Clamp(bounds.X, screen.Bounds.X, screen.Bounds.X + screen.Bounds.Width); + double top = MathUtilities.Clamp(bounds.Y, screen.Bounds.Y, screen.Bounds.Y + screen.Bounds.Height); + double right = MathUtilities.Clamp(bounds.X + bounds.Width, screen.Bounds.X, screen.Bounds.X + screen.Bounds.Width); + double bottom = MathUtilities.Clamp(bounds.Y + bounds.Height, screen.Bounds.Y, screen.Bounds.Y + screen.Bounds.Height); + double area = (right - left) * (bottom - top); + if (area > maxAreaSize) + { + maxAreaSize = area; + currMaxScreen = screen; + } + } + + return currMaxScreen; + } + + public static Screen? ScreenFromWindow(IWindowBaseImpl window, IReadOnlyList screens) + { + var rect = new PixelRect( + window.Position, + PixelSize.FromSize(window.FrameSize ?? window.ClientSize, window.DesktopScaling)); + + return ScreenFromRect(rect, screens); + } + } +} diff --git a/src/Avalonia.Controls/Screens.cs b/src/Avalonia.Controls/Screens.cs index 786502361c..cf4f360cb5 100644 --- a/src/Avalonia.Controls/Screens.cs +++ b/src/Avalonia.Controls/Screens.cs @@ -2,77 +2,45 @@ using System.Collections.Generic; using System.Linq; using Avalonia.Platform; -using Avalonia.Utilities; using Avalonia.VisualTree; +#nullable enable + namespace Avalonia.Controls { public class Screens { - private readonly IScreenImpl _iScreenImpl; + private readonly IScreenImpl _impl; - public int ScreenCount => _iScreenImpl.ScreenCount; - public IReadOnlyList All => _iScreenImpl?.AllScreens ?? Array.Empty(); - public Screen Primary => All.FirstOrDefault(x => x.Primary); + public int ScreenCount => _impl.ScreenCount; + public IReadOnlyList All => _impl?.AllScreens ?? Array.Empty(); + public Screen? Primary => All.FirstOrDefault(x => x.Primary); - public Screens(IScreenImpl iScreenImpl) + public Screens(IScreenImpl impl) { - _iScreenImpl = iScreenImpl; + _impl = impl; } - public Screen ScreenFromBounds(PixelRect bounds) + public Screen? ScreenFromBounds(PixelRect bounds) { - Screen currMaxScreen = _iScreenImpl.ScreenFromRect(bounds); - - if (currMaxScreen == null) - { - double maxAreaSize = 0; - foreach (Screen screen in All) - { - double left = MathUtilities.Clamp(bounds.X, screen.Bounds.X, screen.Bounds.X + screen.Bounds.Width); - double top = MathUtilities.Clamp(bounds.Y, screen.Bounds.Y, screen.Bounds.Y + screen.Bounds.Height); - double right = MathUtilities.Clamp(bounds.X + bounds.Width, screen.Bounds.X, screen.Bounds.X + screen.Bounds.Width); - double bottom = MathUtilities.Clamp(bounds.Y + bounds.Height, screen.Bounds.Y, screen.Bounds.Y + screen.Bounds.Height); - double area = (right - left) * (bottom - top); - if (area > maxAreaSize) - { - maxAreaSize = area; - currMaxScreen = screen; - } - } - } - - return currMaxScreen; + return _impl.ScreenFromRect(bounds); } - public Screen ScreenFromWindow(IWindowBaseImpl window) + public Screen? ScreenFromWindow(IWindowBaseImpl window) { - var screen = _iScreenImpl.ScreenFromWindow(window); - - if (screen == null && window.Position is { } position) - { - screen = ScreenFromPoint(position); - } - - return screen; + return _impl.ScreenFromWindow(window); } - public Screen ScreenFromPoint(PixelPoint point) + public Screen? ScreenFromPoint(PixelPoint point) { - var screen = _iScreenImpl.ScreenFromPoint(point); - - if (screen == null) - { - screen = All.FirstOrDefault(x => x.Bounds.Contains(point)); - } - - return screen; + return _impl.ScreenFromPoint(point); } - public Screen ScreenFromVisual(IVisual visual) + public Screen? ScreenFromVisual(IVisual visual) { var tl = visual.PointToScreen(visual.Bounds.TopLeft); var br = visual.PointToScreen(visual.Bounds.BottomRight); + return ScreenFromBounds(new PixelRect(tl, br)); } } diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs index 3923787ab5..10e4b65982 100644 --- a/src/Avalonia.Controls/Window.cs +++ b/src/Avalonia.Controls/Window.cs @@ -863,9 +863,16 @@ namespace Avalonia.Controls if (WindowStartupLocation == WindowStartupLocation.CenterScreen) { - var screen = Screens.ScreenFromWindow(owner); + Screen screen = null; - if (screen == null) + if (owner is not null) + { + screen = Screens.ScreenFromWindow(owner); + + screen ??= Screens.ScreenFromPoint(owner.Position); + } + + if (screen is null) { screen = Screens.ScreenFromPoint(Position); } diff --git a/src/Avalonia.DesignerSupport/Remote/Stubs.cs b/src/Avalonia.DesignerSupport/Remote/Stubs.cs index b6988a27a6..ab1b918656 100644 --- a/src/Avalonia.DesignerSupport/Remote/Stubs.cs +++ b/src/Avalonia.DesignerSupport/Remote/Stubs.cs @@ -239,17 +239,17 @@ namespace Avalonia.DesignerSupport.Remote public Screen ScreenFromPoint(PixelPoint point) { - return null; + return ScreenHelper.ScreenFromPoint(point, AllScreens); } public Screen ScreenFromRect(PixelRect rect) { - return null; + return ScreenHelper.ScreenFromRect(rect, AllScreens); } public Screen ScreenFromWindow(IWindowBaseImpl window) { - return null; + return ScreenHelper.ScreenFromWindow(window, AllScreens); } } } diff --git a/src/Avalonia.Headless/HeadlessPlatformStubs.cs b/src/Avalonia.Headless/HeadlessPlatformStubs.cs index 9318ae3d59..6b97056882 100644 --- a/src/Avalonia.Headless/HeadlessPlatformStubs.cs +++ b/src/Avalonia.Headless/HeadlessPlatformStubs.cs @@ -206,17 +206,17 @@ namespace Avalonia.Headless public Screen ScreenFromPoint(PixelPoint point) { - return null; + return ScreenHelper.ScreenFromPoint(point, AllScreens); } public Screen ScreenFromRect(PixelRect rect) { - return null; + return ScreenHelper.ScreenFromRect(rect, AllScreens); } public Screen ScreenFromWindow(IWindowBaseImpl window) { - return null; + return ScreenHelper.ScreenFromWindow(window, AllScreens); } } } diff --git a/src/Avalonia.Native/ScreenImpl.cs b/src/Avalonia.Native/ScreenImpl.cs index 03f9b438b4..83db2e8a28 100644 --- a/src/Avalonia.Native/ScreenImpl.cs +++ b/src/Avalonia.Native/ScreenImpl.cs @@ -51,17 +51,17 @@ namespace Avalonia.Native public Screen ScreenFromPoint(PixelPoint point) { - return null; + return ScreenHelper.ScreenFromPoint(point, AllScreens); } public Screen ScreenFromRect(PixelRect rect) { - return null; + return ScreenHelper.ScreenFromRect(rect, AllScreens); } public Screen ScreenFromWindow(IWindowBaseImpl window) { - return null; + return ScreenHelper.ScreenFromWindow(window, AllScreens); } } } diff --git a/src/Avalonia.X11/X11Screens.cs b/src/Avalonia.X11/X11Screens.cs index 906f4af64e..bcaafb6a53 100644 --- a/src/Avalonia.X11/X11Screens.cs +++ b/src/Avalonia.X11/X11Screens.cs @@ -200,19 +200,19 @@ namespace Avalonia.X11 } - public Screen ScreenFromWindow(IWindowBaseImpl window) + public Screen ScreenFromPoint(PixelPoint point) { - return null; + return ScreenHelper.ScreenFromPoint(point, AllScreens); } - public Screen ScreenFromPoint(PixelPoint point) + public Screen ScreenFromRect(PixelRect rect) { - return null; + return ScreenHelper.ScreenFromRect(rect, AllScreens); } - public Screen ScreenFromRect(PixelRect rect) + public Screen ScreenFromWindow(IWindowBaseImpl window) { - return null; + return ScreenHelper.ScreenFromWindow(window, AllScreens); } public int ScreenCount => _impl.Screens.Length; diff --git a/src/Web/Avalonia.Web.Blazor/WinStubs.cs b/src/Web/Avalonia.Web.Blazor/WinStubs.cs index 17c1bca138..7c30a96d35 100644 --- a/src/Web/Avalonia.Web.Blazor/WinStubs.cs +++ b/src/Web/Avalonia.Web.Blazor/WinStubs.cs @@ -58,17 +58,17 @@ namespace Avalonia.Web.Blazor public Screen? ScreenFromPoint(PixelPoint point) { - return null; + return ScreenHelper.ScreenFromPoint(point, AllScreens); } public Screen? ScreenFromRect(PixelRect rect) { - return null; + return ScreenHelper.ScreenFromRect(rect, AllScreens); } public Screen? ScreenFromWindow(IWindowBaseImpl window) { - return null; + return ScreenHelper.ScreenFromWindow(window, AllScreens); } } } diff --git a/src/Windows/Avalonia.Win32/ScreenImpl.cs b/src/Windows/Avalonia.Win32/ScreenImpl.cs index 5942208594..96e45927da 100644 --- a/src/Windows/Avalonia.Win32/ScreenImpl.cs +++ b/src/Windows/Avalonia.Win32/ScreenImpl.cs @@ -76,7 +76,7 @@ namespace Avalonia.Win32 { var handle = window.Handle.Handle; - var monitor = MonitorFromWindow(handle, MONITOR.MONITOR_DEFAULTTONEAREST); + var monitor = MonitorFromWindow(handle, MONITOR.MONITOR_DEFAULTTONULL); return FindScreenByHandle(monitor); } @@ -87,7 +87,7 @@ namespace Avalonia.Win32 { X = point.X, Y = point.Y - }, MONITOR.MONITOR_DEFAULTTONEAREST); + }, MONITOR.MONITOR_DEFAULTTONULL); return FindScreenByHandle(monitor); } @@ -100,7 +100,7 @@ namespace Avalonia.Win32 top = rect.TopLeft.Y, right = rect.TopRight.X, bottom = rect.BottomRight.Y - }, MONITOR.MONITOR_DEFAULTTONEAREST); + }, MONITOR.MONITOR_DEFAULTTONULL); return FindScreenByHandle(monitor); } From 84ead6d0f4b8ebfba4c22423e54ca90fc128eb09 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 15 Jan 2022 00:24:48 +0100 Subject: [PATCH 04/12] Update ApiCompatBaseline.txt --- src/Avalonia.Controls/ApiCompatBaseline.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/ApiCompatBaseline.txt b/src/Avalonia.Controls/ApiCompatBaseline.txt index 9b7d37e108..b019645131 100644 --- a/src/Avalonia.Controls/ApiCompatBaseline.txt +++ b/src/Avalonia.Controls/ApiCompatBaseline.txt @@ -44,6 +44,9 @@ MembersMustExist : Member 'public Avalonia.AvaloniaProperty Avalonia.AvaloniaPro InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Controls.Platform.ITopLevelNativeMenuExporter.SetNativeMenu(Avalonia.Controls.NativeMenu)' is present in the contract but not in the implementation. CannotRemoveBaseTypeOrInterface : Type 'Avalonia.Controls.Primitives.PopupRoot' does not implement interface 'Avalonia.Utilities.IWeakSubscriber' in the implementation but it does in the contract. EnumValuesMustMatch : Enum value 'Avalonia.Platform.ExtendClientAreaChromeHints Avalonia.Platform.ExtendClientAreaChromeHints.Default' is (System.Int32)2 in the implementation but (System.Int32)1 in the contract. +InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.Screen Avalonia.Platform.IScreenImpl.ScreenFromPoint(Avalonia.PixelPoint)' is present in the implementation but not in the contract. +InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.Screen Avalonia.Platform.IScreenImpl.ScreenFromRect(Avalonia.PixelRect)' is present in the implementation but not in the contract. +InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.Screen Avalonia.Platform.IScreenImpl.ScreenFromWindow(Avalonia.Platform.IWindowBaseImpl)' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public System.Nullable Avalonia.Platform.ITopLevelImpl.FrameSize' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public System.Nullable Avalonia.Platform.ITopLevelImpl.FrameSize.get()' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public System.Action Avalonia.Platform.ITopLevelImpl.Resized.get()' is present in the implementation but not in the contract. @@ -62,4 +65,4 @@ InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platfor MembersMustExist : Member 'public void Avalonia.Platform.IWindowImpl.Resize(Avalonia.Size)' does not exist in the implementation but it does exist in the contract. InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.IWindowImpl.Resize(Avalonia.Size, Avalonia.Platform.PlatformResizeReason)' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.ITrayIconImpl Avalonia.Platform.IWindowingPlatform.CreateTrayIcon()' is present in the implementation but not in the contract. -Total Issues: 63 +Total Issues: 66 From a19dc794a57f63444094c83511f4bac3a0a55e2c Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Tue, 8 Mar 2022 11:58:26 +0000 Subject: [PATCH 05/12] add contains exclusive. --- src/Avalonia.Controls/Platform/ScreenHelper.cs | 2 +- src/Avalonia.Visuals/Rect.cs | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Platform/ScreenHelper.cs b/src/Avalonia.Controls/Platform/ScreenHelper.cs index 07affb5ecc..0bd2be69d0 100644 --- a/src/Avalonia.Controls/Platform/ScreenHelper.cs +++ b/src/Avalonia.Controls/Platform/ScreenHelper.cs @@ -11,7 +11,7 @@ namespace Avalonia.Platform { foreach (Screen screen in screens) { - if (screen.Bounds.Contains(point)) + if (screen.Bounds.ContainsExclusive(point)) { return screen; } diff --git a/src/Avalonia.Visuals/Rect.cs b/src/Avalonia.Visuals/Rect.cs index 6d7d6c2e54..7930228d99 100644 --- a/src/Avalonia.Visuals/Rect.cs +++ b/src/Avalonia.Visuals/Rect.cs @@ -252,6 +252,18 @@ namespace Avalonia return p.X >= _x && p.X <= _x + _width && p.Y >= _y && p.Y <= _y + _height; } + + /// + /// Determines whether a point is in the bounds of the rectangle, exclusive of the + /// rectangle's bottom/right edge. + /// + /// The point. + /// true if the point is in the bounds of the rectangle; otherwise false. + public bool ContainsExclusive(Point p) + { + return p.X >= _x && p.X < _x + _width && + p.Y >= _y && p.Y < _y + _height; + } /// /// Determines whether the rectangle fully contains another rectangle. From a643ce6297046f4dc3ab9c89e9d6bfb8604d9463 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Tue, 8 Mar 2022 12:14:06 +0000 Subject: [PATCH 06/12] add containsexclusive to pixelrect --- src/Avalonia.Visuals/Media/PixelRect.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/Avalonia.Visuals/Media/PixelRect.cs b/src/Avalonia.Visuals/Media/PixelRect.cs index 0059a4b483..855ba104ad 100644 --- a/src/Avalonia.Visuals/Media/PixelRect.cs +++ b/src/Avalonia.Visuals/Media/PixelRect.cs @@ -168,6 +168,18 @@ namespace Avalonia { return p.X >= X && p.X <= Right && p.Y >= Y && p.Y <= Bottom; } + + /// + /// Determines whether a point is in the bounds of the rectangle, exclusive of the + /// rectangle's bottom/right edge. + /// + /// The point. + /// true if the point is in the bounds of the rectangle; otherwise false. + public bool ContainsExclusive(PixelPoint p) + { + return p.X >= X && p.X < X + Width && + p.Y >= Y && p.Y < Y + Height; + } /// /// Determines whether the rectangle fully contains another rectangle. From a740301faca1785a8b8e639439ceaa9fcf2cec8d Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Tue, 8 Mar 2022 12:16:34 +0000 Subject: [PATCH 07/12] fix nullability of local var. --- src/Avalonia.Controls/Window.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs index 505f3f7416..feacc3e63a 100644 --- a/src/Avalonia.Controls/Window.cs +++ b/src/Avalonia.Controls/Window.cs @@ -879,7 +879,7 @@ namespace Avalonia.Controls if (startupLocation == WindowStartupLocation.CenterScreen) { - Screen screen = null; + Screen? screen = null; if (owner is not null) { From 2f35cd1b7b42d505ce215a1b4d4c2d11e7af98b9 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Tue, 8 Mar 2022 12:17:20 +0000 Subject: [PATCH 08/12] fix nullability error. --- src/Avalonia.Controls/WindowBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/WindowBase.cs b/src/Avalonia.Controls/WindowBase.cs index d3c940c07c..12ba143c8a 100644 --- a/src/Avalonia.Controls/WindowBase.cs +++ b/src/Avalonia.Controls/WindowBase.cs @@ -60,7 +60,7 @@ namespace Avalonia.Controls public WindowBase(IWindowBaseImpl impl, IAvaloniaDependencyResolver? dependencyResolver) : base(impl, dependencyResolver) { - Screens = new Screens(PlatformImpl?.Screen); + Screens = new Screens(impl.Screen); impl.Activated = HandleActivated; impl.Deactivated = HandleDeactivated; impl.PositionChanged = HandlePositionChanged; From 58df47fa0667c515e66287a5ca7550daf9d75ab4 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 11 Mar 2022 14:17:30 +0000 Subject: [PATCH 09/12] fix unit test. --- src/Avalonia.Controls/ApiCompatBaseline.txt | 4 +--- tests/Avalonia.Controls.UnitTests/WindowTests.cs | 2 ++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Controls/ApiCompatBaseline.txt b/src/Avalonia.Controls/ApiCompatBaseline.txt index dc7b70229a..ce84a7fe84 100644 --- a/src/Avalonia.Controls/ApiCompatBaseline.txt +++ b/src/Avalonia.Controls/ApiCompatBaseline.txt @@ -72,6 +72,4 @@ InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platfor MembersMustExist : Member 'public void Avalonia.Platform.IWindowImpl.Resize(Avalonia.Size)' does not exist in the implementation but it does exist in the contract. InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.IWindowImpl.Resize(Avalonia.Size, Avalonia.Platform.PlatformResizeReason)' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.ITrayIconImpl Avalonia.Platform.IWindowingPlatform.CreateTrayIcon()' is present in the implementation but not in the contract. -Total Issues: 70 -Total Issues: 69 -Total Issues: 66 +Total Issues: 73 diff --git a/tests/Avalonia.Controls.UnitTests/WindowTests.cs b/tests/Avalonia.Controls.UnitTests/WindowTests.cs index eb128ef038..f643a84e37 100644 --- a/tests/Avalonia.Controls.UnitTests/WindowTests.cs +++ b/tests/Avalonia.Controls.UnitTests/WindowTests.cs @@ -516,6 +516,8 @@ namespace Avalonia.Controls.UnitTests var screens = new Mock(); screens.Setup(x => x.AllScreens).Returns(new Screen[] { screen1.Object, screen2.Object }); + screens.Setup(x => x.ScreenFromPoint(It.IsAny())).Returns(screen1.Object); + var windowImpl = MockWindowingPlatform.CreateWindowMock(); windowImpl.Setup(x => x.ClientSize).Returns(new Size(800, 480)); From 9f02c649e75740957d5142646f569447b61dc51b Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 11 Mar 2022 16:11:06 +0000 Subject: [PATCH 10/12] make mica fallback to acrylic on compatible windows 10 --- src/Avalonia.Controls/WindowTransparencyLevel.cs | 5 +++++ src/Windows/Avalonia.Win32/WindowImpl.cs | 8 +++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/WindowTransparencyLevel.cs b/src/Avalonia.Controls/WindowTransparencyLevel.cs index f416b5de91..d463f74a0e 100644 --- a/src/Avalonia.Controls/WindowTransparencyLevel.cs +++ b/src/Avalonia.Controls/WindowTransparencyLevel.cs @@ -22,6 +22,11 @@ /// AcrylicBlur, + /// + /// Force acrylic on some incompatible versions of Windows 10. + /// + ForceAcrylicBlur, + /// /// The window background is based on desktop wallpaper tint with a blur. This will only work on Windows 11 /// diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 3953a0995d..3e1b2a1609 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -396,6 +396,11 @@ namespace Avalonia.Win32 } } + if (Win32Platform.WindowsVersion.Major == 10 && effect == BlurEffect.Mica) + { + effect = BlurEffect.Acrylic; + } + _blurHost?.SetBlur(effect); return transparencyLevel; @@ -428,7 +433,8 @@ namespace Avalonia.Win32 break; case WindowTransparencyLevel.AcrylicBlur: - case (WindowTransparencyLevel.AcrylicBlur + 1): // hack-force acrylic. + case WindowTransparencyLevel.ForceAcrylicBlur: // hack-force acrylic. + case WindowTransparencyLevel.Mica: accent.AccentState = AccentState.ACCENT_ENABLE_ACRYLIC; transparencyLevel = WindowTransparencyLevel.AcrylicBlur; break; From 321dcd38f2fdf0eba2df30967723f1b1c3e3f785 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 11 Mar 2022 16:44:01 +0000 Subject: [PATCH 11/12] fix mica fallback check. --- src/Windows/Avalonia.Win32/WindowImpl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 3e1b2a1609..41269ee7ca 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -396,7 +396,7 @@ namespace Avalonia.Win32 } } - if (Win32Platform.WindowsVersion.Major == 10 && effect == BlurEffect.Mica) + if (Win32Platform.WindowsVersion < WinUICompositorConnection.MinHostBackdropVersion && effect == BlurEffect.Mica) { effect = BlurEffect.Acrylic; } From 462ea76d2a22c4c80531b2552b02e5d7411c4bbb Mon Sep 17 00:00:00 2001 From: chylex Date: Sat, 12 Mar 2022 08:43:57 +0100 Subject: [PATCH 12/12] Fix wrong foreground TextBox color in Fluent theme --- src/Avalonia.Themes.Fluent/Controls/TextBox.xaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Themes.Fluent/Controls/TextBox.xaml b/src/Avalonia.Themes.Fluent/Controls/TextBox.xaml index e5b524beeb..c82757799d 100644 --- a/src/Avalonia.Themes.Fluent/Controls/TextBox.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/TextBox.xaml @@ -121,7 +121,7 @@ -