tessera_ui/lib.rs
1//! Tessera is a cross-platform declarative & functional UI library for rust,
2//! focused on performance and extensibility.
3//!
4//! # Guide
5//!
6//! We recommend reading the [Quick Start](https://tessera-ui.github.io/guide/getting-started.html) to learn how to use tessera.
7//!
8//! # Component Library
9//!
10//! The tessera-ui crate itself does not contain built-in components, but the official project provides a [basic component library](https://crates.io/crates/tessera-components).
11//!
12//! It contains commonly used UI components such as buttons, text boxes, labels,
13//! etc., which help developers quickly build user interfaces.
14//!
15//! # Components
16//!
17//! To define a component in tessera, write it like this:
18//!
19//! ```
20//! use tessera_ui::tessera;
21//!
22//! #[tessera]
23//! fn my_component() {
24//! // component implementation
25//! }
26//! ```
27//!
28//! Functions marked with the `#[tessera]` macro are tessera components.
29//!
30//! Component functions may contain other component functions, enabling nesting
31//! and composition.
32//!
33//! ```
34//! use tessera_ui::tessera;
35//!
36//! #[tessera]
37//! fn child() {
38//! // child component implementation
39//! }
40//!
41//! #[tessera]
42//! fn parent() {
43//! child();
44//! }
45//! ```
46//!
47//! # Memoized state
48//!
49//! Components in tessera are functions. To persist state across frames within a
50//! component, use the memoization API.
51//!
52//! There are two primary primitives for memoized state depending on lifetime:
53//! `remember` and `retain`. The following sections describe their behavior and
54//! usage.
55//!
56//! ## remember
57//!
58//! The `remember` and `remember_with_key` functions can be used to create
59//! persistent state across frames within a component.
60//!
61//! ```
62//! use tessera_ui::{remember, tessera};
63//!
64//! #[tessera]
65//! fn counter() {
66//! let count = remember(|| 0);
67//! count.with_mut(|c| *c += 1);
68//! }
69//! ```
70//!
71//! Memoized state is implemented via macro-based control-flow analysis and
72//! cannot be used outside of functions marked with `#[tessera]`. It also must
73//! not be used inside layout policies or event handler implementations.
74//!
75//! `remember` handles most control flow situations, but it cannot guarantee
76//! stable identity inside loops. If you need to use memoized state within a
77//! loop, use `remember_with_key` and provide a stable key.
78//!
79//! ```
80//! use tessera_ui::{tessera, remember_with_key};
81//!
82//! struct User {
83//! id: i32,
84//! name: String,
85//! }
86//!
87//! #[tessera]
88//! fn user_list() {
89//! let users = vec![
90//! User { id: 101, name: "Alice".to_string() },
91//! User { id: 205, name: "Bob".to_string() },
92//! User { id: 33, name: "Charlie".to_string() },
93//! ];
94//!
95//! for user in users.iter() {
96//! // Regardless of the user's position in the list, this `likes` state will follow the user.id
97//! let likes = remember_with_key(user.id, || 0);
98//!
99//! /* component implementation */
100//! }
101//! }
102//! ```
103//!
104//! Or use the `key` function to influence the `remember` calls inside it.
105//!
106//! ```
107//! use tessera_ui::{key, remember, tessera};
108//!
109//! #[tessera]
110//! fn my_list(items: Vec<String>) {
111//! for item in items.iter() {
112//! key(item.clone(), || {
113//! let state = remember(|| 0);
114//! });
115//! }
116//! }
117//! ```
118//!
119//! This is equivalent to using `remember_with_key(item.clone(), || 0)`, but it
120//! is transparent to child components and necessary for virtual container-like
121//! components.
122//!
123//! However, `remember_with_key` is a litte cheaper than `key` + `remember`, so
124//! prefer it in simple cases.
125//!
126//! ## retain
127//!
128//! `retain` just work as `remember` but is not dropped when a component becomes
129//! invisible; use it for state that should persist for the lifetime of the
130//! process (for example, scroll position).
131//!
132//! It has the same API and typical usage as `remember`, except that its value
133//! is retained across the entire lifetime of the process.
134//!
135//! ```
136//! use tessera_ui::{retain, tessera};
137//!
138//! #[tessera]
139//! fn scrollable_page(page_id: String) {
140//! // Scroll position persists even when navigating away and returning
141//! let scroll_offset = retain(|| 0.0f32);
142//! let _ = page_id;
143//!
144//! /* component implementation */
145//! }
146//! ```
147//!
148//! There is also a `key` variant for `retain`, called `retain_with_key`. Use it
149//! when you need to retain state in a loop or similar scenarios.
150//!
151//! ```
152//! use tessera_ui::{tessera, retain_with_key};
153//!
154//! struct User {
155//! id: i32,
156//! name: String,
157//! }
158//!
159//! #[tessera]
160//! fn user_list() {
161//! let users = vec![
162//! User { id: 101, name: "Alice".to_string() },
163//! User { id: 205, name: "Bob".to_string() },
164//! User { id: 33, name: "Charlie".to_string() },
165//! ];
166//!
167//! for user in users.iter() {
168//! // Regardless of the user's position in the list, this `likes` state will follow the user.id
169//! let likes = retain_with_key(user.id, || 0);
170//!
171//! /* component implementation */
172//! }
173//! }
174//! ```
175//!
176//! Or use the `key` function to influence the `retain` calls inside it.
177//!
178//! ```
179//! use tessera_ui::{key, retain, tessera};
180//!
181//! #[tessera]
182//! fn my_list(items: Vec<String>) {
183//! for item in items.iter() {
184//! key(item.clone(), || {
185//! let state = retain(|| 0);
186//! });
187//! }
188//! }
189//! ```
190//!
191//! # Context
192//!
193//! The context mechanism is used to pass data down the component tree, avoiding
194//! the need to thread it through parameters.
195//!
196//! ```
197//! use tessera_ui::{Color, provide_context, tessera, use_context};
198//!
199//! #[derive(Clone, PartialEq)]
200//! struct Theme {
201//! color: Color,
202//! }
203//!
204//! #[tessera]
205//! fn parent() {
206//! provide_context(
207//! || Theme { color: Color::RED },
208//! || {
209//! child();
210//! },
211//! );
212//! }
213//!
214//! #[tessera]
215//! fn child() {
216//! let theme = use_context::<Theme>().expect("Theme must be provided");
217//! theme.with(|t| assert_eq!(t.color, Color::RED));
218//! }
219//! ```
220//!
221//! A context corresponds to a type. In the component tree, a component will
222//! receive the nearest parent-provided context of the same type.
223//! ```
224//! use tessera_ui::{Color, provide_context, tessera, use_context};
225//!
226//! #[derive(Clone, PartialEq)]
227//! struct Theme {
228//! color: Color,
229//! }
230//!
231//! #[tessera]
232//! fn parent() {
233//! provide_context(
234//! || Theme { color: Color::RED },
235//! || {
236//! child();
237//! },
238//! );
239//! }
240//!
241//! #[tessera]
242//! fn child() {
243//! let theme = use_context::<Theme>().expect("Theme must be provided");
244//! theme.with(|t| assert_eq!(t.color, Color::RED));
245//! provide_context(
246//! || Theme {
247//! color: Color::GREEN,
248//! },
249//! || {
250//! grandchild();
251//! },
252//! );
253//! }
254//!
255//! #[tessera]
256//! fn grandchild() {
257//! let theme = use_context::<Theme>().expect("Theme must be provided");
258//! theme.with(|t| assert_eq!(t.color, Color::GREEN));
259//! }
260//! ```
261//!
262//! # Layout
263//!
264//! Tessera layout behavior is expressed through layout policies attached to the
265//! current node. Framework and component crates do this through internal
266//! layout primitives, while application code typically composes existing
267//! components and modifiers.
268//!
269//! For more details, see the [Layout Guide](https://tessera-ui.github.io/guide/component.html#layout).
270#![deny(missing_docs, clippy::unwrap_used)]
271
272extern crate self as tessera_ui;
273
274#[doc(hidden)]
275pub mod __private;
276pub mod accessibility;
277#[cfg(target_os = "android")]
278pub mod android;
279pub mod asset;
280mod build_tree;
281pub mod color;
282mod component_tree;
283pub mod context;
284mod cursor;
285pub mod dp;
286pub mod entry_point;
287pub mod entry_registry;
288mod execution_context;
289pub mod focus;
290mod ime_state;
291mod keyboard_state;
292pub mod layout;
293pub mod modifier;
294pub(crate) mod pipeline_cache;
295pub mod pipeline_context;
296pub mod plugin;
297#[cfg(feature = "profiling")]
298pub mod profiler;
299mod prop;
300pub mod px;
301mod render_graph;
302pub mod render_module;
303mod render_pass;
304pub mod render_scene;
305pub mod renderer;
306mod runtime;
307#[cfg(feature = "testing")]
308pub mod testing;
309mod thread_utils;
310pub mod time;
311
312#[cfg(feature = "shard")]
313pub mod router;
314
315pub use accesskit;
316pub use indextree::{Arena, NodeId};
317pub use tessera_macros::{entry, tessera};
318pub use wgpu;
319pub use winit;
320
321pub use crate::{
322 accessibility::{AccessibilityActionHandler, AccessibilityId, AccessibilityNode},
323 asset::AssetExt,
324 color::Color,
325 component_tree::{
326 ComponentTree, ComputedData, Constraint, DimensionValue, ImeInput, ImeInputHandlerFn,
327 ImeRequest, ImeSession, KeyboardInput, KeyboardInputHandlerFn, MeasurementError,
328 ParentConstraint, PointerEventPass, PointerInput, PointerInputHandlerFn, WindowAction,
329 },
330 context::{Context, provide_context, use_context},
331 cursor::{
332 CursorEvent, CursorEventContent, GestureState, MOUSE_POINTER_ID, PointerChange, PointerId,
333 PressKeyEventType, ScrollEventContent, ScrollEventSource,
334 },
335 dp::Dp,
336 entry_point::EntryPoint,
337 entry_registry::{EntryRegistry, TesseraPackage},
338 focus::{
339 FocusDirection, FocusGroupNode, FocusManager, FocusProperties, FocusRequester,
340 FocusScopeNode, FocusState, FocusTraversalPolicy, FocusTraversalStrategy,
341 },
342 layout::{
343 DefaultLayoutPolicy, LayoutInput, LayoutOutput, LayoutPolicy, LayoutResult,
344 NoopRenderPolicy, RenderInput, RenderMetadataMut, RenderPolicy,
345 },
346 modifier::{
347 BuildModifierNode, CursorModifierExt, CursorModifierNode, DrawModifierContent,
348 DrawModifierContext, DrawModifierNode, FocusModifierExt, ImeInputModifierNode,
349 KeyboardInputModifierNode, LayoutModifierChild, LayoutModifierInput, LayoutModifierNode,
350 LayoutModifierOutput, Modifier, ParentDataMap, ParentDataModifierNode,
351 PointerInputModifierNode, SemanticsModifierNode,
352 },
353 pipeline_context::PipelineContext,
354 plugin::{
355 Plugin, PluginContext, PluginResult, register_plugin, register_plugin_boxed, with_plugin,
356 with_plugin_mut,
357 },
358 prop::{Callback, CallbackWith, RenderSlot, RenderSlotWith, Slot},
359 px::{Px, PxPosition, PxRect, PxSize},
360 render_graph::{
361 ExternalTextureDesc, RenderFragment, RenderFragmentOp, RenderGraph, RenderGraphOp,
362 RenderGraphParts, RenderResource, RenderResourceId, RenderTextureDesc,
363 },
364 render_module::RenderModule,
365 render_scene::{Command, CompositeCommand, DrawRegion, PaddingRect, SampleRegion},
366 renderer::{
367 Renderer,
368 composite::{
369 self, CompositeBatchItem, CompositeContext, CompositeOutput, CompositePipeline,
370 CompositePipelineRegistry, CompositeReplacement,
371 },
372 compute::{
373 self, ComputablePipeline, ComputeCommand, ComputePipelineRegistry, ComputeResource,
374 ComputeResourceManager, ComputeResourceRef,
375 },
376 drawer::{self, DrawCommand, DrawablePipeline, PipelineRegistry, command},
377 external::{ExternalTextureHandle, ExternalTextureRegistry},
378 },
379 runtime::{
380 FrameNanosControl, State, current_frame_nanos, current_frame_time, frame_delta, key,
381 receive_frame_nanos, remember, remember_with_key, retain, retain_with_key,
382 },
383};
384
385use ime_state::ImeState;
386
387#[cfg(feature = "shard")]
388pub use tessera_macros::shard;
389#[cfg(feature = "shard")]
390pub use tessera_shard;
391
392#[cfg(target_os = "android")]
393pub use {jni, ndk_context, ndk_sys};
394
395/// Backward-compatible alias for the primary focus requester handle.
396pub type Focus = FocusRequester;