Skip to main content

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 a special "revert" button that is used to revert value to its parent's value.
23
24use crate::button::Button;
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    grid::{Column, GridBuilder, Row},
32    image::ImageBuilder,
33    inspector::{
34        editors::{
35            PropertyEditorBuildContext, PropertyEditorDefinition, PropertyEditorInstance,
36            PropertyEditorMessageContext, PropertyEditorTranslationContext,
37        },
38        FieldKind, InheritableAction, InspectorError, PropertyChanged,
39    },
40    message::{MessageData, UiMessage},
41    resources::REVERT_ICON,
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::SceneGraph;
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}
60impl MessageData for InheritablePropertyEditorMessage {}
61
62#[derive(Debug, Clone, Visit, Reflect, ComponentProvider)]
63#[reflect(derived_type = "UiNode")]
64pub struct InheritablePropertyEditor {
65    widget: Widget,
66    revert: Handle<Button>,
67    inner_editor: Handle<UiNode>,
68}
69
70impl Deref for InheritablePropertyEditor {
71    type Target = Widget;
72
73    fn deref(&self) -> &Self::Target {
74        &self.widget
75    }
76}
77
78impl DerefMut for InheritablePropertyEditor {
79    fn deref_mut(&mut self) -> &mut Self::Target {
80        &mut self.widget
81    }
82}
83
84uuid_provider!(InheritablePropertyEditor = "d5dce72c-a54b-4754-96a3-2e923eaa802f");
85
86impl Control for InheritablePropertyEditor {
87    fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
88        self.widget.handle_routed_message(ui, message);
89
90        if let Some(ButtonMessage::Click) = message.data_from(self.revert) {
91            ui.post(self.handle, InheritablePropertyEditorMessage::Revert);
92        } else if let Some(InheritablePropertyEditorMessage::Modified(modified)) = message.data() {
93            if message.destination() == self.handle {
94                ui.send(self.revert, WidgetMessage::Visibility(*modified));
95            }
96        }
97
98        // Re-cast messages from inner editor as message from this editor.
99        // If anything is listening to messages from this editor, let them hear the messages from the inner
100        // editor as if they were coming from this editor, but *do not* re-cast messages to the inner editor
101        // to this editor. Particularly, when the inner editor is made invisible, that does not mean that
102        // this editor should be invisible.
103        if message.destination() == self.inner_editor
104            && message.direction == MessageDirection::FromWidget
105        {
106            let mut clone = message.clone();
107            clone.destination = self.handle;
108            ui.send_message(clone);
109        }
110    }
111}
112
113struct InheritablePropertyEditorBuilder {
114    widget_builder: WidgetBuilder,
115    inner_editor: Handle<UiNode>,
116    container: Handle<UiNode>,
117    modified: bool,
118}
119
120impl InheritablePropertyEditorBuilder {
121    pub fn new(widget_builder: WidgetBuilder) -> Self {
122        Self {
123            widget_builder,
124            inner_editor: Handle::NONE,
125            container: Handle::NONE,
126            modified: false,
127        }
128    }
129
130    pub fn with_inner_editor(mut self, inner_editor: Handle<UiNode>) -> Self {
131        self.inner_editor = inner_editor;
132        self
133    }
134
135    pub fn with_container(mut self, container: Handle<UiNode>) -> Self {
136        self.container = container;
137        self
138    }
139
140    pub fn with_modified(mut self, modified: bool) -> Self {
141        self.modified = modified;
142        self
143    }
144
145    pub fn build(self, ctx: &mut BuildContext) -> Handle<InheritablePropertyEditor> {
146        let revert;
147        let grid = GridBuilder::new(WidgetBuilder::new().with_child(self.container).with_child({
148            revert = ButtonBuilder::new(
149                WidgetBuilder::new()
150                    .with_visibility(self.modified)
151                    .with_width(20.0)
152                    .with_height(20.0)
153                    .with_vertical_alignment(VerticalAlignment::Top)
154                    .with_tooltip(make_simple_tooltip(ctx, "Revert To Parent"))
155                    .with_margin(Thickness::uniform(1.0))
156                    .on_column(1),
157            )
158            .with_content(
159                ImageBuilder::new(
160                    WidgetBuilder::new()
161                        .with_background(ctx.style.property(Style::BRUSH_BRIGHTEST))
162                        .with_margin(Thickness::uniform(1.0))
163                        .with_width(16.0)
164                        .with_height(16.0),
165                )
166                .with_opt_texture(REVERT_ICON.clone())
167                .build(ctx),
168            )
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(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
213impl<T> PropertyEditorDefinition for InheritablePropertyEditorDefinition<T>
214where
215    T: Reflect + FieldValue,
216{
217    fn value_type_id(&self) -> TypeId {
218        TypeId::of::<InheritableVariable<T>>()
219    }
220
221    fn create_instance(
222        &self,
223        ctx: PropertyEditorBuildContext,
224    ) -> Result<PropertyEditorInstance, InspectorError> {
225        if let Some(definition) = ctx
226            .definition_container
227            .definitions()
228            .get(&TypeId::of::<T>())
229        {
230            let property_info = ctx.property_info;
231
232            let value = property_info.cast_value::<InheritableVariable<T>>()?;
233
234            let proxy_property_info = FieldRef {
235                metadata: &FieldMetadata {
236                    name: property_info.name,
237                    display_name: property_info.display_name,
238                    read_only: property_info.read_only,
239                    immutable_collection: property_info.immutable_collection,
240                    min_value: property_info.min_value,
241                    max_value: property_info.max_value,
242                    step: property_info.step,
243                    precision: property_info.precision,
244                    tag: property_info.tag,
245                    doc: property_info.doc,
246                },
247                value: &**value,
248            };
249
250            let instance =
251                definition
252                    .property_editor
253                    .create_instance(PropertyEditorBuildContext {
254                        build_context: ctx.build_context,
255                        property_info: &proxy_property_info,
256                        environment: ctx.environment.clone(),
257                        definition_container: ctx.definition_container.clone(),
258                        layer_index: ctx.layer_index,
259                        generate_property_string_values: ctx.generate_property_string_values,
260                        filter: ctx.filter,
261                        name_column_width: ctx.name_column_width,
262                        base_path: ctx.base_path.clone(),
263                        has_parent_object: ctx.has_parent_object,
264                    })?;
265
266            let wrapper = InheritablePropertyEditorBuilder::new(WidgetBuilder::new())
267                .with_container(match instance {
268                    PropertyEditorInstance::Simple { editor } => editor,
269                    PropertyEditorInstance::Custom { container, .. } => container,
270                })
271                .with_inner_editor(match instance {
272                    PropertyEditorInstance::Simple { editor } => editor,
273                    PropertyEditorInstance::Custom { editor, .. } => editor,
274                })
275                .with_modified(
276                    ctx.has_parent_object
277                        && ctx
278                            .property_info
279                            .cast_value::<InheritableVariable<T>>()?
280                            .is_modified(),
281                )
282                .build(ctx.build_context)
283                .to_base();
284
285            Ok(match instance {
286                PropertyEditorInstance::Simple { .. } => {
287                    PropertyEditorInstance::Simple { editor: wrapper }
288                }
289                PropertyEditorInstance::Custom { .. } => PropertyEditorInstance::Custom {
290                    container: wrapper,
291                    editor: wrapper,
292                },
293            })
294        } else {
295            Err(InspectorError::Custom("No editor!".to_string()))
296        }
297    }
298
299    fn create_message(
300        &self,
301        ctx: PropertyEditorMessageContext,
302    ) -> Result<Option<UiMessage>, InspectorError> {
303        if let Some(definition) = ctx
304            .definition_container
305            .definitions()
306            .get(&TypeId::of::<T>())
307        {
308            let instance = ctx
309                .ui
310                .node(ctx.instance)
311                .cast::<InheritablePropertyEditor>()
312                .unwrap();
313
314            let is_modified = ctx.has_parent_object
315                && ctx
316                    .property_info
317                    .cast_value::<InheritableVariable<T>>()?
318                    .is_modified();
319            ctx.ui.send_sync(
320                instance.handle,
321                InheritablePropertyEditorMessage::Modified(is_modified),
322            );
323
324            let property_info = ctx.property_info;
325
326            let value = property_info.cast_value::<InheritableVariable<T>>()?;
327
328            let proxy_property_info = FieldRef {
329                metadata: &FieldMetadata {
330                    name: property_info.name,
331                    display_name: property_info.display_name,
332                    read_only: property_info.read_only,
333                    immutable_collection: property_info.immutable_collection,
334                    min_value: property_info.min_value,
335                    max_value: property_info.max_value,
336                    step: property_info.step,
337                    precision: property_info.precision,
338                    tag: property_info.tag,
339                    doc: property_info.doc,
340                },
341                value: &**value,
342            };
343
344            return definition
345                .property_editor
346                .create_message(PropertyEditorMessageContext {
347                    property_info: &proxy_property_info,
348                    environment: ctx.environment.clone(),
349                    definition_container: ctx.definition_container.clone(),
350                    instance: instance.inner_editor,
351                    layer_index: ctx.layer_index,
352                    ui: ctx.ui,
353                    generate_property_string_values: ctx.generate_property_string_values,
354                    filter: ctx.filter,
355                    name_column_width: ctx.name_column_width,
356                    base_path: ctx.base_path.clone(),
357                    has_parent_object: ctx.has_parent_object,
358                });
359        }
360
361        Err(InspectorError::Custom("No editor!".to_string()))
362    }
363
364    fn translate_message(&self, ctx: PropertyEditorTranslationContext) -> Option<PropertyChanged> {
365        if let Some(InheritablePropertyEditorMessage::Revert) = ctx.message.data() {
366            return Some(PropertyChanged {
367                name: ctx.name.to_string(),
368                value: FieldKind::Inheritable(InheritableAction::Revert),
369            });
370        }
371
372        // Try to translate other messages using inner property editor.
373        if let Some(definition) = ctx
374            .definition_container
375            .definitions()
376            .get(&TypeId::of::<T>())
377        {
378            return definition.property_editor.translate_message(
379                PropertyEditorTranslationContext {
380                    environment: ctx.environment.clone(),
381                    name: ctx.name,
382                    message: ctx.message,
383                    definition_container: ctx.definition_container.clone(),
384                },
385            );
386        }
387
388        None
389    }
390}