use azul_core::{
dom::{DomId, DomNodeId},
events::{
deduplicate_synthetic_events, EventData, EventProvider, EventSource, EventType,
KeyModifiers, MouseButton, MouseEventData, SyntheticEvent, WindowEventData,
},
geom::{LogicalPosition, LogicalRect},
id::NodeId,
styled_dom::NodeHierarchyItemId,
task::{Instant, SystemTick},
window::{CursorPosition, WindowPosition},
};
use std::collections::BTreeSet;
use crate::window_state::FullWindowState;
pub fn determine_events_from_managers<'a>(
current_state: &FullWindowState,
previous_state: &FullWindowState,
managers: &[&'a dyn EventProvider],
timestamp: Instant,
) -> Vec<SyntheticEvent> {
let mut events = Vec::new();
events.extend(detect_window_state_events(
current_state,
previous_state,
timestamp.clone(),
));
for manager in managers {
events.extend(manager.get_pending_events(timestamp.clone()));
}
deduplicate_synthetic_events(events)
}
fn detect_window_state_events(
current: &FullWindowState,
previous: &FullWindowState,
timestamp: Instant,
) -> Vec<SyntheticEvent> {
let mut events = Vec::new();
if current.size != previous.size {
events.push(SyntheticEvent::new(
EventType::WindowResize,
EventSource::User,
DomNodeId {
dom: DomId { inner: 0 },
node: NodeHierarchyItemId::from_crate_internal(Some(NodeId::ZERO)),
},
timestamp.clone(),
EventData::Window(WindowEventData {
size: Some(LogicalRect {
origin: LogicalPosition { x: 0.0, y: 0.0 },
size: current.size.dimensions.clone(),
}),
position: None,
}),
));
}
if current.position != previous.position {
let position = match current.position {
WindowPosition::Initialized(phys_pos) => Some(LogicalPosition {
x: phys_pos.x as f32,
y: phys_pos.y as f32,
}),
WindowPosition::Uninitialized => None,
};
if let Some(pos) = position {
events.push(SyntheticEvent::new(
EventType::WindowMove,
EventSource::User,
DomNodeId {
dom: DomId { inner: 0 },
node: NodeHierarchyItemId::from_crate_internal(Some(NodeId::ZERO)),
},
timestamp.clone(),
EventData::Window(WindowEventData {
size: None,
position: Some(pos),
}),
));
}
}
if current.theme != previous.theme {
events.push(SyntheticEvent::new(
EventType::ThemeChange,
EventSource::User,
DomNodeId {
dom: DomId { inner: 0 },
node: NodeHierarchyItemId::from_crate_internal(Some(NodeId::ZERO)),
},
timestamp.clone(),
EventData::None,
));
}
let prev_mouse_in_window = matches!(
previous.mouse_state.cursor_position,
CursorPosition::InWindow(_)
);
let curr_mouse_in_window = matches!(
current.mouse_state.cursor_position,
CursorPosition::InWindow(_)
);
if curr_mouse_in_window && !prev_mouse_in_window {
events.push(SyntheticEvent::new(
EventType::MouseEnter,
EventSource::User,
DomNodeId {
dom: DomId { inner: 0 },
node: NodeHierarchyItemId::from_crate_internal(Some(NodeId::ZERO)),
},
timestamp.clone(),
EventData::None,
));
}
if !curr_mouse_in_window && prev_mouse_in_window {
events.push(SyntheticEvent::new(
EventType::MouseLeave,
EventSource::User,
DomNodeId {
dom: DomId { inner: 0 },
node: NodeHierarchyItemId::from_crate_internal(Some(NodeId::ZERO)),
},
timestamp.clone(),
EventData::None,
));
}
events
}
fn get_all_hovered_nodes(
hover_manager: &crate::managers::hover::HoverManager,
frame_index: usize,
) -> BTreeSet<NodeId> {
use crate::managers::hover::InputPointId;
let dom_id = DomId { inner: 0 };
hover_manager
.get_frame(&InputPointId::Mouse, frame_index)
.and_then(|ht| ht.hovered_nodes.get(&dom_id))
.map(|ht| ht.regular_hit_test_nodes.keys().copied().collect())
.unwrap_or_default()
}
pub fn determine_all_events(
current_state: &FullWindowState,
previous_state: &FullWindowState,
hover_manager: &crate::managers::hover::HoverManager,
focus_manager: &crate::managers::focus_cursor::FocusManager,
file_drop_manager: &crate::managers::file_drop::FileDropManager,
gesture_manager: Option<&crate::managers::gesture::GestureAndDragManager>,
managers: &[&dyn EventProvider],
timestamp: Instant,
) -> Vec<SyntheticEvent> {
let mut events = Vec::new();
let root_node = DomNodeId {
dom: DomId { inner: 0 },
node: NodeHierarchyItemId::from_crate_internal(Some(NodeId::ZERO)),
};
let cursor_pos = current_state
.mouse_state
.cursor_position
.get_position()
.unwrap_or(LogicalPosition { x: 0.0, y: 0.0 });
let modifiers = KeyModifiers {
shift: current_state.keyboard_state.shift_down(),
ctrl: current_state.keyboard_state.ctrl_down(),
alt: current_state.keyboard_state.alt_down(),
meta: current_state.keyboard_state.super_down(),
};
let buttons: u8 = (if current_state.mouse_state.left_down { 1 } else { 0 })
| (if current_state.mouse_state.right_down { 2 } else { 0 })
| (if current_state.mouse_state.middle_down { 4 } else { 0 });
let mouse_target = hover_manager
.current_hover_node()
.map(|node_id| DomNodeId {
dom: DomId { inner: 0 },
node: NodeHierarchyItemId::from_crate_internal(Some(node_id)),
})
.unwrap_or(root_node.clone());
let make_mouse_data = |button: MouseButton| -> EventData {
EventData::Mouse(MouseEventData {
position: cursor_pos,
button,
buttons,
modifiers,
})
};
let current_mouse_down = current_state.mouse_state.mouse_down();
let previous_mouse_down = previous_state.mouse_state.mouse_down();
if current_state.mouse_state.left_down && !previous_state.mouse_state.left_down {
events.push(SyntheticEvent::new(
EventType::MouseDown,
EventSource::User,
mouse_target.clone(),
timestamp.clone(),
make_mouse_data(MouseButton::Left),
));
}
if !current_state.mouse_state.left_down && previous_state.mouse_state.left_down {
events.push(SyntheticEvent::new(
EventType::MouseUp,
EventSource::User,
mouse_target.clone(),
timestamp.clone(),
make_mouse_data(MouseButton::Left),
));
}
if current_state.mouse_state.right_down && !previous_state.mouse_state.right_down {
events.push(SyntheticEvent::new(
EventType::MouseDown,
EventSource::User,
mouse_target.clone(),
timestamp.clone(),
make_mouse_data(MouseButton::Right),
));
}
if !current_state.mouse_state.right_down && previous_state.mouse_state.right_down {
events.push(SyntheticEvent::new(
EventType::MouseUp,
EventSource::User,
mouse_target.clone(),
timestamp.clone(),
make_mouse_data(MouseButton::Right),
));
}
if current_state.mouse_state.middle_down && !previous_state.mouse_state.middle_down {
events.push(SyntheticEvent::new(
EventType::MouseDown,
EventSource::User,
mouse_target.clone(),
timestamp.clone(),
make_mouse_data(MouseButton::Middle),
));
}
if !current_state.mouse_state.middle_down && previous_state.mouse_state.middle_down {
events.push(SyntheticEvent::new(
EventType::MouseUp,
EventSource::User,
mouse_target.clone(),
timestamp.clone(),
make_mouse_data(MouseButton::Middle),
));
}
if !current_state.mouse_state.left_down && previous_state.mouse_state.left_down {
let prev_hover = hover_manager.previous_hover_node();
let curr_hover = hover_manager.current_hover_node();
if prev_hover == curr_hover && curr_hover.is_some() {
events.push(SyntheticEvent::new(
EventType::Click,
EventSource::User,
mouse_target.clone(),
timestamp.clone(),
make_mouse_data(MouseButton::Left),
));
}
}
let current_in_window = matches!(
current_state.mouse_state.cursor_position,
CursorPosition::InWindow(_)
);
let previous_in_window = matches!(
previous_state.mouse_state.cursor_position,
CursorPosition::InWindow(_)
);
if current_in_window {
let current_pos = current_state.mouse_state.cursor_position.get_position();
let previous_pos = previous_state.mouse_state.cursor_position.get_position();
if current_pos != previous_pos {
events.push(SyntheticEvent::new(
EventType::MouseOver,
EventSource::User,
mouse_target.clone(),
timestamp.clone(),
make_mouse_data(MouseButton::Left), ));
}
}
{
let dom_id = DomId { inner: 0 };
let current_hovered = get_all_hovered_nodes(hover_manager, 0);
let previous_hovered = get_all_hovered_nodes(hover_manager, 1);
for node_id in previous_hovered.difference(¤t_hovered) {
events.push(SyntheticEvent::new(
EventType::MouseLeave,
EventSource::User,
DomNodeId {
dom: dom_id,
node: NodeHierarchyItemId::from_crate_internal(Some(*node_id)),
},
timestamp.clone(),
EventData::None,
));
}
for node_id in current_hovered.difference(&previous_hovered) {
events.push(SyntheticEvent::new(
EventType::MouseEnter,
EventSource::User,
DomNodeId {
dom: dom_id,
node: NodeHierarchyItemId::from_crate_internal(Some(*node_id)),
},
timestamp.clone(),
EventData::None,
));
}
}
if current_in_window && !previous_in_window {
events.push(SyntheticEvent::new(
EventType::MouseEnter,
EventSource::User,
root_node.clone(),
timestamp.clone(),
EventData::None,
));
}
if !current_in_window && previous_in_window {
events.push(SyntheticEvent::new(
EventType::MouseLeave,
EventSource::User,
root_node.clone(),
timestamp.clone(),
EventData::None,
));
}
let focus_target = focus_manager
.get_focused_node()
.cloned()
.unwrap_or(root_node.clone());
let current_key = current_state
.keyboard_state
.current_virtual_keycode
.into_option();
let previous_key = previous_state
.keyboard_state
.current_virtual_keycode
.into_option();
if current_key.is_some() && (current_key != previous_key || previous_key.is_none()) {
events.push(SyntheticEvent::new(
EventType::KeyDown,
EventSource::User,
focus_target.clone(),
timestamp.clone(),
EventData::None,
));
}
if previous_key.is_some() && current_key.is_none() {
events.push(SyntheticEvent::new(
EventType::KeyUp,
EventSource::User,
focus_target.clone(),
timestamp.clone(),
EventData::None,
));
}
if current_state.size.dimensions != previous_state.size.dimensions {
events.push(SyntheticEvent::new(
EventType::WindowResize,
EventSource::User,
root_node.clone(),
timestamp.clone(),
EventData::Window(WindowEventData {
size: Some(LogicalRect {
origin: LogicalPosition { x: 0.0, y: 0.0 },
size: current_state.size.dimensions.clone(),
}),
position: None,
}),
));
}
if current_state.position != previous_state.position {
if let WindowPosition::Initialized(phys_pos) = current_state.position {
events.push(SyntheticEvent::new(
EventType::WindowMove,
EventSource::User,
root_node.clone(),
timestamp.clone(),
EventData::Window(WindowEventData {
size: None,
position: Some(LogicalPosition {
x: phys_pos.x as f32,
y: phys_pos.y as f32,
}),
}),
));
}
}
if current_state.flags.close_requested && !previous_state.flags.close_requested {
events.push(SyntheticEvent::new(
EventType::WindowClose,
EventSource::User,
root_node.clone(),
timestamp.clone(),
EventData::None,
));
}
if current_state.window_focused && !previous_state.window_focused {
events.push(SyntheticEvent::new(
EventType::WindowFocusIn,
EventSource::User,
root_node.clone(),
timestamp.clone(),
EventData::None,
));
}
if !current_state.window_focused && previous_state.window_focused {
events.push(SyntheticEvent::new(
EventType::WindowFocusOut,
EventSource::User,
root_node.clone(),
timestamp.clone(),
EventData::None,
));
}
if current_state.theme != previous_state.theme {
events.push(SyntheticEvent::new(
EventType::ThemeChange,
EventSource::User,
root_node.clone(),
timestamp.clone(),
EventData::None,
));
}
if current_state.size.dpi != previous_state.size.dpi {
events.push(SyntheticEvent::new(
EventType::WindowDpiChanged,
EventSource::User,
root_node.clone(),
timestamp.clone(),
EventData::None,
));
}
if current_state.monitor_id != previous_state.monitor_id
&& current_state.monitor_id != azul_css::corety::OptionU32::None
&& previous_state.monitor_id != azul_css::corety::OptionU32::None
{
events.push(SyntheticEvent::new(
EventType::WindowMonitorChanged,
EventSource::User,
root_node.clone(),
timestamp.clone(),
EventData::None,
));
}
if let Some(_hovered_file) = file_drop_manager.get_hovered_file() {
events.push(SyntheticEvent::new(
EventType::FileHover,
EventSource::User,
root_node.clone(),
timestamp.clone(),
EventData::None,
));
}
if file_drop_manager.dropped_file.is_some() {
events.push(SyntheticEvent::new(
EventType::FileDrop,
EventSource::User,
root_node.clone(),
timestamp.clone(),
EventData::None,
));
}
if let Some(manager) = gesture_manager {
let event_was_mouse_release = !current_mouse_down && previous_mouse_down;
if let Some(_detected_drag) = manager.detect_drag() {
if !manager.is_dragging() {
events.push(SyntheticEvent::new(
EventType::DragStart,
EventSource::User,
mouse_target.clone(),
timestamp.clone(),
make_mouse_data(MouseButton::Left),
));
}
}
if manager.is_dragging() && current_mouse_down {
let current_pos = current_state.mouse_state.cursor_position.get_position();
let previous_pos = previous_state.mouse_state.cursor_position.get_position();
if current_pos != previous_pos {
events.push(SyntheticEvent::new(
EventType::Drag,
EventSource::User,
mouse_target.clone(),
timestamp.clone(),
make_mouse_data(MouseButton::Left),
));
}
}
if manager.is_dragging() && event_was_mouse_release {
events.push(SyntheticEvent::new(
EventType::DragEnd,
EventSource::User,
mouse_target.clone(),
timestamp.clone(),
make_mouse_data(MouseButton::Left),
));
if manager.is_node_drag_active() {
events.push(SyntheticEvent::new(
EventType::Drop,
EventSource::User,
mouse_target.clone(), timestamp.clone(),
make_mouse_data(MouseButton::Left),
));
}
}
if manager.is_node_drag_active() && current_mouse_down {
let dom_id = DomId { inner: 0 };
let current_hover = hover_manager.current_hover_node();
let previous_hover = hover_manager.previous_hover_node();
if current_hover != previous_hover {
if let Some(prev_node) = previous_hover {
events.push(SyntheticEvent::new(
EventType::DragLeave,
EventSource::User,
DomNodeId {
dom: dom_id,
node: NodeHierarchyItemId::from_crate_internal(Some(prev_node)),
},
timestamp.clone(),
EventData::None,
));
}
if let Some(curr_node) = current_hover {
events.push(SyntheticEvent::new(
EventType::DragEnter,
EventSource::User,
DomNodeId {
dom: dom_id,
node: NodeHierarchyItemId::from_crate_internal(Some(curr_node)),
},
timestamp.clone(),
EventData::None,
));
}
}
if let Some(curr_node) = current_hover {
events.push(SyntheticEvent::new(
EventType::DragOver,
EventSource::User,
DomNodeId {
dom: dom_id,
node: NodeHierarchyItemId::from_crate_internal(Some(curr_node)),
},
timestamp.clone(),
EventData::None,
));
}
}
if manager.detect_double_click() {
events.push(SyntheticEvent::new(
EventType::DoubleClick,
EventSource::User,
mouse_target.clone(),
timestamp.clone(),
make_mouse_data(MouseButton::Left),
));
}
if let Some(long_press) = manager.detect_long_press() {
if !long_press.callback_invoked {
events.push(SyntheticEvent::new(
EventType::LongPress,
EventSource::User,
mouse_target.clone(),
timestamp.clone(),
make_mouse_data(MouseButton::Left),
));
}
}
if let Some(direction) = manager.detect_swipe_direction() {
use crate::managers::gesture::GestureDirection;
let event_type = match direction {
GestureDirection::Left => EventType::SwipeLeft,
GestureDirection::Right => EventType::SwipeRight,
GestureDirection::Up => EventType::SwipeUp,
GestureDirection::Down => EventType::SwipeDown,
};
events.push(SyntheticEvent::new(
event_type,
EventSource::User,
mouse_target.clone(),
timestamp.clone(),
EventData::None,
));
}
if let Some(pinch) = manager.detect_pinch() {
let event_type = if pinch.scale < 1.0 {
EventType::PinchIn
} else {
EventType::PinchOut
};
events.push(SyntheticEvent::new(
event_type,
EventSource::User,
mouse_target.clone(),
timestamp.clone(),
EventData::None,
));
}
if let Some(rotation) = manager.detect_rotation() {
let event_type = if rotation.angle_radians > 0.0 {
EventType::RotateClockwise
} else {
EventType::RotateCounterClockwise
};
events.push(SyntheticEvent::new(
event_type,
EventSource::User,
mouse_target.clone(),
timestamp.clone(),
EventData::None,
));
}
if let Some(pen_state) = manager.get_pen_state() {
if pen_state.in_contact {
let current_pos = current_state.mouse_state.cursor_position.get_position();
let previous_pos = previous_state.mouse_state.cursor_position.get_position();
if current_pos != previous_pos {
events.push(SyntheticEvent::new(
EventType::TouchMove, EventSource::User,
mouse_target.clone(),
timestamp.clone(),
EventData::None,
));
}
}
}
}
for manager in managers {
events.extend(manager.get_pending_events(timestamp.clone()));
}
deduplicate_synthetic_events(events)
}