feat(editor): implement smart entity naming system for improved display names
This commit is contained in:
parent
9e889396db
commit
9993b202e0
@ -36,7 +36,7 @@
|
|||||||
|
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
|
|
||||||
use crate::remote::{client, RemoteClientPlugin, types::{EditorState, ComponentDisplayState, ComponentDataFetched, RemoteConnection, EntitiesFetched, ConnectionStatus, RemoteEntity}};
|
use crate::remote::{client, RemoteClientPlugin, types::{EditorState, ComponentDisplayState, ComponentDataFetched, RemoteConnection, EntitiesFetched, ConnectionStatus}};
|
||||||
use crate::panels::{EntityListPlugin, ComponentInspectorPlugin};
|
use crate::panels::{EntityListPlugin, ComponentInspectorPlugin};
|
||||||
use crate::widgets::{WidgetsPlugin, ScrollViewBuilder, ScrollContent};
|
use crate::widgets::{WidgetsPlugin, ScrollViewBuilder, ScrollContent};
|
||||||
|
|
||||||
@ -71,7 +71,6 @@ impl Plugin for EditorPlugin {
|
|||||||
.add_systems(Startup, setup_editor_ui)
|
.add_systems(Startup, setup_editor_ui)
|
||||||
.add_systems(Update, (
|
.add_systems(Update, (
|
||||||
setup_scroll_content_markers,
|
setup_scroll_content_markers,
|
||||||
refresh_entity_list,
|
|
||||||
handle_entity_selection,
|
handle_entity_selection,
|
||||||
update_entity_button_colors,
|
update_entity_button_colors,
|
||||||
handle_component_inspection,
|
handle_component_inspection,
|
||||||
@ -433,102 +432,7 @@ fn update_remote_connection(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Refresh the entity list display
|
/// Update status bar with connection info
|
||||||
fn refresh_entity_list(
|
|
||||||
editor_state: Res<EditorState>,
|
|
||||||
mut commands: Commands,
|
|
||||||
entity_list_area_query: Query<Entity, With<EntityListArea>>,
|
|
||||||
list_items_query: Query<Entity, With<EntityListItem>>,
|
|
||||||
mut local_entity_count: Local<usize>,
|
|
||||||
) {
|
|
||||||
// Only refresh when the actual entity count changes, not on every state change
|
|
||||||
let current_count = editor_state.entities.len();
|
|
||||||
if *local_entity_count == current_count {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
*local_entity_count = current_count;
|
|
||||||
|
|
||||||
// Clear existing list items
|
|
||||||
for entity in &list_items_query {
|
|
||||||
commands.entity(entity).despawn();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the entity list area and add new items
|
|
||||||
for list_area_entity in entity_list_area_query.iter() {
|
|
||||||
// Clear children by despawning them
|
|
||||||
commands.entity(list_area_entity).despawn_children();
|
|
||||||
|
|
||||||
commands.entity(list_area_entity).with_children(|parent| {
|
|
||||||
if editor_state.entities.is_empty() {
|
|
||||||
// Show empty state
|
|
||||||
parent.spawn((
|
|
||||||
Text::new("No entities connected.\nStart a bevy_remote server to see entities."),
|
|
||||||
TextFont {
|
|
||||||
font_size: 12.0,
|
|
||||||
..default()
|
|
||||||
},
|
|
||||||
TextColor(Color::srgb(0.6, 0.6, 0.6)),
|
|
||||||
Node {
|
|
||||||
padding: UiRect::all(Val::Px(16.0)),
|
|
||||||
..default()
|
|
||||||
},
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
// Add entity items
|
|
||||||
for remote_entity in &editor_state.entities {
|
|
||||||
create_entity_list_item(parent, remote_entity, &editor_state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_entity_list_item(parent: &mut ChildSpawnerCommands, remote_entity: &RemoteEntity, editor_state: &EditorState) {
|
|
||||||
// Determine the correct background color based on selection state
|
|
||||||
let bg_color = if Some(remote_entity.id) == editor_state.selected_entity_id {
|
|
||||||
Color::srgb(0.3, 0.4, 0.5) // Selected state
|
|
||||||
} else {
|
|
||||||
Color::srgb(0.2, 0.2, 0.2) // Default state
|
|
||||||
};
|
|
||||||
|
|
||||||
parent
|
|
||||||
.spawn((
|
|
||||||
Button,
|
|
||||||
Node {
|
|
||||||
width: Val::Percent(100.0),
|
|
||||||
height: Val::Px(32.0),
|
|
||||||
align_items: AlignItems::Center,
|
|
||||||
padding: UiRect::all(Val::Px(10.0)),
|
|
||||||
margin: UiRect::bottom(Val::Px(2.0)),
|
|
||||||
border: UiRect::all(Val::Px(1.0)),
|
|
||||||
..default()
|
|
||||||
},
|
|
||||||
BackgroundColor(bg_color),
|
|
||||||
BorderColor::all(Color::srgb(0.3, 0.3, 0.3)),
|
|
||||||
EntityListItem::from_remote_entity(&remote_entity),
|
|
||||||
))
|
|
||||||
.with_children(|parent| {
|
|
||||||
// Entity icon and name
|
|
||||||
parent.spawn((
|
|
||||||
Node {
|
|
||||||
flex_direction: FlexDirection::Row,
|
|
||||||
align_items: AlignItems::Center,
|
|
||||||
..default()
|
|
||||||
},
|
|
||||||
)).with_children(|parent| {
|
|
||||||
parent.spawn((
|
|
||||||
Text::new(format!("Entity {}", remote_entity.id)),
|
|
||||||
TextFont {
|
|
||||||
font_size: 13.0,
|
|
||||||
..default()
|
|
||||||
},
|
|
||||||
TextColor(Color::srgb(0.9, 0.9, 0.9)),
|
|
||||||
));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create status bar with connection info
|
|
||||||
fn update_status_bar(
|
fn update_status_bar(
|
||||||
editor_state: Res<EditorState>,
|
editor_state: Res<EditorState>,
|
||||||
remote_conn: Res<RemoteConnection>,
|
remote_conn: Res<RemoteConnection>,
|
||||||
|
314
crates/bevy_editor/src/remote/entity_naming.rs
Normal file
314
crates/bevy_editor/src/remote/entity_naming.rs
Normal file
@ -0,0 +1,314 @@
|
|||||||
|
//! Smart entity naming system for the editor
|
||||||
|
//!
|
||||||
|
//! This module provides intelligent naming for entities based on their components.
|
||||||
|
//! It follows a precedence system:
|
||||||
|
//! 1. Name component (highest priority)
|
||||||
|
//! 2. Common Bevy components (Camera, Window, etc.)
|
||||||
|
//! 3. User components
|
||||||
|
//! 4. Entity ID fallback
|
||||||
|
|
||||||
|
use super::types::RemoteEntity;
|
||||||
|
use serde_json::Value;
|
||||||
|
|
||||||
|
/// Component precedence levels for naming
|
||||||
|
#[derive(Debug, PartialEq, PartialOrd, Clone)]
|
||||||
|
pub enum ComponentPrecedence {
|
||||||
|
/// Built-in Name component - highest priority
|
||||||
|
Name = 0,
|
||||||
|
/// User/custom components - very high priority for meaningful names
|
||||||
|
UserComponent = 1,
|
||||||
|
/// Primary Bevy components (Camera, Window, etc.)
|
||||||
|
PrimaryBevy = 2,
|
||||||
|
/// Secondary Bevy components (Transform, Visibility, etc.)
|
||||||
|
SecondaryBevy = 3,
|
||||||
|
/// Entity ID fallback - lowest priority
|
||||||
|
EntityId = 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A component that can provide a name for an entity
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct NamingComponent {
|
||||||
|
pub name: String,
|
||||||
|
pub precedence: ComponentPrecedence,
|
||||||
|
pub display_name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generate a smart display name for an entity based on its components
|
||||||
|
pub fn generate_entity_display_name(entity: &RemoteEntity, component_data: Option<&Value>) -> String {
|
||||||
|
let mut best_naming: Option<NamingComponent> = None;
|
||||||
|
|
||||||
|
// First check if we have component data with actual Name values
|
||||||
|
if let Some(data) = component_data {
|
||||||
|
if let Some(name_value) = extract_name_from_component_data(data) {
|
||||||
|
return format!("#{} ({})", entity.id, name_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Analyze available components to find the best naming option
|
||||||
|
for component_name in &entity.components {
|
||||||
|
if let Some(naming) = analyze_component_for_naming(component_name) {
|
||||||
|
if best_naming.is_none() || naming.precedence < best_naming.as_ref().unwrap().precedence {
|
||||||
|
best_naming = Some(naming);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also check full component names for more accurate detection
|
||||||
|
for full_component_name in &entity.full_component_names {
|
||||||
|
if let Some(naming) = analyze_component_for_naming(full_component_name) {
|
||||||
|
if best_naming.is_none() || naming.precedence < best_naming.as_ref().unwrap().precedence {
|
||||||
|
best_naming = Some(naming);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match best_naming {
|
||||||
|
Some(naming) if naming.precedence != ComponentPrecedence::EntityId => {
|
||||||
|
format!("#{} ({})", entity.id, naming.display_name)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// Fallback to entity ID
|
||||||
|
format!("Entity {}", entity.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extract the actual name value from component data JSON
|
||||||
|
fn extract_name_from_component_data(component_data: &Value) -> Option<String> {
|
||||||
|
if let Some(components) = component_data.get("components").and_then(|v| v.as_object()) {
|
||||||
|
// Look for Name component in various forms
|
||||||
|
for (component_type, component_value) in components {
|
||||||
|
if component_type.contains("Name") || component_type.ends_with("::Name") {
|
||||||
|
// Try to extract the actual name value
|
||||||
|
if let Some(name_str) = component_value.as_str() {
|
||||||
|
return Some(name_str.to_string());
|
||||||
|
}
|
||||||
|
// Handle nested name values
|
||||||
|
if let Some(obj) = component_value.as_object() {
|
||||||
|
if let Some(name_val) = obj.get("name").or_else(|| obj.get("value")).or_else(|| obj.get("0")) {
|
||||||
|
if let Some(name_str) = name_val.as_str() {
|
||||||
|
return Some(name_str.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Analyze a component name to determine if it can provide entity naming
|
||||||
|
fn analyze_component_for_naming(component_name: &str) -> Option<NamingComponent> {
|
||||||
|
// Remove module paths for analysis
|
||||||
|
let clean_name = component_name
|
||||||
|
.split("::")
|
||||||
|
.last()
|
||||||
|
.unwrap_or(component_name);
|
||||||
|
|
||||||
|
// Check for Name component (highest priority)
|
||||||
|
if clean_name == "Name" || component_name.ends_with("::Name") {
|
||||||
|
return Some(NamingComponent {
|
||||||
|
name: component_name.to_string(),
|
||||||
|
precedence: ComponentPrecedence::Name,
|
||||||
|
display_name: "Named Entity".to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Primary Bevy components that are very distinctive
|
||||||
|
match clean_name {
|
||||||
|
"Camera" | "Camera2d" | "Camera3d" => Some(NamingComponent {
|
||||||
|
name: component_name.to_string(),
|
||||||
|
precedence: ComponentPrecedence::PrimaryBevy,
|
||||||
|
display_name: "Camera".to_string(),
|
||||||
|
}),
|
||||||
|
"Window" => Some(NamingComponent {
|
||||||
|
name: component_name.to_string(),
|
||||||
|
precedence: ComponentPrecedence::PrimaryBevy,
|
||||||
|
display_name: "Window".to_string(),
|
||||||
|
}),
|
||||||
|
"DirectionalLight" => Some(NamingComponent {
|
||||||
|
name: component_name.to_string(),
|
||||||
|
precedence: ComponentPrecedence::PrimaryBevy,
|
||||||
|
display_name: "Directional Light".to_string(),
|
||||||
|
}),
|
||||||
|
"PointLight" => Some(NamingComponent {
|
||||||
|
name: component_name.to_string(),
|
||||||
|
precedence: ComponentPrecedence::PrimaryBevy,
|
||||||
|
display_name: "Point Light".to_string(),
|
||||||
|
}),
|
||||||
|
"SpotLight" => Some(NamingComponent {
|
||||||
|
name: component_name.to_string(),
|
||||||
|
precedence: ComponentPrecedence::PrimaryBevy,
|
||||||
|
display_name: "Spot Light".to_string(),
|
||||||
|
}),
|
||||||
|
"AudioListener" => Some(NamingComponent {
|
||||||
|
name: component_name.to_string(),
|
||||||
|
precedence: ComponentPrecedence::PrimaryBevy,
|
||||||
|
display_name: "Audio Listener".to_string(),
|
||||||
|
}),
|
||||||
|
"AudioSource" => Some(NamingComponent {
|
||||||
|
name: component_name.to_string(),
|
||||||
|
precedence: ComponentPrecedence::PrimaryBevy,
|
||||||
|
display_name: "Audio Source".to_string(),
|
||||||
|
}),
|
||||||
|
|
||||||
|
// Secondary Bevy components (lower priority)
|
||||||
|
"Mesh3d" | "Mesh2d" => Some(NamingComponent {
|
||||||
|
name: component_name.to_string(),
|
||||||
|
precedence: ComponentPrecedence::SecondaryBevy,
|
||||||
|
display_name: "Mesh".to_string(),
|
||||||
|
}),
|
||||||
|
"MeshMaterial3d" | "MeshMaterial2d" => Some(NamingComponent {
|
||||||
|
name: component_name.to_string(),
|
||||||
|
precedence: ComponentPrecedence::SecondaryBevy,
|
||||||
|
display_name: "Material".to_string(),
|
||||||
|
}),
|
||||||
|
"Text" | "Text2d" => Some(NamingComponent {
|
||||||
|
name: component_name.to_string(),
|
||||||
|
precedence: ComponentPrecedence::SecondaryBevy,
|
||||||
|
display_name: "Text".to_string(),
|
||||||
|
}),
|
||||||
|
"Sprite" => Some(NamingComponent {
|
||||||
|
name: component_name.to_string(),
|
||||||
|
precedence: ComponentPrecedence::SecondaryBevy,
|
||||||
|
display_name: "Sprite".to_string(),
|
||||||
|
}),
|
||||||
|
"ImageNode" => Some(NamingComponent {
|
||||||
|
name: component_name.to_string(),
|
||||||
|
precedence: ComponentPrecedence::SecondaryBevy,
|
||||||
|
display_name: "Image".to_string(),
|
||||||
|
}),
|
||||||
|
"Button" => Some(NamingComponent {
|
||||||
|
name: component_name.to_string(),
|
||||||
|
precedence: ComponentPrecedence::SecondaryBevy,
|
||||||
|
display_name: "Button".to_string(),
|
||||||
|
}),
|
||||||
|
"Node" => Some(NamingComponent {
|
||||||
|
name: component_name.to_string(),
|
||||||
|
precedence: ComponentPrecedence::SecondaryBevy,
|
||||||
|
display_name: "UI Node".to_string(),
|
||||||
|
}),
|
||||||
|
|
||||||
|
_ => {
|
||||||
|
// Check if it's likely a user component (not starting with common Bevy prefixes)
|
||||||
|
if !is_bevy_builtin_component(component_name) {
|
||||||
|
Some(NamingComponent {
|
||||||
|
name: component_name.to_string(),
|
||||||
|
precedence: ComponentPrecedence::UserComponent,
|
||||||
|
display_name: clean_name.to_string(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if a component is likely a Bevy built-in component
|
||||||
|
fn is_bevy_builtin_component(component_name: &str) -> bool {
|
||||||
|
let bevy_prefixes = [
|
||||||
|
"bevy_",
|
||||||
|
"std::",
|
||||||
|
"core::",
|
||||||
|
"alloc::",
|
||||||
|
"winit::",
|
||||||
|
];
|
||||||
|
|
||||||
|
let has_bevy_prefix = bevy_prefixes.iter().any(|prefix| component_name.starts_with(prefix));
|
||||||
|
let is_common_bevy = matches!(component_name.split("::").last().unwrap_or(""),
|
||||||
|
"Transform" | "GlobalTransform" | "Visibility" | "InheritedVisibility" |
|
||||||
|
"ViewVisibility" | "ComputedVisibility" | "Parent" | "Children" | "Aabb" |
|
||||||
|
"TransformTreeChanged" | "Frustum" | "Projection" | "VisibleEntities" |
|
||||||
|
"DebandDither" | "Tonemapping" | "ClusterConfig" | "RenderEntity" |
|
||||||
|
"SyncToRenderWorld" | "Msaa" | "CubemapFrusta" | "CubemapVisibleEntities"
|
||||||
|
);
|
||||||
|
|
||||||
|
has_bevy_prefix || is_common_bevy
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_entity_naming_precedence() {
|
||||||
|
let mut entity = RemoteEntity {
|
||||||
|
id: 123,
|
||||||
|
components: vec!["Camera".to_string(), "Transform".to_string()],
|
||||||
|
full_component_names: vec!["bevy_render::camera::Camera".to_string(), "bevy_transform::components::Transform".to_string()],
|
||||||
|
};
|
||||||
|
|
||||||
|
let display_name = generate_entity_display_name(&entity, None);
|
||||||
|
assert_eq!(display_name, "#123 (Camera)");
|
||||||
|
|
||||||
|
// Test with user component (should take precedence over Camera)
|
||||||
|
entity.components.push("Player".to_string());
|
||||||
|
entity.full_component_names.push("my_game::Player".to_string());
|
||||||
|
|
||||||
|
let display_name = generate_entity_display_name(&entity, None);
|
||||||
|
assert_eq!(display_name, "#123 (Player)");
|
||||||
|
|
||||||
|
// Test with Name component (should take precedence over everything)
|
||||||
|
entity.components.push("Name".to_string());
|
||||||
|
entity.full_component_names.push("bevy_core::name::Name".to_string());
|
||||||
|
|
||||||
|
let display_name = generate_entity_display_name(&entity, None);
|
||||||
|
assert_eq!(display_name, "#123 (Named Entity)");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_user_component_precedence_over_bevy() {
|
||||||
|
let entity = RemoteEntity {
|
||||||
|
id: 456,
|
||||||
|
components: vec!["Cube".to_string(), "Mesh3d".to_string(), "Transform".to_string()],
|
||||||
|
full_component_names: vec![
|
||||||
|
"my_game::Cube".to_string(),
|
||||||
|
"bevy_mesh::mesh::Mesh3d".to_string(),
|
||||||
|
"bevy_transform::components::Transform".to_string()
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
let display_name = generate_entity_display_name(&entity, None);
|
||||||
|
// User component "Cube" should take precedence over "Mesh3d"
|
||||||
|
assert_eq!(display_name, "#456 (Cube)");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_fallback_to_entity_id() {
|
||||||
|
let entity = RemoteEntity {
|
||||||
|
id: 789,
|
||||||
|
components: vec!["Transform".to_string(), "Visibility".to_string()],
|
||||||
|
full_component_names: vec!["bevy_transform::components::Transform".to_string(), "bevy_render::view::visibility::Visibility".to_string()],
|
||||||
|
};
|
||||||
|
|
||||||
|
let display_name = generate_entity_display_name(&entity, None);
|
||||||
|
assert_eq!(display_name, "Entity 789");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_cube_example_from_server() {
|
||||||
|
// Simulate the cube entity from server.rs example
|
||||||
|
let entity = RemoteEntity {
|
||||||
|
id: 42,
|
||||||
|
components: vec![
|
||||||
|
"Aabb".to_string(),
|
||||||
|
"Cube".to_string(),
|
||||||
|
"Mesh3d".to_string(),
|
||||||
|
"MeshMaterial3d".to_string(),
|
||||||
|
"Transform".to_string(),
|
||||||
|
],
|
||||||
|
full_component_names: vec![
|
||||||
|
"bevy_camera::primitives::Aabb".to_string(),
|
||||||
|
"server::Cube".to_string(),
|
||||||
|
"bevy_mesh::mesh::Mesh3d".to_string(),
|
||||||
|
"bevy_pbr::material::MeshMaterial3d".to_string(),
|
||||||
|
"bevy_transform::components::Transform".to_string(),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
let display_name = generate_entity_display_name(&entity, None);
|
||||||
|
// User component "Cube" should take precedence over all Bevy components including Aabb
|
||||||
|
assert_eq!(display_name, "#42 (Cube)");
|
||||||
|
println!("Cube entity correctly named: {}", display_name);
|
||||||
|
}
|
||||||
|
}
|
@ -34,11 +34,13 @@
|
|||||||
pub mod types;
|
pub mod types;
|
||||||
pub mod client;
|
pub mod client;
|
||||||
pub mod connection;
|
pub mod connection;
|
||||||
|
pub mod entity_naming;
|
||||||
|
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
pub use types::*;
|
pub use types::*;
|
||||||
pub use client::*;
|
pub use client::*;
|
||||||
pub use connection::*;
|
pub use connection::*;
|
||||||
|
pub use entity_naming::*;
|
||||||
|
|
||||||
/// Plugin that handles remote connection functionality
|
/// Plugin that handles remote connection functionality
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -281,9 +281,27 @@ pub struct EntityListItem {
|
|||||||
|
|
||||||
impl EntityListItem {
|
impl EntityListItem {
|
||||||
pub fn from_remote_entity(remote_entity: &crate::remote::types::RemoteEntity) -> Self {
|
pub fn from_remote_entity(remote_entity: &crate::remote::types::RemoteEntity) -> Self {
|
||||||
|
// Use the smart naming system to generate a meaningful display name
|
||||||
|
let display_name = crate::remote::entity_naming::generate_entity_display_name(remote_entity, None);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
entity_id: remote_entity.id,
|
entity_id: remote_entity.id,
|
||||||
name: format!("Entity {}", remote_entity.id),
|
name: display_name,
|
||||||
|
components: remote_entity.components.clone(),
|
||||||
|
children_count: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create EntityListItem with component data for better Name extraction
|
||||||
|
pub fn from_remote_entity_with_data(
|
||||||
|
remote_entity: &crate::remote::types::RemoteEntity,
|
||||||
|
component_data: Option<&serde_json::Value>
|
||||||
|
) -> Self {
|
||||||
|
let display_name = crate::remote::entity_naming::generate_entity_display_name(remote_entity, component_data);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
entity_id: remote_entity.id,
|
||||||
|
name: display_name,
|
||||||
components: remote_entity.components.clone(),
|
components: remote_entity.components.clone(),
|
||||||
children_count: 0,
|
children_count: 0,
|
||||||
}
|
}
|
||||||
@ -292,7 +310,8 @@ impl EntityListItem {
|
|||||||
|
|
||||||
impl ListDisplayable for EntityListItem {
|
impl ListDisplayable for EntityListItem {
|
||||||
fn display_text(&self) -> String {
|
fn display_text(&self) -> String {
|
||||||
format!("Entity {} ({})", self.entity_id, self.name)
|
// The name already contains the smart formatting (e.g., "#123 (Camera)")
|
||||||
|
self.name.clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user