Refactor project structure and enhance UI components

Changed connection string format in ProjectRepository.cs and updated application data path retrieval.

Added project reference to Ghost.Engine in Ghost.Database.csproj.

Added new property Packages in TemplateInfo.cs.

Changed XamlControlsResources source in App.xaml for clarity.

Changed App.xaml.cs to include new Host property and updated OnLaunched method.

Removed unused image files related to branding.

Updated Ghost.Editor.csproj to change target framework and organize content files.

Changed display name in Package.appxmanifest from "Ghost.Editor" to "GhostEngine".

Enhanced UI layout and data binding in CreateProjectPage.xaml and its code-behind.

Changed LandingWindow to use WindowEx for improved functionality.

Updated CreateProjectViewModel to implement INavigationAware for better navigation handling.

Updated AssemblyInfo.cs for internal visibility to Ghost.Database.

Added new files for activation handling and game object management in Ghost.Core and Ghost.Engine.

Introduced SystemUtilities for folder picker dialog functionality.

Created PropertyField control with corresponding XAML for UI consistency.

Added TemplateInfoWarper for managing template information.

Introduced HostHelper class to set up application services.

Overall, these changes reflect a significant restructuring of the project, enhancing architecture, improving UI components, and establishing clearer separation of concerns.
This commit is contained in:
2025-03-26 01:18:16 +09:00
parent 23a08bc8e0
commit 02b3edcd7a
58 changed files with 565 additions and 82 deletions

View File

@@ -0,0 +1,13 @@
namespace Ghost.Core.Activation;
public abstract class ActivationHandler<T> : IActivationHandler
where T : class
{
protected virtual bool CanHandleInternal(T args) => true;
protected abstract Task HandleInternalAsync(T args);
public bool CanHandle(object args) => args is T && CanHandleInternal((args as T)!);
public async Task HandleAsync(object args) => await HandleInternalAsync((args as T)!);
}

View File

@@ -0,0 +1,8 @@
namespace Ghost.Core.Activation;
public interface IActivationHandler
{
bool CanHandle(object args);
Task HandleAsync(object args);
}

View File

@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View File

@@ -1,16 +1,17 @@
using Ghost.Database.Models.Projects; using Ghost.Database.Models.Projects;
using Ghost.Engine.Constants;
using System.Data.SQLite; using System.Data.SQLite;
namespace Ghost.Database.DataContext; namespace Ghost.Database.DataContext;
public class ProjectRepository public class ProjectRepository
{ {
private const string _CONNECTION_STRING = "Data Source={0}/projects.db;Version=3;"; private const string _CONNECTION_STRING = "Data Source={0}\\projects.db;Version=3;";
private const string _CREATE_PROJECT_TABLE_STRING = "CREATE TABLE IF NOT EXISTS Projects (ID INTEGER PRIMARY KEY AUTOINCREMENT, Name TEXT, Path TEXT, EngineVersion TEXT, LastOpened DATETIME);"; private const string _CREATE_PROJECT_TABLE_STRING = "CREATE TABLE IF NOT EXISTS Projects (ID INTEGER PRIMARY KEY AUTOINCREMENT, Name TEXT, Path TEXT, EngineVersion TEXT, LastOpened DATETIME);";
private const string _SELECT_PROJECT_STRING = "SELECT * FROM Projects"; private const string _SELECT_PROJECT_STRING = "SELECT * FROM Projects";
private const string _INSERT_PROJECT_STRING = "INSERT INTO Projects (Name, Path, EngineVersion, LastOpened) VALUES (@Name, @Path, @EngineVersion, @LastOpened);"; private const string _INSERT_PROJECT_STRING = "INSERT INTO Projects (Name, Path, EngineVersion, LastOpened) VALUES (@Name, @Path, @EngineVersion, @LastOpened);";
private static string GetConnectionString() => string.Format(_CONNECTION_STRING, Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)); private static string GetConnectionString() => string.Format(_CONNECTION_STRING, EngineDataPath.ApplicationDataFolder);
private static void EnsureTableCreated(SQLiteConnection connection) private static void EnsureTableCreated(SQLiteConnection connection)
{ {

View File

@@ -12,4 +12,8 @@
<PackageReference Include="System.Drawing.Common" Version="4.7.3" /> <PackageReference Include="System.Drawing.Common" Version="4.7.3" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Ghost.Engine\Ghost.Engine.csproj" />
</ItemGroup>
</Project> </Project>

View File

@@ -21,4 +21,9 @@ public class TemplateInfo
{ {
get; set; get; set;
} }
public Dictionary<string, Version>? Packages
{
get; set;
}
} }

View File

@@ -8,7 +8,7 @@
<ResourceDictionary> <ResourceDictionary>
<ResourceDictionary.MergedDictionaries> <ResourceDictionary.MergedDictionaries>
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" /> <XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
<!-- Other merged dictionaries here --> <XamlControlsResources Source="/Controls/EditorControls.xaml" />
</ResourceDictionary.MergedDictionaries> </ResourceDictionary.MergedDictionaries>
<!-- Other app resources here --> <!-- Other app resources here -->
</ResourceDictionary> </ResourceDictionary>

View File

@@ -1,6 +1,5 @@
using Ghost.Editor.View.Windows; using Ghost.Editor.View.Pages;
using Ghost.Editor.ViewModel.Windows; using Ghost.Editor.View.Windows;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using Microsoft.UI.Xaml; using Microsoft.UI.Xaml;
using System; using System;
@@ -16,22 +15,17 @@ namespace Ghost.Editor
public partial class App : Application public partial class App : Application
{ {
private Window? _window; private Window? _window;
public Window? CurrentWindow
{
get => _window;
set => _window = value;
}
public IHost Host public IHost Host
{ {
get; get;
} }
public static T GetService<T>() where T : class
{
if ((Current as App)!.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> /// <summary>
/// Initializes the singleton application object. This is the first line of authored code /// Initializes the singleton application object. This is the first line of authored code
/// executed, and as such is the logical equivalent of main() or WinMain(). /// executed, and as such is the logical equivalent of main() or WinMain().
@@ -45,12 +39,26 @@ namespace Ghost.Editor
UseContentRoot(AppContext.BaseDirectory). UseContentRoot(AppContext.BaseDirectory).
ConfigureServices((context, services) => ConfigureServices((context, services) =>
{ {
services.AddTransient<LandingWindow>(); HostHelper.SetupPageService(context, services);
services.AddTransient<LandingViewModel>();
}) })
.Build(); .Build();
} }
public static Window? GetWindow()
{
return (Current as App)?.CurrentWindow;
}
public static T GetService<T>() where T : class
{
if ((Current as App)!.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> /// <summary>
/// Invoked when the application is launched. /// Invoked when the application is launched.
/// </summary> /// </summary>
@@ -58,6 +66,9 @@ namespace Ghost.Editor
protected override void OnLaunched(LaunchActivatedEventArgs args) protected override void OnLaunched(LaunchActivatedEventArgs args)
{ {
base.OnLaunched(args); base.OnLaunched(args);
EditorActivationHandler.Handle(args);
Host.Start(); Host.Start();
_window = GetService<LandingWindow>(); _window = GetService<LandingWindow>();

Binary file not shown.

After

Width:  |  Height:  |  Size: 599 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 831 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 580 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 825 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 433 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 583 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 852 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 637 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 283 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

@@ -0,0 +1,9 @@
using Ghost.Engine.Constants;
using System.IO;
namespace Ghost.Editor.Constants;
public static class EditorDataPath
{
public static string ProjectTemplatesFolder = Path.Combine(EngineDataPath.ApplicationDataFolder, "ProjectsTemplates");
}

View File

@@ -0,0 +1,7 @@
namespace Ghost.Editor.Contracts;
internal interface INavigationAware
{
public void OnNavigatedTo(object? parameter);
public void OnNavigatedFrom();
}

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

View 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>

View File

@@ -0,0 +1,6 @@
<?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.MergedDictionaries>
</ResourceDictionary>

View File

@@ -0,0 +1,27 @@
using Ghost.Editor.Constants;
using Ghost.Engine.Constants;
using Microsoft.UI.Xaml;
using System.IO;
namespace Ghost.Editor;
internal static class EditorActivationHandler
{
private static void FolderInitialization()
{
if (!Directory.Exists(EngineDataPath.ApplicationDataFolder))
{
Directory.CreateDirectory(EngineDataPath.ApplicationDataFolder);
}
if (!Directory.Exists(EditorDataPath.ProjectTemplatesFolder))
{
Directory.CreateDirectory(EditorDataPath.ProjectTemplatesFolder);
}
}
public static void Handle(LaunchActivatedEventArgs args)
{
FolderInitialization();
}
}

View File

@@ -1,10 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<TargetFramework>net9.0-windows10.0.19041.0</TargetFramework> <TargetFramework>net9.0-windows10.0.20348.0</TargetFramework>
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion> <TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
<RootNamespace>Ghost.Editor</RootNamespace> <RootNamespace>Ghost.Editor</RootNamespace>
<ApplicationManifest>app.manifest</ApplicationManifest>
<Platforms>x86;x64;ARM64</Platforms> <Platforms>x86;x64;ARM64</Platforms>
<RuntimeIdentifiers>win-x86;win-x64;win-arm64</RuntimeIdentifiers> <RuntimeIdentifiers>win-x86;win-x64;win-arm64</RuntimeIdentifiers>
<PublishProfile>win-$(Platform).pubxml</PublishProfile> <PublishProfile>win-$(Platform).pubxml</PublishProfile>
@@ -13,6 +12,31 @@
<LangVersion>preview</LangVersion> <LangVersion>preview</LangVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <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" />
<Content Remove="Assets\Icon.scale-100.png" />
<Content Remove="Assets\Icon.scale-125.png" />
<Content Remove="Assets\Icon.scale-150.png" />
<Content Remove="Assets\Icon.scale-200.png" />
<Content Remove="Assets\Icon.scale-400.png" />
<Content Remove="Assets\Icon.targetsize-16.png" />
<Content Remove="Assets\Icon.targetsize-24.png" />
<Content Remove="Assets\Icon.targetsize-256.png" />
<Content Remove="Assets\Icon.targetsize-32.png" />
<Content Remove="Assets\Icon.targetsize-48.png" />
</ItemGroup>
<ItemGroup>
<None Remove="Controls\BasicInput\PropertyField.xaml" />
<None Remove="Controls\EditorControls.xaml" />
<None Remove="View\Pages\Landing\CreateProjectPage.xaml" /> <None Remove="View\Pages\Landing\CreateProjectPage.xaml" />
<None Remove="View\Pages\Landing\OpenProjectPage.xaml" /> <None Remove="View\Pages\Landing\OpenProjectPage.xaml" />
</ItemGroup> </ItemGroup>
@@ -21,8 +45,6 @@
<Content Include="Assets\SplashScreen.scale-200.png" /> <Content Include="Assets\SplashScreen.scale-200.png" />
<Content Include="Assets\LockScreenLogo.scale-200.png" /> <Content Include="Assets\LockScreenLogo.scale-200.png" />
<Content Include="Assets\Square150x150Logo.scale-200.png" /> <Content Include="Assets\Square150x150Logo.scale-200.png" />
<Content Include="Assets\Square44x44Logo.scale-200.png" />
<Content Include="Assets\Square44x44Logo.targetsize-24_altform-unplated.png" />
<Content Include="Assets\StoreLogo.png" /> <Content Include="Assets\StoreLogo.png" />
<Content Include="Assets\Wide310x150Logo.scale-200.png" /> <Content Include="Assets\Wide310x150Logo.scale-200.png" />
</ItemGroup> </ItemGroup>
@@ -44,6 +66,7 @@
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.3" /> <PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.3" />
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.1742" /> <PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.1742" />
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.7.250310001" /> <PackageReference Include="Microsoft.WindowsAppSDK" Version="1.7.250310001" />
<PackageReference Include="WinUIEx" Version="2.5.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Ghost.Database\Ghost.Database.csproj" /> <ProjectReference Include="..\Ghost.Database\Ghost.Database.csproj" />
@@ -64,6 +87,20 @@
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
</Page> </Page>
</ItemGroup> </ItemGroup>
<ItemGroup>
<Folder Include="Services\" />
<Folder Include="ViewModel\Windows\" />
</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" /> <PropertyGroup Label="Globals" />
<!-- <!--
@@ -81,5 +118,7 @@
<PublishReadyToRun Condition="'$(Configuration)' != 'Debug'">True</PublishReadyToRun> <PublishReadyToRun Condition="'$(Configuration)' != 'Debug'">True</PublishReadyToRun>
<PublishTrimmed Condition="'$(Configuration)' != 'Debug'">True</PublishTrimmed> <PublishTrimmed Condition="'$(Configuration)' != 'Debug'">True</PublishTrimmed>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<SupportedOSPlatformVersion>10.0.19041.0</SupportedOSPlatformVersion>
<ApplicationManifest>app.manifest</ApplicationManifest>
</PropertyGroup> </PropertyGroup>
</Project> </Project>

View File

@@ -0,0 +1,24 @@
using System;
using System.Threading.Tasks;
using Windows.Storage;
using Windows.Storage.Pickers;
using WinRT.Interop;
namespace Ghost.Editor.Helpers;
public static class SystemUtilities
{
public static async Task<StorageFolder?> OpenFolderPickerAsync(PickerLocationId startLocation = PickerLocationId.DocumentsLibrary, string settingsIdentifier = "")
{
var openPicker = new FolderPicker();
var hWnd = WindowNative.GetWindowHandle(App.GetWindow());
InitializeWithWindow.Initialize(openPicker, hWnd);
openPicker.SuggestedStartLocation = startLocation;
openPicker.FileTypeFilter.Add("*");
openPicker.SettingsIdentifier = settingsIdentifier;
var folder = await openPicker.PickSingleFolderAsync();
return folder;
}
}

View File

@@ -0,0 +1,25 @@
using Ghost.Database.Models.Projects;
using System;
using System.IO;
namespace Ghost.Editor.Models.Warpers;
internal class TemplateInfoWarper(string templatePath, TemplateInfo info)
{
private const string _ICON_NAME = "icon.png";
private const string _PREVIEW_NAME = "preview.png";
public string directory = Path.GetDirectoryName(templatePath)!;
public TemplateInfo Info => info;
public Uri GetIconURI()
{
return new Uri(Path.Combine(directory, _ICON_NAME));
}
public Uri GetPreviewURI()
{
return new Uri(Path.Combine(directory, _PREVIEW_NAME));
}
}

View File

@@ -15,7 +15,7 @@
<mp:PhoneIdentity PhoneProductId="4bcf724a-f735-433b-b5c5-4d17b9d38197" PhonePublisherId="00000000-0000-0000-0000-000000000000"/> <mp:PhoneIdentity PhoneProductId="4bcf724a-f735-433b-b5c5-4d17b9d38197" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
<Properties> <Properties>
<DisplayName>Ghost.Editor</DisplayName> <DisplayName>GhostEngine</DisplayName>
<PublisherDisplayName>Misaki</PublisherDisplayName> <PublisherDisplayName>Misaki</PublisherDisplayName>
<Logo>Assets\StoreLogo.png</Logo> <Logo>Assets\StoreLogo.png</Logo>
</Properties> </Properties>
@@ -34,11 +34,10 @@
Executable="$targetnametoken$.exe" Executable="$targetnametoken$.exe"
EntryPoint="$targetentrypoint$"> EntryPoint="$targetentrypoint$">
<uap:VisualElements <uap:VisualElements
DisplayName="Ghost.Editor" DisplayName="GhostEngine"
Description="Ghost.Editor" Description="GhostEngine"
BackgroundColor="transparent" BackgroundColor="transparent"
Square150x150Logo="Assets\Square150x150Logo.png" Square150x150Logo="Assets\Square150x150Logo.png" Square44x44Logo="Assets\Icon.png">
Square44x44Logo="Assets\Square44x44Logo.png">
<uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png" /> <uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png" />
<uap:SplashScreen Image="Assets\SplashScreen.png" /> <uap:SplashScreen Image="Assets\SplashScreen.png" />
</uap:VisualElements> </uap:VisualElements>

View File

@@ -0,0 +1,22 @@
using Ghost.Editor.View.Pages.Landing;
using Ghost.Editor.View.Windows;
using Ghost.Editor.ViewModel.Pages.Landing;
using Ghost.Editor.ViewModel.Windows;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace Ghost.Editor.View.Pages;
internal static partial class HostHelper
{
public static void SetupPageService(HostBuilderContext context, IServiceCollection services)
{
services.AddTransient<LandingWindow>();
services.AddTransient<LandingViewModel>();
services.AddTransient<CreateProjectPage>();
services.AddTransient<CreateProjectViewModel>();
services.AddTransient<OpenProjectPage>();
}
}

View File

@@ -4,9 +4,10 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:data="using:Ghost.Database.Models.Projects" xmlns:editor="using:Ghost.Editor.Controls"
xmlns:local="using:Ghost.Editor.View.Pages.Landing" xmlns:local="using:Ghost.Editor.View.Pages.Landing"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:warper="using:Ghost.Editor.Models.Warpers"
mc:Ignorable="d"> mc:Ignorable="d">
<Grid> <Grid>
@@ -16,25 +17,114 @@
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<!-- Template Info --> <!-- Template Info -->
<Grid Grid.Column="0"> <Grid Grid.Column="0" Width="300">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock <TextBlock
Grid.Column="0" Grid.Row="0"
Margin="0,0,0,24"
Style="{StaticResource SubtitleTextBlockStyle}" Style="{StaticResource SubtitleTextBlockStyle}"
Text="Template" /> Text="Template" />
<ListView SelectedItem=""> <ListView
Grid.Row="1"
ItemsSource="{x:Bind ViewModel.templates}"
SelectedItem="{x:Bind ViewModel.SelectedTemplate, Mode=TwoWay}">
<ListView.ItemTemplate> <ListView.ItemTemplate>
<DataTemplate x:DataType="data:TemplateInfo"> <DataTemplate x:DataType="warper:TemplateInfoWarper">
<TextBlock VerticalAlignment="Center" Text="{x:Bind Name}" /> <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> </DataTemplate>
</ListView.ItemTemplate> </ListView.ItemTemplate>
</ListView> </ListView>
</Grid> </Grid>
<!-- Project Info --> <!-- Project Info -->
<Grid Grid.Column="1" DataContext="{x:Bind ViewModel.SelectedTemplate, Mode=OneWay}"> <Grid
<Image Stretch="UniformToFill" /> Grid.Column="1"
<TextBlock Text="{x:Bind ViewModel.SelectedTemplate.Description}" /> Margin="16,0,0,0"
Padding="16"
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
CornerRadius="8">
<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.GetPreviewURI()}" />
</Image.Source>
</Image>
<Grid
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Background="{ThemeResource CircleElevationBorderBrush}" />
</Grid>
<StackPanel Grid.Row="1" Margin="8,0">
<TextBlock
Margin="0,16,0,8"
Style="{StaticResource TitleTextBlockStyle}"
Text="{x:Bind ViewModel.SelectedTemplate.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" 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="&#xE8DA;" />
</Button>
</Grid>
</editor:PropertyField>
</StackPanel>
<Grid Grid.Row="2">
<Button
Width="150"
HorizontalAlignment="Right"
Content="Create"
Style="{ThemeResource AccentButtonStyle}" />
</Grid>
</Grid> </Grid>
</Grid> </Grid>

View File

@@ -1,8 +1,6 @@
using Ghost.Editor.ViewModel.Pages.Landing; using Ghost.Editor.ViewModel.Pages.Landing;
using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Navigation;
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
namespace Ghost.Editor.View.Pages.Landing; namespace Ghost.Editor.View.Pages.Landing;
@@ -13,10 +11,16 @@ internal sealed partial class CreateProjectPage : Page
get; get;
} }
public CreateProjectPage(CreateProjectViewModel viewModel) public CreateProjectPage()
{ {
ViewModel = viewModel; ViewModel = App.GetService<CreateProjectViewModel>();
InitializeComponent(); InitializeComponent();
} }
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
ViewModel.OnNavigatedTo(e.Parameter);
}
} }

View File

@@ -1,15 +1,21 @@
<?xml version="1.0" encoding="utf-8" ?> <?xml version="1.0" encoding="utf-8" ?>
<Window <winex:WindowEx
x:Class="Ghost.Editor.View.Windows.LandingWindow" x:Class="Ghost.Editor.View.Windows.LandingWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:Ghost.Editor.View.Windows" xmlns:local="using:Ghost.Editor.View.Windows"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:winex="using:WinUIEx"
Title="Landing" Title="Landing"
IsResizable="False"
mc:Ignorable="d"> mc:Ignorable="d">
<Grid Padding="4"> <Window.SystemBackdrop>
<MicaBackdrop />
</Window.SystemBackdrop>
<Grid Padding="24,32,24,18">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="*" /> <RowDefinition Height="*" />
@@ -21,7 +27,7 @@
SelectionChanged="SelectorBar_SelectionChanged"> SelectionChanged="SelectorBar_SelectionChanged">
<SelectorBarItem IsSelected="True" Text="Open"> <SelectorBarItem IsSelected="True" Text="Open">
<SelectorBarItem.Icon> <SelectorBarItem.Icon>
<FontIcon Glyph="&#xE8A7;" /> <FontIcon Glyph="&#xE838;" />
</SelectorBarItem.Icon> </SelectorBarItem.Icon>
</SelectorBarItem> </SelectorBarItem>
<SelectorBarItem Text="Create"> <SelectorBarItem Text="Create">
@@ -34,7 +40,7 @@
<Frame <Frame
x:Name="ContentFrame" x:Name="ContentFrame"
Grid.Row="1" Grid.Row="1"
Padding="24,8" Padding="8"
IsNavigationStackEnabled="False" /> IsNavigationStackEnabled="False" />
</Grid> </Grid>
</Window> </winex:WindowEx>

View File

@@ -1,30 +1,22 @@
// To learn more about WinUI, the WinUI project structure, using Ghost.Editor.View.Pages.Landing;
// and more about our project templates, see: http://aka.ms/winui-project-info.
using Ghost.Editor.View.Pages.Landing;
using Ghost.Editor.ViewModel.Windows;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media.Animation; using Microsoft.UI.Xaml.Media.Animation;
using WinUIEx;
namespace Ghost.Editor.View.Windows; namespace Ghost.Editor.View.Windows;
internal sealed partial class LandingWindow : Window internal sealed partial class LandingWindow : WindowEx
{ {
public LandingViewModel ViewModel
{
get;
}
private int _previousSelectedIndex; private int _previousSelectedIndex;
public LandingWindow(LandingViewModel viewModel) public LandingWindow()
{ {
ViewModel = viewModel;
InitializeComponent(); InitializeComponent();
AppWindow.Resize(new(1200, 900)); this.SetWindowSize(1000, 750);
this.CenterOnScreen();
ExtendsContentIntoTitleBar = true;
} }
private void SelectorBar_SelectionChanged(SelectorBar sender, SelectorBarSelectionChangedEventArgs e) private void SelectorBar_SelectionChanged(SelectorBar sender, SelectorBarSelectionChangedEventArgs e)

View File

@@ -1,17 +1,70 @@
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Ghost.Database.Models.Projects; using Ghost.Database.Models.Projects;
using Ghost.Editor.Constants;
using Ghost.Editor.Contracts;
using Ghost.Editor.Helpers;
using Ghost.Editor.Models.Warpers;
using Microsoft.UI.Dispatching;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
namespace Ghost.Editor.ViewModel.Pages.Landing; namespace Ghost.Editor.ViewModel.Pages.Landing;
internal partial class CreateProjectViewModel : ObservableRecipient internal partial class CreateProjectViewModel : ObservableRecipient, INavigationAware
{ {
public ObservableCollection<TemplateInfo> templates = new(); public ObservableCollection<TemplateInfoWarper> templates = new();
[ObservableProperty] [ObservableProperty]
public partial TemplateInfo SelectedTemplate public partial TemplateInfoWarper? SelectedTemplate
{ {
get; get;
set; set;
} }
[ObservableProperty]
public partial string ProjectName
{
get;
set;
}
[ObservableProperty]
public partial string ProjectLocation
{
get;
set;
}
public void OnNavigatedTo(object? parameter)
{
DispatcherQueue.GetForCurrentThread().TryEnqueue(DispatcherQueuePriority.High, () =>
{
foreach (var templatePath in Directory.GetFiles(EditorDataPath.ProjectTemplatesFolder, "template.json", SearchOption.AllDirectories))
{
var templateInfo = JsonSerializer.Deserialize<TemplateInfo>(File.ReadAllText(templatePath));
if (templateInfo == null)
{
continue;
}
templates.Add(new(templatePath, templateInfo));
}
SelectedTemplate = templates.FirstOrDefault();
});
}
public void OnNavigatedFrom()
{
}
[RelayCommand]
private async Task SelectionProjectLocation()
{
ProjectLocation = (await SystemUtilities.OpenFolderPickerAsync())?.Path ?? string.Empty;
}
} }

View File

@@ -1,13 +0,0 @@
using CommunityToolkit.Mvvm.ComponentModel;
namespace Ghost.Editor.ViewModel.Windows;
internal partial class LandingViewModel : ObservableRecipient
{
[ObservableProperty]
public partial int TabIndex
{
get;
set;
}
}

View File

@@ -0,0 +1,11 @@
using Ghost.Engine.Models;
namespace Ghost.Engine;
internal static class ActivationHandler
{
public static void Handle(LaunchArgument args)
{
}
}

View File

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

View File

@@ -0,0 +1,11 @@
using Ghost.Engine.Models;
using System.Numerics;
namespace Ghost.Engine.Components;
public class Transform : Component
{
public Vector3 position = Vector3.Zero;
public Quaternion rotation = Quaternion.Identity;
public Vector3 scale = Vector3.One;
}

View File

@@ -0,0 +1,8 @@
namespace Ghost.Engine.Constants;
internal static class EngineDataPath
{
public const string ENGINE_DATA_FOLDER_NAME = "GhostEngine";
public static string ApplicationDataFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), ENGINE_DATA_FOLDER_NAME);
}

View File

@@ -0,0 +1,17 @@
using Ghost.Engine.Models;
namespace Ghost.Engine;
internal class EngineCore
{
public async Task StartAsync()
{
ActivationHandler.Handle(new LaunchArgument());
await Task.CompletedTask;
}
public async Task ShutDownAsync()
{
await Task.CompletedTask;
}
}

View File

@@ -6,4 +6,8 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<Folder Include="Services\" />
</ItemGroup>
</Project> </Project>

View File

@@ -0,0 +1,5 @@
namespace Ghost.Engine.Models;
public class Component
{
}

View File

@@ -0,0 +1,18 @@
using Ghost.Engine.Components;
namespace Ghost.Engine.Models;
public class GameObject
{
private List<Component> _components = new();
public GameObject()
{
AddComponent(new Transform());
}
public void AddComponent(Component component)
{
_components.Add(component);
}
}

View File

@@ -0,0 +1,5 @@
namespace Ghost.Engine.Models;
internal class LaunchArgument
{
}

View File

@@ -0,0 +1,5 @@
namespace Ghost.Engine.Models;
public class Scene
{
}