use crate::layout::style::FlexStyle;
use crate::state::State;
use crate::theme::current;
use crate::view::node::ViewNode;
use crate::view::paint::PaintStyle;
use crate::widget::layout::LayoutAttr;
use crate::widget::{BuildContext, Widget};
use std::sync::Arc;
#[derive(Clone)]
pub struct VirtualList {
height: f32,
item_count: usize,
item_height: f32,
scroll_state: State<(f32, f32)>,
item: Arc<dyn Fn(usize) -> Box<dyn Widget>>,
overscan: usize,
layout: LayoutAttr,
}
impl VirtualList {
pub fn new(
height: f32,
item_count: usize,
item_height: f32,
scroll_state: State<(f32, f32)>,
) -> Self {
Self {
height,
item_count,
item_height,
scroll_state,
item: Arc::new(|_| Box::new(Empty)),
overscan: 3,
layout: LayoutAttr::new(),
}
}
pub fn item(mut self, f: impl Fn(usize) -> Box<dyn Widget> + 'static) -> Self {
self.item = Arc::new(f);
self
}
pub fn item_arc(mut self, f: Arc<dyn Fn(usize) -> Box<dyn Widget>>) -> Self {
self.item = f;
self
}
pub fn overscan(mut self, n: usize) -> Self {
self.overscan = n;
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
}
}
struct Empty;
impl Widget for Empty {
fn build(&self, _ctx: &mut BuildContext) -> ViewNode {
ViewNode::Div {
layout: FlexStyle::default(),
paint: PaintStyle::new(),
children: vec![],
listeners: vec![],
key: None,
}
}
}
impl Widget for VirtualList {
fn build(&self, ctx: &mut BuildContext) -> ViewNode {
let sy = self.scroll_state.get().1; let ih = self.item_height;
let h = self.height;
let count = self.item_count;
let total = count as f32 * ih;
let first = if ih > 0.0 {
(((sy / ih).floor() as usize).saturating_sub(self.overscan)).min(count)
} else {
0
};
let last = if ih > 0.0 {
(((sy + h) / ih).ceil() as usize + self.overscan).min(count)
} else {
count
};
let mut items = Vec::with_capacity(last.saturating_sub(first));
for i in first..last {
let row_widget = (*self.item)(i);
let node = ctx.child(i, &*row_widget);
items.push(ViewNode::Div {
layout: FlexStyle::default().height(ih).flex_shrink(0.0),
paint: PaintStyle::new(),
children: vec![node],
listeners: vec![],
key: Some(format!("row-{i}")),
});
}
let content_box = ViewNode::Div {
layout: FlexStyle::column().flex_shrink(0.0),
paint: PaintStyle::new(),
children: items,
listeners: vec![],
key: Some("content".into()),
};
let mut layout = FlexStyle::default()
.height(h)
.overflow_scroll()
.content_height(total)
.bind_scroll_state(&self.scroll_state);
layout = self.layout.apply(layout);
ViewNode::Div {
layout,
paint: PaintStyle::new()
.background(current().background.primary_default)
.clip(true),
children: vec![content_box],
listeners: vec![],
key: None,
}
}
}