diff --git a/editor/src/inspector/mod.rs b/editor/src/inspector/mod.rs index a9292d076..44667e0b7 100644 --- a/editor/src/inspector/mod.rs +++ b/editor/src/inspector/mod.rs @@ -1,31 +1,32 @@ -use crate::fyrox::{ - asset::manager::ResourceManager, - core::{ - color::Color, - log::{Log, MessageKind}, - pool::ErasedHandle, - pool::Handle, - reflect::prelude::*, - }, - engine::SerializationContext, - graph::BaseSceneGraph, - gui::{ - button::ButtonMessage, - grid::{Column, GridBuilder, Row}, - inspector::{ - editors::PropertyEditorDefinitionContainer, InspectorBuilder, InspectorContext, - InspectorEnvironment, InspectorMessage, - }, - message::{MessageDirection, UiMessage}, - scroll_viewer::ScrollViewerBuilder, - text::{TextBuilder, TextMessage}, - widget::WidgetBuilder, - window::{WindowBuilder, WindowTitle}, - BuildContext, Thickness, UiNode, UserInterface, - }, -}; use crate::{ absm::animation_container_ref, + fyrox::{ + asset::manager::ResourceManager, + core::{ + color::Color, + log::{Log, MessageKind}, + pool::ErasedHandle, + pool::Handle, + reflect::prelude::*, + }, + engine::SerializationContext, + graph::BaseSceneGraph, + gui::{ + button::ButtonMessage, + grid::{Column, GridBuilder, Row}, + inspector::{ + editors::PropertyEditorDefinitionContainer, InspectorBuilder, InspectorContext, + InspectorEnvironment, InspectorError, InspectorMessage, + }, + message::{MessageDirection, UiMessage}, + scroll_viewer::ScrollViewerBuilder, + text::{TextBuilder, TextMessage}, + widget::WidgetBuilder, + window::{WindowBuilder, WindowTitle}, + BuildContext, Thickness, UiNode, UserInterface, + }, + scene::SceneContainer, + }, gui::make_image_button_with_tooltip, inspector::editors::make_property_editors_container, load_image, @@ -78,7 +79,6 @@ pub struct Inspector { warning_text: Handle, type_name_text: Handle, docs_button: Handle, - old_selection: Selection, } #[macro_export] @@ -123,7 +123,7 @@ macro_rules! handle_property_changed { fn fetch_available_animations( selection: &Selection, controller: &dyn SceneController, - engine: &Engine, + scenes: &SceneContainer, ) -> Vec { if let Some(ui_scene) = controller.downcast_ref::() { // TODO: Remove duplicated code. @@ -145,7 +145,7 @@ fn fetch_available_animations( if let Some(game_scene) = controller.downcast_ref::() { if let Some(absm_selection) = selection.as_absm() { if let Some((_, animation_player)) = animation_container_ref( - &engine.scenes[game_scene.scene].graph, + &scenes[game_scene.scene].graph, absm_selection.absm_node_handle, ) { return animation_player @@ -161,6 +161,21 @@ fn fetch_available_animations( Default::default() } +fn print_errors(sync_errors: &[InspectorError]) { + for error in sync_errors { + Log::writeln( + MessageKind::Error, + format!("Failed to sync property. Reason: {:?}", error), + ) + } +} + +fn is_out_of_sync(sync_errors: &[InspectorError]) -> bool { + sync_errors + .iter() + .any(|err| matches!(err, &InspectorError::OutOfSync)) +} + impl Inspector { pub fn new(ctx: &mut BuildContext, sender: MessageSender) -> Self { let property_editors = Arc::new(make_property_editors_container(sender)); @@ -249,11 +264,14 @@ impl Inspector { warning_text, type_name_text, docs_button, - old_selection: Default::default(), } } - fn sync_to(&mut self, obj: &dyn Reflect, ui: &mut UserInterface) { + fn sync_to( + &mut self, + obj: &dyn Reflect, + ui: &mut UserInterface, + ) -> Result<(), Vec> { let ctx = ui .node(self.inspector) .cast::() @@ -261,14 +279,7 @@ impl Inspector { .context() .clone(); - if let Err(sync_errors) = ctx.sync(obj, ui, 0, true, Default::default()) { - for error in sync_errors { - Log::writeln( - MessageKind::Error, - format!("Failed to sync property. Reason: {:?}", error), - ) - } - } + ctx.sync(obj, ui, 0, true, Default::default()) } pub fn sync_to_model( @@ -278,46 +289,42 @@ impl Inspector { engine: &mut Engine, sender: &MessageSender, ) { - if &self.old_selection == editor_selection { - if !editor_selection.is_empty() { - controller.first_selected_entity(editor_selection, &engine.scenes, &mut |entity| { - self.sync_to(entity, engine.user_interfaces.first_mut()); - }); - } else if editor_selection.is_empty() { - self.clear(engine.user_interfaces.first()); - } - } else { - engine - .user_interfaces - .first_mut() - .send_message(WidgetMessage::visibility( - self.warning_text, - MessageDirection::ToWidget, - editor_selection.len() > 1, - )); + let mut need_clear = true; - // `first_selected_entity` is not guaranteed to call its callback. - // This flag allows us to determine whether an entity was found by first_selected_entity. - let mut found_entity = false; - let available_animations = - fetch_available_animations(editor_selection, controller, engine); - controller.first_selected_entity(editor_selection, &engine.scenes, &mut |entity| { - found_entity = true; - self.change_context( - entity, - engine.user_interfaces.first_mut(), - engine.resource_manager.clone(), - engine.serialization_context.clone(), - &available_animations, - sender, - ) - }); - // If `first_selected_entity` found no entity, then clear this inspector. - if !found_entity { - self.clear(engine.user_interfaces.first()); - } + let ui = engine.user_interfaces.first_mut(); - self.old_selection = editor_selection.clone(); + ui.send_message(WidgetMessage::visibility( + self.warning_text, + MessageDirection::ToWidget, + editor_selection.len() > 1, + )); + + controller.first_selected_entity(editor_selection, &engine.scenes, &mut |entity| { + if let Err(errors) = self.sync_to(entity, ui) { + if is_out_of_sync(&errors) { + let available_animations = + fetch_available_animations(editor_selection, controller, &engine.scenes); + + self.change_context( + entity, + ui, + engine.resource_manager.clone(), + engine.serialization_context.clone(), + &available_animations, + sender, + ); + + need_clear = false; + } else { + print_errors(&errors); + } + } else { + need_clear = false; + } + }); + + if need_clear { + self.clear(ui); } } diff --git a/fyrox-ui/src/inspector/mod.rs b/fyrox-ui/src/inspector/mod.rs index 765a506bd..3269ee63b 100644 --- a/fyrox-ui/src/inspector/mod.rs +++ b/fyrox-ui/src/inspector/mod.rs @@ -483,6 +483,7 @@ pub const HEADER_MARGIN: Thickness = Thickness { pub enum InspectorError { /// An error occurred due to reflection when some value did not have its expected type. CastError(CastError), + /// The object type has changed and the inspector context is no longer valid. OutOfSync, /// An error message produced by some editor with specialized details unique to that editor. /// For example, an array editor might complain if there is no editor definition for the type @@ -570,6 +571,8 @@ pub struct InspectorContext { /// editor widgets. This identifies sync messages and prevents the Inspector from trying to translate them, /// and thereby it prevents sync messages from potentially creating an infinite message loop. pub sync_flag: u64, + /// Type id of the object for which the context was created. + pub object_type_id: TypeId, } impl PartialEq for InspectorContext { @@ -589,6 +592,7 @@ impl Default for InspectorContext { ), environment: None, sync_flag: 0, + object_type_id: ().type_id(), } } } @@ -937,6 +941,7 @@ impl InspectorContext { property_definitions: definition_container, sync_flag, environment, + object_type_id: object.type_id(), } } @@ -959,6 +964,10 @@ impl InspectorContext { generate_property_string_values: bool, filter: PropertyFilter, ) -> Result<(), Vec> { + if object.type_id() != self.object_type_id { + return Err(vec![InspectorError::OutOfSync]); + } + let mut sync_errors = Vec::new(); object.fields_info(&mut |fields_info| { @@ -994,6 +1003,8 @@ impl InspectorContext { } Err(e) => sync_errors.push(e), } + } else { + sync_errors.push(InspectorError::OutOfSync); } } }