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