use crossterm::event::{read, Event, KeyCode}; use tui::{backend::Backend, Frame, layout::Layout}; use crate::{state::GameState, person::Person, entity::Entity}; /** * |........................| * | Conversations/Selection| * | | * |........................| * | Available Options | * |........................| */ enum AppStatus { Initial, GuestSelection, TalkToGuest(Option), } pub struct App<'a> { state: GameState, guest_list: Vec>, guest_list_state: tui::widgets::ListState, status: AppStatus, conversation: Vec>, } impl<'a> App<'a> { pub fn new(state: GameState) -> App<'a> { App { state: state, guest_list: vec![], guest_list_state: tui::widgets::ListState::default(), status: AppStatus::Initial, conversation: Vec::new(), } } pub fn step(&mut self) -> bool { match &self.status { AppStatus::Initial => { // determine guests self.guest_list = self.state.guests().iter().map(|guest| { tui::widgets::ListItem::new(guest.name.clone()) }).collect(); self.guest_list_state = tui::widgets::ListState::default(); self.guest_list_state.select(Some(0)); self.status = AppStatus::GuestSelection; true }, AppStatus::GuestSelection => { match read() { Ok(Event::Key(event)) => { match event.code { KeyCode::Up => { let selected = self.guest_list_state.selected().unwrap(); if selected > 0 { self.guest_list_state.select(Some(selected - 1)); } }, KeyCode::Down => { let selected = self.guest_list_state.selected().unwrap(); if selected < self.guest_list.len() - 1 { self.guest_list_state.select(Some(selected + 1)); } }, KeyCode::Enter => { let selected = self.guest_list_state.selected().unwrap(); let guest = &self.state.guests()[selected]; self.conversation = Vec::new(); self.greet_guest(guest); self.status = AppStatus::TalkToGuest(guest.id()); }, KeyCode::Char('.') => { self.state.step(); self.status = AppStatus::Initial; }, KeyCode::Esc => { return false; }, _ => {} } }, _ => {} } true }, AppStatus::TalkToGuest(guest) => { match read() { Ok(Event::Key(event)) => { match event.code { KeyCode::Esc => { self.status = AppStatus::GuestSelection; }, KeyCode::Char('a') => { self.ask_business(*guest); }, _ => {} } }, _ => {} } true } } } pub fn draw(&mut self, f: &mut Frame) { match &self.status { AppStatus::Initial => { self.draw_initial(f); }, AppStatus::GuestSelection => { self.draw_guest_selection(f); }, AppStatus::TalkToGuest(guest_id) => { self.draw_talk_to_guest(f, *guest_id); } } } pub fn draw_initial(&mut self, f: &mut Frame) {} pub fn draw_guest_selection(&mut self, f: &mut Frame) { let chunks = Layout::default() .direction(tui::layout::Direction::Vertical) .constraints( [ tui::layout::Constraint::Min(3), tui::layout::Constraint::Length(2) ] .as_ref(), ) .split(f.size()); let main_window = tui::widgets::List::new(self.guest_list.clone()) .block(tui::widgets::Block::default().title("Guests").borders(tui::widgets::Borders::ALL)) .style(tui::style::Style::default().fg(tui::style::Color::White)) .highlight_style(tui::style::Style::default().add_modifier(tui::style::Modifier::ITALIC)) .highlight_symbol(">>"); let key_style = tui::style::Style::default() .add_modifier(tui::style::Modifier::BOLD) .fg(tui::style::Color::Green) .bg(tui::style::Color::Black); let control_text = tui::text::Spans::from(vec![ tui::text::Span::styled("↑↓", key_style), tui::text::Span::raw(" select guest"), tui::text::Span::raw(" "), tui::text::Span::styled("⏎", key_style), tui::text::Span::raw(" talk to guest"), tui::text::Span::raw(" "), tui::text::Span::styled(".", key_style), tui::text::Span::raw(" pass one day"), tui::text::Span::raw(" "), tui::text::Span::styled("Esc", key_style), tui::text::Span::raw(" quit"), ]); let controls = tui::widgets::Paragraph::new(control_text) .style(tui::style::Style::default().fg(tui::style::Color::White)); f.render_stateful_widget(main_window, chunks[0], &mut self.guest_list_state); f.render_widget(controls, chunks[1]); } fn draw_talk_to_guest(&self, f: &mut Frame, guest: Option) { let guest = self.state.get_person(guest.unwrap()).unwrap(); let key_style = tui::style::Style::default() .add_modifier(tui::style::Modifier::BOLD) .fg(tui::style::Color::Green) .bg(tui::style::Color::Black); let mut full_text = self.conversation.clone(); full_text.push(tui::text::Spans::from(vec![ tui::text::Span::styled("(a) ", key_style), tui::text::Span::raw("What's your business?"), ])); let text = tui::text::Text::from(full_text); let main_window = tui::widgets::Paragraph::new(text) .block(tui::widgets::Block::default().title(guest.name.clone()).borders(tui::widgets::Borders::ALL)) .style(tui::style::Style::default().fg(tui::style::Color::White)); f.render_widget(main_window, f.size()); } /** * Conversation */ fn greet_guest(&mut self, guest: &Person) { self.conversation.push( tui::text::Spans::from(vec![ tui::text::Span::styled("Greetings traveller?\n", tui::style::Style::default().fg(tui::style::Color::Yellow)), ]) ); self.conversation.push( tui::text::Spans::from(vec![ tui::text::Span::raw("Hello, I'm "), tui::text::Span::styled(guest.name.clone(), tui::style::Style::default().add_modifier(tui::style::Modifier::BOLD)), tui::text::Span::raw(".\n"), ]) ); } fn ask_business(&mut self, guest: Option) { let guest = self.state.get_person(guest.unwrap()).unwrap(); self.conversation.push( tui::text::Spans::from(vec![ tui::text::Span::styled("What's your business?\n", tui::style::Style::default().fg(tui::style::Color::Yellow)) ]) ); self.conversation.push( tui::text::Spans::from(vec![ tui::text::Span::raw(guest.say_agenda(& self.state)), tui::text::Span::raw("\n"), ]) ); } }