diff --git a/src/input.rs b/src/input.rs index 223ba91..5a4b92d 100644 --- a/src/input.rs +++ b/src/input.rs @@ -10,6 +10,8 @@ use raylib::ffi::{KeyboardKey, MouseButton}; use raylib::prelude::Rectangle as rayRect; use raylib::RaylibHandle; +use crate::util::ConvertRE; + /// Struct to store values pub struct InputOptions { /// 'Point' to _native pixel_ conversion ratio. 'Points' are `egui`'s logical pixels. @@ -41,16 +43,114 @@ impl Default for InputOptions { } } -fn conv_rect(r: rayRect) -> egRect { - egRect { - min: Pos2::new(r.x, r.y), - max: Pos2::new(r.x + r.width, r.y + r.height), +fn get_mouse_input( + rl: &mut RaylibHandle, + events: &mut Vec, + pixels_per_point: f32, + modifiers: Modifiers, +) { + let mouse_delta = rl.get_mouse_delta().scale_by(1.0 / pixels_per_point); + let mouse_position = rl.get_mouse_position().scale_by(1.0 / pixels_per_point); + + if mouse_delta.x > 0.0 || mouse_delta.y > 0.0 { + events.push(Event::MouseMoved(Vec2::new(mouse_delta.x, mouse_delta.y))); + events.push(Event::PointerMoved(Pos2::new( + mouse_position.x, + mouse_position.y, + ))); + } + + if rl.is_mouse_button_pressed(MouseButton::MOUSE_BUTTON_LEFT) { + let pos = rl.get_mouse_position(); + let pos = Pos2::new(pos.x / pixels_per_point, pos.y / pixels_per_point); + events.push(Event::PointerButton { + pos, + button: egui::PointerButton::Primary, + pressed: true, + modifiers, + }) + } else if rl.is_mouse_button_released(MouseButton::MOUSE_BUTTON_LEFT) { + let pos = rl.get_mouse_position(); + let pos = Pos2::new(pos.x / pixels_per_point, pos.y / pixels_per_point); + events.push(Event::PointerButton { + pos, + button: egui::PointerButton::Primary, + pressed: false, + modifiers, + }) + } + + if rl.is_mouse_button_pressed(MouseButton::MOUSE_BUTTON_RIGHT) { + let pos = rl.get_mouse_position(); + let pos = Pos2::new(pos.x / pixels_per_point, pos.y / pixels_per_point); + events.push(Event::PointerButton { + pos, + button: egui::PointerButton::Secondary, + pressed: true, + modifiers, + }) + } else if rl.is_mouse_button_released(MouseButton::MOUSE_BUTTON_RIGHT) { + let pos = rl.get_mouse_position(); + let pos = Pos2::new(pos.x / pixels_per_point, pos.y / pixels_per_point); + events.push(Event::PointerButton { + pos, + button: egui::PointerButton::Secondary, + pressed: false, + modifiers, + }) + } +} + +fn get_keyboard_input( + opt: &InputOptions, + rl: &mut RaylibHandle, + events: &mut Vec, + modifiers: Modifiers, + ctx: &egui::Context, +) { + events.extend(opt.key_map.iter().filter_map(|(&kk, &key)| { + if rl.is_key_pressed(kk) { + Some(Event::Key { + key, + physical_key: None, + pressed: true, + repeat: false, + modifiers, + }) + } else if rl.is_key_released(kk) { + Some(Event::Key { + key, + physical_key: None, + pressed: false, + repeat: false, + modifiers, + }) + } else { + None + } + })); + + // Egui actually wants Text input right now. + if ctx.wants_keyboard_input() { + let mut buf = String::new(); + // So give them that. Raylib queues characters anyways. + loop { + let r = rl.get_char_pressed(); + if let Some(ch) = r { + buf.push(ch); + } else { + break; + } + } + if !buf.is_empty() { + events.push(Event::Text(buf)); + } } } /// Using the provided input options, gather all required input for egui. -/// `last_key` is simply an option to track the key pressed in previous frame, so that it's release event may be pushed.. -pub fn gather_input(opt: &InputOptions, last_key: &mut HashSet<, ctx: &egui::Context, rl: &mut RaylibHandle) -> RawInput { +/// `last_key` is simply an option to track the key pressed in previous frame, so that it's release event may be pushed.. +pub fn gather_input(opt: &InputOptions, ctx: &egui::Context, rl: &mut RaylibHandle) -> RawInput { let monitor_id = raylib::window::get_current_monitor(); let (mw, mh) = ( raylib::window::get_monitor_width(monitor_id), @@ -81,7 +181,7 @@ pub fn gather_input(opt: &InputOptions, last_key: &mut HashSet<, ctx: &egui::Con focused: Some(rl.is_window_focused()), }; - let screen_rect = opt.region.map(conv_rect).or(window_size); + let screen_rect = opt.region.map(|r| r.convert()).or(window_size); let modifiers = Modifiers { alt: rl.is_key_down(KeyboardKey::KEY_LEFT_ALT) @@ -94,56 +194,9 @@ pub fn gather_input(opt: &InputOptions, last_key: &mut HashSet<, ctx: &egui::Con command: false, }; - let mut events: Vec<_> = opt - .key_map - .iter() - .filter_map(|(&kk, &key)| { - if rl.is_key_pressed(kk) { - Some(Event::Key { - key, - physical_key: None, - pressed: true, - repeat: false, - modifiers, - }) - } else if rl.is_key_released(kk) { - Some(Event::Key { - key, - physical_key: None, - pressed: false, - repeat: false, - modifiers, - }) - } else { - None - } - }) - .collect(); + let mut events: Vec<_> = Vec::new(); - if let Some(key) = last_key.take() { - events.push(Event::Key { - key, - physical_key: None, - pressed: false, - repeat: false, - modifiers, - }); - } - - if let Some(key) = rl.get_char_pressed().and_then(|ch| { - let mut s = String::new(); - s.push(ch); - Key::from_name(&s) - }) { - last_key.replace(key); - events.push(Event::Key { - key, - physical_key: None, - pressed: true, - repeat: false, - modifiers, - }); - } + get_keyboard_input(opt, rl, &mut events, modifiers, ctx); if rl.is_key_pressed(KeyboardKey::KEY_C) && modifiers.ctrl { events.push(Event::Copy) @@ -154,55 +207,9 @@ pub fn gather_input(opt: &InputOptions, last_key: &mut HashSet<, ctx: &egui::Con } } - let mouse_delta = rl.get_mouse_delta().scale_by(1.0 / pixels_per_point); - let mouse_position = rl.get_mouse_position().scale_by(1.0 / pixels_per_point); - if mouse_delta.x > 0.0 || mouse_delta.y > 0.0 { - events.push(Event::MouseMoved(Vec2::new(mouse_delta.x, mouse_delta.y))); - events.push(Event::PointerMoved(Pos2::new( - mouse_position.x, - mouse_position.y, - ))); - } - - if rl.is_mouse_button_pressed(MouseButton::MOUSE_BUTTON_LEFT) { - let pos = rl.get_mouse_position(); - let pos = Pos2::new(pos.x / pixels_per_point, pos.y / pixels_per_point); - events.push(Event::PointerButton { - pos, - button: egui::PointerButton::Primary, - pressed: true, - modifiers, - }) - } else if rl.is_mouse_button_released(MouseButton::MOUSE_BUTTON_LEFT) { - let pos = rl.get_mouse_position(); - let pos = Pos2::new(pos.x / pixels_per_point, pos.y / pixels_per_point); - events.push(Event::PointerButton { - pos, - button: egui::PointerButton::Primary, - pressed: false, - modifiers, - }) - } - - if rl.is_mouse_button_pressed(MouseButton::MOUSE_BUTTON_RIGHT) { - let pos = rl.get_mouse_position(); - let pos = Pos2::new(pos.x / pixels_per_point, pos.y / pixels_per_point); - events.push(Event::PointerButton { - pos, - button: egui::PointerButton::Secondary, - pressed: true, - modifiers, - }) - } else if rl.is_mouse_button_released(MouseButton::MOUSE_BUTTON_RIGHT) { - let pos = rl.get_mouse_position(); - let pos = Pos2::new(pos.x / pixels_per_point, pos.y / pixels_per_point); - events.push(Event::PointerButton { - pos, - button: egui::PointerButton::Secondary, - pressed: false, - modifiers, - }) - } + // if ctx.wants_pointer_input() { + get_mouse_input(rl, &mut events, pixels_per_point, modifiers); + // } let dropped_files = if rl.is_file_dropped() { rl.load_dropped_files() @@ -228,7 +235,9 @@ pub fn gather_input(opt: &InputOptions, last_key: &mut HashSet<, ctx: &egui::Con Vec::new() }; - if !events.is_empty() { println!("Events: {events:?}"); } + if !events.is_empty() { + println!("Events: {events:?}"); + } RawInput { viewport_id: ViewportId::ROOT, diff --git a/src/lib.rs b/src/lib.rs index 44bc2fe..66584a2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -45,6 +45,7 @@ use raylib::{ pub mod input; pub mod paint; +pub mod util; #[cfg(test)] mod tests; @@ -65,7 +66,6 @@ pub struct RlEgui { inopt: InputOptions, prs: Option, painter: paint::Painter, - last_key: Option } impl RlEgui { @@ -76,7 +76,6 @@ impl RlEgui { inopt, prs: None, painter: Painter::default(), - last_key: None } } @@ -100,7 +99,7 @@ impl RlEgui { F: FnOnce(&egui::Context), H: PlatformHandler, { - let raw_input = gather_input(&self.inopt, &mut self.last_key, &self.ctx, rl); + let raw_input = gather_input(&self.inopt, &self.ctx, rl); let output = paint::full_output(rl, raw_input, &self.ctx, run_ui, handler); let prepared = self.painter.predraw(output, rl, rthread); self.prs.replace(prepared); diff --git a/src/paint.rs b/src/paint.rs index d470969..516c73f 100644 --- a/src/paint.rs +++ b/src/paint.rs @@ -6,7 +6,7 @@ use egui::{ ahash::HashMap, epaint::ImageDelta, output::OutputEvent, Context, FullOutput, OpenUrl, RawInput, TextureId, }; -use egui::{Color32, Mesh, Pos2, Vec2}; +use egui::{Mesh, Vec2}; use raylib::color::Color; use raylib::drawing::RaylibScissorModeExt; use raylib::ffi::Rectangle; @@ -17,6 +17,8 @@ use raylib::{drawing::RaylibDraw, ffi::MouseCursor, RaylibHandle}; use raylib::texture::Image as rayImage; use raylib::texture::{RaylibTexture2D, Texture2D as rayTexture}; +use crate::util::ConvertRE; + /// Trait to handle egui's platform-specific output. pub trait PlatformHandler { /// Egui wants to open `url`. @@ -94,30 +96,10 @@ where fout } -/// Convert raw image (Uncompressed RGBA) of size `size`, stored in `rgba` into raylib [Image](raylib::texture::Image) -/// # Safety -/// Unsafe behaviour occurs if image created did not allocate enough pixels for RGBA writing. -/// However, this function uses Raylib's `gen_image_color` to allocate an image before writing. -/// Currently, Raylib's `GenImageColor` function will `calloc` for `size[0]*size[1]*4` bytes in RGBA format itself. -/// Thus, hypothetically this function is always safe. -#[allow(dead_code)] -pub fn rl_img_from_rgba(size: [usize; 2], rgba: &[u8]) -> rayImage { - let mut img = - rayImage::gen_image_color(size[0] as i32, size[1] as i32, Color::BLACK.alpha(0.0)); - img.set_format(raylib::ffi::PixelFormat::PIXELFORMAT_UNCOMPRESSED_R8G8B8A8); - let raw = img.to_raw(); - let len = (raw.width * raw.height * 4) as usize; - let rawptr = raw.data as *mut u8; - unsafe { - std::ptr::copy(rgba.as_ptr(), rawptr, len); - rayImage::from_raw(raw) - } -} - /// Create a raylib image from pixels. -/// Same as [rl_img_from_rgba], except uses slice of pixels instead of an iterator of bytes. +/// Same as [crate::utils::rl_image_from_rgba], except uses slice of pixels instead of an iterator of bytes. /// # Safety -/// Hypothetically safe, see safety condition for [rimg_from_raw]. +/// Hypothetically safe, see safety condition for `rl_image_from_rgba`. fn rimg_from_pixels(size: [usize; 2], pixels: impl Iterator) -> rayImage { let mut img = rayImage::gen_image_color(size[0] as i32, size[1] as i32, Color::BLACK.alpha(0.0)); @@ -142,23 +124,9 @@ pub(crate) struct Painter { fonttex: Option, } -fn col32_torcol(c: Color32) -> Color { - let u = c.to_srgba_unmultiplied(); - Color { - r: u[0], - g: u[1], - b: u[2], - a: u[3], - } -} - -fn pos2_to_rvec(v: &Pos2) -> Vector2 { - Vector2 { x: v.x, y: v.y } -} - fn color_mode_to_color(c: &ColorMode) -> Color { match c { - ColorMode::Solid(c) => col32_torcol(*c), + ColorMode::Solid(c) => c.convert(), ColorMode::UV(_) => { eprintln!("egui-raylib: UV color mode for paths and lines is not yet implemented! Falling back to WHITE."); Color::WHITE @@ -253,8 +221,8 @@ impl Painter { let r1 = (c.radius + c.stroke.width) * pxpp; // First draw stroke, then draw the real circle concentric to it. - d.draw_circle(center_x, center_y, r1, col32_torcol(c.stroke.color)); - d.draw_circle(center_x, center_y, r2, col32_torcol(c.fill)); + d.draw_circle(center_x, center_y, r1, c.stroke.color.convert()); + d.draw_circle(center_x, center_y, r2, c.fill.convert()); }, egui::Shape::Ellipse(es) => { // Similar to circle. @@ -264,12 +232,12 @@ impl Painter { let axes1 = es.radius + Vec2::new(es.stroke.width, es.stroke.width); let axes2 = es.radius; - d.draw_ellipse(center_x, center_y, axes1.x, axes1.y, col32_torcol(es.stroke.color)); - d.draw_ellipse(center_x, center_y, axes2.x, axes2.y, col32_torcol(es.fill)); + d.draw_ellipse(center_x, center_y, axes1.x, axes1.y, es.stroke.color.convert()); + d.draw_ellipse(center_x, center_y, axes2.x, axes2.y, es.fill.convert()); }, egui::Shape::LineSegment { points, stroke } => { - let start_pos = pos2_to_rvec(&points[0]).scale_by(pxpp); - let end_pos = pos2_to_rvec(&points[1]).scale_by(pxpp); + let start_pos = points[0].convert().scale_by(pxpp); + let end_pos = points[1].convert().scale_by(pxpp); let thick = stroke.width * pxpp; d.draw_line_ex(start_pos, end_pos, thick, color_mode_to_color(&stroke.color)) }, @@ -278,19 +246,22 @@ impl Painter { if ps.closed { let mut out = Mesh::default(); let mut p = Path::default(); - let fill = col32_torcol(ps.fill); + let fill = ps.fill.convert(); p.add_line_loop(&ps.points); p.fill(0.2, ps.fill, &mut out); for verts in out.indices.chunks_exact(3) { - let p0 = pos2_to_rvec(&out.vertices[verts[0] as usize].pos).scale_by(pxpp); - let p1 = pos2_to_rvec(&out.vertices[verts[1] as usize].pos).scale_by(pxpp); - let p2 = pos2_to_rvec(&out.vertices[verts[2] as usize].pos).scale_by(pxpp); + let p0 = out.vertices[verts[0] as usize].pos.convert().scale_by(pxpp); + let p1 = out.vertices[verts[1] as usize].pos.convert().scale_by(pxpp); + let p2 = out.vertices[verts[2] as usize].pos.convert().scale_by(pxpp); d.draw_triangle(p0, p1, p2, fill); } } else { let lines = ps.points.iter() .zip(ps.points.iter().skip(1)) - .map(|(a,b)| (pos2_to_rvec(a).scale_by(pxpp), pos2_to_rvec(b).scale_by(pxpp))); + .map(|(a,b)| + (a.convert().scale_by(pxpp), + b.convert().scale_by(pxpp)) + ); let thick = ps.stroke.width * pxpp; let color = color_mode_to_color(&ps.stroke.color); @@ -315,8 +286,8 @@ impl Painter { width: rrect.width + 2.0 * swidth, height: rrect.height + 2.0 * swidth }; - let fill_color = col32_torcol(rs.fill); - let stroke_color = col32_torcol(rs.stroke.color); + let fill_color = rs.fill.convert(); + let stroke_color = rs.stroke.color.convert(); d.draw_rectangle_rec(rrect2, stroke_color); if rs.uv == egui::Rect::ZERO { @@ -346,7 +317,7 @@ impl Painter { for row in ts.galley.rows.iter() { for g in row.glyphs.iter() { let color = ts.override_text_color.unwrap_or_else(|| ts.galley.job.sections[g.section_index as usize].format.color); - let tint = col32_torcol(color); + let tint = color.convert(); let dst_rect = Rectangle { x: origin.x + (g.pos.x + g.uv_rect.offset.x) * pxpp, y: origin.y + (g.pos.y + g.uv_rect.offset.y) * pxpp, @@ -367,22 +338,22 @@ impl Painter { }, egui::Shape::QuadraticBezier(qbez) => { let points: [Vector2; 3] = [ - pos2_to_rvec(&qbez.points[0]).scale_by(pxpp), - pos2_to_rvec(&qbez.points[1]).scale_by(pxpp), - pos2_to_rvec(&qbez.points[2]).scale_by(pxpp) + qbez.points[0].convert().scale_by(pxpp), + qbez.points[1].convert().scale_by(pxpp), + qbez.points[2].convert().scale_by(pxpp) ]; let thick = qbez.stroke.width * pxpp; - d.draw_spline_bezier_quadratic(points.as_slice(), thick, col32_torcol(qbez.fill)) + d.draw_spline_bezier_quadratic(points.as_slice(), thick, qbez.fill.convert()) }, egui::Shape::CubicBezier(cbez) => { let points: [Vector2; 4] = [ - pos2_to_rvec(&cbez.points[0]).scale_by(pxpp), - pos2_to_rvec(&cbez.points[1]).scale_by(pxpp), - pos2_to_rvec(&cbez.points[2]).scale_by(pxpp), - pos2_to_rvec(&cbez.points[3]).scale_by(pxpp) + cbez.points[0].convert().scale_by(pxpp), + cbez.points[1].convert().scale_by(pxpp), + cbez.points[2].convert().scale_by(pxpp), + cbez.points[3].convert().scale_by(pxpp) ]; let thick = cbez.stroke.width * pxpp; - d.draw_spline_bezier_cubic(points.as_slice(), thick, col32_torcol(cbez.fill)); + d.draw_spline_bezier_cubic(points.as_slice(), thick, cbez.fill.convert()); }, egui::Shape::Mesh(_) => unimplemented!("Haven't implemented drawing arbitrary meshes as there is no immediately obvious way of doing it using raylib."), egui::Shape::Callback(_) => unimplemented!("Implement support for PaintCallbacks."), diff --git a/src/tests.rs b/src/tests.rs index 73cd680..090e699 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -1,18 +1,13 @@ use egui::{Color32, Context, Label, RichText, Visuals}; use raylib::prelude::{Color, RaylibDraw}; -use crate::{ - input::{gather_input, InputOptions}, - paint::{full_output, Painter}, - DummyHandler, RlEgui, -}; - -#[derive(PartialEq)] -#[derive(Debug)] +use crate::{input::InputOptions, RlEgui}; + +#[derive(PartialEq, Debug)] enum TestEnum { First, Second, - Third + Third, } /// UI to test widgets. @@ -25,17 +20,16 @@ struct TestUi { radio: TestEnum, scalar: f32, string: String, - color: Color32, - animate_progress_bar: bool + animate_progress_bar: bool, } -// Omitted - +// Omitted - // 1. Image (for now) // 2. ColorPicker (requires Meshes) -// 3. +// 3. fn doc_link_label(a: &str, _b: &str) -> Label { - Label::new(RichText::new(a).color(Color32::BLUE)) + Label::new(RichText::new(a).color(Color32::LIGHT_BLUE)) } impl TestUi { @@ -48,8 +42,7 @@ impl TestUi { radio, scalar, string, - color, - animate_progress_bar + animate_progress_bar, } = self; ui.add(doc_link_label("Label", "label")); @@ -148,6 +141,15 @@ impl TestUi { } fn ui(&mut self, ui: &mut egui::Ui) { + ui.input(|i| { + if i.key_pressed(egui::Key::A) { + println!("A was pressed!"); + } + if i.key_released(egui::Key::A) { + println!("A was released!"); + } + }); + ui.add_enabled_ui(self.enabled, |ui| { if !self.visible { ui.set_invisible(); @@ -223,9 +225,7 @@ fn it_works() { boolean: false, scalar: 0.0, string: String::new(), - color: Color32::WHITE, animate_progress_bar: true, - }; let mut bool_flag = true; @@ -233,7 +233,7 @@ fn it_works() { let mut gui = RlEgui::new(inopt, ctx); while !rl.window_should_close() { - gui.prepare(&mut rl, &thread, |c| test_ui.run(c, &mut bool_flag)); + gui.prepare(&mut rl, &thread, |c| test_ui.run(c, &mut bool_flag)); let mut d = rl.begin_drawing(&thread); d.clear_background(Color::WHITE); @@ -241,4 +241,4 @@ fn it_works() { gui.draw(&mut d); } -} \ No newline at end of file +} diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 0000000..6b7bdfd --- /dev/null +++ b/src/util.rs @@ -0,0 +1,241 @@ +//! Some handy conversion stuff between Raylib and Egui types. + +/// A trait to convert between raylib and egui types. +pub trait ConvertRE { + /// Construct the corresponding type from this type's data. + fn convert(&self) -> T; +} + +impl ConvertRE for egui::Pos2 { + fn convert(&self) -> raylib::prelude::Vector2 { + raylib::prelude::Vector2 { + x: self.x, + y: self.y, + } + } +} + +impl ConvertRE for raylib::prelude::Vector2 { + fn convert(&self) -> egui::Pos2 { + egui::Pos2 { + x: self.x, + y: self.y, + } + } +} + +impl ConvertRE for egui::Rect { + fn convert(&self) -> raylib::math::Rectangle { + raylib::math::Rectangle { + x: self.min.x, + y: self.min.y, + width: self.width(), + height: self.height(), + } + } +} + +impl ConvertRE for raylib::math::Rectangle { + fn convert(&self) -> egui::Rect { + egui::Rect { + min: egui::Pos2 { + x: self.x, + y: self.y, + }, + max: egui::Pos2 { + x: self.x + self.width, + y: self.y + self.height, + }, + } + } +} + +impl ConvertRE for egui::Color32 { + fn convert(&self) -> raylib::prelude::Color { + let v = self.to_srgba_unmultiplied(); + raylib::prelude::Color { + r: v[0], + g: v[1], + b: v[2], + a: v[3], + } + } +} + +impl ConvertRE> for egui::CursorIcon { + fn convert(&self) -> Option { + let v = match self { + egui::CursorIcon::Default => raylib::prelude::MouseCursor::MOUSE_CURSOR_DEFAULT, + egui::CursorIcon::ContextMenu => raylib::prelude::MouseCursor::MOUSE_CURSOR_ARROW, + egui::CursorIcon::Help => raylib::prelude::MouseCursor::MOUSE_CURSOR_POINTING_HAND, + egui::CursorIcon::PointingHand => { + raylib::prelude::MouseCursor::MOUSE_CURSOR_POINTING_HAND + } + egui::CursorIcon::Crosshair => raylib::prelude::MouseCursor::MOUSE_CURSOR_CROSSHAIR, + egui::CursorIcon::Text => raylib::prelude::MouseCursor::MOUSE_CURSOR_IBEAM, + egui::CursorIcon::VerticalText => raylib::prelude::MouseCursor::MOUSE_CURSOR_IBEAM, + egui::CursorIcon::NoDrop => raylib::prelude::MouseCursor::MOUSE_CURSOR_NOT_ALLOWED, + egui::CursorIcon::NotAllowed => raylib::prelude::MouseCursor::MOUSE_CURSOR_NOT_ALLOWED, + egui::CursorIcon::Grab => raylib::prelude::MouseCursor::MOUSE_CURSOR_ARROW, + egui::CursorIcon::Grabbing => raylib::prelude::MouseCursor::MOUSE_CURSOR_POINTING_HAND, + egui::CursorIcon::ResizeHorizontal => { + raylib::prelude::MouseCursor::MOUSE_CURSOR_RESIZE_EW + } + egui::CursorIcon::ResizeNeSw => raylib::prelude::MouseCursor::MOUSE_CURSOR_RESIZE_NESW, + egui::CursorIcon::ResizeNwSe => raylib::prelude::MouseCursor::MOUSE_CURSOR_RESIZE_NWSE, + egui::CursorIcon::ResizeVertical => { + raylib::prelude::MouseCursor::MOUSE_CURSOR_RESIZE_NS + } + egui::CursorIcon::ResizeEast => raylib::prelude::MouseCursor::MOUSE_CURSOR_RESIZE_EW, + egui::CursorIcon::ResizeSouthEast => { + raylib::prelude::MouseCursor::MOUSE_CURSOR_RESIZE_NWSE + } + egui::CursorIcon::ResizeSouth => raylib::prelude::MouseCursor::MOUSE_CURSOR_RESIZE_NS, + egui::CursorIcon::ResizeSouthWest => { + raylib::prelude::MouseCursor::MOUSE_CURSOR_RESIZE_NESW + } + egui::CursorIcon::ResizeWest => raylib::prelude::MouseCursor::MOUSE_CURSOR_RESIZE_EW, + egui::CursorIcon::ResizeNorthWest => { + raylib::prelude::MouseCursor::MOUSE_CURSOR_RESIZE_NWSE + } + egui::CursorIcon::ResizeNorth => raylib::prelude::MouseCursor::MOUSE_CURSOR_RESIZE_NS, + egui::CursorIcon::ResizeNorthEast => { + raylib::prelude::MouseCursor::MOUSE_CURSOR_RESIZE_NESW + } + egui::CursorIcon::ResizeColumn => raylib::prelude::MouseCursor::MOUSE_CURSOR_RESIZE_ALL, + egui::CursorIcon::ResizeRow => raylib::prelude::MouseCursor::MOUSE_CURSOR_RESIZE_ALL, + + egui::CursorIcon::None => { + return None; + } + _ => raylib::prelude::MouseCursor::MOUSE_CURSOR_DEFAULT, + }; + Some(v) + } +} + +// Implement ConvertRE trait for converting from KeyboardKey to Key +impl ConvertRE> for raylib::prelude::KeyboardKey { + fn convert(&self) -> Option { + use egui::Key; + use raylib::prelude::KeyboardKey; + let v = match *self { + KeyboardKey::KEY_NULL => Key::Space, + KeyboardKey::KEY_APOSTROPHE => Key::Quote, + KeyboardKey::KEY_COMMA => Key::Comma, + KeyboardKey::KEY_MINUS => Key::Minus, + KeyboardKey::KEY_PERIOD => Key::Period, + KeyboardKey::KEY_SLASH => Key::Slash, + KeyboardKey::KEY_ZERO => Key::Num0, + KeyboardKey::KEY_ONE => Key::Num1, + KeyboardKey::KEY_TWO => Key::Num2, + KeyboardKey::KEY_THREE => Key::Num3, + KeyboardKey::KEY_FOUR => Key::Num4, + KeyboardKey::KEY_FIVE => Key::Num5, + KeyboardKey::KEY_SIX => Key::Num6, + KeyboardKey::KEY_SEVEN => Key::Num7, + KeyboardKey::KEY_EIGHT => Key::Num8, + KeyboardKey::KEY_NINE => Key::Num9, + KeyboardKey::KEY_SEMICOLON => Key::Semicolon, + KeyboardKey::KEY_EQUAL => Key::Equals, + KeyboardKey::KEY_A => Key::A, + KeyboardKey::KEY_B => Key::B, + KeyboardKey::KEY_C => Key::C, + KeyboardKey::KEY_D => Key::D, + KeyboardKey::KEY_E => Key::E, + KeyboardKey::KEY_F => Key::F, + KeyboardKey::KEY_G => Key::G, + KeyboardKey::KEY_H => Key::H, + KeyboardKey::KEY_I => Key::I, + KeyboardKey::KEY_J => Key::J, + KeyboardKey::KEY_K => Key::K, + KeyboardKey::KEY_L => Key::L, + KeyboardKey::KEY_M => Key::M, + KeyboardKey::KEY_N => Key::N, + KeyboardKey::KEY_O => Key::O, + KeyboardKey::KEY_P => Key::P, + KeyboardKey::KEY_Q => Key::Q, + KeyboardKey::KEY_R => Key::R, + KeyboardKey::KEY_S => Key::S, + KeyboardKey::KEY_T => Key::T, + KeyboardKey::KEY_U => Key::U, + KeyboardKey::KEY_V => Key::V, + KeyboardKey::KEY_W => Key::W, + KeyboardKey::KEY_X => Key::X, + KeyboardKey::KEY_Y => Key::Y, + KeyboardKey::KEY_Z => Key::Z, + KeyboardKey::KEY_LEFT_BRACKET => Key::OpenBracket, + KeyboardKey::KEY_BACKSLASH => Key::Backslash, + KeyboardKey::KEY_RIGHT_BRACKET => Key::CloseBracket, + KeyboardKey::KEY_GRAVE => Key::Backtick, + KeyboardKey::KEY_SPACE => Key::Space, + KeyboardKey::KEY_ESCAPE => Key::Escape, + KeyboardKey::KEY_ENTER => Key::Enter, + KeyboardKey::KEY_TAB => Key::Tab, + KeyboardKey::KEY_BACKSPACE => Key::Backspace, + KeyboardKey::KEY_INSERT => Key::Insert, + KeyboardKey::KEY_DELETE => Key::Delete, + KeyboardKey::KEY_RIGHT => Key::ArrowRight, + KeyboardKey::KEY_LEFT => Key::ArrowLeft, + KeyboardKey::KEY_DOWN => Key::ArrowDown, + KeyboardKey::KEY_UP => Key::ArrowUp, + KeyboardKey::KEY_PAGE_UP => Key::PageUp, + KeyboardKey::KEY_PAGE_DOWN => Key::PageDown, + KeyboardKey::KEY_HOME => Key::Home, + KeyboardKey::KEY_END => Key::End, + KeyboardKey::KEY_F1 => Key::F1, + KeyboardKey::KEY_F2 => Key::F2, + KeyboardKey::KEY_F3 => Key::F3, + KeyboardKey::KEY_F4 => Key::F4, + KeyboardKey::KEY_F5 => Key::F5, + KeyboardKey::KEY_F6 => Key::F6, + KeyboardKey::KEY_F7 => Key::F7, + KeyboardKey::KEY_F8 => Key::F8, + KeyboardKey::KEY_F9 => Key::F9, + KeyboardKey::KEY_F10 => Key::F10, + KeyboardKey::KEY_F11 => Key::F11, + KeyboardKey::KEY_F12 => Key::F12, + KeyboardKey::KEY_KP_0 => Key::Num0, + KeyboardKey::KEY_KP_1 => Key::Num1, + KeyboardKey::KEY_KP_2 => Key::Num2, + KeyboardKey::KEY_KP_3 => Key::Num3, + KeyboardKey::KEY_KP_4 => Key::Num4, + KeyboardKey::KEY_KP_5 => Key::Num5, + KeyboardKey::KEY_KP_6 => Key::Num6, + KeyboardKey::KEY_KP_7 => Key::Num7, + KeyboardKey::KEY_KP_8 => Key::Num8, + KeyboardKey::KEY_KP_9 => Key::Num9, + KeyboardKey::KEY_KP_DECIMAL => Key::Period, + KeyboardKey::KEY_KP_DIVIDE => Key::Slash, + KeyboardKey::KEY_KP_SUBTRACT => Key::Minus, + KeyboardKey::KEY_KP_ADD => Key::Plus, + KeyboardKey::KEY_KP_ENTER => Key::Enter, + KeyboardKey::KEY_KP_EQUAL => Key::Equals, + KeyboardKey::KEY_BACK => Key::Backspace, + _ => { + return None; + } + }; + Some(v) + } +} + +/// Convert raw image (Uncompressed RGBA) of size `size`, stored in `rgba` into raylib [Image](raylib::texture::Image) +/// # Safety +/// Unsafe behaviour occurs if image created did not allocate enough pixels for RGBA writing. +/// However, this function uses Raylib's `gen_image_color` to allocate an image before writing. +/// Currently, Raylib's `GenImageColor` function will `calloc` for `size[0]*size[1]*4` bytes in RGBA format itself. +/// Thus, hypothetically this function is always safe. +#[allow(dead_code)] +pub fn rl_image_from_rgba(size: [usize; 2], rgba: &[u8]) -> raylib::prelude::Image { + use raylib::prelude::{Color, Image}; + let mut img = Image::gen_image_color(size[0] as i32, size[1] as i32, Color::BLACK.alpha(0.0)); + img.set_format(raylib::ffi::PixelFormat::PIXELFORMAT_UNCOMPRESSED_R8G8B8A8); + let raw = img.to_raw(); + let len = (raw.width * raw.height * 4) as usize; + let rawptr = raw.data as *mut u8; + unsafe { + std::ptr::copy(rgba.as_ptr(), rawptr, len); + Image::from_raw(raw) + } +}