1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
use super::*;

use crossterm::event::KeyModifiers;
use tui_textarea::{CursorMove, TextArea};

pub struct TextInput {
    textarea: TextArea<'static>,
    lines: u16,
    changeable: bool,
}

pub enum TextInputResult {
    Cancel,
    Input(String),
    None,
}

impl TextInput {
    pub fn new(text: Option<&str>, initial: &str, lines: u16, changeable: bool) -> Self {
        let mut textarea = TextArea::default();
        textarea.set_style(Style::default());
        if let Some(text) = text {
            textarea.set_placeholder_text(text);
        }
        _ = textarea.insert_str(initial);
        if !changeable {
            textarea.move_cursor(CursorMove::Top);
        }
        Self {
            textarea,
            lines,
            changeable,
        }
    }
}

impl SizedWidget for TextInput {
    fn height(&self) -> Option<u16> {
        Some(self.lines)
    }
}

impl Draw for TextInput {
    fn draw(&mut self, area: Rect, f: &mut Frame<'_>) {
        f.render_widget(&self.textarea, area);
    }
}

impl ProcessEvent for TextInput {
    type Result = TextInputResult;
    fn input(&mut self, event: Event) -> TextInputResult {
        if let Event::Key(key) = event {
            let KeyEvent {
                code, modifiers, ..
            } = key;
            use KeyCode::*;
            if self.changeable {
                match (code, modifiers) {
                    (Esc, _) => return TextInputResult::Cancel,
                    (Enter, _) if self.lines == 1 => {
                        return TextInputResult::Input(self.textarea.lines().join("\n"));
                    }
                    (Char('s'), KeyModifiers::CONTROL) => {
                        return TextInputResult::Input(self.textarea.lines().join("\n"));
                    }
                    _ => {
                        _ = self.textarea.input(key);
                    }
                }
            } else {
                match (code, modifiers) {
                    (Esc | Enter | Char('q') | Char('x'), _) => return TextInputResult::Cancel,
                    (Home, _) => {
                        self.textarea.move_cursor(CursorMove::Top);
                    }
                    (End, _) => {
                        self.textarea.move_cursor(CursorMove::Bottom);
                    }
                    (PageDown | PageUp | Up | Down, _) => {
                        _ = self.textarea.input(key);
                    }
                    _ => {}
                }
            }
        }
        TextInputResult::None
    }
}