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