#![deny(missing_docs)]
#![deny(clippy::cargo)]
use std::marker::PhantomData;
use windows_sys::Win32::{
Foundation::{GetLastError, HWND},
UI::WindowsAndMessaging::{
MessageBoxW, MB_APPLMODAL, MB_DEFAULT_DESKTOP_ONLY, MB_DEFBUTTON1, MB_DEFBUTTON2,
MB_DEFBUTTON3, MB_DEFBUTTON4, MB_HELP, MB_ICONASTERISK, MB_ICONERROR, MB_ICONEXCLAMATION,
MB_ICONHAND, MB_ICONINFORMATION, MB_ICONQUESTION, MB_ICONSTOP, MB_ICONWARNING, MB_RIGHT,
MB_RTLREADING, MB_SERVICE_NOTIFICATION, MB_SETFOREGROUND, MB_SYSTEMMODAL, MB_TASKMODAL,
MB_TOPMOST, MESSAGEBOX_RESULT, MESSAGEBOX_STYLE,
},
};
mod abort_retry_ignore;
mod cancel_try_again_continue;
mod okay;
mod okay_cancel;
pub mod raw;
mod retry_cancel;
mod yes_no;
mod yes_no_cancel;
pub use abort_retry_ignore::*;
pub use cancel_try_again_continue::*;
pub use okay::*;
pub use okay_cancel::*;
pub use retry_cancel::*;
pub use yes_no::*;
pub use yes_no_cancel::*;
pub type Error = windows_sys::Win32::Foundation::WIN32_ERROR;
pub type Result<T> = core::result::Result<T, Error>;
pub trait Options: From<MESSAGEBOX_RESULT> {
fn flags() -> MESSAGEBOX_STYLE;
}
#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash)]
#[repr(u32)] pub enum Icon {
Exclamation,
Warning,
Information,
Asterisk,
Question,
Stop,
Error,
Hand,
}
impl Icon {
fn style(self) -> MESSAGEBOX_STYLE {
match self {
Icon::Exclamation => MB_ICONEXCLAMATION,
Icon::Warning => MB_ICONWARNING,
Icon::Information => MB_ICONINFORMATION,
Icon::Asterisk => MB_ICONASTERISK,
Icon::Question => MB_ICONQUESTION,
Icon::Stop => MB_ICONSTOP,
Icon::Error => MB_ICONERROR,
Icon::Hand => MB_ICONHAND,
}
}
}
#[derive(Debug, Default, Eq, PartialEq, Clone, Copy, Hash)]
#[repr(u32)] pub enum Modal {
#[default]
Application = MB_APPLMODAL,
System = MB_SYSTEMMODAL,
Task = MB_TASKMODAL,
}
#[derive(Debug, Default, Eq, PartialEq, Clone, Copy, Hash)]
#[repr(u32)] pub enum DefaultButton {
#[default]
DefaultButton1 = MB_DEFBUTTON1,
DefaultButton2 = MB_DEFBUTTON2,
DefaultButton3 = MB_DEFBUTTON3,
DefaultButton4 = MB_DEFBUTTON4,
}
pub struct MessageBox<'a, T> {
icon: Icon,
text: &'a str,
title: Option<&'a str>,
hwnd: HWND,
flags: MESSAGEBOX_STYLE,
_response: PhantomData<T>,
}
impl<T> std::fmt::Debug for MessageBox<'_, T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("MessageBox")
.field("title", &self.title)
.field("text", &self.text)
.field("icon", &self.icon)
.field("hwnd", &self.hwnd)
.finish()
}
}
macro_rules! ctors {
($($name:ident => $icon:ident),*) => {
impl <'a, T> MessageBox<'a, T> {
$(
#[doc = concat!("Creates a new message box where its icon is set to [", stringify!($icon), "](Icon::", stringify!($icon),").")]
pub fn $name(text: &'a str) -> Self {
Self::new(text).icon(Icon::$icon)
}
)*
}
$(
#[doc = concat!("Creates a new message box where its icon is set to [", stringify!($icon), "](Icon::", stringify!($icon),").")]
pub fn $name<T>(text: &str) -> MessageBox<'_, T> {
MessageBox::<T>::$name(text)
})*
};
}
impl<'a, T> MessageBox<'a, T> {
pub fn new(text: &'a str) -> Self {
Self {
icon: Icon::Information,
text,
title: None,
hwnd: std::ptr::null_mut(),
flags: 0,
_response: PhantomData,
}
}
pub fn icon(mut self, icon: Icon) -> Self {
self.icon = icon;
self
}
pub fn title(mut self, title: &'a str) -> Self {
self.title = title.into();
self
}
pub fn hwnd(mut self, hwnd: HWND) -> Self {
self.hwnd = hwnd;
self
}
pub fn modal(mut self, modal: Modal) -> Self {
self.flags |= modal as u32;
self
}
pub fn default_button(mut self, btn: DefaultButton) -> Self {
self.flags |= btn as u32;
self
}
pub fn default_desktop_only(mut self) -> Self {
self.flags |= MB_DEFAULT_DESKTOP_ONLY;
self
}
pub fn right(mut self) -> Self {
self.flags |= MB_RIGHT;
self
}
pub fn rtl_reading(mut self) -> Self {
self.flags |= MB_RTLREADING;
self
}
pub fn set_foreground(mut self) -> Self {
self.flags |= MB_SETFOREGROUND;
self
}
pub fn topmost(mut self) -> Self {
self.flags |= MB_TOPMOST;
self
}
pub fn service_notification(mut self) -> Self {
self.flags |= MB_SERVICE_NOTIFICATION;
self
}
pub fn with_help(mut self) -> Self {
self.flags |= MB_HELP;
self
}
}
impl<T: Options> MessageBox<'_, T> {
pub fn show(self) -> Result<T> {
let text: Vec<_> = self.text.encode_utf16().chain(std::iter::once(0)).collect();
let title = match self.title {
Some(t) => t.encode_utf16().chain(std::iter::once(0)).collect(),
None => Vec::new(),
};
let return_code = unsafe {
MessageBoxW(
self.hwnd,
text.as_ptr(),
if title.is_empty() {
std::ptr::null()
} else {
title.as_ptr()
},
T::flags() | self.icon.style() | self.flags,
)
};
match return_code {
0 => Err(unsafe { GetLastError() }),
x => Ok(T::from(x)),
}
}
}
ctors! {
exclamation => Exclamation,
warning => Warning,
information => Information,
asterisk => Asterisk,
question => Question,
stop => Stop,
error => Error,
hand => Hand
}
pub fn show<T: Options>(text: &str) -> Result<T> {
MessageBox::new(text).show()
}