Skip to content

Commit

Permalink
Add jumping to mods in OnScreen tab.
Browse files Browse the repository at this point in the history
  • Loading branch information
Ottermandias committed Nov 29, 2024
1 parent 8b9f594 commit d7095af
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 57 deletions.
3 changes: 3 additions & 0 deletions Penumbra/Interop/ResourceTree/ResourceNode.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Penumbra.Api.Enums;
using Penumbra.Mods;
using Penumbra.String;
using Penumbra.String.Classes;
using Penumbra.UI;
Expand All @@ -16,6 +17,7 @@ public class ResourceNode : ICloneable
public Utf8GamePath[] PossibleGamePaths;
public FullPath FullPath;
public string? ModName;
public readonly WeakReference<Mod> Mod = new(null!);
public string? ModRelativePath;
public CiByteString AdditionalData;
public readonly ulong Length;
Expand Down Expand Up @@ -60,6 +62,7 @@ private ResourceNode(ResourceNode other)
PossibleGamePaths = other.PossibleGamePaths;
FullPath = other.FullPath;
ModName = other.ModName;
Mod = other.Mod;
ModRelativePath = other.ModRelativePath;
AdditionalData = other.AdditionalData;
Length = other.Length;
Expand Down
1 change: 1 addition & 0 deletions Penumbra/Interop/ResourceTree/ResourceTreeFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ private void ResolveModData(ResourceTree tree)
if (node.FullPath.IsRooted && modManager.TryIdentifyPath(node.FullPath.FullName, out var mod, out var relativePath))
{
node.ModName = mod.Name;
node.Mod.SetTarget(mod);
node.ModRelativePath = relativePath;
}
}
Expand Down
112 changes: 58 additions & 54 deletions Penumbra/UI/AdvancedWindow/ResourceTreeViewer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,55 +3,40 @@
using ImGuiNET;
using OtterGui.Raii;
using OtterGui;
using OtterGui.Text;
using Penumbra.Api.Enums;
using Penumbra.Interop.ResourceTree;
using Penumbra.Services;
using Penumbra.UI.Classes;
using Penumbra.String;

namespace Penumbra.UI.AdvancedWindow;

public class ResourceTreeViewer
public class ResourceTreeViewer(
Configuration config,
ResourceTreeFactory treeFactory,
ChangedItemDrawer changedItemDrawer,
IncognitoService incognito,
int actionCapacity,
Action onRefresh,
Action<ResourceNode, Vector2> drawActions,
CommunicatorService communicator)
{
private const ResourceTreeFactory.Flags ResourceTreeFactoryFlags =
ResourceTreeFactory.Flags.RedactExternalPaths | ResourceTreeFactory.Flags.WithUiData | ResourceTreeFactory.Flags.WithOwnership;

private readonly Configuration _config;
private readonly ResourceTreeFactory _treeFactory;
private readonly ChangedItemDrawer _changedItemDrawer;
private readonly IncognitoService _incognito;
private readonly int _actionCapacity;
private readonly Action _onRefresh;
private readonly Action<ResourceNode, Vector2> _drawActions;
private readonly HashSet<nint> _unfolded;
private readonly CommunicatorService _communicator = communicator;
private readonly HashSet<nint> _unfolded = [];

private readonly Dictionary<nint, NodeVisibility> _filterCache;
private readonly Dictionary<nint, NodeVisibility> _filterCache = [];

private TreeCategory _categoryFilter;
private ChangedItemIconFlag _typeFilter;
private string _nameFilter;
private string _nodeFilter;
private TreeCategory _categoryFilter = AllCategories;
private ChangedItemIconFlag _typeFilter = ChangedItemFlagExtensions.AllFlags;
private string _nameFilter = string.Empty;
private string _nodeFilter = string.Empty;

private Task<ResourceTree[]>? _task;

public ResourceTreeViewer(Configuration config, ResourceTreeFactory treeFactory, ChangedItemDrawer changedItemDrawer,
IncognitoService incognito, int actionCapacity, Action onRefresh, Action<ResourceNode, Vector2> drawActions)
{
_config = config;
_treeFactory = treeFactory;
_changedItemDrawer = changedItemDrawer;
_incognito = incognito;
_actionCapacity = actionCapacity;
_onRefresh = onRefresh;
_drawActions = drawActions;
_unfolded = [];

_filterCache = [];

_categoryFilter = AllCategories;
_typeFilter = ChangedItemFlagExtensions.AllFlags;
_nameFilter = string.Empty;
_nodeFilter = string.Empty;
}

public void Draw()
{
DrawControls();
Expand All @@ -74,7 +59,7 @@ public void Draw()
}
else if (_task.IsCompletedSuccessfully)
{
var debugMode = _config.DebugMode;
var debugMode = config.DebugMode;
foreach (var (tree, index) in _task.Result.WithIndex())
{
var category = Classify(tree);
Expand All @@ -83,7 +68,7 @@ public void Draw()

using (var c = ImRaii.PushColor(ImGuiCol.Text, CategoryColor(category).Value()))
{
var isOpen = ImGui.CollapsingHeader($"{(_incognito.IncognitoMode ? tree.AnonymizedName : tree.Name)}###{index}",
var isOpen = ImGui.CollapsingHeader($"{(incognito.IncognitoMode ? tree.AnonymizedName : tree.Name)}###{index}",
index == 0 ? ImGuiTreeNodeFlags.DefaultOpen : 0);
if (debugMode)
{
Expand All @@ -98,19 +83,19 @@ public void Draw()

using var id = ImRaii.PushId(index);

ImGui.TextUnformatted($"Collection: {(_incognito.IncognitoMode ? tree.AnonymizedCollectionName : tree.CollectionName)}");
ImGui.TextUnformatted($"Collection: {(incognito.IncognitoMode ? tree.AnonymizedCollectionName : tree.CollectionName)}");

using var table = ImRaii.Table("##ResourceTree", _actionCapacity > 0 ? 4 : 3,
using var table = ImRaii.Table("##ResourceTree", actionCapacity > 0 ? 4 : 3,
ImGuiTableFlags.SizingFixedFit | ImGuiTableFlags.RowBg);
if (!table)
continue;

ImGui.TableSetupColumn(string.Empty, ImGuiTableColumnFlags.WidthStretch, 0.2f);
ImGui.TableSetupColumn("Game Path", ImGuiTableColumnFlags.WidthStretch, 0.3f);
ImGui.TableSetupColumn("Actual Path", ImGuiTableColumnFlags.WidthStretch, 0.5f);
if (_actionCapacity > 0)
if (actionCapacity > 0)
ImGui.TableSetupColumn(string.Empty, ImGuiTableColumnFlags.WidthFixed,
(_actionCapacity - 1) * 3 * ImGuiHelpers.GlobalScale + _actionCapacity * ImGui.GetFrameHeight());
(actionCapacity - 1) * 3 * ImGuiHelpers.GlobalScale + actionCapacity * ImGui.GetFrameHeight());
ImGui.TableHeadersRow();

DrawNodes(tree.Nodes, 0, unchecked(tree.DrawObjectAddress * 31), 0);
Expand Down Expand Up @@ -150,7 +135,7 @@ private void DrawControls()
ImGui.SetCursorPosY(ImGui.GetCursorPosY() - yOffset);
using (ImRaii.Child("##typeFilter", new Vector2(ImGui.GetContentRegionAvail().X, ChangedItemDrawer.TypeFilterIconSize.Y)))
{
filterChanged |= _changedItemDrawer.DrawTypeFilter(ref _typeFilter);
filterChanged |= changedItemDrawer.DrawTypeFilter(ref _typeFilter);
}

var fieldWidth = (ImGui.GetContentRegionAvail().X - checkSpacing * 2.0f - ImGui.GetFrameHeightWithSpacing()) / 2.0f;
Expand All @@ -160,7 +145,7 @@ private void DrawControls()
ImGui.SetNextItemWidth(fieldWidth);
filterChanged |= ImGui.InputTextWithHint("##NodeFilter", "Filter by Item/Part Name or Path...", ref _nodeFilter, 128);
ImGui.SameLine(0, checkSpacing);
_incognito.DrawToggle(ImGui.GetFrameHeightWithSpacing());
incognito.DrawToggle(ImGui.GetFrameHeightWithSpacing());

if (filterChanged)
_filterCache.Clear();
Expand All @@ -171,24 +156,24 @@ private Task<ResourceTree[]> RefreshCharacterList()
{
try
{
return _treeFactory.FromObjectTable(ResourceTreeFactoryFlags)
return treeFactory.FromObjectTable(ResourceTreeFactoryFlags)
.Select(entry => entry.ResourceTree)
.ToArray();
}
finally
{
_filterCache.Clear();
_unfolded.Clear();
_onRefresh();
onRefresh();
}
});

private void DrawNodes(IEnumerable<ResourceNode> resourceNodes, int level, nint pathHash,
ChangedItemIconFlag parentFilterIconFlag)
{
var debugMode = _config.DebugMode;
var debugMode = config.DebugMode;
var frameHeight = ImGui.GetFrameHeight();
var cellHeight = _actionCapacity > 0 ? frameHeight : 0.0f;
var cellHeight = actionCapacity > 0 ? frameHeight : 0.0f;

foreach (var (resourceNode, index) in resourceNodes.WithIndex())
{
Expand Down Expand Up @@ -231,7 +216,7 @@ private void DrawNodes(IEnumerable<ResourceNode> resourceNodes, int level, nint
ImGui.SameLine(0f, ImGui.GetStyle().ItemInnerSpacing.X);
}

_changedItemDrawer.DrawCategoryIcon(resourceNode.IconFlag);
changedItemDrawer.DrawCategoryIcon(resourceNode.IconFlag);
ImGui.SameLine(0f, ImGui.GetStyle().ItemInnerSpacing.X);
ImGui.TableHeader(resourceNode.Name);
if (ImGui.IsItemClicked() && unfoldable)
Expand Down Expand Up @@ -270,14 +255,33 @@ private void DrawNodes(IEnumerable<ResourceNode> resourceNodes, int level, nint
ImGui.TableNextColumn();
if (resourceNode.FullPath.FullName.Length > 0)
{
var uiFullPathStr = resourceNode.ModName != null && resourceNode.ModRelativePath != null
? $"[{resourceNode.ModName}] {resourceNode.ModRelativePath}"
: resourceNode.FullPath.ToPath();
ImGui.Selectable(uiFullPathStr, false, 0, new Vector2(ImGui.GetContentRegionAvail().X, cellHeight));
var hasMod = resourceNode.Mod.TryGetTarget(out var mod);
if (resourceNode is { ModName: not null, ModRelativePath: not null })
{
var modName = $"[{(hasMod ? mod!.Name : resourceNode.ModName)}]";
var textPos = ImGui.GetCursorPosX() + ImUtf8.CalcTextSize(modName).X + ImGui.GetStyle().ItemInnerSpacing.X;
using var group = ImUtf8.Group();
using (var color = ImRaii.PushColor(ImGuiCol.Text, (hasMod ? ColorId.NewMod : ColorId.DisabledMod).Value()))
{
ImUtf8.Selectable(modName, false, ImGuiSelectableFlags.AllowItemOverlap, new Vector2(ImGui.GetContentRegionAvail().X, cellHeight));
}

ImGui.SameLine();
ImGui.SetCursorPosX(textPos);
ImUtf8.Text(resourceNode.ModRelativePath);
}
else
{
ImGui.Selectable(resourceNode.FullPath.ToPath(), false, ImGuiSelectableFlags.AllowItemOverlap, new Vector2(ImGui.GetContentRegionAvail().X, cellHeight));
}

if (ImGui.IsItemClicked())
ImGui.SetClipboardText(resourceNode.FullPath.ToPath());
if (hasMod && ImGui.IsItemClicked(ImGuiMouseButton.Right) && ImGui.GetIO().KeyCtrl)
_communicator.SelectTab.Invoke(TabType.Mods, mod);

ImGuiUtil.HoverTooltip(
$"{resourceNode.FullPath.ToPath()}\n\nClick to copy to clipboard.{GetAdditionalDataSuffix(resourceNode.AdditionalData)}");
$"{resourceNode.FullPath.ToPath()}\n\nClick to copy to clipboard.{(hasMod ? "\nControl + Right-Click to jump to mod." : string.Empty)}{GetAdditionalDataSuffix(resourceNode.AdditionalData)}");
}
else
{
Expand All @@ -289,12 +293,12 @@ private void DrawNodes(IEnumerable<ResourceNode> resourceNodes, int level, nint

mutedColor.Dispose();

if (_actionCapacity > 0)
if (actionCapacity > 0)
{
ImGui.TableNextColumn();
using var spacing = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing,
ImGui.GetStyle().ItemSpacing with { X = 3 * ImGuiHelpers.GlobalScale });
_drawActions(resourceNode, new Vector2(frameHeight));
drawActions(resourceNode, new Vector2(frameHeight));
}

if (unfolded)
Expand Down
6 changes: 4 additions & 2 deletions Penumbra/UI/AdvancedWindow/ResourceTreeViewerFactory.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
using OtterGui.Services;
using Penumbra.Interop.ResourceTree;
using Penumbra.Services;

namespace Penumbra.UI.AdvancedWindow;

public class ResourceTreeViewerFactory(
Configuration config,
ResourceTreeFactory treeFactory,
ChangedItemDrawer changedItemDrawer,
IncognitoService incognito) : IService
IncognitoService incognito,
CommunicatorService communicator) : IService
{
public ResourceTreeViewer Create(int actionCapacity, Action onRefresh, Action<ResourceNode, Vector2> drawActions)
=> new(config, treeFactory, changedItemDrawer, incognito, actionCapacity, onRefresh, drawActions);
=> new(config, treeFactory, changedItemDrawer, incognito, actionCapacity, onRefresh, drawActions, communicator);
}
8 changes: 7 additions & 1 deletion Penumbra/UI/Tabs/Debug/DebugTab.cs
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,7 @@ private unsafe void DrawActorsDebug()
if (!ImGui.CollapsingHeader("Actors"))
return;

using var table = Table("##actors", 5, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingFixedFit,
using var table = Table("##actors", 8, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingFixedFit,
-Vector2.UnitX);
if (!table)
return;
Expand All @@ -485,6 +485,9 @@ private unsafe void DrawActorsDebug()
? $"{identifier.DataId} | {obj.AsObject->BaseId}"
: identifier.DataId.ToString();
ImGuiUtil.DrawTableColumn(id);
ImGuiUtil.DrawTableColumn(obj.Address != nint.Zero ? $"0x{(*(nint*)obj.Address):X}" : "NULL");
ImGuiUtil.DrawTableColumn(obj.Address != nint.Zero ? $"0x{obj.AsObject->EntityId:X}" : "NULL");
ImGuiUtil.DrawTableColumn(obj.Address != nint.Zero ? obj.AsObject->IsCharacter() ? $"Character: {obj.AsCharacter->ObjectKind}" : "No Character" : "NULL");
}

return;
Expand All @@ -499,6 +502,9 @@ void DrawSpecial(string name, ActorIdentifier id)
ImGuiUtil.DrawTableColumn(string.Empty);
ImGuiUtil.DrawTableColumn(_actors.ToString(id));
ImGuiUtil.DrawTableColumn(string.Empty);
ImGuiUtil.DrawTableColumn(string.Empty);
ImGuiUtil.DrawTableColumn(string.Empty);
ImGuiUtil.DrawTableColumn(string.Empty);
}
}

Expand Down

0 comments on commit d7095af

Please sign in to comment.