use crate::scrollview::*;
use wrflib::*;
#[derive(Default)]
pub struct ListLogic {
pub multi_select: bool,
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>,
}
#[derive(Default)]
pub struct ListItem {
component_base: ComponentBase,
area: Area,
pub animator: Animator,
pub is_selected: bool,
}
impl ListItem {
pub fn area(&self) -> Area {
self.area
}
pub fn set_area(&mut self, cx: &mut Cx, area: Area) {
self.area = area;
self.component_base.register_component_area(cx, self.area);
}
}
#[derive(Debug)]
pub enum ListLogicEvent {
Animate,
Select,
Deselect,
Cleanup,
Over,
Out,
}
pub enum ListEvent {
SelectSingle(usize),
SelectDouble(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 {
#[must_use]
pub fn with_multi_select(self, multi_select: bool) -> Self {
Self { multi_select, ..self }
}
pub fn set_list_len(&mut self, len: usize) {
if self.list_items.len() != len {
self.selection.truncate(0);
}
if self.list_items.len() < len {
for _ in self.list_items.len()..len {
self.list_items.push(ListItem::default())
}
} 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(cx, event) {
cx.request_draw();
match &event {
Event::FingerScroll { .. } => {
return true;
}
Event::FingerMove { .. } => {
return true;
}
_ => (),
}
}
false
}
pub fn begin_list(&mut self, cx: &mut Cx, view: &mut ScrollView, tail_list: bool, row_height: f32) {
view.begin_view(cx, LayoutSize::FILL);
self.set_visible_range_and_scroll(cx, view, tail_list, row_height);
}
pub fn walk_box_to_end(&mut self, cx: &mut Cx, row_height: f32) {
let left = (self.list_items.len() - self.end_item) as f32 * row_height;
cx.add_box(LayoutSize::new(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_box_rect();
let max_scroll_y = ((self.list_items.len() + 1) as f32 * row_height - view_rect.size.y).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.size.y);
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.size.y + 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_draw_pos(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(*last)
} else {
ListSelect::Single(next)
}
} else {
println!("GOT SELECT 0");
ListSelect::Single(0)
}
}
pub fn get_prev_single_selection(&self) -> ListSelect {
if let Some(first) = self.selection.last() {
if *first == 0 {
ListSelect::Single(*first) } 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 dblclick: bool,
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];
if item.animator.handle(cx, event) {
cb(cx, ListLogicEvent::Animate, item, counter)
}
match event.hits(cx, &item.component_base, HitOpt::default()) {
Event::FingerDown(fe) => {
cx.set_down_mouse_cursor(MouseCursor::Hand);
if self.multi_select && (fe.modifiers.logo || fe.modifiers.control) {
select = ListSelect::Toggle(counter)
} else if self.multi_select && fe.modifiers.shift {
select = ListSelect::Range(counter)
} else {
select = ListSelect::Single(counter);
if fe.tap_count > 1 {
dblclick = true;
}
}
}
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);
}
_ => (),
}
}
_ => (),
}
}
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);
if dblclick {
return ListEvent::SelectDouble(select_index);
} else {
return ListEvent::SelectSingle(select_index);
}
}
}
_ => (),
}
match select {
ListSelect::Range(_) | ListSelect::Toggle(_) | ListSelect::All => ListEvent::SelectMultiple,
_ => ListEvent::None,
}
}
}