diff --git a/DiffPlex.Wpf/Controls/Helper.cs b/DiffPlex.Wpf/Controls/Helper.cs index 1cc71604..2a967e5c 100644 --- a/DiffPlex.Wpf/Controls/Helper.cs +++ b/DiffPlex.Wpf/Controls/Helper.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; @@ -32,7 +33,7 @@ public enum VisibilityLevels internal static class Helper { - private const int MaxCount = 3000; + private const int MaxCount = 3000000; public const string FontFamily = "Cascadia Code, Consolas, Courier New, monospace, Microsoft Yahei, Microsoft Jhenghei, Meiryo, Segoe UI, Segoe UI Emoji, Segoe UI Symbol"; /// @@ -48,39 +49,20 @@ internal static void RenderInlineDiffs(InternalLinesViewer viewer, ICollection 1 && !disableSubPieces) - { - var details = GetSubPiecesInfo(line, true); - var c = viewer.Add(line.Position, "+", details, changeType.ToString(), source); - c.Tag = line; - hasAdded = true; - } - - break; case ChangeType.Deleted: - if (line.SubPieces != null && line.SubPieces.Count > 1 && !disableSubPieces) - { - var details = GetSubPiecesInfo(line, false); - var c = viewer.Add(line.Position, "-", details, changeType.ToString(), source); - c.Tag = line; - hasAdded = true; - } - - break; case ChangeType.Unchanged: break; default: @@ -89,15 +71,16 @@ internal static void RenderInlineDiffs(InternalLinesViewer viewer, ICollection 1 && !disableSubPieces) { - var c = viewer.Add(line.Position, changeType switch - { - ChangeType.Inserted => "+", - ChangeType.Deleted => "-", - _ => " " - }, text, changeType.ToString(), source); - c.Tag = line; + bool isOld = line.Type == ChangeType.Inserted ? true : false; + var parts = GetSubPiecesInfo(line, isOld, source); + viewer.Add(line, parts, changeType, source); + } + else + { + viewer.Add(line, text, changeType, source); } } @@ -105,29 +88,12 @@ internal static void RenderInlineDiffs(InternalLinesViewer viewer, ICollection lines, bool isOld, UIElement source, int contextLineCount) + internal static void InsertLines(InternalLinesViewer panel, IEnumerable lines, bool isOld, UIElement source, int contextLineCount) { if (lines == null || panel == null) return; - var guid = panel.TrackingId = Guid.NewGuid(); - if (lines.Count < 500) - { - InsertLinesInteral(panel, lines, isOld, source); - if (contextLineCount > -1) CollapseUnchangedSections(panel, contextLineCount); - return; - } - var disablePieces = lines.Count > MaxCount; // For performance. - InsertLinesInteral(panel, lines.Take(300).ToList(), isOld, source, disablePieces); + InsertLinesInteral(panel, lines, isOld, source); if (contextLineCount > -1) CollapseUnchangedSections(panel, contextLineCount); - Task.Delay(800).ContinueWith(t => // For performance. - { - panel.Dispatcher.Invoke(() => - { - if (panel.TrackingId != guid) return; - InsertLinesInteral(panel, lines.Skip(300).ToList(), isOld, source, disablePieces); - if (contextLineCount > -1) CollapseUnchangedSections(panel, contextLineCount); - }); - }); } internal static void CollapseUnchangedSections(InternalLinesViewer panel, int contextLineCount) @@ -137,10 +103,10 @@ internal static void CollapseUnchangedSections(InternalLinesViewer panel, int co var last = 0; var max = -1; var removing = new List(); - foreach (var ele in panel.GetTagsOfEachLine()) + foreach (var ele in panel.LineDetails) { i++; - if (!(ele is DiffPiece e) || e.Type != ChangeType.Unchanged) + if (ele.Piece.Type != ChangeType.Unchanged) { if (!was) { @@ -197,26 +163,17 @@ internal static bool GoTo(InternalLinesViewer panel, int lineIndex) { try { - var currentScrollPosition = panel.ValueScrollViewer.VerticalOffset; + var currentScrollPosition = panel._ValueScrollViewer.VerticalOffset; var point = new Point(0, currentScrollPosition); - if (lineIndex == 0) + + foreach (var item in panel.LineDetails) { - foreach (var item in panel.ValuePanel.Children) + if(item.Number == lineIndex || lineIndex == 0) { - if (!(item is FrameworkElement ele) || !(ele.Tag is DiffPiece line)) continue; - var pos = ele.TransformToVisual(panel.ValueScrollViewer).Transform(point); - panel.ValueScrollViewer.ScrollToVerticalOffset(pos.Y); + panel.ValuePanel.ScrollIntoView(item); return true; } } - - foreach (var item in panel.ValuePanel.Children) - { - if (!(item is FrameworkElement ele) || !(ele.Tag is DiffPiece line) || line?.Position != lineIndex) continue; - var pos = ele.TransformToVisual(panel.ValueScrollViewer).Transform(point); - panel.ValueScrollViewer.ScrollToVerticalOffset(pos.Y); - return true; - } } catch (InvalidOperationException) { @@ -236,19 +193,13 @@ internal static bool GoTo(InternalLinesViewer panel, DiffPiece line) if (line == null) return false; try { - var scrollView = panel.ValueScrollViewer; - var point = new Point(0, 0); - foreach (var item in panel.ValuePanel.Children) - { - if (!(item is FrameworkElement ele) || ele.Tag != line) continue; - var pos = ele.TranslatePoint(point, panel.ValueScrollViewer); - if (pos.Y >= 0 && pos.Y <= scrollView.ActualHeight - ele.ActualHeight) return true; - var currentScrollPosition = panel.ValueScrollViewer.VerticalOffset; - point = new Point(0, currentScrollPosition); - pos = ele.TransformToVisual(panel.ValueScrollViewer).Transform(point); - panel.ValueScrollViewer.ScrollToVerticalOffset(pos.Y); - return true; - } + var lineItem = panel.LineDetails.FirstOrDefault(l => l.Piece == line); + + if (lineItem == null) + return false; + + panel.ValuePanel.ScrollIntoView(lineItem); + return true; } catch (InvalidOperationException) { @@ -265,13 +216,7 @@ internal static bool GoTo(InternalLinesViewer panel, DiffPiece line) /// The line diff information instance; or null, if non-exists. internal static DiffPiece GetLine(InternalLinesViewer panel, int lineIndex) { - foreach (var item in panel.ValuePanel.Children) - { - if (!(item is FrameworkElement ele) || !(ele.Tag is DiffPiece line) || line?.Position != lineIndex) continue; - return line; - } - - return null; + return panel.LineDetails.FirstOrDefault(ld => ld.Number == lineIndex)?.Piece; } /// @@ -396,48 +341,40 @@ internal static ContextMenu CreateLineContextMenu(FrameworkElement parentElement /// All lines. private static IEnumerable> GetLineViewportStates(InternalLinesViewer panel, VisibilityLevels level) { - var scrollView = panel.ValueScrollViewer; + var scrollView = panel._ValueScrollViewer; var point = new Point(0, 0); - switch (level) + foreach (var item in panel.LineDetails.Where(ld => ld != null)) { - case VisibilityLevels.All: - foreach (var item in panel.ValuePanel.Children) - { - if (!(item is FrameworkElement ele) || !(ele.Tag is DiffPiece line)) continue; - var pos = ele.TranslatePoint(point, panel.ValueScrollViewer); - var isIn = pos.Y >= 0 && pos.Y <= scrollView.ActualHeight - ele.ActualHeight; - yield return new Tuple(line, isIn); - } + var container = panel.ValuePanel.ItemContainerGenerator.ContainerFromItem(item) as UIElement; - break; - case VisibilityLevels.Half: - foreach (var item in panel.ValuePanel.Children) - { - if (!(item is FrameworkElement ele) || !(ele.Tag is DiffPiece line)) continue; - var pos = ele.TranslatePoint(point, panel.ValueScrollViewer); - var halfHeight = ele.ActualHeight / 2; - var isIn = pos.Y >= -halfHeight && pos.Y <= scrollView.ActualHeight - halfHeight; - yield return new Tuple(line, isIn); - } + if (container == null) + continue; - break; - default: - foreach (var item in panel.ValuePanel.Children) - { - if (!(item is FrameworkElement ele) || !(ele.Tag is DiffPiece line)) continue; - var pos = ele.TranslatePoint(point, panel.ValueScrollViewer); - var isIn = pos.Y > -ele.ActualHeight && pos.Y < scrollView.ActualHeight; - yield return new Tuple(line, isIn); - } + var pos = container.TranslatePoint(point, scrollView); - break; + switch (level) + { + case VisibilityLevels.All: + var isAllIn = pos.Y >= 0 && pos.Y <= scrollView.ActualHeight - container.DesiredSize.Height; + yield return new Tuple(item.Piece, isAllIn); + break; + case VisibilityLevels.Half: + var halfHeight = container.DesiredSize.Height / 2; + var isHalfIn = pos.Y >= -halfHeight && pos.Y <= scrollView.ActualHeight - halfHeight; + yield return new Tuple(item.Piece, isHalfIn); + break; + default: + var isAnyIn = pos.Y > -container.DesiredSize.Height && pos.Y < scrollView.ActualHeight; + yield return new Tuple(item.Piece, isAnyIn); + break; + } } } [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0056:", Justification = "Not supported.")] - private static List> GetSubPiecesInfo(DiffPiece line, bool isOld) + private static List GetSubPiecesInfo(DiffPiece line, bool isOld, UIElement source) { - var details = new List>(); + var details = new List(); foreach (var ele in line.SubPieces) { if (string.IsNullOrEmpty(ele?.Text)) continue; @@ -449,31 +386,25 @@ private static List> GetSubPiecesInfo(DiffPiece lin ChangeType.Unchanged => ChangeType.Unchanged, _ => ChangeType.Imaginary }; - var subTypeStr = subType != ChangeType.Imaginary ? subType.ToString() : null; - if (details.Count > 0) - { - var last = details[details.Count - 1]; - if (string.Equals(last.Value, subTypeStr, StringComparison.InvariantCulture)) - { - details[details.Count - 1] = new KeyValuePair(last.Key + ele.Text, subTypeStr); - continue; - } - } - details.Add(new KeyValuePair(ele.Text, subTypeStr)); + details.Add(new LineViewerSegment() + { + TextChunk = ele.Text, + ChunkChange = subType, + Source = source + }); } return details; } - private static void InsertLinesInteral(InternalLinesViewer panel, List lines, bool isOld, UIElement source, bool disableSubPieces = false) + private static void InsertLinesInteral(InternalLinesViewer panel, IEnumerable lines, bool isOld, UIElement source, bool disableSubPieces = false) { foreach (var line in lines) { if (line == null) { - var c = panel.Add(null, null, null as string, ChangeType.Unchanged.ToString(), source); - c.Tag = line; + panel.Add(null, null as string, ChangeType.Unchanged, source); continue; } @@ -486,9 +417,8 @@ private static void InsertLinesInteral(InternalLinesViewer panel, List 1 && !disableSubPieces) { - var details = GetSubPiecesInfo(line, isOld); - var c = panel.Add(line.Position, isOld ? "-" : "+", details, changeType.ToString(), source); - c.Tag = line; + var details = GetSubPiecesInfo(line, isOld, source); + panel.Add(line, details, changeType, source); hasAdded = true; } @@ -505,13 +435,7 @@ private static void InsertLinesInteral(InternalLinesViewer panel, List "+", - ChangeType.Deleted => "-", - _ => " " - }, text, changeType.ToString(), source); - c.Tag = line; + panel.Add(line, text, changeType, source); } } diff --git a/DiffPlex.Wpf/Controls/InternalLinesViewer.xaml b/DiffPlex.Wpf/Controls/InternalLinesViewer.xaml index 440f5f3d..5cce61fe 100644 --- a/DiffPlex.Wpf/Controls/InternalLinesViewer.xaml +++ b/DiffPlex.Wpf/Controls/InternalLinesViewer.xaml @@ -5,12 +5,18 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:DiffPlex.Wpf.Controls" - mc:Ignorable="d" + mc:Ignorable="d" x:Name="lineViewer" d:DesignHeight="450" d:DesignWidth="800"> - + @@ -256,6 +262,19 @@ + + @@ -264,14 +283,110 @@ - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DiffPlex.Wpf/Controls/InternalLinesViewer.xaml.cs b/DiffPlex.Wpf/Controls/InternalLinesViewer.xaml.cs index ed69e488..e8bdb877 100644 --- a/DiffPlex.Wpf/Controls/InternalLinesViewer.xaml.cs +++ b/DiffPlex.Wpf/Controls/InternalLinesViewer.xaml.cs @@ -1,5 +1,9 @@ -using System; +using DiffPlex.DiffBuilder.Model; +using System; using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; @@ -26,23 +30,23 @@ public InternalLinesViewer() InitializeComponent(); } - public event ScrollChangedEventHandler ScrollChanged - { - add => ValueScrollViewer.ScrollChanged += value; - remove => ValueScrollViewer.ScrollChanged -= value; - } + public event ScrollChangedEventHandler ScrollChanged; + //TODO: Switch to dependency property and feed forward into the specific templated scroll control + // We can't get the scroll viewer right after creation, so we need to bind it and let WPF handle it + private ScrollBarVisibility verticalScrollBarVisibility; public ScrollBarVisibility VerticalScrollBarVisibility { - get => ValueScrollViewer.VerticalScrollBarVisibility; - set => ValueScrollViewer.VerticalScrollBarVisibility = value; + get => verticalScrollBarVisibility; + set => verticalScrollBarVisibility = value; } + public Guid TrackingId { get; set; } public ContextMenu LineContextMenu { get; set; } - public double VerticalOffset => ValueScrollViewer.VerticalOffset; + public double VerticalOffset => _ValueScrollViewer.VerticalOffset; public int LineNumberWidth { @@ -59,110 +63,87 @@ public int LineNumberWidth } } - public int Count => ValuePanel.Children.Count; - - public void Clear() + private ScrollViewer numberScrollViewer; + internal ScrollViewer _NumberScrollViewer { - NumberPanel.Children.Clear(); - OperationPanel.Children.Clear(); - ValuePanel.Children.Clear(); + get + { + if (numberScrollViewer == null) + numberScrollViewer = NumberPanel.Template.FindName("NumberScrollViewer", NumberPanel) as ScrollViewer; + + return numberScrollViewer; + } } - public StackPanel Add(int? number, string operation, string value, string changeType, UIElement source) + private ScrollViewer operationScrollViewer; + internal ScrollViewer _OperationScrollViewer { - var index = new TextBlock - { - Text = number.HasValue ? number.ToString() : string.Empty, - TextAlignment = TextAlignment.Right - }; - index.SetBinding(TextBlock.ForegroundProperty, GetBindings("LineNumberForeground", source, Foreground)); - index.SetBinding(TextBlock.BackgroundProperty, GetBindings(changeType + "Background", source)); - ApplyTextBlockProperties(index, source); - NumberPanel.Children.Add(index); - - var op = new TextBlock + get { - Text = operation, - TextAlignment = TextAlignment.Center - }; - op.SetBinding(TextBlock.ForegroundProperty, GetBindings("ChangeTypeForeground", source, Foreground)); - op.SetBinding(TextBlock.BackgroundProperty, GetBindings(changeType + "Background", source)); - ApplyTextBlockProperties(op, source); - OperationPanel.Children.Add(op); + if (operationScrollViewer == null) + operationScrollViewer = OperationPanel.Template.FindName("OperationScrollViewer", OperationPanel) as ScrollViewer; - var panel = new StackPanel { Orientation = Orientation.Horizontal }; - panel.SetBinding(BackgroundProperty, GetBindings(changeType + "Background", source)); - var text = new TextBlock - { - Text = value - }; - if (!string.IsNullOrEmpty(value)) - { - text.SetBinding(TextBlock.ForegroundProperty, GetBindings(changeType + "Foreground", source, Foreground)); - text.SetBinding(TextBlock.BackgroundProperty, GetBindings(changeType + "Background", source)); - ApplyTextBlockProperties(text, source); - panel.ContextMenu = LineContextMenu; + return operationScrollViewer; } - - panel.Children.Add(text); - ValuePanel.Children.Add(panel); - return panel; } - public StackPanel Add(int? number, string operation, List> value, string changeType, UIElement source) + private ScrollViewer valueScrollViewer; + internal ScrollViewer _ValueScrollViewer { - var index = new TextBlock + get { - Text = number.HasValue ? number.ToString() : string.Empty, - TextAlignment = TextAlignment.Right - }; - index.SetBinding(TextBlock.ForegroundProperty, GetBindings("LineNumberForeground", source, Foreground)); - index.SetBinding(TextBlock.BackgroundProperty, GetBindings(changeType + "Background", source)); - ApplyTextBlockProperties(index, source); - NumberPanel.Children.Add(index); + if (valueScrollViewer == null) + valueScrollViewer = ValuePanel.Template.FindName("ValueScrollViewer", ValuePanel) as ScrollViewer; - var op = new TextBlock - { - Text = operation, - TextAlignment = TextAlignment.Center - }; - op.SetBinding(TextBlock.ForegroundProperty, GetBindings("ChangeTypeForeground", source, Foreground)); - op.SetBinding(TextBlock.BackgroundProperty, GetBindings(changeType + "Background", source)); - ApplyTextBlockProperties(op, source); - OperationPanel.Children.Add(op); + return valueScrollViewer; + } + } - var panel = new StackPanel { Orientation = Orientation.Horizontal }; - panel.SetBinding(BackgroundProperty, GetBindings(changeType + "Background", source)); - if (value == null) value = new List>(); - foreach (var ele in value) - { - if (string.IsNullOrEmpty(ele.Key)) continue; - var text = new TextBlock - { - Text = ele.Key - }; - if (!string.IsNullOrEmpty(ele.Value)) - { - if (!string.IsNullOrEmpty(ele.Key)) - text.SetBinding(TextBlock.ForegroundProperty, GetBindings(ele.Value + "Foreground", source, Foreground)); - text.SetBinding(TextBlock.BackgroundProperty, GetBindings(ele.Value + "Background", source)); - } + public ObservableCollection LineDetails { get; set; } + = new ObservableCollection(); - ApplyTextBlockProperties(text, source); - panel.Children.Add(text); - } + public int Count => LineDetails.Count; - if (panel.Children.Count == 0) + public void Clear() + { + LineDetails.Clear(); + } + + public void Add(DiffPiece diffPiece, string value, ChangeType changeType, UIElement source) + { + LineDetails.Add(new LineViewerLineData() { - panel.Children.Add(new TextBlock()); - } - else + Number = diffPiece.Position, + Piece = diffPiece, + Segments = new List() { + new LineViewerSegment() { + TextChunk=value, + ChunkChange=changeType, + Source=source, + FallbackForeground = Foreground + } }, + ChangeType = changeType, + Source = source, + FallbackForeground = Foreground + }); + } + + public void Add(DiffPiece diffPiece, List value, ChangeType changeType, UIElement source) + { + foreach (var item in value) { - panel.ContextMenu = LineContextMenu; + item.FallbackForeground = Foreground; } - ValuePanel.Children.Add(panel); - return panel; + LineDetails.Add(new LineViewerLineData() + { + Number = diffPiece?.Position, + Piece = diffPiece, + Segments = value, + ChangeType = changeType, + Source = source, + FallbackForeground = Foreground + }); } public void SetLineVisible(int index, bool visible) @@ -170,43 +151,23 @@ public void SetLineVisible(int index, bool visible) try { var visibility = visible ? Visibility.Visible : Visibility.Collapsed; - if (NumberPanel.Children[index] is TextBlock number) number.Visibility = visibility; - if (OperationPanel.Children[index] is TextBlock operation) operation.Visibility = visibility; - if (ValuePanel.Children[index] is StackPanel value) value.Visibility = visibility; + LineDetails[index].Visible = visibility; } catch (ArgumentOutOfRangeException) { } } - public IEnumerable GetTagsOfEachLine() - { - foreach (var item in ValuePanel.Children) - { - yield return item is StackPanel p ? p?.Tag : null; - } - } - - private Binding GetBindings(string key, UIElement source) - { - if (bindings.TryGetValue(key, out var r) && r.Source == source) return r; - return bindings[key] = new Binding(key) { Source = source, Mode = BindingMode.OneWay }; - } - - private Binding GetBindings(string key, UIElement source, object defaultValue) - { - if (bindings.TryGetValue(key, out var r) && r.Source == source) return r; - return bindings[key] = new Binding(key) { Source = source, Mode = BindingMode.OneWay, TargetNullValue = defaultValue }; - } - public void ScrollToVerticalOffset(double offset) { - ValueScrollViewer.ScrollToVerticalOffset(offset); + _ValueScrollViewer.ScrollToVerticalOffset(offset); } internal void AdjustScrollView() { - var isV = ValueScrollViewer.ComputedHorizontalScrollBarVisibility == Visibility.Visible; + if (_ValueScrollViewer == null) return; + + var isV = _ValueScrollViewer.ComputedHorizontalScrollBarVisibility == Visibility.Visible; var hasV = ValuePanel.Margin.Bottom > 10; if (isV) { @@ -218,32 +179,30 @@ internal void AdjustScrollView() } } - private void ApplyTextBlockProperties(TextBlock text, UIElement source) - { - text.SetBinding(TextBlock.FontSizeProperty, GetBindings("FontSize", source)); - text.SetBinding(TextBlock.FontFamilyProperty, GetBindings("FontFamily", source, Helper.FontFamily )); - text.SetBinding(TextBlock.FontWeightProperty, GetBindings("FontWeight", source)); - text.SetBinding(TextBlock.FontStretchProperty, GetBindings("FontStretch", source)); - text.SetBinding(TextBlock.FontStyleProperty, GetBindings("FontStyle", source)); - } - private void NumberScrollViewer_ScrollChanged(object sender, ScrollChangedEventArgs e) { - var offset = NumberScrollViewer.VerticalOffset; - ScrollVertical(ValueScrollViewer, offset); + + var offset = _NumberScrollViewer.VerticalOffset; + ScrollVertical(_ValueScrollViewer, offset); + if (ScrollChanged != null) + ScrollChanged(this, e); } private void OperationScrollViewer_ScrollChanged(object sender, ScrollChangedEventArgs e) { - var offset = OperationScrollViewer.VerticalOffset; - ScrollVertical(ValueScrollViewer, offset); + var offset = _OperationScrollViewer.VerticalOffset; + ScrollVertical(_ValueScrollViewer, offset); + if (ScrollChanged != null) + ScrollChanged(this, e); } private void ValueScrollViewer_ScrollChanged(object sender, ScrollChangedEventArgs e) { - var offset = ValueScrollViewer.VerticalOffset; - ScrollVertical(NumberScrollViewer, offset); - ScrollVertical(OperationScrollViewer, offset); + var offset = _ValueScrollViewer.VerticalOffset; + ScrollVertical(_NumberScrollViewer, offset); + ScrollVertical(_OperationScrollViewer, offset); + if (ScrollChanged != null) + ScrollChanged(this, e); } private void ScrollVertical(ScrollViewer scrollViewer, double offset) diff --git a/DiffPlex.Wpf/Controls/LineViewerLineData.cs b/DiffPlex.Wpf/Controls/LineViewerLineData.cs new file mode 100644 index 00000000..759a5675 --- /dev/null +++ b/DiffPlex.Wpf/Controls/LineViewerLineData.cs @@ -0,0 +1,321 @@ +using DiffPlex.DiffBuilder.Model; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Media; + +namespace DiffPlex.Wpf.Controls +{ + + internal class LineViewerLineData : INotifyPropertyChanged + { + private Visibility visible; + + //int? number, string operation, List> value, string changeType, UIElement source + //int? number, string operation, string value, string changeType, UIElement source + + public int? Number { get; set; } + public string Operation + { + get + { + switch (ChangeType) + { + case ChangeType.Inserted: return "+"; + case ChangeType.Deleted: return "-"; + } + return ""; + } + } + public List Segments { get; set; } + + public DiffPiece Piece { get; set; } + + + public ChangeType ChangeType { get; set; } + //TODO: Use styles properly instead of calculating color using this + public UIElement Source { get; set; } + + + public Brush FallbackForeground { get; internal set; } = null; + + public Brush ChangeBackground + { + get + { + if (Source is DiffViewer) + { + switch (ChangeType) + { + case ChangeType.Inserted: + return (SolidColorBrush)Source.GetValue(DiffViewer.InsertedBackgroundProperty); + case ChangeType.Deleted: + return (SolidColorBrush)Source.GetValue(DiffViewer.DeletedBackgroundProperty); + case ChangeType.Unchanged: + return (SolidColorBrush)Source.GetValue(DiffViewer.UnchangedBackgroundProperty); + } + } + else if (Source is InlineDiffViewer) + { + switch (ChangeType) + { + case ChangeType.Inserted: + return (SolidColorBrush)Source.GetValue(InlineDiffViewer.InsertedBackgroundProperty); + case ChangeType.Deleted: + return (SolidColorBrush)Source.GetValue(InlineDiffViewer.DeletedBackgroundProperty); + case ChangeType.Unchanged: + return (SolidColorBrush)Source.GetValue(InlineDiffViewer.UnchangedBackgroundProperty); + } + } + else if (Source is SideBySideDiffViewer) + { + switch (ChangeType) + { + case ChangeType.Inserted: + return (SolidColorBrush)Source.GetValue(SideBySideDiffViewer.InsertedBackgroundProperty); + case ChangeType.Deleted: + return (SolidColorBrush)Source.GetValue(SideBySideDiffViewer.DeletedBackgroundProperty); + case ChangeType.Unchanged: + return (SolidColorBrush)Source.GetValue(SideBySideDiffViewer.UnchangedBackgroundProperty); + } + } + + return null; + } + } + + public Brush NumberForeground + { + get + { + SolidColorBrush brushColor = null; + + if (Source is DiffViewer) + { + brushColor = (SolidColorBrush)Source.GetValue(DiffViewer.LineNumberForegroundProperty); + } + else if (Source is InlineDiffViewer) + { + brushColor = (SolidColorBrush)Source.GetValue(InlineDiffViewer.LineNumberForegroundProperty); + } + else if (Source is SideBySideDiffViewer) + { + brushColor = (SolidColorBrush)Source.GetValue(SideBySideDiffViewer.LineNumberForegroundProperty); + } + + return brushColor ?? FallbackForeground; + } + } + + public Brush ChangeForeground + { + get + { + SolidColorBrush brushColor = null; + + if (Source is DiffViewer) + { + switch (ChangeType) + { + case ChangeType.Inserted: + brushColor = (SolidColorBrush)Source.GetValue(DiffViewer.InsertedForegroundProperty); + break; + case ChangeType.Deleted: + brushColor = (SolidColorBrush)Source.GetValue(DiffViewer.DeletedForegroundProperty); + break; + case ChangeType.Unchanged: + brushColor = (SolidColorBrush)Source.GetValue(DiffViewer.UnchangedForegroundProperty); + break; + case ChangeType.Modified: + brushColor = (SolidColorBrush)Source.GetValue(DiffViewer.ChangeTypeForegroundProperty); + break; + } + } + else if (Source is InlineDiffViewer) + { + switch (ChangeType) + { + case ChangeType.Inserted: + brushColor = (SolidColorBrush)Source.GetValue(InlineDiffViewer.InsertedForegroundProperty); + break; + case ChangeType.Deleted: + brushColor = (SolidColorBrush)Source.GetValue(InlineDiffViewer.DeletedForegroundProperty); + break; + case ChangeType.Unchanged: + brushColor = (SolidColorBrush)Source.GetValue(InlineDiffViewer.UnchangedForegroundProperty); + break; + case ChangeType.Modified: + brushColor = (SolidColorBrush)Source.GetValue(InlineDiffViewer.ChangeTypeForegroundProperty); + break; + } + } + else if (Source is SideBySideDiffViewer) + { + switch (ChangeType) + { + case ChangeType.Inserted: + brushColor = (SolidColorBrush)Source.GetValue(SideBySideDiffViewer.InsertedForegroundProperty); + break; + case ChangeType.Deleted: + brushColor = (SolidColorBrush)Source.GetValue(SideBySideDiffViewer.DeletedForegroundProperty); + break; + case ChangeType.Unchanged: + brushColor = (SolidColorBrush)Source.GetValue(SideBySideDiffViewer.UnchangedForegroundProperty); + break; + case ChangeType.Modified: + brushColor = (SolidColorBrush)Source.GetValue(SideBySideDiffViewer.ChangeTypeForegroundProperty); + break; + } + } + + return brushColor ?? FallbackForeground; + } + } + + public Visibility Visible + { + get => visible; + internal set + { + visible = value; + RaisePropertyChanged(nameof(Visible)); + } + } + + public event PropertyChangedEventHandler PropertyChanged; + private void RaisePropertyChanged(string propertyName) + { + if (PropertyChanged != null) + { + PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); + } + } + } + + public class LineViewerSegment + { + public string TextChunk { get; set; } + public ChangeType ChunkChange { get; set; } + + + + public UIElement Source { get; set; } + public Brush FallbackForeground { get; internal set; } = null; + + public Brush SegmentBackground + { + get + { + if (Source is DiffViewer) + { + switch (ChunkChange) + { + case ChangeType.Inserted: + return (SolidColorBrush)Source.GetValue(DiffViewer.InsertedBackgroundProperty); + case ChangeType.Deleted: + return (SolidColorBrush)Source.GetValue(DiffViewer.DeletedBackgroundProperty); + case ChangeType.Unchanged: + return (SolidColorBrush)Source.GetValue(DiffViewer.UnchangedBackgroundProperty); + } + } + else if (Source is InlineDiffViewer) + { + switch (ChunkChange) + { + case ChangeType.Inserted: + return (SolidColorBrush)Source.GetValue(InlineDiffViewer.InsertedBackgroundProperty); + case ChangeType.Deleted: + return (SolidColorBrush)Source.GetValue(InlineDiffViewer.DeletedBackgroundProperty); + case ChangeType.Unchanged: + return (SolidColorBrush)Source.GetValue(InlineDiffViewer.UnchangedBackgroundProperty); + } + } + else if (Source is SideBySideDiffViewer) + { + switch (ChunkChange) + { + case ChangeType.Inserted: + return (SolidColorBrush)Source.GetValue(SideBySideDiffViewer.InsertedBackgroundProperty); + case ChangeType.Deleted: + return (SolidColorBrush)Source.GetValue(SideBySideDiffViewer.DeletedBackgroundProperty); + case ChangeType.Unchanged: + return (SolidColorBrush)Source.GetValue(SideBySideDiffViewer.UnchangedBackgroundProperty); + } + } + + return null; + } + } + public Brush SegmentForeground + { + get + { + if (string.IsNullOrEmpty(TextChunk)) + return null; + + SolidColorBrush brushColor = null; + + if (Source is DiffViewer) + { + switch (ChunkChange) + { + case ChangeType.Inserted: + brushColor = (SolidColorBrush)Source.GetValue(DiffViewer.InsertedForegroundProperty); + break; + case ChangeType.Deleted: + brushColor = (SolidColorBrush)Source.GetValue(DiffViewer.DeletedForegroundProperty); + break; + case ChangeType.Unchanged: + brushColor = (SolidColorBrush)Source.GetValue(DiffViewer.UnchangedForegroundProperty); + break; + case ChangeType.Modified: + brushColor = (SolidColorBrush)Source.GetValue(DiffViewer.ChangeTypeForegroundProperty); + break; + } + } + else if (Source is InlineDiffViewer) + { + switch (ChunkChange) + { + case ChangeType.Inserted: + brushColor = (SolidColorBrush)Source.GetValue(InlineDiffViewer.InsertedForegroundProperty); + break; + case ChangeType.Deleted: + brushColor = (SolidColorBrush)Source.GetValue(InlineDiffViewer.DeletedForegroundProperty); + break; + case ChangeType.Unchanged: + brushColor = (SolidColorBrush)Source.GetValue(InlineDiffViewer.UnchangedForegroundProperty); + break; + case ChangeType.Modified: + brushColor = (SolidColorBrush)Source.GetValue(InlineDiffViewer.ChangeTypeForegroundProperty); + break; + } + } + else if (Source is SideBySideDiffViewer) + { + switch (ChunkChange) + { + case ChangeType.Inserted: + brushColor = (SolidColorBrush)Source.GetValue(SideBySideDiffViewer.InsertedForegroundProperty); + break; + case ChangeType.Deleted: + brushColor = (SolidColorBrush)Source.GetValue(SideBySideDiffViewer.DeletedForegroundProperty); + break; + case ChangeType.Unchanged: + brushColor = (SolidColorBrush)Source.GetValue(SideBySideDiffViewer.UnchangedForegroundProperty); + break; + case ChangeType.Modified: + brushColor = (SolidColorBrush)Source.GetValue(SideBySideDiffViewer.ChangeTypeForegroundProperty); + break; + } + } + + return brushColor ?? FallbackForeground; + } + } + } +}