sourcegit/SourceGit/UI/DiffViewer.xaml.cs
2020-07-09 17:39:16 +08:00

240 lines
8.5 KiB
C#

using System;
using System.Globalization;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
namespace SourceGit.UI {
/// <summary>
/// Viewer for git diff
/// </summary>
public partial class DiffViewer : UserControl {
private double minWidth = 0;
/// <summary>
/// Constructor
/// </summary>
public DiffViewer() {
InitializeComponent();
Reset();
}
/// <summary>
/// Reset data.
/// </summary>
public void Reset() {
mask.Visibility = Visibility.Visible;
}
/// <summary>
/// Diff with options.
/// </summary>
/// <param name="repo"></param>
/// <param name="options"></param>
/// <param name="path"></param>
/// <param name="orgPath"></param>
public void Diff(Git.Repository repo, string options, string path, string orgPath = null) {
SetTitle(path, orgPath);
loading.Visibility = Visibility.Visible;
Task.Run(() => {
var args = $"{options} -- ";
if (!string.IsNullOrEmpty(orgPath)) args += $"{orgPath} ";
args += $"\"{path}\"";
var rs = Git.Diff.Run(repo, args);
SetData(rs);
});
}
#region LAYOUT
/// <summary>
/// Show diff title
/// </summary>
/// <param name="file"></param>
/// <param name="orgFile"></param>
private void SetTitle(string file, string orgFile) {
fileName.Text = file;
if (!string.IsNullOrEmpty(orgFile)) {
orgFileNamePanel.Visibility = Visibility.Visible;
orgFileName.Text = orgFile;
} else {
orgFileNamePanel.Visibility = Visibility.Collapsed;
}
}
/// <summary>
/// Show diff content.
/// </summary>
/// <param name="rs"></param>
private void SetData(Git.Diff.Result rs) {
Dispatcher.Invoke(() => {
loading.Visibility = Visibility.Collapsed;
mask.Visibility = Visibility.Collapsed;
minWidth = Math.Max(leftText.ActualWidth, rightText.ActualWidth) - 16;
leftLineNumber.Text = "";
rightLineNumber.Text = "";
leftText.Document.Blocks.Clear();
rightText.Document.Blocks.Clear();
foreach (var b in rs.Blocks) ShowBlock(b);
leftText.Document.PageWidth = minWidth + 16;
rightText.Document.PageWidth = minWidth + 16;
leftText.ScrollToHome();
});
}
/// <summary>
/// Make paragraph.
/// </summary>
/// <param name="b"></param>
private void ShowBlock(Git.Diff.Block b) {
var content = b.Builder.ToString();
Paragraph p = new Paragraph(new Run(content));
p.Margin = new Thickness(0);
p.Padding = new Thickness();
p.LineHeight = 1;
p.Background = Brushes.Transparent;
p.Foreground = FindResource("Brush.FG") as SolidColorBrush;
p.FontStyle = FontStyles.Normal;
switch (b.Mode) {
case Git.Diff.LineMode.Normal:
break;
case Git.Diff.LineMode.Indicator:
p.Foreground = Brushes.Gray;
p.FontStyle = FontStyles.Italic;
break;
case Git.Diff.LineMode.Empty:
p.Background = new SolidColorBrush(Color.FromArgb(40, 0, 0, 0));
break;
case Git.Diff.LineMode.Added:
p.Background = new SolidColorBrush(Color.FromArgb(60, 0, 255, 0));
break;
case Git.Diff.LineMode.Deleted:
p.Background = new SolidColorBrush(Color.FromArgb(60, 255, 0, 0));
break;
}
var formatter = new FormattedText(
content,
CultureInfo.CurrentUICulture,
FlowDirection.LeftToRight,
new Typeface(leftText.FontFamily, p.FontStyle, p.FontWeight, p.FontStretch),
leftText.FontSize,
Brushes.Black,
new NumberSubstitution(),
TextFormattingMode.Ideal);
if (minWidth < formatter.Width) minWidth = formatter.Width;
switch (b.Side) {
case Git.Diff.Side.Left:
leftText.Document.Blocks.Add(p);
for (int i = 0; i < b.Count; i++) {
if (b.CanShowNumber) leftLineNumber.AppendText($"{i + b.LeftStart}\n");
else leftLineNumber.AppendText("\n");
}
break;
case Git.Diff.Side.Right:
rightText.Document.Blocks.Add(p);
for (int i = 0; i < b.Count; i++) {
if (b.CanShowNumber) rightLineNumber.AppendText($"{i + b.RightStart}\n");
else rightLineNumber.AppendText("\n");
}
break;
default:
leftText.Document.Blocks.Add(p);
var cp = new Paragraph(new Run(content));
cp.Margin = new Thickness(0);
cp.Padding = new Thickness();
cp.LineHeight = 1;
cp.Background = p.Background;
cp.Foreground = p.Foreground;
cp.FontStyle = p.FontStyle;
rightText.Document.Blocks.Add(cp);
for (int i = 0; i < b.Count; i++) {
if (b.Mode != Git.Diff.LineMode.Indicator) {
leftLineNumber.AppendText($"{i + b.LeftStart}\n");
rightLineNumber.AppendText($"{i + b.RightStart}\n");
} else {
leftLineNumber.AppendText("\n");
rightLineNumber.AppendText("\n");
}
}
break;
}
}
#endregion
#region EVENTS
/// <summary>
/// Sync scroll both sides.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void OnViewerScroll(object sender, ScrollChangedEventArgs e) {
if (e.VerticalChange != 0) {
if (leftText.VerticalOffset != e.VerticalOffset) {
leftText.ScrollToVerticalOffset(e.VerticalOffset);
}
if (rightText.VerticalOffset != e.VerticalOffset) {
rightText.ScrollToVerticalOffset(e.VerticalOffset);
}
leftLineNumber.Margin = new Thickness(4, -e.VerticalOffset, 4, 0);
rightLineNumber.Margin = new Thickness(4, -e.VerticalOffset, 4, 0);
} else {
if (leftText.HorizontalOffset != e.HorizontalOffset) {
leftText.ScrollToHorizontalOffset(e.HorizontalOffset);
}
if (rightText.HorizontalOffset != e.HorizontalOffset) {
rightText.ScrollToHorizontalOffset(e.HorizontalOffset);
}
}
}
/// <summary>
/// Scroll using mouse wheel.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void OnViewerMouseWheel(object sender, MouseWheelEventArgs e) {
var text = sender as RichTextBox;
if (text == null) return;
if (e.Delta > 0) {
text.LineUp();
} else {
text.LineDown();
}
e.Handled = true;
}
private void LeftSizeChanged(object sender, SizeChangedEventArgs e) {
if (leftText.Document.PageWidth < leftText.ActualWidth) {
leftText.Document.PageWidth = leftText.ActualWidth;
}
}
private void RightSizeChanged(object sender, SizeChangedEventArgs e) {
if (rightText.Document.PageWidth < rightText.ActualWidth) {
rightText.Document.PageWidth = rightText.ActualWidth;
}
}
#endregion
}
}