use bevy_ecs::prelude::*;
use bevy_ecs::system::NonSendMarker;
use windows::Win32::Foundation::HWND;
use super::zorder_pair::{
PairFixDecision, SkipReason, log_owner_establish_failed, log_owner_established,
measure_window_above, record_decision,
};
use super::{KeepDirectlyAbove, OwnerLink, ReassertZOrder, WindowHandle, ZOrderPairStrategy};
use crate::api::set_window_owner;
#[derive(Component, Debug, Clone, Copy, PartialEq, Eq)]
pub struct OwnerEstablishFailed {
pub owned_hwnd: HWND,
pub owner_hwnd: HWND,
}
unsafe impl Send for OwnerEstablishFailed {}
unsafe impl Sync for OwnerEstablishFailed {}
pub fn establish_owner_links(
_ui_thread: NonSendMarker,
mut commands: Commands,
strategy: Option<Res<ZOrderPairStrategy>>,
declarations: Query<
(Entity, &KeepDirectlyAbove, Option<&OwnerEstablishFailed>),
Without<OwnerLink>,
>,
handles: Query<Option<Ref<WindowHandle>>>,
) {
let strategy = strategy.map(|s| *s).unwrap_or_default();
for (entity, declaration, failed) in declarations.iter() {
let peer = declaration.peer;
let owned = handles.get(entity).ok().flatten();
let peer_lookup = handles.get(peer);
let handle_added = owned.as_ref().is_some_and(Ref::is_added)
|| peer_lookup
.as_ref()
.ok()
.and_then(Option::as_ref)
.is_some_and(Ref::is_added);
if !handle_added {
continue;
}
if matches!(strategy, ZOrderPairStrategy::ExplicitMaintenance) {
record_decision(
entity,
peer,
PairFixDecision::Skip(SkipReason::StrategyDisabled),
);
continue;
}
let Ok(owner) = peer_lookup else {
record_decision(entity, peer, PairFixDecision::Skip(SkipReason::PeerMissing));
continue;
};
let (Some(owned), Some(owner)) = (owned, owner) else {
record_decision(
entity,
peer,
PairFixDecision::Skip(SkipReason::HandleMissing),
);
continue;
};
let (owned_hwnd, owner_hwnd) = (owned.hwnd, owner.hwnd);
if failed
== Some(&OwnerEstablishFailed {
owned_hwnd,
owner_hwnd,
})
{
continue;
}
match set_window_owner(owned_hwnd, owner_hwnd) {
Ok(()) => {
let measured_prev = measure_window_above(owner_hwnd);
commands.entity(entity).insert((
OwnerLink { owner_hwnd },
ReassertZOrder::default(),
));
log_owner_established(entity, peer, owned_hwnd, owner_hwnd, measured_prev);
}
Err(err) => {
log_owner_establish_failed(entity, &err);
commands.entity(entity).insert(OwnerEstablishFailed {
owned_hwnd,
owner_hwnd,
});
}
}
}
}
#[cfg(test)]
#[path = "zorder_pair_establish_tests.rs"]
mod tests;