Skip to content

Commit

Permalink
feat: Add mousedown and mouseup event (#172)
Browse files Browse the repository at this point in the history
* feat: Add mousedown and mouseup event

* feat: Triggering :active pseudo-class

* fix: Triggering :active pseudo-class
  • Loading branch information
luoxiaozero authored Dec 30, 2024
1 parent 72978dd commit 86cca2f
Show file tree
Hide file tree
Showing 6 changed files with 164 additions and 4 deletions.
41 changes: 41 additions & 0 deletions packages/blitz-dom/src/document.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,8 @@ pub struct Document {
pub(crate) hover_node_id: Option<usize>,
/// The node which is currently focussed (if any)
pub(crate) focus_node_id: Option<usize>,
/// The node which is currently active (if any)
pub(crate) active_node_id: Option<usize>,

pub changed: HashSet<usize>,

Expand Down Expand Up @@ -177,6 +179,7 @@ impl DocumentLike for Document {
let target_node_id = event.target;

match event.data {
EventData::MouseDown { .. } | EventData::MouseUp { .. } => {}
EventData::Click { x, y, .. } => {
let hit = self.hit(x, y);
if let Some(hit) = hit {
Expand Down Expand Up @@ -347,6 +350,7 @@ impl Document {

hover_node_id: None,
focus_node_id: None,
active_node_id: None,
changed: HashSet::new(),
net_provider: Arc::new(DummyNetProvider::default()),
navigation_provider: Arc::new(DummyNavigationProvider {}),
Expand Down Expand Up @@ -987,6 +991,43 @@ impl Document {
true
}

pub fn active_node(&mut self) -> bool {
let Some(hover_node_id) = self.get_hover_node_id() else {
return false;
};

if let Some(active_node_id) = self.active_node_id {
if active_node_id == hover_node_id {
return true;
}
self.unactive_node();
}

let active_node_id = Some(hover_node_id);

let node_path = self.maybe_node_layout_ancestors(active_node_id);
for &id in node_path.iter() {
self.snapshot_node_and(id, |node| node.active());
}

self.active_node_id = active_node_id;

true
}

pub fn unactive_node(&mut self) -> bool {
let Some(active_node_id) = self.active_node_id.take() else {
return false;
};

let node_path = self.maybe_node_layout_ancestors(Some(active_node_id));
for &id in node_path.iter() {
self.snapshot_node_and(id, |node| node.unactive());
}

true
}

pub fn set_hover_to(&mut self, x: f32, y: f32) -> bool {
let hit = self.hit(x, y);
let hover_node_id = hit.map(|hit| hit.node_id);
Expand Down
4 changes: 4 additions & 0 deletions packages/blitz-dom/src/events/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ impl RendererEvent {

#[derive(Debug, Clone)]
pub enum EventData {
MouseDown { x: f32, y: f32, mods: Modifiers },
MouseUp { x: f32, y: f32, mods: Modifiers },
Click { x: f32, y: f32, mods: Modifiers },
KeyPress { event: KeyEvent, mods: Modifiers },
Ime(Ime),
Expand All @@ -32,6 +34,8 @@ pub enum EventData {
impl EventData {
pub fn name(&self) -> &'static str {
match self {
EventData::MouseDown { .. } => "mousedown",
EventData::MouseUp { .. } => "mouseup",
EventData::Click { .. } => "click",
EventData::KeyPress { .. } => "keypress",
EventData::Ime { .. } => "input",
Expand Down
10 changes: 10 additions & 0 deletions packages/blitz-dom/src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,16 @@ impl Node {
.remove(ElementState::FOCUS | ElementState::FOCUSRING);
self.set_restyle_hint(RestyleHint::restyle_subtree());
}

pub fn active(&mut self) {
self.element_state.insert(ElementState::ACTIVE);
self.set_restyle_hint(RestyleHint::restyle_subtree());
}

pub fn unactive(&mut self) {
self.element_state.remove(ElementState::ACTIVE);
self.set_restyle_hint(RestyleHint::restyle_subtree());
}
}

#[derive(Debug, Clone, Copy, PartialEq)]
Expand Down
2 changes: 1 addition & 1 deletion packages/blitz-dom/src/stylo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -463,7 +463,7 @@ impl selectors::Element for BlitzNode<'_> {
_context: &mut MatchingContext<Self::Impl>,
) -> bool {
match *pseudo_class {
NonTSPseudoClass::Active => false,
NonTSPseudoClass::Active => self.element_state.contains(ElementState::ACTIVE),
NonTSPseudoClass::AnyLink => self
.raw_dom_data
.downcast_element()
Expand Down
56 changes: 53 additions & 3 deletions packages/blitz-shell/src/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ pub struct View<Doc: DocumentLike, Rend: DocumentRenderer> {
keyboard_modifiers: Modifiers,
mouse_pos: (f32, f32),
dom_mouse_pos: (f32, f32),
mouse_down_node: Option<usize>,

#[cfg(feature = "accessibility")]
/// Accessibility adapter for `accesskit`.
Expand Down Expand Up @@ -101,6 +102,7 @@ impl<Doc: DocumentLike, Rend: DocumentRenderer> View<Doc, Rend> {
theme_override: None,
mouse_pos: Default::default(),
dom_mouse_pos: Default::default(),
mouse_down_node: None,

#[cfg(feature = "accessibility")]
accessibility: AccessibilityState::new(&winit_window, proxy.clone()),
Expand Down Expand Up @@ -244,6 +246,50 @@ impl<Doc: DocumentLike, Rend: DocumentRenderer> View<Doc, Rend> {
self.doc.as_mut().set_hover_to(dom_x, dom_y)
}

pub fn mouse_down(&mut self, _button: &str) {
let Some(node_id) = self.doc.as_ref().get_hover_node_id() else {
return;
};

self.doc.as_mut().active_node();

// If we hit a node, then we collect the node to its parents, check for listeners, and then
// call those listeners
self.doc.handle_event(RendererEvent {
target: node_id,
data: EventData::MouseDown {
x: self.dom_mouse_pos.0,
y: self.dom_mouse_pos.1,
mods: self.keyboard_modifiers,
},
});

self.mouse_down_node = Some(node_id);
}

pub fn mouse_up(&mut self, button: &str) {
self.doc.as_mut().unactive_node();

let Some(node_id) = self.doc.as_ref().get_hover_node_id() else {
return;
};

// If we hit a node, then we collect the node to its parents, check for listeners, and then
// call those listeners
self.doc.handle_event(RendererEvent {
target: node_id,
data: EventData::MouseUp {
x: self.dom_mouse_pos.0,
y: self.dom_mouse_pos.1,
mods: self.keyboard_modifiers,
},
});

if self.mouse_down_node == Some(node_id) {
self.click(button);
}
}

pub fn click(&mut self, button: &str) {
let Some(node_id) = self.doc.as_ref().get_hover_node_id() else {
return;
Expand Down Expand Up @@ -405,13 +451,17 @@ impl<Doc: DocumentLike, Rend: DocumentRenderer> View<Doc, Rend> {
}
}
WindowEvent::MouseInput { button, state, .. } => {
if state == ElementState::Pressed && matches!(button, MouseButton::Left | MouseButton::Right) {
self.click(match button {
if matches!(button, MouseButton::Left | MouseButton::Right) {
let button = match button {
MouseButton::Left => "left",
MouseButton::Right => "right",
_ => unreachable!(),
};

});
match state {
ElementState::Pressed => self.mouse_down(button),
ElementState::Released => self.mouse_up(button)
}

self.request_redraw();
}
Expand Down
55 changes: 55 additions & 0 deletions packages/dioxus-native/src/dioxus_document.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,61 @@ impl DocumentLike for DioxusDocument {
let mut stop_propagation = false;

match &event.data {
EventData::MouseDown { .. } => {
let click_event_data = wrap_event_data(NativeClickData);

for &DxNodeIds { node_id, dioxus_id } in chain.iter() {
if let Some(id) = dioxus_id {
let click_event = Event::new(click_event_data.clone(), true);
self.vdom
.runtime()
.handle_event("mousedown", click_event.clone(), id);
prevent_default |= !click_event.default_action_enabled();
stop_propagation |= !click_event.propagates();
}

if !prevent_default {
let default_event = RendererEvent {
target: node_id,
data: renderer_event.data.clone(),
};
self.inner.as_mut().handle_event(default_event);
}

if stop_propagation {
break;
}
}
}
EventData::MouseUp { .. } => {
let click_event_data = wrap_event_data(NativeClickData);

for &DxNodeIds {
node_id, dioxus_id, ..
} in chain.iter()
{
if let Some(id) = dioxus_id {
let click_event = Event::new(click_event_data.clone(), true);
self.vdom
.runtime()
.handle_event("mouseup", click_event.clone(), id);
prevent_default |= !click_event.default_action_enabled();
stop_propagation |= !click_event.propagates();
}

if !prevent_default {
let default_event = RendererEvent {
target: node_id,
data: renderer_event.data.clone(),
};
self.inner.as_mut().handle_event(default_event);
}

if stop_propagation {
break;
}
}
}
EventData::Click { .. } => {
// look for the data-dioxus-id attribute on the element
// todo: we might need to walk upwards to find the first element with a data-dioxus-id attribute
Expand Down

0 comments on commit 86cca2f

Please sign in to comment.