diff --git a/Core/Database.lua b/Core/Database.lua
index b5de537..526feb6 100644
--- a/Core/Database.lua
+++ b/Core/Database.lua
@@ -46,7 +46,8 @@ function DB:Initialize()
iconFontSize = 12,
showQualityBorderEquipment = true,
showQualityBorderOther = true,
- showSearchBar = true,
+ showSearchBar = true, -- legacy boolean; preserved for migration. New code reads searchBarMode.
+ searchBarMode = "shown", -- "shown" | "hidden" | "toggle"
hideBagline = true,
hideFooter = false,
bgTransparency = 0.15,
@@ -120,6 +121,11 @@ function DB:Initialize()
if Guda_CharDB.settings.showSearchBar == nil then
Guda_CharDB.settings.showSearchBar = true
end
+ -- Migrate legacy boolean to three-state searchBarMode.
+ if Guda_CharDB.settings.searchBarMode == nil then
+ Guda_CharDB.settings.searchBarMode =
+ (Guda_CharDB.settings.showSearchBar == false) and "hidden" or "shown"
+ end
if Guda_CharDB.settings.questBarPinnedItems == nil then
Guda_CharDB.settings.questBarPinnedItems = {}
end
diff --git a/Core/Utils.lua b/Core/Utils.lua
index 32fdddf..0d304fd 100644
--- a/Core/Utils.lua
+++ b/Core/Utils.lua
@@ -48,6 +48,7 @@ local tooltipCache = {
bindOnEquip = {}, -- IsBindOnEquip results
uniqueItem = {}, -- IsUniqueItem results
restoreTag = {}, -- GetConsumableRestoreTag results
+ fullText = {}, -- GetTooltipText results (full tooltip lowercased, for ~t: search)
}
local tooltipCacheStats = {
hits = 0,
@@ -61,6 +62,7 @@ function Utils:ClearTooltipCache()
tooltipCache.bindOnEquip = {}
tooltipCache.uniqueItem = {}
tooltipCache.restoreTag = {}
+ tooltipCache.fullText = {}
tooltipCacheStats.hits = 0
tooltipCacheStats.misses = 0
addon:Debug("Tooltip cache cleared")
@@ -1240,6 +1242,63 @@ function Utils:GetConsumableRestoreTag(bagID, slotID, itemLink)
return tag
end
+-- Return the full tooltip text of an item as a single lowercase string, with
+-- newlines between lines. Used by the ~t: keyword search filter — one scan
+-- per item per session, cached by item ID so repeated searches are free.
+-- Accepts (bagID, slotID) for live items or (nil, nil, itemLink) as a
+-- link-only fallback (e.g. auction/mail items).
+function Utils:GetTooltipText(bagID, slotID, itemLink)
+ local cacheLink = itemLink or (bagID and slotID and GetContainerItemLink(bagID, slotID))
+ local cacheKey = GetTooltipCacheKey(cacheLink)
+ if cacheKey and tooltipCache.fullText[cacheKey] ~= nil then
+ tooltipCacheStats.hits = tooltipCacheStats.hits + 1
+ local cached = tooltipCache.fullText[cacheKey]
+ return (cached ~= false) and cached or nil
+ end
+ tooltipCacheStats.misses = tooltipCacheStats.misses + 1
+
+ local tooltip = GetScanTooltip()
+ tooltip:ClearLines()
+ local ok = false
+ if bagID and slotID then
+ pcall(function() tooltip:SetBagItem(bagID, slotID); ok = true end)
+ end
+ if not ok and cacheLink then
+ local _, _, itemString = string.find(cacheLink, "|H(item:[^|]+)|h")
+ if itemString then
+ pcall(function() tooltip:SetHyperlink(itemString); ok = true end)
+ end
+ end
+ if not ok then
+ if cacheKey then tooltipCache.fullText[cacheKey] = false end
+ return nil
+ end
+
+ local parts = {}
+ local n = tooltip:NumLines() or 0
+ for i = 1, n do
+ local left = getglobal("GudaScanTooltipTextLeft" .. i)
+ local right = getglobal("GudaScanTooltipTextRight" .. i)
+ local lt = left and left:GetText() or nil
+ local rt = right and right:GetText() or nil
+ if lt and lt ~= "" then table.insert(parts, lt) end
+ if rt and rt ~= "" then table.insert(parts, rt) end
+ end
+
+ -- Don't cache partial data: vanilla sometimes returns a 1-line tooltip
+ -- before the client finishes loading the item record. 2+ lines means
+ -- at least name + one stat/description line was present.
+ if n < 2 then
+ return nil
+ end
+
+ local joined = string.lower(table.concat(parts, "\n"))
+ if cacheKey then
+ tooltipCache.fullText[cacheKey] = joined
+ end
+ return joined
+end
+
-- Check if item is Arrow or Bullet (for Quiver routing).
-- Locale-independent: also matches against the local client's translated
-- subtype strings, resolved from reference vanilla items at runtime.
diff --git a/Guda.toc b/Guda.toc
index 18d4b7f..f270475 100644
--- a/Guda.toc
+++ b/Guda.toc
@@ -2,7 +2,7 @@
## Title: Guda
## Notes: All-in-one bag and bank addon for World of Warcraft 1.12.1 (Turtle WoW)
## Author: Vati
-## Version: 2.2.9
+## Version: 2.3.0
## SavedVariables: Guda_DB
## SavedVariablesPerCharacter: Guda_CharDB
## OptionalDeps: pfUI
diff --git a/UI/BagFrame.lua b/UI/BagFrame.lua
index 3b74922..fb108ec 100644
--- a/UI/BagFrame.lua
+++ b/UI/BagFrame.lua
@@ -237,7 +237,9 @@ function Guda_BagFrame_OnShow(self)
BagFrame:UpdateBorderVisibility()
end
- -- Apply search bar visibility setting
+ -- Apply search bar visibility setting. Always start collapsed in toggle
+ -- mode so opening the bag doesn't leave stale filter state visible.
+ BagFrame.searchBarExpanded = false
if BagFrame.UpdateSearchBarVisibility then
BagFrame:UpdateSearchBarVisibility()
end
@@ -3057,6 +3059,32 @@ function Guda_BagFrame_ClearSearch()
BagFrame:Update()
end
+-- Queue a one-shot warmup of Utils:GetTooltipText for every occupied bag
+-- slot currently visible. Runs once per session the first time the user
+-- types a ~t: query — subsequent keystrokes hit warm cache and filter
+-- instantly. Frame-budgeted via Utils:QueueWork so typing stays responsive.
+local tooltipTextWarmed = false
+local function WarmTooltipTextCache()
+ if tooltipTextWarmed then return end
+ tooltipTextWarmed = true
+ local Utils = addon.Modules.Utils
+ if not Utils or not Utils.QueueWork or not Utils.GetTooltipText then return end
+ for bagID = 0, 4 do
+ local numSlots = GetContainerNumSlots(bagID)
+ if numSlots and numSlots > 0 then
+ for slotID = 1, numSlots do
+ local link = GetContainerItemLink(bagID, slotID)
+ if link then
+ local b, s, l = bagID, slotID, link
+ Utils:QueueWork(function()
+ Utils:GetTooltipText(b, s, l)
+ end, "tooltipTextSearchWarmup")
+ end
+ end
+ end
+ end
+end
+
-- Search changed handler
function Guda_BagFrame_OnSearchChanged(self)
local text = self:GetText()
@@ -3069,6 +3097,12 @@ function Guda_BagFrame_OnSearchChanged(self)
BagFrame.foundFirstMatch = false -- Reset debug flags
BagFrame.warnedAboutParsing = false
BagFrame.warnedAboutNoName = false
+ -- Kick off a one-time tooltip-text warmup if the query uses ~t:.
+ -- string.find returns nil when pattern is absent; plain=true to match
+ -- "~t:" literally (no magic chars in the pattern).
+ if string.find(text, "~t:", 1, true) then
+ WarmTooltipTextCache()
+ end
BagFrame:Update()
end
end
@@ -3677,29 +3711,75 @@ function BagFrame:UpdateBorderVisibility()
end
-- Update search bar visibility based on setting
+-- Reads the three-state searchBarMode setting.
+-- Returns one of "shown", "hidden", "toggle". Falls back to the legacy
+-- boolean showSearchBar if the new setting isn't set yet.
+local function GetSearchBarMode()
+ if not addon or not addon.Modules or not addon.Modules.DB then return "shown" end
+ local mode = addon.Modules.DB:GetSetting("searchBarMode")
+ if mode == "shown" or mode == "hidden" or mode == "toggle" then
+ return mode
+ end
+ local legacy = addon.Modules.DB:GetSetting("showSearchBar")
+ if legacy == false then return "hidden" end
+ return "shown"
+end
+
function BagFrame:UpdateSearchBarVisibility()
if not addon or not addon.Modules or not addon.Modules.DB then return end
- local searchBar = getglobal("Guda_BagFrame_SearchBar")
+ local searchBar = getglobal("Guda_BagFrame_SearchBar")
local itemContainer = getglobal("Guda_BagFrame_ItemContainer")
+ local toggleBtn = getglobal("Guda_BagFrame_SearchToggleButton")
if not searchBar or not itemContainer then return end
- local showSearchBar = addon.Modules.DB:GetSetting("showSearchBar")
- if showSearchBar == nil then
- showSearchBar = true
+ local mode = GetSearchBarMode()
+ local effectiveShown
+ if mode == "shown" then
+ effectiveShown = true
+ elseif mode == "hidden" then
+ effectiveShown = false
+ else -- "toggle"
+ effectiveShown = self.searchBarExpanded and true or false
end
- if showSearchBar then
+ if effectiveShown then
searchBar:Show()
- -- Anchor ItemContainer to SearchBar's bottom
itemContainer:ClearAllPoints()
itemContainer:SetPoint("TOP", searchBar, "BOTTOM", 0, -5)
else
searchBar:Hide()
- -- Anchor ItemContainer directly to frame top (skip search bar space)
itemContainer:ClearAllPoints()
itemContainer:SetPoint("TOP", "Guda_BagFrame", "TOP", 0, -40)
end
+
+ if toggleBtn then
+ if mode == "toggle" then toggleBtn:Show() else toggleBtn:Hide() end
+ end
+end
+
+-- Toggle the search bar's expanded state (only meaningful in "toggle" mode).
+function BagFrame:ToggleSearchBar()
+ local mode = GetSearchBarMode()
+ if mode ~= "toggle" then return end -- no-op in shown/hidden modes
+
+ self.searchBarExpanded = not (self.searchBarExpanded and true or false)
+ self:UpdateSearchBarVisibility()
+
+ local searchBox = getglobal("Guda_BagFrame_SearchBar_SearchBox")
+ if self.searchBarExpanded then
+ if searchBox then searchBox:SetFocus() end
+ else
+ if searchBox then
+ searchBox:SetText("")
+ searchBox:ClearFocus()
+ if Guda_BagFrame_OnSearchChanged then
+ Guda_BagFrame_OnSearchChanged(searchBox)
+ end
+ end
+ end
+
+ if BagFrame.Update then BagFrame:Update() end
end
-- Update footer visibility based on settings
diff --git a/UI/BagFrame.xml b/UI/BagFrame.xml
index 324a2f0..5606c0e 100644
--- a/UI/BagFrame.xml
+++ b/UI/BagFrame.xml
@@ -147,6 +147,56 @@
+
+
+