#[non_exhaustive]pub enum Region<'a> {
Band,
Sidebar,
Pane,
Split,
TabGroup,
Modal,
Bespoke {
name: &'a str,
},
Widget {
name: &'a str,
},
}Expand description
A named part of a screen.
The thing makeover-geometry deliberately does not name: it names the space
between things by relationship, and nothing named the things. Six named
members, taken from what the two webview apps actually use, plus
Region::Bespoke for the parts no description should reach. Both apps’
layout.css currently names exactly two things, .raised and .well, so
this layer is absent rather than divergent, which makes it the cheapest of
the schemas to add and the easiest to over-build.
#[non_exhaustive] arrives with Region::Widget, the pairing RowPart
made at 0.9.0 and Readiness at 0.12.0, and for the same reason: the
member after this one should not be a lockstep event across three renderers.
Variants (Non-exhaustive)§
This enum is marked as non-exhaustive
Band
A full-width strip with a title slot and an actions cluster, either of
which may be empty. goingson’s .page-header, Balanced Breakfast’s
.header and .detail-header are all this, differing only in which
slots they fill.
Sidebar
A persistent column beside the content, holding navigation.
Pane
A region of content with its own scroll.
Split
Two panes side by side, where the left chooses what the right shows.
TabGroup
A set of panes, one visible at a time, with a Selector::Tabs above.
Modal
Content over a scrim, taking input until dismissed.
Bespoke
A region this crate names the place of and nothing else. The app owns what goes in it.
The escape hatch, and the thing that keeps the description honest about its own limits. A day-plan timeline, a kanban board, a calendar and the paint interaction over the timeline are not describable here and are not going to become describable: a description expressive enough to produce a timeline is a widget library wearing a description’s name.
But a screen containing one still has to be a screen. Without this member the description covers only the boring screens, and the four that make goingson worth using would need a second, undescribed path beside the router. Two paths is how the vocabulary starts drifting from the app again, which is the exact failure this crate exists to end.
So the description says “a thing called day-plan goes here” and stops.
The name is opaque: this crate never interprets it, and no renderer is
expected to know what it means beyond handing the space over.
Widget
A named assembly of things the vocabulary already says.
The third tier, between a primitive and Bespoke.
Stated by Max 2026-08-12 answering the carousel: “something in between a
primitive and a bespoke interface, like a widget, which is just an
assembly of primitives.” Full note: wiki widget-tier.
§What separates it from the two members either side
A primitive is a thing every renderer draws from scratch, and the test
it has to pass is that every host has an honest answer. A carousel fails
that test — a terminal has no carousel — which is the same refusal
Node::Html got and is why the carousel sat unsayable for months.
Bespoke fails it from the other side. Bespoke is for
what one app owns and nobody will build twice, and it carries no
contents: the description names the place and stops. A carousel is
furniture any app would have, and every part of it — an ordered set of
frames, a position, prev and next, a strip of position indicators — is
already sayable. Only the assembly had no name.
So this member is the pair the other two are not: a name and contents. The contents are the assembly, in the region’s own body, said in members that already exist.
§Why the name does not have to be understood
A renderer that recognises the name draws it the way its host does it: a carousel in a webview, a pager with a count in a terminal, a selector in egui. A renderer that does not recognise it walks the body, which is primitives all the way down and which it can already draw.
That is what lets the widget set be open without every renderer
knowing every widget. An unrecognised widget degrades to its assembly
instead of failing, so a second or third party can name one without
three renderers releasing in lockstep to accept it. Contrast
Bespoke, which no renderer can degrade: there is
nothing under it to fall back to.
§What it does not do
It does not make a timeline describable, and the refusal in the crate
header stands unchanged. A widget is an assembly of things the
vocabulary already says; anything that needs a member the vocabulary
does not have is a finding about the vocabulary or it is
Bespoke. A widget is never the way a primitive gets
added by the back door.
Implementations§
Source§impl<'a> Region<'a>
impl<'a> Region<'a>
Sourcepub const fn described(self) -> bool
pub const fn described(self) -> bool
Whether this crate can say anything about the region’s contents.
A renderer walks the description and hands every region it understands
to the right drawing code. This is how it tells the two apart, and the
reason it is a method rather than a matches! at each renderer: there
is exactly one opaque member and there should stay exactly one.
Widget is described, and that is the whole of what
separates it from Bespoke here. Both carry a name
this crate never interprets; only one of them carries contents under it.
A renderer that does not recognise a widget’s name still walks its body,
so there is nothing for it to hand over and nothing it cannot draw.
Sourcepub const fn name(self) -> Option<&'a str>
pub const fn name(self) -> Option<&'a str>
The name an app gave this region, if it gave one.
Bespoke and Widget are the two
members that carry a name, for two different purposes: one says what the
app will fill the space with, the other says what the assembly under it
is called. A renderer dispatching on either wants the string without
caring which member it came from, and writing that matches! at each
renderer is how the two drift apart.