use std::{time::Instant, rc::Rc, cell::RefCell};
use egui::{Key, PointerButton, Pos2, RawInput, pos2, vec2};
use ggez::{
event::MouseButton,
input::keyboard::{
KeyCode,
KeyMods,
}
};
pub struct Input {
dt: Instant,
pointer_pos: Pos2,
pub(crate) raw: RawInput,
pub(crate) scale_factor: f32,
pub clipboard: Rc<RefCell<String>>,
}
impl Default for Input {
fn default() -> Self {
Self {
dt: Instant::now(),
pointer_pos: Default::default(),
raw: Default::default(),
scale_factor: 1.0,
clipboard: Default::default()
}
}
}
impl Input {
pub(crate) fn take(&mut self) -> RawInput {
self.raw.predicted_dt = self.dt.elapsed().as_secs_f32();
self.dt = Instant::now();
self.raw.take()
}
pub fn update(&mut self, ctx: &ggez::Context) {
for key in ctx.keyboard.pressed_keys() {
if ctx.keyboard.active_mods().contains(KeyMods::CTRL) {
match key {
KeyCode::C => self.raw.events.push(egui::Event::Copy),
KeyCode::X => self.raw.events.push(egui::Event::Cut),
KeyCode::V => unimplemented!("Pegar esta desimplementado temporalmente"),
_ => ()
}
}
if let Some(key) = translate_keycode(*key) {
self.raw.events.push(egui::Event::Key {
key,
pressed: true,
modifiers: translate_modifier(ctx.keyboard.active_mods())
})
}
}
for button in [MouseButton::Left, MouseButton::Middle, MouseButton::Right] {
if ctx.mouse.button_pressed(button) {
self.raw.events.push(egui::Event::PointerButton {
button: match button {
MouseButton::Left => PointerButton::Primary,
MouseButton::Right => PointerButton::Secondary,
MouseButton::Middle => PointerButton::Middle,
_ => unreachable!()
},
pos: self.pointer_pos,
pressed: true,
modifiers: translate_modifier(ctx.keyboard.active_mods())
});
}
else {
self.raw.events.push(egui::Event::PointerButton {
button: match button {
MouseButton::Left => PointerButton::Primary,
MouseButton::Right => PointerButton::Secondary,
MouseButton::Middle => PointerButton::Middle,
_ => unreachable!()
},
pos: self.pointer_pos,
pressed: false,
modifiers: translate_modifier(ctx.keyboard.active_mods())
});
}
}
if ctx.mouse.delta() != [0.0, 0.0].into() {
let ggez::mint::Point2 { x, y } = ctx.mouse.position();
self.pointer_pos = pos2(
x / self.scale_factor,
y / self.scale_factor
);
self.raw.events.push(egui::Event::PointerMoved(self.pointer_pos));
}
}
pub fn set_scale_factor(&mut self, scale_factor: f32, (w, h): (f32, f32)) {
self.scale_factor = scale_factor;
self.raw.pixels_per_point = Some(scale_factor);
self.resize_event(w, h);
}
pub fn resize_event(&mut self, w: f32, h: f32) {
self.raw.screen_rect = Some(egui::Rect::from_min_size(
Default::default(),
vec2(w, h) / self.scale_factor,
));
}
pub fn mouse_wheel_event(&mut self, x: f32, y: f32) {
self.raw.events.push(egui::Event::Scroll(vec2(x, y)));
}
pub fn text_input_event(&mut self, ch: char) {
if is_printable(ch) {
self.raw.events.push(egui::Event::Text(ch.to_string()));
}
}
}
#[inline]
fn translate_keycode(key: KeyCode) -> Option<egui::Key> {
Some(match key {
KeyCode::Escape => Key::Escape,
KeyCode::Insert => Key::Insert,
KeyCode::Home => Key::Home,
KeyCode::Delete => Key::Delete,
KeyCode::End => Key::End,
KeyCode::PageDown => Key::PageDown,
KeyCode::PageUp => Key::PageUp,
KeyCode::Left => Key::ArrowLeft,
KeyCode::Up => Key::ArrowUp,
KeyCode::Right => Key::ArrowRight,
KeyCode::Down => Key::ArrowDown,
KeyCode::Back => Key::Backspace,
KeyCode::Return => Key::Enter,
KeyCode::Tab => Key::Tab,
KeyCode::Space => Key::Space,
KeyCode::A => Key::A,
KeyCode::K => Key::K,
KeyCode::U => Key::U,
KeyCode::W => Key::W,
KeyCode::Z => Key::Z,
_ => {
return None;
}
})
}
#[inline]
fn translate_modifier(keymods: KeyMods) -> egui::Modifiers {
egui::Modifiers {
alt: keymods.intersects(KeyMods::ALT),
ctrl: keymods.intersects(KeyMods::CTRL),
shift: keymods.intersects(KeyMods::SHIFT),
#[cfg(not(target_os = "macos"))]
mac_cmd: false,
#[cfg(not(target_os = "macos"))]
command: keymods.intersects(KeyMods::CTRL),
#[cfg(target_os = "macos")]
mac_cmd: keymods.intersects(KeyMods::LOGO),
#[cfg(target_os = "macos")]
command: keymods.intersects(KeyMods::LOGO),
}
}
#[inline]
fn is_printable(chr: char) -> bool {
let is_in_private_use_area = '\u{e000}' <= chr && chr <= '\u{f8ff}'
|| '\u{f0000}' <= chr && chr <= '\u{ffffd}'
|| '\u{100000}' <= chr && chr <= '\u{10fffd}';
!is_in_private_use_area && !chr.is_ascii_control()
}