pub struct Field<'a> {Show 17 fields
pub kind: FieldKind,
pub name: &'a str,
pub label: &'a str,
pub hint: Option<&'a str>,
pub error: Option<&'a str>,
pub placeholder: Option<&'a str>,
pub options: &'a [Choice<'a>],
pub accept: &'a [Accepted<'a>],
pub multiple: bool,
pub required: bool,
pub max_length: Option<u32>,
pub min: Option<&'a str>,
pub max: Option<&'a str>,
pub step: Option<&'a str>,
pub curve: Curve<'a>,
pub unit: Option<&'a str>,
pub extended: bool,
}Fields§
§kind: FieldKindWhat kind of value it takes.
name: &'a strThe name the value is submitted under.
label: &'a strWhat the user is asked for.
hint: Option<&'a str>Standing help, shown whether or not anything is wrong.
error: Option<&'a str>What is currently wrong with the value.
placeholder: Option<&'a str>Ghost text shown while the field is empty.
User-facing text, and it sits with label and hint rather than with
the value because it is a property of the question and not of the
answer. It lived renderer-side in makeover-webview until 0.8.0 for one
reason and it was not a reading on where it belonged: adding a field to
a published struct is a breaking change.
Not a substitute for a label. A field labelled only by its placeholder loses its label the moment anything is typed, and no renderer here can make that not happen, so the description keeps both.
options: &'a [Choice<'a>]The options offered, in the order they are offered.
Empty for every kind FieldKind::offers_options rejects. A field
described with no options is sayable on purpose: it is what an app with
an unfinished-loading option list actually has, and a renderer showing
an empty control says so on screen rather than in a log.
Which option is current is not here. That is the value, and the value is renderer state.
accept: &'a [Accepted<'a>]What a file field takes, in the order a host offering the list shows it.
Empty for every kind FieldKind::takes_files rejects, and empty is
also a real answer for one that accepts it: a field that takes any file
says so by listing nothing, which is what an <input type="file"> with
no accept does and what most of the measured sites are.
It is a filter and it is the disclosure cue, and Accepted’s doc
carries which reading is which. Nothing here validates: a host may hand
back a file the list does not cover, exactly as a browser does when the
user switches the picker to “All Files”, and deciding a value is wrong
stays with whoever validated.
Added 0.31.0, f7261a5a.
multiple: boolWhether more than one file may be picked at once.
Only FieldKind::takes_files reads it. A multi-valued answer to any
other question is a different shape — a set of options, a repeated
group — and neither is this flag with a different kind beside it.
False is the common case: 4 of the MNW server’s 16 file inputs carry it.
Added 0.31.0, f7261a5a.
required: boolWhether the form refuses to submit without it.
max_length: Option<u32>The longest the value may be, in characters.
Added 0.11.0 with min and max, joining
required, which had been the only constraint here
since before the crate wrote down that it carried none.
min: Option<&'a str>The lowest value accepted, as the host would write it.
Text rather than a number, because the bound is only a number for some
of the kinds that take one. goingson’s own sites are min="1" on a
duration and min="2026-08-09T14:30" on a datetime, and a numeric member
could say the first and not the second. The kind already
says how to read it, the same way it does for the value.
max: Option<&'a str>The highest value accepted, as the host would write it. See
min.
step: Option<&'a str>The granularity the value moves in, as the host would write it.
Text for min’s reason, and it earns it twice over: the
step of a date is a day and the step of a threshold is 0.01, and a
numeric member could say one of them.
Absent means the host’s own granularity, which is the honest default
rather than a missing value: a webview’s <input> steps by 1 unless told
otherwise, and that is the browser’s rule and not this crate’s to
restate.
§It is the granularity of a typed value
FieldKind::Range reads its own from curve and
ignores this, as of 0.32.0. Until then this member served both, and
serving both is what the split fixes: on a slider the granularity and
the mapping are one decision, and on a typed number there is no mapping
to decide with. See Curve, “Why the step is here”.
Added 0.28.0 with FieldKind::Range, and narrowed away from it at
0.32.0.
curve: Curve<'a>How a slider’s position becomes its value, and how finely it moves.
FieldKind::Range’s, and nothing else reads it: a typed number has a
granularity but no mapping, and takes step instead.
Defaults to Curve::Linear with no step, which is what every range
described before 0.32.0 meant, so this member is additive and no
existing site changes meaning.
Added 0.32.0.
unit: Option<&'a str>What the number is measured in: s, ms, dB, GiB.
A fact about the value, not part of the question’s name, and that
distinction is the whole reason it is a member. The two readings come
apart the moment anything reads a field back rather than drawing it: a
max of -96 and a bound of -96 dBFS are the same number
and not the same answer, and under the convention this replaces the unit
could only be recovered by parsing it back out of a label.
§Where a renderer draws it
Beside the value, wherever that host puts a value. Not in the label: the label is the sentence above the control and that is the one place the convention could put it, which is why it read the same on every host and was wrong on the one host that had somewhere better. egui puts it inside the slider where the readout already is, a terminal appends it to the value in the edit line, a webview sets it adjacent to the input.
§Which kinds read it
FieldKind::measurable answers, and it is
takes_files’s footing: three renderers ask
before they can decide whether to draw this, and a matches! per
renderer is where the next measurable kind goes missing. A unit on a kind
that rejects it is sayable and ignored, the same way
options is on a kind that offers none.
§Why a string
The measured sites are GiB, dBFS, s and ms. An enum would have to
grow a member for every unit any consumer ever wants, and this crate does
not know them; it knows that a number has one.
Written as the symbol alone, with no brackets and no leading space. The spacing is the renderer’s, because a slider’s readout and a sentence want different answers.
Added 0.33.0, 32215e21, on eight sites across four files that had each
arrived at “Attack (s)” separately.
extended: boolWhether the field lives behind a “more options” disclosure.
Implementations§
Source§impl<'a> Field<'a>
impl<'a> Field<'a>
Sourcepub const fn new(kind: FieldKind, name: &'a str, label: &'a str) -> Self
pub const fn new(kind: FieldKind, name: &'a str, label: &'a str) -> Self
A plain required-nothing field of the given kind.
Sourcepub const fn range(
name: &'a str,
label: &'a str,
min: &'a str,
max: &'a str,
) -> Self
pub const fn range( name: &'a str, label: &'a str, min: &'a str, max: &'a str, ) -> Self
A bounded number the user drags across its whole extent.
The third under-described kind, and it gets a constructor for
select’s reason: a range is the one kind whose bounds
are not a rule but the control itself, so a call site that forgot them
has a slider with nothing to slide across. Taking them as arguments is
what makes that unsayable.
The granularity stays a field rather than a fourth argument, and since
0.32.0 it is curve’s: it is genuinely optional — the
host’s own is a real answer — and the two bounds are not.
Sourcepub const fn upload(
name: &'a str,
label: &'a str,
accept: &'a [Accepted<'a>],
) -> Self
pub const fn upload( name: &'a str, label: &'a str, accept: &'a [Accepted<'a>], ) -> Self
A file field, taking the given accept list.
The fourth under-described kind and it gets a constructor for
range’s reason rather than select’s:
a file field with no accept list is not broken, it is a field that takes
anything, and the hazard is the opposite one. A call site that meant to
restrict and forgot has a picker offering every file on the machine and
a server refusing the upload afterwards, which is the failure the list
exists to move forward. Taking it as an argument is what makes an
accidental omission a deliberate &[].
multiple stays a field. One file is the common case
and the honest default; several is the thing worth saying.
Sourcepub const fn select(
name: &'a str,
label: &'a str,
options: &'a [Choice<'a>],
) -> Self
pub const fn select( name: &'a str, label: &'a str, options: &'a [Choice<'a>], ) -> Self
A select offering the given options.
One of the two kinds under-described by Field::new, so it gets a
constructor rather than leaving every call site to remember that a
select with an empty options renders as an empty select.
Sourcepub const fn radio(
name: &'a str,
label: &'a str,
options: &'a [Choice<'a>],
) -> Self
pub const fn radio( name: &'a str, label: &'a str, options: &'a [Choice<'a>], ) -> Self
A radio group offering the given options.
The other. Same hazard as select and a worse one: a
radio group with no options draws nothing at all, so a call site that
forgot them has an empty rectangle rather than a visibly empty control.
Sourcepub const fn invalid(&self) -> bool
pub const fn invalid(&self) -> bool
Whether the field is currently reporting a problem.
Read this rather than testing error.is_some() at each renderer: the
error state has to mark the field’s whole group and not only the
message, because a renderer with no descendant selectors (egui, a
terminal) cannot find the group from the message. goingson already marks
the group and Balanced Breakfast does not, so goingson’s shape is the
one taken here.
Sourcepub const fn bounded(&self) -> bool
pub const fn bounded(&self) -> bool
Whether the field carries both ends of its extent.
Only FieldKind::Range owes them, and it owes them absolutely: a
slider with one end missing has no extent to draw. Named here rather
than left to each renderer to test min.is_some() && max.is_some(),
which is three renderers arriving at the same condition and one of them
getting it wrong, and named as a question about the field rather than
about the kind because the kind cannot see the bounds.
It is a check and not a guarantee. Nothing here refuses to build an
unbounded range — Field::range is what makes the bounded one easy —
so a renderer asks this and falls back to whatever its host does
honestly with a number.
Sourcepub fn accepts_media(&self) -> bool
pub fn accepts_media(&self) -> bool
Whether anything in accept names a media family.
The question a renderer asks before it decides to keep room for a
preview, and it is deliberately the whole list rather than one entry:
the media dropzone this was measured against takes image/*,video/*, so
there is no single family to return and there is still a disclosure to
offer. Which one it turns out to be is known once a file is picked, which
is renderer-side and after the description is gone.
False for an empty list, for a list of suffixes, and for text/csv. A
renderer that wants the family of a particular entry reads
Accepted::family.