diff --git a/src/TraceGui.Base/Model/SvgWriter.cs b/src/TraceGui.Base/Model/SvgWriter.cs index 82d7d84..5d49345 100644 --- a/src/TraceGui.Base/Model/SvgWriter.cs +++ b/src/TraceGui.Base/Model/SvgWriter.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.IO; using System.Text; +using System.Threading.Tasks; using Avalonia.Media; namespace TraceGui.Model; @@ -24,9 +25,10 @@ static class SvgWriter return sb.ToString(); } - public static void Save(int width, int height, IEnumerable paths, string filename, string fillColor = "#000000") + public static async Task Save(int width, int height, IEnumerable paths, Stream stream, string fillColor = "#000000") { var svg = ToSvg(width, height, paths, fillColor); - File.WriteAllText(filename, svg); + await using var writer = new StreamWriter(stream); + await writer.WriteAsync(svg); } -} \ No newline at end of file +} diff --git a/src/TraceGui.Base/ViewModels/MainWindowViewModel.cs b/src/TraceGui.Base/ViewModels/MainWindowViewModel.cs index 8346bb2..bf7cd2b 100644 --- a/src/TraceGui.Base/ViewModels/MainWindowViewModel.cs +++ b/src/TraceGui.Base/ViewModels/MainWindowViewModel.cs @@ -5,10 +5,8 @@ using System.IO; using System.Linq; using System.Threading.Tasks; using System.Windows.Input; -using Avalonia; -using Avalonia.Controls; -using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Media; +using Avalonia.Platform.Storage; using BitmapToVector; using Microsoft.CodeAnalysis.CSharp.Scripting; using Microsoft.CodeAnalysis.Scripting; @@ -136,69 +134,109 @@ public class MainWindowViewModel : ViewModelBase set => this.RaiseAndSetIfChanged(ref _fillColor, value); } + private List GetOpenFileTypes() + { + return new List + { + StorageService.ImageAll, + StorageService.All + }; + } + + private static List GetSaveFileTypes() + { + return new List + { + StorageService.ImageSvg, + StorageService.All + }; + } + private async Task OnOpen() { - var dlg = new OpenFileDialog(); - - dlg.Filters.Add(new FileDialogFilter() + var storageProvider = StorageService.GetStorageProvider(); + if (storageProvider is null) { - Name = "Supported Files (*.png;*.jpg;*.jpeg;*.bmp)", - Extensions = new List {"png", "jpg", "jpeg", "bmp"} + return; + } + + var result = await storageProvider.OpenFilePickerAsync(new FilePickerOpenOptions + { + Title = "Open image", + FileTypeFilter = GetOpenFileTypes(), + AllowMultiple = false }); - dlg.Filters.Add(new FileDialogFilter() - { - Name = "All Files", - Extensions = new List {"*"} - }); + var file = result.FirstOrDefault(); - var result = await dlg.ShowAsync(GetMainWindow()); - if (result is not null && result.Length == 1) + if (file is not null && file.CanOpenRead) { - Load(result[0]); + try + { + await using var stream = await file.OpenReadAsync(); + Load(stream, file.Name); + } + catch (Exception ex) + { + Debug.WriteLine(ex.Message); + Debug.WriteLine(ex.StackTrace); + } } } private async Task OnSave() { - var dlg = new SaveFileDialog(); - - dlg.Filters.Add(new FileDialogFilter() + var storageProvider = StorageService.GetStorageProvider(); + if (storageProvider is null) { - Name = "Svg Files (*.svg)", - Extensions = new List {"svg"} + return; + } + + var file = await storageProvider.SaveFilePickerAsync(new FilePickerSaveOptions + { + Title = "Save drawing", + FileTypeChoices = GetSaveFileTypes(), + SuggestedFileName = Path.GetFileNameWithoutExtension(_fileName), + DefaultExtension = "svg", + ShowOverwritePrompt = true }); - dlg.InitialFileName = Path.GetFileNameWithoutExtension(_fileName); - - var result = await dlg.ShowAsync(GetMainWindow()); - if (result is not null) + if (file is not null && file.CanOpenWrite) { - Save(result); + try + { + await using var stream = await file.OpenWriteAsync(); + await Save(stream); + } + catch (Exception ex) + { + Debug.WriteLine(ex.Message); + Debug.WriteLine(ex.StackTrace); + } } } - private void Save(string filename) + private async Task Save(Stream stream) { if (_paths is not null) { - SvgWriter.Save(Width, _height, _paths, filename, FillColor); + await SvgWriter.Save(Width, _height, _paths, stream, FillColor); } } - public void Load(string filename) + public void Load(Stream stream, string filename) { - Decode(filename); + Decode(stream); Trace(); FileName = filename; } - private void Decode(string filename) + private void Decode(Stream stream) { _source?.Dispose(); - _source = SixLabors.ImageSharp.Image.Load(filename); + _source = SixLabors.ImageSharp.Image.Load(stream); } private async void Trace() @@ -219,7 +257,7 @@ public class MainWindowViewModel : ViewModelBase }; static bool DefaultFilter(Rgba32 c) => c.R < 128; - Func filter = DefaultFilter; + var filter = DefaultFilter; try { @@ -245,9 +283,4 @@ public class MainWindowViewModel : ViewModelBase var compiledFilter = await CSharpScript.EvaluateAsync>(code, options); return compiledFilter; } - - private Window? GetMainWindow() - { - return (Application.Current.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime)?.MainWindow; - } } diff --git a/src/TraceGui.Base/ViewModels/StorageService.cs b/src/TraceGui.Base/ViewModels/StorageService.cs new file mode 100644 index 0000000..5509a06 --- /dev/null +++ b/src/TraceGui.Base/ViewModels/StorageService.cs @@ -0,0 +1,63 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Platform.Storage; +using Avalonia.VisualTree; + +namespace TraceGui.ViewModels; + +internal static class StorageService +{ + public static FilePickerFileType All { get; } = new("All") + { + Patterns = new[] { "*.*" }, + MimeTypes = new[] { "*/*" } + }; + + public static FilePickerFileType ImageSvg { get; } = new("Svg") + { + Patterns = new[] { "*.svg" }, + AppleUniformTypeIdentifiers = new[] { "public.svg-image" }, + MimeTypes = new[] { "image/svg+xml" } + }; + + public static FilePickerFileType ImagePng { get; } = new("PNG image") + { + Patterns = new[] { "*.png" }, + AppleUniformTypeIdentifiers = new[] { "public.png" }, + MimeTypes = new[] { "image/png" } + }; + + public static FilePickerFileType ImageJpg { get; } = new("JPEG image") + { + Patterns = new[] { "*.jpg", "*.jpeg" }, + AppleUniformTypeIdentifiers = new[] { "public.jpeg" }, + MimeTypes = new[] { "image/jpeg" } + }; + + public static FilePickerFileType ImageAll { get; } = new("All Images") + { + Patterns = new[] { "*.png", "*.jpg", "*.jpeg", "*.bmp" }, + AppleUniformTypeIdentifiers = new[] { "public.image" }, + MimeTypes = new[] { "image/*" } + }; + + public static IStorageProvider? GetStorageProvider() + { + if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime { MainWindow: { } window }) + { + return window.StorageProvider; + } + + if (Application.Current?.ApplicationLifetime is ISingleViewApplicationLifetime { MainView: { } mainView }) + { + var visualRoot = mainView.GetVisualRoot(); + if (visualRoot is TopLevel topLevel) + { + return topLevel.StorageProvider; + } + } + + return null; + } +} diff --git a/src/TraceGui.Base/Views/MainView.axaml.cs b/src/TraceGui.Base/Views/MainView.axaml.cs index c346678..ddfac68 100644 --- a/src/TraceGui.Base/Views/MainView.axaml.cs +++ b/src/TraceGui.Base/Views/MainView.axaml.cs @@ -1,4 +1,5 @@ -using System.Linq; +using System.IO; +using System.Linq; using Avalonia.Controls; using Avalonia.Input; using Avalonia.Markup.Xaml; @@ -39,7 +40,8 @@ public class MainView : UserControl { if (DataContext is MainWindowViewModel vm) { - vm.Load(fileName); + using var stream = File.OpenRead(fileName); + vm.Load(stream, fileName); } } }