Browse Source

Add SelectImageCard state loading, improve card visuals

pull/324/head
Ionite 12 months ago
parent
commit
6d2a17a408
No known key found for this signature in database
  1. 127
      StabilityMatrix.Avalonia/Controls/ControlNetCard.axaml
  2. 61
      StabilityMatrix.Avalonia/Controls/SelectImageCard.axaml
  3. 2
      StabilityMatrix.Avalonia/Controls/StackCard.axaml
  4. 8
      StabilityMatrix.Avalonia/Controls/StackCard.axaml.cs
  5. 8
      StabilityMatrix.Avalonia/Controls/StackEditableCard.axaml
  6. 7
      StabilityMatrix.Avalonia/Controls/StackExpander.axaml
  7. 27
      StabilityMatrix.Avalonia/Languages/Resources.Designer.cs
  8. 9
      StabilityMatrix.Avalonia/Languages/Resources.resx
  9. 20
      StabilityMatrix.Avalonia/Models/ImageSource.cs
  10. 3
      StabilityMatrix.Avalonia/Styles/ListBoxStyles.axaml
  11. 34
      StabilityMatrix.Avalonia/ViewModels/Inference/SelectImageCardViewModel.cs

127
StabilityMatrix.Avalonia/Controls/ControlNetCard.axaml

@ -2,91 +2,89 @@
xmlns="https://github.com/avaloniaui" xmlns="https://github.com/avaloniaui"
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:extensions="clr-namespace:StabilityMatrix.Avalonia.Controls.Extensions"
xmlns:fluentIcons="clr-namespace:FluentIcons.FluentAvalonia;assembly=FluentIcons.FluentAvalonia"
xmlns:input="clr-namespace:FluentAvalonia.UI.Input;assembly=FluentAvalonia"
xmlns:lang="clr-namespace:StabilityMatrix.Avalonia.Languages" xmlns:lang="clr-namespace:StabilityMatrix.Avalonia.Languages"
xmlns:mocks="clr-namespace:StabilityMatrix.Avalonia.DesignData" xmlns:mocks="clr-namespace:StabilityMatrix.Avalonia.DesignData"
xmlns:models="clr-namespace:StabilityMatrix.Core.Models;assembly=StabilityMatrix.Core" xmlns:models="clr-namespace:StabilityMatrix.Core.Models;assembly=StabilityMatrix.Core"
xmlns:sg="clr-namespace:SpacedGridControl.Avalonia;assembly=SpacedGridControl.Avalonia" xmlns:sg="clr-namespace:SpacedGridControl.Avalonia;assembly=SpacedGridControl.Avalonia"
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:vmInference="clr-namespace:StabilityMatrix.Avalonia.ViewModels.Inference" xmlns:vmInference="clr-namespace:StabilityMatrix.Avalonia.ViewModels.Inference"
xmlns:input="clr-namespace:FluentAvalonia.UI.Input;assembly=FluentAvalonia"
xmlns:fluentIcons="clr-namespace:FluentIcons.FluentAvalonia;assembly=FluentIcons.FluentAvalonia"
x:DataType="vmInference:ControlNetCardViewModel"> x:DataType="vmInference:ControlNetCardViewModel">
<Design.PreviewWith> <Design.PreviewWith>
<StackPanel Width="350" Height="400"> <StackPanel Width="400" Height="500">
<controls:ControlNetCard DataContext="{x:Static mocks:DesignData.ControlNetCardViewModel}" /> <controls:ControlNetCard DataContext="{x:Static mocks:DesignData.ControlNetCardViewModel}" />
</StackPanel> </StackPanel>
</Design.PreviewWith> </Design.PreviewWith>
<Styles.Resources>
<ToolTip x:Key="WipFeatureToolTip">
<StackPanel>
<TextBlock Text="{x:Static lang:Resources.Label_WipFeature}" Theme="{DynamicResource BodyStrongTextBlockStyle}" />
<TextBlock Foreground="{DynamicResource TextFillColorTertiaryBrush}" Text="{x:Static lang:Resources.Label_WipFeatureDescription}" />
</StackPanel>
</ToolTip>
</Styles.Resources>
<Style Selector="controls|ControlNetCard"> <Style Selector="controls|ControlNetCard">
<!-- Set Defaults --> <!-- Set Defaults -->
<Setter Property="Template"> <Setter Property="Template">
<ControlTemplate> <ControlTemplate>
<controls:Card Padding="12"> <controls:Card Padding="12">
<StackPanel Spacing="6">
<sg:SpacedGrid <sg:SpacedGrid
ColumnDefinitions="0.8*,*" ColumnDefinitions="0.34*,0.65*"
ColumnSpacing="8" ColumnSpacing="8"
RowDefinitions="Auto,Auto,Auto,Auto,5,Auto" RowDefinitions="Auto,Auto,Auto"
RowSpacing="6"> RowSpacing="4">
<!-- Image Select --> <!-- Image Select -->
<controls:SelectImageCard <controls:SelectImageCard
Grid.RowSpan="4" Grid.RowSpan="2"
Padding="6" Padding="6"
VerticalAlignment="Stretch"
DataContext="{Binding SelectImageCardViewModel}" DataContext="{Binding SelectImageCardViewModel}"
FontSize="13" /> FontSize="13" />
<!-- TODO: Preprocessor Model --> <!-- TODO: Preprocessor Model -->
<TextBlock
Grid.Row="0"
Grid.Column="1"
MinWidth="60"
Foreground="{StaticResource ThemeGreyColor}"
VerticalAlignment="Bottom"
Text="{x:Static lang:Resources.Label_Preprocessor}"
TextAlignment="Left" />
<ui:FAComboBox <ui:FAComboBox
Grid.Row="1" Grid.Row="0"
Grid.Column="1" Grid.Column="1"
IsEnabled="False"
Margin="0,0,0,4" Margin="0,0,0,4"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
Theme="{StaticResource FAComboBoxHybridModelTheme}" /> extensions:ShowDisabledTooltipExtension.ShowOnDisabled="True"
Header="{x:Static lang:Resources.Label_Preprocessor}"
IsEnabled="False"
Theme="{StaticResource FAComboBoxHybridModelTheme}"
ToolTip.ShowDelay="1200"
ToolTip.Tip="{StaticResource WipFeatureToolTip}" />
<!-- ControlNet Model --> <!-- ControlNet Model -->
<TextBlock
Grid.Row="2"
Grid.Column="1"
MinWidth="60"
VerticalAlignment="Bottom"
Text="{x:Static lang:Resources.Label_Model}"
TextAlignment="Left" />
<ui:FAComboBox <ui:FAComboBox
x:Name="PART_ModelComboBox" x:Name="PART_ModelComboBox"
Grid.Row="3" Grid.Row="1"
Grid.Column="1" Grid.Column="1"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
Header="{x:Static lang:Resources.Label_Model}"
ItemContainerTheme="{StaticResource FAComboBoxItemHybridModelTheme}"
ItemsSource="{Binding ClientManager.ControlNetModels}" ItemsSource="{Binding ClientManager.ControlNetModels}"
SelectedItem="{Binding SelectedModel}" SelectedItem="{Binding SelectedModel}">
ItemContainerTheme="{StaticResource FAComboBoxItemHybridModelTheme}">
<ui:FAComboBox.Resources> <ui:FAComboBox.Resources>
<input:StandardUICommand <input:StandardUICommand x:Key="RemoteDownloadCommand" Command="{Binding RemoteDownloadCommand}" />
x:Key="RemoteDownloadCommand"
Command="{Binding RemoteDownloadCommand}"/>
</ui:FAComboBox.Resources> </ui:FAComboBox.Resources>
<ui:FAComboBox.DataTemplates> <ui:FAComboBox.DataTemplates>
<controls:HybridModelTemplateSelector> <controls:HybridModelTemplateSelector>
<DataTemplate DataType="models:HybridModelFile" x:Key="{x:Static models:HybridModelType.Downloadable}"> <DataTemplate x:Key="{x:Static models:HybridModelType.Downloadable}" DataType="models:HybridModelFile">
<Grid ColumnDefinitions="*,Auto"> <Grid ColumnDefinitions="*,Auto">
<TextBlock <TextBlock Foreground="{DynamicResource ThemeGreyColor}" Text="{Binding ShortDisplayName}" />
Text="{Binding ShortDisplayName}"
Foreground="{DynamicResource ThemeGreyColor}"/>
<Button <Button
Grid.Column="1" Grid.Column="1"
Classes="transparent-full"
Margin="8,0,0,0" Margin="8,0,0,0"
Padding="0"> Padding="0"
Classes="transparent-full">
<fluentIcons:SymbolIcon <fluentIcons:SymbolIcon
VerticalAlignment="Center" VerticalAlignment="Center"
FontSize="18" FontSize="18"
@ -96,46 +94,45 @@
</Button> </Button>
</Grid> </Grid>
</DataTemplate> </DataTemplate>
<DataTemplate DataType="models:HybridModelFile" x:Key="{x:Static models:HybridModelType.None}"> <DataTemplate x:Key="{x:Static models:HybridModelType.None}" DataType="models:HybridModelFile">
<TextBlock Text="{Binding ShortDisplayName}"/> <TextBlock Text="{Binding ShortDisplayName}" />
</DataTemplate> </DataTemplate>
</controls:HybridModelTemplateSelector> </controls:HybridModelTemplateSelector>
</ui:FAComboBox.DataTemplates> </ui:FAComboBox.DataTemplates>
</ui:FAComboBox> </ui:FAComboBox>
</sg:SpacedGrid>
<sg:SpacedGrid <sg:SpacedGrid
RowDefinitions="Auto,Auto,Auto,Auto"
ColumnDefinitions="*,Auto,Auto" ColumnDefinitions="*,Auto,Auto"
Grid.Row="5" RowDefinitions="Auto,Auto,Auto,Auto"
Grid.Column="0" RowSpacing="0">
Grid.ColumnSpan="2">
<!-- Strength --> <!-- Strength -->
<TextBlock <TextBlock
Text="{x:Static lang:Resources.Label_ControlWeight}"
VerticalAlignment="Center" VerticalAlignment="Center"
Text="{x:Static lang:Resources.Label_ControlWeight}"
TextAlignment="Left" /> TextAlignment="Left" />
<ui:NumberBox <ui:NumberBox
Grid.Row="0" Grid.Row="0"
Grid.Column="1" Grid.Column="1"
Grid.ColumnSpan="2" Grid.ColumnSpan="2"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
SpinButtonPlacementMode="Inline"
Maximum="10" Maximum="10"
Minimum="0" Minimum="0"
SmallChange="0.01"
SimpleNumberFormat="F2" SimpleNumberFormat="F2"
SmallChange="0.01"
SpinButtonPlacementMode="Inline"
Value="{Binding Strength}" /> Value="{Binding Strength}" />
<Slider <Slider
Grid.Row="1" Grid.Row="1"
Grid.Column="0" Grid.Column="0"
Grid.ColumnSpan="3" Grid.ColumnSpan="3"
Margin="0,-8,0,0" Margin="0,-8,0,0"
IsSnapToTickEnabled="True"
Maximum="1" Maximum="1"
Minimum="0" Minimum="0"
TickFrequency="0.01"
IsSnapToTickEnabled="True"
SmallChange="0.01" SmallChange="0.01"
TickFrequency="0.01"
Value="{Binding Strength}" /> Value="{Binding Strength}" />
<!-- Start end percent --> <!-- Start end percent -->
@ -150,63 +147,35 @@
Grid.Row="2" Grid.Row="2"
Grid.Column="1" Grid.Column="1"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
SimpleNumberFormat="P0"
Maximum="10" Maximum="10"
Minimum="0" Minimum="0"
SimpleNumberFormat="P0"
SmallChange="0.01" SmallChange="0.01"
Value="{Binding StartPercent}" /> Value="{Binding StartPercent}" />
<ui:NumberBox <ui:NumberBox
Grid.Row="2" Grid.Row="2"
Grid.Column="2" Grid.Column="2"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
SimpleNumberFormat="P0"
Maximum="10"
Minimum="0"
SmallChange="0.01"
Value="{Binding EndPercent}" />
<!--<sg:SpacedGrid Grid.Row="2"
Grid.Column="1"
HorizontalAlignment="Right"
ColumnDefinitions="*,*">
<ui:NumberBox
HorizontalAlignment="Stretch"
Maximum="10"
Minimum="0"
SmallChange="0.01"
Value="{Binding StartPercent}" />
<ui:NumberBox
Grid.Column="1"
HorizontalAlignment="Stretch"
Maximum="10" Maximum="10"
Minimum="0" Minimum="0"
SimpleNumberFormat="P0"
SmallChange="0.01" SmallChange="0.01"
Value="{Binding EndPercent}" /> Value="{Binding EndPercent}" />
</sg:SpacedGrid>-->
<ui:RangeSlider <ui:RangeSlider
Grid.Row="3" Grid.Row="3"
Grid.Column="0" Grid.Column="0"
Grid.ColumnSpan="3" Grid.ColumnSpan="3"
Margin="0,4,0,0" Margin="0,4,0,0"
ShowValueToolTip="False"
Maximum="1" Maximum="1"
Minimum="0" Minimum="0"
RangeEnd="{Binding EndPercent}" RangeEnd="{Binding EndPercent}"
RangeStart="{Binding StartPercent}" RangeStart="{Binding StartPercent}"
ShowValueToolTip="False"
StepFrequency="0.01" StepFrequency="0.01"
ToolTipStringFormat="P" /> ToolTipStringFormat="P" />
</sg:SpacedGrid> </sg:SpacedGrid>
<StackPanel
Grid.Row="4"
Grid.Column="0"
Grid.ColumnSpan="2">
</StackPanel> </StackPanel>
</sg:SpacedGrid>
</controls:Card> </controls:Card>
</ControlTemplate> </ControlTemplate>
</Setter> </Setter>

61
StabilityMatrix.Avalonia/Controls/SelectImageCard.axaml

@ -3,11 +3,11 @@
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:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:fluentIcons="clr-namespace:FluentIcons.FluentAvalonia;assembly=FluentIcons.FluentAvalonia"
xmlns:lang="clr-namespace:StabilityMatrix.Avalonia.Languages"
xmlns:mocks="using:StabilityMatrix.Avalonia.DesignData" xmlns:mocks="using:StabilityMatrix.Avalonia.DesignData"
xmlns:ui="using:FluentAvalonia.UI.Controls" xmlns:ui="using:FluentAvalonia.UI.Controls"
xmlns:vmInference="clr-namespace:StabilityMatrix.Avalonia.ViewModels.Inference" xmlns:vmInference="clr-namespace:StabilityMatrix.Avalonia.ViewModels.Inference"
xmlns:fluentIcons="clr-namespace:FluentIcons.FluentAvalonia;assembly=FluentIcons.FluentAvalonia"
xmlns:lang="clr-namespace:StabilityMatrix.Avalonia.Languages"
x:DataType="vmInference:SelectImageCardViewModel"> x:DataType="vmInference:SelectImageCardViewModel">
<Design.PreviewWith> <Design.PreviewWith>
@ -31,8 +31,7 @@
Padding="{TemplateBinding Padding}" Padding="{TemplateBinding Padding}"
HorizontalAlignment="{TemplateBinding HorizontalAlignment}" HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
VerticalAlignment="{TemplateBinding VerticalAlignment}" VerticalAlignment="{TemplateBinding VerticalAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalAlignment}" VerticalContentAlignment="{TemplateBinding VerticalAlignment}">
IsCardVisualsEnabled="True">
<!-- Background frame --> <!-- Background frame -->
<ExperimentalAcrylicBorder <ExperimentalAcrylicBorder
@ -47,16 +46,52 @@
CornerRadius="4" CornerRadius="4"
CurrentImage="{Binding CurrentBitmap, Mode=OneWayToSource}" CurrentImage="{Binding CurrentBitmap, Mode=OneWayToSource}"
IsVisible="{Binding !IsSelectionAvailable}" IsVisible="{Binding !IsSelectionAvailable}"
Source="{Binding ImageSource}" /> RenderOptions.BitmapInterpolationMode="HighQuality"
Source="{Binding ImageSource}"
Stretch="UniformToFill"
StretchDirection="Both" />
<!-- Missing image -->
<Border
BorderThickness="3"
BorderBrush="{StaticResource ThemeCoralRedColor}"
BoxShadow="inset 1.2 0 20 1.8 #66000000"
CornerRadius="4"
IsVisible="{Binding IsImageFileNotFound}">
<Grid
HorizontalAlignment="Center"
VerticalAlignment="Center"
RowDefinitions="Auto,Auto,Auto">
<fluentIcons:SymbolIcon
FontSize="28"
IsFilled="True"
Symbol="DocumentQuestionMark" />
<TextBlock
Grid.Row="1"
FontSize="{TemplateBinding FontSize}"
Text="{x:Static lang:Resources.Label_MissingImageFile}"
TextAlignment="Center"
TextWrapping="WrapWithOverflow" />
<SelectableTextBlock
Grid.Row="2"
FontSize="10"
Margin="0,4,0,0"
Foreground="{DynamicResource TextFillColorTertiaryBrush}"
Text="{Binding NotFoundImagePath}"
TextAlignment="Center"
TextWrapping="WrapWithOverflow" />
</Grid>
</Border>
<!-- Active Selection Prompt --> <!-- Active Selection Prompt -->
<Border <Border
IsVisible="{Binding !IsSelectionAvailable}"
Margin="4" Margin="4"
HorizontalAlignment="Right" HorizontalAlignment="Right"
VerticalAlignment="Top" VerticalAlignment="Top"
BoxShadow="inset 1.2 0 80 1.8 #66000000" BoxShadow="inset 1.2 0 80 1.8 #66000000"
CornerRadius="10"> CornerRadius="10"
IsVisible="{Binding !IsSelectionAvailable}">
<Border.Resources> <Border.Resources>
<DropShadowEffect <DropShadowEffect
x:Key="TextDropShadowEffect" x:Key="TextDropShadowEffect"
@ -70,22 +105,24 @@
Color="#FF000000" /> Color="#FF000000" />
</Border.Resources> </Border.Resources>
<Button <Button
ToolTip.Tip="{x:Static lang:Resources.Action_ReplaceContents}"
Padding="2" Padding="2"
Classes="transparent" Classes="transparent"
Command="{Binding SelectImageFromFilePickerCommand}" Command="{Binding SelectImageFromFilePickerCommand}"
CornerRadius="10" CornerRadius="10"
FontSize="{TemplateBinding FontSize}"> FontSize="{TemplateBinding FontSize}"
ToolTip.Tip="{x:Static lang:Resources.Action_ReplaceContents}">
<fluentIcons:SymbolIcon <fluentIcons:SymbolIcon
Effect="{StaticResource ImageDropShadowEffect}" Effect="{StaticResource ImageDropShadowEffect}"
FontSize="28" FontSize="28"
IsFilled="True" IsFilled="True"
Symbol="ImageArrowCounterclockwise"/> Symbol="ImageArrowCounterclockwise" />
</Button> </Button>
</Border> </Border>
<!-- No Image Selection Prompt --> <!-- No Image Selection Prompt -->
<controls:LineDashFrame <controls:LineDashFrame
Padding="8,4"
CornerRadius="8"
IsVisible="{Binding IsSelectionAvailable}" IsVisible="{Binding IsSelectionAvailable}"
Stroke="DimGray" Stroke="DimGray"
StrokeDashLine="6" StrokeDashLine="6"
@ -95,18 +132,18 @@
<Grid <Grid
HorizontalAlignment="Center" HorizontalAlignment="Center"
VerticalAlignment="Center" VerticalAlignment="Center"
Cursor="Hand"
RowDefinitions="*,Auto,Auto"> RowDefinitions="*,Auto,Auto">
<fluentIcons:SymbolIcon <fluentIcons:SymbolIcon
FontSize="28" FontSize="28"
IsFilled="True" IsFilled="True"
Symbol="ImageCopy"/> Symbol="ImageCopy" />
<TextBlock <TextBlock
Grid.Row="1" Grid.Row="1"
FontSize="{TemplateBinding FontSize}" FontSize="{TemplateBinding FontSize}"
Foreground="DarkGray" Foreground="DarkGray"
Text="Drag an image here" Text="Drag an image here"
TextAlignment="Center"
TextWrapping="WrapWithOverflow" /> TextWrapping="WrapWithOverflow" />
<StackPanel <StackPanel

2
StabilityMatrix.Avalonia/Controls/StackCard.axaml

@ -20,7 +20,7 @@
<!-- Set Defaults --> <!-- Set Defaults -->
<Setter Property="Template"> <Setter Property="Template">
<ControlTemplate> <ControlTemplate>
<ScrollViewer Padding="8,0,8,0"> <ScrollViewer Padding="4,0,4,0">
<ItemsControl <ItemsControl
VerticalAlignment="Top" VerticalAlignment="Top"
ItemsSource="{Binding Cards}"> ItemsSource="{Binding Cards}">

8
StabilityMatrix.Avalonia/Controls/StackCard.axaml.cs

@ -9,10 +9,10 @@ namespace StabilityMatrix.Avalonia.Controls;
[Transient] [Transient]
public class StackCard : TemplatedControl public class StackCard : TemplatedControl
{ {
public static readonly StyledProperty<int> SpacingProperty = AvaloniaProperty.Register< public static readonly StyledProperty<int> SpacingProperty = AvaloniaProperty.Register<StackCard, int>(
StackCard, "Spacing",
int 4
>("Spacing", 8); );
public int Spacing public int Spacing
{ {

8
StabilityMatrix.Avalonia/Controls/StackEditableCard.axaml

@ -11,7 +11,7 @@
x:DataType="vmInference:StackEditableCardViewModel"> x:DataType="vmInference:StackEditableCardViewModel">
<Design.PreviewWith> <Design.PreviewWith>
<Grid Width="600" Height="800"> <Grid Width="500" Height="600">
<controls:StackEditableCard <controls:StackEditableCard
DataContext="{x:Static mocks:DesignData.StackEditableCardViewModel}"/> DataContext="{x:Static mocks:DesignData.StackEditableCardViewModel}"/>
</Grid> </Grid>
@ -58,11 +58,15 @@
Grid.Row="1" Grid.Row="1"
Theme="{StaticResource DraggableListBoxTheme}" Theme="{StaticResource DraggableListBoxTheme}"
ItemsSource="{Binding Cards}"> ItemsSource="{Binding Cards}">
<ListBox.DataTemplates> <ListBox.DataTemplates>
<local:ViewLocator/> <local:ViewLocator/>
</ListBox.DataTemplates> </ListBox.DataTemplates>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Spacing="0" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox> </ListBox>
</Grid> </Grid>

7
StabilityMatrix.Avalonia/Controls/StackExpander.axaml

@ -21,10 +21,13 @@
<Style Selector="controls|StackExpander"> <Style Selector="controls|StackExpander">
<!-- Set Defaults --> <!-- Set Defaults -->
<Setter Property="CornerRadius" Value="8"/>
<Setter Property="HorizontalAlignment" Value="Stretch" /> <Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="Template"> <Setter Property="Template">
<ControlTemplate> <ControlTemplate>
<Expander <Expander
MinHeight="0"
CornerRadius="{TemplateBinding CornerRadius}"
ExpandDirection="{TemplateBinding ExpandDirection}" ExpandDirection="{TemplateBinding ExpandDirection}"
IsExpanded="{TemplateBinding IsExpanded}" IsExpanded="{TemplateBinding IsExpanded}"
HorizontalAlignment="{TemplateBinding HorizontalAlignment}" HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
@ -39,9 +42,9 @@
</Expander.Styles> </Expander.Styles>
<Expander.Header> <Expander.Header>
<Grid ColumnDefinitions="Auto,Auto,*,Auto,Auto"> <Grid ColumnDefinitions="Auto,Auto,*,Auto,Auto" Margin="0,-4">
<ToggleSwitch <ToggleSwitch
Margin="2,0,0,2" Margin="2,0,0,4"
VerticalContentAlignment="Center" VerticalContentAlignment="Center"
IsChecked="{Binding IsEnabled}" IsChecked="{Binding IsEnabled}"
OffContent="" OffContent=""

27
StabilityMatrix.Avalonia/Languages/Resources.Designer.cs generated

@ -1418,6 +1418,15 @@ namespace StabilityMatrix.Avalonia.Languages {
} }
} }
/// <summary>
/// Looks up a localized string similar to Missing Image File.
/// </summary>
public static string Label_MissingImageFile {
get {
return ResourceManager.GetString("Label_MissingImageFile", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Model. /// Looks up a localized string similar to Model.
/// </summary> /// </summary>
@ -2156,6 +2165,24 @@ namespace StabilityMatrix.Avalonia.Languages {
} }
} }
/// <summary>
/// Looks up a localized string similar to Not yet available.
/// </summary>
public static string Label_WipFeature {
get {
return ResourceManager.GetString("Label_WipFeature", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Feature will be available in a future update.
/// </summary>
public static string Label_WipFeatureDescription {
get {
return ResourceManager.GetString("Label_WipFeatureDescription", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to You&apos;re up to date. /// Looks up a localized string similar to You&apos;re up to date.
/// </summary> /// </summary>

9
StabilityMatrix.Avalonia/Languages/Resources.resx

@ -885,4 +885,13 @@
<data name="Action_ReplaceContents" xml:space="preserve"> <data name="Action_ReplaceContents" xml:space="preserve">
<value>Replace Contents</value> <value>Replace Contents</value>
</data> </data>
<data name="Label_WipFeature" xml:space="preserve">
<value>Not yet available</value>
</data>
<data name="Label_WipFeatureDescription" xml:space="preserve">
<value>Feature will be available in a future update</value>
</data>
<data name="Label_MissingImageFile" xml:space="preserve">
<value>Missing Image File</value>
</data>
</root> </root>

20
StabilityMatrix.Avalonia/Models/ImageSource.cs

@ -1,5 +1,7 @@
using System; using System;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Text.Json.Serialization;
using System.Threading.Tasks; using System.Threading.Tasks;
using AsyncImageLoader; using AsyncImageLoader;
using Avalonia.Media.Imaging; using Avalonia.Media.Imaging;
@ -27,6 +29,7 @@ public record ImageSource : IDisposable
/// <summary> /// <summary>
/// Bitmap /// Bitmap
/// </summary> /// </summary>
[JsonIgnore]
public Bitmap? Bitmap { get; set; } public Bitmap? Bitmap { get; set; }
/// <summary> /// <summary>
@ -34,6 +37,9 @@ public record ImageSource : IDisposable
/// </summary> /// </summary>
public string? Label { get; set; } public string? Label { get; set; }
[JsonConstructor]
public ImageSource() { }
public ImageSource(FilePath localFile) public ImageSource(FilePath localFile)
{ {
LocalFile = localFile; LocalFile = localFile;
@ -49,6 +55,7 @@ public record ImageSource : IDisposable
Bitmap = bitmap; Bitmap = bitmap;
} }
[JsonIgnore]
public Task<Bitmap?> BitmapAsync => GetBitmapAsync(); public Task<Bitmap?> BitmapAsync => GetBitmapAsync();
/// <summary> /// <summary>
@ -121,9 +128,20 @@ public record ImageSource : IDisposable
throw new InvalidOperationException("ImageSource is not a local file"); throw new InvalidOperationException("ImageSource is not a local file");
} }
// Calculate hash if not available
if (contentHashBlake3 is null) if (contentHashBlake3 is null)
{ {
throw new InvalidOperationException("Blake3 hash has not been calculated yet"); // File must exist
if (!LocalFile.Exists)
{
throw new FileNotFoundException("Image file does not exist", LocalFile);
}
// Fail in debug since hash should have been pre-calculated
Debug.Fail("Hash has not been calculated when GetHashGuidFileNameCached() was called");
var data = LocalFile.ReadAllBytes();
contentHashBlake3 = FileHash.GetBlake3Parallel(data);
} }
var extension = LocalFile.Info.Extension; var extension = LocalFile.Info.Extension;

3
StabilityMatrix.Avalonia/Styles/ListBoxStyles.axaml

@ -22,7 +22,7 @@
<Setter Property="FontFamily" Value="{DynamicResource ContentControlThemeFontFamily}" /> <Setter Property="FontFamily" Value="{DynamicResource ContentControlThemeFontFamily}" />
<Setter Property="FontSize" Value="{DynamicResource ControlContentThemeFontSize}" /> <Setter Property="FontSize" Value="{DynamicResource ControlContentThemeFontSize}" />
<Setter Property="Background" Value="{DynamicResource ListViewItemBackground}" /> <Setter Property="Background" Value="{DynamicResource ListViewItemBackground}" />
<Setter Property="CornerRadius" Value="7" /> <!--<Setter Property="CornerRadius" Value="7" />-->
<Setter Property="Foreground" Value="{DynamicResource ListViewItemForeground}" /> <Setter Property="Foreground" Value="{DynamicResource ListViewItemForeground}" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" /> <Setter Property="HorizontalContentAlignment" Value="Stretch" />
@ -34,7 +34,6 @@
<Panel> <Panel>
<ContentPresenter <ContentPresenter
Name="PART_ContentPresenter" Name="PART_ContentPresenter"
Margin="0,4"
Padding="{TemplateBinding Padding}" Padding="{TemplateBinding Padding}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"

34
StabilityMatrix.Avalonia/ViewModels/Inference/SelectImageCardViewModel.cs

@ -1,8 +1,9 @@
using System; using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Text.Json.Serialization;
using System.Threading.Tasks; using System.Threading.Tasks;
using AsyncAwaitBestPractices; using AsyncAwaitBestPractices;
using Avalonia.Input; using Avalonia.Input;
@ -21,6 +22,7 @@ using StabilityMatrix.Avalonia.ViewModels.Base;
using StabilityMatrix.Core.Attributes; using StabilityMatrix.Core.Attributes;
using StabilityMatrix.Core.Models.Database; using StabilityMatrix.Core.Models.Database;
using Size = System.Drawing.Size; using Size = System.Drawing.Size;
#pragma warning disable CS0657 // Not a valid attribute location for this declaration
namespace StabilityMatrix.Avalonia.ViewModels.Inference; namespace StabilityMatrix.Avalonia.ViewModels.Inference;
@ -28,7 +30,7 @@ namespace StabilityMatrix.Avalonia.ViewModels.Inference;
[ManagedService] [ManagedService]
[Transient] [Transient]
public partial class SelectImageCardViewModel(INotificationService notificationService) public partial class SelectImageCardViewModel(INotificationService notificationService)
: ViewModelBase, : LoadableViewModelBase,
IDropTarget, IDropTarget,
IComfyStep IComfyStep
{ {
@ -36,17 +38,31 @@ public partial class SelectImageCardViewModel(INotificationService notificationS
[ObservableProperty] [ObservableProperty]
[NotifyPropertyChangedFor(nameof(IsSelectionAvailable))] [NotifyPropertyChangedFor(nameof(IsSelectionAvailable))]
[NotifyPropertyChangedFor(nameof(IsImageFileNotFound))]
private ImageSource? imageSource; private ImageSource? imageSource;
[ObservableProperty] [ObservableProperty]
[property: JsonIgnore]
[NotifyPropertyChangedFor(nameof(CurrentBitmapSize))] [NotifyPropertyChangedFor(nameof(CurrentBitmapSize))]
private IImage? currentBitmap; private IImage? currentBitmap;
[ObservableProperty] [ObservableProperty]
[property: JsonIgnore]
[NotifyPropertyChangedFor(nameof(IsSelectionAvailable))] [NotifyPropertyChangedFor(nameof(IsSelectionAvailable))]
private bool isSelectionEnabled = true; private bool isSelectionEnabled = true;
public bool IsSelectionAvailable => IsSelectionEnabled && ImageSource == null; /// <summary>
/// True if the image file is set but the local file does not exist.
/// </summary>
[MemberNotNullWhen(true, nameof(NotFoundImagePath))]
public bool IsImageFileNotFound => ImageSource?.LocalFile?.Exists == false;
public bool IsSelectionAvailable => IsSelectionEnabled && ImageSource == null && !IsImageFileNotFound;
/// <summary>
/// Path of the not found image
/// </summary>
public string? NotFoundImagePath => ImageSource?.LocalFile?.FullPath;
public Size? CurrentBitmapSize => public Size? CurrentBitmapSize =>
CurrentBitmap is null CurrentBitmap is null
@ -63,6 +79,15 @@ public partial class SelectImageCardViewModel(INotificationService notificationS
); );
} }
partial void OnImageSourceChanged(ImageSource? value)
{
// Cache the hash for later upload use
if (value?.LocalFile is { Exists: true })
{
value.GetBlake3HashAsync().SafeFireAndForget();
}
}
[RelayCommand] [RelayCommand]
private async Task SelectImageFromFilePickerAsync() private async Task SelectImageFromFilePickerAsync()
{ {
@ -139,8 +164,5 @@ public partial class SelectImageCardViewModel(INotificationService notificationS
ImageSource = image; ImageSource = image;
current?.Dispose(); current?.Dispose();
// Cache the hash for later upload use
image.GetBlake3HashAsync().SafeFireAndForget();
} }
} }

Loading…
Cancel
Save