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