@@ -39,14 +39,13 @@ import (
3939type OpenList struct {
4040 widget.BaseWidget
4141
42- options []string
42+ // listContents is what the list holds and what it has drawn - see
43+ // listcontents.go for why it is a type of its own.
44+ listContents
45+
4346 // room is how much height the window has left for this list, or nought for
4447 // no limit. See LimitTo and MinSize.
4548 room float32
46- // chosen is the value in the box, marked with a tick. Empty when the box
47- // shows a default nobody has confirmed - see the note in preset.go about a
48- // filled field making "I did not say" impossible to express.
49- chosen string
5049 // active is the row the keyboard is on, or -1 when it has not been used,
5150 // and shown is whether that is drawn.
5251 //
@@ -63,41 +62,22 @@ type OpenList struct {
6362 close func (byKeyboard bool )
6463
6564 list * widget.List
66- // rows is the row showing each position, recorded as the list fills them.
67- //
68- // A registry rather than a walk, because a walk cannot get in: widget.List
69- // keeps the rows it built inside its renderer, so a tree walk stops at the
70- // list and reports an open list with nothing in it. Measured on 2026-08-18
71- // while trying to photograph a row under the pointer.
72- //
73- // Every entry is current whatever the list has scrolled past, because a
74- // recycled row is refilled before it is shown and fill is what writes here.
75- rows map [widget.ListItemID ]* ListRow
7665
7766 // KindOf says what picture goes in front of one value, or nil for a list
7867 // whose values are not things of different kinds. Set from outside, because
7968 // only the screen putting values in knows what they are.
8069 KindOf func (string ) fyne.Resource
8170
82- // headingOf is the heading a value stands under, or nil for a list with no
83- // headings. See GroupUnder.
84- headingOf func (string ) string
8571 // filter is the box at the top that narrows the list, or nil for a list
86- // without one, and typed is what is in it . See WithFilter.
72+ // without one. What is typed into it is listContents.typed . See WithFilter.
8773 filter * FilterBox
88- typed string
89- // entries is what the list draws now - headings, values and the notice
90- // that nothing matched - worked out by arrange. Every row number in this
91- // file is a position in entries, never in options, because the two part
92- // the moment a list has a heading.
93- entries []listEntry
9474}
9575
9676// NewOpenList builds the list. take is called with the value somebody settled
9777// on, and close when they left without settling on one.
9878func NewOpenList (options []string , chosen string , take func (string , bool ), close func (bool )) * OpenList {
99- l := & OpenList {options : options , chosen : chosen , active : - 1 , take : take , close : close ,
100- rows : map [widget.ListItemID ]* ListRow {}}
79+ l := & OpenList {active : - 1 , take : take , close : close ,
80+ listContents : listContents { options : options , chosen : chosen , rows : map [widget.ListItemID ]* ListRow {} }}
10181 l .entries = arrange (options , nil , "" )
10282 l .list = widget .NewList (
10383 func () int { return len (l .entries ) },
@@ -138,15 +118,6 @@ func (l *OpenList) Keyboard() fyne.Focusable {
138118// Filter is the box at the top of the list, or nil, for a guard to type into.
139119func (l * OpenList ) Filter () * FilterBox { return l .filter }
140120
141- // rearrange works out the rows again after the filter or the grouping
142- // changed, and forgets the rows it recorded: a row built for the old
143- // arrangement can still hold a label the list no longer draws, and
144- // RowShowing would report it.
145- func (l * OpenList ) rearrange () {
146- l .entries = arrange (l .options , l .headingOf , l .typed )
147- l .rows = map [widget.ListItemID ]* ListRow {}
148- }
149-
150121// narrowTo is the filter box reporting what it now holds. The keyboard lands
151122// where landing says and the bar is drawn, because typing is using the
152123// keyboard - except when the box has been emptied, where the list goes back
@@ -204,57 +175,6 @@ func (l *OpenList) fill(id widget.ListItemID, row fyne.CanvasObject) {
204175 r .Refresh ()
205176}
206177
207- // Rows is what this list is showing, for a guard to read, in the order it is
208- // drawn - headings and the notice included, so that a position from Active
209- // is a position here.
210- //
211- // The toolkit's menu turned items into widgets of an unexported type, so what
212- // was marked could not be read back off the canvas - only that something was
213- // open. This is the half of that pair we own, and it says what is in the list
214- // and which row carries the tick.
215- func (l * OpenList ) Rows () []Choice {
216- out := make ([]Choice , 0 , len (l .entries ))
217- for _ , e := range l .entries {
218- value := e .kind == entryValue
219- out = append (out , Choice {Label : e .text , Marked : value && l .isChosen (e .text ), Choosable : value })
220- }
221- return out
222- }
223-
224- // isChosen says whether a value is the one in the box.
225- //
226- // One function rather than the same comparison written where a row is filled
227- // and again where the list reports itself. Two copies is what it was for an
228- // hour on 2026-08-18, and the mutation runner said so at once: blanking the
229- // drawn mark left the guard green, because the guard was reading the other
230- // copy. A rule with two homes is a rule no test can pin down.
231- func (l * OpenList ) isChosen (value string ) bool { return value == l .chosen }
232-
233- // DrawnRows is every row the list has actually built, for a guard that has to
234- // ask what is on the screen rather than what the list holds.
235- //
236- // Rows above answers from the options, which is the right half for "what is in
237- // this list" and the wrong half for "what does a row draw". A picture that
238- // never reaches a row would pass the first and fail this one.
239- func (l * OpenList ) DrawnRows () []* ListRow {
240- out := make ([]* ListRow , 0 , len (l .rows ))
241- for _ , row := range l .rows {
242- out = append (out , row )
243- }
244- return out
245- }
246-
247- // RowShowing is the row currently drawing one value, or nil if that value is
248- // scrolled out of sight. For a guard that needs to press or hover a real row.
249- func (l * OpenList ) RowShowing (label string ) * ListRow {
250- for _ , row := range l .rows {
251- if row .Label () == label {
252- return row
253- }
254- }
255- return nil
256- }
257-
258178// Choice is one row of an open list, for a guard to read. Choosable is false
259179// on a heading and on the notice that nothing matched.
260180type Choice struct {
0 commit comments