From 6480905be65d69f02f9a2d63e9649c83b7fe7742 Mon Sep 17 00:00:00 2001 From: leo Date: Fri, 18 Dec 2020 18:00:41 +0800 Subject: [PATCH] refactor: using DrawingContext instead of Canvas to render commit graph; change max number of displaying commits from 8000 to 20000 --- src/Helpers/CommitGraph.cs | 125 +++++++++++++++++++++++++++++++++++-- src/UI/Dashboard.xaml.cs | 2 +- src/UI/Histories.xaml | 7 ++- src/UI/Histories.xaml.cs | 89 ++------------------------ 4 files changed, 130 insertions(+), 93 deletions(-) diff --git a/src/Helpers/CommitGraph.cs b/src/Helpers/CommitGraph.cs index 6ee2b28e..4acc3ed7 100644 --- a/src/Helpers/CommitGraph.cs +++ b/src/Helpers/CommitGraph.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Linq; using System.Windows; @@ -120,8 +121,7 @@ namespace SourceGit.Helpers { /// Dot /// public struct Dot { - public double X; - public double Y; + public Point Center; public Brush Color; } @@ -206,9 +206,9 @@ namespace SourceGit.Helpers { major.IsMerged = isMerged; position.X = major.HorizontalOffset; position.Y = offsetY; - maker.Dots.Add(new Dot() { X = position.X - 3, Y = position.Y - 3, Color = major.Brush }); + maker.Dots.Add(new Dot() { Center = position, Color = major.Brush }); } else { - maker.Dots.Add(new Dot() { X = position.X - 3, Y = position.Y - 3, Color = Brushes.Orange }); + maker.Dots.Add(new Dot() { Center = position, Color = Brushes.Orange }); } // 处理本提交的其他依赖 @@ -239,7 +239,7 @@ namespace SourceGit.Helpers { // 加入本次提交 commit.IsMerged = isMerged; - commit.GraphOffset = System.Math.Max(offsetX + HALF_WIDTH, oldCount * UNIT_WIDTH); + commit.GraphOffset = Math.Max(offsetX + HALF_WIDTH, oldCount * UNIT_WIDTH); // 清理临时数据 ended.Clear(); @@ -258,7 +258,122 @@ namespace SourceGit.Helpers { } unsolved.Clear(); + maker.Lines.Sort((l, h) => l.Points[0].Y.CompareTo(h.Points[0].Y)); return maker; } } + + /// + /// Visual element to render commit graph + /// + public class CommitGraph : FrameworkElement { + private double offsetY; + private CommitGraphMaker maker; + + public CommitGraph() { + Clear(); + } + + public void Clear() { + offsetY = 0; + maker = null; + } + + public void SetCommits(List commits) { + maker = CommitGraphMaker.Parse(commits); + Dispatcher.Invoke(() => InvalidateVisual()); + } + + public void SetOffset(double y) { + offsetY = y * CommitGraphMaker.UNIT_HEIGHT; + InvalidateVisual(); + } + + protected override void OnRender(DrawingContext dc) { + if (maker == null) return; + + var startY = offsetY; + var endY = offsetY + ActualHeight; + + dc.PushTransform(new TranslateTransform(0, -offsetY)); + + // Draw all visible lines. + foreach (var path in maker.Lines) { + var last = path.Points[0]; + var size = path.Points.Count; + + if (last.Y > endY) break; + if (path.Points[size - 1].Y < startY) continue; + + var geo = new StreamGeometry(); + var pen = new Pen(path.Brush, 2); + + using (var geoCtx = geo.Open()) { + geoCtx.BeginFigure(last, false, false); + + var ended = false; + for (int i = 1; i < size; i++) { + var cur = path.Points[i]; + + // Fix line NOT shown in graph if cur.Y is too large than current. + if (cur.Y > endY) { + cur.Y = endY; + ended = true; + } + + if (cur.X > last.X) { + geoCtx.QuadraticBezierTo(new Point(cur.X, last.Y), cur, true, false); + } else if (cur.X < last.X) { + if (i < size - 1) { + cur.Y += CommitGraphMaker.HALF_HEIGHT; + + var midY = (last.Y + cur.Y) / 2; + var midX = (last.X + cur.X) / 2; + geoCtx.PolyQuadraticBezierTo(new Point[] { + new Point(last.X, midY), + new Point(midX, midY), + new Point(cur.X, midY), + cur}, true, false); + } else { + geoCtx.QuadraticBezierTo(new Point(last.X, cur.Y), cur, true, false); + } + } else { + geoCtx.LineTo(cur, true, false); + } + + if (ended) break; + last = cur; + } + } + + geo.Freeze(); + dc.DrawGeometry(null, pen, geo); + } + + // Draw short links + foreach (var link in maker.Links) { + if (link.Start.Y > endY) break; + if (link.End.Y < startY) continue; + + var geo = new StreamGeometry(); + var pen = new Pen(link.Brush, 2); + + using (var geoCtx = geo.Open()) { + geoCtx.BeginFigure(link.Start, false, false); + geoCtx.QuadraticBezierTo(link.Control, link.End, true, false); + } + + geo.Freeze(); + dc.DrawGeometry(null, pen, geo); + } + + // Draw visible points + foreach (var dot in maker.Dots) { + if (dot.Center.Y > endY) break; + if (dot.Center.Y < startY) continue; + + dc.DrawEllipse(dot.Color, null, dot.Center, 3, 3); + } + } + } } diff --git a/src/UI/Dashboard.xaml.cs b/src/UI/Dashboard.xaml.cs index fac9ce9b..f3e170f7 100644 --- a/src/UI/Dashboard.xaml.cs +++ b/src/UI/Dashboard.xaml.cs @@ -111,7 +111,7 @@ namespace SourceGit.UI { }); Task.Run(() => { - var args = "-8000 "; + var args = "-20000 "; if (repo.LogFilters.Count > 0) { args = args + string.Join(" ", repo.LogFilters); } else { diff --git a/src/UI/Histories.xaml b/src/UI/Histories.xaml index dcdb0e7e..2719c7c9 100644 --- a/src/UI/Histories.xaml +++ b/src/UI/Histories.xaml @@ -154,8 +154,11 @@ - - + + diff --git a/src/UI/Histories.xaml.cs b/src/UI/Histories.xaml.cs index 633e92e0..a481c259 100644 --- a/src/UI/Histories.xaml.cs +++ b/src/UI/Histories.xaml.cs @@ -82,7 +82,7 @@ namespace SourceGit.UI { /// Cleanup /// public void Cleanup() { - commitGraph.Children.Clear(); + commitGraph.Clear(); commitList.ItemsSource = null; cachedCommits.Clear(); } @@ -92,91 +92,10 @@ namespace SourceGit.UI { cachedCommits = commits; if (isSearchMode) return; - var maker = Helpers.CommitGraphMaker.Parse(commits); + commitGraph.SetCommits(commits); Dispatcher.Invoke(() => { - commitGraph.Children.Clear(); - isSearchMode = false; txtSearch.Text = ""; - - // Draw all lines. - foreach (var path in maker.Lines) { - var size = path.Points.Count; - var geo = new StreamGeometry(); - var last = path.Points[0]; - - using (var ctx = geo.Open()) { - ctx.BeginFigure(last, false, false); - - for (int i = 1; i < size; i++) { - var cur = path.Points[i]; - - if (cur.X > last.X) { - ctx.QuadraticBezierTo(new Point(cur.X, last.Y), cur, true, false); - } else if (cur.X < last.X) { - if (i < size - 1) { - cur.Y += Helpers.CommitGraphMaker.HALF_HEIGHT; - - var midY = (last.Y + cur.Y) / 2; - var midX = (last.X + cur.X) / 2; - ctx.PolyQuadraticBezierTo(new Point[] { - new Point(last.X, midY), - new Point(midX, midY), - new Point(cur.X, midY), - cur - }, true, false); - } else { - ctx.QuadraticBezierTo(new Point(last.X, cur.Y), cur, true, false); - } - } else { - ctx.LineTo(cur, true, false); - } - - last = cur; - } - } - - geo.Freeze(); - - var p = new Path(); - p.Data = geo; - p.Stroke = path.Brush; - p.StrokeThickness = 2; - commitGraph.Children.Add(p); - } - maker.Lines.Clear(); - - // Draw short links - foreach (var link in maker.Links) { - var geo = new StreamGeometry(); - - using (var ctx = geo.Open()) { - ctx.BeginFigure(link.Start, false, false); - ctx.QuadraticBezierTo(link.Control, link.End, true, false); - } - - geo.Freeze(); - - var p = new Path(); - p.Data = geo; - p.Stroke = link.Brush; - p.StrokeThickness = 2; - commitGraph.Children.Add(p); - } - maker.Links.Clear(); - - // Draw points. - foreach (var dot in maker.Dots) { - var ellipse = new Ellipse(); - ellipse.Height = 6; - ellipse.Width = 6; - ellipse.Fill = dot.Color; - ellipse.SetValue(Canvas.LeftProperty, dot.X); - ellipse.SetValue(Canvas.TopProperty, dot.Y); - commitGraph.Children.Add(ellipse); - } - maker.Dots.Clear(); - commitList.ItemsSource = new List(cachedCommits); SetLoadingEnabled(false); }); @@ -188,7 +107,7 @@ namespace SourceGit.UI { foreach (var c in commits) c.GraphOffset = 0; Dispatcher.Invoke(() => { - commitGraph.Children.Clear(); + commitGraph.Clear(); commitList.ItemsSource = new List(commits); SetLoadingEnabled(false); }); @@ -268,7 +187,7 @@ namespace SourceGit.UI { #region COMMIT_DATAGRID_AND_GRAPH private void CommitListScrolled(object sender, ScrollChangedEventArgs e) { - commitGraph.Margin = new Thickness(0, -e.VerticalOffset * Helpers.CommitGraphMaker.UNIT_HEIGHT, 0, 0); + commitGraph.SetOffset(e.VerticalOffset); } private void CommitSelectChanged(object sender, SelectionChangedEventArgs e) {