diff --git a/src/ViewModels/Histories.cs b/src/ViewModels/Histories.cs index d7a81b0f4..0d88d82ff 100644 --- a/src/ViewModels/Histories.cs +++ b/src/ViewModels/Histories.cs @@ -60,15 +60,7 @@ public bool IsDateTimeColumnVisible public List Commits { get => _commits; - set - { - var lastSelected = SelectedCommit; - if (SetProperty(ref _commits, value)) - { - if (value.Count > 0 && lastSelected != null) - SelectedCommit = value.Find(x => x.SHA.Equals(lastSelected.SHA, StringComparison.Ordinal)); - } - } + set => SetProperty(ref _commits, value); } public Models.CommitGraph Graph @@ -83,6 +75,18 @@ public Models.Commit SelectedCommit set => SetProperty(ref _selectedCommit, value); } + public Models.Commit LastSelectedCommit + { + get => _lastSelectedCommit; + set => SetProperty(ref _lastSelectedCommit, value); + } + + public List LastSelectedCommits + { + get => _lastSelectedCommits; + private set => SetProperty(ref _lastSelectedCommits, value); + } + public long NavigationId { get => _navigationId; @@ -137,6 +141,8 @@ public void Dispose() _repo = null; _graph = null; _selectedCommit = null; + _lastSelectedCommits = null; + _lastSelectedCommit = null; _detailContext?.Dispose(); _detailContext = null; } @@ -209,7 +215,14 @@ public void NavigateTo(string commitSHA) }); } - public void Select(IList commits) + /// + /// + /// notes: if multi selects, `AutoSelectedCommit` which `Binding Mode=OneWay` will not be updated, + /// so that we could not get the lastSelectedCommit automatically + /// + /// + /// + public void Select(IList commits, object selectedCommit) { if (_ignoreSelectionChange) return; @@ -218,13 +231,21 @@ public void Select(IList commits) { _repo.SearchCommitContext.Selected = null; DetailContext = null; + return; } else if (commits.Count == 1) { var commit = (commits[0] as Models.Commit)!; if (_repo.SearchCommitContext.Selected == null || !_repo.SearchCommitContext.Selected.SHA.Equals(commit.SHA, StringComparison.Ordinal)) - _repo.SearchCommitContext.Selected = _repo.SearchCommitContext.Results?.Find(x => x.SHA.Equals(commit.SHA, StringComparison.Ordinal)); + { + var results = _repo.SearchCommitContext.Results; + if (results != null) + { + _repo.SearchCommitContext.Selected = results.Find(x => x.SHA.Equals(commit.SHA, StringComparison.Ordinal)); + } + } + // MarkLastSelectedCommitsAsSelected(); SelectedCommit = commit; NavigationId = _navigationId + 1; @@ -252,6 +273,49 @@ public void Select(IList commits) _repo.SearchCommitContext.Selected = null; DetailContext = new Models.Count(commits.Count); } + + LastSelectedCommit = selectedCommit as Models.Commit; + + var lastSelected = new List(); + foreach (var obj in commits) + { + if (obj is Models.Commit c) + lastSelected.Add(c); + } + LastSelectedCommits = lastSelected; + } + + public void MarkLastSelectedCommitsAsSelected(IList commits) + { + if (commits == null || commits.Count == 0) + { + LastSelectedCommits = []; + NavigationId = _navigationId + 1; + return; + } + + var selectedSHAs = new HashSet(); + foreach (var c in commits) + selectedSHAs.Add(c.SHA); + + var availableCommits = new List(); + foreach (var c in Commits) + { + if (selectedSHAs.Contains(c.SHA)) + availableCommits.Add(c); + } + + // if LastSelectedCommits is not empty, find the AutoSelectedCommit or get last one + if (availableCommits.Count > 0 && LastSelectedCommit != null) + { + // List.Find is not LINQ, it's a method of List + SelectedCommit = availableCommits.Find(x => x.SHA == LastSelectedCommit.SHA) + ?? availableCommits[0]; + } + + // set selectItem above(1item), now select others too + LastSelectedCommits = availableCommits; + NavigationId = _navigationId + 1; } public async Task GetCommitAsync(string sha) @@ -466,6 +530,8 @@ public void CompareWithWorktree(Models.Commit commit) private CommitDetailSharedData _commitDetailSharedData = null; private bool _isLoading = true; private List _commits = new List(); + private List _lastSelectedCommits = []; + private Models.Commit _lastSelectedCommit = null; private Models.CommitGraph _graph = null; private Models.Commit _selectedCommit = null; private Models.Bisect _bisect = null; diff --git a/src/ViewModels/Repository.cs b/src/ViewModels/Repository.cs index 2945f7c63..82b0e8d4b 100644 --- a/src/ViewModels/Repository.cs +++ b/src/ViewModels/Repository.cs @@ -1,13 +1,12 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; - using Avalonia.Collections; using Avalonia.Threading; - using CommunityToolkit.Mvvm.ComponentModel; namespace SourceGit.ViewModels @@ -282,6 +281,12 @@ public SearchCommitContext SearchCommitContext get => _searchCommitContext; } + public List SelectedCommits + { + get => _selectCommits; + set => SetProperty(ref _selectCommits, value); + } + public bool IsLocalBranchGroupExpanded { get => _uiStates.IsLocalBranchesExpandedInSideBar; @@ -1219,6 +1224,7 @@ public void RefreshCommits() { if (_cancellationRefreshCommits is { IsCancellationRequested: false }) _cancellationRefreshCommits.Cancel(); + var oldSelectedCommits = _selectCommits.ToList(); _cancellationRefreshCommits = new CancellationTokenSource(); var token = _cancellationRefreshCommits.Token; @@ -1248,6 +1254,8 @@ public void RefreshCommits() BisectState = _histories.UpdateBisectInfo(); + _histories.MarkLastSelectedCommitsAsSelected(_histories.LastSelectedCommits); + if (!string.IsNullOrEmpty(_navigateToCommitDelayed)) NavigateToCommit(_navigateToCommitDelayed); } @@ -1957,6 +1965,7 @@ private async Task AutoFetchOnUIThread() private bool _isSearchingCommits = false; private SearchCommitContext _searchCommitContext = null; + private List _selectCommits = new List(); private string _filter = string.Empty; private List _remotes = []; diff --git a/src/Views/Histories.axaml.cs b/src/Views/Histories.axaml.cs index 39312ec6e..e4fb4fc96 100644 --- a/src/Views/Histories.axaml.cs +++ b/src/Views/Histories.axaml.cs @@ -123,6 +123,15 @@ public long NavigationId set => SetValue(NavigationIdProperty, value); } + public static readonly StyledProperty> LastSelectedCommitsProperty = + AvaloniaProperty.Register>(nameof(LastSelectedCommits)); + + public List LastSelectedCommits + { + get => GetValue(LastSelectedCommitsProperty); + set => SetValue(LastSelectedCommitsProperty, value); + } + public static readonly StyledProperty IsScrollToTopVisibleProperty = AvaloniaProperty.Register(nameof(IsScrollToTopVisible)); @@ -146,6 +155,18 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang if (CommitListContainer is { SelectedItems.Count: 1, IsLoaded: true } dataGrid) dataGrid.ScrollIntoView(dataGrid.SelectedItem, null); } + if (change.Property == LastSelectedCommitsProperty) + { + if (LastSelectedCommits?.Count > 1 && + (CommitListContainer is DataGrid { IsLoaded: true } dataGrid)) + { + foreach (var c in LastSelectedCommits) + { + dataGrid.SelectedItems.Add(c); + } + } + } + } private void OnCommitListLoaded(object sender, RoutedEventArgs e) @@ -301,7 +322,7 @@ private void OnScrollToTopPointerPressed(object sender, PointerPressedEventArgs private void OnCommitListSelectionChanged(object _, SelectionChangedEventArgs e) { if (DataContext is ViewModels.Histories histories) - histories.Select(CommitListContainer.SelectedItems); + histories.Select(CommitListContainer.SelectedItems, CommitListContainer.SelectedItem); e.Handled = true; } diff --git a/src/Views/Repository.axaml b/src/Views/Repository.axaml index 8151765df..3866331e4 100644 --- a/src/Views/Repository.axaml +++ b/src/Views/Repository.axaml @@ -424,7 +424,7 @@ - + - +