diff options
author | Tiago Conceição <Tiago_caza@hotmail.com> | 2020-11-08 02:47:57 +0300 |
---|---|---|
committer | Tiago Conceição <Tiago_caza@hotmail.com> | 2020-11-08 02:47:57 +0300 |
commit | 2e349328d6dea431325c8242fe915a402a806bb3 (patch) | |
tree | 47d14c3f96cfd0ac140b72ab1ee21869c4eed372 | |
parent | 5da3c7e173094666cd900b3ee09fe3dd93d97985 (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
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 Binary files differnew file mode 100644 index 0000000..e4d7c2b --- /dev/null +++ b/UVtools.WPF/Assets/Icons/clipboard-32x32.png diff --git a/UVtools.WPF/Assets/Icons/layers-alt-32x32.png b/UVtools.WPF/Assets/Icons/layers-alt-32x32.png Binary files differnew file mode 100644 index 0000000..b1ed164 --- /dev/null +++ b/UVtools.WPF/Assets/Icons/layers-alt-32x32.png diff --git a/UVtools.WPF/Assets/Icons/redo-16x16.png b/UVtools.WPF/Assets/Icons/redo-16x16.png Binary files differnew file mode 100644 index 0000000..3408d68 --- /dev/null +++ b/UVtools.WPF/Assets/Icons/redo-16x16.png 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; |