use std::collections::HashSet;
use crate::{application::ApplicationContext, window::WindowId};
use super::{
HitResult, RuntimeOutput, UiDefaultAction, UiEvent, UiEventContext, UiHandlerEvent, UiRect,
};
pub fn dispatch_runtime_output(
output: RuntimeOutput,
application: &ApplicationContext,
window: &WindowId,
mut handle_default_action: impl FnMut(UiDefaultAction) -> RuntimeOutput,
mut finish_context: impl FnMut(UiEventContext),
) -> RuntimeOutput {
let mut pending = vec![output];
let mut combined = empty_output();
let mut default_prevented = HashSet::new();
while let Some(output) = pending.pop() {
for event in &output.action_events {
let mut context = UiEventContext::new(application.clone(), window.clone());
event.dispatch(&mut context);
finish_context(context);
}
for event in &output.handler_events {
let context = dispatch_event_handlers(event, application, window);
if context.default_prevented() {
default_prevented.insert(event.target.clone());
}
finish_context(context);
}
for event in &output.events {
let hit = match event {
UiEvent::Clicked(hit) | UiEvent::Wheel { hit, .. } => hit,
_ => continue,
};
let Some(context) = dispatch_hit_handlers(hit, application, window) else {
continue;
};
if context.default_prevented() {
default_prevented.insert(hit.id.clone());
}
finish_context(context);
}
for action in output.default_actions.iter().cloned() {
if !default_prevented.contains(&action.event_target) {
pending.push(handle_default_action(action));
}
}
merge_output(&mut combined, output);
}
combined
}
pub fn dispatch_hit_handlers(
hit: &HitResult,
application: &ApplicationContext,
window: &WindowId,
) -> Option<UiEventContext> {
if hit.capture_handlers.is_empty() && hit.bubble_handlers.is_empty() {
return None;
}
let mut context = UiEventContext::new(application.clone(), window.clone());
for handler in &hit.capture_handlers {
handler(&mut context);
if context.propagation_stopped() {
return Some(context);
}
}
for handler in &hit.bubble_handlers {
handler(&mut context);
if context.propagation_stopped() {
break;
}
}
Some(context)
}
pub fn dispatch_event_handlers(
event: &UiHandlerEvent,
application: &ApplicationContext,
window: &WindowId,
) -> UiEventContext {
let mut context = UiEventContext::new(application.clone(), window.clone());
for handler in &event.capture_handlers {
handler(&mut context, &event.payload);
if context.propagation_stopped() {
return context;
}
}
for handler in &event.bubble_handlers {
handler(&mut context, &event.payload);
if context.propagation_stopped() {
break;
}
}
context
}
fn empty_output() -> RuntimeOutput {
RuntimeOutput {
events: Vec::new(),
handler_events: Vec::new(),
action_events: Vec::new(),
default_actions: Vec::new(),
dirty_bounds: None,
animation_changed: false,
route_changed: false,
}
}
fn merge_output(combined: &mut RuntimeOutput, mut output: RuntimeOutput) {
combined.events.append(&mut output.events);
combined.handler_events.append(&mut output.handler_events);
combined.action_events.append(&mut output.action_events);
combined.default_actions.append(&mut output.default_actions);
combined.dirty_bounds = union(combined.dirty_bounds, output.dirty_bounds);
combined.animation_changed |= output.animation_changed;
combined.route_changed |= output.route_changed;
}
fn union(left: Option<UiRect>, right: Option<UiRect>) -> Option<UiRect> {
match (left, right) {
(Some(left), Some(right)) => Some(left.union(right)),
(Some(rect), None) | (None, Some(rect)) => Some(rect),
(None, None) => None,
}
}
#[cfg(test)]
#[path = "dispatch_test.rs"]
mod tests;