mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-14 20:54:16 +01:00
Add jumping to mods in OnScreen tab.
This commit is contained in:
parent
8b9f59426e
commit
d7095af89b
5 changed files with 73 additions and 57 deletions
|
|
@ -1,4 +1,5 @@
|
|||
using Penumbra.Api.Enums;
|
||||
using Penumbra.Mods;
|
||||
using Penumbra.String;
|
||||
using Penumbra.String.Classes;
|
||||
using Penumbra.UI;
|
||||
|
|
@ -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;
|
||||
|
|
@ -60,6 +62,7 @@ public class ResourceNode : ICloneable
|
|||
PossibleGamePaths = other.PossibleGamePaths;
|
||||
FullPath = other.FullPath;
|
||||
ModName = other.ModName;
|
||||
Mod = other.Mod;
|
||||
ModRelativePath = other.ModRelativePath;
|
||||
AdditionalData = other.AdditionalData;
|
||||
Length = other.Length;
|
||||
|
|
|
|||
|
|
@ -135,6 +135,7 @@ public class ResourceTreeFactory(
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,55 +3,40 @@ using Dalamud.Interface.Utility;
|
|||
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();
|
||||
|
|
@ -74,7 +59,7 @@ public class ResourceTreeViewer
|
|||
}
|
||||
else if (_task.IsCompletedSuccessfully)
|
||||
{
|
||||
var debugMode = _config.DebugMode;
|
||||
var debugMode = config.DebugMode;
|
||||
foreach (var (tree, index) in _task.Result.WithIndex())
|
||||
{
|
||||
var category = Classify(tree);
|
||||
|
|
@ -83,7 +68,7 @@ public class ResourceTreeViewer
|
|||
|
||||
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)
|
||||
{
|
||||
|
|
@ -98,9 +83,9 @@ public class ResourceTreeViewer
|
|||
|
||||
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;
|
||||
|
|
@ -108,9 +93,9 @@ public class ResourceTreeViewer
|
|||
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);
|
||||
|
|
@ -150,7 +135,7 @@ public class ResourceTreeViewer
|
|||
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;
|
||||
|
|
@ -160,7 +145,7 @@ public class ResourceTreeViewer
|
|||
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();
|
||||
|
|
@ -171,7 +156,7 @@ public class ResourceTreeViewer
|
|||
{
|
||||
try
|
||||
{
|
||||
return _treeFactory.FromObjectTable(ResourceTreeFactoryFlags)
|
||||
return treeFactory.FromObjectTable(ResourceTreeFactoryFlags)
|
||||
.Select(entry => entry.ResourceTree)
|
||||
.ToArray();
|
||||
}
|
||||
|
|
@ -179,16 +164,16 @@ public class ResourceTreeViewer
|
|||
{
|
||||
_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())
|
||||
{
|
||||
|
|
@ -231,7 +216,7 @@ public class ResourceTreeViewer
|
|||
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)
|
||||
|
|
@ -270,14 +255,33 @@ public class ResourceTreeViewer
|
|||
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
|
||||
{
|
||||
|
|
@ -289,12 +293,12 @@ public class ResourceTreeViewer
|
|||
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using OtterGui.Services;
|
||||
using Penumbra.Interop.ResourceTree;
|
||||
using Penumbra.Services;
|
||||
|
||||
namespace Penumbra.UI.AdvancedWindow;
|
||||
|
||||
|
|
@ -7,8 +8,9 @@ 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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -461,7 +461,7 @@ public class DebugTab : Window, ITab, IUiService
|
|||
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;
|
||||
|
|
@ -485,6 +485,9 @@ public class DebugTab : Window, ITab, IUiService
|
|||
? $"{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;
|
||||
|
|
@ -499,6 +502,9 @@ public class DebugTab : Window, ITab, IUiService
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue