Skip to main content

fyrox_ui/inspector/editors/
inspectable.rs

1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21//! A general-purpose property editor definition that creates
22//! a nested inspector within an [Expander](crate::expander::Expander) widget.
23use crate::inspector::InspectorContextArgs;
24use crate::{
25    core::reflect::prelude::*,
26    inspector::{
27        editors::{
28            PropertyEditorBuildContext, PropertyEditorDefinition, PropertyEditorInstance,
29            PropertyEditorMessageContext, PropertyEditorTranslationContext,
30        },
31        make_expander_container, FieldAction, Inspector, InspectorBuilder, InspectorContext,
32        InspectorError, InspectorMessage, PropertyChanged,
33    },
34    message::{MessageDirection, UiMessage},
35    widget::WidgetBuilder,
36    UiNode,
37};
38use fyrox_core::pool::Handle;
39use fyrox_core::PhantomDataSendSync;
40use fyrox_graph::SceneGraph;
41use std::{
42    any::TypeId,
43    fmt::{Debug, Formatter},
44};
45
46/// A general-purpose property editor definition that creates
47/// a nested inspector within an [Expander](crate::expander::Expander) widget to allow the user
48/// to edited properties of type T.
49/// The expander is labeled with [FieldMetadata::display_name].
50/// The layer_index for the inner inspector is increased by 1.
51pub struct InspectablePropertyEditorDefinition<T>
52where
53    T: Reflect,
54{
55    #[allow(dead_code)]
56    phantom: PhantomDataSendSync<T>,
57}
58
59impl<T> InspectablePropertyEditorDefinition<T>
60where
61    T: Reflect,
62{
63    pub fn new() -> Self {
64        Self {
65            phantom: Default::default(),
66        }
67    }
68}
69
70impl<T> Debug for InspectablePropertyEditorDefinition<T>
71where
72    T: Reflect,
73{
74    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
75        writeln!(f, "InspectablePropertyEditorDefinition")
76    }
77}
78
79impl<T> PropertyEditorDefinition for InspectablePropertyEditorDefinition<T>
80where
81    T: Reflect,
82{
83    fn value_type_id(&self) -> TypeId {
84        TypeId::of::<T>()
85    }
86
87    fn create_instance(
88        &self,
89        ctx: PropertyEditorBuildContext,
90    ) -> Result<PropertyEditorInstance, InspectorError> {
91        let value = ctx.property_info.cast_value::<T>()?;
92
93        let inspector_context = InspectorContext::from_object(InspectorContextArgs {
94            object: value,
95            ctx: ctx.build_context,
96            definition_container: ctx.definition_container.clone(),
97            environment: ctx.environment.clone(),
98            layer_index: ctx.layer_index + 1,
99            generate_property_string_values: ctx.generate_property_string_values,
100            filter: ctx.filter,
101            name_column_width: ctx.name_column_width,
102            base_path: ctx.base_path.clone(),
103            has_parent_object: ctx.has_parent_object,
104        });
105
106        let editor;
107        let container = make_expander_container(
108            ctx.layer_index,
109            ctx.property_info.display_name,
110            ctx.property_info.doc,
111            Handle::<UiNode>::NONE,
112            {
113                editor = InspectorBuilder::new(WidgetBuilder::new())
114                    .with_context(inspector_context)
115                    .build(ctx.build_context)
116                    .to_base();
117                editor
118            },
119            ctx.name_column_width,
120            ctx.build_context,
121        );
122
123        Ok(PropertyEditorInstance::Custom { container, editor })
124    }
125
126    /// Instead of creating a message to update its widget,
127    /// call [InspectorContext::sync] to directly send whatever messages are necessary
128    /// and return None.
129    fn create_message(
130        &self,
131        ctx: PropertyEditorMessageContext,
132    ) -> Result<Option<UiMessage>, InspectorError> {
133        let value = ctx.property_info.cast_value::<T>()?;
134
135        let mut error_group = Vec::new();
136
137        let inspector_context = ctx
138            .ui
139            .node(ctx.instance)
140            .cast::<Inspector>()
141            .expect("Must be Inspector!")
142            .context()
143            .clone();
144        if let Err(e) = inspector_context.sync(
145            value,
146            ctx.ui,
147            ctx.layer_index + 1,
148            ctx.generate_property_string_values,
149            ctx.filter,
150            ctx.base_path.clone(),
151        ) {
152            error_group.extend(e)
153        }
154
155        if error_group.is_empty() {
156            Ok(None)
157        } else {
158            Err(InspectorError::Group(error_group))
159        }
160    }
161
162    fn translate_message(&self, ctx: PropertyEditorTranslationContext) -> Option<PropertyChanged> {
163        if let Some(InspectorMessage::PropertyChanged(msg)) = ctx.message.data::<InspectorMessage>()
164        {
165            if ctx.message.direction() == MessageDirection::FromWidget {
166                return Some(PropertyChanged {
167                    name: ctx.name.to_owned(),
168
169                    action: FieldAction::InspectableAction(Box::new(msg.clone())),
170                });
171            }
172        }
173
174        None
175    }
176}