Skip to main content

ratatui_textarea/input/
termion.rs

1use super::{Input, Key};
2use ratatui_termion::termion::event::{Event, Key as KeyEvent, MouseButton, MouseEvent};
3
4impl From<Event> for Input {
5    /// Convert [`ratatui_termion::termion::event::Event`] into [`Input`].
6    fn from(event: Event) -> Self {
7        match event {
8            Event::Key(key) => Self::from(key),
9            Event::Mouse(mouse) => Self::from(mouse),
10            _ => Self::default(),
11        }
12    }
13}
14
15impl From<KeyEvent> for Input {
16    /// Convert [`ratatui_termion::termion::event::Key`] into [`Input`].
17    ///
18    /// termion does not provide a way to get Shift key's state. Instead termion passes key inputs
19    /// as-is. For example, when 'Shift + A' is pressed with US keyboard, termion passes
20    /// `ratatui_termion::termion::event::Key::Char('A')`. We cannot know how the 'A' character was
21    /// input.
22    ///
23    /// So the `shift` field of the returned `Input` instance is always `false` except for
24    /// combinations with arrow keys. For example,
25    /// `ratatui_termion::termion::event::Key::Char('A')` is converted to `Input { key:
26    /// Key::Char('A'), shift: false, .. }`.
27    fn from(key: KeyEvent) -> Self {
28        let (ctrl, alt, shift) = match key {
29            KeyEvent::Ctrl(_)
30            | KeyEvent::CtrlUp
31            | KeyEvent::CtrlRight
32            | KeyEvent::CtrlDown
33            | KeyEvent::CtrlLeft
34            | KeyEvent::CtrlHome
35            | KeyEvent::CtrlEnd => (true, false, false),
36            KeyEvent::Alt(_)
37            | KeyEvent::AltUp
38            | KeyEvent::AltRight
39            | KeyEvent::AltDown
40            | KeyEvent::AltLeft => (false, true, false),
41            KeyEvent::ShiftUp
42            | KeyEvent::ShiftRight
43            | KeyEvent::ShiftDown
44            | KeyEvent::ShiftLeft => (false, false, true),
45            _ => (false, false, false),
46        };
47
48        let key = match key {
49            KeyEvent::Char('\n' | '\r') => Key::Enter,
50            KeyEvent::Char(c) | KeyEvent::Ctrl(c) | KeyEvent::Alt(c) => Key::Char(c),
51            KeyEvent::Backspace => Key::Backspace,
52            KeyEvent::Left | KeyEvent::CtrlLeft | KeyEvent::AltLeft | KeyEvent::ShiftLeft => {
53                Key::Left
54            }
55            KeyEvent::Right | KeyEvent::CtrlRight | KeyEvent::AltRight | KeyEvent::ShiftRight => {
56                Key::Right
57            }
58            KeyEvent::Up | KeyEvent::CtrlUp | KeyEvent::AltUp | KeyEvent::ShiftUp => Key::Up,
59            KeyEvent::Down | KeyEvent::CtrlDown | KeyEvent::AltDown | KeyEvent::ShiftDown => {
60                Key::Down
61            }
62            KeyEvent::Home | KeyEvent::CtrlHome => Key::Home,
63            KeyEvent::End | KeyEvent::CtrlEnd => Key::End,
64            KeyEvent::PageUp => Key::PageUp,
65            KeyEvent::PageDown => Key::PageDown,
66            KeyEvent::BackTab => Key::Tab,
67            KeyEvent::Delete => Key::Delete,
68            KeyEvent::Esc => Key::Esc,
69            KeyEvent::F(x) => Key::F(x),
70            _ => Key::Null,
71        };
72
73        Input {
74            key,
75            ctrl,
76            alt,
77            shift,
78        }
79    }
80}
81
82impl From<MouseButton> for Key {
83    /// Convert [`ratatui_termion::termion::event::MouseButton`] into [`Key`].
84    fn from(button: MouseButton) -> Self {
85        match button {
86            MouseButton::WheelUp => Key::MouseScrollUp,
87            MouseButton::WheelDown => Key::MouseScrollDown,
88            _ => Key::Null,
89        }
90    }
91}
92
93impl From<MouseEvent> for Input {
94    /// Convert [`ratatui_termion::termion::event::MouseEvent`] into [`Input`].
95    fn from(mouse: MouseEvent) -> Self {
96        let key = if let MouseEvent::Press(button, ..) = mouse {
97            Key::from(button)
98        } else {
99            Key::Null
100        };
101        Self {
102            key,
103            ctrl: false,
104            alt: false,
105            shift: false,
106        }
107    }
108}
109
110#[cfg(test)]
111mod tests {
112    use super::*;
113    use crate::input::tests::input;
114
115    #[test]
116    fn key_to_input() {
117        for (from, to) in [
118            (
119                KeyEvent::Char('a'),
120                input(Key::Char('a'), false, false, false),
121            ),
122            (
123                KeyEvent::Ctrl('a'),
124                input(Key::Char('a'), true, false, false),
125            ),
126            (
127                KeyEvent::Alt('a'),
128                input(Key::Char('a'), false, true, false),
129            ),
130            (KeyEvent::Char('\n'), input(Key::Enter, false, false, false)),
131            (KeyEvent::Char('\r'), input(Key::Enter, false, false, false)),
132            (KeyEvent::F(1), input(Key::F(1), false, false, false)),
133            (KeyEvent::BackTab, input(Key::Tab, false, false, false)),
134            (KeyEvent::Null, input(Key::Null, false, false, false)),
135            #[cfg(feature = "termion")]
136            (KeyEvent::ShiftDown, input(Key::Down, false, false, true)),
137            #[cfg(feature = "termion")]
138            (KeyEvent::AltUp, input(Key::Up, false, true, false)),
139            #[cfg(feature = "termion")]
140            (KeyEvent::CtrlLeft, input(Key::Left, true, false, false)),
141            #[cfg(feature = "termion")]
142            (KeyEvent::CtrlHome, input(Key::Home, true, false, false)),
143        ] {
144            assert_eq!(Input::from(from), to, "{:?} -> {:?}", from, to);
145        }
146    }
147
148    #[test]
149    fn mouse_to_input() {
150        for (from, to) in [
151            (
152                MouseEvent::Press(MouseButton::WheelDown, 1, 1),
153                input(Key::MouseScrollDown, false, false, false),
154            ),
155            (
156                MouseEvent::Press(MouseButton::WheelUp, 1, 1),
157                input(Key::MouseScrollUp, false, false, false),
158            ),
159            (
160                MouseEvent::Press(MouseButton::Left, 1, 1),
161                input(Key::Null, false, false, false),
162            ),
163            (
164                MouseEvent::Release(1, 1),
165                input(Key::Null, false, false, false),
166            ),
167            (
168                MouseEvent::Hold(1, 1),
169                input(Key::Null, false, false, false),
170            ),
171        ] {
172            assert_eq!(Input::from(from), to, "{:?} -> {:?}", from, to);
173        }
174    }
175
176    #[test]
177    fn event_to_input() {
178        for (from, to) in [
179            (
180                Event::Key(KeyEvent::Char('a')),
181                input(Key::Char('a'), false, false, false),
182            ),
183            (
184                Event::Mouse(MouseEvent::Press(MouseButton::WheelDown, 1, 1)),
185                input(Key::MouseScrollDown, false, false, false),
186            ),
187            (
188                Event::Unsupported(vec![]),
189                input(Key::Null, false, false, false),
190            ),
191        ] {
192            assert_eq!(Input::from(from.clone()), to, "{:?} -> {:?}", from, to);
193        }
194    }
195}