Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/sn4k3/UVtools.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTiago Conceição <Tiago_caza@hotmail.com>2020-11-08 02:47:57 +0300
committerTiago Conceição <Tiago_caza@hotmail.com>2020-11-08 02:47:57 +0300
commit2e349328d6dea431325c8242fe915a402a806bb3 (patch)
tree47d14c3f96cfd0ac140b72ab1ee21869c4eed372
parent5da3c7e173094666cd900b3ee09fe3dd93d97985 (diff)
v1.2.0v1.2.0
* (Add) RAM usage on title bar * (Add) Clipboard manager: Undo (Ctrl + Z) and Redo (Ctrl + Y) modifications (Memory optimized) * (Add) Current layer properties on information tab * (Fix) Long windows with system zoom bigger than 100% were being hidden and overflow (#90) * (Fix) Do not recompute issues nor properties nor reshow layer if operation is cancelled or failed
-rw-r--r--CHANGELOG.md8
-rw-r--r--UVtools.Core/Extensions/SizeExtensions.cs38
-rw-r--r--UVtools.Core/FileFormats/FileFormat.cs21
-rw-r--r--UVtools.Core/Layer/Layer.cs9
-rw-r--r--UVtools.Core/Layer/LayerManager.cs41
-rw-r--r--UVtools.Core/Managers/ClipboardManager.cs359
-rw-r--r--UVtools.Core/Objects/StringTag.cs45
-rw-r--r--UVtools.Core/Operations/OperationLayerClone.cs7
-rw-r--r--UVtools.Core/Operations/OperationLayerImport.cs7
-rw-r--r--UVtools.Core/Operations/OperationLayerReHeight.cs7
-rw-r--r--UVtools.Core/Operations/OperationMove.cs7
-rw-r--r--UVtools.Core/Operations/OperationPattern.cs7
-rw-r--r--UVtools.Core/Operations/OperationRepairLayers.cs1
-rw-r--r--UVtools.Core/UVtools.Core.csproj2
-rw-r--r--UVtools.WPF/App.axaml.cs85
-rw-r--r--UVtools.WPF/AppSettings.cs80
-rw-r--r--UVtools.WPF/Assets/Icons/clipboard-32x32.pngbin0 -> 313 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/layers-alt-32x32.pngbin0 -> 268 bytes
-rw-r--r--UVtools.WPF/Assets/Icons/redo-16x16.pngbin0 -> 231 bytes
-rw-r--r--UVtools.WPF/MainWindow.Clipboard.cs57
-rw-r--r--UVtools.WPF/MainWindow.Information.cs81
-rw-r--r--UVtools.WPF/MainWindow.LayerPreview.cs24
-rw-r--r--UVtools.WPF/MainWindow.PixelEditor.cs3
-rw-r--r--UVtools.WPF/MainWindow.axaml119
-rw-r--r--UVtools.WPF/MainWindow.axaml.cs66
-rw-r--r--UVtools.WPF/Structures/AppVersionChecker.cs6
-rw-r--r--UVtools.WPF/Structures/OperationProfiles.cs16
-rw-r--r--UVtools.WPF/UVtools.WPF.csproj8
-rw-r--r--UVtools.WPF/UserSettings.cs4
-rw-r--r--UVtools.WPF/Windows/AboutWindow.axaml.cs8
-rw-r--r--UVtools.WPF/Windows/SettingsWindow.axaml.cs4
-rw-r--r--UVtools.WPF/Windows/ToolWindow.axaml.cs4
32 files changed, 950 insertions, 174 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index fa94ac5..37fa92c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,13 @@
# Changelog
+## 07/11/2020 - v1.2.0
+
+* (Add) RAM usage on title bar
+* (Add) Clipboard manager: Undo (Ctrl + Z) and Redo (Ctrl + Y) modifications (Memory optimized)
+* (Add) Current layer properties on information tab
+* (Fix) Long windows with system zoom bigger than 100% were being hidden and overflow (#90)
+* (Fix) Do not recompute issues nor properties nor reshow layer if operation is cancelled or failed
+
## 05/11/2020 - v1.1.3
* (Add) Auto-updater: When a new version is detected UVtools still show the same green button at top,
diff --git a/UVtools.Core/Extensions/SizeExtensions.cs b/UVtools.Core/Extensions/SizeExtensions.cs
new file mode 100644
index 0000000..8e164ec
--- /dev/null
+++ b/UVtools.Core/Extensions/SizeExtensions.cs
@@ -0,0 +1,38 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace UVtools.Core.Extensions
+{
+ public static class SizeExtensions
+ {
+ public static readonly string[] SizeSuffixes =
+ { "bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" };
+
+ public static string SizeSuffix(long value, byte decimalPlaces = 2)
+ {
+ //if (decimalPlaces < 0) { throw new ArgumentOutOfRangeException("decimalPlaces"); }
+ if (value < 0) { return "-" + SizeSuffix(-value); }
+ if (value == 0) { return string.Format("{0:n" + decimalPlaces + "} bytes", 0); }
+
+ // mag is 0 for bytes, 1 for KB, 2, for MB, etc.
+ int mag = (int)Math.Log(value, 1024);
+
+ // 1L << (mag * 10) == 2 ^ (10 * mag)
+ // [i.e. the number of bytes in the unit corresponding to mag]
+ decimal adjustedSize = (decimal)value / (1L << (mag * 10));
+
+ // make adjustment when the value is large enough that
+ // it would round up to 1000 or more
+ if (Math.Round(adjustedSize, decimalPlaces) >= 1000)
+ {
+ mag += 1;
+ adjustedSize /= 1024;
+ }
+
+ return string.Format("{0:n" + decimalPlaces + "} {1}",
+ adjustedSize,
+ SizeSuffixes[mag]);
+ }
+ }
+}
diff --git a/UVtools.Core/FileFormats/FileFormat.cs b/UVtools.Core/FileFormats/FileFormat.cs
index 3ec0f65..fe0f667 100644
--- a/UVtools.Core/FileFormats/FileFormat.cs
+++ b/UVtools.Core/FileFormats/FileFormat.cs
@@ -353,9 +353,23 @@ namespace UVtools.Core.FileFormats
public abstract Size[] ThumbnailsOriginalSize { get; }
public Mat[] Thumbnails { get; set; }
- public LayerManager LayerManager { get; set; }
+
+ public LayerManager LayerManager
+ {
+ get => _layerManager;
+ set
+ {
+ var oldLayerManager = _layerManager;
+ if (!RaiseAndSetIfChanged(ref _layerManager, value) || oldLayerManager is null || value is null) return;
+ if (oldLayerManager.Count != LayerCount)
+ {
+ LayerCount = _layerManager.Count;
+ }
+ }
+ }
private bool _haveModifiedLayers;
+ private LayerManager _layerManager;
/// <summary>
/// Gets or sets if modifications require a full encode to save
@@ -407,7 +421,7 @@ namespace UVtools.Core.FileFormats
public virtual uint LayerCount
{
get => LayerManager?.Count ?? 0;
- set => throw new NotImplementedException();
+ set { }
}
public virtual ushort BottomLayerCount { get; set; }
@@ -469,8 +483,9 @@ namespace UVtools.Core.FileFormats
private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
Debug.WriteLine(e.PropertyName);
- if (e.PropertyName == nameof(LayerCount))
+ if (e.PropertyName == nameof(LayerCount))
{
+ if (this[LayerCount - 1] is null) return; // Not initialized
LayerManager.RebuildLayersProperties();
RebuildGCode();
return;
diff --git a/UVtools.Core/Layer/Layer.cs b/UVtools.Core/Layer/Layer.cs
index e60d856..39f5551 100644
--- a/UVtools.Core/Layer/Layer.cs
+++ b/UVtools.Core/Layer/Layer.cs
@@ -287,16 +287,19 @@ namespace UVtools.Core
public bool Equals(Layer other)
{
- if (ReferenceEquals(null, other)) return false;
+ if (other is null) return false;
if (ReferenceEquals(this, other)) return true;
- return Equals(_compressedBytes, other._compressedBytes);
+ if (_index != other._index) return false;
+ if (_compressedBytes.Length != other._compressedBytes.Length) return false;
+ return _compressedBytes.AsSpan().SequenceEqual(other._compressedBytes.AsSpan());
+ //return Equals(_compressedBytes, other._compressedBytes);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
- if (obj.GetType() != this.GetType()) return false;
+ if (obj.GetType() != GetType()) return false;
return Equals((Layer)obj);
}
diff --git a/UVtools.Core/Layer/LayerManager.cs b/UVtools.Core/Layer/LayerManager.cs
index f6930ea..06f2a72 100644
--- a/UVtools.Core/Layer/LayerManager.cs
+++ b/UVtools.Core/Layer/LayerManager.cs
@@ -237,14 +237,30 @@ namespace UVtools.Core
/// </summary>
/// <param name="index">Layer index</param>
/// <param name="layer">Layer to add</param>
- public void AddLayer(uint index, Layer layer)
+ /// <param name="makeClone">True to add a clone of the layer</param>
+ public void AddLayer(uint index, Layer layer, bool makeClone = false)
{
//layer.Index = index;
- Layers[index] = layer;
+ Layers[index] = makeClone ? layer.Clone() : layer;
layer.ParentLayerManager = this;
}
/// <summary>
+ /// Add a list of layers
+ /// </summary>
+ /// <param name="layers">Layers to add</param>
+ /// <param name="makeClone">True to add a clone of layers</param>
+ public void AddLayers(IEnumerable<Layer> layers, bool makeClone = false)
+ {
+ //layer.Index = index;
+ foreach (var layer in layers)
+ {
+ layer.ParentLayerManager = this;
+ Layers[layer.Index] = makeClone ? layer.Clone() : layer;
+ }
+ }
+
+ /// <summary>
/// Get layer given index
/// </summary>
/// <param name="index">Layer index</param>
@@ -2025,6 +2041,24 @@ namespace UVtools.Core
}
/// <summary>
+ /// Reallocate with new size
+ /// </summary>
+ /// <returns></returns>
+ public LayerManager Reallocate(uint newLayerCount, bool makeClone = false)
+ {
+ LayerManager layerManager = new LayerManager(newLayerCount, SlicerFile);
+ foreach (var layer in this)
+ {
+ if (layer.Index >= newLayerCount) break;
+ layerManager[layer.Index] = makeClone ? layer.Clone() : layer;
+ }
+
+ layerManager.BoundingRectangle = Rectangle.Empty;
+
+ return layerManager;
+ }
+
+ /// <summary>
/// Clone this object
/// </summary>
/// <returns></returns>
@@ -2035,9 +2069,8 @@ namespace UVtools.Core
{
layerManager[layer.Index] = layer.Clone();
}
-
layerManager.BoundingRectangle = BoundingRectangle;
-
+
return layerManager;
}
diff --git a/UVtools.Core/Managers/ClipboardManager.cs b/UVtools.Core/Managers/ClipboardManager.cs
new file mode 100644
index 0000000..0273217
--- /dev/null
+++ b/UVtools.Core/Managers/ClipboardManager.cs
@@ -0,0 +1,359 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Drawing;
+using UVtools.Core.FileFormats;
+using UVtools.Core.Objects;
+
+namespace UVtools.Core.Managers
+{
+ public sealed class ClipboardItem : IList<Layer>
+ {
+ #region Properties
+ private readonly List<Layer> _layers = new List<Layer>();
+
+ /// <summary>
+ /// Gets the LayerCount for this clip
+ /// </summary>
+ public uint LayerCount { get; }
+
+ public float LayerHeight { get; }
+
+ /// <summary>
+ /// Gets the description of this operation
+ /// </summary>
+ public string Description { get; set; }
+
+ #endregion
+
+ #region Constructor
+ public ClipboardItem(FileFormat slicerFile, string description = null)
+ {
+ LayerCount = slicerFile.LayerCount;
+ LayerHeight = slicerFile.LayerHeight;
+ Description = description;
+ }
+ #endregion
+
+ #region List Implementation
+ public IEnumerator<Layer> GetEnumerator() => _layers.GetEnumerator();
+
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+
+ public void Add(Layer item) => _layers.Add(item);
+
+ public void Clear() => _layers.Clear();
+
+ public bool Contains(Layer item) => _layers.Contains(item);
+
+ public void CopyTo(Layer[] array, int arrayIndex) => _layers.CopyTo(array, arrayIndex);
+
+ public bool Remove(Layer item) => _layers.Remove(item);
+
+ public int Count => _layers.Count;
+ public bool IsReadOnly => false;
+ public int IndexOf(Layer item) => _layers.IndexOf(item);
+
+ public void Insert(int index, Layer item) => _layers.Insert(index, item);
+
+ public void RemoveAt(int index) => _layers.RemoveAt(index);
+
+ public Layer this[int index]
+ {
+ get => _layers[index];
+ set => _layers[index] = value;
+ }
+ #endregion
+
+ #region Methods
+ public override string ToString()
+ {
+ return $"{Description} ({Count})";
+ }
+ #endregion
+ }
+
+ public sealed class ClipboardManager : BindableBase, IList<ClipboardItem>
+ {
+ #region Properties
+
+ public ObservableCollection<ClipboardItem> Items { get; } = new ObservableCollection<ClipboardItem>();
+
+ public FileFormat SlicerFile { get; set; }
+
+ private int _currentIndex = -1;
+ private LayerManager _snapshotLayerManager;
+ private bool _reallocatedLayerCount;
+ private bool _changedByClip;
+
+ /// <summary>
+ /// Gets the index of current item
+ /// </summary>
+ public int CurrentIndex {
+ get => _currentIndex;
+ set
+ {
+ if (value >= Count) value = Count-1;
+ if (_currentIndex == value) return;
+ var oldIndex = _currentIndex;
+ _currentIndex = value;
+ //if (!RaiseAndSetIfChanged(ref _currentIndex, value)) return;
+
+ if (value >= 0)
+ {
+ int dir = oldIndex < _currentIndex ? 1 : -1;
+
+ for (int i = oldIndex + dir; i >= 0 && i < Count; i += dir)
+ {
+ var layerManager = SlicerFile.LayerManager;
+ var clip = this[i];
+ if (layerManager.Count != clip.LayerCount) // Need resize layer manager
+ {
+ layerManager = layerManager.Reallocate(clip.LayerCount);
+ ReallocatedLayerCount = true;
+ }
+
+ layerManager.AddLayers(clip);
+
+ layerManager.BoundingRectangle = Rectangle.Empty;
+ if (SlicerFile.LayerHeight != clip.LayerHeight)
+ {
+ SlicerFile.LayerHeight = clip.LayerHeight;
+ }
+ SlicerFile.LayerManager = layerManager.Clone();
+ if (i == _currentIndex) break;
+ }
+ }
+
+ RaisePropertyChanged();
+ RaisePropertyChanged(nameof(CurrentIndexCountStr));
+ RaisePropertyChanged(nameof(CanUndo));
+ RaisePropertyChanged(nameof(CanRedo));
+ return;
+ }
+ }
+
+ public string CurrentIndexCountStr => (CurrentIndex + 1).ToString().PadLeft(Count.ToString().Length, '0');
+
+ public bool ChangedByClip
+ {
+ get => _changedByClip;
+ set => RaiseAndSetIfChanged(ref _changedByClip, value);
+ }
+
+ public bool ReallocatedLayerCount
+ {
+ get => _reallocatedLayerCount;
+ set => RaiseAndSetIfChanged(ref _reallocatedLayerCount, value);
+ }
+
+ public LayerManager SnapshotLayerManager
+ {
+ get => _snapshotLayerManager;
+ private set => RaiseAndSetIfChanged(ref _snapshotLayerManager, value);
+ }
+
+ public bool CanUndo => CurrentIndex < Count - 1;
+ public bool CanRedo => CurrentIndex > 0;
+
+ #endregion
+
+ #region Singleton
+ private static readonly Lazy<ClipboardManager> InstanceHolder =
+ new Lazy<ClipboardManager>(() => new ClipboardManager());
+
+ public static ClipboardManager Instance => InstanceHolder.Value;
+ #endregion
+
+ #region Constructor
+ private ClipboardManager() { }
+ #endregion
+
+ #region List Implementation
+ public IEnumerator<ClipboardItem> GetEnumerator() => Items.GetEnumerator();
+
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+
+ public void Add(ClipboardItem item) => Items.Add(item);
+
+ public void Clear()
+ {
+ Items.Clear();
+ CurrentIndex = -1;
+ }
+
+ public void Clear(bool keepOriginal)
+ {
+ if (!keepOriginal)
+ {
+ Clear();
+ return;
+ }
+
+ if (Count == 0) return;
+
+ Init(SlicerFile);
+ }
+
+ public bool Contains(ClipboardItem item) => Items.Contains(item);
+
+ public void CopyTo(ClipboardItem[] array, int arrayIndex) => Items.CopyTo(array, arrayIndex);
+
+ public bool Remove(ClipboardItem item) => Items.Remove(item);
+
+ public int Count => Items.Count;
+ public bool IsReadOnly => false;
+ public int IndexOf(ClipboardItem item) => Items.IndexOf(item);
+
+ public void Insert(int index, ClipboardItem item) => Items.Insert(index, item);
+
+ public void RemoveAt(int index) => Items.RemoveAt(index);
+
+ public ClipboardItem this[int index]
+ {
+ get => Items[index];
+ set => Items[index] = value;
+ }
+ #endregion
+
+ #region Methods
+
+ /// <summary>
+ /// Clears the manager
+ /// </summary>
+ public void Reset() => Init(null);
+
+ /// <summary>
+ /// Clears and init clipboard
+ /// </summary>
+ /// <param name="slicerFile"></param>
+ public void Init(FileFormat slicerFile)
+ {
+ Clear();
+ SlicerFile = slicerFile;
+ if(slicerFile is null) return;
+ var clip = new ClipboardItem(SlicerFile, "Original layers");
+ for (int layerIndex = 0; layerIndex < SlicerFile.LayerCount; layerIndex++)
+ {
+ clip.Add(SlicerFile[layerIndex].Clone());
+ }
+
+ Add(clip);
+ CurrentIndex = 0;
+ }
+
+ /// <summary>
+ /// Snapshot layers and prepare manager to collect modified layers with <see cref="Clip"/>
+ /// </summary>
+ public void Snapshot(LayerManager layerManager = null)
+ {
+ SnapshotLayerManager = layerManager ?? SlicerFile.LayerManager.Clone();
+ }
+
+ /// <summary>
+ /// Collect differences and create a clip
+ /// </summary>
+ public void Clip(string description = null, LayerManager layerManagerSnapshot = null)
+ {
+ if(!(layerManagerSnapshot is null)) Snapshot(layerManagerSnapshot);
+ if (SnapshotLayerManager is null) throw new InvalidOperationException("A snapshot is required before perform a clip");
+ var clip = new ClipboardItem(SlicerFile, description);
+
+ int layerIndex = 0;
+ for (;
+ layerIndex < SlicerFile.LayerCount
+ && layerIndex < SnapshotLayerManager.Count
+ ; layerIndex++)
+ {
+ //if(SnapshotLayerManager.Count - 1 < layerIndex) break;
+ if(SnapshotLayerManager[layerIndex].Equals(SlicerFile[layerIndex])) continue;
+
+ clip.Add(SlicerFile[layerIndex].Clone());
+ }
+
+ // Collect leftovers from snapshot
+ // This happens when current layer manager has less layers then the snapshot/previous
+ // So we need to preserve them
+ for (;
+ layerIndex < SlicerFile.LayerCount;
+ layerIndex++)
+ {
+ clip.Add(SlicerFile[layerIndex].Clone());
+ }
+
+ SnapshotLayerManager = null;
+
+ if (clip.Count == 0)
+ {
+ if(Count > 0 && this[0].LayerCount == clip.LayerCount)
+ return;
+ }
+
+ ChangedByClip = true;
+ var oldCurrentIndex = _currentIndex;
+ CurrentIndex = -1;
+
+ // Remove all redo's for integrity
+ for (int i = oldCurrentIndex - 1; i >= 0; i--)
+ {
+ RemoveAt(i);
+ }
+
+
+ Insert(0, clip);
+
+ CurrentIndex = 0;
+ ChangedByClip = false;
+ }
+
+ public void Undo()
+ {
+ if (!CanUndo) return;
+ CurrentIndex++;
+ }
+
+ public void Redo()
+ {
+ if (!CanRedo) return;
+ CurrentIndex--;
+ }
+
+ public void SetToOldest() => CurrentIndex = Count-1;
+ public void SetToNewest() => CurrentIndex = 0;
+
+ /*public bool SetTo(int index)
+ {
+ if (index == _currentIndex || index < 0 || index >= Count) return false;
+ bool changed = false;
+ int dir = _currentIndex < index ? 1 : -1;
+
+ for (int i = _currentIndex+dir; i >= 0 && i < Count; i+=dir)
+ {
+ var layerManager = SlicerFile.LayerManager;
+ var clip = this[i];
+ if (layerManager.Count != clip.LayerCount) // Need resize layer manager
+ {
+ layerManager = layerManager.Reallocate(clip.LayerCount);
+ ReallocatedLayerCount = true;
+ }
+
+ layerManager.AddLayers(clip);
+
+ layerManager.BoundingRectangle = Rectangle.Empty;
+ SlicerFile.LayerManager = layerManager.Clone();
+ changed = true;
+ if (i == index) break;
+ }
+
+ if (changed)
+ {
+ CurrentIndex = index;
+ }
+
+ return changed;
+ }*/
+
+ #endregion
+ }
+}
diff --git a/UVtools.Core/Objects/StringTag.cs b/UVtools.Core/Objects/StringTag.cs
index 0e22913..d19d686 100644
--- a/UVtools.Core/Objects/StringTag.cs
+++ b/UVtools.Core/Objects/StringTag.cs
@@ -11,12 +11,28 @@ using System.Collections.Generic;
namespace UVtools.Core.Objects
{
- public class StringTag : IComparable<StringTag>
+ public class StringTag : BindableBase, IEquatable<StringTag>, IComparable<StringTag>
{
- public string Content { get; set; }
+ private string _content;
+ private object _tag;
- public object Tag { get; set; }
- public string TagString => Tag.ToString();
+ public string Content
+ {
+ get => _content;
+ set => RaiseAndSetIfChanged(ref _content, value);
+ }
+
+ public object Tag
+ {
+ get => _tag;
+ set => RaiseAndSetIfChanged(ref _tag, value);
+ }
+
+ public string TagString
+ {
+ get => Tag.ToString();
+ set => Tag = value;
+ }
public StringTag(object content, object tag = null)
{
@@ -29,6 +45,27 @@ namespace UVtools.Core.Objects
Tag = tag;
}
+ public bool Equals(StringTag other)
+ {
+ return _content == other._content && Equals(_tag, other._tag);
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (ReferenceEquals(null, obj)) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ if (obj.GetType() != GetType()) return false;
+ return Equals((StringTag) obj);
+ }
+
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ return ((_content != null ? _content.GetHashCode() : 0) * 397) ^ (_tag != null ? _tag.GetHashCode() : 0);
+ }
+ }
+
private sealed class ContentEqualityComparer : IEqualityComparer<StringTag>
{
public bool Equals(StringTag x, StringTag y)
diff --git a/UVtools.Core/Operations/OperationLayerClone.cs b/UVtools.Core/Operations/OperationLayerClone.cs
index 2271135..eb77b6c 100644
--- a/UVtools.Core/Operations/OperationLayerClone.cs
+++ b/UVtools.Core/Operations/OperationLayerClone.cs
@@ -64,5 +64,12 @@ namespace UVtools.Core.Operations
}
#endregion
+
+ public override string ToString()
+ {
+ var result = $"[Clones: {Clones}]" + LayerRangeString;
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
}
}
diff --git a/UVtools.Core/Operations/OperationLayerImport.cs b/UVtools.Core/Operations/OperationLayerImport.cs
index df45ad4..4a22279 100644
--- a/UVtools.Core/Operations/OperationLayerImport.cs
+++ b/UVtools.Core/Operations/OperationLayerImport.cs
@@ -178,7 +178,12 @@ namespace UVtools.Core.Operations
return (uint)(totalLayers + Files.Count - (ReplaceStartLayer ? 1 : 0));
}
-
+ public override string ToString()
+ {
+ var result = $"[Files: {Count}]" + LayerRangeString;
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
#endregion
}
diff --git a/UVtools.Core/Operations/OperationLayerReHeight.cs b/UVtools.Core/Operations/OperationLayerReHeight.cs
index 32bf0bc..f92c7ce 100644
--- a/UVtools.Core/Operations/OperationLayerReHeight.cs
+++ b/UVtools.Core/Operations/OperationLayerReHeight.cs
@@ -119,5 +119,12 @@ namespace UVtools.Core.Operations
return (IsMultiply ? 'x' : '÷') + $" {Modifier} → {LayerCount} layers at {LayerHeight}mm";
}
}
+
+ public override string ToString()
+ {
+ var result = $"[Layer Count: {Item.LayerCount}] [Layer Height: {Item.LayerHeight}]" + LayerRangeString;
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
}
}
diff --git a/UVtools.Core/Operations/OperationMove.cs b/UVtools.Core/Operations/OperationMove.cs
index b25b611..64956bd 100644
--- a/UVtools.Core/Operations/OperationMove.cs
+++ b/UVtools.Core/Operations/OperationMove.cs
@@ -236,5 +236,12 @@ namespace UVtools.Core.Operations
CalculateDstRoi();
return IsWithinBoundary;
}
+
+ public override string ToString()
+ {
+ var result = $"[{ROI} -> {DstRoi}] [Cut: {IsCutMove}]" + LayerRangeString;
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
}
}
diff --git a/UVtools.Core/Operations/OperationPattern.cs b/UVtools.Core/Operations/OperationPattern.cs
index 7d66f7d..fcf8f4e 100644
--- a/UVtools.Core/Operations/OperationPattern.cs
+++ b/UVtools.Core/Operations/OperationPattern.cs
@@ -254,5 +254,12 @@ namespace UVtools.Core.Operations
var volume = GetPatternVolume;
return IsWithinBoundary = volume.Width <= ImageWidth && volume.Height <= ImageHeight;
}
+
+ public override string ToString()
+ {
+ var result = $"[Rows: {Rows}] [Cols: {Cols}]" + LayerRangeString;
+ if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
+ return result;
+ }
}
}
diff --git a/UVtools.Core/Operations/OperationRepairLayers.cs b/UVtools.Core/Operations/OperationRepairLayers.cs
index d54054e..2622432 100644
--- a/UVtools.Core/Operations/OperationRepairLayers.cs
+++ b/UVtools.Core/Operations/OperationRepairLayers.cs
@@ -96,6 +96,5 @@ namespace UVtools.Core.Operations
[XmlIgnore]
public List<LayerIssue> Issues { get; set; }
-
}
}
diff --git a/UVtools.Core/UVtools.Core.csproj b/UVtools.Core/UVtools.Core.csproj
index 1966dac..b4214c1 100644
--- a/UVtools.Core/UVtools.Core.csproj
+++ b/UVtools.Core/UVtools.Core.csproj
@@ -10,7 +10,7 @@
<RepositoryUrl>https://github.com/sn4k3/UVtools</RepositoryUrl>
<PackageProjectUrl>https://github.com/sn4k3/UVtools</PackageProjectUrl>
<Description>MSLA/DLP, file analysis, repair, conversion and manipulation</Description>
- <Version>1.1.3</Version>
+ <Version>1.2.0</Version>
<Copyright>Copyright © 2020 PTRTECH</Copyright>
<PackageIcon>UVtools.png</PackageIcon>
<Platforms>AnyCPU;x64</Platforms>
diff --git a/UVtools.WPF/App.axaml.cs b/UVtools.WPF/App.axaml.cs
index 8df3fa8..d628c8c 100644
--- a/UVtools.WPF/App.axaml.cs
+++ b/UVtools.WPF/App.axaml.cs
@@ -10,10 +10,12 @@ using System;
using System.Diagnostics;
using System.Globalization;
using System.IO;
+using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;
using Avalonia;
+using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
using Avalonia.Media.Imaging;
@@ -174,7 +176,7 @@ namespace UVtools.WPF
var assemblyName = Assembly.GetEntryAssembly().GetName().Name;
uri = new Uri($"avares://{assemblyName}{url}");
}
-
+
var res = AvaloniaLocator.Current.GetService<IAssetLoader>().Open(uri);
return res;
}
@@ -205,5 +207,86 @@ namespace UVtools.WPF
}
#endregion
+
+ #region Assembly Attribute Accessors
+
+ public static Version Version => Assembly.GetExecutingAssembly().GetName().Version;
+ public static string VersionStr => Version.ToString(3);
+
+ public static string AssemblyTitle
+ {
+ get
+ {
+ object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyTitleAttribute), false);
+ if (attributes.Length > 0)
+ {
+ AssemblyTitleAttribute titleAttribute = (AssemblyTitleAttribute)attributes[0];
+ if (titleAttribute.Title != "")
+ {
+ return titleAttribute.Title;
+ }
+ }
+ return System.IO.Path.GetFileNameWithoutExtension(Assembly.GetExecutingAssembly().CodeBase);
+ }
+ }
+
+ public static string AssemblyVersion => Assembly.GetExecutingAssembly().GetName().Version.ToString();
+
+ public static string AssemblyDescription
+ {
+ get
+ {
+ object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyDescriptionAttribute), false);
+ if (attributes.Length == 0)
+ {
+ return "";
+ }
+
+ string description = ((AssemblyDescriptionAttribute)attributes[0]).Description + $"{Environment.NewLine}{Environment.NewLine}Available File Formats:";
+
+ return FileFormat.AvaliableFormats.SelectMany(fileFormat => fileFormat.FileExtensions).Aggregate(description, (current, fileExtension) => current + $"{Environment.NewLine}- {fileExtension.Description} (.{fileExtension.Extension})");
+ }
+ }
+
+ public static string AssemblyProduct
+ {
+ get
+ {
+ object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyProductAttribute), false);
+ if (attributes.Length == 0)
+ {
+ return "";
+ }
+ return ((AssemblyProductAttribute)attributes[0]).Product;
+ }
+ }
+
+ public static string AssemblyCopyright
+ {
+ get
+ {
+ object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyCopyrightAttribute), false);
+ if (attributes.Length == 0)
+ {
+ return "";
+ }
+ return ((AssemblyCopyrightAttribute)attributes[0]).Copyright;
+ }
+ }
+
+ public static string AssemblyCompany
+ {
+ get
+ {
+ object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyCompanyAttribute), false);
+ if (attributes.Length == 0)
+ {
+ return "";
+ }
+ return ((AssemblyCompanyAttribute)attributes[0]).Company;
+ }
+ }
+
+ #endregion
}
}
diff --git a/UVtools.WPF/AppSettings.cs b/UVtools.WPF/AppSettings.cs
index e33b085..7ea39c9 100644
--- a/UVtools.WPF/AppSettings.cs
+++ b/UVtools.WPF/AppSettings.cs
@@ -14,8 +14,6 @@ namespace UVtools.WPF
{
public static class AppSettings
{
- public static Version Version => Assembly.GetEntryAssembly().GetName().Version;
- public static string VersionStr => Version.ToString(3);
// Supported ZoomLevels for Layer Preview.
// These settings eliminate very small zoom factors from the ImageBox default values,
// while ensuring that 4K/5K build plates can still easily fit on screen.
@@ -43,82 +41,6 @@ namespace UVtools.WPF
/// </summary>
public const byte MinLockedZoomLevel = 200;
- #region Assembly Attribute Accessors
-
- public static string AssemblyTitle
- {
- get
- {
- object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyTitleAttribute), false);
- if (attributes.Length > 0)
- {
- AssemblyTitleAttribute titleAttribute = (AssemblyTitleAttribute)attributes[0];
- if (titleAttribute.Title != "")
- {
- return titleAttribute.Title;
- }
- }
- return System.IO.Path.GetFileNameWithoutExtension(Assembly.GetExecutingAssembly().CodeBase);
- }
- }
-
- public static string AssemblyVersion => Assembly.GetExecutingAssembly().GetName().Version.ToString();
-
- public static string AssemblyDescription
- {
- get
- {
- object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyDescriptionAttribute), false);
- if (attributes.Length == 0)
- {
- return "";
- }
-
- string description = ((AssemblyDescriptionAttribute)attributes[0]).Description + $"{Environment.NewLine}{Environment.NewLine}Available File Formats:";
-
- return FileFormat.AvaliableFormats.SelectMany(fileFormat => fileFormat.FileExtensions).Aggregate(description, (current, fileExtension) => current + $"{Environment.NewLine}- {fileExtension.Description} (.{fileExtension.Extension})");
- }
- }
-
- public static string AssemblyProduct
- {
- get
- {
- object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyProductAttribute), false);
- if (attributes.Length == 0)
- {
- return "";
- }
- return ((AssemblyProductAttribute)attributes[0]).Product;
- }
- }
-
- public static string AssemblyCopyright
- {
- get
- {
- object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyCopyrightAttribute), false);
- if (attributes.Length == 0)
- {
- return "";
- }
- return ((AssemblyCopyrightAttribute)attributes[0]).Copyright;
- }
- }
-
- public static string AssemblyCompany
- {
- get
- {
- object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyCompanyAttribute), false);
- if (attributes.Length == 0)
- {
- return "";
- }
- return ((AssemblyCompanyAttribute)attributes[0]).Company;
- }
- }
-
- #endregion
+
}
}
diff --git a/UVtools.WPF/Assets/Icons/clipboard-32x32.png b/UVtools.WPF/Assets/Icons/clipboard-32x32.png
new file mode 100644
index 0000000..e4d7c2b
--- /dev/null
+++ b/UVtools.WPF/Assets/Icons/clipboard-32x32.png
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/layers-alt-32x32.png b/UVtools.WPF/Assets/Icons/layers-alt-32x32.png
new file mode 100644
index 0000000..b1ed164
--- /dev/null
+++ b/UVtools.WPF/Assets/Icons/layers-alt-32x32.png
Binary files differ
diff --git a/UVtools.WPF/Assets/Icons/redo-16x16.png b/UVtools.WPF/Assets/Icons/redo-16x16.png
new file mode 100644
index 0000000..3408d68
--- /dev/null
+++ b/UVtools.WPF/Assets/Icons/redo-16x16.png
Binary files differ
diff --git a/UVtools.WPF/MainWindow.Clipboard.cs b/UVtools.WPF/MainWindow.Clipboard.cs
new file mode 100644
index 0000000..e69c912
--- /dev/null
+++ b/UVtools.WPF/MainWindow.Clipboard.cs
@@ -0,0 +1,57 @@
+/*
+ * GNU AFFERO GENERAL PUBLIC LICENSE
+ * Version 3, 19 November 2007
+ * Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ * Everyone is permitted to copy and distribute verbatim copies
+ * of this license document, but changing it is not allowed.
+ */
+
+
+using System;
+using System.ComponentModel;
+using Avalonia.Controls;
+using Avalonia.Threading;
+using MessageBox.Avalonia.Enums;
+using UVtools.WPF.Extensions;
+
+namespace UVtools.WPF
+{
+ public partial class MainWindow
+ {
+ public ListBox ClipboardList;
+
+ public void InitClipboardLayers()
+ {
+ ClipboardList = this.FindControl<ListBox>(nameof(ClipboardList));
+ Clipboard.PropertyChanged += ClipboardOnPropertyChanged;
+ }
+
+ private void ClipboardOnPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == nameof(Clipboard.CurrentIndex))
+ {
+ if (Clipboard.CurrentIndex < 0 || Clipboard.ChangedByClip) return;
+
+ if (Clipboard.ReallocatedLayerCount)
+ {
+ DispatcherTimer.RunOnce(() =>
+ {
+ RefreshProperties();
+ ResetDataContext();
+ }, TimeSpan.FromMilliseconds(1));
+ }
+
+ ShowLayer();
+ return;
+ }
+ }
+
+ public async void ClipboardClear()
+ {
+ if (await this.MessageBoxQuestion("Are you sure you want to clear the clipboard?\n" +
+ "Current layers will be placed as original layers\n" +
+ "This action is permanent!", "Clear clipboard?") != ButtonResult.Yes) return;
+ Clipboard.Clear(true);
+ }
+ }
+}
diff --git a/UVtools.WPF/MainWindow.Information.cs b/UVtools.WPF/MainWindow.Information.cs
index 92437c3..f5ee13d 100644
--- a/UVtools.WPF/MainWindow.Information.cs
+++ b/UVtools.WPF/MainWindow.Information.cs
@@ -7,7 +7,9 @@
*/
using System;
using System.Collections;
+using System.Collections.Generic;
using System.Collections.ObjectModel;
+using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
@@ -16,6 +18,7 @@ using Avalonia;
using Avalonia.Controls;
using Avalonia.Input;
using MessageBox.Avalonia.Enums;
+using UVtools.Core.Objects;
using UVtools.WPF.Extensions;
using UVtools.WPF.Structures;
using Bitmap = Avalonia.Media.Imaging.Bitmap;
@@ -27,32 +30,80 @@ namespace UVtools.WPF
{
public ObservableCollection<SlicerProperty> SlicerProperties { get; } = new ObservableCollection<SlicerProperty>();
public DataGrid PropertiesGrid;
+ public DataGrid CurrentLayerGrid;
private uint _visibleThumbnailIndex;
private Bitmap _visibleThumbnailImage;
+ private ObservableCollection<StringTag> _currentLayerProperties = new ObservableCollection<StringTag>();
- public void InitInformation()
+ public ObservableCollection<StringTag> CurrentLayerProperties
{
- PropertiesGrid = this.Find<DataGrid>("PropertiesGrid");
- PropertiesGrid.KeyUp += PropertiesGridOnKeyUp;
+ get => _currentLayerProperties;
+ set => RaiseAndSetIfChanged(ref _currentLayerProperties, value);
}
- private void PropertiesGridOnKeyUp(object? sender, KeyEventArgs e)
+ public void InitInformation()
{
- switch (e.Key)
+ PropertiesGrid = this.Find<DataGrid>(nameof(PropertiesGrid));
+ CurrentLayerGrid = this.Find<DataGrid>(nameof(CurrentLayerGrid));
+ PropertiesGrid.KeyUp += GridOnKeyUp;
+ CurrentLayerGrid.KeyUp += GridOnKeyUp;
+ /*CurrentLayerGrid.BeginningEdit += (sender, e) =>
{
- case Key.Escape:
- PropertiesGrid.SelectedItems.Clear();
- break;
- case Key.Multiply:
- var selectedItems = PropertiesGrid.SelectedItems.OfType<SlicerProperty>().ToList();
- PropertiesGrid.SelectedItems.Clear();
- foreach (SlicerProperty item in SlicerProperties)
+ if (e.Row.DataContext is StringTag stringTag)
+ {
+ if (e.Column.DisplayIndex == 0
+ || e.Row.DataContext.ToString() != nameof(LayerCache.Layer.ExposureTime)
+ && e.Row.DataContext.ToString() != nameof(LayerCache.Layer.LightPWM)
+ )
{
- if (!selectedItems.Contains(item))
- PropertiesGrid.SelectedItems.Add(item);
+ e.Cancel = true;
}
- break;
+ }
+ else
+ {
+ e.Cancel = true;
+ }
+ };
+ CurrentLayerGrid.RowEditEnding += (sender, e) =>
+ {
+ if (e.EditAction == DataGridEditAction.Cancel) return;
+ if (!(e.Row.DataContext is StringTag stringTag)) return;
+ if (float.TryParse(stringTag.TagString, out var result)) return;
+ e.Cancel = true;
+ };
+ CurrentLayerGrid.RowEditEnded += (sender, e) =>
+ {
+ if (e.EditAction == DataGridEditAction.Cancel) return;
+ if (!(e.Row.DataContext is StringTag stringTag)) return;
+ switch (stringTag.Content)
+ {
+ //case nameof(LayerCache.)
+ }
+ };*/
+
+ }
+
+ private void GridOnKeyUp(object? sender, KeyEventArgs e)
+ {
+ if (sender is DataGrid dataGrid)
+ {
+ switch (e.Key)
+ {
+ case Key.Escape:
+ dataGrid.SelectedItems.Clear();
+ break;
+ case Key.Multiply:
+ foreach (var item in dataGrid.Items)
+ {
+ if (dataGrid.SelectedItems.Contains(item))
+ dataGrid.SelectedItems.Remove(item);
+ else
+ dataGrid.SelectedItems.Add(item);
+ }
+
+ break;
+ }
}
}
diff --git a/UVtools.WPF/MainWindow.LayerPreview.cs b/UVtools.WPF/MainWindow.LayerPreview.cs
index fca4b2b..d2b3872 100644
--- a/UVtools.WPF/MainWindow.LayerPreview.cs
+++ b/UVtools.WPF/MainWindow.LayerPreview.cs
@@ -10,6 +10,7 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
+using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
@@ -26,6 +27,7 @@ using Emgu.CV.Structure;
using Emgu.CV.Util;
using UVtools.Core;
using UVtools.Core.Extensions;
+using UVtools.Core.Objects;
using UVtools.Core.PixelEditor;
using UVtools.WPF.Controls;
using UVtools.WPF.Extensions;
@@ -338,15 +340,31 @@ namespace UVtools.WPF
if (!RaiseAndSetIfChanged(ref _actualLayer, value)) return;
ShowLayer();
InvalidateLayerNavigation();
+ var layer = LayerCache.Layer;
+ CurrentLayerProperties.Clear();
+ CurrentLayerProperties.Add(new StringTag(nameof(layer.Index), $"{layer.Index}"));
+ //CurrentLayerProperties.Add(new KeyValuePair<string, string>(nameof(layer.Filename), layer.Filename));
+ CurrentLayerProperties.Add(new StringTag(nameof(layer.PositionZ), $"{layer.PositionZ.ToString(CultureInfo.InvariantCulture)}mm"));
+ CurrentLayerProperties.Add(new StringTag(nameof(layer.IsBottomLayer), layer.IsBottomLayer.ToString()));
+ //CurrentLayerProperties.Add(new StringTag(nameof(layer.BoundingRectangle), layer.BoundingRectangle.ToString()));
+ //CurrentLayerProperties.Add(new StringTag(nameof(layer.NonZeroPixelCount), layer.NonZeroPixelCount.ToString()));
+ CurrentLayerProperties.Add(new StringTag(nameof(layer.ExposureTime), $"{layer.ExposureTime.ToString(CultureInfo.InvariantCulture)}s"));
+ CurrentLayerProperties.Add(new StringTag(nameof(layer.LiftHeight), $"{layer.LiftHeight.ToString(CultureInfo.InvariantCulture)}mm @ {layer.LiftSpeed.ToString(CultureInfo.InvariantCulture)}mm/min"));
+ //CurrentLayerProperties.Adnew StringTagg>(nameof(layer.LiftSpeed), $"{layer.LiftSpeed.ToString(CultureInfo.InvariantCulture)}mm/min"));
+ CurrentLayerProperties.Add(new StringTag(nameof(layer.RetractSpeed), $"{layer.RetractSpeed.ToString(CultureInfo.InvariantCulture)}mm/min"));
+ CurrentLayerProperties.Add(new StringTag(nameof(layer.LayerOffTime), $"{layer.LayerOffTime.ToString(CultureInfo.InvariantCulture)}s"));
+ CurrentLayerProperties.Add(new StringTag(nameof(layer.LightPWM), layer.LightPWM.ToString()));
}
}
public void ForceUpdateActualLayer(uint layerIndex = 0)
{
- _actualLayer = layerIndex;
- ShowLayer();
+ //_actualLayer = layerIndex;
+ /*ShowLayer();
InvalidateLayerNavigation();
- RaisePropertyChanged(nameof(ActualLayer));
+ RaisePropertyChanged(nameof(ActualLayer));*/
+ _actualLayer = uint.MaxValue;
+ ActualLayer = layerIndex;
}
public void InvalidateLayerNavigation()
diff --git a/UVtools.WPF/MainWindow.PixelEditor.cs b/UVtools.WPF/MainWindow.PixelEditor.cs
index 93841da..2800516 100644
--- a/UVtools.WPF/MainWindow.PixelEditor.cs
+++ b/UVtools.WPF/MainWindow.PixelEditor.cs
@@ -387,6 +387,8 @@ namespace UVtools.WPF
{
IsGUIEnabled = false;
+ Clipboard.Snapshot();
+
var task = await Task.Factory.StartNew(async () =>
{
ShowProgressWindow("Drawing pixels");
@@ -411,6 +413,7 @@ namespace UVtools.WPF
IsGUIEnabled = true;
+ Clipboard.Clip($"Draw {Drawings.Count} modifications");
if (Settings.PixelEditor.PartialUpdateIslandsOnEditing)
{
diff --git a/UVtools.WPF/MainWindow.axaml b/UVtools.WPF/MainWindow.axaml
index 8492e14..5fd1b26 100644
--- a/UVtools.WPF/MainWindow.axaml
+++ b/UVtools.WPF/MainWindow.axaml
@@ -312,7 +312,7 @@
<Grid
IsVisible="{Binding IsFileLoaded}"
- RowDefinitions="Auto,Auto,Auto,*">
+ RowDefinitions="Auto,Auto,Auto,*,Auto,Auto">
<!-- Thumbnails -->
<StackPanel
IsVisible="{Binding SlicerFile.CreatedThumbnailsCount}"
@@ -446,7 +446,7 @@
CanUserReorderColumns="True"
CanUserResizeColumns="True"
CanUserSortColumns="True"
- GridLinesVisibility="All"
+ GridLinesVisibility="Horizontal"
IsReadOnly="True"
ClipboardCopyMode="IncludeHeader"
Items="{Binding SlicerProperties}">
@@ -462,7 +462,36 @@
Width="Auto" />
</DataGrid.Columns>
+ </DataGrid>
+
+ <TextBlock Grid.Row="4"
+ Text="Layer data"
+ ToolTip.Tip="Shows the properties for the current selected layer"
+ FontWeight="Bold"
+ TextAlignment="Center"
+ Padding="0,10"/>
+ <DataGrid
+ IsVisible="{Binding IsFileLoaded}"
+ Name="CurrentLayerGrid"
+ Grid.Row="5"
+ CanUserReorderColumns="False"
+ CanUserResizeColumns="False"
+ CanUserSortColumns="False"
+ GridLinesVisibility="Horizontal"
+ IsReadOnly="True"
+ ClipboardCopyMode="IncludeHeader"
+ Items="{Binding CurrentLayerProperties}">
+ <DataGrid.Columns>
+ <DataGridTextColumn Header="Name"
+ Binding="{Binding Content}"
+ Width="Auto" />
+ <DataGridTextColumn Header="Value"
+ Binding="{Binding TagString}"
+ Width="Auto" />
+ </DataGrid.Columns>
+
</DataGrid>
+
</Grid>
</TabItem>
@@ -1239,30 +1268,86 @@
</TabItem>
- <!--
+
<TabItem
- Name="TabLayers"
- ToolTip.Tip="Layers">
+ Name="TabClipboard"
+ IsEnabled="{Binding IsFileLoaded}"
+ ToolTip.Tip="Clipboard">
<TabItem.Header>
<StackPanel VerticalAlignment="Center" Orientation="Horizontal">
- <Image Source="/Assets/Icons/book-32x32.png" Width="32"/>
- <TextBlock Margin="5,0,0,0">Layers</TextBlock>
+ <Image Source="/Assets/Icons/clipboard-32x32.png" Width="32"/>
+ <!--<TextBlock Margin="5,0,0,0">Clipboard</TextBlock>!-->
</StackPanel>
</TabItem.Header>
- <TreeView Items="{Binding SlicerFile.LayerManager.Layers}"
- AutoScrollToSelectedItem="True">
+ <Grid RowDefinitions="Auto,*">
+ <StackPanel Orientation="Horizontal" Spacing="2">
+ <!--
+ <Button
+ IsEnabled="{Binding Clipboard.Items.Count}"
+ Command="{Binding Clipboard.SetToOldest}"
+ ToolTip.Tip="Go to first clip">
+ <Image Source="/Assets/Icons/arrow-end-16x16.png" Width="16"/>
+ </Button>
+ !-->
+
+ <Button
+ IsEnabled="{Binding Clipboard.CanUndo}"
+ Command="{Binding Clipboard.Undo}"
+ HotKey="Ctrl + Z"
+ ToolTip.Tip="Undo [Ctrl + Z]">
+ <Image Source="/Assets/Icons/undo-16x16.png" Width="16"/>
+ </Button>
- <TreeView.ItemTemplate>
- <DataTemplate>
- <TreeViewItem Header="{Binding Index}"/>
- </DataTemplate>
- </TreeView.ItemTemplate>
+ <TextBlock VerticalAlignment="Center">
+ <TextBlock.Text>
+ <MultiBinding StringFormat="\{0\}/\{1\}">
+ <Binding Path="Clipboard.CurrentIndexCountStr"/>
+ <Binding Path="Clipboard.Items.Count"/>
+ </MultiBinding>
+ </TextBlock.Text>
+ </TextBlock>
- </TreeView>
-
+ <Button
+ IsEnabled="{Binding Clipboard.CanRedo}"
+ Command="{Binding Clipboard.Redo}"
+ HotKey="Ctrl + Y"
+ ToolTip.Tip="Redo [Ctrl + Y]"
+ >
+ <Image Source="/Assets/Icons/redo-16x16.png" Width="16"/>
+ </Button>
+
+ <!--
+ <Button
+ IsEnabled="{Binding Clipboard.Items.Count}"
+ Command="{Binding Clipboard.SetToNewest}"
+ ToolTip.Tip="Go to first clip">
+ <Image Source="/Assets/Icons/arrow-top-16x16.png" Width="16"/>
+ </Button>
+ !-->
+
+ </StackPanel>
+
+ <StackPanel Orientation="Horizontal" Spacing="2" HorizontalAlignment="Right">
+ <Button
+ IsEnabled="{Binding Clipboard.Items.Count}"
+ Command="{Binding ClipboardClear}"
+ ToolTip.Tip="Clear all clips"
+ >
+ <Image Source="/Assets/Icons/delete-16x16.png" Width="16"/>
+ </Button>
+ </StackPanel>
+
+ <ListBox
+ Grid.Row="1"
+ Name="ClipboardList"
+ SelectionMode="Single"
+ AutoScrollToSelectedItem="True"
+ SelectedIndex="{Binding Clipboard.CurrentIndex}"
+ Items="{Binding Clipboard.Items}" />
+ </Grid>
</TabItem>
- !-->
+
<TabItem
Name="TabLog"
diff --git a/UVtools.WPF/MainWindow.axaml.cs b/UVtools.WPF/MainWindow.axaml.cs
index 1cff077..e91f678 100644
--- a/UVtools.WPF/MainWindow.axaml.cs
+++ b/UVtools.WPF/MainWindow.axaml.cs
@@ -25,6 +25,7 @@ using MessageBox.Avalonia.Enums;
using UVtools.Core;
using UVtools.Core.Extensions;
using UVtools.Core.FileFormats;
+using UVtools.Core.Managers;
using UVtools.Core.Operations;
using UVtools.WPF.Controls;
using UVtools.WPF.Controls.Tools;
@@ -44,6 +45,7 @@ namespace UVtools.WPF
public AppVersionChecker VersionChecker => App.VersionChecker;
public UserSettings Settings => UserSettings.Instance;
public FileFormat SlicerFile => App.SlicerFile;
+ public ClipboardManager Clipboard => ClipboardManager.Instance;
#endregion
#region Controls
@@ -347,6 +349,7 @@ namespace UVtools.WPF
InitInformation();
InitIssues();
InitPixelEditor();
+ InitClipboardLayers();
InitLayerPreview();
@@ -383,12 +386,11 @@ namespace UVtools.WPF
var windowStateObs = this.GetObservable(WindowStateProperty);
windowStateObs.Subscribe(size => UpdateLayerTrackerHighlightIssues());
-
UpdateTitle();
if (Settings.General.StartMaximized
- || ClientSize.Width > Screens.Primary.Bounds.Width
- || ClientSize.Height > Screens.Primary.Bounds.Height)
+ || ClientSize.Width > Screens.Primary.Bounds.Width / Screens.Primary.PixelDensity
+ || ClientSize.Height > Screens.Primary.Bounds.Height / Screens.Primary.PixelDensity)
{
WindowState = WindowState.Maximized;
}
@@ -417,6 +419,12 @@ namespace UVtools.WPF
{
ProcessFile(About.DemoFile);
}
+
+ DispatcherTimer.Run(() =>
+ {
+ UpdateTitle();
+ return true;
+ }, TimeSpan.FromSeconds(1));
}
private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
@@ -612,6 +620,9 @@ namespace UVtools.WPF
public void CloseFile()
{
if (SlicerFile is null) return;
+
+ ClipboardManager.Instance.Reset();
+
SlicerFile?.Dispose();
App.SlicerFile = null;
@@ -692,7 +703,7 @@ namespace UVtools.WPF
{
var result =
await this.MessageBoxQuestion(
- $"Do you like to auto-update {About.Software} v{AppSettings.Version} to v{VersionChecker.Version}?\n\n" +
+ $"Do you like to auto-update {About.Software} v{App.Version} to v{VersionChecker.Version}?\n\n" +
"Yes: Auto update\n" +
"No: Manual update\n" +
"Cancel: No action", "Update UVtools?", ButtonEnum.YesNoCancel);
@@ -739,10 +750,12 @@ namespace UVtools.WPF
private void UpdateTitle()
{
Title = (SlicerFile is null
- ? $"{About.Software} Version: {AppSettings.VersionStr}"
- : $"{About.Software} File: {Path.GetFileName(SlicerFile.FileFullPath)} ({Math.Round(LastStopWatch.ElapsedMilliseconds / 1000m, 2)}s) Version: {AppSettings.VersionStr}")
+ ? $"{About.Software} Version: {App.VersionStr}"
+ : $"{About.Software} File: {Path.GetFileName(SlicerFile.FileFullPath)} ({Math.Round(LastStopWatch.ElapsedMilliseconds / 1000m, 2)}s) Version: {App.VersionStr}")
;
+ Title += $" RAM: {SizeExtensions.SizeSuffix(Environment.WorkingSet)}";
+
#if DEBUG
Title += " [DEBUG]";
#endif
@@ -825,6 +838,8 @@ namespace UVtools.WPF
return;
}
+ ClipboardManager.Instance.Init(SlicerFile);
+
if (!(SlicerFile.ConvertToFormats is null))
{
List<MenuItem> menuItems = new List<MenuItem>();
@@ -1182,11 +1197,11 @@ namespace UVtools.WPF
IsGUIEnabled = false;
- await Task.Factory.StartNew(() =>
+ LayerManager backup = null;
+ var result = await Task.Factory.StartNew(() =>
{
ShowProgressWindow(baseOperation.ProgressTitle);
- var backup = SlicerFile.LayerManager.Clone();
-
+ backup = SlicerFile.LayerManager.Clone();
/*var backup = new Layer[baseOperation.LayerRangeCount];
uint i = 0;
@@ -1261,6 +1276,8 @@ namespace UVtools.WPF
default:
throw new NotImplementedException();
}
+
+ return true;
}
catch (OperationCanceledException)
{
@@ -1276,24 +1293,33 @@ namespace UVtools.WPF
Dispatcher.UIThread.InvokeAsync(async () =>
await this.MessageBoxError(ex.ToString(), $"{baseOperation.Title} Error"));
}
+
+ return false;
});
+
IsGUIEnabled = true;
- ShowLayer();
- RefreshProperties();
- ResetDataContext();
+ if (result)
+ {
+ string description = baseOperation.ToString();
+ if (!description.StartsWith(baseOperation.Title)) description = $"{baseOperation.Title}: {description}";
+ ClipboardManager.Instance.Clip(description, backup);
- CanSave = true;
+ ShowLayer();
+ RefreshProperties();
+ ResetDataContext();
- switch (baseOperation)
- {
- // Tools
- case OperationRepairLayers operation:
- OnClickDetectIssues();
- break;
- }
+ CanSave = true;
+ switch (baseOperation)
+ {
+ // Tools
+ case OperationRepairLayers operation:
+ OnClickDetectIssues();
+ break;
+ }
+ }
return true;
}
diff --git a/UVtools.WPF/Structures/AppVersionChecker.cs b/UVtools.WPF/Structures/AppVersionChecker.cs
index 0d9f333..57d9ba0 100644
--- a/UVtools.WPF/Structures/AppVersionChecker.cs
+++ b/UVtools.WPF/Structures/AppVersionChecker.cs
@@ -111,9 +111,9 @@ namespace UVtools.WPF.Structures
string tag_name = json.tag_name;
if (string.IsNullOrEmpty(tag_name)) return false;
tag_name = tag_name.Trim(' ', 'v', 'V');
- Debug.WriteLine($"Version checker: v{AppSettings.VersionStr} <=> v{tag_name}");
+ Debug.WriteLine($"Version checker: v{App.VersionStr} <=> v{tag_name}");
- if (string.Compare(tag_name, AppSettings.VersionStr, StringComparison.OrdinalIgnoreCase) > 0)
+ if (string.Compare(tag_name, App.VersionStr, StringComparison.OrdinalIgnoreCase) > 0)
{
Dispatcher.UIThread.InvokeAsync(() =>
{
@@ -190,7 +190,7 @@ namespace UVtools.WPF.Structures
using (var stream = File.CreateText(upgradeFile))
{
stream.WriteLine("#!/bin/bash");
- stream.WriteLine($"echo {About.Software} v{AppSettings.Version} updater script");
+ stream.WriteLine($"echo {About.Software} v{App.Version} updater script");
stream.WriteLine($"cd '{App.ApplicationPath}'");
stream.WriteLine($"killall {About.Software}");
stream.WriteLine("sleep 0.5");
diff --git a/UVtools.WPF/Structures/OperationProfiles.cs b/UVtools.WPF/Structures/OperationProfiles.cs
index c351776..fc35809 100644
--- a/UVtools.WPF/Structures/OperationProfiles.cs
+++ b/UVtools.WPF/Structures/OperationProfiles.cs
@@ -49,15 +49,14 @@ namespace UVtools.WPF.Structures
#endregion
#region Singleton
- private static OperationProfiles _instance;
+
+ private static Lazy<OperationProfiles> _instanceHolder =
+ new Lazy<OperationProfiles>(() => new OperationProfiles());
+
/// <summary>
/// Instance of <see cref="UserSettings"/> (singleton)
/// </summary>
- public static OperationProfiles Instance
- {
- get => _instance ??= new OperationProfiles();
- internal set => _instance = value;
- }
+ public static OperationProfiles Instance => _instanceHolder.Value;
//public static List<Operation> Operations => _instance.Operations;
#endregion
@@ -179,7 +178,8 @@ namespace UVtools.WPF.Structures
try
{
using var myXmlReader = new StreamReader(FilePath);
- _instance = (OperationProfiles)serializer.Deserialize(myXmlReader);
+ var instance = (OperationProfiles) serializer.Deserialize(myXmlReader);
+ _instanceHolder = new Lazy<OperationProfiles>(() => instance);
}
catch (Exception e)
{
@@ -254,7 +254,7 @@ namespace UVtools.WPF.Structures
public static List<T> GetOperations<T>()
{
var result = new List<T>();
- foreach (var operation in _instance)
+ foreach (var operation in Instance)
{
if (operation is T operationCast)
{
diff --git a/UVtools.WPF/UVtools.WPF.csproj b/UVtools.WPF/UVtools.WPF.csproj
index 005df41..f93061a 100644
--- a/UVtools.WPF/UVtools.WPF.csproj
+++ b/UVtools.WPF/UVtools.WPF.csproj
@@ -12,7 +12,7 @@
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<RepositoryUrl>https://github.com/sn4k3/UVtools</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
- <Version>1.1.3</Version>
+ <Version>1.2.0</Version>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
@@ -41,6 +41,9 @@
<AvaloniaResource Include="Assets\Icons\*" />
</ItemGroup>
<ItemGroup>
+ <None Remove="Assets\Icons\clipboard-32x32.png" />
+ <None Remove="Assets\Icons\layers-alt-32x32.png" />
+ <None Remove="Assets\Icons\redo-16x16.png" />
<None Remove="Assets\Icons\UVtools.ico" />
</ItemGroup>
<ItemGroup>
@@ -87,6 +90,9 @@
<Compile Update="Controls\Tools\ToolResizeControl.axaml.cs">
<DependentUpon>ToolResizeControl.axaml</DependentUpon>
</Compile>
+ <Compile Update="MainWindow.Clipboard.cs">
+ <DependentUpon>%(Filename)</DependentUpon>
+ </Compile>
<Compile Update="MainWindow.Information.cs">
<DependentUpon>%(Filename)</DependentUpon>
</Compile>
diff --git a/UVtools.WPF/UserSettings.cs b/UVtools.WPF/UserSettings.cs
index 28fbe6e..f0f9d4f 100644
--- a/UVtools.WPF/UserSettings.cs
+++ b/UVtools.WPF/UserSettings.cs
@@ -1047,7 +1047,7 @@ namespace UVtools.WPF
[NotNull]
public string AppVersion
{
- get => _appVersion ??= AppSettings.Version.ToString();
+ get => _appVersion ??= App.Version.ToString();
set => this.RaiseAndSetIfChanged(ref _appVersion, value);
}
@@ -1143,7 +1143,7 @@ namespace UVtools.WPF
public static void SetVersion()
{
- Instance.AppVersion = AppSettings.Version.ToString();
+ Instance.AppVersion = App.Version.ToString();
}
public static object[] PackObjects =>
diff --git a/UVtools.WPF/Windows/AboutWindow.axaml.cs b/UVtools.WPF/Windows/AboutWindow.axaml.cs
index 605042d..61939ce 100644
--- a/UVtools.WPF/Windows/AboutWindow.axaml.cs
+++ b/UVtools.WPF/Windows/AboutWindow.axaml.cs
@@ -7,10 +7,10 @@ namespace UVtools.WPF.Windows
public class AboutWindow : WindowEx
{
public string Software => About.Software;
- public string Version => $"Version: {AppSettings.VersionStr}";
- public string Copyright => AppSettings.AssemblyCopyright;
- public string Company => AppSettings.AssemblyCompany;
- public string Description => AppSettings.AssemblyDescription;
+ public string Version => $"Version: {App.VersionStr}";
+ public string Copyright => App.AssemblyCopyright;
+ public string Company => App.AssemblyCompany;
+ public string Description => App.AssemblyDescription;
public AboutWindow()
diff --git a/UVtools.WPF/Windows/SettingsWindow.axaml.cs b/UVtools.WPF/Windows/SettingsWindow.axaml.cs
index 8cd1a03..cd767ee 100644
--- a/UVtools.WPF/Windows/SettingsWindow.axaml.cs
+++ b/UVtools.WPF/Windows/SettingsWindow.axaml.cs
@@ -43,7 +43,7 @@ namespace UVtools.WPF.Windows
public SettingsWindow()
{
- Title += $" [v{AppSettings.VersionStr}]";
+ Title += $" [v{App.VersionStr}]";
SettingsBackup = UserSettings.Instance.Clone();
var fileFormats = new List<string>
@@ -61,7 +61,7 @@ namespace UVtools.WPF.Windows
//MaxHeight = Screens.Primary.WorkingArea.Height - 50;
- ScrollViewerMaxHeight = Screens.Primary.WorkingArea.Height - 200;
+ ScrollViewerMaxHeight = Screens.Primary.WorkingArea.Height / Screens.Primary.PixelDensity - 200;
DataContext = this;
diff --git a/UVtools.WPF/Windows/ToolWindow.axaml.cs b/UVtools.WPF/Windows/ToolWindow.axaml.cs
index a767cf5..8f2e9fa 100644
--- a/UVtools.WPF/Windows/ToolWindow.axaml.cs
+++ b/UVtools.WPF/Windows/ToolWindow.axaml.cs
@@ -289,7 +289,7 @@ namespace UVtools.WPF.Windows
operation.ProfileName = null;
ToolControl.BaseOperation = operation;
SelectLayers(operation.LayerRangeSelection);
-
+
if (operation is OperationMorph operationMorph && ToolControl is ToolMorphControl toolMorphControl)
{
toolMorphControl.MorphSelectedIndex = operationMorph.MorphOperationIndex;
@@ -477,7 +477,7 @@ namespace UVtools.WPF.Windows
DispatcherTimer.Run(() =>
{
if (Bounds.Width == 0) return true;
- ScrollViewerMaxHeight = Screens.Primary.WorkingArea.Height - Bounds.Height + ToolControl.Bounds.Height - 250;
+ ScrollViewerMaxHeight = Screens.Primary.WorkingArea.Height / Screens.Primary.PixelDensity - Bounds.Height + ToolControl.Bounds.Height - 250;
DescriptionMaxWidth = Math.Max(Bounds.Width, ToolControl.Bounds.Width) - 40;
Description = toolControl.BaseOperation.Description;
return false;