diff --git a/AetherBags/Addons/AddonCategoryConfigurationWindow.cs b/AetherBags/Addons/AddonCategoryConfigurationWindow.cs index b3cf4d8..f40ea80 100644 --- a/AetherBags/Addons/AddonCategoryConfigurationWindow.cs +++ b/AetherBags/Addons/AddonCategoryConfigurationWindow.cs @@ -119,6 +119,7 @@ public class AddonCategoryConfigurationWindow : NativeAddon listNode.AddOption(newWrapper); RefreshSelectionList(); + System.AddonInventoryWindow.ManualInventoryRefresh(); } private void OnRemoveCategory(CategoryWrapper categoryWrapper) @@ -134,6 +135,7 @@ public class AddonCategoryConfigurationWindow : NativeAddon { OnOptionChanged(null); } + System.AddonInventoryWindow.ManualInventoryRefresh(); } private void RefreshSelectionList() diff --git a/AetherBags/Addons/CategoryWrapper.cs b/AetherBags/Addons/CategoryWrapper.cs index 68b7f85..9ccf226 100644 --- a/AetherBags/Addons/CategoryWrapper.cs +++ b/AetherBags/Addons/CategoryWrapper.cs @@ -1,5 +1,8 @@ using AetherBags.Configuration; +using AetherBags.Inventory; +using Dalamud.Game.Text.SeStringHandling; using KamiToolKit.Premade; +using SeStringBuilder = Lumina.Text.SeStringBuilder; namespace AetherBags.Addons; @@ -13,7 +16,8 @@ public class CategoryWrapper(UserCategoryDefinition categoryDefinition) : IInfoN } public string GetSubLabel() { - return CategoryDefinition!.Enabled ? "Enabled" : "Disabled"; + if(UserCategoryMatcher.IsCatchAll(CategoryDefinition!)) return " No valid rules!"; + return CategoryDefinition!.Enabled ? "✓ Enabled" : " Disabled"; } public uint? GetId() => null; diff --git a/AetherBags/Inventory/CategoryBucketManager.cs b/AetherBags/Inventory/CategoryBucketManager.cs index 6d67130..baf8a7f 100644 --- a/AetherBags/Inventory/CategoryBucketManager.cs +++ b/AetherBags/Inventory/CategoryBucketManager.cs @@ -54,6 +54,13 @@ public static class CategoryBucketManager for (int i = 0; i < sortedScratch.Count; i++) { UserCategoryDefinition category = sortedScratch[i]; + + if (!category.Enabled) + continue; + + if (UserCategoryMatcher.IsCatchAll(category)) + continue; + uint bucketKey = MakeUserCategoryKey(category.Order); if (!bucketsByKey.TryGetValue(bucketKey, out CategoryBucket? bucket)) diff --git a/AetherBags/Inventory/UserCategoryMatcher.cs b/AetherBags/Inventory/UserCategoryMatcher.cs index 3caf34a..7f84052 100644 --- a/AetherBags/Inventory/UserCategoryMatcher.cs +++ b/AetherBags/Inventory/UserCategoryMatcher.cs @@ -11,6 +11,44 @@ internal static class UserCategoryMatcher { var rules = userCategory.Rules; + bool hasIdentificationFilters = rules.AllowedItemIds.Count > 0 || rules.AllowedItemNamePatterns.Count > 0; + + if (hasIdentificationFilters) + { + bool matchesAnyIdentification = false; + + if (rules.AllowedItemIds.Count > 0 && rules.AllowedItemIds.Contains(item.Item.ItemId)) + { + matchesAnyIdentification = true; + } + + if (!matchesAnyIdentification && rules.AllowedItemNamePatterns.Count > 0) + { + for (int i = 0; i < rules.AllowedItemNamePatterns.Count; i++) + { + string pattern = rules.AllowedItemNamePatterns[i]; + if (string.IsNullOrWhiteSpace(pattern)) + continue; + + try + { + if (Regex.IsMatch(item.Name, pattern, RegexOptions.CultureInvariant | RegexOptions.IgnoreCase)) + { + matchesAnyIdentification = true; + break; + } + } + catch + { + // Invalid regex: ignore it. + } + } + } + + if (!matchesAnyIdentification) + return false; + } + if (rules.AllowedUiCategoryIds.Count > 0) { uint uiCategoryId = item.UiCategory.RowId; @@ -18,9 +56,6 @@ internal static class UserCategoryMatcher return false; } - if (rules.AllowedItemIds.Count > 0 && !rules.AllowedItemIds.Contains(item.Item.ItemId)) - return false; - if (rules.AllowedRarities.Count > 0 && !rules.AllowedRarities.Contains(item.Rarity)) return false; @@ -39,40 +74,46 @@ internal static class UserCategoryMatcher if (!MatchesToggle(rules.Dyeable, item.IsDyeable)) return false; if (!MatchesToggle(rules.Repairable, item.IsRepairable)) return false; - if (rules.AllowedItemNamePatterns.Count > 0) - { - bool any = false; - for (int i = 0; i < rules.AllowedItemNamePatterns.Count; i++) - { - string pattern = rules.AllowedItemNamePatterns[i]; - if (string.IsNullOrWhiteSpace(pattern)) - continue; - - // Treat patterns as regex for now. - try - { - if (Regex.IsMatch(item.Name, pattern, RegexOptions.CultureInvariant | RegexOptions.IgnoreCase)) - { - any = true; - break; - } - } - catch - { - // Invalid regex: ignore it. - } - } - - if (!any) - return false; - } - return true; } private static bool InRange(T value, T min, T max) where T : struct, IComparable => value.CompareTo(min) >= 0 && value.CompareTo(max) <= 0; + public static bool IsCatchAll(UserCategoryDefinition userCategory) + { + var rules = userCategory.Rules; + + if (rules.AllowedItemIds.Count > 0) + return false; + if (rules.AllowedItemNamePatterns.Count > 0) + return false; + if (rules.AllowedUiCategoryIds.Count > 0) + return false; + if (rules.AllowedRarities.Count > 0) + return false; + + if (rules.Level.Enabled) + return false; + if (rules.ItemLevel.Enabled) + return false; + if (rules.VendorPrice.Enabled) + return false; + + if (rules.Untradable.ToggleState != ToggleFilterState.Ignored) + return false; + if (rules.Unique.ToggleState != ToggleFilterState.Ignored) + return false; + if (rules.Collectable.ToggleState != ToggleFilterState.Ignored) + return false; + if (rules.Dyeable.ToggleState != ToggleFilterState.Ignored) + return false; + if (rules.Repairable.ToggleState != ToggleFilterState.Ignored) + return false; + + return true; + } + private static bool MatchesToggle(StateFilter filter, bool itemHasProperty) => filter.ToggleState switch { diff --git a/AetherBags/Nodes/Configuration/Category/CategoryDefinitionConfigurationNode.cs b/AetherBags/Nodes/Configuration/Category/CategoryDefinitionConfigurationNode.cs index 97d8255..968f8f9 100644 --- a/AetherBags/Nodes/Configuration/Category/CategoryDefinitionConfigurationNode.cs +++ b/AetherBags/Nodes/Configuration/Category/CategoryDefinitionConfigurationNode.cs @@ -1,6 +1,7 @@ using System; using System.Numerics; using AetherBags.Configuration; +using AetherBags.Inventory; using AetherBags.Nodes.Color; using Dalamud.Utility; using FFXIVClientStructs.FFXIV.Component.GUI; @@ -8,6 +9,7 @@ using KamiToolKit.Classes; using KamiToolKit.Nodes; using Lumina.Excel; using Lumina.Excel.Sheets; +using Lumina.Text; using Action = System.Action; namespace AetherBags.Nodes.Configuration.Category; @@ -65,6 +67,17 @@ public sealed class CategoryDefinitionConfigurationNode : VerticalListNode FitContents = true; ItemSpacing = 4.0f; + var catchAllWarningNode = new TextNode + { + Size = new Vector2(300, 40), + TextFlags = TextFlags.MultiLine | TextFlags.AutoAdjustNodeSize, + SeString = new SeStringBuilder().Append(" Warning: No rules configured\nThis category won't match anything!").ToReadOnlySeString(), + TextColor = ColorHelper.GetColor(17), + LineSpacing = 20, + IsVisible = UserCategoryMatcher.IsCatchAll(CategoryDefinition), + }; + AddNode(catchAllWarningNode); + AddNode(CreateSectionHeader("Basic Settings")); _enabledCheckbox = new CheckboxNode @@ -297,7 +310,7 @@ public sealed class CategoryDefinitionConfigurationNode : VerticalListNode private static void NotifyChanged() { - System.AddonInventoryWindow?.ManualInventoryRefresh(); + System.AddonInventoryWindow.ManualInventoryRefresh(); } private void NotifyCategoryPropertyChanged() diff --git a/AetherBags/Nodes/Inventory/InventoryNotificationNode.cs b/AetherBags/Nodes/Inventory/InventoryNotificationNode.cs index a42f6d0..02e578c 100644 --- a/AetherBags/Nodes/Inventory/InventoryNotificationNode.cs +++ b/AetherBags/Nodes/Inventory/InventoryNotificationNode.cs @@ -87,7 +87,7 @@ public sealed class InventoryNotificationNode : SimpleComponentNode Timeline?.PlayAnimation(101); } - } = new("sdsdsd", "sdsd"); + } // Future Zeff, this always goes on a parent private Timeline ParentLabels => new TimelineBuilder()