diff --git a/FinalEngine.ECS.Components/Core/TagComponent.cs b/FinalEngine.ECS.Components/Core/TagComponent.cs index b38f48df..68a23dfb 100644 --- a/FinalEngine.ECS.Components/Core/TagComponent.cs +++ b/FinalEngine.ECS.Components/Core/TagComponent.cs @@ -1,5 +1,5 @@ // -// Copyright (c) Software Antics. All rights reserved. +// Copyright (c) Software Antics. All rights reserved. // namespace FinalEngine.ECS.Components.Core; @@ -7,26 +7,27 @@ namespace FinalEngine.ECS.Components.Core; using System.ComponentModel; /// -/// Provides a component that represents a name or tag for an . +/// Provides a component that represents a name or tag for an . /// -/// +/// +[Category("Core")] public sealed class TagComponent : IEntityComponent, INotifyPropertyChanged { /// - /// The tag. + /// The tag. /// private string? tag; /// - /// Occurs when a property value changes. + /// Occurs when a property value changes. /// public event PropertyChangedEventHandler? PropertyChanged; /// - /// Gets or sets the tag. + /// Gets or sets the tag. /// /// - /// The tag. + /// The tag. /// public string? Tag { diff --git a/FinalEngine.ECS.Components/Core/TransformComponent.cs b/FinalEngine.ECS.Components/Core/TransformComponent.cs index b85eb0bd..db2dcf33 100644 --- a/FinalEngine.ECS.Components/Core/TransformComponent.cs +++ b/FinalEngine.ECS.Components/Core/TransformComponent.cs @@ -4,16 +4,18 @@ namespace FinalEngine.ECS.Components.Core; +using System.ComponentModel; using System.Numerics; /// -/// Provides a component that represents the translation, rotation and scale of an entity. +/// Provides a component that represents the translation, rotation and scale of an entity. /// -/// +/// +[Category("Core")] public sealed class TransformComponent : IEntityComponent { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// public TransformComponent() { @@ -23,10 +25,10 @@ public TransformComponent() } /// - /// Gets a normalized vector representing the negative Z-axis of the transform in world space. + /// Gets a normalized vector representing the negative Z-axis of the transform in world space. /// /// - /// The normalized vector representing the Z-axis of the transform in world space.. + /// The normalized vector representing the Z-axis of the transform in world space.. /// public Vector3 Backward { @@ -34,10 +36,10 @@ public Vector3 Backward } /// - /// Gets a normalized vector representing the negative Y-axis of the transform in world space. + /// Gets a normalized vector representing the negative Y-axis of the transform in world space. /// /// - /// The normalized vector representing the negative Y-axis of the transform in world space. + /// The normalized vector representing the negative Y-axis of the transform in world space. /// public Vector3 Down { @@ -45,10 +47,10 @@ public Vector3 Down } /// - /// Gets a normalized vector representing the Z-axis of the transform in world space. + /// Gets a normalized vector representing the Z-axis of the transform in world space. /// /// - /// The normalized vector representing the Z-axis of the transform in world space.. + /// The normalized vector representing the Z-axis of the transform in world space.. /// public Vector3 Forward { @@ -56,10 +58,10 @@ public Vector3 Forward } /// - /// Gets a normalized vector representing the negative X-axis of the transform in world space. + /// Gets a normalized vector representing the negative X-axis of the transform in world space. /// /// - /// The normalized vector representing the negative X-axis of the transform in world space. + /// The normalized vector representing the negative X-axis of the transform in world space. /// public Vector3 Left { @@ -67,18 +69,18 @@ public Vector3 Left } /// - /// Gets or sets the position of the transform. + /// Gets or sets the position of the transform. /// /// - /// The position of the transform. + /// The position of the transform. /// public Vector3 Position { get; set; } /// - /// Gets a normalized vector representing the X-axis of the transform in world space. + /// Gets a normalized vector representing the X-axis of the transform in world space. /// /// - /// The normalized vector representing the negative X-axis of the transform in world space. + /// The normalized vector representing the negative X-axis of the transform in world space. /// public Vector3 Right { @@ -86,26 +88,26 @@ public Vector3 Right } /// - /// Gets or sets the rotation of this transform. + /// Gets or sets the rotation of this transform. /// /// - /// The rotation of this transform. + /// The rotation of this transform. /// public Quaternion Rotation { get; set; } /// - /// Gets or sets the scale. + /// Gets or sets the scale. /// /// - /// The scale. + /// The scale. /// public Vector3 Scale { get; set; } /// - /// Gets a normalized vector representing the Y-axis of the transform in world space. + /// Gets a normalized vector representing the Y-axis of the transform in world space. /// /// - /// The normalized vector representing the negative Y-axis of the transform in world space. + /// The normalized vector representing the negative Y-axis of the transform in world space. /// public Vector3 Up { @@ -113,10 +115,10 @@ public Vector3 Up } /// - /// Creates the transformation matrix for this transform. + /// Creates the transformation matrix for this transform. /// /// - /// The newly create that represents the transformation matrix for this transform. + /// The newly create that represents the transformation matrix for this transform. /// public Matrix4x4 CreateTransformationMatrix() { @@ -126,13 +128,13 @@ public Matrix4x4 CreateTransformationMatrix() } /// - /// Creates the view matrix for this transform. + /// Creates the view matrix for this transform. /// /// - /// The camera up vector. + /// The camera up vector. /// /// - /// The newly created that represents the view matrix for this transform. + /// The newly created that represents the view matrix for this transform. /// public Matrix4x4 CreateViewMatrix(Vector3 cameraUp) { @@ -140,13 +142,13 @@ public Matrix4x4 CreateViewMatrix(Vector3 cameraUp) } /// - /// Rotates this transform on the specified by the specified . + /// Rotates this transform on the specified by the specified . /// /// - /// The axis to rotate. + /// The axis to rotate. /// /// - /// The radians that represents the amount to rotate by. + /// The radians that represents the amount to rotate by. /// public void Rotate(Vector3 axis, float radians) { @@ -155,13 +157,13 @@ public void Rotate(Vector3 axis, float radians) } /// - /// Translates this transform in the specified by the specified . + /// Translates this transform in the specified by the specified . /// /// - /// The direction to translate. + /// The direction to translate. /// /// - /// The amount to translate. + /// The amount to translate. /// public void Translate(Vector3 direction, float amount) { diff --git a/FinalEngine.ECS.Components/FinalEngine.ECS.Components.csproj b/FinalEngine.ECS.Components/FinalEngine.ECS.Components.csproj index aea1b33e..5bc917a1 100644 --- a/FinalEngine.ECS.Components/FinalEngine.ECS.Components.csproj +++ b/FinalEngine.ECS.Components/FinalEngine.ECS.Components.csproj @@ -8,7 +8,6 @@ false true x64 - true diff --git a/FinalEngine.Editor.Common/FinalEngine.Editor.Common.csproj b/FinalEngine.Editor.Common/FinalEngine.Editor.Common.csproj index 40cbd522..4b6a8f87 100644 --- a/FinalEngine.Editor.Common/FinalEngine.Editor.Common.csproj +++ b/FinalEngine.Editor.Common/FinalEngine.Editor.Common.csproj @@ -8,7 +8,6 @@ false true x64 - true diff --git a/FinalEngine.Editor.Common/Models/Scenes/Scene.cs b/FinalEngine.Editor.Common/Models/Scenes/Scene.cs index 632e7b25..982cca59 100644 --- a/FinalEngine.Editor.Common/Models/Scenes/Scene.cs +++ b/FinalEngine.Editor.Common/Models/Scenes/Scene.cs @@ -1,5 +1,5 @@ // -// Copyright (c) Software Antics. All rights reserved. +// Copyright (c) Software Antics. All rights reserved. // namespace FinalEngine.Editor.Common.Models.Scenes; @@ -14,36 +14,36 @@ namespace FinalEngine.Editor.Common.Models.Scenes; using Microsoft.Extensions.Logging; /// -/// Represents a scene that contains a collection of entities and systems. +/// Represents a scene that contains a collection of entities and systems. /// public sealed class Scene : IScene { /// - /// The entities contained within the scene. + /// The entities contained within the scene. /// private readonly ObservableCollection entities; /// - /// The logger. + /// The logger. /// private readonly ILogger logger; /// - /// The underlying entity world that contains all the scenes entities and systems. + /// The underlying entity world that contains all the scenes entities and systems. /// private readonly IEntityWorld world; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// - /// The logger. + /// The logger. /// /// - /// The entity world to be associated with this scene. + /// The entity world to be associated with this scene. /// /// - /// The specified or parameter cannot be null. + /// The specified or parameter cannot be null. /// public Scene(ILogger logger, IEntityWorld world) { @@ -60,7 +60,7 @@ public IReadOnlyCollection Entities /// /// - /// The specified parameter cannot be null or whitespace. + /// The specified parameter cannot be null or whitespace. /// public void AddEntity(string tag, Guid uniqueID) { @@ -88,7 +88,7 @@ public void AddEntity(string tag, Guid uniqueID) /// /// - /// Failed to locate an that matches the specified . + /// Failed to locate an that matches the specified . /// public void RemoveEntity(Guid uniqueIdentifier) { diff --git a/FinalEngine.Editor.Desktop/App.xaml.cs b/FinalEngine.Editor.Desktop/App.xaml.cs index 47a7ea24..6f6e446e 100644 --- a/FinalEngine.Editor.Desktop/App.xaml.cs +++ b/FinalEngine.Editor.Desktop/App.xaml.cs @@ -1,5 +1,5 @@ // -// Copyright (c) Software Antics. All rights reserved. +// Copyright (c) Software Antics. All rights reserved. // namespace FinalEngine.Editor.Desktop; @@ -28,6 +28,7 @@ namespace FinalEngine.Editor.Desktop; using FinalEngine.Editor.ViewModels.Scenes; using FinalEngine.Editor.ViewModels.Services; using FinalEngine.Editor.ViewModels.Services.Actions; +using FinalEngine.Editor.ViewModels.Services.Entities; using FinalEngine.Editor.ViewModels.Services.Interactions; using FinalEngine.Editor.ViewModels.Services.Layout; using FinalEngine.Rendering; @@ -37,12 +38,12 @@ namespace FinalEngine.Editor.Desktop; using Microsoft.Extensions.Logging; /// -/// Interaction logic for App.xaml. +/// Interaction logic for App.xaml. /// public partial class App : Application { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// public App() { @@ -52,18 +53,18 @@ public App() } /// - /// Gets or sets the application host. + /// Gets or sets the application host. /// /// - /// The application host. + /// The application host. /// private static IHost? AppHost { get; set; } /// - /// Exits the main application, disposing of any existing resources. + /// Exits the main application, disposing of any existing resources. /// /// - /// The instance containing the event data. + /// The instance containing the event data. /// protected override async void OnExit(ExitEventArgs e) { @@ -72,7 +73,7 @@ protected override async void OnExit(ExitEventArgs e) } /// - /// Starts up the main application on the current platform. + /// Starts up the main application on the current platform. /// /// /// A that contains the event data. @@ -94,13 +95,13 @@ protected override async void OnStartup(StartupEventArgs e) } /// - /// Configures the services to be consumed by the application. + /// Configures the services to be consumed by the application. /// /// - /// The application host builder context. + /// The application host builder context. /// /// - /// The services to be consumed by the application. + /// The services to be consumed by the application. /// private static void ConfigureServices(HostBuilderContext context, IServiceCollection services) { @@ -141,6 +142,8 @@ private static void ConfigureServices(HostBuilderContext context, IServiceCollec services.AddTransient, ManageWindowLayoutsView>(); services.AddTransient, CreateEntityView>(); + services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); diff --git a/FinalEngine.Editor.Desktop/Views/Inspectors/EntityInspectorView.xaml b/FinalEngine.Editor.Desktop/Views/Inspectors/EntityInspectorView.xaml index ffa15617..91d35aea 100644 --- a/FinalEngine.Editor.Desktop/Views/Inspectors/EntityInspectorView.xaml +++ b/FinalEngine.Editor.Desktop/Views/Inspectors/EntityInspectorView.xaml @@ -8,8 +8,31 @@ d:DataContext="{d:DesignInstance Type=vm:EntityInspectorViewModel}" mc:Ignorable="d"> - - + + + @@ -28,5 +51,5 @@ - + diff --git a/FinalEngine.Editor.ViewModels/Inspectors/EntityComponentCategoryViewModel.cs b/FinalEngine.Editor.ViewModels/Inspectors/EntityComponentCategoryViewModel.cs new file mode 100644 index 00000000..d17ff0c5 --- /dev/null +++ b/FinalEngine.Editor.ViewModels/Inspectors/EntityComponentCategoryViewModel.cs @@ -0,0 +1,27 @@ +// +// Copyright (c) Software Antics. All rights reserved. +// + +namespace FinalEngine.Editor.ViewModels.Inspectors; + +using System; +using System.Collections.Generic; +using CommunityToolkit.Mvvm.ComponentModel; + +public sealed class EntityComponentCategoryViewModel : ObservableObject, IEntityComponentCategoryViewModel +{ + public EntityComponentCategoryViewModel(string name, IEnumerable componentTypes) + { + if (string.IsNullOrWhiteSpace(name)) + { + throw new ArgumentException($"'{nameof(name)}' cannot be null or whitespace.", nameof(name)); + } + + this.Name = name; + this.ComponentTypes = componentTypes ?? throw new ArgumentNullException(nameof(componentTypes)); + } + + public IEnumerable ComponentTypes { get; } + + public string Name { get; } +} diff --git a/FinalEngine.Editor.ViewModels/Inspectors/EntityComponentTypeViewModel.cs b/FinalEngine.Editor.ViewModels/Inspectors/EntityComponentTypeViewModel.cs new file mode 100644 index 00000000..ed32bf52 --- /dev/null +++ b/FinalEngine.Editor.ViewModels/Inspectors/EntityComponentTypeViewModel.cs @@ -0,0 +1,73 @@ +// +// Copyright (c) Software Antics. All rights reserved. +// + +namespace FinalEngine.Editor.ViewModels.Inspectors; + +using System; +using System.Windows.Input; +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using CommunityToolkit.Mvvm.Messaging; +using FinalEngine.ECS; +using FinalEngine.Editor.ViewModels.Messages.Entities; + +public sealed class EntityComponentTypeViewModel : ObservableObject, IEntityComponentTypeViewModel +{ + private readonly Entity entity; + + private readonly IMessenger messenger; + + private readonly Type type; + + private ICommand? addCommand; + + public EntityComponentTypeViewModel(IMessenger messenger, Entity entity, Type type) + { + this.messenger = messenger ?? throw new ArgumentNullException(nameof(messenger)); + this.entity = entity ?? throw new ArgumentNullException(nameof(entity)); + + if (type == null) + { + throw new ArgumentNullException(nameof(type)); + } + + if (!typeof(IEntityComponent).IsAssignableFrom(type)) + { + throw new ArgumentException($"The specified {nameof(type)} parameter does not implement {nameof(IEntityComponent)}."); + } + + this.type = type; + this.Name = this.type.Name; + } + + public ICommand AddCommand + { + get { return this.addCommand ??= new RelayCommand(this.AddComponent, this.CanAddComponent); } + } + + public string Name { get; } + + private void AddComponent() + { + object? instance = Activator.CreateInstance(this.type) ?? throw new InvalidOperationException($"The entity component couldn't be instantiated for the specified type: '{this.type}'. Please ensure the component contains a default empty constructor."); + + if (instance is not IEntityComponent component) + { + throw new InvalidCastException($"The created instance of type '{instance.GetType()}' does not implement the {nameof(IEntityComponent)} interface."); + } + + if (this.entity.ContainsComponent(this.type)) + { + return; + } + + this.entity.AddComponent(component); + this.messenger.Send(new EntityModifiedMessage(this.entity)); + } + + private bool CanAddComponent() + { + return !this.entity.ContainsComponent(this.type); + } +} diff --git a/FinalEngine.Editor.ViewModels/Inspectors/EntityInspectorViewModel.cs b/FinalEngine.Editor.ViewModels/Inspectors/EntityInspectorViewModel.cs index 65b88fb9..4b1ff215 100644 --- a/FinalEngine.Editor.ViewModels/Inspectors/EntityInspectorViewModel.cs +++ b/FinalEngine.Editor.ViewModels/Inspectors/EntityInspectorViewModel.cs @@ -7,10 +7,14 @@ namespace FinalEngine.Editor.ViewModels.Inspectors; using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Linq; +using System.Reflection; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Messaging; using FinalEngine.ECS; +using FinalEngine.ECS.Components.Core; using FinalEngine.Editor.ViewModels.Messages.Entities; +using FinalEngine.Editor.ViewModels.Services.Entities; /// /// Provides a standard implementation of an . @@ -19,6 +23,8 @@ namespace FinalEngine.Editor.ViewModels.Inspectors; /// public sealed class EntityInspectorViewModel : ObservableObject, IEntityInspectorViewModel { + private readonly ObservableCollection categorizedComponentTypes; + /// /// The component view models. /// @@ -34,6 +40,8 @@ public sealed class EntityInspectorViewModel : ObservableObject, IEntityInspecto /// private readonly IMessenger messenger; + private readonly IEntityComponentTypeResolver typeResolver; + /// /// Initializes a new instance of the class. /// @@ -46,15 +54,25 @@ public sealed class EntityInspectorViewModel : ObservableObject, IEntityInspecto /// /// The specified parameter cannot be null. /// - public EntityInspectorViewModel(IMessenger messenger, Entity entity) + public EntityInspectorViewModel(IMessenger messenger, IEntityComponentTypeResolver typeResolver, Entity entity) { this.messenger = messenger ?? throw new ArgumentNullException(nameof(messenger)); + this.typeResolver = typeResolver ?? throw new ArgumentNullException(nameof(typeResolver)); this.entity = entity ?? throw new ArgumentNullException(nameof(entity)); + this.componentViewModels = new ObservableCollection(); + this.categorizedComponentTypes = new ObservableCollection(); this.messenger.Register(this, this.HandleEntityModified); this.InitializeEntityComponents(); + this.IntializeComponentTypes(); + } + + /// + public ICollection CategorizedComponentTypes + { + get { return this.categorizedComponentTypes; } } /// @@ -80,6 +98,7 @@ private void HandleEntityModified(object recipient, EntityModifiedMessage messag } this.InitializeEntityComponents(); + this.IntializeComponentTypes(); } /// @@ -94,4 +113,23 @@ private void InitializeEntityComponents() this.componentViewModels.Add(new EntityComponentViewModel(this.messenger, this.entity, component)); } } + + private void IntializeComponentTypes() + { + this.categorizedComponentTypes.Clear(); + + var assembly = Assembly.GetAssembly(typeof(TagComponent)) ?? throw new TypeAccessException("Failed to initialize core engine components."); + + var categoryToTypeMap = this.typeResolver.GetCategorizedTypes(assembly); + + foreach (var kvp in categoryToTypeMap) + { + var typeViewModels = kvp.Value.Select(x => + { + return new EntityComponentTypeViewModel(this.messenger, this.entity, x); + }); + + this.categorizedComponentTypes.Add(new EntityComponentCategoryViewModel(kvp.Key, typeViewModels)); + } + } } diff --git a/FinalEngine.Editor.ViewModels/Inspectors/IEntityComponentCategoryViewModel.cs b/FinalEngine.Editor.ViewModels/Inspectors/IEntityComponentCategoryViewModel.cs new file mode 100644 index 00000000..230c3b62 --- /dev/null +++ b/FinalEngine.Editor.ViewModels/Inspectors/IEntityComponentCategoryViewModel.cs @@ -0,0 +1,14 @@ +// +// Copyright (c) Software Antics. All rights reserved. +// + +namespace FinalEngine.Editor.ViewModels.Inspectors; + +using System.Collections.Generic; + +public interface IEntityComponentCategoryViewModel +{ + IEnumerable ComponentTypes { get; } + + string Name { get; } +} diff --git a/FinalEngine.Editor.ViewModels/Inspectors/IEntityComponentTypeViewModel.cs b/FinalEngine.Editor.ViewModels/Inspectors/IEntityComponentTypeViewModel.cs new file mode 100644 index 00000000..86d3bcff --- /dev/null +++ b/FinalEngine.Editor.ViewModels/Inspectors/IEntityComponentTypeViewModel.cs @@ -0,0 +1,14 @@ +// +// Copyright (c) Software Antics. All rights reserved. +// + +namespace FinalEngine.Editor.ViewModels.Inspectors; + +using System.Windows.Input; + +public interface IEntityComponentTypeViewModel +{ + ICommand AddCommand { get; } + + string Name { get; } +} diff --git a/FinalEngine.Editor.ViewModels/Inspectors/IEntityInspectorViewModel.cs b/FinalEngine.Editor.ViewModels/Inspectors/IEntityInspectorViewModel.cs index f87b7781..a5e4ecf8 100644 --- a/FinalEngine.Editor.ViewModels/Inspectors/IEntityInspectorViewModel.cs +++ b/FinalEngine.Editor.ViewModels/Inspectors/IEntityInspectorViewModel.cs @@ -1,5 +1,5 @@ // -// Copyright (c) Software Antics. All rights reserved. +// Copyright (c) Software Antics. All rights reserved. // namespace FinalEngine.Editor.ViewModels.Inspectors; @@ -7,15 +7,17 @@ namespace FinalEngine.Editor.ViewModels.Inspectors; using System.Collections.Generic; /// -/// Defines an interface that represents a model of the entity inspector view. +/// Defines an interface that represents a model of the entity inspector view. /// public interface IEntityInspectorViewModel { + ICollection CategorizedComponentTypes { get; } + /// - /// Gets the component view models. + /// Gets the component view models. /// /// - /// The component view models. + /// The component view models. /// ICollection ComponentViewModels { get; } } diff --git a/FinalEngine.Editor.ViewModels/Inspectors/PropertiesToolViewModel.cs b/FinalEngine.Editor.ViewModels/Inspectors/PropertiesToolViewModel.cs index f81e5839..4001876d 100644 --- a/FinalEngine.Editor.ViewModels/Inspectors/PropertiesToolViewModel.cs +++ b/FinalEngine.Editor.ViewModels/Inspectors/PropertiesToolViewModel.cs @@ -1,5 +1,5 @@ // -// Copyright (c) Software Antics. All rights reserved. +// Copyright (c) Software Antics. All rights reserved. // namespace FinalEngine.Editor.ViewModels.Inspectors; @@ -9,44 +9,49 @@ namespace FinalEngine.Editor.ViewModels.Inspectors; using CommunityToolkit.Mvvm.Messaging; using FinalEngine.Editor.ViewModels.Docking.Tools; using FinalEngine.Editor.ViewModels.Messages.Entities; +using FinalEngine.Editor.ViewModels.Services.Entities; using Microsoft.Extensions.Logging; /// -/// Provides a standard implementation of an . +/// Provides a standard implementation of an . /// -/// -/// +/// +/// public sealed class PropertiesToolViewModel : ToolViewModelBase, IPropertiesToolViewModel { /// - /// The logger. + /// The logger. /// private readonly ILogger logger; /// - /// The messenger. + /// The messenger. /// private readonly IMessenger messenger; + private readonly IEntityComponentTypeResolver typeResolver; + /// - /// The current view model to be shown in the properties view. + /// The current view model to be shown in the properties view. /// private ObservableObject? currentViewModel; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// - /// The logger. + /// The logger. /// /// - /// The messenger. + /// The messenger. /// public PropertiesToolViewModel( ILogger logger, + IEntityComponentTypeResolver typeResolver, IMessenger messenger) { this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); + this.typeResolver = typeResolver ?? throw new ArgumentNullException(nameof(typeResolver)); this.messenger = messenger ?? throw new ArgumentNullException(nameof(messenger)); this.Title = "Properties"; @@ -66,13 +71,13 @@ public ObservableObject? CurrentViewModel } /// - /// Handles the and resets the view. + /// Handles the and resets the view. /// /// - /// The recipient. + /// The recipient. /// /// - /// The message. + /// The message. /// private void HandleEntityDeleted(object recipient, EntityDeletedMessage message) { @@ -80,27 +85,27 @@ private void HandleEntityDeleted(object recipient, EntityDeletedMessage message) } /// - /// Handles the and updates the view. + /// Handles the and updates the view. /// /// - /// The recipient. + /// The recipient. /// /// - /// The message. + /// The message. /// /// - /// The specified parameter cannot be null. + /// The specified parameter cannot be null. /// private void HandleEntitySelected(object recipient, EntitySelectedMessage message) { this.logger.LogInformation($"Changing properties view to: '{nameof(EntityInspectorViewModel)}'."); this.Title = "Entity Inspector"; - this.CurrentViewModel = new EntityInspectorViewModel(this.messenger, message.Entity); + this.CurrentViewModel = new EntityInspectorViewModel(this.messenger, this.typeResolver, message.Entity); } /// - /// Resets the current view model. + /// Resets the current view model. /// private void ResetCurrentViewModel() { diff --git a/FinalEngine.Editor.ViewModels/Services/Entities/EntityComponentTypeResolver.cs b/FinalEngine.Editor.ViewModels/Services/Entities/EntityComponentTypeResolver.cs new file mode 100644 index 00000000..a28a9d97 --- /dev/null +++ b/FinalEngine.Editor.ViewModels/Services/Entities/EntityComponentTypeResolver.cs @@ -0,0 +1,47 @@ +// +// Copyright (c) Software Antics. All rights reserved. +// + +namespace FinalEngine.Editor.ViewModels.Services.Entities; + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Reflection; +using FinalEngine.ECS; + +public sealed class EntityComponentTypeResolver : IEntityComponentTypeResolver +{ + public IReadOnlyDictionary> GetCategorizedTypes(Assembly assembly) + { + if (assembly == null) + { + throw new ArgumentNullException(nameof(assembly)); + } + + var categoryToTypeMap = new Dictionary>(); + + var componentTypes = assembly.GetTypes() + .Where(x => + { + return typeof(IEntityComponent).IsAssignableFrom(x) && x.GetConstructor(Type.EmptyTypes) != null; + }); + + foreach (var componentType in componentTypes) + { + var categoryAttribute = componentType.GetCustomAttribute(); + string category = categoryAttribute?.Category ?? "Uncategorized"; + + if (!categoryToTypeMap.TryGetValue(category, out var types)) + { + types = new List(); + categoryToTypeMap.Add(category, types); + } + + types.Add(componentType); + } + + return categoryToTypeMap; + } +} diff --git a/FinalEngine.Editor.ViewModels/Services/Entities/IEntityComponentTypeResolver.cs b/FinalEngine.Editor.ViewModels/Services/Entities/IEntityComponentTypeResolver.cs new file mode 100644 index 00000000..dbcbf793 --- /dev/null +++ b/FinalEngine.Editor.ViewModels/Services/Entities/IEntityComponentTypeResolver.cs @@ -0,0 +1,14 @@ +// +// Copyright (c) Software Antics. All rights reserved. +// + +namespace FinalEngine.Editor.ViewModels.Services.Entities; + +using System; +using System.Collections.Generic; +using System.Reflection; + +public interface IEntityComponentTypeResolver +{ + IReadOnlyDictionary> GetCategorizedTypes(Assembly assembly); +}