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