megaui_macroquad/
lib.rs

1use std::collections::HashMap;
2
3use macroquad::prelude::*;
4
5pub use megaui;
6
7struct UiContext {
8    ui: megaui::Ui,
9    ui_draw_list: Vec<megaui::DrawList>,
10    font_texture: Texture2D,
11    megaui_textures: HashMap<u32, Texture2D>,
12    input_processed_this_frame: bool,
13}
14
15static mut UI_CONTEXT: Option<UiContext> = None;
16
17impl UiContext {
18    fn new(ctx: &mut miniquad::Context) -> UiContext {
19        let mut ui = megaui::Ui::new();
20
21        ui.set_clipboard_object(ClipboardObject);
22
23        let texture_data = &ui.font_atlas.texture;
24        let font_texture = Texture2D::from_rgba8(
25            ctx,
26            texture_data.width as u16,
27            texture_data.height as u16,
28            &texture_data.data,
29        );
30        font_texture.set_filter(ctx, FilterMode::Nearest);
31
32        UiContext {
33            ui,
34            ui_draw_list: vec![],
35            font_texture,
36            megaui_textures: HashMap::new(),
37            input_processed_this_frame: false,
38        }
39    }
40
41    fn get() -> &'static mut UiContext {
42        unsafe {
43            if UI_CONTEXT.is_none() {
44                let InternalGlContext {
45                    quad_context: ctx, ..
46                } = get_internal_gl();
47
48                UI_CONTEXT = Some(UiContext::new(ctx));
49            }
50
51            UI_CONTEXT.as_mut().unwrap()
52        }
53    }
54}
55
56pub struct ClipboardObject;
57
58impl megaui::ClipboardObject for ClipboardObject {
59    fn get(&self) -> Option<String> {
60        let InternalGlContext {
61            quad_context: ctx, ..
62        } = unsafe { get_internal_gl() };
63
64        miniquad::clipboard::get(ctx)
65    }
66
67    fn set(&mut self, data: &str) {
68        let InternalGlContext {
69            quad_context: ctx, ..
70        } = unsafe { get_internal_gl() };
71
72        miniquad::clipboard::set(ctx, data)
73    }
74}
75
76pub struct WindowParams {
77    pub label: String,
78    pub movable: bool,
79    pub close_button: bool,
80    pub titlebar: bool,
81}
82
83impl Default for WindowParams {
84    fn default() -> WindowParams {
85        WindowParams {
86            label: "".to_string(),
87            movable: true,
88            close_button: false,
89            titlebar: true,
90        }
91    }
92}
93
94pub fn set_ui_style(style: megaui::Style) {
95    let ctx = UiContext::get();
96
97    ctx.ui.set_style(style);
98}
99
100pub fn set_megaui_texture(id: u32, texture: Texture2D) {
101    let ctx = UiContext::get();
102
103    ctx.megaui_textures.insert(id, texture);
104}
105
106pub fn draw_window<F: FnOnce(&mut megaui::Ui)>(
107    id: megaui::Id,
108    position: glam::Vec2,
109    size: glam::Vec2,
110    params: impl Into<Option<WindowParams>>,
111    f: F,
112) -> bool {
113    let ctx = UiContext::get();
114
115    process_input();
116
117    let ui = &mut ctx.ui;
118    let params = params.into();
119
120    megaui::widgets::Window::new(
121        id,
122        megaui::Vector2::new(position.x(), position.y()),
123        megaui::Vector2::new(size.x(), size.y()),
124    )
125    .label(params.as_ref().map_or("", |params| &params.label))
126    .titlebar(params.as_ref().map_or(true, |params| params.titlebar))
127    .movable(params.as_ref().map_or(true, |params| params.movable))
128    .close_button(params.as_ref().map_or(false, |params| params.close_button))
129    .ui(ui, f)
130}
131
132/// Check for megaui mouse overlap
133pub fn mouse_over_ui() -> bool {
134    let mouse_position = mouse_position();
135
136    UiContext::get()
137        .ui
138        .is_mouse_over(megaui::Vector2::new(mouse_position.0, mouse_position.1))
139}
140
141/// Check for megaui mouse captured by scrolls, drags etc
142pub fn mouse_captured() -> bool {
143    UiContext::get().ui.is_mouse_captured()
144}
145
146fn process_input() {
147    use megaui::InputHandler;
148
149    let mut ctx = UiContext::get();
150
151    if ctx.input_processed_this_frame {
152        return;
153    }
154    let mouse_position = mouse_position();
155
156    ctx.ui.mouse_move(mouse_position);
157
158    if is_mouse_button_pressed(MouseButton::Left) {
159        ctx.ui.mouse_down(mouse_position);
160    }
161    if is_mouse_button_released(MouseButton::Left) {
162        ctx.ui.mouse_up(mouse_position);
163    }
164
165    let shift = is_key_down(KeyCode::LeftShift) || is_key_down(KeyCode::RightShift);
166    let ctrl = is_key_down(KeyCode::LeftControl) || is_key_down(KeyCode::RightControl);
167
168    while let Some(c) = get_char_pressed() {
169        if ctrl == false {
170            ctx.ui.char_event(c, false, false);
171        }
172    }
173
174    if is_key_down(KeyCode::Up) {
175        ctx.ui.key_down(megaui::KeyCode::Up, shift, ctrl);
176    }
177    if is_key_down(KeyCode::Down) {
178        ctx.ui.key_down(megaui::KeyCode::Down, shift, ctrl);
179    }
180    if is_key_down(KeyCode::Right) {
181        ctx.ui.key_down(megaui::KeyCode::Right, shift, ctrl);
182    }
183    if is_key_down(KeyCode::Left) {
184        ctx.ui.key_down(megaui::KeyCode::Left, shift, ctrl);
185    }
186    if is_key_down(KeyCode::Home) {
187        ctx.ui.key_down(megaui::KeyCode::Home, shift, ctrl);
188    }
189    if is_key_down(KeyCode::End) {
190        ctx.ui.key_down(megaui::KeyCode::End, shift, ctrl);
191    }
192    if is_key_down(KeyCode::Delete) {
193        ctx.ui.key_down(megaui::KeyCode::Delete, shift, ctrl);
194    }
195    if is_key_down(KeyCode::Backspace) {
196        ctx.ui.key_down(megaui::KeyCode::Backspace, shift, ctrl);
197    }
198    if is_key_down(KeyCode::Enter) {
199        ctx.ui.key_down(megaui::KeyCode::Enter, shift, ctrl);
200    }
201    if is_key_down(KeyCode::Tab) {
202        ctx.ui.key_down(megaui::KeyCode::Tab, shift, ctrl);
203    }
204    if is_key_down(KeyCode::Z) {
205        ctx.ui.key_down(megaui::KeyCode::Z, shift, ctrl);
206    }
207    if is_key_down(KeyCode::Y) {
208        ctx.ui.key_down(megaui::KeyCode::Y, shift, ctrl);
209    }
210    if is_key_down(KeyCode::C) {
211        ctx.ui.key_down(megaui::KeyCode::C, shift, ctrl);
212    }
213    if is_key_down(KeyCode::X) {
214        ctx.ui.key_down(megaui::KeyCode::X, shift, ctrl);
215    }
216    if is_key_down(KeyCode::V) {
217        ctx.ui.key_down(megaui::KeyCode::V, shift, ctrl);
218    }
219    if is_key_down(KeyCode::A) {
220        ctx.ui.key_down(megaui::KeyCode::A, shift, ctrl);
221    }
222    if is_key_down(KeyCode::Escape) {
223        ctx.ui.key_down(megaui::KeyCode::Escape, shift, ctrl);
224    }
225    if is_key_down(KeyCode::Enter) {
226        ctx.ui.key_down(megaui::KeyCode::Enter, shift, ctrl);
227    }
228    if is_key_down(KeyCode::LeftControl) || is_key_down(KeyCode::RightControl) {
229        ctx.ui.key_down(megaui::KeyCode::Control, shift, ctrl);
230    }
231    let (wheel_x, wheel_y) = mouse_wheel();
232    ctx.ui.mouse_wheel(wheel_x, -wheel_y);
233
234    ctx.input_processed_this_frame = true;
235}
236
237/// Tick megaui state and draw everything
238/// Should be called once per frame at the end of the frame
239pub fn draw_megaui() {
240    let mut ctx = UiContext::get();
241
242    ctx.input_processed_this_frame = false;
243
244    let InternalGlContext { quad_gl, .. } = unsafe { get_internal_gl() };
245
246    ctx.ui_draw_list.clear();
247
248    ctx.ui.render(&mut ctx.ui_draw_list);
249    let mut ui_draw_list = vec![];
250
251    std::mem::swap(&mut ui_draw_list, &mut ctx.ui_draw_list);
252
253    quad_gl.texture(Some(ctx.font_texture));
254
255    for draw_command in &ui_draw_list {
256        if let Some(texture) = draw_command.texture {
257            quad_gl.texture(Some(ctx.megaui_textures[&texture]));
258        } else {
259            quad_gl.texture(Some(ctx.font_texture));
260        }
261        quad_gl.scissor(
262            draw_command
263                .clipping_zone
264                .map(|rect| (rect.x as i32, rect.y as i32, rect.w as i32, rect.h as i32)),
265        );
266        quad_gl.draw_mode(DrawMode::Triangles);
267        quad_gl.geometry(&draw_command.vertices, &draw_command.indices);
268    }
269    quad_gl.texture(None);
270
271    std::mem::swap(&mut ui_draw_list, &mut ctx.ui_draw_list);
272
273    ctx.ui.new_frame(get_frame_time());
274}