Skip to main content

geng_egui/
lib.rs

1pub use egui;
2
3use geng::prelude::*;
4
5mod painter;
6
7use painter::*;
8
9/// Bindings for [egui](https://github.com/emilk/egui).
10pub struct EguiGeng {
11    geng: Geng,
12    egui_ctx: egui::CtxRef,
13    egui_input: egui::RawInput,
14    painter: Painter,
15    shapes: Option<Vec<egui::epaint::ClippedShape>>,
16    screen_height: f32,
17}
18
19impl EguiGeng {
20    pub fn new(geng: &Geng) -> Self {
21        Self {
22            geng: geng.clone(),
23            egui_ctx: egui::CtxRef::default(),
24            egui_input: egui::RawInput::default(),
25            painter: Painter::new(geng),
26            shapes: None,
27            screen_height: 1.0,
28        }
29    }
30
31    /// Use to call ui methods
32    pub fn get_context(&self) -> &egui::CtxRef {
33        &self.egui_ctx
34    }
35
36    /// Call at the beginning of the frame.
37    /// Implement your ui logic inbetween [begin_frame] and [end_frame].
38    pub fn begin_frame(&mut self) {
39        self.gather_input();
40        self.egui_ctx.begin_frame(self.egui_input.take());
41    }
42
43    /// Call at the end of the frame.
44    /// Should be called after the ui logic.
45    pub fn end_frame(&mut self) {
46        let (_output, shapes) = self.egui_ctx.end_frame();
47        if self.shapes.is_some() {
48            error!("Egui contents have not been drawn. Ensure to call `draw` after `end_frame`");
49        }
50        self.shapes = Some(shapes);
51
52        // TODO: process output
53    }
54
55    /// Call after [end_frame] to draw the ui.
56    pub fn draw(&mut self, framebuffer: &mut ugli::Framebuffer) {
57        // Update screen size
58        let framebuffer_size = framebuffer.size().map(|x| x as f32);
59        self.screen_height = framebuffer_size.y;
60        self.egui_input.screen_rect = Some(egui::Rect::from_min_size(
61            egui::Pos2::ZERO,
62            egui::Vec2::new(framebuffer_size.x, framebuffer_size.y),
63        ));
64
65        // Render mesh
66        if let Some(shapes) = self.shapes.take() {
67            let paint_jobs = self.egui_ctx.tessellate(shapes);
68            self.painter
69                .paint(framebuffer, paint_jobs, &self.egui_ctx.texture());
70        } else {
71            error!("Failed to draw egui. Ensure to call `draw` after `end_frame`");
72        }
73    }
74
75    /// Call every time you receive an event from the engine in [geng::State::handle_event].
76    pub fn handle_event(&mut self, event: geng::Event) {
77        match event {
78            geng::Event::Wheel { delta } => {
79                if self.geng.window().is_key_pressed(geng::Key::LShift) {
80                    self.egui_input.scroll_delta = egui::Vec2::new(delta as f32, 0.0);
81                } else {
82                    self.egui_input.scroll_delta = egui::Vec2::new(0.0, delta as f32);
83                }
84            }
85            geng::Event::KeyDown { key } => {
86                if let Some(key) = egui_key(key) {
87                    let modifiers = self.get_modifiers();
88                    self.egui_input.events.push(egui::Event::Key {
89                        key,
90                        modifiers,
91                        pressed: true,
92                    });
93                    if let Some(mut symbol) = key_char(key) {
94                        if modifiers.shift {
95                            symbol = symbol.to_uppercase().next().unwrap();
96                        }
97                        self.egui_input
98                            .events
99                            .push(egui::Event::Text(symbol.to_string()));
100                    }
101                }
102            }
103            geng::Event::KeyUp { key } => {
104                if let Some(key) = egui_key(key) {
105                    self.egui_input.events.push(egui::Event::Key {
106                        key,
107                        modifiers: self.get_modifiers(),
108                        pressed: false,
109                    });
110                }
111            }
112            geng::Event::MouseDown { position, button } => {
113                let button = egui_button(button);
114                self.egui_input.events.push(egui::Event::PointerButton {
115                    pos: self.mouse_to_pos(position),
116                    button,
117                    pressed: true,
118                    modifiers: self.get_modifiers(),
119                });
120            }
121            geng::Event::MouseMove { position, .. } => {
122                self.egui_input
123                    .events
124                    .push(egui::Event::PointerMoved(self.mouse_to_pos(position)));
125            }
126            geng::Event::MouseUp { position, button } => {
127                let button = egui_button(button);
128                self.egui_input.events.push(egui::Event::PointerButton {
129                    pos: self.mouse_to_pos(position),
130                    button,
131                    pressed: false,
132                    modifiers: self.get_modifiers(),
133                });
134            }
135            _ => (),
136        }
137    }
138
139    fn gather_input(&mut self) {
140        self.egui_input.modifiers = self.get_modifiers();
141    }
142
143    fn get_modifiers(&self) -> egui::Modifiers {
144        let window = self.geng.window();
145        egui::Modifiers {
146            alt: window.is_key_pressed(geng::Key::LAlt),
147            ctrl: window.is_key_pressed(geng::Key::LCtrl),
148            shift: window.is_key_pressed(geng::Key::LShift),
149            ..default()
150        }
151    }
152
153    fn mouse_to_pos(&self, mouse: Vec2<f64>) -> egui::Pos2 {
154        egui::Pos2::new(mouse.x as f32, self.screen_height - mouse.y as f32)
155    }
156}
157
158/// Converts [egui::Pos2] to [Vec2]. Moves the origin from top-left to bottom-left.
159fn pos_to_vec(pos: egui::Pos2, height: f32) -> Vec2<f32> {
160    vec2(pos.x, height - pos.y)
161}
162
163fn egui_button(geng_button: geng::MouseButton) -> egui::PointerButton {
164    match geng_button {
165        geng::MouseButton::Left => egui::PointerButton::Primary,
166        geng::MouseButton::Middle => egui::PointerButton::Middle,
167        geng::MouseButton::Right => egui::PointerButton::Secondary,
168    }
169}
170
171fn egui_key(geng_key: geng::Key) -> Option<egui::Key> {
172    use egui::Key::*;
173    match geng_key {
174        geng::Key::Num0 => Some(Num0),
175        geng::Key::Num1 => Some(Num1),
176        geng::Key::Num2 => Some(Num2),
177        geng::Key::Num3 => Some(Num3),
178        geng::Key::Num4 => Some(Num4),
179        geng::Key::Num5 => Some(Num5),
180        geng::Key::Num6 => Some(Num6),
181        geng::Key::Num7 => Some(Num7),
182        geng::Key::Num8 => Some(Num8),
183        geng::Key::Num9 => Some(Num9),
184        geng::Key::A => Some(A),
185        geng::Key::B => Some(B),
186        geng::Key::C => Some(C),
187        geng::Key::D => Some(D),
188        geng::Key::E => Some(E),
189        geng::Key::F => Some(F),
190        geng::Key::G => Some(G),
191        geng::Key::H => Some(H),
192        geng::Key::I => Some(I),
193        geng::Key::J => Some(J),
194        geng::Key::K => Some(K),
195        geng::Key::L => Some(L),
196        geng::Key::M => Some(M),
197        geng::Key::N => Some(N),
198        geng::Key::O => Some(O),
199        geng::Key::P => Some(P),
200        geng::Key::Q => Some(Q),
201        geng::Key::R => Some(R),
202        geng::Key::S => Some(S),
203        geng::Key::T => Some(T),
204        geng::Key::U => Some(U),
205        geng::Key::V => Some(V),
206        geng::Key::W => Some(W),
207        geng::Key::X => Some(X),
208        geng::Key::Y => Some(Y),
209        geng::Key::Z => Some(Z),
210        geng::Key::Escape => Some(Escape),
211        geng::Key::Space => Some(Space),
212        geng::Key::Enter => Some(Enter),
213        geng::Key::Backspace => Some(Backspace),
214        geng::Key::Left => Some(ArrowLeft),
215        geng::Key::Right => Some(ArrowRight),
216        geng::Key::Up => Some(ArrowUp),
217        geng::Key::Down => Some(ArrowDown),
218        geng::Key::PageUp => Some(PageUp),
219        geng::Key::PageDown => Some(PageDown),
220        _ => None,
221    }
222}
223
224fn key_char(key: egui::Key) -> Option<char> {
225    match key {
226        egui::Key::A => Some('a'),
227        egui::Key::B => Some('b'),
228        egui::Key::C => Some('c'),
229        egui::Key::D => Some('d'),
230        egui::Key::E => Some('e'),
231        egui::Key::F => Some('f'),
232        egui::Key::G => Some('g'),
233        egui::Key::H => Some('h'),
234        egui::Key::I => Some('i'),
235        egui::Key::J => Some('j'),
236        egui::Key::K => Some('k'),
237        egui::Key::L => Some('l'),
238        egui::Key::M => Some('m'),
239        egui::Key::N => Some('n'),
240        egui::Key::O => Some('o'),
241        egui::Key::P => Some('p'),
242        egui::Key::Q => Some('q'),
243        egui::Key::R => Some('r'),
244        egui::Key::S => Some('s'),
245        egui::Key::T => Some('t'),
246        egui::Key::U => Some('u'),
247        egui::Key::V => Some('v'),
248        egui::Key::W => Some('w'),
249        egui::Key::X => Some('x'),
250        egui::Key::Y => Some('y'),
251        egui::Key::Z => Some('z'),
252        egui::Key::Num0 => Some('0'),
253        egui::Key::Num1 => Some('1'),
254        egui::Key::Num2 => Some('2'),
255        egui::Key::Num3 => Some('3'),
256        egui::Key::Num4 => Some('4'),
257        egui::Key::Num5 => Some('5'),
258        egui::Key::Num6 => Some('6'),
259        egui::Key::Num7 => Some('7'),
260        egui::Key::Num8 => Some('8'),
261        egui::Key::Num9 => Some('9'),
262        _ => None,
263    }
264}