rg3d_ui/inspector/editors/
enumeration.rs

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