use crate::button::Button;
use crate::{
button::{ButtonBuilder, ButtonMessage},
core::{
pool::Handle, reflect::prelude::*, reflect::FieldValue, type_traits::prelude::*,
uuid_provider, variable::InheritableVariable, visitor::prelude::*, PhantomDataSendSync,
},
grid::{Column, GridBuilder, Row},
image::ImageBuilder,
inspector::{
editors::{
PropertyEditorBuildContext, PropertyEditorDefinition, PropertyEditorInstance,
PropertyEditorMessageContext, PropertyEditorTranslationContext,
},
FieldAction, InheritableAction, InspectorError, PropertyChanged,
},
message::{MessageData, UiMessage},
resources::REVERT_ICON,
style::{resource::StyleResourceExt, Style},
utils::make_simple_tooltip,
widget::WidgetBuilder,
BuildContext, Control, MessageDirection, Thickness, UiNode, UserInterface, VerticalAlignment,
Widget, WidgetMessage,
};
use fyrox_graph::SceneGraph;
use std::{
any::TypeId,
fmt::{Debug, Formatter},
ops::{Deref, DerefMut},
};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum InheritablePropertyEditorMessage {
Revert,
Modified(bool),
}
impl MessageData for InheritablePropertyEditorMessage {}
#[derive(Debug, Clone, Visit, Reflect, ComponentProvider)]
#[reflect(derived_type = "UiNode")]
pub struct InheritablePropertyEditor {
widget: Widget,
revert: Handle<Button>,
inner_editor: Handle<UiNode>,
}
impl Deref for InheritablePropertyEditor {
type Target = Widget;
fn deref(&self) -> &Self::Target {
&self.widget
}
}
impl DerefMut for InheritablePropertyEditor {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.widget
}
}
uuid_provider!(InheritablePropertyEditor = "d5dce72c-a54b-4754-96a3-2e923eaa802f");
impl Control for InheritablePropertyEditor {
fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
self.widget.handle_routed_message(ui, message);
if let Some(ButtonMessage::Click) = message.data_from(self.revert) {
ui.post(self.handle, InheritablePropertyEditorMessage::Revert);
} else if let Some(InheritablePropertyEditorMessage::Modified(modified)) = message.data() {
if message.destination() == self.handle {
ui.send(self.revert, WidgetMessage::Visibility(*modified));
}
}
if message.destination() == self.inner_editor
&& message.direction == MessageDirection::FromWidget
{
let mut clone = message.clone();
clone.destination = self.handle;
ui.send_message(clone);
}
}
}
struct InheritablePropertyEditorBuilder {
widget_builder: WidgetBuilder,
inner_editor: Handle<UiNode>,
container: Handle<UiNode>,
modified: bool,
}
impl InheritablePropertyEditorBuilder {
pub fn new(widget_builder: WidgetBuilder) -> Self {
Self {
widget_builder,
inner_editor: Handle::NONE,
container: Handle::NONE,
modified: false,
}
}
pub fn with_inner_editor(mut self, inner_editor: Handle<UiNode>) -> Self {
self.inner_editor = inner_editor;
self
}
pub fn with_container(mut self, container: Handle<UiNode>) -> Self {
self.container = container;
self
}
pub fn with_modified(mut self, modified: bool) -> Self {
self.modified = modified;
self
}
pub fn build(self, ctx: &mut BuildContext) -> Handle<InheritablePropertyEditor> {
let revert;
let grid = GridBuilder::new(WidgetBuilder::new().with_child(self.container).with_child({
revert = ButtonBuilder::new(
WidgetBuilder::new()
.with_visibility(self.modified)
.with_width(22.0)
.with_height(22.0)
.with_vertical_alignment(VerticalAlignment::Top)
.with_tooltip(make_simple_tooltip(ctx, "Revert To Parent"))
.with_margin(Thickness::uniform(1.0))
.on_column(1),
)
.with_content(
ImageBuilder::new(
WidgetBuilder::new()
.with_background(ctx.style.property(Style::BRUSH_BRIGHTEST))
.with_margin(Thickness::uniform(3.0))
.with_width(16.0)
.with_height(16.0),
)
.with_opt_texture(REVERT_ICON.clone())
.build(ctx),
)
.build(ctx);
revert
}))
.add_row(Row::auto())
.add_column(Column::stretch())
.add_column(Column::auto())
.build(ctx);
ctx.add(InheritablePropertyEditor {
widget: self.widget_builder.with_child(grid).build(ctx),
revert,
inner_editor: self.inner_editor,
})
}
}
pub struct InheritablePropertyEditorDefinition<T>
where
T: FieldValue,
{
#[allow(dead_code)]
phantom: PhantomDataSendSync<T>,
}
impl<T> InheritablePropertyEditorDefinition<T>
where
T: FieldValue,
{
pub fn new() -> Self {
Self {
phantom: Default::default(),
}
}
}
impl<T> Debug for InheritablePropertyEditorDefinition<T>
where
T: Reflect + FieldValue,
{
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
writeln!(f, "InheritablePropertyEditorDefinition")
}
}
impl<T> PropertyEditorDefinition for InheritablePropertyEditorDefinition<T>
where
T: Reflect + FieldValue,
{
fn value_type_id(&self) -> TypeId {
TypeId::of::<InheritableVariable<T>>()
}
fn create_instance(
&self,
ctx: PropertyEditorBuildContext,
) -> Result<PropertyEditorInstance, InspectorError> {
if let Some(definition) = ctx
.definition_container
.definitions()
.get(&TypeId::of::<T>())
{
let property_info = ctx.property_info;
let value = property_info.cast_value::<InheritableVariable<T>>()?;
let proxy_property_info = FieldRef {
metadata: &FieldMetadata {
name: property_info.name,
display_name: property_info.display_name,
read_only: property_info.read_only,
immutable_collection: property_info.immutable_collection,
min_value: property_info.min_value,
max_value: property_info.max_value,
step: property_info.step,
precision: property_info.precision,
tag: property_info.tag,
doc: property_info.doc,
},
value: &**value,
};
let instance =
definition
.property_editor
.create_instance(PropertyEditorBuildContext {
build_context: ctx.build_context,
property_info: &proxy_property_info,
environment: ctx.environment.clone(),
definition_container: ctx.definition_container.clone(),
layer_index: ctx.layer_index,
generate_property_string_values: ctx.generate_property_string_values,
filter: ctx.filter,
name_column_width: ctx.name_column_width,
base_path: ctx.base_path.clone(),
has_parent_object: ctx.has_parent_object,
})?;
let wrapper = InheritablePropertyEditorBuilder::new(WidgetBuilder::new())
.with_container(match instance {
PropertyEditorInstance::Simple { editor } => editor,
PropertyEditorInstance::Custom { container, .. } => container,
})
.with_inner_editor(match instance {
PropertyEditorInstance::Simple { editor } => editor,
PropertyEditorInstance::Custom { editor, .. } => editor,
})
.with_modified(
ctx.has_parent_object
&& ctx
.property_info
.cast_value::<InheritableVariable<T>>()?
.is_modified(),
)
.build(ctx.build_context)
.to_base();
Ok(match instance {
PropertyEditorInstance::Simple { .. } => {
PropertyEditorInstance::Simple { editor: wrapper }
}
PropertyEditorInstance::Custom { .. } => PropertyEditorInstance::Custom {
container: wrapper,
editor: wrapper,
},
})
} else {
Err(InspectorError::Custom("No editor!".to_string()))
}
}
fn create_message(
&self,
ctx: PropertyEditorMessageContext,
) -> Result<Option<UiMessage>, InspectorError> {
if let Some(definition) = ctx
.definition_container
.definitions()
.get(&TypeId::of::<T>())
{
let instance = ctx
.ui
.node(ctx.instance)
.cast::<InheritablePropertyEditor>()
.unwrap();
let is_modified = ctx.has_parent_object
&& ctx
.property_info
.cast_value::<InheritableVariable<T>>()?
.is_modified();
ctx.ui.send_sync(
instance.handle,
InheritablePropertyEditorMessage::Modified(is_modified),
);
let property_info = ctx.property_info;
let value = property_info.cast_value::<InheritableVariable<T>>()?;
let proxy_property_info = FieldRef {
metadata: &FieldMetadata {
name: property_info.name,
display_name: property_info.display_name,
read_only: property_info.read_only,
immutable_collection: property_info.immutable_collection,
min_value: property_info.min_value,
max_value: property_info.max_value,
step: property_info.step,
precision: property_info.precision,
tag: property_info.tag,
doc: property_info.doc,
},
value: &**value,
};
return definition
.property_editor
.create_message(PropertyEditorMessageContext {
property_info: &proxy_property_info,
environment: ctx.environment.clone(),
definition_container: ctx.definition_container.clone(),
instance: instance.inner_editor,
layer_index: ctx.layer_index,
ui: ctx.ui,
generate_property_string_values: ctx.generate_property_string_values,
filter: ctx.filter,
name_column_width: ctx.name_column_width,
base_path: ctx.base_path.clone(),
has_parent_object: ctx.has_parent_object,
});
}
Err(InspectorError::Custom("No editor!".to_string()))
}
fn translate_message(&self, ctx: PropertyEditorTranslationContext) -> Option<PropertyChanged> {
if let Some(InheritablePropertyEditorMessage::Revert) = ctx.message.data() {
return Some(PropertyChanged {
name: ctx.name.to_string(),
action: FieldAction::InheritableAction(InheritableAction::Revert),
});
}
if let Some(definition) = ctx
.definition_container
.definitions()
.get(&TypeId::of::<T>())
{
return definition.property_editor.translate_message(
PropertyEditorTranslationContext {
environment: ctx.environment.clone(),
name: ctx.name,
message: ctx.message,
definition_container: ctx.definition_container.clone(),
},
);
}
None
}
}