瀏覽代碼

Merge branch 'master' into fixes/mica-acrylic-fallback-win32

Jumar Macato 3 年之前
父節點
當前提交
5cccbb90f6

+ 4 - 1
src/Avalonia.Controls/ApiCompatBaseline.txt

@@ -51,6 +51,9 @@ MembersMustExist : Member 'protected void Avalonia.Controls.Presenters.TextPrese
 CannotRemoveBaseTypeOrInterface : Type 'Avalonia.Controls.Primitives.PopupRoot' does not implement interface 'Avalonia.Utilities.IWeakSubscriber<Avalonia.Controls.ResourcesChangedEventArgs>' in the implementation but it does in the contract.
 TypesMustExist : Type 'Avalonia.Platform.ExportWindowingSubsystemAttribute' does not exist in the implementation but it does exist 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.Size> Avalonia.Platform.ITopLevelImpl.FrameSize' is present in the implementation but not in the contract.
 InterfacesShouldHaveSameMembers : Interface member 'public System.Nullable<Avalonia.Size> Avalonia.Platform.ITopLevelImpl.FrameSize.get()' is present in the implementation but not in the contract.
 InterfacesShouldHaveSameMembers : Interface member 'public System.Action<Avalonia.Size, Avalonia.Platform.PlatformResizeReason> Avalonia.Platform.ITopLevelImpl.Resized.get()' is present in the implementation but not in the contract.
@@ -69,4 +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: 73

+ 8 - 0
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<Screen> AllScreens { get; }
+
+        Screen? ScreenFromWindow(IWindowBaseImpl window);
+
+        Screen? ScreenFromPoint(PixelPoint point);
+
+        Screen? ScreenFromRect(PixelRect rect);
     }
 }

+ 54 - 0
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<Screen> screens)
+        {
+            foreach (Screen screen in screens)
+            {
+                if (screen.Bounds.ContainsExclusive(point))
+                {
+                    return screen;
+                }
+            }
+
+            return null;
+        }
+
+        public static Screen? ScreenFromRect(PixelRect bounds, IReadOnlyList<Screen> 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<Screen> screens)
+        {
+            var rect = new PixelRect(
+                window.Position, 
+                PixelSize.FromSize(window.FrameSize ?? window.ClientSize, window.DesktopScaling));
+
+            return ScreenFromRect(rect, screens);
+        }
+    }
+}

+ 2 - 2
src/Avalonia.Controls/Primitives/PopupPositioning/ManagedPopupPositioner.cs

@@ -106,9 +106,9 @@ namespace Avalonia.Controls.Primitives.PopupPositioning
             {
                 var screens = _popup.Screens;
                 
-                var targetScreen = screens.FirstOrDefault(s => s.Bounds.Contains(anchorRect.TopLeft))
+                var targetScreen =  screens.FirstOrDefault(s => s.Bounds.ContainsExclusive(anchorRect.TopLeft))
                                    ?? screens.FirstOrDefault(s => s.Bounds.Intersects(anchorRect))
-                                   ?? screens.FirstOrDefault(s => s.Bounds.Contains(parentGeometry.TopLeft))
+                                   ?? screens.FirstOrDefault(s => s.Bounds.ContainsExclusive(parentGeometry.TopLeft))
                                    ?? screens.FirstOrDefault(s => s.Bounds.Intersects(parentGeometry))
                                    ?? screens.FirstOrDefault();
 

+ 15 - 24
src/Avalonia.Controls/Screens.cs

@@ -2,54 +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 _iScreenImpl;
 
         public int ScreenCount => _iScreenImpl?.ScreenCount ?? 0;
         public IReadOnlyList<Screen> All => _iScreenImpl?.AllScreens ?? Array.Empty<Screen>();
         public Screen? Primary => All.FirstOrDefault(x => x.Primary);
 
-        public Screens(IScreenImpl? iScreenImpl)
+        public Screens(IScreenImpl iScreenImpl)
         {
             _iScreenImpl = iScreenImpl;
         }
 
-        public Screen? ScreenFromBounds(PixelRect bounds){
-        
-            Screen? 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;
+        public Screen? ScreenFromBounds(PixelRect bounds)
+        {
+            return _iScreenImpl.ScreenFromRect(bounds);
         }
         
-        public Screen? ScreenFromPoint(PixelPoint point)
+        public Screen? ScreenFromWindow(IWindowBaseImpl window)
         {
-            return All.FirstOrDefault(x => x.Bounds.Contains(point));        
+            return _iScreenImpl.ScreenFromWindow(window);
+        }
+
+        public Screen? ScreenFromPoint(PixelPoint point)
+        {      
+            return _iScreenImpl.ScreenFromPoint(point);
         }
 
         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));
         }
     }

+ 13 - 1
src/Avalonia.Controls/Window.cs

@@ -879,7 +879,19 @@ namespace Avalonia.Controls
 
             if (startupLocation == WindowStartupLocation.CenterScreen)
             {
-                var screen = Screens.ScreenFromPoint(owner?.Position ?? Position);
+                Screen? screen = null;
+
+                if (owner is not null)
+                {
+                    screen = Screens.ScreenFromWindow(owner);
+
+                    screen ??= Screens.ScreenFromPoint(owner.Position);
+                }
+
+                if (screen is null)
+                {
+                    screen = Screens.ScreenFromPoint(Position);
+                }
 
                 if (screen != null)
                 {

+ 1 - 1
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;

+ 15 - 0
src/Avalonia.DesignerSupport/Remote/Stubs.cs

@@ -237,5 +237,20 @@ namespace Avalonia.DesignerSupport.Remote
 
         public IReadOnlyList<Screen> 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 ScreenHelper.ScreenFromPoint(point, AllScreens);
+        }
+
+        public Screen ScreenFromRect(PixelRect rect)
+        {
+            return ScreenHelper.ScreenFromRect(rect, AllScreens);
+        }
+
+        public Screen ScreenFromWindow(IWindowBaseImpl window)
+        {
+            return ScreenHelper.ScreenFromWindow(window, AllScreens);
+        }
     }
 }

+ 15 - 0
src/Avalonia.Headless/HeadlessPlatformStubs.cs

@@ -200,5 +200,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 ScreenHelper.ScreenFromPoint(point, AllScreens);
+        }
+
+        public Screen ScreenFromRect(PixelRect rect)
+        {
+            return ScreenHelper.ScreenFromRect(rect, AllScreens);
+        }
+
+        public Screen ScreenFromWindow(IWindowBaseImpl window)
+        {
+            return ScreenHelper.ScreenFromWindow(window, AllScreens);
+        }
     }
 }

+ 15 - 0
src/Avalonia.Native/ScreenImpl.cs

@@ -48,5 +48,20 @@ namespace Avalonia.Native
             _native?.Dispose();
             _native = null;
         }
+
+        public Screen ScreenFromPoint(PixelPoint point)
+        {
+            return ScreenHelper.ScreenFromPoint(point, AllScreens);
+        }
+
+        public Screen ScreenFromRect(PixelRect rect)
+        {
+            return ScreenHelper.ScreenFromRect(rect, AllScreens);
+        }
+
+        public Screen ScreenFromWindow(IWindowBaseImpl window)
+        {
+            return ScreenHelper.ScreenFromWindow(window, AllScreens);
+        }
     }
 }

+ 12 - 0
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;
         }
+        
+        /// <summary>
+        /// Determines whether a point is in the bounds of the rectangle, exclusive of the
+        /// rectangle's bottom/right edge.
+        /// </summary>
+        /// <param name="p">The point.</param>
+        /// <returns>true if the point is in the bounds of the rectangle; otherwise false.</returns>    
+        public bool ContainsExclusive(PixelPoint p)
+        {
+            return p.X >= X && p.X < X + Width &&
+                   p.Y >= Y && p.Y < Y + Height;
+        }
 
         /// <summary>
         /// Determines whether the rectangle fully contains another rectangle.

+ 12 - 0
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;
         }
+        
+        /// <summary>
+        /// Determines whether a point is in the bounds of the rectangle, exclusive of the
+        /// rectangle's bottom/right edge.
+        /// </summary>
+        /// <param name="p">The point.</param>
+        /// <returns>true if the point is in the bounds of the rectangle; otherwise false.</returns>    
+        public bool ContainsExclusive(Point p)
+        {
+            return p.X >= _x && p.X < _x + _width &&
+                   p.Y >= _y && p.Y < _y + _height;
+        }
 
         /// <summary>
         /// Determines whether the rectangle fully contains another rectangle.

+ 15 - 0
src/Avalonia.X11/X11Screens.cs

@@ -200,6 +200,21 @@ namespace Avalonia.X11
 
         }
 
+        public Screen ScreenFromPoint(PixelPoint point)
+        {
+            return ScreenHelper.ScreenFromPoint(point, AllScreens);
+        }
+
+        public Screen ScreenFromRect(PixelRect rect)
+        {
+            return ScreenHelper.ScreenFromRect(rect, AllScreens);
+        }
+
+        public Screen ScreenFromWindow(IWindowBaseImpl window)
+        {
+            return ScreenHelper.ScreenFromWindow(window, AllScreens);
+        }
+
         public int ScreenCount => _impl.Screens.Length;
 
         public IReadOnlyList<Screen> AllScreens =>

+ 15 - 0
src/Web/Avalonia.Web.Blazor/WinStubs.cs

@@ -55,5 +55,20 @@ namespace Avalonia.Web.Blazor
 
         public IReadOnlyList<Screen> 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 ScreenHelper.ScreenFromPoint(point, AllScreens);
+        }
+
+        public Screen? ScreenFromRect(PixelRect rect)
+        {
+            return ScreenHelper.ScreenFromRect(rect, AllScreens);
+        }
+
+        public Screen? ScreenFromWindow(IWindowBaseImpl window)
+        {
+            return ScreenHelper.ScreenFromWindow(window, AllScreens);
+        }
     }
 }

+ 39 - 0
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_DEFAULTTONULL);
+
+            return FindScreenByHandle(monitor);
+        }
+
+        public Screen ScreenFromPoint(PixelPoint point)
+        {
+            var monitor = MonitorFromPoint(new POINT
+            {
+                X = point.X,
+                Y = point.Y
+            }, MONITOR.MONITOR_DEFAULTTONULL);
+
+            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_DEFAULTTONULL);
+
+            return FindScreenByHandle(monitor);
+        }
+
+        private Screen FindScreenByHandle(IntPtr handle)
+        {
+            return AllScreens.Cast<WinScreen>().FirstOrDefault(m => m.Handle == handle);
+        }
     }
 }

+ 4 - 2
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);
         }
     }
 }

+ 2 - 0
tests/Avalonia.Controls.UnitTests/WindowTests.cs

@@ -516,6 +516,8 @@ namespace Avalonia.Controls.UnitTests
 
             var screens = new Mock<IScreenImpl>();
             screens.Setup(x => x.AllScreens).Returns(new Screen[] { screen1.Object, screen2.Object });
+            screens.Setup(x => x.ScreenFromPoint(It.IsAny<PixelPoint>())).Returns(screen1.Object);
+            
 
             var windowImpl = MockWindowingPlatform.CreateWindowMock();
             windowImpl.Setup(x => x.ClientSize).Returns(new Size(800, 480));