use bevy_ecs::prelude::*;
use windows::Win32::Foundation::HWND;
use super::zorder_pair::{
FrontScan, PairFixDecision, SkipReason, log_orphan_sink_mark_dropped, log_sink_observed,
measure_window_below, measure_windows_in_front, record_decision,
};
use super::zorder_pair_maintain::HandleQuery;
use super::{KeepDirectlyAbove, WindowHandle};
use crate::api::get_foreground_window;
#[derive(Component, Debug, Default, Clone, Copy, PartialEq, Eq)]
pub struct SinkObservationPending;
pub(crate) type SinkMarkQuery<'w, 's> =
Query<'w, 's, (Entity, Option<&'static KeepDirectlyAbove>), With<SinkObservationPending>>;
pub(crate) fn mark_pair_sink_observation(world: &mut World, deactivated: Entity) -> Option<Entity> {
let holder = if world.get::<KeepDirectlyAbove>(deactivated).is_some() {
deactivated
} else {
let mut declarations = world.query::<(Entity, &KeepDirectlyAbove)>();
declarations
.iter(world)
.find(|(_, declaration)| declaration.peer == deactivated)
.map(|(entity, _)| entity)?
};
world.entity_mut(holder).insert(SinkObservationPending);
Some(holder)
}
pub(crate) fn is_behind_foreground(
target: HWND,
foreground: Option<HWND>,
scan: &FrontScan,
) -> Option<bool> {
let foreground = foreground?;
if foreground == target {
return Some(false);
}
if scan.windows.contains(&foreground) {
return Some(true);
}
scan.reached_top.then_some(false)
}
pub(crate) fn observe_marked_pair_sinks(
commands: &mut Commands,
marks: &SinkMarkQuery,
handles: &HandleQuery,
) {
for (entity, declaration) in marks.iter() {
commands.entity(entity).remove::<SinkObservationPending>();
let Some(declaration) = declaration else {
log_orphan_sink_mark_dropped(entity);
continue;
};
let peer = declaration.peer;
let above_hwnd = handles.get(entity).ok().flatten().map(|h| h.hwnd);
let Ok(peer_handle) = handles.get(peer) else {
record_decision(entity, peer, PairFixDecision::Skip(SkipReason::PeerMissing));
continue;
};
let (Some(above_hwnd), Some(below_hwnd)) =
(above_hwnd, peer_handle.map(|h: &WindowHandle| h.hwnd))
else {
record_decision(
entity,
peer,
PairFixDecision::Skip(SkipReason::HandleMissing),
);
continue;
};
let adjacency_ok = measure_window_below(above_hwnd) == Some(below_hwnd);
let foreground = get_foreground_window();
let scan = measure_windows_in_front(above_hwnd);
let behind_foreground = is_behind_foreground(above_hwnd, foreground, &scan);
log_sink_observed(entity, adjacency_ok, foreground, behind_foreground);
}
}
#[cfg(test)]
#[path = "zorder_pair_sink_tests.rs"]
mod tests;