1use iced::{Limits, Size};
5use iced_core::layout::Node;
6
7use iced_core::Element;
8use iced_core::Overlay;
9use iced_core::event::{self, Event};
10use iced_core::layout;
11use iced_core::mouse;
12use iced_core::overlay;
13use iced_core::renderer::{self};
14use iced_core::widget::Operation;
15use iced_core::widget::tree::Tree;
16use iced_core::{Clipboard, Layout, Length, Point, Rectangle, Shell, Vector, Widget};
17
18pub struct Toaster<'a, Message, Theme, Renderer> {
19 toasts: Element<'a, Message, Theme, Renderer>,
20 content: Element<'a, Message, Theme, Renderer>,
21 is_empty: bool,
22}
23
24impl<'a, Message, Theme, Renderer> Toaster<'a, Message, Theme, Renderer> {
25 pub fn new(
26 toasts: Element<'a, Message, Theme, Renderer>,
27 content: Element<'a, Message, Theme, Renderer>,
28 is_empty: bool,
29 ) -> Self {
30 Self {
31 toasts,
32 content,
33 is_empty,
34 }
35 }
36}
37
38impl<Message, Theme, Renderer> Widget<Message, Theme, Renderer>
39 for Toaster<'_, Message, Theme, Renderer>
40where
41 Renderer: iced_core::Renderer,
42{
43 fn size(&self) -> Size<Length> {
44 self.content.as_widget().size()
45 }
46
47 fn layout(
48 &self,
49 tree: &mut Tree,
50 renderer: &Renderer,
51 limits: &layout::Limits,
52 ) -> layout::Node {
53 self.content
54 .as_widget()
55 .layout(&mut tree.children[0], renderer, limits)
56 }
57
58 fn draw(
59 &self,
60 tree: &Tree,
61 renderer: &mut Renderer,
62 theme: &Theme,
63 style: &renderer::Style,
64 layout: Layout<'_>,
65 cursor: mouse::Cursor,
66 viewport: &Rectangle,
67 ) {
68 self.content.as_widget().draw(
69 &tree.children[0],
70 renderer,
71 theme,
72 style,
73 layout,
74 cursor,
75 viewport,
76 );
77 }
78
79 fn children(&self) -> Vec<Tree> {
80 vec![Tree::new(&self.content), Tree::new(&self.toasts)]
81 }
82
83 fn diff(&mut self, tree: &mut Tree) {
84 tree.diff_children(&mut [&mut self.content, &mut self.toasts]);
85 }
86
87 fn operate<'b>(
88 &'b self,
89 state: &'b mut Tree,
90 layout: Layout<'_>,
91 renderer: &Renderer,
92 operation: &mut dyn Operation<()>,
93 ) {
94 self.content
95 .as_widget()
96 .operate(&mut state.children[0], layout, renderer, operation);
97 }
98
99 fn on_event(
100 &mut self,
101 state: &mut Tree,
102 event: Event,
103 layout: Layout<'_>,
104 cursor: mouse::Cursor,
105 renderer: &Renderer,
106 clipboard: &mut dyn Clipboard,
107 shell: &mut Shell<'_, Message>,
108 viewport: &Rectangle,
109 ) -> event::Status {
110 self.content.as_widget_mut().on_event(
111 &mut state.children[0],
112 event,
113 layout,
114 cursor,
115 renderer,
116 clipboard,
117 shell,
118 viewport,
119 )
120 }
121
122 fn mouse_interaction(
123 &self,
124 state: &Tree,
125 layout: Layout<'_>,
126 cursor: mouse::Cursor,
127 viewport: &Rectangle,
128 renderer: &Renderer,
129 ) -> mouse::Interaction {
130 self.content.as_widget().mouse_interaction(
131 &state.children[0],
132 layout,
133 cursor,
134 viewport,
135 renderer,
136 )
137 }
138
139 fn overlay<'b>(
140 &'b mut self,
141 state: &'b mut Tree,
142 layout: Layout<'_>,
143 renderer: &Renderer,
144 translation: Vector,
145 ) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
146 if self.is_empty {
148 self.content.as_widget_mut().overlay(
149 &mut state.children[0],
150 layout,
151 renderer,
152 translation,
153 )
154 } else {
155 let bounds = layout.bounds();
156
157 Some(overlay::Element::new(Box::new(ToasterOverlay::new(
158 &mut state.children[1],
159 &mut self.toasts,
160 ))))
161 }
162 }
163
164 fn drag_destinations(
165 &self,
166 state: &Tree,
167 layout: Layout<'_>,
168 renderer: &Renderer,
169 dnd_rectangles: &mut iced_core::clipboard::DndDestinationRectangles,
170 ) {
171 self.content.as_widget().drag_destinations(
172 &state.children[0],
173 layout,
174 renderer,
175 dnd_rectangles,
176 );
177 }
178}
179
180struct ToasterOverlay<'a, 'b, Message, Theme = iced::Theme, Renderer = iced::Renderer> {
181 state: &'b mut Tree,
182 element: &'b mut Element<'a, Message, Theme, Renderer>,
183}
184
185impl<'a, 'b, Message, Theme, Renderer> ToasterOverlay<'a, 'b, Message, Theme, Renderer>
186where
187 Renderer: renderer::Renderer,
188{
189 fn new(state: &'b mut Tree, element: &'b mut Element<'a, Message, Theme, Renderer>) -> Self {
190 Self { state, element }
191 }
192}
193
194impl<Message, Theme, Renderer> Overlay<Message, Theme, Renderer>
195 for ToasterOverlay<'_, '_, Message, Theme, Renderer>
196where
197 Renderer: renderer::Renderer,
198{
199 fn layout(&mut self, renderer: &Renderer, bounds: Size) -> Node {
200 let limits = Limits::new(Size::ZERO, bounds);
201
202 let mut node = self
203 .element
204 .as_widget()
205 .layout(self.state, renderer, &limits);
206
207 let offset = 15.;
208
209 let position = Point::new(
210 (bounds.width / 2.) - (node.size().width / 2.),
211 bounds.height - (node.size().height + offset),
212 );
213
214 node.move_to_mut(position);
215
216 node
217 }
218
219 fn draw(
220 &self,
221 renderer: &mut Renderer,
222 theme: &Theme,
223 style: &renderer::Style,
224 layout: Layout<'_>,
225 cursor: mouse::Cursor,
226 ) {
227 let bounds = layout.bounds();
228 self.element
229 .as_widget()
230 .draw(self.state, renderer, theme, style, layout, cursor, &bounds);
231 }
232
233 fn on_event(
234 &mut self,
235 event: Event,
236 layout: Layout<'_>,
237 cursor: mouse::Cursor,
238 renderer: &Renderer,
239 clipboard: &mut dyn Clipboard,
240 shell: &mut Shell<Message>,
241 ) -> event::Status {
242 self.element.as_widget_mut().on_event(
243 self.state,
244 event,
245 layout,
246 cursor,
247 renderer,
248 clipboard,
249 shell,
250 &layout.bounds(),
251 )
252 }
253
254 fn mouse_interaction(
255 &self,
256 layout: Layout<'_>,
257 cursor: mouse::Cursor,
258 viewport: &Rectangle,
259 renderer: &Renderer,
260 ) -> mouse::Interaction {
261 self.element
262 .as_widget()
263 .mouse_interaction(self.state, layout, cursor, viewport, renderer)
264 }
265
266 fn overlay<'c>(
267 &'c mut self,
268 layout: Layout<'_>,
269 renderer: &Renderer,
270 ) -> Option<overlay::Element<'c, Message, Theme, Renderer>> {
271 self.element
272 .as_widget_mut()
273 .overlay(self.state, layout, renderer, Default::default())
274 }
275}
276
277impl<'a, Message, Theme, Renderer> From<Toaster<'a, Message, Theme, Renderer>>
278 for Element<'a, Message, Theme, Renderer>
279where
280 Renderer: renderer::Renderer + 'a,
281 Theme: 'a,
282 Message: 'a,
283{
284 fn from(
285 toaster: Toaster<'a, Message, Theme, Renderer>,
286 ) -> Element<'a, Message, Theme, Renderer> {
287 Element::new(toaster)
288 }
289}