Update editor
@@ -1,6 +0,0 @@
|
||||
namespace Ghost.Data.Repository;
|
||||
|
||||
internal class AssetsRepository
|
||||
{
|
||||
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
using Ghost.Core;
|
||||
|
||||
namespace Ghost.Editor.Core.AppState;
|
||||
|
||||
internal partial class AppStateMachine : IDisposable, IAsyncDisposable
|
||||
{
|
||||
private Dictionary<StateKey, Lazy<IAppState>> _states = new();
|
||||
private IAppState? _current;
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
public void RegisterState(StateKey key, Func<IAppState> stateFactory)
|
||||
{
|
||||
_states[key] = new(stateFactory);
|
||||
}
|
||||
|
||||
public async Task<Result> TransitionToAsync(StateKey stateKey, object? parameter = null)
|
||||
{
|
||||
var previous = _current;
|
||||
if (!_states.TryGetValue(stateKey, out var next))
|
||||
{
|
||||
return Result.Failure($"State '{stateKey}' not found.");
|
||||
}
|
||||
|
||||
Result result;
|
||||
if (previous != null)
|
||||
{
|
||||
result = await previous.OnExitingAsync();
|
||||
if (result.IsFailure)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
result = await next.Value.OnEnteringAsync(parameter);
|
||||
if (result.IsFailure)
|
||||
{
|
||||
if (previous != null)
|
||||
{
|
||||
await previous.OnEnteredAsync(parameter);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
if (previous != null)
|
||||
{
|
||||
result = await previous.OnExitedAsync();
|
||||
if (result.IsFailure)
|
||||
{
|
||||
await next.Value.OnExitedAsync();
|
||||
await previous.OnEnteredAsync(parameter);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
result = await next.Value.OnEnteredAsync(parameter);
|
||||
if (result.IsFailure)
|
||||
{
|
||||
await next.Value.OnExitedAsync();
|
||||
|
||||
if (previous != null)
|
||||
{
|
||||
await previous.OnEnteredAsync(parameter);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
_current = next.Value;
|
||||
|
||||
return Result.Success();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
DisposeAsync().AsTask().Wait();
|
||||
}
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_states.Clear();
|
||||
if (_current != null)
|
||||
{
|
||||
await _current.OnExitingAsync();
|
||||
await _current.OnExitedAsync();
|
||||
}
|
||||
|
||||
_current = null;
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
using Ghost.Core;
|
||||
|
||||
namespace Ghost.Editor.Core.AppState;
|
||||
|
||||
internal interface IAppState
|
||||
{
|
||||
/// <summary>
|
||||
/// Called when exiting the state.
|
||||
/// </summary>
|
||||
public ValueTask<Result> 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 ValueTask<Result> OnEnteringAsync(object? parameter);
|
||||
|
||||
/// <summary>
|
||||
/// Called when exiting the state, specifically for pose transitions.
|
||||
/// </summary>
|
||||
public ValueTask<Result> 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 ValueTask<Result> OnEnteredAsync(object? parameter);
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
namespace Ghost.Editor.Core.AppState;
|
||||
|
||||
internal enum StateKey
|
||||
{
|
||||
None,
|
||||
Landing,
|
||||
EngineEditor,
|
||||
}
|
||||
@@ -208,7 +208,7 @@ public static partial class AssetDatabase
|
||||
/// <summary>
|
||||
/// Move an asset to a new location by path.
|
||||
/// </summary>
|
||||
/// <param name="oldPath">Current path of the asset.</param>
|
||||
/// <param name="oldPath">CurrentApplication path of the asset.</param>
|
||||
/// <param name="newPath">New path for the asset (relative or absolute).</param>
|
||||
/// <returns>Result indicating success or failure.</returns>
|
||||
public static ValueTask<Result> MoveAssetAsync(string oldPath, string newPath, CancellationToken token = default)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Data.Services;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Text.Json;
|
||||
|
||||
@@ -26,7 +25,7 @@ public static partial class AssetDatabase
|
||||
return Result<string>.Failure("AssetsDirectory not initialized");
|
||||
}
|
||||
|
||||
var cacheDir = Path.Combine(AssetsDirectory.Parent!.FullName, ProjectService.CACHE_FOLDER, "ImportedAssets");
|
||||
var cacheDir = Path.Combine(AssetsDirectory.Parent!.FullName, EditorApplication.CACHES_FOLDER_NAME, "ImportedAssets");
|
||||
if (!Directory.Exists(cacheDir))
|
||||
{
|
||||
Directory.CreateDirectory(cacheDir);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Data.Services;
|
||||
using Microsoft.Data.Sqlite;
|
||||
using System.Text.Json;
|
||||
|
||||
@@ -19,7 +18,7 @@ public static partial class AssetDatabase
|
||||
throw new InvalidOperationException("AssetsDirectory is not set. Initialize() must be called first.");
|
||||
}
|
||||
|
||||
var dbPath = Path.Combine(AssetsDirectory.Parent!.FullName, ProjectService.CACHE_FOLDER, "AssetDatabase.db");
|
||||
var dbPath = Path.Combine(AssetsDirectory.Parent!.FullName, EditorApplication.CACHES_FOLDER_NAME, "AssetDatabase.db");
|
||||
var cacheDir = Path.GetDirectoryName(dbPath);
|
||||
if (!Directory.Exists(cacheDir))
|
||||
{
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Data.Services;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
@@ -92,12 +91,7 @@ public static partial class AssetDatabase
|
||||
s_initialized = true;
|
||||
}
|
||||
|
||||
if (ProjectService.CurrentProject.Metadata == null)
|
||||
{
|
||||
throw new InvalidOperationException("Project metadata is not initialized. Ensure that the project is loaded before accessing the AssetDatabase.");
|
||||
}
|
||||
|
||||
AssetsDirectory = new DirectoryInfo(Path.Combine(Path.GetDirectoryName(ProjectService.CurrentProject.Path)!, ProjectService.ASSETS_FOLDER));
|
||||
AssetsDirectory = new DirectoryInfo(Path.Combine(EditorApplication.CurrentProjectPath, EditorApplication.ASSETS_FOLDER_NAME));
|
||||
|
||||
s_commandChannel = Channel.CreateUnbounded<AssetCommand>(new UnboundedChannelOptions
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
|
||||
namespace Ghost.Editor.Core.Inspector;
|
||||
namespace Ghost.Editor.Core.Contracts;
|
||||
|
||||
public interface IInspectable
|
||||
{
|
||||
32
Ghost.Editor.Core/Contracts/IInspectorService.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
namespace Ghost.Editor.Core.Contracts;
|
||||
|
||||
public class InspectorSelectionChangedEventArgs : EventArgs
|
||||
{
|
||||
public object? Source
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public IInspectable? Selected
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public InspectorSelectionChangedEventArgs(object? source, IInspectable? selected)
|
||||
{
|
||||
Source = source;
|
||||
Selected = selected;
|
||||
}
|
||||
}
|
||||
|
||||
public interface IInspectorService
|
||||
{
|
||||
IInspectable? Selected
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
event EventHandler<InspectorSelectionChangedEventArgs> OnSelectionChanged;
|
||||
|
||||
void SetSelected(IInspectable? inspectable, object? source);
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using CommunityToolkit.WinUI.Behaviors;
|
||||
using Ghost.Editor.Core.Notifications;
|
||||
|
||||
namespace Ghost.Editor.Core.Notifications;
|
||||
namespace Ghost.Editor.Core.Contracts;
|
||||
|
||||
public interface INotificationService
|
||||
{
|
||||
12
Ghost.Editor.Core/Contracts/IPreviewService.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
namespace Ghost.Editor.Core.Contracts;
|
||||
|
||||
public enum IconSize
|
||||
{
|
||||
Small,
|
||||
Large
|
||||
}
|
||||
|
||||
public interface IPreviewService
|
||||
{
|
||||
string GetIconPath(string path, bool isDirectory, IconSize size);
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace Ghost.Editor.Core.Progress;
|
||||
namespace Ghost.Editor.Core.Contracts;
|
||||
|
||||
public interface IProgressService
|
||||
{
|
||||
39
Ghost.Editor.Core/EditorApplication.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using Microsoft.UI.Xaml;
|
||||
|
||||
namespace Ghost.Editor.Core;
|
||||
|
||||
public static class EditorApplication
|
||||
{
|
||||
public const string ASSETS_FOLDER_NAME = "Assets";
|
||||
public const string CACHES_FOLDER_NAME = "Caches";
|
||||
|
||||
private static IServiceProvider? s_serviceProvider;
|
||||
private static string s_currentProjectPath = string.Empty;
|
||||
private static string s_currentProjectName = string.Empty;
|
||||
|
||||
internal static Application CurrentApplication => Application.Current;
|
||||
internal static string CurrentProjectPath => s_currentProjectPath;
|
||||
internal static string CurrentProjectName => s_currentProjectName;
|
||||
|
||||
internal static void Initialize(IServiceProvider serviceProvider, string projectPath, string projectName)
|
||||
{
|
||||
s_serviceProvider = serviceProvider;
|
||||
s_currentProjectPath = projectPath;
|
||||
s_currentProjectName = projectName;
|
||||
}
|
||||
|
||||
public static T GetService<T>()
|
||||
where T : class
|
||||
{
|
||||
if (s_serviceProvider?.GetService(typeof(T)) is not T service)
|
||||
{
|
||||
throw new ArgumentException($"{typeof(T)} needs to be registered in ConfigureServices.");
|
||||
}
|
||||
|
||||
return service;
|
||||
}
|
||||
|
||||
internal static void Shutdown()
|
||||
{
|
||||
}
|
||||
}
|
||||
28
Ghost.Editor.Core/EditorInjectionAttribute.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
namespace Ghost.Editor.Core;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = false, Inherited = false)]
|
||||
public class EditorInjectionAttribute : Attribute
|
||||
{
|
||||
public enum ServiceLifetime
|
||||
{
|
||||
Singleton,
|
||||
Transient,
|
||||
Scoped
|
||||
}
|
||||
|
||||
public ServiceLifetime Lifetime
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public Type? ImplementationType
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public EditorInjectionAttribute(ServiceLifetime lifetime, Type? implementationType = null)
|
||||
{
|
||||
Lifetime = lifetime;
|
||||
ImplementationType = implementationType;
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,6 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ghost.Data\Ghost.Data.csproj" />
|
||||
<ProjectReference Include="..\Ghost.Core\Ghost.Core.csproj" />
|
||||
<ProjectReference Include="..\Ghost.Engine\Ghost.Engine.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
namespace Ghost.Editor.Core.Inspector;
|
||||
|
||||
internal interface IInspectorService
|
||||
{
|
||||
public IInspectable? SelectedInspectable
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public event Action? OnSelectionChanged;
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
namespace Ghost.Editor.Core.Inspector;
|
||||
|
||||
public class InspectorService : IInspectorService
|
||||
{
|
||||
public IInspectable? SelectedInspectable
|
||||
{
|
||||
get => field;
|
||||
set
|
||||
{
|
||||
if (field != value)
|
||||
{
|
||||
field = value;
|
||||
OnSelectionChanged?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public event Action? OnSelectionChanged;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Ghost.Editor.Core.Inspector;
|
||||
using Ghost.Editor.Core.Contracts;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
21
Ghost.Editor.Core/Services/InspectorService.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using Ghost.Editor.Core.Contracts;
|
||||
|
||||
namespace Ghost.Editor.Core.Services;
|
||||
|
||||
public class InspectorService : IInspectorService
|
||||
{
|
||||
private IInspectable? _selected;
|
||||
|
||||
public IInspectable? Selected => _selected;
|
||||
|
||||
public event EventHandler<InspectorSelectionChangedEventArgs>? OnSelectionChanged;
|
||||
|
||||
public void SetSelected(IInspectable? inspectable, object? source)
|
||||
{
|
||||
if (_selected != inspectable)
|
||||
{
|
||||
_selected = inspectable;
|
||||
OnSelectionChanged?.Invoke(this, new InspectorSelectionChangedEventArgs(source, inspectable));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
using CommunityToolkit.WinUI.Behaviors;
|
||||
using Ghost.Editor.Core.Contracts;
|
||||
using Ghost.Editor.Core.Notifications;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
|
||||
namespace Ghost.Editor.Core.Notifications;
|
||||
namespace Ghost.Editor.Core.Services;
|
||||
|
||||
public class NotificationService : INotificationService
|
||||
{
|
||||
35
Ghost.Editor.Core/Services/PreviewService.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using Ghost.Editor.Core.Contracts;
|
||||
|
||||
namespace Ghost.Editor.Core.Services;
|
||||
|
||||
internal class PreviewService : IPreviewService
|
||||
{
|
||||
public string GetIconPath(string path, bool isDirectory, IconSize size)
|
||||
{
|
||||
string iconPath;
|
||||
if (isDirectory)
|
||||
{
|
||||
iconPath = "ms-appx:///Assets/EditorIcons/folder-{0}.png";
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: Generate preview icons dynamically for known file types like images, meshes, materials, etc.
|
||||
var ext = Path.GetExtension(path);
|
||||
iconPath = ext switch
|
||||
{
|
||||
".png" or ".jpg" or ".jpeg" or ".gif" or ".bmp" or ".tiff" or ".svg" => "ms-appx:///Assets/EditorIcons/image-{0}.png",
|
||||
_ => "ms-appx:///Assets/EditorIcons/document-{0}.png",
|
||||
};
|
||||
}
|
||||
|
||||
var sizeIndex = size switch
|
||||
{
|
||||
IconSize.Small => "0",
|
||||
IconSize.Large => "1",
|
||||
_ => "0"
|
||||
};
|
||||
|
||||
iconPath = string.Format(iconPath, sizeIndex);
|
||||
return iconPath;
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
using CommunityToolkit.WinUI;
|
||||
using Ghost.Editor.Core.Contracts;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ghost.Editor.Core.Progress;
|
||||
namespace Ghost.Editor.Core.Services;
|
||||
|
||||
public class ProgressService : IProgressService
|
||||
{
|
||||
@@ -1,26 +0,0 @@
|
||||
using Microsoft.UI.Xaml;
|
||||
|
||||
namespace Ghost.Editor.Core.Utilities;
|
||||
|
||||
public static class EditorApplication
|
||||
{
|
||||
private static IServiceProvider? _serviceProvider;
|
||||
|
||||
public static Application Current => Application.Current;
|
||||
|
||||
internal static void Initialize(IServiceProvider serviceProvider)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
public static T GetService<T>()
|
||||
where T : class
|
||||
{
|
||||
if (_serviceProvider?.GetService(typeof(T)) is not T service)
|
||||
{
|
||||
throw new ArgumentException($"{typeof(T)} needs to be registered in ConfigureServices within App.xaml.cs.");
|
||||
}
|
||||
|
||||
return service;
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,10 @@
|
||||
using Ghost.Data.Models;
|
||||
|
||||
namespace Ghost.Editor.Core.Utilities;
|
||||
|
||||
internal static class FileExtensions
|
||||
{
|
||||
public const string META_FILE_EXTENSION = ".gmeta";
|
||||
|
||||
public const string PROJECT_FILE_EXTENSION = "." + ProjectMetadata.PROJECT_FILE_EXTENSION_NAME;
|
||||
public const string PROJECT_FILE_EXTENSION = ".gproj";
|
||||
public const string TEMPLATE_FILE_EXTENSION = ".gtmpl";
|
||||
public const string SCENE_FILE_EXTENSION = ".gscene";
|
||||
public const string ASSET_FILE_EXTENSION = ".gasset";
|
||||
|
||||
@@ -29,6 +29,12 @@ public static class TypeCache
|
||||
s_types = loadableTypes.Select(t => t.GetTypeInfo()).ToArray();
|
||||
}
|
||||
|
||||
internal static void Init()
|
||||
{
|
||||
// Intentionally left blank.
|
||||
// This method exists to force the static constructor to run.
|
||||
}
|
||||
|
||||
public static Type[] GetTypes()
|
||||
{
|
||||
return s_types;
|
||||
|
||||
@@ -1,30 +1,67 @@
|
||||
using Ghost.Data.Resources;
|
||||
using Ghost.Data.Services;
|
||||
using Ghost.Editor.Core;
|
||||
using Ghost.Editor.Core.Utilities;
|
||||
using Ghost.Editor.Models;
|
||||
using Ghost.Engine;
|
||||
using Microsoft.UI.Xaml;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Ghost.Editor;
|
||||
|
||||
internal static class ActivationHandler
|
||||
{
|
||||
private static void FolderInitialization()
|
||||
public static LaunchArguments ParseArguments(ReadOnlySpan<char> args)
|
||||
{
|
||||
if (!Directory.Exists(DataPath.s_applicationDataFolder))
|
||||
var arguments = new LaunchArguments();
|
||||
var properties = typeof(LaunchArguments).GetProperties();
|
||||
var split = args.Split(' ');
|
||||
|
||||
while (split.MoveNext())
|
||||
{
|
||||
Directory.CreateDirectory(DataPath.s_applicationDataFolder);
|
||||
var range = split.Current;
|
||||
var arg = args[range.Start..range.End];
|
||||
if (arg.Length > 2)
|
||||
{
|
||||
if (arg[0] == '-' && arg[1] == '-')
|
||||
{
|
||||
var argName = arg[2..];
|
||||
foreach (var property in properties)
|
||||
{
|
||||
var propName = property.Name;
|
||||
var attr = property.GetCustomAttributes<ArgumentNameAttribute>(false).FirstOrDefault();
|
||||
if (attr != null)
|
||||
{
|
||||
propName = attr.Name;
|
||||
}
|
||||
|
||||
if (!Directory.Exists(DataPath.s_projectTemplateFolder))
|
||||
if (argName.Equals(propName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
Directory.CreateDirectory(DataPath.s_projectTemplateFolder);
|
||||
if (split.MoveNext())
|
||||
{
|
||||
var valueRange = split.Current;
|
||||
var value = args[valueRange.Start..valueRange.End];
|
||||
var convertedValue = Convert.ChangeType(value.ToString(), property.PropertyType);
|
||||
|
||||
property.SetValue(arguments, convertedValue);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Handle(LaunchActivatedEventArgs args)
|
||||
{
|
||||
FolderInitialization();
|
||||
ProjectService.EnsureDefaultTemplate();
|
||||
return arguments;
|
||||
}
|
||||
|
||||
EditorApplication.Initialize(((App)(Application.Current)).Host.Services);
|
||||
public static async Task HandleAsync(LaunchArguments args)
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
TypeCache.Init();
|
||||
((EngineCore)App.GetService<IEngineContext>()).Init();
|
||||
});
|
||||
|
||||
// TODO: Initialize other subsystems here.
|
||||
// await Task.Delay(10000); // Wait 10 seconds to simulate work.
|
||||
}
|
||||
}
|
||||
@@ -9,9 +9,8 @@
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
|
||||
<ResourceDictionary Source="ms-appx:///Microsoft.UI.Xaml/DensityStyles/Compact.xaml" />
|
||||
<ResourceDictionary Source="/Themes/Generic.xaml" />
|
||||
<core:ControlsDictionary />
|
||||
<ResourceDictionary Source="/Themes/Override.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</Application.Resources>
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Editor.Core.AppState;
|
||||
using Ghost.Editor.Core.Inspector;
|
||||
using Ghost.Editor.Core.Notifications;
|
||||
using Ghost.Editor.Core.Progress;
|
||||
using Ghost.Editor.Utilities;
|
||||
using Ghost.Editor.Core;
|
||||
using Ghost.Editor.Core.Contracts;
|
||||
using Ghost.Editor.Core.Services;
|
||||
using Ghost.Editor.View.Pages.EngineEditor;
|
||||
using Ghost.Editor.View.Windows;
|
||||
using Ghost.Editor.ViewModels.Controls;
|
||||
using Ghost.Editor.ViewModels.Pages.EngineEditor;
|
||||
using Ghost.Editor.ViewModels.Windows;
|
||||
using Ghost.Engine;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.UI.Xaml;
|
||||
|
||||
// To learn more about WinUI, the WinUI project structure,
|
||||
// and more about our project templates, see: http://aka.ms/winui-project-info.
|
||||
using System.Diagnostics;
|
||||
using WinUIEx;
|
||||
|
||||
namespace Ghost.Editor;
|
||||
|
||||
@@ -53,13 +56,32 @@ public partial class App : Application
|
||||
UseContentRoot(AppContext.BaseDirectory).
|
||||
ConfigureServices((context, services) =>
|
||||
{
|
||||
HostHelper.AddLandingScope(context, services);
|
||||
HostHelper.AddEngineScope(context, services);
|
||||
services.AddSingleton<IEngineContext, EngineCore>();
|
||||
|
||||
services.AddSingleton<AppStateMachine>();
|
||||
services.AddSingleton<INotificationService, NotificationService>();
|
||||
services.AddSingleton<IProgressService, ProgressService>();
|
||||
services.AddSingleton<IInspectorService, InspectorService>();
|
||||
services.AddSingleton<IPreviewService, PreviewService>();
|
||||
|
||||
services.AddSingleton<EngineEditorViewModel>();
|
||||
|
||||
services.AddTransient<ProjectBrowserViewModel>();
|
||||
|
||||
#region Should be deleted
|
||||
services.AddTransient<ScenePage>();
|
||||
|
||||
services.AddTransient<HierarchyPage>();
|
||||
services.AddTransient<HierarchyViewModel>();
|
||||
|
||||
services.AddTransient<ProjectPage>();
|
||||
services.AddTransient<ProjectViewModel>();
|
||||
|
||||
services.AddTransient<ConsolePage>();
|
||||
services.AddTransient<ConsoleViewModel>();
|
||||
|
||||
services.AddTransient<InspectorPage>();
|
||||
services.AddTransient<InspectorViewModel>();
|
||||
#endregion
|
||||
})
|
||||
.Build();
|
||||
|
||||
@@ -81,32 +103,55 @@ public partial class App : Application
|
||||
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);
|
||||
|
||||
var arguments = ActivationHandler.ParseArguments("--project-path F:/GhostProject/Test2 --project-name Test2"); // args.Arguments
|
||||
if (!arguments.IsValid())
|
||||
{
|
||||
Exit();
|
||||
return;
|
||||
}
|
||||
|
||||
EditorApplication.Initialize(Host.Services, arguments.ProjectPath, arguments.ProjectName);
|
||||
|
||||
var splashWindow = new SplashWindow();
|
||||
splashWindow.Activate();
|
||||
Window = splashWindow;
|
||||
|
||||
await Host.StartAsync();
|
||||
ActivationHandler.Handle(args);
|
||||
await ActivationHandler.HandleAsync(arguments);
|
||||
|
||||
var stateMachine = GetService<AppStateMachine>();
|
||||
stateMachine.RegisterState(StateKey.Landing, () => new LandingState());
|
||||
stateMachine.RegisterState(StateKey.EngineEditor, () => new EditorState());
|
||||
splashWindow.Hide();
|
||||
|
||||
await stateMachine.TransitionToAsync(StateKey.Landing);
|
||||
var editorWindow = new EngineEditorWindow();
|
||||
editorWindow.Activate();
|
||||
Window = editorWindow;
|
||||
|
||||
splashWindow.Close();
|
||||
}
|
||||
|
||||
private void OnClosed(object? sender, WindowEventArgs args)
|
||||
{
|
||||
try
|
||||
{
|
||||
Host.StopAsync().GetAwaiter().GetResult();
|
||||
Host.Dispose();
|
||||
|
||||
EditorApplication.Shutdown();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debugger.BreakForUserUnhandledException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void App_UnhandledException(object sender, Microsoft.UI.Xaml.UnhandledExceptionEventArgs e)
|
||||
{
|
||||
Logger.LogError(e.Exception);
|
||||
#if DEBUG
|
||||
Debugger.BreakForUserUnhandledException(e.Exception);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
BIN
Ghost.Editor/Assets/EditorIcons/document-0.png
Normal file
|
After Width: | Height: | Size: 453 B |
BIN
Ghost.Editor/Assets/EditorIcons/document-1.png
Normal file
|
After Width: | Height: | Size: 869 B |
BIN
Ghost.Editor/Assets/EditorIcons/folder-0.png
Normal file
|
After Width: | Height: | Size: 465 B |
BIN
Ghost.Editor/Assets/EditorIcons/folder-1.png
Normal file
|
After Width: | Height: | Size: 884 B |
BIN
Ghost.Editor/Assets/EditorIcons/image-0.png
Normal file
|
After Width: | Height: | Size: 727 B |
BIN
Ghost.Editor/Assets/EditorIcons/image-1.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 433 B After Width: | Height: | Size: 580 B |
|
Before Width: | Height: | Size: 583 B After Width: | Height: | Size: 825 B |
|
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.0 KiB |
|
Before Width: | Height: | Size: 852 B After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.6 KiB |
BIN
Ghost.Editor/Assets/icon.ico
Normal file
|
After Width: | Height: | Size: 170 KiB |
22
Ghost.Editor/Assets/icon.svg
Normal file
@@ -0,0 +1,22 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 48 48" width="48px" height="48px">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
fill: url(#Безымянный_градиент_199);
|
||||
}
|
||||
|
||||
.cls-2 {
|
||||
fill: #f7c13a;
|
||||
}
|
||||
</style>
|
||||
<linearGradient id="Безымянный_градиент_199" data-name="Безымянный градиент 199" x1="11.80415" y1="-22.237" x2="29.6085" y2="45.263" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#6d6d6d"/>
|
||||
<stop offset="0.12552" stop-color="#626262"/>
|
||||
<stop offset="0.987" stop-color="#464646"/>
|
||||
<stop offset="0.998" stop-color="#454545"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect class="cls-1" x="6" y="6" width="36" height="36" rx="2"/>
|
||||
<path class="cls-2" d="M22,27v1.5a.5.5,0,0,1-.5.5H17a8,8,0,0,1-8-8V19.5a.5.5,0,0,1,.5-.5h4.00612a.501.501,0,0,1,.49758.49688C14.05815,23.13441,14.70582,26,15.5,26c.73344,0,1.33761-2.43083,1.46835-5.65979a.50624.50624,0,0,1,.74437-.42711A7.99072,7.99072,0,0,1,22,27Z"/>
|
||||
<path class="cls-2" d="M39,19.5V21a8,8,0,0,1-8,8H26.5a.5.5,0,0,1-.5-.5V27a7.99072,7.99072,0,0,1,4.28728-7.0869.50624.50624,0,0,1,.74437.42711C31.16239,23.56917,31.76656,26,32.5,26c.79418,0,1.44185-2.86559,1.4963-6.50312A.501.501,0,0,1,34.49388,19H38.5A.5.5,0,0,1,39,19.5Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 580 B After Width: | Height: | Size: 580 B |
|
Before Width: | Height: | Size: 580 B After Width: | Height: | Size: 580 B |
|
Before Width: | Height: | Size: 825 B After Width: | Height: | Size: 825 B |
|
Before Width: | Height: | Size: 825 B After Width: | Height: | Size: 825 B |
|
Before Width: | Height: | Size: 6.0 KiB After Width: | Height: | Size: 6.0 KiB |
|
Before Width: | Height: | Size: 6.0 KiB After Width: | Height: | Size: 6.0 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
@@ -1,38 +0,0 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Ghost.Editor.Core.Contracts;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Navigation;
|
||||
|
||||
namespace Ghost.Editor.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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Data.Models;
|
||||
using Ghost.Data.Services;
|
||||
using Ghost.Editor.Core.AssetHandle;
|
||||
using Ghost.Editor.View.Windows;
|
||||
using Ghost.Engine;
|
||||
|
||||
namespace Ghost.Editor.Core.AppState;
|
||||
|
||||
internal class EditorState : IAppState
|
||||
{
|
||||
private EngineEditorWindow? _window;
|
||||
private EngineCore? _engineCore;
|
||||
|
||||
public ValueTask<Result> OnExitingAsync()
|
||||
{
|
||||
if (App.Window == _window)
|
||||
{
|
||||
App.Window = null;
|
||||
}
|
||||
|
||||
_engineCore?.Dispose();
|
||||
|
||||
return ValueTask.FromResult(Result.Success());
|
||||
}
|
||||
|
||||
public ValueTask<Result> OnEnteringAsync(object? parameter)
|
||||
{
|
||||
if (parameter is not ProjectMetadataInfo metadataInfo)
|
||||
{
|
||||
return ValueTask.FromResult(Result.Failure("Invalid parameter for entering EditorState."));
|
||||
}
|
||||
|
||||
ProjectService.CurrentProject = metadataInfo;
|
||||
|
||||
_engineCore = App.GetService<EngineCore>();
|
||||
_engineCore.Init();
|
||||
|
||||
_window = App.GetService<EngineEditorWindow>();
|
||||
_window.Activate();
|
||||
|
||||
App.Window = _window;
|
||||
|
||||
return ValueTask.FromResult(Result.Success());
|
||||
}
|
||||
|
||||
public ValueTask<Result> OnExitedAsync()
|
||||
{
|
||||
_window?.Close();
|
||||
_window = null;
|
||||
|
||||
return ValueTask.FromResult(Result.Success());
|
||||
}
|
||||
|
||||
public async ValueTask<Result> OnEnteredAsync(object? parameter)
|
||||
{
|
||||
await AssetDatabase.Initialize();
|
||||
return Result.Success();
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
using Ghost.Core;
|
||||
using Ghost.Editor.View.Windows;
|
||||
|
||||
namespace Ghost.Editor.Core.AppState;
|
||||
|
||||
internal class LandingState : IAppState
|
||||
{
|
||||
private LandingWindow? _window;
|
||||
|
||||
public ValueTask<Result> OnExitingAsync()
|
||||
{
|
||||
if (App.Window == _window)
|
||||
{
|
||||
App.Window = null;
|
||||
}
|
||||
|
||||
return ValueTask.FromResult(Result.Success());
|
||||
}
|
||||
|
||||
public ValueTask<Result> OnEnteringAsync(object? parameter)
|
||||
{
|
||||
_window = App.GetService<LandingWindow>();
|
||||
_window.Activate();
|
||||
|
||||
App.Window = _window;
|
||||
|
||||
return ValueTask.FromResult(Result.Success());
|
||||
}
|
||||
|
||||
public ValueTask<Result> OnExitedAsync()
|
||||
{
|
||||
_window?.Close();
|
||||
_window = null;
|
||||
|
||||
return ValueTask.FromResult(Result.Success());
|
||||
}
|
||||
|
||||
public ValueTask<Result> OnEnteredAsync(object? parameter)
|
||||
{
|
||||
return ValueTask.FromResult(Result.Success());
|
||||
}
|
||||
}
|
||||
@@ -45,7 +45,106 @@
|
||||
<ProjectReference Include="..\Ghost.Entities\Ghost.Entities.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="View\Pages\Landing\CreateProjectPage.xaml">
|
||||
<Content Update="Assets\EditorIcons\document-0.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\EditorIcons\document-1.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\EditorIcons\image-0.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\EditorIcons\image-1.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\icon.ico">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\icon.scale-100.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\icon.scale-125.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\icon.scale-150.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\icon.scale-200.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\icon.scale-400.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\icon.svg">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\icon.targetsize-16.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\icon.targetsize-16_altform-lightunplated.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\icon.targetsize-16_altform-unplated.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\icon.targetsize-24.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\icon.targetsize-24_altform-lightunplated.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\icon.targetsize-24_altform-unplated.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\icon.targetsize-256.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\icon.targetsize-256_altform-lightunplated.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\icon.targetsize-256_altform-unplated.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\icon.targetsize-32.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\icon.targetsize-32_altform-lightunplated.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\icon.targetsize-32_altform-unplated.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\icon.targetsize-48.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\icon.targetsize-48_altform-lightunplated.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\icon.targetsize-48_altform-unplated.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<None Update="Assets\icon.svg">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Update="Assets\EditorIcons\folder-0.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Assets\EditorIcons\folder-1.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Assets\icon.ico">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Themes\Generic.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="View\Windows\SplashWindow.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
@@ -54,11 +153,6 @@
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="View\Pages\Landing\OpenProjectPage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="View\Pages\EngineEditor\InspectorPage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
@@ -79,11 +173,6 @@
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Themes\Override.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="View\Windows\EngineEditorWindow.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
|
||||
35
Ghost.Editor/Models/LaunchArguments.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
namespace Ghost.Editor.Models;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
internal sealed class ArgumentNameAttribute : Attribute
|
||||
{
|
||||
public string Name
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public ArgumentNameAttribute(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
}
|
||||
|
||||
internal class LaunchArguments
|
||||
{
|
||||
[ArgumentName("project-path")]
|
||||
public string ProjectPath
|
||||
{
|
||||
get; set;
|
||||
} = string.Empty;
|
||||
|
||||
[ArgumentName("project-name")]
|
||||
public string ProjectName
|
||||
{
|
||||
get; set;
|
||||
} = string.Empty;
|
||||
|
||||
public bool IsValid()
|
||||
{
|
||||
return Directory.Exists(ProjectPath) && !string.IsNullOrWhiteSpace(ProjectName);
|
||||
}
|
||||
}
|
||||
@@ -36,7 +36,7 @@
|
||||
<uap:VisualElements
|
||||
DisplayName="GhostEngine"
|
||||
Description="GhostEngine"
|
||||
Square150x150Logo="Assets\Square150x150Logo.png" Square44x44Logo="Assets\Icon.png" BackgroundColor="transparent">
|
||||
Square150x150Logo="Assets\Square150x150Logo.png" Square44x44Logo="Assets\icon.png" BackgroundColor="transparent">
|
||||
<uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png" >
|
||||
</uap:DefaultTile >
|
||||
<uap:SplashScreen Image="Assets\SplashScreen.png" />
|
||||
|
||||
54
Ghost.Editor/Themes/Generic.xaml
Normal file
@@ -0,0 +1,54 @@
|
||||
<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"
|
||||
xmlns:internal="using:Ghost.Editor.Controls.Internal"
|
||||
xmlns:local="using:Ghost.Editor.Core">
|
||||
|
||||
<ResourceDictionary.ThemeDictionaries>
|
||||
<ResourceDictionary x:Key="Dark">
|
||||
<StaticResource x:Key="TabViewItemHeaderBackgroundSelected" ResourceKey="ControlFillColorSecondaryBrush" />
|
||||
<StaticResource x:Key="TabViewBackground" ResourceKey="AcrylicBackgroundFillColorBaseBrush" />
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary x:Key="Light">
|
||||
<StaticResource x:Key="TabViewItemHeaderBackgroundSelected" ResourceKey="ControlFillColorSecondaryBrush" />
|
||||
<StaticResource x:Key="TabViewBackground" ResourceKey="AcrylicBackgroundFillColorBaseBrush" />
|
||||
</ResourceDictionary>
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
|
||||
<!-- Compact sizing -->
|
||||
<Style TargetType="TextBlock">
|
||||
<Setter Property="FontSize" Value="12" />
|
||||
</Style>
|
||||
<x:Double x:Key="ControlContentThemeFontSize">12</x:Double>
|
||||
<x:Double x:Key="ContentControlFontSize">12</x:Double>
|
||||
<x:Double x:Key="TextControlThemeMinHeight">24</x:Double>
|
||||
<Thickness x:Key="TextControlThemePadding">2,2,6,1</Thickness>
|
||||
<x:Double x:Key="ListViewItemMinHeight">32</x:Double>
|
||||
<x:Double x:Key="TreeViewItemMinHeight">24</x:Double>
|
||||
<x:Double x:Key="TreeViewItemMultiSelectCheckBoxMinHeight">24</x:Double>
|
||||
<x:Double x:Key="TreeViewItemPresenterMargin">0</x:Double>
|
||||
<x:Double x:Key="TreeViewItemPresenterPadding">0</x:Double>
|
||||
<Thickness x:Key="TimePickerHostPadding">0,1,0,2</Thickness>
|
||||
<Thickness x:Key="DatePickerHostPadding">0,1,0,2</Thickness>
|
||||
<Thickness x:Key="DatePickerHostMonthPadding">9,0,0,1</Thickness>
|
||||
<Thickness x:Key="ComboBoxEditableTextPadding">10,0,30,0</Thickness>
|
||||
<x:Double x:Key="ComboBoxMinHeight">24</x:Double>
|
||||
<Thickness x:Key="ComboBoxPadding">12,1,0,3</Thickness>
|
||||
<x:Double x:Key="NavigationViewItemOnLeftMinHeight">32</x:Double>
|
||||
|
||||
<!-- Control override -->
|
||||
<Style TargetType="internal:NavigationTabView">
|
||||
<Setter Property="TabWidthMode" Value="Compact" />
|
||||
</Style>
|
||||
<Style TargetType="NumberBox" />
|
||||
|
||||
<!-- Named Style -->
|
||||
<Style
|
||||
x:Key="ToolbarButton"
|
||||
BasedOn="{StaticResource SubtleButtonStyle}"
|
||||
TargetType="Button" />
|
||||
|
||||
<!-- Named Resource -->
|
||||
<x:Double x:Key="ToolbarIconSize">12</x:Double>
|
||||
</ResourceDictionary>
|
||||
@@ -1,20 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="using:Microsoft.UI.Xaml.Controls"
|
||||
xmlns:internal="using:Ghost.Editor.Controls.Internal">
|
||||
<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="internal:NavigationTabView">
|
||||
<Setter Property="TabWidthMode" Value="Compact" />
|
||||
</Style>
|
||||
<Style TargetType="NumberBox" />
|
||||
</ResourceDictionary>
|
||||
@@ -0,0 +1,26 @@
|
||||
using Ghost.Editor.Core.Contracts;
|
||||
using Ghost.Editor.Models;
|
||||
using Microsoft.UI.Xaml.Data;
|
||||
|
||||
namespace Ghost.Editor.Utilities.Converters;
|
||||
|
||||
public partial class ExplorerItemToIconUriConverter : IValueConverter
|
||||
{
|
||||
private readonly IPreviewService _previewService = App.GetService<IPreviewService>();
|
||||
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
if (value is ExplorerItem item)
|
||||
{
|
||||
var path = _previewService.GetIconPath(item.FullName, item.IsDirectory, IconSize.Small);
|
||||
return new Uri(path);
|
||||
}
|
||||
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
using Ghost.Data.Services;
|
||||
using Ghost.Editor.View.Pages.EngineEditor;
|
||||
using Ghost.Editor.View.Pages.Landing;
|
||||
using Ghost.Editor.View.Windows;
|
||||
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.Editor.Utilities;
|
||||
|
||||
internal static partial class HostHelper
|
||||
{
|
||||
public static void AddLandingScope(HostBuilderContext context, IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton<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.AddSingleton<EngineEditorWindow>();
|
||||
services.AddSingleton<EngineEditorViewModel>();
|
||||
|
||||
services.AddTransient<ScenePage>();
|
||||
|
||||
services.AddTransient<HierarchyPage>();
|
||||
services.AddTransient<HierarchyViewModel>();
|
||||
|
||||
services.AddTransient<ProjectPage>();
|
||||
services.AddTransient<ProjectViewModel>();
|
||||
|
||||
services.AddTransient<ConsolePage>();
|
||||
services.AddTransient<ConsoleViewModel>();
|
||||
|
||||
services.AddTransient<InspectorPage>();
|
||||
services.AddTransient<InspectorViewModel>();
|
||||
}
|
||||
}
|
||||
224
Ghost.Editor/View/Controls/ProjectBrowser.xaml
Normal file
@@ -0,0 +1,224 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<UserControl
|
||||
x:Class="Ghost.Editor.Controls.ProjectBrowser"
|
||||
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:ctc="using:CommunityToolkit.WinUI.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:Ghost.Editor.Controls"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:model="using:Ghost.Editor.Models"
|
||||
xmlns:sys="using:System"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<UserControl.Resources>
|
||||
<converter:ExplorerItemToIconUriConverter x:Key="ExplorerItemToIconUriConverter" />
|
||||
</UserControl.Resources>
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Toolbar -->
|
||||
<Grid
|
||||
Height="36"
|
||||
Padding="4"
|
||||
Background="{ThemeResource CardBackgroundFillColorSecondaryBrush}"
|
||||
BorderBrush="{ThemeResource DividerStrokeColorDefaultBrush}"
|
||||
BorderThickness="0,0,0,1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<StackPanel Grid.Column="0" Orientation="Horizontal">
|
||||
<Button Style="{StaticResource ToolbarButton}">
|
||||
<Button.Content>
|
||||
<FontIcon FontSize="{StaticResource ToolbarIconSize}" Glyph="" />
|
||||
</Button.Content>
|
||||
<Button.Flyout>
|
||||
<MenuFlyout>
|
||||
<MenuFlyoutItem Text="Folder" />
|
||||
<MenuFlyoutItem Text="Script" />
|
||||
<MenuFlyoutSubItem Text="Rendering">
|
||||
<MenuFlyoutItem Text="Material" />
|
||||
<MenuFlyoutItem Text="Volume Profile" />
|
||||
</MenuFlyoutSubItem>
|
||||
</MenuFlyout>
|
||||
</Button.Flyout>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Column="2" Orientation="Horizontal">
|
||||
<AutoSuggestBox Width="250" QueryIcon="Find" />
|
||||
<AppBarSeparator />
|
||||
<Button Style="{StaticResource ToolbarButton}">
|
||||
<Button.Content>
|
||||
<FontIcon FontSize="{StaticResource ToolbarIconSize}" Glyph="" />
|
||||
</Button.Content>
|
||||
<Button.Flyout>
|
||||
<MenuFlyout>
|
||||
<ToggleMenuFlyoutItem Text="Animation" />
|
||||
<ToggleMenuFlyoutItem Text="Audio" />
|
||||
<ToggleMenuFlyoutItem Text="Material" />
|
||||
<ToggleMenuFlyoutItem Text="Script" />
|
||||
<ToggleMenuFlyoutItem Text="Texture" />
|
||||
</MenuFlyout>
|
||||
</Button.Flyout>
|
||||
</Button>
|
||||
<Button Style="{StaticResource ToolbarButton}">
|
||||
<Button.Content>
|
||||
<FontIcon FontSize="{StaticResource ToolbarIconSize}" Glyph="" />
|
||||
</Button.Content>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<!-- Conent Viewer -->
|
||||
<Grid Grid.Row="1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Border
|
||||
Grid.Column="0"
|
||||
Width="200"
|
||||
Padding="4,0,0,0"
|
||||
Background="{ThemeResource CardBackgroundFillColorSecondaryBrush}"
|
||||
BorderBrush="{ThemeResource DividerStrokeColorDefaultBrush}"
|
||||
BorderThickness="0,0,1,0">
|
||||
<TreeView
|
||||
x:Name="PART_DirectoriesView"
|
||||
ItemsSource="{x:Bind ViewModel.Directories, Mode=OneWay}"
|
||||
SelectionChanged="PART_DirectoriesView_SelectionChanged"
|
||||
SelectionMode="Single">
|
||||
<TreeView.ItemTemplate>
|
||||
<DataTemplate x:DataType="model:ExplorerItem">
|
||||
<TreeViewItem ItemsSource="{x:Bind Children}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="4">
|
||||
<!-- TODO: Open/Close folder icon based on state -->
|
||||
<FontIcon
|
||||
VerticalAlignment="Center"
|
||||
FontSize="12"
|
||||
Glyph="" />
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{x:Bind Name}"
|
||||
TextTrimming="CharacterEllipsis" />
|
||||
</StackPanel>
|
||||
</TreeViewItem>
|
||||
</DataTemplate>
|
||||
</TreeView.ItemTemplate>
|
||||
</TreeView>
|
||||
</Border>
|
||||
|
||||
<Grid Grid.Column="1">
|
||||
<Grid.RowDefinitions>
|
||||
<!--<RowDefinition Height="Auto" />-->
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!--<Border
|
||||
Grid.Row="0"
|
||||
Height="24"
|
||||
Background="{ThemeResource LayerFillColorDefaultBrush}"
|
||||
BorderBrush="{ThemeResource DividerStrokeColorDefaultBrush}"
|
||||
BorderThickness="0,0,0,1">
|
||||
<BreadcrumbBar />
|
||||
</Border>-->
|
||||
|
||||
<ItemsView
|
||||
x:Name="PART_FilesView"
|
||||
Grid.Row="0"
|
||||
Padding="8"
|
||||
DoubleTapped="PART_FilesView_DoubleTapped"
|
||||
IsDoubleTapEnabled="True"
|
||||
ItemsSource="{x:Bind ViewModel.Files, Mode=OneWay}"
|
||||
SelectionChanged="PART_FilesView_SelectionChanged"
|
||||
SelectionMode="Single">
|
||||
<ItemsView.ItemTemplate>
|
||||
<DataTemplate x:DataType="model:ExplorerItem">
|
||||
<ItemContainer>
|
||||
<Grid
|
||||
Padding="8"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
RowSpacing="4">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid.ContextFlyout>
|
||||
<MenuFlyout>
|
||||
<MenuFlyoutItem Text="Open" />
|
||||
<MenuFlyoutItem Text="Rename" />
|
||||
<MenuFlyoutItem Text="Delete" />
|
||||
<MenuFlyoutItem Text="Show in Explorer" />
|
||||
</MenuFlyout>
|
||||
</Grid.ContextFlyout>
|
||||
|
||||
<ctc:ConstrainedBox Grid.Row="0" AspectRatio="1:1">
|
||||
<Image HorizontalAlignment="Center">
|
||||
<Image.Source>
|
||||
<BitmapImage DecodePixelWidth="48" UriSource="{x:Bind Converter={StaticResource ExplorerItemToIconUriConverter}}" />
|
||||
</Image.Source>
|
||||
</Image>
|
||||
</ctc:ConstrainedBox>
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
HorizontalAlignment="Center"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{x:Bind Name}"
|
||||
TextTrimming="CharacterEllipsis"
|
||||
TextWrapping="NoWrap" />
|
||||
</Grid>
|
||||
</ItemContainer>
|
||||
</DataTemplate>
|
||||
</ItemsView.ItemTemplate>
|
||||
<ItemsView.Layout>
|
||||
<UniformGridLayout
|
||||
ItemsStretch="Fill"
|
||||
MinColumnSpacing="4"
|
||||
MinItemWidth="72"
|
||||
MinRowSpacing="4" />
|
||||
</ItemsView.Layout>
|
||||
<ItemsView.ContextFlyout>
|
||||
<MenuFlyout>
|
||||
<MenuFlyoutSubItem Text="Create">
|
||||
<MenuFlyoutItem Text="Folder" />
|
||||
<MenuFlyoutItem Text="Script" />
|
||||
<MenuFlyoutSubItem Text="Rendering">
|
||||
<MenuFlyoutItem Text="Material" />
|
||||
<MenuFlyoutItem Text="Volume Profile" />
|
||||
</MenuFlyoutSubItem>
|
||||
</MenuFlyoutSubItem>
|
||||
<MenuFlyoutItem Text="Show in Explorer" />
|
||||
<MenuFlyoutSeparator />
|
||||
<MenuFlyoutItem Text="Refresh" />
|
||||
<MenuFlyoutItem Text="Reimport All" />
|
||||
</MenuFlyout>
|
||||
</ItemsView.ContextFlyout>
|
||||
</ItemsView>
|
||||
|
||||
<Border
|
||||
Grid.Row="1"
|
||||
Height="24"
|
||||
Padding="8,2"
|
||||
Background="{ThemeResource LayerFillColorDefaultBrush}"
|
||||
BorderBrush="{ThemeResource DividerStrokeColorDefaultBrush}"
|
||||
BorderThickness="0,1,0,0">
|
||||
<StackPanel>
|
||||
<TextBlock Text="{x:Bind sys:String.Format('{0} items', ViewModel.Files.Count), Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
122
Ghost.Editor/View/Controls/ProjectBrowser.xaml.cs
Normal file
@@ -0,0 +1,122 @@
|
||||
using Ghost.Editor.Core.Contracts;
|
||||
using Ghost.Editor.Models;
|
||||
using Ghost.Editor.ViewModels.Controls;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
|
||||
namespace Ghost.Editor.Controls;
|
||||
|
||||
internal sealed partial class ProjectBrowser : UserControl
|
||||
{
|
||||
private readonly IInspectorService _inspectorService;
|
||||
private bool _isUpdatingSelection = false;
|
||||
|
||||
public ProjectBrowserViewModel ViewModel
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public ProjectBrowser()
|
||||
{
|
||||
_inspectorService = App.GetService<IInspectorService>();
|
||||
ViewModel = App.GetService<ProjectBrowserViewModel>();
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
Loaded += ProjectBrowser_Loaded;
|
||||
Unloaded += ProjectBrowser_Unloaded;
|
||||
}
|
||||
|
||||
private void ProjectBrowser_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_inspectorService.OnSelectionChanged += _inspectorService_OnSelectionChanged;
|
||||
}
|
||||
|
||||
private void ProjectBrowser_Unloaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_inspectorService.OnSelectionChanged -= _inspectorService_OnSelectionChanged;
|
||||
}
|
||||
|
||||
private void _inspectorService_OnSelectionChanged(object? sender, InspectorSelectionChangedEventArgs e)
|
||||
{
|
||||
if (e.Source is not ProjectBrowserViewModel)
|
||||
{
|
||||
PART_FilesView.DeselectAll();
|
||||
PART_DirectoriesView.SelectedNodes.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
private void PART_DirectoriesView_SelectionChanged(TreeView sender, TreeViewSelectionChangedEventArgs args)
|
||||
{
|
||||
if (_isUpdatingSelection)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isUpdatingSelection = true;
|
||||
|
||||
PART_FilesView.DeselectAll();
|
||||
if (args.AddedItems.Count > 0 && args.AddedItems[0] is ExplorerItem selectedItem)
|
||||
{
|
||||
ViewModel.SelectedItem = selectedItem;
|
||||
ViewModel.NavigateToDirectory(selectedItem.FullName);
|
||||
}
|
||||
|
||||
_isUpdatingSelection = false;
|
||||
}
|
||||
|
||||
private void PART_FilesView_SelectionChanged(ItemsView sender, ItemsViewSelectionChangedEventArgs args)
|
||||
{
|
||||
if (_isUpdatingSelection)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isUpdatingSelection = true;
|
||||
|
||||
PART_DirectoriesView.SelectedNodes.Clear();
|
||||
if (PART_FilesView.SelectedItem is ExplorerItem selectedItem)
|
||||
{
|
||||
ViewModel.SelectedItem = selectedItem;
|
||||
}
|
||||
|
||||
_isUpdatingSelection = false;
|
||||
}
|
||||
|
||||
private async void PART_FilesView_DoubleTapped(object sender, Microsoft.UI.Xaml.Input.DoubleTappedRoutedEventArgs e)
|
||||
{
|
||||
if (PART_FilesView.SelectedItem is ExplorerItem selectedItem)
|
||||
{
|
||||
_isUpdatingSelection = true;
|
||||
|
||||
PART_FilesView.DeselectAll();
|
||||
PART_DirectoriesView.SelectedNodes.Clear();
|
||||
|
||||
// NOTE: There is bug that the hover state of the item may remain when navigating to another folder.
|
||||
// Which causes incorrect selection (double click a folder -> directories view select target folder -> files view select the item that has the same index as the double clicked one and the hover visual stay remain from last level)
|
||||
// Not sure if this is a WinUI bug or something else. This may because of the virtualization of the ItemsView.
|
||||
// The core issue is not sure why PART_FilesView_SelectionChanged been triggered after NavigateToDirectory is already finished. And this only happens after the first double click navigation (first time is always fine).
|
||||
|
||||
// HACK: Wait a moment to let the ui clear it's state, otherwise the bug above will happen because of the virtualization.
|
||||
await Task.Delay(100);
|
||||
|
||||
ViewModel.SelectedItem = selectedItem;
|
||||
|
||||
var navigatedItem = ViewModel.OpenSelected();
|
||||
if (navigatedItem.Item1 != null)
|
||||
{
|
||||
if (navigatedItem.Item2 == 0)
|
||||
{
|
||||
PART_DirectoriesView.SelectedItem = navigatedItem.Item1;
|
||||
}
|
||||
else if (navigatedItem.Item2 == 1)
|
||||
{
|
||||
var index = ViewModel.Files.IndexOf(navigatedItem.Item1);
|
||||
PART_FilesView.Select(index);
|
||||
}
|
||||
}
|
||||
|
||||
_isUpdatingSelection = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
using Ghost.Editor.Controls.Internal;
|
||||
using Ghost.Editor.Core.Inspector;
|
||||
using Ghost.Editor.Core.Contracts;
|
||||
using Ghost.Editor.Core.SceneGraph;
|
||||
using Ghost.Editor.ViewModels.Pages.EngineEditor;
|
||||
using Microsoft.UI.Xaml;
|
||||
@@ -38,11 +38,11 @@ internal sealed partial class HierarchyPage : NavigationTabPage
|
||||
{
|
||||
if (args.AddedItems.Count > 0 && args.AddedItems[0] is IInspectable inspectable)
|
||||
{
|
||||
_inspectorService.SelectedInspectable = inspectable;
|
||||
_inspectorService.SetSelected(inspectable, ViewModel);
|
||||
}
|
||||
else
|
||||
{
|
||||
_inspectorService.SelectedInspectable = null;
|
||||
_inspectorService.SetSelected(null, ViewModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,142 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Page
|
||||
x:Class="Ghost.Editor.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.Core.Controls"
|
||||
xmlns:local="using:Ghost.Editor.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>
|
||||
@@ -1,32 +0,0 @@
|
||||
using Ghost.Editor.ViewModels.Pages.Landing;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Navigation;
|
||||
|
||||
namespace Ghost.Editor.View.Pages.Landing;
|
||||
|
||||
internal sealed partial class CreateProjectPage : Page
|
||||
{
|
||||
public CreateProjectViewModel ViewModel
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public CreateProjectPage()
|
||||
{
|
||||
ViewModel = App.GetService<CreateProjectViewModel>();
|
||||
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedTo(e);
|
||||
ViewModel.OnNavigatedTo(e.Parameter);
|
||||
}
|
||||
|
||||
protected override void OnNavigatedFrom(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedFrom(e);
|
||||
ViewModel.OnNavigatedFrom();
|
||||
}
|
||||
}
|
||||
@@ -1,165 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Page
|
||||
x:Class="Ghost.Editor.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.Editor.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>
|
||||
@@ -1,72 +0,0 @@
|
||||
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.Editor.View.Pages.Landing;
|
||||
|
||||
internal sealed partial class OpenProjectPage : Page
|
||||
{
|
||||
public OpenProjectViewModel ViewModel
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public OpenProjectPage()
|
||||
{
|
||||
ViewModel = App.GetService<OpenProjectViewModel>();
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedTo(e);
|
||||
ViewModel.OnNavigatedTo(e.Parameter);
|
||||
}
|
||||
|
||||
protected override 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 Task.Yield();
|
||||
await ViewModel.OpenProjectAsync(project);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:behaviors="using:CommunityToolkit.WinUI.Behaviors"
|
||||
xmlns:controls="using:CommunityToolkit.WinUI.Controls"
|
||||
xmlns:controls1="using:Ghost.Editor.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:ee="using:Ghost.Editor.View.Pages.EngineEditor"
|
||||
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
|
||||
@@ -12,15 +13,13 @@
|
||||
xmlns:local="using:Ghost.Editor.View.Windows"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:winex="using:WinUIEx"
|
||||
Activated="WindowEx_Activated"
|
||||
Closed="WindowEx_Closed"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Window.SystemBackdrop>
|
||||
<MicaBackdrop />
|
||||
</Window.SystemBackdrop>
|
||||
|
||||
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
|
||||
<Grid Loaded="MainGrid_Loaded" Unloaded="MainGrid_Unloaded">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
@@ -29,29 +28,21 @@
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Titlebar -->
|
||||
<StackPanel
|
||||
<TitleBar
|
||||
x:Name="PART_TitleBar"
|
||||
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>
|
||||
Background="{ThemeResource AcrylicBackgroundFillColorBaseBrush}"
|
||||
Subtitle="Ghost Engine">
|
||||
<TitleBar.IconSource>
|
||||
<ImageIconSource ImageSource="ms-appx:///Assets/icon.targetsize-48.png" />
|
||||
</TitleBar.IconSource>
|
||||
</TitleBar>
|
||||
|
||||
<!-- Toolbar -->
|
||||
<Grid Grid.Row="1" Margin="4,4">
|
||||
<Grid
|
||||
Grid.Row="1"
|
||||
Padding="4,0,4,4"
|
||||
Background="{ThemeResource AcrylicBackgroundFillColorBaseBrush}">
|
||||
<controls:TabbedCommandBar>
|
||||
<controls:TabbedCommandBar.MenuItems>
|
||||
<controls:TabbedCommandBarItem Header="Home">
|
||||
@@ -132,7 +123,7 @@
|
||||
<TabViewItem.IconSource>
|
||||
<FontIconSource Glyph="" />
|
||||
</TabViewItem.IconSource>
|
||||
<ee:ProjectPage />
|
||||
<controls1:ProjectBrowser />
|
||||
</TabViewItem>
|
||||
<TabViewItem Header="Console">
|
||||
<TabViewItem.IconSource>
|
||||
@@ -148,7 +139,7 @@
|
||||
<Grid
|
||||
Grid.Row="3"
|
||||
Height="25"
|
||||
Background="{ThemeResource SolidBackgroundFillColorBaseAltBrush}">
|
||||
Background="{ThemeResource SmokeFillColorDefaultBrush}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
@@ -201,6 +192,7 @@
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<!-- Info and Progress -->
|
||||
<Grid Grid.Row="0" Grid.RowSpan="4">
|
||||
<InfoBar
|
||||
x:Name="InfoBar"
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
using Ghost.Data.Resources;
|
||||
using Ghost.Editor.Core.Notifications;
|
||||
using Ghost.Editor.Core.Progress;
|
||||
using Ghost.Editor.Core;
|
||||
using Ghost.Editor.Core.Contracts;
|
||||
using Ghost.Editor.Core.Services;
|
||||
using Ghost.Editor.ViewModels.Windows;
|
||||
using Ghost.Engine.Resources;
|
||||
using System.Diagnostics;
|
||||
using Windows.ApplicationModel;
|
||||
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.Editor.View.Windows;
|
||||
/// <summary>
|
||||
/// An empty window that can be used on its own or navigated to within a Frame.
|
||||
@@ -29,24 +27,26 @@ internal sealed partial class EngineEditorWindow : WindowEx
|
||||
_notificationService = (NotificationService)App.GetService<INotificationService>();
|
||||
_progressService = (ProgressService)App.GetService<IProgressService>();
|
||||
|
||||
AppWindow.SetIcon(AssetsPath.s_appIconPath);
|
||||
Title = EngineData.ENGINE_NAME;
|
||||
ExtendsContentIntoTitleBar = true;
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
AppWindow.SetIcon(Path.Combine(AppContext.BaseDirectory, "Assets/icon.ico"));
|
||||
Title = "Ghost Engine";
|
||||
ExtendsContentIntoTitleBar = true;
|
||||
|
||||
SetTitleBar(PART_TitleBar);
|
||||
this.CenterOnScreen();
|
||||
}
|
||||
|
||||
private void WindowEx_Activated(object sender, Microsoft.UI.Xaml.WindowActivatedEventArgs args)
|
||||
private void MainGrid_Loaded(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
|
||||
{
|
||||
Bindings.Update();
|
||||
PART_TitleBar.Title = EditorApplication.CurrentProjectName;
|
||||
PART_TitleBar.Subtitle = $"Ghost Engine {Package.Current.Id.Version.Major}.{Package.Current.Id.Version.Minor}.{Package.Current.Id.Version.Build}";
|
||||
|
||||
_notificationService.SetReference(InfoBar, NotificationQueue);
|
||||
_progressService.SetReference(ProgressBarContainer);
|
||||
}
|
||||
|
||||
private void WindowEx_Closed(object sender, Microsoft.UI.Xaml.WindowEventArgs args)
|
||||
private void MainGrid_Unloaded(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
|
||||
{
|
||||
_notificationService.ClearReference();
|
||||
_progressService.ClearReference();
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<winex:WindowEx
|
||||
x:Class="Ghost.Editor.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.Editor.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>
|
||||
@@ -1,59 +0,0 @@
|
||||
using Ghost.Data.Resources;
|
||||
using Ghost.Editor.Core.Notifications;
|
||||
using Ghost.Editor.View.Pages.Landing;
|
||||
using Ghost.Engine.Resources;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Media.Animation;
|
||||
using WinUIEx;
|
||||
|
||||
namespace Ghost.Editor.View.Windows;
|
||||
|
||||
internal sealed partial class LandingWindow : WindowEx
|
||||
{
|
||||
private readonly NotificationService _notificationService;
|
||||
|
||||
private int _previousSelectedIndex;
|
||||
|
||||
public LandingWindow()
|
||||
{
|
||||
_notificationService = (NotificationService)App.GetService<INotificationService>();
|
||||
|
||||
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)
|
||||
{
|
||||
_notificationService.SetReference(InfoBar, NotificationQueue);
|
||||
}
|
||||
|
||||
private void WindowEx_Closed(object sender, Microsoft.UI.Xaml.WindowEventArgs args)
|
||||
{
|
||||
_notificationService.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, null, new SlideNavigationTransitionInfo() { Effect = slideNavigationTransitionEffect });
|
||||
|
||||
_previousSelectedIndex = currentSelectedIndex;
|
||||
}
|
||||
}
|
||||
79
Ghost.Editor/View/Windows/SplashWindow.xaml
Normal file
@@ -0,0 +1,79 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<winex:WindowEx
|
||||
x:Class="Ghost.Editor.View.Windows.SplashWindow"
|
||||
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.Editor.View.Windows"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:winex="using:WinUIEx"
|
||||
Title="SplashWindow"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<winex:WindowEx.SystemBackdrop>
|
||||
<MicaBackdrop />
|
||||
</winex:WindowEx.SystemBackdrop>
|
||||
|
||||
<Grid Margin="32,32,32,16" Loaded="MainGrid_Loaded">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid Grid.Row="0" ColumnSpacing="24">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="1.2*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Grid Grid.Column="0" CornerRadius="8">
|
||||
<Image
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Source="C:\Users\Misaki\Downloads\Screenshot 2024-07-20 035047.png"
|
||||
Stretch="UniformToFill" />
|
||||
</Grid>
|
||||
<Grid Grid.Column="1">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
Style="{StaticResource TitleLargeTextBlockStyle}"
|
||||
Text="Ghost Engine" />
|
||||
<TextBlock
|
||||
x:Name="VersionTextBlock"
|
||||
Grid.Row="1"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Style="{StaticResource BodyLargeStrongTextBlockStyle}" />
|
||||
<TextBlock
|
||||
Grid.Row="2"
|
||||
Margin="0,32,0,0"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Text="An open-source, modern game engine designed for flexibility and performance."
|
||||
TextWrapping="WrapWholeWords" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
<ProgressBar
|
||||
Grid.Row="1"
|
||||
Margin="0,24,0,0"
|
||||
IsIndeterminate="True" />
|
||||
<TextBlock
|
||||
x:Name="LoadingTextBlock"
|
||||
Grid.Row="2"
|
||||
Margin="0,4,0,0"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
|
||||
<TextBlock
|
||||
x:Name="CopyrightTextBlock"
|
||||
Grid.Row="3"
|
||||
Margin="0,24,0,0"
|
||||
HorizontalAlignment="Center"
|
||||
Foreground="{ThemeResource TextFillColorTertiaryBrush}"
|
||||
Style="{StaticResource CaptionTextBlockStyle}" />
|
||||
</Grid>
|
||||
</winex:WindowEx>
|
||||
33
Ghost.Editor/View/Windows/SplashWindow.xaml.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using Ghost.Editor.Core;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Windows.ApplicationModel;
|
||||
using Windows.Storage;
|
||||
using WinUIEx;
|
||||
|
||||
namespace Ghost.Editor.View.Windows;
|
||||
|
||||
/// <summary>
|
||||
/// An empty window that can be used on its own or navigated to within a Frame.
|
||||
/// </summary>
|
||||
internal sealed partial class SplashWindow : WindowEx
|
||||
{
|
||||
public SplashWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
IsResizable = false;
|
||||
IsMaximizable = false;
|
||||
IsMinimizable = false;
|
||||
ExtendsContentIntoTitleBar = true;
|
||||
|
||||
//this.CenterOnScreen(750, 400);
|
||||
}
|
||||
|
||||
private void MainGrid_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var version = Package.Current.Id.Version;
|
||||
VersionTextBlock.Text = $"Version {version.Major}.{version.Minor}.{version.Build}";
|
||||
LoadingTextBlock.Text = $"Loading {EditorApplication.CurrentProjectName}...";
|
||||
CopyrightTextBlock.Text = $"Copyright © {DateTime.Now.Year} Ghost Engine. All rights reserved.";
|
||||
}
|
||||
}
|
||||
108
Ghost.Editor/ViewModels/Controls/ProjectBrowserViewModel.cs
Normal file
@@ -0,0 +1,108 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Ghost.Editor.Core;
|
||||
using Ghost.Editor.Core.AssetHandle;
|
||||
using Ghost.Editor.Core.Contracts;
|
||||
using Ghost.Editor.Core.Utilities;
|
||||
using Ghost.Editor.Models;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Ghost.Editor.ViewModels.Controls;
|
||||
|
||||
internal partial class ProjectBrowserViewModel : ObservableObject
|
||||
{
|
||||
private readonly IInspectorService _inspectorService;
|
||||
|
||||
private readonly Dictionary<string, ExplorerItem> _pathToDirectoryItemMap = new();
|
||||
private ExplorerItem? _selectedItem;
|
||||
|
||||
public ObservableCollection<ExplorerItem> Directories
|
||||
{
|
||||
get;
|
||||
} = new();
|
||||
|
||||
public ObservableCollection<ExplorerItem> Files
|
||||
{
|
||||
get;
|
||||
} = new();
|
||||
|
||||
public ExplorerItem? SelectedItem
|
||||
{
|
||||
get => _selectedItem;
|
||||
set
|
||||
{
|
||||
// TODO: Resolve inspector by reading metadata from selected asset
|
||||
_selectedItem = value;
|
||||
}
|
||||
}
|
||||
|
||||
public ProjectBrowserViewModel(IInspectorService inspectorService)
|
||||
{
|
||||
_inspectorService = inspectorService;
|
||||
|
||||
var assetsRootItem = new ExplorerItem(EditorApplication.ASSETS_FOLDER_NAME, Path.Combine(EditorApplication.CurrentProjectPath, EditorApplication.ASSETS_FOLDER_NAME), true);
|
||||
LoadSubFolderRecursive(assetsRootItem);
|
||||
|
||||
Directories.Add(assetsRootItem);
|
||||
}
|
||||
|
||||
private void LoadSubFolderRecursive(ExplorerItem parentItem)
|
||||
{
|
||||
foreach (var directory in Directory.EnumerateDirectories(parentItem.FullName))
|
||||
{
|
||||
var item = new ExplorerItem(Path.GetFileName(directory), directory, true);
|
||||
LoadSubFolderRecursive(item);
|
||||
|
||||
_pathToDirectoryItemMap[directory] = item;
|
||||
|
||||
parentItem.Children ??= new();
|
||||
parentItem.Children.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
internal void NavigateToDirectory(string? path)
|
||||
{
|
||||
Files.Clear();
|
||||
|
||||
if (!Directory.Exists(path))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var directory in Directory.EnumerateDirectories(path))
|
||||
{
|
||||
var directoryItem = new ExplorerItem(Path.GetFileName(directory), directory, true);
|
||||
Files.Add(directoryItem);
|
||||
}
|
||||
|
||||
foreach (var file in Directory.EnumerateFiles(path))
|
||||
{
|
||||
if (Path.GetExtension(file) == FileExtensions.META_FILE_EXTENSION)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var fileItem = new ExplorerItem(Path.GetFileName(file), file, false);
|
||||
Files.Add(fileItem);
|
||||
}
|
||||
}
|
||||
|
||||
internal (ExplorerItem?, int) OpenSelected()
|
||||
{
|
||||
if (SelectedItem == null)
|
||||
{
|
||||
return (null, 0);
|
||||
}
|
||||
|
||||
if (SelectedItem.IsDirectory)
|
||||
{
|
||||
NavigateToDirectory(SelectedItem.FullName);
|
||||
SelectedItem = _pathToDirectoryItemMap[SelectedItem.FullName];
|
||||
return (SelectedItem, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
AssetDatabase.OpenAsset(SelectedItem.FullName);
|
||||
return (null, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Ghost.Editor.Core.Contracts;
|
||||
using Ghost.Editor.Core.Inspector;
|
||||
|
||||
namespace Ghost.Editor.ViewModels.Pages.EngineEditor;
|
||||
|
||||
@@ -16,7 +15,7 @@ internal partial class InspectorViewModel(IInspectorService inspectorService) :
|
||||
public void OnNavigatedTo(object? parameter)
|
||||
{
|
||||
inspectorService.OnSelectionChanged += OnSelectionChanged;
|
||||
Inspectable = inspectorService.SelectedInspectable;
|
||||
Inspectable = inspectorService.Selected;
|
||||
}
|
||||
|
||||
public void OnNavigatedFrom()
|
||||
@@ -25,8 +24,8 @@ internal partial class InspectorViewModel(IInspectorService inspectorService) :
|
||||
Inspectable = null;
|
||||
}
|
||||
|
||||
private void OnSelectionChanged()
|
||||
private void OnSelectionChanged(object? sender, EventArgs e)
|
||||
{
|
||||
Inspectable = inspectorService.SelectedInspectable;
|
||||
Inspectable = inspectorService.Selected;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Ghost.Data.Services;
|
||||
using Ghost.Editor.Core;
|
||||
using Ghost.Editor.Core.AssetHandle;
|
||||
using Ghost.Editor.Models;
|
||||
using System.Collections.ObjectModel;
|
||||
@@ -36,12 +36,7 @@ internal partial class ProjectViewModel : ObservableObject
|
||||
|
||||
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);
|
||||
var assetsRootItem = new ExplorerItem("Assets", Path.Combine(EditorApplication.CurrentProjectPath, EditorApplication.ASSETS_FOLDER_NAME), true);
|
||||
LoadSubFolderRecursive(ref assetsRootItem);
|
||||
|
||||
SubDirectories.Add(assetsRootItem);
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Ghost.Data.Models;
|
||||
using Ghost.Data.Services;
|
||||
using Ghost.Editor.Core.AppState;
|
||||
using Ghost.Editor.Core.Contracts;
|
||||
using Ghost.Editor.Core.Notifications;
|
||||
using Ghost.Editor.Utilities;
|
||||
using Ghost.Engine.Resources;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Ghost.Editor.ViewModels.Pages.Landing;
|
||||
|
||||
internal partial class CreateProjectViewModel(INotificationService 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", MessageType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
var result = await projectService.CreateProjectAsync(ProjectName, ProjectLocation, EngineData.EngineVersion, SelectedTemplate.Value.directory);
|
||||
if (result.IsFailure)
|
||||
{
|
||||
notificationService.ShowNotification(result.Message, MessageType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await stateService.TransitionToAsync(StateKey.EngineEditor, result.Value);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
notificationService.ShowNotification($"Failed to load project: {e.Message}", MessageType.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Ghost.Data.Models;
|
||||
using Ghost.Data.Services;
|
||||
using Ghost.Editor.Core.AppState;
|
||||
using Ghost.Editor.Core.Contracts;
|
||||
using Ghost.Editor.Core.Notifications;
|
||||
using Microsoft.UI.Xaml;
|
||||
using System.Collections.ObjectModel;
|
||||
using Windows.ApplicationModel.DataTransfer;
|
||||
using Windows.Storage;
|
||||
|
||||
namespace Ghost.Editor.ViewModels.Pages.Landing;
|
||||
|
||||
internal partial class OpenProjectViewModel(ProjectService projectService, INotificationService _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)
|
||||
{
|
||||
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()
|
||||
{
|
||||
projects.Clear();
|
||||
}
|
||||
|
||||
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.IsSuccess)
|
||||
{
|
||||
projects.Add(result.Value);
|
||||
goto CloseDropPanel;
|
||||
}
|
||||
else
|
||||
{
|
||||
errorMessage = result.Message;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
errorMessage = "Unsupported data format. Please drop a folder containing a project.";
|
||||
}
|
||||
|
||||
_notificationService.ShowNotification(errorMessage, MessageType.Error);
|
||||
|
||||
CloseDropPanel:
|
||||
DragVisibility = Visibility.Collapsed;
|
||||
UpdateEmptyPlaceHolderVisibility();
|
||||
}
|
||||
|
||||
public async Task OpenProjectAsync(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}", MessageType.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,7 @@
|
||||
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.EngineVersion}";
|
||||
|
||||
public ProjectMetadataInfo CurrentProject => ProjectService.CurrentProject;
|
||||
}
|
||||
@@ -3,7 +3,7 @@ using Misaki.HighPerformance.Jobs;
|
||||
|
||||
namespace Ghost.Engine;
|
||||
|
||||
public interface IEngineContext
|
||||
public interface IEngineContext : IDisposable
|
||||
{
|
||||
JobScheduler JobScheduler { get; }
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
<Platform Name="x86" />
|
||||
</Configurations>
|
||||
<Folder Name="/Editor/">
|
||||
<Project Path="Ghost.Data/Ghost.Data.csproj" />
|
||||
<Project Path="Ghost.Editor.Core/Ghost.Editor.Core.csproj" />
|
||||
<Project Path="Ghost.Editor/Ghost.Editor.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
|
||||