use std::sync::Arc;
use crate::layout::style::FlexStyle;
use crate::layout::types::FlexAlign;
use crate::state::State;
use crate::view::node::ViewNode;
use crate::view::paint::PaintStyle;
use crate::widget::layout::LayoutAttr;
use crate::widget::scroll_bar::ScrollBar;
use crate::widget::virtual_list::VirtualList;
use crate::widget::{BuildContext, Stateful, Widget};
pub struct ListView {
height: f32,
item_count: usize,
item_height: f32,
item: Arc<dyn Fn(usize) -> Box<dyn Widget>>,
overscan: usize,
scrollbar_thickness: Option<f32>,
layout: LayoutAttr,
}
impl ListView {
pub fn new(
height: f32,
item_count: usize,
item_height: f32,
item: impl Fn(usize) -> Box<dyn Widget> + 'static,
) -> Self {
Self {
height,
item_count,
item_height,
item: Arc::new(item),
overscan: 3,
scrollbar_thickness: Some(8.0),
layout: LayoutAttr::new(),
}
}
pub fn overscan(mut self, n: usize) -> Self {
self.overscan = n;
self
}
pub fn scrollbar(mut self, thickness: Option<f32>) -> Self {
self.scrollbar_thickness = thickness;
self
}
pub fn no_scrollbar(mut self) -> Self {
self.scrollbar_thickness = None;
self
}
pub fn flex_shrink(mut self, v: f32) -> Self {
self.layout = self.layout.flex_shrink(v);
self
}
pub fn layout(mut self, l: LayoutAttr) -> Self {
self.layout = l;
self
}
}
impl Widget for ListView {
fn build(&self, ctx: &mut BuildContext) -> ViewNode {
let scroll_state: Stateful<Option<State<(f32, f32)>>> = ctx.use_state(|| None);
let has_state = scroll_state.get().is_some();
let scroll = if has_state {
(*scroll_state.get()).clone().unwrap()
} else {
let s = State::new((0.0, 0.0));
scroll_state.set(Some(s.clone()));
s
};
let total = self.item_count as f32 * self.item_height;
let flex_shrink = self.layout.flex_shrink.unwrap_or(1.0);
let list_widget = VirtualList::new(
self.height,
self.item_count,
self.item_height,
scroll.clone(),
)
.item_arc(self.item.clone())
.overscan(self.overscan)
.flex_shrink(flex_shrink);
match self.scrollbar_thickness {
Some(t) if t > 0.0 => {
let bar_widget =
ScrollBar::vertical(self.height, scroll, self.height, total).thickness(t);
let list_node = ctx.child(0, &list_widget);
let bar_node = ctx.child(1, &bar_widget);
let mut layout = FlexStyle::row().gap(0.0).align_items(FlexAlign::Stretch);
layout = self.layout.apply(layout);
ViewNode::Div {
layout,
paint: PaintStyle::new(),
children: vec![list_node, bar_node],
listeners: vec![],
key: None,
}
}
_ => {
ctx.child(0, &list_widget)
}
}
}
}