diff --git a/src/Files.App/Actions/BaseUIAction.cs b/src/Files.App/Actions/BaseUIAction.cs new file mode 100644 index 000000000000..e079dd6421ec --- /dev/null +++ b/src/Files.App/Actions/BaseUIAction.cs @@ -0,0 +1,29 @@ +using CommunityToolkit.Mvvm.ComponentModel; +using Files.App.Helpers; +using System.ComponentModel; +using System.Threading.Tasks; + +namespace Files.App.Actions +{ + internal abstract class BaseUIAction : ObservableObject, IAction + { + public abstract string Label { get; } + + public abstract string Description { get; } + + public virtual bool IsExecutable => UIHelpers.CanShowDialog; + + public BaseUIAction() + { + UIHelpers.PropertyChanged += UIHelpers_PropertyChanged; + } + + public abstract Task ExecuteAsync(); + + private void UIHelpers_PropertyChanged(object? sender, PropertyChangedEventArgs e) + { + if (e.PropertyName is nameof(UIHelpers.CanShowDialog)) + OnPropertyChanged(nameof(IsExecutable)); + } + } +} diff --git a/src/Files.App/Actions/Content/Archives/CompressIntoArchiveAction.cs b/src/Files.App/Actions/Content/Archives/CompressIntoArchiveAction.cs index 2c878e9f739e..0679bc76a8f9 100644 --- a/src/Files.App/Actions/Content/Archives/CompressIntoArchiveAction.cs +++ b/src/Files.App/Actions/Content/Archives/CompressIntoArchiveAction.cs @@ -1,32 +1,34 @@ -using CommunityToolkit.Mvvm.ComponentModel; -using CommunityToolkit.Mvvm.DependencyInjection; +using CommunityToolkit.Mvvm.DependencyInjection; using Files.App.Contexts; using Files.App.Dialogs; using Files.App.Extensions; using Files.App.Filesystem.Archive; using Files.App.Helpers; +using Microsoft.UI.Xaml.Controls; using System.ComponentModel; using System.Threading.Tasks; namespace Files.App.Actions { - internal class CompressIntoArchiveAction : ObservableObject, IAction + internal class CompressIntoArchiveAction : BaseUIAction { private readonly IContentPageContext context = Ioc.Default.GetRequiredService(); - public string Label => "CreateArchive".GetLocalizedResource(); + public override string Label => "CreateArchive".GetLocalizedResource(); - public string Description => "TODO: Need to be described."; + public override string Description => "TODO: Need to be described."; - public bool IsExecutable => IsContextPageTypeAdaptedToCommand() - && ArchiveHelpers.CanCompress(context.SelectedItems); + public override bool IsExecutable => + IsContextPageTypeAdaptedToCommand() && + ArchiveHelpers.CanCompress(context.SelectedItems) && + UIHelpers.CanShowDialog; public CompressIntoArchiveAction() { context.PropertyChanged += Context_PropertyChanged; } - public async Task ExecuteAsync() + public override async Task ExecuteAsync() { var (sources, directory, fileName) = ArchiveHelpers.GetCompressDestination(context.ShellPage); @@ -34,9 +36,9 @@ public async Task ExecuteAsync() { FileName = fileName, }; - await dialog.ShowAsync(); + var result = await dialog.TryShowAsync(); - if (!dialog.CanCreate) + if (!dialog.CanCreate || result != ContentDialogResult.Primary) return; IArchiveCreator creator = new ArchiveCreator diff --git a/src/Files.App/Actions/Content/Archives/DecompressArchive.cs b/src/Files.App/Actions/Content/Archives/DecompressArchive.cs index 42c2a6bdb6b9..8e7122ea0f6f 100644 --- a/src/Files.App/Actions/Content/Archives/DecompressArchive.cs +++ b/src/Files.App/Actions/Content/Archives/DecompressArchive.cs @@ -1,5 +1,4 @@ -using CommunityToolkit.Mvvm.ComponentModel; -using CommunityToolkit.Mvvm.DependencyInjection; +using CommunityToolkit.Mvvm.DependencyInjection; using Files.App.Commands; using Files.App.Contexts; using Files.App.Extensions; @@ -9,25 +8,27 @@ namespace Files.App.Actions { - internal class DecompressArchive : ObservableObject, IAction + internal class DecompressArchive : BaseUIAction { private readonly IContentPageContext context = Ioc.Default.GetRequiredService(); - public string Label => "ExtractFiles".GetLocalizedResource(); + public override string Label => "ExtractFiles".GetLocalizedResource(); - public string Description => "TODO: Need to be described."; + public override string Description => "TODO: Need to be described."; public HotKey HotKey { get; } = new(Keys.E, KeyModifiers.Ctrl); - public bool IsExecutable => IsContextPageTypeAdaptedToCommand() - && ArchiveHelpers.CanDecompress(context.SelectedItems); + public override bool IsExecutable => + IsContextPageTypeAdaptedToCommand() && + ArchiveHelpers.CanDecompress(context.SelectedItems) && + UIHelpers.CanShowDialog; public DecompressArchive() { context.PropertyChanged += Context_PropertyChanged; } - public async Task ExecuteAsync() + public override async Task ExecuteAsync() { await ArchiveHelpers.DecompressArchive(context.ShellPage); } diff --git a/src/Files.App/Actions/Content/Archives/DecompressArchiveHere.cs b/src/Files.App/Actions/Content/Archives/DecompressArchiveHere.cs index 68c0fb64ee37..638bb05a14a7 100644 --- a/src/Files.App/Actions/Content/Archives/DecompressArchiveHere.cs +++ b/src/Files.App/Actions/Content/Archives/DecompressArchiveHere.cs @@ -1,5 +1,4 @@ -using CommunityToolkit.Mvvm.ComponentModel; -using CommunityToolkit.Mvvm.DependencyInjection; +using CommunityToolkit.Mvvm.DependencyInjection; using Files.App.Contexts; using Files.App.Extensions; using Files.App.Helpers; @@ -8,23 +7,25 @@ namespace Files.App.Actions { - internal class DecompressArchiveHere : ObservableObject, IAction + internal class DecompressArchiveHere : BaseUIAction { private readonly IContentPageContext context = Ioc.Default.GetRequiredService(); - public string Label => "ExtractHere".GetLocalizedResource(); + public override string Label => "ExtractHere".GetLocalizedResource(); - public string Description => "TODO: Need to be described."; + public override string Description => "TODO: Need to be described."; - public bool IsExecutable => IsContextPageTypeAdaptedToCommand() - && ArchiveHelpers.CanDecompress(context.SelectedItems); + public override bool IsExecutable => + IsContextPageTypeAdaptedToCommand() && + ArchiveHelpers.CanDecompress(context.SelectedItems) && + UIHelpers.CanShowDialog; public DecompressArchiveHere() { context.PropertyChanged += Context_PropertyChanged; } - public async Task ExecuteAsync() + public override async Task ExecuteAsync() { await ArchiveHelpers.DecompressArchiveHere(context.ShellPage); } diff --git a/src/Files.App/Actions/Content/Archives/DecompressArchiveToChildFolderAction.cs b/src/Files.App/Actions/Content/Archives/DecompressArchiveToChildFolderAction.cs index 229a42644086..2f70d00b6c0a 100644 --- a/src/Files.App/Actions/Content/Archives/DecompressArchiveToChildFolderAction.cs +++ b/src/Files.App/Actions/Content/Archives/DecompressArchiveToChildFolderAction.cs @@ -1,5 +1,4 @@ -using CommunityToolkit.Mvvm.ComponentModel; -using CommunityToolkit.Mvvm.DependencyInjection; +using CommunityToolkit.Mvvm.DependencyInjection; using Files.App.Contexts; using Files.App.Extensions; using Files.App.Helpers; @@ -10,23 +9,25 @@ namespace Files.App.Actions { - internal class DecompressArchiveToChildFolderAction : ObservableObject, IAction + internal class DecompressArchiveToChildFolderAction : BaseUIAction { private readonly IContentPageContext context = Ioc.Default.GetRequiredService(); - public string Label => ComputeLabel(); + public override string Label => ComputeLabel(); - public string Description => "TODO: Need to be described."; + public override string Description => "TODO: Need to be described."; - public bool IsExecutable => IsContextPageTypeAdaptedToCommand() - && ArchiveHelpers.CanDecompress(context.SelectedItems); + public override bool IsExecutable => + IsContextPageTypeAdaptedToCommand() && + ArchiveHelpers.CanDecompress(context.SelectedItems) && + UIHelpers.CanShowDialog; public DecompressArchiveToChildFolderAction() { context.PropertyChanged += Context_PropertyChanged; } - public async Task ExecuteAsync() + public override async Task ExecuteAsync() { await ArchiveHelpers.DecompressArchiveToChildFolder(context.ShellPage); } diff --git a/src/Files.App/Actions/FileSystem/CreateFolderAction.cs b/src/Files.App/Actions/FileSystem/CreateFolderAction.cs index 45fcbb68df69..aa890aa6b185 100644 --- a/src/Files.App/Actions/FileSystem/CreateFolderAction.cs +++ b/src/Files.App/Actions/FileSystem/CreateFolderAction.cs @@ -1,5 +1,4 @@ -using CommunityToolkit.Mvvm.ComponentModel; -using CommunityToolkit.Mvvm.DependencyInjection; +using CommunityToolkit.Mvvm.DependencyInjection; using Files.App.Commands; using Files.App.Contexts; using Files.App.Extensions; @@ -10,24 +9,24 @@ namespace Files.App.Actions { - internal class CreateFolderAction : ObservableObject, IAction + internal class CreateFolderAction : BaseUIAction { private readonly IContentPageContext context = Ioc.Default.GetRequiredService(); - public string Label { get; } = "Folder".GetLocalizedResource(); + public override string Label { get; } = "Folder".GetLocalizedResource(); - public string Description => "TODO: Need to be described."; + public override string Description => "TODO: Need to be described."; public RichGlyph Glyph { get; } = new RichGlyph(baseGlyph: "\uE8B7"); - public bool IsExecutable => context.ShellPage is not null; + public override bool IsExecutable => context.ShellPage is not null && UIHelpers.CanShowDialog; public CreateFolderAction() { context.PropertyChanged += Context_PropertyChanged; } - public Task ExecuteAsync() + public override Task ExecuteAsync() { if (context.ShellPage is not null) UIFilesystemHelpers.CreateFileFromDialogResultType(AddItemDialogItemType.Folder, null!, context.ShellPage); diff --git a/src/Files.App/Actions/FileSystem/CreateShortcutAction.cs b/src/Files.App/Actions/FileSystem/CreateShortcutAction.cs index c946c71af710..63a7553e1201 100644 --- a/src/Files.App/Actions/FileSystem/CreateShortcutAction.cs +++ b/src/Files.App/Actions/FileSystem/CreateShortcutAction.cs @@ -1,5 +1,4 @@ -using CommunityToolkit.Mvvm.ComponentModel; -using CommunityToolkit.Mvvm.DependencyInjection; +using CommunityToolkit.Mvvm.DependencyInjection; using Files.App.Commands; using Files.App.Contexts; using Files.App.Extensions; @@ -11,24 +10,24 @@ namespace Files.App.Actions { - internal class CreateShortcutAction : ObservableObject, IAction + internal class CreateShortcutAction : BaseUIAction { private readonly IContentPageContext context = Ioc.Default.GetRequiredService(); - public string Label { get; } = "CreateShortcut".GetLocalizedResource(); + public override string Label { get; } = "CreateShortcut".GetLocalizedResource(); - public string Description => "TODO: Need to be described."; + public override string Description => "TODO: Need to be described."; public RichGlyph Glyph { get; } = new RichGlyph(opacityStyle: "ColorIconShortcut"); - public bool IsExecutable => context.HasSelection; + public override bool IsExecutable => context.HasSelection && UIHelpers.CanShowDialog; public CreateShortcutAction() { context.PropertyChanged += Context_PropertyChanged; } - public async Task ExecuteAsync() + public override async Task ExecuteAsync() { var currentPath = context.ShellPage?.FilesystemViewModel.WorkingDirectory; diff --git a/src/Files.App/Actions/FileSystem/CreateShortcutFromDialogAction.cs b/src/Files.App/Actions/FileSystem/CreateShortcutFromDialogAction.cs index 8f193ad5228e..4b3a241f2703 100644 --- a/src/Files.App/Actions/FileSystem/CreateShortcutFromDialogAction.cs +++ b/src/Files.App/Actions/FileSystem/CreateShortcutFromDialogAction.cs @@ -1,27 +1,39 @@ -using CommunityToolkit.Mvvm.ComponentModel; -using CommunityToolkit.Mvvm.DependencyInjection; +using CommunityToolkit.Mvvm.DependencyInjection; using Files.App.Commands; using Files.App.Contexts; using Files.App.Extensions; using Files.App.Helpers; +using System.ComponentModel; using System.Threading.Tasks; namespace Files.App.Actions { - internal class CreateShortcutFromDialogAction : ObservableObject, IAction + internal class CreateShortcutFromDialogAction : BaseUIAction { private readonly IContentPageContext context = Ioc.Default.GetRequiredService(); - public string Label { get; } = "Shortcut".GetLocalizedResource(); + public override string Label { get; } = "Shortcut".GetLocalizedResource(); - public string Description => "TODO: Need to be described."; + public override string Description => "TODO: Need to be described."; public RichGlyph Glyph { get; } = new RichGlyph(opacityStyle: "ColorIconShortcut"); - public async Task ExecuteAsync() + public override bool IsExecutable => context.ShellPage is not null && UIHelpers.CanShowDialog; + + public CreateShortcutFromDialogAction() + { + context.PropertyChanged += Context_PropertyChanged; + } + + public override async Task ExecuteAsync() + { + await UIFilesystemHelpers.CreateShortcutFromDialogAsync(context.ShellPage); + } + + private void Context_PropertyChanged(object? sender, PropertyChangedEventArgs e) { - if (context.ShellPage is not null) - await UIFilesystemHelpers.CreateShortcutFromDialogAsync(context.ShellPage); + if (e.PropertyName is nameof(IContentPageContext.ShellPage)) + OnPropertyChanged(nameof(IsExecutable)); } } } diff --git a/src/Files.App/Actions/FileSystem/DeleteItemAction.cs b/src/Files.App/Actions/FileSystem/DeleteItemAction.cs index 13872bf4a346..8ac96730028f 100644 --- a/src/Files.App/Actions/FileSystem/DeleteItemAction.cs +++ b/src/Files.App/Actions/FileSystem/DeleteItemAction.cs @@ -1,5 +1,4 @@ -using CommunityToolkit.Mvvm.ComponentModel; -using CommunityToolkit.Mvvm.DependencyInjection; +using CommunityToolkit.Mvvm.DependencyInjection; using Files.App.Commands; using Files.App.Contexts; using Files.App.Extensions; @@ -13,29 +12,30 @@ namespace Files.App.Actions { - internal class DeleteItemAction : ObservableObject, IAction + internal class DeleteItemAction : BaseUIAction { private readonly IContentPageContext context = Ioc.Default.GetRequiredService(); private readonly IFoldersSettingsService settings = Ioc.Default.GetRequiredService(); - public string Label { get; } = "Delete".GetLocalizedResource(); + public override string Label { get; } = "Delete".GetLocalizedResource(); - public string Description => "TODO: Need to be described."; + public override string Description => "TODO: Need to be described."; public RichGlyph Glyph { get; } = new RichGlyph(opacityStyle: "ColorIconDelete"); public HotKey HotKey { get; } = new(Keys.Delete); - public bool IsExecutable => + public override bool IsExecutable => context.HasSelection && - (!context.ShellPage?.SlimContentPage?.IsRenamingItem ?? false); + (!context.ShellPage?.SlimContentPage?.IsRenamingItem ?? false) && + UIHelpers.CanShowDialog; public DeleteItemAction() { context.PropertyChanged += Context_PropertyChanged; } - public async Task ExecuteAsync() + public override async Task ExecuteAsync() { if (context.ShellPage is null || !IsExecutable) return; diff --git a/src/Files.App/Actions/FileSystem/EmptyRecycleBinAction.cs b/src/Files.App/Actions/FileSystem/EmptyRecycleBinAction.cs index bfa97c0edc68..987ebed22102 100644 --- a/src/Files.App/Actions/FileSystem/EmptyRecycleBinAction.cs +++ b/src/Files.App/Actions/FileSystem/EmptyRecycleBinAction.cs @@ -1,5 +1,4 @@ -using CommunityToolkit.Mvvm.ComponentModel; -using CommunityToolkit.Mvvm.DependencyInjection; +using CommunityToolkit.Mvvm.DependencyInjection; using Files.App.Commands; using Files.App.Contexts; using Files.App.Extensions; @@ -9,32 +8,27 @@ namespace Files.App.Actions { - internal class EmptyRecycleBinAction : ObservableObject, IAction + internal class EmptyRecycleBinAction : BaseUIAction { private readonly IContentPageContext context = Ioc.Default.GetRequiredService(); - public string Label { get; } = "EmptyRecycleBin".GetLocalizedResource(); + public override string Label { get; } = "EmptyRecycleBin".GetLocalizedResource(); - public string Description => "TODO: Need to be described."; + public override string Description => "TODO: Need to be described."; public RichGlyph Glyph { get; } = new RichGlyph(opacityStyle: "ColorIconDelete"); - public bool IsExecutable - { - get - { - if (context.PageType is ContentPageTypes.RecycleBin) - return context.HasItem; - return RecycleBinHelpers.RecycleBinHasItems(); - } - } + public override bool IsExecutable => + UIHelpers.CanShowDialog && + ((context.PageType is ContentPageTypes.RecycleBin && context.HasItem) || + RecycleBinHelpers.RecycleBinHasItems()); public EmptyRecycleBinAction() { context.PropertyChanged += Context_PropertyChanged; } - public async Task ExecuteAsync() + public override async Task ExecuteAsync() { await RecycleBinHelpers.EmptyRecycleBin(); } @@ -45,8 +39,7 @@ private void Context_PropertyChanged(object? sender, PropertyChangedEventArgs e) { case nameof(IContentPageContext.PageType): case nameof(IContentPageContext.HasItem): - if (context.PageType is ContentPageTypes.RecycleBin) - OnPropertyChanged(nameof(IsExecutable)); + OnPropertyChanged(nameof(IsExecutable)); break; } } diff --git a/src/Files.App/Actions/FileSystem/PasteItemToSelectionAction.cs b/src/Files.App/Actions/FileSystem/PasteItemToSelectionAction.cs index 0955ca2ac2c7..1f3aff9f45d9 100644 --- a/src/Files.App/Actions/FileSystem/PasteItemToSelectionAction.cs +++ b/src/Files.App/Actions/FileSystem/PasteItemToSelectionAction.cs @@ -1,5 +1,4 @@ -using CommunityToolkit.Mvvm.ComponentModel; -using CommunityToolkit.Mvvm.DependencyInjection; +using CommunityToolkit.Mvvm.DependencyInjection; using Files.App.Commands; using Files.App.Contexts; using Files.App.DataModels; @@ -11,20 +10,20 @@ namespace Files.App.Actions { - internal class PasteItemToSelectionAction : ObservableObject, IAction + internal class PasteItemToSelectionAction : BaseUIAction { private readonly IContentPageContext context = Ioc.Default.GetRequiredService(); - public string Label { get; } = "Paste".GetLocalizedResource(); + public override string Label { get; } = "Paste".GetLocalizedResource(); - public string Description => "PasteItemToSelectionDescription".GetLocalizedResource(); + public override string Description => "PasteItemToSelectionDescription".GetLocalizedResource(); public RichGlyph Glyph { get; } = new(opacityStyle: "ColorIconPaste"); public HotKey HotKey { get; } = new(Keys.V, KeyModifiers.CtrlShift); private bool isExecutable; - public bool IsExecutable => isExecutable; + public override bool IsExecutable => isExecutable; public PasteItemToSelectionAction() { @@ -34,7 +33,7 @@ public PasteItemToSelectionAction() App.AppModel.PropertyChanged += AppModel_PropertyChanged; } - public async Task ExecuteAsync() + public override async Task ExecuteAsync() { if (context.ShellPage is null) return; @@ -54,7 +53,7 @@ public bool GetIsExecutable() return false; if (!context.HasSelection) return true; - return context.SelectedItem?.PrimaryItemAttribute is Windows.Storage.StorageItemTypes.Folder; + return context.SelectedItem?.PrimaryItemAttribute is Windows.Storage.StorageItemTypes.Folder && UIHelpers.CanShowDialog; } private void Context_PropertyChanged(object? sender, PropertyChangedEventArgs e) diff --git a/src/Files.App/Actions/FileSystem/RestoreAllRecycleBinAction.cs b/src/Files.App/Actions/FileSystem/RestoreAllRecycleBinAction.cs index a4c65d8aee44..fe8df94c803e 100644 --- a/src/Files.App/Actions/FileSystem/RestoreAllRecycleBinAction.cs +++ b/src/Files.App/Actions/FileSystem/RestoreAllRecycleBinAction.cs @@ -1,5 +1,4 @@ -using CommunityToolkit.Mvvm.ComponentModel; -using CommunityToolkit.Mvvm.DependencyInjection; +using CommunityToolkit.Mvvm.DependencyInjection; using Files.App.Commands; using Files.App.Contexts; using Files.App.Extensions; @@ -9,32 +8,28 @@ namespace Files.App.Actions { - internal class RestoreAllRecycleBinAction : ObservableObject, IAction + internal class RestoreAllRecycleBinAction : BaseUIAction { private readonly IContentPageContext context = Ioc.Default.GetRequiredService(); - public string Label { get; } = "RestoreAllItems".GetLocalizedResource(); + public override string Label { get; } = "RestoreAllItems".GetLocalizedResource(); - public string Description => "TODO: Need to be described."; + public override string Description => "TODO: Need to be described."; public RichGlyph Glyph { get; } = new RichGlyph(opacityStyle: "ColorIconRestoreItem"); - public bool IsExecutable - { - get - { - if (context.PageType is ContentPageTypes.RecycleBin) - return context.HasItem; - return RecycleBinHelpers.RecycleBinHasItems(); - } - } + public override bool IsExecutable => + context.ShellPage is not null && + UIHelpers.CanShowDialog && + ((context.PageType is ContentPageTypes.RecycleBin && context.HasItem) || + RecycleBinHelpers.RecycleBinHasItems()); public RestoreAllRecycleBinAction() { context.PropertyChanged += Context_PropertyChanged; } - public async Task ExecuteAsync() + public override async Task ExecuteAsync() { if (context.ShellPage is not null) await RecycleBinHelpers.RestoreRecycleBin(context.ShellPage); diff --git a/src/Files.App/Actions/FileSystem/RestoreRecycleBinAction.cs b/src/Files.App/Actions/FileSystem/RestoreRecycleBinAction.cs index ecde003090a3..71cbd4c8c96c 100644 --- a/src/Files.App/Actions/FileSystem/RestoreRecycleBinAction.cs +++ b/src/Files.App/Actions/FileSystem/RestoreRecycleBinAction.cs @@ -1,5 +1,4 @@ -using CommunityToolkit.Mvvm.ComponentModel; -using CommunityToolkit.Mvvm.DependencyInjection; +using CommunityToolkit.Mvvm.DependencyInjection; using Files.App.Commands; using Files.App.Contexts; using Files.App.Extensions; @@ -10,24 +9,27 @@ namespace Files.App.Actions { - internal class RestoreRecycleBinAction : ObservableObject, IAction + internal class RestoreRecycleBinAction : BaseUIAction { private readonly IContentPageContext context = Ioc.Default.GetRequiredService(); - public string Label { get; } = "Restore".GetLocalizedResource(); + public override string Label { get; } = "Restore".GetLocalizedResource(); - public string Description => "TODO: Need to be described."; + public override string Description => "TODO: Need to be described."; public RichGlyph Glyph { get; } = new RichGlyph(opacityStyle: "ColorIconRestoreItem"); - public bool IsExecutable => context.PageType is ContentPageTypes.RecycleBin && context.SelectedItems.Any(); + public override bool IsExecutable => + context.PageType is ContentPageTypes.RecycleBin && + context.SelectedItems.Any() && + UIHelpers.CanShowDialog; public RestoreRecycleBinAction() { context.PropertyChanged += Context_PropertyChanged; } - public async Task ExecuteAsync() + public override async Task ExecuteAsync() { if (context.ShellPage is not null) await RecycleBinHelpers.RestoreSelectionRecycleBin(context.ShellPage); diff --git a/src/Files.App/Actions/Open/OpenSettingsAction.cs b/src/Files.App/Actions/Open/OpenSettingsAction.cs index 9d3ec7b20bef..3d8a495c0644 100644 --- a/src/Files.App/Actions/Open/OpenSettingsAction.cs +++ b/src/Files.App/Actions/Open/OpenSettingsAction.cs @@ -1,26 +1,26 @@ using CommunityToolkit.Mvvm.DependencyInjection; using Files.App.Commands; using Files.App.Extensions; -using Files.Backend.Extensions; +using Files.App.Helpers; using Files.Backend.Services; using Files.Backend.ViewModels.Dialogs; using System.Threading.Tasks; namespace Files.App.Actions { - internal class OpenSettingsAction : IAction + internal class OpenSettingsAction : BaseUIAction { private readonly IDialogService dialogService = Ioc.Default.GetRequiredService(); private readonly SettingsDialogViewModel viewModel = new(); - public string Label => "Settings".GetLocalizedResource(); + public override string Label => "Settings".GetLocalizedResource(); - public string Description => "Settings".GetLocalizedResource(); + public override string Description => "Settings".GetLocalizedResource(); public HotKey HotKey { get; } = new(Keys.OemComma, KeyModifiers.Ctrl); - public async Task ExecuteAsync() + public override async Task ExecuteAsync() { var dialog = dialogService.GetDialog(viewModel); await dialog.TryShowAsync(); diff --git a/src/Files.App/Dialogs/DynamicDialog.xaml.cs b/src/Files.App/Dialogs/DynamicDialog.xaml.cs index 0ac52c7b583b..05f59a37e289 100644 --- a/src/Files.App/Dialogs/DynamicDialog.xaml.cs +++ b/src/Files.App/Dialogs/DynamicDialog.xaml.cs @@ -1,3 +1,4 @@ +using Files.App.Helpers; using Files.App.ViewModels.Dialogs; using Files.Shared.Enums; using Microsoft.UI.Xaml.Controls; @@ -21,17 +22,7 @@ public DynamicDialogResult DynamicResult get => ViewModel.DynamicResult; } - public new Task ShowAsync() => SetContentDialogRoot(this).ShowAsync().AsTask(); - - // WINUI3 - private ContentDialog SetContentDialogRoot(ContentDialog contentDialog) - { - if (Windows.Foundation.Metadata.ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8)) - { - contentDialog.XamlRoot = App.Window.Content.XamlRoot; - } - return contentDialog; - } + public new Task ShowAsync() => this.TryShowAsync(); public DynamicDialog(DynamicDialogViewModel dynamicDialogViewModel) { diff --git a/src/Files.App/Filesystem/FilesystemOperations/FilesystemOperations.cs b/src/Files.App/Filesystem/FilesystemOperations/FilesystemOperations.cs index ac955f7bfd85..64b72233a96e 100644 --- a/src/Files.App/Filesystem/FilesystemOperations/FilesystemOperations.cs +++ b/src/Files.App/Filesystem/FilesystemOperations/FilesystemOperations.cs @@ -166,7 +166,7 @@ await DialogDisplayHelper.ShowDialogAsync( CloseButtonText = "Cancel".GetLocalizedResource() }; - ContentDialogResult result = await SetContentDialogRoot(dialog).ShowAsync(); + ContentDialogResult result = await dialog.TryShowAsync(); if (result == ContentDialogResult.Primary) { @@ -289,16 +289,6 @@ await DialogDisplayHelper.ShowDialogAsync( return new StorageHistory(FileOperationType.Copy, source, pathWithType); } - // WINUI3 - private ContentDialog SetContentDialogRoot(ContentDialog contentDialog) - { - if (Windows.Foundation.Metadata.ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8)) - { - contentDialog.XamlRoot = App.Window.Content.XamlRoot; - } - return contentDialog; - } - public Task MoveAsync(IStorageItem source, string destination, NameCollisionOption collision, @@ -358,7 +348,7 @@ await DialogDisplayHelper.ShowDialogAsync( CloseButtonText = "Cancel".GetLocalizedResource() }; - ContentDialogResult result = await SetContentDialogRoot(dialog).ShowAsync(); + ContentDialogResult result = await dialog.TryShowAsync(); if (result == ContentDialogResult.Primary) { diff --git a/src/Files.App/Helpers/RecycleBinHelpers.cs b/src/Files.App/Helpers/RecycleBinHelpers.cs index 1ec469f2bdd8..e2703de2a3d5 100644 --- a/src/Files.App/Helpers/RecycleBinHelpers.cs +++ b/src/Files.App/Helpers/RecycleBinHelpers.cs @@ -68,7 +68,7 @@ public static async Task EmptyRecycleBin() }; if (userSettingsService.FoldersSettingsService.DeleteConfirmationPolicy is DeleteConfirmationPolicies.Never - || await SetContentDialogRoot(ConfirmEmptyBinDialog).ShowAsync() == ContentDialogResult.Primary) + || await ConfirmEmptyBinDialog.TryShowAsync() == ContentDialogResult.Primary) { string bannerTitle = "EmptyRecycleBin".GetLocalizedResource(); var banner = ongoingTasksViewModel.PostBanner( @@ -108,7 +108,7 @@ public static async Task RestoreRecycleBin(IShellPage associatedInstance) DefaultButton = ContentDialogButton.Primary }; - ContentDialogResult result = await SetContentDialogRoot(ConfirmEmptyBinDialog).ShowAsync(); + ContentDialogResult result = await ConfirmEmptyBinDialog.TryShowAsync(); if (result == ContentDialogResult.Primary) { @@ -128,22 +128,12 @@ public static async Task RestoreSelectionRecycleBin(IShellPage associatedInstanc DefaultButton = ContentDialogButton.Primary }; - ContentDialogResult result = await SetContentDialogRoot(ConfirmEmptyBinDialog).ShowAsync(); + ContentDialogResult result = await ConfirmEmptyBinDialog.TryShowAsync(); if (result == ContentDialogResult.Primary) await RestoreItem(associatedInstance); } - //WINUI3 - private static ContentDialog SetContentDialogRoot(ContentDialog contentDialog) - { - if (Windows.Foundation.Metadata.ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8)) - { - contentDialog.XamlRoot = App.Window.Content.XamlRoot; - } - return contentDialog; - } - public static async Task HasRecycleBin(string? path) { if (string.IsNullOrEmpty(path) || path.StartsWith(@"\\?\", StringComparison.Ordinal)) diff --git a/src/Files.App/Helpers/UIFilesystemHelpers.cs b/src/Files.App/Helpers/UIFilesystemHelpers.cs index 133e16961d71..32dbedb6e761 100644 --- a/src/Files.App/Helpers/UIFilesystemHelpers.cs +++ b/src/Files.App/Helpers/UIFilesystemHelpers.cs @@ -285,15 +285,6 @@ public static async void CreateFileFromDialogResultType(AddItemDialogItemType it await CreateFileFromDialogResultTypeForResult(itemType, itemInfo, associatedInstance); } - // WINUI3 - private static ContentDialog SetContentDialogRoot(ContentDialog contentDialog) - { - if (Windows.Foundation.Metadata.ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8)) - contentDialog.XamlRoot = App.Window.Content.XamlRoot; - - return contentDialog; - } - public static async Task CreateFileFromDialogResultTypeForResult(AddItemDialogItemType itemType, ShellNewEntry itemInfo, IShellPage associatedInstance) { string currentPath = null; @@ -314,7 +305,7 @@ public static async Task CreateFileFromDialogResultTypeForResult(A if (itemType != AddItemDialogItemType.File || itemInfo?.Command is null) { DynamicDialog dialog = DynamicDialogFactory.GetFor_RenameDialog(); - await SetContentDialogRoot(dialog).ShowAsync(); // Show rename dialog + await dialog.TryShowAsync(); // Show rename dialog if (dialog.DynamicResult != DynamicDialogResult.Primary) return null; diff --git a/src/Files.App/Helpers/UIHelpers.cs b/src/Files.App/Helpers/UIHelpers.cs index 18fff24e50d6..5e2b1686a9da 100644 --- a/src/Files.App/Helpers/UIHelpers.cs +++ b/src/Files.App/Helpers/UIHelpers.cs @@ -1,12 +1,15 @@ using CommunityToolkit.WinUI.Notifications; using Files.App.Extensions; using Files.App.Shell; +using Files.Backend.ViewModels.Dialogs; using Files.Shared; +using Files.Shared.Enums; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Media; using Microsoft.UI.Xaml.Media.Imaging; using System; using System.Collections.Generic; +using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Linq; @@ -17,6 +20,21 @@ namespace Files.App.Helpers { public static class UIHelpers { + public static event PropertyChangedEventHandler? PropertyChanged; + + private static bool canShowDialog = true; + public static bool CanShowDialog + { + get => canShowDialog; + private set + { + if (value == canShowDialog) + return; + canShowDialog = value; + PropertyChanged?.Invoke(null, new PropertyChangedEventArgs(nameof(CanShowDialog))); + } + } + /// /// Displays a toast or dialog to indicate the result of /// a device ejection operation. @@ -73,14 +91,28 @@ await DialogDisplayHelper.ShowDialogAsync( public static async Task TryShowAsync(this ContentDialog dialog) { + if (!canShowDialog) + return ContentDialogResult.None; + try { + CanShowDialog = false; return await SetContentDialogRoot(dialog).ShowAsync(); } catch // A content dialog is already open { return ContentDialogResult.None; } + finally + { + CanShowDialog = true; + } + } + + public static async Task TryShowAsync(this IDialog dialog) + where TViewModel : class, INotifyPropertyChanged + { + return (DialogResult)await ((ContentDialog)dialog).TryShowAsync(); } // WINUI3 diff --git a/src/Files.App/ServicesImplementation/DialogService.cs b/src/Files.App/ServicesImplementation/DialogService.cs index f4d982c10d8a..eda915eb9ced 100644 --- a/src/Files.App/ServicesImplementation/DialogService.cs +++ b/src/Files.App/ServicesImplementation/DialogService.cs @@ -1,4 +1,5 @@ using Files.App.Dialogs; +using Files.App.Helpers; using Files.App.ViewModels.Dialogs; using Files.Backend.Services; using Files.Backend.ViewModels.Dialogs; @@ -61,7 +62,7 @@ public Task ShowDialogAsync(TViewModel viewModel) { try { - return GetDialog(viewModel).ShowAsync(); + return GetDialog(viewModel).TryShowAsync(); } catch (Exception ex) { diff --git a/src/Files.App/ServicesImplementation/UpdateService.cs b/src/Files.App/ServicesImplementation/UpdateService.cs index d6d590cf87ed..bea030f3e602 100644 --- a/src/Files.App/ServicesImplementation/UpdateService.cs +++ b/src/Files.App/ServicesImplementation/UpdateService.cs @@ -1,6 +1,7 @@ using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.WinUI.Helpers; using Files.App.Extensions; +using Files.App.Helpers; using Files.Backend.Services; using Microsoft.Extensions.Logging; using Microsoft.UI.Xaml.Controls; @@ -146,7 +147,7 @@ private static async Task ShowDialogAsync() PrimaryButtonText = "ConsentDialogPrimaryButtonText".GetLocalizedResource() }; - ContentDialogResult result = await SetContentDialogRoot(dialog).ShowAsync(); + ContentDialogResult result = await dialog.TryShowAsync(); return result == ContentDialogResult.Primary; } @@ -223,16 +224,6 @@ bool HashEqual(Stream a, Stream b) } } - // WINUI3 - private static ContentDialog SetContentDialogRoot(ContentDialog contentDialog) - { - if (Windows.Foundation.Metadata.ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8)) - { - contentDialog.XamlRoot = App.Window.Content.XamlRoot; - } - return contentDialog; - } - private bool HasUpdates() { return _updatePackages is not null && _updatePackages.Count >= 1; diff --git a/src/Files.App/ViewModels/Widgets/Bundles/BundleContainerViewModel.cs b/src/Files.App/ViewModels/Widgets/Bundles/BundleContainerViewModel.cs index df7124374637..dcc069d97dca 100644 --- a/src/Files.App/ViewModels/Widgets/Bundles/BundleContainerViewModel.cs +++ b/src/Files.App/ViewModels/Widgets/Bundles/BundleContainerViewModel.cs @@ -252,7 +252,7 @@ private async Task RenameBundle() DynamicButtons = DynamicDialogButtons.Primary | DynamicDialogButtons.Cancel }); - await dialog.ShowAsync(); + await dialog.TryShowAsync(); bool CanAddBundleSetErrorMessage() { diff --git a/src/Files.App/ViewModels/Widgets/Bundles/BundlesViewModel.cs b/src/Files.App/ViewModels/Widgets/Bundles/BundlesViewModel.cs index 87177e7fd93e..d4c28bc0bb09 100644 --- a/src/Files.App/ViewModels/Widgets/Bundles/BundlesViewModel.cs +++ b/src/Files.App/ViewModels/Widgets/Bundles/BundlesViewModel.cs @@ -186,7 +186,7 @@ private async Task OpenAddBundleDialog() }, DynamicButtons = DynamicDialogButtons.Primary | DynamicDialogButtons.Cancel }); - await dialog.ShowAsync(); + await dialog.TryShowAsync(); } private void AddBundle(string name) diff --git a/src/Files.App/Views/HomePage.xaml.cs b/src/Files.App/Views/HomePage.xaml.cs index d8d4b70d3f08..693bba249be3 100644 --- a/src/Files.App/Views/HomePage.xaml.cs +++ b/src/Files.App/Views/HomePage.xaml.cs @@ -136,15 +136,6 @@ private void ViewModel_YourHomeLoadedInvoked(object? sender, Microsoft.UI.Xaml.R ReloadWidgets(); } - // WINUI3 - private static ContentDialog SetContentDialogRoot(ContentDialog contentDialog) - { - if (Windows.Foundation.Metadata.ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8)) - contentDialog.XamlRoot = App.Window.Content.XamlRoot; - - return contentDialog; - } - private async void RecentFilesWidget_RecentFileInvoked(object sender, UserControls.PathNavigationEventArgs e) { try @@ -167,7 +158,7 @@ private async void RecentFilesWidget_RecentFileInvoked(object sender, UserContro catch (UnauthorizedAccessException) { DynamicDialog dialog = DynamicDialogFactory.GetFor_ConsentDialog(); - await SetContentDialogRoot(dialog).ShowAsync(); + await dialog.TryShowAsync(); } catch (COMException) { } catch (ArgumentException) { } diff --git a/src/Files.App/Views/MainPage.xaml.cs b/src/Files.App/Views/MainPage.xaml.cs index b590315293a4..b8e9941bdced 100644 --- a/src/Files.App/Views/MainPage.xaml.cs +++ b/src/Files.App/Views/MainPage.xaml.cs @@ -86,7 +86,8 @@ private async Task PromptForReview() SecondaryButtonText = "No".ToLocalized() }; - var result = await SetContentDialogRoot(promptForReviewDialog).ShowAsync(); + var result = await promptForReviewDialog.TryShowAsync(); + if (result == ContentDialogResult.Primary) { try diff --git a/src/Files.App/Views/Properties/DetailsPage.xaml.cs b/src/Files.App/Views/Properties/DetailsPage.xaml.cs index 0ad502bf710e..eae4872ce66f 100644 --- a/src/Files.App/Views/Properties/DetailsPage.xaml.cs +++ b/src/Files.App/Views/Properties/DetailsPage.xaml.cs @@ -29,16 +29,6 @@ protected override void Properties_Loaded(object sender, RoutedEventArgs e) } } - // WINUI3 - private static ContentDialog SetContentDialogRoot(ContentDialog contentDialog) - { - if (Windows.Foundation.Metadata.ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8)) - { - contentDialog.XamlRoot = App.Window.Content.XamlRoot; - } - return contentDialog; - } - public override async Task SaveChangesAsync() { while (true) @@ -54,7 +44,7 @@ public override async Task SaveChangesAsync() } catch { - await SetContentDialogRoot(dialog).TryShowAsync(); + await dialog.TryShowAsync(); switch (dialog.DynamicResult) { case DynamicDialogResult.Primary: diff --git a/src/Files.App/Views/Properties/LibraryPage.xaml.cs b/src/Files.App/Views/Properties/LibraryPage.xaml.cs index 6e506014638b..90fd1d6bbb41 100644 --- a/src/Files.App/Views/Properties/LibraryPage.xaml.cs +++ b/src/Files.App/Views/Properties/LibraryPage.xaml.cs @@ -181,16 +181,6 @@ private bool IsChanged(LibraryItem lib, out string newDefaultSaveFolder, out str return isChanged; } - // WINUI3 - private static ContentDialog SetContentDialogRoot(ContentDialog contentDialog) - { - if (Windows.Foundation.Metadata.ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8)) - { - contentDialog.XamlRoot = App.Window.Content.XamlRoot; - } - return contentDialog; - } - public override async Task SaveChangesAsync() { if (BaseProperties is LibraryProperties props) @@ -219,7 +209,7 @@ public override async Task SaveChangesAsync() } catch { - await SetContentDialogRoot(dialog).TryShowAsync(); + await dialog.TryShowAsync(); switch (dialog.DynamicResult) { case DynamicDialogResult.Primary: diff --git a/src/Files.Backend/Extensions/DialogExtensions.cs b/src/Files.Backend/Extensions/DialogExtensions.cs deleted file mode 100644 index 9d5869ab6eb7..000000000000 --- a/src/Files.Backend/Extensions/DialogExtensions.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Files.Backend.ViewModels.Dialogs; -using Files.Shared.Enums; -using System.ComponentModel; -using System.Threading.Tasks; - -namespace Files.Backend.Extensions -{ - public static class DialogExtensions - { - public static Task TryShowAsync(this IDialog dialog) - where TViewModel : class, INotifyPropertyChanged - { - try - { - return dialog.ShowAsync(); - } - catch - { - // Another dialog is already open - return Task.FromResult(DialogResult.None); - } - } - } -}