audio_processor_iced_design_system/
menu_list.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.
23use crate::spacing::Spacing;
24use iced::{widget::Button, widget::Column, Element, Length};
25
26pub struct State<InnerState, MenuOption> {
27    selected_child: Option<usize>,
28    children: Vec<menu_item::State<InnerState, MenuOption>>,
29}
30
31#[derive(Debug, Clone)]
32pub enum Message<MenuOption> {
33    Selected { index: usize, option: MenuOption },
34}
35
36impl<InnerState, MenuOption: Clone> State<InnerState, MenuOption> {
37    pub fn new(children: Vec<(InnerState, MenuOption)>, selected_child: Option<usize>) -> Self {
38        State {
39            selected_child,
40            children: children
41                .into_iter()
42                .map(|(child, option)| menu_item::State::new(child, option))
43                .collect(),
44        }
45    }
46
47    pub fn update(&mut self, message: Message<MenuOption>) {
48        match message {
49            Message::Selected { index, .. } => {
50                self.selected_child = Some(index);
51            }
52        }
53    }
54
55    pub fn view(
56        &self,
57        renderer: impl Fn(&InnerState) -> Element<Message<MenuOption>>,
58    ) -> Element<Message<MenuOption>> {
59        let selected_child = self.selected_child;
60        let children_elements = self
61            .children
62            .iter()
63            .enumerate()
64            .map(|(index, menu_item::State { state, option })| {
65                let is_selected_child =
66                    selected_child.is_some() && selected_child.unwrap() == index;
67                let inner = renderer(state);
68                Button::new(inner)
69                    .width(Length::Fill)
70                    .style(style::ButtonStyleSheet(is_selected_child).into())
71                    .padding(Spacing::base_spacing())
72                    .on_press(Message::Selected {
73                        index,
74                        option: option.clone(),
75                    })
76                    .into()
77            })
78            .collect();
79
80        Column::with_children(children_elements)
81            .width(Length::Fill)
82            .height(Length::Fill)
83            .into()
84    }
85}
86
87mod menu_item {
88    pub struct State<InnerState, MenuOption> {
89        pub state: InnerState,
90        pub option: MenuOption,
91    }
92
93    impl<InnerState, MenuOption> State<InnerState, MenuOption> {
94        pub fn new(state: InnerState, option: MenuOption) -> Self {
95            State { state, option }
96        }
97    }
98}
99
100mod style {
101    use crate::colors::Colors;
102    use iced::widget::button::Appearance;
103    use iced::Background;
104    use iced_style::Theme;
105
106    pub struct ButtonStyleSheet(pub bool);
107
108    impl From<ButtonStyleSheet> for iced::theme::Button {
109        fn from(value: ButtonStyleSheet) -> Self {
110            Self::Custom(Box::new(value))
111        }
112    }
113
114    impl iced::widget::button::StyleSheet for ButtonStyleSheet {
115        type Style = Theme;
116
117        fn active(&self, _style: &Self::Style) -> Appearance {
118            Appearance {
119                shadow_offset: Default::default(),
120                background: if self.0 {
121                    Some(Background::Color(Colors::selected_background()))
122                } else {
123                    None
124                },
125                border_radius: 0.0,
126                border_width: 0.0,
127                border_color: Default::default(),
128                text_color: Colors::text(),
129            }
130        }
131
132        fn hovered(&self, _style: &Self::Style) -> Appearance {
133            Appearance {
134                shadow_offset: Default::default(),
135                background: if self.0 {
136                    Some(Background::Color(Colors::selected_background()))
137                } else {
138                    Some(Background::Color(Colors::background_level1()))
139                },
140                border_radius: 0.0,
141                border_width: 0.0,
142                border_color: Default::default(),
143                text_color: Colors::text(),
144            }
145        }
146
147        fn pressed(&self, _style: &Self::Style) -> Appearance {
148            Appearance {
149                shadow_offset: Default::default(),
150                background: if self.0 {
151                    Some(Background::Color(Colors::selected_background()))
152                } else {
153                    Some(Background::Color(Colors::background_level2()))
154                },
155                border_radius: 0.0,
156                border_width: 0.0,
157                border_color: Default::default(),
158                text_color: Colors::text(),
159            }
160        }
161
162        fn disabled(&self, _style: &Self::Style) -> Appearance {
163            Appearance {
164                shadow_offset: Default::default(),
165                background: None,
166                border_radius: 0.0,
167                border_width: 0.0,
168                border_color: Default::default(),
169                text_color: Colors::text(),
170            }
171        }
172    }
173}