diff --git a/Toolkit.Avalonia/FileProvider.cs b/Toolkit.Avalonia/FileProvider.cs new file mode 100644 index 0000000..96d6f1e --- /dev/null +++ b/Toolkit.Avalonia/FileProvider.cs @@ -0,0 +1,32 @@ +using Avalonia.Controls; +using Avalonia.Platform.Storage; +using Toolkit.Foundation; + +namespace Toolkit.Avalonia; + + +public class FileProvider(ITopLevelProvider topLevelProvider) : + IFileProvider +{ + public async Task> SelectFiles(FileFilter filter) + { + if (topLevelProvider.Get() is TopLevel topLevel) + { + IReadOnlyList storageFiles = await topLevel.StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions() + { + AllowMultiple = filter.AllowMultiple, + FileTypeFilter = new List + { + new(filter.Name) + { + Patterns = filter.Extensions.Select(x => $"*.{x}").ToList() + } + } + }); + + return storageFiles.Select(file => file.Path.LocalPath).ToList(); + } + + return Array.Empty(); + } +} diff --git a/Toolkit.Avalonia/FileSelector.cs b/Toolkit.Avalonia/FileSelector.cs deleted file mode 100644 index 682d849..0000000 --- a/Toolkit.Avalonia/FileSelector.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Avalonia.Controls; -using Toolkit.Foundation; - -namespace Toolkit.Avalonia; - -public class FileSelector : - IFileSelector -{ - public async Task> SelectFiles(FileFilter filter) - { - //TopLevel topLevel = TopLevel.GetTopLevel(control); - - //var openFileDialog = new OpenFileDialog(); - //openFileDialog.Filters.Add(new FileDialogFilter - //{ - // Name = filter.Name, - // Extensions = filter.Extensions - //}); - - //openFileDialog.AllowMultiple = filter.AllowMultiple; - - //var results = await openFileDialog.ShowAsync(window as Window); - - //if (results != null && results.Length > 0) - //{ - // return results.Select(result => result); - //} - return Enumerable.Empty(); - } -} diff --git a/Toolkit.Avalonia/IImageResizer.cs b/Toolkit.Avalonia/IImageResizer.cs new file mode 100644 index 0000000..ad1bc8b --- /dev/null +++ b/Toolkit.Avalonia/IImageResizer.cs @@ -0,0 +1,11 @@ +using Avalonia.Media.Imaging; + +namespace Toolkit.Avalonia; + +public interface IImageResizer +{ + public Bitmap Resize(Stream stream, + int targetWidth, + int targetHeight, + bool maintainAspectRatio); +} \ No newline at end of file diff --git a/Toolkit.Avalonia/IServiceCollectionExtensions.cs b/Toolkit.Avalonia/IServiceCollectionExtensions.cs index b7996f2..5f2ec1a 100644 --- a/Toolkit.Avalonia/IServiceCollectionExtensions.cs +++ b/Toolkit.Avalonia/IServiceCollectionExtensions.cs @@ -124,6 +124,11 @@ public static class IServiceCollectionExtensions public static IServiceCollection AddAvalonia(this IServiceCollection services) { + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); + services.AddTransient(); services.AddTransient(); diff --git a/Toolkit.Avalonia/ITopLevelProvider.cs b/Toolkit.Avalonia/ITopLevelProvider.cs new file mode 100644 index 0000000..5136c8d --- /dev/null +++ b/Toolkit.Avalonia/ITopLevelProvider.cs @@ -0,0 +1,8 @@ +using Avalonia.Controls; + +namespace Toolkit.Avalonia; + +public interface ITopLevelProvider +{ + TopLevel? Get(); +} diff --git a/Toolkit.Avalonia/ImageProvider.cs b/Toolkit.Avalonia/ImageProvider.cs new file mode 100644 index 0000000..21e8998 --- /dev/null +++ b/Toolkit.Avalonia/ImageProvider.cs @@ -0,0 +1,19 @@ +using Avalonia.Media.Imaging; +using Toolkit.Foundation; + +namespace Toolkit.Avalonia; + +public class ImageProvider(IImageResizer imageResizer) : + IImageProvider +{ + public async Task Get(string filePath, + int width, + int height, + bool maintainAspectRatio) + { + await using FileStream stream = File.OpenRead(filePath); + Bitmap resizedImage = imageResizer.Resize(stream, width, height, maintainAspectRatio); + + return new ImageDescriptor(resizedImage, width, height); + } +} diff --git a/Toolkit.Avalonia/ImageResizer.cs b/Toolkit.Avalonia/ImageResizer.cs new file mode 100644 index 0000000..3f62be8 --- /dev/null +++ b/Toolkit.Avalonia/ImageResizer.cs @@ -0,0 +1,53 @@ +using Avalonia.Media.Imaging; +using SkiaSharp; + +namespace Toolkit.Avalonia; + +public class ImageResizer : + IImageResizer +{ + public Bitmap Resize(Stream stream, + int targetWidth, + int targetHeight, + bool maintainAspectRatio) + { + using SKBitmap original = SKBitmap.Decode(stream); + + float widthRatio = (float)targetWidth / original.Width; + float heightRatio = (float)targetHeight / original.Height; + float scale = maintainAspectRatio ? Math.Max(widthRatio, heightRatio) : Math.Min(widthRatio, heightRatio); + + int newWidth = (int)(original.Width * scale); + int newHeight = (int)(original.Height * scale); + + using SKBitmap resized = new(newWidth, newHeight); + using SKCanvas canvas = new(resized); + + canvas.Clear(SKColors.Transparent); + canvas.DrawBitmap(original, new SKRect(0, 0, newWidth, newHeight)); + + SKBitmap cropped; + if (maintainAspectRatio) + { + int cropX = (newWidth - targetWidth) / 2; + int cropY = (newHeight - targetHeight) / 2; + + cropped = new SKBitmap(targetWidth, targetHeight); + using SKCanvas croppedCanvas = new(cropped); + SKRect cropRect = new(cropX, cropY, cropX + targetWidth, cropY + targetHeight); + croppedCanvas.Clear(SKColors.Transparent); + croppedCanvas.DrawBitmap(resized, cropRect, new SKRect(0, 0, targetWidth, targetHeight)); + } + else + { + cropped = resized; + } + + using SKImage image = SKImage.FromBitmap(cropped); + using MemoryStream outputStream = new(); + image.Encode(SKEncodedImageFormat.Png, 100).SaveTo(outputStream); + outputStream.Seek(0, SeekOrigin.Begin); + + return new Bitmap(outputStream); + } +} \ No newline at end of file diff --git a/Toolkit.Avalonia/TopLevelProvider.cs b/Toolkit.Avalonia/TopLevelProvider.cs new file mode 100644 index 0000000..af35ad2 --- /dev/null +++ b/Toolkit.Avalonia/TopLevelProvider.cs @@ -0,0 +1,30 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.ApplicationLifetimes; + +namespace Toolkit.Avalonia; + +public class TopLevelProvider : + ITopLevelProvider +{ + public TopLevel? Get() + { + if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime classicDesktopStyleApplication) + { + if (TopLevel.GetTopLevel(classicDesktopStyleApplication.MainWindow) is TopLevel topLevel) + { + return topLevel; + } + } + + if (Application.Current?.ApplicationLifetime is ISingleViewApplicationLifetime singleViewApplication) + { + if (TopLevel.GetTopLevel(singleViewApplication.MainView) is TopLevel topLevel) + { + return topLevel; + } + } + + return default; + } +} diff --git a/Toolkit.Foundation/Activated.cs b/Toolkit.Foundation/Activated.cs index adfb47e..001d9f6 100644 --- a/Toolkit.Foundation/Activated.cs +++ b/Toolkit.Foundation/Activated.cs @@ -1,4 +1,5 @@ -namespace Toolkit.Foundation; + +namespace Toolkit.Foundation; public record Activated { diff --git a/Toolkit.Foundation/Delete.cs b/Toolkit.Foundation/Delete.cs index 5937231..916dd25 100644 --- a/Toolkit.Foundation/Delete.cs +++ b/Toolkit.Foundation/Delete.cs @@ -7,4 +7,4 @@ public record Delete public static DeleteEventArgs As() where TSender : new() => new(new TSender()); -} \ No newline at end of file +} diff --git a/Toolkit.Foundation/IFileProvider.cs b/Toolkit.Foundation/IFileProvider.cs new file mode 100644 index 0000000..bf29d76 --- /dev/null +++ b/Toolkit.Foundation/IFileProvider.cs @@ -0,0 +1,6 @@ +namespace Toolkit.Foundation; + +public interface IFileProvider +{ + Task> SelectFiles(FileFilter filter); +} \ No newline at end of file diff --git a/Toolkit.Foundation/IFileSelector.cs b/Toolkit.Foundation/IFileSelector.cs deleted file mode 100644 index 5eb8bb8..0000000 --- a/Toolkit.Foundation/IFileSelector.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Toolkit.Foundation; - -public interface IFileSelector -{ - Task> SelectFiles(FileFilter filter); -} diff --git a/Toolkit.Foundation/IHostBuilderExtension.cs b/Toolkit.Foundation/IHostBuilderExtension.cs index 93a20d4..82673cf 100644 --- a/Toolkit.Foundation/IHostBuilderExtension.cs +++ b/Toolkit.Foundation/IHostBuilderExtension.cs @@ -123,7 +123,7 @@ public static class IHostBuilderExtension IFileInfo? fileInfo = null; if (provider.GetService() is IHostEnvironment hostEnvironment) { - IFileProvider fileProvider = hostEnvironment.ContentRootFileProvider; + Microsoft.Extensions.FileProviders.IFileProvider fileProvider = hostEnvironment.ContentRootFileProvider; fileInfo = fileProvider.GetFileInfo(path); } diff --git a/Toolkit.Foundation/IImageDescriptor.cs b/Toolkit.Foundation/IImageDescriptor.cs new file mode 100644 index 0000000..2e29ed5 --- /dev/null +++ b/Toolkit.Foundation/IImageDescriptor.cs @@ -0,0 +1,10 @@ +namespace Toolkit.Foundation; + +public interface IImageDescriptor +{ + public object Image { get; } + + public int Width { get; } + + public int Height { get; } +} diff --git a/Toolkit.Foundation/IImageProvider.cs b/Toolkit.Foundation/IImageProvider.cs new file mode 100644 index 0000000..517780a --- /dev/null +++ b/Toolkit.Foundation/IImageProvider.cs @@ -0,0 +1,9 @@ +namespace Toolkit.Foundation; + +public interface IImageProvider +{ + Task Get(string filePath, + int width, + int height, + bool maintainAspectRatio = false); +} diff --git a/Toolkit.Foundation/ImageDescriptor.cs b/Toolkit.Foundation/ImageDescriptor.cs new file mode 100644 index 0000000..ff8a7ed --- /dev/null +++ b/Toolkit.Foundation/ImageDescriptor.cs @@ -0,0 +1,4 @@ +namespace Toolkit.Foundation; + +public record ImageDescriptor(object Image, int Width, int Height) : + IImageDescriptor; \ No newline at end of file diff --git a/Toolkit.Foundation/RequestEventArgs.cs b/Toolkit.Foundation/RequestEventArgs.cs index c8d8225..7634c78 100644 --- a/Toolkit.Foundation/RequestEventArgs.cs +++ b/Toolkit.Foundation/RequestEventArgs.cs @@ -1,3 +1,3 @@ namespace Toolkit.Foundation; -public record RequestEventArgs(TValue? Value = default); +public record RequestEventArgs(TSender? Sender = default);