feat(diff): sync vertical scrolling across the split panes
This commit is contained in:
@@ -5,6 +5,8 @@ using System.IO;
|
|||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Media;
|
using Avalonia.Media;
|
||||||
|
using Avalonia.Threading;
|
||||||
|
using Avalonia.VisualTree;
|
||||||
using AvaloniaEdit;
|
using AvaloniaEdit;
|
||||||
using AvaloniaEdit.Document;
|
using AvaloniaEdit.Document;
|
||||||
using AvaloniaEdit.Editing;
|
using AvaloniaEdit.Editing;
|
||||||
@@ -64,6 +66,10 @@ public partial class DiffTextView : UserControl
|
|||||||
|
|
||||||
private DiffLineNumberMargin? _leftMargin, _rightMargin;
|
private DiffLineNumberMargin? _leftMargin, _rightMargin;
|
||||||
|
|
||||||
|
private ScrollViewer? _leftScroll, _rightScroll;
|
||||||
|
private bool _scrollHooked;
|
||||||
|
private bool _syncing;
|
||||||
|
|
||||||
public DiffTextView()
|
public DiffTextView()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
@@ -71,6 +77,7 @@ public partial class DiffTextView : UserControl
|
|||||||
_rightTm = RightEditor.InstallTextMate(Registry);
|
_rightTm = RightEditor.InstallTextMate(Registry);
|
||||||
InstallRenderers();
|
InstallRenderers();
|
||||||
Rebuild();
|
Rebuild();
|
||||||
|
HookScrollSync();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
|
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
|
||||||
@@ -199,6 +206,49 @@ public partial class DiffTextView : UserControl
|
|||||||
RightEditor.TextArea.TextView.InvalidateVisual();
|
RightEditor.TextArea.TextView.InvalidateVisual();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The editors' ScrollViewers only exist once the template has been applied.
|
||||||
|
private void HookScrollSync()
|
||||||
|
{
|
||||||
|
if (_scrollHooked) return;
|
||||||
|
_scrollHooked = true;
|
||||||
|
Dispatcher.UIThread.Post(() =>
|
||||||
|
{
|
||||||
|
_leftScroll = LeftEditor.FindDescendantOfType<ScrollViewer>();
|
||||||
|
_rightScroll = RightEditor.FindDescendantOfType<ScrollViewer>();
|
||||||
|
if (_leftScroll is not null) _leftScroll.ScrollChanged += OnPaneScroll;
|
||||||
|
if (_rightScroll is not null) _rightScroll.ScrollChanged += OnPaneScroll;
|
||||||
|
}, DispatcherPriority.Loaded);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPaneScroll(object? sender, ScrollChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (_syncing || !IsSplit || _leftScroll is null || _rightScroll is null) return;
|
||||||
|
var fromLeft = ReferenceEquals(sender, _leftScroll);
|
||||||
|
var source = fromLeft ? _leftScroll : _rightScroll;
|
||||||
|
var target = fromLeft ? _rightScroll : _leftScroll;
|
||||||
|
var targetEditor = fromLeft ? RightEditor : LeftEditor;
|
||||||
|
var sourceEditor = fromLeft ? LeftEditor : RightEditor;
|
||||||
|
|
||||||
|
_syncing = true;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (WrapLines)
|
||||||
|
{
|
||||||
|
// Line heights differ once lines wrap, so anchor on the top visible line.
|
||||||
|
var topLine = sourceEditor.TextArea.TextView.VisualLines is { Count: > 0 } visualLines
|
||||||
|
? visualLines[0].FirstDocumentLine.LineNumber
|
||||||
|
: (int?)null;
|
||||||
|
if (topLine is { } line && line >= 1 && line <= targetEditor.Document.LineCount)
|
||||||
|
targetEditor.ScrollToLine(line);
|
||||||
|
}
|
||||||
|
else if (Math.Abs(target.Offset.Y - source.Offset.Y) > 0.5)
|
||||||
|
{
|
||||||
|
target.Offset = new Vector(target.Offset.X, source.Offset.Y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally { _syncing = false; }
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>What one document line represents, for the margin and the background renderers.</summary>
|
/// <summary>What one document line represents, for the margin and the background renderers.</summary>
|
||||||
internal sealed record RowInfo(
|
internal sealed record RowInfo(
|
||||||
AlignedSide Kind, int? OldNo, int? NewNo, IReadOnlyList<TextSpan> Spans);
|
AlignedSide Kind, int? OldNo, int? NewNo, IReadOnlyList<TextSpan> Spans);
|
||||||
|
|||||||
Reference in New Issue
Block a user