diff --git a/ILSpy.Tests/Editor/MarginCursorTests.cs b/ILSpy.Tests/Editor/MarginCursorTests.cs index 786898286..6ecec59ab 100644 --- a/ILSpy.Tests/Editor/MarginCursorTests.cs +++ b/ILSpy.Tests/Editor/MarginCursorTests.cs @@ -17,9 +17,11 @@ // DEALINGS IN THE SOFTWARE. using System.Linq; +using System.Reflection; using System.Threading.Tasks; using Avalonia.Controls; +using Avalonia.Headless; using Avalonia.Headless.NUnit; using AvaloniaEdit.Editing; @@ -75,4 +77,37 @@ public class MarginCursorTests $"the {margin.GetType().Name} is a click target, so it must show the arrow rather than the text I-beam"); } } + + [AvaloniaTest] + public async Task Toggling_Line_Numbers_On_Live_Keeps_Gutter_Order_And_Positive_Font_Size() + { + var (window, vm) = await TestHarness.BootAsync(1); + var settings = AppComposition.Current.GetExport().DisplaySettings; + settings.ShowLineNumbers = false; + + var coreLibName = typeof(object).Assembly.GetName().Name!; + var objectNode = vm.AssemblyTreeModel.FindNode(coreLibName, "System", "System.Object"); + vm.AssemblyTreeModel.SelectNode(objectNode); + await vm.DockWorkspace.WaitForDecompiledTextAsync(); + var view = await window.WaitForComponent(); + + settings.ShowLineNumbers = true; + var margins = view.Editor.TextArea.LeftMargins; + var lineNumberMargin = margins.OfType() + .Should().ContainSingle("line numbers should render after being toggled on live").Which; + margins.IndexOf(margins.OfType().Single()) + .Should().Be(margins.IndexOf(lineNumberMargin) - 1, + "the bookmark gutter must stay left of line numbers when line numbers are toggled on live"); + GetLineNumberMarginEmSize(lineNumberMargin).Should().BeGreaterThan(0); + + window.UpdateLayout(); + AvaloniaHeadlessPlatform.ForceRenderTimerTick(); + } + + static double GetLineNumberMarginEmSize(LineNumberMargin margin) + { + return (double)typeof(LineNumberMargin) + .GetProperty("EmSize", BindingFlags.Instance | BindingFlags.NonPublic)! + .GetValue(margin)!; + } } diff --git a/ILSpy/TextView/DecompilerTextEditor.cs b/ILSpy/TextView/DecompilerTextEditor.cs index 8df849bc0..d6c6ce057 100644 --- a/ILSpy/TextView/DecompilerTextEditor.cs +++ b/ILSpy/TextView/DecompilerTextEditor.cs @@ -18,6 +18,7 @@ using System; using System.ComponentModel; +using System.Linq; using Avalonia; using Avalonia.Controls; @@ -100,6 +101,17 @@ namespace ICSharpCode.ILSpy.TextView FontFamily = new FontFamily(displaySettings.SelectedFont); if (displaySettings.SelectedFontSize > 0) FontSize = EditorZoom.EffectiveFontSize(displaySettings); + PrepareLineNumberMarginsForRender(); + } + + internal void PrepareLineNumberMarginsForRender() + { + foreach (var margin in TextArea.LeftMargins.OfType()) + { + margin.SetValue(TextBlock.FontFamilyProperty, FontFamily); + margin.SetValue(TextBlock.FontSizeProperty, FontSize); + margin.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); + } } static DisplaySettings? TryGetDisplaySettings() diff --git a/ILSpy/TextView/DecompilerTextView.axaml.cs b/ILSpy/TextView/DecompilerTextView.axaml.cs index 4cc7feba1..3293f14da 100644 --- a/ILSpy/TextView/DecompilerTextView.axaml.cs +++ b/ILSpy/TextView/DecompilerTextView.axaml.cs @@ -34,6 +34,7 @@ using Avalonia.Threading; using Avalonia.VisualTree; using AvaloniaEdit.Document; +using AvaloniaEdit.Editing; using AvaloniaEdit.Folding; using AvaloniaEdit.Highlighting; @@ -179,6 +180,41 @@ namespace ICSharpCode.ILSpy.TextView margin.Cursor = ArrowCursor; } + void KeepBookmarkMarginBeforeLineNumbers() + { + if (bookmarkMargin == null) + return; + + var margins = Editor.TextArea.LeftMargins; + var lineNumberIndex = -1; + for (var i = 0; i < margins.Count; i++) + { + if (margins[i] is LineNumberMargin) + { + lineNumberIndex = i; + break; + } + } + var bookmarkIndex = margins.IndexOf(bookmarkMargin); + if (lineNumberIndex < 0 || bookmarkIndex < 0 || bookmarkIndex < lineNumberIndex) + return; + + var lineNumberMargin = margins[lineNumberIndex]; + Control? separator = null; + if (lineNumberIndex + 1 < margins.Count + && margins[lineNumberIndex + 1] is global::Avalonia.Controls.Shapes.Line) + separator = margins[lineNumberIndex + 1]; + + if (separator != null) + margins.Remove(separator); + margins.Remove(lineNumberMargin); + + bookmarkIndex = margins.IndexOf(bookmarkMargin); + margins.Insert(bookmarkIndex + 1, lineNumberMargin); + if (separator != null) + margins.Insert(bookmarkIndex + 2, separator); + } + void OnPreviewKeyDownForOmnibar(object? sender, KeyEventArgs e) { if (e.Key == Key.L && e.KeyModifiers == KeyModifiers.Control && Omnibar.IsVisible) @@ -649,6 +685,11 @@ namespace ICSharpCode.ILSpy.TextView { case nameof(DisplaySettings.ShowLineNumbers): Editor.ShowLineNumbers = s.ShowLineNumbers; + if (Editor.ShowLineNumbers) + { + KeepBookmarkMarginBeforeLineNumbers(); + Editor.PrepareLineNumberMarginsForRender(); + } break; case nameof(DisplaySettings.EnableWordWrap): Editor.WordWrap = s.EnableWordWrap;