egui_tetra/
lib.rs

1//! # egui-tetra
2//!
3//! egui-tetra is a library that helps integrate [egui](https://crates.io/crates/egui),
4//! an immediate mode GUI library, with [Tetra](https://crates.io/crates/tetra),
5//! a 2D game framework.
6//!
7//! ## Usage
8//!
9//! The easiest way to use egui-tetra is to make your main state struct implement
10//! egui-tetra's [`State`] trait instead of [Tetra's](tetra::State). This will
11//! give you access to a [`ui`](State::ui) callback where you can do your GUI
12//! rendering.
13//!
14//! ```
15//! use std::error::Error;
16//! use egui_tetra::egui;
17//!
18//! struct MainState;
19//!
20//! impl egui_tetra::State<Box<dyn Error>> for MainState {
21//! 	fn ui(
22//! 		&mut self,
23//! 		ctx: &mut tetra::Context,
24//! 		egui_ctx: &egui::CtxRef,
25//! 	) -> Result<(), Box<dyn Error>> {
26//! 		egui::Window::new("hi!").show(egui_ctx, |ui| {
27//! 			ui.label("Hello world!");
28//! 		});
29//! 		Ok(())
30//! 	}
31//!
32//! 	fn update(
33//! 		&mut self,
34//! 		ctx: &mut tetra::Context,
35//! 		egui_ctx: &egui::CtxRef,
36//! 	) -> Result<(), Box<dyn Error>> {
37//!         /// Your update code here
38//! 		Ok(())
39//! 	}
40//!
41//! 	fn draw(
42//! 		&mut self,
43//! 		ctx: &mut tetra::Context,
44//! 		egui_ctx: &egui::CtxRef,
45//! 	) -> Result<(), Box<dyn Error>> {
46//!         /// Your drawing code here
47//! 		Ok(())
48//! 	}
49//!
50//! 	fn event(
51//! 		&mut self,
52//! 		ctx: &mut tetra::Context,
53//! 		egui_ctx: &egui::CtxRef,
54//! 		event: tetra::Event,
55//! 	) -> Result<(), Box<dyn Error>> {
56//!         /// Your event handling code here
57//! 		Ok(())
58//! 	}
59//! }
60//! ```
61//!
62//! When running the Tetra [`Context`](tetra::Context::run), wrap your state
63//! struct in a [`StateWrapper`] to make it compatible with Tetra's
64//! [`State` trait](tetra::State).
65//!
66//! ```no_run
67//! # use std::error::Error;
68//! # use egui_tetra::egui;
69//! #
70//! # struct MainState;
71//! #
72//! # impl egui_tetra::State<Box<dyn Error>> for MainState {
73//! # 	fn ui(
74//! # 		&mut self,
75//! # 		ctx: &mut tetra::Context,
76//! # 		egui_ctx: &egui::CtxRef,
77//! # 	) -> Result<(), Box<dyn Error>> {
78//! # 		egui::Window::new("hi!").show(egui_ctx, |ui| {
79//! # 			ui.label("Hello world!");
80//! # 		});
81//! # 		Ok(())
82//! # 	}
83//! #
84//! # 	fn update(
85//! # 		&mut self,
86//! # 		ctx: &mut tetra::Context,
87//! # 		egui_ctx: &egui::CtxRef,
88//! # 	) -> Result<(), Box<dyn Error>> {
89//! # 		Ok(())
90//! # 	}
91//! #
92//! # 	fn draw(
93//! # 		&mut self,
94//! # 		ctx: &mut tetra::Context,
95//! # 		egui_ctx: &egui::CtxRef,
96//! # 	) -> Result<(), Box<dyn Error>> {
97//! # 		Ok(())
98//! # 	}
99//! #
100//! # 	fn event(
101//! # 		&mut self,
102//! # 		ctx: &mut tetra::Context,
103//! # 		egui_ctx: &egui::CtxRef,
104//! # 		event: tetra::Event,
105//! # 	) -> Result<(), Box<dyn Error>> {
106//! # 		Ok(())
107//! # 	}
108//! # }
109//! #
110//! fn main() -> Result<(), Box<dyn Error>> {
111//! 	tetra::ContextBuilder::new("example", 800, 600)
112//! 		.build()?
113//! 		.run(|_| Ok(egui_tetra::StateWrapper::new(MainState)))
114//! }
115//! ```
116//!
117//! If you need more control, you can use [`EguiWrapper`] and manually
118//! hook up egui to Tetra's callbacks.
119
120#![warn(missing_docs)]
121#![allow(clippy::tabs_in_doc_comments)]
122
123pub use egui;
124
125use std::{fmt::Display, sync::Arc, time::Instant};
126
127use copypasta::{ClipboardContext, ClipboardProvider};
128use egui::{ClippedMesh, CtxRef, RawInput};
129use tetra::{
130	graphics::{self, BlendAlphaMode, BlendMode},
131	Event, TetraError,
132};
133
134const SCROLL_SENSITIVITY: f32 = 48.0;
135const ZOOM_SENSITIVITY: f32 = 1.25;
136
137fn tetra_vec2_to_egui_pos2(tetra_vec2: tetra::math::Vec2<f32>) -> egui::Pos2 {
138	egui::pos2(tetra_vec2.x, tetra_vec2.y)
139}
140
141fn egui_pos2_to_tetra_vec2(egui_pos2: egui::Pos2) -> tetra::math::Vec2<f32> {
142	tetra::math::Vec2::new(egui_pos2.x, egui_pos2.y)
143}
144
145fn egui_rect_to_tetra_rectangle(egui_rect: egui::Rect) -> tetra::graphics::Rectangle<i32> {
146	tetra::graphics::Rectangle::new(
147		egui_rect.left() as i32,
148		egui_rect.top() as i32,
149		egui_rect.width() as i32,
150		egui_rect.height() as i32,
151	)
152}
153
154fn egui_color32_to_tetra_color(egui_color: egui::Color32) -> tetra::graphics::Color {
155	tetra::graphics::Color::rgba8(
156		egui_color.r(),
157		egui_color.g(),
158		egui_color.b(),
159		egui_color.a(),
160	)
161}
162
163/// Converts a [tetra key](tetra::input::Key) to an
164/// [egui key](egui::Key) if there's an egui equivalent
165/// to the tetra key, otherwise returns `None`.
166///
167/// egui doesn't care about every keyboard key, so its listing of keys
168/// is less comprehensive than tetra's.
169fn tetra_key_to_egui_key(key: tetra::input::Key) -> Option<egui::Key> {
170	match key {
171		tetra::input::Key::A => Some(egui::Key::A),
172		tetra::input::Key::B => Some(egui::Key::B),
173		tetra::input::Key::C => Some(egui::Key::C),
174		tetra::input::Key::D => Some(egui::Key::D),
175		tetra::input::Key::E => Some(egui::Key::E),
176		tetra::input::Key::F => Some(egui::Key::F),
177		tetra::input::Key::G => Some(egui::Key::G),
178		tetra::input::Key::H => Some(egui::Key::H),
179		tetra::input::Key::I => Some(egui::Key::I),
180		tetra::input::Key::J => Some(egui::Key::J),
181		tetra::input::Key::K => Some(egui::Key::K),
182		tetra::input::Key::L => Some(egui::Key::L),
183		tetra::input::Key::M => Some(egui::Key::M),
184		tetra::input::Key::N => Some(egui::Key::N),
185		tetra::input::Key::O => Some(egui::Key::O),
186		tetra::input::Key::P => Some(egui::Key::P),
187		tetra::input::Key::Q => Some(egui::Key::Q),
188		tetra::input::Key::R => Some(egui::Key::R),
189		tetra::input::Key::S => Some(egui::Key::S),
190		tetra::input::Key::T => Some(egui::Key::T),
191		tetra::input::Key::U => Some(egui::Key::U),
192		tetra::input::Key::V => Some(egui::Key::V),
193		tetra::input::Key::W => Some(egui::Key::W),
194		tetra::input::Key::X => Some(egui::Key::X),
195		tetra::input::Key::Y => Some(egui::Key::Y),
196		tetra::input::Key::Z => Some(egui::Key::Z),
197		tetra::input::Key::Num0 => Some(egui::Key::Num0),
198		tetra::input::Key::Num1 => Some(egui::Key::Num1),
199		tetra::input::Key::Num2 => Some(egui::Key::Num2),
200		tetra::input::Key::Num3 => Some(egui::Key::Num3),
201		tetra::input::Key::Num4 => Some(egui::Key::Num4),
202		tetra::input::Key::Num5 => Some(egui::Key::Num5),
203		tetra::input::Key::Num6 => Some(egui::Key::Num6),
204		tetra::input::Key::Num7 => Some(egui::Key::Num7),
205		tetra::input::Key::Num8 => Some(egui::Key::Num8),
206		tetra::input::Key::Num9 => Some(egui::Key::Num9),
207		tetra::input::Key::NumPad0 => Some(egui::Key::Num0),
208		tetra::input::Key::NumPad1 => Some(egui::Key::Num1),
209		tetra::input::Key::NumPad2 => Some(egui::Key::Num2),
210		tetra::input::Key::NumPad3 => Some(egui::Key::Num3),
211		tetra::input::Key::NumPad4 => Some(egui::Key::Num4),
212		tetra::input::Key::NumPad5 => Some(egui::Key::Num5),
213		tetra::input::Key::NumPad6 => Some(egui::Key::Num6),
214		tetra::input::Key::NumPad7 => Some(egui::Key::Num7),
215		tetra::input::Key::NumPad8 => Some(egui::Key::Num8),
216		tetra::input::Key::NumPad9 => Some(egui::Key::Num9),
217		tetra::input::Key::NumPadEnter => Some(egui::Key::Enter),
218		tetra::input::Key::Up => Some(egui::Key::ArrowUp),
219		tetra::input::Key::Down => Some(egui::Key::ArrowDown),
220		tetra::input::Key::Left => Some(egui::Key::ArrowLeft),
221		tetra::input::Key::Right => Some(egui::Key::ArrowRight),
222		tetra::input::Key::Backspace => Some(egui::Key::Backspace),
223		tetra::input::Key::Delete => Some(egui::Key::Delete),
224		tetra::input::Key::End => Some(egui::Key::End),
225		tetra::input::Key::Enter => Some(egui::Key::Enter),
226		tetra::input::Key::Escape => Some(egui::Key::Escape),
227		tetra::input::Key::Home => Some(egui::Key::Home),
228		tetra::input::Key::Insert => Some(egui::Key::Insert),
229		tetra::input::Key::PageDown => Some(egui::Key::PageDown),
230		tetra::input::Key::PageUp => Some(egui::Key::PageUp),
231		tetra::input::Key::Space => Some(egui::Key::Space),
232		tetra::input::Key::Tab => Some(egui::Key::Tab),
233		_ => None,
234	}
235}
236
237/// Converts a [tetra mouse button](tetra::input::MouseButton) to an
238/// [egui mouse button](egui::PointerButton) if there's an egui equivalent
239/// to the tetra mouse button, otherwise returns `None`.
240///
241/// egui only supports left, middle, and right buttons.
242fn tetra_mouse_button_to_egui_pointer_button(
243	tetra_mouse_button: tetra::input::MouseButton,
244) -> Option<egui::PointerButton> {
245	match tetra_mouse_button {
246		tetra::input::MouseButton::Left => Some(egui::PointerButton::Primary),
247		tetra::input::MouseButton::Middle => Some(egui::PointerButton::Middle),
248		tetra::input::MouseButton::Right => Some(egui::PointerButton::Secondary),
249		_ => None,
250	}
251}
252
253fn egui_mesh_to_tetra_mesh(
254	ctx: &mut tetra::Context,
255	egui_mesh: egui::epaint::Mesh,
256	texture: tetra::graphics::Texture,
257) -> tetra::Result<tetra::graphics::mesh::Mesh> {
258	let index_buffer = tetra::graphics::mesh::IndexBuffer::new(ctx, &egui_mesh.indices)?;
259	let vertices: Vec<tetra::graphics::mesh::Vertex> = egui_mesh
260		.vertices
261		.iter()
262		.map(|vertex| {
263			tetra::graphics::mesh::Vertex::new(
264				egui_pos2_to_tetra_vec2(vertex.pos),
265				egui_pos2_to_tetra_vec2(vertex.uv),
266				egui_color32_to_tetra_color(vertex.color),
267			)
268		})
269		.collect();
270	let vertex_buffer = tetra::graphics::mesh::VertexBuffer::new(ctx, &vertices)?;
271	let mut mesh = tetra::graphics::mesh::Mesh::indexed(vertex_buffer, index_buffer);
272	mesh.set_texture(texture);
273	mesh.set_backface_culling(false);
274	Ok(mesh)
275}
276
277/// Converts an [egui font texture](egui::Texture) to a
278/// [tetra texture](tetra::graphics::Texture).
279fn egui_font_image_to_tetra_texture(
280	ctx: &mut tetra::Context,
281	egui_font_image: Arc<egui::FontImage>,
282) -> tetra::Result<tetra::graphics::Texture> {
283	let mut pixels = vec![];
284	// each u8 of the egui texture is the alpha channel.
285	// the other components are always white. since egui
286	// uses premultiplied alpha, we set every component in the
287	// tetra texture to the alpha.
288	for alpha in &egui_font_image.pixels {
289		pixels.push(*alpha);
290		pixels.push(*alpha);
291		pixels.push(*alpha);
292		pixels.push(*alpha);
293	}
294	tetra::graphics::Texture::from_rgba(
295		ctx,
296		egui_font_image.width as i32,
297		egui_font_image.height as i32,
298		&pixels,
299	)
300}
301
302/// An error that can occur when using egui-tetra.
303#[derive(Debug)]
304pub enum Error {
305	/// A Tetra error occurred.
306	TetraError(TetraError),
307	/// An error occurred when opening a URL or other path
308	/// by clicking a hyperlink.
309	OpenError(std::io::Error),
310	/// An error occurred when accessing the system's clipboard.
311	ClipboardError(Box<dyn std::error::Error + Send + Sync>),
312}
313
314impl Display for Error {
315	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
316		match self {
317			Error::TetraError(error) => error.fmt(f),
318			Error::OpenError(error) => error.fmt(f),
319			Error::ClipboardError(error) => error.fmt(f),
320		}
321	}
322}
323
324impl std::error::Error for Error {
325	fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
326		match self {
327			Error::TetraError(error) => Some(error),
328			Error::OpenError(error) => Some(error),
329			Error::ClipboardError(error) => Some(error.as_ref()),
330		}
331	}
332}
333
334impl From<TetraError> for Error {
335	fn from(error: TetraError) -> Self {
336		Self::TetraError(error)
337	}
338}
339
340impl From<std::io::Error> for Error {
341	fn from(v: std::io::Error) -> Self {
342		Self::OpenError(v)
343	}
344}
345
346impl From<Box<dyn std::error::Error + Send + Sync>> for Error {
347	fn from(error: Box<dyn std::error::Error + Send + Sync>) -> Self {
348		Self::ClipboardError(error)
349	}
350}
351
352/// Wraps an egui context with features that are useful
353/// for integrating egui with Tetra.
354pub struct EguiWrapper {
355	raw_input: RawInput,
356	ctx: CtxRef,
357	texture: Option<tetra::graphics::Texture>,
358	last_frame_time: Instant,
359	meshes: Vec<(tetra::graphics::Rectangle<i32>, tetra::graphics::mesh::Mesh)>,
360}
361
362impl EguiWrapper {
363	/// Creates a new [`EguiWrapper`] and underlying egui context.
364	pub fn new() -> Self {
365		Self {
366			raw_input: RawInput::default(),
367			ctx: CtxRef::default(),
368			texture: None,
369			last_frame_time: Instant::now(),
370			meshes: vec![],
371		}
372	}
373
374	/// Returns a reference to the underlying egui context.
375	pub fn ctx(&self) -> &egui::CtxRef {
376		&self.ctx
377	}
378
379	/// Dispaches a Tetra [`Event`](tetra::Event) to the egui context.
380	pub fn event(&mut self, ctx: &tetra::Context, event: &tetra::Event) -> Result<(), Error> {
381		match event {
382			tetra::Event::KeyPressed { key } => {
383				// update modifiers
384				match key {
385					tetra::input::Key::LeftCtrl | tetra::input::Key::RightCtrl => {
386						self.raw_input.modifiers.ctrl = true;
387						self.raw_input.modifiers.command = true;
388					}
389					tetra::input::Key::LeftShift | tetra::input::Key::RightShift => {
390						self.raw_input.modifiers.shift = true;
391					}
392					tetra::input::Key::LeftAlt | tetra::input::Key::RightAlt => {
393						self.raw_input.modifiers.alt = true;
394					}
395					_ => {}
396				}
397
398				// copy/cut/paste
399				if tetra::input::is_key_down(ctx, tetra::input::Key::LeftCtrl)
400					| tetra::input::is_key_down(ctx, tetra::input::Key::RightCtrl)
401				{
402					if let tetra::input::Key::C = key {
403						self.raw_input.events.push(egui::Event::Copy);
404					}
405					if let tetra::input::Key::X = key {
406						self.raw_input.events.push(egui::Event::Cut);
407					}
408					if let tetra::input::Key::V = key {
409						self.raw_input
410							.events
411							.push(egui::Event::Text(ClipboardContext::new()?.get_contents()?));
412					}
413				}
414
415				if let Some(key) = tetra_key_to_egui_key(*key) {
416					self.raw_input.events.push(egui::Event::Key {
417						key,
418						pressed: true,
419						modifiers: self.raw_input.modifiers,
420					});
421				}
422			}
423			tetra::Event::KeyReleased { key } => {
424				match key {
425					tetra::input::Key::LeftCtrl | tetra::input::Key::RightCtrl => {
426						self.raw_input.modifiers.ctrl = false;
427						self.raw_input.modifiers.command = false;
428					}
429					tetra::input::Key::LeftShift | tetra::input::Key::RightShift => {
430						self.raw_input.modifiers.shift = false;
431					}
432					tetra::input::Key::LeftAlt | tetra::input::Key::RightAlt => {
433						self.raw_input.modifiers.alt = false;
434					}
435					_ => {}
436				}
437				if let Some(key) = tetra_key_to_egui_key(*key) {
438					self.raw_input.events.push(egui::Event::Key {
439						key,
440						pressed: false,
441						modifiers: self.raw_input.modifiers,
442					});
443				}
444			}
445			tetra::Event::MouseButtonPressed { button } => {
446				if let Some(button) = tetra_mouse_button_to_egui_pointer_button(*button) {
447					self.raw_input.events.push(egui::Event::PointerButton {
448						pos: tetra_vec2_to_egui_pos2(tetra::input::get_mouse_position(ctx)),
449						button,
450						pressed: true,
451						modifiers: self.raw_input.modifiers,
452					});
453				}
454			}
455			tetra::Event::MouseButtonReleased { button } => {
456				if let Some(button) = tetra_mouse_button_to_egui_pointer_button(*button) {
457					self.raw_input.events.push(egui::Event::PointerButton {
458						pos: tetra_vec2_to_egui_pos2(tetra::input::get_mouse_position(ctx)),
459						button,
460						pressed: false,
461						modifiers: self.raw_input.modifiers,
462					});
463				}
464			}
465			tetra::Event::MouseMoved { position, .. } => {
466				self.raw_input
467					.events
468					.push(egui::Event::PointerMoved(tetra_vec2_to_egui_pos2(
469						*position,
470					)));
471			}
472			tetra::Event::MouseWheelMoved { amount } => {
473				if tetra::input::is_key_down(ctx, tetra::input::Key::LeftCtrl)
474					|| tetra::input::is_key_down(ctx, tetra::input::Key::RightCtrl)
475				{
476					self.raw_input
477						.events
478						.push(egui::Event::Zoom(ZOOM_SENSITIVITY.powi(amount.y)));
479				} else {
480					self.raw_input.events.push(egui::Event::Scroll(
481						egui::vec2(amount.x as f32, amount.y as f32) * SCROLL_SENSITIVITY,
482					));
483				}
484			}
485			tetra::Event::TextInput { text } => {
486				self.raw_input.events.push(egui::Event::Text(text.clone()));
487			}
488			_ => {}
489		}
490		Ok(())
491	}
492
493	/// Begins a new GUI frame.
494	pub fn begin_frame(&mut self, ctx: &mut tetra::Context) -> Result<(), Error> {
495		let now = Instant::now();
496		self.raw_input.screen_rect = Some(egui::Rect {
497			min: egui::pos2(0.0, 0.0),
498			max: egui::pos2(
499				tetra::window::get_width(ctx) as f32,
500				tetra::window::get_height(ctx) as f32,
501			),
502		});
503		self.raw_input.predicted_dt = (now - self.last_frame_time).as_secs_f32();
504		self.last_frame_time = now;
505		self.meshes.clear();
506		self.ctx.begin_frame(self.raw_input.take());
507		if self.texture.is_none() {
508			self.texture = Some(egui_font_image_to_tetra_texture(
509				ctx,
510				self.ctx.font_image(),
511			)?);
512		}
513		Ok(())
514	}
515
516	/// Ends a GUI frame.
517	pub fn end_frame(&mut self, ctx: &mut tetra::Context) -> Result<(), Error> {
518		let (output, shapes) = self.ctx.end_frame();
519		if let Some(texture) = &self.texture {
520			let clipped_meshes = self.ctx.tessellate(shapes);
521			for ClippedMesh(rect, mesh) in clipped_meshes {
522				let rect = egui_rect_to_tetra_rectangle(rect);
523				let mesh = egui_mesh_to_tetra_mesh(ctx, mesh, texture.clone())?;
524				self.meshes.push((rect, mesh));
525			}
526		}
527
528		// open URLs that were clicked
529		if let Some(open_url) = &output.open_url {
530			open::that(&open_url.url)?;
531		}
532
533		// copy text to clipboard
534		if !output.copied_text.is_empty() {
535			ClipboardContext::new()?.set_contents(output.copied_text)?;
536		}
537
538		Ok(())
539	}
540
541	/// Draws the latest finished GUI frame to the screen.
542	///
543	/// Note that this function changes the Tetra blend mode and
544	/// scissor state.
545	pub fn draw_frame(&mut self, ctx: &mut tetra::Context) {
546		graphics::set_blend_mode(ctx, BlendMode::Alpha(BlendAlphaMode::Premultiplied));
547		for (rect, mesh) in &self.meshes {
548			graphics::set_scissor(ctx, *rect);
549			mesh.draw(ctx, tetra::math::Vec2::zero());
550		}
551		graphics::reset_scissor(ctx);
552		graphics::reset_blend_mode(ctx);
553	}
554}
555
556impl Default for EguiWrapper {
557	fn default() -> Self {
558		Self::new()
559	}
560}
561
562/// A trait analogous to [`tetra::State`], but with the addition of a
563/// [`ui`](State::ui) callback and an `egui_ctx` argument in the
564/// other callbacks.
565///
566/// You can use a type implementing this trait as your main game
567/// state by wrapping it with a [`StateWrapper`] and passing the wrapper
568/// to [`tetra::Context::run`].
569#[allow(unused_variables)]
570pub trait State<E: From<Error> = Error> {
571	/// Called when it is time for the game to construct a GUI.
572	fn ui(&mut self, ctx: &mut tetra::Context, egui_ctx: &egui::CtxRef) -> Result<(), E> {
573		Ok(())
574	}
575
576	/// Called when it is time for the game to update.
577	fn update(&mut self, ctx: &mut tetra::Context, egui_ctx: &egui::CtxRef) -> Result<(), E> {
578		Ok(())
579	}
580
581	/// Called when it is time for the game to be drawn.
582	fn draw(&mut self, ctx: &mut tetra::Context, egui_ctx: &egui::CtxRef) -> Result<(), E> {
583		Ok(())
584	}
585
586	/// Called when a window or input event occurs.
587	///
588	/// Mouse and keyboard input events will not be received if the GUI
589	/// is using the mouse or keyboard, respectively.
590	fn event(
591		&mut self,
592		ctx: &mut tetra::Context,
593		egui_ctx: &egui::CtxRef,
594		event: Event,
595	) -> Result<(), E> {
596		Ok(())
597	}
598}
599
600/// An adaptor that implements [`tetra::State`] for implementors of
601/// [`State`].
602pub struct StateWrapper<E: From<Error>> {
603	events: Vec<tetra::Event>,
604	state: Box<dyn State<E>>,
605	egui: EguiWrapper,
606}
607
608impl<E: From<Error>> StateWrapper<E> {
609	/// Wraps an implementor of [`State`] so it implements [`tetra::State`].
610	pub fn new(state: impl State<E> + 'static) -> Self {
611		Self {
612			events: vec![],
613			state: Box::new(state),
614			egui: EguiWrapper::new(),
615		}
616	}
617
618	/// Returns a reference to this wrapper's egui context.
619	pub fn ctx(&self) -> &egui::CtxRef {
620		self.egui.ctx()
621	}
622}
623
624/*
625A note about the order of events:
626
627Tetra's game loop is:
628- Poll and dispatch events
629- Run the update callback (may not happen every frame depending
630on the timestamp setting)
631- Run the draw callback
632
633We don't want to dispatch mouse and keyboard events to the
634gameplay code if egui wants them, but egui can't tell us if
635it wants them until after we call end_frame(). So we need
636to run the UI code at the beginning of the game loop (but
637not draw the result until the end of the game loop, of course).
638
639Here's the order of operations I settled on:
640- Whenever event is called, send it to the egui ctx and queue
641it up for later
642- At the beginning of update, run the UI callback and save the
643resulting meshes and scissor rectangles. Then, dispatch queued
644events to the gameplay code (unless the UI wanted them).
645- In the draw callback, draw gameplay first, then UI
646*/
647
648impl<E: From<Error>> tetra::State<E> for StateWrapper<E> {
649	fn update(&mut self, ctx: &mut tetra::Context) -> Result<(), E> {
650		self.egui.begin_frame(ctx)?;
651		self.state.ui(ctx, self.egui.ctx())?;
652		self.egui.end_frame(ctx)?;
653
654		for event in self.events.drain(..) {
655			match &event {
656				Event::KeyPressed { .. } | Event::KeyReleased { .. } => {
657					if self.egui.ctx().wants_keyboard_input() {
658						continue;
659					}
660				}
661				Event::MouseButtonPressed { .. } | Event::MouseButtonReleased { .. } => {
662					if self.egui.ctx().is_using_pointer() {
663						continue;
664					}
665				}
666				Event::MouseMoved { .. } => {
667					if self.egui.ctx().is_using_pointer() {
668						continue;
669					}
670				}
671				Event::MouseWheelMoved { .. } => {
672					if self.egui.ctx().is_using_pointer() {
673						continue;
674					}
675				}
676				_ => {}
677			}
678			self.state.event(ctx, self.egui.ctx(), event)?;
679		}
680
681		self.state.update(ctx, self.egui.ctx())
682	}
683
684	fn draw(&mut self, ctx: &mut tetra::Context) -> Result<(), E> {
685		self.state.draw(ctx, self.egui.ctx())?;
686		self.egui.draw_frame(ctx);
687		Ok(())
688	}
689
690	fn event(&mut self, ctx: &mut tetra::Context, event: Event) -> Result<(), E> {
691		self.egui.event(ctx, &event)?;
692		self.events.push(event);
693		Ok(())
694	}
695}