Skip to content

Instantly share code, notes, and snippets.

@maluoi
Last active June 8, 2023 06:05
Show Gist options
  • Save maluoi/e0395149999c4691c5607c677d34ad6f to your computer and use it in GitHub Desktop.
Save maluoi/e0395149999c4691c5607c677d34ad6f to your computer and use it in GitHub Desktop.
A reflection based class inspector/editor for StereoKit
using System;
using System.Linq;
using System.Reflection;
namespace StereoKit.Framework
{
///////////////////////////////////////////
abstract class InspectorAttribute : Attribute
{
public abstract void Show(PropertyInfo prop, object inst);
}
///////////////////////////////////////////
class SliderAttribute : InspectorAttribute
{
public float min, max, step;
public SliderAttribute(float min, float max, float step = 0)
{
this.min = min;
this.max = max;
this.step = step;
}
public override void Show(PropertyInfo prop, object inst)
{
if (prop.PropertyType == typeof(float))
{
float val = (float)prop.GetValue(inst);
if (UI.HSlider(prop.Name, ref val, min, max, step, 0, UIConfirm.Pinch))
prop.SetValue(inst, val);
}
else if (prop.PropertyType == typeof(int))
{
float val = (float)(int)prop.GetValue(inst);
if (UI.HSlider(prop.Name, ref val, min, max, step, 0, UIConfirm.Pinch))
prop.SetValue(inst, (int)val);
}
}
}
///////////////////////////////////////////
class BrowseAttribute : InspectorAttribute
{
public string[] filters;
public BrowseAttribute(params string[] filters)
{
this.filters = filters;
}
public override void Show(PropertyInfo prop, object inst)
{
string val = (string)prop.GetValue(inst);
if (UI.Button("Browse"))
{
Platform.FilePicker(PickerMode.Open, (s) =>
{
prop.SetValue(inst, s);
}, null, filters);
}
UI.SameLine();
UI.Label(val, V.XY(UI.LayoutRemaining.x-0.02f, 0));
}
}
///////////////////////////////////////////
class Inspector
{
struct PropertyItem
{
public PropertyInfo prop;
public InspectorAttribute attribute;
}
object item;
Type itemType;
PropertyItem[] properties;
public object Item { get => item; }
public Inspector() { }
public Inspector(object item) { SetObject(item); }
private bool ValidType(Type type)
{
return type == typeof(int)
|| type == typeof(float);
}
public void SetObject(object newItem)
{
item = newItem;
itemType = newItem.GetType();
properties = itemType
.GetProperties()
.Where (p => p.GetCustomAttribute<InspectorAttribute>() != null || ValidType(p.PropertyType))
.Select(p => new PropertyItem
{
prop = p,
attribute = p.GetCustomAttribute<InspectorAttribute>()
})
.ToArray();
}
public void DrawUI()
{
int curr = 1;
foreach (PropertyItem p in properties)
{
UI.PushId(curr++);
UI.Label(p.prop.Name, new Vec2(0.06f, 0));
UI.SameLine();
if (p.attribute != null) { p.attribute.Show(p.prop, item); }
else
{
switch (p.prop.GetValue(item))
{
case string val: if (UI.Input(p.prop.Name, ref val)) { p.prop.SetValue(item, val); } break;
case int val:
{
if (UI.Button("-")) { val -= 1; p.prop.SetValue(item, val); }
UI.SameLine();
UI.Label(val.ToString());
UI.SameLine();
if (UI.Button("+")) { val += 1; p.prop.SetValue(item, val); }
}
break;
}
}
UI.PopId();
}
}
}
///////////////////////////////////////////
class InspectorWindow : IStepper
{
static InspectorWindow instance = null;
public static void Show(object item)
{
if (instance == null) instance = new InspectorWindow();
instance.inspector = new Inspector(item);
instance.pose = new Pose(Input.Head.position + Input.Head.Forward * 0.35f, Quat.FromAngles(0, 180, 0) * Input.Head.orientation);
PropertyInfo info = item.GetType().GetProperty("Name");
instance.name = info != null
? info.GetValue(item).ToString()
: item.GetType().Name;
SK.AddStepper(instance);
}
public static void Hide()
{
SK.RemoveStepper(instance);
instance.inspector = null;
}
string name;
Pose pose;
Inspector inspector;
public Action<object> OnDelete;
public bool Enabled => true;
public InspectorWindow()
{
instance = this;
}
public InspectorWindow(Action<object> onDelete)
{
instance = this;
OnDelete = onDelete;
}
public bool Initialize() => true;
public void Shutdown() { }
public void Step()
{
if (!Enabled) return;
UI.WindowBegin(name, ref pose, V.XY(.3f,0));
inspector.DrawUI();
UI.HSeparator();
if (UI.Button("Close"))
Hide();
if (OnDelete != null)
{
UI.SameLine();
UI.PushTint(new(1,0,0,1));
if (UI.Button("Delete"))
{
OnDelete(inspector.Item);
Hide();
}
UI.PopTint();
}
UI.WindowEnd();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment