wiard 0.8.0

Window handling library for Windows in Rust
Documentation
use super::*;
use std::sync::atomic::{self, AtomicU32};
use windows::Win32::Foundation::HINSTANCE;
use windows::Win32::System::LibraryLoader::GetModuleHandleW;
use windows::Win32::UI::Shell::*;

#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
struct Id(u32);

impl Id {
    fn new() -> Self {
        static GEN_ID: AtomicU32 = AtomicU32::new(0);
        Self(GEN_ID.fetch_add(1, atomic::Ordering::SeqCst))
    }
}

pub struct Builder<'a> {
    window: WindowHandle,
    icon: Option<&'a Icon>,
    tip: Option<String>,
}

impl Builder<'_> {
    fn new(window: &impl IsWindow) -> Self {
        Self {
            window: window.window_handle(),
            icon: None,
            tip: None,
        }
    }

    #[inline]
    pub fn icon(self, icon: &Icon) -> Builder<'_> {
        Builder {
            window: self.window,
            icon: Some(icon),
            tip: self.tip,
        }
    }

    #[inline]
    pub fn tip(mut self, s: impl Into<String>) -> Self {
        self.tip = Some(s.into());
        self
    }

    #[inline]
    pub fn build(self) -> Result<NotifyIcon> {
        let id = Id::new();
        unsafe {
            let mut data = NOTIFYICONDATAW {
                cbSize: std::mem::size_of::<NOTIFYICONDATAW>() as u32,
                hWnd: self.window.as_hwnd(),
                uID: id.0,
                uFlags: NIF_MESSAGE,
                uCallbackMessage: WM_APP_NOTIFY_ICON,
                Anonymous: NOTIFYICONDATAW_0 {
                    uVersion: NOTIFYICON_VERSION_4,
                },
                ..Default::default()
            };
            if let Some(icon) = self.icon {
                let hinstance: Option<HINSTANCE> = Some(GetModuleHandleW(None).unwrap().into());
                data.uFlags |= NIF_ICON;
                data.hIcon = icon.load(hinstance)?;
            }
            if let Some(tip) = self.tip {
                data.uFlags |= NIF_TIP | NIF_SHOWTIP;
                let tip = tip
                    .encode_utf16()
                    .take(data.szTip.len() - 1)
                    .chain(std::iter::once(0));
                for (i, c) in tip.enumerate() {
                    data.szTip[i] = c;
                }
            }
            Shell_NotifyIconW(NIM_ADD, &data).ok()?;
            Shell_NotifyIconW(NIM_SETVERSION, &data).ok()?;
        }
        Ok(NotifyIcon { id })
    }
}

#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
pub struct NotifyIcon {
    id: Id,
}

impl NotifyIcon {
    #[inline]
    #[allow(clippy::new_ret_no_self)]
    pub fn new(window: &impl IsWindow) -> Builder<'_> {
        Builder::new(window)
    }

    pub(crate) fn from_id(id: u32) -> Self {
        Self { id: Id(id) }
    }

    #[inline]
    pub fn delete(self) -> bool {
        unsafe {
            let nid = NOTIFYICONDATAW {
                cbSize: std::mem::size_of::<NOTIFYICONDATAW>() as u32,
                uID: self.id.0,
                uCallbackMessage: WM_APP_NOTIFY_ICON,
                ..Default::default()
            };
            Shell_NotifyIconW(NIM_DELETE, &nid).as_bool()
        }
    }
}

pub mod event {
    use super::*;

    #[derive(Clone, Debug)]
    pub struct MouseInput {
        pub button: MouseButton,
        pub button_state: ButtonState,
        pub position: ScreenPosition<i32>,
    }
}

#[derive(Clone, Debug)]
pub enum NotifyIconEvent {
    MouseInput(event::MouseInput),
    CursorMoved(ScreenPosition<i32>),
    ContextMenu(ScreenPosition<i32>),
    PopupOpen(ScreenPosition<i32>),
    PopupClose,
    Select(ScreenPosition<i32>),
    KeySelect(ScreenPosition<i32>),
    Other(super::event::Other),
}