From 2f3f105a5e29b38cb63a59ac7a6060525d5259b7 Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Thu, 12 Nov 2026 07:25:07 -0500 Subject: [PATCH] feat(machine-config): name blank standard thicknesses on open Thicknesses saved before the auto-fill kept a blank Name / Gauge until someone edited their value, so their nest reports showed only the decimal. Opening Machine Configuration now names every blank thickness that matches a standard gauge or plate fraction and marks the form changed, so closing offers to save them. Thicknesses matching none stay blank, and once saved a reopen finds nothing to fill and does not ask again. Co-Authored-By: Claude Opus 5.5 --- .../Forms/MachineCutsFormsTests.cs | 58 +++++++++++++++++++ OpenNest/Forms/MachineConfigForm.cs | 29 +++++++++- docs/machine-cutting-defaults.md | 4 +- 3 files changed, 89 insertions(+), 2 deletions(-) diff --git a/OpenNest.WinForms.Tests/Forms/MachineCutsFormsTests.cs b/OpenNest.WinForms.Tests/Forms/MachineCutsFormsTests.cs index 878e37b9..09f3976d 100644 --- a/OpenNest.WinForms.Tests/Forms/MachineCutsFormsTests.cs +++ b/OpenNest.WinForms.Tests/Forms/MachineCutsFormsTests.cs @@ -182,6 +182,64 @@ public class MachineCutsFormsTests }); } + [Fact] + public void Opening_NamesBlankStandardThicknessesAndOffersToSaveThem() + { + RunSta(() => + { + var directory = Path.Combine(Path.GetTempPath(), "OpenNestTests", Guid.NewGuid().ToString()); + Directory.CreateDirectory(directory); + try + { + var provider = new LocalJsonProvider(directory); + provider.SaveMachine(new MachineConfig + { + Name = "Laser", + Units = UnitSystem.Inches, + Materials = + { + new MaterialConfig + { + Name = "Mild Steel", + Thicknesses = + { + new ThicknessConfig { Value = 0.25 }, + new ThicknessConfig { Value = 0.0598, Name = "Shop sheet" }, + new ThicknessConfig { Value = 0.3 }, + }, + }, + }, + }); + + using (var form = new MachineConfigForm(provider)) + { + var names = Descendants(Find(form, "_tree").Nodes) + .Where(n => n.Tag is ThicknessConfig).Select(n => n.Text).ToList(); + Assert.Equal(new[] { "0.25 (1/4\")", "0.0598 (Shop sheet)", "0.3" }, names); + Assert.True(Dirty(form)); + } + + // Closed without saving, so the file is still blank; once the names are saved, + // opening again finds nothing to name and changes nothing. + var saved = provider.GetMachine(provider.GetMachines().Single().Id)!; + Assert.True(MachineConfigForm.FillBlankThicknessNames(saved)); + provider.SaveMachine(saved); + Assert.Equal("1/4\"", provider.GetMachine(saved.Id)!.Materials[0].Thicknesses[0].Name); + using (var reopened = new MachineConfigForm(provider)) + Assert.False(Dirty(reopened)); + } + finally + { + Directory.Delete(directory, recursive: true); + } + }); + } + + private static bool Dirty(MachineConfigForm form) => + (bool)typeof(MachineConfigForm) + .GetField("_dirty", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic)! + .GetValue(form)!; + private static IEnumerable Descendants(TreeNodeCollection nodes) => nodes.Cast().SelectMany(n => Descendants(n.Nodes).Prepend(n)); diff --git a/OpenNest/Forms/MachineConfigForm.cs b/OpenNest/Forms/MachineConfigForm.cs index 0f58af74..d4126a49 100644 --- a/OpenNest/Forms/MachineConfigForm.cs +++ b/OpenNest/Forms/MachineConfigForm.cs @@ -81,6 +81,10 @@ namespace OpenNest.Forms if (machine is null) continue; + // Blank names get their standard gauge or fraction now, so the tree and nest + // reports show them; closing then offers to save what was filled. + if (FillBlankThicknessNames(machine)) + MarkDirty(); _tree.Nodes.Add(BuildMachineNode(machine)); } @@ -1176,11 +1180,34 @@ namespace OpenNest.Forms /// matches, such as "16 ga" or "1/4\"", or empty when it matches none. /// private string StandardThicknessName(double value) => + StandardThicknessName(value, _currentMachine?.Units ?? UnitSystem.Inches); + + private static string StandardThicknessName(double value, UnitSystem units) => StandardThicknesses.Find( value, - _currentMachine?.Units == UnitSystem.Millimeters ? Units.Millimeters : Units.Inches + units == UnitSystem.Millimeters ? Units.Millimeters : Units.Inches )?.Name ?? ""; + /// + /// Names every unnamed thickness of that matches a standard + /// gauge or plate fraction; true when it named any. + /// + internal static bool FillBlankThicknessNames(MachineConfig machine) + { + var filled = false; + foreach (var thickness in machine.Materials.SelectMany(m => m.Thicknesses)) + { + if (!string.IsNullOrWhiteSpace(thickness.Name)) + continue; + var name = StandardThicknessName(thickness.Value, machine.Units); + if (name.Length == 0) + continue; + thickness.Name = name; + filled = true; + } + return filled; + } + private string LengthUnitSuffix(object _) => _currentMachine?.Units == UnitSystem.Millimeters ? "mm" : "in"; diff --git a/docs/machine-cutting-defaults.md b/docs/machine-cutting-defaults.md index 6226fe6e..f187954b 100644 --- a/docs/machine-cutting-defaults.md +++ b/docs/machine-cutting-defaults.md @@ -19,7 +19,9 @@ A thickness's **Name / Gauge** ("16 ga") is display only: the tree shows it and put it before the decimal. In Machine Configuration a blank name, or one still the standard name of the old value, follows the thickness value to its standard steel gauge or plate fraction (`StandardThicknesses`, matched to the displayed precision); a typed name is kept. -A new thickness starts with the name of its default value. +A new thickness starts with the name of its default value. Opening Machine Configuration +names every blank thickness that matches a standard one and, when it named any, offers to +save them on close; thicknesses matching none stay blank. ## Where the Cutting Parameters dialog saves