diff --git a/src/Models/Change.cs b/src/Models/Change.cs index 2ad448add..9420559cd 100644 --- a/src/Models/Change.cs +++ b/src/Models/Change.cs @@ -7,6 +7,12 @@ public enum ChangeViewMode Tree, } + public enum ChangeSortMode + { + Path, + Status, + } + public enum ChangeState { None, @@ -79,6 +85,48 @@ public void Set(ChangeState index, ChangeState workTree = ChangeState.None) OriginalPath = OriginalPath.Substring(1, OriginalPath.Length - 2); } + /// + /// Gets the sort priority for a change based on its status, used for sorting changes by status. + /// Lower numbers indicate higher priority (appear first in sorted lists). + /// + /// The change object to get priority for + /// True if sorting in unstaged context, false for staged context + /// Priority value where lower numbers appear first + public static int GetStatusSortPriority(Change change, bool isUnstagedContext) + { + if (change == null) return int.MaxValue; + + if (isUnstagedContext) + { + // For unstaged context, only consider WorkTree state + return change.WorkTree switch + { + ChangeState.Conflicted => 1, // Conflicts first - most urgent + ChangeState.Modified => 2, + ChangeState.TypeChanged => 3, + ChangeState.Deleted => 4, // Missing files + ChangeState.Renamed => 5, + ChangeState.Copied => 6, + ChangeState.Untracked => 7, // New files last + _ => 10 + }; + } + else + { + // For staged context, only consider Index state + return change.Index switch + { + ChangeState.Modified => 1, + ChangeState.TypeChanged => 2, + ChangeState.Renamed => 3, + ChangeState.Copied => 4, + ChangeState.Added => 5, + ChangeState.Deleted => 6, + _ => 10 + }; + } + } + private static readonly string[] TYPE_DESCS = [ "Unknown", diff --git a/src/Resources/Locales/de_DE.axaml b/src/Resources/Locales/de_DE.axaml index bb78b8e7c..67d08fc52 100644 --- a/src/Resources/Locales/de_DE.axaml +++ b/src/Resources/Locales/de_DE.axaml @@ -87,6 +87,8 @@ Zeige als Datei- und Ordnerliste Zeige als Pfadliste Zeige als Dateisystembaum + Nach Pfad sortieren + Nach Status sortieren Ändere URL des Submoduls Submodule: URL: diff --git a/src/Resources/Locales/en_US.axaml b/src/Resources/Locales/en_US.axaml index 655f6e976..1c882266c 100644 --- a/src/Resources/Locales/en_US.axaml +++ b/src/Resources/Locales/en_US.axaml @@ -83,6 +83,8 @@ Show as File and Dir List Show as Path List Show as Filesystem Tree + Sort by Path + Sort by Status Change Submodule's URL Submodule: URL: diff --git a/src/Resources/Locales/es_ES.axaml b/src/Resources/Locales/es_ES.axaml index 4ef06eae9..a8bb15041 100644 --- a/src/Resources/Locales/es_ES.axaml +++ b/src/Resources/Locales/es_ES.axaml @@ -87,6 +87,8 @@ Mostrar como Lista de Archivos y Directorios Mostrar como Lista de Rutas Mostrar como Árbol de Sistema de Archivos + Ordenar por ruta + Ordenar por estado Cambiar la URL del Submódulo Submódulo: URL: diff --git a/src/Resources/Locales/fr_FR.axaml b/src/Resources/Locales/fr_FR.axaml index 919dfeac8..731314fad 100644 --- a/src/Resources/Locales/fr_FR.axaml +++ b/src/Resources/Locales/fr_FR.axaml @@ -70,6 +70,8 @@ Afficher comme liste de dossiers/fichiers Afficher comme liste de chemins Afficher comme arborescence + Trier par chemin + Trier par statut Récupérer la branche Récupérer ce commit Commit : diff --git a/src/Resources/Locales/it_IT.axaml b/src/Resources/Locales/it_IT.axaml index c12e3e53c..7bf68de21 100644 --- a/src/Resources/Locales/it_IT.axaml +++ b/src/Resources/Locales/it_IT.axaml @@ -77,6 +77,8 @@ Mostra come elenco di file e cartelle Mostra come elenco di percorsi Mostra come albero del filesystem + Ordina per percorso + Ordina per stato Checkout Branch Checkout Commit Commit: diff --git a/src/Resources/Locales/ja_JP.axaml b/src/Resources/Locales/ja_JP.axaml index 32c41f216..e45bef786 100644 --- a/src/Resources/Locales/ja_JP.axaml +++ b/src/Resources/Locales/ja_JP.axaml @@ -69,6 +69,8 @@ ファイルとディレクトリのリストを表示 パスのリストを表示 ファイルシステムのツリーを表示 + パスで並び替え + ステータスで並び替え ブランチをチェックアウト コミットをチェックアウト コミット: diff --git a/src/Resources/Locales/pt_BR.axaml b/src/Resources/Locales/pt_BR.axaml index 3fbac432f..cf4481598 100644 --- a/src/Resources/Locales/pt_BR.axaml +++ b/src/Resources/Locales/pt_BR.axaml @@ -61,6 +61,8 @@ Exibir como Lista de Arquivos e Diretórios Exibir como Lista de Caminhos Exibir como Árvore de Sistema de Arquivos + Ordenar por caminho + Ordenar por status Checkout Branch Checkout Commit Commit: diff --git a/src/Resources/Locales/ru_RU.axaml b/src/Resources/Locales/ru_RU.axaml index 413b99e32..0e5b83357 100644 --- a/src/Resources/Locales/ru_RU.axaml +++ b/src/Resources/Locales/ru_RU.axaml @@ -87,6 +87,8 @@ Показывать в виде списка файлов и каталогов Показывать в виде списка путей Показывать в виде дерева файловой системы + Сортировать по пути + Сортировать по статусу Изменить URL-адрес подмодуля Подмодуль: URL-адрес: diff --git a/src/Resources/Locales/zh_CN.axaml b/src/Resources/Locales/zh_CN.axaml index 711ab12f3..418184113 100644 --- a/src/Resources/Locales/zh_CN.axaml +++ b/src/Resources/Locales/zh_CN.axaml @@ -87,6 +87,8 @@ 文件名+路径列表模式 全路径列表模式 文件目录树形结构模式 + 按路径排序 + 按状态排序 修改子模块远程地址 子模块 : 远程地址 : diff --git a/src/ViewModels/ChangeTreeNode.cs b/src/ViewModels/ChangeTreeNode.cs index 4d71a153b..72efc8287 100644 --- a/src/ViewModels/ChangeTreeNode.cs +++ b/src/ViewModels/ChangeTreeNode.cs @@ -47,7 +47,7 @@ public ChangeTreeNode(string path, bool isExpanded, int depth) IsExpanded = isExpanded; } - public static List Build(IList changes, HashSet folded) + public static List Build(IList changes, HashSet folded, Models.ChangeSortMode sortMode = Models.ChangeSortMode.Path, bool isUnstagedContext = false) { var nodes = new List(); var folders = new Dictionary(); @@ -93,7 +93,7 @@ public static List Build(IList changes, HashSet collection, ChangeTreeNode collection.Add(subFolder); } - private static void Sort(List nodes) + private static void Sort(List nodes, Models.ChangeSortMode sortMode, bool isUnstagedContext) { foreach (var node in nodes) { if (node.IsFolder) - Sort(node.Children); + Sort(node.Children, sortMode, isUnstagedContext); } - nodes.Sort((l, r) => + if (sortMode == Models.ChangeSortMode.Status) { - if (l.IsFolder == r.IsFolder) + nodes.Sort((l, r) => + { + // Sort folders first + if (l.IsFolder != r.IsFolder) + return l.IsFolder ? -1 : 1; + + // If both are folders, sort by path + if (l.IsFolder && r.IsFolder) + return Models.NumericSort.Compare(l.FullPath, r.FullPath); + + // For files, sort by status first + var leftPriority = Models.Change.GetStatusSortPriority(l.Change, isUnstagedContext); + var rightPriority = Models.Change.GetStatusSortPriority(r.Change, isUnstagedContext); + + // First sort by status priority + var statusComparison = leftPriority.CompareTo(rightPriority); + if (statusComparison != 0) + return statusComparison; + + // If status priorities are equal, sort by path as secondary sort + // Use the same sorting logic as the path-only sort for consistency return Models.NumericSort.Compare(l.FullPath, r.FullPath); - return l.IsFolder ? -1 : 1; - }); + }); + } + else + { + nodes.Sort((l, r) => + { + if (l.IsFolder == r.IsFolder) + return Models.NumericSort.Compare(l.FullPath, r.FullPath); + return l.IsFolder ? -1 : 1; + }); + } } private bool _isExpanded = true; diff --git a/src/ViewModels/Preferences.cs b/src/ViewModels/Preferences.cs index 05be06010..0a2a975d9 100644 --- a/src/ViewModels/Preferences.cs +++ b/src/ViewModels/Preferences.cs @@ -291,6 +291,30 @@ public Models.ChangeViewMode StashChangeViewMode set => SetProperty(ref _stashChangeViewMode, value); } + public Models.ChangeSortMode UnstagedChangeSortMode + { + get => _unstagedChangeSortMode; + set => SetProperty(ref _unstagedChangeSortMode, value); + } + + public Models.ChangeSortMode StagedChangeSortMode + { + get => _stagedChangeSortMode; + set => SetProperty(ref _stagedChangeSortMode, value); + } + + public Models.ChangeSortMode CommitChangeSortMode + { + get => _commitChangeSortMode; + set => SetProperty(ref _commitChangeSortMode, value); + } + + public Models.ChangeSortMode StashChangeSortMode + { + get => _stashChangeSortMode; + set => SetProperty(ref _stashChangeSortMode, value); + } + public string GitInstallPath { get => Native.OS.GitExecutable; @@ -715,6 +739,11 @@ private bool RemoveInvalidRepositoriesRecursive(List collection) private Models.ChangeViewMode _commitChangeViewMode = Models.ChangeViewMode.List; private Models.ChangeViewMode _stashChangeViewMode = Models.ChangeViewMode.List; + private Models.ChangeSortMode _unstagedChangeSortMode = Models.ChangeSortMode.Path; + private Models.ChangeSortMode _stagedChangeSortMode = Models.ChangeSortMode.Path; + private Models.ChangeSortMode _commitChangeSortMode = Models.ChangeSortMode.Path; + private Models.ChangeSortMode _stashChangeSortMode = Models.ChangeSortMode.Path; + private string _gitDefaultCloneDir = string.Empty; private int _shellOrTerminal = -1; diff --git a/src/Views/BranchCompare.axaml b/src/Views/BranchCompare.axaml index b80fefb5d..b77347b0b 100644 --- a/src/Views/BranchCompare.axaml +++ b/src/Views/BranchCompare.axaml @@ -119,12 +119,14 @@ + ViewMode="{Binding Source={x:Static vm:Preferences.Instance}, Path=CommitChangeViewMode, Mode=TwoWay}" + SortMode="{Binding Source={x:Static vm:Preferences.Instance}, Path=CommitChangeSortMode, Mode=TwoWay}"/> diff --git a/src/Views/ChangeCollectionView.axaml.cs b/src/Views/ChangeCollectionView.axaml.cs index 8b9f90586..df2d55a1a 100644 --- a/src/Views/ChangeCollectionView.axaml.cs +++ b/src/Views/ChangeCollectionView.axaml.cs @@ -109,6 +109,15 @@ public Models.ChangeViewMode ViewMode set => SetValue(ViewModeProperty, value); } + public static readonly StyledProperty SortModeProperty = + AvaloniaProperty.Register(nameof(SortMode), Models.ChangeSortMode.Path); + + public Models.ChangeSortMode SortMode + { + get => GetValue(SortModeProperty); + set => SetValue(SortModeProperty, value); + } + public static readonly StyledProperty> ChangesProperty = AvaloniaProperty.Register>(nameof(Changes)); @@ -250,6 +259,8 @@ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs chang if (change.Property == ViewModeProperty) UpdateDataSource(true); + else if (change.Property == SortModeProperty) + UpdateDataSource(true); else if (change.Property == ChangesProperty) UpdateDataSource(false); else if (change.Property == SelectedChangesProperty) @@ -384,7 +395,7 @@ private void UpdateDataSource(bool onlyViewModeChange) } var tree = new ViewModels.ChangeCollectionAsTree(); - tree.Tree = ViewModels.ChangeTreeNode.Build(changes, oldFolded); + tree.Tree = ViewModels.ChangeTreeNode.Build(changes, oldFolded, SortMode, IsUnstagedChange); var rows = new List(); MakeTreeRows(rows, tree.Tree); @@ -408,7 +419,8 @@ private void UpdateDataSource(bool onlyViewModeChange) else if (ViewMode == Models.ChangeViewMode.Grid) { var grid = new ViewModels.ChangeCollectionAsGrid(); - grid.Changes.AddRange(changes); + var sortedChanges = SortChanges(changes, SortMode); + grid.Changes.AddRange(sortedChanges); if (selected.Count> 0) grid.SelectedChanges.AddRange(selected); @@ -417,7 +429,8 @@ private void UpdateDataSource(bool onlyViewModeChange) else { var list = new ViewModels.ChangeCollectionAsList(); - list.Changes.AddRange(changes); + var sortedChanges = SortChanges(changes, SortMode); + list.Changes.AddRange(sortedChanges); if (selected.Count> 0) list.SelectedChanges.AddRange(selected); @@ -497,6 +510,36 @@ private void UpdateRowTips(Control control, Models.Change change) ToolTip.SetTip(control, tip); } + private List SortChanges(List changes, Models.ChangeSortMode sortMode) + { + var sortedChanges = new List(changes); + + if (sortMode == Models.ChangeSortMode.Status) + { + sortedChanges.Sort((l, r) => + { + var leftPriority = Models.Change.GetStatusSortPriority(l, IsUnstagedChange); + var rightPriority = Models.Change.GetStatusSortPriority(r, IsUnstagedChange); + + // First sort by status priority + var statusComparison = leftPriority.CompareTo(rightPriority); + if (statusComparison != 0) + return statusComparison; + + // If status priorities are equal, sort by path as secondary sort + // Use the same sorting logic as the path-only sort for consistency + return Models.NumericSort.Compare(l.Path, r.Path); + }); + } + else + { + // Path sort mode - use NumericSort for consistency + sortedChanges.Sort((l, r) => Models.NumericSort.Compare(l.Path, r.Path)); + } + + return sortedChanges; + } + private bool _disableSelectionChangingEvent = false; } } diff --git a/src/Views/ChangeViewModeSwitcher.axaml b/src/Views/ChangeViewModeSwitcher.axaml index b72fcbcb8..31c19fb9e 100644 --- a/src/Views/ChangeViewModeSwitcher.axaml +++ b/src/Views/ChangeViewModeSwitcher.axaml @@ -24,6 +24,17 @@ + + + + + + + + + + + diff --git a/src/Views/ChangeViewModeSwitcher.axaml.cs b/src/Views/ChangeViewModeSwitcher.axaml.cs index ed3066196..ef70411b1 100644 --- a/src/Views/ChangeViewModeSwitcher.axaml.cs +++ b/src/Views/ChangeViewModeSwitcher.axaml.cs @@ -15,6 +15,15 @@ public Models.ChangeViewMode ViewMode set => SetValue(ViewModeProperty, value); } + public static readonly StyledProperty SortModeProperty = + AvaloniaProperty.Register(nameof(SortMode)); + + public Models.ChangeSortMode SortMode + { + get => GetValue(SortModeProperty); + set => SetValue(SortModeProperty, value); + } + public ChangeViewModeSwitcher() { InitializeComponent(); @@ -37,5 +46,17 @@ private void SwitchToTree(object sender, RoutedEventArgs e) ViewMode = Models.ChangeViewMode.Tree; e.Handled = true; } + + private void SortByPath(object sender, RoutedEventArgs e) + { + SortMode = Models.ChangeSortMode.Path; + e.Handled = true; + } + + private void SortByStatus(object sender, RoutedEventArgs e) + { + SortMode = Models.ChangeSortMode.Status; + e.Handled = true; + } } } diff --git a/src/Views/CommitChanges.axaml b/src/Views/CommitChanges.axaml index 4a1d95bcf..48abd5216 100644 --- a/src/Views/CommitChanges.axaml +++ b/src/Views/CommitChanges.axaml @@ -41,13 +41,15 @@ + ViewMode="{Binding Source={x:Static vm:Preferences.Instance}, Path=CommitChangeViewMode, Mode=TwoWay}" + SortMode="{Binding Source={x:Static vm:Preferences.Instance}, Path=CommitChangeSortMode, Mode=TwoWay}"/> diff --git a/src/Views/RevisionCompare.axaml b/src/Views/RevisionCompare.axaml index b19bba554..57717c6f2 100644 --- a/src/Views/RevisionCompare.axaml +++ b/src/Views/RevisionCompare.axaml @@ -91,12 +91,14 @@ + ViewMode="{Binding Source={x:Static vm:Preferences.Instance}, Path=CommitChangeViewMode, Mode=TwoWay}" + SortMode="{Binding Source={x:Static vm:Preferences.Instance}, Path=CommitChangeSortMode, Mode=TwoWay}"/> diff --git a/src/Views/StashesPage.axaml b/src/Views/StashesPage.axaml index 36d3d0881..3e3c2c8e8 100644 --- a/src/Views/StashesPage.axaml +++ b/src/Views/StashesPage.axaml @@ -121,13 +121,15 @@ Width="14" Height="14" Margin="0,0,8,0" HorizontalAlignment="Right" - ViewMode="{Binding Source={x:Static vm:Preferences.Instance}, Path=StashChangeViewMode, Mode=TwoWay}"/> + ViewMode="{Binding Source={x:Static vm:Preferences.Instance}, Path=StashChangeViewMode, Mode=TwoWay}" + SortMode="{Binding Source={x:Static vm:Preferences.Instance}, Path=StashChangeSortMode, Mode=TwoWay}"/> diff --git a/src/Views/WorkingCopy.axaml b/src/Views/WorkingCopy.axaml index 99505c9fb..d3ca0ec25 100644 --- a/src/Views/WorkingCopy.axaml +++ b/src/Views/WorkingCopy.axaml @@ -118,7 +118,8 @@ + ViewMode="{Binding Source={x:Static vm:Preferences.Instance}, Path=UnstagedChangeViewMode, Mode=TwoWay}" + SortMode="{Binding Source={x:Static vm:Preferences.Instance}, Path=UnstagedChangeSortMode, Mode=TwoWay}"/> @@ -129,6 +130,7 @@ SelectionMode="Multiple" Background="{DynamicResource Brush.Contents}" ViewMode="{Binding Source={x:Static vm:Preferences.Instance}, Path=UnstagedChangeViewMode}" + SortMode="{Binding Source={x:Static vm:Preferences.Instance}, Path=UnstagedChangeSortMode}" Changes="{Binding VisibleUnstaged}" SelectedChanges="{Binding SelectedUnstaged, Mode=TwoWay}" ContextRequested="OnUnstagedContextRequested" @@ -172,7 +174,8 @@ + ViewMode="{Binding Source={x:Static vm:Preferences.Instance}, Path=StagedChangeViewMode, Mode=TwoWay}" + SortMode="{Binding Source={x:Static vm:Preferences.Instance}, Path=StagedChangeSortMode, Mode=TwoWay}"/> @@ -183,6 +186,7 @@ SelectionMode="Multiple" Background="{DynamicResource Brush.Contents}" ViewMode="{Binding Source={x:Static vm:Preferences.Instance}, Path=StagedChangeViewMode}" + SortMode="{Binding Source={x:Static vm:Preferences.Instance}, Path=StagedChangeSortMode}" Changes="{Binding VisibleStaged}" SelectedChanges="{Binding SelectedStaged, Mode=TwoWay}" ContextRequested="OnStagedContextRequested"

AltStyle によって変換されたページ (->オリジナル) /