diff --git a/OpenNest.Engine/CuttingPlanning/CuttingProposalInspection.cs b/OpenNest.Engine/CuttingPlanning/CuttingProposalInspection.cs
index 551e8c2..0742349 100644
--- a/OpenNest.Engine/CuttingPlanning/CuttingProposalInspection.cs
+++ b/OpenNest.Engine/CuttingPlanning/CuttingProposalInspection.cs
@@ -61,14 +61,21 @@ public sealed class CuttingProposalInspection
}
/// Recheck exact freshness before displaying, including every subsequent paint.
- public Plate RefreshPreview()
+ private int previewIndex = -1;
+
+ public Plate RefreshPreview(int plateIndex = 0)
{
- if (Proposal == null || CloseRequested)
+ if (plateIndex != previewIndex)
+ {
+ Preview = null;
+ previewIndex = plateIndex;
+ }
+ if (Proposal == null || CloseRequested || plateIndex < 0 || plateIndex >= Proposal.Plates.Count)
return Preview = null;
// BuildPreview is the authority; never clone a rejected program for presentation.
- if (Proposal.Plates[0].Result.Snapshot?.PlateState?.IsCurrent() != true)
+ if (Proposal.Plates[plateIndex].Result.Snapshot?.PlateState?.IsCurrent() != true)
return Preview = null;
- return Preview ??= Proposal.BuildPreview(0);
+ return Preview ??= Proposal.BuildPreview(plateIndex);
}
/// Zero-based proposed position for a one-based source diagnostic number.
diff --git a/OpenNest.Tests/CuttingPlanning/CuttingProposalInspectionTests.cs b/OpenNest.Tests/CuttingPlanning/CuttingProposalInspectionTests.cs
index ba881bb..c1decae 100644
--- a/OpenNest.Tests/CuttingPlanning/CuttingProposalInspectionTests.cs
+++ b/OpenNest.Tests/CuttingPlanning/CuttingProposalInspectionTests.cs
@@ -139,6 +139,26 @@ public class CuttingProposalInspectionTests
Assert.True(inspection.RequestClose());
}
+ [Fact]
+ public async Task BatchPreviewNavigatesToSecondPlateAndRejectsItsStaleState()
+ {
+ var first = Plate();
+ var second = Plate();
+ second.Parts[0].Offset(20, 0);
+ var inspection = new CuttingProposalInspection();
+ var work = inspection.BeginPlanning(CuttingPlanBatch.Capture([first, second], ExplicitContourTests.Parameters(), false));
+ await work;
+ inspection.CompletePlanning(work);
+ var preview = inspection.RefreshPreview(1);
+ Assert.NotNull(preview);
+ Assert.Equal(second.Parts[1].Location, preview.Parts[0].Location);
+ Assert.NotSame(preview, inspection.RefreshPreview(0));
+ second.Quantity++;
+ Assert.Null(inspection.RefreshPreview(1));
+ Assert.NotNull(inspection.RefreshPreview(0));
+ inspection.RequestClose();
+ }
+
private static CuttingPlanBatch Capture(Plate plate) =>
CuttingPlanBatch.Capture([plate], ExplicitContourTests.Parameters(), false);
diff --git a/OpenNest.WinForms.Tests/Forms/CuttingInspectionPanelTests.cs b/OpenNest.WinForms.Tests/Forms/CuttingInspectionPanelTests.cs
index 698fbb1..ef10401 100644
--- a/OpenNest.WinForms.Tests/Forms/CuttingInspectionPanelTests.cs
+++ b/OpenNest.WinForms.Tests/Forms/CuttingInspectionPanelTests.cs
@@ -153,6 +153,174 @@ public class CuttingInspectionPanelTests
}
});
+ [Fact]
+ public void AllPlatesNavigatesReadyAndBlockedWithoutPartialApply() => RunSta(() =>
+ {
+ using var editor = Editor();
+ var second = editor.Nest.Plates[^1]; // Fill the managed empty sentinel; a new one is appended.
+ second.Size = new OpenNest.Geometry.Size(100, 100);
+ var locked = Square("locked", 1);
+ var crossing = (OpenNest.CNC.Program)locked.Program.Clone();
+ crossing.MoveTo(5, 5);
+ Assert.True(locked.RestoreLeadInProgram(crossing, true));
+ second.Parts.Add(locked);
+ editor.Show();
+ editor.PlateManager.LoadAt(editor.Nest.Plates.Count - 1); // Different empty sentinel.
+ var original = editor.PlateView.Plate;
+ OpenAll(editor);
+ var panel = editor.CuttingInspection!;
+ PumpUntil(() => panel.Proposal != null && !panel.IsPlanning);
+ Assert.Equal(2, panel.Proposal.Plates.Count);
+ Assert.Equal(ScrollBars.Vertical, Field(panel, "resultLabel").ScrollBars);
+ Assert.False(Field