radiate-ui 1.3.0

A Rust library for genetic algorithms and artificial evolution.
Documentation
use super::nav::DashboardTab;
use radiate_engines::species::SpeciesId;
use radiate_utils::SmallStr;
use ratatui::widgets::{ScrollbarState, TableState};

pub struct TableStates {
    pub time: AppTableState<SmallStr>,
    pub stats: AppTableState<SmallStr>,
    pub dist: AppTableState<SmallStr>,
    pub species: AppTableState<SpeciesId>,
}

impl TableStates {
    pub fn move_down(&mut self, tab: DashboardTab) {
        match tab {
            DashboardTab::Stats => Self::scroll_down(&mut self.stats),
            DashboardTab::Time => Self::scroll_down(&mut self.time),
            DashboardTab::Distribution => Self::scroll_down(&mut self.dist),
            DashboardTab::Species => Self::scroll_down(&mut self.species),
        }
    }

    pub fn move_up(&mut self, tab: DashboardTab) {
        match tab {
            DashboardTab::Stats => Self::scroll_up(&mut self.stats),
            DashboardTab::Time => Self::scroll_up(&mut self.time),
            DashboardTab::Distribution => Self::scroll_up(&mut self.dist),
            DashboardTab::Species => Self::scroll_up(&mut self.species),
        }
    }

    pub fn selected_metric(&self, tab: DashboardTab) -> Option<&str> {
        match tab {
            DashboardTab::Time => self.time.selected_value.as_deref(),
            DashboardTab::Stats => self.stats.selected_value.as_deref(),
            DashboardTab::Distribution => self.dist.selected_value.as_deref(),
            DashboardTab::Species => None,
        }
    }

    fn scroll_down<T>(t: &mut AppTableState<T>) {
        if t.row_count == 0 {
            return;
        }
        if let Some(i) = t.state.selected() {
            let next = if i + 1 >= t.row_count { 0 } else { i + 1 };
            t.state.select(Some(next));
            t.selected_row = next;
            t.scroll_bar = t.scroll_bar.as_mut().map(|sb| sb.position(next));
        } else {
            t.state.select(Some(0));
            t.selected_row = 0;
            t.scroll_bar = t.scroll_bar.as_mut().map(|sb| sb.position(0));
        }
    }

    fn scroll_up<T>(t: &mut AppTableState<T>) {
        if t.row_count == 0 {
            return;
        }
        if let Some(i) = t.state.selected() {
            let next = if i == 0 { t.row_count - 1 } else { i - 1 };
            t.state.select(Some(next));
            t.selected_row = next;
            t.scroll_bar = t.scroll_bar.as_mut().map(|sb| sb.position(next));
        } else {
            let last = t.row_count - 1;
            t.state.select(Some(last));
            t.selected_row = last;
            t.scroll_bar = t.scroll_bar.as_mut().map(|sb| sb.position(last));
        }
    }
}

impl Default for TableStates {
    fn default() -> Self {
        Self {
            time: AppTableState::new(),
            stats: AppTableState::new(),
            dist: AppTableState::new(),
            species: AppTableState::new(),
        }
    }
}

pub struct AppTableState<T> {
    pub state: TableState,
    pub scroll_bar: Option<ScrollbarState>,
    pub selected_value: Option<T>,
    pub selected_row: usize,
    pub row_count: usize,
    pub prev_row_count: usize,
}

impl<T> AppTableState<T> {
    pub fn new() -> Self {
        Self {
            state: TableState::default(),
            scroll_bar: None,
            selected_row: 0,
            row_count: 0,
            prev_row_count: 0,
            selected_value: None,
        }
    }

    pub fn update_rows<K, F>(&mut self, items: &[K], func: F)
    where
        F: Fn(&K) -> T,
    {
        let current_len = items.len();
        self.prev_row_count = self.row_count;
        self.row_count = current_len;

        if (self.prev_row_count == 0 && self.row_count > 0)
            || (self.selected_row < self.prev_row_count && self.selected_row >= self.row_count)
        {
            self.selected_row = 0;
            self.state.select(Some(0));
        }

        if self.selected_row >= current_len && current_len > 0 {
            self.selected_row = current_len - 1;
            self.state.select(Some(self.selected_row));
        }

        self.selected_value = items.get(self.selected_row).map(&func);
    }
}