From b34f74cd0583a0d2d162d2c06d09357f3891082e Mon Sep 17 00:00:00 2001 From: heartacker Date: Fri, 8 Aug 2025 15:38:37 +0800 Subject: [PATCH 1/2] feat: remember the multi selected commit if avalible - it is usefull for multi selection. - like two revision compare, - it will keep those selection for compare while filter changed - and it will restore those selection if grapht fresh --- src/ViewModels/Histories.cs | 15 +++++++++++++++ src/ViewModels/Repository.cs | 10 ++++++++++ src/Views/Histories.axaml.cs | 21 +++++++++++++++++++++ src/Views/Repository.axaml | 5 +++-- 4 files changed, 49 insertions(+), 2 deletions(-) diff --git a/src/ViewModels/Histories.cs b/src/ViewModels/Histories.cs index d7a81b0f4..145a809cd 100644 --- a/src/ViewModels/Histories.cs +++ b/src/ViewModels/Histories.cs @@ -2,6 +2,7 @@ using System.Collections; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Threading.Tasks; using Avalonia.Controls; @@ -83,6 +84,12 @@ public Models.Commit SelectedCommit set => SetProperty(ref _selectedCommit, value); } + public List LastSelectedCommits + { + get => _lastSelectedCommits; + private set => SetProperty(ref _lastSelectedCommits, value); + } + public long NavigationId { get => _navigationId; @@ -252,6 +259,13 @@ public void Select(IList commits) _repo.SearchCommitContext.Selected = null; DetailContext = new Models.Count(commits.Count); } + + _repo.SelectedCommits = commits.Cast().ToList(); + } + + public void MarkCommitsAsSelected(IList commits) + { + LastSelectedCommits = _commits.Where(x => commits.Any(y => y.SHA == x.SHA)).ToList(); } public async Task GetCommitAsync(string sha) @@ -466,6 +480,7 @@ public void CompareWithWorktree(Models.Commit commit) private CommitDetailSharedData _commitDetailSharedData = null; private bool _isLoading = true; private List _commits = new List(); + private List _lastSelectedCommits = []; 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..3918d966d 100644 --- a/src/ViewModels/Repository.cs +++ b/src/ViewModels/Repository.cs @@ -282,6 +282,12 @@ public SearchCommitContext SearchCommitContext get => _searchCommitContext; } + public List SelectedCommits + { + get => _selectCommits; + set => SetProperty(ref _selectCommits, value); + } + public bool IsLocalBranchGroupExpanded { get => _uiStates.IsLocalBranchesExpandedInSideBar; @@ -1219,6 +1225,7 @@ public void RefreshCommits() { if (_cancellationRefreshCommits is { IsCancellationRequested: false }) _cancellationRefreshCommits.Cancel(); + var oldSelectedCommits = _selectCommits.ToList(); _cancellationRefreshCommits = new CancellationTokenSource(); var token = _cancellationRefreshCommits.Token; @@ -1248,6 +1255,8 @@ public void RefreshCommits() BisectState = _histories.UpdateBisectInfo(); + _histories.MarkCommitsAsSelected(oldSelectedCommits); + if (!string.IsNullOrEmpty(_navigateToCommitDelayed)) NavigateToCommit(_navigateToCommitDelayed); } @@ -1957,6 +1966,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..7dc98f878 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) 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 @@ - + - + From 867d043a0006c9b2fe80680ef301e1625027141e Mon Sep 17 00:00:00 2001 From: heartacker Date: Tue, 24 Mar 2026 10:38:38 +0800 Subject: [PATCH 2/2] refactor: Improve commit selection logic and handling - Refactor commit selection logic to track last selected commit separately and improve multi-selection handling. - Refactor commit selection logic and remove commented auto-navigation code. - Pass the selected commit item to the histories view model when the selection changes. --- src/ViewModels/Histories.cs | 81 +++++++++++++++++++++++++++++------- src/ViewModels/Repository.cs | 5 +-- src/Views/Histories.axaml.cs | 2 +- 3 files changed, 69 insertions(+), 19 deletions(-) diff --git a/src/ViewModels/Histories.cs b/src/ViewModels/Histories.cs index 145a809cd..0d88d82ff 100644 --- a/src/ViewModels/Histories.cs +++ b/src/ViewModels/Histories.cs @@ -2,7 +2,6 @@ using System.Collections; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Threading.Tasks; using Avalonia.Controls; @@ -61,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 @@ -84,6 +75,12 @@ 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; @@ -144,6 +141,8 @@ public void Dispose() _repo = null; _graph = null; _selectedCommit = null; + _lastSelectedCommits = null; + _lastSelectedCommit = null; _detailContext?.Dispose(); _detailContext = null; } @@ -216,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; @@ -225,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; @@ -260,12 +274,48 @@ public void Select(IList commits) DetailContext = new Models.Count(commits.Count); } - _repo.SelectedCommits = commits.Cast().ToList(); + 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 MarkCommitsAsSelected(IList commits) + public void MarkLastSelectedCommitsAsSelected(IList commits) { - LastSelectedCommits = _commits.Where(x => commits.Any(y => y.SHA == x.SHA)).ToList(); + 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) @@ -481,6 +531,7 @@ public void CompareWithWorktree(Models.Commit commit) 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 3918d966d..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 @@ -1255,7 +1254,7 @@ public void RefreshCommits() BisectState = _histories.UpdateBisectInfo(); - _histories.MarkCommitsAsSelected(oldSelectedCommits); + _histories.MarkLastSelectedCommitsAsSelected(_histories.LastSelectedCommits); if (!string.IsNullOrEmpty(_navigateToCommitDelayed)) NavigateToCommit(_navigateToCommitDelayed); diff --git a/src/Views/Histories.axaml.cs b/src/Views/Histories.axaml.cs index 7dc98f878..e4fb4fc96 100644 --- a/src/Views/Histories.axaml.cs +++ b/src/Views/Histories.axaml.cs @@ -322,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; }