Skip to content
Snippets Groups Projects
Commit d64cb0e5 authored by MrGVSV's avatar MrGVSV
Browse files

Re-implemented event system

Re-added event propagation and proper event targeting
parent a068b4ab
No related branches found
No related tags found
No related merge requests found
This diff is collapsed.
/// Controls how the cursor interacts on a given node
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum PointerEvents {
/// Allow all pointer events on this node and its children
All,
/// Allow pointer events on this node but not on its children
SelfOnly,
/// Allow pointer events on this node's children but not on itself
ChildrenOnly,
/// Disallow all pointer events on this node and its children
None,
}
impl Default for PointerEvents {
fn default() -> Self {
Self::All
}
}
\ No newline at end of file
......@@ -2,8 +2,13 @@ use crate::{Index, KeyCode};
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Event {
/// The node targeted by this event
pub target: Index,
/// The current target of this event
pub current_target: Index,
/// The type of event
pub event_type: EventType,
/// Indicates whether this event should propagate or not
pub(crate) should_propagate: bool,
}
......@@ -11,12 +16,38 @@ impl Default for Event {
fn default() -> Self {
Self {
target: Default::default(),
current_target: Default::default(),
event_type: EventType::Click,
should_propagate: true,
}
}
}
impl Event {
/// Create a new event
///
/// This is the preferred method for creating an event as it automatically sets up
/// propagation and other event metadata in a standardized manner
pub fn new(target: Index, event_type: EventType) -> Self {
Self {
target,
current_target: target,
event_type,
should_propagate: event_type.propagates(),
}
}
/// Returns whether this event is currently set to propagate
pub fn propagates(&self) -> bool {
self.should_propagate
}
/// If called, prevents this event from propagating up the hierarchy
pub fn stop_propagation(&mut self) {
self.should_propagate = false;
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum EventType {
Click,
......@@ -25,9 +56,57 @@ pub enum EventType {
MouseOut,
MouseDown,
MouseUp,
MousePressed,
Focus,
Blur,
CharInput { c: char },
KeyboardInput { key: KeyCode },
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum EventCategory {
Mouse,
Keyboard,
Focus,
}
impl EventType {
/// Returns whether this event type should propagate by default
///
/// For more details on what should and shouldn't propagate, check out the
/// [W3 specifications](https://www.w3.org/TR/uievents/#event-types), upon which this is based.
pub fn propagates(&self) -> bool {
match self {
// Propagates
Self::Hover => true,
Self::Click => true,
Self::MouseDown => true,
Self::MouseUp => true,
Self::CharInput { .. } => true,
Self::KeyboardInput { .. } => true,
// Doesn't Propagate
Self::MouseIn => false,
Self::MouseOut => false,
Self::Focus => false,
Self::Blur => false,
}
}
/// Get the category of this event
pub fn event_category(&self) -> EventCategory {
match self {
// Mouse
Self::Hover => EventCategory::Mouse,
Self::Click => EventCategory::Mouse,
Self::MouseDown => EventCategory::Mouse,
Self::MouseUp => EventCategory::Mouse,
Self::MouseIn => EventCategory::Mouse,
Self::MouseOut => EventCategory::Mouse,
// Keyboard
Self::CharInput { .. } => EventCategory::Keyboard,
Self::KeyboardInput { .. } => EventCategory::Keyboard,
// Focus
Self::Focus => EventCategory::Focus,
Self::Blur => EventCategory::Focus,
}
}
}
\ No newline at end of file
use crate::KeyCode;
#[derive(Debug)]
pub enum InputEvent {
MouseMoved((f32, f32)),
MouseLeftPress,
......@@ -7,3 +8,23 @@ pub enum InputEvent {
CharEvent { c: char },
Keyboard { key: KeyCode },
}
pub enum InputEventCategory {
Mouse,
Keyboard,
// TODO: Gamepad, etc.
}
impl InputEvent {
pub fn category(&self) -> InputEventCategory {
match self {
// Mouse events
Self::MouseMoved(..) => InputEventCategory::Mouse,
Self::MouseLeftPress => InputEventCategory::Mouse,
Self::MouseLeftRelease => InputEventCategory::Mouse,
// Keyboard events
Self::CharEvent {..} => InputEventCategory::Keyboard,
Self::Keyboard {..} => InputEventCategory::Keyboard,
}
}
}
\ No newline at end of file
......@@ -16,10 +16,12 @@ pub mod tree;
mod vec;
pub mod widget;
pub mod widget_manager;
mod cursor;
use std::sync::{Arc, RwLock};
pub use binding::*;
pub use cursor::PointerEvents;
pub use color::Color;
pub use context::*;
pub use event::*;
......
pub use morphorm::{LayoutType, PositionType, Units};
use crate::{color::Color, render_command::RenderCommand};
use crate::cursor::PointerEvents;
#[derive(Debug, Clone, PartialEq)]
pub enum StyleProp<T: Default + Clone> {
......@@ -57,6 +58,7 @@ pub struct Style {
pub min_height: StyleProp<Units>,
pub max_width: StyleProp<Units>,
pub max_height: StyleProp<Units>,
pub pointer_events: StyleProp<PointerEvents>
}
impl Default for Style {
......@@ -86,6 +88,7 @@ impl Default for Style {
min_height: StyleProp::Default,
max_width: StyleProp::Default,
max_height: StyleProp::Default,
pointer_events: StyleProp::Default,
}
}
}
......
......@@ -12,6 +12,7 @@ use crate::{
tree::Tree,
Arena, Index, Widget,
};
use crate::cursor::PointerEvents;
// use as_any::Downcast;
#[derive(Debug)]
......@@ -384,4 +385,94 @@ impl WidgetManager {
}
None
}
/// Get the nodes under the given position
///
/// The first element in the returned tuple is the node deemed as the "best match". A node is a best match when
/// it is the deepest matching node in the hierarchy or it has a higher z-index than the other matches.
///
/// # Arguments
///
/// * `position`: The 2D point to check under
/// * `parent`: The parent to start at (or `None` for the root element)
///
pub fn get_nodes_under(&self, position: (f32, f32), parent: Option<Index>) -> Option<(Index, Vec<Index>)> {
// TODO: Find a more efficient way of finding a node at a given point
// The main issue with recursively checking if a node contains the point is that we cannot be sure
// that a child of a node will actually contain the point despite the point being contained within
// the parent (this is due to things like self-directed positioning).
// If we could transform a point into a widget's local space, then we could maybe go with the
// iterator approach and just transform the position/node at each point. However, we don't currently
// store the node's transform, making it both difficult and tedious to perform the conversion. We
// would have to track the current transformation offset at every level of recursion.
// Even still, we would need to check every child anyways since a node further down the tree might
// actually be positioned in the spot we're looking for. That kind of iteration skips a node like
// that completely.
// So for now, we'll just check every single node in the tree to find the matching one.
let mut nodes = Vec::default();
let mut z_index = f32::NEG_INFINITY;
let mut best_match: Option<Index> = None;
let mut best_nest_level = 0;
let parent = if let Some(parent) = parent {
parent
} else {
self.node_tree.root_node?
};
let mut stack = vec![(parent, 0)];
while stack.len() > 0 {
let (parent, nest_level) = stack.pop().unwrap();
let mut pointer_events = PointerEvents::default();
if let Some(widget) = self.current_widgets.get(parent).unwrap() {
if let Some(styles) = widget.get_styles() {
pointer_events = styles.pointer_events.resolve();
}
}
if matches!(pointer_events, PointerEvents::None) {
continue;
}
if !matches!(pointer_events, PointerEvents::SelfOnly) {
if let Some(children) = self.node_tree.children.get(&parent) {
for child in children {
stack.push((*child, nest_level + 1));
}
}
if matches!(pointer_events, PointerEvents::ChildrenOnly) {
continue;
}
}
if let Some(rect) = self.layout_cache.rect.get(&parent) {
if rect.contains(&position) {
nodes.push(parent);
if nest_level >= best_nest_level || rect.z_index > z_index {
best_match = Some(parent);
z_index = rect.z_index;
if nest_level > best_nest_level {
best_nest_level = nest_level;
}
}
}
}
}
if let Some(best_match) = best_match {
Some((best_match, nodes))
} else {
None
}
}
/// Get the "best match" node at the given position
#[allow(dead_code)]
pub fn get_node_at(&self, position: (f32, f32), parent: Option<Index>) -> Option<Index> {
Some(self.get_nodes_under(position, parent)?.0)
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment