Refactor project structure and enhance functionality
Changed the project namespace from `Ghost.Editor` to `Ghost.App` across multiple files. Changed the `InternalsVisibleTo` attribute in `AssemblyInfo.cs` to include `Ghost.App`. Changed the `ProjectRepository` class to add new asynchronous methods for retrieving projects by ID, name, and metadata path. Changed the `ProjectService` class to utilize the new asynchronous project loading methods. Changed the `SceneGraph` classes to improve node management and serialization. Changed the `EntityManager` class to enhance entity management with new component handling methods. Added new test classes, `EntityTest` and `SerializationTest`, to ensure reliability in entity and serialization systems. Added the `Ghost.App` project file to establish a modular project structure. Added the `Ghost.Generator` project for automated component serialization code generation. Updated UI components to reflect the new namespace for proper functionality.
28
Ghost.InternalEditor/ActivationHandler.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using Ghost.Data.Resources;
|
||||
using Ghost.Data.Services;
|
||||
using Microsoft.UI.Xaml;
|
||||
using System.IO;
|
||||
|
||||
namespace Ghost.App;
|
||||
|
||||
internal static class ActivationHandler
|
||||
{
|
||||
private static void FolderInitialization()
|
||||
{
|
||||
if (!Directory.Exists(DataPath.s_applicationDataFolder))
|
||||
{
|
||||
Directory.CreateDirectory(DataPath.s_applicationDataFolder);
|
||||
}
|
||||
|
||||
if (!Directory.Exists(DataPath.s_projectTemplateFolder))
|
||||
{
|
||||
Directory.CreateDirectory(DataPath.s_projectTemplateFolder);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Handle(LaunchActivatedEventArgs args)
|
||||
{
|
||||
FolderInitialization();
|
||||
ProjectService.EnsureDefaultTemplate();
|
||||
}
|
||||
}
|
||||
16
Ghost.InternalEditor/App.xaml
Normal file
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Application
|
||||
x:Class="Ghost.App.GhostApplication"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:Ghost.App">
|
||||
<Application.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
|
||||
<XamlControlsResources Source="/Controls/EditorControls.xaml" />
|
||||
<ResourceDictionary Source="/Themes/Override.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
101
Ghost.InternalEditor/App.xaml.cs
Normal file
@@ -0,0 +1,101 @@
|
||||
using Ghost.App.Infrastructures.AppState;
|
||||
using Ghost.App.Services;
|
||||
using Ghost.App.Utilities;
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// Provides application-specific behavior to supplement the default Application class.
|
||||
/// </summary>
|
||||
public partial class GhostApplication : Application
|
||||
{
|
||||
private Window? _window;
|
||||
|
||||
internal static Window? Window
|
||||
{
|
||||
get => (Current as GhostApplication)!._window;
|
||||
set
|
||||
{
|
||||
if (Current is GhostApplication app)
|
||||
{
|
||||
app._window = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal IHost Host
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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().
|
||||
/// </summary>
|
||||
internal GhostApplication()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
Host = Microsoft.Extensions.Hosting.Host.
|
||||
CreateDefaultBuilder().
|
||||
UseContentRoot(AppContext.BaseDirectory).
|
||||
ConfigureServices((context, services) =>
|
||||
{
|
||||
HostHelper.AddLandingScope(context, services);
|
||||
HostHelper.AddEngineScope(context, services);
|
||||
|
||||
services.AddSingleton<AppStateMachine>();
|
||||
services.AddSingleton<StackedNotificationService>();
|
||||
})
|
||||
.Build();
|
||||
|
||||
UnhandledException += App_UnhandledException;
|
||||
}
|
||||
|
||||
internal static IServiceScope CreateScope()
|
||||
{
|
||||
return (Current as GhostApplication)!.Host.Services.CreateScope();
|
||||
}
|
||||
|
||||
public static T GetService<T>() where T : class
|
||||
{
|
||||
if ((Current as GhostApplication)!.Host.Services.GetService(typeof(T)) is not T service)
|
||||
{
|
||||
throw new ArgumentException($"{typeof(T)} needs to be registered in ConfigureServices within App.xaml.cs.");
|
||||
}
|
||||
|
||||
return service;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the application is launched.
|
||||
/// </summary>
|
||||
/// <param name="args">Details about the launch request and process.</param>
|
||||
protected override async void OnLaunched(LaunchActivatedEventArgs args)
|
||||
{
|
||||
base.OnLaunched(args);
|
||||
|
||||
ActivationHandler.Handle(args);
|
||||
|
||||
Host.Start();
|
||||
|
||||
var stateMachine = GetService<AppStateMachine>();
|
||||
stateMachine.RegisterState(StateKey.Landing, () => new LandingState());
|
||||
stateMachine.RegisterState(StateKey.EngineEditor, () => new EditorState());
|
||||
|
||||
await stateMachine.TransitionToAsync(StateKey.Landing);
|
||||
}
|
||||
|
||||
private void App_UnhandledException(object sender, Microsoft.UI.Xaml.UnhandledExceptionEventArgs e)
|
||||
{
|
||||
// TODO: Log and handle exceptions as appropriate.
|
||||
// https://docs.microsoft.com/windows/windows-app-sdk/api/winrt/microsoft.ui.xaml.application.unhandledexception.
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 599 B |
|
After Width: | Height: | Size: 831 B |
|
After Width: | Height: | Size: 5.9 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 580 B |
|
After Width: | Height: | Size: 825 B |
|
After Width: | Height: | Size: 6.0 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
BIN
Ghost.InternalEditor/Assets/Icon.scale-100.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
Ghost.InternalEditor/Assets/Icon.scale-125.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
Ghost.InternalEditor/Assets/Icon.scale-150.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
Ghost.InternalEditor/Assets/Icon.scale-200.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
Ghost.InternalEditor/Assets/Icon.scale-400.png
Normal file
|
After Width: | Height: | Size: 4.4 KiB |
BIN
Ghost.InternalEditor/Assets/Icon.targetsize-16.png
Normal file
|
After Width: | Height: | Size: 433 B |
|
After Width: | Height: | Size: 599 B |
|
After Width: | Height: | Size: 580 B |
BIN
Ghost.InternalEditor/Assets/Icon.targetsize-24.png
Normal file
|
After Width: | Height: | Size: 583 B |
|
After Width: | Height: | Size: 831 B |
|
After Width: | Height: | Size: 825 B |
BIN
Ghost.InternalEditor/Assets/Icon.targetsize-256.png
Normal file
|
After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 5.9 KiB |
|
After Width: | Height: | Size: 6.0 KiB |
BIN
Ghost.InternalEditor/Assets/Icon.targetsize-32.png
Normal file
|
After Width: | Height: | Size: 852 B |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
BIN
Ghost.InternalEditor/Assets/Icon.targetsize-48.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
BIN
Ghost.InternalEditor/Assets/LockScreenLogo.scale-200.png
Normal file
|
After Width: | Height: | Size: 432 B |
BIN
Ghost.InternalEditor/Assets/SplashScreen.scale-200.png
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
Ghost.InternalEditor/Assets/Square150x150Logo.scale-200.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
Ghost.InternalEditor/Assets/StoreLogo.png
Normal file
|
After Width: | Height: | Size: 456 B |
BIN
Ghost.InternalEditor/Assets/Wide310x150Logo.scale-200.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
Ghost.InternalEditor/Assets/icon-256.ico
Normal file
|
After Width: | Height: | Size: 163 KiB |
BIN
Ghost.InternalEditor/Assets/icon-256.png
Normal file
|
After Width: | Height: | Size: 5.1 KiB |
7
Ghost.InternalEditor/Contracts/INavigationAware.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace Ghost.App.Contracts;
|
||||
|
||||
internal interface INavigationAware
|
||||
{
|
||||
public void OnNavigatedTo(object? parameter);
|
||||
public void OnNavigatedFrom();
|
||||
}
|
||||
14
Ghost.InternalEditor/Contracts/INotificationService.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
|
||||
namespace Ghost.App.Contracts;
|
||||
|
||||
internal interface INotificationService
|
||||
{
|
||||
public void ShowNotification(string? message, InfoBarSeverity severity, int duration = 5, string? title = null);
|
||||
}
|
||||
|
||||
internal interface INotificationService<T> : INotificationService
|
||||
{
|
||||
public void Initialize(T notificationQueue);
|
||||
public void ClearQueueReference();
|
||||
}
|
||||
24
Ghost.InternalEditor/Controls/BasicInput/PropertyField.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
|
||||
namespace Ghost.Editor.Controls;
|
||||
|
||||
public sealed partial class PropertyField : ContentControl
|
||||
{
|
||||
public string Label
|
||||
{
|
||||
get => (string)GetValue(LabelProperty);
|
||||
set => SetValue(LabelProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty LabelProperty = DependencyProperty.Register(
|
||||
nameof(Label),
|
||||
typeof(string),
|
||||
typeof(PropertyField),
|
||||
new PropertyMetadata(default(string)));
|
||||
|
||||
public PropertyField()
|
||||
{
|
||||
DefaultStyleKey = typeof(PropertyField);
|
||||
}
|
||||
}
|
||||
33
Ghost.InternalEditor/Controls/BasicInput/PropertyField.xaml
Normal file
@@ -0,0 +1,33 @@
|
||||
<?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">
|
||||
|
||||
<Style TargetType="local:PropertyField">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="local:PropertyField">
|
||||
<Grid Height="32" Margin="2,4">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="125" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
Grid.Column="0"
|
||||
Margin="0,0,0,4"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource BodyTextBlockStyle}"
|
||||
Text="{TemplateBinding Label}"
|
||||
TextTrimming="CharacterEllipsis" />
|
||||
|
||||
<ContentPresenter
|
||||
Grid.Column="1"
|
||||
Content="{TemplateBinding Content}"
|
||||
ContentTemplate="{TemplateBinding ContentTemplate}" />
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
7
Ghost.InternalEditor/Controls/EditorControls.xaml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?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">
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="/Controls/BasicInput/PropertyField.xaml" />
|
||||
<ResourceDictionary Source="/Controls/Internal/InternalControls.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
24
Ghost.InternalEditor/Controls/Internal/InspectorView.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
41
Ghost.InternalEditor/Controls/Internal/InspectorView.xaml
Normal file
@@ -0,0 +1,41 @@
|
||||
<?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>
|
||||
@@ -0,0 +1,4 @@
|
||||
<?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">
|
||||
<ResourceDictionary.MergedDictionaries />
|
||||
</ResourceDictionary>
|
||||
38
Ghost.InternalEditor/Controls/ViewModelPage.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Ghost.App.Contracts;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Navigation;
|
||||
|
||||
namespace Ghost.App.Controls;
|
||||
|
||||
public abstract partial class ViewModelPage<VM> : Page
|
||||
where VM : ObservableObject
|
||||
{
|
||||
public VM ViewModel
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
protected ViewModelPage(VM viewModel)
|
||||
{
|
||||
ViewModel = viewModel;
|
||||
}
|
||||
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedTo(e);
|
||||
if (ViewModel is INavigationAware navigationAware)
|
||||
{
|
||||
navigationAware.OnNavigatedTo(e.Parameter);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnNavigatedFrom(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedFrom(e);
|
||||
if (ViewModel is INavigationAware navigationAware)
|
||||
{
|
||||
navigationAware.OnNavigatedFrom();
|
||||
}
|
||||
}
|
||||
}
|
||||
185
Ghost.InternalEditor/Ghost.App.csproj
Normal file
@@ -0,0 +1,185 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net9.0-windows10.0.22621.0</TargetFramework>
|
||||
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
|
||||
<RootNamespace>Ghost.App</RootNamespace>
|
||||
<Platforms>x86;x64;ARM64</Platforms>
|
||||
<RuntimeIdentifiers>win-x86;win-x64;win-arm64</RuntimeIdentifiers>
|
||||
<PublishProfile>win-$(Platform).pubxml</PublishProfile>
|
||||
<UseWinUI>true</UseWinUI>
|
||||
<EnableMsixTooling>true</EnableMsixTooling>
|
||||
<LangVersion>preview</LangVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Content Remove="Assets\icon-256.png" />
|
||||
<Content Remove="Assets\Icon.altform-lightunplated_targetsize-16.png" />
|
||||
<Content Remove="Assets\Icon.altform-lightunplated_targetsize-24.png" />
|
||||
<Content Remove="Assets\Icon.altform-lightunplated_targetsize-256.png" />
|
||||
<Content Remove="Assets\Icon.altform-lightunplated_targetsize-32.png" />
|
||||
<Content Remove="Assets\Icon.altform-lightunplated_targetsize-48.png" />
|
||||
<Content Remove="Assets\Icon.altform-unplated_targetsize-16.png" />
|
||||
<Content Remove="Assets\Icon.altform-unplated_targetsize-24.png" />
|
||||
<Content Remove="Assets\Icon.altform-unplated_targetsize-256.png" />
|
||||
<Content Remove="Assets\Icon.altform-unplated_targetsize-32.png" />
|
||||
<Content Remove="Assets\Icon.altform-unplated_targetsize-48.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Remove="Assets\Icon.scale-100.png" />
|
||||
<None Remove="Assets\Icon.scale-125.png" />
|
||||
<None Remove="Assets\Icon.scale-150.png" />
|
||||
<None Remove="Assets\Icon.scale-200.png" />
|
||||
<None Remove="Assets\Icon.scale-400.png" />
|
||||
<None Remove="Assets\Icon.targetsize-16.png" />
|
||||
<None Remove="Assets\Icon.targetsize-16_altform-unplated.png" />
|
||||
<None Remove="Assets\Icon.targetsize-24.png" />
|
||||
<None Remove="Assets\Icon.targetsize-24_altform-lightunplated.png" />
|
||||
<None Remove="Assets\Icon.targetsize-256.png" />
|
||||
<None Remove="Assets\Icon.targetsize-256_altform-unplated.png" />
|
||||
<None Remove="Assets\Icon.targetsize-32.png" />
|
||||
<None Remove="Assets\Icon.targetsize-32_altform-lightunplated.png" />
|
||||
<None Remove="Assets\Icon.targetsize-48.png" />
|
||||
<None Remove="Assets\Icon.targetsize-48_altform-unplated.png" />
|
||||
<None Remove="Controls\BasicInput\PropertyField.xaml" />
|
||||
<None Remove="Controls\EditorControls.xaml" />
|
||||
<None Remove="Controls\Internal\InspectorView.xaml" />
|
||||
<None Remove="Controls\Internal\InternalControls.xaml" />
|
||||
<None Remove="View\Pages\EngineEditor\ConsolePage.xaml" />
|
||||
<None Remove="View\Pages\EngineEditor\HierarchyPage.xaml" />
|
||||
<None Remove="View\Pages\EngineEditor\ProjectPage.xaml" />
|
||||
<None Remove="View\Pages\Landing\CreateProjectPage.xaml" />
|
||||
<None Remove="View\Pages\Landing\OpenProjectPage.xaml" />
|
||||
<None Remove="View\Windows\EngineEditorWindow.xaml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Remove="App.xaml" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="Assets\SplashScreen.scale-200.png" />
|
||||
<Content Include="Assets\LockScreenLogo.scale-200.png" />
|
||||
<Content Include="Assets\Square150x150Logo.scale-200.png" />
|
||||
<Content Include="Assets\StoreLogo.png" />
|
||||
<Content Include="Assets\Wide310x150Logo.scale-200.png" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Manifest Include="$(ApplicationManifest)" />
|
||||
</ItemGroup>
|
||||
|
||||
<!--
|
||||
Defining the "Msix" ProjectCapability here allows the Single-project MSIX Packaging
|
||||
Tools extension to be activated for this project even if the Windows App SDK Nuget
|
||||
package has not yet been restored.
|
||||
-->
|
||||
<ItemGroup Condition="'$(DisableMsixProjectCapabilityAddedByProject)'!='true' and '$(EnableMsixTooling)'=='true'">
|
||||
<ProjectCapability Include="Msix" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Behaviors" Version="8.2.250402" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Controls.Primitives" Version="8.2.250402" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Controls.TabbedCommandBar" Version="8.2.250402" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.5" />
|
||||
<PackageReference Include="Microsoft.Windows.CsWinRT" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.4188" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.7.250513003" />
|
||||
<PackageReference Include="System.Private.Uri" Version="4.3.2" />
|
||||
<PackageReference Include="WinUIEx" Version="2.5.1" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ghost.Data\Ghost.Data.csproj" />
|
||||
<ProjectReference Include="..\Ghost.Editor\Ghost.Editor.csproj" />
|
||||
<ProjectReference Include="..\Ghost.Engine\Ghost.Engine.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="View\Pages\Landing\CreateProjectPage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="View\Window\Landing.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="View\Pages\Landing\OpenProjectPage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Resources\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Misaki.HighPerformance.Unsafe">
|
||||
<HintPath>..\..\Class\Misaki.HighPerformance\Misaki.HighPerformance.Unsafe\bin\Release\net9.0\Misaki.HighPerformance.Unsafe.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="View\Pages\EngineEditor\HierarchyPage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="View\Pages\EngineEditor\ProjectPage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="View\Pages\EngineEditor\ConsolePage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Themes\Override.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Controls\Internal\InternalControls.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Controls\Internal\InspectorView.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="View\Windows\EngineEditorWindow.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Controls\BasicInput\PropertyField.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Controls\EditorControls.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals" />
|
||||
|
||||
<!--
|
||||
Defining the "HasPackageAndPublishMenuAddedByProject" property here allows the Solution
|
||||
Explorer "Package and Publish" context menu entry to be enabled for this project even if
|
||||
the Windows App SDK Nuget package has not yet been restored.
|
||||
-->
|
||||
<PropertyGroup Condition="'$(DisableHasPackageAndPublishMenuAddedByProject)'!='true' and '$(EnableMsixTooling)'=='true'">
|
||||
<HasPackageAndPublishMenu>true</HasPackageAndPublishMenu>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Publish Properties -->
|
||||
<PropertyGroup>
|
||||
<PublishReadyToRun Condition="'$(Configuration)' == 'Debug'">False</PublishReadyToRun>
|
||||
<PublishReadyToRun Condition="'$(Configuration)' != 'Debug'">True</PublishReadyToRun>
|
||||
<PublishTrimmed Condition="'$(Configuration)' != 'Debug'">True</PublishTrimmed>
|
||||
<Nullable>enable</Nullable>
|
||||
<SupportedOSPlatformVersion>10.0.20348.0</SupportedOSPlatformVersion>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
<PublishAot>False</PublishAot>
|
||||
<PublishTrimmed>False</PublishTrimmed>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ghost.App.Infrastructures.AppState;
|
||||
|
||||
internal class AppStateMachine
|
||||
{
|
||||
private Dictionary<StateKey, Lazy<IAppState>> s_states = new();
|
||||
private IAppState? s_current;
|
||||
|
||||
public void RegisterState(StateKey key, Func<IAppState> stateFactory)
|
||||
{
|
||||
s_states[key] = new(stateFactory);
|
||||
}
|
||||
|
||||
public async Task TransitionToAsync(StateKey stateKey, object? parameter = null)
|
||||
{
|
||||
var previous = s_current;
|
||||
var next = s_states[stateKey].Value;
|
||||
|
||||
if (previous != null)
|
||||
{
|
||||
await previous.OnExitingAsync();
|
||||
}
|
||||
|
||||
await next.OnEnteringAsync(parameter);
|
||||
|
||||
if (previous != null)
|
||||
{
|
||||
await previous.OnExitedAsync();
|
||||
}
|
||||
|
||||
await next.OnEnteredAsync(parameter);
|
||||
|
||||
s_current = next;
|
||||
}
|
||||
}
|
||||
62
Ghost.InternalEditor/Infrastructures/AppState/EditorState.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
using Ghost.App.View.Windows;
|
||||
using Ghost.Data.Models;
|
||||
using Ghost.Data.Services;
|
||||
using Ghost.Engine;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ghost.App.Infrastructures.AppState;
|
||||
|
||||
internal class EditorState : IAppState
|
||||
{
|
||||
private EngineEditorWindow? _window;
|
||||
private EngineCore? _engineCore;
|
||||
|
||||
public Task OnExitingAsync()
|
||||
{
|
||||
if (GhostApplication.Window == _window)
|
||||
{
|
||||
GhostApplication.Window = null;
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task OnEnteringAsync(object? parameter)
|
||||
{
|
||||
if (parameter is not ProjectMetadataInfo metadataInfo)
|
||||
{
|
||||
throw new ArgumentException("Parameter must be of type ProjectMetadata.", nameof(parameter));
|
||||
}
|
||||
|
||||
ProjectService.CurrentProject = metadataInfo;
|
||||
|
||||
_engineCore = GhostApplication.GetService<EngineCore>();
|
||||
await _engineCore.StartAsync(new Engine.Models.LaunchArgument());
|
||||
|
||||
_window = GhostApplication.GetService<EngineEditorWindow>();
|
||||
_window.Activate();
|
||||
|
||||
GhostApplication.Window = _window;
|
||||
}
|
||||
|
||||
public async Task OnExitedAsync()
|
||||
{
|
||||
if (_engineCore != null)
|
||||
{
|
||||
await _engineCore.ShutDownAsync();
|
||||
}
|
||||
|
||||
if (GhostApplication.Window == _window)
|
||||
{
|
||||
GhostApplication.Window = null;
|
||||
}
|
||||
|
||||
_window?.Close();
|
||||
_window = null;
|
||||
}
|
||||
|
||||
public Task OnEnteredAsync(object? parameter)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
28
Ghost.InternalEditor/Infrastructures/AppState/IAppState.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ghost.App.Infrastructures.AppState;
|
||||
|
||||
internal interface IAppState
|
||||
{
|
||||
/// <summary>
|
||||
/// Called when exiting the state.
|
||||
/// </summary>
|
||||
public Task OnExitingAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Called when entering the state, right after OnEnteringAsync.
|
||||
/// <paramref name="parameter">can be used to pass data into the state, such as a project to load.</summary>
|
||||
/// </summary>
|
||||
public Task OnEnteringAsync(object? parameter);
|
||||
|
||||
/// <summary>
|
||||
/// Called when exiting the state, specifically for pose transitions.
|
||||
/// </summary>
|
||||
public Task OnExitedAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Called when entered the state, specifically after the state has been fully initialized and is ready for interaction.
|
||||
/// </summary>
|
||||
/// <param name="parameter">can be used to pass data into the state, such as a project to load.</param>
|
||||
public Task OnEnteredAsync(object? parameter);
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using Ghost.App.View.Windows;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ghost.App.Infrastructures.AppState;
|
||||
|
||||
internal class LandingState : IAppState
|
||||
{
|
||||
private LandingWindow? _window;
|
||||
|
||||
public Task OnExitingAsync()
|
||||
{
|
||||
if (GhostApplication.Window == _window)
|
||||
{
|
||||
GhostApplication.Window = null;
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task OnEnteringAsync(object? parameter)
|
||||
{
|
||||
_window = GhostApplication.GetService<LandingWindow>();
|
||||
GhostApplication.Window = _window;
|
||||
|
||||
_window.Activate();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task OnExitedAsync()
|
||||
{
|
||||
if (GhostApplication.Window == _window)
|
||||
{
|
||||
GhostApplication.Window = null;
|
||||
}
|
||||
|
||||
_window?.Close();
|
||||
_window = null;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task OnEnteredAsync(object? parameter)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Ghost.App.Infrastructures.AppState;
|
||||
|
||||
internal enum StateKey
|
||||
{
|
||||
None,
|
||||
Landing,
|
||||
EngineEditor,
|
||||
}
|
||||
19
Ghost.InternalEditor/Models/AssetItem.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
namespace Ghost.App.Models;
|
||||
|
||||
internal struct AssetItem()
|
||||
{
|
||||
public string AssetPath
|
||||
{
|
||||
get; set;
|
||||
} = string.Empty;
|
||||
|
||||
public string AssetName
|
||||
{
|
||||
get; set;
|
||||
} = string.Empty;
|
||||
|
||||
public string IconGlyph
|
||||
{
|
||||
get; set;
|
||||
} = string.Empty;
|
||||
}
|
||||
27
Ghost.InternalEditor/Models/ExplorerItem.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Ghost.App.Models;
|
||||
|
||||
internal class ExplorerItem(string name, string path, bool isDirectory)
|
||||
{
|
||||
public string Name
|
||||
{
|
||||
get;
|
||||
} = name;
|
||||
|
||||
public string Path
|
||||
{
|
||||
get;
|
||||
} = path;
|
||||
|
||||
public bool IsDirectory
|
||||
{
|
||||
get;
|
||||
} = isDirectory;
|
||||
|
||||
public ObservableCollection<ExplorerItem>? Children
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
50
Ghost.InternalEditor/Package.appxmanifest
Normal file
@@ -0,0 +1,50 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<Package
|
||||
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
|
||||
xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
|
||||
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
|
||||
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
|
||||
IgnorableNamespaces="uap rescap">
|
||||
|
||||
<Identity
|
||||
Name="4bcf724a-f735-433b-b5c5-4d17b9d38197"
|
||||
Publisher="CN=Misaki"
|
||||
Version="1.0.0.0" />
|
||||
|
||||
<mp:PhoneIdentity PhoneProductId="4bcf724a-f735-433b-b5c5-4d17b9d38197" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
|
||||
|
||||
<Properties>
|
||||
<DisplayName>GhostEngine</DisplayName>
|
||||
<PublisherDisplayName>Misaki</PublisherDisplayName>
|
||||
<Logo>Assets\StoreLogo.png</Logo>
|
||||
</Properties>
|
||||
|
||||
<Dependencies>
|
||||
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
|
||||
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
|
||||
</Dependencies>
|
||||
|
||||
<Resources>
|
||||
<Resource Language="x-generate"/>
|
||||
</Resources>
|
||||
|
||||
<Applications>
|
||||
<Application Id="App"
|
||||
Executable="$targetnametoken$.exe"
|
||||
EntryPoint="$targetentrypoint$">
|
||||
<uap:VisualElements
|
||||
DisplayName="GhostEngine"
|
||||
Description="GhostEngine"
|
||||
Square150x150Logo="Assets\Square150x150Logo.png" Square44x44Logo="Assets\Icon.png" BackgroundColor="transparent">
|
||||
<uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png" >
|
||||
</uap:DefaultTile >
|
||||
<uap:SplashScreen Image="Assets\SplashScreen.png" />
|
||||
</uap:VisualElements>
|
||||
</Application>
|
||||
</Applications>
|
||||
|
||||
<Capabilities>
|
||||
<rescap:Capability Name="runFullTrust" />
|
||||
</Capabilities>
|
||||
</Package>
|
||||
10
Ghost.InternalEditor/Properties/launchSettings.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"profiles": {
|
||||
"Ghost.Editor (Package)": {
|
||||
"commandName": "MsixPackage"
|
||||
},
|
||||
"Ghost.Editor (Unpackaged)": {
|
||||
"commandName": "Project"
|
||||
}
|
||||
}
|
||||
}
|
||||
50
Ghost.InternalEditor/Services/StackedNotificationService.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using CommunityToolkit.WinUI.Behaviors;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using System;
|
||||
|
||||
namespace Ghost.App.Services;
|
||||
|
||||
public class StackedNotificationService
|
||||
{
|
||||
private InfoBar? _infoBar;
|
||||
private StackedNotificationsBehavior? _notificationQueue;
|
||||
|
||||
internal void SetReference(InfoBar infoBar, StackedNotificationsBehavior notificationQueue)
|
||||
{
|
||||
_infoBar = infoBar;
|
||||
_notificationQueue = notificationQueue;
|
||||
}
|
||||
|
||||
internal void ClearReference()
|
||||
{
|
||||
if (_infoBar != null)
|
||||
{
|
||||
_infoBar.IsOpen = false;
|
||||
}
|
||||
_infoBar = null;
|
||||
_notificationQueue = null;
|
||||
}
|
||||
|
||||
public void ShowNotification(string? message, InfoBarSeverity severity, int duration = 5, string? title = null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(message))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var notification = new Notification
|
||||
{
|
||||
Message = message,
|
||||
Severity = severity,
|
||||
Duration = TimeSpan.FromSeconds(duration),
|
||||
Title = title
|
||||
};
|
||||
|
||||
ShowNotification(notification);
|
||||
}
|
||||
|
||||
public void ShowNotification(Notification notification)
|
||||
{
|
||||
_notificationQueue?.Show(notification);
|
||||
}
|
||||
}
|
||||
17
Ghost.InternalEditor/Themes/Override.xaml
Normal file
@@ -0,0 +1,17 @@
|
||||
<?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:controls="using:Microsoft.UI.Xaml.Controls">
|
||||
<ResourceDictionary.ThemeDictionaries>
|
||||
<ResourceDictionary x:Key="Dark">
|
||||
<StaticResource x:Key="TabViewItemHeaderBackgroundSelected" ResourceKey="ControlFillColorSecondaryBrush" />
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary x:Key="Light">
|
||||
<StaticResource x:Key="TabViewItemHeaderBackgroundSelected" ResourceKey="ControlFillColorSecondaryBrush" />
|
||||
</ResourceDictionary>
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
<Style TargetType="TabView">
|
||||
<Setter Property="TabWidthMode" Value="Compact" />
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
30
Ghost.InternalEditor/Utilities/ComponentTypeCache.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using Ghost.Entities;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace Ghost.App.Utilities;
|
||||
|
||||
public static class ComponentTypeCache
|
||||
{
|
||||
private static readonly Type?[][] _componentTypes;
|
||||
|
||||
static ComponentTypeCache()
|
||||
{
|
||||
_componentTypes = new Type[World.WorldCount][];
|
||||
for (var i = 0; i < World.WorldCount; i++)
|
||||
{
|
||||
var world = World.GetWorld(i);
|
||||
var typeHandles = world.ComponentStorage.ComponentPools.Keys;
|
||||
_componentTypes[i] = typeHandles.Select(handle => Type.GetTypeFromHandle(RuntimeTypeHandle.FromIntPtr(handle))).ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
public static Type?[] GetComponentTypes(int worldIndex)
|
||||
{
|
||||
if (worldIndex < 0 || worldIndex >= _componentTypes.Length)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(worldIndex), "Invalid world index.");
|
||||
}
|
||||
return _componentTypes[worldIndex];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using Microsoft.UI.Xaml.Data;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Ghost.Editor.Utilities.Converters;
|
||||
|
||||
public partial class AssetPathToGlyphConverter : IValueConverter
|
||||
{
|
||||
public object? Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
if (value is not string path)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (Directory.Exists(path))
|
||||
{
|
||||
return "\uE8B7";
|
||||
}
|
||||
|
||||
var extension = Path.GetExtension(path).ToLowerInvariant();
|
||||
|
||||
// TODO: Use resource dictionary for icons.
|
||||
return extension switch
|
||||
{
|
||||
".fbx" or ".obj" => "\uF158",
|
||||
".png" or ".jpg" or ".jpeg" or ".gif" or ".bmp" => "\uE91B", // Image icon
|
||||
".mp3" or ".wav" or ".ogg" => "\uE767", // Audio icon
|
||||
".mp4" or ".avi" or ".mkv" => "\uE714", // Video icon
|
||||
".txt" or ".md" => "\uF000", // Text file icon
|
||||
".cs" or ".hlsl" => "\uE943", // Code file icon
|
||||
_ => "\uE8A5", // Default file icon
|
||||
};
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using Microsoft.UI.Xaml.Data;
|
||||
using System;
|
||||
|
||||
namespace Ghost.Editor.Utilities.Converters;
|
||||
|
||||
public partial class GetDirectoryNameConverter : IValueConverter
|
||||
{
|
||||
public object? Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
return value is string path ? System.IO.Path.GetDirectoryName(path) : null;
|
||||
}
|
||||
|
||||
public object? ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
45
Ghost.InternalEditor/Utilities/HostHelpers.Page.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
using Ghost.App.View.Pages.EngineEditor;
|
||||
using Ghost.App.View.Pages.Landing;
|
||||
using Ghost.App.View.Windows;
|
||||
using Ghost.Data.Services;
|
||||
using Ghost.Editor.ViewModels.Pages.EngineEditor;
|
||||
using Ghost.Editor.ViewModels.Pages.Landing;
|
||||
using Ghost.Editor.ViewModels.Windows;
|
||||
using Ghost.Engine;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
namespace Ghost.App.Utilities;
|
||||
|
||||
internal static partial class HostHelper
|
||||
{
|
||||
public static void AddLandingScope(HostBuilderContext context, IServiceCollection services)
|
||||
{
|
||||
services.AddTransient<LandingWindow>();
|
||||
|
||||
services.AddTransient<CreateProjectPage>();
|
||||
services.AddTransient<CreateProjectViewModel>();
|
||||
|
||||
services.AddTransient<OpenProjectPage>();
|
||||
services.AddTransient<OpenProjectViewModel>();
|
||||
|
||||
services.AddTransient<ProjectService>();
|
||||
}
|
||||
|
||||
public static void AddEngineScope(HostBuilderContext context, IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton<EngineCore>();
|
||||
|
||||
services.AddTransient<EngineEditorWindow>();
|
||||
services.AddTransient<EngineEditorViewModel>();
|
||||
|
||||
services.AddTransient<HierarchyPage>();
|
||||
services.AddTransient<HierarchyViewModel>();
|
||||
|
||||
services.AddTransient<ProjectPage>();
|
||||
services.AddTransient<ProjectViewModel>();
|
||||
|
||||
services.AddTransient<ConsolePage>();
|
||||
services.AddTransient<ConsoleViewModel>();
|
||||
}
|
||||
}
|
||||
42
Ghost.InternalEditor/Utilities/SystemUtilities.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Storage;
|
||||
using Windows.Storage.Pickers;
|
||||
using WinRT.Interop;
|
||||
|
||||
namespace Ghost.App.Utilities;
|
||||
|
||||
public static class SystemUtilities
|
||||
{
|
||||
public static async Task<StorageFolder?> OpenFolderPickerAsync(PickerLocationId startLocation = PickerLocationId.DocumentsLibrary, string settingsIdentifier = "")
|
||||
{
|
||||
var openPicker = new FolderPicker();
|
||||
var hWnd = WindowNative.GetWindowHandle(GhostApplication.Window);
|
||||
InitializeWithWindow.Initialize(openPicker, hWnd);
|
||||
|
||||
openPicker.SuggestedStartLocation = startLocation;
|
||||
openPicker.FileTypeFilter.Add("*");
|
||||
openPicker.SettingsIdentifier = settingsIdentifier;
|
||||
|
||||
var folder = await openPicker.PickSingleFolderAsync();
|
||||
return folder;
|
||||
}
|
||||
|
||||
public static async Task<StorageFile?> OpenFilePickerAsync(PickerLocationId startLocation = PickerLocationId.DocumentsLibrary, string settingsIdentifier = "", params IEnumerable<string> filter)
|
||||
{
|
||||
var openPicker = new FileOpenPicker();
|
||||
var hWnd = WindowNative.GetWindowHandle(GhostApplication.Window);
|
||||
InitializeWithWindow.Initialize(openPicker, hWnd);
|
||||
|
||||
openPicker.SuggestedStartLocation = startLocation;
|
||||
openPicker.SettingsIdentifier = settingsIdentifier;
|
||||
foreach (var fileType in filter)
|
||||
{
|
||||
openPicker.FileTypeFilter.Add(fileType);
|
||||
}
|
||||
|
||||
var file = await openPicker.PickSingleFileAsync();
|
||||
return file;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Page
|
||||
x:Class="Ghost.App.View.Pages.EngineEditor.ConsolePage"
|
||||
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:local="using:Ghost.App.View.Pages.EngineEditor"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid Background="{ThemeResource LayerFillColorDefaultBrush}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Toolbar -->
|
||||
<Grid
|
||||
Grid.Row="0"
|
||||
BorderBrush="{ThemeResource CardStrokeColorDefaultSolid}"
|
||||
BorderThickness="0,0,0,1">
|
||||
<CommandBar Background="{ThemeResource CardBackgroundFillColorSecondaryBrush}" DefaultLabelPosition="Collapsed">
|
||||
<CommandBar.PrimaryCommands>
|
||||
<AppBarButton Command="{x:Bind ViewModel.ClearLogsCommand}" Content="Clear" />
|
||||
<AppBarSeparator />
|
||||
<AppBarToggleButton Width="45" IsChecked="{x:Bind ViewModel.ShowInfo, Mode=TwoWay}">
|
||||
<AppBarToggleButton.Icon>
|
||||
<FontIcon Glyph="" />
|
||||
</AppBarToggleButton.Icon>
|
||||
</AppBarToggleButton>
|
||||
<AppBarToggleButton Width="45" IsChecked="{x:Bind ViewModel.ShowWarning, Mode=TwoWay}">
|
||||
<AppBarToggleButton.Icon>
|
||||
<FontIcon Glyph="" />
|
||||
</AppBarToggleButton.Icon>
|
||||
</AppBarToggleButton>
|
||||
<AppBarToggleButton Width="45" IsChecked="{x:Bind ViewModel.ShowError, Mode=TwoWay}">
|
||||
<AppBarToggleButton.Icon>
|
||||
<FontIcon Glyph="" />
|
||||
</AppBarToggleButton.Icon>
|
||||
</AppBarToggleButton>
|
||||
</CommandBar.PrimaryCommands>
|
||||
|
||||
<CommandBar.SecondaryCommands>
|
||||
<AppBarToggleButton BorderThickness="0" Label="Clear On Play" />
|
||||
<AppBarToggleButton
|
||||
BorderThickness="0"
|
||||
IsChecked="{x:Bind ViewModel.ShowStackTrace, Mode=TwoWay}"
|
||||
Label="Show Stack Trace" />
|
||||
</CommandBar.SecondaryCommands>
|
||||
</CommandBar>
|
||||
</Grid>
|
||||
|
||||
<!-- Log Content -->
|
||||
<Grid Grid.Row="1">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="100" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<ListView
|
||||
x:Name="LogListView"
|
||||
Grid.Row="0"
|
||||
ItemsSource="{x:Bind ViewModel.Logs, Mode=OneWay}"
|
||||
SelectedItem="{x:Bind ViewModel.SelectedLog, Mode=TwoWay}" />
|
||||
<Grid
|
||||
Grid.Row="1"
|
||||
Padding="4"
|
||||
BorderBrush="{ThemeResource CardStrokeColorDefaultSolid}"
|
||||
BorderThickness="0,1,0,0">
|
||||
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
|
||||
<TextBlock
|
||||
IsTextSelectionEnabled="True"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{x:Bind ViewModel.SelectedLog.ToStringWithStackTrace(), Mode=OneWay}"
|
||||
TextWrapping="Wrap" />
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Page>
|
||||
@@ -0,0 +1,19 @@
|
||||
using Ghost.Editor.ViewModels.Pages.EngineEditor;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
|
||||
namespace Ghost.App.View.Pages.EngineEditor;
|
||||
|
||||
internal sealed partial class ConsolePage : Page
|
||||
{
|
||||
public ConsoleViewModel ViewModel
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public ConsolePage()
|
||||
{
|
||||
ViewModel = GhostApplication.GetService<ConsoleViewModel>();
|
||||
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Page
|
||||
x:Class="Ghost.App.View.Pages.EngineEditor.HierarchyPage"
|
||||
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:local="using:Ghost.App.View.Pages.EngineEditor"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:sg="using:Ghost.Editor.SceneGraph"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Page.Resources>
|
||||
<DataTemplate x:Key="SceneTemplate" x:DataType="sg:SceneGraphNode">
|
||||
<TreeViewItem
|
||||
AutomationProperties.Name="{x:Bind Name}"
|
||||
Background="{ThemeResource ControlSolidFillColorDefaultBrush}"
|
||||
IsExpanded="True"
|
||||
ItemsSource="{x:Bind Children}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<FontIcon FontSize="14" Glyph="" />
|
||||
<TextBlock Margin="10,0" Text="{x:Bind Name}" />
|
||||
</StackPanel>
|
||||
</TreeViewItem>
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate x:Key="EntityTemplate" x:DataType="sg:SceneGraphNode">
|
||||
<TreeViewItem AutomationProperties.Name="{x:Bind Name}" ItemsSource="{x:Bind Children}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<FontIcon FontSize="14" Glyph="" />
|
||||
<TextBlock Margin="10,0" Text="{x:Bind Name}" />
|
||||
</StackPanel>
|
||||
</TreeViewItem>
|
||||
</DataTemplate>
|
||||
</Page.Resources>
|
||||
|
||||
<Grid Padding="4,6" Background="{ThemeResource LayerFillColorDefaultBrush}">
|
||||
<TreeView ItemsSource="{x:Bind ViewModel.SceneList}">
|
||||
<TreeView.ItemTemplateSelector>
|
||||
<local:HierarchyTemplateSector EntityTemplate="{StaticResource EntityTemplate}" WorldTemplate="{StaticResource SceneTemplate}" />
|
||||
</TreeView.ItemTemplateSelector>
|
||||
</TreeView>
|
||||
</Grid>
|
||||
</Page>
|
||||
@@ -0,0 +1,57 @@
|
||||
using Ghost.Editor.SceneGraph;
|
||||
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.App.View.Pages.EngineEditor;
|
||||
/// <summary>
|
||||
/// An empty page that can be used on its own or navigated to within a Frame.
|
||||
/// </summary>
|
||||
internal sealed partial class HierarchyPage : Page
|
||||
{
|
||||
public HierarchyViewModel ViewModel
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public HierarchyPage()
|
||||
{
|
||||
ViewModel = GhostApplication.GetService<HierarchyViewModel>();
|
||||
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
|
||||
internal partial class HierarchyTemplateSector : DataTemplateSelector
|
||||
{
|
||||
public DataTemplate? WorldTemplate
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public DataTemplate? EntityTemplate
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
protected override DataTemplate SelectTemplateCore(object item)
|
||||
{
|
||||
if (WorldTemplate == null || EntityTemplate == null)
|
||||
{
|
||||
return base.SelectTemplateCore(item);
|
||||
}
|
||||
|
||||
var node = (SceneGraphNode)item;
|
||||
return node.NodeType switch
|
||||
{
|
||||
SceneGraphNodeType.Scene => WorldTemplate,
|
||||
SceneGraphNodeType.Entity => EntityTemplate,
|
||||
_ => base.SelectTemplateCore(item)
|
||||
};
|
||||
}
|
||||
}
|
||||
140
Ghost.InternalEditor/View/Pages/EngineEditor/ProjectPage.xaml
Normal file
@@ -0,0 +1,140 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Page
|
||||
x:Class="Ghost.App.View.Pages.EngineEditor.ProjectPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:converter="using:Ghost.Editor.Utilities.Converters"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:Ghost.App.View.Pages.EngineEditor"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:model="using:Ghost.App.Models"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Page.Resources>
|
||||
<converter:AssetPathToGlyphConverter x:Key="AssetPathToGlyphConverter" />
|
||||
</Page.Resources>
|
||||
|
||||
<Grid Background="{ThemeResource LayerFillColorDefaultBrush}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="250" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Folder Tree View -->
|
||||
<Grid
|
||||
Grid.Column="0"
|
||||
Padding="4"
|
||||
Background="{ThemeResource CardBackgroundFillColorSecondaryBrush}"
|
||||
BorderBrush="{ThemeResource CardStrokeColorDefaultSolid}"
|
||||
BorderThickness="0,0,1,0">
|
||||
<TreeView
|
||||
x:Name="DirectoryTreeView"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
ItemsSource="{x:Bind ViewModel.SubDirectories}"
|
||||
ScrollViewer.HorizontalScrollBarVisibility="Hidden"
|
||||
ScrollViewer.VerticalScrollBarVisibility="Auto"
|
||||
SelectedItem="{x:Bind ViewModel.SelectedDirectory, Mode=TwoWay}">
|
||||
<TreeView.ItemTemplate>
|
||||
<DataTemplate x:DataType="model:ExplorerItem">
|
||||
<TreeViewItem ItemsSource="{x:Bind Children}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<FontIcon
|
||||
VerticalAlignment="Center"
|
||||
FontSize="14"
|
||||
Glyph="" />
|
||||
<TextBlock
|
||||
Margin="8,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{x:Bind Name}"
|
||||
TextTrimming="CharacterEllipsis" />
|
||||
</StackPanel>
|
||||
</TreeViewItem>
|
||||
</DataTemplate>
|
||||
</TreeView.ItemTemplate>
|
||||
</TreeView>
|
||||
</Grid>
|
||||
|
||||
<!-- Files -->
|
||||
<Grid Grid.Column="1">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid
|
||||
Grid.Row="0"
|
||||
Padding="4"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
Background="{ThemeResource CardBackgroundFillColorSecondaryBrush}"
|
||||
BorderBrush="{ThemeResource CardStrokeColorDefaultSolid}"
|
||||
BorderThickness="0,0,0,1">
|
||||
<BreadcrumbBar Height="15" />
|
||||
</Grid>
|
||||
|
||||
<ScrollViewer
|
||||
Grid.Row="1"
|
||||
Padding="8"
|
||||
VerticalAlignment="Stretch"
|
||||
HorizontalScrollBarVisibility="Auto"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
<GridView
|
||||
x:Name="AssetsGridView"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
ItemsSource="{x:Bind ViewModel.DirectoryAssets, Mode=OneWay}"
|
||||
SelectedItem="{x:Bind ViewModel.SelectedAsset, Mode=TwoWay}">
|
||||
<GridView.ItemContainerStyle>
|
||||
<Style BasedOn="{StaticResource DefaultGridViewItemStyle}" TargetType="GridViewItem">
|
||||
<Setter Property="Margin" Value="2" />
|
||||
</Style>
|
||||
</GridView.ItemContainerStyle>
|
||||
|
||||
<GridView.ItemTemplate>
|
||||
<DataTemplate x:DataType="model:ExplorerItem">
|
||||
<Grid
|
||||
Width="100"
|
||||
Height="100"
|
||||
Padding="8"
|
||||
DoubleTapped="GridViewItem_DoubleTapped"
|
||||
IsDoubleTapEnabled="True">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="0.25*" />
|
||||
</Grid.RowDefinitions>
|
||||
<FontIcon FontSize="42" Glyph="{x:Bind Path, Converter={StaticResource AssetPathToGlyphConverter}}" />
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Margin="8,0"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{x:Bind Name}"
|
||||
TextTrimming="CharacterEllipsis" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</GridView.ItemTemplate>
|
||||
</GridView>
|
||||
</ScrollViewer>
|
||||
|
||||
<Grid
|
||||
Grid.Row="2"
|
||||
Padding="4"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
Background="{ThemeResource CardBackgroundFillColorSecondaryBrush}"
|
||||
BorderBrush="{ThemeResource CardStrokeColorDefaultSolid}"
|
||||
BorderThickness="0,1,0,0">
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
HorizontalTextAlignment="Left"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{x:Bind ViewModel.SelectedAsset.Path, Mode=OneWay}"
|
||||
TextTrimming="CharacterEllipsis" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Page>
|
||||
@@ -0,0 +1,25 @@
|
||||
using Ghost.Editor.ViewModels.Pages.EngineEditor;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Input;
|
||||
|
||||
namespace Ghost.App.View.Pages.EngineEditor;
|
||||
|
||||
internal sealed partial class ProjectPage : Page
|
||||
{
|
||||
public ProjectViewModel ViewModel
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public ProjectPage()
|
||||
{
|
||||
ViewModel = GhostApplication.GetService<ProjectViewModel>();
|
||||
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void GridViewItem_DoubleTapped(object sender, DoubleTappedRoutedEventArgs e)
|
||||
{
|
||||
ViewModel.NavigateToSelected();
|
||||
}
|
||||
}
|
||||
142
Ghost.InternalEditor/View/Pages/Landing/CreateProjectPage.xaml
Normal file
@@ -0,0 +1,142 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Page
|
||||
x:Class="Ghost.App.View.Pages.Landing.CreateProjectPage"
|
||||
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:data="using:Ghost.Data.Models"
|
||||
xmlns:editor="using:Ghost.Editor.Controls"
|
||||
xmlns:local="using:Ghost.App.View.Pages.Landing"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
NavigationCacheMode="Enabled"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Template Info -->
|
||||
<Grid Grid.Column="0" Width="300">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
Margin="0,0,0,24"
|
||||
Style="{StaticResource SubtitleTextBlockStyle}"
|
||||
Text="Template" />
|
||||
|
||||
<ListView
|
||||
Grid.Row="1"
|
||||
ItemsSource="{x:Bind ViewModel.templates}"
|
||||
SelectedItem="{x:Bind ViewModel.SelectedTemplate, Mode=TwoWay}">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate x:DataType="data:TemplateData">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<ImageIcon
|
||||
Grid.Column="0"
|
||||
Width="24"
|
||||
Height="24">
|
||||
<ImageIcon.Source>
|
||||
<BitmapImage UriSource="{x:Bind GetIconURI()}" />
|
||||
</ImageIcon.Source>
|
||||
</ImageIcon>
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
Margin="8,0"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Bind Info.Name}" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
</Grid>
|
||||
|
||||
<!-- Project Info -->
|
||||
<Grid
|
||||
Grid.Column="1"
|
||||
Margin="16,0,0,0"
|
||||
Padding="16"
|
||||
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
|
||||
CornerRadius="{StaticResource OverlayCornerRadius}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="300" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid Grid.Row="0" CornerRadius="4">
|
||||
<Image VerticalAlignment="Center" Stretch="UniformToFill">
|
||||
<Image.Source>
|
||||
<BitmapImage UriSource="{x:Bind ViewModel.SelectedTemplate.Value.GetPreviewURI(), Mode=OneWay}" />
|
||||
</Image.Source>
|
||||
</Image>
|
||||
<Grid
|
||||
MaxHeight="100"
|
||||
VerticalAlignment="Bottom"
|
||||
Background="{ThemeResource ControlOnImageFillColorDefaultBrush}">
|
||||
<TextBlock
|
||||
Margin="16"
|
||||
VerticalAlignment="Bottom"
|
||||
Foreground="{ThemeResource TextFillColorTertiaryBrush}"
|
||||
Text="{x:Bind ViewModel.SelectedTemplate.Value.Info.Description, Mode=OneWay}" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<StackPanel Grid.Row="1" Margin="8,0">
|
||||
<TextBlock
|
||||
Margin="0,16,0,8"
|
||||
Style="{StaticResource TitleTextBlockStyle}"
|
||||
Text="{x:Bind ViewModel.SelectedTemplate.Value.Info.Name, Mode=OneWay}" />
|
||||
<TextBlock
|
||||
Margin="0,8,0,16"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Style="{StaticResource SubtitleTextBlockStyle}"
|
||||
Text="Project Settings" />
|
||||
|
||||
<editor:PropertyField Label="Name">
|
||||
<TextBox Text="{x:Bind ViewModel.ProjectName, Mode=TwoWay}" />
|
||||
</editor:PropertyField>
|
||||
<editor:PropertyField Label="Location">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBox
|
||||
Grid.Column="0"
|
||||
IsReadOnly="True"
|
||||
Text="{x:Bind ViewModel.ProjectLocation, Mode=TwoWay}" />
|
||||
<Button
|
||||
Grid.Column="1"
|
||||
Margin="4,0,0,0"
|
||||
VerticalAlignment="Stretch"
|
||||
Command="{x:Bind ViewModel.SelectionProjectLocationCommand}">
|
||||
<FontIcon FontSize="16" Glyph="" />
|
||||
</Button>
|
||||
</Grid>
|
||||
</editor:PropertyField>
|
||||
</StackPanel>
|
||||
|
||||
<Grid Grid.Row="2">
|
||||
<Button
|
||||
Width="150"
|
||||
HorizontalAlignment="Right"
|
||||
Command="{x:Bind ViewModel.CreateProjectCommand}"
|
||||
Content="Create"
|
||||
Style="{ThemeResource AccentButtonStyle}" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
</Grid>
|
||||
</Page>
|
||||
@@ -0,0 +1,26 @@
|
||||
using Ghost.Editor.ViewModels.Pages.Landing;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Navigation;
|
||||
|
||||
namespace Ghost.App.View.Pages.Landing;
|
||||
|
||||
internal sealed partial class CreateProjectPage : Page
|
||||
{
|
||||
public CreateProjectViewModel ViewModel
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public CreateProjectPage()
|
||||
{
|
||||
ViewModel = GhostApplication.GetService<CreateProjectViewModel>();
|
||||
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedTo(e);
|
||||
ViewModel.OnNavigatedTo(e.Parameter);
|
||||
}
|
||||
}
|
||||
165
Ghost.InternalEditor/View/Pages/Landing/OpenProjectPage.xaml
Normal file
@@ -0,0 +1,165 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Page
|
||||
x:Class="Ghost.App.View.Pages.Landing.OpenProjectPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:converters="using:Ghost.Editor.Utilities.Converters"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:data="using:Ghost.Data.Models"
|
||||
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
|
||||
xmlns:local="using:Ghost.App.View.Pages.Landing"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
NavigationCacheMode="Enabled"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Page.Resources>
|
||||
<converters:GetDirectoryNameConverter x:Key="DirNameConverter" />
|
||||
</Page.Resources>
|
||||
|
||||
<Grid x:Name="MainContainer">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid Grid.Row="0" Margin="16,4">
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource SubtitleTextBlockStyle}"
|
||||
Text="Projects" />
|
||||
<AutoSuggestBox
|
||||
Width="300"
|
||||
HorizontalAlignment="Right"
|
||||
PlaceholderText="Search project by name"
|
||||
QueryIcon="Find" />
|
||||
</Grid>
|
||||
|
||||
<!-- Header for the ListView -->
|
||||
<Grid Grid.Row="1" Margin="28,16,45,8">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="200" />
|
||||
<ColumnDefinition Width="165" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock
|
||||
Grid.Column="0"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="NAME" />
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Right"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="LAST OPEN" />
|
||||
<TextBlock
|
||||
Grid.Column="2"
|
||||
HorizontalAlignment="Right"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="ENGINE VERSION" />
|
||||
</Grid>
|
||||
|
||||
<!-- Project ListView -->
|
||||
<Grid
|
||||
Grid.Row="2"
|
||||
Padding="8"
|
||||
AllowDrop="True"
|
||||
DragEnter="ProjectContainer_DragEnter"
|
||||
DragLeave="ProjectContainer_DragLeave"
|
||||
DragOver="ProjectContainer_DragOver"
|
||||
Drop="ProjectContainer_Drop">
|
||||
<ListView
|
||||
Padding="4,8"
|
||||
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
|
||||
CornerRadius="{StaticResource OverlayCornerRadius}"
|
||||
IsItemClickEnabled="True"
|
||||
ItemClick="ListView_ItemClick"
|
||||
ItemsSource="{x:Bind ViewModel.projects}"
|
||||
SelectionMode="None">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate x:DataType="data:ProjectMetadataInfo">
|
||||
<Grid Height="64" Padding="4,8">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="200" />
|
||||
<ColumnDefinition Width="100" />
|
||||
<ColumnDefinition Width="65" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Grid Grid.Column="0" VerticalAlignment="Center">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="16"
|
||||
Style="{StaticResource SubtitleTextBlockStyle}"
|
||||
Text="{x:Bind Metadata.Name}" />
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Margin="0,4,0,0"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{x:Bind Path, Converter={StaticResource DirNameConverter}}" />
|
||||
</Grid>
|
||||
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
Margin="16,4"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Bind Metadata.LastOpened}" />
|
||||
<TextBlock
|
||||
Grid.Column="2"
|
||||
Margin="16,4"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Bind Metadata.EngineVersion}" />
|
||||
<Button
|
||||
Grid.Column="3"
|
||||
HorizontalAlignment="Right"
|
||||
Background="Transparent"
|
||||
BorderThickness="0">
|
||||
<FontIcon Glyph="" />
|
||||
</Button>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
|
||||
<!-- Drag Visual -->
|
||||
<Grid
|
||||
x:Name="DragVisual"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
Background="{ThemeResource CardStrokeColorDefaultBrush}"
|
||||
BorderBrush="{ThemeResource ControlStrongStrokeColorDefaultBrush}"
|
||||
BorderThickness="2"
|
||||
CornerRadius="{StaticResource OverlayCornerRadius}"
|
||||
Visibility="{x:Bind ViewModel.DragVisibility, Mode=OneWay}">
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Style="{StaticResource TitleTextBlockStyle}"
|
||||
Text="Drage Project Folder Here" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<!-- Empty Place Holder -->
|
||||
<Grid
|
||||
x:Name="EmptyPlaceHolder"
|
||||
Grid.Row="2"
|
||||
Visibility="{x:Bind ViewModel.EmptyVisibility, Mode=OneWay}">
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource TitleTextBlockStyle}"
|
||||
Text="No projects found" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Page>
|
||||
@@ -0,0 +1,71 @@
|
||||
using Ghost.Data.Models;
|
||||
using Ghost.Editor.ViewModels.Pages.Landing;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Navigation;
|
||||
using Windows.ApplicationModel.DataTransfer;
|
||||
|
||||
namespace Ghost.App.View.Pages.Landing;
|
||||
|
||||
internal sealed partial class OpenProjectPage : Page
|
||||
{
|
||||
public OpenProjectViewModel ViewModel
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public OpenProjectPage()
|
||||
{
|
||||
ViewModel = GhostApplication.GetService<OpenProjectViewModel>();
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedTo(e);
|
||||
ViewModel.OnNavigatedTo(e.Parameter);
|
||||
}
|
||||
|
||||
override protected void OnNavigatedFrom(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedFrom(e);
|
||||
ViewModel.OnNavigatedFrom();
|
||||
}
|
||||
|
||||
private void ProjectContainer_DragEnter(object sender, DragEventArgs e)
|
||||
{
|
||||
ViewModel.DragVisibility = Visibility.Visible;
|
||||
ViewModel.EmptyVisibility = Visibility.Collapsed;
|
||||
}
|
||||
|
||||
private void ProjectContainer_DragLeave(object sender, DragEventArgs e)
|
||||
{
|
||||
ViewModel.DragVisibility = Visibility.Collapsed;
|
||||
ViewModel.UpdateEmptyPlaceHolderVisibility();
|
||||
}
|
||||
|
||||
private void ProjectContainer_DragOver(object sender, DragEventArgs e)
|
||||
{
|
||||
if (e.DataView.Contains(StandardDataFormats.StorageItems))
|
||||
{
|
||||
e.AcceptedOperation = DataPackageOperation.Link;
|
||||
}
|
||||
else
|
||||
{
|
||||
e.AcceptedOperation = DataPackageOperation.None;
|
||||
}
|
||||
}
|
||||
|
||||
private async void ProjectContainer_Drop(object sender, DragEventArgs e)
|
||||
{
|
||||
await ViewModel.ContentDrop(e.DataView);
|
||||
}
|
||||
|
||||
private async void ListView_ItemClick(object sender, ItemClickEventArgs e)
|
||||
{
|
||||
if (e.ClickedItem is ProjectMetadataInfo project)
|
||||
{
|
||||
await ViewModel.LoadProject(project);
|
||||
}
|
||||
}
|
||||
}
|
||||
196
Ghost.InternalEditor/View/Windows/EngineEditorWindow.xaml
Normal file
@@ -0,0 +1,196 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<winex:WindowEx
|
||||
x:Class="Ghost.App.View.Windows.EngineEditorWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="using:CommunityToolkit.WinUI.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:ee="using:Ghost.App.View.Pages.EngineEditor"
|
||||
xmlns:local="using:Ghost.App.View.Windows"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:winex="using:WinUIEx"
|
||||
Activated="WindowEx_Activated"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Window.SystemBackdrop>
|
||||
<MicaBackdrop />
|
||||
</Window.SystemBackdrop>
|
||||
|
||||
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Titlebar -->
|
||||
<StackPanel
|
||||
Grid.Row="0"
|
||||
Padding="8"
|
||||
Orientation="Horizontal">
|
||||
<ImageIcon
|
||||
Width="24"
|
||||
Height="24"
|
||||
VerticalAlignment="Center"
|
||||
Source="ms-appx:///Assets/Icon.targetsize-32.png" />
|
||||
<TextBlock
|
||||
Margin="8,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{x:Bind ViewModel.engineVersionDescriptor}" />
|
||||
<TextBlock
|
||||
Margin="8,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{x:Bind ViewModel.CurrentProject.Metadata.Name, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- Toolbar -->
|
||||
<Grid Grid.Row="1" Margin="4,4">
|
||||
<controls:TabbedCommandBar>
|
||||
<controls:TabbedCommandBar.MenuItems>
|
||||
<controls:TabbedCommandBarItem Header="Home">
|
||||
<AppBarButton Label="Undo" />
|
||||
<AppBarButton Label="Redo" />
|
||||
<AppBarButton Label="Paste" />
|
||||
</controls:TabbedCommandBarItem>
|
||||
<controls:TabbedCommandBarItem Header="Home">
|
||||
<AppBarButton Label="Undo" />
|
||||
<AppBarButton Label="Redo" />
|
||||
<AppBarButton Label="Paste" />
|
||||
</controls:TabbedCommandBarItem>
|
||||
<controls:TabbedCommandBarItem Header="Home">
|
||||
<AppBarButton Label="Undo" />
|
||||
<AppBarButton Label="Redo" />
|
||||
<AppBarButton Label="Paste" />
|
||||
</controls:TabbedCommandBarItem>
|
||||
</controls:TabbedCommandBar.MenuItems>
|
||||
</controls:TabbedCommandBar>
|
||||
</Grid>
|
||||
|
||||
<!-- Editor -->
|
||||
<Grid Grid.Row="2">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid Grid.Row="0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TabView
|
||||
Grid.Column="0"
|
||||
Width="350"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
<TabView.TabItems>
|
||||
<TabViewItem Header="Hierarchy">
|
||||
<TabViewItem.IconSource>
|
||||
<FontIconSource Glyph="" />
|
||||
</TabViewItem.IconSource>
|
||||
<ee:HierarchyPage />
|
||||
</TabViewItem>
|
||||
</TabView.TabItems>
|
||||
</TabView>
|
||||
|
||||
<TabView Grid.Column="1">
|
||||
<TabView.TabItems>
|
||||
<TabViewItem Header="Scene">
|
||||
<TabViewItem.IconSource>
|
||||
<FontIconSource Glyph="" />
|
||||
</TabViewItem.IconSource>
|
||||
<Image
|
||||
VerticalAlignment="Center"
|
||||
Source="C:\Users\Misaki\OneDrive\Pictures\Screenshots\Screenshot 2024-07-20 021657.png"
|
||||
Stretch="UniformToFill" />
|
||||
</TabViewItem>
|
||||
</TabView.TabItems>
|
||||
</TabView>
|
||||
|
||||
<Grid
|
||||
Grid.Column="2"
|
||||
Width="350"
|
||||
Background="Bisque" />
|
||||
</Grid>
|
||||
|
||||
<TabView Grid.Row="1" Height="350">
|
||||
<TabView.TabItems>
|
||||
<TabViewItem Header="Project">
|
||||
<TabViewItem.IconSource>
|
||||
<FontIconSource Glyph="" />
|
||||
</TabViewItem.IconSource>
|
||||
<ee:ProjectPage />
|
||||
</TabViewItem>
|
||||
<TabViewItem Header="Console">
|
||||
<TabViewItem.IconSource>
|
||||
<FontIconSource Glyph="" />
|
||||
</TabViewItem.IconSource>
|
||||
<ee:ConsolePage />
|
||||
</TabViewItem>
|
||||
</TabView.TabItems>
|
||||
</TabView>
|
||||
</Grid>
|
||||
|
||||
<!-- Status Bar -->
|
||||
<Grid
|
||||
Grid.Row="3"
|
||||
Height="25"
|
||||
Background="{ThemeResource SolidBackgroundFillColorBaseAltBrush}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Grid Grid.Column="0">
|
||||
<FontIcon
|
||||
Margin="8,0,0,0"
|
||||
FontSize="16"
|
||||
Foreground="{ThemeResource SystemFillColorSuccessBrush}"
|
||||
Glyph=""
|
||||
Visibility="Visible" />
|
||||
|
||||
<StackPanel Orientation="Horizontal" Visibility="Collapsed">
|
||||
<FontIcon
|
||||
Margin="8,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="16"
|
||||
Foreground="{ThemeResource SystemFillColorAttentionBrush}"
|
||||
Glyph="" />
|
||||
<TextBlock
|
||||
Margin="4,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="0" />
|
||||
<FontIcon
|
||||
Margin="8,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="16"
|
||||
Foreground="{ThemeResource SystemFillColorCautionBrush}"
|
||||
Glyph="" />
|
||||
<TextBlock
|
||||
Margin="4,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="0" />
|
||||
<FontIcon
|
||||
Margin="8,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="16"
|
||||
Foreground="{ThemeResource SystemFillColorCriticalBrush}"
|
||||
Glyph="" />
|
||||
<TextBlock
|
||||
Margin="4,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="0" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</winex:WindowEx>
|
||||
37
Ghost.InternalEditor/View/Windows/EngineEditorWindow.xaml.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
using Ghost.Data.Resources;
|
||||
using Ghost.Editor.ViewModels.Windows;
|
||||
using Ghost.Engine.Resources;
|
||||
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;
|
||||
/// <summary>
|
||||
/// An empty window that can be used on its own or navigated to within a Frame.
|
||||
/// </summary>
|
||||
internal sealed partial class EngineEditorWindow : WindowEx
|
||||
{
|
||||
public EngineEditorViewModel ViewModel
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public EngineEditorWindow()
|
||||
{
|
||||
ViewModel = GhostApplication.GetService<EngineEditorViewModel>();
|
||||
|
||||
AppWindow.SetIcon(AssetsPath.s_appIconPath);
|
||||
Title = EngineData.ENGINE_NAME;
|
||||
ExtendsContentIntoTitleBar = true;
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
this.CenterOnScreen();
|
||||
}
|
||||
|
||||
private void WindowEx_Activated(object sender, Microsoft.UI.Xaml.WindowActivatedEventArgs args)
|
||||
{
|
||||
Bindings.Update();
|
||||
}
|
||||
}
|
||||
77
Ghost.InternalEditor/View/Windows/LandingWindow.xaml
Normal file
@@ -0,0 +1,77 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<winex:WindowEx
|
||||
x:Class="Ghost.App.View.Windows.LandingWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:behaviors="using:CommunityToolkit.WinUI.Behaviors"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
|
||||
xmlns:local="using:Ghost.App.View.Windows"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:winex="using:WinUIEx"
|
||||
Activated="WindowEx_Activated"
|
||||
Closed="WindowEx_Closed"
|
||||
IsResizable="False"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Window.SystemBackdrop>
|
||||
<MicaBackdrop />
|
||||
</Window.SystemBackdrop>
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid Grid.Row="0">
|
||||
<TextBlock
|
||||
Margin="24,0,0,0"
|
||||
VerticalAlignment="Bottom"
|
||||
Style="{StaticResource BodyTextBlockStyle}"
|
||||
Text="Ghost Engine" />
|
||||
</Grid>
|
||||
|
||||
<Grid Grid.Row="1" Padding="24,0,24,18">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<SelectorBar
|
||||
Grid.Row="0"
|
||||
HorizontalAlignment="Right"
|
||||
SelectionChanged="SelectorBar_SelectionChanged">
|
||||
<SelectorBarItem IsSelected="True" Text="Open">
|
||||
<SelectorBarItem.Icon>
|
||||
<FontIcon Glyph="" />
|
||||
</SelectorBarItem.Icon>
|
||||
</SelectorBarItem>
|
||||
<SelectorBarItem Text="Create">
|
||||
<SelectorBarItem.Icon>
|
||||
<FontIcon Glyph="" />
|
||||
</SelectorBarItem.Icon>
|
||||
</SelectorBarItem>
|
||||
</SelectorBar>
|
||||
|
||||
<Frame
|
||||
x:Name="ContentFrame"
|
||||
Grid.Row="1"
|
||||
Padding="8"
|
||||
CacheMode="BitmapCache"
|
||||
CacheSize="10" />
|
||||
</Grid>
|
||||
|
||||
<Grid Grid.Row="1" Padding="16">
|
||||
<InfoBar
|
||||
x:Name="InfoBar"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Bottom">
|
||||
<interactivity:Interaction.Behaviors>
|
||||
<behaviors:StackedNotificationsBehavior x:Name="NotificationQueue" />
|
||||
</interactivity:Interaction.Behaviors>
|
||||
</InfoBar>
|
||||
|
||||
</Grid>
|
||||
</Grid>
|
||||
</winex:WindowEx>
|
||||
61
Ghost.InternalEditor/View/Windows/LandingWindow.xaml.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
using Ghost.App.Services;
|
||||
using Ghost.App.View.Pages.Landing;
|
||||
using Ghost.Data.Resources;
|
||||
using Ghost.Engine.Resources;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Media.Animation;
|
||||
using WinUIEx;
|
||||
|
||||
namespace Ghost.App.View.Windows;
|
||||
|
||||
internal sealed partial class LandingWindow : WindowEx
|
||||
{
|
||||
private IServiceScope? _landingScope;
|
||||
|
||||
private int _previousSelectedIndex;
|
||||
|
||||
public LandingWindow()
|
||||
{
|
||||
AppWindow.SetIcon(AssetsPath.s_appIconPath);
|
||||
Title = EngineData.ENGINE_NAME;
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
this.SetWindowSize(1000, 750);
|
||||
this.CenterOnScreen();
|
||||
|
||||
ExtendsContentIntoTitleBar = true;
|
||||
}
|
||||
|
||||
private void WindowEx_Activated(object sender, Microsoft.UI.Xaml.WindowActivatedEventArgs args)
|
||||
{
|
||||
_landingScope?.Dispose();
|
||||
_landingScope = GhostApplication.CreateScope();
|
||||
GhostApplication.GetService<StackedNotificationService>().SetReference(InfoBar, NotificationQueue);
|
||||
}
|
||||
|
||||
private void WindowEx_Closed(object sender, Microsoft.UI.Xaml.WindowEventArgs args)
|
||||
{
|
||||
_landingScope?.Dispose();
|
||||
GhostApplication.GetService<StackedNotificationService>().ClearReference();
|
||||
}
|
||||
|
||||
private void SelectorBar_SelectionChanged(SelectorBar sender, SelectorBarSelectionChangedEventArgs e)
|
||||
{
|
||||
var selectedItem = sender.SelectedItem;
|
||||
var currentSelectedIndex = sender.Items.IndexOf(selectedItem);
|
||||
var pageType = currentSelectedIndex switch
|
||||
{
|
||||
1 => typeof(CreateProjectPage),
|
||||
_ => typeof(OpenProjectPage),
|
||||
};
|
||||
|
||||
var slideNavigationTransitionEffect = currentSelectedIndex - _previousSelectedIndex > 0 ?
|
||||
SlideNavigationTransitionEffect.FromRight : SlideNavigationTransitionEffect.FromLeft;
|
||||
|
||||
ContentFrame.Navigate(pageType, _landingScope, new SlideNavigationTransitionInfo() { Effect = slideNavigationTransitionEffect });
|
||||
|
||||
_previousSelectedIndex = currentSelectedIndex;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Ghost.Engine.Models;
|
||||
using Ghost.Engine.Services;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Ghost.Editor.ViewModels.Pages.EngineEditor;
|
||||
|
||||
internal partial class ConsoleViewModel : ObservableObject
|
||||
{
|
||||
[ObservableProperty]
|
||||
public partial ObservableCollection<LogMessage> Logs
|
||||
{
|
||||
get; set;
|
||||
} = new();
|
||||
|
||||
[ObservableProperty]
|
||||
public partial bool ShowInfo
|
||||
{
|
||||
get; set;
|
||||
} = true;
|
||||
|
||||
[ObservableProperty]
|
||||
public partial bool ShowWarning
|
||||
{
|
||||
get; set;
|
||||
} = true;
|
||||
|
||||
[ObservableProperty]
|
||||
public partial bool ShowError
|
||||
{
|
||||
get; set;
|
||||
} = true;
|
||||
|
||||
[ObservableProperty]
|
||||
public partial bool ShowStackTrace
|
||||
{
|
||||
get; set;
|
||||
} = false;
|
||||
|
||||
[ObservableProperty]
|
||||
public partial LogMessage? SelectedLog
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
|
||||
public ConsoleViewModel()
|
||||
{
|
||||
foreach (var log in Logger.Logs)
|
||||
{
|
||||
Logs.Add(log);
|
||||
}
|
||||
|
||||
Logger.OnLogsUpdate += UpdateLogs;
|
||||
}
|
||||
|
||||
~ConsoleViewModel()
|
||||
{
|
||||
Logger.OnLogsUpdate -= UpdateLogs;
|
||||
}
|
||||
|
||||
private void UpdateLogs(LogChangeType updateType)
|
||||
{
|
||||
switch (updateType)
|
||||
{
|
||||
case LogChangeType.LogAdded:
|
||||
Logs.Add(Logger.Logs[^1]);
|
||||
break;
|
||||
case LogChangeType.LogRemoved:
|
||||
if (Logs.Count > 0)
|
||||
{
|
||||
Logs.RemoveAt(0);
|
||||
}
|
||||
break;
|
||||
case LogChangeType.LogsCleared:
|
||||
Logs.Clear();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
partial void OnShowStackTraceChanged(bool value)
|
||||
{
|
||||
Logger.HasStackTrace = value;
|
||||
Logger.LogInfo($"Stack trace visibility set to {value}.");
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void ClearLogs()
|
||||
{
|
||||
Logger.Clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Ghost.Editor.SceneGraph;
|
||||
using Ghost.Entities;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Ghost.Editor.ViewModels.Pages.EngineEditor;
|
||||
|
||||
internal partial class HierarchyViewModel : ObservableObject
|
||||
{
|
||||
[ObservableProperty]
|
||||
public partial ObservableCollection<WorldNode> SceneList
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
} = new();
|
||||
|
||||
public HierarchyViewModel()
|
||||
{
|
||||
// Test only
|
||||
var testWorld = World.Create();
|
||||
var entity1 = SceneGraphHelpers.CreateEntityNode(testWorld, "entity 1");
|
||||
var entity2 = SceneGraphHelpers.CreateEntityNode(testWorld, "entity 3");
|
||||
var entity3 = SceneGraphHelpers.CreateEntityNode(testWorld, "entity 4");
|
||||
var entity4 = SceneGraphHelpers.CreateEntityNode(testWorld, "entity 5");
|
||||
var entity5 = SceneGraphHelpers.CreateEntityNode(testWorld, "entity 2");
|
||||
|
||||
var testScene = new WorldNode(testWorld, "Test Scene");
|
||||
|
||||
SceneGraphHelpers.AttachChild(testScene, entity1, entity2);
|
||||
SceneGraphHelpers.AttachChild(testScene, entity1, entity3);
|
||||
SceneGraphHelpers.AttachChild(testScene, entity2, entity4);
|
||||
|
||||
testScene.AddChild(entity1);
|
||||
testScene.AddChild(entity5);
|
||||
|
||||
SceneList.Add(testScene);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Ghost.App;
|
||||
using Ghost.App.Models;
|
||||
using Ghost.Data.Services;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ghost.Editor.ViewModels.Pages.EngineEditor;
|
||||
|
||||
internal partial class ProjectViewModel : ObservableObject
|
||||
{
|
||||
public ObservableCollection<ExplorerItem> SubDirectories
|
||||
{
|
||||
get;
|
||||
} = new();
|
||||
|
||||
[ObservableProperty]
|
||||
public partial ObservableCollection<ExplorerItem> DirectoryAssets
|
||||
{
|
||||
get;
|
||||
set;
|
||||
} = new();
|
||||
|
||||
[ObservableProperty]
|
||||
public partial ExplorerItem? SelectedDirectory
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[ObservableProperty]
|
||||
public partial ExplorerItem? SelectedAsset
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public ProjectViewModel()
|
||||
{
|
||||
if (ProjectService.CurrentProject.Metadata == null)
|
||||
{
|
||||
throw new InvalidOperationException("Current project is not set.");
|
||||
}
|
||||
|
||||
var assetsRootItem = new ExplorerItem("Assets", Path.Combine(Path.GetDirectoryName(ProjectService.CurrentProject.Path)!, ProjectService.ASSETS_FOLDER), true);
|
||||
LoadSubFolderRecursive(ref assetsRootItem);
|
||||
|
||||
SubDirectories.Add(assetsRootItem);
|
||||
}
|
||||
|
||||
private void LoadSubFolderRecursive(ref ExplorerItem parentItem)
|
||||
{
|
||||
foreach (var directory in Directory.EnumerateDirectories(parentItem.Path))
|
||||
{
|
||||
var item = new ExplorerItem(Path.GetFileName(directory), directory, true);
|
||||
LoadSubFolderRecursive(ref item);
|
||||
|
||||
parentItem.Children ??= new();
|
||||
parentItem.Children.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
public static Task<ExplorerItem?> FindNodeIterative(ExplorerItem root, Func<ExplorerItem, bool> predicate)
|
||||
{
|
||||
var stack = new Stack<ExplorerItem>();
|
||||
stack.Push(root);
|
||||
|
||||
return Task.Run(() =>
|
||||
{
|
||||
while (stack.Count > 0)
|
||||
{
|
||||
var node = stack.Pop();
|
||||
if (predicate(node))
|
||||
{
|
||||
return node;
|
||||
}
|
||||
|
||||
if (node.Children == null || node.Children.Count == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (var i = node.Children.Count - 1; i >= 0; i--)
|
||||
{
|
||||
stack.Push(node.Children[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
private void NavigateToDirectory(string? path)
|
||||
{
|
||||
GhostApplication.Window?.DispatcherQueue.TryEnqueue(async () =>
|
||||
{
|
||||
DirectoryAssets.Clear();
|
||||
|
||||
if (!Directory.Exists(path))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var directory in Directory.EnumerateDirectories(path))
|
||||
{
|
||||
var directoryItem = new ExplorerItem(Path.GetFileName(directory), directory, true);
|
||||
DirectoryAssets.Add(directoryItem);
|
||||
}
|
||||
|
||||
foreach (var file in Directory.EnumerateFiles(path))
|
||||
{
|
||||
var fileItem = new ExplorerItem(Path.GetFileName(file), file, false);
|
||||
DirectoryAssets.Add(fileItem);
|
||||
}
|
||||
|
||||
SelectedDirectory = await FindNodeIterative(SubDirectories[0], x => x.Path == path);
|
||||
});
|
||||
}
|
||||
|
||||
public void NavigateToSelected()
|
||||
{
|
||||
if (SelectedAsset == null || !SelectedAsset.IsDirectory)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
NavigateToDirectory(SelectedAsset.Path);
|
||||
}
|
||||
|
||||
partial void OnSelectedDirectoryChanged(ExplorerItem? value)
|
||||
{
|
||||
DirectoryAssets.Clear();
|
||||
if (value == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
NavigateToDirectory(value.Path);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
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.Engine.Resources;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ghost.Editor.ViewModels.Pages.Landing;
|
||||
|
||||
internal partial class CreateProjectViewModel(StackedNotificationService notificationService, ProjectService projectService, AppStateMachine stateService) : ObservableObject, INavigationAware
|
||||
{
|
||||
public ObservableCollection<TemplateData> templates = new();
|
||||
|
||||
[ObservableProperty]
|
||||
public partial TemplateData? SelectedTemplate
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[ObservableProperty]
|
||||
public partial string? ProjectName
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[ObservableProperty]
|
||||
public partial string? ProjectLocation
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public async void OnNavigatedTo(object? parameter)
|
||||
{
|
||||
templates.Clear();
|
||||
await foreach (var (path, info) in ProjectService.GetProjectTemplatesAsync())
|
||||
{
|
||||
templates.Add(new(path, info));
|
||||
}
|
||||
|
||||
SelectedTemplate = templates.FirstOrDefault();
|
||||
}
|
||||
|
||||
public void OnNavigatedFrom()
|
||||
{
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task SelectionProjectLocation()
|
||||
{
|
||||
var folder = await SystemUtilities.OpenFolderPickerAsync();
|
||||
if (folder != null)
|
||||
{
|
||||
ProjectLocation = folder.Path;
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task CreateProject()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(ProjectName)
|
||||
|| !Directory.Exists(ProjectLocation)
|
||||
|| !SelectedTemplate.HasValue)
|
||||
{
|
||||
notificationService.ShowNotification("Incorrect project info", InfoBarSeverity.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
var result = await projectService.CreateProjectAsync(ProjectName, ProjectLocation, EngineData.s_engineVersion, SelectedTemplate.Value.directory);
|
||||
if (!result.success)
|
||||
{
|
||||
notificationService.ShowNotification(result.message, InfoBarSeverity.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await stateService.TransitionToAsync(StateKey.EngineEditor, result.data);
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
notificationService.ShowNotification($"Failed to load project: {e.Message}", InfoBarSeverity.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Ghost.App.Contracts;
|
||||
using Ghost.App.Infrastructures.AppState;
|
||||
using Ghost.App.Services;
|
||||
using Ghost.Data.Models;
|
||||
using Ghost.Data.Services;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.ApplicationModel.DataTransfer;
|
||||
using Windows.Storage;
|
||||
|
||||
namespace Ghost.Editor.ViewModels.Pages.Landing;
|
||||
|
||||
internal partial class OpenProjectViewModel(ProjectService projectService, StackedNotificationService _notificationService, AppStateMachine _stateService) : ObservableObject, INavigationAware
|
||||
{
|
||||
public readonly ObservableCollection<ProjectMetadataInfo> projects = new();
|
||||
|
||||
[ObservableProperty]
|
||||
public partial Visibility EmptyVisibility
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[ObservableProperty]
|
||||
public partial Visibility DragVisibility
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public void UpdateEmptyPlaceHolderVisibility()
|
||||
{
|
||||
EmptyVisibility = projects.Count == 0 ? Visibility.Visible : Visibility.Collapsed;
|
||||
}
|
||||
|
||||
public async void OnNavigatedTo(object? parameter)
|
||||
{
|
||||
projects.Clear();
|
||||
await foreach (var projectInfo in projectService.GetAllProjectAsync())
|
||||
{
|
||||
var metadata = await ProjectService.LoadMetadataAsync(projectInfo.MetadataPath);
|
||||
if (metadata == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
projects.Add(new(projectInfo.MetadataPath, metadata));
|
||||
}
|
||||
|
||||
UpdateEmptyPlaceHolderVisibility();
|
||||
DragVisibility = Visibility.Collapsed;
|
||||
}
|
||||
|
||||
public void OnNavigatedFrom()
|
||||
{
|
||||
}
|
||||
|
||||
public async Task ContentDrop(DataPackageView dataView)
|
||||
{
|
||||
var errorMessage = string.Empty;
|
||||
if (dataView.Contains(StandardDataFormats.StorageItems))
|
||||
{
|
||||
var items = await dataView.GetStorageItemsAsync();
|
||||
var rootFolder = items.OfType<StorageFolder>().FirstOrDefault();
|
||||
if (rootFolder != null)
|
||||
{
|
||||
var result = await projectService.AddProjectFromDirectoryAsync(rootFolder.Path);
|
||||
if (result.success)
|
||||
{
|
||||
projects.Add(result.data);
|
||||
goto CloseDropPanel;
|
||||
}
|
||||
else
|
||||
{
|
||||
errorMessage = result.message;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
errorMessage = "Unsupported data format. Please drop a folder containing a project.";
|
||||
}
|
||||
|
||||
_notificationService.ShowNotification(errorMessage, InfoBarSeverity.Error);
|
||||
|
||||
CloseDropPanel:
|
||||
DragVisibility = Visibility.Collapsed;
|
||||
UpdateEmptyPlaceHolderVisibility();
|
||||
}
|
||||
|
||||
public async Task LoadProject(ProjectMetadataInfo project)
|
||||
{
|
||||
try
|
||||
{
|
||||
project.Metadata.LastOpened = DateTime.Now;
|
||||
await ProjectService.CreateMetadataFileAsync(project.Path, project.Metadata);
|
||||
|
||||
await _stateService.TransitionToAsync(StateKey.EngineEditor, project);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_notificationService.ShowNotification($"Failed to load project: {e.Message}", InfoBarSeverity.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Ghost.Data.Models;
|
||||
using Ghost.Data.Services;
|
||||
using Ghost.Engine.Resources;
|
||||
|
||||
namespace Ghost.Editor.ViewModels.Windows;
|
||||
|
||||
internal partial class EngineEditorViewModel : ObservableRecipient
|
||||
{
|
||||
public string engineVersionDescriptor = $"{EngineData.ENGINE_NAME} - {EngineData.s_engineVersion}";
|
||||
|
||||
public ProjectMetadataInfo CurrentProject => ProjectService.CurrentProject;
|
||||
}
|
||||
19
Ghost.InternalEditor/app.manifest
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<assemblyIdentity version="1.0.0.0" name="Ghost.Editor.app"/>
|
||||
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<!-- The ID below informs the system that this application is compatible with OS features first introduced in Windows 10.
|
||||
It is necessary to support features in unpackaged applications, for example the custom titlebar implementation.
|
||||
For more info see https://docs.microsoft.com/windows/apps/windows-app-sdk/use-windows-app-sdk-run-time#declare-os-compatibility-in-your-application-manifest -->
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
|
||||
</application>
|
||||
</compatibility>
|
||||
|
||||
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<windowsSettings>
|
||||
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
|
||||
</windowsSettings>
|
||||
</application>
|
||||
</assembly>
|
||||