From 1db51b1cce4610fa572c1d63b45b68f2cb315d82 Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Sat, 14 Mar 2026 12:41:14 -0400 Subject: [PATCH] feat(ui): add elapsed timer to nest progress form Show a live elapsed-time counter that updates every second during nesting. Rename "Remnant" label to "Unused" for clarity. Co-Authored-By: Claude Opus 4.6 (1M context) --- OpenNest/Forms/NestProgressForm.Designer.cs | 30 ++++++++++++++++++--- OpenNest/Forms/NestProgressForm.cs | 26 ++++++++++++++++++ 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/OpenNest/Forms/NestProgressForm.Designer.cs b/OpenNest/Forms/NestProgressForm.Designer.cs index 573e08f..6670048 100644 --- a/OpenNest/Forms/NestProgressForm.Designer.cs +++ b/OpenNest/Forms/NestProgressForm.Designer.cs @@ -39,6 +39,8 @@ namespace OpenNest.Forms this.densityValue = new System.Windows.Forms.Label(); this.remnantLabel = new System.Windows.Forms.Label(); this.remnantValue = new System.Windows.Forms.Label(); + this.elapsedLabel = new System.Windows.Forms.Label(); + this.elapsedValue = new System.Windows.Forms.Label(); this.stopButton = new System.Windows.Forms.Button(); this.buttonPanel = new System.Windows.Forms.FlowLayoutPanel(); this.table.SuspendLayout(); @@ -60,18 +62,21 @@ namespace OpenNest.Forms this.table.Controls.Add(this.densityValue, 1, 3); this.table.Controls.Add(this.remnantLabel, 0, 4); this.table.Controls.Add(this.remnantValue, 1, 4); + this.table.Controls.Add(this.elapsedLabel, 0, 5); + this.table.Controls.Add(this.elapsedValue, 1, 5); this.table.Dock = System.Windows.Forms.DockStyle.Top; this.table.Location = new System.Drawing.Point(0, 0); this.table.Name = "table"; this.table.Padding = new System.Windows.Forms.Padding(8); - this.table.RowCount = 5; + this.table.RowCount = 6; + this.table.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.AutoSize)); this.table.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.AutoSize)); this.table.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.AutoSize)); this.table.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.AutoSize)); this.table.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.AutoSize)); this.table.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.AutoSize)); this.table.AutoSize = true; - this.table.Size = new System.Drawing.Size(264, 130); + this.table.Size = new System.Drawing.Size(264, 156); this.table.TabIndex = 0; // // phaseLabel @@ -140,7 +145,7 @@ namespace OpenNest.Forms this.remnantLabel.Font = new System.Drawing.Font(System.Drawing.SystemFonts.DefaultFont, System.Drawing.FontStyle.Bold); this.remnantLabel.Margin = new System.Windows.Forms.Padding(4); this.remnantLabel.Name = "remnantLabel"; - this.remnantLabel.Text = "Remnant:"; + this.remnantLabel.Text = "Unused:"; // // remnantValue // @@ -149,6 +154,21 @@ namespace OpenNest.Forms this.remnantValue.Name = "remnantValue"; this.remnantValue.Text = "\u2014"; // + // elapsedLabel + // + this.elapsedLabel.AutoSize = true; + this.elapsedLabel.Font = new System.Drawing.Font(System.Drawing.SystemFonts.DefaultFont, System.Drawing.FontStyle.Bold); + this.elapsedLabel.Margin = new System.Windows.Forms.Padding(4); + this.elapsedLabel.Name = "elapsedLabel"; + this.elapsedLabel.Text = "Elapsed:"; + // + // elapsedValue + // + this.elapsedValue.AutoSize = true; + this.elapsedValue.Margin = new System.Windows.Forms.Padding(4); + this.elapsedValue.Name = "elapsedValue"; + this.elapsedValue.Text = "0:00"; + // // stopButton // this.stopButton.Anchor = System.Windows.Forms.AnchorStyles.None; @@ -174,7 +194,7 @@ namespace OpenNest.Forms // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(264, 181); + this.ClientSize = new System.Drawing.Size(264, 207); this.Controls.Add(this.buttonPanel); this.Controls.Add(this.table); this.Controls.SetChildIndex(this.table, 0); @@ -206,6 +226,8 @@ namespace OpenNest.Forms private System.Windows.Forms.Label densityValue; private System.Windows.Forms.Label remnantLabel; private System.Windows.Forms.Label remnantValue; + private System.Windows.Forms.Label elapsedLabel; + private System.Windows.Forms.Label elapsedValue; private System.Windows.Forms.Button stopButton; private System.Windows.Forms.FlowLayoutPanel buttonPanel; } diff --git a/OpenNest/Forms/NestProgressForm.cs b/OpenNest/Forms/NestProgressForm.cs index 6e51a24..79813c8 100644 --- a/OpenNest/Forms/NestProgressForm.cs +++ b/OpenNest/Forms/NestProgressForm.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics; using System.Threading; using System.Windows.Forms; @@ -7,6 +8,8 @@ namespace OpenNest.Forms public partial class NestProgressForm : Form { private readonly CancellationTokenSource cts; + private readonly Stopwatch stopwatch = Stopwatch.StartNew(); + private readonly System.Windows.Forms.Timer elapsedTimer; public NestProgressForm(CancellationTokenSource cts, bool showPlateRow = true) { @@ -18,6 +21,10 @@ namespace OpenNest.Forms plateLabel.Visible = false; plateValue.Visible = false; } + + elapsedTimer = new System.Windows.Forms.Timer { Interval = 1000 }; + elapsedTimer.Tick += (s, e) => UpdateElapsed(); + elapsedTimer.Start(); } public void UpdateProgress(NestProgress progress) @@ -37,6 +44,10 @@ namespace OpenNest.Forms if (IsDisposed || !IsHandleCreated) return; + stopwatch.Stop(); + elapsedTimer.Stop(); + UpdateElapsed(); + phaseValue.Text = "Done"; stopButton.Text = "Close"; stopButton.Enabled = true; @@ -44,6 +55,17 @@ namespace OpenNest.Forms stopButton.Click += (s, e) => Close(); } + private void UpdateElapsed() + { + if (IsDisposed || !IsHandleCreated) + return; + + var elapsed = stopwatch.Elapsed; + elapsedValue.Text = elapsed.TotalHours >= 1 + ? elapsed.ToString(@"h\:mm\:ss") + : elapsed.ToString(@"m\:ss"); + } + private void StopButton_Click(object sender, EventArgs e) { cts.Cancel(); @@ -53,6 +75,10 @@ namespace OpenNest.Forms protected override void OnFormClosing(FormClosingEventArgs e) { + elapsedTimer.Stop(); + elapsedTimer.Dispose(); + stopwatch.Stop(); + if (!cts.IsCancellationRequested) cts.Cancel();