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