Skip to content

Commit

Permalink
Stack event handlers (#304)
Browse files Browse the repository at this point in the history
* Stack event handlers

* Add back event handlers in ViewData

* make clippy happy
  • Loading branch information
jrmoulton authored Feb 7, 2024
1 parent 0de4e90 commit a035cfd
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 79 deletions.
133 changes: 74 additions & 59 deletions src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -495,16 +495,27 @@ impl AppState {
}
}

pub(crate) fn get_event_listener(
pub(crate) fn get_event_listeners(
&self,
id: Id,
listener: &EventListener,
) -> Option<&impl Fn(&Event) -> EventPropagation> {
) -> Option<&Vec<Box<EventCallback>>> {
self.view_states
.get(&id)
.and_then(|s| s.event_listeners.get(listener))
}

pub(crate) fn apply_event(
&self,
id: Id,
listener: &EventListener,
event: &crate::event::Event,
) -> Option<EventPropagation> {
self.view_states
.get(&id)
.and_then(|s| s.apply_event(listener, event))
}

pub(crate) fn focus_changed(&mut self, old: Option<Id>, new: Option<Id>) {
if let Some(id) = new {
// To apply the styles of the Focus selector
Expand All @@ -513,9 +524,9 @@ impl AppState {
{
self.request_style_recursive(id);
}
if let Some(action) = self.get_event_listener(id, &EventListener::FocusGained) {
(*action)(&Event::FocusGained);
}
self.view_states.get(&id).and_then(|state| {
state.apply_event(&EventListener::FocusGained, &Event::FocusGained)
});
}

if let Some(old_id) = old {
Expand All @@ -525,9 +536,9 @@ impl AppState {
{
self.request_style_recursive(old_id);
}
if let Some(action) = self.get_event_listener(old_id, &EventListener::FocusLost) {
(*action)(&Event::FocusLost);
}
self.view_states
.get(&old_id)
.and_then(|state| state.apply_event(&EventListener::FocusLost, &Event::FocusLost));
}
}
}
Expand Down Expand Up @@ -733,10 +744,7 @@ impl<'a> EventCx<'a> {
if rect.contains(pointer_event.pos) {
if self.app_state.is_dragging() {
self.app_state.dragging_over.insert(id);
if let Some(action) = self.get_event_listener(id, &EventListener::DragOver)
{
(*action)(&event);
}
self.apply_event(id, &EventListener::DragOver, &event);
} else {
self.app_state.hovered.insert(id);
let style = self.app_state.get_builtin_style(id);
Expand Down Expand Up @@ -775,18 +783,15 @@ impl<'a> EventCx<'a> {
released_at: None,
});
id.request_paint();
if let Some(action) =
self.get_event_listener(id, &EventListener::DragStart)
{
(*action)(&event);
}
self.apply_event(id, &EventListener::DragStart, &event);
}
}
}
if let Some(action) = self.get_event_listener(id, &EventListener::PointerMove) {
if (*action)(&event).is_processed() {
return EventPropagation::Stop;
}
if self
.apply_event(id, &EventListener::PointerMove, &event)
.is_some_and(|prop| prop.is_processed())
{
return EventPropagation::Stop;
}
}
Event::PointerUp(pointer_event) => {
Expand All @@ -798,21 +803,15 @@ impl<'a> EventCx<'a> {
if on_view {
if let Some(dragging) = self.app_state.dragging.as_mut() {
let dragging_id = dragging.id;
if let Some(action) =
self.get_event_listener(id, &EventListener::Drop)
if self
.apply_event(id, &EventListener::Drop, &event)
.is_some_and(|prop| prop.is_processed())
{
if (*action)(&event).is_processed() {
// if the drop is processed, we set dragging to none so that the animation
// for the dragged view back to its original position isn't played.
self.app_state.dragging = None;
id.request_paint();
if let Some(action) = self.get_event_listener(
dragging_id,
&EventListener::DragEnd,
) {
(*action)(&event);
}
}
// if the drop is processed, we set dragging to none so that the animation
// for the dragged view back to its original position isn't played.
self.app_state.dragging = None;
id.request_paint();
self.apply_event(dragging_id, &EventListener::DragEnd, &event);
}
}
}
Expand All @@ -822,52 +821,57 @@ impl<'a> EventCx<'a> {
let dragging_id = dragging.id;
dragging.released_at = Some(std::time::Instant::now());
id.request_paint();
if let Some(action) =
self.get_event_listener(dragging_id, &EventListener::DragEnd)
{
(*action)(&event);
}
self.apply_event(dragging_id, &EventListener::DragEnd, &event);
}

let last_pointer_down = self.app_state.view_state(id).last_pointer_down.take();
if let Some(action) = self.get_event_listener(id, &EventListener::DoubleClick) {
if let Some(handlers) =
self.get_event_listeners(id, &EventListener::DoubleClick)
{
if on_view
&& self.app_state.is_clicking(&id)
&& last_pointer_down
.as_ref()
.map(|e| e.count == 2)
.unwrap_or(false)
&& (*action)(&event).is_processed()
&& handlers.iter().fold(false, |handled, handler| {
handled | handler(&event).is_processed()
})
{
return EventPropagation::Stop;
}
}
if let Some(action) = self.get_event_listener(id, &EventListener::Click) {
if let Some(handlers) = self.get_event_listeners(id, &EventListener::Click) {
if on_view
&& self.app_state.is_clicking(&id)
&& last_pointer_down.is_some()
&& (*action)(&event).is_processed()
&& handlers.iter().fold(false, |handled, handler| {
handled | handler(&event).is_processed()
})
{
return EventPropagation::Stop;
}
}

if let Some(action) = self.get_event_listener(id, &EventListener::PointerUp) {
if (*action)(&event).is_processed() {
return EventPropagation::Stop;
}
if self
.apply_event(id, &EventListener::PointerUp, &event)
.is_some_and(|prop| prop.is_processed())
{
return EventPropagation::Stop;
}
} else if pointer_event.button.is_secondary() {
let rect = self.get_size(id).unwrap_or_default().to_rect();
let on_view = rect.contains(pointer_event.pos);

let last_pointer_down = self.app_state.view_state(id).last_pointer_down.take();
if let Some(action) =
self.get_event_listener(id, &EventListener::SecondaryClick)
if let Some(handlers) =
self.get_event_listeners(id, &EventListener::SecondaryClick)
{
if on_view
&& last_pointer_down.is_some()
&& (*action)(&event).is_processed()
&& handlers.iter().fold(false, |handled, handler| {
handled | handler(&event).is_processed()
})
{
return EventPropagation::Stop;
}
Expand All @@ -888,9 +892,7 @@ impl<'a> EventCx<'a> {
}
Event::KeyDown(_) => {
if self.app_state.is_focused(&id) && event.is_keyboard_trigger() {
if let Some(action) = self.get_event_listener(id, &EventListener::Click) {
(*action)(&event);
}
self.apply_event(id, &EventListener::Click, &event);
}
}
Event::WindowResized(_) => {
Expand All @@ -910,14 +912,18 @@ impl<'a> EventCx<'a> {
}

if let Some(listener) = event.listener() {
if let Some(action) = self.get_event_listener(id, &listener) {
if let Some(handlers) = self.get_event_listeners(id, &listener) {
let should_run = if let Some(pos) = event.point() {
let rect = self.get_size(id).unwrap_or_default().to_rect();
rect.contains(pos)
} else {
true
};
if should_run && (*action)(&event).is_processed() {
if should_run
&& handlers.iter().fold(false, |handled, handler| {
handled | handler(&event).is_processed()
})
{
return EventPropagation::Stop;
}
}
Expand All @@ -940,12 +946,21 @@ impl<'a> EventCx<'a> {
.unwrap_or(false)
}

pub(crate) fn get_event_listener(
pub(crate) fn get_event_listeners(
&self,
id: Id,
listener: &EventListener,
) -> Option<&Vec<Box<EventCallback>>> {
self.app_state.get_event_listeners(id, listener)
}

pub(crate) fn apply_event(
&self,
id: Id,
listener: &EventListener,
) -> Option<&impl Fn(&Event) -> EventPropagation> {
self.app_state.get_event_listener(id, listener)
event: &Event,
) -> Option<EventPropagation> {
self.app_state.apply_event(id, listener, event)
}

/// translate a window-positioned event to the local coordinate system of a view
Expand Down
23 changes: 22 additions & 1 deletion src/view_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use crate::{
LayoutProps, Outline, OutlineColor, Style, StyleClassRef, StyleSelectors,
},
view::Widget,
EventPropagation,
};
use bitflags::bitflags;
use kurbo::Rect;
Expand Down Expand Up @@ -151,7 +152,7 @@ pub struct ViewState {
pub(crate) dragging_style: Option<Style>,
pub(crate) combined_style: Style,
pub(crate) taffy_style: taffy::style::Style,
pub(crate) event_listeners: HashMap<EventListener, Box<EventCallback>>,
pub(crate) event_listeners: HashMap<EventListener, Vec<Box<EventCallback>>>,
pub(crate) context_menu: Option<Box<MenuCallback>>,
pub(crate) popout_menu: Option<Box<MenuCallback>>,
pub(crate) resize_listener: Option<ResizeListener>,
Expand Down Expand Up @@ -186,6 +187,26 @@ impl ViewState {
}
}

pub(crate) fn apply_event(
&self,
listener: &EventListener,
event: &crate::event::Event,
) -> Option<EventPropagation> {
let mut handled = false;
if let Some(handlers) = self.event_listeners.get(listener) {
for handler in handlers {
handled |= handler(event).is_processed();
}
} else {
return None;
}
if handled {
Some(EventPropagation::Stop)
} else {
Some(EventPropagation::Continue)
}
}

/// Returns `true` if a new frame is requested.
#[allow(clippy::too_many_arguments)]
pub(crate) fn compute_style(
Expand Down
9 changes: 5 additions & 4 deletions src/views/scroll.rs
Original file line number Diff line number Diff line change
Expand Up @@ -857,10 +857,11 @@ impl Widget for Scroll {

if let Event::PointerWheel(pointer_event) = &event {
if let Some(listener) = event.listener() {
if let Some(action) = cx.get_event_listener(self.id(), &listener) {
if (*action)(&event).is_processed() {
return EventPropagation::Stop;
}
if cx
.apply_event(self.id(), &listener, &event)
.is_some_and(|prop| prop.is_processed())
{
return EventPropagation::Stop;
}
}
let delta = pointer_event.delta;
Expand Down
28 changes: 13 additions & 15 deletions src/window_handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,13 +205,11 @@ impl WindowHandle {
break;
}
}

if let Some(listener) = event.listener() {
if let Some(action) =
cx.get_event_listener(self.view.main.view_data().id(), &listener)
{
processed |= (*action)(&event).is_processed();
}
processed |= cx
.app_state
.apply_event(self.view.main.view_data().id(), &listener, &event)
.is_some_and(|prop| prop.is_processed());
}
}

Expand Down Expand Up @@ -296,9 +294,7 @@ impl WindowHandle {
cx.app_state.request_style_recursive(*id);
}
if hovered.contains(id) {
if let Some(action) = cx.get_event_listener(*id, &EventListener::PointerEnter) {
(*action)(&event);
}
cx.apply_event(*id, &EventListener::PointerEnter, &event);
} else {
let id_path = ID_PATHS.with(|paths| paths.borrow().get(id).cloned());
if let Some(id_path) = id_path {
Expand All @@ -316,11 +312,9 @@ impl WindowHandle {
.symmetric_difference(dragging_over)
{
if dragging_over.contains(id) {
if let Some(action) = cx.get_event_listener(*id, &EventListener::DragEnter) {
(*action)(&event);
}
} else if let Some(action) = cx.get_event_listener(*id, &EventListener::DragLeave) {
(*action)(&event);
cx.apply_event(*id, &EventListener::DragEnter, &event);
} else {
cx.apply_event(*id, &EventListener::DragLeave, &event);
}
}
}
Expand Down Expand Up @@ -876,7 +870,11 @@ impl WindowHandle {
action,
} => {
let state = cx.app_state.view_state(id);
state.event_listeners.insert(listener, action);

state
.event_listeners
.entry(listener)
.or_insert(vec![action]);
}
UpdateMessage::ResizeListener { id, action } => {
let state = cx.app_state.view_state(id);
Expand Down

0 comments on commit a035cfd

Please sign in to comment.