Skip to main content

cranpose_ui/
lib.rs

1#![doc = include_str!("../README.md")]
2
3extern crate self as cranpose_ui;
4
5use std::{
6    ops::{Deref, DerefMut},
7    rc::Rc,
8};
9
10use cranpose_core::{ApplierGuard, MemoryApplier, NodeError, NodeId, RuntimeHandle, location_key};
11pub use cranpose_core::{Composition, Key};
12pub use cranpose_macros::composable;
13#[cfg(feature = "localization")]
14pub use cranpose_macros::{message, tr, translation_messages, translations};
15#[cfg(feature = "localization")]
16pub use localization::{
17    Catalog, Language, LanguagePreference, Locale, LocalizedText, PreviewMode, ProvideLocalization,
18    ProvideTranslator,
19};
20pub use text::SharedText;
21#[cfg(feature = "localization")]
22mod ui_text;
23#[cfg(feature = "localization")]
24pub use ui_text::UiText;
25#[cfg(feature = "localization")]
26mod language_preferences;
27#[cfg(feature = "localization")]
28pub use language_preferences::{
29    LocalizationController, ProvideLanguagePreferences, local_localization,
30};
31pub use ui_strings::UiString;
32
33pub mod announce;
34pub mod bring_into_view;
35pub mod clipboard_session;
36mod cursor_animation;
37mod debug;
38pub mod density;
39pub mod draggable;
40mod draw;
41mod environment_locals;
42pub mod fling_animation;
43mod focus_dispatch;
44pub mod focus_manager;
45mod focus_navigation;
46pub mod focus_order;
47pub mod font_scale;
48mod interaction;
49mod key_event;
50pub mod layout;
51pub mod layout_direction;
52pub mod lazy_item;
53mod lazy_prefetch;
54#[cfg(feature = "localization")]
55pub mod localization;
56pub mod modal;
57mod modal_nodes;
58mod modifier;
59mod modifier_nodes;
60pub mod mouse_input;
61pub mod nine_patch;
62mod pointer_dispatch;
63pub mod pointer_icon_session;
64mod primitives;
65mod render_state;
66mod renderer;
67pub mod round_scaling_list;
68pub mod round_scroll_indicator;
69pub mod safe_area;
70pub mod scroll;
71pub mod scrollbar;
72pub mod selection_container;
73mod semantics_dispatch;
74mod semantics_layout_log;
75mod subcompose_layout;
76pub mod text;
77mod text_field_decorator_node;
78pub mod text_field_focus;
79mod text_field_handler;
80mod text_field_input;
81mod text_field_modifier_node;
82pub mod text_input_session;
83pub mod text_layout_result;
84mod text_modifier_node;
85pub mod text_selection;
86mod thread_state;
87mod ui_strings;
88mod unhandled_keys;
89pub mod widgets;
90mod word_boundaries;
91pub mod zoom;
92
93#[doc(hidden)]
94pub use announce::{Announcement, Announcer, announce, drain_announcements, local_announcer};
95pub use bring_into_view::{
96    BringIntoViewResponder, local_bring_into_view_responder, scroll_delta_to_reveal,
97};
98pub use cranpose_foundation::{
99    CanvasSemanticsNode, CollectionInfo, DelegatableNode, LiveRegionMode, ModifierNode,
100    ModifierNodeElement, NodeCapabilities, NodeState, ProgressBarRangeInfo, ScrollAxisRange,
101    SemanticsConfiguration, SemanticsCustomAction, SemanticsDismiss, SemanticsExpand,
102    SemanticsLongClick, SemanticsMagicTap, SemanticsScrollBy, SemanticsScrollToIndex,
103    SemanticsSetProgress, SemanticsSetSelection, SemanticsSetText, SemanticsSpec,
104    SemanticsWidgetRole,
105    lazy::{
106        LazyItems, LazyListItemInfo, LazyListLayoutInfo, LazyListScope, LazyListState,
107        rememberLazyListState, rememberLazyListStateWithPosition,
108    },
109    text::{TextFieldBuffer, TextFieldLineLimits, TextFieldState, TextFieldValue, TextRange},
110};
111pub use cranpose_ui_graphics::{
112    BlurredEdgeTreatment, ColorFilter, CursorIcon, CustomPointerIcon, DashPathEffect, Dp,
113    DrawScope, DrawStyle, ExternalTexture, ImageBitmap, ImageBitmapError, ImagePixelFormat,
114    ImageSampling, Path, PointerIcon, PointerIconError, Stroke, StrokeCap, StrokeJoin,
115    WindowCoordinates,
116};
117pub use cranpose_ui_layout::{AlignmentLines, IntrinsicSize, Measurement};
118pub use cursor_animation::{
119    is_cursor_visible, next_cursor_blink_time, reset_cursor_blink, start_cursor_blink,
120    stop_cursor_blink, tick_cursor_blink,
121};
122pub use debug::{
123    ModifierChainTraceGuard, format_layout_tree, format_modifier_chain, format_render_scene,
124    format_screen_summary, install_modifier_chain_trace, log_layout_tree, log_modifier_chain,
125    log_render_scene, log_screen_summary,
126};
127pub use density::{Density, DensityMeasureScope, density, local_density};
128pub use draggable::{
129    DragDeltaHandler, DraggableState, ScrollableState, rememberDraggableState,
130    rememberScrollableState,
131};
132pub use draw::{
133    DrawCacheBuilder, DrawCommand, DrawCommandFn, command_draw_scope, command_draw_scope_reusing,
134    execute_draw_commands,
135};
136pub use focus_dispatch::{
137    active_focus_target, clear_focus_invalidations, has_pending_focus_invalidations,
138    keyboard_focus_visible, process_focus_invalidations, schedule_focus_invalidation,
139    set_active_focus_target, set_keyboard_focus_visible,
140};
141pub use focus_manager::{FocusManager, local_focus_manager, request_focus_from_platform};
142pub use focus_navigation::{KeyboardFocusTarget, keyboard_focus_target};
143pub use focus_order::{
144    FocusEntry, collect_focus_order, collect_focus_order_under, focus_order_len,
145    selectable_group_of, set_focus_order,
146};
147pub use font_scale::{FontScaleCurve, MAX_FONT_SCALE_KNOTS};
148pub use interaction::{
149    HoverInteraction, HoverInteractionEnter, HoverInteractionExit, Interaction,
150    MutableInteractionSource, PressInteraction, PressInteractionCancel, PressInteractionPress,
151    PressInteractionRelease, collect_is_hovered_as_state, collect_is_pressed_as_state,
152    rememberMutableInteractionSource,
153};
154pub use key_event::{KeyCode, KeyEvent, KeyEventType, Modifiers};
155pub use layout::{
156    LayoutAllocationDebugStats, LayoutBox, LayoutEngine, LayoutMeasurements, LayoutNodeData,
157    LayoutNodeKind, LayoutTree, MeasureLayoutOptions, SemanticsAction, SemanticsCallback,
158    SemanticsDetails, SemanticsNode, SemanticsPlacement, SemanticsRole, SemanticsText,
159    SemanticsTree, build_layout_tree_from_applier, build_semantics_tree_from_applier,
160    build_semantics_tree_from_layout_tree,
161    core::{
162        Alignment, Arrangement, HorizontalAlignment, LinearArrangement, Measurable, Placeable,
163        VerticalAlignment,
164    },
165    has_placed_content, measure_layout, measure_layout_with_options, placed_content_extent,
166    top_modal_from_applier, tree_needs_layout, tree_needs_semantics,
167    update_semantics_tree_from_applier,
168};
169pub use layout_direction::{
170    LayoutDirection, ProvideLayoutDirection, layout_direction, local_layout_direction,
171};
172pub use lazy_item::{ProvideLazyItemKey, lazy_item_key, local_lazy_item_key};
173pub use lazy_prefetch::{
174    drain_lazy_prefetch_requests, has_lazy_prefetch_requests, lazy_prefetch_pass_cost,
175    set_idle_prefetch_passes, warm_prefetched_slices, with_lazy_prefetch_pass,
176};
177pub use modal::{
178    ModalRegistration, clear_modals, dispatch_modal_back, local_modal_depth, modal_depth,
179};
180pub use modal_nodes::modal_node_may_be_live;
181pub use modifier::{
182    BlendMode, Brush, Color, CompositingStrategy, CornerRadii, DpOffset, DragAndDropEvent,
183    DragAndDropOutcome, DragAndDropPayload, DragAndDropPoint, DragAndDropSource,
184    DragAndDropSourceElement, DragAndDropSourceNode, DragAndDropState, DragAndDropTarget,
185    DragAndDropTargetElement, DragAndDropTargetNode, EdgeInsets, FocusDirection, FocusRequestError,
186    FocusRequester, GlassMaterial, GraphicsLayer, KeyEventRoute, LayerShape, Modifier,
187    ModifierLocalKey, ModifierLocalReadScope, ModifierNodeSlices, ModifierNodeSlicesDebugStats,
188    Point, PointerEvent, PointerEventKind, PointerInputScope, PointerSource, Rect, RenderEffect,
189    ResolvedBackground, ResolvedModifiers, RotaryInputModifierNode, RotaryScrollEvent,
190    RoundedCornerShape, RuntimeShader, SemanticsRequester, Shadow, ShadowScope, Size, SliceTextRef,
191    TransformOrigin, WindowRootDescriptor, WindowRootElement, WindowRootEntry, WindowRootNode,
192    WindowRootRegistry, WindowRootRoutingScratch, collect_modifier_slices,
193    collect_semantics_from_modifier, collect_slices_from_modifier, is_window_root,
194    nearest_window_root, nearest_window_roots, nearest_window_roots_into, window_roots,
195    window_roots_revision,
196};
197#[cfg(feature = "test-helpers")]
198pub use modifier::{last_fling_velocity, reset_last_fling_velocity};
199pub use modifier_nodes::{
200    AlphaElement, AlphaNode, BackgroundElement, BackgroundNode, ClickableElement, ClickableNode,
201    CornerShapeElement, CornerShapeNode, FillDirection, FillElement, FillNode,
202    FractionalOffsetElement, FractionalOffsetNode, OffsetElement, OffsetNode, PaddingElement,
203    PaddingNode, SizeElement, SizeNode,
204};
205pub use nine_patch::{
206    NinePatchInsets, PatchFill, PatchQuad, nine_patch_quads, tile_count, tile_quads,
207};
208#[doc(hidden)]
209pub use pointer_dispatch::{
210    clear_pointer_repasses, has_pending_pointer_repasses, process_pointer_repasses,
211    schedule_pointer_repass,
212};
213pub use primitives::{
214    AnimatedVisibility, BasicText, BasicTextField, BasicTextFieldDecorated,
215    BasicTextFieldDecorationScope, BasicTextFieldOptions, BasicTextFieldWithOptions,
216    BasicTextWithOptions, BitmapPainter, BitmapRegionPainter, Box, BoxScope, BoxSpec,
217    BoxWithConstraints, BoxWithConstraintsScope, BoxWithConstraintsScopeImpl, Button, ButtonSpec,
218    Canvas, Column, ColumnSpec, ContentScale, Crossfade, DEFAULT_ALPHA, EnterTransition,
219    ExitTransition, ForEach, Image, Layout, LayoutNode, Painter, Row, RowSpec, Spacer,
220    SubcomposeLayout, SvgPainter, SvgPainterError, Text, TextWithOptions, fade_in, fade_out,
221    rememberSvg, slide_in_vertically, slide_out_vertically,
222};
223#[cfg(any(test, feature = "test-helpers"))]
224#[doc(hidden)]
225pub use render_state::reset_render_state_for_tests;
226pub use render_state::{
227    AppContext, AppContextScope, MAX_FONT_SCALE, MIN_FONT_SCALE, current_density,
228    current_font_scale, current_font_scale_curve, scale_sp, set_density, set_font_scale,
229    set_font_scale_curve,
230};
231#[doc(hidden)]
232pub use render_state::{
233    GeometrySceneNodes, clear_transient_scroll_motion_contexts, debug_last_fling_velocity,
234    debug_reset_last_fling_velocity, has_current_app_context, has_pending_draw_repasses,
235    has_pending_layer_property_repasses, has_pending_layout_repasses, has_pending_measure_repasses,
236    peek_focus_invalidation, peek_layout_invalidation, peek_pointer_invalidation,
237    peek_render_invalidation, pending_layout_repass_nodes_snapshot,
238    pending_measure_repass_nodes_snapshot, prune_draw_observations_to_nodes,
239    request_current_draw_redraw, request_focus_invalidation, request_layout_invalidation,
240    request_pointer_invalidation, request_render_invalidation, schedule_draw_repass,
241    schedule_layout_repass, schedule_measure_repass, take_draw_repass_nodes,
242    take_draw_repass_nodes_into, take_focus_invalidation, take_geometry_scene_nodes,
243    take_geometry_scene_nodes_into, take_layer_property_repass_nodes_into,
244    take_layout_invalidation, take_layout_repass_nodes, take_measure_repass_nodes,
245    take_pointer_invalidation, take_render_invalidation,
246};
247pub use renderer::{HeadlessRenderer, PaintLayer, RecordedRenderScene, RenderOp};
248pub use safe_area::{WindowInsets, local_ime_insets, local_safe_area_insets, window_insets};
249pub use scroll::{ScrollElement, ScrollMetrics, ScrollNode, ScrollSettlePolicy, ScrollState};
250pub use scrollbar::{ThumbBounds, ThumbGeometry, content_delta_for_thumb_drag, thumb_geometry};
251#[doc(hidden)]
252pub use semantics_dispatch::{
253    clear_semantics_invalidations, has_pending_semantics_invalidations,
254    process_semantics_invalidations, schedule_semantics_invalidation,
255};
256pub use subcompose_layout::{
257    Constraints, MeasureResult, Placement, SubcomposeLayoutNode, SubcomposeLayoutScope,
258    SubcomposeMeasureScope, SubcomposeMeasureScopeImpl, clean_slot_skip_count,
259};
260pub use text::{
261    LinkAnnotation, ParagraphStyle, PlatformParagraphStyle, PlatformSpanStyle, PlatformTextStyle,
262    PreparedTextLayout, SpanStyle, StringAnnotation, TextDrawStyle, TextLayoutOptions,
263    TextLayoutResult, TextLinePrefixWidths, TextMeasurer, TextMetrics, TextOptions, TextOverflow,
264    TextShaping, TextStyle, get_cursor_x_for_offset, get_offset_for_position, layout_text,
265    measure_text, measure_text_for_node, measure_text_with_options,
266    measure_text_with_options_for_node, prepare_text_layout, prepare_text_layout_for_node,
267    set_text_measurer,
268};
269pub use text_field_focus::{ImeEditorState, has_focused_field};
270pub use text_field_modifier_node::{TextFieldElement, TextFieldModifierNode, TextPanResolver};
271pub use text_input_session::PlatformTextInputHandler;
272pub use text_modifier_node::{TextModifierElement, TextModifierNode};
273pub use unhandled_keys::{UnhandledKeyEvents, dispatch_unhandled_key_event};
274pub use widgets::{
275    clickable_text::ClickableText,
276    lazy_list::{LazyColumn, LazyColumnSpec, LazyRow, LazyRowSpec},
277    linked_text::LinkedText,
278    padding_values::PaddingValues,
279    popup::{dismiss_top_popup, dismissable_popup_open},
280    slider::{Slider, SliderOrientation, SliderScope, SliderSpec},
281    swipe_to_dismiss::{
282        SwipeDismissDirection, SwipeDismissSide, SwipeDismissState, SwipeToDismiss,
283        SwipeToDismissBox, SwipeToDismissSpec, rememberSwipeDismissState,
284    },
285    text_selection_menu::local_on_light_surface,
286};
287pub use zoom::ZoomState;
288
289/// In-memory composition helper used by tests.
290pub struct TestComposition {
291    _scope: render_state::AppContextScope,
292    app_context: Rc<AppContext>,
293    composition: Composition<MemoryApplier>,
294}
295
296impl TestComposition {
297    pub fn root(&self) -> Option<NodeId> {
298        self.app_context.enter(|| self.composition.root())
299    }
300
301    pub fn runtime_handle(&self) -> RuntimeHandle {
302        self.app_context.enter(|| self.composition.runtime_handle())
303    }
304
305    pub fn should_render(&self) -> bool {
306        self.app_context.enter(|| self.composition.should_render())
307    }
308
309    pub fn take_root_render_request(&mut self) -> bool {
310        let app_context = Rc::clone(&self.app_context);
311        app_context.enter(|| self.composition.take_root_render_request())
312    }
313
314    pub fn flush_pending_node_updates(&mut self) -> Result<(), NodeError> {
315        let app_context = Rc::clone(&self.app_context);
316        app_context.enter(|| self.composition.flush_pending_node_updates())
317    }
318
319    pub fn process_invalid_scopes(&mut self) -> Result<bool, NodeError> {
320        let app_context = Rc::clone(&self.app_context);
321        app_context.enter(|| self.composition.process_invalid_scopes())
322    }
323
324    pub fn render(&mut self, root_key: Key, content: impl FnMut()) -> Result<(), NodeError> {
325        let app_context = Rc::clone(&self.app_context);
326        app_context.enter(|| self.composition.render(root_key, content))
327    }
328
329    pub fn applier_mut(&mut self) -> TestApplierGuard<'_> {
330        let scope = self.app_context.enter_scope();
331        let applier = self.composition.applier_mut();
332        TestApplierGuard {
333            _scope: scope,
334            applier,
335        }
336    }
337
338    pub fn with_app_context<R>(&self, block: impl FnOnce() -> R) -> R {
339        self.app_context.enter(block)
340    }
341}
342
343pub struct TestApplierGuard<'a> {
344    _scope: render_state::AppContextScope,
345    applier: ApplierGuard<'a, MemoryApplier>,
346}
347
348impl Deref for TestApplierGuard<'_> {
349    type Target = MemoryApplier;
350
351    fn deref(&self) -> &Self::Target {
352        &self.applier
353    }
354}
355
356impl DerefMut for TestApplierGuard<'_> {
357    fn deref_mut(&mut self) -> &mut Self::Target {
358        &mut self.applier
359    }
360}
361
362/// Build a composition with a simple in-memory applier and run the provided closure once.
363pub fn run_test_composition(build: impl FnMut()) -> TestComposition {
364    let app_context = AppContext::new();
365    app_context.enter(|| {
366        #[cfg(test)]
367        reset_render_state_for_tests();
368    });
369    let mut test_composition = TestComposition {
370        _scope: app_context.enter_scope(),
371        app_context,
372        composition: Composition::new(MemoryApplier::new()),
373    };
374    test_composition
375        .render(location_key(file!(), line!(), column!()), build)
376        .expect("initial render succeeds");
377    test_composition
378}
379
380pub use cranpose_core::MutableState as SnapshotState;
381
382#[cfg(test)]
383#[path = "tests/anchor_async_tests.rs"]
384mod anchor_async_tests;
385
386#[cfg(test)]
387#[path = "tests/animated_visibility_tests.rs"]
388mod animated_visibility_tests;
389
390#[cfg(test)]
391#[path = "tests/lazy_recycle_effect_tests.rs"]
392mod lazy_recycle_effect_tests;
393
394#[cfg(test)]
395#[path = "tests/measure_time_ui_task_tests.rs"]
396mod measure_time_ui_task_tests;
397
398#[cfg(test)]
399#[path = "tests/animation_frame_pump_tests.rs"]
400mod animation_frame_pump_tests;
401
402#[cfg(test)]
403#[path = "tests/crossfade_tests.rs"]
404mod crossfade_tests;
405
406#[cfg(test)]
407#[path = "tests/async_runtime_full_layout_test.rs"]
408mod async_runtime_full_layout_test;
409
410#[cfg(test)]
411#[path = "tests/cursor_position_tests.rs"]
412mod cursor_position_tests;
413
414#[cfg(test)]
415#[path = "tests/popup_tests.rs"]
416mod popup_tests;
417
418#[cfg(test)]
419#[path = "tests/selection_handle_tests.rs"]
420mod selection_handle_tests;
421
422#[cfg(test)]
423#[path = "tests/tab_switching_tests.rs"]
424mod tab_switching_tests;
425
426#[cfg(test)]
427#[path = "tests/layout_keep_tests.rs"]
428mod layout_keep_tests;
429
430#[cfg(test)]
431#[path = "tests/lazy_list_viewport_tests.rs"]
432mod lazy_list_viewport_tests;
433
434#[cfg(test)]
435#[path = "tests/swipe_to_dismiss_lazy_tests.rs"]
436mod swipe_to_dismiss_lazy_tests;
437
438#[cfg(test)]
439#[path = "tests/top_modal_tests.rs"]
440mod top_modal_tests;
441
442#[cfg(test)]
443#[path = "tests/swipe_to_dismiss_render_tests.rs"]
444mod swipe_to_dismiss_render_tests;
445
446#[cfg(test)]
447#[path = "tests/lazy_list_recompose_tests.rs"]
448mod lazy_list_recompose_tests;
449
450#[cfg(test)]
451#[path = "tests/lazy_row_tests.rs"]
452mod lazy_row_tests;
453
454#[cfg(test)]
455#[path = "tests/wear_widget_tests.rs"]
456mod wear_widget_tests;
457
458#[cfg(test)]
459#[path = "tests/ios_fling_measurement.rs"]
460mod ios_fling_measurement;