ResourceTree: Add name/path filter

This commit is contained in:
Exter-N 2024-05-31 01:05:05 +02:00
parent f4bdbcac53
commit c7046ec006
3 changed files with 60 additions and 19 deletions

View file

@ -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;

View file

@ -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;
}); });
} }

View file

@ -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);
} }
} }