|
@@ -19,13 +19,14 @@ namespace PicView.Avalonia.DragAndDrop;
|
|
|
public static class DragAndDropHelper
|
|
|
{
|
|
|
private static DragDropView? _dragDropView;
|
|
|
-
|
|
|
private static PreLoadValue? _preLoadValue;
|
|
|
|
|
|
+ #region Public Entry Points
|
|
|
+
|
|
|
public static async Task Drop(DragEventArgs e, MainViewModel vm)
|
|
|
{
|
|
|
RemoveDragDropView();
|
|
|
-
|
|
|
+
|
|
|
var files = e.Data.GetFiles();
|
|
|
if (files == null)
|
|
|
{
|
|
@@ -35,77 +36,90 @@ public static class DragAndDropHelper
|
|
|
|
|
|
var storageItems = files as IStorageItem[] ?? files.ToArray();
|
|
|
var firstFile = storageItems.FirstOrDefault();
|
|
|
+ if (firstFile == null)
|
|
|
+ {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Handle opening additional files in new windows if needed
|
|
|
+ _ = Task.Run(() => HandleAdditionalFiles(storageItems.Skip(1)));
|
|
|
+
|
|
|
var path = firstFile.Path.LocalPath;
|
|
|
+
|
|
|
if (e.Data.Contains("text/x-moz-url"))
|
|
|
{
|
|
|
await HandleDropFromUrl(e, vm);
|
|
|
- if (vm.CurrentView != vm.ImageViewer)
|
|
|
- {
|
|
|
- await Dispatcher.UIThread.InvokeAsync(() => vm.CurrentView = vm.ImageViewer);
|
|
|
- }
|
|
|
+ await EnsureImageViewerDisplayed(vm);
|
|
|
}
|
|
|
else if (path.IsSupported())
|
|
|
{
|
|
|
- if (vm.CurrentView != vm.ImageViewer)
|
|
|
- {
|
|
|
- await Dispatcher.UIThread.InvokeAsync(() => vm.CurrentView = vm.ImageViewer);
|
|
|
- }
|
|
|
-
|
|
|
- if (_preLoadValue is not null)
|
|
|
- {
|
|
|
-
|
|
|
- if (_preLoadValue.ImageModel.FileInfo.DirectoryName == Path.GetDirectoryName(path))
|
|
|
- {
|
|
|
- // Check for edge case error
|
|
|
- var isAddedToPreloader = NavigationManager.AddToPreloader(path, _preLoadValue.ImageModel);
|
|
|
- if (isAddedToPreloader)
|
|
|
- {
|
|
|
- NavigationManager.ImageIterator.Resynchronize();
|
|
|
- await NavigationManager.LoadPicFromFile(path, vm, _preLoadValue.ImageModel.FileInfo);
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- await NavigationManager.LoadPicFromStringAsync(path, vm).ConfigureAwait(false);
|
|
|
- }
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- await NavigationManager.LoadPicFromStringAsync(path, vm).ConfigureAwait(false);
|
|
|
- }
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- await NavigationManager.LoadPicFromStringAsync(path, vm).ConfigureAwait(false);
|
|
|
- }
|
|
|
-
|
|
|
+ await EnsureImageViewerDisplayed(vm);
|
|
|
+ await LoadSupportedFile(path, vm);
|
|
|
}
|
|
|
else if (Directory.Exists(path))
|
|
|
{
|
|
|
- if (vm.CurrentView != vm.ImageViewer)
|
|
|
- {
|
|
|
- await Dispatcher.UIThread.InvokeAsync(() => vm.CurrentView = vm.ImageViewer);
|
|
|
- }
|
|
|
+ await EnsureImageViewerDisplayed(vm);
|
|
|
await NavigationManager.LoadPicFromDirectoryAsync(path, vm).ConfigureAwait(false);
|
|
|
-
|
|
|
}
|
|
|
else if (path.IsArchive())
|
|
|
{
|
|
|
- if (vm.CurrentView != vm.ImageViewer)
|
|
|
+ await EnsureImageViewerDisplayed(vm);
|
|
|
+ await NavigationManager.LoadPicFromArchiveAsync(path, vm).ConfigureAwait(false);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public static async Task DragEnter(DragEventArgs e, MainViewModel vm, Control control)
|
|
|
+ {
|
|
|
+ var files = e.Data.GetFiles();
|
|
|
+ if (files != null)
|
|
|
+ {
|
|
|
+ await HandleDragEnterWithFiles(files, vm, control);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ // Try handling as URL
|
|
|
+ var handled = await HandleDragEnterFromUrl(e.Data.Get("text/x-moz-url"), vm);
|
|
|
+ if (!handled)
|
|
|
{
|
|
|
- await Dispatcher.UIThread.InvokeAsync(() => vm.CurrentView = vm.ImageViewer);
|
|
|
+ RemoveDragDropView();
|
|
|
}
|
|
|
- await NavigationManager.LoadPicFromArchiveAsync(path, vm).ConfigureAwait(false);
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- if (!Settings.UIProperties.OpenInSameWindow)
|
|
|
+ public static void DragLeave(DragEventArgs e, Control control)
|
|
|
+ {
|
|
|
+ if (control.IsPointerOver)
|
|
|
{
|
|
|
- foreach (var file in storageItems.Skip(1))
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ RemoveDragDropView();
|
|
|
+ _preLoadValue = null;
|
|
|
+ }
|
|
|
+
|
|
|
+ public static void RemoveDragDropView()
|
|
|
+ {
|
|
|
+ UIHelper.GetMainView.MainGrid.Children.Remove(_dragDropView);
|
|
|
+ _dragDropView = null;
|
|
|
+ }
|
|
|
+
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ #region Private Helpers
|
|
|
+
|
|
|
+ private static void HandleAdditionalFiles(IEnumerable<IStorageItem> additionalFiles)
|
|
|
+ {
|
|
|
+ if (Settings.UIProperties.OpenInSameWindow)
|
|
|
+ {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ foreach (var file in additionalFiles)
|
|
|
+ {
|
|
|
+ var filepath = file.Path.LocalPath;
|
|
|
+ if (filepath.IsSupported())
|
|
|
{
|
|
|
- var filepath = file.Path.LocalPath;
|
|
|
- if (filepath.IsSupported())
|
|
|
- {
|
|
|
- ProcessHelper.StartNewProcess(filepath);
|
|
|
- }
|
|
|
+ ProcessHelper.StartNewProcess(filepath);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -119,157 +133,179 @@ public static class DragAndDropHelper
|
|
|
var url = dataStr.Split((char)10).FirstOrDefault();
|
|
|
if (url != null)
|
|
|
{
|
|
|
- if (url.StartsWith("file://"))
|
|
|
- {
|
|
|
- var file = url[7..];
|
|
|
- if (file.StartsWith('/'))
|
|
|
- {
|
|
|
- file = file[1..];
|
|
|
- }
|
|
|
- if (file.IsArchive())
|
|
|
- {
|
|
|
- await NavigationManager.LoadPicFromArchiveAsync(file, vm).ConfigureAwait(false);
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- await NavigationManager.LoadPicFromFile(file, vm).ConfigureAwait(false);
|
|
|
- }
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- await NavigationManager.LoadPicFromUrlAsync(url, vm).ConfigureAwait(false);
|
|
|
- }
|
|
|
+ await LoadFromUrl(url, vm);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- public static async Task DragEnter(DragEventArgs e, MainViewModel vm, Control control) =>
|
|
|
- await HandleDragEnter(e.Data.GetFiles(), e, vm, control);
|
|
|
-
|
|
|
- private static async Task HandleDragEnter(IEnumerable<IStorageItem> files, DragEventArgs e, MainViewModel vm, Control control)
|
|
|
+ private static async Task LoadFromUrl(string url, MainViewModel vm)
|
|
|
{
|
|
|
- IStorageItem[]? fileArray = null;
|
|
|
- if (files is not null)
|
|
|
+ // Remove preview first and show loading
|
|
|
+ RemoveDragDropView();
|
|
|
+ vm.IsLoading = true;
|
|
|
+ if (vm.ImageViewer?.MainImage != null)
|
|
|
{
|
|
|
- fileArray = files as IStorageItem[] ?? files.ToArray();
|
|
|
+ vm.ImageViewer.MainImage.Source = null;
|
|
|
}
|
|
|
|
|
|
- await Dispatcher.UIThread.InvokeAsync(() =>
|
|
|
+ if (url.StartsWith("file://"))
|
|
|
{
|
|
|
- if (_dragDropView == null)
|
|
|
+ var file = url[7..];
|
|
|
+ if (file.StartsWith('/'))
|
|
|
{
|
|
|
- _dragDropView = new DragDropView
|
|
|
- {
|
|
|
- DataContext = vm
|
|
|
- };
|
|
|
- if (!control.IsPointerOver)
|
|
|
- {
|
|
|
- UIHelper.GetMainView.MainGrid.Children.Add(_dragDropView);
|
|
|
- }
|
|
|
+ file = file[1..];
|
|
|
}
|
|
|
- else
|
|
|
+
|
|
|
+ if (file.IsArchive())
|
|
|
{
|
|
|
- _dragDropView.RemoveThumbnail();
|
|
|
+ await NavigationManager.LoadPicFromArchiveAsync(file, vm).ConfigureAwait(false);
|
|
|
}
|
|
|
- });
|
|
|
- if (fileArray is null)
|
|
|
- {
|
|
|
- var handledFromUrl = await HandleDragEnterFromUrl(e, vm);
|
|
|
- if (!handledFromUrl)
|
|
|
+ else
|
|
|
{
|
|
|
- RemoveDragDropView();
|
|
|
+ await NavigationManager.LoadPicFromFile(file, vm).ConfigureAwait(false);
|
|
|
}
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ await NavigationManager.LoadPicFromUrlAsync(url, vm).ConfigureAwait(false);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static async Task HandleDragEnterWithFiles(IEnumerable<IStorageItem> files, MainViewModel vm,
|
|
|
+ Control control)
|
|
|
+ {
|
|
|
+ var fileArray = files as IStorageItem[] ?? files.ToArray();
|
|
|
+ if (fileArray.Length == 0)
|
|
|
+ {
|
|
|
return;
|
|
|
}
|
|
|
+
|
|
|
+ await EnsureDragDropViewCreated(vm, control);
|
|
|
+
|
|
|
var firstFile = fileArray[0];
|
|
|
var path = firstFile.Path.LocalPath;
|
|
|
+
|
|
|
if (Directory.Exists(path))
|
|
|
{
|
|
|
- await Dispatcher.UIThread.InvokeAsync(() =>
|
|
|
- {
|
|
|
- if (!control.IsPointerOver)
|
|
|
- {
|
|
|
- _dragDropView.AddDirectoryIcon();
|
|
|
- }
|
|
|
- });
|
|
|
+ await ShowDirectoryIcon(control);
|
|
|
}
|
|
|
- else
|
|
|
+ else if (path.IsArchive())
|
|
|
+ {
|
|
|
+ await ShowArchiveIcon(control);
|
|
|
+ }
|
|
|
+ else if (path.IsSupported())
|
|
|
+ {
|
|
|
+ await ShowFilePreview(path, control);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static async Task ShowDirectoryIcon(Control control)
|
|
|
+ {
|
|
|
+ await Dispatcher.UIThread.InvokeAsync(() =>
|
|
|
{
|
|
|
- if (path.IsArchive())
|
|
|
+ if (!control.IsPointerOver)
|
|
|
{
|
|
|
- if (!control.IsPointerOver)
|
|
|
- {
|
|
|
- _dragDropView.AddZipIcon();
|
|
|
- }
|
|
|
+ _dragDropView?.AddDirectoryIcon();
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ private static async Task ShowArchiveIcon(Control control)
|
|
|
+ {
|
|
|
+ await Dispatcher.UIThread.InvokeAsync(() =>
|
|
|
+ {
|
|
|
+ if (!control.IsPointerOver)
|
|
|
+ {
|
|
|
+ _dragDropView?.AddZipIcon();
|
|
|
}
|
|
|
- else if (path.IsSupported())
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ private static async Task ShowFilePreview(string path, Control control)
|
|
|
+ {
|
|
|
+ var ext = Path.GetExtension(path);
|
|
|
+ if (ext.Equals(".svg", StringComparison.InvariantCultureIgnoreCase) ||
|
|
|
+ ext.Equals(".svgz", StringComparison.InvariantCultureIgnoreCase))
|
|
|
+ {
|
|
|
+ await Dispatcher.UIThread.InvokeAsync(() => _dragDropView?.UpdateSvgThumbnail(path));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ await LoadAndShowThumbnail(path);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static async Task LoadAndShowThumbnail(string path)
|
|
|
+ {
|
|
|
+ Bitmap? thumb;
|
|
|
+ // Try to get preloaded image first
|
|
|
+ var preload = NavigationManager.GetPreLoadValue(path);
|
|
|
+ if (preload?.ImageModel?.Image is Bitmap bmp)
|
|
|
+ {
|
|
|
+ thumb = bmp;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ // Generate thumbnail
|
|
|
+ thumb = await GetThumbnails.GetThumbAsync(path, SizeDefaults.WindowMinSize - 30)
|
|
|
+ .ConfigureAwait(false);
|
|
|
+ }
|
|
|
+
|
|
|
+ await UpdateThumbnailUI(thumb);
|
|
|
+
|
|
|
+ // Load full image in background
|
|
|
+ await PreloadFullImage(path, preload, thumb);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static async Task PreloadFullImage(string path, PreLoadValue? preload, Bitmap? thumb)
|
|
|
+ {
|
|
|
+ await Task.Run(async () =>
|
|
|
+ {
|
|
|
+ var fileInfo = new FileInfo(path);
|
|
|
+ var sameDirectory = fileInfo.DirectoryName ==
|
|
|
+ NavigationManager.ImageIterator.InitialFileInfo.DirectoryName;
|
|
|
+
|
|
|
+ if (sameDirectory)
|
|
|
{
|
|
|
- var ext = Path.GetExtension(path);
|
|
|
- if (ext.Equals(".svg", StringComparison.InvariantCultureIgnoreCase) || ext.Equals(".svgz", StringComparison.InvariantCultureIgnoreCase))
|
|
|
+ if (preload is null)
|
|
|
{
|
|
|
- await Dispatcher.UIThread.InvokeAsync(() =>
|
|
|
+ _preLoadValue = await NavigationManager.GetPreLoadValueAsync(path);
|
|
|
+ thumb = _preLoadValue.ImageModel.Image as Bitmap;
|
|
|
+ if (thumb is not null)
|
|
|
{
|
|
|
- _dragDropView?.UpdateSvgThumbnail(path);
|
|
|
- });
|
|
|
+ await UpdateThumbnailUI(thumb);
|
|
|
+ }
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- Bitmap? thumb;
|
|
|
- // ReSharper disable once MethodHasAsyncOverload
|
|
|
- var preload = NavigationManager.GetPreLoadValue(path);
|
|
|
- if (preload?.ImageModel?.Image is Bitmap bmp)
|
|
|
- {
|
|
|
- thumb = bmp;
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- thumb = await GetThumbnails.GetThumbAsync(path, SizeDefaults.WindowMinSize - 30)
|
|
|
- .ConfigureAwait(false);
|
|
|
- }
|
|
|
- await Dispatcher.UIThread.InvokeAsync(() => { _dragDropView?.UpdateThumbnail(thumb); });
|
|
|
- await Task.Run(async () =>
|
|
|
- {
|
|
|
- var fileInfo = new FileInfo(path);
|
|
|
- if (fileInfo.DirectoryName == NavigationManager.ImageIterator.InitialFileInfo.DirectoryName)
|
|
|
- {
|
|
|
- if (preload is null)
|
|
|
- {
|
|
|
- _preLoadValue = await NavigationManager.GetPreLoadValueAsync(path);
|
|
|
- thumb = _preLoadValue.ImageModel.Image as Bitmap;
|
|
|
- if (thumb is not null)
|
|
|
- {
|
|
|
- await Dispatcher.UIThread.InvokeAsync(() =>
|
|
|
- {
|
|
|
- _dragDropView?.UpdateThumbnail(thumb);
|
|
|
- }, DispatcherPriority.Loaded);
|
|
|
- }
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- _preLoadValue = preload;
|
|
|
- }
|
|
|
- }
|
|
|
- else if (preload is null)
|
|
|
- {
|
|
|
- thumb = await GetImage.GetDefaultBitmapAsync(fileInfo);
|
|
|
- await Dispatcher.UIThread.InvokeAsync(() =>
|
|
|
- {
|
|
|
- _dragDropView?.UpdateThumbnail(thumb);
|
|
|
- }, DispatcherPriority.Render);
|
|
|
- }
|
|
|
- });
|
|
|
+ _preLoadValue = preload;
|
|
|
+ await UpdateThumbnailUI(thumb);
|
|
|
}
|
|
|
}
|
|
|
- }
|
|
|
+ else if (preload is null)
|
|
|
+ {
|
|
|
+ var model = await GetImageModel.GetImageModelAsync(fileInfo);
|
|
|
+ await UpdateThumbnailUI(thumb);
|
|
|
+ _preLoadValue = new PreLoadValue(model);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ _preLoadValue = preload;
|
|
|
+ await UpdateThumbnailUI(thumb);
|
|
|
+ }
|
|
|
+ });
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
+ private static async Task UpdateThumbnailUI(Bitmap? thumb) =>
|
|
|
+ await Dispatcher.UIThread.InvokeAsync(() => _dragDropView?.UpdateThumbnail(thumb));
|
|
|
+
|
|
|
private static async Task<bool> HandleDragEnterFromUrl(object? urlObject, MainViewModel vm)
|
|
|
{
|
|
|
if (urlObject is null)
|
|
|
{
|
|
|
- _dragDropView.RemoveThumbnail();
|
|
|
+ _dragDropView?.RemoveThumbnail();
|
|
|
return false;
|
|
|
}
|
|
|
+
|
|
|
await Dispatcher.UIThread.InvokeAsync(() =>
|
|
|
{
|
|
|
_dragDropView ??= new DragDropView { DataContext = vm };
|
|
@@ -277,28 +313,74 @@ public static class DragAndDropHelper
|
|
|
{
|
|
|
_dragDropView.AddLinkChain();
|
|
|
}
|
|
|
+
|
|
|
if (!UIHelper.GetMainView.MainGrid.Children.Contains(_dragDropView))
|
|
|
{
|
|
|
UIHelper.GetMainView.MainGrid.Children.Add(_dragDropView);
|
|
|
}
|
|
|
});
|
|
|
+
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
- public static void DragLeave(DragEventArgs e, Control control)
|
|
|
+ private static async Task EnsureDragDropViewCreated(MainViewModel vm, Control control)
|
|
|
{
|
|
|
- if (control.IsPointerOver)
|
|
|
+ await Dispatcher.UIThread.InvokeAsync(() =>
|
|
|
{
|
|
|
- return;
|
|
|
- }
|
|
|
+ if (_dragDropView == null)
|
|
|
+ {
|
|
|
+ _dragDropView = new DragDropView { DataContext = vm };
|
|
|
+ if (!control.IsPointerOver)
|
|
|
+ {
|
|
|
+ UIHelper.GetMainView.MainGrid.Children.Add(_dragDropView);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ _dragDropView.RemoveThumbnail();
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
|
|
|
- RemoveDragDropView();
|
|
|
- _preLoadValue = null;
|
|
|
+ private static async Task LoadSupportedFile(string path, MainViewModel vm)
|
|
|
+ {
|
|
|
+ if (_preLoadValue is not null)
|
|
|
+ {
|
|
|
+ var currentDirectory = Path.GetDirectoryName(path);
|
|
|
+ var preloadDirectory = _preLoadValue.ImageModel.FileInfo.DirectoryName;
|
|
|
+
|
|
|
+ if (currentDirectory == preloadDirectory)
|
|
|
+ {
|
|
|
+ // Check for edge case error
|
|
|
+ var isAddedToPreloader = NavigationManager.AddToPreloader(path, _preLoadValue.ImageModel);
|
|
|
+ if (isAddedToPreloader)
|
|
|
+ {
|
|
|
+ NavigationManager.ImageIterator.Resynchronize();
|
|
|
+ await NavigationManager.LoadPicFromFile(path, vm, _preLoadValue.ImageModel.FileInfo);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ await NavigationManager.LoadPicFromStringAsync(path, vm).ConfigureAwait(false);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ await NavigationManager.LoadPicFromStringAsync(path, vm).ConfigureAwait(false);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ await NavigationManager.LoadPicFromStringAsync(path, vm).ConfigureAwait(false);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- public static void RemoveDragDropView()
|
|
|
+ private static async Task EnsureImageViewerDisplayed(MainViewModel vm)
|
|
|
{
|
|
|
- UIHelper.GetMainView.MainGrid.Children.Remove(_dragDropView);
|
|
|
- _dragDropView = null;
|
|
|
+ if (vm.CurrentView != vm.ImageViewer)
|
|
|
+ {
|
|
|
+ await Dispatcher.UIThread.InvokeAsync(() => vm.CurrentView = vm.ImageViewer);
|
|
|
+ }
|
|
|
}
|
|
|
+
|
|
|
+ #endregion
|
|
|
}
|