Skip to content

Commit

Permalink
Blame: Display author avatar and a recency marker in the gutter
Browse files Browse the repository at this point in the history
+ add a setting to disable it

Note: Height estimation (proposed in gitextensions#6605 (comment) )

+ Use greenish colors for heatmap with color adaptation for other themes

Color chosen from: https://colorbrewer2.org/#type=sequential&scheme=Greens&n=9
  • Loading branch information
pmiossec committed Apr 5, 2020
1 parent 6c6d884 commit b540a49
Show file tree
Hide file tree
Showing 11 changed files with 288 additions and 10 deletions.
6 changes: 6 additions & 0 deletions GitCommands/Settings/AppSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1689,6 +1689,12 @@ public static bool BlameShowOriginalFilePath
set => SetBool("Blame.ShowOriginalFilePath", value);
}

public static bool BlameShowAuthorAvatar
{
get => GetBool("Blame.ShowAuthorAvatar", true);
set => SetBool("Blame.ShowAuthorAvatar", value);
}

public static bool IsPortable()
{
return Properties.Settings.Default.IsPortable;
Expand Down
2 changes: 1 addition & 1 deletion GitUI/Avatars/AvatarService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ namespace GitUI.Avatars
{
public static class AvatarService
{
private static InitialsAvatarGenerator AvatarGenerator = new InitialsAvatarGenerator();
private static readonly InitialsAvatarGenerator AvatarGenerator = new InitialsAvatarGenerator();
public static IAvatarProvider Default { get; }
= new BackupAvatarProvider(new AvatarMemoryCache(new AvatarPersistentCache(new AvatarDownloader(AvatarGenerator), AvatarGenerator)),
Images.User80);
Expand Down
12 changes: 11 additions & 1 deletion GitUI/CommandsDialogs/FormFileHistory.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions GitUI/CommandsDialogs/FormFileHistory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ public FormFileHistory(GitUICommands commands, string fileName, GitRevision revi
detectMoveAndCopyInAllFilesToolStripMenuItem.Checked = AppSettings.DetectCopyInAllOnBlame;
detectMoveAndCopyInThisFileToolStripMenuItem.Checked = AppSettings.DetectCopyInFileOnBlame;
displayAuthorFirstToolStripMenuItem.Checked = AppSettings.BlameDisplayAuthorFirst;
showAuthorAvatarToolStripMenuItem.Checked = AppSettings.BlameShowAuthorAvatar;
showAuthorToolStripMenuItem.Checked = AppSettings.BlameShowAuthor;
showAuthorDateToolStripMenuItem.Checked = AppSettings.BlameShowAuthorDate;
showAuthorTimeToolStripMenuItem.Checked = AppSettings.BlameShowAuthorTime;
Expand Down Expand Up @@ -704,5 +705,12 @@ private void showOriginalFilePathToolStripMenuItem_Click(object sender, EventArg
showOriginalFilePathToolStripMenuItem.Checked = AppSettings.BlameShowOriginalFilePath;
UpdateSelectedFileViewers(true);
}

private void showAuthorAvatarToolStripMenuItem_Click(object sender, EventArgs e)
{
AppSettings.BlameShowAuthorAvatar = !AppSettings.BlameShowAuthorAvatar;
showAuthorAvatarToolStripMenuItem.Checked = AppSettings.BlameShowAuthorAvatar;
UpdateSelectedFileViewers(true);
}
}
}
110 changes: 110 additions & 0 deletions GitUI/Editor/BlameAuthorMargin.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using GitExtUtils.GitUI;
using ICSharpCode.TextEditor;

namespace GitUI.Editor
{
/// <summary>
/// This class display avatars in the gutter in a blame control.
/// </summary>
public class BlameAuthorMargin : AbstractMargin
{
private static readonly int RecencyIndexWidth = Convert.ToInt32(4 * DpiUtil.ScaleX);
private List<Image> _avatars;
private readonly int _lineHeight;
private readonly Color _backgroundColor;
private List<GitBlameEntry> _blameLines;
private readonly Dictionary<int, SolidBrush> _brushs = new Dictionary<int, SolidBrush>();
private bool _isVisible = true;

public BlameAuthorMargin(TextArea textArea) : base(textArea)
{
_lineHeight = GetFontHeight(textArea.Font);
_backgroundColor = SystemColors.Window;
Width = _lineHeight + RecencyIndexWidth + DpiUtil.Scale(2);
}

public override int Width { get; }
public override bool IsVisible => _isVisible;

private static int GetFontHeight(Font font)
{
var max = Math.Max(
TextRenderer.MeasureText("_", font).Height,
(int)Math.Ceiling(font.GetHeight()));

return max + 1;
}

public void Initialize(List<GitBlameEntry> blameLines)
{
_blameLines = blameLines;
_avatars = blameLines.Select(a => a.Avatar).ToList();

// Update the resolution otherwise the image is not drawn at the good size :(
foreach (var avatar in _avatars)
{
if (avatar is Bitmap bitmapAvatar)
{
bitmapAvatar.SetResolution(DpiUtil.DpiX, DpiUtil.DpiY);
}
}

// Build brushes
foreach (var blameLine in blameLines)
{
if (!_brushs.ContainsKey(blameLine.AgeBucketIndex))
{
_brushs.Add(blameLine.AgeBucketIndex, new SolidBrush(blameLine.AgeBucketColor));
}
}
}

public void SetVisiblity(bool isVisible)
{
_isVisible = isVisible;
}

public override void Paint(Graphics g, Rectangle rect)
{
if (rect.Width <= 0 || rect.Height <= 0)
{
return;
}

g.Clear(_backgroundColor);

if (_avatars == null || _avatars.Count == 0)
{
return;
}

var verticalOffset = textArea.VirtualTop.Y;
var lineStart = verticalOffset / _lineHeight;
var negativeOffset = (lineStart * _lineHeight) - verticalOffset;
var lineCount = (int)Math.Ceiling((double)(rect.Height - negativeOffset) / _lineHeight);

for (int i = 0; i < lineCount; i++)
{
if (lineStart + i >= _avatars.Count)
{
break;
}

int y = negativeOffset + (i * _lineHeight);
g.FillRectangle(_brushs[_blameLines[lineStart + i].AgeBucketIndex], 0, y, RecencyIndexWidth, _lineHeight);

if (_avatars[lineStart + i] != null)
{
g.DrawImage(_avatars[lineStart + i], new Point(RecencyIndexWidth, y));
}
}

base.Paint(g, rect);
}
}
}
11 changes: 11 additions & 0 deletions GitUI/Editor/FileViewer.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.IO;
Expand Down Expand Up @@ -1618,5 +1619,15 @@ public bool ShowSyntaxHighlightingInDiff
public ToolStripButton IgnoreAllWhitespacesButton => _fileViewer.ignoreAllWhitespaces;
public ToolStripMenuItem IgnoreAllWhitespacesMenuItem => _fileViewer.ignoreAllWhitespaceChangesToolStripMenuItem;
}

public void SetGitBlameGutter(List<GitBlameEntry> gitBlameEntries)
{
internalFileViewer.ShowGutterAvatars = AppSettings.BlameShowAuthorAvatar;

if (AppSettings.BlameShowAuthorAvatar)
{
internalFileViewer.SetGitBlameGutter(gitBlameEntries);
}
}
}
}
35 changes: 35 additions & 0 deletions GitUI/Editor/FileViewerInternal.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ public partial class FileViewerInternal : GitModuleControl, IFileViewer
private readonly CurrentViewPositionCache _currentViewPositionCache;
private DiffViewerLineNumberControl _lineNumbersControl;
private DiffHighlightService _diffHighlightService = DiffHighlightService.Instance;
private BlameAuthorMargin _authorsAvatarMargin;
private bool _showGutterAvatars;

public FileViewerInternal()
{
Expand Down Expand Up @@ -487,6 +489,39 @@ private void OnVScrollPositionChanged(EventArgs e)

#endregion

public void SetGitBlameGutter(List<GitBlameEntry> gitBlameEntries)
{
if (_showGutterAvatars)
{
_authorsAvatarMargin.Initialize(gitBlameEntries);
}
}

public bool ShowGutterAvatars
{
get => _showGutterAvatars;
set
{
_showGutterAvatars = value;
if (!_showGutterAvatars)
{
_authorsAvatarMargin?.SetVisiblity(false);

return;
}

if (_authorsAvatarMargin == null)
{
_authorsAvatarMargin = new BlameAuthorMargin(TextEditor.ActiveTextAreaControl.TextArea);
TextEditor.ActiveTextAreaControl.TextArea.InsertLeftMargin(0, _authorsAvatarMargin);
}
else
{
_authorsAvatarMargin.SetVisiblity(true);
}
}
}

internal sealed class CurrentViewPositionCache
{
private readonly FileViewerInternal _viewer;
Expand Down
11 changes: 11 additions & 0 deletions GitUI/Editor/GitBlameEntry.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System.Drawing;

namespace GitUI.Editor
{
public class GitBlameEntry
{
public Image Avatar { get; set; }
public int AgeBucketIndex { get; set; }
public Color AgeBucketColor { get; set; }
}
}
4 changes: 4 additions & 0 deletions GitUI/Translation/English.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -4288,6 +4288,10 @@ If you are not sure just close this window.</source>
<source>Save as</source>
<target />
</trans-unit>
<trans-unit id="showAuthorAvatarToolStripMenuItem.Text">
<source>Show author avatar</source>
<target />
</trans-unit>
<trans-unit id="showAuthorDateToolStripMenuItem.Text">
<source>Show author date</source>
<target />
Expand Down
Loading

0 comments on commit b540a49

Please sign in to comment.