feat: adding categories for Guda Bags
This commit is contained in:
@@ -0,0 +1,686 @@
|
||||
-- Guda Category Manager
|
||||
-- Handles custom category definitions and rule-based item categorization
|
||||
|
||||
local addon = Guda
|
||||
|
||||
local CategoryManager = {}
|
||||
addon.Modules.CategoryManager = CategoryManager
|
||||
|
||||
-- Rule Types:
|
||||
-- itemType: Match by GetItemInfo type (Armor, Weapon, Consumable, etc.)
|
||||
-- itemSubtype: Match by subtype (Cloth, Potion, Herb, etc.)
|
||||
-- namePattern: Lua pattern match on item name
|
||||
-- quality: Match by quality level (0=Gray, 1=White, 2=Green, 3=Blue, 4=Purple, 5=Orange)
|
||||
-- isBoE: Boolean for Bind on Equip items
|
||||
-- isQuestItem: Boolean for quest items
|
||||
-- texturePattern: Match icon texture path
|
||||
-- itemID: Specific item IDs (table of IDs)
|
||||
|
||||
-- Default category definitions that replicate the existing hardcoded behavior
|
||||
local DEFAULT_CATEGORIES = {
|
||||
order = {
|
||||
"BoE", "Weapon", "Armor", "Consumable", "Food", "Drink",
|
||||
"Trade Goods", "Reagent", "Recipe", "Quiver", "Container",
|
||||
"Soul Bag", "Miscellaneous", "Quest", "Junk", "Class Items", "Keyring"
|
||||
},
|
||||
definitions = {
|
||||
["BoE"] = {
|
||||
name = "BoE",
|
||||
icon = "Interface\\Icons\\INV_Misc_Orb_01",
|
||||
rules = {
|
||||
{ type = "isBoE", value = true }
|
||||
},
|
||||
matchMode = "all",
|
||||
priority = 60,
|
||||
enabled = true,
|
||||
isBuiltIn = true,
|
||||
},
|
||||
["Weapon"] = {
|
||||
name = "Weapon",
|
||||
icon = "Interface\\Icons\\INV_Sword_04",
|
||||
rules = {
|
||||
{ type = "itemType", value = "Weapon" }
|
||||
},
|
||||
matchMode = "all",
|
||||
priority = 70,
|
||||
enabled = true,
|
||||
isBuiltIn = true,
|
||||
},
|
||||
["Armor"] = {
|
||||
name = "Armor",
|
||||
icon = "Interface\\Icons\\INV_Chest_Chain",
|
||||
rules = {
|
||||
{ type = "itemType", value = "Armor" }
|
||||
},
|
||||
matchMode = "all",
|
||||
priority = 70,
|
||||
enabled = true,
|
||||
isBuiltIn = true,
|
||||
},
|
||||
["Consumable"] = {
|
||||
name = "Consumable",
|
||||
icon = "Interface\\Icons\\INV_Potion_54",
|
||||
rules = {
|
||||
{ type = "itemType", value = "Consumable" }
|
||||
},
|
||||
matchMode = "all",
|
||||
priority = 50,
|
||||
enabled = true,
|
||||
isBuiltIn = true,
|
||||
},
|
||||
["Food"] = {
|
||||
name = "Food",
|
||||
icon = "Interface\\Icons\\INV_Misc_Food_14",
|
||||
rules = {
|
||||
{ type = "itemType", value = "Consumable" },
|
||||
{ type = "restoreTag", value = "eat" }
|
||||
},
|
||||
matchMode = "all",
|
||||
priority = 55,
|
||||
enabled = true,
|
||||
isBuiltIn = true,
|
||||
},
|
||||
["Drink"] = {
|
||||
name = "Drink",
|
||||
icon = "Interface\\Icons\\INV_Drink_07",
|
||||
rules = {
|
||||
{ type = "itemType", value = "Consumable" },
|
||||
{ type = "restoreTag", value = "drink" }
|
||||
},
|
||||
matchMode = "all",
|
||||
priority = 55,
|
||||
enabled = true,
|
||||
isBuiltIn = true,
|
||||
},
|
||||
["Trade Goods"] = {
|
||||
name = "Trade Goods",
|
||||
icon = "Interface\\Icons\\INV_Fabric_Silk_02",
|
||||
rules = {
|
||||
{ type = "itemType", value = "Trade Goods" }
|
||||
},
|
||||
matchMode = "all",
|
||||
priority = 40,
|
||||
enabled = true,
|
||||
isBuiltIn = true,
|
||||
},
|
||||
["Reagent"] = {
|
||||
name = "Reagent",
|
||||
icon = "Interface\\Icons\\INV_Misc_Dust_02",
|
||||
rules = {
|
||||
{ type = "itemType", value = "Reagent" }
|
||||
},
|
||||
matchMode = "all",
|
||||
priority = 40,
|
||||
enabled = true,
|
||||
isBuiltIn = true,
|
||||
},
|
||||
["Recipe"] = {
|
||||
name = "Recipe",
|
||||
icon = "Interface\\Icons\\INV_Scroll_03",
|
||||
rules = {
|
||||
{ type = "itemType", value = "Recipe" }
|
||||
},
|
||||
matchMode = "all",
|
||||
priority = 40,
|
||||
enabled = true,
|
||||
isBuiltIn = true,
|
||||
},
|
||||
["Quiver"] = {
|
||||
name = "Quiver",
|
||||
icon = "Interface\\Icons\\INV_Misc_Quiver_03",
|
||||
rules = {
|
||||
{ type = "itemType", value = "Quiver" }
|
||||
},
|
||||
matchMode = "all",
|
||||
priority = 40,
|
||||
enabled = true,
|
||||
isBuiltIn = true,
|
||||
},
|
||||
["Container"] = {
|
||||
name = "Container",
|
||||
icon = "Interface\\Icons\\INV_Misc_Bag_07",
|
||||
rules = {
|
||||
{ type = "itemType", value = "Container" }
|
||||
},
|
||||
matchMode = "all",
|
||||
priority = 40,
|
||||
enabled = true,
|
||||
isBuiltIn = true,
|
||||
},
|
||||
["Soul Bag"] = {
|
||||
name = "Soul Bag",
|
||||
icon = "Interface\\Icons\\INV_Misc_Bag_EnchantedMageweave",
|
||||
rules = {
|
||||
{ type = "itemSubtype", value = "Soul Bag" }
|
||||
},
|
||||
matchMode = "all",
|
||||
priority = 45,
|
||||
enabled = true,
|
||||
isBuiltIn = true,
|
||||
},
|
||||
["Miscellaneous"] = {
|
||||
name = "Miscellaneous",
|
||||
icon = "Interface\\Icons\\INV_Misc_Rune_01",
|
||||
rules = {},
|
||||
matchMode = "any",
|
||||
priority = 0,
|
||||
enabled = true,
|
||||
isBuiltIn = true,
|
||||
isFallback = true,
|
||||
},
|
||||
["Quest"] = {
|
||||
name = "Quest",
|
||||
icon = "Interface\\Icons\\INV_Misc_Book_08",
|
||||
rules = {
|
||||
{ type = "isQuestItem", value = true }
|
||||
},
|
||||
matchMode = "all",
|
||||
priority = 80,
|
||||
enabled = true,
|
||||
isBuiltIn = true,
|
||||
},
|
||||
["Junk"] = {
|
||||
name = "Junk",
|
||||
icon = "Interface\\Icons\\INV_Misc_Gear_06",
|
||||
rules = {
|
||||
{ type = "quality", value = 0 }
|
||||
},
|
||||
matchMode = "all",
|
||||
priority = 85,
|
||||
enabled = true,
|
||||
isBuiltIn = true,
|
||||
},
|
||||
["Class Items"] = {
|
||||
name = "Class Items",
|
||||
icon = "Interface\\Icons\\INV_Misc_Ammo_Arrow_01",
|
||||
rules = {
|
||||
{ type = "itemType", value = "Projectile" }
|
||||
},
|
||||
matchMode = "any",
|
||||
priority = 90,
|
||||
enabled = true,
|
||||
isBuiltIn = true,
|
||||
},
|
||||
["Keyring"] = {
|
||||
name = "Keyring",
|
||||
icon = "Interface\\Icons\\INV_Misc_Key_04",
|
||||
rules = {
|
||||
{ type = "itemType", value = "Key" }
|
||||
},
|
||||
matchMode = "all",
|
||||
priority = 40,
|
||||
enabled = true,
|
||||
isBuiltIn = true,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
-- Deep copy a table
|
||||
local function deepCopy(orig)
|
||||
local copy
|
||||
if type(orig) == "table" then
|
||||
copy = {}
|
||||
for k, v in pairs(orig) do
|
||||
copy[k] = deepCopy(v)
|
||||
end
|
||||
else
|
||||
copy = orig
|
||||
end
|
||||
return copy
|
||||
end
|
||||
|
||||
-- Get default categories (returns a deep copy)
|
||||
function CategoryManager:GetDefaultCategories()
|
||||
return deepCopy(DEFAULT_CATEGORIES)
|
||||
end
|
||||
|
||||
-- Initialize categories from database or defaults
|
||||
function CategoryManager:Initialize()
|
||||
if not Guda_CharDB then return end
|
||||
|
||||
if not Guda_CharDB.categories then
|
||||
Guda_CharDB.categories = self:GetDefaultCategories()
|
||||
addon:Debug("CategoryManager: Initialized with default categories")
|
||||
else
|
||||
-- Ensure all built-in categories exist (migration support)
|
||||
self:MigrateCategories()
|
||||
end
|
||||
end
|
||||
|
||||
-- Migrate/update categories to ensure all built-ins exist
|
||||
function CategoryManager:MigrateCategories()
|
||||
local cats = Guda_CharDB.categories
|
||||
if not cats then return end
|
||||
|
||||
-- Ensure definitions table exists
|
||||
if not cats.definitions then
|
||||
cats.definitions = {}
|
||||
end
|
||||
|
||||
-- Ensure order table exists
|
||||
if not cats.order then
|
||||
cats.order = {}
|
||||
end
|
||||
|
||||
-- Add any missing built-in categories
|
||||
for id, def in pairs(DEFAULT_CATEGORIES.definitions) do
|
||||
if not cats.definitions[id] then
|
||||
cats.definitions[id] = deepCopy(def)
|
||||
-- Add to end of order if not present
|
||||
local found = false
|
||||
for _, orderId in ipairs(cats.order) do
|
||||
if orderId == id then
|
||||
found = true
|
||||
break
|
||||
end
|
||||
end
|
||||
if not found then
|
||||
table.insert(cats.order, id)
|
||||
end
|
||||
addon:Debug("CategoryManager: Added missing built-in category: " .. id)
|
||||
end
|
||||
end
|
||||
|
||||
-- Migrate Food/Drink categories to use restoreTag instead of itemSubtype
|
||||
-- This fixes the issue where subtype "Food & Drink" matched both categories
|
||||
local foodCat = cats.definitions["Food"]
|
||||
if foodCat and foodCat.isBuiltIn then
|
||||
local needsUpdate = false
|
||||
if foodCat.rules then
|
||||
for _, rule in ipairs(foodCat.rules) do
|
||||
if rule.type == "itemSubtype" then
|
||||
needsUpdate = true
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
if needsUpdate then
|
||||
foodCat.rules = {
|
||||
{ type = "itemType", value = "Consumable" },
|
||||
{ type = "restoreTag", value = "eat" }
|
||||
}
|
||||
addon:Debug("CategoryManager: Migrated Food category to use restoreTag")
|
||||
end
|
||||
end
|
||||
|
||||
local drinkCat = cats.definitions["Drink"]
|
||||
if drinkCat and drinkCat.isBuiltIn then
|
||||
local needsUpdate = false
|
||||
if drinkCat.rules then
|
||||
for _, rule in ipairs(drinkCat.rules) do
|
||||
if rule.type == "itemSubtype" then
|
||||
needsUpdate = true
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
if needsUpdate then
|
||||
drinkCat.rules = {
|
||||
{ type = "itemType", value = "Consumable" },
|
||||
{ type = "restoreTag", value = "drink" }
|
||||
}
|
||||
addon:Debug("CategoryManager: Migrated Drink category to use restoreTag")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Get all categories
|
||||
function CategoryManager:GetCategories()
|
||||
if not Guda_CharDB or not Guda_CharDB.categories then
|
||||
return self:GetDefaultCategories()
|
||||
end
|
||||
return Guda_CharDB.categories
|
||||
end
|
||||
|
||||
-- Get category order
|
||||
function CategoryManager:GetCategoryOrder()
|
||||
local cats = self:GetCategories()
|
||||
return cats.order or {}
|
||||
end
|
||||
|
||||
-- Get category definition by ID
|
||||
function CategoryManager:GetCategory(categoryId)
|
||||
local cats = self:GetCategories()
|
||||
if cats.definitions then
|
||||
return cats.definitions[categoryId]
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Save categories to database
|
||||
function CategoryManager:SaveCategories(categories)
|
||||
if not Guda_CharDB then return end
|
||||
Guda_CharDB.categories = categories
|
||||
end
|
||||
|
||||
-- Add a new custom category
|
||||
function CategoryManager:AddCategory(categoryId, definition)
|
||||
local cats = self:GetCategories()
|
||||
|
||||
if cats.definitions[categoryId] then
|
||||
addon:Debug("CategoryManager: Category already exists: " .. categoryId)
|
||||
return false
|
||||
end
|
||||
|
||||
definition.isBuiltIn = false
|
||||
cats.definitions[categoryId] = definition
|
||||
table.insert(cats.order, categoryId)
|
||||
|
||||
self:SaveCategories(cats)
|
||||
return true
|
||||
end
|
||||
|
||||
-- Update an existing category
|
||||
function CategoryManager:UpdateCategory(categoryId, definition)
|
||||
local cats = self:GetCategories()
|
||||
|
||||
if not cats.definitions[categoryId] then
|
||||
addon:Debug("CategoryManager: Category not found: " .. categoryId)
|
||||
return false
|
||||
end
|
||||
|
||||
-- Preserve isBuiltIn flag
|
||||
definition.isBuiltIn = cats.definitions[categoryId].isBuiltIn
|
||||
cats.definitions[categoryId] = definition
|
||||
|
||||
self:SaveCategories(cats)
|
||||
return true
|
||||
end
|
||||
|
||||
-- Delete a category (only custom categories can be deleted)
|
||||
function CategoryManager:DeleteCategory(categoryId)
|
||||
local cats = self:GetCategories()
|
||||
local def = cats.definitions[categoryId]
|
||||
|
||||
if not def then
|
||||
return false
|
||||
end
|
||||
|
||||
if def.isBuiltIn then
|
||||
addon:Debug("CategoryManager: Cannot delete built-in category: " .. categoryId)
|
||||
return false
|
||||
end
|
||||
|
||||
cats.definitions[categoryId] = nil
|
||||
|
||||
-- Remove from order
|
||||
for i, id in ipairs(cats.order) do
|
||||
if id == categoryId then
|
||||
table.remove(cats.order, i)
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
self:SaveCategories(cats)
|
||||
return true
|
||||
end
|
||||
|
||||
-- Move category up in order
|
||||
function CategoryManager:MoveCategoryUp(categoryId)
|
||||
local cats = self:GetCategories()
|
||||
|
||||
for i, id in ipairs(cats.order) do
|
||||
if id == categoryId and i > 1 then
|
||||
cats.order[i] = cats.order[i - 1]
|
||||
cats.order[i - 1] = categoryId
|
||||
self:SaveCategories(cats)
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- Move category down in order
|
||||
function CategoryManager:MoveCategoryDown(categoryId)
|
||||
local cats = self:GetCategories()
|
||||
local count = table.getn(cats.order)
|
||||
|
||||
for i, id in ipairs(cats.order) do
|
||||
if id == categoryId and i < count then
|
||||
cats.order[i] = cats.order[i + 1]
|
||||
cats.order[i + 1] = categoryId
|
||||
self:SaveCategories(cats)
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- Toggle category enabled state
|
||||
function CategoryManager:ToggleCategory(categoryId)
|
||||
local cats = self:GetCategories()
|
||||
local def = cats.definitions[categoryId]
|
||||
|
||||
if def then
|
||||
def.enabled = not def.enabled
|
||||
self:SaveCategories(cats)
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- Reset all categories to defaults
|
||||
function CategoryManager:ResetToDefaults()
|
||||
Guda_CharDB.categories = self:GetDefaultCategories()
|
||||
addon:Debug("CategoryManager: Reset to default categories")
|
||||
end
|
||||
|
||||
-------------------------------------------
|
||||
-- Rule Evaluation Engine
|
||||
-------------------------------------------
|
||||
|
||||
-- Evaluate a single rule against item data
|
||||
function CategoryManager:EvaluateRule(rule, itemData, bagID, slotID, isOtherChar)
|
||||
local ruleType = rule.type
|
||||
local ruleValue = rule.value
|
||||
|
||||
if ruleType == "itemType" then
|
||||
return (itemData.class == ruleValue) or (itemData.type == ruleValue)
|
||||
|
||||
elseif ruleType == "itemSubtype" then
|
||||
local subclass = itemData.subclass or ""
|
||||
-- Check for partial match (e.g., "Food" matches "Food & Drink")
|
||||
if string.find(subclass, ruleValue) then
|
||||
return true
|
||||
end
|
||||
return subclass == ruleValue
|
||||
|
||||
elseif ruleType == "namePattern" then
|
||||
local itemName = itemData.name or ""
|
||||
return string.find(itemName, ruleValue) ~= nil
|
||||
|
||||
elseif ruleType == "quality" then
|
||||
return itemData.quality == ruleValue
|
||||
|
||||
elseif ruleType == "isBoE" then
|
||||
if isOtherChar then return false end
|
||||
if itemData.class ~= "Weapon" and itemData.class ~= "Armor" then
|
||||
return false
|
||||
end
|
||||
local isBoE = addon.Modules.Utils:IsBindOnEquip(bagID, slotID, itemData.link)
|
||||
return isBoE == ruleValue
|
||||
|
||||
elseif ruleType == "isQuestItem" then
|
||||
local isQuestItem = false
|
||||
local itemCategory = itemData.class or itemData.category or ""
|
||||
local itemType = itemData.type or ""
|
||||
local isEquipment = (itemCategory == "Weapon" or itemCategory == "Armor")
|
||||
local isQuestCategory = (itemCategory == "Quest" or itemType == "Quest")
|
||||
|
||||
if isQuestCategory then
|
||||
isQuestItem = true
|
||||
elseif not isOtherChar then
|
||||
isQuestItem = addon.Modules.Utils:IsQuestItemTooltip(bagID, slotID)
|
||||
if isQuestItem and isEquipment and not isQuestCategory then
|
||||
isQuestItem = false
|
||||
end
|
||||
end
|
||||
|
||||
-- Also check QuestItemsDB
|
||||
if not isQuestItem and itemData.link then
|
||||
local itemID = addon.Modules.Utils:ExtractItemID(itemData.link)
|
||||
if itemID and addon.IsQuestItemByID then
|
||||
local playerFaction = UnitFactionGroup("player")
|
||||
isQuestItem = addon:IsQuestItemByID(itemID, playerFaction)
|
||||
end
|
||||
end
|
||||
|
||||
return isQuestItem == ruleValue
|
||||
|
||||
elseif ruleType == "texturePattern" then
|
||||
local texture = itemData.texture or ""
|
||||
return string.find(string.lower(texture), string.lower(ruleValue)) ~= nil
|
||||
|
||||
elseif ruleType == "itemID" then
|
||||
if not itemData.link then return false end
|
||||
local itemID = addon.Modules.Utils:ExtractItemID(itemData.link)
|
||||
if not itemID then return false end
|
||||
|
||||
-- ruleValue can be a single ID or a table of IDs
|
||||
if type(ruleValue) == "table" then
|
||||
for _, id in ipairs(ruleValue) do
|
||||
if itemID == id then return true end
|
||||
end
|
||||
return false
|
||||
else
|
||||
return itemID == ruleValue
|
||||
end
|
||||
|
||||
elseif ruleType == "isSoulShard" then
|
||||
return addon.Modules.Utils:IsSoulShard(itemData.link) == ruleValue
|
||||
|
||||
elseif ruleType == "isProjectile" then
|
||||
local isProj = (itemData.class == "Projectile" or
|
||||
itemData.subclass == "Arrow" or
|
||||
itemData.subclass == "Bullet")
|
||||
return isProj == ruleValue
|
||||
|
||||
elseif ruleType == "restoreTag" then
|
||||
-- restoreTag is set by tooltip scanning: "eat", "drink", or "restore"
|
||||
local tag = itemData.restoreTag
|
||||
if not tag then return false end
|
||||
return tag == ruleValue
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
-- Evaluate all rules for a category
|
||||
function CategoryManager:EvaluateCategoryRules(categoryDef, itemData, bagID, slotID, isOtherChar)
|
||||
if not categoryDef.enabled then
|
||||
return false
|
||||
end
|
||||
|
||||
local rules = categoryDef.rules or {}
|
||||
|
||||
-- No rules = fallback category (matches everything)
|
||||
if table.getn(rules) == 0 then
|
||||
return categoryDef.isFallback == true
|
||||
end
|
||||
|
||||
local matchMode = categoryDef.matchMode or "any"
|
||||
|
||||
if matchMode == "all" then
|
||||
-- All rules must match
|
||||
for _, rule in ipairs(rules) do
|
||||
if not self:EvaluateRule(rule, itemData, bagID, slotID, isOtherChar) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
else
|
||||
-- Any rule must match
|
||||
for _, rule in ipairs(rules) do
|
||||
if self:EvaluateRule(rule, itemData, bagID, slotID, isOtherChar) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
-- Get sorted categories by priority (highest first)
|
||||
function CategoryManager:GetCategoriesByPriority()
|
||||
local cats = self:GetCategories()
|
||||
local sorted = {}
|
||||
|
||||
for id, def in pairs(cats.definitions) do
|
||||
if def.enabled then
|
||||
table.insert(sorted, { id = id, def = def })
|
||||
end
|
||||
end
|
||||
|
||||
table.sort(sorted, function(a, b)
|
||||
return (a.def.priority or 0) > (b.def.priority or 0)
|
||||
end)
|
||||
|
||||
return sorted
|
||||
end
|
||||
|
||||
-- Categorize an item using the rule engine
|
||||
-- Returns category ID or "Miscellaneous" as fallback
|
||||
function CategoryManager:CategorizeItem(itemData, bagID, slotID, isOtherChar)
|
||||
local sortedCats = self:GetCategoriesByPriority()
|
||||
|
||||
for _, entry in ipairs(sortedCats) do
|
||||
if not entry.def.isFallback then
|
||||
if self:EvaluateCategoryRules(entry.def, itemData, bagID, slotID, isOtherChar) then
|
||||
return entry.id
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return "Miscellaneous"
|
||||
end
|
||||
|
||||
-- Build the Guda_CategoryList from current category order (for compatibility)
|
||||
function CategoryManager:BuildCategoryList()
|
||||
local order = self:GetCategoryOrder()
|
||||
local list = {}
|
||||
|
||||
for _, id in ipairs(order) do
|
||||
local def = self:GetCategory(id)
|
||||
if def and def.enabled then
|
||||
table.insert(list, id)
|
||||
end
|
||||
end
|
||||
|
||||
return list
|
||||
end
|
||||
|
||||
-- Get available rule types for UI
|
||||
function CategoryManager:GetRuleTypes()
|
||||
return {
|
||||
{ id = "itemType", name = "Item Type", description = "Match by item class (Armor, Weapon, etc.)" },
|
||||
{ id = "itemSubtype", name = "Item Subtype", description = "Match by item subclass (Cloth, Potion, etc.)" },
|
||||
{ id = "namePattern", name = "Name Pattern", description = "Match item name (supports Lua patterns)" },
|
||||
{ id = "quality", name = "Quality", description = "Match by item quality (0=Gray to 5=Legendary)" },
|
||||
{ id = "isBoE", name = "Bind on Equip", description = "Match items that bind when equipped" },
|
||||
{ id = "isQuestItem", name = "Quest Item", description = "Match quest items" },
|
||||
{ id = "texturePattern", name = "Icon Pattern", description = "Match icon texture path" },
|
||||
{ id = "itemID", name = "Item ID", description = "Match specific item IDs" },
|
||||
{ id = "isSoulShard", name = "Soul Shard", description = "Match soul shards" },
|
||||
{ id = "isProjectile", name = "Projectile", description = "Match arrows and bullets" },
|
||||
{ id = "restoreTag", name = "Restore Type", description = "Match by consumable type (eat, drink, restore)" },
|
||||
}
|
||||
end
|
||||
|
||||
-- Get common item types for UI dropdowns
|
||||
function CategoryManager:GetItemTypes()
|
||||
return {
|
||||
"Armor", "Weapon", "Consumable", "Container", "Trade Goods",
|
||||
"Projectile", "Quiver", "Reagent", "Recipe", "Key", "Miscellaneous", "Quest"
|
||||
}
|
||||
end
|
||||
|
||||
-- Get quality names for UI
|
||||
function CategoryManager:GetQualityNames()
|
||||
return {
|
||||
[0] = "Poor (Gray)",
|
||||
[1] = "Common (White)",
|
||||
[2] = "Uncommon (Green)",
|
||||
[3] = "Rare (Blue)",
|
||||
[4] = "Epic (Purple)",
|
||||
[5] = "Legendary (Orange)",
|
||||
}
|
||||
end
|
||||
@@ -122,6 +122,11 @@ function DB:Initialize()
|
||||
Guda_CharDB.settings.markUnusableItems = true
|
||||
end
|
||||
|
||||
-- Initialize CategoryManager for custom categories
|
||||
if addon.Modules.CategoryManager then
|
||||
addon.Modules.CategoryManager:Initialize()
|
||||
end
|
||||
|
||||
-- Initialize this character's data
|
||||
if not Guda_DB.characters[fullName] then
|
||||
local localizedClass, englishClass = UnitClass("player")
|
||||
|
||||
@@ -13,6 +13,7 @@ Core\Database.lua
|
||||
DB\QuestItems.lua
|
||||
Core\Events.lua
|
||||
Core\Utils.lua
|
||||
Core\CategoryManager.lua
|
||||
Core\Tooltip.lua
|
||||
|
||||
Data\BagScanner.lua
|
||||
|
||||
+51
-12
@@ -5,10 +5,18 @@ local FrameHelpers = {}
|
||||
addon.Modules.FrameHelpers = FrameHelpers
|
||||
|
||||
-- Standard category list used by both Bag and Bank frames
|
||||
-- Now dynamically built from CategoryManager if available
|
||||
Guda_CategoryList = {
|
||||
"BoE", "Weapon", "Armor", "Consumable", "Food", "Drink", "Trade Goods", "Reagent", "Recipe", "Quiver", "Container", "Soul Bag", "Miscellaneous", "Quest", "Junk", "Class Items", "Keyring"
|
||||
}
|
||||
|
||||
-- Rebuild category list from CategoryManager
|
||||
function Guda_RefreshCategoryList()
|
||||
if addon.Modules.CategoryManager then
|
||||
Guda_CategoryList = addon.Modules.CategoryManager:BuildCategoryList()
|
||||
end
|
||||
end
|
||||
|
||||
-- Categorize a single item into categories and specialItems tables
|
||||
-- Returns nothing, modifies tables in place
|
||||
function Guda_CategorizeItem(itemData, bagID, slotID, categories, specialItems, isOtherChar)
|
||||
@@ -25,6 +33,7 @@ function Guda_CategorizeItem(itemData, bagID, slotID, categories, specialItems,
|
||||
end
|
||||
|
||||
-- Priority 1: Special items (Hearthstone, Mounts, Tools)
|
||||
-- These are handled separately and go into specialItems table, not categories
|
||||
if string.find(itemName, "Hearthstone") then
|
||||
table.insert(specialItems.Hearthstone, {bagID = bagID, slotID = slotID, itemData = itemData})
|
||||
return
|
||||
@@ -44,22 +53,28 @@ function Guda_CategorizeItem(itemData, bagID, slotID, categories, specialItems,
|
||||
return
|
||||
end
|
||||
|
||||
-- Use CategoryManager rule engine if available, otherwise fall back to legacy logic
|
||||
if addon.Modules.CategoryManager then
|
||||
cat = addon.Modules.CategoryManager:CategorizeItem(itemData, bagID, slotID, isOtherChar)
|
||||
if not categories[cat] then cat = "Miscellaneous" end
|
||||
table.insert(categories[cat], {bagID = bagID, slotID = slotID, itemData = itemData})
|
||||
return
|
||||
end
|
||||
|
||||
-- Legacy categorization logic (fallback if CategoryManager not available)
|
||||
-- Priority 2: Class Items (Soul Shards, Arrows, Bullets)
|
||||
if addon.Modules.Utils:IsSoulShard(itemData.link) or
|
||||
itemData.class == "Projectile" or
|
||||
itemData.subclass == "Arrow" or
|
||||
if addon.Modules.Utils:IsSoulShard(itemData.link) or
|
||||
itemData.class == "Projectile" or
|
||||
itemData.subclass == "Arrow" or
|
||||
itemData.subclass == "Bullet" then
|
||||
table.insert(categories["Class Items"], {bagID = bagID, slotID = slotID, itemData = itemData})
|
||||
return
|
||||
end
|
||||
|
||||
-- Priority 3: Quest Items
|
||||
-- Check multiple sources: tooltip scan, itemClass, itemType, and QuestItemsDB
|
||||
local isQuestItem = false
|
||||
local itemCategory = itemData.class or itemData.category or ""
|
||||
local itemType = itemData.type or ""
|
||||
|
||||
-- If it's a Weapon or Armor, it shouldn't be a QuestItem unless it's specifically categorized as Quest
|
||||
local isEquipment = (itemCategory == "Weapon" or itemCategory == "Armor" or itemType == "Weapon" or itemType == "Armor")
|
||||
local isQuestCategory = (itemCategory == "Quest" or itemType == "Quest")
|
||||
|
||||
@@ -67,12 +82,10 @@ function Guda_CategorizeItem(itemData, bagID, slotID, categories, specialItems,
|
||||
isQuestItem = true
|
||||
elseif not isOtherChar then
|
||||
isQuestItem = addon.Modules.Utils:IsQuestItemTooltip(bagID, slotID)
|
||||
-- If tooltip said it is quest, but it is equipment and not quest category, reject it
|
||||
if isQuestItem and isEquipment and not isQuestCategory then
|
||||
isQuestItem = false
|
||||
end
|
||||
end
|
||||
-- Also check the QuestItemsDB for known faction-specific quest items
|
||||
if not isQuestItem and itemData.link then
|
||||
local itemID = addon.Modules.Utils:ExtractItemID(itemData.link)
|
||||
if itemID and addon.IsQuestItemByID then
|
||||
@@ -106,7 +119,7 @@ function Guda_CategorizeItem(itemData, bagID, slotID, categories, specialItems,
|
||||
return
|
||||
end
|
||||
|
||||
-- Priority 6: BoE Equipment (Armor/Weapons that bind when equipped)
|
||||
-- Priority 6: BoE Equipment
|
||||
if (itemData.class == "Weapon" or itemData.class == "Armor") and not isOtherChar then
|
||||
local isBoE = addon.Modules.Utils:IsBindOnEquip(bagID, slotID, itemData.link)
|
||||
if isBoE then
|
||||
@@ -117,7 +130,7 @@ function Guda_CategorizeItem(itemData, bagID, slotID, categories, specialItems,
|
||||
return
|
||||
end
|
||||
|
||||
-- Priority 7: Equipment for other characters (can't scan tooltip)
|
||||
-- Priority 7: Equipment for other characters
|
||||
if (itemData.class == "Weapon" or itemData.class == "Armor") and isOtherChar then
|
||||
table.insert(categories[itemData.class], {bagID = bagID, slotID = slotID, itemData = itemData})
|
||||
return
|
||||
@@ -131,10 +144,36 @@ end
|
||||
|
||||
-- Initialize empty category tables
|
||||
function Guda_InitCategories()
|
||||
-- Refresh category list from CategoryManager (enabled categories only for display)
|
||||
Guda_RefreshCategoryList()
|
||||
|
||||
-- Create tables for ALL categories (not just enabled) so items can be categorized
|
||||
-- Items in disabled categories simply won't be displayed
|
||||
local categories = {}
|
||||
for _, cat in ipairs(Guda_CategoryList) do
|
||||
categories[cat] = {}
|
||||
|
||||
if addon.Modules.CategoryManager then
|
||||
-- Get full category order (all categories, not just enabled)
|
||||
local allCategories = addon.Modules.CategoryManager:GetCategoryOrder()
|
||||
for _, cat in ipairs(allCategories) do
|
||||
categories[cat] = {}
|
||||
end
|
||||
else
|
||||
-- Fallback: use the display list
|
||||
for _, cat in ipairs(Guda_CategoryList) do
|
||||
categories[cat] = {}
|
||||
end
|
||||
end
|
||||
|
||||
-- Always ensure Miscellaneous exists as fallback
|
||||
if not categories["Miscellaneous"] then
|
||||
categories["Miscellaneous"] = {}
|
||||
end
|
||||
|
||||
-- Always ensure Keyring exists (handled specially in BagFrame)
|
||||
if not categories["Keyring"] then
|
||||
categories["Keyring"] = {}
|
||||
end
|
||||
|
||||
local specialItems = {
|
||||
Hearthstone = {},
|
||||
Mount = {},
|
||||
|
||||
@@ -52,19 +52,23 @@ function Guda_SettingsPopup_SelectTab(tabName)
|
||||
-- Hide all tab content frames
|
||||
local generalTab = getglobal("Guda_SettingsPopup_GeneralTab")
|
||||
local iconsTab = getglobal("Guda_SettingsPopup_IconsTab")
|
||||
local categoriesTab = getglobal("Guda_SettingsPopup_CategoriesTab")
|
||||
local guideTab = getglobal("Guda_SettingsPopup_GuideTab")
|
||||
|
||||
if generalTab then generalTab:Hide() end
|
||||
if iconsTab then iconsTab:Hide() end
|
||||
if categoriesTab then categoriesTab:Hide() end
|
||||
if guideTab then guideTab:Hide() end
|
||||
|
||||
-- Reset all tab button backgrounds to inactive (0.1 alpha)
|
||||
local generalBg = getglobal("Guda_SettingsPopup_GeneralTabButton_Bg")
|
||||
local iconsBg = getglobal("Guda_SettingsPopup_IconsTabButton_Bg")
|
||||
local categoriesBg = getglobal("Guda_SettingsPopup_CategoriesTabButton_Bg")
|
||||
local guideBg = getglobal("Guda_SettingsPopup_GuideTabButton_Bg")
|
||||
|
||||
if generalBg then generalBg:SetTexture(1, 1, 1, 0.1) end
|
||||
if iconsBg then iconsBg:SetTexture(1, 1, 1, 0.1) end
|
||||
if categoriesBg then categoriesBg:SetTexture(1, 1, 1, 0.1) end
|
||||
if guideBg then guideBg:SetTexture(1, 1, 1, 0.1) end
|
||||
|
||||
-- Show selected tab and highlight its button
|
||||
@@ -74,6 +78,10 @@ function Guda_SettingsPopup_SelectTab(tabName)
|
||||
elseif tabName == "icons" then
|
||||
if iconsTab then iconsTab:Show() end
|
||||
if iconsBg then iconsBg:SetTexture(1, 1, 1, 0.3) end
|
||||
elseif tabName == "categories" then
|
||||
if categoriesTab then categoriesTab:Show() end
|
||||
if categoriesBg then categoriesBg:SetTexture(1, 1, 1, 0.3) end
|
||||
Guda_SettingsPopup_CategoriesTab_Update()
|
||||
elseif tabName == "guide" then
|
||||
if guideTab then guideTab:Show() end
|
||||
if guideBg then guideBg:SetTexture(1, 1, 1, 0.3) end
|
||||
@@ -1160,6 +1168,234 @@ function Guda_SettingsPopup_ReverseStackSortCheckbox_OnClick(self)
|
||||
-- No immediate UI update needed
|
||||
end
|
||||
|
||||
-------------------------------------------
|
||||
-- Categories Tab Functions
|
||||
-------------------------------------------
|
||||
|
||||
-- Number of visible rows in the category list
|
||||
local CATEGORY_ROW_HEIGHT = 22
|
||||
local CATEGORY_VISIBLE_ROWS = 14
|
||||
local categoryRowFrames = {}
|
||||
|
||||
-- Create or get a category row frame
|
||||
local function GetCategoryRowFrame(index)
|
||||
if categoryRowFrames[index] then
|
||||
return categoryRowFrames[index]
|
||||
end
|
||||
|
||||
local container = getglobal("Guda_SettingsPopup_CategoryListContainer")
|
||||
if not container then return nil end
|
||||
|
||||
local rowName = "Guda_SettingsPopup_CategoryRow" .. index
|
||||
local row = CreateFrame("Frame", rowName, container)
|
||||
row:SetHeight(CATEGORY_ROW_HEIGHT)
|
||||
row:SetWidth(420)
|
||||
row:SetPoint("TOPLEFT", container, "TOPLEFT", 0, -((index - 1) * CATEGORY_ROW_HEIGHT))
|
||||
|
||||
-- Background highlight
|
||||
local bg = row:CreateTexture(nil, "BACKGROUND")
|
||||
bg:SetAllPoints(row)
|
||||
bg:SetTexture(1, 1, 1, 0)
|
||||
row.bg = bg
|
||||
|
||||
-- Enable checkbox
|
||||
local checkbox = CreateFrame("CheckButton", rowName .. "_Checkbox", row, "UICheckButtonTemplate")
|
||||
checkbox:SetWidth(20)
|
||||
checkbox:SetHeight(20)
|
||||
checkbox:SetPoint("LEFT", row, "LEFT", 0, 0)
|
||||
checkbox:SetScript("OnClick", function()
|
||||
local catId = this:GetParent().categoryId
|
||||
if catId and Guda.Modules.CategoryManager then
|
||||
Guda.Modules.CategoryManager:ToggleCategory(catId)
|
||||
Guda_SettingsPopup_CategoriesTab_Update()
|
||||
Guda_SettingsPopup_RefreshBagFrames()
|
||||
end
|
||||
end)
|
||||
row.checkbox = checkbox
|
||||
|
||||
-- Category name
|
||||
local nameText = row:CreateFontString(nil, "OVERLAY", "GameFontNormal")
|
||||
nameText:SetPoint("LEFT", checkbox, "RIGHT", 5, 0)
|
||||
nameText:SetWidth(200)
|
||||
nameText:SetJustifyH("LEFT")
|
||||
row.nameText = nameText
|
||||
|
||||
-- Move Up button
|
||||
local upBtn = CreateFrame("Button", rowName .. "_UpBtn", row)
|
||||
upBtn:SetWidth(20)
|
||||
upBtn:SetHeight(20)
|
||||
upBtn:SetPoint("LEFT", nameText, "RIGHT", 10, 0)
|
||||
upBtn:SetNormalTexture("Interface\\Buttons\\UI-ScrollBar-ScrollUpButton-Up")
|
||||
upBtn:SetPushedTexture("Interface\\Buttons\\UI-ScrollBar-ScrollUpButton-Down")
|
||||
upBtn:SetHighlightTexture("Interface\\Buttons\\UI-ScrollBar-ScrollUpButton-Highlight")
|
||||
upBtn:SetScript("OnClick", function()
|
||||
local catId = this:GetParent().categoryId
|
||||
if catId and Guda.Modules.CategoryManager then
|
||||
Guda.Modules.CategoryManager:MoveCategoryUp(catId)
|
||||
Guda_SettingsPopup_CategoriesTab_Update()
|
||||
Guda_SettingsPopup_RefreshBagFrames()
|
||||
end
|
||||
end)
|
||||
row.upBtn = upBtn
|
||||
|
||||
-- Move Down button
|
||||
local downBtn = CreateFrame("Button", rowName .. "_DownBtn", row)
|
||||
downBtn:SetWidth(20)
|
||||
downBtn:SetHeight(20)
|
||||
downBtn:SetPoint("LEFT", upBtn, "RIGHT", 2, 0)
|
||||
downBtn:SetNormalTexture("Interface\\Buttons\\UI-ScrollBar-ScrollDownButton-Up")
|
||||
downBtn:SetPushedTexture("Interface\\Buttons\\UI-ScrollBar-ScrollDownButton-Down")
|
||||
downBtn:SetHighlightTexture("Interface\\Buttons\\UI-ScrollBar-ScrollDownButton-Highlight")
|
||||
downBtn:SetScript("OnClick", function()
|
||||
local catId = this:GetParent().categoryId
|
||||
if catId and Guda.Modules.CategoryManager then
|
||||
Guda.Modules.CategoryManager:MoveCategoryDown(catId)
|
||||
Guda_SettingsPopup_CategoriesTab_Update()
|
||||
Guda_SettingsPopup_RefreshBagFrames()
|
||||
end
|
||||
end)
|
||||
row.downBtn = downBtn
|
||||
|
||||
-- Delete button (only for custom categories)
|
||||
local deleteBtn = CreateFrame("Button", rowName .. "_DeleteBtn", row, "UIPanelCloseButton")
|
||||
deleteBtn:SetWidth(20)
|
||||
deleteBtn:SetHeight(20)
|
||||
deleteBtn:SetPoint("LEFT", downBtn, "RIGHT", 5, 0)
|
||||
deleteBtn:SetScript("OnClick", function()
|
||||
local catId = this:GetParent().categoryId
|
||||
if catId and Guda.Modules.CategoryManager then
|
||||
local def = Guda.Modules.CategoryManager:GetCategory(catId)
|
||||
if def and not def.isBuiltIn then
|
||||
Guda.Modules.CategoryManager:DeleteCategory(catId)
|
||||
Guda_SettingsPopup_CategoriesTab_Update()
|
||||
Guda_SettingsPopup_RefreshBagFrames()
|
||||
end
|
||||
end
|
||||
end)
|
||||
row.deleteBtn = deleteBtn
|
||||
|
||||
-- Built-in indicator
|
||||
local builtInText = row:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
|
||||
builtInText:SetPoint("LEFT", deleteBtn, "RIGHT", 5, 0)
|
||||
builtInText:SetText("(Built-in)")
|
||||
builtInText:SetTextColor(0.5, 0.5, 0.5)
|
||||
row.builtInText = builtInText
|
||||
|
||||
-- Hover highlight
|
||||
row:EnableMouse(true)
|
||||
row:SetScript("OnEnter", function()
|
||||
this.bg:SetTexture(1, 1, 1, 0.1)
|
||||
end)
|
||||
row:SetScript("OnLeave", function()
|
||||
this.bg:SetTexture(1, 1, 1, 0)
|
||||
end)
|
||||
|
||||
categoryRowFrames[index] = row
|
||||
return row
|
||||
end
|
||||
|
||||
-- Update the category list display
|
||||
function Guda_SettingsPopup_CategoriesTab_Update()
|
||||
if not Guda.Modules.CategoryManager then return end
|
||||
|
||||
local scrollFrame = getglobal("Guda_SettingsPopup_CategoriesScrollFrame")
|
||||
if not scrollFrame then return end
|
||||
|
||||
local categoryOrder = Guda.Modules.CategoryManager:GetCategoryOrder()
|
||||
local totalCategories = table.getn(categoryOrder)
|
||||
|
||||
-- Update scroll frame
|
||||
FauxScrollFrame_Update(scrollFrame, totalCategories, CATEGORY_VISIBLE_ROWS, CATEGORY_ROW_HEIGHT)
|
||||
|
||||
local offset = FauxScrollFrame_GetOffset(scrollFrame)
|
||||
|
||||
for i = 1, CATEGORY_VISIBLE_ROWS do
|
||||
local row = GetCategoryRowFrame(i)
|
||||
if row then
|
||||
local dataIndex = i + offset
|
||||
if dataIndex <= totalCategories then
|
||||
local categoryId = categoryOrder[dataIndex]
|
||||
local categoryDef = Guda.Modules.CategoryManager:GetCategory(categoryId)
|
||||
|
||||
if categoryDef then
|
||||
row.categoryId = categoryId
|
||||
row.nameText:SetText(categoryDef.name or categoryId)
|
||||
row.checkbox:SetChecked(categoryDef.enabled and 1 or 0)
|
||||
|
||||
-- Show/hide delete button based on whether it's built-in
|
||||
if categoryDef.isBuiltIn then
|
||||
row.deleteBtn:Hide()
|
||||
row.builtInText:Show()
|
||||
else
|
||||
row.deleteBtn:Show()
|
||||
row.builtInText:Hide()
|
||||
end
|
||||
|
||||
-- Enable/disable move buttons based on position
|
||||
if dataIndex == 1 then
|
||||
row.upBtn:Disable()
|
||||
else
|
||||
row.upBtn:Enable()
|
||||
end
|
||||
|
||||
if dataIndex == totalCategories then
|
||||
row.downBtn:Disable()
|
||||
else
|
||||
row.downBtn:Enable()
|
||||
end
|
||||
|
||||
-- Set text color based on enabled state
|
||||
if categoryDef.enabled then
|
||||
row.nameText:SetTextColor(1, 1, 1)
|
||||
else
|
||||
row.nameText:SetTextColor(0.5, 0.5, 0.5)
|
||||
end
|
||||
|
||||
row:Show()
|
||||
else
|
||||
row:Hide()
|
||||
end
|
||||
else
|
||||
row:Hide()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Set reset button text
|
||||
local resetBtn = getglobal("Guda_SettingsPopup_ResetCategoriesButton")
|
||||
if resetBtn then
|
||||
resetBtn:SetText("Reset to Defaults")
|
||||
end
|
||||
end
|
||||
|
||||
-- Reset categories to defaults
|
||||
function Guda_SettingsPopup_ResetCategories_OnClick()
|
||||
if Guda.Modules.CategoryManager then
|
||||
Guda.Modules.CategoryManager:ResetToDefaults()
|
||||
Guda_SettingsPopup_CategoriesTab_Update()
|
||||
Guda_SettingsPopup_RefreshBagFrames()
|
||||
Guda:Print("Categories reset to defaults.")
|
||||
end
|
||||
end
|
||||
|
||||
-- Refresh bag and bank frames after category changes
|
||||
function Guda_SettingsPopup_RefreshBagFrames()
|
||||
-- Refresh category list
|
||||
Guda_RefreshCategoryList()
|
||||
|
||||
-- Update bag frame if visible
|
||||
local bagFrame = getglobal("Guda_BagFrame")
|
||||
if bagFrame and bagFrame:IsShown() then
|
||||
Guda.Modules.BagFrame:Update()
|
||||
end
|
||||
|
||||
-- Update bank frame if visible
|
||||
local bankFrame = getglobal("Guda_BankFrame")
|
||||
if bankFrame and bankFrame:IsShown() then
|
||||
Guda.Modules.BankFrame:Update()
|
||||
end
|
||||
end
|
||||
|
||||
-- Initialize
|
||||
function SettingsPopup:Initialize()
|
||||
Guda:Debug("Settings popup initialized")
|
||||
|
||||
+123
-5
@@ -62,7 +62,7 @@
|
||||
<!-- Tab Buttons (GudaPlates style) -->
|
||||
<Button name="Guda_SettingsPopup_GeneralTabButton" enableMouse="true">
|
||||
<Size>
|
||||
<AbsDimension x="150" y="28"/>
|
||||
<AbsDimension x="113" y="28"/>
|
||||
</Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT" relativeTo="$parent" relativePoint="TOPLEFT">
|
||||
@@ -94,7 +94,7 @@
|
||||
|
||||
<Button name="Guda_SettingsPopup_IconsTabButton" enableMouse="true">
|
||||
<Size>
|
||||
<AbsDimension x="150" y="28"/>
|
||||
<AbsDimension x="113" y="28"/>
|
||||
</Size>
|
||||
<Anchors>
|
||||
<Anchor point="LEFT" relativeTo="Guda_SettingsPopup_GeneralTabButton" relativePoint="RIGHT">
|
||||
@@ -124,9 +124,9 @@
|
||||
</Scripts>
|
||||
</Button>
|
||||
|
||||
<Button name="Guda_SettingsPopup_GuideTabButton" enableMouse="true">
|
||||
<Button name="Guda_SettingsPopup_CategoriesTabButton" enableMouse="true">
|
||||
<Size>
|
||||
<AbsDimension x="150" y="28"/>
|
||||
<AbsDimension x="113" y="28"/>
|
||||
</Size>
|
||||
<Anchors>
|
||||
<Anchor point="LEFT" relativeTo="Guda_SettingsPopup_IconsTabButton" relativePoint="RIGHT">
|
||||
@@ -142,7 +142,39 @@
|
||||
</Texture>
|
||||
</Layer>
|
||||
<Layer level="OVERLAY">
|
||||
<FontString name="$parent_Text" inherits="GameFontNormal" text="Quick Guide">
|
||||
<FontString name="$parent_Text" inherits="GameFontNormal" text="Categories">
|
||||
<Anchors>
|
||||
<Anchor point="CENTER"/>
|
||||
</Anchors>
|
||||
</FontString>
|
||||
</Layer>
|
||||
</Layers>
|
||||
<Scripts>
|
||||
<OnClick>
|
||||
Guda_SettingsPopup_SelectTab("categories")
|
||||
</OnClick>
|
||||
</Scripts>
|
||||
</Button>
|
||||
|
||||
<Button name="Guda_SettingsPopup_GuideTabButton" enableMouse="true">
|
||||
<Size>
|
||||
<AbsDimension x="113" y="28"/>
|
||||
</Size>
|
||||
<Anchors>
|
||||
<Anchor point="LEFT" relativeTo="Guda_SettingsPopup_CategoriesTabButton" relativePoint="RIGHT">
|
||||
<Offset>
|
||||
<AbsDimension x="2" y="0"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Layers>
|
||||
<Layer level="BACKGROUND">
|
||||
<Texture name="$parent_Bg" setAllPoints="true">
|
||||
<Color r="1" g="1" b="1" a="0.1"/>
|
||||
</Texture>
|
||||
</Layer>
|
||||
<Layer level="OVERLAY">
|
||||
<FontString name="$parent_Text" inherits="GameFontNormal" text="Guide">
|
||||
<Anchors>
|
||||
<Anchor point="CENTER"/>
|
||||
</Anchors>
|
||||
@@ -600,6 +632,92 @@
|
||||
</Frames>
|
||||
</Frame>
|
||||
|
||||
<!-- Categories Tab Content -->
|
||||
<Frame name="Guda_SettingsPopup_CategoriesTab" hidden="true">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT" relativeTo="$parent" relativePoint="TOPLEFT">
|
||||
<Offset>
|
||||
<AbsDimension x="15" y="-90"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
<Anchor point="BOTTOMRIGHT" relativeTo="$parent" relativePoint="BOTTOMRIGHT">
|
||||
<Offset>
|
||||
<AbsDimension x="-15" y="15"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Layers>
|
||||
<Layer level="ARTWORK">
|
||||
<FontString name="$parent_Header" inherits="GameFontNormal" text="Manage item categories and their display order:">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset>
|
||||
<AbsDimension x="5" y="-5"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
</FontString>
|
||||
</Layer>
|
||||
</Layers>
|
||||
<Frames>
|
||||
<!-- Scroll Frame for Category List -->
|
||||
<ScrollFrame name="Guda_SettingsPopup_CategoriesScrollFrame" inherits="FauxScrollFrameTemplate">
|
||||
<Size>
|
||||
<AbsDimension x="440" y="320"/>
|
||||
</Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset>
|
||||
<AbsDimension x="5" y="-25"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnVerticalScroll>
|
||||
FauxScrollFrame_OnVerticalScroll(18, Guda_SettingsPopup_CategoriesTab_Update)
|
||||
</OnVerticalScroll>
|
||||
<OnShow>
|
||||
Guda_SettingsPopup_CategoriesTab_Update()
|
||||
</OnShow>
|
||||
</Scripts>
|
||||
</ScrollFrame>
|
||||
|
||||
<!-- Category Row Container -->
|
||||
<Frame name="Guda_SettingsPopup_CategoryListContainer">
|
||||
<Size>
|
||||
<AbsDimension x="420" y="320"/>
|
||||
</Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT" relativeTo="Guda_SettingsPopup_CategoriesScrollFrame" relativePoint="TOPLEFT"/>
|
||||
</Anchors>
|
||||
</Frame>
|
||||
|
||||
<!-- Reset to Defaults Button -->
|
||||
<Button name="Guda_SettingsPopup_ResetCategoriesButton" inherits="UIPanelButtonTemplate">
|
||||
<Size>
|
||||
<AbsDimension x="140" y="25"/>
|
||||
</Size>
|
||||
<Anchors>
|
||||
<Anchor point="BOTTOMLEFT" relativeTo="$parent" relativePoint="BOTTOMLEFT">
|
||||
<Offset>
|
||||
<AbsDimension x="5" y="10"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnClick>
|
||||
Guda_SettingsPopup_ResetCategories_OnClick()
|
||||
</OnClick>
|
||||
</Scripts>
|
||||
<NormalText>
|
||||
<Anchors>
|
||||
<Anchor point="CENTER"/>
|
||||
</Anchors>
|
||||
</NormalText>
|
||||
</Button>
|
||||
</Frames>
|
||||
</Frame>
|
||||
|
||||
<!-- Guide Tab Content -->
|
||||
<Frame name="Guda_SettingsPopup_GuideTab" hidden="true">
|
||||
<Anchors>
|
||||
|
||||
Reference in New Issue
Block a user