Skip to main content

ScrollView

Struct ScrollView 

Source
pub struct ScrollView { /* private fields */ }
Expand description

Scroll container state with UIKit-style indicator behavior.

Implementations§

Source§

impl ScrollView

Source

pub const fn new() -> Self

Creates a scroll view with an enabled vertical indicator.

Examples found in repository?
examples/scroll_and_list.rs (line 64)
60fn main() {
61    let mut canvas = support::NullCanvas::new(Size::new(320, 240));
62    let viewport = Rectangle::new(Point::new(16, 16), Size::new(180, 160));
63
64    let mut scroll = ScrollView::new();
65    scroll.begin_drag(TouchEvent::new(Point::new(8, 8), TouchPhase::Start, 1));
66    let _ = scroll.drag(
67        TouchEvent::new(Point::new(8, 32), TouchPhase::Move, 2),
68        420,
69        160,
70    );
71    let _ = scroll.end_drag(
72        TouchEvent::new(Point::new(8, 32), TouchPhase::End, 3),
73        420,
74        160,
75    );
76    let _ = scroll.tick(16, 420, 160);
77    let _ = scroll.scroll_bar(viewport, 420);
78
79    let mut list = ListView::new(DemoDataSource, DemoDelegate);
80    let _ = list.handle_touch(
81        TouchEvent::new(Point::new(24, 24), TouchPhase::Start, 10),
82        viewport,
83    );
84    let _ = list.handle_touch(
85        TouchEvent::new(Point::new(24, 60), TouchPhase::Move, 11),
86        viewport,
87    );
88    let _ = list.handle_touch(
89        TouchEvent::new(Point::new(24, 60), TouchPhase::End, 12),
90        viewport,
91    );
92    list.draw(
93        &mut canvas,
94        viewport,
95        &ViewEnvironment {
96            theme: &support::theme(),
97            i18n: &support::i18n(),
98        },
99    );
100}
More examples
Hide additional examples
src/list_view/mod.rs (line 28)
26    pub fn new(data_source: DataSource, delegate: Delegate) -> Self {
27        Self {
28            scroll_view: ScrollView::new(),
29            data_source,
30            delegate,
31            touch_started_inside: false,
32            tap_candidate: false,
33            highlighted_index: None,
34            selected_index: None,
35            allows_selection: true,
36        }
37    }
Source

pub const fn state(&self) -> &ScrollViewState

Returns the underlying scroll state.

Examples found in repository?
src/list_view/mod.rs (line 51)
50    pub const fn state(&self) -> &ScrollViewState {
51        self.scroll_view.state()
52    }
Source

pub fn state_mut(&mut self) -> &mut ScrollViewState

Returns the underlying scroll state mutably.

Examples found in repository?
src/list_view/mod.rs (line 56)
55    pub fn state_mut(&mut self) -> &mut ScrollViewState {
56        self.scroll_view.state_mut()
57    }
Source

pub const fn shows_vertical_scroll_indicator(&self) -> bool

Returns whether the vertical indicator is enabled.

Examples found in repository?
src/list_view/render.rs (line 63)
62    pub fn shows_vertical_scroll_indicator(&self) -> bool {
63        self.scroll_view.shows_vertical_scroll_indicator()
64    }
Source

pub fn set_shows_vertical_scroll_indicator(&mut self, shows: bool)

Enables or disables the vertical indicator.

Examples found in repository?
src/list_view/render.rs (line 68)
67    pub fn set_shows_vertical_scroll_indicator(&mut self, shows: bool) {
68        self.scroll_view.set_shows_vertical_scroll_indicator(shows);
69    }
Source

pub const fn is_dragging(&self) -> bool

Returns whether a touch drag is active.

Examples found in repository?
src/list_view/interaction.rs (line 20)
12    pub fn handle_touch<'text>(
13        &mut self,
14        touch: TouchEvent,
15        viewport: Rectangle,
16    ) -> ListEvent<Delegate::Message>
17    where
18        Delegate: ListDelegate<'text, DataSource::ItemId>,
19    {
20        if !touch.within(viewport) && !self.scroll_view.is_dragging() {
21            return ListEvent::none();
22        }
23
24        let local_touch = offset_touch(touch, viewport.top_left);
25        let touched_index = self.item_index_at_point(touch.point, viewport);
26        let was_dragging = self.scroll_view.is_dragging();
27        let previous_highlight = self.highlighted_index;
28        let previous_selection = self.selected_index;
29        let mut activity = ListActivity::None;
30        let mut message = None;
31
32        let scrolled = match touch.phase {
33            TouchPhase::Start => {
34                self.touch_started_inside = touch.within(viewport);
35                self.tap_candidate = self.touch_started_inside;
36                self.scroll_view.begin_drag(local_touch);
37                message = message.or(
38                    self.set_highlighted_index(touched_index.filter(|_| self.touch_started_inside))
39                );
40                false
41            }
42            TouchPhase::Move => {
43                let changed =
44                    self.scroll_view
45                        .drag(local_touch, self.content_height(), viewport.size.height);
46                if changed || self.scroll_view.is_scrolling() {
47                    self.tap_candidate = false;
48                    message = message.or(self.set_highlighted_index(None));
49                } else {
50                    let next_highlight = if self.touch_started_inside && touch.within(viewport) {
51                        touched_index
52                    } else {
53                        None
54                    };
55                    message = message.or(self.set_highlighted_index(next_highlight));
56                }
57                changed
58            }
59            TouchPhase::End | TouchPhase::Cancel => {
60                let changed = self.scroll_view.end_drag(
61                    local_touch,
62                    self.content_height(),
63                    viewport.size.height,
64                );
65
66                if matches!(touch.phase, TouchPhase::End)
67                    && self.touch_started_inside
68                    && self.tap_candidate
69                    && touch.within(viewport)
70                {
71                    if let Some(index) = touched_index {
72                        if self.allows_selection {
73                            self.selected_index = Some(index);
74                        }
75                        if let Some(selection) = self.selection_for_index(index) {
76                            message = message.or(self.delegate.did_select_item(selection));
77                        }
78                    }
79                }
80
81                message = message.or(self.set_highlighted_index(None));
82                self.touch_started_inside = false;
83                self.tap_candidate = false;
84                changed
85            }
86        };
87
88        let visual_state_changed = previous_highlight != self.highlighted_index
89            || previous_selection != self.selected_index;
90        let motion_active = scrolled || self.scroll_view.is_scrolling();
91
92        if motion_active {
93            activity = ListActivity::Motion;
94        } else if visual_state_changed {
95            activity = ListActivity::Interactive;
96        }
97
98        ListEvent {
99            redraw: match activity {
100                ListActivity::None => ViewRedraw::None,
101                ListActivity::Interactive | ListActivity::Motion => ViewRedraw::Dirty(viewport),
102            },
103            captured: was_dragging || touch.within(viewport) || self.touch_started_inside,
104            message,
105            activity,
106        }
107    }
Source

pub const fn is_scrolling(&self) -> bool

Returns whether the view is actively scrolling.

Examples found in repository?
src/list_view/interaction.rs (line 46)
12    pub fn handle_touch<'text>(
13        &mut self,
14        touch: TouchEvent,
15        viewport: Rectangle,
16    ) -> ListEvent<Delegate::Message>
17    where
18        Delegate: ListDelegate<'text, DataSource::ItemId>,
19    {
20        if !touch.within(viewport) && !self.scroll_view.is_dragging() {
21            return ListEvent::none();
22        }
23
24        let local_touch = offset_touch(touch, viewport.top_left);
25        let touched_index = self.item_index_at_point(touch.point, viewport);
26        let was_dragging = self.scroll_view.is_dragging();
27        let previous_highlight = self.highlighted_index;
28        let previous_selection = self.selected_index;
29        let mut activity = ListActivity::None;
30        let mut message = None;
31
32        let scrolled = match touch.phase {
33            TouchPhase::Start => {
34                self.touch_started_inside = touch.within(viewport);
35                self.tap_candidate = self.touch_started_inside;
36                self.scroll_view.begin_drag(local_touch);
37                message = message.or(
38                    self.set_highlighted_index(touched_index.filter(|_| self.touch_started_inside))
39                );
40                false
41            }
42            TouchPhase::Move => {
43                let changed =
44                    self.scroll_view
45                        .drag(local_touch, self.content_height(), viewport.size.height);
46                if changed || self.scroll_view.is_scrolling() {
47                    self.tap_candidate = false;
48                    message = message.or(self.set_highlighted_index(None));
49                } else {
50                    let next_highlight = if self.touch_started_inside && touch.within(viewport) {
51                        touched_index
52                    } else {
53                        None
54                    };
55                    message = message.or(self.set_highlighted_index(next_highlight));
56                }
57                changed
58            }
59            TouchPhase::End | TouchPhase::Cancel => {
60                let changed = self.scroll_view.end_drag(
61                    local_touch,
62                    self.content_height(),
63                    viewport.size.height,
64                );
65
66                if matches!(touch.phase, TouchPhase::End)
67                    && self.touch_started_inside
68                    && self.tap_candidate
69                    && touch.within(viewport)
70                {
71                    if let Some(index) = touched_index {
72                        if self.allows_selection {
73                            self.selected_index = Some(index);
74                        }
75                        if let Some(selection) = self.selection_for_index(index) {
76                            message = message.or(self.delegate.did_select_item(selection));
77                        }
78                    }
79                }
80
81                message = message.or(self.set_highlighted_index(None));
82                self.touch_started_inside = false;
83                self.tap_candidate = false;
84                changed
85            }
86        };
87
88        let visual_state_changed = previous_highlight != self.highlighted_index
89            || previous_selection != self.selected_index;
90        let motion_active = scrolled || self.scroll_view.is_scrolling();
91
92        if motion_active {
93            activity = ListActivity::Motion;
94        } else if visual_state_changed {
95            activity = ListActivity::Interactive;
96        }
97
98        ListEvent {
99            redraw: match activity {
100                ListActivity::None => ViewRedraw::None,
101                ListActivity::Interactive | ListActivity::Motion => ViewRedraw::Dirty(viewport),
102            },
103            captured: was_dragging || touch.within(viewport) || self.touch_started_inside,
104            message,
105            activity,
106        }
107    }
Source

pub fn content_offset_y(&self) -> i32

Returns the integer content offset.

Examples found in repository?
src/list_view/render.rs (line 20)
19    pub fn content_offset_y(&self) -> i32 {
20        self.scroll_view.content_offset_y()
21    }
22
23    /// Returns the total content height.
24    pub fn content_height(&self) -> u32 {
25        (0..self.data_source.item_count())
26            .map(|index| self.data_source.item_height(index))
27            .sum()
28    }
29
30    /// Returns the selected row index, if any.
31    pub fn selected_index(&self) -> Option<usize> {
32        self.selected_index
33            .filter(|index| *index < self.data_source.item_count())
34    }
35
36    /// Returns the selected row payload, if any.
37    pub fn selected_item(&self) -> Option<ListSelection<DataSource::ItemId>> {
38        self.selection_for_index(self.selected_index()?)
39    }
40
41    /// Returns the highlighted row payload, if any.
42    pub fn highlighted_item(&self) -> Option<ListSelection<DataSource::ItemId>> {
43        self.selection_for_index(self.highlighted_index?)
44    }
45
46    /// Sets the selected row index.
47    pub fn set_selected_index(&mut self, index: Option<usize>) -> bool {
48        let normalized = index.filter(|index| *index < self.data_source.item_count());
49        if self.selected_index == normalized {
50            return false;
51        }
52        self.selected_index = normalized;
53        true
54    }
55
56    /// Clears row selection.
57    pub fn clear_selection(&mut self) -> bool {
58        self.set_selected_index(None)
59    }
60
61    /// Returns whether the vertical indicator is enabled.
62    pub fn shows_vertical_scroll_indicator(&self) -> bool {
63        self.scroll_view.shows_vertical_scroll_indicator()
64    }
65
66    /// Enables or disables the vertical indicator.
67    pub fn set_shows_vertical_scroll_indicator(&mut self, shows: bool) {
68        self.scroll_view.set_shows_vertical_scroll_indicator(shows);
69    }
70
71    /// Returns the current scrollbar thumb, if visible.
72    pub fn scroll_bar(&self, viewport: Rectangle) -> Option<ScrollBar> {
73        self.scroll_view.scroll_bar(viewport, self.content_height())
74    }
75
76    /// Returns the dirty rect for indicator updates, if any.
77    pub fn scroll_bar_dirty(&self, viewport: Rectangle) -> Option<Rectangle> {
78        self.scroll_view
79            .scroll_bar_dirty(viewport, self.content_height())
80    }
81
82    /// Returns the content rect used for scroll blitting.
83    pub fn motion_content_rect(&self, viewport: Rectangle) -> Rectangle {
84        self.scroll_view
85            .motion_content_rect(viewport, self.content_height())
86    }
87
88    /// Advances list animation state.
89    pub fn tick(&mut self, dt_ms: u32, viewport: Rectangle) -> ViewRedraw {
90        if self
91            .scroll_view
92            .tick(dt_ms, self.content_height(), viewport.size.height)
93        {
94            ViewRedraw::Dirty(viewport)
95        } else {
96            ViewRedraw::None
97        }
98    }
99
100    /// Returns the frame for one item index, even if only partially visible.
101    pub fn item_frame(&self, index: usize, viewport: Rectangle) -> Option<Rectangle> {
102        if index >= self.data_source.item_count() {
103            return None;
104        }
105
106        let mut cursor = self.scroll_view.content_offset_y();
107        for current in 0..self.data_source.item_count() {
108            let height = self.data_source.item_height(current);
109            let frame = Rectangle::new(
110                Point::new(viewport.top_left.x, viewport.top_left.y + cursor),
111                Size::new(viewport.size.width, height),
112            );
113            if current == index {
114                return Some(frame);
115            }
116            cursor += height as i32;
117        }
118        None
119    }
120
121    /// Draws the list and its scroll indicator.
122    pub fn draw<'text, D>(
123        &self,
124        display: &mut D,
125        viewport: Rectangle,
126        env: &ViewEnvironment<'_, 'text>,
127    ) where
128        D: DrawTarget<Color = Rgb565>,
129        Delegate: ListDelegate<'text, DataSource::ItemId>,
130    {
131        let mut cursor = self.scroll_view.content_offset_y();
132        let mut clipped = display.clipped(&viewport);
133        for index in 0..self.data_source.item_count() {
134            let height = self.data_source.item_height(index);
135            let frame = Rectangle::new(
136                Point::new(viewport.top_left.x, viewport.top_left.y + cursor),
137                Size::new(viewport.size.width, height),
138            );
139            if rects_intersect(frame, viewport) {
140                self.delegate.draw_row(
141                    &mut clipped,
142                    ListRow {
143                        item: ListItem {
144                            id: self.data_source.item_id(index),
145                            index,
146                            frame,
147                        },
148                        state: ListRowState {
149                            selected: self.selected_index() == Some(index),
150                            highlighted: self.highlighted_index == Some(index),
151                        },
152                    },
153                    env,
154                );
155            }
156            cursor += height as i32;
157        }
158        self.scroll_view
159            .draw_scroll_bar(display, viewport, self.content_height(), env.theme);
160    }
More examples
Hide additional examples
src/list_view/interaction.rs (line 123)
118    fn item_index_at_point(&self, point: Point, viewport: Rectangle) -> Option<usize> {
119        if !point_in_rect(point, viewport) {
120            return None;
121        }
122
123        let mut cursor = self.scroll_view.content_offset_y();
124        for index in 0..self.data_source.item_count() {
125            let height = self.data_source.item_height(index);
126            let frame = Rectangle::new(
127                Point::new(viewport.top_left.x, viewport.top_left.y + cursor),
128                embedded_graphics::prelude::Size::new(viewport.size.width, height),
129            );
130            if point_in_rect(point, frame) {
131                return Some(index);
132            }
133            cursor += height as i32;
134        }
135        None
136    }
Source

pub fn begin_drag(&mut self, touch: TouchEvent)

Starts a drag sequence.

Examples found in repository?
examples/scroll_and_list.rs (line 65)
60fn main() {
61    let mut canvas = support::NullCanvas::new(Size::new(320, 240));
62    let viewport = Rectangle::new(Point::new(16, 16), Size::new(180, 160));
63
64    let mut scroll = ScrollView::new();
65    scroll.begin_drag(TouchEvent::new(Point::new(8, 8), TouchPhase::Start, 1));
66    let _ = scroll.drag(
67        TouchEvent::new(Point::new(8, 32), TouchPhase::Move, 2),
68        420,
69        160,
70    );
71    let _ = scroll.end_drag(
72        TouchEvent::new(Point::new(8, 32), TouchPhase::End, 3),
73        420,
74        160,
75    );
76    let _ = scroll.tick(16, 420, 160);
77    let _ = scroll.scroll_bar(viewport, 420);
78
79    let mut list = ListView::new(DemoDataSource, DemoDelegate);
80    let _ = list.handle_touch(
81        TouchEvent::new(Point::new(24, 24), TouchPhase::Start, 10),
82        viewport,
83    );
84    let _ = list.handle_touch(
85        TouchEvent::new(Point::new(24, 60), TouchPhase::Move, 11),
86        viewport,
87    );
88    let _ = list.handle_touch(
89        TouchEvent::new(Point::new(24, 60), TouchPhase::End, 12),
90        viewport,
91    );
92    list.draw(
93        &mut canvas,
94        viewport,
95        &ViewEnvironment {
96            theme: &support::theme(),
97            i18n: &support::i18n(),
98        },
99    );
100}
More examples
Hide additional examples
src/list_view/interaction.rs (line 36)
12    pub fn handle_touch<'text>(
13        &mut self,
14        touch: TouchEvent,
15        viewport: Rectangle,
16    ) -> ListEvent<Delegate::Message>
17    where
18        Delegate: ListDelegate<'text, DataSource::ItemId>,
19    {
20        if !touch.within(viewport) && !self.scroll_view.is_dragging() {
21            return ListEvent::none();
22        }
23
24        let local_touch = offset_touch(touch, viewport.top_left);
25        let touched_index = self.item_index_at_point(touch.point, viewport);
26        let was_dragging = self.scroll_view.is_dragging();
27        let previous_highlight = self.highlighted_index;
28        let previous_selection = self.selected_index;
29        let mut activity = ListActivity::None;
30        let mut message = None;
31
32        let scrolled = match touch.phase {
33            TouchPhase::Start => {
34                self.touch_started_inside = touch.within(viewport);
35                self.tap_candidate = self.touch_started_inside;
36                self.scroll_view.begin_drag(local_touch);
37                message = message.or(
38                    self.set_highlighted_index(touched_index.filter(|_| self.touch_started_inside))
39                );
40                false
41            }
42            TouchPhase::Move => {
43                let changed =
44                    self.scroll_view
45                        .drag(local_touch, self.content_height(), viewport.size.height);
46                if changed || self.scroll_view.is_scrolling() {
47                    self.tap_candidate = false;
48                    message = message.or(self.set_highlighted_index(None));
49                } else {
50                    let next_highlight = if self.touch_started_inside && touch.within(viewport) {
51                        touched_index
52                    } else {
53                        None
54                    };
55                    message = message.or(self.set_highlighted_index(next_highlight));
56                }
57                changed
58            }
59            TouchPhase::End | TouchPhase::Cancel => {
60                let changed = self.scroll_view.end_drag(
61                    local_touch,
62                    self.content_height(),
63                    viewport.size.height,
64                );
65
66                if matches!(touch.phase, TouchPhase::End)
67                    && self.touch_started_inside
68                    && self.tap_candidate
69                    && touch.within(viewport)
70                {
71                    if let Some(index) = touched_index {
72                        if self.allows_selection {
73                            self.selected_index = Some(index);
74                        }
75                        if let Some(selection) = self.selection_for_index(index) {
76                            message = message.or(self.delegate.did_select_item(selection));
77                        }
78                    }
79                }
80
81                message = message.or(self.set_highlighted_index(None));
82                self.touch_started_inside = false;
83                self.tap_candidate = false;
84                changed
85            }
86        };
87
88        let visual_state_changed = previous_highlight != self.highlighted_index
89            || previous_selection != self.selected_index;
90        let motion_active = scrolled || self.scroll_view.is_scrolling();
91
92        if motion_active {
93            activity = ListActivity::Motion;
94        } else if visual_state_changed {
95            activity = ListActivity::Interactive;
96        }
97
98        ListEvent {
99            redraw: match activity {
100                ListActivity::None => ViewRedraw::None,
101                ListActivity::Interactive | ListActivity::Motion => ViewRedraw::Dirty(viewport),
102            },
103            captured: was_dragging || touch.within(viewport) || self.touch_started_inside,
104            message,
105            activity,
106        }
107    }
Source

pub fn drag( &mut self, touch: TouchEvent, content_height: u32, viewport_height: u32, ) -> bool

Updates dragging with a new touch sample.

Examples found in repository?
examples/scroll_and_list.rs (lines 66-70)
60fn main() {
61    let mut canvas = support::NullCanvas::new(Size::new(320, 240));
62    let viewport = Rectangle::new(Point::new(16, 16), Size::new(180, 160));
63
64    let mut scroll = ScrollView::new();
65    scroll.begin_drag(TouchEvent::new(Point::new(8, 8), TouchPhase::Start, 1));
66    let _ = scroll.drag(
67        TouchEvent::new(Point::new(8, 32), TouchPhase::Move, 2),
68        420,
69        160,
70    );
71    let _ = scroll.end_drag(
72        TouchEvent::new(Point::new(8, 32), TouchPhase::End, 3),
73        420,
74        160,
75    );
76    let _ = scroll.tick(16, 420, 160);
77    let _ = scroll.scroll_bar(viewport, 420);
78
79    let mut list = ListView::new(DemoDataSource, DemoDelegate);
80    let _ = list.handle_touch(
81        TouchEvent::new(Point::new(24, 24), TouchPhase::Start, 10),
82        viewport,
83    );
84    let _ = list.handle_touch(
85        TouchEvent::new(Point::new(24, 60), TouchPhase::Move, 11),
86        viewport,
87    );
88    let _ = list.handle_touch(
89        TouchEvent::new(Point::new(24, 60), TouchPhase::End, 12),
90        viewport,
91    );
92    list.draw(
93        &mut canvas,
94        viewport,
95        &ViewEnvironment {
96            theme: &support::theme(),
97            i18n: &support::i18n(),
98        },
99    );
100}
More examples
Hide additional examples
src/list_view/interaction.rs (line 45)
12    pub fn handle_touch<'text>(
13        &mut self,
14        touch: TouchEvent,
15        viewport: Rectangle,
16    ) -> ListEvent<Delegate::Message>
17    where
18        Delegate: ListDelegate<'text, DataSource::ItemId>,
19    {
20        if !touch.within(viewport) && !self.scroll_view.is_dragging() {
21            return ListEvent::none();
22        }
23
24        let local_touch = offset_touch(touch, viewport.top_left);
25        let touched_index = self.item_index_at_point(touch.point, viewport);
26        let was_dragging = self.scroll_view.is_dragging();
27        let previous_highlight = self.highlighted_index;
28        let previous_selection = self.selected_index;
29        let mut activity = ListActivity::None;
30        let mut message = None;
31
32        let scrolled = match touch.phase {
33            TouchPhase::Start => {
34                self.touch_started_inside = touch.within(viewport);
35                self.tap_candidate = self.touch_started_inside;
36                self.scroll_view.begin_drag(local_touch);
37                message = message.or(
38                    self.set_highlighted_index(touched_index.filter(|_| self.touch_started_inside))
39                );
40                false
41            }
42            TouchPhase::Move => {
43                let changed =
44                    self.scroll_view
45                        .drag(local_touch, self.content_height(), viewport.size.height);
46                if changed || self.scroll_view.is_scrolling() {
47                    self.tap_candidate = false;
48                    message = message.or(self.set_highlighted_index(None));
49                } else {
50                    let next_highlight = if self.touch_started_inside && touch.within(viewport) {
51                        touched_index
52                    } else {
53                        None
54                    };
55                    message = message.or(self.set_highlighted_index(next_highlight));
56                }
57                changed
58            }
59            TouchPhase::End | TouchPhase::Cancel => {
60                let changed = self.scroll_view.end_drag(
61                    local_touch,
62                    self.content_height(),
63                    viewport.size.height,
64                );
65
66                if matches!(touch.phase, TouchPhase::End)
67                    && self.touch_started_inside
68                    && self.tap_candidate
69                    && touch.within(viewport)
70                {
71                    if let Some(index) = touched_index {
72                        if self.allows_selection {
73                            self.selected_index = Some(index);
74                        }
75                        if let Some(selection) = self.selection_for_index(index) {
76                            message = message.or(self.delegate.did_select_item(selection));
77                        }
78                    }
79                }
80
81                message = message.or(self.set_highlighted_index(None));
82                self.touch_started_inside = false;
83                self.tap_candidate = false;
84                changed
85            }
86        };
87
88        let visual_state_changed = previous_highlight != self.highlighted_index
89            || previous_selection != self.selected_index;
90        let motion_active = scrolled || self.scroll_view.is_scrolling();
91
92        if motion_active {
93            activity = ListActivity::Motion;
94        } else if visual_state_changed {
95            activity = ListActivity::Interactive;
96        }
97
98        ListEvent {
99            redraw: match activity {
100                ListActivity::None => ViewRedraw::None,
101                ListActivity::Interactive | ListActivity::Motion => ViewRedraw::Dirty(viewport),
102            },
103            captured: was_dragging || touch.within(viewport) || self.touch_started_inside,
104            message,
105            activity,
106        }
107    }
Source

pub fn end_drag( &mut self, touch: TouchEvent, content_height: u32, viewport_height: u32, ) -> bool

Ends dragging and starts any fling animation.

Examples found in repository?
examples/scroll_and_list.rs (lines 71-75)
60fn main() {
61    let mut canvas = support::NullCanvas::new(Size::new(320, 240));
62    let viewport = Rectangle::new(Point::new(16, 16), Size::new(180, 160));
63
64    let mut scroll = ScrollView::new();
65    scroll.begin_drag(TouchEvent::new(Point::new(8, 8), TouchPhase::Start, 1));
66    let _ = scroll.drag(
67        TouchEvent::new(Point::new(8, 32), TouchPhase::Move, 2),
68        420,
69        160,
70    );
71    let _ = scroll.end_drag(
72        TouchEvent::new(Point::new(8, 32), TouchPhase::End, 3),
73        420,
74        160,
75    );
76    let _ = scroll.tick(16, 420, 160);
77    let _ = scroll.scroll_bar(viewport, 420);
78
79    let mut list = ListView::new(DemoDataSource, DemoDelegate);
80    let _ = list.handle_touch(
81        TouchEvent::new(Point::new(24, 24), TouchPhase::Start, 10),
82        viewport,
83    );
84    let _ = list.handle_touch(
85        TouchEvent::new(Point::new(24, 60), TouchPhase::Move, 11),
86        viewport,
87    );
88    let _ = list.handle_touch(
89        TouchEvent::new(Point::new(24, 60), TouchPhase::End, 12),
90        viewport,
91    );
92    list.draw(
93        &mut canvas,
94        viewport,
95        &ViewEnvironment {
96            theme: &support::theme(),
97            i18n: &support::i18n(),
98        },
99    );
100}
More examples
Hide additional examples
src/list_view/interaction.rs (lines 60-64)
12    pub fn handle_touch<'text>(
13        &mut self,
14        touch: TouchEvent,
15        viewport: Rectangle,
16    ) -> ListEvent<Delegate::Message>
17    where
18        Delegate: ListDelegate<'text, DataSource::ItemId>,
19    {
20        if !touch.within(viewport) && !self.scroll_view.is_dragging() {
21            return ListEvent::none();
22        }
23
24        let local_touch = offset_touch(touch, viewport.top_left);
25        let touched_index = self.item_index_at_point(touch.point, viewport);
26        let was_dragging = self.scroll_view.is_dragging();
27        let previous_highlight = self.highlighted_index;
28        let previous_selection = self.selected_index;
29        let mut activity = ListActivity::None;
30        let mut message = None;
31
32        let scrolled = match touch.phase {
33            TouchPhase::Start => {
34                self.touch_started_inside = touch.within(viewport);
35                self.tap_candidate = self.touch_started_inside;
36                self.scroll_view.begin_drag(local_touch);
37                message = message.or(
38                    self.set_highlighted_index(touched_index.filter(|_| self.touch_started_inside))
39                );
40                false
41            }
42            TouchPhase::Move => {
43                let changed =
44                    self.scroll_view
45                        .drag(local_touch, self.content_height(), viewport.size.height);
46                if changed || self.scroll_view.is_scrolling() {
47                    self.tap_candidate = false;
48                    message = message.or(self.set_highlighted_index(None));
49                } else {
50                    let next_highlight = if self.touch_started_inside && touch.within(viewport) {
51                        touched_index
52                    } else {
53                        None
54                    };
55                    message = message.or(self.set_highlighted_index(next_highlight));
56                }
57                changed
58            }
59            TouchPhase::End | TouchPhase::Cancel => {
60                let changed = self.scroll_view.end_drag(
61                    local_touch,
62                    self.content_height(),
63                    viewport.size.height,
64                );
65
66                if matches!(touch.phase, TouchPhase::End)
67                    && self.touch_started_inside
68                    && self.tap_candidate
69                    && touch.within(viewport)
70                {
71                    if let Some(index) = touched_index {
72                        if self.allows_selection {
73                            self.selected_index = Some(index);
74                        }
75                        if let Some(selection) = self.selection_for_index(index) {
76                            message = message.or(self.delegate.did_select_item(selection));
77                        }
78                    }
79                }
80
81                message = message.or(self.set_highlighted_index(None));
82                self.touch_started_inside = false;
83                self.tap_candidate = false;
84                changed
85            }
86        };
87
88        let visual_state_changed = previous_highlight != self.highlighted_index
89            || previous_selection != self.selected_index;
90        let motion_active = scrolled || self.scroll_view.is_scrolling();
91
92        if motion_active {
93            activity = ListActivity::Motion;
94        } else if visual_state_changed {
95            activity = ListActivity::Interactive;
96        }
97
98        ListEvent {
99            redraw: match activity {
100                ListActivity::None => ViewRedraw::None,
101                ListActivity::Interactive | ListActivity::Motion => ViewRedraw::Dirty(viewport),
102            },
103            captured: was_dragging || touch.within(viewport) || self.touch_started_inside,
104            message,
105            activity,
106        }
107    }
Source

pub fn tick( &mut self, dt_ms: u32, content_height: u32, viewport_height: u32, ) -> bool

Advances animation and indicator fade state.

Examples found in repository?
examples/scroll_and_list.rs (line 76)
60fn main() {
61    let mut canvas = support::NullCanvas::new(Size::new(320, 240));
62    let viewport = Rectangle::new(Point::new(16, 16), Size::new(180, 160));
63
64    let mut scroll = ScrollView::new();
65    scroll.begin_drag(TouchEvent::new(Point::new(8, 8), TouchPhase::Start, 1));
66    let _ = scroll.drag(
67        TouchEvent::new(Point::new(8, 32), TouchPhase::Move, 2),
68        420,
69        160,
70    );
71    let _ = scroll.end_drag(
72        TouchEvent::new(Point::new(8, 32), TouchPhase::End, 3),
73        420,
74        160,
75    );
76    let _ = scroll.tick(16, 420, 160);
77    let _ = scroll.scroll_bar(viewport, 420);
78
79    let mut list = ListView::new(DemoDataSource, DemoDelegate);
80    let _ = list.handle_touch(
81        TouchEvent::new(Point::new(24, 24), TouchPhase::Start, 10),
82        viewport,
83    );
84    let _ = list.handle_touch(
85        TouchEvent::new(Point::new(24, 60), TouchPhase::Move, 11),
86        viewport,
87    );
88    let _ = list.handle_touch(
89        TouchEvent::new(Point::new(24, 60), TouchPhase::End, 12),
90        viewport,
91    );
92    list.draw(
93        &mut canvas,
94        viewport,
95        &ViewEnvironment {
96            theme: &support::theme(),
97            i18n: &support::i18n(),
98        },
99    );
100}
More examples
Hide additional examples
src/list_view/render.rs (line 92)
89    pub fn tick(&mut self, dt_ms: u32, viewport: Rectangle) -> ViewRedraw {
90        if self
91            .scroll_view
92            .tick(dt_ms, self.content_height(), viewport.size.height)
93        {
94            ViewRedraw::Dirty(viewport)
95        } else {
96            ViewRedraw::None
97        }
98    }
Source

pub fn scroll_bar( &self, viewport: Rectangle, content_height: u32, ) -> Option<ScrollBar>

Returns the current scrollbar thumb geometry, if visible.

Examples found in repository?
examples/scroll_and_list.rs (line 77)
60fn main() {
61    let mut canvas = support::NullCanvas::new(Size::new(320, 240));
62    let viewport = Rectangle::new(Point::new(16, 16), Size::new(180, 160));
63
64    let mut scroll = ScrollView::new();
65    scroll.begin_drag(TouchEvent::new(Point::new(8, 8), TouchPhase::Start, 1));
66    let _ = scroll.drag(
67        TouchEvent::new(Point::new(8, 32), TouchPhase::Move, 2),
68        420,
69        160,
70    );
71    let _ = scroll.end_drag(
72        TouchEvent::new(Point::new(8, 32), TouchPhase::End, 3),
73        420,
74        160,
75    );
76    let _ = scroll.tick(16, 420, 160);
77    let _ = scroll.scroll_bar(viewport, 420);
78
79    let mut list = ListView::new(DemoDataSource, DemoDelegate);
80    let _ = list.handle_touch(
81        TouchEvent::new(Point::new(24, 24), TouchPhase::Start, 10),
82        viewport,
83    );
84    let _ = list.handle_touch(
85        TouchEvent::new(Point::new(24, 60), TouchPhase::Move, 11),
86        viewport,
87    );
88    let _ = list.handle_touch(
89        TouchEvent::new(Point::new(24, 60), TouchPhase::End, 12),
90        viewport,
91    );
92    list.draw(
93        &mut canvas,
94        viewport,
95        &ViewEnvironment {
96            theme: &support::theme(),
97            i18n: &support::i18n(),
98        },
99    );
100}
More examples
Hide additional examples
src/list_view/render.rs (line 73)
72    pub fn scroll_bar(&self, viewport: Rectangle) -> Option<ScrollBar> {
73        self.scroll_view.scroll_bar(viewport, self.content_height())
74    }
src/scroll_view/mod.rs (line 167)
158    pub fn draw_scroll_bar<D>(
159        &self,
160        display: &mut D,
161        viewport: Rectangle,
162        content_height: u32,
163        theme: &FsTheme,
164    ) where
165        D: DrawTarget<Color = Rgb565>,
166    {
167        if let Some(indicator) = self.scroll_bar(viewport, content_height) {
168            draw_scrollbar(display, indicator, theme);
169        }
170    }
Source

pub fn scroll_bar_dirty( &self, viewport: Rectangle, content_height: u32, ) -> Option<Rectangle>

Returns the viewport area that may be affected by scrollbar updates.

Examples found in repository?
src/list_view/render.rs (line 79)
77    pub fn scroll_bar_dirty(&self, viewport: Rectangle) -> Option<Rectangle> {
78        self.scroll_view
79            .scroll_bar_dirty(viewport, self.content_height())
80    }
Source

pub fn motion_content_rect( &self, viewport: Rectangle, content_height: u32, ) -> Rectangle

Returns the motion content rect, excluding reserved indicator gutter.

Examples found in repository?
src/list_view/render.rs (line 85)
83    pub fn motion_content_rect(&self, viewport: Rectangle) -> Rectangle {
84        self.scroll_view
85            .motion_content_rect(viewport, self.content_height())
86    }
Source

pub fn draw_scroll_bar<D>( &self, display: &mut D, viewport: Rectangle, content_height: u32, theme: &FsTheme, )
where D: DrawTarget<Color = Rgb565>,

Draws the vertical scrollbar thumb.

Examples found in repository?
src/list_view/render.rs (line 159)
122    pub fn draw<'text, D>(
123        &self,
124        display: &mut D,
125        viewport: Rectangle,
126        env: &ViewEnvironment<'_, 'text>,
127    ) where
128        D: DrawTarget<Color = Rgb565>,
129        Delegate: ListDelegate<'text, DataSource::ItemId>,
130    {
131        let mut cursor = self.scroll_view.content_offset_y();
132        let mut clipped = display.clipped(&viewport);
133        for index in 0..self.data_source.item_count() {
134            let height = self.data_source.item_height(index);
135            let frame = Rectangle::new(
136                Point::new(viewport.top_left.x, viewport.top_left.y + cursor),
137                Size::new(viewport.size.width, height),
138            );
139            if rects_intersect(frame, viewport) {
140                self.delegate.draw_row(
141                    &mut clipped,
142                    ListRow {
143                        item: ListItem {
144                            id: self.data_source.item_id(index),
145                            index,
146                            frame,
147                        },
148                        state: ListRowState {
149                            selected: self.selected_index() == Some(index),
150                            highlighted: self.highlighted_index == Some(index),
151                        },
152                    },
153                    env,
154                );
155            }
156            cursor += height as i32;
157        }
158        self.scroll_view
159            .draw_scroll_bar(display, viewport, self.content_height(), env.theme);
160    }

Trait Implementations§

Source§

impl Clone for ScrollView

Source§

fn clone(&self) -> ScrollView

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ScrollView

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for ScrollView

Source§

fn default() -> ScrollView

Returns the “default value” for a type. Read more
Source§

impl Copy for ScrollView

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Az for T

Source§

fn az<Dst>(self) -> Dst
where T: Cast<Dst>,

Casts the value.
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<Src, Dst> CastFrom<Src> for Dst
where Src: Cast<Dst>,

Source§

fn cast_from(src: Src) -> Dst

Casts the value.
Source§

impl<T> CheckedAs for T

Source§

fn checked_as<Dst>(self) -> Option<Dst>
where T: CheckedCast<Dst>,

Casts the value.
Source§

impl<Src, Dst> CheckedCastFrom<Src> for Dst
where Src: CheckedCast<Dst>,

Source§

fn checked_cast_from(src: Src) -> Option<Dst>

Casts the value.
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> OverflowingAs for T

Source§

fn overflowing_as<Dst>(self) -> (Dst, bool)
where T: OverflowingCast<Dst>,

Casts the value.
Source§

impl<Src, Dst> OverflowingCastFrom<Src> for Dst
where Src: OverflowingCast<Dst>,

Source§

fn overflowing_cast_from(src: Src) -> (Dst, bool)

Casts the value.
Source§

impl<T> SaturatingAs for T

Source§

fn saturating_as<Dst>(self) -> Dst
where T: SaturatingCast<Dst>,

Casts the value.
Source§

impl<Src, Dst> SaturatingCastFrom<Src> for Dst
where Src: SaturatingCast<Dst>,

Source§

fn saturating_cast_from(src: Src) -> Dst

Casts the value.
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> UnwrappedAs for T

Source§

fn unwrapped_as<Dst>(self) -> Dst
where T: UnwrappedCast<Dst>,

Casts the value.
Source§

impl<Src, Dst> UnwrappedCastFrom<Src> for Dst
where Src: UnwrappedCast<Dst>,

Source§

fn unwrapped_cast_from(src: Src) -> Dst

Casts the value.
Source§

impl<T> WrappingAs for T

Source§

fn wrapping_as<Dst>(self) -> Dst
where T: WrappingCast<Dst>,

Casts the value.
Source§

impl<Src, Dst> WrappingCastFrom<Src> for Dst
where Src: WrappingCast<Dst>,

Source§

fn wrapping_cast_from(src: Src) -> Dst

Casts the value.