use crate::components::Box;
use crate::core::{BorderStyle, Color, Element, FlexDirection, Overflow};
#[derive(Debug, Clone, Default)]
pub struct ScrollableBox {
inner: Box,
show_scrollbar: bool,
scrollbar_color: Option<Color>,
}
impl ScrollableBox {
pub fn new() -> Self {
let inner = Box::new()
.overflow_y(Overflow::Hidden)
.flex_direction(FlexDirection::Column);
Self {
inner,
show_scrollbar: false,
scrollbar_color: None,
}
}
pub fn height(mut self, height: impl Into<crate::core::Dimension>) -> Self {
self.inner = self.inner.height(height);
self
}
pub fn width(mut self, width: impl Into<crate::core::Dimension>) -> Self {
self.inner = self.inner.width(width);
self
}
pub fn scroll_offset_y(mut self, offset: u16) -> Self {
self.inner = self.inner.scroll_offset_y(offset);
self
}
pub fn scroll_offset_x(mut self, offset: u16) -> Self {
self.inner = self.inner.scroll_offset_x(offset);
self
}
pub fn flex_grow(mut self, grow: f32) -> Self {
self.inner = self.inner.flex_grow(grow);
self
}
pub fn flex_direction(mut self, direction: FlexDirection) -> Self {
self.inner = self.inner.flex_direction(direction);
self
}
pub fn background(mut self, color: Color) -> Self {
self.inner = self.inner.background(color);
self
}
pub fn border_style(mut self, style: BorderStyle) -> Self {
self.inner = self.inner.border_style(style);
self
}
pub fn border_color(mut self, color: Color) -> Self {
self.inner = self.inner.border_color(color);
self
}
pub fn padding(mut self, padding: impl Into<crate::core::Edges>) -> Self {
self.inner = self.inner.padding(padding);
self
}
pub fn scrollbar(mut self, show: bool) -> Self {
self.show_scrollbar = show;
self
}
pub fn scrollbar_color(mut self, color: Color) -> Self {
self.scrollbar_color = Some(color);
self
}
pub fn child(mut self, element: Element) -> Self {
self.inner = self.inner.child(element);
self
}
pub fn children(mut self, elements: impl IntoIterator<Item = Element>) -> Self {
self.inner = self.inner.children(elements);
self
}
pub fn into_element(self) -> Element {
self.inner.into_element()
}
}
pub fn virtual_scroll_view<T, F>(
items: &[T],
scroll_offset: usize,
viewport_height: usize,
render_item: F,
) -> Element
where
F: Fn(&T, usize) -> Element,
{
let start = scroll_offset.min(items.len());
let end = (scroll_offset + viewport_height).min(items.len());
let mut container = Box::new()
.flex_direction(FlexDirection::Column)
.overflow_y(Overflow::Hidden)
.height(viewport_height as i32);
for global_idx in start..end {
if let Some(item) = items.get(global_idx) {
let child = render_item(item, global_idx);
container = container.child(child);
}
}
container.into_element()
}
pub fn fixed_bottom_layout(content: Element, bottom: Element) -> Element {
Box::new()
.flex_direction(FlexDirection::Column)
.height(crate::core::Dimension::Percent(100.0))
.child(
Box::new()
.flex_grow(1.0)
.overflow_y(Overflow::Hidden)
.child(content)
.into_element(),
)
.child(bottom)
.into_element()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::components::Text;
#[test]
fn test_scrollable_box_creation() {
let element = ScrollableBox::new()
.height(10)
.scroll_offset_y(5)
.child(Text::new("Hello").into_element())
.into_element();
assert_eq!(element.scroll_offset_y, Some(5));
assert_eq!(element.children.len(), 1);
}
#[test]
fn test_virtual_scroll_view() {
let items: Vec<String> = (0..100).map(|i| format!("Item {}", i)).collect();
let element = virtual_scroll_view(
&items,
10, 5, |item, _idx| Text::new(item.clone()).into_element(),
);
assert_eq!(element.children.len(), 5);
}
#[test]
fn test_virtual_scroll_view_at_end() {
let items: Vec<String> = (0..10).map(|i| format!("Item {}", i)).collect();
let element = virtual_scroll_view(
&items,
8, 5, |item, _idx| Text::new(item.clone()).into_element(),
);
assert_eq!(element.children.len(), 2);
}
#[test]
fn test_fixed_bottom_layout() {
let content = Text::new("Content").into_element();
let bottom = Text::new("Bottom").into_element();
let element = fixed_bottom_layout(content, bottom);
assert_eq!(element.children.len(), 2);
}
}