Skip to main content

cranpose_ui/
lib.rs

1#![deny(missing_docs)]
2#![doc = include_str!("../README.md")]
3
4extern crate self as cranpose_ui;
5
6use std::{
7    ops::{Deref, DerefMut},
8    rc::Rc,
9};
10
11pub use cranpose_core::Composition;
12use cranpose_core::{ApplierGuard, MemoryApplier, NodeError, NodeId, RuntimeHandle, location_key};
13pub use cranpose_macros::{composable, worker_job};
14#[cfg(feature = "localization")]
15pub use cranpose_macros::{message, tr, translation_messages, translations};
16#[cfg(feature = "localization")]
17pub use localization::{
18    Catalog, Language, LanguagePreference, Locale, LocalizedText, PreviewMode, ProvideLocalization,
19    ProvideTranslator,
20};
21pub use text::SharedText;
22#[cfg(feature = "localization")]
23mod ui_text;
24#[cfg(feature = "localization")]
25pub use ui_text::UiText;
26#[cfg(feature = "localization")]
27mod language_preferences;
28mod memory_headroom;
29#[cfg(feature = "localization")]
30pub use language_preferences::{
31    LocalizationController, ProvideLanguagePreferences, local_localization,
32};
33pub use ui_strings::UiString;
34
35/// Screen reader announcements for events that have no control on screen.
36pub mod announce;
37mod arrow_focus;
38pub mod bring_into_view;
39mod bring_into_view_spec;
40pub mod clipboard_session;
41mod configuration;
42mod cursor_animation;
43mod debug;
44pub mod density;
45mod dirty_node_queue;
46pub mod draggable;
47mod draw;
48mod environment_locals;
49mod eye;
50pub mod fling_animation;
51mod focus_dispatch;
52/// Moving focus between focus targets, as `LocalFocusManager` does in Compose.
53pub mod focus_manager;
54mod focus_navigation;
55/// The ordered list of focus targets that layout publishes for keyboard traversal.
56pub mod focus_order;
57mod focus_within;
58pub mod font_scale;
59mod interaction;
60mod key_event;
61mod keyboard_options;
62/// Layout tree, measurement and placement of nodes.
63pub mod layout;
64pub mod layout_direction;
65mod layout_modifier;
66pub mod lazy_item;
67mod lazy_prefetch;
68#[cfg(feature = "localization")]
69pub mod localization;
70pub mod modal;
71mod modal_nodes;
72#[expect(clippy::multiple_inherent_impl, reason = "one file per modifier")]
73mod modifier;
74mod modifier_nodes;
75pub mod mouse_input;
76mod nested_scroll;
77pub mod nine_patch;
78mod pointer_dispatch;
79pub mod pointer_icon_session;
80mod primitives;
81mod render_state;
82mod renderer;
83pub mod round_scaling_list;
84pub mod round_scroll_indicator;
85pub mod safe_area;
86pub mod scroll;
87mod scroll_semantics;
88pub mod scrollbar;
89pub mod selection_container;
90mod semantics_dispatch;
91mod semantics_layout_log;
92mod subcompose_layout;
93/// Text styling, measurement and layout.
94pub mod text;
95mod text_field_decorator_node;
96pub mod text_field_focus;
97mod text_field_handler;
98mod text_field_input;
99mod text_field_modifier_node;
100mod text_field_presentation;
101pub mod text_input_session;
102pub mod text_layout_result;
103mod text_modifier_node;
104pub mod text_selection;
105mod thread_state;
106mod two_dimensional_focus_search;
107mod ui_strings;
108mod unhandled_keys;
109pub mod widgets;
110mod word_boundaries;
111pub mod zoom;
112
113pub use announce::{Announcement, Announcer, announce, drain_announcements, local_announcer};
114#[doc(hidden)]
115pub use arrow_focus::arrow_focus_target;
116pub use bring_into_view::{
117    BringIntoViewResponder, local_bring_into_view_responder, scroll_delta_to_reveal,
118};
119pub use bring_into_view_spec::local_bring_into_view_spec;
120pub use configuration::{Configuration, UiModeType, local_configuration};
121pub use cranpose_foundation::{
122    BringIntoViewSpec, CanvasSemanticsNode, CollectionInfo, DelegatableNode, LiveRegionMode,
123    ModifierNode, ModifierNodeElement, NodeCapabilities, NodeState, PointerId,
124    ProgressBarRangeInfo, ScrollAxisRange, SemanticsConfiguration, SemanticsCustomAction,
125    SemanticsDismiss, SemanticsExpand, SemanticsLongClick, SemanticsMagicTap, SemanticsScrollBy,
126    SemanticsScrollToIndex, SemanticsSetProgress, SemanticsSetSelection, SemanticsSetText,
127    SemanticsSpec, SemanticsWidgetRole, ToggleableState,
128    lazy::{
129        LazyItems, LazyListItemInfo, LazyListLayoutInfo, LazyListScope, LazyListState,
130        rememberLazyListState, rememberLazyListStateWithPosition,
131    },
132    text::{TextFieldBuffer, TextFieldLineLimits, TextFieldState, TextFieldValue, TextRange},
133};
134/// Composables in 3D: meshes, cameras and scenes for
135/// [`Modifier::scene3d`] and [`Modifier::mesh_surface`].
136pub use cranpose_ui_graphics::space;
137pub use cranpose_ui_graphics::{
138    BlurredEdgeTreatment, ColorFilter, CursorIcon, CustomPointerIcon, DashPathEffect, Dp,
139    DrawScope, DrawSpecialization, DrawStyle, DrawTextStyle, DrawTransform, ExternalTexture,
140    ImageBitmap, ImageBitmapError, ImagePixelFormat, ImageSampling, MemoryHeadroom, Path,
141    PlaceholderShape, PointerIcon, PointerIconError, RuntimeShaderUniformError, ShaderPlaceholder,
142    Stroke, StrokeCap, StrokeJoin, SubstrateSpec, TextMeasurement, TextVerticalAlign, TileMode,
143    WindowCoordinates, glass_material_folds_enabled, set_glass_material_folds,
144};
145pub use cranpose_ui_layout::{
146    AlignmentLines, Axis, IntrinsicSize, LayoutModifierMeasureResult, Measurement,
147};
148pub use cursor_animation::{
149    is_cursor_visible, next_cursor_blink_time, reset_cursor_blink, start_cursor_blink,
150    stop_cursor_blink, tick_cursor_blink,
151};
152pub use debug::{
153    ModifierChainTraceGuard, format_layout_tree, format_modifier_chain, format_render_scene,
154    format_screen_summary, install_modifier_chain_trace, log_layout_tree, log_modifier_chain,
155    log_render_scene, log_screen_summary,
156};
157pub use density::{Density, DensityMeasureScope, density, local_density};
158pub use draggable::{
159    DragDeltaHandler, DraggableState, ScrollableState, rememberDraggableState,
160    rememberScrollableState,
161};
162pub use draw::{
163    DrawCacheBuilder, DrawCommand, DrawCommandFn, command_draw_scope, execute_draw_commands,
164};
165pub use eye::{Eye, local_eye, rememberEye};
166pub use focus_dispatch::{
167    active_focus_target, clear_focus_invalidations, has_focus_targets,
168    has_pending_focus_invalidations, keyboard_focus_visible, process_focus_invalidations,
169    schedule_focus_invalidation, set_active_focus_target, set_keyboard_focus_visible,
170};
171pub use focus_manager::{
172    FocusManager, local_focus_manager, request_focus_from_platform, request_focus_without_keyboard,
173};
174pub use focus_navigation::{KeyboardFocusTarget, keyboard_focus_target};
175pub use focus_order::{
176    FocusEntry, collect_focus_order, collect_focus_order_under, focus_order_len, set_focus_order,
177    take_requested_focus_move,
178};
179pub use focus_within::FocusWithin;
180pub use font_scale::{FontScaleCurve, MAX_FONT_SCALE_KNOTS};
181pub use interaction::{
182    HoverInteraction, HoverInteractionEnter, HoverInteractionExit, Interaction,
183    MutableInteractionSource, PressInteraction, PressInteractionCancel, PressInteractionPress,
184    PressInteractionRelease, collect_is_hovered_as_state, collect_is_pressed_as_state,
185    rememberMutableInteractionSource,
186};
187pub use key_event::{Key, KeyEvent, KeyEventType, Modifiers, NativeKeyEvent};
188pub use keyboard_options::{
189    ContentType, ImeAction, KeyboardActionHandler, KeyboardActionScope, KeyboardCapitalization,
190    KeyboardOptions, KeyboardType,
191};
192pub use layout::{
193    LayoutAllocationDebugStats, LayoutBox, LayoutEngine, LayoutMeasurements, LayoutNodeData,
194    LayoutNodeKind, LayoutTree, MeasureLayoutOptions, SemanticsAction, SemanticsCallback,
195    SemanticsDetails, SemanticsNode, SemanticsPlacement, SemanticsRole, SemanticsText,
196    SemanticsTree, build_layout_tree_from_applier, build_semantics_tree_from_applier,
197    build_semantics_tree_from_layout_tree,
198    core::{
199        Alignment, Arrangement, HorizontalAlignment, LayoutData, LinearArrangement, Measurable,
200        ParentData, Placeable, VerticalAlignment,
201    },
202    has_placed_content, measure_layout, measure_layout_with_options, placed_content_extent,
203    top_modal_from_applier, tree_needs_layout, tree_needs_semantics,
204    update_semantics_tree_from_applier,
205};
206pub use layout_direction::{
207    LayoutDirection, ProvideLayoutDirection, layout_direction, local_layout_direction,
208};
209pub use lazy_item::{ProvideLazyItemKey, lazy_item_key, local_lazy_item_key};
210pub use lazy_prefetch::{
211    drain_lazy_prefetch_requests, has_lazy_prefetch_requests, lazy_prefetch_pass_cost,
212    lazy_prefetch_pass_due, set_idle_prefetch_passes, warm_prefetched_slices,
213    with_lazy_prefetch_pass,
214};
215pub use memory_headroom::{current_memory_headroom, set_memory_headroom};
216pub use modal::{
217    ModalRegistration, clear_modals, dispatch_modal_back, local_modal_depth, modal_depth,
218};
219pub use modifier::{
220    BlendMode, Brush, Color, CompositingStrategy, CornerRadii, DpOffset, DragAndDropEvent,
221    DragAndDropOutcome, DragAndDropPayload, DragAndDropPoint, DragAndDropSource,
222    DragAndDropSourceElement, DragAndDropSourceNode, DragAndDropState, DragAndDropTarget,
223    DragAndDropTargetElement, DragAndDropTargetNode, EdgeInsets, FocusDirection, FocusRequestError,
224    FocusRequester, GlassMaterial, GraphicsLayer, KeyEventRoute, LayerShape, Modifier,
225    ModifierLocalKey, ModifierLocalReadScope, ModifierNodeSlices, ModifierNodeSlicesDebugStats,
226    Point, PointerButton, PointerButtons, PointerEvent, PointerEventKind, PointerInputScope,
227    PointerSource, Rect, RenderEffect, ResolvedBackground, ResolvedModifiers,
228    RotaryInputModifierNode, RotaryScrollEvent, RoundedCornerShape, RuntimeShader,
229    SemanticsRequester, Shadow, ShadowScope, Size, SliceTextRef, TransformOrigin,
230    WindowRootDescriptor, WindowRootElement, WindowRootEntry, WindowRootNode, WindowRootRegistry,
231    WindowRootRoutingScratch, collect_modifier_slices, collect_semantics_from_modifier,
232    collect_slices_from_modifier, is_window_root, nearest_window_root, nearest_window_roots,
233    nearest_window_roots_into, window_roots, window_roots_revision, z_index_in_use,
234    z_index_modifier_in_use,
235};
236#[cfg(feature = "test-helpers")]
237pub use modifier::{last_fling_velocity, reset_last_fling_velocity};
238pub use modifier_nodes::{
239    AlphaElement, AlphaNode, BackgroundElement, BackgroundNode, ClickableElement, ClickableNode,
240    CornerShapeElement, CornerShapeNode, FillDirection, FillElement, FillNode,
241    FractionalOffsetElement, FractionalOffsetNode, OffsetElement, OffsetNode, PaddingElement,
242    PaddingNode, SizeElement, SizeNode,
243};
244pub use nested_scroll::{NestedScroll, NestedScrollConnection, NestedScrollSource};
245pub use nine_patch::{
246    NinePatchInsets, PatchFill, PatchQuad, nine_patch_quads, tile_count, tile_quads,
247};
248#[doc(hidden)]
249pub use pointer_dispatch::{
250    clear_pointer_repasses, has_pending_pointer_repasses, process_pointer_repasses,
251    schedule_pointer_repass,
252};
253pub use primitives::{
254    AnimateScroll, AnimatedContent, AnimatedContentWithKey, AnimatedVisibility,
255    BasicSecureTextField, BasicText, BasicTextField, BasicTextFieldDecorated,
256    BasicTextFieldDecorationScope, BasicTextFieldOptions, BasicTextFieldWithOptions,
257    BasicTextWithOptions, BitmapPainter, BitmapRegionPainter, Box, BoxScope, BoxSpec,
258    BoxWithConstraints, BoxWithConstraintsScope, BoxWithConstraintsScopeImpl, Button, ButtonSpec,
259    Canvas, Column, ColumnSpec, ContentScale, ContentSeek, ContentTransform, Crossfade,
260    DEFAULT_ALPHA, EnterTransition, ExitTransition, ForEach, HorizontalPager, Image, Layout,
261    LayoutNode, PageSize, PagerFling, PagerSpec, PagerState, Painter, Row, RowSpec,
262    SecureTextFieldOptions, SeekableAnimatedContent, SeekableCrossfade, SharedBounds,
263    SharedElement, SharedTransitionLayout, SizeTransform, SlideDirection, SnapPosition, Spacer,
264    SubcomposeLayout, SvgPainter, SvgPainterError, Text, TextObfuscationMode, TextWithOptions,
265    VerticalPager, fade_in, fade_out, rememberPagerState, rememberSvg, scale_in, scale_out,
266    slide_in_horizontally, slide_in_vertically, slide_into_container, slide_out_horizontally,
267    slide_out_of_container, slide_out_vertically,
268};
269#[cfg(any(test, feature = "test-helpers"))]
270#[doc(hidden)]
271pub use render_state::reset_render_state_for_tests;
272pub use render_state::{
273    AppContext, AppContextScope, MAX_FONT_SCALE, MIN_FONT_SCALE, current_density,
274    current_font_scale, current_font_scale_curve, scale_sp, set_density, set_font_scale,
275    set_font_scale_curve,
276};
277#[doc(hidden)]
278pub use render_state::{
279    GeometrySceneNodes, MovedSceneNode, append_z_order_changes,
280    clear_transient_scroll_motion_contexts, debug_last_fling_velocity,
281    debug_reset_last_fling_velocity, has_current_app_context, has_pending_draw_repasses,
282    has_pending_layer_property_repasses, has_pending_layout_repasses, has_pending_measure_repasses,
283    peek_focus_invalidation, peek_layout_invalidation, peek_pointer_invalidation,
284    peek_render_invalidation, pending_layout_repass_nodes_snapshot,
285    pending_measure_repass_nodes_snapshot, prune_draw_observations_to_nodes,
286    request_current_draw_redraw, request_focus_invalidation, request_layout_invalidation,
287    request_pointer_invalidation, request_render_invalidation, schedule_draw_repass,
288    schedule_layout_repass, schedule_measure_repass, schedule_z_order_change,
289    take_draw_repass_nodes, take_draw_repass_nodes_into, take_focus_invalidation,
290    take_geometry_scene_nodes, take_geometry_scene_nodes_into,
291    take_layer_property_repass_nodes_into, take_layout_invalidation, take_layout_repass_nodes,
292    take_measure_repass_nodes, take_pointer_invalidation, take_render_invalidation,
293};
294pub use renderer::{HeadlessRenderer, PaintLayer, RecordedRenderScene, RenderOp};
295pub use safe_area::{WindowInsets, local_ime_insets, local_safe_area_insets, window_insets};
296pub use scroll::{ScrollElement, ScrollMetrics, ScrollNode, ScrollSettlePolicy, ScrollState};
297pub use scrollbar::{ThumbBounds, ThumbGeometry, content_delta_for_thumb_drag, thumb_geometry};
298#[doc(hidden)]
299pub use semantics_dispatch::{
300    clear_semantics_invalidations, has_pending_semantics_invalidations,
301    process_semantics_invalidations, schedule_semantics_invalidation,
302};
303pub use subcompose_layout::{
304    Constraints, MeasureResult, Placement, SubcomposeLayoutNode, SubcomposeLayoutScope,
305    SubcomposeMeasureScope, SubcomposeMeasureScopeImpl, clean_slot_skip_count,
306    retained_measure_count,
307};
308pub use text::{
309    LinkAnnotation, ParagraphStyle, PlatformParagraphStyle, PlatformSpanStyle, PlatformTextStyle,
310    PreparedTextLayout, SpanStyle, StringAnnotation, TextDrawStyle, TextLayoutOptions,
311    TextLayoutResult, TextMetrics, TextOptions, TextOverflow, TextShaping, TextStyle,
312    get_cursor_x_for_offset, get_offset_for_position, layout_text, measure_text,
313    measure_text_with_options, prepare_text_layout, set_text_fonts,
314};
315pub use text_field_focus::{ImeEditorState, has_focused_field};
316pub use text_field_modifier_node::{TextFieldElement, TextFieldModifierNode, TextPanResolver};
317pub use text_input_session::PlatformTextInputHandler;
318pub use text_modifier_node::{TextModifierElement, TextModifierNode};
319pub use unhandled_keys::{UnhandledKeyEvents, dispatch_unhandled_key_event};
320pub use widgets::{
321    clickable_text::ClickableText,
322    lazy_list::{
323        GridCells, LazyColumn, LazyColumnSpec, LazyGridState, LazyHorizontalGrid,
324        LazyHorizontalGridSpec, LazyHorizontalStaggeredGrid, LazyHorizontalStaggeredGridSpec,
325        LazyRow, LazyRowSpec, LazyStaggeredGridState, LazyVerticalGrid, LazyVerticalGridSpec,
326        LazyVerticalStaggeredGrid, LazyVerticalStaggeredGridSpec, rememberLazyGridState,
327        rememberLazyStaggeredGridState,
328    },
329    linked_text::LinkedText,
330    padding_values::PaddingValues,
331    popup::{dismiss_top_popup, dismissable_popup_open},
332    slider::{Slider, SliderOrientation, SliderScope, SliderSpec},
333    swipe_to_dismiss::{
334        SwipeDismissDirection, SwipeDismissSide, SwipeDismissState, SwipeToDismiss,
335        SwipeToDismissBox, SwipeToDismissSpec, rememberSwipeDismissState,
336    },
337    text_selection_menu::local_on_light_surface,
338};
339pub use zoom::ZoomState;
340
341/// In-memory composition helper used by tests.
342pub struct TestComposition {
343    _scope: AppContextScope,
344    app_context: Rc<AppContext>,
345    composition: Composition<MemoryApplier>,
346    /// The root content and its key, which a root render composes again, as
347    /// a host's reconcile does.
348    root_content: Option<(cranpose_core::Key, Box<dyn FnMut()>)>,
349}
350
351impl TestComposition {
352    /// Returns the id of the root node, or `None` when the composition has no root node.
353    pub fn root(&self) -> Option<NodeId> {
354        self.app_context.enter(|| self.composition.root())
355    }
356
357    /// Returns the runtime handle of the composition.
358    pub fn runtime_handle(&self) -> RuntimeHandle {
359        self.app_context.enter(|| self.composition.runtime_handle())
360    }
361
362    /// Returns true when state changes or animation callbacks have work pending.
363    pub fn should_render(&self) -> bool {
364        self.app_context.enter(|| self.composition.should_render())
365    }
366
367    /// Returns true once if a root render was requested since the last call, then resets the
368    /// request.
369    pub fn take_root_render_request(&mut self) -> bool {
370        self.app_context
371            .enter(|| self.composition.take_root_render_request())
372    }
373
374    /// Applies the node updates that state changes queued.
375    pub fn flush_pending_node_updates(&mut self) -> Result<(), NodeError> {
376        self.app_context
377            .enter(|| self.composition.flush_pending_node_updates())
378    }
379
380    /// Recomposes the scopes that state changes invalidated, and composes the root content again
381    /// when a read with no composable around it asked for that, as a host does. Returns true
382    /// when anything recomposed.
383    pub fn process_invalid_scopes(&mut self) -> Result<bool, NodeError> {
384        self.app_context.enter(|| match &mut self.root_content {
385            Some((key, content)) => self.composition.reconcile(*key, content),
386            None => self.composition.process_invalid_scopes(),
387        })
388    }
389
390    /// Renders `content` as the root composition under `root_key`, then recomposes invalidated
391    /// scopes. Later recompositions compose `content` again when the root needs it.
392    pub fn render(
393        &mut self,
394        root_key: cranpose_core::Key,
395        content: impl FnMut() + 'static,
396    ) -> Result<(), NodeError> {
397        let (key, content) = self.root_content.insert((root_key, Box::new(content)));
398        self.app_context
399            .enter(|| self.composition.render(*key, content))
400    }
401
402    /// Measures and places the tree under `root` within `max_size`, as a
403    /// host's layout pass does.
404    pub fn measure_layout(
405        &mut self,
406        root: NodeId,
407        max_size: Size,
408    ) -> Result<LayoutMeasurements, NodeError> {
409        self.with_runtime_applier(|applier| measure_layout(applier, root, max_size))
410    }
411
412    /// Runs `with` on the applier while it holds the composition's runtime
413    /// handle, as a host's layout pass does, and takes the handle back after.
414    pub fn with_runtime_applier<R>(&mut self, with: impl FnOnce(&mut MemoryApplier) -> R) -> R {
415        let handle = self.runtime_handle();
416        let mut applier = self.applier_mut();
417        applier.set_runtime_handle(handle);
418        let result = with(&mut applier);
419        applier.clear_runtime_handle();
420        result
421    }
422
423    /// Borrows the applier that holds the node tree. The guard keeps this composition's app context
424    /// active.
425    pub fn applier_mut(&mut self) -> TestApplierGuard<'_> {
426        let scope = self.app_context.enter_scope();
427        let applier = self.composition.applier_mut();
428        TestApplierGuard {
429            _scope: scope,
430            applier,
431        }
432    }
433
434    /// Runs `block` with this composition's app context active.
435    pub fn with_app_context<R>(&self, block: impl FnOnce() -> R) -> R {
436        self.app_context.enter(block)
437    }
438}
439
440/// Mutable access to the applier of a [`TestComposition`], with the app context of that composition
441/// active while the guard lives.
442pub struct TestApplierGuard<'a> {
443    _scope: AppContextScope,
444    applier: ApplierGuard<'a, MemoryApplier>,
445}
446
447impl Deref for TestApplierGuard<'_> {
448    type Target = MemoryApplier;
449
450    fn deref(&self) -> &Self::Target {
451        &self.applier
452    }
453}
454
455impl DerefMut for TestApplierGuard<'_> {
456    fn deref_mut(&mut self) -> &mut Self::Target {
457        &mut self.applier
458    }
459}
460
461/// Build a composition with a simple in-memory applier and run the provided closure once.
462pub fn run_test_composition(build: impl FnMut() + 'static) -> TestComposition {
463    let composition = Composition::new(MemoryApplier::new());
464    let app_context = AppContext::new_with_density(1.0, Some(composition.runtime_handle()));
465    app_context.enter(|| {
466        #[cfg(test)]
467        reset_render_state_for_tests();
468    });
469    let mut test_composition = TestComposition {
470        _scope: app_context.enter_scope(),
471        app_context,
472        composition,
473        root_content: None,
474    };
475    test_composition
476        .render(location_key(file!(), line!(), column!()), build)
477        .expect("initial render succeeds");
478    test_composition
479}
480
481pub use cranpose_core::MutableState as SnapshotState;
482
483#[cfg(test)]
484#[path = "tests/anchor_async_tests.rs"]
485mod anchor_async_tests;
486
487#[cfg(test)]
488#[path = "tests/animated_visibility_tests.rs"]
489mod animated_visibility_tests;
490
491#[cfg(test)]
492#[path = "tests/lazy_recycle_effect_tests.rs"]
493mod lazy_recycle_effect_tests;
494
495#[cfg(test)]
496#[path = "tests/measure_time_ui_task_tests.rs"]
497mod measure_time_ui_task_tests;
498
499#[cfg(test)]
500#[path = "tests/animation_frame_pump_tests.rs"]
501mod animation_frame_pump_tests;
502
503#[cfg(test)]
504#[path = "tests/crossfade_tests.rs"]
505mod crossfade_tests;
506
507#[cfg(test)]
508#[path = "tests/async_runtime_full_layout_test.rs"]
509mod async_runtime_full_layout_test;
510
511#[cfg(test)]
512#[path = "tests/cursor_position_tests.rs"]
513mod cursor_position_tests;
514
515#[cfg(test)]
516#[path = "tests/popup_tests.rs"]
517mod popup_tests;
518
519#[cfg(test)]
520#[path = "tests/selection_handle_tests.rs"]
521mod selection_handle_tests;
522
523#[cfg(test)]
524#[path = "tests/tab_switching_tests.rs"]
525mod tab_switching_tests;
526
527#[cfg(test)]
528#[path = "tests/layout_keep_tests.rs"]
529mod layout_keep_tests;
530
531#[cfg(test)]
532#[path = "tests/lazy_list_viewport_tests.rs"]
533mod lazy_list_viewport_tests;
534
535#[cfg(test)]
536#[path = "tests/swipe_to_dismiss_lazy_tests.rs"]
537mod swipe_to_dismiss_lazy_tests;
538
539#[cfg(test)]
540#[path = "tests/top_modal_tests.rs"]
541mod top_modal_tests;
542
543#[cfg(test)]
544#[path = "tests/swipe_to_dismiss_render_tests.rs"]
545mod swipe_to_dismiss_render_tests;
546
547#[cfg(test)]
548#[path = "tests/lazy_list_recompose_tests.rs"]
549mod lazy_list_recompose_tests;
550
551#[cfg(test)]
552#[path = "tests/lazy_row_tests.rs"]
553mod lazy_row_tests;
554
555#[cfg(test)]
556#[path = "tests/wear_widget_tests.rs"]
557mod wear_widget_tests;
558
559#[cfg(test)]
560#[path = "tests/ios_fling_measurement.rs"]
561mod ios_fling_measurement;