Skip to content

Commit 1cdfbc2

Browse files
authored
Merge pull request LykosAI#359 from ionite34/more-drag-n-drop
2 parents 7bf16db + da92d45 commit 1cdfbc2

14 files changed

+359
-105
lines changed

CHANGELOG.md

+12-5
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,20 @@ and this project adheres to [Semantic Versioning 2.0](https://semver.org/spec/v2
77

88
## v2.7.0-dev.2
99
### Added
10-
- Outputs Page
11-
- Added Refresh button to update gallery from file system changes
12-
### Changed
13-
- Outputs Page
14-
- Updated button and menu layout
10+
#### General
11+
- Added an X button to all search fields to instantly clear them (Esc key also works)
12+
#### Outputs Page
13+
- Added Refresh button to update gallery from file system changes
14+
#### Checkpoints Page
15+
- Added the ability to drag & drop checkpoints between different folders
16+
## Changed
17+
#### Outputs Page
18+
- Updated button and menu layout
19+
#### Packages Page
20+
- Rearranged Add Package dialog slightly to accommodate longer package list
1521
### Fixed
1622
- Fixed InvalidOperation errors when signing into accounts shortly after signing out, while the previous account update is still running
23+
- Fixed Outputs page reverting back to Shared Output Folder every time the page is reloaded
1724

1825
## v2.7.0-dev.1
1926
### Added

StabilityMatrix.Avalonia/ViewModels/CheckpointBrowserViewModel.cs

+5
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,11 @@ public void LastPage()
534534
CurrentPageNumber = TotalPages;
535535
}
536536

537+
public void ClearSearchQuery()
538+
{
539+
SearchQuery = string.Empty;
540+
}
541+
537542
partial void OnShowNsfwChanged(bool value)
538543
{
539544
settingsManager.Transaction(s => s.ModelBrowserNsfwEnabled, value);

StabilityMatrix.Avalonia/ViewModels/CheckpointManager/CheckpointFile.cs

+10-1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ public partial class CheckpointFile : ViewModelBase
5555
[ObservableProperty]
5656
private CivitModelType modelType;
5757

58+
[ObservableProperty]
59+
private CheckpointFolder parentFolder;
60+
5861
public string FileName => Path.GetFileName(FilePath);
5962

6063
public ObservableCollection<string> Badges { get; set; } = new();
@@ -136,6 +139,8 @@ private async Task DeleteAsync()
136139
RemoveFromParentList();
137140
}
138141

142+
public void OnMoved() => RemoveFromParentList();
143+
139144
[RelayCommand]
140145
private async Task RenameAsync()
141146
{
@@ -218,6 +223,7 @@ private void OpenOnCivitAi()
218223
/// - {filename}.cm-info.json (connected model info)
219224
/// </summary>
220225
public static IEnumerable<CheckpointFile> FromDirectoryIndex(
226+
CheckpointFolder parentFolder,
221227
string directory,
222228
SearchOption searchOption = SearchOption.TopDirectoryOnly
223229
)
@@ -266,6 +272,8 @@ public static IEnumerable<CheckpointFile> FromDirectoryIndex(
266272
checkpointFile.PreviewImagePath = Assets.NoImage.ToString();
267273
}
268274

275+
checkpointFile.ParentFolder = parentFolder;
276+
269277
yield return checkpointFile;
270278
}
271279
}
@@ -327,13 +335,14 @@ var file in Directory.EnumerateFiles(
327335
/// Index with progress reporting.
328336
/// </summary>
329337
public static IEnumerable<CheckpointFile> FromDirectoryIndex(
338+
CheckpointFolder parentFolder,
330339
string directory,
331340
IProgress<ProgressReport> progress,
332341
SearchOption searchOption = SearchOption.TopDirectoryOnly
333342
)
334343
{
335344
var current = 0ul;
336-
foreach (var checkpointFile in FromDirectoryIndex(directory, searchOption))
345+
foreach (var checkpointFile in FromDirectoryIndex(parentFolder, directory, searchOption))
337346
{
338347
current++;
339348
progress.Report(new ProgressReport(current, "Indexing", checkpointFile.FileName));

StabilityMatrix.Avalonia/ViewModels/CheckpointManager/CheckpointFolder.cs

+71-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
using StabilityMatrix.Avalonia.Services;
1919
using StabilityMatrix.Avalonia.ViewModels.Base;
2020
using StabilityMatrix.Core.Attributes;
21+
using StabilityMatrix.Core.Exceptions;
2122
using StabilityMatrix.Core.Extensions;
2223
using StabilityMatrix.Core.Helper;
2324
using StabilityMatrix.Core.Models;
@@ -212,6 +213,10 @@ public async Task OnDrop(DragEventArgs e)
212213
var paths = files.Select(f => f.Path.LocalPath).ToArray();
213214
await ImportFilesAsync(paths, settingsManager.Settings.IsImportAsConnected);
214215
}
216+
else if (e.Data.Get("Context") is CheckpointFile file)
217+
{
218+
await MoveBetweenFolders(file);
219+
}
215220
}
216221
catch (Exception)
217222
{
@@ -320,6 +325,71 @@ private async Task CreateSubFolder()
320325
}
321326
}
322327

328+
public async Task MoveBetweenFolders(CheckpointFile sourceFile)
329+
{
330+
var delay = 1.5f;
331+
try
332+
{
333+
Progress.Value = 0;
334+
var sourcePath = new FilePath(sourceFile.FilePath);
335+
var fileNameWithoutExt = Path.GetFileNameWithoutExtension(sourcePath);
336+
var sourceCmInfoPath = Path.Combine(
337+
sourcePath.Directory,
338+
$"{fileNameWithoutExt}.cm-info.json"
339+
);
340+
var sourcePreviewPath = Path.Combine(
341+
sourcePath.Directory,
342+
$"{fileNameWithoutExt}.preview.jpeg"
343+
);
344+
var destinationFilePath = Path.Combine(DirectoryPath, sourcePath.Name);
345+
var destinationCmInfoPath = Path.Combine(
346+
DirectoryPath,
347+
$"{fileNameWithoutExt}.cm-info.json"
348+
);
349+
var destinationPreviewPath = Path.Combine(
350+
DirectoryPath,
351+
$"{fileNameWithoutExt}.preview.jpeg"
352+
);
353+
354+
// Move files
355+
if (File.Exists(sourcePath))
356+
{
357+
Progress.Text = $"Moving {sourcePath.Name}...";
358+
await FileTransfers.MoveFileAsync(sourcePath, destinationFilePath);
359+
}
360+
361+
Progress.Value = 33;
362+
Progress.Text = $"Moving {sourcePath.Name} metadata...";
363+
364+
if (File.Exists(sourceCmInfoPath))
365+
{
366+
await FileTransfers.MoveFileAsync(sourceCmInfoPath, destinationCmInfoPath);
367+
}
368+
369+
Progress.Value = 66;
370+
371+
if (File.Exists(sourcePreviewPath))
372+
{
373+
await FileTransfers.MoveFileAsync(sourcePreviewPath, destinationPreviewPath);
374+
}
375+
376+
Progress.Value = 100;
377+
Progress.Text = $"Moved {sourcePath.Name} to {Title}";
378+
sourceFile.OnMoved();
379+
BackgroundIndex();
380+
delay = 0.5f;
381+
}
382+
catch (FileTransferExistsException)
383+
{
384+
Progress.Value = 0;
385+
Progress.Text = "Failed to move file: destination file exists";
386+
}
387+
finally
388+
{
389+
DelayedClearProgress(TimeSpan.FromSeconds(delay));
390+
}
391+
}
392+
323393
/// <summary>
324394
/// Imports files to the folder. Reports progress to instance properties.
325395
/// </summary>
@@ -505,7 +575,7 @@ private IEnumerable<CheckpointFile> GetCheckpointFiles()
505575
return Enumerable.Empty<CheckpointFile>();
506576
}
507577

508-
return CheckpointFile.FromDirectoryIndex(DirectoryPath);
578+
return CheckpointFile.FromDirectoryIndex(this, DirectoryPath);
509579
}
510580

511581
/// <summary>

StabilityMatrix.Avalonia/ViewModels/CheckpointsPageViewModel.cs

+5
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,11 @@ public override void OnLoaded()
124124
Logger.Info($"OnLoadedAsync in {sw.ElapsedMilliseconds}ms");
125125
}
126126

127+
public void ClearSearchQuery()
128+
{
129+
SearchFilter = string.Empty;
130+
}
131+
127132
// ReSharper disable once UnusedParameterInPartialMethod
128133
partial void OnSearchFilterChanged(string value)
129134
{

StabilityMatrix.Avalonia/ViewModels/OutputsPageViewModel.cs

+71-48
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,10 @@ public partial class OutputsPageViewModel : PageViewModelBase
6969

7070
[ObservableProperty]
7171
[NotifyPropertyChangedFor(nameof(CanShowOutputTypes))]
72-
private PackageOutputCategory selectedCategory;
72+
private PackageOutputCategory? selectedCategory;
7373

7474
[ObservableProperty]
75-
private SharedOutputType selectedOutputType;
75+
private SharedOutputType? selectedOutputType;
7676

7777
[ObservableProperty]
7878
[NotifyPropertyChangedFor(nameof(NumImagesSelected))]
@@ -136,6 +136,8 @@ ILogger<OutputsPageViewModel> logger
136136
settings => settings.OutputsImageSize,
137137
delay: TimeSpan.FromMilliseconds(250)
138138
);
139+
140+
RefreshCategories();
139141
}
140142

141143
public override void OnLoaded()
@@ -148,49 +150,8 @@ public override void OnLoaded()
148150

149151
Directory.CreateDirectory(settingsManager.ImagesDirectory);
150152

151-
var packageCategories = settingsManager.Settings.InstalledPackages
152-
.Where(x => !x.UseSharedOutputFolder)
153-
.Select(packageFactory.GetPackagePair)
154-
.WhereNotNull()
155-
.Where(
156-
p =>
157-
p.BasePackage.SharedOutputFolders != null
158-
&& p.BasePackage.SharedOutputFolders.Any()
159-
)
160-
.Select(
161-
pair =>
162-
new PackageOutputCategory
163-
{
164-
Path = Path.Combine(
165-
pair.InstalledPackage.FullPath!,
166-
pair.BasePackage.OutputFolderName
167-
),
168-
Name = pair.InstalledPackage.DisplayName ?? ""
169-
}
170-
)
171-
.ToList();
172-
173-
packageCategories.Insert(
174-
0,
175-
new PackageOutputCategory
176-
{
177-
Path = settingsManager.ImagesDirectory,
178-
Name = "Shared Output Folder"
179-
}
180-
);
181-
182-
packageCategories.Insert(
183-
1,
184-
new PackageOutputCategory
185-
{
186-
Path = settingsManager.ImagesInferenceDirectory,
187-
Name = "Inference"
188-
}
189-
);
190-
191-
Categories = new ObservableCollection<PackageOutputCategory>(packageCategories);
192-
SelectedCategory = Categories.First();
193-
SelectedOutputType = SharedOutputType.All;
153+
SelectedCategory ??= Categories.First();
154+
SelectedOutputType ??= SharedOutputType.All;
194155
SearchQuery = string.Empty;
195156
ImageSize = settingsManager.Settings.OutputsImageSize;
196157

@@ -216,14 +177,14 @@ partial void OnSelectedCategoryChanged(
216177
GetOutputs(path);
217178
}
218179

219-
partial void OnSelectedOutputTypeChanged(SharedOutputType oldValue, SharedOutputType newValue)
180+
partial void OnSelectedOutputTypeChanged(SharedOutputType? oldValue, SharedOutputType? newValue)
220181
{
221-
if (oldValue == newValue)
182+
if (oldValue == newValue || newValue == null)
222183
return;
223184

224185
var path =
225186
newValue == SharedOutputType.All
226-
? SelectedCategory.Path
187+
? SelectedCategory?.Path
227188
: Path.Combine(SelectedCategory.Path, newValue.ToString());
228189
GetOutputs(path);
229190
}
@@ -299,6 +260,7 @@ public Task CopyImage(string imagePath)
299260

300261
public void Refresh()
301262
{
263+
Dispatcher.UIThread.Post(RefreshCategories);
302264
Dispatcher.UIThread.Post(OnLoaded);
303265
}
304266

@@ -502,6 +464,11 @@ var path in Directory.EnumerateFiles(
502464
IsConsolidating = false;
503465
}
504466

467+
public void ClearSearchQuery()
468+
{
469+
SearchQuery = string.Empty;
470+
}
471+
505472
private void GetOutputs(string directory)
506473
{
507474
if (!settingsManager.IsLibraryDirSet)
@@ -533,4 +500,60 @@ private void GetOutputs(string directory)
533500
OutputsCache.EditDiff(files);
534501
}
535502
}
503+
504+
private void RefreshCategories()
505+
{
506+
if (Design.IsDesignMode)
507+
return;
508+
509+
if (!settingsManager.IsLibraryDirSet)
510+
return;
511+
512+
var previouslySelectedCategory = SelectedCategory;
513+
514+
var packageCategories = settingsManager.Settings.InstalledPackages
515+
.Where(x => !x.UseSharedOutputFolder)
516+
.Select(packageFactory.GetPackagePair)
517+
.WhereNotNull()
518+
.Where(
519+
p =>
520+
p.BasePackage.SharedOutputFolders != null
521+
&& p.BasePackage.SharedOutputFolders.Any()
522+
)
523+
.Select(
524+
pair =>
525+
new PackageOutputCategory
526+
{
527+
Path = Path.Combine(
528+
pair.InstalledPackage.FullPath!,
529+
pair.BasePackage.OutputFolderName
530+
),
531+
Name = pair.InstalledPackage.DisplayName ?? ""
532+
}
533+
)
534+
.ToList();
535+
536+
packageCategories.Insert(
537+
0,
538+
new PackageOutputCategory
539+
{
540+
Path = settingsManager.ImagesDirectory,
541+
Name = "Shared Output Folder"
542+
}
543+
);
544+
545+
packageCategories.Insert(
546+
1,
547+
new PackageOutputCategory
548+
{
549+
Path = settingsManager.ImagesInferenceDirectory,
550+
Name = "Inference"
551+
}
552+
);
553+
554+
Categories = new ObservableCollection<PackageOutputCategory>(packageCategories);
555+
SelectedCategory =
556+
Categories.FirstOrDefault(x => x.Name == previouslySelectedCategory?.Name)
557+
?? Categories.First();
558+
}
536559
}

0 commit comments

Comments
 (0)