mirror of
https://github.com/cyanfish/naps2.git
synced 2024-09-11 15:26:55 +03:00
Remove RecoveryStorageManager as a dependency of UiImageList
This commit is contained in:
parent
1572dfd095
commit
8de8a4b9df
@ -10,6 +10,7 @@ public class ThumbnailRenderQueue : IDisposable
|
||||
private readonly ScanningContext _scanningContext;
|
||||
private readonly ThumbnailRenderer _thumbnailRenderer;
|
||||
private readonly AutoResetEvent _renderThumbnailsWaitHandle = new(false);
|
||||
private readonly ManualResetEvent _renderThumbnailsCompleteHandle = new(false);
|
||||
private int _thumbnailSize;
|
||||
private UiImageList? _imageList;
|
||||
private bool _started;
|
||||
@ -28,7 +29,7 @@ public class ThumbnailRenderQueue : IDisposable
|
||||
{
|
||||
_thumbnailSize = thumbnailSize;
|
||||
}
|
||||
_renderThumbnailsWaitHandle.Set();
|
||||
BumpRenderThread();
|
||||
}
|
||||
|
||||
public void StartRendering(UiImageList imageList)
|
||||
@ -51,9 +52,24 @@ public class ThumbnailRenderQueue : IDisposable
|
||||
|
||||
private void ImageListUpdated(object? sender, EventArgs args)
|
||||
{
|
||||
BumpRenderThread();
|
||||
}
|
||||
|
||||
private void BumpRenderThread()
|
||||
{
|
||||
_renderThumbnailsCompleteHandle.Reset();
|
||||
_renderThumbnailsWaitHandle.Set();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For testing. Waits for pending renders to complete.
|
||||
/// </summary>
|
||||
public void WaitForRendering()
|
||||
{
|
||||
// TODO: Could also check GetNextThumbnailToRender in a loop (though that has some locking issues)
|
||||
_renderThumbnailsCompleteHandle.WaitOne();
|
||||
}
|
||||
|
||||
private void RenderThumbnails()
|
||||
{
|
||||
// TODO: Make this run as async?
|
||||
@ -107,6 +123,7 @@ public class ThumbnailRenderQueue : IDisposable
|
||||
fallback.Increase();
|
||||
continue;
|
||||
}
|
||||
_renderThumbnailsCompleteHandle.Set();
|
||||
_renderThumbnailsWaitHandle.WaitOne();
|
||||
}
|
||||
}
|
||||
@ -172,7 +189,7 @@ public class ThumbnailRenderQueue : IDisposable
|
||||
{
|
||||
if (_disposed) return;
|
||||
_disposed = true;
|
||||
_renderThumbnailsWaitHandle.Set();
|
||||
BumpRenderThread();
|
||||
if (_imageList != null)
|
||||
{
|
||||
lock (_imageList)
|
||||
|
@ -10,6 +10,7 @@ public class RecoveryModule : NinjectModule
|
||||
{
|
||||
string recoveryFolderPath = Path.Combine(Paths.Recovery, Path.GetRandomFileName());
|
||||
var recoveryStorageManager = RecoveryStorageManager.CreateFolder(recoveryFolderPath);
|
||||
recoveryStorageManager.RegisterForChanges(Kernel.Get<UiImageList>());
|
||||
var fileStorageManager = new FileStorageManager(recoveryFolderPath);
|
||||
Kernel.Bind<RecoveryStorageManager>().ToConstant(recoveryStorageManager);
|
||||
Kernel.Bind<FileStorageManager>().ToConstant(fileStorageManager);
|
||||
|
@ -217,7 +217,8 @@ public class AutomatedScanning
|
||||
|
||||
foreach (var scan in _scanList)
|
||||
{
|
||||
var imageList = new UiImageList(_recoveryStorageManager, scan.Select(x => new UiImage(x)).ToList());
|
||||
// TODO: The result doesn't get propagated back, i.e. this does nothing
|
||||
var imageList = new UiImageList(scan.Select(x => new UiImage(x)).ToList());
|
||||
|
||||
if (_options.AltDeinterleave)
|
||||
{
|
||||
|
@ -5,17 +5,13 @@ namespace NAPS2.Lib.Tests.Images;
|
||||
|
||||
public class ThumbnailRenderQueueTests : ContextualTexts
|
||||
{
|
||||
private readonly RecoveryStorageManager _recoveryStorageManager;
|
||||
private readonly UiImageList _uiImageList;
|
||||
private readonly ThumbnailRenderer _thumbnailRenderer;
|
||||
private readonly ThumbnailRenderQueue _thumbnailRenderQueue;
|
||||
|
||||
public ThumbnailRenderQueueTests()
|
||||
{
|
||||
_recoveryStorageManager = RecoveryStorageManager.CreateFolder(Path.Combine(FolderPath, "recovery"));
|
||||
_uiImageList = new UiImageList(_recoveryStorageManager);
|
||||
_thumbnailRenderer = new ThumbnailRenderer(ImageContext);
|
||||
_thumbnailRenderQueue = new ThumbnailRenderQueue(ScanningContext, _thumbnailRenderer);
|
||||
_uiImageList = new UiImageList();
|
||||
_thumbnailRenderQueue = new ThumbnailRenderQueue(ScanningContext, new ThumbnailRenderer(ImageContext));
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
@ -47,7 +43,7 @@ public class ThumbnailRenderQueueTests : ContextualTexts
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task StartRendering_WithThumbnailNeeded_Renders()
|
||||
public void StartRendering_WithThumbnailNeeded_Renders()
|
||||
{
|
||||
AppendToImageList(CreateScannedImage());
|
||||
Assert.Null(_uiImageList.Images[0].GetThumbnailClone());
|
||||
@ -55,7 +51,7 @@ public class ThumbnailRenderQueueTests : ContextualTexts
|
||||
|
||||
_thumbnailRenderQueue.SetThumbnailSize(128);
|
||||
_thumbnailRenderQueue.StartRendering(_uiImageList);
|
||||
await Task.Delay(200);
|
||||
_thumbnailRenderQueue.WaitForRendering();
|
||||
|
||||
Assert.NotNull(_uiImageList.Images[0].GetThumbnailClone());
|
||||
Assert.False(_uiImageList.Images[0].IsThumbnailDirty);
|
||||
|
@ -37,7 +37,7 @@ public class DesktopControllerTests : ContextualTexts
|
||||
ScanningContext.RecoveryPath = Path.Combine(FolderPath, "recovery");
|
||||
ScanningContext.FileStorageManager = new FileStorageManager(ScanningContext.RecoveryPath);
|
||||
_recoveryStorageManager = RecoveryStorageManager.CreateFolder(ScanningContext.RecoveryPath);
|
||||
_imageList = new UiImageList(_recoveryStorageManager);
|
||||
_imageList = new UiImageList();
|
||||
_thumbnailRenderQueue = new ThumbnailRenderQueue(ScanningContext, new ThumbnailRenderer(ImageContext));
|
||||
_operationProgress = new Mock<OperationProgress>();
|
||||
_config = Naps2Config.Stub();
|
||||
@ -154,7 +154,7 @@ public class DesktopControllerTests : ContextualTexts
|
||||
public async Task Initialize_WithUpdateChecksDisabled_DoesntCheckForUpdate()
|
||||
{
|
||||
await _desktopController.Initialize();
|
||||
await Task.Delay(10);
|
||||
await Task.Delay(50);
|
||||
|
||||
Assert.False(_config.Get(c => c.HasCheckedForUpdates));
|
||||
Assert.Null(_config.Get(c => c.LastUpdateCheckDate));
|
||||
@ -167,7 +167,7 @@ public class DesktopControllerTests : ContextualTexts
|
||||
_config.User.Set(c => c.CheckForUpdates, true);
|
||||
|
||||
await _desktopController.Initialize();
|
||||
await Task.Delay(10);
|
||||
await Task.Delay(50);
|
||||
|
||||
Assert.True(_config.Get(c => c.HasCheckedForUpdates));
|
||||
DateAsserts.Recent(TimeSpan.FromMilliseconds(100), _config.Get(c => c.LastUpdateCheckDate));
|
||||
@ -185,7 +185,7 @@ public class DesktopControllerTests : ContextualTexts
|
||||
_updateChecker.Setup(x => x.CheckForUpdates()).ReturnsAsync(mockUpdateInfo);
|
||||
|
||||
await _desktopController.Initialize();
|
||||
await Task.Delay(10);
|
||||
await Task.Delay(50);
|
||||
|
||||
Assert.True(_config.Get(c => c.HasCheckedForUpdates));
|
||||
DateAsserts.Recent(TimeSpan.FromMilliseconds(100), _config.Get(c => c.LastUpdateCheckDate));
|
||||
@ -202,7 +202,7 @@ public class DesktopControllerTests : ContextualTexts
|
||||
_config.User.Set(c => c.CheckForUpdates, true);
|
||||
|
||||
await _desktopController.Initialize();
|
||||
await Task.Delay(10);
|
||||
await Task.Delay(50);
|
||||
|
||||
Assert.False(_config.Get(c => c.HasCheckedForUpdates));
|
||||
Assert.Null(_config.Get(c => c.LastUpdateCheckDate));
|
||||
@ -219,7 +219,7 @@ public class DesktopControllerTests : ContextualTexts
|
||||
_config.User.Set(c => c.LastUpdateCheckDate, updateCheckDate);
|
||||
|
||||
await _desktopController.Initialize();
|
||||
await Task.Delay(10);
|
||||
await Task.Delay(50);
|
||||
|
||||
Assert.True(_config.Get(c => c.HasCheckedForUpdates));
|
||||
Assert.Equal(updateCheckDate, _config.Get(c => c.LastUpdateCheckDate));
|
||||
@ -239,7 +239,7 @@ public class DesktopControllerTests : ContextualTexts
|
||||
_updateChecker.Setup(x => x.CheckForUpdates()).ReturnsAsync(mockUpdateInfo);
|
||||
|
||||
await _desktopController.Initialize();
|
||||
await Task.Delay(10);
|
||||
await Task.Delay(50);
|
||||
|
||||
Assert.True(_config.Get(c => c.HasCheckedForUpdates));
|
||||
DateAsserts.Recent(TimeSpan.FromMilliseconds(100), _config.Get(c => c.LastUpdateCheckDate));
|
||||
|
@ -81,7 +81,7 @@ namespace NAPS2.WinForms
|
||||
Shown += FDesktop_Shown;
|
||||
FormClosing += FDesktop_FormClosing;
|
||||
Closed += FDesktop_Closed;
|
||||
imageList.ImagesUpdated += (_, _) =>
|
||||
imageList.SelectionChanged += (_, _) =>
|
||||
{
|
||||
SafeInvoke(() =>
|
||||
{
|
||||
@ -89,6 +89,7 @@ namespace NAPS2.WinForms
|
||||
_listView!.Selection = _imageList.Selection;
|
||||
});
|
||||
};
|
||||
imageList.ImagesUpdated += (_, _) => SafeInvoke(UpdateToolbar);
|
||||
_profileManager.ProfilesUpdated += (_, _) => UpdateScanButton();
|
||||
_desktopFormProvider.DesktopForm = this;
|
||||
}
|
||||
|
@ -22,11 +22,14 @@ namespace NAPS2.Images;
|
||||
public class RecoveryStorageManager : IDisposable
|
||||
{
|
||||
public const string LOCK_FILE_NAME = ".lock";
|
||||
private static readonly TimeSpan WriteThrottleInterval = TimeSpan.FromMilliseconds(100);
|
||||
|
||||
private readonly ISerializer<RecoveryIndex> _serializer = new XmlSerializer<RecoveryIndex>();
|
||||
private readonly DirectoryInfo _folder;
|
||||
private readonly FileInfo _folderLockFile;
|
||||
private readonly Stream _folderLock;
|
||||
private readonly TimedThrottle _writeThrottle;
|
||||
private UiImageList _imageList;
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
@ -42,10 +45,38 @@ public class RecoveryStorageManager : IDisposable
|
||||
_folder.Create();
|
||||
_folderLockFile = new FileInfo(Path.Combine(RecoveryFolderPath, LOCK_FILE_NAME));
|
||||
_folderLock = _folderLockFile.Open(FileMode.CreateNew, FileAccess.Write, FileShare.None);
|
||||
_writeThrottle = new TimedThrottle(WriteIndexFromImageList, WriteThrottleInterval);
|
||||
}
|
||||
|
||||
public string RecoveryFolderPath { get; }
|
||||
|
||||
// TODO: Maybe just make UiImageList part of the constructor?
|
||||
public void RegisterForChanges(UiImageList imageList)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
if (_disposed) throw new ObjectDisposedException(nameof(RecoveryStorageManager));
|
||||
if (_imageList != null) throw new InvalidOperationException();
|
||||
_imageList = imageList;
|
||||
imageList.ImagesUpdated += ImageListUpdated;
|
||||
}
|
||||
}
|
||||
|
||||
private void ImageListUpdated(object? sender, EventArgs args)
|
||||
{
|
||||
_writeThrottle.RunAction(null);
|
||||
}
|
||||
|
||||
private void WriteIndexFromImageList()
|
||||
{
|
||||
List<UiImage> images;
|
||||
lock (_imageList)
|
||||
{
|
||||
images = _imageList.Images.ToList();
|
||||
}
|
||||
WriteIndex(images);
|
||||
}
|
||||
|
||||
public void WriteIndex(IEnumerable<UiImage> images)
|
||||
{
|
||||
lock (this)
|
||||
@ -82,6 +113,10 @@ public class RecoveryStorageManager : IDisposable
|
||||
_folderLock.Close();
|
||||
_folderLockFile.Delete();
|
||||
_folder.Delete(true);
|
||||
if (_imageList != null)
|
||||
{
|
||||
_imageList.ImagesUpdated -= ImageListUpdated;
|
||||
}
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
|
@ -5,20 +5,15 @@ namespace NAPS2.Images;
|
||||
|
||||
public class UiImageList
|
||||
{
|
||||
private readonly RecoveryStorageManager _recoveryStorageManager;
|
||||
private readonly TimedThrottle _runUpdateEventsThrottle;
|
||||
private StateToken _savedState = new(ImmutableList<ProcessedImage.WeakReference>.Empty);
|
||||
private ListSelection<UiImage> _selection;
|
||||
|
||||
public UiImageList(RecoveryStorageManager recoveryStorageManager)
|
||||
: this(recoveryStorageManager, new List<UiImage>())
|
||||
public UiImageList() : this(new List<UiImage>())
|
||||
{
|
||||
}
|
||||
|
||||
public UiImageList(RecoveryStorageManager recoveryStorageManager, List<UiImage> images)
|
||||
public UiImageList(List<UiImage> images)
|
||||
{
|
||||
_recoveryStorageManager = recoveryStorageManager;
|
||||
_runUpdateEventsThrottle = new TimedThrottle(RunUpdateEvents, TimeSpan.FromMilliseconds(100));
|
||||
Images = images;
|
||||
_selection = ListSelection.Empty<UiImage>();
|
||||
}
|
||||
@ -44,6 +39,8 @@ public class UiImageList
|
||||
private set => _selection = value ?? throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
|
||||
public event EventHandler? SelectionChanged;
|
||||
|
||||
public event EventHandler? ImagesUpdated;
|
||||
|
||||
public event EventHandler? ImagesThumbnailChanged;
|
||||
@ -53,19 +50,17 @@ public class UiImageList
|
||||
public void UpdateSelection(ListSelection<UiImage> newSelection)
|
||||
{
|
||||
Selection = newSelection;
|
||||
ImagesUpdated?.Invoke(this, EventArgs.Empty);
|
||||
SelectionChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
public void Mutate(ListMutation<UiImage> mutation, ListSelection<UiImage>? selectionToMutate = null)
|
||||
{
|
||||
MutateInternal(mutation, selectionToMutate);
|
||||
_runUpdateEventsThrottle.RunAction(SynchronizationContext.Current);
|
||||
}
|
||||
|
||||
public async Task MutateAsync(ListMutation<UiImage> mutation, ListSelection<UiImage>? selectionToMutate = null)
|
||||
{
|
||||
await Task.Run(() => MutateInternal(mutation, selectionToMutate));
|
||||
_runUpdateEventsThrottle.RunAction(SynchronizationContext.Current);
|
||||
}
|
||||
|
||||
private void MutateInternal(ListMutation<UiImage> mutation, ListSelection<UiImage>? selectionToMutate)
|
||||
@ -110,11 +105,11 @@ public class UiImageList
|
||||
var syncContext = SynchronizationContext.Current;
|
||||
if (syncContext != null)
|
||||
{
|
||||
syncContext.Post(_ => _selection = currentSelection, null);
|
||||
syncContext.Post(_ => UpdateSelection(currentSelection), null);
|
||||
}
|
||||
else
|
||||
{
|
||||
_selection = currentSelection;
|
||||
UpdateSelection(currentSelection);
|
||||
}
|
||||
}
|
||||
|
||||
@ -128,15 +123,6 @@ public class UiImageList
|
||||
ImagesThumbnailInvalidated?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
private void RunUpdateEvents()
|
||||
{
|
||||
// TODO: Maybe move this out of this class to an event handler?
|
||||
lock (this)
|
||||
{
|
||||
_recoveryStorageManager.WriteIndex(Images);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A token that stores the current state of the image list for use in comparisons to determine if changes have been
|
||||
/// made since the last save. No disposal is needed.
|
||||
|
Loading…
Reference in New Issue
Block a user