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