mirror of
https://github.com/omegaleo/Tutorials.git
synced 2026-11-09 01:53:29 +00:00
first commit
This commit is contained in:
@@ -0,0 +1,143 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Godot;
|
||||
using Tutorials.addons.custom_inspector_tools.InspectorPlugins;
|
||||
|
||||
namespace Tutorials.addons.custom_inspector_tools;
|
||||
|
||||
#if TOOLS
|
||||
[Tool]
|
||||
public partial class CustomInspectorPlugin : EditorPlugin
|
||||
{
|
||||
private readonly List<EditorInspectorPlugin> _customPlugins = new();
|
||||
private bool _pluginsBuilt = false;
|
||||
private bool _inspectorPluginsAdded = false;
|
||||
|
||||
private EditorSelection _selection;
|
||||
private bool _refreshingInspector = false;
|
||||
|
||||
public override void _EnterTree()
|
||||
{
|
||||
base._EnterTree();
|
||||
|
||||
if (!_pluginsBuilt)
|
||||
{
|
||||
BuildPlugins();
|
||||
_pluginsBuilt = true;
|
||||
}
|
||||
|
||||
_selection = EditorInterface.Singleton.GetSelection();
|
||||
_selection.SelectionChanged += OnSelectionChanged;
|
||||
}
|
||||
|
||||
private void OnSelectionChanged()
|
||||
{
|
||||
if (!_inspectorPluginsAdded)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
RefreshInspectorForCurrentSelection();
|
||||
}
|
||||
|
||||
private void RefreshInspectorForCurrentSelection()
|
||||
{
|
||||
if (_refreshingInspector)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_refreshingInspector = true;
|
||||
|
||||
try
|
||||
{
|
||||
GodotObject objToInspect = null;
|
||||
|
||||
foreach (var node in _selection.GetSelectedNodes())
|
||||
{
|
||||
if (node is not null)
|
||||
{
|
||||
objToInspect = node;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Drop old inspector UI, then rebuild for the selected object.
|
||||
EditorInterface.Singleton.InspectObject(null);
|
||||
|
||||
if (objToInspect is not null)
|
||||
{
|
||||
EditorInterface.Singleton.InspectObject(objToInspect);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_refreshingInspector = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void BuildPlugins()
|
||||
{
|
||||
_customPlugins.Clear();
|
||||
|
||||
// Add new plugins here as you create them.
|
||||
_customPlugins.Add(new TestBoxInspectorPlugin());
|
||||
}
|
||||
|
||||
public override void _EnablePlugin()
|
||||
{
|
||||
base._EnablePlugin();
|
||||
|
||||
try
|
||||
{
|
||||
if (_inspectorPluginsAdded)
|
||||
return;
|
||||
|
||||
foreach (var plugin in _customPlugins)
|
||||
{
|
||||
AddInspectorPlugin(plugin);
|
||||
GD.Print($"Added inspector plugin: {plugin.GetType().Name}");
|
||||
}
|
||||
|
||||
_inspectorPluginsAdded = true;
|
||||
|
||||
RefreshInspectorForCurrentSelection();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
GD.PrintErr($"Error enabling CustomInspectorPlugin: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
public override void _DisablePlugin()
|
||||
{
|
||||
try
|
||||
{
|
||||
RemoveAllInspectorPlugins();
|
||||
|
||||
EditorInterface.Singleton.InspectObject(null);
|
||||
RefreshInspectorForCurrentSelection();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
GD.PrintErr($"Error disabling CustomInspectorPlugin: {ex.Message}");
|
||||
}
|
||||
base._DisablePlugin();
|
||||
}
|
||||
|
||||
private void RemoveAllInspectorPlugins()
|
||||
{
|
||||
if (!_inspectorPluginsAdded)
|
||||
return;
|
||||
|
||||
foreach (var plugin in _customPlugins)
|
||||
{
|
||||
RemoveInspectorPlugin(plugin);
|
||||
GD.Print($"Removed inspector plugin: {plugin.GetType().Name}");
|
||||
}
|
||||
|
||||
_inspectorPluginsAdded = false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1 @@
|
||||
uid://dxmsbkgfqf1kh
|
||||
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
|
||||
namespace Tutorials.addons.custom_inspector_tools.InspectorPlugins;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
|
||||
public class TestBoxAttribute : Attribute
|
||||
{
|
||||
public string Identifier { get; }
|
||||
public string Text { get; }
|
||||
|
||||
public TestBoxAttribute(string identifier, string text)
|
||||
{
|
||||
Identifier = identifier;
|
||||
Text = text;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
uid://c0rv1g5ikjgrm
|
||||
@@ -0,0 +1,153 @@
|
||||
#if TOOLS
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using Godot;
|
||||
using Microsoft.VisualBasic.CompilerServices;
|
||||
|
||||
namespace Tutorials.addons.custom_inspector_tools.InspectorPlugins;
|
||||
|
||||
[Tool]
|
||||
public partial class TestBoxInspectorPlugin : EditorInspectorPlugin
|
||||
{
|
||||
private const bool DEBUG_LOGS = true;
|
||||
|
||||
// What object are we looking at?
|
||||
private ulong _currentObjectId = 0;
|
||||
|
||||
private readonly HashSet<string> _identifiersInjectedForCurrentObject = new();
|
||||
|
||||
public override bool _CanHandle(GodotObject @object)
|
||||
{
|
||||
if (@object is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var objectId = @object.GetInstanceId();
|
||||
if (DEBUG_LOGS)
|
||||
{
|
||||
GD.Print($"[TestBoxInspectorPlugin] _CanHandle called for object with ID {objectId}");
|
||||
}
|
||||
|
||||
var type = @object.GetType();
|
||||
var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
var fields = type.GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
|
||||
foreach (var property in properties)
|
||||
{
|
||||
if (property.GetCustomAttribute<TestBoxAttribute>() is not null)
|
||||
{
|
||||
if (DEBUG_LOGS)
|
||||
{
|
||||
GD.Print($"[TestBoxInspectorPlugin] Found property with TestBoxAttribute: {property.Name}");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var field in fields)
|
||||
{
|
||||
if (field.GetCustomAttribute<TestBoxAttribute>() is not null)
|
||||
{
|
||||
if (DEBUG_LOGS)
|
||||
{
|
||||
GD.Print($"[TestBoxInspectorPlugin] Found field with TestBoxAttribute: {field.Name}");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool _ParseProperty(GodotObject @object, Variant.Type type, string name, PropertyHint hintType, string hintString,
|
||||
PropertyUsageFlags usageFlags, bool wide)
|
||||
{
|
||||
if (@object is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var objectId = @object.GetInstanceId();
|
||||
|
||||
if (_currentObjectId != objectId)
|
||||
{
|
||||
_currentObjectId = objectId;
|
||||
_identifiersInjectedForCurrentObject.Clear();
|
||||
|
||||
if (DEBUG_LOGS)
|
||||
{
|
||||
GD.Print($"[TestBoxInspectorPlugin] New object selected with ID {_currentObjectId}. Cleared injected identifiers.");
|
||||
}
|
||||
}
|
||||
|
||||
var objectType = @object.GetType();
|
||||
const BindingFlags flags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic;
|
||||
|
||||
var property = objectType.GetProperty(name, flags);
|
||||
var field = objectType.GetField(name, flags);
|
||||
|
||||
if (property is null && field is null && name.Contains("_"))
|
||||
{
|
||||
// Godot mangles property names with underscores, so we need to check for that.
|
||||
var unmangledName = name.Replace("_", "");
|
||||
property = objectType.GetProperty(unmangledName, flags);
|
||||
field = objectType.GetField(unmangledName, flags);
|
||||
}
|
||||
|
||||
var member = (MemberInfo)property ?? field;
|
||||
|
||||
if (member is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var testBoxAttribute = member.GetCustomAttribute<TestBoxAttribute>();
|
||||
if (testBoxAttribute is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_identifiersInjectedForCurrentObject.Add(testBoxAttribute.Identifier))
|
||||
{
|
||||
if (DEBUG_LOGS)
|
||||
{
|
||||
GD.Print($"[TestBoxInspectorPlugin] Injecting TestBox for identifier '{testBoxAttribute.Identifier}' on object ID {_currentObjectId}");
|
||||
}
|
||||
|
||||
AddCustomControl(TestBoxControl.Create(testBoxAttribute.Text));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (DEBUG_LOGS)
|
||||
{
|
||||
GD.Print($"[TestBoxInspectorPlugin] Already injected TestBox for identifier '{testBoxAttribute.Identifier}' on object ID {_currentObjectId}. Skipping.");
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[Tool]
|
||||
public partial class TestBoxControl : PanelContainer
|
||||
{
|
||||
public static TestBoxControl Create(string text)
|
||||
{
|
||||
var control = new TestBoxControl();
|
||||
control.SizeFlagsHorizontal = SizeFlags.ExpandFill;
|
||||
|
||||
var label = new RichTextLabel()
|
||||
{
|
||||
Text = text,
|
||||
FitContent = true,
|
||||
ScrollActive = false,
|
||||
BbcodeEnabled = true,
|
||||
SizeFlagsHorizontal = SizeFlags.ExpandFill
|
||||
};
|
||||
control.AddChild(label);
|
||||
return control;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1 @@
|
||||
uid://t0xwv6w6ue8v
|
||||
@@ -0,0 +1,6 @@
|
||||
[plugin]
|
||||
name="Custom Inspector Tools"
|
||||
description="Custom Inspector Tools"
|
||||
author="omegaleo"
|
||||
version="1.0"
|
||||
script="CustomInspectorPlugin.cs"
|
||||
Reference in New Issue
Block a user