From d3d10be753c986f86ef74b03c29ef071e7cdc1b9 Mon Sep 17 00:00:00 2001 From: JT Date: Wed, 11 Oct 2023 23:44:31 -0700 Subject: [PATCH 1/5] Added selectable image button control for Outputs page & beyond --- StabilityMatrix.Avalonia/App.axaml | 1 + .../SelectableImageButton.axaml | 51 ++++++++++++ .../SelectableImageButton.cs | 25 ++++++ .../DesignData/DesignData.cs | 26 ++++++- .../Languages/Resources.Designer.cs | 18 +++++ .../Languages/Resources.resx | 8 +- .../OutputsPage/OutputImageViewModel.cs | 19 +++++ .../ViewModels/OutputsPageViewModel.cs | 74 +++++++++++++----- .../Views/OutputsPage.axaml | 78 ++++++++++++------- 9 files changed, 249 insertions(+), 51 deletions(-) create mode 100644 StabilityMatrix.Avalonia/Controls/SelectableImageCard/SelectableImageButton.axaml create mode 100644 StabilityMatrix.Avalonia/Controls/SelectableImageCard/SelectableImageButton.cs create mode 100644 StabilityMatrix.Avalonia/ViewModels/OutputsPage/OutputImageViewModel.cs diff --git a/StabilityMatrix.Avalonia/App.axaml b/StabilityMatrix.Avalonia/App.axaml index 0258b36b..fb90f374 100644 --- a/StabilityMatrix.Avalonia/App.axaml +++ b/StabilityMatrix.Avalonia/App.axaml @@ -60,6 +60,7 @@ + + + + + + + + + diff --git a/StabilityMatrix.Avalonia/Controls/SelectableImageCard/SelectableImageButton.cs b/StabilityMatrix.Avalonia/Controls/SelectableImageCard/SelectableImageButton.cs new file mode 100644 index 00000000..3a1ca335 --- /dev/null +++ b/StabilityMatrix.Avalonia/Controls/SelectableImageCard/SelectableImageButton.cs @@ -0,0 +1,25 @@ +using Avalonia; +using Avalonia.Controls; + +namespace StabilityMatrix.Avalonia.Controls.SelectableImageCard; + +public class SelectableImageButton : Button +{ + public static readonly StyledProperty IsSelectedProperty = + CheckBox.IsCheckedProperty.AddOwner(); + + public static readonly StyledProperty SourceProperty = + BetterAdvancedImage.SourceProperty.AddOwner(); + + public bool? IsSelected + { + get => GetValue(IsSelectedProperty); + set => SetValue(IsSelectedProperty, value); + } + + public string? Source + { + get => GetValue(SourceProperty); + set => SetValue(SourceProperty, value); + } +} diff --git a/StabilityMatrix.Avalonia/DesignData/DesignData.cs b/StabilityMatrix.Avalonia/DesignData/DesignData.cs index 4b4d4599..068466b3 100644 --- a/StabilityMatrix.Avalonia/DesignData/DesignData.cs +++ b/StabilityMatrix.Avalonia/DesignData/DesignData.cs @@ -7,6 +7,7 @@ using System.IO; using System.Net.Http; using System.Text; using AvaloniaEdit.Utils; +using DynamicData.Binding; using Microsoft.Extensions.DependencyInjection; using StabilityMatrix.Avalonia.Controls; using StabilityMatrix.Avalonia.Controls.CodeCompletion; @@ -20,6 +21,7 @@ using StabilityMatrix.Avalonia.ViewModels.CheckpointManager; using StabilityMatrix.Avalonia.ViewModels.Dialogs; using StabilityMatrix.Avalonia.ViewModels.Progress; using StabilityMatrix.Avalonia.ViewModels.Inference; +using StabilityMatrix.Avalonia.ViewModels.OutputsPage; using StabilityMatrix.Avalonia.ViewModels.Settings; using StabilityMatrix.Core.Api; using StabilityMatrix.Core.Database; @@ -29,6 +31,7 @@ using StabilityMatrix.Core.Helper.Factory; using StabilityMatrix.Core.Models; using StabilityMatrix.Core.Models.Api; using StabilityMatrix.Core.Models.Api.Comfy; +using StabilityMatrix.Core.Models.Database; using StabilityMatrix.Core.Models.PackageModification; using StabilityMatrix.Core.Models.Progress; using StabilityMatrix.Core.Python; @@ -336,8 +339,27 @@ public static class DesignData public static LaunchPageViewModel LaunchPageViewModel => Services.GetRequiredService(); - public static OutputsPageViewModel OutputsPageViewModel => - Services.GetRequiredService(); + public static OutputsPageViewModel OutputsPageViewModel + { + get + { + var vm = Services.GetRequiredService(); + vm.Outputs = new ObservableCollectionExtended + { + new( + new LocalImageFile + { + AbsolutePath = + "https://image.civitai.com/xG1nkqKTMzGDvpLrqFT7WA/78fd2a0a-42b6-42b0-9815-81cb11bb3d05/00009-2423234823.jpeg", + RelativePath = + "https://image.civitai.com/xG1nkqKTMzGDvpLrqFT7WA/78fd2a0a-42b6-42b0-9815-81cb11bb3d05/00009-2423234823.jpeg", + ImageType = LocalImageFileType.TextToImage + } + ) + }; + return vm; + } + } public static PackageManagerViewModel PackageManagerViewModel { diff --git a/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs b/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs index ab1c7e63..336977f1 100644 --- a/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs +++ b/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs @@ -257,6 +257,15 @@ namespace StabilityMatrix.Avalonia.Languages { } } + /// + /// Looks up a localized string similar to Open in Image Viewer. + /// + public static string Action_OpenInViewer { + get { + return ResourceManager.GetString("Action_OpenInViewer", resourceCulture); + } + } + /// /// Looks up a localized string similar to Open on CivitAI. /// @@ -1157,6 +1166,15 @@ namespace StabilityMatrix.Avalonia.Languages { } } + /// + /// Looks up a localized string similar to {0} images selected. + /// + public static string Label_NumImagesSelected { + get { + return ResourceManager.GetString("Label_NumImagesSelected", resourceCulture); + } + } + /// /// Looks up a localized string similar to Only available on Windows. /// diff --git a/StabilityMatrix.Avalonia/Languages/Resources.resx b/StabilityMatrix.Avalonia/Languages/Resources.resx index a0fd01c3..05d6781e 100644 --- a/StabilityMatrix.Avalonia/Languages/Resources.resx +++ b/StabilityMatrix.Avalonia/Languages/Resources.resx @@ -687,4 +687,10 @@ Copy - \ No newline at end of file + + Open in Image Viewer + + + {0} images selected + + diff --git a/StabilityMatrix.Avalonia/ViewModels/OutputsPage/OutputImageViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/OutputsPage/OutputImageViewModel.cs new file mode 100644 index 00000000..1102d0ab --- /dev/null +++ b/StabilityMatrix.Avalonia/ViewModels/OutputsPage/OutputImageViewModel.cs @@ -0,0 +1,19 @@ +using System; +using CommunityToolkit.Mvvm.ComponentModel; +using StabilityMatrix.Avalonia.ViewModels.Base; +using StabilityMatrix.Core.Models.Database; + +namespace StabilityMatrix.Avalonia.ViewModels.OutputsPage; + +public partial class OutputImageViewModel : ViewModelBase +{ + public LocalImageFile ImageFile { get; } + + [ObservableProperty] + private bool isSelected; + + public OutputImageViewModel(LocalImageFile imageFile) + { + ImageFile = imageFile; + } +} diff --git a/StabilityMatrix.Avalonia/ViewModels/OutputsPageViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/OutputsPageViewModel.cs index 209fd099..4e07140d 100644 --- a/StabilityMatrix.Avalonia/ViewModels/OutputsPageViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/OutputsPageViewModel.cs @@ -4,21 +4,24 @@ using System.Collections.ObjectModel; using System.IO; using System.Linq; using System.Reactive.Linq; +using System.Threading; using System.Threading.Tasks; using AsyncAwaitBestPractices; using AsyncImageLoader; +using Avalonia; using Avalonia.Controls; using Avalonia.Threading; using CommunityToolkit.Mvvm.ComponentModel; using DynamicData; using DynamicData.Binding; using FluentAvalonia.UI.Controls; +using Microsoft.Extensions.Logging; using StabilityMatrix.Avalonia.Extensions; using StabilityMatrix.Avalonia.Models; using StabilityMatrix.Avalonia.Services; using StabilityMatrix.Avalonia.ViewModels.Base; using StabilityMatrix.Avalonia.ViewModels.Dialogs; -using StabilityMatrix.Avalonia.Views; +using StabilityMatrix.Avalonia.ViewModels.OutputsPage; using StabilityMatrix.Core.Attributes; using StabilityMatrix.Core.Helper; using StabilityMatrix.Core.Helper.Factory; @@ -32,21 +35,23 @@ using SymbolIconSource = FluentIcons.FluentAvalonia.SymbolIconSource; namespace StabilityMatrix.Avalonia.ViewModels; -[View(typeof(OutputsPage))] +[View(typeof(Views.OutputsPage))] public partial class OutputsPageViewModel : PageViewModelBase { private readonly ISettingsManager settingsManager; private readonly INotificationService notificationService; private readonly INavigationService navigationService; + private readonly ILogger logger; public override string Title => "Outputs"; public override IconSource IconSource => new SymbolIconSource { Symbol = Symbol.Grid, IsFilled = true }; - public SourceCache OutputsCache { get; } = new(p => p.AbsolutePath); + public SourceCache OutputsCache { get; } = + new(p => p.ImageFile.AbsolutePath); - public IObservableCollection Outputs { get; } = - new ObservableCollectionExtended(); + public IObservableCollection Outputs { get; set; } = + new ObservableCollectionExtended(); public IEnumerable OutputTypes { get; } = Enum.GetValues(); @@ -60,25 +65,35 @@ public partial class OutputsPageViewModel : PageViewModelBase [ObservableProperty] private SharedOutputType selectedOutputType; + [ObservableProperty] + private int numItemsSelected; + public bool CanShowOutputTypes => SelectedCategory.Name.Equals("Shared Output Folder"); public OutputsPageViewModel( ISettingsManager settingsManager, IPackageFactory packageFactory, INotificationService notificationService, - INavigationService navigationService + INavigationService navigationService, + ILogger logger ) { this.settingsManager = settingsManager; this.notificationService = notificationService; this.navigationService = navigationService; + this.logger = logger; OutputsCache .Connect() .DeferUntilLoaded() - .SortBy(x => x.CreatedAt, SortDirection.Descending) + .SortBy(x => x.ImageFile.CreatedAt, SortDirection.Descending) + .ObserveOn(SynchronizationContext.Current) .Bind(Outputs) - .Subscribe(); + .WhenPropertyChanged(p => p.IsSelected) + .Subscribe(_ => + { + NumItemsSelected = Outputs.Count(o => o.IsSelected); + }); if (!settingsManager.IsLibraryDirSet || Design.IsDesignMode) return; @@ -152,16 +167,29 @@ public partial class OutputsPageViewModel : PageViewModelBase GetOutputs(path); } - public async Task OnImageClick(LocalImageFile item) + public async Task OnImageClick(OutputImageViewModel item) + { + // Select image if we're in "select mode" + if (NumItemsSelected > 0) + { + item.IsSelected = !item.IsSelected; + } + else + { + await ShowImageDialog(item); + } + } + + public async Task ShowImageDialog(OutputImageViewModel item) { var currentIndex = Outputs.IndexOf(item); - var image = new ImageSource(new FilePath(item.AbsolutePath)); + var image = new ImageSource(new FilePath(item.ImageFile.AbsolutePath)); // Preload await image.GetBitmapAsync(); - var vm = new ImageViewerViewModel { ImageSource = image, LocalImageFile = item }; + var vm = new ImageViewerViewModel { ImageSource = image, LocalImageFile = item.ImageFile }; using var onNext = Observable .FromEventPattern( @@ -180,14 +208,14 @@ public partial class OutputsPageViewModel : PageViewModelBase { var newImage = Outputs[newIndex]; var newImageSource = new ImageSource( - new FilePath(newImage.AbsolutePath) + new FilePath(newImage.ImageFile.AbsolutePath) ); // Preload await newImageSource.GetBitmapAsync(); sender.ImageSource = newImageSource; - sender.LocalImageFile = newImage; + sender.LocalImageFile = newImage.ImageFile; currentIndex = newIndex; } @@ -207,9 +235,9 @@ public partial class OutputsPageViewModel : PageViewModelBase public async Task OpenImage(string imagePath) => await ProcessRunner.OpenFileBrowser(imagePath); - public async Task DeleteImage(LocalImageFile? item) + public async Task DeleteImage(OutputImageViewModel? item) { - if (item?.GetFullPath(settingsManager.ImagesDirectory) is not { } imagePath) + if (item?.ImageFile.GetFullPath(settingsManager.ImagesDirectory) is not { } imagePath) { return; } @@ -223,7 +251,7 @@ public partial class OutputsPageViewModel : PageViewModelBase return; } - Outputs.Remove(item); + OutputsCache.Remove(item); // Invalidate cache if (ImageLoader.AsyncImageLoader is FallbackRamCachedWebImageLoader loader) @@ -244,6 +272,14 @@ public partial class OutputsPageViewModel : PageViewModelBase EventManager.Instance.OnInferenceUpscaleRequested(image); } + public void ClearSelection() + { + foreach (var output in Outputs) + { + output.IsSelected = false; + } + } + private void GetOutputs(string directory) { if (!settingsManager.IsLibraryDirSet) @@ -257,9 +293,9 @@ public partial class OutputsPageViewModel : PageViewModelBase var list = Directory .EnumerateFiles(directory, "*.png", SearchOption.AllDirectories) - .Select(file => LocalImageFile.FromPath(file)) - .OrderByDescending(f => f.CreatedAt); + .Select(file => new OutputImageViewModel(LocalImageFile.FromPath(file))) + .OrderByDescending(f => f.ImageFile.CreatedAt); - OutputsCache.EditDiff(list, (x, y) => x.AbsolutePath == y.AbsolutePath); + OutputsCache.EditDiff(list, (x, y) => x.ImageFile.AbsolutePath == y.ImageFile.AbsolutePath); } } diff --git a/StabilityMatrix.Avalonia/Views/OutputsPage.axaml b/StabilityMatrix.Avalonia/Views/OutputsPage.axaml index 1c200b5d..32a86148 100644 --- a/StabilityMatrix.Avalonia/Views/OutputsPage.axaml +++ b/StabilityMatrix.Avalonia/Views/OutputsPage.axaml @@ -13,7 +13,8 @@ xmlns:models1="clr-namespace:StabilityMatrix.Avalonia.Models" xmlns:database="clr-namespace:StabilityMatrix.Core.Models.Database;assembly=StabilityMatrix.Core" xmlns:fluentAvalonia="clr-namespace:FluentIcons.FluentAvalonia;assembly=FluentIcons.FluentAvalonia" - xmlns:models="clr-namespace:StabilityMatrix.Core.Models;assembly=StabilityMatrix.Core" + xmlns:outputsPage="clr-namespace:StabilityMatrix.Avalonia.ViewModels.OutputsPage" + xmlns:selectableImageCard="clr-namespace:StabilityMatrix.Avalonia.Controls.SelectableImageCard" d:DataContext="{x:Static mocks:DesignData.OutputsPageViewModel}" d:DesignHeight="450" d:DesignWidth="700" @@ -24,8 +25,9 @@ - + @@ -48,13 +50,31 @@ - + + VerticalContentAlignment="Center" /> + + + + + + + + + From 54c3c409457b613eab624e458309f48605279ade Mon Sep 17 00:00:00 2001 From: JT Date: Fri, 13 Oct 2023 23:19:30 -0700 Subject: [PATCH 2/5] Added select all / clear selection / delete buttons for output viewer & made checkboxes bigger --- .../Languages/Resources.Designer.cs | 99 +++++++++++++ .../Languages/Resources.resx | 33 +++++ .../ViewModels/OutputsPageViewModel.cs | 77 +++++++++- .../Views/OutputsPage.axaml | 134 +++++++++++------- .../Views/PackageManagerPage.axaml | 33 ++++- 5 files changed, 322 insertions(+), 54 deletions(-) diff --git a/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs b/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs index 336977f1..caaba671 100644 --- a/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs +++ b/StabilityMatrix.Avalonia/Languages/Resources.Designer.cs @@ -113,6 +113,15 @@ namespace StabilityMatrix.Avalonia.Languages { } } + /// + /// Looks up a localized string similar to Clear Selection. + /// + public static string Action_ClearSelection { + get { + return ResourceManager.GetString("Action_ClearSelection", resourceCulture); + } + } + /// /// Looks up a localized string similar to Close. /// @@ -401,6 +410,15 @@ namespace StabilityMatrix.Avalonia.Languages { } } + /// + /// Looks up a localized string similar to Select All. + /// + public static string Action_SelectAll { + get { + return ResourceManager.GetString("Action_SelectAll", resourceCulture); + } + } + /// /// Looks up a localized string similar to Select Directory. /// @@ -428,6 +446,15 @@ namespace StabilityMatrix.Avalonia.Languages { } } + /// + /// Looks up a localized string similar to Send to Inference. + /// + public static string Action_SendToInference { + get { + return ResourceManager.GetString("Action_SendToInference", resourceCulture); + } + } + /// /// Looks up a localized string similar to Show in Explorer. /// @@ -905,6 +932,15 @@ namespace StabilityMatrix.Avalonia.Languages { } } + /// + /// Looks up a localized string similar to Image to Image. + /// + public static string Label_ImageToImage { + get { + return ResourceManager.GetString("Label_ImageToImage", resourceCulture); + } + } + /// /// Looks up a localized string similar to Import as Connected. /// @@ -950,6 +986,15 @@ namespace StabilityMatrix.Avalonia.Languages { } } + /// + /// Looks up a localized string similar to Inpainting. + /// + public static string Label_Inpainting { + get { + return ResourceManager.GetString("Label_Inpainting", resourceCulture); + } + } + /// /// Looks up a localized string similar to Input. /// @@ -1175,6 +1220,15 @@ namespace StabilityMatrix.Avalonia.Languages { } } + /// + /// Looks up a localized string similar to 1 image selected. + /// + public static string Label_OneImageSelected { + get { + return ResourceManager.GetString("Label_OneImageSelected", resourceCulture); + } + } + /// /// Looks up a localized string similar to Only available on Windows. /// @@ -1184,6 +1238,33 @@ namespace StabilityMatrix.Avalonia.Languages { } } + /// + /// Looks up a localized string similar to Output Folder. + /// + public static string Label_OutputFolder { + get { + return ResourceManager.GetString("Label_OutputFolder", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Output Browser. + /// + public static string Label_OutputsPageTitle { + get { + return ResourceManager.GetString("Label_OutputsPageTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Output Type. + /// + public static string Label_OutputType { + get { + return ResourceManager.GetString("Label_OutputType", resourceCulture); + } + } + /// /// Looks up a localized string similar to Package Environment. /// @@ -1508,6 +1589,15 @@ namespace StabilityMatrix.Avalonia.Languages { } } + /// + /// Looks up a localized string similar to Text to Image. + /// + public static string Label_TextToImage { + get { + return ResourceManager.GetString("Label_TextToImage", resourceCulture); + } + } + /// /// Looks up a localized string similar to Theme. /// @@ -1553,6 +1643,15 @@ namespace StabilityMatrix.Avalonia.Languages { } } + /// + /// Looks up a localized string similar to Upscale. + /// + public static string Label_Upscale { + get { + return ResourceManager.GetString("Label_Upscale", resourceCulture); + } + } + /// /// Looks up a localized string similar to Output Sharing. /// diff --git a/StabilityMatrix.Avalonia/Languages/Resources.resx b/StabilityMatrix.Avalonia/Languages/Resources.resx index 05d6781e..aa3f91db 100644 --- a/StabilityMatrix.Avalonia/Languages/Resources.resx +++ b/StabilityMatrix.Avalonia/Languages/Resources.resx @@ -693,4 +693,37 @@ {0} images selected + + Output Folder + + + Output Type + + + Clear Selection + + + Select All + + + Send to Inference + + + Text to Image + + + Image to Image + + + Inpainting + + + Upscale + + + Output Browser + + + 1 image selected + diff --git a/StabilityMatrix.Avalonia/ViewModels/OutputsPageViewModel.cs b/StabilityMatrix.Avalonia/ViewModels/OutputsPageViewModel.cs index 4e07140d..cf98a914 100644 --- a/StabilityMatrix.Avalonia/ViewModels/OutputsPageViewModel.cs +++ b/StabilityMatrix.Avalonia/ViewModels/OutputsPageViewModel.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Diagnostics; using System.IO; using System.Linq; using System.Reactive.Linq; @@ -16,7 +17,9 @@ using DynamicData; using DynamicData.Binding; using FluentAvalonia.UI.Controls; using Microsoft.Extensions.Logging; +using StabilityMatrix.Avalonia.Controls; using StabilityMatrix.Avalonia.Extensions; +using StabilityMatrix.Avalonia.Languages; using StabilityMatrix.Avalonia.Models; using StabilityMatrix.Avalonia.Services; using StabilityMatrix.Avalonia.ViewModels.Base; @@ -42,7 +45,7 @@ public partial class OutputsPageViewModel : PageViewModelBase private readonly INotificationService notificationService; private readonly INavigationService navigationService; private readonly ILogger logger; - public override string Title => "Outputs"; + public override string Title => Resources.Label_OutputsPageTitle; public override IconSource IconSource => new SymbolIconSource { Symbol = Symbol.Grid, IsFilled = true }; @@ -66,10 +69,16 @@ public partial class OutputsPageViewModel : PageViewModelBase private SharedOutputType selectedOutputType; [ObservableProperty] + [NotifyPropertyChangedFor(nameof(NumImagesSelected))] private int numItemsSelected; public bool CanShowOutputTypes => SelectedCategory.Name.Equals("Shared Output Folder"); + public string NumImagesSelected => + NumItemsSelected == 1 + ? Resources.Label_OneImageSelected + : string.Format(Resources.Label_NumImagesSelected, NumItemsSelected); + public OutputsPageViewModel( ISettingsManager settingsManager, IPackageFactory packageFactory, @@ -237,6 +246,19 @@ public partial class OutputsPageViewModel : PageViewModelBase public async Task DeleteImage(OutputImageViewModel? item) { + var confirmationDialog = new BetterContentDialog + { + Title = "Are you sure you want to delete this image?", + Content = "This action cannot be undone.", + PrimaryButtonText = Resources.Action_Delete, + SecondaryButtonText = Resources.Action_Cancel, + DefaultButton = ContentDialogButton.Primary, + IsSecondaryButtonEnabled = true, + }; + var dialogResult = await confirmationDialog.ShowAsync(); + if (dialogResult != ContentDialogResult.Primary) + return; + if (item?.ImageFile.GetFullPath(settingsManager.ImagesDirectory) is not { } imagePath) { return; @@ -280,6 +302,59 @@ public partial class OutputsPageViewModel : PageViewModelBase } } + public void SelectAll() + { + foreach (var output in Outputs) + { + output.IsSelected = true; + } + } + + public async Task DeleteAllSelected() + { + var confirmationDialog = new BetterContentDialog + { + Title = $"Are you sure you want to delete {NumItemsSelected} images?", + Content = "This action cannot be undone.", + PrimaryButtonText = Resources.Action_Delete, + SecondaryButtonText = Resources.Action_Cancel, + DefaultButton = ContentDialogButton.Primary, + IsSecondaryButtonEnabled = true, + }; + var dialogResult = await confirmationDialog.ShowAsync(); + if (dialogResult != ContentDialogResult.Primary) + return; + + var selected = Outputs.Where(o => o.IsSelected).ToList(); + Debug.Assert(selected.Count == NumItemsSelected); + foreach (var output in selected) + { + if (output?.ImageFile.GetFullPath(settingsManager.ImagesDirectory) is not { } imagePath) + { + continue; + } + + // Delete the file + var imageFile = new FilePath(imagePath); + var result = await notificationService.TryAsync(imageFile.DeleteAsync()); + + if (!result.IsSuccessful) + { + continue; + } + OutputsCache.Remove(output); + + // Invalidate cache + if (ImageLoader.AsyncImageLoader is FallbackRamCachedWebImageLoader loader) + { + loader.RemoveAllNamesFromCache(imageFile.Name); + } + } + + NumItemsSelected = 0; + ClearSelection(); + } + private void GetOutputs(string directory) { if (!settingsManager.IsLibraryDirSet) diff --git a/StabilityMatrix.Avalonia/Views/OutputsPage.axaml b/StabilityMatrix.Avalonia/Views/OutputsPage.axaml index 32a86148..8fdcac4f 100644 --- a/StabilityMatrix.Avalonia/Views/OutputsPage.axaml +++ b/StabilityMatrix.Avalonia/Views/OutputsPage.axaml @@ -11,10 +11,10 @@ xmlns:vm="clr-namespace:StabilityMatrix.Avalonia.ViewModels" xmlns:lang="clr-namespace:StabilityMatrix.Avalonia.Languages" xmlns:models1="clr-namespace:StabilityMatrix.Avalonia.Models" - xmlns:database="clr-namespace:StabilityMatrix.Core.Models.Database;assembly=StabilityMatrix.Core" xmlns:fluentAvalonia="clr-namespace:FluentIcons.FluentAvalonia;assembly=FluentIcons.FluentAvalonia" xmlns:outputsPage="clr-namespace:StabilityMatrix.Avalonia.ViewModels.OutputsPage" xmlns:selectableImageCard="clr-namespace:StabilityMatrix.Avalonia.Controls.SelectableImageCard" + xmlns:avalonia="clr-namespace:Projektanker.Icons.Avalonia;assembly=Projektanker.Icons.Avalonia" d:DataContext="{x:Static mocks:DesignData.OutputsPageViewModel}" d:DesignHeight="450" d:DesignWidth="700" @@ -22,60 +22,95 @@ x:DataType="vm:OutputsPageViewModel" mc:Ignorable="d"> - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - - - + TextAlignment="Center" + HorizontalAlignment="Right" + Text="{Binding NumImagesSelected, FallbackValue=1234 images selected}" /> - + +