fixed inspector syncing when replacing selected object with other type

- fixes #660
This commit is contained in:
Dmitry Stepanov
2024-06-27 18:13:05 +03:00
parent 11dbad4953
commit 36e5851ca3
2 changed files with 95 additions and 77 deletions
+84 -77
View File
@@ -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<UiNode>,
type_name_text: Handle<UiNode>,
docs_button: Handle<UiNode>,
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<AnimationDefinition> {
if let Some(ui_scene) = controller.downcast_ref::<UiScene>() {
// TODO: Remove duplicated code.
@@ -145,7 +145,7 @@ fn fetch_available_animations(
if let Some(game_scene) = controller.downcast_ref::<GameScene>() {
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<InspectorError>> {
let ctx = ui
.node(self.inspector)
.cast::<fyrox::gui::inspector::Inspector>()
@@ -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);
}
}
+11
View File
@@ -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<InspectorError>> {
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);
}
}
}