use core::{cell::Cell, time::Duration};
use crate::{
focus::{self, FocusAction, FocusGroup, RoleSet},
primitives::Point,
render::ContentShape,
};
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq)]
pub enum Event {
Touch(embedded_touch::Touch),
Scroll(Point),
Focus {
action: FocusAction,
group: FocusGroup,
},
KeyDown(Key),
KeyUp(Key),
}
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Key {
Character(char),
UpArrow,
DownArrow,
LeftArrow,
RightArrow,
Escape,
Backspace,
Delete,
}
impl From<FocusAction> for Event {
fn from(action: FocusAction) -> Self {
Self::Focus {
action,
group: focus::GROUP_0,
}
}
}
impl Event {
#[must_use]
pub fn offset(&self, offset: Point) -> Self {
let mut event = self.clone();
match &mut event {
Self::Touch(touch) => {
touch.location += offset.into();
}
Self::Scroll(_) | Self::Focus { .. } | Self::KeyDown(_) | Self::KeyUp(_) => {}
}
event
}
#[must_use]
pub fn with_focus_group(mut self, group: FocusGroup) -> Self {
if let Self::Focus {
group: ref mut g, ..
} = self
{
*g = group;
}
self
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub enum EventResult {
Handled {
shape: ContentShape,
request_focus: bool,
group: FocusGroup,
},
#[default]
Deferred,
}
impl EventResult {
#[must_use]
pub const fn handled_unfocused() -> Self {
Self::Handled {
shape: ContentShape::Empty,
request_focus: false,
group: focus::GROUP_0,
}
}
#[must_use]
pub const fn handled_focused(shape: ContentShape) -> Self {
Self::Handled {
shape,
request_focus: true,
group: focus::GROUP_0,
}
}
#[must_use]
pub const fn with_group(mut self, group: FocusGroup) -> Self {
let Self::Handled {
group: event_group, ..
} = &mut self
else {
return Self::Deferred;
};
*event_group = group;
self
}
#[must_use]
pub const fn is_handled(&self) -> bool {
matches!(self, Self::Handled { .. })
}
#[must_use]
pub const fn requested_focus(&self) -> bool {
matches!(
self,
Self::Handled {
request_focus: true,
..
}
)
}
#[must_use]
pub fn shape(&self) -> Option<&ContentShape> {
match self {
Self::Handled { shape, .. } => Some(shape),
Self::Deferred => None,
}
}
#[must_use]
pub fn with_offset(mut self, offset: Point) -> Self {
match &mut self {
Self::Handled { shape, .. } => {
shape.offset(offset);
}
Self::Deferred => (),
}
self
}
}
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EventContext {
pub app_time: Duration,
pub redraw_requested: Cell<bool>,
pub view_rebuild_requested: Cell<bool>,
pub roles: RoleSet,
}
impl EventContext {
#[must_use]
pub const fn new(app_time: Duration) -> Self {
Self {
app_time,
redraw_requested: Cell::new(false),
view_rebuild_requested: Cell::new(false),
roles: RoleSet::any(),
}
}
#[must_use]
pub fn with_roles(mut self, roles: impl Into<RoleSet>) -> Self {
self.roles = roles.into();
self
}
pub fn request_view_rebuild(&self) {
self.view_rebuild_requested.set(true);
}
pub fn request_redraw(&self) {
self.redraw_requested.set(true);
}
}
#[cfg(feature = "embedded-graphics-simulator")]
pub mod simulator {
use crate::{event::Key, primitives::Point};
use super::Event;
use embedded_graphics_simulator::{SimulatorEvent, sdl2::Keycode};
use embedded_touch::{Phase, PointerButton, Tool, Touch, TouchPoint};
#[derive(Debug, Default)]
pub struct MouseTracker {
touch: Option<Touch>,
}
impl MouseTracker {
#[must_use]
pub fn new() -> Self {
Self { touch: None }
}
pub fn process_event(&mut self, event: SimulatorEvent) -> Option<Event> {
match event {
SimulatorEvent::MouseButtonDown { point, mouse_btn } => {
let touch = Touch {
id: 0,
location: TouchPoint::new(point.x, point.y),
phase: Phase::Started,
tool: Tool::Pointer {
button: map_button(mouse_btn),
},
};
self.touch = Some(touch.clone());
Some(Event::Touch(touch))
}
SimulatorEvent::MouseButtonUp { point, mouse_btn } => {
let touch = Touch {
id: 0,
location: TouchPoint::new(point.x, point.y),
phase: Phase::Ended,
tool: Tool::Pointer {
button: map_button(mouse_btn),
},
};
self.touch = None;
Some(Event::Touch(touch))
}
SimulatorEvent::MouseMove { point } => {
if let Some(touch) = &mut self.touch {
touch.location = TouchPoint::new(point.x, point.y);
touch.phase = Phase::Moved;
Some(Event::Touch(touch.clone()))
} else {
let touch = Touch {
id: 0,
location: TouchPoint::new(point.x, point.y),
phase: Phase::Hovering(None),
tool: Tool::Pointer {
button: PointerButton::None,
},
};
Some(Event::Touch(touch))
}
}
SimulatorEvent::MouseWheel {
scroll_delta,
direction,
} => {
if direction == embedded_graphics_simulator::sdl2::MouseWheelDirection::Flipped
{
Some(Event::Scroll(Point::new(
scroll_delta.x * 4,
scroll_delta.y * 4,
)))
} else {
Some(Event::Scroll(Point::new(
-scroll_delta.x * 4,
-scroll_delta.y * 4,
)))
}
}
SimulatorEvent::Quit => None,
SimulatorEvent::KeyDown {
keycode,
keymod: _,
repeat: _,
} => keycode.try_into().ok().map(Event::KeyDown),
SimulatorEvent::KeyUp {
keycode,
keymod: _,
repeat: _,
} => keycode.try_into().ok().map(Event::KeyUp),
}
}
}
fn map_button(mouse_btn: embedded_graphics_simulator::sdl2::MouseButton) -> PointerButton {
match mouse_btn {
embedded_graphics_simulator::sdl2::MouseButton::Left => PointerButton::Primary,
embedded_graphics_simulator::sdl2::MouseButton::Right => PointerButton::Secondary,
embedded_graphics_simulator::sdl2::MouseButton::Middle => PointerButton::Tertiary,
_ => PointerButton::None,
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct UnknownKeycode;
impl TryFrom<embedded_graphics_simulator::sdl2::Keycode> for Key {
type Error = UnknownKeycode;
#[allow(clippy::too_many_lines)]
fn try_from(
value: embedded_graphics_simulator::sdl2::Keycode,
) -> Result<Self, Self::Error> {
match value {
Keycode::RIGHT => Ok(Self::RightArrow),
Keycode::LEFT => Ok(Self::LeftArrow),
Keycode::DOWN => Ok(Self::DownArrow),
Keycode::UP => Ok(Self::UpArrow),
Keycode::ESCAPE => Ok(Self::Escape),
Keycode::DELETE => Ok(Self::Delete),
Keycode::BACKSPACE | Keycode::KP_BACKSPACE => Ok(Self::Backspace),
Keycode::TAB | Keycode::KP_TAB => Ok(Self::Character('\t')),
Keycode::RETURN | Keycode::KP_ENTER => Ok(Self::Character('\n')),
Keycode::SPACE | Keycode::KP_SPACE => Ok(Self::Character(' ')),
Keycode::EXCLAIM | Keycode::KP_EXCLAM => Ok(Self::Character('!')),
Keycode::QUOTEDBL => Ok(Self::Character('"')),
Keycode::HASH | Keycode::KP_HASH => Ok(Self::Character('#')),
Keycode::DOLLAR => Ok(Self::Character('$')),
Keycode::PERCENT | Keycode::KP_PERCENT => Ok(Self::Character('%')),
Keycode::AMPERSAND | Keycode::KP_AMPERSAND => Ok(Self::Character('&')),
Keycode::QUOTE => Ok(Self::Character('\'')),
Keycode::LEFTPAREN | Keycode::KP_LEFTPAREN => Ok(Self::Character('(')),
Keycode::RIGHTPAREN | Keycode::KP_RIGHTPAREN => Ok(Self::Character(')')),
Keycode::ASTERISK | Keycode::KP_MULTIPLY => Ok(Self::Character('*')),
Keycode::PLUS | Keycode::KP_PLUS => Ok(Self::Character('+')),
Keycode::COMMA | Keycode::KP_COMMA => Ok(Self::Character(',')),
Keycode::MINUS | Keycode::KP_MINUS => Ok(Self::Character('-')),
Keycode::PERIOD | Keycode::KP_PERIOD => Ok(Self::Character('.')),
Keycode::SLASH | Keycode::KP_DIVIDE => Ok(Self::Character('/')),
Keycode::COLON | Keycode::KP_COLON => Ok(Self::Character(':')),
Keycode::SEMICOLON => Ok(Self::Character(';')),
Keycode::LESS | Keycode::KP_LESS => Ok(Self::Character('<')),
Keycode::EQUALS | Keycode::KP_EQUALS => Ok(Self::Character('=')),
Keycode::GREATER | Keycode::KP_GREATER => Ok(Self::Character('>')),
Keycode::QUESTION => Ok(Self::Character('?')),
Keycode::AT | Keycode::KP_AT => Ok(Self::Character('@')),
Keycode::LEFTBRACKET => Ok(Self::Character('[')),
Keycode::BACKSLASH => Ok(Self::Character('\\')),
Keycode::RIGHTBRACKET => Ok(Self::Character(']')),
Keycode::CARET | Keycode::KP_XOR => Ok(Self::Character('^')),
Keycode::UNDERSCORE => Ok(Self::Character('_')),
Keycode::BACKQUOTE => Ok(Self::Character('`')),
Keycode::KP_LEFTBRACE => Ok(Self::Character('{')),
Keycode::KP_RIGHTBRACE => Ok(Self::Character('}')),
Keycode::KP_VERTICALBAR => Ok(Self::Character('|')),
Keycode::NUM_0 | Keycode::KP_0 => Ok(Self::Character('0')),
Keycode::NUM_1 | Keycode::KP_1 => Ok(Self::Character('1')),
Keycode::NUM_2 | Keycode::KP_2 => Ok(Self::Character('2')),
Keycode::NUM_3 | Keycode::KP_3 => Ok(Self::Character('3')),
Keycode::NUM_4 | Keycode::KP_4 => Ok(Self::Character('4')),
Keycode::NUM_5 | Keycode::KP_5 => Ok(Self::Character('5')),
Keycode::NUM_6 | Keycode::KP_6 => Ok(Self::Character('6')),
Keycode::NUM_7 | Keycode::KP_7 => Ok(Self::Character('7')),
Keycode::NUM_8 | Keycode::KP_8 => Ok(Self::Character('8')),
Keycode::NUM_9 | Keycode::KP_9 => Ok(Self::Character('9')),
Keycode::A | Keycode::KP_A => Ok(Self::Character('a')),
Keycode::B | Keycode::KP_B => Ok(Self::Character('b')),
Keycode::C | Keycode::KP_C => Ok(Self::Character('c')),
Keycode::D | Keycode::KP_D => Ok(Self::Character('d')),
Keycode::E | Keycode::KP_E => Ok(Self::Character('e')),
Keycode::F | Keycode::KP_F => Ok(Self::Character('f')),
Keycode::G => Ok(Self::Character('g')),
Keycode::H => Ok(Self::Character('h')),
Keycode::I => Ok(Self::Character('i')),
Keycode::J => Ok(Self::Character('j')),
Keycode::K => Ok(Self::Character('k')),
Keycode::L => Ok(Self::Character('l')),
Keycode::M => Ok(Self::Character('m')),
Keycode::N => Ok(Self::Character('n')),
Keycode::O => Ok(Self::Character('o')),
Keycode::P => Ok(Self::Character('p')),
Keycode::Q => Ok(Self::Character('q')),
Keycode::R => Ok(Self::Character('r')),
Keycode::S => Ok(Self::Character('s')),
Keycode::T => Ok(Self::Character('t')),
Keycode::U => Ok(Self::Character('u')),
Keycode::V => Ok(Self::Character('v')),
Keycode::W => Ok(Self::Character('w')),
Keycode::X => Ok(Self::Character('x')),
Keycode::Y => Ok(Self::Character('y')),
Keycode::Z => Ok(Self::Character('z')),
_ => Err(UnknownKeycode),
}
}
}
}