a text input form¶
build a small form with a name field, a live echo label, and a numeric field
by the end of this guide you will have a working form panel with three fields: an uncontrolled name entry that keeps its own text, a controlled entry whose value is mirrored live into a label below it, and a numeric field clamped to a 0-100 range. each step introduces one idea so you can see exactly what each property contributes before the next one arrives.
start with an uncontrolled entry¶
an uncontrolled entry seeds itself once from InitialText and then owns its own text. you do not track the value in a field - the entry holds it. add a Placeholder for the hint when it is empty.
using Goo;
using Sandbox.UI;
public class InputFormUI : GooPanel<Container>
{
protected override Container Build() => new Container
{
Padding = 16,
Width = 360,
Gap = 8,
Children =
{
new Text( "name" ),
new Goo.TextEntry { InitialText = "type me", Placeholder = "search" },
},
};
}
press play and type in the field. the entry keeps the text on its own. nothing else in Build knows or cares about it yet.
add a controlled entry with a live label¶
a controlled entry binds Value to a field you own. OnChange fires on every keystroke and hands you the current string - update your field there and call Rebuild() so the echo label below reflects each character as you type. OnSubmit fires when the user presses enter.
add _value and _submitted fields, then extend Build:
public class InputFormUI : GooPanel<Container>
{
string _value = "";
string _submitted = "(nothing yet)";
protected override Container Build() => new Container
{
Padding = 16,
Width = 360,
Gap = 8,
Children =
{
new Text( "name" ),
new Goo.TextEntry { InitialText = "type me", Placeholder = "search" },
new Text( "message" ),
new Goo.TextEntry
{
Value = _value,
Placeholder = "controlled",
OnChange = v => { _value = v; Rebuild(); },
OnSubmit = v => { _submitted = v; Rebuild(); },
},
new Text( $"echo: \"{_value}\" submitted: \"{_submitted}\"" ),
},
};
}
type in the message field and watch the echo line update. press enter and watch the submitted line update. the label is a pure read of _value, so it is always correct.
add a numeric field¶
set Numeric to restrict the field to numbers. MinValue and MaxValue clamp the accepted range on submit. NumberFormat controls how the value displays (standard C# format strings).
new Text( "quantity (0-100)" ),
new Goo.TextEntry
{
InitialText = "42.00",
Numeric = true,
MinValue = 0,
MaxValue = 100,
NumberFormat = "0.00",
MaxLength = 8,
},
the finished class with all three fields:
using Goo;
using Sandbox.UI;
public class InputFormUI : GooPanel<Container>
{
string _value = "";
string _submitted = "(nothing yet)";
protected override Container Build() => new Container
{
Padding = 16,
Width = 360,
Gap = 8,
Children =
{
new Text( "name" ),
new Goo.TextEntry { InitialText = "type me", Placeholder = "search" },
new Text( "message" ),
new Goo.TextEntry
{
Value = _value,
Placeholder = "controlled",
OnChange = v => { _value = v; Rebuild(); },
OnSubmit = v => { _submitted = v; Rebuild(); },
},
new Text( $"echo: \"{_value}\" submitted: \"{_submitted}\"" ),
new Text( "quantity (0-100)" ),
new Goo.TextEntry
{
InitialText = "42.00",
Numeric = true,
MinValue = 0,
MaxValue = 100,
NumberFormat = "0.00",
MaxLength = 8,
},
},
};
}
what just happened¶
the three entries show three distinct modes:
- the name field is uncontrolled:
InitialTextseeds it once and the entry tracks its own text. use this when you only need the value on submit, not while the user types. - the message field is controlled:
Valueis always_value,OnChangeupdates_valueand callsRebuild(), and the echo label reads_valuedirectly insideBuild. the field never gets ahead of your state. - the numeric field layers
Numeric,MinValue,MaxValue,NumberFormat, andMaxLengthon top ofInitialText. the entry enforces the constraints - you do not write a validation callback.
the controlled pattern (field + OnChange + Rebuild()) is the same state loop from your first counter: mutate a field, call Rebuild(), let Build() read the new value.
see also¶
- textentry guidance - the full surface: disabled, multiline, focus and blur callbacks, and styling
- textentry reference - every property and event in one table
- your first counter - the field-plus-Rebuild() state loop this controlled entry builds on
- build method - how
Rebuild()schedules a freshBuild()and how goo diffs the result