diff --git a/UVtools.Core/Extensions/EmguExtensions.cs b/UVtools.Core/Extensions/EmguExtensions.cs
index 7e3c16e..4cbb507 100644
--- a/UVtools.Core/Extensions/EmguExtensions.cs
+++ b/UVtools.Core/Extensions/EmguExtensions.cs
@@ -16,6 +16,11 @@ namespace UVtools.Core.Extensions
{
public static class EmguExtensions
{
+ public static unsafe byte* GetBytePointer(this Mat mat)
+ {
+ return (byte*)mat.DataPointer.ToPointer();
+ }
+
///
/// Gets a single pixel span to manipulate or read pixels
///
diff --git a/UVtools.Core/FileFormats/CWSFile.cs b/UVtools.Core/FileFormats/CWSFile.cs
index 36a7b08..80a5e03 100644
--- a/UVtools.Core/FileFormats/CWSFile.cs
+++ b/UVtools.Core/FileFormats/CWSFile.cs
@@ -333,6 +333,7 @@ namespace UVtools.Core.FileFormats
public override void Decode(string fileFullPath, OperationProgress progress = null)
{
base.Decode(fileFullPath, progress);
+ if(progress is null) progress = new OperationProgress(OperationProgress.StatusGatherLayers, LayerCount);
FileFullPath = fileFullPath;
using (var inputFile = ZipFile.Open(FileFullPath, ZipArchiveMode.Read))
diff --git a/UVtools.Core/Operations/OperationProgress.cs b/UVtools.Core/Operations/OperationProgress.cs
index c1d6501..f34d339 100644
--- a/UVtools.Core/Operations/OperationProgress.cs
+++ b/UVtools.Core/Operations/OperationProgress.cs
@@ -35,10 +35,16 @@ namespace UVtools.Core.Operations
private bool _canCancel = true;
- public OperationProgress()
+ public
+ OperationProgress()
{
}
+ public OperationProgress(string name, uint value = 0)
+ {
+ Reset(name, value);
+ }
+
public OperationProgress(bool canCancel)
{
_canCancel = canCancel;
diff --git a/UVtools.WPF/App.axaml.cs b/UVtools.WPF/App.axaml.cs
index 7e288f9..9e0b688 100644
--- a/UVtools.WPF/App.axaml.cs
+++ b/UVtools.WPF/App.axaml.cs
@@ -6,6 +6,8 @@
* of this license document, but changing it is not allowed.
*/
+using System.Globalization;
+using System.Threading;
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
@@ -29,6 +31,7 @@ namespace UVtools.WPF
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
+ Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("en-GB");
UserSettings.Load();
UserSettings.SetVersion();
diff --git a/UVtools.WPF/Controls/SliderEx.cs b/UVtools.WPF/Controls/SliderEx.cs
new file mode 100644
index 0000000..99ceb22
--- /dev/null
+++ b/UVtools.WPF/Controls/SliderEx.cs
@@ -0,0 +1,284 @@
+using System;
+using System.Diagnostics;
+using Avalonia;
+using Avalonia.Collections;
+using Avalonia.Controls;
+using Avalonia.Controls.Mixins;
+using Avalonia.Controls.Primitives;
+using Avalonia.Input;
+using Avalonia.Interactivity;
+using Avalonia.Layout;
+using Avalonia.Styling;
+using Avalonia.Utilities;
+
+namespace UVtools.WPF.Controls
+{
+ public class SliderEx : RangeBase, IStyleable
+ {
+ Type IStyleable.StyleKey => typeof(Slider);
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty OrientationProperty =
+ ScrollBar.OrientationProperty.AddOwner();
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty IsSnapToTickEnabledProperty =
+ AvaloniaProperty.Register(nameof(IsSnapToTickEnabled), false);
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty TickFrequencyProperty =
+ AvaloniaProperty.Register(nameof(TickFrequency), 0.0);
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty TickPlacementProperty =
+ AvaloniaProperty.Register(nameof(TickPlacement), 0d);
+
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty> TicksProperty =
+ TickBar.TicksProperty.AddOwner();
+
+ // Slider required parts
+ private bool _isDragging = false;
+ private Track _track;
+ private Button _decreaseButton;
+ private Button _increaseButton;
+ private IDisposable _decreaseButtonPressDispose;
+ private IDisposable _decreaseButtonReleaseDispose;
+ private IDisposable _increaseButtonSubscription;
+ private IDisposable _increaseButtonReleaseDispose;
+ private IDisposable _pointerMovedDispose;
+
+ public Track Track => _track;
+
+ ///
+ /// Initializes static members of the class.
+ ///
+ static SliderEx()
+ {
+ PressedMixin.Attach();
+ OrientationProperty.OverrideDefaultValue(typeof(Slider), Orientation.Horizontal);
+ Thumb.DragStartedEvent.AddClassHandler((x, e) => x.OnThumbDragStarted(e), RoutingStrategies.Bubble);
+ Thumb.DragCompletedEvent.AddClassHandler((x, e) => x.OnThumbDragCompleted(e),
+ RoutingStrategies.Bubble);
+ }
+
+ ///
+ /// Instantiates a new instance of the class.
+ ///
+ public SliderEx()
+ {
+ UpdatePseudoClasses(Orientation);
+ }
+
+ ///
+ /// Defines the ticks to be drawn on the tick bar.
+ ///
+ public AvaloniaList Ticks
+ {
+ get => GetValue(TicksProperty);
+ set => SetValue(TicksProperty, value);
+ }
+
+ ///
+ /// Gets or sets the orientation of a .
+ ///
+ public Orientation Orientation
+ {
+ get => GetValue(OrientationProperty);
+ set => SetValue(OrientationProperty, value);
+ }
+
+ ///
+ /// Gets or sets a value that indicates whether the automatically moves the to the closest tick mark.
+ ///
+ public bool IsSnapToTickEnabled
+ {
+ get => GetValue(IsSnapToTickEnabledProperty);
+ set => SetValue(IsSnapToTickEnabledProperty, value);
+ }
+
+ ///
+ /// Gets or sets the interval between tick marks.
+ ///
+ public double TickFrequency
+ {
+ get => GetValue(TickFrequencyProperty);
+ set => SetValue(TickFrequencyProperty, value);
+ }
+
+ ///
+ /// Gets or sets a value that indicates where to draw
+ /// tick marks in relation to the track.
+ ///
+ public TickPlacement TickPlacement
+ {
+ get => GetValue(TickPlacementProperty);
+ set => SetValue(TickPlacementProperty, value);
+ }
+
+ ///
+ protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
+ {
+ base.OnApplyTemplate(e);
+
+ _decreaseButtonPressDispose?.Dispose();
+ _decreaseButtonReleaseDispose?.Dispose();
+ _increaseButtonSubscription?.Dispose();
+ _increaseButtonReleaseDispose?.Dispose();
+ _pointerMovedDispose?.Dispose();
+
+ _decreaseButton = e.NameScope.Find