graphix-package-tui 0.9.0

A dataflow language for UIs and network programming, TUI package
Documentation
use super::{into_borrowed_line, HighlightSpacingV, LineV, StyleV, TuiW, TuiWidget};
use anyhow::{Context, Result};
use arcstr::ArcStr;
use async_trait::async_trait;
use crossterm::event::Event;
use graphix_compiler::expr::ExprId;
use graphix_rt::{GXExt, GXHandle, TRef};
use netidx::publisher::Value;
use ratatui::{
    layout::Rect,
    widgets::{List, ListState},
    Frame,
};
use tokio::try_join;

pub(super) struct ListW<X: GXExt> {
    highlight_spacing: TRef<X, Option<HighlightSpacingV>>,
    highlight_style: TRef<X, Option<StyleV>>,
    highlight_symbol: TRef<X, Option<ArcStr>>,
    items: TRef<X, Vec<LineV>>,
    repeat_highlight_symbol: TRef<X, Option<bool>>,
    scroll: TRef<X, Option<u32>>,
    selected: TRef<X, Option<u32>>,
    style: TRef<X, Option<StyleV>>,
    state: ListState,
}

impl<X: GXExt> ListW<X> {
    pub(super) async fn compile(gx: GXHandle<X>, v: Value) -> Result<TuiW> {
        let [(_, highlight_spacing), (_, highlight_style), (_, highlight_symbol), (_, items), (_, repeat_highlight_symbol), (_, scroll), (_, selected), (_, style)] =
            v.cast_to::<[(ArcStr, u64); 8]>().context("list fields")?;
        let (
            highlight_spacing,
            highlight_style,
            highlight_symbol,
            items,
            repeat_highlight_symbol,
            scroll,
            selected,
            style,
        ) = try_join! {
            gx.compile_ref(highlight_spacing),
            gx.compile_ref(highlight_style),
            gx.compile_ref(highlight_symbol),
            gx.compile_ref(items),
            gx.compile_ref(repeat_highlight_symbol),
            gx.compile_ref(scroll),
            gx.compile_ref(selected),
            gx.compile_ref(style)
        }?;
        let mut t = Self {
            highlight_spacing: TRef::new(highlight_spacing)
                .context("list tref highlight_spacing")?,
            highlight_style: TRef::new(highlight_style)
                .context("list tref highlight_style")?,
            highlight_symbol: TRef::new(highlight_symbol)
                .context("list tref highlight_symbol")?,
            items: TRef::new(items).context("list tref items")?,
            repeat_highlight_symbol: TRef::new(repeat_highlight_symbol)
                .context("list tref repeat_highlight_symbol")?,
            scroll: TRef::new(scroll).context("list tref scroll")?,
            selected: TRef::new(selected).context("list tref selected")?,
            style: TRef::new(style).context("list tref style")?,
            state: ListState::default(),
        };
        if let Some(Some(s)) = t.scroll.t {
            t.state = t.state.with_offset(s as usize);
        }
        if let Some(s) = t.selected.t {
            t.state = t.state.with_selected(s.map(|s| s as usize));
        }
        Ok(Box::new(t))
    }
}

#[async_trait]
impl<X: GXExt> TuiWidget for ListW<X> {
    async fn handle_event(&mut self, _e: Event, _v: Value) -> Result<()> {
        Ok(())
    }

    async fn handle_update(&mut self, id: ExprId, v: Value) -> Result<()> {
        let Self {
            highlight_spacing,
            highlight_style,
            highlight_symbol,
            items,
            repeat_highlight_symbol,
            scroll,
            selected,
            style,
            state,
        } = self;
        highlight_spacing.update(id, &v).context("list update highlight_spacing")?;
        highlight_style.update(id, &v).context("list update highlight_style")?;
        highlight_symbol.update(id, &v).context("list update highlight_symbol")?;
        items.update(id, &v).context("list update items")?;
        repeat_highlight_symbol
            .update(id, &v)
            .context("list update repeat_highlight_symbol")?;
        if let Some(Some(s)) = scroll.update(id, &v).context("list update scroll")? {
            *state = state.clone().with_offset(*s as usize);
        }
        if let Some(s) = selected.update(id, &v).context("list update selected")? {
            *state = state.clone().with_selected(s.map(|s| s as usize));
        }
        style.update(id, &v).context("list update style")?;
        Ok(())
    }

    fn draw(&mut self, frame: &mut Frame, rect: Rect) -> Result<()> {
        let Self {
            highlight_spacing,
            highlight_style,
            highlight_symbol,
            items,
            repeat_highlight_symbol,
            scroll: _,
            selected: _,
            style,
            state,
        } = self;
        let mut list =
            List::new(items.t.iter().flat_map(|l| l).map(|l| into_borrowed_line(&l.0)));
        if let Some(Some(hs)) = &highlight_spacing.t {
            list = list.highlight_spacing(hs.0.clone());
        }
        if let Some(Some(s)) = &highlight_style.t {
            list = list.highlight_style(s.0);
        }
        if let Some(Some(sym)) = &highlight_symbol.t {
            list = list.highlight_symbol(sym.as_str());
        }
        if let Some(Some(r)) = repeat_highlight_symbol.t {
            list = list.repeat_highlight_symbol(r);
        }
        if let Some(Some(s)) = &style.t {
            list = list.style(s.0);
        }
        frame.render_stateful_widget(list, rect, state);
        Ok(())
    }
}