rhythm_gpui/lib.rs
1//! Vertical rhythm typography for [gpui](https://www.gpui.rs), ported from
2//! [rhythm-sass](https://github.com/p233/rhythm-sass).
3//!
4//! gpui's `WrappedLine` paint path follows the model in [`math`](crate::Rhythm):
5//! the `ascent + descent` box is centered in the line height and the baseline
6//! sits at `(line_height - ascent - descent) / 2 + ascent`.
7//! This crate resolves real font metrics through gpui's text system, so
8//! baseline-anchored text lands on the rhythm grid without the manually measured
9//! `baseline-ratio` that the original Sass library required. Cap-anchored helpers
10//! instead align the capitals' ink while preserving whole-row block geometry, and
11//! the ICF anchors do the same for the CJK ideographic character face.
12//!
13//! # Feature flags
14//!
15//! - **`gpui`** (default) — the gpui integration: `RhythmGrid`, `RhythmFont`
16//! (with `RhythmFontSpec` cache keys), measured `RhythmIcfAnchor`s,
17//! `RhythmDropCap`, the `RhythmStyled` extension trait, the `rhythm_frame`
18//! media container (with the `RhythmFit` pad/crop mode), and the configurable
19//! `rhythm_overlay` / `RhythmOverlay` debug grid.
20//! - Disable default features to build only the dependency-free rhythm math
21//! ([`Rhythm`], [`FontRhythm`], [`RhythmLineMetrics`], [`RhythmBlockMetrics`],
22//! [`DropCapRhythm`], [`snap`]), usable from any renderer that centers
23//! `ascent + descent` inside the line height:
24//!
25//! ```toml
26//! rhythm-gpui = { version = "0.2", default-features = false }
27//! ```
28//!
29//! # Example
30//!
31//! ```no_run
32//! # #[cfg(feature = "gpui")]
33//! use gpui::{div, font, px, prelude::*};
34//! # #[cfg(feature = "gpui")]
35//! use rhythm_gpui::{RhythmGrid, RhythmStyled};
36//!
37//! # #[cfg(feature = "gpui")]
38//! fn body(text_system: &gpui::TextSystem) -> impl IntoElement {
39//! let grid = RhythmGrid::new(px(8.));
40//! let para = grid.font(text_system, font("Georgia"), px(16.), 3);
41//! div()
42//! .rhythm_block(¶, 3, 1)
43//! .child("Aligned to the grid.")
44//! }
45//! ```
46//!
47//! The repository's `recipes` example doubles as a recipe collection: a page
48//! scaffold, baseline- and cap-anchored openings, a drop cap with true
49//! wrap-around, fluid-width media padded or cropped to whole rhythm rows, and
50//! mixed fonts (sizes, families, scripts) sharing one alphabetic baseline.
51//!
52//! # Custom renderers
53//!
54//! Document renderers that shape text themselves and paint cached
55//! `WrappedLine`s skip the element layer entirely: build [`RhythmLineMetrics`]
56//! from each shaped line's reported `ascent()` / `descent()` — the maxima over
57//! its explicit font runs, which is how lines mixing bold, inline code, CJK, or
58//! emoji faces actually shape. Those are line metrics, not a guarantee that
59//! platform-selected fallback glyph ink stays inside the box. Lay blocks out
60//! with [`RhythmBlockMetrics`] and place every baseline with
61//! `paint_origin_for`. Under the `gpui` feature, the four values that stay
62//! inside a paint path's `Pixels` chain have `Pixels`-typed `_px` mirrors. The
63//! `direct_paint` example is the complete non-virtualized shape/cache/paint
64//! recipe. A virtualizer additionally accumulates `first_rows` /
65//! `middle_rows` / `last_rows` in an `i64` and rebases that row cursor near
66//! the viewport before converting visible positions to `f32`.
67//!
68//! # Performance contract
69//!
70//! - [`Rhythm`], [`FontRhythm`], and line/block geometry are small `Copy`
71//! values. Their pure geometry and spacing methods are O(1), allocation-free
72//! (enforced by a counting-allocator test), and lock-free by construction,
73//! with hot methods `#[inline]` across the crate boundary.
74//! - gpui `TextSystem` access is confined to font/spec/drop-cap resolution and
75//! optional ICF measurement; geometry and spacing on stored values never
76//! query it.
77//! - The shaped-line adapter reads a line's already-computed ascent/descent
78//! and never walks glyphs.
79//! - `cargo bench --bench resolve` tracks warm font resolution (gpui's request
80//! cache hit plus metric reads). There is deliberately no ns-level CI
81//! threshold: pure `f32` math varies below measurement noise, so the
82//! guarantees above are structural, not benchmarked.
83//!
84//! # Platform scope
85//!
86//! The math layer is renderer- and platform-agnostic, and the resolved-font
87//! path uses cross-platform gpui API. The default integration is compile-checked
88//! on Linux and Windows. The mixed-run maximum and glyph-fallback behaviors are
89//! verified against macOS/CoreText by the `shaping` integration suite; the
90//! DirectWrite and cosmic-text runtime behaviors are not yet verified and must
91//! not be assumed identical.
92
93#![cfg_attr(docsrs, feature(doc_cfg))]
94#![warn(missing_docs)]
95
96mod math;
97mod metrics;
98
99pub use math::{snap, DropCapRhythm, FontRhythm, Rhythm};
100pub use metrics::{RhythmBlockMetrics, RhythmLineMetrics};
101
102#[cfg(feature = "gpui")]
103mod frame;
104#[cfg(feature = "gpui")]
105mod integration;
106
107#[cfg(feature = "gpui")]
108#[cfg_attr(docsrs, doc(cfg(feature = "gpui")))]
109pub use frame::{rhythm_frame, RhythmFit, RhythmFrame};
110#[cfg(feature = "gpui")]
111#[cfg_attr(docsrs, doc(cfg(feature = "gpui")))]
112pub use integration::{
113 rhythm_overlay, IcfMeasurementError, RhythmDropCap, RhythmFont, RhythmFontSpec, RhythmGrid,
114 RhythmIcfAnchor, RhythmOverlay, RhythmStyled,
115};