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