refactor(ui): extract MainForm status bar binding

MainStatusBar owns the status, location, selection and plate labels and
the click-vs-move location mode. Bind(EditNestForm)/Unbind() replace the
manual PlateView subscribe/unsubscribe pairs in OnMdiChildActivate;
MainForm keeps only its overlap-menu subscription. Applying the location
mode now detaches both mouse handlers first, which also drops the old
self-assignment of the mode flag.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ajandClaude Opus 5.5 committed 2026-10-17 21:14:18 -04:00
1 parent 9b46db4891
commit 93d118585f
5 files changed
+224 -175

No files matched your search

+1 -1
View File
@@ -22,7 +22,7 @@ namespace OpenNest.Forms
editForm.PlateChanged += (sender, e) =>
{
NavigationEnableCheck();
UpdatePlateStatus();
statusBar.UpdatePlate();
mnuPlateRemove.Enabled = activeForm?.PlateManager.CanRemoveCurrent ?? false;
};
editForm.WindowState = windowState;
+1 -1
View File
@@ -43,7 +43,7 @@ namespace OpenNest.Forms
if (activeForm == null)
return;
activeForm.ResizePlateToFitParts();
UpdatePlateStatus();
statusBar.UpdatePlate();
}
private void RotateCw_Click(object sender, EventArgs e)
+2 -148
View File
@@ -1,9 +1,5 @@
using System;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using OpenNest.Collections;
using OpenNest.Geometry;
namespace OpenNest.Forms
{
@@ -58,159 +54,17 @@ namespace OpenNest.Forms
NavigationEnableCheck(locked);
}
private void UpdateLocationStatus()
{
if (activeForm == null)
{
locationStatusLabel.Text = string.Empty;
return;
}
locationStatusLabel.Text = string.Format(
"Location: [{0}, {1}]",
activeForm.PlateView.CurrentPoint.X.ToString("n4"),
activeForm.PlateView.CurrentPoint.Y.ToString("n4")
);
}
private void UpdatePlateStatus()
{
if (activeForm == null)
{
plateIndexStatusLabel.Text = string.Empty;
plateSizeStatusLabel.Text = string.Empty;
plateQtyStatusLabel.Text = string.Empty;
plateUtilStatusLabel.Text = string.Empty;
return;
}
plateIndexStatusLabel.Text = string.Format(
"Plate: {0} of {1}",
activeForm.PlateManager.CurrentIndex + 1,
activeForm.PlateManager.Count
);
plateSizeStatusLabel.Text = string.Format("Size: {0}", activeForm.PlateView.Plate.Size);
plateQtyStatusLabel.Text = string.Format(
"Qty: {0}",
activeForm.PlateView.Plate.Quantity
);
plateUtilStatusLabel.Text = string.Format(
"Util: {0:P1}",
activeForm.PlateView.Plate.Utilization()
);
}
private void UpdateSelectionStatus()
{
if (activeForm == null || activeForm.PlateView.SelectedParts.Count == 0)
{
selectionStatusLabel.Text = string.Empty;
return;
}
var selected = activeForm.PlateView.SelectedParts;
if (selected.Count == 1)
{
var box = selected[0].BoundingBox;
selectionStatusLabel.Text = string.Format(
"Selected: [{0}, {1}] {2} x {3}",
box.X.ToString("n4"),
box.Y.ToString("n4"),
box.Width.ToString("n4"),
box.Length.ToString("n4")
);
}
else
{
var bounds = selected.Select(p => p.BasePart).ToList().GetBoundingBox();
selectionStatusLabel.Text = string.Format(
"Selected ({0}): [{1}, {2}] {3} x {4}",
selected.Count,
bounds.X.ToString("n4"),
bounds.Y.ToString("n4"),
bounds.Width.ToString("n4"),
bounds.Length.ToString("n4")
);
}
}
private void UpdateStatus()
{
UpdateLocationStatus();
UpdatePlateStatus();
UpdateSelectionStatus();
}
private void EngineComboBox_SelectedIndexChanged(object sender, EventArgs e)
{
if (engineComboBox.SelectedItem is string name)
EngineSelection.EngineName = name;
}
private void UpdateLocationMode()
{
if (activeForm == null)
return;
if (clickUpdateLocation)
{
clickUpdateLocation = true;
locationStatusLabel.ForeColor = Color.Gray;
activeForm.PlateView.MouseMove -= PlateView_MouseMove;
activeForm.PlateView.MouseClick += PlateView_MouseClick;
}
else
{
clickUpdateLocation = false;
locationStatusLabel.ForeColor = Color.Black;
activeForm.PlateView.MouseMove += PlateView_MouseMove;
activeForm.PlateView.MouseClick -= PlateView_MouseClick;
}
}
#region Statusbar Events
private void LocationStatusLabel_Click(object sender, EventArgs e)
{
clickUpdateLocation = !clickUpdateLocation;
UpdateLocationMode();
}
private void LocationStatusLabel_Click(object sender, EventArgs e) =>
statusBar.ToggleLocationMode();
#endregion Statusbar Events
#region PlateView Events
private void PlateView_PartAdded(object sender, ItemAddedEventArgs<Part> e) =>
UpdatePlateStatus();
private void PlateView_PartRemoved(object sender, ItemRemovedEventArgs<Part> e) =>
UpdatePlateStatus();
private void PlateView_MouseMove(object sender, MouseEventArgs e)
{
UpdateLocationStatus();
}
private void PlateView_MouseClick(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
UpdateLocationStatus();
}
private void PlateView_StatusChanged(object sender, EventArgs e)
{
statusLabel1.Text = activeForm.PlateView.Status;
}
private void PlateView_SelectionChanged(object sender, EventArgs e)
{
UpdateSelectionStatus();
}
#endregion PlateView Events
}
}
+6 -25
View File
@@ -17,7 +17,7 @@ namespace OpenNest.Forms
{
private EditNestForm activeForm;
private bool clickUpdateLocation;
private readonly MainStatusBar statusBar;
private bool nestingInProgress;
private CancellationTokenSource nestingCts;
private readonly NestStorageController storage;
@@ -98,6 +98,8 @@ namespace OpenNest.Forms
storage = new NestStorageController(this, text => statusLabel1.Text = text);
storage.StateChanged += Storage_StateChanged;
InitializeComponent();
statusBar = new MainStatusBar(statusLabel1, locationStatusLabel, selectionStatusLabel,
plateIndexStatusLabel, plateSizeStatusLabel, plateQtyStatusLabel, plateUtilStatusLabel);
toolStrip1.Items.Add(CreateActionButton("btnCutPlanner", "Plan Cutting",
Resources.cut_planner, PlatePlanCutting_Click));
toolStrip1.Items.Add(CreateActionButton("btnManualSequence", "Manual Sequence",
@@ -117,14 +119,12 @@ namespace OpenNest.Forms
mnuViewZoomIn.ShortcutKeyDisplayString = "Ctrl+Plus";
mnuViewZoomOut.ShortcutKeyDisplayString = "Ctrl+Minus";
clickUpdateLocation = true;
this.SetBevel(false);
AllowDrop = true;
LoadPosts(PostProcessorLoader.Load(PostProcessorLoader.DefaultDirectory));
EnableCheck();
UpdateStatus();
statusBar.UpdateAll();
// Jobs-side plug-in discovery: INestingEngine implementations are registered per
// assembly/type with per-DLL isolation. Existing plug-ins must implement the jobs
@@ -205,16 +205,9 @@ namespace OpenNest.Forms
editor.CuttingInspection.RequestClose();
base.OnMdiChildActivate(e);
statusBar.Unbind();
if (activeForm != null)
{
activeForm.PlateView.MouseMove -= PlateView_MouseMove;
activeForm.PlateView.MouseClick -= PlateView_MouseClick;
activeForm.PlateView.StatusChanged -= PlateView_StatusChanged;
activeForm.PlateView.OverlapStateChanged -= OverlapStateChanged;
activeForm.PlateView.SelectionChanged -= PlateView_SelectionChanged;
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)
@@ -226,30 +219,18 @@ namespace OpenNest.Forms
UpdateOverlapMenu();
EnableCheck();
UpdatePlateStatus();
UpdateLocationStatus();
statusBar.Bind(activeForm);
if (activeForm == null)
{
statusLabel1.Text = "";
selectionStatusLabel.Text = "";
return;
}
UpdateLocationMode();
UpdateSelectionStatus();
activeForm.PlateView.StatusChanged += PlateView_StatusChanged;
activeForm.PlateView.OverlapStateChanged += OverlapStateChanged;
activeForm.PlateView.SelectionChanged += PlateView_SelectionChanged;
activeForm.PlateView.PartAdded += PlateView_PartAdded;
activeForm.PlateView.PartRemoved += PlateView_PartRemoved;
var activeNest = activeForm.Nest;
activeForm.PlateView.CanEditClamps = () => !ClampsInUse(activeNest);
mnuViewDrawRapids.Checked = activeForm.PlateView.DrawRapid;
mnuViewDrawPiercePoints.Checked = activeForm.PlateView.DrawPiercePoints;
mnuViewDrawBounds.Checked = activeForm.PlateView.DrawBounds;
mnuViewDrawCutDirection.Checked = activeForm.PlateView.DrawCutDirection;
statusLabel1.Text = activeForm.PlateView.Status;
}
protected override void OnLoad(EventArgs e)
+214
View File
@@ -0,0 +1,214 @@
using System;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using OpenNest.Collections;
using OpenNest.Geometry;
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
/// follows the mouse, or only left clicks while click mode is on (the default).
/// </summary>
internal sealed class MainStatusBar
{
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 plateUtil;
private EditNestForm form;
private bool clickUpdatesLocation = true;
public MainStatusBar(ToolStripStatusLabel status, ToolStripStatusLabel location,
ToolStripStatusLabel selection, ToolStripStatusLabel plateIndex, ToolStripStatusLabel plateSize,
ToolStripStatusLabel plateQty, ToolStripStatusLabel plateUtil)
{
this.status = status;
this.location = location;
this.selection = selection;
this.plateIndex = plateIndex;
this.plateSize = plateSize;
this.plateQty = plateQty;
this.plateUtil = plateUtil;
}
/// <summary>
/// Follows <paramref name="editForm"/>'s plate view after detaching from the previous
/// document; null clears every label.
/// </summary>
public void Bind(EditNestForm editForm)
{
Unbind();
form = editForm;
UpdatePlate();
UpdateLocation();
if (form == null)
{
status.Text = "";
selection.Text = "";
return;
}
ApplyLocationMode();
UpdateSelection();
var view = form.PlateView;
view.StatusChanged += PlateView_StatusChanged;
view.SelectionChanged += PlateView_SelectionChanged;
view.PartAdded += PlateView_PartAdded;
view.PartRemoved += PlateView_PartRemoved;
status.Text = view.Status;
}
public void Unbind()
{
if (form == null)
return;
var view = form.PlateView;
view.MouseMove -= PlateView_MouseMove;
view.MouseClick -= PlateView_MouseClick;
view.StatusChanged -= PlateView_StatusChanged;
view.SelectionChanged -= PlateView_SelectionChanged;
view.PartAdded -= PlateView_PartAdded;
view.PartRemoved -= PlateView_PartRemoved;
form = null;
}
public void ToggleLocationMode()
{
clickUpdatesLocation = !clickUpdatesLocation;
ApplyLocationMode();
}
public void UpdateAll()
{
UpdateLocation();
UpdatePlate();
UpdateSelection();
}
public void UpdateLocation()
{
if (form == null)
{
location.Text = string.Empty;
return;
}
location.Text = string.Format(
"Location: [{0}, {1}]",
form.PlateView.CurrentPoint.X.ToString("n4"),
form.PlateView.CurrentPoint.Y.ToString("n4")
);
}
public void UpdatePlate()
{
if (form == null)
{
plateIndex.Text = string.Empty;
plateSize.Text = string.Empty;
plateQty.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
);
plateUtil.Text = string.Format(
"Util: {0:P1}",
form.PlateView.Plate.Utilization()
);
}
public void UpdateSelection()
{
if (form == null || form.PlateView.SelectedParts.Count == 0)
{
selection.Text = string.Empty;
return;
}
var selected = form.PlateView.SelectedParts;
if (selected.Count == 1)
{
var box = selected[0].BoundingBox;
selection.Text = string.Format(
"Selected: [{0}, {1}] {2} x {3}",
box.X.ToString("n4"),
box.Y.ToString("n4"),
box.Width.ToString("n4"),
box.Length.ToString("n4")
);
}
else
{
var bounds = selected.Select(p => p.BasePart).ToList().GetBoundingBox();
selection.Text = string.Format(
"Selected ({0}): [{1}, {2}] {3} x {4}",
selected.Count,
bounds.X.ToString("n4"),
bounds.Y.ToString("n4"),
bounds.Width.ToString("n4"),
bounds.Length.ToString("n4")
);
}
}
private void ApplyLocationMode()
{
if (form == null)
return;
var view = form.PlateView;
// Detach both first so repeated calls never stack handlers.
view.MouseMove -= PlateView_MouseMove;
view.MouseClick -= PlateView_MouseClick;
if (clickUpdatesLocation)
{
location.ForeColor = Color.Gray;
view.MouseClick += PlateView_MouseClick;
}
else
{
location.ForeColor = Color.Black;
view.MouseMove += PlateView_MouseMove;
}
}
private void PlateView_PartAdded(object sender, ItemAddedEventArgs<Part> e) => UpdatePlate();
private void PlateView_PartRemoved(object sender, ItemRemovedEventArgs<Part> e) => UpdatePlate();
private void PlateView_MouseMove(object sender, MouseEventArgs e) => UpdateLocation();
private void PlateView_MouseClick(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
UpdateLocation();
}
private void PlateView_StatusChanged(object sender, EventArgs e) => status.Text = form.PlateView.Status;
private void PlateView_SelectionChanged(object sender, EventArgs e) => UpdateSelection();
}
}