fix(ui): Diff-Viewer scrollt jetzt auch horizontal synchron
VerticalScrollSync mirrort nur noch Y, X blieb pro Pane unabhaengig. Beide Panes nutzen dieselbe Schrift/Breite, also X 1:1 mitspiegeln statt wie bei Y ueber TargetOffset umzurechnen. Die Echo-Abbruchbedingung prueft jetzt beide Achsen (vorher nur Y), sonst wuerde der neue X-Schreibvorgang seine eigene Echo-Bedingung nie erfuellen und endlos weiterlaufen. Klasse in ScrollSync umbenannt (DiffTextView + ConflictResolverView mitgezogen), da sie nicht mehr nur vertikal synchronisiert. Kein Headless-Test hinzugefuegt: DiffDispatcherGuardTests.cs dokumentiert bereits, dass ein bootstrapptes Avalonia Application/Dispatcher in ClaudeDo.Ui.Tests nach drei eskalierenden Versuchen als Sackgasse verifiziert wurde (Dispatcher-Thread-Affinitaet kollidiert mit xUnits Worker-Pool ohne Thread-Garantie; Avalonia.Headless.XUnit kollidiert mit xunit 2.9.3). Als Finding avaloniaedit-ui-test-dead-end festgehalten. Sichtpruefung bleibt offen (Side-by-side + Merge-Editor mit ueberbreiten Zeilen).
This commit is contained in:
@@ -75,9 +75,11 @@ internal static class DiffEditorSetup
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Keeps a set of editors scrolled to the same vertical position. Construction wires the panes;
|
||||
/// there is nothing to dispose, and nothing to keep a reference to — the event subscriptions on
|
||||
/// the text views own the instance.
|
||||
/// Keeps a set of editors scrolled to the same position, vertically always and horizontally
|
||||
/// whenever the panes share the same content width (no per-pane offset conversion, unlike
|
||||
/// <see cref="TargetOffset"/> for Y). Construction wires the panes; there is nothing to dispose,
|
||||
/// and nothing to keep a reference to — the event subscriptions on the text views own the
|
||||
/// instance.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
@@ -96,7 +98,7 @@ internal static class DiffEditorSetup
|
||||
/// leaves the ScrollViewer's own offset (and therefore the scrollbar thumb) behind.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
internal sealed class VerticalScrollSync
|
||||
internal sealed class ScrollSync
|
||||
{
|
||||
private readonly TextEditor[] _editors;
|
||||
private readonly ScrollViewer?[] _viewers;
|
||||
@@ -108,8 +110,8 @@ internal sealed class VerticalScrollSync
|
||||
/// <param name="isActive">Gate for layouts where the panes are not side by side.</param>
|
||||
/// <param name="anchorByLine">True while lines wrap — see <see cref="TargetOffset"/>.</param>
|
||||
/// <param name="afterSync">Runs once per user scroll, not per echo.</param>
|
||||
public VerticalScrollSync(TextEditor[] editors, Func<bool>? isActive = null,
|
||||
Func<bool>? anchorByLine = null, Action? afterSync = null)
|
||||
public ScrollSync(TextEditor[] editors, Func<bool>? isActive = null,
|
||||
Func<bool>? anchorByLine = null, Action? afterSync = null)
|
||||
{
|
||||
_editors = editors;
|
||||
_viewers = new ScrollViewer?[editors.Length];
|
||||
@@ -136,15 +138,18 @@ internal sealed class VerticalScrollSync
|
||||
_syncing = true;
|
||||
try
|
||||
{
|
||||
var x = _editors[sourceIndex].TextArea.TextView.ScrollOffset.X;
|
||||
for (var i = 0; i < _editors.Length; i++)
|
||||
{
|
||||
if (i == sourceIndex) continue;
|
||||
if (Viewer(i) is not { } viewer) continue;
|
||||
if (TargetOffset(_editors[sourceIndex], _editors[i]) is not { } y) continue;
|
||||
// The echo from the target's own ScrollOffsetChanged lands after _syncing is
|
||||
// cleared again, so this comparison — not the flag — is what ends the loop.
|
||||
if (Math.Abs(viewer.Offset.Y - y) > 0.5)
|
||||
viewer.Offset = new Vector(viewer.Offset.X, y);
|
||||
// cleared again, so this comparison — not the flag — is what ends the loop. Both
|
||||
// axes must be checked: writing only when Y moved left X's own echo unguarded,
|
||||
// which is how it looped forever once X was written below.
|
||||
if (Math.Abs(viewer.Offset.X - x) > 0.5 || Math.Abs(viewer.Offset.Y - y) > 0.5)
|
||||
viewer.Offset = new Vector(x, y);
|
||||
}
|
||||
}
|
||||
finally { _syncing = false; }
|
||||
|
||||
Reference in New Issue
Block a user