BITKit/Src/Unity/Scripts/UX/ContextMenu/UXContextMenu.cs

187 lines
5.3 KiB
C#

using System;
using System.Collections.Generic;
using System.ComponentModel.Design;
using UnityEngine;
using UnityEngine.UIElements;
using UnityEngine.InputSystem;
using BITKit.Mod;
using Object = UnityEngine.Object;
namespace BITKit.UX.Internal
{
public class ContextMenu : IContextMenu
{
public virtual VisualElement GetVisualElement()
{
return new VisualElement();
}
}
public class Label : ContextMenu
{
public Label(string text)
{
_text = text;
}
private readonly string _text;
public override VisualElement GetVisualElement()
{
UnityEngine.UIElements.Label label = new(_text);
return label;
}
}
[Serializable]
public class TestContextMenu
{
public void Execute()
{
ContextMenuBuilder
.Create()
.BuildText("Tools")
.BuildAction("Log", () => Debug.Log(nameof(ContextMenu)))
.Build();
}
}
public class Button : ContextMenu
{
public Button(string text, params Action[] actions)
{
_text = text;
foreach (var x in actions)
{
_action += x;
}
}
private readonly string _text;
private readonly Action _action;
public override VisualElement GetVisualElement()
{
UnityEngine.UIElements.Button button = new();
button.clicked += _action;
button.text = _text;
return button;
}
}
}
namespace BITKit.UX
{
public interface IContextMenu
{
VisualElement GetVisualElement();
}
public static class ContextMenuBuilderExtensions
{
public static ContextMenuBuilder BuildText(this ContextMenuBuilder self, string text)
{
self.Add(new Internal.Label(text));
return self;
}
public static ContextMenuBuilder BuildAction(this ContextMenuBuilder self, string text, Action action)
{
self.Add(new Internal.Button(text, action, self.Execute));
return self;
}
}
public class ContextMenuBuilder
{
private readonly List<IContextMenu> _contexts = new();
public event Action OnExecuted;
private ContextMenuBuilder()
{
}
public static ContextMenuBuilder Create()
{
return new();
}
public void Build()
{
UXContextMenu.Singleton.Create(this);
}
internal void Execute()
{
OnExecuted?.Invoke();
}
public void Add(IContextMenu x) => _contexts.Add(x);
public IEnumerable<IContextMenu> GetContextMenus() => _contexts.ToArray();
}
public class UXContextMenu:IDisposable
{
internal static UXContextMenu Singleton;
private readonly IUXService _uxService;
private VisualElement _root;
private VisualElement _container;
private bool _isInitialized = false;
public UXContextMenu(IUXService uxService)
{
_uxService = uxService;
Singleton = this;
uxService.OnPanelChanged += OnPanelChanged;
InitializeAsync();
}
private async void InitializeAsync()
{
var go = new GameObject(nameof(UXContextMenu));
Object.DontDestroyOnLoad(go);
var document = go.AddComponent<UIDocument>();
document.sortingOrder = 1;
try
{
var panelSettings =await ModService.LoadAsset<PanelSettings>("ux_panel_settings");
document.panelSettings = panelSettings;
}
catch (Exception)
{
BIT4Log.Warning<UXService>("未找到ux_panel_settings");
throw;
}
document.visualTreeAsset = await ModService.LoadAsset<VisualTreeAsset>("ui_context_menu");
_root = document.rootVisualElement;
_container = _root.Q<VisualElement>("menu-container");
_root.Q("background-image").RegisterCallback<MouseDownEvent>(_ =>
{
Close();
});
_isInitialized = true;
Close();
}
public void Create(ContextMenuBuilder builder)
{
var pos = Mouse.current.position.ReadValue();
pos.y = Screen.height - pos.y;
pos = RuntimePanelUtils.ScreenToPanel(_root.panel, pos);
_container.style.position = Position.Absolute;
_container.style.left = pos.x;
_container.style.top = pos.y;
_container.Clear();
_root.SetActive(true);
foreach (var context in builder.GetContextMenus())
{
_container.Add(context.GetVisualElement());
}
builder.OnExecuted += Close;
}
private void Close()
{
if(_isInitialized is false)return;
_container.Clear();
_root.SetActive(false);
}
public void Dispose()
{
_uxService.OnPanelChanged -= OnPanelChanged;
}
private void OnPanelChanged(IUXPanel arg1, IUXPanel arg2)
{
Close();
}
}
}