deckster 0.2.1

Tui to study flashcards in the terminal
Documentation
use tui::widgets::{Block,Borders,Paragraph,Widget,Wrap};
use tui::layout::{Alignment,Layout,Constraint,Direction,Rect};
use tui::buffer::Buffer;
use tui::style::{Style,Color,Modifier};
use tui::symbols::DOT;
use tui::text::{Spans,Span,Text};
use crate::records::Record;
use crate::sm2::Rating;

#[derive(Clone,Default)]
pub struct Slide {
    question: String,
    answer: String,
    selected: usize,
    step: Option<usize>,
}

impl Slide {
    pub fn record(self,record: &Record) -> Self {
        Self {
            question: record.question.clone(),
            answer: record.answer.clone(),
            selected: 0,
            step: Some(0),
        }
    }
    pub fn next(&mut self) {
        if self.selected < 6 {
            self.selected += 2
        }
    }
    pub fn prev(&mut self) {
        if self.selected > 0 {
            self.selected -= 2
        }
    }
    pub fn cont(&mut self) {
        if let Some(x) = self.step {
            if x < 2 { self.step = Some(x+1) }
            else { self.step = None }
        }
    }
    pub fn step(&self) -> Option<usize> {
        self.step
    }
    pub fn rating(&self) -> Rating {
        match self.selected {
            0 => Rating::Again,
            2 => Rating::Hard,
            4 => Rating::Good,
            6 => Rating::Easy,
            _ => unreachable!(),
        }
    }
}

impl Widget for Slide {
    fn render(self, area: Rect, buf: &mut Buffer) {
        let style = Style::default();
        buf.set_style(area,style);

        let block = Block::default()
            .title("Cards")
            .borders(Borders::ALL);
        let slide_area = {
            let inner = block.inner(area);
            block.render(area,buf);
            inner
        };

        if slide_area.height < 1 { return };

        let layout = Layout::default()
            .direction(Direction::Vertical)
            .constraints(
                [
                    Constraint::Percentage(45),
                    Constraint::Percentage(45),
                    Constraint::Percentage(10),
                ].as_ref()
            ).split(slide_area);

        let get_paragraph = |s| {
            Paragraph::new(s)
                .block(Block::default())
                .style(Style::default())
                .alignment(Alignment::Center)
                .wrap(Wrap { trim: true })
        };

        let question = get_paragraph(Text::from(self.question));
        question.render(layout[0],buf);

        if let Some(x) = self.step {
            if x < 1 { return; }
        }

        let ans_style = Style::default().fg(Color::Green);
        let answer = get_paragraph(Text::from(self.answer)).style(ans_style);
        answer.render(layout[1],buf);

        if let Some(x) = self.step {
            if x < 2 { return; }
        }

        let selected = self.selected;

        let style_selected = Style::default()
            .fg(Color::Yellow)
            .add_modifier(Modifier::BOLD);

        let style_unselected = Style::default()
            .add_modifier(Modifier::DIM);

        let tabs = {
            let titles: Vec<Span> = ["Again ",DOT," Hard ",DOT," Good ",DOT," Easy"].iter()
                .cloned()
                .enumerate()
                .map(|(i,s)| {
                    if i == selected { Span::styled(s,style_selected) }
                    // else { Span::from(s) }
                    else { Span::styled(s,style_unselected) }
                })
                .collect();

            let titles = Spans::from(titles);
            get_paragraph(Text::from(titles))
        };

        tabs.render(layout[2],buf);
    }
}