Browse Source

Merge branch 'master' into fixes/project-status-wording

pull/2670/head
Jumar Macato 7 years ago
committed by GitHub
parent
commit
8a945a038d
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      src/Avalonia.Controls/Shapes/Ellipse.cs
  2. 2
      src/Avalonia.Controls/Shapes/Rectangle.cs
  3. 10
      src/Avalonia.Input/DragDropDevice.cs
  4. 5
      src/Avalonia.Input/DragEventArgs.cs
  5. 12
      src/Avalonia.Visuals/Rect.cs
  6. 24
      src/Avalonia.Visuals/Rendering/SceneGraph/RectangleNode.cs
  7. 2
      src/Avalonia.Visuals/Rendering/SceneGraph/Scene.cs
  8. 62
      src/Avalonia.Visuals/Visual.cs
  9. 92
      src/Avalonia.Visuals/VisualExtensions.cs
  10. 11
      src/Avalonia.Visuals/VisualTree/IVisual.cs
  11. 10
      src/Avalonia.Visuals/VisualTree/VisualExtensions.cs
  12. 6
      tests/Avalonia.RenderTests/Controls/CustomRenderTests.cs
  13. 38
      tests/Avalonia.Visuals.UnitTests/VisualExtensionsTests.cs

2
src/Avalonia.Controls/Shapes/Ellipse.cs

@ -14,7 +14,7 @@ namespace Avalonia.Controls.Shapes
protected override Geometry CreateDefiningGeometry()
{
var rect = new Rect(Bounds.Size).Deflate(StrokeThickness);
var rect = new Rect(Bounds.Size).Deflate(StrokeThickness / 2);
return new EllipseGeometry(rect);
}

2
src/Avalonia.Controls/Shapes/Rectangle.cs

@ -14,7 +14,7 @@ namespace Avalonia.Controls.Shapes
protected override Geometry CreateDefiningGeometry()
{
var rect = new Rect(Bounds.Size).Deflate(StrokeThickness);
var rect = new Rect(Bounds.Size).Deflate(StrokeThickness / 2);
return new RectangleGeometry(rect);
}

10
src/Avalonia.Input/DragDropDevice.cs

@ -23,7 +23,13 @@ namespace Avalonia.Input
{
if (target == null)
return DragDropEffects.None;
var args = new DragEventArgs(routedEvent, data, target, inputRoot.TranslatePoint(point, target), modifiers)
var p = inputRoot.TranslatePoint(point, target);
if (!p.HasValue)
return DragDropEffects.None;
var args = new DragEventArgs(routedEvent, data, target, p.Value, modifiers)
{
RoutedEvent = routedEvent,
DragEffects = operation
@ -108,4 +114,4 @@ namespace Avalonia.Input
}
}
}
}
}

5
src/Avalonia.Input/DragEventArgs.cs

@ -26,8 +26,9 @@ namespace Avalonia.Input
if (_target != null)
{
point = _target.TranslatePoint(_targetLocation, relativeTo);
point = _target.TranslatePoint(_targetLocation, relativeTo) ?? point;
}
return point;
}
@ -41,4 +42,4 @@ namespace Avalonia.Input
}
}
}
}

12
src/Avalonia.Visuals/Rect.cs

@ -256,7 +256,7 @@ namespace Avalonia
/// <summary>
/// Inflates the rectangle.
/// </summary>
/// <param name="thickness">The thickness.</param>
/// <param name="thickness">The thickness to be subtracted for each side of the rectangle.</param>
/// <returns>The inflated rectangle.</returns>
public Rect Inflate(double thickness)
{
@ -266,7 +266,7 @@ namespace Avalonia
/// <summary>
/// Inflates the rectangle.
/// </summary>
/// <param name="thickness">The thickness.</param>
/// <param name="thickness">The thickness to be subtracted for each side of the rectangle.</param>
/// <returns>The inflated rectangle.</returns>
public Rect Inflate(Thickness thickness)
{
@ -278,20 +278,18 @@ namespace Avalonia
/// <summary>
/// Deflates the rectangle.
/// </summary>
/// <param name="thickness">The thickness.</param>
/// <param name="thickness">The thickness to be subtracted for each side of the rectangle.</param>
/// <returns>The deflated rectangle.</returns>
/// <remarks>The deflated rectangle size cannot be less than 0.</remarks>
public Rect Deflate(double thickness)
{
return Deflate(new Thickness(thickness / 2));
return Deflate(new Thickness(thickness));
}
/// <summary>
/// Deflates the rectangle by a <see cref="Thickness"/>.
/// </summary>
/// <param name="thickness">The thickness.</param>
/// <param name="thickness">The thickness to be subtracted for each side of the rectangle.</param>
/// <returns>The deflated rectangle.</returns>
/// <remarks>The deflated rectangle size cannot be less than 0.</remarks>
public Rect Deflate(Thickness thickness)
{
return new Rect(

24
src/Avalonia.Visuals/Rendering/SceneGraph/RectangleNode.cs

@ -105,8 +105,28 @@ namespace Avalonia.Rendering.SceneGraph
}
}
// TODO: This doesn't respect CornerRadius yet.
/// <inheritdoc/>
public override bool HitTest(Point p) => Bounds.Contains(p);
public override bool HitTest(Point p)
{
// TODO: This doesn't respect CornerRadius yet.
if (Transform.HasInverse)
{
p *= Transform.Invert();
if (Brush != null)
{
var rect = Rect.Inflate((Pen?.Thickness / 2) ?? 0);
return rect.Contains(p);
}
else
{
var borderRect = Rect.Inflate((Pen?.Thickness / 2) ?? 0);
var emptyRect = Rect.Deflate((Pen?.Thickness / 2) ?? 0);
return borderRect.Contains(p) && !emptyRect.Contains(p);
}
}
return false;
}
}
}

2
src/Avalonia.Visuals/Rendering/SceneGraph/Scene.cs

@ -173,7 +173,7 @@ namespace Avalonia.Rendering.SceneGraph
if (node.GeometryClip != null)
{
var controlPoint = Root.Visual.TranslatePoint(p, node.Visual);
clipped = !node.GeometryClip.FillContains(controlPoint);
clipped = !node.GeometryClip.FillContains(controlPoint.Value);
}
if (!clipped)

62
src/Avalonia.Visuals/Visual.cs

@ -291,29 +291,6 @@ namespace Avalonia
Contract.Requires<ArgumentNullException>(context != null);
}
/// <summary>
/// Returns a transform that transforms the visual's coordinates into the coordinates
/// of the specified <paramref name="visual"/>.
/// </summary>
/// <param name="visual">The visual to translate the coordinates to.</param>
/// <returns>
/// A <see cref="Matrix"/> containing the transform or null if the visuals don't share a
/// common ancestor.
/// </returns>
public Matrix? TransformToVisual(IVisual visual)
{
var common = this.FindCommonVisualAncestor(visual);
if (common != null)
{
var thisOffset = GetOffsetFrom(common, this);
var thatOffset = GetOffsetFrom(common, visual);
return -thatOffset * thisOffset;
}
return null;
}
/// <summary>
/// Indicates that a property change should cause <see cref="InvalidateVisual"/> to be
/// called.
@ -480,45 +457,6 @@ namespace Avalonia
}
}
/// <summary>
/// Gets the visual offset from the specified ancestor.
/// </summary>
/// <param name="ancestor">The ancestor visual.</param>
/// <param name="visual">The visual.</param>
/// <returns>The visual offset.</returns>
private static Matrix GetOffsetFrom(IVisual ancestor, IVisual visual)
{
var result = Matrix.Identity;
while (visual != ancestor)
{
if (visual.RenderTransform?.Value != null)
{
var origin = visual.RenderTransformOrigin.ToPixels(visual.Bounds.Size);
var offset = Matrix.CreateTranslation(origin);
var renderTransform = (-offset) * visual.RenderTransform.Value * (offset);
result *= renderTransform;
}
var topLeft = visual.Bounds.TopLeft;
if (topLeft != default)
{
result *= Matrix.CreateTranslation(topLeft);
}
visual = visual.VisualParent;
if (visual == null)
{
throw new ArgumentException("'visual' is not a descendant of 'ancestor'.");
}
}
return result;
}
/// <summary>
/// Called when a visual's <see cref="RenderTransform"/> changes.
/// </summary>

92
src/Avalonia.Visuals/VisualExtensions.cs

@ -2,7 +2,6 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using Avalonia.Rendering;
using Avalonia.VisualTree;
namespace Avalonia
@ -20,10 +19,8 @@ namespace Avalonia
/// <returns>The point in client coordinates.</returns>
public static Point PointToClient(this IVisual visual, PixelPoint point)
{
var (root, offset) = GetRootAndPosition(visual);
var screenOffset = PixelPoint.FromPoint((Point)offset, root.RenderScaling);
var screenPoint = new PixelPoint(point.X - screenOffset.X, point.Y - screenOffset.Y);
return root.PointToClient(screenPoint);
var rootPoint = visual.VisualRoot.PointToClient(point);
return visual.VisualRoot.TranslatePoint(rootPoint, visual).Value;
}
/// <summary>
@ -34,48 +31,93 @@ namespace Avalonia
/// <returns>The point in screen coordinates.</returns>
public static PixelPoint PointToScreen(this IVisual visual, Point point)
{
var p = GetRootAndPosition(visual);
return p.Item1.PointToScreen(point + p.Item2);
var p = visual.TranslatePoint(point, visual.VisualRoot);
return visual.VisualRoot.PointToScreen(p.Value);
}
/// <summary>
/// Returns a transform that transforms the visual's coordinates into the coordinates
/// of the specified <paramref name="to"/>.
/// </summary>
/// <param name="from">The visual whose coordinates are to be transformed.</param>
/// <param name="to">The visual to translate the coordinates to.</param>
/// <returns>
/// A <see cref="Matrix"/> containing the transform or null if the visuals don't share a
/// common ancestor.
/// </returns>
public static Matrix? TransformToVisual(this IVisual from, IVisual to)
{
var common = from.FindCommonVisualAncestor(to);
if (common != null)
{
var thisOffset = GetOffsetFrom(common, from);
var thatOffset = GetOffsetFrom(common, to);
return -thatOffset * thisOffset;
}
return null;
}
/// <summary>
/// Translates a point relative to this visual to coordinates that are relative to the specified visual.
/// The visual and relativeTo should be descendants of the same root window
/// </summary>
/// <param name="visual">The visual.</param>
/// <param name="point">The point value, as relative to this visual.</param>
/// <param name="relativeTo">The visual to translate the given point into.</param>
/// <returns>A point value, now relative to the target visual rather than this source element.</returns>
public static Point TranslatePoint(this IVisual visual, Point point, IVisual relativeTo)
/// <returns>
/// A point value, now relative to the target visual rather than this source element, or null if the
/// two elements have no common ancestor.
/// </returns>
public static Point? TranslatePoint(this IVisual visual, Point point, IVisual relativeTo)
{
var pos = GetRootAndPosition(visual);
var relToPos = GetRootAndPosition(relativeTo);
var transform = visual.TransformToVisual(relativeTo);
return point - (relToPos.Item2 - pos.Item2);
if (transform.HasValue)
{
return point.Transform(transform.Value);
}
return null;
}
/// <summary>
/// Gets the root of the control's visual tree and the position of the control
/// in the root's coordinate space.
/// Gets a transform from an ancestor to a descendent.
/// </summary>
/// <param name="v">The visual.</param>
/// <returns>A tuple containing the root and the position of the control.</returns>
private static Tuple<IRenderRoot, Vector> GetRootAndPosition(IVisual v)
/// <param name="ancestor">The ancestor visual.</param>
/// <param name="visual">The visual.</param>
/// <returns>The transform.</returns>
private static Matrix GetOffsetFrom(IVisual ancestor, IVisual visual)
{
var result = new Vector();
var result = Matrix.Identity;
while (!(v is IRenderRoot))
while (visual != ancestor)
{
result = new Vector(result.X + v.Bounds.X, result.Y + v.Bounds.Y);
v = v.VisualParent;
if (visual.RenderTransform?.Value != null)
{
var origin = visual.RenderTransformOrigin.ToPixels(visual.Bounds.Size);
var offset = Matrix.CreateTranslation(origin);
var renderTransform = (-offset) * visual.RenderTransform.Value * (offset);
result *= renderTransform;
}
var topLeft = visual.Bounds.TopLeft;
if (topLeft != default)
{
result *= Matrix.CreateTranslation(topLeft);
}
visual = visual.VisualParent;
if (v == null)
if (visual == null)
{
throw new InvalidOperationException("Control is not attached to visual tree.");
throw new ArgumentException("'visual' is not a descendant of 'ancestor'.");
}
}
return Tuple.Create((IRenderRoot)v, result);
return result;
}
}
}

11
src/Avalonia.Visuals/VisualTree/IVisual.cs

@ -116,16 +116,5 @@ namespace Avalonia.VisualTree
/// </summary>
/// <param name="context">The context.</param>
void Render(DrawingContext context);
/// <summary>
/// Returns a transform that transforms the visual's coordinates into the coordinates
/// of the specified <paramref name="visual"/>.
/// </summary>
/// <param name="visual">The visual to translate the coordinates to.</param>
/// <returns>
/// A <see cref="Matrix"/> containing the transform or null if the visuals don't share a
/// common ancestor.
/// </returns>
Matrix? TransformToVisual(IVisual visual);
}
}

10
src/Avalonia.Visuals/VisualTree/VisualExtensions.cs

@ -132,8 +132,14 @@ namespace Avalonia.VisualTree
Contract.Requires<ArgumentNullException>(visual != null);
var root = visual.GetVisualRoot();
p = visual.TranslatePoint(p, root);
return root.Renderer.HitTest(p, visual, filter);
var rootPoint = visual.TranslatePoint(p, root);
if (rootPoint.HasValue)
{
return root.Renderer.HitTest(rootPoint.Value, visual, filter);
}
return Enumerable.Empty<IVisual>();
}
/// <summary>

6
tests/Avalonia.RenderTests/Controls/CustomRenderTests.cs

@ -36,7 +36,7 @@ namespace Avalonia.Direct2D1.RenderTests.Controls
new Rect(control.Bounds.Size),
4);
using (context.PushClip(new Rect(control.Bounds.Size).Deflate(20)))
using (context.PushClip(new Rect(control.Bounds.Size).Deflate(10)))
{
context.FillRectangle(
Brushes.Blue,
@ -100,7 +100,7 @@ namespace Avalonia.Direct2D1.RenderTests.Controls
{
context.FillRectangle(
Brushes.Blue,
new Rect(control.Bounds.Size).Deflate(20),
new Rect(control.Bounds.Size).Deflate(10),
4);
}
}),
@ -140,7 +140,7 @@ namespace Avalonia.Direct2D1.RenderTests.Controls
{
context.FillRectangle(
Brushes.Blue,
new Rect(control.Bounds.Size).Deflate(20),
new Rect(control.Bounds.Size).Deflate(10),
4);
}
}),

38
tests/Avalonia.Visuals.UnitTests/VisualExtensionsTests.cs

@ -0,0 +1,38 @@
using Avalonia.Controls;
using Avalonia.Layout;
using Avalonia.Media;
using Avalonia.UnitTests;
using Xunit;
namespace Avalonia.Visuals.UnitTests
{
public class VisualExtensionsTests
{
[Fact]
public void TranslatePoint_Should_Respect_RenderTransforms()
{
Border target;
var root = new TestRoot
{
Width = 100,
Height = 100,
Child = new Decorator
{
Width = 50,
Height = 50,
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
RenderTransform = new TranslateTransform(25, 25),
Child = target = new Border(),
}
};
root.Measure(Size.Infinity);
root.Arrange(new Rect(root.DesiredSize));
var result = target.TranslatePoint(new Point(0, 0), root);
Assert.Equal(new Point(50, 50), result);
}
}
}
Loading…
Cancel
Save