| 56 |
56 |
|
//! will be visible against what is behind it, and at truecolor it fires on
|
| 57 |
57 |
|
//! exactly the two clamping themes, which is precisely when it should.
|
| 58 |
58 |
|
//!
|
| 59 |
|
- |
//! # 0.13.0: a modal, and two cues four ports were about to each invent
|
|
59 |
+ |
//! # Overlays, selection and the focus ring
|
| 60 |
60 |
|
//!
|
| 61 |
|
- |
//! [`Depth::Overlay`](makeover_layout::Depth::Overlay) arrives in
|
| 62 |
|
- |
//! makeover-layout 0.14.0 and needed nothing here: [`Palette::fill`] has
|
| 63 |
|
- |
//! answered `Fill::Overlay` since this crate had a palette, so what was missing
|
| 64 |
|
- |
//! was the route from a description rather than the drawing. A test asserts it,
|
| 65 |
|
- |
//! because a route nothing exercises is one a refactor can quietly lose.
|
|
61 |
+ |
//! [`Palette::fill`] answers `Fill::Overlay`, so
|
|
62 |
+ |
//! [`Depth::Overlay`](makeover_layout::Depth::Overlay) needs nothing beyond the
|
|
63 |
+ |
//! route from a description. A test asserts that route, because a route nothing
|
|
64 |
+ |
//! exercises is one a refactor can quietly lose.
|
| 66 |
65 |
|
//!
|
| 67 |
|
- |
//! [`Theme::selection_on`] and [`Theme::focus_ring`] are the other half, and
|
| 68 |
|
- |
//! both are DERIVED rather than authored. Every consumer measured did selection
|
| 69 |
|
- |
//! with `REVERSED`, for want of an on-accent foreground; every one that wanted
|
| 70 |
|
- |
//! a focus ring either spent makeover's `border-strong` on it, which is a
|
| 71 |
|
- |
//! divider at 1.63:1 on Akari Dawn, or derived its own the way `alloy_tui`
|
| 72 |
|
- |
//! does. Four terminal ports were each about to answer that separately.
|
|
66 |
+ |
//! [`Theme::selection_on`] and [`Theme::focus_ring`] are DERIVED rather than
|
|
67 |
+ |
//! authored, and the direction is one-way: an authored key can fall back to a
|
|
68 |
+ |
//! derivation and break no theme on disk, while a key this crate started
|
|
69 |
+ |
//! requiring would break every theme that lacks it. So the theme format does not
|
|
70 |
+ |
//! change, and the promotion stays available for a theme that ever needs to tune
|
|
71 |
+ |
//! either. Without them a port does selection with `REVERSED` for want of an
|
|
72 |
+ |
//! on-accent foreground, and spends makeover's `border-strong` on a focus ring,
|
|
73 |
+ |
//! which is a divider at 1.63:1 on Akari Dawn.
|
| 73 |
74 |
|
//!
|
| 74 |
|
- |
//! Derived, not authored, because the direction is one-way. An authored key can
|
| 75 |
|
- |
//! fall back to a derivation and break no theme on disk; a key this crate
|
| 76 |
|
- |
//! started requiring would break every theme that lacks it. So the theme format
|
| 77 |
|
- |
//! does not change and nothing on disk grows, and the promotion stays available
|
| 78 |
|
- |
//! for a theme that ever needs to tune either.
|
|
75 |
+ |
//! # The table
|
| 79 |
76 |
|
//!
|
| 80 |
|
- |
//! # 0.14.0: the first structural widget
|
| 81 |
|
- |
//!
|
| 82 |
|
- |
//! [`table`] is the first thing here that draws content rather than a surface,
|
| 83 |
|
- |
//! and it exists because 14 call sites across `mnw-cli` and `viewer` were
|
| 84 |
|
- |
//! already drawing one. `mnw-cli` had written the mapping layer by hand
|
| 85 |
|
- |
//! (`src/tui/widgets.rs`: a muted bold header, a selected row carried by the
|
| 86 |
|
- |
//! background alone) and `viewer` had written a smaller one, which is two
|
| 87 |
|
- |
//! answers to a question this crate is supposed to answer once.
|
| 88 |
|
- |
//!
|
| 89 |
|
- |
//! It is a mapping layer over [`ratatui::widgets::Table`] rather than a table
|
|
77 |
+ |
//! [`table`] is the one thing here that draws content rather than a surface. It
|
|
78 |
+ |
//! is a mapping layer over [`ratatui::widgets::Table`] rather than a table
|
| 90 |
79 |
|
//! implementation, because ratatui already lays tracks out, draws a header,
|
| 91 |
80 |
|
//! highlights a row and scrolls. What it has no answer for is content
|
| 92 |
81 |
|
//! measurement and narrowing, and those are what the module is.
|
| 93 |
82 |
|
//!
|
| 94 |
|
- |
//! # 0.19.0: `widget` is [`piece`], because the word went to the description
|
|
83 |
+ |
//! # `piece`, not `widget`
|
| 95 |
84 |
|
//!
|
| 96 |
|
- |
//! `makeover-layout` 0.20.0 added `Region::Widget`, the third tier between a
|
| 97 |
|
- |
//! primitive and `Region::Bespoke`: a named assembly of primitives that each
|
| 98 |
|
- |
//! renderer draws its own way. That is host-agnostic and sits *above* every
|
| 99 |
|
- |
//! renderer.
|
|
85 |
+ |
//! `makeover-layout` owns `Region::Widget`, the tier between a primitive and
|
|
86 |
+ |
//! `Region::Bespoke`: a named assembly of primitives that each renderer draws
|
|
87 |
+ |
//! its own way. That is host-agnostic and sits *above* every renderer.
|
| 100 |
88 |
|
//!
|
| 101 |
|
- |
//! This crate's `widget` module is the opposite end of the same stack —
|
|
89 |
+ |
//! This crate's [`piece`] module is the opposite end of the same stack:
|
| 102 |
90 |
|
//! renderer-local, the answer to what a meter looks like in cells, taking a
|
| 103 |
|
- |
//! description plus what only a terminal knows. Two different things wearing
|
| 104 |
|
- |
//! one word, and the collision would have been worst exactly here, in a crate
|
| 105 |
|
- |
//! that has to implement the tier.
|
|
91 |
+ |
//! description plus what only a terminal knows. Two different things must not
|
|
92 |
+ |
//! wear one word, least of all in a crate that has to implement the tier. The
|
|
93 |
+ |
//! style type is [`piece::PieceStyle`].
|
| 106 |
94 |
|
//!
|
| 107 |
|
- |
//! So this half moved and the description's half kept the word. That direction
|
| 108 |
|
- |
//! is not arbitrary: a second or third party naming a widget is naming the
|
| 109 |
|
- |
//! layout kind, and nothing outside this tree ever needed a word for a drawing
|
| 110 |
|
- |
//! routine. `WidgetStyle` is `PieceStyle`.
|
|
95 |
+ |
//! # A bounded number, and an option that is not offered yet
|
| 111 |
96 |
|
//!
|
| 112 |
|
- |
//! # 0.27.0: a bar for a bounded number, and an option that is not offered yet
|
| 113 |
|
- |
//!
|
| 114 |
|
- |
//! `makeover-layout` 0.28.0's form findings, at the renderer that has the bar
|
| 115 |
|
- |
//! already. A [`makeover_layout::FieldKind::Range`] is drawn as [`piece::meter`]'s
|
| 116 |
|
- |
//! cells with its two ends read out either side, because the ends are what the
|
|
97 |
+ |
//! A [`makeover_layout::FieldKind::Range`] is drawn as [`piece::meter`]'s cells
|
|
98 |
+ |
//! with its two ends read out either side, because the ends are what the
|
| 117 |
99 |
|
//! question means and a terminal is where it would be easiest to quietly show a
|
| 118 |
|
- |
//! figure instead. An unbounded range falls back to the text path rather than
|
| 119 |
|
- |
//! to bounds this crate invented.
|
|
100 |
+ |
//! figure instead. An unbounded range falls back to the text path rather than to
|
|
101 |
+ |
//! bounds this crate invented.
|
| 120 |
102 |
|
//!
|
| 121 |
103 |
|
//! `Choice::unavailable` is the one place the three-tone convention's muted is
|
| 122 |
104 |
|
//! the truth rather than the lie it warns about: that option will not answer,
|
| 123 |
|
- |
//! and the reason it will not now sits on its row.
|
|
105 |
+ |
//! and the reason it will not sits on its row.
|
| 124 |
106 |
|
//!
|
| 125 |
|
- |
//! `Choice::detail` (makeover-layout 0.39.0) takes a row of its own under the
|
| 126 |
|
- |
//! option, inset by the width of the mark and muted for the same reason: the
|
| 127 |
|
- |
//! line is not a thing to press. This is the host with the most room of the
|
| 128 |
|
- |
//! three -- a browser's `<select>` has to run the line into its option's text
|
| 129 |
|
- |
//! and a terminal does not, so it does not.
|
|
107 |
+ |
//! `Choice::detail` takes a row of its own under the option, inset by the width
|
|
108 |
+ |
//! of the mark and muted for the same reason: the line is not a thing to press.
|
|
109 |
+ |
//! This is the host with the most room of the three, so unlike a browser's
|
|
110 |
+ |
//! `<select>` it does not run the line into the option's text.
|
| 130 |
111 |
|
//!
|
| 131 |
|
- |
//! `Field::placeholder` on a chooser, the third finding, is already answered
|
| 132 |
|
- |
//! here and needed nothing: this renderer draws every option of a select at
|
| 133 |
|
- |
//! once, so an unanswered one is a list with no mark against any row rather
|
| 134 |
|
- |
//! than an empty box with nothing in it.
|
|
112 |
+ |
//! `Field::placeholder` on a chooser needs nothing here: this renderer draws
|
|
113 |
+ |
//! every option of a select at once, so an unanswered one is a list with no mark
|
|
114 |
+ |
//! against any row rather than an empty box.
|
| 135 |
115 |
|
//!
|
| 136 |
|
- |
//! # 0.33.0: an interval is one line
|
|
116 |
+ |
//! # An interval is one line
|
| 137 |
117 |
|
//!
|
| 138 |
|
- |
//! `makeover-layout` 0.34.0's [`makeover_layout::FieldKind::Interval`], drawn
|
| 139 |
|
- |
//! as the low end, the word `to`, and the high end. One line because it is one
|
| 140 |
|
- |
//! question: two rows would read as two questions, which is exactly what the
|
| 141 |
|
- |
//! kind exists to stop the description saying, and a terminal has no
|
| 142 |
|
- |
//! side-by-side boxes to fall back on.
|
|
118 |
+ |
//! [`makeover_layout::FieldKind::Interval`] is drawn as the low end, the word
|
|
119 |
+ |
//! `to`, and the high end. One line because it is one question: two rows would
|
|
120 |
+ |
//! read as two questions, which is what the kind exists to stop the description
|
|
121 |
+ |
//! saying, and a terminal has no side-by-side boxes to fall back on.
|
| 143 |
122 |
|
//!
|
| 144 |
123 |
|
//! - **An open end draws the bound it falls back to**, muted, because that is
|
| 145 |
124 |
|
//! where the axis ends rather than a value anybody set. With no bound to fall
|
| 155 |
134 |
|
//! states both names: a separator inside one string would make this crate own a
|
| 156 |
135 |
|
//! delimiter either end could contain.
|
| 157 |
136 |
|
//!
|
| 158 |
|
- |
//! # 0.32.0: a number reads with its unit
|
|
137 |
+ |
//! # A number reads with its unit
|
| 159 |
138 |
|
//!
|
| 160 |
|
- |
//! `makeover-layout` 0.33.0's `Field::unit`, drawn on the value rather than in
|
| 161 |
|
- |
//! the label. A terminal has one line the eye is on -- the number -- and the
|
| 162 |
|
- |
//! label is a line above it, so `0.05 s` is the reading and `Attack (s)` two
|
| 163 |
|
- |
//! rows up is not. A range shows it after the readout beside the bar; a typed
|
| 164 |
|
- |
//! number after the value. Every other kind ignores it, and which those are is
|
| 165 |
|
- |
//! `FieldKind::measurable`'s answer rather than a `matches!` kept here.
|
|
139 |
+ |
//! `Field::unit` is drawn on the value rather than in the label. A terminal has
|
|
140 |
+ |
//! one line the eye is on, the number, and the label is a line above it, so
|
|
141 |
+ |
//! `0.05 s` is the reading and `Attack (s)` two rows up is not. A range shows it
|
|
142 |
+ |
//! after the readout beside the bar; a typed number after the value. Every other
|
|
143 |
+ |
//! kind ignores it, and which those are is `FieldKind::measurable`'s answer
|
|
144 |
+ |
//! rather than a `matches!` kept here.
|
| 166 |
145 |
|
//!
|
| 167 |
|
- |
//! # 0.31.0: the bar fills along the curve
|
|
146 |
+ |
//! # The bar fills along the curve
|
| 168 |
147 |
|
//!
|
| 169 |
|
- |
//! `makeover-layout` 0.32.0's [`Curve`](makeover_layout::Curve). Where a value
|
| 170 |
|
- |
//! sits on the bar is the curve's answer now, not its proportion of the extent.
|
| 171 |
|
- |
//! Under `Curve::Linear` those are the same number, so every range drawn before
|
| 172 |
|
- |
//! this is unchanged; under a constant ratio they are not, and a bar filled
|
| 173 |
|
- |
//! linearly would put an envelope's whole useful half inside its first cell.
|
|
148 |
+ |
//! Where a value sits on the bar is [`Curve`](makeover_layout::Curve)'s answer,
|
|
149 |
+ |
//! not its proportion of the extent. Under `Curve::Linear` those are the same
|
|
150 |
+ |
//! number; under a constant ratio they are not, and a bar filled linearly would
|
|
151 |
+ |
//! put an envelope's whole useful half inside its first cell.
|
| 174 |
152 |
|
//!
|
| 175 |
|
- |
//! The two ends beside the bar do not move: they are `f(0)` and `f(1)`, which
|
| 176 |
|
- |
//! is what they always were and is now what the description calls them.
|
|
153 |
+ |
//! The two ends beside the bar do not move: they are `f(0)` and `f(1)`.
|
| 177 |
154 |
|
//!
|
| 178 |
|
- |
//! # The correction this renderer forced
|
|
155 |
+ |
//! # Substitution is renderer policy
|
| 179 |
156 |
|
//!
|
| 180 |
|
- |
//! [`makeover_layout::Fill`] briefly carried a `fallback` method, returning
|
| 181 |
|
- |
//! `Page` for `Well` so a consumer without `surface-well` had something to
|
| 182 |
|
- |
//! use. That is an answer for a renderer that can always paint a colour. Here
|
| 183 |
|
- |
//! it is actively wrong: page *is* the surface a well is usually cut into, so
|
| 184 |
|
- |
//! falling back to it produces the exact invisibility the fallback was meant
|
| 185 |
|
- |
//! to avoid.
|
| 186 |
|
- |
//!
|
| 187 |
|
- |
//! Substituting one intent for another is renderer policy, not description.
|
| 188 |
|
- |
//! The fallback moved out of the description and into
|
| 189 |
|
- |
//! `makeover-immediate`, where it belongs, which is the first thing a second
|
| 190 |
|
- |
//! renderer was built to find.
|
|
157 |
+ |
//! Substituting one intent for another belongs to a renderer, never to the
|
|
158 |
+ |
//! description. Falling back from `Fill::Well` to `Page` is an answer for a
|
|
159 |
+ |
//! renderer that can always paint a colour; here it is wrong, because the page
|
|
160 |
+ |
//! *is* the surface a well is usually cut into, so the substitution produces the
|
|
161 |
+ |
//! exact invisibility it was meant to avoid. `makeover-immediate` wants that
|
|
162 |
+ |
//! fallback and this renderer does not.
|
| 191 |
163 |
|
|
| 192 |
164 |
|
#![forbid(unsafe_code)]
|
| 193 |
165 |
|
|
| 317 |
289 |
|
pub raised: Color,
|
| 318 |
290 |
|
/// `surface-overlay`.
|
| 319 |
291 |
|
pub overlay: Color,
|
| 320 |
|
- |
/// `surface-well`, absent on makeover before 2.3.0.
|
|
292 |
+ |
/// `surface-well`, when the theme has one.
|
| 321 |
293 |
|
pub well: Option<Color>,
|
| 322 |
294 |
|
/// `bevel-light`.
|
| 323 |
295 |
|
pub bevel_light: Color,
|
| 334 |
306 |
|
/// No substitution happens here. A missing intent stays missing, and
|
| 335 |
307 |
|
/// [`frame`] answers it with structure instead of with a different colour.
|
| 336 |
308 |
|
/// That rule is what lets the wildcard below be a real answer rather than
|
| 337 |
|
- |
/// a hole: [`Fill`] is `#[non_exhaustive]` from `makeover-layout` 0.4.0
|
| 338 |
|
- |
/// onward, so the description can name a surface this renderer has not
|
| 339 |
|
- |
/// learned to paint, and saying so is better than failing to build.
|
|
309 |
+ |
/// a hole: [`Fill`] is `#[non_exhaustive]`, so the description can name a
|
|
310 |
+ |
/// surface this renderer has not learned to paint, and saying so is better
|
|
311 |
+ |
/// than failing to build.
|
| 340 |
312 |
|
#[must_use]
|
| 341 |
313 |
|
pub const fn fill(&self, fill: Fill) -> Option<Color> {
|
| 342 |
314 |
|
match fill {
|
| 439 |
411 |
|
/// sides, which draws a *line* rather than a lit edge and gives up the light
|
| 440 |
412 |
|
/// model that makes a bevel legible.
|
| 441 |
413 |
|
///
|
| 442 |
|
- |
/// Adopted from `alloy_tui`, which reached this independently and got there
|
| 443 |
|
- |
/// first (2026-07-26, two days before this crate existed).
|
|
414 |
+ |
/// Shared with `alloy_tui`, which reached the same glyph set independently.
|
| 444 |
415 |
|
pub(crate) const BEVEL: GlyphSet = GlyphSet {
|
| 445 |
416 |
|
top: "▀",
|
| 446 |
417 |
|
bottom: "▄",
|