1use crate::{
22 core::{
23 combine_uuids,
24 pool::Handle,
25 reflect::{prelude::*, Reflect},
26 uuid::{uuid, Uuid},
27 visitor::prelude::*,
28 ComponentProvider, TypeUuidProvider,
29 },
30 dropdown_list::{DropdownList, DropdownListBuilder, DropdownListMessage},
31 inspector::{
32 editors::{
33 PropertyEditorBuildContext, PropertyEditorDefinition,
34 PropertyEditorDefinitionContainer, PropertyEditorInstance,
35 PropertyEditorMessageContext, PropertyEditorTranslationContext,
36 },
37 make_expander_container, FieldKind, Inspector, InspectorBuilder, InspectorContext,
38 InspectorContextArgs, InspectorEnvironment, InspectorError, InspectorMessage,
39 PropertyChanged, PropertyFilter,
40 },
41 message::{MessageData, UiMessage},
42 utils::make_dropdown_list_option,
43 widget::{Widget, WidgetBuilder},
44 BuildContext, Control, Thickness, UiNode, UserInterface,
45};
46use fyrox_graph::SceneGraph;
47use std::{
48 any::TypeId,
49 fmt::{Debug, Formatter},
50 ops::{Deref, DerefMut},
51 str::FromStr,
52 sync::Arc,
53};
54use strum::VariantNames;
55
56const LOCAL_SYNC_FLAG: u64 = 0xFF;
57
58pub trait InspectableEnum: Debug + Reflect + Clone + TypeUuidProvider + Send + 'static {}
59
60impl<T: Debug + Reflect + Clone + TypeUuidProvider + Send + 'static> InspectableEnum for T {}
61
62#[derive(Debug, Clone, PartialEq)]
63pub enum EnumPropertyEditorMessage {
64 Variant(usize),
65 PropertyChanged(PropertyChanged),
66}
67impl MessageData for EnumPropertyEditorMessage {}
68
69#[derive(Visit, Reflect, ComponentProvider)]
70#[reflect(derived_type = "UiNode")]
71pub struct EnumPropertyEditor<T: InspectableEnum> {
72 pub widget: Widget,
73 pub variant_selector: Handle<UiNode>,
74 pub inspector: Handle<Inspector>,
75 #[visit(skip)]
76 #[reflect(hidden)]
77 pub definition: EnumPropertyEditorDefinition<T>,
78 #[visit(skip)]
79 #[reflect(hidden)]
80 pub definition_container: Arc<PropertyEditorDefinitionContainer>,
81 #[visit(skip)]
82 #[reflect(hidden)]
83 pub environment: Option<Arc<dyn InspectorEnvironment>>,
84 #[visit(skip)]
85 #[reflect(hidden)]
86 pub layer_index: usize,
87 #[visit(skip)]
88 #[reflect(hidden)]
89 pub generate_property_string_values: bool,
90 #[visit(skip)]
91 #[reflect(hidden)]
92 pub filter: PropertyFilter,
93 #[visit(skip)]
94 #[reflect(hidden)]
95 pub name_column_width: f32,
96 #[visit(skip)]
97 #[reflect(hidden)]
98 pub base_path: String,
99 #[visit(skip)]
100 #[reflect(hidden)]
101 pub has_parent_object: bool,
102}
103
104impl<T: InspectableEnum> Debug for EnumPropertyEditor<T> {
105 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
106 writeln!(f, "EnumPropertyEditor")
107 }
108}
109
110impl<T: InspectableEnum> Clone for EnumPropertyEditor<T> {
111 fn clone(&self) -> Self {
112 Self {
113 widget: self.widget.clone(),
114 variant_selector: self.variant_selector,
115 inspector: self.inspector,
116 definition: self.definition.clone(),
117 definition_container: self.definition_container.clone(),
118 environment: self.environment.clone(),
119 layer_index: self.layer_index,
120 generate_property_string_values: self.generate_property_string_values,
121 filter: self.filter.clone(),
122 name_column_width: self.name_column_width,
123 base_path: self.base_path.clone(),
124 has_parent_object: self.has_parent_object,
125 }
126 }
127}
128
129impl<T: InspectableEnum> Deref for EnumPropertyEditor<T> {
130 type Target = Widget;
131
132 fn deref(&self) -> &Self::Target {
133 &self.widget
134 }
135}
136
137impl<T: InspectableEnum> DerefMut for EnumPropertyEditor<T> {
138 fn deref_mut(&mut self) -> &mut Self::Target {
139 &mut self.widget
140 }
141}
142
143impl<T> TypeUuidProvider for EnumPropertyEditor<T>
144where
145 T: InspectableEnum,
146{
147 fn type_uuid() -> Uuid {
148 combine_uuids(
149 uuid!("0dbefddc-70fa-45a9-96f0-8fe25f6c1669"),
150 T::type_uuid(),
151 )
152 }
153}
154
155impl<T: InspectableEnum> Control for EnumPropertyEditor<T> {
156 fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
157 self.widget.handle_routed_message(ui, message);
158
159 if let Some(EnumPropertyEditorMessage::Variant(variant)) = message.data_for(self.handle) {
160 let variant = (self.definition.variant_generator)(*variant);
161
162 let ctx = InspectorContext::from_object(InspectorContextArgs {
163 object: &variant,
164 ctx: &mut ui.build_ctx(),
165 definition_container: self.definition_container.clone(),
166 environment: self.environment.clone(),
167 layer_index: self.layer_index,
168 generate_property_string_values: self.generate_property_string_values,
169 filter: self.filter.clone(),
170 name_column_width: self.name_column_width,
171 base_path: self.base_path.clone(),
172 has_parent_object: self.has_parent_object,
173 });
174
175 ui.send(self.inspector, InspectorMessage::Context(ctx));
176 ui.send_message(message.reverse());
177 } else if let Some(InspectorMessage::PropertyChanged(property_changed)) =
178 message.data_from(self.inspector)
179 {
180 ui.post(
181 self.handle,
182 EnumPropertyEditorMessage::PropertyChanged(property_changed.clone()),
183 )
184 }
185 }
186
187 fn preview_message(&self, ui: &UserInterface, message: &mut UiMessage) {
188 if message.flags != LOCAL_SYNC_FLAG {
189 if let Some(DropdownListMessage::Selection(Some(index))) =
190 message.data_from(self.variant_selector)
191 {
192 ui.send(self.handle, EnumPropertyEditorMessage::Variant(*index));
193 }
194 }
195 }
196}
197
198pub struct EnumPropertyEditorBuilder {
199 widget_builder: WidgetBuilder,
200 definition_container: Option<Arc<PropertyEditorDefinitionContainer>>,
201 environment: Option<Arc<dyn InspectorEnvironment>>,
202 variant_selector: Handle<UiNode>,
203 layer_index: usize,
204 generate_property_string_values: bool,
205 filter: PropertyFilter,
206}
207
208impl EnumPropertyEditorBuilder {
209 pub fn new(widget_builder: WidgetBuilder) -> Self {
210 Self {
211 widget_builder,
212 definition_container: None,
213 environment: None,
214 variant_selector: Handle::NONE,
215 layer_index: 0,
216 generate_property_string_values: false,
217 filter: Default::default(),
218 }
219 }
220
221 pub fn with_definition_container(
222 mut self,
223 definition_container: Arc<PropertyEditorDefinitionContainer>,
224 ) -> Self {
225 self.definition_container = Some(definition_container);
226 self
227 }
228
229 pub fn with_environment(mut self, environment: Option<Arc<dyn InspectorEnvironment>>) -> Self {
230 self.environment = environment;
231 self
232 }
233
234 pub fn with_variant_selector(mut self, variant_selector: Handle<UiNode>) -> Self {
235 self.variant_selector = variant_selector;
236 self
237 }
238
239 pub fn with_layer_index(mut self, layer_index: usize) -> Self {
240 self.layer_index = layer_index;
241 self
242 }
243
244 pub fn with_filter(mut self, filter: PropertyFilter) -> Self {
245 self.filter = filter;
246 self
247 }
248
249 pub fn with_generate_property_string_values(
250 mut self,
251 generate_property_string_values: bool,
252 ) -> Self {
253 self.generate_property_string_values = generate_property_string_values;
254 self
255 }
256
257 pub fn build<T: InspectableEnum>(
258 self,
259 ctx: &mut BuildContext,
260 definition: &EnumPropertyEditorDefinition<T>,
261 value: &T,
262 name_column_width: f32,
263 base_path: String,
264 has_parent_object: bool,
265 ) -> Handle<EnumPropertyEditor<T>> {
266 let definition_container = self
267 .definition_container
268 .unwrap_or_else(|| Arc::new(PropertyEditorDefinitionContainer::with_default_editors()));
269
270 let context = InspectorContext::from_object(InspectorContextArgs {
271 object: value,
272 ctx,
273 definition_container: definition_container.clone(),
274 environment: self.environment.clone(),
275 layer_index: self.layer_index,
276 generate_property_string_values: self.generate_property_string_values,
277 filter: self.filter.clone(),
278 name_column_width,
279 base_path: base_path.clone(),
280 has_parent_object,
281 });
282
283 let inspector = InspectorBuilder::new(WidgetBuilder::new())
284 .with_context(context)
285 .build(ctx);
286
287 let editor = EnumPropertyEditor {
288 widget: self
289 .widget_builder
290 .with_preview_messages(true)
291 .with_child(inspector)
292 .build(ctx),
293 variant_selector: self.variant_selector,
294 inspector,
295 definition: definition.clone(),
296 definition_container,
297 environment: self.environment,
298 layer_index: self.layer_index,
299 generate_property_string_values: self.generate_property_string_values,
300 filter: self.filter,
301 name_column_width,
302 base_path,
303 has_parent_object,
304 };
305
306 ctx.add(editor)
307 }
308}
309
310pub struct EnumPropertyEditorDefinition<T: InspectableEnum> {
311 pub variant_generator: fn(usize) -> T,
312 pub index_generator: fn(&T) -> usize,
313 pub names_generator: fn() -> Vec<String>,
314}
315
316impl<T: InspectableEnum + Default> EnumPropertyEditorDefinition<T> {
317 pub fn new_optional() -> EnumPropertyEditorDefinition<Option<T>> {
318 EnumPropertyEditorDefinition {
319 variant_generator: |i| match i {
320 0 => None,
321 1 => Some(Default::default()),
322 _ => unreachable!(),
323 },
324 index_generator: |v| match v {
325 None => 0,
326 Some(_) => 1,
327 },
328 names_generator: || vec!["None".to_string(), "Some".to_string()],
329 }
330 }
331}
332
333impl<T, E: Debug> EnumPropertyEditorDefinition<T>
334where
335 T: InspectableEnum + VariantNames + AsRef<str> + FromStr<Err = E> + Debug,
336{
337 pub fn new() -> Self {
338 Self {
339 variant_generator: |i| T::from_str(T::VARIANTS[i]).unwrap(),
340 index_generator: |in_var| {
341 T::VARIANTS
342 .iter()
343 .position(|v| v == &in_var.as_ref())
344 .unwrap()
345 },
346 names_generator: || T::VARIANTS.iter().map(|v| v.to_string()).collect(),
347 }
348 }
349}
350
351impl<T: InspectableEnum> Clone for EnumPropertyEditorDefinition<T> {
352 fn clone(&self) -> Self {
353 Self {
354 variant_generator: self.variant_generator,
355 index_generator: self.index_generator,
356 names_generator: self.names_generator,
357 }
358 }
359}
360
361impl<T> Debug for EnumPropertyEditorDefinition<T>
362where
363 T: InspectableEnum,
364{
365 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
366 write!(f, "EnumPropertyEditorDefinition")
367 }
368}
369
370impl<T> PropertyEditorDefinition for EnumPropertyEditorDefinition<T>
371where
372 T: InspectableEnum,
373{
374 fn value_type_id(&self) -> TypeId {
375 TypeId::of::<T>()
376 }
377
378 fn create_instance(
379 &self,
380 ctx: PropertyEditorBuildContext,
381 ) -> Result<PropertyEditorInstance, InspectorError> {
382 let value = ctx.property_info.cast_value::<T>()?;
383 let names = (self.names_generator)();
384
385 let variant_selector = DropdownListBuilder::new(
386 WidgetBuilder::new()
387 .on_column(1)
388 .with_margin(Thickness::top_bottom(1.0)),
389 )
390 .with_selected((self.index_generator)(value))
391 .with_items(
392 names
393 .into_iter()
394 .map(|name| make_dropdown_list_option(ctx.build_context, &name))
395 .collect::<Vec<_>>(),
396 )
397 .with_close_on_selection(true)
398 .build(ctx.build_context)
399 .to_base();
400
401 let editor;
402 let container = make_expander_container(
403 ctx.layer_index,
404 ctx.property_info.display_name,
405 ctx.property_info.doc,
406 variant_selector,
407 {
408 editor = EnumPropertyEditorBuilder::new(WidgetBuilder::new())
409 .with_variant_selector(variant_selector)
410 .with_layer_index(ctx.layer_index + 1)
411 .with_definition_container(ctx.definition_container.clone())
412 .with_environment(ctx.environment.clone())
413 .with_generate_property_string_values(ctx.generate_property_string_values)
414 .with_filter(ctx.filter)
415 .build(
416 ctx.build_context,
417 self,
418 value,
419 ctx.name_column_width,
420 ctx.base_path.clone(),
421 ctx.has_parent_object,
422 );
423 editor
424 },
425 ctx.name_column_width,
426 ctx.build_context,
427 );
428
429 Ok(PropertyEditorInstance::Custom {
430 container,
431 editor: editor.to_base(),
432 })
433 }
434
435 fn create_message(
436 &self,
437 ctx: PropertyEditorMessageContext,
438 ) -> Result<Option<UiMessage>, InspectorError> {
439 let value = ctx.property_info.cast_value::<T>()?;
440
441 let instance_ref = ctx
442 .ui
443 .node(ctx.instance)
444 .cast::<EnumPropertyEditor<T>>()
445 .expect("Must be EnumPropertyEditor!");
446
447 let variant_selector_ref = ctx
448 .ui
449 .node(instance_ref.variant_selector)
450 .cast::<DropdownList>()
451 .expect("Must be a DropDownList");
452
453 let variant_index = (self.index_generator)(value);
454 if Some(variant_index) != *variant_selector_ref.selection {
455 let environment = ctx.ui[instance_ref.inspector].context().environment.clone();
456 let mut selection_message = UiMessage::for_widget(
457 instance_ref.variant_selector,
458 DropdownListMessage::Selection(Some(variant_index)),
459 );
460 selection_message.flags = LOCAL_SYNC_FLAG;
461 ctx.ui.send_message(selection_message);
462
463 let inspector = instance_ref.inspector;
464
465 let context = InspectorContext::from_object(InspectorContextArgs {
466 object: value,
467 ctx: &mut ctx.ui.build_ctx(),
468 definition_container: ctx.definition_container.clone(),
469 environment,
470 layer_index: ctx.layer_index + 1,
471 generate_property_string_values: ctx.generate_property_string_values,
472 filter: ctx.filter,
473 name_column_width: ctx.name_column_width,
474 base_path: ctx.base_path.clone(),
475 has_parent_object: ctx.has_parent_object,
476 });
477
478 Ok(Some(UiMessage::for_widget(
479 inspector,
480 InspectorMessage::Context(context),
481 )))
482 } else {
483 let layer_index = ctx.layer_index;
484 let inspector_ctx = ctx.ui[instance_ref.inspector].context().clone();
485 if let Err(e) = inspector_ctx.sync(
486 value,
487 ctx.ui,
488 layer_index + 1,
489 ctx.generate_property_string_values,
490 ctx.filter,
491 ctx.base_path.clone(),
492 ) {
493 Err(InspectorError::Group(e))
494 } else {
495 Ok(None)
496 }
497 }
498 }
499
500 fn translate_message(&self, ctx: PropertyEditorTranslationContext) -> Option<PropertyChanged> {
501 if let Some(msg) = ctx.message.data::<EnumPropertyEditorMessage>() {
502 return match msg {
503 EnumPropertyEditorMessage::PropertyChanged(property_changed) => {
504 Some(PropertyChanged {
505 name: ctx.name.to_string(),
506 value: FieldKind::Inspectable(Box::new(property_changed.clone())),
507 })
508 }
509 EnumPropertyEditorMessage::Variant(index) => Some(PropertyChanged {
510 name: ctx.name.to_string(),
511 value: FieldKind::object((self.variant_generator)(*index)),
512 }),
513 };
514 }
515
516 None
517 }
518}