use bevy_ecs::entity::Entity;
use windows::Win32::Foundation::HWND;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum DesiredState {
Transparent,
Opaque,
}
impl Default for DesiredState {
fn default() -> Self {
Self::Opaque
}
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct ClickThroughTarget {
pub(crate) window: Entity,
pub(crate) hwnd: HWND,
pub(crate) last_applied: DesiredState,
pub(crate) layered_applied: bool,
}
#[derive(Debug, Default)]
pub(crate) struct ClickThroughRegistry {
targets: Vec<ClickThroughTarget>,
}
impl ClickThroughRegistry {
pub(crate) fn new() -> Self {
Self::default()
}
pub(crate) fn register(&mut self, window: Entity, hwnd: HWND) {
if let Some(target) = self.targets.iter_mut().find(|t| t.window == window) {
target.hwnd = hwnd;
target.last_applied = DesiredState::default();
target.layered_applied = false;
} else {
self.targets.push(ClickThroughTarget {
window,
hwnd,
last_applied: DesiredState::default(),
layered_applied: false,
});
}
}
pub(crate) fn remove(&mut self, window: Entity) -> bool {
let before = self.targets.len();
self.targets.retain(|t| t.window != window);
self.targets.len() != before
}
pub(crate) fn len(&self) -> usize {
self.targets.len()
}
pub(crate) fn is_empty(&self) -> bool {
self.targets.is_empty()
}
pub(crate) fn last_applied(&self, window: Entity) -> Option<DesiredState> {
self.targets
.iter()
.find(|t| t.window == window)
.map(|t| t.last_applied)
}
pub(crate) fn set_last_applied(&mut self, window: Entity, state: DesiredState) -> bool {
if let Some(target) = self.targets.iter_mut().find(|t| t.window == window) {
target.last_applied = state;
true
} else {
false
}
}
pub(crate) fn mark_layered_applied(&mut self, window: Entity) -> bool {
if let Some(target) = self.targets.iter_mut().find(|t| t.window == window) {
target.layered_applied = true;
true
} else {
false
}
}
pub(crate) fn iter(&self) -> impl Iterator<Item = &ClickThroughTarget> {
self.targets.iter()
}
pub(crate) fn retain<F: FnMut(&ClickThroughTarget) -> bool>(&mut self, mut keep: F) -> usize {
let before = self.targets.len();
self.targets.retain(|t| keep(t));
before - self.targets.len()
}
}
#[cfg(test)]
mod tests {
use super::*;
use bevy_ecs::world::World;
use windows::Win32::Foundation::HWND;
fn hwnd(v: isize) -> HWND {
HWND(v as *mut core::ffi::c_void)
}
#[test]
fn desired_state_default_is_opaque() {
assert_eq!(DesiredState::default(), DesiredState::Opaque);
}
#[test]
fn freshly_registered_target_is_opaque() {
let mut world = World::new();
let e = world.spawn_empty().id();
let mut reg = ClickThroughRegistry::new();
reg.register(e, hwnd(1));
assert_eq!(reg.last_applied(e), Some(DesiredState::Opaque));
assert_eq!(reg.len(), 1);
}
#[test]
fn register_then_remove() {
let mut world = World::new();
let e = world.spawn_empty().id();
let mut reg = ClickThroughRegistry::new();
assert!(reg.is_empty());
reg.register(e, hwnd(1));
assert!(!reg.is_empty());
assert!(reg.last_applied(e).is_some());
assert!(reg.remove(e));
assert!(reg.is_empty());
assert_eq!(reg.last_applied(e), None);
assert!(!reg.remove(e));
}
#[test]
fn re_registering_same_entity_does_not_duplicate() {
let mut world = World::new();
let e = world.spawn_empty().id();
let mut reg = ClickThroughRegistry::new();
reg.register(e, hwnd(1));
assert!(reg.set_last_applied(e, DesiredState::Transparent));
assert!(reg.mark_layered_applied(e));
reg.register(e, hwnd(2));
assert_eq!(reg.len(), 1);
assert_eq!(reg.last_applied(e), Some(DesiredState::Opaque));
assert_eq!(reg.iter().next().map(|t| t.hwnd), Some(hwnd(2)));
assert_eq!(
reg.iter().next().map(|t| t.layered_applied),
Some(false),
"再登録(HWND 更新)で同伴フラグは再適用が必要"
);
}
#[test]
fn layered_applied_defaults_false_and_marks_true() {
let mut world = World::new();
let e = world.spawn_empty().id();
let mut reg = ClickThroughRegistry::new();
reg.register(e, hwnd(1));
assert_eq!(
reg.iter().next().map(|t| t.layered_applied),
Some(false),
"初期は未適用(false)"
);
assert!(reg.mark_layered_applied(e));
assert_eq!(reg.iter().next().map(|t| t.layered_applied), Some(true));
let other = world.spawn_empty().id();
assert!(!reg.mark_layered_applied(other));
}
#[test]
fn set_last_applied_is_reflected_on_read() {
let mut world = World::new();
let e = world.spawn_empty().id();
let mut reg = ClickThroughRegistry::new();
reg.register(e, hwnd(1));
assert!(reg.set_last_applied(e, DesiredState::Transparent));
assert_eq!(reg.last_applied(e), Some(DesiredState::Transparent));
let other = world.spawn_empty().id();
assert!(!reg.set_last_applied(other, DesiredState::Transparent));
}
#[test]
fn iterates_multiple_targets() {
let mut world = World::new();
let a = world.spawn_empty().id();
let b = world.spawn_empty().id();
let mut reg = ClickThroughRegistry::new();
reg.register(a, hwnd(10));
reg.register(b, hwnd(20));
assert_eq!(reg.len(), 2);
let windows: Vec<Entity> = reg.iter().map(|t| t.window).collect();
assert!(windows.contains(&a));
assert!(windows.contains(&b));
}
}