diff --git a/UI/Controls/NodeTree/PathingCategoryNode.cs b/UI/Controls/NodeTree/PathingCategoryNode.cs index 4124127..79c639c 100644 --- a/UI/Controls/NodeTree/PathingCategoryNode.cs +++ b/UI/Controls/NodeTree/PathingCategoryNode.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using BhModule.Community.Pathing.Behavior.Modifier; @@ -39,7 +39,8 @@ public bool Active public PathingCategory PathingCategory { get; } private PathingCategory PackCategory { get; } - private readonly IList _entities; + private IEnumerable _entities => this.TreeView?.EntityLookup?[this.PathingCategory] + ?? CategoryUtil.GetAssociatedPathingEntities(this.PathingCategory, _packState.Entities); private readonly bool _forceShowAll = false; @@ -53,7 +54,6 @@ public bool Active public PathingCategoryNode(IPackState packState, PathingCategory pathingCategory, bool showForceAll) : base(pathingCategory.DisplayName) { _packState = packState; this.PathingCategory = pathingCategory; - _entities = CategoryUtil.GetAssociatedPathingEntities(pathingCategory, packState.Entities).ToList(); _forceShowAll = showForceAll; if (pathingCategory.IsSeparator) { @@ -63,13 +63,6 @@ public PathingCategoryNode(IPackState packState, PathingCategory pathingCategory this.ShowIconTooltip = false; } else { this.Checkable = true; - - if (_entities.Count <= 0 && (this.PathingCategory.IsSeparator || this.PathingCategory.Count > 0)) { - BackgroundOpacity = 0.3f; - } else { - BackgroundOpacity = 0.05f; - BackgroundOpaqueColor = Color.LightYellow; - } } DetectAndBuildContexts(); @@ -195,7 +188,7 @@ private void BuildAchievementTexture() { } private void BuildEntityCount() { - if (_entities.Count <= 0) { + if (!_entities.Any()) { if (!this.PathingCategory.IsSeparator && this.PathingCategory.Count <= 0) { _ = new Label { Parent = _propertiesPanel, @@ -365,6 +358,15 @@ private void UpdateChildrenActiveState() { protected override void OnParentChanged() { base.OnParentChanged(); + if (this.TreeView != null && !this.PathingCategory.IsSeparator) { + if (!_entities.Any() && (this.PathingCategory.IsSeparator || this.PathingCategory.Count > 0)) { + BackgroundOpacity = 0.3f; + } else { + BackgroundOpacity = 0.05f; + BackgroundOpaqueColor = Color.LightYellow; + } + } + if(Checkable) UpdateActiveState(Checked); } @@ -380,7 +382,7 @@ public void UpdateLabelActiveState() { public int AddSubNodes(bool forceShowAll) { if (this.PathingCategory.Count <= 0) return 0; - (IEnumerable subCategories, int skipped) = this.PathingCategory.FilterCategories(_packState, forceShowAll); + (IEnumerable subCategories, int skipped) = this.PathingCategory.FilterCategories(_packState, forceShowAll, this.TreeView?.EntityLookup); foreach (var subCategory in subCategories) { if (subCategory == null) continue; diff --git a/UI/Controls/NodeTree/TreeView.cs b/UI/Controls/NodeTree/TreeView.cs index 0a56c24..ea4aa20 100644 --- a/UI/Controls/NodeTree/TreeView.cs +++ b/UI/Controls/NodeTree/TreeView.cs @@ -1,8 +1,9 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; +using BhModule.Community.Pathing.Entity; using BhModule.Community.Pathing.State; using BhModule.Community.Pathing.UI.Controls.TreeNodes; using BhModule.Community.Pathing.Utility; @@ -24,12 +25,26 @@ public class TreeView : Container public PackInitiator PackInitiator { get; private set; } public IList AllBaseNodes { get; } = new List(); - public IList ChildBaseNodes { get; } = new List(); - private IList AllCategories { get; set; } = new List(); + private IList ChildBaseNodes { get; } = new List(); + private List _searchRecords { get; set; } = new List(); + + private class CategorySearchRecord { + public PathingCategory Category { get; } + public string NormalizedDisplayName { get; } + public string NormalizedName { get; } + + public CategorySearchRecord(PathingCategory category) { + this.Category = category; + this.NormalizedDisplayName = category.DisplayName?.Replace(" ", "") ?? ""; + this.NormalizedName = category.Name?.Replace(" ", "") ?? ""; + } + } public event EventHandler NodeLoadingStarted; public event EventHandler NodesLoadedFinished; + public ILookup EntityLookup { get; private set; } + public TreeView(PackInitiator packInitiator) { PackInitiator = packInitiator; } @@ -123,6 +138,8 @@ public void LoadNodes() { ClearChildNodes(); AllBaseNodes.Clear(); + this.EntityLookup = PackInitiator.PackState.Entities.ToArray().ToLookup(e => e.Category); + var rootCategory = PackInitiator.GetAllMarkersCategories(); if (rootCategory == null) return; @@ -147,7 +164,7 @@ public void LoadNodes() { _rootNode.Expand(); - AllCategories = CategoryUtil.FlattenCategories(rootCategory).ToList(); + _searchRecords = CategoryUtil.FlattenCategories(rootCategory).Select(c => new CategorySearchRecord(c)).ToList(); this.NodesLoadedFinished?.Invoke(this, EventArgs.Empty); } @@ -173,23 +190,16 @@ private void GlobalPathablesEnabledOnSettingChanged(object sender, ValueChangedE string normalizedInput = input.Replace(" ", ""); - var results = AllCategories - .AsParallel() - .WithCancellation(cancellationToken) + var results = _searchRecords .Where(c => { - if (string.IsNullOrWhiteSpace(c.DisplayName) || string.IsNullOrWhiteSpace(c.Name)) return false; - - string normalizedDisplayName = c.DisplayName?.Replace(" ", ""); - string normalizedName = c.Name.Replace(" ", ""); - - cancellationToken.ThrowIfCancellationRequested(); + if (string.IsNullOrWhiteSpace(c.Category.DisplayName) || string.IsNullOrWhiteSpace(c.Category.Name)) return false; - return (normalizedDisplayName != null && normalizedDisplayName.IndexOf(normalizedInput, StringComparison.OrdinalIgnoreCase) >= 0) || - normalizedName.IndexOf(normalizedInput, StringComparison.OrdinalIgnoreCase) >= 0; - }).ToList(); + return (c.NormalizedDisplayName.IndexOf(normalizedInput, StringComparison.OrdinalIgnoreCase) >= 0) || + c.NormalizedName.IndexOf(normalizedInput, StringComparison.OrdinalIgnoreCase) >= 0; + }).Select(c => c.Category).ToList(); - (filteredResults, skipped) = results.FilterCategories(PackInitiator.PackState, forceShowAll); + (filteredResults, skipped) = results.FilterCategories(PackInitiator.PackState, forceShowAll, this.EntityLookup); return (filteredResults.ToList(), skipped); } diff --git a/Utility/CategoryUtil.cs b/Utility/CategoryUtil.cs index 2b7a4ca..2b1e447 100644 --- a/Utility/CategoryUtil.cs +++ b/Utility/CategoryUtil.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using BhModule.Community.Pathing.Entity; @@ -39,7 +39,7 @@ public static string GetPath(this PathingCategory category) { return $".{category.Namespace}"; } - public static (IEnumerable, int skipped) FilterCategories(this IEnumerable categories, IPackState packState, bool forceShowAll = false) { + public static (IEnumerable, int skipped) FilterCategories(this IEnumerable categories, IPackState packState, bool forceShowAll = false, ILookup entityLookup = null) { if (categories == null || packState == null) return (null, 0); var subCategories = categories.Where(cat => cat.LoadedFromPack && cat.DisplayName != "" && !cat.IsHidden); @@ -67,7 +67,7 @@ public static (IEnumerable, int skipped) FilterCategories(this lastIsSeparator = true; } //Check if the category has any loaded/visible children recursively - else if (subCategory.HasVisibleChildren(packState, true)) + else if (subCategory.HasVisibleChildren(packState, true, entityLookup)) { // If category has visible children, we include it filteredSubCategories.Add(subCategory); @@ -86,7 +86,7 @@ public static (IEnumerable, int skipped) FilterCategories(this return (Enumerable.Reverse(filteredSubCategories), skipped); } - public static bool HasVisibleChildren(this PathingCategory category, IPackState packState, bool recursively = false) { + public static bool HasVisibleChildren(this PathingCategory category, IPackState packState, bool recursively = false, ILookup entityLookup = null) { if (packState == null || string.IsNullOrWhiteSpace(category.DisplayName) || @@ -94,16 +94,18 @@ public static bool HasVisibleChildren(this PathingCategory category, IPackState var mapId = GameService.Gw2Mumble.CurrentMap.Id; - var hasVisibleEntities = packState.Entities - .ToArray() - .Any(e => e.MapId == mapId && e.Category == category); + var hasVisibleEntities = entityLookup != null + ? entityLookup[category].Any(e => e.MapId == mapId) + : packState.Entities + .ToArray() + .Any(e => e.MapId == mapId && e.Category == category); if (hasVisibleEntities) return true; if (recursively) { foreach (var childCategory in category) { - if (childCategory.HasVisibleChildren(packState, true)) + if (childCategory.HasVisibleChildren(packState, true, entityLookup)) { return true; }