use crate::error::Result;
use crate::string::WideString;
use std::cell::RefCell;
use windows::Win32::Foundation::{HWND, LPARAM, LRESULT, WPARAM};
use windows::Win32::Graphics::Gdi::{GetStockObject, HBRUSH, WHITE_BRUSH};
use windows::Win32::System::LibraryLoader::GetModuleHandleW;
use windows::Win32::UI::WindowsAndMessaging::{
CreateWindowExW, DefWindowProcW, DestroyWindow, DispatchMessageW, GetMessageW,
GetWindowLongPtrW, LoadCursorW, PostQuitMessage, RegisterClassExW, SetWindowLongPtrW,
ShowWindow, TranslateMessage, UnregisterClassW, CS_HREDRAW, CS_VREDRAW, CW_USEDEFAULT,
GWLP_USERDATA, IDC_ARROW, MSG, SW_HIDE, SW_SHOW, SW_SHOWDEFAULT, WINDOW_EX_STYLE, WINDOW_STYLE,
WM_CLOSE, WM_CREATE, WM_DESTROY, WM_NCCREATE, WNDCLASSEXW, WS_CAPTION, WS_OVERLAPPEDWINDOW,
WS_SYSMENU, WS_VISIBLE,
};
#[derive(Clone, Copy, Debug)]
pub struct Style(pub WINDOW_STYLE);
impl Style {
pub const OVERLAPPED: Self = Self(WS_OVERLAPPEDWINDOW);
pub const CAPTION: Self = Self(WS_CAPTION);
pub const SYSMENU: Self = Self(WS_SYSMENU);
pub const VISIBLE: Self = Self(WS_VISIBLE);
pub fn with(self, other: Self) -> Self {
Self(WINDOW_STYLE(self.0 .0 | other.0 .0))
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct ExStyle(pub WINDOW_EX_STYLE);
impl ExStyle {
pub const NONE: Self = Self(WINDOW_EX_STYLE(0));
pub fn with(self, other: Self) -> Self {
Self(WINDOW_EX_STYLE(self.0 .0 | other.0 .0))
}
}
#[derive(Clone, Copy, Debug)]
pub struct ShowCommand(pub windows::Win32::UI::WindowsAndMessaging::SHOW_WINDOW_CMD);
impl ShowCommand {
pub const DEFAULT: Self = Self(SW_SHOWDEFAULT);
pub const SHOW: Self = Self(SW_SHOW);
pub const HIDE: Self = Self(SW_HIDE);
}
#[derive(Clone, Copy, Debug)]
pub struct Message {
pub hwnd: HWND,
pub msg: u32,
pub wparam: WPARAM,
pub lparam: LPARAM,
}
impl Message {
pub const CREATE: u32 = WM_CREATE;
pub const DESTROY: u32 = WM_DESTROY;
pub const CLOSE: u32 = WM_CLOSE;
}
pub trait MessageHandler {
fn handle_message(&mut self, msg: Message) -> Option<LRESULT>;
fn on_create(&mut self, _hwnd: HWND) -> bool {
true
}
fn on_destroy(&mut self) {}
fn on_close(&mut self, hwnd: HWND) -> bool {
unsafe {
let _ = DestroyWindow(hwnd);
}
true
}
}
pub struct DefaultHandler;
impl MessageHandler for DefaultHandler {
fn handle_message(&mut self, _msg: Message) -> Option<LRESULT> {
None
}
}
pub struct WindowBuilder {
class_name: String,
title: String,
style: Style,
ex_style: ExStyle,
x: i32,
y: i32,
width: i32,
height: i32,
}
impl Default for WindowBuilder {
fn default() -> Self {
Self::new()
}
}
impl WindowBuilder {
pub fn new() -> Self {
Self {
class_name: String::new(),
title: String::from("Window"),
style: Style::OVERLAPPED,
ex_style: ExStyle::NONE,
x: CW_USEDEFAULT,
y: CW_USEDEFAULT,
width: CW_USEDEFAULT,
height: CW_USEDEFAULT,
}
}
pub fn class_name(mut self, name: impl Into<String>) -> Self {
self.class_name = name.into();
self
}
pub fn title(mut self, title: impl Into<String>) -> Self {
self.title = title.into();
self
}
pub fn style(mut self, style: Style) -> Self {
self.style = style;
self
}
pub fn ex_style(mut self, ex_style: ExStyle) -> Self {
self.ex_style = ex_style;
self
}
pub fn position(mut self, x: i32, y: i32) -> Self {
self.x = x;
self.y = y;
self
}
pub fn size(mut self, width: i32, height: i32) -> Self {
self.width = width;
self.height = height;
self
}
pub fn build<H: MessageHandler + 'static>(self, handler: H) -> Result<Window<H>> {
let class_name = if self.class_name.is_empty() {
format!("ErgonomicWindow_{}", std::process::id())
} else {
self.class_name
};
let class_name_wide = WideString::new(&class_name);
let hinstance = unsafe { GetModuleHandleW(None)? };
let wc = WNDCLASSEXW {
cbSize: std::mem::size_of::<WNDCLASSEXW>() as u32,
style: CS_HREDRAW | CS_VREDRAW,
lpfnWndProc: Some(window_proc::<H>),
hInstance: hinstance.into(),
hCursor: unsafe { LoadCursorW(None, IDC_ARROW)? },
hbrBackground: unsafe { HBRUSH(GetStockObject(WHITE_BRUSH).0) },
lpszClassName: class_name_wide.as_pcwstr(),
..Default::default()
};
let atom = unsafe { RegisterClassExW(&wc) };
if atom == 0 {
return Err(crate::error::last_error());
}
let handler = Box::new(RefCell::new(handler));
let handler_ptr = Box::into_raw(handler);
let title_wide = WideString::new(&self.title);
let hwnd = unsafe {
CreateWindowExW(
self.ex_style.0,
class_name_wide.as_pcwstr(),
title_wide.as_pcwstr(),
self.style.0,
self.x,
self.y,
self.width,
self.height,
None,
None,
hinstance,
Some(handler_ptr as *const _),
)?
};
Ok(Window {
hwnd,
class_name: class_name_wide,
handler: handler_ptr,
hinstance,
})
}
}
pub struct Window<H: MessageHandler> {
hwnd: HWND,
class_name: WideString,
handler: *mut RefCell<H>,
hinstance: windows::Win32::Foundation::HMODULE,
}
impl<H: MessageHandler> Window<H> {
#[inline]
pub fn hwnd(&self) -> HWND {
self.hwnd
}
pub fn show(&self, cmd: ShowCommand) {
unsafe {
let _ = ShowWindow(self.hwnd, cmd.0);
}
}
pub fn handler_mut(&self) -> std::cell::RefMut<'_, H> {
unsafe { (*self.handler).borrow_mut() }
}
pub fn handler(&self) -> std::cell::Ref<'_, H> {
unsafe { (*self.handler).borrow() }
}
pub fn destroy(self) {
}
}
impl<H: MessageHandler> Drop for Window<H> {
fn drop(&mut self) {
unsafe {
let _ = DestroyWindow(self.hwnd);
let _ = UnregisterClassW(self.class_name.as_pcwstr(), self.hinstance);
drop(Box::from_raw(self.handler));
}
}
}
unsafe extern "system" fn window_proc<H: MessageHandler>(
hwnd: HWND,
msg: u32,
wparam: WPARAM,
lparam: LPARAM,
) -> LRESULT {
let handler_ptr = GetWindowLongPtrW(hwnd, GWLP_USERDATA) as *mut RefCell<H>;
if msg == WM_NCCREATE {
let create_struct =
&*(lparam.0 as *const windows::Win32::UI::WindowsAndMessaging::CREATESTRUCTW);
let handler_ptr = create_struct.lpCreateParams as *mut RefCell<H>;
SetWindowLongPtrW(hwnd, GWLP_USERDATA, handler_ptr as isize);
return DefWindowProcW(hwnd, msg, wparam, lparam);
}
if handler_ptr.is_null() {
return DefWindowProcW(hwnd, msg, wparam, lparam);
}
let handler = &*handler_ptr;
let message = Message {
hwnd,
msg,
wparam,
lparam,
};
match msg {
WM_CREATE => {
let mut handler = handler.borrow_mut();
if handler.on_create(hwnd) {
LRESULT(0)
} else {
LRESULT(-1)
}
}
WM_DESTROY => {
handler.borrow_mut().on_destroy();
PostQuitMessage(0);
LRESULT(0)
}
WM_CLOSE => {
let mut handler = handler.borrow_mut();
let _ = handler.on_close(hwnd);
LRESULT(0)
}
_ => {
let mut handler = handler.borrow_mut();
if let Some(result) = handler.handle_message(message) {
result
} else {
DefWindowProcW(hwnd, msg, wparam, lparam)
}
}
}
}
pub fn run_message_loop() -> i32 {
let mut msg = MSG::default();
unsafe {
while GetMessageW(&mut msg, None, 0, 0).as_bool() {
let _ = TranslateMessage(&msg);
DispatchMessageW(&msg);
}
}
msg.wParam.0 as i32
}
pub fn process_messages() -> bool {
use windows::Win32::UI::WindowsAndMessaging::{PeekMessageW, PM_REMOVE, WM_QUIT};
let mut msg = MSG::default();
unsafe {
while PeekMessageW(&mut msg, None, 0, 0, PM_REMOVE).as_bool() {
if msg.message == WM_QUIT {
return true;
}
let _ = TranslateMessage(&msg);
DispatchMessageW(&msg);
}
}
false
}