Skip to content

Commit

Permalink
event pass through
Browse files Browse the repository at this point in the history
  • Loading branch information
jrmoulton committed Nov 5, 2024
1 parent efc1e2b commit ce49cdb
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 103 deletions.
2 changes: 1 addition & 1 deletion src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ impl<'a> EventCx<'a> {
{
return EventPropagation::Stop;
}
if event.is_pointer() {
if event.is_pointer() && !child.state().borrow().pointer_events_pass_through {
break;
}
}
Expand Down
6 changes: 6 additions & 0 deletions src/id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,12 @@ impl ViewId {
.remove(&event);
}

/// Set whether pointer events should be passes to *siblings* that are underneath.
/// Parent/child event relationships are handled by event propagation.
pub fn pointer_events_pass_through(&self, pass_through: bool) {
self.state().borrow_mut().pointer_events_pass_through = pass_through;
}

/// Mark this view as a view that can be dragged
///
/// You can customize the apearance of a view while dragging in the style
Expand Down
215 changes: 113 additions & 102 deletions src/inspector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -797,129 +797,140 @@ fn capture_view(
)
})
.unwrap_or_default();
let size = capture_.window_size;

let contain_ids = create_rw_signal((0, Vec::<ViewId>::new()));

let image = img_dynamic(move || window.clone().unwrap())
.style(move |s| {
s.margin(5.0)
.border(1.)
.border_color(Color::BLACK.multiply_alpha(0.5))
.width(image_width + 2.0)
.height(image_height + 2.0)
.margin_bottom(21.0)
.margin_right(21.0)
})
.keyboard_navigable()
.on_event_stop(EventListener::KeyUp, {
move |event: &Event| {
if let Event::KeyUp(key) = event {
match key.key.logical_key {
keyboard::Key::Named(NamedKey::ArrowUp) => {
let id = contain_ids.try_update(|(match_index, ids)| {
if !ids.is_empty() {
if *match_index == 0 {
*match_index = ids.len() - 1;
} else {
*match_index -= 1;
}
let image = if let Some(window) = window {
img_dynamic(move || window.clone()).into_any()
} else {
empty()
.style(move |s| s.min_width(size.width).min_height(size.height))
.into_any()
}
.style(move |s| {
s.margin(5.0)
.border(1.)
.border_color(Color::BLACK.multiply_alpha(0.5))
.width(image_width + 2.0)
.height(image_height + 2.0)
.margin_bottom(21.0)
.margin_right(21.0)
})
.keyboard_navigable()
.on_event_stop(EventListener::KeyUp, {
move |event: &Event| {
if let Event::KeyUp(key) = event {
match key.key.logical_key {
keyboard::Key::Named(NamedKey::ArrowUp) => {
let id = contain_ids.try_update(|(match_index, ids)| {
if !ids.is_empty() {
if *match_index == 0 {
*match_index = ids.len() - 1;
} else {
*match_index -= 1;
}
ids.get(*match_index).copied()
});
if let Some(Some(id)) = id {
update_select_view_id(id, &capture_view, false);
}
ids.get(*match_index).copied()
});
if let Some(Some(id)) = id {
update_select_view_id(id, &capture_view, false);
}
keyboard::Key::Named(NamedKey::ArrowDown) => {
let id = contain_ids.try_update(|(match_index, ids)| {
if !ids.is_empty() {
*match_index = (*match_index + 1) % ids.len();
}
ids.get(*match_index).copied()
});
if let Some(Some(id)) = id {
update_select_view_id(id, &capture_view, false);
}
keyboard::Key::Named(NamedKey::ArrowDown) => {
let id = contain_ids.try_update(|(match_index, ids)| {
if !ids.is_empty() {
*match_index = (*match_index + 1) % ids.len();
}
ids.get(*match_index).copied()
});
if let Some(Some(id)) = id {
update_select_view_id(id, &capture_view, false);
}
_ => {}
}
_ => {}
}
}
})
.on_event_stop(EventListener::PointerUp, {
let capture_ = capture_.clone();
move |event: &Event| {
if let Event::PointerUp(e) = event {
let find_ids = capture_.root.find_all_by_pos(e.pos);
if !find_ids.is_empty() {
let first = contain_ids.try_update(|(index, ids)| {
*index = 0;
let _ = std::mem::replace(ids, find_ids);
ids.first().copied()
});
if let Some(Some(id)) = first {
update_select_view_id(id, &capture_view, false);
}
}
})
.on_event_stop(EventListener::PointerUp, {
let capture_ = capture_.clone();
move |event: &Event| {
if let Event::PointerUp(e) = event {
let find_ids = capture_.root.find_all_by_pos(e.pos);
if !find_ids.is_empty() {
let first = contain_ids.try_update(|(index, ids)| {
*index = 0;
let _ = std::mem::replace(ids, find_ids);
ids.first().copied()
});
if let Some(Some(id)) = first {
update_select_view_id(id, &capture_view, false);
}
}
}
})
.on_event_stop(EventListener::PointerMove, {
move |event: &Event| {
if let Event::PointerMove(e) = event {
if let Some(view) = capture_.root.find_by_pos(e.pos) {
if capture_view.highlighted.get() != Some(view.id) {
capture_view.highlighted.set(Some(view.id));
}
} else {
capture_view.highlighted.set(None);
}
})
.on_event_stop(EventListener::PointerMove, {
move |event: &Event| {
if let Event::PointerMove(e) = event {
if let Some(view) = capture_.root.find_by_pos(e.pos) {
if capture_view.highlighted.get() != Some(view.id) {
capture_view.highlighted.set(Some(view.id));
}
} else {
capture_view.highlighted.set(None);
}
}
})
.on_event_cont(EventListener::PointerLeave, move |_| {
capture_view.highlighted.set(None)
});

let capture_ = capture.clone();
let selected_overlay = empty().style(move |s| {
if let Some(view) = capture_view
.selected
.get()
.and_then(|id| capture_.root.find(id))
{
s.absolute()
.margin_left(5.0 + view.layout.x0)
.margin_top(5.0 + view.layout.y0)
.width(view.layout.width())
.height(view.layout.height())
.background(Color::rgb8(186, 180, 216).multiply_alpha(0.5))
.border_color(Color::rgb8(186, 180, 216).multiply_alpha(0.7))
.border(1.)
} else {
s
}
})
.on_event_cont(EventListener::PointerLeave, move |_| {
capture_view.highlighted.set(None)
});

let capture_ = capture.clone();
let highlighted_overlay = empty().style(move |s| {
if let Some(view) = capture_view
.highlighted
.get()
.and_then(|id| capture_.root.find(id))
{
s.absolute()
.margin_left(5.0 + view.layout.x0)
.margin_top(5.0 + view.layout.y0)
.width(view.layout.width())
.height(view.layout.height())
.background(Color::rgba8(228, 237, 216, 120))
.border_color(Color::rgba8(75, 87, 53, 120))
.border(1.)
} else {
s
}
});
let selected_overlay = empty()
.style(move |s| {
if let Some(view) = capture_view
.selected
.get()
.and_then(|id| capture_.root.find(id))
{
s.absolute()
.margin_left(5.0 + view.layout.x0)
.margin_top(5.0 + view.layout.y0)
.width(view.layout.width())
.height(view.layout.height())
.background(Color::rgb8(186, 180, 216).multiply_alpha(0.5))
.border_color(Color::rgb8(186, 180, 216).multiply_alpha(0.7))
.border(1.)
} else {
s
}
})
.pointer_events_pass_through(|| true);

let capture_ = capture.clone();
let highlighted_overlay = empty()
.style(move |s| {
if let Some(view) = capture_view
.highlighted
.get()
.and_then(|id| capture_.root.find(id))
{
s.absolute()
.margin_left(5.0 + view.layout.x0)
.margin_top(5.0 + view.layout.y0)
.width(view.layout.width())
.height(view.layout.height())
.background(Color::rgba8(228, 237, 216, 120))
.border_color(Color::rgba8(75, 87, 53, 120))
.border(1.)
} else {
s
}
})
.pointer_events_pass_through(|| true);

let image = stack((image, selected_overlay, highlighted_overlay));

Expand Down
3 changes: 3 additions & 0 deletions src/view_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,8 @@ pub struct ViewState {
pub(crate) is_hidden_state: IsHiddenState,
pub(crate) num_waiting_animations: u16,
pub(crate) disable_default_events: HashSet<EventListener>,
// whether pointer events should be passes to *siblings* that are underneath. parent/child event relationships are handled by event propagation.
pub(crate) pointer_events_pass_through: bool,
pub(crate) transform: Affine,
pub(crate) debug_name: SmallVec<[String; 1]>,
}
Expand Down Expand Up @@ -206,6 +208,7 @@ impl ViewState {
is_hidden_state: IsHiddenState::None,
num_waiting_animations: 0,
disable_default_events: HashSet::new(),
pointer_events_pass_through: false,
transform: Affine::IDENTITY,
debug_name: Default::default(),
}
Expand Down
15 changes: 15 additions & 0 deletions src/views/decorator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,21 @@ pub trait Decorators: IntoView<V = Self::DV> + Sized {
view
}

/// Dynamically set whether pointer events should be passes to **siblings** that are underneath.
/// Parent/child event relationships are handled by event propagation.
///
/// # Reactivity
/// This function is reactive and will re-run the function automatically in response to changes in signals
fn pointer_events_pass_through(self, pass_through: impl Fn() -> bool + 'static) -> Self::DV {
let view = self.into_view();
let id = view.id();
create_effect(move |_| {
let pass_through = pass_through();
id.pointer_events_pass_through(pass_through);
});
view
}

/// Mark the view as draggable
fn draggable(self) -> Self::DV {
let view = self.into_view();
Expand Down

0 comments on commit ce49cdb

Please sign in to comment.