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