From e20930793187c95d2e5392484e0782a494e758fd Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 5 Jun 2020 13:54:09 +0200 Subject: [PATCH 01/12] Added nullable annotations. --- src/Avalonia.Layout/ILayoutManager.cs | 3 +++ src/Avalonia.Layout/ILayoutable.cs | 2 ++ src/Avalonia.Layout/LayoutManager.cs | 10 ++++++---- src/Avalonia.Layout/Layoutable.cs | 5 +++-- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/Avalonia.Layout/ILayoutManager.cs b/src/Avalonia.Layout/ILayoutManager.cs index c3675c18a2..16814f2e1f 100644 --- a/src/Avalonia.Layout/ILayoutManager.cs +++ b/src/Avalonia.Layout/ILayoutManager.cs @@ -1,3 +1,6 @@ +using System; + +#nullable enable namespace Avalonia.Layout { diff --git a/src/Avalonia.Layout/ILayoutable.cs b/src/Avalonia.Layout/ILayoutable.cs index 5c785613a9..316a017f1d 100644 --- a/src/Avalonia.Layout/ILayoutable.cs +++ b/src/Avalonia.Layout/ILayoutable.cs @@ -1,5 +1,7 @@ using Avalonia.VisualTree; +#nullable enable + namespace Avalonia.Layout { /// diff --git a/src/Avalonia.Layout/LayoutManager.cs b/src/Avalonia.Layout/LayoutManager.cs index e8cb937997..92506fee9a 100644 --- a/src/Avalonia.Layout/LayoutManager.cs +++ b/src/Avalonia.Layout/LayoutManager.cs @@ -3,6 +3,8 @@ using System.Diagnostics; using Avalonia.Logging; using Avalonia.Threading; +#nullable enable + namespace Avalonia.Layout { /// @@ -24,7 +26,7 @@ namespace Avalonia.Layout /// public void InvalidateMeasure(ILayoutable control) { - Contract.Requires(control != null); + control = control ?? throw new ArgumentNullException(nameof(control)); Dispatcher.UIThread.VerifyAccess(); if (!control.IsAttachedToVisualTree) @@ -45,7 +47,7 @@ namespace Avalonia.Layout /// public void InvalidateArrange(ILayoutable control) { - Contract.Requires(control != null); + control = control ?? throw new ArgumentNullException(nameof(control)); Dispatcher.UIThread.VerifyAccess(); if (!control.IsAttachedToVisualTree) @@ -73,7 +75,7 @@ namespace Avalonia.Layout { _running = true; - Stopwatch stopwatch = null; + Stopwatch? stopwatch = null; const LogEventLevel timingLogLevel = LogEventLevel.Information; bool captureTiming = Logger.IsEnabled(timingLogLevel); @@ -117,7 +119,7 @@ namespace Avalonia.Layout if (captureTiming) { - stopwatch.Stop(); + stopwatch!.Stop(); Logger.TryGet(timingLogLevel)?.Log(LogArea.Layout, this, "Layout pass finished in {Time}", stopwatch.Elapsed); } diff --git a/src/Avalonia.Layout/Layoutable.cs b/src/Avalonia.Layout/Layoutable.cs index ce5200f4a4..3b13647681 100644 --- a/src/Avalonia.Layout/Layoutable.cs +++ b/src/Avalonia.Layout/Layoutable.cs @@ -1,8 +1,9 @@ using System; using Avalonia.Logging; -using Avalonia.Utilities; using Avalonia.VisualTree; +#nullable enable + namespace Avalonia.Layout { /// @@ -153,7 +154,7 @@ namespace Avalonia.Layout /// /// Occurs when a layout pass completes for the control. /// - public event EventHandler LayoutUpdated; + public event EventHandler? LayoutUpdated; /// /// Gets or sets the width of the element. From d46265233dbd0ee77020e9ef6ed8d27f8f2e75b5 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 5 Jun 2020 14:29:20 +0200 Subject: [PATCH 02/12] Raise LayoutUpdated at the end of layout pass. This attempts to match the behavior of `LayoutUpdated` in WPF and UWP. --- build/Moq.props | 2 +- src/Avalonia.Layout/ILayoutManager.cs | 5 + src/Avalonia.Layout/LayoutManager.cs | 3 + src/Avalonia.Layout/Layoutable.cs | 54 +++++++- .../Avalonia.Controls.UnitTests/GridTests.cs | 18 ++- .../LayoutableTests.cs | 121 ++++++++++++++++++ 6 files changed, 192 insertions(+), 11 deletions(-) diff --git a/build/Moq.props b/build/Moq.props index 7de9b6b6ba..9e2fd1db5d 100644 --- a/build/Moq.props +++ b/build/Moq.props @@ -1,5 +1,5 @@  - + diff --git a/src/Avalonia.Layout/ILayoutManager.cs b/src/Avalonia.Layout/ILayoutManager.cs index 16814f2e1f..6e63d3edbb 100644 --- a/src/Avalonia.Layout/ILayoutManager.cs +++ b/src/Avalonia.Layout/ILayoutManager.cs @@ -9,6 +9,11 @@ namespace Avalonia.Layout /// public interface ILayoutManager { + /// + /// Raised when the layout manager completes a layout pass. + /// + event EventHandler LayoutUpdated; + /// /// Notifies the layout manager that a control requires a measure. /// diff --git a/src/Avalonia.Layout/LayoutManager.cs b/src/Avalonia.Layout/LayoutManager.cs index 92506fee9a..908758045a 100644 --- a/src/Avalonia.Layout/LayoutManager.cs +++ b/src/Avalonia.Layout/LayoutManager.cs @@ -23,6 +23,8 @@ namespace Avalonia.Layout _executeLayoutPass = ExecuteLayoutPass; } + public event EventHandler? LayoutUpdated; + /// public void InvalidateMeasure(ILayoutable control) { @@ -126,6 +128,7 @@ namespace Avalonia.Layout } _queued = false; + LayoutUpdated?.Invoke(this, EventArgs.Empty); } /// diff --git a/src/Avalonia.Layout/Layoutable.cs b/src/Avalonia.Layout/Layoutable.cs index 3b13647681..513d3d540e 100644 --- a/src/Avalonia.Layout/Layoutable.cs +++ b/src/Avalonia.Layout/Layoutable.cs @@ -132,6 +132,7 @@ namespace Avalonia.Layout private bool _measuring; private Size? _previousMeasure; private Rect? _previousArrange; + private EventHandler? _layoutUpdated; /// /// Initializes static members of the class. @@ -154,7 +155,28 @@ namespace Avalonia.Layout /// /// Occurs when a layout pass completes for the control. /// - public event EventHandler? LayoutUpdated; + public event EventHandler? LayoutUpdated + { + add + { + if (_layoutUpdated is null && VisualRoot is ILayoutRoot r) + { + r.LayoutManager.LayoutUpdated += LayoutManagedLayoutUpdated; + } + + _layoutUpdated += value; + } + + remove + { + _layoutUpdated -= value; + + if (_layoutUpdated is null && VisualRoot is ILayoutRoot r) + { + r.LayoutManager.LayoutUpdated -= LayoutManagedLayoutUpdated; + } + } + } /// /// Gets or sets the width of the element. @@ -359,12 +381,9 @@ namespace Avalonia.Layout IsArrangeValid = true; ArrangeCore(rect); _previousArrange = rect; - - LayoutUpdated?.Invoke(this, EventArgs.Empty); } } - /// /// Called by InvalidateMeasure /// @@ -694,6 +713,26 @@ namespace Avalonia.Layout InvalidateMeasure(); } + protected override void OnAttachedToVisualTreeCore(VisualTreeAttachmentEventArgs e) + { + base.OnAttachedToVisualTreeCore(e); + + if (_layoutUpdated is object && e.Root is ILayoutRoot r) + { + r.LayoutManager.LayoutUpdated += LayoutManagedLayoutUpdated; + } + } + + protected override void OnDetachedFromVisualTreeCore(VisualTreeAttachmentEventArgs e) + { + base.OnDetachedFromVisualTreeCore(e); + + if (_layoutUpdated is object && e.Root is ILayoutRoot r) + { + r.LayoutManager.LayoutUpdated -= LayoutManagedLayoutUpdated; + } + } + /// protected sealed override void OnVisualParentChanged(IVisual oldParent, IVisual newParent) { @@ -702,6 +741,13 @@ namespace Avalonia.Layout base.OnVisualParentChanged(oldParent, newParent); } + /// + /// Called when the layout manager raises a LayoutUpdated event. + /// + /// The sender. + /// The event args. + private void LayoutManagedLayoutUpdated(object sender, EventArgs e) => _layoutUpdated?.Invoke(this, e); + /// /// Tests whether any of a 's properties include negative values, /// a NaN or Infinity. diff --git a/tests/Avalonia.Controls.UnitTests/GridTests.cs b/tests/Avalonia.Controls.UnitTests/GridTests.cs index 353bb9c98d..b3882c534b 100644 --- a/tests/Avalonia.Controls.UnitTests/GridTests.cs +++ b/tests/Avalonia.Controls.UnitTests/GridTests.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Avalonia.UnitTests; using Xunit; using Xunit.Abstractions; @@ -1182,13 +1183,18 @@ namespace Avalonia.Controls.UnitTests foreach (var xgrids in grids) scope.Children.Add(xgrids); - var root = new Grid(); - root.UseLayoutRounding = false; - root.SetValue(Grid.IsSharedSizeScopeProperty, true); - root.Children.Add(scope); + var rootGrid = new Grid(); + rootGrid.UseLayoutRounding = false; + rootGrid.SetValue(Grid.IsSharedSizeScopeProperty, true); + rootGrid.Children.Add(scope); - root.Measure(new Size(50, 50)); - root.Arrange(new Rect(new Point(), new Point(50, 50))); + var root = new TestRoot(rootGrid) + { + Width = 50, + Height = 50, + }; + + root.LayoutManager.ExecuteInitialLayoutPass(root); PrintColumnDefinitions(grids[0]); Assert.Equal(5, grids[0].ColumnDefinitions[0].ActualWidth); diff --git a/tests/Avalonia.Layout.UnitTests/LayoutableTests.cs b/tests/Avalonia.Layout.UnitTests/LayoutableTests.cs index a1c1e62f58..7013e93a43 100644 --- a/tests/Avalonia.Layout.UnitTests/LayoutableTests.cs +++ b/tests/Avalonia.Layout.UnitTests/LayoutableTests.cs @@ -203,6 +203,127 @@ namespace Avalonia.Layout.UnitTests Assert.Equal(new Rect(expectedX, 0, childWidth, 100), target.Bounds); } + [Fact] + public void LayoutUpdated_Is_Called_At_End_Of_Layout_Pass() + { + Border border1; + Border border2; + var layoutManager = new LayoutManager(); + var root = new TestRoot + { + Child = border1 = new Border + { + Child = border2 = new Border + { + } + }, + LayoutManager = layoutManager, + }; + var raised = 0; + + void ValidateBounds(object sender, EventArgs e) + { + Assert.Equal(new Rect(0, 0, 100, 100), border1.Bounds); + Assert.Equal(new Rect(0, 0, 100, 100), border2.Bounds); + ++raised; + } + + root.LayoutUpdated += ValidateBounds; + border1.LayoutUpdated += ValidateBounds; + border2.LayoutUpdated += ValidateBounds; + + root.Measure(new Size(100, 100)); + root.Arrange(new Rect(0, 0, 100, 100)); + + layoutManager.ExecuteLayoutPass(); + + Assert.Equal(3, raised); + Assert.Equal(new Rect(0, 0, 100, 100), border1.Bounds); + Assert.Equal(new Rect(0, 0, 100, 100), border2.Bounds); + } + + [Fact] + public void LayoutUpdated_Subscribes_To_LayoutManager() + { + Border target; + var layoutManager = new Mock(); + layoutManager.SetupAdd(m => m.LayoutUpdated += (sender, args) => { }); + + var root = new TestRoot + { + Child = new Border + { + Child = target = new Border(), + }, + LayoutManager = layoutManager.Object, + }; + + void Handler(object sender, EventArgs e) {} + + layoutManager.Invocations.Clear(); + target.LayoutUpdated += Handler; + + layoutManager.VerifyAdd( + x => x.LayoutUpdated += It.IsAny(), + Times.Once); + + layoutManager.Invocations.Clear(); + target.LayoutUpdated -= Handler; + + layoutManager.VerifyRemove( + x => x.LayoutUpdated -= It.IsAny(), + Times.Once); + } + + [Fact] + public void LayoutManager_LayoutUpdated_Is_Subscribed_When_Attached_To_Tree() + { + Border border1; + var layoutManager = new Mock(); + layoutManager.SetupAdd(m => m.LayoutUpdated += (sender, args) => { }); + + var root = new TestRoot + { + Child = border1 = new Border(), + LayoutManager = layoutManager.Object, + }; + + var border2 = new Border(); + border2.LayoutUpdated += (s, e) => { }; + + layoutManager.Invocations.Clear(); + border1.Child = border2; + + layoutManager.VerifyAdd( + x => x.LayoutUpdated += It.IsAny(), + Times.Once); + } + + [Fact] + public void LayoutManager_LayoutUpdated_Is_Unsubscribed_When_Detached_From_Tree() + { + Border border1; + var layoutManager = new Mock(); + layoutManager.SetupAdd(m => m.LayoutUpdated += (sender, args) => { }); + + var root = new TestRoot + { + Child = border1 = new Border(), + LayoutManager = layoutManager.Object, + }; + + var border2 = new Border(); + border2.LayoutUpdated += (s, e) => { }; + border1.Child = border2; + + layoutManager.Invocations.Clear(); + border1.Child = null; + + layoutManager.VerifyRemove( + x => x.LayoutUpdated -= It.IsAny(), + Times.Once); + } + private class TestLayoutable : Layoutable { public Size ArrangeSize { get; private set; } From c3a5c48d6fcc11903339c95597b9137c7a40b69f Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 5 Jun 2020 14:52:50 +0200 Subject: [PATCH 03/12] Raise ScrollChanged when layout updated. To be consistent with WPF/UWP `ScrollChanged` should be raised when layout finishes updating, rather than when the individual properties are changed. --- src/Avalonia.Controls/ScrollViewer.cs | 65 +++++++++++-------- .../ScrollViewerTests.cs | 25 ++++++- 2 files changed, 60 insertions(+), 30 deletions(-) diff --git a/src/Avalonia.Controls/ScrollViewer.cs b/src/Avalonia.Controls/ScrollViewer.cs index c3f0dc0056..058c11c9ee 100644 --- a/src/Avalonia.Controls/ScrollViewer.cs +++ b/src/Avalonia.Controls/ScrollViewer.cs @@ -181,6 +181,9 @@ namespace Avalonia.Controls private Size _extent; private Vector _offset; private Size _viewport; + private Size _oldExtent; + private Vector _oldOffset; + private Size _oldViewport; private Size _largeChange; private Size _smallChange = new Size(DefaultSmallChange, DefaultSmallChange); @@ -198,6 +201,7 @@ namespace Avalonia.Controls /// public ScrollViewer() { + LayoutUpdated += OnLayoutUpdated; } /// @@ -221,11 +225,9 @@ namespace Avalonia.Controls private set { - var old = _extent; - if (SetAndRaise(ExtentProperty, ref _extent, value)) { - CalculatedPropertiesChanged(extentDelta: value - old); + CalculatedPropertiesChanged(); } } } @@ -242,13 +244,11 @@ namespace Avalonia.Controls set { - var old = _offset; - value = ValidateOffset(this, value); if (SetAndRaise(OffsetProperty, ref _offset, value)) { - CalculatedPropertiesChanged(offsetDelta: value - old); + CalculatedPropertiesChanged(); } } } @@ -265,11 +265,9 @@ namespace Avalonia.Controls private set { - var old = _viewport; - if (SetAndRaise(ViewportProperty, ref _viewport, value)) { - CalculatedPropertiesChanged(viewportDelta: value - old); + CalculatedPropertiesChanged(); } } } @@ -549,10 +547,7 @@ namespace Avalonia.Controls } } - private void CalculatedPropertiesChanged( - Size extentDelta = default, - Vector offsetDelta = default, - Size viewportDelta = default) + private void CalculatedPropertiesChanged() { // Pass old values of 0 here because we don't have the old values at this point, // and it shouldn't matter as only the template uses these properies. @@ -573,20 +568,6 @@ namespace Avalonia.Controls SetAndRaise(SmallChangeProperty, ref _smallChange, new Size(DefaultSmallChange, DefaultSmallChange)); SetAndRaise(LargeChangeProperty, ref _largeChange, Viewport); } - - if (extentDelta != default || offsetDelta != default || viewportDelta != default) - { - using var route = BuildEventRoute(ScrollChangedEvent); - - if (route.HasHandlers) - { - var e = new ScrollChangedEventArgs( - new Vector(extentDelta.Width, extentDelta.Height), - offsetDelta, - new Vector(viewportDelta.Width, viewportDelta.Height)); - route.RaiseEvent(this, e); - } - } } protected override void OnKeyDown(KeyEventArgs e) @@ -602,5 +583,35 @@ namespace Avalonia.Controls e.Handled = true; } } + + /// + /// Called when a change in scrolling state is detected, such as a change in scroll + /// position, extent, or viewport size. + /// + /// The event args. + /// + /// If you override this method, call `base.OnScrollChanged(ScrollChangedEventArgs)` to + /// ensure that this event is raised. + /// + protected virtual void OnScrollChanged(ScrollChangedEventArgs e) + { + RaiseEvent(e); + } + + private void OnLayoutUpdated(object sender, EventArgs e) => RaiseScrollChanged(); + + private void RaiseScrollChanged() + { + var e = new ScrollChangedEventArgs( + new Vector(Extent.Width - _oldExtent.Width, Extent.Height - _oldExtent.Height), + Offset - _oldOffset, + new Vector(Viewport.Width - _oldViewport.Width, Viewport.Height - _oldViewport.Height)); + + OnScrollChanged(e); + + _oldExtent = Extent; + _oldOffset = Offset; + _oldViewport = Viewport; + } } } diff --git a/tests/Avalonia.Controls.UnitTests/ScrollViewerTests.cs b/tests/Avalonia.Controls.UnitTests/ScrollViewerTests.cs index 8da1e26f0d..deca3cfb75 100644 --- a/tests/Avalonia.Controls.UnitTests/ScrollViewerTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ScrollViewerTests.cs @@ -4,6 +4,7 @@ using Avalonia.Controls.Presenters; using Avalonia.Controls.Primitives; using Avalonia.Controls.Templates; using Avalonia.Layout; +using Avalonia.UnitTests; using Moq; using Xunit; @@ -150,12 +151,15 @@ namespace Avalonia.Controls.UnitTests public void Changing_Extent_Should_Raise_ScrollChanged() { var target = new ScrollViewer(); + var root = new TestRoot(target); var raised = 0; target.SetValue(ScrollViewer.ExtentProperty, new Size(100, 100)); target.SetValue(ScrollViewer.ViewportProperty, new Size(50, 50)); target.Offset = new Vector(10, 10); + root.LayoutManager.ExecuteInitialLayoutPass(root); + target.ScrollChanged += (s, e) => { Assert.Equal(new Vector(11, 12), e.ExtentDelta); @@ -166,20 +170,26 @@ namespace Avalonia.Controls.UnitTests target.SetValue(ScrollViewer.ExtentProperty, new Size(111, 112)); - Assert.Equal(1, raised); + Assert.Equal(0, raised); + + root.LayoutManager.ExecuteLayoutPass(); + Assert.Equal(1, raised); } [Fact] public void Changing_Offset_Should_Raise_ScrollChanged() { var target = new ScrollViewer(); + var root = new TestRoot(target); var raised = 0; target.SetValue(ScrollViewer.ExtentProperty, new Size(100, 100)); target.SetValue(ScrollViewer.ViewportProperty, new Size(50, 50)); target.Offset = new Vector(10, 10); + root.LayoutManager.ExecuteInitialLayoutPass(root); + target.ScrollChanged += (s, e) => { Assert.Equal(default, e.ExtentDelta); @@ -190,20 +200,26 @@ namespace Avalonia.Controls.UnitTests target.Offset = new Vector(22, 24); - Assert.Equal(1, raised); + Assert.Equal(0, raised); + root.LayoutManager.ExecuteLayoutPass(); + + Assert.Equal(1, raised); } [Fact] public void Changing_Viewport_Should_Raise_ScrollChanged() { var target = new ScrollViewer(); + var root = new TestRoot(target); var raised = 0; target.SetValue(ScrollViewer.ExtentProperty, new Size(100, 100)); target.SetValue(ScrollViewer.ViewportProperty, new Size(50, 50)); target.Offset = new Vector(10, 10); + root.LayoutManager.ExecuteInitialLayoutPass(root); + target.ScrollChanged += (s, e) => { Assert.Equal(default, e.ExtentDelta); @@ -214,8 +230,11 @@ namespace Avalonia.Controls.UnitTests target.SetValue(ScrollViewer.ViewportProperty, new Size(56, 58)); - Assert.Equal(1, raised); + Assert.Equal(0, raised); + + root.LayoutManager.ExecuteLayoutPass(); + Assert.Equal(1, raised); } private Control CreateTemplate(ScrollViewer control, INameScope scope) From b51cc92ba56a2c111efa588e15c25a9fbc602ba5 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Fri, 5 Jun 2020 12:36:34 -0300 Subject: [PATCH 04/12] Add rounded rect clip to drawing context. --- src/Avalonia.Visuals/Media/DrawingContext.cs | 6 ++++++ .../Platform/IDrawingContextImpl.cs | 6 ++++++ .../Rendering/SceneGraph/IVisualNode.cs | 5 +++++ .../Rendering/SceneGraph/VisualNode.cs | 3 +++ src/Skia/Avalonia.Skia/DrawingContextImpl.cs | 6 ++++++ src/Skia/Avalonia.Skia/SkiaSharpExtensions.cs | 15 +++++++++++++++ 6 files changed, 41 insertions(+) diff --git a/src/Avalonia.Visuals/Media/DrawingContext.cs b/src/Avalonia.Visuals/Media/DrawingContext.cs index 6fdcd9631b..190675fe48 100644 --- a/src/Avalonia.Visuals/Media/DrawingContext.cs +++ b/src/Avalonia.Visuals/Media/DrawingContext.cs @@ -282,6 +282,12 @@ namespace Avalonia.Media } + public PushedState PushClip(RoundedRect clip) + { + PlatformImpl.PushClip(clip); + return new PushedState(this, PushedState.PushedStateType.Clip); + } + /// /// Pushes a clip rectangle. /// diff --git a/src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs b/src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs index 660d10c088..c87946b3ea 100644 --- a/src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs +++ b/src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs @@ -107,6 +107,12 @@ namespace Avalonia.Platform /// The clip rectangle. void PushClip(Rect clip); + /// + /// Pushes a clip rounded rectangle. + /// + /// The clip rounded rectangle + void PushClip(RoundedRect clip); + /// /// Pops the latest pushed clip rectangle. /// diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/IVisualNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/IVisualNode.cs index 8b77c37c1f..679cf3daa0 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/IVisualNode.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/IVisualNode.cs @@ -26,6 +26,11 @@ namespace Avalonia.Rendering.SceneGraph /// Matrix Transform { get; } + /// + /// Gets the corner radius of visual. Contents are clipped to this radius. + /// + CornerRadius CornerRadius { get; } + /// /// Gets the bounds of the node's geometry in global coordinates. /// diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs index 6f566ff6d6..6276d5c7c3 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs @@ -46,6 +46,9 @@ namespace Avalonia.Rendering.SceneGraph /// public IVisualNode Parent { get; } + /// + public CornerRadius CornerRadius { get; } + /// public Matrix Transform { get; set; } diff --git a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs index 26fdb08a4b..ae756f4eab 100644 --- a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs +++ b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs @@ -389,6 +389,12 @@ namespace Avalonia.Skia Canvas.ClipRect(clip.ToSKRect()); } + public void PushClip(RoundedRect clip) + { + Canvas.Save(); + Canvas.ClipRoundRect(clip.ToSKRoundRect()); + } + /// public void PopClip() { diff --git a/src/Skia/Avalonia.Skia/SkiaSharpExtensions.cs b/src/Skia/Avalonia.Skia/SkiaSharpExtensions.cs index 6375f74c59..ec7e0a67ed 100644 --- a/src/Skia/Avalonia.Skia/SkiaSharpExtensions.cs +++ b/src/Skia/Avalonia.Skia/SkiaSharpExtensions.cs @@ -40,6 +40,21 @@ namespace Avalonia.Skia return new SKRect((float)r.X, (float)r.Y, (float)r.Right, (float)r.Bottom); } + public static SKRoundRect ToSKRoundRect(this RoundedRect r) + { + var rc = r.Rect.ToSKRect(); + var result = new SKRoundRect(); + + result.SetRectRadii(rc, + new[] + { + r.RadiiTopLeft.ToSKPoint(), r.RadiiTopRight.ToSKPoint(), + r.RadiiBottomRight.ToSKPoint(), r.RadiiBottomLeft.ToSKPoint(), + }); + + return result; + } + public static Rect ToAvaloniaRect(this SKRect r) { return new Rect(r.Left, r.Top, r.Right - r.Left, r.Bottom - r.Top); From da580f0373e3ae154c48a64b9b27c48ed5004b26 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Sun, 14 Jun 2020 20:24:59 +0300 Subject: [PATCH 05/12] Internal API for rounded border clip for usage with Border. --- samples/ControlCatalog/Pages/BorderPage.xaml | 7 +++++++ src/Avalonia.Controls/Border.cs | 5 ++++- .../Rendering/ImmediateRenderer.cs | 6 +++++- .../Rendering/SceneGraph/ClipNode.cs | 14 ++++++++++++-- .../SceneGraph/DeferredDrawingContextImpl.cs | 17 ++++++++++++++++- .../Rendering/SceneGraph/IVisualNode.cs | 2 +- .../Rendering/SceneGraph/SceneBuilder.cs | 5 +++++ .../Rendering/SceneGraph/VisualNode.cs | 8 ++++++-- src/Avalonia.Visuals/RoundedRect.cs | 11 +++++++++++ .../VisualTree/IVisualWithRoundRectClip.cs | 15 +++++++++++++++ .../Media/DrawingContextImpl.cs | 6 ++++++ 11 files changed, 88 insertions(+), 8 deletions(-) create mode 100644 src/Avalonia.Visuals/VisualTree/IVisualWithRoundRectClip.cs diff --git a/samples/ControlCatalog/Pages/BorderPage.xaml b/samples/ControlCatalog/Pages/BorderPage.xaml index c30056d5e5..429db7917e 100644 --- a/samples/ControlCatalog/Pages/BorderPage.xaml +++ b/samples/ControlCatalog/Pages/BorderPage.xaml @@ -29,6 +29,13 @@ Padding="16"> Rounded Corners + + + Clipped Rounded Corners + + diff --git a/src/Avalonia.Controls/Border.cs b/src/Avalonia.Controls/Border.cs index b355310244..c8337df99c 100644 --- a/src/Avalonia.Controls/Border.cs +++ b/src/Avalonia.Controls/Border.cs @@ -1,13 +1,14 @@ using Avalonia.Controls.Utils; using Avalonia.Layout; using Avalonia.Media; +using Avalonia.VisualTree; namespace Avalonia.Controls { /// /// A control which decorates a child with a border and background. /// - public partial class Border : Decorator + public partial class Border : Decorator, IVisualWithRoundRectClip { /// /// Defines the property. @@ -129,5 +130,7 @@ namespace Avalonia.Controls { return LayoutHelper.ArrangeChild(Child, finalSize, Padding, BorderThickness); } + + public CornerRadius ClipToBoundsRadius => CornerRadius; } } diff --git a/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs b/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs index c1c4b6bc99..b1f51b941a 100644 --- a/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs +++ b/src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs @@ -289,7 +289,11 @@ namespace Avalonia.Rendering using (context.PushPostTransform(m)) using (context.PushOpacity(opacity)) - using (clipToBounds ? context.PushClip(bounds) : default(DrawingContext.PushedState)) + using (clipToBounds + ? visual is IVisualWithRoundRectClip roundClipVisual + ? context.PushClip(new RoundedRect(bounds, roundClipVisual.ClipToBoundsRadius)) + : context.PushClip(bounds) + : default(DrawingContext.PushedState)) using (visual.Clip != null ? context.PushGeometryClip(visual.Clip) : default(DrawingContext.PushedState)) using (visual.OpacityMask != null ? context.PushOpacityMask(visual.OpacityMask, bounds) : default(DrawingContext.PushedState)) using (context.PushTransformContainer()) diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/ClipNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/ClipNode.cs index 34f042e334..ada04bfefd 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/ClipNode.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/ClipNode.cs @@ -16,6 +16,16 @@ namespace Avalonia.Rendering.SceneGraph { Clip = clip; } + + /// + /// Initializes a new instance of the class that represents a + /// clip push. + /// + /// The clip to push. + public ClipNode(RoundedRect clip) + { + Clip = clip; + } /// /// Initializes a new instance of the class that represents a @@ -31,7 +41,7 @@ namespace Avalonia.Rendering.SceneGraph /// /// Gets the clip to be pushed or null if the operation represents a pop. /// - public Rect? Clip { get; } + public RoundedRect? Clip { get; } /// public bool HitTest(Point p) => false; @@ -45,7 +55,7 @@ namespace Avalonia.Rendering.SceneGraph /// The properties of the other draw operation are passed in as arguments to prevent /// allocation of a not-yet-constructed draw operation object. /// - public bool Equals(Rect? clip) => Clip == clip; + public bool Equals(RoundedRect? clip) => Clip == clip; /// public void Render(IDrawingContextImpl context) diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs index 6ad71ac111..936f5d30a7 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs @@ -207,7 +207,7 @@ namespace Avalonia.Rendering.SceneGraph { throw new NotSupportedException("Creating layers on a deferred drawing context not supported"); } - + /// public void PopClip() { @@ -282,6 +282,21 @@ namespace Avalonia.Rendering.SceneGraph ++_drawOperationindex; } } + + /// + public void PushClip(RoundedRect clip) + { + var next = NextDrawAs(); + + if (next == null || !next.Item.Equals(clip)) + { + Add(new ClipNode(clip)); + } + else + { + ++_drawOperationindex; + } + } /// public void PushGeometryClip(IGeometryImpl clip) diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/IVisualNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/IVisualNode.cs index 679cf3daa0..6d12b5bca4 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/IVisualNode.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/IVisualNode.cs @@ -29,7 +29,7 @@ namespace Avalonia.Rendering.SceneGraph /// /// Gets the corner radius of visual. Contents are clipped to this radius. /// - CornerRadius CornerRadius { get; } + CornerRadius ClipToBoundsRadius { get; } /// /// Gets the bounds of the node's geometry in global coordinates. diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs index 263a491933..5da44c5943 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs @@ -163,6 +163,10 @@ namespace Avalonia.Rendering.SceneGraph var visual = node.Visual; var opacity = visual.Opacity; var clipToBounds = visual.ClipToBounds; + var clipToBoundsRadius = visual is IVisualWithRoundRectClip roundRectClip ? + roundRectClip.ClipToBoundsRadius : + default; + var bounds = new Rect(visual.Bounds.Size); var contextImpl = (DeferredDrawingContextImpl)context.PlatformImpl; @@ -201,6 +205,7 @@ namespace Avalonia.Rendering.SceneGraph node.ClipBounds = clipBounds; node.ClipToBounds = clipToBounds; node.LayoutBounds = globalBounds; + node.ClipToBoundsRadius = clipToBoundsRadius; node.GeometryClip = visual.Clip?.PlatformImpl; node.Opacity = opacity; diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs index 6276d5c7c3..8fb6b2542a 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs @@ -47,7 +47,7 @@ namespace Avalonia.Rendering.SceneGraph public IVisualNode Parent { get; } /// - public CornerRadius CornerRadius { get; } + public CornerRadius ClipToBoundsRadius { get; set; } /// public Matrix Transform { get; set; } @@ -265,6 +265,7 @@ namespace Avalonia.Rendering.SceneGraph { Transform = Transform, ClipBounds = ClipBounds, + ClipToBoundsRadius = ClipToBoundsRadius, ClipToBounds = ClipToBounds, LayoutBounds = LayoutBounds, GeometryClip = GeometryClip, @@ -304,7 +305,10 @@ namespace Avalonia.Rendering.SceneGraph if (ClipToBounds) { context.Transform = Matrix.Identity; - context.PushClip(ClipBounds); + if (ClipToBoundsRadius.IsEmpty) + context.PushClip(ClipBounds); + else + context.PushClip(new RoundedRect(ClipBounds, ClipToBoundsRadius)); } context.Transform = Transform; diff --git a/src/Avalonia.Visuals/RoundedRect.cs b/src/Avalonia.Visuals/RoundedRect.cs index ad860240f2..3452bc1ff8 100644 --- a/src/Avalonia.Visuals/RoundedRect.cs +++ b/src/Avalonia.Visuals/RoundedRect.cs @@ -27,6 +27,10 @@ namespace Avalonia } } + public static bool operator ==(RoundedRect left, RoundedRect right) => left.Equals(right); + + public static bool operator !=(RoundedRect left, RoundedRect right) => !left.Equals(right); + public Rect Rect { get; } public Vector RadiiTopLeft { get; } public Vector RadiiTopRight { get; } @@ -74,6 +78,13 @@ namespace Avalonia } + public RoundedRect(in Rect bounds, in CornerRadius radius) : this(bounds, + radius.TopLeft, radius.TopRight, + radius.BottomRight, radius.BottomLeft) + { + + } + public static implicit operator RoundedRect(Rect r) => new RoundedRect(r); public bool IsRounded => RadiiTopLeft != default || RadiiTopRight != default || RadiiBottomRight != default || diff --git a/src/Avalonia.Visuals/VisualTree/IVisualWithRoundRectClip.cs b/src/Avalonia.Visuals/VisualTree/IVisualWithRoundRectClip.cs new file mode 100644 index 0000000000..9ace215d03 --- /dev/null +++ b/src/Avalonia.Visuals/VisualTree/IVisualWithRoundRectClip.cs @@ -0,0 +1,15 @@ +using System; + +namespace Avalonia.VisualTree +{ + [Obsolete("Internal API, will be removed in future versions, you've been warned")] + public interface IVisualWithRoundRectClip + { + /// + /// Gets a value indicating the corner radius of control's clip bounds + /// + [Obsolete("Internal API, will be removed in future versions, you've been warned")] + CornerRadius ClipToBoundsRadius { get; } + + } +} diff --git a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs index bbb45cf64c..696dc5978e 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs @@ -359,6 +359,12 @@ namespace Avalonia.Direct2D1.Media _deviceContext.PushAxisAlignedClip(clip.ToSharpDX(), AntialiasMode.PerPrimitive); } + public void PushClip(RoundedRect clip) + { + //TODO: radius + _deviceContext.PushAxisAlignedClip(clip.Rect.ToDirect2D(), AntialiasMode.PerPrimitive); + } + public void PopClip() { _deviceContext.PopAxisAlignedClip(); From bc891efc13d742291765f0fb4584a72fdd2ebcd6 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 16 Jun 2020 15:56:24 +0200 Subject: [PATCH 06/12] Don't raise ScrollChanged if nothing changed. And remove empty braces in test. --- src/Avalonia.Controls/ScrollViewer.cs | 19 +++++++++++-------- .../LayoutableTests.cs | 4 +--- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/Avalonia.Controls/ScrollViewer.cs b/src/Avalonia.Controls/ScrollViewer.cs index 058c11c9ee..d77833b32e 100644 --- a/src/Avalonia.Controls/ScrollViewer.cs +++ b/src/Avalonia.Controls/ScrollViewer.cs @@ -602,16 +602,19 @@ namespace Avalonia.Controls private void RaiseScrollChanged() { - var e = new ScrollChangedEventArgs( - new Vector(Extent.Width - _oldExtent.Width, Extent.Height - _oldExtent.Height), - Offset - _oldOffset, - new Vector(Viewport.Width - _oldViewport.Width, Viewport.Height - _oldViewport.Height)); + var extentDelta = new Vector(Extent.Width - _oldExtent.Width, Extent.Height - _oldExtent.Height); + var offsetDelta = Offset - _oldOffset; + var viewportDelta = new Vector(Viewport.Width - _oldViewport.Width, Viewport.Height - _oldViewport.Height); - OnScrollChanged(e); + if (extentDelta != default || offsetDelta != default || viewportDelta != default) + { + var e = new ScrollChangedEventArgs(extentDelta, offsetDelta, viewportDelta); + OnScrollChanged(e); - _oldExtent = Extent; - _oldOffset = Offset; - _oldViewport = Viewport; + _oldExtent = Extent; + _oldOffset = Offset; + _oldViewport = Viewport; + } } } } diff --git a/tests/Avalonia.Layout.UnitTests/LayoutableTests.cs b/tests/Avalonia.Layout.UnitTests/LayoutableTests.cs index 7013e93a43..a21c8d589d 100644 --- a/tests/Avalonia.Layout.UnitTests/LayoutableTests.cs +++ b/tests/Avalonia.Layout.UnitTests/LayoutableTests.cs @@ -213,9 +213,7 @@ namespace Avalonia.Layout.UnitTests { Child = border1 = new Border { - Child = border2 = new Border - { - } + Child = border2 = new Border(), }, LayoutManager = layoutManager, }; From 62bc5279ca556d22f2eb99e6cf0d1e177704fdf0 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 17 Jun 2020 16:20:58 -0300 Subject: [PATCH 07/12] use nearly equals for deciding to raise scrollchanged event. --- src/Avalonia.Controls/ScrollViewer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/ScrollViewer.cs b/src/Avalonia.Controls/ScrollViewer.cs index def947a433..aceb2761a4 100644 --- a/src/Avalonia.Controls/ScrollViewer.cs +++ b/src/Avalonia.Controls/ScrollViewer.cs @@ -638,7 +638,7 @@ namespace Avalonia.Controls var offsetDelta = Offset - _oldOffset; var viewportDelta = new Vector(Viewport.Width - _oldViewport.Width, Viewport.Height - _oldViewport.Height); - if (extentDelta != default || offsetDelta != default || viewportDelta != default) + if (!extentDelta.NearlyEquals(default) && !offsetDelta.NearlyEquals(default) && !viewportDelta.NearlyEquals(default)) { var e = new ScrollChangedEventArgs(extentDelta, offsetDelta, viewportDelta); OnScrollChanged(e); From beb4c30107aeb67a1055b3f1b2389cbc4dffac8e Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 17 Jun 2020 16:50:33 -0300 Subject: [PATCH 08/12] fix if statement logic . --- src/Avalonia.Controls/ScrollViewer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/ScrollViewer.cs b/src/Avalonia.Controls/ScrollViewer.cs index aceb2761a4..a5f55eaa02 100644 --- a/src/Avalonia.Controls/ScrollViewer.cs +++ b/src/Avalonia.Controls/ScrollViewer.cs @@ -638,7 +638,7 @@ namespace Avalonia.Controls var offsetDelta = Offset - _oldOffset; var viewportDelta = new Vector(Viewport.Width - _oldViewport.Width, Viewport.Height - _oldViewport.Height); - if (!extentDelta.NearlyEquals(default) && !offsetDelta.NearlyEquals(default) && !viewportDelta.NearlyEquals(default)) + if (!extentDelta.NearlyEquals(default) || !offsetDelta.NearlyEquals(default) || !viewportDelta.NearlyEquals(default)) { var e = new ScrollChangedEventArgs(extentDelta, offsetDelta, viewportDelta); OnScrollChanged(e); From b9cec08b41c4ae6213e66ced4494f30623f8f3e7 Mon Sep 17 00:00:00 2001 From: JamRemco <58340108+JamRemco@users.noreply.github.com> Date: Wed, 17 Jun 2020 22:08:50 +0200 Subject: [PATCH 09/12] ToggleSwitchpage for Control gallery (#4137) Co-authored-by: danwalmsley --- samples/ControlCatalog/MainView.xaml | 1 + .../Pages/ToggleSwitchPage.xaml | 79 +++++++++++++++++++ .../Pages/ToggleSwitchPage.xaml.cs | 19 +++++ src/Avalonia.Controls/ToggleSwitch.cs | 33 +++++++- .../Avalonia.Themes.Fluent.csproj | 2 +- 5 files changed, 132 insertions(+), 2 deletions(-) create mode 100644 samples/ControlCatalog/Pages/ToggleSwitchPage.xaml create mode 100644 samples/ControlCatalog/Pages/ToggleSwitchPage.xaml.cs diff --git a/samples/ControlCatalog/MainView.xaml b/samples/ControlCatalog/MainView.xaml index af2d093bc7..fef68bb5f5 100644 --- a/samples/ControlCatalog/MainView.xaml +++ b/samples/ControlCatalog/MainView.xaml @@ -57,6 +57,7 @@ + diff --git a/samples/ControlCatalog/Pages/ToggleSwitchPage.xaml b/samples/ControlCatalog/Pages/ToggleSwitchPage.xaml new file mode 100644 index 0000000000..161ee2ee16 --- /dev/null +++ b/samples/ControlCatalog/Pages/ToggleSwitchPage.xaml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/ControlCatalog/Pages/ToggleSwitchPage.xaml.cs b/samples/ControlCatalog/Pages/ToggleSwitchPage.xaml.cs new file mode 100644 index 0000000000..66f7d14c7f --- /dev/null +++ b/samples/ControlCatalog/Pages/ToggleSwitchPage.xaml.cs @@ -0,0 +1,19 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; + +namespace ControlCatalog.Pages +{ + public class ToggleSwitchPage : UserControl + { + public ToggleSwitchPage() + { + this.InitializeComponent(); + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } + } +} diff --git a/src/Avalonia.Controls/ToggleSwitch.cs b/src/Avalonia.Controls/ToggleSwitch.cs index 6c6426a31d..4b42c574cf 100644 --- a/src/Avalonia.Controls/ToggleSwitch.cs +++ b/src/Avalonia.Controls/ToggleSwitch.cs @@ -1,4 +1,5 @@ -using Avalonia.Controls.Primitives; +using Avalonia.Controls.Presenters; +using Avalonia.Controls.Primitives; using Avalonia.Controls.Templates; using Avalonia.LogicalTree; @@ -57,6 +58,18 @@ namespace Avalonia.Controls set { SetValue(OffContentProperty, value); } } + public IContentPresenter OffContentPresenter + { + get; + private set; + } + + public IContentPresenter OnContentPresenter + { + get; + private set; + } + /// /// Gets or Sets the used to display the . /// @@ -100,6 +113,24 @@ namespace Avalonia.Controls LogicalChildren.Add(newChild); } } + + protected override bool RegisterContentPresenter(IContentPresenter presenter) + { + var result = base.RegisterContentPresenter(presenter); + + if (presenter.Name == "Part_OnContentPresenter") + { + OnContentPresenter = presenter; + result = true; + } + else if (presenter.Name == "PART_OffContentPresenter") + { + OffContentPresenter = presenter; + result = true; + } + + return result; + } } } diff --git a/src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj b/src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj index 3c3e14010d..a4eab83e4a 100644 --- a/src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj +++ b/src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj @@ -19,4 +19,4 @@ - + \ No newline at end of file From e555f46d4092bc5310aacaf6ef5d0fe371261d20 Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Wed, 17 Jun 2020 22:10:06 +0200 Subject: [PATCH 10/12] Add attribution for chromium source code. --- NOTICE.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/NOTICE.md b/NOTICE.md index 0e1d792e84..92fd725957 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -271,3 +271,35 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE + +# Chromium + +https://github.com/chromium/chromium + +// Copyright 2015 The Chromium Authors. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file From 25b005ec9684349cb78540314afc5d6a13672a1e Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 17 Jun 2020 17:55:38 -0300 Subject: [PATCH 11/12] update example to show intended usage. --- samples/ControlCatalog/Pages/BorderPage.xaml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/samples/ControlCatalog/Pages/BorderPage.xaml b/samples/ControlCatalog/Pages/BorderPage.xaml index 429db7917e..8133d0e408 100644 --- a/samples/ControlCatalog/Pages/BorderPage.xaml +++ b/samples/ControlCatalog/Pages/BorderPage.xaml @@ -29,13 +29,13 @@ Padding="16"> Rounded Corners - - - Clipped Rounded Corners - + + + From c55b0e27c3fc9f79b93e834aacb1b2797da470b5 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 17 Jun 2020 17:58:20 -0300 Subject: [PATCH 12/12] fix whitespace. --- .../Rendering/SceneGraph/DeferredDrawingContextImpl.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs index 936f5d30a7..dfb21a0289 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs @@ -163,7 +163,7 @@ namespace Avalonia.Rendering.SceneGraph ++_drawOperationindex; } } - + public void Custom(ICustomDrawOperation custom) { var next = NextDrawAs(); @@ -207,7 +207,7 @@ namespace Avalonia.Rendering.SceneGraph { throw new NotSupportedException("Creating layers on a deferred drawing context not supported"); } - + /// public void PopClip() { @@ -282,7 +282,7 @@ namespace Avalonia.Rendering.SceneGraph ++_drawOperationindex; } } - + /// public void PushClip(RoundedRect clip) { @@ -383,7 +383,7 @@ namespace Avalonia.Rendering.SceneGraph { using (var refCounted = RefCountable.Create(node)) { - Add(refCounted); + Add(refCounted); } }