Browse Source

Image viewer fixes

pull/165/head
Ionite 1 year ago
parent
commit
586b0029a5
No known key found for this signature in database
  1. 1
      StabilityMatrix.Avalonia/Controls/FrameCarousel.axaml
  2. 52
      StabilityMatrix.Avalonia/Controls/FrameCarousel.axaml.cs
  3. 41
      StabilityMatrix.Avalonia/Controls/ImageGalleryCard.axaml
  4. 30
      StabilityMatrix.Avalonia/ViewModels/Inference/InferenceTextToImageViewModel.cs

1
StabilityMatrix.Avalonia/Controls/FrameCarousel.axaml

@ -16,6 +16,7 @@
CornerRadius="{TemplateBinding CornerRadius}"> CornerRadius="{TemplateBinding CornerRadius}">
<ui:Frame <ui:Frame
Name="PART_Frame" Name="PART_Frame"
CornerRadius="{TemplateBinding CornerRadius}"
ContentTemplate="{TemplateBinding ContentTemplate}"/> ContentTemplate="{TemplateBinding ContentTemplate}"/>
</Border> </Border>
</ControlTemplate> </ControlTemplate>

52
StabilityMatrix.Avalonia/Controls/FrameCarousel.axaml.cs

@ -1,14 +1,10 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates; using Avalonia.Controls.Templates;
using Avalonia.Interactivity;
using DynamicData.Binding;
using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Controls;
using FluentAvalonia.UI.Media.Animation; using FluentAvalonia.UI.Media.Animation;
using FluentAvalonia.UI.Navigation; using FluentAvalonia.UI.Navigation;
@ -67,24 +63,6 @@ public class FrameCarousel : SelectingItemsControl
TransitionInfoOverride = new SuppressNavigationTransitionInfo() TransitionInfoOverride = new SuppressNavigationTransitionInfo()
}; };
private void ItemsView_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
if (frame is null) return;
// If current size is 0, reset the frame cache
var count = ItemCount;
if (count == 0)
{
frame.ClearValue(Frame.CacheSizeProperty);
// Setting this to false clears the page cache and stack caches
frame.IsNavigationStackEnabled = false;
}
else
{
frame.SetValue(Frame.CacheSizeProperty, count);
}
}
/// <inheritdoc /> /// <inheritdoc />
protected override void OnApplyTemplate(TemplateAppliedEventArgs e) protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{ {
@ -95,32 +73,21 @@ public class FrameCarousel : SelectingItemsControl
frame.NavigationPageFactory = new FrameNavigationFactory(SourcePageType); frame.NavigationPageFactory = new FrameNavigationFactory(SourcePageType);
ItemsView.CollectionChanged += ItemsView_CollectionChanged;
if (SelectedItem is not null) if (SelectedItem is not null)
{ {
frame.NavigateFromObject(SelectedItem, DirectionlessNavigationOptions); frame.NavigateFromObject(SelectedItem, DirectionlessNavigationOptions);
} }
} }
/// <inheritdoc />
protected override void OnLoaded(RoutedEventArgs e)
{
base.OnLoaded(e);
if (SelectedItem is not null)
{
frame!.NavigateFromObject(SelectedItem, DirectionlessNavigationOptions);
}
}
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{ {
base.OnPropertyChanged(change); base.OnPropertyChanged(change);
if (change.Property == SelectedItemProperty && frame is not null) if (frame is null) return;
if (change.Property == SelectedItemProperty)
{ {
var value = change.GetNewValue<object?>(); if (change.GetNewValue<object?>() is not { } value) return;
if (SelectedIndex > previousIndex) if (SelectedIndex > previousIndex)
{ {
@ -139,6 +106,17 @@ public class FrameCarousel : SelectingItemsControl
previousIndex = SelectedIndex; previousIndex = SelectedIndex;
} }
else if (change.Property == ItemCountProperty)
{
// On item count change to 0, clear the frame cache
var value = change.GetNewValue<int>();
if (value == 0)
{
var pageCache = frame.GetPrivateField<IList>("_pageCache");
pageCache?.Clear();
}
}
} }
/// <summary> /// <summary>

41
StabilityMatrix.Avalonia/Controls/ImageGalleryCard.axaml

@ -3,8 +3,6 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:StabilityMatrix.Avalonia.Controls" xmlns:controls="using:StabilityMatrix.Avalonia.Controls"
xmlns:mocks="clr-namespace:StabilityMatrix.Avalonia.DesignData" xmlns:mocks="clr-namespace:StabilityMatrix.Avalonia.DesignData"
xmlns:system="clr-namespace:System;assembly=System.Runtime"
xmlns:ui="using:FluentAvalonia.UI.Controls"
xmlns:vmInference="clr-namespace:StabilityMatrix.Avalonia.ViewModels.Inference" xmlns:vmInference="clr-namespace:StabilityMatrix.Avalonia.ViewModels.Inference"
xmlns:models="clr-namespace:StabilityMatrix.Avalonia.Models" xmlns:models="clr-namespace:StabilityMatrix.Avalonia.Models"
x:DataType="vmInference:ImageGalleryCardViewModel"> x:DataType="vmInference:ImageGalleryCardViewModel">
@ -40,40 +38,13 @@
<!-- Main image view --> <!-- Main image view -->
<Grid Margin="4"> <Grid Margin="4">
<controls:FrameCarousel <controls:FrameCarousel
x:Name="ImageCarousel" x:Name="PART_ImageCarousel"
CornerRadius="4"
IsVisible="{Binding !IsPreviewOverlayEnabled}"
SourcePageType="controls:AdvancedImageBoxView" SourcePageType="controls:AdvancedImageBoxView"
SelectedItem="{Binding SelectedImage}" SelectedItem="{Binding SelectedImage}"
SelectedIndex="{Binding SelectedImageIndex}" SelectedIndex="{Binding SelectedImageIndex}"
ItemsSource="{Binding ImageSources}"> ItemsSource="{Binding ImageSources}" />
<!--<controls:FrameCarousel.ContentTemplate>
<DataTemplate DataType="{x:Type models:ImageSource}">
<controls:AdvancedImageBox
x:Name="ImageView"
CornerRadius="4"
SizeMode="Fit"
Image="{Binding BitmapAsync^}">
<controls:AdvancedImageBox.ContextFlyout>
<ui:FAMenuFlyout>
~2~<ui:MenuFlyoutItem
Command="{ReflectionBinding #ImageCarousel.DataContext.FlyoutPreviewCommand}"
CommandParameter="{Binding #ImageView.Image}"
Text="Expanded Preview"
HotKey="Space"
IconSource="ZoomInFilled" />
<ui:MenuFlyoutSeparator/>@2@
<ui:MenuFlyoutItem
IsEnabled="{OnPlatform Windows=True, Default=False}"
Command="{ReflectionBinding #ImageCarousel.DataContext.FlyoutCopyCommand}"
CommandParameter="{Binding #ImageView.Image}"
Text="Copy"
HotKey="Ctrl+C"
IconSource="Copy" />
</ui:FAMenuFlyout>
</controls:AdvancedImageBox.ContextFlyout>
</controls:AdvancedImageBox>
</DataTemplate>
</controls:FrameCarousel.ContentTemplate>-->
</controls:FrameCarousel>
<!--<Carousel <!--<Carousel
ScrollViewer.IsScrollChainingEnabled="False" ScrollViewer.IsScrollChainingEnabled="False"
@ -152,7 +123,7 @@
IsEnabled="{Binding CanNavigateBack}" IsEnabled="{Binding CanNavigateBack}"
Classes="transparent" Classes="transparent"
Padding="10,20" Padding="10,20"
Command="{Binding #ImageCarousel.Previous}"> Command="{Binding #PART_ImageCarousel.Previous}">
<Path Data="M20,11V13H8L13.5,18.5L12.08,19.92L4.16,12L12.08,4.08L13.5,5.5L8,11H20Z" <Path Data="M20,11V13H8L13.5,18.5L12.08,19.92L4.16,12L12.08,4.08L13.5,5.5L8,11H20Z"
Fill="{DynamicResource ButtonForeground}" /> Fill="{DynamicResource ButtonForeground}" />
</Button> </Button>
@ -167,7 +138,7 @@
IsEnabled="{Binding CanNavigateForward}" IsEnabled="{Binding CanNavigateForward}"
Classes="transparent" Classes="transparent"
Padding="10,20" Padding="10,20"
Command="{Binding #ImageCarousel.Next}"> Command="{Binding #PART_ImageCarousel.Next}">
<Path Data="M4,11V13H16L10.5,18.5L11.92,19.92L19.84,12L11.92,4.08L10.5,5.5L16,11H4Z" <Path Data="M4,11V13H16L10.5,18.5L11.92,19.92L19.84,12L11.92,4.08L10.5,5.5L16,11H4Z"
Fill="{DynamicResource ButtonForeground}" /> Fill="{DynamicResource ButtonForeground}" />
</Button> </Button>

30
StabilityMatrix.Avalonia/ViewModels/Inference/InferenceTextToImageViewModel.cs

@ -1,23 +1,18 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using AsyncAwaitBestPractices; using AsyncAwaitBestPractices;
using Avalonia.Media.Imaging; using Avalonia.Media.Imaging;
using AvaloniaEdit.Document;
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Input;
using NLog; using NLog;
using Refit; using Refit;
using SkiaSharp; using SkiaSharp;
using StabilityMatrix.Avalonia.Controls;
using StabilityMatrix.Avalonia.Extensions;
using StabilityMatrix.Avalonia.Helpers; using StabilityMatrix.Avalonia.Helpers;
using StabilityMatrix.Avalonia.Models; using StabilityMatrix.Avalonia.Models;
using StabilityMatrix.Avalonia.Services; using StabilityMatrix.Avalonia.Services;
@ -30,6 +25,7 @@ using StabilityMatrix.Core.Models.Api.Comfy;
using StabilityMatrix.Core.Models.Api.Comfy.Nodes; using StabilityMatrix.Core.Models.Api.Comfy.Nodes;
using StabilityMatrix.Core.Models.Api.Comfy.NodeTypes; using StabilityMatrix.Core.Models.Api.Comfy.NodeTypes;
using StabilityMatrix.Core.Models.Api.Comfy.WebSocketData; using StabilityMatrix.Core.Models.Api.Comfy.WebSocketData;
#pragma warning disable CS0657 // Not a valid attribute location for this declaration #pragma warning disable CS0657 // Not a valid attribute location for this declaration
namespace StabilityMatrix.Avalonia.ViewModels.Inference; namespace StabilityMatrix.Avalonia.ViewModels.Inference;
@ -129,7 +125,7 @@ public partial class InferenceTextToImageViewModel : InferenceTabViewModelBase
} }
); );
GenerateImageCommand.WithNotificationErrorHandler(notificationService); // GenerateImageCommand.WithNotificationErrorHandler(notificationService);
} }
private (NodeDictionary prompt, string[] outputs) BuildPrompt() private (NodeDictionary prompt, string[] outputs) BuildPrompt()
@ -364,6 +360,7 @@ public partial class InferenceTextToImageViewModel : InferenceTabViewModelBase
// client.ProgressUpdateReceived += OnProgressUpdateReceived; // client.ProgressUpdateReceived += OnProgressUpdateReceived;
client.PreviewImageReceived += OnPreviewImageReceived; client.PreviewImageReceived += OnPreviewImageReceived;
ComfyTask? promptTask = null; ComfyTask? promptTask = null;
try try
{ {
@ -434,18 +431,25 @@ public partial class InferenceTextToImageViewModel : InferenceTabViewModelBase
var lastName = outputImages.Last().LocalFile?.Info.Name; var lastName = outputImages.Last().LocalFile?.Info.Name;
var gridPath = client.OutputImagesDir!.JoinFile($"grid-{lastName}"); var gridPath = client.OutputImagesDir!.JoinFile($"grid-{lastName}");
await using var fileStream = gridPath.Info.OpenWrite(); await using (var fileStream = gridPath.Info.OpenWrite())
{
await fileStream.WriteAsync(grid.Encode().ToArray(), cancellationToken); await fileStream.WriteAsync(grid.Encode().ToArray(), cancellationToken);
}
// Insert to start of images // Insert to start of images
ImageGalleryCardViewModel.ImageSources.Add(new ImageSource(gridPath)); var gridImage = new ImageSource(gridPath);
// Preload
await gridImage.GetBitmapAsync();
ImageGalleryCardViewModel.ImageSources.Add(gridImage);
} }
// Add rest of images // Add rest of images
ImageGalleryCardViewModel.ImageSources.AddRange(outputImages); foreach (var img in outputImages)
{
// Preload all images // Preload
await Task.WhenAll(outputImages.Select(i => i.BitmapAsync)); await img.GetBitmapAsync();
ImageGalleryCardViewModel.ImageSources.Add(img);
}
} }
finally finally
{ {
@ -462,7 +466,7 @@ public partial class InferenceTextToImageViewModel : InferenceTabViewModelBase
} }
} }
[RelayCommand(IncludeCancelCommand = true, FlowExceptionsToTaskScheduler = true)] [RelayCommand(IncludeCancelCommand = true)]
private async Task GenerateImage(CancellationToken cancellationToken = default) private async Task GenerateImage(CancellationToken cancellationToken = default)
{ {
try try

Loading…
Cancel
Save