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
10 changes: 10 additions & 0 deletions src/Models/Commit.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public class Commit : ObservableObject
public string Subject { get; set; } = string.Empty;
public List<string> Parents { get; set; } = new();
public List<Decorator> Decorators { get; set; } = new();
public List<Decorator> GraphBranches { get; set; } = [];

public bool IsMerged { get; set; } = false;
public int Color { get; set; } = 0;
Expand All @@ -37,6 +38,15 @@ public bool IsHighlightedInGraph
public bool IsCommitterVisible => !Author.Equals(Committer) || AuthorTime != CommitterTime;
public bool IsCurrentHead => Decorators.Find(x => x.Type is DecoratorType.CurrentBranchHead or DecoratorType.CurrentCommitHead) != null;
public bool HasDecorators => Decorators.Count > 0;
public string BranchToolTip
{
get
{
var branches = Decorators.FindAll(x => x.IsBranch);
return branches.Count > 0 ? string.Join("\n", branches.ConvertAll(x => x.Name)) : null;
}
}

public string FirstParentToCompare => Parents.Count > 0 ? $"{SHA}^" : EmptyTreeHash.Guess(SHA);

public string GetFriendlyName()
Expand Down
8 changes: 8 additions & 0 deletions src/Models/CommitGraph.cs
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,12 @@ public static CommitGraph Generate(List<Commit> commits, bool firstParentOnlyEna
temp.Paths.Add(major.Path);
}

// Carry branch identity along the lane, independently of recycled graph colors.
var branches = commit.Decorators.FindAll(x => x.IsBranch);
commit.GraphBranches = branches.Count > 0 ? branches : major?.Branches ?? [];
if (major != null && branches.Count > 0)
major.Branches = branches;

// Calculate link position of this commit.
var position = new Point(major?.LastX ?? offsetX, offsetY);
var dotColor = major?.Path.Color ?? 0;
Expand Down Expand Up @@ -241,6 +247,7 @@ public static CommitGraph Generate(List<Commit> commits, bool firstParentOnlyEna

// Create new curve for parent commit that not includes before
var l = new PathHelper(parentHash, isHighlighted, colorPicker.Next(), position, new Point(offsetX, position.Y + halfHeight));
l.Branches = commit.GraphBranches;
unsolved.Add(l);
temp.Paths.Add(l.Path);
}
Expand Down Expand Up @@ -292,6 +299,7 @@ public void Recycle(int idx)

private class PathHelper
{
public List<Decorator> Branches { get; set; } = [];
public Path Path { get; private set; }
public string Next { get; set; }
public double LastX { get; private set; }
Expand Down
1 change: 1 addition & 0 deletions src/Models/Decorator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ public class Decorator
public DecoratorType Type { get; set; } = DecoratorType.None;
public string Name { get; set; } = "";
public bool IsTag => Type == DecoratorType.Tag;
public bool IsBranch => Type is DecoratorType.CurrentBranchHead or DecoratorType.LocalBranchHead or DecoratorType.RemoteBranchHead;
}
}
4 changes: 4 additions & 0 deletions src/Models/RepositoryUIStates.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ public HistoryShowFlags HistoryShowFlags
set;
} = HistoryShowFlags.None;

public bool IsBranchesColumnVisibleInHistory { get; set; } = false;

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

public bool IsAuthorColumnVisibleInHistory
{
get;
Expand Down
1 change: 1 addition & 0 deletions src/Resources/Locales/en_US.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,7 @@
<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.Branches" xml:space="preserve">BRANCHES</x:String>
<x:String x:Key="Text.Histories.Header.CommitTime" xml:space="preserve">COMMIT TIME</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>
Expand Down
19 changes: 19 additions & 0 deletions src/ViewModels/Histories.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,19 @@ public bool EnableTopoOrder
}
}

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

public bool IsAuthorColumnVisible
{
get => _repo.UIStates.IsAuthorColumnVisibleInHistory;
Expand Down Expand Up @@ -204,6 +217,12 @@ public GridLength BottomArea
}
}

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

public double AuthorColumnWidth
{
get => _repo.UIStates.AuthorColumnWidth;
Expand Down
60 changes: 60 additions & 0 deletions src/Views/CommitBranchConnections.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
using System.Collections.Generic;

using Avalonia;
using Avalonia.Controls;
using Avalonia.Media;

namespace SourceGit.Views
{
public record CommitBranchConnection(Point Start, Point End, IBrush Brush, bool IsActive, double TextGapWidth = 0);

public class CommitBranchConnections : Control
{
public IReadOnlyList<CommitBranchConnection> Connections => _connections;

public void UpdateConnections(List<CommitBranchConnection> connections)
{
if (_connections.Count == connections.Count)
{
var unchanged = true;
for (var i = 0; i < connections.Count; i++)
unchanged &= _connections[i] == connections[i];
if (unchanged)
return;
}

_connections = connections;
InvalidateVisual();
}

public override void Render(DrawingContext context)
{
base.Render(context);

using (context.PushClip(new Rect(Bounds.Size)))
{
foreach (var connection in _connections)
{
using (context.PushOpacity(connection.IsActive ? 1 : 0.7))
{
var pen = new Pen(connection.Brush, 1);
if (connection.TextGapWidth > 0)
{
// Leave the plain-text branch count readable between the badge and the line.
context.DrawLine(pen, connection.Start, new Point(System.Math.Min(connection.Start.X + 2, connection.End.X), connection.Start.Y));
var resumeX = connection.Start.X + connection.TextGapWidth;
if (resumeX < connection.End.X)
context.DrawLine(pen, new Point(resumeX, connection.Start.Y), connection.End);
}
else
{
context.DrawLine(pen, connection.Start, connection.End);
}
}
}
}
}

private List<CommitBranchConnection> _connections = [];
}
}
57 changes: 57 additions & 0 deletions src/Views/CommitMessagePanel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
using System;

using Avalonia;
using Avalonia.Controls;

namespace SourceGit.Views
{
public class CommitMessagePanel : Panel
{
public static readonly StyledProperty<bool> RefsAfterSubjectProperty =
AvaloniaProperty.Register<CommitMessagePanel, bool>(nameof(RefsAfterSubject));

static CommitMessagePanel()
{
AffectsMeasure<CommitMessagePanel>(RefsAfterSubjectProperty);
}

public bool RefsAfterSubject
{
get => GetValue(RefsAfterSubjectProperty);
set => SetValue(RefsAfterSubjectProperty, value);
}

protected override Size MeasureOverride(Size availableSize)
{
var refs = Children[0];
var subject = Children[1];
refs.Measure(availableSize);
var gap = RefsAfterSubject && refs.DesiredSize.Width > 0 ? 4 : 0;
subject.Measure(new Size(Math.Max(0, availableSize.Width - refs.DesiredSize.Width - gap), availableSize.Height));
return new Size(refs.DesiredSize.Width + subject.DesiredSize.Width + gap,
Math.Max(refs.DesiredSize.Height, subject.DesiredSize.Height));
}

protected override Size ArrangeOverride(Size finalSize)
{
var refs = Children[0];
var subject = Children[1];
var refsWidth = Math.Min(refs.DesiredSize.Width, finalSize.Width);
var gap = RefsAfterSubject && refsWidth > 0 ? 4 : 0;
var subjectWidth = Math.Max(0, finalSize.Width - refsWidth - gap);
if (RefsAfterSubject)
{
subjectWidth = Math.Min(subjectWidth, subject.DesiredSize.Width);
subject.Arrange(new Rect(0, 0, subjectWidth, finalSize.Height));
refs.Arrange(new Rect(subjectWidth + gap, 0, refsWidth, finalSize.Height));
}
else
{
refs.Arrange(new Rect(0, 0, refsWidth, finalSize.Height));
subject.Arrange(new Rect(refsWidth, 0, subjectWidth, finalSize.Height));
}

return finalSize;
}
}
}
Loading