1use crate::fyrox::{
22 core::{
23 pool::Handle, reflect::prelude::*, type_traits::prelude::*, uuid_provider,
24 visitor::prelude::*,
25 },
26 gui::{
27 border::BorderBuilder,
28 decorator::DecoratorBuilder,
29 define_widget_deref,
30 dropdown_list::{DropdownListBuilder, DropdownListMessage},
31 grid::{Column, GridBuilder, Row},
32 list_view::{ListViewBuilder, ListViewMessage},
33 message::UiMessage,
34 text::{TextBuilder, TextMessage},
35 utils::make_simple_tooltip,
36 widget::{Widget, WidgetBuilder},
37 BuildContext, Control, HorizontalAlignment, Thickness, UiNode, UserInterface,
38 VerticalAlignment,
39 },
40 scene::sound::{AudioBus, AudioBusGraph},
41};
42use fyrox::gui::dropdown_list::DropdownList;
43use fyrox::gui::list_view::ListView;
44use fyrox::gui::message::MessageData;
45use fyrox::gui::style::resource::StyleResourceExt;
46use fyrox::gui::style::Style;
47use fyrox::gui::text::Text;
48use fyrox::gui::utils::make_dropdown_list_option;
49
50#[derive(Debug, Clone, PartialEq)]
51pub enum AudioBusViewMessage {
52 ChangeParent(Handle<AudioBus>),
53 PossibleParentBuses(Vec<(Handle<AudioBus>, String)>),
54 EffectNames(Vec<String>),
55 Name(String),
56}
57impl MessageData for AudioBusViewMessage {}
58
59#[derive(Clone, Visit, Reflect, Debug, ComponentProvider)]
60#[reflect(derived_type = "UiNode")]
61pub struct AudioBusView {
62 widget: Widget,
63 pub bus: Handle<AudioBus>,
64 parent_bus_selector: Handle<DropdownList>,
65 possible_parent_buses: Vec<Handle<AudioBus>>,
66 effect_names_list: Handle<ListView>,
67 name: Handle<Text>,
68}
69
70define_widget_deref!(AudioBusView);
71
72uuid_provider!(AudioBusView = "5439e3a9-096a-4155-922c-ed57a76a46f3");
73
74impl Control for AudioBusView {
75 fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
76 self.widget.handle_routed_message(ui, message);
77
78 if let Some(msg) = message.data_for::<AudioBusViewMessage>(self.handle) {
79 match msg {
80 AudioBusViewMessage::ChangeParent(_) => {
81 }
83 AudioBusViewMessage::PossibleParentBuses(buses) => {
84 self.possible_parent_buses =
85 buses.iter().map(|(handle, _)| *handle).collect::<Vec<_>>();
86
87 let items = make_items(buses, &mut ui.build_ctx());
88
89 ui.send(self.parent_bus_selector, DropdownListMessage::Items(items))
90 }
91 AudioBusViewMessage::EffectNames(names) => {
92 let items = make_effect_names(names, &mut ui.build_ctx());
93 ui.send(self.effect_names_list, ListViewMessage::Items(items));
94 }
95 AudioBusViewMessage::Name(new_name) => {
96 ui.send(self.name, TextMessage::Text(new_name.clone()))
97 }
98 }
99 }
100
101 if let Some(DropdownListMessage::Selection(Some(selection))) =
102 message.data_from(self.parent_bus_selector)
103 {
104 let parent = self.possible_parent_buses[*selection];
105 ui.post(self.handle, AudioBusViewMessage::ChangeParent(parent));
106 }
107 }
108}
109
110fn make_items(buses: &[(Handle<AudioBus>, String)], ctx: &mut BuildContext) -> Vec<Handle<UiNode>> {
111 buses
112 .iter()
113 .map(|(_, name)| make_dropdown_list_option(ctx, name))
114 .collect::<Vec<_>>()
115}
116
117fn make_effect_names(names: &[String], ctx: &mut BuildContext) -> Vec<Handle<UiNode>> {
118 if names.is_empty() {
119 vec![TextBuilder::new(
120 WidgetBuilder::new().with_foreground(ctx.style.property(Style::BRUSH_LIGHTER)),
121 )
122 .with_text("No Effects")
123 .with_horizontal_text_alignment(HorizontalAlignment::Center)
124 .build(ctx)
125 .to_base()]
126 } else {
127 names
128 .iter()
129 .map(|n| {
130 TextBuilder::new(WidgetBuilder::new().with_margin(Thickness::uniform(1.0)))
131 .with_text(n)
132 .with_horizontal_text_alignment(HorizontalAlignment::Center)
133 .build(ctx)
134 .to_base()
135 })
136 .collect::<Vec<_>>()
137 }
138}
139
140pub struct AudioBusViewBuilder {
141 widget_builder: WidgetBuilder,
142 name: String,
143 effect_names: Vec<String>,
144 bus: Handle<AudioBus>,
145 parent_bus: Handle<AudioBus>,
146 possible_parent_buses: Vec<(Handle<AudioBus>, String)>,
147}
148
149impl AudioBusViewBuilder {
150 pub fn new(widget_builder: WidgetBuilder) -> Self {
151 Self {
152 widget_builder,
153 name: AudioBusGraph::PRIMARY_BUS.to_string(),
154 effect_names: Default::default(),
155 bus: Default::default(),
156 parent_bus: Default::default(),
157 possible_parent_buses: Default::default(),
158 }
159 }
160
161 pub fn with_name<S: AsRef<str>>(mut self, name: S) -> Self {
162 name.as_ref().clone_into(&mut self.name);
163 self
164 }
165
166 pub fn with_audio_bus(mut self, bus: Handle<AudioBus>) -> Self {
167 self.bus = bus;
168 self
169 }
170
171 pub fn with_effect_names(mut self, names: Vec<String>) -> Self {
172 self.effect_names = names;
173 self
174 }
175
176 pub fn with_parent_bus(mut self, parent_bus: Handle<AudioBus>) -> Self {
177 self.parent_bus = parent_bus;
178 self
179 }
180
181 pub fn with_possible_parent_buses(
182 mut self,
183 possible_parent_buses: Vec<(Handle<AudioBus>, String)>,
184 ) -> Self {
185 self.possible_parent_buses = possible_parent_buses;
186 self
187 }
188
189 pub fn build(self, ctx: &mut BuildContext) -> Handle<AudioBusView> {
190 let effect_names_list;
191 let name;
192 let parent_bus_selector;
193 let grid = GridBuilder::new(
194 WidgetBuilder::new()
195 .with_child(
196 BorderBuilder::new(WidgetBuilder::new().on_row(0).on_column(0).with_child({
197 name = TextBuilder::new(
198 WidgetBuilder::new()
199 .with_horizontal_alignment(HorizontalAlignment::Center)
200 .with_vertical_alignment(VerticalAlignment::Center),
201 )
202 .with_text(self.name)
203 .build(ctx);
204 name
205 }))
206 .build(ctx),
207 )
208 .with_child(
209 BorderBuilder::new(
210 WidgetBuilder::new()
211 .on_row(1)
212 .on_column(0)
213 .with_margin(Thickness::uniform(1.0))
214 .with_tooltip(make_simple_tooltip(
215 ctx,
216 "A list of effects applied to the audio bus.",
217 ))
218 .with_child({
219 effect_names_list = ListViewBuilder::new(
220 WidgetBuilder::new().with_margin(Thickness::uniform(1.0)),
221 )
222 .with_items(make_effect_names(&self.effect_names, ctx))
223 .build(ctx);
224 effect_names_list
225 }),
226 )
227 .build(ctx),
228 )
229 .with_child({
230 parent_bus_selector = DropdownListBuilder::new(
231 WidgetBuilder::new()
232 .with_visibility(self.parent_bus.is_some())
233 .on_row(2)
234 .on_column(0)
235 .with_margin(Thickness::uniform(1.0))
236 .with_tooltip(make_simple_tooltip(
237 ctx,
238 "A parent audio bus to which this audio bus will send its data.",
239 )),
240 )
241 .with_opt_selected(
242 self.possible_parent_buses
243 .iter()
244 .position(|(h, _)| *h == self.parent_bus),
245 )
246 .with_items(make_items(&self.possible_parent_buses, ctx))
247 .build(ctx);
248 parent_bus_selector
249 }),
250 )
251 .add_row(Row::strict(25.0))
252 .add_row(Row::stretch())
253 .add_row(Row::strict(25.0))
254 .add_column(Column::stretch())
255 .build(ctx);
256
257 let view = AudioBusView {
258 widget: self
259 .widget_builder
260 .with_child(
261 DecoratorBuilder::new(BorderBuilder::new(
262 WidgetBuilder::new().with_child(grid),
263 ))
264 .build(ctx),
265 )
266 .build(ctx),
267 bus: self.bus,
268 parent_bus_selector,
269 possible_parent_buses: self
270 .possible_parent_buses
271 .into_iter()
272 .map(|(handle, _)| handle)
273 .collect::<Vec<_>>(),
274 effect_names_list,
275 name,
276 };
277 ctx.add(view)
278 }
279}
280
281#[cfg(test)]
282mod test {
283 use crate::audio::bus::AudioBusViewBuilder;
284 use fyrox::{gui::test::test_widget_deletion, gui::widget::WidgetBuilder};
285
286 #[test]
287 fn test_deletion() {
288 test_widget_deletion(|ctx| AudioBusViewBuilder::new(WidgetBuilder::new()).build(ctx));
289 }
290}