max / makeover-layout
| 1 | // Names this module's prose links to, resolved for rustdoc. |
| 2 | |
| 3 | use crate::; |
| 4 | |
| 5 | /// How much room a placement asks for. |
| 6 | /// |
| 7 | /// A column says it, and so does a [`Field`]. An intent, so the actual floor |
| 8 | /// stays with `makeover-geometry`. goingson's task table spells these as |
| 9 | /// `minmax(200px, 1fr)`, `140px` and content-sized; only the first three words |
| 10 | /// of that survive deferral. |
| 11 | /// `#[non_exhaustive]`, for the reason [`Fill`] and [`FieldKind`] are: a |
| 12 | /// renderer matches on this and a vocabulary that grows must not break every |
| 13 | /// renderer when it does. |
| 14 | |
| 15 | |
| 16 | |
| 17 | /// Takes what it needs and no more. |
| 18 | Content, |
| 19 | /// A fixed share, the same at every width. |
| 20 | Fixed, |
| 21 | /// Absorbs whatever is left over. |
| 22 | /// |
| 23 | /// **Several fills divide what is left equally.** Stated because it would |
| 24 | /// otherwise be undefined and each renderer would invent something, and |
| 25 | /// stated this way because equal division is the only sharing rule that |
| 26 | /// answers to "Any width, one answer" without a tiebreak: allocating in |
| 27 | /// declaration order makes the result depend on the order the description |
| 28 | /// was written in, which is a fact about the source file and not about the |
| 29 | /// screen. It documents what both renderers already do — CSS grid gives |
| 30 | /// `1fr 1fr`, ratatui gives each a `Constraint::Fill(1)` — rather than |
| 31 | /// changing anything. |
| 32 | /// |
| 33 | /// So a row of fills is a legal thing to describe, and there is no rule |
| 34 | /// against it. |
| 35 | Fill, |
| 36 | |
| 37 | |
| 38 | /// What a member is worth when there is not room for all of them. |
| 39 | /// |
| 40 | /// Written for table columns and no longer only theirs. Three shapes ask the |
| 41 | /// same question and this answers all three: a table too narrow for its |
| 42 | /// columns, a row too narrow for its parts (see [`RowPart::priority`]), and a |
| 43 | /// group of regions sharing one run of room -- goingson's tab strip and the |
| 44 | /// [`Region::Band`] beside it, which is the case wiki `layout-room-and-fallback` |
| 45 | /// was ruled on. It is what any member of a group is worth, not a table |
| 46 | /// concept, and [`Fallback::Shed`] is what reads it. |
| 47 | /// |
| 48 | /// The doc below is the column argument, which is where the type was measured; |
| 49 | /// the sentence that gave it away is [`Priority::Essential`]'s, which was |
| 50 | /// already written about a row. |
| 51 | /// |
| 52 | /// Ordered: [`Priority::Optional`] drops first, [`Priority::Essential`] never |
| 53 | /// drops. This replaces addressing columns by position, which is what both |
| 54 | /// webview apps do today and is a live bug rather than only verbosity. goingson |
| 55 | /// hides mobile columns with `nth-child(n+5)` against a seven-column table, so |
| 56 | /// inserting a column silently hides the wrong one. |
| 57 | /// `#[non_exhaustive]`, same reasoning as [`Width`]. Note the ordering is the |
| 58 | /// whole point of the type, so a new tier has to be declared in its place in |
| 59 | /// the sequence rather than appended. |
| 60 | |
| 61 | |
| 62 | |
| 63 | /// Dropped first. |
| 64 | Optional, |
| 65 | /// Dropped once the optional members are gone. |
| 66 | Secondary, |
| 67 | /// Never dropped. Without it the group does not identify itself. |
| 68 | Essential, |
| 69 | |
| 70 | |
| 71 | /// What a group does when it runs out of room. |
| 72 | /// |
| 73 | /// Authored, and required: the field carrying this has no `Default` and a group |
| 74 | /// cannot be described without saying what it does when it runs out of room. |
| 75 | /// Max ruled on that: more intentionality from layout designers is |
| 76 | /// acceptable so long as the constraints are solvable, because the goal is |
| 77 | /// enabling good layouts rather than rescuing bad ones. A default here would be |
| 78 | /// the crate guessing, and the guess would be silently wrong on the screens |
| 79 | /// that matter. |
| 80 | /// |
| 81 | /// Relief resolves inside-out. A group asks its children to fall back before |
| 82 | /// falling back itself, or an outer group collapses while an inner one still |
| 83 | /// had slack. |
| 84 | /// |
| 85 | /// # No `Swap` |
| 86 | /// |
| 87 | /// An authored alternate group for the tight case is deliberately out of the |
| 88 | /// first cut. It doubles the description for that group and the two halves can |
| 89 | /// drift, which is the failure this vocabulary exists to end. Add it when a |
| 90 | /// site proves it needs one. |
| 91 | /// |
| 92 | /// `#[non_exhaustive]`, [`Width`]'s reasoning. Unlike [`Priority`] there is no |
| 93 | /// order to preserve, so a member can be appended. |
| 94 | |
| 95 | |
| 96 | |
| 97 | /// One row becomes two. Every member stays, in the order described. |
| 98 | Wrap, |
| 99 | /// A row becomes a column. Every member stays, full width. |
| 100 | Stack, |
| 101 | /// Members drop by [`Priority`], down to [`Priority::Essential`]. |
| 102 | /// |
| 103 | /// What a narrow table already does with its columns, applied to a group. |
| 104 | /// What drops is gone from the screen, so this is right when the dropped |
| 105 | /// members are facts the reader can do without and wrong when they are the |
| 106 | /// only way to act. |
| 107 | Shed, |
| 108 | /// The members [`Shed`](Self::Shed) would drop move into one overflow |
| 109 | /// control instead. |
| 110 | /// |
| 111 | /// The answer when a group holds actions. A control is not a fact: dropping |
| 112 | /// it does not cost the reader a detail, it costs them the only way to act, |
| 113 | /// which is [`RowPart::priority`]'s argument one level up. |
| 114 | Menu, |
| 115 | |
| 116 | |
| 117 | /// One column of a table. |
| 118 | /// |
| 119 | /// Described once. The grid track, the cell order and the drop behaviour are |
| 120 | /// all derived from this, rather than being three hand-written encodings that |
| 121 | /// must agree and are never checked against each other. |
| 122 | |
| 123 | |
| 124 | /// The heading, and the name the cell is addressed by. |
| 125 | pub name: &'a str, |
| 126 | /// How much room it asks for. |
| 127 | pub width: Width, |
| 128 | /// What it is worth when room runs out. |
| 129 | pub priority: Priority, |
| 130 | /// Whether the user can reorder the table by this column. |
| 131 | /// |
| 132 | /// What reordering *calls* is not here — that is an address, and this |
| 133 | /// crate names none — so a host pairs this with the route the way it pairs |
| 134 | /// a row's parts with the row's activation. This says the affordance |
| 135 | /// exists, which is what a renderer needs to draw a header a user can |
| 136 | /// press rather than a heading they cannot. |
| 137 | pub sortable: bool, |
| 138 | /// Which way the table is ordered by this column, if it is. |
| 139 | /// |
| 140 | /// `None` on every column but the one in force. A renderer draws the caret |
| 141 | /// from this and a webview sets `aria-sort`, which is why it is per column |
| 142 | /// rather than a single fact on the table: the host idiom is a property of |
| 143 | /// the header cell. |
| 144 | /// |
| 145 | /// Independent of [`sortable`](Self::sortable) rather than implied by it, |
| 146 | /// because both combinations mean something. A column sorted and not |
| 147 | /// sortable is a list ordered by a key the user cannot change, which is a |
| 148 | /// real thing to describe and a caret worth drawing. |
| 149 | pub sorted: , |
| 150 | |
| 151 | |
| 152 | /// Which way a column is ordered. |
| 153 | /// |
| 154 | /// Two, because there is no third. "Unsorted" is [`Column::sorted`] being |
| 155 | /// `None`, and folding it in here would be the same absence said twice. |
| 156 | |
| 157 | |
| 158 | /// Smallest, earliest or first alphabetically at the top. |
| 159 | Ascending, |
| 160 | /// The other way. |
| 161 | Descending, |
| 162 | |
| 163 | |
| 164 | |
| 165 | /// The other direction, for a header that flips when pressed. |
| 166 | |
| 167 | pub const |
| 168 | match self |
| 169 | SelfAscending => SelfDescending, |
| 170 | SelfDescending => SelfAscending, |
| 171 | |
| 172 | |
| 173 | |
| 174 | /// What a webview writes into `aria-sort`. |
| 175 | /// |
| 176 | /// Named here rather than in the webview renderer because a terminal and an |
| 177 | /// immediate-mode painter both want the same two words for a caret's label, |
| 178 | /// and three renderers picking their own is the drift this crate ends. |
| 179 | |
| 180 | pub const |
| 181 | match self |
| 182 | SelfAscending => "ascending", |
| 183 | SelfDescending => "descending", |
| 184 | |
| 185 | |
| 186 | |
| 187 | /// The caret a renderer draws for this direction. |
| 188 | /// |
| 189 | /// Here for [`as_str`](Self::as_str)'s reason, said about a glyph rather |
| 190 | /// than a word: three renderers picking their own is the drift this crate |
| 191 | /// ends. They had picked their own — two on the solid triangles and |
| 192 | /// `makeover-webview` on the arrows U+2191/U+2193 — and agreeing by |
| 193 | /// coincidence in three files is not agreement. |
| 194 | /// |
| 195 | /// The reason generalizes past this pair and is the house rule now — |
| 196 | /// prefer the bolder, simpler glyph over the thinner or more complicated |
| 197 | /// one. A third spelling is not open for re-argument. |
| 198 | /// |
| 199 | /// **Bare, with no spacing.** Where the gap goes is each renderer's |
| 200 | /// business: `makeover-tui` and `makeover-immediate` carry a leading space |
| 201 | /// inside their `TableStyle` string and a webview emits its own in |
| 202 | /// `content`, so folding a space in here would make one of the two wrong. |
| 203 | /// |
| 204 | /// Neither face the web apps self-host carries these — IBM Plex Mono has one |
| 205 | /// glyph in the whole geometric-shapes block and Lato has none — so a |
| 206 | /// browser falls back per glyph until the in-house face ships with them |
| 207 | /// drawn in (wiki `typography-standard`). Cosmetic |
| 208 | /// drift in one renderer, not a reason to spell it three ways. |
| 209 | |
| 210 | pub const |
| 211 | match self |
| 212 | SelfAscending => "\u{25B2}", |
| 213 | SelfDescending => "\u{25BC}", |
| 214 | |
| 215 | |
| 216 | |
| 217 | |
| 218 | |
| 219 | /// A column that absorbs slack and drops after the optional ones. |
| 220 | |
| 221 | pub const |
| 222 | Self |
| 223 | name, |
| 224 | width: Fill, |
| 225 | priority: Secondary, |
| 226 | sortable: false, |
| 227 | sorted: None, |
| 228 | |
| 229 | |
| 230 | |
| 231 | /// Whether this column survives at the given cutoff. |
| 232 | /// |
| 233 | /// A renderer narrows by raising the cutoff, and never by counting |
| 234 | /// positions. |
| 235 | |
| 236 | pub const |
| 237 | >= |
| 238 | |
| 239 | |
| 240 | |
| 241 | /// What a table cell holds. |
| 242 | /// |
| 243 | /// [`RowPart`] for tables, and it exists for the same reason: a part that |
| 244 | /// carries a control is not text, and a renderer with one class for the whole |
| 245 | /// cell paints it as though it were: a button in a cell inherits the cell's |
| 246 | /// content colour, which is the drift [`RowPart::intent`] prevents for rows. |
| 247 | /// |
| 248 | /// Four members, and the count is what quasi's `Cell` was measured to carry: a |
| 249 | /// value, tokens, actions and a link. Nothing was added past what something |
| 250 | /// holds. |
| 251 | /// |
| 252 | /// `#[non_exhaustive]` for [`RowPart`]'s reason: growth here must not be a |
| 253 | /// lockstep event across three renderers. |
| 254 | /// |
| 255 | /// # No hover-reveal |
| 256 | /// |
| 257 | /// This enum never gets one. A cell's actions are shown at rest in every |
| 258 | /// consumer measured, and a member nothing uses is one three renderers owe an |
| 259 | /// answer for. |
| 260 | |
| 261 | |
| 262 | |
| 263 | /// The cell's own text. |
| 264 | Value, |
| 265 | /// Small labelled things in the cell: a status badge, a chip. |
| 266 | Tokens, |
| 267 | /// Controls that act on what the row is about. |
| 268 | Actions, |
| 269 | /// The cell's value, where the value is itself a link. |
| 270 | Link, |
| 271 | |
| 272 | |
| 273 | |
| 274 | /// The content intent the part takes. |
| 275 | /// |
| 276 | /// One part is text and three are not, so three answer with the intent |
| 277 | /// inheriting already gives. That is [`RowPart::intent`]'s shape with the |
| 278 | /// text side narrower: a cell's secondary and muted readings are the |
| 279 | /// column's business, not the cell's. |
| 280 | |
| 281 | pub const |
| 282 | match self |
| 283 | SelfValue => "content", |
| 284 | // A token carries its own tone, and a part-level intent underneath |
| 285 | // it would fight the token sitting on it. |
| 286 | SelfTokens => "content", |
| 287 | // Actions carry controls rather than text. |
| 288 | SelfActions => "content", |
| 289 | // A link takes the action colour from the control it is, rather |
| 290 | // than the cell's text colour from the cell it sits in. |
| 291 | SelfLink => "content", |
| 292 | |
| 293 | |
| 294 | |
| 295 |