radiate-ui 1.2.22

A Rust library for genetic algorithms and artificial evolution.
Documentation
use crate::{
    state::{AppState, PanelId, TabId},
    widgets::{
        DistributionTableWidget, EngineStatusPanelWidget, FitnessChartPanelWidget, FnWidget,
        MetricDetailPanelWidget, Panel, SearchBarWidget, StatsTableWidget, TabComponent,
        TimeTableWidget,
        components::{SpeciesPieChartComponent, SpeciesSparklineComponent, TimePieChartComponent},
        panels::tables::SpeciesTableWidget,
    },
};
use radiate_engines::Chromosome;
use ratatui::{
    buffer::Buffer,
    layout::{Constraint, Direction, Layout, Rect},
    widgets::{StatefulWidget, Widget},
};

pub enum LayoutNode {
    Horizontal {
        constraints: Vec<Constraint>,
        children: Vec<LayoutNode>,
    },
    Vertical {
        constraints: Vec<Constraint>,
        children: Vec<LayoutNode>,
    },
    Tabbed {
        id: TabId,
        tabs: Vec<&'static str>,
        children: Vec<LayoutNode>,
    },
    Widget(PanelId),
}

impl LayoutNode {
    pub fn draw<C: Chromosome>(&self, area: Rect, buf: &mut Buffer, state: &mut AppState<C>) {
        match self {
            LayoutNode::Horizontal {
                constraints,
                children,
            } => {
                let areas = Layout::default()
                    .direction(Direction::Horizontal)
                    .constraints(constraints)
                    .split(area);

                for (child, &child_area) in children.iter().zip(areas.into_iter()) {
                    child.draw(child_area, buf, state);
                }
            }
            LayoutNode::Vertical {
                constraints,
                children,
            } => {
                let areas = Layout::default()
                    .direction(Direction::Vertical)
                    .constraints(constraints)
                    .split(area);

                for (child, &child_area) in children.iter().zip(areas.into_iter()) {
                    child.draw(child_area, buf, state);
                }
            }
            LayoutNode::Tabbed { id, tabs, children } => {
                let active_tab_idx = state.active_tab_index(id);

                let areas = Layout::default()
                    .direction(Direction::Vertical)
                    .constraints([Constraint::Length(3), Constraint::Fill(1)])
                    .split(area);

                Panel::new(FnWidget::new(|area, buf| {
                    TabComponent::from(tabs.clone())
                        .select(active_tab_idx)
                        .render(area, buf);
                }))
                .render_inside_block(true)
                .render(areas[0], buf);

                if let Some(active_child) = children.get(active_tab_idx) {
                    active_child.draw(areas[1], buf, state);
                }
            }

            LayoutNode::Widget(panel_id) => match panel_id {
                PanelId::EngineStatus => EngineStatusPanelWidget::new().render(area, buf, state),
                PanelId::FitnessChart => FitnessChartPanelWidget::new().render(area, buf, state),
                PanelId::TimeTable => TimeTableWidget::new().render(area, buf, state),
                PanelId::StatsTable => StatsTableWidget::new().render(area, buf, state),
                PanelId::DistTable => DistributionTableWidget::new().render(area, buf, state),
                PanelId::SpeciesTable => SpeciesTableWidget::new().render(area, buf, state),
                PanelId::MetricDetail => MetricDetailPanelWidget::new().render(area, buf, state),
                PanelId::SpeciesPieChart => {
                    SpeciesPieChartComponent::new().render(area, buf, state)
                }
                PanelId::TimePieChart => TimePieChartComponent::new().render(area, buf, state),
                PanelId::SpeciesSparkline => {
                    SpeciesSparklineComponent::new().render(area, buf, state)
                }
                PanelId::Search => SearchBarWidget::new(state).render(area, buf),
                _ => {}
            },
        }
    }
}

impl Default for LayoutNode {
    fn default() -> Self {
        use LayoutNode::*;

        Vertical {
            constraints: vec![Constraint::Percentage(30), Constraint::Fill(1)],
            children: vec![
                Horizontal {
                    constraints: vec![Constraint::Percentage(25), Constraint::Fill(1)],
                    children: vec![Widget(PanelId::EngineStatus), Widget(PanelId::FitnessChart)],
                },
                Vertical {
                    constraints: vec![Constraint::Fill(1), Constraint::Length(3)],
                    children: vec![
                        Tabbed {
                            id: TabId::Dashboard,
                            tabs: vec!["Stats", "Time", "Distribution", "Species"],
                            children: vec![
                                Horizontal {
                                    constraints: vec![
                                        Constraint::Fill(1),
                                        Constraint::Percentage(15),
                                    ],
                                    children: vec![
                                        Widget(PanelId::StatsTable),
                                        Widget(PanelId::MetricDetail),
                                    ],
                                },
                                Horizontal {
                                    constraints: vec![
                                        Constraint::Fill(1),
                                        Constraint::Percentage(30),
                                        Constraint::Percentage(20),
                                    ],
                                    children: vec![
                                        Widget(PanelId::TimeTable),
                                        Widget(PanelId::TimePieChart),
                                        Widget(PanelId::MetricDetail),
                                    ],
                                },
                                Horizontal {
                                    constraints: vec![
                                        Constraint::Fill(1),
                                        Constraint::Percentage(20),
                                    ],
                                    children: vec![
                                        Widget(PanelId::DistTable),
                                        Widget(PanelId::MetricDetail),
                                    ],
                                },
                                Horizontal {
                                    constraints: vec![
                                        Constraint::Fill(1),
                                        Constraint::Percentage(25),
                                        Constraint::Percentage(25),
                                    ],
                                    children: vec![
                                        Widget(PanelId::SpeciesTable),
                                        Widget(PanelId::SpeciesSparkline),
                                        Widget(PanelId::SpeciesPieChart),
                                    ],
                                },
                            ],
                        },
                        Horizontal {
                            constraints: vec![Constraint::Fill(1)],
                            children: vec![Widget(PanelId::Search)],
                        },
                    ],
                },
            ],
        }
    }
}