audio_processor_iced_design_system/
menu_list.rs1use 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}