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