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