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