GoField — the row that can be wrong
One line at the top of a five-field form saying "check your input" does not say which field. That single thing is what makes people abandon a sign-up.
var name_field := GoField.make("Character name", GoStyle.line_edit("2-12"), "Cannot be changed later")
name_field.set_error("That name is taken") # the server said no
name_field.clear_error()
The hint hides while an error shows, so the row does not grow by a line and push everything below it, and the error text is joined into the control's accessible name — a red border alone says nothing to someone who cannot see red.
GoStyle.field() is the static version: label, control, hint, no error state. Use the factory
when nothing can go wrong and the node for anything a server can reject.
GoInputGroup — an input welded to its button
GoInputGroup.make(GoStyle.line_edit("Message"), {"suffix": GoStyle.icon_button(&"send", send)})
GoInputGroup.make(GoStyle.line_edit("Name"), {"prefix_icon": &"search"})
What it solves is corners. Two rounded shapes meeting in the middle look pinched, and the default gap makes them read as two objects. Outer corners stay round, touching ones go square — in every button state, or the group splits the moment it is pressed. Chamfered skins have no corner fields at all, so there it leaves them alone and the square faces meet cleanly.
GoCombobox — a picker that searches
Under ten entries GoStyle.select() is better: one less tap and the whole list is visible.
Past thirty, scanning is work.
var picker := GoCombobox.make(server_names, 0, "Choose a server")
picker.picked.connect(func(i: int) -> void: connect_to(servers[i]))
GoCodeInput — coupon and gift codes
A code is pasted from a message far more often than it is typed. Twelve real fields would break pasting
at the first cell and lose characters to an IME, so one hidden field takes the text and the cells are
drawn. ABCD-EFGH-IJKL loses its dashes on the way in.
var coupon := GoCodeInput.make(12, 4)
coupon.completed.connect(func(code: String) -> void: server.redeem(code))
coupon.set_error("Already used")
GoTable — sortable headers, selectable rows
var board := GoTable.make(
[{"text": "Rank", "width": 56}, {"text": "Name"}, {"text": "Score", "numeric": true}], rows)
board.row_selected.connect(func(i: int) -> void: open_profile(rows[i]))
numeric: true, or the ranking inverts. Compared as text, "9124" beats
"91240" — and scores, gold and damage all have mixed digit counts.
row_selected gives the index in your original array, not the sorted position. The sort
direction is shown with a glyph, not only a colour, and whole rows are pressable so nobody has to hit one
cell. GoStyle.table() remains the read-only grid.
GoPagination — pages, or a More row
var pager := GoPagination.make(1, 12, func(page: int) -> void: load_mail(page))
var more := GoPagination.more(load_next) # the mobile-friendly variant
Numbers are a mouse UI; on a phone the More row reads better. The current page stays centred in the window, so pressing next does not reshuffle every number. A total of 0 means "unknown" and only the arrows are drawn — it does not invent a page count it was not given.