From c1dd44ead4cf42d13bb36b4ef74b1caa52a4c35e Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Sun, 21 Oct 2018 23:48:43 +0300 Subject: [PATCH 01/20] unit test for issue #2000 button with render transform don't trigger click --- .../ButtonTests.cs | 165 +++++++++++++++++- 1 file changed, 163 insertions(+), 2 deletions(-) diff --git a/tests/Avalonia.Controls.UnitTests/ButtonTests.cs b/tests/Avalonia.Controls.UnitTests/ButtonTests.cs index d218960726..9c9d09d4f8 100644 --- a/tests/Avalonia.Controls.UnitTests/ButtonTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ButtonTests.cs @@ -1,7 +1,10 @@ using System; using System.Windows.Input; using Avalonia.Data; -using Avalonia.Markup.Data; +using Avalonia.Input; +using Avalonia.Media; +using Avalonia.VisualTree; +using Moq; using Xunit; namespace Avalonia.Controls.UnitTests @@ -92,6 +95,164 @@ namespace Avalonia.Controls.UnitTests Assert.False(target.IsEnabled); } + [Fact] + public void Button_Is_Raising_Click() + { + var mouse = Mock.Of(); + IInputElement captured = null; + Mock.Get(mouse).Setup(m => m.GetPosition(It.IsAny())).Returns(new Point(50, 50)); + Mock.Get(mouse).Setup(m => m.Capture(It.IsAny())).Callback(v => captured = v); + Mock.Get(mouse).Setup(m => m.Captured).Returns(() => captured); + + var target = new TestButton() { Bounds = new Rect(0, 0, 100, 100) }; + + bool clicked = false; + + target.Click += (s, e) => clicked = true; + + RaisePointerEnter(target, mouse); + RaisePointerMove(target, mouse); + RaisePointerPressed(target, mouse, 1, MouseButton.Left); + + Assert.Equal(captured, target); + + RaisePointerReleased(target, mouse, MouseButton.Left); + + Assert.Equal(captured, null); + + Assert.True(clicked); + } + + [Fact] + public void Button_Is_Not_Raising_Click_When_PointerReleased_Outside() + { + var mouse = Mock.Of(); + IInputElement captured = null; + Mock.Get(mouse).Setup(m => m.GetPosition(It.IsAny())).Returns(new Point(200, 50)); + Mock.Get(mouse).Setup(m => m.Capture(It.IsAny())).Callback(v => captured = v); + Mock.Get(mouse).Setup(m => m.Captured).Returns(() => captured); + + var target = new TestButton() { Bounds = new Rect(0, 0, 100, 100) }; + + bool clicked = false; + + target.Click += (s, e) => clicked = true; + + RaisePointerEnter(target, mouse); + RaisePointerMove(target, mouse); + RaisePointerPressed(target, mouse, 1, MouseButton.Left); + RaisePointerLeave(target, mouse); + + Assert.Equal(captured, target); + + RaisePointerReleased(target, mouse, MouseButton.Left); + + Assert.Equal(captured, null); + + Assert.False(clicked); + } + + [Fact] + public void Button_With_RenderTransform_Is_Raising_Click() + { + var mouse = Mock.Of(); + IInputElement captured = null; + Mock.Get(mouse).Setup(m => m.GetPosition(It.IsAny())).Returns(new Point(150, 50)); + Mock.Get(mouse).Setup(m => m.Capture(It.IsAny())).Callback(v => captured = v); + Mock.Get(mouse).Setup(m => m.Captured).Returns(() => captured); + + var target = new TestButton() + { + Bounds = new Rect(0, 0, 100, 100), + RenderTransform = new TranslateTransform { X = 100, Y = 0 } + }; + + //actual bounds of button should be 100,0,100,100 x -> translated 100 pixels + //so mouse with x=150 coordinates should trigger click + //button shouldn't count on bounds to calculate pointer is in the over or not, but + //on avalonia event system, as renderer hit test will properly calculate whether to send + //mouse over events to button based on rendered bounds + //note: button also may have not rectangular shape and only renderer hit testing is reliable + + bool clicked = false; + + target.Click += (s, e) => clicked = true; + + RaisePointerEnter(target, mouse); + RaisePointerMove(target, mouse); + RaisePointerPressed(target, mouse, 1, MouseButton.Left); + + Assert.Equal(captured, target); + + RaisePointerReleased(target, mouse, MouseButton.Left); + + Assert.Equal(captured, null); + + Assert.True(clicked); + } + + private class TestButton : Button + { + public new Rect Bounds + { + get => base.Bounds; + set => base.Bounds = value; + } + } + + private void RaisePointerPressed(Button button, IMouseDevice device, int clickCount, MouseButton mouseButton) + { + button.RaiseEvent(new PointerPressedEventArgs + { + RoutedEvent = InputElement.PointerPressedEvent, + Source = button, + MouseButton = mouseButton, + ClickCount = clickCount, + Device = device, + }); + } + + private void RaisePointerReleased(Button button, IMouseDevice device, MouseButton mouseButton) + { + button.RaiseEvent(new PointerReleasedEventArgs + { + RoutedEvent = InputElement.PointerReleasedEvent, + Source = button, + MouseButton = mouseButton, + Device = device, + }); + } + + private void RaisePointerEnter(Button button, IMouseDevice device) + { + button.RaiseEvent(new PointerEventArgs + { + RoutedEvent = InputElement.PointerEnterEvent, + Source = button, + Device = device, + }); + } + + private void RaisePointerLeave(Button button, IMouseDevice device) + { + button.RaiseEvent(new PointerEventArgs + { + RoutedEvent = InputElement.PointerLeaveEvent, + Source = button, + Device = device, + }); + } + + private void RaisePointerMove(Button button, IMouseDevice device) + { + button.RaiseEvent(new PointerEventArgs + { + RoutedEvent = InputElement.PointerMovedEvent, + Source = button, + Device = device, + }); + } + private class TestCommand : ICommand { private bool _enabled; @@ -123,4 +284,4 @@ namespace Avalonia.Controls.UnitTests } } } -} \ No newline at end of file +} From e1874b4a4c442f0cda18ffe9e02d1304d783913b Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Tue, 23 Oct 2018 01:13:04 +0300 Subject: [PATCH 02/20] fix for issue #2000 button with rendertransform don't fire click --- src/Avalonia.Controls/Button.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Button.cs b/src/Avalonia.Controls/Button.cs index 24b2af7996..9c4c33f549 100644 --- a/src/Avalonia.Controls/Button.cs +++ b/src/Avalonia.Controls/Button.cs @@ -251,7 +251,9 @@ namespace Avalonia.Controls IsPressed = false; e.Handled = true; - if (ClickMode == ClickMode.Release && new Rect(Bounds.Size).Contains(e.GetPosition(this))) + //only renderer (hittesting) know better whether pointer is over the bounds of the button + if (ClickMode == ClickMode.Release && + (IsPointerOver || new Rect(Bounds.Size).Contains(e.GetPosition(this)))) { OnClick(); } From a600fb52ae17e05ae97f311396d0ad715fb08662 Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Sun, 21 Oct 2018 18:42:35 +0300 Subject: [PATCH 03/20] unit test for invalidate on immediate renderer when renderedtransfom is used, issue #1998 --- .../Rendering/ImmediateRendererTests.cs | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 tests/Avalonia.Visuals.UnitTests/Rendering/ImmediateRendererTests.cs diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/ImmediateRendererTests.cs b/tests/Avalonia.Visuals.UnitTests/Rendering/ImmediateRendererTests.cs new file mode 100644 index 0000000000..1fff36f8ff --- /dev/null +++ b/tests/Avalonia.Visuals.UnitTests/Rendering/ImmediateRendererTests.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Avalonia.Media; +using Avalonia.Rendering; +using Avalonia.VisualTree; +using Moq; +using Xunit; + +namespace Avalonia.Visuals.UnitTests.Rendering +{ + public class ImmediateRendererTests + { + [Fact] + public void AddDirty_Call_RenderRoot_Invalidate() + { + var visual = new Mock(); + var child = new Mock(); + var renderRoot = visual.As(); + + visual.As().Setup(v => v.Bounds).Returns(new Rect(0, 0, 400, 400)); + + child.As().Setup(v => v.Bounds).Returns(new Rect(10, 10, 100, 100)); + child.As().Setup(v => v.VisualParent).Returns(visual.Object); + child.As().Setup(v => v.RenderTransform).Returns(default(Transform)); + child.As().Setup(v => v.RenderTransformOrigin).Returns(new RelativePoint(0.5, 0.5, RelativeUnit.Relative)); + child.As().Setup(v => v.TransformToVisual(It.IsAny())).CallBase(); + + var target = new ImmediateRenderer(visual.Object); + + target.AddDirty(child.Object); + + renderRoot.Verify(v => v.Invalidate(new Rect(10, 10, 100, 100))); + } + + + [Fact] + public void AddDirty_With_RenderTransform_Call_RenderRoot_Invalidate() + { + var visual = new Mock(); + var child = new Mock(); + var renderRoot = visual.As(); + + visual.As().Setup(v => v.Bounds).Returns(new Rect(0, 0, 400, 400)); + + child.As().Setup(v => v.Bounds).Returns(new Rect(100, 100, 100, 100)); + child.As().Setup(v => v.VisualParent).Returns(visual.Object); + child.As().Setup(v => v.RenderTransform).Returns(new ScaleTransform() { ScaleX = 2, ScaleY = 2 }); + child.As().Setup(v => v.RenderTransformOrigin).Returns(new RelativePoint(0.5, 0.5, RelativeUnit.Relative)); + child.As().Setup(v => v.TransformToVisual(It.IsAny())).CallBase(); + + var target = new ImmediateRenderer(visual.Object); + + target.AddDirty(child.Object); + + renderRoot.Verify(v => v.Invalidate(new Rect(50, 50, 200, 200))); + } + + + public class TestVisual : Visual + { + public new Rect Bounds + { + get => base.Bounds; + set => base.Bounds = value; + } + } + } +} From 7fd6975658b6fe82deb7f461174480d4b6642753 Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Tue, 23 Oct 2018 01:19:16 +0300 Subject: [PATCH 04/20] fix ImmediateRenderer invalidation for controls with RenderTransform issue #1998 --- .../Rendering/ImmediateRenderer.cs | 37 ++++++++++++++++++- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs b/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs index d373e7ef2a..96512b41a1 100644 --- a/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs +++ b/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs @@ -115,12 +115,45 @@ namespace Avalonia.Rendering } } + private static Matrix? TransformToVisual(IVisual visual, IVisual root) + { + var result = Matrix.Identity; + + while (visual != root) + { + 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) + { + return null; + } + } + + return result; + } + /// public void AddDirty(IVisual visual) { if (visual.Bounds != Rect.Empty) { - var m = visual.TransformToVisual(_root); + var m = TransformToVisual(visual, _root); if (m.HasValue) { @@ -191,7 +224,7 @@ namespace Avalonia.Rendering } } - static IEnumerable HitTest( + private static IEnumerable HitTest( IVisual visual, Point p, Func filter) From 5ce71be598691be6b34caaee56076096f1bded52 Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Tue, 23 Oct 2018 14:00:30 +0300 Subject: [PATCH 05/20] another failing unit test for invalidate on immediate renderer when renderedtransfom is used, issue #1998 --- .../Rendering/ImmediateRendererTests.cs | 52 +++++++++++++++++-- 1 file changed, 48 insertions(+), 4 deletions(-) diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/ImmediateRendererTests.cs b/tests/Avalonia.Visuals.UnitTests/Rendering/ImmediateRendererTests.cs index 1fff36f8ff..41e1691f28 100644 --- a/tests/Avalonia.Visuals.UnitTests/Rendering/ImmediateRendererTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/Rendering/ImmediateRendererTests.cs @@ -1,7 +1,7 @@ -using System; -using System.Collections.Generic; -using System.Text; +using System.Collections.Generic; +using Avalonia.Collections; using Avalonia.Media; +using Avalonia.Platform; using Avalonia.Rendering; using Avalonia.VisualTree; using Moq; @@ -33,7 +33,6 @@ namespace Avalonia.Visuals.UnitTests.Rendering renderRoot.Verify(v => v.Invalidate(new Rect(10, 10, 100, 100))); } - [Fact] public void AddDirty_With_RenderTransform_Call_RenderRoot_Invalidate() { @@ -56,6 +55,51 @@ namespace Avalonia.Visuals.UnitTests.Rendering renderRoot.Verify(v => v.Invalidate(new Rect(50, 50, 200, 200))); } + [Fact] + public void AddDirty_For_Child_Moved_Should_Invalidate_Previous_Bounds() + { + var visual = new Mock(); + var child = new Mock(); + var renderRoot = visual.As(); + var renderTarget = visual.As(); + + renderRoot.Setup(r => r.CreateRenderTarget()).Returns(renderTarget.Object); + renderTarget.Setup(r => r.CreateDrawingContext(It.IsAny())).Returns(Mock.Of()); + + visual.As().Setup(v => v.Bounds).Returns(new Rect(0, 0, 400, 400)); + visual.As().Setup(v => v.VisualChildren).Returns(new AvaloniaList() { child.As().Object }); + + Rect childBounds = new Rect(0, 0, 100, 100); + child.As().Setup(v => v.Bounds).Returns(() => childBounds); + child.As().Setup(v => v.VisualParent).Returns(visual.Object); + child.As().Setup(v => v.TransformToVisual(It.IsAny())).CallBase(); + child.As().Setup(v => v.VisualChildren).Returns(new AvaloniaList()); + + var invalidationCalls = new List(); + + renderRoot.Setup(v => v.Invalidate(It.IsAny())).Callback(v => invalidationCalls.Add(v)); + + var target = new ImmediateRenderer(visual.Object); + + target.AddDirty(child.Object); + + Assert.Equal(new Rect(0, 0, 100, 100), invalidationCalls[0]); + + target.Paint(new Rect(0, 0, 100, 100)); + + //move child 100 pixels bottom/right + childBounds = new Rect(100, 100, 100, 100); + + //renderer should invalidate old child bounds with new one + //as on old area there can be artifacts + target.AddDirty(child.Object); + + //invalidate first old position + Assert.Equal(new Rect(0, 0, 100, 100), invalidationCalls[1]); + + //then new position + Assert.Equal(new Rect(100, 100, 100, 100), invalidationCalls[2]); + } public class TestVisual : Visual { From 03384d3b5efea383f435e276efea055d83627680 Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Tue, 23 Oct 2018 14:05:53 +0300 Subject: [PATCH 06/20] fix ImmediateRenderer invalidation for controls moved with RenderTransform issue #1998 --- src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs b/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs index 96512b41a1..e5e3533bf6 100644 --- a/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs +++ b/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs @@ -23,6 +23,7 @@ namespace Avalonia.Rendering private readonly IVisual _root; private readonly IRenderRoot _renderRoot; private IRenderTarget _renderTarget; + private Rect _lastPaintBounds; /// /// Initializes a new instance of the class. @@ -45,6 +46,8 @@ namespace Avalonia.Rendering /// public void Paint(Rect rect) { + _lastPaintBounds = rect; + if (_renderTarget == null) { _renderTarget = ((IRenderRoot)_root).CreateRenderTarget(); @@ -158,6 +161,13 @@ namespace Avalonia.Rendering if (m.HasValue) { var bounds = new Rect(visual.Bounds.Size).TransformToAABB(m.Value); + + if (_lastPaintBounds != default) + { + _renderRoot?.Invalidate(_lastPaintBounds); + _lastPaintBounds = default; + } + _renderRoot?.Invalidate(bounds); } } From 922917b29937ca0cc07e4a2d7d62c3bc5e0f8503 Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Tue, 23 Oct 2018 14:12:00 +0300 Subject: [PATCH 07/20] fix pr nits --- src/Avalonia.Controls/Button.cs | 1 - tests/Avalonia.Controls.UnitTests/ButtonTests.cs | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.Controls/Button.cs b/src/Avalonia.Controls/Button.cs index 9c4c33f549..0e8a765e57 100644 --- a/src/Avalonia.Controls/Button.cs +++ b/src/Avalonia.Controls/Button.cs @@ -251,7 +251,6 @@ namespace Avalonia.Controls IsPressed = false; e.Handled = true; - //only renderer (hittesting) know better whether pointer is over the bounds of the button if (ClickMode == ClickMode.Release && (IsPointerOver || new Rect(Bounds.Size).Contains(e.GetPosition(this)))) { diff --git a/tests/Avalonia.Controls.UnitTests/ButtonTests.cs b/tests/Avalonia.Controls.UnitTests/ButtonTests.cs index 9c9d09d4f8..afc53f2fa9 100644 --- a/tests/Avalonia.Controls.UnitTests/ButtonTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ButtonTests.cs @@ -96,7 +96,7 @@ namespace Avalonia.Controls.UnitTests } [Fact] - public void Button_Is_Raising_Click() + public void Button_Raises_Click() { var mouse = Mock.Of(); IInputElement captured = null; @@ -124,7 +124,7 @@ namespace Avalonia.Controls.UnitTests } [Fact] - public void Button_Is_Not_Raising_Click_When_PointerReleased_Outside() + public void Button_Does_Not_Raise_Click_When_PointerReleased_Outside() { var mouse = Mock.Of(); IInputElement captured = null; @@ -153,7 +153,7 @@ namespace Avalonia.Controls.UnitTests } [Fact] - public void Button_With_RenderTransform_Is_Raising_Click() + public void Button_With_RenderTransform_Raises_Click() { var mouse = Mock.Of(); IInputElement captured = null; From ef158e3ec36bdd32bec03a9aa36b8ca88b48f7be Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Fri, 26 Oct 2018 10:59:49 +0300 Subject: [PATCH 08/20] hittest when button is pressed --- src/Avalonia.Controls/Button.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.Controls/Button.cs b/src/Avalonia.Controls/Button.cs index 0e8a765e57..dc65df2584 100644 --- a/src/Avalonia.Controls/Button.cs +++ b/src/Avalonia.Controls/Button.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. See licence.md file in the project root for full license information. using System; +using System.Linq; using System.Windows.Input; using Avalonia.Data; using Avalonia.Input; @@ -251,8 +252,9 @@ namespace Avalonia.Controls IsPressed = false; e.Handled = true; - if (ClickMode == ClickMode.Release && - (IsPointerOver || new Rect(Bounds.Size).Contains(e.GetPosition(this)))) + var hittest = VisualRoot?.Renderer?.HitTest(e.GetPosition(VisualRoot), VisualRoot, null); + + if (ClickMode == ClickMode.Release && hittest?.Any(v => v == this) == true) { OnClick(); } @@ -262,9 +264,9 @@ namespace Avalonia.Controls protected override void UpdateDataValidation(AvaloniaProperty property, BindingNotification status) { base.UpdateDataValidation(property, status); - if(property == CommandProperty) + if (property == CommandProperty) { - if(status?.ErrorType == BindingErrorType.Error) + if (status?.ErrorType == BindingErrorType.Error) { IsEnabled = false; } From 87437b82327b63bfffa58c6a860747d07c045313 Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Fri, 26 Oct 2018 11:29:00 +0300 Subject: [PATCH 09/20] update button unit tests --- .../ButtonTests.cs | 46 +++++++++++++++++-- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/tests/Avalonia.Controls.UnitTests/ButtonTests.cs b/tests/Avalonia.Controls.UnitTests/ButtonTests.cs index afc53f2fa9..c318229700 100644 --- a/tests/Avalonia.Controls.UnitTests/ButtonTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ButtonTests.cs @@ -3,6 +3,8 @@ using System.Windows.Input; using Avalonia.Data; using Avalonia.Input; using Avalonia.Media; +using Avalonia.Platform; +using Avalonia.Rendering; using Avalonia.VisualTree; using Moq; using Xunit; @@ -99,12 +101,20 @@ namespace Avalonia.Controls.UnitTests public void Button_Raises_Click() { var mouse = Mock.Of(); + var renderer = Mock.Of(); IInputElement captured = null; Mock.Get(mouse).Setup(m => m.GetPosition(It.IsAny())).Returns(new Point(50, 50)); Mock.Get(mouse).Setup(m => m.Capture(It.IsAny())).Callback(v => captured = v); Mock.Get(mouse).Setup(m => m.Captured).Returns(() => captured); + Mock.Get(renderer).Setup(r => r.HitTest(It.IsAny(), It.IsAny(), null)) + .Returns>((p, r, f) => + r.Bounds.Contains(p) ? new IVisual[] { r } : new IVisual[0]); - var target = new TestButton() { Bounds = new Rect(0, 0, 100, 100) }; + var target = new TestButton() + { + Bounds = new Rect(0, 0, 100, 100), + Renderer = renderer + }; bool clicked = false; @@ -127,12 +137,20 @@ namespace Avalonia.Controls.UnitTests public void Button_Does_Not_Raise_Click_When_PointerReleased_Outside() { var mouse = Mock.Of(); + var renderer = Mock.Of(); IInputElement captured = null; Mock.Get(mouse).Setup(m => m.GetPosition(It.IsAny())).Returns(new Point(200, 50)); Mock.Get(mouse).Setup(m => m.Capture(It.IsAny())).Callback(v => captured = v); Mock.Get(mouse).Setup(m => m.Captured).Returns(() => captured); + Mock.Get(renderer).Setup(r => r.HitTest(It.IsAny(), It.IsAny(), null)) + .Returns>((p, r, f) => + r.Bounds.Contains(p) ? new IVisual[] { r } : new IVisual[0]); - var target = new TestButton() { Bounds = new Rect(0, 0, 100, 100) }; + var target = new TestButton() + { + Bounds = new Rect(0, 0, 100, 100), + Renderer = renderer + }; bool clicked = false; @@ -156,15 +174,21 @@ namespace Avalonia.Controls.UnitTests public void Button_With_RenderTransform_Raises_Click() { var mouse = Mock.Of(); + var renderer = Mock.Of(); IInputElement captured = null; Mock.Get(mouse).Setup(m => m.GetPosition(It.IsAny())).Returns(new Point(150, 50)); Mock.Get(mouse).Setup(m => m.Capture(It.IsAny())).Callback(v => captured = v); Mock.Get(mouse).Setup(m => m.Captured).Returns(() => captured); + Mock.Get(renderer).Setup(r => r.HitTest(It.IsAny(), It.IsAny(), null)) + .Returns>((p, r, f) => + r.Bounds.Contains(p.Transform(r.RenderTransform.Value.Invert())) ? + new IVisual[] { r } : new IVisual[0]); var target = new TestButton() { Bounds = new Rect(0, 0, 100, 100), - RenderTransform = new TranslateTransform { X = 100, Y = 0 } + RenderTransform = new TranslateTransform { X = 100, Y = 0 }, + Renderer = renderer }; //actual bounds of button should be 100,0,100,100 x -> translated 100 pixels @@ -191,13 +215,27 @@ namespace Avalonia.Controls.UnitTests Assert.True(clicked); } - private class TestButton : Button + private class TestButton : Button, IRenderRoot { public new Rect Bounds { get => base.Bounds; set => base.Bounds = value; } + + public Size ClientSize => throw new NotImplementedException(); + + public IRenderer Renderer { get; set; } + + public double RenderScaling => throw new NotImplementedException(); + + 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(); } private void RaisePointerPressed(Button button, IMouseDevice device, int clickCount, MouseButton mouseButton) From 08d552de108c613d3f0bd745ff48ba80aa89a1aa Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Fri, 26 Oct 2018 16:11:38 +0300 Subject: [PATCH 10/20] fix button click for deferedrenderer --- src/Avalonia.Controls/Button.cs | 6 ++++-- tests/Avalonia.Controls.UnitTests/ButtonTests.cs | 11 ++++++++--- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/Avalonia.Controls/Button.cs b/src/Avalonia.Controls/Button.cs index dc65df2584..2d80af8e4a 100644 --- a/src/Avalonia.Controls/Button.cs +++ b/src/Avalonia.Controls/Button.cs @@ -7,6 +7,7 @@ using System.Windows.Input; using Avalonia.Data; using Avalonia.Input; using Avalonia.Interactivity; +using Avalonia.VisualTree; namespace Avalonia.Controls { @@ -252,9 +253,10 @@ namespace Avalonia.Controls IsPressed = false; e.Handled = true; - var hittest = VisualRoot?.Renderer?.HitTest(e.GetPosition(VisualRoot), VisualRoot, null); + var hittest = this.GetVisualsAt(e.GetPosition(this)); - if (ClickMode == ClickMode.Release && hittest?.Any(v => v == this) == true) + if (ClickMode == ClickMode.Release && + hittest.Any(c => c == this || (c as IStyledElement)?.TemplatedParent == this) == true) { OnClick(); } diff --git a/tests/Avalonia.Controls.UnitTests/ButtonTests.cs b/tests/Avalonia.Controls.UnitTests/ButtonTests.cs index c318229700..76f2898700 100644 --- a/tests/Avalonia.Controls.UnitTests/ButtonTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ButtonTests.cs @@ -106,7 +106,7 @@ namespace Avalonia.Controls.UnitTests Mock.Get(mouse).Setup(m => m.GetPosition(It.IsAny())).Returns(new Point(50, 50)); Mock.Get(mouse).Setup(m => m.Capture(It.IsAny())).Callback(v => captured = v); Mock.Get(mouse).Setup(m => m.Captured).Returns(() => captured); - Mock.Get(renderer).Setup(r => r.HitTest(It.IsAny(), It.IsAny(), null)) + Mock.Get(renderer).Setup(r => r.HitTest(It.IsAny(), It.IsAny(), It.IsAny>())) .Returns>((p, r, f) => r.Bounds.Contains(p) ? new IVisual[] { r } : new IVisual[0]); @@ -142,7 +142,7 @@ namespace Avalonia.Controls.UnitTests Mock.Get(mouse).Setup(m => m.GetPosition(It.IsAny())).Returns(new Point(200, 50)); Mock.Get(mouse).Setup(m => m.Capture(It.IsAny())).Callback(v => captured = v); Mock.Get(mouse).Setup(m => m.Captured).Returns(() => captured); - Mock.Get(renderer).Setup(r => r.HitTest(It.IsAny(), It.IsAny(), null)) + Mock.Get(renderer).Setup(r => r.HitTest(It.IsAny(), It.IsAny(), It.IsAny>())) .Returns>((p, r, f) => r.Bounds.Contains(p) ? new IVisual[] { r } : new IVisual[0]); @@ -179,7 +179,7 @@ namespace Avalonia.Controls.UnitTests Mock.Get(mouse).Setup(m => m.GetPosition(It.IsAny())).Returns(new Point(150, 50)); Mock.Get(mouse).Setup(m => m.Capture(It.IsAny())).Callback(v => captured = v); Mock.Get(mouse).Setup(m => m.Captured).Returns(() => captured); - Mock.Get(renderer).Setup(r => r.HitTest(It.IsAny(), It.IsAny(), null)) + Mock.Get(renderer).Setup(r => r.HitTest(It.IsAny(), It.IsAny(), It.IsAny>())) .Returns>((p, r, f) => r.Bounds.Contains(p.Transform(r.RenderTransform.Value.Invert())) ? new IVisual[] { r } : new IVisual[0]); @@ -217,6 +217,11 @@ namespace Avalonia.Controls.UnitTests private class TestButton : Button, IRenderRoot { + public TestButton() + { + IsVisible = true; + } + public new Rect Bounds { get => base.Bounds; From 8461bfc6887cbfba8e13064d2f530b7d796b100b Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Mon, 29 Oct 2018 17:21:16 +0200 Subject: [PATCH 11/20] add tests for transform to visual --- .../Avalonia.Visuals.UnitTests/VisualTests.cs | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/tests/Avalonia.Visuals.UnitTests/VisualTests.cs b/tests/Avalonia.Visuals.UnitTests/VisualTests.cs index b62bf5858d..0414ac4c74 100644 --- a/tests/Avalonia.Visuals.UnitTests/VisualTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/VisualTests.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; using Avalonia.Controls; +using Avalonia.Media; using Avalonia.Rendering; using Avalonia.UnitTests; using Avalonia.VisualTree; @@ -192,5 +193,48 @@ namespace Avalonia.Visuals.UnitTests Assert.Throws(() => root2.Child = child); Assert.Empty(root2.GetVisualChildren()); } + + [Fact] + public void TransformToVisual_Should_Work() + { + var child = new Decorator { Width = 100, Height = 100 }; + var root = new TestRoot() { Child = child, Width = 400, Height = 400 }; + + root.Measure(Size.Infinity); + root.Arrange(new Rect(new Point(), root.DesiredSize)); + + var tr = child.TransformToVisual(root); + + Assert.NotNull(tr); + + var point = root.Bounds.TopLeft * tr; + + //child is centered (400 - 100)/2 + Assert.Equal(new Point(150, 150), point); + } + + [Fact] + public void TransformToVisual_With_RenderTransform_Should_Work() + { + var child = new Decorator + { + Width = 100, + Height = 100, + RenderTransform = new ScaleTransform() { ScaleX = 2, ScaleY = 2 } + }; + var root = new TestRoot() { Child = child, Width = 400, Height = 400 }; + + root.Measure(Size.Infinity); + root.Arrange(new Rect(new Point(), root.DesiredSize)); + + var tr = child.TransformToVisual(root); + + Assert.NotNull(tr); + + var point = root.Bounds.TopLeft * tr; + + //child is centered (400 - 100*2 scale)/2 + Assert.Equal(new Point(100, 100), point); + } } } From 85c786e4e993488e47e58a2f37feb2604cf80406 Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Mon, 29 Oct 2018 17:23:08 +0200 Subject: [PATCH 12/20] Fir TransformToVisual and use it in ImmediateRenderer --- .../Rendering/ImmediateRenderer.cs | 35 +------------------ src/Avalonia.Visuals/Visual.cs | 23 +++++++++--- 2 files changed, 20 insertions(+), 38 deletions(-) diff --git a/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs b/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs index e5e3533bf6..967dd24acf 100644 --- a/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs +++ b/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs @@ -118,45 +118,12 @@ namespace Avalonia.Rendering } } - private static Matrix? TransformToVisual(IVisual visual, IVisual root) - { - var result = Matrix.Identity; - - while (visual != root) - { - 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) - { - return null; - } - } - - return result; - } - /// public void AddDirty(IVisual visual) { if (visual.Bounds != Rect.Empty) { - var m = TransformToVisual(visual, _root); + var m = visual.TransformToVisual(_root); if (m.HasValue) { diff --git a/src/Avalonia.Visuals/Visual.cs b/src/Avalonia.Visuals/Visual.cs index e5fcf1ba1d..f26c21d1b6 100644 --- a/src/Avalonia.Visuals/Visual.cs +++ b/src/Avalonia.Visuals/Visual.cs @@ -304,7 +304,7 @@ namespace Avalonia { var thisOffset = GetOffsetFrom(common, this); var thatOffset = GetOffsetFrom(common, visual); - return Matrix.CreateTranslation(-thatOffset) * Matrix.CreateTranslation(thisOffset); + return -thatOffset * thisOffset; } return null; @@ -454,13 +454,28 @@ namespace Avalonia /// The ancestor visual. /// The visual. /// The visual offset. - private static Vector GetOffsetFrom(IVisual ancestor, IVisual visual) + private static Matrix GetOffsetFrom(IVisual ancestor, IVisual visual) { - var result = new Vector(); + var result = Matrix.Identity; while (visual != ancestor) { - result = new Vector(result.X + visual.Bounds.X, result.Y + visual.Bounds.Y); + 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) From 4455e698d757028cc579b2bb905e24fa9e01cf14 Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Tue, 30 Oct 2018 23:54:09 +0200 Subject: [PATCH 13/20] add easy way to test rendertransform in control catalog --- samples/ControlCatalog/Pages/LayoutTransformControlPage.xaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/ControlCatalog/Pages/LayoutTransformControlPage.xaml b/samples/ControlCatalog/Pages/LayoutTransformControlPage.xaml index b428cd1b9f..f7e1c08cac 100644 --- a/samples/ControlCatalog/Pages/LayoutTransformControlPage.xaml +++ b/samples/ControlCatalog/Pages/LayoutTransformControlPage.xaml @@ -19,7 +19,7 @@ - Layout Transform + From 2e430260335f54b9e68646a9c023ec10483950f2 Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Wed, 31 Oct 2018 00:36:41 +0200 Subject: [PATCH 14/20] cleanup ImmediateRenderer tests --- .../Rendering/ImmediateRendererTests.cs | 25 ++++--------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/ImmediateRendererTests.cs b/tests/Avalonia.Visuals.UnitTests/Rendering/ImmediateRendererTests.cs index 41e1691f28..82294246b1 100644 --- a/tests/Avalonia.Visuals.UnitTests/Rendering/ImmediateRendererTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/Rendering/ImmediateRendererTests.cs @@ -15,16 +15,13 @@ namespace Avalonia.Visuals.UnitTests.Rendering public void AddDirty_Call_RenderRoot_Invalidate() { var visual = new Mock(); - var child = new Mock(); + var child = new Mock() { CallBase = true }; var renderRoot = visual.As(); visual.As().Setup(v => v.Bounds).Returns(new Rect(0, 0, 400, 400)); child.As().Setup(v => v.Bounds).Returns(new Rect(10, 10, 100, 100)); child.As().Setup(v => v.VisualParent).Returns(visual.Object); - child.As().Setup(v => v.RenderTransform).Returns(default(Transform)); - child.As().Setup(v => v.RenderTransformOrigin).Returns(new RelativePoint(0.5, 0.5, RelativeUnit.Relative)); - child.As().Setup(v => v.TransformToVisual(It.IsAny())).CallBase(); var target = new ImmediateRenderer(visual.Object); @@ -37,16 +34,14 @@ namespace Avalonia.Visuals.UnitTests.Rendering public void AddDirty_With_RenderTransform_Call_RenderRoot_Invalidate() { var visual = new Mock(); - var child = new Mock(); + var child = new Mock() { CallBase = true }; var renderRoot = visual.As(); visual.As().Setup(v => v.Bounds).Returns(new Rect(0, 0, 400, 400)); child.As().Setup(v => v.Bounds).Returns(new Rect(100, 100, 100, 100)); child.As().Setup(v => v.VisualParent).Returns(visual.Object); - child.As().Setup(v => v.RenderTransform).Returns(new ScaleTransform() { ScaleX = 2, ScaleY = 2 }); - child.As().Setup(v => v.RenderTransformOrigin).Returns(new RelativePoint(0.5, 0.5, RelativeUnit.Relative)); - child.As().Setup(v => v.TransformToVisual(It.IsAny())).CallBase(); + child.Object.RenderTransform = new ScaleTransform() { ScaleX = 2, ScaleY = 2 }; var target = new ImmediateRenderer(visual.Object); @@ -58,8 +53,8 @@ namespace Avalonia.Visuals.UnitTests.Rendering [Fact] public void AddDirty_For_Child_Moved_Should_Invalidate_Previous_Bounds() { - var visual = new Mock(); - var child = new Mock(); + var visual = new Mock() { CallBase = true }; + var child = new Mock() { CallBase = true }; var renderRoot = visual.As(); var renderTarget = visual.As(); @@ -72,7 +67,6 @@ namespace Avalonia.Visuals.UnitTests.Rendering Rect childBounds = new Rect(0, 0, 100, 100); child.As().Setup(v => v.Bounds).Returns(() => childBounds); child.As().Setup(v => v.VisualParent).Returns(visual.Object); - child.As().Setup(v => v.TransformToVisual(It.IsAny())).CallBase(); child.As().Setup(v => v.VisualChildren).Returns(new AvaloniaList()); var invalidationCalls = new List(); @@ -100,14 +94,5 @@ namespace Avalonia.Visuals.UnitTests.Rendering //then new position Assert.Equal(new Rect(100, 100, 100, 100), invalidationCalls[2]); } - - public class TestVisual : Visual - { - public new Rect Bounds - { - get => base.Bounds; - set => base.Bounds = value; - } - } } } From b06a5a2991d66c6ff4c7d5c09ec0746d519ed248 Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Wed, 31 Oct 2018 00:38:34 +0200 Subject: [PATCH 15/20] fix finally #1998 for immediaterenderer --- .../Rendering/ImmediateRenderer.cs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs b/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs index 967dd24acf..08f3803e9b 100644 --- a/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs +++ b/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs @@ -23,7 +23,6 @@ namespace Avalonia.Rendering private readonly IVisual _root; private readonly IRenderRoot _renderRoot; private IRenderTarget _renderTarget; - private Rect _lastPaintBounds; /// /// Initializes a new instance of the class. @@ -46,8 +45,6 @@ namespace Avalonia.Rendering /// public void Paint(Rect rect) { - _lastPaintBounds = rect; - if (_renderTarget == null) { _renderTarget = ((IRenderRoot)_root).CreateRenderTarget(); @@ -129,10 +126,17 @@ namespace Avalonia.Rendering { var bounds = new Rect(visual.Bounds.Size).TransformToAABB(m.Value); - if (_lastPaintBounds != default) + //use transformedbounds as previous render state of the visual bounds + //so we can invalidate old and new bounds of a control in case it moved/shrinked + if (visual.TransformedBounds.HasValue) { - _renderRoot?.Invalidate(_lastPaintBounds); - _lastPaintBounds = default; + var trb = visual.TransformedBounds.Value; + var trBounds = trb.Bounds.TransformToAABB(trb.Transform); + + if (trBounds != bounds) + { + _renderRoot?.Invalidate(trBounds); + } } _renderRoot?.Invalidate(bounds); From 0cbd7b5117dac184c31d18421017749f26cecfde Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Wed, 31 Oct 2018 09:57:34 +0200 Subject: [PATCH 16/20] fix devtools controls highlighting with immediaterenderer issue #1927 --- src/Avalonia.Controls/Primitives/AdornerLayer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Primitives/AdornerLayer.cs b/src/Avalonia.Controls/Primitives/AdornerLayer.cs index 4b58197ef3..5308a062ec 100644 --- a/src/Avalonia.Controls/Primitives/AdornerLayer.cs +++ b/src/Avalonia.Controls/Primitives/AdornerLayer.cs @@ -89,7 +89,7 @@ namespace Avalonia.Controls.Primitives control.Clip = clip; } - clip.Rect = bounds.Clip.TransformToAABB(-bounds.Transform); + clip.Rect = bounds.Bounds; } private void ChildrenCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) From 7136750d6b12074ac89fd7ec892bec83d330dc7a Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Wed, 31 Oct 2018 19:27:13 +0200 Subject: [PATCH 17/20] add failing unittests for derivedlist --- .../Collections/AvaloniaListExtenionsTests.cs | 29 +++++++++++++++---- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/tests/Avalonia.Base.UnitTests/Collections/AvaloniaListExtenionsTests.cs b/tests/Avalonia.Base.UnitTests/Collections/AvaloniaListExtenionsTests.cs index b996db8d48..7f118a2c1d 100644 --- a/tests/Avalonia.Base.UnitTests/Collections/AvaloniaListExtenionsTests.cs +++ b/tests/Avalonia.Base.UnitTests/Collections/AvaloniaListExtenionsTests.cs @@ -1,5 +1,4 @@ -using System; -using System.Linq; +using System.Linq; using Avalonia.Collections; using Xunit; @@ -82,13 +81,31 @@ namespace Avalonia.Base.UnitTests.Collections Assert.Equal(source, result); } - [Fact] - public void CreateDerivedList_Handles_MoveRange() + [Theory] + [InlineData(0, 2, 3)] + [InlineData(0, 2, 4)] + [InlineData(0, 2, 5)] + [InlineData(0, 4, 4)] + [InlineData(1, 2, 0)] + [InlineData(1, 2, 4)] + [InlineData(1, 2, 5)] + [InlineData(1, 4, 0)] + [InlineData(2, 2, 0)] + [InlineData(2, 2, 1)] + [InlineData(2, 2, 3)] + [InlineData(2, 2, 4)] + [InlineData(2, 2, 5)] + [InlineData(4, 2, 0)] + [InlineData(4, 2, 1)] + [InlineData(4, 2, 3)] + [InlineData(5, 1, 0)] + [InlineData(5, 1, 3)] + public void CreateDerivedList_Handles_MoveRange(int oldIndex, int count, int newIndex) { - var source = new AvaloniaList(new[] { 0, 1, 2, 3 }); + var source = new AvaloniaList(new[] { 0, 1, 2, 3, 4, 5 }); var target = source.CreateDerivedList(x => new Wrapper(x)); - source.MoveRange(1, 2, 0); + source.MoveRange(oldIndex, count, newIndex); var result = target.Select(x => x.Value).ToList(); From aa21fe9d17da5593ffbb81c3600a0dd8e36b1ca9 Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Wed, 31 Oct 2018 19:57:31 +0200 Subject: [PATCH 18/20] fix derived list/extensions for moverange --- src/Avalonia.Base/Collections/AvaloniaListExtensions.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Base/Collections/AvaloniaListExtensions.cs b/src/Avalonia.Base/Collections/AvaloniaListExtensions.cs index 1731950222..58f3413780 100644 --- a/src/Avalonia.Base/Collections/AvaloniaListExtensions.cs +++ b/src/Avalonia.Base/Collections/AvaloniaListExtensions.cs @@ -104,7 +104,12 @@ namespace Avalonia.Collections case NotifyCollectionChangedAction.Move: case NotifyCollectionChangedAction.Replace: Remove(e.OldStartingIndex, e.OldItems); - Add(e.NewStartingIndex, e.NewItems); + int newIndex = e.NewStartingIndex; + if(newIndex > e.OldStartingIndex) + { + newIndex -= e.OldItems.Count; + } + Add(newIndex, e.NewItems); break; case NotifyCollectionChangedAction.Remove: From 5e25f25ce24ea914fcd0efec88d5ea68f298eee9 Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Fri, 2 Nov 2018 10:36:21 +0200 Subject: [PATCH 19/20] fix local cake script build --- parameters.cake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parameters.cake b/parameters.cake index d13e503bd0..4ef7e8e05a 100644 --- a/parameters.cake +++ b/parameters.cake @@ -64,7 +64,7 @@ public class Parameters IsPullRequest = buildSystem.AppVeyor.Environment.PullRequest.IsPullRequest; IsMainRepo = StringComparer.OrdinalIgnoreCase.Equals(MainRepo, context.EnvironmentVariable("BUILD_REPOSITORY_URI")); IsMasterBranch = StringComparer.OrdinalIgnoreCase.Equals(MasterBranch, context.EnvironmentVariable("BUILD_SOURCEBRANCHNAME")); - IsReleaseBranch = context.EnvironmentVariable("BUILD_SOURCEBRANCH").ToLower().StartsWith(ReleaseBranchPrefix.ToLower()); + IsReleaseBranch = (context.EnvironmentVariable("BUILD_SOURCEBRANCH")??"").StartsWith(ReleaseBranchPrefix, StringComparison.OrdinalIgnoreCase); IsTagged = buildSystem.AppVeyor.Environment.Repository.Tag.IsTag && !string.IsNullOrWhiteSpace(buildSystem.AppVeyor.Environment.Repository.Tag.Name); IsReleasable = StringComparer.OrdinalIgnoreCase.Equals(ReleaseConfiguration, Configuration); From 81c274328b96ba319711ff15c537086aa97011ce Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Fri, 2 Nov 2018 11:04:29 +0200 Subject: [PATCH 20/20] fix tiny tiny nit --- src/Avalonia.Controls/Button.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Button.cs b/src/Avalonia.Controls/Button.cs index 2d80af8e4a..1f3fcbafb3 100644 --- a/src/Avalonia.Controls/Button.cs +++ b/src/Avalonia.Controls/Button.cs @@ -256,7 +256,7 @@ namespace Avalonia.Controls var hittest = this.GetVisualsAt(e.GetPosition(this)); if (ClickMode == ClickMode.Release && - hittest.Any(c => c == this || (c as IStyledElement)?.TemplatedParent == this) == true) + hittest.Any(c => c == this || (c as IStyledElement)?.TemplatedParent == this)) { OnClick(); }