From ce543adbcea2a03a67728f6e060f782d13086e31 Mon Sep 17 00:00:00 2001 From: leo Date: Fri, 13 Feb 2026 11:31:53 +0800 Subject: [PATCH] ux: pixel perfect rendering for lines Signed-off-by: leo --- src/Views/Blame.axaml.cs | 2 +- src/Views/CommitMessageToolBox.axaml.cs | 4 +++- src/Views/MergeConflictEditor.axaml.cs | 14 +++++++++----- src/Views/TextDiffView.axaml.cs | 18 +++++++++++++----- 4 files changed, 26 insertions(+), 12 deletions(-) diff --git a/src/Views/Blame.axaml.cs b/src/Views/Blame.axaml.cs index 8f68826a..c23dc311 100644 --- a/src/Views/Blame.axaml.cs +++ b/src/Views/Blame.axaml.cs @@ -245,7 +245,7 @@ namespace SourceGit.Views public override void Render(DrawingContext context) { var pen = new Pen(_editor.BorderBrush); - context.DrawLine(pen, new Point(0, 0), new Point(0, Bounds.Height)); + context.DrawLine(pen, new Point(0.5, 0), new Point(0.5, Bounds.Height)); } protected override Size MeasureOverride(Size availableSize) diff --git a/src/Views/CommitMessageToolBox.axaml.cs b/src/Views/CommitMessageToolBox.axaml.cs index 73bca38f..d5d9a90c 100644 --- a/src/Views/CommitMessageToolBox.axaml.cs +++ b/src/Views/CommitMessageToolBox.axaml.cs @@ -136,6 +136,7 @@ namespace SourceGit.Views base.Render(context); var w = Bounds.Width; + var pixelHeight = PixelSnapHelpers.GetPixelSize(this).Height; var pen = new Pen(SubjectLineBrush) { DashStyle = DashStyle.Dash }; if (SubjectLength == 0) @@ -153,7 +154,7 @@ namespace SourceGit.Views context.DrawText(formatted, new Point(4, 2)); - var y = 6 + formatted.Height; + var y = PixelSnapHelpers.PixelAlign(6 + formatted.Height, pixelHeight); context.DrawLine(pen, new Point(0, y), new Point(w, y)); } @@ -183,6 +184,7 @@ namespace SourceGit.Views if (line.FirstDocumentLine.LineNumber == _subjectEndLine) { var y = line.GetTextLineVisualYPosition(line.TextLines[^1], VisualYPosition.LineBottom) - view.VerticalOffset + 4; + y = PixelSnapHelpers.PixelAlign(y, pixelHeight); context.DrawLine(pen, new Point(0, y), new Point(w, y)); return; } diff --git a/src/Views/MergeConflictEditor.axaml.cs b/src/Views/MergeConflictEditor.axaml.cs index 4831755e..5ee5a6fd 100644 --- a/src/Views/MergeConflictEditor.axaml.cs +++ b/src/Views/MergeConflictEditor.axaml.cs @@ -95,7 +95,7 @@ namespace SourceGit.Views public override void Render(DrawingContext context) { var pen = new Pen(Brushes.DarkGray); - context.DrawLine(pen, new Point(0, 0), new Point(0, Bounds.Height)); + context.DrawLine(pen, new Point(0.5, 0), new Point(0.5, Bounds.Height)); } protected override Size MeasureOverride(Size availableSize) @@ -150,6 +150,7 @@ namespace SourceGit.Views return; var width = textView.Bounds.Width; + var pixelHeight = PixelSnapHelpers.GetPixelSize(_presenter).Height; foreach (var line in textView.VisualLines) { if (line.IsDisposed || line.FirstDocumentLine == null || line.FirstDocumentLine.IsDeleted) @@ -168,15 +169,18 @@ namespace SourceGit.Views var endY = line.GetTextLineVisualYPosition(line.TextLines[^1], VisualYPosition.LineBottom) - textView.VerticalOffset; var rect = new Rect(0, startY, width, endY - startY); + var alignedTop = PixelSnapHelpers.PixelAlign(startY, pixelHeight); + var alignedBottom = PixelSnapHelpers.PixelAlign(endY, pixelHeight); + var lineState = vm.GetLineState(lineIndex); if (lineState == Models.ConflictLineState.ConflictBlockStart) - drawingContext.DrawLine(new Pen(new SolidColorBrush(Colors.Red, 0.6)), new Point(0, startY + 0.5), new Point(width, startY + 0.5)); + drawingContext.DrawLine(new Pen(new SolidColorBrush(Colors.Red, 0.6)), new Point(0, alignedTop), new Point(width, alignedTop)); else if (lineState == Models.ConflictLineState.ConflictBlockEnd) - drawingContext.DrawLine(new Pen(new SolidColorBrush(Colors.Red, 0.6)), new Point(0, endY - 0.5), new Point(width, endY - 0.5)); + drawingContext.DrawLine(new Pen(new SolidColorBrush(Colors.Red, 0.6)), new Point(0, alignedBottom), new Point(width, alignedBottom)); else if (lineState == Models.ConflictLineState.ResolvedBlockStart) - drawingContext.DrawLine(new Pen(new SolidColorBrush(Colors.Green, 0.6)), new Point(0, startY + 0.5), new Point(width, startY + 0.5)); + drawingContext.DrawLine(new Pen(new SolidColorBrush(Colors.Green, 0.6)), new Point(0, alignedTop), new Point(width, alignedTop)); else if (lineState == Models.ConflictLineState.ResolvedBlockEnd) - drawingContext.DrawLine(new Pen(new SolidColorBrush(Colors.Green, 0.6)), new Point(0, endY - 0.5), new Point(width, endY - 0.5)); + drawingContext.DrawLine(new Pen(new SolidColorBrush(Colors.Green, 0.6)), new Point(0, alignedBottom), new Point(width, alignedBottom)); if (lineState >= Models.ConflictLineState.ResolvedBlockStart) drawingContext.DrawRectangle(new SolidColorBrush(Colors.Green, 0.1), null, rect); diff --git a/src/Views/TextDiffView.axaml.cs b/src/Views/TextDiffView.axaml.cs index b30ceeb9..516b6e8e 100644 --- a/src/Views/TextDiffView.axaml.cs +++ b/src/Views/TextDiffView.axaml.cs @@ -203,6 +203,7 @@ namespace SourceGit.Views var lines = _presenter.GetLines(); var width = textView.Bounds.Width; + var pixelHeight = PixelSnapHelpers.GetPixelSize(textView).Height; foreach (var line in textView.VisualLines) { if (line.IsDisposed || line.FirstDocumentLine == null || line.FirstDocumentLine.IsDeleted) @@ -265,10 +266,16 @@ namespace SourceGit.Views continue; if (index == changeBlock.Start) - drawingContext.DrawLine(changeBlockBorder, new Point(0, startY), new Point(width, startY)); + { + var alignedY = PixelSnapHelpers.PixelAlign(startY, pixelHeight); + drawingContext.DrawLine(changeBlockBorder, new Point(0, alignedY), new Point(width, alignedY)); + } if (index == changeBlock.End) - drawingContext.DrawLine(changeBlockBorder, new Point(0, endY), new Point(width, endY)); + { + var alignedY = PixelSnapHelpers.PixelAlign(endY, pixelHeight); + drawingContext.DrawLine(changeBlockBorder, new Point(0, alignedY), new Point(width, alignedY)); + } } } @@ -488,10 +495,11 @@ namespace SourceGit.Views var brush = new SolidColorBrush(color, 0.1); var pen = new Pen(color.ToUInt32()); var rect = new Rect(0, chunk.Y, Bounds.Width, chunk.Height); + var aligned = PixelSnapHelpers.PixelAlign(rect, PixelSnapHelpers.GetPixelSize(this)); - context.DrawRectangle(brush, null, rect); - context.DrawLine(pen, rect.TopLeft, rect.TopRight); - context.DrawLine(pen, rect.BottomLeft, rect.BottomRight); + context.DrawRectangle(brush, null, aligned); + context.DrawLine(pen, aligned.TopLeft, aligned.TopRight); + context.DrawLine(pen, aligned.BottomLeft, aligned.BottomRight); } protected override void OnLoaded(RoutedEventArgs e)