Skip to main content

loam_egui/console/
mod.rs

1//! Console keys are consumed via `InputState::consume_key` before `TextEdit` sees them.
2
3mod key;
4mod panel;
5
6pub use loam_console::{
7    cmd, console_echo_enabled, parse_line, render_line, set_console_echo, subcommands, Command,
8    Console, ConsoleWriter, FnCommand, HistoryLine, Key, LineKind, SubcommandSet,
9    MAX_HISTORY_LINES, MAX_INPUT_HISTORY,
10};
11
12pub const ANIM_DURATION_SECS: f32 = 0.15;
13
14/// Half the viewport height, the Quake convention.
15pub const PANEL_HEIGHT_FRACTION: f32 = 0.5;
16
17pub trait ConsoleUi {
18    fn ui(&mut self, egui_ctx: &egui::Context);
19}
20
21impl<Ctx: 'static> ConsoleUi for Console<Ctx> {
22    fn ui(&mut self, egui_ctx: &egui::Context) {
23        // A printable key also emits a Text event; strip it or it leaks into the input.
24        let toggle = self.toggle_key();
25        let toggle_text = key::key_text(toggle);
26        let toggle_pressed = egui_ctx.input_mut(|i| {
27            let pressed = i.consume_key(egui::Modifiers::NONE, key::to_egui(toggle));
28            if pressed {
29                if let Some(t) = toggle_text {
30                    i.events
31                        .retain(|e| !matches!(e, egui::Event::Text(s) if s == t));
32                }
33            }
34            pressed
35        });
36        if toggle_pressed {
37            self.toggle();
38        }
39
40        if !self.is_open() {
41            let mut fired: Vec<String> = Vec::new();
42            egui_ctx.input_mut(|i| {
43                for (bound, line) in self.binds() {
44                    if i.consume_key(egui::Modifiers::NONE, key::to_egui(bound)) {
45                        fired.push(line.to_string());
46                    }
47                }
48            });
49            for line in fired {
50                self.execute(&line);
51            }
52        }
53
54        if self.is_open() {
55            handle_panel_keys(self, egui_ctx);
56        }
57
58        let target = if self.is_open() { 1.0 } else { 0.0 };
59        let progress = egui_ctx.animate_value_with_time(
60            egui::Id::new("loam_console_open_progress"),
61            target,
62            ANIM_DURATION_SECS,
63        );
64
65        let visible = if self.is_detached() {
66            self.is_open()
67        } else {
68            progress > 0.0
69        };
70        if visible {
71            panel::draw(self, egui_ctx, progress);
72        }
73    }
74}
75
76fn handle_panel_keys<Ctx: 'static>(console: &mut Console<Ctx>, ctx: &egui::Context) {
77    if ctx.input_mut(|i| i.consume_key(egui::Modifiers::NONE, egui::Key::Escape)) {
78        console.close();
79    }
80    if ctx.input_mut(|i| i.consume_key(egui::Modifiers::NONE, egui::Key::ArrowUp)) {
81        console.history_prev();
82    }
83    if ctx.input_mut(|i| i.consume_key(egui::Modifiers::NONE, egui::Key::ArrowDown)) {
84        console.history_next();
85    }
86    if ctx.input_mut(|i| i.consume_key(egui::Modifiers::NONE, egui::Key::Tab)) {
87        console.tab_complete();
88    }
89    if ctx.input_mut(|i| i.consume_key(egui::Modifiers::COMMAND, egui::Key::L)) {
90        console.clear_history();
91    }
92    if ctx.input_mut(|i| i.consume_key(egui::Modifiers::COMMAND, egui::Key::C)) {
93        console.clear_input();
94    }
95}