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