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