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