use crate::{
element::{MeasureContext, ProcessContext, UiElement},
signal::{trigger::SignalTrigger, Signal},
};
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub enum InteractableEvent {
#[default]
Click,
Hover,
Active,
}
pub struct Interactable {
pub element: Box<dyn UiElement>,
pub event: InteractableEvent,
pub signal: SignalTrigger,
}
impl Interactable {
pub fn new<S: Signal, F: Fn() -> S + 'static>(
element: Box<dyn UiElement>,
event: InteractableEvent,
signal: F
) -> Self {
Self {
element,
event,
signal: SignalTrigger::new(signal),
}
}
}
impl UiElement for Interactable {
fn name(&self) -> &'static str {
"interactable"
}
fn size(&self) -> Option<crate::layout::Size2d> {
self.element.size()
}
fn measure(&self, ctx: MeasureContext) -> crate::measure::Response {
self.element.measure(ctx)
}
fn process(&self, ctx: ProcessContext) {
let rect = ctx.measure.rect(ctx.layout.position);
let event_happened = match self.event {
InteractableEvent::Click => ctx.input.check_click(rect).is_some(),
InteractableEvent::Hover => ctx.input.check_hover(rect),
InteractableEvent::Active => ctx.input.check_active(rect).is_some(),
};
if event_happened {
self.signal.fire(ctx.signal);
}
self.element.process(ctx)
}
}
pub trait ElementInteractableExt: UiElement {
fn into_interactable<S: Signal, F: Fn() -> S + 'static>(self, event: InteractableEvent, signal: F) -> Interactable;
fn on_click<S: Signal, F: Fn() -> S + 'static>(self, signal: F) -> Interactable;
fn on_hover<S: Signal, F: Fn() -> S + 'static>(self, signal: F) -> Interactable;
fn on_active<S: Signal, F: Fn() -> S + 'static>(self, signal: F) -> Interactable;
}
impl<T: UiElement + 'static> ElementInteractableExt for T {
fn into_interactable<S: Signal, F: Fn() -> S + 'static>(self, event: InteractableEvent, signal: F) -> Interactable {
Interactable::new(Box::new(self), event, signal)
}
fn on_click<S: Signal, F: Fn() -> S + 'static>(self, signal: F) -> Interactable {
self.into_interactable(InteractableEvent::Click, signal)
}
fn on_hover<S: Signal, F: Fn() -> S + 'static>(self, signal: F) -> Interactable {
self.into_interactable(InteractableEvent::Hover, signal)
}
fn on_active<S: Signal, F: Fn() -> S + 'static>(self, signal: F) -> Interactable {
self.into_interactable(InteractableEvent::Active, signal)
}
}