309 lines
7.9 KiB
Rust
309 lines
7.9 KiB
Rust
use crate::prelude::*;
|
|
use std::{
|
|
ops::{BitOr, Deref, DerefMut},
|
|
rc::Rc,
|
|
};
|
|
|
|
#[derive(Clone, Copy, PartialEq)]
|
|
pub enum CursorButton {
|
|
Left,
|
|
Right,
|
|
Middle,
|
|
}
|
|
|
|
#[derive(Clone, Copy, PartialEq)]
|
|
pub enum CursorSense {
|
|
PressStart(CursorButton),
|
|
Pressing(CursorButton),
|
|
PressEnd(CursorButton),
|
|
HoverStart,
|
|
Hovering,
|
|
HoverEnd,
|
|
Scroll,
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct CursorSenses(Vec<CursorSense>);
|
|
|
|
impl Event for CursorSenses {
|
|
type Data<'a> = CursorData<'a>;
|
|
type State = SensorState;
|
|
fn should_run<'a>(&self, data: &Self::Data<'a>) -> Option<Self::Data<'a>> {
|
|
if let Some(sense) = should_run(self, &data.cursor, data.hover) {
|
|
let mut data = data.clone();
|
|
data.sense = sense;
|
|
Some(data)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
}
|
|
|
|
impl CursorSense {
|
|
pub fn click() -> Self {
|
|
Self::PressStart(CursorButton::Left)
|
|
}
|
|
pub fn click_or_drag() -> CursorSenses {
|
|
Self::click() | Self::Pressing(CursorButton::Left)
|
|
}
|
|
pub fn unclick() -> Self {
|
|
Self::PressEnd(CursorButton::Left)
|
|
}
|
|
pub fn is_dragging(&self) -> bool {
|
|
matches!(self, CursorSense::Pressing(CursorButton::Left))
|
|
}
|
|
}
|
|
|
|
#[derive(Default, Clone)]
|
|
pub struct CursorState {
|
|
pub pos: Vec2,
|
|
pub exists: bool,
|
|
pub buttons: CursorButtons,
|
|
pub scroll_delta: Vec2,
|
|
}
|
|
|
|
#[derive(Default, Clone)]
|
|
pub struct CursorButtons {
|
|
pub left: ActivationState,
|
|
pub middle: ActivationState,
|
|
pub right: ActivationState,
|
|
}
|
|
|
|
impl CursorButtons {
|
|
pub fn select(&self, button: &CursorButton) -> &ActivationState {
|
|
match button {
|
|
CursorButton::Left => &self.left,
|
|
CursorButton::Right => &self.right,
|
|
CursorButton::Middle => &self.middle,
|
|
}
|
|
}
|
|
|
|
pub fn end_frame(&mut self) {
|
|
self.left.end_frame();
|
|
self.middle.end_frame();
|
|
self.right.end_frame();
|
|
}
|
|
|
|
pub fn iter(&self) -> impl Iterator<Item = (CursorButton, &ActivationState)> {
|
|
[
|
|
CursorButton::Left,
|
|
CursorButton::Middle,
|
|
CursorButton::Right,
|
|
]
|
|
.into_iter()
|
|
.map(|b| (b, self.select(&b)))
|
|
}
|
|
}
|
|
|
|
impl CursorState {
|
|
pub fn end_frame(&mut self) {
|
|
self.buttons.end_frame();
|
|
self.scroll_delta = Vec2::ZERO;
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, Default, PartialEq)]
|
|
pub enum ActivationState {
|
|
Start,
|
|
On,
|
|
End,
|
|
#[default]
|
|
Off,
|
|
}
|
|
|
|
/// this and other similar stuff has a generic
|
|
/// because I kind of want to make CursorModule generic
|
|
/// or basically have some way to have custom senses
|
|
/// that depend on active widget positions
|
|
/// but I'm not sure how or if worth it
|
|
pub struct Sensor<Ctx: HasEvents, Data> {
|
|
pub senses: CursorSenses,
|
|
pub f: Rc<dyn EventFn<Ctx, Data>>,
|
|
}
|
|
|
|
pub type SenseShape = UiRegion;
|
|
|
|
#[derive(Default, Debug)]
|
|
pub struct SensorState {
|
|
pub hover: ActivationState,
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct CursorData<'a> {
|
|
/// where this widget was hit
|
|
pub pos: Vec2,
|
|
pub size: Vec2,
|
|
pub scroll_delta: Vec2,
|
|
pub hover: ActivationState,
|
|
pub cursor: CursorState,
|
|
/// the first sense that triggered this
|
|
pub sense: CursorSense,
|
|
pub render: &'a UiRenderState,
|
|
}
|
|
|
|
pub trait SensorUi {
|
|
fn run_sensors<Rsc: HasEvents>(
|
|
&self,
|
|
rsc: &mut Rsc,
|
|
state: &mut Rsc::State,
|
|
cursor: CursorState,
|
|
window_size: Vec2,
|
|
);
|
|
}
|
|
|
|
impl SensorUi for UiRenderState {
|
|
fn run_sensors<Rsc: HasEvents>(
|
|
&self,
|
|
rsc: &mut Rsc,
|
|
state: &mut Rsc::State,
|
|
cursor: CursorState,
|
|
window_size: Vec2,
|
|
) {
|
|
// in order to remove this take, need to store active list in UiRenderState somehow
|
|
// this would probably be done through a generic parameter that adds yet another rsc /
|
|
// state like thing, but local to render state, and is passed to UiRsc events so you can
|
|
// update it there?
|
|
let mut active = std::mem::take(&mut rsc.events_mut().get_type::<CursorSense>().active);
|
|
for layer in self.layers.indices().rev() {
|
|
let mut sensed = false;
|
|
for (id, sensor) in active.get_mut(&layer).into_flat_iter() {
|
|
let shape = self.active.get(id).unwrap().region;
|
|
let region = shape.to_px(window_size);
|
|
let in_shape = cursor.exists && region.contains(cursor.pos);
|
|
sensor.hover.update(in_shape);
|
|
if sensor.hover == ActivationState::Off {
|
|
continue;
|
|
}
|
|
sensed = true;
|
|
|
|
let cursor = cursor.clone();
|
|
|
|
let data = CursorData {
|
|
pos: cursor.pos - region.top_left,
|
|
size: region.bot_right - region.top_left,
|
|
scroll_delta: cursor.scroll_delta,
|
|
hover: sensor.hover,
|
|
cursor,
|
|
// this does not have any meaning;
|
|
// might wanna set up Event to have a prepare stage
|
|
sense: CursorSense::Hovering,
|
|
render: self,
|
|
};
|
|
rsc.run_event::<CursorSense>(*id, data, state);
|
|
}
|
|
if sensed {
|
|
break;
|
|
}
|
|
}
|
|
rsc.events_mut().get_type::<CursorSense>().active = active;
|
|
}
|
|
}
|
|
|
|
pub fn should_run(
|
|
senses: &CursorSenses,
|
|
cursor: &CursorState,
|
|
hover: ActivationState,
|
|
) -> Option<CursorSense> {
|
|
for sense in senses.iter() {
|
|
if match sense {
|
|
CursorSense::PressStart(button) => cursor.buttons.select(button).is_start(),
|
|
CursorSense::Pressing(button) => cursor.buttons.select(button).is_on(),
|
|
CursorSense::PressEnd(button) => cursor.buttons.select(button).is_end(),
|
|
CursorSense::HoverStart => hover.is_start(),
|
|
CursorSense::Hovering => hover.is_on(),
|
|
CursorSense::HoverEnd => hover.is_end(),
|
|
CursorSense::Scroll => cursor.scroll_delta != Vec2::ZERO,
|
|
} {
|
|
return Some(*sense);
|
|
}
|
|
}
|
|
None
|
|
}
|
|
|
|
impl ActivationState {
|
|
pub fn is_start(&self) -> bool {
|
|
*self == Self::Start
|
|
}
|
|
pub fn is_on(&self) -> bool {
|
|
*self == Self::Start || *self == Self::On
|
|
}
|
|
pub fn is_end(&self) -> bool {
|
|
*self == Self::End
|
|
}
|
|
pub fn is_off(&self) -> bool {
|
|
*self == Self::End || *self == Self::Off
|
|
}
|
|
pub fn update(&mut self, on: bool) {
|
|
*self = match *self {
|
|
Self::Start => match on {
|
|
true => Self::On,
|
|
false => Self::End,
|
|
},
|
|
Self::On => match on {
|
|
true => Self::On,
|
|
false => Self::End,
|
|
},
|
|
Self::End => match on {
|
|
true => Self::Start,
|
|
false => Self::Off,
|
|
},
|
|
Self::Off => match on {
|
|
true => Self::Start,
|
|
false => Self::Off,
|
|
},
|
|
}
|
|
}
|
|
|
|
pub fn end_frame(&mut self) {
|
|
match self {
|
|
Self::Start => *self = Self::On,
|
|
Self::End => *self = Self::Off,
|
|
_ => (),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl EventLike for CursorSense {
|
|
type Event = CursorSenses;
|
|
fn into_event(self) -> Self::Event {
|
|
self.into()
|
|
}
|
|
}
|
|
|
|
impl Deref for CursorSenses {
|
|
type Target = Vec<CursorSense>;
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
&self.0
|
|
}
|
|
}
|
|
|
|
impl DerefMut for CursorSenses {
|
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
&mut self.0
|
|
}
|
|
}
|
|
|
|
impl From<CursorSense> for CursorSenses {
|
|
fn from(val: CursorSense) -> Self {
|
|
CursorSenses(vec![val])
|
|
}
|
|
}
|
|
|
|
impl BitOr for CursorSense {
|
|
type Output = CursorSenses;
|
|
|
|
fn bitor(self, rhs: Self) -> Self::Output {
|
|
CursorSenses(vec![self, rhs])
|
|
}
|
|
}
|
|
|
|
impl BitOr<CursorSense> for CursorSenses {
|
|
type Output = Self;
|
|
|
|
fn bitor(mut self, rhs: CursorSense) -> Self::Output {
|
|
self.0.push(rhs);
|
|
self
|
|
}
|
|
}
|