From bdc78e5d01c10e967bad8b8ef890f055421645e8 Mon Sep 17 00:00:00 2001 From: Yair <39923744+yaira2@users.noreply.github.com> Date: Mon, 25 Mar 2024 13:00:04 -0400 Subject: [PATCH 01/41] =?UTF-8?q?Fix:=20Fixed=20an=20issue=20where=20there?= =?UTF-8?q?=20wasn't=20space=20to=20right=20click=20folder=20in=E2=80=A6?= =?UTF-8?q?=20(#15044)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Files.App/Views/Layouts/ColumnLayoutPage.xaml | 8 ++------ src/Files.App/Views/Layouts/DetailsLayoutPage.xaml | 8 +------- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/src/Files.App/Views/Layouts/ColumnLayoutPage.xaml b/src/Files.App/Views/Layouts/ColumnLayoutPage.xaml index 5a088cc73a92..9e08d44e7391 100644 --- a/src/Files.App/Views/Layouts/ColumnLayoutPage.xaml +++ b/src/Files.App/Views/Layouts/ColumnLayoutPage.xaml @@ -175,6 +175,7 @@ - - - - - - - - - - - - - Date: Mon, 25 Mar 2024 13:49:24 -0400 Subject: [PATCH 02/41] GitHub: Update code signing version number --- .github/workflows/deploy-preview.yml | 2 +- .github/workflows/deploy-stable.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy-preview.yml b/.github/workflows/deploy-preview.yml index b0a4f9327c8c..7467a0929bcf 100644 --- a/.github/workflows/deploy-preview.yml +++ b/.github/workflows/deploy-preview.yml @@ -140,7 +140,7 @@ jobs: $fileContent | Set-Content $localFilePath - name: Sign files with Azure Code Signing - uses: azure/azure-code-signing-action@v0.2.21 + uses: azure/azure-code-signing-action@v0.3.0 with: azure-tenant-id: ${{ secrets.AZURE_TENANT_ID }} azure-client-id: ${{ secrets.AZURE_CLIENT_ID }} diff --git a/.github/workflows/deploy-stable.yml b/.github/workflows/deploy-stable.yml index 21b5289e43e0..a61ea8b09f7b 100644 --- a/.github/workflows/deploy-stable.yml +++ b/.github/workflows/deploy-stable.yml @@ -140,7 +140,7 @@ jobs: $fileContent | Set-Content $localFilePath - name: Sign files with Azure Code Signing - uses: azure/azure-code-signing-action@v0.2.21 + uses: azure/azure-code-signing-action@v0.3.0 with: azure-tenant-id: ${{ secrets.AZURE_TENANT_ID }} azure-client-id: ${{ secrets.AZURE_CLIENT_ID }} From 8b9b6bfa5b37e4e051ec849959f427310e19c6a9 Mon Sep 17 00:00:00 2001 From: Gustavo Mauricio de Barros Date: Mon, 25 Mar 2024 23:51:42 -0300 Subject: [PATCH 03/41] Code Quality: Apply CA1829 and CA1859 across the solution (#14970) Co-authored-by: 0x5bfa <62196528+0x5bfa@users.noreply.github.com> Co-authored-by: hishitetsu <66369541+hishitetsu@users.noreply.github.com> --- .../Navigation/OpenDirectoryInNewTabAction.cs | 2 +- .../Navigation/OpenInNewWindowItemAction.cs | 2 +- src/Files.App/Data/Commands/HotKey/HotKey.cs | 10 +++++----- .../Data/Commands/Manager/CommandManager.cs | 17 ++++++++++------- .../Manager/ModifiableCommandManager.cs | 11 ++++++----- .../Data/Contexts/HomePage/HomePageContext.cs | 2 +- .../ContentPageContextFlyoutFactory.cs | 5 ++--- .../PropertiesNavigationViewItemFactory.cs | 3 ++- .../Data/Factories/ShellContextFlyoutHelper.cs | 8 ++++---- src/Files.App/Data/Models/ItemViewModel.cs | 2 +- .../Dialogs/CreateArchiveDialog.xaml.cs | 6 +++--- src/Files.App/Services/DialogService.cs | 5 +++-- src/Files.App/Services/JumpListService.cs | 4 ++-- .../UserControls/Widgets/DrivesWidget.xaml.cs | 2 +- .../Widgets/QuickAccessWidget.xaml.cs | 2 +- .../Utils/Archives/DecompressHelper.cs | 2 +- src/Files.App/Utils/Shell/ContextMenu.cs | 2 +- .../Storage/History/StorageHistoryOperations.cs | 4 ++-- .../Operations/ShellFilesystemOperations.cs | 6 +++--- .../ViewModels/Properties/HashesViewModel.cs | 2 +- .../ViewModels/Settings/AppearanceViewModel.cs | 3 +-- .../Previews/CodePreviewViewModel.cs | 11 ++++++----- src/Files.App/Views/Layouts/BaseLayoutPage.cs | 2 +- src/Files.App/Views/Shells/BaseShellPage.cs | 2 +- src/Files.Core/Data/Models/ByteSize.cs | 6 +++--- 25 files changed, 63 insertions(+), 58 deletions(-) diff --git a/src/Files.App/Actions/Navigation/OpenDirectoryInNewTabAction.cs b/src/Files.App/Actions/Navigation/OpenDirectoryInNewTabAction.cs index edb57c715686..5b154cdf712d 100644 --- a/src/Files.App/Actions/Navigation/OpenDirectoryInNewTabAction.cs +++ b/src/Files.App/Actions/Navigation/OpenDirectoryInNewTabAction.cs @@ -22,7 +22,7 @@ public RichGlyph Glyph context.ShellPage is not null && context.ShellPage.SlimContentPage is not null && context.SelectedItems.Count <= 5 && - context.SelectedItems.Where(x => x.IsFolder == true).Count() == context.SelectedItems.Count && + context.SelectedItems.Count(x => x.IsFolder) == context.SelectedItems.Count && userSettingsService.GeneralSettingsService.ShowOpenInNewTab; public OpenDirectoryInNewTabAction() diff --git a/src/Files.App/Actions/Navigation/OpenInNewWindowItemAction.cs b/src/Files.App/Actions/Navigation/OpenInNewWindowItemAction.cs index b9b8770e84f9..7f57c32efa8d 100644 --- a/src/Files.App/Actions/Navigation/OpenInNewWindowItemAction.cs +++ b/src/Files.App/Actions/Navigation/OpenInNewWindowItemAction.cs @@ -27,7 +27,7 @@ public RichGlyph Glyph context.ShellPage is not null && context.ShellPage.SlimContentPage is not null && context.SelectedItems.Count <= 5 && - context.SelectedItems.Where(x => x.IsFolder == true).Count() == context.SelectedItems.Count && + context.SelectedItems.Count(x => x.IsFolder) == context.SelectedItems.Count && userSettingsService.GeneralSettingsService.ShowOpenInNewWindow; public OpenInNewWindowItemAction() diff --git a/src/Files.App/Data/Commands/HotKey/HotKey.cs b/src/Files.App/Data/Commands/HotKey/HotKey.cs index 4110dbe64003..424e9bbe73e4 100644 --- a/src/Files.App/Data/Commands/HotKey/HotKey.cs +++ b/src/Files.App/Data/Commands/HotKey/HotKey.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -using System.Collections.Immutable; +using System.Collections.Frozen; using System.Runtime.InteropServices; using System.Text; using Forms = System.Windows.Forms; @@ -11,15 +11,15 @@ namespace Files.App.Data.Commands [DebuggerDisplay("{Code}")] public readonly struct HotKey : IEquatable { - private static readonly IImmutableDictionary modifiers = new Dictionary() + private static readonly FrozenDictionary modifiers = new Dictionary() { [KeyModifiers.Menu] = GetKeyString("Menu"), [KeyModifiers.Ctrl] = GetKeyString("Control"), [KeyModifiers.Shift] = GetKeyString("Shift"), [KeyModifiers.Win] = GetKeyString("Windows"), - }.ToImmutableDictionary(); + }.ToFrozenDictionary(); - private static readonly IImmutableDictionary keys = new Dictionary() + private static readonly FrozenDictionary keys = new Dictionary() { [Keys.Enter] = GetKeyString("Enter"), [Keys.Space] = GetKeyString("Space"), @@ -151,7 +151,7 @@ namespace Files.App.Data.Commands [Keys.Mute] = GetKeyString("MediaMute"), [Keys.VolumeDown] = GetKeyString("MediaVolumeDown"), [Keys.VolumeUp] = GetKeyString("MediaVolumeUp"), - }.ToImmutableDictionary(); + }.ToFrozenDictionary(); public static HotKey None { get; } = new(Keys.None, KeyModifiers.None); diff --git a/src/Files.App/Data/Commands/Manager/CommandManager.cs b/src/Files.App/Data/Commands/Manager/CommandManager.cs index 5e271bf07143..0ecd844f6a3e 100644 --- a/src/Files.App/Data/Commands/Manager/CommandManager.cs +++ b/src/Files.App/Data/Commands/Manager/CommandManager.cs @@ -1,12 +1,13 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. +using System.Collections.Frozen; +using System.Collections.Immutable; using Files.App.Actions; using Microsoft.AppCenter.Analytics; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Input; -using System.Collections.Immutable; namespace Files.App.Data.Commands { @@ -14,8 +15,8 @@ internal sealed class CommandManager : ICommandManager { private readonly IGeneralSettingsService settings = Ioc.Default.GetRequiredService(); - private readonly IImmutableDictionary commands; - private IImmutableDictionary hotKeys = new Dictionary().ToImmutableDictionary(); + private readonly FrozenDictionary commands; + private ImmutableDictionary hotKeys = new Dictionary().ToImmutableDictionary(); public IRichCommand this[CommandCodes code] => commands.TryGetValue(code, out var command) ? command : None; public IRichCommand this[string code] @@ -196,16 +197,18 @@ public CommandManager() .Select(action => new ActionCommand(this, action.Key, action.Value)) .Cast() .Append(new NoneCommand()) - .ToImmutableDictionary(command => command.Code); + .ToFrozenDictionary(command => command.Code); settings.PropertyChanged += Settings_PropertyChanged; UpdateHotKeys(); } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - public IEnumerator GetEnumerator() => commands.Values.GetEnumerator(); - private static IDictionary CreateActions() => new Dictionary + public IEnumerator GetEnumerator() => + (commands.Values as IEnumerable).GetEnumerator(); + + private static Dictionary CreateActions() => new Dictionary { [CommandCodes.OpenHelp] = new OpenHelpAction(), [CommandCodes.ToggleFullScreen] = new ToggleFullScreenAction(), @@ -362,7 +365,7 @@ public CommandManager() private void UpdateHotKeys() { - ISet useds = new HashSet(); + var useds = new HashSet(); var customs = new Dictionary(); foreach (var custom in settings.Actions) diff --git a/src/Files.App/Data/Commands/Manager/ModifiableCommandManager.cs b/src/Files.App/Data/Commands/Manager/ModifiableCommandManager.cs index ca61c04d9553..2ef98be35bb2 100644 --- a/src/Files.App/Data/Commands/Manager/ModifiableCommandManager.cs +++ b/src/Files.App/Data/Commands/Manager/ModifiableCommandManager.cs @@ -1,6 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. +using System.Collections.Frozen; using System.Collections.Immutable; namespace Files.App.Data.Commands @@ -9,7 +10,7 @@ internal sealed class ModifiableCommandManager : IModifiableCommandManager { private static readonly ICommandManager Commands = Ioc.Default.GetRequiredService(); - private readonly IImmutableDictionary ModifiableCommands; + private readonly FrozenDictionary ModifiableCommands; public IRichCommand this[CommandCodes code] => ModifiableCommands.TryGetValue(code, out var command) ? command : None; @@ -19,13 +20,13 @@ internal sealed class ModifiableCommandManager : IModifiableCommandManager public ModifiableCommandManager() { - ModifiableCommands = CreateModifiableCommands().ToImmutableDictionary(); + ModifiableCommands = CreateModifiableCommands(); } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - public IEnumerator GetEnumerator() => ModifiableCommands.Values.GetEnumerator(); + public IEnumerator GetEnumerator() => (ModifiableCommands.Values as IEnumerable).GetEnumerator(); - private static IDictionary CreateModifiableCommands() => new Dictionary + private static FrozenDictionary CreateModifiableCommands() => new Dictionary { [CommandCodes.None] = new NoneCommand(), [CommandCodes.PasteItem] = new ModifiableCommand(Commands.PasteItem, new() { @@ -34,6 +35,6 @@ public ModifiableCommandManager() [CommandCodes.DeleteItem] = new ModifiableCommand(Commands.DeleteItem, new() { { KeyModifiers.Shift, Commands.DeleteItemPermanently } }), - }; + }.ToFrozenDictionary(); } } diff --git a/src/Files.App/Data/Contexts/HomePage/HomePageContext.cs b/src/Files.App/Data/Contexts/HomePage/HomePageContext.cs index 17b5cc9361b0..b65ce7706a17 100644 --- a/src/Files.App/Data/Contexts/HomePage/HomePageContext.cs +++ b/src/Files.App/Data/Contexts/HomePage/HomePageContext.cs @@ -9,7 +9,7 @@ namespace Files.App.Data.Contexts { internal sealed class HomePageContext : ObservableObject, IHomePageContext { - private static readonly IImmutableList emptyTaggedItems = Enumerable.Empty().ToImmutableList(); + private static readonly ImmutableList emptyTaggedItems = Enumerable.Empty().ToImmutableList(); public bool IsAnyItemRightClicked => rightClickedItem is not null; diff --git a/src/Files.App/Data/Factories/ContentPageContextFlyoutFactory.cs b/src/Files.App/Data/Factories/ContentPageContextFlyoutFactory.cs index 96a1b12c09f7..b75ea039ae1e 100644 --- a/src/Files.App/Data/Factories/ContentPageContextFlyoutFactory.cs +++ b/src/Files.App/Data/Factories/ContentPageContextFlyoutFactory.cs @@ -40,7 +40,7 @@ public static List Filter(List Check(item: x, currentInstanceViewModel: currentInstanceViewModel, selectedItems: selectedItems)).ToList(); items.ForEach(x => x.Items = x.Items?.Where(y => Check(item: y, currentInstanceViewModel: currentInstanceViewModel, selectedItems: selectedItems)).ToList()); - var overflow = items.Where(x => x.ID == "ItemOverflow").FirstOrDefault(); + var overflow = items.FirstOrDefault(x => x.ID == "ItemOverflow"); if (overflow is not null) { if (!shiftPressed && UserSettingsService.GeneralSettingsService.MoveShellExtensionsToSubMenu) // items with ShowOnShift to overflow menu @@ -664,8 +664,7 @@ public static List GetNewItemItems(BaseLayoutVie public static void SwapPlaceholderWithShellOption(CommandBarFlyout contextMenu, string placeholderName, ContextMenuFlyoutItemViewModel? replacingItem, int position) { var placeholder = contextMenu.SecondaryCommands - .Where(x => Equals((x as AppBarButton)?.Tag, placeholderName)) - .FirstOrDefault() as AppBarButton; + .FirstOrDefault(x => Equals((x as AppBarButton)?.Tag, placeholderName)) as AppBarButton; if (placeholder is not null) placeholder.Visibility = Microsoft.UI.Xaml.Visibility.Collapsed; diff --git a/src/Files.App/Data/Factories/PropertiesNavigationViewItemFactory.cs b/src/Files.App/Data/Factories/PropertiesNavigationViewItemFactory.cs index ceb1d386c022..acd28cd6b6f7 100644 --- a/src/Files.App/Data/Factories/PropertiesNavigationViewItemFactory.cs +++ b/src/Files.App/Data/Factories/PropertiesNavigationViewItemFactory.cs @@ -74,7 +74,8 @@ public static ObservableCollection Initialize if (item is List listedItems) { - var commonFileExt = listedItems.Select(x => x.FileExtension).Distinct().Count() == 1 ? listedItems.First().FileExtension : null; + var firstFileExtension = listedItems.FirstOrDefault()?.FileExtension; + var commonFileExt = listedItems.All(x => x.FileExtension == firstFileExtension) ? firstFileExtension : null; var compatibilityItemEnabled = listedItems.All(listedItem => FileExtensionHelpers.IsExecutableFile(listedItem is ShortcutItem sht ? sht.TargetPath : commonFileExt, true)); var onlyFiles = listedItems.All(listedItem => listedItem.PrimaryItemAttribute == StorageItemTypes.File || listedItem.IsArchive); diff --git a/src/Files.App/Data/Factories/ShellContextFlyoutHelper.cs b/src/Files.App/Data/Factories/ShellContextFlyoutHelper.cs index 1c158a14f0c4..2773f3d5b8db 100644 --- a/src/Files.App/Data/Factories/ShellContextFlyoutHelper.cs +++ b/src/Files.App/Data/Factories/ShellContextFlyoutHelper.cs @@ -80,9 +80,9 @@ bool filterMenuItemsImpl(string menuItem) => !string.IsNullOrEmpty(menuItem) var menuItems = menuFlyoutItems.TakeWhile(x => x.Type == MenuItemType.MFT_SEPARATOR || ++itemsCount <= itemsBeforeOverflow).ToList(); var overflowItems = menuFlyoutItems.Except(menuItems).ToList(); - if (overflowItems.Where(x => x.Type != MenuItemType.MFT_SEPARATOR).Any()) + if (overflowItems.Any(x => x.Type != MenuItemType.MFT_SEPARATOR)) { - var moreItem = menuItemsListLocal.Where(x => x.ID == "ItemOverflow").FirstOrDefault(); + var moreItem = menuItemsListLocal.FirstOrDefault(x => x.ID == "ItemOverflow"); if (moreItem is null) { var menuLayoutSubItem = new ContextMenuFlyoutItemViewModel() @@ -341,7 +341,7 @@ async Task InvokeShellMenuItemAsync(ContextMenu contextMenu, object? tag) OpacityIconStyle = "ColorIconOpenWith", }; var (_, openWithItems) = ContextFlyoutModelToElementHelper.GetAppBarItemsFromModel(new List() { openWithItem }); - var placeholder = itemContextMenuFlyout.SecondaryCommands.Where(x => Equals((x as AppBarButton)?.Tag, "OpenWithPlaceholder")).FirstOrDefault() as AppBarButton; + var placeholder = itemContextMenuFlyout.SecondaryCommands.FirstOrDefault(x => Equals((x as AppBarButton)?.Tag, "OpenWithPlaceholder")) as AppBarButton; if (placeholder is not null) placeholder.Visibility = Visibility.Collapsed; itemContextMenuFlyout.SecondaryCommands.Insert(0, openWithItems.FirstOrDefault()); @@ -353,7 +353,7 @@ async Task InvokeShellMenuItemAsync(ContextMenu contextMenu, object? tag) await sendToItem.LoadSubMenuAction(); var (_, sendToItems) = ContextFlyoutModelToElementHelper.GetAppBarItemsFromModel(new List() { sendToItem }); - var placeholder = itemContextMenuFlyout.SecondaryCommands.Where(x => Equals((x as AppBarButton)?.Tag, "SendToPlaceholder")).FirstOrDefault() as AppBarButton; + var placeholder = itemContextMenuFlyout.SecondaryCommands.FirstOrDefault(x => Equals((x as AppBarButton)?.Tag, "SendToPlaceholder")) as AppBarButton; if (placeholder is not null) placeholder.Visibility = Visibility.Collapsed; itemContextMenuFlyout.SecondaryCommands.Insert(1, sendToItems.FirstOrDefault()); diff --git a/src/Files.App/Data/Models/ItemViewModel.cs b/src/Files.App/Data/Models/ItemViewModel.cs index cc64fdc02acc..c89f0e0c9325 100644 --- a/src/Files.App/Data/Models/ItemViewModel.cs +++ b/src/Files.App/Data/Models/ItemViewModel.cs @@ -1060,7 +1060,7 @@ public async Task LoadExtendedItemPropertiesAsync(ListedItem item) BaseStorageFile? matchingStorageFile = null; if (item.Key is not null && FilesAndFolders.IsGrouped && FilesAndFolders.GetExtendedGroupHeaderInfo is not null) { - gp = FilesAndFolders.GroupedCollection?.ToList().Where(x => x.Model.Key == item.Key).FirstOrDefault(); + gp = FilesAndFolders.GroupedCollection?.ToList().FirstOrDefault(x => x.Model.Key == item.Key); loadGroupHeaderInfo = gp is not null && !gp.Model.Initialized && gp.GetExtendedGroupHeaderInfo is not null; } diff --git a/src/Files.App/Dialogs/CreateArchiveDialog.xaml.cs b/src/Files.App/Dialogs/CreateArchiveDialog.xaml.cs index 01e4c21d516f..7810ac9d71a9 100644 --- a/src/Files.App/Dialogs/CreateArchiveDialog.xaml.cs +++ b/src/Files.App/Dialogs/CreateArchiveDialog.xaml.cs @@ -165,13 +165,13 @@ public string Password } } - public IImmutableList FileFormats { get; } = new List + public ImmutableList FileFormats { get; } = new List { new(ArchiveFormats.Zip, ".zip"), new(ArchiveFormats.SevenZip, ".7z"), }.ToImmutableList(); - public IImmutableList CompressionLevels { get; } = new List + public ImmutableList CompressionLevels { get; } = new List { new CompressionLevelItem(ArchiveCompressionLevels.Ultra, "CompressionLevelUltra".GetLocalizedResource()), new CompressionLevelItem(ArchiveCompressionLevels.High, "CompressionLevelHigh".GetLocalizedResource()), @@ -181,7 +181,7 @@ public string Password new CompressionLevelItem(ArchiveCompressionLevels.None, "CompressionLevelNone".GetLocalizedResource()), }.ToImmutableList(); - public IImmutableList SplittingSizes { get; } = new List + public ImmutableList SplittingSizes { get; } = new List { new(ArchiveSplittingSizes.None, "Do not split".GetLocalizedResource()), new(ArchiveSplittingSizes.Mo10, ToSizeText(10)), diff --git a/src/Files.App/Services/DialogService.cs b/src/Files.App/Services/DialogService.cs index cbadb612ba70..95f24887b506 100644 --- a/src/Files.App/Services/DialogService.cs +++ b/src/Files.App/Services/DialogService.cs @@ -1,6 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. +using System.Collections.Frozen; using Files.App.Dialogs; using Files.App.ViewModels.Dialogs; using Files.Core.Services; @@ -16,7 +17,7 @@ namespace Files.App.Services /// internal sealed class DialogService : IDialogService { - private readonly IReadOnlyDictionary> _dialogs; + private readonly FrozenDictionary> _dialogs; public DialogService() { @@ -34,7 +35,7 @@ public DialogService() { typeof(GitHubLoginDialogViewModel), () => new GitHubLoginDialog() }, { typeof(FileTooLargeDialogViewModel), () => new FileTooLargeDialog() }, { typeof(ReleaseNotesDialogViewModel), () => new ReleaseNotesDialog() }, - }; + }.ToFrozenDictionary(); } /// diff --git a/src/Files.App/Services/JumpListService.cs b/src/Files.App/Services/JumpListService.cs index 0a64ac420d14..4e1439df9d00 100644 --- a/src/Files.App/Services/JumpListService.cs +++ b/src/Files.App/Services/JumpListService.cs @@ -113,7 +113,7 @@ private void AddFolder(string path, string group, JumpList instance) var drivesViewModel = Ioc.Default.GetRequiredService(); // Jumplist item argument can't end with a slash so append a character that can't exist in a directory name to support listing drives. - var drive = drivesViewModel.Drives.Where(drive => drive.Path == path).FirstOrDefault(); + var drive = drivesViewModel.Drives.FirstOrDefault(drive => drive.Path == path); if (drive is null) return; @@ -162,7 +162,7 @@ private void AddFolder(string path, string group, JumpList instance) } else { - var pinnedItemsCount = instance.Items.Where(x => x.GroupName == JumpListPinnedGroupHeader).Count(); + var pinnedItemsCount = instance.Items.Count(x => x.GroupName == JumpListPinnedGroupHeader); instance.Items.Insert(pinnedItemsCount, jumplistItem); } } diff --git a/src/Files.App/UserControls/Widgets/DrivesWidget.xaml.cs b/src/Files.App/UserControls/Widgets/DrivesWidget.xaml.cs index cbca27b57411..9f9a3cff30e6 100644 --- a/src/Files.App/UserControls/Widgets/DrivesWidget.xaml.cs +++ b/src/Files.App/UserControls/Widgets/DrivesWidget.xaml.cs @@ -111,7 +111,7 @@ private async void Drives_CollectionChanged(object? sender, NotifyCollectionChan public override List GetItemMenuItems(WidgetCardItem item, bool isPinned, bool isFolder = false) { - var drive = ItemsAdded.Where(x => string.Equals(PathNormalization.NormalizePath(x.Path), PathNormalization.NormalizePath(item.Path), StringComparison.OrdinalIgnoreCase)).FirstOrDefault(); + var drive = ItemsAdded.FirstOrDefault(x => string.Equals(PathNormalization.NormalizePath(x.Path), PathNormalization.NormalizePath(item.Path), StringComparison.OrdinalIgnoreCase)); var options = drive?.Item.MenuOptions; return new List() diff --git a/src/Files.App/UserControls/Widgets/QuickAccessWidget.xaml.cs b/src/Files.App/UserControls/Widgets/QuickAccessWidget.xaml.cs index f9466c677e7e..a6d93e9b937d 100644 --- a/src/Files.App/UserControls/Widgets/QuickAccessWidget.xaml.cs +++ b/src/Files.App/UserControls/Widgets/QuickAccessWidget.xaml.cs @@ -311,7 +311,7 @@ public override async Task PinToSidebarAsync(WidgetCardItem item) var items = (await QuickAccessService.GetPinnedFoldersAsync()) .Where(link => !((bool?)link.Properties["System.Home.IsPinned"] ?? false)); - var recentItem = items.Where(x => !ItemsAdded.ToList().Select(y => y.Path).Contains(x.FilePath)).FirstOrDefault(); + var recentItem = items.FirstOrDefault(x => !ItemsAdded.ToList().Select(y => y.Path).Contains(x.FilePath)); if (recentItem is not null) { ModifyItemAsync(this, new ModifyQuickAccessEventArgs(new[] { recentItem.FilePath }, true) diff --git a/src/Files.App/Utils/Archives/DecompressHelper.cs b/src/Files.App/Utils/Archives/DecompressHelper.cs index 1a72a20d7694..90503bd6bea1 100644 --- a/src/Files.App/Utils/Archives/DecompressHelper.cs +++ b/src/Files.App/Utils/Archives/DecompressHelper.cs @@ -32,7 +32,7 @@ public static async Task DecompressArchiveAsync(BaseStorageFile archive, B // Fill files byte[] buffer = new byte[4096]; - int entriesAmount = zipFile.ArchiveFileData.Where(x => !x.IsDirectory).Count(); + int entriesAmount = zipFile.ArchiveFileData.Count(x => !x.IsDirectory); StatusCenterItemProgressModel fsProgress = new( progress, diff --git a/src/Files.App/Utils/Shell/ContextMenu.cs b/src/Files.App/Utils/Shell/ContextMenu.cs index 3d96a6da428b..232c950b3349 100644 --- a/src/Files.App/Utils/Shell/ContextMenu.cs +++ b/src/Files.App/Utils/Shell/ContextMenu.cs @@ -53,7 +53,7 @@ public async Task InvokeVerb(string? verb) if (string.IsNullOrEmpty(verb)) return false; - var item = Items.Where(x => x.CommandString == verb).FirstOrDefault(); + var item = Items.FirstOrDefault(x => x.CommandString == verb); if (item is not null && item.ID >= 0) // Prefer invocation by ID return await InvokeItem(item.ID); diff --git a/src/Files.App/Utils/Storage/History/StorageHistoryOperations.cs b/src/Files.App/Utils/Storage/History/StorageHistoryOperations.cs index 80aa225e9dde..c78375318323 100644 --- a/src/Files.App/Utils/Storage/History/StorageHistoryOperations.cs +++ b/src/Files.App/Utils/Storage/History/StorageHistoryOperations.cs @@ -9,7 +9,7 @@ namespace Files.App.Utils.Storage public sealed class StorageHistoryOperations : IStorageHistoryOperations { private IFilesystemHelpers helpers; - private IFilesystemOperations operations; + private ShellFilesystemOperations operations; private readonly CancellationToken cancellationToken; @@ -47,7 +47,7 @@ public async Task Undo(IStorageHistory history) if (!IsHistoryNull(history)) { NameCollisionOption collision = NameCollisionOption.GenerateUniqueName; - for (int i = 0; i < history.Destination.Count(); i++) + for (int i = 0; i < history.Destination.Count; i++) { string name = Path.GetFileName(history.Source[i].Path); await operations.RenameAsync(history.Destination[i], name, collision, progress, cancellationToken); diff --git a/src/Files.App/Utils/Storage/Operations/ShellFilesystemOperations.cs b/src/Files.App/Utils/Storage/Operations/ShellFilesystemOperations.cs index 9ab7ffdd15e1..a28dcff2ada1 100644 --- a/src/Files.App/Utils/Storage/Operations/ShellFilesystemOperations.cs +++ b/src/Files.App/Utils/Storage/Operations/ShellFilesystemOperations.cs @@ -49,7 +49,7 @@ public async Task CopyItemsAsync(IList so progress, true, FileSystemStatusCode.InProgress, - source.Count()); + source.Count); fsProgress.Report(); @@ -353,7 +353,7 @@ public async Task DeleteItemsAsync(IList progress, true, FileSystemStatusCode.InProgress, - source.Count()); + source.Count); fsProgress.Report(); @@ -472,7 +472,7 @@ public async Task MoveItemsAsync(IList so progress, true, FileSystemStatusCode.InProgress, - source.Count()); + source.Count); fsProgress.Report(); diff --git a/src/Files.App/ViewModels/Properties/HashesViewModel.cs b/src/Files.App/ViewModels/Properties/HashesViewModel.cs index f40ec77bc060..4089979f9682 100644 --- a/src/Files.App/ViewModels/Properties/HashesViewModel.cs +++ b/src/Files.App/ViewModels/Properties/HashesViewModel.cs @@ -59,7 +59,7 @@ public HashesViewModel(ListedItem item) private void ToggleIsEnabled(string? algorithm) { - var hashInfoItem = Hashes.Where(x => x.Algorithm == algorithm).First(); + var hashInfoItem = Hashes.First(x => x.Algorithm == algorithm); hashInfoItem.IsEnabled = !hashInfoItem.IsEnabled; if (ShowHashes[hashInfoItem.Algorithm] != hashInfoItem.IsEnabled) diff --git a/src/Files.App/ViewModels/Settings/AppearanceViewModel.cs b/src/Files.App/ViewModels/Settings/AppearanceViewModel.cs index 0c71d6ba95fa..dbf7a169fb25 100644 --- a/src/Files.App/ViewModels/Settings/AppearanceViewModel.cs +++ b/src/Files.App/ViewModels/Settings/AppearanceViewModel.cs @@ -67,8 +67,7 @@ private void UpdateSelectedResource() } SelectedAppThemeResources = AppThemeResources - .Where(p => p.BackgroundColor == themeBackgroundColor) - .FirstOrDefault() ?? AppThemeResources[0]; + .FirstOrDefault(p => p.BackgroundColor == themeBackgroundColor) ?? AppThemeResources[0]; } private AppThemeResourceItem selectedAppThemeResources; diff --git a/src/Files.App/ViewModels/UserControls/Previews/CodePreviewViewModel.cs b/src/Files.App/ViewModels/UserControls/Previews/CodePreviewViewModel.cs index 98f5bc947c4a..f0fe0cde10b6 100644 --- a/src/Files.App/ViewModels/UserControls/Previews/CodePreviewViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Previews/CodePreviewViewModel.cs @@ -1,6 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. +using System.Collections.Frozen; using ColorCode; using Files.App.ViewModels.Properties; @@ -8,7 +9,7 @@ namespace Files.App.ViewModels.Previews { public sealed class CodePreviewViewModel : BasePreviewModel { - private static readonly Lazy> extensions = new(GetDictionary); + private static readonly FrozenDictionary extensions = GetDictionary(); private string textValue; public string TextValue @@ -30,7 +31,7 @@ public CodePreviewViewModel(ListedItem item) } public static bool ContainsExtension(string extension) - => extensions.Value.ContainsKey(extension); + => extensions.ContainsKey(extension); public async override Task> LoadPreviewAndDetailsAsync() { @@ -41,7 +42,7 @@ public async override Task> LoadPreviewAndDetailsAsync() var text = TextValue ?? await ReadFileAsTextAsync(Item.ItemFile); details.Add(GetFileProperty("PropertyLineCount", text.Split('\n').Length)); - CodeLanguage = extensions.Value[Item.FileExtension.ToLowerInvariant()]; + CodeLanguage = extensions[Item.FileExtension.ToLowerInvariant()]; TextValue = text.Left(Constants.PreviewPane.TextCharacterLimit); } catch (Exception e) @@ -52,7 +53,7 @@ public async override Task> LoadPreviewAndDetailsAsync() return details; } - private static IReadOnlyDictionary GetDictionary() + private static FrozenDictionary GetDictionary() { var items = new Dictionary { @@ -84,7 +85,7 @@ public async override Task> LoadPreviewAndDetailsAsync() } } - return new ReadOnlyDictionary(dictionary); + return dictionary.ToFrozenDictionary(); } } } diff --git a/src/Files.App/Views/Layouts/BaseLayoutPage.cs b/src/Files.App/Views/Layouts/BaseLayoutPage.cs index d61acbe0229d..68c99a3eebb7 100644 --- a/src/Files.App/Views/Layouts/BaseLayoutPage.cs +++ b/src/Files.App/Views/Layouts/BaseLayoutPage.cs @@ -776,7 +776,7 @@ private async Task AddShellMenuItemsAsync(List s var ttv = secondaryMenu.TransformToVisual(MainWindow.Instance.Content); var cMenuPos = ttv.TransformPoint(new Point(0, 0)); - var requiredHeight = contextMenuFlyout.SecondaryCommands.Concat(mainItems).Where(x => x is not AppBarSeparator).Count() * Constants.UI.ContextMenuSecondaryItemsHeight; + var requiredHeight = contextMenuFlyout.SecondaryCommands.Concat(mainItems).Count(x => x is not AppBarSeparator) * Constants.UI.ContextMenuSecondaryItemsHeight; var availableHeight = MainWindow.Instance.Bounds.Height - cMenuPos.Y - Constants.UI.ContextMenuPrimaryItemsHeight; // Set menu max height to current height (Avoid menu repositioning) diff --git a/src/Files.App/Views/Shells/BaseShellPage.cs b/src/Files.App/Views/Shells/BaseShellPage.cs index fe380dcb33b2..9eb986389d9d 100644 --- a/src/Files.App/Views/Shells/BaseShellPage.cs +++ b/src/Files.App/Views/Shells/BaseShellPage.cs @@ -688,7 +688,7 @@ protected void FilesystemViewModel_ItemLoadStatusChanged(object sender, ItemLoad if (folderToSelect.EndsWith(separator)) folderToSelect = folderToSelect.Remove(folderToSelect.Length - 1, 1); - var itemToSelect = FilesystemViewModel.FilesAndFolders.ToList().Where((item) => item.ItemPath == folderToSelect).FirstOrDefault(); + var itemToSelect = FilesystemViewModel.FilesAndFolders.ToList().FirstOrDefault((item) => item.ItemPath == folderToSelect); if (itemToSelect is not null && ContentPage is not null) { diff --git a/src/Files.Core/Data/Models/ByteSize.cs b/src/Files.Core/Data/Models/ByteSize.cs index 5dd3821b63d3..8a782e8afbda 100644 --- a/src/Files.Core/Data/Models/ByteSize.cs +++ b/src/Files.Core/Data/Models/ByteSize.cs @@ -1,13 +1,13 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -using System.Collections.Immutable; +using System.Collections.Frozen; namespace Files.Core.Data.Models { public struct ByteSize : IEquatable, IComparable { - private static readonly IImmutableDictionary units = new Dictionary + private static readonly FrozenDictionary units = new Dictionary { [ByteSizeLib.ByteSize.BitSymbol] = "ByteSymbol".ToLocalized(), [ByteSizeLib.ByteSize.ByteSymbol] = "ByteSymbol".ToLocalized(), @@ -16,7 +16,7 @@ public struct ByteSize : IEquatable, IComparable [ByteSizeLib.ByteSize.GibiByteSymbol] = "GigaByteSymbol".ToLocalized(), [ByteSizeLib.ByteSize.TebiByteSymbol] = "TeraByteSymbol".ToLocalized(), [ByteSizeLib.ByteSize.PebiByteSymbol] = "PetaByteSymbol".ToLocalized(), - }.ToImmutableDictionary(); + }.ToFrozenDictionary(); private readonly ByteSizeLib.ByteSize size; From f3882fbc6e9e798e74bae29b64822fa586b7d8f8 Mon Sep 17 00:00:00 2001 From: Yair <39923744+yaira2@users.noreply.github.com> Date: Sat, 30 Mar 2024 21:40:44 -0400 Subject: [PATCH 04/41] Feature: Added support for Listary integration (#15069) --- src/Files.App/UserControls/AddressToolbar.xaml | 12 ++++++++++++ .../UserControls/AddressToolbarViewModel.cs | 6 ++++++ 2 files changed, 18 insertions(+) diff --git a/src/Files.App/UserControls/AddressToolbar.xaml b/src/Files.App/UserControls/AddressToolbar.xaml index fedeec6325ab..f60185b763b4 100644 --- a/src/Files.App/UserControls/AddressToolbar.xaml +++ b/src/Files.App/UserControls/AddressToolbar.xaml @@ -217,6 +217,18 @@ + + + + + + + + + Date: Sun, 31 Mar 2024 16:23:59 +0200 Subject: [PATCH 05/41] Fix: Fixed bug where the wrong item was shown as active in the sidebar (#15080) --- src/Files.App/ViewModels/UserControls/SidebarViewModel.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Files.App/ViewModels/UserControls/SidebarViewModel.cs b/src/Files.App/ViewModels/UserControls/SidebarViewModel.cs index 806a8fe06d7c..92c16d406cbf 100644 --- a/src/Files.App/ViewModels/UserControls/SidebarViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/SidebarViewModel.cs @@ -113,8 +113,7 @@ public void UpdateSidebarSelectedItemFromArgs(string? arg) } item = filteredItems.FirstOrDefault(x => x.Path.Equals(value, StringComparison.OrdinalIgnoreCase)); - item ??= filteredItems.FirstOrDefault(x => x.Path.Equals(value + "\\", StringComparison.OrdinalIgnoreCase)); - item ??= filteredItems.Where(x => value.StartsWith(x.Path, StringComparison.OrdinalIgnoreCase)).MaxBy(x => x.Path.Length); + item ??= filteredItems.Where(x => value.StartsWith(x.Path + "\\", StringComparison.OrdinalIgnoreCase)).MaxBy(x => x.Path.Length); item ??= filteredItems.FirstOrDefault(x => x.Path.Equals(Path.GetPathRoot(value), StringComparison.OrdinalIgnoreCase)); if (item is null && value == "Home") From abe586beb7e9e1d41dc00c0a3c8ad339e5a603b0 Mon Sep 17 00:00:00 2001 From: hishitetsu <66369541+hishitetsu@users.noreply.github.com> Date: Sun, 31 Mar 2024 23:24:57 +0900 Subject: [PATCH 06/41] Code Quality: Update dependencies (#15082) --- src/Files.App/Files.App.csproj | 12 ++++++------ .../UserControls/AddressToolbarViewModel.cs | 1 + .../Files.InteractionTests.csproj | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/Files.App/Files.App.csproj b/src/Files.App/Files.App.csproj index 8e43a72a060b..7a035134eca0 100644 --- a/src/Files.App/Files.App.csproj +++ b/src/Files.App/Files.App.csproj @@ -72,11 +72,11 @@ - + - + @@ -95,11 +95,11 @@ - - + + - - + + diff --git a/src/Files.App/ViewModels/UserControls/AddressToolbarViewModel.cs b/src/Files.App/ViewModels/UserControls/AddressToolbarViewModel.cs index f6b7ba275695..f1e5f86af0e5 100644 --- a/src/Files.App/ViewModels/UserControls/AddressToolbarViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/AddressToolbarViewModel.cs @@ -12,6 +12,7 @@ using System.Windows.Input; using Windows.ApplicationModel.DataTransfer; using Windows.UI.Text; +using FocusManager = Microsoft.UI.Xaml.Input.FocusManager; namespace Files.App.ViewModels.UserControls { diff --git a/tests/Files.InteractionTests/Files.InteractionTests.csproj b/tests/Files.InteractionTests/Files.InteractionTests.csproj index dcddd7908183..7f3b27f4cd60 100644 --- a/tests/Files.InteractionTests/Files.InteractionTests.csproj +++ b/tests/Files.InteractionTests/Files.InteractionTests.csproj @@ -19,7 +19,7 @@ - + From 322513e78ae5aedced98fd56d14f347487881be9 Mon Sep 17 00:00:00 2001 From: Marco Franzen Date: Sun, 31 Mar 2024 16:26:41 +0200 Subject: [PATCH 07/41] Fixed: Fixed issue where holding the arrow key would cause preview pane to freeze (#15076) --- .../UserControls/InfoPaneViewModel.cs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/Files.App/ViewModels/UserControls/InfoPaneViewModel.cs b/src/Files.App/ViewModels/UserControls/InfoPaneViewModel.cs index ee5385ac98a9..1f914202a1a4 100644 --- a/src/Files.App/ViewModels/UserControls/InfoPaneViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/InfoPaneViewModel.cs @@ -36,8 +36,8 @@ public bool IsEnabled /// Current selected item in the file list. /// TODO see about removing this and accessing it from the page context instead /// - private ListedItem selectedItem; - public ListedItem SelectedItem + private ListedItem? selectedItem; + public ListedItem? SelectedItem { get => selectedItem; set @@ -117,17 +117,24 @@ public InfoPaneViewModel() IsEnabled = infoPaneSettingsService.IsEnabled; } - private void ContentPageContext_PropertyChanged(object? sender, PropertyChangedEventArgs e) + private async void ContentPageContext_PropertyChanged(object? sender, PropertyChangedEventArgs e) { switch (e.PropertyName) { case nameof(IContentPageContext.Folder): case nameof(IContentPageContext.SelectedItem): + ListedItem? tempSelectedItem = null; if (contentPageContext.SelectedItems.Count == 1) - SelectedItem = contentPageContext.SelectedItems.First(); - else - SelectedItem = null; + tempSelectedItem = contentPageContext.SelectedItems.First(); + + // Don't update preview pane when the selected item changes too frequently + const int delayBeforeUpdatingPreviewPane = 100; + await Task.Delay(delayBeforeUpdatingPreviewPane); + if (tempSelectedItem is not null && !tempSelectedItem.Equals(contentPageContext.SelectedItem)) + return; + + SelectedItem = tempSelectedItem; if (!App.AppModel.IsMainWindowClosed) { From 7cde881b6aea31fb128781dbd802fb8778bfcfd9 Mon Sep 17 00:00:00 2001 From: Marco Franzen Date: Sun, 31 Mar 2024 16:39:38 +0200 Subject: [PATCH 08/41] Fix: Fixed issue with editing properties of multiple items at the same time (#15071) --- .../SelectedItemsPropertiesViewModel.cs | 16 +++--- .../Properties/Items/CombinedProperties.cs | 56 +++++++++++++------ .../Properties/Items/FileProperties.cs | 34 ++++------- .../Properties/Items/FolderProperties.cs | 13 ++--- .../Properties/Items/LibraryProperties.cs | 22 ++++---- .../Views/Properties/GeneralPage.xaml.cs | 17 ++++-- 6 files changed, 83 insertions(+), 75 deletions(-) diff --git a/src/Files.App/Data/Models/SelectedItemsPropertiesViewModel.cs b/src/Files.App/Data/Models/SelectedItemsPropertiesViewModel.cs index 97a7a62b80ef..7ae6ba094138 100644 --- a/src/Files.App/Data/Models/SelectedItemsPropertiesViewModel.cs +++ b/src/Files.App/Data/Models/SelectedItemsPropertiesViewModel.cs @@ -663,8 +663,8 @@ public ObservableCollection FileProperties set => SetProperty(ref fileProperties, value); } - private bool isReadOnly; - public bool IsReadOnly + private bool? isReadOnly; + public bool? IsReadOnly { get => isReadOnly; set @@ -675,8 +675,8 @@ public bool IsReadOnly } } - private bool isReadOnlyEditedValue; - public bool IsReadOnlyEditedValue + private bool? isReadOnlyEditedValue; + public bool? IsReadOnlyEditedValue { get => isReadOnlyEditedValue; set @@ -693,8 +693,8 @@ public bool IsReadOnlyEnabled set => SetProperty(ref isReadOnlyEnabled, value); } - private bool isHidden; - public bool IsHidden + private bool? isHidden; + public bool? IsHidden { get => isHidden; set @@ -704,8 +704,8 @@ public bool IsHidden } } - private bool isHiddenEditedValue; - public bool IsHiddenEditedValue + private bool? isHiddenEditedValue; + public bool? IsHiddenEditedValue { get => isHiddenEditedValue; set => SetProperty(ref isHiddenEditedValue, value); diff --git a/src/Files.App/ViewModels/Properties/Items/CombinedProperties.cs b/src/Files.App/ViewModels/Properties/Items/CombinedProperties.cs index a13e30134b30..ef9849f1b915 100644 --- a/src/Files.App/ViewModels/Properties/Items/CombinedProperties.cs +++ b/src/Files.App/ViewModels/Properties/Items/CombinedProperties.cs @@ -59,9 +59,23 @@ public sealed override void GetBaseProperties() public override async Task GetSpecialPropertiesAsync() { if (List.All(x => x.PrimaryItemAttribute == StorageItemTypes.File)) - ViewModel.IsReadOnly = List.All(x => NativeFileOperationsHelper.HasFileAttribute(x.ItemPath, System.IO.FileAttributes.ReadOnly)); + { + var fileAttributesReadOnly = List.Select(x => NativeFileOperationsHelper.HasFileAttribute(x.ItemPath, System.IO.FileAttributes.ReadOnly)); + if (fileAttributesReadOnly.All(x => x)) + ViewModel.IsReadOnly = true; + else if (!fileAttributesReadOnly.Any(x => x)) + ViewModel.IsReadOnly = false; + else + ViewModel.IsReadOnly = null; + } - ViewModel.IsHidden = List.All(x => NativeFileOperationsHelper.HasFileAttribute(x.ItemPath, System.IO.FileAttributes.Hidden)); + var fileAttributesHidden = List.Select(x => NativeFileOperationsHelper.HasFileAttribute(x.ItemPath, System.IO.FileAttributes.Hidden)); + if (fileAttributesHidden.All(x => x)) + ViewModel.IsHidden = true; + else if (!fileAttributesHidden.Any(x => x)) + ViewModel.IsHidden = false; + else + ViewModel.IsHidden = null; ViewModel.LastSeparatorVisibility = false; ViewModel.ItemSizeVisibility = true; @@ -122,30 +136,36 @@ private void ViewModel_PropertyChanged(object sender, System.ComponentModel.Prop { case "IsReadOnly": { - if (ViewModel.IsReadOnly) + if (ViewModel.IsReadOnly is not null) { - List.ForEach(x => NativeFileOperationsHelper.SetFileAttribute( - x.ItemPath, System.IO.FileAttributes.ReadOnly)); - } - else - { - List.ForEach(x => NativeFileOperationsHelper.UnsetFileAttribute( - x.ItemPath, System.IO.FileAttributes.ReadOnly)); + if ((bool)ViewModel.IsReadOnly) + { + List.ForEach(x => NativeFileOperationsHelper.SetFileAttribute( + x.ItemPath, System.IO.FileAttributes.ReadOnly)); + } + else + { + List.ForEach(x => NativeFileOperationsHelper.UnsetFileAttribute( + x.ItemPath, System.IO.FileAttributes.ReadOnly)); + } } } break; case "IsHidden": { - if (ViewModel.IsHidden) - { - List.ForEach(x => NativeFileOperationsHelper.SetFileAttribute( - x.ItemPath, System.IO.FileAttributes.Hidden)); - } - else + if (ViewModel.IsHidden is not null) { - List.ForEach(x => NativeFileOperationsHelper.UnsetFileAttribute( - x.ItemPath, System.IO.FileAttributes.Hidden)); + if ((bool)ViewModel.IsHidden) + { + List.ForEach(x => NativeFileOperationsHelper.SetFileAttribute( + x.ItemPath, System.IO.FileAttributes.Hidden)); + } + else + { + List.ForEach(x => NativeFileOperationsHelper.UnsetFileAttribute( + x.ItemPath, System.IO.FileAttributes.Hidden)); + } } } diff --git a/src/Files.App/ViewModels/Properties/Items/FileProperties.cs b/src/Files.App/ViewModels/Properties/Items/FileProperties.cs index d6dd2d85921a..cc0cd707f791 100644 --- a/src/Files.App/ViewModels/Properties/Items/FileProperties.cs +++ b/src/Files.App/ViewModels/Properties/Items/FileProperties.cs @@ -275,37 +275,23 @@ private async void ViewModel_PropertyChanged(object sender, System.ComponentMode switch (e.PropertyName) { case nameof(ViewModel.IsReadOnly): - if (ViewModel.IsReadOnly) + if (ViewModel.IsReadOnly is not null) { - NativeFileOperationsHelper.SetFileAttribute( - Item.ItemPath, - System.IO.FileAttributes.ReadOnly - ); - } - else - { - NativeFileOperationsHelper.UnsetFileAttribute( - Item.ItemPath, - System.IO.FileAttributes.ReadOnly - ); + if ((bool)ViewModel.IsReadOnly) + NativeFileOperationsHelper.SetFileAttribute(Item.ItemPath, System.IO.FileAttributes.ReadOnly); + else + NativeFileOperationsHelper.UnsetFileAttribute(Item.ItemPath, System.IO.FileAttributes.ReadOnly); } break; case nameof(ViewModel.IsHidden): - if (ViewModel.IsHidden) - { - NativeFileOperationsHelper.SetFileAttribute( - Item.ItemPath, - System.IO.FileAttributes.Hidden - ); - } - else + if (ViewModel.IsHidden is not null) { - NativeFileOperationsHelper.UnsetFileAttribute( - Item.ItemPath, - System.IO.FileAttributes.Hidden - ); + if ((bool)ViewModel.IsHidden) + NativeFileOperationsHelper.SetFileAttribute(Item.ItemPath, System.IO.FileAttributes.Hidden); + else + NativeFileOperationsHelper.UnsetFileAttribute(Item.ItemPath, System.IO.FileAttributes.Hidden); } break; diff --git a/src/Files.App/ViewModels/Properties/Items/FolderProperties.cs b/src/Files.App/ViewModels/Properties/Items/FolderProperties.cs index 84149c699dea..52f616258502 100644 --- a/src/Files.App/ViewModels/Properties/Items/FolderProperties.cs +++ b/src/Files.App/ViewModels/Properties/Items/FolderProperties.cs @@ -200,15 +200,12 @@ private async void ViewModel_PropertyChanged(object sender, System.ComponentMode switch (e.PropertyName) { case "IsHidden": - if (ViewModel.IsHidden) + if (ViewModel.IsHidden is not null) { - NativeFileOperationsHelper.SetFileAttribute( - Item.ItemPath, System.IO.FileAttributes.Hidden); - } - else - { - NativeFileOperationsHelper.UnsetFileAttribute( - Item.ItemPath, System.IO.FileAttributes.Hidden); + if ((bool)ViewModel.IsHidden) + NativeFileOperationsHelper.SetFileAttribute(Item.ItemPath, System.IO.FileAttributes.Hidden); + else + NativeFileOperationsHelper.UnsetFileAttribute(Item.ItemPath, System.IO.FileAttributes.Hidden); } break; diff --git a/src/Files.App/ViewModels/Properties/Items/LibraryProperties.cs b/src/Files.App/ViewModels/Properties/Items/LibraryProperties.cs index b89862acf7fe..7871789bb773 100644 --- a/src/Files.App/ViewModels/Properties/Items/LibraryProperties.cs +++ b/src/Files.App/ViewModels/Properties/Items/LibraryProperties.cs @@ -141,25 +141,23 @@ private void ViewModel_PropertyChanged(object sender, System.ComponentModel.Prop switch (e.PropertyName) { case "IsReadOnly": - if (ViewModel.IsReadOnly) + if (ViewModel.IsReadOnly is not null) { - NativeFileOperationsHelper.SetFileAttribute(Library.ItemPath, System.IO.FileAttributes.ReadOnly); - } - else - { - NativeFileOperationsHelper.UnsetFileAttribute(Library.ItemPath, System.IO.FileAttributes.ReadOnly); + if ((bool)ViewModel.IsReadOnly) + NativeFileOperationsHelper.SetFileAttribute(Library.ItemPath, System.IO.FileAttributes.ReadOnly); + else + NativeFileOperationsHelper.UnsetFileAttribute(Library.ItemPath, System.IO.FileAttributes.ReadOnly); } break; case "IsHidden": - if (ViewModel.IsHidden) - { - NativeFileOperationsHelper.SetFileAttribute(Library.ItemPath, System.IO.FileAttributes.Hidden); - } - else + if (ViewModel.IsHidden is not null) { - NativeFileOperationsHelper.UnsetFileAttribute(Library.ItemPath, System.IO.FileAttributes.Hidden); + if ((bool)ViewModel.IsHidden) + NativeFileOperationsHelper.SetFileAttribute(Library.ItemPath, System.IO.FileAttributes.Hidden); + else + NativeFileOperationsHelper.UnsetFileAttribute(Library.ItemPath, System.IO.FileAttributes.Hidden); } break; diff --git a/src/Files.App/Views/Properties/GeneralPage.xaml.cs b/src/Files.App/Views/Properties/GeneralPage.xaml.cs index f9b1ad91c7ec..2ec15dd24a61 100644 --- a/src/Files.App/Views/Properties/GeneralPage.xaml.cs +++ b/src/Files.App/Views/Properties/GeneralPage.xaml.cs @@ -131,9 +131,16 @@ async Task SaveCombinedAsync(IList fileOrFolders) { foreach (var fileOrFolder in fileOrFolders) { - await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(() => - UIFilesystemHelpers.SetHiddenAttributeItem(fileOrFolder, ViewModel.IsHidden, itemMM) - ); + if (ViewModel.IsHiddenEditedValue is not null) + { + var isHiddenEditedValue = (bool)ViewModel.IsHiddenEditedValue; + await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(() => + UIFilesystemHelpers.SetHiddenAttributeItem(fileOrFolder, isHiddenEditedValue, itemMM) + ); + ViewModel.IsHidden = isHiddenEditedValue; + } + + ViewModel.IsReadOnly = ViewModel.IsReadOnlyEditedValue; if (ViewModel.IsAblumCoverModified) { @@ -153,10 +160,10 @@ async Task SaveBaseAsync(ListedItem item) { // Handle the visibility attribute for a single file var itemMM = AppInstance?.SlimContentPage?.ItemManipulationModel; - if (itemMM is not null) // null on homepage + if (itemMM is not null && ViewModel.IsHiddenEditedValue is not null) // null on homepage { await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(() => - UIFilesystemHelpers.SetHiddenAttributeItem(item, ViewModel.IsHidden, itemMM) + UIFilesystemHelpers.SetHiddenAttributeItem(item, (bool)ViewModel.IsHiddenEditedValue, itemMM) ); } From fd57b28139d7d76ec727531ca82cce97853817db Mon Sep 17 00:00:00 2001 From: Yair <39923744+yaira2@users.noreply.github.com> Date: Sun, 31 Mar 2024 10:45:52 -0400 Subject: [PATCH 09/41] Fix: Fixed conflict with svg thumbnails generated by PowerToys (#15073) --- src/Files.App/Data/Models/ItemViewModel.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Files.App/Data/Models/ItemViewModel.cs b/src/Files.App/Data/Models/ItemViewModel.cs index c89f0e0c9325..7b1819c93ff9 100644 --- a/src/Files.App/Data/Models/ItemViewModel.cs +++ b/src/Files.App/Data/Models/ItemViewModel.cs @@ -923,7 +923,9 @@ private async Task LoadThumbnailAsync(ListedItem item, CancellationToken cancell var returnIconOnly = UserSettingsService.FoldersSettingsService.ShowThumbnails == false || thumbnailSize < 48; byte[]? result = null; - if (item.IsFolder) + + // Non-cached thumbnails take longer to generate + if (item.IsFolder || !FileExtensionHelpers.IsExecutableFile(item.FileExtension)) { if (!returnIconOnly) { From 58552ac3ec806584fd87876aa94bb4b8cb4e5f30 Mon Sep 17 00:00:00 2001 From: 0x5bfa <62196528+0x5bfa@users.noreply.github.com> Date: Mon, 1 Apr 2024 07:03:54 +0900 Subject: [PATCH 10/41] Code Quality: Merge P/Invoke into single partial class (#15075) --- .../Contracts/IDataTransferManagerInterop.cs | 17 ++ src/Files.App/Data/Models/AppModel.cs | 2 +- src/Files.App/Data/Models/ItemViewModel.cs | 2 +- src/Files.App/Data/Models/RemovableDevice.cs | 2 +- src/Files.App/Helpers/Interop/FileUtils.cs | 145 ------------ .../Helpers/Interop/InteropHelpers.cs | 107 --------- .../Interop/NativeDirectoryChangesHelper.cs | 80 ------- .../Interop/NativeIoDeviceControlHelper.cs | 61 ----- src/Files.App/Helpers/ShareItemHelpers.cs | 2 +- .../Helpers/Win32/Win32Helper.Process.cs | 71 ++++++ .../Win32/Win32Helper.WindowManagement.cs | 40 +++- .../Helpers/Win32/Win32PInvoke.Consts.cs | 35 +++ .../Helpers/Win32/Win32PInvoke.Enums.cs | 19 ++ .../Helpers/Win32/Win32PInvoke.Methods.cs | 214 ++++++++++++++++++ .../Helpers/Win32/Win32PInvoke.Structs.cs | 82 +++++++ src/Files.App/MainWindow.xaml.cs | 2 +- src/Files.App/Program.cs | 2 +- .../UserControls/TabBar/TabBar.xaml.cs | 6 +- .../Storage/Helpers/FilePropertiesHelpers.cs | 2 +- .../Operations/FileOperationsHelpers.cs | 2 +- .../Dialogs/CreateShortcutDialogViewModel.cs | 6 +- .../Previews/ShellPreviewViewModel.cs | 4 +- .../Views/Layouts/BaseGroupableLayoutPage.cs | 2 +- 23 files changed, 494 insertions(+), 411 deletions(-) create mode 100644 src/Files.App/Data/Contracts/IDataTransferManagerInterop.cs delete mode 100644 src/Files.App/Helpers/Interop/FileUtils.cs delete mode 100644 src/Files.App/Helpers/Interop/InteropHelpers.cs delete mode 100644 src/Files.App/Helpers/Interop/NativeDirectoryChangesHelper.cs delete mode 100644 src/Files.App/Helpers/Interop/NativeIoDeviceControlHelper.cs create mode 100644 src/Files.App/Helpers/Win32/Win32PInvoke.Consts.cs create mode 100644 src/Files.App/Helpers/Win32/Win32PInvoke.Enums.cs create mode 100644 src/Files.App/Helpers/Win32/Win32PInvoke.Methods.cs create mode 100644 src/Files.App/Helpers/Win32/Win32PInvoke.Structs.cs diff --git a/src/Files.App/Data/Contracts/IDataTransferManagerInterop.cs b/src/Files.App/Data/Contracts/IDataTransferManagerInterop.cs new file mode 100644 index 000000000000..b9bd34b0a2ff --- /dev/null +++ b/src/Files.App/Data/Contracts/IDataTransferManagerInterop.cs @@ -0,0 +1,17 @@ +// Copyright (c) 2024 Files Community +// Licensed under the MIT License. See the LICENSE. + +using System.Runtime.InteropServices; + +namespace Files.App.Data.Contracts +{ + [ComImport] + [Guid("3A3DCD6C-3EAB-43DC-BCDE-45671CE800C8")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + interface IDataTransferManagerInterop + { + IntPtr GetForWindow([In] IntPtr appWindow, [In] ref Guid riid); + + void ShowShareUIForWindow(IntPtr appWindow); + } +} diff --git a/src/Files.App/Data/Models/AppModel.cs b/src/Files.App/Data/Models/AppModel.cs index 2077b8d5f03a..e3f045bb44f4 100644 --- a/src/Files.App/Data/Models/AppModel.cs +++ b/src/Files.App/Data/Models/AppModel.cs @@ -121,7 +121,7 @@ public string PCloudDrivePath /// Gets or sets a value indicating the AppWindow DPI. /// TODO update value if the DPI changes /// - private float _AppWindowDPI = InteropHelpers.GetDpiForWindow(MainWindow.Instance.WindowHandle) / 96f; + private float _AppWindowDPI = Win32PInvoke.GetDpiForWindow(MainWindow.Instance.WindowHandle) / 96f; public float AppWindowDPI { get => _AppWindowDPI; diff --git a/src/Files.App/Data/Models/ItemViewModel.cs b/src/Files.App/Data/Models/ItemViewModel.cs index 7b1819c93ff9..a89c45f5ef52 100644 --- a/src/Files.App/Data/Models/ItemViewModel.cs +++ b/src/Files.App/Data/Models/ItemViewModel.cs @@ -18,7 +18,7 @@ using Windows.Storage; using Windows.Storage.FileProperties; using Windows.Storage.Search; -using static Files.App.Helpers.NativeDirectoryChangesHelper; +using static Files.App.Helpers.Win32PInvoke; using static Files.Core.Helpers.NativeFindStorageItemHelper; using DispatcherQueue = Microsoft.UI.Dispatching.DispatcherQueue; using FileAttributes = System.IO.FileAttributes; diff --git a/src/Files.App/Data/Models/RemovableDevice.cs b/src/Files.App/Data/Models/RemovableDevice.cs index 0ce23aa403c5..26666b047661 100644 --- a/src/Files.App/Data/Models/RemovableDevice.cs +++ b/src/Files.App/Data/Models/RemovableDevice.cs @@ -4,7 +4,7 @@ using System; using System.Diagnostics; using System.Threading.Tasks; -using static Files.App.Helpers.NativeIoDeviceControlHelper; +using static Files.App.Helpers.Win32PInvoke; namespace Files.App.Data.Models { diff --git a/src/Files.App/Helpers/Interop/FileUtils.cs b/src/Files.App/Helpers/Interop/FileUtils.cs deleted file mode 100644 index 4a2d1e7e49e1..000000000000 --- a/src/Files.App/Helpers/Interop/FileUtils.cs +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Runtime.InteropServices; - -namespace Files.App.Helpers -{ - // https://stackoverflow.com/questions/317071/how-do-i-find-out-which-process-is-locking-a-file-using-net/317209#317209 - public static class FileUtils - { - [StructLayout(LayoutKind.Sequential)] - struct RM_UNIQUE_PROCESS - { - public int dwProcessId; - public System.Runtime.InteropServices.ComTypes.FILETIME ProcessStartTime; - } - - const int RmRebootReasonNone = 0; - const int CCH_RM_MAX_APP_NAME = 255; - const int CCH_RM_MAX_SVC_NAME = 63; - - enum RM_APP_TYPE - { - RmUnknownApp = 0, - RmMainWindow = 1, - RmOtherWindow = 2, - RmService = 3, - RmExplorer = 4, - RmConsole = 5, - RmCritical = 1000 - } - - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] - struct RM_PROCESS_INFO - { - public RM_UNIQUE_PROCESS Process; - - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_APP_NAME + 1)] - public string strAppName; - - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_SVC_NAME + 1)] - public string strServiceShortName; - - public RM_APP_TYPE ApplicationType; - public uint AppStatus; - public uint TSSessionId; - [MarshalAs(UnmanagedType.Bool)] - public bool bRestartable; - } - - [DllImport("rstrtmgr.dll", CharSet = CharSet.Unicode)] - static extern int RmRegisterResources(uint pSessionHandle, - UInt32 nFiles, - string[] rgsFilenames, - UInt32 nApplications, - [In] RM_UNIQUE_PROCESS[] rgApplications, - UInt32 nServices, - string[] rgsServiceNames); - - [DllImport("rstrtmgr.dll", CharSet = CharSet.Unicode)] - static extern int RmStartSession(out uint pSessionHandle, int dwSessionFlags, string strSessionKey); - - [DllImport("rstrtmgr.dll")] - static extern int RmEndSession(uint pSessionHandle); - - [DllImport("rstrtmgr.dll")] - static extern int RmGetList(uint dwSessionHandle, - out uint pnProcInfoNeeded, - ref uint pnProcInfo, - [In, Out] RM_PROCESS_INFO[] rgAffectedApps, - ref uint lpdwRebootReasons); - - /// - /// Find out what process(es) have a lock on the specified file. - /// - /// Path of the file. - /// Processes locking the file - /// See also: - /// http://msdn.microsoft.com/library/windows/desktop/aa373661(v=vs.85).aspx - /// http://wyupdate.googlecode.com/svn-history/r401/trunk/frmFilesInUse.cs (no copyright in code at time of viewing) - /// - /// - public static List WhoIsLocking(string[] resources) - { - string key = Guid.NewGuid().ToString(); - List processes = new List(); - - int res = RmStartSession(out uint handle, 0, key); - if (res != 0) throw new Exception("Could not begin restart session. Unable to determine file locker."); - - try - { - const int ERROR_MORE_DATA = 234; - uint pnProcInfo = 0; - uint lpdwRebootReasons = RmRebootReasonNone; - - res = RmRegisterResources(handle, (uint)resources.Length, resources, 0, null, 0, null); - - if (res != 0) throw new Exception("Could not register resource."); - - //Note: there's a race condition here -- the first call to RmGetList() returns - // the total number of process. However, when we call RmGetList() again to get - // the actual processes this number may have increased. - res = RmGetList(handle, out uint pnProcInfoNeeded, ref pnProcInfo, null, ref lpdwRebootReasons); - - if (res == ERROR_MORE_DATA) - { - // Create an array to store the process results - RM_PROCESS_INFO[] processInfo = new RM_PROCESS_INFO[pnProcInfoNeeded]; - pnProcInfo = pnProcInfoNeeded; - - // Get the list - res = RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, processInfo, ref lpdwRebootReasons); - if (res == 0) - { - processes = new List((int)pnProcInfo); - - // Enumerate all of the results and add them to the - // list to be returned - for (int i = 0; i < pnProcInfo; i++) - { - try - { - processes.Add(Process.GetProcessById(processInfo[i].Process.dwProcessId)); - } - // catch the error -- in case the process is no longer running - catch (ArgumentException) { } - } - } - else throw new Exception("Could not list processes locking resource."); - } - else if (res != 0) throw new Exception("Could not list processes locking resource. Failed to get size of result."); - } - finally - { - _ = RmEndSession(handle); - } - - return processes; - } - } -} diff --git a/src/Files.App/Helpers/Interop/InteropHelpers.cs b/src/Files.App/Helpers/Interop/InteropHelpers.cs deleted file mode 100644 index 54fad3ec1ac5..000000000000 --- a/src/Files.App/Helpers/Interop/InteropHelpers.cs +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Microsoft.UI.Input; -using Microsoft.UI.Xaml; -using System.Reflection; -using System.Runtime.InteropServices; -using System.Text; -using Vanara.PInvoke; -using static Vanara.PInvoke.User32; - -namespace Files.App.Helpers -{ - public static class InteropHelpers - { - public static readonly Guid DataTransferManagerInteropIID = - new(0xa5caee9b, 0x8708, 0x49d1, 0x8d, 0x36, 0x67, 0xd2, 0x5a, 0x8d, 0xa0, 0x0c); - - [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)] - public static extern bool SetPropW(IntPtr hWnd, string lpString, IntPtr hData); - - [DllImport("user32.dll")] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool GetCursorPos(out POINT point); - - [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] - public static extern IntPtr CreateEvent(IntPtr lpEventAttributes, bool bManualReset, bool bInitialState, string lpName); - - [DllImport("kernel32.dll")] - public static extern bool SetEvent(IntPtr hEvent); - - [DllImport("User32.dll", SetLastError = true, CharSet = CharSet.Unicode)] - public static extern int GetDpiForWindow(IntPtr hwnd); - - [DllImport("ole32.dll")] - public static extern uint CoWaitForMultipleObjects(uint dwFlags, uint dwMilliseconds, ulong nHandles, IntPtr[] pHandles, out uint dwIndex); - - [StructLayout(LayoutKind.Sequential)] - public struct POINT - { - public int X; - - public int Y; - - public POINT(int x, int y) => (X, Y) = (x, y); - } - - public static void ChangeCursor(this UIElement uiElement, InputCursor cursor) - { - Type type = typeof(UIElement); - - type.InvokeMember( - "ProtectedCursor", - BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.SetProperty | BindingFlags.Instance, - null, - uiElement, - new object[] { cursor } - ); - } - - [DllImport(Lib.User32, SetLastError = true, EntryPoint = "SetWindowLong")] - private static extern int SetWindowLongPtr32(HWND hWnd, WindowLongFlags nIndex, IntPtr dwNewLong); - - [DllImport(Lib.User32, SetLastError = true, EntryPoint = "SetWindowLongPtr")] - private static extern IntPtr SetWindowLongPtr64(HWND hWnd, WindowLongFlags nIndex, IntPtr dwNewLong); - - public static IntPtr SetWindowLong(HWND hWnd, WindowLongFlags nIndex, IntPtr dwNewLong) - { - if (IntPtr.Size == 4) - return SetWindowLongPtr32(hWnd, nIndex, dwNewLong); - else - return SetWindowLongPtr64(hWnd, nIndex, dwNewLong); - } - - [DllImport("User32.dll")] - public extern static short GetKeyState(int n); - - [DllImport("shell32.dll")] - public static extern IntPtr SHBrowseForFolder(ref BROWSEINFO lpbi); - - [DllImport("shell32.dll", CharSet = CharSet.Unicode)] - public static extern bool SHGetPathFromIDList(IntPtr pidl, [MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszPath); - - [StructLayout(LayoutKind.Sequential)] - public struct BROWSEINFO - { - public IntPtr hwndOwner; - public IntPtr pidlRoot; - public string pszDisplayName; - public string lpszTitle; - public uint ulFlags; - public IntPtr lpfn; - public int lParam; - public IntPtr iImage; - } - } - - [ComImport] - [Guid("3A3DCD6C-3EAB-43DC-BCDE-45671CE800C8")] - [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - interface IDataTransferManagerInterop - { - IntPtr GetForWindow([In] IntPtr appWindow, [In] ref Guid riid); - - void ShowShareUIForWindow(IntPtr appWindow); - } -} diff --git a/src/Files.App/Helpers/Interop/NativeDirectoryChangesHelper.cs b/src/Files.App/Helpers/Interop/NativeDirectoryChangesHelper.cs deleted file mode 100644 index be87244ed52c..000000000000 --- a/src/Files.App/Helpers/Interop/NativeDirectoryChangesHelper.cs +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using System; -using System.Runtime.InteropServices; - -namespace Files.App.Helpers -{ - public sealed class NativeDirectoryChangesHelper - { - [DllImport("api-ms-win-core-handle-l1-1-0.dll")] - public static extern bool CloseHandle(IntPtr hObject); - - [DllImport("api-ms-win-core-io-l1-1-1.dll")] - public static extern bool GetOverlappedResult(IntPtr hFile, OVERLAPPED lpOverlapped, out int lpNumberOfBytesTransferred, bool bWait); - - [DllImport("api-ms-win-core-io-l1-1-1.dll")] - public static extern bool CancelIo(IntPtr hFile); - - [DllImport("api-ms-win-core-io-l1-1-1.dll")] - public static extern bool CancelIoEx(IntPtr hFile, IntPtr lpOverlapped); - - [DllImport("api-ms-win-core-synch-l1-2-0.dll")] - public static extern uint WaitForMultipleObjectsEx(uint nCount, IntPtr[] lpHandles, bool bWaitAll, uint dwMilliseconds, bool bAlertable); - - [DllImport("api-ms-win-core-synch-l1-2-0.dll", SetLastError = true)] - public static extern IntPtr CreateEvent(IntPtr lpEventAttributes, bool bManualReset, bool bInitialState, string lpName); - - [DllImport("api-ms-win-core-synch-l1-2-0.dll", SetLastError = true)] - public static extern bool ResetEvent(IntPtr hEvent); - - [DllImport("api-ms-win-core-synch-l1-2-0.dll", SetLastError = true)] - public static extern UInt32 WaitForSingleObjectEx(IntPtr hHandle, UInt32 dwMilliseconds, bool bAlertable); - - public delegate void LpoverlappedCompletionRoutine(uint dwErrorCode, - uint dwNumberOfBytesTransfered, - OVERLAPPED lpOverlapped - ); - - public unsafe struct OVERLAPPED - { - public IntPtr Internal; - public IntPtr InternalHigh; - public Union PointerAndOffset; - public IntPtr hEvent; - - [StructLayout(LayoutKind.Explicit)] - public struct Union - { - [FieldOffset(0)] public void* IntPtr; - [FieldOffset(0)] public OffsetPair Offset; - - public struct OffsetPair { public uint Offset; public uint OffsetHigh; } - } - } - - public const int FILE_NOTIFY_CHANGE_FILE_NAME = 1; - public const int FILE_NOTIFY_CHANGE_DIR_NAME = 2; - public const int FILE_NOTIFY_CHANGE_ATTRIBUTES = 4; - public const int FILE_NOTIFY_CHANGE_SIZE = 8; - public const int FILE_NOTIFY_CHANGE_LAST_WRITE = 16; - public const int FILE_NOTIFY_CHANGE_LAST_ACCESS = 32; - public const int FILE_NOTIFY_CHANGE_CREATION = 64; - public const int FILE_NOTIFY_CHANGE_SECURITY = 256; - - public unsafe struct FILE_NOTIFY_INFORMATION - { - public uint NextEntryOffset; - public uint Action; - public uint FileNameLength; - public fixed char FileName[1]; - } - - [DllImport("api-ms-win-core-file-l2-1-0.dll", SetLastError = true, CharSet = CharSet.Unicode)] - public unsafe static extern bool ReadDirectoryChangesW(IntPtr hDirectory, byte* lpBuffer, - int nBufferLength, bool bWatchSubtree, int dwNotifyFilter, int* - lpBytesReturned, ref OVERLAPPED lpOverlapped, - LpoverlappedCompletionRoutine lpCompletionRoutine); - } -} \ No newline at end of file diff --git a/src/Files.App/Helpers/Interop/NativeIoDeviceControlHelper.cs b/src/Files.App/Helpers/Interop/NativeIoDeviceControlHelper.cs deleted file mode 100644 index dd34b692ccbc..000000000000 --- a/src/Files.App/Helpers/Interop/NativeIoDeviceControlHelper.cs +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using System; -using System.Runtime.InteropServices; - -namespace Files.App.Helpers -{ - public sealed class NativeIoDeviceControlHelper - { - [DllImport("api-ms-win-core-file-fromapp-l1-1-0.dll", SetLastError = true, CharSet = CharSet.Auto)] - public static extern IntPtr CreateFileFromAppW( - string lpFileName, - uint dwDesiredAccess, - uint dwShareMode, - IntPtr SecurityAttributes, - uint dwCreationDisposition, - uint dwFlagsAndAttributes, - IntPtr hTemplateFile - ); - - [DllImport("api-ms-win-core-io-l1-1-0.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)] - public static extern bool DeviceIoControl( - IntPtr hDevice, - uint dwIoControlCode, - IntPtr lpInBuffer, - uint nInBufferSize, - IntPtr lpOutBuffer, - uint nOutBufferSize, - out uint lpBytesReturned, - IntPtr lpOverlapped - ); - - [DllImport("api-ms-win-core-io-l1-1-0.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)] - public static extern bool DeviceIoControl( - IntPtr hDevice, - uint dwIoControlCode, - byte[] lpInBuffer, - uint nInBufferSize, - IntPtr lpOutBuffer, - uint nOutBufferSize, - out uint lpBytesReturned, - IntPtr lpOverlapped - ); - - [DllImport("api-ms-win-core-handle-l1-1-0.dll", SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool CloseHandle(IntPtr hObject); - - public const int INVALID_HANDLE_VALUE = -1; - public const uint GENERIC_READ = 0x80000000; - public const uint GENERIC_WRITE = 0x40000000; - public const int FILE_SHARE_READ = 0x00000001; - public const int FILE_SHARE_WRITE = 0x00000002; - public const int OPEN_EXISTING = 3; - public const int FSCTL_LOCK_VOLUME = 0x00090018; - public const int FSCTL_DISMOUNT_VOLUME = 0x00090020; - public const int IOCTL_STORAGE_EJECT_MEDIA = 0x2D4808; - public const int IOCTL_STORAGE_MEDIA_REMOVAL = 0x002D4804; - } -} \ No newline at end of file diff --git a/src/Files.App/Helpers/ShareItemHelpers.cs b/src/Files.App/Helpers/ShareItemHelpers.cs index 8822c0fe7bec..b69a739985d2 100644 --- a/src/Files.App/Helpers/ShareItemHelpers.cs +++ b/src/Files.App/Helpers/ShareItemHelpers.cs @@ -27,7 +27,7 @@ public static async Task ShareItemsAsync(IEnumerable itemsToShare) return; var interop = DataTransferManager.As(); - IntPtr result = interop.GetForWindow(MainWindow.Instance.WindowHandle, InteropHelpers.DataTransferManagerInteropIID); + IntPtr result = interop.GetForWindow(MainWindow.Instance.WindowHandle, Win32PInvoke.DataTransferManagerInteropIID); var manager = WinRT.MarshalInterface.FromAbi(result); manager.DataRequested += new TypedEventHandler(Manager_DataRequested); diff --git a/src/Files.App/Helpers/Win32/Win32Helper.Process.cs b/src/Files.App/Helpers/Win32/Win32Helper.Process.cs index d0a79258cc30..6165db4610c6 100644 --- a/src/Files.App/Helpers/Win32/Win32Helper.Process.cs +++ b/src/Files.App/Helpers/Win32/Win32Helper.Process.cs @@ -37,5 +37,76 @@ public static async Task InvokeWin32ComponentsAsync(IEnumerable ap return await LaunchHelper.LaunchAppAsync(application, arguments, workingDirectory); } } + + /// + /// Gets process(es) that have a lock on the specified file. + /// + /// Path of the file. + /// Processes locking the file. + /// + /// For more info, visit + ///
+ /// - + ///
+ /// -
+ ///
+ public static List WhoIsLocking(string[] resources) + { + string key = Guid.NewGuid().ToString(); + List processes = []; + + int res = Win32PInvoke.RmStartSession(out uint handle, 0, key); + if (res != 0) throw new Exception("Could not begin restart session. Unable to determine file locker."); + + try + { + const int ERROR_MORE_DATA = 234; + uint pnProcInfo = 0; + uint lpdwRebootReasons = Win32PInvoke.RmRebootReasonNone; + + res = Win32PInvoke.RmRegisterResources(handle, (uint)resources.Length, resources, 0, null, 0, null); + + if (res != 0) throw new Exception("Could not register resource."); + + // Note: + // There's a race condition here -- the first call to RmGetList() returns the total number of process. + // However, when we call RmGetList() again to get the actual processes this number may have increased. + res = Win32PInvoke.RmGetList(handle, out uint pnProcInfoNeeded, ref pnProcInfo, null, ref lpdwRebootReasons); + + if (res == ERROR_MORE_DATA) + { + // Create an array to store the process results + var processInfo = new Win32PInvoke.RM_PROCESS_INFO[pnProcInfoNeeded]; + pnProcInfo = pnProcInfoNeeded; + + // Get the list + res = Win32PInvoke.RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, processInfo, ref lpdwRebootReasons); + if (res == 0) + { + processes = new List((int)pnProcInfo); + + // Enumerate all of the results and add them to the + // list to be returned + for (int i = 0; i < pnProcInfo; i++) + { + try + { + processes.Add(Process.GetProcessById(processInfo[i].Process.dwProcessId)); + } + // catch the error -- in case the process is no longer running + catch (ArgumentException) { } + } + } + else throw new Exception("Could not list processes locking resource."); + } + else if (res != 0) throw new Exception("Could not list processes locking resource. Failed to get size of result."); + } + finally + { + _ = Win32PInvoke.RmEndSession(handle); + } + + return processes; + } } } diff --git a/src/Files.App/Helpers/Win32/Win32Helper.WindowManagement.cs b/src/Files.App/Helpers/Win32/Win32Helper.WindowManagement.cs index 5264c3c1e021..1b28a1f8717e 100644 --- a/src/Files.App/Helpers/Win32/Win32Helper.WindowManagement.cs +++ b/src/Files.App/Helpers/Win32/Win32Helper.WindowManagement.cs @@ -1,8 +1,13 @@ -// Copyright (c) 2023 Files Community +// Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. +using Microsoft.UI.Input; +using Microsoft.UI.Xaml; +using System.Reflection; +using Vanara.PInvoke; using Windows.Win32; using Windows.Win32.UI.WindowsAndMessaging; +using static Vanara.PInvoke.User32; namespace Files.App.Helpers { @@ -37,5 +42,38 @@ public static unsafe void BringToForegroundEx(Windows.Win32.Foundation.HWND hWnd PInvoke.SetActiveWindow(hWnd); PInvoke.AttachThreadInput(dwCurID, dwMyID, false); } + + /// + /// Sets cursor when hovering on a specific element. + /// + /// An element to be changed. + /// Cursor to change. + public static void ChangeCursor(this UIElement uiElement, InputCursor cursor) + { + Type type = typeof(UIElement); + + type.InvokeMember( + "ProtectedCursor", + BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.SetProperty | BindingFlags.Instance, + null, + uiElement, + [cursor] + ); + } + + /// + /// Changes an attribute of the specified window. + /// + /// A handle to the window and, indirectly, the class to which the window belongs. + /// The zero-based offset to the value to be set. + /// The replacement value. + /// If the function succeeds, the return value is the previous value of the specified offset. + public static IntPtr SetWindowLong(HWND hWnd, WindowLongFlags nIndex, IntPtr dwNewLong) + { + return + IntPtr.Size == 4 + ? Win32PInvoke.SetWindowLongPtr32(hWnd, nIndex, dwNewLong) + : Win32PInvoke.SetWindowLongPtr64(hWnd, nIndex, dwNewLong); + } } } diff --git a/src/Files.App/Helpers/Win32/Win32PInvoke.Consts.cs b/src/Files.App/Helpers/Win32/Win32PInvoke.Consts.cs new file mode 100644 index 000000000000..0bdf6dfcf2e5 --- /dev/null +++ b/src/Files.App/Helpers/Win32/Win32PInvoke.Consts.cs @@ -0,0 +1,35 @@ +// Copyright (c) 2024 Files Community +// Licensed under the MIT License. See the LICENSE. + +namespace Files.App.Helpers +{ + public static partial class Win32PInvoke + { + public static readonly Guid DataTransferManagerInteropIID = + new(0xa5caee9b, 0x8708, 0x49d1, 0x8d, 0x36, 0x67, 0xd2, 0x5a, 0x8d, 0xa0, 0x0c); + + public const int RmRebootReasonNone = 0; + public const int CCH_RM_MAX_APP_NAME = 255; + public const int CCH_RM_MAX_SVC_NAME = 63; + + public const int FILE_NOTIFY_CHANGE_FILE_NAME = 1; + public const int FILE_NOTIFY_CHANGE_DIR_NAME = 2; + public const int FILE_NOTIFY_CHANGE_ATTRIBUTES = 4; + public const int FILE_NOTIFY_CHANGE_SIZE = 8; + public const int FILE_NOTIFY_CHANGE_LAST_WRITE = 16; + public const int FILE_NOTIFY_CHANGE_LAST_ACCESS = 32; + public const int FILE_NOTIFY_CHANGE_CREATION = 64; + public const int FILE_NOTIFY_CHANGE_SECURITY = 256; + + public const int INVALID_HANDLE_VALUE = -1; + public const uint GENERIC_READ = 0x80000000; + public const uint GENERIC_WRITE = 0x40000000; + public const int FILE_SHARE_READ = 0x00000001; + public const int FILE_SHARE_WRITE = 0x00000002; + public const int OPEN_EXISTING = 3; + public const int FSCTL_LOCK_VOLUME = 0x00090018; + public const int FSCTL_DISMOUNT_VOLUME = 0x00090020; + public const int IOCTL_STORAGE_EJECT_MEDIA = 0x2D4808; + public const int IOCTL_STORAGE_MEDIA_REMOVAL = 0x002D4804; + } +} diff --git a/src/Files.App/Helpers/Win32/Win32PInvoke.Enums.cs b/src/Files.App/Helpers/Win32/Win32PInvoke.Enums.cs new file mode 100644 index 000000000000..f12643361ab7 --- /dev/null +++ b/src/Files.App/Helpers/Win32/Win32PInvoke.Enums.cs @@ -0,0 +1,19 @@ +// Copyright (c) 2024 Files Community +// Licensed under the MIT License. See the LICENSE. + +namespace Files.App.Helpers +{ + public static partial class Win32PInvoke + { + public enum RM_APP_TYPE + { + RmUnknownApp = 0, + RmMainWindow = 1, + RmOtherWindow = 2, + RmService = 3, + RmExplorer = 4, + RmConsole = 5, + RmCritical = 1000 + } + } +} diff --git a/src/Files.App/Helpers/Win32/Win32PInvoke.Methods.cs b/src/Files.App/Helpers/Win32/Win32PInvoke.Methods.cs new file mode 100644 index 000000000000..096481537042 --- /dev/null +++ b/src/Files.App/Helpers/Win32/Win32PInvoke.Methods.cs @@ -0,0 +1,214 @@ +// Copyright (c) 2024 Files Community +// Licensed under the MIT License. See the LICENSE. + +using System.Runtime.InteropServices; +using System.Text; +using Vanara.PInvoke; +using static Vanara.PInvoke.User32; + +namespace Files.App.Helpers +{ + public static partial class Win32PInvoke + { + public delegate void LpoverlappedCompletionRoutine( + uint dwErrorCode, + uint dwNumberOfBytesTransfered, + OVERLAPPED lpOverlapped + ); + + [DllImport("rstrtmgr.dll", CharSet = CharSet.Unicode)] + public static extern int RmRegisterResources( + uint pSessionHandle, + uint nFiles, + string[] rgsFilenames, + uint nApplications, + [In] RM_UNIQUE_PROCESS[] rgApplications, + uint nServices, + string[] rgsServiceNames + ); + + [DllImport("rstrtmgr.dll", CharSet = CharSet.Unicode)] + public static extern int RmStartSession( + out uint pSessionHandle, + int dwSessionFlags, + string strSessionKey + ); + + [DllImport("rstrtmgr.dll")] + public static extern int RmEndSession( + uint pSessionHandle + ); + + [DllImport("rstrtmgr.dll")] + public static extern int RmGetList( + uint dwSessionHandle, + out uint pnProcInfoNeeded, + ref uint pnProcInfo, + [In, Out] RM_PROCESS_INFO[] rgAffectedApps, + ref uint lpdwRebootReasons + ); + + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + public static extern bool SetPropW( + IntPtr hWnd, + string lpString, + IntPtr hData + ); + + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool GetCursorPos( + out POINT point + ); + + [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] + public static extern IntPtr CreateEvent( + IntPtr lpEventAttributes, + bool bManualReset, + bool bInitialState, + string lpName + ); + + [DllImport("kernel32.dll")] + public static extern bool SetEvent( + IntPtr hEvent + ); + + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + public static extern int GetDpiForWindow( + IntPtr hwnd + ); + + [DllImport("ole32.dll")] + public static extern uint CoWaitForMultipleObjects( + uint dwFlags, + uint dwMilliseconds, + ulong nHandles, + IntPtr[] pHandles, + out uint dwIndex + ); + + [DllImport("user32.dll", SetLastError = true, EntryPoint = "SetWindowLong")] + public static extern int SetWindowLongPtr32( + HWND hWnd, + WindowLongFlags nIndex, + IntPtr dwNewLong + ); + + [DllImport("user32.dll", SetLastError = true, EntryPoint = "SetWindowLongPtr")] + public static extern IntPtr SetWindowLongPtr64( + HWND hWnd, + WindowLongFlags nIndex, + IntPtr dwNewLong + ); + + [DllImport("User32.dll")] + public extern static short GetKeyState( + int n + ); + + [DllImport("shell32.dll")] + public static extern IntPtr SHBrowseForFolder( + ref BROWSEINFO lpbi + ); + + [DllImport("shell32.dll", CharSet = CharSet.Unicode)] + public static extern bool SHGetPathFromIDList( + IntPtr pidl, + [MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszPath + ); + + [DllImport("api-ms-win-core-handle-l1-1-0.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool CloseHandle( + IntPtr hObject + ); + + [DllImport("api-ms-win-core-io-l1-1-1.dll")] + public static extern bool GetOverlappedResult( + IntPtr hFile, + OVERLAPPED lpOverlapped, + out int lpNumberOfBytesTransferred, + bool bWait + ); + + [DllImport("api-ms-win-core-io-l1-1-1.dll")] + public static extern bool CancelIo( + IntPtr hFile + ); + + [DllImport("api-ms-win-core-io-l1-1-1.dll")] + public static extern bool CancelIoEx( + IntPtr hFile, + IntPtr lpOverlapped + ); + + [DllImport("api-ms-win-core-synch-l1-2-0.dll")] + public static extern uint WaitForMultipleObjectsEx( + uint nCount, + IntPtr[] lpHandles, + bool bWaitAll, + uint dwMilliseconds, + bool bAlertable + ); + + [DllImport("api-ms-win-core-synch-l1-2-0.dll", SetLastError = true)] + public static extern bool ResetEvent( + IntPtr hEvent + ); + + [DllImport("api-ms-win-core-synch-l1-2-0.dll", SetLastError = true)] + public static extern uint WaitForSingleObjectEx( + IntPtr hHandle, + uint dwMilliseconds, + bool bAlertable + ); + + [DllImport("api-ms-win-core-file-l2-1-0.dll", SetLastError = true, CharSet = CharSet.Unicode)] + public unsafe static extern bool ReadDirectoryChangesW( + IntPtr hDirectory, + byte* lpBuffer, + int nBufferLength, + bool bWatchSubtree, + int dwNotifyFilter, + int* lpBytesReturned, + ref OVERLAPPED lpOverlapped, + LpoverlappedCompletionRoutine lpCompletionRoutine + ); + + [DllImport("api-ms-win-core-file-fromapp-l1-1-0.dll", SetLastError = true, CharSet = CharSet.Auto)] + public static extern IntPtr CreateFileFromAppW( + string lpFileName, + uint dwDesiredAccess, + uint dwShareMode, + IntPtr SecurityAttributes, + uint dwCreationDisposition, + uint dwFlagsAndAttributes, + IntPtr hTemplateFile + ); + + [DllImport("api-ms-win-core-io-l1-1-0.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)] + public static extern bool DeviceIoControl( + IntPtr hDevice, + uint dwIoControlCode, + IntPtr lpInBuffer, + uint nInBufferSize, + IntPtr lpOutBuffer, + uint nOutBufferSize, + out uint lpBytesReturned, + IntPtr lpOverlapped + ); + + [DllImport("api-ms-win-core-io-l1-1-0.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)] + public static extern bool DeviceIoControl( + IntPtr hDevice, + uint dwIoControlCode, + byte[] lpInBuffer, + uint nInBufferSize, + IntPtr lpOutBuffer, + uint nOutBufferSize, + out uint lpBytesReturned, + IntPtr lpOverlapped + ); + } +} diff --git a/src/Files.App/Helpers/Win32/Win32PInvoke.Structs.cs b/src/Files.App/Helpers/Win32/Win32PInvoke.Structs.cs new file mode 100644 index 000000000000..002112c0d814 --- /dev/null +++ b/src/Files.App/Helpers/Win32/Win32PInvoke.Structs.cs @@ -0,0 +1,82 @@ +// Copyright (c) 2024 Files Community +// Licensed under the MIT License. See the LICENSE. + +using System.Runtime.InteropServices; + +namespace Files.App.Helpers +{ + public static partial class Win32PInvoke + { + [StructLayout(LayoutKind.Sequential)] + public struct RM_UNIQUE_PROCESS + { + public int dwProcessId; + public System.Runtime.InteropServices.ComTypes.FILETIME ProcessStartTime; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + public struct RM_PROCESS_INFO + { + public RM_UNIQUE_PROCESS Process; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_APP_NAME + 1)] + public string strAppName; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_SVC_NAME + 1)] + public string strServiceShortName; + + public RM_APP_TYPE ApplicationType; + public uint AppStatus; + public uint TSSessionId; + [MarshalAs(UnmanagedType.Bool)] + public bool bRestartable; + } + + [StructLayout(LayoutKind.Sequential)] + public struct POINT + { + public int X; + public int Y; + + public POINT(int x, int y) => (X, Y) = (x, y); + } + + [StructLayout(LayoutKind.Sequential)] + public struct BROWSEINFO + { + public IntPtr hwndOwner; + public IntPtr pidlRoot; + public string pszDisplayName; + public string lpszTitle; + public uint ulFlags; + public IntPtr lpfn; + public int lParam; + public IntPtr iImage; + } + + public unsafe struct OVERLAPPED + { + public IntPtr Internal; + public IntPtr InternalHigh; + public Union PointerAndOffset; + public IntPtr hEvent; + + [StructLayout(LayoutKind.Explicit)] + public struct Union + { + [FieldOffset(0)] public void* IntPtr; + [FieldOffset(0)] public OffsetPair Offset; + + public struct OffsetPair { public uint Offset; public uint OffsetHigh; } + } + } + + public unsafe struct FILE_NOTIFY_INFORMATION + { + public uint NextEntryOffset; + public uint Action; + public uint FileNameLength; + public fixed char FileName[1]; + } + } +} diff --git a/src/Files.App/MainWindow.xaml.cs b/src/Files.App/MainWindow.xaml.cs index 052543d799a2..790e352bd4a5 100644 --- a/src/Files.App/MainWindow.xaml.cs +++ b/src/Files.App/MainWindow.xaml.cs @@ -54,7 +54,7 @@ private void EnsureEarlyWindow() // or "Automatically hide the taskbar in desktop mode" in Windows 10 is enabled. // Setting this property when the setting is disabled will result in the taskbar overlapping the application if (AppLifecycleHelper.IsAutoHideTaskbarEnabled()) - InteropHelpers.SetPropW(WindowHandle, "NonRudeHWND", new IntPtr(1)); + Win32PInvoke.SetPropW(WindowHandle, "NonRudeHWND", new IntPtr(1)); } public void ShowSplashScreen() diff --git a/src/Files.App/Program.cs b/src/Files.App/Program.cs index 715927f0b953..3e71ff0e7017 100644 --- a/src/Files.App/Program.cs +++ b/src/Files.App/Program.cs @@ -8,7 +8,7 @@ using System.IO; using Windows.ApplicationModel.Activation; using Windows.Storage; -using static Files.App.Helpers.InteropHelpers; +using static Files.App.Helpers.Win32PInvoke; namespace Files.App { diff --git a/src/Files.App/UserControls/TabBar/TabBar.xaml.cs b/src/Files.App/UserControls/TabBar/TabBar.xaml.cs index 65d123a60ede..fa7babdfaea5 100644 --- a/src/Files.App/UserControls/TabBar/TabBar.xaml.cs +++ b/src/Files.App/UserControls/TabBar/TabBar.xaml.cs @@ -58,7 +58,7 @@ public Rectangle DragArea => DragAreaRectangle; /// Starting position when dragging a tab. - private InteropHelpers.POINT dragStartPoint; + private Win32PInvoke.POINT dragStartPoint; /// Starting time when dragging a tab. private DateTimeOffset dragStartTime; @@ -150,7 +150,7 @@ private void TabView_TabDragStarting(TabView sender, TabViewTabDragStartingEvent args.Data.RequestedOperation = DataPackageOperation.Move; // Get cursor position & time to track how far the tab was dragged. - InteropHelpers.GetCursorPos(out dragStartPoint); + Win32PInvoke.GetCursorPos(out dragStartPoint); dragStartTime = DateTimeOffset.UtcNow; // Focus the UI Element, without this the focus sometimes changes @@ -241,7 +241,7 @@ private async void TabView_TabDroppedOutside(TabView sender, TabViewTabDroppedOu if (isCancelingDragOperation) return; - InteropHelpers.GetCursorPos(out var droppedPoint); + Win32PInvoke.GetCursorPos(out var droppedPoint); var droppedTime = DateTimeOffset.UtcNow; var dragTime = droppedTime - dragStartTime; var dragDistance = Math.Sqrt(Math.Pow((dragStartPoint.X - droppedPoint.X), 2) + Math.Pow((dragStartPoint.Y - droppedPoint.Y), 2)); diff --git a/src/Files.App/Utils/Storage/Helpers/FilePropertiesHelpers.cs b/src/Files.App/Utils/Storage/Helpers/FilePropertiesHelpers.cs index 3dc6280dae0e..bc88e1a63956 100644 --- a/src/Files.App/Utils/Storage/Helpers/FilePropertiesHelpers.cs +++ b/src/Files.App/Utils/Storage/Helpers/FilePropertiesHelpers.cs @@ -134,7 +134,7 @@ public static void OpenPropertiesWindow(object item, IShellPage associatedInstan new SuppressNavigationTransitionInfo()); // WINUI3: Move window to cursor position - InteropHelpers.GetCursorPos(out var pointerPosition); + Win32PInvoke.GetCursorPos(out var pointerPosition); var displayArea = DisplayArea.GetFromPoint(new PointInt32(pointerPosition.X, pointerPosition.Y), DisplayAreaFallback.Nearest); var appWindowPos = new PointInt32 { diff --git a/src/Files.App/Utils/Storage/Operations/FileOperationsHelpers.cs b/src/Files.App/Utils/Storage/Operations/FileOperationsHelpers.cs index 79f61cd2cad5..c8011f7f9008 100644 --- a/src/Files.App/Utils/Storage/Operations/FileOperationsHelpers.cs +++ b/src/Files.App/Utils/Storage/Operations/FileOperationsHelpers.cs @@ -661,7 +661,7 @@ public static void TryCancelOperation(string operationId) public static IEnumerable? CheckFileInUse(string[] fileToCheckPath) { - var processes = SafetyExtensions.IgnoreExceptions(() => FileUtils.WhoIsLocking(fileToCheckPath), App.Logger); + var processes = SafetyExtensions.IgnoreExceptions(() => Win32Helper.WhoIsLocking(fileToCheckPath), App.Logger); if (processes is not null) { diff --git a/src/Files.App/ViewModels/Dialogs/CreateShortcutDialogViewModel.cs b/src/Files.App/ViewModels/Dialogs/CreateShortcutDialogViewModel.cs index 7d522f05813f..3021aa78a99c 100644 --- a/src/Files.App/ViewModels/Dialogs/CreateShortcutDialogViewModel.cs +++ b/src/Files.App/ViewModels/Dialogs/CreateShortcutDialogViewModel.cs @@ -91,14 +91,14 @@ public CreateShortcutDialogViewModel(string workingDirectory) private Task SelectDestination() { - InteropHelpers.BROWSEINFO bi = new InteropHelpers.BROWSEINFO(); + Win32PInvoke.BROWSEINFO bi = new Win32PInvoke.BROWSEINFO(); bi.ulFlags = 0x00004000; bi.lpszTitle = "Select a folder"; - nint pidl = InteropHelpers.SHBrowseForFolder(ref bi); + nint pidl = Win32PInvoke.SHBrowseForFolder(ref bi); if (pidl != nint.Zero) { StringBuilder path = new StringBuilder(260); - if (InteropHelpers.SHGetPathFromIDList(pidl, path)) + if (Win32PInvoke.SHGetPathFromIDList(pidl, path)) { DestinationItemPath = path.ToString(); } diff --git a/src/Files.App/ViewModels/UserControls/Previews/ShellPreviewViewModel.cs b/src/Files.App/ViewModels/UserControls/Previews/ShellPreviewViewModel.cs index 8c31365eb5fa..df4a552077ed 100644 --- a/src/Files.App/ViewModels/UserControls/Previews/ShellPreviewViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Previews/ShellPreviewViewModel.cs @@ -193,11 +193,11 @@ public void PointerEntered(bool onPreview) { DwmApi.DwmSetWindowAttribute(hwnd, DwmApi.DWMWINDOWATTRIBUTE.DWMWA_CLOAK, false); if (isOfficePreview) - InteropHelpers.SetWindowLong(hwnd, WindowLongFlags.GWL_EXSTYLE, 0); + Win32Helper.SetWindowLong(hwnd, WindowLongFlags.GWL_EXSTYLE, 0); } else { - InteropHelpers.SetWindowLong(hwnd, WindowLongFlags.GWL_EXSTYLE, + Win32Helper.SetWindowLong(hwnd, WindowLongFlags.GWL_EXSTYLE, (nint)(WindowStylesEx.WS_EX_LAYERED | WindowStylesEx.WS_EX_COMPOSITED)); DwmApi.DwmSetWindowAttribute(hwnd, DwmApi.DWMWINDOWATTRIBUTE.DWMWA_CLOAK, true); } diff --git a/src/Files.App/Views/Layouts/BaseGroupableLayoutPage.cs b/src/Files.App/Views/Layouts/BaseGroupableLayoutPage.cs index d51d65e4fe09..5924d0ada5a3 100644 --- a/src/Files.App/Views/Layouts/BaseGroupableLayoutPage.cs +++ b/src/Files.App/Views/Layouts/BaseGroupableLayoutPage.cs @@ -286,7 +286,7 @@ protected virtual async void RenameTextBox_LostFocus(object sender, RoutedEventA protected async void RenameTextBox_KeyDown(object sender, KeyRoutedEventArgs e) { var textBox = (TextBox)sender; - var isShiftPressed = (InteropHelpers.GetKeyState((int)VirtualKey.Shift) & KEY_DOWN_MASK) != 0; + var isShiftPressed = (Win32PInvoke.GetKeyState((int)VirtualKey.Shift) & KEY_DOWN_MASK) != 0; switch (e.Key) { From be0ab4605a209d9174934ad733ff23373fe455ab Mon Sep 17 00:00:00 2001 From: Marcel W Date: Mon, 1 Apr 2024 17:50:45 +0200 Subject: [PATCH 11/41] Fix: Fixed issue of overlapping brushes leaving gap in the Columns View (#15096) Co-authored-by: Yair <39923744+yaira2@users.noreply.github.com> --- .../Helpers/Layout/LayoutSizeKindHelper.cs | 12 +++++------ .../Views/Layouts/ColumnLayoutPage.xaml | 1 + .../Views/Layouts/ColumnLayoutPage.xaml.cs | 21 +++++++++++++------ 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/src/Files.App/Helpers/Layout/LayoutSizeKindHelper.cs b/src/Files.App/Helpers/Layout/LayoutSizeKindHelper.cs index 7b0af4364acb..fa25561d4737 100644 --- a/src/Files.App/Helpers/Layout/LayoutSizeKindHelper.cs +++ b/src/Files.App/Helpers/Layout/LayoutSizeKindHelper.cs @@ -103,17 +103,17 @@ public static int GetColumnsViewRowHeight(ColumnsViewSizeKind columnsViewSizeKin switch (columnsViewSizeKind) { case ColumnsViewSizeKind.Compact: - return 28; + return 24; case ColumnsViewSizeKind.Small: - return 36; + return 32; case ColumnsViewSizeKind.Medium: - return 40; + return 36; case ColumnsViewSizeKind.Large: - return 44; + return 40; case ColumnsViewSizeKind.ExtraLarge: - return 48; + return 44; default: - return 36; + return 32; } } diff --git a/src/Files.App/Views/Layouts/ColumnLayoutPage.xaml b/src/Files.App/Views/Layouts/ColumnLayoutPage.xaml index 9e08d44e7391..e4c52f8e63bf 100644 --- a/src/Files.App/Views/Layouts/ColumnLayoutPage.xaml +++ b/src/Files.App/Views/Layouts/ColumnLayoutPage.xaml @@ -206,6 +206,7 @@ ()!; - presenter!.Background = new SolidColorBrush(Microsoft.UI.Colors.Transparent); + SetFolderBackground(openedFolderPresenter, new SolidColorBrush(Microsoft.UI.Colors.Transparent)); openedFolderPresenter = null; } @@ -157,8 +156,8 @@ private void HighlightPathDirectory(ListViewBase sender, ContainerContentChangin if (args.Item is ListedItem item && columnsOwner?.OwnerPath is string ownerPath && (ownerPath == item.ItemPath || ownerPath.StartsWith(item.ItemPath) && ownerPath[item.ItemPath.Length] is '/' or '\\')) { - var presenter = args.ItemContainer.FindDescendant()!; - presenter!.Background = this.Resources["ListViewItemBackgroundSelected"] as SolidColorBrush; + SetFolderBackground(args.ItemContainer as ListViewItem, this.Resources["ListViewItemBackgroundSelected"] as SolidColorBrush); + openedFolderPresenter = FileList.ContainerFromItem(item) as ListViewItem; FileList.ContainerContentChanging -= HighlightPathDirectory; } @@ -279,8 +278,7 @@ protected override void FileList_SelectionChanged(object sender, SelectionChange if (e.RemovedItems.Count > 0 && openedFolderPresenter != null) { - var presenter = openedFolderPresenter.FindDescendant()!; - presenter!.Background = this.Resources["ListViewItemBackgroundSelected"] as SolidColorBrush; + SetFolderBackground(openedFolderPresenter, this.Resources["ListViewItemBackgroundSelected"] as SolidColorBrush); } if (SelectedItems?.Count == 1 && SelectedItem?.PrimaryItemAttribute is StorageItemTypes.Folder) @@ -592,5 +590,16 @@ internal void ClearSelectionIndicator() FileList.SelectedItem = null; LockPreviewPaneContent = false; } + + private static void SetFolderBackground(ListViewItem? lvi, SolidColorBrush? backgroundColor) + { + if (lvi == null || backgroundColor == null) return; + + + if (lvi.FindDescendant() is Grid presenter) + { + presenter.Background = backgroundColor; + } + } } } From bb8031c0aa8ec3632f74d61271c5c73cd48ff715 Mon Sep 17 00:00:00 2001 From: 0x5bfa <62196528+0x5bfa@users.noreply.github.com> Date: Thu, 4 Apr 2024 00:02:32 +0900 Subject: [PATCH 12/41] GitHub: Added ps1 scripts to simplify CI/CD (#15030) --- .../{deploy-preview.yml => cd-preview.yml} | 85 +++++-------------- .../{deploy-stable.yml => cd-stable.yml} | 83 +++++------------- scripts/Configure-AppxManifest.ps1 | 82 ++++++++++++++++++ 3 files changed, 127 insertions(+), 123 deletions(-) rename .github/workflows/{deploy-preview.yml => cd-preview.yml} (60%) rename .github/workflows/{deploy-stable.yml => cd-stable.yml} (63%) create mode 100644 scripts/Configure-AppxManifest.ps1 diff --git a/.github/workflows/deploy-preview.yml b/.github/workflows/cd-preview.yml similarity index 60% rename from .github/workflows/deploy-preview.yml rename to .github/workflows/cd-preview.yml index 7467a0929bcf..4d8a75256594 100644 --- a/.github/workflows/deploy-preview.yml +++ b/.github/workflows/cd-preview.yml @@ -1,4 +1,7 @@ -name: Deploy Preview Pipeline +# Copyright (c) 2024 Files Community +# Licensed under the MIT License. See the LICENSE. + +name: Files CD (Preview) on: workflow_dispatch: @@ -27,72 +30,31 @@ jobs: steps: - name: Checkout the repository uses: actions/checkout@v4 - - name: Setup MSBuild uses: microsoft/setup-msbuild@v1 - - name: Setup NuGet uses: NuGet/setup-nuget@v1.1.1 - - name: Setup .NET 8 uses: actions/setup-dotnet@v3 with: dotnet-version: '8.0.x' - # TODO: Move the command to PowerShell script instead - - name: Update Package.appxmanifest + - name: Configure the package manifest, logo sets, and secrets shell: pwsh run: | - [xml]$xmlDoc = Get-Content "$env:PACKAGE_PROJECT_DIR\Package.appxmanifest" - $xmlDoc.Package.Identity.Name="FilesPreview" - $xmlDoc.Package.Identity.Publisher="$env:SIDELOAD_PUBLISHER_SECRET" - $xmlDoc.Package.Properties.DisplayName="Files - Preview" - $xmlDoc.Package.Applications.Application.VisualElements.DisplayName="Files - Preview" - $xmlDoc.Save("$env:PACKAGE_PROJECT_DIR\Package.appxmanifest") + . './scripts/Configure-AppxManifest.ps1' ` + -Branch "$env:CONFIGURATION" ` + -PackageProjectDir "$env:PACKAGE_PROJECT_DIR" ` + -Publisher "$env:SIDELOAD_PUBLISHER_SECRET" ` + -WorkingDir "$env:WORKING_DIR" ` + -SecretBingMapsKey "$env:SECRET_BINGMAPS_KEY" ` + -SecretAppCenter "$env:SECRET_APPCENTER" ` + -SecretGitHubOAuthClientId "$env:SECRET_GITHUB_OAUTH_CLIENT_ID" env: SIDELOAD_PUBLISHER_SECRET: ${{ secrets.SIDELOAD_PUBLISHER_SECRET }} - - # TODO: Move the command to PowerShell script instead - - name: Use the ${{ env.CONFIGURATION }} logo sets - shell: pwsh - run: | - Get-ChildItem "$env:WORKING_DIR\src" -Include *.csproj, *.appxmanifest, *.wapproj, *.xaml -recurse | ForEach -Process ` - { ` - (Get-Content $_ -Raw | ForEach -Process { $_ -replace "Assets\\AppTiles\\Dev", "Assets\AppTiles\Preview" }) | ` - Set-Content $_ -NoNewline ` - } - - - name: Inject the Bing Maps API token - shell: pwsh - run: | - Get-ChildItem "$env:WORKING_DIR\src" -Include *.cs -recurse | ForEach-Object -Process ` - { ` - (Get-Content $_ -Raw | ForEach-Object -Process { $_ -replace "bingmapskey.secret", "$env:BING_MAPS_SECRET" }) | ` - Set-Content $_ -NoNewline ` - } - env: - BING_MAPS_SECRET: ${{ secrets.BING_MAPS_SECRET }} - - - name: Inject the AppCenter token - shell: pwsh - run: | - Get-ChildItem "$env:WORKING_DIR\src" -Include *.cs -recurse | ForEach-Object -Process ` - { ` - (Get-Content $_ -Raw | ForEach-Object -Process { $_ -replace "appcenter.secret", "$env:APP_CENTER_SECRET" }) | ` - Set-Content $_ -NoNewline ` - } - env: - APP_CENTER_SECRET: ${{ secrets.APP_CENTER_SECRET }} - - - name: Inject the GitHub OAuth client ID - run: | - Get-ChildItem "$env:WORKING_DIR\src" -Include *.cs -recurse | ForEach-Object -Process ` - { ` - (Get-Content $_ -Raw | ForEach-Object -Process { $_ -replace "githubclientid.secret", "$env:GH_OAUTH_CLIENT_ID" }) | ` - Set-Content $_ -NoNewline ` - } - env: - GH_OAUTH_CLIENT_ID: ${{ secrets.GH_OAUTH_CLIENT_ID }} + SECRET_BINGMAPS_KEY: ${{ secrets.BING_MAPS_SECRET }} + SECRET_APPCENTER: ${{ secrets.APP_CENTER_SECRET }} + SECRET_GITHUB_OAUTH_CLIENT_ID: ${{ secrets.GH_OAUTH_CLIENT_ID }} - name: Use Windows SDK Preview shell: cmd @@ -156,21 +118,20 @@ jobs: timestamp-rfc3161: http://timestamp.acs.microsoft.com timestamp-digest: SHA256 - - uses: azure/login@v1 + - name: Login to Azure + uses: azure/login@v1 with: - creds: ${{ secrets.AZURE_CREDENTIALS }} + creds: ${{ secrets.AZURE_CREDENTIALS }} - - name: Upload to blob storage + - name: Upload to Azure blob storage uses: azure/powershell@v1 with: inlineScript: | - az storage blob upload-batch --account-name "filescommunity" --destination "files" --destination-path "preview" --source ${{ env.APPX_PACKAGE_DIR }} --overwrite true + az storage blob upload-batch --account-name "filescommunity" --destination "files" --destination-path "preview" --source ${{ env.APPX_PACKAGE_DIR }} --overwrite true azPSVersion: "latest" - # Azure logout - - name: logout - run: | - az logout + - name: Logout from Azure + run: 'az logout' - name: Upload the packages to GitHub Actions uses: actions/upload-artifact@v3 diff --git a/.github/workflows/deploy-stable.yml b/.github/workflows/cd-stable.yml similarity index 63% rename from .github/workflows/deploy-stable.yml rename to .github/workflows/cd-stable.yml index a61ea8b09f7b..df00b0b887bc 100644 --- a/.github/workflows/deploy-stable.yml +++ b/.github/workflows/cd-stable.yml @@ -1,4 +1,7 @@ -name: Deploy Stable Pipeline +# Copyright (c) 2024 Files Community +# Licensed under the MIT License. See the LICENSE. + +name: Files CD (Stable) on: workflow_dispatch: @@ -27,73 +30,32 @@ jobs: steps: - name: Checkout the repository uses: actions/checkout@v4 - - name: Setup MSBuild uses: microsoft/setup-msbuild@v1 - - name: Setup NuGet uses: NuGet/setup-nuget@v1.1.1 - - name: Setup .NET 8 uses: actions/setup-dotnet@v3 with: dotnet-version: '8.0.x' - # TODO: Move the command to PowerShell script instead - - name: Update Package.appxmanifest + - name: Configure the package manifest, logo sets, and secrets shell: pwsh run: | - [xml]$xmlDoc = Get-Content "$env:PACKAGE_PROJECT_DIR\Package.appxmanifest" - $xmlDoc.Package.Identity.Name="Files" - $xmlDoc.Package.Identity.Publisher="$env:SIDELOAD_PUBLISHER_SECRET" - $xmlDoc.Package.Properties.DisplayName="Files" - $xmlDoc.Package.Applications.Application.VisualElements.DisplayName="Files" - $xmlDoc.Save("$env:PACKAGE_PROJECT_DIR\Package.appxmanifest") + . './scripts/Configure-AppxManifest.ps1' ` + -Branch "$env:CONFIGURATION" ` + -PackageProjectDir "$env:PACKAGE_PROJECT_DIR" ` + -Publisher "$env:SIDELOAD_PUBLISHER_SECRET" ` + -WorkingDir "$env:WORKING_DIR" ` + -SecretBingMapsKey "$env:SECRET_BINGMAPS_KEY" ` + -SecretAppCenter "$env:SECRET_APPCENTER" ` + -SecretGitHubOAuthClientId "$env:SECRET_GITHUB_OAUTH_CLIENT_ID" env: SIDELOAD_PUBLISHER_SECRET: ${{ secrets.SIDELOAD_PUBLISHER_SECRET }} - - # TODO: Move the command to PowerShell script instead - - name: Use the ${{ env.CONFIGURATION }} logo sets - shell: pwsh - run: | - Get-ChildItem "$env:WORKING_DIR\src" -Include *.csproj, *.appxmanifest, *.wapproj, *.xaml -recurse | ForEach -Process ` - { ` - (Get-Content $_ -Raw | ForEach -Process { $_ -replace "Assets\\AppTiles\\Dev", "Assets\AppTiles\Release" }) | ` - Set-Content $_ -NoNewline ` - } - - - name: Inject the Bing Maps API token - shell: pwsh - run: | - Get-ChildItem "$env:WORKING_DIR\src" -Include *.cs -recurse | ForEach-Object -Process ` - { ` - (Get-Content $_ -Raw | ForEach-Object -Process { $_ -replace "bingmapskey.secret", "$env:BING_MAPS_SECRET" }) | ` - Set-Content $_ -NoNewline ` - } - env: - BING_MAPS_SECRET: ${{ secrets.BING_MAPS_SECRET }} - - - name: Inject the AppCenter token - shell: pwsh - run: | - Get-ChildItem "$env:WORKING_DIR\src" -Include *.cs -recurse | ForEach-Object -Process ` - { ` - (Get-Content $_ -Raw | ForEach-Object -Process { $_ -replace "appcenter.secret", "$env:APP_CENTER_SECRET" }) | ` - Set-Content $_ -NoNewline ` - } - env: - APP_CENTER_SECRET: ${{ secrets.APP_CENTER_SECRET }} - - - name: Inject the GitHub OAuth client ID - run: | - Get-ChildItem "$env:WORKING_DIR\src" -Include *.cs -recurse | ForEach-Object -Process ` - { ` - (Get-Content $_ -Raw | ForEach-Object -Process { $_ -replace "githubclientid.secret", "$env:GH_OAUTH_CLIENT_ID" }) | ` - Set-Content $_ -NoNewline ` - } - env: - GH_OAUTH_CLIENT_ID: ${{ secrets.GH_OAUTH_CLIENT_ID }} - + SECRET_BINGMAPS_KEY: ${{ secrets.BING_MAPS_SECRET }} + SECRET_APPCENTER: ${{ secrets.APP_CENTER_SECRET }} + SECRET_GITHUB_OAUTH_CLIENT_ID: ${{ secrets.GH_OAUTH_CLIENT_ID }} + - name: Use Windows SDK Preview shell: cmd run: | @@ -156,21 +118,20 @@ jobs: timestamp-rfc3161: http://timestamp.acs.microsoft.com timestamp-digest: SHA256 - - uses: azure/login@v1 + - name: Login to Azure + uses: azure/login@v1 with: creds: ${{ secrets.AZURE_CREDENTIALS }} - - name: Upload to blob storage + - name: Upload to Azure blob storage uses: azure/powershell@v1 with: inlineScript: | az storage blob upload-batch --account-name "filescommunity" --destination "files" --destination-path "stable" --source ${{ env.APPX_PACKAGE_DIR }} --overwrite true azPSVersion: "latest" - # Azure logout - - name: logout - run: | - az logout + - name: Logout from Azure + run: 'az logout' - name: Upload the packages to GitHub Actions uses: actions/upload-artifact@v3 diff --git a/scripts/Configure-AppxManifest.ps1 b/scripts/Configure-AppxManifest.ps1 new file mode 100644 index 000000000000..6d275a8539ba --- /dev/null +++ b/scripts/Configure-AppxManifest.ps1 @@ -0,0 +1,82 @@ +# Copyright (c) 2024 Files Community +# Licensed under the MIT License. See the LICENSE. + +param( + [string]$Branch = "", + [string]$PackageProjectDir = "", + [string]$Publisher = "", + [string]$WorkingDir = "", + [string]$SecretBingMapsKey = "", + [string]$SecretAppCenter = "", + [string]$SecretGitHubOAuthClientId = "" +) + +[xml]$xmlDoc = Get-Content "$PackageProjectDir\Package.appxmanifest" +$xmlDoc.Package.Identity.Publisher="$Publisher" + +if ($Branch -eq "Preview") +{ + # Set identities + $xmlDoc.Package.Identity.Name="FilesPreview" + $xmlDoc.Package.Properties.DisplayName="Files - Preview" + $xmlDoc.Package.Applications.Application.VisualElements.DisplayName="Files - Preview" + + Get-ChildItem "$WorkingDir\src" -Include *.csproj, *.appxmanifest, *.wapproj, *.xaml -recurse | ForEach -Process + { + (Get-Content $_ -Raw | ForEach -Process { $_ -replace "Assets\\AppTiles\\Dev", "Assets\AppTiles\Preview" }) | + Set-Content $_ -NoNewline + } +} +elseif ($Branch -eq "Stable") +{ + # Set identities + $xmlDoc.Package.Identity.Name="Files" + $xmlDoc.Package.Properties.DisplayName="Files" + $xmlDoc.Package.Applications.Application.VisualElements.DisplayName="Files" + + Get-ChildItem "$WorkingDir\src" -Include *.csproj, *.appxmanifest, *.wapproj, *.xaml -recurse | ForEach -Process + { + (Get-Content $_ -Raw | ForEach -Process { $_ -replace "Assets\\AppTiles\\Dev", "Assets\AppTiles\Release" }) | + Set-Content $_ -NoNewline + } +} +elseif ($Branch -eq "Store") +{ + # Set identities + $xmlDoc.Package.Identity.Name="49306atecsolution.FilesUWP" + $xmlDoc.Package.Properties.DisplayName="Files" + $xmlDoc.Package.Applications.Application.VisualElements.DisplayName="Files" + + # Remove an capability that is used for the sideload + $nsmgr = New-Object System.Xml.XmlNamespaceManager($xmlDoc.NameTable) + $nsmgr.AddNamespace("pkg", "http://schemas.microsoft.com/appx/manifest/foundation/windows10") + $nsmgr.AddNamespace("rescap", "http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities") + $pm = $xmlDoc.SelectSingleNode("/pkg:Package/pkg:Capabilities/rescap:Capability[@Name='packageManagement']", $nsmgr) + $xmlDoc.Package.Capabilities.RemoveChild($pm) + + Get-ChildItem "$WorkingDir\src" -Include *.csproj, *.appxmanifest, *.wapproj, *.xaml -recurse | ForEach -Process + { + (Get-Content $_ -Raw | ForEach -Process { $_ -replace "Assets\\AppTiles\\Dev", "Assets\AppTiles\Release" }) | + Set-Content $_ -NoNewline + } +} + +$xmlDoc.Save("$PackageProjectDir\Package.appxmanifest") + +Get-ChildItem "$WorkingDir\src" -Include *.cs -recurse | ForEach-Object -Process +{ + (Get-Content $_ -Raw | ForEach-Object -Process { $_ -replace "bingmapskey.secret", "$SecretBingMapsKey" }) | + Set-Content $_ -NoNewline +} + +Get-ChildItem "$WorkingDir\src" -Include *.cs -recurse | ForEach-Object -Process +{ + (Get-Content $_ -Raw | ForEach-Object -Process { $_ -replace "appcenter.secret", "$SecretAppCenter" }) | + Set-Content $_ -NoNewline +} + +Get-ChildItem "$WorkingDir\src" -Include *.cs -recurse | ForEach-Object -Process +{ + (Get-Content $_ -Raw | ForEach-Object -Process { $_ -replace "githubclientid.secret", "$SecretGitHubOAuthClientId" }) | + Set-Content $_ -NoNewline +} From a0acba3e8d5201bd67d63c770e5f339e4d257a76 Mon Sep 17 00:00:00 2001 From: Steve Date: Thu, 4 Apr 2024 00:19:19 +0900 Subject: [PATCH 13/41] Feature: LiteDB server and Files.Core removal (#14911) --- Files.sln | 33 -- src/Files.App (Package)/Package.appxmanifest | 9 + .../Files.App.BackgroundTasks.csproj | 2 +- .../Files.App.Launcher.vcxproj | 14 +- src/Files.App.Launcher/packages.config | 4 +- src/Files.App.Server/AppInstanceMonitor.cs | 5 +- .../Data/ColumnPreferences.cs | 24 ++ .../Data/ColumnPreferencesItem.cs | 12 + .../Data/Enums/FolderLayoutModes.cs | 2 +- .../Data/Enums/GroupByDateUnit.cs | 4 +- .../Data/Enums/GroupOption.cs | 26 +- .../Data/Enums/SortDirection.cs | 4 +- .../Data/Enums/SortOption.cs | 24 +- .../Data/LayoutPreferences.cs | 17 + .../Data/LayoutPreferencesItem.cs | 25 ++ src/Files.App.Server/Data/TaggedFile.cs | 15 + .../Database/FileTagsDatabase.cs} | 102 +++--- .../Database/LayoutPreferencesDatabase.cs | 164 ++++++++++ src/Files.App.Server/Files.App.Server.csproj | 4 +- src/Files.App.Server/Helpers.cs | 5 +- src/Files.App.Server/Program.cs | 5 +- src/Files.App/Actions/Display/GroupAction.cs | 2 + src/Files.App/Actions/Display/SortAction.cs | 2 + .../Data/AppModels/BaseDatabaseModel.cs | 2 +- .../Data/AppModels/SingleFileDatabaseModel.cs | 2 +- .../DisplayPage/DisplayPageContext.cs | 1 + .../DisplayPage/IDisplayPageContext.cs | 2 + .../Data/Enums/AddItemDialogItemType.cs | 2 +- .../Data/Enums/AppEnvironment.cs | 2 +- .../Data/Enums/ArchiveCompressionLevels.cs | 2 +- .../Data/Enums/ArchiveFormats.cs | 2 +- .../Data/Enums/ArchiveSplittingSizes.cs | 2 +- .../Data/Enums/BackdropMaterialType.cs | 2 +- .../Data/Enums/ColumnsViewSizeKind.cs | 2 +- .../Data/Enums/ContextMenuFlyoutItemType.cs | 2 +- .../Data/Enums/CopyEngineResult.cs | 2 +- .../Data/Enums/DateTimeFormats.cs | 2 +- .../Data/Enums/DeleteConfirmationPolicy.cs | 2 +- .../Data/Enums/DetailsViewSizeKind.cs | 2 +- .../Data/Enums/DialogResult.cs | 2 +- .../Data/Enums/DynamicDialogButtons.cs | 2 +- .../Data/Enums/DynamicDialogResult.cs | 2 +- .../FileNameConflictResolveOptionType.cs | 2 +- .../Data/Enums/FileOperationType.cs | 2 +- .../Data/Enums/FileSystemStatusCode.cs | 2 +- .../Data/Enums/FilesystemItemType.cs | 2 +- .../Data/Enums/FilesystemOperationType.cs | 2 +- .../Data/Enums/GitCheckoutOptions.cs | 2 +- .../Data/Enums/GitOperationResult.cs | 2 +- .../Data/Enums/GridViewSizeKind.cs | 2 +- .../Data/Enums/IconOptions.cs | 2 +- .../Data/Enums/IconPersistenceOptions.cs | 2 +- .../Enums/ImpossibleActionResponseTypes.cs | 2 +- .../Data/Enums/InfoPaneTabs.cs | 2 +- .../Data/Enums/ListViewSizeKind.cs | 2 +- .../Data/Enums/ParsedCommandType.cs | 2 +- .../Data/Enums/PreviewPaneStates.cs | 2 +- .../Enums/PropertiesNavigationViewItemType.cs | 2 +- .../Data/Enums/ReturnResult.cs | 2 +- .../Data/Enums/SearchBoxTextChangeReason.cs | 2 +- .../Data/Enums/StatusCenterItemIconKind.cs | 2 +- .../Data/Enums/StatusCenterItemKind.cs | 2 +- .../Data/Enums/TilesViewSizeKind.cs | 2 +- .../Data/Enums/WallpaperType.cs | 2 +- .../Data/Enums/WindowsCompatibilityModes.cs | 2 +- .../EventArguments/LayoutModeEventArgs.cs | 2 + .../EventArguments/SettingChangedEventArgs.cs | 2 +- .../PropertiesNavigationViewItemFactory.cs | 2 +- .../Data/Items/ContextMenu.cs | 2 +- .../Data/Items/DetailsLayoutColumnItem.cs | 12 +- .../Data/Items/DeviceEvent.cs | 2 +- .../Data/Items/IconFileInfo.cs | 2 +- src/Files.App/Data/Items/ListedItem.cs | 1 + .../Data/Items/ShellFileItem.cs | 2 +- .../Data/Items/ShellLibraryItem.cs | 2 +- .../Data/Items/ShellLinkItem.cs | 2 +- .../Data/Items/ShellNewEntry.cs | 2 +- .../Data/Items/ShellOperationResult.cs | 2 +- .../Data/Items/Win32Process.cs | 2 +- .../Data/Items/WindowsCompatibilityOptions.cs | 2 +- .../FileSystemDialogOptionChangedMessage.cs | 4 +- .../Data/Models/AddItemDialogResultModel.cs | 2 +- .../Data/Models/ByteSize.cs | 2 +- src/Files.App/Data/Models/ColumnsViewModel.cs | 2 - .../Data/Models/CurrentInstanceViewModel.cs | 2 + .../Data/Models/DisposableArray.cs | 2 +- src/Files.App/Data/Models/DrivesViewModel.cs | 2 +- .../Data/Models/FreeableStore.cs | 2 +- .../Data/Models/HashInfoItem.cs | 2 +- .../Data/Models/IDatabaseModel.cs | 2 +- .../Data/Models/ISerializedModel.cs | 2 +- .../Data/Models/IStorageDeviceWatcher.cs | 2 +- src/Files.App/Data/Models/ItemViewModel.cs | 5 +- .../Data/Models/NetworkDrivesViewModel.cs | 2 +- .../Data/Models/TaggedItemModel.cs | 2 +- .../Data/Models/VolumeInfo.cs | 2 +- .../FileSystemDialogItemSelector.cs | 2 +- src/Files.App/Dialogs/AddItemDialog.xaml | 2 +- src/Files.App/Dialogs/AddItemDialog.xaml.cs | 4 +- .../Dialogs/CreateShortcutDialog.xaml.cs | 2 +- .../Dialogs/ElevateConfirmDialog.xaml.cs | 2 +- .../Dialogs/FilesystemOperationDialog.xaml | 2 +- .../Dialogs/ReorderSidebarItemsDialog.xaml.cs | 2 +- src/Files.App/Dialogs/SettingsDialog.xaml.cs | 2 +- .../Extensions/GroupOptionExtensions.cs | 4 +- .../Extensions/LocalizationExtensions.cs | 4 +- .../Extensions/Win32Extensions.cs | 2 +- .../Extensions/Win32FindDataExtensions.cs | 4 +- src/Files.App/Files.App.csproj | 6 +- src/Files.App/GlobalUsings.cs | 31 +- .../Helpers/Application/AppLifecycleHelper.cs | 2 +- .../Interop/NativeFileOperationsHelper.cs | 2 +- .../Layout/LayoutPreferencesDatabaseItem.cs | 3 - .../LayoutPreferencesDatabaseManager.cs | 257 +++++++-------- .../Helpers/Layout/LayoutPreferencesItem.cs | 24 +- .../Layout/LayoutPreferencesManager.cs | 37 ++- .../Helpers/NativeFindStorageItemHelper.cs | 2 +- .../Helpers/Navigation/NavigationHelpers.cs | 1 + .../Helpers/UI/AppThemeResourcesHelper.cs | 4 +- .../AbstractDateTimeFormatter.cs | 1 + .../DateTimeFormatter/IDateTimeFormatter.cs | 4 +- .../IDateTimeFormatterFactory.cs | 2 +- .../DateTimeFormatter/ITimeSpanLabel.cs | 2 +- .../UserDateTimeFormatter.cs | 2 + src/Files.App/Services/DialogService.cs | 8 +- .../Services/IAddItemService.cs | 2 +- .../Services/IDialogService.cs | 6 +- .../Services/IFileExplorerService.cs | 2 +- .../Services/IFileTagsService.cs | 4 +- .../Services/IImageService.cs | 2 +- .../Services/IJumpListService.cs | 2 +- .../Services/ILocalizationService.cs | 2 +- .../Services/INetworkDrivesService.cs | 2 +- .../Services/IPreviewPopupProvider.cs | 2 +- .../Services/IPreviewPopupService.cs | 2 +- .../Services/IQuickAccessService.cs | 0 .../Services/IRemovableDrivesService.cs | 2 +- .../Services/IResourcesService.cs | 2 +- .../Services/IStartMenuService.cs | 2 +- .../Services/IThreadingService.cs | 2 +- .../Services/IUpdateService.cs | 2 +- .../Services/IVolumeInfoFactory.cs | 2 +- .../PreviewPopupProviders/SeerProProvider.cs | 2 +- .../Services/Settings/AppSettingsService.cs | 2 +- .../Settings/ApplicationSettingsService.cs | 2 +- .../Settings/FileTagsSettingsService.cs | 4 +- .../Settings/FoldersSettingsService.cs | 1 + .../Services/Settings/IAppSettingsService.cs | 2 +- .../Settings/IAppearanceSettingsService.cs | 2 +- .../Settings/IApplicationSettingsService.cs | 2 +- .../Services/Settings/IBaseSettingsService.cs | 2 +- .../Settings/IFileTagsSettingsService.cs | 4 +- .../Settings/IFoldersSettingsService.cs | 4 +- .../Settings/IGeneralSettingsService.cs | 4 +- .../Settings/IInfoPaneSettingsService.cs | 2 +- .../Settings/ILayoutSettingsService.cs | 5 +- .../Services/Settings/IUserSettingsService.cs | 2 +- .../Settings/InfoPaneSettingsService.cs | 2 +- .../Settings/LayoutSettingsService.cs | 1 + .../Services/Settings/UserSettingsService.cs | 2 +- .../SizeProvider/CachedSizeProvider.cs | 6 +- .../SizeProvider/DrivesSizeProvider.cs | 2 +- .../Services/SizeProvider/ISizeProvider.cs | 2 +- .../Services/SizeProvider/NoSizeProvider.cs | 2 +- .../SizeProvider/SizeChangedEventArgs.cs | 2 +- .../SizeProvider/SizeChangedValueState.cs | 2 +- src/Files.App/Services/UserSizeProvider.cs | 2 +- .../UserControls/DataGridHeader.xaml.cs | 1 + .../FilePreviews/MediaPreview.xaml.cs | 2 +- .../UserControls/Menus/FileTagsContextMenu.cs | 2 +- .../Utils/Cloud/CloudDriveSyncStatus.cs | 2 +- .../Utils/Cloud/CloudDriveSyncStatusUI.cs | 2 +- .../Utils/Cloud/CloudProvider.cs | 2 +- .../Utils/Cloud/CloudProviders.cs | 2 +- .../Cloud/Detector/AbstractCloudDetector.cs | 2 +- .../Utils/Cloud/Detector/BoxCloudDetector.cs | 2 +- .../Utils/Cloud/Detector/CloudDetector.cs | 2 +- .../Cloud/Detector/DropBoxCloudDetector.cs | 2 +- .../Cloud/Detector/GenericCloudDetector.cs | 2 +- .../Detector/SynologyDriveCloudDetector.cs | 2 +- .../Utils/Cloud/ICloudDetector.cs | 2 +- .../Utils/Cloud/ICloudProvider.cs | 2 +- .../Utils/CommandLine/CommandLineParser.cs | 2 +- .../Utils/CommandLine/ParsedCommand.cs | 2 +- .../Utils/CommandLine/ParsedCommands.cs | 2 +- .../Utils/FileTags/FileTagsHelper.cs | 6 +- .../Storage/Collection/GroupingHelper.cs | 1 + .../Utils/Storage/Collection/SortingHelper.cs | 1 + .../Enumerators/Win32StorageEnumerator.cs | 4 +- .../Utils/Storage/Helpers/FolderHelpers.cs | 2 +- .../Operations/FileOperationsHelpers.cs | 2 +- .../Utils/Storage/Search/FolderSearch.cs | 4 +- .../StorageItems/VirtualStorageItem.cs | 2 +- .../AddItemDialogListItemViewModel.cs | 2 +- .../AddItemDialog/AddItemDialogViewModel.cs | 2 +- .../ViewModels/Dialogs/BaseDialogViewModel.cs | 2 +- .../Dialogs/CredentialDialogViewModel.cs | 2 +- .../Dialogs/ElevateConfirmDialogViewModel.cs | 2 +- .../BaseFileSystemDialogItemViewModel.cs | 2 +- .../FileSystemDialogConflictItemViewModel.cs | 4 +- .../FileSystemDialogDefaultItemViewModel.cs | 2 +- .../FileSystemDialogViewModel.cs | 4 +- .../IFileSystemDialogConflictItemViewModel.cs | 4 +- .../Dialogs/FileTooLargeDialogViewModel.cs | 2 +- .../Dialogs/GitHubLoginDialogViewModel.cs | 2 +- .../ViewModels/Dialogs/IDialog.cs | 4 +- .../Dialogs/ReleaseNotesDialogViewModel.cs | 2 +- .../Dialogs/SettingsDialogViewModel.cs | 2 +- .../ViewModels/FileTags/ListedTagViewModel.cs | 2 +- .../ViewModels/FileTags/TagViewModel.cs | 2 +- .../Properties/Items/BaseProperties.cs | 2 +- .../ViewModels/Settings/AdvancedViewModel.cs | 4 +- .../ViewModels/Settings/FoldersViewModel.cs | 2 + .../ViewModels/Settings/LayoutViewModel.cs | 2 + .../UserControls/AddressToolbarViewModel.cs | 1 + .../FileTagsContainerViewModel.cs | 2 +- .../FileTagsWidget/FileTagsItemViewModel.cs | 2 +- .../FileTagsWidget/FileTagsWidgetViewModel.cs | 2 +- .../Views/Layouts/BaseGroupableLayoutPage.cs | 1 + src/Files.App/Views/Layouts/BaseLayoutPage.cs | 4 +- .../Views/Layouts/ColumnLayoutPage.xaml.cs | 1 + .../Views/Layouts/DetailsLayoutPage.xaml | 2 +- .../Views/Layouts/DetailsLayoutPage.xaml.cs | 3 +- .../Views/Layouts/GridLayoutPage.xaml.cs | 1 + .../Views/Properties/CustomizationPage.xaml | 2 +- .../Views/Properties/HashesPage.xaml | 2 +- src/Files.App/Views/Settings/TagsPage.xaml | 2 +- src/Files.App/Views/Settings/TagsPage.xaml.cs | 2 +- src/Files.App/Views/Shells/BaseShellPage.cs | 2 +- .../Views/Shells/ColumnShellPage.xaml.cs | 1 + .../Microsoft.UI.Content.cs | 301 ------------------ src/Files.App/nupkgs/Microsoft.UI.winmd | Bin 0 -> 289280 bytes .../Files.Core.SourceGenerator.csproj | 4 +- src/Files.Core/Data/Items/IsExternalInit.cs | 11 - src/Files.Core/Files.Core.csproj | 31 -- src/Files.Core/GlobalUsings.cs | 35 -- src/Files.Shared/Files.Shared.csproj | 2 - 237 files changed, 781 insertions(+), 926 deletions(-) create mode 100644 src/Files.App.Server/Data/ColumnPreferences.cs create mode 100644 src/Files.App.Server/Data/ColumnPreferencesItem.cs rename src/{Files.Core => Files.App.Server}/Data/Enums/FolderLayoutModes.cs (93%) rename src/{Files.Core => Files.App.Server}/Data/Enums/GroupByDateUnit.cs (82%) rename src/{Files.Core => Files.App.Server}/Data/Enums/GroupOption.cs (80%) rename src/{Files.Core => Files.App.Server}/Data/Enums/SortDirection.cs (85%) rename src/{Files.Core => Files.App.Server}/Data/Enums/SortOption.cs (80%) create mode 100644 src/Files.App.Server/Data/LayoutPreferences.cs create mode 100644 src/Files.App.Server/Data/LayoutPreferencesItem.cs create mode 100644 src/Files.App.Server/Data/TaggedFile.cs rename src/{Files.Core/Data/Items/FileTagsDb.cs => Files.App.Server/Database/FileTagsDatabase.cs} (60%) create mode 100644 src/Files.App.Server/Database/LayoutPreferencesDatabase.cs rename src/{Files.Core => Files.App}/Data/AppModels/BaseDatabaseModel.cs (96%) rename src/{Files.Core => Files.App}/Data/AppModels/SingleFileDatabaseModel.cs (99%) rename src/{Files.Core => Files.App}/Data/Enums/AddItemDialogItemType.cs (94%) rename src/{Files.Core => Files.App}/Data/Enums/AppEnvironment.cs (95%) rename src/{Files.Core => Files.App}/Data/Enums/ArchiveCompressionLevels.cs (95%) rename src/{Files.Core => Files.App}/Data/Enums/ArchiveFormats.cs (93%) rename src/{Files.Core => Files.App}/Data/Enums/ArchiveSplittingSizes.cs (97%) rename src/{Files.Core => Files.App}/Data/Enums/BackdropMaterialType.cs (94%) rename src/{Files.Core => Files.App}/Data/Enums/ColumnsViewSizeKind.cs (95%) rename src/{Files.Core => Files.App}/Data/Enums/ContextMenuFlyoutItemType.cs (94%) rename src/{Files.Core => Files.App}/Data/Enums/CopyEngineResult.cs (99%) rename src/{Files.Core => Files.App}/Data/Enums/DateTimeFormats.cs (92%) rename src/{Files.Core => Files.App}/Data/Enums/DeleteConfirmationPolicy.cs (92%) rename src/{Files.Core => Files.App}/Data/Enums/DetailsViewSizeKind.cs (95%) rename src/{Files.Core => Files.App}/Data/Enums/DialogResult.cs (92%) rename src/{Files.Core => Files.App}/Data/Enums/DynamicDialogButtons.cs (93%) rename src/{Files.Core => Files.App}/Data/Enums/DynamicDialogResult.cs (92%) rename src/{Files.Core => Files.App}/Data/Enums/FileNameConflictResolveOptionType.cs (93%) rename src/{Files.Core => Files.App}/Data/Enums/FileOperationType.cs (97%) rename src/{Files.Core => Files.App}/Data/Enums/FileSystemStatusCode.cs (97%) rename src/{Files.Core => Files.App}/Data/Enums/FilesystemItemType.cs (95%) rename src/{Files.Core => Files.App}/Data/Enums/FilesystemOperationType.cs (91%) rename src/{Files.Core => Files.App}/Data/Enums/GitCheckoutOptions.cs (94%) rename src/{Files.Core => Files.App}/Data/Enums/GitOperationResult.cs (93%) rename src/{Files.Core => Files.App}/Data/Enums/GridViewSizeKind.cs (97%) rename src/{Files.Core => Files.App}/Data/Enums/IconOptions.cs (96%) rename src/{Files.Core => Files.App}/Data/Enums/IconPersistenceOptions.cs (89%) rename src/{Files.Core => Files.App}/Data/Enums/ImpossibleActionResponseTypes.cs (92%) rename src/{Files.Core => Files.App}/Data/Enums/InfoPaneTabs.cs (91%) rename src/{Files.Core => Files.App}/Data/Enums/ListViewSizeKind.cs (95%) rename src/{Files.Core => Files.App}/Data/Enums/ParsedCommandType.cs (96%) rename src/{Files.Core => Files.App}/Data/Enums/PreviewPaneStates.cs (95%) rename src/{Files.Core => Files.App}/Data/Enums/PropertiesNavigationViewItemType.cs (96%) rename src/{Files.Core => Files.App}/Data/Enums/ReturnResult.cs (97%) rename src/{Files.Core => Files.App}/Data/Enums/SearchBoxTextChangeReason.cs (94%) rename src/{Files.Core => Files.App}/Data/Enums/StatusCenterItemIconKind.cs (91%) rename src/{Files.Core => Files.App}/Data/Enums/StatusCenterItemKind.cs (89%) rename src/{Files.Core => Files.App}/Data/Enums/TilesViewSizeKind.cs (90%) rename src/{Files.Core => Files.App}/Data/Enums/WallpaperType.cs (90%) rename src/{Files.Core => Files.App}/Data/Enums/WindowsCompatibilityModes.cs (97%) rename src/{Files.Core => Files.App}/Data/EventArguments/SettingChangedEventArgs.cs (90%) rename src/{Files.Core => Files.App}/Data/Items/ContextMenu.cs (97%) rename src/{Files.Core => Files.App}/Data/Items/DeviceEvent.cs (84%) rename src/{Files.Core => Files.App}/Data/Items/IconFileInfo.cs (90%) rename src/{Files.Core => Files.App}/Data/Items/ShellFileItem.cs (97%) rename src/{Files.Core => Files.App}/Data/Items/ShellLibraryItem.cs (98%) rename src/{Files.Core => Files.App}/Data/Items/ShellLinkItem.cs (96%) rename src/{Files.Core => Files.App}/Data/Items/ShellNewEntry.cs (92%) rename src/{Files.Core => Files.App}/Data/Items/ShellOperationResult.cs (96%) rename src/{Files.Core => Files.App}/Data/Items/Win32Process.cs (89%) rename src/{Files.Core => Files.App}/Data/Items/WindowsCompatibilityOptions.cs (98%) rename src/{Files.Core => Files.App}/Data/Messages/FileSystemDialogOptionChangedMessage.cs (85%) rename src/{Files.Core => Files.App}/Data/Models/AddItemDialogResultModel.cs (93%) rename src/{Files.Core => Files.App}/Data/Models/ByteSize.cs (98%) rename src/{Files.Core => Files.App}/Data/Models/DisposableArray.cs (97%) rename src/{Files.Core => Files.App}/Data/Models/FreeableStore.cs (95%) rename src/{Files.Core => Files.App}/Data/Models/HashInfoItem.cs (96%) rename src/{Files.Core => Files.App}/Data/Models/IDatabaseModel.cs (97%) rename src/{Files.Core => Files.App}/Data/Models/ISerializedModel.cs (92%) rename src/{Files.Core => Files.App}/Data/Models/IStorageDeviceWatcher.cs (97%) rename src/{Files.Core => Files.App}/Data/Models/TaggedItemModel.cs (92%) rename src/{Files.Core => Files.App}/Data/Models/VolumeInfo.cs (97%) rename src/{Files.Core => Files.App}/Extensions/GroupOptionExtensions.cs (82%) rename src/{Files.Core => Files.App}/Extensions/LocalizationExtensions.cs (92%) rename src/{Files.Core => Files.App}/Extensions/Win32Extensions.cs (89%) rename src/{Files.Core => Files.App}/Extensions/Win32FindDataExtensions.cs (83%) rename src/{Files.Core => Files.App}/Helpers/NativeFindStorageItemHelper.cs (99%) rename src/{Files.Core => Files.App}/Services/DateTimeFormatter/IDateTimeFormatter.cs (80%) rename src/{Files.Core => Files.App}/Services/DateTimeFormatter/IDateTimeFormatterFactory.cs (82%) rename src/{Files.Core => Files.App}/Services/DateTimeFormatter/ITimeSpanLabel.cs (81%) rename src/{Files.Core => Files.App}/Services/IAddItemService.cs (94%) rename src/{Files.Core => Files.App}/Services/IDialogService.cs (92%) rename src/{Files.Core => Files.App}/Services/IFileExplorerService.cs (98%) rename src/{Files.Core => Files.App}/Services/IFileTagsService.cs (97%) rename src/{Files.Core => Files.App}/Services/IImageService.cs (97%) rename src/{Files.Core => Files.App}/Services/IJumpListService.cs (90%) rename src/{Files.Core => Files.App}/Services/ILocalizationService.cs (86%) rename src/{Files.Core => Files.App}/Services/INetworkDrivesService.cs (96%) rename src/{Files.Core => Files.App}/Services/IPreviewPopupProvider.cs (95%) rename src/{Files.Core => Files.App}/Services/IPreviewPopupService.cs (91%) rename src/{Files.Core => Files.App}/Services/IQuickAccessService.cs (100%) rename src/{Files.Core => Files.App}/Services/IRemovableDrivesService.cs (97%) rename src/{Files.Core => Files.App}/Services/IResourcesService.cs (98%) rename src/{Files.Core => Files.App}/Services/IStartMenuService.cs (98%) rename src/{Files.Core => Files.App}/Services/IThreadingService.cs (89%) rename src/{Files.Core => Files.App}/Services/IUpdateService.cs (97%) rename src/{Files.Core => Files.App}/Services/IVolumeInfoFactory.cs (86%) rename src/{Files.Core => Files.App}/Services/Settings/IAppSettingsService.cs (95%) rename src/{Files.Core => Files.App}/Services/Settings/IAppearanceSettingsService.cs (97%) rename src/{Files.Core => Files.App}/Services/Settings/IApplicationSettingsService.cs (93%) rename src/{Files.Core => Files.App}/Services/Settings/IBaseSettingsService.cs (78%) rename src/{Files.Core => Files.App}/Services/Settings/IFileTagsSettingsService.cs (89%) rename src/{Files.Core => Files.App}/Services/Settings/IFoldersSettingsService.cs (97%) rename src/{Files.Core => Files.App}/Services/Settings/IGeneralSettingsService.cs (99%) rename src/{Files.Core => Files.App}/Services/Settings/IInfoPaneSettingsService.cs (96%) rename src/{Files.Core => Files.App}/Services/Settings/ILayoutSettingsService.cs (98%) rename src/{Files.Core => Files.App}/Services/Settings/IUserSettingsService.cs (94%) rename src/{Files.Core => Files.App}/Services/SizeProvider/CachedSizeProvider.cs (95%) rename src/{Files.Core => Files.App}/Services/SizeProvider/DrivesSizeProvider.cs (98%) rename src/{Files.Core => Files.App}/Services/SizeProvider/ISizeProvider.cs (91%) rename src/{Files.Core => Files.App}/Services/SizeProvider/NoSizeProvider.cs (93%) rename src/{Files.Core => Files.App}/Services/SizeProvider/SizeChangedEventArgs.cs (91%) rename src/{Files.Core => Files.App}/Services/SizeProvider/SizeChangedValueState.cs (80%) rename src/{Files.Core => Files.App}/Utils/Cloud/CloudDriveSyncStatus.cs (92%) rename src/{Files.Core => Files.App}/Utils/Cloud/CloudProvider.cs (95%) rename src/{Files.Core => Files.App}/Utils/Cloud/CloudProviders.cs (92%) rename src/{Files.Core => Files.App}/Utils/Cloud/ICloudDetector.cs (85%) rename src/{Files.Core => Files.App}/Utils/Cloud/ICloudProvider.cs (90%) rename src/{Files.Core => Files.App}/Utils/CommandLine/CommandLineParser.cs (99%) rename src/{Files.Core => Files.App}/Utils/CommandLine/ParsedCommand.cs (94%) rename src/{Files.Core => Files.App}/Utils/CommandLine/ParsedCommands.cs (85%) rename src/{Files.Core => Files.App}/ViewModels/Dialogs/AddItemDialog/AddItemDialogListItemViewModel.cs (89%) rename src/{Files.Core => Files.App}/ViewModels/Dialogs/AddItemDialog/AddItemDialogViewModel.cs (97%) rename src/{Files.Core => Files.App}/ViewModels/Dialogs/BaseDialogViewModel.cs (97%) rename src/{Files.Core => Files.App}/ViewModels/Dialogs/CredentialDialogViewModel.cs (96%) rename src/{Files.Core => Files.App}/ViewModels/Dialogs/ElevateConfirmDialogViewModel.cs (81%) rename src/{Files.Core => Files.App}/ViewModels/Dialogs/FileSystemDialog/BaseFileSystemDialogItemViewModel.cs (94%) rename src/{Files.Core => Files.App}/ViewModels/Dialogs/FileSystemDialog/FileSystemDialogConflictItemViewModel.cs (95%) rename src/{Files.Core => Files.App}/ViewModels/Dialogs/FileSystemDialog/FileSystemDialogDefaultItemViewModel.cs (77%) rename src/{Files.Core => Files.App}/ViewModels/Dialogs/FileSystemDialog/FileSystemDialogViewModel.cs (99%) rename src/{Files.Core => Files.App}/ViewModels/Dialogs/FileSystemDialog/IFileSystemDialogConflictItemViewModel.cs (79%) rename src/{Files.Core => Files.App}/ViewModels/Dialogs/FileTooLargeDialogViewModel.cs (88%) rename src/{Files.Core => Files.App}/ViewModels/Dialogs/GitHubLoginDialogViewModel.cs (96%) rename src/{Files.Core => Files.App}/ViewModels/Dialogs/IDialog.cs (80%) rename src/{Files.Core => Files.App}/ViewModels/Dialogs/ReleaseNotesDialogViewModel.cs (92%) rename src/{Files.Core => Files.App}/ViewModels/Dialogs/SettingsDialogViewModel.cs (80%) rename src/{Files.Core => Files.App}/ViewModels/FileTags/ListedTagViewModel.cs (96%) rename src/{Files.Core => Files.App}/ViewModels/FileTags/TagViewModel.cs (93%) rename src/{Files.Core => Files.App}/ViewModels/Widgets/FileTagsWidget/FileTagsContainerViewModel.cs (96%) rename src/{Files.Core => Files.App}/ViewModels/Widgets/FileTagsWidget/FileTagsItemViewModel.cs (96%) rename src/{Files.Core => Files.App}/ViewModels/Widgets/FileTagsWidget/FileTagsWidgetViewModel.cs (96%) delete mode 100644 src/Files.App/nupkgs/Microsoft.UI.Content/Microsoft.UI.Content.cs create mode 100644 src/Files.App/nupkgs/Microsoft.UI.winmd delete mode 100644 src/Files.Core/Data/Items/IsExternalInit.cs delete mode 100644 src/Files.Core/Files.Core.csproj delete mode 100644 src/Files.Core/GlobalUsings.cs diff --git a/Files.sln b/Files.sln index 7984206f36a8..9ee97dfae037 100644 --- a/Files.sln +++ b/Files.sln @@ -14,8 +14,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Files.Shared", "src\Files.S EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Files.App.Storage", "src\Files.App.Storage\Files.App.Storage.csproj", "{B8051E11-5BF2-49F7-A7C8-E3820DBB8209}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Files.Core", "src\Files.Core\Files.Core.csproj", "{74704E22-9A09-4675-AE35-7896DE81EC4A}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Files.Core.Storage", "src\Files.Core.Storage\Files.Core.Storage.csproj", "{53371D77-7AC1-4975-9A2A-5E0EB0B37CE3}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Files.App.BackgroundTasks", "src\Files.App.BackgroundTasks\Files.App.BackgroundTasks.csproj", "{BB1DA0B0-4E5B-4336-961E-DF389482C094}" @@ -116,36 +114,6 @@ Global {B8051E11-5BF2-49F7-A7C8-E3820DBB8209}.Store|x64.Build.0 = Store|x64 {B8051E11-5BF2-49F7-A7C8-E3820DBB8209}.Store|x86.ActiveCfg = Store|x86 {B8051E11-5BF2-49F7-A7C8-E3820DBB8209}.Store|x86.Build.0 = Store|x86 - {74704E22-9A09-4675-AE35-7896DE81EC4A}.Debug|arm64.ActiveCfg = Debug|arm64 - {74704E22-9A09-4675-AE35-7896DE81EC4A}.Debug|arm64.Build.0 = Debug|arm64 - {74704E22-9A09-4675-AE35-7896DE81EC4A}.Debug|x64.ActiveCfg = Debug|x64 - {74704E22-9A09-4675-AE35-7896DE81EC4A}.Debug|x64.Build.0 = Debug|x64 - {74704E22-9A09-4675-AE35-7896DE81EC4A}.Debug|x86.ActiveCfg = Debug|x86 - {74704E22-9A09-4675-AE35-7896DE81EC4A}.Debug|x86.Build.0 = Debug|x86 - {74704E22-9A09-4675-AE35-7896DE81EC4A}.Preview|arm64.ActiveCfg = Preview|arm64 - {74704E22-9A09-4675-AE35-7896DE81EC4A}.Preview|arm64.Build.0 = Preview|arm64 - {74704E22-9A09-4675-AE35-7896DE81EC4A}.Preview|x64.ActiveCfg = Preview|x64 - {74704E22-9A09-4675-AE35-7896DE81EC4A}.Preview|x64.Build.0 = Preview|x64 - {74704E22-9A09-4675-AE35-7896DE81EC4A}.Preview|x86.ActiveCfg = Preview|x86 - {74704E22-9A09-4675-AE35-7896DE81EC4A}.Preview|x86.Build.0 = Preview|x86 - {74704E22-9A09-4675-AE35-7896DE81EC4A}.Release|arm64.ActiveCfg = Release|arm64 - {74704E22-9A09-4675-AE35-7896DE81EC4A}.Release|arm64.Build.0 = Release|arm64 - {74704E22-9A09-4675-AE35-7896DE81EC4A}.Release|x64.ActiveCfg = Release|x64 - {74704E22-9A09-4675-AE35-7896DE81EC4A}.Release|x64.Build.0 = Release|x64 - {74704E22-9A09-4675-AE35-7896DE81EC4A}.Release|x86.ActiveCfg = Release|x86 - {74704E22-9A09-4675-AE35-7896DE81EC4A}.Release|x86.Build.0 = Release|x86 - {74704E22-9A09-4675-AE35-7896DE81EC4A}.Stable|arm64.ActiveCfg = Stable|arm64 - {74704E22-9A09-4675-AE35-7896DE81EC4A}.Stable|arm64.Build.0 = Stable|arm64 - {74704E22-9A09-4675-AE35-7896DE81EC4A}.Stable|x64.ActiveCfg = Stable|x64 - {74704E22-9A09-4675-AE35-7896DE81EC4A}.Stable|x64.Build.0 = Stable|x64 - {74704E22-9A09-4675-AE35-7896DE81EC4A}.Stable|x86.ActiveCfg = Stable|x86 - {74704E22-9A09-4675-AE35-7896DE81EC4A}.Stable|x86.Build.0 = Stable|x86 - {74704E22-9A09-4675-AE35-7896DE81EC4A}.Store|arm64.ActiveCfg = Store|arm64 - {74704E22-9A09-4675-AE35-7896DE81EC4A}.Store|arm64.Build.0 = Store|arm64 - {74704E22-9A09-4675-AE35-7896DE81EC4A}.Store|x64.ActiveCfg = Store|x64 - {74704E22-9A09-4675-AE35-7896DE81EC4A}.Store|x64.Build.0 = Store|x64 - {74704E22-9A09-4675-AE35-7896DE81EC4A}.Store|x86.ActiveCfg = Store|x86 - {74704E22-9A09-4675-AE35-7896DE81EC4A}.Store|x86.Build.0 = Store|x86 {53371D77-7AC1-4975-9A2A-5E0EB0B37CE3}.Debug|arm64.ActiveCfg = Debug|arm64 {53371D77-7AC1-4975-9A2A-5E0EB0B37CE3}.Debug|arm64.Build.0 = Debug|arm64 {53371D77-7AC1-4975-9A2A-5E0EB0B37CE3}.Debug|x64.ActiveCfg = Debug|x64 @@ -452,7 +420,6 @@ Global {9F36C2AD-005D-4EA5-A1F1-6BC42773FC85} = {A74DCE98-A744-4D71-A2B1-7EE4FED0936B} {94F77692-D47C-48D8-A1A7-645192EF38A4} = {9F36C2AD-005D-4EA5-A1F1-6BC42773FC85} {B8051E11-5BF2-49F7-A7C8-E3820DBB8209} = {A188C26B-E731-4E0B-9D17-D21CEBD9B43F} - {74704E22-9A09-4675-AE35-7896DE81EC4A} = {9F36C2AD-005D-4EA5-A1F1-6BC42773FC85} {53371D77-7AC1-4975-9A2A-5E0EB0B37CE3} = {9F36C2AD-005D-4EA5-A1F1-6BC42773FC85} {BB1DA0B0-4E5B-4336-961E-DF389482C094} = {A188C26B-E731-4E0B-9D17-D21CEBD9B43F} {4ED01D20-8529-4DEF-8C1B-4E31031AE7E0} = {481DE2EA-E6CE-4A9C-A220-3B543B95AAA1} diff --git a/src/Files.App (Package)/Package.appxmanifest b/src/Files.App (Package)/Package.appxmanifest index 60f580963cc8..74b8ea2fe6df 100644 --- a/src/Files.App (Package)/Package.appxmanifest +++ b/src/Files.App (Package)/Package.appxmanifest @@ -166,6 +166,15 @@ Files.App.Server\Files.App.Server.exe singleInstance + + + + + + + + + diff --git a/src/Files.App.BackgroundTasks/Files.App.BackgroundTasks.csproj b/src/Files.App.BackgroundTasks/Files.App.BackgroundTasks.csproj index 1eb7aa9b3862..297b1bc9976c 100644 --- a/src/Files.App.BackgroundTasks/Files.App.BackgroundTasks.csproj +++ b/src/Files.App.BackgroundTasks/Files.App.BackgroundTasks.csproj @@ -26,7 +26,7 @@ - + diff --git a/src/Files.App.Launcher/Files.App.Launcher.vcxproj b/src/Files.App.Launcher/Files.App.Launcher.vcxproj index 888587a53416..d4a97cd06ccb 100644 --- a/src/Files.App.Launcher/Files.App.Launcher.vcxproj +++ b/src/Files.App.Launcher/Files.App.Launcher.vcxproj @@ -1,7 +1,7 @@ - + Debug @@ -321,21 +321,23 @@ + + - - + + This project references a NuGet package that is not on this computer. To download those packages, use Restore NuGet Packages. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - + + + \ No newline at end of file diff --git a/src/Files.App.Launcher/packages.config b/src/Files.App.Launcher/packages.config index 78f22041613c..1b706ca9a10d 100644 --- a/src/Files.App.Launcher/packages.config +++ b/src/Files.App.Launcher/packages.config @@ -1,5 +1,5 @@  - - + + \ No newline at end of file diff --git a/src/Files.App.Server/AppInstanceMonitor.cs b/src/Files.App.Server/AppInstanceMonitor.cs index 2eb32dde3a31..c1b08fe6f1e8 100644 --- a/src/Files.App.Server/AppInstanceMonitor.cs +++ b/src/Files.App.Server/AppInstanceMonitor.cs @@ -1,4 +1,7 @@ -using System.Diagnostics; +// Copyright (c) 2024 Files Community +// Licensed under the MIT License. See the LICENSE. + +using System.Diagnostics; namespace Files.App.Server; diff --git a/src/Files.App.Server/Data/ColumnPreferences.cs b/src/Files.App.Server/Data/ColumnPreferences.cs new file mode 100644 index 000000000000..93cb5e32150d --- /dev/null +++ b/src/Files.App.Server/Data/ColumnPreferences.cs @@ -0,0 +1,24 @@ +// Copyright (c) 2024 Files Community +// Licensed under the MIT License. See the LICENSE. + +namespace Files.App.Server.Data +{ + public sealed class ColumnPreferences + { + public ColumnPreferencesItem GitStatusColumn { get; set; } = new(); + public ColumnPreferencesItem GitLastCommitDateColumn { get; set; } = new(); + public ColumnPreferencesItem GitLastCommitMessageColumn { get; set; } = new(); + public ColumnPreferencesItem GitCommitAuthorColumn { get; set; } = new(); + public ColumnPreferencesItem GitLastCommitShaColumn { get; set; } = new(); + public ColumnPreferencesItem TagColumn { get; set; } = new(); + public ColumnPreferencesItem NameColumn { get; set; } = new(); + public ColumnPreferencesItem StatusColumn { get; set; } = new(); + public ColumnPreferencesItem DateModifiedColumn { get; set; } = new(); + public ColumnPreferencesItem PathColumn { get; set; } = new(); + public ColumnPreferencesItem OriginalPathColumn { get; set; } = new(); + public ColumnPreferencesItem ItemTypeColumn { get; set; } = new(); + public ColumnPreferencesItem DateDeletedColumn { get; set; } = new(); + public ColumnPreferencesItem DateCreatedColumn { get; set; } = new(); + public ColumnPreferencesItem SizeColumn { get; set; } = new(); + } +} diff --git a/src/Files.App.Server/Data/ColumnPreferencesItem.cs b/src/Files.App.Server/Data/ColumnPreferencesItem.cs new file mode 100644 index 000000000000..7dd35c8426cc --- /dev/null +++ b/src/Files.App.Server/Data/ColumnPreferencesItem.cs @@ -0,0 +1,12 @@ +// Copyright (c) 2024 Files Community +// Licensed under the MIT License. See the LICENSE. + +namespace Files.App.Server.Data +{ + public sealed class ColumnPreferencesItem + { + public double UserLengthPixels { get; set; } + public double NormalMaxLength { get; set; } = 800; + public bool UserCollapsed { get; set; } + } +} diff --git a/src/Files.Core/Data/Enums/FolderLayoutModes.cs b/src/Files.App.Server/Data/Enums/FolderLayoutModes.cs similarity index 93% rename from src/Files.Core/Data/Enums/FolderLayoutModes.cs rename to src/Files.App.Server/Data/Enums/FolderLayoutModes.cs index da8ec49448a4..e3049846a9e2 100644 --- a/src/Files.Core/Data/Enums/FolderLayoutModes.cs +++ b/src/Files.App.Server/Data/Enums/FolderLayoutModes.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Data.Enums +namespace Files.App.Server.Data.Enums { public enum FolderLayoutModes { diff --git a/src/Files.Core/Data/Enums/GroupByDateUnit.cs b/src/Files.App.Server/Data/Enums/GroupByDateUnit.cs similarity index 82% rename from src/Files.Core/Data/Enums/GroupByDateUnit.cs rename to src/Files.App.Server/Data/Enums/GroupByDateUnit.cs index 8800b81e2077..21dfb84bfa3d 100644 --- a/src/Files.Core/Data/Enums/GroupByDateUnit.cs +++ b/src/Files.App.Server/Data/Enums/GroupByDateUnit.cs @@ -1,9 +1,9 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Data.Enums +namespace Files.App.Server.Data.Enums { - public enum GroupByDateUnit : byte + public enum GroupByDateUnit { /// /// Group items by year. diff --git a/src/Files.Core/Data/Enums/GroupOption.cs b/src/Files.App.Server/Data/Enums/GroupOption.cs similarity index 80% rename from src/Files.Core/Data/Enums/GroupOption.cs rename to src/Files.App.Server/Data/Enums/GroupOption.cs index 2763762dfb19..eb8fcfd8a12b 100644 --- a/src/Files.Core/Data/Enums/GroupOption.cs +++ b/src/Files.App.Server/Data/Enums/GroupOption.cs @@ -1,39 +1,39 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Data.Enums +namespace Files.App.Server.Data.Enums { - public enum GroupOption : byte + public enum GroupOption { /// /// No grouping. /// - None, + None = 0, /// /// Group by name /// - Name, + Name = 1, /// /// Group by date modified. /// - DateModified, + DateModified = 2, /// /// Group by date created. /// - DateCreated, + DateCreated = 3, /// /// Group by size. /// - Size, + Size = 4, /// /// Group by file type. /// - FileType, + FileType = 5, /// /// Group by sync status. @@ -41,12 +41,12 @@ public enum GroupOption : byte /// /// Preserved for cloud drives. /// - SyncStatus, + SyncStatus = 6, /// /// Group by file tags. /// - FileTag, + FileTag = 7, /// /// Group by original folder. @@ -54,7 +54,7 @@ public enum GroupOption : byte /// /// Preserved for recycle bin. /// - OriginalFolder, + OriginalFolder = 8, /// /// Group by date deleted. @@ -62,7 +62,7 @@ public enum GroupOption : byte /// /// Preserved for recycle bin. /// - DateDeleted, + DateDeleted = 9, /// /// Group by folder path. @@ -70,6 +70,6 @@ public enum GroupOption : byte /// /// Preserved for libraries. /// - FolderPath, + FolderPath = 10, } } diff --git a/src/Files.Core/Data/Enums/SortDirection.cs b/src/Files.App.Server/Data/Enums/SortDirection.cs similarity index 85% rename from src/Files.Core/Data/Enums/SortDirection.cs rename to src/Files.App.Server/Data/Enums/SortDirection.cs index 843c8b82f425..58d4035ffbe6 100644 --- a/src/Files.Core/Data/Enums/SortDirection.cs +++ b/src/Files.App.Server/Data/Enums/SortDirection.cs @@ -1,12 +1,12 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Data.Enums +namespace Files.App.Server.Data.Enums { /// /// SortDirection is used instead of the CommunityToolkit equivalent because it is tied to the model /// - public enum SortDirection : byte + public enum SortDirection { /// /// Sort in ascending order. diff --git a/src/Files.Core/Data/Enums/SortOption.cs b/src/Files.App.Server/Data/Enums/SortOption.cs similarity index 80% rename from src/Files.Core/Data/Enums/SortOption.cs rename to src/Files.App.Server/Data/Enums/SortOption.cs index b6ccefa36184..240d468b63c4 100644 --- a/src/Files.Core/Data/Enums/SortOption.cs +++ b/src/Files.App.Server/Data/Enums/SortOption.cs @@ -1,34 +1,34 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Data.Enums +namespace Files.App.Server.Data.Enums { - public enum SortOption : byte + public enum SortOption { /// /// Sort by name. /// - Name, + Name = 0, /// /// Sort by date modified. /// - DateModified, + DateModified = 1, /// /// Sort by date created. /// - DateCreated, + DateCreated = 2, /// /// Sort by size. /// - Size, + Size = 3, /// /// Sort by file type. /// - FileType, + FileType = 4, /// /// Sort by sync status. @@ -36,12 +36,12 @@ public enum SortOption : byte /// /// Reserved for cloud drives. /// - SyncStatus, + SyncStatus = 5, /// /// Sort by file tags. /// - FileTag, + FileTag = 6, /// /// Sort by original folder. @@ -49,7 +49,7 @@ public enum SortOption : byte /// /// Preserved for recycle bin. /// - OriginalFolder, + OriginalFolder = 7, /// /// Sort by date deleted. @@ -57,7 +57,7 @@ public enum SortOption : byte /// /// Preserved for recycle bin. /// - DateDeleted, + DateDeleted = 8, /// /// Sort by path. @@ -65,6 +65,6 @@ public enum SortOption : byte /// /// Preserved for search results. /// - Path + Path = 9 } } diff --git a/src/Files.App.Server/Data/LayoutPreferences.cs b/src/Files.App.Server/Data/LayoutPreferences.cs new file mode 100644 index 000000000000..7b3eff40f426 --- /dev/null +++ b/src/Files.App.Server/Data/LayoutPreferences.cs @@ -0,0 +1,17 @@ +// Copyright (c) 2024 Files Community +// Licensed under the MIT License. See the LICENSE. + +using LiteDB; + +namespace Files.App.Server.Data +{ + public sealed class LayoutPreferences + { + [BsonId] + public int Id { get; set; } + public ulong? Frn { get; set; } + public string FilePath { get; set; } = string.Empty; + + public LayoutPreferencesItem LayoutPreferencesManager { get; set; } = new(); + } +} diff --git a/src/Files.App.Server/Data/LayoutPreferencesItem.cs b/src/Files.App.Server/Data/LayoutPreferencesItem.cs new file mode 100644 index 000000000000..98e4f7dfec00 --- /dev/null +++ b/src/Files.App.Server/Data/LayoutPreferencesItem.cs @@ -0,0 +1,25 @@ +// Copyright (c) 2024 Files Community +// Licensed under the MIT License. See the LICENSE. + +using Files.App.Server.Data.Enums; + +namespace Files.App.Server.Data +{ + public sealed class LayoutPreferencesItem + { + public ColumnPreferences ColumnsViewModel { get; set; } = new(); + + public bool SortDirectoriesAlongsideFiles { get; set; } + public bool SortFilesFirst { get; set; } + public bool IsAdaptiveLayoutOverridden { get; set; } + + public FolderLayoutModes LayoutMode { get; set; } + + public SortOption DirectorySortOption { get; set; } + public SortDirection DirectorySortDirection { get; set; } + public SortDirection DirectoryGroupDirection { get; set; } + + public GroupOption DirectoryGroupOption { get; set; } + public GroupByDateUnit DirectoryGroupByDateUnit { get; set; } + } +} diff --git a/src/Files.App.Server/Data/TaggedFile.cs b/src/Files.App.Server/Data/TaggedFile.cs new file mode 100644 index 000000000000..3e60b534a9be --- /dev/null +++ b/src/Files.App.Server/Data/TaggedFile.cs @@ -0,0 +1,15 @@ +// Copyright (c) 2024 Files Community +// Licensed under the MIT License. See the LICENSE. + +using LiteDB; + +namespace Files.App.Server.Data +{ + public sealed class TaggedFile + { + [BsonId] public int Id { get; set; } + public ulong? Frn { get; set; } + public string FilePath { get; set; } = string.Empty; + public string[] Tags { get; set; } = []; + } +} diff --git a/src/Files.Core/Data/Items/FileTagsDb.cs b/src/Files.App.Server/Database/FileTagsDatabase.cs similarity index 60% rename from src/Files.Core/Data/Items/FileTagsDb.cs rename to src/Files.App.Server/Database/FileTagsDatabase.cs index 58eb6180e288..19226cd2a15b 100644 --- a/src/Files.Core/Data/Items/FileTagsDb.cs +++ b/src/Files.App.Server/Database/FileTagsDatabase.cs @@ -1,40 +1,54 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. +using Files.App.Server.Data; using Files.Shared.Extensions; using LiteDB; +using System.Runtime.InteropServices.WindowsRuntime; using System.Text; -using IO = System.IO; +using Windows.Foundation.Metadata; +using Windows.Storage; -namespace Files.Core.Data.Items +namespace Files.App.Server.Database { - public sealed class FileTagsDb : IDisposable + public sealed class FileTagsDatabase { - private readonly LiteDatabase db; + private static LiteDatabase _database = default!; + private static readonly object _lockObject = new(); private const string TaggedFiles = "taggedfiles"; - public FileTagsDb(string connection, bool shared = false) - { - SafetyExtensions.IgnoreExceptions(() => CheckDbVersion(connection)); + public static string FileTagsDbPath + => Path.Combine(ApplicationData.Current.LocalFolder.Path, "filetags.db"); - db = new LiteDatabase(new ConnectionString(connection) + public FileTagsDatabase() + { + lock (_lockObject) { - Mode = shared ? LiteDB.FileMode.Shared : LiteDB.FileMode.Exclusive - }); + if (_database is null) + { + SafetyExtensions.IgnoreExceptions(() => CheckDbVersion(FileTagsDbPath)); + + _database = new LiteDatabase(new ConnectionString(FileTagsDbPath) + { + Connection = ConnectionType.Direct, + Upgrade = true + }); - UpdateDb(); + UpdateDb(); + } + } } - public void SetTags(string filePath, ulong? frn, string[]? tags) + public void SetTags(string filePath, ulong? frn, [ReadOnlyArray] string[] tags) { // Get a collection (or create, if doesn't exist) - var col = db.GetCollection(TaggedFiles); + var col = _database.GetCollection(TaggedFiles); - var tmp = _FindTag(filePath, frn); + var tmp = FindTag(filePath, frn); if (tmp is null) { - if (tags is not null && tags.Any()) + if (tags.Any()) { // Insert new tagged file (Id will be auto-incremented) var newTag = new TaggedFile() @@ -50,7 +64,7 @@ public void SetTags(string filePath, ulong? frn, string[]? tags) } else { - if (tags is not null && tags.Any()) + if (tags.Any()) { // Update file tag tmp.Tags = tags; @@ -64,10 +78,10 @@ public void SetTags(string filePath, ulong? frn, string[]? tags) } } - private TaggedFile? _FindTag(string? filePath = null, ulong? frn = null) + private TaggedFile? FindTag(string? filePath, ulong? frn) { // Get a collection (or create, if doesn't exist) - var col = db.GetCollection(TaggedFiles); + var col = _database.GetCollection(TaggedFiles); if (filePath is not null) { @@ -104,10 +118,11 @@ public void SetTags(string filePath, ulong? frn, string[]? tags) return null; } - public void UpdateTag(string oldFilePath, ulong? frn = null, string? newFilePath = null) + [DefaultOverload] + public void UpdateTag(string oldFilePath, ulong? frn, string? newFilePath) { // Get a collection (or create, if doesn't exist) - var col = db.GetCollection(TaggedFiles); + var col = _database.GetCollection(TaggedFiles); var tmp = col.FindOne(x => x.FilePath == oldFilePath); if (tmp is not null) { @@ -125,10 +140,11 @@ public void UpdateTag(string oldFilePath, ulong? frn = null, string? newFilePath } } - public void UpdateTag(ulong oldFrn, ulong? frn = null, string? newFilePath = null) + [Overload("UpdateTagByFrn")] + public void UpdateTag(ulong oldFrn, ulong? frn, string? newFilePath) { // Get a collection (or create, if doesn't exist) - var col = db.GetCollection(TaggedFiles); + var col = _database.GetCollection(TaggedFiles); var tmp = col.FindOne(x => x.Frn == oldFrn); if (tmp is not null) { @@ -146,60 +162,50 @@ public void UpdateTag(ulong oldFrn, ulong? frn = null, string? newFilePath = nul } } - public string[]? GetTags(string? filePath = null, ulong? frn = null) + public string[]? GetTags(string? filePath, ulong? frn) { - return _FindTag(filePath, frn)?.Tags; + return FindTag(filePath, frn)?.Tags; } public IEnumerable GetAll() { - var col = db.GetCollection(TaggedFiles); + var col = _database.GetCollection(TaggedFiles); return col.FindAll(); } public IEnumerable GetAllUnderPath(string folderPath) { - var col = db.GetCollection(TaggedFiles); + var col = _database.GetCollection(TaggedFiles); if (string.IsNullOrEmpty(folderPath)) return col.FindAll(); return col.Find(x => x.FilePath.StartsWith(folderPath, StringComparison.OrdinalIgnoreCase)); } - ~FileTagsDb() - { - Dispose(); - } - - public void Dispose() - { - db.Dispose(); - } - public void Import(string json) { var dataValues = JsonSerializer.DeserializeArray(json); - var col = db.GetCollection(TaggedFiles); - col.Delete(Query.All()); + var col = _database.GetCollection(TaggedFiles); + col.DeleteAll(); col.InsertBulk(dataValues.Select(x => x.AsDocument)); } public string Export() { - return JsonSerializer.Serialize(new BsonArray(db.GetCollection(TaggedFiles).FindAll())); + return JsonSerializer.Serialize(new BsonArray(_database.GetCollection(TaggedFiles).FindAll())); } private void UpdateDb() { - if (db.Engine.UserVersion == 0) + if (_database.UserVersion == 0) { - var col = db.GetCollection(TaggedFiles); + var col = _database.GetCollection(TaggedFiles); foreach (var doc in col.FindAll()) { doc["Tags"] = new BsonValue(new[] { doc["Tag"].AsString }); doc.Remove("Tags"); col.Update(doc); } - db.Engine.UserVersion = 1; + _database.UserVersion = 1; } } @@ -207,7 +213,7 @@ private void UpdateDb() private void CheckDbVersion(string filename) { var buffer = new byte[8192 * 2]; - using (var stream = new IO.FileStream(filename, IO.FileMode.Open, IO.FileAccess.Read, IO.FileShare.ReadWrite)) + using (var stream = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { // read first 16k stream.Read(buffer, 0, buffer.Length); @@ -219,15 +225,7 @@ private void CheckDbVersion(string filename) return; // version 4.1.4 } } - IO.File.Delete(filename); // recreate DB with correct version - } - - public sealed class TaggedFile - { - [BsonId] public int Id { get; set; } - public ulong? Frn { get; set; } - public string FilePath { get; set; } = string.Empty; - public string[] Tags { get; set; } = Array.Empty(); + File.Delete(filename); // recreate DB with correct version } } } diff --git a/src/Files.App.Server/Database/LayoutPreferencesDatabase.cs b/src/Files.App.Server/Database/LayoutPreferencesDatabase.cs new file mode 100644 index 000000000000..ba7ad974feed --- /dev/null +++ b/src/Files.App.Server/Database/LayoutPreferencesDatabase.cs @@ -0,0 +1,164 @@ +// Copyright (c) 2024 Files Community +// Licensed under the MIT License. See the LICENSE. + +using Files.App.Server.Data; +using LiteDB; +using Windows.Storage; + +namespace Files.App.Server.Database +{ + public sealed class LayoutPreferencesDatabase + { + private static LiteDatabase _database = default!; + private static readonly object _lockObject = new(); + + private const string LayoutPreferences = "layoutprefs"; + + public static string LayoutSettingsDbPath + => Path.Combine(ApplicationData.Current.LocalFolder.Path, "user_settings.db"); + + public LayoutPreferencesDatabase() + { + lock (_lockObject) + { + _database ??= new( + new ConnectionString(LayoutSettingsDbPath) + { + Connection = ConnectionType.Direct, + Upgrade = true, + }); + } + } + + public LayoutPreferencesItem? GetPreferences(string? filePath, ulong? frn) + { + return FindPreferences(filePath, frn)?.LayoutPreferencesManager; + } + + public void SetPreferences(string filePath, ulong? frn, LayoutPreferencesItem? preferencesItem) + { + // Get a collection (or create, if doesn't exist) + var col = _database.GetCollection(LayoutPreferences); + + var tmp = FindPreferences(filePath, frn); + + if (tmp is null) + { + if (preferencesItem is not null) + { + // Insert new tagged file (Id will be auto-incremented) + var newPref = new LayoutPreferences() + { + FilePath = filePath, + Frn = frn, + LayoutPreferencesManager = preferencesItem + }; + + col.Insert(newPref); + col.EnsureIndex(x => x.Frn); + col.EnsureIndex(x => x.FilePath); + } + } + else + { + if (preferencesItem is not null) + { + // Update file tag + tmp.LayoutPreferencesManager = preferencesItem; + col.Update(tmp); + } + else + { + // Remove file tag + col.Delete(tmp.Id); + } + } + } + + public void ResetAll(LayoutPreferencesFilterPredicate? predicate) + { + var col = _database.GetCollection(LayoutPreferences); + + if (predicate is null) + { + col.DeleteAll(); + } + else + { + col.DeleteMany(x => predicate(x)); + } + } + + public void ApplyToAll(LayoutPreferencesUpdateAction updateAction, LayoutPreferencesFilterPredicate? predicate) + { + var col = _database.GetCollection(LayoutPreferences); + + var allDocs = predicate is null ? col.FindAll() : col.Find(x => predicate(x)); + + foreach (var doc in allDocs) + { + updateAction(doc); + } + + col.Update(allDocs); + } + + public void Import(string json) + { + var dataValues = JsonSerializer.DeserializeArray(json); + + var col = _database.GetCollection(LayoutPreferences); + + col.DeleteAll(); + col.InsertBulk(dataValues.Select(x => x.AsDocument)); + } + + public string Export() + { + return JsonSerializer.Serialize(new BsonArray(_database.GetCollection(LayoutPreferences).FindAll())); + } + + private LayoutPreferences? FindPreferences(string? filePath, ulong? frn) + { + // Get a collection (or create, if doesn't exist) + var col = _database.GetCollection(LayoutPreferences); + + if (filePath is not null) + { + var tmp = col.FindOne(x => x.FilePath == filePath); + if (tmp is not null) + { + if (frn is not null) + { + // Keep entry updated + tmp.Frn = frn; + col.Update(tmp); + } + + return tmp; + } + } + + if (frn is not null) + { + var tmp = col.FindOne(x => x.Frn == frn); + if (tmp is not null) + { + if (filePath is not null) + { + // Keep entry updated + tmp.FilePath = filePath; + col.Update(tmp); + } + + return tmp; + } + } + + return null; + } + } + + public delegate bool LayoutPreferencesFilterPredicate(LayoutPreferences preference); + public delegate void LayoutPreferencesUpdateAction(LayoutPreferences preference); +} diff --git a/src/Files.App.Server/Files.App.Server.csproj b/src/Files.App.Server/Files.App.Server.csproj index 7fa0396657c0..721f4af32be0 100644 --- a/src/Files.App.Server/Files.App.Server.csproj +++ b/src/Files.App.Server/Files.App.Server.csproj @@ -25,6 +25,7 @@ True False True + true @@ -38,7 +39,8 @@ - + + diff --git a/src/Files.App.Server/Helpers.cs b/src/Files.App.Server/Helpers.cs index 6352487e6168..0bdc20a01fa3 100644 --- a/src/Files.App.Server/Helpers.cs +++ b/src/Files.App.Server/Helpers.cs @@ -1,4 +1,7 @@ -using System.Runtime.CompilerServices; +// Copyright (c) 2024 Files Community +// Licensed under the MIT License. See the LICENSE. + +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using WinRT; diff --git a/src/Files.App.Server/Program.cs b/src/Files.App.Server/Program.cs index 796857a492d9..629dd526431f 100644 --- a/src/Files.App.Server/Program.cs +++ b/src/Files.App.Server/Program.cs @@ -1,4 +1,7 @@ -using Files.Shared.Helpers; +// Copyright (c) 2024 Files Community +// Licensed under the MIT License. See the LICENSE. + +using Files.Shared.Helpers; using System.Runtime.InteropServices; using Windows.Win32; using Windows.Win32.Foundation; diff --git a/src/Files.App/Actions/Display/GroupAction.cs b/src/Files.App/Actions/Display/GroupAction.cs index 02887e25fe66..f9be3723f3a9 100644 --- a/src/Files.App/Actions/Display/GroupAction.cs +++ b/src/Files.App/Actions/Display/GroupAction.cs @@ -1,6 +1,8 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. +using Files.App.Server.Data.Enums; + namespace Files.App.Actions { internal sealed class GroupByNoneAction : GroupByAction diff --git a/src/Files.App/Actions/Display/SortAction.cs b/src/Files.App/Actions/Display/SortAction.cs index 857a91d72073..a60d5b8c2ffc 100644 --- a/src/Files.App/Actions/Display/SortAction.cs +++ b/src/Files.App/Actions/Display/SortAction.cs @@ -1,6 +1,8 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. +using Files.App.Server.Data.Enums; + namespace Files.App.Actions { internal sealed class SortByNameAction : SortByAction diff --git a/src/Files.Core/Data/AppModels/BaseDatabaseModel.cs b/src/Files.App/Data/AppModels/BaseDatabaseModel.cs similarity index 96% rename from src/Files.Core/Data/AppModels/BaseDatabaseModel.cs rename to src/Files.App/Data/AppModels/BaseDatabaseModel.cs index 468aa9fbfa95..3cf6e3c86dfe 100644 --- a/src/Files.Core/Data/AppModels/BaseDatabaseModel.cs +++ b/src/Files.App/Data/AppModels/BaseDatabaseModel.cs @@ -2,7 +2,7 @@ using System.Collections.Concurrent; using System.IO; -namespace Files.Core.Data.AppModels +namespace Files.App.Data.AppModels { /// /// Represents a dictionary-based database model. diff --git a/src/Files.Core/Data/AppModels/SingleFileDatabaseModel.cs b/src/Files.App/Data/AppModels/SingleFileDatabaseModel.cs similarity index 99% rename from src/Files.Core/Data/AppModels/SingleFileDatabaseModel.cs rename to src/Files.App/Data/AppModels/SingleFileDatabaseModel.cs index cadbf664310f..29d4ed93c17d 100644 --- a/src/Files.Core/Data/AppModels/SingleFileDatabaseModel.cs +++ b/src/Files.App/Data/AppModels/SingleFileDatabaseModel.cs @@ -5,7 +5,7 @@ using Files.Shared.Utils; using System.IO; -namespace Files.Core.Data.AppModels +namespace Files.App.Data.AppModels { /// public sealed class SingleFileDatabaseModel : BaseDatabaseModel diff --git a/src/Files.App/Data/Contexts/DisplayPage/DisplayPageContext.cs b/src/Files.App/Data/Contexts/DisplayPage/DisplayPageContext.cs index fdf0449b11df..ae702eefdfcb 100644 --- a/src/Files.App/Data/Contexts/DisplayPage/DisplayPageContext.cs +++ b/src/Files.App/Data/Contexts/DisplayPage/DisplayPageContext.cs @@ -1,6 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. +using Files.App.Server.Data.Enums; using static Files.App.Constants; namespace Files.App.Data.Contexts diff --git a/src/Files.App/Data/Contexts/DisplayPage/IDisplayPageContext.cs b/src/Files.App/Data/Contexts/DisplayPage/IDisplayPageContext.cs index 1bf0a919891f..d549e15d6307 100644 --- a/src/Files.App/Data/Contexts/DisplayPage/IDisplayPageContext.cs +++ b/src/Files.App/Data/Contexts/DisplayPage/IDisplayPageContext.cs @@ -1,6 +1,8 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. +using Files.App.Server.Data.Enums; + namespace Files.App.Data.Contexts { public interface IDisplayPageContext : INotifyPropertyChanging, INotifyPropertyChanged diff --git a/src/Files.Core/Data/Enums/AddItemDialogItemType.cs b/src/Files.App/Data/Enums/AddItemDialogItemType.cs similarity index 94% rename from src/Files.Core/Data/Enums/AddItemDialogItemType.cs rename to src/Files.App/Data/Enums/AddItemDialogItemType.cs index 642f3d64fa77..47889ded9def 100644 --- a/src/Files.Core/Data/Enums/AddItemDialogItemType.cs +++ b/src/Files.App/Data/Enums/AddItemDialogItemType.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Data.Enums +namespace Files.App.Data.Enums { /// /// Defines constants that specify item type for item creation dialog. diff --git a/src/Files.Core/Data/Enums/AppEnvironment.cs b/src/Files.App/Data/Enums/AppEnvironment.cs similarity index 95% rename from src/Files.Core/Data/Enums/AppEnvironment.cs rename to src/Files.App/Data/Enums/AppEnvironment.cs index b5b787a22895..8ee11c048271 100644 --- a/src/Files.Core/Data/Enums/AppEnvironment.cs +++ b/src/Files.App/Data/Enums/AppEnvironment.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Data.Enums +namespace Files.App.Data.Enums { /// /// Defines constants that specify application distribution type. diff --git a/src/Files.Core/Data/Enums/ArchiveCompressionLevels.cs b/src/Files.App/Data/Enums/ArchiveCompressionLevels.cs similarity index 95% rename from src/Files.Core/Data/Enums/ArchiveCompressionLevels.cs rename to src/Files.App/Data/Enums/ArchiveCompressionLevels.cs index bedf72fd783e..74c695bf7784 100644 --- a/src/Files.Core/Data/Enums/ArchiveCompressionLevels.cs +++ b/src/Files.App/Data/Enums/ArchiveCompressionLevels.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Data.Enums +namespace Files.App.Data.Enums { /// /// Defines constants that specify archive compression level. diff --git a/src/Files.Core/Data/Enums/ArchiveFormats.cs b/src/Files.App/Data/Enums/ArchiveFormats.cs similarity index 93% rename from src/Files.Core/Data/Enums/ArchiveFormats.cs rename to src/Files.App/Data/Enums/ArchiveFormats.cs index 2ecda4ba523e..5a354a7915f5 100644 --- a/src/Files.Core/Data/Enums/ArchiveFormats.cs +++ b/src/Files.App/Data/Enums/ArchiveFormats.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Data.Enums +namespace Files.App.Data.Enums { /// /// Defines constants that specify archive format. diff --git a/src/Files.Core/Data/Enums/ArchiveSplittingSizes.cs b/src/Files.App/Data/Enums/ArchiveSplittingSizes.cs similarity index 97% rename from src/Files.Core/Data/Enums/ArchiveSplittingSizes.cs rename to src/Files.App/Data/Enums/ArchiveSplittingSizes.cs index 9ac6f9b7c6f5..3c2386cf9fb4 100644 --- a/src/Files.Core/Data/Enums/ArchiveSplittingSizes.cs +++ b/src/Files.App/Data/Enums/ArchiveSplittingSizes.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Data.Enums +namespace Files.App.Data.Enums { /// /// Defines constants that specify 7zip archive splitting size. diff --git a/src/Files.Core/Data/Enums/BackdropMaterialType.cs b/src/Files.App/Data/Enums/BackdropMaterialType.cs similarity index 94% rename from src/Files.Core/Data/Enums/BackdropMaterialType.cs rename to src/Files.App/Data/Enums/BackdropMaterialType.cs index 3e69f1c66ecc..f0de61e05482 100644 --- a/src/Files.Core/Data/Enums/BackdropMaterialType.cs +++ b/src/Files.App/Data/Enums/BackdropMaterialType.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Data.Enums +namespace Files.App.Data.Enums { /// /// Defines constants that specify application backdrop material type on Windows. diff --git a/src/Files.Core/Data/Enums/ColumnsViewSizeKind.cs b/src/Files.App/Data/Enums/ColumnsViewSizeKind.cs similarity index 95% rename from src/Files.Core/Data/Enums/ColumnsViewSizeKind.cs rename to src/Files.App/Data/Enums/ColumnsViewSizeKind.cs index 54f75317995c..c9b60990f54b 100644 --- a/src/Files.Core/Data/Enums/ColumnsViewSizeKind.cs +++ b/src/Files.App/Data/Enums/ColumnsViewSizeKind.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Data.Enums +namespace Files.App.Data.Enums { /// /// Defines constants that specify the size in the Columns View layout. diff --git a/src/Files.Core/Data/Enums/ContextMenuFlyoutItemType.cs b/src/Files.App/Data/Enums/ContextMenuFlyoutItemType.cs similarity index 94% rename from src/Files.Core/Data/Enums/ContextMenuFlyoutItemType.cs rename to src/Files.App/Data/Enums/ContextMenuFlyoutItemType.cs index 2a29edac69a6..96d9f203a096 100644 --- a/src/Files.Core/Data/Enums/ContextMenuFlyoutItemType.cs +++ b/src/Files.App/Data/Enums/ContextMenuFlyoutItemType.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Data.Enums +namespace Files.App.Data.Enums { /// /// Defines constants that specify item type of ContextMenuFlyout on Windows. diff --git a/src/Files.Core/Data/Enums/CopyEngineResult.cs b/src/Files.App/Data/Enums/CopyEngineResult.cs similarity index 99% rename from src/Files.Core/Data/Enums/CopyEngineResult.cs rename to src/Files.App/Data/Enums/CopyEngineResult.cs index b049656a7eaa..4a4f637365a6 100644 --- a/src/Files.Core/Data/Enums/CopyEngineResult.cs +++ b/src/Files.App/Data/Enums/CopyEngineResult.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Data.Enums +namespace Files.App.Data.Enums { public struct CopyEngineResult { diff --git a/src/Files.Core/Data/Enums/DateTimeFormats.cs b/src/Files.App/Data/Enums/DateTimeFormats.cs similarity index 92% rename from src/Files.Core/Data/Enums/DateTimeFormats.cs rename to src/Files.App/Data/Enums/DateTimeFormats.cs index f37a3b951220..f36c54dcb0cf 100644 --- a/src/Files.Core/Data/Enums/DateTimeFormats.cs +++ b/src/Files.App/Data/Enums/DateTimeFormats.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Data.Enums +namespace Files.App.Data.Enums { public enum DateTimeFormats { diff --git a/src/Files.Core/Data/Enums/DeleteConfirmationPolicy.cs b/src/Files.App/Data/Enums/DeleteConfirmationPolicy.cs similarity index 92% rename from src/Files.Core/Data/Enums/DeleteConfirmationPolicy.cs rename to src/Files.App/Data/Enums/DeleteConfirmationPolicy.cs index 75b63eae5acb..35ed1880fb6c 100644 --- a/src/Files.Core/Data/Enums/DeleteConfirmationPolicy.cs +++ b/src/Files.App/Data/Enums/DeleteConfirmationPolicy.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Data.Enums +namespace Files.App.Data.Enums { public enum DeleteConfirmationPolicies : byte { diff --git a/src/Files.Core/Data/Enums/DetailsViewSizeKind.cs b/src/Files.App/Data/Enums/DetailsViewSizeKind.cs similarity index 95% rename from src/Files.Core/Data/Enums/DetailsViewSizeKind.cs rename to src/Files.App/Data/Enums/DetailsViewSizeKind.cs index a9c0efecb98f..b49c1cfd3dfc 100644 --- a/src/Files.Core/Data/Enums/DetailsViewSizeKind.cs +++ b/src/Files.App/Data/Enums/DetailsViewSizeKind.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Data.Enums +namespace Files.App.Data.Enums { /// /// Defines constants that specify the size in the Details View layout. diff --git a/src/Files.Core/Data/Enums/DialogResult.cs b/src/Files.App/Data/Enums/DialogResult.cs similarity index 92% rename from src/Files.Core/Data/Enums/DialogResult.cs rename to src/Files.App/Data/Enums/DialogResult.cs index 40cea2d3a27e..960a122079d1 100644 --- a/src/Files.Core/Data/Enums/DialogResult.cs +++ b/src/Files.App/Data/Enums/DialogResult.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Data.Enums +namespace Files.App.Data.Enums { public enum DialogResult { diff --git a/src/Files.Core/Data/Enums/DynamicDialogButtons.cs b/src/Files.App/Data/Enums/DynamicDialogButtons.cs similarity index 93% rename from src/Files.Core/Data/Enums/DynamicDialogButtons.cs rename to src/Files.App/Data/Enums/DynamicDialogButtons.cs index 94bb59323c9f..fa0219041e59 100644 --- a/src/Files.Core/Data/Enums/DynamicDialogButtons.cs +++ b/src/Files.App/Data/Enums/DynamicDialogButtons.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Data.Enums +namespace Files.App.Data.Enums { public enum DynamicDialogButtons { diff --git a/src/Files.Core/Data/Enums/DynamicDialogResult.cs b/src/Files.App/Data/Enums/DynamicDialogResult.cs similarity index 92% rename from src/Files.Core/Data/Enums/DynamicDialogResult.cs rename to src/Files.App/Data/Enums/DynamicDialogResult.cs index d1af23004eec..9ca0e622fe72 100644 --- a/src/Files.Core/Data/Enums/DynamicDialogResult.cs +++ b/src/Files.App/Data/Enums/DynamicDialogResult.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Data.Enums +namespace Files.App.Data.Enums { public enum DynamicDialogResult { diff --git a/src/Files.Core/Data/Enums/FileNameConflictResolveOptionType.cs b/src/Files.App/Data/Enums/FileNameConflictResolveOptionType.cs similarity index 93% rename from src/Files.Core/Data/Enums/FileNameConflictResolveOptionType.cs rename to src/Files.App/Data/Enums/FileNameConflictResolveOptionType.cs index d4e48a141f3a..5b0cec97b64f 100644 --- a/src/Files.Core/Data/Enums/FileNameConflictResolveOptionType.cs +++ b/src/Files.App/Data/Enums/FileNameConflictResolveOptionType.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Data.Enums +namespace Files.App.Data.Enums { public enum FileNameConflictResolveOptionType : uint { diff --git a/src/Files.Core/Data/Enums/FileOperationType.cs b/src/Files.App/Data/Enums/FileOperationType.cs similarity index 97% rename from src/Files.Core/Data/Enums/FileOperationType.cs rename to src/Files.App/Data/Enums/FileOperationType.cs index 96af3f6678a7..123003707b6e 100644 --- a/src/Files.Core/Data/Enums/FileOperationType.cs +++ b/src/Files.App/Data/Enums/FileOperationType.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Data.Enums +namespace Files.App.Data.Enums { /// /// Type of operation on Files filesystem that took place diff --git a/src/Files.Core/Data/Enums/FileSystemStatusCode.cs b/src/Files.App/Data/Enums/FileSystemStatusCode.cs similarity index 97% rename from src/Files.Core/Data/Enums/FileSystemStatusCode.cs rename to src/Files.App/Data/Enums/FileSystemStatusCode.cs index 603e45886931..7365b66cd91e 100644 --- a/src/Files.Core/Data/Enums/FileSystemStatusCode.cs +++ b/src/Files.App/Data/Enums/FileSystemStatusCode.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Data.Enums +namespace Files.App.Data.Enums { [Flags] public enum FileSystemStatusCode diff --git a/src/Files.Core/Data/Enums/FilesystemItemType.cs b/src/Files.App/Data/Enums/FilesystemItemType.cs similarity index 95% rename from src/Files.Core/Data/Enums/FilesystemItemType.cs rename to src/Files.App/Data/Enums/FilesystemItemType.cs index 681c1a584778..577b629273c1 100644 --- a/src/Files.Core/Data/Enums/FilesystemItemType.cs +++ b/src/Files.App/Data/Enums/FilesystemItemType.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Data.Enums +namespace Files.App.Data.Enums { /// /// Defines constants that specify item type of the file system on Windows. diff --git a/src/Files.Core/Data/Enums/FilesystemOperationType.cs b/src/Files.App/Data/Enums/FilesystemOperationType.cs similarity index 91% rename from src/Files.Core/Data/Enums/FilesystemOperationType.cs rename to src/Files.App/Data/Enums/FilesystemOperationType.cs index 0702ed3b9888..08623018b173 100644 --- a/src/Files.Core/Data/Enums/FilesystemOperationType.cs +++ b/src/Files.App/Data/Enums/FilesystemOperationType.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Data.Enums +namespace Files.App.Data.Enums { public enum FilesystemOperationType { diff --git a/src/Files.Core/Data/Enums/GitCheckoutOptions.cs b/src/Files.App/Data/Enums/GitCheckoutOptions.cs similarity index 94% rename from src/Files.Core/Data/Enums/GitCheckoutOptions.cs rename to src/Files.App/Data/Enums/GitCheckoutOptions.cs index 942e9f32e2cf..e9f6384d5bf5 100644 --- a/src/Files.Core/Data/Enums/GitCheckoutOptions.cs +++ b/src/Files.App/Data/Enums/GitCheckoutOptions.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Data.Enums +namespace Files.App.Data.Enums { /// /// Defines constants that specify checkout operation type. diff --git a/src/Files.Core/Data/Enums/GitOperationResult.cs b/src/Files.App/Data/Enums/GitOperationResult.cs similarity index 93% rename from src/Files.Core/Data/Enums/GitOperationResult.cs rename to src/Files.App/Data/Enums/GitOperationResult.cs index d46136800ef8..e1e0cdb48e28 100644 --- a/src/Files.Core/Data/Enums/GitOperationResult.cs +++ b/src/Files.App/Data/Enums/GitOperationResult.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Data.Enums +namespace Files.App.Data.Enums { /// /// Defines constants that specify git operation result. diff --git a/src/Files.Core/Data/Enums/GridViewSizeKind.cs b/src/Files.App/Data/Enums/GridViewSizeKind.cs similarity index 97% rename from src/Files.Core/Data/Enums/GridViewSizeKind.cs rename to src/Files.App/Data/Enums/GridViewSizeKind.cs index e96688ed6194..49f39bd52fff 100644 --- a/src/Files.Core/Data/Enums/GridViewSizeKind.cs +++ b/src/Files.App/Data/Enums/GridViewSizeKind.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Data.Enums +namespace Files.App.Data.Enums { /// /// Defines constants that specify the size in the Grid View layout. diff --git a/src/Files.Core/Data/Enums/IconOptions.cs b/src/Files.App/Data/Enums/IconOptions.cs similarity index 96% rename from src/Files.Core/Data/Enums/IconOptions.cs rename to src/Files.App/Data/Enums/IconOptions.cs index 1f203642de32..7dd2d3eb65a1 100644 --- a/src/Files.Core/Data/Enums/IconOptions.cs +++ b/src/Files.App/Data/Enums/IconOptions.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Data.Enums +namespace Files.App.Data.Enums { /// /// Behavior used to retrieve and adjust icons diff --git a/src/Files.Core/Data/Enums/IconPersistenceOptions.cs b/src/Files.App/Data/Enums/IconPersistenceOptions.cs similarity index 89% rename from src/Files.Core/Data/Enums/IconPersistenceOptions.cs rename to src/Files.App/Data/Enums/IconPersistenceOptions.cs index 93b22f236394..caeee020fd05 100644 --- a/src/Files.Core/Data/Enums/IconPersistenceOptions.cs +++ b/src/Files.App/Data/Enums/IconPersistenceOptions.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Data.Enums +namespace Files.App.Data.Enums { public enum IconPersistenceOptions { diff --git a/src/Files.Core/Data/Enums/ImpossibleActionResponseTypes.cs b/src/Files.App/Data/Enums/ImpossibleActionResponseTypes.cs similarity index 92% rename from src/Files.Core/Data/Enums/ImpossibleActionResponseTypes.cs rename to src/Files.App/Data/Enums/ImpossibleActionResponseTypes.cs index 0ed962009b19..4a3fd03dcec8 100644 --- a/src/Files.Core/Data/Enums/ImpossibleActionResponseTypes.cs +++ b/src/Files.App/Data/Enums/ImpossibleActionResponseTypes.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Data.Enums +namespace Files.App.Data.Enums { /// /// Defines constants that specify an action for a response of impossible operation. diff --git a/src/Files.Core/Data/Enums/InfoPaneTabs.cs b/src/Files.App/Data/Enums/InfoPaneTabs.cs similarity index 91% rename from src/Files.Core/Data/Enums/InfoPaneTabs.cs rename to src/Files.App/Data/Enums/InfoPaneTabs.cs index 7c2c9c4d2e04..3abec135026e 100644 --- a/src/Files.Core/Data/Enums/InfoPaneTabs.cs +++ b/src/Files.App/Data/Enums/InfoPaneTabs.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Data.Enums +namespace Files.App.Data.Enums { /// /// Defines constants that specify the selected tab in the Info Pane. diff --git a/src/Files.Core/Data/Enums/ListViewSizeKind.cs b/src/Files.App/Data/Enums/ListViewSizeKind.cs similarity index 95% rename from src/Files.Core/Data/Enums/ListViewSizeKind.cs rename to src/Files.App/Data/Enums/ListViewSizeKind.cs index 736becac7085..2833a7cf5bac 100644 --- a/src/Files.Core/Data/Enums/ListViewSizeKind.cs +++ b/src/Files.App/Data/Enums/ListViewSizeKind.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Data.Enums +namespace Files.App.Data.Enums { /// /// Defines constants that specify the size in the List View layout. diff --git a/src/Files.Core/Data/Enums/ParsedCommandType.cs b/src/Files.App/Data/Enums/ParsedCommandType.cs similarity index 96% rename from src/Files.Core/Data/Enums/ParsedCommandType.cs rename to src/Files.App/Data/Enums/ParsedCommandType.cs index 175f3d19323a..0c4d46f39991 100644 --- a/src/Files.Core/Data/Enums/ParsedCommandType.cs +++ b/src/Files.App/Data/Enums/ParsedCommandType.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Data.Enums +namespace Files.App.Data.Enums { /// /// Defines constants that specify parsed command line item type on Windows. diff --git a/src/Files.Core/Data/Enums/PreviewPaneStates.cs b/src/Files.App/Data/Enums/PreviewPaneStates.cs similarity index 95% rename from src/Files.Core/Data/Enums/PreviewPaneStates.cs rename to src/Files.App/Data/Enums/PreviewPaneStates.cs index 3353eb366715..36e076f797dd 100644 --- a/src/Files.Core/Data/Enums/PreviewPaneStates.cs +++ b/src/Files.App/Data/Enums/PreviewPaneStates.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Data.Enums +namespace Files.App.Data.Enums { /// /// Defines constants that specify preview pane status. diff --git a/src/Files.Core/Data/Enums/PropertiesNavigationViewItemType.cs b/src/Files.App/Data/Enums/PropertiesNavigationViewItemType.cs similarity index 96% rename from src/Files.Core/Data/Enums/PropertiesNavigationViewItemType.cs rename to src/Files.App/Data/Enums/PropertiesNavigationViewItemType.cs index 6385cba366da..9df54411253f 100644 --- a/src/Files.Core/Data/Enums/PropertiesNavigationViewItemType.cs +++ b/src/Files.App/Data/Enums/PropertiesNavigationViewItemType.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Data.Enums +namespace Files.App.Data.Enums { /// /// Represents a page type used in Properties window diff --git a/src/Files.Core/Data/Enums/ReturnResult.cs b/src/Files.App/Data/Enums/ReturnResult.cs similarity index 97% rename from src/Files.Core/Data/Enums/ReturnResult.cs rename to src/Files.App/Data/Enums/ReturnResult.cs index e71996b2ab41..743d3f34d118 100644 --- a/src/Files.Core/Data/Enums/ReturnResult.cs +++ b/src/Files.App/Data/Enums/ReturnResult.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Data.Enums +namespace Files.App.Data.Enums { /// /// Contains all kinds of return status diff --git a/src/Files.Core/Data/Enums/SearchBoxTextChangeReason.cs b/src/Files.App/Data/Enums/SearchBoxTextChangeReason.cs similarity index 94% rename from src/Files.Core/Data/Enums/SearchBoxTextChangeReason.cs rename to src/Files.App/Data/Enums/SearchBoxTextChangeReason.cs index fbcfa52e6973..06632a610671 100644 --- a/src/Files.Core/Data/Enums/SearchBoxTextChangeReason.cs +++ b/src/Files.App/Data/Enums/SearchBoxTextChangeReason.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Data.Enums +namespace Files.App.Data.Enums { /// /// Defines constants that specify search box text changed reason. diff --git a/src/Files.Core/Data/Enums/StatusCenterItemIconKind.cs b/src/Files.App/Data/Enums/StatusCenterItemIconKind.cs similarity index 91% rename from src/Files.Core/Data/Enums/StatusCenterItemIconKind.cs rename to src/Files.App/Data/Enums/StatusCenterItemIconKind.cs index 3173ca7d2d59..672139c75a0e 100644 --- a/src/Files.Core/Data/Enums/StatusCenterItemIconKind.cs +++ b/src/Files.App/Data/Enums/StatusCenterItemIconKind.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Data.Enums +namespace Files.App.Data.Enums { /// /// Defines constants that specify icon kind of Status Center item. diff --git a/src/Files.Core/Data/Enums/StatusCenterItemKind.cs b/src/Files.App/Data/Enums/StatusCenterItemKind.cs similarity index 89% rename from src/Files.Core/Data/Enums/StatusCenterItemKind.cs rename to src/Files.App/Data/Enums/StatusCenterItemKind.cs index 06172081c415..a837782b8284 100644 --- a/src/Files.Core/Data/Enums/StatusCenterItemKind.cs +++ b/src/Files.App/Data/Enums/StatusCenterItemKind.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Data.Enums +namespace Files.App.Data.Enums { /// /// Defines constants that specify item kind of Status Center. diff --git a/src/Files.Core/Data/Enums/TilesViewSizeKind.cs b/src/Files.App/Data/Enums/TilesViewSizeKind.cs similarity index 90% rename from src/Files.Core/Data/Enums/TilesViewSizeKind.cs rename to src/Files.App/Data/Enums/TilesViewSizeKind.cs index 290323fa59cf..896de5d2591b 100644 --- a/src/Files.Core/Data/Enums/TilesViewSizeKind.cs +++ b/src/Files.App/Data/Enums/TilesViewSizeKind.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Data.Enums +namespace Files.App.Data.Enums { /// /// Defines constants that specify the size in the Tiles View layout. diff --git a/src/Files.Core/Data/Enums/WallpaperType.cs b/src/Files.App/Data/Enums/WallpaperType.cs similarity index 90% rename from src/Files.Core/Data/Enums/WallpaperType.cs rename to src/Files.App/Data/Enums/WallpaperType.cs index 0d6b7244fec5..ea8bf18bee2d 100644 --- a/src/Files.Core/Data/Enums/WallpaperType.cs +++ b/src/Files.App/Data/Enums/WallpaperType.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Data.Enums +namespace Files.App.Data.Enums { public enum WallpaperType { diff --git a/src/Files.Core/Data/Enums/WindowsCompatibilityModes.cs b/src/Files.App/Data/Enums/WindowsCompatibilityModes.cs similarity index 97% rename from src/Files.Core/Data/Enums/WindowsCompatibilityModes.cs rename to src/Files.App/Data/Enums/WindowsCompatibilityModes.cs index d44bfdfd1f8b..0da8c6e9a0d7 100644 --- a/src/Files.Core/Data/Enums/WindowsCompatibilityModes.cs +++ b/src/Files.App/Data/Enums/WindowsCompatibilityModes.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Data.Enums +namespace Files.App.Data.Enums { [Flags] public enum WindowsCompatDpiOverrideKind diff --git a/src/Files.App/Data/EventArguments/LayoutModeEventArgs.cs b/src/Files.App/Data/EventArguments/LayoutModeEventArgs.cs index b2973490e58d..fb3e03bebd78 100644 --- a/src/Files.App/Data/EventArguments/LayoutModeEventArgs.cs +++ b/src/Files.App/Data/EventArguments/LayoutModeEventArgs.cs @@ -1,6 +1,8 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. +using Files.App.Server.Data.Enums; + namespace Files.App.Data.EventArguments { public sealed class LayoutModeEventArgs diff --git a/src/Files.Core/Data/EventArguments/SettingChangedEventArgs.cs b/src/Files.App/Data/EventArguments/SettingChangedEventArgs.cs similarity index 90% rename from src/Files.Core/Data/EventArguments/SettingChangedEventArgs.cs rename to src/Files.App/Data/EventArguments/SettingChangedEventArgs.cs index 4486b78a3815..2eb9f8cf82f7 100644 --- a/src/Files.Core/Data/EventArguments/SettingChangedEventArgs.cs +++ b/src/Files.App/Data/EventArguments/SettingChangedEventArgs.cs @@ -3,7 +3,7 @@ using System; -namespace Files.Core.Data.EventArguments +namespace Files.App.Data.EventArguments { public sealed class SettingChangedEventArgs : EventArgs { diff --git a/src/Files.App/Data/Factories/PropertiesNavigationViewItemFactory.cs b/src/Files.App/Data/Factories/PropertiesNavigationViewItemFactory.cs index acd28cd6b6f7..4388919c382c 100644 --- a/src/Files.App/Data/Factories/PropertiesNavigationViewItemFactory.cs +++ b/src/Files.App/Data/Factories/PropertiesNavigationViewItemFactory.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -using Files.Core.Helpers; +using Files.App.Helpers; using Microsoft.UI.Xaml; using Windows.Storage; using Files.Shared.Helpers; diff --git a/src/Files.Core/Data/Items/ContextMenu.cs b/src/Files.App/Data/Items/ContextMenu.cs similarity index 97% rename from src/Files.Core/Data/Items/ContextMenu.cs rename to src/Files.App/Data/Items/ContextMenu.cs index e4375ba6dfb9..b110d4b61209 100644 --- a/src/Files.Core/Data/Items/ContextMenu.cs +++ b/src/Files.App/Data/Items/ContextMenu.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Data.Items +namespace Files.App.Data.Items { // Same definition of Vanara.PInvoke.User32.MenuItemType public enum MenuItemType : uint diff --git a/src/Files.App/Data/Items/DetailsLayoutColumnItem.cs b/src/Files.App/Data/Items/DetailsLayoutColumnItem.cs index ed27cb6e5151..edee515bb519 100644 --- a/src/Files.App/Data/Items/DetailsLayoutColumnItem.cs +++ b/src/Files.App/Data/Items/DetailsLayoutColumnItem.cs @@ -36,33 +36,27 @@ public bool UserCollapsed } } - [LiteDB.BsonIgnore] public bool IsResizable { get; set; } = true; - [LiteDB.BsonIgnore] public double MinLength => UserCollapsed || IsHidden ? 0 : NormalMinLength; - [LiteDB.BsonIgnore] public Visibility Visibility => UserCollapsed || IsHidden ? Visibility.Collapsed : Visibility.Visible; - [LiteDB.BsonIgnore] public GridLength Length => UserCollapsed || IsHidden ? new GridLength(0) : UserLength; - [LiteDB.BsonIgnore] public GridLength LengthIncludingGridSplitter => UserCollapsed || IsHidden ? new(0) : new(UserLength.Value + (IsResizable ? GRID_SPLITTER_WIDTH : 0)); - [LiteDB.BsonIgnore] public double MaxLength => UserCollapsed || IsHidden ? 0 : NormalMaxLength; private bool _IsHidden; - [LiteDB.BsonIgnore] + public bool IsHidden { get => _IsHidden; @@ -70,7 +64,7 @@ public bool IsHidden } private double _NormalMinLength = 50; - [LiteDB.BsonIgnore] + public double NormalMinLength { get => _NormalMinLength; @@ -82,7 +76,7 @@ public double NormalMinLength } private GridLength _UserLength = new(200, GridUnitType.Pixel); - [LiteDB.BsonIgnore] + public GridLength UserLength { get => _UserLength; diff --git a/src/Files.Core/Data/Items/DeviceEvent.cs b/src/Files.App/Data/Items/DeviceEvent.cs similarity index 84% rename from src/Files.Core/Data/Items/DeviceEvent.cs rename to src/Files.App/Data/Items/DeviceEvent.cs index 286b2c80ed01..229aa1a4e600 100644 --- a/src/Files.Core/Data/Items/DeviceEvent.cs +++ b/src/Files.App/Data/Items/DeviceEvent.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Data.Items +namespace Files.App.Data.Items { public enum DeviceEvent { diff --git a/src/Files.Core/Data/Items/IconFileInfo.cs b/src/Files.App/Data/Items/IconFileInfo.cs similarity index 90% rename from src/Files.Core/Data/Items/IconFileInfo.cs rename to src/Files.App/Data/Items/IconFileInfo.cs index baf95c435595..21a066d9bc52 100644 --- a/src/Files.Core/Data/Items/IconFileInfo.cs +++ b/src/Files.App/Data/Items/IconFileInfo.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Data.Items +namespace Files.App.Data.Items { public sealed class IconFileInfo { diff --git a/src/Files.App/Data/Items/ListedItem.cs b/src/Files.App/Data/Items/ListedItem.cs index 35b659b46f36..d7deb502397c 100644 --- a/src/Files.App/Data/Items/ListedItem.cs +++ b/src/Files.App/Data/Items/ListedItem.cs @@ -100,6 +100,7 @@ public string[] FileTags { if (SetProperty(ref fileTags, value)) { + Debug.Assert(value != null); var dbInstance = FileTagsHelper.GetDbInstance(); dbInstance.SetTags(ItemPath, FileFRN, value); HasTags = !FileTags.IsEmpty(); diff --git a/src/Files.Core/Data/Items/ShellFileItem.cs b/src/Files.App/Data/Items/ShellFileItem.cs similarity index 97% rename from src/Files.Core/Data/Items/ShellFileItem.cs rename to src/Files.App/Data/Items/ShellFileItem.cs index 9e6eee626e1c..b26b0d796fb3 100644 --- a/src/Files.Core/Data/Items/ShellFileItem.cs +++ b/src/Files.App/Data/Items/ShellFileItem.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Data.Items +namespace Files.App.Data.Items { public class ShellFileItem { diff --git a/src/Files.Core/Data/Items/ShellLibraryItem.cs b/src/Files.App/Data/Items/ShellLibraryItem.cs similarity index 98% rename from src/Files.Core/Data/Items/ShellLibraryItem.cs rename to src/Files.App/Data/Items/ShellLibraryItem.cs index 47a7d464f587..085e8c374413 100644 --- a/src/Files.Core/Data/Items/ShellLibraryItem.cs +++ b/src/Files.App/Data/Items/ShellLibraryItem.cs @@ -3,7 +3,7 @@ using System.IO; -namespace Files.Core.Data.Items +namespace Files.App.Data.Items { public sealed class ShellLibraryItem { diff --git a/src/Files.Core/Data/Items/ShellLinkItem.cs b/src/Files.App/Data/Items/ShellLinkItem.cs similarity index 96% rename from src/Files.Core/Data/Items/ShellLinkItem.cs rename to src/Files.App/Data/Items/ShellLinkItem.cs index af5555c143b9..91c68766fb7f 100644 --- a/src/Files.Core/Data/Items/ShellLinkItem.cs +++ b/src/Files.App/Data/Items/ShellLinkItem.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Data.Items +namespace Files.App.Data.Items { public sealed class ShellLinkItem : ShellFileItem { diff --git a/src/Files.Core/Data/Items/ShellNewEntry.cs b/src/Files.App/Data/Items/ShellNewEntry.cs similarity index 92% rename from src/Files.Core/Data/Items/ShellNewEntry.cs rename to src/Files.App/Data/Items/ShellNewEntry.cs index 44aec91e410c..f476954330f5 100644 --- a/src/Files.Core/Data/Items/ShellNewEntry.cs +++ b/src/Files.App/Data/Items/ShellNewEntry.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Data.Items +namespace Files.App.Data.Items { public sealed class ShellNewEntry { diff --git a/src/Files.Core/Data/Items/ShellOperationResult.cs b/src/Files.App/Data/Items/ShellOperationResult.cs similarity index 96% rename from src/Files.Core/Data/Items/ShellOperationResult.cs rename to src/Files.App/Data/Items/ShellOperationResult.cs index 2be4956ccea0..0b69d68f4ee5 100644 --- a/src/Files.Core/Data/Items/ShellOperationResult.cs +++ b/src/Files.App/Data/Items/ShellOperationResult.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Data.Items +namespace Files.App.Data.Items { public sealed class ShellOperationResult { diff --git a/src/Files.Core/Data/Items/Win32Process.cs b/src/Files.App/Data/Items/Win32Process.cs similarity index 89% rename from src/Files.Core/Data/Items/Win32Process.cs rename to src/Files.App/Data/Items/Win32Process.cs index b35efb94f033..cbc5f4873cf6 100644 --- a/src/Files.Core/Data/Items/Win32Process.cs +++ b/src/Files.App/Data/Items/Win32Process.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Data.Items +namespace Files.App.Data.Items { public sealed class Win32Process { diff --git a/src/Files.Core/Data/Items/WindowsCompatibilityOptions.cs b/src/Files.App/Data/Items/WindowsCompatibilityOptions.cs similarity index 98% rename from src/Files.Core/Data/Items/WindowsCompatibilityOptions.cs rename to src/Files.App/Data/Items/WindowsCompatibilityOptions.cs index 08d667cfdd02..c14df49ce725 100644 --- a/src/Files.Core/Data/Items/WindowsCompatibilityOptions.cs +++ b/src/Files.App/Data/Items/WindowsCompatibilityOptions.cs @@ -3,7 +3,7 @@ using Files.Shared.Extensions; -namespace Files.Core.Data.Items +namespace Files.App.Data.Items { public sealed class WindowsCompatibilityOptions { diff --git a/src/Files.Core/Data/Messages/FileSystemDialogOptionChangedMessage.cs b/src/Files.App/Data/Messages/FileSystemDialogOptionChangedMessage.cs similarity index 85% rename from src/Files.Core/Data/Messages/FileSystemDialogOptionChangedMessage.cs rename to src/Files.App/Data/Messages/FileSystemDialogOptionChangedMessage.cs index e48b2faa312d..c47416af0b5c 100644 --- a/src/Files.Core/Data/Messages/FileSystemDialogOptionChangedMessage.cs +++ b/src/Files.App/Data/Messages/FileSystemDialogOptionChangedMessage.cs @@ -2,9 +2,9 @@ // Licensed under the MIT License. See the LICENSE. using CommunityToolkit.Mvvm.Messaging.Messages; -using Files.Core.ViewModels.Dialogs.FileSystemDialog; +using Files.App.ViewModels.Dialogs.FileSystemDialog; -namespace Files.Core.Data.Messages +namespace Files.App.Data.Messages { /// /// Represents a messenger for FileSystemDialog option changed. diff --git a/src/Files.Core/Data/Models/AddItemDialogResultModel.cs b/src/Files.App/Data/Models/AddItemDialogResultModel.cs similarity index 93% rename from src/Files.Core/Data/Models/AddItemDialogResultModel.cs rename to src/Files.App/Data/Models/AddItemDialogResultModel.cs index 4f4a4a2db8fe..e22061947d69 100644 --- a/src/Files.Core/Data/Models/AddItemDialogResultModel.cs +++ b/src/Files.App/Data/Models/AddItemDialogResultModel.cs @@ -3,7 +3,7 @@ using Files.Shared; -namespace Files.Core.Data.Models +namespace Files.App.Data.Models { /// /// Represents a model for AddItemDialog result. diff --git a/src/Files.Core/Data/Models/ByteSize.cs b/src/Files.App/Data/Models/ByteSize.cs similarity index 98% rename from src/Files.Core/Data/Models/ByteSize.cs rename to src/Files.App/Data/Models/ByteSize.cs index 8a782e8afbda..b779bbb1fbc5 100644 --- a/src/Files.Core/Data/Models/ByteSize.cs +++ b/src/Files.App/Data/Models/ByteSize.cs @@ -3,7 +3,7 @@ using System.Collections.Frozen; -namespace Files.Core.Data.Models +namespace Files.App.Data.Models { public struct ByteSize : IEquatable, IComparable { diff --git a/src/Files.App/Data/Models/ColumnsViewModel.cs b/src/Files.App/Data/Models/ColumnsViewModel.cs index 87a8f29ad9fc..90b461de8e62 100644 --- a/src/Files.App/Data/Models/ColumnsViewModel.cs +++ b/src/Files.App/Data/Models/ColumnsViewModel.cs @@ -13,7 +13,6 @@ public sealed class ColumnsViewModel : ObservableObject IsResizable = false, }; - [LiteDB.BsonIgnore] public DetailsLayoutColumnItem IconColumn { get => iconColumn; @@ -141,7 +140,6 @@ public DetailsLayoutColumnItem SizeColumn set => SetProperty(ref sizeColumn, value); } - [LiteDB.BsonIgnore] public double TotalWidth => IconColumn.Length.Value + GitStatusColumn.Length.Value + diff --git a/src/Files.App/Data/Models/CurrentInstanceViewModel.cs b/src/Files.App/Data/Models/CurrentInstanceViewModel.cs index 17ae3b2fb04b..44985617370d 100644 --- a/src/Files.App/Data/Models/CurrentInstanceViewModel.cs +++ b/src/Files.App/Data/Models/CurrentInstanceViewModel.cs @@ -1,6 +1,8 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. +using Files.App.Server.Data.Enums; + namespace Files.App.Data.Models { public sealed class CurrentInstanceViewModel : ObservableObject diff --git a/src/Files.Core/Data/Models/DisposableArray.cs b/src/Files.App/Data/Models/DisposableArray.cs similarity index 97% rename from src/Files.Core/Data/Models/DisposableArray.cs rename to src/Files.App/Data/Models/DisposableArray.cs index 3d658c37de69..4aee254357f8 100644 --- a/src/Files.Core/Data/Models/DisposableArray.cs +++ b/src/Files.App/Data/Models/DisposableArray.cs @@ -3,7 +3,7 @@ using Files.Shared.Extensions; -namespace Files.Core.Data.Models +namespace Files.App.Data.Models { public sealed class DisposableArray : FreeableStore { diff --git a/src/Files.App/Data/Models/DrivesViewModel.cs b/src/Files.App/Data/Models/DrivesViewModel.cs index edcae42e75de..2a9e38f8904a 100644 --- a/src/Files.App/Data/Models/DrivesViewModel.cs +++ b/src/Files.App/Data/Models/DrivesViewModel.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -using Files.Core.Services.SizeProvider; +using Files.App.Services.SizeProvider; using Files.Core.Storage.LocatableStorage; using Microsoft.Extensions.Logging; using System.IO; diff --git a/src/Files.Core/Data/Models/FreeableStore.cs b/src/Files.App/Data/Models/FreeableStore.cs similarity index 95% rename from src/Files.Core/Data/Models/FreeableStore.cs rename to src/Files.App/Data/Models/FreeableStore.cs index baf920360eae..6fde59cecc75 100644 --- a/src/Files.Core/Data/Models/FreeableStore.cs +++ b/src/Files.App/Data/Models/FreeableStore.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Data.Models +namespace Files.App.Data.Models { public abstract class FreeableStore : IDisposable, IEquatable where TImplementation : class diff --git a/src/Files.Core/Data/Models/HashInfoItem.cs b/src/Files.App/Data/Models/HashInfoItem.cs similarity index 96% rename from src/Files.Core/Data/Models/HashInfoItem.cs rename to src/Files.App/Data/Models/HashInfoItem.cs index a23e3149e5cd..3550295bb072 100644 --- a/src/Files.Core/Data/Models/HashInfoItem.cs +++ b/src/Files.App/Data/Models/HashInfoItem.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Data.Models +namespace Files.App.Data.Models { public sealed class HashInfoItem : ObservableObject { diff --git a/src/Files.Core/Data/Models/IDatabaseModel.cs b/src/Files.App/Data/Models/IDatabaseModel.cs similarity index 97% rename from src/Files.Core/Data/Models/IDatabaseModel.cs rename to src/Files.App/Data/Models/IDatabaseModel.cs index de51f75b7f62..476c9c421ee2 100644 --- a/src/Files.Core/Data/Models/IDatabaseModel.cs +++ b/src/Files.App/Data/Models/IDatabaseModel.cs @@ -1,6 +1,6 @@ using Files.Shared.Utils; -namespace Files.Core.Data.Models +namespace Files.App.Data.Models { /// /// Represents a database to store data identified by . diff --git a/src/Files.Core/Data/Models/ISerializedModel.cs b/src/Files.App/Data/Models/ISerializedModel.cs similarity index 92% rename from src/Files.Core/Data/Models/ISerializedModel.cs rename to src/Files.App/Data/Models/ISerializedModel.cs index 3fc3730e1d36..a829c4289c4a 100644 --- a/src/Files.Core/Data/Models/ISerializedModel.cs +++ b/src/Files.App/Data/Models/ISerializedModel.cs @@ -1,4 +1,4 @@ -namespace Files.Core.Data.Models +namespace Files.App.Data.Models { /// /// Represents a model that holds serialized data. diff --git a/src/Files.Core/Data/Models/IStorageDeviceWatcher.cs b/src/Files.App/Data/Models/IStorageDeviceWatcher.cs similarity index 97% rename from src/Files.Core/Data/Models/IStorageDeviceWatcher.cs rename to src/Files.App/Data/Models/IStorageDeviceWatcher.cs index e441d917c96a..bb95c9fdf589 100644 --- a/src/Files.Core/Data/Models/IStorageDeviceWatcher.cs +++ b/src/Files.App/Data/Models/IStorageDeviceWatcher.cs @@ -4,7 +4,7 @@ using Files.Core.Storage.LocatableStorage; using System; -namespace Files.Core.Data.Models +namespace Files.App.Data.Models { /// /// Represents a storage device watcher diff --git a/src/Files.App/Data/Models/ItemViewModel.cs b/src/Files.App/Data/Models/ItemViewModel.cs index a89c45f5ef52..8449494619b6 100644 --- a/src/Files.App/Data/Models/ItemViewModel.cs +++ b/src/Files.App/Data/Models/ItemViewModel.cs @@ -1,7 +1,8 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -using Files.Core.Services.SizeProvider; +using Files.App.Server.Data.Enums; +using Files.App.Services.SizeProvider; using Files.Shared.Helpers; using LibGit2Sharp; using Microsoft.Extensions.Logging; @@ -19,7 +20,7 @@ using Windows.Storage.FileProperties; using Windows.Storage.Search; using static Files.App.Helpers.Win32PInvoke; -using static Files.Core.Helpers.NativeFindStorageItemHelper; +using static Files.App.Helpers.NativeFindStorageItemHelper; using DispatcherQueue = Microsoft.UI.Dispatching.DispatcherQueue; using FileAttributes = System.IO.FileAttributes; diff --git a/src/Files.App/Data/Models/NetworkDrivesViewModel.cs b/src/Files.App/Data/Models/NetworkDrivesViewModel.cs index 26dd7e6fd0e7..fcad7f93bc9c 100644 --- a/src/Files.App/Data/Models/NetworkDrivesViewModel.cs +++ b/src/Files.App/Data/Models/NetworkDrivesViewModel.cs @@ -2,7 +2,7 @@ // Licensed under the MIT License. See the LICENSE. using Files.App.Data.Items; -using Files.Core.Services; +using Files.App.Services; using Files.Core.Storage.LocatableStorage; namespace Files.App.Data.Models diff --git a/src/Files.Core/Data/Models/TaggedItemModel.cs b/src/Files.App/Data/Models/TaggedItemModel.cs similarity index 92% rename from src/Files.Core/Data/Models/TaggedItemModel.cs rename to src/Files.App/Data/Models/TaggedItemModel.cs index 0909d7106a58..990d2ee092c7 100644 --- a/src/Files.Core/Data/Models/TaggedItemModel.cs +++ b/src/Files.App/Data/Models/TaggedItemModel.cs @@ -3,7 +3,7 @@ using Files.Core.Storage; -namespace Files.Core.Data.Models +namespace Files.App.Data.Models { /// /// Represents an item that is tagged. diff --git a/src/Files.Core/Data/Models/VolumeInfo.cs b/src/Files.App/Data/Models/VolumeInfo.cs similarity index 97% rename from src/Files.Core/Data/Models/VolumeInfo.cs rename to src/Files.App/Data/Models/VolumeInfo.cs index 9cdbc63039ac..cace46330bfa 100644 --- a/src/Files.Core/Data/Models/VolumeInfo.cs +++ b/src/Files.App/Data/Models/VolumeInfo.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Data.Models +namespace Files.App.Data.Models { /// /// Represents an item for volume info on Windows. diff --git a/src/Files.App/Data/TemplateSelectors/FileSystemDialogItemSelector.cs b/src/Files.App/Data/TemplateSelectors/FileSystemDialogItemSelector.cs index e957696b51f6..c5a794a811ca 100644 --- a/src/Files.App/Data/TemplateSelectors/FileSystemDialogItemSelector.cs +++ b/src/Files.App/Data/TemplateSelectors/FileSystemDialogItemSelector.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -using Files.Core.ViewModels.Dialogs.FileSystemDialog; +using Files.App.ViewModels.Dialogs.FileSystemDialog; using Microsoft.UI.Xaml; namespace Files.App.Data.TemplateSelectors diff --git a/src/Files.App/Dialogs/AddItemDialog.xaml b/src/Files.App/Dialogs/AddItemDialog.xaml index c2befdfe69b5..95960cca90f8 100644 --- a/src/Files.App/Dialogs/AddItemDialog.xaml +++ b/src/Files.App/Dialogs/AddItemDialog.xaml @@ -7,7 +7,7 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:helpers="using:Files.App.Helpers" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:vm="using:Files.Core.ViewModels.Dialogs.AddItemDialog" + xmlns:vm="using:Files.App.ViewModels.Dialogs.AddItemDialog" x:Name="AddDialog" Title="{helpers:ResourceString Name=AddDialog/Title}" Grid.RowSpan="4" diff --git a/src/Files.App/Dialogs/AddItemDialog.xaml.cs b/src/Files.App/Dialogs/AddItemDialog.xaml.cs index 3891cae2550e..0af0cc61faff 100644 --- a/src/Files.App/Dialogs/AddItemDialog.xaml.cs +++ b/src/Files.App/Dialogs/AddItemDialog.xaml.cs @@ -1,8 +1,8 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -using Files.Core.ViewModels.Dialogs; -using Files.Core.ViewModels.Dialogs.AddItemDialog; +using Files.App.ViewModels.Dialogs; +using Files.App.ViewModels.Dialogs.AddItemDialog; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; diff --git a/src/Files.App/Dialogs/CreateShortcutDialog.xaml.cs b/src/Files.App/Dialogs/CreateShortcutDialog.xaml.cs index 16060d6f151f..98e979351664 100644 --- a/src/Files.App/Dialogs/CreateShortcutDialog.xaml.cs +++ b/src/Files.App/Dialogs/CreateShortcutDialog.xaml.cs @@ -2,7 +2,7 @@ // Licensed under the MIT License. See the LICENSE. using Files.App.ViewModels.Dialogs; -using Files.Core.ViewModels.Dialogs; +using Files.App.ViewModels.Dialogs; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Data; diff --git a/src/Files.App/Dialogs/ElevateConfirmDialog.xaml.cs b/src/Files.App/Dialogs/ElevateConfirmDialog.xaml.cs index 695eff677187..38f0434babd3 100644 --- a/src/Files.App/Dialogs/ElevateConfirmDialog.xaml.cs +++ b/src/Files.App/Dialogs/ElevateConfirmDialog.xaml.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -using Files.Core.ViewModels.Dialogs; +using Files.App.ViewModels.Dialogs; using Microsoft.UI.Xaml.Controls; using System; using System.Threading.Tasks; diff --git a/src/Files.App/Dialogs/FilesystemOperationDialog.xaml b/src/Files.App/Dialogs/FilesystemOperationDialog.xaml index 3e16d6e5ed4c..3ffb6222d25e 100644 --- a/src/Files.App/Dialogs/FilesystemOperationDialog.xaml +++ b/src/Files.App/Dialogs/FilesystemOperationDialog.xaml @@ -9,7 +9,7 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:templateselectors="using:Files.App.Data.TemplateSelectors" xmlns:tvc="using:CommunityToolkit.WinUI.UI.Converters" - xmlns:vm="using:Files.Core.ViewModels.Dialogs.FileSystemDialog" + xmlns:vm="using:Files.App.ViewModels.Dialogs.FileSystemDialog" x:Name="RootDialog" Title="{x:Bind ViewModel.Title, Mode=OneWay}" Closing="RootDialog_Closing" diff --git a/src/Files.App/Dialogs/ReorderSidebarItemsDialog.xaml.cs b/src/Files.App/Dialogs/ReorderSidebarItemsDialog.xaml.cs index 4c8cdea0d7bb..725c273d8338 100644 --- a/src/Files.App/Dialogs/ReorderSidebarItemsDialog.xaml.cs +++ b/src/Files.App/Dialogs/ReorderSidebarItemsDialog.xaml.cs @@ -5,7 +5,7 @@ using Files.App.Data.Items; using Files.App.Extensions; using Files.App.ViewModels.Dialogs; -using Files.Core.ViewModels.Dialogs; +using Files.App.ViewModels.Dialogs; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Input; diff --git a/src/Files.App/Dialogs/SettingsDialog.xaml.cs b/src/Files.App/Dialogs/SettingsDialog.xaml.cs index 6210700eb9e8..54325bc2952b 100644 --- a/src/Files.App/Dialogs/SettingsDialog.xaml.cs +++ b/src/Files.App/Dialogs/SettingsDialog.xaml.cs @@ -2,7 +2,7 @@ // Licensed under the MIT License. See the LICENSE. using Files.App.Views.Settings; -using Files.Core.ViewModels.Dialogs; +using Files.App.ViewModels.Dialogs; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using System; diff --git a/src/Files.Core/Extensions/GroupOptionExtensions.cs b/src/Files.App/Extensions/GroupOptionExtensions.cs similarity index 82% rename from src/Files.Core/Extensions/GroupOptionExtensions.cs rename to src/Files.App/Extensions/GroupOptionExtensions.cs index 44a1d9a7cf72..cae8fdbfd7df 100644 --- a/src/Files.Core/Extensions/GroupOptionExtensions.cs +++ b/src/Files.App/Extensions/GroupOptionExtensions.cs @@ -1,7 +1,9 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Extensions +using Files.App.Server.Data.Enums; + +namespace Files.App.Extensions { public static class GroupOptionExtensions { diff --git a/src/Files.Core/Extensions/LocalizationExtensions.cs b/src/Files.App/Extensions/LocalizationExtensions.cs similarity index 92% rename from src/Files.Core/Extensions/LocalizationExtensions.cs rename to src/Files.App/Extensions/LocalizationExtensions.cs index 6cd30ba52180..9974d2d513f6 100644 --- a/src/Files.Core/Extensions/LocalizationExtensions.cs +++ b/src/Files.App/Extensions/LocalizationExtensions.cs @@ -1,9 +1,9 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -using Files.Core.Services; +using Files.App.Services; -namespace Files.Core.Extensions +namespace Files.App.Extensions { /// /// Provides static extension for localization. diff --git a/src/Files.Core/Extensions/Win32Extensions.cs b/src/Files.App/Extensions/Win32Extensions.cs similarity index 89% rename from src/Files.Core/Extensions/Win32Extensions.cs rename to src/Files.App/Extensions/Win32Extensions.cs index 6ec37653c364..b5ef2e49b6c7 100644 --- a/src/Files.Core/Extensions/Win32Extensions.cs +++ b/src/Files.App/Extensions/Win32Extensions.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Extensions +namespace Files.App.Extensions { public static class Win32Extensions { diff --git a/src/Files.Core/Extensions/Win32FindDataExtensions.cs b/src/Files.App/Extensions/Win32FindDataExtensions.cs similarity index 83% rename from src/Files.Core/Extensions/Win32FindDataExtensions.cs rename to src/Files.App/Extensions/Win32FindDataExtensions.cs index ec75fc44a967..7a81be8460c8 100644 --- a/src/Files.Core/Extensions/Win32FindDataExtensions.cs +++ b/src/Files.App/Extensions/Win32FindDataExtensions.cs @@ -1,9 +1,9 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -using static Files.Core.Helpers.NativeFindStorageItemHelper; +using static Files.App.Helpers.NativeFindStorageItemHelper; -namespace Files.Core.Extensions +namespace Files.App.Extensions { public static class Win32FindDataExtensions { diff --git a/src/Files.App/Files.App.csproj b/src/Files.App/Files.App.csproj index 7a035134eca0..e614edfdd97d 100644 --- a/src/Files.App/Files.App.csproj +++ b/src/Files.App/Files.App.csproj @@ -25,7 +25,7 @@ true true Debug;Release;Stable;Preview;Store - Files.App.Server + Files.App.Server;Microsoft.UI.Content.ContentExternalOutputLink;Microsoft.UI.Content.IContentExternalOutputLink bin\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\publish\ False True @@ -103,10 +103,11 @@ - + + PreserveNewest @@ -118,7 +119,6 @@ - diff --git a/src/Files.App/GlobalUsings.cs b/src/Files.App/GlobalUsings.cs index efcbf9568cfa..994cd5a70fdb 100644 --- a/src/Files.App/GlobalUsings.cs +++ b/src/Files.App/GlobalUsings.cs @@ -57,27 +57,16 @@ global using global::Files.App.Views; global using global::Files.App.Views.Layouts; global using global::Files.App.Views.Shells; - -// Files.Core -global using global::Files.Core.Data.Enums; -global using global::Files.Core.Data.EventArguments; -global using global::Files.Core.Data.Items; -global using global::Files.Core.Data.Messages; -global using global::Files.Core.Data.Models; -global using global::Files.Core.Extensions; -global using global::Files.Core.Helpers; -global using global::Files.Core.Services; -global using global::Files.Core.Services.DateTimeFormatter; -global using global::Files.Core.Services.Settings; -global using global::Files.Core.ViewModels; -global using global::Files.Core.ViewModels.Dialogs; -global using global::Files.Core.ViewModels.Dialogs.AddItemDialog; -global using global::Files.Core.ViewModels.Dialogs.FileSystemDialog; -global using global::Files.Core.ViewModels.FileTags; -global using global::Files.Core.ViewModels.Widgets; -global using global::Files.Core.Utils; -global using global::Files.Core.Utils.Cloud; -global using global::Files.Core.Utils.CommandLine; +global using global::Files.App.Data.Enums; +global using global::Files.App.Data.Messages; +global using global::Files.App.Services.DateTimeFormatter; +global using global::Files.App.Services.Settings; +global using global::Files.App.ViewModels.Dialogs; +global using global::Files.App.ViewModels.Dialogs.AddItemDialog; +global using global::Files.App.ViewModels.Dialogs.FileSystemDialog; +global using global::Files.App.ViewModels.FileTags; +global using global::Files.App.ViewModels.Widgets; +global using global::Files.App.Utils.CommandLine; // Files.Core.Storage diff --git a/src/Files.App/Helpers/Application/AppLifecycleHelper.cs b/src/Files.App/Helpers/Application/AppLifecycleHelper.cs index e04b821d0c8a..1f72b5893b42 100644 --- a/src/Files.App/Helpers/Application/AppLifecycleHelper.cs +++ b/src/Files.App/Helpers/Application/AppLifecycleHelper.cs @@ -7,7 +7,7 @@ using Files.App.Storage.FtpStorage; using Files.App.Storage.NativeStorage; using Files.App.ViewModels.Settings; -using Files.Core.Services.SizeProvider; +using Files.App.Services.SizeProvider; using Files.Core.Storage; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; diff --git a/src/Files.App/Helpers/Interop/NativeFileOperationsHelper.cs b/src/Files.App/Helpers/Interop/NativeFileOperationsHelper.cs index 44663fbb2b17..1285f252cef9 100644 --- a/src/Files.App/Helpers/Interop/NativeFileOperationsHelper.cs +++ b/src/Files.App/Helpers/Interop/NativeFileOperationsHelper.cs @@ -10,7 +10,7 @@ using System.Text; using System.Threading; using Vanara.PInvoke; -using static Files.Core.Helpers.NativeFindStorageItemHelper; +using static Files.App.Helpers.NativeFindStorageItemHelper; namespace Files.App.Helpers { diff --git a/src/Files.App/Helpers/Layout/LayoutPreferencesDatabaseItem.cs b/src/Files.App/Helpers/Layout/LayoutPreferencesDatabaseItem.cs index 2544b7354794..739b2b81e8e4 100644 --- a/src/Files.App/Helpers/Layout/LayoutPreferencesDatabaseItem.cs +++ b/src/Files.App/Helpers/Layout/LayoutPreferencesDatabaseItem.cs @@ -1,8 +1,6 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -using LiteDB; - namespace Files.App.Helpers { /// @@ -10,7 +8,6 @@ namespace Files.App.Helpers /// public sealed class LayoutPreferencesDatabaseItem { - [BsonId] public int Id { get; set; } public ulong? Frn { get; set; } diff --git a/src/Files.App/Helpers/Layout/LayoutPreferencesDatabaseManager.cs b/src/Files.App/Helpers/Layout/LayoutPreferencesDatabaseManager.cs index a5247bd7a54e..1b7d6aa8a22b 100644 --- a/src/Files.App/Helpers/Layout/LayoutPreferencesDatabaseManager.cs +++ b/src/Files.App/Helpers/Layout/LayoutPreferencesDatabaseManager.cs @@ -1,200 +1,159 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -using LiteDB; -using System.Text; +using System.Diagnostics.CodeAnalysis; namespace Files.App.Helpers { /// /// Represents manager for the database of layout preferences. /// - public sealed class LayoutPreferencesDatabaseManager : IDisposable + public class LayoutPreferencesDatabaseManager { // Fields + private readonly Server.Database.LayoutPreferencesDatabase _database = new(); - private readonly LiteDatabase _database; - - // Constructor + private DetailsLayoutColumnItem FromDatabaseEntity(Server.Data.ColumnPreferencesItem entry) + { + return new() + { + NormalMaxLength = entry.NormalMaxLength, + UserCollapsed = entry.UserCollapsed, + UserLengthPixels = entry.UserLengthPixels, + }; + } - public LayoutPreferencesDatabaseManager(string connection, bool shared = false) + [return: NotNullIfNotNull(nameof(entry))] + private LayoutPreferencesItem? FromDatabaseEntity(Server.Data.LayoutPreferencesItem? entry) { - SafetyExtensions.IgnoreExceptions(() => EnsureDatabaseVersion(connection)); + if (entry is null) + { + return null; + } - _database = new( - new ConnectionString(connection) + return new() + { + ColumnsViewModel = { - Mode = shared - ? FileMode.Shared - : FileMode.Exclusive + DateCreatedColumn = FromDatabaseEntity(entry.ColumnsViewModel.DateCreatedColumn), + DateDeletedColumn = FromDatabaseEntity(entry.ColumnsViewModel.DateDeletedColumn), + DateModifiedColumn = FromDatabaseEntity(entry.ColumnsViewModel.DateModifiedColumn), + GitCommitAuthorColumn = FromDatabaseEntity(entry.ColumnsViewModel.GitCommitAuthorColumn), + GitLastCommitDateColumn = FromDatabaseEntity(entry.ColumnsViewModel.GitLastCommitDateColumn), + GitLastCommitMessageColumn = FromDatabaseEntity(entry.ColumnsViewModel.GitLastCommitMessageColumn), + GitLastCommitShaColumn = FromDatabaseEntity(entry.ColumnsViewModel.GitLastCommitShaColumn), + GitStatusColumn = FromDatabaseEntity(entry.ColumnsViewModel.GitStatusColumn), + ItemTypeColumn = FromDatabaseEntity(entry.ColumnsViewModel.ItemTypeColumn), + NameColumn = FromDatabaseEntity(entry.ColumnsViewModel.NameColumn), + OriginalPathColumn = FromDatabaseEntity(entry.ColumnsViewModel.OriginalPathColumn), + PathColumn = FromDatabaseEntity(entry.ColumnsViewModel.PathColumn), + SizeColumn = FromDatabaseEntity(entry.ColumnsViewModel.SizeColumn), + StatusColumn = FromDatabaseEntity(entry.ColumnsViewModel.StatusColumn), + TagColumn = FromDatabaseEntity(entry.ColumnsViewModel.TagColumn), }, - new() - { - IncludeFields = true - }); + DirectoryGroupByDateUnit = entry.DirectoryGroupByDateUnit, + DirectoryGroupDirection = entry.DirectoryGroupDirection, + DirectoryGroupOption = entry.DirectoryGroupOption, + DirectorySortDirection = entry.DirectorySortDirection, + DirectorySortOption = entry.DirectorySortOption, + IsAdaptiveLayoutOverridden = entry.IsAdaptiveLayoutOverridden, + LayoutMode = entry.LayoutMode, + SortDirectoriesAlongsideFiles = entry.SortDirectoriesAlongsideFiles, + SortFilesFirst = entry.SortFilesFirst, + }; } - // Methods - - public LayoutPreferencesItem? GetPreferences(string? filePath = null, ulong? frn = null) + private LayoutPreferencesDatabaseItem FromDatabaseEntity(Server.Data.LayoutPreferences entry) { - return FindPreferences(filePath, frn)?.LayoutPreferencesManager; + return new() + { + FilePath = entry.FilePath, + Frn = entry.Frn, + Id = entry.Id, + LayoutPreferencesManager = FromDatabaseEntity(entry.LayoutPreferencesManager), + }; } - public void SetPreferences(string filePath, ulong? frn, LayoutPreferencesItem? preferencesItem) + private Server.Data.ColumnPreferencesItem ToDatabaseEntity(DetailsLayoutColumnItem entry) { - // Get a collection (or create, if doesn't exist) - var col = _database.GetCollection("layoutprefs"); - - var tmp = FindPreferences(filePath, frn); - - if (tmp is null) - { - if (preferencesItem is not null) - { - // Insert new tagged file (Id will be auto-incremented) - var newPref = new LayoutPreferencesDatabaseItem() - { - FilePath = filePath, - Frn = frn, - LayoutPreferencesManager = preferencesItem - }; - - col.Insert(newPref); - col.EnsureIndex(x => x.Frn); - col.EnsureIndex(x => x.FilePath); - } - } - else + return new() { - if (preferencesItem is not null) - { - // Update file tag - tmp.LayoutPreferencesManager = preferencesItem; - col.Update(tmp); - } - else - { - // Remove file tag - col.Delete(tmp.Id); - } - } + NormalMaxLength = entry.NormalMaxLength, + UserCollapsed = entry.UserCollapsed, + UserLengthPixels = entry.UserLengthPixels, + }; } - public void ResetAll(Func? predicate = null) + [return: NotNullIfNotNull(nameof(entry))] + private Server.Data.LayoutPreferencesItem? ToDatabaseEntity(LayoutPreferencesItem? entry) { - var col = _database.GetCollection("layoutprefs"); - - if (predicate is null) + if (entry is null) { - col.Delete(Query.All()); + return null; } - else - { - col.Delete(x => predicate(x)); - } - } - - public void ApplyToAll(Action updateAction, Func? predicate = null) - { - var col = _database.GetCollection("layoutprefs"); - var allDocs = predicate is null ? col.FindAll() : col.Find(x => predicate(x)); - - allDocs.ForEach(x => updateAction(x)); - col.Update(allDocs); + return new() + { + ColumnsViewModel = + { + DateCreatedColumn = ToDatabaseEntity(entry.ColumnsViewModel.DateCreatedColumn), + DateDeletedColumn = ToDatabaseEntity(entry.ColumnsViewModel.DateDeletedColumn), + DateModifiedColumn = ToDatabaseEntity(entry.ColumnsViewModel.DateModifiedColumn), + GitCommitAuthorColumn = ToDatabaseEntity(entry.ColumnsViewModel.GitCommitAuthorColumn), + GitLastCommitDateColumn = ToDatabaseEntity(entry.ColumnsViewModel.GitLastCommitDateColumn), + GitLastCommitMessageColumn = ToDatabaseEntity(entry.ColumnsViewModel.GitLastCommitMessageColumn), + GitLastCommitShaColumn = ToDatabaseEntity(entry.ColumnsViewModel.GitLastCommitShaColumn), + GitStatusColumn = ToDatabaseEntity(entry.ColumnsViewModel.GitStatusColumn), + ItemTypeColumn = ToDatabaseEntity(entry.ColumnsViewModel.ItemTypeColumn), + NameColumn = ToDatabaseEntity(entry.ColumnsViewModel.NameColumn), + OriginalPathColumn = ToDatabaseEntity(entry.ColumnsViewModel.OriginalPathColumn), + PathColumn = ToDatabaseEntity(entry.ColumnsViewModel.PathColumn), + SizeColumn = ToDatabaseEntity(entry.ColumnsViewModel.SizeColumn), + StatusColumn = ToDatabaseEntity(entry.ColumnsViewModel.StatusColumn), + TagColumn = ToDatabaseEntity(entry.ColumnsViewModel.TagColumn), + }, + DirectoryGroupByDateUnit = entry.DirectoryGroupByDateUnit, + DirectoryGroupDirection = entry.DirectoryGroupDirection, + DirectoryGroupOption = entry.DirectoryGroupOption, + DirectorySortDirection = entry.DirectorySortDirection, + DirectorySortOption = entry.DirectorySortOption, + IsAdaptiveLayoutOverridden = entry.IsAdaptiveLayoutOverridden, + LayoutMode = entry.LayoutMode, + SortDirectoriesAlongsideFiles = entry.SortDirectoriesAlongsideFiles, + SortFilesFirst = entry.SortFilesFirst, + }; } - public void Import(string json) + // Methods + public LayoutPreferencesItem? GetPreferences(string? filePath = null, ulong? frn = null) { - var dataValues = JsonSerializer.DeserializeArray(json); - - var col = _database.GetCollection("layoutprefs"); - - col.Delete(Query.All()); - col.InsertBulk(dataValues.Select(x => x.AsDocument)); + return FromDatabaseEntity(_database.GetPreferences(filePath, frn)); } - public string Export() + public void SetPreferences(string filePath, ulong? frn, LayoutPreferencesItem? preferencesItem) { - return JsonSerializer.Serialize(new BsonArray(_database.GetCollection("layoutprefs").FindAll())); + _database.SetPreferences(filePath, frn, ToDatabaseEntity(preferencesItem)); } - private LayoutPreferencesDatabaseItem? FindPreferences(string? filePath = null, ulong? frn = null) + public void ResetAll(Func? predicate = null) { - // Get a collection (or create, if doesn't exist) - var col = _database.GetCollection("layoutprefs"); - - if (filePath is not null) - { - var tmp = col.FindOne(x => x.FilePath == filePath); - if (tmp is not null) - { - if (frn is not null) - { - // Keep entry updated - tmp.Frn = frn; - col.Update(tmp); - } - - return tmp; - } - } - - if (frn is not null) - { - var tmp = col.FindOne(x => x.Frn == frn); - if (tmp is not null) - { - if (filePath is not null) - { - // Keep entry updated - tmp.FilePath = filePath; - col.Update(tmp); - } - - return tmp; - } - } - - return null; + _database.ResetAll(item => predicate?.Invoke(FromDatabaseEntity(item)) ?? true); } - private void EnsureDatabaseVersion(string filename) + public void ApplyToAll(Action updateAction, Func? predicate = null) { - // NOTE: - // For more information, visit - // https://github.com/mbdavid/LiteDB/blob/master/LiteDB/Engine/Engine/Upgrade.cs - - var buffer = new byte[8192 * 2]; - - using var stream = new SystemIO.FileStream(filename, SystemIO.FileMode.Open, SystemIO.FileAccess.Read, SystemIO.FileShare.ReadWrite); - - // Read first 16k - stream.Read(buffer, 0, buffer.Length); - - // Check if v7 (plain or encrypted) - if (Encoding.UTF8.GetString(buffer, 25, "** This is a LiteDB file **".Length) == "** This is a LiteDB file **" && - buffer[52] == 7) - { - // version 4.1.4 - return; - } - - // Re-create database with correct version - SystemIO.File.Delete(filename); + _database.ApplyToAll(item => updateAction.Invoke(FromDatabaseEntity(item)), + item => predicate?.Invoke(FromDatabaseEntity(item)) ?? true); } - // De-constructor & Disposer - - ~LayoutPreferencesDatabaseManager() + public void Import(string json) { - Dispose(); + _database.Import(json); } - public void Dispose() + public string Export() { - _database.Dispose(); + return _database.Export(); } } } diff --git a/src/Files.App/Helpers/Layout/LayoutPreferencesItem.cs b/src/Files.App/Helpers/Layout/LayoutPreferencesItem.cs index 321e9ac16c97..617fbb3b4bbf 100644 --- a/src/Files.App/Helpers/Layout/LayoutPreferencesItem.cs +++ b/src/Files.App/Helpers/Layout/LayoutPreferencesItem.cs @@ -1,6 +1,8 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. +using Files.App.Server.Data.Enums; + namespace Files.App.Helpers { /// @@ -12,22 +14,22 @@ public sealed class LayoutPreferencesItem private IUserSettingsService UserSettingsService { get; } = Ioc.Default.GetRequiredService(); - // Fields + // Properties - public ColumnsViewModel ColumnsViewModel; + public ColumnsViewModel ColumnsViewModel { get; set; } - public bool SortDirectoriesAlongsideFiles; - public bool SortFilesFirst; - public bool IsAdaptiveLayoutOverridden; + public bool SortDirectoriesAlongsideFiles { get; set; } + public bool SortFilesFirst { get; set; } + public bool IsAdaptiveLayoutOverridden { get; set; } - public FolderLayoutModes LayoutMode; + public FolderLayoutModes LayoutMode { get; set; } - public SortOption DirectorySortOption; - public SortDirection DirectorySortDirection; - public SortDirection DirectoryGroupDirection; + public SortOption DirectorySortOption { get; set; } + public SortDirection DirectorySortDirection { get; set; } + public SortDirection DirectoryGroupDirection { get; set; } - public GroupOption DirectoryGroupOption; - public GroupByDateUnit DirectoryGroupByDateUnit; + public GroupOption DirectoryGroupOption { get; set; } + public GroupByDateUnit DirectoryGroupByDateUnit { get; set; } // Constructor diff --git a/src/Files.App/Helpers/Layout/LayoutPreferencesManager.cs b/src/Files.App/Helpers/Layout/LayoutPreferencesManager.cs index 3a1549702fdf..845238898b02 100644 --- a/src/Files.App/Helpers/Layout/LayoutPreferencesManager.cs +++ b/src/Files.App/Helpers/Layout/LayoutPreferencesManager.cs @@ -1,6 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. +using Files.App.Server.Data.Enums; using System.Text.Json; using Windows.Storage; @@ -18,7 +19,7 @@ public sealed class LayoutPreferencesManager : ObservableObject // Fields private static readonly Lazy _databaseInstance = - new(() => new LayoutPreferencesDatabaseManager(LayoutSettingsDbPath, true)); + new(() => new LayoutPreferencesDatabaseManager()); private readonly FolderLayoutModes? _rootLayoutMode; @@ -35,7 +36,7 @@ public bool IsAdaptiveLayoutEnabled get => !LayoutPreferencesItem.IsAdaptiveLayoutOverridden; set { - if (SetProperty(ref LayoutPreferencesItem.IsAdaptiveLayoutOverridden, !value, nameof(IsAdaptiveLayoutEnabled))) + if (SetProperty(item => item.IsAdaptiveLayoutOverridden, item => item.IsAdaptiveLayoutOverridden = !value, nameof(IsAdaptiveLayoutEnabled))) LayoutPreferencesUpdateRequired?.Invoke(this, new LayoutPreferenceEventArgs(LayoutPreferencesItem, true)); } } @@ -45,7 +46,7 @@ public FolderLayoutModes LayoutMode get => _rootLayoutMode ?? LayoutPreferencesItem.LayoutMode; set { - if (SetProperty(ref LayoutPreferencesItem.LayoutMode, value, nameof(LayoutMode))) + if (SetProperty(item => item.LayoutMode, item => item.LayoutMode = value, nameof(LayoutMode))) LayoutPreferencesUpdateRequired?.Invoke(this, new LayoutPreferenceEventArgs(LayoutPreferencesItem)); } } @@ -55,7 +56,7 @@ public SortOption DirectorySortOption get => LayoutPreferencesItem.DirectorySortOption; set { - if (SetProperty(ref LayoutPreferencesItem.DirectorySortOption, value, nameof(DirectorySortOption))) + if (SetProperty(item => item.DirectorySortOption, item => item.DirectorySortOption = value, nameof(DirectorySortOption))) { LayoutPreferencesUpdateRequired?.Invoke(this, new LayoutPreferenceEventArgs(LayoutPreferencesItem)); SortOptionPreferenceUpdated?.Invoke(this, DirectorySortOption); @@ -68,7 +69,7 @@ public GroupOption DirectoryGroupOption get => LayoutPreferencesItem.DirectoryGroupOption; set { - if (SetProperty(ref LayoutPreferencesItem.DirectoryGroupOption, value, nameof(DirectoryGroupOption))) + if (SetProperty(item => item.DirectoryGroupOption, item => item.DirectoryGroupOption = value, nameof(DirectoryGroupOption))) { LayoutPreferencesUpdateRequired?.Invoke(this, new LayoutPreferenceEventArgs(LayoutPreferencesItem)); GroupOptionPreferenceUpdated?.Invoke(this, DirectoryGroupOption); @@ -81,7 +82,7 @@ public SortDirection DirectorySortDirection get => LayoutPreferencesItem.DirectorySortDirection; set { - if (SetProperty(ref LayoutPreferencesItem.DirectorySortDirection, value, nameof(DirectorySortDirection))) + if (SetProperty(item => item.DirectorySortDirection, item => item.DirectorySortDirection = value, nameof(DirectorySortDirection))) { LayoutPreferencesUpdateRequired?.Invoke(this, new LayoutPreferenceEventArgs(LayoutPreferencesItem)); SortDirectionPreferenceUpdated?.Invoke(this, DirectorySortDirection); @@ -94,7 +95,7 @@ public SortDirection DirectoryGroupDirection get => LayoutPreferencesItem.DirectoryGroupDirection; set { - if (SetProperty(ref LayoutPreferencesItem.DirectoryGroupDirection, value, nameof(DirectoryGroupDirection))) + if (SetProperty(item => item.DirectoryGroupDirection, item => item.DirectoryGroupDirection = value, nameof(DirectoryGroupDirection))) { LayoutPreferencesUpdateRequired?.Invoke(this, new LayoutPreferenceEventArgs(LayoutPreferencesItem)); GroupDirectionPreferenceUpdated?.Invoke(this, DirectoryGroupDirection); @@ -107,7 +108,7 @@ public GroupByDateUnit DirectoryGroupByDateUnit get => LayoutPreferencesItem.DirectoryGroupByDateUnit; set { - if (SetProperty(ref LayoutPreferencesItem.DirectoryGroupByDateUnit, value, nameof(DirectoryGroupByDateUnit))) + if (SetProperty(item => item.DirectoryGroupByDateUnit, item => item.DirectoryGroupByDateUnit = value, nameof(DirectoryGroupByDateUnit))) { LayoutPreferencesUpdateRequired?.Invoke(this, new LayoutPreferenceEventArgs(LayoutPreferencesItem)); GroupByDateUnitPreferenceUpdated?.Invoke(this, DirectoryGroupByDateUnit); @@ -120,7 +121,7 @@ public bool SortDirectoriesAlongsideFiles get => LayoutPreferencesItem.SortDirectoriesAlongsideFiles; set { - if (SetProperty(ref LayoutPreferencesItem.SortDirectoriesAlongsideFiles, value, nameof(SortDirectoriesAlongsideFiles))) + if (SetProperty(item => item.SortDirectoriesAlongsideFiles, item => item.SortDirectoriesAlongsideFiles = value, nameof(SortDirectoriesAlongsideFiles))) { LayoutPreferencesUpdateRequired?.Invoke(this, new LayoutPreferenceEventArgs(LayoutPreferencesItem)); SortDirectoriesAlongsideFilesPreferenceUpdated?.Invoke(this, SortDirectoriesAlongsideFiles); @@ -133,7 +134,7 @@ public bool SortFilesFirst get => LayoutPreferencesItem.SortFilesFirst; set { - if (SetProperty(ref LayoutPreferencesItem.SortFilesFirst, value, nameof(SortFilesFirst))) + if (SetProperty(item => item.SortFilesFirst, item => item.SortFilesFirst = value, nameof(SortFilesFirst))) { LayoutPreferencesUpdateRequired?.Invoke(this, new LayoutPreferenceEventArgs(LayoutPreferencesItem)); SortFilesFirstPreferenceUpdated?.Invoke(this, SortFilesFirst); @@ -146,7 +147,7 @@ public ColumnsViewModel ColumnsViewModel get => LayoutPreferencesItem.ColumnsViewModel; set { - SetProperty(ref LayoutPreferencesItem.ColumnsViewModel, value, nameof(ColumnsViewModel)); + SetProperty(item => item.ColumnsViewModel, item => item.ColumnsViewModel = value, nameof(ColumnsViewModel)); LayoutPreferencesUpdateRequired?.Invoke(this, new LayoutPreferenceEventArgs(LayoutPreferencesItem)); } } @@ -589,5 +590,19 @@ private static void SetLayoutPreferencesToDatabase(string path, ulong? frn, Layo dbInstance.SetPreferences(path, frn, preferencesItem); } + + private bool SetProperty(Func prop, Action update, string propertyName) + { + var oldValue = prop(LayoutPreferencesItem); + update(LayoutPreferencesItem); + + if (EqualityComparer.Default.Equals(prop(LayoutPreferencesItem), oldValue)) + { + return false; + } + + OnPropertyChanged(propertyName); + return true; + } } } diff --git a/src/Files.Core/Helpers/NativeFindStorageItemHelper.cs b/src/Files.App/Helpers/NativeFindStorageItemHelper.cs similarity index 99% rename from src/Files.Core/Helpers/NativeFindStorageItemHelper.cs rename to src/Files.App/Helpers/NativeFindStorageItemHelper.cs index d61030f42b64..bc9f3bc9f0f7 100644 --- a/src/Files.Core/Helpers/NativeFindStorageItemHelper.cs +++ b/src/Files.App/Helpers/NativeFindStorageItemHelper.cs @@ -4,7 +4,7 @@ using System.Runtime.InteropServices; using System.Runtime.InteropServices.ComTypes; -namespace Files.Core.Helpers +namespace Files.App.Helpers { /// /// Provides a bunch of Win32API for native find storage items. diff --git a/src/Files.App/Helpers/Navigation/NavigationHelpers.cs b/src/Files.App/Helpers/Navigation/NavigationHelpers.cs index e5f89b04dd4a..b6c369daad1e 100644 --- a/src/Files.App/Helpers/Navigation/NavigationHelpers.cs +++ b/src/Files.App/Helpers/Navigation/NavigationHelpers.cs @@ -1,6 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. +using Files.App.Server.Data.Enums; using Files.Shared.Helpers; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Media.Imaging; diff --git a/src/Files.App/Helpers/UI/AppThemeResourcesHelper.cs b/src/Files.App/Helpers/UI/AppThemeResourcesHelper.cs index 696fe518b8b1..ee2443808ad4 100644 --- a/src/Files.App/Helpers/UI/AppThemeResourcesHelper.cs +++ b/src/Files.App/Helpers/UI/AppThemeResourcesHelper.cs @@ -2,8 +2,8 @@ // Licensed under the MIT License. See the LICENSE. using CommunityToolkit.WinUI.Helpers; -using Files.Core.Services; -using Files.Core.Services.Settings; +using Files.App.Services; +using Files.App.Services.Settings; using System; namespace Files.App.Helpers diff --git a/src/Files.App/Services/DateTimeFormatter/AbstractDateTimeFormatter.cs b/src/Files.App/Services/DateTimeFormatter/AbstractDateTimeFormatter.cs index 3a26859280b1..3c39434c87ea 100644 --- a/src/Files.App/Services/DateTimeFormatter/AbstractDateTimeFormatter.cs +++ b/src/Files.App/Services/DateTimeFormatter/AbstractDateTimeFormatter.cs @@ -1,6 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. +using Files.App.Server.Data.Enums; using System.Globalization; using Windows.Globalization; diff --git a/src/Files.Core/Services/DateTimeFormatter/IDateTimeFormatter.cs b/src/Files.App/Services/DateTimeFormatter/IDateTimeFormatter.cs similarity index 80% rename from src/Files.Core/Services/DateTimeFormatter/IDateTimeFormatter.cs rename to src/Files.App/Services/DateTimeFormatter/IDateTimeFormatter.cs index 5ddf0ce3612f..92c356942975 100644 --- a/src/Files.Core/Services/DateTimeFormatter/IDateTimeFormatter.cs +++ b/src/Files.App/Services/DateTimeFormatter/IDateTimeFormatter.cs @@ -1,7 +1,9 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Services.DateTimeFormatter +using Files.App.Server.Data.Enums; + +namespace Files.App.Services.DateTimeFormatter { public interface IDateTimeFormatter { diff --git a/src/Files.Core/Services/DateTimeFormatter/IDateTimeFormatterFactory.cs b/src/Files.App/Services/DateTimeFormatter/IDateTimeFormatterFactory.cs similarity index 82% rename from src/Files.Core/Services/DateTimeFormatter/IDateTimeFormatterFactory.cs rename to src/Files.App/Services/DateTimeFormatter/IDateTimeFormatterFactory.cs index 1b5a146cdba4..4b4a7a00eac1 100644 --- a/src/Files.Core/Services/DateTimeFormatter/IDateTimeFormatterFactory.cs +++ b/src/Files.App/Services/DateTimeFormatter/IDateTimeFormatterFactory.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Services.DateTimeFormatter +namespace Files.App.Services.DateTimeFormatter { public interface IDateTimeFormatterFactory { diff --git a/src/Files.Core/Services/DateTimeFormatter/ITimeSpanLabel.cs b/src/Files.App/Services/DateTimeFormatter/ITimeSpanLabel.cs similarity index 81% rename from src/Files.Core/Services/DateTimeFormatter/ITimeSpanLabel.cs rename to src/Files.App/Services/DateTimeFormatter/ITimeSpanLabel.cs index 774fb4c86281..1e68929bcaa5 100644 --- a/src/Files.Core/Services/DateTimeFormatter/ITimeSpanLabel.cs +++ b/src/Files.App/Services/DateTimeFormatter/ITimeSpanLabel.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Services.DateTimeFormatter +namespace Files.App.Services.DateTimeFormatter { public interface ITimeSpanLabel { diff --git a/src/Files.App/Services/DateTimeFormatter/UserDateTimeFormatter.cs b/src/Files.App/Services/DateTimeFormatter/UserDateTimeFormatter.cs index f1ad9be7cfff..e2bea27c5ecd 100644 --- a/src/Files.App/Services/DateTimeFormatter/UserDateTimeFormatter.cs +++ b/src/Files.App/Services/DateTimeFormatter/UserDateTimeFormatter.cs @@ -1,6 +1,8 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. +using Files.App.Server.Data.Enums; + namespace Files.App.Services.DateTimeFormatter { internal sealed class UserDateTimeFormatter : IDateTimeFormatter diff --git a/src/Files.App/Services/DialogService.cs b/src/Files.App/Services/DialogService.cs index 95f24887b506..298533b94d04 100644 --- a/src/Files.App/Services/DialogService.cs +++ b/src/Files.App/Services/DialogService.cs @@ -4,10 +4,10 @@ using System.Collections.Frozen; using Files.App.Dialogs; using Files.App.ViewModels.Dialogs; -using Files.Core.Services; -using Files.Core.ViewModels.Dialogs; -using Files.Core.ViewModels.Dialogs.AddItemDialog; -using Files.Core.ViewModels.Dialogs.FileSystemDialog; +using Files.App.Services; +using Files.App.ViewModels.Dialogs; +using Files.App.ViewModels.Dialogs.AddItemDialog; +using Files.App.ViewModels.Dialogs.FileSystemDialog; using Microsoft.Extensions.Logging; using Microsoft.UI.Xaml.Controls; using Windows.Foundation.Metadata; diff --git a/src/Files.Core/Services/IAddItemService.cs b/src/Files.App/Services/IAddItemService.cs similarity index 94% rename from src/Files.Core/Services/IAddItemService.cs rename to src/Files.App/Services/IAddItemService.cs index 61f47979ca41..e3420dade763 100644 --- a/src/Files.Core/Services/IAddItemService.cs +++ b/src/Files.App/Services/IAddItemService.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Services +namespace Files.App.Services { /// /// A service to retrieve available item types diff --git a/src/Files.Core/Services/IDialogService.cs b/src/Files.App/Services/IDialogService.cs similarity index 92% rename from src/Files.Core/Services/IDialogService.cs rename to src/Files.App/Services/IDialogService.cs index cbd92fa8c6b8..389cc1377a80 100644 --- a/src/Files.Core/Services/IDialogService.cs +++ b/src/Files.App/Services/IDialogService.cs @@ -1,10 +1,10 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -using Files.Core.ViewModels.Dialogs; -using Files.Core.Data.Enums; +using Files.App.ViewModels.Dialogs; +using Files.App.Data.Enums; -namespace Files.Core.Services +namespace Files.App.Services { /// /// A service to manage dialogs. diff --git a/src/Files.Core/Services/IFileExplorerService.cs b/src/Files.App/Services/IFileExplorerService.cs similarity index 98% rename from src/Files.Core/Services/IFileExplorerService.cs rename to src/Files.App/Services/IFileExplorerService.cs index 49893c4ba8e8..23f2fcb43f3a 100644 --- a/src/Files.Core/Services/IFileExplorerService.cs +++ b/src/Files.App/Services/IFileExplorerService.cs @@ -3,7 +3,7 @@ using Files.Core.Storage.LocatableStorage; -namespace Files.Core.Services +namespace Files.App.Services { /// /// A service that interacts with the system file explorer. diff --git a/src/Files.Core/Services/IFileTagsService.cs b/src/Files.App/Services/IFileTagsService.cs similarity index 97% rename from src/Files.Core/Services/IFileTagsService.cs rename to src/Files.App/Services/IFileTagsService.cs index b4705a9f0d88..e55d41cddd54 100644 --- a/src/Files.Core/Services/IFileTagsService.cs +++ b/src/Files.App/Services/IFileTagsService.cs @@ -1,10 +1,10 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -using Files.Core.ViewModels.FileTags; +using Files.App.ViewModels.FileTags; using Files.Core.Storage.LocatableStorage; -namespace Files.Core.Services +namespace Files.App.Services { /// /// Represents a service used to manage file tags. diff --git a/src/Files.Core/Services/IImageService.cs b/src/Files.App/Services/IImageService.cs similarity index 97% rename from src/Files.Core/Services/IImageService.cs rename to src/Files.App/Services/IImageService.cs index 940484631ec1..b5a0436d905d 100644 --- a/src/Files.Core/Services/IImageService.cs +++ b/src/Files.App/Services/IImageService.cs @@ -4,7 +4,7 @@ using Files.Core.Storage; using Files.Shared.Utils; -namespace Files.Core.Services +namespace Files.App.Services { /// /// Represents a service used for data to image conversion. diff --git a/src/Files.Core/Services/IJumpListService.cs b/src/Files.App/Services/IJumpListService.cs similarity index 90% rename from src/Files.Core/Services/IJumpListService.cs rename to src/Files.App/Services/IJumpListService.cs index 36577ac6d683..a08c15718e4b 100644 --- a/src/Files.Core/Services/IJumpListService.cs +++ b/src/Files.App/Services/IJumpListService.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Services +namespace Files.App.Services { public interface IJumpListService { diff --git a/src/Files.Core/Services/ILocalizationService.cs b/src/Files.App/Services/ILocalizationService.cs similarity index 86% rename from src/Files.Core/Services/ILocalizationService.cs rename to src/Files.App/Services/ILocalizationService.cs index a34a6388034b..c713a9fa7e06 100644 --- a/src/Files.Core/Services/ILocalizationService.cs +++ b/src/Files.App/Services/ILocalizationService.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Services +namespace Files.App.Services { public interface ILocalizationService { diff --git a/src/Files.Core/Services/INetworkDrivesService.cs b/src/Files.App/Services/INetworkDrivesService.cs similarity index 96% rename from src/Files.Core/Services/INetworkDrivesService.cs rename to src/Files.App/Services/INetworkDrivesService.cs index 4359965152b7..a246b1fe8437 100644 --- a/src/Files.Core/Services/INetworkDrivesService.cs +++ b/src/Files.App/Services/INetworkDrivesService.cs @@ -3,7 +3,7 @@ using Files.Core.Storage.LocatableStorage; -namespace Files.Core.Services +namespace Files.App.Services { public interface INetworkDrivesService { diff --git a/src/Files.Core/Services/IPreviewPopupProvider.cs b/src/Files.App/Services/IPreviewPopupProvider.cs similarity index 95% rename from src/Files.Core/Services/IPreviewPopupProvider.cs rename to src/Files.App/Services/IPreviewPopupProvider.cs index c0e51d5fc037..1e0d014d7232 100644 --- a/src/Files.Core/Services/IPreviewPopupProvider.cs +++ b/src/Files.App/Services/IPreviewPopupProvider.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Services +namespace Files.App.Services { public interface IPreviewPopupProvider { diff --git a/src/Files.Core/Services/IPreviewPopupService.cs b/src/Files.App/Services/IPreviewPopupService.cs similarity index 91% rename from src/Files.Core/Services/IPreviewPopupService.cs rename to src/Files.App/Services/IPreviewPopupService.cs index bd48197df110..b1a641d8cf59 100644 --- a/src/Files.Core/Services/IPreviewPopupService.cs +++ b/src/Files.App/Services/IPreviewPopupService.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Services +namespace Files.App.Services { public interface IPreviewPopupService : INotifyPropertyChanged { diff --git a/src/Files.Core/Services/IQuickAccessService.cs b/src/Files.App/Services/IQuickAccessService.cs similarity index 100% rename from src/Files.Core/Services/IQuickAccessService.cs rename to src/Files.App/Services/IQuickAccessService.cs diff --git a/src/Files.Core/Services/IRemovableDrivesService.cs b/src/Files.App/Services/IRemovableDrivesService.cs similarity index 97% rename from src/Files.Core/Services/IRemovableDrivesService.cs rename to src/Files.App/Services/IRemovableDrivesService.cs index 5a1099cd7d57..73719e2aa809 100644 --- a/src/Files.Core/Services/IRemovableDrivesService.cs +++ b/src/Files.App/Services/IRemovableDrivesService.cs @@ -3,7 +3,7 @@ using Files.Core.Storage.LocatableStorage; -namespace Files.Core.Services +namespace Files.App.Services { /// /// Represents a service to enumerate drives and create a storage device watcher diff --git a/src/Files.Core/Services/IResourcesService.cs b/src/Files.App/Services/IResourcesService.cs similarity index 98% rename from src/Files.Core/Services/IResourcesService.cs rename to src/Files.App/Services/IResourcesService.cs index d935a02400b3..bbd1ab7a6299 100644 --- a/src/Files.Core/Services/IResourcesService.cs +++ b/src/Files.App/Services/IResourcesService.cs @@ -3,7 +3,7 @@ using System.Drawing; -namespace Files.Core.Services +namespace Files.App.Services { /// /// Contains methods related to modifying various app theme resources diff --git a/src/Files.Core/Services/IStartMenuService.cs b/src/Files.App/Services/IStartMenuService.cs similarity index 98% rename from src/Files.Core/Services/IStartMenuService.cs rename to src/Files.App/Services/IStartMenuService.cs index 2a9b5c93e627..3c77efae11a3 100644 --- a/src/Files.Core/Services/IStartMenuService.cs +++ b/src/Files.App/Services/IStartMenuService.cs @@ -1,6 +1,6 @@ using Files.Core.Storage; -namespace Files.Core.Services +namespace Files.App.Services { /// /// A service that manages actions associated with system Start Menu. diff --git a/src/Files.Core/Services/IThreadingService.cs b/src/Files.App/Services/IThreadingService.cs similarity index 89% rename from src/Files.Core/Services/IThreadingService.cs rename to src/Files.App/Services/IThreadingService.cs index 6a973fc8bb24..4e54a25edd06 100644 --- a/src/Files.Core/Services/IThreadingService.cs +++ b/src/Files.App/Services/IThreadingService.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Services +namespace Files.App.Services { public interface IThreadingService { diff --git a/src/Files.Core/Services/IUpdateService.cs b/src/Files.App/Services/IUpdateService.cs similarity index 97% rename from src/Files.Core/Services/IUpdateService.cs rename to src/Files.App/Services/IUpdateService.cs index 7ca86d940619..c1157232a2cc 100644 --- a/src/Files.Core/Services/IUpdateService.cs +++ b/src/Files.App/Services/IUpdateService.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Services +namespace Files.App.Services { public interface IUpdateService : INotifyPropertyChanged { diff --git a/src/Files.Core/Services/IVolumeInfoFactory.cs b/src/Files.App/Services/IVolumeInfoFactory.cs similarity index 86% rename from src/Files.Core/Services/IVolumeInfoFactory.cs rename to src/Files.App/Services/IVolumeInfoFactory.cs index bc7cbc203aed..d4d0e9a87c33 100644 --- a/src/Files.Core/Services/IVolumeInfoFactory.cs +++ b/src/Files.App/Services/IVolumeInfoFactory.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Services +namespace Files.App.Services { public interface IVolumeInfoFactory { diff --git a/src/Files.App/Services/PreviewPopupProviders/SeerProProvider.cs b/src/Files.App/Services/PreviewPopupProviders/SeerProProvider.cs index af03cd732893..e8a9c5248e14 100644 --- a/src/Files.App/Services/PreviewPopupProviders/SeerProProvider.cs +++ b/src/Files.App/Services/PreviewPopupProviders/SeerProProvider.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -using Files.Core.Services; +using Files.App.Services; using System.Runtime.InteropServices; using Vanara.PInvoke; diff --git a/src/Files.App/Services/Settings/AppSettingsService.cs b/src/Files.App/Services/Settings/AppSettingsService.cs index 6c503bd854a6..d7669fd5ac0e 100644 --- a/src/Files.App/Services/Settings/AppSettingsService.cs +++ b/src/Files.App/Services/Settings/AppSettingsService.cs @@ -2,7 +2,7 @@ // Licensed under the MIT License. See the LICENSE. using Files.App.Utils.Serialization; -using Files.Core.Services.Settings; +using Files.App.Services.Settings; using Microsoft.AppCenter.Analytics; namespace Files.App.Services.Settings diff --git a/src/Files.App/Services/Settings/ApplicationSettingsService.cs b/src/Files.App/Services/Settings/ApplicationSettingsService.cs index 7ff6fe9df722..44fbf60eb930 100644 --- a/src/Files.App/Services/Settings/ApplicationSettingsService.cs +++ b/src/Files.App/Services/Settings/ApplicationSettingsService.cs @@ -2,7 +2,7 @@ // Licensed under the MIT License. See the LICENSE. using Files.App.Utils.Serialization; -using Files.Core.Services.Settings; +using Files.App.Services.Settings; namespace Files.App.Services.Settings { diff --git a/src/Files.App/Services/Settings/FileTagsSettingsService.cs b/src/Files.App/Services/Settings/FileTagsSettingsService.cs index db3a22303c9c..67e851d44865 100644 --- a/src/Files.App/Services/Settings/FileTagsSettingsService.cs +++ b/src/Files.App/Services/Settings/FileTagsSettingsService.cs @@ -6,8 +6,8 @@ using Files.App.Helpers; using Files.App.Utils.Serialization; using Files.App.Utils.Serialization.Implementation; -using Files.Core.Services.Settings; -using Files.Core.ViewModels.FileTags; +using Files.App.Services.Settings; +using Files.App.ViewModels.FileTags; using Microsoft.Extensions.Logging; using System.IO; using Windows.Storage; diff --git a/src/Files.App/Services/Settings/FoldersSettingsService.cs b/src/Files.App/Services/Settings/FoldersSettingsService.cs index d99aeb8fa63a..88f192e86071 100644 --- a/src/Files.App/Services/Settings/FoldersSettingsService.cs +++ b/src/Files.App/Services/Settings/FoldersSettingsService.cs @@ -1,6 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. +using Files.App.Server.Data.Enums; using Microsoft.AppCenter.Analytics; namespace Files.App.Services.Settings diff --git a/src/Files.Core/Services/Settings/IAppSettingsService.cs b/src/Files.App/Services/Settings/IAppSettingsService.cs similarity index 95% rename from src/Files.Core/Services/Settings/IAppSettingsService.cs rename to src/Files.App/Services/Settings/IAppSettingsService.cs index 5e6fc19e3ac2..52afe32b31d8 100644 --- a/src/Files.Core/Services/Settings/IAppSettingsService.cs +++ b/src/Files.App/Services/Settings/IAppSettingsService.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Services.Settings +namespace Files.App.Services.Settings { public interface IAppSettingsService : IBaseSettingsService, INotifyPropertyChanged { diff --git a/src/Files.Core/Services/Settings/IAppearanceSettingsService.cs b/src/Files.App/Services/Settings/IAppearanceSettingsService.cs similarity index 97% rename from src/Files.Core/Services/Settings/IAppearanceSettingsService.cs rename to src/Files.App/Services/Settings/IAppearanceSettingsService.cs index 96414d38ad45..04a572775951 100644 --- a/src/Files.Core/Services/Settings/IAppearanceSettingsService.cs +++ b/src/Files.App/Services/Settings/IAppearanceSettingsService.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Services.Settings +namespace Files.App.Services.Settings { public interface IAppearanceSettingsService : IBaseSettingsService, INotifyPropertyChanged { diff --git a/src/Files.Core/Services/Settings/IApplicationSettingsService.cs b/src/Files.App/Services/Settings/IApplicationSettingsService.cs similarity index 93% rename from src/Files.Core/Services/Settings/IApplicationSettingsService.cs rename to src/Files.App/Services/Settings/IApplicationSettingsService.cs index cbc685dd5631..293b9bb18af7 100644 --- a/src/Files.Core/Services/Settings/IApplicationSettingsService.cs +++ b/src/Files.App/Services/Settings/IApplicationSettingsService.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Services.Settings +namespace Files.App.Services.Settings { public interface IApplicationSettingsService : IBaseSettingsService { diff --git a/src/Files.Core/Services/Settings/IBaseSettingsService.cs b/src/Files.App/Services/Settings/IBaseSettingsService.cs similarity index 78% rename from src/Files.Core/Services/Settings/IBaseSettingsService.cs rename to src/Files.App/Services/Settings/IBaseSettingsService.cs index 402921208fc1..3aa7eb99da1b 100644 --- a/src/Files.Core/Services/Settings/IBaseSettingsService.cs +++ b/src/Files.App/Services/Settings/IBaseSettingsService.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Services.Settings +namespace Files.App.Services.Settings { public interface IBaseSettingsService { diff --git a/src/Files.Core/Services/Settings/IFileTagsSettingsService.cs b/src/Files.App/Services/Settings/IFileTagsSettingsService.cs similarity index 89% rename from src/Files.Core/Services/Settings/IFileTagsSettingsService.cs rename to src/Files.App/Services/Settings/IFileTagsSettingsService.cs index ad52a4d426fa..355838295230 100644 --- a/src/Files.Core/Services/Settings/IFileTagsSettingsService.cs +++ b/src/Files.App/Services/Settings/IFileTagsSettingsService.cs @@ -1,9 +1,9 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -using Files.Core.ViewModels.FileTags; +using Files.App.ViewModels.FileTags; -namespace Files.Core.Services.Settings +namespace Files.App.Services.Settings { public interface IFileTagsSettingsService : IBaseSettingsService { diff --git a/src/Files.Core/Services/Settings/IFoldersSettingsService.cs b/src/Files.App/Services/Settings/IFoldersSettingsService.cs similarity index 97% rename from src/Files.Core/Services/Settings/IFoldersSettingsService.cs rename to src/Files.App/Services/Settings/IFoldersSettingsService.cs index ef64079e1fd2..ce03281bc66a 100644 --- a/src/Files.Core/Services/Settings/IFoldersSettingsService.cs +++ b/src/Files.App/Services/Settings/IFoldersSettingsService.cs @@ -1,7 +1,9 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Services.Settings +using Files.App.Server.Data.Enums; + +namespace Files.App.Services.Settings { public interface IFoldersSettingsService : IBaseSettingsService, INotifyPropertyChanged { diff --git a/src/Files.Core/Services/Settings/IGeneralSettingsService.cs b/src/Files.App/Services/Settings/IGeneralSettingsService.cs similarity index 99% rename from src/Files.Core/Services/Settings/IGeneralSettingsService.cs rename to src/Files.App/Services/Settings/IGeneralSettingsService.cs index ce50638ff58f..cf27c996664b 100644 --- a/src/Files.Core/Services/Settings/IGeneralSettingsService.cs +++ b/src/Files.App/Services/Settings/IGeneralSettingsService.cs @@ -1,11 +1,11 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -using Files.Core.Data.Enums; +using Files.App.Data.Enums; using System.Collections.Generic; using System.ComponentModel; -namespace Files.Core.Services.Settings +namespace Files.App.Services.Settings { public interface IGeneralSettingsService : IBaseSettingsService, INotifyPropertyChanged { diff --git a/src/Files.Core/Services/Settings/IInfoPaneSettingsService.cs b/src/Files.App/Services/Settings/IInfoPaneSettingsService.cs similarity index 96% rename from src/Files.Core/Services/Settings/IInfoPaneSettingsService.cs rename to src/Files.App/Services/Settings/IInfoPaneSettingsService.cs index 20c759f9c637..18e7a1f0d0dc 100644 --- a/src/Files.Core/Services/Settings/IInfoPaneSettingsService.cs +++ b/src/Files.App/Services/Settings/IInfoPaneSettingsService.cs @@ -3,7 +3,7 @@ using System.ComponentModel; -namespace Files.Core.Services.Settings +namespace Files.App.Services.Settings { public interface IInfoPaneSettingsService : IBaseSettingsService, INotifyPropertyChanged { diff --git a/src/Files.Core/Services/Settings/ILayoutSettingsService.cs b/src/Files.App/Services/Settings/ILayoutSettingsService.cs similarity index 98% rename from src/Files.Core/Services/Settings/ILayoutSettingsService.cs rename to src/Files.App/Services/Settings/ILayoutSettingsService.cs index 468ae732a1ee..9f18a8a2b067 100644 --- a/src/Files.Core/Services/Settings/ILayoutSettingsService.cs +++ b/src/Files.App/Services/Settings/ILayoutSettingsService.cs @@ -1,9 +1,10 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -using Files.Core.Data.Enums; +using Files.App.Data.Enums; +using Files.App.Server.Data.Enums; -namespace Files.Core.Services.Settings +namespace Files.App.Services.Settings { public interface ILayoutSettingsService : IBaseSettingsService, INotifyPropertyChanged { diff --git a/src/Files.Core/Services/Settings/IUserSettingsService.cs b/src/Files.App/Services/Settings/IUserSettingsService.cs similarity index 94% rename from src/Files.Core/Services/Settings/IUserSettingsService.cs rename to src/Files.App/Services/Settings/IUserSettingsService.cs index e830c2a315f1..8e6484e4d553 100644 --- a/src/Files.Core/Services/Settings/IUserSettingsService.cs +++ b/src/Files.App/Services/Settings/IUserSettingsService.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Services.Settings +namespace Files.App.Services.Settings { public interface IUserSettingsService : IBaseSettingsService { diff --git a/src/Files.App/Services/Settings/InfoPaneSettingsService.cs b/src/Files.App/Services/Settings/InfoPaneSettingsService.cs index 2a22c68d1e95..42a5ba932e74 100644 --- a/src/Files.App/Services/Settings/InfoPaneSettingsService.cs +++ b/src/Files.App/Services/Settings/InfoPaneSettingsService.cs @@ -2,7 +2,7 @@ // Licensed under the MIT License. See the LICENSE. using Files.App.Utils.Serialization; -using Files.Core.Services.Settings; +using Files.App.Services.Settings; using Microsoft.AppCenter.Analytics; using System; diff --git a/src/Files.App/Services/Settings/LayoutSettingsService.cs b/src/Files.App/Services/Settings/LayoutSettingsService.cs index 5ee22d14ba51..04de8b70b566 100644 --- a/src/Files.App/Services/Settings/LayoutSettingsService.cs +++ b/src/Files.App/Services/Settings/LayoutSettingsService.cs @@ -1,6 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. +using Files.App.Server.Data.Enums; using Microsoft.AppCenter.Analytics; namespace Files.App.Services.Settings diff --git a/src/Files.App/Services/Settings/UserSettingsService.cs b/src/Files.App/Services/Settings/UserSettingsService.cs index 1bbe9049eb70..b378e3969d34 100644 --- a/src/Files.App/Services/Settings/UserSettingsService.cs +++ b/src/Files.App/Services/Settings/UserSettingsService.cs @@ -4,7 +4,7 @@ using CommunityToolkit.Mvvm.DependencyInjection; using Files.App.Utils.Serialization; using Files.App.Utils.Serialization.Implementation; -using Files.Core.Services.Settings; +using Files.App.Services.Settings; using Files.Shared.Extensions; using System.Collections.Generic; using System.IO; diff --git a/src/Files.Core/Services/SizeProvider/CachedSizeProvider.cs b/src/Files.App/Services/SizeProvider/CachedSizeProvider.cs similarity index 95% rename from src/Files.Core/Services/SizeProvider/CachedSizeProvider.cs rename to src/Files.App/Services/SizeProvider/CachedSizeProvider.cs index 6fcc1ac6b503..738aed5ef537 100644 --- a/src/Files.Core/Services/SizeProvider/CachedSizeProvider.cs +++ b/src/Files.App/Services/SizeProvider/CachedSizeProvider.cs @@ -1,15 +1,15 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -using Files.Core.Extensions; +using Files.App.Extensions; using System; using System.Collections.Concurrent; using System.IO; using System.Threading; using System.Threading.Tasks; -using static Files.Core.Helpers.NativeFindStorageItemHelper; +using static Files.App.Helpers.NativeFindStorageItemHelper; -namespace Files.Core.Services.SizeProvider +namespace Files.App.Services.SizeProvider { public sealed class CachedSizeProvider : ISizeProvider { diff --git a/src/Files.Core/Services/SizeProvider/DrivesSizeProvider.cs b/src/Files.App/Services/SizeProvider/DrivesSizeProvider.cs similarity index 98% rename from src/Files.Core/Services/SizeProvider/DrivesSizeProvider.cs rename to src/Files.App/Services/SizeProvider/DrivesSizeProvider.cs index d10c54515127..b552e716fb31 100644 --- a/src/Files.Core/Services/SizeProvider/DrivesSizeProvider.cs +++ b/src/Files.App/Services/SizeProvider/DrivesSizeProvider.cs @@ -8,7 +8,7 @@ using System.Threading; using System.Threading.Tasks; -namespace Files.Core.Services.SizeProvider +namespace Files.App.Services.SizeProvider { public sealed class DrivesSizeProvider : ISizeProvider { diff --git a/src/Files.Core/Services/SizeProvider/ISizeProvider.cs b/src/Files.App/Services/SizeProvider/ISizeProvider.cs similarity index 91% rename from src/Files.Core/Services/SizeProvider/ISizeProvider.cs rename to src/Files.App/Services/SizeProvider/ISizeProvider.cs index 5907cd8ef14c..a366bd4017e5 100644 --- a/src/Files.Core/Services/SizeProvider/ISizeProvider.cs +++ b/src/Files.App/Services/SizeProvider/ISizeProvider.cs @@ -5,7 +5,7 @@ using System.Threading; using System.Threading.Tasks; -namespace Files.Core.Services.SizeProvider +namespace Files.App.Services.SizeProvider { public interface ISizeProvider : IDisposable { diff --git a/src/Files.Core/Services/SizeProvider/NoSizeProvider.cs b/src/Files.App/Services/SizeProvider/NoSizeProvider.cs similarity index 93% rename from src/Files.Core/Services/SizeProvider/NoSizeProvider.cs rename to src/Files.App/Services/SizeProvider/NoSizeProvider.cs index faa14a4a606f..6bdfe17a72bc 100644 --- a/src/Files.Core/Services/SizeProvider/NoSizeProvider.cs +++ b/src/Files.App/Services/SizeProvider/NoSizeProvider.cs @@ -5,7 +5,7 @@ using System.Threading; using System.Threading.Tasks; -namespace Files.Core.Services.SizeProvider +namespace Files.App.Services.SizeProvider { public sealed class NoSizeProvider : ISizeProvider { diff --git a/src/Files.Core/Services/SizeProvider/SizeChangedEventArgs.cs b/src/Files.App/Services/SizeProvider/SizeChangedEventArgs.cs similarity index 91% rename from src/Files.Core/Services/SizeProvider/SizeChangedEventArgs.cs rename to src/Files.App/Services/SizeProvider/SizeChangedEventArgs.cs index 668e97531856..ba452686c938 100644 --- a/src/Files.Core/Services/SizeProvider/SizeChangedEventArgs.cs +++ b/src/Files.App/Services/SizeProvider/SizeChangedEventArgs.cs @@ -3,7 +3,7 @@ using System; -namespace Files.Core.Services.SizeProvider +namespace Files.App.Services.SizeProvider { public sealed class SizeChangedEventArgs : EventArgs { diff --git a/src/Files.Core/Services/SizeProvider/SizeChangedValueState.cs b/src/Files.App/Services/SizeProvider/SizeChangedValueState.cs similarity index 80% rename from src/Files.Core/Services/SizeProvider/SizeChangedValueState.cs rename to src/Files.App/Services/SizeProvider/SizeChangedValueState.cs index 7720bd21fe0d..c76284d45ebc 100644 --- a/src/Files.Core/Services/SizeProvider/SizeChangedValueState.cs +++ b/src/Files.App/Services/SizeProvider/SizeChangedValueState.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Services.SizeProvider +namespace Files.App.Services.SizeProvider { public enum SizeChangedValueState : ushort { diff --git a/src/Files.App/Services/UserSizeProvider.cs b/src/Files.App/Services/UserSizeProvider.cs index 2fda8908b1ac..3d5e5b4645d5 100644 --- a/src/Files.App/Services/UserSizeProvider.cs +++ b/src/Files.App/Services/UserSizeProvider.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -using Files.Core.Services.SizeProvider; +using Files.App.Services.SizeProvider; namespace Files.App.Services { diff --git a/src/Files.App/UserControls/DataGridHeader.xaml.cs b/src/Files.App/UserControls/DataGridHeader.xaml.cs index 77c77e995fc7..d307722388a8 100644 --- a/src/Files.App/UserControls/DataGridHeader.xaml.cs +++ b/src/Files.App/UserControls/DataGridHeader.xaml.cs @@ -6,6 +6,7 @@ using System.ComponentModel; using System.Runtime.CompilerServices; using System.Windows.Input; +using Files.App.Server.Data.Enums; namespace Files.App.UserControls { diff --git a/src/Files.App/UserControls/FilePreviews/MediaPreview.xaml.cs b/src/Files.App/UserControls/FilePreviews/MediaPreview.xaml.cs index 12fae7a720be..4b765872919d 100644 --- a/src/Files.App/UserControls/FilePreviews/MediaPreview.xaml.cs +++ b/src/Files.App/UserControls/FilePreviews/MediaPreview.xaml.cs @@ -1,6 +1,6 @@ using CommunityToolkit.Mvvm.DependencyInjection; using Files.App.ViewModels.Previews; -using Files.Core.Services.Settings; +using Files.App.Services.Settings; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Input; diff --git a/src/Files.App/UserControls/Menus/FileTagsContextMenu.cs b/src/Files.App/UserControls/Menus/FileTagsContextMenu.cs index 7fa4c885939c..cd77efeb51ea 100644 --- a/src/Files.App/UserControls/Menus/FileTagsContextMenu.cs +++ b/src/Files.App/UserControls/Menus/FileTagsContextMenu.cs @@ -77,7 +77,7 @@ private void RemoveFileTag(IEnumerable selectedListedItems, TagViewM if (existingTags.Contains(removed.Uid)) { var tagList = existingTags.Except(new[] { removed.Uid }).ToArray(); - selectedItem.FileTags = tagList.Any() ? tagList : null; + selectedItem.FileTags = tagList; } } } diff --git a/src/Files.Core/Utils/Cloud/CloudDriveSyncStatus.cs b/src/Files.App/Utils/Cloud/CloudDriveSyncStatus.cs similarity index 92% rename from src/Files.Core/Utils/Cloud/CloudDriveSyncStatus.cs rename to src/Files.App/Utils/Cloud/CloudDriveSyncStatus.cs index 39e57798efb8..10f1d964ccaf 100644 --- a/src/Files.Core/Utils/Cloud/CloudDriveSyncStatus.cs +++ b/src/Files.App/Utils/Cloud/CloudDriveSyncStatus.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Utils.Cloud +namespace Files.App.Utils.Cloud { public enum CloudDriveSyncStatus { diff --git a/src/Files.App/Utils/Cloud/CloudDriveSyncStatusUI.cs b/src/Files.App/Utils/Cloud/CloudDriveSyncStatusUI.cs index 39ed63b939c4..54904d55743b 100644 --- a/src/Files.App/Utils/Cloud/CloudDriveSyncStatusUI.cs +++ b/src/Files.App/Utils/Cloud/CloudDriveSyncStatusUI.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -using Files.Core.Utils.Cloud; +using Files.App.Utils.Cloud; using Microsoft.UI.Xaml; namespace Files.App.Utils.Cloud diff --git a/src/Files.Core/Utils/Cloud/CloudProvider.cs b/src/Files.App/Utils/Cloud/CloudProvider.cs similarity index 95% rename from src/Files.Core/Utils/Cloud/CloudProvider.cs rename to src/Files.App/Utils/Cloud/CloudProvider.cs index 775bd9f29f7f..9444423327ac 100644 --- a/src/Files.Core/Utils/Cloud/CloudProvider.cs +++ b/src/Files.App/Utils/Cloud/CloudProvider.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Utils.Cloud +namespace Files.App.Utils.Cloud { public sealed class CloudProvider : ICloudProvider { diff --git a/src/Files.Core/Utils/Cloud/CloudProviders.cs b/src/Files.App/Utils/Cloud/CloudProviders.cs similarity index 92% rename from src/Files.Core/Utils/Cloud/CloudProviders.cs rename to src/Files.App/Utils/Cloud/CloudProviders.cs index c8c37d7edeac..b87bde844ac6 100644 --- a/src/Files.Core/Utils/Cloud/CloudProviders.cs +++ b/src/Files.App/Utils/Cloud/CloudProviders.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Utils.Cloud +namespace Files.App.Utils.Cloud { public enum CloudProviders { diff --git a/src/Files.App/Utils/Cloud/Detector/AbstractCloudDetector.cs b/src/Files.App/Utils/Cloud/Detector/AbstractCloudDetector.cs index ece54f67d6f5..4ec15924ed76 100644 --- a/src/Files.App/Utils/Cloud/Detector/AbstractCloudDetector.cs +++ b/src/Files.App/Utils/Cloud/Detector/AbstractCloudDetector.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -using Files.Core.Utils.Cloud; +using Files.App.Utils.Cloud; using System.Collections.Generic; using System.Threading.Tasks; diff --git a/src/Files.App/Utils/Cloud/Detector/BoxCloudDetector.cs b/src/Files.App/Utils/Cloud/Detector/BoxCloudDetector.cs index 195f12be6180..64dd61658340 100644 --- a/src/Files.App/Utils/Cloud/Detector/BoxCloudDetector.cs +++ b/src/Files.App/Utils/Cloud/Detector/BoxCloudDetector.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -using Files.Core.Utils.Cloud; +using Files.App.Utils.Cloud; using System.IO; using Windows.Storage; diff --git a/src/Files.App/Utils/Cloud/Detector/CloudDetector.cs b/src/Files.App/Utils/Cloud/Detector/CloudDetector.cs index e710e8b6830a..07fed1f7880b 100644 --- a/src/Files.App/Utils/Cloud/Detector/CloudDetector.cs +++ b/src/Files.App/Utils/Cloud/Detector/CloudDetector.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -using Files.Core.Utils.Cloud; +using Files.App.Utils.Cloud; namespace Files.App.Utils.Cloud { diff --git a/src/Files.App/Utils/Cloud/Detector/DropBoxCloudDetector.cs b/src/Files.App/Utils/Cloud/Detector/DropBoxCloudDetector.cs index 92f071cef596..9f15379da01b 100644 --- a/src/Files.App/Utils/Cloud/Detector/DropBoxCloudDetector.cs +++ b/src/Files.App/Utils/Cloud/Detector/DropBoxCloudDetector.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -using Files.Core.Utils.Cloud; +using Files.App.Utils.Cloud; using System.IO; using System.Text.Json; using Windows.Storage; diff --git a/src/Files.App/Utils/Cloud/Detector/GenericCloudDetector.cs b/src/Files.App/Utils/Cloud/Detector/GenericCloudDetector.cs index ffd207db6752..b5e11225a01f 100644 --- a/src/Files.App/Utils/Cloud/Detector/GenericCloudDetector.cs +++ b/src/Files.App/Utils/Cloud/Detector/GenericCloudDetector.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -using Files.Core.Utils.Cloud; +using Files.App.Utils.Cloud; namespace Files.App.Utils.Cloud { diff --git a/src/Files.App/Utils/Cloud/Detector/SynologyDriveCloudDetector.cs b/src/Files.App/Utils/Cloud/Detector/SynologyDriveCloudDetector.cs index 422de0664ba4..2c98b557cf48 100644 --- a/src/Files.App/Utils/Cloud/Detector/SynologyDriveCloudDetector.cs +++ b/src/Files.App/Utils/Cloud/Detector/SynologyDriveCloudDetector.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -using Files.Core.Utils.Cloud; +using Files.App.Utils.Cloud; using Microsoft.Data.Sqlite; using System.IO; using Windows.Storage; diff --git a/src/Files.Core/Utils/Cloud/ICloudDetector.cs b/src/Files.App/Utils/Cloud/ICloudDetector.cs similarity index 85% rename from src/Files.Core/Utils/Cloud/ICloudDetector.cs rename to src/Files.App/Utils/Cloud/ICloudDetector.cs index 36e284860323..c5877a25e133 100644 --- a/src/Files.Core/Utils/Cloud/ICloudDetector.cs +++ b/src/Files.App/Utils/Cloud/ICloudDetector.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Utils.Cloud +namespace Files.App.Utils.Cloud { public interface ICloudDetector { diff --git a/src/Files.Core/Utils/Cloud/ICloudProvider.cs b/src/Files.App/Utils/Cloud/ICloudProvider.cs similarity index 90% rename from src/Files.Core/Utils/Cloud/ICloudProvider.cs rename to src/Files.App/Utils/Cloud/ICloudProvider.cs index 9bdffe25f690..97aae4ab62a7 100644 --- a/src/Files.Core/Utils/Cloud/ICloudProvider.cs +++ b/src/Files.App/Utils/Cloud/ICloudProvider.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Utils.Cloud +namespace Files.App.Utils.Cloud { public interface ICloudProvider : IEquatable { diff --git a/src/Files.Core/Utils/CommandLine/CommandLineParser.cs b/src/Files.App/Utils/CommandLine/CommandLineParser.cs similarity index 99% rename from src/Files.Core/Utils/CommandLine/CommandLineParser.cs rename to src/Files.App/Utils/CommandLine/CommandLineParser.cs index d531a4ddb671..edfd19a02de6 100644 --- a/src/Files.Core/Utils/CommandLine/CommandLineParser.cs +++ b/src/Files.App/Utils/CommandLine/CommandLineParser.cs @@ -3,7 +3,7 @@ using System.IO; -namespace Files.Core.Utils.CommandLine +namespace Files.App.Utils.CommandLine { /// /// Provides static helper for parsing of command line arguments on Windows. diff --git a/src/Files.Core/Utils/CommandLine/ParsedCommand.cs b/src/Files.App/Utils/CommandLine/ParsedCommand.cs similarity index 94% rename from src/Files.Core/Utils/CommandLine/ParsedCommand.cs rename to src/Files.App/Utils/CommandLine/ParsedCommand.cs index a90b97a56014..e9a39040fd01 100644 --- a/src/Files.Core/Utils/CommandLine/ParsedCommand.cs +++ b/src/Files.App/Utils/CommandLine/ParsedCommand.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Utils.CommandLine +namespace Files.App.Utils.CommandLine { /// /// Represents a parsed command node on Windows. diff --git a/src/Files.Core/Utils/CommandLine/ParsedCommands.cs b/src/Files.App/Utils/CommandLine/ParsedCommands.cs similarity index 85% rename from src/Files.Core/Utils/CommandLine/ParsedCommands.cs rename to src/Files.App/Utils/CommandLine/ParsedCommands.cs index e4436f6e3f58..1b8e90fe6a28 100644 --- a/src/Files.Core/Utils/CommandLine/ParsedCommands.cs +++ b/src/Files.App/Utils/CommandLine/ParsedCommands.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.Utils.CommandLine +namespace Files.App.Utils.CommandLine { /// /// Represents a collection of parsed command. diff --git a/src/Files.App/Utils/FileTags/FileTagsHelper.cs b/src/Files.App/Utils/FileTags/FileTagsHelper.cs index aff18d0ba138..323ce48f70fc 100644 --- a/src/Files.App/Utils/FileTags/FileTagsHelper.cs +++ b/src/Files.App/Utils/FileTags/FileTagsHelper.cs @@ -11,11 +11,9 @@ namespace Files.App.Utils.FileTags { public static class FileTagsHelper { - public static string FileTagsDbPath => IO.Path.Combine(ApplicationData.Current.LocalFolder.Path, "filetags.db"); + private static readonly Lazy dbInstance = new(() => new()); - private static readonly Lazy dbInstance = new(() => new FileTagsDb(FileTagsDbPath, true)); - - public static FileTagsDb GetDbInstance() => dbInstance.Value; + public static Server.Database.FileTagsDatabase GetDbInstance() => dbInstance.Value; public static string[] ReadFileTag(string filePath) { diff --git a/src/Files.App/Utils/Storage/Collection/GroupingHelper.cs b/src/Files.App/Utils/Storage/Collection/GroupingHelper.cs index f35299cd59ad..adec6f08d825 100644 --- a/src/Files.App/Utils/Storage/Collection/GroupingHelper.cs +++ b/src/Files.App/Utils/Storage/Collection/GroupingHelper.cs @@ -1,6 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. +using Files.App.Server.Data.Enums; using Windows.Storage; namespace Files.App.Utils.Storage diff --git a/src/Files.App/Utils/Storage/Collection/SortingHelper.cs b/src/Files.App/Utils/Storage/Collection/SortingHelper.cs index f8af4a99643c..e9ed4324fad0 100644 --- a/src/Files.App/Utils/Storage/Collection/SortingHelper.cs +++ b/src/Files.App/Utils/Storage/Collection/SortingHelper.cs @@ -1,6 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. +using Files.App.Server.Data.Enums; using Windows.Storage; namespace Files.App.Utils.Storage diff --git a/src/Files.App/Utils/Storage/Enumerators/Win32StorageEnumerator.cs b/src/Files.App/Utils/Storage/Enumerators/Win32StorageEnumerator.cs index 0e56a6511b4e..c8afa24c02ed 100644 --- a/src/Files.App/Utils/Storage/Enumerators/Win32StorageEnumerator.cs +++ b/src/Files.App/Utils/Storage/Enumerators/Win32StorageEnumerator.cs @@ -1,12 +1,12 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -using Files.Core.Services.SizeProvider; +using Files.App.Services.SizeProvider; using Files.Shared.Helpers; using System.IO; using Vanara.PInvoke; using Windows.Storage; -using static Files.Core.Helpers.NativeFindStorageItemHelper; +using static Files.App.Helpers.NativeFindStorageItemHelper; using FileAttributes = System.IO.FileAttributes; namespace Files.App.Utils.Storage diff --git a/src/Files.App/Utils/Storage/Helpers/FolderHelpers.cs b/src/Files.App/Utils/Storage/Helpers/FolderHelpers.cs index f73bdc766c78..a10670497dab 100644 --- a/src/Files.App/Utils/Storage/Helpers/FolderHelpers.cs +++ b/src/Files.App/Utils/Storage/Helpers/FolderHelpers.cs @@ -2,7 +2,7 @@ // Licensed under the MIT License. See the LICENSE. using System.IO; -using static Files.Core.Helpers.NativeFindStorageItemHelper; +using static Files.App.Helpers.NativeFindStorageItemHelper; namespace Files.App.Utils.Storage { diff --git a/src/Files.App/Utils/Storage/Operations/FileOperationsHelpers.cs b/src/Files.App/Utils/Storage/Operations/FileOperationsHelpers.cs index c8011f7f9008..131823ec9d8a 100644 --- a/src/Files.App/Utils/Storage/Operations/FileOperationsHelpers.cs +++ b/src/Files.App/Utils/Storage/Operations/FileOperationsHelpers.cs @@ -862,7 +862,7 @@ private static void UpdateFileTagsDb(ShellFileOperations2.ShellFileOpEventArgs e { if (operationType == "copy") { - var tag = dbInstance.GetTags(sourcePath); + var tag = dbInstance.GetTags(sourcePath, null); dbInstance.SetTags(destination, FileTagsHelper.GetFileFRN(destination), tag); // copy tag to new files using var si = new ShellItem(destination); diff --git a/src/Files.App/Utils/Storage/Search/FolderSearch.cs b/src/Files.App/Utils/Storage/Search/FolderSearch.cs index cc76814f217a..eedee3b49c5a 100644 --- a/src/Files.App/Utils/Storage/Search/FolderSearch.cs +++ b/src/Files.App/Utils/Storage/Search/FolderSearch.cs @@ -6,9 +6,9 @@ using Windows.Storage; using Windows.Storage.FileProperties; using Windows.Storage.Search; -using static Files.Core.Helpers.NativeFindStorageItemHelper; +using static Files.App.Helpers.NativeFindStorageItemHelper; using FileAttributes = System.IO.FileAttributes; -using WIN32_FIND_DATA = Files.Core.Helpers.NativeFindStorageItemHelper.WIN32_FIND_DATA; +using WIN32_FIND_DATA = Files.App.Helpers.NativeFindStorageItemHelper.WIN32_FIND_DATA; namespace Files.App.Utils.Storage { diff --git a/src/Files.App/Utils/Storage/StorageItems/VirtualStorageItem.cs b/src/Files.App/Utils/Storage/StorageItems/VirtualStorageItem.cs index f72c891f7125..237d9703fc34 100644 --- a/src/Files.App/Utils/Storage/StorageItems/VirtualStorageItem.cs +++ b/src/Files.App/Utils/Storage/StorageItems/VirtualStorageItem.cs @@ -6,7 +6,7 @@ using Windows.Foundation; using Windows.Storage; using Windows.Storage.FileProperties; -using static Files.Core.Helpers.NativeFindStorageItemHelper; +using static Files.App.Helpers.NativeFindStorageItemHelper; namespace Files.App.Utils.Storage { diff --git a/src/Files.Core/ViewModels/Dialogs/AddItemDialog/AddItemDialogListItemViewModel.cs b/src/Files.App/ViewModels/Dialogs/AddItemDialog/AddItemDialogListItemViewModel.cs similarity index 89% rename from src/Files.Core/ViewModels/Dialogs/AddItemDialog/AddItemDialogListItemViewModel.cs rename to src/Files.App/ViewModels/Dialogs/AddItemDialog/AddItemDialogListItemViewModel.cs index dd7652877692..dfc6664b0bdd 100644 --- a/src/Files.Core/ViewModels/Dialogs/AddItemDialog/AddItemDialogListItemViewModel.cs +++ b/src/Files.App/ViewModels/Dialogs/AddItemDialog/AddItemDialogListItemViewModel.cs @@ -3,7 +3,7 @@ using Files.Shared.Utils; -namespace Files.Core.ViewModels.Dialogs.AddItemDialog +namespace Files.App.ViewModels.Dialogs.AddItemDialog { public sealed class AddItemDialogListItemViewModel : ObservableObject { diff --git a/src/Files.Core/ViewModels/Dialogs/AddItemDialog/AddItemDialogViewModel.cs b/src/Files.App/ViewModels/Dialogs/AddItemDialog/AddItemDialogViewModel.cs similarity index 97% rename from src/Files.Core/ViewModels/Dialogs/AddItemDialog/AddItemDialogViewModel.cs rename to src/Files.App/ViewModels/Dialogs/AddItemDialog/AddItemDialogViewModel.cs index cac034bbd467..7dba993f3b47 100644 --- a/src/Files.Core/ViewModels/Dialogs/AddItemDialog/AddItemDialogViewModel.cs +++ b/src/Files.App/ViewModels/Dialogs/AddItemDialog/AddItemDialogViewModel.cs @@ -4,7 +4,7 @@ using Files.Shared; using Files.Shared.Utils; -namespace Files.Core.ViewModels.Dialogs.AddItemDialog +namespace Files.App.ViewModels.Dialogs.AddItemDialog { public sealed class AddItemDialogViewModel : ObservableObject { diff --git a/src/Files.Core/ViewModels/Dialogs/BaseDialogViewModel.cs b/src/Files.App/ViewModels/Dialogs/BaseDialogViewModel.cs similarity index 97% rename from src/Files.Core/ViewModels/Dialogs/BaseDialogViewModel.cs rename to src/Files.App/ViewModels/Dialogs/BaseDialogViewModel.cs index f21138ae7bc2..5808e3d139da 100644 --- a/src/Files.Core/ViewModels/Dialogs/BaseDialogViewModel.cs +++ b/src/Files.App/ViewModels/Dialogs/BaseDialogViewModel.cs @@ -3,7 +3,7 @@ using System.Windows.Input; -namespace Files.Core.ViewModels.Dialogs +namespace Files.App.ViewModels.Dialogs { /// /// Serves as the base dialog view model containing reusable, diff --git a/src/Files.Core/ViewModels/Dialogs/CredentialDialogViewModel.cs b/src/Files.App/ViewModels/Dialogs/CredentialDialogViewModel.cs similarity index 96% rename from src/Files.Core/ViewModels/Dialogs/CredentialDialogViewModel.cs rename to src/Files.App/ViewModels/Dialogs/CredentialDialogViewModel.cs index 28362bd77387..de1e7eeb206a 100644 --- a/src/Files.Core/ViewModels/Dialogs/CredentialDialogViewModel.cs +++ b/src/Files.App/ViewModels/Dialogs/CredentialDialogViewModel.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.ViewModels.Dialogs +namespace Files.App.ViewModels.Dialogs { public sealed class CredentialDialogViewModel : ObservableObject { diff --git a/src/Files.Core/ViewModels/Dialogs/ElevateConfirmDialogViewModel.cs b/src/Files.App/ViewModels/Dialogs/ElevateConfirmDialogViewModel.cs similarity index 81% rename from src/Files.Core/ViewModels/Dialogs/ElevateConfirmDialogViewModel.cs rename to src/Files.App/ViewModels/Dialogs/ElevateConfirmDialogViewModel.cs index 7cc5a694f715..6bda9586d11c 100644 --- a/src/Files.Core/ViewModels/Dialogs/ElevateConfirmDialogViewModel.cs +++ b/src/Files.App/ViewModels/Dialogs/ElevateConfirmDialogViewModel.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.ViewModels.Dialogs +namespace Files.App.ViewModels.Dialogs { public sealed class ElevateConfirmDialogViewModel : ObservableObject { diff --git a/src/Files.Core/ViewModels/Dialogs/FileSystemDialog/BaseFileSystemDialogItemViewModel.cs b/src/Files.App/ViewModels/Dialogs/FileSystemDialog/BaseFileSystemDialogItemViewModel.cs similarity index 94% rename from src/Files.Core/ViewModels/Dialogs/FileSystemDialog/BaseFileSystemDialogItemViewModel.cs rename to src/Files.App/ViewModels/Dialogs/FileSystemDialog/BaseFileSystemDialogItemViewModel.cs index cada4967e56f..d30d2b5cfd66 100644 --- a/src/Files.Core/ViewModels/Dialogs/FileSystemDialog/BaseFileSystemDialogItemViewModel.cs +++ b/src/Files.App/ViewModels/Dialogs/FileSystemDialog/BaseFileSystemDialogItemViewModel.cs @@ -4,7 +4,7 @@ using Files.Shared.Utils; using System.IO; -namespace Files.Core.ViewModels.Dialogs.FileSystemDialog +namespace Files.App.ViewModels.Dialogs.FileSystemDialog { public abstract class BaseFileSystemDialogItemViewModel : ObservableObject { diff --git a/src/Files.Core/ViewModels/Dialogs/FileSystemDialog/FileSystemDialogConflictItemViewModel.cs b/src/Files.App/ViewModels/Dialogs/FileSystemDialog/FileSystemDialogConflictItemViewModel.cs similarity index 95% rename from src/Files.Core/ViewModels/Dialogs/FileSystemDialog/FileSystemDialogConflictItemViewModel.cs rename to src/Files.App/ViewModels/Dialogs/FileSystemDialog/FileSystemDialogConflictItemViewModel.cs index 003eddef0fe2..fec4ba818baf 100644 --- a/src/Files.Core/ViewModels/Dialogs/FileSystemDialog/FileSystemDialogConflictItemViewModel.cs +++ b/src/Files.App/ViewModels/Dialogs/FileSystemDialog/FileSystemDialogConflictItemViewModel.cs @@ -1,10 +1,10 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -using Files.Core.Data.Enums; +using Files.App.Data.Enums; using System.IO; -namespace Files.Core.ViewModels.Dialogs.FileSystemDialog +namespace Files.App.ViewModels.Dialogs.FileSystemDialog { public sealed class FileSystemDialogConflictItemViewModel : BaseFileSystemDialogItemViewModel, IFileSystemDialogConflictItemViewModel { diff --git a/src/Files.Core/ViewModels/Dialogs/FileSystemDialog/FileSystemDialogDefaultItemViewModel.cs b/src/Files.App/ViewModels/Dialogs/FileSystemDialog/FileSystemDialogDefaultItemViewModel.cs similarity index 77% rename from src/Files.Core/ViewModels/Dialogs/FileSystemDialog/FileSystemDialogDefaultItemViewModel.cs rename to src/Files.App/ViewModels/Dialogs/FileSystemDialog/FileSystemDialogDefaultItemViewModel.cs index 7bb9ee8e2f25..df0f9ebedcdf 100644 --- a/src/Files.Core/ViewModels/Dialogs/FileSystemDialog/FileSystemDialogDefaultItemViewModel.cs +++ b/src/Files.App/ViewModels/Dialogs/FileSystemDialog/FileSystemDialogDefaultItemViewModel.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.ViewModels.Dialogs.FileSystemDialog +namespace Files.App.ViewModels.Dialogs.FileSystemDialog { public sealed class FileSystemDialogDefaultItemViewModel : BaseFileSystemDialogItemViewModel { diff --git a/src/Files.Core/ViewModels/Dialogs/FileSystemDialog/FileSystemDialogViewModel.cs b/src/Files.App/ViewModels/Dialogs/FileSystemDialog/FileSystemDialogViewModel.cs similarity index 99% rename from src/Files.Core/ViewModels/Dialogs/FileSystemDialog/FileSystemDialogViewModel.cs rename to src/Files.App/ViewModels/Dialogs/FileSystemDialog/FileSystemDialogViewModel.cs index db6438f42fd3..d755224f5b66 100644 --- a/src/Files.Core/ViewModels/Dialogs/FileSystemDialog/FileSystemDialogViewModel.cs +++ b/src/Files.App/ViewModels/Dialogs/FileSystemDialog/FileSystemDialogViewModel.cs @@ -1,10 +1,10 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -using Files.Core.Data.Enums; +using Files.App.Data.Enums; using Files.Shared.Extensions; -namespace Files.Core.ViewModels.Dialogs.FileSystemDialog +namespace Files.App.ViewModels.Dialogs.FileSystemDialog { public sealed class FileSystemDialogViewModel : BaseDialogViewModel, IRecipient { diff --git a/src/Files.Core/ViewModels/Dialogs/FileSystemDialog/IFileSystemDialogConflictItemViewModel.cs b/src/Files.App/ViewModels/Dialogs/FileSystemDialog/IFileSystemDialogConflictItemViewModel.cs similarity index 79% rename from src/Files.Core/ViewModels/Dialogs/FileSystemDialog/IFileSystemDialogConflictItemViewModel.cs rename to src/Files.App/ViewModels/Dialogs/FileSystemDialog/IFileSystemDialogConflictItemViewModel.cs index 5bdf7549a833..e9506d6c4756 100644 --- a/src/Files.Core/ViewModels/Dialogs/FileSystemDialog/IFileSystemDialogConflictItemViewModel.cs +++ b/src/Files.App/ViewModels/Dialogs/FileSystemDialog/IFileSystemDialogConflictItemViewModel.cs @@ -1,9 +1,9 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -using Files.Core.Data.Enums; +using Files.App.Data.Enums; -namespace Files.Core.ViewModels.Dialogs.FileSystemDialog +namespace Files.App.ViewModels.Dialogs.FileSystemDialog { public interface IFileSystemDialogConflictItemViewModel { diff --git a/src/Files.Core/ViewModels/Dialogs/FileTooLargeDialogViewModel.cs b/src/Files.App/ViewModels/Dialogs/FileTooLargeDialogViewModel.cs similarity index 88% rename from src/Files.Core/ViewModels/Dialogs/FileTooLargeDialogViewModel.cs rename to src/Files.App/ViewModels/Dialogs/FileTooLargeDialogViewModel.cs index 62c277fbc0a1..00d6eab283ea 100644 --- a/src/Files.Core/ViewModels/Dialogs/FileTooLargeDialogViewModel.cs +++ b/src/Files.App/ViewModels/Dialogs/FileTooLargeDialogViewModel.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.ViewModels.Dialogs +namespace Files.App.ViewModels.Dialogs { public sealed class FileTooLargeDialogViewModel: ObservableObject { diff --git a/src/Files.Core/ViewModels/Dialogs/GitHubLoginDialogViewModel.cs b/src/Files.App/ViewModels/Dialogs/GitHubLoginDialogViewModel.cs similarity index 96% rename from src/Files.Core/ViewModels/Dialogs/GitHubLoginDialogViewModel.cs rename to src/Files.App/ViewModels/Dialogs/GitHubLoginDialogViewModel.cs index 44cd5484bbb6..787afc11f11d 100644 --- a/src/Files.Core/ViewModels/Dialogs/GitHubLoginDialogViewModel.cs +++ b/src/Files.App/ViewModels/Dialogs/GitHubLoginDialogViewModel.cs @@ -3,7 +3,7 @@ using System.Windows.Input; -namespace Files.Core.ViewModels.Dialogs +namespace Files.App.ViewModels.Dialogs { public sealed class GitHubLoginDialogViewModel : ObservableObject { diff --git a/src/Files.Core/ViewModels/Dialogs/IDialog.cs b/src/Files.App/ViewModels/Dialogs/IDialog.cs similarity index 80% rename from src/Files.Core/ViewModels/Dialogs/IDialog.cs rename to src/Files.App/ViewModels/Dialogs/IDialog.cs index 9e619329778c..2852cc543a26 100644 --- a/src/Files.Core/ViewModels/Dialogs/IDialog.cs +++ b/src/Files.App/ViewModels/Dialogs/IDialog.cs @@ -1,9 +1,9 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -using Files.Core.Data.Enums; +using Files.App.Data.Enums; -namespace Files.Core.ViewModels.Dialogs +namespace Files.App.ViewModels.Dialogs { public interface IDialog where TViewModel : class, INotifyPropertyChanged diff --git a/src/Files.Core/ViewModels/Dialogs/ReleaseNotesDialogViewModel.cs b/src/Files.App/ViewModels/Dialogs/ReleaseNotesDialogViewModel.cs similarity index 92% rename from src/Files.Core/ViewModels/Dialogs/ReleaseNotesDialogViewModel.cs rename to src/Files.App/ViewModels/Dialogs/ReleaseNotesDialogViewModel.cs index 872489acb99e..0d63596b9fd3 100644 --- a/src/Files.Core/ViewModels/Dialogs/ReleaseNotesDialogViewModel.cs +++ b/src/Files.App/ViewModels/Dialogs/ReleaseNotesDialogViewModel.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.ViewModels.Dialogs +namespace Files.App.ViewModels.Dialogs { public sealed class ReleaseNotesDialogViewModel : ObservableObject { diff --git a/src/Files.Core/ViewModels/Dialogs/SettingsDialogViewModel.cs b/src/Files.App/ViewModels/Dialogs/SettingsDialogViewModel.cs similarity index 80% rename from src/Files.Core/ViewModels/Dialogs/SettingsDialogViewModel.cs rename to src/Files.App/ViewModels/Dialogs/SettingsDialogViewModel.cs index 93c69ba23521..a058523c4401 100644 --- a/src/Files.Core/ViewModels/Dialogs/SettingsDialogViewModel.cs +++ b/src/Files.App/ViewModels/Dialogs/SettingsDialogViewModel.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.ViewModels.Dialogs +namespace Files.App.ViewModels.Dialogs { public sealed class SettingsDialogViewModel : ObservableObject { diff --git a/src/Files.Core/ViewModels/FileTags/ListedTagViewModel.cs b/src/Files.App/ViewModels/FileTags/ListedTagViewModel.cs similarity index 96% rename from src/Files.Core/ViewModels/FileTags/ListedTagViewModel.cs rename to src/Files.App/ViewModels/FileTags/ListedTagViewModel.cs index 46357edee972..3eccbe3ffe96 100644 --- a/src/Files.Core/ViewModels/FileTags/ListedTagViewModel.cs +++ b/src/Files.App/ViewModels/FileTags/ListedTagViewModel.cs @@ -1,7 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -namespace Files.Core.ViewModels.FileTags +namespace Files.App.ViewModels.FileTags { public sealed class ListedTagViewModel : ObservableObject { diff --git a/src/Files.Core/ViewModels/FileTags/TagViewModel.cs b/src/Files.App/ViewModels/FileTags/TagViewModel.cs similarity index 93% rename from src/Files.Core/ViewModels/FileTags/TagViewModel.cs rename to src/Files.App/ViewModels/FileTags/TagViewModel.cs index 0c0d6c2120a7..31b66f90d0ad 100644 --- a/src/Files.Core/ViewModels/FileTags/TagViewModel.cs +++ b/src/Files.App/ViewModels/FileTags/TagViewModel.cs @@ -3,7 +3,7 @@ using System.Text.Json.Serialization; -namespace Files.Core.ViewModels.FileTags +namespace Files.App.ViewModels.FileTags { [Serializable] public sealed partial class TagViewModel : ObservableObject diff --git a/src/Files.App/ViewModels/Properties/Items/BaseProperties.cs b/src/Files.App/ViewModels/Properties/Items/BaseProperties.cs index 6a0440234019..407dadb653b3 100644 --- a/src/Files.App/ViewModels/Properties/Items/BaseProperties.cs +++ b/src/Files.App/ViewModels/Properties/Items/BaseProperties.cs @@ -4,7 +4,7 @@ using Microsoft.UI.Dispatching; using System.IO; using Windows.Storage.FileProperties; -using static Files.Core.Helpers.NativeFindStorageItemHelper; +using static Files.App.Helpers.NativeFindStorageItemHelper; using FileAttributes = System.IO.FileAttributes; namespace Files.App.ViewModels.Properties diff --git a/src/Files.App/ViewModels/Settings/AdvancedViewModel.cs b/src/Files.App/ViewModels/Settings/AdvancedViewModel.cs index 84cef37ff61e..750b3ad3d6aa 100644 --- a/src/Files.App/ViewModels/Settings/AdvancedViewModel.cs +++ b/src/Files.App/ViewModels/Settings/AdvancedViewModel.cs @@ -176,7 +176,7 @@ private async Task ImportSettingsAsync() var fileTagsList = await zipFolder.GetFileAsync(Constants.LocalSettings.FileTagSettingsFileName); string importTags = await fileTagsList.ReadTextAsync(); fileTagsSettingsService.ImportSettings(importTags); - var fileTagsDB = await zipFolder.GetFileAsync(Path.GetFileName(FileTagsHelper.FileTagsDbPath)); + var fileTagsDB = await zipFolder.GetFileAsync(Path.GetFileName(Server.Database.FileTagsDatabase.FileTagsDbPath)); string importTagsDB = await fileTagsDB.ReadTextAsync(); var tagDbInstance = FileTagsHelper.GetDbInstance(); tagDbInstance.Import(importTagsDB); @@ -224,7 +224,7 @@ private async Task ExportSettingsAsync() await zipFolder.CreateFileAsync(new MemoryStream(exportTags), Constants.LocalSettings.FileTagSettingsFileName, CreationCollisionOption.ReplaceExisting); var tagDbInstance = FileTagsHelper.GetDbInstance(); byte[] exportTagsDB = UTF8Encoding.UTF8.GetBytes(tagDbInstance.Export()); - await zipFolder.CreateFileAsync(new MemoryStream(exportTagsDB), Path.GetFileName(FileTagsHelper.FileTagsDbPath), CreationCollisionOption.ReplaceExisting); + await zipFolder.CreateFileAsync(new MemoryStream(exportTagsDB), Path.GetFileName(Server.Database.FileTagsDatabase.FileTagsDbPath), CreationCollisionOption.ReplaceExisting); // Export layout preferences DB var layoutDbInstance = LayoutPreferencesManager.GetDatabaseManagerInstance(); diff --git a/src/Files.App/ViewModels/Settings/FoldersViewModel.cs b/src/Files.App/ViewModels/Settings/FoldersViewModel.cs index b610a287ccfe..a7bc2d206d84 100644 --- a/src/Files.App/ViewModels/Settings/FoldersViewModel.cs +++ b/src/Files.App/ViewModels/Settings/FoldersViewModel.cs @@ -1,6 +1,8 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. +using Files.App.Server.Data.Enums; + namespace Files.App.ViewModels.Settings { public sealed class FoldersViewModel : ObservableObject diff --git a/src/Files.App/ViewModels/Settings/LayoutViewModel.cs b/src/Files.App/ViewModels/Settings/LayoutViewModel.cs index 7d33a834e958..039bb426b2a2 100644 --- a/src/Files.App/ViewModels/Settings/LayoutViewModel.cs +++ b/src/Files.App/ViewModels/Settings/LayoutViewModel.cs @@ -1,6 +1,8 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. +using Files.App.Server.Data.Enums; + namespace Files.App.ViewModels.Settings { public sealed class LayoutViewModel : ObservableObject diff --git a/src/Files.App/ViewModels/UserControls/AddressToolbarViewModel.cs b/src/Files.App/ViewModels/UserControls/AddressToolbarViewModel.cs index f1e5f86af0e5..01603ecaa120 100644 --- a/src/Files.App/ViewModels/UserControls/AddressToolbarViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/AddressToolbarViewModel.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. See the LICENSE. using CommunityToolkit.WinUI.UI; +using Files.App.Server.Data.Enums; using Files.Shared.Helpers; using Microsoft.UI.Dispatching; using Microsoft.UI.Xaml; diff --git a/src/Files.Core/ViewModels/Widgets/FileTagsWidget/FileTagsContainerViewModel.cs b/src/Files.App/ViewModels/Widgets/FileTagsWidget/FileTagsContainerViewModel.cs similarity index 96% rename from src/Files.Core/ViewModels/Widgets/FileTagsWidget/FileTagsContainerViewModel.cs rename to src/Files.App/ViewModels/Widgets/FileTagsWidget/FileTagsContainerViewModel.cs index ea2c5afd0f5f..29ccb0ea9290 100644 --- a/src/Files.Core/ViewModels/Widgets/FileTagsWidget/FileTagsContainerViewModel.cs +++ b/src/Files.App/ViewModels/Widgets/FileTagsWidget/FileTagsContainerViewModel.cs @@ -3,7 +3,7 @@ using Files.Shared.Utils; -namespace Files.Core.ViewModels.Widgets.FileTagsWidget +namespace Files.App.ViewModels.Widgets.FileTagsWidget { public sealed partial class FileTagsContainerViewModel : ObservableObject, IAsyncInitialize { diff --git a/src/Files.Core/ViewModels/Widgets/FileTagsWidget/FileTagsItemViewModel.cs b/src/Files.App/ViewModels/Widgets/FileTagsWidget/FileTagsItemViewModel.cs similarity index 96% rename from src/Files.Core/ViewModels/Widgets/FileTagsWidget/FileTagsItemViewModel.cs rename to src/Files.App/ViewModels/Widgets/FileTagsWidget/FileTagsItemViewModel.cs index 2f8deef6079d..ca05e1cb4e87 100644 --- a/src/Files.Core/ViewModels/Widgets/FileTagsWidget/FileTagsItemViewModel.cs +++ b/src/Files.App/ViewModels/Widgets/FileTagsWidget/FileTagsItemViewModel.cs @@ -6,7 +6,7 @@ using Files.Shared.Helpers; using Files.Shared.Utils; -namespace Files.Core.ViewModels.Widgets.FileTagsWidget +namespace Files.App.ViewModels.Widgets.FileTagsWidget { public sealed partial class FileTagsItemViewModel : ObservableObject { diff --git a/src/Files.Core/ViewModels/Widgets/FileTagsWidget/FileTagsWidgetViewModel.cs b/src/Files.App/ViewModels/Widgets/FileTagsWidget/FileTagsWidgetViewModel.cs similarity index 96% rename from src/Files.Core/ViewModels/Widgets/FileTagsWidget/FileTagsWidgetViewModel.cs rename to src/Files.App/ViewModels/Widgets/FileTagsWidget/FileTagsWidgetViewModel.cs index c8221341e8fa..1d7c71082270 100644 --- a/src/Files.Core/ViewModels/Widgets/FileTagsWidget/FileTagsWidgetViewModel.cs +++ b/src/Files.App/ViewModels/Widgets/FileTagsWidget/FileTagsWidgetViewModel.cs @@ -3,7 +3,7 @@ using Files.Shared.Utils; -namespace Files.Core.ViewModels.Widgets.FileTagsWidget +namespace Files.App.ViewModels.Widgets.FileTagsWidget { public sealed partial class FileTagsWidgetViewModel : ObservableObject, IAsyncInitialize { diff --git a/src/Files.App/Views/Layouts/BaseGroupableLayoutPage.cs b/src/Files.App/Views/Layouts/BaseGroupableLayoutPage.cs index 5924d0ada5a3..9e771195f245 100644 --- a/src/Files.App/Views/Layouts/BaseGroupableLayoutPage.cs +++ b/src/Files.App/Views/Layouts/BaseGroupableLayoutPage.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. See the LICENSE. using CommunityToolkit.WinUI.UI; +using Files.App.Server.Data.Enums; using Files.App.ViewModels.Layouts; using Microsoft.UI.Input; using Microsoft.UI.Xaml; diff --git a/src/Files.App/Views/Layouts/BaseLayoutPage.cs b/src/Files.App/Views/Layouts/BaseLayoutPage.cs index 68c99a3eebb7..c0b35f9a312a 100644 --- a/src/Files.App/Views/Layouts/BaseLayoutPage.cs +++ b/src/Files.App/Views/Layouts/BaseLayoutPage.cs @@ -3,6 +3,7 @@ using CommunityToolkit.WinUI.UI; using Files.App.Helpers.ContextFlyouts; +using Files.App.Server.Data.Enums; using Files.App.UserControls.Menus; using Files.App.ViewModels.Layouts; using Microsoft.UI.Xaml; @@ -23,7 +24,6 @@ using Windows.System; using static Files.App.Helpers.PathNormalization; using DispatcherQueueTimer = Microsoft.UI.Dispatching.DispatcherQueueTimer; -using SortDirection = Files.Core.Data.Enums.SortDirection; using VanaraWindowsShell = Vanara.Windows.Shell; namespace Files.App.Views.Layouts @@ -514,7 +514,7 @@ private async void FolderSettings_GroupOptionPreferenceUpdated(object? sender, G await GroupPreferenceUpdatedAsync(); } - private async void FolderSettings_GroupDirectionPreferenceUpdated(object? sender, SortDirection e) + private async void FolderSettings_GroupDirectionPreferenceUpdated(object? sender, Server.Data.Enums.SortDirection e) { await GroupPreferenceUpdatedAsync(); } diff --git a/src/Files.App/Views/Layouts/ColumnLayoutPage.xaml.cs b/src/Files.App/Views/Layouts/ColumnLayoutPage.xaml.cs index ad2b655f31fb..662e1421cde4 100644 --- a/src/Files.App/Views/Layouts/ColumnLayoutPage.xaml.cs +++ b/src/Files.App/Views/Layouts/ColumnLayoutPage.xaml.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. See the LICENSE. using CommunityToolkit.WinUI.UI; +using Files.App.Server.Data.Enums; using Files.App.UserControls.Selection; using Microsoft.UI.Dispatching; using Microsoft.UI.Input; diff --git a/src/Files.App/Views/Layouts/DetailsLayoutPage.xaml b/src/Files.App/Views/Layouts/DetailsLayoutPage.xaml index c8c39c3f91d0..79b7d41f4dd6 100644 --- a/src/Files.App/Views/Layouts/DetailsLayoutPage.xaml +++ b/src/Files.App/Views/Layouts/DetailsLayoutPage.xaml @@ -7,7 +7,7 @@ xmlns:converters="using:Files.App.Converters" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:filesystem="using:Files.App.Utils" - xmlns:filetags="using:Files.Core.ViewModels.FileTags" + xmlns:filetags="using:Files.App.ViewModels.FileTags" xmlns:helpers="using:Files.App.Helpers" xmlns:i="using:Microsoft.Xaml.Interactivity" xmlns:icore="using:Microsoft.Xaml.Interactions.Core" diff --git a/src/Files.App/Views/Layouts/DetailsLayoutPage.xaml.cs b/src/Files.App/Views/Layouts/DetailsLayoutPage.xaml.cs index 0d07b601e03e..d5db6ad8515f 100644 --- a/src/Files.App/Views/Layouts/DetailsLayoutPage.xaml.cs +++ b/src/Files.App/Views/Layouts/DetailsLayoutPage.xaml.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. See the LICENSE. using CommunityToolkit.WinUI.UI; +using Files.App.Server.Data.Enums; using Files.App.UserControls.Selection; using Microsoft.UI.Input; using Microsoft.UI.Xaml; @@ -13,7 +14,7 @@ using Windows.Storage; using Windows.System; using Windows.UI.Core; -using SortDirection = Files.Core.Data.Enums.SortDirection; +using SortDirection = Files.App.Server.Data.Enums.SortDirection; namespace Files.App.Views.Layouts { diff --git a/src/Files.App/Views/Layouts/GridLayoutPage.xaml.cs b/src/Files.App/Views/Layouts/GridLayoutPage.xaml.cs index edec07cba3fd..311ffef05b0c 100644 --- a/src/Files.App/Views/Layouts/GridLayoutPage.xaml.cs +++ b/src/Files.App/Views/Layouts/GridLayoutPage.xaml.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. See the LICENSE. using CommunityToolkit.WinUI.UI; +using Files.App.Server.Data.Enums; using Files.App.UserControls.Selection; using Microsoft.UI.Input; using Microsoft.UI.Xaml; diff --git a/src/Files.App/Views/Properties/CustomizationPage.xaml b/src/Files.App/Views/Properties/CustomizationPage.xaml index 29343159b956..bc6a51c8bdf0 100644 --- a/src/Files.App/Views/Properties/CustomizationPage.xaml +++ b/src/Files.App/Views/Properties/CustomizationPage.xaml @@ -3,7 +3,7 @@ x:Class="Files.App.Views.Properties.CustomizationPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:coredataitems="using:Files.Core.Data.Items" + xmlns:coredataitems="using:Files.App.Data.Items" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:helpers="using:Files.App.Helpers" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" diff --git a/src/Files.App/Views/Properties/HashesPage.xaml b/src/Files.App/Views/Properties/HashesPage.xaml index ddf00880e10f..d2577b961467 100644 --- a/src/Files.App/Views/Properties/HashesPage.xaml +++ b/src/Files.App/Views/Properties/HashesPage.xaml @@ -4,7 +4,7 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:datamodels="using:Files.Core.Data.Models" + xmlns:datamodels="using:Files.App.Data.Models" xmlns:helpers="using:Files.App.Helpers" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:toolkitconverters="using:CommunityToolkit.WinUI.UI.Converters" diff --git a/src/Files.App/Views/Settings/TagsPage.xaml b/src/Files.App/Views/Settings/TagsPage.xaml index 2e4f70d218bb..a2357cb63a91 100644 --- a/src/Files.App/Views/Settings/TagsPage.xaml +++ b/src/Files.App/Views/Settings/TagsPage.xaml @@ -176,7 +176,7 @@ ReorderMode="Enabled" SelectionMode="None"> - + diff --git a/src/Files.App/Views/Settings/TagsPage.xaml.cs b/src/Files.App/Views/Settings/TagsPage.xaml.cs index 7add5b3e7710..e1f117051319 100644 --- a/src/Files.App/Views/Settings/TagsPage.xaml.cs +++ b/src/Files.App/Views/Settings/TagsPage.xaml.cs @@ -2,7 +2,7 @@ // Licensed under the MIT License. See the LICENSE. using CommunityToolkit.WinUI.UI; -using Files.Core.ViewModels.FileTags; +using Files.App.ViewModels.FileTags; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Controls.Primitives; diff --git a/src/Files.App/Views/Shells/BaseShellPage.cs b/src/Files.App/Views/Shells/BaseShellPage.cs index 9eb986389d9d..f7e7a7c1b7a6 100644 --- a/src/Files.App/Views/Shells/BaseShellPage.cs +++ b/src/Files.App/Views/Shells/BaseShellPage.cs @@ -1,6 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. +using Files.App.Server.Data.Enums; using Microsoft.UI.Input; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; @@ -13,7 +14,6 @@ using Windows.System; using Windows.UI.Core; using DispatcherQueueTimer = Microsoft.UI.Dispatching.DispatcherQueueTimer; -using SortDirection = Files.Core.Data.Enums.SortDirection; namespace Files.App.Views.Shells { diff --git a/src/Files.App/Views/Shells/ColumnShellPage.xaml.cs b/src/Files.App/Views/Shells/ColumnShellPage.xaml.cs index 9eb43944118b..30110004f162 100644 --- a/src/Files.App/Views/Shells/ColumnShellPage.xaml.cs +++ b/src/Files.App/Views/Shells/ColumnShellPage.xaml.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. See the LICENSE. using CommunityToolkit.WinUI.UI; +using Files.App.Server.Data.Enums; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Input; diff --git a/src/Files.App/nupkgs/Microsoft.UI.Content/Microsoft.UI.Content.cs b/src/Files.App/nupkgs/Microsoft.UI.Content/Microsoft.UI.Content.cs deleted file mode 100644 index 88b3e9316d76..000000000000 --- a/src/Files.App/nupkgs/Microsoft.UI.Content/Microsoft.UI.Content.cs +++ /dev/null @@ -1,301 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This file was generated by cswinrt.exe version 2.0.3.230608.1 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Diagnostics; -using System.Linq.Expressions; -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using WinRT; -using WinRT.Interop; - - -#pragma warning disable 0169 // warning CS0169: The field '...' is never used -#pragma warning disable 0649 // warning CS0169: Field '...' is never assigned to -#pragma warning disable CA2207, CA1063, CA1033, CA1001, CA2213 - -namespace Microsoft.UI.Content -{ - [global::WinRT.WindowsRuntimeType("Microsoft.UI.Content")][global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Microsoft.UI.Content.ContentExternalOutputLink))] - [global::WinRT.ProjectedRuntimeClass(typeof(IContentExternalOutputLink))] - [global::WinRT.ObjectReferenceWrapper(nameof(_inner))] - [global::Windows.Foundation.Metadata.ContractVersion(typeof(global::Microsoft.Foundation.WindowsAppSDKContract), 65540u)] - [global::Windows.Foundation.Metadata.Experimental] - public sealed class ContentExternalOutputLink : global::System.IDisposable, global::System.Runtime.InteropServices.ICustomQueryInterface, IWinRTObject, IEquatable - { - private IntPtr ThisPtr => _inner == null ? (((IWinRTObject)this).NativeObject).ThisPtr : _inner.ThisPtr; - - private IObjectReference _inner = null; - - private volatile global::System.IDisposable _lazy_global__System_IDisposable; - private global::System.IDisposable Make__lazy_global__System_IDisposable() - { - global::System.Threading.Interlocked.CompareExchange(ref _lazy_global__System_IDisposable, (global::System.IDisposable)(object)new SingleInterfaceOptimizedObject(typeof(global::System.IDisposable), _inner ?? ((IWinRTObject)this).NativeObject), null); - return _lazy_global__System_IDisposable; - } - - - - private IObjectReference _objRef_global__Microsoft_UI_Content_Private_IContentExternalOutputLink => _inner; - private IContentExternalOutputLink _default => null; - - public static I As() => ActivationFactory.AsInterface(); - - private static volatile IObjectReference ___objRef_global__Microsoft_UI_Content_Private_IContentExternalOutputLinkStatics; - private static IObjectReference Make___objRef_global__Microsoft_UI_Content_Private_IContentExternalOutputLinkStatics() - { - global::System.Threading.Interlocked.CompareExchange(ref ___objRef_global__Microsoft_UI_Content_Private_IContentExternalOutputLinkStatics, ActivationFactory.As(GuidGenerator.GetIID(typeof(IContentExternalOutputLinkStatics).GetHelperType())), null); - return ___objRef_global__Microsoft_UI_Content_Private_IContentExternalOutputLinkStatics; - } - private static IObjectReference _objRef_global__Microsoft_UI_Content_Private_IContentExternalOutputLinkStatics => ___objRef_global__Microsoft_UI_Content_Private_IContentExternalOutputLinkStatics ?? Make___objRef_global__Microsoft_UI_Content_Private_IContentExternalOutputLinkStatics(); - - - - public static ContentExternalOutputLink Create(global::Microsoft.UI.Composition.Compositor compositor) => global::ABI.Microsoft.UI.Content.IContentExternalOutputLinkStaticsMethods.Create(_objRef_global__Microsoft_UI_Content_Private_IContentExternalOutputLinkStatics, compositor); - - public static bool IsSupported() => global::ABI.Microsoft.UI.Content.IContentExternalOutputLinkStaticsMethods.IsSupported(_objRef_global__Microsoft_UI_Content_Private_IContentExternalOutputLinkStatics); - - public static ContentExternalOutputLink FromAbi(IntPtr thisPtr) - { - if (thisPtr == IntPtr.Zero) return null; - return MarshalInspectable.FromAbi(thisPtr); - } - - internal ContentExternalOutputLink(IObjectReference objRef) - { - _inner = objRef.As(GuidGenerator.GetIID(typeof(IContentExternalOutputLink).GetHelperType())); - - } - - public static bool operator ==(ContentExternalOutputLink x, ContentExternalOutputLink y) => (x?.ThisPtr ?? IntPtr.Zero) == (y?.ThisPtr ?? IntPtr.Zero); - public static bool operator !=(ContentExternalOutputLink x, ContentExternalOutputLink y) => !(x == y); - public bool Equals(ContentExternalOutputLink other) => this == other; - public override bool Equals(object obj) => obj is ContentExternalOutputLink that && this == that; - public override int GetHashCode() => ThisPtr.GetHashCode(); - - - bool IWinRTObject.HasUnwrappableNativeObject => true; - IObjectReference IWinRTObject.NativeObject => _inner; - private volatile global::System.Collections.Concurrent.ConcurrentDictionary _queryInterfaceCache; - private global::System.Collections.Concurrent.ConcurrentDictionary MakeQueryInterfaceCache() - { - global::System.Threading.Interlocked.CompareExchange(ref _queryInterfaceCache, new global::System.Collections.Concurrent.ConcurrentDictionary(), null); - return _queryInterfaceCache; - } - global::System.Collections.Concurrent.ConcurrentDictionary IWinRTObject.QueryInterfaceCache => _queryInterfaceCache ?? MakeQueryInterfaceCache(); - private volatile global::System.Collections.Concurrent.ConcurrentDictionary _additionalTypeData; - private global::System.Collections.Concurrent.ConcurrentDictionary MakeAdditionalTypeData() - { - global::System.Threading.Interlocked.CompareExchange(ref _additionalTypeData, new global::System.Collections.Concurrent.ConcurrentDictionary(), null); - return _additionalTypeData; - } - global::System.Collections.Concurrent.ConcurrentDictionary IWinRTObject.AdditionalTypeData => _additionalTypeData ?? MakeAdditionalTypeData(); - - private struct InterfaceTag{}; - - - private global::System.IDisposable AsInternal(InterfaceTag _) => _lazy_global__System_IDisposable ?? Make__lazy_global__System_IDisposable(); - - public void Dispose() => AsInternal(new InterfaceTag()).Dispose(); - - public global::Windows.UI.Color BackgroundColor - { - get => global::ABI.Microsoft.UI.Content.IContentExternalOutputLinkMethods.get_BackgroundColor(_objRef_global__Microsoft_UI_Content_Private_IContentExternalOutputLink); - set => global::ABI.Microsoft.UI.Content.IContentExternalOutputLinkMethods.set_BackgroundColor(_objRef_global__Microsoft_UI_Content_Private_IContentExternalOutputLink, value); - } - - public global::Microsoft.UI.Dispatching.DispatcherQueue DispatcherQueue => global::ABI.Microsoft.UI.Content.IContentExternalOutputLinkMethods.get_DispatcherQueue(_objRef_global__Microsoft_UI_Content_Private_IContentExternalOutputLink); - - public global::Microsoft.UI.Composition.CompositionBorderMode ExternalOutputBorderMode - { - get => global::ABI.Microsoft.UI.Content.IContentExternalOutputLinkMethods.get_ExternalOutputBorderMode(_objRef_global__Microsoft_UI_Content_Private_IContentExternalOutputLink); - set => global::ABI.Microsoft.UI.Content.IContentExternalOutputLinkMethods.set_ExternalOutputBorderMode(_objRef_global__Microsoft_UI_Content_Private_IContentExternalOutputLink, value); - } - - public global::Microsoft.UI.Composition.Visual PlacementVisual => global::ABI.Microsoft.UI.Content.IContentExternalOutputLinkMethods.get_PlacementVisual(_objRef_global__Microsoft_UI_Content_Private_IContentExternalOutputLink); - - private bool IsOverridableInterface(Guid iid) => false; - - global::System.Runtime.InteropServices.CustomQueryInterfaceResult global::System.Runtime.InteropServices.ICustomQueryInterface.GetInterface(ref Guid iid, out IntPtr ppv) - { - ppv = IntPtr.Zero; - if (IsOverridableInterface(iid) || global::WinRT.InterfaceIIDs.IInspectable_IID == iid) - { - return global::System.Runtime.InteropServices.CustomQueryInterfaceResult.NotHandled; - } - - if (((IWinRTObject)this).NativeObject.TryAs(iid, out ppv) >= 0) - { - return global::System.Runtime.InteropServices.CustomQueryInterfaceResult.Handled; - } - - return global::System.Runtime.InteropServices.CustomQueryInterfaceResult.NotHandled; - } - } - - [global::WinRT.WindowsRuntimeType("Microsoft.UI.Content")] - [Guid("FED9A1E8-F804-5A26-A8B0-ED077215D422")] - [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Microsoft.UI.Content.IContentExternalOutputLink))] - [global::Windows.Foundation.Metadata.ContractVersion(typeof(global::Microsoft.Foundation.WindowsAppSDKContract), 65540u)] - [global::Windows.Foundation.Metadata.Experimental] - internal interface IContentExternalOutputLink - { - global::Windows.UI.Color BackgroundColor { get; set; } - global::Microsoft.UI.Dispatching.DispatcherQueue DispatcherQueue { get; } - global::Microsoft.UI.Composition.CompositionBorderMode ExternalOutputBorderMode { get; set; } - global::Microsoft.UI.Composition.Visual PlacementVisual { get; } - } - - [global::WinRT.WindowsRuntimeType("Microsoft.UI.Content")] - [Guid("B758F401-833E-587D-B0CD-A3934EBA3721")] - [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Microsoft.UI.Content.IContentExternalOutputLinkStatics))] - [global::Windows.Foundation.Metadata.ContractVersion(typeof(global::Microsoft.Foundation.WindowsAppSDKContract), 65540u)] - [global::Windows.Foundation.Metadata.Experimental] - internal interface IContentExternalOutputLinkStatics - { - ContentExternalOutputLink Create(global::Microsoft.UI.Composition.Compositor compositor); - bool IsSupported(); - } -} - -#pragma warning disable CA1416 -namespace ABI.Microsoft.UI.Content -{ - [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] - public struct ContentExternalOutputLink - { - - public static IObjectReference CreateMarshaler(global::Microsoft.UI.Content.ContentExternalOutputLink obj) => obj is null ? null : MarshalInspectable.CreateMarshaler(obj, GuidGenerator.GetIID(typeof(global::Microsoft.UI.Content.IContentExternalOutputLink).GetHelperType())); - public static ObjectReferenceValue CreateMarshaler2(global::Microsoft.UI.Content.ContentExternalOutputLink obj) => MarshalInspectable.CreateMarshaler2(obj, GuidGenerator.GetIID(typeof(global::Microsoft.UI.Content.IContentExternalOutputLink).GetHelperType())); - public static IntPtr GetAbi(IObjectReference value) => value is null ? IntPtr.Zero : MarshalInterfaceHelper.GetAbi(value); - public static global::Microsoft.UI.Content.ContentExternalOutputLink FromAbi(IntPtr thisPtr) => global::Microsoft.UI.Content.ContentExternalOutputLink.FromAbi(thisPtr); - public static IntPtr FromManaged(global::Microsoft.UI.Content.ContentExternalOutputLink obj) => obj is null ? IntPtr.Zero : CreateMarshaler2(obj).Detach(); - public static unsafe MarshalInterfaceHelper.MarshalerArray CreateMarshalerArray(global::Microsoft.UI.Content.ContentExternalOutputLink[] array) => MarshalInterfaceHelper.CreateMarshalerArray2(array, (o) => CreateMarshaler2(o)); - public static (int length, IntPtr data) GetAbiArray(object box) => MarshalInterfaceHelper.GetAbiArray(box); - public static unsafe global::Microsoft.UI.Content.ContentExternalOutputLink[] FromAbiArray(object box) => MarshalInterfaceHelper.FromAbiArray(box, FromAbi); - public static (int length, IntPtr data) FromManagedArray(global::Microsoft.UI.Content.ContentExternalOutputLink[] array) => MarshalInterfaceHelper.FromManagedArray(array, (o) => FromManaged(o)); - public static void DisposeMarshaler(IObjectReference value) => MarshalInspectable.DisposeMarshaler(value); - public static void DisposeMarshalerArray(MarshalInterfaceHelper.MarshalerArray array) => MarshalInterfaceHelper.DisposeMarshalerArray(array); - public static void DisposeAbi(IntPtr abi) => MarshalInspectable.DisposeAbi(abi); - public static unsafe void DisposeAbiArray(object box) => MarshalInspectable.DisposeAbiArray(box); - } - - internal static class IContentExternalOutputLinkMethods - { - public static unsafe global::Windows.UI.Color get_BackgroundColor(IObjectReference _obj) - { - var ThisPtr = _obj.ThisPtr; - - global::Windows.UI.Color __retval = default; - global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[6](ThisPtr, out __retval)); - return __retval; - } - public static unsafe void set_BackgroundColor(IObjectReference _obj, global::Windows.UI.Color value) - { - var ThisPtr = _obj.ThisPtr; - - global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[7](ThisPtr, value)); - } - - public static unsafe global::Microsoft.UI.Dispatching.DispatcherQueue get_DispatcherQueue(IObjectReference _obj) - { - var ThisPtr = _obj.ThisPtr; - - IntPtr __retval = default; - try - { - global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[8](ThisPtr, out __retval)); - return global::ABI.Microsoft.UI.Dispatching.DispatcherQueue.FromAbi(__retval); - } - finally - { - global::ABI.Microsoft.UI.Dispatching.DispatcherQueue.DisposeAbi(__retval); - } - } - - public static unsafe global::Microsoft.UI.Composition.CompositionBorderMode get_ExternalOutputBorderMode(IObjectReference _obj) - { - var ThisPtr = _obj.ThisPtr; - - global::Microsoft.UI.Composition.CompositionBorderMode __retval = default; - global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[9](ThisPtr, out __retval)); - return __retval; - } - public static unsafe void set_ExternalOutputBorderMode(IObjectReference _obj, global::Microsoft.UI.Composition.CompositionBorderMode value) - { - var ThisPtr = _obj.ThisPtr; - - global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[10](ThisPtr, value)); - } - - public static unsafe global::Microsoft.UI.Composition.Visual get_PlacementVisual(IObjectReference _obj) - { - var ThisPtr = _obj.ThisPtr; - - IntPtr __retval = default; - try - { - global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[11](ThisPtr, out __retval)); - return global::ABI.Microsoft.UI.Composition.Visual.FromAbi(__retval); - } - finally - { - global::ABI.Microsoft.UI.Composition.Visual.DisposeAbi(__retval); - } - } - } - - [Guid("FED9A1E8-F804-5A26-A8B0-ED077215D422")] - internal interface IContentExternalOutputLink : global::Microsoft.UI.Content.IContentExternalOutputLink - { - } - - internal static class IContentExternalOutputLinkStaticsMethods - { - - public static unsafe global::Microsoft.UI.Content.ContentExternalOutputLink Create(IObjectReference _obj, global::Microsoft.UI.Composition.Compositor compositor) - { - var ThisPtr = _obj.ThisPtr; - - ObjectReferenceValue __compositor = default; - IntPtr __retval = default; - try - { - __compositor = global::ABI.Microsoft.UI.Composition.Compositor.CreateMarshaler2(compositor); - global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[6](ThisPtr, MarshalInspectable.GetAbi(__compositor), out __retval)); - return global::ABI.Microsoft.UI.Content.ContentExternalOutputLink.FromAbi(__retval); - } - finally - { - MarshalInspectable.DisposeMarshaler(__compositor); - global::ABI.Microsoft.UI.Content.ContentExternalOutputLink.DisposeAbi(__retval); - } - } - - public static unsafe bool IsSupported(IObjectReference _obj) - { - var ThisPtr = _obj.ThisPtr; - - byte __retval = default; - global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[7](ThisPtr, out __retval)); - return __retval != 0; - } - - } - - [Guid("B758F401-833E-587D-B0CD-A3934EBA3721")] - internal interface IContentExternalOutputLinkStatics : global::Microsoft.UI.Content.IContentExternalOutputLinkStatics - { - } -} -#pragma warning restore CA1416 diff --git a/src/Files.App/nupkgs/Microsoft.UI.winmd b/src/Files.App/nupkgs/Microsoft.UI.winmd new file mode 100644 index 0000000000000000000000000000000000000000..82e094e3e8c40611687468832c820c691cddddec GIT binary patch literal 289280 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 literal 0 HcmV?d00001 diff --git a/src/Files.Core.SourceGenerator/Files.Core.SourceGenerator.csproj b/src/Files.Core.SourceGenerator/Files.Core.SourceGenerator.csproj index 48c2c854307b..f6af3e1cfa21 100644 --- a/src/Files.Core.SourceGenerator/Files.Core.SourceGenerator.csproj +++ b/src/Files.Core.SourceGenerator/Files.Core.SourceGenerator.csproj @@ -19,9 +19,9 @@ - + - + diff --git a/src/Files.Core/Data/Items/IsExternalInit.cs b/src/Files.Core/Data/Items/IsExternalInit.cs deleted file mode 100644 index aa43d5200821..000000000000 --- a/src/Files.Core/Data/Items/IsExternalInit.cs +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - - -namespace System.Runtime.CompilerServices -{ - [EditorBrowsable(EditorBrowsableState.Never)] - internal sealed class IsExternalInit - { - } -} diff --git a/src/Files.Core/Files.Core.csproj b/src/Files.Core/Files.Core.csproj deleted file mode 100644 index 5425b782907b..000000000000 --- a/src/Files.Core/Files.Core.csproj +++ /dev/null @@ -1,31 +0,0 @@ - - - - - net8.0 - enable - true - Debug;Release;Stable;Preview;Store - x86;x64;arm64 - win-x86;win-x64;win-arm64 - - - - TRACE;DEBUG;NETFX_CORE - - - TRACE;RELEASE;NETFX_CORE - true - - - - - - - - - - - - - diff --git a/src/Files.Core/GlobalUsings.cs b/src/Files.Core/GlobalUsings.cs deleted file mode 100644 index 1d684c735ab7..000000000000 --- a/src/Files.Core/GlobalUsings.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -// System -global using global::System; -global using global::System.Collections; -global using global::System.Collections.Generic; -global using global::System.Collections.ObjectModel; -global using global::System.Linq; -global using global::System.Threading; -global using global::System.Threading.Tasks; -global using global::System.ComponentModel; -global using global::System.Diagnostics; -global using SystemIO = global::System.IO; - -// Windows Community Toolkit -global using global::CommunityToolkit.Mvvm.ComponentModel; -global using global::CommunityToolkit.Mvvm.DependencyInjection; -global using global::CommunityToolkit.Mvvm.Input; -global using global::CommunityToolkit.Mvvm.Messaging; - -// Files Back-end -global using global::Files.Core.Data.Enums; -global using global::Files.Core.Data.EventArguments; -global using global::Files.Core.Data.Items; -global using global::Files.Core.Data.Messages; -global using global::Files.Core.Data.Models; -global using global::Files.Core.Extensions; -global using global::Files.Core.Helpers; -global using global::Files.Core.Services; -global using global::Files.Core.Services.Settings; -global using global::Files.Core.Services.SizeProvider; -global using global::Files.Core.ViewModels; -global using global::Files.Core.Utils; -global using global::Files.Core.Utils.CommandLine; diff --git a/src/Files.Shared/Files.Shared.csproj b/src/Files.Shared/Files.Shared.csproj index c38fc9258103..30954645ae4e 100644 --- a/src/Files.Shared/Files.Shared.csproj +++ b/src/Files.Shared/Files.Shared.csproj @@ -19,8 +19,6 @@ - - From 2a1a4278d6a699cf2fdb9a1354957d729bd7bb28 Mon Sep 17 00:00:00 2001 From: 0x5bfa <62196528+0x5bfa@users.noreply.github.com> Date: Thu, 4 Apr 2024 05:21:43 +0900 Subject: [PATCH 14/41] Feature: Improved the display of keyboard shortcuts in the Command Palette (#15106) --- src/Files.App/Data/Commands/HotKey/HotKey.cs | 4 +- .../Data/Items/NavigationBarSuggestionItem.cs | 7 + .../UserControls/AddressToolbar.xaml | 42 +++-- .../KeyboardShortcut.Properties.cs | 65 +++++++ .../KeyboardShortcut/KeyboardShortcut.cs | 103 +++++++++++ .../KeyboardShortcut/KeyboardShortcut.xaml | 171 ++++++++++++++++++ .../KeyboardShortcutItem.Properties.cs | 65 +++++++ .../KeyboardShortcut/KeyboardShortcutItem.cs | 46 +++++ .../KeyboardShortcutItemKind.cs | 14 ++ .../KeyboardShortcutItemSize.cs | 14 ++ .../KeyboardShortcutItemStyleSelector.cs | 33 ++++ .../UserControls/AddressToolbarViewModel.cs | 9 +- 12 files changed, 550 insertions(+), 23 deletions(-) create mode 100644 src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcut.Properties.cs create mode 100644 src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcut.cs create mode 100644 src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcut.xaml create mode 100644 src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcutItem.Properties.cs create mode 100644 src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcutItem.cs create mode 100644 src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcutItemKind.cs create mode 100644 src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcutItemSize.cs create mode 100644 src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcutItemStyleSelector.cs diff --git a/src/Files.App/Data/Commands/HotKey/HotKey.cs b/src/Files.App/Data/Commands/HotKey/HotKey.cs index 424e9bbe73e4..8b90f42fc04e 100644 --- a/src/Files.App/Data/Commands/HotKey/HotKey.cs +++ b/src/Files.App/Data/Commands/HotKey/HotKey.cs @@ -11,7 +11,7 @@ namespace Files.App.Data.Commands [DebuggerDisplay("{Code}")] public readonly struct HotKey : IEquatable { - private static readonly FrozenDictionary modifiers = new Dictionary() + public static readonly FrozenDictionary modifiers = new Dictionary() { [KeyModifiers.Menu] = GetKeyString("Menu"), [KeyModifiers.Ctrl] = GetKeyString("Control"), @@ -19,7 +19,7 @@ namespace Files.App.Data.Commands [KeyModifiers.Win] = GetKeyString("Windows"), }.ToFrozenDictionary(); - private static readonly FrozenDictionary keys = new Dictionary() + public static readonly FrozenDictionary keys = new Dictionary() { [Keys.Enter] = GetKeyString("Enter"), [Keys.Space] = GetKeyString("Space"), diff --git a/src/Files.App/Data/Items/NavigationBarSuggestionItem.cs b/src/Files.App/Data/Items/NavigationBarSuggestionItem.cs index f78dd6673e94..90ff2a4a4df5 100644 --- a/src/Files.App/Data/Items/NavigationBarSuggestionItem.cs +++ b/src/Files.App/Data/Items/NavigationBarSuggestionItem.cs @@ -69,6 +69,13 @@ public sealed class NavigationBarSuggestionItem : ObservableObject set => SetProperty(ref _SupplementaryDisplay, value); } + private HotKeyCollection _HotKeys = new(); + public HotKeyCollection HotKeys + { + get => _HotKeys; + set => SetProperty(ref _HotKeys, value); + } + private void UpdatePrimaryDisplay() { if (SearchText is null || PrimaryDisplay is null) diff --git a/src/Files.App/UserControls/AddressToolbar.xaml b/src/Files.App/UserControls/AddressToolbar.xaml index f60185b763b4..89f8b2a05a19 100644 --- a/src/Files.App/UserControls/AddressToolbar.xaml +++ b/src/Files.App/UserControls/AddressToolbar.xaml @@ -12,6 +12,7 @@ xmlns:extensions="using:CommunityToolkit.WinUI.UI" xmlns:helpers="using:Files.App.Helpers" xmlns:items="using:Files.App.Data.Items" + xmlns:keyboard="using:Files.App.UserControls.KeyboardShortcut" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:triggers="using:CommunityToolkit.WinUI.UI.Triggers" xmlns:uc="using:Files.App.UserControls" @@ -28,6 +29,8 @@ + + @@ -313,34 +316,39 @@ - + - - - + + - - + - + Foreground="{ThemeResource TextFillColorSecondaryBrush}" + Text="{x:Bind SecondaryDisplay, Mode=OneWay}" /> - - + + + + diff --git a/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcut.Properties.cs b/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcut.Properties.cs new file mode 100644 index 000000000000..3194cdf177ba --- /dev/null +++ b/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcut.Properties.cs @@ -0,0 +1,65 @@ +// Copyright (c) 2023 Files Community +// Licensed under the MIT License. See the LICENSE. + +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; + +namespace Files.App.UserControls.KeyboardShortcut +{ + public sealed partial class KeyboardShortcut + { + public static readonly DependencyProperty ItemTypeProperty = + DependencyProperty.Register( + nameof(ItemType), + typeof(KeyboardShortcutItemKind), + typeof(KeyboardShortcutItem), + new PropertyMetadata(defaultValue: KeyboardShortcutItemKind.Outlined, (d, e) => ((KeyboardShortcutItem)d).OnItemTypePropertyChanged())); + + public KeyboardShortcutItemKind ItemType + { + get => (KeyboardShortcutItemKind)GetValue(ItemTypeProperty); + set => SetValue(ItemTypeProperty, value); + } + + public static readonly DependencyProperty SizeProperty = + DependencyProperty.Register( + nameof(Size), + typeof(KeyboardShortcutItemSize), + typeof(KeyboardShortcutItem), + new PropertyMetadata(defaultValue: KeyboardShortcutItemSize.Small, (d, e) => ((KeyboardShortcutItem)d).OnSizePropertyChanged())); + + public KeyboardShortcutItemSize Size + { + get => (KeyboardShortcutItemSize)GetValue(SizeProperty); + set => SetValue(SizeProperty, value); + } + + public static readonly DependencyProperty HotKeysProperty = + DependencyProperty.Register( + nameof(HotKeys), + typeof(HotKeyCollection), + typeof(KeyboardShortcut), + new PropertyMetadata(defaultValue: new(), (d, e) => ((KeyboardShortcut)d).OnHotKeysPropertyChanged())); + + public HotKeyCollection HotKeys + { + get => (HotKeyCollection)GetValue(HotKeysProperty); + set => SetValue(HotKeysProperty, value); + } + + public void OnItemTypePropertyChanged() + { + OnItemTypeChanged(); + } + + public void OnSizePropertyChanged() + { + OnSizeChanged(); + } + + private void OnHotKeysPropertyChanged() + { + OnHotKeysChanged(); + } + } +} diff --git a/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcut.cs b/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcut.cs new file mode 100644 index 000000000000..a1398e9b8cbd --- /dev/null +++ b/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcut.cs @@ -0,0 +1,103 @@ +// Copyright (c) 2023 Files Community +// Licensed under the MIT License. See the LICENSE. + +using CommunityToolkit.WinUI.UI; +using Microsoft.UI.Input; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Automation; +using Microsoft.UI.Xaml.Automation.Peers; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Controls.Primitives; + +namespace Files.App.UserControls.KeyboardShortcut +{ + public sealed partial class KeyboardShortcut : Control + { + internal const string KeyboardShortcutItemsControl = "PART_KeyboardShortcutItemsControl"; + + public KeyboardShortcut() + { + DefaultStyleKey = typeof(KeyboardShortcut); + } + + private void OnItemTypeChanged() + { + } + + private void OnSizeChanged() + { + } + + private void OnHotKeysChanged() + { + if (HotKeys.IsEmpty) + return; + + List items = new(); + + foreach (var item in HotKeys) + { + if (items.Any()) + { + items.Add(new() { Text = ",", ItemType = KeyboardShortcutItemKind.TextOnly, Size = Size }); + } + + switch(item.Key, item.Modifier) + { + // No keys or modifiers specified + case (Keys.None, KeyModifiers.None): + break; + + // Key modifiers only + case (Keys.None, _): + GetModifierCode(item.Modifier); + items.RemoveAt(items.Count - 1); + break; + + // Keys only + case (_, KeyModifiers.None): + var key = HotKey.keys[item.Key]; + items.Add(new() { Text = key, ItemType = ItemType, Size = Size }); + break; + + // Others + default: + GetModifierCode(item.Modifier); + key = HotKey.keys[item.Key]; + items.Add(new() { Text = key, ItemType = ItemType, Size = Size }); + break; + } + + void GetModifierCode(KeyModifiers modifier) + { + if (modifier.HasFlag(KeyModifiers.Menu)) + { + items.Add(new() { Text = HotKey.modifiers[KeyModifiers.Menu], ItemType = ItemType, Size = Size }); + items.Add(new() { Text = "+", ItemType = KeyboardShortcutItemKind.TextOnly, Size = Size }); + } + if (modifier.HasFlag(KeyModifiers.Ctrl)) + { + items.Add(new() { Text = HotKey.modifiers[KeyModifiers.Ctrl], ItemType = ItemType, Size = Size }); + items.Add(new() { Text = "+", ItemType = KeyboardShortcutItemKind.TextOnly, Size = Size }); + } + if (modifier.HasFlag(KeyModifiers.Shift)) + { + items.Add(new() { Text = HotKey.modifiers[KeyModifiers.Shift], ItemType = ItemType, Size = Size }); + items.Add(new() { Text = "+", ItemType = KeyboardShortcutItemKind.TextOnly, Size = Size }); + } + if (modifier.HasFlag(KeyModifiers.Win)) + { + items.Add(new() { Text = HotKey.modifiers[KeyModifiers.Win], ItemType = ItemType, Size = Size }); + items.Add(new() { Text = "+", ItemType = KeyboardShortcutItemKind.TextOnly, Size = Size }); + } + } + } + + // Set value + if (GetTemplateChild(KeyboardShortcutItemsControl) is ItemsControl keyboardShortcutItemsControl) + { + keyboardShortcutItemsControl.ItemsSource = items; + } + } + } +} diff --git a/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcut.xaml b/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcut.xaml new file mode 100644 index 000000000000..1cc08525a54e --- /dev/null +++ b/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcut.xaml @@ -0,0 +1,171 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcutItem.Properties.cs b/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcutItem.Properties.cs new file mode 100644 index 000000000000..7b694933119d --- /dev/null +++ b/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcutItem.Properties.cs @@ -0,0 +1,65 @@ +// Copyright (c) 2023 Files Community +// Licensed under the MIT License. See the LICENSE. + +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; + +namespace Files.App.UserControls.KeyboardShortcut +{ + public sealed partial class KeyboardShortcutItem + { + public static readonly DependencyProperty ItemTypeProperty = + DependencyProperty.Register( + nameof(ItemType), + typeof(KeyboardShortcutItemKind), + typeof(KeyboardShortcutItem), + new PropertyMetadata(defaultValue: KeyboardShortcutItemKind.Outlined, (d, e) => ((KeyboardShortcutItem)d).OnItemTypePropertyChanged())); + + public KeyboardShortcutItemKind ItemType + { + get => (KeyboardShortcutItemKind)GetValue(ItemTypeProperty); + set => SetValue(ItemTypeProperty, value); + } + + public static readonly DependencyProperty SizeProperty = + DependencyProperty.Register( + nameof(Size), + typeof(KeyboardShortcutItemSize), + typeof(KeyboardShortcutItem), + new PropertyMetadata(defaultValue: KeyboardShortcutItemSize.Small, (d, e) => ((KeyboardShortcutItem)d).OnSizePropertyChanged())); + + public KeyboardShortcutItemSize Size + { + get => (KeyboardShortcutItemSize)GetValue(SizeProperty); + set => SetValue(SizeProperty, value); + } + + public static readonly DependencyProperty TextProperty = + DependencyProperty.Register( + nameof(Text), + typeof(string), + typeof(KeyboardShortcutItem), + new PropertyMetadata(defaultValue: string.Empty, (d, e) => ((KeyboardShortcutItem)d).OnTextPropertyChanged())); + + public string Text + { + get => (string)GetValue(TextProperty); + set => SetValue(TextProperty, value); + } + + public void OnItemTypePropertyChanged() + { + OnItemTypeChanged(); + } + + public void OnSizePropertyChanged() + { + OnSizeChanged(); + } + + public void OnTextPropertyChanged() + { + OnTextChanged(); + } + } +} diff --git a/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcutItem.cs b/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcutItem.cs new file mode 100644 index 000000000000..fb174fceda9a --- /dev/null +++ b/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcutItem.cs @@ -0,0 +1,46 @@ +// Copyright (c) 2023 Files Community +// Licensed under the MIT License. See the LICENSE. + +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; + +namespace Files.App.UserControls.KeyboardShortcut +{ + public sealed partial class KeyboardShortcutItem : Control + { + internal const string SmallState = "Small"; + internal const string MediumState = "Medium"; + internal const string LargeState = "Large"; + + internal const string MainTextTextBlock = "PART_MainTextTextBlock"; + + public KeyboardShortcutItem() + { + DefaultStyleKey = typeof(KeyboardShortcutItem); + } + + private void OnItemTypeChanged() + { + } + + private void OnSizeChanged() + { + switch (Size) + { + case KeyboardShortcutItemSize.Small: + VisualStateManager.GoToState(this, SmallState, true); + break; + case KeyboardShortcutItemSize.Medium: + VisualStateManager.GoToState(this, MediumState, true); + break; + case KeyboardShortcutItemSize.Large: + VisualStateManager.GoToState(this, LargeState, true); + break; + } + } + + private void OnTextChanged() + { + } + } +} diff --git a/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcutItemKind.cs b/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcutItemKind.cs new file mode 100644 index 000000000000..2233a13cccb9 --- /dev/null +++ b/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcutItemKind.cs @@ -0,0 +1,14 @@ +// Copyright (c) 2023 Files Community +// Licensed under the MIT License. See the LICENSE. + +namespace Files.App.UserControls.KeyboardShortcut +{ + public enum KeyboardShortcutItemKind + { + Outlined, + + Filled, + + TextOnly, + } +} diff --git a/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcutItemSize.cs b/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcutItemSize.cs new file mode 100644 index 000000000000..80a629d00421 --- /dev/null +++ b/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcutItemSize.cs @@ -0,0 +1,14 @@ +// Copyright (c) 2023 Files Community +// Licensed under the MIT License. See the LICENSE. + +namespace Files.App.UserControls.KeyboardShortcut +{ + public enum KeyboardShortcutItemSize + { + Small, + + Medium, + + Large, + } +} diff --git a/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcutItemStyleSelector.cs b/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcutItemStyleSelector.cs new file mode 100644 index 000000000000..132e916c11c0 --- /dev/null +++ b/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcutItemStyleSelector.cs @@ -0,0 +1,33 @@ +// Copyright (c) 2023 Files Community +// Licensed under the MIT License. See the LICENSE. + +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; + +namespace Files.App.UserControls.KeyboardShortcut +{ + public class KeyboardShortcutItemStyleSelector : StyleSelector + { + public Style OutlinedItemStyle { get; set; } = null!; + + public Style FilledItemStyle { get; set; } = null!; + + public Style TextOnlyItemStyle { get; set; } = null!; + + protected override Style SelectStyleCore(object item, DependencyObject container) + { + if (container is KeyboardShortcutItem keyboardShortcutItem) + { + return keyboardShortcutItem.ItemType switch + { + KeyboardShortcutItemKind.Outlined => OutlinedItemStyle, + KeyboardShortcutItemKind.Filled => FilledItemStyle, + KeyboardShortcutItemKind.TextOnly => TextOnlyItemStyle, + _ => OutlinedItemStyle, + }; + } + + return OutlinedItemStyle; + } + } +} diff --git a/src/Files.App/ViewModels/UserControls/AddressToolbarViewModel.cs b/src/Files.App/ViewModels/UserControls/AddressToolbarViewModel.cs index 01603ecaa120..ff16fb2ec9fa 100644 --- a/src/Files.App/ViewModels/UserControls/AddressToolbarViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/AddressToolbarViewModel.cs @@ -839,14 +839,15 @@ public async Task SetAddressBarSuggestionsAsync(AutoSuggestBox sender, IShellPag { IsCommandPaletteOpen = true; var searchText = sender.Text.Substring(1).Trim(); - suggestions = Commands.Where(command => command.IsExecutable && - (command.Description.Contains(searchText, StringComparison.OrdinalIgnoreCase) - || command.Code.ToString().Contains(searchText, StringComparison.OrdinalIgnoreCase))) + suggestions = Commands.Where(command => + command.IsExecutable && + (command.Description.Contains(searchText, StringComparison.OrdinalIgnoreCase) || + command.Code.ToString().Contains(searchText, StringComparison.OrdinalIgnoreCase))) .Select(command => new NavigationBarSuggestionItem() { Text = ">" + command.Code, PrimaryDisplay = command.Description, - SupplementaryDisplay = command.HotKeyText, + HotKeys = command.HotKeys, SearchText = searchText, }).ToList(); } From 49f2cf2a7a37adcfbfdb705f66e733a054f8ddaa Mon Sep 17 00:00:00 2001 From: mbartlett21 <29034492+mbartlett21@users.noreply.github.com> Date: Thu, 4 Apr 2024 08:55:59 +1000 Subject: [PATCH 15/41] Feature: Added support for listing multiple SharePoint drives in the sidebar (#15070) --- .../Utils/Cloud/CloudDrivesDetector.cs | 36 ++++++++++--------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/src/Files.App/Utils/Cloud/CloudDrivesDetector.cs b/src/Files.App/Utils/Cloud/CloudDrivesDetector.cs index f2bf78ad7e34..915b54de34c5 100644 --- a/src/Files.App/Utils/Cloud/CloudDrivesDetector.cs +++ b/src/Files.App/Utils/Cloud/CloudDrivesDetector.cs @@ -190,14 +190,14 @@ private static Task> DetectSharepoint() var sharepointAccounts = new List(); foreach (var account in oneDriveAccountsKey.GetSubKeyNames()) { - var accountKeyName = @$"{oneDriveAccountsKey.Name}\{account}"; - var displayName = (string)Registry.GetValue(accountKeyName, "DisplayName", null); - var userFolderToExcludeFromResults = (string)Registry.GetValue(accountKeyName, "UserFolder", null); - var accountName = string.IsNullOrWhiteSpace(displayName) ? "SharePoint" : $"SharePoint - {displayName}"; + var accountKey = oneDriveAccountsKey.OpenSubKey(account); + if (accountKey is null) + continue; - var sharePointSyncFolders = new List(); - var mountPointKeyName = @$"SOFTWARE\Microsoft\OneDrive\Accounts\{account}\ScopeIdToMountPointPathCache"; - using (var mountPointsKey = Registry.CurrentUser.OpenSubKey(mountPointKeyName)) + var userFolderToExcludeFromResults = (string)accountKey.GetValue("UserFolder", ""); + + var sharePointParentFolders = new List(); + using (var mountPointsKey = accountKey.OpenSubKey("ScopeIdToMountPointPathCache")) { if (mountPointsKey is null) { @@ -207,25 +207,27 @@ private static Task> DetectSharepoint() var valueNames = mountPointsKey.GetValueNames(); foreach (var valueName in valueNames) { - var value = (string)Registry.GetValue(@$"HKEY_CURRENT_USER\{mountPointKeyName}", valueName, null); - if (!string.Equals(value, userFolderToExcludeFromResults, StringComparison.OrdinalIgnoreCase)) + var directory = (string?)mountPointsKey.GetValue(valueName, null); + if (directory != null && !string.Equals(directory, userFolderToExcludeFromResults, StringComparison.OrdinalIgnoreCase)) { - sharePointSyncFolders.Add(value); + var parentFolder = Directory.GetParent(directory); + if (parentFolder != null) + sharePointParentFolders.Add(parentFolder); } } } - sharePointSyncFolders.Sort(StringComparer.Ordinal); - foreach (var sharePointSyncFolder in sharePointSyncFolders) + sharePointParentFolders.Sort((left, right) => left.FullName.CompareTo(right.FullName)); + + foreach (var sharePointParentFolder in sharePointParentFolders) { - var parentFolder = Directory.GetParent(sharePointSyncFolder)?.FullName ?? string.Empty; - if (!sharepointAccounts.Any(acc => - string.Equals(acc.Name, accountName, StringComparison.OrdinalIgnoreCase)) && !string.IsNullOrWhiteSpace(parentFolder)) + string name = $"SharePoint - {sharePointParentFolder.Name}"; + if (!sharepointAccounts.Any(acc => string.Equals(acc.Name, name, StringComparison.OrdinalIgnoreCase))) { sharepointAccounts.Add(new CloudProvider(CloudProviders.OneDriveCommercial) { - Name = accountName, - SyncFolder = parentFolder, + Name = name, + SyncFolder = sharePointParentFolder.FullName, }); } } From 5831ac2fd253df48619ca4d2d9fe68909b180e60 Mon Sep 17 00:00:00 2001 From: Gustavo Mauricio de Barros Date: Thu, 4 Apr 2024 12:11:14 -0300 Subject: [PATCH 16/41] Feature: Follow file extension setting for Recent Files widget (#15113) --- src/Files.App/Utils/RecentItem/RecentItem.cs | 10 ++++------ src/Files.App/Utils/RecentItem/RecentItems.cs | 12 +++++++++--- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/Files.App/Utils/RecentItem/RecentItem.cs b/src/Files.App/Utils/RecentItem/RecentItem.cs index 85201d4a55f1..e9491b1dd4e9 100644 --- a/src/Files.App/Utils/RecentItem/RecentItem.cs +++ b/src/Files.App/Utils/RecentItem/RecentItem.cs @@ -2,8 +2,6 @@ // Licensed under the MIT License. See the LICENSE. using Microsoft.UI.Xaml.Media.Imaging; -using Windows.Storage; -using Windows.Storage.FileProperties; namespace Files.App.Utils.RecentItem { @@ -40,11 +38,11 @@ public RecentItem(string linkPath) : base() /// /// Create a RecentItem from a ShellLinkItem (usually from shortcuts in `Windows\Recent`) /// - public RecentItem(ShellLinkItem linkItem) : base() + public RecentItem(ShellLinkItem linkItem, bool showFileExtension) : base() { LinkPath = linkItem.FilePath; RecentPath = linkItem.TargetPath; - Name = NameOrPathWithoutExtension(linkItem.FileName); + Name = showFileExtension ? linkItem.FileName : NameOrPathWithoutExtension(linkItem.FileName); LastModified = linkItem.ModifiedDate; PIDL = linkItem.PIDL; } @@ -53,11 +51,11 @@ public RecentItem(ShellLinkItem linkItem) : base() /// Create a RecentItem from a ShellFileItem (usually from enumerating Quick Access directly). /// /// The shell file item - public RecentItem(ShellFileItem fileItem) : base() + public RecentItem(ShellFileItem fileItem, bool showFileExtension) : base() { LinkPath = ShellStorageFolder.IsShellPath(fileItem.FilePath) ? fileItem.RecyclePath : fileItem.FilePath; // use true path on disk for shell items RecentPath = LinkPath; // intentionally the same - Name = NameOrPathWithoutExtension(fileItem.FileName); + Name = showFileExtension ? fileItem.FileName : NameOrPathWithoutExtension(fileItem.FileName); LastModified = fileItem.ModifiedDate; PIDL = fileItem.PIDL; } diff --git a/src/Files.App/Utils/RecentItem/RecentItems.cs b/src/Files.App/Utils/RecentItem/RecentItems.cs index 87e2c381cb0d..dd05f8a6fa74 100644 --- a/src/Files.App/Utils/RecentItem/RecentItems.cs +++ b/src/Files.App/Utils/RecentItem/RecentItems.cs @@ -43,9 +43,15 @@ public sealed class RecentItems : IDisposable } } - public RecentItems() + private readonly IUserSettingsService UserSettingsService; + + private bool ShowFileExtensions => UserSettingsService.FoldersSettingsService.ShowFileExtensions; + + + public RecentItems(IUserSettingsService userSettingsService) { RecentItemsManager.Default.RecentItemsChanged += OnRecentItemsChangedAsync; + UserSettingsService = userSettingsService; } private async void OnRecentItemsChangedAsync(object? sender, EventArgs e) @@ -105,7 +111,7 @@ public async Task> ListRecentFilesAsync() { return (await Win32Helper.GetShellFolderAsync(QuickAccessGuid, "Enumerate", 0, int.MaxValue)).Enumerate .Where(link => !link.IsFolder) - .Select(link => new RecentItem(link)).ToList(); + .Select(link => new RecentItem(link, ShowFileExtensions)).ToList(); } /// @@ -127,7 +133,7 @@ public async Task> ListRecentFoldersAsync() if (!string.IsNullOrEmpty(link.TargetPath) && link.Target.IsFolder) { var shellLinkItem = ShellFolderExtensions.GetShellLinkItem(link); - return new RecentItem(shellLinkItem); + return new RecentItem(shellLinkItem, ShowFileExtensions); } } catch (FileNotFoundException) From 07bfd666cb035c069b486c4f2d1c200bf8f0959d Mon Sep 17 00:00:00 2001 From: Nikhil B <59918974+heftymouse@users.noreply.github.com> Date: Thu, 4 Apr 2024 20:42:56 +0530 Subject: [PATCH 17/41] GitHub: Fixed formatting workflow (#15114) --- .github/workflows/format-xaml.yml | 35 +++++++++++++++++++------------ 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/.github/workflows/format-xaml.yml b/.github/workflows/format-xaml.yml index 9e72b8288deb..ebe9a94530a9 100644 --- a/.github/workflows/format-xaml.yml +++ b/.github/workflows/format-xaml.yml @@ -7,7 +7,6 @@ jobs: format-xaml: if: github.event.issue.pull_request && github.event.comment.body == '/format' runs-on: ubuntu-latest - environment: Pull Requests defaults: run: shell: pwsh @@ -15,12 +14,10 @@ jobs: steps: - name: Generate GitHub Apps token id: generate - uses: tibdex/github-app-token@v1 + uses: actions/create-github-app-token@v1 with: - app_id: ${{ secrets.BOT_APP_ID }} - - private_key: ${{ secrets.BOT_PRIVATE_KEY }} - + app-id: ${{ secrets.BOT_APP_ID }} + private-key: ${{ secrets.BOT_PRIVATE_KEY }} - name: Create run condition variable run: | @@ -43,6 +40,9 @@ jobs: # all steps after this one must have the env.CAN_RUN == 1 condition + - uses: actions/checkout@v4 + if: env.CAN_RUN == 1 + - name: Set git identity if: env.CAN_RUN == 1 run: | @@ -56,10 +56,6 @@ jobs: git config --global user.name "$($data.login)" git config --global user.email "$($data.databaseId)+$($data.login)@users.noreply.github.com" - - - uses: actions/checkout@v4 - if: env.CAN_RUN == 1 - - name: Checkout PR if: env.CAN_RUN == 1 run: gh pr checkout ${{ github.event.issue.number }} -b pr @@ -86,8 +82,17 @@ jobs: - name: Commit formatted files if: env.CAN_RUN == 1 run: | - git add . - git commit -m "Formatted XAML files" + git add --renormalize *.xaml + git status --porcelain + if ((git status --porcelain) -eq $null) + { + gh pr comment ${{ github.event.issue.number }} -b "No XAML files changed." + "CAN_RUN=0" | Out-File -FilePath $env:GITHUB_ENV -Append + } + else + { + git commit -m "Formatted XAML files" + } - name: Push to PR if: env.CAN_RUN == 1 @@ -113,7 +118,7 @@ jobs: }' -F owner=$owner -F name=$name -F number=$number --jq '{Branch: .data.repository.pullRequest.headRef.name, Url: .data.repository.pullRequest.headRepository.url}' | ConvertFrom-Json $url = [UriBuilder]($data.Url) - $url.UserName = 'Personal Access Token' + $url.UserName = 'x-access-token' $url.Password = '${{ steps.generate.outputs.token }}' git push $url.Uri.AbsoluteUri pr:$($data.Branch) @@ -121,6 +126,10 @@ jobs: { gh pr comment ${{ github.event.issue.number }} -b "Successfully formatted XAML files." } + else + { + "CAN_RUN=0" | Out-File -FilePath $env:GITHUB_ENV -Append + } continue-on-error: true - name: Comment if failed From 59e2158081107e69566a8f26374a1ddbce7d0c0e Mon Sep 17 00:00:00 2001 From: Yair <39923744+yaira2@users.noreply.github.com> Date: Thu, 4 Apr 2024 09:28:45 -0700 Subject: [PATCH 18/41] Preview: v3.3.1 --- src/Files.App (Package)/Package.appxmanifest | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Files.App (Package)/Package.appxmanifest b/src/Files.App (Package)/Package.appxmanifest index 74b8ea2fe6df..a85d70a25e16 100644 --- a/src/Files.App (Package)/Package.appxmanifest +++ b/src/Files.App (Package)/Package.appxmanifest @@ -16,7 +16,7 @@ + Version="3.3.1.0" /> Files - Dev From 49d514454d3d62bb772d40b617576254f5892dc9 Mon Sep 17 00:00:00 2001 From: Nikhil B <59918974+heftymouse@users.noreply.github.com> Date: Fri, 5 Apr 2024 01:31:49 +0530 Subject: [PATCH 19/41] GitHub: Updated formatting workflow messages (#15116) --- .github/workflows/format-xaml.yml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/.github/workflows/format-xaml.yml b/.github/workflows/format-xaml.yml index ebe9a94530a9..39282446b005 100644 --- a/.github/workflows/format-xaml.yml +++ b/.github/workflows/format-xaml.yml @@ -10,6 +10,8 @@ jobs: defaults: run: shell: pwsh + permissions: + contents: write steps: - name: Generate GitHub Apps token @@ -34,7 +36,7 @@ jobs: run: | if (!(gh pr -R ${{ github.repository }} view ${{ github.event.issue.number }} --json maintainerCanModify -q '.maintainerCanModify' | ConvertFrom-Json)) { - gh pr comment ${{ github.event.issue.number }} -b "This PR cannot be committed to. Ensure that Allow edits from maintainers is enabled." + gh pr comment ${{ github.event.issue.number }} -b "🔒 This PR cannot be committed to. Ensure that Allow edits from maintainers is enabled." "CAN_RUN=0" | Out-File -FilePath $env:GITHUB_ENV -Append } @@ -67,7 +69,7 @@ jobs: $changedFiles = (git diff --name-only pr..$baseRef) -split "\n" | Where-Object {$_ -like "*.xaml"} if ($changedFiles.Count -eq 0) { - gh pr comment ${{ github.event.issue.number }} -b "No XAML files found to format." + gh pr comment ${{ github.event.issue.number }} -b "⛔ No XAML files found to format." "CAN_RUN=0" | Out-File -FilePath $env:GITHUB_ENV -Append } @@ -86,7 +88,7 @@ jobs: git status --porcelain if ((git status --porcelain) -eq $null) { - gh pr comment ${{ github.event.issue.number }} -b "No XAML files changed." + gh pr comment ${{ github.event.issue.number }} -b "⛔ No XAML files changed." "CAN_RUN=0" | Out-File -FilePath $env:GITHUB_ENV -Append } else @@ -124,7 +126,7 @@ jobs: if ($LASTEXITCODE -eq 0) { - gh pr comment ${{ github.event.issue.number }} -b "Successfully formatted XAML files." + gh pr comment ${{ github.event.issue.number }} -b "✅ Successfully formatted XAML files." } else { @@ -133,5 +135,5 @@ jobs: continue-on-error: true - name: Comment if failed - if: failure() && env.CAN_RUN == 1 - run: gh pr comment ${{ github.event.issue.number }} -b "Failed to format XAML files, check logs for more information." + if: failure() && env.CAN_RUN == 0 + run: gh pr comment ${{ github.event.issue.number }} -b "⚠️ Failed to format XAML files, check [the logs](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}) for more information." From ba56951113e4a22765e6cd532c69c2ac40641eaf Mon Sep 17 00:00:00 2001 From: Yair <39923744+yaira2@users.noreply.github.com> Date: Thu, 4 Apr 2024 12:38:43 -0400 Subject: [PATCH 20/41] GitHub: Update actions --- .github/workflows/cd-preview.yml | 6 +- .github/workflows/cd-stable.yml | 6 +- .github/workflows/deploy-preview-legacy.yml | 179 ++++++++++++++++++++ .github/workflows/deploy-stable-legacy.yml | 179 ++++++++++++++++++++ .github/workflows/format-xaml.yml | 1 + 5 files changed, 365 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/deploy-preview-legacy.yml create mode 100644 .github/workflows/deploy-stable-legacy.yml diff --git a/.github/workflows/cd-preview.yml b/.github/workflows/cd-preview.yml index 4d8a75256594..958613b8c366 100644 --- a/.github/workflows/cd-preview.yml +++ b/.github/workflows/cd-preview.yml @@ -101,13 +101,13 @@ jobs: $fileContent = $fileContent.Replace("http://schemas.microsoft.com/appx/appinstaller/2017/2", $newSchema) $fileContent | Set-Content $localFilePath - - name: Sign files with Azure Code Signing - uses: azure/azure-code-signing-action@v0.3.0 + - name: Sign files with Azure Trusted Signing + uses: azure/trusted-signing-action@v0.3.16 with: azure-tenant-id: ${{ secrets.AZURE_TENANT_ID }} azure-client-id: ${{ secrets.AZURE_CLIENT_ID }} azure-client-secret: ${{ secrets.AZURE_CLIENT_SECRET }} - endpoint: https://wus2.codesigning.azure.net/ + endpoint: https://eus.codesigning.azure.net/ code-signing-account-name: ${{ secrets.SIGNING_ACCOUNT_NAME }} certificate-profile-name: ${{ secrets.SIGNING_PROFILE_NAME }} files-folder: ${{ env.APPX_PACKAGE_DIR }} diff --git a/.github/workflows/cd-stable.yml b/.github/workflows/cd-stable.yml index df00b0b887bc..88a720915d6b 100644 --- a/.github/workflows/cd-stable.yml +++ b/.github/workflows/cd-stable.yml @@ -101,13 +101,13 @@ jobs: $fileContent = $fileContent.Replace("http://schemas.microsoft.com/appx/appinstaller/2017/2", $newSchema) $fileContent | Set-Content $localFilePath - - name: Sign files with Azure Code Signing - uses: azure/azure-code-signing-action@v0.3.0 + - name: Sign files with Azure Trusted Signing + uses: azure/trusted-signing-action@v0.3.16 with: azure-tenant-id: ${{ secrets.AZURE_TENANT_ID }} azure-client-id: ${{ secrets.AZURE_CLIENT_ID }} azure-client-secret: ${{ secrets.AZURE_CLIENT_SECRET }} - endpoint: https://wus2.codesigning.azure.net/ + endpoint: https://eus.codesigning.azure.net/ code-signing-account-name: ${{ secrets.SIGNING_ACCOUNT_NAME }} certificate-profile-name: ${{ secrets.SIGNING_PROFILE_NAME }} files-folder: ${{ env.APPX_PACKAGE_DIR }} diff --git a/.github/workflows/deploy-preview-legacy.yml b/.github/workflows/deploy-preview-legacy.yml new file mode 100644 index 000000000000..cf3c6cc7572b --- /dev/null +++ b/.github/workflows/deploy-preview-legacy.yml @@ -0,0 +1,179 @@ +name: Deploy Preview Pipeline + +on: + workflow_dispatch: + +jobs: + build: + runs-on: windows-latest + environment: Deployments + strategy: + fail-fast: false + matrix: + configuration: [Preview] + platform: [x64] + env: + SOLUTION_NAME: 'Files.sln' + PACKAGE_PROJECT_DIR: 'src\Files.App (Package)' + PACKAGE_PROJECT_PATH: 'src\Files.App (Package)\Files.Package.wapproj' + TEST_PROJECT_PATH: 'tests\Files.InteractionTests\Files.InteractionTests.csproj' + CONFIGURATION: ${{ matrix.configuration }} + PLATFORM: ${{ matrix.platform }} + APPX_BUNDLE_PLATFORMS: 'x64|arm64' + WORKING_DIR: ${{ github.workspace }} # Default: D:\a\Files\Files\ + ARTIFACTS_STAGING_DIR: ${{ github.workspace }}\artifacts + APPX_PACKAGE_DIR: ${{ github.workspace }}\artifacts\AppxPackages + + steps: + - name: Checkout the repository + uses: actions/checkout@v4 + + - name: Setup MSBuild + uses: microsoft/setup-msbuild@v1 + + - name: Setup NuGet + uses: NuGet/setup-nuget@v1.1.1 + + - name: Setup .NET 8 + uses: actions/setup-dotnet@v3 + with: + dotnet-version: '8.0.x' + + # TODO: Move the command to PowerShell script instead + - name: Update Package.appxmanifest + shell: pwsh + run: | + [xml]$xmlDoc = Get-Content "$env:PACKAGE_PROJECT_DIR\Package.appxmanifest" + $xmlDoc.Package.Identity.Name="FilesPreview" + $xmlDoc.Package.Identity.Publisher="$env:SIDELOAD_PUBLISHER_SECRET" + $xmlDoc.Package.Properties.DisplayName="Files - Preview" + $xmlDoc.Package.Applications.Application.VisualElements.DisplayName="Files - Preview" + $xmlDoc.Save("$env:PACKAGE_PROJECT_DIR\Package.appxmanifest") + env: + SIDELOAD_PUBLISHER_SECRET: ${{ secrets.SIDELOAD_PUBLISHER_SECRET }} + + # TODO: Move the command to PowerShell script instead + - name: Use the ${{ env.CONFIGURATION }} logo sets + shell: pwsh + run: | + Get-ChildItem "$env:WORKING_DIR\src" -Include *.csproj, *.appxmanifest, *.wapproj, *.xaml -recurse | ForEach -Process ` + { ` + (Get-Content $_ -Raw | ForEach -Process { $_ -replace "Assets\\AppTiles\\Dev", "Assets\AppTiles\Preview" }) | ` + Set-Content $_ -NoNewline ` + } + + - name: Inject the Bing Maps API token + shell: pwsh + run: | + Get-ChildItem "$env:WORKING_DIR\src" -Include *.cs -recurse | ForEach-Object -Process ` + { ` + (Get-Content $_ -Raw | ForEach-Object -Process { $_ -replace "bingmapskey.secret", "$env:BING_MAPS_SECRET" }) | ` + Set-Content $_ -NoNewline ` + } + env: + BING_MAPS_SECRET: ${{ secrets.BING_MAPS_SECRET }} + + - name: Inject the AppCenter token + shell: pwsh + run: | + Get-ChildItem "$env:WORKING_DIR\src" -Include *.cs -recurse | ForEach-Object -Process ` + { ` + (Get-Content $_ -Raw | ForEach-Object -Process { $_ -replace "appcenter.secret", "$env:APP_CENTER_SECRET" }) | ` + Set-Content $_ -NoNewline ` + } + env: + APP_CENTER_SECRET: ${{ secrets.APP_CENTER_SECRET }} + + - name: Inject the GitHub OAuth client ID + run: | + Get-ChildItem "$env:WORKING_DIR\src" -Include *.cs -recurse | ForEach-Object -Process ` + { ` + (Get-Content $_ -Raw | ForEach-Object -Process { $_ -replace "githubclientid.secret", "$env:GH_OAUTH_CLIENT_ID" }) | ` + Set-Content $_ -NoNewline ` + } + env: + GH_OAUTH_CLIENT_ID: ${{ secrets.GH_OAUTH_CLIENT_ID }} + + - name: Use Windows SDK Preview + shell: cmd + run: | + for /f %%a in ('dir /b /a:d %localappdata%\Microsoft\VisualStudio\17*') do echo UsePreviews=True>%localappdata%\Microsoft\VisualStudio\%%a\sdk.txt + + - name: Restore NuGet + shell: pwsh + run: 'nuget restore $env:SOLUTION_NAME' + + - name: Restore ${{ env.SOLUTION_NAME }} + shell: pwsh + run: | + msbuild $env:SOLUTION_NAME ` + -t:Restore ` + -p:Platform=$env:PLATFORM ` + -p:Configuration=$env:CONFIGURATION ` + -p:PublishReadyToRun=true + + - name: Build ${{ env.SOLUTION_NAME }} + shell: pwsh + run: | + msbuild "$env:PACKAGE_PROJECT_PATH" ` + -t:Build ` + -t:_GenerateAppxPackage ` + -p:Platform=$env:PLATFORM ` + -p:Configuration=$env:CONFIGURATION ` + -p:AppxBundlePlatforms=$env:APPX_BUNDLE_PLATFORMS ` + -p:AppxPackageDir="$env:APPX_PACKAGE_DIR" ` + -p:AppxBundle=Always ` + -p:UapAppxPackageBuildMode=Sideload ` + -p:GenerateAppInstallerFile=True ` + -p:AppInstallerUri=https://cdn.files.community/files/preview/ + + - name: Remove empty files from the packages + shell: bash + run: find $ARTIFACTS_STAGING_DIR -empty -delete + + - name: Update appinstaller schema + run: | + $newSchema = "http://schemas.microsoft.com/appx/appinstaller/2018" + $localFilePath = "${{ env.APPX_PACKAGE_DIR }}/Files.Package.appinstaller" + $fileContent = Get-Content $localFilePath + $fileContent = $fileContent.Replace("http://schemas.microsoft.com/appx/appinstaller/2017/2", $newSchema) + $fileContent | Set-Content $localFilePath + + - name: Sign files with Azure Trusted Signing + uses: azure/trusted-signing-action@v0.3.16 + with: + azure-tenant-id: ${{ secrets.AZURE_TENANT_ID }} + azure-client-id: ${{ secrets.AZURE_CLIENT_ID }} + azure-client-secret: ${{ secrets.AZURE_CLIENT_SECRET }} + endpoint: https://eus.codesigning.azure.net/ + code-signing-account-name: ${{ secrets.SIGNING_ACCOUNT_NAME }} + certificate-profile-name: ${{ secrets.SIGNING_PROFILE_NAME }} + files-folder: ${{ env.APPX_PACKAGE_DIR }} + files-folder-filter: msixbundle + files-folder-recurse: true + files-folder-depth: 4 + file-digest: SHA256 + timestamp-rfc3161: http://timestamp.acs.microsoft.com + timestamp-digest: SHA256 + + - uses: azure/login@v1 + with: + creds: ${{ secrets.AZURE_CREDENTIALS }} + + - name: Upload to blob storage + uses: azure/powershell@v1 + with: + inlineScript: | + az storage blob upload-batch --account-name "filescommunity" --destination "files" --destination-path "preview" --source ${{ env.APPX_PACKAGE_DIR }} --overwrite true + azPSVersion: "latest" + + # Azure logout + - name: logout + run: | + az logout + + - name: Upload the packages to GitHub Actions + uses: actions/upload-artifact@v3 + with: + name: 'Appx Packages (${{ env.CONFIGURATION }}, ${{ env.PLATFORM }})' + path: ${{ env.ARTIFACTS_STAGING_DIR }} diff --git a/.github/workflows/deploy-stable-legacy.yml b/.github/workflows/deploy-stable-legacy.yml new file mode 100644 index 000000000000..64dc136979d9 --- /dev/null +++ b/.github/workflows/deploy-stable-legacy.yml @@ -0,0 +1,179 @@ +name: Deploy Stable Pipeline + +on: + workflow_dispatch: + +jobs: + build: + runs-on: windows-latest + environment: Deployments + strategy: + fail-fast: false + matrix: + configuration: [Stable] + platform: [x64] + env: + SOLUTION_NAME: 'Files.sln' + PACKAGE_PROJECT_DIR: 'src\Files.App (Package)' + PACKAGE_PROJECT_PATH: 'src\Files.App (Package)\Files.Package.wapproj' + TEST_PROJECT_PATH: 'tests\Files.InteractionTests\Files.InteractionTests.csproj' + CONFIGURATION: ${{ matrix.configuration }} + PLATFORM: ${{ matrix.platform }} + APPX_BUNDLE_PLATFORMS: 'x64|arm64' + WORKING_DIR: ${{ github.workspace }} # Default: D:\a\Files\Files\ + ARTIFACTS_STAGING_DIR: ${{ github.workspace }}\artifacts + APPX_PACKAGE_DIR: ${{ github.workspace }}\artifacts\AppxPackages + + steps: + - name: Checkout the repository + uses: actions/checkout@v4 + + - name: Setup MSBuild + uses: microsoft/setup-msbuild@v1 + + - name: Setup NuGet + uses: NuGet/setup-nuget@v1.1.1 + + - name: Setup .NET 8 + uses: actions/setup-dotnet@v3 + with: + dotnet-version: '8.0.x' + + # TODO: Move the command to PowerShell script instead + - name: Update Package.appxmanifest + shell: pwsh + run: | + [xml]$xmlDoc = Get-Content "$env:PACKAGE_PROJECT_DIR\Package.appxmanifest" + $xmlDoc.Package.Identity.Name="Files" + $xmlDoc.Package.Identity.Publisher="$env:SIDELOAD_PUBLISHER_SECRET" + $xmlDoc.Package.Properties.DisplayName="Files" + $xmlDoc.Package.Applications.Application.VisualElements.DisplayName="Files" + $xmlDoc.Save("$env:PACKAGE_PROJECT_DIR\Package.appxmanifest") + env: + SIDELOAD_PUBLISHER_SECRET: ${{ secrets.SIDELOAD_PUBLISHER_SECRET }} + + # TODO: Move the command to PowerShell script instead + - name: Use the ${{ env.CONFIGURATION }} logo sets + shell: pwsh + run: | + Get-ChildItem "$env:WORKING_DIR\src" -Include *.csproj, *.appxmanifest, *.wapproj, *.xaml -recurse | ForEach -Process ` + { ` + (Get-Content $_ -Raw | ForEach -Process { $_ -replace "Assets\\AppTiles\\Dev", "Assets\AppTiles\Release" }) | ` + Set-Content $_ -NoNewline ` + } + + - name: Inject the Bing Maps API token + shell: pwsh + run: | + Get-ChildItem "$env:WORKING_DIR\src" -Include *.cs -recurse | ForEach-Object -Process ` + { ` + (Get-Content $_ -Raw | ForEach-Object -Process { $_ -replace "bingmapskey.secret", "$env:BING_MAPS_SECRET" }) | ` + Set-Content $_ -NoNewline ` + } + env: + BING_MAPS_SECRET: ${{ secrets.BING_MAPS_SECRET }} + + - name: Inject the AppCenter token + shell: pwsh + run: | + Get-ChildItem "$env:WORKING_DIR\src" -Include *.cs -recurse | ForEach-Object -Process ` + { ` + (Get-Content $_ -Raw | ForEach-Object -Process { $_ -replace "appcenter.secret", "$env:APP_CENTER_SECRET" }) | ` + Set-Content $_ -NoNewline ` + } + env: + APP_CENTER_SECRET: ${{ secrets.APP_CENTER_SECRET }} + + - name: Inject the GitHub OAuth client ID + run: | + Get-ChildItem "$env:WORKING_DIR\src" -Include *.cs -recurse | ForEach-Object -Process ` + { ` + (Get-Content $_ -Raw | ForEach-Object -Process { $_ -replace "githubclientid.secret", "$env:GH_OAUTH_CLIENT_ID" }) | ` + Set-Content $_ -NoNewline ` + } + env: + GH_OAUTH_CLIENT_ID: ${{ secrets.GH_OAUTH_CLIENT_ID }} + + - name: Use Windows SDK Preview + shell: cmd + run: | + for /f %%a in ('dir /b /a:d %localappdata%\Microsoft\VisualStudio\17*') do echo UsePreviews=True>%localappdata%\Microsoft\VisualStudio\%%a\sdk.txt + + - name: Restore NuGet + shell: pwsh + run: 'nuget restore $env:SOLUTION_NAME' + + - name: Restore ${{ env.SOLUTION_NAME }} + shell: pwsh + run: | + msbuild $env:SOLUTION_NAME ` + -t:Restore ` + -p:Platform=$env:PLATFORM ` + -p:Configuration=$env:CONFIGURATION ` + -p:PublishReadyToRun=true + + - name: Build ${{ env.SOLUTION_NAME }} + shell: pwsh + run: | + msbuild "$env:PACKAGE_PROJECT_PATH" ` + -t:Build ` + -t:_GenerateAppxPackage ` + -p:Platform=$env:PLATFORM ` + -p:Configuration=$env:CONFIGURATION ` + -p:AppxBundlePlatforms=$env:APPX_BUNDLE_PLATFORMS ` + -p:AppxPackageDir="$env:APPX_PACKAGE_DIR" ` + -p:AppxBundle=Always ` + -p:UapAppxPackageBuildMode=Sideload ` + -p:GenerateAppInstallerFile=True ` + -p:AppInstallerUri=https://cdn.files.community/files/stable/ + + - name: Remove empty files from the packages + shell: bash + run: find $ARTIFACTS_STAGING_DIR -empty -delete + + - name: Update appinstaller schema + run: | + $newSchema = "http://schemas.microsoft.com/appx/appinstaller/2018" + $localFilePath = "${{ env.APPX_PACKAGE_DIR }}/Files.Package.appinstaller" + $fileContent = Get-Content $localFilePath + $fileContent = $fileContent.Replace("http://schemas.microsoft.com/appx/appinstaller/2017/2", $newSchema) + $fileContent | Set-Content $localFilePath + + - name: Sign files with Azure Trusted Signing + uses: azure/trusted-signing-action@v0.3.16 + with: + azure-tenant-id: ${{ secrets.AZURE_TENANT_ID }} + azure-client-id: ${{ secrets.AZURE_CLIENT_ID }} + azure-client-secret: ${{ secrets.AZURE_CLIENT_SECRET }} + endpoint: https://eus.codesigning.azure.net/ + code-signing-account-name: ${{ secrets.SIGNING_ACCOUNT_NAME }} + certificate-profile-name: ${{ secrets.SIGNING_PROFILE_NAME }} + files-folder: ${{ env.APPX_PACKAGE_DIR }} + files-folder-filter: msixbundle + files-folder-recurse: true + files-folder-depth: 4 + file-digest: SHA256 + timestamp-rfc3161: http://timestamp.acs.microsoft.com + timestamp-digest: SHA256 + + - uses: azure/login@v1 + with: + creds: ${{ secrets.AZURE_CREDENTIALS }} + + - name: Upload to blob storage + uses: azure/powershell@v1 + with: + inlineScript: | + az storage blob upload-batch --account-name "filescommunity" --destination "files" --destination-path "stable" --source ${{ env.APPX_PACKAGE_DIR }} --overwrite true + azPSVersion: "latest" + + # Azure logout + - name: logout + run: | + az logout + + - name: Upload the packages to GitHub Actions + uses: actions/upload-artifact@v3 + with: + name: 'Appx Packages (${{ env.CONFIGURATION }}, ${{ env.PLATFORM }})' + path: ${{ env.ARTIFACTS_STAGING_DIR }} diff --git a/.github/workflows/format-xaml.yml b/.github/workflows/format-xaml.yml index 39282446b005..f84fd5348a5a 100644 --- a/.github/workflows/format-xaml.yml +++ b/.github/workflows/format-xaml.yml @@ -7,6 +7,7 @@ jobs: format-xaml: if: github.event.issue.pull_request && github.event.comment.body == '/format' runs-on: ubuntu-latest + environment: Pull Requests defaults: run: shell: pwsh From 65e511dcef5ff75b58f8c07cb95b7d6589beadec Mon Sep 17 00:00:00 2001 From: Gustavo Mauricio de Barros Date: Sat, 6 Apr 2024 22:55:37 -0300 Subject: [PATCH 21/41] Code Quality: Fixed caused by useless predicate (#15126) --- src/Files.App (Package)/Package.appxmanifest | 1 - .../Database/LayoutPreferencesDatabase.cs | 16 ++++------------ .../Layout/LayoutPreferencesDatabaseManager.cs | 9 ++++----- 3 files changed, 8 insertions(+), 18 deletions(-) diff --git a/src/Files.App (Package)/Package.appxmanifest b/src/Files.App (Package)/Package.appxmanifest index a85d70a25e16..362569b90ba3 100644 --- a/src/Files.App (Package)/Package.appxmanifest +++ b/src/Files.App (Package)/Package.appxmanifest @@ -168,7 +168,6 @@ - diff --git a/src/Files.App.Server/Database/LayoutPreferencesDatabase.cs b/src/Files.App.Server/Database/LayoutPreferencesDatabase.cs index ba7ad974feed..828f8391b6fa 100644 --- a/src/Files.App.Server/Database/LayoutPreferencesDatabase.cs +++ b/src/Files.App.Server/Database/LayoutPreferencesDatabase.cs @@ -75,25 +75,18 @@ public void SetPreferences(string filePath, ulong? frn, LayoutPreferencesItem? p } } - public void ResetAll(LayoutPreferencesFilterPredicate? predicate) + public void ResetAll() { var col = _database.GetCollection(LayoutPreferences); - if (predicate is null) - { - col.DeleteAll(); - } - else - { - col.DeleteMany(x => predicate(x)); - } + col.DeleteAll(); } - public void ApplyToAll(LayoutPreferencesUpdateAction updateAction, LayoutPreferencesFilterPredicate? predicate) + public void ApplyToAll(LayoutPreferencesUpdateAction updateAction) { var col = _database.GetCollection(LayoutPreferences); - var allDocs = predicate is null ? col.FindAll() : col.Find(x => predicate(x)); + var allDocs = col.FindAll(); foreach (var doc in allDocs) { @@ -159,6 +152,5 @@ public string Export() } } - public delegate bool LayoutPreferencesFilterPredicate(LayoutPreferences preference); public delegate void LayoutPreferencesUpdateAction(LayoutPreferences preference); } diff --git a/src/Files.App/Helpers/Layout/LayoutPreferencesDatabaseManager.cs b/src/Files.App/Helpers/Layout/LayoutPreferencesDatabaseManager.cs index 1b7d6aa8a22b..36c35c7aa4d9 100644 --- a/src/Files.App/Helpers/Layout/LayoutPreferencesDatabaseManager.cs +++ b/src/Files.App/Helpers/Layout/LayoutPreferencesDatabaseManager.cs @@ -135,15 +135,14 @@ public void SetPreferences(string filePath, ulong? frn, LayoutPreferencesItem? p _database.SetPreferences(filePath, frn, ToDatabaseEntity(preferencesItem)); } - public void ResetAll(Func? predicate = null) + public void ResetAll() { - _database.ResetAll(item => predicate?.Invoke(FromDatabaseEntity(item)) ?? true); + _database.ResetAll(); } - public void ApplyToAll(Action updateAction, Func? predicate = null) + public void ApplyToAll(Action updateAction) { - _database.ApplyToAll(item => updateAction.Invoke(FromDatabaseEntity(item)), - item => predicate?.Invoke(FromDatabaseEntity(item)) ?? true); + _database.ApplyToAll(item => updateAction.Invoke(FromDatabaseEntity(item))); } public void Import(string json) From 92c5a104dc66ed403a0394372726eb452c661e8f Mon Sep 17 00:00:00 2001 From: hishitetsu <66369541+hishitetsu@users.noreply.github.com> Date: Sun, 7 Apr 2024 10:56:32 +0900 Subject: [PATCH 22/41] Fix: Fixed issue where items could not be sorted by clicking on the header of the details layout (#15128) --- .../Views/Layouts/DetailsLayoutPage.xaml.cs | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/Files.App/Views/Layouts/DetailsLayoutPage.xaml.cs b/src/Files.App/Views/Layouts/DetailsLayoutPage.xaml.cs index d5db6ad8515f..f214439176d8 100644 --- a/src/Files.App/Views/Layouts/DetailsLayoutPage.xaml.cs +++ b/src/Files.App/Views/Layouts/DetailsLayoutPage.xaml.cs @@ -73,6 +73,21 @@ public DetailsLayoutPage() : base() DataContext = this; var selectionRectangle = RectangleSelection.Create(FileList, SelectionRectangle, FileList_SelectionChanged); selectionRectangle.SelectionEnded += SelectionRectangle_SelectionEnded; + + UpdateSortOptionsCommand = new RelayCommand(x => + { + if (!Enum.TryParse(x, out var val)) + return; + if (FolderSettings.DirectorySortOption == val) + { + FolderSettings.DirectorySortDirection = (SortDirection)(((int)FolderSettings.DirectorySortDirection + 1) % 2); + } + else + { + FolderSettings.DirectorySortOption = val; + FolderSettings.DirectorySortDirection = SortDirection.Ascending; + } + }); } // Methods @@ -150,21 +165,6 @@ protected override void OnNavigatedTo(NavigationEventArgs eventArgs) if (parameters.IsLayoutSwitch) _ = ReloadItemIconsAsync(); - UpdateSortOptionsCommand = new RelayCommand(x => - { - if (!Enum.TryParse(x, out var val)) - return; - if (FolderSettings.DirectorySortOption == val) - { - FolderSettings.DirectorySortDirection = (SortDirection)(((int)FolderSettings.DirectorySortDirection + 1) % 2); - } - else - { - FolderSettings.DirectorySortOption = val; - FolderSettings.DirectorySortDirection = SortDirection.Ascending; - } - }); - FilesystemViewModel_PageTypeUpdated(null, new PageTypeUpdatedEventArgs() { IsTypeCloudDrive = InstanceViewModel?.IsPageTypeCloudDrive ?? false, From 5fcd2160a0abbb0a356c62dc8017ad7dc7cd110f Mon Sep 17 00:00:00 2001 From: hishitetsu <66369541+hishitetsu@users.noreply.github.com> Date: Sun, 7 Apr 2024 23:34:28 +0900 Subject: [PATCH 23/41] Feature: Added support for filtering current directory when typing into search box (#15119) --- src/Files.App/Data/Models/ItemViewModel.cs | 7 ++++++- .../ViewModels/UserControls/AddressToolbarViewModel.cs | 3 ++- src/Files.App/Views/Shells/BaseShellPage.cs | 3 +++ src/Files.App/Views/Shells/ModernShellPage.xaml.cs | 2 ++ 4 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/Files.App/Data/Models/ItemViewModel.cs b/src/Files.App/Data/Models/ItemViewModel.cs index 8449494619b6..c80f307c1fa2 100644 --- a/src/Files.App/Data/Models/ItemViewModel.cs +++ b/src/Files.App/Data/Models/ItemViewModel.cs @@ -663,6 +663,8 @@ public void UpdateEmptyTextType() EmptyTextType = FilesAndFolders.Count == 0 ? (IsSearchResults ? EmptyTextType.NoSearchResultsFound : EmptyTextType.FolderEmpty) : EmptyTextType.None; } + public string? FilesAndFoldersFilter { get; set; } + // Apply changes immediately after manipulating on filesAndFolders completed public async Task ApplyFilesAndFoldersChangesAsync() { @@ -706,7 +708,10 @@ void ClearDisplay() return; FilesAndFolders.Clear(); - FilesAndFolders.AddRange(filesAndFoldersLocal); + if (string.IsNullOrEmpty(FilesAndFoldersFilter)) + FilesAndFolders.AddRange(filesAndFoldersLocal); + else + FilesAndFolders.AddRange(filesAndFoldersLocal.Where(x => x.Name.Contains(FilesAndFoldersFilter, StringComparison.OrdinalIgnoreCase))); if (folderSettings.DirectoryGroupOption != GroupOption.None) OrderGroups(); diff --git a/src/Files.App/ViewModels/UserControls/AddressToolbarViewModel.cs b/src/Files.App/ViewModels/UserControls/AddressToolbarViewModel.cs index ff16fb2ec9fa..249fcd5f7693 100644 --- a/src/Files.App/ViewModels/UserControls/AddressToolbarViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/AddressToolbarViewModel.cs @@ -581,11 +581,12 @@ private void CloseSearchBox(bool doFocus = false) } else { - SearchBox.Query = string.Empty; IsSearchBoxVisible = false; if (doFocus) { + SearchBox.Query = string.Empty; + var page = Ioc.Default.GetRequiredService().ShellPage?.SlimContentPage; if (page is BaseGroupableLayoutPage svb && svb.IsLoaded) diff --git a/src/Files.App/Views/Shells/BaseShellPage.cs b/src/Files.App/Views/Shells/BaseShellPage.cs index f7e7a7c1b7a6..061cb608df52 100644 --- a/src/Files.App/Views/Shells/BaseShellPage.cs +++ b/src/Files.App/Views/Shells/BaseShellPage.cs @@ -363,6 +363,9 @@ protected async void ShellPage_QuerySubmitted(ISearchBoxViewModel sender, Search protected async void ShellPage_TextChanged(ISearchBoxViewModel sender, SearchBoxTextChangedEventArgs e) { + FilesystemViewModel.FilesAndFoldersFilter = sender.Query; + await FilesystemViewModel.ApplyFilesAndFoldersChangesAsync(); + if (e.Reason != SearchBoxTextChangeReason.UserInput) return; diff --git a/src/Files.App/Views/Shells/ModernShellPage.xaml.cs b/src/Files.App/Views/Shells/ModernShellPage.xaml.cs index 8df12fc8bd91..3c8a65547c05 100644 --- a/src/Files.App/Views/Shells/ModernShellPage.xaml.cs +++ b/src/Files.App/Views/Shells/ModernShellPage.xaml.cs @@ -300,6 +300,8 @@ public override void NavigateHome() public override void NavigateToPath(string? navigationPath, Type? sourcePageType, NavigationArguments? navArgs = null) { + FilesystemViewModel.FilesAndFoldersFilter = null; + if (sourcePageType is null && !string.IsNullOrEmpty(navigationPath)) sourcePageType = InstanceViewModel.FolderSettings.GetLayoutType(navigationPath); From 938aaa72348c1f051f062fcb01def31e463f893d Mon Sep 17 00:00:00 2001 From: Gustavo Mauricio de Barros Date: Sun, 7 Apr 2024 11:54:57 -0300 Subject: [PATCH 24/41] Code Quality: Migrated CopyFileFromApp and MoveFileFromApp to CsWin32 (#15131) --- .../Interop/NativeFileOperationsHelper.cs | 17 ----------------- src/Files.App/NativeMethods.txt | 2 ++ .../Utils/Storage/Helpers/FilesystemResult.cs | 8 +++++++- .../Storage/Operations/FilesystemOperations.cs | 11 ++++++----- .../Storage/StorageItems/NativeStorageFile.cs | 7 ++++--- .../Storage/StorageItems/ZipStorageFile.cs | 3 ++- .../Storage/StorageItems/ZipStorageFolder.cs | 3 ++- 7 files changed, 23 insertions(+), 28 deletions(-) diff --git a/src/Files.App/Helpers/Interop/NativeFileOperationsHelper.cs b/src/Files.App/Helpers/Interop/NativeFileOperationsHelper.cs index 1285f252cef9..f120ebd469f4 100644 --- a/src/Files.App/Helpers/Interop/NativeFileOperationsHelper.cs +++ b/src/Files.App/Helpers/Interop/NativeFileOperationsHelper.cs @@ -142,23 +142,6 @@ IntPtr pCreateExParams IntPtr SecurityAttributes ); - [DllImport("api-ms-win-core-file-fromapp-l1-1-0.dll", CharSet = CharSet.Auto, - CallingConvention = CallingConvention.StdCall, - SetLastError = true)] - public static extern bool MoveFileFromApp( - string lpExistingFileName, - string lpNewFileName - ); - - [DllImport("api-ms-win-core-file-fromapp-l1-1-0.dll", CharSet = CharSet.Auto, - CallingConvention = CallingConvention.StdCall, - SetLastError = true)] - public static extern bool CopyFileFromApp( - string lpExistingFileName, - string lpNewFileName, - bool bFailIfExists - ); - [DllImport("api-ms-win-core-file-fromapp-l1-1-0.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)] diff --git a/src/Files.App/NativeMethods.txt b/src/Files.App/NativeMethods.txt index 08eb7629ab21..104c1b02dd2b 100644 --- a/src/Files.App/NativeMethods.txt +++ b/src/Files.App/NativeMethods.txt @@ -39,3 +39,5 @@ AttachThreadInput SetWindowPos SetFocus SetActiveWindow +CopyFileFromApp +MoveFileFromApp \ No newline at end of file diff --git a/src/Files.App/Utils/Storage/Helpers/FilesystemResult.cs b/src/Files.App/Utils/Storage/Helpers/FilesystemResult.cs index 290bbd61ab18..f791fbcdb56b 100644 --- a/src/Files.App/Utils/Storage/Helpers/FilesystemResult.cs +++ b/src/Files.App/Utils/Storage/Helpers/FilesystemResult.cs @@ -1,6 +1,8 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. +using Windows.Win32.Foundation; + namespace Files.App.Utils.Storage { public class FilesystemResult @@ -12,8 +14,12 @@ public class FilesystemResult public static implicit operator FileSystemStatusCode(FilesystemResult res) => res.ErrorCode; public static implicit operator FilesystemResult(FileSystemStatusCode res) => new(res); - public static implicit operator bool(FilesystemResult res) => res is not null && res.ErrorCode is FileSystemStatusCode.Success; + public static implicit operator bool(FilesystemResult res) => res?.ErrorCode is FileSystemStatusCode.Success; public static explicit operator FilesystemResult(bool res) => new(res ? FileSystemStatusCode.Success : FileSystemStatusCode.Generic); + + + public static implicit operator BOOL(FilesystemResult res) => res?.ErrorCode is FileSystemStatusCode.Success; + public static explicit operator FilesystemResult(BOOL res) => new(res ? FileSystemStatusCode.Success : FileSystemStatusCode.Generic); } public sealed class FilesystemResult : FilesystemResult diff --git a/src/Files.App/Utils/Storage/Operations/FilesystemOperations.cs b/src/Files.App/Utils/Storage/Operations/FilesystemOperations.cs index f28d4b2f242c..e6b9e2bcbd16 100644 --- a/src/Files.App/Utils/Storage/Operations/FilesystemOperations.cs +++ b/src/Files.App/Utils/Storage/Operations/FilesystemOperations.cs @@ -7,6 +7,7 @@ using System.Text; using Windows.Foundation.Metadata; using Windows.Storage; +using Windows.Win32; namespace Files.App.Utils.Storage { @@ -210,7 +211,7 @@ public async Task CopyAsync(IStorageItemWithPath source, string } else if (source.ItemType == FilesystemItemType.File) { - var fsResult = (FilesystemResult)await Task.Run(() => NativeFileOperationsHelper.CopyFileFromApp(source.Path, destination, true)); + var fsResult = (FilesystemResult)await Task.Run(() => PInvoke.CopyFileFromApp(source.Path, destination, true)); if (!fsResult) { @@ -363,7 +364,7 @@ public async Task MoveAsync(IStorageItemWithPath source, string } else { - var fsResult = (FilesystemResult)await Task.Run(() => NativeFileOperationsHelper.MoveFileFromApp(source.Path, destination)); + var fsResult = (FilesystemResult)await Task.Run(() => PInvoke.MoveFileFromApp(source.Path, destination)); if (!fsResult) { @@ -423,7 +424,7 @@ public async Task MoveAsync(IStorageItemWithPath source, string } else if (source.ItemType == FilesystemItemType.File) { - var fsResult = (FilesystemResult)await Task.Run(() => NativeFileOperationsHelper.MoveFileFromApp(source.Path, destination)); + var fsResult = (FilesystemResult)await Task.Run(() => PInvoke.MoveFileFromApp(source.Path, destination)); if (!fsResult) { @@ -620,7 +621,7 @@ public Task RenameAsync(IStorageItem source, string newName, Na { // Try again with MoveFileFromApp var destination = Path.Combine(Path.GetDirectoryName(source.Path), newName); - if (NativeFileOperationsHelper.MoveFileFromApp(source.Path, destination)) + if (PInvoke.MoveFileFromApp(source.Path, destination)) { fsProgress.ReportStatus(FileSystemStatusCode.Success); @@ -729,7 +730,7 @@ public async Task RestoreFromTrashAsync(IStorageItemWithPath so FilesystemResult fsResult = FileSystemStatusCode.InProgress; - fsResult = (FilesystemResult)await Task.Run(() => NativeFileOperationsHelper.MoveFileFromApp(source.Path, destination)); + fsResult = (FilesystemResult)await Task.Run(() => PInvoke.MoveFileFromApp(source.Path, destination)); if (!fsResult) { diff --git a/src/Files.App/Utils/Storage/StorageItems/NativeStorageFile.cs b/src/Files.App/Utils/Storage/StorageItems/NativeStorageFile.cs index 9e51228cd85d..9a70bf82020b 100644 --- a/src/Files.App/Utils/Storage/StorageItems/NativeStorageFile.cs +++ b/src/Files.App/Utils/Storage/StorageItems/NativeStorageFile.cs @@ -9,6 +9,7 @@ using Windows.Storage; using Windows.Storage.FileProperties; using Windows.Storage.Streams; +using Windows.Win32; using IO = System.IO; namespace Files.App.Utils.Storage @@ -74,7 +75,7 @@ public override IAsyncOperation CopyAsync(IStorageFolder destin var destFile = new NativeStorageFile(destination, desiredNewName, DateTime.Now); if (!IsAlternateStream) { - if (!await Task.Run(() => NativeFileOperationsHelper.CopyFileFromApp(Path, destination, option != NameCollisionOption.ReplaceExisting))) + if (!await Task.Run(() => PInvoke.CopyFileFromApp(Path, destination, option != NameCollisionOption.ReplaceExisting))) { throw new Win32Exception(Marshal.GetLastWin32Error()); } @@ -185,7 +186,7 @@ public override IAsyncAction MoveAsync(IStorageFolder destinationFolder, string var destination = IO.Path.Combine(destinationFolder.Path, desiredNewName); if (!IsAlternateStream) { - if (!await Task.Run(() => NativeFileOperationsHelper.MoveFileFromApp(Path, destination))) + if (!await Task.Run(() => PInvoke.MoveFileFromApp(Path, destination))) { throw new Win32Exception(Marshal.GetLastWin32Error()); } @@ -237,7 +238,7 @@ public override IAsyncAction RenameAsync(string desiredName, NameCollisionOption var destFile = new NativeStorageFile(destination, desiredName, DateTime.Now); if (!IsAlternateStream) { - if (!await Task.Run(() => NativeFileOperationsHelper.MoveFileFromApp(Path, destination))) + if (!await Task.Run(() => PInvoke.MoveFileFromApp(Path, destination))) { throw new Win32Exception(Marshal.GetLastWin32Error()); } diff --git a/src/Files.App/Utils/Storage/StorageItems/ZipStorageFile.cs b/src/Files.App/Utils/Storage/StorageItems/ZipStorageFile.cs index a5eafabb7e95..e88a1a0b39cc 100644 --- a/src/Files.App/Utils/Storage/StorageItems/ZipStorageFile.cs +++ b/src/Files.App/Utils/Storage/StorageItems/ZipStorageFile.cs @@ -9,6 +9,7 @@ using Windows.Storage; using Windows.Storage.FileProperties; using Windows.Storage.Streams; +using Windows.Win32; using IO = System.IO; namespace Files.App.Utils.Storage @@ -310,7 +311,7 @@ public override IAsyncAction RenameAsync(string desiredName, NameCollisionOption else { var fileName = IO.Path.Combine(IO.Path.GetDirectoryName(Path), desiredName); - NativeFileOperationsHelper.MoveFileFromApp(Path, fileName); + PInvoke.MoveFileFromApp(Path, fileName); } } else diff --git a/src/Files.App/Utils/Storage/StorageItems/ZipStorageFolder.cs b/src/Files.App/Utils/Storage/StorageItems/ZipStorageFolder.cs index f68080e65be0..86e31cd9b99e 100644 --- a/src/Files.App/Utils/Storage/StorageItems/ZipStorageFolder.cs +++ b/src/Files.App/Utils/Storage/StorageItems/ZipStorageFolder.cs @@ -11,6 +11,7 @@ using Windows.Storage; using Windows.Storage.FileProperties; using Windows.Storage.Search; +using Windows.Win32; using IO = System.IO; namespace Files.App.Utils.Storage @@ -350,7 +351,7 @@ public override IAsyncAction RenameAsync(string desiredName, NameCollisionOption else { var fileName = IO.Path.Combine(IO.Path.GetDirectoryName(Path), desiredName); - NativeFileOperationsHelper.MoveFileFromApp(Path, fileName); + PInvoke.MoveFileFromApp(Path, fileName); } } else From eb60923f3eb4e79903a46ad209e83b05ac87e0f6 Mon Sep 17 00:00:00 2001 From: Gustavo Mauricio de Barros Date: Sun, 7 Apr 2024 13:52:33 -0300 Subject: [PATCH 25/41] Code Quality: Migrated DeleteFileFromApp and RemoveDirectoryFromApp to CsWin32 (#15136) --- src/Files.App/Helpers/Layout/LayoutPreferencesManager.cs | 3 ++- src/Files.App/NativeMethods.txt | 4 +++- src/Files.App/Utils/Archives/CompressHelper.cs | 3 ++- src/Files.App/Utils/FileTags/FileTagsHelper.cs | 3 ++- .../Utils/Storage/Operations/FilesystemOperations.cs | 2 +- .../Utils/Storage/StorageItems/NativeStorageFile.cs | 6 ++++-- src/Files.App/Utils/Storage/StorageItems/ZipStorageFile.cs | 2 +- .../Utils/Storage/StorageItems/ZipStorageFolder.cs | 2 +- src/Files.App/Views/Properties/GeneralPage.xaml.cs | 3 ++- 9 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/Files.App/Helpers/Layout/LayoutPreferencesManager.cs b/src/Files.App/Helpers/Layout/LayoutPreferencesManager.cs index 845238898b02..a024363c1146 100644 --- a/src/Files.App/Helpers/Layout/LayoutPreferencesManager.cs +++ b/src/Files.App/Helpers/Layout/LayoutPreferencesManager.cs @@ -4,6 +4,7 @@ using Files.App.Server.Data.Enums; using System.Text.Json; using Windows.Storage; +using Windows.Win32; namespace Files.App.Data.Models { @@ -530,7 +531,7 @@ public static void SetLayoutPreferencesForPath(string path, LayoutPreferencesIte // Port settings to the database, delete the ADS SetLayoutPreferencesToDatabase(path, frn, layoutPreferences); - NativeFileOperationsHelper.DeleteFileFromApp($"{path}:files_layoutmode"); + PInvoke.DeleteFileFromApp($"{path}:files_layoutmode"); return layoutPreferences; } diff --git a/src/Files.App/NativeMethods.txt b/src/Files.App/NativeMethods.txt index 104c1b02dd2b..b59d366f3c6a 100644 --- a/src/Files.App/NativeMethods.txt +++ b/src/Files.App/NativeMethods.txt @@ -40,4 +40,6 @@ SetWindowPos SetFocus SetActiveWindow CopyFileFromApp -MoveFileFromApp \ No newline at end of file +MoveFileFromApp +DeleteFileFromApp +RemoveDirectoryFromApp \ No newline at end of file diff --git a/src/Files.App/Utils/Archives/CompressHelper.cs b/src/Files.App/Utils/Archives/CompressHelper.cs index e2b4acade43b..800e044ffb6e 100644 --- a/src/Files.App/Utils/Archives/CompressHelper.cs +++ b/src/Files.App/Utils/Archives/CompressHelper.cs @@ -4,6 +4,7 @@ using Files.Shared.Helpers; using System.IO; using Windows.Storage; +using Windows.Win32; namespace Files.App.Utils.Archives { @@ -92,7 +93,7 @@ public static async Task CompressArchiveAsync(ICompressArchiveModel creator) } else { - NativeFileOperationsHelper.DeleteFileFromApp(archivePath); + PInvoke.DeleteFileFromApp(archivePath); StatusCenterHelper.AddCard_Compress( creator.Sources, diff --git a/src/Files.App/Utils/FileTags/FileTagsHelper.cs b/src/Files.App/Utils/FileTags/FileTagsHelper.cs index 323ce48f70fc..f63b8fe5044e 100644 --- a/src/Files.App/Utils/FileTags/FileTagsHelper.cs +++ b/src/Files.App/Utils/FileTags/FileTagsHelper.cs @@ -5,6 +5,7 @@ using Windows.Foundation.Metadata; using Windows.Storage; using Windows.Storage.FileProperties; +using Windows.Win32; using IO = System.IO; namespace Files.App.Utils.FileTags @@ -31,7 +32,7 @@ public static async void WriteFileTag(string filePath, string[] tag) } if (tag is null || !tag.Any()) { - NativeFileOperationsHelper.DeleteFileFromApp($"{filePath}:files"); + PInvoke.DeleteFileFromApp($"{filePath}:files"); } else if (ReadFileTag(filePath) is not string[] arr || !tag.SequenceEqual(arr)) { diff --git a/src/Files.App/Utils/Storage/Operations/FilesystemOperations.cs b/src/Files.App/Utils/Storage/Operations/FilesystemOperations.cs index e6b9e2bcbd16..93226587cabf 100644 --- a/src/Files.App/Utils/Storage/Operations/FilesystemOperations.cs +++ b/src/Files.App/Utils/Storage/Operations/FilesystemOperations.cs @@ -504,7 +504,7 @@ public async Task DeleteAsync(IStorageItemWithPath source, IPro if (permanently) { - fsResult = (FilesystemResult)NativeFileOperationsHelper.DeleteFileFromApp(source.Path); + fsResult = (FilesystemResult)PInvoke.DeleteFileFromApp(source.Path); } if (!fsResult) { diff --git a/src/Files.App/Utils/Storage/StorageItems/NativeStorageFile.cs b/src/Files.App/Utils/Storage/StorageItems/NativeStorageFile.cs index 9a70bf82020b..8967d1d4e06d 100644 --- a/src/Files.App/Utils/Storage/StorageItems/NativeStorageFile.cs +++ b/src/Files.App/Utils/Storage/StorageItems/NativeStorageFile.cs @@ -105,12 +105,14 @@ private void CreateFile() public override IAsyncAction DeleteAsync() { - return AsyncInfo.Run(async (cancellationToken) => + return AsyncInfo.Run(_ => { - if (!NativeFileOperationsHelper.DeleteFileFromApp(Path)) + if (!PInvoke.DeleteFileFromApp(Path)) { throw new Win32Exception(Marshal.GetLastWin32Error()); } + + return Task.CompletedTask; }); } diff --git a/src/Files.App/Utils/Storage/StorageItems/ZipStorageFile.cs b/src/Files.App/Utils/Storage/StorageItems/ZipStorageFile.cs index e88a1a0b39cc..924487dd8c33 100644 --- a/src/Files.App/Utils/Storage/StorageItems/ZipStorageFile.cs +++ b/src/Files.App/Utils/Storage/StorageItems/ZipStorageFile.cs @@ -355,7 +355,7 @@ public override IAsyncAction DeleteAsync(StorageDeleteOption option) } else if (option == StorageDeleteOption.PermanentDelete) { - NativeFileOperationsHelper.DeleteFileFromApp(Path); + PInvoke.DeleteFileFromApp(Path); } else { diff --git a/src/Files.App/Utils/Storage/StorageItems/ZipStorageFolder.cs b/src/Files.App/Utils/Storage/StorageItems/ZipStorageFolder.cs index 86e31cd9b99e..4570dc586ee9 100644 --- a/src/Files.App/Utils/Storage/StorageItems/ZipStorageFolder.cs +++ b/src/Files.App/Utils/Storage/StorageItems/ZipStorageFolder.cs @@ -398,7 +398,7 @@ public override IAsyncAction DeleteAsync(StorageDeleteOption option) } else if (option == StorageDeleteOption.PermanentDelete) { - NativeFileOperationsHelper.DeleteFileFromApp(Path); + PInvoke.DeleteFileFromApp(Path); } else { diff --git a/src/Files.App/Views/Properties/GeneralPage.xaml.cs b/src/Files.App/Views/Properties/GeneralPage.xaml.cs index 2ec15dd24a61..eee1260d9961 100644 --- a/src/Files.App/Views/Properties/GeneralPage.xaml.cs +++ b/src/Files.App/Views/Properties/GeneralPage.xaml.cs @@ -8,6 +8,7 @@ using System.IO; using System.Text.RegularExpressions; using Windows.Storage; +using Windows.Win32; namespace Files.App.Views.Properties { @@ -168,7 +169,7 @@ async Task SaveBaseAsync(ListedItem item) } if (ViewModel.IsUnblockFileSelected) - NativeFileOperationsHelper.DeleteFileFromApp($"{item.ItemPath}:Zone.Identifier"); + PInvoke.DeleteFileFromApp($"{item.ItemPath}:Zone.Identifier"); if (ViewModel.IsAblumCoverModified) { From 030198d8c5ae3d152cb822db63cb74e678ee6b6a Mon Sep 17 00:00:00 2001 From: Gustavo Mauricio de Barros Date: Sun, 7 Apr 2024 17:32:14 -0300 Subject: [PATCH 26/41] Code Quality: Files.App/Helpers - convert into constant. (#15090) --- .../Helpers/Environment/SoftwareHelpers.cs | 17 ++++++------ .../Helpers/NaturalStringComparer.cs | 26 +++++++++---------- .../Helpers/WMI/ManagementEventWatcher.cs | 4 +-- .../Helpers/Win32/Win32Helper.Storage.cs | 4 +-- 4 files changed, 26 insertions(+), 25 deletions(-) diff --git a/src/Files.App/Helpers/Environment/SoftwareHelpers.cs b/src/Files.App/Helpers/Environment/SoftwareHelpers.cs index 6afc9f944e6d..cb7ea97fc915 100644 --- a/src/Files.App/Helpers/Environment/SoftwareHelpers.cs +++ b/src/Files.App/Helpers/Environment/SoftwareHelpers.cs @@ -8,21 +8,22 @@ namespace Files.App.Helpers { internal static class SoftwareHelpers { + private const string UninstallRegistryKey = @"Software\Microsoft\Windows\CurrentVersion\Uninstall"; + private const string VsRegistryKey = @"SOFTWARE\Microsoft\VisualStudio"; + + private const string VsCodeName = "Microsoft Visual Studio Code"; + + public static bool IsVSCodeInstalled() { - string registryKey = @"Software\Microsoft\Windows\CurrentVersion\Uninstall"; - string vsCodeName = "Microsoft Visual Studio Code"; - return - ContainsName(Registry.CurrentUser.OpenSubKey(registryKey), vsCodeName) || - ContainsName(Registry.LocalMachine.OpenSubKey(registryKey), vsCodeName); + ContainsName(Registry.CurrentUser.OpenSubKey(UninstallRegistryKey), VsCodeName) || + ContainsName(Registry.LocalMachine.OpenSubKey(UninstallRegistryKey), VsCodeName); } public static bool IsVSInstalled() { - string registryKey = @"SOFTWARE\Microsoft\VisualStudio"; - - var key = Registry.LocalMachine.OpenSubKey(registryKey); + var key = Registry.LocalMachine.OpenSubKey(VsRegistryKey); if (key is null) return false; diff --git a/src/Files.App/Helpers/NaturalStringComparer.cs b/src/Files.App/Helpers/NaturalStringComparer.cs index 00b1d32d6f05..e786d92e7a36 100644 --- a/src/Files.App/Helpers/NaturalStringComparer.cs +++ b/src/Files.App/Helpers/NaturalStringComparer.cs @@ -9,20 +9,20 @@ namespace Files.App.Helpers { internal static class SafeNativeMethods { - public static readonly Int32 NORM_IGNORECASE = 0x00000001; - public static readonly Int32 NORM_IGNORENONSPACE = 0x00000002; - public static readonly Int32 NORM_IGNORESYMBOLS = 0x00000004; - public static readonly Int32 LINGUISTIC_IGNORECASE = 0x00000010; - public static readonly Int32 LINGUISTIC_IGNOREDIACRITIC = 0x00000020; - public static readonly Int32 NORM_IGNOREKANATYPE = 0x00010000; - public static readonly Int32 NORM_IGNOREWIDTH = 0x00020000; - public static readonly Int32 NORM_LINGUISTIC_CASING = 0x08000000; - public static readonly Int32 SORT_STRINGSORT = 0x00001000; - public static readonly Int32 SORT_DIGITSASNUMBERS = 0x00000008; + public const Int32 NORM_IGNORECASE = 0x00000001; + public const Int32 NORM_IGNORENONSPACE = 0x00000002; + public const Int32 NORM_IGNORESYMBOLS = 0x00000004; + public const Int32 LINGUISTIC_IGNORECASE = 0x00000010; + public const Int32 LINGUISTIC_IGNOREDIACRITIC = 0x00000020; + public const Int32 NORM_IGNOREKANATYPE = 0x00010000; + public const Int32 NORM_IGNOREWIDTH = 0x00020000; + public const Int32 NORM_LINGUISTIC_CASING = 0x08000000; + public const Int32 SORT_STRINGSORT = 0x00001000; + public const Int32 SORT_DIGITSASNUMBERS = 0x00000008; - public static readonly String LOCALE_NAME_USER_DEFAULT = null; - public static readonly String LOCALE_NAME_INVARIANT = String.Empty; - public static readonly String LOCALE_NAME_SYSTEM_DEFAULT = "!sys-default-locale"; + public const String LOCALE_NAME_USER_DEFAULT = null; + public const String LOCALE_NAME_INVARIANT = ""; + public const String LOCALE_NAME_SYSTEM_DEFAULT = "!sys-default-locale"; [DllImport("api-ms-win-core-string-l1-1-0.dll", CharSet = CharSet.Unicode)] public static extern Int32 CompareStringEx( diff --git a/src/Files.App/Helpers/WMI/ManagementEventWatcher.cs b/src/Files.App/Helpers/WMI/ManagementEventWatcher.cs index 94903eaf8240..09a470019743 100644 --- a/src/Files.App/Helpers/WMI/ManagementEventWatcher.cs +++ b/src/Files.App/Helpers/WMI/ManagementEventWatcher.cs @@ -40,8 +40,8 @@ internal enum CimWatcherStatus private CimSession _cimSession; private CimAsyncMultipleResults _cimObservable; private IDisposable _subscription; - internal static readonly string DefaultNameSpace = @"root\cimv2"; - internal static readonly string DefaultQueryDialect = "WQL"; + internal const string DefaultNameSpace = @"root\cimv2"; + internal const string DefaultQueryDialect = "WQL"; /// /// Initializes a new instance of the class. diff --git a/src/Files.App/Helpers/Win32/Win32Helper.Storage.cs b/src/Files.App/Helpers/Win32/Win32Helper.Storage.cs index 7d8fd3656044..2fd47d40fa07 100644 --- a/src/Files.App/Helpers/Win32/Win32Helper.Storage.cs +++ b/src/Files.App/Helpers/Win32/Win32Helper.Storage.cs @@ -228,7 +228,7 @@ public static string ExtractStringFromDLL(string file, int number) public static byte[]? GetIconOverlay(string path, bool isDirectory) { var shFileInfo = new Shell32.SHFILEINFO(); - var flags = Shell32.SHGFI.SHGFI_OVERLAYINDEX | Shell32.SHGFI.SHGFI_ICON | Shell32.SHGFI.SHGFI_SYSICONINDEX | Shell32.SHGFI.SHGFI_ICONLOCATION; + const Shell32.SHGFI flags = Shell32.SHGFI.SHGFI_OVERLAYINDEX | Shell32.SHGFI.SHGFI_ICON | Shell32.SHGFI.SHGFI_SYSICONINDEX | Shell32.SHGFI.SHGFI_ICONLOCATION; byte[]? overlayData = null; try @@ -326,7 +326,7 @@ public static string ExtractStringFromDLL(string file, int number) else { var shfi = new Shell32.SHFILEINFO(); - var flags = Shell32.SHGFI.SHGFI_OVERLAYINDEX | Shell32.SHGFI.SHGFI_ICON | Shell32.SHGFI.SHGFI_SYSICONINDEX | Shell32.SHGFI.SHGFI_ICONLOCATION | Shell32.SHGFI.SHGFI_USEFILEATTRIBUTES; + const Shell32.SHGFI flags = Shell32.SHGFI.SHGFI_OVERLAYINDEX | Shell32.SHGFI.SHGFI_ICON | Shell32.SHGFI.SHGFI_SYSICONINDEX | Shell32.SHGFI.SHGFI_ICONLOCATION | Shell32.SHGFI.SHGFI_USEFILEATTRIBUTES; // Cannot access file, use file attributes var useFileAttibutes = iconData is null; From fb1d0031dbffe44cfc2cc50309ad663348a1fc61 Mon Sep 17 00:00:00 2001 From: Gustavo Mauricio de Barros Date: Sun, 7 Apr 2024 19:54:54 -0300 Subject: [PATCH 27/41] Code Quality: Use concrete types when possible for improved performance (#15093) --- src/Files.App/Data/Factories/ShellContextFlyoutHelper.cs | 2 +- src/Files.App/Data/Models/ItemViewModel.cs | 2 +- src/Files.App/Services/UpdateService.cs | 2 +- src/Files.App/Utils/RecycleBin/RecycleBinManager.cs | 2 +- .../Collection/BulkConcurrentObservableCollection.cs | 2 +- .../Utils/Storage/Collection/ConcurrentCollection.cs | 2 +- .../Utils/Storage/Enumerators/Win32StorageEnumerator.cs | 2 +- src/Files.App/Utils/Storage/Operations/FilesystemHelpers.cs | 2 +- .../Utils/Storage/StorageItems/ZipStorageFolder.cs | 2 +- .../ViewModels/Properties/CompatibilityViewModel.cs | 2 +- src/Files.App/ViewModels/Settings/AboutViewModel.cs | 2 +- src/Files.App/ViewModels/UserControls/SidebarViewModel.cs | 6 +++--- 12 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/Files.App/Data/Factories/ShellContextFlyoutHelper.cs b/src/Files.App/Data/Factories/ShellContextFlyoutHelper.cs index 2773f3d5b8db..ddfbae896897 100644 --- a/src/Files.App/Data/Factories/ShellContextFlyoutHelper.cs +++ b/src/Files.App/Data/Factories/ShellContextFlyoutHelper.cs @@ -66,7 +66,7 @@ bool filterMenuItemsImpl(string menuItem) => !string.IsNullOrEmpty(menuItem) } private static void LoadMenuFlyoutItem( - IList menuItemsListLocal, + List menuItemsListLocal, ContextMenu contextMenu, IEnumerable menuFlyoutItems, CancellationToken cancellationToken, diff --git a/src/Files.App/Data/Models/ItemViewModel.cs b/src/Files.App/Data/Models/ItemViewModel.cs index c80f307c1fa2..03469d3c7933 100644 --- a/src/Files.App/Data/Models/ItemViewModel.cs +++ b/src/Files.App/Data/Models/ItemViewModel.cs @@ -39,7 +39,7 @@ public sealed class ItemViewModel : ObservableObject, IDisposable private readonly AsyncManualResetEvent gitChangedEvent; private readonly DispatcherQueue dispatcherQueue; private readonly JsonElement defaultJson = JsonSerializer.SerializeToElement("{}"); - private readonly IStorageCacheController fileListCache = StorageCacheController.GetInstance(); + private readonly StorageCacheController fileListCache = StorageCacheController.GetInstance(); private readonly string folderTypeTextLocalized = "Folder".GetLocalizedResource(); private Task? aProcessQueueAction; diff --git a/src/Files.App/Services/UpdateService.cs b/src/Files.App/Services/UpdateService.cs index d4aabb028428..14cf22e8eccf 100644 --- a/src/Files.App/Services/UpdateService.cs +++ b/src/Files.App/Services/UpdateService.cs @@ -16,7 +16,7 @@ namespace Files.App.Services internal sealed class UpdateService : ObservableObject, IUpdateService { private StoreContext? _storeContext; - private IList? _updatePackages; + private List? _updatePackages; private bool IsMandatory => _updatePackages?.Where(e => e.Mandatory).ToList().Count >= 1; diff --git a/src/Files.App/Utils/RecycleBin/RecycleBinManager.cs b/src/Files.App/Utils/RecycleBin/RecycleBinManager.cs index 95b39b7a3551..a7ed5424582e 100644 --- a/src/Files.App/Utils/RecycleBin/RecycleBinManager.cs +++ b/src/Files.App/Utils/RecycleBin/RecycleBinManager.cs @@ -12,7 +12,7 @@ public sealed class RecycleBinManager { private static readonly Lazy lazy = new(() => new RecycleBinManager()); - private IList? binWatchers; + private List? binWatchers; public event SystemIO.FileSystemEventHandler? RecycleBinItemCreated; diff --git a/src/Files.App/Utils/Storage/Collection/BulkConcurrentObservableCollection.cs b/src/Files.App/Utils/Storage/Collection/BulkConcurrentObservableCollection.cs index 92d159462de8..47dadb7cf226 100644 --- a/src/Files.App/Utils/Storage/Collection/BulkConcurrentObservableCollection.cs +++ b/src/Files.App/Utils/Storage/Collection/BulkConcurrentObservableCollection.cs @@ -487,7 +487,7 @@ public void Order(Func, IEnumerable> func) public void OrderOne(Func, IEnumerable> func, T item) { - IList result; + List result; lock (syncRoot) { result = func.Invoke(collection).ToList(); diff --git a/src/Files.App/Utils/Storage/Collection/ConcurrentCollection.cs b/src/Files.App/Utils/Storage/Collection/ConcurrentCollection.cs index 46f19af65e98..f16ce899f590 100644 --- a/src/Files.App/Utils/Storage/Collection/ConcurrentCollection.cs +++ b/src/Files.App/Utils/Storage/Collection/ConcurrentCollection.cs @@ -243,7 +243,7 @@ public void Order(Func, IEnumerable> func) public void OrderOne(Func, IEnumerable> func, T item) { - IList result; + List result; lock (syncRoot) { diff --git a/src/Files.App/Utils/Storage/Enumerators/Win32StorageEnumerator.cs b/src/Files.App/Utils/Storage/Enumerators/Win32StorageEnumerator.cs index c8afa24c02ed..b94b408e69b3 100644 --- a/src/Files.App/Utils/Storage/Enumerators/Win32StorageEnumerator.cs +++ b/src/Files.App/Utils/Storage/Enumerators/Win32StorageEnumerator.cs @@ -17,7 +17,7 @@ public static class Win32StorageEnumerator private static readonly string folderTypeTextLocalized = "Folder".GetLocalizedResource(); - private static readonly IStorageCacheController fileListCache = StorageCacheController.GetInstance(); + private static readonly StorageCacheController fileListCache = StorageCacheController.GetInstance(); public static async Task> ListEntries( string path, diff --git a/src/Files.App/Utils/Storage/Operations/FilesystemHelpers.cs b/src/Files.App/Utils/Storage/Operations/FilesystemHelpers.cs index 62cf8e732e1d..fca8d72dee6d 100644 --- a/src/Files.App/Utils/Storage/Operations/FilesystemHelpers.cs +++ b/src/Files.App/Utils/Storage/Operations/FilesystemHelpers.cs @@ -24,7 +24,7 @@ public sealed class FilesystemHelpers : IFilesystemHelpers private IShellPage associatedInstance; private readonly IJumpListService jumpListService; - private IFilesystemOperations filesystemOperations; + private ShellFilesystemOperations filesystemOperations; private ItemManipulationModel? itemManipulationModel => associatedInstance.SlimContentPage?.ItemManipulationModel; diff --git a/src/Files.App/Utils/Storage/StorageItems/ZipStorageFolder.cs b/src/Files.App/Utils/Storage/StorageItems/ZipStorageFolder.cs index 4570dc586ee9..794bbeba7cb0 100644 --- a/src/Files.App/Utils/Storage/StorageItems/ZipStorageFolder.cs +++ b/src/Files.App/Utils/Storage/StorageItems/ZipStorageFolder.cs @@ -517,7 +517,7 @@ private static bool CheckAccess(Stream stream) return false; } } - private static async Task CheckAccess(IStorageFile file) + private static async Task CheckAccess(BaseStorageFile file) { return await SafetyExtensions.IgnoreExceptions(async () => { diff --git a/src/Files.App/ViewModels/Properties/CompatibilityViewModel.cs b/src/Files.App/ViewModels/Properties/CompatibilityViewModel.cs index e0ce1db58bbf..c5ed13885e91 100644 --- a/src/Files.App/ViewModels/Properties/CompatibilityViewModel.cs +++ b/src/Files.App/ViewModels/Properties/CompatibilityViewModel.cs @@ -116,7 +116,7 @@ public bool SetCompatibilityOptions() return WindowsCompatibilityService.SetCompatibilityOptionsForPath(ItemPath, CompatibilityOptions); } - private Task ExecuteRunTroubleshooterCommand() + private Task ExecuteRunTroubleshooterCommand() { return LaunchHelper.RunCompatibilityTroubleshooterAsync(ItemPath); } diff --git a/src/Files.App/ViewModels/Settings/AboutViewModel.cs b/src/Files.App/ViewModels/Settings/AboutViewModel.cs index 33ceee30dc14..f26d8239520b 100644 --- a/src/Files.App/ViewModels/Settings/AboutViewModel.cs +++ b/src/Files.App/ViewModels/Settings/AboutViewModel.cs @@ -48,7 +48,7 @@ public AboutViewModel() OpenCrowdinCommand = new AsyncRelayCommand(DoOpenCrowdin); } - private Task OpenLogLocation() + private Task OpenLogLocation() { return Launcher.LaunchFolderAsync(ApplicationData.Current.LocalFolder).AsTask(); } diff --git a/src/Files.App/ViewModels/UserControls/SidebarViewModel.cs b/src/Files.App/ViewModels/UserControls/SidebarViewModel.cs index 92c16d406cbf..5780cc6e9abc 100644 --- a/src/Files.App/ViewModels/UserControls/SidebarViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/SidebarViewModel.cs @@ -268,7 +268,7 @@ public SidebarViewModel() ReorderItemsCommand = new AsyncRelayCommand(ReorderItemsAsync); } - private Task CreateItemHomeAsync() + private Task CreateItemHomeAsync() { return CreateSectionAsync(SectionType.Home); } @@ -379,7 +379,7 @@ private async Task AddElementToSectionAsync(INavigationControlItem elem, Locatio else { string drivePath = drive.Path; - IList paths = section.ChildItems.Select(item => item.Path).ToList(); + var paths = section.ChildItems.Select(item => item.Path).ToList(); if (!paths.Contains(drivePath)) { @@ -1276,7 +1276,7 @@ private async Task HandleLocationItemDroppedAsync(LocationItem locationItem, Ite } } - private Task HandleDriveItemDroppedAsync(DriveItem driveItem, ItemDroppedEventArgs args) + private Task HandleDriveItemDroppedAsync(DriveItem driveItem, ItemDroppedEventArgs args) { return FilesystemHelpers.PerformOperationTypeAsync(args.RawEvent.AcceptedOperation, args.RawEvent.DataView, driveItem.Path, false, true); } From 0e145be1372c0feba6859263e86dbc035bd230df Mon Sep 17 00:00:00 2001 From: hishitetsu <66369541+hishitetsu@users.noreply.github.com> Date: Mon, 8 Apr 2024 23:24:39 +0900 Subject: [PATCH 28/41] Code Quality: Avoid unnecessary shell folder fetching (#15140) --- src/Files.App/Helpers/Win32/Win32Helper.Shell.cs | 7 ++++--- src/Files.App/Services/QuickAccessService.cs | 2 +- src/Files.App/Utils/RecentItem/RecentItems.cs | 2 +- src/Files.App/Utils/RecycleBin/RecycleBinHelpers.cs | 2 +- .../Utils/Storage/StorageItems/ShellStorageFolder.cs | 2 +- 5 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/Files.App/Helpers/Win32/Win32Helper.Shell.cs b/src/Files.App/Helpers/Win32/Win32Helper.Shell.cs index 873bd38df45e..42325b4ebef7 100644 --- a/src/Files.App/Helpers/Win32/Win32Helper.Shell.cs +++ b/src/Files.App/Helpers/Win32/Win32Helper.Shell.cs @@ -17,7 +17,7 @@ public static partial class Win32Helper private readonly static ShellFolder _controlPanelCategoryView = new("::{26EE0668-A00A-44D7-9371-BEB064C98683}"); - public static async Task<(ShellFileItem Folder, List Enumerate)> GetShellFolderAsync(string path, string action, int from, int count, params string[] properties) + public static async Task<(ShellFileItem Folder, List Enumerate)> GetShellFolderAsync(string path, bool getFolder, bool getEnumerate, int from, int count, params string[] properties) { if (path.StartsWith("::{", StringComparison.Ordinal)) { @@ -43,9 +43,10 @@ public static async Task<(ShellFileItem Folder, List Enumerate)> return (null, flc); } - folder = ShellFolderExtensions.GetShellFileItem(shellFolder); + if (getFolder) + folder = ShellFolderExtensions.GetShellFileItem(shellFolder); - if (action == "Enumerate") + if (getEnumerate) { foreach (var folderItem in shellFolder.Skip(from).Take(count)) { diff --git a/src/Files.App/Services/QuickAccessService.cs b/src/Files.App/Services/QuickAccessService.cs index 30b6462f5cf0..51975e79c911 100644 --- a/src/Files.App/Services/QuickAccessService.cs +++ b/src/Files.App/Services/QuickAccessService.cs @@ -12,7 +12,7 @@ internal sealed class QuickAccessService : IQuickAccessService public async Task> GetPinnedFoldersAsync() { - var result = (await Win32Helper.GetShellFolderAsync(guid, "Enumerate", 0, int.MaxValue, "System.Home.IsPinned")).Enumerate + var result = (await Win32Helper.GetShellFolderAsync(guid, false, true, 0, int.MaxValue, "System.Home.IsPinned")).Enumerate .Where(link => link.IsFolder); return result; } diff --git a/src/Files.App/Utils/RecentItem/RecentItems.cs b/src/Files.App/Utils/RecentItem/RecentItems.cs index dd05f8a6fa74..fc21174f3083 100644 --- a/src/Files.App/Utils/RecentItem/RecentItems.cs +++ b/src/Files.App/Utils/RecentItem/RecentItems.cs @@ -109,7 +109,7 @@ public async Task UpdateRecentFoldersAsync() /// public async Task> ListRecentFilesAsync() { - return (await Win32Helper.GetShellFolderAsync(QuickAccessGuid, "Enumerate", 0, int.MaxValue)).Enumerate + return (await Win32Helper.GetShellFolderAsync(QuickAccessGuid, false, true, 0, int.MaxValue)).Enumerate .Where(link => !link.IsFolder) .Select(link => new RecentItem(link, ShowFileExtensions)).ToList(); } diff --git a/src/Files.App/Utils/RecycleBin/RecycleBinHelpers.cs b/src/Files.App/Utils/RecycleBin/RecycleBinHelpers.cs index 9d1129418e79..493c15d5337c 100644 --- a/src/Files.App/Utils/RecycleBin/RecycleBinHelpers.cs +++ b/src/Files.App/Utils/RecycleBin/RecycleBinHelpers.cs @@ -19,7 +19,7 @@ public static class RecycleBinHelpers public static async Task> EnumerateRecycleBin() { - return (await Win32Helper.GetShellFolderAsync(Constants.UserEnvironmentPaths.RecycleBinPath, "Enumerate", 0, int.MaxValue)).Enumerate; + return (await Win32Helper.GetShellFolderAsync(Constants.UserEnvironmentPaths.RecycleBinPath, false, true, 0, int.MaxValue)).Enumerate; } public static ulong GetSize() diff --git a/src/Files.App/Utils/Storage/StorageItems/ShellStorageFolder.cs b/src/Files.App/Utils/Storage/StorageItems/ShellStorageFolder.cs index c461cd09efc2..44b867205205 100644 --- a/src/Files.App/Utils/Storage/StorageItems/ShellStorageFolder.cs +++ b/src/Files.App/Utils/Storage/StorageItems/ShellStorageFolder.cs @@ -108,7 +108,7 @@ public static IAsyncOperation FromPathAsync(string path) protected static async Task<(ShellFileItem Folder, List Items)> GetFolderAndItems(string path, bool enumerate, int startIndex = 0, int maxItemsToRetrieve = int.MaxValue) { - return await Win32Helper.GetShellFolderAsync(path, enumerate ? "Enumerate" : "Query", startIndex, maxItemsToRetrieve); + return await Win32Helper.GetShellFolderAsync(path, !enumerate, enumerate, startIndex, maxItemsToRetrieve); } public override IAsyncOperation ToStorageFolderAsync() => throw new NotSupportedException(); From afcd8b48a06fcf9a445ec2b3a11f45623f823979 Mon Sep 17 00:00:00 2001 From: hishitetsu <66369541+hishitetsu@users.noreply.github.com> Date: Tue, 9 Apr 2024 00:03:45 +0900 Subject: [PATCH 29/41] Fix: Fixed issue where some file operations would fail (#15139) --- src/Files.App/Data/Items/ListedItem.cs | 2 +- src/Files.App/Data/Models/ItemViewModel.cs | 2 +- src/Files.App/MainWindow.xaml.cs | 2 +- src/Files.App/Utils/FileTags/FileTagsHelper.cs | 6 +++--- .../Utils/Storage/Operations/FileOperationsHelpers.cs | 8 ++++---- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Files.App/Data/Items/ListedItem.cs b/src/Files.App/Data/Items/ListedItem.cs index d7deb502397c..012dca90fb24 100644 --- a/src/Files.App/Data/Items/ListedItem.cs +++ b/src/Files.App/Data/Items/ListedItem.cs @@ -102,7 +102,7 @@ public string[] FileTags { Debug.Assert(value != null); var dbInstance = FileTagsHelper.GetDbInstance(); - dbInstance.SetTags(ItemPath, FileFRN, value); + dbInstance.SetTags(ItemPath, FileFRN, value ?? []); HasTags = !FileTags.IsEmpty(); FileTagsHelper.WriteFileTag(ItemPath, value); OnPropertyChanged(nameof(FileTagsUI)); diff --git a/src/Files.App/Data/Models/ItemViewModel.cs b/src/Files.App/Data/Models/ItemViewModel.cs index 03469d3c7933..0abbd0e2d618 100644 --- a/src/Files.App/Data/Models/ItemViewModel.cs +++ b/src/Files.App/Data/Models/ItemViewModel.cs @@ -1036,7 +1036,7 @@ private async Task LoadThumbnailAsync(ListedItem item, CancellationToken cancell private static void SetFileTag(ListedItem item) { var dbInstance = FileTagsHelper.GetDbInstance(); - dbInstance.SetTags(item.ItemPath, item.FileFRN, item.FileTags); + dbInstance.SetTags(item.ItemPath, item.FileFRN, item.FileTags ?? []); } // This works for recycle bin as well as GetFileFromPathAsync/GetFolderFromPathAsync work diff --git a/src/Files.App/MainWindow.xaml.cs b/src/Files.App/MainWindow.xaml.cs index 790e352bd4a5..6030e0900d30 100644 --- a/src/Files.App/MainWindow.xaml.cs +++ b/src/Files.App/MainWindow.xaml.cs @@ -290,7 +290,7 @@ async Task PerformNavigationAsync(string payload, string selectItem = null) .OnSuccess(item => FileTagsHelper.GetFileFRN(item)); if (fileFRN is not null) { - var tagUid = tag is not null ? new[] { tag.Uid } : null; + var tagUid = tag is not null ? new[] { tag.Uid } : []; var dbInstance = FileTagsHelper.GetDbInstance(); dbInstance.SetTags(file, fileFRN, tagUid); FileTagsHelper.WriteFileTag(file, tagUid); diff --git a/src/Files.App/Utils/FileTags/FileTagsHelper.cs b/src/Files.App/Utils/FileTags/FileTagsHelper.cs index f63b8fe5044e..b1eb0d4cd60d 100644 --- a/src/Files.App/Utils/FileTags/FileTagsHelper.cs +++ b/src/Files.App/Utils/FileTags/FileTagsHelper.cs @@ -79,7 +79,7 @@ public static void UpdateTagsDb() } else { - dbInstance.SetTags(null, file.Frn, null); + dbInstance.SetTags(null, file.Frn, []); } } else @@ -94,12 +94,12 @@ public static void UpdateTagsDb() dbInstance.SetTags(file.FilePath, frn, tag); }, App.Logger)) { - dbInstance.SetTags(file.FilePath, null, null); + dbInstance.SetTags(file.FilePath, null, []); } } else { - dbInstance.SetTags(file.FilePath, null, null); + dbInstance.SetTags(file.FilePath, null, []); } } } diff --git a/src/Files.App/Utils/Storage/Operations/FileOperationsHelpers.cs b/src/Files.App/Utils/Storage/Operations/FileOperationsHelpers.cs index 131823ec9d8a..747da26c9351 100644 --- a/src/Files.App/Utils/Storage/Operations/FileOperationsHelpers.cs +++ b/src/Files.App/Utils/Storage/Operations/FileOperationsHelpers.cs @@ -854,7 +854,7 @@ private static void UpdateFileTagsDb(ShellFileOperations2.ShellFileOpEventArgs e }; if (destination is null) { - dbInstance.SetTags(sourcePath, null, null); // remove tag from deleted files + dbInstance.SetTags(sourcePath, null, []); // remove tag from deleted files } else { @@ -864,7 +864,7 @@ private static void UpdateFileTagsDb(ShellFileOperations2.ShellFileOpEventArgs e { var tag = dbInstance.GetTags(sourcePath, null); - dbInstance.SetTags(destination, FileTagsHelper.GetFileFRN(destination), tag); // copy tag to new files + dbInstance.SetTags(destination, FileTagsHelper.GetFileFRN(destination), tag ?? []); // copy tag to new files using var si = new ShellItem(destination); if (si.IsFolder) // File tag is not copied automatically for folders { @@ -882,7 +882,7 @@ private static void UpdateFileTagsDb(ShellFileOperations2.ShellFileOpEventArgs e var tags = dbInstance.GetAllUnderPath(sourcePath).ToList(); if (destination is null) // remove tag for items contained in the folder { - tags.ForEach(t => dbInstance.SetTags(t.FilePath, null, null)); + tags.ForEach(t => dbInstance.SetTags(t.FilePath, null, [])); } else { @@ -893,7 +893,7 @@ private static void UpdateFileTagsDb(ShellFileOperations2.ShellFileOpEventArgs e SafetyExtensions.IgnoreExceptions(() => { var subPath = t.FilePath.Replace(sourcePath, destination, StringComparison.Ordinal); - dbInstance.SetTags(subPath, FileTagsHelper.GetFileFRN(subPath), t.Tags); + dbInstance.SetTags(subPath, FileTagsHelper.GetFileFRN(subPath), t.Tags ?? []); }, App.Logger); }); } From d10270aa94c4e944ce1c65e95f4e69ec17b0462d Mon Sep 17 00:00:00 2001 From: Steve Date: Wed, 10 Apr 2024 01:05:06 +0900 Subject: [PATCH 30/41] Code Quality: Use collection expressions and make sure FileTags is not null (#15143) --- .../Database/FileTagsDatabase.cs | 4 +- .../FtpStorage/FtpManager.cs | 2 +- .../FileSystem/OpenFileLocationAction.cs | 2 +- .../Actions/FileSystem/OpenItemAction.cs | 2 +- .../Actions/Open/OpenTerminalAction.cs | 4 +- .../Sidebar/PinFolderToSidebarAction.cs | 2 +- .../Sidebar/UnpinFolderToSidebarAction.cs | 2 +- .../Data/Commands/HotKey/HotKeyCollection.cs | 4 +- .../Factories/AppThemeResourcesFactory.cs | 6 +- .../ContentPageContextFlyoutFactory.cs | 66 +++++++++---------- .../PropertiesNavigationViewItemFactory.cs | 2 +- ...ecurityAdvancedAccessControlItemFactory.cs | 12 ++-- .../Factories/ShellContextFlyoutHelper.cs | 12 ++-- src/Files.App/Data/Items/DriveItem.cs | 4 +- src/Files.App/Data/Items/ListedItem.cs | 2 +- src/Files.App/Data/Items/ShellFileItem.cs | 2 +- .../Data/Items/ShellOperationResult.cs | 2 +- .../Data/Items/WidgetFileTagsContainerItem.cs | 2 +- .../Models/DirectoryPropertiesViewModel.cs | 4 +- src/Files.App/Data/Models/DrivesViewModel.cs | 2 +- src/Files.App/Data/Models/ItemViewModel.cs | 10 +-- .../Data/Models/NetworkDrivesViewModel.cs | 8 ++- src/Files.App/Data/Models/RemovableDevice.cs | 4 +- .../SelectedItemsPropertiesViewModel.cs | 4 +- .../Dialogs/CreateArchiveDialog.xaml.cs | 18 ++--- .../NavigationInteractionTracker.cs | 2 +- src/Files.App/Helpers/ShareItemHelpers.cs | 2 +- .../Helpers/UI/UIFilesystemHelpers.cs | 4 +- .../Helpers/Win32/Win32Helper.Storage.cs | 6 +- src/Files.App/Program.cs | 4 +- src/Files.App/Services/JumpListService.cs | 4 +- src/Files.App/Services/QuickAccessService.cs | 6 +- .../Settings/FileTagsSettingsService.cs | 6 +- .../Settings/GeneralSettingsService.cs | 2 +- .../Services/Settings/UserSettingsService.cs | 4 +- src/Files.App/Services/UpdateService.cs | 2 +- .../KeyboardShortcut/KeyboardShortcut.cs | 2 +- .../UserControls/Menus/FileTagsContextMenu.cs | 6 +- .../RectangleSelection_ListViewBase.cs | 2 +- .../SideBar/SidebarViewAutomationPeer.cs | 8 +-- .../UserControls/TabBar/BaseTabBar.cs | 6 +- .../UserControls/Widgets/DrivesWidget.xaml.cs | 4 +- .../Widgets/FileTagsWidget.xaml.cs | 2 +- .../Widgets/QuickAccessWidget.xaml.cs | 4 +- .../Widgets/RecentFilesWidget.xaml.cs | 4 +- .../Utils/Archives/CompressArchiveModel.cs | 2 +- .../Utils/Cloud/CloudDrivesDetector.cs | 2 +- .../Utils/Cloud/CloudDrivesManager.cs | 2 +- .../Utils/CommandLine/CommandLineParser.cs | 4 +- .../Utils/CommandLine/ParsedCommand.cs | 2 +- .../Utils/FileTags/FileTagsHelper.cs | 6 +- .../Utils/FileTags/FileTagsManager.cs | 2 +- src/Files.App/Utils/Git/GitHelpers.cs | 2 +- .../Utils/Global/WSLDistroManager.cs | 2 +- .../Utils/Global/WallpaperHelpers.cs | 2 +- src/Files.App/Utils/Library/LibraryManager.cs | 4 +- src/Files.App/Utils/RecentItem/RecentItems.cs | 4 +- .../Utils/RecycleBin/RecycleBinManager.cs | 2 +- src/Files.App/Utils/Shell/ContextMenu.cs | 6 +- .../Utils/Shell/ShellNewMenuHelper.cs | 2 +- .../Utils/StatusCenter/StatusCenterItem.cs | 50 +++++++------- .../BulkConcurrentObservableCollection.cs | 4 +- .../Collection/ConcurrentCollection.cs | 2 +- .../Collection/GroupedHeaderViewModel.cs | 2 +- .../Storage/Collection/GroupingHelper.cs | 6 +- .../Storage/Helpers/FilePropertiesHelpers.cs | 2 +- .../Storage/Helpers/FileSecurityHelpers.cs | 4 +- .../Utils/Storage/Helpers/FolderHelpers.cs | 2 +- .../Storage/Helpers/StorageFileExtensions.cs | 2 +- .../Storage/History/StorageHistoryWrapper.cs | 2 +- .../Operations/FileOperationsHelpers.cs | 4 +- .../Storage/Operations/FilesystemHelpers.cs | 14 ++-- .../Operations/ShellFilesystemOperations.cs | 2 +- .../Utils/Storage/Search/FolderSearch.cs | 2 +- .../Storage/Security/AccessControlList.cs | 4 +- .../Dialogs/AddBranchDialogViewModel.cs | 2 +- .../AddItemDialog/AddItemDialogViewModel.cs | 2 +- src/Files.App/ViewModels/HomeViewModel.cs | 2 +- src/Files.App/ViewModels/MainPageViewModel.cs | 8 +-- .../Properties/CustomizationViewModel.cs | 2 +- .../ViewModels/Properties/HashesViewModel.cs | 8 +-- .../Properties/Items/BaseProperties.cs | 6 +- .../Items/CombinedFileProperties.cs | 12 ++-- .../Properties/Items/DriveProperties.cs | 2 +- .../Properties/Items/FileProperties.cs | 12 ++-- .../Properties/Items/FileProperty.cs | 10 +-- .../ViewModels/Settings/AdvancedViewModel.cs | 2 +- .../Settings/AppearanceViewModel.cs | 8 +-- .../ViewModels/Settings/GeneralViewModel.cs | 2 +- .../ViewModels/Settings/TagsViewModel.cs | 2 +- .../UserControls/AddressToolbarViewModel.cs | 6 +- .../UserControls/InfoPaneViewModel.cs | 2 +- .../UserControls/Previews/BasePreviewModel.cs | 4 +- .../Previews/FolderPreviewViewModel.cs | 6 +- .../Previews/HtmlPreviewViewModel.cs | 2 +- .../Previews/ImagePreviewViewModel.cs | 2 +- .../Previews/MarkdownPreviewViewModel.cs | 2 +- .../Previews/PDFPreviewViewModel.cs | 2 +- .../Previews/RichTextPreviewViewModel.cs | 2 +- .../Previews/ShellPreviewViewModel.cs | 6 +- .../UserControls/SearchBoxViewModel.cs | 4 +- .../UserControls/SidebarViewModel.cs | 13 ++-- .../UserControls/StatusCenterViewModel.cs | 2 +- .../FileTagsContainerViewModel.cs | 2 +- .../FileTagsWidget/FileTagsWidgetViewModel.cs | 2 +- src/Files.App/Views/HomePage.xaml.cs | 2 +- src/Files.App/Views/Layouts/BaseLayoutPage.cs | 12 ++-- .../Views/Layouts/DetailsLayoutPage.xaml.cs | 2 +- .../Views/Properties/LibraryPage.xaml.cs | 2 +- .../Utilities/SourceGeneratorHelper.cs | 17 ++--- .../Extensions/EnumerableExtensions.cs | 4 +- src/Files.Shared/Extensions/LinqExtensions.cs | 2 +- .../Helper/TestHelper.cs | 2 +- .../Files.InteractionTests/SessionManager.cs | 4 +- 114 files changed, 300 insertions(+), 292 deletions(-) diff --git a/src/Files.App.Server/Database/FileTagsDatabase.cs b/src/Files.App.Server/Database/FileTagsDatabase.cs index 19226cd2a15b..6b771f393a54 100644 --- a/src/Files.App.Server/Database/FileTagsDatabase.cs +++ b/src/Files.App.Server/Database/FileTagsDatabase.cs @@ -162,9 +162,9 @@ public void UpdateTag(ulong oldFrn, ulong? frn, string? newFilePath) } } - public string[]? GetTags(string? filePath, ulong? frn) + public string[] GetTags(string? filePath, ulong? frn) { - return FindTag(filePath, frn)?.Tags; + return FindTag(filePath, frn)?.Tags ?? []; } public IEnumerable GetAll() diff --git a/src/Files.App.Storage/FtpStorage/FtpManager.cs b/src/Files.App.Storage/FtpStorage/FtpManager.cs index 908df7c19ff3..f7bcef00b2b5 100644 --- a/src/Files.App.Storage/FtpStorage/FtpManager.cs +++ b/src/Files.App.Storage/FtpStorage/FtpManager.cs @@ -8,7 +8,7 @@ namespace Files.App.Storage.FtpStorage { public static class FtpManager { - public static readonly Dictionary Credentials = new(); + public static readonly Dictionary Credentials = []; public static readonly NetworkCredential Anonymous = new("anonymous", "anonymous"); } diff --git a/src/Files.App/Actions/FileSystem/OpenFileLocationAction.cs b/src/Files.App/Actions/FileSystem/OpenFileLocationAction.cs index cbbf1e54e8ac..0b769b318075 100644 --- a/src/Files.App/Actions/FileSystem/OpenFileLocationAction.cs +++ b/src/Files.App/Actions/FileSystem/OpenFileLocationAction.cs @@ -49,7 +49,7 @@ public async Task ExecuteAsync() context.ShellPage?.NavigateWithArguments(context.ShellPage.InstanceViewModel.FolderSettings.GetLayoutType(folderPath), new NavigationArguments() { NavPathParam = folderPath, - SelectItems = new[] { Path.GetFileName(item.TargetPath.TrimPath()) }, + SelectItems = [Path.GetFileName(item.TargetPath.TrimPath())], AssociatedTabInstance = context.ShellPage }); } diff --git a/src/Files.App/Actions/FileSystem/OpenItemAction.cs b/src/Files.App/Actions/FileSystem/OpenItemAction.cs index 2227fe21f2a1..f69cbc2bb11c 100644 --- a/src/Files.App/Actions/FileSystem/OpenItemAction.cs +++ b/src/Files.App/Actions/FileSystem/OpenItemAction.cs @@ -132,7 +132,7 @@ public async Task ExecuteAsync() context.ShellPage.NavigateWithArguments(context.ShellPage.InstanceViewModel.FolderSettings.GetLayoutType(folderPath), new NavigationArguments() { NavPathParam = folderPath, - SelectItems = new[] { item.ItemNameRaw }, + SelectItems = [item.ItemNameRaw], AssociatedTabInstance = context.ShellPage }); } diff --git a/src/Files.App/Actions/Open/OpenTerminalAction.cs b/src/Files.App/Actions/Open/OpenTerminalAction.cs index 0a1e70b7c8a7..047206883c04 100644 --- a/src/Files.App/Actions/Open/OpenTerminalAction.cs +++ b/src/Files.App/Actions/Open/OpenTerminalAction.cs @@ -85,10 +85,10 @@ protected string[] GetPaths() } else if (context.Folder is not null) { - return new string[1] { context.Folder.ItemPath }; + return [context.Folder.ItemPath]; } - return Array.Empty(); + return []; } private bool GetIsExecutable() diff --git a/src/Files.App/Actions/Sidebar/PinFolderToSidebarAction.cs b/src/Files.App/Actions/Sidebar/PinFolderToSidebarAction.cs index 3fd4439d11b4..835701c1a9bf 100644 --- a/src/Files.App/Actions/Sidebar/PinFolderToSidebarAction.cs +++ b/src/Files.App/Actions/Sidebar/PinFolderToSidebarAction.cs @@ -47,7 +47,7 @@ public async Task ExecuteAsync() private bool GetIsExecutable() { - string[] pinnedFolders = App.QuickAccessManager.Model.PinnedFolders.ToArray(); + string[] pinnedFolders = [.. App.QuickAccessManager.Model.PinnedFolders]; return context.HasSelection ? context.SelectedItems.All(IsPinnable) diff --git a/src/Files.App/Actions/Sidebar/UnpinFolderToSidebarAction.cs b/src/Files.App/Actions/Sidebar/UnpinFolderToSidebarAction.cs index 6554eb5addf7..ae15537323e8 100644 --- a/src/Files.App/Actions/Sidebar/UnpinFolderToSidebarAction.cs +++ b/src/Files.App/Actions/Sidebar/UnpinFolderToSidebarAction.cs @@ -44,7 +44,7 @@ public async Task ExecuteAsync() private bool GetIsExecutable() { - string[] pinnedFolders = App.QuickAccessManager.Model.PinnedFolders.ToArray(); + string[] pinnedFolders = [.. App.QuickAccessManager.Model.PinnedFolders]; return context.HasSelection ? context.SelectedItems.All(IsPinned) diff --git a/src/Files.App/Data/Commands/HotKey/HotKeyCollection.cs b/src/Files.App/Data/Commands/HotKey/HotKeyCollection.cs index 34cbbacf2e96..dd7a4ae332d0 100644 --- a/src/Files.App/Data/Commands/HotKey/HotKeyCollection.cs +++ b/src/Files.App/Data/Commands/HotKey/HotKeyCollection.cs @@ -11,7 +11,7 @@ namespace Files.App.Data.Commands [DebuggerDisplay("{Code}")] public readonly struct HotKeyCollection : IEnumerable, IEquatable { - private static readonly char[] parseSeparators = new char[] { ',', ';', ' ', '\t' }; + private static readonly char[] parseSeparators = [',', ';', ' ', '\t']; private readonly ImmutableArray hotKeys; @@ -25,7 +25,7 @@ namespace Files.App.Data.Commands public string Code => string.Join(',', hotKeys.Select(hotKey => hotKey.Code)); public string Label => string.Join(", ", hotKeys.Where(hotKey => hotKey.IsVisible).Select(hotKey => hotKey.Label)); - public HotKeyCollection() => hotKeys = ImmutableArray.Empty; + public HotKeyCollection() => hotKeys = []; public HotKeyCollection(params HotKey[] hotKeys) => this.hotKeys = Clean(hotKeys); public HotKeyCollection(IEnumerable hotKeys) => this.hotKeys = Clean(hotKeys); diff --git a/src/Files.App/Data/Factories/AppThemeResourcesFactory.cs b/src/Files.App/Data/Factories/AppThemeResourcesFactory.cs index db5222661595..d87f6ee44c82 100644 --- a/src/Files.App/Data/Factories/AppThemeResourcesFactory.cs +++ b/src/Files.App/Data/Factories/AppThemeResourcesFactory.cs @@ -5,8 +5,8 @@ namespace Files.App.Data.Factories { public static class AppThemeResourceFactory { - public static ObservableCollection AppThemeResources { get; } = new ObservableCollection() - { + public static ObservableCollection AppThemeResources { get; } = + [ new AppThemeResourceItem { BackgroundColor = "#00000000", /* Transparent */ @@ -107,6 +107,6 @@ public static class AppThemeResourceFactory BackgroundColor = "#327E735F", /* #7E735F */ Name = "Camouflage".GetLocalizedResource() } - }; + ]; } } diff --git a/src/Files.App/Data/Factories/ContentPageContextFlyoutFactory.cs b/src/Files.App/Data/Factories/ContentPageContextFlyoutFactory.cs index b75ea039ae1e..f5e172cdab2a 100644 --- a/src/Files.App/Data/Factories/ContentPageContextFlyoutFactory.cs +++ b/src/Files.App/Data/Factories/ContentPageContextFlyoutFactory.cs @@ -106,8 +106,8 @@ private static bool Check(ContextMenuFlyoutItemViewModel item, CurrentInstanceVi ShowInSearchPage = true, ShowInFtpPage = true, ShowInZipPage = true, - Items = new List - { + Items = + [ new ContextMenuFlyoutItemViewModelBuilder(Commands.LayoutDetails) { IsToggle = true @@ -132,7 +132,7 @@ private static bool Check(ContextMenuFlyoutItemViewModel item, CurrentInstanceVi { IsToggle = true }.Build(), - }, + ], }, new ContextMenuFlyoutItemViewModel() { @@ -146,8 +146,8 @@ private static bool Check(ContextMenuFlyoutItemViewModel item, CurrentInstanceVi ShowInSearchPage = true, ShowInFtpPage = true, ShowInZipPage = true, - Items = new List - { + Items = + [ new ContextMenuFlyoutItemViewModelBuilder(Commands.SortByName) { IsToggle = true @@ -204,7 +204,7 @@ private static bool Check(ContextMenuFlyoutItemViewModel item, CurrentInstanceVi { IsToggle = true }.Build(), - }, + ], }, new ContextMenuFlyoutItemViewModel() { @@ -215,8 +215,8 @@ private static bool Check(ContextMenuFlyoutItemViewModel item, CurrentInstanceVi ShowInSearchPage = true, ShowInFtpPage = true, ShowInZipPage = true, - Items = new List - { + Items = + [ new ContextMenuFlyoutItemViewModelBuilder(Commands.GroupByNone) { IsToggle = true @@ -232,8 +232,8 @@ private static bool Check(ContextMenuFlyoutItemViewModel item, CurrentInstanceVi ShowInSearchPage = true, ShowInFtpPage = true, ShowInZipPage = true, - Items = new List - { + Items = + [ new ContextMenuFlyoutItemViewModelBuilder(Commands.GroupByDateModifiedYear) { IsToggle = true @@ -246,7 +246,7 @@ private static bool Check(ContextMenuFlyoutItemViewModel item, CurrentInstanceVi { IsToggle = true }.Build(), - }, + ], }, new ContextMenuFlyoutItemViewModel() { @@ -255,8 +255,8 @@ private static bool Check(ContextMenuFlyoutItemViewModel item, CurrentInstanceVi ShowInSearchPage = true, ShowInFtpPage = true, ShowInZipPage = true, - Items = new List - { + Items = + [ new ContextMenuFlyoutItemViewModelBuilder(Commands.GroupByDateCreatedYear) { IsToggle = true @@ -269,7 +269,7 @@ private static bool Check(ContextMenuFlyoutItemViewModel item, CurrentInstanceVi { IsToggle = true }.Build(), - }, + ], }, new ContextMenuFlyoutItemViewModelBuilder(Commands.GroupByType) { @@ -296,8 +296,8 @@ private static bool Check(ContextMenuFlyoutItemViewModel item, CurrentInstanceVi Text = "DateDeleted".GetLocalizedResource(), ShowInRecycleBin = true, IsHidden = !currentInstanceViewModel.IsPageTypeRecycleBin, - Items = new List - { + Items = + [ new ContextMenuFlyoutItemViewModelBuilder(Commands.GroupByDateDeletedYear) { IsToggle = true @@ -310,7 +310,7 @@ private static bool Check(ContextMenuFlyoutItemViewModel item, CurrentInstanceVi { IsToggle = true }.Build(), - }, + ], }, new ContextMenuFlyoutItemViewModelBuilder(Commands.GroupByFolderPath) { @@ -334,7 +334,7 @@ private static bool Check(ContextMenuFlyoutItemViewModel item, CurrentInstanceVi IsToggle = true, IsVisible = true }.Build(), - }, + ], }, new ContextMenuFlyoutItemViewModelBuilder(Commands.RefreshItems) { @@ -387,13 +387,13 @@ private static bool Check(ContextMenuFlyoutItemViewModel item, CurrentInstanceVi Tag = "OpenWithOverflow", IsHidden = true, CollapseLabel = true, - Items = new List() { + Items = [ new() { Text = "Placeholder", ShowInSearchPage = true, } - }, + ], ShowInSearchPage = true, ShowItem = itemsSelected && showOpenItemWith }, @@ -406,12 +406,12 @@ private static bool Check(ContextMenuFlyoutItemViewModel item, CurrentInstanceVi Text = "BaseLayoutItemContextFlyoutSetAs/Text".GetLocalizedResource(), ShowItem = itemsSelected && (selectedItemsPropertiesViewModel?.IsSelectedItemImage ?? false), ShowInSearchPage = true, - Items = new List - { + Items = + [ new ContextMenuFlyoutItemViewModelBuilder(Commands.SetAsWallpaperBackground).Build(), new ContextMenuFlyoutItemViewModelBuilder(Commands.SetAsLockscreenBackground).Build(), new ContextMenuFlyoutItemViewModelBuilder(Commands.SetAsSlideshowBackground).Build(), - } + ] }, new ContextMenuFlyoutItemViewModelBuilder(Commands.RotateLeft) { @@ -511,12 +511,12 @@ private static bool Check(ContextMenuFlyoutItemViewModel item, CurrentInstanceVi { OpacityIconStyle = "ColorIconZip", }, - Items = new List - { + Items = + [ new ContextMenuFlyoutItemViewModelBuilder(Commands.CompressIntoArchive).Build(), new ContextMenuFlyoutItemViewModelBuilder(Commands.CompressIntoZip).Build(), new ContextMenuFlyoutItemViewModelBuilder(Commands.CompressIntoSevenZip).Build(), - }, + ], ShowItem = UserSettingsService.GeneralSettingsService.ShowCompressionOptions && itemsSelected && CompressHelper.CanCompress(selectedItems) }, new ContextMenuFlyoutItemViewModel @@ -527,13 +527,13 @@ private static bool Check(ContextMenuFlyoutItemViewModel item, CurrentInstanceVi { OpacityIconStyle = "ColorIconZip", }, - Items = new List - { + Items = + [ new ContextMenuFlyoutItemViewModelBuilder(Commands.DecompressArchive).Build(), new ContextMenuFlyoutItemViewModelBuilder(Commands.DecompressArchiveHereSmart).Build(), new ContextMenuFlyoutItemViewModelBuilder(Commands.DecompressArchiveHere).Build(), new ContextMenuFlyoutItemViewModelBuilder(Commands.DecompressArchiveToChildFolder).Build(), - }, + ], ShowItem = UserSettingsService.GeneralSettingsService.ShowCompressionOptions && CompressHelper.CanDecompress(selectedItems) }, new ContextMenuFlyoutItemViewModel() @@ -550,13 +550,13 @@ private static bool Check(ContextMenuFlyoutItemViewModel item, CurrentInstanceVi Tag = "SendToOverflow", IsHidden = true, CollapseLabel = true, - Items = new List() { + Items = [ new() { Text = "Placeholder", ShowInSearchPage = true, } - }, + ], ShowInSearchPage = true, ShowItem = itemsSelected && UserSettingsService.GeneralSettingsService.ShowSendToMenu }, @@ -592,7 +592,7 @@ private static bool Check(ContextMenuFlyoutItemViewModel item, CurrentInstanceVi { Text = "Loading".GetLocalizedResource(), Glyph = "\xE712", - Items = new List(), + Items = [], ID = "ItemOverflow", Tag = "ItemOverflow", ShowInFtpPage = true, @@ -671,7 +671,7 @@ public static void SwapPlaceholderWithShellOption(CommandBarFlyout contextMenu, if (replacingItem is not null) { - var (_, bitLockerCommands) = ContextFlyoutModelToElementHelper.GetAppBarItemsFromModel(new List() { replacingItem }); + var (_, bitLockerCommands) = ContextFlyoutModelToElementHelper.GetAppBarItemsFromModel([replacingItem]); contextMenu.SecondaryCommands.Insert( position, bitLockerCommands.FirstOrDefault() diff --git a/src/Files.App/Data/Factories/PropertiesNavigationViewItemFactory.cs b/src/Files.App/Data/Factories/PropertiesNavigationViewItemFactory.cs index 4388919c382c..df4da3b33448 100644 --- a/src/Files.App/Data/Factories/PropertiesNavigationViewItemFactory.cs +++ b/src/Files.App/Data/Factories/PropertiesNavigationViewItemFactory.cs @@ -12,7 +12,7 @@ public static class PropertiesNavigationViewItemFactory { public static ObservableCollection Initialize(object item) { - ObservableCollection PropertiesNavigationViewItems = new(); + ObservableCollection PropertiesNavigationViewItems = []; var generalItem = new NavigationViewItemButtonStyleItem() { diff --git a/src/Files.App/Data/Factories/SecurityAdvancedAccessControlItemFactory.cs b/src/Files.App/Data/Factories/SecurityAdvancedAccessControlItemFactory.cs index 0d631b6b92ec..c00b96a4514a 100644 --- a/src/Files.App/Data/Factories/SecurityAdvancedAccessControlItemFactory.cs +++ b/src/Files.App/Data/Factories/SecurityAdvancedAccessControlItemFactory.cs @@ -23,8 +23,8 @@ public static ObservableCollection Initialize(AccessControlEntry if (isAdvanced) { - accessControls = new() - { + accessControls = + [ new(current) { AccessMask = AccessMaskFlags.FullControl, @@ -133,7 +133,7 @@ public static ObservableCollection Initialize(AccessControlEntry AccessMaskName = "SecurityTakeOwnershipLabel/Text".GetLocalizedResource(), IsEditable = !isInherited } - }; + ]; if (isFolder) { @@ -155,8 +155,8 @@ public static ObservableCollection Initialize(AccessControlEntry } else { - accessControls = new() - { + accessControls = + [ new(current) { AccessMask = AccessMaskFlags.FullControl, @@ -197,7 +197,7 @@ public static ObservableCollection Initialize(AccessControlEntry { AccessMaskName = "SecuritySpecialLabel/Text".GetLocalizedResource() } - }; + ]; if (!isFolder) { diff --git a/src/Files.App/Data/Factories/ShellContextFlyoutHelper.cs b/src/Files.App/Data/Factories/ShellContextFlyoutHelper.cs index ddfbae896897..f14866d8bb93 100644 --- a/src/Files.App/Data/Factories/ShellContextFlyoutHelper.cs +++ b/src/Files.App/Data/Factories/ShellContextFlyoutHelper.cs @@ -138,7 +138,7 @@ bool filterMenuItemsImpl(string menuItem) => !string.IsNullOrEmpty(menuItem) Text = menuFlyoutItem.Label.Replace("&", "", StringComparison.Ordinal), Tag = menuFlyoutItem, BitmapIcon = image, - Items = new List(), + Items = [], }; if (menuFlyoutItem.SubItems.Any()) @@ -181,11 +181,11 @@ async Task InvokeShellMenuItemAsync(ContextMenu contextMenu, object? tag) switch (verb) { case "install" when isFont: - await Win32Helper.InstallFontsAsync(contextMenu.ItemsPath.ToArray(), false); + await Win32Helper.InstallFontsAsync([.. contextMenu.ItemsPath], false); break; case "installAllUsers" when isFont: - await Win32Helper.InstallFontsAsync(contextMenu.ItemsPath.ToArray(), true); + await Win32Helper.InstallFontsAsync([.. contextMenu.ItemsPath], true); break; case "mount": @@ -240,7 +240,7 @@ async Task InvokeShellMenuItemAsync(ContextMenu contextMenu, object? tag) var shiftPressed = InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.Shift).HasFlag(CoreVirtualKeyStates.Down); var shellMenuItems = await ContentPageContextFlyoutFactory.GetItemContextShellCommandsAsync( workingDir: null, - new List() { new ListedItem(null) { ItemPath = path } }, + [new ListedItem(null) { ItemPath = path }], shiftPressed: shiftPressed, showOpenMenu: false, default); @@ -340,7 +340,7 @@ async Task InvokeShellMenuItemAsync(ContextMenu contextMenu, object? tag) { OpacityIconStyle = "ColorIconOpenWith", }; - var (_, openWithItems) = ContextFlyoutModelToElementHelper.GetAppBarItemsFromModel(new List() { openWithItem }); + var (_, openWithItems) = ContextFlyoutModelToElementHelper.GetAppBarItemsFromModel([openWithItem]); var placeholder = itemContextMenuFlyout.SecondaryCommands.FirstOrDefault(x => Equals((x as AppBarButton)?.Tag, "OpenWithPlaceholder")) as AppBarButton; if (placeholder is not null) placeholder.Visibility = Visibility.Collapsed; @@ -352,7 +352,7 @@ async Task InvokeShellMenuItemAsync(ContextMenu contextMenu, object? tag) { await sendToItem.LoadSubMenuAction(); - var (_, sendToItems) = ContextFlyoutModelToElementHelper.GetAppBarItemsFromModel(new List() { sendToItem }); + var (_, sendToItems) = ContextFlyoutModelToElementHelper.GetAppBarItemsFromModel([sendToItem]); var placeholder = itemContextMenuFlyout.SecondaryCommands.FirstOrDefault(x => Equals((x as AppBarButton)?.Tag, "SendToPlaceholder")) as AppBarButton; if (placeholder is not null) placeholder.Visibility = Visibility.Collapsed; diff --git a/src/Files.App/Data/Items/DriveItem.cs b/src/Files.App/Data/Items/DriveItem.cs index 61d2b808645b..1e12528b3832 100644 --- a/src/Files.App/Data/Items/DriveItem.cs +++ b/src/Files.App/Data/Items/DriveItem.cs @@ -260,7 +260,7 @@ public async Task UpdateLabelAsync() { try { - var properties = await Root.Properties.RetrievePropertiesAsync(new[] { "System.ItemNameDisplay" }) + var properties = await Root.Properties.RetrievePropertiesAsync(["System.ItemNameDisplay"]) .AsTask().WithTimeoutAsync(TimeSpan.FromSeconds(5)); Text = (string)properties["System.ItemNameDisplay"]; } @@ -273,7 +273,7 @@ public async Task UpdatePropertiesAsync() { try { - var properties = await Root.Properties.RetrievePropertiesAsync(new[] { "System.FreeSpace", "System.Capacity" }) + var properties = await Root.Properties.RetrievePropertiesAsync(["System.FreeSpace", "System.Capacity"]) .AsTask().WithTimeoutAsync(TimeSpan.FromSeconds(5)); if (properties is not null && properties["System.Capacity"] is not null && properties["System.FreeSpace"] is not null) diff --git a/src/Files.App/Data/Items/ListedItem.cs b/src/Files.App/Data/Items/ListedItem.cs index 012dca90fb24..d7deb502397c 100644 --- a/src/Files.App/Data/Items/ListedItem.cs +++ b/src/Files.App/Data/Items/ListedItem.cs @@ -102,7 +102,7 @@ public string[] FileTags { Debug.Assert(value != null); var dbInstance = FileTagsHelper.GetDbInstance(); - dbInstance.SetTags(ItemPath, FileFRN, value ?? []); + dbInstance.SetTags(ItemPath, FileFRN, value); HasTags = !FileTags.IsEmpty(); FileTagsHelper.WriteFileTag(ItemPath, value); OnPropertyChanged(nameof(FileTagsUI)); diff --git a/src/Files.App/Data/Items/ShellFileItem.cs b/src/Files.App/Data/Items/ShellFileItem.cs index b26b0d796fb3..5c53603c6e2f 100644 --- a/src/Files.App/Data/Items/ShellFileItem.cs +++ b/src/Files.App/Data/Items/ShellFileItem.cs @@ -31,7 +31,7 @@ public class ShellFileItem public ShellFileItem() { - Properties = new Dictionary(); + Properties = []; } public ShellFileItem(bool isFolder, string recyclePath, string fileName, string filePath, DateTime recycleDate, DateTime modifiedDate, DateTime createdDate, string fileSize, ulong fileSizeBytes, string fileType, byte[] pidl) : this() diff --git a/src/Files.App/Data/Items/ShellOperationResult.cs b/src/Files.App/Data/Items/ShellOperationResult.cs index 0b69d68f4ee5..cac1133fd98b 100644 --- a/src/Files.App/Data/Items/ShellOperationResult.cs +++ b/src/Files.App/Data/Items/ShellOperationResult.cs @@ -7,7 +7,7 @@ public sealed class ShellOperationResult { public ShellOperationResult() { - Items = new List(); + Items = []; } /// diff --git a/src/Files.App/Data/Items/WidgetFileTagsContainerItem.cs b/src/Files.App/Data/Items/WidgetFileTagsContainerItem.cs index a665938d518b..ec162812cc34 100644 --- a/src/Files.App/Data/Items/WidgetFileTagsContainerItem.cs +++ b/src/Files.App/Data/Items/WidgetFileTagsContainerItem.cs @@ -50,7 +50,7 @@ public WidgetFileTagsContainerItem(string tagUid, Func openAction) { _tagUid = tagUid; _openAction = openAction; - Tags = new(); + Tags = []; ViewMoreCommand = new AsyncRelayCommand(ViewMore); OpenAllCommand = new AsyncRelayCommand(OpenAll); diff --git a/src/Files.App/Data/Models/DirectoryPropertiesViewModel.cs b/src/Files.App/Data/Models/DirectoryPropertiesViewModel.cs index 96f5535834a6..fe5dc6e1b7a4 100644 --- a/src/Files.App/Data/Models/DirectoryPropertiesViewModel.cs +++ b/src/Files.App/Data/Models/DirectoryPropertiesViewModel.cs @@ -14,9 +14,9 @@ public sealed class DirectoryPropertiesViewModel : ObservableObject private string? _gitRepositoryPath; - private readonly ObservableCollection _localBranches = new(); + private readonly ObservableCollection _localBranches = []; - private readonly ObservableCollection _remoteBranches = new(); + private readonly ObservableCollection _remoteBranches = []; public bool IsBranchesFlyoutExpaned { get; set; } = false; diff --git a/src/Files.App/Data/Models/DrivesViewModel.cs b/src/Files.App/Data/Models/DrivesViewModel.cs index 2a9e38f8904a..49ae47e5a6b5 100644 --- a/src/Files.App/Data/Models/DrivesViewModel.cs +++ b/src/Files.App/Data/Models/DrivesViewModel.cs @@ -35,7 +35,7 @@ public DrivesViewModel(IRemovableDrivesService removableDrivesService, ISizeProv this.folderSizeProvider = folderSizeProvider; this.logger = logger; - drives = new ObservableCollection(); + drives = []; watcher = removableDrivesService.CreateWatcher(); watcher.DeviceAdded += Watcher_DeviceAdded; diff --git a/src/Files.App/Data/Models/ItemViewModel.cs b/src/Files.App/Data/Models/ItemViewModel.cs index 0abbd0e2d618..7044490ae24f 100644 --- a/src/Files.App/Data/Models/ItemViewModel.cs +++ b/src/Files.App/Data/Models/ItemViewModel.cs @@ -473,8 +473,8 @@ public bool AreFilesSortedFirst public ItemViewModel(LayoutPreferencesManager folderSettingsViewModel) { folderSettings = folderSettingsViewModel; - filesAndFolders = new ConcurrentCollection(); - FilesAndFolders = new BulkConcurrentObservableCollection(); + filesAndFolders = []; + FilesAndFolders = []; operationQueue = new ConcurrentQueue<(uint Action, string FileName)>(); gitChangesQueue = new ConcurrentQueue(); itemLoadQueue = new ConcurrentDictionary(); @@ -1755,13 +1755,13 @@ public async Task CheckCloudDriveSyncStatusAsync(IStorageI int? syncStatus = null; if (item is BaseStorageFile file && file.Properties is not null) { - var extraProperties = await FilesystemTasks.Wrap(() => file.Properties.RetrievePropertiesAsync(new string[] { "System.FilePlaceholderStatus" }).AsTask()); + var extraProperties = await FilesystemTasks.Wrap(() => file.Properties.RetrievePropertiesAsync(["System.FilePlaceholderStatus"]).AsTask()); if (extraProperties) syncStatus = (int?)(uint?)extraProperties.Result["System.FilePlaceholderStatus"]; } else if (item is BaseStorageFolder folder && folder.Properties is not null) { - var extraProperties = await FilesystemTasks.Wrap(() => folder.Properties.RetrievePropertiesAsync(new string[] { "System.FilePlaceholderStatus", "System.FileOfflineAvailabilityStatus" }).AsTask()); + var extraProperties = await FilesystemTasks.Wrap(() => folder.Properties.RetrievePropertiesAsync(["System.FilePlaceholderStatus", "System.FileOfflineAvailabilityStatus"]).AsTask()); if (extraProperties) { syncStatus = (int?)(uint?)extraProperties.Result["System.FileOfflineAvailabilityStatus"]; @@ -2110,7 +2110,7 @@ async Task HandleChangesOccurredAsync() if (lastItemAdded is not null && !lastItemAdded.IsArchive) { - await RequestSelectionAsync(new List() { lastItemAdded }); + await RequestSelectionAsync([lastItemAdded]); lastItemAdded = null; } diff --git a/src/Files.App/Data/Models/NetworkDrivesViewModel.cs b/src/Files.App/Data/Models/NetworkDrivesViewModel.cs index fcad7f93bc9c..30c6e6a99e5e 100644 --- a/src/Files.App/Data/Models/NetworkDrivesViewModel.cs +++ b/src/Files.App/Data/Models/NetworkDrivesViewModel.cs @@ -21,7 +21,7 @@ public ObservableCollection Drives public NetworkDrivesViewModel(INetworkDrivesService networkDrivesService) { this.networkDrivesService = networkDrivesService; - drives = new ObservableCollection(); + drives = []; var networkItem = new DriveItem { @@ -47,8 +47,10 @@ public NetworkDrivesViewModel(INetworkDrivesService networkDrivesService) public async Task UpdateDrivesAsync() { - var unsortedDrives = new List(); - unsortedDrives.Add(drives.Single(x => x is DriveItem o && o.DeviceID == "network-folder")); + var unsortedDrives = new List + { + drives.Single(x => x is DriveItem o && o.DeviceID == "network-folder") + }; await foreach (ILocatableFolder item in networkDrivesService.GetDrivesAsync()) { unsortedDrives.Add(item); diff --git a/src/Files.App/Data/Models/RemovableDevice.cs b/src/Files.App/Data/Models/RemovableDevice.cs index 26666b047661..cd24e822e5ab 100644 --- a/src/Files.App/Data/Models/RemovableDevice.cs +++ b/src/Files.App/Data/Models/RemovableDevice.cs @@ -77,9 +77,7 @@ private bool DismountVolume() private bool PreventRemovalOfVolume(bool prevent) { - byte[] buf = new byte[1]; - buf[0] = prevent ? (byte)1 : (byte)0; - + byte[] buf = [prevent ? (byte)1 : (byte)0]; return DeviceIoControl(handle, IOCTL_STORAGE_MEDIA_REMOVAL, buf, 1, nint.Zero, 0, out _, nint.Zero); } diff --git a/src/Files.App/Data/Models/SelectedItemsPropertiesViewModel.cs b/src/Files.App/Data/Models/SelectedItemsPropertiesViewModel.cs index 7ae6ba094138..35ee474cd5af 100644 --- a/src/Files.App/Data/Models/SelectedItemsPropertiesViewModel.cs +++ b/src/Files.App/Data/Models/SelectedItemsPropertiesViewModel.cs @@ -649,14 +649,14 @@ public Uri FolderIconSource } } - private ObservableCollection propertySections = new(); + private ObservableCollection propertySections = []; public ObservableCollection PropertySections { get => propertySections; set => SetProperty(ref propertySections, value); } - private ObservableCollection fileProperties = new(); + private ObservableCollection fileProperties = []; public ObservableCollection FileProperties { get => fileProperties; diff --git a/src/Files.App/Dialogs/CreateArchiveDialog.xaml.cs b/src/Files.App/Dialogs/CreateArchiveDialog.xaml.cs index 7810ac9d71a9..ded31c49b2fc 100644 --- a/src/Files.App/Dialogs/CreateArchiveDialog.xaml.cs +++ b/src/Files.App/Dialogs/CreateArchiveDialog.xaml.cs @@ -165,24 +165,24 @@ public string Password } } - public ImmutableList FileFormats { get; } = new List - { + public ImmutableList FileFormats { get; } = + [ new(ArchiveFormats.Zip, ".zip"), new(ArchiveFormats.SevenZip, ".7z"), - }.ToImmutableList(); + ]; - public ImmutableList CompressionLevels { get; } = new List - { + public ImmutableList CompressionLevels { get; } = + [ new CompressionLevelItem(ArchiveCompressionLevels.Ultra, "CompressionLevelUltra".GetLocalizedResource()), new CompressionLevelItem(ArchiveCompressionLevels.High, "CompressionLevelHigh".GetLocalizedResource()), new CompressionLevelItem(ArchiveCompressionLevels.Normal, "CompressionLevelNormal".GetLocalizedResource()), new CompressionLevelItem(ArchiveCompressionLevels.Low, "CompressionLevelLow".GetLocalizedResource()), new CompressionLevelItem(ArchiveCompressionLevels.Fast, "CompressionLevelFast".GetLocalizedResource()), new CompressionLevelItem(ArchiveCompressionLevels.None, "CompressionLevelNone".GetLocalizedResource()), - }.ToImmutableList(); + ]; - public ImmutableList SplittingSizes { get; } = new List - { + public ImmutableList SplittingSizes { get; } = + [ new(ArchiveSplittingSizes.None, "Do not split".GetLocalizedResource()), new(ArchiveSplittingSizes.Mo10, ToSizeText(10)), new(ArchiveSplittingSizes.Mo100, ToSizeText(100)), @@ -195,7 +195,7 @@ public string Password new(ArchiveSplittingSizes.Mo5120, ToSizeText(5120)), new(ArchiveSplittingSizes.Dvd8128, ToSizeText(8128), "DVD".GetLocalizedResource()), new(ArchiveSplittingSizes.Bd23040, ToSizeText(23040), "Bluray".GetLocalizedResource()), - }.ToImmutableList(); + ]; public DialogViewModel() { diff --git a/src/Files.App/Helpers/Navigation/NavigationInteractionTracker.cs b/src/Files.App/Helpers/Navigation/NavigationInteractionTracker.cs index 360873181a65..ab2eaa4a65c6 100644 --- a/src/Files.App/Helpers/Navigation/NavigationInteractionTracker.cs +++ b/src/Files.App/Helpers/Navigation/NavigationInteractionTracker.cs @@ -115,7 +115,7 @@ private void SetupAnimations() var backResistance = CreateResistanceCondition(-96f, 0f); var forwardResistance = CreateResistanceCondition(0f, 96f); - List conditionalValues = new() { backResistance, forwardResistance }; + List conditionalValues = [backResistance, forwardResistance]; _source.ConfigureDeltaPositionXModifiers(conditionalValues); var backAnim = compositor.CreateExpressionAnimation("(-clamp(tracker.Position.X, -96, 0) * 2) - 48"); diff --git a/src/Files.App/Helpers/ShareItemHelpers.cs b/src/Files.App/Helpers/ShareItemHelpers.cs index b69a739985d2..c67e2a8b9ba4 100644 --- a/src/Files.App/Helpers/ShareItemHelpers.cs +++ b/src/Files.App/Helpers/ShareItemHelpers.cs @@ -54,7 +54,7 @@ public static async Task ShareItemsAsync(IEnumerable itemsToShare) async void Manager_DataRequested(DataTransferManager sender, DataRequestedEventArgs args) { DataRequestDeferral dataRequestDeferral = args.Request.GetDeferral(); - List items = new(); + List items = []; DataRequest dataRequest = args.Request; foreach (ListedItem item in itemsToShare) diff --git a/src/Files.App/Helpers/UI/UIFilesystemHelpers.cs b/src/Files.App/Helpers/UI/UIFilesystemHelpers.cs index 189d175ef5bf..4df5ad003698 100644 --- a/src/Files.App/Helpers/UI/UIFilesystemHelpers.cs +++ b/src/Files.App/Helpers/UI/UIFilesystemHelpers.cs @@ -25,7 +25,7 @@ public static async Task CutItemAsync(IShellPage associatedInstance) { RequestedOperation = DataPackageOperation.Move }; - ConcurrentBag items = new(); + ConcurrentBag items = []; if (associatedInstance.SlimContentPage.IsItemSelected) { @@ -134,7 +134,7 @@ public static async Task CopyItemAsync(IShellPage associatedInstance) { RequestedOperation = DataPackageOperation.Copy }; - ConcurrentBag items = new(); + ConcurrentBag items = []; if (associatedInstance.SlimContentPage.IsItemSelected) { diff --git a/src/Files.App/Helpers/Win32/Win32Helper.Storage.cs b/src/Files.App/Helpers/Win32/Win32Helper.Storage.cs index 2fd47d40fa07..138bae36e0f5 100644 --- a/src/Files.App/Helpers/Win32/Win32Helper.Storage.cs +++ b/src/Files.App/Helpers/Win32/Win32Helper.Storage.cs @@ -194,7 +194,7 @@ public static string ExtractStringFromDLL(string file, int number) public static string?[] CommandLineToArgs(string commandLine) { if (string.IsNullOrEmpty(commandLine)) - return Array.Empty(); + return []; var argv = Shell32.CommandLineToArgvW(commandLine, out int argc); if (argv == IntPtr.Zero) @@ -371,14 +371,14 @@ public static string ExtractStringFromDLL(string file, int number) else if (isFolder) { // Could not icon, load generic icon - var icons = ExtractSelectedIconsFromDLL(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), "imageres.dll"), new[] { 2 }, size); + var icons = ExtractSelectedIconsFromDLL(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), "imageres.dll"), [2], size); var generic = icons.SingleOrDefault(x => x.Index == 2); iconData = generic?.IconData; } else { // Could not icon, load generic icon - var icons = ExtractSelectedIconsFromDLL(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), "shell32.dll"), new[] { 1 }, size); + var icons = ExtractSelectedIconsFromDLL(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), "shell32.dll"), [1], size); var generic = icons.SingleOrDefault(x => x.Index == 1); iconData = generic?.IconData; } diff --git a/src/Files.App/Program.cs b/src/Files.App/Program.cs index 3e71ff0e7017..f37b28c6ffa3 100644 --- a/src/Files.App/Program.cs +++ b/src/Files.App/Program.cs @@ -203,7 +203,7 @@ public static void RedirectActivationTo(AppInstance keyInstance, AppActivationAr CWMO_DEFAULT, INFINITE, 1, - new IntPtr[] { eventHandle }, + [eventHandle], out uint handleIndex); } @@ -226,7 +226,7 @@ public static void OpenFileFromTile(string filePath) CWMO_DEFAULT, INFINITE, 1, - new IntPtr[] { eventHandle }, + [eventHandle], out uint handleIndex); } } diff --git a/src/Files.App/Services/JumpListService.cs b/src/Files.App/Services/JumpListService.cs index 4e1439df9d00..6357cdea2899 100644 --- a/src/Files.App/Services/JumpListService.cs +++ b/src/Files.App/Services/JumpListService.cs @@ -48,12 +48,12 @@ public async Task> GetFoldersAsync() } catch { - return Enumerable.Empty(); + return []; } } else { - return Enumerable.Empty(); + return []; } } diff --git a/src/Files.App/Services/QuickAccessService.cs b/src/Files.App/Services/QuickAccessService.cs index 51975e79c911..5d85503740e2 100644 --- a/src/Files.App/Services/QuickAccessService.cs +++ b/src/Files.App/Services/QuickAccessService.cs @@ -24,7 +24,7 @@ public async Task> GetPinnedFoldersAsync() private async Task PinToSidebarAsync(string[] folderPaths, bool doUpdateQuickAccessWidget) { foreach (string folderPath in folderPaths) - await ContextMenu.InvokeVerb("pintohome", new[] {folderPath}); + await ContextMenu.InvokeVerb("pintohome", [folderPath]); await App.QuickAccessManager.Model.LoadAsync(); if (doUpdateQuickAccessWidget) @@ -39,7 +39,7 @@ private async Task UnpinFromSidebarAsync(string[] folderPaths, bool doUpdateQuic { Type? shellAppType = Type.GetTypeFromProgID("Shell.Application"); object? shell = Activator.CreateInstance(shellAppType); - dynamic? f2 = shellAppType.InvokeMember("NameSpace", System.Reflection.BindingFlags.InvokeMethod, null, shell, new object[] { $"shell:{guid}" }); + dynamic? f2 = shellAppType.InvokeMember("NameSpace", System.Reflection.BindingFlags.InvokeMethod, null, shell, [$"shell:{guid}"]); if (folderPaths.Length == 0) folderPaths = (await GetPinnedFoldersAsync()) @@ -91,7 +91,7 @@ public async Task SaveAsync(string[] items) App.QuickAccessManager.PinnedItemsWatcher.EnableRaisingEvents = false; // Unpin every item that is below this index and then pin them all in order - await UnpinFromSidebarAsync(Array.Empty(), false); + await UnpinFromSidebarAsync([], false); await PinToSidebarAsync(items, false); App.QuickAccessManager.PinnedItemsWatcher.EnableRaisingEvents = true; diff --git a/src/Files.App/Services/Settings/FileTagsSettingsService.cs b/src/Files.App/Services/Settings/FileTagsSettingsService.cs index 67e851d44865..e49cf1035570 100644 --- a/src/Files.App/Services/Settings/FileTagsSettingsService.cs +++ b/src/Files.App/Services/Settings/FileTagsSettingsService.cs @@ -20,13 +20,13 @@ internal sealed class FileTagsSettingsService : BaseJsonSettings, IFileTagsSetti public event EventHandler OnTagsUpdated; - private static readonly List DefaultFileTags = new List() - { + private static readonly List DefaultFileTags = + [ new("Home", "#0072BD", "f7e0e137-2eb5-4fa4-a50d-ddd65df17c34"), new("Work", "#D95319", "c84a8131-c4de-47d9-9440-26e859d14b3d"), new("Photos", "#EDB120", "d4b8d4bd-ceaf-4e58-ac61-a185fcf96c5d"), new("Important", "#77AC30", "79376daf-c44a-4fe4-aa3b-8b30baea453e") - }; + ]; public FileTagsSettingsService() { diff --git a/src/Files.App/Services/Settings/GeneralSettingsService.cs b/src/Files.App/Services/Settings/GeneralSettingsService.cs index df74ed11061a..aca48cbb4837 100644 --- a/src/Files.App/Services/Settings/GeneralSettingsService.cs +++ b/src/Files.App/Services/Settings/GeneralSettingsService.cs @@ -253,7 +253,7 @@ public FileNameConflictResolveOptionType ConflictsResolveOption public Dictionary Actions { - get => Get>(null) ?? new(); + get => Get>(null) ?? []; set => Set(value); } diff --git a/src/Files.App/Services/Settings/UserSettingsService.cs b/src/Files.App/Services/Settings/UserSettingsService.cs index b378e3969d34..942f610a6381 100644 --- a/src/Files.App/Services/Settings/UserSettingsService.cs +++ b/src/Files.App/Services/Settings/UserSettingsService.cs @@ -81,9 +81,9 @@ public override bool ImportSettings(object import) { Dictionary settingsImport = import switch { - string s => JsonSettingsSerializer?.DeserializeFromJson>(s) ?? new(), + string s => JsonSettingsSerializer?.DeserializeFromJson>(s) ?? [], Dictionary d => d, - _ => new(), + _ => [], }; if (!settingsImport.IsEmpty() && base.ImportSettings(settingsImport)) diff --git a/src/Files.App/Services/UpdateService.cs b/src/Files.App/Services/UpdateService.cs index 14cf22e8eccf..129b1031302f 100644 --- a/src/Files.App/Services/UpdateService.cs +++ b/src/Files.App/Services/UpdateService.cs @@ -48,7 +48,7 @@ public bool IsAppUpdated public UpdateService() { - _updatePackages = new List(); + _updatePackages = []; } public async Task DownloadUpdatesAsync() diff --git a/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcut.cs b/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcut.cs index a1398e9b8cbd..71758ab5f46b 100644 --- a/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcut.cs +++ b/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcut.cs @@ -33,7 +33,7 @@ private void OnHotKeysChanged() if (HotKeys.IsEmpty) return; - List items = new(); + List items = []; foreach (var item in HotKeys) { diff --git a/src/Files.App/UserControls/Menus/FileTagsContextMenu.cs b/src/Files.App/UserControls/Menus/FileTagsContextMenu.cs index cd77efeb51ea..a83161f89724 100644 --- a/src/Files.App/UserControls/Menus/FileTagsContextMenu.cs +++ b/src/Files.App/UserControls/Menus/FileTagsContextMenu.cs @@ -73,7 +73,7 @@ private void RemoveFileTag(IEnumerable selectedListedItems, TagViewM { foreach (var selectedItem in selectedListedItems) { - var existingTags = selectedItem.FileTags ?? Array.Empty(); + var existingTags = selectedItem.FileTags ?? []; if (existingTags.Contains(removed.Uid)) { var tagList = existingTags.Except(new[] { removed.Uid }).ToArray(); @@ -86,10 +86,10 @@ private void AddFileTag(IEnumerable selectedListedItems, TagViewMode { foreach (var selectedItem in selectedListedItems) { - var existingTags = selectedItem.FileTags ?? Array.Empty(); + var existingTags = selectedItem.FileTags ?? []; if (!existingTags.Contains(added.Uid)) { - selectedItem.FileTags = existingTags.Append(added.Uid).ToArray(); + selectedItem.FileTags = [.. existingTags, added.Uid]; } } } diff --git a/src/Files.App/UserControls/Selection/RectangleSelection_ListViewBase.cs b/src/Files.App/UserControls/Selection/RectangleSelection_ListViewBase.cs index 2bf7076a8685..ebe1f5d3220b 100644 --- a/src/Files.App/UserControls/Selection/RectangleSelection_ListViewBase.cs +++ b/src/Files.App/UserControls/Selection/RectangleSelection_ListViewBase.cs @@ -33,7 +33,7 @@ public RectangleSelection_ListViewBase(ListViewBase uiElement, Rectangle selecti this.uiElement = uiElement; this.selectionRectangle = selectionRectangle; this.selectionChanged = selectionChanged; - itemsPosition = new Dictionary(); + itemsPosition = []; timer = DispatcherQueue.GetForCurrentThread().CreateTimer(); InitEvents(null, null); } diff --git a/src/Files.App/UserControls/SideBar/SidebarViewAutomationPeer.cs b/src/Files.App/UserControls/SideBar/SidebarViewAutomationPeer.cs index 5ed23886cbed..9cdb719c124c 100644 --- a/src/Files.App/UserControls/SideBar/SidebarViewAutomationPeer.cs +++ b/src/Files.App/UserControls/SideBar/SidebarViewAutomationPeer.cs @@ -30,11 +30,11 @@ protected override object GetPatternCore(PatternInterface patternInterface) public IRawElementProviderSimple[] GetSelection() { if (Owner.SelectedItemContainer != null) - return new IRawElementProviderSimple[] - { + return + [ ProviderFromPeer(CreatePeerForElement(Owner.SelectedItemContainer)) - }; - return Array.Empty(); + ]; + return []; } } } diff --git a/src/Files.App/UserControls/TabBar/BaseTabBar.cs b/src/Files.App/UserControls/TabBar/BaseTabBar.cs index 365adf3a3db2..58266b380aab 100644 --- a/src/Files.App/UserControls/TabBar/BaseTabBar.cs +++ b/src/Files.App/UserControls/TabBar/BaseTabBar.cs @@ -142,10 +142,10 @@ public void CloseTab(TabBarItem tabItem) tabItem.Unload(); // Dispose and save tab arguments - PushRecentTab(new CustomTabViewItemParameter[] - { + PushRecentTab( + [ tabItem.NavigationParameter, - }); + ]); // Save the updated tab list AppLifecycleHelper.SaveSessionTabs(); diff --git a/src/Files.App/UserControls/Widgets/DrivesWidget.xaml.cs b/src/Files.App/UserControls/Widgets/DrivesWidget.xaml.cs index 9f9a3cff30e6..56cce2566a8f 100644 --- a/src/Files.App/UserControls/Widgets/DrivesWidget.xaml.cs +++ b/src/Files.App/UserControls/Widgets/DrivesWidget.xaml.cs @@ -30,7 +30,7 @@ public sealed partial class DrivesWidget : BaseWidgetViewModel, IWidgetViewModel public delegate void DrivesWidgetNewPaneInvokedEventHandler(object sender, DrivesWidgetInvokedEventArgs e); public event DrivesWidgetNewPaneInvokedEventHandler DrivesWidgetNewPaneInvoked; public event PropertyChangedEventHandler? PropertyChanged; - public static ObservableCollection ItemsAdded = new(); + public static ObservableCollection ItemsAdded = []; private IShellPage associatedInstance; @@ -212,7 +212,7 @@ public override List GetItemMenuItems(WidgetCard { Text = "Loading".GetLocalizedResource(), Glyph = "\xE712", - Items = new List(), + Items = [], ID = "ItemOverflow", Tag = "ItemOverflow", IsEnabled = false, diff --git a/src/Files.App/UserControls/Widgets/FileTagsWidget.xaml.cs b/src/Files.App/UserControls/Widgets/FileTagsWidget.xaml.cs index da7035461d5a..f7c719d73663 100644 --- a/src/Files.App/UserControls/Widgets/FileTagsWidget.xaml.cs +++ b/src/Files.App/UserControls/Widgets/FileTagsWidget.xaml.cs @@ -240,7 +240,7 @@ public override List GetItemMenuItems(WidgetCard { Text = "Loading".GetLocalizedResource(), Glyph = "\xE712", - Items = new List(), + Items = [], ID = "ItemOverflow", Tag = "ItemOverflow", IsEnabled = false, diff --git a/src/Files.App/UserControls/Widgets/QuickAccessWidget.xaml.cs b/src/Files.App/UserControls/Widgets/QuickAccessWidget.xaml.cs index a6d93e9b937d..e3f9b4736895 100644 --- a/src/Files.App/UserControls/Widgets/QuickAccessWidget.xaml.cs +++ b/src/Files.App/UserControls/Widgets/QuickAccessWidget.xaml.cs @@ -23,7 +23,7 @@ public sealed partial class QuickAccessWidget : BaseWidgetViewModel, IWidgetView public IUserSettingsService userSettingsService { get; } = Ioc.Default.GetRequiredService(); private IHomePageContext HomePageContext { get; } = Ioc.Default.GetRequiredService(); - public static ObservableCollection ItemsAdded = new(); + public static ObservableCollection ItemsAdded = []; static QuickAccessWidget() { @@ -139,7 +139,7 @@ public override List GetItemMenuItems(WidgetCard { Text = "Loading".GetLocalizedResource(), Glyph = "\xE712", - Items = new List(), + Items = [], ID = "ItemOverflow", Tag = "ItemOverflow", IsEnabled = false, diff --git a/src/Files.App/UserControls/Widgets/RecentFilesWidget.xaml.cs b/src/Files.App/UserControls/Widgets/RecentFilesWidget.xaml.cs index fe1fb20aac39..ffb4bdcbde5e 100644 --- a/src/Files.App/UserControls/Widgets/RecentFilesWidget.xaml.cs +++ b/src/Files.App/UserControls/Widgets/RecentFilesWidget.xaml.cs @@ -30,7 +30,7 @@ public sealed partial class RecentFilesWidget : BaseWidgetViewModel, IWidgetView public event RecentFileInvokedEventHandler RecentFileInvoked; public event PropertyChangedEventHandler PropertyChanged; - private ObservableCollection recentItemsCollection = new ObservableCollection(); + private ObservableCollection recentItemsCollection = []; private SemaphoreSlim refreshRecentsSemaphore; @@ -205,7 +205,7 @@ public override List GetItemMenuItems(WidgetCard { Text = "Loading".GetLocalizedResource(), Glyph = "\xE712", - Items = new List(), + Items = [], ID = "ItemOverflow", Tag = "ItemOverflow", IsEnabled = false, diff --git a/src/Files.App/Utils/Archives/CompressArchiveModel.cs b/src/Files.App/Utils/Archives/CompressArchiveModel.cs index 6f06ea08c039..ef89b15644ea 100644 --- a/src/Files.App/Utils/Archives/CompressArchiveModel.cs +++ b/src/Files.App/Utils/Archives/CompressArchiveModel.cs @@ -163,7 +163,7 @@ public async Task RunCreationAsync() var files = sources.Where(File.Exists).ToArray(); var directories = sources.Where(SystemIO.Directory.Exists); - _sizeCalculator = new FileSizeCalculator(files.Concat(directories).ToArray()); + _sizeCalculator = new FileSizeCalculator([.. files, .. directories]); var sizeTask = _sizeCalculator.ComputeSizeAsync(cts.Token); _ = sizeTask.ContinueWith(_ => { diff --git a/src/Files.App/Utils/Cloud/CloudDrivesDetector.cs b/src/Files.App/Utils/Cloud/CloudDrivesDetector.cs index 915b54de34c5..1f2f8ed9ec79 100644 --- a/src/Files.App/Utils/Cloud/CloudDrivesDetector.cs +++ b/src/Files.App/Utils/Cloud/CloudDrivesDetector.cs @@ -60,7 +60,7 @@ private static Task> DetectGenericCloudDrive() using var clsidKey = Registry.ClassesRoot.OpenSubKey(@"CLSID"); using var namespaceKey = Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Desktop\NameSpace"); - foreach (var subKeyName in namespaceKey?.GetSubKeyNames() ?? Array.Empty()) + foreach (var subKeyName in namespaceKey?.GetSubKeyNames() ?? []) { using var clsidSubKey = SafetyExtensions.IgnoreExceptions(() => clsidKey.OpenSubKey(subKeyName)); if (clsidSubKey is not null && (int?)clsidSubKey.GetValue("System.IsPinnedToNameSpaceTree") is 1) diff --git a/src/Files.App/Utils/Cloud/CloudDrivesManager.cs b/src/Files.App/Utils/Cloud/CloudDrivesManager.cs index ecda4aecd564..c4eec511b3d9 100644 --- a/src/Files.App/Utils/Cloud/CloudDrivesManager.cs +++ b/src/Files.App/Utils/Cloud/CloudDrivesManager.cs @@ -15,7 +15,7 @@ public static class CloudDrivesManager public static EventHandler DataChanged; - private static readonly List _Drives = new(); + private static readonly List _Drives = []; public static IReadOnlyList Drives { get diff --git a/src/Files.App/Utils/CommandLine/CommandLineParser.cs b/src/Files.App/Utils/CommandLine/CommandLineParser.cs index edfd19a02de6..8dedecacf741 100644 --- a/src/Files.App/Utils/CommandLine/CommandLineParser.cs +++ b/src/Files.App/Utils/CommandLine/CommandLineParser.cs @@ -164,7 +164,7 @@ public static string[] SplitArguments(string commandLine, bool trimQuotes = fals } if (parsedArgs.Count == 0 && args.Length >= 2) - parsedArgs.Add(new KeyValuePair("Cmdless", new[] { string.Join(' ', args.Skip(1)).TrimStart() })); + parsedArgs.Add(new KeyValuePair("Cmdless", [string.Join(' ', args.Skip(1)).TrimStart()])); return parsedArgs; } @@ -212,7 +212,7 @@ public static string[] SplitArguments(string commandLine, bool trimQuotes = fals return key is not null - ? new KeyValuePair(key, val.ToArray()) + ? new KeyValuePair(key, [.. val]) : default; } } diff --git a/src/Files.App/Utils/CommandLine/ParsedCommand.cs b/src/Files.App/Utils/CommandLine/ParsedCommand.cs index e9a39040fd01..df85b472bcd4 100644 --- a/src/Files.App/Utils/CommandLine/ParsedCommand.cs +++ b/src/Files.App/Utils/CommandLine/ParsedCommand.cs @@ -29,7 +29,7 @@ public string Payload /// public ParsedCommand() { - Args = new(); + Args = []; } } } diff --git a/src/Files.App/Utils/FileTags/FileTagsHelper.cs b/src/Files.App/Utils/FileTags/FileTagsHelper.cs index b1eb0d4cd60d..4149057dfe0a 100644 --- a/src/Files.App/Utils/FileTags/FileTagsHelper.cs +++ b/src/Files.App/Utils/FileTags/FileTagsHelper.cs @@ -19,7 +19,7 @@ public static class FileTagsHelper public static string[] ReadFileTag(string filePath) { var tagString = NativeFileOperationsHelper.ReadStringFromFile($"{filePath}:files"); - return tagString?.Split(',', StringSplitOptions.RemoveEmptyEntries); + return tagString?.Split(',', StringSplitOptions.RemoveEmptyEntries) ?? []; } public static async void WriteFileTag(string filePath, string[] tag) @@ -30,7 +30,7 @@ public static async void WriteFileTag(string filePath, string[] tag) { NativeFileOperationsHelper.UnsetFileAttribute(filePath, IO.FileAttributes.ReadOnly); } - if (tag is null || !tag.Any()) + if (!tag.Any()) { PInvoke.DeleteFileFromApp($"{filePath}:files"); } @@ -118,7 +118,7 @@ public static void UpdateTagsDb() static async Task GetFileFRN(IStorageItemExtraProperties properties) { - var extra = await properties.RetrievePropertiesAsync(new string[] { "System.FileFRN" }); + var extra = await properties.RetrievePropertiesAsync(["System.FileFRN"]); return (ulong?)extra["System.FileFRN"]; } } diff --git a/src/Files.App/Utils/FileTags/FileTagsManager.cs b/src/Files.App/Utils/FileTags/FileTagsManager.cs index 1c2051b37715..f3c6cc502818 100644 --- a/src/Files.App/Utils/FileTags/FileTagsManager.cs +++ b/src/Files.App/Utils/FileTags/FileTagsManager.cs @@ -13,7 +13,7 @@ public sealed class FileTagsManager public EventHandler DataChanged; - private readonly List fileTags = new(); + private readonly List fileTags = []; public IReadOnlyList FileTags { get diff --git a/src/Files.App/Utils/Git/GitHelpers.cs b/src/Files.App/Utils/Git/GitHelpers.cs index 3a11b061b671..79a4d90788d3 100644 --- a/src/Files.App/Utils/Git/GitHelpers.cs +++ b/src/Files.App/Utils/Git/GitHelpers.cs @@ -113,7 +113,7 @@ public static string GetOriginRepositoryName(string? path) public static async Task GetBranchesNames(string? path) { if (string.IsNullOrWhiteSpace(path) || !Repository.IsValid(path)) - return Array.Empty(); + return []; var (result, returnValue) = await DoGitOperationAsync<(GitOperationResult, BranchItem[])>(() => { diff --git a/src/Files.App/Utils/Global/WSLDistroManager.cs b/src/Files.App/Utils/Global/WSLDistroManager.cs index 28e6d3923c5c..066478174bfb 100644 --- a/src/Files.App/Utils/Global/WSLDistroManager.cs +++ b/src/Files.App/Utils/Global/WSLDistroManager.cs @@ -12,7 +12,7 @@ public static class WSLDistroManager { public static EventHandler DataChanged; - private static readonly List distros = new(); + private static readonly List distros = []; public static IReadOnlyList Distros { get diff --git a/src/Files.App/Utils/Global/WallpaperHelpers.cs b/src/Files.App/Utils/Global/WallpaperHelpers.cs index 100cb690e301..0ad655761166 100644 --- a/src/Files.App/Utils/Global/WallpaperHelpers.cs +++ b/src/Files.App/Utils/Global/WallpaperHelpers.cs @@ -43,7 +43,7 @@ public static void SetSlideshow(string[] filePaths) try { var idList = filePaths.Select(Shell32.IntILCreateFromPath).ToArray(); - Shell32.SHCreateShellItemArrayFromIDLists((uint)idList.Length, idList.ToArray(), out var shellItemArray); + Shell32.SHCreateShellItemArrayFromIDLists((uint)idList.Length, [.. idList], out var shellItemArray); // Set SlideShow var wallpaper = (Shell32.IDesktopWallpaper)new Shell32.DesktopWallpaper(); diff --git a/src/Files.App/Utils/Library/LibraryManager.cs b/src/Files.App/Utils/Library/LibraryManager.cs index 0c2be4f539c0..772c6479d1c3 100644 --- a/src/Files.App/Utils/Library/LibraryManager.cs +++ b/src/Files.App/Utils/Library/LibraryManager.cs @@ -19,7 +19,7 @@ public sealed class LibraryManager : IDisposable public EventHandler? DataChanged; private FileSystemWatcher librariesWatcher; - private readonly List libraries = new(); + private readonly List libraries = []; private static readonly Lazy lazy = new(() => new LibraryManager()); public static LibraryManager Default @@ -90,7 +90,7 @@ public static async Task> ListUserLibraries() App.Logger.LogWarning(e, null); } - return new(); + return []; }); return libraries.Select(lib => new LibraryLocationItem(lib)).ToList(); diff --git a/src/Files.App/Utils/RecentItem/RecentItems.cs b/src/Files.App/Utils/RecentItem/RecentItems.cs index fc21174f3083..911342f780a6 100644 --- a/src/Files.App/Utils/RecentItem/RecentItems.cs +++ b/src/Files.App/Utils/RecentItem/RecentItems.cs @@ -18,7 +18,7 @@ public sealed class RecentItems : IDisposable public EventHandler? RecentFoldersChanged; // recent files - private readonly List recentFiles = new(); + private readonly List recentFiles = []; public IReadOnlyList RecentFiles // already sorted { get @@ -31,7 +31,7 @@ public sealed class RecentItems : IDisposable } // recent folders - private readonly List recentFolders = new(); + private readonly List recentFolders = []; public IReadOnlyList RecentFolders // already sorted { get diff --git a/src/Files.App/Utils/RecycleBin/RecycleBinManager.cs b/src/Files.App/Utils/RecycleBin/RecycleBinManager.cs index a7ed5424582e..1a0548d6e2a1 100644 --- a/src/Files.App/Utils/RecycleBin/RecycleBinManager.cs +++ b/src/Files.App/Utils/RecycleBin/RecycleBinManager.cs @@ -40,7 +40,7 @@ private void StartRecycleBinWatcher() { // NOTE: SHChangeNotifyRegister only works if recycle bin is open in explorer // Create file system watcher to monitor recycle bin folder(s) - binWatchers = new List(); + binWatchers = []; var sid = WindowsIdentity.GetCurrent().User.ToString(); diff --git a/src/Files.App/Utils/Shell/ContextMenu.cs b/src/Files.App/Utils/Shell/ContextMenu.cs index 232c950b3349..788d5fe44841 100644 --- a/src/Files.App/Utils/Shell/ContextMenu.cs +++ b/src/Files.App/Utils/Shell/ContextMenu.cs @@ -35,10 +35,10 @@ private ContextMenu(Shell32.IContextMenu cMenu, User32.SafeHMENU hMenu, IEnumera _hMenu = hMenu; _owningThread = owningThread; _itemFilter = itemFilter; - _loadSubMenuActions = new(); + _loadSubMenuActions = []; ItemsPath = itemsPath.ToList(); - Items = new(); + Items = []; } public async static Task InvokeVerb(string verb, params string[] filePaths) @@ -125,7 +125,7 @@ public async Task InvokeItem(int itemID) foreach (var filePathItem in filePathList.Where(x => !string.IsNullOrEmpty(x))) shellItems.Add(ShellFolderExtensions.GetShellItemFromPathOrPIDL(filePathItem)); - return GetContextMenuForFiles(shellItems.ToArray(), flags, owningThread, itemFilter); + return GetContextMenuForFiles([.. shellItems], flags, owningThread, itemFilter); } catch { diff --git a/src/Files.App/Utils/Shell/ShellNewMenuHelper.cs b/src/Files.App/Utils/Shell/ShellNewMenuHelper.cs index 655f9584c15a..1e1673a344c9 100644 --- a/src/Files.App/Utils/Shell/ShellNewMenuHelper.cs +++ b/src/Files.App/Utils/Shell/ShellNewMenuHelper.cs @@ -35,7 +35,7 @@ public static async Task> GetNewContextMenuEntries() if (!newMenuItems.Any(x => ".txt".Equals(x.Extension, StringComparison.OrdinalIgnoreCase))) newMenuItems.Add(await CreateShellNewEntry(".txt", null, null, null)); - return newMenuItems.OrderBy(item => item.Name).ToList(); + return [.. newMenuItems.OrderBy(item => item.Name)]; } public static async Task GetNewContextMenuEntryForType(string extension) diff --git a/src/Files.App/Utils/StatusCenter/StatusCenterItem.cs b/src/Files.App/Utils/StatusCenter/StatusCenterItem.cs index a33ed7b101c9..ee5c871942a3 100644 --- a/src/Files.App/Utils/StatusCenter/StatusCenterItem.cs +++ b/src/Files.App/Utils/StatusCenter/StatusCenterItem.cs @@ -217,8 +217,8 @@ public CancellationToken CancellationToken TotalSize = totalSize; IconBackgroundCircleBorderOpacity = 1; AnimatedIconState = "NormalOff"; - SpeedGraphValues = new(); - SpeedGraphBackgroundValues = new(); + SpeedGraphValues = []; + SpeedGraphBackgroundValues = []; CancelCommand = new RelayCommand(ExecuteCancelCommand); Message = "ProcessingItems".GetLocalizedResource(); Source = source; @@ -229,8 +229,8 @@ public CancellationToken CancellationToken return; // Initialize graph background fill series - SpeedGraphBackgroundSeries = new() - { + SpeedGraphBackgroundSeries = + [ new LineSeries { Values = SpeedGraphBackgroundValues, @@ -245,17 +245,17 @@ public CancellationToken CancellationToken // Fill under the stroke Fill = new LinearGradientPaint( - new SKColor[] { + [ new(accentBrush.Color.R, accentBrush.Color.G, accentBrush.Color.B, 40) - }, + ], new(0f, 0f), new(0f, 0f)), } - }; + ]; // Initialize graph series - SpeedGraphSeries = new() - { + SpeedGraphSeries = + [ new LineSeries { Values = SpeedGraphValues, @@ -270,19 +270,19 @@ public CancellationToken CancellationToken // Fill under the stroke Fill = new LinearGradientPaint( - new SKColor[] { + [ new(accentBrush.Color.R, accentBrush.Color.G, accentBrush.Color.B, 50), new(accentBrush.Color.R, accentBrush.Color.G, accentBrush.Color.B, 10) - }, + ], new(0f, 0f), new(0f, 0f), - new[] { 0.1f, 1.0f }), + [0.1f, 1.0f]), }, - }; + ]; // Initialize X axes of the graph background fill - SpeedGraphBackgroundXAxes = new() - { + SpeedGraphBackgroundXAxes = + [ new Axis { Padding = new Padding(0, 0), @@ -290,11 +290,11 @@ public CancellationToken CancellationToken MaxLimit = 100, ShowSeparatorLines = false, } - }; + ]; // Initialize X axes of the graph - SpeedGraphXAxes = new() - { + SpeedGraphXAxes = + [ new Axis { Padding = new Padding(0, 0), @@ -302,11 +302,11 @@ public CancellationToken CancellationToken MaxLimit = 100, ShowSeparatorLines = false, } - }; + ]; // Initialize Y axes of the graph background fill - SpeedGraphBackgroundYAxes = new() - { + SpeedGraphBackgroundYAxes = + [ new Axis { Padding = new Padding(0, 0), @@ -314,18 +314,18 @@ public CancellationToken CancellationToken ShowSeparatorLines = false, MaxLimit = 100, } - }; + ]; // Initialize Y axes of the graph - SpeedGraphYAxes = new() - { + SpeedGraphYAxes = + [ new Axis { Padding = new Padding(0, 0), Labels = new List(), ShowSeparatorLines = false, } - }; + ]; SpeedGraphXAxes[0].SharedWith = SpeedGraphBackgroundXAxes; SpeedGraphBackgroundXAxes[0].SharedWith = SpeedGraphXAxes; diff --git a/src/Files.App/Utils/Storage/Collection/BulkConcurrentObservableCollection.cs b/src/Files.App/Utils/Storage/Collection/BulkConcurrentObservableCollection.cs index 47dadb7cf226..4bba7f55eccd 100644 --- a/src/Files.App/Utils/Storage/Collection/BulkConcurrentObservableCollection.cs +++ b/src/Files.App/Utils/Storage/Collection/BulkConcurrentObservableCollection.cs @@ -11,7 +11,7 @@ public class BulkConcurrentObservableCollection : INotifyCollectionChanged, I { protected bool isBulkOperationStarted; private readonly object syncRoot = new object(); - private readonly List collection = new List(); + private readonly List collection = []; // When 'GroupOption' is set to 'None' or when a folder is opened, 'GroupedCollection' is assigned 'null' by 'ItemGroupKeySelector' public BulkConcurrentObservableCollection>? GroupedCollection { get; private set; } @@ -90,7 +90,7 @@ public int Count { itemGroupKeySelector = value; if (value is not null) - GroupedCollection ??= new BulkConcurrentObservableCollection>(); + GroupedCollection ??= []; else GroupedCollection = null; } diff --git a/src/Files.App/Utils/Storage/Collection/ConcurrentCollection.cs b/src/Files.App/Utils/Storage/Collection/ConcurrentCollection.cs index f16ce899f590..7ba91e7d946d 100644 --- a/src/Files.App/Utils/Storage/Collection/ConcurrentCollection.cs +++ b/src/Files.App/Utils/Storage/Collection/ConcurrentCollection.cs @@ -9,7 +9,7 @@ public sealed class ConcurrentCollection : ICollection, IList, ICollect { private readonly object syncRoot = new object(); - private readonly List collection = new List(); + private readonly List collection = []; public int Count { diff --git a/src/Files.App/Utils/Storage/Collection/GroupedHeaderViewModel.cs b/src/Files.App/Utils/Storage/Collection/GroupedHeaderViewModel.cs index 816cd4a8d344..fe069cac1f39 100644 --- a/src/Files.App/Utils/Storage/Collection/GroupedHeaderViewModel.cs +++ b/src/Files.App/Utils/Storage/Collection/GroupedHeaderViewModel.cs @@ -103,7 +103,7 @@ public void ResumePropertyChangedNotifications(bool triggerUpdates = true) } } - private List changedPropQueue = new List(); + private List changedPropQueue = []; // This is true by default to make it easier to initialize groups from a different thread private bool deferPropChangedNotifs = true; diff --git a/src/Files.App/Utils/Storage/Collection/GroupingHelper.cs b/src/Files.App/Utils/Storage/Collection/GroupingHelper.cs index adec6f08d825..f59fb7e2537e 100644 --- a/src/Files.App/Utils/Storage/Collection/GroupingHelper.cs +++ b/src/Files.App/Utils/Storage/Collection/GroupingHelper.cs @@ -158,14 +158,14 @@ public static string GetGroupSizeKey(long size) return "0"; } - private static readonly (long size, string text, string sizeText)[] sizeGroups = new (long, string, string)[] - { + private static readonly (long size, string text, string sizeText)[] sizeGroups = + [ (5000000000, "ItemSizeText_Huge".GetLocalizedResource(), "5 GiB".ConvertSizeAbbreviation()), (1000000000, "ItemSizeText_VeryLarge".GetLocalizedResource(), "1 GiB".ConvertSizeAbbreviation()), (128000000, "ItemSizeText_Large".GetLocalizedResource(), "128 MiB".ConvertSizeAbbreviation()), (1000000, "ItemSizeText_Medium".GetLocalizedResource(), "1 MiB".ConvertSizeAbbreviation()), (16000, "ItemSizeText_Small".GetLocalizedResource(), "16 KiB".ConvertSizeAbbreviation()), - }; + ]; private static string GetFolderName(string path) { diff --git a/src/Files.App/Utils/Storage/Helpers/FilePropertiesHelpers.cs b/src/Files.App/Utils/Storage/Helpers/FilePropertiesHelpers.cs index bc88e1a63956..684a430d3d5e 100644 --- a/src/Files.App/Utils/Storage/Helpers/FilePropertiesHelpers.cs +++ b/src/Files.App/Utils/Storage/Helpers/FilePropertiesHelpers.cs @@ -34,7 +34,7 @@ public static nint GetWindowHandle(Window w) => WinRT.Interop.WindowNative.GetWindowHandle(w); private static TaskCompletionSource? PropertiesWindowsClosingTCS; - private static BlockingCollection WindowCache = new(); + private static BlockingCollection WindowCache = []; /// /// Open properties window diff --git a/src/Files.App/Utils/Storage/Helpers/FileSecurityHelpers.cs b/src/Files.App/Utils/Storage/Helpers/FileSecurityHelpers.cs index 4aab6345bc2d..bbba2f4b4ce7 100644 --- a/src/Files.App/Utils/Storage/Helpers/FileSecurityHelpers.cs +++ b/src/Files.App/Utils/Storage/Helpers/FileSecurityHelpers.cs @@ -98,7 +98,7 @@ public static Win32Error GetAccessControlList(string path, bool isFolder, out Ac var isValidAcl = IsValidAcl(pDacl); - List aces = new(); + List aces = []; // Get ACEs for (uint i = 0; i < aclSize.AceCount; i++) @@ -196,7 +196,7 @@ public static Win32Error AddAccessControlEntry(string szPath, string szSid) }; // Add an new ACE and get a new ACL - result = SetEntriesInAcl(1, new[] { explicitAccess }, pDACL, out var pNewDACL); + result = SetEntriesInAcl(1, [explicitAccess], pDACL, out var pNewDACL); if (result.Failed) return result; diff --git a/src/Files.App/Utils/Storage/Helpers/FolderHelpers.cs b/src/Files.App/Utils/Storage/Helpers/FolderHelpers.cs index a10670497dab..2b98c3a28206 100644 --- a/src/Files.App/Utils/Storage/Helpers/FolderHelpers.cs +++ b/src/Files.App/Utils/Storage/Helpers/FolderHelpers.cs @@ -29,7 +29,7 @@ public static async Task CheckBitlockerStatusAsync(BaseStorageFolder rootF if (Path.IsPathRooted(path) && Path.GetPathRoot(path) == path) { IDictionary extraProperties = - await rootFolder.Properties.RetrievePropertiesAsync(new string[] { "System.Volume.BitLockerProtection" }); + await rootFolder.Properties.RetrievePropertiesAsync(["System.Volume.BitLockerProtection"]); return (int?)extraProperties["System.Volume.BitLockerProtection"] == 6; // Drive is bitlocker protected and locked } return false; diff --git a/src/Files.App/Utils/Storage/Helpers/StorageFileExtensions.cs b/src/Files.App/Utils/Storage/Helpers/StorageFileExtensions.cs index 0598a0e1ba43..271f3c8c512c 100644 --- a/src/Files.App/Utils/Storage/Helpers/StorageFileExtensions.cs +++ b/src/Files.App/Utils/Storage/Helpers/StorageFileExtensions.cs @@ -96,7 +96,7 @@ public static bool AreItemsAlreadyInFolder(this IEnumerable GetDirectoryPathComponents(string value) { - List pathBoxItems = new(); + List pathBoxItems = []; if (value.Contains('/', StringComparison.Ordinal)) { diff --git a/src/Files.App/Utils/Storage/History/StorageHistoryWrapper.cs b/src/Files.App/Utils/Storage/History/StorageHistoryWrapper.cs index c4a2eaef3f8c..ba846486dcc3 100644 --- a/src/Files.App/Utils/Storage/History/StorageHistoryWrapper.cs +++ b/src/Files.App/Utils/Storage/History/StorageHistoryWrapper.cs @@ -7,7 +7,7 @@ public sealed class StorageHistoryWrapper : IDisposable { private int index = -1; - private List histories = new(); + private List histories = []; public bool CanRedo() => index + 1 < histories.Count; public bool CanUndo() => index >= 0 && histories.Count > 0; diff --git a/src/Files.App/Utils/Storage/Operations/FileOperationsHelpers.cs b/src/Files.App/Utils/Storage/Operations/FileOperationsHelpers.cs index 747da26c9351..2b096c8dbc89 100644 --- a/src/Files.App/Utils/Storage/Operations/FileOperationsHelpers.cs +++ b/src/Files.App/Utils/Storage/Operations/FileOperationsHelpers.cs @@ -31,7 +31,7 @@ public static Task SetClipboard(string[] filesToCopy, DataPackageOperation opera var fileList = new System.Collections.Specialized.StringCollection(); fileList.AddRange(filesToCopy); MemoryStream dropEffect = new MemoryStream(operation == DataPackageOperation.Copy ? - new byte[] { 5, 0, 0, 0 } : new byte[] { 2, 0, 0, 0 }); + [5, 0, 0, 0] : [2, 0, 0, 0]); var data = new System.Windows.Forms.DataObject(); data.SetFileDropList(fileList); data.SetData("Preferred DropEffect", dropEffect); @@ -864,7 +864,7 @@ private static void UpdateFileTagsDb(ShellFileOperations2.ShellFileOpEventArgs e { var tag = dbInstance.GetTags(sourcePath, null); - dbInstance.SetTags(destination, FileTagsHelper.GetFileFRN(destination), tag ?? []); // copy tag to new files + dbInstance.SetTags(destination, FileTagsHelper.GetFileFRN(destination), tag); // copy tag to new files using var si = new ShellItem(destination); if (si.IsFolder) // File tag is not copied automatically for folders { diff --git a/src/Files.App/Utils/Storage/Operations/FilesystemHelpers.cs b/src/Files.App/Utils/Storage/Operations/FilesystemHelpers.cs index fca8d72dee6d..386620fe8e06 100644 --- a/src/Files.App/Utils/Storage/Operations/FilesystemHelpers.cs +++ b/src/Files.App/Utils/Storage/Operations/FilesystemHelpers.cs @@ -35,13 +35,13 @@ private static char[] RestrictedCharacters { var userSettingsService = Ioc.Default.GetRequiredService(); return userSettingsService.FoldersSettingsService.AreAlternateStreamsVisible - ? new[] { '\\', '/', '*', '?', '"', '<', '>', '|' } // Allow ":" char - : new[] { '\\', '/', ':', '*', '?', '"', '<', '>', '|' }; + ? ['\\', '/', '*', '?', '"', '<', '>', '|'] // Allow ":" char + : ['\\', '/', ':', '*', '?', '"', '<', '>', '|']; } } - private static readonly string[] RestrictedFileNames = new string[] - { + private static readonly string[] RestrictedFileNames = + [ "CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", @@ -49,7 +49,7 @@ private static char[] RestrictedCharacters "COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9" - }; + ]; private IUserSettingsService UserSettingsService { get; } = Ioc.Default.GetRequiredService(); public FilesystemHelpers(IShellPage associatedInstance, CancellationToken cancellationToken) @@ -125,7 +125,7 @@ public async Task DeleteItemsAsync(IEnumerable(); @@ -699,7 +699,7 @@ await Ioc.Default.GetRequiredService().TryGetFileAsync(item. { if (result != DialogResult.Primary) // Operation was cancelled { - return (new(), true, itemsResult); + return ([], true, itemsResult); } } diff --git a/src/Files.App/Utils/Storage/Operations/ShellFilesystemOperations.cs b/src/Files.App/Utils/Storage/Operations/ShellFilesystemOperations.cs index a28dcff2ada1..aab8616c71eb 100644 --- a/src/Files.App/Utils/Storage/Operations/ShellFilesystemOperations.cs +++ b/src/Files.App/Utils/Storage/Operations/ShellFilesystemOperations.cs @@ -737,7 +737,7 @@ public async Task RestoreItemsFromTrashAsync(IList s.Path).ToArray(), destination.ToArray(), false, MainWindow.Instance.WindowHandle.ToInt64(), asAdmin, progress, operationID); + var (status, response) = await FileOperationsHelpers.MoveItemAsync(source.Select(s => s.Path).ToArray(), [.. destination], false, MainWindow.Instance.WindowHandle.ToInt64(), asAdmin, progress, operationID); var result = (FilesystemResult)status; moveResult.Items.AddRange(response?.Final ?? Enumerable.Empty()); diff --git a/src/Files.App/Utils/Storage/Search/FolderSearch.cs b/src/Files.App/Utils/Storage/Search/FolderSearch.cs index eedee3b49c5a..229230dfd4be 100644 --- a/src/Files.App/Utils/Storage/Search/FolderSearch.cs +++ b/src/Files.App/Utils/Storage/Search/FolderSearch.cs @@ -110,7 +110,7 @@ private async Task AddItemsForHomeAsync(IList results, CancellationT public async Task> SearchAsync() { - ObservableCollection results = new ObservableCollection(); + ObservableCollection results = []; try { var token = CancellationToken.None; diff --git a/src/Files.App/Utils/Storage/Security/AccessControlList.cs b/src/Files.App/Utils/Storage/Security/AccessControlList.cs index 03b35e14117c..7506293da517 100644 --- a/src/Files.App/Utils/Storage/Security/AccessControlList.cs +++ b/src/Files.App/Utils/Storage/Security/AccessControlList.cs @@ -40,14 +40,14 @@ public AccessControlList(string path, bool isFolder, Principal owner, bool isVal IsFolder = isFolder; Owner = owner; IsValid = isValid; - AccessControlEntries = new(); + AccessControlEntries = []; } public AccessControlList() { Path = string.Empty; Owner = new(string.Empty); - AccessControlEntries = new(); + AccessControlEntries = []; } } } diff --git a/src/Files.App/ViewModels/Dialogs/AddBranchDialogViewModel.cs b/src/Files.App/ViewModels/Dialogs/AddBranchDialogViewModel.cs index 1a958ffad57e..722a931b6a5c 100644 --- a/src/Files.App/ViewModels/Dialogs/AddBranchDialogViewModel.cs +++ b/src/Files.App/ViewModels/Dialogs/AddBranchDialogViewModel.cs @@ -51,7 +51,7 @@ public AddBranchDialogViewModel(string repositoryPath, string activeBranch) { _repositoryPath = repositoryPath; BasedOn = activeBranch; - Branches = Array.Empty(); + Branches = []; } public async Task LoadBranches() diff --git a/src/Files.App/ViewModels/Dialogs/AddItemDialog/AddItemDialogViewModel.cs b/src/Files.App/ViewModels/Dialogs/AddItemDialog/AddItemDialogViewModel.cs index 7dba993f3b47..c6992076c86c 100644 --- a/src/Files.App/ViewModels/Dialogs/AddItemDialog/AddItemDialogViewModel.cs +++ b/src/Files.App/ViewModels/Dialogs/AddItemDialog/AddItemDialogViewModel.cs @@ -20,7 +20,7 @@ public AddItemDialogViewModel() _imagingService = Ioc.Default.GetRequiredService(); // Initialize - AddItemsList = new(); + AddItemsList = []; ResultType = new() { ItemType = AddItemDialogItemType.Cancel diff --git a/src/Files.App/ViewModels/HomeViewModel.cs b/src/Files.App/ViewModels/HomeViewModel.cs index ba0ce88d7f1d..3ebf1cdb123f 100644 --- a/src/Files.App/ViewModels/HomeViewModel.cs +++ b/src/Files.App/ViewModels/HomeViewModel.cs @@ -8,7 +8,7 @@ namespace Files.App.ViewModels { public sealed class HomeViewModel : ObservableObject, IDisposable { - public ObservableCollection WidgetItems { get; } = new(); + public ObservableCollection WidgetItems { get; } = []; public ICommand HomePageLoadedCommand { get; } diff --git a/src/Files.App/ViewModels/MainPageViewModel.cs b/src/Files.App/ViewModels/MainPageViewModel.cs index 781bb2c178fc..95e804599fc1 100644 --- a/src/Files.App/ViewModels/MainPageViewModel.cs +++ b/src/Files.App/ViewModels/MainPageViewModel.cs @@ -23,9 +23,9 @@ public sealed class MainPageViewModel : ObservableObject // Properties - public static ObservableCollection AppInstances { get; private set; } = new(); + public static ObservableCollection AppInstances { get; private set; } = []; - public List MultitaskingControls { get; } = new(); + public List MultitaskingControls { get; } = []; public ITabBar? MultitaskingControl { get; set; } @@ -133,7 +133,7 @@ public async Task OnNavigatedToAsync(NavigationEventArgs e) var defaultArg = new CustomTabViewItemParameter() { InitialPageType = typeof(PaneHolderPage), NavigationParameter = "Home" }; - UserSettingsService.GeneralSettingsService.LastSessionTabList = new List { defaultArg.Serialize() }; + UserSettingsService.GeneralSettingsService.LastSessionTabList = [defaultArg.Serialize()]; } else { @@ -168,7 +168,7 @@ public async Task OnNavigatedToAsync(NavigationEventArgs e) var defaultArg = new CustomTabViewItemParameter() { InitialPageType = typeof(PaneHolderPage), NavigationParameter = "Home" }; - UserSettingsService.GeneralSettingsService.LastSessionTabList = new List { defaultArg.Serialize() }; + UserSettingsService.GeneralSettingsService.LastSessionTabList = [defaultArg.Serialize()]; } } catch { } diff --git a/src/Files.App/ViewModels/Properties/CustomizationViewModel.cs b/src/Files.App/ViewModels/Properties/CustomizationViewModel.cs index 3ae35fe0e6a4..4cfdc9c2052e 100644 --- a/src/Files.App/ViewModels/Properties/CustomizationViewModel.cs +++ b/src/Files.App/ViewModels/Properties/CustomizationViewModel.cs @@ -61,7 +61,7 @@ public CustomizationViewModel(IShellPage appInstance, BaseProperties basePropert IsShortcut = item.IsShortcut; _selectedItemPath = item.ItemPath; - DllIcons = new(); + DllIcons = []; // Get default LoadIconsForPath(IconResourceItemPath); diff --git a/src/Files.App/ViewModels/Properties/HashesViewModel.cs b/src/Files.App/ViewModels/Properties/HashesViewModel.cs index 4089979f9682..157edfec52b2 100644 --- a/src/Files.App/ViewModels/Properties/HashesViewModel.cs +++ b/src/Files.App/ViewModels/Properties/HashesViewModel.cs @@ -35,17 +35,17 @@ public HashesViewModel(ListedItem item) _item = item; _cancellationTokenSource = new(); - Hashes = new() - { + Hashes = + [ new() { Algorithm = "CRC32" }, new() { Algorithm = "MD5" }, new() { Algorithm = "SHA1" }, new() { Algorithm = "SHA256" }, new() { Algorithm = "SHA384" }, new() { Algorithm = "SHA512" }, - }; + ]; - ShowHashes = UserSettingsService.GeneralSettingsService.ShowHashesDictionary ?? new(); + ShowHashes = UserSettingsService.GeneralSettingsService.ShowHashesDictionary ?? []; // Default settings ShowHashes.TryAdd("CRC32", true); ShowHashes.TryAdd("MD5", true); diff --git a/src/Files.App/ViewModels/Properties/Items/BaseProperties.cs b/src/Files.App/ViewModels/Properties/Items/BaseProperties.cs index 407dadb653b3..6edfd9a03123 100644 --- a/src/Files.App/ViewModels/Properties/Items/BaseProperties.cs +++ b/src/Files.App/ViewModels/Properties/Items/BaseProperties.cs @@ -28,11 +28,11 @@ public async Task GetOtherPropertiesAsync(IStorageItemExtraProperties properties string dateAccessedProperty = "System.DateAccessed"; string dateModifiedProperty = "System.DateModified"; - List propertiesName = new() - { + List propertiesName = + [ dateAccessedProperty, dateModifiedProperty - }; + ]; IDictionary extraProperties = await properties.RetrievePropertiesAsync(propertiesName); diff --git a/src/Files.App/ViewModels/Properties/Items/CombinedFileProperties.cs b/src/Files.App/ViewModels/Properties/Items/CombinedFileProperties.cs index 9a1b83a2a6b6..25cd03f56253 100644 --- a/src/Files.App/ViewModels/Properties/Items/CombinedFileProperties.cs +++ b/src/Files.App/ViewModels/Properties/Items/CombinedFileProperties.cs @@ -99,8 +99,10 @@ public async Task SyncPropertyChangesAsync() { if (!prop.IsReadOnly && prop.Modified) { - var newDict = new Dictionary(); - newDict.Add(prop.Property, prop.Value); + var newDict = new Dictionary + { + { prop.Property, prop.Value } + }; foreach (var file in files) { @@ -146,8 +148,10 @@ public async Task ClearPropertiesAsync() { if (!prop.IsReadOnly) { - var newDict = new Dictionary(); - newDict.Add(prop.Property, null); + var newDict = new Dictionary + { + { prop.Property, null } + }; foreach (var file in files) { diff --git a/src/Files.App/ViewModels/Properties/Items/DriveProperties.cs b/src/Files.App/ViewModels/Properties/Items/DriveProperties.cs index b7a1a8c8172e..750ac63b8ed0 100644 --- a/src/Files.App/ViewModels/Properties/Items/DriveProperties.cs +++ b/src/Files.App/ViewModels/Properties/Items/DriveProperties.cs @@ -83,7 +83,7 @@ public async override Task GetSpecialPropertiesAsync() string capacity = "System.Capacity"; string fileSystem = "System.Volume.FileSystem"; - var properties = await diskRoot.Properties.RetrievePropertiesAsync(new[] { freeSpace, capacity, fileSystem }); + var properties = await diskRoot.Properties.RetrievePropertiesAsync([freeSpace, capacity, fileSystem]); ViewModel.DriveCapacityValue = (ulong)properties[capacity]; ViewModel.DriveFreeSpaceValue = (ulong)properties[freeSpace]; diff --git a/src/Files.App/ViewModels/Properties/Items/FileProperties.cs b/src/Files.App/ViewModels/Properties/Items/FileProperties.cs index cc0cd707f791..defe14c8c572 100644 --- a/src/Files.App/ViewModels/Properties/Items/FileProperties.cs +++ b/src/Files.App/ViewModels/Properties/Items/FileProperties.cs @@ -211,8 +211,10 @@ public async Task SyncPropertyChangesAsync() { if (!prop.IsReadOnly && prop.Modified) { - var newDict = new Dictionary(); - newDict.Add(prop.Property, prop.Value); + var newDict = new Dictionary + { + { prop.Property, prop.Value } + }; try { @@ -249,8 +251,10 @@ public async Task ClearPropertiesAsync() { if (!prop.IsReadOnly) { - var newDict = new Dictionary(); - newDict.Add(prop.Property, null); + var newDict = new Dictionary + { + { prop.Property, null } + }; try { diff --git a/src/Files.App/ViewModels/Properties/Items/FileProperty.cs b/src/Files.App/ViewModels/Properties/Items/FileProperty.cs index bd547c6e14ae..c0ea00835b6d 100644 --- a/src/Files.App/ViewModels/Properties/Items/FileProperty.cs +++ b/src/Files.App/ViewModels/Properties/Items/FileProperty.cs @@ -163,8 +163,10 @@ public Task SaveValueToFile(BaseStorageFile file) return Task.CompletedTask; } - var propsToSave = new Dictionary(); - propsToSave.Add(Property, Converter.ConvertBack(Value, null, null, null)); + var propsToSave = new Dictionary + { + { Property, Converter.ConvertBack(Value, null, null, null) } + }; return file.Properties.SavePropertiesAsync(propsToSave).AsTask(); } @@ -273,7 +275,7 @@ private object ConvertBack(string value) return value; } - private static Dictionary cachedPropertiesListFiles = new Dictionary(); + private static Dictionary cachedPropertiesListFiles = []; /// /// This function retrieves the list of properties to display from the PropertiesInformation.json @@ -319,7 +321,7 @@ public async static Task> RetrieveAndInitializePropertiesAsyn { if (file.Properties is not null) { - val = (await file.Properties.RetrievePropertiesAsync(new string[] { prop })).First().Value; + val = (await file.Properties.RetrievePropertiesAsync([prop])).First().Value; } } catch (ArgumentException e) diff --git a/src/Files.App/ViewModels/Settings/AdvancedViewModel.cs b/src/Files.App/ViewModels/Settings/AdvancedViewModel.cs index 750b3ad3d6aa..4bbd3a1052a2 100644 --- a/src/Files.App/ViewModels/Settings/AdvancedViewModel.cs +++ b/src/Files.App/ViewModels/Settings/AdvancedViewModel.cs @@ -199,7 +199,7 @@ private async Task ImportSettingsAsync() private async Task ExportSettingsAsync() { FileSavePicker filePicker = InitializeWithWindow(new FileSavePicker()); - filePicker.FileTypeChoices.Add("Zip File", new[] { ".zip" }); + filePicker.FileTypeChoices.Add("Zip File", [".zip"]); filePicker.SuggestedFileName = $"Files_{AppLifecycleHelper.AppVersion}"; StorageFile file = await filePicker.PickSaveFileAsync(); diff --git a/src/Files.App/ViewModels/Settings/AppearanceViewModel.cs b/src/Files.App/ViewModels/Settings/AppearanceViewModel.cs index dbf7a169fb25..6e463b8b65ae 100644 --- a/src/Files.App/ViewModels/Settings/AppearanceViewModel.cs +++ b/src/Files.App/ViewModels/Settings/AppearanceViewModel.cs @@ -12,7 +12,7 @@ public sealed class AppearanceViewModel : ObservableObject private readonly IResourcesService ResourcesService; public List Themes { get; private set; } - public Dictionary BackdropMaterialTypes { get; private set; } = new(); + public Dictionary BackdropMaterialTypes { get; private set; } = []; public ObservableCollection AppThemeResources { get; } @@ -21,12 +21,12 @@ public AppearanceViewModel(IUserSettingsService userSettingsService, IResourcesS UserSettingsService = userSettingsService; ResourcesService = resourcesService; - Themes = new List() - { + Themes = + [ "Default".GetLocalizedResource(), "LightTheme".GetLocalizedResource(), "DarkTheme".GetLocalizedResource() - }; + ]; // TODO: Re-add Solid and regular Mica when theming is revamped //BackdropMaterialTypes.Add(BackdropMaterialType.Solid, "Solid".GetLocalizedResource()); diff --git a/src/Files.App/ViewModels/Settings/GeneralViewModel.cs b/src/Files.App/ViewModels/Settings/GeneralViewModel.cs index 40450e3bf268..cfa12ba59c89 100644 --- a/src/Files.App/ViewModels/Settings/GeneralViewModel.cs +++ b/src/Files.App/ViewModels/Settings/GeneralViewModel.cs @@ -105,7 +105,7 @@ public GeneralViewModel() if (UserSettingsService.GeneralSettingsService.TabsOnStartupList is not null) PagesOnStartupList = new ObservableCollection(UserSettingsService.GeneralSettingsService.TabsOnStartupList.Select((p) => new PageOnStartupViewModel(p))); else - PagesOnStartupList = new ObservableCollection(); + PagesOnStartupList = []; PagesOnStartupList.CollectionChanged += PagesOnStartupList_CollectionChanged; diff --git a/src/Files.App/ViewModels/Settings/TagsViewModel.cs b/src/Files.App/ViewModels/Settings/TagsViewModel.cs index 42b46c6cdcd4..5c5ca49cba37 100644 --- a/src/Files.App/ViewModels/Settings/TagsViewModel.cs +++ b/src/Files.App/ViewModels/Settings/TagsViewModel.cs @@ -35,7 +35,7 @@ public TagsViewModel() SaveNewTagCommand = new RelayCommand(DoSaveNewTag); CancelNewTagCommand = new RelayCommand(DoCancelNewTag); - Tags = new ObservableCollection(); + Tags = []; Tags.CollectionChanged += Tags_CollectionChanged; fileTagsSettingsService.FileTagList?.ForEach(tag => Tags.Add(new ListedTagViewModel(tag))); diff --git a/src/Files.App/ViewModels/UserControls/AddressToolbarViewModel.cs b/src/Files.App/ViewModels/UserControls/AddressToolbarViewModel.cs index 249fcd5f7693..087f34dcd72f 100644 --- a/src/Files.App/ViewModels/UserControls/AddressToolbarViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/AddressToolbarViewModel.cs @@ -61,7 +61,7 @@ public sealed class AddressToolbarViewModel : ObservableObject, IAddressToolbarV public event EventHandler? RefreshWidgetsRequested; - public ObservableCollection PathComponents { get; } = new(); + public ObservableCollection PathComponents { get; } = []; private bool _isCommandPaletteOpen; public bool IsCommandPaletteOpen @@ -170,7 +170,7 @@ public bool IsSearchBoxVisible } } - public ObservableCollection NavigationBarSuggestions = new(); + public ObservableCollection NavigationBarSuggestions = []; private CurrentInstanceViewModel instanceViewModel; public CurrentInstanceViewModel InstanceViewModel @@ -803,7 +803,7 @@ public async Task CheckPathInputAsync(string currentInput, string currentSelecte private void SavePathToHistory(string path) { - var pathHistoryList = UserSettingsService.GeneralSettingsService.PathHistoryList?.ToList() ?? new List(); + var pathHistoryList = UserSettingsService.GeneralSettingsService.PathHistoryList?.ToList() ?? []; pathHistoryList.Remove(path); pathHistoryList.Insert(0, path); diff --git a/src/Files.App/ViewModels/UserControls/InfoPaneViewModel.cs b/src/Files.App/ViewModels/UserControls/InfoPaneViewModel.cs index 1f914202a1a4..b4a5c62c88dd 100644 --- a/src/Files.App/ViewModels/UserControls/InfoPaneViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/InfoPaneViewModel.cs @@ -107,7 +107,7 @@ public bool LoadTagsList PreviewPaneState is PreviewPaneStates.NoPreviewAvailable || PreviewPaneState is PreviewPaneStates.PreviewAndDetailsAvailable; - public ObservableCollection Items { get; } = new(); + public ObservableCollection Items { get; } = []; public InfoPaneViewModel() { diff --git a/src/Files.App/ViewModels/UserControls/Previews/BasePreviewModel.cs b/src/Files.App/ViewModels/UserControls/Previews/BasePreviewModel.cs index d5e4e0461e95..fd4f5009d672 100644 --- a/src/Files.App/ViewModels/UserControls/Previews/BasePreviewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Previews/BasePreviewModel.cs @@ -49,7 +49,7 @@ public static Task ReadFileAsTextAsync(BaseStorageFile file, int maxLeng /// The task to run public virtual async Task LoadAsync() { - List detailsFull = new(); + List detailsFull = []; if (Item.ItemFile is null) { @@ -95,7 +95,7 @@ public async virtual Task> LoadPreviewAndDetailsAsync() else FileImage ??= await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(() => new BitmapImage()); - return new List(); + return []; } /// diff --git a/src/Files.App/ViewModels/UserControls/Previews/FolderPreviewViewModel.cs b/src/Files.App/ViewModels/UserControls/Previews/FolderPreviewViewModel.cs index e0bfa3d4bc8f..f826df056e35 100644 --- a/src/Files.App/ViewModels/UserControls/Previews/FolderPreviewViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Previews/FolderPreviewViewModel.cs @@ -41,13 +41,13 @@ private async Task LoadPreviewAndDetailsAsync() var info = await Folder.GetBasicPropertiesAsync(); - Item.FileDetails = new() - { + Item.FileDetails = + [ GetFileProperty("PropertyItemCount", items.Count), GetFileProperty("PropertyDateModified", info.DateModified), GetFileProperty("PropertyDateCreated", info.DateCreated), GetFileProperty("PropertyParsingPath", Folder.Path), - }; + ]; if (GitHelpers.IsRepositoryEx(Item.ItemPath, out var repoPath) && !string.IsNullOrEmpty(repoPath)) diff --git a/src/Files.App/ViewModels/UserControls/Previews/HtmlPreviewViewModel.cs b/src/Files.App/ViewModels/UserControls/Previews/HtmlPreviewViewModel.cs index 02622cfc5209..4edbb7d8343e 100644 --- a/src/Files.App/ViewModels/UserControls/Previews/HtmlPreviewViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Previews/HtmlPreviewViewModel.cs @@ -16,6 +16,6 @@ public static bool ContainsExtension(string extension) => extension is ".htm" or ".html" or ".svg"; public async override Task> LoadPreviewAndDetailsAsync() - => new List(); + => []; } } diff --git a/src/Files.App/ViewModels/UserControls/Previews/ImagePreviewViewModel.cs b/src/Files.App/ViewModels/UserControls/Previews/ImagePreviewViewModel.cs index 6bcb91cb8fbe..a7a1bb8f6eae 100644 --- a/src/Files.App/ViewModels/UserControls/Previews/ImagePreviewViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Previews/ImagePreviewViewModel.cs @@ -39,7 +39,7 @@ public override async Task> LoadPreviewAndDetailsAsync() ImageSource = bitmap; }); - return new List(); + return []; } } } diff --git a/src/Files.App/ViewModels/UserControls/Previews/MarkdownPreviewViewModel.cs b/src/Files.App/ViewModels/UserControls/Previews/MarkdownPreviewViewModel.cs index f8edfa4023a6..75f70c4a25d4 100644 --- a/src/Files.App/ViewModels/UserControls/Previews/MarkdownPreviewViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Previews/MarkdownPreviewViewModel.cs @@ -27,7 +27,7 @@ public override async Task> LoadPreviewAndDetailsAsync() var text = await ReadFileAsTextAsync(Item.ItemFile); TextValue = text.Left(Constants.PreviewPane.TextCharacterLimit); - return new List(); + return []; } } } diff --git a/src/Files.App/ViewModels/UserControls/Previews/PDFPreviewViewModel.cs b/src/Files.App/ViewModels/UserControls/Previews/PDFPreviewViewModel.cs index 671936ab7c05..c2b0f8c86c48 100644 --- a/src/Files.App/ViewModels/UserControls/Previews/PDFPreviewViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Previews/PDFPreviewViewModel.cs @@ -28,7 +28,7 @@ public int PageCount set => SetProperty(ref pageCount, value); } - public ObservableCollection Pages { get; } = new(); + public ObservableCollection Pages { get; } = []; public PDFPreviewViewModel(ListedItem item) : base(item) diff --git a/src/Files.App/ViewModels/UserControls/Previews/RichTextPreviewViewModel.cs b/src/Files.App/ViewModels/UserControls/Previews/RichTextPreviewViewModel.cs index 5d891e6c9174..fda43bedea55 100644 --- a/src/Files.App/ViewModels/UserControls/Previews/RichTextPreviewViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Previews/RichTextPreviewViewModel.cs @@ -19,7 +19,7 @@ public async override Task> LoadPreviewAndDetailsAsync() { Stream = await Item.ItemFile.OpenReadAsync(); - return new List(); + return []; } } } diff --git a/src/Files.App/ViewModels/UserControls/Previews/ShellPreviewViewModel.cs b/src/Files.App/ViewModels/UserControls/Previews/ShellPreviewViewModel.cs index df4a552077ed..761f1090580d 100644 --- a/src/Files.App/ViewModels/UserControls/Previews/ShellPreviewViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Previews/ShellPreviewViewModel.cs @@ -20,7 +20,7 @@ public ShellPreviewViewModel(ListedItem item) } public async override Task> LoadPreviewAndDetailsAsync() - => new List(); + => []; private const string IPreviewHandlerIid = "{8895b1c6-b41f-4c1c-a562-0d564250836f}"; private static readonly Guid QueryAssociationsClsid = new Guid(0xa07034fd, 0x6caa, 0x4954, 0xac, 0x3f, 0x97, 0xa2, 0x72, 0x16, 0xf9, 0x8a); @@ -115,10 +115,10 @@ public void LoadPreview(UIElement presenter) private bool ChildWindowToXaml(IntPtr parent, UIElement presenter) { D3D_DRIVER_TYPE[] driverTypes = - { + [ D3D_DRIVER_TYPE.D3D_DRIVER_TYPE_HARDWARE, D3D_DRIVER_TYPE.D3D_DRIVER_TYPE_WARP, - }; + ]; ID3D11Device? d3d11Device = null; ID3D11DeviceContext? d3d11DeviceContext = null; diff --git a/src/Files.App/ViewModels/UserControls/SearchBoxViewModel.cs b/src/Files.App/ViewModels/UserControls/SearchBoxViewModel.cs index 35a1b735976d..e61d35f200c7 100644 --- a/src/Files.App/ViewModels/UserControls/SearchBoxViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/SearchBoxViewModel.cs @@ -28,9 +28,9 @@ public string Query private readonly SuggestionComparer suggestionComparer = new SuggestionComparer(); - public ObservableCollection Suggestions { get; } = new ObservableCollection(); + public ObservableCollection Suggestions { get; } = []; - private readonly List oldQueries = new List(); + private readonly List oldQueries = []; public void ClearSuggestions() { diff --git a/src/Files.App/ViewModels/UserControls/SidebarViewModel.cs b/src/Files.App/ViewModels/UserControls/SidebarViewModel.cs index 5780cc6e9abc..53e42b94ced2 100644 --- a/src/Files.App/ViewModels/UserControls/SidebarViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/SidebarViewModel.cs @@ -74,8 +74,7 @@ public SidebarDisplayMode SidebarDisplayMode public static event EventHandler? RightClickedItemChanged; private readonly SectionType[] SectionOrder = - new SectionType[] - { + [ SectionType.Home, SectionType.Pinned, SectionType.Library, @@ -84,7 +83,7 @@ public SidebarDisplayMode SidebarDisplayMode SectionType.Network, SectionType.WSL, SectionType.FileTag - }; + ]; public bool IsSidebarCompactSize => SidebarDisplayMode == SidebarDisplayMode.Compact || SidebarDisplayMode == SidebarDisplayMode.Minimal; @@ -235,7 +234,7 @@ public SidebarViewModel() dispatcherQueue = Microsoft.UI.Dispatching.DispatcherQueue.GetForCurrentThread(); fileTagsService = Ioc.Default.GetRequiredService(); - sidebarItems = new BulkConcurrentObservableCollection(); + sidebarItems = []; UserSettingsService.OnSettingChangedEvent += UserSettingsService_OnSettingChangedEvent; CreateItemHomeAsync(); @@ -559,7 +558,7 @@ private LocationItem BuildSection(string sectionName, SectionType sectionType, C Section = sectionType, MenuOptions = options, SelectsOnInvoked = selectsOnInvoked, - ChildItems = new BulkConcurrentObservableCollection() + ChildItems = [] }; } @@ -1067,7 +1066,7 @@ private List GetLocationItemMenuItems(INavigatio { Text = "Loading".GetLocalizedResource(), Glyph = "\xE712", - Items = new List(), + Items = [], ID = "ItemOverflow", Tag = "ItemOverflow", IsEnabled = false, @@ -1290,7 +1289,7 @@ private async Task HandleTagItemDroppedAsync(FileTagItem fileTagItem, ItemDroppe { ItemPath = item.Path, FileFRN = await FileTagsHelper.GetFileFRN(item.Item), - FileTags = new[] { fileTagItem.FileTag.Uid } + FileTags = [fileTagItem.FileTag.Uid] }; } } diff --git a/src/Files.App/ViewModels/UserControls/StatusCenterViewModel.cs b/src/Files.App/ViewModels/UserControls/StatusCenterViewModel.cs index d2cff1eb1815..d1f376a5dcdc 100644 --- a/src/Files.App/ViewModels/UserControls/StatusCenterViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/StatusCenterViewModel.cs @@ -5,7 +5,7 @@ namespace Files.App.ViewModels.UserControls { public sealed class StatusCenterViewModel : ObservableObject { - public ObservableCollection StatusCenterItems { get; } = new(); + public ObservableCollection StatusCenterItems { get; } = []; private int _AverageOperationProgressValue = 0; public int AverageOperationProgressValue diff --git a/src/Files.App/ViewModels/Widgets/FileTagsWidget/FileTagsContainerViewModel.cs b/src/Files.App/ViewModels/Widgets/FileTagsWidget/FileTagsContainerViewModel.cs index 29ccb0ea9290..4959581f588f 100644 --- a/src/Files.App/ViewModels/Widgets/FileTagsWidget/FileTagsContainerViewModel.cs +++ b/src/Files.App/ViewModels/Widgets/FileTagsWidget/FileTagsContainerViewModel.cs @@ -38,7 +38,7 @@ public FileTagsContainerViewModel(string tagUid, Func openAction) _tagUid = tagUid; _openAction = openAction; - Tags = new(); + Tags = []; } /// diff --git a/src/Files.App/ViewModels/Widgets/FileTagsWidget/FileTagsWidgetViewModel.cs b/src/Files.App/ViewModels/Widgets/FileTagsWidget/FileTagsWidgetViewModel.cs index 1d7c71082270..7a15073ed388 100644 --- a/src/Files.App/ViewModels/Widgets/FileTagsWidget/FileTagsWidgetViewModel.cs +++ b/src/Files.App/ViewModels/Widgets/FileTagsWidget/FileTagsWidgetViewModel.cs @@ -22,7 +22,7 @@ public FileTagsWidgetViewModel(Func openAction) _fileTagsSettingsService = Ioc.Default.GetRequiredService(); _openAction = openAction; - Containers = new(); + Containers = []; _fileTagsSettingsService.OnTagsUpdated += FileTagsSettingsService_OnTagsUpdated; } diff --git a/src/Files.App/Views/HomePage.xaml.cs b/src/Files.App/Views/HomePage.xaml.cs index 437d6c7d9ee6..70c7539318c2 100644 --- a/src/Files.App/Views/HomePage.xaml.cs +++ b/src/Files.App/Views/HomePage.xaml.cs @@ -226,7 +226,7 @@ private void WidgetOpenLocationInvoked(object sender, PathNavigationEventArgs e) AppInstance.NavigateWithArguments(FolderSettings.GetLayoutType(e.ItemPath), new NavigationArguments() { NavPathParam = e.ItemPath, - SelectItems = new[] { e.ItemName }, + SelectItems = [e.ItemName], AssociatedTabInstance = AppInstance }); } diff --git a/src/Files.App/Views/Layouts/BaseLayoutPage.cs b/src/Files.App/Views/Layouts/BaseLayoutPage.cs index c0b35f9a312a..afd93b6c3195 100644 --- a/src/Files.App/Views/Layouts/BaseLayoutPage.cs +++ b/src/Files.App/Views/Layouts/BaseLayoutPage.cs @@ -216,7 +216,7 @@ public string JumpString } } - private List? selectedItems = new(); + private List? selectedItems = []; public List? SelectedItems { get => selectedItems; @@ -494,8 +494,10 @@ public void SetSelectedItemsOnNavigation() navigationArguments.SelectItems is not null && navigationArguments.SelectItems.Any()) { - List listedItemsToSelect = new(); - listedItemsToSelect.AddRange(ParentShellPageInstance!.FilesystemViewModel.FilesAndFolders.ToList().Where((li) => navigationArguments.SelectItems.Contains(li.ItemNameRaw))); + List listedItemsToSelect = + [ + .. ParentShellPageInstance!.FilesystemViewModel.FilesAndFolders.ToList().Where((li) => navigationArguments.SelectItems.Contains(li.ItemNameRaw)), + ]; ItemManipulationModel.SetSelectedItems(listedItemsToSelect); ItemManipulationModel.FocusSelectedItems(); @@ -643,7 +645,7 @@ private async void BaseContextFlyout_Opening(object? sender, object e) shellContextMenuItemCancellationToken = new CancellationTokenSource(); shiftPressed = Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.Shift).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down); - var items = ContentPageContextFlyoutFactory.GetItemContextCommandsWithoutShellItems(currentInstanceViewModel: InstanceViewModel!, selectedItems: new List { ParentShellPageInstance!.FilesystemViewModel.CurrentFolder }, commandsViewModel: CommandsViewModel!, shiftPressed: shiftPressed, itemViewModel: ParentShellPageInstance!.FilesystemViewModel, selectedItemsPropertiesViewModel: null); + var items = ContentPageContextFlyoutFactory.GetItemContextCommandsWithoutShellItems(currentInstanceViewModel: InstanceViewModel!, selectedItems: [ParentShellPageInstance!.FilesystemViewModel.CurrentFolder], commandsViewModel: CommandsViewModel!, shiftPressed: shiftPressed, itemViewModel: ParentShellPageInstance!.FilesystemViewModel, selectedItemsPropertiesViewModel: null); BaseContextMenuFlyout.PrimaryCommands.Clear(); BaseContextMenuFlyout.SecondaryCommands.Clear(); @@ -660,7 +662,7 @@ private async void BaseContextFlyout_Opening(object? sender, object e) if (!InstanceViewModel!.IsPageTypeSearchResults && !InstanceViewModel.IsPageTypeZipFolder && !InstanceViewModel.IsPageTypeFtp) { - var shellMenuItems = await ContentPageContextFlyoutFactory.GetItemContextShellCommandsAsync(workingDir: ParentShellPageInstance.FilesystemViewModel.WorkingDirectory, selectedItems: new List(), shiftPressed: shiftPressed, showOpenMenu: false, shellContextMenuItemCancellationToken.Token); + var shellMenuItems = await ContentPageContextFlyoutFactory.GetItemContextShellCommandsAsync(workingDir: ParentShellPageInstance.FilesystemViewModel.WorkingDirectory, selectedItems: [], shiftPressed: shiftPressed, showOpenMenu: false, shellContextMenuItemCancellationToken.Token); if (shellMenuItems.Any()) await AddShellMenuItemsAsync(shellMenuItems, BaseContextMenuFlyout, shiftPressed); else diff --git a/src/Files.App/Views/Layouts/DetailsLayoutPage.xaml.cs b/src/Files.App/Views/Layouts/DetailsLayoutPage.xaml.cs index f214439176d8..d153f05bb1b7 100644 --- a/src/Files.App/Views/Layouts/DetailsLayoutPage.xaml.cs +++ b/src/Files.App/Views/Layouts/DetailsLayoutPage.xaml.cs @@ -908,7 +908,7 @@ private void RemoveTagIcon_Tapped(object sender, TappedRoutedEventArgs e) if (tagId is not null) { item.FileTags = item.FileTags - .Except(new string[] { tagId }) + .Except([tagId]) .ToArray(); } diff --git a/src/Files.App/Views/Properties/LibraryPage.xaml.cs b/src/Files.App/Views/Properties/LibraryPage.xaml.cs index 49ff5e48bc33..3eccdf7d8e9f 100644 --- a/src/Files.App/Views/Properties/LibraryPage.xaml.cs +++ b/src/Files.App/Views/Properties/LibraryPage.xaml.cs @@ -28,7 +28,7 @@ private void NotifyPropertyChanged([CallerMemberName] string propertyName = "") PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } - public ObservableCollection Folders { get; } = new(); + public ObservableCollection Folders { get; } = []; public bool IsLibraryEmpty => Folders.Count == 0; diff --git a/src/Files.Core.SourceGenerator/Utilities/SourceGeneratorHelper.cs b/src/Files.Core.SourceGenerator/Utilities/SourceGeneratorHelper.cs index 736d5313ba26..5d767dc205dc 100644 --- a/src/Files.Core.SourceGenerator/Utilities/SourceGeneratorHelper.cs +++ b/src/Files.Core.SourceGenerator/Utilities/SourceGeneratorHelper.cs @@ -180,29 +180,26 @@ internal static PropertyDeclarationSyntax GetPropertyDeclaration(string property .AddAccessorListAccessors(getter, setter); internal static AttributeListSyntax[] GetAttributeForField(string generatorName) => - new[] - { + [ AttributeList().AddAttributes(Attribute(IdentifierName("global::System.CodeDom.Compiler.GeneratedCode")) .AddArgumentListArguments( AttributeArgument(LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(AssemblyName + generatorName))), AttributeArgument(LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(AssemblyVersion))) )) - }; + ]; internal static AttributeListSyntax[] GetAttributeForEvent(string generatorName) => - new[] - { + [ AttributeList().AddAttributes( Attribute(IdentifierName("global::System.CodeDom.Compiler.GeneratedCode")).AddArgumentListArguments( AttributeArgument(LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(AssemblyName + generatorName))), AttributeArgument(LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(AssemblyVersion))))), AttributeList().AddAttributes( Attribute(IdentifierName("global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage"))) - }; + ]; internal static AttributeListSyntax[] GetAttributeForMethod(string generatorName) => - new[] - { + [ AttributeList().AddAttributes(Attribute(IdentifierName("global::System.CodeDom.Compiler.GeneratedCode")) .AddArgumentListArguments( AttributeArgument(LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(AssemblyName + generatorName))), @@ -210,7 +207,7 @@ internal static PropertyDeclarationSyntax GetPropertyDeclaration(string property )), AttributeList().AddAttributes(Attribute(IdentifierName("global::System.Diagnostics.DebuggerNonUserCode"))), AttributeList().AddAttributes(Attribute(IdentifierName("global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage"))) - }; + ]; /// /// Generate the following code @@ -229,7 +226,7 @@ internal static ClassDeclarationSyntax GetClassDeclaration(ISymbol specificType, return ClassDeclaration(specificType.Name) .AddModifiers(Token(SyntaxKind.PartialKeyword)) - .AddMembers(members.ToArray()); + .AddMembers([.. members]); } /// diff --git a/src/Files.Shared/Extensions/EnumerableExtensions.cs b/src/Files.Shared/Extensions/EnumerableExtensions.cs index ec2289ef2de7..2db3814ceea3 100644 --- a/src/Files.Shared/Extensions/EnumerableExtensions.cs +++ b/src/Files.Shared/Extensions/EnumerableExtensions.cs @@ -20,12 +20,12 @@ public static class EnumerableExtensions /// with public static IEnumerable CreateEnumerable(this T item) { - return new[] { item }; + return [item]; } public static List CreateList(this T item) { - return new() { item }; + return [item]; } public static IList AddIfNotPresent(this IList list, T element) diff --git a/src/Files.Shared/Extensions/LinqExtensions.cs b/src/Files.Shared/Extensions/LinqExtensions.cs index 105f377d832c..75b25afc7578 100644 --- a/src/Files.Shared/Extensions/LinqExtensions.cs +++ b/src/Files.Shared/Extensions/LinqExtensions.cs @@ -169,7 +169,7 @@ public static List RemoveFrom(this List list, int index) return list; return index <= 0 - ? new List(0) + ? [] : list.Take(index - 1).ToList(); } } diff --git a/tests/Files.InteractionTests/Helper/TestHelper.cs b/tests/Files.InteractionTests/Helper/TestHelper.cs index 250dcdfd3075..618e24164915 100644 --- a/tests/Files.InteractionTests/Helper/TestHelper.cs +++ b/tests/Files.InteractionTests/Helper/TestHelper.cs @@ -18,7 +18,7 @@ public static List GetElementsOfTypeWithContent(string elementTy public static List GetItemsWithContent(ICollection elements, string content) { - List elementsToReturn = new List(); + List elementsToReturn = []; foreach (WindowsElement element in elements) { if (element.Text.Contains(content, StringComparison.OrdinalIgnoreCase)) diff --git a/tests/Files.InteractionTests/SessionManager.cs b/tests/Files.InteractionTests/SessionManager.cs index 788b6b12a6c1..ad57ec0b6805 100644 --- a/tests/Files.InteractionTests/SessionManager.cs +++ b/tests/Files.InteractionTests/SessionManager.cs @@ -14,11 +14,11 @@ namespace Files.InteractionTests public sealed class SessionManager { private const string WindowsApplicationDriverUrl = "http://127.0.0.1:4723"; - private static string[] FilesAppIDs = new string[]{ + private static string[] FilesAppIDs = [ "FilesDev_ykqwq8d6ps0ag!App", // Needed to run on the local end and/or the CI "FilesDev_9bhem8es8z4gp!App", // Needed to run on the local end and/or the CI "FilesDev_dwm5abbcs5pn0!App", // Needed to run on the CI - }; + ]; private static uint appIdIndex = 0; From c6ac35fd755c112dae8cac28f44fcee15eaa25b9 Mon Sep 17 00:00:00 2001 From: hishitetsu <66369541+hishitetsu@users.noreply.github.com> Date: Wed, 10 Apr 2024 10:47:55 +0900 Subject: [PATCH 31/41] Code Quality: Updated Windows App SDK and others (#15145) --- .github/README.md | 2 +- src/Files.App (Package)/Files.Package.wapproj | 2 +- src/Files.App/Files.App.csproj | 4 ++-- tests/Files.InteractionTests/Files.InteractionTests.csproj | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/README.md b/.github/README.md index e25fb55160ed..6c6564819a3e 100644 --- a/.github/README.md +++ b/.github/README.md @@ -23,7 +23,7 @@ Files also offers advanced features such as file tagging for easy organization, - MSVC v143 - VS 2022 C++ x64/x86 or ARM64 build tools (latest) - C++ ATL for latest v143 build tools (x86 & x64 or ARM64) - Git for Windows -- [Windows App SDK 1.4](https://learn.microsoft.com/windows/apps/windows-app-sdk/downloads#current-releases) +- [Windows App SDK 1.5](https://learn.microsoft.com/windows/apps/windows-app-sdk/downloads#current-releases) ### 2. Clone the repository diff --git a/src/Files.App (Package)/Files.Package.wapproj b/src/Files.App (Package)/Files.Package.wapproj index 0ba1119ebd51..13f95e8ce02b 100644 --- a/src/Files.App (Package)/Files.Package.wapproj +++ b/src/Files.App (Package)/Files.Package.wapproj @@ -107,7 +107,7 @@ - + diff --git a/src/Files.App/Files.App.csproj b/src/Files.App/Files.App.csproj index e614edfdd97d..586c31b67ba6 100644 --- a/src/Files.App/Files.App.csproj +++ b/src/Files.App/Files.App.csproj @@ -80,14 +80,14 @@ - + - + diff --git a/tests/Files.InteractionTests/Files.InteractionTests.csproj b/tests/Files.InteractionTests/Files.InteractionTests.csproj index 7f3b27f4cd60..c0289f9f51b2 100644 --- a/tests/Files.InteractionTests/Files.InteractionTests.csproj +++ b/tests/Files.InteractionTests/Files.InteractionTests.csproj @@ -21,8 +21,8 @@ - - + + From ea73284a799e7a5694cd3e8a6aeb131c5134410e Mon Sep 17 00:00:00 2001 From: hishitetsu <66369541+hishitetsu@users.noreply.github.com> Date: Wed, 10 Apr 2024 23:59:10 +0900 Subject: [PATCH 32/41] Fix: Fixed issue where existing tabs were duplicated when opening a folder from the outside of Files (#15146) --- src/Files.App/ViewModels/MainPageViewModel.cs | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/src/Files.App/ViewModels/MainPageViewModel.cs b/src/Files.App/ViewModels/MainPageViewModel.cs index 95e804599fc1..db4bde05d779 100644 --- a/src/Files.App/ViewModels/MainPageViewModel.cs +++ b/src/Files.App/ViewModels/MainPageViewModel.cs @@ -125,15 +125,14 @@ public async Task OnNavigatedToAsync(NavigationEventArgs e) else if (UserSettingsService.GeneralSettingsService.ContinueLastSessionOnStartUp && UserSettingsService.GeneralSettingsService.LastSessionTabList is not null) { - foreach (string tabArgsString in UserSettingsService.GeneralSettingsService.LastSessionTabList) + if (AppInstances.Count == 0) { - var tabArgs = CustomTabViewItemParameter.Deserialize(tabArgsString); - await NavigationHelpers.AddNewTabByParamAsync(tabArgs.InitialPageType, tabArgs.NavigationParameter); + foreach (string tabArgsString in UserSettingsService.GeneralSettingsService.LastSessionTabList) + { + var tabArgs = CustomTabViewItemParameter.Deserialize(tabArgsString); + await NavigationHelpers.AddNewTabByParamAsync(tabArgs.InitialPageType, tabArgs.NavigationParameter); + } } - - var defaultArg = new CustomTabViewItemParameter() { InitialPageType = typeof(PaneHolderPage), NavigationParameter = "Home" }; - - UserSettingsService.GeneralSettingsService.LastSessionTabList = [defaultArg.Serialize()]; } else { @@ -158,17 +157,14 @@ public async Task OnNavigatedToAsync(NavigationEventArgs e) await NavigationHelpers.AddNewTabByPathAsync(typeof(PaneHolderPage), path, true); } else if (UserSettingsService.GeneralSettingsService.ContinueLastSessionOnStartUp && - UserSettingsService.GeneralSettingsService.LastSessionTabList is not null) + UserSettingsService.GeneralSettingsService.LastSessionTabList is not null && + AppInstances.Count == 0) { foreach (string tabArgsString in UserSettingsService.GeneralSettingsService.LastSessionTabList) { var tabArgs = CustomTabViewItemParameter.Deserialize(tabArgsString); await NavigationHelpers.AddNewTabByParamAsync(tabArgs.InitialPageType, tabArgs.NavigationParameter); } - - var defaultArg = new CustomTabViewItemParameter() { InitialPageType = typeof(PaneHolderPage), NavigationParameter = "Home" }; - - UserSettingsService.GeneralSettingsService.LastSessionTabList = [defaultArg.Serialize()]; } } catch { } From b641dcb376932e4e32bde36fec40f5822c92c85e Mon Sep 17 00:00:00 2001 From: 0x5bfa <62196528+0x5bfa@users.noreply.github.com> Date: Thu, 11 Apr 2024 01:50:55 +0900 Subject: [PATCH 33/41] Feature: Added support for custom key bindings (#15109) Co-authored-by: Yair <39923744+yaira2@users.noreply.github.com> --- .../DecompressArchiveToChildFolderAction.cs | 2 +- .../FileSystem/CopyPathWithQuotesAction.cs | 2 +- .../Actions/Global/EditPathAction.cs | 2 +- .../Global/EnterCompactOverlayAction.cs | 2 +- .../Global/ExitCompactOverlayAction.cs | 2 +- .../Actions/Navigation/NavigateBackAction.cs | 4 +- .../Navigation/NavigateForwardAction.cs | 4 +- .../Actions/Navigation/NavigateUpAction.cs | 2 +- .../Navigation/OpenInNewWindowItemAction.cs | 2 +- .../Actions/Navigation/OpenNewPaneAction.cs | 4 +- .../Actions/Open/OpenPropertiesAction.cs | 2 +- .../Actions/Show/ToggleDetailsPaneAction.cs | 2 +- .../Actions/Show/ToggleInfoPaneAction.cs | 2 +- .../Actions/Show/TogglePreviewPaneAction.cs | 2 +- src/Files.App/Data/Commands/HotKey/HotKey.cs | 261 ++++++----- .../Data/Commands/HotKey/HotKeyCollection.cs | 124 ++++-- .../Data/Commands/HotKey/HotKeyHelpers.cs | 4 +- .../Data/Commands/HotKey/KeyModifiers.cs | 18 +- src/Files.App/Data/Commands/IRichCommand.cs | 3 +- .../Commands/{ => Manager}/CommandCodes.cs | 0 .../Data/Commands/Manager/CommandManager.cs | 46 +- .../Data/Commands/Manager/ICommandManager.cs | 3 + .../Data/Commands/ModifiableCommand.cs | 3 + src/Files.App/Data/Commands/NoneCommand.cs | 6 + src/Files.App/Data/Enums/SettingsPageKind.cs | 18 + .../Data/Items/ModifiableCommandHotKeyItem.cs | 59 +++ .../ContextMenuFlyoutItemViewModelBuilder.cs | 2 +- src/Files.App/Dialogs/SettingsDialog.xaml | 32 +- src/Files.App/Dialogs/SettingsDialog.xaml.cs | 21 +- .../Helpers/Win32/Win32PInvoke.Methods.cs | 10 + .../Settings/GeneralSettingsService.cs | 2 +- .../Settings/IGeneralSettingsService.cs | 2 +- src/Files.App/Strings/en-US/Resources.resw | 24 ++ .../KeyboardShortcut/KeyboardShortcut.cs | 14 +- .../ViewModels/Settings/ActionsViewModel.cs | 225 ++++++++++ src/Files.App/Views/Settings/ActionsPage.xaml | 408 ++++++++++++++++++ .../Views/Settings/ActionsPage.xaml.cs | 342 +++++++++++++++ .../Tests/SettingsTests.cs | 1 + 38 files changed, 1446 insertions(+), 216 deletions(-) rename src/Files.App/Data/Commands/{ => Manager}/CommandCodes.cs (100%) create mode 100644 src/Files.App/Data/Enums/SettingsPageKind.cs create mode 100644 src/Files.App/Data/Items/ModifiableCommandHotKeyItem.cs create mode 100644 src/Files.App/ViewModels/Settings/ActionsViewModel.cs create mode 100644 src/Files.App/Views/Settings/ActionsPage.xaml create mode 100644 src/Files.App/Views/Settings/ActionsPage.xaml.cs diff --git a/src/Files.App/Actions/Content/Archives/Decompress/DecompressArchiveToChildFolderAction.cs b/src/Files.App/Actions/Content/Archives/Decompress/DecompressArchiveToChildFolderAction.cs index 2b31acc6b357..7c087e0a0a30 100644 --- a/src/Files.App/Actions/Content/Archives/Decompress/DecompressArchiveToChildFolderAction.cs +++ b/src/Files.App/Actions/Content/Archives/Decompress/DecompressArchiveToChildFolderAction.cs @@ -40,7 +40,7 @@ protected override void Context_PropertyChanged(object? sender, PropertyChangedE private string ComputeLabel() { if (context.SelectedItems == null || context.SelectedItems.Count == 0) - return string.Empty; + return string.Format("BaseLayoutItemContextFlyoutExtractToChildFolder".GetLocalizedResource(), string.Empty); return context.SelectedItems.Count > 1 ? string.Format("BaseLayoutItemContextFlyoutExtractToChildFolder".GetLocalizedResource(), "*") diff --git a/src/Files.App/Actions/FileSystem/CopyPathWithQuotesAction.cs b/src/Files.App/Actions/FileSystem/CopyPathWithQuotesAction.cs index 5cf56cefd12d..ec6b23e06701 100644 --- a/src/Files.App/Actions/FileSystem/CopyPathWithQuotesAction.cs +++ b/src/Files.App/Actions/FileSystem/CopyPathWithQuotesAction.cs @@ -19,7 +19,7 @@ public RichGlyph Glyph => new RichGlyph(opacityStyle: "ColorIconCopyPath"); public HotKey HotKey - => new(Keys.C, KeyModifiers.MenuCtrl); + => new(Keys.C, KeyModifiers.CtrlAlt); public bool IsExecutable => context.HasSelection; diff --git a/src/Files.App/Actions/Global/EditPathAction.cs b/src/Files.App/Actions/Global/EditPathAction.cs index a1ea2c3039d3..f83fa4e2fe01 100644 --- a/src/Files.App/Actions/Global/EditPathAction.cs +++ b/src/Files.App/Actions/Global/EditPathAction.cs @@ -17,7 +17,7 @@ public HotKey HotKey => new(Keys.L, KeyModifiers.Ctrl); public HotKey SecondHotKey - => new(Keys.D, KeyModifiers.Menu); + => new(Keys.D, KeyModifiers.Alt); public EditPathAction() { diff --git a/src/Files.App/Actions/Global/EnterCompactOverlayAction.cs b/src/Files.App/Actions/Global/EnterCompactOverlayAction.cs index 33716116dcc3..de36c1ae5d2a 100644 --- a/src/Files.App/Actions/Global/EnterCompactOverlayAction.cs +++ b/src/Files.App/Actions/Global/EnterCompactOverlayAction.cs @@ -17,7 +17,7 @@ public RichGlyph Glyph => new(opacityStyle: "ColorIconEnterCompactOverlay"); public HotKey HotKey - => new(Keys.Up, KeyModifiers.MenuCtrl); + => new(Keys.Up, KeyModifiers.CtrlAlt); public string Description => "EnterCompactOverlayDescription".GetLocalizedResource(); diff --git a/src/Files.App/Actions/Global/ExitCompactOverlayAction.cs b/src/Files.App/Actions/Global/ExitCompactOverlayAction.cs index ab0b4c7b7ae7..9b83bc244e85 100644 --- a/src/Files.App/Actions/Global/ExitCompactOverlayAction.cs +++ b/src/Files.App/Actions/Global/ExitCompactOverlayAction.cs @@ -16,7 +16,7 @@ public RichGlyph Glyph => new(opacityStyle: "ColorIconExitCompactOverlay"); public HotKey HotKey - => new(Keys.Down, KeyModifiers.MenuCtrl); + => new(Keys.Down, KeyModifiers.CtrlAlt); public string Description => "ExitCompactOverlayDescription".GetLocalizedResource(); diff --git a/src/Files.App/Actions/Navigation/NavigateBackAction.cs b/src/Files.App/Actions/Navigation/NavigateBackAction.cs index 972a4b0aa45f..fd07e59546ce 100644 --- a/src/Files.App/Actions/Navigation/NavigateBackAction.cs +++ b/src/Files.App/Actions/Navigation/NavigateBackAction.cs @@ -14,7 +14,7 @@ public string Description => "NavigateBackDescription".GetLocalizedResource(); public HotKey HotKey - => new(Keys.Left, KeyModifiers.Menu); + => new(Keys.Left, KeyModifiers.Alt); public HotKey SecondHotKey => new(Keys.Back); @@ -23,7 +23,7 @@ public HotKey ThirdHotKey => new(Keys.Mouse4); public HotKey MediaHotKey - => new(Keys.GoBack, false); + => new(Keys.GoBack, KeyModifiers.None, false); public RichGlyph Glyph => new("\uE72B"); diff --git a/src/Files.App/Actions/Navigation/NavigateForwardAction.cs b/src/Files.App/Actions/Navigation/NavigateForwardAction.cs index e4cd09c32486..618dceff9859 100644 --- a/src/Files.App/Actions/Navigation/NavigateForwardAction.cs +++ b/src/Files.App/Actions/Navigation/NavigateForwardAction.cs @@ -14,13 +14,13 @@ public string Description => "NavigateForwardDescription".GetLocalizedResource(); public HotKey HotKey - => new(Keys.Right, KeyModifiers.Menu); + => new(Keys.Right, KeyModifiers.Alt); public HotKey SecondHotKey => new(Keys.Mouse5); public HotKey MediaHotKey - => new(Keys.GoForward, false); + => new(Keys.GoForward, KeyModifiers.None, false); public RichGlyph Glyph => new("\uE72A"); diff --git a/src/Files.App/Actions/Navigation/NavigateUpAction.cs b/src/Files.App/Actions/Navigation/NavigateUpAction.cs index 893ea943ff56..3f233d7d5a1e 100644 --- a/src/Files.App/Actions/Navigation/NavigateUpAction.cs +++ b/src/Files.App/Actions/Navigation/NavigateUpAction.cs @@ -14,7 +14,7 @@ public string Description => "NavigateUpDescription".GetLocalizedResource(); public HotKey HotKey - => new(Keys.Up, KeyModifiers.Menu); + => new(Keys.Up, KeyModifiers.Alt); public RichGlyph Glyph => new("\uE74A"); diff --git a/src/Files.App/Actions/Navigation/OpenInNewWindowItemAction.cs b/src/Files.App/Actions/Navigation/OpenInNewWindowItemAction.cs index 7f57c32efa8d..b23cf3be56dc 100644 --- a/src/Files.App/Actions/Navigation/OpenInNewWindowItemAction.cs +++ b/src/Files.App/Actions/Navigation/OpenInNewWindowItemAction.cs @@ -18,7 +18,7 @@ public string Description => "OpenInNewWindowDescription".GetLocalizedResource(); public HotKey HotKey - => new(Keys.Enter, KeyModifiers.MenuCtrl); + => new(Keys.Enter, KeyModifiers.CtrlAlt); public RichGlyph Glyph => new(opacityStyle: "ColorIconOpenInNewWindow"); diff --git a/src/Files.App/Actions/Navigation/OpenNewPaneAction.cs b/src/Files.App/Actions/Navigation/OpenNewPaneAction.cs index 03286ac2bc04..18de2a2f281b 100644 --- a/src/Files.App/Actions/Navigation/OpenNewPaneAction.cs +++ b/src/Files.App/Actions/Navigation/OpenNewPaneAction.cs @@ -14,10 +14,10 @@ public string Description => "OpenNewPaneDescription".GetLocalizedResource(); public HotKey HotKey - => new(Keys.OemPlus, KeyModifiers.MenuShift); + => new(Keys.OemPlus, KeyModifiers.AltShift); public HotKey SecondHotKey - => new(Keys.Add, KeyModifiers.MenuShift); + => new(Keys.Add, KeyModifiers.AltShift); public RichGlyph Glyph => new(opacityStyle: "ColorIconOpenNewPane"); diff --git a/src/Files.App/Actions/Open/OpenPropertiesAction.cs b/src/Files.App/Actions/Open/OpenPropertiesAction.cs index 679f3e8056f4..bb22d3993195 100644 --- a/src/Files.App/Actions/Open/OpenPropertiesAction.cs +++ b/src/Files.App/Actions/Open/OpenPropertiesAction.cs @@ -17,7 +17,7 @@ public RichGlyph Glyph => new(opacityStyle: "ColorIconProperties"); public HotKey HotKey - => new(Keys.Enter, KeyModifiers.Menu); + => new(Keys.Enter, KeyModifiers.Alt); public bool IsExecutable => context.PageType is not ContentPageTypes.Home && diff --git a/src/Files.App/Actions/Show/ToggleDetailsPaneAction.cs b/src/Files.App/Actions/Show/ToggleDetailsPaneAction.cs index 6b7025d0417a..d65410d69c21 100644 --- a/src/Files.App/Actions/Show/ToggleDetailsPaneAction.cs +++ b/src/Files.App/Actions/Show/ToggleDetailsPaneAction.cs @@ -18,7 +18,7 @@ public RichGlyph Glyph => new(opacityStyle: "ColorIconRightPane"); public HotKey HotKey - => new(Keys.D, KeyModifiers.MenuCtrl); + => new(Keys.D, KeyModifiers.CtrlAlt); public bool IsOn => viewModel.IsEnabled; diff --git a/src/Files.App/Actions/Show/ToggleInfoPaneAction.cs b/src/Files.App/Actions/Show/ToggleInfoPaneAction.cs index 2a6b1108f69c..cca75d70ae75 100644 --- a/src/Files.App/Actions/Show/ToggleInfoPaneAction.cs +++ b/src/Files.App/Actions/Show/ToggleInfoPaneAction.cs @@ -17,7 +17,7 @@ public RichGlyph Glyph => new(opacityStyle: "ColorIconRightPane"); public HotKey HotKey - => new(Keys.I, KeyModifiers.MenuCtrl); + => new(Keys.I, KeyModifiers.CtrlAlt); public bool IsOn => viewModel.IsEnabled; diff --git a/src/Files.App/Actions/Show/TogglePreviewPaneAction.cs b/src/Files.App/Actions/Show/TogglePreviewPaneAction.cs index 48c8caf9a927..349e4d616261 100644 --- a/src/Files.App/Actions/Show/TogglePreviewPaneAction.cs +++ b/src/Files.App/Actions/Show/TogglePreviewPaneAction.cs @@ -18,7 +18,7 @@ public RichGlyph Glyph => new(opacityStyle: "ColorIconRightPane"); public HotKey HotKey - => new(Keys.P, KeyModifiers.MenuCtrl); + => new(Keys.P, KeyModifiers.CtrlAlt); public bool IsOn => viewModel.IsEnabled; diff --git a/src/Files.App/Data/Commands/HotKey/HotKey.cs b/src/Files.App/Data/Commands/HotKey/HotKey.cs index 8b90f42fc04e..8df12097cd13 100644 --- a/src/Files.App/Data/Commands/HotKey/HotKey.cs +++ b/src/Files.App/Data/Commands/HotKey/HotKey.cs @@ -8,42 +8,45 @@ namespace Files.App.Data.Commands { - [DebuggerDisplay("{Code}")] + /// + /// Represents hot key. + /// + [DebuggerDisplay("{LocalizedLabel}")] public readonly struct HotKey : IEquatable { - public static readonly FrozenDictionary modifiers = new Dictionary() + public static FrozenDictionary LocalizedModifiers { get; } = new Dictionary() { - [KeyModifiers.Menu] = GetKeyString("Menu"), - [KeyModifiers.Ctrl] = GetKeyString("Control"), - [KeyModifiers.Shift] = GetKeyString("Shift"), - [KeyModifiers.Win] = GetKeyString("Windows"), + [KeyModifiers.Alt] = GetLocalizedKey("Menu"), + [KeyModifiers.Ctrl] = GetLocalizedKey("Control"), + [KeyModifiers.Shift] = GetLocalizedKey("Shift"), + [KeyModifiers.Win] = GetLocalizedKey("Windows"), }.ToFrozenDictionary(); - public static readonly FrozenDictionary keys = new Dictionary() + public static FrozenDictionary LocalizedKeys { get; } = new Dictionary() { - [Keys.Enter] = GetKeyString("Enter"), - [Keys.Space] = GetKeyString("Space"), - [Keys.Escape] = GetKeyString("Escape"), - [Keys.Back] = GetKeyString("Back"), - [Keys.Tab] = GetKeyString("Tab"), - [Keys.Insert] = GetKeyString("Insert"), - [Keys.Delete] = GetKeyString("Delete"), - [Keys.Left] = GetKeyString("Left"), - [Keys.Right] = GetKeyString("Right"), - [Keys.Down] = GetKeyString("Down"), - [Keys.Up] = GetKeyString("Up"), - [Keys.Home] = GetKeyString("Home"), - [Keys.End] = GetKeyString("End"), - [Keys.PageDown] = GetKeyString("PageDown"), - [Keys.PageUp] = GetKeyString("PageUp"), - [Keys.Separator] = GetKeyString("Separator"), - [Keys.Pause] = GetKeyString("Pause"), - [Keys.Sleep] = GetKeyString("Sleep"), - [Keys.Clear] = GetKeyString("Clear"), - [Keys.Print] = GetKeyString("Print"), - [Keys.Help] = GetKeyString("Help"), - [Keys.Mouse4] = GetKeyString("Mouse4"), - [Keys.Mouse5] = GetKeyString("Mouse5"), + [Keys.Enter] = GetLocalizedKey("Enter"), + [Keys.Space] = GetLocalizedKey("Space"), + [Keys.Escape] = GetLocalizedKey("Escape"), + [Keys.Back] = GetLocalizedKey("Back"), + [Keys.Tab] = GetLocalizedKey("Tab"), + [Keys.Insert] = GetLocalizedKey("Insert"), + [Keys.Delete] = GetLocalizedKey("Delete"), + [Keys.Left] = GetLocalizedKey("Left"), + [Keys.Right] = GetLocalizedKey("Right"), + [Keys.Down] = GetLocalizedKey("Down"), + [Keys.Up] = GetLocalizedKey("Up"), + [Keys.Home] = GetLocalizedKey("Home"), + [Keys.End] = GetLocalizedKey("End"), + [Keys.PageDown] = GetLocalizedKey("PageDown"), + [Keys.PageUp] = GetLocalizedKey("PageUp"), + [Keys.Separator] = GetLocalizedKey("Separator"), + [Keys.Pause] = GetLocalizedKey("Pause"), + [Keys.Sleep] = GetLocalizedKey("Sleep"), + [Keys.Clear] = GetLocalizedKey("Clear"), + [Keys.Print] = GetLocalizedKey("Print"), + [Keys.Help] = GetLocalizedKey("Help"), + [Keys.Mouse4] = GetLocalizedKey("Mouse4"), + [Keys.Mouse5] = GetLocalizedKey("Mouse5"), [Keys.F1] = "F1", [Keys.F2] = "F2", [Keys.F3] = "F3", @@ -78,16 +81,16 @@ namespace Files.App.Data.Commands [Keys.Number7] = "7", [Keys.Number8] = "8", [Keys.Number9] = "9", - [Keys.Pad0] = GetKeyString("Pad0"), - [Keys.Pad1] = GetKeyString("Pad1"), - [Keys.Pad2] = GetKeyString("Pad2"), - [Keys.Pad3] = GetKeyString("Pad3"), - [Keys.Pad4] = GetKeyString("Pad4"), - [Keys.Pad5] = GetKeyString("Pad5"), - [Keys.Pad6] = GetKeyString("Pad6"), - [Keys.Pad7] = GetKeyString("Pad7"), - [Keys.Pad8] = GetKeyString("Pad8"), - [Keys.Pad9] = GetKeyString("Pad9"), + [Keys.Pad0] = GetLocalizedKey("Pad0"), + [Keys.Pad1] = GetLocalizedKey("Pad1"), + [Keys.Pad2] = GetLocalizedKey("Pad2"), + [Keys.Pad3] = GetLocalizedKey("Pad3"), + [Keys.Pad4] = GetLocalizedKey("Pad4"), + [Keys.Pad5] = GetLocalizedKey("Pad5"), + [Keys.Pad6] = GetLocalizedKey("Pad6"), + [Keys.Pad7] = GetLocalizedKey("Pad7"), + [Keys.Pad8] = GetLocalizedKey("Pad8"), + [Keys.Pad9] = GetLocalizedKey("Pad9"), [Keys.A] = "A", [Keys.B] = "B", [Keys.C] = "C", @@ -132,55 +135,75 @@ namespace Files.App.Data.Commands [Keys.OemPeriod] = GetKeyCharacter(Forms.Keys.OemPeriod), [Keys.Oem102] = GetKeyCharacter(Forms.Keys.Oem102), [Keys.OemClear] = GetKeyCharacter(Forms.Keys.OemClear), - [Keys.Application] = GetKeyString("Application"), - [Keys.Application1] = GetKeyString("Application1"), - [Keys.Application2] = GetKeyString("Application2"), - [Keys.Mail] = GetKeyString("Mail"), - [Keys.GoHome] = GetKeyString("BrowserGoHome"), - [Keys.GoBack] = GetKeyString("BrowserGoBack"), - [Keys.GoForward] = GetKeyString("BrowserGoForward"), - [Keys.Refresh] = GetKeyString("BrowserRefresh"), - [Keys.BrowserStop] = GetKeyString("BrowserStop"), - [Keys.Search] = GetKeyString("BrowserSearch"), - [Keys.Favorites] = GetKeyString("BrowserFavorites"), - [Keys.PlayPause] = GetKeyString("MediaPlayPause"), - [Keys.MediaStop] = GetKeyString("MediaStop"), - [Keys.PreviousTrack] = GetKeyString("MediaPreviousTrack"), - [Keys.NextTrack] = GetKeyString("MediaNextTrack"), - [Keys.MediaSelect] = GetKeyString("MediaSelect"), - [Keys.Mute] = GetKeyString("MediaMute"), - [Keys.VolumeDown] = GetKeyString("MediaVolumeDown"), - [Keys.VolumeUp] = GetKeyString("MediaVolumeUp"), + [Keys.Application] = GetLocalizedKey("Application"), + [Keys.Application1] = GetLocalizedKey("Application1"), + [Keys.Application2] = GetLocalizedKey("Application2"), + [Keys.Mail] = GetLocalizedKey("Mail"), + [Keys.GoHome] = GetLocalizedKey("BrowserGoHome"), + [Keys.GoBack] = GetLocalizedKey("BrowserGoBack"), + [Keys.GoForward] = GetLocalizedKey("BrowserGoForward"), + [Keys.Refresh] = GetLocalizedKey("BrowserRefresh"), + [Keys.BrowserStop] = GetLocalizedKey("BrowserStop"), + [Keys.Search] = GetLocalizedKey("BrowserSearch"), + [Keys.Favorites] = GetLocalizedKey("BrowserFavorites"), + [Keys.PlayPause] = GetLocalizedKey("MediaPlayPause"), + [Keys.MediaStop] = GetLocalizedKey("MediaStop"), + [Keys.PreviousTrack] = GetLocalizedKey("MediaPreviousTrack"), + [Keys.NextTrack] = GetLocalizedKey("MediaNextTrack"), + [Keys.MediaSelect] = GetLocalizedKey("MediaSelect"), + [Keys.Mute] = GetLocalizedKey("MediaMute"), + [Keys.VolumeDown] = GetLocalizedKey("MediaVolumeDown"), + [Keys.VolumeUp] = GetLocalizedKey("MediaVolumeUp"), }.ToFrozenDictionary(); + /// + /// Gets the none value. + /// public static HotKey None { get; } = new(Keys.None, KeyModifiers.None); + /// + /// Gets the value that indicates whether the hotkey is none. + /// public bool IsNone => Key is Keys.None && Modifier is KeyModifiers.None; + /// + /// Gets the value that indicates whether the key should be visible. + /// public bool IsVisible { get; init; } + /// + /// Gets the key. + /// public Keys Key { get; } + + /// + /// Gets the modifier. + /// public KeyModifiers Modifier { get; } - public string Code + /// + /// Gets the raw label of the hotkey. + /// + /// + /// For example, this is "Ctrl+A" and "Ctrl+Menu+C" + /// + public string RawLabel { get { return (Key, Modifier) switch { (Keys.None, KeyModifiers.None) => string.Empty, - (Keys.None, _) => $"{GetVisibleCode(IsVisible)}{GetModifierCode(Modifier)}", - (_, KeyModifiers.None) => $"{GetVisibleCode(IsVisible)}{Key}", - _ => $"{GetVisibleCode(IsVisible)}{GetModifierCode(Modifier)}+{Key}", + (Keys.None, _) => $"{GetModifierCode(Modifier)}", + (_, KeyModifiers.None) => $"{Key}", + _ => $"{GetModifierCode(Modifier)}+{Key}", }; - static string GetVisibleCode(bool isVisible) => isVisible ? string.Empty : "!"; - static string GetModifierCode(KeyModifiers modifiers) { StringBuilder builder = new(); - if (modifiers.HasFlag(KeyModifiers.Menu)) - builder.Append($"+{KeyModifiers.Menu}"); + if (modifiers.HasFlag(KeyModifiers.Alt)) + builder.Append($"+{KeyModifiers.Alt}"); if (modifiers.HasFlag(KeyModifiers.Ctrl)) builder.Append($"+{KeyModifiers.Ctrl}"); if (modifiers.HasFlag(KeyModifiers.Shift)) @@ -193,7 +216,13 @@ static string GetModifierCode(KeyModifiers modifiers) } } - public string Label + /// + /// Gets the localized label of the hotkey to shown in the UI. + /// + /// + /// For example, this is "Ctrl+A" and "Ctrl+Alt+C" + /// + public string LocalizedLabel { get { @@ -201,29 +230,34 @@ public string Label { (Keys.None, KeyModifiers.None) => string.Empty, (Keys.None, _) => GetModifierCode(Modifier), - (_, KeyModifiers.None) => keys[Key], - _ => $"{GetModifierCode(Modifier)}+{keys[Key]}", + (_, KeyModifiers.None) => LocalizedKeys[Key], + _ => $"{GetModifierCode(Modifier)}+{LocalizedKeys[Key]}", }; static string GetModifierCode(KeyModifiers modifier) { StringBuilder builder = new(); - if (modifier.HasFlag(KeyModifiers.Menu)) - builder.Append($"+{modifiers[KeyModifiers.Menu]}"); + if (modifier.HasFlag(KeyModifiers.Alt)) + builder.Append($"+{LocalizedModifiers[KeyModifiers.Alt]}"); if (modifier.HasFlag(KeyModifiers.Ctrl)) - builder.Append($"+{modifiers[KeyModifiers.Ctrl]}"); + builder.Append($"+{LocalizedModifiers[KeyModifiers.Ctrl]}"); if (modifier.HasFlag(KeyModifiers.Shift)) - builder.Append($"+{modifiers[KeyModifiers.Shift]}"); + builder.Append($"+{LocalizedModifiers[KeyModifiers.Shift]}"); if (modifier.HasFlag(KeyModifiers.Win)) - builder.Append($"+{modifiers[KeyModifiers.Win]}"); + builder.Append($"+{LocalizedModifiers[KeyModifiers.Win]}"); builder.Remove(0, 1); return builder.ToString(); } } } - public HotKey(Keys key, bool isVisible = true) : this(key, KeyModifiers.None, isVisible) {} - public HotKey(Keys key, KeyModifiers modifier, bool isVisible = true) + /// + /// Initializes an instance of . + /// + /// A key + /// A modifier + /// A value that indicates the hotkey should be available. + public HotKey(Keys key, KeyModifiers modifier = KeyModifiers.None, bool isVisible = true) { if (!Enum.IsDefined(key) || !Enum.IsDefined(modifier)) return; @@ -233,67 +267,76 @@ public HotKey(Keys key, KeyModifiers modifier, bool isVisible = true) Modifier = modifier; } - public void Deconstruct(out Keys key, out KeyModifiers modifier) - => (key, modifier) = (Key, Modifier); - public void Deconstruct(out Keys key, out KeyModifiers modifier, out bool isVisible) - => (key, modifier, isVisible) = (Key, Modifier, IsVisible); - - public static HotKey Parse(string code) + /// + /// Parses humanized hotkey code with separators. + /// + /// Humanized code to parse. + /// Whether the code is localized. + /// Humanized code with a format . + public static HotKey Parse(string code, bool localized = true) { var key = Keys.None; var modifier = KeyModifiers.None; bool isVisible = true; code = code.Trim(); - if (code.StartsWith('!')) - { - isVisible = false; - code = code.Remove(0, 1); - } - var parts = code.Split('+').Select(part => part.Trim()); + foreach (var part in parts) { - if (Enum.TryParse(part, true, out Keys partKey)) - key = partKey; - if (Enum.TryParse(part, true, out KeyModifiers partModifier)) - modifier |= partModifier; + if (localized) + { + key |= LocalizedKeys.FirstOrDefault(x => x.Value == part).Key; + modifier |= LocalizedModifiers.FirstOrDefault(x => x.Value == part).Key; + } + else + { + if (Enum.TryParse(part, true, out Keys partKey)) + key = partKey; + if (Enum.TryParse(part, true, out KeyModifiers partModifier)) + modifier |= partModifier; + } } + return new(key, modifier, isVisible); } - public HotKeyCollection AsCollection() => new(this); + /// + /// Converts this instance into a instance. + /// + /// + public HotKeyCollection AsCollection() + { + return new(this); + } - public static implicit operator string(HotKey hotKey) => hotKey.Label; + // Operator overloads + public static implicit operator string(HotKey hotKey) => hotKey.LocalizedLabel; public static bool operator ==(HotKey a, HotKey b) => a.Equals(b); public static bool operator !=(HotKey a, HotKey b) => !a.Equals(b); - public override string ToString() => Label; + // Default methods + public override string ToString() => LocalizedLabel; public override int GetHashCode() => (Key, Modifier, IsVisible).GetHashCode(); public override bool Equals(object? other) => other is HotKey hotKey && Equals(hotKey); public bool Equals(HotKey other) => (other.Key, other.Modifier, other.IsVisible).Equals((Key, Modifier, IsVisible)); - private static string GetKeyString(string key) => $"Key/{key}".GetLocalizedResource(); + // Private methods + + private static string GetLocalizedKey(string key) + { + return $"Key/{key}".GetLocalizedResource(); + } private static string GetKeyCharacter(Forms.Keys key) { var buffer = new StringBuilder(256); var state = new byte[256]; - _ = ToUnicode((uint)key, 0, state, buffer, 256, 0); + _ = Win32PInvoke.ToUnicode((uint)key, 0, state, buffer, 256, 0); + return buffer.ToString(); } - - [DllImport("user32.dll")] - private static extern int ToUnicode - ( - uint virtualKeyCode, - uint scanCode, - byte[] keyboardState, - [Out, MarshalAs(UnmanagedType.LPWStr, SizeConst = 64)] StringBuilder receivingBuffer, - int bufferSize, - uint flags - ); } } diff --git a/src/Files.App/Data/Commands/HotKey/HotKeyCollection.cs b/src/Files.App/Data/Commands/HotKey/HotKeyCollection.cs index dd7a4ae332d0..02c1352dd686 100644 --- a/src/Files.App/Data/Commands/HotKey/HotKeyCollection.cs +++ b/src/Files.App/Data/Commands/HotKey/HotKeyCollection.cs @@ -1,65 +1,133 @@ -using Files.App.Actions; -using System; -using System.Collections; -using System.Collections.Generic; +// Copyright (c) 2024 Files Community +// Licensed under the MIT License. See the LICENSE. + using System.Collections.Immutable; -using System.Diagnostics; -using System.Linq; namespace Files.App.Data.Commands { - [DebuggerDisplay("{Code}")] + /// + /// Represents immutable collection of . + /// + [DebuggerDisplay("{LocalizedLabel}")] public readonly struct HotKeyCollection : IEnumerable, IEquatable { - private static readonly char[] parseSeparators = [',', ';', ' ', '\t']; + // Fields + + private static readonly char[] parseSeparators = [' ', '\t']; private readonly ImmutableArray hotKeys; + // Properties + + /// + /// Gets empty instance of . + /// public static HotKeyCollection Empty { get; } = new(ImmutableArray.Empty); public HotKey this[int index] => index >= 0 && index < hotKeys.Length ? hotKeys[index] : HotKey.None; - public bool IsEmpty => hotKeys.IsDefaultOrEmpty; - public int Length => hotKeys.Length; + /// + /// Gets an value that indicates whether the hotkey collection is null. + /// + public bool IsEmpty + => hotKeys.IsDefaultOrEmpty; + + /// + public int Length + => hotKeys.Length; + + /// + /// Gets the raw code of the hotkey, separated by a space. + /// + /// + /// For example, this is "Ctrl+A Ctrl+Menu+C" + /// + public string RawLabel + => string.Join(' ', hotKeys.Select(hotKey => hotKey.RawLabel)); + + /// + /// Gets the humanized label of the hotkey to shown in the UI, separated by a command and double space. + /// + /// + /// For example, this is "Ctrl+A, Ctrl+Alt+C" + /// + public string LocalizedLabel + => string.Join(", ", hotKeys.Where(hotKey => hotKey.IsVisible).Select(hotKey => hotKey.LocalizedLabel)); + + // Constructors + + public HotKeyCollection() + { + hotKeys = []; + } + + public HotKeyCollection(params HotKey[] hotKeys) + { + this.hotKeys = Standardize(hotKeys); + } - public string Code => string.Join(',', hotKeys.Select(hotKey => hotKey.Code)); - public string Label => string.Join(", ", hotKeys.Where(hotKey => hotKey.IsVisible).Select(hotKey => hotKey.Label)); + public HotKeyCollection(IEnumerable hotKeys) + { + this.hotKeys = Standardize(hotKeys); + } - public HotKeyCollection() => hotKeys = []; - public HotKeyCollection(params HotKey[] hotKeys) => this.hotKeys = Clean(hotKeys); - public HotKeyCollection(IEnumerable hotKeys) => this.hotKeys = Clean(hotKeys); + // Operator overloads public static bool operator ==(HotKeyCollection hotKeysA, HotKeyCollection hotKeysB) => hotKeysA.Equals(hotKeysB); public static bool operator !=(HotKeyCollection hotKeysA, HotKeyCollection hotKeysB) => !hotKeysA.Equals(hotKeysB); - public static HotKeyCollection Parse(string code) + /// + /// Parses humanized hotkey code collection with separators. + /// + /// Humanized code to parse. + /// Whether the code is localized. + /// Humanized code with a format . + public static HotKeyCollection Parse(string code, bool localized = true) { var hotKeys = code - .Replace("!", " !") .Split(parseSeparators) .Select(part => part.Trim()) - .Select(HotKey.Parse); + .Select(x => HotKey.Parse(x, localized)); + return new(hotKeys); } - public void Contains(HotKey hotKey) => hotKeys.Contains(hotKey); + /// + public bool Contains(HotKey hotKey) + => hotKeys.Contains(hotKey); + + /// + IEnumerator IEnumerable.GetEnumerator() + => GetEnumerator(); + + /// + public IEnumerator GetEnumerator() + => (hotKeys as IEnumerable).GetEnumerator(); + + // Default methods + + public override string ToString() + => LocalizedLabel; + + public override int GetHashCode() + => hotKeys.GetHashCode(); + + public override bool Equals(object? other) + => other is HotKeyCollection hotKeys && Equals(hotKeys); - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - public IEnumerator GetEnumerator() => (hotKeys as IEnumerable).GetEnumerator(); + public bool Equals(HotKeyCollection other) + => hotKeys.SequenceEqual(other.hotKeys); - public override string ToString() => Label; - public override int GetHashCode() => hotKeys.GetHashCode(); - public override bool Equals(object? other) => other is HotKeyCollection hotKeys && Equals(hotKeys); - public bool Equals(HotKeyCollection other) => hotKeys.SequenceEqual(other.hotKeys); + // Private methods - private static ImmutableArray Clean(IEnumerable hotKeys) + private static ImmutableArray Standardize(IEnumerable hotKeys) { return hotKeys .Distinct() .Where(hotKey => !hotKey.IsNone) - .GroupBy(hotKey => hotKey with { IsVisible = true}) + .GroupBy(hotKey => hotKey with { IsVisible = true }) .Select(group => group.OrderBy(hotKey => hotKey.IsVisible).Last()) .ToImmutableArray(); } } -} +} \ No newline at end of file diff --git a/src/Files.App/Data/Commands/HotKey/HotKeyHelpers.cs b/src/Files.App/Data/Commands/HotKey/HotKeyHelpers.cs index a85e2ffcc6ca..fcd9d74391cf 100644 --- a/src/Files.App/Data/Commands/HotKey/HotKeyHelpers.cs +++ b/src/Files.App/Data/Commands/HotKey/HotKeyHelpers.cs @@ -25,7 +25,9 @@ public static KeyModifiers GetCurrentKeyModifiers() return (KeyModifiers)modifiers; static bool IsPressed(VirtualKey key) - => InputKeyboardSource.GetKeyStateForCurrentThread(key).HasFlag(CoreVirtualKeyStates.Down); + { + return InputKeyboardSource.GetKeyStateForCurrentThread(key).HasFlag(CoreVirtualKeyStates.Down); + } } } } diff --git a/src/Files.App/Data/Commands/HotKey/KeyModifiers.cs b/src/Files.App/Data/Commands/HotKey/KeyModifiers.cs index aaf7b60cb19f..0ec78e4841b7 100644 --- a/src/Files.App/Data/Commands/HotKey/KeyModifiers.cs +++ b/src/Files.App/Data/Commands/HotKey/KeyModifiers.cs @@ -1,8 +1,6 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. -using System; - namespace Files.App.Data.Commands { [Flags] @@ -10,19 +8,19 @@ public enum KeyModifiers : ushort { None = 0x0000, Ctrl = 0x0001, - Menu = 0x0002, + Alt = 0x0002, Shift = 0x0004, Win = 0x0008, - MenuCtrl = Ctrl + Menu, + CtrlAlt = Ctrl + Alt, CtrlShift = Ctrl + Shift, CtrlWin = Ctrl + Win, - MenuShift = Menu + Shift, - MenuWin = Menu + Win, + AltShift = Alt + Shift, + AltWin = Alt + Win, ShiftWin = Shift + Win, - MenuCtrlShift = Ctrl + Menu + Shift, - MenuCtrlWin = Ctrl + Menu + Win, + CtrlAltShift = Ctrl + Alt + Shift, + CtrlAltWin = Ctrl + Alt + Win, CtrlShiftWin = Ctrl + Shift + Win, - MenuShiftWin = Menu + Shift + Win, - MenuCtrlShiftWin = Ctrl + Menu + Shift + Win, + AltShiftWin = Alt + Shift + Win, + AltCtrlShiftWin = Ctrl + Alt + Shift + Win, } } diff --git a/src/Files.App/Data/Commands/IRichCommand.cs b/src/Files.App/Data/Commands/IRichCommand.cs index 63f712117b9e..717bd53b3506 100644 --- a/src/Files.App/Data/Commands/IRichCommand.cs +++ b/src/Files.App/Data/Commands/IRichCommand.cs @@ -4,8 +4,6 @@ using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Input; -using System.ComponentModel; -using System.Threading.Tasks; using System.Windows.Input; namespace Files.App.Data.Commands @@ -28,6 +26,7 @@ public interface IRichCommand : ICommand, INotifyPropertyChanging, INotifyProper bool IsCustomHotKeys { get; } string? HotKeyText { get; } HotKeyCollection HotKeys { get; set; } + HotKeyCollection DefaultHotKeys { get; } bool IsToggle { get; } bool IsOn { get; set; } diff --git a/src/Files.App/Data/Commands/CommandCodes.cs b/src/Files.App/Data/Commands/Manager/CommandCodes.cs similarity index 100% rename from src/Files.App/Data/Commands/CommandCodes.cs rename to src/Files.App/Data/Commands/Manager/CommandCodes.cs diff --git a/src/Files.App/Data/Commands/Manager/CommandManager.cs b/src/Files.App/Data/Commands/Manager/CommandManager.cs index 0ecd844f6a3e..bb9a0e06ff06 100644 --- a/src/Files.App/Data/Commands/Manager/CommandManager.cs +++ b/src/Files.App/Data/Commands/Manager/CommandManager.cs @@ -13,7 +13,11 @@ namespace Files.App.Data.Commands { internal sealed class CommandManager : ICommandManager { - private readonly IGeneralSettingsService settings = Ioc.Default.GetRequiredService(); + // Dependency injections + + private IGeneralSettingsService GeneralSettingsService { get; } = Ioc.Default.GetRequiredService(); + + // Fields private readonly FrozenDictionary commands; private ImmutableDictionary hotKeys = new Dictionary().ToImmutableDictionary(); @@ -199,7 +203,7 @@ public CommandManager() .Append(new NoneCommand()) .ToFrozenDictionary(command => command.Code); - settings.PropertyChanged += Settings_PropertyChanged; + GeneralSettingsService.PropertyChanged += Settings_PropertyChanged; UpdateHotKeys(); } @@ -363,19 +367,28 @@ public CommandManager() [CommandCodes.OpenAllTaggedItems] = new OpenAllTaggedActions(), }; + /// + /// Replace default hotkey collection with customized one(s) if exists. + /// private void UpdateHotKeys() { + if (GeneralSettingsService.Actions is null) + return; + var useds = new HashSet(); var customs = new Dictionary(); - foreach (var custom in settings.Actions) + + // Get custom hotkeys from the user settings + foreach (var custom in GeneralSettingsService.Actions) { if (Enum.TryParse(custom.Key, true, out CommandCodes code)) { if (code is CommandCodes.None) continue; - var hotKeys = new HotKeyCollection(HotKeyCollection.Parse(custom.Value).Except(useds)); + // Parse and add the hotkeys + var hotKeys = new HotKeyCollection(HotKeyCollection.Parse(custom.Value, false).Except(useds)); customs.Add(code, new(hotKeys)); foreach (var hotKey in hotKeys) @@ -394,6 +407,7 @@ private void UpdateHotKeys() ? customs[command.Code] : new HotKeyCollection(GetHotKeys(command.Action).Except(useds)); + // Replace with custom hotkeys command.UpdateHotKeys(isCustom, hotkeys); } @@ -402,7 +416,7 @@ private void UpdateHotKeys() .ToImmutableDictionary(item => item.HotKey, item => item.Command); } - private static HotKeyCollection GetHotKeys(IAction action) + public static HotKeyCollection GetHotKeys(IAction action) => new(action.HotKey, action.SecondHotKey, action.ThirdHotKey, action.MediaHotKey); private void Settings_PropertyChanged(object? sender, PropertyChangedEventArgs e) @@ -411,9 +425,12 @@ private void Settings_PropertyChanged(object? sender, PropertyChangedEventArgs e UpdateHotKeys(); } + // TODO: Move to a new file [DebuggerDisplay("Command {Code}")] internal sealed class ActionCommand : ObservableObject, IRichCommand { + private IGeneralSettingsService GeneralSettingsService { get; } = Ioc.Default.GetRequiredService(); + public event EventHandler? CanExecuteChanged; private readonly CommandManager manager; @@ -439,7 +456,7 @@ internal sealed class ActionCommand : ObservableObject, IRichCommand { get { - string text = HotKeys.Label; + string text = HotKeys.LocalizedLabel; if (string.IsNullOrEmpty(text)) return null; return text; @@ -456,19 +473,21 @@ public HotKeyCollection HotKeys return; string code = Code.ToString(); - var customs = new Dictionary(manager.settings.Actions); + var customs = new Dictionary(GeneralSettingsService.Actions); if (!customs.ContainsKey(code)) - customs.Add(code, value.Code); + customs.Add(code, value.RawLabel); else if (value != GetHotKeys(Action)) - customs[code] = value.Code; + customs[code] = value.RawLabel; else customs.Remove(code); - manager.settings.Actions = customs; + GeneralSettingsService.Actions = customs; } } + public HotKeyCollection DefaultHotKeys { get; } + public bool IsToggle => Action is IToggleAction; public bool IsOn @@ -491,7 +510,8 @@ public ActionCommand(CommandManager manager, CommandCodes code, IAction action) Icon = action.Glyph.ToIcon(); FontIcon = action.Glyph.ToFontIcon(); OpacityStyle = action.Glyph.ToOpacityStyle(); - hotKeys = GetHotKeys(action); + hotKeys = CommandManager.GetHotKeys(action); + DefaultHotKeys = CommandManager.GetHotKeys(action); if (action is INotifyPropertyChanging notifyPropertyChanging) notifyPropertyChanging.PropertyChanging += Action_PropertyChanging; @@ -520,9 +540,9 @@ public void ResetHotKeys() if (!IsCustomHotKeys) return; - var customs = new Dictionary(manager.settings.Actions); + var customs = new Dictionary(GeneralSettingsService.Actions); customs.Remove(Code.ToString()); - manager.settings.Actions = customs; + GeneralSettingsService.Actions = customs; } internal void UpdateHotKeys(bool isCustom, HotKeyCollection hotKeys) diff --git a/src/Files.App/Data/Commands/Manager/ICommandManager.cs b/src/Files.App/Data/Commands/Manager/ICommandManager.cs index c05c5cbbb23f..425f63a7c983 100644 --- a/src/Files.App/Data/Commands/Manager/ICommandManager.cs +++ b/src/Files.App/Data/Commands/Manager/ICommandManager.cs @@ -3,6 +3,9 @@ namespace Files.App.Data.Commands { + /// + /// Represents a collection of and provides manager classes. + /// public interface ICommandManager : IEnumerable { IRichCommand this[CommandCodes code] { get; } diff --git a/src/Files.App/Data/Commands/ModifiableCommand.cs b/src/Files.App/Data/Commands/ModifiableCommand.cs index 96e3997f4e91..e4dae209b66f 100644 --- a/src/Files.App/Data/Commands/ModifiableCommand.cs +++ b/src/Files.App/Data/Commands/ModifiableCommand.cs @@ -40,6 +40,8 @@ public HotKeyCollection HotKeys set => BaseCommand.HotKeys = value; } + public HotKeyCollection DefaultHotKeys { get; } + public bool IsToggle => BaseCommand.IsToggle; public bool IsOn @@ -54,6 +56,7 @@ public ModifiableCommand(IRichCommand baseCommand, Dictionary throw new InvalidOperationException("This command is readonly."); } + public HotKeyCollection DefaultHotKeys + { + get => HotKeyCollection.Empty; + set => throw new InvalidOperationException("This command is readonly."); + } + public bool IsToggle => false; public bool IsOn { get => false; set { } } public bool IsExecutable => false; diff --git a/src/Files.App/Data/Enums/SettingsPageKind.cs b/src/Files.App/Data/Enums/SettingsPageKind.cs new file mode 100644 index 000000000000..15b00e4bfb07 --- /dev/null +++ b/src/Files.App/Data/Enums/SettingsPageKind.cs @@ -0,0 +1,18 @@ +// Copyright (c) 2024 Files Community +// Licensed under the MIT License. See the LICENSE. + +namespace Files.App.Data.Enums +{ + public enum SettingsPageKind + { + GeneralPage, + AppearancePage, + LayoutPage, + FoldersPage, + ActionsPage, + TagsPage, + GitPage, + AdvancedPage, + AboutPage, + } +} diff --git a/src/Files.App/Data/Items/ModifiableCommandHotKeyItem.cs b/src/Files.App/Data/Items/ModifiableCommandHotKeyItem.cs new file mode 100644 index 000000000000..fb1e15b0c307 --- /dev/null +++ b/src/Files.App/Data/Items/ModifiableCommandHotKeyItem.cs @@ -0,0 +1,59 @@ +// Copyright (c) 2024 Files Community +// Licensed under the MIT License. See the LICENSE. + +namespace Files.App.Data.Items +{ + /// + /// Represents item for a hotkey that is registered for a . + /// + public class ModifiableCommandHotKeyItem : ObservableObject + { + public CommandCodes CommandCode { get; set; } + + public string Label { get; set; } = string.Empty; + + public string Description { get; set; } = string.Empty; + + public HotKeyCollection DefaultHotKeyCollection { get; set; } = HotKeyCollection.Empty; + + public HotKey PreviousHotKey { get; set; } = HotKey.None; + + public HotKeyCollection HotKeys { get; set; } + + private string _HotKeyText = string.Empty; + public string HotKeyText + { + get => _HotKeyText; + set => SetProperty(ref _HotKeyText, value); + } + + private bool _IsEditMode; + public bool IsEditMode + { + get => _IsEditMode; + set => SetProperty(ref _IsEditMode, value); + } + + private bool _IsCustomized; + public bool IsDefaultKey + { + get => _IsCustomized; + set => SetProperty(ref _IsCustomized, value); + } + + private HotKey _HotKey; + public HotKey HotKey + { + get => _HotKey; + set + { + if (SetProperty(ref _HotKey, value)) + { + HotKeyText = HotKey.LocalizedLabel; + HotKeys = new(value); + OnPropertyChanged(nameof(HotKeys)); + } + } + } + } +} diff --git a/src/Files.App/Data/Models/ContextMenuFlyoutItemViewModelBuilder.cs b/src/Files.App/Data/Models/ContextMenuFlyoutItemViewModelBuilder.cs index 40e46fa500c3..ebeaed3a74c2 100644 --- a/src/Files.App/Data/Models/ContextMenuFlyoutItemViewModelBuilder.cs +++ b/src/Files.App/Data/Models/ContextMenuFlyoutItemViewModelBuilder.cs @@ -89,7 +89,7 @@ public ContextMenuFlyoutItemViewModel Build() Key = (VirtualKey)command.HotKeys[0].Key, Modifiers = (VirtualKeyModifiers)command.HotKeys[0].Modifier }; - viewModel.KeyboardAcceleratorTextOverride = command.HotKeys[0].Label; + viewModel.KeyboardAcceleratorTextOverride = command.HotKeys[0].LocalizedLabel; } return viewModel; diff --git a/src/Files.App/Dialogs/SettingsDialog.xaml b/src/Files.App/Dialogs/SettingsDialog.xaml index fcbd82f7ec12..f02a9df37202 100644 --- a/src/Files.App/Dialogs/SettingsDialog.xaml +++ b/src/Files.App/Dialogs/SettingsDialog.xaml @@ -85,74 +85,74 @@ + Tag="GeneralPage"> + Tag="AppearancePage"> + Tag="LayoutPage"> + Tag="FoldersPage"> + + + + + + Tag="TagsPage"> + Tag="GitPage"> + Tag="AdvancedPage"> + Tag="AboutPage"> diff --git a/src/Files.App/Dialogs/SettingsDialog.xaml.cs b/src/Files.App/Dialogs/SettingsDialog.xaml.cs index 54325bc2952b..05978c8ab42c 100644 --- a/src/Files.App/Dialogs/SettingsDialog.xaml.cs +++ b/src/Files.App/Dialogs/SettingsDialog.xaml.cs @@ -7,6 +7,7 @@ using Microsoft.UI.Xaml.Controls; using System; using System.Threading.Tasks; +using Files.App.Data.Enums; namespace Files.App.Dialogs { @@ -45,18 +46,18 @@ private void UpdateDialogLayout() private void MainSettingsNavigationView_SelectionChanged(NavigationView sender, NavigationViewSelectionChangedEventArgs args) { var selectedItem = (NavigationViewItem)args.SelectedItem; - int selectedItemTag = Convert.ToInt32(selectedItem.Tag); - _ = selectedItemTag switch + _ = Enum.Parse(selectedItem.Tag.ToString()) switch { - 0 => SettingsContentFrame.Navigate(typeof(GeneralPage)), - 1 => SettingsContentFrame.Navigate(typeof(AppearancePage)), - 2 => SettingsContentFrame.Navigate(typeof(LayoutPage)), - 3 => SettingsContentFrame.Navigate(typeof(FoldersPage)), - 4 => SettingsContentFrame.Navigate(typeof(TagsPage)), - 5 => SettingsContentFrame.Navigate(typeof(GitPage)), - 6 => SettingsContentFrame.Navigate(typeof(AdvancedPage)), - 7 => SettingsContentFrame.Navigate(typeof(AboutPage)), + SettingsPageKind.GeneralPage => SettingsContentFrame.Navigate(typeof(GeneralPage)), + SettingsPageKind.AppearancePage => SettingsContentFrame.Navigate(typeof(AppearancePage)), + SettingsPageKind.LayoutPage => SettingsContentFrame.Navigate(typeof(LayoutPage)), + SettingsPageKind.FoldersPage => SettingsContentFrame.Navigate(typeof(FoldersPage)), + SettingsPageKind.ActionsPage => SettingsContentFrame.Navigate(typeof(ActionsPage)), + SettingsPageKind.TagsPage => SettingsContentFrame.Navigate(typeof(TagsPage)), + SettingsPageKind.GitPage => SettingsContentFrame.Navigate(typeof(GitPage)), + SettingsPageKind.AdvancedPage => SettingsContentFrame.Navigate(typeof(AdvancedPage)), + SettingsPageKind.AboutPage => SettingsContentFrame.Navigate(typeof(AboutPage)), _ => SettingsContentFrame.Navigate(typeof(AppearancePage)) }; } diff --git a/src/Files.App/Helpers/Win32/Win32PInvoke.Methods.cs b/src/Files.App/Helpers/Win32/Win32PInvoke.Methods.cs index 096481537042..96013695c6fa 100644 --- a/src/Files.App/Helpers/Win32/Win32PInvoke.Methods.cs +++ b/src/Files.App/Helpers/Win32/Win32PInvoke.Methods.cs @@ -210,5 +210,15 @@ IntPtr lpOverlapped out uint lpBytesReturned, IntPtr lpOverlapped ); + + [DllImport("user32.dll")] + public static extern int ToUnicode( + uint virtualKeyCode, + uint scanCode, + byte[] keyboardState, + [Out, MarshalAs(UnmanagedType.LPWStr, SizeConst = 64)] StringBuilder receivingBuffer, + int bufferSize, + uint flags + ); } } diff --git a/src/Files.App/Services/Settings/GeneralSettingsService.cs b/src/Files.App/Services/Settings/GeneralSettingsService.cs index aca48cbb4837..8bc1aa8a6fec 100644 --- a/src/Files.App/Services/Settings/GeneralSettingsService.cs +++ b/src/Files.App/Services/Settings/GeneralSettingsService.cs @@ -251,7 +251,7 @@ public FileNameConflictResolveOptionType ConflictsResolveOption set => Set(value); } - public Dictionary Actions + public Dictionary? Actions { get => Get>(null) ?? []; set => Set(value); diff --git a/src/Files.App/Services/Settings/IGeneralSettingsService.cs b/src/Files.App/Services/Settings/IGeneralSettingsService.cs index cf27c996664b..3fbfd2893390 100644 --- a/src/Files.App/Services/Settings/IGeneralSettingsService.cs +++ b/src/Files.App/Services/Settings/IGeneralSettingsService.cs @@ -207,6 +207,6 @@ public interface IGeneralSettingsService : IBaseSettingsService, INotifyProperty /// /// A dictionary to determine the custom hotkeys /// - Dictionary Actions { get; set; } + Dictionary? Actions { get; set; } } } diff --git a/src/Files.App/Strings/en-US/Resources.resw b/src/Files.App/Strings/en-US/Resources.resw index 27c3707e11cc..b6eebb417a8c 100644 --- a/src/Files.App/Strings/en-US/Resources.resw +++ b/src/Files.App/Strings/en-US/Resources.resw @@ -3716,4 +3716,28 @@ Layout type + + Actions + + + Commands + + + Add command + + + Restore defaults + + + Choose an action + + + Are you sure you want to restore the default key bindings? This action cannot be undone. + + + This key binding is already being used, please choose a different key binding to continue. + + + Customized + \ No newline at end of file diff --git a/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcut.cs b/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcut.cs index 71758ab5f46b..fcd9020340e3 100644 --- a/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcut.cs +++ b/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcut.cs @@ -56,38 +56,38 @@ private void OnHotKeysChanged() // Keys only case (_, KeyModifiers.None): - var key = HotKey.keys[item.Key]; + var key = HotKey.LocalizedKeys[item.Key]; items.Add(new() { Text = key, ItemType = ItemType, Size = Size }); break; // Others default: GetModifierCode(item.Modifier); - key = HotKey.keys[item.Key]; + key = HotKey.LocalizedKeys[item.Key]; items.Add(new() { Text = key, ItemType = ItemType, Size = Size }); break; } void GetModifierCode(KeyModifiers modifier) { - if (modifier.HasFlag(KeyModifiers.Menu)) + if (modifier.HasFlag(KeyModifiers.Alt)) { - items.Add(new() { Text = HotKey.modifiers[KeyModifiers.Menu], ItemType = ItemType, Size = Size }); + items.Add(new() { Text = HotKey.LocalizedModifiers[KeyModifiers.Alt], ItemType = ItemType, Size = Size }); items.Add(new() { Text = "+", ItemType = KeyboardShortcutItemKind.TextOnly, Size = Size }); } if (modifier.HasFlag(KeyModifiers.Ctrl)) { - items.Add(new() { Text = HotKey.modifiers[KeyModifiers.Ctrl], ItemType = ItemType, Size = Size }); + items.Add(new() { Text = HotKey.LocalizedModifiers[KeyModifiers.Ctrl], ItemType = ItemType, Size = Size }); items.Add(new() { Text = "+", ItemType = KeyboardShortcutItemKind.TextOnly, Size = Size }); } if (modifier.HasFlag(KeyModifiers.Shift)) { - items.Add(new() { Text = HotKey.modifiers[KeyModifiers.Shift], ItemType = ItemType, Size = Size }); + items.Add(new() { Text = HotKey.LocalizedModifiers[KeyModifiers.Shift], ItemType = ItemType, Size = Size }); items.Add(new() { Text = "+", ItemType = KeyboardShortcutItemKind.TextOnly, Size = Size }); } if (modifier.HasFlag(KeyModifiers.Win)) { - items.Add(new() { Text = HotKey.modifiers[KeyModifiers.Win], ItemType = ItemType, Size = Size }); + items.Add(new() { Text = HotKey.LocalizedModifiers[KeyModifiers.Win], ItemType = ItemType, Size = Size }); items.Add(new() { Text = "+", ItemType = KeyboardShortcutItemKind.TextOnly, Size = Size }); } } diff --git a/src/Files.App/ViewModels/Settings/ActionsViewModel.cs b/src/Files.App/ViewModels/Settings/ActionsViewModel.cs new file mode 100644 index 000000000000..a4e0723d14b8 --- /dev/null +++ b/src/Files.App/ViewModels/Settings/ActionsViewModel.cs @@ -0,0 +1,225 @@ +// Copyright (c) 2024 Files Community +// Licensed under the MIT License. See the LICENSE. + +using System.Windows.Input; + +namespace Files.App.ViewModels.Settings +{ + public sealed class ActionsViewModel : ObservableObject + { + // Dependency injections + + private IGeneralSettingsService GeneralSettingsService { get; } = Ioc.Default.GetRequiredService(); + private ICommandManager Commands { get; } = Ioc.Default.GetRequiredService(); + + // Properties + + public ObservableCollection ValidKeyboardShortcuts { get; } = []; + public ObservableCollection AllKeyboardShortcuts { get; } = []; + + private bool _IsResetAllConfirmationTeachingTipOpened; + public bool IsResetAllConfirmationTeachingTipOpened + { + get => _IsResetAllConfirmationTeachingTipOpened; + set => SetProperty(ref _IsResetAllConfirmationTeachingTipOpened, value); + } + + private bool _IsAlreadyUsedTeachingTipOpened; + public bool IsAlreadyUsedTeachingTipOpened + { + get => _IsAlreadyUsedTeachingTipOpened; + set => SetProperty(ref _IsAlreadyUsedTeachingTipOpened, value); + } + + private bool _ShowAddNewHotKeySection; + public bool ShowAddNewHotKeySection + { + get => _ShowAddNewHotKeySection; + set => SetProperty(ref _ShowAddNewHotKeySection, value); + } + + private ModifiableCommandHotKeyItem? _SelectedNewShortcutItem; + public ModifiableCommandHotKeyItem? SelectedNewShortcutItem + { + get => _SelectedNewShortcutItem; + set => SetProperty(ref _SelectedNewShortcutItem, value); + } + + // Commands + + public ICommand LoadCommandsCommand { get; set; } + public ICommand ShowAddNewHotKeySectionCommand { get; set; } + public ICommand HideAddNewHotKeySectionCommand { get; set; } + public ICommand AddNewHotKeyCommand { get; set; } + public ICommand ShowRestoreDefaultsConfirmationCommand { get; set; } + public ICommand RestoreDefaultsCommand { get; set; } + + // Constructor + + public ActionsViewModel() + { + LoadCommandsCommand = new AsyncRelayCommand(ExecuteLoadCommandsCommand); + ShowAddNewHotKeySectionCommand = new RelayCommand(ExecuteShowAddNewHotKeySectionCommand); + HideAddNewHotKeySectionCommand = new RelayCommand(ExecuteHideAddNewHotKeySectionCommand); + AddNewHotKeyCommand = new RelayCommand(ExecuteAddNewHotKeyCommand); + ShowRestoreDefaultsConfirmationCommand = new RelayCommand(ExecuteShowRestoreDefaultsConfirmationCommand); + RestoreDefaultsCommand = new RelayCommand(ExecuteRestoreDefaultsCommand); + } + + // Command methods + + private async Task ExecuteLoadCommandsCommand() + { + await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(() => + { + ValidKeyboardShortcuts.Clear(); + AllKeyboardShortcuts.Clear(); + + foreach (var command in Commands) + { + var defaultKeys = command.DefaultHotKeys; + + if (command is NoneCommand) + continue; + + AllKeyboardShortcuts.Add(new() + { + CommandCode = command.Code, + Label = command.Label, + Description = command.Description, + HotKey = new(), + }); + + foreach (var hotkey in command.HotKeys) + { + // Don't show mouse hotkeys for now because no editor provided for mouse input as of now + if (!hotkey.IsVisible || hotkey.Key == Keys.Mouse4 || hotkey.Key == Keys.Mouse5) + continue; + + ValidKeyboardShortcuts.Add(new() + { + CommandCode = command.Code, + Label = command.Label, + Description = command.Description, + HotKey = hotkey, + DefaultHotKeyCollection = defaultKeys, + PreviousHotKey = hotkey, + IsDefaultKey = defaultKeys.Contains(hotkey), + }); + } + } + }); + } + + private void ExecuteShowAddNewHotKeySectionCommand() + { + ShowAddNewHotKeySection = true; + + // Reset edit mode of every item + foreach (var hotkey in ValidKeyboardShortcuts) + { + hotkey.IsEditMode = false; + hotkey.HotKeyText = hotkey.HotKey.LocalizedLabel; + } + } + + private void ExecuteHideAddNewHotKeySectionCommand() + { + ShowAddNewHotKeySection = false; + + if (SelectedNewShortcutItem is null) + return; + + SelectedNewShortcutItem.HotKeyText = ""; + SelectedNewShortcutItem = null; + } + + private void ExecuteAddNewHotKeyCommand() + { + if (SelectedNewShortcutItem is null) + return; + + // Check if this hot key is already taken + foreach (var hotkey in ValidKeyboardShortcuts) + { + if (SelectedNewShortcutItem.HotKeyText == hotkey.PreviousHotKey) + { + IsAlreadyUsedTeachingTipOpened = true; + return; + } + } + + var actions = + GeneralSettingsService.Actions is not null + ? new Dictionary(GeneralSettingsService.Actions) + : []; + + // Get raw string keys stored in the user setting + var storedKeys = actions.GetValueOrDefault(SelectedNewShortcutItem.CommandCode.ToString()); + + // Initialize + var newHotKey = HotKey.Parse(SelectedNewShortcutItem.HotKeyText); + var modifiedCollection = HotKeyCollection.Empty; + + // The first time to customize + if (string.IsNullOrEmpty(storedKeys)) + { + // Replace with new one + var modifiableDefaultCollection = SelectedNewShortcutItem.DefaultHotKeyCollection.ToList(); + modifiableDefaultCollection.RemoveAll(x => x.RawLabel == SelectedNewShortcutItem.PreviousHotKey.RawLabel); + modifiableDefaultCollection.Add(newHotKey); + modifiedCollection = new HotKeyCollection(modifiableDefaultCollection); + } + // Stored in the user setting + else + { + // Replace with new one + var modifiableCollection = HotKeyCollection.Parse(storedKeys).ToList(); + modifiableCollection.RemoveAll(x => x.RawLabel == SelectedNewShortcutItem.PreviousHotKey.RawLabel); + modifiableCollection.Add(newHotKey); + modifiedCollection = new HotKeyCollection(modifiableCollection); + } + + // Remove previous one and add new one + actions.Remove(SelectedNewShortcutItem.CommandCode.ToString()); + actions.Add(SelectedNewShortcutItem.CommandCode.ToString(), modifiedCollection.RawLabel); + + // Store + GeneralSettingsService.Actions = actions; + + // Create a clone + var selectedNewItem = new ModifiableCommandHotKeyItem() + { + CommandCode = SelectedNewShortcutItem.CommandCode, + Label = SelectedNewShortcutItem.Label, + Description = SelectedNewShortcutItem.Description, + HotKey = HotKey.Parse(SelectedNewShortcutItem.HotKeyText), + DefaultHotKeyCollection = new(SelectedNewShortcutItem.DefaultHotKeyCollection), + PreviousHotKey = HotKey.Parse(SelectedNewShortcutItem.PreviousHotKey.RawLabel), + }; + + // Hide the section + ShowAddNewHotKeySection = false; + + // Remove from excluded list and set null + SelectedNewShortcutItem.HotKeyText = ""; + SelectedNewShortcutItem = null; + + // Add to existing list + ValidKeyboardShortcuts.Insert(0, selectedNewItem); + } + + private void ExecuteShowRestoreDefaultsConfirmationCommand() + { + IsResetAllConfirmationTeachingTipOpened = true; + } + + private void ExecuteRestoreDefaultsCommand() + { + GeneralSettingsService.Actions = null; + IsResetAllConfirmationTeachingTipOpened = false; + + _ = ExecuteLoadCommandsCommand(); + } + } +} diff --git a/src/Files.App/Views/Settings/ActionsPage.xaml b/src/Files.App/Views/Settings/ActionsPage.xaml new file mode 100644 index 000000000000..d5f3e37a62d3 --- /dev/null +++ b/src/Files.App/Views/Settings/ActionsPage.xaml @@ -0,0 +1,408 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Files.App/Views/Settings/ActionsPage.xaml.cs b/src/Files.App/Views/Settings/ActionsPage.xaml.cs new file mode 100644 index 000000000000..854a6df213e0 --- /dev/null +++ b/src/Files.App/Views/Settings/ActionsPage.xaml.cs @@ -0,0 +1,342 @@ +// Copyright (c) 2024 Files Community +// Licensed under the MIT License. See the LICENSE. + +using CommunityToolkit.WinUI.UI; +using Files.App.ViewModels.Settings; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Input; +using Windows.System; + +namespace Files.App.Views.Settings +{ + public sealed partial class ActionsPage : Page + { + private IGeneralSettingsService GeneralSettingsService { get; } = Ioc.Default.GetRequiredService(); + + private readonly string PART_EditButton = "EditButton"; + private readonly string NormalState = "Normal"; + private readonly string PointerOverState = "PointerOver"; + + private ActionsViewModel ViewModel { get; set; } = new(); + + public ActionsPage() + { + InitializeComponent(); + } + + private void RootGrid_PointerEntered(object sender, PointerRoutedEventArgs e) + { + VisualStateManager.GoToState((UserControl)sender, PointerOverState, true); + + // Make edit button visible on pointer in + if (sender is UserControl userControl && + userControl.FindChild(PART_EditButton) is Button editButton && + userControl.DataContext is ModifiableCommandHotKeyItem item && + !item.IsEditMode) + editButton.Visibility = Visibility.Visible; + } + + private void RootGrid_PointerExited(object sender, PointerRoutedEventArgs e) + { + VisualStateManager.GoToState((UserControl)sender, NormalState, true); + + // Make edit button invisible on pointer out + if (sender is UserControl userControl && + userControl.FindChild(PART_EditButton) is Button editButton && + userControl.DataContext is ModifiableCommandHotKeyItem item && + !item.IsEditMode) + editButton.Visibility = Visibility.Collapsed; + } + + private void EditButton_Click(object sender, RoutedEventArgs e) + { + if (sender is Button button && button.DataContext is ModifiableCommandHotKeyItem item) + { + // Hide the add command grid + ViewModel.ShowAddNewHotKeySection = false; + + // Reset the selected item's info + if (ViewModel.SelectedNewShortcutItem is not null) + { + ViewModel.SelectedNewShortcutItem.HotKeyText = ""; + ViewModel.SelectedNewShortcutItem = null; + } + + // Reset edit mode of every item + foreach (var hotkey in ViewModel.ValidKeyboardShortcuts) + { + hotkey.IsEditMode = false; + hotkey.HotKeyText = hotkey.HotKey.LocalizedLabel; + } + + // Enter edit mode for the item + item.IsEditMode = true; + } + } + + private void SaveButton_Click(object sender, RoutedEventArgs e) + { + if (sender is not Button button || button.DataContext is not ModifiableCommandHotKeyItem item) + return; + + if (item.HotKeyText == item.PreviousHotKey.LocalizedLabel) + { + item.IsEditMode = false; + return; + } + + // Check if this hot key is already taken + foreach (var hotkey in ViewModel.ValidKeyboardShortcuts) + { + if (item.HotKeyText == hotkey.PreviousHotKey) + { + ViewModel.IsAlreadyUsedTeachingTipOpened = true; + return; + } + } + + // Get clone of customized hotkeys to overwrite + var actions = + GeneralSettingsService.Actions is not null + ? new Dictionary(GeneralSettingsService.Actions) + : []; + + // Get raw string keys stored in the user setting + var storedKeys = actions.GetValueOrDefault(item.CommandCode.ToString()); + + // Initialize + var newHotKey = HotKey.Parse(item.HotKeyText); + var modifiedCollection = HotKeyCollection.Empty; + + if (item.IsDefaultKey) + { + // The first time to customize in the user setting + if (string.IsNullOrEmpty(storedKeys)) + { + // Replace with new one + var modifiableDefaultCollection = item.DefaultHotKeyCollection.ToList(); + modifiableDefaultCollection.RemoveAll(x => x.RawLabel == item.PreviousHotKey.RawLabel); + modifiableDefaultCollection.Add(newHotKey); + modifiedCollection = new HotKeyCollection(modifiableDefaultCollection); + } + // Stored in the user setting + else + { + // Replace with new one + var modifiableCollection = HotKeyCollection.Parse(storedKeys).ToList(); + modifiableCollection.RemoveAll(x => x.RawLabel == item.PreviousHotKey.RawLabel); + modifiableCollection.Add(newHotKey); + modifiedCollection = new HotKeyCollection(modifiableCollection); + } + + // Store + actions.Add(item.CommandCode.ToString(), modifiedCollection.RawLabel); + GeneralSettingsService.Actions = actions; + + // Update visual + item.PreviousHotKey = newHotKey; + item.HotKey = newHotKey; + + // Set as customized + foreach (var action in ViewModel.ValidKeyboardShortcuts) + { + if (action.CommandCode == item.CommandCode) + action.IsDefaultKey = item.DefaultHotKeyCollection.Contains(action.HotKey); + } + + // Exit edit mode + item.IsEditMode = false; + + return; + } + else + { + // Remove existing setting + var modifiableCollection = HotKeyCollection.Parse(storedKeys!).ToList(); + if (modifiableCollection.Contains(newHotKey)) + return; + modifiableCollection.Add(HotKey.Parse(item.HotKeyText)); + modifiedCollection = new(modifiableCollection); + + // Remove previous one + actions.Remove(item.CommandCode.ToString()); + + // Add new one + if (modifiedCollection.Select(x => x.RawLabel).SequenceEqual(item.DefaultHotKeyCollection.Select(x => x.RawLabel))) + actions.Add(item.CommandCode.ToString(), modifiedCollection.RawLabel); + + // Save + GeneralSettingsService.Actions = actions; + + // Update visual + item.PreviousHotKey = newHotKey; + item.HotKey = newHotKey; + + // Set as customized + foreach (var action in ViewModel.ValidKeyboardShortcuts) + { + if (action.CommandCode == item.CommandCode) + action.IsDefaultKey = item.DefaultHotKeyCollection.Contains(action.HotKey); + } + + // Exit edit mode + item.IsEditMode = false; + } + } + + private void CancelButton_Click(object sender, RoutedEventArgs e) + { + if (sender is not Button button || button.DataContext is not ModifiableCommandHotKeyItem item) + return; + + item.IsEditMode = false; + item.HotKeyText = item.HotKey.LocalizedLabel; + } + + private void DeleteButton_Click(object sender, RoutedEventArgs e) + { + if (sender is not Button button || button.DataContext is not ModifiableCommandHotKeyItem item) + return; + + // Get clone of customized hotkeys to overwrite + var actions = + GeneralSettingsService.Actions is not null + ? new Dictionary(GeneralSettingsService.Actions) + : []; + + // Get raw string keys stored in the user setting + var storedKeys = actions.GetValueOrDefault(item.CommandCode.ToString()); + + // Initialize + var modifiedCollection = HotKeyCollection.Empty; + + if (item.IsDefaultKey) + { + // The first time to customize in the user setting + if (string.IsNullOrEmpty(storedKeys)) + { + // Replace with new one + var modifiableDefaultCollection = item.DefaultHotKeyCollection.ToList(); + modifiableDefaultCollection.RemoveAll(x => x.RawLabel == item.PreviousHotKey.RawLabel); + modifiedCollection = new HotKeyCollection(modifiableDefaultCollection); + } + // Stored in the user setting + else + { + // Replace with new one + var modifiableCollection = HotKeyCollection.Parse(storedKeys).ToList(); + modifiableCollection.RemoveAll(x => x.RawLabel == item.PreviousHotKey.RawLabel); + modifiedCollection = new HotKeyCollection(modifiableCollection); + } + + // Remove previous one and add new one + actions.Remove(item.CommandCode.ToString()); + actions.Add(item.CommandCode.ToString(), modifiedCollection.RawLabel); + + // Store + GeneralSettingsService.Actions = actions; + + // Exit + item.IsEditMode = false; + ViewModel.ValidKeyboardShortcuts.Remove(item); + + return; + } + else + { + // Remove existing setting + var modifiableCollection = HotKeyCollection.Parse(storedKeys!).ToList(); + modifiableCollection.RemoveAll(x => x.RawLabel == item.PreviousHotKey.RawLabel || x.RawLabel == $"!{item.PreviousHotKey.RawLabel}"); + modifiedCollection = new(modifiableCollection); + + // Remove previous + actions.Remove(item.CommandCode.ToString()); + + if (modifiedCollection.LocalizedLabel != string.Empty) + actions.Add(item.CommandCode.ToString(), modifiedCollection.RawLabel); + + // Save + GeneralSettingsService.Actions = actions; + + // Exit + item.IsEditMode = false; + ViewModel.ValidKeyboardShortcuts.Remove(item); + + return; + } + } + + private void EditorTextBox_PreviewKeyDown(object sender, KeyRoutedEventArgs e) + { + if (sender is not TextBox textBox) + return; + + var pressedKey = e.OriginalKey; + + List modifierKeys = + [ + VirtualKey.Shift, + VirtualKey.Control, + VirtualKey.Menu, + VirtualKey.LeftWindows, + VirtualKey.RightWindows, + VirtualKey.LeftShift, + VirtualKey.LeftControl, + VirtualKey.RightControl, + VirtualKey.LeftMenu, + VirtualKey.RightMenu + ]; + + // If pressed key is one of modifier don't show it in the TextBox yet + foreach (var modifier in modifierKeys) + { + if (pressedKey == modifier) + { + // Prevent key down event in other UIElements from getting invoked + e.Handled = true; + + return; + } + } + + var pressedModifiers = HotKeyHelpers.GetCurrentKeyModifiers(); + string text = string.Empty; + + // Add the modifiers with translated + if (pressedModifiers.HasFlag(KeyModifiers.Ctrl)) + text += $"{HotKey.LocalizedModifiers.GetValueOrDefault(KeyModifiers.Ctrl)}+"; + if (pressedModifiers.HasFlag(KeyModifiers.Alt)) + text += $"{HotKey.LocalizedModifiers.GetValueOrDefault(KeyModifiers.Alt)}+"; + if (pressedModifiers.HasFlag(KeyModifiers.Shift)) + text += $"{HotKey.LocalizedModifiers.GetValueOrDefault(KeyModifiers.Shift)}+"; + + // Add the key with translated + text += HotKey.LocalizedKeys.GetValueOrDefault((Keys)pressedKey); + + // Set text + textBox.Text = text; + + // Prevent key down event in other UIElements from getting invoked + e.Handled = true; + } + + private void KeyboardShortcutEditorTextBox_Loaded(object sender, RoutedEventArgs e) + { + // Focus Key Binding TextBox + TextBox keyboardShortcutEditorTextBox = (TextBox)sender; + keyboardShortcutEditorTextBox.Focus(FocusState.Programmatic); + } + + private void KeyboardShortcutEditorTextBox_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e) + { + // Check if a new action is selected + if (ViewModel.SelectedNewShortcutItem is null) + return; + + // Focus Key Binding TextBox + TextBox keyboardShortcutEditorTextBox = (TextBox)sender; + keyboardShortcutEditorTextBox.Focus(FocusState.Programmatic); + } + } +} diff --git a/tests/Files.InteractionTests/Tests/SettingsTests.cs b/tests/Files.InteractionTests/Tests/SettingsTests.cs index b1dca87c8f1b..a48135a74cea 100644 --- a/tests/Files.InteractionTests/Tests/SettingsTests.cs +++ b/tests/Files.InteractionTests/Tests/SettingsTests.cs @@ -30,6 +30,7 @@ public void VerifySettingsAreAccessible() "SettingsItemAppearance", //"SettingsItemLayout", TODO find workaround for the "Group by" setting block issue "SettingsItemFolders", + "SettingsItemActions", "SettingsItemTags", "SettingsItemGit", "SettingsItemAdvanced", From ca593986a2d32db55176467a923fcf235fa79d11 Mon Sep 17 00:00:00 2001 From: hishitetsu <66369541+hishitetsu@users.noreply.github.com> Date: Thu, 11 Apr 2024 01:52:07 +0900 Subject: [PATCH 34/41] Fix: Fixed issue where properties window could not navigate to other pages after returning from advanced settings (#15149) --- .../ViewModels/Properties/MainPropertiesViewModel.cs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/Files.App/ViewModels/Properties/MainPropertiesViewModel.cs b/src/Files.App/ViewModels/Properties/MainPropertiesViewModel.cs index adce93d08f15..7f74a375cb67 100644 --- a/src/Files.App/ViewModels/Properties/MainPropertiesViewModel.cs +++ b/src/Files.App/ViewModels/Properties/MainPropertiesViewModel.cs @@ -21,8 +21,7 @@ public NavigationViewItemButtonStyleItem SelectedNavigationViewItem get => _SelectedNavigationViewItem; set { - if (SetProperty(ref _SelectedNavigationViewItem, value) && - !_selectionChangedAutomatically) + if (SetProperty(ref _SelectedNavigationViewItem, value)) { var parameter = new PropertiesPageNavigationParameter { @@ -47,8 +46,6 @@ public NavigationViewItemButtonStyleItem SelectedNavigationViewItem _mainFrame?.Navigate(page, parameter, new EntranceNavigationTransitionInfo()); } - - _selectionChangedAutomatically = false; } } @@ -85,8 +82,6 @@ public NavigationViewItemButtonStyleItem SelectedNavigationViewItem private readonly PropertiesPageNavigationParameter _parameter; - private bool _selectionChangedAutomatically { get; set; } - public IRelayCommand DoBackwardNavigationCommand { get; } public IAsyncRelayCommand SaveChangedPropertiesCommand { get; } public IRelayCommand CancelChangedPropertiesCommand { get; } @@ -120,8 +115,6 @@ private void ExecuteDoBackwardNavigationCommand() var pageTag = ((Page)_mainFrame.Content).Tag.ToString(); - _selectionChangedAutomatically = true; - // Move selection indicator _SelectedNavigationViewItem = NavigationViewItems.First(x => string.Equals(x.ItemType.ToString(), pageTag, StringComparison.CurrentCultureIgnoreCase)) From 98d5e9ff190085458b54deaad67cb290e068e629 Mon Sep 17 00:00:00 2001 From: hishitetsu <66369541+hishitetsu@users.noreply.github.com> Date: Fri, 12 Apr 2024 23:15:55 +0900 Subject: [PATCH 35/41] Fix: Fixed possible crash when entering characters into path bar (#15160) --- .../UserControls/AddressToolbarViewModel.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Files.App/ViewModels/UserControls/AddressToolbarViewModel.cs b/src/Files.App/ViewModels/UserControls/AddressToolbarViewModel.cs index 087f34dcd72f..3d7eecc66ce4 100644 --- a/src/Files.App/ViewModels/UserControls/AddressToolbarViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/AddressToolbarViewModel.cs @@ -956,11 +956,13 @@ public async Task SetAddressBarSuggestionsAsync(AutoSuggestBox sender, IShellPag return true; })) { - NavigationBarSuggestions.Clear(); - NavigationBarSuggestions.Add(new NavigationBarSuggestionItem() - { - Text = shellpage.FilesystemViewModel.WorkingDirectory, - PrimaryDisplay = "NavigationToolbarVisiblePathNoResults".GetLocalizedResource() + SafetyExtensions.IgnoreExceptions(() => { + NavigationBarSuggestions.Clear(); + NavigationBarSuggestions.Add(new NavigationBarSuggestionItem() + { + Text = shellpage.FilesystemViewModel.WorkingDirectory, + PrimaryDisplay = "NavigationToolbarVisiblePathNoResults".GetLocalizedResource() + }); }); } } From 9a37c20a4f97f66dc4bd2dafcd0f6948f62c7077 Mon Sep 17 00:00:00 2001 From: 0x5bfa <62196528+0x5bfa@users.noreply.github.com> Date: Sun, 14 Apr 2024 11:35:34 +0900 Subject: [PATCH 36/41] Code Quality: Replace ThemeHelper with AppThemeModeService (#15150) --- .../Data/Contracts/IAppThemeModeService.cs | 38 +++++ src/Files.App/Dialogs/AddBranchDialog.xaml | 2 +- src/Files.App/Dialogs/AddBranchDialog.xaml.cs | 4 + src/Files.App/Dialogs/AddItemDialog.xaml | 2 +- src/Files.App/Dialogs/AddItemDialog.xaml.cs | 3 + .../Dialogs/CreateArchiveDialog.xaml | 2 +- .../Dialogs/CreateArchiveDialog.xaml.cs | 3 + .../Dialogs/CreateShortcutDialog.xaml | 2 +- .../Dialogs/CreateShortcutDialog.xaml.cs | 4 + src/Files.App/Dialogs/CredentialDialog.xaml | 2 +- .../Dialogs/CredentialDialog.xaml.cs | 4 + .../Dialogs/DecompressArchiveDialog.xaml | 2 +- .../Dialogs/DecompressArchiveDialog.xaml.cs | 4 + src/Files.App/Dialogs/DynamicDialog.xaml | 2 +- src/Files.App/Dialogs/DynamicDialog.xaml.cs | 4 + .../Dialogs/ElevateConfirmDialog.xaml | 2 +- .../Dialogs/ElevateConfirmDialog.xaml.cs | 4 + src/Files.App/Dialogs/FileTooLargeDialog.xaml | 2 +- .../Dialogs/FileTooLargeDialog.xaml.cs | 4 + .../Dialogs/FilesystemOperationDialog.xaml | 2 +- .../Dialogs/FilesystemOperationDialog.xaml.cs | 3 + src/Files.App/Dialogs/GitHubLoginDialog.xaml | 2 +- .../Dialogs/GitHubLoginDialog.xaml.cs | 4 + src/Files.App/Dialogs/ReleaseNotesDialog.xaml | 2 +- .../Dialogs/ReleaseNotesDialog.xaml.cs | 3 + .../Dialogs/ReorderSidebarItemsDialog.xaml | 2 +- .../Dialogs/ReorderSidebarItemsDialog.xaml.cs | 3 + .../Helpers/Application/AppLifecycleHelper.cs | 1 + src/Files.App/Helpers/UI/ThemeHelper.cs | 133 ------------------ src/Files.App/Services/AppThemeModeService.cs | 106 ++++++++++++++ src/Files.App/Services/ResourcesService.cs | 6 +- .../Settings/AppearanceSettingsService.cs | 7 + .../Settings/IAppearanceSettingsService.cs | 5 + .../Storage/Helpers/FilePropertiesHelpers.cs | 4 +- src/Files.App/ViewModels/MainPageViewModel.cs | 17 +-- .../Settings/AppearanceViewModel.cs | 6 +- src/Files.App/ViewModels/SettingsViewModel.cs | 10 -- .../Properties/MainPropertiesPage.xaml.cs | 29 +--- 38 files changed, 239 insertions(+), 196 deletions(-) create mode 100644 src/Files.App/Data/Contracts/IAppThemeModeService.cs delete mode 100644 src/Files.App/Helpers/UI/ThemeHelper.cs create mode 100644 src/Files.App/Services/AppThemeModeService.cs diff --git a/src/Files.App/Data/Contracts/IAppThemeModeService.cs b/src/Files.App/Data/Contracts/IAppThemeModeService.cs new file mode 100644 index 000000000000..28311d8950b4 --- /dev/null +++ b/src/Files.App/Data/Contracts/IAppThemeModeService.cs @@ -0,0 +1,38 @@ +// Copyright (c) 2024 Files Community +// Licensed under the MIT License. See the LICENSE. + +using Microsoft.UI.Windowing; +using Microsoft.UI.Xaml; + +namespace Files.App.Data.Contracts +{ + public interface IAppThemeModeService + { + /// + /// Gets invoked when application theme mode is changed. + /// + public event EventHandler? AppThemeModeChanged; + + /// + /// Gets or sets application theme mode. + /// + public ElementTheme AppThemeMode { get; set; } + + /// + /// Refreshes the application theme mode only for the main window. + /// + /// + /// This is a workaround for a WinUI bug. + /// + public void ApplyResources(); + + /// + /// Sets application theme mode to the main window or a specific window. + /// + /// A window instance to set application theme mode. + /// A window's title bar instance to adjust contrast of action buttons. + /// A theme mode to set. + /// Whether the method should notify the theme mode changed. If this called for a specific window, doesn't have to call. + public void SetAppThemeMode(Window? window = null, AppWindowTitleBar? titleBar = null, ElementTheme? rootTheme = null, bool callThemeModeChangedEvent = true); + } +} diff --git a/src/Files.App/Dialogs/AddBranchDialog.xaml b/src/Files.App/Dialogs/AddBranchDialog.xaml index 19189ce7c390..88dcaa9db948 100644 --- a/src/Files.App/Dialogs/AddBranchDialog.xaml +++ b/src/Files.App/Dialogs/AddBranchDialog.xaml @@ -15,7 +15,7 @@ IsPrimaryButtonEnabled="{x:Bind ViewModel.IsBranchValid, Mode=OneWay}" PrimaryButtonStyle="{StaticResource AccentButtonStyle}" PrimaryButtonText="{helpers:ResourceString Name=Create}" - RequestedTheme="{x:Bind helpers:ThemeHelper.RootTheme}" + RequestedTheme="{x:Bind RootAppElement.RequestedTheme, Mode=OneWay}" SecondaryButtonText="{helpers:ResourceString Name=Cancel}" Style="{StaticResource DefaultContentDialogStyle}" mc:Ignorable="d"> diff --git a/src/Files.App/Dialogs/AddBranchDialog.xaml.cs b/src/Files.App/Dialogs/AddBranchDialog.xaml.cs index cdc9e57014de..0087cbab901b 100644 --- a/src/Files.App/Dialogs/AddBranchDialog.xaml.cs +++ b/src/Files.App/Dialogs/AddBranchDialog.xaml.cs @@ -2,12 +2,16 @@ // Licensed under the MIT License. See the LICENSE. using Files.App.ViewModels.Dialogs; +using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; namespace Files.App.Dialogs { public sealed partial class AddBranchDialog : ContentDialog, IDialog { + private FrameworkElement RootAppElement + => (FrameworkElement)MainWindow.Instance.Content; + public AddBranchDialogViewModel ViewModel { get => (AddBranchDialogViewModel)DataContext; diff --git a/src/Files.App/Dialogs/AddItemDialog.xaml b/src/Files.App/Dialogs/AddItemDialog.xaml index 95960cca90f8..77bac7ea8c92 100644 --- a/src/Files.App/Dialogs/AddItemDialog.xaml +++ b/src/Files.App/Dialogs/AddItemDialog.xaml @@ -15,7 +15,7 @@ CornerRadius="{StaticResource OverlayCornerRadius}" HighContrastAdjustment="None" Loaded="AddItemDialog_Loaded" - RequestedTheme="{x:Bind helpers:ThemeHelper.RootTheme}" + RequestedTheme="{x:Bind RootAppElement.RequestedTheme, Mode=OneWay}" SecondaryButtonText="{helpers:ResourceString Name=Cancel}" Style="{StaticResource DefaultContentDialogStyle}" mc:Ignorable="d"> diff --git a/src/Files.App/Dialogs/AddItemDialog.xaml.cs b/src/Files.App/Dialogs/AddItemDialog.xaml.cs index 0af0cc61faff..938f17d42495 100644 --- a/src/Files.App/Dialogs/AddItemDialog.xaml.cs +++ b/src/Files.App/Dialogs/AddItemDialog.xaml.cs @@ -12,6 +12,9 @@ public sealed partial class AddItemDialog : ContentDialog, IDialog(); + private FrameworkElement RootAppElement + => (FrameworkElement)MainWindow.Instance.Content; + public AddItemDialogViewModel ViewModel { get => (AddItemDialogViewModel)DataContext; diff --git a/src/Files.App/Dialogs/CreateArchiveDialog.xaml b/src/Files.App/Dialogs/CreateArchiveDialog.xaml index 9cac872226b1..2e32f4ecee9c 100644 --- a/src/Files.App/Dialogs/CreateArchiveDialog.xaml +++ b/src/Files.App/Dialogs/CreateArchiveDialog.xaml @@ -18,7 +18,7 @@ IsPrimaryButtonEnabled="{x:Bind ViewModel.IsNameValid, Mode=OneWay}" Loaded="ContentDialog_Loaded" PrimaryButtonText="{helpers:ResourceString Name=Create}" - RequestedTheme="{x:Bind helpers:ThemeHelper.RootTheme}" + RequestedTheme="{x:Bind RootAppElement.RequestedTheme, Mode=OneWay}" Style="{StaticResource DefaultContentDialogStyle}" mc:Ignorable="d"> diff --git a/src/Files.App/Dialogs/CreateArchiveDialog.xaml.cs b/src/Files.App/Dialogs/CreateArchiveDialog.xaml.cs index ded31c49b2fc..6715c9d22d14 100644 --- a/src/Files.App/Dialogs/CreateArchiveDialog.xaml.cs +++ b/src/Files.App/Dialogs/CreateArchiveDialog.xaml.cs @@ -10,6 +10,9 @@ namespace Files.App.Dialogs { public sealed partial class CreateArchiveDialog : ContentDialog { + private FrameworkElement RootAppElement + => (FrameworkElement)MainWindow.Instance.Content; + private bool canCreate = false; public bool CanCreate => canCreate; diff --git a/src/Files.App/Dialogs/CreateShortcutDialog.xaml b/src/Files.App/Dialogs/CreateShortcutDialog.xaml index b8a37175839a..6fb503d885e9 100644 --- a/src/Files.App/Dialogs/CreateShortcutDialog.xaml +++ b/src/Files.App/Dialogs/CreateShortcutDialog.xaml @@ -12,7 +12,7 @@ IsPrimaryButtonEnabled="{x:Bind ViewModel.IsLocationValid, Mode=OneWay}" PrimaryButtonCommand="{x:Bind ViewModel.PrimaryButtonCommand}" PrimaryButtonText="{helpers:ResourceString Name=Create}" - RequestedTheme="{x:Bind helpers:ThemeHelper.RootTheme}" + RequestedTheme="{x:Bind RootAppElement.RequestedTheme, Mode=OneWay}" SecondaryButtonText="{helpers:ResourceString Name=Cancel}" Style="{StaticResource DefaultContentDialogStyle}" mc:Ignorable="d"> diff --git a/src/Files.App/Dialogs/CreateShortcutDialog.xaml.cs b/src/Files.App/Dialogs/CreateShortcutDialog.xaml.cs index 98e979351664..007337e109bd 100644 --- a/src/Files.App/Dialogs/CreateShortcutDialog.xaml.cs +++ b/src/Files.App/Dialogs/CreateShortcutDialog.xaml.cs @@ -3,6 +3,7 @@ using Files.App.ViewModels.Dialogs; using Files.App.ViewModels.Dialogs; +using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Data; @@ -10,6 +11,9 @@ namespace Files.App.Dialogs { public sealed partial class CreateShortcutDialog : ContentDialog, IDialog { + private FrameworkElement RootAppElement + => (FrameworkElement)MainWindow.Instance.Content; + public CreateShortcutDialogViewModel ViewModel { get => (CreateShortcutDialogViewModel)DataContext; diff --git a/src/Files.App/Dialogs/CredentialDialog.xaml b/src/Files.App/Dialogs/CredentialDialog.xaml index a5b8141f2359..8e0d78521bb7 100644 --- a/src/Files.App/Dialogs/CredentialDialog.xaml +++ b/src/Files.App/Dialogs/CredentialDialog.xaml @@ -13,7 +13,7 @@ PrimaryButtonClick="ContentDialog_PrimaryButtonClick" PrimaryButtonStyle="{StaticResource AccentButtonStyle}" PrimaryButtonText="{helpers:ResourceString Name=OK}" - RequestedTheme="{x:Bind helpers:ThemeHelper.RootTheme}" + RequestedTheme="{x:Bind RootAppElement.RequestedTheme, Mode=OneWay}" SecondaryButtonText="{helpers:ResourceString Name=Cancel}" Style="{StaticResource DefaultContentDialogStyle}" mc:Ignorable="d"> diff --git a/src/Files.App/Dialogs/CredentialDialog.xaml.cs b/src/Files.App/Dialogs/CredentialDialog.xaml.cs index b3dbe8a51c41..92ba79ba03c5 100644 --- a/src/Files.App/Dialogs/CredentialDialog.xaml.cs +++ b/src/Files.App/Dialogs/CredentialDialog.xaml.cs @@ -1,6 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. +using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using System.Text; @@ -8,6 +9,9 @@ namespace Files.App.Dialogs { public sealed partial class CredentialDialog : ContentDialog, IDialog { + private FrameworkElement RootAppElement + => (FrameworkElement)MainWindow.Instance.Content; + public CredentialDialogViewModel ViewModel { get => (CredentialDialogViewModel)DataContext; diff --git a/src/Files.App/Dialogs/DecompressArchiveDialog.xaml b/src/Files.App/Dialogs/DecompressArchiveDialog.xaml index 3f989be6f07b..fc93af059c8f 100644 --- a/src/Files.App/Dialogs/DecompressArchiveDialog.xaml +++ b/src/Files.App/Dialogs/DecompressArchiveDialog.xaml @@ -12,7 +12,7 @@ HighContrastAdjustment="None" PrimaryButtonClick="ContentDialog_PrimaryButtonClick" PrimaryButtonText="{helpers:ResourceString Name=Extract}" - RequestedTheme="{x:Bind helpers:ThemeHelper.RootTheme}" + RequestedTheme="{x:Bind RootAppElement.RequestedTheme, Mode=OneWay}" SecondaryButtonText="{helpers:ResourceString Name=Cancel}" Style="{StaticResource DefaultContentDialogStyle}" mc:Ignorable="d"> diff --git a/src/Files.App/Dialogs/DecompressArchiveDialog.xaml.cs b/src/Files.App/Dialogs/DecompressArchiveDialog.xaml.cs index 9fb82d152c89..7a4543fd8476 100644 --- a/src/Files.App/Dialogs/DecompressArchiveDialog.xaml.cs +++ b/src/Files.App/Dialogs/DecompressArchiveDialog.xaml.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. See the LICENSE. using Files.App.ViewModels.Dialogs; +using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using System.Text; @@ -9,6 +10,9 @@ namespace Files.App.Dialogs { public sealed partial class DecompressArchiveDialog : ContentDialog { + private FrameworkElement RootAppElement + => (FrameworkElement)MainWindow.Instance.Content; + public DecompressArchiveDialogViewModel ViewModel { get => (DecompressArchiveDialogViewModel)DataContext; diff --git a/src/Files.App/Dialogs/DynamicDialog.xaml b/src/Files.App/Dialogs/DynamicDialog.xaml index 61ed93d46ce6..6ca18e7a521b 100644 --- a/src/Files.App/Dialogs/DynamicDialog.xaml +++ b/src/Files.App/Dialogs/DynamicDialog.xaml @@ -19,7 +19,7 @@ KeyDown="ContentDialog_KeyDown" PrimaryButtonClick="ContentDialog_PrimaryButtonClick" PrimaryButtonText="{x:Bind ViewModel.PrimaryButtonText, Mode=OneWay}" - RequestedTheme="{x:Bind helpers:ThemeHelper.RootTheme}" + RequestedTheme="{x:Bind RootAppElement.RequestedTheme, Mode=OneWay}" SecondaryButtonClick="ContentDialog_SecondaryButtonClick" SecondaryButtonText="{x:Bind ViewModel.SecondaryButtonText, Mode=OneWay}" Style="{StaticResource DefaultContentDialogStyle}" diff --git a/src/Files.App/Dialogs/DynamicDialog.xaml.cs b/src/Files.App/Dialogs/DynamicDialog.xaml.cs index 6b3111aadb64..fe0f628440a7 100644 --- a/src/Files.App/Dialogs/DynamicDialog.xaml.cs +++ b/src/Files.App/Dialogs/DynamicDialog.xaml.cs @@ -2,12 +2,16 @@ // Licensed under the MIT License. See the LICENSE. using Files.App.ViewModels.Dialogs; +using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; namespace Files.App.Dialogs { public sealed partial class DynamicDialog : ContentDialog, IDisposable { + private FrameworkElement RootAppElement + => (FrameworkElement)MainWindow.Instance.Content; + public DynamicDialogViewModel ViewModel { get => (DynamicDialogViewModel)DataContext; diff --git a/src/Files.App/Dialogs/ElevateConfirmDialog.xaml b/src/Files.App/Dialogs/ElevateConfirmDialog.xaml index 1eddf51eba51..19c434575d72 100644 --- a/src/Files.App/Dialogs/ElevateConfirmDialog.xaml +++ b/src/Files.App/Dialogs/ElevateConfirmDialog.xaml @@ -14,7 +14,7 @@ DefaultButton="None" HighContrastAdjustment="None" PrimaryButtonText="{helpers:ResourceString Name=Yes}" - RequestedTheme="{x:Bind helpers:ThemeHelper.RootTheme}" + RequestedTheme="{x:Bind RootAppElement.RequestedTheme, Mode=OneWay}" Style="{StaticResource DefaultContentDialogStyle}" mc:Ignorable="d"> diff --git a/src/Files.App/Dialogs/ElevateConfirmDialog.xaml.cs b/src/Files.App/Dialogs/ElevateConfirmDialog.xaml.cs index 38f0434babd3..924602ce2b3d 100644 --- a/src/Files.App/Dialogs/ElevateConfirmDialog.xaml.cs +++ b/src/Files.App/Dialogs/ElevateConfirmDialog.xaml.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. See the LICENSE. using Files.App.ViewModels.Dialogs; +using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using System; using System.Threading.Tasks; @@ -10,6 +11,9 @@ namespace Files.App.Dialogs { public sealed partial class ElevateConfirmDialog : ContentDialog, IDialog { + private FrameworkElement RootAppElement + => (FrameworkElement)MainWindow.Instance.Content; + public ElevateConfirmDialogViewModel ViewModel { get => (ElevateConfirmDialogViewModel)DataContext; diff --git a/src/Files.App/Dialogs/FileTooLargeDialog.xaml b/src/Files.App/Dialogs/FileTooLargeDialog.xaml index e09d56c3cc8c..f2d569ea5a5e 100644 --- a/src/Files.App/Dialogs/FileTooLargeDialog.xaml +++ b/src/Files.App/Dialogs/FileTooLargeDialog.xaml @@ -11,7 +11,7 @@ DefaultButton="Primary" HighContrastAdjustment="None" PrimaryButtonText="{helpers:ResourceString Name=OK}" - RequestedTheme="{x:Bind helpers:ThemeHelper.RootTheme}" + RequestedTheme="{x:Bind RootAppElement.RequestedTheme, Mode=OneWay}" Style="{StaticResource DefaultContentDialogStyle}" mc:Ignorable="d"> diff --git a/src/Files.App/Dialogs/FileTooLargeDialog.xaml.cs b/src/Files.App/Dialogs/FileTooLargeDialog.xaml.cs index cd81868e00b7..4daf884822b9 100644 --- a/src/Files.App/Dialogs/FileTooLargeDialog.xaml.cs +++ b/src/Files.App/Dialogs/FileTooLargeDialog.xaml.cs @@ -1,12 +1,16 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. +using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; namespace Files.App.Dialogs { public sealed partial class FileTooLargeDialog : ContentDialog, IDialog { + private FrameworkElement RootAppElement + => (FrameworkElement)MainWindow.Instance.Content; + public FileTooLargeDialogViewModel ViewModel { get => (FileTooLargeDialogViewModel)DataContext; diff --git a/src/Files.App/Dialogs/FilesystemOperationDialog.xaml b/src/Files.App/Dialogs/FilesystemOperationDialog.xaml index 3ffb6222d25e..e9eea56c4d92 100644 --- a/src/Files.App/Dialogs/FilesystemOperationDialog.xaml +++ b/src/Files.App/Dialogs/FilesystemOperationDialog.xaml @@ -19,7 +19,7 @@ IsPrimaryButtonEnabled="{x:Bind ViewModel.PrimaryButtonEnabled, Mode=OneWay}" Opened="FilesystemOperationDialog_Opened" PrimaryButtonText="{x:Bind ViewModel.PrimaryButtonText, Mode=OneWay}" - RequestedTheme="{x:Bind helpers:ThemeHelper.RootTheme}" + RequestedTheme="{x:Bind RootAppElement.RequestedTheme, Mode=OneWay}" SecondaryButtonCommand="{x:Bind ViewModel.SecondaryButtonClickCommand, Mode=OneWay}" SecondaryButtonText="{x:Bind ViewModel.SecondaryButtonText, Mode=OneWay}" Style="{StaticResource DefaultContentDialogStyle}" diff --git a/src/Files.App/Dialogs/FilesystemOperationDialog.xaml.cs b/src/Files.App/Dialogs/FilesystemOperationDialog.xaml.cs index d7075660242b..7da30eaaa48b 100644 --- a/src/Files.App/Dialogs/FilesystemOperationDialog.xaml.cs +++ b/src/Files.App/Dialogs/FilesystemOperationDialog.xaml.cs @@ -10,6 +10,9 @@ namespace Files.App.Dialogs { public sealed partial class FilesystemOperationDialog : ContentDialog, IDialog { + private FrameworkElement RootAppElement + => (FrameworkElement)MainWindow.Instance.Content; + public FileSystemDialogViewModel ViewModel { get => (FileSystemDialogViewModel)DataContext; diff --git a/src/Files.App/Dialogs/GitHubLoginDialog.xaml b/src/Files.App/Dialogs/GitHubLoginDialog.xaml index 2b612c20eaf4..a3e21b7b6ce5 100644 --- a/src/Files.App/Dialogs/GitHubLoginDialog.xaml +++ b/src/Files.App/Dialogs/GitHubLoginDialog.xaml @@ -15,7 +15,7 @@ HighContrastAdjustment="None" PrimaryButtonClick="ContentDialog_PrimaryButtonClick" PrimaryButtonText="{helpers:ResourceString Name=OK}" - RequestedTheme="{x:Bind helpers:ThemeHelper.RootTheme}" + RequestedTheme="{x:Bind RootAppElement.RequestedTheme, Mode=OneWay}" Style="{StaticResource DefaultContentDialogStyle}" mc:Ignorable="d"> diff --git a/src/Files.App/Dialogs/GitHubLoginDialog.xaml.cs b/src/Files.App/Dialogs/GitHubLoginDialog.xaml.cs index c33ffa0d2a78..07fe45d4036c 100644 --- a/src/Files.App/Dialogs/GitHubLoginDialog.xaml.cs +++ b/src/Files.App/Dialogs/GitHubLoginDialog.xaml.cs @@ -1,6 +1,7 @@ // Copyright (c) 2024 Files Community // Licensed under the MIT License. See the LICENSE. +using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Windows.ApplicationModel.DataTransfer; @@ -8,6 +9,9 @@ namespace Files.App.Dialogs { public sealed partial class GitHubLoginDialog : ContentDialog, IDialog { + private FrameworkElement RootAppElement + => (FrameworkElement)MainWindow.Instance.Content; + public GitHubLoginDialogViewModel ViewModel { get => (GitHubLoginDialogViewModel)DataContext; diff --git a/src/Files.App/Dialogs/ReleaseNotesDialog.xaml b/src/Files.App/Dialogs/ReleaseNotesDialog.xaml index c1dd9fb257f0..109966681635 100644 --- a/src/Files.App/Dialogs/ReleaseNotesDialog.xaml +++ b/src/Files.App/Dialogs/ReleaseNotesDialog.xaml @@ -11,7 +11,7 @@ CornerRadius="{StaticResource OverlayCornerRadius}" DefaultButton="None" HighContrastAdjustment="None" - RequestedTheme="{x:Bind helpers:ThemeHelper.RootTheme}" + RequestedTheme="{x:Bind RootAppElement.RequestedTheme, Mode=OneWay}" Style="{StaticResource DefaultContentDialogStyle}" mc:Ignorable="d"> diff --git a/src/Files.App/Dialogs/ReleaseNotesDialog.xaml.cs b/src/Files.App/Dialogs/ReleaseNotesDialog.xaml.cs index a4d666ed12bb..9035234e02c6 100644 --- a/src/Files.App/Dialogs/ReleaseNotesDialog.xaml.cs +++ b/src/Files.App/Dialogs/ReleaseNotesDialog.xaml.cs @@ -8,6 +8,9 @@ namespace Files.App.Dialogs { public sealed partial class ReleaseNotesDialog : ContentDialog, IDialog { + private FrameworkElement RootAppElement + => (FrameworkElement)MainWindow.Instance.Content; + public ReleaseNotesDialogViewModel ViewModel { get => (ReleaseNotesDialogViewModel)DataContext; diff --git a/src/Files.App/Dialogs/ReorderSidebarItemsDialog.xaml b/src/Files.App/Dialogs/ReorderSidebarItemsDialog.xaml index 643c4bc1ace8..3b6f65af8384 100644 --- a/src/Files.App/Dialogs/ReorderSidebarItemsDialog.xaml +++ b/src/Files.App/Dialogs/ReorderSidebarItemsDialog.xaml @@ -13,7 +13,7 @@ IsPrimaryButtonEnabled="True" PrimaryButtonCommand="{x:Bind ViewModel.PrimaryButtonCommand}" PrimaryButtonText="{helpers:ResourceString Name=Save}" - RequestedTheme="{x:Bind helpers:ThemeHelper.RootTheme}" + RequestedTheme="{x:Bind RootAppElement.RequestedTheme, Mode=OneWay}" SecondaryButtonText="{helpers:ResourceString Name=Cancel}" Style="{StaticResource DefaultContentDialogStyle}" mc:Ignorable="d"> diff --git a/src/Files.App/Dialogs/ReorderSidebarItemsDialog.xaml.cs b/src/Files.App/Dialogs/ReorderSidebarItemsDialog.xaml.cs index 725c273d8338..598d1da9edf6 100644 --- a/src/Files.App/Dialogs/ReorderSidebarItemsDialog.xaml.cs +++ b/src/Files.App/Dialogs/ReorderSidebarItemsDialog.xaml.cs @@ -17,6 +17,9 @@ namespace Files.App.Dialogs { public sealed partial class ReorderSidebarItemsDialog : ContentDialog, IDialog { + private FrameworkElement RootAppElement + => (FrameworkElement)MainWindow.Instance.Content; + public ReorderSidebarItemsDialogViewModel ViewModel { get => (ReorderSidebarItemsDialogViewModel)DataContext; diff --git a/src/Files.App/Helpers/Application/AppLifecycleHelper.cs b/src/Files.App/Helpers/Application/AppLifecycleHelper.cs index 1f72b5893b42..9eebb6a14434 100644 --- a/src/Files.App/Helpers/Application/AppLifecycleHelper.cs +++ b/src/Files.App/Helpers/Application/AppLifecycleHelper.cs @@ -159,6 +159,7 @@ public static IHost ConfigureHost() .AddSingleton() .AddSingleton() // Services + .AddSingleton() .AddSingleton() .AddSingleton() .AddSingleton() diff --git a/src/Files.App/Helpers/UI/ThemeHelper.cs b/src/Files.App/Helpers/UI/ThemeHelper.cs deleted file mode 100644 index 475898aec655..000000000000 --- a/src/Files.App/Helpers/UI/ThemeHelper.cs +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright (c) 2024 Files Community -// Licensed under the MIT License. See the LICENSE. - -using Microsoft.UI; -using Microsoft.UI.Windowing; -using Microsoft.UI.Xaml; -using Windows.Storage; -using Windows.UI; -using Windows.UI.ViewManagement; - -namespace Files.App.Helpers -{ - /// - /// Class providing functionality around switching and restoring theme settings - /// - public static class ThemeHelper - { - private const string selectedAppThemeKey = "theme"; - private static Window? currentApplicationWindow; - private static AppWindowTitleBar? titleBar; - private static bool isInitialized = false; - - // Keep reference so it does not get optimized/garbage collected - public static UISettings UiSettings; - - /// - /// Gets or sets (with LocalSettings persistence) the RequestedTheme of the root element. - /// - public static ElementTheme RootTheme - { - get - { - var savedTheme = ApplicationData.Current.LocalSettings.Values[selectedAppThemeKey]?.ToString(); - - return !string.IsNullOrEmpty(savedTheme) - ? EnumExtensions.GetEnum(savedTheme) - : ElementTheme.Default; - } - set - { - ApplicationData.Current.LocalSettings.Values[selectedAppThemeKey] = value.ToString(); - ApplyTheme(); - } - } - - public static bool Initialize() - { - if (isInitialized) - return false; - - isInitialized = true; - - // Save reference as this might be null when the user is in another app - currentApplicationWindow = MainWindow.Instance; - - // Set TitleBar background color - if (currentApplicationWindow is not null) - titleBar = MainWindow.Instance.AppWindow.TitleBar; - - // Apply the desired theme based on what is set in the application settings - ApplyTheme(); - - // Registering to color changes, thus we notice when user changes theme system wide - UiSettings = new UISettings(); - UiSettings.ColorValuesChanged += UiSettings_ColorValuesChanged; - - return true; - } - - public static void ApplyResources() - { - // Toggle between the themes to force reload the resource styles - ApplyTheme(ElementTheme.Dark); - ApplyTheme(ElementTheme.Light); - - // Restore the theme to the correct theme - ApplyTheme(); - } - - private static async void UiSettings_ColorValuesChanged(UISettings sender, object args) - { - // Make sure we have a reference to our window so we dispatch a UI change - if (currentApplicationWindow is null) - { - currentApplicationWindow = MainWindow.Instance; - - if (currentApplicationWindow is null) - return; - } - - titleBar ??= MainWindow.Instance.AppWindow.TitleBar; - - // Dispatch on UI thread so that we have a current appbar to access and change - await currentApplicationWindow.DispatcherQueue.EnqueueOrInvokeAsync(ApplyTheme); - } - - private static void ApplyTheme() - { - ApplyTheme(RootTheme); - } - - private static void ApplyTheme(ElementTheme rootTheme) - { - if (MainWindow.Instance.Content is FrameworkElement rootElement) - rootElement.RequestedTheme = rootTheme; - - if (titleBar is not null) - { - titleBar.ButtonBackgroundColor = Colors.Transparent; - titleBar.ButtonInactiveBackgroundColor = Colors.Transparent; - - switch (rootTheme) - { - case ElementTheme.Default: - titleBar.ButtonHoverBackgroundColor = (Color)Application.Current.Resources["SystemBaseLowColor"]; - titleBar.ButtonForegroundColor = (Color)Application.Current.Resources["SystemBaseHighColor"]; - break; - - case ElementTheme.Light: - titleBar.ButtonHoverBackgroundColor = Color.FromArgb(51, 0, 0, 0); - titleBar.ButtonForegroundColor = Colors.Black; - break; - - case ElementTheme.Dark: - titleBar.ButtonHoverBackgroundColor = Color.FromArgb(51, 255, 255, 255); - titleBar.ButtonForegroundColor = Colors.White; - break; - } - } - Ioc.Default.GetRequiredService().UpdateThemeElements.Execute(null); - } - } -} \ No newline at end of file diff --git a/src/Files.App/Services/AppThemeModeService.cs b/src/Files.App/Services/AppThemeModeService.cs new file mode 100644 index 000000000000..9507c0c50213 --- /dev/null +++ b/src/Files.App/Services/AppThemeModeService.cs @@ -0,0 +1,106 @@ +// Copyright (c) 2024 Files Community +// Licensed under the MIT License. See the LICENSE. + +using Microsoft.UI; +using Microsoft.UI.Windowing; +using Microsoft.UI.Xaml; +using Windows.Storage; +using Windows.UI; +using Windows.UI.ViewManagement; + +namespace Files.App.Services +{ + public class AppThemeModeService : IAppThemeModeService + { + private IUserSettingsService UserSettingsService { get; } = Ioc.Default.GetRequiredService(); + + private UISettings UISettings { get; } = new(); + + /// + public ElementTheme AppThemeMode + { + get + { + var theme = UserSettingsService.AppearanceSettingsService.AppThemeMode; + + return EnumExtensions.GetEnum(theme); + } + set + { + UserSettingsService.AppearanceSettingsService.AppThemeMode = value.ToString(); + + SetAppThemeMode(); + } + } + + /// + public event EventHandler? AppThemeModeChanged; + + /// + /// Initializes an instance of . + /// + public AppThemeModeService() + { + // Set the desired theme based on what is set in the application settings + SetAppThemeMode(); + + // Registering to color changes, so that we can notice when changed system theme mode + UISettings.ColorValuesChanged += UISettings_ColorValuesChanged; + } + + /// + public void ApplyResources() + { + // Toggle between the themes to force reload the resource styles + SetAppThemeMode(null, null, ElementTheme.Dark); + SetAppThemeMode(null, null, ElementTheme.Light); + + // Restore the theme to the correct one + SetAppThemeMode(); + } + + /// + public void SetAppThemeMode(Window? window = null, AppWindowTitleBar? titleBar = null, ElementTheme? rootTheme = null, bool callThemeModeChangedEvent = true) + { + window ??= MainWindow.Instance; + titleBar ??= MainWindow.Instance.AppWindow.TitleBar; + rootTheme ??= AppThemeMode; + + if (window.Content is FrameworkElement rootElement) + rootElement.RequestedTheme = (ElementTheme)rootTheme; + + if (titleBar is not null) + { + titleBar.ButtonBackgroundColor = Colors.Transparent; + titleBar.ButtonInactiveBackgroundColor = Colors.Transparent; + + switch (rootTheme) + { + case ElementTheme.Default: + titleBar.ButtonHoverBackgroundColor = (Color)Application.Current.Resources["SystemBaseLowColor"]; + titleBar.ButtonForegroundColor = (Color)Application.Current.Resources["SystemBaseHighColor"]; + break; + case ElementTheme.Light: + titleBar.ButtonHoverBackgroundColor = Color.FromArgb(51, 0, 0, 0); + titleBar.ButtonForegroundColor = Colors.Black; + break; + case ElementTheme.Dark: + titleBar.ButtonHoverBackgroundColor = Color.FromArgb(51, 255, 255, 255); + titleBar.ButtonForegroundColor = Colors.White; + break; + } + } + + if (callThemeModeChangedEvent) + AppThemeModeChanged?.Invoke(null, EventArgs.Empty); + } + + private async void UISettings_ColorValuesChanged(UISettings sender, object args) + { + await MainWindow.Instance.DispatcherQueue.EnqueueOrInvokeAsync(() => + { + SetAppThemeMode(); + }); + } + } +} diff --git a/src/Files.App/Services/ResourcesService.cs b/src/Files.App/Services/ResourcesService.cs index 9a4073dcc6fe..81b598355c81 100644 --- a/src/Files.App/Services/ResourcesService.cs +++ b/src/Files.App/Services/ResourcesService.cs @@ -9,6 +9,8 @@ namespace Files.App.Services /// public sealed class ResourcesService : IResourcesService { + private IAppThemeModeService AppThemeModeService { get; } = Ioc.Default.GetRequiredService(); + /// public void SetAppThemeBackgroundColor(Color appThemeBackgroundColor) { @@ -45,7 +47,7 @@ public void SetAppThemeFontFamily(string contentControlThemeFontFamily) /// public void ApplyResources() { - ThemeHelper.ApplyResources(); + AppThemeModeService.ApplyResources(); } } -} \ No newline at end of file +} diff --git a/src/Files.App/Services/Settings/AppearanceSettingsService.cs b/src/Files.App/Services/Settings/AppearanceSettingsService.cs index feb6eff21e8e..2afaaa0724d4 100644 --- a/src/Files.App/Services/Settings/AppearanceSettingsService.cs +++ b/src/Files.App/Services/Settings/AppearanceSettingsService.cs @@ -25,6 +25,13 @@ public bool IsSidebarOpen set => Set(value); } + /// + public string AppThemeMode + { + get => Get("Default"); + set => Set(value); + } + /// public String AppThemeBackgroundColor { diff --git a/src/Files.App/Services/Settings/IAppearanceSettingsService.cs b/src/Files.App/Services/Settings/IAppearanceSettingsService.cs index 04a572775951..445a778c07e1 100644 --- a/src/Files.App/Services/Settings/IAppearanceSettingsService.cs +++ b/src/Files.App/Services/Settings/IAppearanceSettingsService.cs @@ -19,6 +19,11 @@ public interface IAppearanceSettingsService : IBaseSettingsService, INotifyPrope #endregion + /// + /// Gets or sets a value for the app theme mode. + /// + string AppThemeMode { get; set; } + /// /// Gets or sets a value for the app theme background color. /// diff --git a/src/Files.App/Utils/Storage/Helpers/FilePropertiesHelpers.cs b/src/Files.App/Utils/Storage/Helpers/FilePropertiesHelpers.cs index 684a430d3d5e..c4df858d5260 100644 --- a/src/Files.App/Utils/Storage/Helpers/FilePropertiesHelpers.cs +++ b/src/Files.App/Utils/Storage/Helpers/FilePropertiesHelpers.cs @@ -19,6 +19,8 @@ namespace Files.App.Utils.Storage /// public static class FilePropertiesHelpers { + private static IAppThemeModeService AppThemeModeService { get; } = Ioc.Default.GetRequiredService(); + /// /// Whether LayoutDirection (FlowDirection) is set to right-to-left (RTL) /// @@ -96,7 +98,7 @@ public static void OpenPropertiesWindow(object item, IShellPage associatedInstan var frame = new Frame { - RequestedTheme = ThemeHelper.RootTheme + RequestedTheme = (ElementTheme)AppThemeModeService.AppThemeMode }; WinUIEx.WindowEx propertiesWindow; diff --git a/src/Files.App/ViewModels/MainPageViewModel.cs b/src/Files.App/ViewModels/MainPageViewModel.cs index db4bde05d779..6277edaab594 100644 --- a/src/Files.App/ViewModels/MainPageViewModel.cs +++ b/src/Files.App/ViewModels/MainPageViewModel.cs @@ -75,10 +75,6 @@ public async Task OnNavigatedToAsync(NavigationEventArgs e) if (e.NavigationMode == NavigationMode.Back) return; - // Initialize the static theme helper to capture a reference to this window - // to handle theme changes without restarting the app - var isInitialized = ThemeHelper.Initialize(); - var parameter = e.Parameter; var ignoreStartupSettings = false; if (parameter is MainPageNavigationArguments mainPageNavigationArguments) @@ -178,15 +174,12 @@ public async Task OnNavigatedToAsync(NavigationEventArgs e) await NavigationHelpers.AddNewTabByParamAsync(tabArgs.InitialPageType, tabArgs.NavigationParameter); } - if (isInitialized) - { - // Load the app theme resources - ResourcesService.LoadAppResources(AppearanceSettingsService); + // Load the app theme resources + ResourcesService.LoadAppResources(AppearanceSettingsService); - await Task.WhenAll( - DrivesViewModel.UpdateDrivesAsync(), - NetworkDrivesViewModel.UpdateDrivesAsync()); - } + await Task.WhenAll( + DrivesViewModel.UpdateDrivesAsync(), + NetworkDrivesViewModel.UpdateDrivesAsync()); } // Command methods diff --git a/src/Files.App/ViewModels/Settings/AppearanceViewModel.cs b/src/Files.App/ViewModels/Settings/AppearanceViewModel.cs index 6e463b8b65ae..c04181bd7dff 100644 --- a/src/Files.App/ViewModels/Settings/AppearanceViewModel.cs +++ b/src/Files.App/ViewModels/Settings/AppearanceViewModel.cs @@ -8,6 +8,7 @@ namespace Files.App.ViewModels.Settings { public sealed class AppearanceViewModel : ObservableObject { + private IAppThemeModeService AppThemeModeService { get; } = Ioc.Default.GetRequiredService(); private readonly IUserSettingsService UserSettingsService; private readonly IResourcesService ResourcesService; @@ -20,6 +21,7 @@ public AppearanceViewModel(IUserSettingsService userSettingsService, IResourcesS { UserSettingsService = userSettingsService; ResourcesService = resourcesService; + selectedThemeIndex = (int)Enum.Parse(AppThemeModeService.AppThemeMode.ToString()); Themes = [ @@ -84,7 +86,7 @@ public AppThemeResourceItem SelectedAppThemeResources } } - private int selectedThemeIndex = (int)Enum.Parse(typeof(ElementTheme), ThemeHelper.RootTheme.ToString()); + private int selectedThemeIndex; public int SelectedThemeIndex { get => selectedThemeIndex; @@ -92,7 +94,7 @@ public int SelectedThemeIndex { if (SetProperty(ref selectedThemeIndex, value)) { - ThemeHelper.RootTheme = (ElementTheme)value; + AppThemeModeService.AppThemeMode = (ElementTheme)value; OnPropertyChanged(nameof(SelectedElementTheme)); } } diff --git a/src/Files.App/ViewModels/SettingsViewModel.cs b/src/Files.App/ViewModels/SettingsViewModel.cs index 5e1d66bac6c1..916fc9f92ab5 100644 --- a/src/Files.App/ViewModels/SettingsViewModel.cs +++ b/src/Files.App/ViewModels/SettingsViewModel.cs @@ -4,7 +4,6 @@ using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Runtime.CompilerServices; -using System.Windows.Input; using Windows.Storage; namespace Files.App.ViewModels @@ -14,15 +13,6 @@ public sealed class SettingsViewModel : ObservableObject { private readonly ApplicationDataContainer localSettings = ApplicationData.Current.LocalSettings; - public SettingsViewModel() - { - UpdateThemeElements = new RelayCommand(() => ThemeModeChanged?.Invoke(this, EventArgs.Empty)); - } - - public event EventHandler ThemeModeChanged; - - public ICommand UpdateThemeElements { get; } - #region ReadAndSaveSettings public bool Set(TValue value, [CallerMemberName] string propertyName = null) diff --git a/src/Files.App/Views/Properties/MainPropertiesPage.xaml.cs b/src/Files.App/Views/Properties/MainPropertiesPage.xaml.cs index a9797da85036..b46a369ed7cf 100644 --- a/src/Files.App/Views/Properties/MainPropertiesPage.xaml.cs +++ b/src/Files.App/Views/Properties/MainPropertiesPage.xaml.cs @@ -17,12 +17,12 @@ namespace Files.App.Views.Properties { public sealed partial class MainPropertiesPage : BasePropertiesPage { + private IAppThemeModeService AppThemeModeService { get; } = Ioc.Default.GetRequiredService(); + private AppWindow AppWindow => Window.AppWindow; private Window Window; - private SettingsViewModel AppSettings { get; set; } - private MainPropertiesViewModel MainPropertiesViewModel { get; set; } public MainPropertiesPage() @@ -58,8 +58,7 @@ protected override void OnNavigatedTo(NavigationEventArgs e) private void Page_Loaded(object sender, RoutedEventArgs e) { - AppSettings = Ioc.Default.GetRequiredService(); - AppSettings.ThemeModeChanged += AppSettings_ThemeModeChanged; + AppThemeModeService.AppThemeModeChanged += AppThemeModeService_AppThemeModeChanged; Window.Closed += Window_Closed; UpdatePageLayout(); @@ -97,36 +96,20 @@ private void UpdatePageLayout() foreach (var item in MainPropertiesViewModel.NavigationViewItems) item.IsCompact = false; } - private async void AppSettings_ThemeModeChanged(object? sender, EventArgs e) + private async void AppThemeModeService_AppThemeModeChanged(object? sender, EventArgs e) { if (Parent is null) return; await DispatcherQueue.EnqueueOrInvokeAsync(() => { - ((Frame)Parent).RequestedTheme = ThemeHelper.RootTheme; - - switch (ThemeHelper.RootTheme) - { - case ElementTheme.Default: - AppWindow.TitleBar.ButtonHoverBackgroundColor = (Color)Application.Current.Resources["SystemBaseLowColor"]; - AppWindow.TitleBar.ButtonForegroundColor = (Color)Application.Current.Resources["SystemBaseHighColor"]; - break; - case ElementTheme.Light: - AppWindow.TitleBar.ButtonHoverBackgroundColor = Color.FromArgb(0x33, 0, 0, 0); - AppWindow.TitleBar.ButtonForegroundColor = Colors.Black; - break; - case ElementTheme.Dark: - AppWindow.TitleBar.ButtonHoverBackgroundColor = Color.FromArgb(0x33, 0xFF, 0xFF, 0xFF); - AppWindow.TitleBar.ButtonForegroundColor = Colors.White; - break; - } + AppThemeModeService.SetAppThemeMode(Window, Window.AppWindow.TitleBar, AppThemeModeService.AppThemeMode, false); }); } private void Window_Closed(object sender, WindowEventArgs args) { - AppSettings.ThemeModeChanged -= AppSettings_ThemeModeChanged; + AppThemeModeService.AppThemeModeChanged -= AppThemeModeService_AppThemeModeChanged; Window.Closed -= Window_Closed; Window.AppWindow.Changed -= AppWindow_Changed; From 27bb2e4762ef8ae0ba071be1b7211dcd06fe683a Mon Sep 17 00:00:00 2001 From: hishitetsu <66369541+hishitetsu@users.noreply.github.com> Date: Sun, 14 Apr 2024 11:43:01 +0900 Subject: [PATCH 37/41] Feature: Disable access keys when content dialogs are open (#15171) --- src/Files.App/UserControls/AddressToolbar.xaml | 9 +++++++++ .../UserControls/AddressToolbar.xaml.cs | 17 ++++++++++++++++- .../UserControls/InnerNavigationToolbar.xaml | 15 +++++++++++++++ .../UserControls/InnerNavigationToolbar.xaml.cs | 8 ++++++++ 4 files changed, 48 insertions(+), 1 deletion(-) diff --git a/src/Files.App/UserControls/AddressToolbar.xaml b/src/Files.App/UserControls/AddressToolbar.xaml index 89f8b2a05a19..69c8e342570b 100644 --- a/src/Files.App/UserControls/AddressToolbar.xaml +++ b/src/Files.App/UserControls/AddressToolbar.xaml @@ -241,6 +241,7 @@ x:Name="Back" x:FieldModifier="public" AccessKey="B" + AccessKeyInvoked="Button_AccessKeyInvoked" AutomationProperties.FullDescription="{x:Bind ViewModel.Commands.NavigateBack.Description, Mode=OneWay}" AutomationProperties.Name="{x:Bind ViewModel.Commands.NavigateBack.Label, Mode=OneWay}" Command="{x:Bind ViewModel.Commands.NavigateBack, Mode=OneWay}" @@ -254,6 +255,7 @@ x:Name="Forward" x:FieldModifier="public" AccessKey="F" + AccessKeyInvoked="Button_AccessKeyInvoked" AutomationProperties.FullDescription="{x:Bind ViewModel.Commands.NavigateForward.Description, Mode=OneWay}" AutomationProperties.Name="{x:Bind ViewModel.Commands.NavigateForward.Label, Mode=OneWay}" Command="{x:Bind ViewModel.Commands.NavigateForward, Mode=OneWay}" @@ -267,6 +269,7 @@ x:Name="Up" x:FieldModifier="public" AccessKey="U" + AccessKeyInvoked="Button_AccessKeyInvoked" AutomationProperties.FullDescription="{x:Bind ViewModel.Commands.NavigateUp.Description, Mode=OneWay}" AutomationProperties.Name="{x:Bind ViewModel.Commands.NavigateUp.Label, Mode=OneWay}" Command="{x:Bind ViewModel.Commands.NavigateUp, Mode=OneWay}" @@ -280,6 +283,7 @@ x:Name="Refresh" x:FieldModifier="public" AccessKey="R" + AccessKeyInvoked="Button_AccessKeyInvoked" AutomationProperties.Name="{x:Bind Commands.RefreshItems.Label}" Command="{x:Bind Commands.RefreshItems, Mode=OneWay}" IsEnabled="{x:Bind Commands.RefreshItems.IsExecutable, Mode=OneWay}" @@ -410,6 +414,7 @@