Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions src/Converters/CommitGraphStyleConverters.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using System.Collections.Generic;

using Avalonia;
using Avalonia.Data.Converters;

namespace SourceGit.Converters
{
public static class CommitGraphStyleConverters
{
public static readonly FuncValueConverter<Models.CommitGraphStyle, bool> IsClassic =
new(v => v == Models.CommitGraphStyle.Classic);

public static readonly FuncValueConverter<Models.CommitGraphStyle, bool> IsAvatars =
new(v => v == Models.CommitGraphStyle.Avatars);

public static readonly FuncValueConverter<Models.CommitGraphStyle, double> ToRowHeight =
new(v => v == Models.CommitGraphStyle.Avatars ? 28 : 26);

// Places a hit-area centered on the commit's avatar in the graph column.
public static readonly FuncValueConverter<double, Thickness> ToAvatarHitAreaMargin =
new(v => new Thickness(Views.CommitGraph.GetAvatarsStyleX(v) - 12, 0, 0, 0));

// Values: [Commit.LeftMargin, CommitGraphStyle]. Only the classic style draws the graph under the subject.
public static readonly FuncMultiValueConverter<object, Thickness> ToSubjectMargin =
new(v =>
{
var values = new List<object>(v);
if (values.Count == 2 && values[0] is double margin && values[1] is Models.CommitGraphStyle.Classic)
return new Thickness(margin, 0, 0, 0);

return new Thickness(4, 0, 0, 0);
});
}
}
6 changes: 1 addition & 5 deletions src/Converters/DoubleConverters.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using Avalonia;
using Avalonia.Data.Converters;
using Avalonia.Data.Converters;

namespace SourceGit.Converters
{
Expand All @@ -16,8 +15,5 @@ public static class DoubleConverters

public static readonly FuncValueConverter<double, string> OneMinusToPercentage =
new FuncValueConverter<double, string>(v => ((1.0 - v) * 100).ToString("F0") + "%");

public static readonly FuncValueConverter<double, Thickness> ToLeftMargin =
new FuncValueConverter<double, Thickness>(v => new Thickness(v, 0, 0, 0));
}
}
1 change: 1 addition & 0 deletions src/Models/Commit.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public class Commit : ObservableObject
public bool IsMerged { get; set; } = false;
public int Color { get; set; } = 0;
public double LeftMargin { get; set; } = 0;
public double GraphNodeX { get; set; } = 0;

public bool IsHighlightedInGraph
{
Expand Down
14 changes: 12 additions & 2 deletions src/Models/CommitGraph.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

namespace SourceGit.Models
{
public record CommitGraphLayout(double StartY, double ClipWidth, double RowHeight);
public record CommitGraphLayout(double StartY, double OffsetX, double ClipWidth, double RowHeight);

public enum CommitGraphHighlighting
{
Expand All @@ -17,6 +17,12 @@ public enum CommitGraphHighlighting
SelectedCommitsOnlyFirstParent,
}

public enum CommitGraphStyle
{
Classic = 0,
Avatars,
}

public class CommitGraph
{
public static List<Pen> Pens { get; } = [];
Expand Down Expand Up @@ -65,11 +71,13 @@ public class Dot
public Point Center;
public int Color;
public bool IsHighlighted;
public Commit Commit;
}

public List<Path> Paths { get; } = [];
public List<Link> Links { get; } = [];
public List<Dot> Dots { get; } = [];
public double Width { get; private set; } = 0;

public static CommitGraph Generate(List<Commit> commits, bool firstParentOnlyEnabled, CommitGraphHighlighting highlighting, HashSet<string> highlightExtraCommits)
{
Expand Down Expand Up @@ -198,7 +206,7 @@ public static CommitGraph Generate(List<Commit> commits, bool firstParentOnlyEna
// Calculate link position of this commit.
var position = new Point(major?.LastX ?? offsetX, offsetY);
var dotColor = major?.Path.Color ?? 0;
var anchor = new Dot() { Center = position, Color = dotColor, IsHighlighted = isHighlighted };
var anchor = new Dot() { Center = position, Color = dotColor, IsHighlighted = isHighlighted, Commit = commit };
if (commit.IsCurrentHead)
anchor.Type = DotType.Head;
else if (commit.Parents.Count > 1)
Expand Down Expand Up @@ -249,7 +257,9 @@ public static CommitGraph Generate(List<Commit> commits, bool firstParentOnlyEna

// Margins & colors (used by Views.Histories).
commit.Color = dotColor;
commit.GraphNodeX = position.X;
commit.LeftMargin = Math.Max(offsetX, maxOffsetOld) + halfWidth + 2;
temp.Width = Math.Max(temp.Width, commit.LeftMargin);
}

// Deal with curves haven't ended yet.
Expand Down
18 changes: 18 additions & 0 deletions src/Models/RepositoryUIStates.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,24 @@ public double AuthorColumnWidth
set;
} = 120;

public bool IsRefsColumnVisibleInHistory
{
get;
set;
} = true;

public double RefsColumnWidth
{
get;
set;
} = 180;

public double GraphColumnWidth
{
get;
set;
} = 0;

public bool EnableTopoOrderInHistory
{
get;
Expand Down
6 changes: 6 additions & 0 deletions src/Resources/Locales/en_US.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -517,9 +517,12 @@
<x:String x:Key="Text.Histories.AdvancedOptions" xml:space="preserve">Advanced Options</x:String>
<x:String x:Key="Text.Histories.Header.Author" xml:space="preserve">AUTHOR</x:String>
<x:String x:Key="Text.Histories.Header.AuthorTime" xml:space="preserve">AUTHOR TIME</x:String>
<x:String x:Key="Text.Histories.Header.BranchesAndTags" xml:space="preserve">BRANCH / TAG</x:String>
<x:String x:Key="Text.Histories.Header.CommitTime" xml:space="preserve">COMMIT TIME</x:String>
<x:String x:Key="Text.Histories.Header.Graph" xml:space="preserve">GRAPH</x:String>
<x:String x:Key="Text.Histories.Header.GraphAndSubject" xml:space="preserve">GRAPH &amp; SUBJECT</x:String>
<x:String x:Key="Text.Histories.Header.SHA" xml:space="preserve">SHA</x:String>
<x:String x:Key="Text.Histories.Header.Subject" xml:space="preserve">COMMIT MESSAGE</x:String>
<x:String x:Key="Text.Histories.HighlightsInGraph" xml:space="preserve">HIGHLIGHTS IN GRAPH</x:String>
<x:String x:Key="Text.Histories.HighlightsInGraph.All" xml:space="preserve">All</x:String>
<x:String x:Key="Text.Histories.HighlightsInGraph.CurrentBranchOnly" xml:space="preserve">Current Branch Only</x:String>
Expand Down Expand Up @@ -674,6 +677,9 @@
<x:String x:Key="Text.Preferences.AI.ReasoningEffortLevel" xml:space="preserve">Reasoning Effort Level</x:String>
<x:String x:Key="Text.Preferences.AI.Server" xml:space="preserve">Server</x:String>
<x:String x:Key="Text.Preferences.Appearance" xml:space="preserve">APPEARANCE</x:String>
<x:String x:Key="Text.Preferences.Appearance.CommitGraphStyle" xml:space="preserve">Commit Graph Style</x:String>
<x:String x:Key="Text.Preferences.Appearance.CommitGraphStyle.Avatars" xml:space="preserve">Avatars (branches, graph and message in separate columns)</x:String>
<x:String x:Key="Text.Preferences.Appearance.CommitGraphStyle.Classic" xml:space="preserve">Classic</x:String>
<x:String x:Key="Text.Preferences.Appearance.DefaultFont" xml:space="preserve">Default Font</x:String>
<x:String x:Key="Text.Preferences.Appearance.EditorTabWidth" xml:space="preserve">Editor Tab Width</x:String>
<x:String x:Key="Text.Preferences.Appearance.FontSize" xml:space="preserve">Font Size</x:String>
Expand Down
28 changes: 28 additions & 0 deletions src/ViewModels/Histories.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,19 @@ public bool IsAuthorColumnVisible
}
}

public bool IsRefsColumnVisible
{
get => _repo.UIStates.IsRefsColumnVisibleInHistory;
set
{
if (_repo.UIStates.IsRefsColumnVisibleInHistory != value)
{
_repo.UIStates.IsRefsColumnVisibleInHistory = value;
OnPropertyChanged();
}
}
}

public bool IsSHAColumnVisible
{
get => _repo.UIStates.IsSHAColumnVisibleInHistory;
Expand Down Expand Up @@ -210,6 +223,21 @@ public double AuthorColumnWidth
set => _repo.UIStates.AuthorColumnWidth = value;
}

public double RefsColumnWidth
{
get => _repo.UIStates.RefsColumnWidth;
set => _repo.UIStates.RefsColumnWidth = value;
}

/// <summary>
/// Width of the graph column in `Avatars` style. Zero means fit to the graph.
/// </summary>
public double GraphColumnWidth
{
get => _repo.UIStates.GraphColumnWidth;
set => _repo.UIStates.GraphColumnWidth = value;
}

public bool IsOpenAsStandaloneVisible
{
get => DetailContext is CommitDetail or RevisionCompare;
Expand Down
7 changes: 7 additions & 0 deletions src/ViewModels/Preferences.cs
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,12 @@ public bool UseCompactBranchNamesInGraph
set => SetProperty(ref _useCompactBranchNamesInGraph, value);
}

public Models.CommitGraphStyle CommitGraphStyle
{
get => _commitGraphStyle;
set => SetProperty(ref _commitGraphStyle, value);
}

public bool UseTwoColumnsLayoutInHistories
{
get => _useTwoColumnsLayoutInHistories;
Expand Down Expand Up @@ -824,6 +830,7 @@ private bool RemoveInvalidRepositoriesRecursive(List<RepositoryNode> collection)
private bool _useAutoHideScrollBars = true;
private bool _useGitHubStyleAvatar = true;
private bool _useCompactBranchNamesInGraph = true;
private Models.CommitGraphStyle _commitGraphStyle = Models.CommitGraphStyle.Classic;

private bool _check4UpdatesOnStartup = true;
private double _lastCheckUpdateTime = 0;
Expand Down
57 changes: 31 additions & 26 deletions src/Views/Avatar.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,31 +68,7 @@ public override void Render(DrawingContext context)
}
else if (!_useGitHubStyleAvatar)
{
var fallback = GetFallbackString(_user.Name);
var typeface = new Typeface("fonts:SourceGit#JetBrains Mono NL");
var label = new FormattedText(
fallback,
CultureInfo.CurrentCulture,
FlowDirection.LeftToRight,
typeface,
Math.Max(Bounds.Width * 0.65, 10),
Brushes.White);

var chars = fallback.ToCharArray();
var sum = 0;
foreach (var c in chars)
sum += Math.Abs(c);

var bg = new LinearGradientBrush()
{
GradientStops = FALLBACK_GRADIENTS[sum % FALLBACK_GRADIENTS.Length],
StartPoint = new RelativePoint(0, 0, RelativeUnit.Relative),
EndPoint = new RelativePoint(0, 1, RelativeUnit.Relative),
};

Point textOrigin = new Point((Bounds.Width - label.Width) * 0.5, (Bounds.Height - label.Height) * 0.5);
context.DrawRectangle(bg, null, new Rect(0, 0, Bounds.Width, Bounds.Height), corner, corner);
context.DrawText(label, textOrigin);
DrawFallback(context, _user.Name, rect, corner);
}
else
{
Expand Down Expand Up @@ -275,7 +251,36 @@ private void OnContextRequested(object sender, ContextRequestedEventArgs e)
menu.Open(this);
}

private string GetFallbackString(string name)
public static void DrawFallback(DrawingContext context, string name, Rect rect, double corner)
{
var fallback = GetFallbackString(name);
var typeface = new Typeface("fonts:SourceGit#JetBrains Mono NL");
var label = new FormattedText(
fallback,
CultureInfo.CurrentCulture,
FlowDirection.LeftToRight,
typeface,
Math.Max(rect.Width * 0.65, 10),
Brushes.White);

var chars = fallback.ToCharArray();
var sum = 0;
foreach (var c in chars)
sum += Math.Abs(c);

var bg = new LinearGradientBrush()
{
GradientStops = FALLBACK_GRADIENTS[sum % FALLBACK_GRADIENTS.Length],
StartPoint = new RelativePoint(0, 0, RelativeUnit.Relative),
EndPoint = new RelativePoint(0, 1, RelativeUnit.Relative),
};

var textOrigin = new Point(rect.X + (rect.Width - label.Width) * 0.5, rect.Y + (rect.Height - label.Height) * 0.5);
context.DrawRectangle(bg, null, rect, corner, corner);
context.DrawText(label, textOrigin);
}

private static string GetFallbackString(string name)
{
if (string.IsNullOrWhiteSpace(name))
return "?";
Expand Down
Loading