feature: add buttons to go to prev/next change in text diff view (#616)

Signed-off-by: leo <longshuang@msn.cn>
This commit is contained in:
leo 2024-11-16 14:54:50 +08:00
parent cd137e222c
commit 134c71064e
No known key found for this signature in database
3 changed files with 161 additions and 16 deletions

View file

@ -35,7 +35,23 @@
<!-- Toolbar Buttons --> <!-- Toolbar Buttons -->
<StackPanel Grid.Column="3" Margin="8,0,0,0" Orientation="Horizontal" VerticalAlignment="Center"> <StackPanel Grid.Column="3" Margin="8,0,0,0" Orientation="Horizontal" VerticalAlignment="Center">
<Button Classes="icon_button" <Button Classes="icon_button"
Width="32" Width="28"
Click="OnGotoPrevChange"
IsVisible="{Binding IsTextDiff}"
ToolTip.Tip="{DynamicResource Text.Diff.Prev}">
<Path Width="12" Height="12" Stretch="Uniform" Margin="0,6,0,0" Data="{StaticResource Icons.Up}"/>
</Button>
<Button Classes="icon_button"
Width="28"
Click="OnGotoNextChange"
IsVisible="{Binding IsTextDiff}"
ToolTip.Tip="{DynamicResource Text.Diff.Next}">
<Path Width="12" Height="12" Stretch="Uniform" Margin="0,6,0,0" Data="{StaticResource Icons.Down}"/>
</Button>
<Button Classes="icon_button"
Width="28"
Command="{Binding IncrUnified}" Command="{Binding IncrUnified}"
IsVisible="{Binding IsTextDiff}" IsVisible="{Binding IsTextDiff}"
ToolTip.Tip="{DynamicResource Text.Diff.VisualLines.Incr}"> ToolTip.Tip="{DynamicResource Text.Diff.VisualLines.Incr}">
@ -46,7 +62,7 @@
</Button> </Button>
<Button Classes="icon_button" <Button Classes="icon_button"
Width="32" Width="28"
Command="{Binding DecrUnified}" Command="{Binding DecrUnified}"
IsVisible="{Binding IsTextDiff}" IsVisible="{Binding IsTextDiff}"
ToolTip.Tip="{DynamicResource Text.Diff.VisualLines.Decr}"> ToolTip.Tip="{DynamicResource Text.Diff.VisualLines.Decr}">
@ -60,9 +76,7 @@
</Button> </Button>
<ToggleButton Classes="line_path" <ToggleButton Classes="line_path"
Width="32" Height="18" Width="28"
Background="Transparent"
Padding="9,6"
Command="{Binding ToggleFullTextDiff}" Command="{Binding ToggleFullTextDiff}"
IsChecked="{Binding Source={x:Static vm:Preference.Instance}, Path=UseFullTextDiff, Mode=OneWay}" IsChecked="{Binding Source={x:Static vm:Preference.Instance}, Path=UseFullTextDiff, Mode=OneWay}"
IsVisible="{Binding IsTextDiff}" IsVisible="{Binding IsTextDiff}"
@ -71,9 +85,8 @@
</ToggleButton> </ToggleButton>
<ToggleButton Classes="line_path" <ToggleButton Classes="line_path"
Width="32" Height="18" Width="28"
Background="Transparent" Background="Transparent"
Padding="9,6"
IsChecked="{Binding Source={x:Static vm:Preference.Instance}, Path=UseSyntaxHighlighting, Mode=TwoWay}" IsChecked="{Binding Source={x:Static vm:Preference.Instance}, Path=UseSyntaxHighlighting, Mode=TwoWay}"
IsVisible="{Binding IsTextDiff}" IsVisible="{Binding IsTextDiff}"
ToolTip.Tip="{DynamicResource Text.Diff.SyntaxHighlight}"> ToolTip.Tip="{DynamicResource Text.Diff.SyntaxHighlight}">
@ -81,9 +94,7 @@
</ToggleButton> </ToggleButton>
<ToggleButton Classes="line_path" <ToggleButton Classes="line_path"
Width="32" Height="18" Width="28"
Background="Transparent"
Padding="9,6"
IsChecked="{Binding Source={x:Static vm:Preference.Instance}, Path=EnableDiffViewWordWrap, Mode=TwoWay}" IsChecked="{Binding Source={x:Static vm:Preference.Instance}, Path=EnableDiffViewWordWrap, Mode=TwoWay}"
ToolTip.Tip="{DynamicResource Text.Diff.ToggleWordWrap}"> ToolTip.Tip="{DynamicResource Text.Diff.ToggleWordWrap}">
<ToggleButton.IsVisible> <ToggleButton.IsVisible>
@ -97,14 +108,14 @@
</ToggleButton> </ToggleButton>
<ToggleButton Classes="line_path" <ToggleButton Classes="line_path"
Width="32" Width="28"
IsChecked="{Binding IgnoreWhitespace, Mode=TwoWay}" IsChecked="{Binding IgnoreWhitespace, Mode=TwoWay}"
ToolTip.Tip="{DynamicResource Text.Diff.IgnoreWhitespace}"> ToolTip.Tip="{DynamicResource Text.Diff.IgnoreWhitespace}">
<Path Width="14" Height="14" Stretch="Uniform" Data="{StaticResource Icons.Whitespace}"/> <Path Width="14" Height="14" Stretch="Uniform" Data="{StaticResource Icons.Whitespace}"/>
</ToggleButton> </ToggleButton>
<ToggleButton Classes="line_path" <ToggleButton Classes="line_path"
Width="32" Width="28"
IsChecked="{Binding Source={x:Static vm:Preference.Instance}, Path=ShowHiddenSymbolsInDiffView, Mode=TwoWay}" IsChecked="{Binding Source={x:Static vm:Preference.Instance}, Path=ShowHiddenSymbolsInDiffView, Mode=TwoWay}"
IsVisible="{Binding IsTextDiff}" IsVisible="{Binding IsTextDiff}"
ToolTip.Tip="{DynamicResource Text.Diff.ShowHiddenSymbols}"> ToolTip.Tip="{DynamicResource Text.Diff.ShowHiddenSymbols}">
@ -112,16 +123,14 @@
</ToggleButton> </ToggleButton>
<ToggleButton Classes="line_path" <ToggleButton Classes="line_path"
Width="32" Height="18" Width="28" Height="18"
Background="Transparent"
Padding="9,6"
IsChecked="{Binding Source={x:Static vm:Preference.Instance}, Path=UseSideBySideDiff, Mode=TwoWay}" IsChecked="{Binding Source={x:Static vm:Preference.Instance}, Path=UseSideBySideDiff, Mode=TwoWay}"
IsVisible="{Binding IsTextDiff}" IsVisible="{Binding IsTextDiff}"
ToolTip.Tip="{DynamicResource Text.Diff.SideBySide}"> ToolTip.Tip="{DynamicResource Text.Diff.SideBySide}">
<Path Width="12" Height="12" Data="{StaticResource Icons.LayoutHorizontal}" Margin="0,2,0,0"/> <Path Width="12" Height="12" Data="{StaticResource Icons.LayoutHorizontal}" Margin="0,2,0,0"/>
</ToggleButton> </ToggleButton>
<Button Classes="icon_button" Width="32" Command="{Binding OpenExternalMergeTool}" ToolTip.Tip="{DynamicResource Text.Diff.UseMerger}"> <Button Classes="icon_button" Width="28" Command="{Binding OpenExternalMergeTool}" ToolTip.Tip="{DynamicResource Text.Diff.UseMerger}">
<Path Width="12" Height="12" Stretch="Uniform" Data="{StaticResource Icons.OpenWith}"/> <Path Width="12" Height="12" Stretch="Uniform" Data="{StaticResource Icons.OpenWith}"/>
</Button> </Button>
</StackPanel> </StackPanel>

View file

@ -1,4 +1,6 @@
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.VisualTree;
namespace SourceGit.Views namespace SourceGit.Views
{ {
@ -8,5 +10,31 @@ namespace SourceGit.Views
{ {
InitializeComponent(); InitializeComponent();
} }
private void OnGotoPrevChange(object _, RoutedEventArgs e)
{
var textDiff = this.FindDescendantOfType<ThemedTextDiffPresenter>();
if (textDiff == null)
return;
textDiff.GotoPrevChange();
if (textDiff is SingleSideTextDiffPresenter presenter)
presenter.ForceSyncScrollOffset();
e.Handled = true;
}
private void OnGotoNextChange(object _, RoutedEventArgs e)
{
var textDiff = this.FindDescendantOfType<ThemedTextDiffPresenter>();
if (textDiff == null)
return;
textDiff.GotoNextChange();
if (textDiff is SingleSideTextDiffPresenter presenter)
presenter.ForceSyncScrollOffset();
e.Handled = true;
}
} }
} }

View file

@ -498,6 +498,108 @@ namespace SourceGit.Views
{ {
} }
public void GotoPrevChange()
{
var view = TextArea.TextView;
var lines = GetLines();
var firstLineIdx = lines.Count;
foreach (var line in view.VisualLines)
{
if (line.IsDisposed || line.FirstDocumentLine == null || line.FirstDocumentLine.IsDeleted)
continue;
var index = line.FirstDocumentLine.LineNumber - 1;
if (index >= lines.Count)
continue;
if (firstLineIdx > index)
firstLineIdx = index;
}
var firstLineType = lines[firstLineIdx].Type;
var isChangeFirstLine = firstLineType != Models.TextDiffLineType.Normal && firstLineType != Models.TextDiffLineType.Indicator;
if (isChangeFirstLine)
{
for (var i = firstLineIdx - 1; i >= 0; i--)
{
var prevType = lines[i].Type;
if (prevType == Models.TextDiffLineType.Normal || prevType == Models.TextDiffLineType.Indicator)
{
ScrollToLine(i + 2);
return;
}
}
}
else
{
var prevChangeEnd = -1;
for (var i = firstLineIdx - 1; i >= 0; i--)
{
var prevType = lines[i].Type;
if (prevType == Models.TextDiffLineType.None ||
prevType == Models.TextDiffLineType.Added ||
prevType == Models.TextDiffLineType.Deleted)
{
prevChangeEnd = i;
break;
}
}
if (prevChangeEnd <= 0)
return;
for (var i = prevChangeEnd - 1; i >= 0; i--)
{
var prevType = lines[i].Type;
if (prevType == Models.TextDiffLineType.Normal || prevType == Models.TextDiffLineType.Indicator)
{
ScrollToLine(i + 2);
return;
}
}
}
}
public void GotoNextChange()
{
var view = TextArea.TextView;
var lines = GetLines();
var lastLineIdx = -1;
foreach (var line in view.VisualLines)
{
if (line.IsDisposed || line.FirstDocumentLine == null || line.FirstDocumentLine.IsDeleted)
continue;
var index = line.FirstDocumentLine.LineNumber - 1;
if (index >= lines.Count)
continue;
if (lastLineIdx < index)
lastLineIdx = index;
}
var lastLineType = lines[lastLineIdx].Type;
var findNormalLine = lastLineType == Models.TextDiffLineType.Normal || lastLineType == Models.TextDiffLineType.Indicator;
for (var idx = lastLineIdx + 1; idx < lines.Count; idx++)
{
var nextType = lines[idx].Type;
if (nextType == Models.TextDiffLineType.None ||
nextType == Models.TextDiffLineType.Added ||
nextType == Models.TextDiffLineType.Deleted)
{
if (findNormalLine)
{
ScrollToLine(idx + 1);
return;
}
}
else if (!findNormalLine)
{
findNormalLine = true;
}
}
}
public override void Render(DrawingContext context) public override void Render(DrawingContext context)
{ {
base.Render(context); base.Render(context);
@ -968,6 +1070,12 @@ namespace SourceGit.Views
TextArea.LeftMargins.Add(new LineModifyTypeMargin()); TextArea.LeftMargins.Add(new LineModifyTypeMargin());
} }
public void ForceSyncScrollOffset()
{
if (DataContext is ViewModels.TwoSideTextDiff diff)
diff.SyncScrollOffset = _scrollViewer.Offset;
}
public override List<Models.TextDiffLine> GetLines() public override List<Models.TextDiffLine> GetLines()
{ {
if (DataContext is ViewModels.TwoSideTextDiff diff) if (DataContext is ViewModels.TwoSideTextDiff diff)