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? FallbackName;
public ChangedItemIcon Icon;
public ChangedItemIcon DescendentIcons;
public readonly ResourceType Type;
public readonly nint ObjectAddress;
public readonly nint ResourceHandle;
@ -53,7 +52,6 @@ public class ResourceNode : ICloneable
Name = other.Name;
FallbackName = other.FallbackName;
Icon = other.Icon;
DescendentIcons = other.DescendentIcons;
Type = other.Type;
ObjectAddress = other.ObjectAddress;
ResourceHandle = other.ResourceHandle;

View file

@ -116,9 +116,6 @@ public class ResourceTreeFactory(
{
if (node.Name == parent?.Name)
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 HashSet<nint> _unfolded;
private readonly Dictionary<nint, NodeVisibility> _filterCache;
private TreeCategory _categoryFilter;
private ChangedItemDrawer.ChangedItemIcon _typeFilter;
private string _nameFilter;
private string _nodeFilter;
private Task<ResourceTree[]>? _task;
@ -40,11 +43,14 @@ public class ResourceTreeViewer
_actionCapacity = actionCapacity;
_onRefresh = onRefresh;
_drawActions = drawActions;
_unfolded = new HashSet<nint>();
_unfolded = [];
_filterCache = [];
_categoryFilter = AllCategories;
_typeFilter = ChangedItemDrawer.AllFlags;
_nameFilter = string.Empty;
_nodeFilter = string.Empty;
}
public void Draw()
@ -107,7 +113,7 @@ public class ResourceTreeViewer
(_actionCapacity - 1) * 3 * ImGuiHelpers.GlobalScale + _actionCapacity * ImGui.GetFrameHeight());
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);
var filterChanged = false;
ImGui.SetCursorPosY(ImGui.GetCursorPosY() - yOffset);
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());
ImGui.InputTextWithHint("##TreeNameFilter", "Filter by Character/Entity Name...", ref _nameFilter, 128);
var fieldWidth = (ImGui.GetContentRegionAvail().X - checkSpacing * 2.0f - ImGui.GetFrameHeightWithSpacing()) / 2.0f;
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);
_incognito.DrawToggle();
if (filterChanged)
_filterCache.Clear();
}
private Task<ResourceTree[]> RefreshCharacterList()
@ -161,36 +175,68 @@ public class ResourceTreeViewer
}
finally
{
_filterCache.Clear();
_unfolded.Clear();
_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 frameHeight = ImGui.GetFrameHeight();
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)
return NodeVisibility.Hidden;
if (_typeFilter.HasFlag(node.Icon))
var filterIcon = node.Icon != 0 ? node.Icon : parentFilterIcon;
if (MatchesFilter(node, filterIcon))
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;
}
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)
=> !debugMode || data.IsEmpty ? string.Empty : $"\n\nAdditional Data: {data}";
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)
continue;
@ -199,14 +245,14 @@ public class ResourceTreeViewer
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);
ImGui.TableNextColumn();
var unfolded = _unfolded.Contains(nodePathHash);
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;
if (unfoldable)
{
@ -291,7 +337,7 @@ public class ResourceTreeViewer
}
if (unfolded)
DrawNodes(resourceNode.Children, level + 1, unchecked(nodePathHash * 31));
DrawNodes(resourceNode.Children, level + 1, unchecked(nodePathHash * 31), filterIcon);
}
}