witas 0.11.2

An asynchronous window library in Rust for Windows
Documentation
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)
    }
}