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(
271 missing_docs,
272 clippy::unwrap_used,
273 rustdoc::broken_intra_doc_links,
274 rustdoc::invalid_rust_codeblocks,
275 rustdoc::invalid_html_tags
276)]
277
278extern crate self as tessera_ui;
279
280#[doc(hidden)]
281pub mod __private;
282pub mod accessibility;
283#[cfg(target_os = "android")]
284pub mod android;
285pub mod asset;
286mod build_tree;
287pub mod color;
288mod component_tree;
289pub mod context;
290mod cursor;
291pub mod dp;
292pub mod entry_point;
293pub mod entry_registry;
294mod execution_context;
295pub mod focus;
296mod ime_state;
297mod keyboard_state;
298pub mod layout;
299pub mod modifier;
300pub(crate) mod pipeline_cache;
301pub mod pipeline_context;
302pub mod plugin;
303#[cfg(feature = "profiling")]
304pub mod profiler;
305mod prop;
306pub mod px;
307mod render_graph;
308pub mod render_module;
309mod render_pass;
310pub mod render_scene;
311pub mod renderer;
312mod runtime;
313pub mod scroll;
314#[cfg(feature = "testing")]
315pub mod testing;
316mod thread_utils;
317pub mod time;
318
319pub use accesskit;
320pub use indextree::{Arena, NodeId};
321pub use tessera_macros::{entry, tessera};
322pub use wgpu;
323pub use winit;
324
325pub use crate::{
326 accessibility::{AccessibilityActionHandler, AccessibilityId, AccessibilityNode},
327 asset::AssetExt,
328 color::Color,
329 component_tree::{
330 AxisConstraint, ComponentTree, ComputedData, Constraint, ImeInput, ImeInputHandlerFn,
331 ImeRequest, ImeSession, KeyboardInput, KeyboardInputHandlerFn, MeasurementError,
332 ParentConstraint, PointerEventPass, PointerInput, PointerInputHandlerFn,
333 },
334 context::{Context, provide_context, use_context},
335 cursor::{
336 CursorEventContent, GestureState, MOUSE_POINTER_ID, PointerChange, PointerId,
337 PressKeyEventType, ScrollDeltaUnit, ScrollEventContent, ScrollEventSource,
338 },
339 dp::Dp,
340 entry_point::EntryPoint,
341 entry_registry::{EntryRegistry, TesseraPackage},
342 focus::{
343 FocusDirection, FocusGroupNode, FocusManager, FocusProperties, FocusRequester,
344 FocusScopeNode, FocusState, FocusTraversalPolicy, FocusTraversalStrategy,
345 },
346 layout::{
347 DefaultLayoutPolicy, LayoutPolicy, LayoutResult, NoopRenderPolicy, RenderInput,
348 RenderMetadataMut, RenderPolicy,
349 },
350 modifier::{
351 BuildModifierNode, CursorModifierExt, CursorModifierNode, DrawModifierContent,
352 DrawModifierContext, DrawModifierNode, FocusModifierExt, ImeInputModifierNode,
353 KeyboardInputModifierNode, LayoutModifierChild, LayoutModifierInput, LayoutModifierNode,
354 LayoutModifierOutput, Modifier, ParentDataMap, ParentDataModifierNode,
355 PlacementModifierNode, PointerInputModifierNode, SemanticsModifierNode,
356 },
357 pipeline_context::PipelineContext,
358 plugin::{
359 DesktopPlatformContext, DesktopWindowAction, Plugin, PluginContext, PluginResult,
360 register_plugin, register_plugin_boxed, with_plugin, with_plugin_mut,
361 },
362 prop::{Callback, CallbackWith, RenderSlot, RenderSlotWith, Slot},
363 px::{Px, PxPosition, PxRect, PxSize},
364 render_graph::{
365 ExternalTextureDesc, RenderFragment, RenderFragmentOp, RenderGraph, RenderGraphOp,
366 RenderGraphParts, RenderResource, RenderResourceId, RenderTextureDesc,
367 },
368 render_module::RenderModule,
369 render_scene::{Command, CompositeCommand, DrawRegion, PaddingRect, SampleRegion},
370 renderer::{
371 Renderer,
372 composite::{
373 self, CompositeBatchItem, CompositeContext, CompositeOutput, CompositePipeline,
374 CompositePipelineRegistry, CompositeReplacement,
375 },
376 compute::{
377 self, ComputablePipeline, ComputeCommand, ComputePipelineRegistry, ComputeResource,
378 ComputeResourceManager, ComputeResourceRef,
379 },
380 drawer::{self, DrawCommand, DrawablePipeline, PipelineRegistry, command},
381 external::{ExternalTextureHandle, ExternalTextureRegistry},
382 },
383 runtime::{
384 FrameNanosControl, State, current_frame_nanos, current_frame_time, frame_delta, key,
385 receive_frame_nanos, remember, remember_with_key, retain, retain_with_key,
386 },
387 scroll::{PlatformScrollConfig, normalize_platform_scroll_delta, platform_scroll_config},
388};
389
390use ime_state::ImeState;
391
392#[cfg(target_os = "android")]
393pub use {jni, ndk_context, ndk_sys};