From c325a1745ab804cdce6027884fb222e535d76364 Mon Sep 17 00:00:00 2001 From: AJ Isaacs Date: Thu, 22 Oct 2026 14:50:07 -0400 Subject: [PATCH] fix(desktop): list the first sets in Add a set before anything is typed The release dialog's Add a set box was a pure type-ahead: it queried the server only after typing paused, so opening it, or clicking its arrow with nothing typed, showed an empty list. From Drawing Sets > Release that was easy to miss, but New Release starts with nothing listed, and an empty dropdown there reads as a missing server connection. The dialog now reads the first active sets by name when it is shown, and again when the list is opened while still empty, without dropping the list open on its own; typing narrows it as before. Co-Authored-By: Claude Fable 5.1 --- .../Forms/SetReleaseFormsTests.cs | 27 +++++++++++++++++++ OpenNest/Forms/ReleaseSetForm.Designer.cs | 1 + OpenNest/Forms/ReleaseSetForm.cs | 19 ++++++++++--- docs/nest-storage.md | 6 ++--- 4 files changed, 47 insertions(+), 6 deletions(-) diff --git a/OpenNest.WinForms.Tests/Forms/SetReleaseFormsTests.cs b/OpenNest.WinForms.Tests/Forms/SetReleaseFormsTests.cs index 96d3045e..e487b9b3 100644 --- a/OpenNest.WinForms.Tests/Forms/SetReleaseFormsTests.cs +++ b/OpenNest.WinForms.Tests/Forms/SetReleaseFormsTests.cs @@ -639,6 +639,33 @@ public class SetReleaseFormsTests "The all-releases view test did not complete."); } + [Fact] + public void Release_ListsTheFirstSetsInTheAddSetBox_BeforeAnythingIsTyped() + { + StaTestThread.Run( + () => + { + var library = new PackageLibrary(); + var a = library.Add("SYN-A", "Synthetic Steel"); + var sets = new InMemoryDrawingSets(); + CreateSet(sets, "Synthetic kit", (a, 1)); + CreateSet(sets, "Synthetic frame", (a, 2)); + var nests = new MemoryNests(); + + using var form = new ReleaseSetForm(sets, library, nests, Server, Array.Empty(), + Array.Empty(), (_, _) => { }); + form.Show(); + var combo = Field(form, "addSetCombo"); + PumpUntil(() => combo.Items.Count == 2, "the set list"); + + Assert.Equal(new[] { "Synthetic frame", "Synthetic kit" }, combo.Items.Cast().Select(i => i.ToString()!.Split(" (")[0])); + Assert.Equal("", combo.Text); + Assert.False(combo.DroppedDown); + }, + TestTimeout, + "The set list test did not complete."); + } + [Fact] public void Release_StartedEmpty_AddsASetAndReleasesIt() { diff --git a/OpenNest/Forms/ReleaseSetForm.Designer.cs b/OpenNest/Forms/ReleaseSetForm.Designer.cs index b001cd9e..f5716fc9 100644 --- a/OpenNest/Forms/ReleaseSetForm.Designer.cs +++ b/OpenNest/Forms/ReleaseSetForm.Designer.cs @@ -141,6 +141,7 @@ namespace OpenNest.Forms addSetCombo.Name = "addSetCombo"; addSetCombo.TabIndex = 1; addSetCombo.TextUpdate += AddSetCombo_TextUpdate; + addSetCombo.DropDown += AddSetCombo_DropDown; addSetCombo.KeyDown += AddSetCombo_KeyDown; // // addSetButton diff --git a/OpenNest/Forms/ReleaseSetForm.cs b/OpenNest/Forms/ReleaseSetForm.cs index 1c16b1a6..26624472 100644 --- a/OpenNest/Forms/ReleaseSetForm.cs +++ b/OpenNest/Forms/ReleaseSetForm.cs @@ -121,6 +121,8 @@ namespace OpenNest.Forms AddRow(line.Drawing, line.Drawing.Name, line.Drawing.Customer, line.Drawing.Revision, line.Quantity); populating = false; BuildPlan(); + // The first sets by name are listed before anything is typed, so the arrow shows them. + Shown += async (_, _) => await SearchSetsAsync("", dropDown: false); } /// The sets listed, in order, with their kits. @@ -282,6 +284,13 @@ namespace OpenNest.Forms BuildPlan(); } + // Opening the empty list reads the first sets when the initial read failed or found none yet. + private async void AddSetCombo_DropDown(object sender, EventArgs e) + { + if (addSetCombo.Items.Count == 0 && string.IsNullOrEmpty(addSetCombo.Text) && addSetCombo.Enabled) + await SearchSetsAsync("", dropDown: false); + } + // Type-ahead: the set list is queried once typing pauses. private void AddSetCombo_TextUpdate(object sender, EventArgs e) { @@ -305,8 +314,12 @@ namespace OpenNest.Forms private async void AddSetButton_Click(object sender, EventArgs e) => await AddTypedSetAsync(); - /// Lists active sets whose name, customer or notes contain . - internal async Task SearchSetsAsync(string text) + /// + /// Lists the first active sets by name whose name, customer or notes contain + /// (all of them for an empty text); + /// opens the list when the box has focus, as typing does. + /// + internal async Task SearchSetsAsync(string text, bool dropDown = true) { searchCancellation?.Cancel(); searchCancellation?.Dispose(); @@ -340,7 +353,7 @@ namespace OpenNest.Forms addSetCombo.EndUpdate(); addSetCombo.Text = typed; addSetCombo.SelectionStart = System.Math.Min(caret, typed.Length); - if (addSetCombo.Focused && page.Items.Count > 0) + if (dropDown && addSetCombo.Focused && page.Items.Count > 0) addSetCombo.DroppedDown = true; } diff --git a/docs/nest-storage.md b/docs/nest-storage.md index 1147bc89..ef5407c2 100644 --- a/docs/nest-storage.md +++ b/docs/nest-storage.md @@ -408,9 +408,9 @@ review and Save; nothing is stored before then. **Release...** (in the drawing sets window) reads the selected active set with its members and opens the release dialog with it listed. -More sets are added by typing in **Add a set** (the list of matching active sets -fills as you type; Enter or **Add** takes the picked set, or the one set whose -name is exactly what was typed). **Add Drawings...** adds library drawings on +More sets are added from **Add a set**: the list opens with the first active sets +by name and narrows as you type; Enter or **Add** takes the picked set, or the one +set whose name is exactly what was typed. **Add Drawings...** adds library drawings on their own, outside any set: it browses every active drawing as the set editor's library pane does (search, server-side sort, pages, several selected at once), and **Add to Release**, a double-click or Enter adds the selection at the