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