feature<TextDiffView>: do NOT reset scroll offset after recompute diff with same change

This commit is contained in:
leo 2024-02-29 11:29:54 +08:00
parent 096fd6cb22
commit 2b97d7c599
5 changed files with 38 additions and 12 deletions

View file

@ -1,4 +1,5 @@
using Avalonia.Threading; using Avalonia;
using Avalonia.Threading;
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using System.IO; using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -49,6 +50,11 @@ namespace SourceGit.ViewModels {
private set => SetProperty(ref _content, value); private set => SetProperty(ref _content, value);
} }
public Vector SyncScrollOffset {
get => _syncScrollOffset;
set => SetProperty(ref _syncScrollOffset, value);
}
public DiffContext(string repo, Models.DiffOption option) { public DiffContext(string repo, Models.DiffOption option) {
_repo = repo; _repo = repo;
_option = option; _option = option;
@ -113,5 +119,6 @@ namespace SourceGit.ViewModels {
private bool _isNoChange = false; private bool _isNoChange = false;
private bool _isTextDiff = false; private bool _isTextDiff = false;
private object _content = null; private object _content = null;
private Vector _syncScrollOffset = Vector.Zero;
} }
} }

View file

@ -4,11 +4,6 @@ using System.Collections.Generic;
namespace SourceGit.ViewModels { namespace SourceGit.ViewModels {
public class TwoSideTextDiff : ObservableObject { public class TwoSideTextDiff : ObservableObject {
public Vector SyncScrollOffset {
get => _syncScrollOffset;
set => SetProperty(ref _syncScrollOffset, value);
}
public string File { get; set; } = string.Empty; public string File { get; set; } = string.Empty;
public List<Models.TextDiffLine> Old { get; set; } = new List<Models.TextDiffLine>(); public List<Models.TextDiffLine> Old { get; set; } = new List<Models.TextDiffLine>();
public List<Models.TextDiffLine> New { get; set; } = new List<Models.TextDiffLine>(); public List<Models.TextDiffLine> New { get; set; } = new List<Models.TextDiffLine>();
@ -46,7 +41,5 @@ namespace SourceGit.ViewModels {
for (int i = 0; i < diff; i++) New.Add(new Models.TextDiffLine()); for (int i = 0; i < diff; i++) New.Add(new Models.TextDiffLine());
} }
} }
private Vector _syncScrollOffset;
} }
} }

View file

@ -1,4 +1,5 @@
using Avalonia.Controls; using Avalonia;
using Avalonia.Controls;
using Avalonia.Platform.Storage; using Avalonia.Platform.Storage;
using Avalonia.Threading; using Avalonia.Threading;
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
@ -194,6 +195,9 @@ namespace SourceGit.ViewModels {
// Restore last selection states. // Restore last selection states.
if (viewChange != null) { if (viewChange != null) {
var scrollOffset = Vector.Zero;
if (_detailContext is DiffContext old) scrollOffset = old.SyncScrollOffset;
if (lastSelectedIsUnstaged) { if (lastSelectedIsUnstaged) {
SelectedUnstagedChange = viewChange; SelectedUnstagedChange = viewChange;
SelectedUnstagedTreeNode = FileTreeNode.SelectByPath(_unstagedTree, viewFile); SelectedUnstagedTreeNode = FileTreeNode.SelectByPath(_unstagedTree, viewFile);
@ -201,6 +205,8 @@ namespace SourceGit.ViewModels {
SelectedStagedChange = viewChange; SelectedStagedChange = viewChange;
SelectedStagedTreeNode = FileTreeNode.SelectByPath(_stagedTree, viewFile); SelectedStagedTreeNode = FileTreeNode.SelectByPath(_stagedTree, viewFile);
} }
if (_detailContext is DiffContext cur) cur.SyncScrollOffset = scrollOffset;
} else { } else {
SelectedUnstagedChange = null; SelectedUnstagedChange = null;
SelectedUnstagedTreeNode = null; SelectedUnstagedTreeNode = null;

View file

@ -18,13 +18,14 @@
SecondaryFG="{DynamicResource Brush.FG2}" SecondaryFG="{DynamicResource Brush.FG2}"
FontFamily="{StaticResource JetBrainsMono}" FontFamily="{StaticResource JetBrainsMono}"
FontSize="12" FontSize="12"
DiffData="{Binding}"/> DiffData="{Binding}"
SyncScrollOffset="{Binding $parent[v:DiffView].DataContext.(vm:DiffContext).SyncScrollOffset, Mode=TwoWay}"/>
</DataTemplate> </DataTemplate>
<DataTemplate DataType="vm:TwoSideTextDiff"> <DataTemplate DataType="vm:TwoSideTextDiff">
<Grid ColumnDefinitions="*,1,*"> <Grid ColumnDefinitions="*,1,*">
<v:SingleSideTextDiffPresenter Grid.Column="0" <v:SingleSideTextDiffPresenter Grid.Column="0"
SyncScrollOffset="{Binding SyncScrollOffset, Mode=TwoWay}" SyncScrollOffset="{Binding $parent[v:DiffView].DataContext.(vm:DiffContext).SyncScrollOffset, Mode=TwoWay}"
IsOld="True" IsOld="True"
HorizontalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"
@ -39,7 +40,7 @@
<Rectangle Grid.Column="1" Fill="{DynamicResource Brush.Border2}" Width="1" HorizontalAlignment="Center" VerticalAlignment="Stretch"/> <Rectangle Grid.Column="1" Fill="{DynamicResource Brush.Border2}" Width="1" HorizontalAlignment="Center" VerticalAlignment="Stretch"/>
<v:SingleSideTextDiffPresenter Grid.Column="2" <v:SingleSideTextDiffPresenter Grid.Column="2"
SyncScrollOffset="{Binding SyncScrollOffset, Mode=TwoWay}" SyncScrollOffset="{Binding $parent[v:DiffView].DataContext.(vm:DiffContext).SyncScrollOffset, Mode=TwoWay}"
IsOld="False" IsOld="False"
HorizontalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"

View file

@ -188,6 +188,14 @@ namespace SourceGit.Views {
set => SetValue(SecondaryFGProperty, value); set => SetValue(SecondaryFGProperty, value);
} }
public static readonly StyledProperty<Vector> SyncScrollOffsetProperty =
AvaloniaProperty.Register<SingleSideTextDiffPresenter, Vector>(nameof(SyncScrollOffset));
public Vector SyncScrollOffset {
get => GetValue(SyncScrollOffsetProperty);
set => SetValue(SyncScrollOffsetProperty, value);
}
protected override Type StyleKeyOverride => typeof(TextEditor); protected override Type StyleKeyOverride => typeof(TextEditor);
public CombinedTextDiffPresenter() : base(new TextArea(), new TextDocument()) { public CombinedTextDiffPresenter() : base(new TextArea(), new TextDocument()) {
@ -208,6 +216,7 @@ namespace SourceGit.Views {
TextArea.TextView.BackgroundRenderers.Add(new LineBackgroundRenderer(this)); TextArea.TextView.BackgroundRenderers.Add(new LineBackgroundRenderer(this));
TextArea.TextView.LineTransformers.Add(new LineStyleTransformer(this, SecondaryFG)); TextArea.TextView.LineTransformers.Add(new LineStyleTransformer(this, SecondaryFG));
TextArea.TextView.ContextRequested += OnTextViewContextRequested; TextArea.TextView.ContextRequested += OnTextViewContextRequested;
TextArea.TextView.ScrollOffsetChanged += OnTextViewScrollOffsetChanged;
if (App.Current?.ActualThemeVariant == ThemeVariant.Dark) { if (App.Current?.ActualThemeVariant == ThemeVariant.Dark) {
_registryOptions = new RegistryOptions(ThemeName.DarkPlus); _registryOptions = new RegistryOptions(ThemeName.DarkPlus);
@ -226,6 +235,7 @@ namespace SourceGit.Views {
TextArea.TextView.BackgroundRenderers.Clear(); TextArea.TextView.BackgroundRenderers.Clear();
TextArea.TextView.LineTransformers.Clear(); TextArea.TextView.LineTransformers.Clear();
TextArea.TextView.ContextRequested -= OnTextViewContextRequested; TextArea.TextView.ContextRequested -= OnTextViewContextRequested;
TextArea.TextView.ScrollOffsetChanged -= OnTextViewScrollOffsetChanged;
_registryOptions = null; _registryOptions = null;
_textMate.Dispose(); _textMate.Dispose();
_textMate = null; _textMate = null;
@ -255,6 +265,10 @@ namespace SourceGit.Views {
e.Handled = true; e.Handled = true;
} }
private void OnTextViewScrollOffsetChanged(object sender, EventArgs e) {
SyncScrollOffset = TextArea.TextView.ScrollOffset;
}
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) { protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) {
base.OnPropertyChanged(change); base.OnPropertyChanged(change);
@ -270,6 +284,11 @@ namespace SourceGit.Views {
} else { } else {
Text = string.Empty; Text = string.Empty;
} }
} else if (change.Property == SyncScrollOffsetProperty) {
if (TextArea.TextView.ScrollOffset != SyncScrollOffset) {
IScrollable scrollable = TextArea.TextView;
scrollable.Offset = SyncScrollOffset;
}
} else if (change.Property.Name == "ActualThemeVariant" && change.NewValue != null && _textMate != null) { } else if (change.Property.Name == "ActualThemeVariant" && change.NewValue != null && _textMate != null) {
if (App.Current?.ActualThemeVariant == ThemeVariant.Dark) { if (App.Current?.ActualThemeVariant == ThemeVariant.Dark) {
_textMate.SetTheme(_registryOptions.LoadTheme(ThemeName.DarkPlus)); _textMate.SetTheme(_registryOptions.LoadTheme(ThemeName.DarkPlus));