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