1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
//! `Widget`/`StatefulWidget` structs: one file per widget, each a builder
//! that owns its own drawing logic.
//!
//! `new()` takes only the arguments a widget cannot mean anything without
//! (the content: a value, a label, a slice of samples/rows). Every other
//! knob (styles, offsets, titles) has a default and is set through a
//! chainable `#[must_use] fn field(mut self, ...) -> Self` method, the same
//! shape as [`Panel::title`] or [`Log::offset`]. See `crates/ui/AGENTS.md`
//! for the rule this is enforcing and why.
//!
//! A few widgets share logic: [`Gauge`] and [`StatBar`] both delegate to a
//! crate-private `bar` module, and [`Sparkline`]/[`Gauge`]/[`StatBar`] all
//! use [`Meter`] for their ratio-to-color ramp. [`Paragraph`], [`List`],
//! [`Table`], [`Log`], and [`Panel`] additionally implement [`Measure`], so
//! a caller can report a height before rendering instead of guessing a
//! fixed height or a full-remaining-space fill: [`Paragraph`] reports its
//! wrapped line count (with the `egc` feature enabled, via
//! `retroglyph_core::layout::TextLayout`'s grapheme-aware word-wrap,
//! otherwise via its own `char`-boundary-safe fallback); [`List`]/[`Table`]/
//! [`Log`] report their item/row/message count directly, since none of them
//! wrap; [`Panel`] reports its border-plus-padding chrome height, since it
//! owns no content of its own to measure.
use Frame;
use Size;
use crateSurface;
use crateDensity;
use crateResponse;
use crateSense;
pub use BorderType;
pub use BoxBorder;
pub use Button;
pub use Gauge;
pub use HighlightSpacing;
pub use List;
pub use ListDirection;
pub use Log;
pub use ;
pub use ;
pub use Meter;
pub use Modal;
pub use ;
pub use Paragraph;
pub use ;
pub use PrintLine;
pub use ProgressBar;
pub use Scrollbar;
pub use Sparkline;
pub use StatBar;
pub use Table;
pub use Tabs;
pub use Text;
pub use TextInput;
/// A type that draws itself into a [`Surface`], without retaining any
/// state: the minimal shape shared by every widget-like consumer.
///
/// # Examples
///
/// ```
/// use retroglyph_core::color::Style;
/// use retroglyph_core::grid::{Grid, Rect};
/// use retroglyph_ui::widget::Widget;
/// use retroglyph_ui::Surface;
///
/// struct Marker(char);
///
/// impl Widget for Marker {
/// fn render(&self, surface: &mut Surface<'_>) {
/// surface.put((0, 0), self.0, Style::new());
/// }
/// }
///
/// let area = Rect::new(0, 0, 4, 1);
/// let mut grid = Grid::new(4, 1);
/// Marker('*').render(&mut Surface::new(&mut grid, area, 0));
/// ```
/// Like [`Widget`], but for widgets that read (and may update) externally
/// owned state (a selection index, a scroll offset) that outlives a
/// single render call. See [`crate::state::ListState`].
///
/// # Examples
///
/// ```
/// use retroglyph_core::color::Style;
/// use retroglyph_core::grid::{Grid, Rect};
/// use retroglyph_ui::widget::StatefulWidget;
/// use retroglyph_ui::Surface;
///
/// struct Counter;
///
/// impl StatefulWidget for Counter {
/// type State = u32;
///
/// fn render(&self, surface: &mut Surface<'_>, state: &mut Self::State) {
/// *state += 1;
/// surface.put((0, 0), 'x', Style::new());
/// }
/// }
///
/// let area = Rect::new(0, 0, 4, 1);
/// let mut grid = Grid::new(4, 1);
/// let mut renders = 0;
/// Counter.render(&mut Surface::new(&mut grid, area, 0), &mut renders);
/// assert_eq!(renders, 1);
/// ```
/// A widget that can report the height it needs for a given width, before
/// ever being rendered.
///
/// Lets a caller size a pane to fit content (e.g. a wrapped `Paragraph`)
/// instead of guessing a fixed height up front.
/// Sizing is pure content math, not drawing.
///
/// # Examples
///
/// ```
/// use retroglyph_ui::widget::Measure;
///
/// struct FixedHeight(u16);
///
/// impl Measure for FixedHeight {
/// fn height_for(&self, _width: u16) -> u16 {
/// self.0
/// }
/// }
///
/// assert_eq!(FixedHeight(3).height_for(80), 3);
/// ```
/// A widget that can report the smallest size its content and [`Density`] allow, before ever
/// being rendered.
///
/// This is a different question from [`Measure::height_for`]: `height_for` answers "how tall are
/// you, given this width" (content wrapped into a fixed width); `min_size` answers "how small
/// can you be at all" (a floor driven by both content and the [`Density`] a caller is targeting).
/// Conflating the two into one trait/method would force every `Measure` impl to also take a
/// `Density` it doesn't need, and every `MinSize` impl to accept a `width` it can't use to answer
/// "how small can you get". Kept separate on purpose; a widget can implement either, both, or
/// neither.
///
/// Interactive widgets ([`Button`], [`Tabs`], [`Scrollbar`], [`TextInput`]) are the intended
/// implementors: [`Density::min_target_size`] exists specifically so a caller sizing a
/// clickable/tappable control doesn't have to invent its own touch-target floor, and `min_size`
/// is how a widget applies that floor to its own content.
///
/// # Examples
///
/// ```
/// use retroglyph_core::grid::Size;
/// use retroglyph_ui::interact::Density;
/// use retroglyph_ui::widget::MinSize;
///
/// struct FixedSize(Size);
///
/// impl MinSize for FixedSize {
/// fn min_size(&self, density: Density) -> Size {
/// self.0.max(density.min_target_size())
/// }
/// }
///
/// let small = FixedSize(Size::new(1, 1));
/// assert_eq!(small.min_size(Density::Mouse), Density::Mouse.min_target_size());
/// ```
/// Like [`StatefulWidget`], but for widgets whose state evolves with wall-clock time.
///
/// Covers state like [`crate::state::ScrollState`]'s momentum/rubber-band physics or a
/// [`Tween`](crate::animate::Tween)-driven transition, which advance on their own rather than
/// only in response to input.
///
/// [`StatefulWidget`] has no way to reach the [`Frame`] an [`App`](retroglyph_core::app::App) already
/// receives every frame, so a widget with time-based state has nowhere to advance it: not in
/// `render` (no `Frame` parameter), and not in a second, app-defined call, because nothing
/// enforces that call happening before `render` rather than after it: the two orders differ by
/// one frame of animation, silently. `AnimatedWidget` closes that gap with a single call that both
/// advances and draws, so the ordering question doesn't arise. See [`Scrollbar`]'s impl for a
/// worked example: it ticks [`crate::state::ScrollState`]'s physics forward by `frame.delta`, then draws
/// the thumb at the resulting offset, in one call.
///
/// A sibling of [`StatefulWidget`], not a replacement: a widget with no time-based state (a
/// selection index that only moves on a keypress, say) has no use for `frame` and should keep
/// implementing [`StatefulWidget`] instead. Nothing stops a widget from implementing both, the way
/// [`Scrollbar`] implements [`Widget`] (a plain, offset-at-a-fixed-value track+thumb) alongside
/// this trait (an animated one driven by [`crate::state::ScrollState`]).
///
/// # Examples
///
/// ```
/// use core::time::Duration;
/// use retroglyph_core::app::Frame;
/// use retroglyph_core::grid::{Grid, Rect};
/// use retroglyph_ui::widget::AnimatedWidget;
/// use retroglyph_ui::Surface;
///
/// struct Blinker;
///
/// impl AnimatedWidget for Blinker {
/// type State = Duration;
///
/// fn render(&self, surface: &mut Surface<'_>, state: &mut Self::State, frame: &Frame) {
/// *state += frame.delta;
/// let on = state.as_millis() / 500 % 2 == 0;
/// surface.put((0, 0), if on { '*' } else { ' ' }, retroglyph_core::color::Style::new());
/// }
/// }
///
/// let area = Rect::new(0, 0, 4, 1);
/// let mut grid = Grid::new(4, 1);
/// let mut state = Duration::ZERO;
/// let frame = Frame::new(Duration::from_millis(100), 0);
/// Blinker.render(&mut Surface::new(&mut grid, area, 0), &mut state, &frame);
/// assert_eq!(state, Duration::from_millis(100));
/// ```
/// A widget that renders itself styled by an already-resolved [`Response`], with the [`Sense`]
/// it needs fixed by the widget rather than chosen at the call site.
///
/// A composite widget like [`Button`], [`Scrollbar`], [`List`], or [`Tabs`] needs to know what
/// happened to it this frame (hovered, pressed, clicked, dragged) to pick its style and resolve
/// its own hit-testing, but never calls
/// [`Interaction::interact`](crate::interact::Interaction::interact) itself: doing so would let it register
/// the wrong rect, or let a call site register it with a [`Sense`] its presentation doesn't
/// match (a click handler drawn without ever showing a hover state, say). [`sense`](Self::sense)
/// fixes what the widget needs so a call site can't get that pairing wrong, and
/// [`render`](Self::render) takes the resulting [`Response`] as a plain argument rather than
/// calling `interact` itself: the widget never receives an
/// [`Interaction`](crate::interact::Interaction), and has no `Id` type parameter, so it can't call
/// `interact` with the wrong rect because it has nothing to call `interact` on.
///
/// `type State` covers widgets with no state ([`Button`], [`Tabs`]: `()`), a scroll position
/// ([`Scrollbar`]: [`ScrollState`](crate::state::ScrollState)), or a selection/scroll index ([`List`]:
/// [`ListState`](crate::state::ListState)), the same [`Widget`]/[`StatefulWidget`] split applied to
/// interactive widgets rather than a separate `InteractiveStatefulWidget` trait.
///
/// Has no generic method, so `dyn InteractiveWidget<Id, State = ()>` is object-safe,
/// e.g. a `Vec<Box<dyn InteractiveWidget<Id, State = ()>>>` of heterogeneous stateless widgets.
/// `Id` is the trait's own type parameter (not a generic method) precisely so that stays true.
///
/// # Examples
///
/// ```
/// use retroglyph_core::color::Style;
/// use retroglyph_core::grid::{Grid, Rect};
/// use retroglyph_ui::interact::{Response, Sense};
/// use retroglyph_ui::widget::InteractiveWidget;
/// use retroglyph_ui::Surface;
///
/// struct Marker(char);
///
/// impl<Id> InteractiveWidget<Id> for Marker {
/// type State = ();
///
/// fn sense(&self) -> Sense {
/// Sense::click()
/// }
///
/// fn render(&self, surface: &mut Surface<'_>, _state: &mut Self::State, response: Response<Id>) {
/// let style = if response.hovered() { Style::new().bg(retroglyph_core::color::Color::RED) } else { Style::new() };
/// surface.put((0, 0), self.0, style);
/// }
/// }
/// ```