freya_core/events/
handler.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
use dioxus_core::{
    Event,
    VirtualDom,
};
use freya_native_core::dioxus::NodeImmutableDioxusExt;
use tracing::info;

use super::{
    DomEvent,
    NodesState,
    PotentialEvent,
};
use crate::{
    dom::SafeDOM,
    events::NodesStatesUpdate,
};

pub struct ProcessedEvents {
    pub(crate) dom_events: Vec<DomEvent>,
    pub(crate) flattened_potential_events: Vec<PotentialEvent>,
    pub(crate) nodes_states_update: NodesStatesUpdate,
}

pub fn handle_processed_events(
    sdom: &SafeDOM,
    vdom: &mut VirtualDom,
    nodes_state: &mut NodesState,
    ProcessedEvents {
        mut dom_events,
        flattened_potential_events,
        mut nodes_states_update,
    }: ProcessedEvents,
) {
    let fdom = sdom.get();
    let rdom = fdom.rdom();
    let mut processed_events = Vec::<DomEvent>::new();

    #[cfg(debug_assertions)]
    info!("Processing {} DOM events", dom_events.len());

    while !dom_events.is_empty() {
        let dom_event = dom_events.remove(0);

        let Some(element_id) = rdom
            .get(dom_event.node_id)
            .and_then(|node| node.mounted_id())
        else {
            continue;
        };
        let event_name = dom_event.name;
        let event = Event::new(dom_event.data.clone().any(), dom_event.bubbles);
        let event_clone = event.clone();

        #[cfg(debug_assertions)]
        info!("Running event {event_name:?} in Element {element_id:?}");

        // Call the actual event handler
        vdom.runtime()
            .handle_event(event_name.into(), event, element_id);

        if !event_clone.default_action_enabled() {
            // Get the events that this event can cancel
            let cancellable_events = dom_event.name.get_cancellable_events();

            // Remove the rest of dom events that are cancellable
            dom_events.retain(|event| !cancellable_events.contains(&event.name));

            // Discard the potential events that dont find a matching dom event
            // So for instance, a cancelled potential mousemove event wont be discarded if a dom mousenter was processed before
            // At the same time, a cancelled potential mousemove event that actually gets discarded will only discard the node state
            // made in this run, but will not change what was already before this run
            // So if the affected node was already being hovered from the last events run, it will continue to be as so
            for potential_event in &flattened_potential_events {
                let is_cancellable = cancellable_events.contains(&potential_event.name);
                if is_cancellable {
                    let processed_event = processed_events.iter().find(|event| {
                        potential_event.name == event.source_event
                            && potential_event.node_id == event.node_id
                    });
                    if processed_event.is_none() {
                        nodes_states_update
                            .discard(&potential_event.name, &potential_event.node_id);
                    }
                }
            }
        }

        processed_events.push(dom_event);
    }

    vdom.process_events();

    nodes_state.apply_update(nodes_states_update);
}