use std::collections::HashMap;
use bevy_ecs::prelude::*;
use bevy_egui::EguiContext;
use bevy_picking::pointer::PointerInteraction;
use bevy_reflect::prelude::*;
use bytemuck::TransparentWrapper;
use rmf_site_camera::{TypeInfo, plugins::BlockerRegistration, resources::BlockStatus};
pub(crate) mod plugins;
use tracing::warn;
use crate::*;
#[derive(Resource, Default, TransparentWrapper)]
#[repr(transparent)]
pub struct UiFocused(pub bool);
#[derive(Resource, Default, TransparentWrapper)]
#[repr(transparent)]
pub struct IteractionMaskHovered(pub bool);
#[derive(Reflect, Resource, Default, TransparentWrapper)]
#[reflect(Resource)]
#[repr(transparent)]
pub struct PickingBlockers(pub HashMap<TypeInfo, bool>);
pub type PickBlockerRegistration<T> = BlockerRegistration<T, PickingBlockers>;
pub type PickBlockStatus = BlockStatus<PickingBlockers>;
#[derive(Debug, Clone, Copy, Default, Resource)]
pub struct Picked {
pub current: Option<Entity>,
pub refresh: bool,
}
#[derive(Debug, Clone, Copy, Default, Event)]
pub struct ChangePick {
pub from: Option<Entity>,
pub to: Option<Entity>,
}
pub(crate) fn pick_topmost(
picks: impl Iterator<Item = Entity>,
selectable: &Query<&Selectable, Without<Preview>>,
) -> Option<Entity> {
for topmost_entity in picks {
if let Ok(sel) = selectable.get(topmost_entity) {
if !sel.is_selectable {
continue;
}
} else {
continue;
}
return Some(topmost_entity);
}
return None;
}
pub(crate) fn update_picked(
selectable: Query<&Selectable, Without<Preview>>,
block_status: Res<PickBlockStatus>,
pointers: Query<&PointerInteraction>,
visual_cues: Query<&ComputedVisualCue, Without<Preview>>,
mut picked: ResMut<Picked>,
mut change_pick: EventWriter<ChangePick>,
) {
if block_status.blocked() {
if picked.current.is_some() {
change_pick.write(ChangePick {
from: picked.current,
to: None,
});
picked.current = None;
}
return;
}
let current_picked = 'current_picked: {
for interactions in &pointers {
if let Some(topmost) = pick_topmost(
interactions
.iter()
.filter(|(e, _)| {
visual_cues
.get(*e)
.ok()
.filter(|cue| cue.xray.any())
.is_some()
})
.map(|(e, _)| *e),
&selectable,
) {
break 'current_picked Some(topmost);
}
if let Some(topmost) = pick_topmost(interactions.iter().map(|(e, _)| *e), &selectable) {
break 'current_picked Some(topmost);
}
}
None
};
let refresh = picked.refresh;
if refresh {
picked.refresh = false;
}
if picked.current != current_picked || refresh {
change_pick.write(ChangePick {
from: picked.current,
to: current_picked,
});
picked.current = current_picked;
}
}
pub(crate) fn check_ui_focus(
mut window: Query<&mut EguiContext>,
mut ui_status: ResMut<UiFocused>,
) {
let Ok(mut ctx) = window
.single_mut()
.inspect_err(|err| warn!("couldn't check ui focus status. Reason: {:#}", err))
else {
return;
};
let ctx = ctx.get_mut();
let ui_has_focus =
ctx.wants_pointer_input() || ctx.wants_keyboard_input() || ctx.is_pointer_over_area();
ui_status.0 = ui_has_focus;
}