use std::sync::atomic::{AtomicU64, Ordering};
use egui::Color32;
use crate::icons::{Icon, icons};
use crate::tokens::DESIGN_TOKENS;
static TOAST_ID_COUNTER: AtomicU64 = AtomicU64::new(0);
fn next_toast_id() -> u64 {
TOAST_ID_COUNTER.fetch_add(1, Ordering::Relaxed)
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum ToastKind {
#[default]
Info,
Success,
Warning,
Error,
}
impl ToastKind {
pub fn bg_color(&self) -> Color32 {
match self {
ToastKind::Info => DESIGN_TOKENS.semantic.extended.info.linear_multiply(0.9),
ToastKind::Success => DESIGN_TOKENS.semantic.extended.success.linear_multiply(0.9),
ToastKind::Warning => DESIGN_TOKENS.semantic.extended.warning.linear_multiply(0.9),
ToastKind::Error => DESIGN_TOKENS.semantic.extended.error.linear_multiply(0.9),
}
}
pub fn text_color(&self) -> Color32 {
DESIGN_TOKENS.semantic.ui.text_light
}
pub fn icon(&self) -> &'static Icon {
match self {
ToastKind::Info => &icons::INFO,
ToastKind::Success => &icons::SETTINGS_STATUS_LINE,
ToastKind::Warning => &icons::STATUS_ERROR,
ToastKind::Error => &icons::STATUS_ERROR,
}
}
}
#[derive(Clone, Debug)]
pub struct Toast {
pub id: u64,
pub kind: ToastKind,
pub message: String,
pub title: Option<String>,
pub duration: f32,
pub created_at: f64,
pub dismissible: bool,
}
impl Toast {
pub fn new(kind: ToastKind, message: impl Into<String>) -> Self {
Self {
id: next_toast_id(),
kind,
message: message.into(),
title: None,
duration: 5.0, created_at: 0.0,
dismissible: true,
}
}
pub fn info(message: impl Into<String>) -> Self {
Self::new(ToastKind::Info, message)
}
pub fn success(message: impl Into<String>) -> Self {
Self::new(ToastKind::Success, message)
}
pub fn warning(message: impl Into<String>) -> Self {
Self::new(ToastKind::Warning, message)
}
pub fn error(message: impl Into<String>) -> Self {
Self::new(ToastKind::Error, message)
}
pub fn with_title(mut self, title: impl Into<String>) -> Self {
self.title = Some(title.into());
self
}
pub fn duration(mut self, seconds: f32) -> Self {
self.duration = seconds;
self
}
pub fn permanent(mut self) -> Self {
self.duration = 0.0;
self
}
pub fn dismissible(mut self, dismissible: bool) -> Self {
self.dismissible = dismissible;
self
}
pub fn is_expired(&self, current_time: f64) -> bool {
if self.duration <= 0.0 {
return false; }
current_time - self.created_at >= self.duration as f64
}
pub fn remaining_fraction(&self, current_time: f64) -> f32 {
if self.duration <= 0.0 {
return 1.0; }
let elapsed = current_time - self.created_at;
(1.0 - (elapsed / self.duration as f64) as f32).clamp(0.0, 1.0)
}
}