diff --git a/CHANGELOG.md b/CHANGELOG.md index ae40cd5..ac1490d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## ??/??/2020 - v0.9.0.0 + +* (Add) Multi-OS with Linux support +* (Add) Themes support +* (Add) Fullscreen support (F11) +* (Change) GUI was rewritten from Windows Forms to WPF Avalonia, C# +* (Improvement) More detailed descriptions on error messages +* (Improvement) GUI is now scalable +* (Fix) Many bug found and fixed during convertion + ## ??/09/2020 - v0.8.2.5 * (Fix) Extract: Use trail zeros to layer filenames diff --git a/UVtools.Core/Operations/OperationChangeResolution.cs b/UVtools.Core/Operations/OperationChangeResolution.cs index b1af86f..5f6b0e9 100644 --- a/UVtools.Core/Operations/OperationChangeResolution.cs +++ b/UVtools.Core/Operations/OperationChangeResolution.cs @@ -5,6 +5,8 @@ * Everyone is permitted to copy and distribute verbatim copies * of this license document, but changing it is not allowed. */ + +using System; using System.Drawing; using System.Text; using UVtools.Core.Objects; @@ -13,12 +15,16 @@ namespace UVtools.Core.Operations { public sealed class OperationChangeResolution : Operation { + private uint _newResolutionX; + private uint _newResolutionY; + #region Subclasses public class Resolution { public uint ResolutionX { get; } public uint ResolutionY { get; } public string Name { get; } + public bool IsEmpty => ResolutionX == 0 && ResolutionY == 0; public Resolution(uint resolutionX, uint resolutionY, string name = null) { @@ -29,6 +35,7 @@ namespace UVtools.Core.Operations public override string ToString() { + if(IsEmpty) return string.Empty; var str = $"{ResolutionX} x {ResolutionY}"; if (!string.IsNullOrEmpty(Name)) { @@ -81,10 +88,22 @@ namespace UVtools.Core.Operations #region Properties public Size OldResolution { get; } - public uint NewResolutionX { get; set; } - public uint NewResolutionY { get; set; } + public uint NewResolutionX + { + get => _newResolutionX; + set => SetProperty(ref _newResolutionX, value); + } + + public uint NewResolutionY + { + get => _newResolutionY; + set => SetProperty(ref _newResolutionY, value); + } + public Rectangle VolumeBonds { get; } + public Size VolumeBondsSize => VolumeBonds.Size; + #endregion #region Constructor @@ -97,6 +116,8 @@ namespace UVtools.Core.Operations { OldResolution = oldResolution; VolumeBonds = volumeBonds; + NewResolutionX = (uint) oldResolution.Width; + NewResolutionY = (uint) oldResolution.Height; } #endregion @@ -104,6 +125,7 @@ namespace UVtools.Core.Operations public static Resolution[] GetResolutions() { return new [] { + //new Resolution(0, 0, string.Empty), new Resolution(854, 480, "FWVGA"), new Resolution(960, 1708), new Resolution(1080, 1920, "FHD"), @@ -120,6 +142,9 @@ namespace UVtools.Core.Operations new Resolution(3840, 2400, "WQUXGA"), }; } + + public static Resolution[] Presets => GetResolutions(); + #endregion } diff --git a/UVtools.Core/Operations/OperationRotate.cs b/UVtools.Core/Operations/OperationRotate.cs index e8f5b60..c52bacf 100644 --- a/UVtools.Core/Operations/OperationRotate.cs +++ b/UVtools.Core/Operations/OperationRotate.cs @@ -12,6 +12,7 @@ namespace UVtools.Core.Operations { public class OperationRotate : Operation { + private decimal _angleDegrees = 90; public override string Title => "Rotate"; public override string Description => "Rotate the layers of the model.\n"; @@ -24,6 +25,10 @@ namespace UVtools.Core.Operations public override string ProgressAction => "Rotated layers"; - public decimal AngleDegrees { get; set; } + public decimal AngleDegrees + { + get => _angleDegrees; + set => SetProperty(ref _angleDegrees, value); + } } } diff --git a/UVtools.Core/Operations/OperationThreshold.cs b/UVtools.Core/Operations/OperationThreshold.cs index befdb46..183bc00 100644 --- a/UVtools.Core/Operations/OperationThreshold.cs +++ b/UVtools.Core/Operations/OperationThreshold.cs @@ -6,12 +6,17 @@ * of this license document, but changing it is not allowed. */ +using System; using Emgu.CV.CvEnum; namespace UVtools.Core.Operations { public class OperationThreshold : Operation { + private byte _threshold = 127; + private byte _maximum = 255; + private ThresholdType _type = ThresholdType.Binary; + public override string Title => "Threshold pixels"; public override string Description => "Manipulate pixel values based on a threshold.\n\n" + @@ -26,12 +31,25 @@ namespace UVtools.Core.Operations $"Applying threshold {Threshold} with max {Maximum} from layers {LayerIndexStart} through {LayerIndexEnd}"; public override string ProgressAction => "Thresholded layers"; - - public byte Threshold { get; set; } - public byte Maximum { get; set; } - public ThresholdType Type { get; set; } + public byte Threshold + { + get => _threshold; + set => SetProperty(ref _threshold, value); + } + public byte Maximum + { + get => _maximum; + set => SetProperty(ref _maximum, value); + } + public ThresholdType Type + { + get => _type; + set => SetProperty(ref _type, value); + } + + public static Array ThresholdTypes => Enum.GetValues(typeof(ThresholdType)); } } diff --git a/UVtools.WPF/App.axaml.cs b/UVtools.WPF/App.axaml.cs index 96faff3..4c01abb 100644 --- a/UVtools.WPF/App.axaml.cs +++ b/UVtools.WPF/App.axaml.cs @@ -18,7 +18,9 @@ using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; using Avalonia.Platform; using Avalonia.ThemeManager; +using Emgu.CV; using UVtools.Core.FileFormats; +using UVtools.WPF.Extensions; namespace UVtools.WPF { @@ -33,7 +35,7 @@ namespace UVtools.WPF AvaloniaXamlLoader.Load(this); } - public override void OnFrameworkInitializationCompleted() + public override async void OnFrameworkInitializationCompleted() { if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { @@ -43,10 +45,24 @@ namespace UVtools.WPF ThemeSelector = Avalonia.ThemeManager.ThemeSelector.Create("Assets/Themes"); ThemeSelector.LoadSelectedTheme("Assets/selected.theme"); - desktop.MainWindow = MainWindow = new MainWindow + MainWindow = new MainWindow(); + + try { - //DataContext = Selector - }; + CvInvoke.CheckLibraryLoaded(); + } + catch (Exception e) + { + await MainWindow.MessageBoxError("UVtools can not run due lack of dependencies from cvextern/OpenCV\n" + + "Please build or install this dependencies in order to run UVtools\n" + + "Check manual or page at 'Requirements' section for help\n\n" + + "Additional information:\n" + + $"{e}", "UVtools can not run"); + return; + } + + + desktop.MainWindow = MainWindow; desktop.Exit += (sender, e) => ThemeSelector.SaveSelectedTheme("Assets/selected.theme"); } @@ -59,11 +75,18 @@ namespace UVtools.WPF { try { - var info = new ProcessStartInfo("UVtools.exe", $"\"{filePath}\"") + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - UseShellExecute = true - }; - Process.Start(info)?.Dispose(); + var info = new ProcessStartInfo("UVtools.exe", $"\"{filePath}\"") + { + UseShellExecute = true + }; + Process.Start(info).Dispose(); + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + Process.Start("dotnet", $"UVtools.dll \"{filePath}\"").Dispose(); + } } catch (Exception e) { @@ -77,15 +100,15 @@ namespace UVtools.WPF { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - Process.Start(new ProcessStartInfo(url) { UseShellExecute = true }); + Process.Start(new ProcessStartInfo(url) { UseShellExecute = true }).Dispose(); } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { - Process.Start("xdg-open", url); + Process.Start("xdg-open", url).Dispose(); } else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { - Process.Start("open", url); + Process.Start("open", url).Dispose(); } else { diff --git a/UVtools.WPF/Controls/Tools/ToolChangeResolutionControl.axaml b/UVtools.WPF/Controls/Tools/ToolChangeResolutionControl.axaml new file mode 100644 index 0000000..598fe9c --- /dev/null +++ b/UVtools.WPF/Controls/Tools/ToolChangeResolutionControl.axaml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + diff --git a/UVtools.WPF/Controls/Tools/ToolChangeResolutionControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolChangeResolutionControl.axaml.cs new file mode 100644 index 0000000..97260e8 --- /dev/null +++ b/UVtools.WPF/Controls/Tools/ToolChangeResolutionControl.axaml.cs @@ -0,0 +1,43 @@ +using System.Timers; +using Avalonia.Markup.Xaml; +using UVtools.Core.Operations; + +namespace UVtools.WPF.Controls.Tools +{ + public class ToolChangeResolutionControl : ToolControl + { + private OperationChangeResolution.Resolution _selectedPresetItem; + public OperationChangeResolution Operation { get; } + + public OperationChangeResolution.Resolution SelectedPresetItem + { + get => _selectedPresetItem; + set + { + SetProperty(ref _selectedPresetItem, value); + if (_selectedPresetItem is null || _selectedPresetItem.IsEmpty) return; + Operation.NewResolutionX = _selectedPresetItem.ResolutionX; + Operation.NewResolutionY = _selectedPresetItem.ResolutionY; + Timer timer = new Timer(50); + timer.Elapsed += (sender, args) => + { + SelectedPresetItem = null; + timer.Dispose(); + }; + timer.Start(); + } + } + + public ToolChangeResolutionControl() + { + InitializeComponent(); + BaseOperation = Operation = new OperationChangeResolution(App.SlicerFile.Resolution, App.SlicerFile.LayerManager.BoundingRectangle); + DataContext = this; + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } + } +} diff --git a/UVtools.WPF/Controls/Tools/ToolRotateControl.axaml b/UVtools.WPF/Controls/Tools/ToolRotateControl.axaml new file mode 100644 index 0000000..cf5d180 --- /dev/null +++ b/UVtools.WPF/Controls/Tools/ToolRotateControl.axaml @@ -0,0 +1,21 @@ + + + + + + + + diff --git a/UVtools.WPF/Controls/Tools/ToolRotateControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolRotateControl.axaml.cs new file mode 100644 index 0000000..4e6e89d --- /dev/null +++ b/UVtools.WPF/Controls/Tools/ToolRotateControl.axaml.cs @@ -0,0 +1,22 @@ +using Avalonia.Markup.Xaml; +using UVtools.Core.Operations; + +namespace UVtools.WPF.Controls.Tools +{ + public class ToolRotateControl : ToolControl + { + public OperationRotate Operation { get; } + + public ToolRotateControl() + { + InitializeComponent(); + BaseOperation = Operation = new OperationRotate(); + DataContext = this; + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } + } +} diff --git a/UVtools.WPF/Controls/Tools/ToolThresholdControl.axaml b/UVtools.WPF/Controls/Tools/ToolThresholdControl.axaml new file mode 100644 index 0000000..5e42a42 --- /dev/null +++ b/UVtools.WPF/Controls/Tools/ToolThresholdControl.axaml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/UVtools.WPF/Controls/Tools/ToolThresholdControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolThresholdControl.axaml.cs new file mode 100644 index 0000000..6776c84 --- /dev/null +++ b/UVtools.WPF/Controls/Tools/ToolThresholdControl.axaml.cs @@ -0,0 +1,86 @@ +using Avalonia.Markup.Xaml; +using Emgu.CV.CvEnum; +using UVtools.Core.Operations; + +namespace UVtools.WPF.Controls.Tools +{ + public class ToolThresholdControl : ToolControl + { + private int _selectedPresetIndex; + private bool _isThresholdEnabled = true; + private bool _isMaximumEnabled = true; + private bool _isTypeEnabled = true; + public OperationThreshold Operation { get; } + + public string[] Presets => new[] + { + "Free use", + "Strip AntiAliasing", + "Set pixel brightness" + }; + + public bool IsThresholdEnabled + { + get => _isThresholdEnabled; + set => SetProperty(ref _isThresholdEnabled, value); + } + + public bool IsMaximumEnabled + { + get => _isMaximumEnabled; + set => SetProperty(ref _isMaximumEnabled, value); + } + + public bool IsTypeEnabled + { + get => _isTypeEnabled; + set => SetProperty(ref _isTypeEnabled, value); + } + + public int SelectedPresetIndex + { + get => _selectedPresetIndex; + set + { + if (!SetProperty(ref _selectedPresetIndex, value)) return; + + switch (_selectedPresetIndex) + { + case 0: + IsThresholdEnabled = true; + IsMaximumEnabled = true; + IsTypeEnabled = true; + break; + case 1: + IsThresholdEnabled = true; + IsMaximumEnabled = false; + IsTypeEnabled = false; + Operation.Threshold = 127; + Operation.Maximum = 255; + Operation.Type = ThresholdType.Binary; + break; + case 2: + IsThresholdEnabled = false; + IsMaximumEnabled = true; + IsTypeEnabled = false; + Operation.Threshold = 254; + //Operation.Maximum = 254; + Operation.Type = ThresholdType.Binary; + break; + } + } + } + + public ToolThresholdControl() + { + InitializeComponent(); + BaseOperation = Operation = new OperationThreshold(); + DataContext = this; + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } + } +} diff --git a/UVtools.WPF/Extensions/WindowExtensions.cs b/UVtools.WPF/Extensions/WindowExtensions.cs index 55ecdf8..1e1f157 100644 --- a/UVtools.WPF/Extensions/WindowExtensions.cs +++ b/UVtools.WPF/Extensions/WindowExtensions.cs @@ -7,6 +7,7 @@ */ using System.Threading.Tasks; using Avalonia.Controls; +using Avalonia.Threading; using MessageBox.Avalonia.DTO; using MessageBox.Avalonia.Enums; @@ -28,7 +29,7 @@ namespace UVtools.WPF.Extensions WindowStartupLocation = location, CanResize = false }); - + return await messageBoxStandardWindow.ShowDialog(window); } diff --git a/UVtools.WPF/MainWindow.axaml.cs b/UVtools.WPF/MainWindow.axaml.cs index 3e2f590..af4f9b5 100644 --- a/UVtools.WPF/MainWindow.axaml.cs +++ b/UVtools.WPF/MainWindow.axaml.cs @@ -277,11 +277,22 @@ namespace UVtools.WPF //if (ProgressWindow is null) return; LastStopWatch = ProgressWindow.StopWatch; - Dispatcher.UIThread.InvokeAsync(() => + ProgressWindow.Close(); + ProgressWindow.Dispose(); + /*if (Dispatcher.UIThread.CheckAccess()) { ProgressWindow.Close(); ProgressWindow.Dispose(); - }); + } + else + { + Dispatcher.UIThread.InvokeAsync(() => + { + ProgressWindow.Close(); + ProgressWindow.Dispose(); + }); + }*/ + } @@ -456,7 +467,7 @@ namespace UVtools.WPF } catch (Exception e) { - await this.MessageBoxError(e.Message, "Error occur while save properties"); + await this.MessageBoxError(e.ToString(), "Error occur while save properties"); return; } @@ -527,7 +538,7 @@ namespace UVtools.WPF } catch (Exception e) { - await this.MessageBoxError(e.Message, "Error occur while save gcode"); + await this.MessageBoxError(e.ToString(), "Error occur while save gcode"); return; } @@ -599,8 +610,9 @@ namespace UVtools.WPF var progress = ProgressWindow.RestartProgress(false); progress.Reset("Removing selected issues", (uint)processIssues.Count); - var task = Task.Factory.StartNew(() => + var task = await Task.Factory.StartNew(() => { + ShowProgressWindow(); bool result = false; try { @@ -657,19 +669,16 @@ namespace UVtools.WPF } catch (Exception ex) { - this.MessageBoxError(ex.Message, "Removal failed"); - } - finally - { - IsGUIEnabled = true; + Dispatcher.UIThread.InvokeAsync(async () => + await this.MessageBoxError(ex.ToString(), "Removal failed")); } return result; }); - await ProgressWindow.ShowDialog(this); + IsGUIEnabled = true; - if (!task.Result) return; + if (!task) return; var whiteListLayers = new List(); @@ -740,8 +749,9 @@ namespace UVtools.WPF } Issues.RemoveMany(toRemove); - var result = Task>.Factory.StartNew(() => + var resultIssues = await Task.Factory.StartNew(() => { + ShowProgressWindow(); try { var issues = SlicerFile.LayerManager.GetAllIssues(islandConfig, resinTrapConfig, @@ -758,21 +768,18 @@ namespace UVtools.WPF } catch (Exception ex) { - this.MessageBoxError(ex.Message, "Error while trying to compute issues"); - } - finally - { - IsGUIEnabled = true; + Dispatcher.UIThread.InvokeAsync(async () => + await this.MessageBoxError(ex.ToString(), "Error while trying to compute issues")); } return null; }); - - await ProgressWindow.ShowDialog(this); - - if (result.Result is null || result.Result.Count == 0) return; - Issues.AddRange(result.Result); + IsGUIEnabled = true; + + if (resultIssues is null || resultIssues.Count == 0) return; + + Issues.AddRange(resultIssues); Issues = new ObservableCollection(Issues.OrderBy(issue => issue.Type) .ThenBy(issue => issue.LayerIndex) .ThenBy(issue => issue.PixelsCount).ToList()); @@ -871,8 +878,9 @@ namespace UVtools.WPF ProgressWindow.SetTitle("Computing Issues"); - var task = Task>.Factory.StartNew(() => + var resultIssues = await Task.Factory.StartNew(() => { + ShowProgressWindow(); try { var issues = SlicerFile.LayerManager.GetAllIssues(islandConfig, resinTrapConfig, touchingBoundConfig, @@ -885,19 +893,17 @@ namespace UVtools.WPF } catch (Exception ex) { - this.MessageBoxError(ex.Message, "Error while trying compute issues"); - } - finally - { - IsGUIEnabled = true; + Dispatcher.UIThread.InvokeAsync(async () => + await this.MessageBoxError(ex.ToString(), "Error while trying compute issues")); } return null; }); - await ProgressWindow.ShowDialog(this); - if (task.Result is null) return; - Issues.AddRange(task.Result); + IsGUIEnabled = true; + + if (resultIssues is null) return; + Issues.AddRange(resultIssues); OnPropertyChanged(nameof(IssueSelectedIndexStr)); OnPropertyChanged(nameof(IssueCanGoPrevious)); @@ -1406,8 +1412,10 @@ namespace UVtools.WPF ProgressWindow.SetTitle($"Opening: {fileNameOnly}"); IsGUIEnabled = false; - var task = Task.Factory.StartNew(() => + + var task = await Task.Factory.StartNew( () => { + ShowProgressWindow(); try { SlicerFile.Decode(fileName, ProgressWindow.RestartProgress()); @@ -1418,18 +1426,16 @@ namespace UVtools.WPF } catch (Exception exception) { - this.MessageBoxError(exception.ToString(), "Error opening the file"); - } - finally - { - IsGUIEnabled = true; + Dispatcher.UIThread.InvokeAsync(async () => + await this.MessageBoxError(exception.ToString(), "Error opening the file")); } return false; }); - await ProgressWindow.ShowDialog(this); - if (!task.Result) + IsGUIEnabled = true; + + if (!task) { SlicerFile.Dispose(); App.SlicerFile = null; @@ -1479,23 +1485,40 @@ namespace UVtools.WPF VisibleThumbnailIndex = 1; RefreshProperties(); - + UpdateTitle(); - + if (!(mat is null) && Settings.LayerPreview.AutoRotateLayerBestView) { - _showLayerImageRotated = mat.Height > mat.Width; + _showLayerImageRotated = mat.Height > mat.Width; } ResetDataContext(); ForceUpdateActualLayer(actualLayer.Clamp(actualLayer, SliderMaximumValue)); - + if (Settings.LayerPreview.ZoomToFitPrintVolumeBounds) { ZoomToFit(); } + + + } + + private async void ShowProgressWindow() + { + if (Dispatcher.UIThread.CheckAccess()) + { + await ProgressWindow.ShowDialog(this); + } + else + { + await Dispatcher.UIThread.InvokeAsync(async () => + { + await ProgressWindow.ShowDialog(this); + }); + } } private async void ConvertToOnTapped(object? sender, RoutedEventArgs e) @@ -1520,8 +1543,9 @@ namespace UVtools.WPF ProgressWindow.SetTitle( $"Converting {Path.GetFileName(SlicerFile.FileFullPath)} to {Path.GetExtension(result)}"); - Task task = Task.Factory.StartNew(() => + var task = await Task.Factory.StartNew(() => { + ShowProgressWindow(); try { return SlicerFile.Convert(fileFormat, result, ProgressWindow.RestartProgress()); @@ -1538,19 +1562,16 @@ namespace UVtools.WPF "Go to \"Help\" -> \"Install profiles into PrusaSlicer\" to install printers.\n"; } - this.MessageBoxError($"Convertion was not successful! Maybe not implemented...\n{extraMessage}{ex.Message}", "Convertion unsuccessful"); - } - finally - { - IsGUIEnabled = true; + Dispatcher.UIThread.InvokeAsync(async () => + await this.MessageBoxError($"Convertion was not successful! Maybe not implemented...\n{extraMessage}{ex}", "Convertion unsuccessful")); } return false; }); - await ProgressWindow.ShowDialog(this); - - if (task.Result) + IsGUIEnabled = true; + + if (task) { if (await this.MessageBoxQuestion( $"Conversion completed in {LastStopWatch.ElapsedMilliseconds / 1000}s\n\n" + @@ -2027,9 +2048,9 @@ namespace UVtools.WPF IsGUIEnabled = false; ProgressWindow.SetTitle($"Saving {Path.GetFileName(filepath)}"); - var task = Task.Factory.StartNew( () => + var task = await Task.Factory.StartNew( () => { - bool result = false; + ShowProgressWindow(); try { @@ -2040,7 +2061,7 @@ namespace UVtools.WPF } File.Move(tempFile, filepath); SlicerFile.FileFullPath = filepath; - result = true; + return true; } catch (OperationCanceledException) { @@ -2052,26 +2073,23 @@ namespace UVtools.WPF } catch (Exception ex) { - this.MessageBoxError(ex.Message, "Error while saving the file"); - } - finally - { - IsGUIEnabled = true; + Dispatcher.UIThread.InvokeAsync(async () => + await this.MessageBoxError(ex.ToString(), "Error while saving the file")); } - return result; + return false; }); - await ProgressWindow.ShowDialog(this); + IsGUIEnabled = true; - if (task.Result) + if (task) { SavesCount++; CanSave = false; UpdateTitle(); } - return task.Result; + return task; } public async void ExtractFile() @@ -2092,41 +2110,37 @@ namespace UVtools.WPF string finalPath = Path.Combine(result, fileNameNoExt); - try + IsGUIEnabled = false; + ProgressWindow.SetTitle($"Extracting {Path.GetFileName(SlicerFile.FileFullPath)}"); + + await Task.Factory.StartNew(() => { - IsGUIEnabled = false; - ProgressWindow.SetTitle($"Extracting {Path.GetFileName(SlicerFile.FileFullPath)}"); - - var task = Task.Factory.StartNew(() => + ShowProgressWindow(); + try { - try - { - SlicerFile.Extract(finalPath, true, true, ProgressWindow.RestartProgress()); - } - catch (OperationCanceledException) - { - - } - finally - { - IsGUIEnabled = true; - } - }); - - await ProgressWindow.ShowDialog(this); - - - if (await this.MessageBoxQuestion( - $"Extraction to {finalPath} completed in ({LastStopWatch.ElapsedMilliseconds / 1000}s)\n\n" + - "'Yes' to open target folder, 'No' to continue.", - "Extraction complete") == ButtonResult.Yes) - { - App.StartProcess(finalPath); + SlicerFile.Extract(finalPath, true, true, ProgressWindow.RestartProgress()); } - } - catch (Exception ex) + catch (OperationCanceledException) + { + + } + catch (Exception ex) + { + Dispatcher.UIThread.InvokeAsync(async () => + await this.MessageBoxError(ex.ToString(), "Error while try extracting the file")); + } + }); + + + IsGUIEnabled = true; + + + if (await this.MessageBoxQuestion( + $"Extraction to {finalPath} completed in ({LastStopWatch.ElapsedMilliseconds / 1000}s)\n\n" + + "'Yes' to open target folder, 'No' to continue.", + "Extraction complete") == ButtonResult.Yes) { - await this.MessageBoxError(ex.ToString(), "Error while try extracting the file"); + App.StartProcess(finalPath); } } @@ -2422,8 +2436,10 @@ namespace UVtools.WPF ProgressWindow.SetTitle(baseOperation.ProgressTitle); - var task = Task.Factory.StartNew(async () => + await Task.Factory.StartNew(() => { + ShowProgressWindow(); + var backup = new Layer[baseOperation.LayerRangeCount]; uint i = 0; for (uint layerIndex = baseOperation.LayerIndexStart; layerIndex <= baseOperation.LayerIndexEnd; layerIndex++) @@ -2504,15 +2520,12 @@ namespace UVtools.WPF } catch (Exception ex) { - await this.MessageBoxError(ex.Message, $"{baseOperation.Title} Error"); - } - finally - { - IsGUIEnabled = true; + Dispatcher.UIThread.InvokeAsync(async () => + await this.MessageBoxError(ex.ToString(), $"{baseOperation.Title} Error")); } }); - await ProgressWindow.ShowDialog(this); + IsGUIEnabled = true; ShowLayer(); RefreshProperties(); diff --git a/UVtools.WPF/Program.cs b/UVtools.WPF/Program.cs index b3f91bb..2b856b3 100644 --- a/UVtools.WPF/Program.cs +++ b/UVtools.WPF/Program.cs @@ -13,6 +13,7 @@ namespace UVtools.WPF public static void Main(string[] args) { Args = args; + BuildAvaloniaApp() .StartWithClassicDesktopLifetime(args); } @@ -21,10 +22,11 @@ namespace UVtools.WPF public static AppBuilder BuildAvaloniaApp() => AppBuilder.Configure() .UsePlatformDetect() - .With(new Win32PlatformOptions - { - AllowEglInitialization = true, - }) + .With(new Win32PlatformOptions { AllowEglInitialization = true}) + .With(new X11PlatformOptions { UseGpu = true, UseEGL = true }) + .With(new MacOSPlatformOptions { ShowInDock = true }) + .With(new AvaloniaNativePlatformOptions { UseGpu = true }) + .UseSkia() .LogToDebug(); } } diff --git a/UVtools.WPF/UVtools.WPF.csproj b/UVtools.WPF/UVtools.WPF.csproj index 66160ec..aec357e 100644 --- a/UVtools.WPF/UVtools.WPF.csproj +++ b/UVtools.WPF/UVtools.WPF.csproj @@ -177,6 +177,9 @@ PreserveNewest + + PreserveNewest + MSBuild:Compile @@ -192,6 +195,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest diff --git a/UVtools.WPF/UVtools.sh b/UVtools.WPF/UVtools.sh new file mode 100644 index 0000000..96f85f3 --- /dev/null +++ b/UVtools.WPF/UVtools.sh @@ -0,0 +1,2 @@ +#!/bin/bash +dotnet UVtools.dll \ No newline at end of file diff --git a/UVtools.WPF/libcvextern.so b/UVtools.WPF/libcvextern.so new file mode 100644 index 0000000..a75ef49 Binary files /dev/null and b/UVtools.WPF/libcvextern.so differ