/// ScrollView — scrollable container. /// /// Wraps content that may exceed the available space. Scrolling axis can be /// vertical (default), horizontal, or both. On platforms with native scroll /// behaviors (iOS, Android), the backend translates this to a native scroll /// container — you get momentum, overscroll, pull-to-refresh for free. use el_style::modifier::{StyleModifier, StyleSet}; /// Which axis (or axes) can scroll. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ScrollAxis { /// Vertical scrolling only (default). Most content views. Vertical, /// Horizontal scrolling only. Carousels, horizontal lists. Horizontal, /// Free scrolling in both directions. Maps, canvases. Both, } /// Scroll indicator visibility. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ScrollIndicator { /// Show when scrolling, hide otherwise (platform default). Automatic, /// Always show. Always, /// Never show. Never, } /// A scrollable container. #[derive(Debug, Clone)] pub struct ScrollView { pub axis: ScrollAxis, pub indicator: ScrollIndicator, /// Whether the user can zoom (pinch-to-zoom). Default: false. pub zoomable: bool, /// Minimum zoom scale (only relevant when zoomable = true). pub min_zoom: f32, /// Maximum zoom scale (only relevant when zoomable = true). pub max_zoom: f32, /// Whether to clip content to the scroll view bounds. Default: true. pub clips_to_bounds: bool, pub style: StyleSet, } impl Default for ScrollView { fn default() -> Self { Self { axis: ScrollAxis::Vertical, indicator: ScrollIndicator::Automatic, zoomable: false, min_zoom: 1.0, max_zoom: 3.0, clips_to_bounds: true, style: StyleSet::default(), } } } impl ScrollView { pub fn new() -> Self { Self::default() } pub fn horizontal() -> Self { Self { axis: ScrollAxis::Horizontal, ..Self::default() } } pub fn both_axes() -> Self { Self { axis: ScrollAxis::Both, ..Self::default() } } pub fn indicator(mut self, indicator: ScrollIndicator) -> Self { self.indicator = indicator; self } pub fn zoomable(mut self, min: f32, max: f32) -> Self { self.zoomable = true; self.min_zoom = min; self.max_zoom = max; self } } impl StyleModifier for ScrollView { fn style_mut(&mut self) -> &mut StyleSet { &mut self.style } } #[cfg(test)] mod tests { use super::*; #[test] fn scroll_default_is_vertical() { let sv = ScrollView::new(); assert_eq!(sv.axis, ScrollAxis::Vertical); } #[test] fn scroll_horizontal_constructor() { let sv = ScrollView::horizontal(); assert_eq!(sv.axis, ScrollAxis::Horizontal); } #[test] fn scroll_both_axes() { let sv = ScrollView::both_axes(); assert_eq!(sv.axis, ScrollAxis::Both); } #[test] fn scroll_zoomable() { let sv = ScrollView::new().zoomable(0.5, 4.0); assert!(sv.zoomable); assert_eq!(sv.min_zoom, 0.5); assert_eq!(sv.max_zoom, 4.0); } #[test] fn scroll_clips_by_default() { let sv = ScrollView::new(); assert!(sv.clips_to_bounds); } #[test] fn scroll_indicator_setting() { let sv = ScrollView::new().indicator(ScrollIndicator::Never); assert_eq!(sv.indicator, ScrollIndicator::Never); } }