audio_processor_iced_design_system/
container.rs

1// Augmented Audio: Audio libraries and applications
2// Copyright (c) 2022 Pedro Tacla Yamada
3//
4// The MIT License (MIT)
5//
6// Permission is hereby granted, free of charge, to any person obtaining a copy
7// of this software and associated documentation files (the "Software"), to deal
8// in the Software without restriction, including without limitation the rights
9// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10// copies of the Software, and to permit persons to whom the Software is
11// furnished to do so, subject to the following conditions:
12//
13// The above copyright notice and this permission notice shall be included in
14// all copies or substantial portions of the Software.
15//
16// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22// THE SOFTWARE.
23pub use hover_container::HoverContainer;
24
25/// Modified `iced_native::container::Container` to have styles on hover/pressed
26pub mod hover_container {
27    use iced::{Alignment, Color, Length, Point, Rectangle};
28    use iced_native::layout::{Limits, Node};
29    use iced_native::renderer::Quad;
30    use iced_native::widget::{Operation, Tree};
31    use iced_native::{overlay, Clipboard, Element, Event, Layout, Padding, Shell, Widget};
32
33    pub struct HoverContainer<'a, Message, Renderer: iced_native::Renderer> {
34        padding: Padding,
35        content: Element<'a, Message, Renderer>,
36        width: Length,
37        height: Length,
38        max_width: u32,
39        max_height: u32,
40        horizontal_alignment: Alignment,
41        vertical_alignment: Alignment,
42        style: Box<dyn self::style::StyleSheet>,
43    }
44
45    impl<'a, Message, Renderer> HoverContainer<'a, Message, Renderer>
46    where
47        Renderer: iced_native::Renderer,
48    {
49        pub fn new<T>(content: T) -> Self
50        where
51            T: Into<Element<'a, Message, Renderer>>,
52        {
53            HoverContainer {
54                padding: Padding::ZERO,
55                width: Length::Shrink,
56                height: Length::Shrink,
57                max_width: u32::MAX,
58                max_height: u32::MAX,
59                horizontal_alignment: Alignment::Start,
60                vertical_alignment: Alignment::Start,
61                style: Box::<crate::style::HoverContainer>::default(),
62                content: content.into(),
63            }
64        }
65
66        /// Sets the [`Padding`] of the [`iced::Container`].
67        pub fn padding<P: Into<Padding>>(mut self, padding: P) -> Self {
68            self.padding = padding.into();
69            self
70        }
71
72        /// Sets the width of the [`iced::Container`].
73        pub fn width(mut self, width: Length) -> Self {
74            self.width = width;
75            self
76        }
77
78        /// Sets the height of the [`iced::Container`].
79        pub fn height(mut self, height: Length) -> Self {
80            self.height = height;
81            self
82        }
83
84        /// Sets the maximum width of the [`iced::Container`].
85        pub fn max_width(mut self, max_width: u32) -> Self {
86            self.max_width = max_width;
87            self
88        }
89
90        /// Sets the maximum height of the [`iced::Container`] in pixels.
91        pub fn max_height(mut self, max_height: u32) -> Self {
92            self.max_height = max_height;
93            self
94        }
95
96        /// Sets the content alignment for the horizontal axis of the [`iced::Container`].
97        pub fn align_x(mut self, alignment: Alignment) -> Self {
98            self.horizontal_alignment = alignment;
99            self
100        }
101
102        /// Sets the content alignment for the vertical axis of the [`iced::Container`].
103        pub fn align_y(mut self, alignment: Alignment) -> Self {
104            self.vertical_alignment = alignment;
105            self
106        }
107
108        /// Centers the contents in the horizontal axis of the [`iced::Container`].
109        pub fn center_x(mut self) -> Self {
110            self.horizontal_alignment = Alignment::Center;
111            self
112        }
113
114        /// Centers the contents in the vertical axis of the [`iced::Container`].
115        pub fn center_y(mut self) -> Self {
116            self.vertical_alignment = Alignment::Center;
117            self
118        }
119
120        /// Sets the stylesheet of the [`iced::Container`].
121        pub fn style(mut self, stylesheet: impl Into<Box<dyn self::style::StyleSheet>>) -> Self {
122            self.style = stylesheet.into();
123            self
124        }
125    }
126
127    impl<'a, Message, Renderer> Widget<Message, Renderer> for HoverContainer<'a, Message, Renderer>
128    where
129        Renderer: iced_native::Renderer,
130    {
131        fn width(&self) -> Length {
132            self.width
133        }
134
135        fn height(&self) -> Length {
136            self.height
137        }
138
139        fn layout(&self, renderer: &Renderer, limits: &Limits) -> Node {
140            let limits = limits
141                .loose()
142                .max_width(self.max_width as f32)
143                .max_height(self.max_height as f32)
144                .width(self.width)
145                .height(self.height)
146                .pad(self.padding);
147
148            let mut content = self.content.as_widget().layout(renderer, &limits.loose());
149            let size = limits.resolve(content.size());
150
151            content.move_to(Point::new(
152                self.padding.left.into(),
153                self.padding.top.into(),
154            ));
155            content.align(self.horizontal_alignment, self.vertical_alignment, size);
156
157            Node::with_children(size.pad(self.padding), vec![content])
158        }
159
160        fn draw(
161            &self,
162            state: &iced_native::widget::Tree,
163            renderer: &mut Renderer,
164            theme: &Renderer::Theme,
165            style: &iced_native::renderer::Style,
166            layout: Layout<'_>,
167            cursor_position: Point,
168            viewport: &Rectangle,
169        ) {
170            let is_hovered = layout.bounds().contains(cursor_position);
171            let container_style = if is_hovered {
172                self.style.hovered()
173            } else {
174                self.style.style()
175            };
176            renderer.fill_quad(
177                Quad {
178                    bounds: layout.bounds(),
179                    border_radius: iced_native::renderer::BorderRadius::from(
180                        container_style.border_radius,
181                    ),
182                    border_color: container_style.border_color,
183                    border_width: container_style.border_width,
184                },
185                container_style
186                    .background
187                    .unwrap_or_else(|| Color::TRANSPARENT.into()),
188            );
189            self.content.as_widget().draw(
190                &state.children[0],
191                renderer,
192                theme,
193                &iced_native::renderer::Style {
194                    text_color: container_style.text_color.unwrap_or(style.text_color),
195                },
196                layout.children().next().unwrap(),
197                cursor_position,
198                viewport,
199            );
200        }
201
202        fn diff(&self, tree: &mut Tree) {
203            tree.diff_children(std::slice::from_ref(&self.content))
204        }
205
206        fn children(&self) -> Vec<Tree> {
207            vec![Tree::new(&self.content)]
208        }
209
210        fn operate(
211            &self,
212            tree: &mut Tree,
213            layout: Layout<'_>,
214            renderer: &Renderer,
215            operation: &mut dyn Operation<Message>,
216        ) {
217            operation.container(None, &mut |operation| {
218                self.content.as_widget().operate(
219                    &mut tree.children[0],
220                    layout.children().next().unwrap(),
221                    renderer,
222                    operation,
223                );
224            });
225        }
226
227        fn overlay<'b>(
228            &'b mut self,
229            state: &'b mut Tree,
230            layout: Layout<'_>,
231            renderer: &Renderer,
232        ) -> Option<overlay::Element<'b, Message, Renderer>> {
233            self.content.as_widget_mut().overlay(
234                &mut state.children[0],
235                layout.children().next().unwrap(),
236                renderer,
237            )
238        }
239
240        fn on_event(
241            &mut self,
242            state: &mut iced_native::widget::Tree,
243            event: Event,
244            layout: Layout<'_>,
245            cursor_position: Point,
246            renderer: &Renderer,
247            clipboard: &mut dyn Clipboard,
248            shell: &mut Shell<'_, Message>,
249        ) -> iced_native::event::Status {
250            self.content.as_widget_mut().on_event(
251                &mut state.children[0],
252                event,
253                layout.children().next().unwrap(),
254                cursor_position,
255                renderer,
256                clipboard,
257                shell,
258            )
259        }
260    }
261
262    impl<'a, Message, Renderer> From<HoverContainer<'a, Message, Renderer>>
263        for Element<'a, Message, Renderer>
264    where
265        Renderer: 'a + iced_native::Renderer,
266        Message: 'a,
267    {
268        fn from(
269            container: HoverContainer<'a, Message, Renderer>,
270        ) -> Element<'a, Message, Renderer> {
271            Element::new(container)
272        }
273    }
274
275    pub mod style {
276        use iced::{Background, Color};
277
278        #[derive(Debug, Clone)]
279        pub struct Style {
280            pub text_color: Option<Color>,
281            pub background: Option<Background>,
282            pub border_radius: f32,
283            pub border_width: f32,
284            pub border_color: Color,
285        }
286
287        impl std::default::Default for Style {
288            fn default() -> Self {
289                Self {
290                    text_color: None,
291                    background: None,
292                    border_radius: 0.0,
293                    border_width: 0.0,
294                    border_color: Color::TRANSPARENT,
295                }
296            }
297        }
298
299        /// A set of rules that dictate the style of a container.
300        pub trait StyleSheet {
301            /// Produces the style of a container.
302            fn style(&self) -> Style;
303
304            fn hovered(&self) -> Style;
305        }
306
307        struct Default;
308
309        impl StyleSheet for Default {
310            fn style(&self) -> Style {
311                Style {
312                    text_color: None,
313                    background: None,
314                    border_radius: 0.0,
315                    border_width: 0.0,
316                    border_color: Color::TRANSPARENT,
317                }
318            }
319
320            fn hovered(&self) -> Style {
321                self.style()
322            }
323        }
324
325        impl std::default::Default for Box<dyn StyleSheet> {
326            fn default() -> Self {
327                Box::new(Default)
328            }
329        }
330
331        impl<T> From<T> for Box<dyn StyleSheet>
332        where
333            T: 'static + StyleSheet,
334        {
335            fn from(style: T) -> Self {
336                Box::new(style)
337            }
338        }
339    }
340}