1use std::{
4 ops::{Deref, DerefMut},
5 rc::Rc,
6};
7
8use cranpose_core::{ApplierGuard, MemoryApplier, NodeError, NodeId, RuntimeHandle, location_key};
9pub use cranpose_core::{Composition, Key};
10pub use cranpose_macros::composable;
11
12pub mod announce;
13pub mod bring_into_view;
14pub mod clipboard_session;
15mod cursor_animation;
16mod debug;
17pub mod density;
18pub mod draggable;
19mod draw;
20pub mod fling_animation;
21mod focus_dispatch;
22pub mod focus_manager;
23pub mod focus_order;
24pub mod font_scale;
25mod interaction;
26mod key_event;
27pub mod layout;
28pub mod layout_direction;
29pub mod lazy_item;
30pub mod modal;
31mod modifier;
32mod modifier_nodes;
33pub mod nine_patch;
34mod pointer_dispatch;
35mod primitives;
36mod render_state;
37mod renderer;
38pub mod round_scaling_list;
39pub mod round_scroll_indicator;
40pub mod safe_area;
41pub mod scroll;
42pub mod scrollbar;
43mod semantics_dispatch;
44mod subcompose_layout;
45pub mod text;
46pub mod text_field_focus;
47mod text_field_handler;
48mod text_field_input;
49mod text_field_modifier_node;
50pub mod text_input_session;
51pub mod text_layout_result;
52mod text_modifier_node;
53pub mod text_selection;
54pub mod widgets;
55mod word_boundaries;
56pub mod zoom;
57
58#[doc(hidden)]
59pub use announce::{Announcement, Announcer, announce, drain_announcements, local_announcer};
60pub use bring_into_view::{
61 BringIntoViewResponder, local_bring_into_view_responder, scroll_delta_to_reveal,
62};
63pub use cranpose_foundation::{
64 CanvasSemanticsNode, DelegatableNode, LiveRegionMode, ModifierNode, ModifierNodeElement,
65 NodeCapabilities, NodeState, SemanticsConfiguration, SemanticsCustomAction,
66 SemanticsWidgetRole,
67 lazy::{
68 LazyItems, LazyListItemInfo, LazyListLayoutInfo, LazyListScope, LazyListState,
69 rememberLazyListState, rememberLazyListStateWithPosition,
70 },
71 text::{TextFieldBuffer, TextFieldLineLimits, TextFieldState, TextFieldValue, TextRange},
72};
73pub use cranpose_ui_graphics::{BlurredEdgeTreatment, ColorFilter, Dp, ImageBitmap, ImageSampling};
74pub use cranpose_ui_layout::IntrinsicSize;
75pub use cursor_animation::{
76 is_cursor_visible, next_cursor_blink_time, reset_cursor_blink, start_cursor_blink,
77 stop_cursor_blink, tick_cursor_blink,
78};
79pub use debug::{
80 ModifierChainTraceGuard, format_layout_tree, format_modifier_chain, format_render_scene,
81 format_screen_summary, install_modifier_chain_trace, log_layout_tree, log_modifier_chain,
82 log_render_scene, log_screen_summary,
83};
84pub use density::{Density, DensityMeasureScope, density, local_density};
85pub use draggable::{DragDeltaHandler, DraggableState, rememberDraggableState};
86pub use draw::{
87 DrawCacheBuilder, DrawCommand, DrawCommandFn, command_draw_scope, command_draw_scope_reusing,
88 execute_draw_commands,
89};
90pub use focus_dispatch::{
91 active_focus_target, clear_focus_invalidations, has_pending_focus_invalidations,
92 process_focus_invalidations, schedule_focus_invalidation, set_active_focus_target,
93};
94pub use focus_manager::{FocusManager, local_focus_manager, request_focus_from_platform};
95pub use focus_order::{FocusEntry, collect_focus_order, focus_order_len, set_focus_order};
96pub use font_scale::{FontScaleCurve, MAX_FONT_SCALE_KNOTS};
97pub use interaction::{
98 Interaction, MutableInteractionSource, PressInteraction, PressInteractionCancel,
99 PressInteractionPress, PressInteractionRelease, collect_is_pressed_as_state,
100 rememberMutableInteractionSource,
101};
102pub use key_event::{KeyCode, KeyEvent, KeyEventType, Modifiers};
103pub use layout::{
104 LayoutAllocationDebugStats, LayoutBox, LayoutEngine, LayoutMeasurements, LayoutNodeData,
105 LayoutNodeKind, LayoutTree, MeasureLayoutOptions, SemanticsAction, SemanticsCallback,
106 SemanticsNode, SemanticsRole, SemanticsTree, build_layout_tree_from_applier,
107 build_semantics_tree_from_applier, build_semantics_tree_from_layout_tree,
108 core::{
109 Alignment, Arrangement, HorizontalAlignment, LinearArrangement, Measurable, Placeable,
110 VerticalAlignment,
111 },
112 measure_layout, measure_layout_with_options, tree_needs_layout, tree_needs_semantics,
113};
114pub use layout_direction::{
115 LayoutDirection, ProvideLayoutDirection, layout_direction, local_layout_direction,
116};
117pub use lazy_item::{ProvideLazyItemKey, lazy_item_key, local_lazy_item_key};
118pub use modal::{
119 ModalRegistration, clear_modals, dispatch_modal_back, local_modal_depth, modal_depth,
120};
121pub use modifier::{
122 BlendMode, Brush, Color, CompositingStrategy, CornerRadii, DpOffset, EdgeInsets,
123 FocusDirection, FocusRequestError, FocusRequester, GlassMaterial, GraphicsLayer, LayerShape,
124 Modifier, ModifierLocalKey, ModifierLocalReadScope, ModifierNodeSlices,
125 ModifierNodeSlicesDebugStats, Point, PointerEvent, PointerEventKind, PointerInputScope,
126 PointerSource, Rect, RenderEffect, ResolvedBackground, ResolvedModifiers,
127 RotaryInputModifierNode, RotaryScrollEvent, RoundedCornerShape, RuntimeShader,
128 SemanticsRequester, Shadow, ShadowScope, Size, TransformOrigin, collect_modifier_slices,
129 collect_semantics_from_modifier, collect_slices_from_modifier,
130};
131#[cfg(feature = "test-helpers")]
132pub use modifier::{last_fling_velocity, reset_last_fling_velocity};
133pub use modifier_nodes::{
134 AlphaElement, AlphaNode, BackgroundElement, BackgroundNode, ClickableElement, ClickableNode,
135 CornerShapeElement, CornerShapeNode, FillDirection, FillElement, FillNode,
136 FractionalOffsetElement, FractionalOffsetNode, OffsetElement, OffsetNode, PaddingElement,
137 PaddingNode, SizeElement, SizeNode,
138};
139pub use nine_patch::{
140 NinePatchInsets, PatchFill, PatchQuad, nine_patch_quads, tile_count, tile_quads,
141};
142#[doc(hidden)]
143pub use pointer_dispatch::{
144 clear_pointer_repasses, has_pending_pointer_repasses, process_pointer_repasses,
145 schedule_pointer_repass,
146};
147pub use primitives::{
148 AnimatedVisibility, BasicText, BasicTextField, BasicTextFieldDecorated,
149 BasicTextFieldDecorationScope, BasicTextFieldOptions, BasicTextFieldWithOptions,
150 BasicTextWithOptions, BitmapPainter, BitmapRegionPainter, Box, BoxScope, BoxSpec,
151 BoxWithConstraints, BoxWithConstraintsScope, BoxWithConstraintsScopeImpl, Button, ButtonSpec,
152 Canvas, Column, ColumnSpec, ContentScale, Crossfade, DEFAULT_ALPHA, EnterTransition,
153 ExitTransition, ForEach, Image, Layout, LayoutNode, Painter, Row, RowSpec, Spacer,
154 SubcomposeLayout, SvgPainter, SvgPainterError, Text, TextWithOptions, fade_in, fade_out,
155 rememberSvg, slide_in_vertically, slide_out_vertically,
156};
157#[cfg(any(test, feature = "test-helpers"))]
158#[doc(hidden)]
159pub use render_state::reset_render_state_for_tests;
160pub use render_state::{
161 AppContext, AppContextScope, MAX_FONT_SCALE, MIN_FONT_SCALE, current_density,
162 current_font_scale, current_font_scale_curve, scale_sp, set_density, set_font_scale,
163 set_font_scale_curve,
164};
165#[doc(hidden)]
166pub use render_state::{
167 clear_transient_scroll_motion_contexts, debug_last_fling_velocity,
168 debug_reset_last_fling_velocity, has_current_app_context, has_pending_draw_repasses,
169 has_pending_layout_repasses, has_pending_measure_repasses, peek_focus_invalidation,
170 peek_layout_invalidation, peek_pointer_invalidation, peek_render_invalidation,
171 pending_layout_repass_nodes_snapshot, pending_measure_repass_nodes_snapshot,
172 prune_draw_observations_to_nodes, request_current_draw_redraw, request_focus_invalidation,
173 request_layout_invalidation, request_pointer_invalidation, request_render_invalidation,
174 schedule_draw_repass, schedule_layout_repass, schedule_measure_repass, take_draw_repass_nodes,
175 take_focus_invalidation, take_geometry_scene_nodes, take_layout_invalidation,
176 take_layout_repass_nodes, take_measure_repass_nodes, take_pointer_invalidation,
177 take_render_invalidation,
178};
179pub use renderer::{HeadlessRenderer, PaintLayer, RecordedRenderScene, RenderOp};
180pub use safe_area::{WindowInsets, local_ime_insets, local_safe_area_insets, window_insets};
181pub use scroll::{ScrollElement, ScrollMetrics, ScrollNode, ScrollSettlePolicy, ScrollState};
182pub use scrollbar::{ThumbBounds, ThumbGeometry, content_delta_for_thumb_drag, thumb_geometry};
183#[doc(hidden)]
184pub use semantics_dispatch::{
185 clear_semantics_invalidations, has_pending_semantics_invalidations,
186 process_semantics_invalidations, schedule_semantics_invalidation,
187};
188pub use subcompose_layout::{
189 Constraints, MeasureResult, Placement, SubcomposeLayoutNode, SubcomposeLayoutScope,
190 SubcomposeMeasureScope, SubcomposeMeasureScopeImpl, clean_slot_skip_count,
191};
192pub use text::{
193 LinkAnnotation, ParagraphStyle, PlatformParagraphStyle, PlatformSpanStyle, PlatformTextStyle,
194 PreparedTextLayout, SpanStyle, StringAnnotation, TextDrawStyle, TextLayoutOptions,
195 TextLayoutResult, TextLinePrefixWidths, TextMeasurer, TextMetrics, TextOptions, TextOverflow,
196 TextShaping, TextStyle, get_cursor_x_for_offset, get_offset_for_position, layout_text,
197 measure_text, measure_text_for_node, measure_text_with_options,
198 measure_text_with_options_for_node, prepare_text_layout, prepare_text_layout_for_node,
199 set_text_measurer,
200};
201pub use text_field_focus::{ImeEditorState, has_focused_field};
202pub use text_field_modifier_node::{TextFieldElement, TextFieldModifierNode, TextPanResolver};
203pub use text_input_session::PlatformTextInputHandler;
204pub use text_modifier_node::{TextModifierElement, TextModifierNode};
205pub use widgets::{
206 clickable_text::ClickableText,
207 lazy_list::{LazyColumn, LazyColumnSpec, LazyRow, LazyRowSpec},
208 linked_text::LinkedText,
209 slider::{Slider, SliderOrientation, SliderScope, SliderSpec},
210 swipe_to_dismiss::{
211 SwipeDismissDirection, SwipeDismissSide, SwipeDismissState, SwipeToDismiss,
212 SwipeToDismissBox, SwipeToDismissSpec, rememberSwipeDismissState,
213 },
214 text_selection_menu::local_on_light_surface,
215};
216pub use zoom::ZoomState;
217
218pub struct TestComposition {
220 _scope: render_state::AppContextScope,
221 app_context: Rc<AppContext>,
222 composition: Composition<MemoryApplier>,
223}
224
225impl TestComposition {
226 pub fn root(&self) -> Option<NodeId> {
227 self.app_context.enter(|| self.composition.root())
228 }
229
230 pub fn runtime_handle(&self) -> RuntimeHandle {
231 self.app_context.enter(|| self.composition.runtime_handle())
232 }
233
234 pub fn should_render(&self) -> bool {
235 self.app_context.enter(|| self.composition.should_render())
236 }
237
238 pub fn take_root_render_request(&mut self) -> bool {
239 let app_context = Rc::clone(&self.app_context);
240 app_context.enter(|| self.composition.take_root_render_request())
241 }
242
243 pub fn flush_pending_node_updates(&mut self) -> Result<(), NodeError> {
244 let app_context = Rc::clone(&self.app_context);
245 app_context.enter(|| self.composition.flush_pending_node_updates())
246 }
247
248 pub fn process_invalid_scopes(&mut self) -> Result<bool, NodeError> {
249 let app_context = Rc::clone(&self.app_context);
250 app_context.enter(|| self.composition.process_invalid_scopes())
251 }
252
253 pub fn render(&mut self, root_key: Key, content: impl FnMut()) -> Result<(), NodeError> {
254 let app_context = Rc::clone(&self.app_context);
255 app_context.enter(|| self.composition.render(root_key, content))
256 }
257
258 pub fn applier_mut(&mut self) -> TestApplierGuard<'_> {
259 let scope = self.app_context.enter_scope();
260 let applier = self.composition.applier_mut();
261 TestApplierGuard {
262 _scope: scope,
263 applier,
264 }
265 }
266
267 pub fn with_app_context<R>(&self, block: impl FnOnce() -> R) -> R {
268 self.app_context.enter(block)
269 }
270}
271
272pub struct TestApplierGuard<'a> {
273 _scope: render_state::AppContextScope,
274 applier: ApplierGuard<'a, MemoryApplier>,
275}
276
277impl Deref for TestApplierGuard<'_> {
278 type Target = MemoryApplier;
279
280 fn deref(&self) -> &Self::Target {
281 &self.applier
282 }
283}
284
285impl DerefMut for TestApplierGuard<'_> {
286 fn deref_mut(&mut self) -> &mut Self::Target {
287 &mut self.applier
288 }
289}
290
291pub fn run_test_composition(build: impl FnMut()) -> TestComposition {
293 let app_context = AppContext::new();
294 app_context.enter(|| {
295 #[cfg(test)]
296 reset_render_state_for_tests();
297 });
298 let mut test_composition = TestComposition {
299 _scope: app_context.enter_scope(),
300 app_context,
301 composition: Composition::new(MemoryApplier::new()),
302 };
303 test_composition
304 .render(location_key(file!(), line!(), column!()), build)
305 .expect("initial render succeeds");
306 test_composition
307}
308
309pub use cranpose_core::MutableState as SnapshotState;
310
311#[cfg(test)]
312#[path = "tests/anchor_async_tests.rs"]
313mod anchor_async_tests;
314
315#[cfg(test)]
316#[path = "tests/animated_visibility_tests.rs"]
317mod animated_visibility_tests;
318
319#[cfg(test)]
320#[path = "tests/lazy_recycle_effect_tests.rs"]
321mod lazy_recycle_effect_tests;
322
323#[cfg(test)]
324#[path = "tests/animation_frame_pump_tests.rs"]
325mod animation_frame_pump_tests;
326
327#[cfg(test)]
328#[path = "tests/crossfade_tests.rs"]
329mod crossfade_tests;
330
331#[cfg(test)]
332#[path = "tests/async_runtime_full_layout_test.rs"]
333mod async_runtime_full_layout_test;
334
335#[cfg(test)]
336#[path = "tests/cursor_position_tests.rs"]
337mod cursor_position_tests;
338
339#[cfg(test)]
340#[path = "tests/popup_tests.rs"]
341mod popup_tests;
342
343#[cfg(test)]
344#[path = "tests/selection_handle_tests.rs"]
345mod selection_handle_tests;
346
347#[cfg(test)]
348#[path = "tests/tab_switching_tests.rs"]
349mod tab_switching_tests;
350
351#[cfg(test)]
352#[path = "tests/lazy_list_viewport_tests.rs"]
353mod lazy_list_viewport_tests;
354
355#[cfg(test)]
356#[path = "tests/swipe_to_dismiss_lazy_tests.rs"]
357mod swipe_to_dismiss_lazy_tests;
358
359#[cfg(test)]
360#[path = "tests/swipe_to_dismiss_render_tests.rs"]
361mod swipe_to_dismiss_render_tests;
362
363#[cfg(test)]
364#[path = "tests/lazy_list_recompose_tests.rs"]
365mod lazy_list_recompose_tests;
366
367#[cfg(test)]
368#[path = "tests/lazy_row_tests.rs"]
369mod lazy_row_tests;
370
371#[cfg(test)]
372#[path = "tests/wear_widget_tests.rs"]
373mod wear_widget_tests;
374
375#[cfg(test)]
376#[path = "tests/ios_fling_measurement.rs"]
377mod ios_fling_measurement;