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