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