use crate::window::{WindowKind, WindowProperties};
use crate::*;
use std::collections::HashMap;
use std::sync::Mutex;
use windows::Win32::{
Foundation::{HWND, LPARAM, WPARAM},
UI::WindowsAndMessaging::{PostMessageW, WM_CLOSE},
};
pub(crate) struct Object {
pub kind: WindowKind,
pub props: WindowProperties,
pub sender: crate::window::UnboundedSender<Event>,
pub raw_input_sender: Option<crate::window::UnboundedSender<raw_input::RawInputEvent>>,
pub children: Vec<HWND>,
}
pub(crate) struct Context {
window_map: HashMap<isize, Object>,
}
static CONTEXT: once_cell::sync::Lazy<Mutex<Context>> =
once_cell::sync::Lazy::new(|| Mutex::new(Context::new()));
impl Context {
fn new() -> Self {
Self {
window_map: HashMap::new(),
}
}
pub fn is_empty() -> bool {
let ctx = CONTEXT.lock().unwrap();
ctx.window_map.is_empty()
}
pub fn register_window(
hwnd: HWND,
kind: WindowKind,
props: WindowProperties,
sender: crate::window::UnboundedSender<Event>,
raw_input_sender: Option<crate::window::UnboundedSender<raw_input::RawInputEvent>>,
) {
let mut ctx = CONTEXT.lock().unwrap();
let parent = props.parent.clone();
ctx.window_map.insert(
hwnd.0,
Object {
props,
kind,
sender,
raw_input_sender,
children: vec![],
},
);
if let Some(parent) = parent {
let parent_handle = HWND(parent.raw_handle() as _);
if let Some(parent) = ctx.window_map.get_mut(&parent_handle.0) {
parent.children.push(hwnd);
}
}
}
pub fn send_event(hwnd: HWND, event: Event) {
let ctx = CONTEXT.lock().unwrap();
let Some(obj) = ctx.window_map.get(&hwnd.0) else {
return;
};
obj.sender.send((event, obj.kind.clone())).unwrap_or(());
}
pub fn send_raw_input_event(hwnd: HWND, event: raw_input::RawInputEvent) {
let ctx = CONTEXT.lock().unwrap();
let Some(obj) = ctx.window_map.get(&hwnd.0) else {
return;
};
if let Some(sender) = obj.raw_input_sender.as_ref() {
sender.send((event, obj.kind.clone())).unwrap_or(());
}
}
pub fn quit() {
let ctx = CONTEXT.lock().unwrap();
for (_, obj) in ctx.window_map.iter() {
obj.sender
.send((Event::Closed, obj.kind.clone()))
.unwrap_or(());
if let Some(sender) = obj.raw_input_sender.as_ref() {
sender
.send((raw_input::RawInputEvent::Closed, obj.kind.clone()))
.unwrap_or(());
}
}
}
pub fn remove_window(hwnd: HWND) -> Option<Object> {
let mut ctx = CONTEXT.lock().unwrap();
let obj = ctx.window_map.remove(&hwnd.0);
if let Some(obj) = obj.as_ref() {
for child in &obj.children {
unsafe {
PostMessageW(*child, WM_CLOSE, WPARAM(0), LPARAM(0)).ok();
}
}
if let Some(parent) = obj.props.parent.clone() {
if let Some(parent_obj) = ctx.window_map.get_mut(&(parent.raw_handle() as _)) {
if let Some(index) = parent_obj
.children
.iter()
.position(|child| child == &HWND(parent.raw_handle() as _))
{
parent_obj.children.remove(index);
}
}
}
}
obj
}
pub fn get_window_property<F, T>(hwnd: HWND, f: F) -> Option<T>
where
F: FnOnce(&WindowProperties) -> T,
{
let ctx = CONTEXT.lock().unwrap();
ctx.window_map.get(&hwnd.0).map(|obj| f(&obj.props))
}
pub fn set_window_property<F>(hwnd: HWND, f: F)
where
F: FnOnce(&mut WindowProperties),
{
let mut ctx = CONTEXT.lock().unwrap();
if let Some(obj) = ctx.window_map.get_mut(&hwnd.0) {
f(&mut obj.props);
}
}
pub fn window_is_closed(hwnd: HWND) -> bool {
let ctx = CONTEXT.lock().unwrap();
!ctx.window_map.contains_key(&hwnd.0)
}
}