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.
This commit is contained in:
2025-06-17 19:37:30 +09:00
parent ff14c0f49a
commit fc44c73ca8
80 changed files with 1244 additions and 309 deletions

View File

@@ -3,7 +3,7 @@ using Ghost.Data.Services;
using Microsoft.UI.Xaml; using Microsoft.UI.Xaml;
using System.IO; using System.IO;
namespace Ghost.App; namespace Ghost.Editor;
internal static class ActivationHandler internal static class ActivationHandler
{ {

View File

@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="utf-8" ?> <?xml version="1.0" encoding="utf-8" ?>
<Application <Application
x:Class="Ghost.App.GhostApplication" x:Class="Ghost.Editor.EditorApplication"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Ghost.App"> xmlns:local="using:Ghost.Editor">
<Application.Resources> <Application.Resources>
<ResourceDictionary> <ResourceDictionary>
<ResourceDictionary.MergedDictionaries> <ResourceDictionary.MergedDictionaries>

View File

@@ -1,31 +1,30 @@
using Ghost.App.Infrastructures.AppState; using Ghost.Editor.Core.AppState;
using Ghost.App.Services; using Ghost.Editor.Services;
using Ghost.App.Utilities;
using Ghost.Editor.Services.Contracts; using Ghost.Editor.Services.Contracts;
using Ghost.Editor.Utilities;
using Ghost.Engine.Services; using Ghost.Engine.Services;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using Microsoft.UI.Xaml; using Microsoft.UI.Xaml;
using System;
// To learn more about WinUI, the WinUI project structure, // To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info. // and more about our project templates, see: http://aka.ms/winui-project-info.
namespace Ghost.App; namespace Ghost.Editor;
/// <summary> /// <summary>
/// Provides application-specific behavior to supplement the default Application class. /// Provides application-specific behavior to supplement the default Application class.
/// </summary> /// </summary>
public partial class GhostApplication : Application public partial class EditorApplication : Application
{ {
private Window? _window; private Window? _window;
internal static Window? Window internal static Window? Window
{ {
get => (Current as GhostApplication)!._window; get => (Current as EditorApplication)!._window;
set set
{ {
if (Current is GhostApplication app) if (Current is EditorApplication app)
{ {
app._window = value; 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 /// 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(). /// executed, and as such is the logical equivalent of main() or WinMain().
/// </summary> /// </summary>
internal GhostApplication() internal EditorApplication()
{ {
InitializeComponent(); InitializeComponent();
@@ -56,6 +55,7 @@ public partial class GhostApplication : Application
services.AddSingleton<AppStateMachine>(); services.AddSingleton<AppStateMachine>();
services.AddSingleton<INotificationService, NotificationService>(); services.AddSingleton<INotificationService, NotificationService>();
services.AddSingleton<IProgressService, ProgressService>(); services.AddSingleton<IProgressService, ProgressService>();
services.AddSingleton<IInspectorService, InspectorService>();
}) })
.Build(); .Build();
@@ -64,12 +64,12 @@ public partial class GhostApplication : Application
internal static IServiceScope CreateScope() internal static IServiceScope CreateScope()
{ {
return (Current as GhostApplication)!.Host.Services.CreateScope(); return (Current as EditorApplication)!.Host.Services.CreateScope();
} }
public static T GetService<T>() where T : class public static T GetService<T>() 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."); throw new ArgumentException($"{typeof(T)} needs to be registered in ConfigureServices within App.xaml.cs.");
} }

View File

@@ -0,0 +1,3 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Ghost.UnitTest")]

View File

@@ -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();
}

View File

@@ -1,4 +1,4 @@
namespace Ghost.App.Contracts; namespace Ghost.Editor.Contracts;
public interface INavigationAware public interface INavigationAware
{ {

View File

@@ -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);
}
}

View File

@@ -1,41 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Ghost.Editor.Controls.Internal">
<Style TargetType="local:InspectorView">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:InspectorView">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<!-- Header -->
<Grid Grid.Row="0">
<ContentPresenter
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Content="{TemplateBinding Header}"
ContentTemplate="{TemplateBinding HeaderTemplate}" />
</Grid>
<!-- Content -->
<Grid Grid.Row="1">
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<ContentPresenter
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
ContentTransitions="{TemplateBinding ContentTransitions}" />
</ScrollViewer>
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>

View File

@@ -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);
}
}
}

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8" ?>
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Ghost.Editor.Controls.Internal" />

View File

@@ -1,9 +1,9 @@
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using Ghost.App.Contracts; using Ghost.Editor.Contracts;
using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Navigation; using Microsoft.UI.Xaml.Navigation;
namespace Ghost.App.Controls; namespace Ghost.Editor.Controls;
public abstract partial class ViewModelPage<VM> : Page public abstract partial class ViewModelPage<VM> : Page
where VM : ObservableObject where VM : ObservableObject

View File

@@ -1,8 +1,4 @@
using System; namespace Ghost.Editor.Core.AppState;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Ghost.App.Infrastructures.AppState;
internal class AppStateMachine internal class AppStateMachine
{ {

View File

@@ -1,13 +1,10 @@
using Ghost.App.View.Windows; using Ghost.Data.Models;
using Ghost.Data.Models;
using Ghost.Data.Services; using Ghost.Data.Services;
using Ghost.Editor; using Ghost.Editor.Core.AssetHandle;
using Ghost.Editor.View.Windows;
using Ghost.Engine; 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 internal class EditorState : IAppState
{ {
@@ -16,9 +13,9 @@ internal class EditorState : IAppState
public Task OnExitingAsync() public Task OnExitingAsync()
{ {
if (GhostApplication.Window == _window) if (EditorApplication.Window == _window)
{ {
GhostApplication.Window = null; EditorApplication.Window = null;
} }
return Task.CompletedTask; return Task.CompletedTask;
} }
@@ -32,13 +29,13 @@ internal class EditorState : IAppState
ProjectService.CurrentProject = metadataInfo; ProjectService.CurrentProject = metadataInfo;
_engineCore = GhostApplication.GetService<EngineCore>(); _engineCore = EditorApplication.GetService<EngineCore>();
await _engineCore.StartAsync(new Engine.Models.LaunchArgument()); await _engineCore.StartAsync(new Engine.Models.LaunchArgument());
_window = GhostApplication.GetService<EngineEditorWindow>(); _window = EditorApplication.GetService<EngineEditorWindow>();
_window.Activate(); _window.Activate();
GhostApplication.Window = _window; EditorApplication.Window = _window;
} }
public async Task OnExitedAsync() public async Task OnExitedAsync()
@@ -48,9 +45,9 @@ internal class EditorState : IAppState
await _engineCore.ShutDownAsync(); await _engineCore.ShutDownAsync();
} }
if (GhostApplication.Window == _window) if (EditorApplication.Window == _window)
{ {
GhostApplication.Window = null; EditorApplication.Window = null;
} }
_window?.Close(); _window?.Close();
@@ -59,7 +56,7 @@ internal class EditorState : IAppState
public Task OnEnteredAsync(object? parameter) public Task OnEnteredAsync(object? parameter)
{ {
EditorApplication.Activate(parameter, ((GhostApplication)(Application.Current)).Host.Services); AssetDatabase.Initialize();
return Task.CompletedTask; return Task.CompletedTask;
} }
} }

View File

@@ -1,6 +1,6 @@
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Ghost.App.Infrastructures.AppState; namespace Ghost.Editor.Core.AppState;
internal interface IAppState internal interface IAppState
{ {

View File

@@ -1,7 +1,8 @@
using Ghost.App.View.Windows; using Ghost.Editor;
using Ghost.Editor.View.Windows;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Ghost.App.Infrastructures.AppState; namespace Ghost.Editor.Core.AppState;
internal class LandingState : IAppState internal class LandingState : IAppState
{ {
@@ -9,17 +10,17 @@ internal class LandingState : IAppState
public Task OnExitingAsync() public Task OnExitingAsync()
{ {
if (GhostApplication.Window == _window) if (EditorApplication.Window == _window)
{ {
GhostApplication.Window = null; EditorApplication.Window = null;
} }
return Task.CompletedTask; return Task.CompletedTask;
} }
public Task OnEnteringAsync(object? parameter) public Task OnEnteringAsync(object? parameter)
{ {
_window = GhostApplication.GetService<LandingWindow>(); _window = EditorApplication.GetService<LandingWindow>();
GhostApplication.Window = _window; EditorApplication.Window = _window;
_window.Activate(); _window.Activate();
return Task.CompletedTask; return Task.CompletedTask;
@@ -27,9 +28,9 @@ internal class LandingState : IAppState
public Task OnExitedAsync() public Task OnExitedAsync()
{ {
if (GhostApplication.Window == _window) if (EditorApplication.Window == _window)
{ {
GhostApplication.Window = null; EditorApplication.Window = null;
} }
_window?.Close(); _window?.Close();

View File

@@ -1,4 +1,4 @@
namespace Ghost.App.Infrastructures.AppState; namespace Ghost.Editor.Core.AppState;
internal enum StateKey internal enum StateKey
{ {

View File

@@ -0,0 +1,24 @@
namespace Ghost.Editor.Core.AssetHandle;
public abstract class Asset
{
/// <summary>
/// Get the Guid of the asset.
/// </summary>
public Guid GUID
{
get;
} = Guid.NewGuid();
/// <summary>
/// True if the asset is a folder, false if it is a file.
/// </summary>
public bool IsFolder
{
get;
}
internal void GenerateMetadata()
{
}
}

View File

@@ -0,0 +1,60 @@
using System.Diagnostics;
using System.Reflection;
namespace Ghost.Editor.Core.AssetHandle;
public static class AssetDatabase
{
private static readonly Dictionary<string, Action<string>> _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<AssetOpenHandlerAttribute>() != null &&
m.GetParameters().Length == 1 &&
m.GetParameters()[0].ParameterType == typeof(string));
foreach (var method in methods)
{
var attr = method.GetCustomAttribute<AssetOpenHandlerAttribute>()!;
var del = (Action<string>)Delegate.CreateDelegate(typeof(Action<string>), 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
});
}
}
}

View File

@@ -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();
}
}

View File

@@ -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<string, WorldNode> _loadedWorlds = new();
public static IEnumerable<WorldNode> LoadedWorlds => _loadedWorlds.Values;
public static event Action<WorldNode>? OnWorldLoaded;
public static event Action<WorldNode>? 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<IProgressService>();
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<WorldNode>(readStream, StaticResource.defaultSerializerOptions) ?? throw new Exception("Deserialization failed.");
_loadedWorlds.Clear();
_loadedWorlds[worldPath] = deserializedScene;
await deserializedScene.LoadAsync();
progressService.HideProgress();
OnWorldLoaded?.Invoke(deserializedScene);
}
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1,112 @@
using Ghost.Engine.Components;
using Ghost.Entities;
namespace Ghost.Editor.Core.SceneGraph;
public class SceneGraphHelpers
{
/// <summary>
/// Creates a new <see cref="EntityNode"/> entity with default components.
/// </summary>
/// <param name="world">The world context where the entity will be created.</param>
/// <param name="entity">The entity to be wrapped in the <see cref="EntityNode"/>.</param>
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);
}
/// <summary>
/// Creates a new <see cref="Entity"/> and <see cref="EntityNode"/> entity with default components.
/// </summary>
/// <param name="world">The world context where the entity will be created.</param>
public static EntityNode CreateEntityNode(World world, string name)
{
var entity = world.EntityManager.CreateEntity();
return CreateEntityNode(world, entity, name);
}
/// <summary>
/// Attaches childEntity to parentEntity in the scene graph.
/// </summary>
/// <param name="world">The world context where the entities exist.</param>
/// <param name="parentNode">The parent entity to which the child will be attached.</param>
/// <param name="childNode">The child entity to be attached.</param>
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<Hierarchy>(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<Hierarchy>(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);
}
/// <summary>
/// Detaches the specified entity from its parent in the scene graph.
/// </summary>
/// <param name="world">The world context where the entities exist.</param>
/// <param name="node">The entity to detach from its parent.</param>
public static void DetachFromParent(WorldNode scene, EntityNode node)
{
var hierarchy = scene.World.EntityManager.GetComponent<Hierarchy>(node.Entity);
var parent = hierarchy.ValueRO.parent;
if (parent == Entity.Invalid)
{
return; // already root
}
var parentHierarchy = scene.World.EntityManager.GetComponent<Hierarchy>(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<Hierarchy>(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);
}
}

View File

@@ -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<SceneGraphNode>? 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];
}
}

View File

@@ -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<WorldNode>
{
private World _world;
private Dictionary<Entity, EntityNode> _entityNodeLookup = new();
public World World => _world;
public Dictionary<Entity, EntityNode> 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<Hierarchy>(entity);
var child = hc.ValueRO.firstChild;
while (child != Entity.Invalid)
{
node.AddChild(BuildNodeRecursive(child, world));
var childHC = world.EntityManager.GetComponent<Hierarchy>(child);
child = childHC.ValueRO.nextSibling;
}
return node;
}
private void BuildGraph()
{
foreach (var (entity, hierarchy) in _world.Query<Hierarchy>())
{
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;
}
}

View File

@@ -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<WorldNode>
{
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);
});
});
}
}

View File

@@ -3,7 +3,6 @@
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<TargetFramework>net9.0-windows10.0.22621.0</TargetFramework> <TargetFramework>net9.0-windows10.0.22621.0</TargetFramework>
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion> <TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
<RootNamespace>Ghost.App</RootNamespace>
<Platforms>x86;x64;ARM64</Platforms> <Platforms>x86;x64;ARM64</Platforms>
<RuntimeIdentifiers>win-x86;win-x64;win-arm64</RuntimeIdentifiers> <RuntimeIdentifiers>win-x86;win-x64;win-arm64</RuntimeIdentifiers>
<PublishProfile>win-$(Platform).pubxml</PublishProfile> <PublishProfile>win-$(Platform).pubxml</PublishProfile>
@@ -42,10 +41,11 @@
<None Remove="Assets\Icon.targetsize-48_altform-unplated.png" /> <None Remove="Assets\Icon.targetsize-48_altform-unplated.png" />
<None Remove="Controls\BasicInput\PropertyField.xaml" /> <None Remove="Controls\BasicInput\PropertyField.xaml" />
<None Remove="Controls\EditorControls.xaml" /> <None Remove="Controls\EditorControls.xaml" />
<None Remove="Controls\Internal\InspectorView.xaml" />
<None Remove="Controls\Internal\InternalControls.xaml" /> <None Remove="Controls\Internal\InternalControls.xaml" />
<None Remove="Controls\Internal\NavigationTabView.xaml" />
<None Remove="View\Pages\EngineEditor\ConsolePage.xaml" /> <None Remove="View\Pages\EngineEditor\ConsolePage.xaml" />
<None Remove="View\Pages\EngineEditor\HierarchyPage.xaml" /> <None Remove="View\Pages\EngineEditor\HierarchyPage.xaml" />
<None Remove="View\Pages\EngineEditor\InspectorPage.xaml" />
<None Remove="View\Pages\EngineEditor\ProjectPage.xaml" /> <None Remove="View\Pages\EngineEditor\ProjectPage.xaml" />
<None Remove="View\Pages\Landing\CreateProjectPage.xaml" /> <None Remove="View\Pages\Landing\CreateProjectPage.xaml" />
<None Remove="View\Pages\Landing\OpenProjectPage.xaml" /> <None Remove="View\Pages\Landing\OpenProjectPage.xaml" />
@@ -89,7 +89,6 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Ghost.Data\Ghost.Data.csproj" /> <ProjectReference Include="..\Ghost.Data\Ghost.Data.csproj" />
<ProjectReference Include="..\Ghost.Editor\Ghost.Editor.csproj" />
<ProjectReference Include="..\Ghost.Engine\Ghost.Engine.csproj" /> <ProjectReference Include="..\Ghost.Engine\Ghost.Engine.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@@ -107,14 +106,16 @@
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
</Page> </Page>
</ItemGroup> </ItemGroup>
<ItemGroup>
<Folder Include="Resources\" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Misaki.HighPerformance.Unsafe"> <Reference Include="Misaki.HighPerformance.Unsafe">
<HintPath>..\..\Class\Misaki.HighPerformance\Misaki.HighPerformance.Unsafe\bin\Release\net9.0\Misaki.HighPerformance.Unsafe.dll</HintPath> <HintPath>..\..\Class\Misaki.HighPerformance\Misaki.HighPerformance.Unsafe\bin\Release\net9.0\Misaki.HighPerformance.Unsafe.dll</HintPath>
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<ItemGroup>
<Page Update="View\Pages\EngineEditor\InspectorPage.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup> <ItemGroup>
<Page Update="View\Pages\EngineEditor\HierarchyPage.xaml"> <Page Update="View\Pages\EngineEditor\HierarchyPage.xaml">
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
@@ -140,11 +141,6 @@
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
</Page> </Page>
</ItemGroup> </ItemGroup>
<ItemGroup>
<Page Update="Controls\Internal\InspectorView.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup> <ItemGroup>
<Page Update="View\Windows\EngineEditorWindow.xaml"> <Page Update="View\Windows\EngineEditorWindow.xaml">
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
@@ -160,6 +156,14 @@
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
</Page> </Page>
</ItemGroup> </ItemGroup>
<ItemGroup>
<Folder Include="Controls\Layout\" />
</ItemGroup>
<ItemGroup>
<Page Update="Controls\Internal\NavigationTabView.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<PropertyGroup Label="Globals" /> <PropertyGroup Label="Globals" />
<!-- <!--
@@ -181,5 +185,7 @@
<ApplicationManifest>app.manifest</ApplicationManifest> <ApplicationManifest>app.manifest</ApplicationManifest>
<PublishAot>False</PublishAot> <PublishAot>False</PublishAot>
<PublishTrimmed>False</PublishTrimmed> <PublishTrimmed>False</PublishTrimmed>
<RootNamespace>Ghost.Editor</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup> </PropertyGroup>
</Project> </Project>

View File

@@ -1,4 +1,4 @@
namespace Ghost.App.Models; namespace Ghost.Editor.Models;
internal struct AssetItem() internal struct AssetItem()
{ {

View File

@@ -1,6 +1,6 @@
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
namespace Ghost.App.Models; namespace Ghost.Editor.Models;
internal class ExplorerItem(string name, string path, bool isDirectory) internal class ExplorerItem(string name, string path, bool isDirectory)
{ {

View File

@@ -0,0 +1,9 @@
namespace Ghost.Editor.Models;
public enum MessageType
{
Informational,
Success,
Warning,
Error
}

View File

@@ -0,0 +1,18 @@
using Microsoft.UI.Xaml.Controls;
namespace Ghost.Editor.Resources;
public static class EditorIconSource
{
public static readonly IconSource scene_24 = new FontIconSource
{
Glyph = "\uF159",
FontSize = 24
};
public static readonly IconSource entity_24 = new FontIconSource
{
Glyph = "\uF158",
FontSize = 24
};
}

View File

@@ -0,0 +1,11 @@
namespace Ghost.Editor.Resources;
internal static class FileExtensions
{
public const string PROJECT_FILE_EXTENSION = ".ghostproj";
public const string TEMPLATE_FILE_EXTENSION = ".ghosttemplate";
public const string SCENE_FILE_EXTENSION = ".ghostscene";
public const string ASSET_FILE_EXTENSION = ".ghostasset";
public const string SHADER_FILE_EXTENSION = ".ghostshader";
public const string MATERIAL_FILE_EXTENSION = ".ghostmaterial";
}

View File

@@ -0,0 +1,14 @@
using Ghost.Editor.Contracts;
namespace Ghost.Editor.Services.Contracts;
internal interface IInspectorService
{
public IInspectable? SelectedInspectable
{
get;
set;
}
public event Action? OnSelectionChanged;
}

View File

@@ -0,0 +1,8 @@
using Ghost.Editor.Models;
namespace Ghost.Editor.Services.Contracts;
public interface INotificationService
{
public void ShowNotification(string? message, MessageType type, int duration = 5, string? title = null);
}

View File

@@ -0,0 +1,9 @@
namespace Ghost.Editor.Services.Contracts;
public interface IProgressService
{
public void ShowProgress(string message, double progress = 0.0);
public void ShowIndeterminateProgress(string message);
public void SetProgress(double progress);
public void HideProgress();
}

View File

@@ -0,0 +1,22 @@
using Ghost.Editor.Contracts;
using Ghost.Editor.Services.Contracts;
namespace Ghost.Editor.Services;
public class InspectorService : IInspectorService
{
public IInspectable? SelectedInspectable
{
get => field;
set
{
if (field != value)
{
field = value;
OnSelectionChanged?.Invoke();
}
}
}
public event Action? OnSelectionChanged;
}

View File

@@ -4,7 +4,7 @@ using Ghost.Editor.Services.Contracts;
using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Controls;
using System; using System;
namespace Ghost.App.Services; namespace Ghost.Editor.Services;
public class NotificationService : INotificationService public class NotificationService : INotificationService
{ {

View File

@@ -4,7 +4,7 @@ using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Controls;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace Ghost.App.Services; namespace Ghost.Editor.Services;
public class ProgressService : IProgressService public class ProgressService : IProgressService
{ {

View File

@@ -2,7 +2,8 @@
<ResourceDictionary <ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:Microsoft.UI.Xaml.Controls"> xmlns:controls="using:Microsoft.UI.Xaml.Controls"
xmlns:internal="using:Ghost.Editor.Controls.Internal">
<ResourceDictionary.ThemeDictionaries> <ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Dark"> <ResourceDictionary x:Key="Dark">
<StaticResource x:Key="TabViewItemHeaderBackgroundSelected" ResourceKey="ControlFillColorSecondaryBrush" /> <StaticResource x:Key="TabViewItemHeaderBackgroundSelected" ResourceKey="ControlFillColorSecondaryBrush" />
@@ -11,7 +12,7 @@
<StaticResource x:Key="TabViewItemHeaderBackgroundSelected" ResourceKey="ControlFillColorSecondaryBrush" /> <StaticResource x:Key="TabViewItemHeaderBackgroundSelected" ResourceKey="ControlFillColorSecondaryBrush" />
</ResourceDictionary> </ResourceDictionary>
</ResourceDictionary.ThemeDictionaries> </ResourceDictionary.ThemeDictionaries>
<Style TargetType="TabView"> <Style TargetType="internal:NavigationTabView">
<Setter Property="TabWidthMode" Value="Compact" /> <Setter Property="TabWidthMode" Value="Compact" />
</Style> </Style>
</ResourceDictionary> </ResourceDictionary>

View File

@@ -2,7 +2,7 @@
using System; using System;
using System.Linq; using System.Linq;
namespace Ghost.App.Utilities; namespace Ghost.Editor.Utilities;
public static class ComponentTypeCache public static class ComponentTypeCache
{ {

View File

@@ -1,7 +1,7 @@
using Ghost.App.View.Pages.EngineEditor; using Ghost.Data.Services;
using Ghost.App.View.Pages.Landing; using Ghost.Editor.View.Pages.EngineEditor;
using Ghost.App.View.Windows; using Ghost.Editor.View.Pages.Landing;
using Ghost.Data.Services; using Ghost.Editor.View.Windows;
using Ghost.Editor.ViewModels.Pages.EngineEditor; using Ghost.Editor.ViewModels.Pages.EngineEditor;
using Ghost.Editor.ViewModels.Pages.Landing; using Ghost.Editor.ViewModels.Pages.Landing;
using Ghost.Editor.ViewModels.Windows; using Ghost.Editor.ViewModels.Windows;
@@ -9,7 +9,7 @@ using Ghost.Engine;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
namespace Ghost.App.Utilities; namespace Ghost.Editor.Utilities;
internal static partial class HostHelper internal static partial class HostHelper
{ {
@@ -41,5 +41,8 @@ internal static partial class HostHelper
services.AddTransient<ConsolePage>(); services.AddTransient<ConsolePage>();
services.AddTransient<ConsoleViewModel>(); services.AddTransient<ConsoleViewModel>();
services.AddTransient<InspectorPage>();
services.AddTransient<InspectorViewModel>();
} }
} }

View File

@@ -1,18 +1,19 @@
using System; using Ghost.Editor;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Windows.Storage; using Windows.Storage;
using Windows.Storage.Pickers; using Windows.Storage.Pickers;
using WinRT.Interop; using WinRT.Interop;
namespace Ghost.App.Utilities; namespace Ghost.Editor.Utilities;
public static class SystemUtilities public static class SystemUtilities
{ {
public static async Task<StorageFolder?> OpenFolderPickerAsync(PickerLocationId startLocation = PickerLocationId.DocumentsLibrary, string settingsIdentifier = "") public static async Task<StorageFolder?> OpenFolderPickerAsync(PickerLocationId startLocation = PickerLocationId.DocumentsLibrary, string settingsIdentifier = "")
{ {
var openPicker = new FolderPicker(); var openPicker = new FolderPicker();
var hWnd = WindowNative.GetWindowHandle(GhostApplication.Window); var hWnd = WindowNative.GetWindowHandle(EditorApplication.Window);
InitializeWithWindow.Initialize(openPicker, hWnd); InitializeWithWindow.Initialize(openPicker, hWnd);
openPicker.SuggestedStartLocation = startLocation; openPicker.SuggestedStartLocation = startLocation;
@@ -26,7 +27,7 @@ public static class SystemUtilities
public static async Task<StorageFile?> OpenFilePickerAsync(PickerLocationId startLocation = PickerLocationId.DocumentsLibrary, string settingsIdentifier = "", params IEnumerable<string> filter) public static async Task<StorageFile?> OpenFilePickerAsync(PickerLocationId startLocation = PickerLocationId.DocumentsLibrary, string settingsIdentifier = "", params IEnumerable<string> filter)
{ {
var openPicker = new FileOpenPicker(); var openPicker = new FileOpenPicker();
var hWnd = WindowNative.GetWindowHandle(GhostApplication.Window); var hWnd = WindowNative.GetWindowHandle(EditorApplication.Window);
InitializeWithWindow.Initialize(openPicker, hWnd); InitializeWithWindow.Initialize(openPicker, hWnd);
openPicker.SuggestedStartLocation = startLocation; openPicker.SuggestedStartLocation = startLocation;

View File

@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="utf-8" ?> <?xml version="1.0" encoding="utf-8" ?>
<Page <Page
x:Class="Ghost.App.View.Pages.EngineEditor.ConsolePage" x:Class="Ghost.Editor.View.Pages.EngineEditor.ConsolePage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:Ghost.App.View.Pages.EngineEditor" xmlns:local="using:Ghost.Editor.View.Pages.EngineEditor"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"> mc:Ignorable="d">
@@ -17,9 +17,10 @@
<!-- Toolbar --> <!-- Toolbar -->
<Grid <Grid
Grid.Row="0" Grid.Row="0"
Background="{ThemeResource CardBackgroundFillColorSecondaryBrush}"
BorderBrush="{ThemeResource CardStrokeColorDefaultSolid}" BorderBrush="{ThemeResource CardStrokeColorDefaultSolid}"
BorderThickness="0,0,0,1"> BorderThickness="0,0,0,1">
<CommandBar Background="{ThemeResource CardBackgroundFillColorSecondaryBrush}" DefaultLabelPosition="Collapsed"> <CommandBar DefaultLabelPosition="Collapsed">
<CommandBar.PrimaryCommands> <CommandBar.PrimaryCommands>
<AppBarButton Command="{x:Bind ViewModel.ClearLogsCommand}" Content="Clear" /> <AppBarButton Command="{x:Bind ViewModel.ClearLogsCommand}" Content="Clear" />
<AppBarSeparator /> <AppBarSeparator />

View File

@@ -1,7 +1,7 @@
using Ghost.Editor.ViewModels.Pages.EngineEditor; using Ghost.Editor.ViewModels.Pages.EngineEditor;
using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Controls;
namespace Ghost.App.View.Pages.EngineEditor; namespace Ghost.Editor.View.Pages.EngineEditor;
internal sealed partial class ConsolePage : Page internal sealed partial class ConsolePage : Page
{ {
@@ -12,8 +12,8 @@ internal sealed partial class ConsolePage : Page
public ConsolePage() public ConsolePage()
{ {
ViewModel = GhostApplication.GetService<ConsoleViewModel>(); ViewModel = EditorApplication.GetService<ConsoleViewModel>();
InitializeComponent(); InitializeComponent();
} }
} }

View File

@@ -1,15 +1,16 @@
<?xml version="1.0" encoding="utf-8" ?> <?xml version="1.0" encoding="utf-8" ?>
<Page <internal:NavigationTabPage
x:Class="Ghost.App.View.Pages.EngineEditor.HierarchyPage" x:Class="Ghost.Editor.View.Pages.EngineEditor.HierarchyPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:Ghost.App.View.Pages.EngineEditor" xmlns:internal="using:Ghost.Editor.Controls.Internal"
xmlns:local="using:Ghost.Editor.View.Pages.EngineEditor"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:sg="using:Ghost.Editor.SceneGraph" xmlns:sg="using:Ghost.Editor.Core.SceneGraph"
mc:Ignorable="d"> mc:Ignorable="d">
<Page.Resources> <internal:NavigationTabPage.Resources>
<DataTemplate x:Key="SceneTemplate" x:DataType="sg:SceneGraphNode"> <DataTemplate x:Key="SceneTemplate" x:DataType="sg:SceneGraphNode">
<TreeViewItem <TreeViewItem
AutomationProperties.Name="{x:Bind Name}" AutomationProperties.Name="{x:Bind Name}"
@@ -25,19 +26,19 @@
<DataTemplate x:Key="EntityTemplate" x:DataType="sg:SceneGraphNode"> <DataTemplate x:Key="EntityTemplate" x:DataType="sg:SceneGraphNode">
<TreeViewItem AutomationProperties.Name="{x:Bind Name}" ItemsSource="{x:Bind Children}"> <TreeViewItem AutomationProperties.Name="{x:Bind Name}" ItemsSource="{x:Bind Children}">
<StackPanel Orientation="Horizontal"> <StackPanel Margin="10,0" Orientation="Horizontal">
<FontIcon FontSize="14" Glyph="&#xF158;" /> <FontIcon FontSize="14" Glyph="&#xF158;" />
<TextBlock Margin="10,0" Text="{x:Bind Name}" /> <TextBlock Margin="5,0,0,0" Text="{x:Bind Name}" />
</StackPanel> </StackPanel>
</TreeViewItem> </TreeViewItem>
</DataTemplate> </DataTemplate>
</Page.Resources> </internal:NavigationTabPage.Resources>
<Grid Padding="4,6" Background="{ThemeResource LayerFillColorDefaultBrush}"> <Grid Padding="4,6" Background="{ThemeResource LayerFillColorDefaultBrush}">
<TreeView ItemsSource="{x:Bind ViewModel.SceneList}"> <TreeView ItemsSource="{x:Bind ViewModel.SceneList}" SelectionChanged="TreeView_SelectionChanged">
<TreeView.ItemTemplateSelector> <TreeView.ItemTemplateSelector>
<local:HierarchyTemplateSector EntityTemplate="{StaticResource EntityTemplate}" WorldTemplate="{StaticResource SceneTemplate}" /> <local:HierarchyTemplateSector EntityTemplate="{StaticResource EntityTemplate}" WorldTemplate="{StaticResource SceneTemplate}" />
</TreeView.ItemTemplateSelector> </TreeView.ItemTemplateSelector>
</TreeView> </TreeView>
</Grid> </Grid>
</Page> </internal:NavigationTabPage>

View File

@@ -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 Ghost.Editor.ViewModels.Pages.EngineEditor;
using Microsoft.UI.Xaml; using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Controls;
// To learn more about WinUI, the WinUI project structure, namespace Ghost.Editor.View.Pages.EngineEditor;
// and more about our project templates, see: http://aka.ms/winui-project-info.
namespace Ghost.App.View.Pages.EngineEditor; internal sealed partial class HierarchyPage : NavigationTabPage
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
internal sealed partial class HierarchyPage : Page
{ {
private readonly IInspectorService _inspectorService;
public HierarchyViewModel ViewModel public HierarchyViewModel ViewModel
{ {
get; get;
@@ -19,9 +19,38 @@ internal sealed partial class HierarchyPage : Page
public HierarchyPage() public HierarchyPage()
{ {
ViewModel = GhostApplication.GetService<HierarchyViewModel>(); _inspectorService = EditorApplication.GetService<IInspectorService>();
ViewModel = EditorApplication.GetService<HierarchyViewModel>();
InitializeComponent(); 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;
}
} }
} }

View File

@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8" ?>
<internal:NavigationTabPage
x:Class="Ghost.Editor.View.Pages.EngineEditor.InspectorPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:internal="using:Ghost.Editor.Controls.Internal"
xmlns:local="using:Ghost.Editor.View.Pages.EngineEditor"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource LayerFillColorDefaultBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="75" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<!-- Header -->
<Grid
Grid.Row="0"
Padding="15,0,10,0"
Background="{ThemeResource CardBackgroundFillColorSecondaryBrush}"
BorderBrush="{ThemeResource CardStrokeColorDefaultSolid}"
BorderThickness="0,0,0,1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<IconSourceElement
Grid.Column="0"
Margin="0,0,15,0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
IconSource="{x:Bind ViewModel.Inspectable.Icon, Mode=OneWay}" />
<ContentPresenter Grid.Column="1" Content="{x:Bind ViewModel.Inspectable.HeaderContent(), Mode=OneWay}" />
</Grid>
<!-- Content -->
<Grid Grid.Row="1" Padding="10,0,10,0">
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<ContentPresenter Content="{x:Bind ViewModel.Inspectable.InspectorContent(), Mode=OneWay}" />
</ScrollViewer>
</Grid>
</Grid>
</internal:NavigationTabPage>

View File

@@ -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<InspectorViewModel>();
InitializeComponent();
Header = "Inspector";
IconSource = new FontIconSource
{
Glyph = "\uEC7A"
};
}
public override void OnNavigatedTo(object? parameter)
{
ViewModel.OnNavigatedTo(parameter);
}
public override void OnNavigatedFrom()
{
ViewModel.OnNavigatedFrom();
}
}

View File

@@ -1,13 +1,13 @@
<?xml version="1.0" encoding="utf-8" ?> <?xml version="1.0" encoding="utf-8" ?>
<Page <Page
x:Class="Ghost.App.View.Pages.EngineEditor.ProjectPage" x:Class="Ghost.Editor.View.Pages.EngineEditor.ProjectPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:converter="using:Ghost.Editor.Utilities.Converters" xmlns:converter="using:Ghost.Editor.Utilities.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:Ghost.App.View.Pages.EngineEditor" xmlns:local="using:Ghost.Editor.View.Pages.EngineEditor"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:model="using:Ghost.App.Models" xmlns:model="using:Ghost.Editor.Models"
mc:Ignorable="d"> mc:Ignorable="d">
<Page.Resources> <Page.Resources>

View File

@@ -2,7 +2,7 @@ using Ghost.Editor.ViewModels.Pages.EngineEditor;
using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Input; using Microsoft.UI.Xaml.Input;
namespace Ghost.App.View.Pages.EngineEditor; namespace Ghost.Editor.View.Pages.EngineEditor;
internal sealed partial class ProjectPage : Page internal sealed partial class ProjectPage : Page
{ {
@@ -13,13 +13,13 @@ internal sealed partial class ProjectPage : Page
public ProjectPage() public ProjectPage()
{ {
ViewModel = GhostApplication.GetService<ProjectViewModel>(); ViewModel = EditorApplication.GetService<ProjectViewModel>();
InitializeComponent(); InitializeComponent();
} }
private async void GridViewItem_DoubleTapped(object sender, DoubleTappedRoutedEventArgs e) private void GridViewItem_DoubleTapped(object sender, DoubleTappedRoutedEventArgs e)
{ {
await ViewModel.OpenSelected(); ViewModel.OpenSelected();
} }
} }

View File

@@ -1,12 +1,12 @@
<?xml version="1.0" encoding="utf-8" ?> <?xml version="1.0" encoding="utf-8" ?>
<Page <Page
x:Class="Ghost.App.View.Pages.Landing.CreateProjectPage" x:Class="Ghost.Editor.View.Pages.Landing.CreateProjectPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:data="using:Ghost.Data.Models" xmlns:data="using:Ghost.Data.Models"
xmlns:editor="using:Ghost.Editor.Controls" xmlns:editor="using:Ghost.Editor.Controls"
xmlns:local="using:Ghost.App.View.Pages.Landing" xmlns:local="using:Ghost.Editor.View.Pages.Landing"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
NavigationCacheMode="Enabled" NavigationCacheMode="Enabled"
mc:Ignorable="d"> mc:Ignorable="d">

View File

@@ -2,7 +2,7 @@
using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Navigation; using Microsoft.UI.Xaml.Navigation;
namespace Ghost.App.View.Pages.Landing; namespace Ghost.Editor.View.Pages.Landing;
internal sealed partial class CreateProjectPage : Page internal sealed partial class CreateProjectPage : Page
{ {
@@ -13,7 +13,7 @@ internal sealed partial class CreateProjectPage : Page
public CreateProjectPage() public CreateProjectPage()
{ {
ViewModel = GhostApplication.GetService<CreateProjectViewModel>(); ViewModel = EditorApplication.GetService<CreateProjectViewModel>();
InitializeComponent(); InitializeComponent();
} }

View File

@@ -1,13 +1,13 @@
<?xml version="1.0" encoding="utf-8" ?> <?xml version="1.0" encoding="utf-8" ?>
<Page <Page
x:Class="Ghost.App.View.Pages.Landing.OpenProjectPage" x:Class="Ghost.Editor.View.Pages.Landing.OpenProjectPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:converters="using:Ghost.Editor.Utilities.Converters" xmlns:converters="using:Ghost.Editor.Utilities.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:data="using:Ghost.Data.Models" xmlns:data="using:Ghost.Data.Models"
xmlns:interactivity="using:Microsoft.Xaml.Interactivity" xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:local="using:Ghost.App.View.Pages.Landing" xmlns:local="using:Ghost.Editor.View.Pages.Landing"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
NavigationCacheMode="Enabled" NavigationCacheMode="Enabled"
mc:Ignorable="d"> mc:Ignorable="d">

View File

@@ -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;
using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Navigation; using Microsoft.UI.Xaml.Navigation;
using Windows.ApplicationModel.DataTransfer; using Windows.ApplicationModel.DataTransfer;
namespace Ghost.App.View.Pages.Landing; namespace Ghost.Editor.View.Pages.Landing;
internal sealed partial class OpenProjectPage : Page internal sealed partial class OpenProjectPage : Page
{ {
@@ -16,7 +16,7 @@ internal sealed partial class OpenProjectPage : Page
public OpenProjectPage() public OpenProjectPage()
{ {
ViewModel = GhostApplication.GetService<OpenProjectViewModel>(); ViewModel = EditorApplication.GetService<OpenProjectViewModel>();
InitializeComponent(); InitializeComponent();
} }

View File

@@ -1,14 +1,15 @@
<?xml version="1.0" encoding="utf-8" ?> <?xml version="1.0" encoding="utf-8" ?>
<winex:WindowEx <winex:WindowEx
x:Class="Ghost.App.View.Windows.EngineEditorWindow" x:Class="Ghost.Editor.View.Windows.EngineEditorWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:behaviors="using:CommunityToolkit.WinUI.Behaviors" xmlns:behaviors="using:CommunityToolkit.WinUI.Behaviors"
xmlns:controls="using:CommunityToolkit.WinUI.Controls" xmlns:controls="using:CommunityToolkit.WinUI.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:ee="using:Ghost.App.View.Pages.EngineEditor" xmlns:ee="using:Ghost.Editor.View.Pages.EngineEditor"
xmlns:interactivity="using:Microsoft.Xaml.Interactivity" xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:local="using:Ghost.App.View.Windows" xmlns:internal="using:Ghost.Editor.Controls.Internal"
xmlns:local="using:Ghost.Editor.View.Windows"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:winex="using:WinUIEx" xmlns:winex="using:WinUIEx"
Activated="WindowEx_Activated" Activated="WindowEx_Activated"
@@ -86,23 +87,18 @@
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<TabView <internal:NavigationTabView
Grid.Column="0" Grid.Column="0"
Width="350" Width="350"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"> VerticalAlignment="Stretch">
<TabView.TabItems> <internal:NavigationTabView.TabItems>
<TabViewItem Header="Hierarchy"> <ee:HierarchyPage />
<TabViewItem.IconSource> </internal:NavigationTabView.TabItems>
<FontIconSource Glyph="&#xE8A4;" /> </internal:NavigationTabView>
</TabViewItem.IconSource>
<ee:HierarchyPage />
</TabViewItem>
</TabView.TabItems>
</TabView>
<TabView Grid.Column="1"> <internal:NavigationTabView Grid.Column="1">
<TabView.TabItems> <internal:NavigationTabView.TabItems>
<TabViewItem Header="Scene"> <TabViewItem Header="Scene">
<TabViewItem.IconSource> <TabViewItem.IconSource>
<FontIconSource Glyph="&#xF159;" /> <FontIconSource Glyph="&#xF159;" />
@@ -112,17 +108,22 @@
Source="C:\Users\Misaki\OneDrive\Pictures\Screenshots\Screenshot 2024-07-20 021657.png" Source="C:\Users\Misaki\OneDrive\Pictures\Screenshots\Screenshot 2024-07-20 021657.png"
Stretch="UniformToFill" /> Stretch="UniformToFill" />
</TabViewItem> </TabViewItem>
</TabView.TabItems> </internal:NavigationTabView.TabItems>
</TabView> </internal:NavigationTabView>
<Grid <internal:NavigationTabView
Grid.Column="2" Grid.Column="2"
Width="350" Width="350"
Background="Bisque" /> HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<internal:NavigationTabView.TabItems>
<ee:InspectorPage />
</internal:NavigationTabView.TabItems>
</internal:NavigationTabView>
</Grid> </Grid>
<TabView Grid.Row="1" Height="350"> <internal:NavigationTabView Grid.Row="1" Height="350">
<TabView.TabItems> <internal:NavigationTabView.TabItems>
<TabViewItem Header="Project"> <TabViewItem Header="Project">
<TabViewItem.IconSource> <TabViewItem.IconSource>
<FontIconSource Glyph="&#xEC50;" /> <FontIconSource Glyph="&#xEC50;" />
@@ -135,8 +136,8 @@
</TabViewItem.IconSource> </TabViewItem.IconSource>
<ee:ConsolePage /> <ee:ConsolePage />
</TabViewItem> </TabViewItem>
</TabView.TabItems> </internal:NavigationTabView.TabItems>
</TabView> </internal:NavigationTabView>
</Grid> </Grid>
<!-- Status Bar --> <!-- Status Bar -->

View File

@@ -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.Services.Contracts;
using Ghost.Editor.ViewModels.Windows; using Ghost.Editor.ViewModels.Windows;
using Ghost.Engine.Resources; using Ghost.Engine.Resources;
@@ -9,7 +9,7 @@ using WinUIEx;
// To learn more about WinUI, the WinUI project structure, // To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info. // and more about our project templates, see: http://aka.ms/winui-project-info.
namespace Ghost.App.View.Windows; namespace Ghost.Editor.View.Windows;
/// <summary> /// <summary>
/// An empty window that can be used on its own or navigated to within a Frame. /// An empty window that can be used on its own or navigated to within a Frame.
/// </summary> /// </summary>
@@ -27,10 +27,10 @@ internal sealed partial class EngineEditorWindow : WindowEx
public EngineEditorWindow() public EngineEditorWindow()
{ {
ViewModel = GhostApplication.GetService<EngineEditorViewModel>(); ViewModel = EditorApplication.GetService<EngineEditorViewModel>();
_notificationService = (NotificationService)GhostApplication.GetService<INotificationService>(); _notificationService = (NotificationService)EditorApplication.GetService<INotificationService>();
_progressService = (ProgressService)GhostApplication.GetService<IProgressService>(); _progressService = (ProgressService)EditorApplication.GetService<IProgressService>();
AppWindow.SetIcon(AssetsPath.s_appIconPath); AppWindow.SetIcon(AssetsPath.s_appIconPath);
Title = EngineData.ENGINE_NAME; Title = EngineData.ENGINE_NAME;
@@ -46,7 +46,7 @@ internal sealed partial class EngineEditorWindow : WindowEx
Bindings.Update(); Bindings.Update();
_editorScope?.Dispose(); _editorScope?.Dispose();
_editorScope = GhostApplication.CreateScope(); _editorScope = EditorApplication.CreateScope();
_notificationService.SetReference(InfoBar, NotificationQueue); _notificationService.SetReference(InfoBar, NotificationQueue);
_progressService.SetReference(ProgressBarContainer); _progressService.SetReference(ProgressBarContainer);

View File

@@ -1,12 +1,12 @@
<?xml version="1.0" encoding="utf-8" ?> <?xml version="1.0" encoding="utf-8" ?>
<winex:WindowEx <winex:WindowEx
x:Class="Ghost.App.View.Windows.LandingWindow" x:Class="Ghost.Editor.View.Windows.LandingWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:behaviors="using:CommunityToolkit.WinUI.Behaviors" xmlns:behaviors="using:CommunityToolkit.WinUI.Behaviors"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:interactivity="using:Microsoft.Xaml.Interactivity" xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:local="using:Ghost.App.View.Windows" xmlns:local="using:Ghost.Editor.View.Windows"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:winex="using:WinUIEx" xmlns:winex="using:WinUIEx"
Activated="WindowEx_Activated" Activated="WindowEx_Activated"

View File

@@ -1,6 +1,6 @@
using Ghost.App.Services; using Ghost.Editor.View.Pages.Landing;
using Ghost.App.View.Pages.Landing;
using Ghost.Data.Resources; using Ghost.Data.Resources;
using Ghost.Editor.Services;
using Ghost.Editor.Services.Contracts; using Ghost.Editor.Services.Contracts;
using Ghost.Engine.Resources; using Ghost.Engine.Resources;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
@@ -8,7 +8,7 @@ using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media.Animation; using Microsoft.UI.Xaml.Media.Animation;
using WinUIEx; using WinUIEx;
namespace Ghost.App.View.Windows; namespace Ghost.Editor.View.Windows;
internal sealed partial class LandingWindow : WindowEx internal sealed partial class LandingWindow : WindowEx
{ {
@@ -20,7 +20,7 @@ internal sealed partial class LandingWindow : WindowEx
public LandingWindow() public LandingWindow()
{ {
_notificationService = (NotificationService)GhostApplication.GetService<INotificationService>(); _notificationService = (NotificationService)EditorApplication.GetService<INotificationService>();
AppWindow.SetIcon(AssetsPath.s_appIconPath); AppWindow.SetIcon(AssetsPath.s_appIconPath);
Title = EngineData.ENGINE_NAME; 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) private void WindowEx_Activated(object sender, Microsoft.UI.Xaml.WindowActivatedEventArgs args)
{ {
_landingScope?.Dispose(); _landingScope?.Dispose();
_landingScope = GhostApplication.CreateScope(); _landingScope = EditorApplication.CreateScope();
_notificationService.SetReference(InfoBar, NotificationQueue); _notificationService.SetReference(InfoBar, NotificationQueue);
} }

View File

@@ -1,11 +1,11 @@
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using Ghost.Editor.SceneGraph; using Ghost.Editor.Contracts;
using System; using Ghost.Editor.Core.SceneGraph;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
namespace Ghost.Editor.ViewModels.Pages.EngineEditor; namespace Ghost.Editor.ViewModels.Pages.EngineEditor;
internal partial class HierarchyViewModel : ObservableObject, IDisposable internal partial class HierarchyViewModel : ObservableObject, INavigationAware
{ {
[ObservableProperty] [ObservableProperty]
public partial ObservableCollection<WorldNode> SceneList public partial ObservableCollection<WorldNode> SceneList
@@ -14,12 +14,6 @@ internal partial class HierarchyViewModel : ObservableObject, IDisposable
private set; private set;
} = new(EditorWorldManager.LoadedWorlds); } = new(EditorWorldManager.LoadedWorlds);
public HierarchyViewModel()
{
EditorWorldManager.OnWorldLoaded += OnWorldLoaded;
EditorWorldManager.OnWorldUnloaded += OnWorldUnloaded;
}
private void OnWorldLoaded(WorldNode node) private void OnWorldLoaded(WorldNode node)
{ {
SceneList.Add(node); SceneList.Add(node);
@@ -30,7 +24,13 @@ internal partial class HierarchyViewModel : ObservableObject, IDisposable
SceneList.Remove(node); SceneList.Remove(node);
} }
public void Dispose() public void OnNavigatedTo(object? parameter)
{
EditorWorldManager.OnWorldLoaded += OnWorldLoaded;
EditorWorldManager.OnWorldUnloaded += OnWorldUnloaded;
}
public void OnNavigatedFrom()
{ {
EditorWorldManager.OnWorldLoaded -= OnWorldLoaded; EditorWorldManager.OnWorldLoaded -= OnWorldLoaded;
EditorWorldManager.OnWorldUnloaded -= OnWorldUnloaded; EditorWorldManager.OnWorldUnloaded -= OnWorldUnloaded;

View File

@@ -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;
}
}

View File

@@ -1,13 +1,8 @@
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using Ghost.App;
using Ghost.App.Models;
using Ghost.Data.Services; using Ghost.Data.Services;
using Ghost.Editor.AssetHandle; using Ghost.Editor.Core.AssetHandle;
using System; using Ghost.Editor.Models;
using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.IO;
using System.Threading.Tasks;
namespace Ghost.Editor.ViewModels.Pages.EngineEditor; namespace Ghost.Editor.ViewModels.Pages.EngineEditor;
@@ -96,7 +91,7 @@ internal partial class ProjectViewModel : ObservableObject
private void NavigateToDirectory(string? path) private void NavigateToDirectory(string? path)
{ {
GhostApplication.Window?.DispatcherQueue.TryEnqueue(async () => EditorApplication.Window?.DispatcherQueue.TryEnqueue(async () =>
{ {
DirectoryAssets.Clear(); DirectoryAssets.Clear();
@@ -121,7 +116,7 @@ internal partial class ProjectViewModel : ObservableObject
}); });
} }
public async Task OpenSelected() public void OpenSelected()
{ {
if (SelectedAsset == null) if (SelectedAsset == null)
{ {
@@ -134,7 +129,7 @@ internal partial class ProjectViewModel : ObservableObject
} }
else else
{ {
await AssetDatabase.OpenAsset(SelectedAsset.FullName); AssetDatabase.OpenAsset(SelectedAsset.FullName);
} }
} }

View File

@@ -1,12 +1,12 @@
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input; 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.Models;
using Ghost.Data.Services; using Ghost.Data.Services;
using Ghost.Editor.Contracts;
using Ghost.Editor.Core.AppState;
using Ghost.Editor.Models; using Ghost.Editor.Models;
using Ghost.Editor.Services;
using Ghost.Editor.Utilities;
using Ghost.Engine.Resources; using Ghost.Engine.Resources;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.IO; using System.IO;

View File

@@ -1,15 +1,12 @@
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using Ghost.App.Contracts;
using Ghost.App.Infrastructures.AppState;
using Ghost.Data.Models; using Ghost.Data.Models;
using Ghost.Data.Services; using Ghost.Data.Services;
using Ghost.Editor.Contracts;
using Ghost.Editor.Core.AppState;
using Ghost.Editor.Models; using Ghost.Editor.Models;
using Ghost.Editor.Services.Contracts; using Ghost.Editor.Services.Contracts;
using Microsoft.UI.Xaml; using Microsoft.UI.Xaml;
using System;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using Windows.ApplicationModel.DataTransfer; using Windows.ApplicationModel.DataTransfer;
using Windows.Storage; using Windows.Storage;

View File

@@ -1,3 +1,3 @@
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Ghost.App")] [assembly: InternalsVisibleTo("Ghost.Editor")]

View File

@@ -1,4 +1,4 @@
namespace Ghost.Editor.Models; namespace Ghost.Editor.AssetHandle;
public abstract class Asset public abstract class Asset
{ {

View File

@@ -3,5 +3,6 @@
namespace Ghost.Editor.Contracts; namespace Ghost.Editor.Contracts;
internal interface IInspectable internal interface IInspectable
{ {
public UIElement OnInspectorDraw(); public UIElement HeaderContent();
public UIElement InspectorContent();
} }

View File

@@ -167,7 +167,12 @@ public partial class WorldNode : SceneGraphNode, IEquatable<WorldNode>
public partial class WorldNode : IInspectable public partial class WorldNode : IInspectable
{ {
public UIElement OnInspectorDraw() public UIElement HeaderContent()
{
throw new NotImplementedException();
}
public UIElement InspectorContent()
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }

View File

@@ -9,4 +9,6 @@ internal interface IInspectorService
get; get;
set; set;
} }
public event Action? OnSelectionChanged;
} }

View File

@@ -1,3 +1,3 @@
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Ghost.App")] [assembly: InternalsVisibleTo("Ghost.Editor")]

View File

@@ -4,7 +4,6 @@ global using WorldID = System.UInt16;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Ghost.App")]
[assembly: InternalsVisibleTo("Ghost.Engine")] [assembly: InternalsVisibleTo("Ghost.Engine")]
[assembly: InternalsVisibleTo("Ghost.Editor")] [assembly: InternalsVisibleTo("Ghost.Editor")]
[assembly: InternalsVisibleTo("Ghost.UnitTest")] [assembly: InternalsVisibleTo("Ghost.UnitTest")]

View File

@@ -49,7 +49,7 @@
<PackageReference Include="MSTest.TestFramework" Version="3.9.1" /> <PackageReference Include="MSTest.TestFramework" Version="3.9.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Ghost.Editor\Ghost.Editor.csproj" /> <ProjectReference Include="..\Ghost.App\Ghost.Editor.csproj" />
<ProjectReference Include="..\Ghost.Engine\Ghost.Engine.csproj" /> <ProjectReference Include="..\Ghost.Engine\Ghost.Engine.csproj" />
<ProjectReference Include="..\Ghost.Entities\Ghost.Entities.csproj" /> <ProjectReference Include="..\Ghost.Entities\Ghost.Entities.csproj" />
</ItemGroup> </ItemGroup>

View File

@@ -1,10 +1,10 @@
using Ghost.Entities; using Ghost.Entities;
using Ghost.Entities.Components; using Ghost.Entities.Components;
using Ghost.Entities.Systems; using Ghost.Entities.Systems;
using Ghost.Test.TestFramework; using Ghost.UnitTest.TestFramework;
using System.Numerics; using System.Numerics;
namespace Ghost.Test; namespace Ghost.UnitTest;
public partial class EntityTest : ITest public partial class EntityTest : ITest
{ {

View File

@@ -1,9 +1,9 @@
using Ghost.Editor.SceneGraph; using Ghost.Editor.Core.SceneGraph;
using Ghost.Entities; using Ghost.Entities;
using Ghost.Test.TestFramework; using Ghost.UnitTest.TestFramework;
using System.Text.Json; using System.Text.Json;
namespace Ghost.Test; namespace Ghost.UnitTest.Test;
internal class SerializationTest : ITest internal class SerializationTest : ITest
{ {

View File

@@ -1,4 +1,4 @@
namespace Ghost.Test.TestFramework; namespace Ghost.UnitTest.TestFramework;
internal interface ITest internal interface ITest
{ {

View File

@@ -1,4 +1,4 @@
namespace Ghost.Test.TestFramework; namespace Ghost.UnitTest.TestFramework;
internal class TestRunner internal class TestRunner
{ {

View File

@@ -1,21 +1,5 @@
using Microsoft.UI.Xaml; 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 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, // To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info. // 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. /// Invoked when the application is launched.
/// </summary> /// </summary>
/// <param name="args">Details about the launch request and process.</param> /// <param name="args">Details about the launch request and process.</param>
protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args) protected override void OnLaunched(LaunchActivatedEventArgs args)
{ {
Microsoft.VisualStudio.TestPlatform.TestExecutor.UnitTestClient.CreateDefaultUI(); Microsoft.VisualStudio.TestPlatform.TestExecutor.UnitTestClient.CreateDefaultUI();

View File

@@ -1,17 +1,4 @@
using Microsoft.UI.Xaml; 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, // To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info. // and more about our project templates, see: http://aka.ms/winui-project-info.

View File

@@ -1,9 +1,6 @@
using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Controls;
using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.VisualStudio.TestTools.UnitTesting.AppContainer; using Microsoft.VisualStudio.TestTools.UnitTesting.AppContainer;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Ghost.UnitTest; namespace Ghost.UnitTest;
[TestClass] [TestClass]

View File

@@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17 # Visual Studio Version 17
VisualStudioVersion = 17.14.35906.104 VisualStudioVersion = 17.14.35906.104
MinimumVisualStudioVersion = 10.0.40219.1 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 EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ghost.Engine", "Ghost.Engine\Ghost.Engine.csproj", "{1ED62E09-8F36-4671-896B-16C1C1530202}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ghost.Engine", "Ghost.Engine\Ghost.Engine.csproj", "{1ED62E09-8F36-4671-896B-16C1C1530202}"
EndProject EndProject
@@ -15,8 +15,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ghost.Graphics", "Ghost.Gra
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ghost.Generator", "Ghost.Generator\Ghost.Generator.csproj", "{996ABECC-1C5A-4F07-B8AC-D063F91962CB}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ghost.Generator", "Ghost.Generator\Ghost.Generator.csproj", "{996ABECC-1C5A-4F07-B8AC-D063F91962CB}"
EndProject 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}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ghost.UnitTest", "Ghost.UnitTest\Ghost.UnitTest.csproj", "{4179873E-8174-4D17-9584-8C223BA71366}"
EndProject EndProject
Global Global
@@ -107,18 +105,6 @@ Global
{996ABECC-1C5A-4F07-B8AC-D063F91962CB}.Release|x64.Build.0 = Release|Any CPU {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.ActiveCfg = Release|Any CPU
{996ABECC-1C5A-4F07-B8AC-D063F91962CB}.Release|x86.Build.0 = 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.ActiveCfg = Debug|ARM64
{4179873E-8174-4D17-9584-8C223BA71366}.Debug|ARM64.Build.0 = Debug|ARM64 {4179873E-8174-4D17-9584-8C223BA71366}.Debug|ARM64.Build.0 = Debug|ARM64
{4179873E-8174-4D17-9584-8C223BA71366}.Debug|ARM64.Deploy.0 = Debug|ARM64 {4179873E-8174-4D17-9584-8C223BA71366}.Debug|ARM64.Deploy.0 = Debug|ARM64

13
Test/Class1.cs Normal file
View File

@@ -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
{
}

17
Test/Test.csproj Normal file
View File

@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0-windows10.0.19041.0</TargetFramework>
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
<RootNamespace>Test</RootNamespace>
<RuntimeIdentifiers>win-x86;win-x64;win-arm64</RuntimeIdentifiers>
<UseWinUI>true</UseWinUI>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.4188" />
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.7.250606001" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Ghost.Editor\Ghost.Editor.csproj" />
</ItemGroup>
</Project>