bernardo-tui 0.2.7

A keyboard-only, distraction-free TUI widget library
Documentation
use std::rc::Rc;

use crate::config::theme::Theme;
use crate::experiments::screen_shot::screenshot;
use crate::experiments::screenspace::Screenspace;
use crate::io::input_event::InputEvent;
use crate::io::output::FinalOutput;
use crate::mocks::meta_frame::MetaOutputFrame;
use crate::mocks::mock_output::MockOutput;
use crate::mocks::with_scroll_interpreter::WithScrollWidgetInterpreter;
use crate::primitives::rect::Rect;
use crate::primitives::scroll::ScrollDirection;
use crate::primitives::sized_xy::SizedXY;
use crate::primitives::xy::XY;
use crate::widget::fill_policy::SizePolicy;
use crate::widget::widget::Widget;
use crate::widgets::list_widget::list_widget::ListWidget;
use crate::widgets::list_widget::list_widget_item::ListWidgetItem;
use crate::widgets::with_scroll::with_scroll::WithScroll;

impl ListWidgetItem for Rc<String> {
    fn get_column_name(idx: usize) -> &'static str {
        match idx {
            0 => "name",
            _ => "",
        }
    }

    fn get_min_column_width(idx: usize) -> u16 {
        match idx {
            0 => 8,
            _ => 0,
        }
    }

    fn len_columns() -> usize {
        1
    }

    fn get(&self, idx: usize) -> Option<Rc<String>> {
        match idx {
            0 => Some(self.clone()),
            _ => None,
        }
    }
}

pub type ListWithScroll = WithScroll<ListWidget<Rc<String>>>;

pub struct WithScrollTestbed {
    pub widget: ListWithScroll,
    pub size: XY,
    pub theme: Theme,
    pub last_frame: Option<MetaOutputFrame>,
}

impl WithScrollTestbed {
    pub fn new() -> Self {
        let mut list_widget = ListWidget::<Rc<String>>::new();
        list_widget.set_fill_policy(SizePolicy::MATCH_LAYOUT);

        Self {
            widget: ListWithScroll::new(ScrollDirection::Vertical, list_widget).with_line_no(),
            size: XY::new(10, 20),
            theme: Default::default(),
            last_frame: None,
        }
    }

    pub fn next_frame(&mut self) {
        let (mut output, rcvr) = MockOutput::new(self.size, false, self.theme.clone());

        self.widget.prelayout();
        self.widget.layout(Screenspace::full_output(output.size()));
        self.widget.render(&self.theme, true, &mut output);

        output.end_frame().unwrap();

        let frame = rcvr.recv().unwrap();
        self.last_frame = Some(frame);
    }

    pub fn frame_op(&self) -> Option<&MetaOutputFrame> {
        self.last_frame.as_ref()
    }

    pub fn interpreter(&self) -> Option<WithScrollWidgetInterpreter<'_, ListWidget<Rc<String>>>> {
        self.frame_op().map(|frame| {
            let meta = frame
                .metadata
                .iter()
                .find(|item| item.typename == ListWithScroll::static_typename())
                .unwrap();

            WithScrollWidgetInterpreter::new(frame, meta)
        })
    }

    pub fn screenshot(&self) -> bool {
        self.frame_op().map(|frame| screenshot(&frame.buffer)).unwrap_or(false)
    }

    pub fn send_input(&mut self, input: InputEvent) {
        self.widget.act_on(input);
        self.next_frame();
    }

    pub fn last_child_visible_rect(&self) -> Option<Rect> {
        self.widget.internal().get_last_size().map(|i| i.visible_rect())
    }

    pub fn observed_highlighted_op(&self) -> Option<String> {
        let frame = self.last_frame.as_ref()?;
        frame
            .buffer
            .items_of_style(self.theme.highlighted(true))
            .map(|item| item.text.trim().to_string())
            .next()
    }
}