feat(ui): replace status bar plate labels with nest material

The status bar's "Plate N of M" counted the trailing empty plate, and its
plate number and size repeated what the plate view header already shows.
Drop Plate, Size and Qty from the status bar. Show the plate quantity in the
header as "×N" when it is above one, and keep Util in the status bar so it
stays visible while the Drawings tab is open.

Add a Material label (name and thickness) that opens Nest Info when
clicked. Nest Info remains the only place to edit material, and the label
refreshes after the dialog closes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ajandClaude Opus 5.5 committed 2026-10-19 11:53:46 -04:00
1 parent 3d1270a860
commit be8d1443e3
9 files changed
+94 -64

No files matched your search

+12 -5
View File
@@ -48,17 +48,24 @@ namespace OpenNest
return plate != null && plate.Parts.Count == 0;
}
/// <summary>Header text for the plate at <paramref name="storageIndex"/>.</summary>
public static string FormatHeader(IList<Plate> plates, int storageIndex, string plateSizeText)
/// <summary>
/// Header text for the plate at <paramref name="storageIndex"/>. A quantity above one is
/// appended as "×N"; a single sheet shows no quantity.
/// </summary>
public static string FormatHeader(IList<Plate> 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;
}
}
}
@@ -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()
{
@@ -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;
}
}
}
+1 -1
View File
@@ -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;
+13 -31
View File
@@ -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;
+4 -2
View File
@@ -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)
+3
View File
@@ -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
}
}
+1 -1
View File
@@ -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",
+22 -24
View File
@@ -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
{
/// <summary>
/// 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.
/// </summary>
@@ -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
);
}
/// <summary>Status text for the nest's material and thickness, e.g. "Material: A36 · 0.25 in".</summary>
internal static string FormatMaterial(string name, double thickness, Units units)
{
var parts = new List<string>();
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)