mirror of
https://github.com/sourcegit-scm/sourcegit.git
synced 2024-12-25 21:07:20 -08:00
Merge 408ece148e
into f4618afee6
This commit is contained in:
commit
7f54d72542
8 changed files with 327 additions and 47 deletions
|
@ -51,6 +51,9 @@ namespace SourceGit.Commands
|
|||
_result.TextDiff.MaxLineNumber = Math.Max(_newLine, _oldLine);
|
||||
}
|
||||
|
||||
if (_result.TextDiff != null)
|
||||
_result.TextDiff.ProcessChangeBlocks();
|
||||
|
||||
return _result;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
using Avalonia;
|
||||
using Avalonia.Media.Imaging;
|
||||
|
||||
|
@ -59,16 +61,70 @@ namespace SourceGit.Models
|
|||
}
|
||||
}
|
||||
|
||||
public partial class TextDiff
|
||||
public class TextDiffChangeBlock
|
||||
{
|
||||
public TextDiffChangeBlock(int startLine, int endLine)
|
||||
{
|
||||
StartLine = startLine;
|
||||
EndLine = endLine;
|
||||
}
|
||||
|
||||
public int StartLine { get; set; } = 0;
|
||||
public int EndLine { get; set; } = 0;
|
||||
|
||||
public bool IsInRange(int line)
|
||||
{
|
||||
return line >= StartLine && line <= EndLine;
|
||||
}
|
||||
}
|
||||
|
||||
public partial class TextDiff : ObservableObject
|
||||
{
|
||||
public string File { get; set; } = string.Empty;
|
||||
public List<TextDiffLine> Lines { get; set; } = new List<TextDiffLine>();
|
||||
public Vector ScrollOffset { get; set; } = Vector.Zero;
|
||||
public int MaxLineNumber = 0;
|
||||
|
||||
public int CurrentChangeBlockIdx
|
||||
{
|
||||
get => _currentChangeBlockIdx;
|
||||
set => SetProperty(ref _currentChangeBlockIdx, value);
|
||||
}
|
||||
|
||||
public string Repo { get; set; } = null;
|
||||
public DiffOption Option { get; set; } = null;
|
||||
|
||||
public List<TextDiffChangeBlock> ChangeBlocks { get; set; } = [];
|
||||
|
||||
public void ProcessChangeBlocks()
|
||||
{
|
||||
ChangeBlocks.Clear();
|
||||
int lineIdx = 0, blockStartIdx = 0;
|
||||
bool isNewBlock = true;
|
||||
foreach (var line in Lines)
|
||||
{
|
||||
lineIdx++;
|
||||
if (line.Type == Models.TextDiffLineType.Added ||
|
||||
line.Type == Models.TextDiffLineType.Deleted ||
|
||||
line.Type == Models.TextDiffLineType.None) // Empty
|
||||
{
|
||||
if (isNewBlock)
|
||||
{
|
||||
isNewBlock = false;
|
||||
blockStartIdx = lineIdx;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!isNewBlock)
|
||||
{
|
||||
ChangeBlocks.Add(new TextDiffChangeBlock(blockStartIdx, lineIdx - 1));
|
||||
isNewBlock = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public TextDiffSelection MakeSelection(int startLine, int endLine, bool isCombined, bool isOldSide)
|
||||
{
|
||||
var rs = new TextDiffSelection();
|
||||
|
@ -626,6 +682,8 @@ namespace SourceGit.Models
|
|||
return true;
|
||||
}
|
||||
|
||||
private int _currentChangeBlockIdx = -1; // NOTE: Use -1 as "not set".
|
||||
|
||||
[GeneratedRegex(@"^@@ \-(\d+),?\d* \+(\d+),?\d* @@")]
|
||||
private static partial Regex REG_INDICATOR();
|
||||
}
|
||||
|
|
|
@ -51,6 +51,12 @@ namespace SourceGit.ViewModels
|
|||
private set => SetProperty(ref _unifiedLines, value);
|
||||
}
|
||||
|
||||
public string ChangeBlockIndicator
|
||||
{
|
||||
get => _changeBlockIndicator;
|
||||
private set => SetProperty(ref _changeBlockIndicator, value);
|
||||
}
|
||||
|
||||
public DiffContext(string repo, Models.DiffOption option, DiffContext previous = null)
|
||||
{
|
||||
_repo = repo;
|
||||
|
@ -73,6 +79,54 @@ namespace SourceGit.ViewModels
|
|||
LoadDiffContent();
|
||||
}
|
||||
|
||||
public void PrevChange()
|
||||
{
|
||||
if (_content is Models.TextDiff textDiff)
|
||||
{
|
||||
if (textDiff.CurrentChangeBlockIdx > 0)
|
||||
{
|
||||
textDiff.CurrentChangeBlockIdx--;
|
||||
}
|
||||
else if (textDiff.ChangeBlocks.Count > 0)
|
||||
{
|
||||
// Force property value change and (re-)jump to first change block
|
||||
textDiff.CurrentChangeBlockIdx = -1;
|
||||
textDiff.CurrentChangeBlockIdx = 0;
|
||||
}
|
||||
}
|
||||
RefreshChangeBlockIndicator();
|
||||
}
|
||||
|
||||
public void NextChange()
|
||||
{
|
||||
if (_content is Models.TextDiff textDiff)
|
||||
{
|
||||
if (textDiff.CurrentChangeBlockIdx < textDiff.ChangeBlocks.Count - 1)
|
||||
{
|
||||
textDiff.CurrentChangeBlockIdx++;
|
||||
}
|
||||
else if (textDiff.ChangeBlocks.Count > 0)
|
||||
{
|
||||
// Force property value change and (re-)jump to last change block
|
||||
textDiff.CurrentChangeBlockIdx = -1;
|
||||
textDiff.CurrentChangeBlockIdx = textDiff.ChangeBlocks.Count - 1;
|
||||
}
|
||||
RefreshChangeBlockIndicator();
|
||||
}
|
||||
}
|
||||
|
||||
public void RefreshChangeBlockIndicator()
|
||||
{
|
||||
string curr = "-", tot = "-";
|
||||
if (_content is Models.TextDiff textDiff)
|
||||
{
|
||||
if (textDiff.CurrentChangeBlockIdx >= 0)
|
||||
curr = (textDiff.CurrentChangeBlockIdx + 1).ToString();
|
||||
tot = (textDiff.ChangeBlocks.Count).ToString();
|
||||
}
|
||||
ChangeBlockIndicator = curr + "/" + tot;
|
||||
}
|
||||
|
||||
public void ToggleFullTextDiff()
|
||||
{
|
||||
Preference.Instance.UseFullTextDiff = !Preference.Instance.UseFullTextDiff;
|
||||
|
@ -91,6 +145,12 @@ namespace SourceGit.ViewModels
|
|||
LoadDiffContent();
|
||||
}
|
||||
|
||||
public void ToggleTwoSideDiff()
|
||||
{
|
||||
Preference.Instance.UseSideBySideDiff = !Preference.Instance.UseSideBySideDiff;
|
||||
RefreshChangeBlockIndicator();
|
||||
}
|
||||
|
||||
public void OpenExternalMergeTool()
|
||||
{
|
||||
var toolType = Preference.Instance.ExternalMergeToolType;
|
||||
|
@ -217,7 +277,9 @@ namespace SourceGit.ViewModels
|
|||
FileModeChange = latest.FileModeChange;
|
||||
Content = rs;
|
||||
IsTextDiff = rs is Models.TextDiff;
|
||||
});
|
||||
|
||||
RefreshChangeBlockIndicator();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -281,6 +343,7 @@ namespace SourceGit.ViewModels
|
|||
private string _title;
|
||||
private string _fileModeChange = string.Empty;
|
||||
private int _unifiedLines = 4;
|
||||
private string _changeBlockIndicator = "-/-";
|
||||
private bool _isTextDiff = false;
|
||||
private bool _ignoreWhitespace = false;
|
||||
private object _content = null;
|
||||
|
|
|
@ -45,10 +45,43 @@ namespace SourceGit.ViewModels
|
|||
|
||||
FillEmptyLines();
|
||||
|
||||
ProcessChangeBlocks();
|
||||
|
||||
if (previous != null && previous.File == File)
|
||||
_syncScrollOffset = previous._syncScrollOffset;
|
||||
}
|
||||
|
||||
public List<Models.TextDiffChangeBlock> ChangeBlocks { get; set; } = [];
|
||||
|
||||
public void ProcessChangeBlocks()
|
||||
{
|
||||
ChangeBlocks.Clear();
|
||||
int lineIdx = 0, blockStartIdx = 0;
|
||||
bool isNewBlock = true;
|
||||
foreach (var line in Old) // NOTE: Same block size in both Old and New lines.
|
||||
{
|
||||
lineIdx++;
|
||||
if (line.Type == Models.TextDiffLineType.Added ||
|
||||
line.Type == Models.TextDiffLineType.Deleted ||
|
||||
line.Type == Models.TextDiffLineType.None) // Empty
|
||||
{
|
||||
if (isNewBlock)
|
||||
{
|
||||
isNewBlock = false;
|
||||
blockStartIdx = lineIdx;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!isNewBlock)
|
||||
{
|
||||
ChangeBlocks.Add(new Models.TextDiffChangeBlock(blockStartIdx, lineIdx - 1));
|
||||
isNewBlock = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ConvertsToCombinedRange(Models.TextDiff combined, ref int startLine, ref int endLine, bool isOldSide)
|
||||
{
|
||||
endLine = Math.Min(endLine, combined.Lines.Count - 1);
|
||||
|
|
|
@ -42,6 +42,13 @@
|
|||
<Path Width="12" Height="12" Stretch="Uniform" Margin="0,6,0,0" Data="{StaticResource Icons.Up}"/>
|
||||
</Button>
|
||||
|
||||
<TextBlock Classes="primary"
|
||||
Margin="0,0,0,0"
|
||||
Text="{Binding ChangeBlockIndicator}"
|
||||
FontSize="11"
|
||||
TextTrimming="CharacterEllipsis"
|
||||
IsVisible="{Binding IsTextDiff}"/>
|
||||
|
||||
<Button Classes="icon_button"
|
||||
Width="28"
|
||||
Click="OnGotoNextChange"
|
||||
|
@ -124,7 +131,8 @@
|
|||
|
||||
<ToggleButton Classes="line_path"
|
||||
Width="28" Height="18"
|
||||
IsChecked="{Binding Source={x:Static vm:Preference.Instance}, Path=UseSideBySideDiff, Mode=TwoWay}"
|
||||
Command="{Binding ToggleTwoSideDiff}"
|
||||
IsChecked="{Binding Source={x:Static vm:Preference.Instance}, Path=UseSideBySideDiff, Mode=OneWay}"
|
||||
IsVisible="{Binding IsTextDiff}"
|
||||
ToolTip.Tip="{DynamicResource Text.Diff.SideBySide}">
|
||||
<Path Width="12" Height="12" Data="{StaticResource Icons.LayoutHorizontal}" Margin="0,2,0,0"/>
|
||||
|
@ -241,7 +249,8 @@
|
|||
<DataTemplate DataType="m:TextDiff">
|
||||
<v:TextDiffView
|
||||
UseSideBySideDiff="{Binding Source={x:Static vm:Preference.Instance}, Path=UseSideBySideDiff, Mode=OneWay}"
|
||||
UseFullTextDiff="{Binding Source={x:Static vm:Preference.Instance}, Path=UseFullTextDiff, Mode=OneWay}"/>
|
||||
UseFullTextDiff="{Binding Source={x:Static vm:Preference.Instance}, Path=UseFullTextDiff, Mode=OneWay}"
|
||||
CurrentChangeBlockIdx="{Binding CurrentChangeBlockIdx, Mode=OneWay}"/>
|
||||
</DataTemplate>
|
||||
|
||||
<!-- Empty or only EOL changes -->
|
||||
|
|
|
@ -11,30 +11,48 @@ namespace SourceGit.Views
|
|||
InitializeComponent();
|
||||
}
|
||||
|
||||
public bool UseChangeBlocks { get; set; } = true;
|
||||
|
||||
private void OnGotoPrevChange(object _, RoutedEventArgs e)
|
||||
{
|
||||
var textDiff = this.FindDescendantOfType<ThemedTextDiffPresenter>();
|
||||
if (textDiff == null)
|
||||
return;
|
||||
if (UseChangeBlocks)
|
||||
{
|
||||
if (DataContext is ViewModels.DiffContext diffCtx)
|
||||
diffCtx.PrevChange();
|
||||
}
|
||||
else
|
||||
{
|
||||
var textDiff = this.FindDescendantOfType<ThemedTextDiffPresenter>();
|
||||
if (textDiff == null)
|
||||
return;
|
||||
|
||||
textDiff.GotoPrevChange();
|
||||
if (textDiff is SingleSideTextDiffPresenter presenter)
|
||||
presenter.ForceSyncScrollOffset();
|
||||
textDiff.GotoPrevChange();
|
||||
if (textDiff is SingleSideTextDiffPresenter presenter)
|
||||
presenter.ForceSyncScrollOffset();
|
||||
|
||||
e.Handled = true;
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnGotoNextChange(object _, RoutedEventArgs e)
|
||||
{
|
||||
var textDiff = this.FindDescendantOfType<ThemedTextDiffPresenter>();
|
||||
if (textDiff == null)
|
||||
return;
|
||||
if (UseChangeBlocks)
|
||||
{
|
||||
if (DataContext is ViewModels.DiffContext diffCtx)
|
||||
diffCtx.NextChange();
|
||||
}
|
||||
else
|
||||
{
|
||||
var textDiff = this.FindDescendantOfType<ThemedTextDiffPresenter>();
|
||||
if (textDiff == null)
|
||||
return;
|
||||
|
||||
textDiff.GotoNextChange();
|
||||
if (textDiff is SingleSideTextDiffPresenter presenter)
|
||||
presenter.ForceSyncScrollOffset();
|
||||
textDiff.GotoNextChange();
|
||||
if (textDiff is SingleSideTextDiffPresenter presenter)
|
||||
presenter.ForceSyncScrollOffset();
|
||||
|
||||
e.Handled = true;
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
UseSyntaxHighlighting="{Binding Source={x:Static vm:Preference.Instance}, Path=UseSyntaxHighlighting}"
|
||||
WordWrap="{Binding Source={x:Static vm:Preference.Instance}, Path=EnableDiffViewWordWrap}"
|
||||
ShowHiddenSymbols="{Binding Source={x:Static vm:Preference.Instance}, Path=ShowHiddenSymbolsInDiffView}"
|
||||
CurrentChangeBlockIdx="{Binding #ThisControl.CurrentChangeBlockIdx}"
|
||||
EnableChunkSelection="{Binding #ThisControl.EnableChunkSelection}"
|
||||
SelectedChunk="{Binding #ThisControl.SelectedChunk, Mode=TwoWay}"/>
|
||||
|
||||
|
@ -61,6 +62,7 @@
|
|||
UseSyntaxHighlighting="{Binding Source={x:Static vm:Preference.Instance}, Path=UseSyntaxHighlighting}"
|
||||
WordWrap="False"
|
||||
ShowHiddenSymbols="{Binding Source={x:Static vm:Preference.Instance}, Path=ShowHiddenSymbolsInDiffView}"
|
||||
CurrentChangeBlockIdx="{Binding #ThisControl.CurrentChangeBlockIdx}"
|
||||
EnableChunkSelection="{Binding #ThisControl.EnableChunkSelection}"
|
||||
SelectedChunk="{Binding #ThisControl.SelectedChunk, Mode=TwoWay}"/>
|
||||
|
||||
|
@ -82,6 +84,7 @@
|
|||
UseSyntaxHighlighting="{Binding Source={x:Static vm:Preference.Instance}, Path=UseSyntaxHighlighting}"
|
||||
WordWrap="False"
|
||||
ShowHiddenSymbols="{Binding Source={x:Static vm:Preference.Instance}, Path=ShowHiddenSymbolsInDiffView}"
|
||||
CurrentChangeBlockIdx="{Binding #ThisControl.CurrentChangeBlockIdx}"
|
||||
EnableChunkSelection="{Binding #ThisControl.EnableChunkSelection}"
|
||||
SelectedChunk="{Binding #ThisControl.SelectedChunk, Mode=TwoWay}"/>
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
using Avalonia;
|
||||
|
@ -254,6 +255,10 @@ namespace SourceGit.Views
|
|||
if (_presenter.Document == null || !textView.VisualLinesValid)
|
||||
return;
|
||||
|
||||
var changeBlock = _presenter.GetCurrentChangeBlock();
|
||||
Brush changeBlockBG = new SolidColorBrush(Colors.Gray, 0.25);
|
||||
Pen changeBlockFG = new Pen(Brushes.Gray, 1);
|
||||
|
||||
var lines = _presenter.GetLines();
|
||||
var width = textView.Bounds.Width;
|
||||
foreach (var line in textView.VisualLines)
|
||||
|
@ -266,51 +271,63 @@ namespace SourceGit.Views
|
|||
break;
|
||||
|
||||
var info = lines[index - 1];
|
||||
var bg = GetBrushByLineType(info.Type);
|
||||
if (bg == null)
|
||||
continue;
|
||||
|
||||
var startY = line.GetTextLineVisualYPosition(line.TextLines[0], VisualYPosition.LineTop) - textView.VerticalOffset;
|
||||
var endY = line.GetTextLineVisualYPosition(line.TextLines[^1], VisualYPosition.LineBottom) - textView.VerticalOffset;
|
||||
drawingContext.DrawRectangle(bg, null, new Rect(0, startY, width, endY - startY));
|
||||
|
||||
if (info.Highlights.Count > 0)
|
||||
var bg = GetBrushByLineType(info.Type);
|
||||
if (bg != null)
|
||||
{
|
||||
var highlightBG = info.Type == Models.TextDiffLineType.Added ? _presenter.AddedHighlightBrush : _presenter.DeletedHighlightBrush;
|
||||
var processingIdxStart = 0;
|
||||
var processingIdxEnd = 0;
|
||||
var nextHightlight = 0;
|
||||
if (bg != null)
|
||||
drawingContext.DrawRectangle(bg, null, new Rect(0, startY, width, endY - startY));
|
||||
|
||||
foreach (var tl in line.TextLines)
|
||||
if (info.Highlights.Count > 0)
|
||||
{
|
||||
processingIdxEnd += tl.Length;
|
||||
var highlightBG = info.Type == Models.TextDiffLineType.Added ? _presenter.AddedHighlightBrush : _presenter.DeletedHighlightBrush;
|
||||
var processingIdxStart = 0;
|
||||
var processingIdxEnd = 0;
|
||||
var nextHighlight = 0;
|
||||
|
||||
var y = line.GetTextLineVisualYPosition(tl, VisualYPosition.LineTop) - textView.VerticalOffset;
|
||||
var h = line.GetTextLineVisualYPosition(tl, VisualYPosition.LineBottom) - textView.VerticalOffset - y;
|
||||
|
||||
while (nextHightlight < info.Highlights.Count)
|
||||
foreach (var tl in line.TextLines)
|
||||
{
|
||||
var highlight = info.Highlights[nextHightlight];
|
||||
if (highlight.Start >= processingIdxEnd)
|
||||
break;
|
||||
processingIdxEnd += tl.Length;
|
||||
|
||||
var start = line.GetVisualColumn(highlight.Start < processingIdxStart ? processingIdxStart : highlight.Start);
|
||||
var end = line.GetVisualColumn(highlight.End >= processingIdxEnd ? processingIdxEnd : highlight.End + 1);
|
||||
var y = line.GetTextLineVisualYPosition(tl, VisualYPosition.LineTop) - textView.VerticalOffset;
|
||||
var h = line.GetTextLineVisualYPosition(tl, VisualYPosition.LineBottom) - textView.VerticalOffset - y;
|
||||
|
||||
var x = line.GetTextLineVisualXPosition(tl, start) - textView.HorizontalOffset;
|
||||
var w = line.GetTextLineVisualXPosition(tl, end) - textView.HorizontalOffset - x;
|
||||
var rect = new Rect(x, y, w, h);
|
||||
drawingContext.DrawRectangle(highlightBG, null, rect);
|
||||
while (nextHighlight < info.Highlights.Count)
|
||||
{
|
||||
var highlight = info.Highlights[nextHighlight];
|
||||
if (highlight.Start >= processingIdxEnd)
|
||||
break;
|
||||
|
||||
if (highlight.End >= processingIdxEnd)
|
||||
break;
|
||||
var start = line.GetVisualColumn(highlight.Start < processingIdxStart ? processingIdxStart : highlight.Start);
|
||||
var end = line.GetVisualColumn(highlight.End >= processingIdxEnd ? processingIdxEnd : highlight.End + 1);
|
||||
|
||||
nextHightlight++;
|
||||
var x = line.GetTextLineVisualXPosition(tl, start) - textView.HorizontalOffset;
|
||||
var w = line.GetTextLineVisualXPosition(tl, end) - textView.HorizontalOffset - x;
|
||||
var rect = new Rect(x, y, w, h);
|
||||
drawingContext.DrawRectangle(highlightBG, null, rect);
|
||||
|
||||
if (highlight.End >= processingIdxEnd)
|
||||
break;
|
||||
|
||||
nextHighlight++;
|
||||
}
|
||||
|
||||
processingIdxStart = processingIdxEnd;
|
||||
}
|
||||
|
||||
processingIdxStart = processingIdxEnd;
|
||||
}
|
||||
}
|
||||
|
||||
if (changeBlock != null && changeBlock.IsInRange(index))
|
||||
{
|
||||
drawingContext.DrawRectangle(changeBlockBG, null, new Rect(0, startY, width, endY - startY));
|
||||
if (index == changeBlock.StartLine)
|
||||
drawingContext.DrawLine(changeBlockFG, new Point(0, startY), new Point(width, startY));
|
||||
if (index == changeBlock.EndLine)
|
||||
drawingContext.DrawLine(changeBlockFG, new Point(0, endY), new Point(width, endY));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -486,6 +503,15 @@ namespace SourceGit.Views
|
|||
set => SetValue(DisplayRangeProperty, value);
|
||||
}
|
||||
|
||||
public static readonly StyledProperty<int> CurrentChangeBlockIdxProperty =
|
||||
AvaloniaProperty.Register<ThemedTextDiffPresenter, int>(nameof(CurrentChangeBlockIdx));
|
||||
|
||||
public int CurrentChangeBlockIdx
|
||||
{
|
||||
get => GetValue(CurrentChangeBlockIdxProperty);
|
||||
set => SetValue(CurrentChangeBlockIdxProperty, value);
|
||||
}
|
||||
|
||||
protected override Type StyleKeyOverride => typeof(TextEditor);
|
||||
|
||||
public ThemedTextDiffPresenter(TextArea area, TextDocument doc) : base(area, doc)
|
||||
|
@ -590,6 +616,27 @@ namespace SourceGit.Views
|
|||
}
|
||||
}
|
||||
|
||||
public Models.TextDiffChangeBlock GetCurrentChangeBlock()
|
||||
{
|
||||
return GetChangeBlock(CurrentChangeBlockIdx);
|
||||
}
|
||||
|
||||
public virtual Models.TextDiffChangeBlock GetChangeBlock(int changeBlockIdx)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public void JumpToChangeBlock(int changeBlockIdx)
|
||||
{
|
||||
var changeBlock = GetChangeBlock(changeBlockIdx);
|
||||
if (changeBlock != null)
|
||||
{
|
||||
TextArea.Caret.Line = changeBlock.StartLine;
|
||||
//TextArea.Caret.BringCaretToView(); // NOTE: Brings caret line (barely) into view.
|
||||
ScrollToLine(changeBlock.StartLine); // NOTE: Brings specified line into center of view.
|
||||
}
|
||||
}
|
||||
|
||||
public override void Render(DrawingContext context)
|
||||
{
|
||||
base.Render(context);
|
||||
|
@ -1017,6 +1064,16 @@ namespace SourceGit.Views
|
|||
}
|
||||
}
|
||||
|
||||
public override Models.TextDiffChangeBlock GetChangeBlock(int changeBlockIdx)
|
||||
{
|
||||
if (DataContext is Models.TextDiff diff)
|
||||
{
|
||||
if (changeBlockIdx >= 0 && changeBlockIdx < diff.ChangeBlocks.Count)
|
||||
return diff.ChangeBlocks[changeBlockIdx];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected override void OnLoaded(RoutedEventArgs e)
|
||||
{
|
||||
base.OnLoaded(e);
|
||||
|
@ -1088,6 +1145,8 @@ namespace SourceGit.Views
|
|||
|
||||
public void ForceSyncScrollOffset()
|
||||
{
|
||||
if (_scrollViewer == null)
|
||||
return;
|
||||
if (DataContext is ViewModels.TwoSideTextDiff diff)
|
||||
diff.SyncScrollOffset = _scrollViewer.Offset;
|
||||
}
|
||||
|
@ -1233,6 +1292,16 @@ namespace SourceGit.Views
|
|||
}
|
||||
}
|
||||
|
||||
public override Models.TextDiffChangeBlock GetChangeBlock(int changeBlockIdx)
|
||||
{
|
||||
if (DataContext is ViewModels.TwoSideTextDiff diff)
|
||||
{
|
||||
if (changeBlockIdx >= 0 && changeBlockIdx < diff.ChangeBlocks.Count)
|
||||
return diff.ChangeBlocks[changeBlockIdx];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected override void OnLoaded(RoutedEventArgs e)
|
||||
{
|
||||
base.OnLoaded(e);
|
||||
|
@ -1479,6 +1548,15 @@ namespace SourceGit.Views
|
|||
set => SetValue(EnableChunkSelectionProperty, value);
|
||||
}
|
||||
|
||||
public static readonly StyledProperty<int> CurrentChangeBlockIdxProperty =
|
||||
AvaloniaProperty.Register<TextDiffView, int>(nameof(CurrentChangeBlockIdx));
|
||||
|
||||
public int CurrentChangeBlockIdx
|
||||
{
|
||||
get => GetValue(CurrentChangeBlockIdxProperty);
|
||||
set => SetValue(CurrentChangeBlockIdxProperty, value);
|
||||
}
|
||||
|
||||
static TextDiffView()
|
||||
{
|
||||
UseSideBySideDiffProperty.Changed.AddClassHandler<TextDiffView>((v, _) =>
|
||||
|
@ -1505,6 +1583,19 @@ namespace SourceGit.Views
|
|||
v.Popup.Margin = new Thickness(0, top, right, 0);
|
||||
v.Popup.IsVisible = true;
|
||||
});
|
||||
|
||||
CurrentChangeBlockIdxProperty.Changed.AddClassHandler<TextDiffView>((v, e) =>
|
||||
{
|
||||
if ((int)e.NewValue >= 0 && v.Editor.Presenter != null)
|
||||
{
|
||||
foreach (var p in v.Editor.Presenter.GetVisualDescendants().OfType<ThemedTextDiffPresenter>())
|
||||
{
|
||||
p.JumpToChangeBlock((int)e.NewValue);
|
||||
if (p is SingleSideTextDiffPresenter ssp)
|
||||
ssp.ForceSyncScrollOffset();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public TextDiffView()
|
||||
|
@ -1552,6 +1643,8 @@ namespace SourceGit.Views
|
|||
|
||||
IsUnstagedChange = diff.Option.IsUnstaged;
|
||||
EnableChunkSelection = diff.Option.WorkingCopyChange != null;
|
||||
|
||||
diff.CurrentChangeBlockIdx = -1; // Unset current change block.
|
||||
}
|
||||
|
||||
private void OnStageChunk(object _1, RoutedEventArgs _2)
|
||||
|
|
Loading…
Reference in a new issue