From d3f06b03b6fe6be1e77314e3eb0a5f8dbb87327d Mon Sep 17 00:00:00 2001 From: hishitetsu <66369541+hishitetsu@users.noreply.github.com> Date: Mon, 18 Dec 2023 01:37:50 +0900 Subject: [PATCH] Feature: Added option to prioritize files when sorting (#14253) --- ...s => SortFilesAndFoldersTogetherAction.cs} | 10 ++--- .../Actions/Display/SortFilesFirstAction.cs | 40 +++++++++++++++++++ .../Actions/Display/SortFoldersFirstAction.cs | 40 +++++++++++++++++++ src/Files.App/Data/Commands/CommandCodes.cs | 4 +- .../Data/Commands/Manager/CommandManager.cs | 8 +++- .../Data/Commands/Manager/ICommandManager.cs | 4 +- .../DisplayPage/DisplayPageContext.cs | 15 +++++++ .../DisplayPage/IDisplayPageContext.cs | 1 + src/Files.App/Data/Models/ItemViewModel.cs | 29 ++++++++++++-- .../Helpers/Layout/LayoutPreferencesItem.cs | 4 ++ .../Layout/LayoutPreferencesManager.cs | 19 +++++++++ .../MenuFlyout/ContextFlyoutItemHelper.cs | 2 +- .../Settings/FoldersSettingsService.cs | 6 +++ src/Files.App/Strings/en-US/Resources.resw | 33 +++++++++------ .../UserControls/InnerNavigationToolbar.xaml | 11 +++-- .../Utils/Storage/Collection/SortingHelper.cs | 22 +++++----- .../ViewModels/Settings/FoldersViewModel.cs | 29 +++++++++++--- src/Files.App/Views/Settings/FoldersPage.xaml | 13 +++--- src/Files.App/Views/Shells/BaseShellPage.cs | 7 ++++ .../Settings/IFoldersSettingsService.cs | 5 +++ 20 files changed, 248 insertions(+), 54 deletions(-) rename src/Files.App/Actions/Display/{ToggleSortDirectoriesAlongsideFilesAction.cs => SortFilesAndFoldersTogetherAction.cs} (66%) create mode 100644 src/Files.App/Actions/Display/SortFilesFirstAction.cs create mode 100644 src/Files.App/Actions/Display/SortFoldersFirstAction.cs diff --git a/src/Files.App/Actions/Display/ToggleSortDirectoriesAlongsideFilesAction.cs b/src/Files.App/Actions/Display/SortFilesAndFoldersTogetherAction.cs similarity index 66% rename from src/Files.App/Actions/Display/ToggleSortDirectoriesAlongsideFilesAction.cs rename to src/Files.App/Actions/Display/SortFilesAndFoldersTogetherAction.cs index 4e728da5d074..2d860df3a6eb 100644 --- a/src/Files.App/Actions/Display/ToggleSortDirectoriesAlongsideFilesAction.cs +++ b/src/Files.App/Actions/Display/SortFilesAndFoldersTogetherAction.cs @@ -3,20 +3,20 @@ namespace Files.App.Actions { - internal class ToggleSortDirectoriesAlongsideFilesAction : ObservableObject, IToggleAction + internal class SortFilesAndFoldersTogetherAction : ObservableObject, IToggleAction { private readonly IDisplayPageContext context; public string Label - => "SettingsListAndSortDirectoriesAlongsideFiles".GetLocalizedResource(); + => "SortFilesAndFoldersTogether".GetLocalizedResource(); public string Description - => "ToggleSortDirectoriesAlongsideFilesDescription".GetLocalizedResource(); + => "SortFilesAndFoldersTogetherDescription".GetLocalizedResource(); public bool IsOn => context.SortDirectoriesAlongsideFiles; - public ToggleSortDirectoriesAlongsideFilesAction() + public SortFilesAndFoldersTogetherAction() { context = Ioc.Default.GetRequiredService(); @@ -25,7 +25,7 @@ public ToggleSortDirectoriesAlongsideFilesAction() public Task ExecuteAsync() { - context.SortDirectoriesAlongsideFiles = !IsOn; + context.SortDirectoriesAlongsideFiles = true; return Task.CompletedTask; } diff --git a/src/Files.App/Actions/Display/SortFilesFirstAction.cs b/src/Files.App/Actions/Display/SortFilesFirstAction.cs new file mode 100644 index 000000000000..e17b610d93fd --- /dev/null +++ b/src/Files.App/Actions/Display/SortFilesFirstAction.cs @@ -0,0 +1,40 @@ +// Copyright (c) 2023 Files Community +// Licensed under the MIT License. See the LICENSE. + +namespace Files.App.Actions +{ + internal class SortFilesFirstAction : ObservableObject, IToggleAction + { + private readonly IDisplayPageContext context; + + public string Label + => "SortFilesFirst".GetLocalizedResource(); + + public string Description + => "SortFilesFirstDescription".GetLocalizedResource(); + + public bool IsOn + => context.SortFilesFirst && !context.SortDirectoriesAlongsideFiles; + + public SortFilesFirstAction() + { + context = Ioc.Default.GetRequiredService(); + + context.PropertyChanged += Context_PropertyChanged; + } + + public Task ExecuteAsync() + { + context.SortFilesFirst = true; + context.SortDirectoriesAlongsideFiles = false; + + return Task.CompletedTask; + } + + private void Context_PropertyChanged(object? sender, PropertyChangedEventArgs e) + { + if (e.PropertyName is nameof(IDisplayPageContext.SortFilesFirst) or nameof(IDisplayPageContext.SortDirectoriesAlongsideFiles)) + OnPropertyChanged(nameof(IsOn)); + } + } +} diff --git a/src/Files.App/Actions/Display/SortFoldersFirstAction.cs b/src/Files.App/Actions/Display/SortFoldersFirstAction.cs new file mode 100644 index 000000000000..32306ce7ade5 --- /dev/null +++ b/src/Files.App/Actions/Display/SortFoldersFirstAction.cs @@ -0,0 +1,40 @@ +// Copyright (c) 2023 Files Community +// Licensed under the MIT License. See the LICENSE. + +namespace Files.App.Actions +{ + internal class SortFoldersFirstAction : ObservableObject, IToggleAction + { + private readonly IDisplayPageContext context; + + public string Label + => "SortFoldersFirst".GetLocalizedResource(); + + public string Description + => "SortFoldersFirstDescription".GetLocalizedResource(); + + public bool IsOn + => !context.SortFilesFirst && !context.SortDirectoriesAlongsideFiles; + + public SortFoldersFirstAction() + { + context = Ioc.Default.GetRequiredService(); + + context.PropertyChanged += Context_PropertyChanged; + } + + public Task ExecuteAsync() + { + context.SortFilesFirst = false; + context.SortDirectoriesAlongsideFiles = false; + + return Task.CompletedTask; + } + + private void Context_PropertyChanged(object? sender, PropertyChangedEventArgs e) + { + if (e.PropertyName is nameof(IDisplayPageContext.SortFilesFirst) or nameof(IDisplayPageContext.SortDirectoriesAlongsideFiles)) + OnPropertyChanged(nameof(IsOn)); + } + } +} diff --git a/src/Files.App/Data/Commands/CommandCodes.cs b/src/Files.App/Data/Commands/CommandCodes.cs index 8aed8fafef8e..7bfcf167ab3a 100644 --- a/src/Files.App/Data/Commands/CommandCodes.cs +++ b/src/Files.App/Data/Commands/CommandCodes.cs @@ -133,7 +133,9 @@ public enum CommandCodes SortAscending, SortDescending, ToggleSortDirection, - ToggleSortDirectoriesAlongsideFiles, + SortFoldersFirst, + SortFilesFirst, + SortFilesAndFoldersTogether, // Group by GroupByNone, diff --git a/src/Files.App/Data/Commands/Manager/CommandManager.cs b/src/Files.App/Data/Commands/Manager/CommandManager.cs index 340f75d40859..7fac5a3add36 100644 --- a/src/Files.App/Data/Commands/Manager/CommandManager.cs +++ b/src/Files.App/Data/Commands/Manager/CommandManager.cs @@ -131,7 +131,9 @@ public IRichCommand this[HotKey hotKey] public IRichCommand SortAscending => commands[CommandCodes.SortAscending]; public IRichCommand SortDescending => commands[CommandCodes.SortDescending]; public IRichCommand ToggleSortDirection => commands[CommandCodes.ToggleSortDirection]; - public IRichCommand ToggleSortDirectoriesAlongsideFiles => commands[CommandCodes.ToggleSortDirectoriesAlongsideFiles]; + public IRichCommand SortFoldersFirst => commands[CommandCodes.SortFoldersFirst]; + public IRichCommand SortFilesFirst => commands[CommandCodes.SortFilesFirst]; + public IRichCommand SortFilesAndFoldersTogether => commands[CommandCodes.SortFilesAndFoldersTogether]; public IRichCommand GroupByNone => commands[CommandCodes.GroupByNone]; public IRichCommand GroupByName => commands[CommandCodes.GroupByName]; public IRichCommand GroupByDateModified => commands[CommandCodes.GroupByDateModified]; @@ -296,7 +298,9 @@ public CommandManager() [CommandCodes.SortAscending] = new SortAscendingAction(), [CommandCodes.SortDescending] = new SortDescendingAction(), [CommandCodes.ToggleSortDirection] = new ToggleSortDirectionAction(), - [CommandCodes.ToggleSortDirectoriesAlongsideFiles] = new ToggleSortDirectoriesAlongsideFilesAction(), + [CommandCodes.SortFoldersFirst] = new SortFoldersFirstAction(), + [CommandCodes.SortFilesFirst] = new SortFilesFirstAction(), + [CommandCodes.SortFilesAndFoldersTogether] = new SortFilesAndFoldersTogetherAction(), [CommandCodes.GroupByNone] = new GroupByNoneAction(), [CommandCodes.GroupByName] = new GroupByNameAction(), [CommandCodes.GroupByDateModified] = new GroupByDateModifiedAction(), diff --git a/src/Files.App/Data/Commands/Manager/ICommandManager.cs b/src/Files.App/Data/Commands/Manager/ICommandManager.cs index cb5d33173d53..2122b133ca79 100644 --- a/src/Files.App/Data/Commands/Manager/ICommandManager.cs +++ b/src/Files.App/Data/Commands/Manager/ICommandManager.cs @@ -118,7 +118,9 @@ public interface ICommandManager : IEnumerable IRichCommand SortAscending { get; } IRichCommand SortDescending { get; } IRichCommand ToggleSortDirection { get; } - IRichCommand ToggleSortDirectoriesAlongsideFiles { get; } + IRichCommand SortFoldersFirst { get; } + IRichCommand SortFilesFirst { get; } + IRichCommand SortFilesAndFoldersTogether { get; } IRichCommand GroupByNone { get; } IRichCommand GroupByName { get; } diff --git a/src/Files.App/Data/Contexts/DisplayPage/DisplayPageContext.cs b/src/Files.App/Data/Contexts/DisplayPage/DisplayPageContext.cs index e15fb2e47162..2b2409c22dbe 100644 --- a/src/Files.App/Data/Contexts/DisplayPage/DisplayPageContext.cs +++ b/src/Files.App/Data/Contexts/DisplayPage/DisplayPageContext.cs @@ -115,6 +115,17 @@ public bool SortDirectoriesAlongsideFiles } } + private bool _SortFilesFirst = false; + public bool SortFilesFirst + { + get => _SortFilesFirst; + set + { + if (FolderSettings is LayoutPreferencesManager viewModel) + viewModel.SortFilesFirst = value; + } + } + private LayoutPreferencesManager? FolderSettings => context.PaneOrColumn?.InstanceViewModel?.FolderSettings; public DisplayPageContext() @@ -181,6 +192,9 @@ private void FolderSettings_PropertyChanged(object? sender, PropertyChangedEvent case nameof(LayoutPreferencesManager.SortDirectoriesAlongsideFiles): SetProperty(ref _SortDirectoriesAlongsideFiles, viewModel.SortDirectoriesAlongsideFiles, nameof(SortDirectoriesAlongsideFiles)); break; + case nameof(LayoutPreferencesManager.SortFilesFirst): + SetProperty(ref _SortFilesFirst, viewModel.SortFilesFirst, nameof(SortFilesFirst)); + break; } } @@ -214,6 +228,7 @@ private void Update() SetProperty(ref _GroupDirection, viewModel.DirectoryGroupDirection, nameof(GroupDirection)); SetProperty(ref _GroupByDateUnit, viewModel.DirectoryGroupByDateUnit, nameof(GroupByDateUnit)); SetProperty(ref _SortDirectoriesAlongsideFiles, viewModel.SortDirectoriesAlongsideFiles, nameof(SortDirectoriesAlongsideFiles)); + SetProperty(ref _SortFilesFirst, viewModel.SortFilesFirst, nameof(SortFilesFirst)); } } diff --git a/src/Files.App/Data/Contexts/DisplayPage/IDisplayPageContext.cs b/src/Files.App/Data/Contexts/DisplayPage/IDisplayPageContext.cs index 1b5160447ad1..fe0b29f72f20 100644 --- a/src/Files.App/Data/Contexts/DisplayPage/IDisplayPageContext.cs +++ b/src/Files.App/Data/Contexts/DisplayPage/IDisplayPageContext.cs @@ -16,6 +16,7 @@ public interface IDisplayPageContext : INotifyPropertyChanging, INotifyPropertyC GroupByDateUnit GroupByDateUnit { get; set; } bool SortDirectoriesAlongsideFiles { get; set; } + bool SortFilesFirst { get; set; } void DecreaseLayoutSize(); void IncreaseLayoutSize(); diff --git a/src/Files.App/Data/Models/ItemViewModel.cs b/src/Files.App/Data/Models/ItemViewModel.cs index 1b6b5fe6a0c9..8ac47e8b3da3 100644 --- a/src/Files.App/Data/Models/ItemViewModel.cs +++ b/src/Files.App/Data/Models/ItemViewModel.cs @@ -221,6 +221,14 @@ public async Task UpdateSortDirectoriesAlongsideFilesAsync() await ApplyFilesAndFoldersChangesAsync(); } + public async Task UpdateSortFilesFirstAsync() + { + OnPropertyChanged(nameof(AreFilesSortedFirst)); + + await OrderFilesAndFoldersAsync(); + await ApplyFilesAndFoldersChangesAsync(); + } + private void UpdateSortAndGroupOptions() { OnPropertyChanged(nameof(IsSortedByName)); @@ -236,6 +244,7 @@ private void UpdateSortAndGroupOptions() OnPropertyChanged(nameof(IsSortedAscending)); OnPropertyChanged(nameof(IsSortedDescending)); OnPropertyChanged(nameof(AreDirectoriesSortedAlongsideFiles)); + OnPropertyChanged(nameof(AreFilesSortedFirst)); } public bool IsSortedByName @@ -406,6 +415,16 @@ public bool AreDirectoriesSortedAlongsideFiles } } + public bool AreFilesSortedFirst + { + get => folderSettings.SortFilesFirst; + set + { + folderSettings.SortFilesFirst = value; + OnPropertyChanged(nameof(AreFilesSortedFirst)); + } + } + public bool HasNoWatcher { get; private set; } public ItemViewModel(LayoutPreferencesManager folderSettingsViewModel) @@ -547,6 +566,7 @@ await dispatcherQueue.EnqueueOrInvokeAsync(() => case nameof(UserSettingsService.FoldersSettingsService.DefaultSortOption): case nameof(UserSettingsService.FoldersSettingsService.DefaultGroupOption): case nameof(UserSettingsService.FoldersSettingsService.DefaultSortDirectoriesAlongsideFiles): + case nameof(UserSettingsService.FoldersSettingsService.DefaultSortFilesFirst): case nameof(UserSettingsService.FoldersSettingsService.SyncFolderPreferencesAcrossDirectories): case nameof(UserSettingsService.FoldersSettingsService.DefaultGroupByDateUnit): await dispatcherQueue.EnqueueOrInvokeAsync(() => @@ -602,7 +622,8 @@ await dispatcherQueue.EnqueueOrInvokeAsync(() => { var key = FilesAndFolders.ItemGroupKeySelector?.Invoke(item); var group = FilesAndFolders.GroupedCollection?.FirstOrDefault(x => x.Model.Key == key); - group?.OrderOne(list => SortingHelper.OrderFileList(list, folderSettings.DirectorySortOption, folderSettings.DirectorySortDirection, folderSettings.SortDirectoriesAlongsideFiles), item); + group?.OrderOne(list => SortingHelper.OrderFileList(list, folderSettings.DirectorySortOption, folderSettings.DirectorySortDirection, + folderSettings.SortDirectoriesAlongsideFiles, folderSettings.SortFilesFirst), item); } UpdateEmptyTextType(); @@ -753,7 +774,8 @@ void OrderEntries() if (filesAndFolders.Count == 0) return; - filesAndFolders = new ConcurrentCollection(SortingHelper.OrderFileList(filesAndFolders.ToList(), folderSettings.DirectorySortOption, folderSettings.DirectorySortDirection, folderSettings.SortDirectoriesAlongsideFiles)); + filesAndFolders = new ConcurrentCollection(SortingHelper.OrderFileList(filesAndFolders.ToList(), folderSettings.DirectorySortOption, folderSettings.DirectorySortDirection, + folderSettings.SortDirectoriesAlongsideFiles, folderSettings.SortFilesFirst)); } if (NativeWinApiHelper.IsHasThreadAccessPropertyPresent && dispatcherQueue.HasThreadAccess) @@ -775,7 +797,8 @@ private void OrderGroups(CancellationToken token = default) if (token.IsCancellationRequested) return; - gp.Order(list => SortingHelper.OrderFileList(list, folderSettings.DirectorySortOption, folderSettings.DirectorySortDirection, folderSettings.SortDirectoriesAlongsideFiles)); + gp.Order(list => SortingHelper.OrderFileList(list, folderSettings.DirectorySortOption, folderSettings.DirectorySortDirection, + folderSettings.SortDirectoriesAlongsideFiles, folderSettings.SortFilesFirst)); } if (FilesAndFolders.GroupedCollection is null || FilesAndFolders.GroupedCollection.IsSorted) diff --git a/src/Files.App/Helpers/Layout/LayoutPreferencesItem.cs b/src/Files.App/Helpers/Layout/LayoutPreferencesItem.cs index 4bc07f010907..e56bf72f2844 100644 --- a/src/Files.App/Helpers/Layout/LayoutPreferencesItem.cs +++ b/src/Files.App/Helpers/Layout/LayoutPreferencesItem.cs @@ -17,6 +17,7 @@ public class LayoutPreferencesItem public ColumnsViewModel ColumnsViewModel; public bool SortDirectoriesAlongsideFiles; + public bool SortFilesFirst; public bool IsAdaptiveLayoutOverridden; public int GridViewSize; @@ -43,6 +44,7 @@ public LayoutPreferencesItem() DirectoryGroupDirection = UserSettingsService.FoldersSettingsService.DefaultDirectoryGroupDirection; DirectoryGroupByDateUnit = UserSettingsService.FoldersSettingsService.DefaultGroupByDateUnit; SortDirectoriesAlongsideFiles = UserSettingsService.FoldersSettingsService.DefaultSortDirectoriesAlongsideFiles; + SortFilesFirst = UserSettingsService.FoldersSettingsService.DefaultSortFilesFirst; IsAdaptiveLayoutOverridden = defaultLayout is not FolderLayoutModes.Adaptive; ColumnsViewModel = new ColumnsViewModel(); @@ -99,6 +101,7 @@ public override bool Equals(object? obj) item.DirectoryGroupDirection == DirectoryGroupDirection && item.DirectoryGroupByDateUnit == DirectoryGroupByDateUnit && item.SortDirectoriesAlongsideFiles == SortDirectoriesAlongsideFiles && + item.SortFilesFirst == SortFilesFirst && item.IsAdaptiveLayoutOverridden == IsAdaptiveLayoutOverridden && item.ColumnsViewModel.Equals(ColumnsViewModel)); } @@ -117,6 +120,7 @@ public override int GetHashCode() hash.Add(DirectoryGroupDirection); hash.Add(DirectoryGroupByDateUnit); hash.Add(SortDirectoriesAlongsideFiles); + hash.Add(SortFilesFirst); hash.Add(IsAdaptiveLayoutOverridden); hash.Add(ColumnsViewModel); diff --git a/src/Files.App/Helpers/Layout/LayoutPreferencesManager.cs b/src/Files.App/Helpers/Layout/LayoutPreferencesManager.cs index dfd685ca398f..e7e568c54060 100644 --- a/src/Files.App/Helpers/Layout/LayoutPreferencesManager.cs +++ b/src/Files.App/Helpers/Layout/LayoutPreferencesManager.cs @@ -221,6 +221,19 @@ public bool SortDirectoriesAlongsideFiles } } + public bool SortFilesFirst + { + get => LayoutPreferencesItem.SortFilesFirst; + set + { + if (SetProperty(ref LayoutPreferencesItem.SortFilesFirst, value, nameof(SortFilesFirst))) + { + LayoutPreferencesUpdateRequired?.Invoke(this, new LayoutPreferenceEventArgs(LayoutPreferencesItem)); + SortFilesFirstPreferenceUpdated?.Invoke(this, SortFilesFirst); + } + } + } + public ColumnsViewModel ColumnsViewModel { get => LayoutPreferencesItem.ColumnsViewModel; @@ -256,6 +269,7 @@ private set OnPropertyChanged(nameof(DirectoryGroupDirection)); OnPropertyChanged(nameof(DirectoryGroupByDateUnit)); OnPropertyChanged(nameof(SortDirectoriesAlongsideFiles)); + OnPropertyChanged(nameof(SortFilesFirst)); OnPropertyChanged(nameof(ColumnsViewModel)); } } @@ -270,6 +284,7 @@ private set public event EventHandler? GroupDirectionPreferenceUpdated; public event EventHandler? GroupByDateUnitPreferenceUpdated; public event EventHandler? SortDirectoriesAlongsideFilesPreferenceUpdated; + public event EventHandler? SortFilesFirstPreferenceUpdated; public event EventHandler? LayoutModeChangeRequested; public event EventHandler? GridViewSizeChangeRequested; @@ -429,6 +444,9 @@ public void OnDefaultPreferencesChanged(string path, string settingsName) case nameof(UserSettingsService.FoldersSettingsService.DefaultSortDirectoriesAlongsideFiles): SortDirectoriesAlongsideFiles = preferencesItem.SortDirectoriesAlongsideFiles; break; + case nameof(UserSettingsService.FoldersSettingsService.DefaultSortFilesFirst): + SortFilesFirst = preferencesItem.SortFilesFirst; + break; case nameof(UserSettingsService.FoldersSettingsService.SyncFolderPreferencesAcrossDirectories): LayoutPreferencesItem = preferencesItem; // TODO: Update layout @@ -512,6 +530,7 @@ public static void SetLayoutPreferencesForPath(string path, LayoutPreferencesIte UserSettingsService.FoldersSettingsService.DefaultDirectoryGroupDirection = preferencesItem.DirectoryGroupDirection; UserSettingsService.FoldersSettingsService.DefaultGroupByDateUnit = preferencesItem.DirectoryGroupByDateUnit; UserSettingsService.FoldersSettingsService.DefaultSortDirectoriesAlongsideFiles = preferencesItem.SortDirectoriesAlongsideFiles; + UserSettingsService.FoldersSettingsService.DefaultSortFilesFirst = preferencesItem.SortFilesFirst; UserSettingsService.FoldersSettingsService.NameColumnWidth = preferencesItem.ColumnsViewModel.NameColumn.UserLengthPixels; diff --git a/src/Files.App/Helpers/MenuFlyout/ContextFlyoutItemHelper.cs b/src/Files.App/Helpers/MenuFlyout/ContextFlyoutItemHelper.cs index 071b39990dc6..f0a61cb82ef1 100644 --- a/src/Files.App/Helpers/MenuFlyout/ContextFlyoutItemHelper.cs +++ b/src/Files.App/Helpers/MenuFlyout/ContextFlyoutItemHelper.cs @@ -211,7 +211,7 @@ public static List GetBaseItemMenuItems( }, new ContextMenuFlyoutItemViewModel() { - Text = "NavToolbarGroupByRadioButtons/Text".GetLocalizedResource(), + Text = "GroupBy".GetLocalizedResource(), Glyph = "\uF168", ShowItem = !itemsSelected, ShowInRecycleBin = true, diff --git a/src/Files.App/Services/Settings/FoldersSettingsService.cs b/src/Files.App/Services/Settings/FoldersSettingsService.cs index 63d81d909081..c65b970d256d 100644 --- a/src/Files.App/Services/Settings/FoldersSettingsService.cs +++ b/src/Files.App/Services/Settings/FoldersSettingsService.cs @@ -339,6 +339,12 @@ public bool DefaultSortDirectoriesAlongsideFiles set => Set(value); } + public bool DefaultSortFilesFirst + { + get => Get(false); + set => Set(value); + } + public bool ShowFileExtensions { get => Get(true); diff --git a/src/Files.App/Strings/en-US/Resources.resw b/src/Files.App/Strings/en-US/Resources.resw index 11b8d1f2eb74..f1bfad7a508a 100644 --- a/src/Files.App/Strings/en-US/Resources.resw +++ b/src/Files.App/Strings/en-US/Resources.resw @@ -978,9 +978,6 @@ Search results in {1} for {0} - - List and sort directories alongside files - Details @@ -1323,18 +1320,12 @@ Sort - - Group By - Date modified Original folder - - Sort By - Descending @@ -2628,9 +2619,6 @@ Toggle item sort direction - - List and sort directories alongside files - List items without grouping @@ -3650,4 +3638,25 @@ Extract here (Smart) + + Sort files and folders together + + + Sort files and folders together + + + Sort files first + + + Sort files first then folders + + + Sort folders first + + + Sort folders first then files + + + Sort priority + \ No newline at end of file diff --git a/src/Files.App/UserControls/InnerNavigationToolbar.xaml b/src/Files.App/UserControls/InnerNavigationToolbar.xaml index 83ff1bd1a0ba..115d2e3b2693 100644 --- a/src/Files.App/UserControls/InnerNavigationToolbar.xaml +++ b/src/Files.App/UserControls/InnerNavigationToolbar.xaml @@ -510,7 +510,7 @@ + Text="{helpers:ResourceString Name=SortBy}"> @@ -546,7 +546,7 @@ + Text="{helpers:ResourceString Name=GroupBy}"> @@ -589,10 +589,9 @@ Text="{x:Bind Commands.GroupDescending.Label}" /> - + + + diff --git a/src/Files.App/Utils/Storage/Collection/SortingHelper.cs b/src/Files.App/Utils/Storage/Collection/SortingHelper.cs index 6a1cf7eaaaee..3858dc96266e 100644 --- a/src/Files.App/Utils/Storage/Collection/SortingHelper.cs +++ b/src/Files.App/Utils/Storage/Collection/SortingHelper.cs @@ -28,15 +28,15 @@ private static object OrderByNameFunc(ListedItem item) }; } - public static IEnumerable OrderFileList(IList filesAndFolders, SortOption directorySortOption, SortDirection directorySortDirection, bool sortDirectoriesAlongsideFiles) + public static IEnumerable OrderFileList(IList filesAndFolders, SortOption directorySortOption, SortDirection directorySortDirection, + bool sortDirectoriesAlongsideFiles, bool sortFilesFirst) { var orderFunc = GetSortFunc(directorySortOption); var naturalStringComparer = NaturalStringComparer.GetForProcessor(); - // In ascending order, show folders first, then files. - // So, we use == StorageItemTypes.File to make the value for a folder equal to 0, and equal to 1 for the rest. - static bool FolderThenFileAsync(ListedItem listedItem) - => (listedItem.PrimaryItemAttribute == StorageItemTypes.File || listedItem.IsShortcut || listedItem.IsArchive); + // Function to prioritize folders (if sortFilesFirst is false) or files (if sortFilesFirst is true) + bool PrioritizeFilesOrFolders(ListedItem listedItem) + => (listedItem.PrimaryItemAttribute == StorageItemTypes.File || listedItem.IsShortcut || listedItem.IsArchive) ^ sortFilesFirst; IOrderedEnumerable ordered; @@ -46,17 +46,17 @@ static bool FolderThenFileAsync(ListedItem listedItem) { SortOption.Name => sortDirectoriesAlongsideFiles ? filesAndFolders.OrderBy(orderFunc, naturalStringComparer) - : filesAndFolders.OrderBy(FolderThenFileAsync).ThenBy(orderFunc, naturalStringComparer), + : filesAndFolders.OrderBy(PrioritizeFilesOrFolders).ThenBy(orderFunc, naturalStringComparer), SortOption.FileTag => sortDirectoriesAlongsideFiles ? filesAndFolders.OrderBy(x => string.IsNullOrEmpty(orderFunc(x) as string)).ThenBy(orderFunc) - : filesAndFolders.OrderBy(FolderThenFileAsync) + : filesAndFolders.OrderBy(PrioritizeFilesOrFolders) .ThenBy(x => string.IsNullOrEmpty(orderFunc(x) as string)) .ThenBy(orderFunc), _ => sortDirectoriesAlongsideFiles ? filesAndFolders.OrderBy(orderFunc) - : filesAndFolders.OrderBy(FolderThenFileAsync).ThenBy(orderFunc) + : filesAndFolders.OrderBy(PrioritizeFilesOrFolders).ThenBy(orderFunc) }; } else @@ -65,19 +65,19 @@ static bool FolderThenFileAsync(ListedItem listedItem) { SortOption.Name => sortDirectoriesAlongsideFiles ? filesAndFolders.OrderByDescending(orderFunc, naturalStringComparer) - : filesAndFolders.OrderBy(FolderThenFileAsync) + : filesAndFolders.OrderBy(PrioritizeFilesOrFolders) .ThenByDescending(orderFunc, naturalStringComparer), SortOption.FileTag => sortDirectoriesAlongsideFiles ? filesAndFolders.OrderBy(x => string.IsNullOrEmpty(orderFunc(x) as string)) .ThenByDescending(orderFunc) - : filesAndFolders.OrderBy(FolderThenFileAsync) + : filesAndFolders.OrderBy(PrioritizeFilesOrFolders) .ThenBy(x => string.IsNullOrEmpty(orderFunc(x) as string)) .ThenByDescending(orderFunc), _ => sortDirectoriesAlongsideFiles ? filesAndFolders.OrderByDescending(orderFunc) - : filesAndFolders.OrderBy(FolderThenFileAsync).ThenByDescending(orderFunc) + : filesAndFolders.OrderBy(PrioritizeFilesOrFolders).ThenByDescending(orderFunc) }; } diff --git a/src/Files.App/ViewModels/Settings/FoldersViewModel.cs b/src/Files.App/ViewModels/Settings/FoldersViewModel.cs index dff294ab5e05..8e0d7a1645e8 100644 --- a/src/Files.App/ViewModels/Settings/FoldersViewModel.cs +++ b/src/Files.App/ViewModels/Settings/FoldersViewModel.cs @@ -16,6 +16,7 @@ public FoldersViewModel() SelectedDefaultLayoutModeIndex = (int)DefaultLayoutMode; SelectedDefaultSortingIndex = UserSettingsService.FoldersSettingsService.DefaultSortOption == SortOption.FileTag ? FileTagSortingIndex : (int)UserSettingsService.FoldersSettingsService.DefaultSortOption; SelectedDefaultGroupingIndex = UserSettingsService.FoldersSettingsService.DefaultGroupOption == GroupOption.FileTag ? FileTagGroupingIndex : (int)UserSettingsService.FoldersSettingsService.DefaultGroupOption; + SelectedDefaultSortPriorityIndex = UserSettingsService.FoldersSettingsService.DefaultSortDirectoriesAlongsideFiles ? 2 : UserSettingsService.FoldersSettingsService.DefaultSortFilesFirst ? 1 : 0; SelectedDeleteConfirmationPolicyIndex = (int)DeleteConfirmationPolicy; } @@ -291,16 +292,32 @@ public bool GroupByMonth public bool IsGroupByDate => UserSettingsService.FoldersSettingsService.DefaultGroupOption.IsGroupByDate(); - public bool ListAndSortDirectoriesAlongsideFiles + private int selectedDefaultSortPriorityIndex; + public int SelectedDefaultSortPriorityIndex { - get => UserSettingsService.FoldersSettingsService.DefaultSortDirectoriesAlongsideFiles; + get => selectedDefaultSortPriorityIndex; set { - if (value != UserSettingsService.FoldersSettingsService.DefaultSortDirectoriesAlongsideFiles) + if (SetProperty(ref selectedDefaultSortPriorityIndex, value)) { - UserSettingsService.FoldersSettingsService.DefaultSortDirectoriesAlongsideFiles = value; - - OnPropertyChanged(); + OnPropertyChanged(nameof(SelectedDefaultSortPriorityIndex)); + + switch (value) + { + case 0: + UserSettingsService.FoldersSettingsService.DefaultSortDirectoriesAlongsideFiles = false; + UserSettingsService.FoldersSettingsService.DefaultSortFilesFirst = false; + break; + case 1: + UserSettingsService.FoldersSettingsService.DefaultSortDirectoriesAlongsideFiles = false; + UserSettingsService.FoldersSettingsService.DefaultSortFilesFirst = true; + break; + case 2: + UserSettingsService.FoldersSettingsService.DefaultSortDirectoriesAlongsideFiles = true; + break; + default: + break; + } } } } diff --git a/src/Files.App/Views/Settings/FoldersPage.xaml b/src/Files.App/Views/Settings/FoldersPage.xaml index 5ce4872d87d4..419051ca5161 100644 --- a/src/Files.App/Views/Settings/FoldersPage.xaml +++ b/src/Files.App/Views/Settings/FoldersPage.xaml @@ -150,12 +150,13 @@ Style="{StaticResource RightAlignedToggleSwitchStyle}" /> - - - + + + + + + + diff --git a/src/Files.App/Views/Shells/BaseShellPage.cs b/src/Files.App/Views/Shells/BaseShellPage.cs index 7e263cfe3dd9..0f5a809a2230 100644 --- a/src/Files.App/Views/Shells/BaseShellPage.cs +++ b/src/Files.App/Views/Shells/BaseShellPage.cs @@ -201,6 +201,7 @@ public BaseShellPage(CurrentInstanceViewModel instanceViewModel) InstanceViewModel.FolderSettings.SortDirectionPreferenceUpdated += AppSettings_SortDirectionPreferenceUpdated; InstanceViewModel.FolderSettings.SortOptionPreferenceUpdated += AppSettings_SortOptionPreferenceUpdated; InstanceViewModel.FolderSettings.SortDirectoriesAlongsideFilesPreferenceUpdated += AppSettings_SortDirectoriesAlongsideFilesPreferenceUpdated; + InstanceViewModel.FolderSettings.SortFilesFirstPreferenceUpdated += AppSettings_SortFilesFirstPreferenceUpdated; PointerPressed += CoreWindow_PointerPressed; @@ -399,6 +400,11 @@ protected void AppSettings_SortDirectoriesAlongsideFilesPreferenceUpdated(object FilesystemViewModel?.UpdateSortDirectoriesAlongsideFilesAsync(); } + protected void AppSettings_SortFilesFirstPreferenceUpdated(object sender, bool e) + { + FilesystemViewModel?.UpdateSortFilesFirstAsync(); + } + protected void CoreWindow_PointerPressed(object sender, PointerRoutedEventArgs args) { if (!IsCurrentInstance) @@ -806,6 +812,7 @@ public virtual void Dispose() InstanceViewModel.FolderSettings.SortDirectionPreferenceUpdated -= AppSettings_SortDirectionPreferenceUpdated; InstanceViewModel.FolderSettings.SortOptionPreferenceUpdated -= AppSettings_SortOptionPreferenceUpdated; InstanceViewModel.FolderSettings.SortDirectoriesAlongsideFilesPreferenceUpdated -= AppSettings_SortDirectoriesAlongsideFilesPreferenceUpdated; + InstanceViewModel.FolderSettings.SortFilesFirstPreferenceUpdated -= AppSettings_SortFilesFirstPreferenceUpdated; // Prevent weird case of this being null when many tabs are opened/closed quickly if (FilesystemViewModel is not null) diff --git a/src/Files.Core/Services/Settings/IFoldersSettingsService.cs b/src/Files.Core/Services/Settings/IFoldersSettingsService.cs index 4fabe666c4c7..2bba10f773aa 100644 --- a/src/Files.Core/Services/Settings/IFoldersSettingsService.cs +++ b/src/Files.Core/Services/Settings/IFoldersSettingsService.cs @@ -233,6 +233,11 @@ public interface IFoldersSettingsService : IBaseSettingsService, INotifyProperty /// bool DefaultSortDirectoriesAlongsideFiles { get; set; } + /// + /// Gets or sets a value indicating if files should be sorted first. + /// + bool DefaultSortFilesFirst { get; set; } + /// /// Gets or sets a value indicating if file extensions should be displayed. ///