fyrox_ui/inspector/editors/
inherit.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//! Property editor for [`InheritableVariable`]. It acts like a proxy to inner property, but also
22//! adds special "revert" button that is used to revert value to its parent's value.
23
24use crate::{
25    button::{ButtonBuilder, ButtonMessage},
26    core::{
27        pool::Handle, reflect::prelude::*, reflect::FieldValue, type_traits::prelude::*,
28        uuid_provider, variable::InheritableVariable, visitor::prelude::*, PhantomDataSendSync,
29    },
30    define_constructor,
31    grid::{Column, GridBuilder, Row},
32    inspector::{
33        editors::{
34            PropertyEditorBuildContext, PropertyEditorDefinition, PropertyEditorInstance,
35            PropertyEditorMessageContext, PropertyEditorTranslationContext,
36        },
37        InspectorError, PropertyChanged,
38    },
39    inspector::{FieldKind, InheritableAction},
40    message::UiMessage,
41    utils::make_simple_tooltip,
42    widget::WidgetBuilder,
43    BuildContext, Control, MessageDirection, Thickness, UiNode, UserInterface, VerticalAlignment,
44    Widget, WidgetMessage,
45};
46use fyrox_graph::BaseSceneGraph;
47use std::{
48    any::TypeId,
49    fmt::{Debug, Formatter},
50    ops::{Deref, DerefMut},
51};
52
53#[derive(Debug, Clone, PartialEq, Eq)]
54pub enum InheritablePropertyEditorMessage {
55    Revert,
56    Modified(bool),
57}
58
59impl InheritablePropertyEditorMessage {
60    define_constructor!(InheritablePropertyEditorMessage:Revert => fn revert(), layout: false);
61    define_constructor!(InheritablePropertyEditorMessage:Modified => fn modified(bool), layout: false);
62}
63
64#[derive(Debug, Clone, Visit, Reflect, ComponentProvider)]
65pub struct InheritablePropertyEditor {
66    widget: Widget,
67    revert: Handle<UiNode>,
68    inner_editor: Handle<UiNode>,
69}
70
71impl Deref for InheritablePropertyEditor {
72    type Target = Widget;
73
74    fn deref(&self) -> &Self::Target {
75        &self.widget
76    }
77}
78
79impl DerefMut for InheritablePropertyEditor {
80    fn deref_mut(&mut self) -> &mut Self::Target {
81        &mut self.widget
82    }
83}
84
85uuid_provider!(InheritablePropertyEditor = "d5dce72c-a54b-4754-96a3-2e923eaa802f");
86
87impl Control for InheritablePropertyEditor {
88    fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
89        self.widget.handle_routed_message(ui, message);
90
91        if let Some(ButtonMessage::Click) = message.data() {
92            if message.destination() == self.revert {
93                ui.send_message(InheritablePropertyEditorMessage::revert(
94                    self.handle,
95                    MessageDirection::FromWidget,
96                ));
97            }
98        } else if let Some(InheritablePropertyEditorMessage::Modified(modified)) = message.data() {
99            if message.destination() == self.handle {
100                ui.send_message(WidgetMessage::visibility(
101                    self.revert,
102                    MessageDirection::ToWidget,
103                    *modified,
104                ));
105            }
106        }
107
108        // Re-cast messages from inner editor as message from this editor.
109        // If anything is listening to messages from this editor, let them hear the messages from the inner
110        // editor as if they were coming from this editor, but *do not* re-cast messages to the inner editor
111        // to this editor. Particularly, when the inner editor is made invisible, that does not mean that
112        // this editor should be invisible.
113        if message.destination() == self.inner_editor
114            && message.direction == MessageDirection::FromWidget
115        {
116            let mut clone = message.clone();
117            clone.destination = self.handle;
118            ui.send_message(clone);
119        }
120    }
121}
122
123struct InheritablePropertyEditorBuilder {
124    widget_builder: WidgetBuilder,
125    inner_editor: Handle<UiNode>,
126    container: Handle<UiNode>,
127    modified: bool,
128}
129
130impl InheritablePropertyEditorBuilder {
131    pub fn new(widget_builder: WidgetBuilder) -> Self {
132        Self {
133            widget_builder,
134            inner_editor: Handle::NONE,
135            container: Handle::NONE,
136            modified: false,
137        }
138    }
139
140    pub fn with_inner_editor(mut self, inner_editor: Handle<UiNode>) -> Self {
141        self.inner_editor = inner_editor;
142        self
143    }
144
145    pub fn with_container(mut self, container: Handle<UiNode>) -> Self {
146        self.container = container;
147        self
148    }
149
150    pub fn with_modified(mut self, modified: bool) -> Self {
151        self.modified = modified;
152        self
153    }
154
155    pub fn build(self, ctx: &mut BuildContext) -> Handle<UiNode> {
156        let revert;
157        let grid = GridBuilder::new(WidgetBuilder::new().with_child(self.container).with_child({
158            revert = ButtonBuilder::new(
159                WidgetBuilder::new()
160                    .with_visibility(self.modified)
161                    .with_width(16.0)
162                    .with_height(16.0)
163                    .with_vertical_alignment(VerticalAlignment::Top)
164                    .with_tooltip(make_simple_tooltip(ctx, "Revert To Parent"))
165                    .with_margin(Thickness::uniform(1.0))
166                    .on_column(1),
167            )
168            .with_text("<")
169            .build(ctx);
170            revert
171        }))
172        .add_row(Row::auto())
173        .add_column(Column::stretch())
174        .add_column(Column::auto())
175        .build(ctx);
176
177        ctx.add_node(UiNode::new(InheritablePropertyEditor {
178            widget: self.widget_builder.with_child(grid).build(ctx),
179            revert,
180            inner_editor: self.inner_editor,
181        }))
182    }
183}
184
185pub struct InheritablePropertyEditorDefinition<T>
186where
187    T: FieldValue,
188{
189    #[allow(dead_code)]
190    phantom: PhantomDataSendSync<T>,
191}
192
193impl<T> InheritablePropertyEditorDefinition<T>
194where
195    T: FieldValue,
196{
197    pub fn new() -> Self {
198        Self {
199            phantom: Default::default(),
200        }
201    }
202}
203
204impl<T> Debug for InheritablePropertyEditorDefinition<T>
205where
206    T: Reflect + FieldValue,
207{
208    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
209        writeln!(f, "InheritablePropertyEditorDefinition")
210    }
211}
212
213fn make_proxy<'a, 'b, 'c, T>(
214    property_info: &'b FieldInfo<'a, 'c>,
215) -> Result<FieldInfo<'a, 'c>, InspectorError>
216where
217    T: Reflect + FieldValue,
218    'b: 'a,
219{
220    let value = property_info.cast_value::<InheritableVariable<T>>()?;
221
222    Ok(FieldInfo {
223        owner_type_id: TypeId::of::<T>(),
224        name: property_info.name,
225        display_name: property_info.display_name,
226        value: &**value,
227        reflect_value: &**value,
228        read_only: property_info.read_only,
229        immutable_collection: property_info.immutable_collection,
230        min_value: property_info.min_value,
231        max_value: property_info.max_value,
232        step: property_info.step,
233        precision: property_info.precision,
234        description: property_info.description,
235        tag: property_info.tag,
236        type_name: property_info.type_name,
237        doc: property_info.doc,
238    })
239}
240
241impl<T> PropertyEditorDefinition for InheritablePropertyEditorDefinition<T>
242where
243    T: Reflect + FieldValue,
244{
245    fn value_type_id(&self) -> TypeId {
246        TypeId::of::<InheritableVariable<T>>()
247    }
248
249    fn create_instance(
250        &self,
251        ctx: PropertyEditorBuildContext,
252    ) -> Result<PropertyEditorInstance, InspectorError> {
253        if let Some(definition) = ctx
254            .definition_container
255            .definitions()
256            .get(&TypeId::of::<T>())
257        {
258            let instance =
259                definition
260                    .property_editor
261                    .create_instance(PropertyEditorBuildContext {
262                        build_context: ctx.build_context,
263                        property_info: &make_proxy::<T>(ctx.property_info)?,
264                        environment: ctx.environment.clone(),
265                        definition_container: ctx.definition_container.clone(),
266                        sync_flag: ctx.sync_flag,
267                        layer_index: ctx.layer_index,
268                        generate_property_string_values: ctx.generate_property_string_values,
269                        filter: ctx.filter,
270                        name_column_width: ctx.name_column_width,
271                    })?;
272
273            let wrapper = InheritablePropertyEditorBuilder::new(WidgetBuilder::new())
274                .with_container(match instance {
275                    PropertyEditorInstance::Simple { editor } => editor,
276                    PropertyEditorInstance::Custom { container, .. } => container,
277                })
278                .with_inner_editor(match instance {
279                    PropertyEditorInstance::Simple { editor } => editor,
280                    PropertyEditorInstance::Custom { editor, .. } => editor,
281                })
282                .with_modified(
283                    ctx.property_info
284                        .cast_value::<InheritableVariable<T>>()?
285                        .is_modified(),
286                )
287                .build(ctx.build_context);
288
289            Ok(match instance {
290                PropertyEditorInstance::Simple { .. } => {
291                    PropertyEditorInstance::Simple { editor: wrapper }
292                }
293                PropertyEditorInstance::Custom { .. } => PropertyEditorInstance::Custom {
294                    container: wrapper,
295                    editor: wrapper,
296                },
297            })
298        } else {
299            Err(InspectorError::Custom("No editor!".to_string()))
300        }
301    }
302
303    fn create_message(
304        &self,
305        ctx: PropertyEditorMessageContext,
306    ) -> Result<Option<UiMessage>, InspectorError> {
307        if let Some(definition) = ctx
308            .definition_container
309            .definitions()
310            .get(&TypeId::of::<T>())
311        {
312            let instance = ctx
313                .ui
314                .node(ctx.instance)
315                .cast::<InheritablePropertyEditor>()
316                .unwrap();
317
318            ctx.ui
319                .send_message(InheritablePropertyEditorMessage::modified(
320                    instance.handle,
321                    MessageDirection::ToWidget,
322                    ctx.property_info
323                        .cast_value::<InheritableVariable<T>>()?
324                        .is_modified(),
325                ));
326
327            return definition
328                .property_editor
329                .create_message(PropertyEditorMessageContext {
330                    property_info: &make_proxy::<T>(ctx.property_info)?,
331                    environment: ctx.environment.clone(),
332                    definition_container: ctx.definition_container.clone(),
333                    sync_flag: ctx.sync_flag,
334                    instance: instance.inner_editor,
335                    layer_index: ctx.layer_index,
336                    ui: ctx.ui,
337                    generate_property_string_values: ctx.generate_property_string_values,
338                    filter: ctx.filter,
339                    name_column_width: ctx.name_column_width,
340                });
341        }
342
343        Err(InspectorError::Custom("No editor!".to_string()))
344    }
345
346    fn translate_message(&self, ctx: PropertyEditorTranslationContext) -> Option<PropertyChanged> {
347        if let Some(InheritablePropertyEditorMessage::Revert) = ctx.message.data() {
348            return Some(PropertyChanged {
349                name: ctx.name.to_string(),
350                owner_type_id: ctx.owner_type_id,
351                value: FieldKind::Inheritable(InheritableAction::Revert),
352            });
353        }
354
355        // Try translate other messages using inner property editor.
356        if let Some(definition) = ctx
357            .definition_container
358            .definitions()
359            .get(&TypeId::of::<T>())
360        {
361            return definition.property_editor.translate_message(
362                PropertyEditorTranslationContext {
363                    environment: ctx.environment.clone(),
364                    name: ctx.name,
365                    owner_type_id: ctx.owner_type_id,
366                    message: ctx.message,
367                    definition_container: ctx.definition_container.clone(),
368                },
369            );
370        }
371
372        None
373    }
374}