Skip to main content

yakui_macroquad/
lib.rs

1//!
2//! `yakui-macroquad` integrates yakui with macroquad.
3//!
4//! # Usage
5//! To use this library, you call [`start`] when you wish to begin
6//! submitting ui draw commands and [`finish`] when you are done.
7//!
8//! Though, there's also the [`ui`] helper that takes a closure and will call [`start`] before your code and [`finish`] after.
9//! But using [`start`] and [`finish`] is closer to how yakui itself does it, so that's probably what you should do.
10//!
11//! To then render your ui, simply call [`draw`]!
12//!
13//! ```no_run
14//! use macroquad::prelude::*;
15//! use yakui_macroquad::*;
16//! use yakui::*;
17//!
18//! #[macroquad::main("yakui-macroquad-example")]
19//! async fn main() {
20//!    
21//!     loop {
22//!
23//!         clear_background(WHITE);
24//!
25//!         yakui_macroquad::start();
26//!
27//!         yakui::center(|| {
28//!             let mut text_box = yakui::widgets::Text::new(32.0, "hello, world!");
29//!             text_box.style.color = yakui::Color::BLACK;
30//!             text_box.show();
31//!         });
32//!
33//!         yakui_macroquad::finish();
34//!
35//!         yakui_macroquad::draw();
36//!
37//!         next_frame().await;
38//!
39//!     }
40//!    
41//! }
42//!```
43
44use send_wrapper::SendWrapper;
45use std::sync::{RwLock, RwLockWriteGuard};
46
47use macroquad::miniquad as mq;
48use macroquad::window::get_internal_gl;
49use yakui_miniquad::*;
50
51pub use macroquad;
52
53struct Yakui(YakuiMiniQuad, usize);
54
55// Global variable and global functions because it's more like macroquad way
56static YAKUI: RwLock<Option<SendWrapper<Yakui>>> = RwLock::new(None);
57
58fn get_yakui() -> RwLockWriteGuard<'static, Option<SendWrapper<Yakui>>> {
59    match YAKUI.try_write() {
60        Ok(mut yakui) => {
61            if yakui.is_some() {
62                yakui
63            } else {
64                *yakui = Some(SendWrapper::new(Yakui::new()));
65                yakui
66            }
67        }
68        Err(_) => panic!(
69            "tried to borrow yakui mutably twice, did you accidentally nest ui or cfg calls?"
70        ),
71    }
72}
73
74impl Yakui {
75    fn new() -> Self {
76        Self(
77            YakuiMiniQuad::new(unsafe { get_internal_gl() }.quad_context),
78            macroquad::input::utils::register_input_subscriber(),
79        )
80    }
81
82    fn start(&mut self) {
83        macroquad::input::utils::repeat_all_miniquad_input(self, self.1);
84        self.0.start();
85    }
86
87    fn finish(&mut self) {
88        self.0.finish();
89    }
90
91    fn ui<F>(&mut self, f: F)
92    where
93        F: FnOnce(&mut yakui_core::Yakui),
94    {
95        macroquad::input::utils::repeat_all_miniquad_input(self, self.1);
96
97        self.0.run(f);
98    }
99
100    fn draw(&mut self) {
101        let mut gl = unsafe { get_internal_gl() };
102        // Ensure that macroquad's shapes are not going to be lost, and draw them now
103        gl.flush();
104        self.0.draw(gl.quad_context);
105    }
106}
107
108/// Returns true if the last mouse or keyboard event was sunk by yakui, and should not be handled by your game.
109pub fn has_input_focus() -> bool {
110    get_yakui().as_ref().unwrap().0.has_input_focus()
111}
112
113/// Returns true if the last keyboard event was sunk by yakui, and should not be handled by your game.
114pub fn has_keyboard_focus() -> bool {
115    get_yakui().as_ref().unwrap().0.has_keyboard_focus()
116}
117
118/// Returns true if the last mouse event was sunk by yakui, and should not be handled by your game.
119pub fn has_mouse_focus() -> bool {
120    get_yakui().as_ref().unwrap().0.has_mouse_focus()
121}
122
123/// Binds the yakui context to the current thread.
124pub fn start() {
125    get_yakui().as_mut().unwrap().start();
126}
127
128/// Finishes the current yakui context and prepares it for rendering.
129pub fn finish() {
130    get_yakui().as_mut().unwrap().finish();
131}
132
133/// Allows you to submit commands to the yakui context inside the scope of the closure passed, calls [`start`] and [`finish`] for you.
134pub fn ui<F: FnOnce(&mut yakui_core::Yakui)>(f: F) {
135    get_yakui().as_mut().unwrap().ui(|ctx| f(ctx))
136}
137
138/// Allows you configure the yakui context within the scope of the closure passed, if you need to.
139pub fn cfg<F: FnOnce(&mut yakui_core::Yakui)>(f: F) {
140    f(get_yakui().as_mut().unwrap().0.ctx());
141}
142
143/// Draws the yakui ui. Must be called after `finish`/`ui` and once per frame.
144pub fn draw() {
145    get_yakui().as_mut().unwrap().draw()
146}
147
148impl mq::EventHandler for Yakui {
149    fn update(&mut self) {}
150
151    fn draw(&mut self) {}
152
153    fn mouse_motion_event(&mut self, x: f32, y: f32) {
154        self.0.mouse_motion_event(x, y);
155    }
156
157    fn mouse_wheel_event(&mut self, dx: f32, dy: f32) {
158        self.0.mouse_wheel_event(dx, dy);
159    }
160
161    fn mouse_button_down_event(&mut self, mb: mq::MouseButton, x: f32, y: f32) {
162        self.0.mouse_button_down_event(mb, x, y);
163    }
164
165    fn mouse_button_up_event(&mut self, mb: mq::MouseButton, x: f32, y: f32) {
166        self.0.mouse_button_up_event(mb, x, y);
167    }
168
169    fn char_event(&mut self, character: char, keymods: mq::KeyMods, repeat: bool) {
170        self.0.char_event(character, keymods, repeat);
171    }
172
173    fn key_down_event(&mut self, keycode: mq::KeyCode, keymods: mq::KeyMods, repeat: bool) {
174        self.0.key_down_event(keycode, keymods, repeat);
175    }
176
177    fn key_up_event(&mut self, keycode: mq::KeyCode, keymods: mq::KeyMods) {
178        self.0.key_up_event(keycode, keymods);
179    }
180}