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