1use bevy_egui::egui;
2
3use crate::{Anchor, TOAST_HEIGHT, TOAST_WIDTH};
4use egui::{pos2, vec2, FontId, Pos2, Rect};
5use std::{fmt::Debug, time::Duration};
6
7#[derive(Debug, Default, Clone, PartialEq, Eq)]
9#[allow(missing_docs)]
10pub enum ToastLevel {
11 #[default]
12 Info,
13 Warning,
14 Error,
15 Success,
16 None,
17 Custom(String, egui::Color32),
18}
19
20#[derive(Debug)]
21pub enum ToastState {
23 Appear,
25 Disappear,
27 Disappeared,
29 Idle,
31}
32
33impl ToastState {
34 pub const fn appearing(&self) -> bool {
36 matches!(self, Self::Appear)
37 }
38
39 pub const fn disappearing(&self) -> bool {
41 matches!(self, Self::Disappear)
42 }
43
44 pub const fn disappeared(&self) -> bool {
46 matches!(self, Self::Disappeared)
47 }
48
49 pub const fn idling(&self) -> bool {
51 matches!(self, Self::Idle)
52 }
53}
54
55pub struct ToastOptions {
57 duration: Option<Duration>,
58 level: ToastLevel,
59 closable: bool,
60 show_progress_bar: bool,
61}
62
63#[derive(Debug)]
65pub struct Toast {
66 pub(crate) level: ToastLevel,
67 pub(crate) caption: String,
68 pub(crate) font: Option<FontId>,
69 pub(crate) duration: Option<(f32, f32)>,
71 pub(crate) height: f32,
72 pub(crate) width: f32,
73 pub(crate) closable: bool,
74 pub(crate) show_progress_bar: bool,
75
76 pub(crate) state: ToastState,
77 pub(crate) value: f32,
78}
79
80impl Default for ToastOptions {
81 fn default() -> Self {
82 Self {
83 duration: Some(Duration::from_millis(3500)),
84 level: ToastLevel::None,
85 closable: true,
86 show_progress_bar: true,
87 }
88 }
89}
90
91fn duration_to_seconds_f32(duration: Duration) -> f32 {
92 duration.as_nanos() as f32 * 1e-9
93}
94
95impl Toast {
96 fn new(caption: impl Into<String>, options: ToastOptions) -> Self {
97 Self {
98 caption: caption.into(),
99 height: TOAST_HEIGHT,
100 width: TOAST_WIDTH,
101 duration: options.duration.map(|dur| {
102 let max_dur = duration_to_seconds_f32(dur);
103 (max_dur, max_dur)
104 }),
105 closable: options.closable,
106 show_progress_bar: options.show_progress_bar,
107 level: options.level,
108
109 value: 0.,
110 state: ToastState::Appear,
111 font: None,
112 }
113 }
114
115 pub fn basic(caption: impl Into<String>) -> Self {
117 Self::new(caption, ToastOptions::default())
118 }
119
120 pub fn success(caption: impl Into<String>) -> Self {
122 Self::new(
123 caption,
124 ToastOptions {
125 level: ToastLevel::Success,
126 ..ToastOptions::default()
127 },
128 )
129 }
130
131 pub fn info(caption: impl Into<String>) -> Self {
133 Self::new(
134 caption,
135 ToastOptions {
136 level: ToastLevel::Info,
137 ..ToastOptions::default()
138 },
139 )
140 }
141
142 pub fn warning(caption: impl Into<String>) -> Self {
144 Self::new(
145 caption,
146 ToastOptions {
147 level: ToastLevel::Warning,
148 ..ToastOptions::default()
149 },
150 )
151 }
152
153 pub fn error(caption: impl Into<String>) -> Self {
155 Self::new(
156 caption,
157 ToastOptions {
158 closable: false,
159 level: ToastLevel::Error,
160 ..ToastOptions::default()
161 },
162 )
163 }
164
165 pub fn custom(caption: impl Into<String>, level: ToastLevel) -> Self {
167 Self::new(
168 caption,
169 ToastOptions {
170 level,
171 ..ToastOptions::default()
172 },
173 )
174 }
175
176 pub fn options(&mut self, options: ToastOptions) -> &mut Self {
178 self.closable(options.closable);
179 self.duration(options.duration);
180 self.level(options.level);
181 self
182 }
183
184 pub fn level(&mut self, level: ToastLevel) -> &mut Self {
186 self.level = level;
187 self
188 }
189
190 pub fn font(&mut self, font: FontId) -> &mut Self {
193 self.font = Some(font);
194 self
195 }
196
197 pub fn closable(&mut self, closable: bool) -> &mut Self {
199 self.closable = closable;
200 self
201 }
202
203 pub fn show_progress_bar(&mut self, show_progress_bar: bool) -> &mut Self {
205 self.show_progress_bar = show_progress_bar;
206 self
207 }
208
209 pub fn duration(&mut self, duration: Option<Duration>) -> &mut Self {
211 if let Some(duration) = duration {
212 let max_dur = duration_to_seconds_f32(duration);
213 self.duration = Some((max_dur, max_dur));
214 } else {
215 self.duration = None;
216 }
217 self
218 }
219
220 pub fn height(&mut self, height: f32) -> &mut Self {
222 self.height = height;
223 self
224 }
225
226 pub fn width(&mut self, width: f32) -> &mut Self {
228 self.width = width;
229 self
230 }
231
232 pub fn dismiss(&mut self) {
234 self.state = ToastState::Disappear;
235 }
236
237 pub(crate) fn calc_anchored_rect(&self, pos: Pos2, anchor: Anchor) -> Rect {
238 match anchor {
239 Anchor::TopRight => Rect {
240 min: pos2(pos.x - self.width, pos.y),
241 max: pos2(pos.x, pos.y + self.height),
242 },
243 Anchor::TopLeft => Rect {
244 min: pos,
245 max: pos + vec2(self.width, self.height),
246 },
247 Anchor::BottomRight => Rect {
248 min: pos - vec2(self.width, self.height),
249 max: pos,
250 },
251 Anchor::BottomLeft => Rect {
252 min: pos2(pos.x, pos.y - self.height),
253 max: pos2(pos.x + self.width, pos.y),
254 },
255 }
256 }
257
258 pub(crate) fn adjust_next_pos(&self, pos: &mut Pos2, anchor: Anchor, spacing: f32) {
259 match anchor {
260 Anchor::TopRight | Anchor::TopLeft => pos.y += self.height + spacing,
261 Anchor::BottomRight | Anchor::BottomLeft => pos.y -= self.height + spacing,
262 }
263 }
264}