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