diff options
-rw-r--r-- | CHANGELOG.md | 8 | ||||
-rw-r--r-- | UVtools.Core/FileFormats/FileFormat.cs | 50 | ||||
-rw-r--r-- | UVtools.Core/GCode/GCodeBuilder.cs | 6 | ||||
-rw-r--r-- | UVtools.Core/Operations/OperationEditParameters.cs | 19 | ||||
-rw-r--r-- | UVtools.Core/Operations/OperationTimelapse.cs | 60 | ||||
-rw-r--r-- | UVtools.Core/UVtools.Core.csproj | 2 | ||||
-rw-r--r-- | UVtools.WPF/Assets/Icons/terminal-16x16.png | bin | 0 -> 123 bytes | |||
-rw-r--r-- | UVtools.WPF/Controls/Tools/ToolEditParametersControl.axaml.cs | 4 | ||||
-rw-r--r-- | UVtools.WPF/Controls/Tools/ToolTimelapseControl.axaml | 38 | ||||
-rw-r--r-- | UVtools.WPF/Extensions/WindowExtensions.cs | 19 | ||||
-rw-r--r-- | UVtools.WPF/MainWindow.axaml | 15 | ||||
-rw-r--r-- | UVtools.WPF/MainWindow.axaml.cs | 11 | ||||
-rw-r--r-- | UVtools.WPF/UVtools.WPF.csproj | 4 | ||||
-rw-r--r-- | UVtools.WPF/Windows/TerminalWindow.axaml | 78 | ||||
-rw-r--r-- | UVtools.WPF/Windows/TerminalWindow.axaml.cs | 200 | ||||
-rw-r--r-- | UVtools.WPF/Windows/ToolWindow.axaml.cs | 9 |
16 files changed, 439 insertions, 84 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index f0e0b26..72c96dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 15/02/2022 - v2.28.1 + +- (Add) File - Terminal: Inject C# code into UVtools with an interactive terminal +- (Improvement) Modifiers: Better increment values for the spin up/down buttons +- (Improvement) Tool - Timelapse: Better lift and feeds for virtual layer, also allow to set custom lift speed for that mode +- (Fix) Tool - Edit print parameters: Disallow to change PositionZ in layers with an active alternating pattern +- (Fix) GCode: When generating layers higher than the next, it will not return to the correct position z + ## 13/02/2022 - v2.28.0 - **Core:** diff --git a/UVtools.Core/FileFormats/FileFormat.cs b/UVtools.Core/FileFormats/FileFormat.cs index 007dbff..ce5ad49 100644 --- a/UVtools.Core/FileFormats/FileFormat.cs +++ b/UVtools.Core/FileFormats/FileFormat.cs @@ -147,46 +147,46 @@ namespace UVtools.Core.FileFormats { #region Instances - public static PrintParameterModifier PositionZ { get; } = new ("Position Z", "Absolute Z position", "mm",0, 100000, Layer.HeightPrecision); - public static PrintParameterModifier BottomLayerCount { get; } = new ("Bottom layers count", "Number of bottom/burn-in layers", "layers",0, ushort.MaxValue, 0); - + public static PrintParameterModifier PositionZ { get; } = new ("Position Z", "Absolute Z position", "mm",0, 100000, 0.01, Layer.HeightPrecision); + public static PrintParameterModifier BottomLayerCount { get; } = new ("Bottom layers count", "Number of bottom/burn-in layers", "layers",0, ushort.MaxValue, 1, 0); + public static PrintParameterModifier BottomLightOffDelay { get; } = new("Bottom light-off seconds", "Total motor movement time + rest time to wait before cure a new bottom layer", "s"); public static PrintParameterModifier LightOffDelay { get; } = new("Light-off seconds", "Total motor movement time + rest time to wait before cure a new layer", "s"); - public static PrintParameterModifier BottomWaitTimeBeforeCure { get; } = new ("Bottom wait before cure", "Time to wait/rest before cure a new bottom layer\nChitubox: Rest after retract\nLychee: Wait before print", "s", 0, 1000, 2); - public static PrintParameterModifier WaitTimeBeforeCure { get; } = new ("Wait before cure", "Time to wait/rest before cure a new layer\nChitubox: Rest after retract\nLychee: Wait before print", "s", 0, 1000, 2); + public static PrintParameterModifier BottomWaitTimeBeforeCure { get; } = new ("Bottom wait before cure", "Time to wait/rest before cure a new bottom layer\nChitubox: Rest after retract\nLychee: Wait before print", "s"); + public static PrintParameterModifier WaitTimeBeforeCure { get; } = new ("Wait before cure", "Time to wait/rest before cure a new layer\nChitubox: Rest after retract\nLychee: Wait before print", "s"); - public static PrintParameterModifier BottomExposureTime { get; } = new ("Bottom exposure time", "Bottom layers cure time", "s", 0.1M, 1000, 2); - public static PrintParameterModifier ExposureTime { get; } = new ("Exposure time", "Layers cure time", "s", 0.1M, 1000, 2); + public static PrintParameterModifier BottomExposureTime { get; } = new ("Bottom exposure time", "Bottom layers cure time", "s", 0.1M); + public static PrintParameterModifier ExposureTime { get; } = new ("Exposure time", "Layers cure time", "s", 0.1M); - public static PrintParameterModifier BottomWaitTimeAfterCure { get; } = new("Bottom wait after cure", "Time to wait/rest after cure a new bottom layer\nChitubox: Rest before lift\nLychee: Wait after print", "s", 0, 1000, 2); - public static PrintParameterModifier WaitTimeAfterCure { get; } = new("Wait after cure", "Time to wait/rest after cure a new bottom layer\nChitubox: Rest before lift\nLychee: Wait after print", "s", 0, 1000, 2); + public static PrintParameterModifier BottomWaitTimeAfterCure { get; } = new("Bottom wait after cure", "Time to wait/rest after cure a new bottom layer\nChitubox: Rest before lift\nLychee: Wait after print", "s"); + public static PrintParameterModifier WaitTimeAfterCure { get; } = new("Wait after cure", "Time to wait/rest after cure a new bottom layer\nChitubox: Rest before lift\nLychee: Wait after print", "s"); public static PrintParameterModifier BottomLiftHeight { get; } = new ("Bottom lift height", "Bottom lift/peel height between layers", "mm", 1); public static PrintParameterModifier LiftHeight { get; } = new ("Lift height", @"Lift/peel height between layers", "mm", 1); - public static PrintParameterModifier BottomLiftSpeed { get; } = new ("Bottom lift speed", null, "mm/min", 10, 5000, 2); - public static PrintParameterModifier LiftSpeed { get; } = new ("Lift speed", null, "mm/min", 10, 5000, 2); + public static PrintParameterModifier BottomLiftSpeed { get; } = new ("Bottom lift speed", null, "mm/min", 10, 5000, 5); + public static PrintParameterModifier LiftSpeed { get; } = new ("Lift speed", null, "mm/min", 10, 5000, 5); public static PrintParameterModifier BottomLiftHeight2 { get; } = new("2) Bottom lift height", "Bottom second lift/peel height between layers", "mm"); public static PrintParameterModifier LiftHeight2 { get; } = new("2) Lift height", @"Second lift/peel height between layers", "mm"); - public static PrintParameterModifier BottomLiftSpeed2 { get; } = new("2) Bottom lift speed", null, "mm/min", 10, 5000, 2); - public static PrintParameterModifier LiftSpeed2 { get; } = new("2) Lift speed", null, "mm/min", 10, 5000, 2); + public static PrintParameterModifier BottomLiftSpeed2 { get; } = new("2) Bottom lift speed", null, "mm/min", 10, 5000, 5); + public static PrintParameterModifier LiftSpeed2 { get; } = new("2) Lift speed", null, "mm/min", 10, 5000, 5); - public static PrintParameterModifier BottomWaitTimeAfterLift { get; } = new("Bottom wait after lift", "Time to wait/rest after a lift/peel sequence at bottom layers\nChitubox: Rest after lift\nLychee: Wait after lift", "s", 0, 1000, 2); - public static PrintParameterModifier WaitTimeAfterLift { get; } = new("Wait after lift", "Time to wait/rest after a lift/peel sequence at layers\nChitubox: Rest after lift\nLychee: Wait after lift", "s", 0, 1000, 2); + public static PrintParameterModifier BottomWaitTimeAfterLift { get; } = new("Bottom wait after lift", "Time to wait/rest after a lift/peel sequence at bottom layers\nChitubox: Rest after lift\nLychee: Wait after lift", "s"); + public static PrintParameterModifier WaitTimeAfterLift { get; } = new("Wait after lift", "Time to wait/rest after a lift/peel sequence at layers\nChitubox: Rest after lift\nLychee: Wait after lift", "s"); - public static PrintParameterModifier BottomRetractSpeed { get; } = new ("Bottom retract speed", "Bottom down speed from lift height to next layer cure position", "mm/min", 10, 5000, 2); - public static PrintParameterModifier RetractSpeed { get; } = new ("Retract speed", "Down speed from lift height to next layer cure position", "mm/min", 10, 5000, 2); + public static PrintParameterModifier BottomRetractSpeed { get; } = new ("Bottom retract speed", "Bottom down speed from lift height to next layer cure position", "mm/min", 10, 5000, 5); + public static PrintParameterModifier RetractSpeed { get; } = new ("Retract speed", "Down speed from lift height to next layer cure position", "mm/min", 10, 5000, 5); public static PrintParameterModifier BottomRetractHeight2 { get; } = new("2) Bottom retract height", null, "mm"); public static PrintParameterModifier RetractHeight2 { get; } = new("2) Retract height", null, "mm"); - public static PrintParameterModifier BottomRetractSpeed2 { get; } = new("2) Bottom retract speed", null, "mm/min", 10, 5000, 2); - public static PrintParameterModifier RetractSpeed2 { get; } = new("2) Retract speed", null, "mm/min", 10, 5000, 2); + public static PrintParameterModifier BottomRetractSpeed2 { get; } = new("2) Bottom retract speed", null, "mm/min", 10, 5000, 5); + public static PrintParameterModifier RetractSpeed2 { get; } = new("2) Retract speed", null, "mm/min", 10, 5000, 5); - public static PrintParameterModifier BottomLightPWM { get; } = new ("Bottom light PWM", "UV LED power for bottom layers", "☀", 1, byte.MaxValue, 0); - public static PrintParameterModifier LightPWM { get; } = new ("Light PWM", "UV LED power for layers", "☀", 1, byte.MaxValue, 0); + public static PrintParameterModifier BottomLightPWM { get; } = new ("Bottom light PWM", "UV LED power for bottom layers", "☀", 1, byte.MaxValue, 5, 0); + public static PrintParameterModifier LightPWM { get; } = new ("Light PWM", "UV LED power for layers", "☀", 1, byte.MaxValue, 5, 0); /*public static PrintParameterModifier[] Parameters = { BottomLayerCount, @@ -241,6 +241,11 @@ namespace UVtools.Core.FileFormats public decimal Maximum { get; } /// <summary> + /// Gets the incrementing value for the dropdown + /// </summary> + public double Increment { get; set; } = 1; + + /// <summary> /// Gets the number of decimal plates /// </summary> public byte DecimalPlates { get; } @@ -268,13 +273,14 @@ namespace UVtools.Core.FileFormats #endregion #region Constructor - public PrintParameterModifier(string name, string description = null, string valueUnit = null, decimal minimum = 0, decimal maximum = 1000, byte decimalPlates = 2) + public PrintParameterModifier(string name, string description = null, string valueUnit = null, decimal minimum = 0, decimal maximum = 1000, double increment = 0.5, byte decimalPlates = 2) { Name = name; Description = description ?? $"Modify '{name}'"; ValueUnit = valueUnit ?? string.Empty; Minimum = minimum; Maximum = maximum; + Increment = decimalPlates == 0 ? Math.Max(1, increment) : increment; DecimalPlates = decimalPlates; } #endregion diff --git a/UVtools.Core/GCode/GCodeBuilder.cs b/UVtools.Core/GCode/GCodeBuilder.cs index 6081be0..4ee83cc 100644 --- a/UVtools.Core/GCode/GCodeBuilder.cs +++ b/UVtools.Core/GCode/GCodeBuilder.cs @@ -673,15 +673,15 @@ namespace UVtools.Core.GCode { AppendLiftMoveGx(lifts, retracts, waitAfterLift, 0, layer); } - else if (lastZPosition < layer.PositionZ) // Ensure Z is on correct position + else if (lastZPosition != layer.PositionZ) // Ensure Z is on correct position { switch (GCodePositioningType) { case GCodePositioningTypes.Absolute: - AppendMoveGx(layer.PositionZ, liftSpeed); + AppendMoveGx(layer.PositionZ, lastZPosition < layer.PositionZ ? Math.Max(liftSpeed, liftSpeed2) : Math.Max(retractSpeed, retractSpeed2)); break; case GCodePositioningTypes.Partial: - AppendMoveGx(Layer.RoundHeight(layer.PositionZ - lastZPosition), liftSpeed); + AppendMoveGx(Layer.RoundHeight(layer.PositionZ - lastZPosition), lastZPosition < layer.PositionZ ? Math.Max(liftSpeed, liftSpeed2) : Math.Max(retractSpeed, retractSpeed2)); break; } diff --git a/UVtools.Core/Operations/OperationEditParameters.cs b/UVtools.Core/Operations/OperationEditParameters.cs index 7e6e8f3..99d1b8a 100644 --- a/UVtools.Core/Operations/OperationEditParameters.cs +++ b/UVtools.Core/Operations/OperationEditParameters.cs @@ -22,7 +22,7 @@ namespace UVtools.Core.Operations private bool _propagateModificationsToLayers = true; private bool _perLayerOverride; private uint _setNumberOfLayer = 1; - private uint _skipNumberOfLayer = 0; + private uint _skipNumberOfLayer; #endregion @@ -92,6 +92,15 @@ namespace UVtools.Core.Operations sb.AppendLine("Nothing changed\nDo some changes or cancel the operation."); } + if (Modifiers.Contains(FileFormat.PrintParameterModifier.PositionZ) + && FileFormat.PrintParameterModifier.PositionZ.HasChanged + && _skipNumberOfLayer > 0 + && LayerRangeCount > 1 + && _setNumberOfLayer + _skipNumberOfLayer < LayerRangeCount) + { + sb.AppendLine("Can not change the PositionZ in layers with an active alternating pattern."); + } + return sb.ToString(); } @@ -156,18 +165,18 @@ namespace UVtools.Core.Operations protected override bool ExecuteInternally(OperationProgress progress) { - if (PerLayerOverride) + if (_perLayerOverride) { uint setLayers = 0; for (uint layerIndex = LayerIndexStart; layerIndex <= LayerIndexEnd; layerIndex++) { SlicerFile[layerIndex].SetValuesFromPrintParametersModifiers(Modifiers); - if (SkipNumberOfLayer <= 0) continue; + if (_skipNumberOfLayer == 0) continue; setLayers++; - if (setLayers >= SetNumberOfLayer) + if (setLayers >= _setNumberOfLayer) { setLayers = 0; - layerIndex += SkipNumberOfLayer; + layerIndex += _skipNumberOfLayer; } } diff --git a/UVtools.Core/Operations/OperationTimelapse.cs b/UVtools.Core/Operations/OperationTimelapse.cs index 084ea57..075bee4 100644 --- a/UVtools.Core/Operations/OperationTimelapse.cs +++ b/UVtools.Core/Operations/OperationTimelapse.cs @@ -105,11 +105,6 @@ namespace UVtools.Core.Operations #region Properties - /// <summary> - /// Gets the minimum possible - /// </summary> - public float MinimumPositionZ => Layer.RoundHeight(SlicerFile.PrintHeight + SlicerFile.LayerHeight); - public TimelapseRaiseMode RaiseMode { get => _raiseMode; @@ -178,6 +173,8 @@ namespace UVtools.Core.Operations set => RaiseAndSetIfChanged(ref _useCustomLift, value); } + public bool CanUseCustomLift => _useCustomLift && SlicerFile.CanUseLayerLiftHeight; + public decimal SlowLiftHeight { get => _slowLiftHeight; @@ -307,7 +304,7 @@ namespace UVtools.Core.Operations switch (_raiseMode) { case TimelapseRaiseMode.LiftHeight: - if (_useCustomLift) + if (CanUseCustomLift) { layer.LiftSpeed = (float)_liftSpeed; layer.RetractSpeed = (float)_retractSpeed; @@ -333,7 +330,7 @@ namespace UVtools.Core.Operations } } - if (SlicerFile.CanUseLayerLiftHeight2 && (layer.LiftHeight2 > 0 || _useCustomLift && _slowLiftHeight > 0)) // TSMC + if (SlicerFile.CanUseLayerLiftHeight2 && (layer.LiftHeight2 > 0 || CanUseCustomLift && _slowLiftHeight > 0)) // TSMC { layer.LiftHeight2 = Math.Max(0, (float)_raisePositionZ - layer.PositionZ - layer.LiftHeight); } @@ -375,37 +372,52 @@ namespace UVtools.Core.Operations RetractHeight2 = 0 }; - layer.SetNoDelays(); - - /*if (_useCustomLift) + if (CanUseCustomLift) { - layer.LiftSpeed = (float) _liftSpeed; - layer.RetractSpeed = (float) _retractSpeed; + layer.LiftSpeed = (float)_liftSpeed; + layer.RetractSpeed = (float)_retractSpeed; + + /*if (SlicerFile.CanUseLayerLiftHeight2) + { + layer.LiftHeight = (float)_slowLiftHeight; + }*/ if (SlicerFile.CanUseLayerLiftSpeed2) { - layer.LiftSpeed2 = (float) _liftSpeed2; + layer.LiftSpeed2 = (float)_liftSpeed2; } if (SlicerFile.CanUseLayerRetractSpeed2) { - layer.RetractSpeed2 = (float) _retractSpeed2; + layer.RetractSpeed2 = (float)_retractSpeed2; } - }*/ + } + + layer.SetNoDelays(); var layers = SlicerFile.ToList(); for (int i = 0; i < virtualLayers.Count; i++) { - /*var newLayer = layer.Clone(); - if (!_useCustomLift) + int insertIndex = (int)virtualLayers[i]; + SlicerFile[insertIndex].LiftHeightTotal = SlicerFile.SupportsGCode ? 0 : 0.1f; + + if (CanUseCustomLift) { - var intersectLayer = Math.Clamp(virtualLayers[i] + i, 0, SlicerFile.LastLayerIndex); - SlicerFile[intersectLayer].CopyLiftTo(newLayer); - // This layer does not require a lift procedure - newLayer.LiftHeightTotal = SlicerFile.SupportsGCode ? 0 : 0.1f; - newLayer.RetractHeight2 = 0; - }*/ - layers.Insert((int)(virtualLayers[i] + i), layer.Clone()); + SlicerFile[insertIndex].LiftSpeed = (float)_liftSpeed; + SlicerFile[insertIndex].RetractSpeed = (float)_retractSpeed; + + if (SlicerFile.CanUseLayerLiftSpeed2) + { + SlicerFile[insertIndex].LiftSpeed2 = (float)_retractSpeed2; + } + + if (SlicerFile.CanUseLayerRetractSpeed2) + { + SlicerFile[insertIndex].RetractSpeed2 = (float)_retractSpeed2; + } + } + + layers.Insert(insertIndex + i, layer.Clone()); } SlicerFile.SuppressRebuildPropertiesWork(() => SlicerFile.LayerManager.Layers = layers.ToArray()); } diff --git a/UVtools.Core/UVtools.Core.csproj b/UVtools.Core/UVtools.Core.csproj index b020595..b0724ec 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, calibration, repair, conversion and manipulation</Description> - <Version>2.28.0</Version> + <Version>2.28.1</Version> <Copyright>Copyright © 2020 PTRTECH</Copyright> <PackageIcon>UVtools.png</PackageIcon> <Platforms>AnyCPU;x64</Platforms> diff --git a/UVtools.WPF/Assets/Icons/terminal-16x16.png b/UVtools.WPF/Assets/Icons/terminal-16x16.png Binary files differnew file mode 100644 index 0000000..98f041d --- /dev/null +++ b/UVtools.WPF/Assets/Icons/terminal-16x16.png diff --git a/UVtools.WPF/Controls/Tools/ToolEditParametersControl.axaml.cs b/UVtools.WPF/Controls/Tools/ToolEditParametersControl.axaml.cs index 475edb0..69447da 100644 --- a/UVtools.WPF/Controls/Tools/ToolEditParametersControl.axaml.cs +++ b/UVtools.WPF/Controls/Tools/ToolEditParametersControl.axaml.cs @@ -62,7 +62,7 @@ namespace UVtools.WPF.Controls.Tools HorizontalAlignment = HorizontalAlignment.Stretch, Minimum = (double) modifier.Minimum, Maximum = (double) modifier.Maximum, - Increment = modifier.DecimalPlates == 0 ? 1 : 0.5, + Increment = modifier.Increment, Value = (double)modifier.NewValue, Tag = this, //Width = 100, @@ -70,7 +70,7 @@ namespace UVtools.WPF.Controls.Tools }; if (modifier.DecimalPlates > 0) { - NewValue.FormatString = "F02"; + NewValue.FormatString = $"F{modifier.DecimalPlates}"; } Unit = new TextBlock diff --git a/UVtools.WPF/Controls/Tools/ToolTimelapseControl.axaml b/UVtools.WPF/Controls/Tools/ToolTimelapseControl.axaml index edabb6f..57d9702 100644 --- a/UVtools.WPF/Controls/Tools/ToolTimelapseControl.axaml +++ b/UVtools.WPF/Controls/Tools/ToolTimelapseControl.axaml @@ -151,7 +151,6 @@ IsChecked="{Binding Operation.UseCustomLift}"> <CheckBox.IsVisible> <MultiBinding Converter="{x:Static BoolConverters.And}"> - <Binding Path="Operation.IsLiftHeightMode"/> <Binding Path="SlicerFile.CanUseLayerLiftHeight"/> </MultiBinding> </CheckBox.IsVisible> @@ -162,7 +161,6 @@ ColumnDefinitions="Auto,10,210,5,Auto,5,210"> <Grid.IsVisible> <MultiBinding Converter="{x:Static BoolConverters.And}"> - <Binding Path="Operation.IsLiftHeightMode"/> <Binding Path="Operation.UseCustomLift"/> <Binding Path="SlicerFile.CanUseLayerLiftHeight"/> </MultiBinding> @@ -172,6 +170,7 @@ VerticalAlignment="Center" ToolTip.Tip="The slow first lift sequence (TSMC). 
Use a low value or 0 to disable." + IsVisible="{Binding !Operation.IsVirtualLayerMode}" Text="Slow lift height:"/> <NumericUpDown Grid.Row="0" Grid.Column="2" @@ -179,15 +178,26 @@ Minimum="0" Maximum="20" Increment="1" - IsVisible="{Binding SlicerFile.CanUseLayerLiftHeight2}" Value="{Binding Operation.SlowLiftHeight}"> + <NumericUpDown.IsVisible> + <MultiBinding Converter="{x:Static BoolConverters.And}"> + <Binding Path="SlicerFile.CanUseLayerLiftHeight2"/> + <Binding Path="!Operation.IsVirtualLayerMode"/> + </MultiBinding> + </NumericUpDown.IsVisible> </NumericUpDown> <TextBlock Grid.Row="0" Grid.Column="2" VerticalAlignment="Center" ToolTip.Tip="(Not supported by your printer / file format)" - IsVisible="{Binding !SlicerFile.CanUseLayerLiftHeight2}" - Text="(Not supported)"/> + Text="(Not supported)"> + <TextBlock.IsVisible> + <MultiBinding Converter="{x:Static BoolConverters.And}"> + <Binding Path="!SlicerFile.CanUseLayerLiftHeight2"/> + <Binding Path="!Operation.IsVirtualLayerMode"/> + </MultiBinding> + </TextBlock.IsVisible> + </TextBlock> <TextBlock Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" @@ -225,6 +235,7 @@ VerticalAlignment="Center" ToolTip.Tip="The slow last retract sequence (TSMC). 
Use a low value or 0 to disable." + IsVisible="{Binding !Operation.IsVirtualLayerMode}" Text="Slow retract height:"/> <NumericUpDown Grid.Row="4" Grid.Column="2" @@ -232,15 +243,26 @@ Minimum="0" Maximum="20" Increment="1" - IsVisible="{Binding SlicerFile.CanUseLayerRetractHeight2}" Value="{Binding Operation.SlowRetractHeight}"> + <NumericUpDown.IsVisible> + <MultiBinding Converter="{x:Static BoolConverters.And}"> + <Binding Path="SlicerFile.CanUseLayerRetractHeight2"/> + <Binding Path="!Operation.IsVirtualLayerMode"/> + </MultiBinding> + </NumericUpDown.IsVisible> </NumericUpDown> <TextBlock Grid.Row="4" Grid.Column="2" VerticalAlignment="Center" ToolTip.Tip="(Not supported by your printer / file format)" - IsVisible="{Binding !SlicerFile.CanUseLayerRetractHeight2}" - Text="(Not supported)"/> + Text="(Not supported)"> + <TextBlock.IsVisible> + <MultiBinding Converter="{x:Static BoolConverters.And}"> + <Binding Path="!SlicerFile.CanUseLayerRetractHeight2"/> + <Binding Path="!Operation.IsVirtualLayerMode"/> + </MultiBinding> + </TextBlock.IsVisible> + </TextBlock> <TextBlock Grid.Row="6" Grid.Column="0" diff --git a/UVtools.WPF/Extensions/WindowExtensions.cs b/UVtools.WPF/Extensions/WindowExtensions.cs index c6bb5c0..70915fd 100644 --- a/UVtools.WPF/Extensions/WindowExtensions.cs +++ b/UVtools.WPF/Extensions/WindowExtensions.cs @@ -20,7 +20,7 @@ namespace UVtools.WPF.Extensions public static class WindowExtensions { public static async Task<ButtonResult> MessageBoxGeneric(this Window window, string message, string title = null, - ButtonEnum buttons = ButtonEnum.Ok, Icon icon = Icon.None, bool topMost = false, WindowStartupLocation location = WindowStartupLocation.CenterOwner, Style style = Style.None) + ButtonEnum buttons = ButtonEnum.Ok, Icon icon = Icon.None, bool topMost = false, WindowStartupLocation location = WindowStartupLocation.CenterOwner) { var messageBoxStandardWindow = MessageBox.Avalonia.MessageBoxManager.GetMessageBoxStandardWindow( new MessageBoxStandardParams @@ -29,7 +29,6 @@ namespace UVtools.WPF.Extensions ContentTitle = title ?? window.Title, ContentMessage = message, Icon = icon, - Style = style, WindowIcon = new WindowIcon(App.GetAsset("/Assets/Icons/UVtools.ico")), WindowStartupLocation = location, CanResize = UserSettings.Instance.General.WindowsCanResize, @@ -42,17 +41,17 @@ namespace UVtools.WPF.Extensions return await messageBoxStandardWindow.ShowDialog(window); } - public static async Task<ButtonResult> MessageBoxInfo(this Window window, string message, string title = null, ButtonEnum buttons = ButtonEnum.Ok, bool topMost = false, Style style = Style.None) - => await window.MessageBoxGeneric(message, title ?? $"{window.Title} - Information", buttons, Icon.Info, topMost, WindowStartupLocation.CenterOwner, style); + public static async Task<ButtonResult> MessageBoxInfo(this Window window, string message, string title = null, ButtonEnum buttons = ButtonEnum.Ok, bool topMost = false) + => await window.MessageBoxGeneric(message, title ?? $"{window.Title} - Information", buttons, Icon.Info, topMost, WindowStartupLocation.CenterOwner); - public static async Task<ButtonResult> MessageBoxError(this Window window, string message, string title = null, ButtonEnum buttons = ButtonEnum.Ok, bool topMost = false, Style style = Style.None) - => await window.MessageBoxGeneric(message, title ?? $"{window.Title} - Error", buttons, Icon.Error, topMost, WindowStartupLocation.CenterOwner, style); + public static async Task<ButtonResult> MessageBoxError(this Window window, string message, string title = null, ButtonEnum buttons = ButtonEnum.Ok, bool topMost = false) + => await window.MessageBoxGeneric(message, title ?? $"{window.Title} - Error", buttons, Icon.Error, topMost, WindowStartupLocation.CenterOwner); - public static async Task<ButtonResult> MessageBoxQuestion(this Window window, string message, string title = null, ButtonEnum buttons = ButtonEnum.YesNo, bool topMost = false, Style style = Style.None) - => await window.MessageBoxGeneric(message, title ?? $"{window.Title} - Question", buttons, Icon.Setting, topMost, WindowStartupLocation.CenterOwner, style); + public static async Task<ButtonResult> MessageBoxQuestion(this Window window, string message, string title = null, ButtonEnum buttons = ButtonEnum.YesNo, bool topMost = false) + => await window.MessageBoxGeneric(message, title ?? $"{window.Title} - Question", buttons, Icon.Setting, topMost, WindowStartupLocation.CenterOwner); - public static async Task<ButtonResult> MessageBoxWaring(this Window window, string message, string title = null, ButtonEnum buttons = ButtonEnum.Ok, bool topMost = false, Style style = Style.None) - => await window.MessageBoxGeneric(message, title ?? $"{window.Title} - Question", buttons, Icon.Warning, topMost, WindowStartupLocation.CenterOwner, style); + public static async Task<ButtonResult> MessageBoxWaring(this Window window, string message, string title = null, ButtonEnum buttons = ButtonEnum.Ok, bool topMost = false) + => await window.MessageBoxGeneric(message, title ?? $"{window.Title} - Question", buttons, Icon.Warning, topMost, WindowStartupLocation.CenterOwner); public static void ShowDialogSync(this Window window, Window parent = null) diff --git a/UVtools.WPF/MainWindow.axaml b/UVtools.WPF/MainWindow.axaml index d141d39..9fff996 100644 --- a/UVtools.WPF/MainWindow.axaml +++ b/UVtools.WPF/MainWindow.axaml @@ -127,8 +127,19 @@ <Image Source="\Assets\Icons\extract-object-16x16.png"/> </MenuItem.Icon> </MenuItem> - - <MenuItem + + <MenuItem Name="MainMenu.File.Terminal" + HotKey="Ctrl+Shift+T" InputGesture="Ctrl+Shift+T" + Header="_Terminal" + Command="{Binding OpenTerminal}" + IsEnabled="{Binding IsFileLoaded}"> + <MenuItem.Icon> + <Image Source="\Assets\Icons\terminal-16x16.png"/> + </MenuItem.Icon> + </MenuItem> + + + <MenuItem Name="MainMenu.File.Convert" Header="_Convert to" IsEnabled="{Binding IsFileLoaded}" diff --git a/UVtools.WPF/MainWindow.axaml.cs b/UVtools.WPF/MainWindow.axaml.cs index 92d60f3..c5e7297 100644 --- a/UVtools.WPF/MainWindow.axaml.cs +++ b/UVtools.WPF/MainWindow.axaml.cs @@ -617,7 +617,7 @@ namespace UVtools.WPF AddHandler(DragDrop.DropEvent, (sender, e) => { - ProcessFiles(e.Data.GetFileNames().ToArray()); + ProcessFiles(e.Data.GetFileNames()?.ToArray()); }); _menuFileSendTo = this.FindControl<MenuItem>("MainMenu.File.SendTo"); @@ -1823,8 +1823,6 @@ namespace UVtools.WPF VisibleThumbnailIndex = 1; - RefreshProperties(); - UpdateTitle(); if (mat is not null) @@ -1904,7 +1902,7 @@ namespace UVtools.WPF $"Ratio: {xRatio}:{yRatio}\n", "Incorrect image ratio detected"); } - + RefreshProperties(); ResetDataContext(); ForceUpdateActualLayer(actualLayer.Clamp(actualLayer, SliderMaximumValue)); @@ -2234,6 +2232,11 @@ namespace UVtools.WPF } + public void OpenTerminal() + { + new TerminalWindow().Show(this); + } + #region Operations public async Task<Operation> ShowRunOperation(Operation loadOperation) => diff --git a/UVtools.WPF/UVtools.WPF.csproj b/UVtools.WPF/UVtools.WPF.csproj index 013498f..052eca7 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>2.28.0</Version> + <Version>2.28.1</Version> <Platforms>AnyCPU;x64</Platforms> <PackageIcon>UVtools.png</PackageIcon> <PackageReadmeFile>README.md</PackageReadmeFile> @@ -44,7 +44,7 @@ <PackageReference Include="Avalonia.Desktop" Version="0.10.12" /> <PackageReference Include="Avalonia.Diagnostics" Version="0.10.12" /> <PackageReference Include="Emgu.CV.runtime.windows" Version="4.5.5.4823" /> - <PackageReference Include="MessageBox.Avalonia" Version="1.7.1" /> + <PackageReference Include="MessageBox.Avalonia" Version="1.8.1-night" /> <PackageReference Include="ThemeEditor.Controls.ColorPicker" Version="0.10.12" /> </ItemGroup> <ItemGroup> diff --git a/UVtools.WPF/Windows/TerminalWindow.axaml b/UVtools.WPF/Windows/TerminalWindow.axaml new file mode 100644 index 0000000..fe88998 --- /dev/null +++ b/UVtools.WPF/Windows/TerminalWindow.axaml @@ -0,0 +1,78 @@ +<uc:WindowEx xmlns="https://github.com/avaloniaui" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:uc="clr-namespace:UVtools.WPF.Controls" + mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="500" + MinWidth="500" + MinHeight="300" + Width="800" + Height="500" + Padding="10" + WindowStartupLocation="CenterOwner" + DragDrop.AllowDrop="True" + Icon="/Assets/Icons/UVtools.ico" + x:Class="UVtools.WPF.Windows.TerminalWindow" + Title="UVtools interactive terminal"> + <Grid RowDefinitions="4*,5,*,5,Auto"> + <TextBox Grid.Row="0" + Name="TerminalTextBox" + AcceptsReturn="True" + IsReadOnly="True" + Watermark="Terminal" + UseFloatingWatermark="True" + Text="{Binding TerminalText}"/> + + <GridSplitter Grid.Row="1" + ResizeDirection="Rows" + ResizeBehavior="PreviousAndNext"/> + + <TextBox Grid.Row="2" + AcceptsReturn="{Binding MultiLine}" + Watermark="> Type in the command or text to send +
Alt + enter: Shortcut to send the command" + Text="{Binding CommandText}"/> + + <WrapPanel Grid.Row="4" Orientation="Horizontal" HorizontalAlignment="Right"> + <!-- + <ToggleSwitch VerticalAlignment="Center" + IsChecked="{Binding MultiLine}" + OnContent="Multi line" + OffContent="Single line"/> +--> + <ToggleButton Margin="0,0,0,0" + VerticalAlignment="Center" + IsChecked="{Binding Verbose}" + ToolTip.Tip="Sent command appears on the terminal text" + Content="Verbose"/> + + <ToggleButton Margin="2,0,0,0" + VerticalAlignment="Center" + IsChecked="{Binding ClearCommandAfterSend}" + ToolTip.Tip="Clears the sent command on the input box" + Content="Clear"/> + + <ToggleButton Margin="2,0,0,0" + VerticalAlignment="Center" + IsChecked="{Binding AutoScroll}" + Content="Auto scroll"/> + + + <Button Margin="15,0,0,0" + Content="Clear" + VerticalAlignment="Center" + Command="{Binding Clear}" + ToolTip.Tip="Clears the terminal text (cls) +
Alt + delete" + HotKey="Alt+Delete"/> + <Button Margin="5,0,0,0" + Content="Send" + Padding="40,6" + VerticalAlignment="Center" + Command="{Binding SendCommand}" + IsDefault="True" + HotKey="Alt+Enter"/> + </WrapPanel> + + </Grid> +</uc:WindowEx> diff --git a/UVtools.WPF/Windows/TerminalWindow.axaml.cs b/UVtools.WPF/Windows/TerminalWindow.axaml.cs new file mode 100644 index 0000000..8e937c6 --- /dev/null +++ b/UVtools.WPF/Windows/TerminalWindow.axaml.cs @@ -0,0 +1,200 @@ +/* + * 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.IO; +using System.Text; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Input; +using Avalonia.Markup.Xaml; +using Microsoft.CodeAnalysis.CSharp.Scripting; +using Microsoft.CodeAnalysis.Scripting; +using UVtools.Core; +using UVtools.WPF.Controls; + +namespace UVtools.WPF.Windows +{ + public partial class TerminalWindow : WindowEx + { + private static readonly string DefaultTerminalText = $"> Welcome to {About.Software} interactive terminal.\n" + + "> Type in some commands in C# language to inject code.\n" + + "> Example 1: SlicerFile.FirstLayer\n" + + "> Example 2: SlicerFile.ExposureTime = 3\n\n"; + + private string _terminalText = DefaultTerminalText; + private string _commandText = string.Empty; + + private bool _multiLine = true; + private bool _autoScroll = true; + private bool _verbose; + private bool _clearCommandAfterSend = true; + + private readonly TextBox _terminalTextBox; + public ScriptState _scriptState; + + #region Properties + + public string TerminalText + { + get => _terminalText; + set + { + if(!RaiseAndSetIfChanged(ref _terminalText, value)) return; + if(_autoScroll) _terminalTextBox.CaretIndex = _terminalText.Length - 1; + } + } + + public string CommandText + { + get => _commandText; + set => RaiseAndSetIfChanged(ref _commandText, value ?? string.Empty); + } + + public bool MultiLine + { + get => _multiLine; + set => RaiseAndSetIfChanged(ref _multiLine, value); + } + + public bool AutoScroll + { + get => _autoScroll; + set => RaiseAndSetIfChanged(ref _autoScroll, value); + } + + public bool Verbose + { + get => _verbose; + set => RaiseAndSetIfChanged(ref _verbose, value); + } + + public bool ClearCommandAfterSend + { + get => _clearCommandAfterSend; + set => RaiseAndSetIfChanged(ref _clearCommandAfterSend, value); + } + + #endregion + + public TerminalWindow() + { + InitializeComponent(); +#if DEBUG + this.AttachDevTools(); +#endif + + _terminalTextBox = this.FindControl<TextBox>("TerminalTextBox"); + AddHandler(DragDrop.DropEvent, (sender, e) => + { + var text = e.Data.GetText(); + if (text is not null) + { + CommandText = text; + return; + } + + var fileNames = e.Data.GetFileNames(); + if (fileNames is not null) + { + var sb = new StringBuilder(); + foreach (var fileName in fileNames) + { + try + { + if (!File.Exists(fileName)) continue; + var fi = new FileInfo(fileName); + if(fi.Length > 5000000) continue; // 5Mb only! + sb.AppendLine(File.ReadAllText(fi.FullName)); + } + catch (Exception exception) + { + Console.WriteLine(exception); + } + + } + + CommandText = sb.ToString(); + } + }); + + DataContext = this; + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } + + public void Clear() + { + TerminalText = DefaultTerminalText; + } + + public async void SendCommand() + { + if (string.IsNullOrWhiteSpace(_commandText)) + { + TerminalText += '\n'; + return; + } + + var output = new StringBuilder(_terminalText); + if (_verbose) output.AppendLine(_commandText); + + try + { + if (_scriptState is null) + { + _scriptState = CSharpScript.RunAsync(_commandText, + ScriptOptions.Default + .AddReferences(typeof(About).Assembly) + .AddImports( + "System", + "System.Collections.Generic", + "System.Math", + "System.IO", + "System.Linq", + "System.Threading", + "System.Threading.Tasks", + "UVtools.Core", + "UVtools.Core.Extensions", + "UVtools.Core.FileFormats", + "UVtools.Core.Layers", + "UVtools.Core.Objects", + "UVtools.Core.Operations") + .WithAllowUnsafe(true), this).Result; + } + else + { + _scriptState = await _scriptState.ContinueWithAsync(_commandText); + } + + if (_scriptState.ReturnValue is not null) + { + output.AppendLine(_scriptState.ReturnValue.ToString()); + } + else if (_scriptState.Exception is not null) + { + output.AppendLine(_scriptState.Exception.ToString()); + } + else if(!_verbose) + { + output.AppendLine(_commandText); + } + } + catch (Exception e) + { + output.AppendLine(e.Message); + } + + TerminalText = output.ToString(); + + if (_clearCommandAfterSend) CommandText = string.Empty; + } + } +} diff --git a/UVtools.WPF/Windows/ToolWindow.axaml.cs b/UVtools.WPF/Windows/ToolWindow.axaml.cs index 43d13cd..a3543d6 100644 --- a/UVtools.WPF/Windows/ToolWindow.axaml.cs +++ b/UVtools.WPF/Windows/ToolWindow.axaml.cs @@ -1,4 +1,11 @@ -using System; +/* + * 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.Collections.ObjectModel; using System.Drawing; using Avalonia; |