guise/input/keys.rs
1//! Keyboard handling for single-line text fields.
2//!
3//! Shared by [`TextInput`](super::TextInput) and by hosts that drive a
4//! [`TextEdit`](super::TextEdit) directly — inline fields that render their own
5//! chrome (a search bar, a palette) rather than embedding the full component.
6//! macOS/Linux conventions: Option = word-wise, Cmd = line-wise, plus the
7//! Emacs-style Ctrl+A / Ctrl+E / Ctrl+K.
8
9use gpui::Keystroke;
10
11use super::edit::TextEdit;
12
13/// What a keystroke did to a single-line field, so the host can react.
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum KeyOutcome {
16 /// Enter — the host should commit.
17 Submit,
18 /// Escape — the host should dismiss.
19 Cancel,
20 /// The field changed; redraw.
21 Edited,
22 /// Not handled here; the host may act on it (e.g. Tab, Cmd+W).
23 Pass,
24}
25
26/// Apply `ks` to `edit`, returning what the host should do. `platform` is Cmd
27/// on macOS; `alt` is Option.
28pub fn apply_key(edit: &mut TextEdit, ks: &Keystroke) -> KeyOutcome {
29 let m = &ks.modifiers;
30 match ks.key.as_str() {
31 "enter" => return KeyOutcome::Submit,
32 "escape" => return KeyOutcome::Cancel,
33 "left" => {
34 if m.platform {
35 edit.home();
36 } else if m.alt {
37 edit.word_left();
38 } else {
39 edit.left();
40 }
41 return KeyOutcome::Edited;
42 }
43 "right" => {
44 if m.platform {
45 edit.end();
46 } else if m.alt {
47 edit.word_right();
48 } else {
49 edit.right();
50 }
51 return KeyOutcome::Edited;
52 }
53 // Single-line: vertical keys collapse to the line edges.
54 "up" | "home" => {
55 edit.home();
56 return KeyOutcome::Edited;
57 }
58 "down" | "end" => {
59 edit.end();
60 return KeyOutcome::Edited;
61 }
62 "backspace" => {
63 if m.platform {
64 edit.delete_to_start();
65 } else if m.alt {
66 edit.delete_word_back();
67 } else {
68 edit.backspace();
69 }
70 return KeyOutcome::Edited;
71 }
72 "delete" => {
73 if m.platform {
74 edit.delete_to_end();
75 } else if m.alt {
76 edit.delete_word_forward();
77 } else {
78 edit.delete();
79 }
80 return KeyOutcome::Edited;
81 }
82 "k" if m.control => {
83 edit.delete_to_end();
84 return KeyOutcome::Edited;
85 }
86 "a" if m.control => {
87 edit.home();
88 return KeyOutcome::Edited;
89 }
90 "e" if m.control => {
91 edit.end();
92 return KeyOutcome::Edited;
93 }
94 _ => {}
95 }
96 // Printable input: never on Cmd/Ctrl chords (those are shortcuts);
97 // Option+key is allowed so composed glyphs land.
98 if !m.platform && !m.control {
99 if let Some(t) = ks.key_char.as_deref().filter(|t| !t.is_empty()) {
100 edit.insert(t);
101 return KeyOutcome::Edited;
102 }
103 }
104 KeyOutcome::Pass
105}