use crate::core::{Action, CursorStyle, EntityMap, FocusHandle, HitboxId, Modifiers, MouseButton, Point};
use crate::view::drag::{DragSource, DropTarget};
use crate::view::key_context::KeyContext;
use crate::view::style::Style;
use std::any::TypeId;
use std::cell::Cell;
use std::rc::Rc;
#[derive(Clone, Copy, Debug)]
pub struct ClickEvent {
pub position: Point,
pub button: MouseButton,
pub modifiers: Modifiers,
pub click_count: u32,
}
#[derive(Clone, Copy, Debug)]
pub struct ScrollEvent {
pub position: Point,
pub delta: Point,
}
#[derive(Clone, Copy, Debug)]
pub struct DragEvent {
pub position: Point,
pub modifiers: Modifiers,
}
#[derive(Clone, Debug)]
pub struct KeyDownEvent {
pub keystroke: String,
pub text: Option<String>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum TextInputEvent {
Insert(String),
Preedit {
text: String,
cursor: Option<usize>,
},
}
#[derive(Clone, Copy, Debug)]
pub struct ActionEvent {
pub action: Action,
}
#[derive(Default)]
pub struct Effects {
pub(crate) redraw: bool,
pub(crate) actions: Vec<Action>,
pub(crate) propagate: bool,
pub(crate) pointer: Option<Point>,
}
impl Effects {
pub fn notify(&mut self) {
self.redraw = true;
}
pub fn dispatch_action(&mut self, action: Action) {
self.actions.push(action);
}
pub fn propagate(&mut self) {
self.propagate = true;
}
}
pub struct EventContext<'a> {
pub entities: &'a mut EntityMap,
pub effects: Effects,
}
impl EventContext<'_> {
pub fn pointer(&self) -> Option<Point> {
self.effects.pointer
}
pub fn notify(&mut self) {
self.effects.notify();
}
pub fn dispatch_action(&mut self, action: Action) {
self.effects.dispatch_action(action);
}
pub fn propagate(&mut self) {
self.effects.propagate();
}
pub fn focus(&mut self, handle: &FocusHandle) {
self.entities.focus.focus(handle);
self.effects.notify();
}
}
pub type Handler<E> = Rc<dyn Fn(&E, &mut EventContext)>;
pub type ClickHandler = Handler<ClickEvent>;
pub type ScrollHandler = Handler<ScrollEvent>;
pub type DragHandler = Handler<DragEvent>;
pub type KeyDownHandler = Handler<KeyDownEvent>;
pub type TextInputHandler = Handler<TextInputEvent>;
pub type ActionHandler = Handler<ActionEvent>;
#[derive(Default)]
pub struct Interactivity {
pub id: Option<String>,
pub hover_style: Option<Style>,
pub active_style: Option<Style>,
pub focus_style: Option<Style>,
pub variants: Vec<(crate::view::variants::Variant, Style)>,
pub on_click: Option<ClickHandler>,
pub on_mouse_down: Option<ClickHandler>,
pub on_right_click: Option<ClickHandler>,
pub on_scroll: Option<ScrollHandler>,
pub on_drag: Option<DragHandler>,
pub on_mouse_move: Option<DragHandler>,
pub blocks_mouse: bool,
pub record_bounds: Option<Rc<Cell<crate::core::Bounds>>>,
pub tooltip: Option<Rc<str>>,
pub focus: Option<FocusHandle>,
pub tab_index: Option<i32>,
pub key_context: Option<KeyContext>,
pub actions: Vec<(Action, ActionHandler)>,
pub on_key_down: Option<KeyDownHandler>,
pub on_text_input: Option<TextInputHandler>,
pub cursor: Option<CursorStyle>,
pub group: Option<Rc<str>>,
pub group_hover: Option<(Rc<str>, Style)>,
pub drag: Option<DragSource>,
pub drops: Vec<DropTarget>,
pub drag_over: Vec<(TypeId, Style)>,
pub role: Option<accesskit::Role>,
pub label: Option<Rc<str>>,
}
impl Interactivity {
pub fn needs_hitbox(&self, scrollable: bool) -> bool {
self.hover_style.is_some()
|| self.active_style.is_some()
|| self.on_click.is_some()
|| self.on_mouse_down.is_some()
|| self.on_right_click.is_some()
|| self.on_scroll.is_some()
|| self.on_drag.is_some()
|| self.on_mouse_move.is_some()
|| self.focus.is_some()
|| self.blocks_mouse
|| self.tooltip.is_some()
|| self.cursor.is_some()
|| self.group.is_some()
|| self.drag.is_some()
|| !self.drops.is_empty()
|| !self.drag_over.is_empty()
|| scrollable
}
pub fn needs_dispatch_node(&self) -> bool {
self.focus.is_some()
|| self.key_context.is_some()
|| !self.actions.is_empty()
|| self.on_key_down.is_some()
|| self.on_text_input.is_some()
}
}
#[derive(Clone)]
pub struct HandlerEntry {
pub hitbox: HitboxId,
pub on_click: Option<ClickHandler>,
pub on_mouse_down: Option<ClickHandler>,
pub on_right_click: Option<ClickHandler>,
pub on_scroll: Option<ScrollHandler>,
pub on_drag: Option<DragHandler>,
pub on_mouse_move: Option<DragHandler>,
pub scroll: Option<SharedScroll>,
pub focus: Option<FocusHandle>,
pub tooltip: Option<Rc<str>>,
pub cursor: Option<CursorStyle>,
pub drag: Option<DragSource>,
pub drops: Vec<DropTarget>,
}
impl HandlerEntry {
pub fn new(hitbox: HitboxId) -> Self {
Self {
hitbox,
on_click: None,
on_mouse_down: None,
on_right_click: None,
on_scroll: None,
on_drag: None,
on_mouse_move: None,
scroll: None,
focus: None,
tooltip: None,
cursor: None,
drag: None,
drops: Vec::new(),
}
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct ScrollState {
pub offset_y: f32,
pub max_y: f32,
pub viewport_y: f32,
pub grab: Option<f32>,
}
pub type SharedScroll = Rc<Cell<ScrollState>>;
impl ScrollState {
pub fn scroll_by(shared: &SharedScroll, delta_y: f32) -> bool {
let mut state = shared.get();
let next = (state.offset_y - delta_y).clamp(0.0, state.max_y);
let changed = next != state.offset_y;
state.offset_y = next;
shared.set(state);
changed
}
}