Misc UiDebug2 Fixes

- The widget will now only check the `FFXIVClientStructs` assembly for addon type information.
- Updated ImRaii usage
- Clarified suppression in `NodeTree.Text.cs`
- Restored the original Addon Inspector in the Data window, so that both versions can coexist for the time being
This commit is contained in:
ItsBexy 2024-10-17 10:52:22 -06:00
parent e19f9284e5
commit 552aafd70d
17 changed files with 379 additions and 347 deletions

View file

@ -16,8 +16,10 @@ public unsafe partial class AddonTree
{ {
private static readonly Dictionary<string, Type?> AddonTypeDict = []; private static readonly Dictionary<string, Type?> AddonTypeDict = [];
private static readonly Assembly? ClientStructs = AppDomain.CurrentDomain.GetAssemblies().SingleOrDefault(static a => a.GetName().Name == "FFXIVClientStructs");
/// <summary> /// <summary>
/// Gets or sets a collection of names corresponding to pointers documented within the addon struct. /// Gets or sets a collection of names for field offsets that have been documented in FFXIVClientStructs.
/// </summary> /// </summary>
internal Dictionary<nint, List<string>> FieldNames { get; set; } = []; internal Dictionary<nint, List<string>> FieldNames { get; set; } = [];
@ -28,28 +30,25 @@ public unsafe partial class AddonTree
return null; return null;
} }
if (AddonTypeDict.TryAdd(this.AddonName, null)) if (AddonTypeDict.TryAdd(this.AddonName, null) && ClientStructs != null)
{ {
foreach (var a in AppDomain.CurrentDomain.GetAssemblies()) try
{ {
try foreach (var t in from t in ClientStructs.GetTypes()
where t.IsPublic
let xivAddonAttr = (Addon?)t.GetCustomAttribute(typeof(Addon), false)
where xivAddonAttr != null
where xivAddonAttr.AddonIdentifiers.Contains(this.AddonName)
select t)
{ {
foreach (var t in from t in a.GetTypes() AddonTypeDict[this.AddonName] = t;
where t.IsPublic break;
let xivAddonAttr = (Addon?)t.GetCustomAttribute(typeof(Addon), false)
where xivAddonAttr != null
where xivAddonAttr.AddonIdentifiers.Contains(this.AddonName)
select t)
{
AddonTypeDict[this.AddonName] = t;
break;
}
}
catch
{
// ignored
} }
} }
catch
{
// ignored
}
} }
return AddonTypeDict.TryGetValue(this.AddonName, out var result) && result != null ? Marshal.PtrToStructure(new(addon), result) : *addon; return AddonTypeDict.TryGetValue(this.AddonName, out var result) && result != null ? Marshal.PtrToStructure(new(addon), result) : *addon;
@ -108,7 +107,7 @@ public unsafe partial class AddonTree
{ {
try try
{ {
if (this.FieldNames.TryAdd(fieldAddr, [..path!, name]) && fieldType.DeclaringType == baseType) if (this.FieldNames.TryAdd(fieldAddr, [..path, name]) && fieldType.DeclaringType == baseType)
{ {
this.PopulateFieldNames(field.GetValue(obj), fieldAddr, [..path, name]); this.PopulateFieldNames(field.GetValue(obj), fieldAddr, [..path, name]);
} }
@ -140,7 +139,7 @@ public unsafe partial class AddonTree
var itemAddr = fieldAddr + (size * i); var itemAddr = fieldAddr + (size * i);
var itemName = $"{name}[{i}]"; var itemName = $"{name}[{i}]";
this.FieldNames.TryAdd(itemAddr, [..path!, itemName]); this.FieldNames.TryAdd(itemAddr, [..path, itemName]);
var item = Marshal.PtrToStructure(itemAddr, itemType); var item = Marshal.PtrToStructure(itemAddr, itemType);
if (itemType.DeclaringType == baseType) if (itemType.DeclaringType == baseType)
@ -176,10 +175,10 @@ public unsafe partial class AddonTree
return; return;
} }
this.FieldNames.TryAdd(fieldAddr, [..path!, name]); this.FieldNames.TryAdd(fieldAddr, [..path, name]);
this.FieldNames.TryAdd(pointer, [..path, name]); this.FieldNames.TryAdd(pointer, [..path, name]);
if (itemType?.DeclaringType != baseType || itemType!.IsPointer) if (itemType?.DeclaringType != baseType || itemType.IsPointer)
{ {
return; return;
} }

View file

@ -26,44 +26,41 @@ public static class Events
return; return;
} }
var tree = ImRaii.TreeNode($"Events##{(nint)node:X}eventTree"); using var tree = ImRaii.TreeNode($"Events##{(nint)node:X}eventTree");
if (tree) if (tree)
{ {
var tab = ImRaii.Table($"##{(nint)node:X}eventTable", 7, Resizable | SizingFixedFit | Borders | RowBg); using (ImRaii.Table($"##{(nint)node:X}eventTable", 7, Resizable | SizingFixedFit | Borders | RowBg))
ImGui.TableSetupColumn("#", WidthFixed);
ImGui.TableSetupColumn("Type", WidthFixed);
ImGui.TableSetupColumn("Param", WidthFixed);
ImGui.TableSetupColumn("Flags", WidthFixed);
ImGui.TableSetupColumn("Unk29", WidthFixed);
ImGui.TableSetupColumn("Target", WidthFixed);
ImGui.TableSetupColumn("Listener", WidthFixed);
ImGui.TableHeadersRow();
var i = 0;
while (evt != null)
{ {
ImGui.TableNextColumn(); ImGui.TableSetupColumn("#", WidthFixed);
ImGui.Text($"{i++}"); ImGui.TableSetupColumn("Type", WidthFixed);
ImGui.TableNextColumn(); ImGui.TableSetupColumn("Param", WidthFixed);
ImGui.Text($"{evt->Type}"); ImGui.TableSetupColumn("Flags", WidthFixed);
ImGui.TableNextColumn(); ImGui.TableSetupColumn("Unk29", WidthFixed);
ImGui.Text($"{evt->Param}"); ImGui.TableSetupColumn("Target", WidthFixed);
ImGui.TableNextColumn(); ImGui.TableSetupColumn("Listener", WidthFixed);
ImGui.Text($"{evt->Flags}");
ImGui.TableNextColumn(); ImGui.TableHeadersRow();
ImGui.Text($"{evt->Unk29}");
ImGui.TableNextColumn(); var i = 0;
Gui.ClickToCopyText($"{(nint)evt->Target:X}"); while (evt != null)
ImGui.TableNextColumn(); {
Gui.ClickToCopyText($"{(nint)evt->Listener:X}"); ImGui.TableNextColumn();
evt = evt->NextEvent; ImGui.Text($"{i++}");
ImGui.TableNextColumn();
ImGui.Text($"{evt->Type}");
ImGui.TableNextColumn();
ImGui.Text($"{evt->Param}");
ImGui.TableNextColumn();
ImGui.Text($"{evt->Flags}");
ImGui.TableNextColumn();
ImGui.Text($"{evt->Unk29}");
ImGui.TableNextColumn();
Gui.ClickToCopyText($"{(nint)evt->Target:X}");
ImGui.TableNextColumn();
Gui.ClickToCopyText($"{(nint)evt->Listener:X}");
evt = evt->NextEvent;
}
} }
tab.Dispose();
} }
tree.Dispose();
} }
} }

View file

@ -26,11 +26,10 @@ internal unsafe partial class ResNodeTree
/// </summary> /// </summary>
private protected void DrawNodeEditorTable() private protected void DrawNodeEditorTable()
{ {
var tab = ImRaii.Table($"###Editor{(nint)this.Node}", 2, SizingStretchProp | NoHostExtendX); using (ImRaii.Table($"###Editor{(nint)this.Node}", 2, SizingStretchProp | NoHostExtendX))
{
this.DrawEditorRows(); this.DrawEditorRows();
}
tab.Dispose();
} }
/// <summary> /// <summary>

View file

@ -51,8 +51,8 @@ internal unsafe partial class ImageNodeTree : ResNodeTree
/// <summary> /// <summary>
/// Draws the texture inside the window, in either of two styles.<br/><br/> /// Draws the texture inside the window, in either of two styles.<br/><br/>
/// <term>Full Image</term>presents the texture in full as a spritesheet.<br/> /// <term>Full Image (0)</term>presents the texture in full as a spritesheet.<br/>
/// <term>Parts List</term>presents the individual parts as rows in a table. /// <term>Parts List (1)</term>presents the individual parts as rows in a table.
/// </summary> /// </summary>
private protected void DrawTextureAndParts() private protected void DrawTextureAndParts()
{ {
@ -63,7 +63,7 @@ internal unsafe partial class ImageNodeTree : ResNodeTree
return; return;
} }
var tree = ImRaii.TreeNode($"Texture##texture{(nint)this.TexData.Texture->D3D11ShaderResourceView:X}", SpanFullWidth); using var tree = ImRaii.TreeNode($"Texture##texture{(nint)this.TexData.Texture->D3D11ShaderResourceView:X}", SpanFullWidth);
if (tree) if (tree)
{ {
@ -99,8 +99,6 @@ internal unsafe partial class ImageNodeTree : ResNodeTree
this.DrawFullTexture(); this.DrawFullTexture();
} }
} }
tree.Dispose();
} }
/// <summary> /// <summary>
@ -191,58 +189,62 @@ internal unsafe partial class ImageNodeTree : ResNodeTree
private void PrintPartsTable() private void PrintPartsTable()
{ {
var tab = ImRaii.Table($"partsTable##{(nint)this.TexData.Texture->D3D11ShaderResourceView:X}", 3, Borders | RowBg | Reorderable); using (ImRaii.Table($"partsTable##{(nint)this.TexData.Texture->D3D11ShaderResourceView:X}", 3, Borders | RowBg | Reorderable))
ImGui.TableSetupColumn("Part ID", WidthFixed);
ImGui.TableSetupColumn("Part Texture", WidthFixed);
ImGui.TableSetupColumn("Coordinates", WidthFixed);
ImGui.TableHeadersRow();
var tWidth = this.TexData.Texture->Width;
var tHeight = this.TexData.Texture->Height;
var textureSize = new Vector2(tWidth, tHeight);
for (ushort i = 0; i < this.TexData.PartCount; i++)
{ {
ImGui.TableNextColumn(); ImGui.TableSetupColumn("Part ID", WidthFixed);
ImGui.TableSetupColumn("Part Texture", WidthFixed);
ImGui.TableSetupColumn("Coordinates", WidthFixed);
var col = i == this.TexData.PartId ? new Vector4(0, 0.85F, 1, 1) : new(1); ImGui.TableHeadersRow();
ImGui.TextColored(col, $"#{i.ToString().PadLeft(this.TexData.PartCount.ToString().Length, '0')}");
ImGui.TableNextColumn(); var tWidth = this.TexData.Texture->Width;
var tHeight = this.TexData.Texture->Height;
var textureSize = new Vector2(tWidth, tHeight);
var part = this.TexData.PartsList->Parts[i]; for (ushort i = 0; i < this.TexData.PartCount; i++)
var hiRes = this.TexData.HiRes; {
ImGui.TableNextColumn();
var u = hiRes ? part.U * 2f : part.U; var col = i == this.TexData.PartId ? new Vector4(0, 0.85F, 1, 1) : new(1);
var v = hiRes ? part.V * 2f : part.V; ImGui.TextColored(col, $"#{i.ToString().PadLeft(this.TexData.PartCount.ToString().Length, '0')}");
var width = hiRes ? part.Width * 2f : part.Width;
var height = hiRes ? part.Height * 2f : part.Height;
ImGui.Image(new(this.TexData.Texture->D3D11ShaderResourceView), new(width, height), new Vector2(u, v) / textureSize, new Vector2(u + width, v + height) / textureSize); ImGui.TableNextColumn();
ImGui.TableNextColumn(); var part = this.TexData.PartsList->Parts[i];
var hiRes = this.TexData.HiRes;
ImGui.TextColored(!hiRes ? new(1) : new(0.6f, 0.6f, 0.6f, 1), "Standard:\t"); var u = hiRes ? part.U * 2f : part.U;
ImGui.SameLine(); var v = hiRes ? part.V * 2f : part.V;
var cursX = ImGui.GetCursorPosX(); var width = hiRes ? part.Width * 2f : part.Width;
var height = hiRes ? part.Height * 2f : part.Height;
PrintPartCoords(u / 2f, v / 2f, width / 2f, height / 2f); ImGui.Image(
new(this.TexData.Texture->D3D11ShaderResourceView),
new(width, height),
new Vector2(u, v) / textureSize,
new Vector2(u + width, v + height) / textureSize);
ImGui.TextColored(hiRes ? new(1) : new(0.6f, 0.6f, 0.6f, 1), "Hi-Res:\t"); ImGui.TableNextColumn();
ImGui.SameLine();
ImGui.SetCursorPosX(cursX);
PrintPartCoords(u, v, width, height); ImGui.TextColored(!hiRes ? new(1) : new(0.6f, 0.6f, 0.6f, 1), "Standard:\t");
ImGui.SameLine();
var cursX = ImGui.GetCursorPosX();
ImGui.Text("UV:\t"); PrintPartCoords(u / 2f, v / 2f, width / 2f, height / 2f);
ImGui.SameLine();
ImGui.SetCursorPosX(cursX);
PrintPartCoords(u / tWidth, v / tWidth, (u + width) / tWidth, (v + height) / tHeight, true, true); ImGui.TextColored(hiRes ? new(1) : new(0.6f, 0.6f, 0.6f, 1), "Hi-Res:\t");
ImGui.SameLine();
ImGui.SetCursorPosX(cursX);
PrintPartCoords(u, v, width, height);
ImGui.Text("UV:\t");
ImGui.SameLine();
ImGui.SetCursorPosX(cursX);
PrintPartCoords(u / tWidth, v / tWidth, (u + width) / tWidth, (v + height) / tHeight, true, true);
}
} }
tab.Dispose();
} }
/// <summary> /// <summary>

View file

@ -90,11 +90,11 @@ internal unsafe partial class ResNodeTree : IDisposable
NodeType.ClippingMask => new ClippingMaskNodeTree(node, addonTree), NodeType.ClippingMask => new ClippingMaskNodeTree(node, addonTree),
NodeType.Counter => new CounterNodeTree(node, addonTree), NodeType.Counter => new CounterNodeTree(node, addonTree),
NodeType.Collision => new CollisionNodeTree(node, addonTree), NodeType.Collision => new CollisionNodeTree(node, addonTree),
_ => new ResNodeTree(node, addonTree), _ => new ResNodeTree(node, addonTree)
}; };
/// <summary> /// <summary>
/// Prints a list of NodeTree for a given list of nodes. /// Prints a list of NodeTrees for a given list of nodes.
/// </summary> /// </summary>
/// <param name="nodeList">The address of the start of the list.</param> /// <param name="nodeList">The address of the start of the list.</param>
/// <param name="count">The number of nodes in the list.</param> /// <param name="count">The number of nodes in the list.</param>
@ -122,8 +122,8 @@ internal unsafe partial class ResNodeTree : IDisposable
return; return;
} }
var c = ImRaii.PushColor(Text, color); using var c = ImRaii.PushColor(Text, color);
var tree = ImRaii.TreeNode($"{label}##{(nint)nodeList:X}", SpanFullWidth); using var tree = ImRaii.TreeNode($"{label}##{(nint)nodeList:X}", SpanFullWidth);
c.Pop(); c.Pop();
if (tree) if (tree)
@ -139,8 +139,6 @@ internal unsafe partial class ResNodeTree : IDisposable
ImGui.GetWindowDrawList().AddLine(lineStart, lineEnd, RgbaVector4ToUint(color), 1); ImGui.GetWindowDrawList().AddLine(lineStart, lineEnd, RgbaVector4ToUint(color), 1);
} }
} }
tree.Dispose();
} }
/// <summary> /// <summary>
@ -275,8 +273,8 @@ internal unsafe partial class ResNodeTree : IDisposable
ImGui.SetNextItemOpen(true, ImGuiCond.Always); ImGui.SetNextItemOpen(true, ImGuiCond.Always);
} }
var col = ImRaii.PushColor(Text, displayColor); using var col = ImRaii.PushColor(Text, displayColor);
var tree = ImRaii.TreeNode(label, SpanFullWidth); using var tree = ImRaii.TreeNode(label, SpanFullWidth);
if (ImGui.IsItemHovered()) if (ImGui.IsItemHovered())
{ {
@ -331,8 +329,6 @@ internal unsafe partial class ResNodeTree : IDisposable
ImGui.GetWindowDrawList().AddLine(lineStart, lineEnd, RgbaVector4ToUint(displayColor), 1); ImGui.GetWindowDrawList().AddLine(lineStart, lineEnd, RgbaVector4ToUint(displayColor), 1);
} }
} }
tree.Dispose();
} }
private void DrawBasicControls() private void DrawBasicControls()

View file

@ -50,7 +50,6 @@ internal unsafe partial class TextNodeTree : ResNodeTree
ImGui.TextColored(new(1), "Text:"); ImGui.TextColored(new(1), "Text:");
ImGui.SameLine(); ImGui.SameLine();
#pragma warning disable
try try
{ {
var style = new SeStringDrawParams var style = new SeStringDrawParams
@ -61,13 +60,14 @@ internal unsafe partial class TextNodeTree : ResNodeTree
EdgeStrength = 1f EdgeStrength = 1f
}; };
#pragma warning disable SeStringRenderer
ImGuiHelpers.SeStringWrapped(this.NodeText.AsSpan(), style); ImGuiHelpers.SeStringWrapped(this.NodeText.AsSpan(), style);
#pragma warning restore SeStringRenderer
} }
catch catch
{ {
ImGui.Text(Marshal.PtrToStringAnsi(new(this.NodeText.StringPtr)) ?? ""); ImGui.Text(Marshal.PtrToStringAnsi(new(this.NodeText.StringPtr)) ?? "");
} }
#pragma warning restore
PrintFieldValuePairs( PrintFieldValuePairs(
("Font", $"{this.TxtNode->FontType}"), ("Font", $"{this.TxtNode->FontType}"),
@ -83,7 +83,7 @@ internal unsafe partial class TextNodeTree : ResNodeTree
private void PrintPayloads() private void PrintPayloads()
{ {
var tree = ImRaii.TreeNode($"Text Payloads##{(nint)this.Node:X}"); using var tree = ImRaii.TreeNode($"Text Payloads##{(nint)this.Node:X}");
if (tree) if (tree)
{ {
@ -116,7 +116,5 @@ internal unsafe partial class TextNodeTree : ResNodeTree
} }
} }
} }
tree.Dispose();
} }
} }

View file

@ -56,7 +56,7 @@ public readonly unsafe partial struct TimelineTree
if (count > 0) if (count > 0)
{ {
var tree = ImRaii.TreeNode($"Timeline##{(nint)this.node:X}timeline", SpanFullWidth); using var tree = ImRaii.TreeNode($"Timeline##{(nint)this.node:X}timeline", SpanFullWidth);
if (tree) if (tree)
{ {
@ -81,8 +81,6 @@ public readonly unsafe partial struct TimelineTree
this.PrintAnimation(animation, a, isActive, (nint)(this.NodeTimeline->Resource->Animations + (a * sizeof(AtkTimelineAnimation)))); this.PrintAnimation(animation, a, isActive, (nint)(this.NodeTimeline->Resource->Animations + (a * sizeof(AtkTimelineAnimation))));
} }
} }
tree.Dispose();
} }
} }
@ -310,45 +308,46 @@ public readonly unsafe partial struct TimelineTree
{ {
var columns = this.BuildColumns(animation); var columns = this.BuildColumns(animation);
var col = ImRaii.PushColor(ImGuiCol.Text, isActive ? new Vector4(1, 0.65F, 0.4F, 1) : new(1)); using (ImRaii.PushColor(ImGuiCol.Text, new Vector4(1, 0.65F, 0.4F, 1), isActive))
var tree = ImRaii.TreeNode($"[#{a}] [Frames {animation.StartFrameIdx}-{animation.EndFrameIdx}] {(isActive ? " (Active)" : string.Empty)}###{(nint)this.node}animTree{a}");
col.Dispose();
if (tree)
{ {
PrintFieldValuePair("Animation", $"{address:X}"); using var tree = ImRaii.TreeNode($"[#{a}] [Frames {animation.StartFrameIdx}-{animation.EndFrameIdx}] {(isActive ? " (Active)" : string.Empty)}###{(nint)this.node}animTree{a}");
ShowStruct((AtkTimelineAnimation*)address); if (tree)
if (columns.Count > 0)
{ {
var table = ImRaii.Table($"##{(nint)this.node}animTable{a}", columns.Count, Borders | SizingFixedFit | RowBg | NoHostExtendX); PrintFieldValuePair("Animation", $"{address:X}");
foreach (var c in columns) ShowStruct((AtkTimelineAnimation*)address);
if (columns.Count > 0)
{ {
ImGui.TableSetupColumn(c.Name, WidthFixed, c.Width); using (ImRaii.Table(
} $"##{(nint)this.node}animTable{a}",
columns.Count,
ImGui.TableHeadersRow(); Borders | SizingFixedFit | RowBg | NoHostExtendX))
var rows = columns.Select(static c => c.Count).Max();
for (var i = 0; i < rows; i++)
{
ImGui.TableNextRow();
foreach (var c in columns)
{ {
ImGui.TableNextColumn(); foreach (var c in columns)
c.PrintValueAt(i); {
ImGui.TableSetupColumn(c.Name, WidthFixed, c.Width);
}
ImGui.TableHeadersRow();
var rows = columns.Select(static c => c.Count).Max();
for (var i = 0; i < rows; i++)
{
ImGui.TableNextRow();
foreach (var c in columns)
{
ImGui.TableNextColumn();
c.PrintValueAt(i);
}
}
} }
} }
table.Dispose();
} }
} }
tree.Dispose();
} }
private List<IKeyGroupColumn> BuildColumns(AtkTimelineAnimation animation) private List<IKeyGroupColumn> BuildColumns(AtkTimelineAnimation animation)

View file

@ -79,43 +79,53 @@ internal unsafe class ElementSelector : IDisposable
/// </summary> /// </summary>
internal void DrawInterface() internal void DrawInterface()
{ {
var ch = ImRaii.Child("###sidebar_elementSelector", new(250, 0), true); using (ImRaii.Child("###sidebar_elementSelector", new(250, 0), true))
var f = ImRaii.PushFont(IconFont);
var col = ImRaii.PushColor(Text, this.Active ? new Vector4(1, 1, 0.2f, 1) : new(1));
if (ImGui.Button($"{(char)ObjectUngroup}"))
{ {
this.Active = !this.Active; using var f = ImRaii.PushFont(IconFont);
} using var col = ImRaii.PushColor(Text, new Vector4(1, 1, 0.2f, 1), this.Active);
if (Countdown > 0) if (ImGui.Button($"{(char)ObjectUngroup}"))
{
Countdown -= 1;
if (Countdown < 0)
{ {
Countdown = 0; this.Active = !this.Active;
}
if (Countdown > 0)
{
Countdown -= 1;
if (Countdown < 0)
{
Countdown = 0;
}
}
col.Pop();
f.Pop();
if (ImGui.IsItemHovered())
{
ImGui.SetTooltip("Element Selector");
}
ImGui.SameLine();
ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X - 32);
ImGui.InputTextWithHint(
"###addressSearchInput",
"Address Search",
ref this.addressSearchInput,
18,
ImGuiInputTextFlags.AutoSelectAll);
ImGui.SameLine();
if (ImGuiComponents.IconButton("###elemSelectorAddrSearch", Search) && nint.TryParse(
this.addressSearchInput,
NumberStyles.HexNumber | NumberStyles.AllowHexSpecifier,
InvariantInfo,
out var address))
{
this.PerformSearch(address);
} }
} }
col.Pop();
f.Pop();
if (ImGui.IsItemHovered())
{
ImGui.SetTooltip("Element Selector");
}
ImGui.SameLine();
ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X - 32);
ImGui.InputTextWithHint("###addressSearchInput", "Address Search", ref this.addressSearchInput, 18, ImGuiInputTextFlags.AutoSelectAll);
ImGui.SameLine();
if (ImGuiComponents.IconButton("###elemSelectorAddrSearch", Search) && nint.TryParse(this.addressSearchInput, NumberStyles.HexNumber | NumberStyles.AllowHexSpecifier, InvariantInfo, out var address))
{
this.PerformSearch(address);
}
ch.Dispose();
} }
/// <summary> /// <summary>
@ -141,72 +151,71 @@ internal unsafe class ElementSelector : IDisposable
var mousePos = ImGui.GetMousePos() - MainViewport.Pos; var mousePos = ImGui.GetMousePos() - MainViewport.Pos;
var addonResults = GetAtkUnitBaseAtPosition(mousePos); var addonResults = GetAtkUnitBaseAtPosition(mousePos);
var col = ImRaii.PushColor(WindowBg, new Vector4(0.5f)); using var col = ImRaii.PushColor(WindowBg, new Vector4(0.5f));
var ch = ImRaii.Child("noClick", new(800, 2000), false, NoInputs | NoBackground | NoScrollWithMouse); using (ImRaii.Child("noClick", new(800, 2000), false, NoInputs | NoBackground | NoScrollWithMouse))
var g = ImRaii.Group();
Gui.PrintFieldValuePair("Mouse Position", $"{mousePos.X}, {mousePos.Y}");
ImGui.Spacing();
ImGui.Text("RESULTS:\n");
var i = 0;
foreach (var a in addonResults)
{ {
var name = a.Addon->NameString; using (ImRaii.Group())
ImGui.Text($"[Addon] {name}");
ImGui.Indent(15);
foreach (var n in a.Nodes)
{ {
var nSelected = i++ == this.index; Gui.PrintFieldValuePair("Mouse Position", $"{mousePos.X}, {mousePos.Y}");
ImGui.Spacing();
ImGui.Text("RESULTS:\n");
PrintNodeHeaderOnly(n.Node, nSelected, a.Addon); var i = 0;
foreach (var a in addonResults)
if (nSelected && ImGui.IsMouseClicked(ImGuiMouseButton.Left))
{ {
this.Active = false; var name = a.Addon->NameString;
ImGui.Text($"[Addon] {name}");
this.uiDebug2.SelectedAddonName = a.Addon->NameString; ImGui.Indent(15);
foreach (var n in a.Nodes)
var ptrList = new List<nint> { (nint)n.Node };
var nextNode = n.Node->ParentNode;
while (nextNode != null)
{ {
ptrList.Add((nint)nextNode); var nSelected = i++ == this.index;
nextNode = nextNode->ParentNode;
PrintNodeHeaderOnly(n.Node, nSelected, a.Addon);
if (nSelected && ImGui.IsMouseClicked(ImGuiMouseButton.Left))
{
this.Active = false;
this.uiDebug2.SelectedAddonName = a.Addon->NameString;
var ptrList = new List<nint> { (nint)n.Node };
var nextNode = n.Node->ParentNode;
while (nextNode != null)
{
ptrList.Add((nint)nextNode);
nextNode = nextNode->ParentNode;
}
SearchResults = [.. ptrList];
Countdown = 100;
Scrolled = false;
}
if (nSelected)
{
n.NodeBounds.DrawFilled(new(1, 1, 0.2f, 1));
}
} }
SearchResults = [.. ptrList]; ImGui.Indent(-15);
Countdown = 100;
Scrolled = false;
} }
if (nSelected) if (i != 0)
{ {
n.NodeBounds.DrawFilled(new(1, 1, 0.2f, 1)); this.index -= (int)ImGui.GetIO().MouseWheel;
while (this.index < 0)
{
this.index += i;
}
while (this.index >= i)
{
this.index -= i;
}
} }
} }
ImGui.Indent(-15);
} }
if (i != 0)
{
this.index -= (int)ImGui.GetIO().MouseWheel;
while (this.index < 0)
{
this.index += i;
}
while (this.index >= i)
{
this.index -= i;
}
}
g.Dispose();
ch.Dispose();
col.Pop();
} }
private static List<AddonResult> GetAtkUnitBaseAtPosition(Vector2 position) private static List<AddonResult> GetAtkUnitBaseAtPosition(Vector2 position)
@ -384,9 +393,8 @@ internal unsafe class ElementSelector : IDisposable
return; return;
} }
var col = ImRaii.PushColor(Text, selected ? new Vector4(1, 1, 0.2f, 1) : new(0.6f, 0.6f, 0.6f, 1)); using var col = ImRaii.PushColor(Text, selected ? new Vector4(1, 1, 0.2f, 1) : new(0.6f, 0.6f, 0.6f, 1));
ResNodeTree.GetOrCreate(node, tree).WriteTreeHeading(); ResNodeTree.GetOrCreate(node, tree).WriteTreeHeading();
col.Pop();
} }
private void PerformSearch(nint address) private void PerformSearch(nint address)

View file

@ -39,9 +39,10 @@ internal class AddonPopoutWindow : Window, IDisposable
/// <inheritdoc/> /// <inheritdoc/>
public override void Draw() public override void Draw()
{ {
var ch = ImRaii.Child($"{this.WindowName}child", new(-1, -1), true); using (ImRaii.Child($"{this.WindowName}child", new(-1, -1), true))
this.addonTree.Draw(); {
ch.Dispose(); this.addonTree.Draw();
}
} }
/// <inheritdoc/> /// <inheritdoc/>

View file

@ -50,10 +50,11 @@ internal unsafe class NodePopoutWindow : Window, IDisposable
{ {
if (this.Node != null && this.AddonTree.ContainsNode(this.Node)) if (this.Node != null && this.AddonTree.ContainsNode(this.Node))
{ {
var ch = ImRaii.Child($"{(nint)this.Node:X}popoutChild", new(-1, -1), true); using (ImRaii.Child($"{(nint)this.Node:X}popoutChild", new(-1, -1), true))
ResNodeTree.GetOrCreate(this.Node, this.AddonTree).Print(null, this.firstDraw); {
ch.Dispose(); ResNodeTree.GetOrCreate(this.Node, this.AddonTree).Print(null, this.firstDraw);
this.firstDraw = false; this.firstDraw = false;
}
} }
else else
{ {

View file

@ -16,7 +16,7 @@ namespace Dalamud.Interface.Internal.UiDebug2;
/// <inheritdoc cref="UiDebug2"/> /// <inheritdoc cref="UiDebug2"/>
internal unsafe partial class UiDebug2 internal unsafe partial class UiDebug2
{ {
/// <summary> /// <summary>
/// All unit lists to check for addons. /// All unit lists to check for addons.
/// </summary> /// </summary>
internal static readonly List<UnitListOption> UnitListOptions = internal static readonly List<UnitListOption> UnitListOptions =
@ -38,7 +38,7 @@ internal unsafe partial class UiDebug2
new(12, "Depth Layer 13"), new(12, "Depth Layer 13"),
new(15, "Units 16"), new(15, "Units 16"),
new(16, "Units 17"), new(16, "Units 17"),
new(17, "Units 18"), new(17, "Units 18")
]; ];
private string addonNameSearch = string.Empty; private string addonNameSearch = string.Empty;
@ -53,54 +53,54 @@ internal unsafe partial class UiDebug2
private void DrawSidebar() private void DrawSidebar()
{ {
var g = ImRaii.Group(); using (ImRaii.Group())
{
this.DrawNameSearch(); this.DrawNameSearch();
this.DrawAddonSelectionList(); this.DrawAddonSelectionList();
this.elementSelector.DrawInterface(); this.elementSelector.DrawInterface();
}
g.Dispose();
} }
private void DrawNameSearch() private void DrawNameSearch()
{ {
var ch = ImRaii.Child("###sidebar_nameSearch", new(250, 40), true); using (ImRaii.Child("###sidebar_nameSearch", new(250, 40), true))
var atkUnitBaseSearch = this.addonNameSearch;
Vector4? defaultColor = this.visFilter ? new(0.0f, 0.8f, 0.2f, 1f) : new Vector4(0.6f, 0.6f, 0.6f, 1);
if (ImGuiComponents.IconButton("filter", LowVision, defaultColor))
{ {
this.visFilter = !this.visFilter; var atkUnitBaseSearch = this.addonNameSearch;
Vector4? defaultColor = this.visFilter ? new(0.0f, 0.8f, 0.2f, 1f) : new Vector4(0.6f, 0.6f, 0.6f, 1);
if (ImGuiComponents.IconButton("filter", LowVision, defaultColor))
{
this.visFilter = !this.visFilter;
}
if (ImGui.IsItemHovered())
{
ImGui.SetTooltip("Filter by visibility");
}
ImGui.SameLine();
ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X);
if (ImGui.InputTextWithHint("###atkUnitBaseSearch", "Filter by name", ref atkUnitBaseSearch, 0x20))
{
this.addonNameSearch = atkUnitBaseSearch;
}
} }
if (ImGui.IsItemHovered())
{
ImGui.SetTooltip("Filter by visibility");
}
ImGui.SameLine();
ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X);
if (ImGui.InputTextWithHint("###atkUnitBaseSearch", "Filter by name", ref atkUnitBaseSearch, 0x20))
{
this.addonNameSearch = atkUnitBaseSearch;
}
ch.Dispose();
} }
private void DrawAddonSelectionList() private void DrawAddonSelectionList()
{ {
var ch = ImRaii.Child("###sideBar_addonList", new(250, -44), true, ImGuiWindowFlags.AlwaysVerticalScrollbar); using (ImRaii.Child("###sideBar_addonList", new(250, -44), true, ImGuiWindowFlags.AlwaysVerticalScrollbar))
var unitListBaseAddr = GetUnitListBaseAddr();
foreach (var unit in UnitListOptions)
{ {
this.DrawUnitListOption(unitListBaseAddr, unit); var unitListBaseAddr = GetUnitListBaseAddr();
}
ch.Dispose(); foreach (var unit in UnitListOptions)
{
this.DrawUnitListOption(unitListBaseAddr, unit);
}
;
}
} }
private void DrawUnitListOption(AtkUnitList* unitListBaseAddr, UnitListOption unit) private void DrawUnitListOption(AtkUnitList* unitListBaseAddr, UnitListOption unit)
@ -148,25 +148,23 @@ internal unsafe partial class UiDebug2
var countStr = $"{(usingFilter ? $"{matchCount}/" : string.Empty)}{totalCount}"; var countStr = $"{(usingFilter ? $"{matchCount}/" : string.Empty)}{totalCount}";
var col1 = ImRaii.PushColor(ImGuiCol.Text, anyVisible ? new Vector4(1) : new Vector4(0.6f, 0.6f, 0.6f, 1)); using var col1 = ImRaii.PushColor(ImGuiCol.Text, anyVisible ? new Vector4(1) : new Vector4(0.6f, 0.6f, 0.6f, 1));
var tree = ImRaii.TreeNode($"{unit.Name} [{countStr}]###unitListTree{unit.Index}"); using var tree = ImRaii.TreeNode($"{unit.Name} [{countStr}]###unitListTree{unit.Index}");
col1.Pop(); col1.Pop();
if (tree) if (tree)
{ {
foreach (var option in options) foreach (var option in options)
{ {
var col2 = ImRaii.PushColor(ImGuiCol.Text, option.Visible ? new Vector4(0.1f, 1f, 0.1f, 1f) : new Vector4(0.6f, 0.6f, 0.6f, 1)); using (ImRaii.PushColor(ImGuiCol.Text, option.Visible ? new Vector4(0.1f, 1f, 0.1f, 1f) : new Vector4(0.6f, 0.6f, 0.6f, 1)))
if (ImGui.Selectable($"{option.Name}##select{option.Name}", this.SelectedAddonName == option.Name))
{ {
this.SelectedAddonName = option.Name; if (ImGui.Selectable($"{option.Name}##select{option.Name}", this.SelectedAddonName == option.Name))
{
this.SelectedAddonName = option.Name;
}
} }
col2.Pop();
} }
} }
tree.Dispose();
} }
/// <summary> /// <summary>

View file

@ -31,17 +31,13 @@ internal partial class UiDebug2 : IDisposable
internal UiDebug2() internal UiDebug2()
{ {
this.elementSelector = new(this); this.elementSelector = new(this);
GameGui = Service<GameGui>.Get();
Log = new ModuleLog("UiDebug2");
} }
/// <inheritdoc cref="ModuleLog"/> /// <inheritdoc cref="ModuleLog"/>
internal static ModuleLog Log { get; set; } = null!; internal static ModuleLog Log { get; set; } = new("UiDebug2");
/// <inheritdoc cref="IGameGui"/> /// <inheritdoc cref="IGameGui"/>
internal static IGameGui GameGui { get; set; } = null!; internal static IGameGui GameGui { get; set; } = Service<GameGui>.Get();
/// <summary> /// <summary>
/// Gets a collection of <see cref="AddonTree"/> instances, each representing an <see cref="FFXIVClientStructs.FFXIV.Component.GUI.AtkUnitBase"/>. /// Gets a collection of <see cref="AddonTree"/> instances, each representing an <see cref="FFXIVClientStructs.FFXIV.Component.GUI.AtkUnitBase"/>.
@ -86,28 +82,28 @@ internal partial class UiDebug2 : IDisposable
private void DrawMainPanel() private void DrawMainPanel()
{ {
ImGui.SameLine(); ImGui.SameLine();
var ch = ImRaii.Child("###uiDebugMainPanel", new(-1, -1), true, HorizontalScrollbar);
if (this.elementSelector.Active) using (ImRaii.Child("###uiDebugMainPanel", new(-1, -1), true, HorizontalScrollbar))
{ {
this.elementSelector.DrawSelectorOutput(); if (this.elementSelector.Active)
}
else
{
if (this.SelectedAddonName != null)
{ {
var addonTree = AddonTree.GetOrCreate(this.SelectedAddonName); this.elementSelector.DrawSelectorOutput();
}
if (addonTree == null) else
{
if (this.SelectedAddonName != null)
{ {
this.SelectedAddonName = null; var addonTree = AddonTree.GetOrCreate(this.SelectedAddonName);
return;
}
addonTree.Draw(); if (addonTree == null)
{
this.SelectedAddonName = null;
return;
}
addonTree.Draw();
}
} }
} }
ch.Dispose();
} }
} }

View file

@ -24,8 +24,8 @@ internal static class Gui
/// <typeparam name="T">The type of value being set.</typeparam> /// <typeparam name="T">The type of value being set.</typeparam>
/// <param name="label">The label for the inputs.</param> /// <param name="label">The label for the inputs.</param>
/// <param name="val">The value being set.</param> /// <param name="val">The value being set.</param>
/// <param name="options">A list of all options to create buttons for.</param> /// <param name="options">A list of all options.</param>
/// <param name="icons">A list of the icons to use for each option.</param> /// <param name="icons">A list of icons corresponding to the options.</param>
/// <returns>true if a button is clicked.</returns> /// <returns>true if a button is clicked.</returns>
internal static unsafe bool IconSelectInput<T>(string label, ref T val, List<T> options, List<FontAwesomeIcon> icons) internal static unsafe bool IconSelectInput<T>(string label, ref T val, List<T> options, List<FontAwesomeIcon> icons)
{ {
@ -104,14 +104,11 @@ internal static class Gui
/// <remarks>Colors the text itself either white or black, depending on the luminosity of the background color.</remarks> /// <remarks>Colors the text itself either white or black, depending on the luminosity of the background color.</remarks>
internal static void PrintColor(Vector4 color, string fmt) internal static void PrintColor(Vector4 color, string fmt)
{ {
var c = new ImRaii.Color().Push(Text, Luminosity(color) < 0.5f ? new Vector4(1) : new(0, 0, 0, 1)) using (new ImRaii.Color().Push(Text, Luminosity(color) < 0.5f ? new Vector4(1) : new(0, 0, 0, 1)).Push(Button, color).Push(ButtonActive, color).Push(ButtonHovered, color))
.Push(Button, color) {
.Push(ButtonActive, color) ImGui.SmallButton(fmt);
.Push(ButtonHovered, color); }
ImGui.SmallButton(fmt);
c.Pop(4);
return; return;
static double Luminosity(Vector4 vector4) => static double Luminosity(Vector4 vector4) =>
@ -125,20 +122,21 @@ internal static class Gui
/// <inheritdoc cref="ImGuiHelpers.ClickToCopyText"/> /// <inheritdoc cref="ImGuiHelpers.ClickToCopyText"/>
internal static void ClickToCopyText(string text, string? textCopy = null) internal static void ClickToCopyText(string text, string? textCopy = null)
{ {
var c = ImRaii.PushColor(Text, new Vector4(0.6f, 0.6f, 0.6f, 1)); using (ImRaii.PushColor(Text, new Vector4(0.6f, 0.6f, 0.6f, 1)))
ImGuiHelpers.ClickToCopyText(text, textCopy); {
c.Pop(); ImGuiHelpers.ClickToCopyText(text, textCopy);
}
if (ImGui.IsItemHovered()) if (ImGui.IsItemHovered())
{ {
var t = ImRaii.Tooltip(); using (ImRaii.Tooltip())
var f = ImRaii.PushFont(UiBuilder.IconFont); {
ImGui.Text(FontAwesomeIcon.Copy.ToIconString()); using var f = ImRaii.PushFont(UiBuilder.IconFont);
f.Pop(); ImGui.Text(FontAwesomeIcon.Copy.ToIconString());
ImGui.SameLine(); f.Pop();
ImGui.Text($"{textCopy ?? text}"); ImGui.SameLine();
ImGui.Text($"{textCopy ?? text}");
t.Dispose(); }
} }
} }
@ -161,9 +159,10 @@ internal static class Gui
var index = (int)Math.Floor(prog * tooltips.Length); var index = (int)Math.Floor(prog * tooltips.Length);
var t = ImRaii.Tooltip(); using (ImRaii.Tooltip())
ImGui.Text(tooltips[index]); {
t.Dispose(); ImGui.Text(tooltips[index]);
}
return true; return true;
} }

View file

@ -7,6 +7,7 @@ using FFXIVClientStructs.FFXIV.Component.GUI;
using ImGuiNET; using ImGuiNET;
using static System.Math; using static System.Math;
using static Dalamud.Interface.ColorHelpers;
namespace Dalamud.Interface.Internal.UiDebug2.Utility; namespace Dalamud.Interface.Internal.UiDebug2.Utility;
@ -61,8 +62,8 @@ public unsafe struct NodeBounds
if (this.Points.Count == 1) if (this.Points.Count == 1)
{ {
ImGui.GetBackgroundDrawList().AddCircle(this.Points[0], 10, ColorHelpers.RgbaVector4ToUint(col with { W = col.W / 2 }), 12, thickness); ImGui.GetBackgroundDrawList().AddCircle(this.Points[0], 10, RgbaVector4ToUint(col with { W = col.W / 2 }), 12, thickness);
ImGui.GetBackgroundDrawList().AddCircle(this.Points[0], thickness, ColorHelpers.RgbaVector4ToUint(col), 12, thickness + 1); ImGui.GetBackgroundDrawList().AddCircle(this.Points[0], thickness, RgbaVector4ToUint(col), 12, thickness + 1);
} }
else else
{ {
@ -73,7 +74,7 @@ public unsafe struct NodeBounds
} }
ImGui.GetBackgroundDrawList() ImGui.GetBackgroundDrawList()
.AddPolyline(ref path[0], path.Length, ColorHelpers.RgbaVector4ToUint(col), ImDrawFlags.Closed, thickness); .AddPolyline(ref path[0], path.Length, RgbaVector4ToUint(col), ImDrawFlags.Closed, thickness);
path.Dispose(); path.Dispose();
} }
@ -94,8 +95,8 @@ public unsafe struct NodeBounds
if (this.Points.Count == 1) if (this.Points.Count == 1)
{ {
ImGui.GetBackgroundDrawList() ImGui.GetBackgroundDrawList()
.AddCircleFilled(this.Points[0], 10, ColorHelpers.RgbaVector4ToUint(col with { W = col.W / 2 }), 12); .AddCircleFilled(this.Points[0], 10, RgbaVector4ToUint(col with { W = col.W / 2 }), 12);
ImGui.GetBackgroundDrawList().AddCircle(this.Points[0], 10, ColorHelpers.RgbaVector4ToUint(col), 12, thickness); ImGui.GetBackgroundDrawList().AddCircle(this.Points[0], 10, RgbaVector4ToUint(col), 12, thickness);
} }
else else
{ {
@ -106,9 +107,9 @@ public unsafe struct NodeBounds
} }
ImGui.GetBackgroundDrawList() ImGui.GetBackgroundDrawList()
.AddConvexPolyFilled(ref path[0], path.Length, ColorHelpers.RgbaVector4ToUint(col with { W = col.W / 2 })); .AddConvexPolyFilled(ref path[0], path.Length, RgbaVector4ToUint(col with { W = col.W / 2 }));
ImGui.GetBackgroundDrawList() ImGui.GetBackgroundDrawList()
.AddPolyline(ref path[0], path.Length, ColorHelpers.RgbaVector4ToUint(col), ImDrawFlags.Closed, thickness); .AddPolyline(ref path[0], path.Length, RgbaVector4ToUint(col), ImDrawFlags.Closed, thickness);
path.Dispose(); path.Dispose();
} }
@ -147,7 +148,9 @@ public unsafe struct NodeBounds
var sinR = (float)Sin(r); var sinR = (float)Sin(r);
var d = (p - o) * s; var d = (p - o) * s;
return new(o.X + (d.X * cosR) - (d.Y * sinR), o.Y + (d.X * sinR) + (d.Y * cosR)); return new(
o.X + (d.X * cosR) - (d.Y * sinR),
o.Y + (d.X * sinR) + (d.Y * cosR));
} }
private void TransformPoints(AtkResNode* transformNode) private void TransformPoints(AtkResNode* transformNode)

View file

@ -21,6 +21,7 @@ internal class DataWindow : Window, IDisposable
private readonly IDataWindowWidget[] modules = private readonly IDataWindowWidget[] modules =
{ {
new AddonInspectorWidget(), new AddonInspectorWidget(),
new AddonInspectorWidget2(),
new AddonLifecycleWidget(), new AddonLifecycleWidget(),
new AddonWidget(), new AddonWidget(),
new AddressesWidget(), new AddressesWidget(),

View file

@ -5,7 +5,7 @@ namespace Dalamud.Interface.Internal.Windows.Data.Widgets;
/// </summary> /// </summary>
internal class AddonInspectorWidget : IDataWindowWidget internal class AddonInspectorWidget : IDataWindowWidget
{ {
private UiDebug2.UiDebug2? addonInspector; private UiDebug? addonInspector;
/// <inheritdoc/> /// <inheritdoc/>
public string[]? CommandShortcuts { get; init; } = { "ai", "addoninspector" }; public string[]? CommandShortcuts { get; init; } = { "ai", "addoninspector" };
@ -19,7 +19,7 @@ internal class AddonInspectorWidget : IDataWindowWidget
/// <inheritdoc/> /// <inheritdoc/>
public void Load() public void Load()
{ {
this.addonInspector = new UiDebug2.UiDebug2(); this.addonInspector = new UiDebug();
if (this.addonInspector is not null) if (this.addonInspector is not null)
{ {

View file

@ -0,0 +1,35 @@
namespace Dalamud.Interface.Internal.Windows.Data.Widgets;
/// <summary>
/// Widget for displaying addon inspector.
/// </summary>
internal class AddonInspectorWidget2 : IDataWindowWidget
{
private UiDebug2.UiDebug2? addonInspector2;
/// <inheritdoc/>
public string[]? CommandShortcuts { get; init; } = ["ai2", "addoninspector2"];
/// <inheritdoc/>
public string DisplayName { get; init; } = "Addon Inspector v2 (Testing)";
/// <inheritdoc/>
public bool Ready { get; set; }
/// <inheritdoc/>
public void Load()
{
this.addonInspector2 = new UiDebug2.UiDebug2();
if (this.addonInspector2 is not null)
{
this.Ready = true;
}
}
/// <inheritdoc/>
public void Draw()
{
this.addonInspector2?.Draw();
}
}