Disable toolbar buttons that cannot be used

Plate commands need an open nest and no running job; Trim/Extend Cut-Off
needs a cut-off on the plate and Sequence by Path needs two parts. The
Plate menu's Trim/Extend item follows the same rule.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ajandClaude Opus 5.5 committed 2026-11-01 16:33:36 -05:00
1 parent a89adc7911
commit d57678ecee
4 files changed
+114 -7

No files matched your search

@@ -1,4 +1,5 @@
using System.Reflection;
using System.Windows.Forms;
using OpenNest.Controls;
using OpenNest.Forms;
using OpenNest.Geometry;
@@ -77,6 +78,65 @@ public class MainFormCharacterizationTests
Assert.Equal(new Guards(false, true, false, false), Read(host, editor, views));
}, TimeSpan.FromMinutes(1), "The STA test did not complete.");
[Fact]
public void ToolbarFollowsTheActiveDocumentAndItsPlate() => UndoTestEnv.Run(env =>
{
var host = env.Host;
bool Enabled(string button) => Field<ToolStripItem>(typeof(MainForm), host, button).Enabled;
void AddCutOff()
{
using var change = env.View.BeginChange("Add Cut-Off");
change.MarkChanged();
env.View.Plate.CutOffs.Add(new CutOff(new Vector(50, 0), CutOffAxis.Vertical));
env.View.Plate.RegenerateCutOffs(env.View.CutOffSettings);
change.Complete();
}
// The first plate holds two parts and no cut-offs.
env.Editor.PlateManager.LoadAt(0);
Assert.True(Enabled("btnCutOff"));
Assert.True(Enabled("btnSequenceByPath"));
Assert.False(Enabled("btnTrimExtendCutOff"));
Assert.False(UndoTestEnv.MenuItem(host, "mnuPlateTrimExtendCutOff").Enabled);
AddCutOff();
Assert.True(Enabled("btnTrimExtendCutOff"));
Assert.True(UndoTestEnv.MenuItem(host, "mnuPlateTrimExtendCutOff").Enabled);
Assert.True(host.TryUndo(env.Editor));
Assert.False(Enabled("btnTrimExtendCutOff"));
Assert.True(host.TryRedo(env.Editor));
Assert.True(Enabled("btnTrimExtendCutOff"));
// Another plate has its own cut-offs; the third one is empty.
env.Editor.PlateManager.LoadAt(2);
Assert.False(Enabled("btnTrimExtendCutOff"));
Assert.False(Enabled("btnSequenceByPath"));
env.View.Plate.Parts.Add(new Part(env.Nest.Drawings.First(), new Vector(5, 5)));
env.View.Plate.Parts.Add(new Part(env.Nest.Drawings.First(), new Vector(25, 5)));
Assert.True(Enabled("btnSequenceByPath"));
// A running job locks the plate commands, as it does the Nest and Plate menus.
Invoke(host, "SetNestingLockout", true);
Assert.False(Enabled("btnAutoNest"));
Assert.False(Enabled("btnCutOff"));
Assert.False(Enabled("postButton"));
Invoke(host, "SetNestingLockout", false);
Assert.True(Enabled("btnAutoNest"));
env.Editor.Close();
foreach (var button in ToolbarPlateCommands)
Assert.False(Enabled(button), button);
Assert.True(Enabled("btnNew"));
Assert.True(Enabled("btnOpen"));
});
private static readonly string[] ToolbarPlateCommands =
{
"btnSave", "btnSaveAs", "btnAutoNest", "btnShowRemnants", "btnCutOff", "btnTrimExtendCutOff",
"btnCutPlanner", "btnSimulateCutting", "btnSequenceByPath", "postButton",
};
private sealed record Guards(bool ExpandSpacing, bool CuttingPlan, bool Clamps, bool NestJob);
private static Guards Read(MainForm host, EditNestForm editor, PlateView[] views) => new(
+28
View File
@@ -1,5 +1,6 @@
using System;
using System.Windows.Forms;
using OpenNest.Collections;
namespace OpenNest.Forms
{
@@ -8,6 +9,7 @@ namespace OpenNest.Forms
private void EnableCheck()
{
NavigationEnableCheck();
ToolbarEnableCheck();
var hasValue = activeForm != null;
@@ -45,6 +47,31 @@ namespace OpenNest.Forms
mnuNestLastPlate.Enabled = hasPlates && !plates.IsLast;
}
// The plate commands need a plate to act on and, like the Nest and Plate menus, no
// running job. Trim/Extend works on existing cut-offs; Sequence by Path needs two parts.
private void ToolbarEnableCheck()
{
var plate = nestingInProgress ? null : activeForm?.PlateView?.Plate;
var hasPlate = plate != null;
var hasCutOffs = plate?.CutOffs.Count > 0;
btnAutoNest.Enabled = hasPlate;
btnShowRemnants.Enabled = hasPlate;
btnCutOff.Enabled = hasPlate;
btnTrimExtendCutOff.Enabled = hasCutOffs;
mnuPlateTrimExtendCutOff.Enabled = hasCutOffs;
btnCutPlanner.Enabled = hasPlate;
btnSimulateCutting.Enabled = hasPlate;
btnSequenceByPath.Enabled = plate?.Parts.Count >= 2;
postButton.Enabled = hasPlate;
}
private void EditForm_PlateChanged(object sender, EventArgs e) => ToolbarEnableCheck();
private void PlateView_PartAdded(object sender, ItemAddedEventArgs<Part> e) => ToolbarEnableCheck();
private void PlateView_PartRemoved(object sender, ItemRemovedEventArgs<Part> e) => ToolbarEnableCheck();
private void SetNestingLockout(bool locked)
{
nestingInProgress = locked;
@@ -53,6 +80,7 @@ namespace OpenNest.Forms
mnuNest.Enabled = !locked;
mnuPlate.Enabled = !locked;
NavigationEnableCheck(locked);
ToolbarEnableCheck();
}
private void EngineComboBox_SelectedIndexChanged(object sender, EventArgs e)
+6 -1
View File
@@ -105,7 +105,12 @@ namespace OpenNest.Forms
UpdateUndoMenu();
}
private void History_Changed(object sender, EventArgs e) => UpdateUndoMenu();
// Every recorded edit lands here, including those that add or remove a cut-off.
private void History_Changed(object sender, EventArgs e)
{
UpdateUndoMenu();
ToolbarEnableCheck();
}
internal void UpdateUndoMenu()
{
+20 -6
View File
@@ -25,6 +25,9 @@ namespace OpenNest.Forms
private readonly NestStorageController storage;
private readonly ToolStripMenuItem storageModeMenu = new("Storage Mode...");
private readonly ToolStripMenuItem exportNestMenu = new("Export .nest...");
private readonly ToolStripButton btnCutPlanner;
private readonly ToolStripButton btnSimulateCutting;
private readonly ToolStripButton btnSequenceByPath;
private readonly ToolStripDropDownButton postButton = new()
{
Name = "btnPost",
@@ -124,12 +127,15 @@ namespace OpenNest.Forms
statusStripToolTips = new StatusStripToolTips(statusStrip1); // The hold list lives in the ledger label's tooltip.
statusBar = new MainStatusBar(statusLabel1, locationStatusLabel, selectionStatusLabel,
machineStatusLabel, materialStatusLabel, plateUtilStatusLabel, ledgerStatusLabel);
toolStrip1.Items.Add(CreateActionButton("btnCutPlanner", "Plan Cutting",
Resources.cut_planner, ToolbarPlanCutting_Click));
toolStrip1.Items.Add(CreateActionButton("btnSimulateCutting", "Simulate Cutting",
Resources.cut_simulate, SimulateCutting_Click));
toolStrip1.Items.Add(CreateActionButton("btnSequenceByPath", "Sequence by Path",
Resources.manual_sequence, SequenceByPath_Click));
btnCutPlanner = CreateActionButton("btnCutPlanner", "Plan Cutting",
Resources.cut_planner, ToolbarPlanCutting_Click);
btnSimulateCutting = CreateActionButton("btnSimulateCutting", "Simulate Cutting",
Resources.cut_simulate, SimulateCutting_Click);
btnSequenceByPath = CreateActionButton("btnSequenceByPath", "Sequence by Path",
Resources.manual_sequence, SequenceByPath_Click);
toolStrip1.Items.Add(btnCutPlanner);
toolStrip1.Items.Add(btnSimulateCutting);
toolStrip1.Items.Add(btnSequenceByPath);
toolStrip1.Items.Add(postButton);
storageModeMenu.Click += StorageMode_Click;
exportNestMenu.Click += ExportNest_Click;
@@ -262,7 +268,12 @@ namespace OpenNest.Forms
statusBar.Unbind();
if (activeForm != null)
{
activeForm.PlateChanged -= EditForm_PlateChanged;
activeForm.PlateView.OverlapStateChanged -= OverlapStateChanged;
activeForm.PlateView.PartAdded -= PlateView_PartAdded;
activeForm.PlateView.PartRemoved -= PlateView_PartRemoved;
}
// If nesting is in progress and the active form changed, cancel nesting
if (nestingInProgress && nestingCts != null)
@@ -280,7 +291,10 @@ namespace OpenNest.Forms
if (activeForm == null)
return;
activeForm.PlateChanged += EditForm_PlateChanged;
activeForm.PlateView.OverlapStateChanged += OverlapStateChanged;
activeForm.PlateView.PartAdded += PlateView_PartAdded;
activeForm.PlateView.PartRemoved += PlateView_PartRemoved;
var activeNest = activeForm.Nest;
activeForm.PlateView.CanEditClamps = () => !ClampsInUse(activeNest);
mnuViewDrawRapids.Checked = activeForm.PlateView.DrawRapid;