use super::*;
fn child_local_pos(parent: &dyn Widget, child_bounds: Rect, pos_in_parent: Point) -> Point {
let mut x = pos_in_parent.x;
let mut y = pos_in_parent.y;
if let Some(t) = parent.child_transform() {
t.inverse_transform(&mut x, &mut y);
}
Point::new(x - child_bounds.x, y - child_bounds.y)
}
pub fn mark_subtree_dirty(widget: &mut dyn Widget) {
widget.mark_dirty();
for child in widget.children_mut().iter_mut() {
mark_subtree_dirty(child.as_mut());
}
}
pub fn hit_test_subtree(widget: &dyn Widget, local_pos: Point) -> Option<Vec<usize>> {
if !widget.is_visible() || !widget.hit_test(local_pos) {
return None;
}
if widget.claims_pointer_exclusively(local_pos) || widget.blocks_child_interaction() {
return Some(vec![]);
}
for (i, child) in widget.children().iter().enumerate().rev() {
let child_local = child_local_pos(widget, child.bounds(), local_pos);
if let Some(mut sub_path) = hit_test_subtree(child.as_ref(), child_local) {
sub_path.insert(0, i);
return Some(sub_path);
}
}
Some(vec![]) }
pub fn active_modal_path(widget: &dyn Widget) -> Option<Vec<usize>> {
if !widget.is_visible() {
return None;
}
for (i, child) in widget.children().iter().enumerate().rev() {
if let Some(mut sub_path) = active_modal_path(child.as_ref()) {
sub_path.insert(0, i);
return Some(sub_path);
}
}
if widget.has_active_modal() {
Some(vec![])
} else {
None
}
}
pub fn global_overlay_hit_path(widget: &dyn Widget, local_pos: Point) -> Option<Vec<usize>> {
if !widget.is_visible() {
return None;
}
for (i, child) in widget.children().iter().enumerate().rev() {
let child_local = child_local_pos(widget, child.bounds(), local_pos);
if let Some(mut sub_path) = global_overlay_hit_path(child.as_ref(), child_local) {
sub_path.insert(0, i);
return Some(sub_path);
}
}
if widget.hit_test_global_overlay(local_pos) {
Some(vec![])
} else {
None
}
}
fn deliver(widget: &mut dyn Widget, event: &Event) -> EventResult {
auto_request_draw(widget.on_event(event))
}
fn auto_request_draw(result: EventResult) -> EventResult {
if result.requests_redraw() {
crate::animation::request_draw();
}
result
}
pub fn dispatch_event(
root: &mut Box<dyn Widget>,
path: &[usize],
event: &Event,
pos_in_root: Point,
) -> EventResult {
if path.is_empty() {
let before = crate::animation::invalidation_epoch();
let result = deliver(root.as_mut(), event);
if result.requests_redraw() || before != crate::animation::invalidation_epoch() {
root.mark_dirty();
}
return result;
}
let idx = path[0];
if idx >= root.children().len() {
return deliver(root.as_mut(), event);
}
let child_bounds = root.children()[idx].bounds();
let child_pos = child_local_pos(root.as_ref(), child_bounds, pos_in_root);
let translated_event = translate_event(event, child_pos);
let before_child = crate::animation::invalidation_epoch();
let child_result = dispatch_event(
&mut root.children_mut()[idx],
&path[1..],
&translated_event,
child_pos,
);
if before_child != crate::animation::invalidation_epoch() {
root.mark_dirty();
}
if child_result.is_consumed() {
return child_result;
}
let before_self = crate::animation::invalidation_epoch();
let result = deliver(root.as_mut(), event);
if result.requests_redraw() || before_self != crate::animation::invalidation_epoch() {
root.mark_dirty();
}
result
}
pub fn dispatch_event_dyn(
root: &mut dyn Widget,
path: &[usize],
event: &Event,
pos_in_root: Point,
) -> EventResult {
if path.is_empty() {
let before = crate::animation::invalidation_epoch();
let result = deliver(root, event);
if result.requests_redraw() || before != crate::animation::invalidation_epoch() {
root.mark_dirty();
}
return result;
}
let idx = path[0];
if idx >= root.children().len() {
return deliver(root, event);
}
let child_bounds = root.children()[idx].bounds();
let child_pos = child_local_pos(root, child_bounds, pos_in_root);
let translated_event = translate_event(event, child_pos);
let before_child = crate::animation::invalidation_epoch();
let child_result = dispatch_event(
&mut root.children_mut()[idx],
&path[1..],
&translated_event,
child_pos,
);
if before_child != crate::animation::invalidation_epoch() {
root.mark_dirty();
}
if child_result.is_consumed() {
return child_result;
}
let before_self = crate::animation::invalidation_epoch();
let result = deliver(root, event);
if result.requests_redraw() || before_self != crate::animation::invalidation_epoch() {
root.mark_dirty();
}
result
}
pub fn dispatch_unconsumed_key(
widget: &mut dyn Widget,
key: &Key,
modifiers: Modifiers,
) -> EventResult {
if !widget.is_visible() {
return EventResult::Ignored;
}
let mut consumed = None;
for child in widget.children_mut().iter_mut().rev() {
let r = dispatch_unconsumed_key(child.as_mut(), key, modifiers);
if r.is_consumed() {
consumed = Some(r);
break;
}
}
if let Some(r) = consumed {
widget.mark_dirty();
return r;
}
let before = crate::animation::invalidation_epoch();
let result = auto_request_draw(widget.on_unconsumed_key(key, modifiers));
if result.requests_redraw() || before != crate::animation::invalidation_epoch() {
widget.mark_dirty();
}
result
}
fn translate_event(event: &Event, new_pos: Point) -> Event {
match event {
Event::MouseMove { .. } => Event::MouseMove { pos: new_pos },
Event::MouseDown {
button, modifiers, ..
} => Event::MouseDown {
pos: new_pos,
button: *button,
modifiers: *modifiers,
},
Event::MouseUp {
button, modifiers, ..
} => Event::MouseUp {
pos: new_pos,
button: *button,
modifiers: *modifiers,
},
Event::MouseWheel {
delta_y,
delta_x,
modifiers,
..
} => Event::MouseWheel {
pos: new_pos,
delta_y: *delta_y,
delta_x: *delta_x,
modifiers: *modifiers,
},
Event::FileDropped { paths, .. } => Event::FileDropped {
pos: new_pos,
paths: paths.clone(),
},
other => other.clone(),
}
}
pub fn dispatch_event_broadcast(
root: &mut Box<dyn Widget>,
event: &Event,
pos_in_root: Point,
) -> EventResult {
if !root.is_visible() {
return EventResult::Ignored;
}
for i in (0..root.children().len()).rev() {
let child_bounds = root.children()[i].bounds();
let child_pos = child_local_pos(root.as_ref(), child_bounds, pos_in_root);
let translated = translate_event(event, child_pos);
let r = dispatch_event_broadcast(&mut root.children_mut()[i], &translated, child_pos);
if r.is_consumed() {
root.mark_dirty();
return r;
}
}
let result = deliver(root.as_mut(), event);
if result.is_consumed() {
root.mark_dirty();
}
result
}