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) diff --git a/src/Avalonia.Visuals/Media/GlyphRun.cs b/src/Avalonia.Visuals/Media/GlyphRun.cs index 66a8c1dd0c..af228ec57b 100644 --- a/src/Avalonia.Visuals/Media/GlyphRun.cs +++ b/src/Avalonia.Visuals/Media/GlyphRun.cs @@ -399,14 +399,14 @@ namespace Avalonia.Media if (characterIndex > GlyphClusters[GlyphClusters.Length - 1]) { - return _glyphClusters.End; + return _glyphClusters.Length - 1; } } else { if (characterIndex < GlyphClusters[GlyphClusters.Length - 1]) { - return _glyphClusters.End; + return _glyphClusters.Length - 1; } if (characterIndex > GlyphClusters[0]) diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextFormatterImpl.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextFormatterImpl.cs index 394dae8253..6ae5258323 100644 --- a/src/Avalonia.Visuals/Media/TextFormatting/TextFormatterImpl.cs +++ b/src/Avalonia.Visuals/Media/TextFormatting/TextFormatterImpl.cs @@ -339,14 +339,6 @@ namespace Avalonia.Media.TextFormatting return true; } - //The line breaker isn't treating \n\r as a pair so we have to fix that here. - if (textRun.Text[lineBreak.PositionMeasure] == '\n' - && textRun.Text[lineBreak.PositionWrap] == '\r') - { - lineBreak = new LineBreak(lineBreak.PositionMeasure, lineBreak.PositionWrap + 1, - lineBreak.Required); - } - return true; } 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) => diff --git a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLayoutTests.cs b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLayoutTests.cs index 26e8ce4797..f7bc75c05d 100644 --- a/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLayoutTests.cs +++ b/tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLayoutTests.cs @@ -417,7 +417,6 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting [Theory] [InlineData("abcde\r\n", 7)] // Carriage Return + Line Feed - [InlineData("abcde\n\r", 7)] // This isn't valid but we somehow have to support it. [InlineData("abcde\u000A", 6)] // Line Feed [InlineData("abcde\u000B", 6)] // Vertical Tab [InlineData("abcde\u000C", 6)] // Form Feed @@ -575,6 +574,27 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting } } + [Fact] + public void Should_Process_Multiple_NewLines_Properly() + { + using (Start()) + { + var text = "123\r\n\r\n456\r\n\r\n"; + var layout = new TextLayout( + text, + Typeface.Default, + 12.0f, + Brushes.Black); + + Assert.Equal(5, layout.TextLines.Count); + + Assert.Equal("123\r\n", layout.TextLines[0].TextRuns[0].Text); + Assert.Equal("\r\n", layout.TextLines[1].TextRuns[0].Text); + Assert.Equal("456\r\n", layout.TextLines[2].TextRuns[0].Text); + Assert.Equal("\r\n", layout.TextLines[3].TextRuns[0].Text); + } + } + [Fact] public void Should_Wrap_Min_OneCharacter_EveryLine() {