use makepad_render::*;
use crate::scrollview::*;
#[derive(Clone, Default)]
pub struct ListLogic {
pub list_items: Vec<ListItem>,
pub scroll_item_in_view: Option<usize>,
pub set_scroll_pos: Option<Vec2>,
pub start_item: usize,
pub end_item: usize,
pub end_fill: usize,
pub selection: Vec<usize>,
pub last_range: Option<(usize, usize)>,
}
#[derive(Clone)]
pub struct ListItem {
pub animator: Animator,
pub is_selected: bool
}
pub enum ListLogicEvent {
Animate(AnimateEvent),
AnimEnded,
Select,
Deselect,
Cleanup,
Over,
Out
}
pub enum ListEvent {
SelectSingle(usize),
SelectMultiple,
None
}
#[derive(PartialEq)]
pub enum ListSelect {
None,
Single(usize),
Range(usize),
Toggle(usize),
All
}
impl ListSelect {
pub fn item_index(&self) -> Option<usize> {
match self {
ListSelect::Single(index) => Some(*index),
_ => None
}
}
}
impl ListLogic {
pub fn set_list_len(&mut self, len: usize)
{
if self.list_items.len() < len {
for _ in self.list_items.len()..len {
self.list_items.push(ListItem {
animator: Animator::default(),
is_selected: false
})
}
}
else {
self.list_items.truncate(len);
}
}
pub fn handle_list_scroll_bars(&mut self, cx: &mut Cx, event: &mut Event, view: &mut ScrollView)
-> bool {
if view.handle_scroll_bars(cx, event) {
view.redraw_view_area(cx);
match &event {
Event::FingerScroll {..} => {
return true;
},
Event::FingerMove {..} => {
return true;
},
_ => ()
}
}
return false
}
pub fn begin_list(&mut self, cx: &mut Cx, view: &mut ScrollView, tail_list: bool, row_height: f32) -> Result<(), ()>
{
view.begin_view(cx, Layout {
direction: Direction::Down,
..Layout::default()
}) ?;
self.set_visible_range_and_scroll(cx, view, tail_list, row_height);
Ok(())
}
pub fn walk_turtle_to_end(&mut self, cx: &mut Cx, row_height: f32) {
let left = (self.list_items.len() - self.end_item) as f32 * row_height;
cx.walk_turtle(Walk::wh(Width::Fill, Height::Fix(left)));
}
pub fn end_list(&mut self, cx: &mut Cx, view: &mut ScrollView) {
view.end_view(cx);
if let Some(set_scroll_pos) = self.set_scroll_pos {
view.set_scroll_pos(cx, set_scroll_pos);
}
}
pub fn set_visible_range_and_scroll(&mut self, cx: &mut Cx, view: &mut ScrollView, tail_list: bool, row_height: f32) {
let view_rect = cx.get_turtle_rect();
let max_scroll_y = ((self.list_items.len() + 1) as f32 * row_height - view_rect.h).max(0.);
let (scroll_pos, set_scroll_pos) = if tail_list {
(Vec2 {x: 0., y: max_scroll_y}, true)
}
else {
let sp = view.get_scroll_pos(cx);
if let Some(scroll_item_in_view) = self.scroll_item_in_view {
self.scroll_item_in_view = None;
let item_y = scroll_item_in_view as f32 * row_height;
let dy = (item_y + row_height) - (sp.y + view_rect.h);
if item_y < sp.y {
(Vec2 {x: 0., y: item_y}, true)
}
else if dy > 0. {
(Vec2 {x: 0., y: sp.y + dy}, true)
}
else {
(sp, false)
}
}
else {
if sp.y > max_scroll_y {
(Vec2 {x: 0., y: max_scroll_y}, false)
}
else {
(sp, false)
}
}
};
let start_item = (scroll_pos.y / row_height).floor() as usize;
let end_item = ((scroll_pos.y + view_rect.h + row_height) / row_height).ceil() as usize;
self.start_item = start_item.min(self.list_items.len());
self.end_fill = end_item;
self.end_item = end_item.min(self.list_items.len());
let start_scroll = (self.start_item as f32) * row_height;
if set_scroll_pos {
self.set_scroll_pos = Some(scroll_pos);
}
else {
self.set_scroll_pos = None;
}
cx.move_turtle(0., start_scroll);
}
pub fn get_next_single_selection(&self) -> ListSelect {
if let Some(last) = self.selection.last() {
let next = last + 1;
if next >= self.list_items.len() { ListSelect::Single(0)
}
else {
ListSelect::Single(next)
}
}
else {
ListSelect::Single(0)
}
}
pub fn get_prev_single_selection(&self) -> ListSelect {
if let Some(first) = self.selection.last() {
if *first == 0 { ListSelect::Single(self.list_items.len().max(1) - 1)
}
else {
ListSelect::Single(first - 1)
}
}
else {
ListSelect::Single(0)
}
}
pub fn handle_list_logic<F>(&mut self, cx: &mut Cx, event: &mut Event, select: ListSelect, mut cb: F) -> ListEvent
where F: FnMut(&mut Cx, ListLogicEvent, &mut ListItem, usize)
{
let mut select = select;
for counter in self.start_item..self.end_item {
if counter >= self.list_items.len() {
break;
}
let item = &mut self.list_items[counter];
match event.hits(cx, item.animator.area, HitOpt::default()) {
Event::Animate(ae) => {
cb(cx, ListLogicEvent::Animate(ae), item, counter)
},
Event::AnimEnded(_) => {
cb(cx, ListLogicEvent::AnimEnded, item, counter)
},
Event::FingerDown(fe) => {
cx.set_down_mouse_cursor(MouseCursor::Hand);
if fe.modifiers.logo || fe.modifiers.control {
select = ListSelect::Toggle(counter)
}
else if fe.modifiers.shift {
select = ListSelect::Range(counter)
}
else {
select = ListSelect::Single(counter)
}
},
Event::FingerUp(_fe) => {
},
Event::FingerMove(_fe) => {
},
Event::FingerHover(fe) => {
cx.set_hover_mouse_cursor(MouseCursor::Hand);
match fe.hover_state {
HoverState::In => {
cb(cx, ListLogicEvent::Over, item, counter);
},
HoverState::Out => {
cb(cx, ListLogicEvent::Out, item, counter);
},
_ => ()
}
},
_ => ()
}
};
if let Some(last_range) = self.last_range {
for counter in last_range.0..last_range.1 {
if counter >= self.list_items.len() {
break;
}
if counter < self.start_item || counter >= self.end_item {
let dm = &mut self.list_items[counter];
cb(cx, ListLogicEvent::Deselect, dm, counter);
}
}
}
self.last_range = Some((self.start_item, self.end_item));
match select {
ListSelect::Range(select_index) => {
if let Some(first) = self.selection.first() {
if let Some(last) = self.selection.last() {
let (start, end) = if select_index < *first {
(select_index, *last)
}
else if select_index > *last {
(*first, select_index)
}
else {
(select_index, select_index)
};
for counter in &self.selection {
if *counter >= self.list_items.len() || *counter >= start && *counter <= end {
continue;
}
let dm = &mut self.list_items[*counter];
if *counter != select_index {
dm.is_selected = false;
cb(cx, ListLogicEvent::Deselect, dm, select_index);
}
}
self.selection.truncate(0);
for i in start..= end {
let dm = &mut self.list_items[i];
dm.is_selected = true;
cb(cx, ListLogicEvent::Select, dm, i);
self.selection.push(i);
}
}
}
},
ListSelect::Toggle(select_index) => {
let dm = &mut self.list_items[select_index];
if dm.is_selected {
dm.is_selected = false;
cb(cx, ListLogicEvent::Deselect, dm, select_index);
if let Some(pos) = self.selection.iter().position( | v | *v == select_index) {
self.selection.remove(pos);
}
}
else {
self.selection.push(select_index);
dm.is_selected = true;
cb(cx, ListLogicEvent::Over, dm, select_index);
}
},
ListSelect::All => {
self.selection.truncate(0);
for i in 0..self.list_items.len() {
self.selection.push(i);
let dm = &mut self.list_items[i];
dm.is_selected = true;
cb(cx, ListLogicEvent::Over, dm, i);
}
},
ListSelect::Single(select_index) => {
for counter in &self.selection {
if *counter >= self.list_items.len() {
continue;
}
let dm = &mut self.list_items[*counter];
if *counter != select_index {
dm.is_selected = false;
cb(cx, ListLogicEvent::Cleanup, dm, *counter);
}
}
self.selection.truncate(0);
if select_index < self.list_items.len() {
self.selection.push(select_index);
let dm = &mut self.list_items[select_index];
dm.is_selected = true;
cb(cx, ListLogicEvent::Over, dm, select_index);
return ListEvent::SelectSingle(select_index)
}
},
_ => ()
}
match select {
ListSelect::Range(_) | ListSelect::Toggle(_) | ListSelect::All => {
ListEvent::SelectMultiple
},
_ => ListEvent::None
}
}
}