Skip to content

Commit e84a13f

Browse files
committed
Merge pull request AvaloniaUI#7165 from MarchingCube/platform-screen-api
Add more platform specific screen from Window/Rect/Point methods. # Conflicts: # src/Avalonia.Controls/ApiCompatBaseline.txt # src/Avalonia.Controls/Screens.cs
1 parent 160a560 commit e84a13f

File tree

17 files changed

+239
-29
lines changed

17 files changed

+239
-29
lines changed

src/Avalonia.Controls/ApiCompatBaseline.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ MembersMustExist : Member 'public Avalonia.AvaloniaProperty Avalonia.AvaloniaPro
1616
InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Controls.Platform.ITopLevelNativeMenuExporter.SetNativeMenu(Avalonia.Controls.NativeMenu)' is present in the contract but not in the implementation.
1717
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.
1818
EnumValuesMustMatch : Enum value 'Avalonia.Platform.ExtendClientAreaChromeHints Avalonia.Platform.ExtendClientAreaChromeHints.Default' is (System.Int32)2 in the implementation but (System.Int32)1 in the contract.
19+
InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.Screen Avalonia.Platform.IScreenImpl.ScreenFromPoint(Avalonia.PixelPoint)' is present in the implementation but not in the contract.
20+
InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.Screen Avalonia.Platform.IScreenImpl.ScreenFromRect(Avalonia.PixelRect)' is present in the implementation but not in the contract.
21+
InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.Screen Avalonia.Platform.IScreenImpl.ScreenFromWindow(Avalonia.Platform.IWindowBaseImpl)' is present in the implementation but not in the contract.
1922
InterfacesShouldHaveSameMembers : Interface member 'public System.Nullable<Avalonia.Size> Avalonia.Platform.ITopLevelImpl.FrameSize' is present in the implementation but not in the contract.
2023
InterfacesShouldHaveSameMembers : Interface member 'public System.Nullable<Avalonia.Size> Avalonia.Platform.ITopLevelImpl.FrameSize.get()' is present in the implementation but not in the contract.
2124
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.
@@ -34,4 +37,4 @@ InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platfor
3437
MembersMustExist : Member 'public void Avalonia.Platform.IWindowImpl.Resize(Avalonia.Size)' does not exist in the implementation but it does exist in the contract.
3538
InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.IWindowImpl.Resize(Avalonia.Size, Avalonia.Platform.PlatformResizeReason)' is present in the implementation but not in the contract.
3639
InterfacesShouldHaveSameMembers : Interface member 'public Avalonia.Platform.ITrayIconImpl Avalonia.Platform.IWindowingPlatform.CreateTrayIcon()' is present in the implementation but not in the contract.
37-
Total Issues: 35
40+
Total Issues: 73
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,19 @@
11
using System.Collections.Generic;
22

3+
#nullable enable
4+
35
namespace Avalonia.Platform
46
{
57
public interface IScreenImpl
68
{
79
int ScreenCount { get; }
810

911
IReadOnlyList<Screen> AllScreens { get; }
12+
13+
Screen? ScreenFromWindow(IWindowBaseImpl window);
14+
15+
Screen? ScreenFromPoint(PixelPoint point);
16+
17+
Screen? ScreenFromRect(PixelRect rect);
1018
}
1119
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
using System.Collections.Generic;
2+
using Avalonia.Utilities;
3+
4+
#nullable enable
5+
6+
namespace Avalonia.Platform
7+
{
8+
public static class ScreenHelper
9+
{
10+
public static Screen? ScreenFromPoint(PixelPoint point, IReadOnlyList<Screen> screens)
11+
{
12+
foreach (Screen screen in screens)
13+
{
14+
if (screen.Bounds.ContainsExclusive(point))
15+
{
16+
return screen;
17+
}
18+
}
19+
20+
return null;
21+
}
22+
23+
public static Screen? ScreenFromRect(PixelRect bounds, IReadOnlyList<Screen> screens)
24+
{
25+
Screen? currMaxScreen = null;
26+
double maxAreaSize = 0;
27+
28+
foreach (Screen screen in screens)
29+
{
30+
double left = MathUtilities.Clamp(bounds.X, screen.Bounds.X, screen.Bounds.X + screen.Bounds.Width);
31+
double top = MathUtilities.Clamp(bounds.Y, screen.Bounds.Y, screen.Bounds.Y + screen.Bounds.Height);
32+
double right = MathUtilities.Clamp(bounds.X + bounds.Width, screen.Bounds.X, screen.Bounds.X + screen.Bounds.Width);
33+
double bottom = MathUtilities.Clamp(bounds.Y + bounds.Height, screen.Bounds.Y, screen.Bounds.Y + screen.Bounds.Height);
34+
double area = (right - left) * (bottom - top);
35+
if (area > maxAreaSize)
36+
{
37+
maxAreaSize = area;
38+
currMaxScreen = screen;
39+
}
40+
}
41+
42+
return currMaxScreen;
43+
}
44+
45+
public static Screen? ScreenFromWindow(IWindowBaseImpl window, IReadOnlyList<Screen> screens)
46+
{
47+
var rect = new PixelRect(
48+
window.Position,
49+
PixelSize.FromSize(window.FrameSize ?? window.ClientSize, window.DesktopScaling));
50+
51+
return ScreenFromRect(rect, screens);
52+
}
53+
}
54+
}

src/Avalonia.Controls/Primitives/PopupPositioning/ManagedPopupPositioner.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,9 +106,9 @@ Rect GetBounds()
106106
{
107107
var screens = _popup.Screens;
108108

109-
var targetScreen = screens.FirstOrDefault(s => s.Bounds.Contains(anchorRect.TopLeft))
109+
var targetScreen = screens.FirstOrDefault(s => s.Bounds.ContainsExclusive(anchorRect.TopLeft))
110110
?? screens.FirstOrDefault(s => s.Bounds.Intersects(anchorRect))
111-
?? screens.FirstOrDefault(s => s.Bounds.Contains(parentGeometry.TopLeft))
111+
?? screens.FirstOrDefault(s => s.Bounds.ContainsExclusive(parentGeometry.TopLeft))
112112
?? screens.FirstOrDefault(s => s.Bounds.Intersects(parentGeometry))
113113
?? screens.FirstOrDefault();
114114

src/Avalonia.Controls/Screens.cs

Lines changed: 13 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22
using System.Collections.Generic;
33
using System.Linq;
44
using Avalonia.Platform;
5-
using Avalonia.Utilities;
65
using Avalonia.VisualTree;
76

7+
#nullable enable
8+
89
namespace Avalonia.Controls
910
{
1011
public class Screens
@@ -20,36 +21,26 @@ public Screens(IScreenImpl iScreenImpl)
2021
_iScreenImpl = iScreenImpl;
2122
}
2223

23-
public Screen ScreenFromBounds(PixelRect bounds){
24-
25-
Screen currMaxScreen = null;
26-
double maxAreaSize = 0;
27-
foreach (Screen screen in All)
28-
{
29-
double left = MathUtilities.Clamp(bounds.X, screen.Bounds.X, screen.Bounds.X + screen.Bounds.Width);
30-
double top = MathUtilities.Clamp(bounds.Y, screen.Bounds.Y, screen.Bounds.Y + screen.Bounds.Height);
31-
double right = MathUtilities.Clamp(bounds.X + bounds.Width, screen.Bounds.X, screen.Bounds.X + screen.Bounds.Width);
32-
double bottom = MathUtilities.Clamp(bounds.Y + bounds.Height, screen.Bounds.Y, screen.Bounds.Y + screen.Bounds.Height);
33-
double area = (right - left) * (bottom - top);
34-
if (area > maxAreaSize)
35-
{
36-
maxAreaSize = area;
37-
currMaxScreen = screen;
38-
}
39-
}
40-
41-
return currMaxScreen;
24+
public Screen? ScreenFromBounds(PixelRect bounds)
25+
{
26+
return _iScreenImpl.ScreenFromRect(bounds);
4227
}
4328

44-
public Screen ScreenFromPoint(PixelPoint point)
29+
public Screen? ScreenFromWindow(IWindowBaseImpl window)
4530
{
46-
return All.FirstOrDefault(x => x.Bounds.Contains(point));
31+
return _iScreenImpl.ScreenFromWindow(window);
32+
}
33+
34+
public Screen? ScreenFromPoint(PixelPoint point)
35+
{
36+
return _iScreenImpl.ScreenFromPoint(point);
4737
}
4838

4939
public Screen ScreenFromVisual(IVisual visual)
5040
{
5141
var tl = visual.PointToScreen(visual.Bounds.TopLeft);
5242
var br = visual.PointToScreen(visual.Bounds.BottomRight);
43+
5344
return ScreenFromBounds(new PixelRect(tl, br));
5445
}
5546
}

src/Avalonia.Controls/Window.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -879,7 +879,19 @@ Owner is Window ownerWindow &&
879879

880880
if (startupLocation == WindowStartupLocation.CenterScreen)
881881
{
882-
var screen = Screens.ScreenFromPoint(owner?.Position ?? Position);
882+
Screen? screen = null;
883+
884+
if (owner is not null)
885+
{
886+
screen = Screens.ScreenFromWindow(owner);
887+
888+
screen ??= Screens.ScreenFromPoint(owner.Position);
889+
}
890+
891+
if (screen is null)
892+
{
893+
screen = Screens.ScreenFromPoint(Position);
894+
}
883895

884896
if (screen != null)
885897
{

src/Avalonia.Controls/WindowBase.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ public WindowBase(IWindowBaseImpl impl) : this(impl, AvaloniaLocator.Current)
5959

6060
public WindowBase(IWindowBaseImpl impl, IAvaloniaDependencyResolver dependencyResolver) : base(impl, dependencyResolver)
6161
{
62-
Screens = new Screens(PlatformImpl?.Screen);
62+
Screens = new Screens(impl.Screen);
6363
impl.Activated = HandleActivated;
6464
impl.Deactivated = HandleDeactivated;
6565
impl.PositionChanged = HandlePositionChanged;

src/Avalonia.DesignerSupport/Remote/Stubs.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,5 +236,20 @@ class ScreenStub : IScreenImpl
236236

237237
public IReadOnlyList<Screen> AllScreens { get; } =
238238
new Screen[] { new Screen(1, new PixelRect(0, 0, 4000, 4000), new PixelRect(0, 0, 4000, 4000), true) };
239+
240+
public Screen ScreenFromPoint(PixelPoint point)
241+
{
242+
return ScreenHelper.ScreenFromPoint(point, AllScreens);
243+
}
244+
245+
public Screen ScreenFromRect(PixelRect rect)
246+
{
247+
return ScreenHelper.ScreenFromRect(rect, AllScreens);
248+
}
249+
250+
public Screen ScreenFromWindow(IWindowBaseImpl window)
251+
{
252+
return ScreenHelper.ScreenFromWindow(window, AllScreens);
253+
}
239254
}
240255
}

src/Avalonia.Headless/HeadlessPlatformStubs.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,5 +199,20 @@ class HeadlessScreensStub : IScreenImpl
199199
new Screen(1, new PixelRect(0, 0, 1920, 1280),
200200
new PixelRect(0, 0, 1920, 1280), true),
201201
};
202+
203+
public Screen ScreenFromPoint(PixelPoint point)
204+
{
205+
return ScreenHelper.ScreenFromPoint(point, AllScreens);
206+
}
207+
208+
public Screen ScreenFromRect(PixelRect rect)
209+
{
210+
return ScreenHelper.ScreenFromRect(rect, AllScreens);
211+
}
212+
213+
public Screen ScreenFromWindow(IWindowBaseImpl window)
214+
{
215+
return ScreenHelper.ScreenFromWindow(window, AllScreens);
216+
}
202217
}
203218
}

src/Avalonia.Native/ScreenImpl.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,5 +48,20 @@ public void Dispose ()
4848
_native?.Dispose();
4949
_native = null;
5050
}
51+
52+
public Screen ScreenFromPoint(PixelPoint point)
53+
{
54+
return ScreenHelper.ScreenFromPoint(point, AllScreens);
55+
}
56+
57+
public Screen ScreenFromRect(PixelRect rect)
58+
{
59+
return ScreenHelper.ScreenFromRect(rect, AllScreens);
60+
}
61+
62+
public Screen ScreenFromWindow(IWindowBaseImpl window)
63+
{
64+
return ScreenHelper.ScreenFromWindow(window, AllScreens);
65+
}
5166
}
5267
}

src/Avalonia.Visuals/Media/PixelRect.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,18 @@ public bool Contains(PixelPoint p)
168168
{
169169
return p.X >= X && p.X <= Right && p.Y >= Y && p.Y <= Bottom;
170170
}
171+
172+
/// <summary>
173+
/// Determines whether a point is in the bounds of the rectangle, exclusive of the
174+
/// rectangle's bottom/right edge.
175+
/// </summary>
176+
/// <param name="p">The point.</param>
177+
/// <returns>true if the point is in the bounds of the rectangle; otherwise false.</returns>
178+
public bool ContainsExclusive(PixelPoint p)
179+
{
180+
return p.X >= X && p.X < X + Width &&
181+
p.Y >= Y && p.Y < Y + Height;
182+
}
171183

172184
/// <summary>
173185
/// Determines whether the rectangle fully contains another rectangle.

src/Avalonia.Visuals/Rect.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,18 @@ public bool Contains(Point p)
252252
return p.X >= _x && p.X <= _x + _width &&
253253
p.Y >= _y && p.Y <= _y + _height;
254254
}
255+
256+
/// <summary>
257+
/// Determines whether a point is in the bounds of the rectangle, exclusive of the
258+
/// rectangle's bottom/right edge.
259+
/// </summary>
260+
/// <param name="p">The point.</param>
261+
/// <returns>true if the point is in the bounds of the rectangle; otherwise false.</returns>
262+
public bool ContainsExclusive(Point p)
263+
{
264+
return p.X >= _x && p.X < _x + _width &&
265+
p.Y >= _y && p.Y < _y + _height;
266+
}
255267

256268
/// <summary>
257269
/// Determines whether the rectangle fully contains another rectangle.

src/Avalonia.X11/X11Screens.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,21 @@ public static IX11Screens Init(AvaloniaX11Platform platform)
200200

201201
}
202202

203+
public Screen ScreenFromPoint(PixelPoint point)
204+
{
205+
return ScreenHelper.ScreenFromPoint(point, AllScreens);
206+
}
207+
208+
public Screen ScreenFromRect(PixelRect rect)
209+
{
210+
return ScreenHelper.ScreenFromRect(rect, AllScreens);
211+
}
212+
213+
public Screen ScreenFromWindow(IWindowBaseImpl window)
214+
{
215+
return ScreenHelper.ScreenFromWindow(window, AllScreens);
216+
}
217+
203218
public int ScreenCount => _impl.Screens.Length;
204219

205220
public IReadOnlyList<Screen> AllScreens =>

src/Web/Avalonia.Web.Blazor/WinStubs.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,5 +55,20 @@ internal class ScreenStub : IScreenImpl
5555

5656
public IReadOnlyList<Screen> AllScreens { get; } =
5757
new[] { new Screen(96, new PixelRect(0, 0, 4000, 4000), new PixelRect(0, 0, 4000, 4000), true) };
58+
59+
public Screen? ScreenFromPoint(PixelPoint point)
60+
{
61+
return ScreenHelper.ScreenFromPoint(point, AllScreens);
62+
}
63+
64+
public Screen? ScreenFromRect(PixelRect rect)
65+
{
66+
return ScreenHelper.ScreenFromRect(rect, AllScreens);
67+
}
68+
69+
public Screen? ScreenFromWindow(IWindowBaseImpl window)
70+
{
71+
return ScreenHelper.ScreenFromWindow(window, AllScreens);
72+
}
5873
}
5974
}

src/Windows/Avalonia.Win32/ScreenImpl.cs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Linq;
34
using Avalonia.Platform;
45
using Avalonia.Win32.Interop;
56
using static Avalonia.Win32.Interop.UnmanagedMethods;
@@ -70,5 +71,43 @@ public void InvalidateScreensCache()
7071
{
7172
_allScreens = null;
7273
}
74+
75+
public Screen ScreenFromWindow(IWindowBaseImpl window)
76+
{
77+
var handle = window.Handle.Handle;
78+
79+
var monitor = MonitorFromWindow(handle, MONITOR.MONITOR_DEFAULTTONULL);
80+
81+
return FindScreenByHandle(monitor);
82+
}
83+
84+
public Screen ScreenFromPoint(PixelPoint point)
85+
{
86+
var monitor = MonitorFromPoint(new POINT
87+
{
88+
X = point.X,
89+
Y = point.Y
90+
}, MONITOR.MONITOR_DEFAULTTONULL);
91+
92+
return FindScreenByHandle(monitor);
93+
}
94+
95+
public Screen ScreenFromRect(PixelRect rect)
96+
{
97+
var monitor = MonitorFromRect(new RECT
98+
{
99+
left = rect.TopLeft.X,
100+
top = rect.TopLeft.Y,
101+
right = rect.TopRight.X,
102+
bottom = rect.BottomRight.Y
103+
}, MONITOR.MONITOR_DEFAULTTONULL);
104+
105+
return FindScreenByHandle(monitor);
106+
}
107+
108+
private Screen FindScreenByHandle(IntPtr handle)
109+
{
110+
return AllScreens.Cast<WinScreen>().FirstOrDefault(m => m.Handle == handle);
111+
}
73112
}
74113
}

0 commit comments

Comments
 (0)