Skip to content

Commit 552e30b

Browse files
donislawdevclaude
andauthored
gui: a menu, a switch or a press that lays the batch screen out reads the form once (#133)
* gui: a menu, a switch or a press that lays the batch screen out reads the form once rebuild said what the form comes to, and a menu or a switch on the batch screen is followed by the live check, which said it again - two readings, and with a base preset two expansions. Adding or duplicating a batch read it twice as well: choosing the new batch's format laid the screen out once, the press a second time. rebuild now only lays the screen out. A menu or a switch is said by recheck, and a press no box reports goes through afterAPress, which lays out and says the line once. TestAChangeThatLaysTheBatchScreenOutAgainReadsTheFormOnce asks exactly one reading of four menus and switches and six presses - red on main in seven of the ten. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * changelog: the batch screen works the form out once for a menu or a switch Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * gui: the base switched off with no batch left brings one back without saying the line twice Raised in review. The switch brought the batch back through addBatch, which lays out and says the line, and the switch's own check then said it again - two readings, measured by the guard before the change. newBatchAtTheEnd puts the batch in without saying anything; addBatch is that and afterAPress. The guard now removes every batch with the base on, switches the base off, and asks for one reading and a batch back. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 18f72fa commit 552e30b

4 files changed

Lines changed: 116 additions & 10 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,10 @@ because it turns other people's test suites red.
509509
long as before. On the `Several batches` screen with a preset switched on,
510510
a key typed into a box now works the preset out once rather than twice.
511511

512+
- **Choosing a format, switching the base or choosing a base preset on
513+
`Several batches` works the form out once instead of twice.** Switching
514+
the base to `upload-validation` took half as long as before.
515+
512516
- **A preview or a run refused while it was being planned no longer leaves
513517
"Working out what this would cost..." standing over the refusal.**
514518

‎internal/guard/settleonce_test.go‎

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"github.com/donislawdev/TestingFilesGenerator/internal/engine"
77
"github.com/donislawdev/TestingFilesGenerator/internal/format"
88
"github.com/donislawdev/TestingFilesGenerator/internal/gui/parts"
9+
"github.com/donislawdev/TestingFilesGenerator/internal/gui/text"
910
"github.com/donislawdev/TestingFilesGenerator/internal/gui/window"
1011
"github.com/donislawdev/TestingFilesGenerator/internal/recipe"
1112
)
@@ -67,6 +68,86 @@ func TestOneChangeOfABoxReadsTheFormOnce(t *testing.T) {
6768
}
6869
}
6970

71+
// A menu, a switch or a press that lays the batch screen out again reads the
72+
// form once.
73+
//
74+
// The batch screen lays itself out again when a batch's format changes, when
75+
// the base is switched or chosen, and when a press adds, copies or takes away
76+
// a batch or what an archive holds. The layout said what the form comes to,
77+
// and until 2026-09-24 a menu or a switch was then followed by the live check,
78+
// which said it again: two readings, and with a base preset two expansions -
79+
// switching the base to upload-validation took 278-321 ms in the real window
80+
// (docs/GUI-MEMORY-2026-09-23.md section 4j).
81+
//
82+
// Exactly one, for the reason the guard above gives, and it matters here in
83+
// the other direction too: a press is followed by no check, so nought is a
84+
// press after which the line under the buttons still describes the old form.
85+
func TestAChangeThatLaysTheBatchScreenOutAgainReadsTheFormOnce(t *testing.T) {
86+
host := newFakeHost(t)
87+
rec := window.NewRecipe(host)
88+
body := rec.Object()
89+
once := func(what string, act func()) {
90+
t.Helper()
91+
host.settles = 0
92+
act()
93+
switch {
94+
case host.settles == 0:
95+
t.Fatalf("%s told the host of no reading of the form - either nothing changed or the line under the buttons was not said, and this guard cannot tell which",
96+
what)
97+
case host.settles > 1:
98+
t.Errorf("%s read the form %d times, expected once", what, host.settles)
99+
}
100+
}
101+
press := func(name string) func() {
102+
return func() {
103+
t.Helper()
104+
b := buttonNamed(body, name)
105+
if b == nil {
106+
t.Fatalf("the batch screen has no %q button, so this guard cannot press it", name)
107+
}
108+
b.OnTapped()
109+
}
110+
}
111+
base := func() *parts.Chooser {
112+
for _, c := range reportingControls(findField(rec.Fields(), recipe.KeyExtends).Control) {
113+
if pick, is := c.(*parts.Chooser); is {
114+
return pick
115+
}
116+
}
117+
t.Fatal("the base is switched on and there is no menu of presets under it")
118+
return nil
119+
}
120+
121+
once("choosing png for the first batch", func() {
122+
chooserIn(t, rec.Fields(), recipe.TargetAddress(1, recipe.KeyFormat)).SetSelected("png")
123+
})
124+
once("switching the base on", func() { toggleIn(t, rec.Fields(), "start_from_preset").SetChecked(true) })
125+
once("choosing text-encoding as the base", func() { base().SetSelected("text-encoding") })
126+
once("switching the base off", func() { toggleIn(t, rec.Fields(), "start_from_preset").SetChecked(false) })
127+
128+
once("adding a batch", press(text.ButtonAddBatch()))
129+
once("duplicating a batch", press(text.ButtonDuplicateBatch()))
130+
once("removing a batch", press(text.ButtonRemoveBatch()))
131+
once("choosing zip for the first batch", func() {
132+
chooserIn(t, rec.Fields(), recipe.TargetAddress(1, recipe.KeyFormat)).SetSelected("zip")
133+
})
134+
once("adding what an archive holds", press(text.ButtonAddContents()))
135+
once("removing what an archive holds", press(text.ButtonRemoveContents()))
136+
137+
// The base switched off with no batch left, which brings a batch back and
138+
// is the one way a switch and a press meet. Raised by the review of #133
139+
// (docs/REVIEW-133-2026-09-24.md): the batch came back through the press's
140+
// path, which says the line, and the switch's check then said it again.
141+
once("switching the base on again", func() { toggleIn(t, rec.Fields(), "start_from_preset").SetChecked(true) })
142+
for findField(rec.Fields(), recipe.TargetAddress(1, recipe.KeyID)) != nil {
143+
once("removing a batch with the base on", press(text.ButtonRemoveBatch()))
144+
}
145+
once("switching the base off with no batch left", func() { toggleIn(t, rec.Fields(), "start_from_preset").SetChecked(false) })
146+
if findField(rec.Fields(), recipe.TargetAddress(1, recipe.KeyID)) == nil {
147+
t.Error("the base was switched off with no batch left and no batch came back, so the screen can produce nothing")
148+
}
149+
}
150+
70151
// Typing into a box a preset is not given does not expand the preset again.
71152
//
72153
// The seed and the output directory are read with the preset's values but are

‎internal/gui/window/recipe.go‎

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -354,8 +354,20 @@ func (r *Recipe) rebuild() {
354354
r.baseBox.Refresh()
355355
r.batchBox.Refresh()
356356
r.outBox.Refresh()
357-
// A batch added, copied or taken away changes what the form comes to,
358-
// and none of those goes through a box somebody typed in.
357+
// Nothing is said here about what the form comes to. Whoever changed the
358+
// form says it: a menu or a switch is followed by recheck, which reads the
359+
// form once for the line and the box, and a press no box reports goes
360+
// through afterAPress. This said it too until 2026-09-24, so a new format,
361+
// base or preset read the form twice - and with a base preset, each
362+
// reading expanded it (docs/GUI-MEMORY-2026-09-23.md section 4j).
363+
}
364+
365+
// afterAPress lays the screen out again and says what the form now comes to,
366+
// for a change no box, menu or switch reports: a batch added, copied or taken
367+
// away, contents added or taken away. A function rather than a method, because
368+
// the screen stands near its ceiling of methods.
369+
func afterAPress(r *Recipe) {
370+
r.rebuild()
359371
r.runner.refreshLine()
360372
}
361373

@@ -496,7 +508,7 @@ func (r *Recipe) contentsBlock(index int, b *batch) fyne.CanvasObject {
496508

497509
addContents := parts.NewButton(parts.Secondary, text.ButtonAddContents(), func() {
498510
b.contents = append(b.contents, r.newContent())
499-
r.rebuild()
511+
afterAPress(r)
500512
})
501513
if len(b.contents) == 0 {
502514
if !holds {
@@ -583,12 +595,21 @@ func (r *Recipe) outputSection() fyne.CanvasObject {
583595

584596
// addBatch puts another batch at the end of the list.
585597
func (r *Recipe) addBatch() {
598+
newBatchAtTheEnd(r)
599+
afterAPress(r)
600+
}
601+
602+
// newBatchAtTheEnd puts a batch after the last one, without saying what the
603+
// form comes to. Apart from addBatch for the one caller that is not a press:
604+
// the base switched off with no batch left brings one back, and the switch's
605+
// own check says the line. Through addBatch it was said twice - found by the
606+
// review of #133 (docs/REVIEW-133-2026-09-24.md).
607+
func newBatchAtTheEnd(r *Recipe) {
586608
r.batches = append(r.batches, r.newBatch())
587609
// A new batch has no format until one is chosen, and its declared settings
588610
// come with that choice. Chosen here rather than left empty so that a batch
589611
// arrives looking like the one above it.
590612
r.batches[len(r.batches)-1].formatPick.SetSelected(format.IDs()[0])
591-
r.rebuild()
592613
}
593614

594615
// removeBatch drops one batch. The last cannot go, unless the screen builds
@@ -629,7 +650,7 @@ func (r *Recipe) duplicateBatch(index int) {
629650

630651
rest := append([]*batch{to}, r.batches[index+1:]...)
631652
r.batches = append(r.batches[:index+1], rest...)
632-
r.rebuild()
653+
afterAPress(r)
633654
}
634655

635656
func (r *Recipe) removeBatch(index int) {
@@ -640,15 +661,15 @@ func (r *Recipe) removeBatch(index int) {
640661
return
641662
}
642663
r.batches = append(r.batches[:index], r.batches[index+1:]...)
643-
r.rebuild()
664+
afterAPress(r)
644665
}
645666

646667
func (r *Recipe) removeContent(b *batch, index int) {
647668
if index < 0 || index >= len(b.contents) {
648669
return
649670
}
650671
b.contents = append(b.contents[:index], b.contents[index+1:]...)
651-
r.rebuild()
672+
afterAPress(r)
652673
}
653674

654675
// onFormatChosen replaces the declared settings of one batch.

‎internal/gui/window/recipebase.go‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,10 @@ func newBase(r *Recipe) *base {
4949
b := &base{}
5050
b.on = parts.NewToggle(func(on bool) {
5151
// Off with no batch left is a form that can produce nothing, so a
52-
// batch comes back - the one the screen opened with.
52+
// batch comes back - the one the screen opened with. What the form
53+
// then comes to is said by the switch's own check, as for any switch.
5354
if !on && len(r.batches) == 0 {
54-
r.addBatch()
55-
return
55+
newBatchAtTheEnd(r)
5656
}
5757
r.rebuild()
5858
})

0 commit comments

Comments
 (0)