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
41 changes: 41 additions & 0 deletions src/App.Extensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
using System;
using System.Collections;
using Avalonia;
using Avalonia.Controls;

namespace SourceGit
{
Expand All @@ -23,4 +26,42 @@ public static T Use<T>(this T cmd, Models.ICommandLog log) where T : Commands.Co
return cmd;
}
}

public class DataGridExtension
{
public static readonly AttachedProperty<IList> SelectedItemsProperty =
AvaloniaProperty.RegisterAttached<DataGridExtension, DataGrid, IList>("SelectedItems");

public static void SetSelectedItems(DataGrid obj, IList value) => obj.SetValue(SelectedItemsProperty, value);
public static IList GetSelectedItems(DataGrid obj) => obj.GetValue(SelectedItemsProperty);


public static readonly AttachedProperty<bool> IsUpdatingSelectedItemsProperty =
AvaloniaProperty.RegisterAttached<DataGridExtension, DataGrid, bool>("IsUpdatingSelectedItems");

public static void SetIsUpdatingSelectedItems(DataGrid obj, bool value) =>
obj.SetValue(IsUpdatingSelectedItemsProperty, value);

public static bool GetIsUpdatingSelectedItems(DataGrid obj) => obj.GetValue(IsUpdatingSelectedItemsProperty);

static DataGridExtension()
{
SelectedItemsProperty.Changed.AddClassHandler((DataGrid target,
AvaloniaPropertyChangedEventArgs<IList> args) =>
{
SetIsUpdatingSelectedItems(target, true);
target.SelectedItems.Clear();
var newItems = args.GetNewValue<IList>();
if (newItems != null)
{
foreach (var item in newItems)
{
target.SelectedItems.Add(item);
}
}

SetIsUpdatingSelectedItems(target, false);
});
}
}
}
5 changes: 4 additions & 1 deletion src/ViewModels/BranchTreeNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

namespace SourceGit.ViewModels
{
public class BranchTreeNode : ObservableObject
public class BranchTreeNode : ObservableObject, ICommitTreeNode
{
public string Name { get; private set; } = string.Empty;
public string Path { get; private set; } = string.Empty;
Expand All @@ -16,6 +16,9 @@ public class BranchTreeNode : ObservableObject
public List<BranchTreeNode> Children { get; private set; } = new List<BranchTreeNode>();
public int Counter { get; set; } = 0;

IEnumerable<ICommitTreeNode> ICommitTreeNode.Children => Children;
string ICommitTreeNode.CommitSHA => Backend is Models.Branch b ? b.Head : string.Empty;

public Models.FilterMode FilterMode
{
get => _filterMode;
Expand Down
3 changes: 2 additions & 1 deletion src/ViewModels/Fetch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ public override async Task<bool> Sure()
{
using var lockWatcher = _repo.LockWatcher();

var navigateToUpstreamHEAD = _repo.SelectedView is Histories { AutoSelectedCommit: { IsCurrentHead: true } };
var navigateToUpstreamHEAD = _repo.SelectedView is Histories { AutoSelectedCommits.Count: 1 } h &&
h.AutoSelectedCommits[0].IsCurrentHead;
var notags = _repo.Settings.FetchWithoutTags;
var force = _repo.Settings.EnableForceOnFetch;
var log = _repo.CreateLog("Fetch");
Expand Down
105 changes: 91 additions & 14 deletions src/ViewModels/Histories.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

using Avalonia.Controls;
using Avalonia.Threading;
using CommunityToolkit.Mvvm.ComponentModel;
using SourceGit.Models;

namespace SourceGit.ViewModels
{
Expand All @@ -23,11 +24,13 @@ public List<Models.Commit> Commits
get => _commits;
set
{
var lastSelected = AutoSelectedCommit;
var lastSelected = AutoSelectedCommits;
if (SetProperty(ref _commits, value))
{
if (value.Count > 0 && lastSelected != null)
AutoSelectedCommit = value.Find(x => x.SHA == lastSelected.SHA);
{
AutoSelectedCommits = value.Where(x => lastSelected.Any(s => x.SHA == s.SHA)).ToArray();
}
}
}
}
Expand All @@ -38,10 +41,10 @@ public Models.CommitGraph Graph
set => SetProperty(ref _graph, value);
}

public Models.Commit AutoSelectedCommit
public IReadOnlyList<Models.Commit> AutoSelectedCommits
{
get => _autoSelectedCommit;
set => SetProperty(ref _autoSelectedCommit, value);
get => _autoSelectedCommits;
set => SetProperty(ref _autoSelectedCommits, value);
}

public long NavigationId
Expand Down Expand Up @@ -97,7 +100,7 @@ public void Dispose()
Commits = [];
_repo = null;
_graph = null;
_autoSelectedCommit = null;
_autoSelectedCommits = null;
_detailContext?.Dispose();
_detailContext = null;
}
Expand Down Expand Up @@ -152,7 +155,76 @@ public void NavigateTo(string commitSHA)
});
}

public void Select(IList commits)
public void UpdateFromSelection()
{
var localBranchCommitSHAs =
CollectSelectedCommitSHAsRecursive(_repo.LocalBranchTrees);

var remoteBranchCommitSHAs =
CollectSelectedCommitSHAsRecursive(_repo.RemoteBranchTrees);

var tagsCommitSHAs =
_repo.VisibleTags switch
{
TagCollectionAsTree tree =>
CollectSelectedCommitSHAsRecursive(tree.Tree),
TagCollectionAsList list => list.TagItems.Where(t => t.IsSelected)
.Select(t => t.Tag.SHA),
_ => Enumerable.Empty<string>()
};

var neededCommitSHAs =
localBranchCommitSHAs.Union(remoteBranchCommitSHAs).Union(tagsCommitSHAs).ToHashSet();
var foundCommits = new List<Commit>();

// perf: we expect only a view commits to be selected (1-2), hence the cost is similar to the
// NaviateToCommit() even though we have a O(N*M) loop here, we loop the large commit list once,
// and the needed commits N times
foreach (var commit in _commits)
{
var match = neededCommitSHAs.FirstOrDefault(a => commit.SHA.StartsWith(a, StringComparison.Ordinal));
if (match != null)
{
foundCommits.Add(commit);
neededCommitSHAs.Remove(match);
}
}

if (neededCommitSHAs.Count == 0)
{
Dispatcher.UIThread.Post(() => Select(foundCommits, true));
}
else
{
Task.Run(async () =>
{
var remaining = await Task.WhenAll(neededCommitSHAs.Select(sha =>
new Commands.QuerySingleCommit(_repo.FullPath, sha)
.GetResultAsync()
)).ConfigureAwait(false);
foundCommits.AddRange(remaining);
Dispatcher.UIThread.Post(() => Select(foundCommits, true));
});
}
}

private static IEnumerable<string> CollectSelectedCommitSHAsRecursive(IEnumerable<ICommitTreeNode> treeNodes)
{
foreach (var node in treeNodes)
{
if (node.IsSelected && !string.IsNullOrEmpty(node.CommitSHA))
{
yield return node.CommitSHA;
}

foreach (var child in CollectSelectedCommitSHAsRecursive(node.Children))
{
yield return child;
}
}
}

public void Select(IList commits, bool autoSelect)
{
if (commits.Count == 0)
{
Expand All @@ -163,10 +235,8 @@ public void Select(IList commits)
{
var commit = (commits[0] as Models.Commit)!;
if (_repo.SearchCommitContext.Selected == null || _repo.SearchCommitContext.Selected.SHA != commit.SHA)
_repo.SearchCommitContext.Selected = _repo.SearchCommitContext.Results?.Find(x => x.SHA == commit.SHA);

AutoSelectedCommit = commit;
NavigationId = _navigationId + 1;
_repo.SearchCommitContext.Selected =
_repo.SearchCommitContext.Results?.Find(x => x.SHA == commit.SHA);

if (_detailContext is CommitDetail detail)
{
Expand All @@ -192,6 +262,13 @@ public void Select(IList commits)
_repo.SearchCommitContext.Selected = null;
DetailContext = new Models.Count(commits.Count);
}

if (autoSelect && commits.Count > 0)
{
AutoSelectedCommits =
commits as IReadOnlyList<Commit> ?? commits.OfType<Models.Commit>().ToArray();
NavigationId = _navigationId + 1;
}
}

public async Task<bool> CheckoutBranchByDecoratorAsync(Models.Decorator decorator)
Expand Down Expand Up @@ -397,7 +474,7 @@ public void CompareWithWorktree(Models.Commit commit)

private void NavigateTo(Models.Commit commit)
{
AutoSelectedCommit = commit;
AutoSelectedCommits = [commit];

if (commit == null)
{
Expand Down Expand Up @@ -425,7 +502,7 @@ private void NavigateTo(Models.Commit commit)
private bool _isLoading = true;
private List<Models.Commit> _commits = new List<Models.Commit>();
private Models.CommitGraph _graph = null;
private Models.Commit _autoSelectedCommit = null;
private IReadOnlyList<Models.Commit> _autoSelectedCommits = null;
private Models.Bisect _bisect = null;
private long _navigationId = 0;
private IDisposable _detailContext = null;
Expand Down
11 changes: 11 additions & 0 deletions src/ViewModels/ICommitTreeNode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System.Collections.Generic;

namespace SourceGit.ViewModels
{
public interface ICommitTreeNode
{
IEnumerable<ICommitTreeNode> Children { get; }
string CommitSHA { get; }
bool IsSelected { get; }
}
}
2 changes: 1 addition & 1 deletion src/ViewModels/Repository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1438,7 +1438,7 @@ public async Task CompareBranchWithWorktreeAsync(Models.Branch branch)
_searchCommitContext.Selected = null;

var target = await new Commands.QuerySingleCommit(FullPath, branch.Head).GetResultAsync();
_histories.AutoSelectedCommit = null;
_histories.AutoSelectedCommits = null;
_histories.DetailContext = new RevisionCompare(FullPath, target, null);
}
}
Expand Down
6 changes: 5 additions & 1 deletion src/ViewModels/TagCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,18 @@ public TagToolTip(Models.Tag t)
}
}

public class TagTreeNode : ObservableObject
public class TagTreeNode : ObservableObject, ICommitTreeNode
{
public string FullPath { get; private set; }
public int Depth { get; private set; } = 0;
public Models.Tag Tag { get; private set; } = null;
public TagToolTip ToolTip { get; private set; } = null;
public List<TagTreeNode> Children { get; private set; } = [];
public int Counter { get; set; } = 0;

IEnumerable<ICommitTreeNode> ICommitTreeNode.Children => Children;
string ICommitTreeNode.CommitSHA => Tag.SHA;


public bool IsFolder
{
Expand Down
1 change: 0 additions & 1 deletion src/Views/BranchTree.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
<ListBox.ItemTemplate>
<DataTemplate DataType="vm:BranchTreeNode">
<Border Background="Transparent"
PointerPressed="OnNodePointerPressed"
ToolTip.Tip="{Binding Backend}"
ToolTip.Placement="Right">
<Border.DataTemplates>
Expand Down
22 changes: 0 additions & 22 deletions src/Views/BranchTree.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -418,28 +418,6 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang
}
}

private void OnNodePointerPressed(object sender, PointerPressedEventArgs e)
{
var ctrl = OperatingSystem.IsMacOS() ? KeyModifiers.Meta : KeyModifiers.Control;
if (e.KeyModifiers.HasFlag(ctrl) || e.KeyModifiers.HasFlag(KeyModifiers.Shift))
return;

var p = e.GetCurrentPoint(this);
if (!p.Properties.IsLeftButtonPressed)
return;

if (DataContext is not ViewModels.Repository repo)
return;

if (sender is not Border { DataContext: ViewModels.BranchTreeNode node })
return;

if (node.Backend is not Models.Branch branch)
return;

repo.NavigateToCommit(branch.Head);
}

private void OnNodesSelectionChanged(object _, SelectionChangedEventArgs e)
{
if (_disableSelectionChangingEvent)
Expand Down
3 changes: 2 additions & 1 deletion src/Views/Histories.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
xmlns:vm="using:SourceGit.ViewModels"
xmlns:v="using:SourceGit.Views"
xmlns:c="using:SourceGit.Converters"
xmlns:sourceGit="clr-namespace:SourceGit"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="SourceGit.Views.Histories"
x:DataType="vm:Histories"
Expand All @@ -30,7 +31,7 @@
Background="{DynamicResource Brush.Window}"
SelectionMode="Extended"
ItemsSource="{Binding Commits, Mode=OneWay}"
SelectedItem="{Binding AutoSelectedCommit, Mode=OneWay}"
sourceGit:DataGridExtension.SelectedItems="{Binding AutoSelectedCommits, Mode=OneWay}"
CanUserReorderColumns="False"
CanUserResizeColumns="True"
CanUserSortColumns="False"
Expand Down
9 changes: 6 additions & 3 deletions src/Views/Histories.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,8 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang

if (change.Property == NavigationIdProperty)
{
if (CommitListContainer is { SelectedItems.Count: 1, IsLoaded: true } dataGrid)
dataGrid.ScrollIntoView(dataGrid.SelectedItem, null);
if (CommitListContainer is { SelectedItems.Count: > 0, IsLoaded: true } dataGrid)
dataGrid.ScrollIntoView(dataGrid.SelectedItems[^1], null);
}
}

Expand Down Expand Up @@ -208,8 +208,11 @@ private void OnScrollToTopPointerPressed(object sender, PointerPressedEventArgs

private void OnCommitListSelectionChanged(object _, SelectionChangedEventArgs e)
{
if (DataGridExtension.GetIsUpdatingSelectedItems(CommitListContainer))
return;

if (DataContext is ViewModels.Histories histories)
histories.Select(CommitListContainer.SelectedItems);
histories.Select(CommitListContainer.SelectedItems, false);

e.Handled = true;
}
Expand Down
Loading