Shows what key you pressed last
| 1 | use std::{ |
| 2 | io, |
| 3 | time::{ |
| 4 | Duration, |
| 5 | Instant, |
| 6 | }, |
| 7 | }; |
| 8 | |
| 9 | use crossterm::event::{ |
| 10 | self, |
| 11 | poll, |
| 12 | KeyCode, |
| 13 | KeyModifiers, |
| 14 | KeyEvent, |
| 15 | KeyEventKind, |
| 16 | Event, |
| 17 | }; |
| 18 | |
| 19 | use ratatui::{ |
| 20 | buffer::Buffer, |
| 21 | layout::{Rect, Constraint}, |
| 22 | text::{Line, Span}, |
| 23 | style::Stylize, |
| 24 | symbols::border, |
| 25 | DefaultTerminal, |
| 26 | widgets::{Widget, Block}, |
| 27 | Frame, |
| 28 | }; |
| 29 | |
| 30 | #[derive(Debug, PartialEq, Eq)] |
| 31 | enum State { |
| 32 | Run, |
| 33 | Consider, |
| 34 | Quit, |
| 35 | } |
| 36 | |
| 37 | #[derive(Debug)] |
| 38 | pub struct ShowKey { |
| 39 | state: State, |
| 40 | current_key: Option<KeyEvent>, |
| 41 | key_time: Option<Instant>, |
| 42 | } |
| 43 | |
| 44 | impl Default for ShowKey { |
| 45 | fn default() -> Self { |
| 46 | Self { |
| 47 | state: State::Run, |
| 48 | current_key: None, |
| 49 | key_time: None, |
| 50 | } |
| 51 | } |
| 52 | } |
| 53 | |
| 54 | impl Widget for &ShowKey { |
| 55 | fn render(self, area: Rect, buf: &mut Buffer) { |
| 56 | |
| 57 | let title = Line::from(" Show Key ".bold()); |
| 58 | |
| 59 | let block = Block::bordered() |
| 60 | .title(title.centered()) |
| 61 | .border_set(border::ROUNDED); |
| 62 | |
| 63 | let block_area = area.centered( |
| 64 | Constraint::Length(25), |
| 65 | Constraint::Length(7), |
| 66 | ); |
| 67 | |
| 68 | let pressed = Line::from(self |
| 69 | .current_key.map(|key| vec![ |
| 70 | Span::from(match key.modifiers { |
| 71 | KeyModifiers::NONE => "".into(), |
| 72 | other => format!("{other} + "), |
| 73 | }), |
| 74 | Span::from(match key.code { |
| 75 | KeyCode::Char(c) => if c == ' ' { |
| 76 | "\" \"".into() |
| 77 | } else { |
| 78 | c.to_lowercase().to_string() |
| 79 | }, |
| 80 | KeyCode::F(f) => format!("F{f}"), |
| 81 | other => format!("{other}"), |
| 82 | }), |
| 83 | ] |
| 84 | |
| 85 | |
| 86 | ) |
| 87 | .unwrap_or(vec![Span::from("Fuck you")]) |
| 88 | ); |
| 89 | |
| 90 | let pressed_area = block |
| 91 | .inner(block_area) |
| 92 | .centered( |
| 93 | Constraint::Length(pressed.width() as u16), |
| 94 | Constraint::Length(1), |
| 95 | ); |
| 96 | |
| 97 | block.render(block_area, buf); |
| 98 | pressed.render(pressed_area, buf); |
| 99 | } |
| 100 | } |
| 101 | |
| 102 | impl ShowKey { |
| 103 | fn run(&mut self, terminal: &mut DefaultTerminal) -> io::Result<()> { |
| 104 | |
| 105 | terminal.draw(|frame| self.draw(frame))?; |
| 106 | |
| 107 | while self.state != State::Quit { |
| 108 | |
| 109 | if poll(Duration::from_millis(100))? { |
| 110 | self.handle_events()?; |
| 111 | } |
| 112 | |
| 113 | match self.key_time { |
| 114 | Some(time) if time.elapsed() >= Duration::from_secs(1) => { |
| 115 | self.unset(); |
| 116 | self.detime(); |
| 117 | }, |
| 118 | _ => {}, |
| 119 | } |
| 120 | |
| 121 | terminal.draw(|frame| self.draw(frame))?; |
| 122 | } |
| 123 | Ok(()) |
| 124 | } |
| 125 | |
| 126 | fn draw(&self, frame: &mut Frame) { |
| 127 | frame.render_widget(self, frame.area()); |
| 128 | } |
| 129 | |
| 130 | fn handle_events(&mut self) -> io::Result<()> { |
| 131 | match event::read()? { |
| 132 | Event::Key(key_event) if key_event.kind == KeyEventKind::Press => { |
| 133 | self.key_time = Some(Instant::now()); |
| 134 | self.handle_key_event(key_event); |
| 135 | }, |
| 136 | _ => {}, |
| 137 | } |
| 138 | Ok(()) |
| 139 | } |
| 140 | |
| 141 | fn handle_key_event(&mut self, key_event: KeyEvent) { |
| 142 | |
| 143 | self.current_key = Some(key_event); |
| 144 | |
| 145 | match key_event.code { |
| 146 | KeyCode::Esc => self.deeper(), |
| 147 | _ => self.higher(), |
| 148 | } |
| 149 | } |
| 150 | |
| 151 | fn unset(&mut self) { |
| 152 | self.current_key = None; |
| 153 | } |
| 154 | |
| 155 | fn detime(&mut self) { |
| 156 | self.key_time = None; |
| 157 | } |
| 158 | |
| 159 | fn deeper(&mut self) { |
| 160 | match self.state { |
| 161 | State::Run => self.state = State::Consider, |
| 162 | State::Consider => self.state = State::Quit, |
| 163 | State::Quit => {} |
| 164 | } |
| 165 | } |
| 166 | |
| 167 | fn higher(&mut self) { |
| 168 | match self.state { |
| 169 | State::Run => {} |
| 170 | State::Consider => self.state = State::Run, |
| 171 | State::Quit => self.state = State::Consider, |
| 172 | } |
| 173 | } |
| 174 | } |
| 175 | |
| 176 | |
| 177 | fn main() -> io::Result<()> { |
| 178 | ratatui::run(|term| ShowKey::default().run(term)) |
| 179 | } |