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