Skip to content

Commit

Permalink
Merge pull request AvaloniaUI#7165 from MarchingCube/platform-screen-api
Browse files Browse the repository at this point in the history
Add more platform specific screen from Window/Rect/Point methods.
# Conflicts:
#	src/Avalonia.Controls/ApiCompatBaseline.txt
#	src/Avalonia.Controls/Screens.cs
  • Loading branch information
danwalmsley committed Mar 11, 2022
1 parent 160a560 commit e84a13f
Show file tree
Hide file tree
Showing 17 changed files with 239 additions and 29 deletions.
5 changes: 4 additions & 1 deletion src/Avalonia.Controls/ApiCompatBaseline.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,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<Avalonia.Controls.ResourcesChangedEventArgs>' 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.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.
Expand All @@ -34,4 +37,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: 35
Total Issues: 73
8 changes: 8 additions & 0 deletions src/Avalonia.Controls/Platform/IScreenImpl.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
using System.Collections.Generic;

#nullable enable

namespace Avalonia.Platform
{
public interface IScreenImpl
{
int ScreenCount { get; }

IReadOnlyList<Screen> AllScreens { get; }

Screen? ScreenFromWindow(IWindowBaseImpl window);

Screen? ScreenFromPoint(PixelPoint point);

Screen? ScreenFromRect(PixelRect rect);
}
}
54 changes: 54 additions & 0 deletions src/Avalonia.Controls/Platform/ScreenHelper.cs
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,9 @@ Rect GetBounds()
{
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();

Expand Down
35 changes: 13 additions & 22 deletions src/Avalonia.Controls/Screens.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
using System.Collections.Generic;
using System.Linq;
using Avalonia.Platform;
using Avalonia.Utilities;
using Avalonia.VisualTree;

#nullable enable

namespace Avalonia.Controls
{
public class Screens
Expand All @@ -20,36 +21,26 @@ 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));
}
}
Expand Down
14 changes: 13 additions & 1 deletion src/Avalonia.Controls/Window.cs
Original file line number Diff line number Diff line change
Expand Up @@ -879,7 +879,19 @@ Owner is Window ownerWindow &&

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)
{
Expand Down
2 changes: 1 addition & 1 deletion src/Avalonia.Controls/WindowBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public WindowBase(IWindowBaseImpl impl) : this(impl, AvaloniaLocator.Current)

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;
Expand Down
15 changes: 15 additions & 0 deletions src/Avalonia.DesignerSupport/Remote/Stubs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -236,5 +236,20 @@ class ScreenStub : IScreenImpl

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 changes: 15 additions & 0 deletions src/Avalonia.Headless/HeadlessPlatformStubs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -199,5 +199,20 @@ class HeadlessScreensStub : IScreenImpl
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 changes: 15 additions & 0 deletions src/Avalonia.Native/ScreenImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,20 @@ public void Dispose ()
_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 changes: 12 additions & 0 deletions src/Avalonia.Visuals/Media/PixelRect.cs
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,18 @@ public bool Contains(PixelPoint p)
{
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.
Expand Down
12 changes: 12 additions & 0 deletions src/Avalonia.Visuals/Rect.cs
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,18 @@ public bool Contains(Point p)
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.
Expand Down
15 changes: 15 additions & 0 deletions src/Avalonia.X11/X11Screens.cs
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,21 @@ public static IX11Screens Init(AvaloniaX11Platform platform)

}

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 =>
Expand Down
15 changes: 15 additions & 0 deletions src/Web/Avalonia.Web.Blazor/WinStubs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,5 +55,20 @@ internal class ScreenStub : IScreenImpl

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 changes: 39 additions & 0 deletions src/Windows/Avalonia.Win32/ScreenImpl.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -70,5 +71,43 @@ public void InvalidateScreensCache()
{
_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);
}
}
}
Loading

0 comments on commit e84a13f

Please sign in to comment.