use embedded_graphics::{
draw_target::{DrawTarget, DrawTargetExt},
pixelcolor::Rgb565,
prelude::{Point, Size},
primitives::Rectangle,
};
use crate::{ScrollBar, ViewEnvironment, ViewRedraw};
use super::{
ListDataSource, ListDelegate, ListItem, ListRow, ListRowState, ListSelection, ListView,
};
impl<DataSource, Delegate> ListView<DataSource, Delegate>
where
DataSource: ListDataSource,
{
pub fn content_offset_y(&self) -> i32 {
self.scroll_view.content_offset_y()
}
pub fn content_height(&self) -> u32 {
(0..self.data_source.item_count())
.map(|index| self.data_source.item_height(index))
.sum()
}
pub fn selected_index(&self) -> Option<usize> {
self.selected_index
.filter(|index| *index < self.data_source.item_count())
}
pub fn selected_item(&self) -> Option<ListSelection<DataSource::ItemId>> {
self.selection_for_index(self.selected_index()?)
}
pub fn highlighted_item(&self) -> Option<ListSelection<DataSource::ItemId>> {
self.selection_for_index(self.highlighted_index?)
}
pub fn set_selected_index(&mut self, index: Option<usize>) -> bool {
let normalized = index.filter(|index| *index < self.data_source.item_count());
if self.selected_index == normalized {
return false;
}
self.selected_index = normalized;
true
}
pub fn clear_selection(&mut self) -> bool {
self.set_selected_index(None)
}
pub fn shows_vertical_scroll_indicator(&self) -> bool {
self.scroll_view.shows_vertical_scroll_indicator()
}
pub fn set_shows_vertical_scroll_indicator(&mut self, shows: bool) {
self.scroll_view.set_shows_vertical_scroll_indicator(shows);
}
pub fn scroll_bar(&self, viewport: Rectangle) -> Option<ScrollBar> {
self.scroll_view.scroll_bar(viewport, self.content_height())
}
pub fn scroll_bar_dirty(&self, viewport: Rectangle) -> Option<Rectangle> {
self.scroll_view
.scroll_bar_dirty(viewport, self.content_height())
}
pub fn motion_content_rect(&self, viewport: Rectangle) -> Rectangle {
self.scroll_view
.motion_content_rect(viewport, self.content_height())
}
pub fn tick(&mut self, dt_ms: u32, viewport: Rectangle) -> ViewRedraw {
if self
.scroll_view
.tick(dt_ms, self.content_height(), viewport.size.height)
{
ViewRedraw::Dirty(viewport)
} else {
ViewRedraw::None
}
}
pub fn item_frame(&self, index: usize, viewport: Rectangle) -> Option<Rectangle> {
if index >= self.data_source.item_count() {
return None;
}
let mut cursor = self.scroll_view.content_offset_y();
for current in 0..self.data_source.item_count() {
let height = self.data_source.item_height(current);
let frame = Rectangle::new(
Point::new(viewport.top_left.x, viewport.top_left.y + cursor),
Size::new(viewport.size.width, height),
);
if current == index {
return Some(frame);
}
cursor += height as i32;
}
None
}
pub fn draw<'text, D>(
&self,
display: &mut D,
viewport: Rectangle,
env: &ViewEnvironment<'_, 'text>,
) where
D: DrawTarget<Color = Rgb565>,
Delegate: ListDelegate<'text, DataSource::ItemId>,
{
let mut cursor = self.scroll_view.content_offset_y();
let mut clipped = display.clipped(&viewport);
for index in 0..self.data_source.item_count() {
let height = self.data_source.item_height(index);
let frame = Rectangle::new(
Point::new(viewport.top_left.x, viewport.top_left.y + cursor),
Size::new(viewport.size.width, height),
);
if rects_intersect(frame, viewport) {
self.delegate.draw_row(
&mut clipped,
ListRow {
item: ListItem {
id: self.data_source.item_id(index),
index,
frame,
},
state: ListRowState {
selected: self.selected_index() == Some(index),
highlighted: self.highlighted_index == Some(index),
},
},
env,
);
}
cursor += height as i32;
}
self.scroll_view
.draw_scroll_bar(display, viewport, self.content_height(), env.theme);
}
}
fn rects_intersect(left: Rectangle, right: Rectangle) -> bool {
let left_right = left.top_left.x + left.size.width as i32;
let left_bottom = left.top_left.y + left.size.height as i32;
let right_right = right.top_left.x + right.size.width as i32;
let right_bottom = right.top_left.y + right.size.height as i32;
left.top_left.x < right_right
&& left_right > right.top_left.x
&& left.top_left.y < right_bottom
&& left_bottom > right.top_left.y
}