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;