audio_processor_iced_design_system/
tree_view.rs1use iced::{widget::Column, Element, Length};
24
25pub use item::ItemState;
26
27use crate::updatable::Updatable;
28
29pub struct State<InnerState: Updatable> {
30 items: Vec<ItemState<InnerState>>,
31}
32
33impl<InnerState: Updatable> State<InnerState> {
34 pub fn new(items: Vec<ItemState<InnerState>>) -> Self {
35 State { items }
36 }
37}
38
39#[derive(Debug, Clone)]
40pub enum Message<InnerMessage> {
41 Child(usize, item::Message<InnerMessage>),
42}
43
44impl<InnerState: Updatable + 'static> State<InnerState> {
45 pub fn update(&mut self, msg: Message<InnerState::Message>) {
46 match msg {
47 Message::Child(index, msg) => {
48 self.items[index].update(msg);
49 }
50 }
51 }
52
53 pub fn view(&self) -> Element<Message<InnerState::Message>> {
54 let children = self
55 .items
56 .iter()
57 .enumerate()
58 .map(|(index, item)| item.view().map(move |msg| Message::Child(index, msg)))
59 .collect();
60 Column::with_children(children).width(Length::Fill).into()
61 }
62}
63
64mod item {
65 use std::fmt::Debug;
66
67 use iced::{widget::Button, widget::Column, widget::Container, widget::Text, Element};
68
69 use crate::spacing::Spacing;
70 use crate::updatable::Updatable;
71
72 #[derive(Debug, Clone)]
73 pub enum Message<InnerMessage> {
74 Toggle,
75 Child(usize, Box<Message<InnerMessage>>),
76 Inner(InnerMessage),
77 }
78
79 #[derive(Debug, Clone)]
80 pub enum ItemState<InnerState> {
81 Item {
82 title: String,
83 state: InnerState,
84 },
85 Parent {
86 title: String,
87 children: Vec<ItemState<InnerState>>,
88 is_collapsed: bool,
89 },
90 }
91
92 impl ItemState<()> {
93 pub fn child(title: String) -> Self {
94 ItemState::Item { title, state: () }
95 }
96 }
97
98 impl<InnerState> ItemState<InnerState>
99 where
100 InnerState: Updatable + 'static,
101 {
102 pub fn child_with(title: String, state: InnerState) -> Self {
103 ItemState::Item { title, state }
104 }
105
106 pub fn parent(title: String, children: Vec<ItemState<InnerState>>) -> Self {
107 ItemState::Parent {
108 title,
109 children,
110 is_collapsed: false,
111 }
112 }
113
114 pub fn update(&mut self, message: Message<InnerState::Message>) {
115 match self {
116 ItemState::Parent {
117 is_collapsed,
118 children,
119 ..
120 } => match message {
121 Message::Toggle => {
122 *is_collapsed = !*is_collapsed;
123 }
124 Message::Child(index, msg) => {
125 children[index].update(*msg);
126 }
127 _ => {}
128 },
129 ItemState::Item { state, .. } => {
130 if let Message::Inner(inner) = message {
131 state.update(inner);
132 }
133 }
134 }
135 }
136
137 pub fn view(&self) -> Element<Message<InnerState::Message>> {
138 match self {
139 ItemState::Item { title, .. } => Text::new(title).into(),
140 ItemState::Parent {
141 title,
142 children,
143 is_collapsed,
144 } => {
145 let child_elements = Container::new(Column::with_children(
146 children
147 .iter()
148 .enumerate()
149 .map(|(index, item)| {
150 item.view()
151 .map(move |msg| Message::Child(index, Box::new(msg)))
152 })
153 .collect(),
154 ))
155 .padding([0, 0, 0, Spacing::base_spacing()])
156 .into();
157
158 let toggle_button = Button::new(Text::new(title))
159 .padding(0)
160 .style(crate::style::ChromelessButton.into())
161 .on_press(Message::Toggle)
162 .into();
163
164 let children = if *is_collapsed {
165 vec![toggle_button]
166 } else {
167 vec![toggle_button, child_elements]
168 };
169
170 Column::with_children(children).into()
171 }
172 }
173 }
174 }
175}