Browse Source

Implement file indexed folder cards

pull/5/head
Ionite 1 year ago
parent
commit
2b1d81497f
No known key found for this signature in database
  1. 120
      StabilityMatrix/CheckpointManagerPage.xaml
  2. 66
      StabilityMatrix/CheckpointManagerPage.xaml.cs
  3. 59
      StabilityMatrix/DesignData/MockCheckpointManagerViewModel.cs
  4. 16
      StabilityMatrix/Models/CheckpointCard.cs
  5. 88
      StabilityMatrix/Models/CheckpointFile.cs
  6. 49
      StabilityMatrix/Models/CheckpointFolder.cs
  7. 34
      StabilityMatrix/Models/CheckpointFolderCard.cs
  8. 26
      StabilityMatrix/ViewModels/CheckpointManagerViewModel.cs

120
StabilityMatrix/CheckpointManagerPage.xaml

@ -1,72 +1,89 @@
<Page
Background="{DynamicResource ApplicationBackgroundBrush}"
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
d:DataContext="{d:DesignInstance Type=viewModels:CheckpointManagerViewModel,
Loaded="CheckpointManagerPage_OnLoaded"
d:DataContext="{d:DesignInstance Type=designData:MockCheckpointManagerViewModel,
IsDesignTimeCreatable=True}"
d:DesignHeight="450"
d:DesignWidth="700"
d:DesignHeight="500"
d:DesignWidth="650"
mc:Ignorable="d"
x:Class="StabilityMatrix.CheckpointManagerPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:designData="clr-namespace:StabilityMatrix.DesignData"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:models="clr-namespace:StabilityMatrix.Models"
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
xmlns:viewModels="clr-namespace:StabilityMatrix.ViewModels"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<d:DesignProperties.DataContext>
<viewModels:CheckpointManagerViewModel>
<viewModels:CheckpointManagerViewModel.CheckpointFolderCards>
<models:CheckpointFolderCard Name="hi" />
<models:CheckpointFolderCard Name="hi" />
<models:CheckpointFolderCard Name="hi" />
</viewModels:CheckpointManagerViewModel.CheckpointFolderCards>
</viewModels:CheckpointManagerViewModel>
</d:DesignProperties.DataContext>
<Page.Resources>
<DataTemplate DataType="{x:Type models:CheckpointFolderCard}" x:Key="CheckpointFolderCardGridDataTemplate">
<DataTemplate DataType="{x:Type models:CheckpointFile}" x:Key="CheckpointCardDataTemplate">
<ui:Card Width="256">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="0.2*" />
</Grid.ColumnDefinitions>
<StackPanel Orientation="Vertical">
<ui:Image
CornerRadius="4"
Margin="4,4,4,8"
MinHeight="256"
MinWidth="200"
Source="{Binding PreviewImage}"
Stretch="UniformToFill"
Visibility="Collapsed"
Width="128" />
<TextBlock
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
Margin="0,0,0,0"
Text="{Binding Title}"
VerticalAlignment="Center" />
<TextBlock
FontSize="11"
Foreground="{DynamicResource TextFillColorTertiaryBrush}"
Margin="0,2,0,0"
Text="{Binding FileName}"
VerticalAlignment="Center" />
</StackPanel>
<ui:DropDownButton Grid.Column="1" Margin="16,0,0,0">
<ui:DropDownButton.Style>
<Style BasedOn="{StaticResource {x:Type ui:DropDownButton}}" TargetType="{x:Type ui:DropDownButton}">
<Setter Property="Visibility" Value="Hidden" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ui:Card}}}" Value="True">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</ui:DropDownButton.Style>
<ui:SymbolIcon Symbol="ChevronDown24" />
</ui:DropDownButton>
</Grid>
</ui:Card>
</DataTemplate>
<DataTemplate DataType="{x:Type models:CheckpointFolder}" x:Key="CheckpointFolderCardGridDataTemplate">
<ui:CardExpander
Header="{Binding Name}"
AllowDrop="True"
Drop="FolderCard_OnDrop"
Header="{Binding Title}"
IsExpanded="True"
Margin="8">
<ui:VirtualizingGridView>
<ItemsControl ItemsSource="{Binding CheckpointCards}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type models:CheckpointCard}">
<ui:Card Margin="8">
<VirtualizingStackPanel Orientation="Vertical">
<ui:Image
CornerRadius="4"
Margin="4,4,4,8"
MinHeight="256"
MinWidth="200"
Source="{Binding Image}"
Stretch="UniformToFill"
Visibility="Collapsed"
Width="128" />
<TextBlock
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
Margin="0,0,0,0"
Text="{Binding Name}"
VerticalAlignment="Center" />
<TextBlock
FontSize="11"
Foreground="{DynamicResource TextFillColorTertiaryBrush}"
Margin="0,2,0,0"
Text="{Binding FileName}"
VerticalAlignment="Center" />
</VirtualizingStackPanel>
</ui:Card>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ui:VirtualizingGridView>
Margin="8"
PreviewDragEnter="FolderCard_OnPreviewDragEnter"
PreviewDragLeave="FolderCard_OnPreviewDragLeave"
PreviewDragOver="FolderCard_OnPreviewDragOver"
PreviewDrop="FolderCard_OnPreviewDrop">
<ui:VirtualizingGridView
ItemTemplate="{StaticResource CheckpointCardDataTemplate}"
ItemsSource="{Binding CheckpointFiles}"
Padding="5"
SpacingMode="StartAndEndOnly" />
</ui:CardExpander>
</DataTemplate>
<DataTemplate DataType="{x:Type models:CheckpointFolderCard}" x:Key="CheckpointFolderCardDataTemplate">
<DataTemplate DataType="{x:Type models:CheckpointFolder}" x:Key="CheckpointFolderCardDataTemplate">
<ContentControl Content="{Binding}">
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
@ -80,8 +97,7 @@
<!-- Option Cards -->
<ItemsControl
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Center"
ItemTemplate="{StaticResource CheckpointFolderCardDataTemplate}"
ItemsSource="{Binding CheckpointFolderCards}"
ItemTemplate="{StaticResource CheckpointFolderCardGridDataTemplate}"
ItemsSource="{Binding CheckpointFolders}"
Margin="16,16,16,16" />
</Page>

66
StabilityMatrix/CheckpointManagerPage.xaml.cs

@ -1,13 +1,77 @@
using System.Windows.Controls;
using System.Diagnostics;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Effects;
using StabilityMatrix.ViewModels;
using Wpf.Ui.Controls;
namespace StabilityMatrix;
public partial class CheckpointManagerPage : Page
{
private readonly CheckpointManagerViewModel viewModel;
public CheckpointManagerPage(CheckpointManagerViewModel viewModel)
{
this.viewModel = viewModel;
InitializeComponent();
DataContext = viewModel;
}
private void FolderCard_OnPreviewDrop(object sender, DragEventArgs e)
{
Debug.WriteLine($"PreviewDrop: {sender}, {e}");
if (e.Data.GetDataPresent(DataFormats.FileDrop))
{
var files = e.Data.GetData(DataFormats.FileDrop) as string[];
var firstFile = files?[0];
// Make title by title casing the file name
var title = System.IO.Path.GetFileNameWithoutExtension(firstFile);
title = Thread.CurrentThread.CurrentCulture.TextInfo.ToTitleCase(title!);
viewModel.CheckpointFolders[0].CheckpointFiles.Add(new()
{
Title = title,
FileName = firstFile!,
});
}
}
private void FolderCard_OnDrop(object sender, DragEventArgs e)
{
Debug.WriteLine($"Drop: {sender}, {e}");
}
private void FolderCard_OnPreviewDragOver(object sender, DragEventArgs e)
{
Debug.WriteLine($"PreviewDragOver: {sender}, {e}");
}
private void FolderCard_OnPreviewDragLeave(object sender, DragEventArgs e)
{
var senderCard = (CardExpander) sender;
senderCard.Header = "Stable Diffusion";
Debug.WriteLine($"PreviewDragLeave: {sender}, {e}");
}
private void FolderCard_OnPreviewDragEnter(object sender, DragEventArgs e)
{
var senderCard = (CardExpander) sender;
senderCard.Header = "Drag here to add a checkpoint";
// Apply a hover-over effect
senderCard.Effect = new DropShadowEffect
{
Color = Colors.Black,
Direction = 0,
ShadowDepth = 0,
Opacity = 0.5,
BlurRadius = 10
};
Debug.WriteLine($"PreviewDragEnter: {sender}, {e}");
}
private async void CheckpointManagerPage_OnLoaded(object sender, RoutedEventArgs e)
{
await viewModel.OnLoaded();
}
}

59
StabilityMatrix/DesignData/MockCheckpointManagerViewModel.cs

@ -0,0 +1,59 @@
using System.ComponentModel;
using StabilityMatrix.ViewModels;
namespace StabilityMatrix.DesignData;
[DesignOnly(true)]
public class MockCheckpointManagerViewModel : CheckpointManagerViewModel
{
public MockCheckpointManagerViewModel()
{
CheckpointFolders = new()
{
new()
{
Title = "Stable Diffusion",
CheckpointFiles = new()
{
new()
{
Title = "Stable Diffusion v1.5",
FileName = "v1-5-pruned-emaonly.safetensors",
},
new()
{
Title = "Scenery Mix",
FileName = "scenery-mix.pt",
},
new()
{
Title = "Example Realistic",
FileName = "exr-v21.safetensors",
},
new()
{
Title = "Painting e12",
FileName = "painting-e12.pt",
},
}
},
new()
{
Title = "Lora",
CheckpointFiles = new()
{
new()
{
Title = "Detail Tweaker LoRA",
FileName = "add_detail.safetensors",
},
new()
{
Title = "Armor Suit LoRa",
FileName = "ArmorSuit_v1.safetensors",
},
}
}
};
}
}

16
StabilityMatrix/Models/CheckpointCard.cs

@ -1,16 +0,0 @@
using System.Windows.Media.Imaging;
using CommunityToolkit.Mvvm.ComponentModel;
namespace StabilityMatrix.Models;
public partial class CheckpointCard : ObservableObject
{
[ObservableProperty]
private BitmapImage? image;
[ObservableProperty]
private string name;
[ObservableProperty]
private string fileName;
}

88
StabilityMatrix/Models/CheckpointFile.cs

@ -0,0 +1,88 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Windows.Media.Imaging;
using CommunityToolkit.Mvvm.ComponentModel;
namespace StabilityMatrix.Models;
public partial class CheckpointFile : ObservableObject
{
/// <summary>
/// Absolute path to the checkpoint file.
/// </summary>
[ObservableProperty]
private string filePath;
/// <summary>
/// Custom title for UI.
/// </summary>
[ObservableProperty]
private string title;
[ObservableProperty]
private string? previewImagePath;
[ObservableProperty]
private BitmapImage? previewImage;
public bool IsPreviewImageLoaded => PreviewImage != null;
[ObservableProperty]
private string fileName;
private static readonly string[] SupportedCheckpointExtensions = { ".safetensors", ".pt" };
private static readonly string[] SupportedImageExtensions = { ".png", ".jpg", ".jpeg" };
/// <summary>
/// Indexes directory and yields all checkpoint files.
/// First we match all files with supported extensions.
/// If found, we also look for
/// - {filename}.preview.{image-extensions}
/// </summary>
public static IEnumerable<CheckpointFile> FromDirectoryIndex(string directory, SearchOption searchOption = SearchOption.TopDirectoryOnly)
{
// Get all files with supported extensions
var allExtensions = SupportedCheckpointExtensions.Concat(SupportedImageExtensions).ToImmutableHashSet();
var files = allExtensions.AsParallel()
.SelectMany(pattern => Directory.EnumerateFiles(directory, $"*{pattern}", searchOption)).ToDictionary<string, string>(Path.GetFileName);
foreach (var file in files.Keys.Where(k => SupportedCheckpointExtensions.Contains(Path.GetExtension(k))))
{
var checkpointFile = new CheckpointFile
{
Title = Path.GetFileNameWithoutExtension(file),
FilePath = Path.Combine(directory, file),
FileName = file,
};
// Check for preview image
var previewImage = SupportedImageExtensions.Select(ext => $"{checkpointFile.FileName}.preview.{ext}").FirstOrDefault(files.ContainsKey);
if (previewImage != null)
{
checkpointFile.PreviewImage = new BitmapImage(new Uri(Path.Combine(directory, previewImage)));
}
yield return checkpointFile;
}
}
/// <summary>
/// Index with progress reporting.
/// </summary>
public static IEnumerable<CheckpointFile> FromDirectoryIndex(string directory, IProgress<ProgressReport> progress,
SearchOption searchOption = SearchOption.TopDirectoryOnly)
{
var current = 0ul;
foreach (var checkpointFile in FromDirectoryIndex(directory, searchOption))
{
current++;
progress.Report(new ProgressReport(current, "Indexing", checkpointFile.FileName));
yield return checkpointFile;
}
}
}

49
StabilityMatrix/Models/CheckpointFolder.cs

@ -0,0 +1,49 @@
using System;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.ComponentModel;
namespace StabilityMatrix.Models;
public partial class CheckpointFolder : ObservableObject
{
/// <summary>
/// Absolute path to the folder.
/// </summary>
[ObservableProperty]
private string directoryPath;
/// <summary>
/// Custom title for UI.
/// </summary>
[ObservableProperty]
private string title;
/// <summary>
/// State of indexing.
/// </summary>
[ObservableProperty]
private LoadState indexState = LoadState.NotLoaded;
public ObservableCollection<CheckpointFile> CheckpointFiles { get; set; } = new();
/// <summary>
/// Indexes the folder for checkpoint files.
/// </summary>
public async Task IndexAsync(IProgress<ProgressReport>? progress = default)
{
IndexState = LoadState.Loading;
var checkpointFiles = await (progress switch
{
null => Task.Run(() => CheckpointFile.FromDirectoryIndex(DirectoryPath)),
_ => Task.Run(() => CheckpointFile.FromDirectoryIndex(DirectoryPath, progress))
});
CheckpointFiles.Clear();
foreach (var checkpointFile in checkpointFiles)
{
CheckpointFiles.Add(checkpointFile);
}
IndexState = LoadState.Loaded;
}
}

34
StabilityMatrix/Models/CheckpointFolderCard.cs

@ -1,34 +0,0 @@
using System.Collections.ObjectModel;
using CommunityToolkit.Mvvm.ComponentModel;
namespace StabilityMatrix.Models;
public partial class CheckpointFolderCard : ObservableObject
{
[ObservableProperty]
private string name;
public ObservableCollection<CheckpointCard> CheckpointCards { get; set; } = new()
{
new CheckpointCard
{
Name = "Stable Diffusion v1.5",
FileName = "v1-5-pruned-emaonly.safetensors",
},
new CheckpointCard
{
Name = "Stable Diffusion v1.5 (EMA)",
FileName = "v1-5-emaonly.safetensors",
},
new CheckpointCard
{
Name = "Stable Diffusion v1.5 (EMA, 512x512)",
FileName = "v1-5-emaonly-512.safetensors",
},
new CheckpointCard
{
Name = "Stable Diffusion v2.0",
FileName = "v2-0-pruned-emaonly.safetensors",
},
};
}

26
StabilityMatrix/ViewModels/CheckpointManagerViewModel.cs

@ -1,4 +1,5 @@
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.ComponentModel;
using StabilityMatrix.Models;
@ -6,15 +7,22 @@ namespace StabilityMatrix.ViewModels;
public partial class CheckpointManagerViewModel : ObservableObject
{
public ObservableCollection<CheckpointFolderCard> CheckpointFolderCards { get; set; } = new()
public ObservableCollection<CheckpointFolder> CheckpointFolders { get; set; } = new();
public async Task OnLoaded()
{
new()
{
Name = "Stable Diffusion"
},
new()
CheckpointFolders.Clear();
var folder = new CheckpointFolder
{
Name = "Lora"
}
};
Title = "Stable Diffusion",
DirectoryPath = @"L:\Image ML\stable-diffusion-webui\models\Stable-diffusion"
};
CheckpointFolders.Add(folder);
await folder.IndexAsync();
}
public void OnFolderCardDrop()
{
}
}

Loading…
Cancel
Save