Skip to main content

retroglyph_ui/
lib.rs

1//! Immediate-mode drawing helpers over a [`Rect`](retroglyph_core::grid::Rect).
2//!
3//! Box borders, filled panels, gauges, tables, sparklines, and a small
4//! constraint-based [`Rect`](retroglyph_core::grid::Rect) splitter with
5//! ratatui-style `Fixed`/`Percent`/`Fill`/`Min`/`Max` constraints and `Flex`
6//! alignment ([`layout`]).
7//!
8//! Every widget ([`widget`]) is a builder struct that draws itself into a
9//! [`Grid`](retroglyph_core::grid::Grid) via [`Widget`]/[`StatefulWidget`] and
10//! retains no state of its own: state that outlives one render call (a
11//! selection index, a scroll offset, a text field's value and cursor)
12//! lives in [`ListState`]/[`TextInputState`] instead. A
13//! handful of things that are genuinely just functions ([`fill_rect`],
14//! [`thumb_geometry`]/[`offset_for_pos`] in [`draw`]; [`truncate`]/[`truncate_owned`] in
15//! [`text`]) stay free functions rather than pretending to be widgets. Three more independent
16//! layers build on top:
17//!
18//! - [`Widget`]/[`StatefulWidget`] ([`widget`]) render into a [`Surface`],
19//!   an area-relative, single-layer view over a [`Grid`](retroglyph_core::grid::Grid), and let
20//!   callers box or store heterogeneous widgets, e.g. a `Vec<Box<dyn Widget>>` of panes to
21//!   render each frame, with no `Backend` type parameter, since drawing touches nothing but
22//!   cells. [`AnimatedWidget`] is `StatefulWidget`'s sibling for state that evolves with
23//!   wall-clock time (e.g. [`ScrollState`]'s momentum physics) instead of only in response to
24//!   input; see its own docs. [`InteractiveWidget`] is the sibling trait for widgets that also
25//!   read a [`Response`] (`Button`, `Scrollbar`, `List`, `Tabs`).
26//! - [`Interaction`] ([`interact`]) for hover/click/drag/focus tracking
27//!   without a retained widget tree: the sibling of [`ListState`] for
28//!   widgets that don't have a natural selection index of their own. [`Ui`] pairs one frame's
29//!   [`Surface`] with an `Interaction`, via [`Interaction::frame`]: [`Ui::show`] is how an
30//!   [`InteractiveWidget`] gets hit-tested and drawn from the one area/id a call site names.
31//! - [`BoxStyle`] ([`style`]) for a Lip-Gloss-style box model (padding,
32//!   border, margin) rendered into a standalone `Grid`.
33//! - [`join_h`]/[`join_v`] ([`block`]) to compose several `Grid`s (e.g. `BoxStyle::render`
34//!   output) into one; `retroglyph_core::surface::Surface::blit` stamps the result onto a surface.
35//! - [`Theme`] ([`theme`]) for named color roles (an app picks
36//!   [`Theme::DARK`]/[`Theme::LIGHT`], or builds its own), independent of
37//!   how the app decides which one is active.
38//!
39//! This crate is itself optional: games that draw manually depend only on
40//! `retroglyph-core`.
41//!
42//! This crate is `no_std`-compatible: disable the `std` feature and enable `libm` instead (also
43//! requires an allocator). See the `std` and `libm` features below.
44//!
45//! # Features
46//!
47//! <!-- gen-features:start -->
48//! Default features: `std`.
49//!
50//! ### `dev`
51//!
52//! ⚪ Optional.
53//!
54//! Forwards `retroglyph-core`'s `dev` feature, which forces development diagnostics on in a build
55//! that would otherwise compile them out (see [`retroglyph_core::dev`]).
56//!
57//! ### `egc`
58//!
59//! ⚪ Optional.
60//!
61//! Forwards to `retroglyph-core`'s `egc` feature.
62//!
63//! Upgrades [`Paragraph`]'s word-wrap (always available) to grapheme-cluster-aware correctness.
64//!
65//! ### `libm`
66//!
67//! ⚪ Optional.
68//!
69//! Uses `retroglyph-core`'s `libm` feature (the `no_std` float backend: easing curves and
70//! tweens, scrollbar geometry, gauge/sparkline/bar percentage rounding, scroll momentum decay)
71//! instead of `std`'s own float intrinsics. See `std` below; a build needs exactly one of the two.
72//!
73//! ### `serde`
74//!
75//! ⚪ Optional.
76//!
77//! Adds `Serialize`/`Deserialize` impls for [`Theme`] and `Density`, forwarding to
78//! `retroglyph-core`'s `serde` feature.
79//!
80//! [`Theme`] round-trips through `Color`'s own `serde` impl.
81//!
82//! ### `std`
83//!
84//! 🟢 Enabled by default.
85//!
86//! Enables `retroglyph-core/std`, whose float intrinsics back this crate's own float use (see
87//! `libm` above for the `no_std` alternative).
88//!
89//! Disabling this feature (`--no-default-features`) builds this crate `no_std` (requires an
90//! allocator and one of `std`/`libm`; see the crate-level `compile_error!` in `src/lib.rs`).
91//! <!-- gen-features:end -->
92
93#![cfg_attr(not(feature = "std"), no_std)]
94#![cfg_attr(docsrs, feature(doc_cfg))]
95extern crate alloc;
96
97// This crate's float use -- easing curves and tweens, scrollbar geometry, gauge/sparkline/bar
98// percentage rounding, scroll momentum decay -- is load-bearing across the widget set, not an
99// opt-in extra. There is no equivalent of "the affected module just isn't compiled in", so a
100// build with neither backend fails loudly here instead of hitting `retroglyph_core::math`'s own
101// unresolved-`libm` error deep in a call site.
102#[cfg(not(any(feature = "std", feature = "libm")))]
103compile_error!("retroglyph-ui needs a float backend: enable `std` or `libm`.");
104
105// Compile the code blocks in this crate's own README as doctests so its quick start is
106// type-checked on every test run and cannot silently rot. The `cfg(doctest)` gate keeps this out
107// of the rendered crate documentation: see `retroglyph-crossterm`'s matching include for the
108// same pattern applied to the workspace root README.
109#[cfg(doctest)]
110#[doc = include_str!("../README.md")]
111struct ReadmeDoctests;
112
113pub mod align;
114/// Time-driven value animation: easing curves, a stateful `Tween`, and a periodic oscillator.
115///
116/// The trig-based easing curves and the oscillator's sine wave go through `retroglyph_core`'s
117/// `math` shim, so they use `std`'s float intrinsics or `libm`'s software implementation
118/// depending on which backend feature is on.
119pub mod animate;
120pub mod block;
121/// A scrolling viewport into a world larger than the screen.
122pub mod camera;
123pub mod draw;
124pub mod interact;
125pub mod layout;
126/// A live frame-time/FPS overlay: [`PerfOverlayApp`] wraps any `App` with one, on any `Backend`.
127pub mod perf;
128pub mod state;
129pub mod style;
130pub mod text;
131pub mod theme;
132pub mod ui;
133pub mod widget;
134
135pub use align::Align;
136pub use animate::{Easing, Tween, oscillate, oscillate_with_phase};
137pub use block::{join_h, join_v};
138pub use camera::Camera;
139pub use draw::{fill_rect, offset_for_pos, thumb_geometry};
140pub use interact::{
141    Consumed, DEFAULT_DRAG_THRESHOLD, Density, FocusRing, HitTester, Interaction, Pointer,
142    Response, Sense, Shortcuts,
143};
144pub use layout::{
145    Constraint, Flex, Side, Spacing, anchored_rect, centered_rect, split_h, split_h_flex,
146    split_h_n, split_h_n_flex, split_h_n_spaced, split_h_spaced, split_v, split_v_flex, split_v_n,
147    split_v_n_flex, split_v_n_spaced, split_v_spaced,
148};
149pub use perf::{
150    DEFAULT_LAYER, DefaultPerfRenderer, FRAME_HISTORY, PerfOverlayApp, PerfOverlayMode,
151    PerfRenderer, default_is_toggle_key,
152};
153pub use retroglyph_core::surface::{Layer, StyledSurface, Surface};
154pub use state::{ListState, ScrollPhysics, ScrollState, SelectionWrap, TextInputState};
155pub use style::{BoxStyle, Sides};
156pub use text::{draw_clipped, truncate, truncate_owned};
157pub use theme::Theme;
158pub use ui::Ui;
159pub use widget::{
160    AnimatedPerfOverlay, AnimatedWidget, BorderType, BoxBorder, Button, Gauge, HighlightSpacing,
161    InteractiveWidget, List, ListDirection, Log, Measure, Meter, Modal, Panel, PanelTitle,
162    Paragraph, PerfOverlay, PrintLine, ProgressBar, Scrollbar, Sparkline, StatBar, StatefulWidget,
163    Table, Tabs, Text, TextInput, TitlePosition, Widget,
164};