mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-15 05:04:15 +01:00
ResourceTree: Add name/path filter
This commit is contained in:
parent
f4bdbcac53
commit
c7046ec006
3 changed files with 60 additions and 19 deletions
|
|
@ -10,7 +10,6 @@ public class ResourceNode : ICloneable
|
||||||
public string? Name;
|
public string? Name;
|
||||||
public string? FallbackName;
|
public string? FallbackName;
|
||||||
public ChangedItemIcon Icon;
|
public ChangedItemIcon Icon;
|
||||||
public ChangedItemIcon DescendentIcons;
|
|
||||||
public readonly ResourceType Type;
|
public readonly ResourceType Type;
|
||||||
public readonly nint ObjectAddress;
|
public readonly nint ObjectAddress;
|
||||||
public readonly nint ResourceHandle;
|
public readonly nint ResourceHandle;
|
||||||
|
|
@ -53,7 +52,6 @@ public class ResourceNode : ICloneable
|
||||||
Name = other.Name;
|
Name = other.Name;
|
||||||
FallbackName = other.FallbackName;
|
FallbackName = other.FallbackName;
|
||||||
Icon = other.Icon;
|
Icon = other.Icon;
|
||||||
DescendentIcons = other.DescendentIcons;
|
|
||||||
Type = other.Type;
|
Type = other.Type;
|
||||||
ObjectAddress = other.ObjectAddress;
|
ObjectAddress = other.ObjectAddress;
|
||||||
ResourceHandle = other.ResourceHandle;
|
ResourceHandle = other.ResourceHandle;
|
||||||
|
|
|
||||||
|
|
@ -116,9 +116,6 @@ public class ResourceTreeFactory(
|
||||||
{
|
{
|
||||||
if (node.Name == parent?.Name)
|
if (node.Name == parent?.Name)
|
||||||
node.Name = null;
|
node.Name = null;
|
||||||
|
|
||||||
if (parent != null)
|
|
||||||
parent.DescendentIcons |= node.Icon | node.DescendentIcons;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,9 +24,12 @@ public class ResourceTreeViewer
|
||||||
private readonly Action<ResourceNode, Vector2> _drawActions;
|
private readonly Action<ResourceNode, Vector2> _drawActions;
|
||||||
private readonly HashSet<nint> _unfolded;
|
private readonly HashSet<nint> _unfolded;
|
||||||
|
|
||||||
|
private readonly Dictionary<nint, NodeVisibility> _filterCache;
|
||||||
|
|
||||||
private TreeCategory _categoryFilter;
|
private TreeCategory _categoryFilter;
|
||||||
private ChangedItemDrawer.ChangedItemIcon _typeFilter;
|
private ChangedItemDrawer.ChangedItemIcon _typeFilter;
|
||||||
private string _nameFilter;
|
private string _nameFilter;
|
||||||
|
private string _nodeFilter;
|
||||||
|
|
||||||
private Task<ResourceTree[]>? _task;
|
private Task<ResourceTree[]>? _task;
|
||||||
|
|
||||||
|
|
@ -40,11 +43,14 @@ public class ResourceTreeViewer
|
||||||
_actionCapacity = actionCapacity;
|
_actionCapacity = actionCapacity;
|
||||||
_onRefresh = onRefresh;
|
_onRefresh = onRefresh;
|
||||||
_drawActions = drawActions;
|
_drawActions = drawActions;
|
||||||
_unfolded = new HashSet<nint>();
|
_unfolded = [];
|
||||||
|
|
||||||
|
_filterCache = [];
|
||||||
|
|
||||||
_categoryFilter = AllCategories;
|
_categoryFilter = AllCategories;
|
||||||
_typeFilter = ChangedItemDrawer.AllFlags;
|
_typeFilter = ChangedItemDrawer.AllFlags;
|
||||||
_nameFilter = string.Empty;
|
_nameFilter = string.Empty;
|
||||||
|
_nodeFilter = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Draw()
|
public void Draw()
|
||||||
|
|
@ -107,7 +113,7 @@ public class ResourceTreeViewer
|
||||||
(_actionCapacity - 1) * 3 * ImGuiHelpers.GlobalScale + _actionCapacity * ImGui.GetFrameHeight());
|
(_actionCapacity - 1) * 3 * ImGuiHelpers.GlobalScale + _actionCapacity * ImGui.GetFrameHeight());
|
||||||
ImGui.TableHeadersRow();
|
ImGui.TableHeadersRow();
|
||||||
|
|
||||||
DrawNodes(tree.Nodes, 0, unchecked(tree.DrawObjectAddress * 31));
|
DrawNodes(tree.Nodes, 0, unchecked(tree.DrawObjectAddress * 31), 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -140,14 +146,22 @@ public class ResourceTreeViewer
|
||||||
|
|
||||||
ImGui.SameLine(0, checkPadding);
|
ImGui.SameLine(0, checkPadding);
|
||||||
|
|
||||||
|
var filterChanged = false;
|
||||||
ImGui.SetCursorPosY(ImGui.GetCursorPosY() - yOffset);
|
ImGui.SetCursorPosY(ImGui.GetCursorPosY() - yOffset);
|
||||||
using (ImRaii.Child("##typeFilter", new Vector2(ImGui.GetContentRegionAvail().X, ChangedItemDrawer.TypeFilterIconSize.Y)))
|
using (ImRaii.Child("##typeFilter", new Vector2(ImGui.GetContentRegionAvail().X, ChangedItemDrawer.TypeFilterIconSize.Y)))
|
||||||
_changedItemDrawer.DrawTypeFilter(ref _typeFilter);
|
filterChanged |= _changedItemDrawer.DrawTypeFilter(ref _typeFilter);
|
||||||
|
|
||||||
ImGui.SetNextItemWidth(ImGui.GetContentRegionAvail().X - checkSpacing - ImGui.GetFrameHeightWithSpacing());
|
var fieldWidth = (ImGui.GetContentRegionAvail().X - checkSpacing * 2.0f - ImGui.GetFrameHeightWithSpacing()) / 2.0f;
|
||||||
ImGui.InputTextWithHint("##TreeNameFilter", "Filter by Character/Entity Name...", ref _nameFilter, 128);
|
ImGui.SetNextItemWidth(fieldWidth);
|
||||||
|
filterChanged |= ImGui.InputTextWithHint("##TreeNameFilter", "Filter by Character/Entity Name...", ref _nameFilter, 128);
|
||||||
|
ImGui.SameLine(0, checkSpacing);
|
||||||
|
ImGui.SetNextItemWidth(fieldWidth);
|
||||||
|
filterChanged |= ImGui.InputTextWithHint("##NodeFilter", "Filter by Item/Part Name or Path...", ref _nodeFilter, 128);
|
||||||
ImGui.SameLine(0, checkSpacing);
|
ImGui.SameLine(0, checkSpacing);
|
||||||
_incognito.DrawToggle();
|
_incognito.DrawToggle();
|
||||||
|
|
||||||
|
if (filterChanged)
|
||||||
|
_filterCache.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task<ResourceTree[]> RefreshCharacterList()
|
private Task<ResourceTree[]> RefreshCharacterList()
|
||||||
|
|
@ -161,36 +175,68 @@ public class ResourceTreeViewer
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
_filterCache.Clear();
|
||||||
_unfolded.Clear();
|
_unfolded.Clear();
|
||||||
_onRefresh();
|
_onRefresh();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
private void DrawNodes(IEnumerable<ResourceNode> resourceNodes, int level, nint pathHash)
|
private void DrawNodes(IEnumerable<ResourceNode> resourceNodes, int level, nint pathHash, ChangedItemDrawer.ChangedItemIcon parentFilterIcon)
|
||||||
{
|
{
|
||||||
var debugMode = _config.DebugMode;
|
var debugMode = _config.DebugMode;
|
||||||
var frameHeight = ImGui.GetFrameHeight();
|
var frameHeight = ImGui.GetFrameHeight();
|
||||||
var cellHeight = _actionCapacity > 0 ? frameHeight : 0.0f;
|
var cellHeight = _actionCapacity > 0 ? frameHeight : 0.0f;
|
||||||
|
|
||||||
NodeVisibility GetNodeVisibility(ResourceNode node)
|
bool MatchesFilter(ResourceNode node, ChangedItemDrawer.ChangedItemIcon filterIcon)
|
||||||
|
{
|
||||||
|
if (!_typeFilter.HasFlag(filterIcon))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (_nodeFilter.Length == 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return node.Name != null && node.Name.Contains(_nodeFilter, StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| node.FullPath.FullName.Contains(_nodeFilter, StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| node.FullPath.InternalName.ToString().Contains(_nodeFilter, StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| Array.Exists(node.PossibleGamePaths, path => path.Path.ToString().Contains(_nodeFilter, StringComparison.OrdinalIgnoreCase));
|
||||||
|
}
|
||||||
|
|
||||||
|
NodeVisibility CalculateNodeVisibility(nint nodePathHash, ResourceNode node, ChangedItemDrawer.ChangedItemIcon parentFilterIcon)
|
||||||
{
|
{
|
||||||
if (node.Internal && !debugMode)
|
if (node.Internal && !debugMode)
|
||||||
return NodeVisibility.Hidden;
|
return NodeVisibility.Hidden;
|
||||||
|
|
||||||
if (_typeFilter.HasFlag(node.Icon))
|
var filterIcon = node.Icon != 0 ? node.Icon : parentFilterIcon;
|
||||||
|
if (MatchesFilter(node, filterIcon))
|
||||||
return NodeVisibility.Visible;
|
return NodeVisibility.Visible;
|
||||||
if ((_typeFilter & node.DescendentIcons) != 0)
|
|
||||||
return NodeVisibility.DescendentsOnly;
|
foreach (var child in node.Children)
|
||||||
|
{
|
||||||
|
if (GetNodeVisibility(unchecked(nodePathHash * 31 + child.ResourceHandle), child, filterIcon) != NodeVisibility.Hidden)
|
||||||
|
return NodeVisibility.DescendentsOnly;
|
||||||
|
}
|
||||||
|
|
||||||
return NodeVisibility.Hidden;
|
return NodeVisibility.Hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NodeVisibility GetNodeVisibility(nint nodePathHash, ResourceNode node, ChangedItemDrawer.ChangedItemIcon parentFilterIcon)
|
||||||
|
{
|
||||||
|
if (!_filterCache.TryGetValue(nodePathHash, out var visibility))
|
||||||
|
{
|
||||||
|
visibility = CalculateNodeVisibility(nodePathHash, node, parentFilterIcon);
|
||||||
|
_filterCache.Add(nodePathHash, visibility);
|
||||||
|
}
|
||||||
|
return visibility;
|
||||||
|
}
|
||||||
|
|
||||||
string GetAdditionalDataSuffix(ByteString data)
|
string GetAdditionalDataSuffix(ByteString data)
|
||||||
=> !debugMode || data.IsEmpty ? string.Empty : $"\n\nAdditional Data: {data}";
|
=> !debugMode || data.IsEmpty ? string.Empty : $"\n\nAdditional Data: {data}";
|
||||||
|
|
||||||
foreach (var (resourceNode, index) in resourceNodes.WithIndex())
|
foreach (var (resourceNode, index) in resourceNodes.WithIndex())
|
||||||
{
|
{
|
||||||
var visibility = GetNodeVisibility(resourceNode);
|
var nodePathHash = unchecked(pathHash + resourceNode.ResourceHandle);
|
||||||
|
|
||||||
|
var visibility = GetNodeVisibility(nodePathHash, resourceNode, parentFilterIcon);
|
||||||
if (visibility == NodeVisibility.Hidden)
|
if (visibility == NodeVisibility.Hidden)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
|
@ -199,14 +245,14 @@ public class ResourceTreeViewer
|
||||||
|
|
||||||
using var mutedColor = ImRaii.PushColor(ImGuiCol.Text, textColorInternal, resourceNode.Internal);
|
using var mutedColor = ImRaii.PushColor(ImGuiCol.Text, textColorInternal, resourceNode.Internal);
|
||||||
|
|
||||||
var nodePathHash = unchecked(pathHash + resourceNode.ResourceHandle);
|
var filterIcon = resourceNode.Icon != 0 ? resourceNode.Icon : parentFilterIcon;
|
||||||
|
|
||||||
using var id = ImRaii.PushId(index);
|
using var id = ImRaii.PushId(index);
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
var unfolded = _unfolded.Contains(nodePathHash);
|
var unfolded = _unfolded.Contains(nodePathHash);
|
||||||
using (var indent = ImRaii.PushIndent(level))
|
using (var indent = ImRaii.PushIndent(level))
|
||||||
{
|
{
|
||||||
var hasVisibleChildren = resourceNode.Children.Any(child => GetNodeVisibility(child) != NodeVisibility.Hidden);
|
var hasVisibleChildren = resourceNode.Children.Any(child => GetNodeVisibility(unchecked(nodePathHash * 31 + child.ResourceHandle), child, filterIcon) != NodeVisibility.Hidden);
|
||||||
var unfoldable = hasVisibleChildren && visibility != NodeVisibility.DescendentsOnly;
|
var unfoldable = hasVisibleChildren && visibility != NodeVisibility.DescendentsOnly;
|
||||||
if (unfoldable)
|
if (unfoldable)
|
||||||
{
|
{
|
||||||
|
|
@ -291,7 +337,7 @@ public class ResourceTreeViewer
|
||||||
}
|
}
|
||||||
|
|
||||||
if (unfolded)
|
if (unfolded)
|
||||||
DrawNodes(resourceNode.Children, level + 1, unchecked(nodePathHash * 31));
|
DrawNodes(resourceNode.Children, level + 1, unchecked(nodePathHash * 31), filterIcon);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue