use std::any::Any;
use egui::{Color32, Id, WidgetText};
use crate::*;
pub trait Dialog<Reply> {
fn show(&mut self, ctx: &egui::Context, dctx: &DialogContext) -> Option<Reply>;
}
pub struct DialogDetails<'a, Reply>
where
Reply: 'a + Any,
{
pub(crate) dialog: Box<dyn Dialog<Reply> + 'a>,
pub(crate) mask: Option<Color32>,
pub(crate) id: Option<Id>,
}
impl<'a, Reply> DialogDetails<'a, Reply>
where
Reply: 'a + Any,
{
#[inline]
pub fn new(dialog: impl Dialog<Reply> + 'a) -> Self {
Self::new_dyn(Box::new(dialog))
}
pub fn new_dyn(dialog: Box<dyn Dialog<Reply> + 'a>) -> Self {
Self {
dialog,
mask: Some(Color32::from_black_alpha(0x80)),
id: None,
}
}
#[inline]
pub fn on_reply<R: Any>(self, handler: impl FnOnce(Reply) -> R + 'a) -> DialogDetails<'a, R> {
self.on_reply_dyn(Box::new(handler))
}
#[inline]
pub fn on_reply_dyn<R: Any>(
self,
handler: Box<dyn FnOnce(Reply) -> R + 'a>,
) -> DialogDetails<'a, R> {
struct MappedDialog<'m, From, To> {
dialog: Box<dyn Dialog<From> + 'm>,
mapper: Option<Box<dyn FnOnce(From) -> To + 'm>>,
}
impl<'m, From, To> Dialog<To> for MappedDialog<'m, From, To> {
fn show(&mut self, ctx: &egui::Context, dctx: &DialogContext) -> Option<To> {
self.dialog
.show(ctx, dctx)
.and_then(|from| self.mapper.take().map(|mapper| (mapper)(from)))
}
}
DialogDetails {
dialog: Box::new(MappedDialog {
dialog: self.dialog,
mapper: Some(handler),
}),
mask: self.mask,
id: self.id,
}
}
#[inline]
pub fn with_mask(mut self, mask: Option<Color32>) -> Self {
self.mask = mask;
self
}
#[inline]
pub fn mask(&self) -> Option<Color32> {
self.mask
}
#[inline]
pub fn with_id(mut self, id: impl Into<Id>) -> Self {
self.id = Some(id.into());
self
}
#[inline]
pub fn id(&self) -> Option<Id> {
self.id
}
#[inline]
pub fn show(self, dialogs: &mut Dialogs<'a>) {
dialogs.add(self);
}
#[inline]
pub fn show_if_absent(self, dialogs: &mut Dialogs<'a>) {
dialogs.add_if_absent(self);
}
}
pub type StandardDialogDetails<'a> = DialogDetails<'a, StandardReply>;
impl StandardDialogDetails<'_> {
#[inline]
pub fn info(title: impl Into<WidgetText>, message: impl Into<WidgetText>) -> Self {
StandardDialogDetails::new(StandardDialog::info(title, message))
}
#[inline]
pub fn success(title: impl Into<WidgetText>, message: impl Into<WidgetText>) -> Self {
StandardDialogDetails::new(StandardDialog::success(title, message))
}
#[inline]
pub fn confirm(title: impl Into<WidgetText>, message: impl Into<WidgetText>) -> Self {
StandardDialogDetails::new(StandardDialog::confirm(title, message))
}
#[inline]
pub fn warning(title: impl Into<WidgetText>, message: impl Into<WidgetText>) -> Self {
StandardDialogDetails::new(StandardDialog::warning(title, message))
}
#[inline]
pub fn error(title: impl Into<WidgetText>, message: impl Into<WidgetText>) -> Self {
StandardDialogDetails::new(StandardDialog::error(title, message))
}
}
impl<'a> StandardDialogDetails<'a> {
#[inline]
pub fn on_accepted(self, handler: impl FnOnce() + 'a) -> Self {
self.on_reply(|reply| {
if reply.accepted() {
(handler)();
}
reply
})
}
#[inline]
pub fn on_rejected(self, handler: impl FnOnce() + 'a) -> Self {
self.on_reply(|reply| {
if reply.rejected() {
(handler)();
}
reply
})
}
#[inline]
pub fn map_accepted<R: Any>(
self,
handler: impl FnOnce(bool) -> R + 'a,
) -> DialogDetails<'a, R> {
self.on_reply(|reply| (handler)(reply.accepted()))
}
#[inline]
pub fn map_rejected<R: Any>(
self,
handler: impl FnOnce(bool) -> R + 'a,
) -> DialogDetails<'a, R> {
self.on_reply(|reply| (handler)(reply.rejected()))
}
#[inline]
pub fn into_accepted(self) -> DialogDetails<'a, bool> {
self.on_reply(StandardReply::accepted)
}
#[inline]
pub fn into_rejected(self) -> DialogDetails<'a, bool> {
self.on_reply(StandardReply::rejected)
}
#[inline]
pub fn match_accepted<R: Any>(self, accepted: R, rejected: R) -> DialogDetails<'a, R> {
self.on_reply(|reply| if reply.accepted() { accepted } else { rejected })
}
}