Skip to main content

Crate makeover_immediate

Crate makeover_immediate 

Source
Expand description

The immediate-mode renderer for makeover_layout.

Named for the mode, not the library, the way makeover-tui is named for the target and not for ratatui. Immediate mode is the constraint that actually separates this renderer from the other two, and egui is the backend it is written against.

It is the harshest renderer the description has to survive: no box-shadow, no inset, no cascade, no retained tree to mutate, and Visuals.widgets.*.bg_stroke is a single stroke with no per-side control. A two-tone lit edge is not something egui can be configured into producing, so it gets painted by hand here, once, instead of in every consuming app.

§What this crate does and does not own

It owns the expression: two mitred polylines for a bevel and a Frame for a filled region. It owns no colours, no sizes and no substitutions: makeover derives surface-well and every consumer reads the real token. Palette is supplied by the caller, already resolved, and every radius, margin and stroke width arrives in FrameStyle.

That split is why the crate has no dependency on makeover itself: the app already resolves a theme, and coupling a renderer to a colour crate’s version would buy nothing.

§The cascade is the real difference

A stylesheet can say “a pressed button inverts its bevel” once and let the cascade carry it. An immediate-mode renderer has nowhere to put that, so every call site decides. makeover_layout::Depth::pressed is what keeps the decision from being re-derived per widget.

§Overlays

Palette::cast is what overlaying means in immediate mode. frame hands the cast shadow to the egui::Frame for any depth whose fill is Fill::Overlay, keyed off the fill rather than the variant.

§The table

table draws makeover_layout::CellPart. Two things it forces, both named where they land:

  • egui_extras, this crate’s one dependency past egui. egui has no table, and Grid gives no per-column sizing, no sticky header and no scroll sync. A third answer here would reimplement that crate worse.
  • Palette::action, on the footing Palette::content sits on: a link in a cell needs the action intent.

Narrowing works differently from the terminal’s and the module header says why: a content column cannot be measured before the app’s closure has drawn it, so egui_extras sizes it and the declared floor budgets it.

Three things are host idiom rather than description, which is why they land here and not in makeover-layout, and all three are answered on a handle the app never sees: the egui_extras row and builder this crate owns. That is table::cell’s reasoning again: what the app cannot reach, the renderer owes it.

  • A selected row. table::Body::selected, a predicate asked per row, because set_selected is a method on the row. Without it a file list has no way to show what is selected, which is most of what a file list does.
  • Scrolling a row into view. table::Body::scroll_to, because scroll_to_row is a method on the builder. A keyboard cursor that moves off-screen and stays there is the bug this prevents.
  • Dragging a divider. table::TableStyle::resizable, which is a knob because egui_extras offers two settings here and a renderer can honestly make either choice.

Cells are centred on the row’s centre line, always, because there is no second honest answer and egui’s own default (top-aligned) is the one thing it cannot be. That is not a knob.

table::Body is also what splits a table’s per-frame facts from its description and from its style. A row count, a selection and a scroll request are none of them style, and none of them survive the frame.

§The nodes that are not fields, tables or frames

widget draws a meter, a token, a control and a figure. The four are ordinary nodes, so without them a screen walk has to draw them itself, one copy per consumer.

Palette carries the three status intents together rather than one per widget, for the reason Palette::fill is an Option: Tone is five members wide, and a resolver missing one has to invent a colour, which is a substitution this crate does not make.

§Forms

The field vocabulary sits on top of the depth vocabulary: makeover_layout::Field rendered to egui widgets, in field, and a set of them laid down a column in group.

makeover-webview’s form emitter is the precedent, followed rather than re-derived, including the parts that are bug fixes: a select handed a value none of its options carries keeps that value visible instead of silently reading as the first option, which is a save-the-wrong- thing bug goingson hit for real.

What differs is forced by the mode and not chosen:

  • The value arrives as a &mut. Filling borrows the app’s own field and the widget writes through it. There is no DOM to read back out of, which is also why the description deliberately does not carry the value.
  • A text control is drawn as a well and a select is not. The description holds that a well is for anything the user looks into, and a text field is its own example; a select and a checkbox are pressed rather than looked into, so they keep egui’s own control painting.
  • Focus is not describable, and egui owns all of it here. Reach, focus and the focus ring are this renderer’s three answers and egui already has all three: its own id stack decides what is reachable, its own state decides what holds the keyboard, and it paints exactly one ring. A description states none of them, and drawing a second ring on top of egui’s would break the one-ring rule. The terms are defined once in makeover_layout’s crate header, “Reach, focus and the focus ring”. makeover_layout::State::Disabled is drawn, because egui has no opinion about it until told.
  • App-level chrome is not drawn here, and it is not this crate’s to draw. quasi-router names the affordances that outlive one screen: a Chrome of key bindings, and an Outcome::Over for a screen drawn over another. Both are answered by quasi-webview and quasi-tui, and neither is answerable here, because this crate depends on makeover-layout and not on quasi-router — it is the peer of makeover-webview and makeover-tui, one layer below the renderers that consume a Screen. What is missing is the egui crate at that layer, which does not exist: nothing renders a quasi Screen in egui at all, and chrome is one item on the list such a crate would owe. Said here because this is where a reader looks for it, and because the silent version reads as “egui does not need a palette” rather than “nobody has built the renderer yet”.

§An interval is a sixth control shape

FieldKind::Interval is drawn as Control::Spanned: two drag boxes on one row with the word to between them.

  • Dragged rather than typed, because that is what these controls already were. audiofiles’ six filter axes are DragValue pairs sharing an extent, a speed and a suffix, and describing them into two text boxes would be a port that cost the app a control.
  • One row, not two wells stacked. Two wells are two questions on screen whatever the description says, and the arrangement is the whole content of the kind.
  • An empty end reads as the bound it stands for. An unset minimum sits on the low edge and stores no filter, which is what the shipped control did; egui’s DragValue has no empty state, and a text box in its place would cost the app a control. With no extent to fall back on it reads zero – the one number this renderer invents, invented where the description declined to say anything.
  • The word rather than a dash, which on a signed axis is a minus sign. audiofiles filters loudness in dBFS.

Axis holds the four facts both boxes share, because they are one axis: reading min, max, step and unit once is what stops the two ends drifting apart.

§A number draws its unit

Field::unit, and this host is the one with somewhere better than the label to put it: egui’s Slider draws a suffix beside its readout.

So a slider takes it as a suffix, inside the control. A typed number has no readout of its own and takes it as a muted label after the box. Every other kind ignores it, and the description says which those are – FieldKind::measurable, rather than a matches! kept here.

§The slider’s track is a curve

makeover-layout says what a slider is: a fraction and a function taking numbers to numbers, with min and max being f(0) and f(1) rather than the control’s extent. This host has the easiest job of the three, because egui already has the control – Slider::logarithmic is a constant-ratio track, so the mapping is a builder call rather than an arithmetic of its own.

Two things worth knowing. The granularity rides on the curve, so a range reads Field::curve.step() and every other kind reads Field::step; the step is in the value’s own units under either curve, so the display precision derives from it directly. And the fallback for a ratio curve across zero is asked of Curve::is_ratio rather than matched on the variant, so this renderer and a terminal cannot disagree about when a logarithmic request is honoured.

§The slider, the unanswered chooser, and the option that is not offered

yet

Three things a description can say here.

  • FieldKind::Range is a fifth control shape, Control::Slid, drawn with egui’s Slider. A range missing an end falls back to a well rather than to invented bounds, which is what makeover_layout::Field::bounded is for.
  • Field::placeholder reads on a chooser, so a select with nothing chosen does not show an empty box.
  • Choice::unavailable is drawn rather than dropped. The option stays in the list, inert, with its precondition beside it instead of behind a hover — a greyed row with no reason reads as a dead end, which is the whole finding.
  • Choice::detail is drawn under the option in a radio group and inside the row in a combo. A closed chooser hides its list, so everything an option carries has to travel with its row; a group has a line to spare and putting a sentence beside the control instead would push every option’s radio out of line with its neighbours.

The value still arrives as a &mut String and a slider is a number, so the parse and the write-back are this renderer’s, and the write happens only on a real drag: a value the app put there that this host cannot read survives being looked at.

Modules§

host
Columns, narrowing, cell parts and the sort caret, over egui_extras. The design system, handed to egui as a Style.
table
Columns, narrowing, cell parts and the sort caret, over egui_extras.
widget
The described things that are not fields, tables or frames.

Structs§

FieldStyle
The geometry a field group is drawn with.
FrameStyle
The geometry a framed region is drawn with.
Palette
The resolved colours this renderer needs, as flat values.

Enums§

Filling
What the field currently holds, borrowed from wherever the app keeps it.

Functions§

field
One field, as the column the app drops into its form.
frame
Draw a region at a given Depth: its fill and its edge, together.
group
A set of fields, laid down a column.
paint_bevel
Paint a two-tone edge just inside rect.