use carbide_core::widget::*;
use carbide_core::event_handler::{MouseEvent, KeyboardEvent};
use carbide_core::state::state::State;
use carbide_core::widget::primitive::foreach::{ForEachDelegate, ForEach};
use carbide_core::color::{RED, BLUE};
use std::option::Option::Some;
use carbide_core::prelude::StateSync;
pub trait ListIndex: ForEachDelegate {}
impl<T> ListIndex for T where T: ForEachDelegate {}
#[event(handle_keyboard_event, handle_mouse_event)]
#[derive(Clone, Widget)]
#[state_sync(sync_state)]
pub struct List<GS, T> where GS: GlobalState, T: ListIndex + 'static {
id: Id,
child: Box<dyn Widget<GS>>,
delegate: Box<dyn Widget<GS>>,
position: Point,
dimension: Dimensions,
#[state] model: Box<dyn State<Vec<T>, GS>>,
#[state] internal_model: Box<dyn State<Vec<T>, GS>>,
#[state] index_offset: Box<dyn State<usize, GS>>,
#[state] start_offset: Box<dyn State<f64, GS>>,
#[state] end_offset: Box<dyn State<f64, GS>>,
id_state: Box<dyn State<T, GS>>,
index_state: Box<dyn State<usize, GS>>,
}
impl<GS: GlobalState, T: ListIndex + 'static> List<GS, T> {
pub fn new(delegate: Box<dyn Widget<GS>>, model: Box<dyn State<Vec<T>, GS>>) -> Box<Self> {
let index_offset_state = Box::new(CommonState::new_local_with_key(&0));
let start_offset = CommonState::new_local_with_key(&-10.0);
let end_offset = CommonState::new_local_with_key(&-10.0);
let internal_model = Box::new(CommonState::new_local_with_key(model.get_latest_value()));
Box::new(List {
id: Id::new_v4(),
child: Scroll::new(VStack::initialize(vec![
Rectangle::initialize(vec![]).fill(RED).frame(SCALE.into(), Box::new(start_offset.clone())),
ForEach::new(internal_model.clone(), delegate.clone())
.index_offset(index_offset_state.clone()),
Rectangle::initialize(vec![]).fill(BLUE).frame(SCALE.into(), Box::new(end_offset.clone())),
])),
delegate,
position: [0.0,0.0],
dimension: [0.0,0.0],
model,
internal_model,
index_offset: index_offset_state,
id_state: Box::new(CommonState::new_local("id", &T::default())),
index_state: Box::new(CommonState::new_local("index", &0)),
start_offset: Box::new(start_offset),
end_offset: Box::new(end_offset)
})
}
pub fn id_state(mut self, state: Box<dyn State<T, GS>>) -> Box<Self> {
self.id_state = state;
self.child = Scroll::new(VStack::initialize(vec![
Rectangle::initialize(vec![]).fill(RED).frame(SCALE.into(), Box::new(self.start_offset.clone())),
ForEach::new(self.internal_model.clone(), self.delegate.clone())
.index_offset(self.index_offset.clone())
.id_state(self.id_state.clone())
.index_state(self.index_state.clone()),
Rectangle::initialize(vec![]).fill(BLUE).frame(SCALE.into(), Box::new(self.end_offset.clone())),
]));
Box::new(self)
}
pub fn index_state(mut self, state: Box<dyn State<usize, GS>>) -> Box<Self> {
self.index_state = state;
self.child = Scroll::new(VStack::initialize(vec![
Rectangle::initialize(vec![]).fill(RED).frame(SCALE.into(), Box::new(self.start_offset.clone())),
ForEach::new(self.internal_model.clone(), self.delegate.clone())
.index_offset(self.index_offset.clone())
.id_state(self.id_state.clone())
.index_state(self.index_state.clone()),
Rectangle::initialize(vec![]).fill(BLUE).frame(SCALE.into(), Box::new(self.end_offset.clone())),
]));
Box::new(self)
}
fn handle_mouse_event(&mut self, _: &MouseEvent, _: &bool, _: &mut Environment<GS>, _: &mut GS) {
}
fn handle_keyboard_event(&mut self, _: &KeyboardEvent, _: &mut Environment<GS>, _: &mut GS) {
}
fn recalculate_visible_children(&mut self, env: &mut Environment<GS>, _: &GS) {
let spacing = 10.0;
let dims = self.get_dimension();
let self_y = self.get_y();
let self_height = self.get_height();
let mut self_start_offset = self.start_offset.clone();
let mut self_end_offset = self.end_offset.clone();
let mut self_internal_model = self.internal_model.clone();
let self_model = self.model.clone();
let mut self_index_offset = self.index_offset.clone();
if let Some(scroll_view) = self.get_children_mut().next() {
scroll_view.calculate_size(dims, env);
scroll_view.position_children();
if let Some(vstack) = scroll_view.get_children_mut().next() {
let internal_model_size = self_internal_model.get_latest_value().len();
let mut vstack_children = vstack.get_children_mut().enumerate();
vstack_children.next();
while let Some((index, child)) = vstack_children.next() {
if index > internal_model_size {continue}
if child.get_y() + child.get_height() < self_y {
*self_start_offset.get_latest_value_mut() += child.get_height() + spacing;
self_internal_model.get_latest_value_mut().remove(0);
*self_index_offset.get_latest_value_mut() += 1;
}
if child.get_y() > self_y + self_height {
*self_end_offset.get_latest_value_mut() += child.get_height() + spacing;
self_internal_model.get_latest_value_mut().pop();
}
}
let mut vstack_children = vstack.get_children_mut().enumerate();
let (_, initial_child) = vstack_children.next()
.expect("Could not find the initial rectangle");
let inital_y = initial_child.get_y();
let mut min_height = 1000.0;
while let Some((index, child)) = vstack_children.next() {
if index > internal_model_size {continue}
if child.get_height() < min_height {
min_height = child.get_height();
}
}
while *self_start_offset.get_latest_value() + inital_y > self_y {
*self_start_offset.get_latest_value_mut() -= min_height + spacing;
*self_index_offset.get_latest_value_mut() -= 1;
let index_to_take_from = *self_index_offset.get_latest_value();
if self_model.get_latest_value().len() < index_to_take_from {
panic!("Can not take index from model, that is outside the bounds of said model")
}
self_internal_model.get_latest_value_mut().insert(0, self_model.get_latest_value()[index_to_take_from].clone());
}
let mut vstack_children = vstack.get_children_mut();
let mut last_y = 0.0;
while let Some(vstack_child) = vstack_children.next() {
last_y = vstack_child.get_y();
}
while last_y < self_y + self_height {
last_y += min_height + spacing;
*self_end_offset.get_latest_value_mut() -= min_height + spacing;
let index_to_take_from = *self_index_offset.get_latest_value() + self_internal_model.get_latest_value().len();
if self_model.get_latest_value().len() < index_to_take_from {
panic!("Can not take index from model, that is outside the bounds of said model")
}
self_internal_model.get_latest_value_mut().push(self_model.get_latest_value()[index_to_take_from].clone());
}
}
}
self.start_offset = self_start_offset;
self.end_offset = self_end_offset;
self.internal_model = self_internal_model;
self.index_offset = self_index_offset;
}
fn sync_state(&mut self, env: &mut Environment<GS>, global_state: &GS) {
self.update_all_widget_state(env, global_state);
self.recalculate_visible_children(env, global_state);
self.insert_local_state(env);
for child in self.get_proxied_children() {
child.sync_state(env, global_state)
}
self.update_local_widget_state(env);
}
}
impl<GS: GlobalState, T: ListIndex> CommonWidget<GS> for List<GS, T> {
fn get_id(&self) -> Id {
self.id
}
fn get_flag(&self) -> Flags {
Flags::EMPTY
}
fn get_children(&self) -> WidgetIter<GS> {
if self.child.get_flag() == Flags::PROXY {
self.child.get_children()
} else {
WidgetIter::single(&self.child)
}
}
fn get_children_mut(&mut self) -> WidgetIterMut<GS> {
if self.child.get_flag() == Flags::PROXY {
self.child.get_children_mut()
} else {
WidgetIterMut::single(&mut self.child)
}
}
fn get_proxied_children(&mut self) -> WidgetIterMut<GS> {
WidgetIterMut::single(&mut self.child)
}
fn get_position(&self) -> Point {
self.position
}
fn set_position(&mut self, position: Dimensions) {
self.position = position;
}
fn get_dimension(&self) -> Dimensions {
self.dimension
}
fn set_dimension(&mut self, dimensions: Dimensions) {
self.dimension = dimensions
}
}
impl<GS: GlobalState, T: ListIndex> ChildRender for List<GS, T> {}
impl<GS: GlobalState, T: ListIndex> Layout<GS> for List<GS, T> {
fn flexibility(&self) -> u32 {
10
}
fn calculate_size(&mut self, requested_size: Dimensions, env: &Environment<GS>) -> Dimensions {
if let Some(child) = self.get_children_mut().next() {
child.calculate_size(requested_size, env);
}
self.set_dimension(requested_size);
requested_size
}
fn position_children(&mut self) {
let positioning = BasicLayouter::Center.position();
let position = self.get_position();
let dimension = self.get_dimension();
if let Some(child) = self.get_children_mut().next() {
positioning(position, dimension, child);
child.position_children();
}
}
}
impl<GS: GlobalState, T: ListIndex + 'static> WidgetExt<GS> for List<GS, T> {}