Skip to content

Commit

Permalink
fix: Order colateral events by the DOM height of their Nodes
Browse files Browse the repository at this point in the history
  • Loading branch information
marc2332 committed Feb 15, 2024
1 parent bebe6e3 commit b7079c2
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 52 deletions.
25 changes: 16 additions & 9 deletions crates/core/src/events/dom_events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub struct DomEvent {
pub node_id: NodeId,
pub element_id: ElementId,
pub data: DomEventData,
pub bubbles: bool,
}

impl Eq for DomEvent {}
Expand All @@ -43,15 +44,6 @@ impl Ord for DomEvent {
}

impl DomEvent {
pub fn does_move_cursor(&self) -> bool {
return does_event_move_cursor(self.name.as_str());
}

// Bubble all events except keyboard
pub fn should_bubble(&self) -> bool {
!matches!(self.data, DomEventData::Keyboard(_))
}

pub fn new(
node_id: NodeId,
element_id: ElementId,
Expand All @@ -62,6 +54,8 @@ impl DomEvent {
let is_pointer_event = event.is_pointer_event();
let event_name = event.get_name().to_string();

let bubbles = event.does_bubble();

match event {
FreyaEvent::Mouse { cursor, button, .. } => {
let screen_coordinates = *cursor / scale_factor;
Expand Down Expand Up @@ -91,13 +85,15 @@ impl DomEvent {
element_id,
name: event_name,
data: event_data,
bubbles,
}
}
FreyaEvent::Wheel { scroll, .. } => Self {
node_id,
element_id,
name: event_name,
data: DomEventData::Wheel(WheelData::new(scroll.x, scroll.y)),
bubbles,
},
FreyaEvent::Keyboard {
ref key,
Expand All @@ -109,6 +105,7 @@ impl DomEvent {
element_id,
name: event_name,
data: DomEventData::Keyboard(KeyboardData::new(key.clone(), *code, *modifiers)),
bubbles,
},
FreyaEvent::Touch {
location,
Expand Down Expand Up @@ -145,10 +142,20 @@ impl DomEvent {
element_id,
name: event_name,
data: event_data,
bubbles,
}
}
}
}

pub fn does_move_cursor(&self) -> bool {
return does_event_move_cursor(self.name.as_str());
}

// Check if this even can change the hover state of an Element.
pub fn can_change_element_hover_state(&self) -> bool {
["mouseover", "mousenter", "pointerover", "pointerenter"].contains(&self.name.as_str())
}
}

/// Data of a DOM event.
Expand Down
41 changes: 17 additions & 24 deletions crates/core/src/events/elements_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,35 +48,28 @@ impl ElementsState {
true
});

// All these events will mark the node as being hovered
// "mouseover" "mouseenter" "pointerover" "pointerenter"

// We clone this here so events emitted in the same batch that mark an element as hovered will not affect the other events
// We clone this here so events emitted in the same batch that mark an element
// as hovered will not affect the other events
let hovered_elements = self.hovered_elements.clone();

// Emit valid events
// Emit new colateral events
for event in events_to_emit {
let id = &event.node_id;
let should_trigger = if event.can_change_element_hover_state() {
let is_hovered = hovered_elements.contains(&event.node_id);

let should_trigger = match event.name.as_str() {
name @ "mouseover"
| name @ "mouseenter"
| name @ "pointerover"
| name @ "pointerenter" => {
let is_hovered = hovered_elements.contains(id);

if !is_hovered {
self.hovered_elements.insert(*id);
}

if name == "mouseenter" || name == "pointerenter" {
// If the event is already being hovered then it's pointless to trigger the movement event
!is_hovered
} else {
true
}
// Mark the Node as hovered if it wasn't already
if !is_hovered {
self.hovered_elements.insert(event.node_id);
}
_ => true,

if event.name == "mouseenter" || event.name == "pointerenter" {
// If the Node was already hovered, we don't need to emit an `enter` event again.
!is_hovered
} else {
true
}
} else {
true
};

if should_trigger {
Expand Down
30 changes: 20 additions & 10 deletions crates/core/src/events/events_measurer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,26 +31,36 @@ pub fn process_events(
let dom_events = measure_dom_events(potential_events, dom, scale_factor);

// 4. Filter the dom events and get potential derived events, e.g mouseover -> mouseenter
let (potential_colateral_events, mut to_emit_dom_events) =
let (mut potential_colateral_events, mut to_emit_dom_events) =
elements_state.process_events(&dom_events, events);

// 5. Get what derived events can actually be emitted
// 5. Order potential colateral events by their Nodes height in the DOM
for events in potential_colateral_events.values_mut() {
let rdom = dom.rdom();
events.sort_by(|(l, _), (r, _)| {
let height_l = rdom.tree_ref().height(*l);
let height_r = rdom.tree_ref().height(*r);
height_l.cmp(&height_r)
})
}

// 6. Get what derived events can actually be emitted
let to_emit_dom_colateral_events =
measure_dom_events(potential_colateral_events, dom, scale_factor);

// 6. Join both the dom and colateral dom events and sort them
// 7. Join both the dom and colateral dom events and sort them
to_emit_dom_events.extend(to_emit_dom_colateral_events);
to_emit_dom_events.sort_unstable();

// 7. Emit the DOM events
// 8. Emit the DOM events
for event in to_emit_dom_events {
event_emitter.send(event).unwrap();
}

// 8. Emit the global events
// 9. Emit the global events
emit_global_events_listeners(global_events, dom, event_emitter, scale_factor);

// 9. Clear the events queue
// 10. Clear the events queue
events.clear();
}

Expand Down Expand Up @@ -220,17 +230,17 @@ fn measure_dom_events(
valid_event.set_name(derivated_event_name.to_string());
found_nodes.push((node_id, valid_event));

// Only stop looking for valid nodes when the event isn't of type keyboard
if !event.is_keyboard_event() {
continue 'event;
// Stop looking for valid nodes when the event bubbles up
if event.does_bubble() {
break 'event;
}
}
}
}

let Style { background, .. } = &*node.get::<Style>().unwrap();

if background != &Fill::Color(Color::TRANSPARENT) && !event.is_keyboard_event() {
if background != &Fill::Color(Color::TRANSPARENT) && !event.does_bubble() {
// If the background isn't transparent,
// we must make sure that next nodes are parent of it
// This only matters for pointer-based events, and not to e.g keyboard events
Expand Down
8 changes: 6 additions & 2 deletions crates/core/src/events/freya_events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,11 @@ impl FreyaEvent {
self.get_name().starts_with("point")
}

pub fn is_keyboard_event(&self) -> bool {
matches!(self, Self::Keyboard { .. })
// Bubble all events except:
// - Keyboard events
// - Mouseleave events
pub fn does_bubble(&self) -> bool {
!(matches!(self, Self::Keyboard { .. })
|| ["mouseleave", "pointerleave"].contains(&self.get_name()))
}
}
3 changes: 1 addition & 2 deletions crates/renderer/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,9 +168,8 @@ impl<State: 'static + Clone> App<State> {
select! {
ev = self.event_receiver.recv() => {
if let Some(ev) = ev {
let bubble = ev.should_bubble();
let data = ev.data.any();
self.vdom.handle_event(&ev.name, data, ev.element_id, bubble);
self.vdom.handle_event(&ev.name, data, ev.element_id, ev.bubbles);

self.vdom.process_events();
}
Expand Down
10 changes: 5 additions & 5 deletions examples/counter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@ fn app() -> Element {
direction: "horizontal",
Button {
onclick: move |_| count += 1,
label { "Increase" }
}
Button {
onclick: move |_| count -= 1,
label { "Decrease" }
Button {
onclick: move |_| count -= 1,
label { "Decrease" }
}
}

}
)
}

0 comments on commit b7079c2

Please sign in to comment.