1use crate::iohandler::scanner;
5use crossterm::{
6 event::{poll, read, Event, KeyCode},
7 terminal,
8};
9use log::debug;
10use std::{io, process, time::Duration};
11use tui::{
12 backend::CrosstermBackend,
13 layout::{Alignment, Constraint, Direction, Layout},
14 style::{Color, Modifier, Style},
15 widgets::{Block, Borders, Paragraph, SelectableList, Text, Widget},
16 Terminal,
17};
18
19pub struct DrawState<'a> {
23 list_items: Vec<&'a str>,
24 selected_item: usize,
25}
26pub fn run() -> Result<(), crossterm::ErrorKind> {
28 terminal::enable_raw_mode()?;
29 let backend = CrosstermBackend::new(io::stdout());
30 let mut term = Terminal::new(backend).unwrap();
31
32 term.clear()?;
33
34 let mut draw_state = DrawState {
35 list_items: vec!["Init", "Add", "Remove", "Switch"],
36 selected_item: 0,
37 };
38
39 draw(&mut term, &draw_state).unwrap();
40
41 loop {
42 check_update(&mut term, &mut draw_state)?;
43 }
44}
45
46fn up_pressed(state: &mut DrawState) {
48 if state.selected_item != 0 {
49 state.selected_item -= 1;
50 }
51}
52
53fn down_pressed(state: &mut DrawState) {
55 if state.selected_item + 1 != state.list_items.len() {
56 state.selected_item += 1;
57 }
58}
59
60fn enter_pressed(_state: &mut DrawState) {
62 }
64
65fn exit_routine(
67 term: &mut Terminal<CrosstermBackend<io::Stdout>>,
68 _state: &mut DrawState,
69) -> Result<(), crossterm::ErrorKind> {
70 term.clear()?;
72 term.show_cursor()?;
73 terminal::disable_raw_mode()?;
74 debug!("Nice chirping... Bye!");
75 process::exit(1);
76}
77
78pub fn check_update(
80 mut term: &mut Terminal<CrosstermBackend<io::Stdout>>,
81 mut draw_state: &mut DrawState,
82) -> Result<(), crossterm::ErrorKind> {
83 if poll(Duration::from_millis(100))? {
84 match read()? {
85 Event::Key(event) => {
86 match event.code {
87 KeyCode::Up => up_pressed(&mut draw_state),
88 KeyCode::Down => down_pressed(&mut draw_state),
89 KeyCode::Enter => enter_pressed(&mut draw_state),
90 KeyCode::Esc => exit_routine(&mut term, &mut draw_state)?,
91 _ => (),
92 }
93 draw(&mut term, &mut draw_state)?;
94 }
95 Event::Mouse(event) => debug!("{:?}", event),
96 Event::Resize(width, _) => {
97 debug!("{:?}", width);
98 draw(&mut term, &mut draw_state)?;
99 }
100 }
101 }
102 Ok(())
103}
104
105pub fn draw(
107 term: &mut Terminal<CrosstermBackend<io::Stdout>>,
108 state: &DrawState,
109) -> Result<(), crossterm::ErrorKind> {
110 term.hide_cursor()?;
111
112 term.draw(|mut f| {
113 let size = f.size();
114
115 Block::default().borders(Borders::ALL).render(&mut f, size);
116
117 let chunks = Layout::default()
118 .direction(Direction::Vertical)
119 .margin(1)
120 .constraints([Constraint::Percentage(80), Constraint::Percentage(20)].as_ref())
121 .split(f.size());
122
123 let art_para =
124 scanner::parse_art("assets/ansi/art.ans", f.size().width as usize).unwrap_or_default();
125
126 Paragraph::new(
127 art_para
128 .into_iter()
129 .map(|line| Text::raw(line))
130 .collect::<Vec<Text>>()
131 .iter(),
132 )
133 .style(Style::default().fg(Color::White).bg(Color::Black))
134 .alignment(Alignment::Center)
135 .wrap(true)
136 .render(&mut f, chunks[0]);
137
138 SelectableList::default()
139 .block(Block::default().title("Operation").borders(Borders::ALL))
140 .items(&state.list_items)
141 .select(Some(state.selected_item))
142 .style(Style::default().fg(Color::White))
143 .highlight_style(Style::default().modifier(Modifier::ITALIC))
144 .highlight_symbol(">>")
145 .render(&mut f, chunks[1]);
146 })?;
147 Ok(())
148}