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
296pub 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
369pub 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;