1use crate::core;
3use crate::core::border;
4use crate::core::layout;
5use crate::core::mouse;
6use crate::core::overlay;
7use crate::core::renderer;
8use crate::core::widget;
9use crate::core::widget::tree;
10use crate::core::{
11 Event, Layout, Length, Rectangle, Shadow, Shell, Size, Transformation, Vector, Widget,
12};
13
14pub struct Float<'a, W, Theme = crate::Theme>
16where
17 Theme: Catalog,
18{
19 content: W,
20 scale: f32,
21 translate: Option<Box<dyn Fn(Rectangle, Rectangle) -> Vector + 'a>>,
22 class: Theme::Class<'a>,
23}
24
25impl<'a, W, Theme> Float<'a, W, Theme>
26where
27 Theme: Catalog,
28{
29 pub fn new(content: W) -> Self {
31 Self {
32 content,
33 scale: 1.0,
34 translate: None,
35 class: Theme::default(),
36 }
37 }
38
39 pub fn scale(mut self, scale: f32) -> Self {
41 self.scale = scale;
42 self
43 }
44
45 pub fn translate(mut self, translate: impl Fn(Rectangle, Rectangle) -> Vector + 'a) -> Self {
51 self.translate = Some(Box::new(translate));
52 self
53 }
54
55 #[must_use]
57 pub fn style(mut self, style: impl Fn(&Theme) -> Style + 'a) -> Self
58 where
59 Theme::Class<'a>: From<StyleFn<'a, Theme>>,
60 {
61 self.class = (Box::new(style) as StyleFn<'a, Theme>).into();
62 self
63 }
64
65 #[cfg(feature = "advanced")]
67 #[must_use]
68 pub fn class(mut self, class: impl Into<Theme::Class<'a>>) -> Self {
69 self.class = class.into();
70 self
71 }
72
73 fn is_floating(&self, bounds: Rectangle, viewport: Rectangle) -> bool {
74 self.scale > 1.0
75 || self
76 .translate
77 .as_ref()
78 .is_some_and(|translate| translate(bounds, viewport) != Vector::ZERO)
79 }
80}
81
82impl<W, Theme> widget::Meta for Float<'_, W, Theme> where Theme: Catalog {}
83
84impl<W, Message, Theme, Renderer> Widget<Message, Theme, Renderer> for Float<'_, W, Theme>
85where
86 Theme: Catalog,
87 Renderer: core::Renderer,
88 W: Widget<Message, Theme, Renderer>,
89{
90 fn tag(&self) -> tree::Tag {
91 self.content.tag()
92 }
93
94 fn state(&self) -> tree::State {
95 self.content.state()
96 }
97
98 fn diff(&mut self, tree: &mut widget::Tree) {
99 self.content.diff(tree);
100 }
101
102 fn size(&self) -> Size<Length> {
103 self.content.size()
104 }
105
106 fn layout(&mut self, tree: &mut widget::Tree, renderer: &Renderer, limits: &layout::Limits) {
107 self.content.layout(tree, renderer, limits);
108 }
109
110 fn update(
111 &mut self,
112 tree: &mut widget::Tree,
113 event: &Event,
114 layout: Layout,
115 cursor: mouse::Cursor,
116 renderer: &Renderer,
117 shell: &mut Shell<'_, Message>,
118 viewport: &Rectangle,
119 ) {
120 if self.is_floating(layout.bounds(), *viewport) {
121 return;
122 }
123
124 self.content
125 .update(tree, event, layout, cursor, renderer, shell, viewport);
126 }
127
128 fn draw(
129 &self,
130 tree: &widget::Tree,
131 renderer: &mut Renderer,
132 theme: &Theme,
133 style: &renderer::Style,
134 layout: Layout,
135 cursor: mouse::Cursor,
136 viewport: &Rectangle,
137 ) {
138 if self.is_floating(layout.bounds(), *viewport) {
139 return;
140 }
141
142 {
143 let style = theme.style(&self.class);
144
145 if style.shadow.color.a > 0.0 {
146 renderer.fill_quad(
147 renderer::Quad {
148 bounds: layout.bounds().shrink(1.0),
149 shadow: style.shadow,
150 border: border::rounded(style.shadow_border_radius),
151 snap: false,
152 },
153 style.shadow.color,
154 );
155 }
156 }
157
158 self.content
159 .draw(tree, renderer, theme, style, layout, cursor, viewport);
160 }
161
162 fn mouse_interaction(
163 &self,
164 tree: &widget::Tree,
165 layout: Layout,
166 cursor: mouse::Cursor,
167 viewport: &Rectangle,
168 renderer: &Renderer,
169 ) -> mouse::Interaction {
170 if self.is_floating(layout.bounds(), *viewport) {
171 return mouse::Interaction::None;
172 }
173
174 self.content
175 .mouse_interaction(tree, layout, cursor, viewport, renderer)
176 }
177
178 fn operate(
179 &mut self,
180 tree: &mut widget::Tree,
181 layout: Layout,
182 viewport: &Rectangle,
183 renderer: &Renderer,
184 operation: &mut dyn widget::Operation,
185 ) {
186 self.content
187 .operate(tree, layout, viewport, renderer, operation);
188 }
189
190 fn overlay<'a>(
191 &'a mut self,
192 state: &'a mut widget::Tree,
193 layout: Layout,
194 renderer: &Renderer,
195 viewport: &Rectangle,
196 offset: Vector,
197 window: Size,
198 ) -> Vec<overlay::Element<'a, Message, Theme, Renderer>> {
199 let bounds = layout.bounds();
200
201 let translation = self
202 .translate
203 .as_ref()
204 .map(|translate| translate(bounds + offset, *viewport + offset))
205 .unwrap_or(Vector::ZERO);
206
207 if self.scale > 1.0 || translation != Vector::ZERO {
208 let translation = translation + offset;
209
210 let transformation = Transformation::translate(
211 bounds.x + bounds.width / 2.0 + translation.x,
212 bounds.y + bounds.height / 2.0 + translation.y,
213 ) * Transformation::scale(self.scale)
214 * Transformation::translate(
215 -bounds.x - bounds.width / 2.0,
216 -bounds.y - bounds.height / 2.0,
217 );
218
219 vec![overlay::Element::new(Box::new(Overlay {
220 float: self,
221 state,
222 layout,
223 viewport: *viewport + offset,
224 window,
225 transformation,
226 }))]
227 } else {
228 self.content
229 .overlay(state, layout, renderer, viewport, offset, window)
230 }
231 }
232}
233
234struct Overlay<'a, 'b, W, Theme>
235where
236 Theme: Catalog,
237{
238 float: &'a mut Float<'b, W, Theme>,
239 state: &'a mut widget::Tree,
240 layout: Layout,
241 viewport: Rectangle,
242 window: Size,
243 transformation: Transformation,
244}
245
246impl<W, Message, Theme, Renderer> core::Overlay<Message, Theme, Renderer>
247 for Overlay<'_, '_, W, Theme>
248where
249 Theme: Catalog,
250 Renderer: core::Renderer,
251 W: Widget<Message, Theme, Renderer>,
252{
253 fn update(
254 &mut self,
255 event: &Event,
256 cursor: mouse::Cursor,
257 renderer: &Renderer,
258 shell: &mut Shell<'_, Message>,
259 ) {
260 let inverse = self.transformation.inverse();
261
262 self.float.content.update(
263 self.state,
264 event,
265 self.layout,
266 cursor * inverse,
267 renderer,
268 shell,
269 &(self.viewport * inverse),
270 );
271 }
272
273 fn draw(
274 &self,
275 renderer: &mut Renderer,
276 theme: &Theme,
277 style: &renderer::Style,
278 cursor: mouse::Cursor,
279 ) {
280 let bounds = self.layout.bounds();
281 let inverse = self.transformation.inverse();
282
283 renderer.with_layer(self.viewport, |renderer| {
284 renderer.with_transformation(self.transformation, |renderer| {
285 {
286 let style = theme.style(&self.float.class);
287
288 if style.shadow.color.a > 0.0 {
289 renderer.fill_quad(
290 renderer::Quad {
291 bounds: bounds.shrink(1.0),
292 shadow: style.shadow,
293 border: border::rounded(style.shadow_border_radius),
294 snap: false,
295 },
296 style.shadow.color,
297 );
298 }
299 }
300
301 self.float.content.draw(
302 self.state,
303 renderer,
304 theme,
305 style,
306 self.layout,
307 cursor * inverse,
308 &(self.viewport * inverse),
309 );
310 });
311 });
312 }
313
314 fn mouse_interaction(&self, cursor: mouse::Cursor, renderer: &Renderer) -> mouse::Interaction {
315 let bounds = self.layout.bounds() * self.transformation;
316
317 if !cursor.is_over(bounds) {
318 return mouse::Interaction::None;
319 }
320
321 let inverse = self.transformation.inverse();
322
323 self.float.content.mouse_interaction(
324 self.state,
325 self.layout,
326 cursor * inverse,
327 &(self.viewport * inverse),
328 renderer,
329 )
330 }
331
332 fn index(&self) -> f32 {
333 self.float.scale * 0.5
334 }
335
336 fn overlay<'a>(
337 &'a mut self,
338 renderer: &Renderer,
339 ) -> Vec<overlay::Element<'a, Message, Theme, Renderer>> {
340 self.float.content.overlay(
341 self.state,
342 self.layout,
343 renderer,
344 &(self.viewport * self.transformation.inverse()),
345 self.transformation.translation(),
346 self.window,
347 )
348 }
349}
350
351pub trait Catalog {
356 type Class<'a>;
358
359 fn default<'a>() -> Self::Class<'a>;
361
362 fn style(&self, class: &Self::Class<'_>) -> Style;
364}
365
366pub type StyleFn<'a, Theme> = Box<dyn Fn(&Theme) -> Style + 'a>;
368
369impl Catalog for crate::Theme {
370 type Class<'a> = StyleFn<'a, Self>;
371
372 fn default<'a>() -> Self::Class<'a> {
373 Box::new(|_| Style::default())
374 }
375
376 fn style(&self, class: &Self::Class<'_>) -> Style {
377 class(self)
378 }
379}
380
381#[derive(Debug, Clone, Copy, PartialEq, Default)]
383pub struct Style {
384 pub shadow: Shadow,
386 pub shadow_border_radius: border::Radius,
388}