#![allow(clippy::bad_bit_mask)]
use std::error;
use std::ffi::{CString, NulError};
use std::fmt;
use std::os::raw::{c_char, c_int};
use std::ptr;
use crate::get_error;
use crate::video::Window;
use crate::sys;
bitflags! {
pub struct MessageBoxFlag: u32 {
const ERROR =
sys::SDL_MessageBoxFlags::SDL_MESSAGEBOX_ERROR as u32;
const WARNING =
sys::SDL_MessageBoxFlags::SDL_MESSAGEBOX_WARNING as u32;
const INFORMATION =
sys::SDL_MessageBoxFlags::SDL_MESSAGEBOX_INFORMATION as u32;
}
}
bitflags! {
pub struct MessageBoxButtonFlag: u32 {
const ESCAPEKEY_DEFAULT =
sys::SDL_MessageBoxButtonFlags::SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT as u32;
const RETURNKEY_DEFAULT =
sys::SDL_MessageBoxButtonFlags::SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT as u32;
const NOTHING = 0;
}
}
#[derive(Debug)]
pub struct MessageBoxColorScheme {
pub background: (u8, u8, u8),
pub text: (u8, u8, u8),
pub button_border: (u8, u8, u8),
pub button_background: (u8, u8, u8),
pub button_selected: (u8, u8, u8),
}
impl From<MessageBoxColorScheme> for sys::SDL_MessageBoxColorScheme {
fn from(val: MessageBoxColorScheme) -> Self {
sys::SDL_MessageBoxColorScheme { colors: val.into() }
}
}
impl From<sys::SDL_MessageBoxColorScheme> for MessageBoxColorScheme {
fn from(prim: sys::SDL_MessageBoxColorScheme) -> MessageBoxColorScheme {
prim.colors.into()
}
}
#[derive(Debug)]
pub struct ButtonData<'a> {
pub flags: MessageBoxButtonFlag,
pub button_id: i32,
pub text: &'a str,
}
#[derive(Debug)]
pub enum ClickedButton<'a> {
CloseButton,
CustomButton(&'a ButtonData<'a>),
}
impl From<MessageBoxColorScheme> for [sys::SDL_MessageBoxColor; 5] {
fn from(scheme: MessageBoxColorScheme) -> [sys::SDL_MessageBoxColor; 5] {
fn to_message_box_color(t: (u8, u8, u8)) -> sys::SDL_MessageBoxColor {
sys::SDL_MessageBoxColor {
r: t.0,
g: t.1,
b: t.2,
}
}
[
to_message_box_color(scheme.background),
to_message_box_color(scheme.text),
to_message_box_color(scheme.button_border),
to_message_box_color(scheme.button_background),
to_message_box_color(scheme.button_selected),
]
}
}
impl From<[sys::SDL_MessageBoxColor; 5]> for MessageBoxColorScheme {
fn from(val: [sys::SDL_MessageBoxColor; 5]) -> Self {
fn from_message_box_color(prim_color: sys::SDL_MessageBoxColor) -> (u8, u8, u8) {
(prim_color.r, prim_color.g, prim_color.b)
}
MessageBoxColorScheme {
background: from_message_box_color(val[0]),
text: from_message_box_color(val[1]),
button_border: from_message_box_color(val[2]),
button_background: from_message_box_color(val[3]),
button_selected: from_message_box_color(val[4]),
}
}
}
#[derive(Debug, Clone)]
pub enum ShowMessageError {
InvalidTitle(NulError),
InvalidMessage(NulError),
InvalidButton(NulError, i32),
SdlError(String),
}
impl fmt::Display for ShowMessageError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::ShowMessageError::*;
match *self {
InvalidTitle(ref e) => write!(f, "Invalid title: {}", e),
InvalidMessage(ref e) => write!(f, "Invalid message: {}", e),
InvalidButton(ref e, value) => write!(f, "Invalid button ({}): {}", value, e),
SdlError(ref e) => write!(f, "SDL error: {}", e),
}
}
}
impl error::Error for ShowMessageError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
Self::InvalidTitle(err) => Some(err),
Self::InvalidMessage(err) => Some(err),
Self::InvalidButton(err, _) => Some(err),
Self::SdlError(_) => None,
}
}
}
#[doc(alias = "SDL_ShowSimpleMessageBox")]
pub fn show_simple_message_box<'a, W>(
flags: MessageBoxFlag,
title: &str,
message: &str,
window: W,
) -> Result<(), ShowMessageError>
where
W: Into<Option<&'a Window>>,
{
use self::ShowMessageError::*;
let result = unsafe {
let title = match CString::new(title) {
Ok(s) => s,
Err(err) => return Err(InvalidTitle(err)),
};
let message = match CString::new(message) {
Ok(s) => s,
Err(err) => return Err(InvalidMessage(err)),
};
sys::SDL_ShowSimpleMessageBox(
flags.bits(),
title.as_ptr() as *const c_char,
message.as_ptr() as *const c_char,
window.into().map_or(ptr::null_mut(), |win| win.raw()),
)
} == 0;
if result {
Ok(())
} else {
Err(SdlError(get_error()))
}
}
#[doc(alias = "SDL_ShowMessageBox")]
pub fn show_message_box<'a, 'b, W, M>(
flags: MessageBoxFlag,
buttons: &'a [ButtonData],
title: &str,
message: &str,
window: W,
scheme: M,
) -> Result<ClickedButton<'a>, ShowMessageError>
where
W: Into<Option<&'b Window>>,
M: Into<Option<MessageBoxColorScheme>>,
{
let window = window.into();
let scheme = scheme.into();
use self::ShowMessageError::*;
let mut button_id: c_int = 0;
let title = match CString::new(title) {
Ok(s) => s,
Err(err) => return Err(InvalidTitle(err)),
};
let message = match CString::new(message) {
Ok(s) => s,
Err(err) => return Err(InvalidMessage(err)),
};
let button_texts: Result<Vec<_>, (_, i32)> = buttons
.iter()
.map(|b| CString::new(b.text).map_err(|e| (e, b.button_id)))
.collect(); let button_texts = match button_texts {
Ok(b) => b,
Err(e) => return Err(InvalidButton(e.0, e.1)),
};
let raw_buttons: Vec<sys::SDL_MessageBoxButtonData> = buttons
.iter()
.zip(button_texts.iter())
.map(|(b, b_text)| sys::SDL_MessageBoxButtonData {
flags: b.flags.bits(),
buttonid: b.button_id as c_int,
text: b_text.as_ptr(),
})
.collect();
let result = unsafe {
let scheme = scheme.map(|scheme| sys::SDL_MessageBoxColorScheme {
colors: scheme.into(),
});
let msg_box_data = sys::SDL_MessageBoxData {
flags: flags.bits(),
window: window.map_or(ptr::null_mut(), |win| win.raw()),
title: title.as_ptr() as *const c_char,
message: message.as_ptr() as *const c_char,
numbuttons: raw_buttons.len() as c_int,
buttons: raw_buttons.as_ptr(),
colorScheme: scheme
.as_ref()
.map(|p| p as *const _)
.unwrap_or(ptr::null()),
};
sys::SDL_ShowMessageBox(&msg_box_data as *const _, &mut button_id as &mut _)
} == 0;
if result {
match button_id {
-1 => Ok(ClickedButton::CloseButton),
id => {
let button = buttons.iter().find(|b| b.button_id == id);
Ok(ClickedButton::CustomButton(button.unwrap()))
}
}
} else {
Err(SdlError(get_error()))
}
}