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