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