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_ime_insets, 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, has_pending_measure_repasses, peek_focus_invalidation,
122 peek_layout_invalidation, peek_pointer_invalidation, peek_render_invalidation,
123 pending_layout_repass_nodes_snapshot, request_focus_invalidation, request_layout_invalidation,
124 request_pointer_invalidation, request_render_invalidation, schedule_draw_repass,
125 schedule_layout_repass, schedule_measure_repass, set_density, take_draw_repass_nodes,
126 take_focus_invalidation, take_layout_invalidation, take_layout_repass_nodes,
127 take_measure_repass_nodes, take_pointer_invalidation, take_render_invalidation, AppContext,
128 AppContextScope,
129};
130pub use renderer::{HeadlessRenderer, PaintLayer, RecordedRenderScene, RenderOp};
131pub use scroll::{ScrollElement, ScrollNode, ScrollState};
132pub use zoom::ZoomState;
133#[cfg(feature = "test-helpers")]
135pub use modifier::{last_fling_velocity, reset_last_fling_velocity};
136pub use subcompose_layout::{
137 Constraints, MeasureResult, Placement, SubcomposeLayoutNode, SubcomposeLayoutScope,
138 SubcomposeMeasureScope, SubcomposeMeasureScopeImpl,
139};
140pub use text::{
141 get_cursor_x_for_offset, get_offset_for_position, layout_text, measure_text,
142 measure_text_for_node, measure_text_with_options, measure_text_with_options_for_node,
143 prepare_text_layout, prepare_text_layout_for_node, set_text_measurer, LinkAnnotation,
144 ParagraphStyle, PlatformParagraphStyle, PlatformSpanStyle, PlatformTextStyle,
145 PreparedTextLayout, SpanStyle, StringAnnotation, TextDrawStyle, TextLayoutOptions,
146 TextLayoutResult, TextLinePrefixWidths, TextMeasurer, TextMetrics, TextOptions, TextOverflow,
147 TextShaping, TextStyle,
148};
149pub use text_field_modifier_node::{TextFieldElement, TextFieldModifierNode, TextPanResolver};
150pub use text_modifier_node::{TextModifierElement, TextModifierNode};
151pub use widgets::clickable_text::ClickableText;
152pub use widgets::lazy_list::{LazyColumn, LazyColumnSpec, LazyRow, LazyRowSpec};
153pub use widgets::linked_text::LinkedText;
154pub use widgets::swipe_to_dismiss::{SwipeDismissSide, SwipeToDismiss, SwipeToDismissSpec};
155
156pub use debug::{
158 format_layout_tree, format_modifier_chain, format_render_scene, format_screen_summary,
159 install_modifier_chain_trace, log_layout_tree, log_modifier_chain, log_render_scene,
160 log_screen_summary, ModifierChainTraceGuard,
161};
162
163pub struct TestComposition {
165 _scope: render_state::AppContextScope,
166 app_context: Rc<AppContext>,
167 composition: Composition<MemoryApplier>,
168}
169
170impl TestComposition {
171 pub fn root(&self) -> Option<NodeId> {
172 self.app_context.enter(|| self.composition.root())
173 }
174
175 pub fn runtime_handle(&self) -> RuntimeHandle {
176 self.app_context.enter(|| self.composition.runtime_handle())
177 }
178
179 pub fn should_render(&self) -> bool {
180 self.app_context.enter(|| self.composition.should_render())
181 }
182
183 pub fn take_root_render_request(&mut self) -> bool {
184 let app_context = Rc::clone(&self.app_context);
185 app_context.enter(|| self.composition.take_root_render_request())
186 }
187
188 pub fn flush_pending_node_updates(&mut self) -> Result<(), NodeError> {
189 let app_context = Rc::clone(&self.app_context);
190 app_context.enter(|| self.composition.flush_pending_node_updates())
191 }
192
193 pub fn process_invalid_scopes(&mut self) -> Result<bool, NodeError> {
194 let app_context = Rc::clone(&self.app_context);
195 app_context.enter(|| self.composition.process_invalid_scopes())
196 }
197
198 pub fn render(&mut self, root_key: Key, content: impl FnMut()) -> Result<(), NodeError> {
199 let app_context = Rc::clone(&self.app_context);
200 app_context.enter(|| self.composition.render(root_key, content))
201 }
202
203 pub fn applier_mut(&mut self) -> TestApplierGuard<'_> {
204 let scope = self.app_context.enter_scope();
205 let applier = self.composition.applier_mut();
206 TestApplierGuard {
207 _scope: scope,
208 applier,
209 }
210 }
211
212 pub fn with_app_context<R>(&self, block: impl FnOnce() -> R) -> R {
213 self.app_context.enter(block)
214 }
215}
216
217pub struct TestApplierGuard<'a> {
218 _scope: render_state::AppContextScope,
219 applier: ApplierGuard<'a, MemoryApplier>,
220}
221
222impl Deref for TestApplierGuard<'_> {
223 type Target = MemoryApplier;
224
225 fn deref(&self) -> &Self::Target {
226 &self.applier
227 }
228}
229
230impl DerefMut for TestApplierGuard<'_> {
231 fn deref_mut(&mut self) -> &mut Self::Target {
232 &mut self.applier
233 }
234}
235
236pub fn run_test_composition(build: impl FnMut()) -> TestComposition {
238 let app_context = AppContext::new();
239 app_context.enter(|| {
240 #[cfg(test)]
241 reset_render_state_for_tests();
242 });
243 let mut test_composition = TestComposition {
244 _scope: app_context.enter_scope(),
245 app_context,
246 composition: Composition::new(MemoryApplier::new()),
247 };
248 test_composition
249 .render(location_key(file!(), line!(), column!()), build)
250 .expect("initial render succeeds");
251 test_composition
252}
253
254pub use cranpose_core::MutableState as SnapshotState;
255
256#[cfg(test)]
257#[path = "tests/anchor_async_tests.rs"]
258mod anchor_async_tests;
259
260#[cfg(test)]
261#[path = "tests/animated_visibility_tests.rs"]
262mod animated_visibility_tests;
263
264#[cfg(test)]
265#[path = "tests/crossfade_tests.rs"]
266mod crossfade_tests;
267
268#[cfg(test)]
269#[path = "tests/async_runtime_full_layout_test.rs"]
270mod async_runtime_full_layout_test;
271
272#[cfg(test)]
273#[path = "tests/cursor_position_tests.rs"]
274mod cursor_position_tests;
275
276#[cfg(test)]
277#[path = "tests/popup_tests.rs"]
278mod popup_tests;
279
280#[cfg(test)]
281#[path = "tests/selection_handle_tests.rs"]
282mod selection_handle_tests;
283
284#[cfg(test)]
285#[path = "tests/tab_switching_tests.rs"]
286mod tab_switching_tests;
287
288#[cfg(test)]
289#[path = "tests/lazy_list_viewport_tests.rs"]
290mod lazy_list_viewport_tests;
291
292#[cfg(test)]
293#[path = "tests/swipe_to_dismiss_lazy_tests.rs"]
294mod swipe_to_dismiss_lazy_tests;
295
296#[cfg(test)]
297#[path = "tests/swipe_to_dismiss_render_tests.rs"]
298mod swipe_to_dismiss_render_tests;
299
300#[cfg(test)]
301#[path = "tests/lazy_list_recompose_tests.rs"]
302mod lazy_list_recompose_tests;