/*
┌──────────────────────────────────────────────────────────────────┐
│ Author: Ivan Murzak (https://github.com/IvanMurzak) │
│ Repository: GitHub (https://github.com/IvanMurzak/Unity-MCP) │
│ Copyright (c) 2025 Ivan Murzak │
│ Licensed under the Apache License, Version 2.0. │
│ See the LICENSE file in the project root for more information. │
└──────────────────────────────────────────────────────────────────┘
*/
#nullable enable
using System;
using com.IvanMurzak.Unity.MCP.Editor.Utils;
using com.IvanMurzak.Unity.MCP.Utils;
using Microsoft.Extensions.Logging;
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
namespace com.IvanMurzak.Unity.MCP.Editor.UI
{
///
/// Base class for MCP Editor windows providing common UXML/USS loading functionality.
///
public abstract class McpWindowBase : EditorWindow
{
protected Microsoft.Extensions.Logging.ILogger Logger { get; private set; } = null!;
protected abstract string[] WindowUxmlPaths { get; }
protected abstract string[] WindowUssPaths { get; }
protected abstract string WindowTitle { get; }
protected virtual void OnEnable()
{
Logger = UnityLoggerFactory.LoggerFactory.CreateLogger(GetType().Name);
}
protected void SetupWindowWithIcon(string? customTitle = null)
{
var icon = EditorAssetLoader.LoadAssetAtPath(EditorAssetLoader.PackageLogoIcon);
if (icon != null)
titleContent = new GUIContent(customTitle ?? WindowTitle, icon);
}
public virtual void CreateGUI()
{
rootVisualElement.Clear();
var visualTree = EditorAssetLoader.LoadAssetAtPath(WindowUxmlPaths, Logger);
if (visualTree == null)
{
Logger.LogWarning("{method} UXML template not found.", nameof(CreateGUI));
return;
}
visualTree.CloneTree(rootVisualElement);
ApplyStyleSheets(rootVisualElement);
OnGUICreated(rootVisualElement);
}
protected virtual void OnGUICreated(VisualElement root)
{
}
protected void ApplyStyleSheets(VisualElement root)
{
var sheet = EditorAssetLoader.LoadAssetAtPath(WindowUssPaths, Logger);
if (sheet == null)
{
Logger.LogWarning("{method} USS file not found.", nameof(ApplyStyleSheets));
return;
}
try
{
root.styleSheets.Add(sheet);
Logger.LogTrace("{method} Applied USS", nameof(ApplyStyleSheets));
}
catch (Exception ex)
{
Logger.LogWarning("{method} Failed to add USS: {ex}", nameof(ApplyStyleSheets), ex);
}
}
protected void UpdateItemClasses(VisualElement? itemContainer, bool isEnabled)
{
if (itemContainer == null)
return;
itemContainer.EnableInClassList("enabled", isEnabled);
itemContainer.EnableInClassList("disabled", !isEnabled);
}
public static void UpdateFoldoutState(Foldout foldout, bool expanded)
{
foldout.EnableInClassList("expanded", expanded);
foldout.EnableInClassList("collapsed", !expanded);
}
public static void EnableSmoothFoldoutTransitions(VisualElement root)
{
root.Query().ForEach(foldout =>
{
foldout.RegisterValueChangedCallback(evt =>
{
UpdateFoldoutState(foldout, evt.newValue);
});
UpdateFoldoutState(foldout, foldout.value);
});
}
}
}