use crate::{Anchor, TOAST_HEIGHT, TOAST_WIDTH};
use egui::{pos2, vec2, FontId, Pos2, Rect};
use std::{fmt::Debug, time::Duration};
#[derive(Debug, Default, Clone, PartialEq, Eq)]
#[allow(missing_docs)]
pub enum ToastLevel {
#[default]
Info,
Warning,
Error,
Success,
None,
Custom(String, egui::Color32),
}
#[derive(Debug)]
pub(crate) enum ToastState {
Appear,
Disapper,
Disappeared,
Idle,
}
impl ToastState {
pub fn appearing(&self) -> bool {
matches!(self, Self::Appear)
}
pub fn disappearing(&self) -> bool {
matches!(self, Self::Disapper)
}
pub fn disappeared(&self) -> bool {
matches!(self, Self::Disappeared)
}
pub fn idling(&self) -> bool {
matches!(self, Self::Idle)
}
}
pub struct ToastOptions {
duration: Option<Duration>,
level: ToastLevel,
closable: bool,
show_progress_bar: bool,
}
#[derive(Debug)]
pub struct Toast {
pub(crate) level: ToastLevel,
pub(crate) caption: String,
pub(crate) font: Option<FontId>,
pub(crate) duration: Option<(f32, f32)>,
pub(crate) height: f32,
pub(crate) width: f32,
pub(crate) closable: bool,
pub(crate) show_progress_bar: bool,
pub(crate) state: ToastState,
pub(crate) value: f32,
}
impl Default for ToastOptions {
fn default() -> Self {
Self {
duration: Some(Duration::from_millis(3500)),
level: ToastLevel::None,
closable: true,
show_progress_bar: true,
}
}
}
fn duration_to_seconds_f32(duration: Duration) -> f32 {
duration.as_nanos() as f32 * 1e-9
}
impl Toast {
fn new(caption: impl Into<String>, options: ToastOptions) -> Self {
Self {
caption: caption.into(),
height: TOAST_HEIGHT,
width: TOAST_WIDTH,
duration: if let Some(dur) = options.duration {
let max_dur = duration_to_seconds_f32(dur);
Some((max_dur, max_dur))
} else {
None
},
closable: options.closable,
show_progress_bar: options.show_progress_bar,
level: options.level,
value: 0.,
state: ToastState::Appear,
font: None,
}
}
pub fn basic(caption: impl Into<String>) -> Self {
Self::new(caption, ToastOptions::default())
}
pub fn success(caption: impl Into<String>) -> Self {
Self::new(
caption,
ToastOptions {
level: ToastLevel::Success,
..ToastOptions::default()
},
)
}
pub fn info(caption: impl Into<String>) -> Self {
Self::new(
caption,
ToastOptions {
level: ToastLevel::Info,
..ToastOptions::default()
},
)
}
pub fn warning(caption: impl Into<String>) -> Self {
Self::new(
caption,
ToastOptions {
level: ToastLevel::Warning,
..ToastOptions::default()
},
)
}
pub fn error(caption: impl Into<String>) -> Self {
Self::new(
caption,
ToastOptions {
closable: false,
level: ToastLevel::Error,
..ToastOptions::default()
},
)
}
pub fn custom(caption: impl Into<String>, level: ToastLevel) -> Self {
Self::new(
caption,
ToastOptions {
level,
..ToastOptions::default()
},
)
}
pub fn set_options(&mut self, options: ToastOptions) -> &mut Self {
self.set_closable(options.closable);
self.set_duration(options.duration);
self.set_level(options.level);
self
}
pub fn set_level(&mut self, level: ToastLevel) -> &mut Self {
self.level = level;
self
}
pub fn set_font(&mut self, font: FontId) -> &mut Self {
self.font = Some(font);
self
}
pub fn set_closable(&mut self, closable: bool) -> &mut Self {
self.closable = closable;
self
}
pub fn set_show_progress_bar(&mut self, show_progress_bar: bool) -> &mut Self {
self.show_progress_bar = show_progress_bar;
self
}
pub fn set_duration(&mut self, duration: Option<Duration>) -> &mut Self {
if let Some(duration) = duration {
let max_dur = duration_to_seconds_f32(duration);
self.duration = Some((max_dur, max_dur));
} else {
self.duration = None;
}
self
}
pub fn set_height(&mut self, height: f32) -> &mut Self {
self.height = height;
self
}
pub fn set_width(&mut self, width: f32) -> &mut Self {
self.width = width;
self
}
pub fn dismiss(&mut self) {
self.state = ToastState::Disapper;
}
pub(crate) fn calc_anchored_rect(&self, pos: Pos2, anchor: Anchor) -> Rect {
match anchor {
Anchor::TopRight => Rect {
min: pos2(pos.x - self.width, pos.y),
max: pos2(pos.x, pos.y + self.height),
},
Anchor::TopLeft => Rect {
min: pos,
max: pos + vec2(self.width, self.height),
},
Anchor::BottomRight => Rect {
min: pos - vec2(self.width, self.height),
max: pos,
},
Anchor::BottomLeft => Rect {
min: pos2(pos.x, pos.y - self.height),
max: pos2(pos.x + self.width, pos.y),
},
}
}
pub(crate) fn adjust_next_pos(&self, pos: &mut Pos2, anchor: Anchor, spacing: f32) {
match anchor {
Anchor::TopRight | Anchor::TopLeft => pos.y += self.height + spacing,
Anchor::BottomRight | Anchor::BottomLeft => pos.y -= self.height + spacing,
}
}
}