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