Updating ProjectBrowser

This commit is contained in:
2026-02-04 19:08:18 +09:00
parent 59991f47d5
commit eadd13931f
30 changed files with 382 additions and 139 deletions

View File

@@ -4,7 +4,6 @@
<ResourceDictionary Source="ms-appx:///Ghost.Editor.Core/Controls/BasicInput/PropertyField.xaml" />
<ResourceDictionary Source="ms-appx:///Ghost.Editor.Core/Controls/BasicInput/Float3Field.xaml" />
<ResourceDictionary Source="ms-appx:///Ghost.Editor.Core/Controls/Internal/ComponentDataView.xaml" />
<ResourceDictionary Source="ms-appx:///Ghost.Editor.Core/Controls/Internal/NavigationTabView.xaml" />
<ResourceDictionary Source="ms-appx:///Ghost.Editor.Core/Controls/Internal/ComponentView.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>

View File

@@ -7,7 +7,7 @@ using Microsoft.UI.Xaml.Controls;
using System.Reflection;
using System.Runtime.InteropServices;
namespace Ghost.Editor.Core.Controls.Internal;
namespace Ghost.Editor.Core.Controls;
internal sealed unsafe partial class ComponentView : Control
{

View File

@@ -2,7 +2,7 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Ghost.Editor.Core.Controls.Internal">
xmlns:local="using:Ghost.Editor.Core.Controls">
<Style TargetType="local:ComponentView">
<Setter Property="Template">

View File

@@ -2,7 +2,7 @@ using Ghost.Editor.Core.Contracts;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
namespace Ghost.Editor.Controls.Internal;
namespace Ghost.Editor.Controls;
public partial class NavigationTabPage : TabViewItem, INavigationAware
{

View File

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

View File

@@ -0,0 +1,95 @@
using Ghost.Editor.Core.Utilities;
using Microsoft.UI.Xaml.Controls;
using System.Reflection;
using System.Runtime.InteropServices;
namespace Ghost.Editor.Core.Controls;
public sealed partial class ContextFlyout : MenuFlyout
{
private bool _isPopulated;
public string Tag
{
get; set;
} = string.Empty;
public ContextFlyout()
{
Opening += ContextFlyout_Opening;
}
private void PopulateContextMenu()
{
var methods = TypeCache.GetMethodsWithAttribute<ContextMenuItemAttribute>();
if (methods == null)
{
return;
}
var list = new List<(ContextMenuItemAttribute attr, MethodInfo method)>();
foreach (var method in methods)
{
var attributes = method.GetCustomAttributes(typeof(ContextMenuItemAttribute), false);
var attribute = (ContextMenuItemAttribute)attributes[0];
if (!string.Equals(attribute.Tag, Tag, StringComparison.OrdinalIgnoreCase))
{
continue;
}
list.Add((attribute, method));
}
var span = CollectionsMarshal.AsSpan(list);
span.Sort((a, b) =>
{
var result = a.attr.Group.CompareTo(b.attr.Group);
if (result == 0)
{
result = string.CompareOrdinal(a.attr.Name, b.attr.Name);
}
return result;
});
// itemContainer may be a main thread only collection (for example, ObservableCollection), we run it on the UI thread
var i = 0;
var group = 0;
while (i < list.Count)
{
var (attr, method) = list[i];
if (attr.Group != group)
{
Items.Add(new MenuFlyoutSeparator());
group = attr.Group;
}
// TODO: Group items with / in the name into submenus
var menuItem = new MenuFlyoutItem
{
Text = attr.Name
};
menuItem.Click += (_, _) =>
{
method.Invoke(null, null);
};
Items.Add(menuItem);
i++;
}
}
private async void ContextFlyout_Opening(object? sender, object e)
{
if (_isPopulated)
{
return;
}
PopulateContextMenu();
_isPopulated = true;
}
}