From 12d8daefaf3e55267ffed30bf4bf606cd940ed45 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 5 Oct 2020 18:29:07 +0200 Subject: [PATCH 01/35] Added nullable annotations. To `ScrollContentPresenter` and `IScrollAnchorProvider`. --- .../IScrollAnchorProvider.cs | 6 ++-- .../Presenters/ScrollContentPresenter.cs | 31 ++++++++----------- 2 files changed, 17 insertions(+), 20 deletions(-) diff --git a/src/Avalonia.Controls/IScrollAnchorProvider.cs b/src/Avalonia.Controls/IScrollAnchorProvider.cs index 93f3a0abb8..7ba02e99ea 100644 --- a/src/Avalonia.Controls/IScrollAnchorProvider.cs +++ b/src/Avalonia.Controls/IScrollAnchorProvider.cs @@ -1,4 +1,6 @@ -namespace Avalonia.Controls +#nullable enable + +namespace Avalonia.Controls { /// /// Specifies a contract for a scrolling control that supports scroll anchoring. @@ -8,7 +10,7 @@ /// /// The currently chosen anchor element to use for scroll anchoring. /// - IControl CurrentAnchor { get; } + IControl? CurrentAnchor { get; } /// /// Registers a control as a potential scroll anchor candidate. diff --git a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs b/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs index 5fcb14c858..2a04558fc8 100644 --- a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs +++ b/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs @@ -7,6 +7,8 @@ using Avalonia.Controls.Primitives; using Avalonia.Input; using Avalonia.VisualTree; +#nullable enable + namespace Avalonia.Controls.Presenters { /// @@ -64,11 +66,11 @@ namespace Avalonia.Controls.Presenters private bool _arranging; private Size _extent; private Vector _offset; - private IDisposable _logicalScrollSubscription; + private IDisposable? _logicalScrollSubscription; private Size _viewport; - private Dictionary _activeLogicalGestureScrolls; - private List _anchorCandidates; - private (IControl control, Rect bounds) _anchor; + private Dictionary? _activeLogicalGestureScrolls; + private List? _anchorCandidates; + private (IControl? control, Rect bounds) _anchor; /// /// Initializes static members of the class. @@ -90,8 +92,6 @@ namespace Avalonia.Controls.Presenters this.GetObservable(ChildProperty).Subscribe(UpdateScrollableSubscription); } - internal event EventHandler PreArrange; - /// /// Gets or sets a value indicating whether the content can be scrolled horizontally. /// @@ -138,7 +138,7 @@ namespace Avalonia.Controls.Presenters } /// - IControl IScrollAnchorProvider.CurrentAnchor => _anchor.control; + IControl? IScrollAnchorProvider.CurrentAnchor => _anchor.control; /// /// Attempts to bring a portion of the target visual into view by scrolling the content. @@ -247,11 +247,6 @@ namespace Avalonia.Controls.Presenters /// protected override Size ArrangeOverride(Size finalSize) { - PreArrange?.Invoke(this, new VectorEventArgs - { - Vector = new Vector(finalSize.Width, finalSize.Height), - }); - if (_logicalScrollSubscription != null || Child == null) { return base.ArrangeOverride(finalSize); @@ -350,7 +345,7 @@ namespace Avalonia.Controls.Presenters { var logicalUnits = delta.Y / LogicalScrollItemSize; delta = delta.WithY(delta.Y - logicalUnits * LogicalScrollItemSize); - dy = logicalUnits * scrollable.ScrollSize.Height; + dy = logicalUnits * scrollable!.ScrollSize.Height; } else dy = delta.Y; @@ -368,7 +363,7 @@ namespace Avalonia.Controls.Presenters { var logicalUnits = delta.X / LogicalScrollItemSize; delta = delta.WithX(delta.X - logicalUnits * LogicalScrollItemSize); - dx = logicalUnits * scrollable.ScrollSize.Width; + dx = logicalUnits * scrollable!.ScrollSize.Width; } else dx = delta.X; @@ -405,7 +400,7 @@ namespace Avalonia.Controls.Presenters if (Extent.Height > Viewport.Height) { - double height = isLogical ? scrollable.ScrollSize.Height : 50; + double height = isLogical ? scrollable!.ScrollSize.Height : 50; y += -e.Delta.Y * height; y = Math.Max(y, 0); y = Math.Min(y, Extent.Height - Viewport.Height); @@ -413,7 +408,7 @@ namespace Avalonia.Controls.Presenters if (Extent.Width > Viewport.Width) { - double width = isLogical ? scrollable.ScrollSize.Width : 50; + double width = isLogical ? scrollable!.ScrollSize.Width : 50; x += -e.Delta.X * width; x = Math.Max(x, 0); x = Math.Min(x, Extent.Width - Viewport.Width); @@ -441,7 +436,7 @@ namespace Avalonia.Controls.Presenters private void ChildChanged(AvaloniaPropertyChangedEventArgs e) { - UpdateScrollableSubscription((IControl)e.NewValue); + UpdateScrollableSubscription((IControl?)e.NewValue); if (e.OldValue != null) { @@ -449,7 +444,7 @@ namespace Avalonia.Controls.Presenters } } - private void UpdateScrollableSubscription(IControl child) + private void UpdateScrollableSubscription(IControl? child) { var scrollable = child as ILogicalScrollable; From ce3c8316b5cd90db1e293e7cb4bf4a97cae8c823 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 6 Oct 2020 10:46:15 +0200 Subject: [PATCH 02/35] Don't invalidate ItemsRepeater measure on children change. --- src/Avalonia.Controls/Panel.cs | 5 +++++ src/Avalonia.Controls/Repeater/ItemsRepeater.cs | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/src/Avalonia.Controls/Panel.cs b/src/Avalonia.Controls/Panel.cs index ccb92dc497..b7eeb065da 100644 --- a/src/Avalonia.Controls/Panel.cs +++ b/src/Avalonia.Controls/Panel.cs @@ -137,6 +137,11 @@ namespace Avalonia.Controls throw new NotSupportedException(); } + InvalidateMeasureOnChildrenChanged(); + } + + private protected virtual void InvalidateMeasureOnChildrenChanged() + { InvalidateMeasure(); } diff --git a/src/Avalonia.Controls/Repeater/ItemsRepeater.cs b/src/Avalonia.Controls/Repeater/ItemsRepeater.cs index 40f1b8dbb9..9ed09df7db 100644 --- a/src/Avalonia.Controls/Repeater/ItemsRepeater.cs +++ b/src/Avalonia.Controls/Repeater/ItemsRepeater.cs @@ -267,6 +267,11 @@ namespace Avalonia.Controls return result; } + private protected override void InvalidateMeasureOnChildrenChanged() + { + // Don't invalidate measure when children change. + } + protected override Size MeasureOverride(Size availableSize) { if (_isLayoutInProgress) From e9c990d9082bd9b43d014b61612ba1baf3ab9b52 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 6 Oct 2020 11:23:21 +0200 Subject: [PATCH 03/35] Tweak ScrollContentPresenter anchoring. Using WinUI's `ScrollPresenter` implementation to guide us. --- .../Presenters/ScrollContentPresenter.cs | 110 +++++++++++------- 1 file changed, 68 insertions(+), 42 deletions(-) diff --git a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs b/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs index 2a04558fc8..b0b52812b9 100644 --- a/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs +++ b/src/Avalonia.Controls/Presenters/ScrollContentPresenter.cs @@ -16,6 +16,8 @@ namespace Avalonia.Controls.Presenters /// public class ScrollContentPresenter : ContentPresenter, IPresenter, IScrollable, IScrollAnchorProvider { + private const double EdgeDetectionTolerance = 0.1; + /// /// Defines the property. /// @@ -70,7 +72,9 @@ namespace Avalonia.Controls.Presenters private Size _viewport; private Dictionary? _activeLogicalGestureScrolls; private List? _anchorCandidates; - private (IControl? control, Rect bounds) _anchor; + private IControl? _anchorElement; + private Rect _anchorElementBounds; + private bool _isAnchorElementDirty; /// /// Initializes static members of the class. @@ -138,7 +142,14 @@ namespace Avalonia.Controls.Presenters } /// - IControl? IScrollAnchorProvider.CurrentAnchor => _anchor.control; + IControl? IScrollAnchorProvider.CurrentAnchor + { + get + { + EnsureAnchorElementSelection(); + return _anchorElement; + } + } /// /// Attempts to bring a portion of the target visual into view by scrolling the content. @@ -215,16 +226,18 @@ namespace Avalonia.Controls.Presenters _anchorCandidates ??= new List(); _anchorCandidates.Add(element); + _isAnchorElementDirty = true; } /// void IScrollAnchorProvider.UnregisterAnchorCandidate(IControl element) { _anchorCandidates?.Remove(element); + _isAnchorElementDirty = true; - if (_anchor.control == element) + if (_anchorElement == element) { - _anchor = default; + _anchorElement = null; } } @@ -266,59 +279,69 @@ namespace Avalonia.Controls.Presenters // If we have an anchor and its position relative to Child has changed during the // arrange then that change wasn't just due to scrolling (as scrolling doesn't adjust // relative positions within Child). - if (_anchor.control != null && - TranslateBounds(_anchor.control, Child, out var updatedBounds) && - updatedBounds.Position != _anchor.bounds.Position) + if (_anchorElement != null && + TranslateBounds(_anchorElement, Child, out var updatedBounds) && + updatedBounds.Position != _anchorElementBounds.Position) { - var offset = updatedBounds.Position - _anchor.bounds.Position; + var offset = updatedBounds.Position - _anchorElementBounds.Position; return offset; } return default; } - // Calculate the new anchor element. - _anchor = CalculateCurrentAnchor(); + var isAnchoring = Offset.X >= EdgeDetectionTolerance || Offset.Y >= EdgeDetectionTolerance; - // Do the arrange. - ArrangeOverrideImpl(size, -Offset); + if (isAnchoring) + { + // Calculate the new anchor element if necessary. + EnsureAnchorElementSelection(); - // If the anchor moved during the arrange, we need to adjust the offset and do another arrange. - var anchorShift = TrackAnchor(); + // Do the arrange. + ArrangeOverrideImpl(size, -Offset); - if (anchorShift != default) - { - var newOffset = Offset + anchorShift; - var newExtent = Extent; - var maxOffset = new Vector(Extent.Width - Viewport.Width, Extent.Height - Viewport.Height); + // If the anchor moved during the arrange, we need to adjust the offset and do another arrange. + var anchorShift = TrackAnchor(); - if (newOffset.X > maxOffset.X) + if (anchorShift != default) { - newExtent = newExtent.WithWidth(newOffset.X + Viewport.Width); - } + var newOffset = Offset + anchorShift; + var newExtent = Extent; + var maxOffset = new Vector(Extent.Width - Viewport.Width, Extent.Height - Viewport.Height); - if (newOffset.Y > maxOffset.Y) - { - newExtent = newExtent.WithHeight(newOffset.Y + Viewport.Height); - } + if (newOffset.X > maxOffset.X) + { + newExtent = newExtent.WithWidth(newOffset.X + Viewport.Width); + } - Extent = newExtent; + if (newOffset.Y > maxOffset.Y) + { + newExtent = newExtent.WithHeight(newOffset.Y + Viewport.Height); + } - try - { - _arranging = true; - Offset = newOffset; - } - finally - { - _arranging = false; + Extent = newExtent; + + try + { + _arranging = true; + Offset = newOffset; + } + finally + { + _arranging = false; + } + + ArrangeOverrideImpl(size, -Offset); } - + } + else + { ArrangeOverrideImpl(size, -Offset); } Viewport = finalSize; Extent = Child.Bounds.Size.Inflate(Child.Margin); + _isAnchorElementDirty = true; return finalSize; } @@ -493,13 +516,17 @@ namespace Avalonia.Controls.Presenters } } - private (IControl, Rect) CalculateCurrentAnchor() + private void EnsureAnchorElementSelection() { - if (_anchorCandidates == null) + if (!_isAnchorElementDirty || _anchorCandidates is null) { - return default; + return; } + _anchorElement = null; + _anchorElementBounds = default; + _isAnchorElementDirty = false; + var bestCandidate = default(IControl); var bestCandidateDistance = double.MaxValue; @@ -526,10 +553,9 @@ namespace Avalonia.Controls.Presenters // bounds aren't relative to the ScrollContentPresenter itself, if they change // then we know it wasn't just due to scrolling. var unscrolledBounds = TranslateBounds(bestCandidate, Child); - return (bestCandidate, unscrolledBounds); + _anchorElement = bestCandidate; + _anchorElementBounds = unscrolledBounds; } - - return default; } private bool GetViewportBounds(IControl element, out Rect bounds) From d5224d4bb0a98f55c1da1cae9eaab6a57d4b3697 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 6 Oct 2020 11:43:56 +0200 Subject: [PATCH 04/35] Ensure valid index before calculating offset. --- src/Avalonia.Layout/StackLayout.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Layout/StackLayout.cs b/src/Avalonia.Layout/StackLayout.cs index 909c7bc7eb..4a93c8344f 100644 --- a/src/Avalonia.Layout/StackLayout.cs +++ b/src/Avalonia.Layout/StackLayout.cs @@ -249,8 +249,8 @@ namespace Avalonia.Layout realizationWindowOffsetInExtent + _orientation.MajorSize(realizationRect) >= 0 && realizationWindowOffsetInExtent <= majorSize) { anchorIndex = (int) (realizationWindowOffsetInExtent / averageElementSize); - offset = anchorIndex* averageElementSize + _orientation.MajorStart(lastExtent); anchorIndex = Math.Max(0, Math.Min(itemsCount - 1, anchorIndex)); + offset = anchorIndex* averageElementSize + _orientation.MajorStart(lastExtent); } } From bba2cafcc1efe44af5e7cf286a2bedaa389cdc20 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 6 Oct 2020 16:13:21 +0200 Subject: [PATCH 05/35] Don't register anchor candidates until they're laid out. --- src/Avalonia.Controls/Repeater/ItemsRepeater.cs | 13 +++++++++++-- src/Avalonia.Controls/Repeater/ViewManager.cs | 2 +- src/Avalonia.Controls/Repeater/ViewportManager.cs | 14 ++++++++++++-- .../Repeater/VirtualizationInfo.cs | 1 + 4 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/Avalonia.Controls/Repeater/ItemsRepeater.cs b/src/Avalonia.Controls/Repeater/ItemsRepeater.cs index 9ed09df7db..fb2da09e73 100644 --- a/src/Avalonia.Controls/Repeater/ItemsRepeater.cs +++ b/src/Avalonia.Controls/Repeater/ItemsRepeater.cs @@ -369,6 +369,12 @@ namespace Avalonia.Controls { var newBounds = element.Bounds; virtInfo.ArrangeBounds = newBounds; + + if (!virtInfo.IsRegisteredAsAnchorCandidate) + { + _viewportManager.RegisterScrollAnchorCandidate(element); + virtInfo.IsRegisteredAsAnchorCandidate = true; + } } } @@ -520,11 +526,14 @@ namespace Avalonia.Controls return element; } - internal void OnElementPrepared(IControl element, int index) + internal void OnElementPrepared(IControl element, VirtualizationInfo virtInfo) { - _viewportManager.OnElementPrepared(element); + _viewportManager.OnElementPrepared(element, virtInfo); + if (ElementPrepared != null) { + var index = virtInfo.Index; + if (_elementPreparedArgs == null) { _elementPreparedArgs = new ItemsRepeaterElementPreparedEventArgs(element, index); diff --git a/src/Avalonia.Controls/Repeater/ViewManager.cs b/src/Avalonia.Controls/Repeater/ViewManager.cs index 416b1e2824..cf2066b373 100644 --- a/src/Avalonia.Controls/Repeater/ViewManager.cs +++ b/src/Avalonia.Controls/Repeater/ViewManager.cs @@ -661,7 +661,7 @@ namespace Avalonia.Controls children.Add(element); } - repeater.OnElementPrepared(element, index); + repeater.OnElementPrepared(element, virtInfo); // Update realized indices _firstRealizedElementIndexHeldByLayout = Math.Min(_firstRealizedElementIndexHeldByLayout, index); diff --git a/src/Avalonia.Controls/Repeater/ViewportManager.cs b/src/Avalonia.Controls/Repeater/ViewportManager.cs index bdb0fa3270..6e24408aa9 100644 --- a/src/Avalonia.Controls/Repeater/ViewportManager.cs +++ b/src/Avalonia.Controls/Repeater/ViewportManager.cs @@ -240,9 +240,14 @@ namespace Avalonia.Controls } } - public void OnElementPrepared(IControl element) + public void OnElementPrepared(IControl element, VirtualizationInfo virtInfo) { - _scroller?.RegisterAnchorCandidate(element); + // WinUI registers the element as an anchor candidate here, but I feel that's in error: + // at this point the element has not yet been positioned by the arrange pass so it will + // have its previous position, meaning that when the arrange pass moves it into its new + // position, an incorrect scroll anchoring will occur. Instead signal that it's not yet + // registered as a scroll anchor candidate. + virtInfo.IsRegisteredAsAnchorCandidate = false; } public void OnElementCleared(IControl element) @@ -373,6 +378,11 @@ namespace Avalonia.Controls } } + public void RegisterScrollAnchorCandidate(IControl element) + { + _scroller?.RegisterAnchorCandidate(element); + } + private IControl GetImmediateChildOfRepeater(IControl descendant) { var targetChild = descendant; diff --git a/src/Avalonia.Controls/Repeater/VirtualizationInfo.cs b/src/Avalonia.Controls/Repeater/VirtualizationInfo.cs index 7a639419c1..f8cfde609e 100644 --- a/src/Avalonia.Controls/Repeater/VirtualizationInfo.cs +++ b/src/Avalonia.Controls/Repeater/VirtualizationInfo.cs @@ -38,6 +38,7 @@ namespace Avalonia.Controls public bool IsInUniqueIdResetPool => Owner == ElementOwner.UniqueIdResetPool; public bool MustClearDataContext { get; set; } public bool KeepAlive { get; set; } + public bool IsRegisteredAsAnchorCandidate { get; set; } public ElementOwner Owner { get; private set; } = ElementOwner.ElementFactory; public string UniqueId { get; private set; } From 73ab16e68c5ae1a55738d628ac9d43d292dec3b4 Mon Sep 17 00:00:00 2001 From: Benedikt Schroeder Date: Thu, 8 Oct 2020 12:00:26 +0200 Subject: [PATCH 06/35] Fix Direct2D1 text rendering --- .../Avalonia.Direct2D1/Direct2D1Platform.cs | 34 +++- .../Media/TextShaperImpl.cs | 160 ++++++++++-------- 2 files changed, 121 insertions(+), 73 deletions(-) diff --git a/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs b/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs index ae927d44a5..9c2ca053e6 100644 --- a/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs +++ b/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs @@ -238,22 +238,44 @@ namespace Avalonia.Direct2D1 width = 0; - for (var i = 0; i < glyphCount; i++) + if (glyphRun.GlyphAdvances.IsEmpty) + { + for (var i = 0; i < glyphCount; i++) + { + var advance = glyphTypeface.GetGlyphAdvance(glyphRun.GlyphIndices[i]); + + run.Advances[i] = advance; + + width += advance; + } + } + else + { + for (var i = 0; i < glyphCount; i++) + { + var advance = (float)glyphRun.GlyphAdvances[i]; + + run.Advances[i] = advance; + + width += advance; + } + } + + if (glyphRun.GlyphOffsets.IsEmpty) { - run.Advances[i] = (float)glyphRun.GlyphAdvances[i]; - width += run.Advances[i]; + return new GlyphRunImpl(run); } run.Offsets = new GlyphOffset[glyphCount]; for (var i = 0; i < glyphCount; i++) { - var offset = glyphRun.GlyphOffsets[i]; + var (x, y) = glyphRun.GlyphOffsets[i]; run.Offsets[i] = new GlyphOffset { - AdvanceOffset = (float)offset.X, - AscenderOffset = (float)offset.Y + AdvanceOffset = (float)x, + AscenderOffset = (float)y }; } diff --git a/src/Windows/Avalonia.Direct2D1/Media/TextShaperImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/TextShaperImpl.cs index 254b5684a4..20b09a9aac 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/TextShaperImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/TextShaperImpl.cs @@ -1,6 +1,6 @@ -using System.Globalization; +using System; +using System.Globalization; using Avalonia.Media; -using Avalonia.Media.TextFormatting; using Avalonia.Media.TextFormatting.Unicode; using Avalonia.Platform; using Avalonia.Utilities; @@ -15,51 +15,9 @@ namespace Avalonia.Direct2D1.Media { using (var buffer = new Buffer()) { - buffer.ContentType = ContentType.Unicode; + FillBuffer(buffer, text); - var breakCharPosition = text.Length - 1; - - var codepoint = Codepoint.ReadAt(text, breakCharPosition, out var count); - - if (codepoint.IsBreakChar) - { - var breakCharCount = 1; - - if (text.Length > 1) - { - var previousCodepoint = Codepoint.ReadAt(text, breakCharPosition - count, out _); - - if (codepoint == '\r' && previousCodepoint == '\n' - || codepoint == '\n' && previousCodepoint == '\r') - { - breakCharCount = 2; - } - } - - if (breakCharPosition != text.Start) - { - buffer.AddUtf16(text.Buffer.Span.Slice(0, text.Length - breakCharCount)); - } - - var cluster = buffer.GlyphInfos.Length > 0 ? - buffer.GlyphInfos[buffer.Length - 1].Cluster + 1 : - (uint)text.Start; - - switch (breakCharCount) - { - case 1: - buffer.Add('\u200C', cluster); - break; - case 2: - buffer.Add('\u200C', cluster); - buffer.Add('\u200D', cluster); - break; - } - } - else - { - buffer.AddUtf16(text.Buffer.Span); - } + buffer.Language = new Language(culture ?? CultureInfo.CurrentCulture); buffer.GuessSegmentProperties(); @@ -67,44 +25,38 @@ namespace Avalonia.Direct2D1.Media var font = ((GlyphTypefaceImpl)glyphTypeface.PlatformImpl).Font; - buffer.Language = new Language(culture ?? CultureInfo.CurrentCulture); - font.Shape(buffer); font.GetScale(out var scaleX, out _); var textScale = fontRenderingEmSize / scaleX; - var len = buffer.Length; + var bufferLength = buffer.Length; - var info = buffer.GetGlyphInfoSpan(); + var glyphInfos = buffer.GetGlyphInfoSpan(); - var pos = buffer.GetGlyphPositionSpan(); + var glyphPositions = buffer.GetGlyphPositionSpan(); - var glyphIndices = new ushort[len]; + var glyphIndices = new ushort[bufferLength]; - var clusters = new ushort[len]; + var clusters = new ushort[bufferLength]; - var glyphAdvances = new double[len]; + double[] glyphAdvances = null; - var glyphOffsets = new Vector[len]; + Vector[] glyphOffsets = null; - for (var i = 0; i < len; i++) + for (var i = 0; i < bufferLength; i++) { - glyphIndices[i] = (ushort)info[i].Codepoint; - - clusters[i] = (ushort)(text.Start + info[i].Cluster); - - var advanceX = pos[i].XAdvance * textScale; - // Depends on direction of layout - //var advanceY = pos[i].YAdvance * textScale; + glyphIndices[i] = (ushort)glyphInfos[i].Codepoint; - glyphAdvances[i] = advanceX; + clusters[i] = (ushort)glyphInfos[i].Cluster; - var offsetX = pos[i].XOffset * textScale; - var offsetY = pos[i].YOffset * textScale; + if (!glyphTypeface.IsFixedPitch) + { + SetAdvance(glyphPositions, i, textScale, ref glyphAdvances); + } - glyphOffsets[i] = new Vector(offsetX, offsetY); + SetOffset(glyphPositions, i, textScale, ref glyphOffsets); } return new GlyphRun(glyphTypeface, fontRenderingEmSize, @@ -115,5 +67,79 @@ namespace Avalonia.Direct2D1.Media new ReadOnlySlice(clusters)); } } + + private static void FillBuffer(Buffer buffer, ReadOnlySlice text) + { + buffer.ContentType = ContentType.Unicode; + + var i = 0; + + while (i < text.Length) + { + var codepoint = Codepoint.ReadAt(text, i, out var count); + + var cluster = (uint)(text.Start + i); + + if (codepoint.IsBreakChar) + { + if (i + 1 < text.Length) + { + var nextCodepoint = Codepoint.ReadAt(text, i + 1, out _); + + if (nextCodepoint == '\r' && codepoint == '\n' || nextCodepoint == '\n' && codepoint == '\r') + { + count++; + + buffer.Add('\u200C', cluster); + + buffer.Add('\u200D', cluster); + } + else + { + buffer.Add('\u200C', cluster); + } + } + else + { + buffer.Add('\u200C', cluster); + } + } + else + { + buffer.Add(codepoint, cluster); + } + + i += count; + } + } + + private static void SetOffset(ReadOnlySpan glyphPositions, int index, double textScale, + ref Vector[] offsetBuffer) + { + var position = glyphPositions[index]; + + if (position.XOffset == 0 && position.YOffset == 0) + { + return; + } + + offsetBuffer ??= new Vector[glyphPositions.Length]; + + var offsetX = position.XOffset * textScale; + + var offsetY = position.YOffset * textScale; + + offsetBuffer[index] = new Vector(offsetX, offsetY); + } + + private static void SetAdvance(ReadOnlySpan glyphPositions, int index, double textScale, + ref double[] advanceBuffer) + { + advanceBuffer ??= new double[glyphPositions.Length]; + + // Depends on direction of layout + // advanceBuffer[index] = buffer.GlyphPositions[index].YAdvance * textScale; + advanceBuffer[index] = glyphPositions[index].XAdvance * textScale; + } } } From 94b6f133ea30f1fb6661403d5f46daa4ea45265d Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 12 Oct 2020 17:30:36 +0200 Subject: [PATCH 07/35] Added failing test for #4822. --- .../Shapes/PathTests.cs | 22 ++++++++++++++++++- .../MockPlatformRenderInterface.cs | 2 +- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/tests/Avalonia.Controls.UnitTests/Shapes/PathTests.cs b/tests/Avalonia.Controls.UnitTests/Shapes/PathTests.cs index 88c64e76cc..2a0bcc8d39 100644 --- a/tests/Avalonia.Controls.UnitTests/Shapes/PathTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Shapes/PathTests.cs @@ -1,4 +1,5 @@ -using Avalonia.Controls.Shapes; +using System.Runtime.InteropServices; +using Avalonia.Controls.Shapes; using Avalonia.Media; using Avalonia.UnitTests; using Xunit; @@ -34,5 +35,24 @@ namespace Avalonia.Controls.UnitTests.Shapes root.Child = null; } + + [Fact] + public void Arrange_Without_Measure_Updates_RenderedGeometry_Transform() + { + using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface); + + var target = new Path + { + Data = new RectangleGeometry { Rect = new Rect(0, 0, 100, 100) }, + Stretch = Stretch.Fill, + }; + + target.Measure(new Size(200, 200)); + target.Arrange(new Rect(0, 0, 200, 200)); + Assert.Equal(Matrix.CreateScale(2, 2), target.RenderedGeometry.Transform.Value); + + target.Arrange(new Rect(0, 0, 300, 300)); + Assert.Equal(Matrix.CreateScale(3, 3), target.RenderedGeometry.Transform.Value); + } } } diff --git a/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs b/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs index 08df23cbe1..e73a76357a 100644 --- a/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs +++ b/tests/Avalonia.UnitTests/MockPlatformRenderInterface.cs @@ -34,7 +34,7 @@ namespace Avalonia.UnitTests public IGeometryImpl CreateRectangleGeometry(Rect rect) { - return Mock.Of(); + return Mock.Of(x => x.Bounds == rect); } public IRenderTarget CreateRenderTarget(IEnumerable surfaces) From d631474ddf75f6ec5a41488aac4339fe4eb34a10 Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Mon, 12 Oct 2020 15:10:06 +0300 Subject: [PATCH 08/35] add failing test for autoscroll to selected item in listbox #4855 --- .../ListBoxTests.cs | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs b/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs index 2e2ccf7326..145fce4fed 100644 --- a/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs @@ -407,6 +407,53 @@ namespace Avalonia.Controls.UnitTests Assert.Equal(1, raised); } + [Fact] + public void Adding_And_Selecting_Item_With_AutoScrollToSelectedItem_Should_NotHide_FirstItem() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var items = new AvaloniaList(); + + var wnd = new Window() { Width = 100, Height = 100, IsVisible = true }; + + var target = new ListBox() + { + VerticalAlignment = Layout.VerticalAlignment.Top, + AutoScrollToSelectedItem = true, + Width = 50, + VirtualizationMode = ItemVirtualizationMode.Simple, + ItemTemplate = new FuncDataTemplate((c, _) => new Border() { Height = 10 }), + Items = items, + }; + wnd.Content = target; + + var lm = wnd.LayoutManager; + + lm.ExecuteInitialLayoutPass(); + + var panel = target.Presenter.Panel; + + items.Add("Item 1"); + target.Selection.Select(0); + lm.ExecuteLayoutPass(); + + Assert.Equal(1, panel.Children.Count); + + items.Add("Item 2"); + target.Selection.Select(1); + lm.ExecuteLayoutPass(); + + Assert.Equal(2, panel.Children.Count); + + //make sure we have enough space to show all items + Assert.True(panel.Bounds.Height >= panel.Children.Sum(c => c.Bounds.Height)); + + //make sure we show items and they completelly visible, not only partially + Assert.True(panel.Children[0].Bounds.Top >= 0 && panel.Children[0].Bounds.Bottom <= panel.Bounds.Height, "first item is not completelly visible!"); + Assert.True(panel.Children[1].Bounds.Top >= 0 && panel.Children[1].Bounds.Bottom <= panel.Bounds.Height, "second item is not completelly visible!"); + } + } + private FuncControlTemplate ListBoxTemplate() { return new FuncControlTemplate((parent, scope) => From b894a812d356d13da07c57ec597fd0b58ae8812c Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Mon, 12 Oct 2020 23:59:07 +0300 Subject: [PATCH 09/35] fix #4855 before try to scroll invalidate panel --- src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs b/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs index 3fac440c40..bdc68bee7e 100644 --- a/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs +++ b/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs @@ -512,6 +512,14 @@ namespace Avalonia.Controls.Presenters var generator = Owner.ItemContainerGenerator; var newOffset = -1.0; + if (!panel.IsMeasureValid && panel.PreviousMeasure.HasValue) + { + //before any kind of scrolling we need to make sure panel measure is valid + //or we risk get panel into not valid state + //we make a preemptive quick measure so scrolling is valid + panel.Measure(panel.PreviousMeasure.Value); + } + if (index >= 0 && index < ItemCount) { if (index <= FirstIndex) From f17c33c149da02afa92a810f134cd0040f244a5c Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Mon, 12 Oct 2020 23:11:26 +0200 Subject: [PATCH 10/35] Fix hittest returning top-left and top-right for non-resize corner areas. --- .../WindowImpl.CustomCaptionProc.cs | 52 ++++++++++--------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/src/Windows/Avalonia.Win32/WindowImpl.CustomCaptionProc.cs b/src/Windows/Avalonia.Win32/WindowImpl.CustomCaptionProc.cs index a3b7574369..a31357bce5 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.CustomCaptionProc.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.CustomCaptionProc.cs @@ -1,5 +1,4 @@ using System; -using System.Diagnostics; using Avalonia.Controls; using Avalonia.Input; using static Avalonia.Win32.Interop.UnmanagedMethods; @@ -11,70 +10,75 @@ namespace Avalonia.Win32 public partial class WindowImpl { // Hit test the frame for resizing and moving. - HitTestValues HitTestNCA(IntPtr hWnd, IntPtr wParam, IntPtr lParam) + private HitTestValues HitTestNCA(IntPtr hWnd, IntPtr wParam, IntPtr lParam) { - // Get the point coordinates for the hit test. + // Get the point coordinates for the hit test (screen space). var ptMouse = PointFromLParam(lParam); - // Get the window rectangle. + // Get the window rectangle. GetWindowRect(hWnd, out var rcWindow); // Get the frame rectangle, adjusted for the style without a caption. - RECT rcFrame = new RECT(); + var rcFrame = new RECT(); AdjustWindowRectEx(ref rcFrame, (uint)(WindowStyles.WS_OVERLAPPEDWINDOW & ~WindowStyles.WS_CAPTION), false, 0); - RECT border_thickness = new RECT(); + var borderThickness = new RECT(); if (GetStyle().HasFlag(WindowStyles.WS_THICKFRAME)) { - AdjustWindowRectEx(ref border_thickness, (uint)(GetStyle()), false, 0); - border_thickness.left *= -1; - border_thickness.top *= -1; + AdjustWindowRectEx(ref borderThickness, (uint)(GetStyle()), false, 0); + borderThickness.left *= -1; + borderThickness.top *= -1; } else if (GetStyle().HasFlag(WindowStyles.WS_BORDER)) { - border_thickness = new RECT { bottom = 1, left = 1, right = 1, top = 1 }; + borderThickness = new RECT { bottom = 1, left = 1, right = 1, top = 1 }; } if (_extendTitleBarHint >= 0) { - border_thickness.top = (int)(_extendedMargins.Top * RenderScaling); + borderThickness.top = (int)(_extendedMargins.Top * RenderScaling); } // Determine if the hit test is for resizing. Default middle (1,1). ushort uRow = 1; ushort uCol = 1; - bool fOnResizeBorder = false; + bool onResizeBorder = false; // Determine if the point is at the top or bottom of the window. - if (ptMouse.Y >= rcWindow.top && ptMouse.Y < rcWindow.top + border_thickness.top) + if (ptMouse.Y >= rcWindow.top && ptMouse.Y < rcWindow.top + borderThickness.top) { - fOnResizeBorder = (ptMouse.Y < (rcWindow.top - rcFrame.top)); - uRow = 0; + onResizeBorder = (ptMouse.Y < (rcWindow.top - rcFrame.top)); + + if (onResizeBorder) + { + uRow = 0; + } } - else if (ptMouse.Y < rcWindow.bottom && ptMouse.Y >= rcWindow.bottom - border_thickness.bottom) + else if (ptMouse.Y < rcWindow.bottom && ptMouse.Y >= rcWindow.bottom - borderThickness.bottom) { uRow = 2; } // Determine if the point is at the left or right of the window. - if (ptMouse.X >= rcWindow.left && ptMouse.X < rcWindow.left + border_thickness.left) + if (ptMouse.X >= rcWindow.left && ptMouse.X < rcWindow.left + borderThickness.left) { uCol = 0; // left side } - else if (ptMouse.X < rcWindow.right && ptMouse.X >= rcWindow.right - border_thickness.right) + else if (ptMouse.X < rcWindow.right && ptMouse.X >= rcWindow.right - borderThickness.right) { uCol = 2; // right side } - // Hit test (HTTOPLEFT, ... HTBOTTOMRIGHT) - HitTestValues[][] hitTests = new[] + ReadOnlySpan hitZones = stackalloc HitTestValues[] { - new []{ HitTestValues.HTTOPLEFT, fOnResizeBorder ? HitTestValues.HTTOP : HitTestValues.HTCAPTION, HitTestValues.HTTOPRIGHT }, - new []{ HitTestValues.HTLEFT, HitTestValues.HTNOWHERE, HitTestValues.HTRIGHT }, - new []{ HitTestValues.HTBOTTOMLEFT, HitTestValues.HTBOTTOM, HitTestValues.HTBOTTOMRIGHT }, + HitTestValues.HTTOPLEFT, onResizeBorder ? HitTestValues.HTTOP : HitTestValues.HTCAPTION, + HitTestValues.HTTOPRIGHT, HitTestValues.HTLEFT, HitTestValues.HTNOWHERE, HitTestValues.HTRIGHT, + HitTestValues.HTBOTTOMLEFT, HitTestValues.HTBOTTOM, HitTestValues.HTBOTTOMRIGHT }; - return hitTests[uRow][uCol]; + var zoneIndex = uRow * 3 + uCol; + + return hitZones[zoneIndex]; } protected virtual IntPtr CustomCaptionProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam, ref bool callDwp) From ba8b49d918748ae9ce91eb58bc5e25d3187cb19b Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Mon, 12 Oct 2020 23:22:28 +0200 Subject: [PATCH 11/35] Fix caption hit test. --- .../WindowImpl.CustomCaptionProc.cs | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/Windows/Avalonia.Win32/WindowImpl.CustomCaptionProc.cs b/src/Windows/Avalonia.Win32/WindowImpl.CustomCaptionProc.cs index a31357bce5..68bd40da79 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.CustomCaptionProc.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.CustomCaptionProc.cs @@ -44,12 +44,25 @@ namespace Avalonia.Win32 ushort uCol = 1; bool onResizeBorder = false; + // Determine if the point is at the left or right of the window. + if (ptMouse.X >= rcWindow.left && ptMouse.X < rcWindow.left + borderThickness.left) + { + uCol = 0; // left side + } + else if (ptMouse.X < rcWindow.right && ptMouse.X >= rcWindow.right - borderThickness.right) + { + uCol = 2; // right side + } + // Determine if the point is at the top or bottom of the window. if (ptMouse.Y >= rcWindow.top && ptMouse.Y < rcWindow.top + borderThickness.top) { onResizeBorder = (ptMouse.Y < (rcWindow.top - rcFrame.top)); - if (onResizeBorder) + // Two cases where we have a valid row 0 hit test: + // - window resize border (top resize border hit) + // - area below resize border that is actual titlebar (caption hit). + if (onResizeBorder || uCol == 1) { uRow = 0; } @@ -59,16 +72,6 @@ namespace Avalonia.Win32 uRow = 2; } - // Determine if the point is at the left or right of the window. - if (ptMouse.X >= rcWindow.left && ptMouse.X < rcWindow.left + borderThickness.left) - { - uCol = 0; // left side - } - else if (ptMouse.X < rcWindow.right && ptMouse.X >= rcWindow.right - borderThickness.right) - { - uCol = 2; // right side - } - ReadOnlySpan hitZones = stackalloc HitTestValues[] { HitTestValues.HTTOPLEFT, onResizeBorder ? HitTestValues.HTTOP : HitTestValues.HTCAPTION, From 93f215d8eaca8fa1dd9f04fb562055f338afae02 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 13 Oct 2020 16:47:50 +0200 Subject: [PATCH 12/35] More tests for #4822. Tested WPF's `Path` to confirm expected results: https://github.com/wieslawsoltes/WpfUnitTests --- .../Shapes/PathTests.cs | 108 +++++++++++++++++- 1 file changed, 106 insertions(+), 2 deletions(-) diff --git a/tests/Avalonia.Controls.UnitTests/Shapes/PathTests.cs b/tests/Avalonia.Controls.UnitTests/Shapes/PathTests.cs index 2a0bcc8d39..a10a03a5ae 100644 --- a/tests/Avalonia.Controls.UnitTests/Shapes/PathTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Shapes/PathTests.cs @@ -1,5 +1,4 @@ -using System.Runtime.InteropServices; -using Avalonia.Controls.Shapes; +using Avalonia.Controls.Shapes; using Avalonia.Media; using Avalonia.UnitTests; using Xunit; @@ -36,6 +35,111 @@ namespace Avalonia.Controls.UnitTests.Shapes root.Child = null; } + [Theory] + [InlineData(Stretch.None, 100, 200)] + [InlineData(Stretch.Fill, 500, 500)] + [InlineData(Stretch.Uniform, 250, 500)] + [InlineData(Stretch.UniformToFill, 500, 500)] + public void Calculates_Correct_DesiredSize_For_Finite_Bounds(Stretch stretch, double expectedWidth, double expectedHeight) + { + using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface); + + var target = new Path() + { + Data = new RectangleGeometry { Rect = new Rect(0, 0, 100, 200) }, + Stretch = stretch, + }; + + target.Measure(new Size(500, 500)); + + Assert.Equal(new Size(expectedWidth, expectedHeight), target.DesiredSize); + } + + [Theory] + [InlineData(Stretch.None)] + [InlineData(Stretch.Fill)] + [InlineData(Stretch.Uniform)] + [InlineData(Stretch.UniformToFill)] + public void Calculates_Correct_DesiredSize_For_Infinite_Bounds(Stretch stretch) + { + using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface); + + var target = new Path() + { + Data = new RectangleGeometry { Rect = new Rect(0, 0, 100, 200) }, + Stretch = stretch, + }; + + target.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); + + Assert.Equal(new Size(100, 200), target.DesiredSize); + } + + [Fact] + public void Measure_Does_Not_Update_RenderedGeometry_Transform() + { + using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface); + + var target = new Path + { + Data = new RectangleGeometry { Rect = new Rect(0, 0, 100, 200) }, + Stretch = Stretch.Fill, + }; + + target.Measure(new Size(500, 500)); + + Assert.Null(target.RenderedGeometry.Transform); + } + + [Theory] + [InlineData(Stretch.None, 1, 1)] + [InlineData(Stretch.Fill, 5, 2.5)] + [InlineData(Stretch.Uniform, 2.5, 2.5)] + [InlineData(Stretch.UniformToFill, 5, 5)] + public void Arrange_Updates_RenderedGeometry_Transform(Stretch stretch, double expectedScaleX, double expectedScaleY) + { + using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface); + + var target = new Path + { + Data = new RectangleGeometry { Rect = new Rect(0, 0, 100, 200) }, + Stretch = stretch, + }; + + target.Measure(new Size(500, 500)); + target.Arrange(new Rect(0, 0, 500, 500)); + + if (expectedScaleX == 1 && expectedScaleY == 1) + { + Assert.Null(target.RenderedGeometry.Transform); + } + else + { + Assert.Equal(Matrix.CreateScale(expectedScaleX, expectedScaleY), target.RenderedGeometry.Transform.Value); + } + } + + [Fact] + public void Measure_Without_Arrange_Does_Not_Clear_RenderedGeometry_Transform() + { + using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface); + + var target = new Path + { + Data = new RectangleGeometry { Rect = new Rect(0, 0, 100, 100) }, + Stretch = Stretch.Fill, + }; + + target.Measure(new Size(200, 200)); + target.Arrange(new Rect(0, 0, 200, 200)); + + Assert.Equal(Matrix.CreateScale(2, 2), target.RenderedGeometry.Transform.Value); + + target.Measure(new Size(300, 300)); + + Assert.Equal(Matrix.CreateScale(2, 2), target.RenderedGeometry.Transform.Value); + } + [Fact] public void Arrange_Without_Measure_Updates_RenderedGeometry_Transform() { From beada97aabaf46f44564f4bcdcfe2100c1421ddc Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 13 Oct 2020 16:48:06 +0200 Subject: [PATCH 13/35] Set RenderedGeometry.Transform on arrange. Fixes #4822 but breaks a bunch of other tests. --- src/Avalonia.Controls/Shapes/Shape.cs | 39 +++------------------------ 1 file changed, 4 insertions(+), 35 deletions(-) diff --git a/src/Avalonia.Controls/Shapes/Shape.cs b/src/Avalonia.Controls/Shapes/Shape.cs index 7d1525afc4..67a7d48c61 100644 --- a/src/Avalonia.Controls/Shapes/Shape.cs +++ b/src/Avalonia.Controls/Shapes/Shape.cs @@ -248,52 +248,21 @@ namespace Avalonia.Controls.Shapes protected override Size MeasureOverride(Size availableSize) { - bool deferCalculateTransform; - switch (Stretch) + if (DefiningGeometry is null) { - case Stretch.Fill: - case Stretch.UniformToFill: - deferCalculateTransform = double.IsInfinity(availableSize.Width) || double.IsInfinity(availableSize.Height); - break; - case Stretch.Uniform: - deferCalculateTransform = double.IsInfinity(availableSize.Width) && double.IsInfinity(availableSize.Height); - break; - case Stretch.None: - default: - deferCalculateTransform = false; - break; + return default; } - if (deferCalculateTransform) - { - _calculateTransformOnArrange = true; - return DefiningGeometry?.Bounds.Size ?? Size.Empty; - } - else - { - _calculateTransformOnArrange = false; - return CalculateShapeSizeAndSetTransform(availableSize); - } + return CalculateSizeAndTransform(availableSize, DefiningGeometry.Bounds, Stretch).Item1; } protected override Size ArrangeOverride(Size finalSize) - { - if (_calculateTransformOnArrange) - { - _calculateTransformOnArrange = false; - CalculateShapeSizeAndSetTransform(finalSize); - } - - return finalSize; - } - - private Size CalculateShapeSizeAndSetTransform(Size availableSize) { if (DefiningGeometry != null) { // This should probably use GetRenderBounds(strokeThickness) but then the calculations // will multiply the stroke thickness as well, which isn't correct. - var (size, transform) = CalculateSizeAndTransform(availableSize, DefiningGeometry.Bounds, Stretch); + var (size, transform) = CalculateSizeAndTransform(finalSize, DefiningGeometry.Bounds, Stretch); if (_transform != transform) { From 51437baf7301e9313f6727c4075cd0014b21bd10 Mon Sep 17 00:00:00 2001 From: Benedikt Schroeder Date: Tue, 13 Oct 2020 17:31:35 +0200 Subject: [PATCH 14/35] Properly scale fixed pitch fonts --- src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs b/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs index 9c2ca053e6..6ae27870e8 100644 --- a/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs +++ b/src/Windows/Avalonia.Direct2D1/Direct2D1Platform.cs @@ -238,11 +238,13 @@ namespace Avalonia.Direct2D1 width = 0; + var scale = (float)(glyphRun.FontRenderingEmSize / glyphTypeface.DesignEmHeight); + if (glyphRun.GlyphAdvances.IsEmpty) { for (var i = 0; i < glyphCount; i++) { - var advance = glyphTypeface.GetGlyphAdvance(glyphRun.GlyphIndices[i]); + var advance = glyphTypeface.GetGlyphAdvance(glyphRun.GlyphIndices[i]) * scale; run.Advances[i] = advance; From 9f8f4b224f188d2cf48d9a613291b3dbb51dcc04 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 13 Oct 2020 17:42:42 +0200 Subject: [PATCH 15/35] Provide a RenderInterface for controls which use shapes. --- tests/Avalonia.Controls.UnitTests/CanvasTests.cs | 9 +++++++++ .../LayoutTransformControlTests.cs | 15 +++++++++++++++ .../RelativePanelTests.cs | 6 ++++++ tests/Avalonia.Controls.UnitTests/ViewboxTests.cs | 13 +++++++++++++ 4 files changed, 43 insertions(+) diff --git a/tests/Avalonia.Controls.UnitTests/CanvasTests.cs b/tests/Avalonia.Controls.UnitTests/CanvasTests.cs index da1698330f..11a349f53e 100644 --- a/tests/Avalonia.Controls.UnitTests/CanvasTests.cs +++ b/tests/Avalonia.Controls.UnitTests/CanvasTests.cs @@ -1,5 +1,6 @@ using System; using Avalonia.Controls.Shapes; +using Avalonia.UnitTests; using Xunit; namespace Avalonia.Controls.UnitTests @@ -9,6 +10,8 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Left_Property_Should_Work() { + using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface); + Rectangle rect; var target = new Canvas { @@ -34,6 +37,8 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Top_Property_Should_Work() { + using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface); + Rectangle rect; var target = new Canvas { @@ -59,6 +64,8 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Right_Property_Should_Work() { + using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface); + Rectangle rect; var target = new Canvas { @@ -84,6 +91,8 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Bottom_Property_Should_Work() { + using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface); + Rectangle rect; var target = new Canvas { diff --git a/tests/Avalonia.Controls.UnitTests/LayoutTransformControlTests.cs b/tests/Avalonia.Controls.UnitTests/LayoutTransformControlTests.cs index 13c946b549..60139c2881 100644 --- a/tests/Avalonia.Controls.UnitTests/LayoutTransformControlTests.cs +++ b/tests/Avalonia.Controls.UnitTests/LayoutTransformControlTests.cs @@ -1,5 +1,6 @@ using Avalonia.Controls.Shapes; using Avalonia.Media; +using Avalonia.UnitTests; using Xunit; namespace Avalonia.Controls.UnitTests @@ -171,6 +172,8 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Should_Generate_RotateTransform_90_degrees() { + using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface); + LayoutTransformControl lt = CreateWithChildAndMeasureAndTransform( 100, 25, @@ -193,6 +196,8 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Should_Generate_RotateTransform_minus_90_degrees() { + using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface); + LayoutTransformControl lt = CreateWithChildAndMeasureAndTransform( 100, 25, @@ -215,6 +220,8 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Should_Generate_ScaleTransform_x2() { + using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface); + LayoutTransformControl lt = CreateWithChildAndMeasureAndTransform( 100, 50, @@ -236,6 +243,8 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Should_Generate_SkewTransform_45_degrees() { + using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface); + LayoutTransformControl lt = CreateWithChildAndMeasureAndTransform( 100, 100, @@ -258,6 +267,8 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Should_Generate_SkewTransform_minus_45_degrees() { + using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface); + LayoutTransformControl lt = CreateWithChildAndMeasureAndTransform( 100, 100, @@ -279,6 +290,8 @@ namespace Avalonia.Controls.UnitTests private static void TransformMeasureSizeTest(Size size, Transform transform, Size expectedSize) { + using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface); + LayoutTransformControl lt = CreateWithChildAndMeasureAndTransform( size.Width, size.Height, @@ -292,6 +305,8 @@ namespace Avalonia.Controls.UnitTests private static void TransformRootBoundsTest(Size size, Transform transform, Rect expectedBounds) { + using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface); + LayoutTransformControl lt = CreateWithChildAndMeasureAndTransform(size.Width, size.Height, transform); Rect outBounds = lt.TransformRoot.Bounds; diff --git a/tests/Avalonia.Controls.UnitTests/RelativePanelTests.cs b/tests/Avalonia.Controls.UnitTests/RelativePanelTests.cs index 6e171a58e7..6b2f05c923 100644 --- a/tests/Avalonia.Controls.UnitTests/RelativePanelTests.cs +++ b/tests/Avalonia.Controls.UnitTests/RelativePanelTests.cs @@ -1,4 +1,5 @@ using Avalonia.Controls.Shapes; +using Avalonia.UnitTests; using Xunit; namespace Avalonia.Controls.UnitTests @@ -8,6 +9,7 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Lays_Out_1_Child_Next_the_other() { + using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface); var rect1 = new Rectangle { Height = 20, Width = 20 }; var rect2 = new Rectangle { Height = 20, Width = 20 }; @@ -34,6 +36,7 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Lays_Out_1_Child_Below_the_other() { + using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface); var rect1 = new Rectangle { Height = 20, Width = 20 }; var rect2 = new Rectangle { Height = 20, Width = 20 }; @@ -60,6 +63,7 @@ namespace Avalonia.Controls.UnitTests [Fact] public void RelativePanel_Can_Center() { + using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface); var rect1 = new Rectangle { Height = 20, Width = 20 }; var rect2 = new Rectangle { Height = 20, Width = 20 }; @@ -86,6 +90,7 @@ namespace Avalonia.Controls.UnitTests [Fact] public void LeftOf_Measures_Correctly() { + using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface); var rect1 = new Rectangle { Height = 20, Width = 20 }; var rect2 = new Rectangle { Height = 20, Width = 20 }; @@ -111,6 +116,7 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Above_Measures_Correctly() { + using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface); var rect1 = new Rectangle { Height = 20, Width = 20 }; var rect2 = new Rectangle { Height = 20, Width = 20 }; diff --git a/tests/Avalonia.Controls.UnitTests/ViewboxTests.cs b/tests/Avalonia.Controls.UnitTests/ViewboxTests.cs index ad0f318d2f..e005bafbf9 100644 --- a/tests/Avalonia.Controls.UnitTests/ViewboxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ViewboxTests.cs @@ -1,5 +1,6 @@ using Avalonia.Controls.Shapes; using Avalonia.Media; +using Avalonia.UnitTests; using Xunit; namespace Avalonia.Controls.UnitTests @@ -9,6 +10,8 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Viewbox_Stretch_Uniform_Child() { + using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface); + var target = new Viewbox() { Child = new Rectangle() { Width = 100, Height = 50 } }; target.Measure(new Size(200, 200)); @@ -25,6 +28,8 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Viewbox_Stretch_None_Child() { + using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface); + var target = new Viewbox() { Stretch = Stretch.None, Child = new Rectangle() { Width = 100, Height = 50 } }; target.Measure(new Size(200, 200)); @@ -41,6 +46,8 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Viewbox_Stretch_Fill_Child() { + using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface); + var target = new Viewbox() { Stretch = Stretch.Fill, Child = new Rectangle() { Width = 100, Height = 50 } }; target.Measure(new Size(200, 200)); @@ -57,6 +64,8 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Viewbox_Stretch_UniformToFill_Child() { + using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface); + var target = new Viewbox() { Stretch = Stretch.UniformToFill, Child = new Rectangle() { Width = 100, Height = 50 } }; target.Measure(new Size(200, 200)); @@ -73,6 +82,8 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Viewbox_Stretch_Uniform_Child_With_Unrestricted_Width() { + using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface); + var target = new Viewbox() { Child = new Rectangle() { Width = 100, Height = 50 } }; target.Measure(new Size(double.PositiveInfinity, 200)); @@ -89,6 +100,8 @@ namespace Avalonia.Controls.UnitTests [Fact] public void Viewbox_Stretch_Uniform_Child_With_Unrestricted_Height() { + using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface); + var target = new Viewbox() { Child = new Rectangle() { Width = 100, Height = 50 } }; target.Measure(new Size(200, double.PositiveInfinity)); From 10b6cfc0117427bda643b4ba9716df7dfa52e495 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 13 Oct 2020 17:43:24 +0200 Subject: [PATCH 16/35] Reserve all of arrange rect. --- src/Avalonia.Controls/Shapes/Shape.cs | 3 +-- .../Shapes/PathTests.cs | 20 +++++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/Shapes/Shape.cs b/src/Avalonia.Controls/Shapes/Shape.cs index 67a7d48c61..28206a398a 100644 --- a/src/Avalonia.Controls/Shapes/Shape.cs +++ b/src/Avalonia.Controls/Shapes/Shape.cs @@ -62,7 +62,6 @@ namespace Avalonia.Controls.Shapes private Matrix _transform = Matrix.Identity; private Geometry? _definingGeometry; private Geometry? _renderedGeometry; - private bool _calculateTransformOnArrange; static Shape() { @@ -270,7 +269,7 @@ namespace Avalonia.Controls.Shapes _renderedGeometry = null; } - return size; + return finalSize; } return Size.Empty; diff --git a/tests/Avalonia.Controls.UnitTests/Shapes/PathTests.cs b/tests/Avalonia.Controls.UnitTests/Shapes/PathTests.cs index a10a03a5ae..8b8656a76b 100644 --- a/tests/Avalonia.Controls.UnitTests/Shapes/PathTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Shapes/PathTests.cs @@ -119,6 +119,26 @@ namespace Avalonia.Controls.UnitTests.Shapes } } + [Fact] + public void Arrange_Reserves_All_Of_Arrange_Rect() + { + using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface); + + RectangleGeometry geometry; + var target = new Path + { + Data = geometry = new RectangleGeometry { Rect = new Rect(0, 0, 100, 200) }, + Stretch = Stretch.Uniform, + }; + + target.Measure(new Size(400, 400)); + target.Arrange(new Rect(0, 0, 400, 400)); + + Assert.Equal(new Rect(0, 0, 100, 200), geometry.Rect); + Assert.Equal(Matrix.CreateScale(2, 2), target.RenderedGeometry.Transform.Value); + Assert.Equal(new Rect(0, 0, 400, 400), target.Bounds); + } + [Fact] public void Measure_Without_Arrange_Does_Not_Clear_RenderedGeometry_Transform() { From 67215a85904eb250e979a905a3728c0fffff2a8c Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 13 Oct 2020 18:07:14 +0200 Subject: [PATCH 17/35] Removed outdated tests. --- .../ShapeLayoutTests.cs | 113 ------------------ 1 file changed, 113 deletions(-) delete mode 100644 tests/Avalonia.Layout.UnitTests/ShapeLayoutTests.cs diff --git a/tests/Avalonia.Layout.UnitTests/ShapeLayoutTests.cs b/tests/Avalonia.Layout.UnitTests/ShapeLayoutTests.cs deleted file mode 100644 index 2ccd6fb04c..0000000000 --- a/tests/Avalonia.Layout.UnitTests/ShapeLayoutTests.cs +++ /dev/null @@ -1,113 +0,0 @@ -using System.Collections.Generic; -using Avalonia.Controls; -using Avalonia.Controls.Shapes; -using Avalonia.Media; -using Avalonia.Platform; -using Avalonia.UnitTests; -using Xunit; - -namespace Avalonia.Layout.UnitTests - -{ - public class ShapeLayoutTests : TestWithServicesBase - { - - public ShapeLayoutTests() - { - AvaloniaLocator.CurrentMutable - .Bind().ToSingleton(); - } - - [Fact] - public void Shape_Transformation_Calculation_Should_Be_Deferred_To_Arrange_When_Strech_Is_Fill_And_Aviable_Size_Is_Infinite() - { - var shape = new Polygon() - { - Points = new List - { - new Point(0, 0), - new Point(10, 5), - new Point(0, 10) - }, - Stretch = Stretch.Fill - }; - - var availableSize = new Size(double.PositiveInfinity, double.PositiveInfinity); - shape.Measure(availableSize); - Geometry postMeasureGeometry = shape.RenderedGeometry; - Transform postMeasureTransform = postMeasureGeometry.Transform; - - var finalSize = new Size(100, 50); - var finalRect = new Rect(finalSize); - shape.Arrange(finalRect); - - Geometry postArrangeGeometry = shape.RenderedGeometry; - Transform postArrangeTransform = postArrangeGeometry.Transform; - - Assert.NotEqual(postMeasureGeometry, postArrangeGeometry); - Assert.NotEqual(postMeasureTransform, postArrangeTransform); - Assert.Equal(finalSize, shape.Bounds.Size); - } - - [Fact] - public void Shape_Transformation_Calculation_Should_Not_Be_Deferred_To_Arrange_When_Strech_Is_Fill_And_Aviable_Size_Is_Finite() - { - var shape = new Polygon() - { - Points = new List - { - new Point(0, 0), - new Point(10, 5), - new Point(0, 10) - }, - Stretch = Stretch.Fill - }; - - var availableSize = new Size(100, 50); - shape.Measure(availableSize); - Geometry postMeasureGeometry = shape.RenderedGeometry; - Transform postMeasureTransform = postMeasureGeometry.Transform; - - var finalRect = new Rect(availableSize); - shape.Arrange(finalRect); - - Geometry postArrangeGeometry = shape.RenderedGeometry; - Transform postArrangeTransform = postArrangeGeometry.Transform; - - Assert.Equal(postMeasureGeometry, postArrangeGeometry); - Assert.Equal(postMeasureTransform, postArrangeTransform); - Assert.Equal(availableSize, shape.Bounds.Size); - } - - [Fact] - public void Shape_Transformation_Calculation_Should_Not_Be_Deferred_To_Arrange_When_Strech_Is_None() - { - var shape = new Polygon() - { - Points = new List - { - new Point(0, 0), - new Point(10, 5), - new Point(0, 10) - }, - Stretch = Stretch.None - }; - - var availableSize = new Size(double.PositiveInfinity, double.PositiveInfinity); - shape.Measure(availableSize); - Geometry postMeasureGeometry = shape.RenderedGeometry; - Transform postMeasureTransform = postMeasureGeometry.Transform; - - var finalSize = new Size(100, 50); - var finalRect = new Rect(finalSize); - shape.Arrange(finalRect); - - Geometry postArrangeGeometry = shape.RenderedGeometry; - Transform postArrangeTransform = postArrangeGeometry.Transform; - - Assert.Equal(postMeasureGeometry, postArrangeGeometry); - Assert.Equal(postMeasureTransform, postArrangeTransform); - Assert.Equal(finalSize, shape.Bounds.Size); - } - } -} From 41050b67422be08b8b15700f3e99fd5879acb58a Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 13 Oct 2020 18:26:34 +0200 Subject: [PATCH 18/35] Added some tests for Rectangle and Ellipse. Because these classes override the base `Shape` layout. --- .../Shapes/EllipseTests.cs | 57 +++++++++++++++++++ .../Shapes/RectangleTests.cs | 56 ++++++++++++++++-- 2 files changed, 107 insertions(+), 6 deletions(-) create mode 100644 tests/Avalonia.Controls.UnitTests/Shapes/EllipseTests.cs diff --git a/tests/Avalonia.Controls.UnitTests/Shapes/EllipseTests.cs b/tests/Avalonia.Controls.UnitTests/Shapes/EllipseTests.cs new file mode 100644 index 0000000000..626945894f --- /dev/null +++ b/tests/Avalonia.Controls.UnitTests/Shapes/EllipseTests.cs @@ -0,0 +1,57 @@ +using Avalonia.Controls.Shapes; +using Avalonia.Media; +using Avalonia.UnitTests; +using Xunit; + +namespace Avalonia.Controls.UnitTests.Shapes +{ + public class EllipseTests + { + [Fact] + public void Measure_Does_Not_Set_RenderedGeometry_Rect() + { + using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface); + + var target = new Ellipse(); + + target.Measure(new Size(100, 100)); + + var geometry = Assert.IsType(target.RenderedGeometry); + Assert.Equal(default, geometry.Rect); + } + + [Fact] + public void Arrange_Sets_RenderedGeometry_Properties() + { + using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface); + + var target = new Ellipse(); + + target.Measure(new Size(100, 100)); + target.Arrange(new Rect(0, 0, 100, 100)); + + var geometry = Assert.IsType(target.RenderedGeometry); + Assert.Equal(new Rect(0, 0, 100, 100), geometry.Rect); + } + + [Fact] + public void Rearranging_Updates_RenderedGeometry_Rect() + { + using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface); + + var target = new Ellipse(); + + target.Measure(new Size(100, 100)); + target.Arrange(new Rect(0, 0, 100, 100)); + + var geometry = Assert.IsType(target.RenderedGeometry); + Assert.Equal(new Rect(0, 0, 100, 100), geometry.Rect); + + target.Measure(new Size(200, 200)); + target.Arrange(new Rect(0, 0, 200, 200)); + + geometry = Assert.IsType(target.RenderedGeometry); + Assert.Equal(new Rect(0, 0, 200, 200), geometry.Rect); + } + } +} diff --git a/tests/Avalonia.Controls.UnitTests/Shapes/RectangleTests.cs b/tests/Avalonia.Controls.UnitTests/Shapes/RectangleTests.cs index 0ec73edec0..8d8ce10d4c 100644 --- a/tests/Avalonia.Controls.UnitTests/Shapes/RectangleTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Shapes/RectangleTests.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; -using Avalonia.Controls.Shapes; +using Avalonia.Controls.Shapes; using Avalonia.Media; using Avalonia.UnitTests; using Moq; @@ -11,6 +8,53 @@ namespace Avalonia.Controls.UnitTests.Shapes { public class RectangleTests { + [Fact] + public void Measure_Does_Not_Set_RenderedGeometry_Rect() + { + using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface); + + var target = new Rectangle(); + + target.Measure(new Size(100, 100)); + + var geometry = Assert.IsType(target.RenderedGeometry); + Assert.Equal(Rect.Empty, geometry.Rect); + } + + [Fact] + public void Arrange_Sets_RenderedGeometry_Rect() + { + using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface); + + var target = new Rectangle(); + + target.Measure(new Size(100, 100)); + target.Arrange(new Rect(0, 0, 100, 100)); + + var geometry = Assert.IsType(target.RenderedGeometry); + Assert.Equal(new Rect(0, 0, 100, 100), geometry.Rect); + } + + [Fact] + public void Rearranging_Updates_RenderedGeometry_Rect() + { + using var app = UnitTestApplication.Start(TestServices.MockPlatformRenderInterface); + + var target = new Rectangle(); + + target.Measure(new Size(100, 100)); + target.Arrange(new Rect(0, 0, 100, 100)); + + var geometry = Assert.IsType(target.RenderedGeometry); + Assert.Equal(new Rect(0, 0, 100, 100), geometry.Rect); + + target.Measure(new Size(200, 200)); + target.Arrange(new Rect(0, 0, 200, 200)); + + geometry = Assert.IsType(target.RenderedGeometry); + Assert.Equal(new Rect(0, 0, 200, 200), geometry.Rect); + } + [Fact] public void Changing_Fill_Brush_Color_Should_Invalidate_Visual() { @@ -21,7 +65,7 @@ namespace Avalonia.Controls.UnitTests.Shapes var root = new TestRoot(target); var renderer = Mock.Get(root.Renderer); - renderer.ResetCalls(); + renderer.Invocations.Clear(); ((SolidColorBrush)target.Fill).Color = Colors.Green; @@ -38,7 +82,7 @@ namespace Avalonia.Controls.UnitTests.Shapes var root = new TestRoot(target); var renderer = Mock.Get(root.Renderer); - renderer.ResetCalls(); + renderer.Invocations.Clear(); ((SolidColorBrush)target.Stroke).Color = Colors.Green; From 374cdd1549562e4e2e47c5950c6cb9589d567e41 Mon Sep 17 00:00:00 2001 From: Benedikt Schroeder Date: Wed, 14 Oct 2020 07:15:00 +0200 Subject: [PATCH 19/35] Use Skia's font metrics --- src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs | 44 ++++++++------------- 1 file changed, 17 insertions(+), 27 deletions(-) diff --git a/src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs b/src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs index 5571bd890d..c649f3d8bf 100644 --- a/src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs +++ b/src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs @@ -1,6 +1,5 @@ using System; using System.Runtime.InteropServices; -using Avalonia.Media; using Avalonia.Platform; using HarfBuzzSharp; using SkiaSharp; @@ -24,40 +23,31 @@ namespace Avalonia.Skia Font.SetFunctionsOpenType(); - Font.GetScale(out var xScale, out _); + DesignEmHeight = (short)Typeface.UnitsPerEm; - DesignEmHeight = (short)xScale; + var metrics = Typeface.ToFont().Metrics; - if (!Font.TryGetHorizontalFontExtents(out var fontExtents)) - { - Font.TryGetVerticalFontExtents(out fontExtents); - } + Ascent = (int)(metrics.Ascent / 12.0 * Typeface.UnitsPerEm); - Ascent = -fontExtents.Ascender; + Descent = (int)(metrics.Descent / 12.0 * Typeface.UnitsPerEm); - Descent = -fontExtents.Descender; + LineGap = (int)(metrics.Leading / 12.0 * Typeface.UnitsPerEm); - LineGap = fontExtents.LineGap; + UnderlinePosition = metrics.UnderlinePosition != null ? + (int)(metrics.UnderlinePosition / 12.0 * Typeface.UnitsPerEm) : + 0; - if (Font.OpenTypeMetrics.TryGetPosition(OpenTypeMetricsTag.UnderlineOffset, out var underlinePosition)) - { - UnderlinePosition = underlinePosition; - } + UnderlineThickness = metrics.UnderlineThickness != null ? + (int)(metrics.UnderlineThickness / 12.0 * Typeface.UnitsPerEm) : + 0; - if (Font.OpenTypeMetrics.TryGetPosition(OpenTypeMetricsTag.UnderlineSize, out var underlineThickness)) - { - UnderlineThickness = underlineThickness; - } + StrikethroughPosition = metrics.StrikeoutPosition != null ? + (int)(metrics.StrikeoutPosition / 12.0 * Typeface.UnitsPerEm) : + 0; - if (Font.OpenTypeMetrics.TryGetPosition(OpenTypeMetricsTag.StrikeoutOffset, out var strikethroughPosition)) - { - StrikethroughPosition = strikethroughPosition; - } - - if (Font.OpenTypeMetrics.TryGetPosition(OpenTypeMetricsTag.StrikeoutSize, out var strikethroughThickness)) - { - StrikethroughThickness = strikethroughThickness; - } + StrikethroughThickness = metrics.StrikeoutThickness != null ? + (int)(metrics.StrikeoutThickness / 12.0 * Typeface.UnitsPerEm) : + 0; IsFixedPitch = Typeface.IsFixedPitch; } From 7cb133a33a41a8073a840fe538bfcceafb676604 Mon Sep 17 00:00:00 2001 From: Benedikt Schroeder Date: Wed, 14 Oct 2020 11:50:42 +0200 Subject: [PATCH 20/35] Introduce defaultFontRenderingEmSize --- src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs b/src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs index c649f3d8bf..ceccc481f9 100644 --- a/src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs +++ b/src/Skia/Avalonia.Skia/GlyphTypefaceImpl.cs @@ -27,26 +27,28 @@ namespace Avalonia.Skia var metrics = Typeface.ToFont().Metrics; - Ascent = (int)(metrics.Ascent / 12.0 * Typeface.UnitsPerEm); + const double defaultFontRenderingEmSize = 12.0; - Descent = (int)(metrics.Descent / 12.0 * Typeface.UnitsPerEm); + Ascent = (int)(metrics.Ascent / defaultFontRenderingEmSize * Typeface.UnitsPerEm); - LineGap = (int)(metrics.Leading / 12.0 * Typeface.UnitsPerEm); + Descent = (int)(metrics.Descent / defaultFontRenderingEmSize * Typeface.UnitsPerEm); + + LineGap = (int)(metrics.Leading / defaultFontRenderingEmSize * Typeface.UnitsPerEm); UnderlinePosition = metrics.UnderlinePosition != null ? - (int)(metrics.UnderlinePosition / 12.0 * Typeface.UnitsPerEm) : + (int)(metrics.UnderlinePosition / defaultFontRenderingEmSize * Typeface.UnitsPerEm) : 0; UnderlineThickness = metrics.UnderlineThickness != null ? - (int)(metrics.UnderlineThickness / 12.0 * Typeface.UnitsPerEm) : + (int)(metrics.UnderlineThickness / defaultFontRenderingEmSize * Typeface.UnitsPerEm) : 0; StrikethroughPosition = metrics.StrikeoutPosition != null ? - (int)(metrics.StrikeoutPosition / 12.0 * Typeface.UnitsPerEm) : + (int)(metrics.StrikeoutPosition / defaultFontRenderingEmSize * Typeface.UnitsPerEm) : 0; StrikethroughThickness = metrics.StrikeoutThickness != null ? - (int)(metrics.StrikeoutThickness / 12.0 * Typeface.UnitsPerEm) : + (int)(metrics.StrikeoutThickness / defaultFontRenderingEmSize * Typeface.UnitsPerEm) : 0; IsFixedPitch = Typeface.IsFixedPitch; From 8f1586c8249b0f019a958e436badf37515ac0edb Mon Sep 17 00:00:00 2001 From: Dariusz Komosinski Date: Thu, 15 Oct 2020 10:36:54 +0200 Subject: [PATCH 21/35] Revert change to win32 paint handler that was causing infinite repaint cycle. --- .../Avalonia.Win32/WindowImpl.AppWndProc.cs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs b/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs index 01576500e7..370e2a2a4b 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs @@ -343,16 +343,21 @@ namespace Avalonia.Win32 case WindowsMessage.WM_PAINT: { - using (_rendererLock.Lock()) + using (_rendererLock.Lock()) + { + if (BeginPaint(_hwnd, out PAINTSTRUCT ps) != IntPtr.Zero) { - Paint?.Invoke(default); + var f = RenderScaling; + var r = ps.rcPaint; + Paint?.Invoke(new Rect(r.left / f, r.top / f, (r.right - r.left) / f, + (r.bottom - r.top) / f)); + EndPaint(_hwnd, ref ps); } - - ValidateRect(hWnd, IntPtr.Zero); - - return IntPtr.Zero; } + return IntPtr.Zero; + } + case WindowsMessage.WM_SIZE: { using (_rendererLock.Lock()) From d96c157231c24cc3a167cfa6be10cc13a92b80be Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 15 Oct 2020 11:40:53 +0200 Subject: [PATCH 22/35] Name tuple values. --- src/Avalonia.Controls/Shapes/Shape.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/Shapes/Shape.cs b/src/Avalonia.Controls/Shapes/Shape.cs index 28206a398a..cd16ac402d 100644 --- a/src/Avalonia.Controls/Shapes/Shape.cs +++ b/src/Avalonia.Controls/Shapes/Shape.cs @@ -252,7 +252,7 @@ namespace Avalonia.Controls.Shapes return default; } - return CalculateSizeAndTransform(availableSize, DefiningGeometry.Bounds, Stretch).Item1; + return CalculateSizeAndTransform(availableSize, DefiningGeometry.Bounds, Stretch).size; } protected override Size ArrangeOverride(Size finalSize) @@ -275,7 +275,7 @@ namespace Avalonia.Controls.Shapes return Size.Empty; } - internal static (Size, Matrix) CalculateSizeAndTransform(Size availableSize, Rect shapeBounds, Stretch Stretch) + internal static (Size size, Matrix transform) CalculateSizeAndTransform(Size availableSize, Rect shapeBounds, Stretch Stretch) { Size shapeSize = new Size(shapeBounds.Right, shapeBounds.Bottom); Matrix translate = Matrix.Identity; From cf6032d8e4d726c97e78f678f6c7630dc52d6235 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 15 Oct 2020 11:41:36 +0200 Subject: [PATCH 23/35] Discard unused value. --- src/Avalonia.Controls/Shapes/Shape.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/Shapes/Shape.cs b/src/Avalonia.Controls/Shapes/Shape.cs index cd16ac402d..0b7595ec9a 100644 --- a/src/Avalonia.Controls/Shapes/Shape.cs +++ b/src/Avalonia.Controls/Shapes/Shape.cs @@ -261,7 +261,7 @@ namespace Avalonia.Controls.Shapes { // This should probably use GetRenderBounds(strokeThickness) but then the calculations // will multiply the stroke thickness as well, which isn't correct. - var (size, transform) = CalculateSizeAndTransform(finalSize, DefiningGeometry.Bounds, Stretch); + var (_, transform) = CalculateSizeAndTransform(finalSize, DefiningGeometry.Bounds, Stretch); if (_transform != transform) { From cdb4e9051854d2d7eff66993ae2733de28cec315 Mon Sep 17 00:00:00 2001 From: Yoh Deadfall Date: Thu, 15 Oct 2020 19:37:28 +0300 Subject: [PATCH 24/35] Simplified conditions --- .../Primitives/SelectingItemsControl.cs | 20 +++++++------------ 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs index e34b3b145f..4786e88fca 100644 --- a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs +++ b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs @@ -794,18 +794,13 @@ namespace Avalonia.Controls.Primitives /// The event. private void ContainerSelectionChanged(RoutedEventArgs e) { - if (!_ignoreContainerSelectionChanged) + if (!_ignoreContainerSelectionChanged && + e.Source is IControl control && + e.Source is ISelectable selectable && + control.LogicalParent == this && + ItemContainerGenerator?.IndexFromContainer(control) != -1) { - var control = e.Source as IControl; - var selectable = e.Source as ISelectable; - - if (control != null && - selectable != null && - control.LogicalParent == this && - ItemContainerGenerator?.IndexFromContainer(control) != -1) - { - UpdateSelection(control, selectable.IsSelected); - } + UpdateSelection(control, selectable.IsSelected); } if (e.Source != this) @@ -824,12 +819,11 @@ namespace Avalonia.Controls.Primitives { try { - var selectable = container as ISelectable; bool result; _ignoreContainerSelectionChanged = true; - if (selectable != null) + if (container is ISelectable selectable) { result = selectable.IsSelected; selectable.IsSelected = selected; From 3b48238fe07642d780740a9b1c26a0b6860374ff Mon Sep 17 00:00:00 2001 From: Yoh Deadfall Date: Thu, 15 Oct 2020 19:38:06 +0300 Subject: [PATCH 25/35] Removed dead code --- .../Primitives/SelectingItemsControl.cs | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs index 4786e88fca..301487ebb6 100644 --- a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs +++ b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs @@ -842,21 +842,6 @@ namespace Avalonia.Controls.Primitives } } - /// - /// Sets an item container's 'selected' class or . - /// - /// The index of the item. - /// Whether the item should be selected or deselected. - private void MarkItemSelected(int index, bool selected) - { - var container = ItemContainerGenerator?.ContainerFromIndex(index); - - if (container != null) - { - MarkContainerSelected(container, selected); - } - } - private void UpdateContainerSelection() { if (Presenter?.Panel is IPanel panel) From dad0c4d18afc0462753b04d70bdb5b32ee6af335 Mon Sep 17 00:00:00 2001 From: Yoh Deadfall Date: Thu, 15 Oct 2020 20:24:03 +0300 Subject: [PATCH 26/35] Fixed invalid cast issue if source isn't visual --- .../Primitives/SelectingItemsControl.cs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs index 4317d795f1..19c2b3b8fa 100644 --- a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs +++ b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs @@ -356,17 +356,13 @@ namespace Avalonia.Controls.Primitives /// The container or null if the event did not originate in a container. protected IControl? GetContainerFromEventSource(IInteractive eventSource) { - var parent = (IVisual)eventSource; - - while (parent != null) + for (var current = eventSource as IVisual; current != null; current = current.VisualParent) { - if (parent is IControl control && control.LogicalParent == this - && ItemContainerGenerator?.IndexFromContainer(control) != -1) + if (current is IControl control && control.LogicalParent == this && + ItemContainerGenerator?.IndexFromContainer(control) != -1) { return control; } - - parent = parent.VisualParent; } return null; From 02e6a4ffa5c635f17e6eb51393f88b685599d148 Mon Sep 17 00:00:00 2001 From: Yoh Deadfall Date: Thu, 15 Oct 2020 20:24:57 +0300 Subject: [PATCH 27/35] Fixed nullability issues --- src/Avalonia.Controls/Primitives/SelectingItemsControl.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs index 19c2b3b8fa..d3e7751c4e 100644 --- a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs +++ b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs @@ -354,7 +354,7 @@ namespace Avalonia.Controls.Primitives /// /// The control that raised the event. /// The container or null if the event did not originate in a container. - protected IControl? GetContainerFromEventSource(IInteractive eventSource) + protected IControl? GetContainerFromEventSource(IInteractive? eventSource) { for (var current = eventSource as IVisual; current != null; current = current.VisualParent) { @@ -666,7 +666,7 @@ namespace Avalonia.Controls.Primitives /// false. /// protected bool UpdateSelectionFromEventSource( - IInteractive eventSource, + IInteractive? eventSource, bool select = true, bool rangeModifier = false, bool toggleModifier = false, From fdd9b1f31ff3c9ea3e70030c3c231983bcf0d765 Mon Sep 17 00:00:00 2001 From: Yoh Deadfall Date: Thu, 15 Oct 2020 22:01:11 +0300 Subject: [PATCH 28/35] Fixed key gesture matching when this has an OEM key --- src/Avalonia.Input/KeyGesture.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Input/KeyGesture.cs b/src/Avalonia.Input/KeyGesture.cs index aa6fcc8bff..e155666631 100644 --- a/src/Avalonia.Input/KeyGesture.cs +++ b/src/Avalonia.Input/KeyGesture.cs @@ -144,7 +144,10 @@ namespace Avalonia.Input return s.ToString(); } - public bool Matches(KeyEventArgs keyEvent) => ResolveNumPadOperationKey(keyEvent.Key) == Key && keyEvent.KeyModifiers == KeyModifiers; + public bool Matches(KeyEventArgs keyEvent) => + keyEvent != null && + keyEvent.KeyModifiers == KeyModifiers && + ResolveNumPadOperationKey(keyEvent.Key) == ResolveNumPadOperationKey(Key); // TODO: Move that to external key parser private static Key ParseKey(string key) From 919c1edde4e7be6716023204df3547b1b0c9d49a Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Sat, 17 Oct 2020 10:30:45 +0200 Subject: [PATCH 29/35] Remove warning sample --- samples/BindingDemo/App.xaml.cs | 20 +++++++++++------- samples/ControlCatalog.Desktop/Program.cs | 8 ++----- .../ControlCatalog.NetCore.csproj | 1 - samples/Previewer/App.xaml.cs | 10 ++++++++- samples/Previewer/Program.cs | 15 ++++++------- samples/RenderDemo/App.xaml.cs | 11 +++++++++- samples/RenderDemo/Pages/GlyphRunPage.xaml | 6 +++--- samples/RenderDemo/Pages/GlyphRunPage.xaml.cs | 7 ++++--- samples/VirtualizationDemo/App.xaml.cs | 8 +++++++ samples/VirtualizationDemo/Program.cs | 20 ++++++++---------- .../interop/Direct3DInteropSample/App.paml.cs | 8 +++++++ .../interop/Direct3DInteropSample/Program.cs | 21 ++++++++----------- .../NativeEmbedSample.csproj | 1 - 13 files changed, 83 insertions(+), 53 deletions(-) diff --git a/samples/BindingDemo/App.xaml.cs b/samples/BindingDemo/App.xaml.cs index 13875aeb21..5c38ab8305 100644 --- a/samples/BindingDemo/App.xaml.cs +++ b/samples/BindingDemo/App.xaml.cs @@ -1,6 +1,5 @@ -using System; using Avalonia; -using Avalonia.Controls; +using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; using Avalonia.ReactiveUI; @@ -13,13 +12,20 @@ namespace BindingDemo AvaloniaXamlLoader.Load(this); } - private static void Main() + public override void OnFrameworkInitializationCompleted() { - AppBuilder.Configure() + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + desktop.MainWindow = new MainWindow(); + base.OnFrameworkInitializationCompleted(); + } + + public static int Main(string[] args) + => BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); + + public static AppBuilder BuildAvaloniaApp() + => AppBuilder.Configure() .UsePlatformDetect() .UseReactiveUI() - .LogToDebug() - .Start(); - } + .LogToDebug(); } } diff --git a/samples/ControlCatalog.Desktop/Program.cs b/samples/ControlCatalog.Desktop/Program.cs index b2df1953f5..e0cc8cc904 100644 --- a/samples/ControlCatalog.Desktop/Program.cs +++ b/samples/ControlCatalog.Desktop/Program.cs @@ -10,12 +10,8 @@ namespace ControlCatalog internal class Program { [STAThread] - static void Main(string[] args) - { - // TODO: Make this work with GTK/Skia/Cairo depending on command-line args - // again. - BuildAvaloniaApp().Start(); - } + public static int Main(string[] args) + => BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); /// /// This method is needed for IDE previewer infrastructure diff --git a/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj b/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj index 28b0257eda..2752703e21 100644 --- a/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj +++ b/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj @@ -11,7 +11,6 @@ - diff --git a/samples/Previewer/App.xaml.cs b/samples/Previewer/App.xaml.cs index fffa987a27..ab83d45cd3 100644 --- a/samples/Previewer/App.xaml.cs +++ b/samples/Previewer/App.xaml.cs @@ -1,4 +1,5 @@ using Avalonia; +using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; namespace Previewer @@ -9,6 +10,13 @@ namespace Previewer { AvaloniaXamlLoader.Load(this); } + + public override void OnFrameworkInitializationCompleted() + { + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + desktop.MainWindow = new MainWindow(); + base.OnFrameworkInitializationCompleted(); + } } -} \ No newline at end of file +} diff --git a/samples/Previewer/Program.cs b/samples/Previewer/Program.cs index 48363e27f2..b12b93974a 100644 --- a/samples/Previewer/Program.cs +++ b/samples/Previewer/Program.cs @@ -1,13 +1,14 @@ -using System; -using Avalonia; +using Avalonia; namespace Previewer { class Program { - static void Main(string[] args) - { - AppBuilder.Configure().UsePlatformDetect().Start(); - } + public static AppBuilder BuildAvaloniaApp() + => AppBuilder.Configure() + .UsePlatformDetect(); + + public static int Main(string[] args) + => BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); } -} \ No newline at end of file +} diff --git a/samples/RenderDemo/App.xaml.cs b/samples/RenderDemo/App.xaml.cs index 340ccdae19..2247fd7c5a 100644 --- a/samples/RenderDemo/App.xaml.cs +++ b/samples/RenderDemo/App.xaml.cs @@ -1,4 +1,5 @@ using Avalonia; +using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; using Avalonia.ReactiveUI; @@ -11,9 +12,17 @@ namespace RenderDemo AvaloniaXamlLoader.Load(this); } + public override void OnFrameworkInitializationCompleted() + { + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + desktop.MainWindow = new MainWindow(); + base.OnFrameworkInitializationCompleted(); + } + // TODO: Make this work with GTK/Skia/Cairo depending on command-line args // again. - static void Main(string[] args) => BuildAvaloniaApp().Start(); + static void Main(string[] args) + => BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); // App configuration, used by the entry point and previewer static AppBuilder BuildAvaloniaApp() diff --git a/samples/RenderDemo/Pages/GlyphRunPage.xaml b/samples/RenderDemo/Pages/GlyphRunPage.xaml index fb3e318a0e..c2914e8847 100644 --- a/samples/RenderDemo/Pages/GlyphRunPage.xaml +++ b/samples/RenderDemo/Pages/GlyphRunPage.xaml @@ -6,9 +6,9 @@ x:Class="RenderDemo.Pages.GlyphRunPage"> - - + diff --git a/samples/RenderDemo/Pages/GlyphRunPage.xaml.cs b/samples/RenderDemo/Pages/GlyphRunPage.xaml.cs index ddee880288..857358f6b2 100644 --- a/samples/RenderDemo/Pages/GlyphRunPage.xaml.cs +++ b/samples/RenderDemo/Pages/GlyphRunPage.xaml.cs @@ -9,7 +9,7 @@ namespace RenderDemo.Pages { public class GlyphRunPage : UserControl { - private DrawingPresenter _drawingPresenter; + private Image _imageControl; private GlyphTypeface _glyphTypeface = Typeface.Default.GlyphTypeface; private readonly Random _rand = new Random(); private ushort[] _glyphIndices = new ushort[1]; @@ -25,7 +25,8 @@ namespace RenderDemo.Pages { AvaloniaXamlLoader.Load(this); - _drawingPresenter = this.FindControl("drawingPresenter"); + _imageControl = this.FindControl("imageControl"); + _imageControl.Source = new DrawingImage(); DispatcherTimer.Run(() => { @@ -73,7 +74,7 @@ namespace RenderDemo.Pages drawingGroup.Children.Add(geometryDrawing); - _drawingPresenter.Drawing = drawingGroup; + (_imageControl.Source as DrawingImage).Drawing = drawingGroup; } } } diff --git a/samples/VirtualizationDemo/App.xaml.cs b/samples/VirtualizationDemo/App.xaml.cs index 5990dd282c..81b80c1f40 100644 --- a/samples/VirtualizationDemo/App.xaml.cs +++ b/samples/VirtualizationDemo/App.xaml.cs @@ -1,4 +1,5 @@ using Avalonia; +using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; namespace VirtualizationDemo @@ -9,5 +10,12 @@ namespace VirtualizationDemo { AvaloniaXamlLoader.Load(this); } + + public override void OnFrameworkInitializationCompleted() + { + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + desktop.MainWindow = new MainWindow(); + base.OnFrameworkInitializationCompleted(); + } } } diff --git a/samples/VirtualizationDemo/Program.cs b/samples/VirtualizationDemo/Program.cs index 93ea5e1b88..c23bfd7ad9 100644 --- a/samples/VirtualizationDemo/Program.cs +++ b/samples/VirtualizationDemo/Program.cs @@ -1,19 +1,17 @@ -using System; -using Avalonia; -using Avalonia.Controls; +using Avalonia; using Avalonia.ReactiveUI; namespace VirtualizationDemo { class Program { - static void Main(string[] args) - { - AppBuilder.Configure() - .UsePlatformDetect() - .UseReactiveUI() - .LogToDebug() - .Start(); - } + public static AppBuilder BuildAvaloniaApp() + => AppBuilder.Configure() + .UsePlatformDetect() + .UseReactiveUI() + .LogToDebug(); + + public static int Main(string[] args) + => BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); } } diff --git a/samples/interop/Direct3DInteropSample/App.paml.cs b/samples/interop/Direct3DInteropSample/App.paml.cs index 1b6d5fd39c..29365decfe 100644 --- a/samples/interop/Direct3DInteropSample/App.paml.cs +++ b/samples/interop/Direct3DInteropSample/App.paml.cs @@ -1,4 +1,5 @@ using Avalonia; +using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; namespace Direct3DInteropSample @@ -9,5 +10,12 @@ namespace Direct3DInteropSample { AvaloniaXamlLoader.Load(this); } + + public override void OnFrameworkInitializationCompleted() + { + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + desktop.MainWindow = new MainWindow(); + base.OnFrameworkInitializationCompleted(); + } } } diff --git a/samples/interop/Direct3DInteropSample/Program.cs b/samples/interop/Direct3DInteropSample/Program.cs index 21302fa68a..bf8e76d7e4 100644 --- a/samples/interop/Direct3DInteropSample/Program.cs +++ b/samples/interop/Direct3DInteropSample/Program.cs @@ -1,19 +1,16 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Avalonia; +using Avalonia; namespace Direct3DInteropSample { class Program { - static void Main(string[] args) - { - AppBuilder.Configure() - .With(new Win32PlatformOptions {UseDeferredRendering = false}) - .UseWin32().UseDirect2D1().Start(); - } + public static AppBuilder BuildAvaloniaApp() + => AppBuilder.Configure() + .With(new Win32PlatformOptions { UseDeferredRendering = false }) + .UseWin32() + .UseDirect2D1(); + + public static int Main(string[] args) + => BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); } } diff --git a/samples/interop/NativeEmbedSample/NativeEmbedSample.csproj b/samples/interop/NativeEmbedSample/NativeEmbedSample.csproj index cc831ef8ae..0e0e71fbb2 100644 --- a/samples/interop/NativeEmbedSample/NativeEmbedSample.csproj +++ b/samples/interop/NativeEmbedSample/NativeEmbedSample.csproj @@ -9,7 +9,6 @@ - From a6c705bf70f56389392fadabff43e05b3010f143 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Sat, 17 Oct 2020 10:35:18 +0200 Subject: [PATCH 30/35] Update XML Comment --- .../DataGridColumn.cs | 1 + .../Primitives/IPopupHost.cs | 1 + src/Avalonia.Controls/Window.cs | 2 +- src/Avalonia.Visuals/Media/Imaging/Bitmap.cs | 1 - .../Platform/IDrawingContextImpl.cs | 2 +- .../SceneGraph/ExperimentalAcrylicNode.cs | 7 ++--- .../Rendering/SceneGraph/GeometryNode.cs | 1 - .../Rendering/SceneGraph/RectangleNode.cs | 2 +- .../Avalonia.LinuxFramebuffer/Output/Drm.cs | 16 +++++----- .../AvaloniaRuntimeXamlLoader.cs | 31 ++++++++++++++++--- .../Media/DrawingContextImpl.cs | 1 - .../Avalonia.Win32/Interop/TaskBarList.cs | 1 + 12 files changed, 43 insertions(+), 23 deletions(-) diff --git a/src/Avalonia.Controls.DataGrid/DataGridColumn.cs b/src/Avalonia.Controls.DataGrid/DataGridColumn.cs index 23c4acdf6c..92ddd4e736 100644 --- a/src/Avalonia.Controls.DataGrid/DataGridColumn.cs +++ b/src/Avalonia.Controls.DataGrid/DataGridColumn.cs @@ -665,6 +665,7 @@ namespace Avalonia.Controls /// /// The data item represented by the row that contains the intended cell. /// + /// When the method returns, contains the applied binding. /// /// A new editing element that is bound to the column's property value. /// diff --git a/src/Avalonia.Controls/Primitives/IPopupHost.cs b/src/Avalonia.Controls/Primitives/IPopupHost.cs index 82a49c4189..ab81fe869e 100644 --- a/src/Avalonia.Controls/Primitives/IPopupHost.cs +++ b/src/Avalonia.Controls/Primitives/IPopupHost.cs @@ -47,6 +47,7 @@ namespace Avalonia.Controls.Primitives /// The offset, in device-independent pixels. /// The anchor point. /// The popup gravity. + /// Defines how a popup position will be adjusted if the unadjusted position would result in the popup being partly constrained. /// /// The anchor rect. If null, the bounds of will be used. /// diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs index 317b6d3f2e..54574a7e1c 100644 --- a/src/Avalonia.Controls/Window.cs +++ b/src/Avalonia.Controls/Window.cs @@ -451,7 +451,7 @@ namespace Avalonia.Controls /// /// The dialog result. /// - /// When the window is shown with the + /// When the window is shown with the /// or method, the /// resulting task will produce the value when the window /// is closed. diff --git a/src/Avalonia.Visuals/Media/Imaging/Bitmap.cs b/src/Avalonia.Visuals/Media/Imaging/Bitmap.cs index ca303211cd..5a0c57b333 100644 --- a/src/Avalonia.Visuals/Media/Imaging/Bitmap.cs +++ b/src/Avalonia.Visuals/Media/Imaging/Bitmap.cs @@ -99,7 +99,6 @@ namespace Avalonia.Media.Imaging /// Initializes a new instance of the class. /// /// The pixel format. - /// The alpha format. /// The pointer to the source bytes. /// The size of the bitmap in device pixels. /// The DPI of the bitmap. diff --git a/src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs b/src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs index cfe2cf979a..9cf4e62030 100644 --- a/src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs +++ b/src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs @@ -63,7 +63,7 @@ namespace Avalonia.Platform /// The brush used to fill the rectangle, or null for no fill. /// The pen used to stroke the rectangle, or null for no stroke. /// The rectangle bounds. - /// Box shadow effect parameters + /// Box shadow effect parameters /// /// The brush and the pen can both be null. If the brush is null, then no fill is performed. /// If the pen is null, then no stoke is performed. If both the pen and the brush are null, then the drawing is not visible. diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/ExperimentalAcrylicNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/ExperimentalAcrylicNode.cs index 336d11e3fd..8bd079d070 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/ExperimentalAcrylicNode.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/ExperimentalAcrylicNode.cs @@ -13,10 +13,9 @@ namespace Avalonia.Rendering.SceneGraph /// /// Initializes a new instance of the class. /// - /// The transform. + /// The transform. + /// /// The rectangle to draw. - /// The box shadow parameters - /// Child scenes for drawing visual brushes. public ExperimentalAcrylicNode( Matrix transform, IExperimentalAcrylicMaterial material, @@ -44,7 +43,7 @@ namespace Avalonia.Rendering.SceneGraph /// Determines if this draw operation equals another. /// /// The transform of the other draw operation. - /// The fill of the other draw operation. + /// The fill of the other draw operation. /// The rectangle of the other draw operation. /// True if the draw operations are the same, otherwise false. /// diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/GeometryNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/GeometryNode.cs index 8a19679c77..508ca0ad18 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/GeometryNode.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/GeometryNode.cs @@ -63,7 +63,6 @@ namespace Avalonia.Rendering.SceneGraph /// The fill of the other draw operation. /// The stroke of the other draw operation. /// The geometry of the other draw operation. - /// The box shadow parameters /// True if the draw operations are the same, otherwise false. /// /// The properties of the other draw operation are passed in as arguments to prevent diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/RectangleNode.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/RectangleNode.cs index ec1a7753b1..d0c4566485 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/RectangleNode.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/RectangleNode.cs @@ -73,7 +73,7 @@ namespace Avalonia.Rendering.SceneGraph /// The fill of the other draw operation. /// The stroke of the other draw operation. /// The rectangle of the other draw operation. - /// The box shadow parameters of the other draw operation + /// The box shadow parameters of the other draw operation /// True if the draw operations are the same, otherwise false. /// /// The properties of the other draw operation are passed in as arguments to prevent diff --git a/src/Linux/Avalonia.LinuxFramebuffer/Output/Drm.cs b/src/Linux/Avalonia.LinuxFramebuffer/Output/Drm.cs index 787a2e4cb8..9c476b1b63 100644 --- a/src/Linux/Avalonia.LinuxFramebuffer/Output/Drm.cs +++ b/src/Linux/Avalonia.LinuxFramebuffer/Output/Drm.cs @@ -113,22 +113,22 @@ namespace Avalonia.LinuxFramebuffer.Output [StructLayout(LayoutKind.Sequential)] public struct drmModeConnector { public uint connector_id; - public uint encoder_id; /**< Encoder currently connected to */ + public uint encoder_id; // Encoder currently connected to public uint connector_type; public uint connector_type_id; public DrmModeConnection connection; - public uint mmWidth, mmHeight; /**< HxW in millimeters */ + public uint mmWidth, mmHeight; // HxW in millimeters public DrmModeSubPixel subpixel; public int count_modes; public drmModeModeInfo* modes; public int count_props; - public uint *props; /**< List of property ids */ - public ulong *prop_values; /**< List of property values */ + public uint *props; // List of property ids + public ulong *prop_values; // List of property values public int count_encoders; - public uint *encoders; /**< List of encoder ids */ + public uint *encoders; //List of encoder ids } [StructLayout(LayoutKind.Sequential)] @@ -143,14 +143,14 @@ namespace Avalonia.LinuxFramebuffer.Output [StructLayout(LayoutKind.Sequential)] public struct drmModeCrtc { public uint crtc_id; - public uint buffer_id; /**< FB id to connect to 0 = disconnect */ + public uint buffer_id; // FB id to connect to 0 = disconnect - public uint x, y; /**< Position on the framebuffer */ + public uint x, y; // Position on the framebuffer public uint width, height; public int mode_valid; public drmModeModeInfo mode; - public int gamma_size; /**< Number of gamma stops */ + public int gamma_size; // Number of gamma stops } diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaRuntimeXamlLoader.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaRuntimeXamlLoader.cs index 4569970d01..4df07bcdd8 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaRuntimeXamlLoader.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaRuntimeXamlLoader.cs @@ -3,7 +3,6 @@ using System.IO; using System.Reflection; using System.Text; using Avalonia.Markup.Xaml.XamlIl; -// ReSharper disable CheckNamespace namespace Avalonia.Markup.Xaml { @@ -13,10 +12,10 @@ namespace Avalonia.Markup.Xaml /// Loads XAML from a string. /// /// The string containing the XAML. - /// Default assembly for clr-namespace: - /// - /// The optional instance into which the XAML should be loaded. - /// + /// Default assembly for clr-namespace:. + /// The optional instance into which the XAML should be loaded. + /// The URI of the XAML being loaded. + /// Indicates whether the XAML is being loaded in design mode. /// The loaded object. public static object Load(string xaml, Assembly localAssembly = null, object rootInstance = null, Uri uri = null, bool designMode = false) { @@ -28,13 +27,35 @@ namespace Avalonia.Markup.Xaml } } + /// + /// Loads XAML from a stream. + /// + /// The stream containing the XAML. + /// Default assembly for clr-namespace: + /// The optional instance into which the XAML should be loaded. + /// The URI of the XAML being loaded. + /// Indicates whether the XAML is being loaded in design mode. + /// The loaded object. public static object Load(Stream stream, Assembly localAssembly, object rootInstance = null, Uri uri = null, bool designMode = false) => AvaloniaXamlIlRuntimeCompiler.Load(stream, localAssembly, rootInstance, uri, designMode); + /// + /// Parse XAML from a string. + /// + /// The string containing the XAML. + /// Default assembly for clr-namespace:. + /// The loaded object. public static object Parse(string xaml, Assembly localAssembly = null) => Load(xaml, localAssembly); + /// + /// Parse XAML from a string. + /// + /// The type of the returned object. + /// >The string containing the XAML. + /// >Default assembly for clr-namespace:. + /// The loaded object. public static T Parse(string xaml, Assembly localAssembly = null) => (T)Parse(xaml, localAssembly); diff --git a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs index 59292d605c..ace658654d 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs @@ -323,7 +323,6 @@ namespace Avalonia.Direct2D1.Media /// /// The foreground. /// The glyph run. - /// public void DrawGlyphRun(IBrush foreground, GlyphRun glyphRun) { using (var brush = CreateBrush(foreground, glyphRun.Size)) diff --git a/src/Windows/Avalonia.Win32/Interop/TaskBarList.cs b/src/Windows/Avalonia.Win32/Interop/TaskBarList.cs index 1b01ebbe7f..88b907aeec 100644 --- a/src/Windows/Avalonia.Win32/Interop/TaskBarList.cs +++ b/src/Windows/Avalonia.Win32/Interop/TaskBarList.cs @@ -13,6 +13,7 @@ namespace Avalonia.Win32.Interop /// /// Ported from https://github.com/chromium/chromium/blob/master/ui/views/win/fullscreen_handler.cc /// + /// The window handle. /// Fullscreen state. public static unsafe void MarkFullscreen(IntPtr hwnd, bool fullscreen) { From c98918bc2afc97da3692a616bf5e74db393523a9 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Sat, 17 Oct 2020 10:38:17 +0200 Subject: [PATCH 31/35] Update tests --- .../AnimatableTests.cs | 8 +- .../AvaloniaObjectTests_Metadata.cs | 4 +- .../Collections/AvaloniaListExtenionsTests.cs | 4 + .../Data/Core/BindingExpressionTests.cs | 2 +- .../Data/DefaultValueConverterTests.cs | 14 ++-- tests/Avalonia.Benchmarks/NullRenderer.cs | 3 +- .../NullThreadingPlatform.cs | 3 + .../BorderTests.cs | 2 +- .../Avalonia.Controls.UnitTests/PanelTests.cs | 2 +- .../DefaultMenuInteractionHandlerTests.cs | 10 +-- .../ContentPresenterTests_Standalone.cs | 2 +- .../TextBlockTests.cs | 4 +- .../App.xaml.cs | 13 ++-- .../Program.cs | 20 ++--- .../LayoutableTests.cs | 6 +- ...ayoutableTests_EffectiveViewportChanged.cs | 2 + tests/Avalonia.LeakTests/ControlTests.cs | 7 +- ...ExpressionObserverBuilderTests_Negation.cs | 15 +++- .../Avalonia.RenderTests/Media/BitmapTests.cs | 2 + .../ResourceDictionaryTests.cs | 12 +-- .../SelectorTests_Child.cs | 75 ------------------- .../SelectorTests_Descendent.cs | 74 ------------------ .../Avalonia.Styling.UnitTests/StyleTests.cs | 2 +- .../Avalonia.Styling.UnitTests/StylesTests.cs | 8 +- .../Rendering/DeferredRendererTests.cs | 2 +- .../Avalonia.Visuals.UnitTests/VisualTests.cs | 2 +- 26 files changed, 86 insertions(+), 212 deletions(-) diff --git a/tests/Avalonia.Animation.UnitTests/AnimatableTests.cs b/tests/Avalonia.Animation.UnitTests/AnimatableTests.cs index 784f40fe1f..7633a761a3 100644 --- a/tests/Avalonia.Animation.UnitTests/AnimatableTests.cs +++ b/tests/Avalonia.Animation.UnitTests/AnimatableTests.cs @@ -113,7 +113,7 @@ namespace Avalonia.Animation.UnitTests It.IsAny(), 1.0, 0.5)); - target.ResetCalls(); + target.Invocations.Clear(); control.SetValue(Visual.OpacityProperty, 0.8, BindingPriority.StyleTrigger); @@ -135,7 +135,7 @@ namespace Avalonia.Animation.UnitTests target.Setup(x => x.Apply(control, It.IsAny(), 1.0, 0.5)).Returns(sub.Object); control.Opacity = 0.5; - sub.ResetCalls(); + sub.Invocations.Clear(); control.Opacity = 0.4; sub.Verify(x => x.Dispose()); @@ -158,7 +158,7 @@ namespace Avalonia.Animation.UnitTests control.Opacity = 0.5; Assert.Equal(0.9, control.Opacity); - target.ResetCalls(); + target.Invocations.Clear(); control.Opacity = 0.4; @@ -182,7 +182,7 @@ namespace Avalonia.Animation.UnitTests It.IsAny(), 1.0, 0.5)); - target.ResetCalls(); + target.Invocations.Clear(); var root = (TestRoot)control.Parent; root.Child = null; diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Metadata.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Metadata.cs index 161911dfd5..2edb3deff0 100644 --- a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Metadata.cs +++ b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Metadata.cs @@ -67,7 +67,7 @@ namespace Avalonia.Base.UnitTests public static readonly DirectProperty DirectProperty = AvaloniaProperty.RegisterDirect("Styled", o => o.Direct, unsetValue: "foo"); - private string _direct; + private string _direct = default; public string Direct { @@ -92,7 +92,7 @@ namespace Avalonia.Base.UnitTests public static readonly DirectProperty DirectProperty = Class1.DirectProperty.AddOwner(o => o.Direct, unsetValue: "baz"); - private string _direct; + private string _direct = default; static Class3() { diff --git a/tests/Avalonia.Base.UnitTests/Collections/AvaloniaListExtenionsTests.cs b/tests/Avalonia.Base.UnitTests/Collections/AvaloniaListExtenionsTests.cs index 7f118a2c1d..b1b2d3d8f2 100644 --- a/tests/Avalonia.Base.UnitTests/Collections/AvaloniaListExtenionsTests.cs +++ b/tests/Avalonia.Base.UnitTests/Collections/AvaloniaListExtenionsTests.cs @@ -6,10 +6,12 @@ namespace Avalonia.Base.UnitTests.Collections { public class AvaloniaListExtenionsTests { +#pragma warning disable CS0618 // Type or member is obsolete [Fact] public void CreateDerivedList_Creates_Initial_Items() { var source = new AvaloniaList(new[] { 0, 1, 2, 3 }); + var target = source.CreateDerivedList(x => new Wrapper(x)); var result = target.Select(x => x.Value).ToList(); @@ -137,6 +139,8 @@ namespace Avalonia.Base.UnitTests.Collections Assert.Equal(source, result); } +#pragma warning restore CS0618 // Type or member is obsolete + private class Wrapper { diff --git a/tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.cs b/tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.cs index 1e1f4bd4b2..339cf8a334 100644 --- a/tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.cs +++ b/tests/Avalonia.Base.UnitTests/Data/Core/BindingExpressionTests.cs @@ -356,7 +356,7 @@ namespace Avalonia.Base.UnitTests.Data.Core } [Fact] - public async Task Null_Value_Should_Use_TargetNullValue() + public void Null_Value_Should_Use_TargetNullValue() { var data = new Class1 { StringValue = "foo" }; diff --git a/tests/Avalonia.Base.UnitTests/Data/DefaultValueConverterTests.cs b/tests/Avalonia.Base.UnitTests/Data/DefaultValueConverterTests.cs index efa81dcc1b..f522acf9ce 100644 --- a/tests/Avalonia.Base.UnitTests/Data/DefaultValueConverterTests.cs +++ b/tests/Avalonia.Base.UnitTests/Data/DefaultValueConverterTests.cs @@ -1,12 +1,11 @@ +using System; +using System.ComponentModel; using System.Globalization; -using Avalonia.Controls; -using Avalonia.Data; -using Xunit; using System.Windows.Input; -using System; +using Avalonia.Data; using Avalonia.Data.Converters; using Avalonia.Layout; -using System.ComponentModel; +using Xunit; namespace Avalonia.Base.UnitTests.Data.Converters { @@ -251,6 +250,11 @@ namespace Avalonia.Base.UnitTests.Data.Converters { return obj is CustomType other && this.Value == other.Value; } + + public override int GetHashCode() + { + return 8399587^Value.GetHashCode(); + } } private class CustomTypeConverter : TypeConverter diff --git a/tests/Avalonia.Benchmarks/NullRenderer.cs b/tests/Avalonia.Benchmarks/NullRenderer.cs index 9a756aaf0b..c1701b5d57 100644 --- a/tests/Avalonia.Benchmarks/NullRenderer.cs +++ b/tests/Avalonia.Benchmarks/NullRenderer.cs @@ -9,8 +9,9 @@ namespace Avalonia.Benchmarks { public bool DrawFps { get; set; } public bool DrawDirtyRects { get; set; } +#pragma warning disable CS0067 public event EventHandler SceneInvalidated; - +#pragma warning restore CS0067 public void AddDirty(IVisual visual) { } diff --git a/tests/Avalonia.Benchmarks/NullThreadingPlatform.cs b/tests/Avalonia.Benchmarks/NullThreadingPlatform.cs index ba84b5afcc..bb469a6b33 100644 --- a/tests/Avalonia.Benchmarks/NullThreadingPlatform.cs +++ b/tests/Avalonia.Benchmarks/NullThreadingPlatform.cs @@ -23,6 +23,9 @@ namespace Avalonia.Benchmarks public bool CurrentThreadIsLoopThread => true; +#pragma warning disable CS0067 public event Action Signaled; +#pragma warning restore CS0067 + } } diff --git a/tests/Avalonia.Controls.UnitTests/BorderTests.cs b/tests/Avalonia.Controls.UnitTests/BorderTests.cs index 6e2599c9fd..ab33eaaff9 100644 --- a/tests/Avalonia.Controls.UnitTests/BorderTests.cs +++ b/tests/Avalonia.Controls.UnitTests/BorderTests.cs @@ -54,7 +54,7 @@ namespace Avalonia.Controls.UnitTests var root = new TestRoot(target); var renderer = Mock.Get(root.Renderer); - renderer.ResetCalls(); + renderer.Invocations.Clear(); ((SolidColorBrush)target.Background).Color = Colors.Green; diff --git a/tests/Avalonia.Controls.UnitTests/PanelTests.cs b/tests/Avalonia.Controls.UnitTests/PanelTests.cs index 79ee6d1236..f189638c7d 100644 --- a/tests/Avalonia.Controls.UnitTests/PanelTests.cs +++ b/tests/Avalonia.Controls.UnitTests/PanelTests.cs @@ -127,7 +127,7 @@ namespace Avalonia.Controls.UnitTests var root = new TestRoot(target); var renderer = Mock.Get(root.Renderer); - renderer.ResetCalls(); + renderer.Invocations.Clear(); ((SolidColorBrush)target.Background).Color = Colors.Green; diff --git a/tests/Avalonia.Controls.UnitTests/Platform/DefaultMenuInteractionHandlerTests.cs b/tests/Avalonia.Controls.UnitTests/Platform/DefaultMenuInteractionHandlerTests.cs index 64f35049ce..1a11091b81 100644 --- a/tests/Avalonia.Controls.UnitTests/Platform/DefaultMenuInteractionHandlerTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Platform/DefaultMenuInteractionHandlerTests.cs @@ -503,26 +503,26 @@ namespace Avalonia.Controls.UnitTests.Platform target.PointerEnter(item, enter); Assert.True(timer.ActionIsQueued); Mock.Get(parentItem).VerifySet(x => x.SelectedItem = item); - Mock.Get(parentItem).ResetCalls(); + Mock.Get(parentItem).Invocations.Clear(); // SubMenu shown after a delay. timer.Pulse(); Mock.Get(item).Verify(x => x.Open()); Mock.Get(item).SetupGet(x => x.IsSubMenuOpen).Returns(true); - Mock.Get(item).ResetCalls(); + Mock.Get(item).Invocations.Clear(); // Pointer briefly exits item, but submenu remains open. target.PointerLeave(item, leave); Mock.Get(item).Verify(x => x.Close(), Times.Never); - Mock.Get(item).ResetCalls(); + Mock.Get(item).Invocations.Clear(); // Pointer enters child item; is selected. enter.Source = childItem; target.PointerEnter(childItem, enter); Mock.Get(item).VerifySet(x => x.SelectedItem = childItem); Mock.Get(parentItem).VerifySet(x => x.SelectedItem = item); - Mock.Get(item).ResetCalls(); - Mock.Get(parentItem).ResetCalls(); + Mock.Get(item).Invocations.Clear(); + Mock.Get(parentItem).Invocations.Clear(); } [Fact] diff --git a/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_Standalone.cs b/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_Standalone.cs index c0440ebc7b..c7aa583b6f 100644 --- a/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_Standalone.cs +++ b/tests/Avalonia.Controls.UnitTests/Presenters/ContentPresenterTests_Standalone.cs @@ -212,7 +212,7 @@ namespace Avalonia.Controls.UnitTests.Presenters var root = new TestRoot(target); var renderer = Mock.Get(root.Renderer); - renderer.ResetCalls(); + renderer.Invocations.Clear(); ((SolidColorBrush)target.Background).Color = Colors.Green; diff --git a/tests/Avalonia.Controls.UnitTests/TextBlockTests.cs b/tests/Avalonia.Controls.UnitTests/TextBlockTests.cs index 2d2c197220..b180a536a5 100644 --- a/tests/Avalonia.Controls.UnitTests/TextBlockTests.cs +++ b/tests/Avalonia.Controls.UnitTests/TextBlockTests.cs @@ -37,7 +37,7 @@ namespace Avalonia.Controls.UnitTests var root = new TestRoot(target); var renderer = Mock.Get(root.Renderer); - renderer.ResetCalls(); + renderer.Invocations.Clear(); ((SolidColorBrush)target.Background).Color = Colors.Green; @@ -54,7 +54,7 @@ namespace Avalonia.Controls.UnitTests var root = new TestRoot(target); var renderer = Mock.Get(root.Renderer); - renderer.ResetCalls(); + renderer.Invocations.Clear(); ((SolidColorBrush)target.Foreground).Color = Colors.Green; diff --git a/tests/Avalonia.DesignerSupport.TestApp/App.xaml.cs b/tests/Avalonia.DesignerSupport.TestApp/App.xaml.cs index 653a51232b..97b6b9021a 100644 --- a/tests/Avalonia.DesignerSupport.TestApp/App.xaml.cs +++ b/tests/Avalonia.DesignerSupport.TestApp/App.xaml.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; namespace Avalonia.DesignerSupport.TestApp @@ -13,5 +9,12 @@ namespace Avalonia.DesignerSupport.TestApp { AvaloniaXamlLoader.Load(this); } + + public override void OnFrameworkInitializationCompleted() + { + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + desktop.MainWindow = new MainWindow(); + base.OnFrameworkInitializationCompleted(); + } } } diff --git a/tests/Avalonia.DesignerSupport.TestApp/Program.cs b/tests/Avalonia.DesignerSupport.TestApp/Program.cs index c7c60a4eca..b8428511b3 100644 --- a/tests/Avalonia.DesignerSupport.TestApp/Program.cs +++ b/tests/Avalonia.DesignerSupport.TestApp/Program.cs @@ -1,24 +1,14 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Avalonia.Controls; - -namespace Avalonia.DesignerSupport.TestApp +namespace Avalonia.DesignerSupport.TestApp { static class Program { /// /// The main entry point for the application. /// - static void Main() - { - BuildAvaloniaApp().Start(); - } + public static int Main(string[] args) + => BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); - private static AppBuilder BuildAvaloniaApp() - { - return AppBuilder.Configure().UsePlatformDetect(); - } + public static AppBuilder BuildAvaloniaApp() + => AppBuilder.Configure().UsePlatformDetect(); } } diff --git a/tests/Avalonia.Layout.UnitTests/LayoutableTests.cs b/tests/Avalonia.Layout.UnitTests/LayoutableTests.cs index 44a5af94b9..a8aa0bbf0e 100644 --- a/tests/Avalonia.Layout.UnitTests/LayoutableTests.cs +++ b/tests/Avalonia.Layout.UnitTests/LayoutableTests.cs @@ -112,7 +112,7 @@ namespace Avalonia.Layout.UnitTests root.Measure(Size.Infinity); root.Arrange(new Rect(root.DesiredSize)); - target.ResetCalls(); + target.Invocations.Clear(); control.InvalidateMeasure(); control.InvalidateMeasure(); @@ -133,7 +133,7 @@ namespace Avalonia.Layout.UnitTests root.Measure(Size.Infinity); root.Arrange(new Rect(root.DesiredSize)); - target.ResetCalls(); + target.Invocations.Clear(); control.InvalidateArrange(); control.InvalidateArrange(); @@ -163,7 +163,7 @@ namespace Avalonia.Layout.UnitTests Assert.False(control.IsMeasureValid); Assert.True(root.IsMeasureValid); - target.ResetCalls(); + target.Invocations.Clear(); root.Child = control; diff --git a/tests/Avalonia.Layout.UnitTests/LayoutableTests_EffectiveViewportChanged.cs b/tests/Avalonia.Layout.UnitTests/LayoutableTests_EffectiveViewportChanged.cs index 504e3fa585..f1cec24516 100644 --- a/tests/Avalonia.Layout.UnitTests/LayoutableTests_EffectiveViewportChanged.cs +++ b/tests/Avalonia.Layout.UnitTests/LayoutableTests_EffectiveViewportChanged.cs @@ -15,7 +15,9 @@ namespace Avalonia.Layout.UnitTests [Fact] public async Task EffectiveViewportChanged_Not_Raised_When_Control_Added_To_Tree() { +#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously await RunOnUIThread.Execute(async () => +#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously { var root = CreateRoot(); var target = new Canvas(); diff --git a/tests/Avalonia.LeakTests/ControlTests.cs b/tests/Avalonia.LeakTests/ControlTests.cs index 0c7b966f29..b03ae00cfe 100644 --- a/tests/Avalonia.LeakTests/ControlTests.cs +++ b/tests/Avalonia.LeakTests/ControlTests.cs @@ -460,7 +460,7 @@ namespace Avalonia.LeakTests AttachShowAndDetachContextMenu(window); - Mock.Get(window.PlatformImpl).ResetCalls(); + Mock.Get(window.PlatformImpl).Invocations.Clear(); dotMemory.Check(memory => Assert.Equal(initialMenuCount, memory.GetObjects(where => where.Type.Is()).ObjectsCount)); dotMemory.Check(memory => @@ -505,7 +505,7 @@ namespace Avalonia.LeakTests BuildAndShowContextMenu(window); BuildAndShowContextMenu(window); - Mock.Get(window.PlatformImpl).ResetCalls(); + Mock.Get(window.PlatformImpl).Invocations.Clear(); dotMemory.Check(memory => Assert.Equal(initialMenuCount, memory.GetObjects(where => where.Type.Is()).ObjectsCount)); dotMemory.Check(memory => @@ -601,8 +601,9 @@ namespace Avalonia.LeakTests { public bool DrawFps { get; set; } public bool DrawDirtyRects { get; set; } +#pragma warning disable CS0067 public event EventHandler SceneInvalidated; - +#pragma warning restore CS0067 public void AddDirty(IVisual visual) { } diff --git a/tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_Negation.cs b/tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_Negation.cs index 0be3bbbb9f..0028502eeb 100644 --- a/tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_Negation.cs +++ b/tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_Negation.cs @@ -168,10 +168,23 @@ namespace Avalonia.Markup.UnitTests.Parsers private class Test : INotifyDataErrorInfo { + private string _dataValidationError; + public bool Foo { get; set; } public object Bar { get; set; } - public string DataValidationError { get; set; } + public string DataValidationError + { + get => _dataValidationError; + set + { + if (value == _dataValidationError) + return; + _dataValidationError = value; + ErrorsChanged? + .Invoke(this, new DataErrorsChangedEventArgs(nameof(DataValidationError))); + } + } public bool HasErrors => !string.IsNullOrWhiteSpace(DataValidationError); public event EventHandler ErrorsChanged; diff --git a/tests/Avalonia.RenderTests/Media/BitmapTests.cs b/tests/Avalonia.RenderTests/Media/BitmapTests.cs index d52539c371..b629304b77 100644 --- a/tests/Avalonia.RenderTests/Media/BitmapTests.cs +++ b/tests/Avalonia.RenderTests/Media/BitmapTests.cs @@ -102,7 +102,9 @@ namespace Avalonia.Direct2D1.RenderTests.Media [InlineData(PixelFormat.Bgra8888), InlineData(PixelFormat.Rgba8888)] public void WriteableBitmapShouldBeUsable(PixelFormat fmt) { +#pragma warning disable CS0618 // Type or member is obsolete var writeableBitmap = new WriteableBitmap(new PixelSize(256, 256), new Vector(96, 96), fmt); +#pragma warning restore CS0618 // Type or member is obsolete var data = new int[256 * 256]; for (int y = 0; y < 256; y++) diff --git a/tests/Avalonia.Styling.UnitTests/ResourceDictionaryTests.cs b/tests/Avalonia.Styling.UnitTests/ResourceDictionaryTests.cs index 84b0d09b61..d91d1fab9e 100644 --- a/tests/Avalonia.Styling.UnitTests/ResourceDictionaryTests.cs +++ b/tests/Avalonia.Styling.UnitTests/ResourceDictionaryTests.cs @@ -108,7 +108,7 @@ namespace Avalonia.Styling.UnitTests var target = new ResourceDictionary { { "foo", "bar" } }; ((IResourceProvider)target).AddOwner(host.Object); - host.ResetCalls(); + host.Invocations.Clear(); ((IResourceProvider)target).RemoveOwner(host.Object); host.Verify(x => x.NotifyHostedResourcesChanged(It.IsAny())); @@ -120,7 +120,7 @@ namespace Avalonia.Styling.UnitTests var host = new Mock(); var target = new ResourceDictionary(host.Object); - host.ResetCalls(); + host.Invocations.Clear(); target.Add("foo", "bar"); host.Verify(x => x.NotifyHostedResourcesChanged(It.IsAny())); @@ -132,7 +132,7 @@ namespace Avalonia.Styling.UnitTests var host = new Mock(); var target = new ResourceDictionary(host.Object); - host.ResetCalls(); + host.Invocations.Clear(); target.MergedDictionaries.Add(new ResourceDictionary { { "foo", "bar" }, @@ -149,7 +149,7 @@ namespace Avalonia.Styling.UnitTests var host = new Mock(); var target = new ResourceDictionary(host.Object); - host.ResetCalls(); + host.Invocations.Clear(); target.MergedDictionaries.Add(new ResourceDictionary()); host.Verify( @@ -169,7 +169,7 @@ namespace Avalonia.Styling.UnitTests } }; - host.ResetCalls(); + host.Invocations.Clear(); target.MergedDictionaries.RemoveAt(0); host.Verify( @@ -189,7 +189,7 @@ namespace Avalonia.Styling.UnitTests } }; - host.ResetCalls(); + host.Invocations.Clear(); ((IResourceDictionary)target.MergedDictionaries[0]).Add("foo", "bar"); host.Verify( diff --git a/tests/Avalonia.Styling.UnitTests/SelectorTests_Child.cs b/tests/Avalonia.Styling.UnitTests/SelectorTests_Child.cs index 8ae556cc6f..d011c16ebb 100644 --- a/tests/Avalonia.Styling.UnitTests/SelectorTests_Child.cs +++ b/tests/Avalonia.Styling.UnitTests/SelectorTests_Child.cs @@ -1,12 +1,7 @@ -using System; using System.Collections.Generic; -using System.Linq; -using System.Reactive; using System.Reactive.Linq; using System.Threading.Tasks; -using Avalonia.Collections; using Avalonia.Controls; -using Avalonia.Data; using Avalonia.LogicalTree; using Xunit; @@ -85,76 +80,6 @@ namespace Avalonia.Styling.UnitTests get => Parent; set => ((ISetLogicalParent)this).SetParent(value); } - - public void ClearValue(AvaloniaProperty property) - { - throw new NotImplementedException(); - } - - public void ClearValue(AvaloniaProperty property) - { - throw new NotImplementedException(); - } - - public void AddInheritanceChild(IAvaloniaObject child) - { - throw new NotImplementedException(); - } - - public void RemoveInheritanceChild(IAvaloniaObject child) - { - throw new NotImplementedException(); - } - - public void InheritanceParentChanged(StyledPropertyBase property, IAvaloniaObject oldParent, IAvaloniaObject newParent) - { - throw new NotImplementedException(); - } - - public void InheritedPropertyChanged(AvaloniaProperty property, Optional oldValue, Optional newValue) - { - throw new NotImplementedException(); - } - - public void ClearValue(StyledPropertyBase property) - { - throw new NotImplementedException(); - } - - public void ClearValue(DirectPropertyBase property) - { - throw new NotImplementedException(); - } - - public T GetValue(StyledPropertyBase property) - { - throw new NotImplementedException(); - } - - public T GetValue(DirectPropertyBase property) - { - throw new NotImplementedException(); - } - - public void SetValue(StyledPropertyBase property, T value, BindingPriority priority = BindingPriority.LocalValue) - { - throw new NotImplementedException(); - } - - public void SetValue(DirectPropertyBase property, T value) - { - throw new NotImplementedException(); - } - - public IDisposable Bind(StyledPropertyBase property, IObservable> source, BindingPriority priority = BindingPriority.LocalValue) - { - throw new NotImplementedException(); - } - - public IDisposable Bind(DirectPropertyBase property, IObservable> source) - { - throw new NotImplementedException(); - } } public class TestLogical1 : TestLogical diff --git a/tests/Avalonia.Styling.UnitTests/SelectorTests_Descendent.cs b/tests/Avalonia.Styling.UnitTests/SelectorTests_Descendent.cs index 81485f0345..aacf2ce223 100644 --- a/tests/Avalonia.Styling.UnitTests/SelectorTests_Descendent.cs +++ b/tests/Avalonia.Styling.UnitTests/SelectorTests_Descendent.cs @@ -1,11 +1,7 @@ -using System; using System.Linq; -using System.Reactive; using System.Reactive.Linq; using System.Threading.Tasks; -using Avalonia.Collections; using Avalonia.Controls; -using Avalonia.Data; using Avalonia.LogicalTree; using Xunit; @@ -115,76 +111,6 @@ namespace Avalonia.Styling.UnitTests get => Parent; set => ((ISetLogicalParent)this).SetParent(value); } - - public void ClearValue(AvaloniaProperty property) - { - throw new NotImplementedException(); - } - - public void ClearValue(AvaloniaProperty property) - { - throw new NotImplementedException(); - } - - public void AddInheritanceChild(IAvaloniaObject child) - { - throw new NotImplementedException(); - } - - public void RemoveInheritanceChild(IAvaloniaObject child) - { - throw new NotImplementedException(); - } - - public void InheritanceParentChanged(StyledPropertyBase property, IAvaloniaObject oldParent, IAvaloniaObject newParent) - { - throw new NotImplementedException(); - } - - public void InheritedPropertyChanged(AvaloniaProperty property, Optional oldValue, Optional newValue) - { - throw new NotImplementedException(); - } - - public void ClearValue(StyledPropertyBase property) - { - throw new NotImplementedException(); - } - - public void ClearValue(DirectPropertyBase property) - { - throw new NotImplementedException(); - } - - public T GetValue(StyledPropertyBase property) - { - throw new NotImplementedException(); - } - - public T GetValue(DirectPropertyBase property) - { - throw new NotImplementedException(); - } - - public void SetValue(StyledPropertyBase property, T value, BindingPriority priority = BindingPriority.LocalValue) - { - throw new NotImplementedException(); - } - - public void SetValue(DirectPropertyBase property, T value) - { - throw new NotImplementedException(); - } - - public IDisposable Bind(StyledPropertyBase property, IObservable> source, BindingPriority priority = BindingPriority.LocalValue) - { - throw new NotImplementedException(); - } - - public IDisposable Bind(DirectPropertyBase property, IObservable> source) - { - throw new NotImplementedException(); - } } public class TestLogical1 : TestLogical diff --git a/tests/Avalonia.Styling.UnitTests/StyleTests.cs b/tests/Avalonia.Styling.UnitTests/StyleTests.cs index 3e2bd68cf0..df94887340 100644 --- a/tests/Avalonia.Styling.UnitTests/StyleTests.cs +++ b/tests/Avalonia.Styling.UnitTests/StyleTests.cs @@ -443,7 +443,7 @@ namespace Avalonia.Styling.UnitTests var resources = new Mock(); target.Resources = resources.Object; - host.ResetCalls(); + host.Invocations.Clear(); ((IResourceProvider)target).AddOwner(host.Object); resources.Verify(x => x.AddOwner(host.Object), Times.Once); } diff --git a/tests/Avalonia.Styling.UnitTests/StylesTests.cs b/tests/Avalonia.Styling.UnitTests/StylesTests.cs index 9d3704c91d..88ab124e01 100644 --- a/tests/Avalonia.Styling.UnitTests/StylesTests.cs +++ b/tests/Avalonia.Styling.UnitTests/StylesTests.cs @@ -15,7 +15,7 @@ namespace Avalonia.Styling.UnitTests var style = new Mock(); var rp = style.As(); - host.ResetCalls(); + host.Invocations.Clear(); target.Add(style.Object); rp.Verify(x => x.AddOwner(host.Object)); @@ -29,7 +29,7 @@ namespace Avalonia.Styling.UnitTests var style = new Mock(); var rp = style.As(); - host.ResetCalls(); + host.Invocations.Clear(); target.Add(style.Object); target.Remove(style.Object); @@ -58,7 +58,7 @@ namespace Avalonia.Styling.UnitTests var resources = new Mock(); target.Resources = resources.Object; - host.ResetCalls(); + host.Invocations.Clear(); ((IResourceProvider)target).AddOwner(host.Object); resources.Verify(x => x.AddOwner(host.Object), Times.Once); } @@ -87,7 +87,7 @@ namespace Avalonia.Styling.UnitTests var resourceProvider = style.As(); target.Add(style.Object); - host.ResetCalls(); + host.Invocations.Clear(); ((IResourceProvider)target).AddOwner(host.Object); resourceProvider.Verify(x => x.AddOwner(host.Object), Times.Once); } diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests.cs b/tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests.cs index bfcc341eed..e58eea42d8 100644 --- a/tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests.cs @@ -756,7 +756,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering private void IgnoreFirstFrame(IRenderLoopTask task, Mock sceneBuilder) { RunFrame(task); - sceneBuilder.ResetCalls(); + sceneBuilder.Invocations.Clear(); } private void RunFrame(IRenderLoopTask task) diff --git a/tests/Avalonia.Visuals.UnitTests/VisualTests.cs b/tests/Avalonia.Visuals.UnitTests/VisualTests.cs index 97ad608346..447a68aa69 100644 --- a/tests/Avalonia.Visuals.UnitTests/VisualTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/VisualTests.cs @@ -173,7 +173,7 @@ namespace Avalonia.Visuals.UnitTests }; root.Child = child; - renderer.ResetCalls(); + renderer.Invocations.Clear(); root.Child = null; renderer.Verify(x => x.AddDirty(child)); From 5a4a172652444c1e39d3da18ebbd76e8af14528f Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Sat, 17 Oct 2020 10:43:04 +0200 Subject: [PATCH 32/35] fix NU5040 --- azure-pipelines.yml | 4 ++++ build/Assets/Icon.png | Bin 0 -> 14349 bytes build/SharedVersion.props | 6 +++++- 3 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 build/Assets/Icon.png diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 721a0415f4..e67fa14c57 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -31,6 +31,8 @@ jobs: condition: not(canceled()) - job: macOS + variables: + SolutionDir: '$(Build.SourcesDirectory)' pool: vmImage: 'macOS-10.14' steps: @@ -97,6 +99,8 @@ jobs: - job: Windows pool: vmImage: 'windows-2019' + variables: + SolutionDir: '$(Build.SourcesDirectory)' steps: - task: UseDotNet@2 displayName: 'Use .NET Core SDK 3.1.401' diff --git a/build/Assets/Icon.png b/build/Assets/Icon.png new file mode 100644 index 0000000000000000000000000000000000000000..41a2a618fb02e4cb7f6a15caf572b693bfe1ebb1 GIT binary patch literal 14349 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 latest MIT - https://avatars2.githubusercontent.com/u/14075148?s=200 + Icon.png Avalonia is a WPF/UWP-inspired cross-platform XAML-based UI framework providing a flexible styling system and supporting a wide range of Operating Systems such as Windows (.NET Framework, .NET Core), Linux (via Xorg), MacOS and with experimental support for Android and iOS. avalonia;avaloniaui;mvvm;rx;reactive extensions;android;ios;mac;forms;wpf;net;netstandard;net461;uwp;xamarin https://github.com/AvaloniaUI/Avalonia/releases git + + + + From c364ca6f9bc268a4dfc336a8e39f6d826df6721e Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Sat, 17 Oct 2020 10:44:27 +0200 Subject: [PATCH 33/35] fix NU5128 --- build/SourceLink.props | 4 ++-- dirs.proj | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/build/SourceLink.props b/build/SourceLink.props index 0c9b6a34f8..e27727c9e8 100644 --- a/build/SourceLink.props +++ b/build/SourceLink.props @@ -1,5 +1,5 @@ - + - \ No newline at end of file + diff --git a/dirs.proj b/dirs.proj index bf32abef72..594f2c22d3 100644 --- a/dirs.proj +++ b/dirs.proj @@ -21,6 +21,7 @@ + From a2296478ffc11106d635370d8e9761e84abc2dbc Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Sat, 17 Oct 2020 11:46:59 +0200 Subject: [PATCH 34/35] Supress warning MSBXXXX --- .../Avalonia.Win32.Interop/Avalonia.Win32.Interop.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Windows/Avalonia.Win32.Interop/Avalonia.Win32.Interop.csproj b/src/Windows/Avalonia.Win32.Interop/Avalonia.Win32.Interop.csproj index 0d0dd98a77..0e11ee3b92 100644 --- a/src/Windows/Avalonia.Win32.Interop/Avalonia.Win32.Interop.csproj +++ b/src/Windows/Avalonia.Win32.Interop/Avalonia.Win32.Interop.csproj @@ -2,7 +2,7 @@ net461 true - true + true true true Avalonia.Win32.Interoperability From 4403d94c5c26d50c7910dc580cdd7b313a85e1d1 Mon Sep 17 00:00:00 2001 From: Giuseppe Lippolis Date: Sat, 17 Oct 2020 11:51:05 +0200 Subject: [PATCH 35/35] Change boxShadow to boxShadows --- src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs b/src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs index 9cf4e62030..d6e88a7507 100644 --- a/src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs +++ b/src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs @@ -63,13 +63,13 @@ namespace Avalonia.Platform /// The brush used to fill the rectangle, or null for no fill. /// The pen used to stroke the rectangle, or null for no stroke. /// The rectangle bounds. - /// Box shadow effect parameters + /// Box shadow effect parameters /// /// The brush and the pen can both be null. If the brush is null, then no fill is performed. /// If the pen is null, then no stoke is performed. If both the pen and the brush are null, then the drawing is not visible. /// void DrawRectangle(IBrush brush, IPen pen, RoundedRect rect, - BoxShadows boxShadow = default); + BoxShadows boxShadows = default); /// /// Draws text.