Browse Source

Use PixelPoint and PixelRect for device points/rects.

The affected members are:

- `IWindowBaseImpl.Position`
- `IWindowBaseImpl.PositionChanged`
- `ITopLevelImpl.PointToClient`
- `ITopLevelImpl.PointToScreen`
- `IMouseDevice.Position`
- `Screen.Bounds`
- `Screen.WorkingArea`
Steven Kirk 6 years ago
parent
commit
4ec2b1c554
45 changed files with 227 additions and 224 deletions
  1. 3 1
      samples/ControlCatalog/Pages/ScreenPage.cs
  2. 3 3
      src/Android/Avalonia.Android/Platform/SkiaPlatform/PopupImpl.cs
  3. 4 4
      src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
  4. 2 2
      src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs
  5. 27 0
      src/Avalonia.Controls/PixelPointEventArgs.cs
  6. 2 2
      src/Avalonia.Controls/Platform/ITopLevelImpl.cs
  7. 3 3
      src/Avalonia.Controls/Platform/IWindowBaseImpl.cs
  8. 4 4
      src/Avalonia.Controls/Platform/Screen.cs
  9. 0 27
      src/Avalonia.Controls/PointEventArgs.cs
  10. 13 17
      src/Avalonia.Controls/Primitives/Popup.cs
  11. 5 5
      src/Avalonia.Controls/Primitives/PopupRoot.cs
  12. 7 7
      src/Avalonia.Controls/Screens.cs
  13. 1 0
      src/Avalonia.Controls/ToolTip.cs
  14. 4 4
      src/Avalonia.Controls/TopLevel.cs
  15. 16 4
      src/Avalonia.Controls/Window.cs
  16. 5 5
      src/Avalonia.Controls/WindowBase.cs
  17. 2 2
      src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs
  18. 5 5
      src/Avalonia.DesignerSupport/Remote/Stubs.cs
  19. 1 1
      src/Avalonia.Input/IMouseDevice.cs
  20. 1 1
      src/Avalonia.Input/MouseDevice.cs
  21. 15 0
      src/Avalonia.Native/Helpers.cs
  22. 4 1
      src/Avalonia.Native/ScreenImpl.cs
  23. 7 7
      src/Avalonia.Native/WindowImplBase.cs
  24. 4 4
      src/Avalonia.Visuals/Rendering/IRenderRoot.cs
  25. 8 4
      src/Avalonia.Visuals/VisualExtensions.cs
  26. 8 8
      src/Avalonia.X11/X11Screens.cs
  27. 11 9
      src/Avalonia.X11/X11Window.cs
  28. 2 2
      src/Gtk/Avalonia.Gtk3/GtkScreen.cs
  29. 2 2
      src/Gtk/Avalonia.Gtk3/ScreenImpl.cs
  30. 7 7
      src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs
  31. 2 2
      src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs
  32. 2 0
      src/Windows/Avalonia.Win32.Interop/Wpf/WpfInteropExtensions.cs
  33. 2 2
      src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs
  34. 1 1
      src/Windows/Avalonia.Win32/OleDropTarget.cs
  35. 3 3
      src/Windows/Avalonia.Win32/ScreenImpl.cs
  36. 2 2
      src/Windows/Avalonia.Win32/WinScreen.cs
  37. 11 11
      src/Windows/Avalonia.Win32/WindowImpl.cs
  38. 2 2
      src/iOS/Avalonia.iOS/TopLevelImpl.cs
  39. 2 2
      tests/Avalonia.Controls.UnitTests/ButtonTests.cs
  40. 1 1
      tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs
  41. 4 18
      tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization.cs
  42. 4 24
      tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization_Simple.cs
  43. 11 11
      tests/Avalonia.Controls.UnitTests/WindowTests.cs
  44. 2 2
      tests/Avalonia.UnitTests/TestRoot.cs
  45. 2 2
      tests/Avalonia.UnitTests/TestTemplatedRoot.cs

+ 3 - 1
samples/ControlCatalog/Pages/ScreenPage.cs

@@ -4,6 +4,7 @@ using Avalonia.Controls;
 using Avalonia.Markup.Xaml;
 using Avalonia.Markup.Xaml;
 using Avalonia.Media;
 using Avalonia.Media;
 using Avalonia.Platform;
 using Avalonia.Platform;
+using Avalonia.Rendering;
 
 
 namespace ControlCatalog.Pages
 namespace ControlCatalog.Pages
 {
 {
@@ -23,6 +24,7 @@ namespace ControlCatalog.Pages
             base.Render(context);
             base.Render(context);
             Window w = (Window)VisualRoot;
             Window w = (Window)VisualRoot;
             Screen[] screens = w.Screens.All;
             Screen[] screens = w.Screens.All;
+            var scaling = ((IRenderRoot)w).RenderScaling;
 
 
             Pen p = new Pen(Brushes.Black);
             Pen p = new Pen(Brushes.Black);
             if (screens != null)
             if (screens != null)
@@ -56,7 +58,7 @@ namespace ControlCatalog.Pages
                     text.Text = $"Primary: {screen.Primary}";
                     text.Text = $"Primary: {screen.Primary}";
                     context.DrawText(Brushes.Black, boundsRect.Position.WithY(boundsRect.Size.Height + 40), text);
                     context.DrawText(Brushes.Black, boundsRect.Position.WithY(boundsRect.Size.Height + 40), text);
                     
                     
-                    text.Text = $"Current: {screen.Equals(w.Screens.ScreenFromBounds(new Rect(w.Position, w.Bounds.Size)))}";
+                    text.Text = $"Current: {screen.Equals(w.Screens.ScreenFromBounds(new PixelRect(w.Position, PixelSize.FromSize(w.Bounds.Size, scaling))))}";
                     context.DrawText(Brushes.Black, boundsRect.Position.WithY(boundsRect.Size.Height + 60), text);
                     context.DrawText(Brushes.Black, boundsRect.Position.WithY(boundsRect.Size.Height + 60), text);
                 }
                 }
 
 

+ 3 - 3
src/Android/Avalonia.Android/Platform/SkiaPlatform/PopupImpl.cs

@@ -10,10 +10,10 @@ namespace Avalonia.Android.Platform.SkiaPlatform
 {
 {
     class PopupImpl : TopLevelImpl, IPopupImpl
     class PopupImpl : TopLevelImpl, IPopupImpl
     {
     {
-        private Point _position;
+        private PixelPoint _position;
         private bool _isAdded;
         private bool _isAdded;
         Action IWindowBaseImpl.Activated { get; set; }
         Action IWindowBaseImpl.Activated { get; set; }
-        public Action<Point> PositionChanged { get; set; }
+        public Action<PixelPoint> PositionChanged { get; set; }
         public Action Deactivated { get; set; }
         public Action Deactivated { get; set; }
 
 
         public PopupImpl() : base(ActivityTracker.Current, true)
         public PopupImpl() : base(ActivityTracker.Current, true)
@@ -36,7 +36,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform
 
 
         public IScreenImpl Screen { get; }
         public IScreenImpl Screen { get; }
 
 
-        public Point Position
+        public PixelPoint Position
         {
         {
             get { return _position; }
             get { return _position; }
             set
             set

+ 4 - 4
src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs

@@ -99,14 +99,14 @@ namespace Avalonia.Android.Platform.SkiaPlatform
             if (_view.Holder?.Surface?.IsValid == true) _view.Invalidate();
             if (_view.Holder?.Surface?.IsValid == true) _view.Invalidate();
         }
         }
 
 
-        public Point PointToClient(Point point)
+        public Point PointToClient(PixelPoint point)
         {
         {
-            return point;
+            return point.ToPoint(1);
         }
         }
 
 
-        public Point PointToScreen(Point point)
+        public PixelPoint PointToScreen(Point point)
         {
         {
-            return point;
+            return PixelPoint.FromPoint(point, 1);
         }
         }
 
 
         public void SetCursor(IPlatformHandle cursor)
         public void SetCursor(IPlatformHandle cursor)

+ 2 - 2
src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs

@@ -49,9 +49,9 @@ namespace Avalonia.Controls.Embedding.Offscreen
         public Action<double> ScalingChanged { get; set; }
         public Action<double> ScalingChanged { get; set; }
         public void SetInputRoot(IInputRoot inputRoot) => InputRoot = inputRoot;
         public void SetInputRoot(IInputRoot inputRoot) => InputRoot = inputRoot;
 
 
-        public virtual Point PointToClient(Point point) => point;
+        public virtual Point PointToClient(PixelPoint point) => point.ToPoint(1);
 
 
-        public virtual Point PointToScreen(Point point) => point;
+        public virtual PixelPoint PointToScreen(Point point) => PixelPoint.FromPoint(point, 1);
 
 
         public virtual void SetCursor(IPlatformHandle cursor)
         public virtual void SetCursor(IPlatformHandle cursor)
         {
         {

+ 27 - 0
src/Avalonia.Controls/PixelPointEventArgs.cs

@@ -0,0 +1,27 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+using System;
+
+namespace Avalonia.Controls
+{
+    /// <summary>
+    /// Provides <see cref="PixelPoint"/> data for events.
+    /// </summary>
+    public class PixelPointEventArgs : EventArgs
+    {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="PixelPointEventArgs"/> class.
+        /// </summary>
+        /// <param name="point">The <see cref=PixelPoint"/> data.</param>
+        public PixelPointEventArgs(PixelPoint point)
+        {
+            Point = point;
+        }
+
+        /// <summary>
+        /// Gets the <see cref="PixelPoint"/> data.
+        /// </summary>
+        public PixelPoint Point { get; }
+    }
+}

+ 2 - 2
src/Avalonia.Controls/Platform/ITopLevelImpl.cs

@@ -82,14 +82,14 @@ namespace Avalonia.Platform
         /// </summary>
         /// </summary>
         /// <param name="point">The point in screen coordinates.</param>
         /// <param name="point">The point in screen coordinates.</param>
         /// <returns>The point in client coordinates.</returns>
         /// <returns>The point in client coordinates.</returns>
-        Point PointToClient(Point point);
+        Point PointToClient(PixelPoint point);
 
 
         /// <summary>
         /// <summary>
         /// Converts a point from client to screen coordinates.
         /// Converts a point from client to screen coordinates.
         /// </summary>
         /// </summary>
         /// <param name="point">The point in client coordinates.</param>
         /// <param name="point">The point in client coordinates.</param>
         /// <returns>The point in screen coordinates.</returns>
         /// <returns>The point in screen coordinates.</returns>
-        Point PointToScreen(Point point);
+        PixelPoint PointToScreen(Point point);
 
 
         /// <summary>
         /// <summary>
         /// Sets the cursor associated with the toplevel.
         /// Sets the cursor associated with the toplevel.

+ 3 - 3
src/Avalonia.Controls/Platform/IWindowBaseImpl.cs

@@ -27,14 +27,14 @@ namespace Avalonia.Platform
         void BeginResizeDrag(WindowEdge edge);
         void BeginResizeDrag(WindowEdge edge);
 
 
         /// <summary>
         /// <summary>
-        /// Gets position of the window relatively to the screen
+        /// Gets the position of the window relative in device pixels.
         /// </summary>
         /// </summary>
-        Point Position { get; set; }
+        PixelPoint Position { get; set; }
         
         
         /// <summary>
         /// <summary>
         /// Gets or sets a method called when the window's position changes.
         /// Gets or sets a method called when the window's position changes.
         /// </summary>
         /// </summary>
-        Action<Point> PositionChanged { get; set; }
+        Action<PixelPoint> PositionChanged { get; set; }
 
 
         /// <summary>
         /// <summary>
         /// Activates the window.
         /// Activates the window.

+ 4 - 4
src/Avalonia.Controls/Platform/Screen.cs

@@ -2,17 +2,17 @@
 {
 {
     public class Screen
     public class Screen
     {
     {
-        public Rect Bounds { get; }
+        public PixelRect Bounds { get; }
 
 
-        public Rect WorkingArea { get; }
+        public PixelRect WorkingArea { get; }
 
 
         public bool Primary { get; }
         public bool Primary { get; }
         
         
-        public Screen(Rect bounds, Rect workingArea, bool primary)
+        public Screen(PixelRect bounds, PixelRect workingArea, bool primary)
         {
         {
             this.Bounds = bounds;
             this.Bounds = bounds;
             this.WorkingArea = workingArea;
             this.WorkingArea = workingArea;
             this.Primary = primary;
             this.Primary = primary;
         } 
         } 
     }
     }
-}
+}

+ 0 - 27
src/Avalonia.Controls/PointEventArgs.cs

@@ -1,27 +0,0 @@
-// Copyright (c) The Avalonia Project. All rights reserved.
-// Licensed under the MIT license. See licence.md file in the project root for full license information.
-
-using System;
-
-namespace Avalonia.Controls
-{
-    /// <summary>
-    /// Provides <see cref="Point"/> data for events.
-    /// </summary>
-    public class PointEventArgs : EventArgs
-    {
-        /// <summary>
-        /// Initializes a new instance of the <see cref="PointEventArgs"/> class.
-        /// </summary>
-        /// <param name="point">The <see cref="Point"/> data.</param>
-        public PointEventArgs(Point point)
-        {
-            Point = point;
-        }
-
-        /// <summary>
-        /// Gets the <see cref="Point"/> data.
-        /// </summary>
-        public Point Point { get; }
-    }
-}

+ 13 - 17
src/Avalonia.Controls/Primitives/Popup.cs

@@ -380,7 +380,7 @@ namespace Avalonia.Controls.Primitives
         /// Gets the position for the popup based on the placement properties.
         /// Gets the position for the popup based on the placement properties.
         /// </summary>
         /// </summary>
         /// <returns>The popup's position in screen coordinates.</returns>
         /// <returns>The popup's position in screen coordinates.</returns>
-        protected virtual Point GetPosition()
+        protected virtual PixelPoint GetPosition()
         {
         {
             var result = GetPosition(PlacementTarget ?? this.GetVisualParent<Control>(), PlacementMode, PopupRoot,
             var result = GetPosition(PlacementTarget ?? this.GetVisualParent<Control>(), PlacementMode, PopupRoot,
                 HorizontalOffset, VerticalOffset);
                 HorizontalOffset, VerticalOffset);
@@ -388,35 +388,31 @@ namespace Avalonia.Controls.Primitives
             return result;
             return result;
         }
         }
 
 
-        internal static Point GetPosition(Control target, PlacementMode placement, PopupRoot popupRoot, double horizontalOffset, double verticalOffset)
+        internal static PixelPoint GetPosition(Control target, PlacementMode placement, PopupRoot popupRoot, double horizontalOffset, double verticalOffset)
         {
         {
-            var zero = default(Point);
-            var mode = placement;
-
-            if (target?.GetVisualRoot() == null)
-            {
-                mode = PlacementMode.Pointer;
-            }
+            var root = target?.GetVisualRoot();
+            var mode = root != null ? placement : PlacementMode.Pointer;
+            var scaling = root?.RenderScaling ?? 1;
 
 
             switch (mode)
             switch (mode)
             {
             {
                 case PlacementMode.Pointer:
                 case PlacementMode.Pointer:
                     if (popupRoot != null)
                     if (popupRoot != null)
                     {
                     {
-                        // Scales the Horizontal and Vertical offset to screen co-ordinates.
-                        var screenOffset = new Point(horizontalOffset * (popupRoot as ILayoutRoot).LayoutScaling,
-                            verticalOffset * (popupRoot as ILayoutRoot).LayoutScaling);
-                        return (((IInputRoot)popupRoot)?.MouseDevice?.Position ?? default(Point)) + screenOffset;
+                        var screenOffset = PixelPoint.FromPoint(new Point(horizontalOffset, verticalOffset), scaling);
+                        var mouseOffset = ((IInputRoot)popupRoot)?.MouseDevice?.Position ?? default;
+                        return new PixelPoint(
+                            screenOffset.X + mouseOffset.X,
+                            screenOffset.Y + mouseOffset.Y);
                     }
                     }
 
 
-                    return default(Point);
+                    return default;
 
 
                 case PlacementMode.Bottom:
                 case PlacementMode.Bottom:
-                    return target?.PointToScreen(new Point(0 + horizontalOffset, target.Bounds.Height + verticalOffset)) ??
-                           zero;
+                    return target?.PointToScreen(new Point(0 + horizontalOffset, target.Bounds.Height + verticalOffset)) ?? default;
 
 
                 case PlacementMode.Right:
                 case PlacementMode.Right:
-                    return target?.PointToScreen(new Point(target.Bounds.Width + horizontalOffset, 0 + verticalOffset)) ?? zero;
+                    return target?.PointToScreen(new Point(target.Bounds.Width + horizontalOffset, 0 + verticalOffset)) ?? default;
 
 
                 default:
                 default:
                     throw new InvalidOperationException("Invalid value for Popup.PlacementMode");
                     throw new InvalidOperationException("Invalid value for Popup.PlacementMode");

+ 5 - 5
src/Avalonia.Controls/Primitives/PopupRoot.cs

@@ -85,18 +85,18 @@ namespace Avalonia.Controls.Primitives
             if (screen != null)
             if (screen != null)
             {
             {
                 var scaling = VisualRoot.RenderScaling;
                 var scaling = VisualRoot.RenderScaling;
-
-                var screenX = Position.X + (Bounds.Width * scaling) - screen.Bounds.X;
-                var screenY = Position.Y + (Bounds.Height * scaling) - screen.Bounds.Y;
+                var bounds = PixelRect.FromRect(Bounds, scaling);
+                var screenX = Position.X + bounds.Width - screen.Bounds.X;
+                var screenY = Position.Y + bounds.Height - screen.Bounds.Y;
 
 
                 if (screenX > screen.Bounds.Width)
                 if (screenX > screen.Bounds.Width)
                 {
                 {
-                    Position = Position.WithX(Position.X - (screenX - screen.Bounds.Width));
+                    Position = Position.WithX(Position.X - screenX - bounds.Width);
                 }
                 }
 
 
                 if (screenY > screen.Bounds.Height)
                 if (screenY > screen.Bounds.Height)
                 {
                 {
-                    Position = Position.WithY(Position.Y - (screenY - screen.Bounds.Height));
+                    Position = Position.WithY(Position.Y - screenY - bounds.Height);
                 }
                 }
             }
             }
         }
         }

+ 7 - 7
src/Avalonia.Controls/Screens.cs

@@ -18,7 +18,7 @@ namespace Avalonia.Controls
             _iScreenImpl = iScreenImpl;
             _iScreenImpl = iScreenImpl;
         }
         }
 
 
-        public Screen ScreenFromBounds(Rect bounds){
+        public Screen ScreenFromBounds(PixelRect bounds){
         
         
             Screen currMaxScreen = null;
             Screen currMaxScreen = null;
             double maxAreaSize = 0;
             double maxAreaSize = 0;
@@ -39,16 +39,16 @@ namespace Avalonia.Controls
             return currMaxScreen;
             return currMaxScreen;
         }
         }
         
         
-        public Screen ScreenFromPoint(Point point)
+        public Screen ScreenFromPoint(PixelPoint point)
         {
         {
-            return All.FirstOrDefault(x=>x.Bounds.Contains(point));        
+            return All.FirstOrDefault(x => x.Bounds.Contains(point));        
         }
         }
 
 
         public Screen ScreenFromVisual(IVisual visual)
         public Screen ScreenFromVisual(IVisual visual)
         {
         {
-            Point tl = visual.PointToScreen(visual.Bounds.TopLeft);
-            Point br = visual.PointToScreen(visual.Bounds.BottomRight);
-            return ScreenFromBounds(new Rect(tl,br));
+            var tl = visual.PointToScreen(visual.Bounds.TopLeft);
+            var br = visual.PointToScreen(visual.Bounds.BottomRight);
+            return ScreenFromBounds(new PixelRect(tl, br));
         }
         }
     }
     }
-}
+}

+ 1 - 0
src/Avalonia.Controls/ToolTip.cs

@@ -236,6 +236,7 @@ namespace Avalonia.Controls
 
 
             _popup = new PopupRoot { Content = this,  };
             _popup = new PopupRoot { Content = this,  };
             ((ISetLogicalParent)_popup).SetParent(control);
             ((ISetLogicalParent)_popup).SetParent(control);
+
             _popup.Position = Popup.GetPosition(control, GetPlacement(control), _popup,
             _popup.Position = Popup.GetPosition(control, GetPlacement(control), _popup,
                 GetHorizontalOffset(control), GetVerticalOffset(control));
                 GetHorizontalOffset(control), GetVerticalOffset(control));
             _popup.Show();
             _popup.Show();

+ 4 - 4
src/Avalonia.Controls/TopLevel.cs

@@ -233,15 +233,15 @@ namespace Avalonia.Controls
         }
         }
 
 
         /// <inheritdoc/>
         /// <inheritdoc/>
-        Point IRenderRoot.PointToClient(Point p)
+        Point IRenderRoot.PointToClient(PixelPoint p)
         {
         {
-            return PlatformImpl?.PointToClient(p) ?? default(Point);
+            return PlatformImpl?.PointToClient(p) ?? default;
         }
         }
 
 
         /// <inheritdoc/>
         /// <inheritdoc/>
-        Point IRenderRoot.PointToScreen(Point p)
+        PixelPoint IRenderRoot.PointToScreen(Point p)
         {
         {
-            return PlatformImpl?.PointToScreen(p) ?? default(Point);
+            return PlatformImpl?.PointToScreen(p) ?? default;
         }
         }
         
         
         /// <summary>
         /// <summary>

+ 16 - 4
src/Avalonia.Controls/Window.cs

@@ -462,19 +462,31 @@ namespace Avalonia.Controls
 
 
         void SetWindowStartupLocation()
         void SetWindowStartupLocation()
         {
         {
+            var scaling = PlatformImpl?.Scaling ?? 1;
+
+            // TODO: We really need non-client size here.
+            var rect = new PixelRect(
+                PixelPoint.Origin,
+                PixelSize.FromSize(ClientSize, scaling));
+
             if (WindowStartupLocation == WindowStartupLocation.CenterScreen)
             if (WindowStartupLocation == WindowStartupLocation.CenterScreen)
             {
             {
-                var screen = Screens.ScreenFromPoint(Bounds.Position);
+                var screen = Screens.ScreenFromPoint(Position);
 
 
                 if (screen != null)
                 if (screen != null)
-                    Position = screen.WorkingArea.CenterRect(new Rect(ClientSize)).Position;
+                {
+                    Position = screen.WorkingArea.CenterRect(rect).Position;
+                }
             }
             }
             else if (WindowStartupLocation == WindowStartupLocation.CenterOwner)
             else if (WindowStartupLocation == WindowStartupLocation.CenterOwner)
             {
             {
                 if (Owner != null)
                 if (Owner != null)
                 {
                 {
-                    var positionAsSize = Owner.ClientSize / 2 - ClientSize / 2;
-                    Position = Owner.Position + new Point(positionAsSize.Width, positionAsSize.Height);
+                    // TODO: We really need non-client size here.
+                    var ownerRect = new PixelRect(
+                        Owner.Position,
+                        PixelSize.FromSize(Owner.ClientSize, scaling));
+                    Position = ownerRect.CenterRect(rect).Position;
                 }
                 }
             }
             }
         }
         }

+ 5 - 5
src/Avalonia.Controls/WindowBase.cs

@@ -81,7 +81,7 @@ namespace Avalonia.Controls
         /// <summary>
         /// <summary>
         /// Fired when the window position is changed.
         /// Fired when the window position is changed.
         /// </summary>
         /// </summary>
-        public event EventHandler<PointEventArgs> PositionChanged;
+        public event EventHandler<PixelPointEventArgs> PositionChanged;
 
 
         [CanBeNull]
         [CanBeNull]
         public new IWindowBaseImpl PlatformImpl => (IWindowBaseImpl) base.PlatformImpl;
         public new IWindowBaseImpl PlatformImpl => (IWindowBaseImpl) base.PlatformImpl;
@@ -98,9 +98,9 @@ namespace Avalonia.Controls
         /// <summary>
         /// <summary>
         /// Gets or sets the window position in screen coordinates.
         /// Gets or sets the window position in screen coordinates.
         /// </summary>
         /// </summary>
-        public Point Position
+        public PixelPoint Position
         {
         {
-            get { return PlatformImpl?.Position ?? default(Point); }
+            get { return PlatformImpl?.Position ?? PixelPoint.Origin; }
             set
             set
             {
             {
                 if (PlatformImpl is IWindowBaseImpl impl)
                 if (PlatformImpl is IWindowBaseImpl impl)
@@ -267,9 +267,9 @@ namespace Avalonia.Controls
         /// <see cref="IWindowBaseImpl.PositionChanged"/>.
         /// <see cref="IWindowBaseImpl.PositionChanged"/>.
         /// </summary>
         /// </summary>
         /// <param name="pos">The window position.</param>
         /// <param name="pos">The window position.</param>
-        private void HandlePositionChanged(Point pos)
+        private void HandlePositionChanged(PixelPoint pos)
         {
         {
-            PositionChanged?.Invoke(this, new PointEventArgs(pos));
+            PositionChanged?.Invoke(this, new PixelPointEventArgs(pos));
         }
         }
 
 
         /// <summary>
         /// <summary>

+ 2 - 2
src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs

@@ -35,8 +35,8 @@ namespace Avalonia.DesignerSupport.Remote
         {
         {
         }
         }
 
 
-        public Point Position { get; set; }
-        public Action<Point> PositionChanged { get; set; }
+        public PixelPoint Position { get; set; }
+        public Action<PixelPoint> PositionChanged { get; set; }
         public Action Deactivated { get; set; }
         public Action Deactivated { get; set; }
         public Action Activated { get; set; }
         public Action Activated { get; set; }
         public Func<bool> Closing { get; set; }
         public Func<bool> Closing { get; set; }

+ 5 - 5
src/Avalonia.DesignerSupport/Remote/Stubs.cs

@@ -29,8 +29,8 @@ namespace Avalonia.DesignerSupport.Remote
         public Func<bool> Closing { get; set; }
         public Func<bool> Closing { get; set; }
         public Action Closed { get; set; }
         public Action Closed { get; set; }
         public IMouseDevice MouseDevice { get; } = new MouseDevice();
         public IMouseDevice MouseDevice { get; } = new MouseDevice();
-        public Point Position { get; set; }
-        public Action<Point> PositionChanged { get; set; }
+        public PixelPoint Position { get; set; }
+        public Action<PixelPoint> PositionChanged { get; set; }
         public WindowState WindowState { get; set; }
         public WindowState WindowState { get; set; }
         public Action<WindowState> WindowStateChanged { get; set; }
         public Action<WindowState> WindowStateChanged { get; set; }
         public IRenderer CreateRenderer(IRenderRoot root) => new ImmediateRenderer(root);
         public IRenderer CreateRenderer(IRenderRoot root) => new ImmediateRenderer(root);
@@ -45,9 +45,9 @@ namespace Avalonia.DesignerSupport.Remote
         {
         {
         }
         }
 
 
-        public Point PointToClient(Point point) => point;
+        public Point PointToClient(PixelPoint p) => p.ToPoint(1);
 
 
-        public Point PointToScreen(Point point) => point;
+        public PixelPoint PointToScreen(Point p) => PixelPoint.FromPoint(p, 1);
 
 
         public void SetCursor(IPlatformHandle cursor)
         public void SetCursor(IPlatformHandle cursor)
         {
         {
@@ -157,6 +157,6 @@ namespace Avalonia.DesignerSupport.Remote
         public int ScreenCount => 1;
         public int ScreenCount => 1;
 
 
         public Screen[] AllScreens { get; } =
         public Screen[] AllScreens { get; } =
-            {new Screen(new Rect(0, 0, 4000, 4000), new Rect(0, 0, 4000, 4000), true)};
+            {new Screen(new PixelRect(0, 0, 4000, 4000), new PixelRect(0, 0, 4000, 4000), true)};
     }
     }
 }
 }

+ 1 - 1
src/Avalonia.Input/IMouseDevice.cs

@@ -11,6 +11,6 @@ namespace Avalonia.Input
         /// <summary>
         /// <summary>
         /// Gets the mouse position, in screen coordinates.
         /// Gets the mouse position, in screen coordinates.
         /// </summary>
         /// </summary>
-        Point Position { get; }
+        PixelPoint Position { get; }
     }
     }
 }
 }

+ 1 - 1
src/Avalonia.Input/MouseDevice.cs

@@ -54,7 +54,7 @@ namespace Avalonia.Input
         /// <summary>
         /// <summary>
         /// Gets the mouse position, in screen coordinates.
         /// Gets the mouse position, in screen coordinates.
         /// </summary>
         /// </summary>
-        public Point Position
+        public PixelPoint Position
         {
         {
             get;
             get;
             protected set;
             protected set;

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

@@ -12,11 +12,21 @@ namespace Avalonia.Native
             return new Point(pt.X, pt.Y);
             return new Point(pt.X, pt.Y);
         }
         }
 
 
+        public static PixelPoint ToAvaloniaPixelPoint(this AvnPoint pt)
+        {
+            return new PixelPoint((int)pt.X, (int)pt.Y);
+        }
+
         public static AvnPoint ToAvnPoint (this Point pt)
         public static AvnPoint ToAvnPoint (this Point pt)
         {
         {
             return new AvnPoint { X = pt.X, Y = pt.Y };
             return new AvnPoint { X = pt.X, Y = pt.Y };
         }
         }
 
 
+        public static AvnPoint ToAvnPoint(this PixelPoint pt)
+        {
+            return new AvnPoint { X = pt.X, Y = pt.Y };
+        }
+
         public static AvnSize ToAvnSize (this Size size)
         public static AvnSize ToAvnSize (this Size size)
         {
         {
             return new AvnSize { Height = size.Height, Width = size.Width };
             return new AvnSize { Height = size.Height, Width = size.Width };
@@ -31,5 +41,10 @@ namespace Avalonia.Native
         {
         {
             return new Rect(rect.X, rect.Y, rect.Width, rect.Height);
             return new Rect(rect.X, rect.Y, rect.Width, rect.Height);
         }
         }
+
+        public static PixelRect ToAvaloniaPixelRect(this AvnRect rect)
+        {
+            return new PixelRect((int)rect.X, (int)rect.Y, (int)rect.Width, (int)rect.Height);
+        }
     }
     }
 }
 }

+ 4 - 1
src/Avalonia.Native/ScreenImpl.cs

@@ -29,7 +29,10 @@ namespace Avalonia.Native
                 {
                 {
                     var screen = _native.GetScreen(i);
                     var screen = _native.GetScreen(i);
 
 
-                    result[i] = new Screen(screen.Bounds.ToAvaloniaRect(), screen.WorkingArea.ToAvaloniaRect(), screen.Primary);
+                    result[i] = new Screen(
+                        screen.Bounds.ToAvaloniaPixelRect(),
+                        screen.WorkingArea.ToAvaloniaPixelRect(),
+                        screen.Primary);
                 }
                 }
 
 
                 return result;
                 return result;

+ 7 - 7
src/Avalonia.Native/WindowImplBase.cs

@@ -159,7 +159,7 @@ namespace Avalonia.Native
 
 
             void IAvnWindowBaseEvents.PositionChanged(AvnPoint position)
             void IAvnWindowBaseEvents.PositionChanged(AvnPoint position)
             {
             {
-                _parent.PositionChanged?.Invoke(position.ToAvaloniaPoint());
+                _parent.PositionChanged?.Invoke(position.ToAvaloniaPixelPoint());
             }
             }
 
 
             void IAvnWindowBaseEvents.RawMouseEvent(AvnRawMouseEventType type, uint timeStamp, AvnInputModifiers modifiers, AvnPoint point, AvnVector delta)
             void IAvnWindowBaseEvents.RawMouseEvent(AvnRawMouseEventType type, uint timeStamp, AvnInputModifiers modifiers, AvnPoint point, AvnVector delta)
@@ -275,20 +275,20 @@ namespace Avalonia.Native
         }
         }
 
 
 
 
-        public Point Position
+        public PixelPoint Position
         {
         {
-            get => _native.GetPosition().ToAvaloniaPoint();
+            get => _native.GetPosition().ToAvaloniaPixelPoint();
             set => _native.SetPosition(value.ToAvnPoint());
             set => _native.SetPosition(value.ToAvnPoint());
         }
         }
 
 
-        public Point PointToClient(Point point)
+        public Point PointToClient(PixelPoint point)
         {
         {
             return _native.PointToClient(point.ToAvnPoint()).ToAvaloniaPoint();
             return _native.PointToClient(point.ToAvnPoint()).ToAvaloniaPoint();
         }
         }
 
 
-        public Point PointToScreen(Point point)
+        public PixelPoint PointToScreen(Point point)
         {
         {
-            return _native.PointToScreen(point.ToAvnPoint()).ToAvaloniaPoint();
+            return _native.PointToScreen(point.ToAvnPoint()).ToAvaloniaPixelPoint();
         }
         }
 
 
         public void Hide()
         public void Hide()
@@ -320,7 +320,7 @@ namespace Avalonia.Native
             _native.Cursor = newCursor.Cursor;
             _native.Cursor = newCursor.Cursor;
         }
         }
 
 
-        public Action<Point> PositionChanged { get; set; }
+        public Action<PixelPoint> PositionChanged { get; set; }
 
 
         public Action<RawInputEventArgs> Input { get; set; }
         public Action<RawInputEventArgs> Input { get; set; }
 
 

+ 4 - 4
src/Avalonia.Visuals/Rendering/IRenderRoot.cs

@@ -41,15 +41,15 @@ namespace Avalonia.Rendering
         /// <summary>
         /// <summary>
         /// Converts a point from screen to client coordinates.
         /// Converts a point from screen to client coordinates.
         /// </summary>
         /// </summary>
-        /// <param name="point">The point in screen coordinates.</param>
+        /// <param name="point">The point in screen device coordinates.</param>
         /// <returns>The point in client coordinates.</returns>
         /// <returns>The point in client coordinates.</returns>
-        Point PointToClient(Point point);
+        Point PointToClient(PixelPoint point);
 
 
         /// <summary>
         /// <summary>
         /// Converts a point from client to screen coordinates.
         /// Converts a point from client to screen coordinates.
         /// </summary>
         /// </summary>
         /// <param name="point">The point in client coordinates.</param>
         /// <param name="point">The point in client coordinates.</param>
-        /// <returns>The point in screen coordinates.</returns>
-        Point PointToScreen(Point point);
+        /// <returns>The point in screen device coordinates.</returns>
+        PixelPoint PointToScreen(Point point);
     }
     }
 }
 }

+ 8 - 4
src/Avalonia.Visuals/VisualExtensions.cs

@@ -18,10 +18,14 @@ namespace Avalonia
         /// <param name="visual">The visual.</param>
         /// <param name="visual">The visual.</param>
         /// <param name="point">The point in screen coordinates.</param>
         /// <param name="point">The point in screen coordinates.</param>
         /// <returns>The point in client coordinates.</returns>
         /// <returns>The point in client coordinates.</returns>
-        public static Point PointToClient(this IVisual visual, Point point)
+        public static Point PointToClient(this IVisual visual, PixelPoint point)
         {
         {
-            var p = GetRootAndPosition(visual);
-            return p.Item1.PointToClient(point - p.Item2);
+            var (root, offset) = GetRootAndPosition(visual);
+            var screenOffset = PixelPoint.FromPoint((Point)offset, root.RenderScaling);
+            var screenPoint = PixelPoint.FromPoint(
+                new Point(point.X - screenOffset.X, point.Y - screenOffset.Y),
+                root.RenderScaling);
+            return root.PointToClient(screenPoint);
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -30,7 +34,7 @@ namespace Avalonia
         /// <param name="visual">The visual.</param>
         /// <param name="visual">The visual.</param>
         /// <param name="point">The point in client coordinates.</param>
         /// <param name="point">The point in client coordinates.</param>
         /// <returns>The point in screen coordinates.</returns>
         /// <returns>The point in screen coordinates.</returns>
-        public static Point PointToScreen(this IVisual visual, Point point)
+        public static PixelPoint PointToScreen(this IVisual visual, Point point)
         {
         {
             var p = GetRootAndPosition(visual);
             var p = GetRootAndPosition(visual);
             return p.Item1.PointToScreen(point + p.Item2);
             return p.Item1.PointToScreen(point + p.Item2);

+ 8 - 8
src/Avalonia.X11/X11Screens.cs

@@ -20,7 +20,7 @@ namespace Avalonia.X11
 
 
         static unsafe X11Screen[] UpdateWorkArea(X11Info info, X11Screen[] screens)
         static unsafe X11Screen[] UpdateWorkArea(X11Info info, X11Screen[] screens)
         {
         {
-            var rect = default(Rect);
+            var rect = default(PixelRect);
             foreach (var s in screens)
             foreach (var s in screens)
             {
             {
                 rect = rect.Union(s.Bounds);
                 rect = rect.Union(s.Bounds);
@@ -46,7 +46,7 @@ namespace Avalonia.X11
                 return screens;
                 return screens;
 
 
             var pwa = (IntPtr*)prop;
             var pwa = (IntPtr*)prop;
-            var wa = new Rect(pwa[0].ToInt32(), pwa[1].ToInt32(), pwa[2].ToInt32(), pwa[3].ToInt32());
+            var wa = new PixelRect(pwa[0].ToInt32(), pwa[1].ToInt32(), pwa[2].ToInt32(), pwa[3].ToInt32());
 
 
             
             
             foreach (var s in screens) 
             foreach (var s in screens) 
@@ -105,7 +105,7 @@ namespace Avalonia.X11
 
 
                         density *= _settings.GlobalScaleFactor;
                         density *= _settings.GlobalScaleFactor;
                         
                         
-                        var bounds = new Rect(mon.X, mon.Y, mon.Width, mon.Height);
+                        var bounds = new PixelRect(mon.X, mon.Y, mon.Width, mon.Height);
                         screens[c] = new X11Screen(bounds, 
                         screens[c] = new X11Screen(bounds, 
                             mon.Primary != 0, 
                             mon.Primary != 0, 
                             name,
                             name,
@@ -130,12 +130,12 @@ namespace Avalonia.X11
                     Screens = UpdateWorkArea(info,
                     Screens = UpdateWorkArea(info,
                         new[]
                         new[]
                         {
                         {
-                            new X11Screen(new Rect(0, 0, geo.width, geo.height), true, "Default", null,
+                            new X11Screen(new PixelRect(0, 0, geo.width, geo.height), true, "Default", null,
                                 settings.GlobalScaleFactor)
                                 settings.GlobalScaleFactor)
                         });
                         });
                 }
                 }
 
 
-                Screens = new[] {new X11Screen(new Rect(0, 0, 1920, 1280), true, "Default", null, settings.GlobalScaleFactor)};
+                Screens = new[] {new X11Screen(new PixelRect(0, 0, 1920, 1280), true, "Default", null, settings.GlobalScaleFactor)};
             }
             }
 
 
             public X11Screen[] Screens { get; }
             public X11Screen[] Screens { get; }
@@ -220,12 +220,12 @@ namespace Avalonia.X11
     {
     {
         public bool Primary { get; }
         public bool Primary { get; }
         public string Name { get; set; }
         public string Name { get; set; }
-        public Rect Bounds { get; set; }
+        public PixelRect Bounds { get; set; }
         public Size? PhysicalSize { get; set; }
         public Size? PhysicalSize { get; set; }
         public double PixelDensity { get; set; }
         public double PixelDensity { get; set; }
-        public Rect WorkingArea { get; set; }
+        public PixelRect WorkingArea { get; set; }
 
 
-        public X11Screen(Rect bounds, bool primary,
+        public X11Screen(PixelRect bounds, bool primary,
             string name, Size? physicalSize, double? pixelDensity)
             string name, Size? physicalSize, double? pixelDensity)
         {
         {
             Primary = primary;
             Primary = primary;

+ 11 - 9
src/Avalonia.X11/X11Window.cs

@@ -24,12 +24,12 @@ namespace Avalonia.X11
         private readonly X11Info _x11;
         private readonly X11Info _x11;
         private bool _invalidated;
         private bool _invalidated;
         private XConfigureEvent? _configure;
         private XConfigureEvent? _configure;
-        private Point? _configurePoint;
+        private PixelPoint? _configurePoint;
         private bool _triggeredExpose;
         private bool _triggeredExpose;
         private IInputRoot _inputRoot;
         private IInputRoot _inputRoot;
         private readonly IMouseDevice _mouse;
         private readonly IMouseDevice _mouse;
         private readonly IKeyboardDevice _keyboard;
         private readonly IKeyboardDevice _keyboard;
-        private Point? _position;
+        private PixelPoint? _position;
         private PixelSize _realSize;
         private PixelSize _realSize;
         private IntPtr _handle;
         private IntPtr _handle;
         private IntPtr _xic;
         private IntPtr _xic;
@@ -235,7 +235,7 @@ namespace Avalonia.X11
         public Func<bool> Closing { get; set; }
         public Func<bool> Closing { get; set; }
         public Action<WindowState> WindowStateChanged { get; set; }
         public Action<WindowState> WindowStateChanged { get; set; }
         public Action Closed { get; set; }
         public Action Closed { get; set; }
-        public Action<Point> PositionChanged { get; set; }
+        public Action<PixelPoint> PositionChanged { get; set; }
 
 
         public IRenderer CreateRenderer(IRenderRoot root) =>
         public IRenderer CreateRenderer(IRenderRoot root) =>
             new DeferredRenderer(root, AvaloniaLocator.Current.GetService<IRenderLoop>());
             new DeferredRenderer(root, AvaloniaLocator.Current.GetService<IRenderLoop>());
@@ -321,13 +321,13 @@ namespace Avalonia.X11
                 var needEnqueue = (_configure == null);
                 var needEnqueue = (_configure == null);
                 _configure = ev.ConfigureEvent;
                 _configure = ev.ConfigureEvent;
                 if (ev.ConfigureEvent.override_redirect || ev.ConfigureEvent.send_event)
                 if (ev.ConfigureEvent.override_redirect || ev.ConfigureEvent.send_event)
-                    _configurePoint = new Point(ev.ConfigureEvent.x, ev.ConfigureEvent.y);
+                    _configurePoint = new PixelPoint(ev.ConfigureEvent.x, ev.ConfigureEvent.y);
                 else
                 else
                 {
                 {
                     XTranslateCoordinates(_x11.Display, _handle, _x11.RootWindow,
                     XTranslateCoordinates(_x11.Display, _handle, _x11.RootWindow,
                         0, 0,
                         0, 0,
                         out var tx, out var ty, out _);
                         out var tx, out var ty, out _);
-                    _configurePoint = new Point(tx, ty);
+                    _configurePoint = new PixelPoint(tx, ty);
                 }
                 }
                 if (needEnqueue)
                 if (needEnqueue)
                     Dispatcher.UIThread.Post(() =>
                     Dispatcher.UIThread.Post(() =>
@@ -660,9 +660,11 @@ namespace Avalonia.X11
         public void Hide() => XUnmapWindow(_x11.Display, _handle);
         public void Hide() => XUnmapWindow(_x11.Display, _handle);
         
         
         
         
-        public Point PointToClient(Point point) => new Point((point.X - Position.X) / Scaling, (point.Y - Position.Y) / Scaling);
+        public Point PointToClient(PixelPoint point) => new Point((point.X - Position.X) / Scaling, (point.Y - Position.Y) / Scaling);
 
 
-        public Point PointToScreen(Point point) => new Point(point.X * Scaling + Position.X, point.Y * Scaling + Position.Y);
+        public PixelPoint PointToScreen(Point point) => new PixelPoint(
+            (int)(point.X * Scaling + Position.X),
+            (int)(point.Y * Scaling + Position.Y));
         
         
         public void SetSystemDecorations(bool enabled)
         public void SetSystemDecorations(bool enabled)
         {
         {
@@ -716,7 +718,7 @@ namespace Avalonia.X11
 
 
         public IPlatformHandle Handle { get; }
         public IPlatformHandle Handle { get; }
         
         
-        public Point Position
+        public PixelPoint Position
         {
         {
             get => _position ?? default;
             get => _position ?? default;
             set
             set
@@ -752,7 +754,7 @@ namespace Avalonia.X11
 
 
         public IScreenImpl Screen => _platform.Screens;
         public IScreenImpl Screen => _platform.Screens;
 
 
-        public Size MaxClientSize => _platform.X11Screens.Screens.Select(s => s.Bounds.Size / s.PixelDensity)
+        public Size MaxClientSize => _platform.X11Screens.Screens.Select(s => s.Bounds.Size.ToSize(s.PixelDensity))
             .OrderByDescending(x => x.Width + x.Height).FirstOrDefault();
             .OrderByDescending(x => x.Width + x.Height).FirstOrDefault();
 
 
 
 

+ 2 - 2
src/Gtk/Avalonia.Gtk3/GtkScreen.cs

@@ -6,7 +6,7 @@ namespace Avalonia.Gtk3
     {
     {
         private readonly int _screenId;
         private readonly int _screenId;
         
         
-        public GtkScreen(Rect bounds, Rect workingArea, bool primary, int screenId) : base(bounds, workingArea, primary)
+        public GtkScreen(PixelRect bounds, PixelRect workingArea, bool primary, int screenId) : base(bounds, workingArea, primary)
         {
         {
             this._screenId = screenId;
             this._screenId = screenId;
         }
         }
@@ -21,4 +21,4 @@ namespace Avalonia.Gtk3
             return (obj is GtkScreen screen) ? this._screenId == screen._screenId : base.Equals(obj);
             return (obj is GtkScreen screen) ? this._screenId == screen._screenId : base.Equals(obj);
         }
         }
     }
     }
-}
+}

+ 2 - 2
src/Gtk/Avalonia.Gtk3/ScreenImpl.cs

@@ -27,8 +27,8 @@ namespace Avalonia.Gtk3
                         GdkRectangle workArea = new GdkRectangle(), geometry = new GdkRectangle();
                         GdkRectangle workArea = new GdkRectangle(), geometry = new GdkRectangle();
                         Native.GdkScreenGetMonitorGeometry(screen, i, ref geometry);
                         Native.GdkScreenGetMonitorGeometry(screen, i, ref geometry);
                         Native.GdkScreenGetMonitorWorkarea(screen, i, ref workArea);
                         Native.GdkScreenGetMonitorWorkarea(screen, i, ref workArea);
-                        Rect workAreaRect = new Rect(workArea.X, workArea.Y, workArea.Width, workArea.Height);
-                        Rect geometryRect = new Rect(geometry.X, geometry.Y, geometry.Width, geometry.Height);
+                        PixelRect workAreaRect = new PixelRect(workArea.X, workArea.Y, workArea.Width, workArea.Height);
+                        PixelRect geometryRect = new PixelRect(geometry.X, geometry.Y, geometry.Width, geometry.Height);
                         GtkScreen s = new GtkScreen(geometryRect, workAreaRect, i == primary, i);
                         GtkScreen s = new GtkScreen(geometryRect, workAreaRect, i == primary, i);
                         screens[i] = s;
                         screens[i] = s;
                     }
                     }

+ 7 - 7
src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs

@@ -24,7 +24,7 @@ namespace Avalonia.Gtk3
         private readonly EglGlPlatformSurface _egl;
         private readonly EglGlPlatformSurface _egl;
         protected readonly List<IDisposable> Disposables = new List<IDisposable>();
         protected readonly List<IDisposable> Disposables = new List<IDisposable>();
         private Size _lastSize;
         private Size _lastSize;
-        private Point _lastPosition;
+        private PixelPoint _lastPosition;
         private double _lastScaling;
         private double _lastScaling;
         private uint _lastKbdEvent;
         private uint _lastKbdEvent;
         private uint _lastSmoothScrollEvent;
         private uint _lastSmoothScrollEvent;
@@ -383,7 +383,7 @@ namespace Avalonia.Gtk3
         public Action<Rect> Paint { get; set; }
         public Action<Rect> Paint { get; set; }
         public Action<Size> Resized { get; set; }
         public Action<Size> Resized { get; set; }
         public Action<double> ScalingChanged { get; set; } //TODO
         public Action<double> ScalingChanged { get; set; } //TODO
-        public Action<Point> PositionChanged { get; set; }
+        public Action<PixelPoint> PositionChanged { get; set; }
 
 
         public void Activate() => Native.GtkWidgetActivate(GtkWidget);
         public void Activate() => Native.GtkWidgetActivate(GtkWidget);
 
 
@@ -402,7 +402,7 @@ namespace Avalonia.Gtk3
             Dispatcher.UIThread.Post(() => Input?.Invoke(args), DispatcherPriority.Input);
             Dispatcher.UIThread.Post(() => Input?.Invoke(args), DispatcherPriority.Input);
         }
         }
 
 
-        public Point PointToClient(Point point)
+        public Point PointToClient(PixelPoint point)
         {
         {
             int x, y;
             int x, y;
             Native.GdkWindowGetOrigin(Native.GtkWidgetGetWindow(GtkWidget), out x, out y);
             Native.GdkWindowGetOrigin(Native.GtkWidgetGetWindow(GtkWidget), out x, out y);
@@ -410,11 +410,11 @@ namespace Avalonia.Gtk3
             return new Point(point.X - x, point.Y - y);
             return new Point(point.X - x, point.Y - y);
         }
         }
 
 
-        public Point PointToScreen(Point point)
+        public PixelPoint PointToScreen(Point point)
         {
         {
             int x, y;
             int x, y;
             Native.GdkWindowGetOrigin(Native.GtkWidgetGetWindow(GtkWidget), out x, out y);
             Native.GdkWindowGetOrigin(Native.GtkWidgetGetWindow(GtkWidget), out x, out y);
-            return new Point(point.X + x, point.Y + y);
+            return new PixelPoint((int)(point.X + x), (int)(point.Y + y));
         }
         }
 
 
         public void SetCursor(IPlatformHandle cursor)
         public void SetCursor(IPlatformHandle cursor)
@@ -490,13 +490,13 @@ namespace Avalonia.Gtk3
             get;
             get;
         } = new ScreenImpl();
         } = new ScreenImpl();
 
 
-        public Point Position
+        public PixelPoint Position
         {
         {
             get
             get
             {
             {
                 int x, y;
                 int x, y;
                 Native.GtkWindowGetPosition(GtkWidget, out x, out y);
                 Native.GtkWindowGetPosition(GtkWidget, out x, out y);
-                return new Point(x, y);
+                return new PixelPoint(x, y);
             }
             }
             set { Native.GtkWindowMove(GtkWidget, (int)value.X, (int)value.Y); }
             set { Native.GtkWindowMove(GtkWidget, (int)value.X, (int)value.Y); }
         }
         }

+ 2 - 2
src/Linux/Avalonia.LinuxFramebuffer/FramebufferToplevelImpl.cs

@@ -51,9 +51,9 @@ namespace Avalonia.LinuxFramebuffer
             InputRoot = inputRoot;
             InputRoot = inputRoot;
         }
         }
 
 
-        public Point PointToClient(Point point) => point;
+        public Point PointToClient(PixelPoint p) => p.ToPoint(1);
 
 
-        public Point PointToScreen(Point point) => point;
+        public PixelPoint PointToScreen(Point p) => PixelPoint.FromPoint(p, 1);
 
 
         public void SetCursor(IPlatformHandle cursor)
         public void SetCursor(IPlatformHandle cursor)
         {
         {

+ 2 - 0
src/Windows/Avalonia.Win32.Interop/Wpf/WpfInteropExtensions.cs

@@ -3,7 +3,9 @@
     static class WpfInteropExtensions
     static class WpfInteropExtensions
     {
     {
         public static System.Windows.Point ToWpfPoint(this Point pt) => new System.Windows.Point(pt.X, pt.Y);
         public static System.Windows.Point ToWpfPoint(this Point pt) => new System.Windows.Point(pt.X, pt.Y);
+        public static System.Windows.Point ToWpfPoint(this PixelPoint pt) => new System.Windows.Point(pt.X, pt.Y);
         public static Point ToAvaloniaPoint(this System.Windows.Point pt) => new Point(pt.X, pt.Y);
         public static Point ToAvaloniaPoint(this System.Windows.Point pt) => new Point(pt.X, pt.Y);
+        public static PixelPoint ToAvaloniaPixelPoint(this System.Windows.Point pt) => new PixelPoint((int)pt.X, (int)pt.Y);
         public static System.Windows.Size ToWpfSize(this Size pt) => new System.Windows.Size(pt.Width, pt.Height);
         public static System.Windows.Size ToWpfSize(this Size pt) => new System.Windows.Size(pt.Width, pt.Height);
         public static Size ToAvaloniaSize(this System.Windows.Size pt) => new Size(pt.Width, pt.Height);
         public static Size ToAvaloniaSize(this System.Windows.Size pt) => new Size(pt.Width, pt.Height);
     }
     }

+ 2 - 2
src/Windows/Avalonia.Win32.Interop/Wpf/WpfTopLevelImpl.cs

@@ -135,9 +135,9 @@ namespace Avalonia.Win32.Interop.Wpf
 
 
         void ITopLevelImpl.SetInputRoot(IInputRoot inputRoot) => _inputRoot = inputRoot;
         void ITopLevelImpl.SetInputRoot(IInputRoot inputRoot) => _inputRoot = inputRoot;
 
 
-        Point ITopLevelImpl.PointToClient(Point point) => PointFromScreen(point.ToWpfPoint()).ToAvaloniaPoint();
+        Point ITopLevelImpl.PointToClient(PixelPoint point) => PointFromScreen(point.ToWpfPoint()).ToAvaloniaPoint();
 
 
-        Point ITopLevelImpl.PointToScreen(Point point) => PointToScreen(point.ToWpfPoint()).ToAvaloniaPoint();
+        PixelPoint ITopLevelImpl.PointToScreen(Point point) => PointToScreen(point.ToWpfPoint()).ToAvaloniaPixelPoint();
 
 
         protected override void OnLostFocus(RoutedEventArgs e) => LostFocus?.Invoke();
         protected override void OnLostFocus(RoutedEventArgs e) => LostFocus?.Invoke();
 
 

+ 1 - 1
src/Windows/Avalonia.Win32/OleDropTarget.cs

@@ -177,7 +177,7 @@ namespace Avalonia.Win32
             int x = (int)dragPoint;
             int x = (int)dragPoint;
             int y = (int)(dragPoint >> 32);
             int y = (int)(dragPoint >> 32);
 
 
-            Point screenPt = new Point(x, y);
+            var screenPt = new PixelPoint(x, y);
             return _target.PointToClient(screenPt);
             return _target.PointToClient(screenPt);
         }
         }
     }
     }

+ 3 - 3
src/Windows/Avalonia.Win32/ScreenImpl.cs

@@ -31,10 +31,10 @@ namespace Avalonia.Win32
                             {
                             {
                                 RECT bounds = monitorInfo.rcMonitor;
                                 RECT bounds = monitorInfo.rcMonitor;
                                 RECT workingArea = monitorInfo.rcWork;
                                 RECT workingArea = monitorInfo.rcWork;
-                                Rect avaloniaBounds = new Rect(bounds.left, bounds.top, bounds.right - bounds.left,
+                                PixelRect avaloniaBounds = new PixelRect(bounds.left, bounds.top, bounds.right - bounds.left,
                                     bounds.bottom - bounds.top);
                                     bounds.bottom - bounds.top);
-                                Rect avaloniaWorkArea =
-                                    new Rect(workingArea.left, workingArea.top, workingArea.right - workingArea.left,
+                                PixelRect avaloniaWorkArea =
+                                    new PixelRect(workingArea.left, workingArea.top, workingArea.right - workingArea.left,
                                         workingArea.bottom - workingArea.top);
                                         workingArea.bottom - workingArea.top);
                                 screens[index] =
                                 screens[index] =
                                     new WinScreen(avaloniaBounds, avaloniaWorkArea, monitorInfo.dwFlags == 1,
                                     new WinScreen(avaloniaBounds, avaloniaWorkArea, monitorInfo.dwFlags == 1,

+ 2 - 2
src/Windows/Avalonia.Win32/WinScreen.cs

@@ -7,7 +7,7 @@ namespace Avalonia.Win32
     {
     {
         private readonly IntPtr _hMonitor;
         private readonly IntPtr _hMonitor;
 
 
-        public WinScreen(Rect bounds, Rect workingArea, bool primary, IntPtr hMonitor) : base(bounds, workingArea, primary)
+        public WinScreen(PixelRect bounds, PixelRect workingArea, bool primary, IntPtr hMonitor) : base(bounds, workingArea, primary)
         {
         {
             this._hMonitor = hMonitor;
             this._hMonitor = hMonitor;
         }
         }
@@ -22,4 +22,4 @@ namespace Avalonia.Win32
             return (obj is WinScreen screen) ? this._hMonitor == screen._hMonitor : base.Equals(obj);
             return (obj is WinScreen screen) ? this._hMonitor == screen._hMonitor : base.Equals(obj);
         }
         }
     }
     }
-}
+}

+ 11 - 11
src/Windows/Avalonia.Win32/WindowImpl.cs

@@ -87,7 +87,7 @@ namespace Avalonia.Win32
 
 
         public Action<double> ScalingChanged { get; set; }
         public Action<double> ScalingChanged { get; set; }
 
 
-        public Action<Point> PositionChanged { get; set; }
+        public Action<PixelPoint> PositionChanged { get; set; }
 
 
         public Action<WindowState> WindowStateChanged { get; set; }
         public Action<WindowState> WindowStateChanged { get; set; }
 
 
@@ -297,19 +297,19 @@ namespace Avalonia.Win32
             UnmanagedMethods.InvalidateRect(_hwnd, ref r, false);
             UnmanagedMethods.InvalidateRect(_hwnd, ref r, false);
         }
         }
 
 
-        public Point PointToClient(Point point)
+        public Point PointToClient(PixelPoint point)
         {
         {
             var p = new UnmanagedMethods.POINT { X = (int)point.X, Y = (int)point.Y };
             var p = new UnmanagedMethods.POINT { X = (int)point.X, Y = (int)point.Y };
             UnmanagedMethods.ScreenToClient(_hwnd, ref p);
             UnmanagedMethods.ScreenToClient(_hwnd, ref p);
             return new Point(p.X, p.Y) / Scaling;
             return new Point(p.X, p.Y) / Scaling;
         }
         }
 
 
-        public Point PointToScreen(Point point)
+        public PixelPoint PointToScreen(Point point)
         {
         {
             point *= Scaling;
             point *= Scaling;
             var p = new UnmanagedMethods.POINT { X = (int)point.X, Y = (int)point.Y };
             var p = new UnmanagedMethods.POINT { X = (int)point.X, Y = (int)point.Y };
             UnmanagedMethods.ClientToScreen(_hwnd, ref p);
             UnmanagedMethods.ClientToScreen(_hwnd, ref p);
-            return new Point(p.X, p.Y);
+            return new PixelPoint(p.X, p.Y);
         }
         }
 
 
         public void SetInputRoot(IInputRoot inputRoot)
         public void SetInputRoot(IInputRoot inputRoot)
@@ -357,21 +357,21 @@ namespace Avalonia.Win32
 #endif
 #endif
         }
         }
 
 
-        public Point Position
+        public PixelPoint Position
         {
         {
             get
             get
             {
             {
                 UnmanagedMethods.RECT rc;
                 UnmanagedMethods.RECT rc;
                 UnmanagedMethods.GetWindowRect(_hwnd, out rc);
                 UnmanagedMethods.GetWindowRect(_hwnd, out rc);
-                return new Point(rc.left, rc.top);
+                return new PixelPoint(rc.left, rc.top);
             }
             }
             set
             set
             {
             {
                 UnmanagedMethods.SetWindowPos(
                 UnmanagedMethods.SetWindowPos(
                     Handle.Handle,
                     Handle.Handle,
                     IntPtr.Zero,
                     IntPtr.Zero,
-                    (int)value.X,
-                    (int)value.Y,
+                    value.X,
+                    value.Y,
                     0,
                     0,
                     0,
                     0,
                     UnmanagedMethods.SetWindowPosFlags.SWP_NOSIZE | UnmanagedMethods.SetWindowPosFlags.SWP_NOACTIVATE);
                     UnmanagedMethods.SetWindowPosFlags.SWP_NOSIZE | UnmanagedMethods.SetWindowPosFlags.SWP_NOACTIVATE);
@@ -661,7 +661,7 @@ namespace Avalonia.Win32
                     return IntPtr.Zero;
                     return IntPtr.Zero;
 
 
                 case UnmanagedMethods.WindowsMessage.WM_MOVE:
                 case UnmanagedMethods.WindowsMessage.WM_MOVE:
-                    PositionChanged?.Invoke(new Point((short)(ToInt32(lParam) & 0xffff), (short)(ToInt32(lParam) >> 16)));
+                    PositionChanged?.Invoke(new PixelPoint((short)(ToInt32(lParam) & 0xffff), (short)(ToInt32(lParam) >> 16)));
                     return IntPtr.Zero;
                     return IntPtr.Zero;
 
 
                 case UnmanagedMethods.WindowsMessage.WM_GETMINMAXINFO:
                 case UnmanagedMethods.WindowsMessage.WM_GETMINMAXINFO:
@@ -785,9 +785,9 @@ namespace Avalonia.Win32
             return new Point((short)(ToInt32(lParam) & 0xffff), (short)(ToInt32(lParam) >> 16)) / Scaling;
             return new Point((short)(ToInt32(lParam) & 0xffff), (short)(ToInt32(lParam) >> 16)) / Scaling;
         }
         }
 
 
-        private Point PointFromLParam(IntPtr lParam)
+        private PixelPoint PointFromLParam(IntPtr lParam)
         {
         {
-            return new Point((short)(ToInt32(lParam) & 0xffff), (short)(ToInt32(lParam) >> 16));
+            return new PixelPoint((short)(ToInt32(lParam) & 0xffff), (short)(ToInt32(lParam) >> 16));
         }
         }
 
 
         private Point ScreenToClient(Point point)
         private Point ScreenToClient(Point point)

+ 2 - 2
src/iOS/Avalonia.iOS/TopLevelImpl.cs

@@ -68,9 +68,9 @@ namespace Avalonia.iOS
 
 
         public void SetInputRoot(IInputRoot inputRoot) => _inputRoot = inputRoot;
         public void SetInputRoot(IInputRoot inputRoot) => _inputRoot = inputRoot;
 
 
-        public Point PointToClient(Point point) => point;
+        public Point PointToClient(PixelPoint point) => point.ToPoint(1);
 
 
-        public Point PointToScreen(Point point) => point;
+        public PixelPoint PointToScreen(Point point) => PixelPoint.FromPoint(point, 1);
 
 
         public void SetCursor(IPlatformHandle cursor)
         public void SetCursor(IPlatformHandle cursor)
         {
         {

+ 2 - 2
tests/Avalonia.Controls.UnitTests/ButtonTests.cs

@@ -238,9 +238,9 @@ namespace Avalonia.Controls.UnitTests
 
 
             public void Invalidate(Rect rect) => throw new NotImplementedException();
             public void Invalidate(Rect rect) => throw new NotImplementedException();
 
 
-            public Point PointToClient(Point point) => throw new NotImplementedException();
+            public Point PointToClient(PixelPoint p) => throw new NotImplementedException();
 
 
-            public Point PointToScreen(Point point) => throw new NotImplementedException();
+            public PixelPoint PointToScreen(Point p) => throw new NotImplementedException();
         }
         }
 
 
         private void RaisePointerPressed(Button button, IMouseDevice device, int clickCount, MouseButton mouseButton)
         private void RaisePointerPressed(Button button, IMouseDevice device, int clickCount, MouseButton mouseButton)

+ 1 - 1
tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs

@@ -160,7 +160,7 @@ namespace Avalonia.Controls.UnitTests
 
 
         private IDisposable Application()
         private IDisposable Application()
         {
         {
-            var screen = new Rect(new Point(), new Size(100, 100));
+            var screen = new PixelRect(new PixelPoint(), new PixelSize(100, 100));
             var screenImpl = new Mock<IScreenImpl>();
             var screenImpl = new Mock<IScreenImpl>();
             screenImpl.Setup(x => x.ScreenCount).Returns(1);
             screenImpl.Setup(x => x.ScreenCount).Returns(1);
             screenImpl.Setup(X => X.AllScreens).Returns( new[] { new Screen(screen, screen, true) });
             screenImpl.Setup(X => X.AllScreens).Returns( new[] { new Screen(screen, screen, true) });

+ 4 - 18
tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization.cs

@@ -336,25 +336,11 @@ namespace Avalonia.Controls.UnitTests.Presenters
             public double LayoutScaling => 1;
             public double LayoutScaling => 1;
 
 
             public ILayoutManager LayoutManager { get; } = new LayoutManager();
             public ILayoutManager LayoutManager { get; } = new LayoutManager();
-            public IRenderTarget CreateRenderTarget()
-            {
-                throw new NotImplementedException();
-            }
-
-            public void Invalidate(Rect rect)
-            {
-                throw new NotImplementedException();
-            }
 
 
-            public Point PointToClient(Point point)
-            {
-                throw new NotImplementedException();
-            }
-
-            public Point PointToScreen(Point point)
-            {
-                throw new NotImplementedException();
-            }
+            public IRenderTarget CreateRenderTarget() => throw new NotImplementedException();
+            public void Invalidate(Rect rect) => throw new NotImplementedException();
+            public Point PointToClient(PixelPoint p) => throw new NotImplementedException();
+            public PixelPoint PointToScreen(Point p) => throw new NotImplementedException();
         }
         }
 
 
         private class TestItemsPresenter : ItemsPresenter
         private class TestItemsPresenter : ItemsPresenter

+ 4 - 24
tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization_Simple.cs

@@ -1077,30 +1077,10 @@ namespace Avalonia.Controls.UnitTests.Presenters
 
 
             public ILayoutManager LayoutManager { get; } = new LayoutManager();
             public ILayoutManager LayoutManager { get; } = new LayoutManager();
 
 
-            public IRenderTarget CreateRenderTarget()
-            {
-                throw new NotImplementedException();
-            }
-
-            public void Invalidate(Rect rect)
-            {
-                throw new NotImplementedException();
-            }
-
-            public Point PointToClient(Point point)
-            {
-                throw new NotImplementedException();
-            }
-
-            public Point PointToScreen(Point point)
-            {
-                throw new NotImplementedException();
-            }
-
-            protected override Size MeasureOverride(Size availableSize)
-            {
-                return base.MeasureOverride(availableSize);
-            }
+            public IRenderTarget CreateRenderTarget() => throw new NotImplementedException();
+            public void Invalidate(Rect rect) => throw new NotImplementedException();
+            public Point PointToClient(PixelPoint p) => throw new NotImplementedException();
+            public PixelPoint PointToScreen(Point p) => throw new NotImplementedException();
         }
         }
 
 
         private class TestItemsPresenter : ItemsPresenter
         private class TestItemsPresenter : ItemsPresenter

+ 11 - 11
tests/Avalonia.Controls.UnitTests/WindowTests.cs

@@ -278,8 +278,8 @@ namespace Avalonia.Controls.UnitTests
         [Fact]
         [Fact]
         public void Window_Should_Be_Centered_When_WindowStartupLocation_Is_CenterScreen()
         public void Window_Should_Be_Centered_When_WindowStartupLocation_Is_CenterScreen()
         {
         {
-            var screen1 = new Mock<Screen>(new Rect(new Size(1920, 1080)), new Rect(new Size(1920, 1040)), true);
-            var screen2 = new Mock<Screen>(new Rect(new Size(1366, 768)), new Rect(new Size(1366, 728)), false);
+            var screen1 = new Mock<Screen>(new PixelRect(new PixelSize(1920, 1080)), new PixelRect(new PixelSize(1920, 1040)), true);
+            var screen2 = new Mock<Screen>(new PixelRect(new PixelSize(1366, 768)), new PixelRect(new PixelSize(1366, 728)), false);
 
 
             var screens = new Mock<IScreenImpl>();
             var screens = new Mock<IScreenImpl>();
             screens.Setup(x => x.AllScreens).Returns(new Screen[] { screen1.Object, screen2.Object });
             screens.Setup(x => x.AllScreens).Returns(new Screen[] { screen1.Object, screen2.Object });
@@ -294,13 +294,13 @@ namespace Avalonia.Controls.UnitTests
             {
             {
                 var window = new Window(windowImpl.Object);
                 var window = new Window(windowImpl.Object);
                 window.WindowStartupLocation = WindowStartupLocation.CenterScreen;
                 window.WindowStartupLocation = WindowStartupLocation.CenterScreen;
-                window.Position = new Point(60, 40);
+                window.Position = new PixelPoint(60, 40);
 
 
                 window.Show();
                 window.Show();
 
 
-                var expectedPosition = new Point(
-                    screen1.Object.WorkingArea.Size.Width / 2 - window.ClientSize.Width / 2,
-                    screen1.Object.WorkingArea.Size.Height / 2 - window.ClientSize.Height / 2);
+                var expectedPosition = new PixelPoint(
+                    (int)(screen1.Object.WorkingArea.Size.Width / 2 - window.ClientSize.Width / 2),
+                    (int)(screen1.Object.WorkingArea.Size.Height / 2 - window.ClientSize.Height / 2));
 
 
                 Assert.Equal(window.Position, expectedPosition);
                 Assert.Equal(window.Position, expectedPosition);
             }
             }
@@ -330,7 +330,7 @@ namespace Avalonia.Controls.UnitTests
             using (UnitTestApplication.Start(parentWindowServices))
             using (UnitTestApplication.Start(parentWindowServices))
             {
             {
                 var parentWindow = new Window();
                 var parentWindow = new Window();
-                parentWindow.Position = new Point(60, 40);
+                parentWindow.Position = new PixelPoint(60, 40);
 
 
                 parentWindow.Show();
                 parentWindow.Show();
 
 
@@ -338,14 +338,14 @@ namespace Avalonia.Controls.UnitTests
                 {
                 {
                     var window = new Window();
                     var window = new Window();
                     window.WindowStartupLocation = WindowStartupLocation.CenterOwner;
                     window.WindowStartupLocation = WindowStartupLocation.CenterOwner;
-                    window.Position = new Point(60, 40);
+                    window.Position = new PixelPoint(60, 40);
                     window.Owner = parentWindow;
                     window.Owner = parentWindow;
 
 
                     window.Show();
                     window.Show();
 
 
-                    var expectedPosition = new Point(
-                        parentWindow.Position.X + parentWindow.ClientSize.Width / 2 - window.ClientSize.Width / 2,
-                        parentWindow.Position.Y + parentWindow.ClientSize.Height / 2 - window.ClientSize.Height / 2);
+                    var expectedPosition = new PixelPoint(
+                        (int)(parentWindow.Position.X + parentWindow.ClientSize.Width / 2 - window.ClientSize.Width / 2),
+                        (int)(parentWindow.Position.Y + parentWindow.ClientSize.Height / 2 - window.ClientSize.Height / 2));
 
 
                     Assert.Equal(window.Position, expectedPosition);
                     Assert.Equal(window.Position, expectedPosition);
                 }
                 }

+ 2 - 2
tests/Avalonia.UnitTests/TestRoot.cs

@@ -90,9 +90,9 @@ namespace Avalonia.UnitTests
         {
         {
         }
         }
 
 
-        public Point PointToClient(Point p) => p;
+        public Point PointToClient(PixelPoint p) => p.ToPoint(1);
 
 
-        public Point PointToScreen(Point p) => p;
+        public PixelPoint PointToScreen(Point p) => PixelPoint.FromPoint(p, 1);
 
 
         void INameScope.Register(string name, object element)
         void INameScope.Register(string name, object element)
         {
         {

+ 2 - 2
tests/Avalonia.UnitTests/TestTemplatedRoot.cs

@@ -57,9 +57,9 @@ namespace Avalonia.UnitTests
             throw new NotImplementedException();
             throw new NotImplementedException();
         }
         }
 
 
-        public Point PointToClient(Point p) => p;
+        public Point PointToClient(PixelPoint p) => p.ToPoint(1);
 
 
-        public Point PointToScreen(Point p) => p;
+        public PixelPoint PointToScreen(Point p) => PixelPoint.FromPoint(p, 1);
 
 
         void INameScope.Register(string name, object element)
         void INameScope.Register(string name, object element)
         {
         {