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