From fc44c73ca872f7d73680aa77003e2ec057ac36be Mon Sep 17 00:00:00 2001 From: Misaki Date: Tue, 17 Jun 2025 19:37:30 +0900 Subject: [PATCH] Refactor project from Ghost.App to Ghost.Editor Changed the project structure to reflect a shift from `Ghost.App` to `Ghost.Editor`, updating namespaces and class names throughout. Changed the application class in `App.xaml` and `App.xaml.cs` from `GhostApplication` to `EditorApplication`. Changed several service interfaces to reside under `Ghost.Editor.Services.Contracts`, including `IInspectorService`, `INotificationService`, and `IProgressService`. Added `InspectorView` and `InspectorViewModel` classes to manage inspector functionality. Added `NavigationTabView` and `NavigationTabPage` classes to facilitate navigation within the editor. Enhanced `WorldNode` and `EntityNode` classes to support scene graph functionality, including serialization and entity management. Updated the project file `Ghost.Editor.csproj` to reflect the new structure and removed old references. Modified the solution file `GhostEngine.sln` to remove references to `Ghost.App` and include `Ghost.Editor`. Updated unit tests to align with the new namespaces and project structure. --- Ghost.App/ActivationHandler.cs | 2 +- Ghost.App/App.xaml | 4 +- Ghost.App/App.xaml.cs | 22 +- Ghost.App/AssemblyInfo.cs | 3 + Ghost.App/Contracts/IInspectable.cs | 15 ++ Ghost.App/Contracts/INavigationAware.cs | 2 +- Ghost.App/Controls/Internal/InspectorView.cs | 24 --- .../Controls/Internal/InspectorView.xaml | 41 ---- .../Controls/Internal/NavigationTabView.cs | 39 ++++ .../Controls/Internal/NavigationTabView.xaml | 5 + Ghost.App/Controls/ViewModelPage.cs | 4 +- .../AppState/AppStateMachine.cs | 6 +- .../AppState/EditorState.cs | 27 ++- .../AppState/IAppState.cs | 2 +- .../AppState/LandingState.cs | 17 +- .../AppState/StateKey.cs | 2 +- Ghost.App/Core/AssetHandle/Asset.cs | 24 +++ Ghost.App/Core/AssetHandle/AssetDatabase.cs | 60 ++++++ .../AssetHandle/AssetOpenHandlerAttribute .cs | 15 ++ .../Core/SceneGraph/EditorWorldManager.cs | 53 +++++ Ghost.App/Core/SceneGraph/EntityNode.cs | 70 +++++++ .../Core/SceneGraph/SceneGraphHelpers.cs | 112 ++++++++++ Ghost.App/Core/SceneGraph/SceneGraphNode.cs | 54 +++++ Ghost.App/Core/SceneGraph/WorldNode.cs | 195 ++++++++++++++++++ .../Core/Serializer/WorldNodeSerializer.cs | 131 ++++++++++++ .../{Ghost.App.csproj => Ghost.Editor.csproj} | 28 ++- Ghost.App/Models/AssetItem.cs | 2 +- Ghost.App/Models/ExplorerItem.cs | 2 +- Ghost.App/Models/MessageType.cs | 9 + Ghost.App/Resources/EditorIconSource.cs | 18 ++ Ghost.App/Resources/FileExtensions.cs | 11 + .../Services/Contracts/IInspectorService.cs | 14 ++ .../Contracts/INotificationService.cs | 8 + .../Services/Contracts/IProgressService.cs | 9 + Ghost.App/Services/InspectorService.cs | 22 ++ Ghost.App/Services/NotificationService.cs | 2 +- Ghost.App/Services/ProgressService.cs | 2 +- Ghost.App/Themes/Override.xaml | 5 +- Ghost.App/Utilities/ComponentTypeCache.cs | 2 +- Ghost.App/Utilities/HostHelpers.Page.cs | 13 +- Ghost.App/Utilities/SystemUtilities.cs | 9 +- .../View/Pages/EngineEditor/ConsolePage.xaml | 7 +- .../Pages/EngineEditor/ConsolePage.xaml.cs | 6 +- .../Pages/EngineEditor/HierarchyPage.xaml | 21 +- .../Pages/EngineEditor/HierarchyPage.xaml.cs | 47 ++++- .../Pages/EngineEditor/InspectorPage.xaml | 45 ++++ .../Pages/EngineEditor/InspectorPage.xaml.cs | 36 ++++ .../View/Pages/EngineEditor/ProjectPage.xaml | 6 +- .../Pages/EngineEditor/ProjectPage.xaml.cs | 8 +- .../View/Pages/Landing/CreateProjectPage.xaml | 4 +- .../Pages/Landing/CreateProjectPage.xaml.cs | 4 +- .../View/Pages/Landing/OpenProjectPage.xaml | 4 +- .../Pages/Landing/OpenProjectPage.xaml.cs | 8 +- .../View/Windows/EngineEditorWindow.xaml | 47 ++--- .../View/Windows/EngineEditorWindow.xaml.cs | 14 +- Ghost.App/View/Windows/LandingWindow.xaml | 4 +- Ghost.App/View/Windows/LandingWindow.xaml.cs | 10 +- .../Pages/EngineEditor/HierarchyViewModel.cs | 20 +- .../Pages/EngineEditor/InspectorViewModel.cs | 32 +++ .../Pages/EngineEditor/ProjectViewModel.cs | 15 +- .../Pages/Landing/CreateProjectViewModel.cs | 8 +- .../Pages/Landing/OpenProjectViewModel.cs | 7 +- Ghost.Data/AssemblyInfo.cs | 2 +- Ghost.Editor/{Models => AssetHandle}/Asset.cs | 2 +- Ghost.Editor/Contracts/IInspectable.cs | 3 +- Ghost.Editor/SceneGraph/WorldNode.cs | 7 +- .../Services/Contracts/IInspectorService.cs | 2 + Ghost.Engine/AssemblyInfo.cs | 2 +- Ghost.Entities/AssemblyInfo.cs | 1 - Ghost.UnitTest/Ghost.UnitTest.csproj | 2 +- Ghost.UnitTest/Test/EntityTest.cs | 4 +- Ghost.UnitTest/Test/SerializationTest.cs | 6 +- Ghost.UnitTest/TestFramework/ITest.cs | 2 +- Ghost.UnitTest/TestFramework/TestRunner.cs | 2 +- Ghost.UnitTest/UnitTestApp.xaml.cs | 18 +- Ghost.UnitTest/UnitTestAppWindow.xaml.cs | 13 -- Ghost.UnitTest/UnitTests.cs | 3 - GhostEngine.sln | 16 +- Test/Class1.cs | 13 ++ Test/Test.csproj | 17 ++ 80 files changed, 1244 insertions(+), 309 deletions(-) create mode 100644 Ghost.App/AssemblyInfo.cs create mode 100644 Ghost.App/Contracts/IInspectable.cs delete mode 100644 Ghost.App/Controls/Internal/InspectorView.cs delete mode 100644 Ghost.App/Controls/Internal/InspectorView.xaml create mode 100644 Ghost.App/Controls/Internal/NavigationTabView.cs create mode 100644 Ghost.App/Controls/Internal/NavigationTabView.xaml rename Ghost.App/{Infrastructures => Core}/AppState/AppStateMachine.cs (85%) rename Ghost.App/{Infrastructures => Core}/AppState/EditorState.cs (60%) rename Ghost.App/{Infrastructures => Core}/AppState/IAppState.cs (95%) rename Ghost.App/{Infrastructures => Core}/AppState/LandingState.cs (60%) rename Ghost.App/{Infrastructures => Core}/AppState/StateKey.cs (57%) create mode 100644 Ghost.App/Core/AssetHandle/Asset.cs create mode 100644 Ghost.App/Core/AssetHandle/AssetDatabase.cs create mode 100644 Ghost.App/Core/AssetHandle/AssetOpenHandlerAttribute .cs create mode 100644 Ghost.App/Core/SceneGraph/EditorWorldManager.cs create mode 100644 Ghost.App/Core/SceneGraph/EntityNode.cs create mode 100644 Ghost.App/Core/SceneGraph/SceneGraphHelpers.cs create mode 100644 Ghost.App/Core/SceneGraph/SceneGraphNode.cs create mode 100644 Ghost.App/Core/SceneGraph/WorldNode.cs create mode 100644 Ghost.App/Core/Serializer/WorldNodeSerializer.cs rename Ghost.App/{Ghost.App.csproj => Ghost.Editor.csproj} (94%) create mode 100644 Ghost.App/Models/MessageType.cs create mode 100644 Ghost.App/Resources/EditorIconSource.cs create mode 100644 Ghost.App/Resources/FileExtensions.cs create mode 100644 Ghost.App/Services/Contracts/IInspectorService.cs create mode 100644 Ghost.App/Services/Contracts/INotificationService.cs create mode 100644 Ghost.App/Services/Contracts/IProgressService.cs create mode 100644 Ghost.App/Services/InspectorService.cs create mode 100644 Ghost.App/View/Pages/EngineEditor/InspectorPage.xaml create mode 100644 Ghost.App/View/Pages/EngineEditor/InspectorPage.xaml.cs create mode 100644 Ghost.App/ViewModels/Pages/EngineEditor/InspectorViewModel.cs rename Ghost.Editor/{Models => AssetHandle}/Asset.cs (90%) create mode 100644 Test/Class1.cs create mode 100644 Test/Test.csproj diff --git a/Ghost.App/ActivationHandler.cs b/Ghost.App/ActivationHandler.cs index a9343ec..9ac4713 100644 --- a/Ghost.App/ActivationHandler.cs +++ b/Ghost.App/ActivationHandler.cs @@ -3,7 +3,7 @@ using Ghost.Data.Services; using Microsoft.UI.Xaml; using System.IO; -namespace Ghost.App; +namespace Ghost.Editor; internal static class ActivationHandler { diff --git a/Ghost.App/App.xaml b/Ghost.App/App.xaml index 885167e..d768f52 100644 --- a/Ghost.App/App.xaml +++ b/Ghost.App/App.xaml @@ -1,9 +1,9 @@ + xmlns:local="using:Ghost.Editor"> diff --git a/Ghost.App/App.xaml.cs b/Ghost.App/App.xaml.cs index e338d7a..2d8668e 100644 --- a/Ghost.App/App.xaml.cs +++ b/Ghost.App/App.xaml.cs @@ -1,31 +1,30 @@ -using Ghost.App.Infrastructures.AppState; -using Ghost.App.Services; -using Ghost.App.Utilities; +using Ghost.Editor.Core.AppState; +using Ghost.Editor.Services; using Ghost.Editor.Services.Contracts; +using Ghost.Editor.Utilities; using Ghost.Engine.Services; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.UI.Xaml; -using System; // To learn more about WinUI, the WinUI project structure, // and more about our project templates, see: http://aka.ms/winui-project-info. -namespace Ghost.App; +namespace Ghost.Editor; /// /// Provides application-specific behavior to supplement the default Application class. /// -public partial class GhostApplication : Application +public partial class EditorApplication : Application { private Window? _window; internal static Window? Window { - get => (Current as GhostApplication)!._window; + get => (Current as EditorApplication)!._window; set { - if (Current is GhostApplication app) + if (Current is EditorApplication app) { app._window = value; } @@ -41,7 +40,7 @@ public partial class GhostApplication : Application /// Initializes the singleton application object. This is the first line of authored code /// executed, and as such is the logical equivalent of main() or WinMain(). /// - internal GhostApplication() + internal EditorApplication() { InitializeComponent(); @@ -56,6 +55,7 @@ public partial class GhostApplication : Application services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); }) .Build(); @@ -64,12 +64,12 @@ public partial class GhostApplication : Application internal static IServiceScope CreateScope() { - return (Current as GhostApplication)!.Host.Services.CreateScope(); + return (Current as EditorApplication)!.Host.Services.CreateScope(); } public static T GetService() where T : class { - if ((Current as GhostApplication)!.Host.Services.GetService(typeof(T)) is not T service) + if ((Current as EditorApplication)!.Host.Services.GetService(typeof(T)) is not T service) { throw new ArgumentException($"{typeof(T)} needs to be registered in ConfigureServices within App.xaml.cs."); } diff --git a/Ghost.App/AssemblyInfo.cs b/Ghost.App/AssemblyInfo.cs new file mode 100644 index 0000000..d3b1fb4 --- /dev/null +++ b/Ghost.App/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Ghost.UnitTest")] diff --git a/Ghost.App/Contracts/IInspectable.cs b/Ghost.App/Contracts/IInspectable.cs new file mode 100644 index 0000000..958d40c --- /dev/null +++ b/Ghost.App/Contracts/IInspectable.cs @@ -0,0 +1,15 @@ +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; + +namespace Ghost.Editor.Contracts; + +public interface IInspectable +{ + public IconSource? Icon + { + get; + } + + public UIElement? HeaderContent(); + public UIElement? InspectorContent(); +} \ No newline at end of file diff --git a/Ghost.App/Contracts/INavigationAware.cs b/Ghost.App/Contracts/INavigationAware.cs index 51349ab..4dcd428 100644 --- a/Ghost.App/Contracts/INavigationAware.cs +++ b/Ghost.App/Contracts/INavigationAware.cs @@ -1,4 +1,4 @@ -namespace Ghost.App.Contracts; +namespace Ghost.Editor.Contracts; public interface INavigationAware { diff --git a/Ghost.App/Controls/Internal/InspectorView.cs b/Ghost.App/Controls/Internal/InspectorView.cs deleted file mode 100644 index e87b7d6..0000000 --- a/Ghost.App/Controls/Internal/InspectorView.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; - -namespace Ghost.Editor.Controls.Internal; - -internal sealed partial class InspectorView : ContentControl -{ - public UIElement? Header - { - get => (UIElement)GetValue(HeaderProperty); - set => SetValue(HeaderProperty, value); - } - - public static readonly DependencyProperty HeaderProperty = DependencyProperty.Register( - nameof(Header), - typeof(UIElement), - typeof(InspectorView), - new PropertyMetadata(null)); - - public InspectorView() - { - DefaultStyleKey = typeof(InspectorView); - } -} diff --git a/Ghost.App/Controls/Internal/InspectorView.xaml b/Ghost.App/Controls/Internal/InspectorView.xaml deleted file mode 100644 index 9dccfeb..0000000 --- a/Ghost.App/Controls/Internal/InspectorView.xaml +++ /dev/null @@ -1,41 +0,0 @@ - - - - diff --git a/Ghost.App/Controls/Internal/NavigationTabView.cs b/Ghost.App/Controls/Internal/NavigationTabView.cs new file mode 100644 index 0000000..cf33757 --- /dev/null +++ b/Ghost.App/Controls/Internal/NavigationTabView.cs @@ -0,0 +1,39 @@ +using Ghost.Editor.Contracts; +using Microsoft.UI.Xaml.Controls; + +namespace Ghost.Editor.Controls.Internal; + +public partial class NavigationTabPage : TabViewItem, INavigationAware +{ + public virtual void OnNavigatedTo(object? parameter) + { + } + + public virtual void OnNavigatedFrom() + { + } +} + +public sealed partial class NavigationTabView : TabView +{ + public NavigationTabView() + { + this.SelectionChanged += NavigationTabView_SelectionChanged; + } + + private void NavigationTabView_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + foreach (var oldItem in e.RemovedItems) + { + if (oldItem is NavigationTabPage oldPage) + { + oldPage.OnNavigatedFrom(); + } + } + + if (SelectedItem is NavigationTabPage newPage) + { + newPage.OnNavigatedTo(null); + } + } +} diff --git a/Ghost.App/Controls/Internal/NavigationTabView.xaml b/Ghost.App/Controls/Internal/NavigationTabView.xaml new file mode 100644 index 0000000..ab3ac5e --- /dev/null +++ b/Ghost.App/Controls/Internal/NavigationTabView.xaml @@ -0,0 +1,5 @@ + + diff --git a/Ghost.App/Controls/ViewModelPage.cs b/Ghost.App/Controls/ViewModelPage.cs index de29a1c..06f3870 100644 --- a/Ghost.App/Controls/ViewModelPage.cs +++ b/Ghost.App/Controls/ViewModelPage.cs @@ -1,9 +1,9 @@ using CommunityToolkit.Mvvm.ComponentModel; -using Ghost.App.Contracts; +using Ghost.Editor.Contracts; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Navigation; -namespace Ghost.App.Controls; +namespace Ghost.Editor.Controls; public abstract partial class ViewModelPage : Page where VM : ObservableObject diff --git a/Ghost.App/Infrastructures/AppState/AppStateMachine.cs b/Ghost.App/Core/AppState/AppStateMachine.cs similarity index 85% rename from Ghost.App/Infrastructures/AppState/AppStateMachine.cs rename to Ghost.App/Core/AppState/AppStateMachine.cs index d6700a2..0990c09 100644 --- a/Ghost.App/Infrastructures/AppState/AppStateMachine.cs +++ b/Ghost.App/Core/AppState/AppStateMachine.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace Ghost.App.Infrastructures.AppState; +namespace Ghost.Editor.Core.AppState; internal class AppStateMachine { diff --git a/Ghost.App/Infrastructures/AppState/EditorState.cs b/Ghost.App/Core/AppState/EditorState.cs similarity index 60% rename from Ghost.App/Infrastructures/AppState/EditorState.cs rename to Ghost.App/Core/AppState/EditorState.cs index de9d32c..0fa6514 100644 --- a/Ghost.App/Infrastructures/AppState/EditorState.cs +++ b/Ghost.App/Core/AppState/EditorState.cs @@ -1,13 +1,10 @@ -using Ghost.App.View.Windows; -using Ghost.Data.Models; +using Ghost.Data.Models; using Ghost.Data.Services; -using Ghost.Editor; +using Ghost.Editor.Core.AssetHandle; +using Ghost.Editor.View.Windows; using Ghost.Engine; -using Microsoft.UI.Xaml; -using System; -using System.Threading.Tasks; -namespace Ghost.App.Infrastructures.AppState; +namespace Ghost.Editor.Core.AppState; internal class EditorState : IAppState { @@ -16,9 +13,9 @@ internal class EditorState : IAppState public Task OnExitingAsync() { - if (GhostApplication.Window == _window) + if (EditorApplication.Window == _window) { - GhostApplication.Window = null; + EditorApplication.Window = null; } return Task.CompletedTask; } @@ -32,13 +29,13 @@ internal class EditorState : IAppState ProjectService.CurrentProject = metadataInfo; - _engineCore = GhostApplication.GetService(); + _engineCore = EditorApplication.GetService(); await _engineCore.StartAsync(new Engine.Models.LaunchArgument()); - _window = GhostApplication.GetService(); + _window = EditorApplication.GetService(); _window.Activate(); - GhostApplication.Window = _window; + EditorApplication.Window = _window; } public async Task OnExitedAsync() @@ -48,9 +45,9 @@ internal class EditorState : IAppState await _engineCore.ShutDownAsync(); } - if (GhostApplication.Window == _window) + if (EditorApplication.Window == _window) { - GhostApplication.Window = null; + EditorApplication.Window = null; } _window?.Close(); @@ -59,7 +56,7 @@ internal class EditorState : IAppState public Task OnEnteredAsync(object? parameter) { - EditorApplication.Activate(parameter, ((GhostApplication)(Application.Current)).Host.Services); + AssetDatabase.Initialize(); return Task.CompletedTask; } } \ No newline at end of file diff --git a/Ghost.App/Infrastructures/AppState/IAppState.cs b/Ghost.App/Core/AppState/IAppState.cs similarity index 95% rename from Ghost.App/Infrastructures/AppState/IAppState.cs rename to Ghost.App/Core/AppState/IAppState.cs index 56e2092..b863805 100644 --- a/Ghost.App/Infrastructures/AppState/IAppState.cs +++ b/Ghost.App/Core/AppState/IAppState.cs @@ -1,6 +1,6 @@ using System.Threading.Tasks; -namespace Ghost.App.Infrastructures.AppState; +namespace Ghost.Editor.Core.AppState; internal interface IAppState { diff --git a/Ghost.App/Infrastructures/AppState/LandingState.cs b/Ghost.App/Core/AppState/LandingState.cs similarity index 60% rename from Ghost.App/Infrastructures/AppState/LandingState.cs rename to Ghost.App/Core/AppState/LandingState.cs index 5e132db..e6f7ec4 100644 --- a/Ghost.App/Infrastructures/AppState/LandingState.cs +++ b/Ghost.App/Core/AppState/LandingState.cs @@ -1,7 +1,8 @@ -using Ghost.App.View.Windows; +using Ghost.Editor; +using Ghost.Editor.View.Windows; using System.Threading.Tasks; -namespace Ghost.App.Infrastructures.AppState; +namespace Ghost.Editor.Core.AppState; internal class LandingState : IAppState { @@ -9,17 +10,17 @@ internal class LandingState : IAppState public Task OnExitingAsync() { - if (GhostApplication.Window == _window) + if (EditorApplication.Window == _window) { - GhostApplication.Window = null; + EditorApplication.Window = null; } return Task.CompletedTask; } public Task OnEnteringAsync(object? parameter) { - _window = GhostApplication.GetService(); - GhostApplication.Window = _window; + _window = EditorApplication.GetService(); + EditorApplication.Window = _window; _window.Activate(); return Task.CompletedTask; @@ -27,9 +28,9 @@ internal class LandingState : IAppState public Task OnExitedAsync() { - if (GhostApplication.Window == _window) + if (EditorApplication.Window == _window) { - GhostApplication.Window = null; + EditorApplication.Window = null; } _window?.Close(); diff --git a/Ghost.App/Infrastructures/AppState/StateKey.cs b/Ghost.App/Core/AppState/StateKey.cs similarity index 57% rename from Ghost.App/Infrastructures/AppState/StateKey.cs rename to Ghost.App/Core/AppState/StateKey.cs index ac91313..882baf2 100644 --- a/Ghost.App/Infrastructures/AppState/StateKey.cs +++ b/Ghost.App/Core/AppState/StateKey.cs @@ -1,4 +1,4 @@ -namespace Ghost.App.Infrastructures.AppState; +namespace Ghost.Editor.Core.AppState; internal enum StateKey { diff --git a/Ghost.App/Core/AssetHandle/Asset.cs b/Ghost.App/Core/AssetHandle/Asset.cs new file mode 100644 index 0000000..b29a045 --- /dev/null +++ b/Ghost.App/Core/AssetHandle/Asset.cs @@ -0,0 +1,24 @@ +namespace Ghost.Editor.Core.AssetHandle; + +public abstract class Asset +{ + /// + /// Get the Guid of the asset. + /// + public Guid GUID + { + get; + } = Guid.NewGuid(); + + /// + /// True if the asset is a folder, false if it is a file. + /// + public bool IsFolder + { + get; + } + + internal void GenerateMetadata() + { + } +} \ No newline at end of file diff --git a/Ghost.App/Core/AssetHandle/AssetDatabase.cs b/Ghost.App/Core/AssetHandle/AssetDatabase.cs new file mode 100644 index 0000000..3714b67 --- /dev/null +++ b/Ghost.App/Core/AssetHandle/AssetDatabase.cs @@ -0,0 +1,60 @@ +using System.Diagnostics; +using System.Reflection; + +namespace Ghost.Editor.Core.AssetHandle; + +public static class AssetDatabase +{ + private static readonly Dictionary> _assetOpenHandlers = new(StringComparer.OrdinalIgnoreCase); + + static AssetDatabase() + { + Initialize(); + } + + internal static void Initialize() + { + RegisterAssetHandles(); + } + + private static void RegisterAssetHandles() + { + var methods = AppDomain.CurrentDomain.GetAssemblies() + .SelectMany(a => a.GetTypes()) + .SelectMany(t => t.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)) + .Where(m => m.GetCustomAttribute() != null && + m.GetParameters().Length == 1 && + m.GetParameters()[0].ParameterType == typeof(string)); + + foreach (var method in methods) + { + var attr = method.GetCustomAttribute()!; + var del = (Action)Delegate.CreateDelegate(typeof(Action), method); + foreach (var ext in attr.Extensions) + { + if (_assetOpenHandlers.ContainsKey(ext)) + { + throw new InvalidOperationException($"Duplicate handler for extension '{ext}'"); + } + + _assetOpenHandlers[ext] = del; + } + } + } + + public static void OpenAsset(string path) + { + var extension = Path.GetExtension(path); + if (_assetOpenHandlers.TryGetValue(extension, out var handler)) + { + handler(path); + } + else + { + Process.Start(new ProcessStartInfo(path) + { + UseShellExecute = true + }); + } + } +} \ No newline at end of file diff --git a/Ghost.App/Core/AssetHandle/AssetOpenHandlerAttribute .cs b/Ghost.App/Core/AssetHandle/AssetOpenHandlerAttribute .cs new file mode 100644 index 0000000..1d2cfb7 --- /dev/null +++ b/Ghost.App/Core/AssetHandle/AssetOpenHandlerAttribute .cs @@ -0,0 +1,15 @@ +namespace Ghost.Editor.Core.AssetHandle; + +[AttributeUsage(AttributeTargets.Method)] +public class AssetOpenHandlerAttribute : Attribute +{ + public string[] Extensions + { + get; + } + + public AssetOpenHandlerAttribute(params string[] extensions) + { + Extensions = extensions.Select(e => e.StartsWith('.') ? e.ToLowerInvariant() : '.' + e.ToLowerInvariant()).ToArray(); + } +} \ No newline at end of file diff --git a/Ghost.App/Core/SceneGraph/EditorWorldManager.cs b/Ghost.App/Core/SceneGraph/EditorWorldManager.cs new file mode 100644 index 0000000..79da3f5 --- /dev/null +++ b/Ghost.App/Core/SceneGraph/EditorWorldManager.cs @@ -0,0 +1,53 @@ +using Ghost.Editor.Resources; +using Ghost.Editor.Services.Contracts; +using Ghost.Engine.Resources; +using System.Text.Json; + +namespace Ghost.Editor.Core.SceneGraph; + +public enum OpenWorldMode +{ + Single, + Additive, + AdditiveWithoutLoading +} + +public static class EditorWorldManager +{ + // TODO: Use guid keys instead of string paths for better performance and uniqueness + private static readonly Dictionary _loadedWorlds = new(); + public static IEnumerable LoadedWorlds => _loadedWorlds.Values; + + public static event Action? OnWorldLoaded; + public static event Action? OnWorldUnloaded; + + public static async Task LoadWorld(string worldPath) + { + if (_loadedWorlds.ContainsKey(worldPath) + || !File.Exists(worldPath) + || Path.GetExtension(worldPath) != FileExtensions.SCENE_FILE_EXTENSION) + { + return; + } + + var progressService = EditorApplication.GetService(); + progressService.ShowIndeterminateProgress("Loading world..."); + + foreach (var world in _loadedWorlds) + { + world.Value.Unload(); + OnWorldUnloaded?.Invoke(world.Value); + } + + await using var readStream = new FileStream(worldPath, FileMode.Open, FileAccess.Read, FileShare.Read); + var deserializedScene = await JsonSerializer.DeserializeAsync(readStream, StaticResource.defaultSerializerOptions) ?? throw new Exception("Deserialization failed."); + + _loadedWorlds.Clear(); + + _loadedWorlds[worldPath] = deserializedScene; + await deserializedScene.LoadAsync(); + + progressService.HideProgress(); + OnWorldLoaded?.Invoke(deserializedScene); + } +} \ No newline at end of file diff --git a/Ghost.App/Core/SceneGraph/EntityNode.cs b/Ghost.App/Core/SceneGraph/EntityNode.cs new file mode 100644 index 0000000..dd38ed1 --- /dev/null +++ b/Ghost.App/Core/SceneGraph/EntityNode.cs @@ -0,0 +1,70 @@ +using Ghost.Editor.Contracts; +using Ghost.Editor.Resources; +using Ghost.Entities; +using Microsoft.UI.Text; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Data; + +namespace Ghost.Editor.Core.SceneGraph; + +public partial class EntityNode : SceneGraphNode +{ + private readonly Entity _entity; + + public Entity Entity => _entity; + public override SceneGraphNodeType NodeType => SceneGraphNodeType.Entity; + + public EntityNode(Entity entity, string name) + { + _entity = entity; + Name = name; + } + + internal EntityNode() + { + } +} + +public partial class EntityNode : IInspectable +{ + public IconSource? Icon => EditorIconSource.entity_24; + + public UIElement? HeaderContent() + { + var root = new StackPanel() + { + HorizontalAlignment = HorizontalAlignment.Stretch, + VerticalAlignment = VerticalAlignment.Center + }; + + var nameText = new TextBox + { + Text = Name, + FontWeight = FontWeights.Bold, + }; + var idText = new TextBlock + { + Text = $"ID: {_entity.ID}", + Margin = new Thickness(0, 5, 0, 0), + }; + + nameText.SetBinding(TextBox.TextProperty, new Binding + { + Source = this, + Path = new PropertyPath(nameof(Name)), + Mode = BindingMode.TwoWay, + UpdateSourceTrigger = UpdateSourceTrigger.LostFocus, + }); + + root.Children.Add(nameText); + root.Children.Add(idText); + + return root; + } + + public UIElement? InspectorContent() + { + return null; + } +} \ No newline at end of file diff --git a/Ghost.App/Core/SceneGraph/SceneGraphHelpers.cs b/Ghost.App/Core/SceneGraph/SceneGraphHelpers.cs new file mode 100644 index 0000000..3254ac0 --- /dev/null +++ b/Ghost.App/Core/SceneGraph/SceneGraphHelpers.cs @@ -0,0 +1,112 @@ +using Ghost.Engine.Components; +using Ghost.Entities; + +namespace Ghost.Editor.Core.SceneGraph; + +public class SceneGraphHelpers +{ + /// + /// Creates a new entity with default components. + /// + /// The world context where the entity will be created. + /// The entity to be wrapped in the . + public static EntityNode CreateEntityNode(World world, Entity entity, string name) + { + world.EntityManager.AddComponent(entity, LocalToWorld.Identity); + world.EntityManager.AddComponent(entity, Hierarchy.Root); + return new EntityNode(entity, name); + } + + /// + /// Creates a new and entity with default components. + /// + /// The world context where the entity will be created. + public static EntityNode CreateEntityNode(World world, string name) + { + var entity = world.EntityManager.CreateEntity(); + return CreateEntityNode(world, entity, name); + } + + /// + /// Attaches childEntity to parentEntity in the scene graph. + /// + /// The world context where the entities exist. + /// The parent entity to which the child will be attached. + /// The child entity to be attached. + public static void AttachChild(WorldNode scene, EntityNode parentNode, EntityNode childNode) + { + // 1) If the child already has a parent, detach it first + var childHierarchy = scene.World.EntityManager.GetComponent(childNode.Entity); + if (childHierarchy.ValueRO.parent != Entity.Invalid) + { + DetachFromParent(scene, childNode); + } + + // 2) Link child to new parent + childHierarchy.ValueRW.parent = parentNode.Entity; + + // 3) Insert child at the head of parent's child list + var parentHierarchy = scene.World.EntityManager.GetComponent(parentNode.Entity); + + childHierarchy.ValueRW.nextSibling = parentHierarchy.ValueRO.firstChild; + parentHierarchy.ValueRW.firstChild = childNode.Entity; + + // 4) Write back + scene.World.EntityManager.SetComponent(parentNode.Entity, in parentHierarchy.ValueRO); + scene.World.EntityManager.SetComponent(childNode.Entity, in childHierarchy.ValueRO); + + // 5) Update children list in parent node + parentNode.AddChild(childNode); + } + + /// + /// Detaches the specified entity from its parent in the scene graph. + /// + /// The world context where the entities exist. + /// The entity to detach from its parent. + public static void DetachFromParent(WorldNode scene, EntityNode node) + { + var hierarchy = scene.World.EntityManager.GetComponent(node.Entity); + var parent = hierarchy.ValueRO.parent; + if (parent == Entity.Invalid) + { + return; // already root + } + + var parentHierarchy = scene.World.EntityManager.GetComponent(parent); + + // If entity is the first child, simply move head + if (parentHierarchy.ValueRO.firstChild == node.Entity) + { + parentHierarchy.ValueRW.firstChild = hierarchy.ValueRO.nextSibling; + } + else + { + // Otherwise, find the previous sibling in the linked list + var prevSibling = parentHierarchy.ValueRO.firstChild; + while (prevSibling != Entity.Invalid) + { + var prevHierarchy = scene.World.EntityManager.GetComponent(prevSibling); + if (prevHierarchy.ValueRW.nextSibling == node.Entity) + { + prevHierarchy.ValueRW.nextSibling = hierarchy.ValueRO.nextSibling; + scene.World.EntityManager.SetComponent(prevSibling, in prevHierarchy.ValueRO); + break; + } + + prevSibling = prevHierarchy.ValueRO.nextSibling; + } + } + + // Clear child's references + hierarchy.ValueRW.parent = Entity.Invalid; + hierarchy.ValueRW.nextSibling = Entity.Invalid; + + // Write back + scene.World.EntityManager.SetComponent(parent, in parentHierarchy.ValueRO); + scene.World.EntityManager.SetComponent(node.Entity, in hierarchy.ValueRO); + + // Remove from parent's children list + scene.EntityNodeLookup[parent].RemoveChild(node); + } +} \ No newline at end of file diff --git a/Ghost.App/Core/SceneGraph/SceneGraphNode.cs b/Ghost.App/Core/SceneGraph/SceneGraphNode.cs new file mode 100644 index 0000000..d8afc1d --- /dev/null +++ b/Ghost.App/Core/SceneGraph/SceneGraphNode.cs @@ -0,0 +1,54 @@ +using CommunityToolkit.Mvvm.ComponentModel; +using System.Collections.ObjectModel; + +namespace Ghost.Editor.Core.SceneGraph; + +public enum SceneGraphNodeType +{ + Scene, + Entity, +} + +public abstract partial class SceneGraphNode : ObservableObject +{ + public ObservableCollection? Children + { + get; + private set; + } + + [ObservableProperty] + public partial string Name + { + get; + set; + } + + public abstract SceneGraphNodeType NodeType + { + get; + } + + public int ChildCount => Children?.Count ?? 0; + + public virtual void AddChild(SceneGraphNode child) + { + Children ??= new(); + Children.Add(child); + } + + public virtual bool RemoveChild(SceneGraphNode child) + { + return Children?.Remove(child) ?? false; + } + + public SceneGraphNode GetChild(int index) + { + if (Children == null || index < 0 || index >= Children.Count) + { + throw new ArgumentOutOfRangeException(nameof(index), "Index is out of range."); + } + + return Children[index]; + } +} \ No newline at end of file diff --git a/Ghost.App/Core/SceneGraph/WorldNode.cs b/Ghost.App/Core/SceneGraph/WorldNode.cs new file mode 100644 index 0000000..944087e --- /dev/null +++ b/Ghost.App/Core/SceneGraph/WorldNode.cs @@ -0,0 +1,195 @@ +using Ghost.Editor.Contracts; +using Ghost.Editor.Core.AssetHandle; +using Ghost.Editor.Core.Serializer; +using Ghost.Editor.Resources; +using Ghost.Engine.Components; +using Ghost.Entities; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using System.Text.Json.Serialization; + +namespace Ghost.Editor.Core.SceneGraph; + +[JsonConverter(typeof(WorldNodeSerializer))] +public partial class WorldNode : SceneGraphNode, IEquatable +{ + private World _world; + private Dictionary _entityNodeLookup = new(); + + public World World => _world; + public Dictionary EntityNodeLookup => _entityNodeLookup; + + public override SceneGraphNodeType NodeType => SceneGraphNodeType.Scene; + + public WorldNode(World world, string name) + { + _world = world; + Name = name; + } + + internal WorldNode() + { + _world = World.Create(); + } + + private void UpdateLookup(Entity key, EntityNode value) + { + _entityNodeLookup[key] = value; + if (value.Children == null) + { + return; + } + + foreach (var child in value.Children) + { + if (child is EntityNode entityChild) + { + UpdateLookup(entityChild.Entity, entityChild); + } + } + } + + public override void AddChild(SceneGraphNode child) + { + if (child is not EntityNode entityNode) + { + throw new ArgumentException("Child must be of type EntityNode.", nameof(child)); + } + + base.AddChild(entityNode); + UpdateLookup(entityNode.Entity, entityNode); + } + + public override bool RemoveChild(SceneGraphNode child) + { + if (child is not EntityNode entityNode) + { + throw new ArgumentException("Child must be of type EntityNode.", nameof(child)); + } + + var result = base.RemoveChild(child); + if (result) + { + _entityNodeLookup.Remove(entityNode.Entity); + } + + return result; + } + + private EntityNode BuildNodeRecursive(Entity entity, World world) + { + if (!_entityNodeLookup.TryGetValue(entity, out var node)) + { + node = new EntityNode(entity, "New Entity"); + _entityNodeLookup[entity] = node; + } + + var hc = world.EntityManager.GetComponent(entity); + var child = hc.ValueRO.firstChild; + + while (child != Entity.Invalid) + { + node.AddChild(BuildNodeRecursive(child, world)); + var childHC = world.EntityManager.GetComponent(child); + child = childHC.ValueRO.nextSibling; + } + + return node; + } + + private void BuildGraph() + { + foreach (var (entity, hierarchy) in _world.Query()) + { + if (hierarchy.ValueRO.parent == Entity.Invalid) + { + var node = BuildNodeRecursive(entity, _world); + AddChild(node); + } + } + } + + public Task LoadAsync() + { + return Task.Run(BuildGraph); + } + + public void Unload() + { + _world.Dispose(); + _world = null!; + + Children?.Clear(); + _entityNodeLookup.Clear(); + } + + public override string ToString() + { + return $"WorldNode: {Name} (World ID: {_world.ID})"; + } + + public override int GetHashCode() + { + return HashCode.Combine(_world, Name); + } + + public override bool Equals(object? obj) + { + return obj is WorldNode other && Equals(other); + } + + public bool Equals(WorldNode? other) + { + if (other is null) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + + return _world.Equals(other._world) && Name == other.Name; + } + + public static bool operator ==(WorldNode? left, WorldNode? right) + { + if (left is null) + { + return right is null; + } + return left.Equals(right); + } + + public static bool operator !=(WorldNode? left, WorldNode? right) + { + return !(left == right); + } +} + +public partial class WorldNode : IInspectable +{ + public IconSource? Icon => EditorIconSource.scene_24; + + [AssetOpenHandler(FileExtensions.SCENE_FILE_EXTENSION)] + public static async void Open(string path) + { + await EditorWorldManager.LoadWorld(path); + } + + public UIElement? HeaderContent() + { + return new TextBlock + { + Text = Name, + Style = Application.Current.Resources["SubtitleTextBlockStyle"] as Style, + VerticalAlignment = VerticalAlignment.Center + }; + } + + public UIElement? InspectorContent() + { + return null; + } +} \ No newline at end of file diff --git a/Ghost.App/Core/Serializer/WorldNodeSerializer.cs b/Ghost.App/Core/Serializer/WorldNodeSerializer.cs new file mode 100644 index 0000000..5004c76 --- /dev/null +++ b/Ghost.App/Core/Serializer/WorldNodeSerializer.cs @@ -0,0 +1,131 @@ +using Ghost.Editor.Core.SceneGraph; +using Ghost.Engine.Utilities; +using Ghost.Entities; +using Ghost.Entities.Components; +using Ghost.Entities.Utilities; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Ghost.Editor.Core.Serializer; + +internal class WorldNodeSerializer : JsonConverter +{ + private static class Property + { + public const string NAME = "Name"; + public const string ENTITIES = "Entities"; + public const string ID = "ID"; + public const string ENTITY_ID = "EntityID"; + public const string COMPONENTS = "Components"; + public const string DATA = "Data"; + public const string SYSTEMS = "Systems"; + } + + public override bool CanConvert(Type typeToConvert) + { + return typeToConvert == typeof(WorldNode) || typeToConvert.IsSubclassOf(typeof(WorldNode)); + } + + public override WorldNode? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + var element = JsonDocument.ParseValue(ref reader).RootElement; + var name = element.GetProperty(Property.NAME).GetString() ?? "New World"; + + var world = World.Create(); + var result = new WorldNode(world, name); + + foreach (var entityElement in element.GetProperty(Property.ENTITIES).EnumerateArray()) + { + var entityName = entityElement.GetProperty(Property.NAME).GetString() ?? "New Entity"; + var entityID = entityElement.GetProperty(Property.ID).GetInt32(); + var entity = new Entity(entityID, 0); + var node = new EntityNode(entity, entityName); + + world.EntityManager.AddEntityInternal(entity); + result.EntityNodeLookup[entity] = node; + } + + foreach (var componentElement in element.GetProperty(Property.COMPONENTS).EnumerateObject()) + { + var typeName = componentElement.Name; + var type = Type.GetType(typeName) ?? throw new Exception($"Type {typeName} not found."); + + foreach (var dataElement in componentElement.Value.EnumerateArray()) + { + var entityID = dataElement.GetProperty(Property.ENTITY_ID).GetInt32(); + var entity = new Entity(entityID, 0); + + var dataProperty = dataElement.GetProperty(Property.DATA); + var component = JsonSerializer.Deserialize(dataProperty.GetRawText(), type, options); + if (component is IComponentData data) + { + world.EntityManager.AddComponent(entity, data, type); + } + } + } + + foreach (var systemElement in element.GetProperty(Property.SYSTEMS).EnumerateArray()) + { + var typeString = systemElement.GetString(); + if (string.IsNullOrEmpty(typeString)) + { + continue; + } + + var systemType = Type.GetType(typeString); + if (systemType == null) + { + continue; + } + + world.SystemStorage.AddSystem(systemType); + } + + return result; + } + + public override void Write(Utf8JsonWriter writer, WorldNode value, JsonSerializerOptions options) + { + writer.WriteObject(() => + { + writer.WriteString(Property.NAME, value.Name); + writer.WriteArray(Property.ENTITIES, value.World.EntityManager.Entities, entity => + { + if (!entity.IsValid) + { + return; + } + + writer.WriteObject(() => + { + writer.WriteString(Property.NAME, value.EntityNodeLookup[entity].Name); + writer.WriteNumber(Property.ID, entity.ID); + }); + }); + + writer.WriteObject(Property.COMPONENTS, () => + { + foreach (var kvp in value.World.ComponentStorage.ComponentPools) + { + var type = TypeHandle.ToType(kvp.Key) ?? throw new Exception($"Type {kvp.Key} not found."); + var typeName = type.AssemblyQualifiedName ?? type.Name; + + writer.WriteArray(typeName, kvp.Value.Enumerate(), data => + { + writer.WriteObject(() => + { + writer.WriteNumber(Property.ENTITY_ID, data.entity.ID); + writer.WritePropertyName(Property.DATA); + JsonSerializer.Serialize(writer, data.component, type, options); + }); + }); + } + }); + + writer.WriteArray(Property.SYSTEMS, value.World.SystemStorage.Systems, systemType => + { + writer.WriteStringValue(systemType.AssemblyQualifiedName ?? systemType.Name); + }); + }); + } +} \ No newline at end of file diff --git a/Ghost.App/Ghost.App.csproj b/Ghost.App/Ghost.Editor.csproj similarity index 94% rename from Ghost.App/Ghost.App.csproj rename to Ghost.App/Ghost.Editor.csproj index 95fbcf2..e0e9bee 100644 --- a/Ghost.App/Ghost.App.csproj +++ b/Ghost.App/Ghost.Editor.csproj @@ -3,7 +3,6 @@ WinExe net9.0-windows10.0.22621.0 10.0.17763.0 - Ghost.App x86;x64;ARM64 win-x86;win-x64;win-arm64 win-$(Platform).pubxml @@ -42,10 +41,11 @@ - + + @@ -89,7 +89,6 @@ - @@ -107,14 +106,16 @@ MSBuild:Compile - - - ..\..\Class\Misaki.HighPerformance\Misaki.HighPerformance.Unsafe\bin\Release\net9.0\Misaki.HighPerformance.Unsafe.dll + + + MSBuild:Compile + + MSBuild:Compile @@ -140,11 +141,6 @@ MSBuild:Compile - - - MSBuild:Compile - - MSBuild:Compile @@ -160,6 +156,14 @@ MSBuild:Compile + + + + + + MSBuild:Compile + + - + diff --git a/Ghost.App/View/Pages/EngineEditor/ConsolePage.xaml.cs b/Ghost.App/View/Pages/EngineEditor/ConsolePage.xaml.cs index cca9c42..c606188 100644 --- a/Ghost.App/View/Pages/EngineEditor/ConsolePage.xaml.cs +++ b/Ghost.App/View/Pages/EngineEditor/ConsolePage.xaml.cs @@ -1,7 +1,7 @@ using Ghost.Editor.ViewModels.Pages.EngineEditor; using Microsoft.UI.Xaml.Controls; -namespace Ghost.App.View.Pages.EngineEditor; +namespace Ghost.Editor.View.Pages.EngineEditor; internal sealed partial class ConsolePage : Page { @@ -12,8 +12,8 @@ internal sealed partial class ConsolePage : Page public ConsolePage() { - ViewModel = GhostApplication.GetService(); + ViewModel = EditorApplication.GetService(); InitializeComponent(); } -} +} \ No newline at end of file diff --git a/Ghost.App/View/Pages/EngineEditor/HierarchyPage.xaml b/Ghost.App/View/Pages/EngineEditor/HierarchyPage.xaml index 17a1368..777e47e 100644 --- a/Ghost.App/View/Pages/EngineEditor/HierarchyPage.xaml +++ b/Ghost.App/View/Pages/EngineEditor/HierarchyPage.xaml @@ -1,15 +1,16 @@ - - + - + - + - + - + - + diff --git a/Ghost.App/View/Pages/EngineEditor/HierarchyPage.xaml.cs b/Ghost.App/View/Pages/EngineEditor/HierarchyPage.xaml.cs index 9bddc03..76399d3 100644 --- a/Ghost.App/View/Pages/EngineEditor/HierarchyPage.xaml.cs +++ b/Ghost.App/View/Pages/EngineEditor/HierarchyPage.xaml.cs @@ -1,17 +1,17 @@ -using Ghost.Editor.SceneGraph; +using Ghost.Editor.Contracts; +using Ghost.Editor.Controls.Internal; +using Ghost.Editor.Core.SceneGraph; +using Ghost.Editor.Services.Contracts; using Ghost.Editor.ViewModels.Pages.EngineEditor; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; -// To learn more about WinUI, the WinUI project structure, -// and more about our project templates, see: http://aka.ms/winui-project-info. +namespace Ghost.Editor.View.Pages.EngineEditor; -namespace Ghost.App.View.Pages.EngineEditor; -/// -/// An empty page that can be used on its own or navigated to within a Frame. -/// -internal sealed partial class HierarchyPage : Page +internal sealed partial class HierarchyPage : NavigationTabPage { + private readonly IInspectorService _inspectorService; + public HierarchyViewModel ViewModel { get; @@ -19,9 +19,38 @@ internal sealed partial class HierarchyPage : Page public HierarchyPage() { - ViewModel = GhostApplication.GetService(); + _inspectorService = EditorApplication.GetService(); + ViewModel = EditorApplication.GetService(); InitializeComponent(); + + Header = "Hierarchy"; + IconSource = new FontIconSource + { + Glyph = "\uE8A4" + }; + } + + public override void OnNavigatedTo(object? parameter) + { + ViewModel.OnNavigatedTo(parameter); + } + + public override void OnNavigatedFrom() + { + ViewModel.OnNavigatedFrom(); + } + + private void TreeView_SelectionChanged(TreeView sender, TreeViewSelectionChangedEventArgs args) + { + if (args.AddedItems.Count > 0 && args.AddedItems[0] is IInspectable inspectable) + { + _inspectorService.SelectedInspectable = inspectable; + } + else + { + _inspectorService.SelectedInspectable = null; + } } } diff --git a/Ghost.App/View/Pages/EngineEditor/InspectorPage.xaml b/Ghost.App/View/Pages/EngineEditor/InspectorPage.xaml new file mode 100644 index 0000000..3263b23 --- /dev/null +++ b/Ghost.App/View/Pages/EngineEditor/InspectorPage.xaml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Ghost.App/View/Pages/EngineEditor/InspectorPage.xaml.cs b/Ghost.App/View/Pages/EngineEditor/InspectorPage.xaml.cs new file mode 100644 index 0000000..3a1e0c8 --- /dev/null +++ b/Ghost.App/View/Pages/EngineEditor/InspectorPage.xaml.cs @@ -0,0 +1,36 @@ +using Ghost.Editor.Controls.Internal; +using Ghost.Editor.ViewModels.Pages.EngineEditor; +using Microsoft.UI.Xaml.Controls; + +namespace Ghost.Editor.View.Pages.EngineEditor; + +internal sealed partial class InspectorPage : NavigationTabPage +{ + public InspectorViewModel ViewModel + { + get; + } + + public InspectorPage() + { + ViewModel = EditorApplication.GetService(); + + InitializeComponent(); + + Header = "Inspector"; + IconSource = new FontIconSource + { + Glyph = "\uEC7A" + }; + } + + public override void OnNavigatedTo(object? parameter) + { + ViewModel.OnNavigatedTo(parameter); + } + + public override void OnNavigatedFrom() + { + ViewModel.OnNavigatedFrom(); + } +} diff --git a/Ghost.App/View/Pages/EngineEditor/ProjectPage.xaml b/Ghost.App/View/Pages/EngineEditor/ProjectPage.xaml index 8bedf5c..00b792c 100644 --- a/Ghost.App/View/Pages/EngineEditor/ProjectPage.xaml +++ b/Ghost.App/View/Pages/EngineEditor/ProjectPage.xaml @@ -1,13 +1,13 @@ diff --git a/Ghost.App/View/Pages/EngineEditor/ProjectPage.xaml.cs b/Ghost.App/View/Pages/EngineEditor/ProjectPage.xaml.cs index ab7d437..1292b88 100644 --- a/Ghost.App/View/Pages/EngineEditor/ProjectPage.xaml.cs +++ b/Ghost.App/View/Pages/EngineEditor/ProjectPage.xaml.cs @@ -2,7 +2,7 @@ using Ghost.Editor.ViewModels.Pages.EngineEditor; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Input; -namespace Ghost.App.View.Pages.EngineEditor; +namespace Ghost.Editor.View.Pages.EngineEditor; internal sealed partial class ProjectPage : Page { @@ -13,13 +13,13 @@ internal sealed partial class ProjectPage : Page public ProjectPage() { - ViewModel = GhostApplication.GetService(); + ViewModel = EditorApplication.GetService(); InitializeComponent(); } - private async void GridViewItem_DoubleTapped(object sender, DoubleTappedRoutedEventArgs e) + private void GridViewItem_DoubleTapped(object sender, DoubleTappedRoutedEventArgs e) { - await ViewModel.OpenSelected(); + ViewModel.OpenSelected(); } } \ No newline at end of file diff --git a/Ghost.App/View/Pages/Landing/CreateProjectPage.xaml b/Ghost.App/View/Pages/Landing/CreateProjectPage.xaml index 602597e..c448a62 100644 --- a/Ghost.App/View/Pages/Landing/CreateProjectPage.xaml +++ b/Ghost.App/View/Pages/Landing/CreateProjectPage.xaml @@ -1,12 +1,12 @@ diff --git a/Ghost.App/View/Pages/Landing/CreateProjectPage.xaml.cs b/Ghost.App/View/Pages/Landing/CreateProjectPage.xaml.cs index 645f812..c645e4c 100644 --- a/Ghost.App/View/Pages/Landing/CreateProjectPage.xaml.cs +++ b/Ghost.App/View/Pages/Landing/CreateProjectPage.xaml.cs @@ -2,7 +2,7 @@ using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Navigation; -namespace Ghost.App.View.Pages.Landing; +namespace Ghost.Editor.View.Pages.Landing; internal sealed partial class CreateProjectPage : Page { @@ -13,7 +13,7 @@ internal sealed partial class CreateProjectPage : Page public CreateProjectPage() { - ViewModel = GhostApplication.GetService(); + ViewModel = EditorApplication.GetService(); InitializeComponent(); } diff --git a/Ghost.App/View/Pages/Landing/OpenProjectPage.xaml b/Ghost.App/View/Pages/Landing/OpenProjectPage.xaml index 910f6a9..e694d4c 100644 --- a/Ghost.App/View/Pages/Landing/OpenProjectPage.xaml +++ b/Ghost.App/View/Pages/Landing/OpenProjectPage.xaml @@ -1,13 +1,13 @@ diff --git a/Ghost.App/View/Pages/Landing/OpenProjectPage.xaml.cs b/Ghost.App/View/Pages/Landing/OpenProjectPage.xaml.cs index 12f11e7..a6507b1 100644 --- a/Ghost.App/View/Pages/Landing/OpenProjectPage.xaml.cs +++ b/Ghost.App/View/Pages/Landing/OpenProjectPage.xaml.cs @@ -1,11 +1,11 @@ -using Ghost.Data.Models; -using Ghost.Editor.ViewModels.Pages.Landing; +using Ghost.Editor.ViewModels.Pages.Landing; +using Ghost.Data.Models; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Navigation; using Windows.ApplicationModel.DataTransfer; -namespace Ghost.App.View.Pages.Landing; +namespace Ghost.Editor.View.Pages.Landing; internal sealed partial class OpenProjectPage : Page { @@ -16,7 +16,7 @@ internal sealed partial class OpenProjectPage : Page public OpenProjectPage() { - ViewModel = GhostApplication.GetService(); + ViewModel = EditorApplication.GetService(); InitializeComponent(); } diff --git a/Ghost.App/View/Windows/EngineEditorWindow.xaml b/Ghost.App/View/Windows/EngineEditorWindow.xaml index 0eb4763..a5ec2d1 100644 --- a/Ghost.App/View/Windows/EngineEditorWindow.xaml +++ b/Ghost.App/View/Windows/EngineEditorWindow.xaml @@ -1,14 +1,15 @@ - - - - - - - - - - + + + + - - + + @@ -112,17 +108,22 @@ Source="C:\Users\Misaki\OneDrive\Pictures\Screenshots\Screenshot 2024-07-20 021657.png" Stretch="UniformToFill" /> - - + + - + HorizontalAlignment="Stretch" + VerticalAlignment="Stretch"> + + + + - - + + @@ -135,8 +136,8 @@ - - + + diff --git a/Ghost.App/View/Windows/EngineEditorWindow.xaml.cs b/Ghost.App/View/Windows/EngineEditorWindow.xaml.cs index 5770242..88131db 100644 --- a/Ghost.App/View/Windows/EngineEditorWindow.xaml.cs +++ b/Ghost.App/View/Windows/EngineEditorWindow.xaml.cs @@ -1,5 +1,5 @@ -using Ghost.App.Services; -using Ghost.Data.Resources; +using Ghost.Data.Resources; +using Ghost.Editor.Services; using Ghost.Editor.Services.Contracts; using Ghost.Editor.ViewModels.Windows; using Ghost.Engine.Resources; @@ -9,7 +9,7 @@ using WinUIEx; // To learn more about WinUI, the WinUI project structure, // and more about our project templates, see: http://aka.ms/winui-project-info. -namespace Ghost.App.View.Windows; +namespace Ghost.Editor.View.Windows; /// /// An empty window that can be used on its own or navigated to within a Frame. /// @@ -27,10 +27,10 @@ internal sealed partial class EngineEditorWindow : WindowEx public EngineEditorWindow() { - ViewModel = GhostApplication.GetService(); + ViewModel = EditorApplication.GetService(); - _notificationService = (NotificationService)GhostApplication.GetService(); - _progressService = (ProgressService)GhostApplication.GetService(); + _notificationService = (NotificationService)EditorApplication.GetService(); + _progressService = (ProgressService)EditorApplication.GetService(); AppWindow.SetIcon(AssetsPath.s_appIconPath); Title = EngineData.ENGINE_NAME; @@ -46,7 +46,7 @@ internal sealed partial class EngineEditorWindow : WindowEx Bindings.Update(); _editorScope?.Dispose(); - _editorScope = GhostApplication.CreateScope(); + _editorScope = EditorApplication.CreateScope(); _notificationService.SetReference(InfoBar, NotificationQueue); _progressService.SetReference(ProgressBarContainer); diff --git a/Ghost.App/View/Windows/LandingWindow.xaml b/Ghost.App/View/Windows/LandingWindow.xaml index 09cdbcf..c541f9e 100644 --- a/Ghost.App/View/Windows/LandingWindow.xaml +++ b/Ghost.App/View/Windows/LandingWindow.xaml @@ -1,12 +1,12 @@ (); + _notificationService = (NotificationService)EditorApplication.GetService(); AppWindow.SetIcon(AssetsPath.s_appIconPath); Title = EngineData.ENGINE_NAME; @@ -36,7 +36,7 @@ internal sealed partial class LandingWindow : WindowEx private void WindowEx_Activated(object sender, Microsoft.UI.Xaml.WindowActivatedEventArgs args) { _landingScope?.Dispose(); - _landingScope = GhostApplication.CreateScope(); + _landingScope = EditorApplication.CreateScope(); _notificationService.SetReference(InfoBar, NotificationQueue); } diff --git a/Ghost.App/ViewModels/Pages/EngineEditor/HierarchyViewModel.cs b/Ghost.App/ViewModels/Pages/EngineEditor/HierarchyViewModel.cs index f472be7..35aec54 100644 --- a/Ghost.App/ViewModels/Pages/EngineEditor/HierarchyViewModel.cs +++ b/Ghost.App/ViewModels/Pages/EngineEditor/HierarchyViewModel.cs @@ -1,11 +1,11 @@ using CommunityToolkit.Mvvm.ComponentModel; -using Ghost.Editor.SceneGraph; -using System; +using Ghost.Editor.Contracts; +using Ghost.Editor.Core.SceneGraph; using System.Collections.ObjectModel; namespace Ghost.Editor.ViewModels.Pages.EngineEditor; -internal partial class HierarchyViewModel : ObservableObject, IDisposable +internal partial class HierarchyViewModel : ObservableObject, INavigationAware { [ObservableProperty] public partial ObservableCollection SceneList @@ -14,12 +14,6 @@ internal partial class HierarchyViewModel : ObservableObject, IDisposable private set; } = new(EditorWorldManager.LoadedWorlds); - public HierarchyViewModel() - { - EditorWorldManager.OnWorldLoaded += OnWorldLoaded; - EditorWorldManager.OnWorldUnloaded += OnWorldUnloaded; - } - private void OnWorldLoaded(WorldNode node) { SceneList.Add(node); @@ -30,7 +24,13 @@ internal partial class HierarchyViewModel : ObservableObject, IDisposable SceneList.Remove(node); } - public void Dispose() + public void OnNavigatedTo(object? parameter) + { + EditorWorldManager.OnWorldLoaded += OnWorldLoaded; + EditorWorldManager.OnWorldUnloaded += OnWorldUnloaded; + } + + public void OnNavigatedFrom() { EditorWorldManager.OnWorldLoaded -= OnWorldLoaded; EditorWorldManager.OnWorldUnloaded -= OnWorldUnloaded; diff --git a/Ghost.App/ViewModels/Pages/EngineEditor/InspectorViewModel.cs b/Ghost.App/ViewModels/Pages/EngineEditor/InspectorViewModel.cs new file mode 100644 index 0000000..86792af --- /dev/null +++ b/Ghost.App/ViewModels/Pages/EngineEditor/InspectorViewModel.cs @@ -0,0 +1,32 @@ +using CommunityToolkit.Mvvm.ComponentModel; +using Ghost.Editor.Contracts; +using Ghost.Editor.Services.Contracts; + +namespace Ghost.Editor.ViewModels.Pages.EngineEditor; + +internal partial class InspectorViewModel(IInspectorService inspectorService) : ObservableObject, INavigationAware +{ + [ObservableProperty] + public partial IInspectable? Inspectable + { + get; + set; + } + + public void OnNavigatedTo(object? parameter) + { + inspectorService.OnSelectionChanged += OnSelectionChanged; + Inspectable = inspectorService.SelectedInspectable; + } + + public void OnNavigatedFrom() + { + inspectorService.OnSelectionChanged -= OnSelectionChanged; + Inspectable = null; + } + + private void OnSelectionChanged() + { + Inspectable = inspectorService.SelectedInspectable; + } +} \ No newline at end of file diff --git a/Ghost.App/ViewModels/Pages/EngineEditor/ProjectViewModel.cs b/Ghost.App/ViewModels/Pages/EngineEditor/ProjectViewModel.cs index cfa8947..288f0f3 100644 --- a/Ghost.App/ViewModels/Pages/EngineEditor/ProjectViewModel.cs +++ b/Ghost.App/ViewModels/Pages/EngineEditor/ProjectViewModel.cs @@ -1,13 +1,8 @@ using CommunityToolkit.Mvvm.ComponentModel; -using Ghost.App; -using Ghost.App.Models; using Ghost.Data.Services; -using Ghost.Editor.AssetHandle; -using System; -using System.Collections.Generic; +using Ghost.Editor.Core.AssetHandle; +using Ghost.Editor.Models; using System.Collections.ObjectModel; -using System.IO; -using System.Threading.Tasks; namespace Ghost.Editor.ViewModels.Pages.EngineEditor; @@ -96,7 +91,7 @@ internal partial class ProjectViewModel : ObservableObject private void NavigateToDirectory(string? path) { - GhostApplication.Window?.DispatcherQueue.TryEnqueue(async () => + EditorApplication.Window?.DispatcherQueue.TryEnqueue(async () => { DirectoryAssets.Clear(); @@ -121,7 +116,7 @@ internal partial class ProjectViewModel : ObservableObject }); } - public async Task OpenSelected() + public void OpenSelected() { if (SelectedAsset == null) { @@ -134,7 +129,7 @@ internal partial class ProjectViewModel : ObservableObject } else { - await AssetDatabase.OpenAsset(SelectedAsset.FullName); + AssetDatabase.OpenAsset(SelectedAsset.FullName); } } diff --git a/Ghost.App/ViewModels/Pages/Landing/CreateProjectViewModel.cs b/Ghost.App/ViewModels/Pages/Landing/CreateProjectViewModel.cs index fef5e1b..fb6b54d 100644 --- a/Ghost.App/ViewModels/Pages/Landing/CreateProjectViewModel.cs +++ b/Ghost.App/ViewModels/Pages/Landing/CreateProjectViewModel.cs @@ -1,12 +1,12 @@ using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; -using Ghost.App.Contracts; -using Ghost.App.Infrastructures.AppState; -using Ghost.App.Services; -using Ghost.App.Utilities; using Ghost.Data.Models; using Ghost.Data.Services; +using Ghost.Editor.Contracts; +using Ghost.Editor.Core.AppState; using Ghost.Editor.Models; +using Ghost.Editor.Services; +using Ghost.Editor.Utilities; using Ghost.Engine.Resources; using System.Collections.ObjectModel; using System.IO; diff --git a/Ghost.App/ViewModels/Pages/Landing/OpenProjectViewModel.cs b/Ghost.App/ViewModels/Pages/Landing/OpenProjectViewModel.cs index 6a84951..e46c5d7 100644 --- a/Ghost.App/ViewModels/Pages/Landing/OpenProjectViewModel.cs +++ b/Ghost.App/ViewModels/Pages/Landing/OpenProjectViewModel.cs @@ -1,15 +1,12 @@ using CommunityToolkit.Mvvm.ComponentModel; -using Ghost.App.Contracts; -using Ghost.App.Infrastructures.AppState; using Ghost.Data.Models; using Ghost.Data.Services; +using Ghost.Editor.Contracts; +using Ghost.Editor.Core.AppState; using Ghost.Editor.Models; using Ghost.Editor.Services.Contracts; using Microsoft.UI.Xaml; -using System; using System.Collections.ObjectModel; -using System.Linq; -using System.Threading.Tasks; using Windows.ApplicationModel.DataTransfer; using Windows.Storage; diff --git a/Ghost.Data/AssemblyInfo.cs b/Ghost.Data/AssemblyInfo.cs index 1320452..ef8099a 100644 --- a/Ghost.Data/AssemblyInfo.cs +++ b/Ghost.Data/AssemblyInfo.cs @@ -1,3 +1,3 @@ using System.Runtime.CompilerServices; -[assembly: InternalsVisibleTo("Ghost.App")] +[assembly: InternalsVisibleTo("Ghost.Editor")] \ No newline at end of file diff --git a/Ghost.Editor/Models/Asset.cs b/Ghost.Editor/AssetHandle/Asset.cs similarity index 90% rename from Ghost.Editor/Models/Asset.cs rename to Ghost.Editor/AssetHandle/Asset.cs index a49a2f6..22433ab 100644 --- a/Ghost.Editor/Models/Asset.cs +++ b/Ghost.Editor/AssetHandle/Asset.cs @@ -1,4 +1,4 @@ -namespace Ghost.Editor.Models; +namespace Ghost.Editor.AssetHandle; public abstract class Asset { diff --git a/Ghost.Editor/Contracts/IInspectable.cs b/Ghost.Editor/Contracts/IInspectable.cs index 87499fe..f959a81 100644 --- a/Ghost.Editor/Contracts/IInspectable.cs +++ b/Ghost.Editor/Contracts/IInspectable.cs @@ -3,5 +3,6 @@ namespace Ghost.Editor.Contracts; internal interface IInspectable { - public UIElement OnInspectorDraw(); + public UIElement HeaderContent(); + public UIElement InspectorContent(); } \ No newline at end of file diff --git a/Ghost.Editor/SceneGraph/WorldNode.cs b/Ghost.Editor/SceneGraph/WorldNode.cs index d65697d..ddc6be6 100644 --- a/Ghost.Editor/SceneGraph/WorldNode.cs +++ b/Ghost.Editor/SceneGraph/WorldNode.cs @@ -167,7 +167,12 @@ public partial class WorldNode : SceneGraphNode, IEquatable public partial class WorldNode : IInspectable { - public UIElement OnInspectorDraw() + public UIElement HeaderContent() + { + throw new NotImplementedException(); + } + + public UIElement InspectorContent() { throw new NotImplementedException(); } diff --git a/Ghost.Editor/Services/Contracts/IInspectorService.cs b/Ghost.Editor/Services/Contracts/IInspectorService.cs index ce02b28..d2881c6 100644 --- a/Ghost.Editor/Services/Contracts/IInspectorService.cs +++ b/Ghost.Editor/Services/Contracts/IInspectorService.cs @@ -9,4 +9,6 @@ internal interface IInspectorService get; set; } + + public event Action? OnSelectionChanged; } \ No newline at end of file diff --git a/Ghost.Engine/AssemblyInfo.cs b/Ghost.Engine/AssemblyInfo.cs index 1320452..9092fb4 100644 --- a/Ghost.Engine/AssemblyInfo.cs +++ b/Ghost.Engine/AssemblyInfo.cs @@ -1,3 +1,3 @@ using System.Runtime.CompilerServices; -[assembly: InternalsVisibleTo("Ghost.App")] +[assembly: InternalsVisibleTo("Ghost.Editor")] diff --git a/Ghost.Entities/AssemblyInfo.cs b/Ghost.Entities/AssemblyInfo.cs index ef61a5e..6e543db 100644 --- a/Ghost.Entities/AssemblyInfo.cs +++ b/Ghost.Entities/AssemblyInfo.cs @@ -4,7 +4,6 @@ global using WorldID = System.UInt16; using System.Runtime.CompilerServices; -[assembly: InternalsVisibleTo("Ghost.App")] [assembly: InternalsVisibleTo("Ghost.Engine")] [assembly: InternalsVisibleTo("Ghost.Editor")] [assembly: InternalsVisibleTo("Ghost.UnitTest")] \ No newline at end of file diff --git a/Ghost.UnitTest/Ghost.UnitTest.csproj b/Ghost.UnitTest/Ghost.UnitTest.csproj index ff9737f..7922365 100644 --- a/Ghost.UnitTest/Ghost.UnitTest.csproj +++ b/Ghost.UnitTest/Ghost.UnitTest.csproj @@ -49,7 +49,7 @@ - + diff --git a/Ghost.UnitTest/Test/EntityTest.cs b/Ghost.UnitTest/Test/EntityTest.cs index d6048a3..2d44ecc 100644 --- a/Ghost.UnitTest/Test/EntityTest.cs +++ b/Ghost.UnitTest/Test/EntityTest.cs @@ -1,10 +1,10 @@ using Ghost.Entities; using Ghost.Entities.Components; using Ghost.Entities.Systems; -using Ghost.Test.TestFramework; +using Ghost.UnitTest.TestFramework; using System.Numerics; -namespace Ghost.Test; +namespace Ghost.UnitTest; public partial class EntityTest : ITest { diff --git a/Ghost.UnitTest/Test/SerializationTest.cs b/Ghost.UnitTest/Test/SerializationTest.cs index 6dd453f..8346a93 100644 --- a/Ghost.UnitTest/Test/SerializationTest.cs +++ b/Ghost.UnitTest/Test/SerializationTest.cs @@ -1,9 +1,9 @@ -using Ghost.Editor.SceneGraph; +using Ghost.Editor.Core.SceneGraph; using Ghost.Entities; -using Ghost.Test.TestFramework; +using Ghost.UnitTest.TestFramework; using System.Text.Json; -namespace Ghost.Test; +namespace Ghost.UnitTest.Test; internal class SerializationTest : ITest { diff --git a/Ghost.UnitTest/TestFramework/ITest.cs b/Ghost.UnitTest/TestFramework/ITest.cs index 8e86a99..9f14893 100644 --- a/Ghost.UnitTest/TestFramework/ITest.cs +++ b/Ghost.UnitTest/TestFramework/ITest.cs @@ -1,4 +1,4 @@ -namespace Ghost.Test.TestFramework; +namespace Ghost.UnitTest.TestFramework; internal interface ITest { diff --git a/Ghost.UnitTest/TestFramework/TestRunner.cs b/Ghost.UnitTest/TestFramework/TestRunner.cs index 70202e5..601d5c7 100644 --- a/Ghost.UnitTest/TestFramework/TestRunner.cs +++ b/Ghost.UnitTest/TestFramework/TestRunner.cs @@ -1,4 +1,4 @@ -namespace Ghost.Test.TestFramework; +namespace Ghost.UnitTest.TestFramework; internal class TestRunner { diff --git a/Ghost.UnitTest/UnitTestApp.xaml.cs b/Ghost.UnitTest/UnitTestApp.xaml.cs index 4211086..ee34a33 100644 --- a/Ghost.UnitTest/UnitTestApp.xaml.cs +++ b/Ghost.UnitTest/UnitTestApp.xaml.cs @@ -1,21 +1,5 @@ using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Controls.Primitives; -using Microsoft.UI.Xaml.Data; -using Microsoft.UI.Xaml.Input; -using Microsoft.UI.Xaml.Media; -using Microsoft.UI.Xaml.Navigation; -using Microsoft.UI.Xaml.Shapes; using Microsoft.VisualStudio.TestTools.UnitTesting.AppContainer; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices.WindowsRuntime; -using Windows.ApplicationModel; -using Windows.ApplicationModel.Activation; -using Windows.Foundation; -using Windows.Foundation.Collections; // To learn more about WinUI, the WinUI project structure, // and more about our project templates, see: http://aka.ms/winui-project-info. @@ -41,7 +25,7 @@ public partial class UnitTestApp : Application /// Invoked when the application is launched. /// /// Details about the launch request and process. - protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args) + protected override void OnLaunched(LaunchActivatedEventArgs args) { Microsoft.VisualStudio.TestPlatform.TestExecutor.UnitTestClient.CreateDefaultUI(); diff --git a/Ghost.UnitTest/UnitTestAppWindow.xaml.cs b/Ghost.UnitTest/UnitTestAppWindow.xaml.cs index 67bbc8f..c459480 100644 --- a/Ghost.UnitTest/UnitTestAppWindow.xaml.cs +++ b/Ghost.UnitTest/UnitTestAppWindow.xaml.cs @@ -1,17 +1,4 @@ using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls; -using Microsoft.UI.Xaml.Controls.Primitives; -using Microsoft.UI.Xaml.Data; -using Microsoft.UI.Xaml.Input; -using Microsoft.UI.Xaml.Media; -using Microsoft.UI.Xaml.Navigation; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices.WindowsRuntime; -using Windows.Foundation; -using Windows.Foundation.Collections; // To learn more about WinUI, the WinUI project structure, // and more about our project templates, see: http://aka.ms/winui-project-info. diff --git a/Ghost.UnitTest/UnitTests.cs b/Ghost.UnitTest/UnitTests.cs index ddda339..d153e52 100644 --- a/Ghost.UnitTest/UnitTests.cs +++ b/Ghost.UnitTest/UnitTests.cs @@ -1,9 +1,6 @@ using Microsoft.UI.Xaml.Controls; using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting.AppContainer; -using System; -using System.Collections.Generic; -using System.Linq; namespace Ghost.UnitTest; [TestClass] diff --git a/GhostEngine.sln b/GhostEngine.sln index d71f4ce..43bc32d 100644 --- a/GhostEngine.sln +++ b/GhostEngine.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.14.35906.104 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ghost.App", "Ghost.App\Ghost.App.csproj", "{15AFE3A1-0CAF-4B36-8835-121C4D683BBF}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ghost.Editor", "Ghost.App\Ghost.Editor.csproj", "{15AFE3A1-0CAF-4B36-8835-121C4D683BBF}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ghost.Engine", "Ghost.Engine\Ghost.Engine.csproj", "{1ED62E09-8F36-4671-896B-16C1C1530202}" EndProject @@ -15,8 +15,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ghost.Graphics", "Ghost.Gra EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ghost.Generator", "Ghost.Generator\Ghost.Generator.csproj", "{996ABECC-1C5A-4F07-B8AC-D063F91962CB}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ghost.Editor", "Ghost.Editor\Ghost.Editor.csproj", "{CC6E1FCB-A80E-4F6C-B519-C8CADB6D5650}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ghost.UnitTest", "Ghost.UnitTest\Ghost.UnitTest.csproj", "{4179873E-8174-4D17-9584-8C223BA71366}" EndProject Global @@ -107,18 +105,6 @@ Global {996ABECC-1C5A-4F07-B8AC-D063F91962CB}.Release|x64.Build.0 = Release|Any CPU {996ABECC-1C5A-4F07-B8AC-D063F91962CB}.Release|x86.ActiveCfg = Release|Any CPU {996ABECC-1C5A-4F07-B8AC-D063F91962CB}.Release|x86.Build.0 = Release|Any CPU - {CC6E1FCB-A80E-4F6C-B519-C8CADB6D5650}.Debug|ARM64.ActiveCfg = Debug|Any CPU - {CC6E1FCB-A80E-4F6C-B519-C8CADB6D5650}.Debug|ARM64.Build.0 = Debug|Any CPU - {CC6E1FCB-A80E-4F6C-B519-C8CADB6D5650}.Debug|x64.ActiveCfg = Debug|Any CPU - {CC6E1FCB-A80E-4F6C-B519-C8CADB6D5650}.Debug|x64.Build.0 = Debug|Any CPU - {CC6E1FCB-A80E-4F6C-B519-C8CADB6D5650}.Debug|x86.ActiveCfg = Debug|Any CPU - {CC6E1FCB-A80E-4F6C-B519-C8CADB6D5650}.Debug|x86.Build.0 = Debug|Any CPU - {CC6E1FCB-A80E-4F6C-B519-C8CADB6D5650}.Release|ARM64.ActiveCfg = Release|Any CPU - {CC6E1FCB-A80E-4F6C-B519-C8CADB6D5650}.Release|ARM64.Build.0 = Release|Any CPU - {CC6E1FCB-A80E-4F6C-B519-C8CADB6D5650}.Release|x64.ActiveCfg = Release|Any CPU - {CC6E1FCB-A80E-4F6C-B519-C8CADB6D5650}.Release|x64.Build.0 = Release|Any CPU - {CC6E1FCB-A80E-4F6C-B519-C8CADB6D5650}.Release|x86.ActiveCfg = Release|Any CPU - {CC6E1FCB-A80E-4F6C-B519-C8CADB6D5650}.Release|x86.Build.0 = Release|Any CPU {4179873E-8174-4D17-9584-8C223BA71366}.Debug|ARM64.ActiveCfg = Debug|ARM64 {4179873E-8174-4D17-9584-8C223BA71366}.Debug|ARM64.Build.0 = Debug|ARM64 {4179873E-8174-4D17-9584-8C223BA71366}.Debug|ARM64.Deploy.0 = Debug|ARM64 diff --git a/Test/Class1.cs b/Test/Class1.cs new file mode 100644 index 0000000..1329426 --- /dev/null +++ b/Test/Class1.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +// To learn more about WinUI, the WinUI project structure, +// and more about our project templates, see: http://aka.ms/winui-project-info. + +namespace Test; +public partial class Class1 +{ +} diff --git a/Test/Test.csproj b/Test/Test.csproj new file mode 100644 index 0000000..f58207f --- /dev/null +++ b/Test/Test.csproj @@ -0,0 +1,17 @@ + + + net8.0-windows10.0.19041.0 + 10.0.17763.0 + Test + win-x86;win-x64;win-arm64 + true + + + + + + + + + + \ No newline at end of file