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