max / makeover-layout
| 1 | use crateTone; |
| 2 | |
| 3 | // Names this module's prose links to, resolved for rustdoc. |
| 4 | |
| 5 | use crate::; |
| 6 | |
| 7 | /// What is in a region right now. |
| 8 | /// |
| 9 | /// The state, not the shimmer. Whether pending paints a skeleton, a spinner or |
| 10 | /// nothing at all is renderer policy, the same class of decision that got |
| 11 | /// `Fill::fallback` deleted from this crate. goingson and Balanced Breakfast |
| 12 | /// each grew a skeleton with differently-named parts; both keep them, as the |
| 13 | /// webview renderer's expression of [`Readiness::Pending`]. audiofiles has none |
| 14 | /// and needs none, because an immediate-mode renderer simply repaints. |
| 15 | /// |
| 16 | /// # Four states and not two |
| 17 | /// |
| 18 | /// Naming only `Ready` and `Pending` leaves a screen whose list came back empty |
| 19 | /// with nothing to say about it, so it renders an empty region or invents its |
| 20 | /// own placeholder text and neither says what it is. Left to the apps, the |
| 21 | /// class family drifts: `empty-state`, `empty-state--error`, `error-state` and |
| 22 | /// six more. |
| 23 | /// |
| 24 | /// The four are one axis because they are mutually exclusive: a region shows its |
| 25 | /// content, or a sign that it is coming, or a sign that there is none, or a sign |
| 26 | /// that it broke. Never two. That is the test for one enum against several |
| 27 | /// fields, and it is why this grew rather than a new member arriving beside it. |
| 28 | /// |
| 29 | /// # What is not here |
| 30 | /// |
| 31 | /// **The message.** "No projects yet" is content, and this names a state. It |
| 32 | /// lives with whatever holds the region — in quasi's case a `Slot` — alongside |
| 33 | /// the action that leads out of the emptiness, since an address is the one thing |
| 34 | /// this crate never names. |
| 35 | /// |
| 36 | /// **How much room it gets.** goingson's `--compact`, `--dashboard` and |
| 37 | /// `--padded` are the same state at three sizes, and a size is |
| 38 | /// `makeover-geometry`'s question. Naming them here would be this crate stating |
| 39 | /// values again. |
| 40 | /// |
| 41 | /// **The icon.** Presentation, and each host has its own answer or none. |
| 42 | |
| 43 | |
| 44 | |
| 45 | /// The content is here. |
| 46 | Ready, |
| 47 | /// The content is on its way. |
| 48 | /// |
| 49 | /// For a region that changes *after* the first paint, and never for the |
| 50 | /// first paint itself: see "First paint is final paint" in the crate header. |
| 51 | /// A host that renders once, with its data already in hand, has nothing to |
| 52 | /// say this about, and a screen arriving in this state is describing a |
| 53 | /// moment its host should not have been in. |
| 54 | /// |
| 55 | /// What stands in occupies the geometry the content will occupy. A stand-in |
| 56 | /// sized to itself rather than to what replaces it is the reflow the rule |
| 57 | /// forbids, arriving one repaint later. |
| 58 | Pending, |
| 59 | /// The content arrived and there is none of it. |
| 60 | /// |
| 61 | /// Not a failure. An empty list is the normal state of a new install, and a |
| 62 | /// renderer that drew it in a danger tone would be reporting a fault where |
| 63 | /// there is none. |
| 64 | Empty, |
| 65 | /// The content did not arrive. |
| 66 | Failed, |
| 67 | |
| 68 | |
| 69 | |
| 70 | /// Whether the region draws its own content, or something standing in for |
| 71 | /// it. |
| 72 | /// |
| 73 | /// The question every renderer asks first, so it is answered once here |
| 74 | /// rather than by a `matches!` in each. A state added later is a stand-in |
| 75 | /// until proven otherwise: falling back to drawing content that may not be |
| 76 | /// there is the worse of the two mistakes. |
| 77 | |
| 78 | pub const |
| 79 | matches! |
| 80 | |
| 81 | |
| 82 | /// What the state means, for a renderer choosing a colour. |
| 83 | /// |
| 84 | /// Derived rather than carried, which is the opposite of [`Meter`] and |
| 85 | /// [`Figure`], and the difference is worth stating: a proportion's meaning |
| 86 | /// depends on what is being counted and only the app knows it, while |
| 87 | /// "nothing here yet" and "this broke" mean the same thing in every app that |
| 88 | /// will ever have them. |
| 89 | |
| 90 | pub const |
| 91 | match self |
| 92 | SelfFailed => Danger, |
| 93 | _ => Neutral, |
| 94 | |
| 95 | |
| 96 | |
| 97 | |
| 98 | /// An action is waiting on something that resolves once, in expected finite |
| 99 | /// time. |
| 100 | /// |
| 101 | /// The control-side sibling of [`Readiness`]. That enum names four states for a |
| 102 | /// region and named nothing at all for the button that is currently doing what |
| 103 | /// it was clicked for, so the in-flight treatment is hand-written wherever it |
| 104 | /// exists: the MNW server carries 57 in-flight indicators against 2 guards |
| 105 | /// against a second press, which is the spinner mostly present and the guard |
| 106 | /// mostly absent, on a codebase whose money path is a purchase button. |
| 107 | /// |
| 108 | /// # What is described here, and what is not |
| 109 | /// |
| 110 | /// The fact is that there is an outstanding thing which will complete. Not that |
| 111 | /// the address is remote: a heavy local query waits too, and a server calling a |
| 112 | /// payment provider is not the browser leaving the app. Not that the call is |
| 113 | /// slow either, which is a judgement about a call rather than a property of one. |
| 114 | /// |
| 115 | /// Resolving **once** is the boundary, and it is what separates this from a |
| 116 | /// screen that keeps changing. A live screen never resolves and has no name in |
| 117 | /// this crate yet. |
| 118 | /// |
| 119 | /// # One mark, two renderings |
| 120 | /// |
| 121 | /// | what reads it | what it does | |
| 122 | /// |---|---| |
| 123 | /// | a control that was pressed | goes busy and refuses a second press until it resolves | |
| 124 | /// | a region fed by it | stands in as [`Readiness::Pending`], then fills | |
| 125 | /// |
| 126 | /// The two were on the table separately and both were taken. Controls alone |
| 127 | /// leaves a slow region hand-split into its own route, which is what MNW's user |
| 128 | /// dashboard does with its payout summary; regions alone leaves the purchase |
| 129 | /// button unguarded. |
| 130 | /// |
| 131 | /// # A quantity when it is measured, never a duration |
| 132 | /// |
| 133 | /// [`amount`](Self::amount) is stated only when it is a measured fact about the |
| 134 | /// payload. An upload's file length, yes; a round trip to a payment provider, |
| 135 | /// [`None`]. A duration is described nowhere, and a renderer may not manufacture |
| 136 | /// one from the amount either: a determinate bar shows what is done over what |
| 137 | /// there is, plus the time it has taken so far, and never a remaining time, an |
| 138 | /// arrival time or a rate extrapolated forwards. A prediction is wrong the |
| 139 | /// moment the transfer stalls, and being confidently wrong is worse than being |
| 140 | /// honestly indeterminate. |
| 141 | /// |
| 142 | /// This is why the crate refuses to say how long an undo stays offered and |
| 143 | /// accepts a byte count here. The refusal is about naming a decision that |
| 144 | /// belongs to the renderer; a file's length is not a decision, nobody chose it. |
| 145 | /// |
| 146 | /// # Not [`Meter`] |
| 147 | /// |
| 148 | /// [`Meter`] is how much of a set is done, and its own docs refuse the progress |
| 149 | /// of an operation on the grounds that a description is built once and dropped |
| 150 | /// while an operation runs between renders. That refusal stands. This names the |
| 151 | /// operation and its size, which is all that is known before it starts; how much |
| 152 | /// of it has gone through is the renderer's to observe live, and nothing round |
| 153 | /// trips through a description to say so. |
| 154 | |
| 155 | |
| 156 | |
| 157 | /// Total work to get through, when it is a measured fact about the payload. |
| 158 | /// |
| 159 | /// `None` when the wait has no countable size, which is the common case and |
| 160 | /// the default. |
| 161 | /// |
| 162 | /// Unit-agnostic on purpose. Bytes for an upload, rows for an import; what |
| 163 | /// is being counted is the app's business and a renderer draws a proportion |
| 164 | /// either way. |
| 165 | pub amount: , |
| 166 | |
| 167 | |
| 168 | |
| 169 | /// A wait with no countable size. |
| 170 | |
| 171 | pub const |
| 172 | Self |
| 173 | |
| 174 | |
| 175 | /// A wait whose size is known. |
| 176 | /// |
| 177 | /// Reach for it only with a measured figure. An estimate written in here is |
| 178 | /// a prediction wearing a fact's clothes, and the renderer has no way to |
| 179 | /// tell the two apart. |
| 180 | |
| 181 | pub const |
| 182 | Self |
| 183 | amount: Some, |
| 184 | |
| 185 | |
| 186 | |
| 187 | /// Whether there is a proportion to draw. |
| 188 | /// |
| 189 | /// The question every renderer asks first, answered once here rather than by |
| 190 | /// a `matches!` in each. False means indeterminate, which is the honest |
| 191 | /// drawing when nothing countable was measured. |
| 192 | |
| 193 | pub const |
| 194 | self.amount.is_some |
| 195 | |
| 196 | |
| 197 | |
| 198 | /// When a picture is needed. |
| 199 | /// |
| 200 | /// A claim about *importance and position* rather than a fetch mechanism, which |
| 201 | /// is why it is the description's to make: only the app knows whether a picture |
| 202 | /// is the first thing on the screen or the fortieth thing down a list. |
| 203 | /// |
| 204 | /// # Eager is the default, and that is a correctness choice |
| 205 | /// |
| 206 | /// Emitting the webview's `loading="lazy"` for every picture reads one |
| 207 | /// consumer's habit as a rule. Deferring a picture that is on screen at first paint does not |
| 208 | /// save anything -- it is needed immediately either way -- and it delays the |
| 209 | /// arrival, so the space it eventually takes is claimed later and the shift is |
| 210 | /// more visible, not less. |
| 211 | /// |
| 212 | /// So the safe answer is the default and the optimisation is opted into. A |
| 213 | /// carousel is the case that proves the two cannot be one setting for the |
| 214 | /// renderer to choose: its first frame is on screen and its other frames are |
| 215 | /// not, in the same widget, at the same moment. |
| 216 | |
| 217 | |
| 218 | |
| 219 | /// Needed with the screen. Fetch it now. |
| 220 | |
| 221 | Eager, |
| 222 | /// Not on screen yet. It can wait until it is near. |
| 223 | Lazy, |
| 224 | |
| 225 |