use crossterm::{
- event::{self, Event, KeyEvent, KeyCode, KeyEventKind},
+ event::{self, Event, KeyEvent, KeyCode, KeyEventKind, KeyModifiers},
terminal::{
disable_raw_mode, enable_raw_mode, EnterAlternateScreen,
LeaveAlternateScreen,
use super::activity_stack::*;
use super::client::Client;
use super::coloured_string::{ColouredString, ColouredStringSlice};
+use super::menu::*;
use super::text::{parse_html, Paragraph, TextFragment};
fn ratatui_style_from_colour(colour: char) -> Style {
TermEv(Event),
}
-enum HandleEventResult {
+pub enum HandleEventResult {
Nothing,
Beep,
Exit,
state: TuiLogicalState,
}
+#[derive(Eq, PartialEq, Debug, Clone, Copy)]
+pub enum OurKey {
+ Pr(char),
+ Ctrl(char),
+ FunKey(u8),
+ Backspace, Return, Escape,
+ Up, Down, Left, Right,
+ PgUp, PgDn, Home, End, Ins, Del,
+}
+
impl Tui {
pub fn run() -> std::io::Result<()> {
stdout().execute(EnterAlternateScreen)?;
result
}
+ fn translate_keypress(ev: KeyEvent) -> Vec<OurKey> {
+ let main = match ev.code {
+ KeyCode::Up => Some(OurKey::Up),
+ KeyCode::Down => Some(OurKey::Down),
+ KeyCode::Left => Some(OurKey::Left),
+ KeyCode::Right => Some(OurKey::Right),
+ KeyCode::PageUp => Some(OurKey::PgUp),
+ KeyCode::PageDown => Some(OurKey::PgDn),
+ KeyCode::Home => Some(OurKey::Home),
+ KeyCode::End => Some(OurKey::End),
+ KeyCode::Insert => Some(OurKey::Ins),
+ KeyCode::Delete => Some(OurKey::Del),
+ KeyCode::F(n) => Some(OurKey::FunKey(n)),
+ KeyCode::Esc => Some(OurKey::Escape),
+ KeyCode::Backspace => Some(OurKey::Backspace),
+ KeyCode::Enter => Some(OurKey::Return),
+ KeyCode::Tab => Some(OurKey::Ctrl('I')),
+ KeyCode::Char(c) => {
+ let initial = if ('\0'..' ').contains(&c) {
+ Some(OurKey::Ctrl(
+ char::from_u32((c as u32) + 0x40).unwrap()))
+ } else if ('\u{80}'..'\u{A0}').contains(&c) {
+ None
+ } else if ev.modifiers.contains(KeyModifiers::CONTROL) {
+ Some(OurKey::Ctrl(
+ char::from_u32(((c as u32) & 0x1F) + 0x40).unwrap()))
+ } else {
+ Some(OurKey::Pr(c))
+ };
+ match initial {
+ Some(OurKey::Ctrl('H')) => Some(OurKey::Backspace),
+ Some(OurKey::Ctrl('M')) => Some(OurKey::Return),
+ Some(OurKey::Ctrl('[')) => Some(OurKey::Escape),
+ other => other,
+ }
+ },
+ _ => None,
+ };
+ if let Some(main) = main {
+ if ev.modifiers.contains(KeyModifiers::ALT) {
+ vec! { OurKey::Escape, main }
+ } else {
+ vec! { main }
+ }
+ } else {
+ vec! {}
+ }
+ }
+
fn main_loop(&mut self) -> std::io::Result<()> {
- loop {
+ 'outer: loop {
self.terminal.draw(|frame| {
let area = frame.size();
let buf = frame.buffer_mut();
match ev {
Event::Key(key) => {
if key.kind == KeyEventKind::Press {
- match self.state.handle_keypress(key) {
- HandleEventResult::Beep => Self::beep()?,
-
- // FIXME: errors?
- HandleEventResult::Exit => break Ok(()),
-
- HandleEventResult::Nothing => (),
+ for ourkey in Self::translate_keypress(key) {
+ match self.state.handle_keypress(ourkey) {
+ HandleEventResult::Beep => {
+ Self::beep()?
+ },
+
+ // FIXME: errors?
+ HandleEventResult::Exit => {
+ break 'outer Ok(());
+ },
+
+ HandleEventResult::Nothing => (),
+ }
}
}
},
}
}
+pub trait ActivityState {
+ fn handle_keypress(&mut self, key: OurKey) -> HandleEventResult;
+}
+
struct TuiLogicalState {
activity_stack: ActivityStack,
+ activity_state: Box<dyn ActivityState>,
+}
+
+fn new_activity_state(activity: Activity) -> Box<dyn ActivityState> {
+ match activity {
+ Activity::NonUtil(NonUtilityActivity::MainMenu) => main_menu(),
+ _ => panic!("FIXME"),
+ }
}
impl TuiLogicalState {
fn new() -> Self {
+ let activity_stack = ActivityStack::new();
+ let activity_state = new_activity_state(activity_stack.top());
+
TuiLogicalState {
- activity_stack: ActivityStack::new(),
+ activity_stack: activity_stack,
+ activity_state: activity_state,
}
}
).slice());
}
- fn handle_keypress(&mut self, key: KeyEvent) -> HandleEventResult {
- match key.code {
- KeyCode::Char('q') => HandleEventResult::Exit,
- KeyCode::Char('b') => HandleEventResult::Beep,
- _ => HandleEventResult::Nothing,
+ fn handle_keypress(&mut self, key: OurKey) -> HandleEventResult {
+ dbg!(key);
+
+ match key {
+ // FIXME: ESC should really go to the utilities menu
+ OurKey::Escape => HandleEventResult::Exit,
+
+ _ => self.activity_state.handle_keypress(key),
}
}
}