diff --git a/OpenNest.Core/PlateDisplayNumbering.cs b/OpenNest.Core/PlateDisplayNumbering.cs index e6b761b9..396ba3ca 100644 --- a/OpenNest.Core/PlateDisplayNumbering.cs +++ b/OpenNest.Core/PlateDisplayNumbering.cs @@ -48,17 +48,24 @@ namespace OpenNest return plate != null && plate.Parts.Count == 0; } - /// Header text for the plate at . - public static string FormatHeader(IList plates, int storageIndex, string plateSizeText) + /// + /// Header text for the plate at . A quantity above one is + /// appended as "×N"; a single sheet shows no quantity. + /// + public static string FormatHeader(IList plates, int storageIndex, string plateSizeText, int quantity = 1) { var displayed = DisplayedPlateNumber(plates, storageIndex); if (displayed == null) return IsTrailingSentinel(plates, storageIndex) ? "New plate (empty)" : "No plates"; - return string.IsNullOrEmpty(plateSizeText) - ? string.Format("Plate {0} of {1}", displayed.Value, DisplayedPlateCount(plates)) - : string.Format("Plate {0} of {1} | {2}", displayed.Value, DisplayedPlateCount(plates), plateSizeText); + + var text = string.Format("Plate {0} of {1}", displayed.Value, DisplayedPlateCount(plates)); + if (!string.IsNullOrEmpty(plateSizeText)) + text += " | " + plateSizeText; + if (quantity > 1) + text += " | ×" + quantity; + return text; } } } diff --git a/OpenNest.Tests/PlateDisplayNumberingTests.cs b/OpenNest.Tests/PlateDisplayNumberingTests.cs index 105735a6..31540823 100644 --- a/OpenNest.Tests/PlateDisplayNumberingTests.cs +++ b/OpenNest.Tests/PlateDisplayNumberingTests.cs @@ -44,6 +44,18 @@ public class PlateDisplayNumberingTests Assert.Equal("New plate (empty)", PlateDisplayNumbering.FormatHeader(nest.Plates, sentinel, null)); } + [Fact] + public void Header_ShowsSizeAndQuantityAboveOne() + { + var nest = CreateNest(); + nest.Plates.Add(WithPart()); + + Assert.Equal("Plate 1 of 1 | 60 x 120", PlateDisplayNumbering.FormatHeader(nest.Plates, 0, "60 x 120")); + Assert.Equal("Plate 1 of 1 | 60 x 120", PlateDisplayNumbering.FormatHeader(nest.Plates, 0, "60 x 120", 1)); + Assert.Equal("Plate 1 of 1 | 60 x 120 | ×4", PlateDisplayNumbering.FormatHeader(nest.Plates, 0, "60 x 120", 4)); + Assert.Equal("Plate 1 of 1 | ×2", PlateDisplayNumbering.FormatHeader(nest.Plates, 0, null, 2)); + } + [Fact] public void TwoRealPlatesThenSentinel_TotalIsTwo() { diff --git a/OpenNest.WinForms.Tests/Forms/MainStatusBarTests.cs b/OpenNest.WinForms.Tests/Forms/MainStatusBarTests.cs new file mode 100644 index 00000000..035788b4 --- /dev/null +++ b/OpenNest.WinForms.Tests/Forms/MainStatusBarTests.cs @@ -0,0 +1,26 @@ +using System.Globalization; +using OpenNest.Forms; + +namespace OpenNest.WinForms.Tests.Forms; + +public class MainStatusBarTests +{ + [Fact] + public void FormatMaterial_ShowsNameAndThicknessWithUnits() + { + var previous = CultureInfo.CurrentCulture; + CultureInfo.CurrentCulture = CultureInfo.InvariantCulture; + try + { + Assert.Equal("Material: A36 · 0.25 in", MainStatusBar.FormatMaterial("A36", 0.25, Units.Inches)); + Assert.Equal("Material: 304 SS · 3 mm", MainStatusBar.FormatMaterial(" 304 SS ", 3, Units.Millimeters)); + Assert.Equal("Material: A36", MainStatusBar.FormatMaterial("A36", 0, Units.Inches)); + Assert.Equal("Material: 0.1875 in", MainStatusBar.FormatMaterial(null, 0.1875, Units.Inches)); + Assert.Equal("Material: (none)", MainStatusBar.FormatMaterial("", 0, Units.Inches)); + } + finally + { + CultureInfo.CurrentCulture = previous; + } + } +} diff --git a/OpenNest/Forms/EditNestForm.cs b/OpenNest/Forms/EditNestForm.cs index c0e7e6f5..13de566b 100644 --- a/OpenNest/Forms/EditNestForm.cs +++ b/OpenNest/Forms/EditNestForm.cs @@ -995,7 +995,7 @@ namespace OpenNest.Forms // Display-only numbering: the trailing empty new-plate sentinel is excluded from the // total (and labeled as such); navigation and storage indexes are untouched. plateInfoLabel.Text = PlateDisplayNumbering.FormatHeader( - Nest.Plates, PlateManager.CurrentIndex, plate.Size.ToString()); + Nest.Plates, PlateManager.CurrentIndex, plate.Size.ToString(), plate.Quantity); btnFirstPlate.Enabled = !PlateManager.IsFirst; btnPreviousPlate.Enabled = !PlateManager.IsFirst; diff --git a/OpenNest/Forms/MainForm.Designer.cs b/OpenNest/Forms/MainForm.Designer.cs index df0af5b8..b770d868 100644 --- a/OpenNest/Forms/MainForm.Designer.cs +++ b/OpenNest/Forms/MainForm.Designer.cs @@ -167,9 +167,7 @@ namespace OpenNest.Forms statusLabel1 = new System.Windows.Forms.ToolStripStatusLabel(); locationStatusLabel = new System.Windows.Forms.ToolStripStatusLabel(); spacerLabel = new System.Windows.Forms.ToolStripStatusLabel(); - plateIndexStatusLabel = new System.Windows.Forms.ToolStripStatusLabel(); - plateSizeStatusLabel = new System.Windows.Forms.ToolStripStatusLabel(); - plateQtyStatusLabel = new System.Windows.Forms.ToolStripStatusLabel(); + materialStatusLabel = new System.Windows.Forms.ToolStripStatusLabel(); plateUtilStatusLabel = new System.Windows.Forms.ToolStripStatusLabel(); selectionStatusLabel = new System.Windows.Forms.ToolStripStatusLabel(); toolStrip1 = new System.Windows.Forms.ToolStrip(); @@ -1094,7 +1092,7 @@ namespace OpenNest.Forms // // statusStrip1 // - statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { statusLabel1, locationStatusLabel, selectionStatusLabel, spacerLabel, plateIndexStatusLabel, plateSizeStatusLabel, plateQtyStatusLabel, plateUtilStatusLabel }); + statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { statusLabel1, locationStatusLabel, selectionStatusLabel, spacerLabel, materialStatusLabel, plateUtilStatusLabel }); statusStrip1.Location = new System.Drawing.Point(0, 630); statusStrip1.Name = "statusStrip1"; statusStrip1.Padding = new System.Windows.Forms.Padding(1, 0, 16, 0); @@ -1130,30 +1128,16 @@ namespace OpenNest.Forms spacerLabel.Padding = new System.Windows.Forms.Padding(5, 0, 5, 0); spacerLabel.Size = new System.Drawing.Size(931, 19); spacerLabel.Spring = true; - // - // plateIndexStatusLabel - // - plateIndexStatusLabel.BorderSides = System.Windows.Forms.ToolStripStatusLabelBorderSides.Left; - plateIndexStatusLabel.Name = "plateIndexStatusLabel"; - plateIndexStatusLabel.Padding = new System.Windows.Forms.Padding(5, 0, 5, 0); - plateIndexStatusLabel.Size = new System.Drawing.Size(85, 19); - plateIndexStatusLabel.Text = "Plate : 0 of 0"; - // - // plateSizeStatusLabel - // - plateSizeStatusLabel.BorderSides = System.Windows.Forms.ToolStripStatusLabelBorderSides.Left; - plateSizeStatusLabel.Name = "plateSizeStatusLabel"; - plateSizeStatusLabel.Padding = new System.Windows.Forms.Padding(5, 0, 5, 0); - plateSizeStatusLabel.Size = new System.Drawing.Size(67, 19); - plateSizeStatusLabel.Text = "Size : 0x0"; - // - // plateQtyStatusLabel - // - plateQtyStatusLabel.BorderSides = System.Windows.Forms.ToolStripStatusLabelBorderSides.Left; - plateQtyStatusLabel.Name = "plateQtyStatusLabel"; - plateQtyStatusLabel.Padding = new System.Windows.Forms.Padding(5, 0, 5, 0); - plateQtyStatusLabel.Size = new System.Drawing.Size(55, 19); - plateQtyStatusLabel.Text = "Qty : 0"; + // + // materialStatusLabel + // + materialStatusLabel.BorderSides = System.Windows.Forms.ToolStripStatusLabelBorderSides.Left; + materialStatusLabel.Name = "materialStatusLabel"; + materialStatusLabel.Padding = new System.Windows.Forms.Padding(5, 0, 5, 0); + materialStatusLabel.Size = new System.Drawing.Size(85, 19); + materialStatusLabel.Text = "Material: (none)"; + materialStatusLabel.ToolTipText = "Click to edit nest info"; + materialStatusLabel.Click += MaterialStatusLabel_Click; // // plateUtilStatusLabel // @@ -1374,11 +1358,10 @@ namespace OpenNest.Forms private System.Windows.Forms.ToolStripSeparator toolStripSeparator2; private System.Windows.Forms.ToolStripMenuItem mnuPlateRotate180; private System.Windows.Forms.StatusStrip statusStrip1; - private System.Windows.Forms.ToolStripStatusLabel plateSizeStatusLabel; private System.Windows.Forms.ToolStripStatusLabel locationStatusLabel; private System.Windows.Forms.ToolStrip toolStrip1; private System.Windows.Forms.ToolStripButton btnOpen; - private System.Windows.Forms.ToolStripStatusLabel plateIndexStatusLabel; + private System.Windows.Forms.ToolStripStatusLabel materialStatusLabel; private System.Windows.Forms.ToolStripSeparator toolStripSeparator1; private System.Windows.Forms.ToolStripMenuItem mnuWindow; private System.Windows.Forms.ToolStripMenuItem mnuWindowCascade; @@ -1386,7 +1369,6 @@ namespace OpenNest.Forms private System.Windows.Forms.ToolStripMenuItem mnuWindowTileHorizontal; private System.Windows.Forms.ToolStripSeparator toolStripMenuItem10; private System.Windows.Forms.ToolStripMenuItem mnuCloseAll; - private System.Windows.Forms.ToolStripStatusLabel plateQtyStatusLabel; private System.Windows.Forms.ToolStripStatusLabel plateUtilStatusLabel; private System.Windows.Forms.ToolStripMenuItem mnuFileExportAll; private System.Windows.Forms.ToolStripMenuItem mnuFileExportNestReport; diff --git a/OpenNest/Forms/MainForm.Nest.cs b/OpenNest/Forms/MainForm.Nest.cs index fc7ff18d..c8ba479e 100644 --- a/OpenNest/Forms/MainForm.Nest.cs +++ b/OpenNest/Forms/MainForm.Nest.cs @@ -47,8 +47,10 @@ namespace OpenNest.Forms private async void EditNest_Click(object sender, EventArgs e) { - if (activeForm != null) - await activeForm.ShowNestInfoEditorAsync(); + if (activeForm == null) + return; + await activeForm.ShowNestInfoEditorAsync(); + statusBar.UpdatePlate(); // Material and thickness may have changed. } private void Clamps_Click(object sender, EventArgs e) diff --git a/OpenNest/Forms/MainForm.Status.cs b/OpenNest/Forms/MainForm.Status.cs index 3d5c704e..c781a220 100644 --- a/OpenNest/Forms/MainForm.Status.cs +++ b/OpenNest/Forms/MainForm.Status.cs @@ -65,6 +65,9 @@ namespace OpenNest.Forms private void LocationStatusLabel_Click(object sender, EventArgs e) => statusBar.ToggleLocationMode(); + private void MaterialStatusLabel_Click(object sender, EventArgs e) => + EditNest_Click(sender, e); + #endregion Statusbar Events } } diff --git a/OpenNest/Forms/MainForm.cs b/OpenNest/Forms/MainForm.cs index 76c56364..3b1a40b1 100644 --- a/OpenNest/Forms/MainForm.cs +++ b/OpenNest/Forms/MainForm.cs @@ -106,7 +106,7 @@ namespace OpenNest.Forms storage.StateChanged += Storage_StateChanged; InitializeComponent(); statusBar = new MainStatusBar(statusLabel1, locationStatusLabel, selectionStatusLabel, - plateIndexStatusLabel, plateSizeStatusLabel, plateQtyStatusLabel, plateUtilStatusLabel); + materialStatusLabel, plateUtilStatusLabel); toolStrip1.Items.Add(CreateActionButton("btnCutPlanner", "Plan Cutting", Resources.cut_planner, PlatePlanCutting_Click)); toolStrip1.Items.Add(CreateActionButton("btnSimulateCutting", "Simulate Cutting", diff --git a/OpenNest/Forms/MainStatusBar.cs b/OpenNest/Forms/MainStatusBar.cs index a0d95a87..cb56577a 100644 --- a/OpenNest/Forms/MainStatusBar.cs +++ b/OpenNest/Forms/MainStatusBar.cs @@ -1,5 +1,7 @@ using System; +using System.Collections.Generic; using System.Drawing; +using System.Globalization; using System.Linq; using System.Windows.Forms; using OpenNest.Collections; @@ -10,7 +12,8 @@ namespace OpenNest.Forms { /// /// The main window's status labels for the active nest document: status text, cursor - /// location, selection bounds and plate index/size/quantity/utilization. The location + /// location, selection bounds, nest material and plate utilization. Plate number, size and + /// quantity live in the plate view header instead. The location /// follows the mouse (the default, throttled), or only left clicks while click mode is on; /// the mode persists as a user setting. /// @@ -22,9 +25,7 @@ namespace OpenNest.Forms private readonly ToolStripStatusLabel status; private readonly ToolStripStatusLabel location; private readonly ToolStripStatusLabel selection; - private readonly ToolStripStatusLabel plateIndex; - private readonly ToolStripStatusLabel plateSize; - private readonly ToolStripStatusLabel plateQty; + private readonly ToolStripStatusLabel material; private readonly ToolStripStatusLabel plateUtil; private readonly Timer locationTimer; @@ -33,15 +34,12 @@ namespace OpenNest.Forms private bool locationPending; public MainStatusBar(ToolStripStatusLabel status, ToolStripStatusLabel location, - ToolStripStatusLabel selection, ToolStripStatusLabel plateIndex, ToolStripStatusLabel plateSize, - ToolStripStatusLabel plateQty, ToolStripStatusLabel plateUtil) + ToolStripStatusLabel selection, ToolStripStatusLabel material, ToolStripStatusLabel plateUtil) { this.status = status; this.location = location; this.selection = selection; - this.plateIndex = plateIndex; - this.plateSize = plateSize; - this.plateQty = plateQty; + this.material = material; this.plateUtil = plateUtil; locationTimer = new Timer { Interval = LocationRefreshIntervalMs }; @@ -126,25 +124,13 @@ namespace OpenNest.Forms { if (form == null) { - plateIndex.Text = string.Empty; - plateSize.Text = string.Empty; - plateQty.Text = string.Empty; + material.Text = string.Empty; plateUtil.Text = string.Empty; return; } - plateIndex.Text = string.Format( - "Plate: {0} of {1}", - form.PlateManager.CurrentIndex + 1, - form.PlateManager.Count - ); - - plateSize.Text = string.Format("Size: {0}", form.PlateView.Plate.Size); - - plateQty.Text = string.Format( - "Qty: {0}", - form.PlateView.Plate.Quantity - ); + var nest = form.Nest; + material.Text = FormatMaterial(nest.Material?.Name, nest.Thickness, nest.Units); plateUtil.Text = string.Format( "Util: {0:P1}", @@ -152,6 +138,18 @@ namespace OpenNest.Forms ); } + /// Status text for the nest's material and thickness, e.g. "Material: A36 · 0.25 in". + internal static string FormatMaterial(string name, double thickness, Units units) + { + var parts = new List(); + if (!string.IsNullOrWhiteSpace(name)) + parts.Add(name.Trim()); + if (thickness > 0) + parts.Add(thickness.ToString("0.####", CultureInfo.CurrentCulture) + " " + UnitsHelper.GetShortString(units)); + + return "Material: " + (parts.Count == 0 ? "(none)" : string.Join(" · ", parts)); + } + public void UpdateSelection() { if (form == null || form.PlateView.SelectedParts.Count == 0)