Skip to content

Commit f48bc31

Browse files
[Editor] Add collapsing headers to components in the inspector;
1 parent fa2477d commit f48bc31

File tree

2 files changed

+177
-39
lines changed

2 files changed

+177
-39
lines changed

Engine/Staple.Editor/Editors/EditorGUI.cs

Lines changed: 141 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,12 @@ public enum SelectableFlags
3232

3333
private static readonly Dictionary<string, bool> treeViewStates = [];
3434

35+
private static readonly Dictionary<string, bool> collapsingHeaderStates = [];
36+
3537
private static readonly HashSet<int> usedTreeViewStates = [];
3638

39+
private static readonly HashSet<int> usedCollapsingHeaderStates = [];
40+
3741
private static readonly List<Action> endFrameCallbacks = [];
3842

3943
private static string MakeIdentifier(string identifier, string key) => $"{identifier}##{key}";
@@ -123,12 +127,29 @@ internal static void OnFrameStart()
123127

124128
treeViewStates.Remove(key);
125129
}
130+
131+
keys = collapsingHeaderStates.Keys.ToArray();
132+
133+
foreach(var key in keys)
134+
{
135+
if(usedCollapsingHeaderStates.Contains(key.GetHashCode()))
136+
{
137+
continue;
138+
}
139+
140+
collapsingHeaderStates.Remove(key);
141+
}
126142
}
127143

128144
if (usedTreeViewStates.Count > 0)
129145
{
130146
usedTreeViewStates.Clear();
131147
}
148+
149+
if(usedCollapsingHeaderStates.Count > 0)
150+
{
151+
usedCollapsingHeaderStates.Clear();
152+
}
132153
}
133154

134155
internal static void OnFrameEnd()
@@ -1357,7 +1378,6 @@ public static void TreeNodeIcon(Texture icon, Color color, string label, string
13571378
/// <param name="leaf">Whether it's a leaf (doesn't open on click, no arrow)</param>
13581379
/// <param name="openHandler">A handler for when it is open</param>
13591380
/// <param name="clickHandler">A handler for when it is clicked</param>
1360-
/// <param name="openHandler">A handler for when it is open</param>
13611381
/// <param name="prefixHandler">A handler to run regardless of the node being open</param>
13621382
/// <remarks>Click Handler will trigger when the left or right mouse button is clicked</remarks>
13631383
public static void TreeNodeIcon(Texture icon, string label, string key, bool leaf, ref bool open, Action openHandler, Action clickHandler,
@@ -1885,4 +1905,124 @@ public static void Table(string key, int rows, int columns, bool showHeader, Act
18851905
ImGui.EndTable();
18861906
}
18871907
}
1908+
1909+
/// <summary>
1910+
/// Create a collapsing header
1911+
/// </summary>
1912+
/// <param name="label">The label for the header</param>
1913+
/// <param name="key">The header's unique ID</param>
1914+
/// <param name="showCloseButton">Whether we should show a close button</param>
1915+
/// <param name="handler">A callback when the header is expanded</param>
1916+
/// <param name="closed">A callback when the header's close button is clicked</param>
1917+
/// <param name="defaultOpen">Whether the tree should be open by default</param>
1918+
public static void CollapsingHeader(string label, string key, bool showCloseButton, Action handler,
1919+
Action closed, bool defaultOpen = false)
1920+
{
1921+
var flags = ImGuiTreeNodeFlags.OpenOnArrow | ImGuiTreeNodeFlags.OpenOnDoubleClick;
1922+
1923+
if (defaultOpen)
1924+
{
1925+
flags |= ImGuiTreeNodeFlags.DefaultOpen;
1926+
}
1927+
1928+
var visible = showCloseButton;
1929+
bool open;
1930+
1931+
if(showCloseButton)
1932+
{
1933+
open = ImGui.CollapsingHeader(MakeIdentifier(label, key), ref visible, flags);
1934+
}
1935+
else
1936+
{
1937+
open = ImGui.CollapsingHeader(MakeIdentifier(label, key), flags);
1938+
}
1939+
1940+
var stateKey = $"{label}-{key}";
1941+
1942+
if (collapsingHeaderStates.TryGetValue(stateKey, out var isOpen) == false)
1943+
{
1944+
isOpen = false;
1945+
1946+
collapsingHeaderStates.Add(stateKey, isOpen);
1947+
}
1948+
1949+
usedCollapsingHeaderStates.Add(stateKey.GetHashCode());
1950+
1951+
if (isOpen != open)
1952+
{
1953+
collapsingHeaderStates.AddOrSetKey(stateKey, open);
1954+
}
1955+
1956+
isOpen = open;
1957+
1958+
if(open)
1959+
{
1960+
ExecuteHandler(handler, key);
1961+
}
1962+
1963+
if(showCloseButton && visible == false)
1964+
{
1965+
ExecuteHandler(closed, $"{key} closed");
1966+
}
1967+
}
1968+
1969+
/// <summary>
1970+
/// Create a collapsing header
1971+
/// </summary>
1972+
/// <param name="label">The label for the header</param>
1973+
/// <param name="key">The header's unique ID</param>
1974+
/// <param name="open">Whether the header is open</param>
1975+
/// <param name="showCloseButton">Whether we should show a close button</param>
1976+
/// <param name="handler">A callback when the header is expanded</param>
1977+
/// <param name="closed">A callback when the header's close button is clicked</param>
1978+
/// <param name="defaultOpen">Whether the tree should be open by default</param>
1979+
public static void CollapsingHeader(string label, string key, ref bool open, bool showCloseButton, Action handler,
1980+
Action closed, bool defaultOpen = false)
1981+
{
1982+
var flags = ImGuiTreeNodeFlags.OpenOnArrow | ImGuiTreeNodeFlags.OpenOnDoubleClick;
1983+
1984+
if (defaultOpen)
1985+
{
1986+
flags |= ImGuiTreeNodeFlags.DefaultOpen;
1987+
}
1988+
1989+
var visible = showCloseButton;
1990+
1991+
if (showCloseButton)
1992+
{
1993+
open = ImGui.CollapsingHeader(MakeIdentifier(label, key), ref visible, flags);
1994+
}
1995+
else
1996+
{
1997+
open = ImGui.CollapsingHeader(MakeIdentifier(label, key), flags);
1998+
}
1999+
2000+
var stateKey = $"{label}-{key}";
2001+
2002+
if (collapsingHeaderStates.TryGetValue(stateKey, out var isOpen) == false)
2003+
{
2004+
isOpen = false;
2005+
2006+
collapsingHeaderStates.Add(stateKey, isOpen);
2007+
}
2008+
2009+
usedCollapsingHeaderStates.Add(stateKey.GetHashCode());
2010+
2011+
if (isOpen != open)
2012+
{
2013+
collapsingHeaderStates.AddOrSetKey(stateKey, open);
2014+
}
2015+
2016+
isOpen = open;
2017+
2018+
if(open)
2019+
{
2020+
ExecuteHandler(handler, key);
2021+
}
2022+
2023+
if(showCloseButton && visible == false)
2024+
{
2025+
ExecuteHandler(closed, $"{key} closed");
2026+
}
2027+
}
18882028
}

Engine/Staple.Editor/StapleEditor+Panels.cs

Lines changed: 36 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1062,61 +1062,59 @@ private void Inspector(ImGuiIOPtr io)
10621062
var localComponent = component;
10631063
var removed = false;
10641064

1065-
EditorGUI.TreeNode(component.GetType().Name.ExpandCamelCaseName(), $"SELECTED{component.GetType().FullName}", false, () =>
1066-
{
1067-
if(removed)
1065+
EditorGUI.CollapsingHeader(component.GetType().Name.ExpandCamelCaseName(), $"SELECTED{component.GetType().FullName}",
1066+
component is not Transform,
1067+
() =>
10681068
{
1069-
return;
1070-
}
1069+
if(removed)
1070+
{
1071+
return;
1072+
}
10711073

1072-
if (localComponent is Transform transform)
1073-
{
1074-
transform.LocalPosition = EditorGUI.Vector3Field("Position", $"SELECTED{localComponent.GetType().FullName}POSITION", transform.LocalPosition);
1074+
if (localComponent is Transform transform)
1075+
{
1076+
transform.LocalPosition = EditorGUI.Vector3Field("Position", $"SELECTED{localComponent.GetType().FullName}POSITION",
1077+
transform.LocalPosition);
10751078

1076-
var rotation = transform.LocalRotation.ToEulerAngles();
1079+
var rotation = transform.LocalRotation.ToEulerAngles();
10771080

1078-
var newRotation = EditorGUI.Vector3Field("Rotation", $"SELECTED{localComponent.GetType().FullName}ROTATION", rotation);
1081+
var newRotation = EditorGUI.Vector3Field("Rotation", $"SELECTED{localComponent.GetType().FullName}ROTATION", rotation);
10791082

1080-
if (rotation != newRotation)
1081-
{
1082-
transform.LocalRotation = Quaternion.Euler(newRotation);
1083-
}
1083+
if (rotation != newRotation)
1084+
{
1085+
transform.LocalRotation = Quaternion.Euler(newRotation);
1086+
}
10841087

1085-
transform.LocalScale = EditorGUI.Vector3Field("Scale", $"SELECTED{localComponent.GetType().FullName}SCALE", transform.LocalScale);
1086-
}
1087-
else
1088-
{
1089-
if (cachedEditors.TryGetValue($"{counter}{localComponent.GetType().FullName}", out var editor))
1090-
{
1091-
editor.OnInspectorGUI();
1088+
transform.LocalScale = EditorGUI.Vector3Field("Scale", $"SELECTED{localComponent.GetType().FullName}SCALE",
1089+
transform.LocalScale);
10921090
}
10931091
else
10941092
{
1095-
cachedEditors.Add($"{counter}{localComponent.GetType().FullName}", new Editor()
1093+
if (cachedEditors.TryGetValue($"{counter}{localComponent.GetType().FullName}", out var editor))
10961094
{
1097-
target = localComponent
1098-
});
1095+
editor.OnInspectorGUI();
1096+
}
1097+
else
1098+
{
1099+
cachedEditors.Add($"{counter}{localComponent.GetType().FullName}", new Editor()
1100+
{
1101+
target = localComponent
1102+
});
1103+
}
10991104
}
1100-
}
1101-
1102-
if (EditorGUI.Changed)
1103-
{
1104-
selectedEntity.SetComponent(localComponent);
1105-
}
1106-
},
1107-
null,
1108-
() =>
1109-
{
1110-
EditorGUI.SameLine();
11111105

1112-
EditorGUI.Button("X", $"ENTITYREMOVE{localComponent.GetType()}", () =>
1106+
if (EditorGUI.Changed)
1107+
{
1108+
selectedEntity.SetComponent(localComponent);
1109+
}
1110+
},
1111+
() =>
11131112
{
11141113
selectedEntity.RemoveComponent(localComponent.GetType());
11151114

11161115
removed = true;
11171116
resetSelection = true;
1118-
});
1119-
}, true);
1117+
}, true);
11201118
});
11211119

11221120
if(ImGui.Button("Add Component"))

0 commit comments

Comments
 (0)