use crate::geom::{Rect, Vec2};
use crate::input::{KeyCode, KeyMods, KeyPhase, KeyPress, MouseButton, UiEvent};
use crate::value::Value;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum DragPhase {
Start,
Move,
End,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Drag {
pub phase: DragPhase,
pub pos: Vec2,
pub delta: Vec2,
pub parent: Rect,
pub cell: Option<(u32, u32)>,
pub line: Option<u32>,
pub byte: Option<usize>,
pub clicks: Option<u32>,
}
impl Drag {
pub fn ratio(&self) -> Vec2 {
let along = |p: f32, at: f32, len: f32| {
if len > 0.0 {
((p - at) / len).clamp(0.0, 1.0)
} else {
0.0
}
};
Vec2::new(
along(self.pos.x, self.parent.x, self.parent.w),
along(self.pos.y, self.parent.y, self.parent.h),
)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ButtonPhase {
Press,
Move,
Release,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct ButtonEvent {
pub phase: ButtonPhase,
pub button: MouseButton,
pub pos: Vec2,
pub clicks: Option<u32>,
pub cell: Option<(u32, u32)>,
pub line: Option<u32>,
pub byte: Option<usize>,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Scroll {
pub pos: Vec2,
pub delta: Vec2,
pub lines: Option<i64>,
pub mods: Option<KeyMods>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum HoverPhase {
Enter,
Leave,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum HoverBy {
Pointer,
Content,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Hover {
pub phase: HoverPhase,
pub by: HoverBy,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Layout {
pub rect: Rect,
pub parent: Rect,
pub scale: f32,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct TextInput<'a> {
pub text: &'a str,
pub pasted: bool,
pub concealed: bool,
pub transient: bool,
}
fn point(p: &Value) -> Option<Vec2> {
Some(Vec2::new(p.get_f32("x")?, p.get_f32("y")?))
}
fn rect(p: &Value) -> Option<Rect> {
Some(Rect::new(
p.get_f32("x")?,
p.get_f32("y")?,
p.get_f32("w")?,
p.get_f32("h")?,
))
}
fn small(p: &Value, key: &str) -> Option<u32> {
p.get_int(key).and_then(|v| u32::try_from(v).ok())
}
impl UiEvent {
pub fn tag(&self) -> Option<&Value> {
self.payload.get("tag")
}
pub fn drag(&self) -> Option<Drag> {
let p = &self.payload;
if self.kind()? != "drag" {
return None;
}
let phase = match p.get_str("phase")? {
"start" => DragPhase::Start,
"move" => DragPhase::Move,
"end" => DragPhase::End,
_ => return None,
};
let cell = p
.get("cell")
.and_then(|c| Some((small(c, "row")?, small(c, "col")?)));
Some(Drag {
phase,
pos: point(p)?,
delta: Vec2::new(p.get_f32("dx")?, p.get_f32("dy")?),
parent: p.get("parent").and_then(rect).unwrap_or_default(),
cell,
line: small(p, "line"),
byte: p.get_int("byte").and_then(|v| usize::try_from(v).ok()),
clicks: small(p, "clicks"),
})
}
pub fn button(&self) -> Option<ButtonEvent> {
let p = &self.payload;
if self.kind()? != "button" {
return None;
}
let phase = match p.get_str("phase")? {
"press" => ButtonPhase::Press,
"move" => ButtonPhase::Move,
"release" => ButtonPhase::Release,
_ => return None,
};
let button = match p.get("button")? {
Value::Str(name) => MouseButton::from_name(name)?,
Value::Int(code) => MouseButton::from_code(u32::try_from(*code).ok()?),
_ => return None,
};
let cell = p
.get("cell")
.and_then(|c| Some((small(c, "row")?, small(c, "col")?)));
Some(ButtonEvent {
phase,
button,
pos: point(p)?,
clicks: small(p, "clicks"),
cell,
line: small(p, "line"),
byte: p.get_int("byte").and_then(|v| usize::try_from(v).ok()),
})
}
pub fn key_press(&self) -> Option<(KeyPhase, KeyPress)> {
let p = &self.payload;
if self.kind()? != "key" {
return None;
}
let phase = match p.get_str("phase")? {
"down" => KeyPhase::Down,
"up" => KeyPhase::Up,
_ => return None,
};
let code = KeyCode::from_name(p.get_str("code")?)?;
let physical = p
.get_str("physical")
.and_then(KeyCode::from_name)
.unwrap_or(KeyCode::Unknown);
let press = KeyPress {
code,
physical,
mods: mods(p),
text: p.get_str("text").map(str::to_string),
repeat: p.get_bool("repeat").unwrap_or(false),
location: p
.get_str("location")
.and_then(crate::input::KeyLocation::from_name)
.unwrap_or_default(),
locks: crate::input::KeyLocks {
caps: p.get_bool("caps_lock").unwrap_or(false),
num: p.get_bool("num_lock").unwrap_or(false),
},
};
Some((phase, press))
}
pub fn text(&self) -> Option<TextInput<'_>> {
let p = &self.payload;
if self.kind()? != "text" {
return None;
}
Some(TextInput {
text: p.get_str("text")?,
pasted: p.get_bool("pasted").unwrap_or(false),
concealed: p.get_bool("concealed").unwrap_or(false),
transient: p.get_bool("transient").unwrap_or(false),
})
}
pub fn scroll(&self) -> Option<Scroll> {
let p = &self.payload;
if self.kind()? != "scroll" {
return None;
}
Some(Scroll {
pos: point(p)?,
delta: Vec2::new(p.get_f32("dx")?, p.get_f32("dy")?),
lines: p.get_int("lines"),
mods: p.get("mods").map(mods),
})
}
pub fn hover(&self) -> Option<Hover> {
if self.kind()? != "hover" {
return None;
}
let phase = match self.payload.get_str("phase")? {
"enter" => HoverPhase::Enter,
"leave" => HoverPhase::Leave,
_ => return None,
};
let by = match self.payload.get_str("by") {
Some("content") => HoverBy::Content,
_ => HoverBy::Pointer,
};
Some(Hover { phase, by })
}
pub fn modifiers(&self) -> Option<KeyMods> {
if self.kind()? != "modifiers" {
return None;
}
Some(mods(&self.payload))
}
pub fn layout(&self) -> Option<Layout> {
let p = &self.payload;
if self.kind()? != "layout" {
return None;
}
Some(Layout {
rect: rect(p)?,
parent: p.get("parent").and_then(rect).unwrap_or_default(),
scale: p.get_f32("scale").unwrap_or(1.0),
})
}
}
fn mods(p: &Value) -> KeyMods {
let on = |k| p.get_bool(k).unwrap_or(false);
KeyMods {
shift: on("shift"),
ctrl: on("ctrl"),
alt: on("alt"),
super_key: on("super"),
}
}