diff --git a/UVtools.Core/FileFormats/FileFormat.cs b/UVtools.Core/FileFormats/FileFormat.cs
index 375d315..7c72f15 100644
--- a/UVtools.Core/FileFormats/FileFormat.cs
+++ b/UVtools.Core/FileFormats/FileFormat.cs
@@ -321,6 +321,8 @@ namespace UVtools.Core.FileFormats
set => _haveModifiedLayers = value;
} // => LayerManager.IsModified;
+ public Size Resolution => new Size((int) ResolutionX, (int) ResolutionY);
+
public abstract uint ResolutionX { get; set; }
public abstract uint ResolutionY { get; set; }
@@ -363,6 +365,7 @@ namespace UVtools.Core.FileFormats
public StringBuilder GCode { get; set; }
+ public bool HaveGCode => !(GCode is null);
public abstract object[] Configs { get; }
public bool IsValid => !ReferenceEquals(FileFullPath, null);
diff --git a/UVtools.Core/FileFormats/IFileFormat.cs b/UVtools.Core/FileFormats/IFileFormat.cs
index 0ca1b84..e166b55 100644
--- a/UVtools.Core/FileFormats/IFileFormat.cs
+++ b/UVtools.Core/FileFormats/IFileFormat.cs
@@ -7,6 +7,7 @@
*/
using System;
+using System.Drawing;
using System.Text;
using Emgu.CV;
using UVtools.Core.Operations;
@@ -82,6 +83,8 @@ namespace UVtools.Core.FileFormats
///
LayerManager LayerManager { get; set; }
+ Size Resolution { get; }
+
///
/// Gets the image width resolution
///
@@ -207,6 +210,11 @@ namespace UVtools.Core.FileFormats
///
StringBuilder GCode { get; set; }
+ ///
+ /// Gets if this file have available gcode
+ ///
+ bool HaveGCode { get; }
+
///
/// Get all configuration objects with properties and values
///
diff --git a/UVtools.Core/FileFormats/PHZFile.cs b/UVtools.Core/FileFormats/PHZFile.cs
index b331ace..8a68196 100644
--- a/UVtools.Core/FileFormats/PHZFile.cs
+++ b/UVtools.Core/FileFormats/PHZFile.cs
@@ -978,6 +978,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);
using (var inputFile = new FileStream(fileFullPath, FileMode.Open, FileAccess.Read))
{
diff --git a/UVtools.Core/Operations/OperationProgress.cs b/UVtools.Core/Operations/OperationProgress.cs
index f34d339..3ffce1f 100644
--- a/UVtools.Core/Operations/OperationProgress.cs
+++ b/UVtools.Core/Operations/OperationProgress.cs
@@ -6,11 +6,14 @@
* of this license document, but changing it is not allowed.
*/
using System;
+using System.Diagnostics;
using System.Threading;
+using UVtools.Core.Extensions;
+using UVtools.Core.Objects;
namespace UVtools.Core.Operations
{
- public sealed class OperationProgress
+ public sealed class OperationProgress : BindableBase
{
public const string StatusDecodeThumbnails = "Decoded Thumbnails";
public const string StatusGatherLayers = "Gathered Layers";
@@ -34,6 +37,11 @@ namespace UVtools.Core.Operations
public CancellationToken Token => TokenSource.Token;
private bool _canCancel = true;
+ private string _title = "Operation";
+ private string _itemName = StatusDecodeLayers;
+ private uint _processedItems;
+ private uint _itemCount;
+
public
OperationProgress()
@@ -50,6 +58,8 @@ namespace UVtools.Core.Operations
_canCancel = canCancel;
}
+ public Stopwatch StopWatch { get; } = new Stopwatch();
+
///
/// Gets or sets if operation can be cancelled
///
@@ -60,35 +70,74 @@ namespace UVtools.Core.Operations
if (!_canCancel) return _canCancel;
return !Token.IsCancellationRequested && Token.CanBeCanceled && _canCancel;
}
- set => _canCancel = value;
+ set => SetProperty(ref _canCancel, value);
}
///
/// Gets or sets the item name for the operation
///
- public string ItemName { get; set; } = StatusDecodeLayers;
+ public string Title
+ {
+ get => _title;
+ set => SetProperty(ref _title, value);
+ }
+
+ public string ElapsedTimeStr => $"{StopWatch.Elapsed.Minutes}m {StopWatch.Elapsed.Seconds}s {StopWatch.Elapsed.Milliseconds}ms";
+
+ ///
+ /// Gets or sets the item name for the operation
+ ///
+ public string ItemName
+ {
+ get => _itemName;
+ set => SetProperty(ref _itemName, value);
+ }
///
/// Gets or sets the number of processed items
///
- public uint ProcessedItems { get; set; }
+ public uint ProcessedItems
+ {
+ get => _processedItems;
+ set
+ {
+ //_processedItems = value;
+ SetProperty(ref _processedItems, value);
+ OnPropertyChanged(nameof(ProgressPercent));
+ OnPropertyChanged(nameof(Description));
+ }
+ }
///
/// Gets or sets the total of item count on this operation
///
- public uint ItemCount { get; set; }
+ public uint ItemCount
+ {
+ get => _itemCount;
+ set
+ {
+ SetProperty(ref _itemCount, value);
+ OnPropertyChanged(nameof(IsIndeterminate));
+ OnPropertyChanged(nameof(ProgressPercent));
+ OnPropertyChanged(nameof(Description));
+ }
+ }
///
/// Gets the remaining items to be processed
///
public uint RemainingItems => ItemCount - ProcessedItems;
- public int ProgressStep => (int) Math.Min(ProcessedItems * 100 / ItemCount, 100);
+ public int ProgressStep => (int)ProgressPercent;
+
+ public string Description => ToString();
+
+ public bool IsIndeterminate => ItemCount == 0;
///
/// Gets the progress from 0 to 100%
///
- public double ProgressPercent => Math.Round(ProcessedItems * 100.0 / ItemCount, 2);
+ public double ProgressPercent => ItemCount == 0 ? 0 : Math.Round(ProcessedItems * 100.0 / ItemCount, 2).Clamp(0, 100);
public static OperationProgress operator +(OperationProgress progress, uint value)
{
@@ -122,5 +171,13 @@ namespace UVtools.Core.Operations
$"{ProcessedItems}/? {ItemName}" :
$"{ProcessedItems}/{ItemCount} {ItemName} | {ProgressPercent:0.00}%";
}
+
+ public void TriggerRefresh()
+ {
+ OnPropertyChanged(nameof(ElapsedTimeStr));
+ OnPropertyChanged(nameof(CanCancel));
+ //OnPropertyChanged(nameof(ProgressPercent));
+ //OnPropertyChanged(nameof(Description));
+ }
}
}
diff --git a/UVtools.GUI/Forms/FrmLoading.cs b/UVtools.GUI/Forms/FrmLoading.cs
index 61cd510..8806c57 100644
--- a/UVtools.GUI/Forms/FrmLoading.cs
+++ b/UVtools.GUI/Forms/FrmLoading.cs
@@ -11,7 +11,7 @@ namespace UVtools.GUI.Forms
public partial class FrmLoading : Form
{
public Stopwatch StopWatch { get; } = new Stopwatch();
- public OperationProgress Progress { get; set; }
+ public OperationProgress Progress { get; private set; }
private LogItem OperationLog;
diff --git a/UVtools.WPF/App.axaml b/UVtools.WPF/App.axaml
index 9b64e5d..3eb218e 100644
--- a/UVtools.WPF/App.axaml
+++ b/UVtools.WPF/App.axaml
@@ -4,6 +4,7 @@
+
diff --git a/UVtools.WPF/App.axaml.cs b/UVtools.WPF/App.axaml.cs
index 9e0b688..f22ddb1 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;
+using System.Diagnostics;
using System.Globalization;
using System.Threading;
using Avalonia;
@@ -49,7 +51,19 @@ namespace UVtools.WPF
}
#region Utilities
-
+ public static void NewInstance(string filePath)
+ {
+ try
+ {
+ var info = new ProcessStartInfo("UVtools.exe", $"\"{filePath}\"");
+ Process.Start(info)?.Dispose();
+ }
+ catch (Exception e)
+ {
+ Debug.WriteLine(e);
+ }
+
+ }
#endregion
}
diff --git a/UVtools.WPF/AppSettings.cs b/UVtools.WPF/AppSettings.cs
index 9b35e1f..162d76c 100644
--- a/UVtools.WPF/AppSettings.cs
+++ b/UVtools.WPF/AppSettings.cs
@@ -27,7 +27,7 @@ namespace UVtools.WPF
///
/// Returns the zoom level that will be used for autozoom actions
///
- public const ushort LockedZoomLevel = 1200;
+ public static int LockedZoomLevel => ZoomLevels[UserSettings.Instance.LayerPreview.ZoomLockLevelIndex + ZoomLevelSkipCount];
///
diff --git a/UVtools.WPF/Controls/AdvancedImageBox.cs b/UVtools.WPF/Controls/AdvancedImageBox.cs
index 513e11d..0922927 100644
--- a/UVtools.WPF/Controls/AdvancedImageBox.cs
+++ b/UVtools.WPF/Controls/AdvancedImageBox.cs
@@ -408,8 +408,17 @@ namespace UVtools.WPF.Controls
set
{
_image = value;
- SizedContainer.Width = _image.Size.Width;
- SizedContainer.Height = _image.Size.Height;
+ if (_image is null)
+ {
+ SizedContainer.Width = 0;
+ SizedContainer.Height = 0;
+ }
+ else
+ {
+ SizedContainer.Width = _image.Size.Width;
+ SizedContainer.Height = _image.Size.Height;
+ }
+
InvalidateVisual();
}
}
diff --git a/UVtools.WPF/LayerCache.cs b/UVtools.WPF/LayerCache.cs
index 53660fc..e46d4e7 100644
--- a/UVtools.WPF/LayerCache.cs
+++ b/UVtools.WPF/LayerCache.cs
@@ -10,28 +10,35 @@ using System;
using Emgu.CV;
using Emgu.CV.CvEnum;
using Emgu.CV.Util;
+using UVtools.Core;
namespace UVtools.WPF
{
public sealed class LayerCache
{
+ private Layer _layer;
private Mat _image;
private Array _layerHierarchyJagged;
private VectorOfVectorOfPoint _layerContours;
private Mat _layerHierarchy;
- public Mat Image
+ public bool IsCached => !ReferenceEquals(_layer, null);
+
+ public Layer Layer
{
- get => _image;
+ get => _layer;
set
{
Clear();
- _image = value;
+ _layer = value;
+ _image = _layer.LayerMat;
ImageBgr = new Mat();
- CvInvoke.CvtColor(value, ImageBgr, ColorConversion.Gray2Bgr);
+ CvInvoke.CvtColor(_image, ImageBgr, ColorConversion.Gray2Bgr);
}
}
+ public Mat Image => _image;
+
public Mat ImageBgr { get; set; }
public VectorOfVectorOfPoint LayerContours
@@ -81,6 +88,7 @@ namespace UVtools.WPF
///
public void Clear()
{
+ _layer = null;
_image?.Dispose();
ImageBgr?.Dispose();
_layerContours?.Dispose();
diff --git a/UVtools.WPF/MainWindow.axaml b/UVtools.WPF/MainWindow.axaml
index 35058e9..76f60c1 100644
--- a/UVtools.WPF/MainWindow.axaml
+++ b/UVtools.WPF/MainWindow.axaml
@@ -11,27 +11,42 @@
Icon="/Assets/Icons/UVtools.ico"
MinWidth="1024"
MinHeight="600"
+ DragDrop.AllowDrop="True"
>
-
-
-
-
+
- Issues
+
+
+ Information
+
-
-
-
-
+
+
+
+
+
+ Gcode
+
+
+
+
+
- GCode
+
+
+ Issues
+
-
-
-
-
+
+
+
- Log
+
+
+ Log
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -287,7 +378,7 @@
@@ -349,8 +440,69 @@
Name="Layer.Image"
/>
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/UVtools.WPF/MainWindow.axaml.cs b/UVtools.WPF/MainWindow.axaml.cs
index b61abb5..39c9101 100644
--- a/UVtools.WPF/MainWindow.axaml.cs
+++ b/UVtools.WPF/MainWindow.axaml.cs
@@ -6,22 +6,27 @@
* of this license document, but changing it is not allowed.
*/
using System;
+using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
+using System.Linq;
using System.Runtime.CompilerServices;
+using System.Threading;
using System.Threading.Tasks;
using Avalonia;
using Avalonia.Collections;
using Avalonia.Controls;
+using Avalonia.Input;
using Avalonia.Markup.Xaml;
+using Avalonia.Threading;
using Emgu.CV;
using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
-using Emgu.CV.Util;
using UVtools.Core;
using UVtools.Core.Extensions;
using UVtools.Core.FileFormats;
+using UVtools.Core.PixelEditor;
using UVtools.WPF.Controls;
using UVtools.WPF.Extensions;
using UVtools.WPF.Structures;
@@ -87,15 +92,28 @@ namespace UVtools.WPF
#endregion
#region Controls
+
+ public ProgressWindow ProgressWindow = new ProgressWindow();
+
+ //private ProgressWindow _progressWindow;
public AdvancedImageBox LayerImageBox;
public SliderEx LayerSlider;
public Panel LayerNavigationTooltipPanel;
public Border LayerNavigationTooltipBorder;
+
+ #region DataSets
+ public ObservableCollection Logs { get; } = new ObservableCollection();
+ public ObservableCollection Issues { get; } = new ObservableCollection();
+ public ObservableCollection Drawings { get; } = new ObservableCollection();
+ #endregion
+
#endregion
#region Members
private uint _actualLayer;
private bool _isGUIEnabled = true;
+ private bool _canSave;
+ private bool _isVerbose;
private bool _showLayerImageRotated;
private bool _showLayerImageDifference;
private bool _showLayerImageIssues = true;
@@ -113,7 +131,22 @@ namespace UVtools.WPF
public bool IsGUIEnabled
{
get => _isGUIEnabled;
- set => SetProperty(ref _isGUIEnabled, value);
+ set
+ {
+ if (!SetProperty(ref _isGUIEnabled, value)) return;
+ if (!_isGUIEnabled) return;
+ if (ProgressWindow.IsActive)
+ {
+ Dispatcher.UIThread.InvokeAsync(() =>
+ {
+ ProgressWindow.Close();
+ ProgressWindow.Dispose();
+ ProgressWindow = new ProgressWindow();
+ });
+ }
+
+
+ }
}
public bool IsFileLoaded
@@ -122,6 +155,18 @@ namespace UVtools.WPF
set => OnPropertyChanged();
}
+ public bool CanSave
+ {
+ get => _canSave;
+ set => SetProperty(ref _canSave, value);
+ }
+
+ public bool IsVerbose
+ {
+ get => _isVerbose;
+ set => SetProperty(ref _isVerbose, value);
+ }
+
public bool ShowLayerImageRotated
{
get => _showLayerImageRotated;
@@ -233,6 +278,21 @@ namespace UVtools.WPF
public bool CanGoUp => _actualLayer < SliderMaximumValue;
public bool CanGoDown => _actualLayer > 0;
+ public string LayerPixelCountStr
+ {
+ get
+ {
+ if (!LayerCache.IsCached) return "0";
+ var pixelPercent =
+ Math.Round(
+ LayerCache.Layer.NonZeroPixelCount * 100.0 / (SlicerFile.ResolutionX * SlicerFile.ResolutionY), 2);
+ return $"{LayerCache.Layer.NonZeroPixelCount} ({pixelPercent}%)";
+ }
+ }
+
+ public string LayerBoundsStr => LayerCache.Layer is null ? "NS" : LayerCache.Layer.BoundingRectangle.ToString();
+ public string LayerROIStr => LayerCache.Layer is null ? "NS" : "TODO";
+
public long ShowLayerRenderMs
{
get => _showLayerRenderMs;
@@ -240,7 +300,13 @@ namespace UVtools.WPF
}
#endregion
- private uint ActualLayer
+ public PixelPicker LayerPixelPicker { get; } = new PixelPicker();
+
+ public string LayerZoomStr => $"{LayerImageBox.Zoom / 100}x"+
+ (AppSettings.LockedZoomLevel == LayerImageBox.Zoom ? "🔒" : string.Empty);
+ public string LayerResolutionStr => SlicerFile?.Resolution.ToString() ?? "Unloaded";
+
+ public uint ActualLayer
{
get => _actualLayer;
set
@@ -250,6 +316,8 @@ namespace UVtools.WPF
OnPropertyChanged(nameof(CanGoUp));
OnPropertyChanged(nameof(ActualLayerTooltip));
OnPropertyChanged(nameof(LayerNavigationTooltipMargin));
+ OnPropertyChanged(nameof(LayerPixelCountStr));
+ OnPropertyChanged(nameof(LayerBoundsStr));
ShowLayer();
}
}
@@ -259,7 +327,7 @@ namespace UVtools.WPF
get
{
double top = 0;
- if (LayerSlider != null)
+ if (LayerSlider?.Track != null)
{
double trackerPos = LayerSlider.Track.Thumb.Bounds.Height / 2 + LayerSlider.Track.Thumb.Bounds.Top;
double halfTooltipHeight = LayerNavigationTooltipBorder.Bounds.Height / 2;
@@ -279,19 +347,12 @@ namespace UVtools.WPF
public LayerCache LayerCache = new LayerCache();
-
#endregion
#region Constructors
public MainWindow()
{
- _showLayerImageDifference = Settings.LayerPreview.ShowLayerDifference;
- _showLayerOutlinePrintVolumeBoundary = Settings.LayerPreview.VolumeBoundsOutline;
- _showLayerOutlineLayerBoundary = Settings.LayerPreview.LayerBoundsOutline;
- _showLayerOutlineHollowAreas = Settings.LayerPreview.HollowOutline;
-
- DataContext = this;
InitializeComponent();
#if DEBUG
//this.AttachDevTools();
@@ -301,6 +362,12 @@ namespace UVtools.WPF
LayerSlider = this.FindControl("Layer.Navigation.Slider");
LayerNavigationTooltipPanel = this.FindControl("Layer.Navigation.Tooltip.Panel");
LayerNavigationTooltipBorder = this.FindControl("Layer.Navigation.Tooltip.Border");
+
+ _showLayerImageDifference = Settings.LayerPreview.ShowLayerDifference;
+ _showLayerOutlinePrintVolumeBoundary = Settings.LayerPreview.VolumeBoundsOutline;
+ _showLayerOutlineLayerBoundary = Settings.LayerPreview.LayerBoundsOutline;
+ _showLayerOutlineHollowAreas = Settings.LayerPreview.HollowOutline;
+
/*LayerSlider.PropertyChanged += (sender, args) =>
{
Debug.WriteLine(args.Property.Name);
@@ -311,8 +378,22 @@ namespace UVtools.WPF
}
};*/
PropertyChanged += OnPropertyChanged;
+ DataContext = this;
+ AddHandler(DragDrop.DropEvent, (sender, e) =>
+ {
+ ProcessFiles(e.Data.GetFileNames().ToArray());
+ });
+
+ AddLog($"{About.Software} start");
}
+ public override void Show()
+ {
+ base.Show();
+ ProcessFiles(Program.Args);
+ }
+
+
private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
Debug.WriteLine(e.PropertyName);
@@ -332,14 +413,28 @@ namespace UVtools.WPF
#region Events
- public async void MenuFileOpenClicked()
+ public void MenuFileOpenClicked() => OpenFile();
+ public void MenuFileOpenNewWindowClicked() => OpenFile(true);
+
+ public async void OpenFile(bool newWindow = false)
{
var dialog = new OpenFileDialog
{
- AllowMultiple = false,
+ AllowMultiple = true,
+
};
var files = await dialog.ShowAsync(this);
- ProcessFiles(files);
+ ProcessFiles(files, newWindow);
+ }
+
+ public void CloseFile()
+ {
+ if (SlicerFile is null) return;
+ SlicerFile?.Dispose();
+ App.SlicerFile = null;
+ LayerCache.Clear();
+ LayerImageBox.Image = null;
+ ResetDataContext();
}
public async void MenuFileSettingsClicked()
@@ -347,6 +442,7 @@ namespace UVtools.WPF
SettingsWindow settingsWindow = new SettingsWindow();
await settingsWindow.ShowDialog(this);
}
+
#endregion
#region Methods
@@ -361,58 +457,63 @@ namespace UVtools.WPF
ProcessFile(files[i]);
continue;
}
+
+ App.NewInstance(files[i]);
+
}
}
- void ReloadFile(uint actualLayer = 0)
+ void ReloadFile() => ReloadFile(_actualLayer);
+
+ void ReloadFile(uint actualLayer)
{
if (App.SlicerFile is null) return;
- ProcessFile(App.SlicerFile.FileFullPath, actualLayer);
+ ProcessFile(SlicerFile.FileFullPath, _actualLayer);
}
- void ProcessFile(string fileName, uint actualLayer = 0)
+ async void ProcessFile(string fileName, uint actualLayer = 0)
{
+ if (!File.Exists(fileName)) return;
+ CloseFile();
var fileNameOnly = Path.GetFileName(fileName);
App.SlicerFile = FileFormat.FindByExtension(fileName, true, true);
- if (App.SlicerFile is null) return;
+ if (SlicerFile is null) return;
+ ProgressWindow.SetTitle($"Opening: {fileNameOnly}");
IsGUIEnabled = false;
-
var task = Task.Factory.StartNew(() =>
{
try
{
- App.SlicerFile.Decode(fileName);
+ SlicerFile.Decode(fileName, ProgressWindow.RestartProgress());
}
catch (OperationCanceledException)
{
- App.SlicerFile.Clear();
+ SlicerFile.Clear();
}
finally
{
- /* Invoke((MethodInvoker)delegate
- {
- // Running on the UI thread
- EnableGUI(true);
- });*/
+ IsGUIEnabled = true;
}
});
//ProgressWindow progressWindow = new ProgressWindow();
//progressWindow.ShowDialog(this);
- task.Wait();
+ await ProgressWindow.ShowDialog(this);
- var mat = App.SlicerFile[0].LayerMat;
+ /*var mat = App.SlicerFile[0].LayerMat;
var matRgb = App.SlicerFile[0].BrgMat;
Debug.WriteLine("4K grayscale - BGRA convertion:");
var bitmap = mat.ToBitmap();
Debug.WriteLine("4K BGR - BGRA convertion:");
var bitmapRgb = matRgb.ToBitmap();
- LayerImageBox.Image = bitmapRgb;
+ LayerImageBox.Image = bitmapRgb;*/
+
+ _actualLayer = actualLayer;
+ ShowLayer();
- IsGUIEnabled = true;
ResetDataContext();
}
@@ -444,6 +545,11 @@ namespace UVtools.WPF
ActualLayer = SliderMaximumValue;
}
+ public void ZoomToFit()
+ {
+ LayerImageBox.ZoomToFit();
+ }
+
///
/// Shows a layer number
///
@@ -453,17 +559,11 @@ namespace UVtools.WPF
Debug.WriteLine($"Showing layer: {_actualLayer}");
-
- //AddLogVerbose($"Show Layer: {layerNum}");
-
-
Stopwatch watch = Stopwatch.StartNew();
- var layer = SlicerFile[_actualLayer];
+ LayerCache.Layer = SlicerFile[_actualLayer];
try
{
- LayerCache.Image = layer.LayerMat;
-
//var imageSpan = LayerCache.Image.GetPixelSpan();
//var imageBgrSpan = LayerCache.ImageBgr.GetPixelSpan();
@@ -805,6 +905,7 @@ namespace UVtools.WPF
watch.Stop();
ShowLayerRenderMs = watch.ElapsedMilliseconds;
+ AddLogVerbose($"Show Layer: {_actualLayer}", watch.Elapsed.TotalSeconds);
}
catch (Exception e)
{
@@ -812,6 +913,25 @@ namespace UVtools.WPF
}
}
+
+ public void AddLog(LogItem log)
+ {
+ log.Index = Logs.Count;
+ Logs.Insert(0, log);
+ //lbLogOperations.Text = $"Operations: {count}";
+ }
+
+ public void AddLog(string description, double elapsedTime = 0) =>
+ AddLog(new LogItem(Logs.Count, description, elapsedTime));
+
+
+ public void AddLogVerbose(string description, double elapsedTime = 0)
+ {
+ if (!_isVerbose) return;
+ AddLog(description, elapsedTime);
+ Debug.WriteLine(description);
+ }
+
#endregion
}
}
diff --git a/UVtools.WPF/Program.cs b/UVtools.WPF/Program.cs
index ea81577..b3f91bb 100644
--- a/UVtools.WPF/Program.cs
+++ b/UVtools.WPF/Program.cs
@@ -1,14 +1,21 @@
-using Avalonia;
+using System;
+using Avalonia;
namespace UVtools.WPF
{
- class Program
+ public static class Program
{
+ public static string[] Args;
// Initialization code. Don't use any Avalonia, third-party APIs or any
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized
// yet and stuff might break.
- public static void Main(string[] args) => BuildAvaloniaApp()
- .StartWithClassicDesktopLifetime(args);
+ [STAThread]
+ public static void Main(string[] args)
+ {
+ Args = args;
+ BuildAvaloniaApp()
+ .StartWithClassicDesktopLifetime(args);
+ }
// Avalonia configuration, don't remove; also used by visual designer.
public static AppBuilder BuildAvaloniaApp()
diff --git a/UVtools.WPF/Structures/LogItem.cs b/UVtools.WPF/Structures/LogItem.cs
new file mode 100644
index 0000000..4dc6872
--- /dev/null
+++ b/UVtools.WPF/Structures/LogItem.cs
@@ -0,0 +1,61 @@
+/*
+ * GNU AFFERO GENERAL PUBLIC LICENSE
+ * Version 3, 19 November 2007
+ * Copyright (C) 2007 Free Software Foundation, Inc.
+ * Everyone is permitted to copy and distribute verbatim copies
+ * of this license document, but changing it is not allowed.
+ */
+
+using System;
+using UVtools.Core.Objects;
+
+namespace UVtools.WPF.Structures
+{
+ public sealed class LogItem : BindableBase
+ {
+ private int _index;
+ private string _startTime;
+ private double _elapsedTime;
+ private string _description;
+
+ public int Index
+ {
+ get => _index;
+ set => SetProperty(ref _index, value);
+ }
+
+ public string StartTime
+ {
+ get => _startTime;
+ set => SetProperty(ref _startTime, value);
+ }
+
+ public double ElapsedTime
+ {
+ get => _elapsedTime;
+ set => SetProperty(ref _elapsedTime, Math.Round(value, 2));
+ }
+
+ public string Description
+ {
+ get => _description;
+ set => SetProperty(ref _description, value);
+ }
+
+ public LogItem(int index, string description, double elapsedTime = 0)
+ {
+ _index = index;
+ _description = description;
+ ElapsedTime = elapsedTime;
+ _startTime = DateTime.Now.ToString("HH:mm:ss");
+ }
+
+ public LogItem(string description = null, uint elapsedTime = 0) : this(0, description, elapsedTime)
+ { }
+
+ public override string ToString()
+ {
+ return Description;
+ }
+ }
+}
diff --git a/UVtools.WPF/Structures/PixelPicker.cs b/UVtools.WPF/Structures/PixelPicker.cs
new file mode 100644
index 0000000..f011954
--- /dev/null
+++ b/UVtools.WPF/Structures/PixelPicker.cs
@@ -0,0 +1,46 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Text;
+using Avalonia;
+using ReactiveUI;
+
+namespace UVtools.WPF.Structures
+{
+ public class PixelPicker : ReactiveObject
+ {
+ private bool _isSet;
+ private Point _location = new Point(0,0);
+ private byte _brightness;
+
+ public bool IsSet
+ {
+ get => _isSet;
+ private set => this.RaiseAndSetIfChanged(ref _isSet, value);
+ }
+
+ public Point Location
+ {
+ get => _location;
+ private set => this.RaiseAndSetIfChanged(ref _location, value);
+ }
+
+ public byte Brightness
+ {
+ get => _brightness;
+ private set => this.RaiseAndSetIfChanged(ref _brightness, value);
+ }
+
+ public void Set(Point location, byte brightness)
+ {
+ Location = location;
+ Brightness = brightness;
+ IsSet = true;
+ }
+
+ public override string ToString()
+ {
+ return $"{{X={_location.X}, Y={_location.Y}, B={_brightness}}}";
+ }
+ }
+}
diff --git a/UVtools.WPF/UVtools.WPF.csproj b/UVtools.WPF/UVtools.WPF.csproj
index 6d7eac4..8e47665 100644
--- a/UVtools.WPF/UVtools.WPF.csproj
+++ b/UVtools.WPF/UVtools.WPF.csproj
@@ -22,6 +22,7 @@
+
diff --git a/UVtools.WPF/Windows/ProgressWindow.axaml b/UVtools.WPF/Windows/ProgressWindow.axaml
index fbcc4a2..435bd20 100644
--- a/UVtools.WPF/Windows/ProgressWindow.axaml
+++ b/UVtools.WPF/Windows/ProgressWindow.axaml
@@ -14,12 +14,21 @@
Icon="/Assets/Icons/UVtools.ico"
>
-
-
-
+
+
+
-
-
+
+
diff --git a/UVtools.WPF/Windows/ProgressWindow.axaml.cs b/UVtools.WPF/Windows/ProgressWindow.axaml.cs
index b9744e6..953340a 100644
--- a/UVtools.WPF/Windows/ProgressWindow.axaml.cs
+++ b/UVtools.WPF/Windows/ProgressWindow.axaml.cs
@@ -5,22 +5,79 @@
* Everyone is permitted to copy and distribute verbatim copies
* of this license document, but changing it is not allowed.
*/
+
+using System;
+using System.Diagnostics;
+using System.Timers;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
+using Avalonia.Threading;
+using UVtools.Core.Operations;
+using UVtools.WPF.Structures;
namespace UVtools.WPF.Windows
{
- public class ProgressWindow : Window
+ public class ProgressWindow : Window, IDisposable
{
+ public Stopwatch StopWatch { get; } = new Stopwatch();
+ public OperationProgress Progress { get; } = new OperationProgress();
+
+ private LogItem _logItem = new LogItem();
+
+ private Timer _timer;
+
+ public bool CanCancel => Progress?.CanCancel ?? false;
+
public ProgressWindow()
{
InitializeComponent();
+ DataContext = Progress;
+ _timer = new Timer(100) {AutoReset = true};
+ _timer.Elapsed += (sender, args) =>
+ {
+ Progress.TriggerRefresh();
+ };
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
-
+ }
+
+ protected override void OnClosed(EventArgs e)
+ {
+ base.OnClosed(e);
+ _timer.Stop();
+ Progress.StopWatch.Stop();
+ _logItem.ElapsedTime = Math.Round(Progress.StopWatch.Elapsed.TotalSeconds, 2);
+ App.MainWindow.AddLog(_logItem);
+ }
+
+ public void SetTitle(string title)
+ {
+ Progress.Title = title;
+ _logItem.Description = title;
+ }
+
+ public OperationProgress RestartProgress(bool canCancel = true)
+ {
+ Progress.CanCancel = canCancel;
+ Progress.StopWatch.Restart();
+
+ if (!_timer.Enabled)
+ {
+ _timer.Enabled = true;
+ _timer.Start();
+ }
+
+ Dispatcher.UIThread.InvokeAsync(() => DataContext = Progress);
+ return Progress;
+ }
+
+ public void Dispose()
+ {
+ _timer.Close();
+ _timer.Dispose();
}
}
}