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