Skip to main content

vol_limiter/components/
hov_container_row.rs

1use iced_core::{
2    self, 
3    border::Border, 
4    layout::{self, Layout, Limits, Node}, 
5    mouse::{self, Cursor}, 
6    widget::{tree::{self, Tree}, Operation}, 
7    Alignment, Background, Clipboard, Color, Element, Length, 
8    Padding, Rectangle, Shadow, Shell, Size, Theme, Widget, event, 
9    Vector, renderer, overlay,  
10};
11
12use crate::styles::{equal_radius, get_rgb_color};
13
14pub struct HovContainer<'a, Message, Theme = iced::Theme, Renderer = iced::Renderer,> 
15    where 
16        Renderer: iced_core::renderer::Renderer,
17        Theme: Catalog,
18    {
19    content: Vec<Element<'a, Message, Theme, Renderer>>,
20    padding: Padding,
21    height: Length,
22    width: Length,
23    on_hover: Option<OnHover<'a, Message>>,
24    on_exit: Option<OnExit<'a, Message>>,
25    hover_col: Option<iced::Color>,
26    theme: Theme::Class<'a>
27}
28
29impl<'a, Message, Theme, Renderer> HovContainer<'a, Message, Theme, Renderer>
30    where 
31        Renderer: iced_core::renderer::Renderer,
32        Theme: Catalog,
33        {
34        pub fn new() -> Self{
35            let content = Vec::new();
36            Self {
37                content,
38                padding: DEFAULT_PADDING,
39                height: Length::Shrink,
40                width: Length::Shrink,
41                on_hover: None,
42                on_exit: None,
43                hover_col: None,  
44                theme: Theme::default(), 
45            }
46        }
47
48        pub fn with_content(content: Vec<Element<'a, Message, Theme, Renderer >>) -> Self{
49            Self {
50                content,
51                padding: DEFAULT_PADDING,
52                height: Length::Shrink,
53                width: Length::Shrink,
54                on_hover: None,
55                on_exit: None,
56                hover_col: None,  
57                theme: Theme::default(), 
58            }
59        }
60
61        pub fn with_capacity(capacity: usize) -> Self {
62            Self::with_content(Vec::with_capacity(capacity))
63        }
64
65        pub fn with_children(children: impl IntoIterator<Item = Element<'a, Message, Theme, Renderer>>) -> Self {
66            let iterator = children.into_iter();
67            
68            Self::with_capacity(iterator.size_hint().0).extend(iterator)
69        }
70
71        pub fn width(mut self, width: Length) -> Self {
72            self.width = width;
73            self
74        }
75
76        pub fn height(mut self, height: Length) -> Self {
77            self.height = height;
78            self
79        }
80
81        pub fn on_hover(mut self, message: Message) -> Self{
82            self.on_hover = Some(OnHover::Direct(message));
83            self
84        } 
85
86        pub fn on_exit(mut self, message: Message) -> Self {
87            self.on_exit = Some(OnExit::Direct(message));
88            self
89        }
90
91        pub fn padding<P>(mut self, padding: P) -> Self 
92            where
93                P: Into<Padding> {
94                self.padding = padding.into();
95                self
96        }
97
98        pub fn hover_color(mut self, color: Color) -> Self {
99            self.hover_col = Some(color);
100            self
101        }
102
103        pub fn push(
104                mut self,
105                child: impl Into<Element<'a, Message, Theme, Renderer>>,
106            ) -> Self {
107                let child = child.into();
108                let child_size = child.as_widget().size_hint();
109
110                self.width = self.width.enclose(child_size.width);
111                self.height = self.height.enclose(child_size.height);
112
113                self.content.push(child);
114                self
115            }
116        
117        pub fn extend(
118            self,
119            children: impl IntoIterator<Item = Element<'a, Message, Theme, Renderer>>) -> Self {
120                children.into_iter().fold(self, Self::push)
121            }
122
123        pub fn style(mut self, style: impl Fn(&Theme, Status) -> Style + 'a) -> Self 
124        where
125            Theme::Class<'a>: From<StyleFn<'a, Theme>>,
126        {
127            self.theme = (Box::new(style) as StyleFn<'a, Theme>).into();
128            self
129        }
130        
131        
132
133}
134
135enum OnHover <'a, Message> {
136    Direct(Message),
137    Closure(Box<dyn Fn() -> Message + 'a>),
138}
139
140enum OnExit <'a, Message> {
141    Direct(Message),
142    Closure(Box<dyn Fn() -> Message + 'a>),
143}
144
145impl <'a, Message: Clone> OnHover <'a, Message> {
146    fn get(&self) -> Message {
147        match self {
148            OnHover::Direct(message) => message.clone(),
149            OnHover::Closure(f) => f(),
150        }
151    }
152}
153
154impl <'a, Message: Clone> OnExit <'a, Message> {
155    fn get(&self) -> Message {
156        match self {
157            OnExit::Direct(message) => message.clone(),
158            OnExit::Closure(f) => f(),
159        }
160    }
161}
162
163#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
164struct State {
165    is_hovered: bool,
166}
167
168impl <'a, Message, Theme, Renderer> Widget<Message, Theme, Renderer> 
169    for HovContainer<'a, Message, Theme, Renderer>
170where 
171    Message: 'a + Clone,
172    Renderer: 'a + iced_core::renderer::Renderer,
173    Theme: Catalog,
174{
175    fn tag(&self) -> tree::Tag {
176        tree::Tag::of::<State>()
177    }
178
179    fn state(&self) -> tree::State {
180        tree::State::new(State::default())
181    }
182
183    fn diff(&self, tree: &mut Tree) {
184        tree.diff_children(&self.content);
185    }
186    
187    fn size(&self) -> Size<Length>{
188        Size {
189            width: self.width,
190            height: self.height,
191        }
192    }
193
194    fn children(&self) -> Vec<Tree> {
195        self.content.iter().map(Tree::new).collect()
196    }
197
198    fn layout(&self, tree: &mut Tree, renderer: &Renderer, limits: &Limits) -> Node {
199        layout::flex::resolve(
200            layout::flex::Axis::Horizontal,
201            renderer,
202            limits,
203            self.width,
204            self.height,
205            self.padding,
206            2.0,
207            Alignment::Center,
208            &self.content,
209            &mut tree.children,
210        )
211    }
212
213    fn operate(&self, tree: &mut Tree, layout: Layout<'_>, renderer: &Renderer, operation: &mut dyn Operation) {
214        operation.container(None, layout.bounds(), &mut |operation| {
215            self.content.iter().zip(&mut tree.children).zip(layout.children())
216            .for_each(|((child, state), layout)| {
217                child.as_widget().operate(state, layout, renderer, operation);
218            })
219        })
220    }
221
222    fn on_event(&mut self, tree: &mut Tree, event: event::Event, layout: Layout<'_>, cursor: mouse::Cursor, renderer: &Renderer, clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, viewport: &Rectangle,) -> event::Status {
223        
224        // self.content.iter_mut()
225        self.content
226        .iter_mut()
227        .zip(&mut tree.children)
228        .zip(layout.children())
229        .map(|((child, state), layout)| {
230            child.as_widget_mut().on_event(
231                state,
232                event.clone(),
233                layout,
234                cursor,
235                renderer,
236                clipboard,
237                shell,
238                viewport,
239            )
240        })
241        .fold(event::Status::Ignored, event::Status::merge);
242
243        if let Some(on_exit) = self.on_exit.as_ref().map(OnExit::get) {
244            if let Some(on_hover) = self.on_hover.as_ref().map(OnHover::get)
245            {
246                match tree.state {
247                    tree::State::None => {
248                        // eprintln!("State Empty");
249                        return event::Status::Ignored;
250                    }
251                    tree::State::Some(_) => {
252                        let state = tree.state.downcast_mut::<State>();
253                        // eprintln!("Something");
254                        let was_hovered = state.is_hovered;
255                        let mut now_hovered = false;
256                        if let Some(position) = cursor.position() {
257                            now_hovered = layout.bounds().contains(position);
258                        }
259
260                        match (was_hovered, now_hovered) {
261                            (true, false) => {
262                                state.is_hovered = now_hovered;
263                                shell.publish(on_exit);
264                                return event::Status::Captured;
265                            }
266                            (false, true) => {
267                                state.is_hovered = now_hovered;
268                                shell.publish(on_hover);
269                                return event::Status::Captured;
270                            }
271                            _ => {
272                                return event::Status::Ignored;
273                            }
274                        }
275                    }
276                }
277            }
278            else {
279                event::Status::Ignored
280            }
281        }
282        else {
283            match tree.state {
284                tree::State::None => {
285                    // eprintln!("State Empty");
286                    return event::Status::Ignored;
287                }
288                tree::State::Some(_) => {
289                    let state = tree.state.downcast_mut::<State>();
290                    // eprintln!("Something");
291                    let was_hovered = state.is_hovered;
292                    let mut now_hovered = false;
293                    if let Some(position) = cursor.position() {
294                        now_hovered = layout.bounds().contains(position);
295                    }
296
297                    match (was_hovered, now_hovered) {
298                        (true, false) => {
299                            state.is_hovered = now_hovered;
300                            return event::Status::Ignored;
301                        }
302                        (false, true) => {
303                            state.is_hovered = now_hovered;
304                            return event::Status::Ignored;
305                        }
306                        _ => {
307                            return event::Status::Ignored;
308                        }
309                    }
310                }
311            }
312            event::Status::Captured
313        }
314
315    }
316    fn draw(&self, tree: &Tree, renderer: &mut Renderer, theme: &Theme, _style: &renderer::Style, layout: Layout, cursor: Cursor, rect: &Rectangle) {
317        let bounds = layout.bounds();
318        let content_layout = layout.children().next().unwrap();
319        let is_mouse_over = cursor.is_over(bounds);
320        
321        let status = if self.on_hover.is_none() {
322            Status::Disabled
323        } else if is_mouse_over && tree.state.downcast_ref::<State>().is_hovered{
324            Status::Hovered
325        } else {
326            Status::NotHovered
327        };
328
329        let style = theme.style(&self.theme, status);
330
331        if style.background.is_some() ||
332            style.border.width > 0.0 ||
333            style.shadow.color.a > 0.0
334        {
335            renderer.fill_quad(
336                renderer::Quad {
337                    bounds,
338                    border: style.border,
339                    shadow: style.shadow,
340                },
341                style.background.unwrap_or(Background::Color(Color::TRANSPARENT))
342            );
343        }
344
345        for ((child, state), layout) in self
346                .content
347                .iter()
348                .zip(&tree.children)
349                .zip(layout.children())
350            {
351                child.as_widget().draw(
352                    &tree.children[0],
353                    renderer,
354                    theme,
355                    &renderer::Style {
356                        text_color: style.text_color,
357                    },
358                    content_layout,
359                    cursor,
360                    &rect,
361                );
362
363            }
364        }
365
366    fn overlay<'b> (
367        &'b mut self,
368        tree: &'b mut Tree,
369        layout: Layout<'_>,
370        renderer: &Renderer,
371        translation: Vector
372    ) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
373        overlay::from_children(
374            &mut self.content,
375            tree, 
376            layout, 
377            renderer, 
378            translation,
379        )
380    }
381}
382
383impl <'a, Message, Theme, Renderer> From <HovContainer<'a, Message, Theme, Renderer>>
384    for Element<'a, Message, Theme, Renderer> 
385where
386    Message: Clone + 'a,
387    Theme: Catalog + 'a,
388    Renderer: iced_core::Renderer + 'a,
389{
390    fn from (hov_cont: HovContainer<'a, Message, Theme, Renderer>) -> Self {
391        Self::new(hov_cont)
392    }
393}
394
395impl <'a, Message, Theme, Renderer: iced_core::Renderer> FromIterator <Element<'a, Message, Theme, Renderer>>
396    for HovContainer<'a, Message, Theme, Renderer>
397    where 
398        Theme: Catalog,
399{
400    fn from_iter<T: IntoIterator<Item = Element<'a, Message, Theme, Renderer>>>(iter: T) -> Self {
401        Self::with_children(iter)
402    }
403}
404
405pub(crate) const DEFAULT_PADDING: Padding = Padding {
406    top: 5.0,
407    bottom: 5.0,
408    right: 10.0,
409    left: 10.0,
410};
411
412#[derive(Debug, Clone, Copy, PartialEq, Eq)]
413pub enum Status{
414    Disabled,
415    Hovered,
416    NotHovered,
417}
418
419
420#[derive(Debug, Clone, Copy, PartialEq)]
421pub struct Style {
422    pub background: Option<Background>,
423    pub text_color: Color,
424    pub border: Border,
425    pub shadow: Shadow,
426}
427
428impl Style {
429    pub fn with_border_color(self, border: Border) -> Self {
430        Self {
431            border: border,
432            ..self
433        }
434    }
435}
436
437impl Default for Style {
438    fn default() -> Self {
439        Self {
440            background: None,
441            text_color: Color::WHITE,
442            border: Border::default(),
443            shadow: Shadow::default(),
444        }
445    }
446}
447
448pub trait Catalog {
449    type Class<'a>;
450
451    fn default<'a>() -> Self::Class<'a>;
452
453    fn style(&self, class: &Self::Class<'_>, status: Status) -> Style;
454}
455
456pub type StyleFn<'a, Theme> = Box<dyn Fn(&Theme, Status) -> Style + 'a>;
457
458impl Catalog for Theme {
459    type Class<'a> = StyleFn<'a, Self>;
460
461    fn default<'a>() -> Self::Class<'a> {
462        Box::new(primary)
463    }
464
465    fn style(&self, class: &Self::Class<'_>, status: Status) -> Style {
466        class(self, status)
467    }
468}
469
470pub fn primary(theme: &Theme, status: Status) -> Style {
471    let _palette = theme.extended_palette();
472    match status {
473        Status::Disabled => {
474            Style {
475                background: Some(Background::Color(Color::TRANSPARENT)),
476                text_color: get_rgb_color(255, 255, 255),
477                border: Border {color: get_rgb_color(100, 100, 100), width: 2.0, radius: equal_radius(5)},
478                ..Default::default()
479            }
480        },
481        Status::Hovered => {
482            Style {
483                background: Some(Background::Color(Color::TRANSPARENT)),
484                text_color: get_rgb_color(255, 255, 255),
485                border: Border {color: get_rgb_color(0, 0, 255), width: 2.0, radius: equal_radius(5)},
486                ..Default::default()
487            }
488        },
489        Status::NotHovered => {
490            Style {
491                background: Some(Background::Color(Color::TRANSPARENT)),
492                text_color: get_rgb_color(255, 255, 255),
493                border: Border {color: get_rgb_color(150, 150, 150), width: 2.0, radius: equal_radius(5)},
494                ..Default::default()
495            }
496        }
497    }
498}
499
500pub fn auto_style(unhov_color: Color, hov_color: Color, width: i32, radius: u32) -> impl Fn(&Theme, Status) -> Style {
501    move |_theme: &Theme, status | {
502        match status {
503            Status::Disabled => {
504                let border = Border {
505                    color: unhov_color,
506                    width: width as f32,
507                    radius: equal_radius(radius),
508                };
509                Style::with_border_color(Style{..Default::default()}, border)
510            },
511            Status::Hovered => {
512                let border = Border {
513                    color: hov_color,
514                    width: width as f32,
515                    radius: equal_radius(radius),
516                };
517                Style::with_border_color(Style{..Default::default()}, border)
518            },
519            Status::NotHovered => {
520                let border = Border {
521                    color: unhov_color,
522                    width: width as f32,
523                    radius: equal_radius(radius),
524                };
525                Style::with_border_color(Style{..Default::default()}, border)
526            },
527        }
528    }
529}
530