From 47b3dccd2ade0305255664a5a8c6728661a37add Mon Sep 17 00:00:00 2001
From: wowaddonmaker
Date: Sun, 15 Mar 2026 23:37:03 -0400
Subject: [PATCH 01/10] Execute DirectOpen navigation synchronously for instant
transitions
DirectOpen previously chained each step with C_Timer.After delays
(0.1-0.3s each), causing visible flickering through intermediate panels
and ~0.75s of timer overhead before the highlight could start.
Now all navigation clicks (micro button, tabs, side tabs, sidebars,
categories, search buttons) execute in a single frame. The user sees
only the final panel state. StartGuideAtStep also attempts an immediate
highlight before falling back to the 0.1s polling ticker.
The only remaining async path is currency/reputation tab resync, which
genuinely needs one frame for the ScrollBox to rebuild after toggling.
---
Highlight.lua | 16 +++-
UI.lua | 225 +++++++++++++++++++++-----------------------------
2 files changed, 106 insertions(+), 135 deletions(-)
diff --git a/Highlight.lua b/Highlight.lua
index 18740ce..a7d1129 100644
--- a/Highlight.lua
+++ b/Highlight.lua
@@ -237,11 +237,21 @@ function Highlight:StartGuideAtStep(guideData, stepIndex)
currentGuide = guideData
currentStepIndex = stepIndex
+ -- Attempt immediate highlight (avoids waiting for first ticker interval)
+ local ok, err = pcall(self.UpdateGuide, self)
+ if not ok then
+ ns.DebugPrint("Guide error: " .. tostring(err))
+ return
+ end
+
+ -- If guide completed or was cancelled by the immediate attempt, no ticker needed
+ if not currentGuide then return end
+
stepTicker = C_Timer.NewTicker(0.1, function()
- local ok, err = pcall(self.UpdateGuide, self)
- if not ok then
+ local ok2, err2 = pcall(self.UpdateGuide, self)
+ if not ok2 then
if stepTicker then stepTicker:Cancel(); stepTicker = nil end
- ns.DebugPrint("Guide error: " .. tostring(err))
+ ns.DebugPrint("Guide error: " .. tostring(err2))
end
end)
end
diff --git a/UI.lua b/UI.lua
index 23a87ef..9fcdac1 100644
--- a/UI.lua
+++ b/UI.lua
@@ -3561,166 +3561,127 @@ function UI:DirectOpen(data)
return
end
- local function executeStep(stepIndex)
- -- Done executing - either finished completely or hand off to highlight
- if stepIndex > executeCount then
- if not finalStepNavigable then
- -- Final step is highlight-only - show it to the user
- C_Timer.After(0.15, function()
- if Highlight then
- Highlight:StartGuideAtStep(data, totalSteps)
- end
- end)
+ -- Execute all navigable steps synchronously in one frame. WoW frame
+ -- operations (ClickButton, tab selection) process immediately, so
+ -- child frames are available right after their parent is shown.
+ -- The only exception is currency/reputation tab resync, which toggles
+ -- tabs and needs one frame for the ScrollBox to rebuild.
+ local function executeFrom(start)
+ for i = start, executeCount do
+ local step = steps[i]
+
+ if step.buttonFrame then
+ local stepFrame = _G[step.buttonFrame]
+ if stepFrame then ClickButton(stepFrame) end
end
- -- If final step was navigable, we already executed it - nothing more to do
- return
- end
-
- local step = steps[stepIndex]
- local nextDelay = 0.1
- -- Click a micro menu button (like LFDMicroButton, CharacterMicroButton, etc.)
- if step.buttonFrame then
- local stepFrame = _G[step.buttonFrame]
- if stepFrame then ClickButton(stepFrame) end
- nextDelay = 0.15
- end
-
- -- Click a main tab (Dungeons & Raids / Player vs. Player / etc.)
- if step.waitForFrame and step.tabIndex then
- local resync = false
- if step.waitForFrame == "CharacterFrame" then
- if needsCurrencyResync and step.tabIndex == 3 then
- resync = true
- needsCurrencyResync = false
- elseif needsReputationResync and step.tabIndex == 2 then
- resync = true
- needsReputationResync = false
+ if step.waitForFrame and step.tabIndex then
+ local resync = false
+ if step.waitForFrame == "CharacterFrame" then
+ if needsCurrencyResync and step.tabIndex == 3 then
+ resync = true
+ needsCurrencyResync = false
+ elseif needsReputationResync and step.tabIndex == 2 then
+ resync = true
+ needsReputationResync = false
+ end
end
- end
- if resync then
- -- Headers were pre-expanded via API. Toggle tabs to force
- -- the ScrollBox to rebuild with the expanded state.
- ClickButton(Highlight:GetTabButton("CharacterFrame", 1))
- C_Timer.After(0.05, function()
+ if resync then
+ -- Toggle tabs to force ScrollBox rebuild with expanded headers.
+ -- Needs one frame to propagate; defer remaining steps.
+ ClickButton(Highlight:GetTabButton("CharacterFrame", 1))
+ local waitFrame = step.waitForFrame
+ local tabIdx = step.tabIndex
+ local resume = i + 1
+ C_Timer.After(0.05, function()
+ ClickButton(Highlight:GetTabButton(waitFrame, tabIdx))
+ executeFrom(resume)
+ end)
+ return
+ else
ClickButton(Highlight:GetTabButton(step.waitForFrame, step.tabIndex))
- end)
- nextDelay = 0.2
- else
- ClickButton(Highlight:GetTabButton(step.waitForFrame, step.tabIndex))
- nextDelay = 0.15
+ end
end
- end
- -- Click a PvE side tab (Dungeon Finder / Raid Finder / Premade Groups)
- if step.sideTabIndex then
- C_Timer.After(0.05, function()
+ if step.sideTabIndex then
ClickButton(Highlight:GetSideTabButton(step.waitForFrame or "PVEFrame", step.sideTabIndex))
- end)
- nextDelay = 0.2
- end
+ end
- -- Click a PvP side tab (Quick Match / Rated / Premade Groups / Training Grounds)
- if step.pvpSideTabIndex then
- C_Timer.After(0.05, function()
+ if step.pvpSideTabIndex then
ClickButton(Highlight:GetPvPSideTabButton(step.waitForFrame or "PVEFrame", step.pvpSideTabIndex))
- end)
- nextDelay = 0.2
- end
-
- -- Click a Character Frame sidebar tab
- if step.sidebarButtonFrame or step.sidebarIndex then
- self:ClickCharacterSidebar(step.sidebarIndex)
- nextDelay = 0.15
- end
+ end
- -- Click a statistics or achievement category
- local categoryToClick = step.statisticsCategory or step.achievementCategory
- if categoryToClick then
- self:ClickAchievementCategory(categoryToClick)
- nextDelay = 0.3
- end
+ if step.sidebarButtonFrame or step.sidebarIndex then
+ self:ClickCharacterSidebar(step.sidebarIndex)
+ end
- -- Currency headers are pre-expanded via API. Skip these steps
- -- (the tab resync below handles syncing TokenFrame's display).
- if step.currencyHeader then
- nextDelay = 0.05
- end
+ local categoryToClick = step.statisticsCategory or step.achievementCategory
+ if categoryToClick then
+ self:ClickAchievementCategory(categoryToClick)
+ end
- -- Scroll to a currency
- if step.currencyID then
- Highlight:ScrollToCurrencyRow(step.currencyID)
- -- If this is the last step, highlight it
- if stepIndex == executeCount then
- C_Timer.After(0.05, function()
- local currencyRow = Highlight:GetCurrencyRowButton(step.currencyID)
- if currencyRow then
- Highlight:HighlightFrame(currencyRow, nil)
- -- Set up hover detection to clear highlight
- local checkHover
- checkHover = function()
- if currencyRow:IsMouseOver() then
- Highlight:HideHighlight()
- else
- C_Timer.After(0.1, checkHover)
+ -- Currency/faction headers pre-expanded via API, nothing to execute
+
+ if step.currencyID then
+ Highlight:ScrollToCurrencyRow(step.currencyID)
+ if i == executeCount then
+ -- ScrollBox needs one frame to update after scroll; defer highlight
+ local cID = step.currencyID
+ C_Timer.After(0.05, function()
+ local currencyRow = Highlight:GetCurrencyRowButton(cID)
+ if currencyRow then
+ Highlight:HighlightFrame(currencyRow, nil)
+ local checkHover
+ checkHover = function()
+ if currencyRow:IsMouseOver() then
+ Highlight:HideHighlight()
+ else
+ C_Timer.After(0.1, checkHover)
+ end
end
+ C_Timer.After(0.3, checkHover)
end
- C_Timer.After(0.3, checkHover)
- end
- end)
+ end)
+ end
end
- nextDelay = 0.15
- end
-
- -- Faction headers are pre-expanded via API (same as currency).
- if step.factionHeader then
- nextDelay = 0.05
- end
- -- Scroll to a faction
- if step.factionID then
- Highlight:ScrollToFactionRow(step.factionID)
- -- If this is the last step, highlight it
- if stepIndex == executeCount then
- C_Timer.After(0.05, function()
- local factionRow = Highlight:GetFactionRowButton(step.factionID)
- if factionRow then
- Highlight:HighlightFrame(factionRow, nil)
- -- Set up hover detection to clear highlight
- local checkHover
- checkHover = function()
- if factionRow:IsMouseOver() then
- Highlight:HideHighlight()
- else
- C_Timer.After(0.1, checkHover)
+ if step.factionID then
+ Highlight:ScrollToFactionRow(step.factionID)
+ if i == executeCount then
+ local fID = step.factionID
+ C_Timer.After(0.05, function()
+ local factionRow = Highlight:GetFactionRowButton(fID)
+ if factionRow then
+ Highlight:HighlightFrame(factionRow, nil)
+ local checkHover
+ checkHover = function()
+ if factionRow:IsMouseOver() then
+ Highlight:HideHighlight()
+ else
+ C_Timer.After(0.1, checkHover)
+ end
end
+ C_Timer.After(0.3, checkHover)
end
- C_Timer.After(0.3, checkHover)
- end
- end)
+ end)
+ end
end
- nextDelay = 0.15
- end
- -- Click a button found by text search (Premade Groups categories, PvP queue buttons, etc.)
- if step.searchButtonText then
- C_Timer.After(0.05, function()
+ if step.searchButtonText then
local parentFrame = step.waitForFrame and _G[step.waitForFrame]
if parentFrame then
ClickButton(SearchFrameTreeFuzzy(parentFrame, slower(step.searchButtonText)))
end
- end)
- nextDelay = 0.3
+ end
end
- -- Chain to the next step after a delay
- C_Timer.After(nextDelay, function()
- executeStep(stepIndex + 1)
- end)
+ -- All navigable steps executed; highlight the final step if needed
+ if not finalStepNavigable and Highlight then
+ Highlight:StartGuideAtStep(data, totalSteps)
+ end
end
- -- Start executing from step 1
- executeStep(1)
+ executeFrom(1)
end
-- Helper function to click Character Frame sidebar buttons
From 335cfa27508eb5fa2a1bbd1ecd3d2a08ef4df1d3 Mon Sep 17 00:00:00 2001
From: wowaddonmaker
Date: Mon, 16 Mar 2026 01:01:57 -0400
Subject: [PATCH 02/10] Consolidate Map Search options into multi-select
dropdown menus
Replace 10 individual checkboxes with 4 categorized dropdown menus
(Search Bars, Map Pins, Minimap, Automation) that show comma-separated
active options as summary text. Each dropdown's flyout contains its
related checkboxes and sliders, keeping all settings for a feature
grouped together.
Other options panel changes:
- Narrow panel from 570px to 485px, stack bottom buttons in 2x2 grid
- Single-column keybind layout, remove Toggle+Focus row
- Shorten all sliders by 40%
- Flyouts parented to UIParent with solid backgrounds so they render
on top and aren't clipped by the panel
- Arrival Distance slider disables when Auto Clear is unchecked
- Rename button labels: Reset Settings, Report Bug, Request Feature
---
Options.lua | 467 +++++++++++++++++++++++++++++++---------------------
1 file changed, 282 insertions(+), 185 deletions(-)
diff --git a/Options.lua b/Options.lua
index b7f5460..40daa73 100644
--- a/Options.lua
+++ b/Options.lua
@@ -7,7 +7,7 @@ local Utils = ns.Utils
local sformat = Utils.sformat
local mfloor, mmin, mmax = Utils.mfloor, Utils.mmin, Utils.mmax
local tonumber, tostring = Utils.tonumber, Utils.tostring
-local tinsert = Utils.tinsert
+local tinsert, tconcat = Utils.tinsert, Utils.tconcat
local IsMouseButtonDown = IsMouseButtonDown
local DEFAULT_OPACITY = ns.DEFAULT_OPACITY
@@ -92,7 +92,7 @@ end
-- Helper to create a slider (anchored manually by caller)
local function CreateSlider(parent, name, label, minVal, maxVal, step, tooltipText, formatFunc, defaultValue, unitSuffix)
local slider = CreateFrame("Slider", "EasyFindOptions" .. name .. "Slider", parent, "OptionsSliderTemplate")
- slider:SetWidth(200)
+ slider:SetWidth(120)
slider:SetMinMaxValues(minVal, maxVal)
slider:SetValueStep(step)
slider:SetObeyStepOnDrag(true)
@@ -235,6 +235,151 @@ local function CreateCheckbox(parent, name, label, tooltipText)
return checkbox
end
+-- Multi-select dropdown: a label + button that opens a flyout with checkboxes.
+-- optionDefs: array of { label, dbKey, default, tooltip, callback }
+-- Returns a wrapper frame with :UpdateVisuals(), :SetGroupEnabled(bool),
+-- :AddSlider(...), and .flyout / .checkRows / .sliders fields.
+local function CreateMultiSelectDropdown(parent, groupLabel, optionDefs, btnWidth, flyoutWidth)
+ btnWidth = btnWidth or 160
+ flyoutWidth = flyoutWidth or btnWidth
+
+ local wrapper = CreateFrame("Frame", nil, parent)
+ wrapper:SetHeight(22)
+
+ local label = wrapper:CreateFontString(nil, "OVERLAY", "GameFontNormal")
+ label:SetPoint("LEFT", 4, 0)
+ label:SetText(groupLabel .. ":")
+ wrapper.label = label
+
+ -- Summary text: comma-separated short names of active options
+ local function GetSummaryText()
+ local parts = {}
+ for _, def in ipairs(optionDefs) do
+ local val = EasyFind.db[def.dbKey]
+ if val == nil then val = def.default end
+ if val then tinsert(parts, def.shortLabel or def.label) end
+ end
+ if #parts == 0 then return "None" end
+ return tconcat(parts, ", ")
+ end
+
+ local btnFrame = CreateFrame("Button", nil, wrapper, "BackdropTemplate")
+ btnFrame:SetSize(btnWidth, 22)
+ btnFrame:SetBackdrop(SELECTOR_BACKDROP)
+ btnFrame:SetBackdropColor(0.1, 0.1, 0.1, 0.8)
+ btnFrame:SetBackdropBorderColor(0.6, 0.6, 0.6, 0.8)
+ wrapper.button = btnFrame
+
+ local btnText = btnFrame:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
+ btnText:SetPoint("CENTER")
+ btnText:SetText(GetSummaryText())
+ wrapper.btnText = btnText
+
+ -- Flyout panel with checkboxes (and optional sliders via AddSlider).
+ -- Parented to UIParent so it isn't clipped by the options frame.
+ local ROW_H = 22
+ local flyout = CreateFrame("Frame", nil, UIParent, "BackdropTemplate")
+ flyout:SetSize(flyoutWidth, #optionDefs * ROW_H + 8)
+ flyout:SetFrameStrata("FULLSCREEN_DIALOG")
+ flyout:SetFrameLevel(900)
+ flyout:SetBackdrop({
+ bgFile = "Interface\\BUTTONS\\WHITE8X8",
+ edgeFile = TOOLTIP_BORDER,
+ tile = true, tileSize = 16, edgeSize = 12,
+ insets = { left = 2, right = 2, top = 2, bottom = 2 },
+ })
+ flyout:SetBackdropColor(0.08, 0.08, 0.08, 1)
+ flyout:Hide()
+ wrapper.flyout = flyout
+
+ -- Track vertical cursor for adding content (checkboxes, then sliders)
+ local contentH = #optionDefs * ROW_H + 4
+
+ btnFrame:SetScript("OnClick", function()
+ -- Re-anchor each time since the button may have moved
+ flyout:ClearAllPoints()
+ flyout:SetPoint("TOP", btnFrame, "BOTTOM", 0, -2)
+ flyout:SetShown(not flyout:IsShown())
+ end)
+
+ -- Click-away to close
+ flyout:SetScript("OnShow", function(self)
+ self:SetScript("OnUpdate", function(self)
+ if not self:IsMouseOver() and not btnFrame:IsMouseOver() then
+ if IsMouseButtonDown("LeftButton") or IsMouseButtonDown("RightButton") then
+ self:Hide()
+ end
+ end
+ end)
+ end)
+ flyout:SetScript("OnHide", function(self)
+ self:SetScript("OnUpdate", nil)
+ end)
+
+ wrapper.checkRows = {}
+ for i, def in ipairs(optionDefs) do
+ local row = CreateFrame("CheckButton", nil, flyout, "InterfaceOptionsCheckButtonTemplate")
+ row:SetPoint("TOPLEFT", flyout, "TOPLEFT", 4, -4 - (i - 1) * ROW_H)
+ row.Text:SetText(def.label)
+ row.Text:SetFontObject("GameFontHighlightSmall")
+
+ local val = EasyFind.db[def.dbKey]
+ if val == nil then val = def.default end
+ row:SetChecked(val)
+
+ row:SetScript("OnClick", function(self)
+ EasyFind.db[def.dbKey] = self:GetChecked()
+ btnText:SetText(GetSummaryText())
+ if def.callback then def.callback() end
+ end)
+
+ if def.tooltip then
+ row:SetScript("OnEnter", function(self)
+ GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
+ GameTooltip:SetText(def.label)
+ GameTooltip:AddLine(def.tooltip, 1, 1, 1, true)
+ GameTooltip:Show()
+ end)
+ row:SetScript("OnLeave", GameTooltip_Hide)
+ end
+
+ row.dbKey = def.dbKey
+ row.defaultVal = def.default
+ tinsert(wrapper.checkRows, row)
+ end
+
+ -- Add a slider inside the flyout, below existing content.
+ -- Returns the slider frame. Uses the same API as CreateSlider.
+ wrapper.sliders = {}
+ function wrapper:AddSlider(name, sliderLabel, minVal, maxVal, step, tooltipText, formatFunc, defaultValue, unitSuffix)
+ contentH = contentH + 18
+ local slider = CreateSlider(flyout, name, sliderLabel, minVal, maxVal, step, tooltipText, formatFunc, defaultValue, unitSuffix)
+ slider:SetWidth(flyoutWidth - 110)
+ slider:SetPoint("TOPLEFT", flyout, "TOPLEFT", 14, -contentH)
+ contentH = contentH + 36
+ flyout:SetHeight(contentH + 4)
+ tinsert(self.sliders, slider)
+ return slider
+ end
+
+ function wrapper:UpdateVisuals()
+ for _, row in ipairs(self.checkRows) do
+ local val = EasyFind.db[row.dbKey]
+ if val == nil then val = row.defaultVal end
+ row:SetChecked(val)
+ end
+ btnText:SetText(GetSummaryText())
+ end
+
+ function wrapper:SetGroupEnabled(enabled)
+ if enabled then btnFrame:Enable() else btnFrame:Disable() end
+ self:SetAlpha(enabled and 1.0 or 0.35)
+ if not enabled and flyout:IsShown() then flyout:Hide() end
+ end
+
+ return wrapper
+end
+
local DISABLED_TEXT = { 0.5, 0.5, 0.5 }
local NORMAL_TEXT = { 1.0, 0.82, 0.0 } -- GameFontNormal yellow
@@ -264,7 +409,11 @@ local function SetControlsEnabled(controls, enabled)
elseif objType == "Button" then
if enabled then ctrl:Enable() else ctrl:Disable() end
elseif objType == "Frame" then
- ctrl:SetAlpha(enabled and 1.0 or 0.35)
+ if ctrl.SetGroupEnabled then
+ ctrl:SetGroupEnabled(enabled)
+ else
+ ctrl:SetAlpha(enabled and 1.0 or 0.35)
+ end
end
end
end
@@ -273,14 +422,14 @@ function Options:Initialize()
if isInitialized then return end
isInitialized = true
- local FRAME_W = 570
+ local FRAME_W = 485
local COL_LEFT = 4 -- Left column offset within content frames
- local COL_RIGHT = 280 -- Right column offset within content frames
+ local COL_RIGHT = 262 -- Right column offset within content frames
local BTN_OFFSET = 105 -- Label LEFT to button LEFT (aligns selectors/keybinds)
local HEADER_H = 26
local HEADER_GAP = 4
local TITLE_Y = -50 -- Y where first section header starts
- local BOTTOM_PAD = 88 -- Space for separator, tips, reset buttons
+ local BOTTOM_PAD = 116 -- Space for separator, tips, 2x2 reset buttons
-- Create the main options frame
optionsFrame = CreateFrame("Frame", "EasyFindOptionsFrame", UIParent, "BackdropTemplate")
@@ -773,7 +922,7 @@ function Options:Initialize()
UpdateUIToggleVisual()
-- SECTION 3: Map Search
- local sec2 = CreateSection("Map Search", 394)
+ local sec2 = CreateSection("Map Search", 210)
local mapEnableCheckbox = CreateCheckbox(sec2, "EnableMap", "Enable Map Search Module",
"Uncheck to disable map search bars, pins, and all map overlay features.\n\nRequires a UI reload to take effect.")
@@ -847,9 +996,28 @@ function Options:Initialize()
end)
resizeMapBtn:SetScript("OnLeave", GameTooltip_Hide)
- local mapFontSlider = CreateSlider(sec2, "MapFontSize", "Font Size|cffff3333*|r", 0.5, 2.0, 0.1,
+ local FLYOUT_W = 260
+
+ local searchBarGroup = CreateMultiSelectDropdown(sec2, "Search Bars", {
+ { label = "Smart Show", shortLabel = "Smart", dbKey = "mapSmartShow", default = false,
+ tooltip = "Map search bars hide until you move your mouse near them.\nBars reappear on hover and fade when you move away.\nText in search bars or an open results list prevents fading.",
+ callback = function() if ns.MapSearch and ns.MapSearch.UpdateMapSmartShow then ns.MapSearch:UpdateMapSmartShow() end end },
+ { label = "Hide Fullscreen", shortLabel = "No Full", dbKey = "hideSearchBarsMaximized", default = false,
+ tooltip = "Both map search bars are hidden when the world map is maximized (full screen).\nThey reappear when you return to the windowed map.",
+ callback = function() if ns.MapSearch and ns.MapSearch.UpdateHideMaximized then ns.MapSearch:UpdateHideMaximized() end end },
+ { label = "Results Above", shortLabel = "Above", dbKey = "mapResultsAbove", default = false,
+ tooltip = "Map search results appear above the bar instead of below.\nApplies to both local and global map search bars.",
+ callback = function() if ns.MapSearch and ns.MapSearch.RefreshResultsAnchor then ns.MapSearch:RefreshResultsAnchor() end end },
+ }, nil, FLYOUT_W)
+ searchBarGroup:SetPoint("TOPLEFT", mapSpeedBox, "BOTTOMLEFT", 0, -8)
+ searchBarGroup:SetPoint("RIGHT", sec2, "RIGHT", -8, 0)
+ searchBarGroup.label:SetWidth(85)
+ searchBarGroup.label:SetJustifyH("LEFT")
+ searchBarGroup.button:SetPoint("LEFT", searchBarGroup.label, "RIGHT", 6, 0)
+ optionsFrame.searchBarGroup = searchBarGroup
+
+ local mapFontSlider = searchBarGroup:AddSlider("MapFontSize", "Font Size|cffff3333*|r", 0.5, 2.0, 0.1,
"Changing font size also affects search bar height and results window sizing.", nil, 1.0)
- mapFontSlider:SetPoint("TOPLEFT", mapOptionsBox, "TOPLEFT", COL_RIGHT - 2, -28)
mapFontSlider:SetValue(EasyFind.db.mapFontSize or 1.0)
mapFontSlider:HookScript("OnValueChanged", function(self, value)
EasyFind.db.mapFontSize = value
@@ -859,46 +1027,9 @@ function Options:Initialize()
end)
optionsFrame.mapFontSlider = mapFontSlider
- local mapSmartShowCheckbox = CreateCheckbox(sec2, "MapSmartShow", "Smart Show (auto-hide)",
- "When enabled, map search bars hide until you move your mouse near them.\n\nThe bars reappear on hover and fade away when you move away.\n\nText in the search bars or an open results list prevents fading.")
- mapSmartShowCheckbox:SetPoint("TOPLEFT", mapSpeedBox, "BOTTOMLEFT", 8, -6)
- mapSmartShowCheckbox:SetChecked(EasyFind.db.mapSmartShow or false)
- mapSmartShowCheckbox:SetScript("OnClick", function(self)
- EasyFind.db.mapSmartShow = self:GetChecked()
- if ns.MapSearch and ns.MapSearch.UpdateMapSmartShow then
- ns.MapSearch:UpdateMapSmartShow()
- end
- end)
- optionsFrame.mapSmartShowCheckbox = mapSmartShowCheckbox
-
- local hideMaxCheckbox = CreateCheckbox(sec2, "HideMaximized", "Hide Bars in Full Screen Map",
- "When enabled, both map search bars are hidden when the world map is maximized (full screen).\n\nThey reappear when you return to the windowed map.")
- hideMaxCheckbox:SetPoint("TOPLEFT", mapSmartShowCheckbox, "BOTTOMLEFT", 0, -4)
- hideMaxCheckbox:SetChecked(EasyFind.db.hideSearchBarsMaximized or false)
- hideMaxCheckbox:SetScript("OnClick", function(self)
- EasyFind.db.hideSearchBarsMaximized = self:GetChecked()
- if ns.MapSearch and ns.MapSearch.UpdateHideMaximized then
- ns.MapSearch:UpdateHideMaximized()
- end
- end)
- optionsFrame.hideMaxCheckbox = hideMaxCheckbox
-
- local mapResultsAboveCheckbox = CreateCheckbox(sec2, "MapResultsAbove", "Map Results Above",
- "When enabled, map search bars show results above the bar instead of below.\n\nApplies to both local and global map search bars.")
- mapResultsAboveCheckbox:SetPoint("TOPLEFT", hideMaxCheckbox, "BOTTOMLEFT", 0, -4)
- mapResultsAboveCheckbox:SetChecked(EasyFind.db.mapResultsAbove or false)
- mapResultsAboveCheckbox:SetScript("OnClick", function(self)
- EasyFind.db.mapResultsAbove = self:GetChecked()
- if ns.MapSearch and ns.MapSearch.RefreshResultsAnchor then
- ns.MapSearch:RefreshResultsAnchor()
- end
- end)
- optionsFrame.mapResultsAboveCheckbox = mapResultsAboveCheckbox
-
- local mapYOffsetSlider = CreateSlider(sec2, "MapYOffset", "Bar Y Offset", -20, 20, 1,
+ local mapYOffsetSlider = searchBarGroup:AddSlider("MapYOffset", "Bar Y Offset", -20, 20, 1,
"Vertical offset for the map search bars relative to the map bottom edge.\nPositive moves up, negative moves down.",
function(val) return tostring(mfloor(val + 0.5)) .. "px" end, 0, "px")
- mapYOffsetSlider:SetPoint("TOPLEFT", mapFontSlider, "BOTTOMLEFT", 0, -38)
mapYOffsetSlider:SetValue(EasyFind.db.mapSearchYOffset or 0)
mapYOffsetSlider:HookScript("OnValueChanged", function(self, value)
value = mfloor(value + 0.5)
@@ -909,9 +1040,23 @@ function Options:Initialize()
end)
optionsFrame.mapYOffsetSlider = mapYOffsetSlider
- local mapIconSlider = CreateSlider(sec2, "MapIcon", "Icon Size", 0.5, 2.0, 0.1,
+ local mapPinGroup = CreateMultiSelectDropdown(sec2, "Map Pins", {
+ { label = "Highlight Box", shortLabel = "Highlight", dbKey = "mapPinHighlight", default = true,
+ tooltip = "A yellow highlight box is drawn around map search pins.\nDisable to show only the pin icon and indicator arrow.",
+ callback = function() if ns.MapSearch and ns.MapSearch.UpdatePinHighlight then ns.MapSearch:UpdatePinHighlight() end end },
+ { label = "Blinking", shortLabel = "Blink", dbKey = "blinkingPins", default = false,
+ tooltip = "Map search pins and highlight boxes pulse in sync with the indicator arrow.\nWhen disabled, pins and highlights are steady. The indicator arrow always bobs.",
+ callback = function() if ns.MapSearch and ns.MapSearch.UpdateBlinkingPins then ns.MapSearch:UpdateBlinkingPins() end end },
+ }, nil, FLYOUT_W)
+ mapPinGroup:SetPoint("TOPLEFT", searchBarGroup, "BOTTOMLEFT", 0, -6)
+ mapPinGroup:SetPoint("RIGHT", sec2, "RIGHT", -8, 0)
+ mapPinGroup.label:SetWidth(85)
+ mapPinGroup.label:SetJustifyH("LEFT")
+ mapPinGroup.button:SetPoint("LEFT", mapPinGroup.label, "RIGHT", 6, 0)
+ optionsFrame.mapPinGroup = mapPinGroup
+
+ local mapIconSlider = mapPinGroup:AddSlider("MapIcon", "Icon Size", 0.5, 2.0, 0.1,
"Adjusts the size of map search result icons on the world map.", nil, 0.8)
- mapIconSlider:SetPoint("TOPLEFT", mapYOffsetSlider, "BOTTOMLEFT", 0, -38)
mapIconSlider:SetValue(EasyFind.db.iconScale or 0.8)
mapIconSlider:HookScript("OnValueChanged", function(self, value)
EasyFind.db.iconScale = value
@@ -925,10 +1070,46 @@ function Options:Initialize()
end)
optionsFrame.mapIconSlider = mapIconSlider
- local arrivalSlider = CreateSlider(sec2, "ArrivalDist", "Arrival Distance", 3, 50, 1,
+ local minimapGroup = CreateMultiSelectDropdown(sec2, "Minimap", {
+ { label = "Arrow Glow", shortLabel = "Arrow", dbKey = "minimapArrowGlow", default = true,
+ tooltip = "A pulsing glow highlights the minimap perimeter arrow that points toward your active map pin.\nDisable if you find the glow distracting." },
+ { label = "Guide Circle", shortLabel = "Circle", dbKey = "minimapGuideCircle", default = true,
+ tooltip = "A directional ring and arrow appears around your character on the minimap when a map pin is nearby, pointing toward the destination.\nDisable if you prefer only the default minimap pin." },
+ { label = "Pin Glow", shortLabel = "Glow", dbKey = "minimapPinGlow", default = true,
+ tooltip = "A pulsing glow appears on the minimap pin when the guide circle shrinks onto it.\nDisable if you find the glow distracting." },
+ }, nil, FLYOUT_W)
+ minimapGroup:SetPoint("TOPLEFT", mapPinGroup, "BOTTOMLEFT", 0, -6)
+ minimapGroup:SetPoint("RIGHT", sec2, "RIGHT", -8, 0)
+ minimapGroup.label:SetWidth(85)
+ minimapGroup.label:SetJustifyH("LEFT")
+ minimapGroup.button:SetPoint("LEFT", minimapGroup.label, "RIGHT", 6, 0)
+ optionsFrame.minimapGroup = minimapGroup
+
+ local circleScaleSlider = minimapGroup:AddSlider("CircleScale", "Guide Circle Size", 0.5, 2.0, 0.1,
+ "Adjusts the size of the minimap guide circle and arrow that appears when tracking a map pin.",
+ nil, 1.0)
+ circleScaleSlider:SetValue(EasyFind.db.guideCircleScale or 1.0)
+ circleScaleSlider:HookScript("OnValueChanged", function(self, value)
+ EasyFind.db.guideCircleScale = value
+ end)
+ optionsFrame.circleScaleSlider = circleScaleSlider
+
+ local automationGroup = CreateMultiSelectDropdown(sec2, "Automation", {
+ { label = "Auto Track", shortLabel = "Track", dbKey = "autoTrackPins", default = true,
+ tooltip = "Placing a map pin (Ctrl+Click) automatically starts tracking it on the minimap.\nWhen disabled, you must click the pin to start tracking." },
+ { label = "Auto Clear", shortLabel = "Clear", dbKey = "autoPinClear", default = true,
+ tooltip = "Your map pin is automatically cleared when you arrive at the destination.\nDisable if you prefer to clear pins manually." },
+ }, nil, FLYOUT_W)
+ automationGroup:SetPoint("TOPLEFT", minimapGroup, "BOTTOMLEFT", 0, -6)
+ automationGroup:SetPoint("RIGHT", sec2, "RIGHT", -8, 0)
+ automationGroup.label:SetWidth(85)
+ automationGroup.label:SetJustifyH("LEFT")
+ automationGroup.button:SetPoint("LEFT", automationGroup.label, "RIGHT", 6, 0)
+ optionsFrame.automationGroup = automationGroup
+
+ local arrivalSlider = automationGroup:AddSlider("ArrivalDist", "Arrival Distance", 3, 50, 1,
"How close (in yards) you must be to a tracked location before the waypoint auto-clears.",
function(val) return tostring(mfloor(val + 0.5)) .. "yd" end, 10, "yd")
- arrivalSlider:SetPoint("TOPLEFT", mapIconSlider, "BOTTOMLEFT", 0, -38)
arrivalSlider:SetValue(EasyFind.db.arrivalDistance or 10)
arrivalSlider:HookScript("OnValueChanged", function(self, value)
value = mfloor(value + 0.5)
@@ -936,90 +1117,29 @@ function Options:Initialize()
end)
optionsFrame.arrivalSlider = arrivalSlider
- local circleScaleSlider = CreateSlider(sec2, "CircleScale", "Guide Circle Size", 0.5, 2.0, 0.1,
- "Adjusts the size of the minimap guide circle and arrow that appears when tracking a map pin.",
- nil, 1.0)
- circleScaleSlider:SetPoint("TOPLEFT", arrivalSlider, "BOTTOMLEFT", 0, -38)
- circleScaleSlider:SetValue(EasyFind.db.guideCircleScale or 1.0)
- circleScaleSlider:HookScript("OnValueChanged", function(self, value)
- EasyFind.db.guideCircleScale = value
- end)
- optionsFrame.circleScaleSlider = circleScaleSlider
+ -- Border around Auto Clear + Arrival Distance to show they're linked
+ local clearGroup = CreateFrame("Frame", nil, automationGroup.flyout, "BackdropTemplate")
+ local autoClearRow = automationGroup.checkRows[2] -- Auto Clear is second
+ clearGroup:SetPoint("TOPLEFT", autoClearRow, "TOPLEFT", -4, 4)
+ clearGroup:SetPoint("RIGHT", automationGroup.flyout, "RIGHT", -4, 0)
+ clearGroup:SetPoint("BOTTOM", automationGroup.flyout, "BOTTOM", 0, 3)
+ clearGroup:SetBackdrop({
+ edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
+ edgeSize = 10,
+ insets = { left = 2, right = 2, top = 2, bottom = 2 },
+ })
+ clearGroup:SetBackdropBorderColor(0.4, 0.4, 0.4, 0.5)
- local blinkingPinsCheckbox = CreateCheckbox(sec2, "BlinkingPins", "Blinking Map Pins",
- "When enabled, map search pins and highlight boxes pulse in sync with the indicator arrow.\n\nWhen disabled (default), pins and highlights are steady. The indicator arrow always bobs.")
- blinkingPinsCheckbox:SetPoint("TOPLEFT", mapResultsAboveCheckbox, "BOTTOMLEFT", 0, -4)
- blinkingPinsCheckbox:SetChecked(EasyFind.db.blinkingPins or false)
- blinkingPinsCheckbox:SetScript("OnClick", function(self)
- EasyFind.db.blinkingPins = self:GetChecked()
- if ns.MapSearch and ns.MapSearch.UpdateBlinkingPins then
- ns.MapSearch:UpdateBlinkingPins()
- end
- end)
- optionsFrame.blinkingPinsCheckbox = blinkingPinsCheckbox
-
- local pinHighlightCheckbox = CreateCheckbox(sec2, "PinHighlight", "Pin Highlight Box",
- "When enabled, a yellow highlight box is drawn around map search pins.\n\nDisable to show only the pin icon and indicator arrow.")
- pinHighlightCheckbox:SetPoint("TOPLEFT", blinkingPinsCheckbox, "BOTTOMLEFT", 0, -4)
- pinHighlightCheckbox:SetChecked(EasyFind.db.mapPinHighlight ~= false)
- pinHighlightCheckbox:SetScript("OnClick", function(self)
- EasyFind.db.mapPinHighlight = self:GetChecked()
- if ns.MapSearch and ns.MapSearch.UpdatePinHighlight then
- ns.MapSearch:UpdatePinHighlight()
- end
- end)
- optionsFrame.pinHighlightCheckbox = pinHighlightCheckbox
-
- local arrowGlowCheckbox = CreateCheckbox(sec2, "ArrowGlow", "Minimap Arrow Glow",
- "When enabled, a pulsing glow highlights the minimap perimeter arrow that points toward your active map pin.\n\nDisable if you find the glow distracting.")
- arrowGlowCheckbox:SetPoint("TOPLEFT", pinHighlightCheckbox, "BOTTOMLEFT", 0, -4)
- arrowGlowCheckbox:SetChecked(EasyFind.db.minimapArrowGlow ~= false)
- arrowGlowCheckbox:SetScript("OnClick", function(self)
- EasyFind.db.minimapArrowGlow = self:GetChecked()
- end)
- optionsFrame.arrowGlowCheckbox = arrowGlowCheckbox
-
- local guideCircleCheckbox = CreateCheckbox(sec2, "GuideCircle", "Minimap Guide Circle",
- "When enabled, a directional ring and arrow appears around your character on the minimap when a map pin is nearby, pointing toward the destination.\n\nDisable if you prefer only the default minimap pin.")
- guideCircleCheckbox:SetPoint("TOPLEFT", arrowGlowCheckbox, "BOTTOMLEFT", 0, -4)
- guideCircleCheckbox:SetChecked(EasyFind.db.minimapGuideCircle ~= false)
- guideCircleCheckbox:SetScript("OnClick", function(self)
- EasyFind.db.minimapGuideCircle = self:GetChecked()
- end)
- optionsFrame.guideCircleCheckbox = guideCircleCheckbox
-
- local autoPinClearCheckbox = CreateCheckbox(sec2, "AutoPinClear", "Auto Map Pin Clear",
- "When enabled, your map pin is automatically cleared when you arrive at the destination.\n\nDisable if you prefer to clear pins manually.")
- autoPinClearCheckbox:SetPoint("TOPLEFT", guideCircleCheckbox, "BOTTOMLEFT", 0, -4)
- autoPinClearCheckbox:SetChecked(EasyFind.db.autoPinClear ~= false)
- autoPinClearCheckbox:SetScript("OnClick", function(self)
- EasyFind.db.autoPinClear = self:GetChecked()
- end)
- optionsFrame.autoPinClearCheckbox = autoPinClearCheckbox
-
- local autoTrackCheckbox = CreateCheckbox(sec2, "AutoTrack", "Auto Track Map Pins",
- "When enabled, placing a map pin (Ctrl+Click) automatically starts tracking it on the minimap.\n\nWhen disabled, you must click the pin to start tracking.")
- autoTrackCheckbox:SetPoint("TOPLEFT", autoPinClearCheckbox, "BOTTOMLEFT", 0, -4)
- autoTrackCheckbox:SetChecked(EasyFind.db.autoTrackPins ~= false)
- autoTrackCheckbox:SetScript("OnClick", function(self)
- EasyFind.db.autoTrackPins = self:GetChecked()
- end)
- optionsFrame.autoTrackCheckbox = autoTrackCheckbox
-
- local pinGlowCheckbox = CreateCheckbox(sec2, "PinGlow", "Map Pin Glow",
- "When enabled, a pulsing glow appears on the minimap pin when the guide circle shrinks onto it.\n\nDisable if you find the glow distracting.")
- pinGlowCheckbox:SetPoint("TOPLEFT", autoTrackCheckbox, "BOTTOMLEFT", 0, -4)
- pinGlowCheckbox:SetChecked(EasyFind.db.minimapPinGlow ~= false)
- pinGlowCheckbox:SetScript("OnClick", function(self)
- EasyFind.db.minimapPinGlow = self:GetChecked()
- end)
- optionsFrame.pinGlowCheckbox = pinGlowCheckbox
+ -- Gray out Arrival Distance slider when Auto Clear is unchecked
+ local function UpdateArrivalSliderState()
+ SetControlsEnabled({arrivalSlider}, EasyFind.db.autoPinClear ~= false)
+ end
+ UpdateArrivalSliderState()
+ autoClearRow:HookScript("OnClick", UpdateArrivalSliderState)
mapControls = {
- mapOptionsBox, resizeMapBtn, mapFontSlider, mapIconSlider, arrivalSlider, circleScaleSlider,
- mapSmartShowCheckbox, hideMaxCheckbox, zoneNavCheckbox, mapResultsAboveCheckbox,
- blinkingPinsCheckbox, pinHighlightCheckbox, arrowGlowCheckbox, guideCircleCheckbox,
- autoPinClearCheckbox, autoTrackCheckbox, pinGlowCheckbox
+ mapOptionsBox, resizeMapBtn, zoneNavCheckbox,
+ searchBarGroup, mapPinGroup, minimapGroup, automationGroup
}
UpdateMapToggleVisual()
@@ -1061,9 +1181,6 @@ function Options:Initialize()
.. "|cFFFFD100Other:|r\n"
.. "|cFF00FF00Shift+Drag|r Reposition search bars\n"
.. "|cFF00FF00Right-click|r a result to pin/unpin it\n\n"
- .. "|cFFFFD100Speed options:|r Enable |cFF00FF00Open Panels Directly|r (UI Search) to skip step-by-step "
- .. "guides and jump straight to the destination. Enable |cFF00FF00Navigate to Zones Directly|r "
- .. "(Map Search) to open map zones in one click instead of highlighting them first."
)
-- Keybind buttons
local KEYBIND_ROW_H = 28
@@ -1072,32 +1189,23 @@ function Options:Initialize()
local keybindDefs = {
{ label = "Toggle Bar", action = "EASYFIND_TOGGLE" },
{ label = "Focus Bar", action = "EASYFIND_FOCUS" },
- { label = "Toggle+Focus", action = "EASYFIND_TOGGLE_FOCUS" },
{ label = "Clear All", action = "EASYFIND_CLEAR" },
}
local keybindTooltips = {
EASYFIND_TOGGLE = { "Toggle Search Bar", "Shows or hides the main search bar." },
EASYFIND_FOCUS = { "Focus Search Bar", "Places the cursor in the search bar without toggling visibility." },
- EASYFIND_TOGGLE_FOCUS = { "Toggle + Focus", "Opens and focuses the search bar in one press. When the map is open, focuses the local map search bar instead." },
EASYFIND_CLEAR = { "Clear All", "Dismisses all active highlights, map pins, zone highlights, and pending waypoints." },
}
local keybindButtons = {}
- local KEYBIND_LABEL_W = 105
+ local KEYBIND_LABEL_W = 95
local keybindAnchor = shortcutText
local keybindRowLabels = {}
for i, def in ipairs(keybindDefs) do
- local col = (i - 1) % 2
- local row = math.floor((i - 1) / 2)
-
local rowLabel = sec4:CreateFontString(nil, "OVERLAY", "GameFontNormal")
- if col == 0 then
- rowLabel:SetPoint("TOPLEFT", row == 0 and keybindAnchor or keybindRowLabels[i - 2],
- "BOTTOMLEFT", 0, row == 0 and -12 or -6)
- else
- rowLabel:SetPoint("LEFT", keybindRowLabels[i - 1], "LEFT", COL_RIGHT - 14, 0)
- end
+ rowLabel:SetPoint("TOPLEFT", i == 1 and keybindAnchor or keybindRowLabels[i - 1],
+ "BOTTOMLEFT", 0, i == 1 and -12 or -8)
rowLabel:SetText(def.label .. ":")
keybindRowLabels[i] = rowLabel
@@ -1128,14 +1236,14 @@ function Options:Initialize()
end
optionsFrame.keybindBtn = keybindButtons["EASYFIND_TOGGLE"]
optionsFrame.focusBtn = keybindButtons["EASYFIND_FOCUS"]
- optionsFrame.toggleFocusBtn = keybindButtons["EASYFIND_TOGGLE_FOCUS"]
+
optionsFrame.clearBtn = keybindButtons["EASYFIND_CLEAR"]
-- Auto-size section to text + keybind buttons
C_Timer.After(0, function()
local textH = shortcutText:GetStringHeight()
if textH and textH > 0 then
- local keybindRows = math.ceil(#keybindDefs / 2)
+ local keybindRows = #keybindDefs
local keybindAreaH = 12 + keybindRows * KEYBIND_ROW_H
local newH = textH + keybindAreaH + 16
sec4:SetHeight(newH)
@@ -1228,43 +1336,42 @@ function Options:Initialize()
preferredIndex = 3,
}
- -- Bottom buttons: 4 evenly spaced across 570px frame
- -- 4 x 115px buttons + 5 x 22px gaps = 570px
- local BTN_W = 115
- local BTN_GAP = (FRAME_W - BTN_W * 4) / 5
+ -- Bottom buttons: 2x2 grid
+ local BTN_W = (FRAME_W - 36) / 2
+ local BTN_ROW_GAP = 6
local resetAllBtn = CreateFrame("Button", nil, optionsFrame, "UIPanelButtonTemplate")
resetAllBtn:SetSize(BTN_W, 22)
- resetAllBtn:SetPoint("BOTTOMLEFT", optionsFrame, "BOTTOMLEFT", BTN_GAP, 18)
- resetAllBtn:SetText("Reset All Settings")
+ resetAllBtn:SetPoint("BOTTOMLEFT", optionsFrame, "BOTTOMLEFT", 12, 18 + 22 + BTN_ROW_GAP)
+ resetAllBtn:SetText("Reset Settings")
resetAllBtn:SetScript("OnClick", function()
StaticPopup_Show("EASYFIND_RESET_ALL")
end)
+ local resetPosBtn = CreateFrame("Button", nil, optionsFrame, "UIPanelButtonTemplate")
+ resetPosBtn:SetSize(BTN_W, 22)
+ resetPosBtn:SetPoint("LEFT", resetAllBtn, "RIGHT", 12, 0)
+ resetPosBtn:SetText("Reset Positions")
+ resetPosBtn:SetScript("OnClick", function()
+ StaticPopup_Show("EASYFIND_RESET_POSITIONS")
+ end)
+
local bugBtn = CreateFrame("Button", nil, optionsFrame, "UIPanelButtonTemplate")
bugBtn:SetSize(BTN_W, 22)
- bugBtn:SetPoint("LEFT", resetAllBtn, "RIGHT", BTN_GAP, 0)
- bugBtn:SetText("Report a Bug")
+ bugBtn:SetPoint("BOTTOMLEFT", optionsFrame, "BOTTOMLEFT", 12, 18)
+ bugBtn:SetText("Report Bug")
bugBtn:SetScript("OnClick", function()
EasyFind:OpenBugReport()
end)
local featureBtn = CreateFrame("Button", nil, optionsFrame, "UIPanelButtonTemplate")
featureBtn:SetSize(BTN_W, 22)
- featureBtn:SetPoint("LEFT", bugBtn, "RIGHT", BTN_GAP, 0)
- featureBtn:SetText("Request a Feature")
+ featureBtn:SetPoint("LEFT", bugBtn, "RIGHT", 12, 0)
+ featureBtn:SetText("Request Feature")
featureBtn:SetScript("OnClick", function()
EasyFind:OpenFeatureRequest()
end)
- local resetPosBtn = CreateFrame("Button", nil, optionsFrame, "UIPanelButtonTemplate")
- resetPosBtn:SetSize(BTN_W, 22)
- resetPosBtn:SetPoint("LEFT", featureBtn, "RIGHT", BTN_GAP, 0)
- resetPosBtn:SetText("Reset Positions")
- resetPosBtn:SetScript("OnClick", function()
- StaticPopup_Show("EASYFIND_RESET_POSITIONS")
- end)
-
-- Initial layout (all collapsed)
RelayoutSections()
@@ -1394,19 +1501,13 @@ function Options:DoResetAll()
optionsFrame.zoneNavCheckbox:SetChecked(false)
optionsFrame.smartShowCheckbox:SetChecked(false)
optionsFrame.staticOpacityCheckbox:SetChecked(false)
- optionsFrame.blinkingPinsCheckbox:SetChecked(false)
- optionsFrame.pinHighlightCheckbox:SetChecked(true)
optionsFrame.loginMessageCheckbox:SetChecked(true)
optionsFrame.uiResultsAboveCheckbox:SetChecked(false)
- optionsFrame.mapResultsAboveCheckbox:SetChecked(false)
optionsFrame.minimapBtnCheckbox:SetChecked(true)
- optionsFrame.arrowGlowCheckbox:SetChecked(true)
- optionsFrame.guideCircleCheckbox:SetChecked(true)
- optionsFrame.autoPinClearCheckbox:SetChecked(true)
- optionsFrame.autoTrackCheckbox:SetChecked(true)
- optionsFrame.pinGlowCheckbox:SetChecked(true)
- optionsFrame.mapSmartShowCheckbox:SetChecked(false)
- optionsFrame.hideMaxCheckbox:SetChecked(false)
+ optionsFrame.searchBarGroup:UpdateVisuals()
+ optionsFrame.mapPinGroup:UpdateVisuals()
+ optionsFrame.minimapGroup:UpdateVisuals()
+ optionsFrame.automationGroup:UpdateVisuals()
if optionsFrame.UpdateUIToggleVisual then optionsFrame.UpdateUIToggleVisual() end
if optionsFrame.UpdateMapToggleVisual then optionsFrame.UpdateMapToggleVisual() end
optionsFrame.arrivalSlider:SetValue(10)
@@ -1419,7 +1520,6 @@ function Options:DoResetAll()
optionsFrame.colorSwatch:SetColorTexture(defaultRGB[1], defaultRGB[2], defaultRGB[3], 1)
optionsFrame.keybindBtn:SetText("Not Bound")
optionsFrame.focusBtn:SetText("Not Bound")
- optionsFrame.toggleFocusBtn:SetText("Not Bound")
if _G["EasyFindSearchFrame"] and ns.UI then
if ns.UI.ResetPosition then ns.UI:ResetPosition() end
@@ -1508,14 +1608,13 @@ function Options:Show()
optionsFrame.zoneNavCheckbox:SetChecked(EasyFind.db.navigateToZonesDirectly or false)
optionsFrame.smartShowCheckbox:SetChecked(EasyFind.db.smartShow or false)
optionsFrame.staticOpacityCheckbox:SetChecked(EasyFind.db.staticOpacity or false)
- optionsFrame.blinkingPinsCheckbox:SetChecked(EasyFind.db.blinkingPins or false)
- optionsFrame.pinHighlightCheckbox:SetChecked(EasyFind.db.mapPinHighlight ~= false)
optionsFrame.loginMessageCheckbox:SetChecked(EasyFind.db.showLoginMessage ~= false)
optionsFrame.uiResultsAboveCheckbox:SetChecked(EasyFind.db.uiResultsAbove or false)
- optionsFrame.mapResultsAboveCheckbox:SetChecked(EasyFind.db.mapResultsAbove or false)
- optionsFrame.mapSmartShowCheckbox:SetChecked(EasyFind.db.mapSmartShow or false)
- optionsFrame.hideMaxCheckbox:SetChecked(EasyFind.db.hideSearchBarsMaximized or false)
optionsFrame.minimapBtnCheckbox:SetChecked(EasyFind.db.showMinimapButton or false)
+ optionsFrame.searchBarGroup:UpdateVisuals()
+ optionsFrame.mapPinGroup:UpdateVisuals()
+ optionsFrame.minimapGroup:UpdateVisuals()
+ optionsFrame.automationGroup:UpdateVisuals()
optionsFrame.themeBtnText:SetText(EasyFind.db.resultsTheme or "Retail")
optionsFrame.indicatorBtnText:SetText(EasyFind.db.indicatorStyle or "EasyFind Arrow")
local clr = EasyFind.db.indicatorColor or "Yellow"
@@ -1528,8 +1627,6 @@ function Options:Show()
optionsFrame.keybindBtn:SetText(key1 or "Not Bound")
local key2 = GetBindingKey("EASYFIND_FOCUS")
optionsFrame.focusBtn:SetText(key2 or "Not Bound")
- local key3 = GetBindingKey("EASYFIND_TOGGLE_FOCUS")
- optionsFrame.toggleFocusBtn:SetText(key3 or "Not Bound")
local key4 = GetBindingKey("EASYFIND_CLEAR")
optionsFrame.clearBtn:SetText(key4 or "Not Bound")
From e7a1f1573f5e2e7c4047d061625719221bc1e71b Mon Sep 17 00:00:00 2001
From: wowaddonmaker
Date: Mon, 16 Mar 2026 03:19:21 -0400
Subject: [PATCH 03/10] Redesign options panel
---
Core.lua | 5 +-
Options.lua | 762 ++++++++++++++++++++++++++++++++++++----------------
2 files changed, 525 insertions(+), 242 deletions(-)
diff --git a/Core.lua b/Core.lua
index 8cd5562..21a438b 100644
--- a/Core.lua
+++ b/Core.lua
@@ -236,7 +236,7 @@ local function OnInitialize()
if ns.UI then ns.UI:Hide() end
elseif msg == "show" then
if ns.UI then ns.UI:Show() end
- elseif msg == "clear" then
+ elseif msg == "c" or msg == "clear" then
EasyFind:ClearAll()
elseif msg:find("^test ") then
-- /ef test Interface\\Path\\To\\Texture
@@ -254,7 +254,7 @@ local function OnInitialize()
sf:SetBackdrop(nil)
EasyFind:Print("Border hidden - /reload to restore")
end
- elseif msg == "reset" then
+ elseif msg == "r" or msg == "reset" then
if ns.Options then
ns.Options:Initialize()
StaticPopup_Show("EASYFIND_RESET_ALL")
@@ -400,7 +400,6 @@ function EasyFind:ClearAll()
ns.MapSearch:ClearZoneHighlight()
ns.MapSearch.pendingWaypoint = nil
end
- EasyFind:Print("Active highlights cleared.")
end
function EasyFind:StartGuide(guideData)
diff --git a/Options.lua b/Options.lua
index 40daa73..b6c7450 100644
--- a/Options.lua
+++ b/Options.lua
@@ -92,7 +92,7 @@ end
-- Helper to create a slider (anchored manually by caller)
local function CreateSlider(parent, name, label, minVal, maxVal, step, tooltipText, formatFunc, defaultValue, unitSuffix)
local slider = CreateFrame("Slider", "EasyFindOptions" .. name .. "Slider", parent, "OptionsSliderTemplate")
- slider:SetWidth(120)
+ slider:SetWidth(160)
slider:SetMinMaxValues(minVal, maxVal)
slider:SetValueStep(step)
slider:SetObeyStepOnDrag(true)
@@ -422,18 +422,17 @@ function Options:Initialize()
if isInitialized then return end
isInitialized = true
- local FRAME_W = 485
+ local FRAME_W = 380
+ local FRAME_H = 380
local COL_LEFT = 4 -- Left column offset within content frames
- local COL_RIGHT = 262 -- Right column offset within content frames
local BTN_OFFSET = 105 -- Label LEFT to button LEFT (aligns selectors/keybinds)
- local HEADER_H = 26
- local HEADER_GAP = 4
- local TITLE_Y = -50 -- Y where first section header starts
- local BOTTOM_PAD = 116 -- Space for separator, tips, 2x2 reset buttons
+ local TAB_Y = -42 -- Y where tab buttons start
+ local CONTENT_Y = -68 -- Y where tab content starts (below tab bar)
+ local CONTENT_H = 294 -- Fixed content area height
- -- Create the main options frame
+ -- Create the main options frame (fixed size)
optionsFrame = CreateFrame("Frame", "EasyFindOptionsFrame", UIParent, "BackdropTemplate")
- optionsFrame:SetSize(FRAME_W, 250)
+ optionsFrame:SetSize(FRAME_W, FRAME_H)
if EasyFind.db.optionsPosition then
local pos = EasyFind.db.optionsPosition
optionsFrame:SetPoint(pos[1], UIParent, pos[2], pos[3], pos[4])
@@ -458,7 +457,6 @@ function Options:Initialize()
insets = { left = 4, right = 4, top = 4, bottom = 4 },
})
optionsFrame:SetBackdropBorderColor(0.50, 0.48, 0.45, 1.0)
- optionsFrame:SetClipsChildren(true)
local bgTex = optionsFrame:CreateTexture(nil, "BACKGROUND", nil, -1)
bgTex:SetPoint("TOPLEFT", 4, -4)
@@ -469,98 +467,149 @@ function Options:Initialize()
-- Title
local title = optionsFrame:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
- title:SetPoint("TOP", optionsFrame, "TOP", 0, -20)
+ title:SetPoint("TOP", optionsFrame, "TOP", 0, -16)
title:SetText("EasyFind Options")
-- Close button
local closeBtn = CreateFrame("Button", nil, optionsFrame, "UIPanelCloseButton")
closeBtn:SetPoint("TOPRIGHT", optionsFrame, "TOPRIGHT", -5, -5)
- -- Collapsible section infrastructure
- local sections = {}
- local sep, instructionText -- forward-declared for RelayoutSections
-
- local function RelayoutSections()
- local y = TITLE_Y
- for _, section in ipairs(sections) do
- section.header:ClearAllPoints()
- section.header:SetPoint("TOPLEFT", optionsFrame, "TOPLEFT", 16, y)
- section.header:SetPoint("TOPRIGHT", optionsFrame, "TOPRIGHT", -16, y)
- y = y - HEADER_H
- if section.expanded then
- y = y - section.contentHeight
- end
- y = y - HEADER_GAP
- end
- -- Position bottom elements below last section
- if sep then
- sep:ClearAllPoints()
- sep:SetPoint("TOPLEFT", optionsFrame, "TOPLEFT", 20, y - 2)
- sep:SetPoint("TOPRIGHT", optionsFrame, "TOPRIGHT", -20, y - 2)
+ -- Content border (all tabs render inside this)
+ local contentBorder = CreateFrame("Frame", nil, optionsFrame, "BackdropTemplate")
+ contentBorder:SetPoint("TOPLEFT", optionsFrame, "TOPLEFT", 12, CONTENT_Y)
+ contentBorder:SetPoint("TOPRIGHT", optionsFrame, "TOPRIGHT", -12, CONTENT_Y)
+ contentBorder:SetHeight(CONTENT_H)
+ contentBorder:SetBackdrop({
+ edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
+ edgeSize = 14,
+ insets = { left = 3, right = 3, top = 3, bottom = 3 },
+ })
+ contentBorder:SetBackdropBorderColor(0.5, 0.5, 0.5, 0.6)
+
+ -- Tab system using WoW standard tab atlases (flipped for top attachment)
+ local tabFrames = {}
+ local tabButtons = {}
+ local activeTabIndex
+
+ -- Flip a texture vertically for top-attached tabs
+ local function FlipV(tex)
+ tex:SetTexCoord(0, 1, 1, 0)
+ end
+
+ local function SetTabActive(btn, active)
+ if active then
+ btn.leftActive:Show(); btn.midActive:Show(); btn.rightActive:Show()
+ btn.leftInactive:Hide(); btn.midInactive:Hide(); btn.rightInactive:Hide()
+ btn.leftHl:Hide(); btn.midHl:Hide(); btn.rightHl:Hide()
+ btn:SetHeight(32)
+ btn:GetFontString():SetTextColor(1.0, 0.82, 0.0)
+ else
+ btn.leftActive:Hide(); btn.midActive:Hide(); btn.rightActive:Hide()
+ btn.leftInactive:Show(); btn.midInactive:Show(); btn.rightInactive:Show()
+ btn.leftHl:Show(); btn.midHl:Show(); btn.rightHl:Show()
+ btn:SetHeight(28)
+ btn:GetFontString():SetTextColor(0.8, 0.8, 0.8)
end
- if instructionText then
- instructionText:ClearAllPoints()
- instructionText:SetPoint("TOPLEFT", optionsFrame, "TOPLEFT", 22, y - 10)
+ end
+
+ local function SwitchToTab(index)
+ for i, tf in ipairs(tabFrames) do
+ tf:SetShown(i == index)
+ SetTabActive(tabButtons[i], i == index)
end
- -- Adjust frame height
- local totalH = -y + BOTTOM_PAD
- optionsFrame:SetHeight(totalH)
+ activeTabIndex = index
end
- optionsFrame.RelayoutSections = RelayoutSections
-
- local function CreateSection(sectionTitle, contentHeight)
- local section = { expanded = false, contentHeight = contentHeight }
-
- -- Header button (full width clickable bar)
- local header = CreateFrame("Button", nil, optionsFrame)
- header:SetHeight(HEADER_H)
-
- -- Background using quest log tab atlas
- local bg = header:CreateTexture(nil, "BACKGROUND")
- bg:SetAllPoints()
- bg:SetAtlas("QuestLog-tab")
-
- -- Hover highlight
- local hl = header:CreateTexture(nil, "HIGHLIGHT")
- hl:SetAllPoints()
- hl:SetAtlas("QuestLog-tab")
- hl:SetBlendMode("ADD")
- hl:SetAlpha(0.3)
-
- -- Section title text
- local text = header:CreateFontString(nil, "OVERLAY", "GameFontNormal")
- text:SetPoint("LEFT", header, "LEFT", 10, 0)
- text:SetText(sectionTitle)
-
- -- Expand/collapse icon
- local icon = header:CreateTexture(nil, "ARTWORK")
- icon:SetSize(18, 17)
- icon:SetPoint("RIGHT", header, "RIGHT", -8, 0)
- icon:SetAtlas("QuestLog-icon-expand")
-
- section.header = header
- section.icon = icon
-
- -- Content frame (hidden by default)
- local content = CreateFrame("Frame", nil, optionsFrame)
- content:SetPoint("TOPLEFT", header, "BOTTOMLEFT", 0, 0)
- content:SetPoint("TOPRIGHT", header, "BOTTOMRIGHT", 0, 0)
- content:SetHeight(contentHeight)
+ optionsFrame.SwitchToTab = SwitchToTab
+
+ local function CreateTab(tabName)
+ local index = #tabFrames + 1
+
+ local btn = CreateFrame("Button", nil, optionsFrame)
+ btn:SetNormalFontObject("GameFontHighlightSmall")
+ btn:SetText(tabName)
+
+ local textW = btn:GetFontString():GetStringWidth()
+ local tabW = mmax(textW + 28, 48)
+ btn:SetSize(tabW, 28)
+
+ -- Force center text within tab
+ local fs = btn:GetFontString()
+ fs:ClearAllPoints()
+ fs:SetPoint("CENTER", btn, "CENTER", -2, 0)
+ fs:SetJustifyH("CENTER")
+
+ -- Active state textures (flipped vertically for top attachment)
+ local la = btn:CreateTexture(nil, "BACKGROUND")
+ la:SetAtlas("uiframe-activetab-left"); la:SetSize(35, 32)
+ la:SetPoint("BOTTOMLEFT", 0, 0); FlipV(la); la:Hide()
+ btn.leftActive = la
+
+ local ma = btn:CreateTexture(nil, "BACKGROUND")
+ ma:SetAtlas("_uiframe-activetab-center")
+ ma:SetPoint("BOTTOMLEFT", la, "BOTTOMRIGHT", 0, 0)
+ ma:SetPoint("BOTTOMRIGHT", btn, "BOTTOMRIGHT", -35, 0)
+ ma:SetHeight(32); FlipV(ma); ma:Hide()
+ btn.midActive = ma
+
+ local ra = btn:CreateTexture(nil, "BACKGROUND")
+ ra:SetAtlas("uiframe-activetab-right"); ra:SetSize(37, 32)
+ ra:SetPoint("BOTTOMRIGHT", 0, 0); FlipV(ra); ra:Hide()
+ btn.rightActive = ra
+
+ -- Inactive state textures (flipped)
+ local li = btn:CreateTexture(nil, "BACKGROUND")
+ li:SetAtlas("uiframe-tab-left"); li:SetSize(35, 28)
+ li:SetPoint("BOTTOMLEFT", 0, 0); FlipV(li)
+ btn.leftInactive = li
+
+ local mi = btn:CreateTexture(nil, "BACKGROUND")
+ mi:SetAtlas("_uiframe-tab-center")
+ mi:SetPoint("BOTTOMLEFT", li, "BOTTOMRIGHT", 0, 0)
+ mi:SetPoint("BOTTOMRIGHT", btn, "BOTTOMRIGHT", -35, 0)
+ mi:SetHeight(28); FlipV(mi)
+ btn.midInactive = mi
+
+ local ri = btn:CreateTexture(nil, "BACKGROUND")
+ ri:SetAtlas("uiframe-tab-right"); ri:SetSize(37, 28)
+ ri:SetPoint("BOTTOMRIGHT", 0, 0); FlipV(ri)
+ btn.rightInactive = ri
+
+ -- Highlight textures (flipped, shown over inactive only)
+ local lh = btn:CreateTexture(nil, "HIGHLIGHT")
+ lh:SetAtlas("uiframe-tab-left"); lh:SetSize(35, 28)
+ lh:SetPoint("BOTTOMLEFT", 0, 0); FlipV(lh); lh:SetAlpha(0.4)
+ btn.leftHl = lh
+
+ local mh = btn:CreateTexture(nil, "HIGHLIGHT")
+ mh:SetAtlas("_uiframe-tab-center")
+ mh:SetPoint("BOTTOMLEFT", lh, "BOTTOMRIGHT", 0, 0)
+ mh:SetPoint("BOTTOMRIGHT", btn, "BOTTOMRIGHT", -35, 0)
+ mh:SetHeight(28); FlipV(mh); mh:SetAlpha(0.4)
+ btn.midHl = mh
+
+ local rh = btn:CreateTexture(nil, "HIGHLIGHT")
+ rh:SetAtlas("uiframe-tab-right"); rh:SetSize(37, 28)
+ rh:SetPoint("BOTTOMRIGHT", 0, 0); FlipV(rh); rh:SetAlpha(0.4)
+ btn.rightHl = rh
+
+ -- Position: tabs sit above the content border, bottom edges touching it
+ if index == 1 then
+ btn:SetPoint("BOTTOMLEFT", contentBorder, "TOPLEFT", 5, -2)
+ else
+ btn:SetPoint("BOTTOMLEFT", tabButtons[index - 1], "BOTTOMRIGHT", -8, 0)
+ end
+ btn:SetScript("OnClick", function() SwitchToTab(index) end)
+ tinsert(tabButtons, btn)
+
+ -- Content frame inside the border
+ local content = CreateFrame("Frame", nil, contentBorder)
+ content:SetPoint("TOPLEFT", 6, -6)
+ content:SetPoint("BOTTOMRIGHT", -6, 6)
content:Hide()
- section.content = content
-
- -- Toggle on click
- header:SetScript("OnClick", function()
- section.expanded = not section.expanded
- icon:SetAtlas(section.expanded and "QuestLog-icon-shrink" or "QuestLog-icon-expand")
- content:SetShown(section.expanded)
- RelayoutSections()
- end)
+ tinsert(tabFrames, content)
- tinsert(sections, section)
return content
end
- optionsFrame.sections = sections
-- Keybind helpers (defined early since Section 4 needs them)
local function GetCurrentKeybindText(action)
@@ -606,7 +655,7 @@ function Options:Initialize()
SetBinding(combo, action)
SaveBindings(GetCurrentBindingSet())
StopCapture(self, action)
- EasyFind:Print("Keybind set to: " .. combo)
+
end)
end
end
@@ -615,30 +664,77 @@ function Options:Initialize()
keybindBtn:SetScript("OnEnter", function(self)
GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
GameTooltip:SetText(titleText)
- GameTooltip:AddLine(line1, 1, 1, 1)
- GameTooltip:AddLine("Right-click to clear. Escape to cancel.", 0.7, 0.7, 0.7)
+ GameTooltip:AddLine(line1, 1, 1, 1, true)
+ GameTooltip:AddLine("Right-click to clear. Escape to cancel.", 0.7, 0.7, 0.7, true)
GameTooltip:Show()
end)
keybindBtn:SetScript("OnLeave", GameTooltip_Hide)
end
-- SECTION 1: General
- local sec3 = CreateSection("General", 182)
+ -- HOME TAB
+ local homeTab = CreateTab("Home")
+ local homeIcon = homeTab:CreateTexture(nil, "ARTWORK")
+ homeIcon:SetSize(48, 48)
+ homeIcon:SetPoint("TOPLEFT", homeTab, "TOPLEFT", 12, -16)
+ homeIcon:SetTexture("Interface\\MINIMAP\\Tracking\\None")
+
+ local homeTitle = homeTab:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
+ homeTitle:SetPoint("LEFT", homeIcon, "RIGHT", 12, 6)
+ homeTitle:SetText("EasyFind")
+
+ local homeVersion = homeTab:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
+ homeVersion:SetPoint("LEFT", homeTitle, "RIGHT", 6, 0)
+ local tocVersion = C_AddOns and C_AddOns.GetAddOnMetadata and C_AddOns.GetAddOnMetadata("EasyFind", "Version")
+ homeVersion:SetText("|cFF888888v" .. (tocVersion or "") .. "|r")
+
+ local homeDesc = homeTab:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
+ homeDesc:SetPoint("TOPLEFT", homeIcon, "BOTTOMLEFT", 0, -12)
+ homeDesc:SetPoint("RIGHT", homeTab, "RIGHT", -12, 0)
+ homeDesc:SetJustifyH("LEFT")
+ homeDesc:SetSpacing(3)
+ homeDesc:SetText(
+ "|cFFFFD100Quick start:|r Type in the search bar to find UI panels, map locations, "
+ .. "and more. Click a result to navigate there.\n\n"
+ .. "Use the |cFFFFD100filter button|r on the search bar to add mounts, toys, pets, and "
+ .. "map results to your searches. The world map also has its own search bars "
+ .. "for finding zones, dungeons, and points of interest directly on the map.\n\n"
+ .. "|cFFFFD100Slash commands:|r\n"
+ .. " |cFF00FF00/ef o|r Open this options panel\n"
+ .. " |cFF00FF00/ef c|r Dismiss all highlights and pins\n\n"
+ .. "For a full walkthrough, see the CurseForge page:"
+ )
- local genOptionsBox = CreateFrame("Frame", nil, sec3, "BackdropTemplate")
- genOptionsBox:SetPoint("TOPLEFT", sec3, "TOPLEFT", 2, -4)
- genOptionsBox:SetPoint("TOPRIGHT", sec3, "TOPRIGHT", -2, -4)
- genOptionsBox:SetPoint("BOTTOM", sec3, "BOTTOM", 0, 4)
- genOptionsBox:SetBackdrop({
- edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
- edgeSize = 14,
- insets = { left = 3, right = 3, top = 3, bottom = 3 },
- })
- genOptionsBox:SetBackdropBorderColor(0.5, 0.5, 0.5, 0.6)
+ local thankYou = optionsFrame:CreateFontString(nil, "OVERLAY", "GameFontDisableSmall")
+ thankYou:SetPoint("BOTTOMRIGHT", optionsFrame, "BOTTOMRIGHT", -16, 6)
+ thankYou:SetText("Thanks for trying EasyFind!")
+
+ local function CreateURLBox(parent, url, anchor, yOff)
+ local box = CreateFrame("EditBox", nil, parent, "InputBoxTemplate")
+ box:SetSize(FRAME_W - 60, 18)
+ box:SetPoint("TOPLEFT", anchor, "BOTTOMLEFT", 0, yOff)
+ box:SetAutoFocus(false)
+ box:SetFontObject("GameFontHighlightSmall")
+ box:SetText(url)
+ box:SetCursorPosition(0)
+ box:SetScript("OnEscapePressed", function(self) self:ClearFocus() end)
+ box:SetScript("OnEditFocusGained", function(self) self:HighlightText() end)
+ box:SetScript("OnTextChanged", function(self) self:SetText(url); self:SetCursorPosition(0) end)
+ box:SetScript("OnMouseUp", function(self)
+ if not self:IsMouseOver() then self:ClearFocus() end
+ end)
+ return box
+ end
+
+ local cfBox = CreateURLBox(homeTab, "https://www.curseforge.com/wow/addons/easyfind", homeDesc, -6)
+
+ local sec3 = CreateTab("General")
+
+ -- General tab layout (no inner border, content fills the tab)
local loginMessageCheckbox = CreateCheckbox(sec3, "LoginMessage", "Show Login Message",
"When enabled, shows a short \"EasyFind loaded!\" message in chat when you log in.\n\nDisable to keep chat cleaner.")
- loginMessageCheckbox:SetPoint("TOPLEFT", genOptionsBox, "TOPLEFT", 16, -10)
+ loginMessageCheckbox:SetPoint("TOPLEFT", sec3, "TOPLEFT", 8, -8)
loginMessageCheckbox:SetChecked(EasyFind.db.showLoginMessage ~= false)
loginMessageCheckbox:SetScript("OnClick", function(self)
EasyFind.db.showLoginMessage = self:GetChecked()
@@ -647,7 +743,7 @@ function Options:Initialize()
local minimapBtnCheckbox = CreateCheckbox(sec3, "MinimapBtn", "Show Minimap Button",
"When enabled, adds a small search icon button to the minimap edge.\n\nLeft-click the button to toggle the search bar.\nRight-click to open options.\nDrag to reposition it around the minimap.")
- minimapBtnCheckbox:SetPoint("TOPLEFT", loginMessageCheckbox, "BOTTOMLEFT", 0, -4)
+ minimapBtnCheckbox:SetPoint("LEFT", loginMessageCheckbox, "RIGHT", 120, 0)
minimapBtnCheckbox:SetChecked(EasyFind.db.showMinimapButton ~= false)
minimapBtnCheckbox:SetScript("OnClick", function(self)
EasyFind.db.showMinimapButton = self:GetChecked()
@@ -656,7 +752,7 @@ function Options:Initialize()
optionsFrame.minimapBtnCheckbox = minimapBtnCheckbox
local indicatorLabel = sec3:CreateFontString(nil, "OVERLAY", "GameFontNormal")
- indicatorLabel:SetPoint("TOPLEFT", minimapBtnCheckbox, "BOTTOMLEFT", 4, -10)
+ indicatorLabel:SetPoint("TOPLEFT", loginMessageCheckbox, "BOTTOMLEFT", 4, -10)
indicatorLabel:SetText("Indicator Style:")
local indicatorChoices = {"EasyFind Arrow", "Classic Quest Arrow", "Minimap Player Arrow", "Low-res Gauntlet", "HD Gauntlet"}
@@ -761,7 +857,7 @@ function Options:Initialize()
local panelOpacitySlider = CreateSlider(sec3, "PanelOpacity", "Options Menu Opacity", 0.3, 1.0, 0.05,
"Adjusts the opacity of the options panel background.", nil, 0.9)
- panelOpacitySlider:SetPoint("TOPLEFT", genOptionsBox, "TOPLEFT", COL_RIGHT - 2, -28)
+ panelOpacitySlider:SetPoint("TOPLEFT", themeLabel, "BOTTOMLEFT", 0, -30)
panelOpacitySlider:SetValue(EasyFind.db.panelOpacity or 0.9)
panelOpacitySlider:HookScript("OnValueChanged", function(self, value)
EasyFind.db.panelOpacity = value
@@ -786,8 +882,10 @@ function Options:Initialize()
end)
optionsFrame.opacitySlider = opacitySlider
+ local RESET_BTN_W = 120
+
-- SECTION 2: UI Search
- local sec1 = CreateSection("UI Search", 180)
+ local sec1 = CreateTab("UI")
local uiEnableCheckbox = CreateCheckbox(sec1, "EnableUI", "Enable UI Search Module",
"Uncheck to disable the main UI search bar.\n\nRequires a UI reload to take effect.")
@@ -813,20 +911,15 @@ function Options:Initialize()
end
end)
- local uiOptionsBox = CreateFrame("Frame", nil, sec1, "BackdropTemplate")
- uiOptionsBox:SetPoint("TOPLEFT", sec1, "TOPLEFT", 2, -34)
- uiOptionsBox:SetPoint("TOPRIGHT", sec1, "TOPRIGHT", -2, -34)
- uiOptionsBox:SetPoint("BOTTOM", sec1, "BOTTOM", 0, 4)
- uiOptionsBox:SetBackdrop({
- edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
- edgeSize = 14,
- insets = { left = 3, right = 3, top = 3, bottom = 3 },
- })
- uiOptionsBox:SetBackdropBorderColor(0.5, 0.5, 0.5, 0.6)
-
+ -- Gold separator under enable checkbox
+ local uiSep = sec1:CreateTexture(nil, "ARTWORK")
+ uiSep:SetPoint("TOPLEFT", sec1, "TOPLEFT", 6, -30)
+ uiSep:SetPoint("RIGHT", sec1, "RIGHT", -6, 0)
+ uiSep:SetHeight(1)
+ uiSep:SetColorTexture(0.8, 0.65, 0.0, 0.6)
local uiSpeedBox = CreateFrame("Frame", nil, sec1, "BackdropTemplate")
- uiSpeedBox:SetPoint("TOPLEFT", uiOptionsBox, "TOPLEFT", 8, -8)
+ uiSpeedBox:SetPoint("TOPLEFT", sec1, "TOPLEFT", 8, -38)
uiSpeedBox:SetSize(210, 36)
uiSpeedBox:SetBackdrop({
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
@@ -861,7 +954,7 @@ function Options:Initialize()
local resizeUIBtn = CreateFrame("Button", nil, sec1, "UIPanelButtonTemplate")
resizeUIBtn:SetSize(160, 22)
- resizeUIBtn:SetPoint("TOPRIGHT", sec1, "TOPRIGHT", -COL_LEFT, -6)
+ resizeUIBtn:SetPoint("BOTTOMLEFT", sec1, "BOTTOMLEFT", 8, 32)
resizeUIBtn:SetText("Resize UI Search")
resizeUIBtn:SetScript("OnClick", function()
if ns.Rescaler then ns.Rescaler:Enter("ui") end
@@ -873,19 +966,7 @@ function Options:Initialize()
end)
resizeUIBtn:SetScript("OnLeave", GameTooltip_Hide)
- local uiFontSlider = CreateSlider(sec1, "UIFontSize", "Font Size|cffff3333*|r", 0.5, 2.0, 0.1,
- "Changing font size also affects search bar height and results window sizing.", nil, 1.0)
- uiFontSlider:SetPoint("TOPLEFT", uiOptionsBox, "TOPLEFT", COL_RIGHT - 2, -28)
- uiFontSlider:SetValue(EasyFind.db.fontSize or 1.0)
- uiFontSlider:HookScript("OnValueChanged", function(self, value)
- EasyFind.db.fontSize = value
- if ns.UI and ns.UI.UpdateFontSize then
- ns.UI:UpdateFontSize()
- end
- end)
- optionsFrame.uiFontSlider = uiFontSlider
-
- local smartShowCheckbox = CreateCheckbox(sec1, "SmartShow", "Smart Show (auto-hide)",
+ local smartShowCheckbox = CreateCheckbox(sec1, "SmartShow", "Smart Show |cFF888888(Recommended)|r",
"When enabled, the UI search bar hides itself until you move your mouse near its position.\n\nThe bar reappears when your mouse enters the area and fades away when you move away.\n\nA separate Smart Show toggle for map search bars is available in the Map Search section.")
smartShowCheckbox:SetPoint("TOPLEFT", uiSpeedBox, "BOTTOMLEFT", 8, -6)
smartShowCheckbox:SetChecked(EasyFind.db.smartShow or false)
@@ -918,11 +999,39 @@ function Options:Initialize()
end)
optionsFrame.uiResultsAboveCheckbox = uiResultsAboveCheckbox
- uiControls = { uiOptionsBox, resizeUIBtn, uiFontSlider, directOpenCheckbox, smartShowCheckbox, staticOpacityCheckbox, uiResultsAboveCheckbox }
+ local uiFontSlider = CreateSlider(sec1, "UIFontSize", "Font Size|cffff3333*|r", 0.5, 2.0, 0.1,
+ "Changing font size also affects search bar height and results window sizing.", nil, 1.0)
+ uiFontSlider:SetPoint("TOPLEFT", uiResultsAboveCheckbox, "BOTTOMLEFT", 4, -20)
+ uiFontSlider:SetValue(EasyFind.db.fontSize or 1.0)
+ uiFontSlider:HookScript("OnValueChanged", function(self, value)
+ EasyFind.db.fontSize = value
+ if ns.UI and ns.UI.UpdateFontSize then
+ ns.UI:UpdateFontSize()
+ end
+ end)
+ optionsFrame.uiFontSlider = uiFontSlider
+
+ local resetUIBtn = CreateFrame("Button", nil, sec1, "UIPanelButtonTemplate")
+ resetUIBtn:SetSize(RESET_BTN_W, 20)
+ resetUIBtn:SetPoint("BOTTOMLEFT", sec1, "BOTTOMLEFT", 8, 8)
+ resetUIBtn:SetText("Reset Settings")
+ resetUIBtn:SetScript("OnClick", function()
+ StaticPopup_Show("EASYFIND_RESET_UI")
+ end)
+
+ local resetUIPosBtn = CreateFrame("Button", nil, sec1, "UIPanelButtonTemplate")
+ resetUIPosBtn:SetSize(RESET_BTN_W, 20)
+ resetUIPosBtn:SetPoint("LEFT", resetUIBtn, "RIGHT", 8, 0)
+ resetUIPosBtn:SetText("Reset Positions")
+ resetUIPosBtn:SetScript("OnClick", function()
+ StaticPopup_Show("EASYFIND_RESET_UI_POS")
+ end)
+
+ uiControls = { resizeUIBtn, resetUIBtn, resetUIPosBtn, uiFontSlider, directOpenCheckbox, smartShowCheckbox, staticOpacityCheckbox, uiResultsAboveCheckbox }
UpdateUIToggleVisual()
-- SECTION 3: Map Search
- local sec2 = CreateSection("Map Search", 210)
+ local sec2 = CreateTab("Map")
local mapEnableCheckbox = CreateCheckbox(sec2, "EnableMap", "Enable Map Search Module",
"Uncheck to disable map search bars, pins, and all map overlay features.\n\nRequires a UI reload to take effect.")
@@ -948,20 +1057,15 @@ function Options:Initialize()
end
end)
- local mapOptionsBox = CreateFrame("Frame", nil, sec2, "BackdropTemplate")
- mapOptionsBox:SetPoint("TOPLEFT", sec2, "TOPLEFT", 2, -34)
- mapOptionsBox:SetPoint("TOPRIGHT", sec2, "TOPRIGHT", -2, -34)
- mapOptionsBox:SetPoint("BOTTOM", sec2, "BOTTOM", 0, 4)
- mapOptionsBox:SetBackdrop({
- edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
- edgeSize = 14,
- insets = { left = 3, right = 3, top = 3, bottom = 3 },
- })
- mapOptionsBox:SetBackdropBorderColor(0.5, 0.5, 0.5, 0.6)
-
+ -- Gold separator under enable checkbox
+ local mapSep = sec2:CreateTexture(nil, "ARTWORK")
+ mapSep:SetPoint("TOPLEFT", sec2, "TOPLEFT", 6, -30)
+ mapSep:SetPoint("RIGHT", sec2, "RIGHT", -6, 0)
+ mapSep:SetHeight(1)
+ mapSep:SetColorTexture(0.8, 0.65, 0.0, 0.6)
local mapSpeedBox = CreateFrame("Frame", nil, sec2, "BackdropTemplate")
- mapSpeedBox:SetPoint("TOPLEFT", mapOptionsBox, "TOPLEFT", 8, -8)
+ mapSpeedBox:SetPoint("TOPLEFT", sec2, "TOPLEFT", 8, -38)
mapSpeedBox:SetSize(210, 36)
mapSpeedBox:SetBackdrop({
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
@@ -984,7 +1088,7 @@ function Options:Initialize()
local resizeMapBtn = CreateFrame("Button", nil, sec2, "UIPanelButtonTemplate")
resizeMapBtn:SetSize(160, 22)
- resizeMapBtn:SetPoint("TOPRIGHT", sec2, "TOPRIGHT", -COL_LEFT, -6)
+ resizeMapBtn:SetPoint("BOTTOMLEFT", sec2, "BOTTOMLEFT", 8, 32)
resizeMapBtn:SetText("Resize Map Search")
resizeMapBtn:SetScript("OnClick", function()
if ns.Rescaler then ns.Rescaler:Enter("map") end
@@ -999,7 +1103,7 @@ function Options:Initialize()
local FLYOUT_W = 260
local searchBarGroup = CreateMultiSelectDropdown(sec2, "Search Bars", {
- { label = "Smart Show", shortLabel = "Smart", dbKey = "mapSmartShow", default = false,
+ { label = "Smart Show |cFF888888(Recommended)|r", shortLabel = "Smart", dbKey = "mapSmartShow", default = false,
tooltip = "Map search bars hide until you move your mouse near them.\nBars reappear on hover and fade when you move away.\nText in search bars or an open results list prevents fading.",
callback = function() if ns.MapSearch and ns.MapSearch.UpdateMapSmartShow then ns.MapSearch:UpdateMapSmartShow() end end },
{ label = "Hide Fullscreen", shortLabel = "No Full", dbKey = "hideSearchBarsMaximized", default = false,
@@ -1137,80 +1241,93 @@ function Options:Initialize()
UpdateArrivalSliderState()
autoClearRow:HookScript("OnClick", UpdateArrivalSliderState)
+ local resetMapBtn = CreateFrame("Button", nil, sec2, "UIPanelButtonTemplate")
+ resetMapBtn:SetSize(RESET_BTN_W, 20)
+ resetMapBtn:SetPoint("BOTTOMLEFT", sec2, "BOTTOMLEFT", 8, 8)
+ resetMapBtn:SetText("Reset Settings")
+ resetMapBtn:SetScript("OnClick", function()
+ StaticPopup_Show("EASYFIND_RESET_MAP")
+ end)
+
+ local resetMapPosBtn = CreateFrame("Button", nil, sec2, "UIPanelButtonTemplate")
+ resetMapPosBtn:SetSize(RESET_BTN_W, 20)
+ resetMapPosBtn:SetPoint("LEFT", resetMapBtn, "RIGHT", 8, 0)
+ resetMapPosBtn:SetText("Reset Positions")
+ resetMapPosBtn:SetScript("OnClick", function()
+ StaticPopup_Show("EASYFIND_RESET_MAP_POS")
+ end)
+
mapControls = {
- mapOptionsBox, resizeMapBtn, zoneNavCheckbox,
+ resizeMapBtn, resetMapBtn, resetMapPosBtn, zoneNavCheckbox,
searchBarGroup, mapPinGroup, minimapGroup, automationGroup
}
UpdateMapToggleVisual()
-- SECTION 4: Keyboard Shortcuts
- local sec4 = CreateSection("Keyboard Shortcuts", 220)
+ local sec4 = CreateTab("Shortcuts")
- local kbOptionsBox = CreateFrame("Frame", nil, sec4, "BackdropTemplate")
- kbOptionsBox:SetPoint("TOPLEFT", sec4, "TOPLEFT", 2, -4)
- kbOptionsBox:SetPoint("TOPRIGHT", sec4, "TOPRIGHT", -2, -4)
- kbOptionsBox:SetPoint("BOTTOM", sec4, "BOTTOM", 0, 4)
- kbOptionsBox:SetBackdrop({
- edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
- edgeSize = 14,
- insets = { left = 3, right = 3, top = 3, bottom = 3 },
- })
- kbOptionsBox:SetBackdropBorderColor(0.5, 0.5, 0.5, 0.6)
+ -- Shortcuts tab layout (no inner border)
local shortcutText = sec4:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
- shortcutText:SetPoint("TOPLEFT", kbOptionsBox, "TOPLEFT", 12, -10)
+ shortcutText:SetPoint("TOPLEFT", sec4, "TOPLEFT", 8, -8)
shortcutText:SetWidth(FRAME_W - 60)
shortcutText:SetJustifyH("LEFT")
shortcutText:SetSpacing(2)
shortcutText:SetText(
- "|cFFFFD100Toggle+Focus|r is the recommended keybind for fast, keyboard-driven searching. "
- .. "It opens the search bar and places the cursor in it. Press it again to close. "
- .. "When the world map is open, it focuses the local map search bar instead.\n\n"
- .. "|cFFFFD100From the search box:|r\n"
+ "|cFFFFD100Search box:|r\n"
.. "|cFF00FF00Down|r Enter results list\n"
- .. "|cFF00FF00Tab / Shift+Tab|r Cycle between search box, clear, and filter buttons\n"
+ .. "|cFF00FF00Tab / Shift+Tab|r Cycle between search/clear/filter buttons\n"
.. "|cFF00FF00Enter|r Activate focused button or highlighted result\n"
.. "|cFF00FF00Escape|r Remove cursor from search bar\n\n"
- .. "|cFFFFD100From the results list:|r\n"
+ .. "|cFFFFD100Results list:|r\n"
.. "|cFF00FF00Up / Down|r Move through results\n"
- .. "|cFF00FF00Tab / Shift+Tab|r Toggle focus between result row and nav button\n"
+ .. "|cFF00FF00Tab / Shift+Tab|r Toggle focus between result/nav button\n"
.. "|cFF00FF00Page Up / Page Down|r Jump 5 results\n"
- .. "|cFF00FF00Home / End / Ctrl+Up / Ctrl+Down|r Jump to first / last result\n"
- .. "|cFF00FF00Shift+Up / Shift+Down|r Jump between result sections (UI, Map, Mounts, etc.)\n"
- .. "|cFF00FF00Ctrl+Tab|r Switch between local and global map search bar\n\n"
+ .. "|cFF00FF00Home / End|r Jump to first / last result\n"
+ .. "|cFF00FF00Shift+Up / Shift+Down|r Jump between sections\n"
+ .. "|cFF00FF00Ctrl+Tab|r Switch local / global map search bar\n\n"
.. "|cFFFFD100Other:|r\n"
.. "|cFF00FF00Shift+Drag|r Reposition search bars\n"
- .. "|cFF00FF00Right-click|r a result to pin/unpin it\n\n"
+ .. "|cFF00FF00Right-click|r a result to pin/unpin it\n"
+ .. "|cFF00FF00/ef show|r |cFF00FF00/ef hide|r Toggle the search bar\n"
)
-- Keybind buttons
- local KEYBIND_ROW_H = 28
- local KEYBIND_BTN_W = 140
+ local KEYBIND_ROW_H = 18
+ local KEYBIND_BTN_W = 80
local keybindDefs = {
{ label = "Toggle Bar", action = "EASYFIND_TOGGLE" },
{ label = "Focus Bar", action = "EASYFIND_FOCUS" },
{ label = "Clear All", action = "EASYFIND_CLEAR" },
+ { label = "Toggle+Foc", action = "EASYFIND_TOGGLE_FOCUS" },
}
local keybindTooltips = {
EASYFIND_TOGGLE = { "Toggle Search Bar", "Shows or hides the main search bar." },
EASYFIND_FOCUS = { "Focus Search Bar", "Places the cursor in the search bar without toggling visibility." },
+ EASYFIND_TOGGLE_FOCUS = { "Toggle + Focus", "Opens and focuses the search bar in one press. Press again to close. When the map is open, focuses the local map search bar instead." },
EASYFIND_CLEAR = { "Clear All", "Dismisses all active highlights, map pins, zone highlights, and pending waypoints." },
}
local keybindButtons = {}
- local KEYBIND_LABEL_W = 95
- local keybindAnchor = shortcutText
- local keybindRowLabels = {}
+ local KEYBIND_LABEL_W = 70
+ local COL2_X = 160
for i, def in ipairs(keybindDefs) do
- local rowLabel = sec4:CreateFontString(nil, "OVERLAY", "GameFontNormal")
- rowLabel:SetPoint("TOPLEFT", i == 1 and keybindAnchor or keybindRowLabels[i - 1],
- "BOTTOMLEFT", 0, i == 1 and -12 or -8)
+ local row = (i - 1) % 2 -- 0 or 1
+ local col = (i <= 2) and 0 or 1 -- left or right column
+
+ local rowLabel = sec4:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
+ if row == 0 then
+ rowLabel:SetPoint("TOPLEFT", shortcutText, "BOTTOMLEFT", col * COL2_X, -12)
+ else
+ rowLabel:SetPoint("TOPLEFT", shortcutText, "BOTTOMLEFT", col * COL2_X, -12 - KEYBIND_ROW_H)
+ end
rowLabel:SetText(def.label .. ":")
- keybindRowLabels[i] = rowLabel
local keybindBtn = CreateFrame("Button", nil, sec4, "UIPanelButtonTemplate")
- keybindBtn:SetSize(KEYBIND_BTN_W, 22)
+ keybindBtn:SetNormalFontObject("GameFontHighlightSmall")
+ keybindBtn:SetHighlightFontObject("GameFontHighlightSmall")
+ keybindBtn:SetSize(KEYBIND_BTN_W, 18)
keybindBtn:SetPoint("LEFT", rowLabel, "LEFT", KEYBIND_LABEL_W, 0)
keybindBtn:SetText(GetCurrentKeybindText(def.action))
keybindBtn:SetScript("OnClick", function(self, button)
@@ -1220,7 +1337,7 @@ function Options:Initialize()
if old2 then SetBinding(old2) end
SaveBindings(GetCurrentBindingSet())
self:SetText("Not Bound")
- EasyFind:Print("Keybind cleared.")
+
else
StartCapture(self, def.action)
end
@@ -1236,35 +1353,11 @@ function Options:Initialize()
end
optionsFrame.keybindBtn = keybindButtons["EASYFIND_TOGGLE"]
optionsFrame.focusBtn = keybindButtons["EASYFIND_FOCUS"]
-
+ optionsFrame.toggleFocusBtn = keybindButtons["EASYFIND_TOGGLE_FOCUS"]
optionsFrame.clearBtn = keybindButtons["EASYFIND_CLEAR"]
- -- Auto-size section to text + keybind buttons
- C_Timer.After(0, function()
- local textH = shortcutText:GetStringHeight()
- if textH and textH > 0 then
- local keybindRows = #keybindDefs
- local keybindAreaH = 12 + keybindRows * KEYBIND_ROW_H
- local newH = textH + keybindAreaH + 16
- sec4:SetHeight(newH)
- for _, section in ipairs(optionsFrame.sections) do
- if section.content == sec4 then
- section.contentHeight = newH
- break
- end
- end
- end
- end)
-
- -- BOTTOM - Separator, Tips, Reset buttons
- sep = optionsFrame:CreateTexture(nil, "ARTWORK")
- sep:SetHeight(1)
- sep:SetColorTexture(0.5, 0.5, 0.5, 0.4)
- instructionText = optionsFrame:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
- instructionText:SetWidth(FRAME_W - 44)
- instructionText:SetJustifyH("LEFT")
- instructionText:SetText("|cFFFFFF00Tips:|r Hold |cFF00FF00Shift|r + drag to reposition bars |cFF888888|||r |cFF00FF00/ef o|r options\n|cFF00FF00/ef show|r |cFF00FF00/ef hide|r toggle bar (or click minimap button) |cFF888888|||r |cFF00FF00/ef clear|r dismiss highlights\n|cFF00FF00/ef reset|r reset all settings (useful if the options panel gets lost off-screen)")
+ -- BOTTOM - Reset buttons (tips moved to Home tab)
StaticPopupDialogs["EASYFIND_RESET_ALL"] = {
text = "Reset all EasyFind settings to defaults?",
@@ -1336,44 +1429,113 @@ function Options:Initialize()
preferredIndex = 3,
}
- -- Bottom buttons: 2x2 grid
- local BTN_W = (FRAME_W - 36) / 2
- local BTN_ROW_GAP = 6
+ StaticPopupDialogs["EASYFIND_RESET_UI"] = {
+ text = "Reset UI Search settings to defaults?",
+ button1 = "Reset",
+ button2 = "Cancel",
+ OnAccept = function() Options:DoResetUI() end,
+ timeout = 0,
+ whileDead = true,
+ hideOnEscape = true,
+ preferredIndex = 3,
+ }
- local resetAllBtn = CreateFrame("Button", nil, optionsFrame, "UIPanelButtonTemplate")
- resetAllBtn:SetSize(BTN_W, 22)
- resetAllBtn:SetPoint("BOTTOMLEFT", optionsFrame, "BOTTOMLEFT", 12, 18 + 22 + BTN_ROW_GAP)
- resetAllBtn:SetText("Reset Settings")
+ StaticPopupDialogs["EASYFIND_RESET_MAP"] = {
+ text = "Reset Map Search settings to defaults?",
+ button1 = "Reset",
+ button2 = "Cancel",
+ OnAccept = function() Options:DoResetMap() end,
+ timeout = 0,
+ whileDead = true,
+ hideOnEscape = true,
+ preferredIndex = 3,
+ }
+
+ StaticPopupDialogs["EASYFIND_RESET_UI_POS"] = {
+ text = "Reset UI Search positions to defaults?",
+ button1 = "Reset",
+ button2 = "Cancel",
+ OnAccept = function() Options:DoResetUIPositions() end,
+ timeout = 0,
+ whileDead = true,
+ hideOnEscape = true,
+ preferredIndex = 3,
+ }
+
+ StaticPopupDialogs["EASYFIND_RESET_MAP_POS"] = {
+ text = "Reset Map Search positions to defaults?",
+ button1 = "Reset",
+ button2 = "Cancel",
+ OnAccept = function() Options:DoResetMapPositions() end,
+ timeout = 0,
+ whileDead = true,
+ hideOnEscape = true,
+ preferredIndex = 3,
+ }
+
+ -- Reset buttons inside General tab (below last slider)
+ local resetAllBtn = CreateFrame("Button", nil, sec3, "UIPanelButtonTemplate")
+ resetAllBtn:SetSize(RESET_BTN_W, 20)
+ resetAllBtn:SetPoint("BOTTOMLEFT", sec3, "BOTTOMLEFT", 8, 8)
+ resetAllBtn:SetText("Reset All Settings")
resetAllBtn:SetScript("OnClick", function()
StaticPopup_Show("EASYFIND_RESET_ALL")
end)
- local resetPosBtn = CreateFrame("Button", nil, optionsFrame, "UIPanelButtonTemplate")
- resetPosBtn:SetSize(BTN_W, 22)
- resetPosBtn:SetPoint("LEFT", resetAllBtn, "RIGHT", 12, 0)
- resetPosBtn:SetText("Reset Positions")
+ local resetPosBtn = CreateFrame("Button", nil, sec3, "UIPanelButtonTemplate")
+ resetPosBtn:SetSize(RESET_BTN_W, 20)
+ resetPosBtn:SetPoint("LEFT", resetAllBtn, "RIGHT", 8, 0)
+ resetPosBtn:SetText("Reset All Positions")
resetPosBtn:SetScript("OnClick", function()
StaticPopup_Show("EASYFIND_RESET_POSITIONS")
end)
- local bugBtn = CreateFrame("Button", nil, optionsFrame, "UIPanelButtonTemplate")
- bugBtn:SetSize(BTN_W, 22)
- bugBtn:SetPoint("BOTTOMLEFT", optionsFrame, "BOTTOMLEFT", 12, 18)
+ -- FEEDBACK TAB
+ local feedbackTab = CreateTab("Feedback")
+
+ local feedbackDesc = feedbackTab:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
+ feedbackDesc:SetPoint("TOPLEFT", feedbackTab, "TOPLEFT", 12, -16)
+ feedbackDesc:SetPoint("RIGHT", feedbackTab, "RIGHT", -12, 0)
+ feedbackDesc:SetJustifyH("LEFT")
+ feedbackDesc:SetSpacing(3)
+ feedbackDesc:SetText(
+ "Found a bug or have an idea for a new feature? Clicking either button "
+ .. "below will give you a link to submit your feedback.\n\n"
+ .. "When reporting a bug, please include what you were doing when it happened "
+ .. "and any error messages you saw. Screenshots are great!"
+ )
+
+ local bugBtn = CreateFrame("Button", nil, feedbackTab, "UIPanelButtonTemplate")
+ bugBtn:SetSize(RESET_BTN_W, 20)
+ bugBtn:SetPoint("TOPLEFT", feedbackDesc, "BOTTOMLEFT", 0, -12)
bugBtn:SetText("Report Bug")
bugBtn:SetScript("OnClick", function()
EasyFind:OpenBugReport()
end)
- local featureBtn = CreateFrame("Button", nil, optionsFrame, "UIPanelButtonTemplate")
- featureBtn:SetSize(BTN_W, 22)
+ local featureBtn = CreateFrame("Button", nil, feedbackTab, "UIPanelButtonTemplate")
+ featureBtn:SetSize(RESET_BTN_W, 20)
featureBtn:SetPoint("LEFT", bugBtn, "RIGHT", 12, 0)
featureBtn:SetText("Request Feature")
featureBtn:SetScript("OnClick", function()
EasyFind:OpenFeatureRequest()
end)
- -- Initial layout (all collapsed)
- RelayoutSections()
+ local enjoyDesc = feedbackTab:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
+ enjoyDesc:SetPoint("TOPLEFT", bugBtn, "BOTTOMLEFT", 0, -18)
+ enjoyDesc:SetPoint("RIGHT", feedbackTab, "RIGHT", -12, 0)
+ enjoyDesc:SetJustifyH("LEFT")
+ enjoyDesc:SetSpacing(3)
+ enjoyDesc:SetText(
+ "If you're enjoying EasyFind, spreading the word or leaving a friendly "
+ .. "comment on the CurseForge page is always appreciated. "
+ .. "Copy the link below to share with friends:"
+ )
+
+ local shareBox = CreateURLBox(feedbackTab, "https://www.curseforge.com/wow/addons/easyfind", enjoyDesc, -6)
+
+ -- Show Home tab by default
+ SwitchToTab(1)
optionsFrame:Hide()
@@ -1393,7 +1555,6 @@ function Options:DoResetPositions()
optionsFrame:ClearAllPoints()
optionsFrame:SetPoint("TOP", UIParent, "TOP", 0, -100)
end
- EasyFind:Print("All positions reset to defaults.")
end
function Options:DoResetAll()
@@ -1520,6 +1681,7 @@ function Options:DoResetAll()
optionsFrame.colorSwatch:SetColorTexture(defaultRGB[1], defaultRGB[2], defaultRGB[3], 1)
optionsFrame.keybindBtn:SetText("Not Bound")
optionsFrame.focusBtn:SetText("Not Bound")
+ optionsFrame.toggleFocusBtn:SetText("Not Bound")
if _G["EasyFindSearchFrame"] and ns.UI then
if ns.UI.ResetPosition then ns.UI:ResetPosition() end
@@ -1546,12 +1708,140 @@ function Options:DoResetAll()
if _G["EasyFindSearchFrame"] and ns.UI and ns.UI.Show then ns.UI:Show() end
EasyFind:UpdateMinimapButton()
- EasyFind:Print("All settings reset to defaults.")
if needsReload then
StaticPopup_Show("EASYFIND_RELOAD_PROMPT")
end
end
+function Options:DoResetUI()
+ EasyFind.db.directOpen = false
+ EasyFind.db.smartShow = false
+ EasyFind.db.staticOpacity = false
+ EasyFind.db.uiResultsAbove = false
+ EasyFind.db.fontSize = 1.0
+ EasyFind.db.uiSearchScale = 1.0
+ EasyFind.db.uiSearchWidth = 1.0
+ EasyFind.db.uiResultsScale = 1.0
+ EasyFind.db.uiResultsWidth = 1.0
+ EasyFind.db.uiSearchPosition = nil
+ EasyFind.db.uiMaxResults = 10
+ EasyFind.db.uiSearchFilters = { ui = true, mounts = false, toys = false, pets = false, map = false }
+ EasyFind.db.uiMapSearchLocal = true
+
+ optionsFrame.directOpenCheckbox:SetChecked(false)
+ optionsFrame.smartShowCheckbox:SetChecked(false)
+ optionsFrame.staticOpacityCheckbox:SetChecked(false)
+ optionsFrame.uiResultsAboveCheckbox:SetChecked(false)
+ optionsFrame.uiFontSlider:SetValue(1.0)
+ local sf = _G["EasyFindSearchFrame"]
+ if sf and sf.modeBtn and ns.UpdateModeButtonVisual then
+ ns.UpdateModeButtonVisual(sf.modeBtn)
+ end
+
+ ns.Highlight:ClearAll()
+ if _G["EasyFindSearchFrame"] and ns.UI then
+ if ns.UI.ResetPosition then ns.UI:ResetPosition() end
+ if ns.UI.UpdateScale then ns.UI:UpdateScale() end
+ if ns.UI.UpdateWidth then ns.UI:UpdateWidth() end
+ if ns.UI.UpdateSmartShow then ns.UI:UpdateSmartShow() end
+ if ns.UI.UpdateFontSize then ns.UI:UpdateFontSize() end
+ if ns.UI.RefreshResults then ns.UI:RefreshResults() end
+ end
+end
+
+function Options:DoResetMap()
+ EasyFind.db.navigateToZonesDirectly = false
+ EasyFind.db.mapSmartShow = false
+ EasyFind.db.hideSearchBarsMaximized = false
+ EasyFind.db.mapResultsAbove = false
+ EasyFind.db.mapFontSize = 1.0
+ EasyFind.db.mapSearchYOffset = 0
+ EasyFind.db.iconScale = 0.8
+ EasyFind.db.mapSearchScale = 1.0
+ EasyFind.db.mapSearchWidth = 1.0
+ EasyFind.db.mapResultsScale = 1.0
+ EasyFind.db.mapResultsWidth = 1.0
+ EasyFind.db.mapSearchPosition = nil
+ EasyFind.db.globalSearchPosition = nil
+ EasyFind.db.mapSearchPositionMax = nil
+ EasyFind.db.globalSearchPositionMax = nil
+ EasyFind.db.mapMaxResults = 6
+ EasyFind.db.mapPinHighlight = true
+ EasyFind.db.blinkingPins = false
+ EasyFind.db.minimapArrowGlow = true
+ EasyFind.db.minimapGuideCircle = true
+ EasyFind.db.minimapPinGlow = true
+ EasyFind.db.guideCircleScale = 1.0
+ EasyFind.db.autoPinClear = true
+ EasyFind.db.autoTrackPins = true
+ EasyFind.db.arrivalDistance = 10
+ EasyFind.db.pinsCollapsed = false
+ EasyFind.db.globalSearchFilters = { zones = true, dungeons = true, raids = true, delves = true }
+ EasyFind.db.localSearchFilters = { instances = true, travel = true, services = true }
+
+ optionsFrame.zoneNavCheckbox:SetChecked(false)
+ optionsFrame.searchBarGroup:UpdateVisuals()
+ optionsFrame.mapPinGroup:UpdateVisuals()
+ optionsFrame.minimapGroup:UpdateVisuals()
+ optionsFrame.automationGroup:UpdateVisuals()
+ optionsFrame.mapFontSlider:SetValue(1.0)
+ optionsFrame.mapYOffsetSlider:SetValue(0)
+ optionsFrame.mapIconSlider:SetValue(0.8)
+ optionsFrame.arrivalSlider:SetValue(10)
+ optionsFrame.circleScaleSlider:SetValue(1.0)
+
+ if ns.MapSearch then
+ if ns.MapSearch.HideSuperTrackGlow then pcall(ns.MapSearch.HideSuperTrackGlow) end
+ if _G["EasyFindMapSearchFrame"] then
+ pcall(ns.MapSearch.ClearAll, ns.MapSearch)
+ pcall(ns.MapSearch.ClearZoneHighlight, ns.MapSearch)
+ end
+ ns.MapSearch.pendingWaypoint = nil
+ end
+ if _G["EasyFindMapSearchFrame"] and ns.MapSearch then
+ if ns.MapSearch.ResetPosition then ns.MapSearch:ResetPosition() end
+ if ns.MapSearch.UpdateScale then ns.MapSearch:UpdateScale() end
+ if ns.MapSearch.UpdateWidth then ns.MapSearch:UpdateWidth() end
+ if ns.MapSearch.UpdateFontSize then ns.MapSearch:UpdateFontSize() end
+ if ns.MapSearch.UpdateIconScales then ns.MapSearch:UpdateIconScales() end
+ if ns.MapSearch.RefreshIndicators then ns.MapSearch:RefreshIndicators() end
+ if ns.MapSearch.UpdateMapSmartShow then ns.MapSearch:UpdateMapSmartShow() end
+ end
+ local uiInd = _G["EasyFindIndicatorFrame"]
+ if uiInd then uiInd:SetScale(0.8) end
+end
+
+function Options:DoResetUIPositions()
+ EasyFind.db.uiSearchPosition = nil
+ EasyFind.db.uiSearchScale = 1.0
+ EasyFind.db.uiSearchWidth = 1.0
+ EasyFind.db.uiResultsScale = 1.0
+ EasyFind.db.uiResultsWidth = 1.0
+ if _G["EasyFindSearchFrame"] and ns.UI then
+ if ns.UI.ResetPosition then ns.UI:ResetPosition() end
+ if ns.UI.UpdateScale then ns.UI:UpdateScale() end
+ if ns.UI.UpdateWidth then ns.UI:UpdateWidth() end
+ end
+end
+
+function Options:DoResetMapPositions()
+ EasyFind.db.mapSearchPosition = nil
+ EasyFind.db.globalSearchPosition = nil
+ EasyFind.db.mapSearchPositionMax = nil
+ EasyFind.db.globalSearchPositionMax = nil
+ EasyFind.db.mapSearchScale = 1.0
+ EasyFind.db.mapSearchWidth = 1.0
+ EasyFind.db.mapResultsScale = 1.0
+ EasyFind.db.mapResultsWidth = 1.0
+ EasyFind.db.mapSearchYOffset = 0
+ optionsFrame.mapYOffsetSlider:SetValue(0)
+ if _G["EasyFindMapSearchFrame"] and ns.MapSearch then
+ if ns.MapSearch.ResetPosition then ns.MapSearch:ResetPosition() end
+ if ns.MapSearch.UpdateScale then ns.MapSearch:UpdateScale() end
+ if ns.MapSearch.UpdateWidth then ns.MapSearch:UpdateWidth() end
+ end
+end
+
function Options:RegisterWithBlizzardOptions()
-- Create a panel for the Interface Options
local panel = CreateFrame("Frame")
@@ -1589,13 +1879,6 @@ function Options:Show()
self:Initialize()
end
- -- Collapse all sections on open for a clean view
- for _, section in ipairs(optionsFrame.sections) do
- section.expanded = false
- section.icon:SetAtlas("QuestLog-icon-expand")
- section.content:Hide()
- end
-
-- Refresh values from saved vars
optionsFrame.panelOpacitySlider:SetValue(EasyFind.db.panelOpacity or 0.9)
optionsFrame.opacitySlider:SetValue(EasyFind.db.searchBarOpacity or DEFAULT_OPACITY)
@@ -1627,13 +1910,14 @@ function Options:Show()
optionsFrame.keybindBtn:SetText(key1 or "Not Bound")
local key2 = GetBindingKey("EASYFIND_FOCUS")
optionsFrame.focusBtn:SetText(key2 or "Not Bound")
+ local key3 = GetBindingKey("EASYFIND_TOGGLE_FOCUS")
+ optionsFrame.toggleFocusBtn:SetText(key3 or "Not Bound")
local key4 = GetBindingKey("EASYFIND_CLEAR")
optionsFrame.clearBtn:SetText(key4 or "Not Bound")
if optionsFrame.bgTex then
optionsFrame.bgTex:SetAlpha(EasyFind.db.panelOpacity or 0.9)
end
- optionsFrame.RelayoutSections()
optionsFrame:Show()
end
From d5a5f5d5b60e99133a8743470dd7d1c4089c21e4 Mon Sep 17 00:00:00 2001
From: wowaddonmaker
Date: Mon, 16 Mar 2026 03:34:14 -0400
Subject: [PATCH 04/10] Embed options panel directly in Blizzard addon settings
---
Options.lua | 94 ++++++++++++++++++++++++++++++++++++++++-------------
1 file changed, 71 insertions(+), 23 deletions(-)
diff --git a/Options.lua b/Options.lua
index b6c7450..a71937e 100644
--- a/Options.lua
+++ b/Options.lua
@@ -17,6 +17,12 @@ local DARK_PANEL_BG = ns.DARK_PANEL_BG
local optionsFrame
local isInitialized = false
+local FRAME_BACKDROP = {
+ edgeFile = TOOLTIP_BORDER,
+ edgeSize = 16,
+ insets = { left = 4, right = 4, top = 4, bottom = 4 },
+}
+
-- Shared backdrop for selector buttons and flyout panels
local SELECTOR_BACKDROP = {
bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background",
@@ -451,11 +457,7 @@ function Options:Initialize()
EasyFind.db.optionsPosition = {point, relPoint, x, y}
end)
- optionsFrame:SetBackdrop({
- edgeFile = TOOLTIP_BORDER,
- edgeSize = 16,
- insets = { left = 4, right = 4, top = 4, bottom = 4 },
- })
+ optionsFrame:SetBackdrop(FRAME_BACKDROP)
optionsFrame:SetBackdropBorderColor(0.50, 0.48, 0.45, 1.0)
local bgTex = optionsFrame:CreateTexture(nil, "BACKGROUND", nil, -1)
@@ -469,10 +471,12 @@ function Options:Initialize()
local title = optionsFrame:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
title:SetPoint("TOP", optionsFrame, "TOP", 0, -16)
title:SetText("EasyFind Options")
+ optionsFrame.titleText = title
-- Close button
local closeBtn = CreateFrame("Button", nil, optionsFrame, "UIPanelCloseButton")
closeBtn:SetPoint("TOPRIGHT", optionsFrame, "TOPRIGHT", -5, -5)
+ optionsFrame.closeBtn = closeBtn
-- Content border (all tabs render inside this)
local contentBorder = CreateFrame("Frame", nil, optionsFrame, "BackdropTemplate")
@@ -1843,29 +1847,38 @@ function Options:DoResetMapPositions()
end
function Options:RegisterWithBlizzardOptions()
- -- Create a panel for the Interface Options
local panel = CreateFrame("Frame")
panel.name = "EasyFind"
- local title = panel:CreateFontString(nil, "ARTWORK", "GameFontNormalLarge")
- title:SetPoint("TOPLEFT", 16, -16)
- title:SetText("EasyFind")
-
- local desc = panel:CreateFontString(nil, "ARTWORK", "GameFontHighlight")
- desc:SetPoint("TOPLEFT", title, "BOTTOMLEFT", 0, -8)
- desc:SetWidth(550)
- desc:SetJustifyH("LEFT")
- desc:SetText("EasyFind helps you find UI elements and map locations.\n\nUse /ef to search, or /ef o to open options.")
-
- local openOptionsBtn = CreateFrame("Button", nil, panel, "UIPanelButtonTemplate")
- openOptionsBtn:SetSize(150, 30)
- openOptionsBtn:SetPoint("TOPLEFT", desc, "BOTTOMLEFT", 0, -20)
- openOptionsBtn:SetText("Open EasyFind Options")
- openOptionsBtn:SetScript("OnClick", function()
+ panel:SetScript("OnShow", function(self)
+ if not isInitialized then Options:Initialize() end
+ Options.embedded = true
+ Options.embedding = true
+
+ -- Hide standalone chrome
+ optionsFrame.titleText:Hide()
+ optionsFrame.closeBtn:Hide()
+ optionsFrame.bgTex:Hide()
+ optionsFrame:SetBackdrop(nil)
+
+ -- Reparent into Blizzard panel
+ optionsFrame:SetParent(self)
+ optionsFrame:ClearAllPoints()
+ optionsFrame:SetPoint("TOPLEFT", self, "TOPLEFT", 0, 34)
+ optionsFrame:SetFrameStrata("HIGH")
+ optionsFrame:SetMovable(false)
+ optionsFrame:RegisterForDrag()
+
Options:Show()
+ Options.embedding = false
+ end)
+
+ panel:SetScript("OnHide", function()
+ if not Options.embedded then return end
+ Options:RestoreStandalone()
+ optionsFrame:Hide()
end)
- -- Register with the new Settings API if available, otherwise use old method
if Settings and Settings.RegisterCanvasLayoutCategory then
local category = Settings.RegisterCanvasLayoutCategory(panel, panel.name)
Settings.RegisterAddOnCategory(category)
@@ -1879,6 +1892,11 @@ function Options:Show()
self:Initialize()
end
+ -- If called standalone while embedded in Blizzard panel, pull out
+ if self.embedded and not self.embedding then
+ self:RestoreStandalone()
+ end
+
-- Refresh values from saved vars
optionsFrame.panelOpacitySlider:SetValue(EasyFind.db.panelOpacity or 0.9)
optionsFrame.opacitySlider:SetValue(EasyFind.db.searchBarOpacity or DEFAULT_OPACITY)
@@ -1915,12 +1933,36 @@ function Options:Show()
local key4 = GetBindingKey("EASYFIND_CLEAR")
optionsFrame.clearBtn:SetText(key4 or "Not Bound")
- if optionsFrame.bgTex then
+ if not self.embedded and optionsFrame.bgTex then
optionsFrame.bgTex:SetAlpha(EasyFind.db.panelOpacity or 0.9)
end
optionsFrame:Show()
end
+function Options:RestoreStandalone()
+ self.embedded = false
+
+ optionsFrame.titleText:Show()
+ optionsFrame.closeBtn:Show()
+ optionsFrame.bgTex:Show()
+ optionsFrame.bgTex:SetAlpha(EasyFind.db.panelOpacity or 0.9)
+ optionsFrame:SetBackdrop(FRAME_BACKDROP)
+ optionsFrame:SetBackdropBorderColor(0.50, 0.48, 0.45, 1.0)
+
+ optionsFrame:SetParent(UIParent)
+ optionsFrame:SetFrameStrata("DIALOG")
+ optionsFrame:SetMovable(true)
+ optionsFrame:RegisterForDrag("LeftButton")
+
+ optionsFrame:ClearAllPoints()
+ if EasyFind.db.optionsPosition then
+ local pos = EasyFind.db.optionsPosition
+ optionsFrame:SetPoint(pos[1], UIParent, pos[2], pos[3], pos[4])
+ else
+ optionsFrame:SetPoint("TOP", UIParent, "TOP", 0, -100)
+ end
+end
+
function Options:Hide()
if optionsFrame then
optionsFrame:Hide()
@@ -1932,6 +1974,12 @@ function Options:Toggle()
self:Initialize()
end
+ if self.embedded then
+ self:RestoreStandalone()
+ self:Show()
+ return
+ end
+
if optionsFrame:IsShown() then
self:Hide()
else
From bce0d73dd18c9b2867f3bc0bf4567b7cc6fc9bf5 Mon Sep 17 00:00:00 2001
From: wowaddonmaker
Date: Mon, 16 Mar 2026 03:51:49 -0400
Subject: [PATCH 05/10] Cancel druid shapeshift form when clicking mount
results
---
UI.lua | 16 ++++++++++------
1 file changed, 10 insertions(+), 6 deletions(-)
diff --git a/UI.lua b/UI.lua
index 9fcdac1..85f46ae 100644
--- a/UI.lua
+++ b/UI.lua
@@ -2354,14 +2354,20 @@ function UI:ShowHierarchicalResults(hierarchical, preserveScroll)
resultRow.selectionHighlight:SetVertexColor(unpack(theme.selectionColor))
resultRow.data = data
- -- Set secure action attributes for toys (UseToyByItemID is protected)
+ -- Set secure action attributes for toys and mounts
if not InCombatLockdown() then
if data and data.toyItemID then
resultRow:SetAttribute("type", "toy")
resultRow:SetAttribute("toy", data.toyItemID)
+ resultRow:SetAttribute("macrotext", nil)
+ elseif data and data.mountID then
+ resultRow:SetAttribute("type", "macro")
+ resultRow:SetAttribute("macrotext", "/cancelform [form]\n/run C_MountJournal.SummonByID(" .. data.mountID .. ")")
+ resultRow:SetAttribute("toy", nil)
else
resultRow:SetAttribute("type", nil)
resultRow:SetAttribute("toy", nil)
+ resultRow:SetAttribute("macrotext", nil)
end
end
resultRow.isPathNode = entry.isPathNode
@@ -3387,7 +3393,7 @@ function UI:SelectResult(data)
if not data then return end
- -- Mount: summon/dismiss (SummonByID is not protected, callable from addon code)
+ -- Mount: secure button handles cancelform on click; Enter falls back to direct API
if data.mountID then
if C_MountJournal and C_MountJournal.SummonByID then
C_MountJournal.SummonByID(data.mountID)
@@ -3395,10 +3401,8 @@ function UI:SelectResult(data)
return
end
- -- Toy: activated via SecureActionButton on mousedown (UseToyByItemID is protected)
- if data.toyItemID then
- return
- end
+ -- Toy: handled by SecureActionButton on mousedown (UseToyByItemID is protected)
+ if data.toyItemID then return end
-- Pet: summon/dismiss
if data.petID then
From e72dfa081308b99957719ce630cddf78c76268ec Mon Sep 17 00:00:00 2001
From: wowaddonmaker
Date: Tue, 17 Mar 2026 06:04:23 -0400
Subject: [PATCH 06/10] Improve map search UX, Smart Show, tooltips, and
settings panel overlap
---
Highlight.lua | 22 +-
MapSearch.lua | 540 +++++++++++++++++++++++++++++++++++++-------------
Options.lua | 405 ++++++++++++++++++++++++-------------
UI.lua | 54 ++---
4 files changed, 698 insertions(+), 323 deletions(-)
diff --git a/Highlight.lua b/Highlight.lua
index a7d1129..f8cf047 100644
--- a/Highlight.lua
+++ b/Highlight.lua
@@ -12,6 +12,8 @@ local select, ipairs = Utils.select, Utils.ipairs
local sfind, slower = Utils.sfind, Utils.slower
local mmax, mpi = Utils.mmax, Utils.mpi
local pcall = Utils.pcall
+local xpcall = Utils.xpcall
+local ErrorHandler = Utils.ErrorHandler
local GOLD_COLOR = ns.GOLD_COLOR
local YELLOW_HIGHLIGHT = ns.YELLOW_HIGHLIGHT
@@ -124,14 +126,14 @@ function Highlight:CreateInstructionFrame()
tile = true, tileSize = 32, edgeSize = 16,
insets = { left = 5, right = 5, top = 5, bottom = 5 }
})
- instructionFrame:SetBackdropColor(0, 0, 0, 0.95) -- Very dark background
+ instructionFrame:SetBackdropColor(0, 0, 0, 0.95)
local text = instructionFrame:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
text:SetPoint("TOPLEFT", 15, -15)
text:SetPoint("TOPRIGHT", -15, -15)
text:SetTextColor(YELLOW_HIGHLIGHT[1], YELLOW_HIGHLIGHT[2], YELLOW_HIGHLIGHT[3])
text:SetJustifyH("CENTER")
- text:SetWordWrap(true) -- Enable word wrap
+ text:SetWordWrap(true)
text:SetNonSpaceWrap(true)
instructionFrame.text = text
@@ -214,7 +216,7 @@ function Highlight:StartGuide(guideData)
-- Use a ticker to continuously check step conditions
stepTicker = C_Timer.NewTicker(0.1, function()
- local ok, err = pcall(self.UpdateGuide, self)
+ local ok, err = xpcall(self.UpdateGuide, ErrorHandler, self)
if not ok then
if stepTicker then stepTicker:Cancel(); stepTicker = nil end
ns.DebugPrint("Guide error: " .. tostring(err))
@@ -238,7 +240,7 @@ function Highlight:StartGuideAtStep(guideData, stepIndex)
currentStepIndex = stepIndex
-- Attempt immediate highlight (avoids waiting for first ticker interval)
- local ok, err = pcall(self.UpdateGuide, self)
+ local ok, err = xpcall(self.UpdateGuide, ErrorHandler, self)
if not ok then
ns.DebugPrint("Guide error: " .. tostring(err))
return
@@ -248,7 +250,7 @@ function Highlight:StartGuideAtStep(guideData, stepIndex)
if not currentGuide then return end
stepTicker = C_Timer.NewTicker(0.1, function()
- local ok2, err2 = pcall(self.UpdateGuide, self)
+ local ok2, err2 = xpcall(self.UpdateGuide, ErrorHandler, self)
if not ok2 then
if stepTicker then stepTicker:Cancel(); stepTicker = nil end
ns.DebugPrint("Guide error: " .. tostring(err2))
@@ -2229,7 +2231,6 @@ end
-- Currency navigation helpers
--- Check if a currency header is currently expanded
-- Returns: true (expanded), false (collapsed), nil (not in list - parent collapsed)
function Highlight:IsCurrencyHeaderExpanded(headerName)
if not C_CurrencyInfo or not C_CurrencyInfo.GetCurrencyListSize then return nil end
@@ -2331,8 +2332,7 @@ end
-- REPUTATION HELPERS
---- Check if a faction header is expanded
---- Returns: true (expanded), false (collapsed), nil (not found/parent collapsed)
+-- Returns: true (expanded), false (collapsed), nil (not found/parent collapsed)
function Highlight:IsFactionHeaderExpanded(headerName)
if not C_Reputation or not C_Reputation.GetNumFactions then return nil end
@@ -2355,7 +2355,7 @@ function Highlight:IsFactionHeaderExpanded(headerName)
return nil -- header not visible, parent must be collapsed
end
---- Find the visible UI button for a faction header in the ReputationFrame ScrollBox
+-- Find the visible UI button for a faction header in the ReputationFrame ScrollBox
function Highlight:GetFactionHeaderButton(headerName)
if not ReputationFrame or not ReputationFrame:IsShown() then return nil end
@@ -2389,7 +2389,7 @@ function Highlight:GetFactionHeaderButton(headerName)
return nil
end
---- Scroll the ReputationFrame ScrollBox to show a specific faction by ID
+-- Scroll the ReputationFrame ScrollBox to show a specific faction by ID
function Highlight:ScrollToFactionRow(factionID)
if not ReputationFrame or not ReputationFrame:IsShown() then return end
if not ReputationFrame.ScrollBox then return end
@@ -2415,7 +2415,7 @@ function Highlight:ScrollToFactionRow(factionID)
end, fraction)
end
---- Find the visible UI button/row for a specific faction by ID in the ReputationFrame ScrollBox
+-- Find the visible UI button/row for a specific faction by ID in the ReputationFrame ScrollBox
function Highlight:GetFactionRowButton(factionID)
if not ReputationFrame or not ReputationFrame:IsShown() then return nil end
if not ReputationFrame.ScrollBox then return nil end
diff --git a/MapSearch.lua b/MapSearch.lua
index 3f2d9a7..0d059d6 100644
--- a/MapSearch.lua
+++ b/MapSearch.lua
@@ -9,8 +9,10 @@ local pairs, ipairs, type, select = Utils.pairs, Utils.ipairs, Utils.type, Utils
local tinsert, tsort, tconcat, tremove = Utils.tinsert, Utils.tsort, Utils.tconcat, Utils.tremove
local sfind, slower = Utils.sfind, Utils.slower
local mmin, mmax, mpi = Utils.mmin, Utils.mmax, Utils.mpi
-local pcall, tostring = Utils.pcall, Utils.tostring
+local pcall, xpcall, tostring = Utils.pcall, Utils.xpcall, Utils.tostring
+local ErrorHandler = Utils.ErrorHandler
+local LIGHTNING_BOLT_TEX = "Interface\\AddOns\\EasyFind\\textures\\lightning-bolt"
local GOLD_COLOR = ns.GOLD_COLOR
local YELLOW_HIGHLIGHT = ns.YELLOW_HIGHLIGHT
local DEFAULT_OPACITY = ns.DEFAULT_OPACITY
@@ -46,6 +48,7 @@ local GetUserWaypoint = C_Map.GetUserWaypoint
local GetWorldPosFromMapPos = C_Map.GetWorldPosFromMapPos
local GetMapRectOnMap = C_Map.GetMapRectOnMap
local GetMapHighlightInfoAtPosition = C_Map.GetMapHighlightInfoAtPosition
+local GetPlayerMapPosition = C_Map.GetPlayerMapPosition
local sgsub = string.gsub
@@ -161,7 +164,7 @@ ns.PIN_GLOW_SIZE = 40 -- Map pin glow
ns.HIGHLIGHT_SIZE = 30 -- Yellow highlight border box
-- Multi-pin (slightly smaller so clusters don't overlap)
-ns.MULTI_SCALE = 0.85
+ns.MULTI_SCALE = 1.0
-- Zone indicator (continent maps)
ns.ZONE_ICON_SIZE = 48
@@ -172,12 +175,12 @@ ns.BREADCRUMB_SIZE = 48
local ANIM_DURATION = 0.5
---- Convert a size in UI units to canvas units so it appears the same visual
---- size as a same-valued element on UIParent.
---- WoW's map zooms by making the canvas LARGER, not by changing scale.
---- So the conversion is: canvasWidth / viewportWidth (canvas units per screen unit).
---- @param uiSize number size in UI coordinate units
---- @return number equivalent canvas coordinate units
+-- Convert a size in UI units to canvas units so it appears the same visual
+-- size as a same-valued element on UIParent.
+-- WoW's map zooms by making the canvas LARGER, not by changing scale.
+-- So the conversion is: canvasWidth / viewportWidth (canvas units per screen unit).
+-- @param uiSize number size in UI coordinate units
+-- @return number equivalent canvas coordinate units
function ns.UIToCanvas(uiSize)
local sc = WorldMapFrame and WorldMapFrame.ScrollContainer
if not sc or not sc.Child then return uiSize end
@@ -193,11 +196,11 @@ end
-- Every indicator icon in the addon (map search, zone search, UI search, breadcrumb)
-- MUST use these two functions so they all look identical.
---- Create icon + glow textures on a parent frame.
---- Returns nothing; sets parentFrame.indicator and parentFrame.glow.
---- @param parentFrame Frame - the frame the icon sits in
---- @param iconSize number|nil - override size (defaults to ns.ICON_SIZE)
---- @param glowSize number|nil - override glow (defaults to ns.ICON_GLOW_SIZE; 0 = no glow)
+-- Create icon + glow textures on a parent frame.
+-- Returns nothing; sets parentFrame.indicator and parentFrame.glow.
+-- @param parentFrame Frame - the frame the icon sits in
+-- @param iconSize number|nil - override size (defaults to ns.ICON_SIZE)
+-- @param glowSize number|nil - override glow (defaults to ns.ICON_GLOW_SIZE; 0 = no glow)
function ns.CreateIndicatorTextures(parentFrame, iconSize, glowSize)
iconSize = iconSize or ns.ICON_SIZE
glowSize = glowSize or ns.ICON_GLOW_SIZE
@@ -240,9 +243,9 @@ function ns.CreateIndicatorTextures(parentFrame, iconSize, glowSize)
end)
end
---- Update an existing indicator (and optional glow) to match current settings.
---- Works on any frame that was set up with ns.CreateIndicatorTextures.
---- @param parentFrame Frame
+-- Update an existing indicator (and optional glow) to match current settings.
+-- Works on any frame that was set up with ns.CreateIndicatorTextures.
+-- @param parentFrame Frame
function ns.UpdateIndicator(parentFrame)
if not parentFrame or not parentFrame.indicator then return end
local style = GetIndicatorTexture()
@@ -289,10 +292,10 @@ function ns.UpdateIndicator(parentFrame)
end
end
---- Compute the rotation for an indicator pointing in a given direction.
---- Takes the style's own rotation into account so every style works correctly.
---- @param direction string "down"|"up"|"left"|"right"
---- @return number rotation in radians
+-- Compute the rotation for an indicator pointing in a given direction.
+-- Takes the style's own rotation into account so every style works correctly.
+-- @param direction string "down"|"up"|"left"|"right"
+-- @return number rotation in radians
function ns.GetDirectionalRotation(direction)
local style = GetIndicatorTexture()
-- Base rotation is whatever points the indicator downward:
@@ -732,7 +735,13 @@ local function CreateWaypointTracker()
-- Minimap pin becomes visible at 75% of the view radius
local showCircle = EasyFind.db.minimapGuideCircle ~= false
+ if showCircle and EasyFind.db.circleOnlyEasyFind and not efPlacedWaypoint then
+ showCircle = false
+ end
local showGlow = EasyFind.db.minimapArrowGlow ~= false
+ if showGlow and EasyFind.db.glowOnlyEasyFind and not efPlacedWaypoint then
+ showGlow = false
+ end
if dist < viewRadius * 0.75 then
-- NEAR MODE: ring around player (Blizzard's pin is visible on minimap)
if not self.lastMode or self.lastMode ~= "NEAR" then
@@ -819,7 +828,7 @@ local function CreateWaypointTracker()
end
waypointController:SetScript("OnUpdate", function(self, elapsed)
- local ok, err = pcall(WaypointOnUpdate, self, elapsed)
+ local ok, err = xpcall(WaypointOnUpdate, ErrorHandler, self, elapsed)
if not ok then
self:SetScript("OnUpdate", nil)
DebugPrint("Waypoint tracker stopped: " .. tostring(err))
@@ -1081,8 +1090,8 @@ end
-- Category icons mapping
local CATEGORY_ICONS = {
flightmaster = "atlas:TaxiNode_Neutral",
- zeppelin = "Interface\\Icons\\INV_Misc_AirshipPart_Propeller",
- boat = "Interface\\Icons\\Achievement_BG_captureflag_EOS",
+ zeppelin = 342918,
+ boat = 1126431,
portal = "Interface\\Icons\\Spell_Arcane_PortalDalaran",
tram = "Interface\\Icons\\INV_Misc_Gear_01",
-- Cropped texCoords from atlas sprite sheet 1121272 - removes the built-in glow border
@@ -1092,11 +1101,11 @@ local CATEGORY_ICONS = {
dungeon = { file = 1121272, coords = { 0.2056, 0.2397, 0.4478, 0.4819 } },
raid = { file = 1121272, coords = { 0.2056, 0.2397, 0.4986, 0.5327 } },
delve = { file = 1121272, coords = { 0.0104, 0.0541, 0.3990, 0.4427 } },
- bank = "Interface\\Icons\\INV_Misc_Bag_10_Blue",
- auctionhouse = "Interface\\Icons\\INV_Misc_Coin_01",
- innkeeper = "Interface\\Icons\\Spell_Holy_GreaterHeal",
- trainer = "Interface\\Icons\\INV_Misc_Book_09",
- proftrainer = "Interface\\Icons\\INV_Misc_Book_09",
+ bank = 136453,
+ auctionhouse = 136452,
+ innkeeper = 136458,
+ trainer = 136463,
+ proftrainer = 136463,
classtrainer = { file = 131016, coords = { 0.000, 0.250, 0.375, 0.500 } },
prof_alchemy = "Interface\\Icons\\Trade_Alchemy",
prof_blacksmithing = "Interface\\Icons\\Trade_BlackSmithing",
@@ -1113,25 +1122,31 @@ local CATEGORY_ICONS = {
prof_tailoring = "Interface\\Icons\\Trade_Tailoring",
prof_firstaid = "Interface\\Icons\\Spell_Holy_SealOfSacrifice",
prof_archaeology = "Interface\\Icons\\Trade_Archaeology",
+ trainingdummy = "Interface\\Icons\\Ability_Warrior_Charge",
vendor = "Interface\\Icons\\INV_Misc_Bag_07",
pvpvendor = 236396,
pvpquest = 236396,
battlemasters = 236396,
- quartermaster = 236396,
- mailbox = "Interface\\Icons\\INV_Letter_15",
- stablemaster = "Interface\\Icons\\Ability_Hunter_BeastCall",
- repairvendor = "Interface\\Icons\\INV_Hammer_20",
- barber = "Interface\\Icons\\INV_Misc_Comb_01",
- transmogrifier = "Interface\\Icons\\INV_Arcane_Orb",
+ quartermaster = { file = 1121272, coords = { 0.5090, 0.5390, 0.6070, 0.6370 } },
+ mailbox = 136459,
+ stablemaster = 136466,
+ repairvendor = 136465,
+ barber = 3852099,
+ transmogrifier = 1598183,
rare = "Interface\\Icons\\INV_Misc_Head_Dragon_01",
treasure = "Interface\\Icons\\INV_Misc_Bag_10",
catalyst = "Interface\\Icons\\INV_10_GearUpgrade_Catalyst_Charged",
greatvault = "Interface\\Icons\\INV_Misc_Lockbox_1",
- upgradevendor = 463442, -- Reforge icon (FileDataID)
+ upgradevendor = 4025144,
guildservices = "Interface\\Icons\\Achievement_GuildPerk_EverybodysFriend",
voidstorage = "Interface\\Icons\\INV_Enchant_VoidCrystal",
tradingpost = "Interface\\Icons\\tradingpostcurrency",
+ decor = { file = 1121272, coords = { 0.4078, 0.4380, 0.8713, 0.9040 } },
chromie = "atlas:ChromieTime-32x32",
+ craftingorders = { file = 1121272, coords = { 0.8764, 0.9040, 0.5102, 0.5357 } },
+ rostrum = { file = 1121272, coords = { 0.7738, 0.8033, 0.4066, 0.4394 } },
+ pettrainer = "atlas:WildBattlePetCapturable",
+ ridingtrainer = "atlas:StableMaster",
areapoi = "Interface\\Icons\\INV_Misc_QuestionMark",
unknown = "Interface\\Icons\\INV_Misc_QuestionMark",
}
@@ -1196,6 +1211,7 @@ local CATEGORIES = {
greatvault = { keywords = {"great vault", "vault", "weekly rewards", "weekly chest"}, parent = "service" },
upgradevendor = { keywords = {"upgrade", "upgrade vendor", "flightstone", "crest"}, parent = "service" },
tradingpost = { keywords = {"trading post", "trader's tender", "tender", "tmog", "xmog"}, parent = "service" },
+ decor = { keywords = {"decor", "decoration", "decorations", "decorator", "housing", "furniture"}, parent = "service" },
}
-- Categories allowed in global (cross-zone) search results.
@@ -1478,16 +1494,70 @@ function MapSearch:CreateSearchFrame()
local contentSz = ns.SEARCHBAR_HEIGHT * ns.SEARCHBAR_FILL
local iconSz = contentSz * ns.SEARCHBAR_ICON_SCALE
- local searchIcon = searchFrame:CreateTexture(nil, "ARTWORK")
+
+ local localModeBtn = CreateFrame("Button", nil, searchFrame)
+ localModeBtn:SetPoint("TOP", searchFrame, "TOP", 0, 0)
+ localModeBtn:SetPoint("BOTTOM", searchFrame, "BOTTOM", 0, 0)
+ localModeBtn:SetPoint("LEFT", searchFrame, "LEFT", 0, 0)
+ localModeBtn:SetWidth(ns.SEARCHBAR_HEIGHT)
+ localModeBtn:SetFrameLevel(searchFrame:GetFrameLevel() + 10)
+
+ local searchIcon = localModeBtn:CreateTexture(nil, "OVERLAY")
searchIcon:SetSize(iconSz, iconSz)
- searchIcon:SetPoint("LEFT", 10, 0)
- searchIcon:SetAtlas("common-search-magnifyingglass")
- searchIcon:SetVertexColor(GOLD_COLOR[1], GOLD_COLOR[2], GOLD_COLOR[3])
+ searchIcon:SetPoint("CENTER")
searchFrame.searchIcon = searchIcon
+ localModeBtn.icon = searchIcon
+
+ local localModeBtnBg = localModeBtn:CreateTexture(nil, "ARTWORK")
+ localModeBtnBg:SetAllPoints()
+ localModeBtnBg:SetTexture(796424)
+ localModeBtnBg:Hide()
+ localModeBtn.btnBg = localModeBtnBg
+ localModeBtn:SetHighlightTexture(130757)
+ searchFrame.modeBtn = localModeBtn
+
+ local function UpdateLocalModeBtnVisual()
+ if EasyFind.db.localMapDirectOpen then
+ searchIcon:SetAtlas(nil)
+ searchIcon:SetTexture(LIGHTNING_BOLT_TEX)
+ else
+ searchIcon:SetTexture(nil)
+ searchIcon:SetAtlas("common-search-magnifyingglass")
+ end
+ searchIcon:SetVertexColor(GOLD_COLOR[1], GOLD_COLOR[2], GOLD_COLOR[3])
+ end
+ localModeBtn.UpdateVisual = UpdateLocalModeBtnVisual
+
+ localModeBtn:SetScript("OnEnter", function(self)
+ self.btnBg:Show()
+ GameTooltip:SetOwner(self, "ANCHOR_TOP")
+ if EasyFind.db.localMapDirectOpen then
+ GameTooltip:SetText("Fast Mode")
+ GameTooltip:AddLine("Click to switch to Standard mode.", 1, 1, 1, true)
+ else
+ GameTooltip:SetText("Standard Mode")
+ GameTooltip:AddLine("Click to switch to Fast mode.", 1, 1, 1, true)
+ end
+ GameTooltip:AddLine("Hold |cFF00FF00Shift|r and drag to reposition.", 0.7, 0.7, 0.7)
+ GameTooltip:Show()
+ end)
+ localModeBtn:SetScript("OnLeave", function(self)
+ if not self.keyboardFocused then self.btnBg:Hide() end
+ GameTooltip_Hide()
+ end)
+ localModeBtn:SetScript("OnClick", function(self)
+ EasyFind.db.localMapDirectOpen = not EasyFind.db.localMapDirectOpen
+ UpdateLocalModeBtnVisual()
+ local optPanel = _G["EasyFindOptionsFrame"]
+ if optPanel and optPanel.localNavCheckbox then
+ optPanel.localNavCheckbox:SetChecked(EasyFind.db.localMapDirectOpen)
+ end
+ end)
+ UpdateLocalModeBtnVisual()
local editBox = CreateFrame("EditBox", "EasyFindMapSearchBox", searchFrame)
editBox:SetHeight(contentSz)
- editBox:SetPoint("LEFT", searchIcon, "RIGHT", 5, 0)
+ editBox:SetPoint("LEFT", localModeBtn, "RIGHT", 0, 0)
-- RIGHT anchor set below after clearBtn creation
editBox:SetFontObject(ns.SEARCHBAR_FONT)
editBox:SetAutoFocus(false)
@@ -1534,6 +1604,7 @@ function MapSearch:CreateSearchFrame()
if self:GetText() ~= "" then
self.placeholder:Hide()
end
+ if not self:HasFocus() then return end
isGlobalSearch = false
activeSearchFrame = searchFrame
MapSearch:OnSearchTextChanged(self:GetText())
@@ -1571,6 +1642,9 @@ function MapSearch:CreateSearchFrame()
localArrow:SetPoint("CENTER")
localArrow:SetTexture(423808)
localArrow:SetTexCoord(0.453, 0.203, 0.453, 0.016, 0.641, 0.203, 0.641, 0.016)
+ localArrow:SetDesaturated(true)
+ localArrow:SetBlendMode("ADD")
+ localArrow:SetVertexColor(GOLD_COLOR[1], GOLD_COLOR[2], GOLD_COLOR[3])
localFilterBtn.arrow = localArrow
local localBtnBg = localFilterBtn:CreateTexture(nil, "ARTWORK")
@@ -1627,11 +1701,17 @@ function MapSearch:CreateSearchFrame()
-- Tooltip showing full placeholder when truncated and unfocused
editBox:HookScript("OnEnter", function(self)
- if placeholder:IsShown() and placeholder:IsTruncated() and not self:HasFocus() then
- GameTooltip:SetOwner(searchFrame, "ANCHOR_TOP")
- GameTooltip:SetText(placeholder:GetText())
- GameTooltip:Show()
+ if self:HasFocus() or not placeholder:IsShown() then return end
+ GameTooltip:SetOwner(searchFrame, "ANCHOR_TOP")
+ if EasyFind.db.localMapDirectOpen then
+ GameTooltip:SetText("|cFFFFD100Zone Search|r (Fast)")
+ GameTooltip:AddLine("Navigates directly to results without zone highlighting.", 1, 1, 1, true)
+ else
+ GameTooltip:SetText("|cFFFFD100Zone Search|r (Standard)")
+ GameTooltip:AddLine("Highlights zones on the map and guides navigation step by step.", 1, 1, 1, true)
end
+ GameTooltip:AddLine("Hold |cFF00FF00Shift|r and drag to reposition.", 0.7, 0.7, 0.7)
+ GameTooltip:Show()
end)
editBox:HookScript("OnLeave", function()
GameTooltip:Hide()
@@ -1664,27 +1744,6 @@ function MapSearch:CreateSearchFrame()
end
end)
- -- Invisible button over the icon to capture hover for tooltip
- local searchIconHitbox = CreateFrame("Button", nil, searchFrame)
- searchIconHitbox:SetSize(22, 22)
- searchIconHitbox:SetPoint("CENTER", searchIcon, "CENTER", 0, 0)
- searchIconHitbox:SetFrameLevel(searchFrame:GetFrameLevel() + 2)
- searchIconHitbox:EnableMouse(true)
- searchIconHitbox:RegisterForClicks()
- searchIconHitbox:SetScript("OnEnter", function(self)
- GameTooltip:SetOwner(self, "ANCHOR_TOP")
- GameTooltip:SetText("|cFFFFD100This Zone|r Search")
- GameTooltip:AddLine("Searches for POIs and sub-zones within the map you're currently viewing.", 1, 1, 1, true)
- GameTooltip:AddLine(" ")
- GameTooltip:AddLine("Hold |cFF00FF00Shift|r and drag to reposition.", 0.7, 0.7, 0.7)
- GameTooltip:Show()
- end)
- searchIconHitbox:SetScript("OnLeave", GameTooltip_Hide)
- searchIconHitbox:SetScript("OnMouseDown", function(_, button)
- if button == "LeftButton" and not IsShiftKeyDown() then
- editBox:SetFocus()
- end
- end)
-- Local filter dropdown
local LOCAL_FILTER_OPTIONS = {
@@ -1783,16 +1842,69 @@ function MapSearch:CreateSearchFrame()
end
end)
- local globalSearchIcon = globalSearchFrame:CreateTexture(nil, "ARTWORK")
+ local globalModeBtn = CreateFrame("Button", nil, globalSearchFrame)
+ globalModeBtn:SetPoint("TOP", globalSearchFrame, "TOP", 0, 0)
+ globalModeBtn:SetPoint("BOTTOM", globalSearchFrame, "BOTTOM", 0, 0)
+ globalModeBtn:SetPoint("LEFT", globalSearchFrame, "LEFT", 0, 0)
+ globalModeBtn:SetWidth(ns.SEARCHBAR_HEIGHT)
+ globalModeBtn:SetFrameLevel(globalSearchFrame:GetFrameLevel() + 10)
+
+ local globalSearchIcon = globalModeBtn:CreateTexture(nil, "OVERLAY")
globalSearchIcon:SetSize(iconSz, iconSz)
- globalSearchIcon:SetPoint("LEFT", 10, 0)
- globalSearchIcon:SetAtlas("common-search-magnifyingglass")
- globalSearchIcon:SetVertexColor(0.4, 0.8, 1)
+ globalSearchIcon:SetPoint("CENTER")
globalSearchFrame.searchIcon = globalSearchIcon
+ globalModeBtn.icon = globalSearchIcon
+
+ local globalModeBtnBg = globalModeBtn:CreateTexture(nil, "ARTWORK")
+ globalModeBtnBg:SetAllPoints()
+ globalModeBtnBg:SetTexture(796424)
+ globalModeBtnBg:Hide()
+ globalModeBtn.btnBg = globalModeBtnBg
+ globalModeBtn:SetHighlightTexture(130757)
+ globalSearchFrame.modeBtn = globalModeBtn
+
+ local function UpdateGlobalModeBtnVisual()
+ if EasyFind.db.globalMapDirectOpen then
+ globalSearchIcon:SetAtlas(nil)
+ globalSearchIcon:SetTexture(LIGHTNING_BOLT_TEX)
+ else
+ globalSearchIcon:SetTexture(nil)
+ globalSearchIcon:SetAtlas("common-search-magnifyingglass")
+ end
+ globalSearchIcon:SetVertexColor(0.4, 0.8, 1)
+ end
+ globalModeBtn.UpdateVisual = UpdateGlobalModeBtnVisual
+
+ globalModeBtn:SetScript("OnEnter", function(self)
+ self.btnBg:Show()
+ GameTooltip:SetOwner(self, "ANCHOR_TOP")
+ if EasyFind.db.globalMapDirectOpen then
+ GameTooltip:SetText("Fast Mode")
+ GameTooltip:AddLine("Click to switch to Standard mode.", 1, 1, 1, true)
+ else
+ GameTooltip:SetText("Standard Mode")
+ GameTooltip:AddLine("Click to switch to Fast mode.", 1, 1, 1, true)
+ end
+ GameTooltip:AddLine("Hold |cFF00FF00Shift|r and drag to reposition.", 0.7, 0.7, 0.7)
+ GameTooltip:Show()
+ end)
+ globalModeBtn:SetScript("OnLeave", function(self)
+ if not self.keyboardFocused then self.btnBg:Hide() end
+ GameTooltip_Hide()
+ end)
+ globalModeBtn:SetScript("OnClick", function(self)
+ EasyFind.db.globalMapDirectOpen = not EasyFind.db.globalMapDirectOpen
+ UpdateGlobalModeBtnVisual()
+ local optPanel = _G["EasyFindOptionsFrame"]
+ if optPanel and optPanel.globalNavCheckbox then
+ optPanel.globalNavCheckbox:SetChecked(EasyFind.db.globalMapDirectOpen)
+ end
+ end)
+ UpdateGlobalModeBtnVisual()
local globalEditBox = CreateFrame("EditBox", "EasyFindMapGlobalSearchBox", globalSearchFrame)
globalEditBox:SetHeight(contentSz)
- globalEditBox:SetPoint("LEFT", globalSearchIcon, "RIGHT", 5, 0)
+ globalEditBox:SetPoint("LEFT", globalModeBtn, "RIGHT", 0, 0)
-- RIGHT anchor set below after globalClearBtn creation
globalEditBox:SetFontObject(ns.SEARCHBAR_FONT)
globalEditBox:SetAutoFocus(false)
@@ -1839,6 +1951,7 @@ function MapSearch:CreateSearchFrame()
if self:GetText() ~= "" then
self.placeholder:Hide()
end
+ if not self:HasFocus() then return end
isGlobalSearch = true
activeSearchFrame = globalSearchFrame
MapSearch:OnSearchTextChanged(self:GetText())
@@ -1876,6 +1989,9 @@ function MapSearch:CreateSearchFrame()
globalArrow:SetPoint("CENTER")
globalArrow:SetTexture(423808)
globalArrow:SetTexCoord(0.453, 0.203, 0.453, 0.016, 0.641, 0.203, 0.641, 0.016)
+ globalArrow:SetDesaturated(true)
+ globalArrow:SetBlendMode("ADD")
+ globalArrow:SetVertexColor(0.4, 0.8, 1)
filterBtn.arrow = globalArrow
local globalBtnBg = filterBtn:CreateTexture(nil, "ARTWORK")
@@ -1932,11 +2048,17 @@ function MapSearch:CreateSearchFrame()
-- Tooltip showing full placeholder when truncated and unfocused
globalEditBox:HookScript("OnEnter", function(self)
- if globalPlaceholder:IsShown() and globalPlaceholder:IsTruncated() and not self:HasFocus() then
- GameTooltip:SetOwner(globalSearchFrame, "ANCHOR_TOP")
- GameTooltip:SetText(globalPlaceholder:GetText())
- GameTooltip:Show()
+ if self:HasFocus() or not globalPlaceholder:IsShown() then return end
+ GameTooltip:SetOwner(globalSearchFrame, "ANCHOR_TOP")
+ if EasyFind.db.globalMapDirectOpen then
+ GameTooltip:SetText("|cFF66CCFFGlobal Search|r (Fast)")
+ GameTooltip:AddLine("Navigates directly to clicked zones and dungeon entrances.", 1, 1, 1, true)
+ else
+ GameTooltip:SetText("|cFF66CCFFGlobal Search|r (Standard)")
+ GameTooltip:AddLine("Highlights zones on the map and guides navigation step by step.", 1, 1, 1, true)
end
+ GameTooltip:AddLine("Hold |cFF00FF00Shift|r and drag to reposition.", 0.7, 0.7, 0.7)
+ GameTooltip:Show()
end)
globalEditBox:HookScript("OnLeave", function()
GameTooltip:Hide()
@@ -1968,27 +2090,6 @@ function MapSearch:CreateSearchFrame()
end
end)
- -- Invisible button over the icon to capture hover for tooltip
- local globalIconHitbox = CreateFrame("Button", nil, globalSearchFrame)
- globalIconHitbox:SetSize(22, 22)
- globalIconHitbox:SetPoint("CENTER", globalSearchIcon, "CENTER", 0, 0)
- globalIconHitbox:SetFrameLevel(globalSearchFrame:GetFrameLevel() + 2)
- globalIconHitbox:EnableMouse(true)
- globalIconHitbox:RegisterForClicks()
- globalIconHitbox:SetScript("OnEnter", function(self)
- GameTooltip:SetOwner(self, "ANCHOR_TOP")
- GameTooltip:SetText("|cFF66CCFFAll Zones|r Search")
- GameTooltip:AddLine("Searches every zone in the entire world - continents, dungeons, and more.", 1, 1, 1, true)
- GameTooltip:AddLine(" ")
- GameTooltip:AddLine("Hold |cFF00FF00Shift|r and drag to reposition.", 0.7, 0.7, 0.7)
- GameTooltip:Show()
- end)
- globalIconHitbox:SetScript("OnLeave", GameTooltip_Hide)
- globalIconHitbox:SetScript("OnMouseDown", function(_, button)
- if button == "LeftButton" and not IsShiftKeyDown() then
- globalEditBox:SetFocus()
- end
- end)
globalSearchFrame.editBox = globalEditBox
globalSearchFrame:Hide()
@@ -2451,6 +2552,21 @@ function MapSearch:CreateResultButton(index)
pinIcon:Hide()
resultRow.pinIcon = pinIcon
+ -- Pin header elements (collapse toggle + gold separator line)
+ local pinToggle = resultRow:CreateTexture(nil, "OVERLAY")
+ pinToggle:SetSize(14, 14)
+ pinToggle:SetPoint("RIGHT", -4, 0)
+ pinToggle:Hide()
+ resultRow.pinToggle = pinToggle
+
+ local pinHeaderLine = resultRow:CreateTexture(nil, "OVERLAY")
+ pinHeaderLine:SetHeight(1)
+ pinHeaderLine:SetPoint("BOTTOMLEFT", 4, 0)
+ pinHeaderLine:SetPoint("BOTTOMRIGHT", -4, 0)
+ pinHeaderLine:SetColorTexture(1.0, 0.82, 0.0, 0.4)
+ pinHeaderLine:Hide()
+ resultRow.pinHeaderLine = pinHeaderLine
+
-- Navigate button - shortcut: select result + auto-set waypoint in one click
local navBtn = CreateFrame("Button", nil, resultRow)
navBtn:SetSize(24, 24)
@@ -2462,6 +2578,24 @@ function MapSearch:CreateResultButton(index)
navBtn.texture = navTex
navBtn:SetHighlightTexture("Interface\\Buttons\\ButtonHilight-Square", "ADD")
navBtn:SetScript("OnEnter", function(self)
+ -- Show preview pinned to the nearest instance (since that's where navigate will go)
+ local data = resultRow.data
+ if data then
+ local coords = MapSearch:GetPreviewCoords(data)
+ if coords then
+ MapSearch._savedPinState = activePinState
+ MapSearch._previewing = true
+ if coords.instances then
+ local nearest = MapSearch:GetNearestInstance(coords.instances, WorldMapFrame:GetMapID())
+ if nearest then
+ MapSearch:ShowWaypointAt(nearest.x, nearest.y, nil, nearest.category)
+ end
+ else
+ MapSearch:ShowWaypointAt(coords.x, coords.y, coords.icon, coords.category)
+ end
+ activePinState = MapSearch._savedPinState
+ end
+ end
GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
if self.disabled then
GameTooltip:SetText("Navigate", 0.5, 0.5, 0.5)
@@ -2472,7 +2606,21 @@ function MapSearch:CreateResultButton(index)
end
GameTooltip:Show()
end)
- navBtn:SetScript("OnLeave", GameTooltip_Hide)
+ navBtn:SetScript("OnLeave", function(self)
+ GameTooltip_Hide()
+ if not MapSearch._previewing then return end
+ MapSearch._previewing = nil
+ MapSearch:ClearHighlight()
+ local saved = MapSearch._savedPinState
+ MapSearch._savedPinState = nil
+ if saved and saved.mapID == WorldMapFrame:GetMapID() then
+ if saved.instances then
+ MapSearch:ShowMultipleWaypoints(saved.instances)
+ else
+ MapSearch:ShowWaypointAt(saved.x, saved.y, saved.icon, saved.category)
+ end
+ end
+ end)
navBtn:SetScript("OnClick", function(self)
if self.disabled then return end
local data = resultRow.data
@@ -2496,6 +2644,17 @@ function MapSearch:CreateResultButton(index)
resultRow:RegisterForClicks("LeftButtonUp", "RightButtonUp")
resultRow:SetScript("OnClick", function(self, mouseButton)
+ if self.data and self.data.isPinHeader then
+ EasyFind.db.mapPinsCollapsed = not EasyFind.db.mapPinsCollapsed
+ local editBox = activeSearchFrame and activeSearchFrame.editBox
+ local text = editBox and editBox:GetText() or ""
+ if text == "" and editBox and editBox:HasFocus() then
+ MapSearch:ShowPinnedItems()
+ else
+ MapSearch:OnSearchTextChanged(text)
+ end
+ return
+ end
if mouseButton == "RightButton" and self.data then
local pinData = self.data
local isPinned = IsMapItemPinned(pinData)
@@ -2674,10 +2833,15 @@ function MapSearch:CreateHighlightFrame()
waypointPin:SetScript("OnLeave", GameTooltip_Hide)
waypointPin:SetScript("OnMouseUp", function(self, button)
if button == "LeftButton" and self.isLocalSearch and self.waypointX and self.waypointY then
+ local x, y = self.waypointX, self.waypointY
+ -- Collapse multi-pin to this one if in multi-pin state
+ if activePinState and activePinState.instances then
+ MapSearch:ShowWaypointAt(x, y, nil, self.waypointCategory)
+ end
local playerMapID = GetBestMapForUnit("player")
local viewingMapID = WorldMapFrame:GetMapID()
if viewingMapID and playerMapID == viewingMapID then
- SetUserWaypoint(UiMapPoint.CreateFromCoordinates(viewingMapID, self.waypointX, self.waypointY))
+ SetUserWaypoint(UiMapPoint.CreateFromCoordinates(viewingMapID, x, y))
C_SuperTrack.SetSuperTrackedUserWaypoint(true)
efPlacedWaypoint = true
ShowSuperTrackGlow()
@@ -4280,12 +4444,12 @@ local function SmartShowFadeIn(frame)
end
local function SmartShowFadeOut(frame)
- if frame.editBox:HasFocus() or frame.editBox:GetText() ~= "" then return end
+ if frame.editBox:HasFocus() then return end
if resultsFrame and resultsFrame:IsShown() then return end
if frame.smartShowTimer then frame.smartShowTimer:Cancel() end
frame.smartShowTimer = C_Timer.NewTimer(0.4, function()
frame.smartShowTimer = nil
- if frame.editBox:HasFocus() or frame.editBox:GetText() ~= "" then return end
+ if frame.editBox:HasFocus() then return end
if resultsFrame and resultsFrame:IsShown() then return end
if frame:IsMouseOver() then return end
frame.smartShowVisible = false
@@ -4293,6 +4457,15 @@ local function SmartShowFadeOut(frame)
end)
end
+local function HookSmartShowChild(child, frame)
+ child:HookScript("OnEnter", function()
+ if EasyFind.db.mapSmartShow then SmartShowFadeIn(frame) end
+ end)
+ child:HookScript("OnLeave", function()
+ if EasyFind.db.mapSmartShow then SmartShowFadeOut(frame) end
+ end)
+end
+
local function HookMapSmartShow(frame)
frame.smartShowVisible = true
frame:HookScript("OnEnter", function()
@@ -4301,6 +4474,21 @@ local function HookMapSmartShow(frame)
frame:HookScript("OnLeave", function()
if EasyFind.db.mapSmartShow then SmartShowFadeOut(frame) end
end)
+ frame.editBox:HookScript("OnEditFocusGained", function()
+ if EasyFind.db.mapSmartShow then SmartShowFadeIn(frame) end
+ end)
+ frame.editBox:HookScript("OnEditFocusLost", function()
+ if not EasyFind.db.mapSmartShow then return end
+ if frame:IsMouseOver() then return end
+ frame.smartShowVisible = false
+ UIFrameFadeOut(frame, 0.25, frame:GetAlpha(), 0)
+ end)
+ -- Child buttons intercept mouse events and don't propagate OnEnter/OnLeave
+ -- to the parent frame, so hook each one explicitly.
+ HookSmartShowChild(frame.modeBtn, frame)
+ HookSmartShowChild(frame.editBox, frame)
+ HookSmartShowChild(frame.clearBtn, frame)
+ HookSmartShowChild(frame.filterBtn, frame)
end
function MapSearch:UpdateMapSmartShow()
@@ -4713,7 +4901,7 @@ function MapSearch:ScanDungeonEntrances(mapID)
x = ex,
y = ey,
pathPrefix = parentLabel,
- keywords = {cat, "instance", "entrance", "portal"},
+ keywords = {cat, "instance", "entrance"},
})
end
@@ -5070,6 +5258,8 @@ function MapSearch:ScanMapPOIs()
category = "tradingpost"
elseif sfind(poiName, "quartermaster") then
category = "quartermaster"
+ elseif sfind(poiName, "decor") then
+ category = "decor"
elseif sfind(poiName, "conquest") or sfind(poiName, "honor") or sfind(poiName, "pvp") then
category = "pvpvendor"
elseif sfind(poiName, "chromie") then
@@ -5178,6 +5368,9 @@ function MapSearch:GetPinInfo(pin)
elseif sfind(poiName, "quartermaster") then
category = "quartermaster"
pinType = "quartermaster"
+ elseif sfind(poiName, "decor") then
+ category = "decor"
+ pinType = "decor"
elseif sfind(poiName, "pvp") or sfind(poiName, "arena") or sfind(poiName, "battleground") or sfind(poiDesc, "pvp") or sfind(poiName, "conquest") or sfind(poiName, "honor") or sfind(poiName, "weekly") then
category = "pvpvendor"
pinType = "pvpvendor"
@@ -5506,9 +5699,11 @@ function MapSearch:OnSearchTextChanged(text)
end
end
for _, loc in ipairs(staticLocations) do
+ -- Don't update existingNames here: multiple static locations with the same
+ -- name but different coords (e.g., two AHs in Orgrimmar) must all reach
+ -- SearchPOIs so the duplicate-tracking system can group and pin them.
if not zoneNames[slower(loc.name)] and not existingNames[slower(loc.name)] then
tinsert(allPOIs, loc)
- existingNames[slower(loc.name)] = true
end
end
end
@@ -5559,19 +5754,27 @@ function MapSearch:OnSearchTextChanged(text)
results = filteredResults
end
- -- Prepend pinned items (always shown at top regardless of query)
+ -- Prepend pinned items with header (always shown at top regardless of query)
local pins = EasyFind.db.pinnedMapItems
if pins and #pins > 0 then
wipe(reusePinnedKeys)
wipe(reusePinned)
local pinnedKeys = reusePinnedKeys
local pinned = reusePinned
- for _, pin in ipairs(pins) do
- local copy = {}
- for k, v in pairs(pin) do copy[k] = v end
- copy.isPinned = true
- tinsert(pinned, copy)
- pinnedKeys[GetMapPinKey(pin)] = true
+ -- Header row
+ tinsert(pinned, { isPinHeader = true, name = "Pinned" })
+ if not EasyFind.db.mapPinsCollapsed then
+ for _, pin in ipairs(pins) do
+ local copy = {}
+ for k, v in pairs(pin) do copy[k] = v end
+ copy.isPinned = true
+ tinsert(pinned, copy)
+ pinnedKeys[GetMapPinKey(pin)] = true
+ end
+ else
+ for _, pin in ipairs(pins) do
+ pinnedKeys[GetMapPinKey(pin)] = true
+ end
end
wipe(reuseFiltered)
local filtered = reuseFiltered
@@ -5580,7 +5783,6 @@ function MapSearch:OnSearchTextChanged(text)
tinsert(filtered, r)
end
end
- -- Combine: pins first, then search results
for _, r in ipairs(filtered) do
tinsert(pinned, r)
end
@@ -5786,12 +5988,26 @@ function MapSearch:ShowResults(results)
if resultRow.prefixText then resultRow.prefixText:Hide() end
if resultRow.indentLine then resultRow.indentLine:Hide() end
if resultRow.pinIcon then resultRow.pinIcon:Hide() end
+ if resultRow.pinToggle then resultRow.pinToggle:Hide() end
+ if resultRow.pinHeaderLine then resultRow.pinHeaderLine:Hide() end
if resultRow.navBtn then resultRow.navBtn:Hide() end
if resultRow.selectionHighlight then resultRow.selectionHighlight:Hide() end
if resultRow.navBtnHighlight then resultRow.navBtnHighlight:Hide() end
-- Format based on type
- if data.isZoneParent then
+ if data.isPinHeader then
+ resultRow.icon:Hide()
+ resultRow.text:ClearAllPoints()
+ resultRow.text:SetPoint("LEFT", resultRow, "LEFT", 4, 0)
+ resultRow.text:SetPoint("RIGHT", resultRow.pinToggle, "LEFT", -4, 0)
+ resultRow.text:SetText("Pinned")
+ resultRow.text:SetTextColor(0.7, 0.7, 0.7)
+ local collapsed = EasyFind.db.mapPinsCollapsed
+ resultRow.pinToggle:SetAtlas(collapsed and "Soulbinds_Collection_CategoryHeader_Expand" or "Soulbinds_Collection_CategoryHeader_Collapse")
+ resultRow.pinToggle:Show()
+ resultRow.pinHeaderLine:Show()
+
+ elseif data.isZoneParent then
resultRow.icon:Hide()
resultRow.text:ClearAllPoints()
resultRow.text:SetPoint("LEFT", resultRow, "LEFT", 8, 0)
@@ -5863,6 +6079,15 @@ function MapSearch:ShowResults(results)
resultRow:Show()
+ -- Separator line after last pinned item before regular results
+ if data.isPinned and not data.isPinHeader then
+ local nextData = results[i + 1]
+ if nextData and not nextData.isPinned and not nextData.isPinHeader then
+ resultRow.pinHeaderLine:Show()
+ yOffset = yOffset - 4
+ end
+ end
+
local scrollGutter = willScroll and (scrollBar:GetWidth() + 0) or 0
local rowW = resultsFrame:GetWidth() - ROW_INSET - scrollGutter
resultRow:SetWidth(rowW)
@@ -5951,13 +6176,15 @@ function MapSearch:ShowPinnedItems()
self:HideResults()
return
end
- -- Mark each pin so ShowResults renders the indicator
local display = {}
- for _, pin in ipairs(pins) do
- local copy = {}
- for k, v in pairs(pin) do copy[k] = v end
- copy.isPinned = true
- tinsert(display, copy)
+ tinsert(display, { isPinHeader = true, name = "Pinned" })
+ if not EasyFind.db.mapPinsCollapsed then
+ for _, pin in ipairs(pins) do
+ local copy = {}
+ for k, v in pairs(pin) do copy[k] = v end
+ copy.isPinned = true
+ tinsert(display, copy)
+ end
end
self:ShowResults(display)
end
@@ -6093,23 +6320,30 @@ end
function MapSearch:FocusLocalSearch()
if not searchFrame or not searchFrame.editBox then return end
- C_Timer.After(0, function()
- searchFrame.editBox:SetFocus()
- end)
+ if EasyFind.db.mapSmartShow then SmartShowFadeIn(searchFrame) end
+ searchFrame.editBox:SetFocus()
end
function MapSearch:FocusGlobalSearch()
if not globalSearchFrame or not globalSearchFrame.editBox then return end
- C_Timer.After(0, function()
- globalSearchFrame.editBox:SetFocus()
- end)
+ if EasyFind.db.mapSmartShow then SmartShowFadeIn(globalSearchFrame) end
+ globalSearchFrame.editBox:SetFocus()
+end
+
+function MapSearch:UpdateMapModeBtns()
+ if searchFrame and searchFrame.modeBtn and searchFrame.modeBtn.UpdateVisual then
+ searchFrame.modeBtn.UpdateVisual()
+ end
+ if globalSearchFrame and globalSearchFrame.modeBtn and globalSearchFrame.modeBtn.UpdateVisual then
+ globalSearchFrame.modeBtn.UpdateVisual()
+ end
end
-- Shared logic for navigating to an instance entrance.
-- If already on the target map, shows waypoint directly.
-- Otherwise checks if the entrance is visible on the current map,
-- falling back to map navigation with a pending waypoint.
-function MapSearch:NavigateToEntrance(name, x, y, icon, category, targetMapID)
+function MapSearch:NavigateToEntrance(name, x, y, icon, category, targetMapID, directMode)
local currentMapID = WorldMapFrame:GetMapID()
if currentMapID == targetMapID then
self:ShowWaypointAt(x, y, icon, category)
@@ -6120,7 +6354,7 @@ function MapSearch:NavigateToEntrance(name, x, y, icon, category, targetMapID)
self:ShowWaypointAt(ex, ey, icon, category)
return
end
- if IsOrphanZone(targetMapID) or EasyFind.db.navigateToZonesDirectly then
+ if IsOrphanZone(targetMapID) or directMode then
self:ClearZoneHighlight()
self.pendingWaypoint = {x = x, y = y, icon = icon, category = category, mapID = targetMapID}
WorldMapFrame:SetMapID(targetMapID)
@@ -6163,6 +6397,9 @@ function MapSearch:SelectResult(data)
return
end
+ local directMode = isGlobalSearch and (EasyFind.db.globalMapDirectOpen or false)
+ or (EasyFind.db.localMapDirectOpen or false)
+
-- Handle zone selection
if data.isZone and data.zoneMapID then
-- Orphan zones have no physical position on any parent map
@@ -6174,8 +6411,8 @@ function MapSearch:SelectResult(data)
WorldMapFrame:SetMapID(data.zoneMapID)
elseif data.entranceX and data.entranceY and data.entranceMapID then
DebugPrint("[EasyFind] SelectResult → ZONE+ENTRANCE branch, entranceMapID=", data.entranceMapID)
- self:NavigateToEntrance(data.name, data.entranceX, data.entranceY, data.entranceIcon, data.entranceCategory, data.entranceMapID)
- elseif EasyFind.db.navigateToZonesDirectly then
+ self:NavigateToEntrance(data.name, data.entranceX, data.entranceY, data.entranceIcon, data.entranceCategory, data.entranceMapID, directMode)
+ elseif directMode then
DebugPrint("[EasyFind] SelectResult → ZONE DIRECT branch, zoneMapID=", data.zoneMapID)
self:ClearZoneHighlight()
WorldMapFrame:SetMapID(data.zoneMapID)
@@ -6189,7 +6426,7 @@ function MapSearch:SelectResult(data)
-- Dungeon/raid entrance from global search: navigate to zone, then show waypoint
if data.isDungeonEntrance and data.entranceMapID then
DebugPrint("[EasyFind] SelectResult → DUNGEON ENTRANCE branch, entranceMapID=", data.entranceMapID)
- self:NavigateToEntrance(data.name, data.x, data.y, data.icon, data.category, data.entranceMapID)
+ self:NavigateToEntrance(data.name, data.x, data.y, data.icon, data.category, data.entranceMapID, directMode)
return
end
@@ -6303,9 +6540,12 @@ function MapSearch:ShowMultipleWaypoints(instances)
extraPin:SetScript("OnLeave", GameTooltip_Hide)
extraPin:SetScript("OnMouseUp", function(self, button)
if button == "LeftButton" and self.isLocalSearch and self.waypointX and self.waypointY then
+ local x, y, cat = self.waypointX, self.waypointY, self.waypointCategory
+ -- Collapse multi-pin to this one, then place waypoint
+ MapSearch:ShowWaypointAt(x, y, nil, cat)
local viewingMapID = WorldMapFrame:GetMapID()
if viewingMapID then
- SetUserWaypoint(UiMapPoint.CreateFromCoordinates(viewingMapID, self.waypointX, self.waypointY))
+ SetUserWaypoint(UiMapPoint.CreateFromCoordinates(viewingMapID, x, y))
C_SuperTrack.SetSuperTrackedUserWaypoint(true)
efPlacedWaypoint = true
ShowSuperTrackGlow()
@@ -6383,6 +6623,7 @@ function MapSearch:ShowMultipleWaypoints(instances)
pin:SetPoint("CENTER", canvas, "TOPLEFT", instance.x * canvasWidth, -instance.y * canvasHeight)
pin.waypointX = instance.x
pin.waypointY = instance.y
+ pin.waypointCategory = instance.category
pin.isLocalSearch = not isGlobalSearch
local iconTexture = GetCategoryIcon(instance.category)
@@ -6414,10 +6655,19 @@ function MapSearch:ShowMultipleWaypoints(instances)
ind:SetPoint("BOTTOM", highlight, "TOP", 0, 2)
ind:Show()
- if ind.animGroup then ind.animGroup:Play() end
+ if ind.animGroup then
+ ind:SetAlpha(1)
+ ind.animGroup:Play()
+ end
if EasyFind.db.blinkingPins then
- if pin.animGroup then pin.animGroup:Play() end
- if highlight.animGroup then highlight.animGroup:Play() end
+ if pin.animGroup then
+ pin:SetAlpha(1)
+ pin.animGroup:Play()
+ end
+ if highlight.animGroup then
+ highlight:SetAlpha(1)
+ highlight.animGroup:Play()
+ end
end
end
end
@@ -6636,6 +6886,25 @@ function MapSearch:ClearAll()
end
end
+function MapSearch:GetNearestInstance(instances, mapID)
+ if #instances == 1 then return instances[1] end
+ local pos = GetPlayerMapPosition(mapID, "player")
+ if not pos then return instances[1] end
+ local px, py = pos:GetXY()
+ local nearest, bestDist = instances[1], math.huge
+ for _, inst in ipairs(instances) do
+ if inst.x and inst.y then
+ local dx, dy = inst.x - px, inst.y - py
+ local dist = dx * dx + dy * dy
+ if dist < bestDist then
+ bestDist = dist
+ nearest = inst
+ end
+ end
+ end
+ return nearest
+end
+
-- Auto-track the currently active pin on the minimap via Blizzard waypoint.
-- Called by the navigate button to combine select + track in one action.
function MapSearch:TrackActivePin()
@@ -6643,8 +6912,12 @@ function MapSearch:TrackActivePin()
local mapID = activePinState.mapID
local x, y
if activePinState.instances then
- local first = activePinState.instances[1]
- if first then x, y = first.x, first.y end
+ local nearest = self:GetNearestInstance(activePinState.instances, mapID)
+ if nearest then
+ x, y = nearest.x, nearest.y
+ -- Collapse multi-pin display to just this instance
+ self:ShowWaypointAt(x, y, nil, nearest.category)
+ end
else
x, y = activePinState.x, activePinState.y
end
@@ -6807,6 +7080,7 @@ function MapSearch:UpdateFontSize()
frame:SetHeight(barH)
frame.editBox:SetHeight(contentSz)
if frame.searchIcon then frame.searchIcon:SetSize(iconSz, iconSz) end
+ if frame.modeBtn then frame.modeBtn:SetWidth(barH) end
if frame.clearBtn then frame.clearBtn:SetSize(clearSz, clearSz) end
if frame.filterBtn then
frame.filterBtn:SetWidth(barH)
diff --git a/Options.lua b/Options.lua
index a71937e..0f95325 100644
--- a/Options.lua
+++ b/Options.lua
@@ -137,6 +137,7 @@ local function CreateSlider(parent, name, label, minVal, maxVal, step, tooltipTe
local suffixLabel = slider:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
suffixLabel:SetPoint("LEFT", inputBox, "RIGHT", 2, 0)
suffixLabel:SetText(suffixText)
+ slider.suffixLabel = suffixLabel
end
-- Helper to get display value (for percentage: multiply by 100)
@@ -257,30 +258,43 @@ local function CreateMultiSelectDropdown(parent, groupLabel, optionDefs, btnWidt
label:SetText(groupLabel .. ":")
wrapper.label = label
+ local btnFrame = CreateFrame("Button", nil, wrapper)
+ btnFrame:SetSize(btnWidth, 22)
+ wrapper.button = btnFrame
+
+ -- WoW-style dropdown button
+ local btnBg = btnFrame:CreateTexture(nil, "BACKGROUND")
+ btnBg:SetAllPoints()
+ btnBg:SetAtlas("common-dropdown-b-button-hover")
+ btnFrame.bg = btnBg
+
-- Summary text: comma-separated short names of active options
local function GetSummaryText()
local parts = {}
for _, def in ipairs(optionDefs) do
- local val = EasyFind.db[def.dbKey]
- if val == nil then val = def.default end
- if val then tinsert(parts, def.shortLabel or def.label) end
+ if not def.hiddenFromSummary then
+ local val = EasyFind.db[def.dbKey]
+ if val == nil then val = def.default end
+ if val then tinsert(parts, def.shortLabel or def.label) end
+ end
end
if #parts == 0 then return "None" end
return tconcat(parts, ", ")
end
- local btnFrame = CreateFrame("Button", nil, wrapper, "BackdropTemplate")
- btnFrame:SetSize(btnWidth, 22)
- btnFrame:SetBackdrop(SELECTOR_BACKDROP)
- btnFrame:SetBackdropColor(0.1, 0.1, 0.1, 0.8)
- btnFrame:SetBackdropBorderColor(0.6, 0.6, 0.6, 0.8)
- wrapper.button = btnFrame
-
local btnText = btnFrame:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
- btnText:SetPoint("CENTER")
+ btnText:SetPoint("LEFT", 4, 0)
+ btnText:SetPoint("RIGHT", -16, 0)
+ btnText:SetJustifyH("CENTER")
btnText:SetText(GetSummaryText())
wrapper.btnText = btnText
+ local arrow = btnFrame:CreateTexture(nil, "OVERLAY")
+ arrow:SetSize(16, 16)
+ arrow:SetPoint("RIGHT", -2, 0)
+ arrow:SetAtlas("common-dropdown-b-arrow-closed")
+ btnFrame.arrow = arrow
+
-- Flyout panel with checkboxes (and optional sliders via AddSlider).
-- Parented to UIParent so it isn't clipped by the options frame.
local ROW_H = 22
@@ -302,10 +316,11 @@ local function CreateMultiSelectDropdown(parent, groupLabel, optionDefs, btnWidt
local contentH = #optionDefs * ROW_H + 4
btnFrame:SetScript("OnClick", function()
- -- Re-anchor each time since the button may have moved
flyout:ClearAllPoints()
flyout:SetPoint("TOP", btnFrame, "BOTTOM", 0, -2)
- flyout:SetShown(not flyout:IsShown())
+ local opening = not flyout:IsShown()
+ flyout:SetShown(opening)
+ arrow:SetAtlas(opening and "common-dropdown-b-arrow-open" or "common-dropdown-b-arrow-closed")
end)
-- Click-away to close
@@ -320,12 +335,14 @@ local function CreateMultiSelectDropdown(parent, groupLabel, optionDefs, btnWidt
end)
flyout:SetScript("OnHide", function(self)
self:SetScript("OnUpdate", nil)
+ arrow:SetAtlas("common-dropdown-b-arrow-closed")
end)
wrapper.checkRows = {}
for i, def in ipairs(optionDefs) do
local row = CreateFrame("CheckButton", nil, flyout, "InterfaceOptionsCheckButtonTemplate")
- row:SetPoint("TOPLEFT", flyout, "TOPLEFT", 4, -4 - (i - 1) * ROW_H)
+ local indent = def.indent and 16 or 0
+ row:SetPoint("TOPLEFT", flyout, "TOPLEFT", 4 + indent, -4 - (i - 1) * ROW_H)
row.Text:SetText(def.label)
row.Text:SetFontObject("GameFontHighlightSmall")
@@ -351,20 +368,73 @@ local function CreateMultiSelectDropdown(parent, groupLabel, optionDefs, btnWidt
row.dbKey = def.dbKey
row.defaultVal = def.default
+ row.parentDbKey = def.parentDbKey
tinsert(wrapper.checkRows, row)
end
+ wrapper.sliders = {}
+
+ -- Update enabled state of child options based on parent
+ local function UpdateChildStates()
+ for _, row in ipairs(wrapper.checkRows) do
+ if row.parentDbKey then
+ local parentVal = EasyFind.db[row.parentDbKey]
+ if parentVal == nil then parentVal = true end
+ if parentVal then
+ row:Enable()
+ if row.Text then row.Text:SetTextColor(1, 1, 1) end
+ else
+ row:Disable()
+ if row.Text then row.Text:SetTextColor(0.5, 0.5, 0.5) end
+ end
+ end
+ end
+ for _, slider in ipairs(wrapper.sliders) do
+ if slider.parentDbKey then
+ local parentVal = EasyFind.db[slider.parentDbKey]
+ if parentVal == nil then parentVal = true end
+ if parentVal then
+ slider:Enable()
+ if slider.Text then slider.Text:SetTextColor(1.0, 0.82, 0.0) end
+ if slider.Low then slider.Low:SetTextColor(0.7, 0.7, 0.7) end
+ if slider.High then slider.High:SetTextColor(0.7, 0.7, 0.7) end
+ if slider.valueText then slider.valueText:SetTextColor(1, 1, 1) end
+ if slider.inputBox then slider.inputBox:Enable(); slider.inputBox:SetTextColor(1, 1, 1) end
+ if slider.suffixLabel then slider.suffixLabel:SetTextColor(1, 1, 1) end
+ if slider.resetBtn then slider.resetBtn:Enable() end
+ else
+ slider:Disable()
+ if slider.Text then slider.Text:SetTextColor(0.5, 0.5, 0.5) end
+ if slider.Low then slider.Low:SetTextColor(0.4, 0.4, 0.4) end
+ if slider.High then slider.High:SetTextColor(0.4, 0.4, 0.4) end
+ if slider.valueText then slider.valueText:SetTextColor(0.4, 0.4, 0.4) end
+ if slider.inputBox then slider.inputBox:Disable(); slider.inputBox:SetTextColor(0.4, 0.4, 0.4) end
+ if slider.suffixLabel then slider.suffixLabel:SetTextColor(0.4, 0.4, 0.4) end
+ if slider.resetBtn then slider.resetBtn:Disable() end
+ end
+ end
+ end
+ end
+
+ -- Hook OnClick to refresh child states after any toggle
+ for _, row in ipairs(wrapper.checkRows) do
+ row:HookScript("OnClick", function() UpdateChildStates() end)
+ end
+ UpdateChildStates()
+
-- Add a slider inside the flyout, below existing content.
-- Returns the slider frame. Uses the same API as CreateSlider.
- wrapper.sliders = {}
- function wrapper:AddSlider(name, sliderLabel, minVal, maxVal, step, tooltipText, formatFunc, defaultValue, unitSuffix)
+ function wrapper:AddSlider(name, sliderLabel, minVal, maxVal, step, tooltipText, formatFunc, defaultValue, unitSuffix, parentDbKey)
contentH = contentH + 18
local slider = CreateSlider(flyout, name, sliderLabel, minVal, maxVal, step, tooltipText, formatFunc, defaultValue, unitSuffix)
- slider:SetWidth(flyoutWidth - 110)
- slider:SetPoint("TOPLEFT", flyout, "TOPLEFT", 14, -contentH)
+ local sliderIndent = parentDbKey and 30 or 14
+ slider:SetWidth(flyoutWidth - 110 - (sliderIndent - 14))
+ slider:SetPoint("TOPLEFT", flyout, "TOPLEFT", sliderIndent, -contentH)
contentH = contentH + 36
flyout:SetHeight(contentH + 4)
+ slider.parentDbKey = parentDbKey
tinsert(self.sliders, slider)
+ if parentDbKey then UpdateChildStates() end
return slider
end
@@ -387,7 +457,7 @@ local function CreateMultiSelectDropdown(parent, groupLabel, optionDefs, btnWidt
end
local DISABLED_TEXT = { 0.5, 0.5, 0.5 }
-local NORMAL_TEXT = { 1.0, 0.82, 0.0 } -- GameFontNormal yellow
+local NORMAL_TEXT = ns.GOLD_COLOR
local function SetControlsEnabled(controls, enabled)
for _, ctrl in ipairs(controls) do
@@ -407,8 +477,10 @@ local function SetControlsEnabled(controls, enabled)
if ctrl.High then ctrl.High:SetTextColor(enabled and 0.7 or 0.4, enabled and 0.7 or 0.4, enabled and 0.7 or 0.4) end
if ctrl.valueText then ctrl.valueText:SetTextColor(enabled and 1.0 or 0.4, enabled and 1.0 or 0.4, enabled and 1.0 or 0.4) end
if ctrl.inputBox then
- if enabled then ctrl.inputBox:Enable() else ctrl.inputBox:Disable() end
+ if enabled then ctrl.inputBox:Enable(); ctrl.inputBox:SetTextColor(1, 1, 1)
+ else ctrl.inputBox:Disable(); ctrl.inputBox:SetTextColor(0.4, 0.4, 0.4) end
end
+ if ctrl.suffixLabel then ctrl.suffixLabel:SetTextColor(enabled and 1 or 0.4, enabled and 1 or 0.4, enabled and 1 or 0.4) end
if ctrl.resetBtn then
if enabled then ctrl.resetBtn:Enable() else ctrl.resetBtn:Disable() end
end
@@ -426,7 +498,6 @@ end
function Options:Initialize()
if isInitialized then return end
- isInitialized = true
local FRAME_W = 380
local FRAME_H = 380
@@ -445,7 +516,7 @@ function Options:Initialize()
else
optionsFrame:SetPoint("TOP", UIParent, "TOP", 0, -100)
end
- optionsFrame:SetFrameStrata("DIALOG")
+ optionsFrame:SetFrameStrata("MEDIUM")
optionsFrame:SetMovable(true)
optionsFrame:EnableMouse(true)
optionsFrame:SetClampedToScreen(true)
@@ -501,18 +572,17 @@ function Options:Initialize()
end
local function SetTabActive(btn, active)
+ btn.isActive = active
if active then
btn.leftActive:Show(); btn.midActive:Show(); btn.rightActive:Show()
btn.leftInactive:Hide(); btn.midInactive:Hide(); btn.rightInactive:Hide()
- btn.leftHl:Hide(); btn.midHl:Hide(); btn.rightHl:Hide()
btn:SetHeight(32)
- btn:GetFontString():SetTextColor(1.0, 0.82, 0.0)
+ btn:GetFontString():SetTextColor(1, 1, 1)
else
btn.leftActive:Hide(); btn.midActive:Hide(); btn.rightActive:Hide()
btn.leftInactive:Show(); btn.midInactive:Show(); btn.rightInactive:Show()
- btn.leftHl:Show(); btn.midHl:Show(); btn.rightHl:Show()
btn:SetHeight(28)
- btn:GetFontString():SetTextColor(0.8, 0.8, 0.8)
+ btn:GetFontString():SetTextColor(1.0, 0.82, 0.0)
end
end
@@ -536,7 +606,6 @@ function Options:Initialize()
local tabW = mmax(textW + 28, 48)
btn:SetSize(tabW, 28)
- -- Force center text within tab
local fs = btn:GetFontString()
fs:ClearAllPoints()
fs:SetPoint("CENTER", btn, "CENTER", -2, 0)
@@ -578,23 +647,28 @@ function Options:Initialize()
ri:SetPoint("BOTTOMRIGHT", 0, 0); FlipV(ri)
btn.rightInactive = ri
- -- Highlight textures (flipped, shown over inactive only)
- local lh = btn:CreateTexture(nil, "HIGHLIGHT")
- lh:SetAtlas("uiframe-tab-left"); lh:SetSize(35, 28)
- lh:SetPoint("BOTTOMLEFT", 0, 0); FlipV(lh); lh:SetAlpha(0.4)
- btn.leftHl = lh
-
- local mh = btn:CreateTexture(nil, "HIGHLIGHT")
- mh:SetAtlas("_uiframe-tab-center")
- mh:SetPoint("BOTTOMLEFT", lh, "BOTTOMRIGHT", 0, 0)
- mh:SetPoint("BOTTOMRIGHT", btn, "BOTTOMRIGHT", -35, 0)
- mh:SetHeight(28); FlipV(mh); mh:SetAlpha(0.4)
- btn.midHl = mh
+ -- Hover: brighten inactive tabs and switch text to white
+ btn:SetScript("OnEnter", function(self)
+ if not self.isActive then
+ li:SetVertexColor(1.0, 1.0, 1.0)
+ mi:SetVertexColor(1.0, 1.0, 1.0)
+ ri:SetVertexColor(1.0, 1.0, 1.0)
+ self:GetFontString():SetTextColor(1, 1, 1)
+ end
+ end)
+ btn:SetScript("OnLeave", function(self)
+ if not self.isActive then
+ li:SetVertexColor(0.75, 0.75, 0.75)
+ mi:SetVertexColor(0.75, 0.75, 0.75)
+ ri:SetVertexColor(0.75, 0.75, 0.75)
+ self:GetFontString():SetTextColor(1.0, 0.82, 0.0)
+ end
+ end)
- local rh = btn:CreateTexture(nil, "HIGHLIGHT")
- rh:SetAtlas("uiframe-tab-right"); rh:SetSize(37, 28)
- rh:SetPoint("BOTTOMRIGHT", 0, 0); FlipV(rh); rh:SetAlpha(0.4)
- btn.rightHl = rh
+ -- Start inactive tabs slightly darker so hover contrast is visible
+ li:SetVertexColor(0.75, 0.75, 0.75)
+ mi:SetVertexColor(0.75, 0.75, 0.75)
+ ri:SetVertexColor(0.75, 0.75, 0.75)
-- Position: tabs sit above the content border, bottom edges touching it
if index == 1 then
@@ -680,7 +754,7 @@ function Options:Initialize()
local homeTab = CreateTab("Home")
local homeIcon = homeTab:CreateTexture(nil, "ARTWORK")
homeIcon:SetSize(48, 48)
- homeIcon:SetPoint("TOPLEFT", homeTab, "TOPLEFT", 12, -16)
+ homeIcon:SetPoint("TOPLEFT", homeTab, "TOPLEFT", 12, -4)
homeIcon:SetTexture("Interface\\MINIMAP\\Tracking\\None")
local homeTitle = homeTab:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
@@ -693,7 +767,7 @@ function Options:Initialize()
homeVersion:SetText("|cFF888888v" .. (tocVersion or "") .. "|r")
local homeDesc = homeTab:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
- homeDesc:SetPoint("TOPLEFT", homeIcon, "BOTTOMLEFT", 0, -12)
+ homeDesc:SetPoint("TOPLEFT", homeIcon, "BOTTOMLEFT", 0, -4)
homeDesc:SetPoint("RIGHT", homeTab, "RIGHT", -12, 0)
homeDesc:SetJustifyH("LEFT")
homeDesc:SetSpacing(3)
@@ -704,8 +778,9 @@ function Options:Initialize()
.. "map results to your searches. The world map also has its own search bars "
.. "for finding zones, dungeons, and points of interest directly on the map.\n\n"
.. "|cFFFFD100Slash commands:|r\n"
- .. " |cFF00FF00/ef o|r Open this options panel\n"
- .. " |cFF00FF00/ef c|r Dismiss all highlights and pins\n\n"
+ .. " |cFF00FF00/ef|r Open this options panel\n"
+ .. " |cFF00FF00/ef c|r Dismiss all highlights and pins\n"
+ .. " |cFF00FF00/ef toggle|r Show/hide search bar (try Smart Show, the minimap button, or a keybind in the |cFFFFD100Shortcuts|r tab instead)\n\n"
.. "For a full walkthrough, see the CurseForge page:"
)
@@ -941,13 +1016,6 @@ function Options:Initialize()
directOpenCheckbox:SetChecked(EasyFind.db.directOpen or false)
directOpenCheckbox:SetScript("OnClick", function(self)
EasyFind.db.directOpen = self:GetChecked()
- local filters = EasyFind.db.uiSearchFilters
- if filters and filters.map ~= false then
- EasyFind.db.navigateToZonesDirectly = EasyFind.db.directOpen
- if optionsFrame.zoneNavCheckbox then
- optionsFrame.zoneNavCheckbox:SetChecked(EasyFind.db.navigateToZonesDirectly)
- end
- end
ns.Highlight:ClearAll()
local sf = _G["EasyFindSearchFrame"]
if sf and sf.modeBtn and ns.UpdateModeButtonVisual then
@@ -1070,7 +1138,7 @@ function Options:Initialize()
local mapSpeedBox = CreateFrame("Frame", nil, sec2, "BackdropTemplate")
mapSpeedBox:SetPoint("TOPLEFT", sec2, "TOPLEFT", 8, -38)
- mapSpeedBox:SetSize(210, 36)
+ mapSpeedBox:SetSize(210, 72)
mapSpeedBox:SetBackdrop({
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
edgeSize = 14,
@@ -1081,14 +1149,29 @@ function Options:Initialize()
mapSpeedLabel:SetPoint("BOTTOM", mapSpeedBox, "TOP", 0, -8)
mapSpeedLabel:SetText("Speed")
- local zoneNavCheckbox = CreateCheckbox(sec2, "ZoneNav", "Navigate Zones Directly",
- "When enabled, clicking a zone search result will immediately open that zone's map.\n\nWhen disabled (default), you will be guided step by step through the map hierarchy so you can learn how to navigate there yourself.")
- zoneNavCheckbox:SetPoint("TOPLEFT", mapSpeedBox, "TOPLEFT", 8, -4)
- zoneNavCheckbox:SetChecked(EasyFind.db.navigateToZonesDirectly or false)
- zoneNavCheckbox:SetScript("OnClick", function(self)
- EasyFind.db.navigateToZonesDirectly = self:GetChecked()
+ local localNavCheckbox = CreateCheckbox(sec2, "LocalNav", "Navigate Directly: Zone Bar",
+ "When enabled, clicking zone or entrance results in the zone search bar navigates directly without step-by-step zone highlighting.\n\nAlso toggled by clicking the search icon on the zone bar.")
+ localNavCheckbox:SetPoint("TOPLEFT", mapSpeedBox, "TOPLEFT", 8, -10)
+ localNavCheckbox:SetChecked(EasyFind.db.localMapDirectOpen or false)
+ localNavCheckbox:SetScript("OnClick", function(self)
+ EasyFind.db.localMapDirectOpen = self:GetChecked()
+ if ns.MapSearch and ns.MapSearch.UpdateMapModeBtns then
+ ns.MapSearch:UpdateMapModeBtns()
+ end
end)
- optionsFrame.zoneNavCheckbox = zoneNavCheckbox
+ optionsFrame.localNavCheckbox = localNavCheckbox
+
+ local globalNavCheckbox = CreateCheckbox(sec2, "GlobalNav", "Navigate Directly: Global Bar",
+ "When enabled, clicking zone or dungeon results in the global search bar navigates directly without breadcrumb zone highlighting.\n\nAlso toggled by clicking the search icon on the global bar.")
+ globalNavCheckbox:SetPoint("TOPLEFT", localNavCheckbox, "BOTTOMLEFT", 0, -4)
+ globalNavCheckbox:SetChecked(EasyFind.db.globalMapDirectOpen or false)
+ globalNavCheckbox:SetScript("OnClick", function(self)
+ EasyFind.db.globalMapDirectOpen = self:GetChecked()
+ if ns.MapSearch and ns.MapSearch.UpdateMapModeBtns then
+ ns.MapSearch:UpdateMapModeBtns()
+ end
+ end)
+ optionsFrame.globalNavCheckbox = globalNavCheckbox
local resizeMapBtn = CreateFrame("Button", nil, sec2, "UIPanelButtonTemplate")
resizeMapBtn:SetSize(160, 22)
@@ -1148,7 +1231,7 @@ function Options:Initialize()
end)
optionsFrame.mapYOffsetSlider = mapYOffsetSlider
- local mapPinGroup = CreateMultiSelectDropdown(sec2, "Map Pins", {
+ local mapPinGroup = CreateMultiSelectDropdown(sec2, "EF Map Icons", {
{ label = "Highlight Box", shortLabel = "Highlight", dbKey = "mapPinHighlight", default = true,
tooltip = "A yellow highlight box is drawn around map search pins.\nDisable to show only the pin icon and indicator arrow.",
callback = function() if ns.MapSearch and ns.MapSearch.UpdatePinHighlight then ns.MapSearch:UpdatePinHighlight() end end },
@@ -1181,9 +1264,13 @@ function Options:Initialize()
local minimapGroup = CreateMultiSelectDropdown(sec2, "Minimap", {
{ label = "Arrow Glow", shortLabel = "Arrow", dbKey = "minimapArrowGlow", default = true,
tooltip = "A pulsing glow highlights the minimap perimeter arrow that points toward your active map pin.\nDisable if you find the glow distracting." },
+ { label = "Only EasyFind Pins", dbKey = "glowOnlyEasyFind", default = false, indent = true, hiddenFromSummary = true, parentDbKey = "minimapArrowGlow",
+ tooltip = "When enabled, the arrow glow only appears for waypoints placed by EasyFind (clicking search results or navigation pins).\nWaypoints placed by Ctrl+clicking the map or other means will not show the glow." },
{ label = "Guide Circle", shortLabel = "Circle", dbKey = "minimapGuideCircle", default = true,
tooltip = "A directional ring and arrow appears around your character on the minimap when a map pin is nearby, pointing toward the destination.\nDisable if you prefer only the default minimap pin." },
- { label = "Pin Glow", shortLabel = "Glow", dbKey = "minimapPinGlow", default = true,
+ { label = "Only EasyFind Pins", dbKey = "circleOnlyEasyFind", default = false, indent = true, hiddenFromSummary = true, parentDbKey = "minimapGuideCircle",
+ tooltip = "When enabled, the guide circle only appears for waypoints placed by EasyFind.\nWaypoints placed by Ctrl+clicking the map or other means will not show the circle." },
+ { label = "Pin Glow", shortLabel = "Glow", dbKey = "minimapPinGlow", default = true, indent = true, parentDbKey = "minimapGuideCircle",
tooltip = "A pulsing glow appears on the minimap pin when the guide circle shrinks onto it.\nDisable if you find the glow distracting." },
}, nil, FLYOUT_W)
minimapGroup:SetPoint("TOPLEFT", mapPinGroup, "BOTTOMLEFT", 0, -6)
@@ -1195,17 +1282,17 @@ function Options:Initialize()
local circleScaleSlider = minimapGroup:AddSlider("CircleScale", "Guide Circle Size", 0.5, 2.0, 0.1,
"Adjusts the size of the minimap guide circle and arrow that appears when tracking a map pin.",
- nil, 1.0)
+ nil, 1.0, nil, "minimapGuideCircle")
circleScaleSlider:SetValue(EasyFind.db.guideCircleScale or 1.0)
circleScaleSlider:HookScript("OnValueChanged", function(self, value)
EasyFind.db.guideCircleScale = value
end)
optionsFrame.circleScaleSlider = circleScaleSlider
- local automationGroup = CreateMultiSelectDropdown(sec2, "Automation", {
- { label = "Auto Track", shortLabel = "Track", dbKey = "autoTrackPins", default = true,
+ local automationGroup = CreateMultiSelectDropdown(sec2, "Map Pins", {
+ { label = "Auto Track Map Pins", shortLabel = "Track", dbKey = "autoTrackPins", default = true,
tooltip = "Placing a map pin (Ctrl+Click) automatically starts tracking it on the minimap.\nWhen disabled, you must click the pin to start tracking." },
- { label = "Auto Clear", shortLabel = "Clear", dbKey = "autoPinClear", default = true,
+ { label = "Auto Clear Map Pins", shortLabel = "Clear", dbKey = "autoPinClear", default = true,
tooltip = "Your map pin is automatically cleared when you arrive at the destination.\nDisable if you prefer to clear pins manually." },
}, nil, FLYOUT_W)
automationGroup:SetPoint("TOPLEFT", minimapGroup, "BOTTOMLEFT", 0, -6)
@@ -1217,7 +1304,7 @@ function Options:Initialize()
local arrivalSlider = automationGroup:AddSlider("ArrivalDist", "Arrival Distance", 3, 50, 1,
"How close (in yards) you must be to a tracked location before the waypoint auto-clears.",
- function(val) return tostring(mfloor(val + 0.5)) .. "yd" end, 10, "yd")
+ function(val) return tostring(mfloor(val + 0.5)) .. "yd" end, 10, "yd", "autoPinClear")
arrivalSlider:SetValue(EasyFind.db.arrivalDistance or 10)
arrivalSlider:HookScript("OnValueChanged", function(self, value)
value = mfloor(value + 0.5)
@@ -1225,26 +1312,6 @@ function Options:Initialize()
end)
optionsFrame.arrivalSlider = arrivalSlider
- -- Border around Auto Clear + Arrival Distance to show they're linked
- local clearGroup = CreateFrame("Frame", nil, automationGroup.flyout, "BackdropTemplate")
- local autoClearRow = automationGroup.checkRows[2] -- Auto Clear is second
- clearGroup:SetPoint("TOPLEFT", autoClearRow, "TOPLEFT", -4, 4)
- clearGroup:SetPoint("RIGHT", automationGroup.flyout, "RIGHT", -4, 0)
- clearGroup:SetPoint("BOTTOM", automationGroup.flyout, "BOTTOM", 0, 3)
- clearGroup:SetBackdrop({
- edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
- edgeSize = 10,
- insets = { left = 2, right = 2, top = 2, bottom = 2 },
- })
- clearGroup:SetBackdropBorderColor(0.4, 0.4, 0.4, 0.5)
-
- -- Gray out Arrival Distance slider when Auto Clear is unchecked
- local function UpdateArrivalSliderState()
- SetControlsEnabled({arrivalSlider}, EasyFind.db.autoPinClear ~= false)
- end
- UpdateArrivalSliderState()
- autoClearRow:HookScript("OnClick", UpdateArrivalSliderState)
-
local resetMapBtn = CreateFrame("Button", nil, sec2, "UIPanelButtonTemplate")
resetMapBtn:SetSize(RESET_BTN_W, 20)
resetMapBtn:SetPoint("BOTTOMLEFT", sec2, "BOTTOMLEFT", 8, 8)
@@ -1262,7 +1329,7 @@ function Options:Initialize()
end)
mapControls = {
- resizeMapBtn, resetMapBtn, resetMapPosBtn, zoneNavCheckbox,
+ resizeMapBtn, resetMapBtn, resetMapPosBtn, localNavCheckbox, globalNavCheckbox,
searchBarGroup, mapPinGroup, minimapGroup, automationGroup
}
UpdateMapToggleVisual()
@@ -1360,8 +1427,7 @@ function Options:Initialize()
optionsFrame.toggleFocusBtn = keybindButtons["EASYFIND_TOGGLE_FOCUS"]
optionsFrame.clearBtn = keybindButtons["EASYFIND_CLEAR"]
-
- -- BOTTOM - Reset buttons (tips moved to Home tab)
+ -- Reset buttons (tips on Home tab)
StaticPopupDialogs["EASYFIND_RESET_ALL"] = {
text = "Reset all EasyFind settings to defaults?",
@@ -1485,6 +1551,14 @@ function Options:Initialize()
resetAllBtn:SetScript("OnClick", function()
StaticPopup_Show("EASYFIND_RESET_ALL")
end)
+ resetAllBtn:SetScript("OnEnter", function(self)
+ GameTooltip:SetOwner(self, "ANCHOR_TOP")
+ GameTooltip:AddLine("Reset All Settings")
+ GameTooltip:AddLine("Restores all options to their default values.", 1, 1, 1, true)
+ GameTooltip:AddLine("Slash command: /ef reset", 0.7, 0.7, 0.7)
+ GameTooltip:Show()
+ end)
+ resetAllBtn:SetScript("OnLeave", GameTooltip_Hide)
local resetPosBtn = CreateFrame("Button", nil, sec3, "UIPanelButtonTemplate")
resetPosBtn:SetSize(RESET_BTN_W, 20)
@@ -1516,6 +1590,14 @@ function Options:Initialize()
bugBtn:SetScript("OnClick", function()
EasyFind:OpenBugReport()
end)
+ bugBtn:SetScript("OnEnter", function(self)
+ GameTooltip:SetOwner(self, "ANCHOR_TOP")
+ GameTooltip:AddLine("Report Bug")
+ GameTooltip:AddLine("Opens a link to submit a bug report on GitHub.", 1, 1, 1, true)
+ GameTooltip:AddLine("Slash command: /ef bug", 0.7, 0.7, 0.7)
+ GameTooltip:Show()
+ end)
+ bugBtn:SetScript("OnLeave", GameTooltip_Hide)
local featureBtn = CreateFrame("Button", nil, feedbackTab, "UIPanelButtonTemplate")
featureBtn:SetSize(RESET_BTN_W, 20)
@@ -1524,6 +1606,14 @@ function Options:Initialize()
featureBtn:SetScript("OnClick", function()
EasyFind:OpenFeatureRequest()
end)
+ featureBtn:SetScript("OnEnter", function(self)
+ GameTooltip:SetOwner(self, "ANCHOR_TOP")
+ GameTooltip:AddLine("Request Feature")
+ GameTooltip:AddLine("Opens a link to suggest a feature on GitHub.", 1, 1, 1, true)
+ GameTooltip:AddLine("Slash command: /ef feature", 0.7, 0.7, 0.7)
+ GameTooltip:Show()
+ end)
+ featureBtn:SetScript("OnLeave", GameTooltip_Hide)
local enjoyDesc = feedbackTab:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
enjoyDesc:SetPoint("TOPLEFT", bugBtn, "BOTTOMLEFT", 0, -18)
@@ -1533,7 +1623,7 @@ function Options:Initialize()
enjoyDesc:SetText(
"If you're enjoying EasyFind, spreading the word or leaving a friendly "
.. "comment on the CurseForge page is always appreciated. "
- .. "Copy the link below to share with friends:"
+ .. "Copy the link below to share directly with friends:"
)
local shareBox = CreateURLBox(feedbackTab, "https://www.curseforge.com/wow/addons/easyfind", enjoyDesc, -6)
@@ -1543,6 +1633,7 @@ function Options:Initialize()
optionsFrame:Hide()
+ isInitialized = true
self:RegisterWithBlizzardOptions()
end
@@ -1566,15 +1657,15 @@ function Options:DoResetAll()
EasyFind.db.iconScale = 0.8
EasyFind.db.uiSearchScale = 1.0
EasyFind.db.mapSearchScale = 1.0
- EasyFind.db.mapSearchWidth = 1.0
- EasyFind.db.uiSearchWidth = 1.0
+ EasyFind.db.mapSearchWidth = 0.88
+ EasyFind.db.uiSearchWidth = 0.88
EasyFind.db.uiResultsScale = 1.0
- EasyFind.db.uiResultsWidth = 1.0
+ EasyFind.db.uiResultsWidth = 300
EasyFind.db.mapResultsScale = 1.0
- EasyFind.db.mapResultsWidth = 1.0
+ EasyFind.db.mapResultsWidth = 300
EasyFind.db.searchBarOpacity = DEFAULT_OPACITY
- EasyFind.db.fontSize = 1.0
- EasyFind.db.mapFontSize = 1.0
+ EasyFind.db.fontSize = 0.9
+ EasyFind.db.mapFontSize = 0.9
EasyFind.db.uiSearchPosition = nil
EasyFind.db.mapSearchPosition = nil
EasyFind.db.globalSearchPosition = nil
@@ -1582,7 +1673,8 @@ function Options:DoResetAll()
EasyFind.db.globalSearchPositionMax = nil
EasyFind.db.mapSearchYOffset = 0
EasyFind.db.directOpen = false
- EasyFind.db.navigateToZonesDirectly = false
+ EasyFind.db.localMapDirectOpen = false
+ EasyFind.db.globalMapDirectOpen = false
EasyFind.db.smartShow = false
EasyFind.db.resultsTheme = "Retail"
EasyFind.db.uiMaxResults = 10
@@ -1597,9 +1689,12 @@ function Options:DoResetAll()
EasyFind.db.uiResultsAbove = false
EasyFind.db.mapResultsAbove = false
EasyFind.db.showMinimapButton = true
+ EasyFind.db.minimapButtonAngle = 200
EasyFind.db.arrivalDistance = 10
EasyFind.db.panelOpacity = 0.9
EasyFind.db.minimapArrowGlow = true
+ EasyFind.db.glowOnlyEasyFind = false
+ EasyFind.db.circleOnlyEasyFind = false
EasyFind.db.minimapGuideCircle = true
EasyFind.db.autoPinClear = true
EasyFind.db.autoTrackPins = true
@@ -1656,23 +1751,25 @@ function Options:DoResetAll()
optionsFrame.mapYOffsetSlider:SetValue(0)
optionsFrame.panelOpacitySlider:SetValue(0.9)
optionsFrame.opacitySlider:SetValue(DEFAULT_OPACITY)
- optionsFrame.uiFontSlider:SetValue(1.0)
- optionsFrame.mapFontSlider:SetValue(1.0)
+ optionsFrame.uiFontSlider:SetValue(0.9)
+ optionsFrame.mapFontSlider:SetValue(0.9)
optionsFrame.directOpenCheckbox:SetChecked(false)
local sf = _G["EasyFindSearchFrame"]
if sf and sf.modeBtn and ns.UpdateModeButtonVisual then
ns.UpdateModeButtonVisual(sf.modeBtn)
end
- optionsFrame.zoneNavCheckbox:SetChecked(false)
+ optionsFrame.localNavCheckbox:SetChecked(false)
+ optionsFrame.globalNavCheckbox:SetChecked(false)
+ if ns.MapSearch and ns.MapSearch.UpdateMapModeBtns then ns.MapSearch:UpdateMapModeBtns() end
optionsFrame.smartShowCheckbox:SetChecked(false)
optionsFrame.staticOpacityCheckbox:SetChecked(false)
optionsFrame.loginMessageCheckbox:SetChecked(true)
optionsFrame.uiResultsAboveCheckbox:SetChecked(false)
optionsFrame.minimapBtnCheckbox:SetChecked(true)
- optionsFrame.searchBarGroup:UpdateVisuals()
- optionsFrame.mapPinGroup:UpdateVisuals()
- optionsFrame.minimapGroup:UpdateVisuals()
- optionsFrame.automationGroup:UpdateVisuals()
+ if optionsFrame.searchBarGroup then optionsFrame.searchBarGroup:UpdateVisuals() end
+ if optionsFrame.mapPinGroup then optionsFrame.mapPinGroup:UpdateVisuals() end
+ if optionsFrame.minimapGroup then optionsFrame.minimapGroup:UpdateVisuals() end
+ if optionsFrame.automationGroup then optionsFrame.automationGroup:UpdateVisuals() end
if optionsFrame.UpdateUIToggleVisual then optionsFrame.UpdateUIToggleVisual() end
if optionsFrame.UpdateMapToggleVisual then optionsFrame.UpdateMapToggleVisual() end
optionsFrame.arrivalSlider:SetValue(10)
@@ -1701,6 +1798,7 @@ function Options:DoResetAll()
if ns.MapSearch.ResetPosition then ns.MapSearch:ResetPosition() end
if ns.MapSearch.UpdateScale then ns.MapSearch:UpdateScale() end
if ns.MapSearch.UpdateWidth then ns.MapSearch:UpdateWidth() end
+ if ns.MapSearch.UpdateResultsWidth then ns.MapSearch:UpdateResultsWidth() end
if ns.MapSearch.UpdateFontSize then ns.MapSearch:UpdateFontSize() end
if ns.MapSearch.UpdateIconScales then ns.MapSearch:UpdateIconScales() end
if ns.MapSearch.RefreshIndicators then ns.MapSearch:RefreshIndicators() end
@@ -1722,11 +1820,11 @@ function Options:DoResetUI()
EasyFind.db.smartShow = false
EasyFind.db.staticOpacity = false
EasyFind.db.uiResultsAbove = false
- EasyFind.db.fontSize = 1.0
+ EasyFind.db.fontSize = 0.9
EasyFind.db.uiSearchScale = 1.0
- EasyFind.db.uiSearchWidth = 1.0
+ EasyFind.db.uiSearchWidth = 0.88
EasyFind.db.uiResultsScale = 1.0
- EasyFind.db.uiResultsWidth = 1.0
+ EasyFind.db.uiResultsWidth = 300
EasyFind.db.uiSearchPosition = nil
EasyFind.db.uiMaxResults = 10
EasyFind.db.uiSearchFilters = { ui = true, mounts = false, toys = false, pets = false, map = false }
@@ -1736,7 +1834,7 @@ function Options:DoResetUI()
optionsFrame.smartShowCheckbox:SetChecked(false)
optionsFrame.staticOpacityCheckbox:SetChecked(false)
optionsFrame.uiResultsAboveCheckbox:SetChecked(false)
- optionsFrame.uiFontSlider:SetValue(1.0)
+ optionsFrame.uiFontSlider:SetValue(0.9)
local sf = _G["EasyFindSearchFrame"]
if sf and sf.modeBtn and ns.UpdateModeButtonVisual then
ns.UpdateModeButtonVisual(sf.modeBtn)
@@ -1754,17 +1852,18 @@ function Options:DoResetUI()
end
function Options:DoResetMap()
- EasyFind.db.navigateToZonesDirectly = false
+ EasyFind.db.localMapDirectOpen = false
+ EasyFind.db.globalMapDirectOpen = false
EasyFind.db.mapSmartShow = false
EasyFind.db.hideSearchBarsMaximized = false
EasyFind.db.mapResultsAbove = false
- EasyFind.db.mapFontSize = 1.0
+ EasyFind.db.mapFontSize = 0.9
EasyFind.db.mapSearchYOffset = 0
EasyFind.db.iconScale = 0.8
EasyFind.db.mapSearchScale = 1.0
- EasyFind.db.mapSearchWidth = 1.0
+ EasyFind.db.mapSearchWidth = 0.88
EasyFind.db.mapResultsScale = 1.0
- EasyFind.db.mapResultsWidth = 1.0
+ EasyFind.db.mapResultsWidth = 300
EasyFind.db.mapSearchPosition = nil
EasyFind.db.globalSearchPosition = nil
EasyFind.db.mapSearchPositionMax = nil
@@ -1773,6 +1872,8 @@ function Options:DoResetMap()
EasyFind.db.mapPinHighlight = true
EasyFind.db.blinkingPins = false
EasyFind.db.minimapArrowGlow = true
+ EasyFind.db.glowOnlyEasyFind = false
+ EasyFind.db.circleOnlyEasyFind = false
EasyFind.db.minimapGuideCircle = true
EasyFind.db.minimapPinGlow = true
EasyFind.db.guideCircleScale = 1.0
@@ -1783,12 +1884,14 @@ function Options:DoResetMap()
EasyFind.db.globalSearchFilters = { zones = true, dungeons = true, raids = true, delves = true }
EasyFind.db.localSearchFilters = { instances = true, travel = true, services = true }
- optionsFrame.zoneNavCheckbox:SetChecked(false)
- optionsFrame.searchBarGroup:UpdateVisuals()
- optionsFrame.mapPinGroup:UpdateVisuals()
- optionsFrame.minimapGroup:UpdateVisuals()
- optionsFrame.automationGroup:UpdateVisuals()
- optionsFrame.mapFontSlider:SetValue(1.0)
+ optionsFrame.localNavCheckbox:SetChecked(false)
+ optionsFrame.globalNavCheckbox:SetChecked(false)
+ if ns.MapSearch and ns.MapSearch.UpdateMapModeBtns then ns.MapSearch:UpdateMapModeBtns() end
+ if optionsFrame.searchBarGroup then optionsFrame.searchBarGroup:UpdateVisuals() end
+ if optionsFrame.mapPinGroup then optionsFrame.mapPinGroup:UpdateVisuals() end
+ if optionsFrame.minimapGroup then optionsFrame.minimapGroup:UpdateVisuals() end
+ if optionsFrame.automationGroup then optionsFrame.automationGroup:UpdateVisuals() end
+ optionsFrame.mapFontSlider:SetValue(0.9)
optionsFrame.mapYOffsetSlider:SetValue(0)
optionsFrame.mapIconSlider:SetValue(0.8)
optionsFrame.arrivalSlider:SetValue(10)
@@ -1806,6 +1909,7 @@ function Options:DoResetMap()
if ns.MapSearch.ResetPosition then ns.MapSearch:ResetPosition() end
if ns.MapSearch.UpdateScale then ns.MapSearch:UpdateScale() end
if ns.MapSearch.UpdateWidth then ns.MapSearch:UpdateWidth() end
+ if ns.MapSearch.UpdateResultsWidth then ns.MapSearch:UpdateResultsWidth() end
if ns.MapSearch.UpdateFontSize then ns.MapSearch:UpdateFontSize() end
if ns.MapSearch.UpdateIconScales then ns.MapSearch:UpdateIconScales() end
if ns.MapSearch.RefreshIndicators then ns.MapSearch:RefreshIndicators() end
@@ -1818,9 +1922,9 @@ end
function Options:DoResetUIPositions()
EasyFind.db.uiSearchPosition = nil
EasyFind.db.uiSearchScale = 1.0
- EasyFind.db.uiSearchWidth = 1.0
+ EasyFind.db.uiSearchWidth = 0.88
EasyFind.db.uiResultsScale = 1.0
- EasyFind.db.uiResultsWidth = 1.0
+ EasyFind.db.uiResultsWidth = 300
if _G["EasyFindSearchFrame"] and ns.UI then
if ns.UI.ResetPosition then ns.UI:ResetPosition() end
if ns.UI.UpdateScale then ns.UI:UpdateScale() end
@@ -1834,9 +1938,9 @@ function Options:DoResetMapPositions()
EasyFind.db.mapSearchPositionMax = nil
EasyFind.db.globalSearchPositionMax = nil
EasyFind.db.mapSearchScale = 1.0
- EasyFind.db.mapSearchWidth = 1.0
+ EasyFind.db.mapSearchWidth = 0.88
EasyFind.db.mapResultsScale = 1.0
- EasyFind.db.mapResultsWidth = 1.0
+ EasyFind.db.mapResultsWidth = 300
EasyFind.db.mapSearchYOffset = 0
optionsFrame.mapYOffsetSlider:SetValue(0)
if _G["EasyFindMapSearchFrame"] and ns.MapSearch then
@@ -1885,6 +1989,26 @@ function Options:RegisterWithBlizzardOptions()
else
InterfaceOptions_AddCategory(panel)
end
+
+ -- Hook the root Blizzard settings frame so the search bar hides whenever
+ -- settings are open (regardless of which tab is active).
+ local settingsRoot = SettingsPanel or InterfaceOptionsFrame
+ if settingsRoot then
+ local restoreOnClose = false
+ settingsRoot:HookScript("OnShow", function()
+ local sf = _G["EasyFindSearchFrame"]
+ restoreOnClose = sf and sf:IsShown() or false
+ if sf then sf:Hide() end
+ if ns.UI and ns.UI.HideResults then ns.UI:HideResults() end
+ end)
+ settingsRoot:HookScript("OnHide", function()
+ if restoreOnClose then
+ restoreOnClose = false
+ local sf = _G["EasyFindSearchFrame"]
+ if sf then sf:Show() end
+ end
+ end)
+ end
end
function Options:Show()
@@ -1897,25 +2021,26 @@ function Options:Show()
self:RestoreStandalone()
end
- -- Refresh values from saved vars
- optionsFrame.panelOpacitySlider:SetValue(EasyFind.db.panelOpacity or 0.9)
- optionsFrame.opacitySlider:SetValue(EasyFind.db.searchBarOpacity or DEFAULT_OPACITY)
- optionsFrame.uiFontSlider:SetValue(EasyFind.db.fontSize or 1.0)
- optionsFrame.mapFontSlider:SetValue(EasyFind.db.mapFontSize or 1.0)
- optionsFrame.mapIconSlider:SetValue(EasyFind.db.iconScale or 0.8)
- optionsFrame.mapYOffsetSlider:SetValue(EasyFind.db.mapSearchYOffset or 0)
- optionsFrame.arrivalSlider:SetValue(EasyFind.db.arrivalDistance or 10)
- optionsFrame.directOpenCheckbox:SetChecked(EasyFind.db.directOpen or false)
- optionsFrame.zoneNavCheckbox:SetChecked(EasyFind.db.navigateToZonesDirectly or false)
+ -- Refresh values from saved vars (nil-safe in case init partially failed)
+ if optionsFrame.panelOpacitySlider then optionsFrame.panelOpacitySlider:SetValue(EasyFind.db.panelOpacity or 0.9) end
+ if optionsFrame.opacitySlider then optionsFrame.opacitySlider:SetValue(EasyFind.db.searchBarOpacity or DEFAULT_OPACITY) end
+ if optionsFrame.uiFontSlider then optionsFrame.uiFontSlider:SetValue(EasyFind.db.fontSize or 0.9) end
+ if optionsFrame.mapFontSlider then optionsFrame.mapFontSlider:SetValue(EasyFind.db.mapFontSize or 0.9) end
+ if optionsFrame.mapIconSlider then optionsFrame.mapIconSlider:SetValue(EasyFind.db.iconScale or 0.8) end
+ if optionsFrame.mapYOffsetSlider then optionsFrame.mapYOffsetSlider:SetValue(EasyFind.db.mapSearchYOffset or 0) end
+ if optionsFrame.arrivalSlider then optionsFrame.arrivalSlider:SetValue(EasyFind.db.arrivalDistance or 10) end
+ if optionsFrame.directOpenCheckbox then optionsFrame.directOpenCheckbox:SetChecked(EasyFind.db.directOpen or false) end
+ if optionsFrame.localNavCheckbox then optionsFrame.localNavCheckbox:SetChecked(EasyFind.db.localMapDirectOpen or false) end
+ if optionsFrame.globalNavCheckbox then optionsFrame.globalNavCheckbox:SetChecked(EasyFind.db.globalMapDirectOpen or false) end
optionsFrame.smartShowCheckbox:SetChecked(EasyFind.db.smartShow or false)
optionsFrame.staticOpacityCheckbox:SetChecked(EasyFind.db.staticOpacity or false)
optionsFrame.loginMessageCheckbox:SetChecked(EasyFind.db.showLoginMessage ~= false)
optionsFrame.uiResultsAboveCheckbox:SetChecked(EasyFind.db.uiResultsAbove or false)
optionsFrame.minimapBtnCheckbox:SetChecked(EasyFind.db.showMinimapButton or false)
- optionsFrame.searchBarGroup:UpdateVisuals()
- optionsFrame.mapPinGroup:UpdateVisuals()
- optionsFrame.minimapGroup:UpdateVisuals()
- optionsFrame.automationGroup:UpdateVisuals()
+ if optionsFrame.searchBarGroup then optionsFrame.searchBarGroup:UpdateVisuals() end
+ if optionsFrame.mapPinGroup then optionsFrame.mapPinGroup:UpdateVisuals() end
+ if optionsFrame.minimapGroup then optionsFrame.minimapGroup:UpdateVisuals() end
+ if optionsFrame.automationGroup then optionsFrame.automationGroup:UpdateVisuals() end
optionsFrame.themeBtnText:SetText(EasyFind.db.resultsTheme or "Retail")
optionsFrame.indicatorBtnText:SetText(EasyFind.db.indicatorStyle or "EasyFind Arrow")
local clr = EasyFind.db.indicatorColor or "Yellow"
@@ -1950,7 +2075,7 @@ function Options:RestoreStandalone()
optionsFrame:SetBackdropBorderColor(0.50, 0.48, 0.45, 1.0)
optionsFrame:SetParent(UIParent)
- optionsFrame:SetFrameStrata("DIALOG")
+ optionsFrame:SetFrameStrata("MEDIUM")
optionsFrame:SetMovable(true)
optionsFrame:RegisterForDrag("LeftButton")
diff --git a/UI.lua b/UI.lua
index 85f46ae..f442cbb 100644
--- a/UI.lua
+++ b/UI.lua
@@ -484,21 +484,11 @@ function UI:CreateSearchFrame()
modeBtn:SetScript("OnClick", function(self)
EasyFind.db.directOpen = not EasyFind.db.directOpen
- -- Sync map search "navigate zones directly" when map filter is enabled
- local filters = EasyFind.db.uiSearchFilters
- if filters and filters.map ~= false then
- EasyFind.db.navigateToZonesDirectly = EasyFind.db.directOpen
- end
UpdateModeButtonVisual(self)
ns.Highlight:ClearAll()
local optPanel = _G["EasyFindOptionsFrame"]
- if optPanel then
- if optPanel.directOpenCheckbox then
- optPanel.directOpenCheckbox:SetChecked(EasyFind.db.directOpen)
- end
- if optPanel.zoneNavCheckbox then
- optPanel.zoneNavCheckbox:SetChecked(EasyFind.db.navigateToZonesDirectly or false)
- end
+ if optPanel and optPanel.directOpenCheckbox then
+ optPanel.directOpenCheckbox:SetChecked(EasyFind.db.directOpen)
end
end)
@@ -628,6 +618,9 @@ function UI:CreateSearchFrame()
filterArrow:SetPoint("CENTER")
filterArrow:SetTexture(423808)
filterArrow:SetTexCoord(0.453, 0.203, 0.453, 0.016, 0.641, 0.203, 0.641, 0.016)
+ filterArrow:SetDesaturated(true)
+ filterArrow:SetBlendMode("ADD")
+ filterArrow:SetVertexColor(1, 1, 1)
filterBtn.arrow = filterArrow
local filterBtnBg = filterBtn:CreateTexture(nil, "ARTWORK")
@@ -2211,9 +2204,7 @@ function UI:ShowHierarchicalResults(hierarchical, preserveScroll)
resultsFrame:SetClipsChildren(false)
end
- -- ----------------------------------------------------------------
-- Build the visible list by filtering out children of collapsed nodes
- -- ----------------------------------------------------------------
local visible = {}
local skipBelowDepth = nil -- when set, skip entries deeper than this
local skipPins = false -- when pin header is collapsed, skip pinned entries
@@ -2270,9 +2261,7 @@ function UI:ShowHierarchicalResults(hierarchical, preserveScroll)
scrollInset = resultsFrame.scrollBar:GetWidth()
end
- -- ----------------------------------------------------------------
-- Pre-compute last-child flags on the VISIBLE list
- -- ----------------------------------------------------------------
local isLastChild = {}
for i = 1, count do
local d = visible[i].depth or 0
@@ -2287,9 +2276,7 @@ function UI:ShowHierarchicalResults(hierarchical, preserveScroll)
end
end
- -- ----------------------------------------------------------------
-- Determine pin separator placement
- -- ----------------------------------------------------------------
local PIN_SEP_HEIGHT = 9 -- 4px gap + 1px line + 4px gap
local CAT_SEP_HEIGHT = 9 -- same dimensions as pin separator
local lastPinIndex = 0
@@ -2314,9 +2301,7 @@ function UI:ShowHierarchicalResults(hierarchical, preserveScroll)
end
end
- -- ----------------------------------------------------------------
-- Render visible rows
- -- ----------------------------------------------------------------
local yOffset = 0
local pinEndYOffset = 0
local catSepYPositions = {}
@@ -3086,7 +3071,6 @@ function UI:ShowHierarchicalResults(hierarchical, preserveScroll)
end
end
-
-- Anchor results above or below based on setting
resultsFrame:ClearAllPoints()
if EasyFind.db.uiResultsAbove then
@@ -3886,7 +3870,7 @@ function UI:ExpandCurrencyHeader(headerName)
return false
end
---- Helper function to expand a faction header by name
+-- Helper function to expand a faction header by name
function UI:ExpandFactionHeader(headerName)
if not C_Reputation or not C_Reputation.GetNumFactions then return false end
@@ -4213,7 +4197,7 @@ function UI:ShowFirstTimeSetup()
searchFrame.setupMode = true
searchFrame.editBox:EnableMouse(false)
- -- ── Golden glow overlay ─────────────────────────────────────────────
+ -- Golden glow overlay
local glow = CreateFrame("Frame", "EasyFindSetupGlow", searchFrame, "BackdropTemplate")
glow:SetPoint("TOPLEFT", searchFrame, "TOPLEFT", -6, 6)
glow:SetPoint("BOTTOMRIGHT", searchFrame, "BOTTOMRIGHT", 6, -6)
@@ -4251,7 +4235,7 @@ function UI:ShowFirstTimeSetup()
setupLabel:SetText("EasyFind")
setupLabel:SetTextColor(GOLD_COLOR[1], GOLD_COLOR[2], GOLD_COLOR[3], 0.7)
- -- ── Resize handle (bottom-left corner) ──────────────────────────────
+ -- Resize handle (bottom-left corner)
local resizer = CreateFrame("Button", nil, glow)
resizer:SetSize(16, 16)
resizer:SetPoint("BOTTOMLEFT", glow, "BOTTOMLEFT", 0, 0)
@@ -4300,9 +4284,9 @@ function UI:ShowFirstTimeSetup()
self.lastY = cy
end)
- -- ── Instruction panel (anchored below the glow) ─────────────────────
+ -- Instruction panel (anchored below the glow)
local panel = CreateFrame("Frame", nil, glow, "BackdropTemplate")
- panel:SetSize(340, 260)
+ panel:SetSize(340, 215)
panel:SetPoint("TOP", glow, "BOTTOM", 0, -6)
panel:SetFrameStrata("DIALOG")
panel:SetBackdrop({
@@ -4342,7 +4326,7 @@ function UI:ShowFirstTimeSetup()
-- Smart Show checkbox (default checked - matches DB_DEFAULTS.smartShow = true)
local smartShowCheckbox = CreateFrame("CheckButton", nil, panel, "InterfaceOptionsCheckButtonTemplate")
smartShowCheckbox:SetPoint("TOPLEFT", sep, "BOTTOMLEFT", 0, -6)
- smartShowCheckbox.Text:SetText("|cffFFD100Smart Show|r")
+ smartShowCheckbox.Text:SetText("|cffFFD100Smart Show|r |cff999999(Recommended)|r")
smartShowCheckbox:SetChecked(false)
smartShowCheckbox:SetScript("OnClick", function(self)
-- Update live so the user can see the hover behavior immediately
@@ -4356,15 +4340,7 @@ function UI:ShowFirstTimeSetup()
smartDesc:SetPoint("TOPLEFT", smartShowCheckbox.Text, "BOTTOMLEFT", 0, -2)
smartDesc:SetWidth(284)
smartDesc:SetJustifyH("LEFT")
- smartDesc:SetText(
- "|cff999999If enabled, the bar hides when your mouse|r\n" ..
- "|cff999999moves away and reappears when you hover near|r\n" ..
- "|cff999999it. If kept unchecked, the bar stays visible and can be|r\n" ..
- "|cff999999toggled with the minimap button or|r\n" ..
- "|cffFFD100/ef show|r |cff999999and|r |cffFFD100/ef hide|r|cff999999.|r\n" ..
- "|cff999999You can also enable this for the map search|r\n" ..
- "|cff999999bars in|r |cffFFD100/ef o|r|cff999999.|r"
- )
+ smartDesc:SetText("|cff999999Bar hides when your mouse moves away and reappears when you hover near it.|r")
-- Fade While Moving checkbox (default checked - staticOpacity defaults to false, meaning fade IS active)
local fadeCheckbox = CreateFrame("CheckButton", nil, panel, "InterfaceOptionsCheckButtonTemplate")
@@ -4388,7 +4364,7 @@ function UI:ShowFirstTimeSetup()
footer:SetPoint("BOTTOM", panel, "BOTTOM", 0, 36)
footer:SetWidth(310)
footer:SetJustifyH("CENTER")
- footer:SetText("|cff666666These and more settings can be changed in |cffFFD100/ef o|r|cff666666.|r")
+ footer:SetText("|cff666666These and more settings can be changed in |cffFFD100/ef|r|cff666666.|r")
-- Done button
local doneBtn = CreateFrame("Button", nil, panel, "UIPanelButtonTemplate")
@@ -4396,12 +4372,12 @@ function UI:ShowFirstTimeSetup()
doneBtn:SetPoint("BOTTOM", panel, "BOTTOM", 0, 12)
doneBtn:SetText("Done")
- -- ── During setup: allow drag WITHOUT holding Shift ───────────────────
+ -- During setup: allow drag without holding Shift
searchFrame:SetScript("OnDragStart", function(self)
self:StartMoving()
end)
- -- ── Done handler: persist, cleanup, restore normal drag ─────────────
+ -- Done handler: persist, cleanup, restore normal drag
doneBtn:SetScript("OnClick", function()
EasyFind.db.setupComplete = true
From 5b69ff8bdfd77ce76955ac15bcc8e0c2d3b9c283 Mon Sep 17 00:00:00 2001
From: wowaddonmaker
Date: Tue, 17 Mar 2026 06:04:31 -0400
Subject: [PATCH 07/10] Update DB schema, slash commands, constants, and
cleanup
---
Core.lua | 49 ++++++++++++++++++++++++++++---------------------
Database.lua | 42 ++++++++++++++++++++++--------------------
Rescaler.lua | 24 ++++++++++++++----------
3 files changed, 64 insertions(+), 51 deletions(-)
diff --git a/Core.lua b/Core.lua
index 21a438b..c07ea8b 100644
--- a/Core.lua
+++ b/Core.lua
@@ -35,15 +35,15 @@ local DB_DEFAULTS = {
iconScale = 0.8,
uiSearchScale = 1.0,
mapSearchScale = 1.0,
- mapSearchWidth = 1.0,
- uiSearchWidth = 1.0,
+ mapSearchWidth = 0.88,
+ uiSearchWidth = 0.88,
uiResultsScale = 1.0,
- uiResultsWidth = 350,
+ uiResultsWidth = 300,
mapResultsScale = 1.0,
- mapResultsWidth = 1.0,
+ mapResultsWidth = 300,
searchBarOpacity = 0.75, -- ns.DEFAULT_OPACITY
- fontSize = 1.0, -- UI search font size multiplier (0.5-2.0)
- mapFontSize = 1.0, -- Map search font size multiplier (0.5-2.0)
+ fontSize = 0.9, -- UI search font size multiplier (0.5-2.0)
+ mapFontSize = 0.9, -- Map search font size multiplier (0.5-2.0)
uiSearchPosition = nil, -- {point, relPoint, x, y}
mapSearchPosition = nil, -- x offset from map left edge
globalSearchPosition = nil, -- x offset from map right edge (windowed)
@@ -52,7 +52,8 @@ local DB_DEFAULTS = {
mapSearchYOffset = 0, -- y offset for search bars relative to map bottom
hideSearchBarsMaximized = false, -- Hide search bars when map is full screen
directOpen = false, -- Open panels directly instead of step-by-step
- navigateToZonesDirectly = false, -- Clicking a zone goes directly to it
+ localMapDirectOpen = false, -- Zone bar: navigate directly (no zone highlighting)
+ globalMapDirectOpen = false, -- Global bar: navigate directly (no zone highlighting)
smartShow = false, -- Hide search bar until mouse hovers nearby
mapSmartShow = false, -- Hide map search bars until mouse hovers nearby
resultsTheme = "Retail", -- "Classic" or "Retail"
@@ -66,12 +67,15 @@ local DB_DEFAULTS = {
pinnedUIItems = {}, -- Pinned UI search results (persist across sessions)
pinnedMapItems = {}, -- Pinned map search results (persist across sessions)
pinsCollapsed = false, -- Whether the "Pinned Paths" header is collapsed
+ mapPinsCollapsed = false, -- Whether the map search "Pinned" header is collapsed
showLoginMessage = true, -- Show "EasyFind loaded!" message on login
blinkingPins = false, -- Pulse map pins and highlights in sync with indicator bob
mapPinHighlight = true, -- Show yellow highlight box around map pins
arrivalDistance = 10, -- Yards - auto-clear waypoint when player is this close
minimapArrowGlow = true, -- Pulsing glow on minimap perimeter arrow
+ glowOnlyEasyFind = false, -- Only show glow for waypoints placed by EasyFind
minimapGuideCircle = true, -- Near-track ring + arrow around player on minimap
+ circleOnlyEasyFind = false, -- Only show guide circle for EasyFind waypoints
guideCircleScale = 1.0, -- Scale multiplier for guide circle ring+arrow
minimapPinGlow = true, -- Pulsing glow on map pin when guide circle shrinks onto it
autoPinClear = true, -- Auto-clear map pin when player arrives
@@ -80,7 +84,7 @@ local DB_DEFAULTS = {
mapResultsAbove = false, -- Show map search results above the search bar
panelOpacity = 0.9, -- Options panel background opacity
showMinimapButton = true, -- Show toggle button on minimap
- minimapButtonAngle = 220, -- Position angle (degrees) around minimap edge
+ minimapButtonAngle = 200, -- Position angle (degrees) around minimap edge
globalSearchFilters = { -- Global search category filters (all enabled by default)
zones = true,
dungeons = true,
@@ -110,7 +114,7 @@ local DB_MIGRATIONS = {
if not db.mapMaxResults then db.mapMaxResults = db.maxResults end
db.maxResults = nil
end
- if db.uiResultsWidth == 1.0 then db.uiResultsWidth = 350 end
+ if db.uiResultsWidth == 1.0 then db.uiResultsWidth = 300 end
end,
}
@@ -217,12 +221,6 @@ local function OnInitialize()
EasyFind.db = EasyFindDB
- -- Sync navigateToZonesDirectly with directOpen when map search is enabled
- local filters = EasyFind.db.uiSearchFilters
- if filters and filters.map ~= false then
- EasyFind.db.navigateToZonesDirectly = EasyFind.db.directOpen or false
- end
-
-- Read version from TOC for What's New detection
ns.version = C_AddOns.GetAddOnMetadata(ADDON_NAME, "Version")
@@ -232,10 +230,8 @@ local function OnInitialize()
msg = msg and msg:lower():trim() or ""
if msg == "o" or msg == "options" or msg == "config" or msg == "settings" then
EasyFind:OpenOptions()
- elseif msg == "hide" then
- if ns.UI then ns.UI:Hide() end
- elseif msg == "show" then
- if ns.UI then ns.UI:Show() end
+ elseif msg == "toggle" or msg == "t" then
+ if ns.UI then ns.UI:Toggle() end
elseif msg == "c" or msg == "clear" then
EasyFind:ClearAll()
elseif msg:find("^test ") then
@@ -270,8 +266,18 @@ local function OnInitialize()
end
elseif msg == "whatsnew" then
if ns.UI then ns.UI:ShowWhatsNew(ns.version) end
+ elseif msg == "help" or msg == "h" or msg == "?" then
+ EasyFind:Print("Commands:")
+ EasyFind:Print(" /ef: open options panel")
+ EasyFind:Print(" /ef toggle: show/hide search bar")
+ EasyFind:Print(" /ef clear: dismiss highlights, pins, breadcrumbs")
+ EasyFind:Print(" /ef reset: reset all settings")
+ EasyFind:Print(" /ef bug: report a bug")
+ EasyFind:Print(" /ef feature: request a feature")
+ elseif msg == "" then
+ EasyFind:OpenOptions()
else
- EasyFind:Print("Usage: /ef show | hide | clear | options | reset | bug | feature")
+ print("|cFFFFFF00Type '/ef help' for a list of commands.|r")
end
end
@@ -565,7 +571,7 @@ local function CreateMinimapButton()
mmBtn:SetScript("OnLeave", GameTooltip_Hide)
minimapButton = mmBtn
- PositionMinimapButton(EasyFind.db.minimapButtonAngle or 220)
+ PositionMinimapButton(EasyFind.db.minimapButtonAngle or 200)
return mmBtn
end
@@ -575,6 +581,7 @@ function EasyFind:UpdateMinimapButton()
CreateMinimapButton()
end
minimapButton:Show()
+ PositionMinimapButton(EasyFind.db.minimapButtonAngle or 200)
elseif minimapButton then
minimapButton:Hide()
end
diff --git a/Database.lua b/Database.lua
index 41b04bc..50bd875 100644
--- a/Database.lua
+++ b/Database.lua
@@ -576,6 +576,7 @@ function Database:FlattenTree(tree, parentPath, parentSteps, parentButtonFrame,
if node.icon then entry.icon = node.icon end
if node.available then entry.available = node.available end
if node.canQueue then entry.canQueue = true end
+ if node.children then entry.isContainer = true end
uiSearchData[#uiSearchData + 1] = entry
@@ -1558,9 +1559,8 @@ local FUZZY_BLOCKLIST = {
["pve"] = { ["pvp"] = true },
}
---- Check if `query` appears at a word boundary in `text`.
---- A word boundary is the start of the string or right after a space/punctuation.
---- Returns true if found at a boundary, false if only found mid-word or not at all.
+-- A word boundary is the start of the string or right after a space/punctuation.
+-- Returns true if found at a boundary, false if only found mid-word or not at all.
function Database:FindAtWordBoundary(text, query)
-- Check at start of string
if ssub(text, 1, #query) == query then return true end
@@ -1581,11 +1581,11 @@ function Database:FindAtWordBoundary(text, query)
end
end
---- Score how well `query` matches as initials/abbreviation of words in `text`.
---- "rb" → "rated battlegrounds" = 130 (each char matches a word start)
---- "raba" → "random battleground" = 125 (prefix of words)
---- "ranb" → "random battleground" = 115 (longer prefix matching)
---- Returns 0 if no reasonable initials match found.
+-- Score how well `query` matches as initials/abbreviation of words in `text`.
+-- "rb" → "rated battlegrounds" = 130 (each char matches a word start)
+-- "raba" → "random battleground" = 125 (prefix of words)
+-- "ranb" → "random battleground" = 115 (longer prefix matching)
+-- Returns 0 if no reasonable initials match found.
function Database:ScoreInitials(text, query)
-- Use cached word split (input is already lowercase)
local words = GetWords(text)
@@ -1649,9 +1649,9 @@ function Database:ScoreInitials(text, query)
return 0
end
---- Score fuzzy/typo matching using Damerau-Levenshtein distance.
---- Only applied to individual words in `text` that are similar in length to `query`.
---- Returns a score > 0 if a close match is found, 0 otherwise.
+-- Score fuzzy/typo matching using Damerau-Levenshtein distance.
+-- Only applied to individual words in `text` that are similar in length to `query`.
+-- Returns a score > 0 if a close match is found, 0 otherwise.
function Database:ScoreFuzzy(text, query, queryLen)
local bestScore = 0
@@ -1678,17 +1678,18 @@ function Database:ScoreFuzzy(text, query, queryLen)
return bestScore
end
--- Check if all characters of `query` appear in order within `word`.
-- Requires first character match to avoid spurious hits like "ahn'" in "magtheridon's".
function Database:IsSubsequence(word, query, queryLen)
if ssub(word, 1, 1) ~= ssub(query, 1, 1) then return false end
local wi = 1
local wordLen = #word
+ local firstPos
for qi = 1, queryLen do
local qc = ssub(query, qi, qi)
local found = false
while wi <= wordLen do
if ssub(word, wi, wi) == qc then
+ if not firstPos then firstPos = wi end
wi = wi + 1
found = true
break
@@ -1697,11 +1698,12 @@ function Database:IsSubsequence(word, query, queryLen)
end
if not found then return false end
end
- return true
+ -- Reject sparse matches: "inn" in "instance" matches i(1)n(2)n(6) but span 6 > 5
+ return (wi - 1) - firstPos + 1 <= queryLen * 2 - 1
end
---- Damerau-Levenshtein distance (supports transpositions).
---- Capped: returns early if distance exceeds 2 (saves CPU).
+-- Damerau-Levenshtein distance (supports transpositions).
+-- Capped: returns early if distance exceeds 2 (saves CPU).
function Database:DamerauLevenshtein(s1, s2, len1, len2)
if mabs(len1 - len2) > 2 then return 3 end -- too different, skip
@@ -1735,9 +1737,9 @@ function Database:DamerauLevenshtein(s1, s2, len1, len2)
return prev[len2]
end
---- Unified name scoring: exact → starts-with → word-boundary → substring → initials → fuzzy.
---- All search features (UI, map zone, map POI) use this single function.
---- Returns a score ≥ 0. Caller decides the minimum threshold.
+-- Unified name scoring: exact → starts-with → word-boundary → substring → initials → fuzzy.
+-- All search features (UI, map zone, map POI) use this single function.
+-- Returns a score ≥ 0. Caller decides the minimum threshold.
function Database:ScoreName(nameLower, query, queryLen, optQueryWords)
-- Trim trailing whitespace so "windrnr " behaves like "windrnr"
if ssub(query, queryLen, queryLen) == " " then
@@ -1880,8 +1882,8 @@ function Database:ScoreName(nameLower, query, queryLen, optQueryWords)
return score
end
---- Unified keyword scoring: additive score from matching against a list of keywords.
---- Returns a total score to ADD to the name score.
+-- Unified keyword scoring: additive score from matching against a list of keywords.
+-- Returns a total score to ADD to the name score.
function Database:ScoreKeywords(keywordsLower, query, queryLen, optQueryWords)
if not keywordsLower then return 0 end
diff --git a/Rescaler.lua b/Rescaler.lua
index d42d7eb..6440844 100644
--- a/Rescaler.lua
+++ b/Rescaler.lua
@@ -586,9 +586,13 @@ end
-- Done panel
local function CreateDonePanel(parent)
- local totalW = 110 + 2 + 80
+ local BACK_W = 110
+ local DONE_W = 80
+ local BTN_GAP = 2
+ local BTN_H = 22
+ local PANEL_PAD = 8
local panel = CreateFrame("Frame", nil, parent, "BackdropTemplate")
- panel:SetSize(totalW + 16, 22 + 14)
+ panel:SetSize(BACK_W + BTN_GAP + DONE_W + PANEL_PAD * 2, BTN_H + 14)
panel:SetFrameStrata("FULLSCREEN_DIALOG")
panel:SetFrameLevel(209)
panel:SetBackdrop({
@@ -600,14 +604,14 @@ local function CreateDonePanel(parent)
panel:SetBackdropColor(DARK_PANEL_BG[1], DARK_PANEL_BG[2], DARK_PANEL_BG[3], DARK_PANEL_BG[4])
local backBtn = CreateFrame("Button", nil, panel, "UIPanelButtonTemplate")
- backBtn:SetSize(110, 22)
- backBtn:SetPoint("LEFT", panel, "LEFT", 8, 0)
+ backBtn:SetSize(BACK_W, BTN_H)
+ backBtn:SetPoint("LEFT", panel, "LEFT", PANEL_PAD, 0)
backBtn:SetText("Back to Options")
panel.backBtn = backBtn
local doneBtn = CreateFrame("Button", nil, panel, "UIPanelButtonTemplate")
- doneBtn:SetSize(80, 22)
- doneBtn:SetPoint("LEFT", backBtn, "RIGHT", 2, 0)
+ doneBtn:SetSize(DONE_W, BTN_H)
+ doneBtn:SetPoint("LEFT", backBtn, "RIGHT", BTN_GAP, 0)
doneBtn:SetText("Done")
panel.doneBtn = doneBtn
@@ -771,7 +775,7 @@ function Rescaler:Enter(mode)
setBarWidth(v)
end)
AddResetButton(barOverlay.widthBox, function()
- local defW = 250
+ local defW = 250 * 0.88 -- 220px (matches DB_DEFAULTS searchWidth = 0.88)
setBarWidth(defW)
barOverlay.widthBox:SetText(mfloor(defW + 0.5))
end)
@@ -796,9 +800,9 @@ function Rescaler:Enter(mode)
self:ClearFocus()
end)
AddResetButton(barOverlay.fontBox, function()
- SetFontScale(1.0)
+ SetFontScale(0.9)
ApplyFontUpdate()
- barOverlay.fontBox:SetText(100)
+ barOverlay.fontBox:SetText(90)
previewResults:UpdatePreviewFont()
end)
SetupFontDrag(barOverlay.bottomHandle, barOverlay.fontBox, previewResults)
@@ -817,7 +821,7 @@ function Rescaler:Enter(mode)
previewResults:SetWidth(v)
end)
AddResetButton(resultsOverlay.widthBox, function()
- local defW = (mode == "ui") and 350 or 380
+ local defW = 300
setResultsWidth(defW)
previewResults:SetWidth(defW)
resultsOverlay.widthBox:SetText(mfloor(defW + 0.5))
From d6bd2103c3982dc46bf12d28fca806925696d577 Mon Sep 17 00:00:00 2001
From: wowaddonmaker
Date: Tue, 17 Mar 2026 06:04:40 -0400
Subject: [PATCH 08/10] Regenerate POI data and update README
---
README.md | 76 +++++++++++++++++++++-----------------
StaticLocations.lua | 89 +++++++++++++++++++++++++++++++--------------
2 files changed, 103 insertions(+), 62 deletions(-)
diff --git a/README.md b/README.md
index b28c967..dbdf741 100644
--- a/README.md
+++ b/README.md
@@ -2,6 +2,16 @@
EasyFind lets you search for any panel, tab, or setting in WoW's interface and any point of interest on the map. Type what you're looking for, and EasyFind walks you to it step by step, or if you prefer, opens it directly.
+
+
+Guide mode walks you through each step
+
+
+
+
+Direct open skips straight to the panel instantly
+
+
## Features
### UI Search
@@ -22,13 +32,8 @@ Find and navigate to any interface element such as:
* Coverage is always expanding. If a panel exists in the default UI, the goal is for EasyFind to reach it
-
-Guide mode walks you through each step
-
-
-
-
-Direct open skips straight to the panel
+
+Mount and toy search with click-to-summon. Results are scrollable with full category paths shown.
Currencies show your current amounts inline, and reputations display progress bars with renown level, friendship rank, or traditional standing.
@@ -39,13 +44,6 @@ Currencies show your current amounts inline, and reputations display progress ba
Currency amounts and reputation progress bars in search results
-Results are scrollable, so even broad searches like "rated" that would normally get cut off by the window show every match.
-
-
-
-Scrollable results with full category paths
-
-
### Map Search
Locate places and NPCs across Azeroth:
@@ -60,6 +58,7 @@ Locate places and NPCs across Azeroth:
* Stable masters, void storage, and guild services
* The Great Vault, Creation Catalyst, and Trading Post
* Chromie (Timewalking Campaigns)
+
Search the zone of the map you're currently focused on with the local bar (left side), or search across all of Azeroth with the global bar (right side). Map search results also appear in the UI search bar when the Map Search filter is enabled.
@@ -93,11 +92,12 @@ The global bar finds zones, dungeons, raids, and delves with full breadcrumb pat
Pinned 3v3 Arena appears at the top of every search
-* **Category Filters**: Narrow results by type. Filter global results by zones, dungeons, raids, and delves. Filter local results by instances, travel, and services.
+* **Category Filters**: Narrow results by type. Filter global results by zones, dungeons, raids, and delves. Filter local results by instances, travel, and services. The UI search bar also has its own filter to mix in map results, mounts, toys, and pets alongside UI results.
-
-Filter dropdown for global map search
+
+
+UI search bar filter (left) and map search filter (right)
* **Fuzzy and Abbreviation Matching**: Vowel-stripped abbreviations like `qtr` find "quartermaster". Multi-word queries like `twlght hghlnds` find "Twilight Highlands".
@@ -115,13 +115,19 @@ The global bar finds zones, dungeons, raids, and delves with full breadcrumb pat
### Two Navigation Modes
* **Guide Mode** (default): Walks you through each step to reach your destination. Highlights the correct button or tab with a yellow pulsing border and an animated arrow so you learn where things live.
-* **Direct Open Mode**: Opens panels and tabs directly with no extra steps, for when you already know the UI and just want the convenience.
+* **Direct Open Mode**: Opens panels and tabs directly in a single instant, for when you already know the UI and just want the convenience.
-Toggle between modes using the mode button in the search bar (click the magnifying glass icon) or through the options panel. UI search and map search each have their own toggle. Enable **Open Panels Directly** (under UI Search options) to skip step-by-step guidance for interface panels. Enable **Navigate Zones Directly** (under Map Search options) to jump straight to a zone on the map instead of stepping through parent zones. Both are off by default.
+Toggle between modes using the mode button on the left of each search bar. The icon changes to reflect the current mode. UI search and map search each have their own toggle.
-
-Navigate Zones Directly jumps straight to the target zone
+
+
+Standard mode (left) and Fast mode (right) toggle button states
+
+
+
+
+Fast mode jumps straight to the target zone
## How to Use
@@ -142,10 +148,10 @@ Results show their full path so you always know where you're going:
## First-Time Setup
-When you install EasyFind for the first time, you'll see an interactive setup overlay that helps you position and resize the search bar. Simply drag the bar where you want it and use the corner handle to adjust the size, then click **Done** when ready. You can always resize it later through the options menu (/ef o) and reposition it later by holding **Shift** and dragging.
+When you install EasyFind for the first time, you'll see an interactive setup overlay that helps you position and resize the search bar. Simply drag the bar where you want it and use the corner handle to adjust the size, then click **Done** when ready. You can always resize it later through the options panel (`/ef`) and reposition it by holding **Shift** and dragging.
-
+
First-time setup overlay
@@ -153,17 +159,16 @@ When you install EasyFind for the first time, you'll see an interactive setup ov
| Command |Description |
| --------- |------------------------------------ |
-| /ef o |Open the options panel |
-| /ef hide |Hide the search bar |
-| /ef show |Show the search bar |
-| /ef clear |Dismiss all active highlights and guides |
-| /ef reset |Reset all settings to defaults (opens confirmation dialog) |
+| /ef /ef o |Open the options panel |
+| /ef toggle /ef t |Show/hide the search bar |
+| /ef clear /ef c |Dismiss all active highlights and guides |
+| /ef reset /ef r |Reset all settings to defaults (opens confirmation dialog) |
| /ef setup |Re-run the first-time setup overlay |
| /ef whatsnew |Show the What's New dialog for the current version |
| /ef bug |Get a link to submit a bug report on GitHub |
| /ef feature |Get a link to submit a feature request on GitHub |
-Options are also available via ESC > Interface > AddOns > EasyFind, or through the addon compartment button.
+Options are also accessible via ESC > Interface > AddOns > EasyFind or through the addon compartment button.
## Keybinds
@@ -179,7 +184,7 @@ EasyFind provides customizable keybinds:
## Options
-
+
Options panel
@@ -192,8 +197,9 @@ EasyFind provides customizable keybinds:
* **Panel Opacity**: Adjust the options panel background transparency.
### UI Search
+
* **Enable UI Search Module**: Toggle the entire UI search feature on or off (requires reload).
-* **Open Panels Directly**: Selecting a UI result opens the target panel immediately instead of guiding you through each step. Off by default.
+* **Open Panels Directly**: Selecting a UI result opens the target panel instantly instead of guiding you through each step. Off by default.
* **Smart Show**: Hide the search bar until you hover over it.
@@ -224,16 +230,18 @@ EasyFind provides customizable keybinds:
* **Hide Bars in Full Screen Map**: Hide both map search bars when the map is maximized. Bars reappear in windowed mode.
* **Blinking Map Pins**: Pins and highlight boxes pulse in sync with the indicator arrow. Off by default. The indicator arrow always bobs regardless.
* **Pin Highlight Box**: Toggle the yellow highlight box around map pins. Indicator arrow and pin icon remain visible either way.
-* **Minimap Arrow Glow**: Pulsing glow effect on the minimap perimeter arrow during navigation.
-* **Minimap Guide Circle**: Ring and directional arrow around your character on the minimap when navigating nearby.
+* **Minimap Arrow Glow**: Pulsing glow effect on the minimap perimeter arrow during navigation. Sub-option: **Only EasyFind Pins** — restrict the glow to waypoints placed by EasyFind, ignoring others.
+* **Minimap Guide Circle**: Ring and directional arrow around your character on the minimap when navigating nearby. Sub-option: **Only EasyFind Pins** — restrict the guide circle to waypoints placed by EasyFind, ignoring others.
* **Map Pin Glow**: Pulsing glow on the map pin when the guide circle shrinks onto it.
* **Auto Map Pin Clear**: Automatically clear map pins when you arrive at the destination.
* **Auto Track Map Pins**: Automatically supertrack newly placed map pins for minimap arrow guidance.
* **Map Search Y-Offset**: Adjust vertical position of map search bars relative to the map bottom edge.
* **Category Filters**: Filter global results by zones, dungeons, raids, and delves. Filter local results by instances, travel, and services.
+Map Search options are organized into grouped dropdowns (Search Bars, Map Pins, Minimap, Automation) to keep related settings together.
+
### Reset
-* **Reset All Settings**: Return all settings to defaults.
+* **Reset Settings**: Return all settings to defaults.
* **Reset Positions**: Return search bar positions to defaults.
## Moving the Search Bars
diff --git a/StaticLocations.lua b/StaticLocations.lua
index 439ded8..786bff3 100644
--- a/StaticLocations.lua
+++ b/StaticLocations.lua
@@ -1,9 +1,13 @@
--- EasyFind Static Locations (auto-generated 2026-03-11 01:48)
-local _, ns = ...
+-- EasyFind Static Locations (auto-generated 2026-03-17 03:17)
+local ADDON_NAME, ns = ...
ns.STATIC_LOCATIONS = {
+ [18] = { -- Tirisfal Glades
+ { name = "Portal to Stranglethorn Vale", category = "portal", x = 0.6142756296653066, y = 0.5879643697403164, keywords = {"portal", "teleport", "mage"} },
+ { name = "Portal to Orgrimmar", category = "portal", x = 0.6107386922901901, y = 0.5889822784330836, keywords = {"portal", "teleport", "mage"} },
+ { name = "Portal to Howling Fjord", category = "portal", x = 0.5946528870491194, y = 0.5878667441489194, keywords = {"portal", "teleport", "mage"} },
+ },
[84] = { -- Stormwind City
- { name = "Inn", category = "innkeeper", x = 0.6076649384171665, y = 0.7474117877548194, keywords = {"inn", "innkeeper", "hearth", "rest"} },
- { name = "Auction House", category = "auctionhouse", x = 0.6137234451764463, y = 0.7151582408447317, keywords = {"auction", "ah"} },
+ { name = "Innkeeper", category = "innkeeper", x = 0.6076649384171665, y = 0.7474117877548194, keywords = {"inn", "innkeeper", "hearth", "rest"} },
{ name = "Personal Bank", category = "bank", x = 0.6287458295533704, y = 0.7815457034658084, keywords = {"bank", "vault", "storage"} },
{ name = "Guild Vault", category = "bank", x = 0.6353951605252898, y = 0.799470813899537, keywords = {"bank", "vault", "storage"} },
{ name = "PvP Mount Vendor", category = "vendor", x = 0.768484854436593, y = 0.6515890235802723, keywords = {"vendor", "merchant", "shop"} },
@@ -11,14 +15,13 @@ ns.STATIC_LOCATIONS = {
{ name = "Deeprun Tram", category = "tram", x = 0.6666872667606668, y = 0.3468250224608364, keywords = {"tram", "deeprun"} },
{ name = "Personal Bank", category = "bank", x = 0.6448791374651772, y = 0.2852704736326513, keywords = {"bank", "vault", "storage"} },
{ name = "Guild Vault", category = "bank", x = 0.657062636416047, y = 0.2719092533262282, keywords = {"bank", "vault", "storage"} },
- { name = "Auction House", category = "auctionhouse", x = 0.6061703023504855, y = 0.3197927819839693, keywords = {"auction", "ah"} },
{ name = "Training Dummies", category = "trainingdummy", x = 0.7981892074620177, y = 0.6390044377338833, keywords = {"training", "dummy", "dummies", "target", "practice"} },
{ name = "Chromie", category = "chromie", x = 0.5570498939412215, y = 0.1731803309699413, keywords = {"chromie", "timewalking", "time", "leveling"} },
{ name = "Inscription Trainer", category = "prof_inscription", x = 0.4986017644405365, y = 0.7380831241607666, keywords = {"profession", "trainer", "inscription", "glyph"} },
{ name = "Honor & Conquest Quartermasters", category = "quartermaster", x = 0.7564612627029419, y = 0.6661708354949951, keywords = {"quartermaster", "vendor", "gear", "pvp"} },
{ name = "Rogue", category = "classtrainer", x = 0.8000873923301697, y = 0.7070390582084656, keywords = {"trainer", "class", "rogue"} },
{ name = "Trading Post", category = "tradingpost", x = 0.5099332928657532, y = 0.7184862494468689, keywords = {"tmog", "xmog", "trading post", "tender"} },
- { name = "Dwarven District Inn", category = "innkeeper", x = 0.6438179612159729, y = 0.3245589137077332, keywords = {"inn", "innkeeper", "hearth", "rest", "tavern"} },
+ { name = "Innkeeper", category = "innkeeper", x = 0.6438179612159729, y = 0.3245589137077332, keywords = {"inn", "innkeeper", "hearth", "rest", "tavern"} },
{ name = "Guild Master & Vendor", category = "guildservices", x = 0.644954264163971, y = 0.769818127155304, keywords = {"guild", "tabard", "registrar"} },
{ name = "Stormwind Harbor", category = "boat", x = 0.4217675030231476, y = 0.4986781179904938, keywords = {"boat", "ship", "ferry", "travel"} },
{ name = "Dwarven District Bank", category = "bank", x = 0.6469756364822388, y = 0.2866156697273254, keywords = {"bank", "vault", "storage"} },
@@ -27,7 +30,7 @@ ns.STATIC_LOCATIONS = {
{ name = "Shaman", category = "classtrainer", x = 0.7872968912124634, y = 0.7087392210960388, keywords = {"trainer", "class", "shaman"} },
{ name = "Monk", category = "classtrainer", x = 0.8032164573669434, y = 0.7010206580162048, keywords = {"trainer", "class", "monk"} },
{ name = "Fishing Trainer", category = "prof_fishing", x = 0.5479564666748047, y = 0.695954442024231, keywords = {"profession", "trainer", "fishing"} },
- { name = "Trade District Inn", category = "innkeeper", x = 0.6086640954017639, y = 0.7483740448951721, keywords = {"inn", "innkeeper", "hearth", "rest", "tavern"} },
+ { name = "Innkeeper", category = "innkeeper", x = 0.6086640954017639, y = 0.7483740448951721, keywords = {"inn", "innkeeper", "hearth", "rest", "tavern"} },
{ name = "Transmogrification", category = "transmogrifier", x = 0.5037522315979004, y = 0.6054830551147461, keywords = {"transmog", "transmogrifier", "appearance"} },
{ name = "Demon Hunter", category = "classtrainer", x = 0.4008559286594391, y = 0.7816379070281982, keywords = {"trainer", "class", "demon hunter", "dh"} },
{ name = "Warlock", category = "classtrainer", x = 0.7952552437782288, y = 0.698750913143158, keywords = {"trainer", "class", "warlock"} },
@@ -38,7 +41,7 @@ ns.STATIC_LOCATIONS = {
{ name = "Justice & Valor Quartermasters", category = "quartermaster", x = 0.7913053035736084, y = 0.6983883380889893, keywords = {"quartermaster", "vendor", "gear"} },
{ name = "Dwarven District Stable Master", category = "stablemaster", x = 0.6721125841140747, y = 0.3772968649864197, keywords = {"stable", "pet"} },
{ name = "Cooking Trainer", category = "prof_cooking", x = 0.7524042725563049, y = 0.5515660047531128, keywords = {"profession", "trainer", "cooking", "food"} },
- { name = "Dwarven District Auction House", category = "auctionhouse", x = 0.6128095984458923, y = 0.3177553713321686, keywords = {"auction", "ah"} },
+ { name = "Auction House", category = "auctionhouse", x = 0.6128095984458923, y = 0.3177553713321686, keywords = {"auction", "ah"} },
{ name = "Old Town Stable Master", category = "stablemaster", x = 0.7780223488807678, y = 0.6729971766471863, keywords = {"stable", "pet"} },
{ name = "Tailoring Trainer", category = "prof_tailoring", x = 0.540519118309021, y = 0.8164535164833069, keywords = {"profession", "trainer", "tailoring", "cloth"} },
{ name = "Leatherworking Trainer", category = "prof_leatherworking", x = 0.7242438197135925, y = 0.6233569383621216, keywords = {"profession", "trainer", "leatherworking", "leather", "lw"} },
@@ -49,7 +52,7 @@ ns.STATIC_LOCATIONS = {
{ name = "Skinning Trainer", category = "prof_skinning", x = 0.7242438197135925, y = 0.6233569383621216, keywords = {"profession", "trainer", "skinning"} },
{ name = "Jewelcrafting Trainer", category = "prof_jewelcrafting", x = 0.6338410973548889, y = 0.6161665916442871, keywords = {"profession", "trainer", "jewelcrafting", "gem", "jc"} },
{ name = "Alchemy Trainer", category = "prof_alchemy", x = 0.5519677400588989, y = 0.8492006063461304, keywords = {"profession", "trainer", "alchemy", "potion", "flask"} },
- { name = "Lion's Rest", category = "innkeeper", x = 0.3970444798469544, y = 0.6654699444770813, keywords = {"inn", "innkeeper", "hearth", "rest", "tavern"} },
+ { name = "Innkeeper", category = "innkeeper", x = 0.3970444798469544, y = 0.6654699444770813, keywords = {"inn", "innkeeper", "hearth", "rest", "tavern"} },
{ name = "First Aid Trainer", category = "prof_firstaid", x = 0.5218287706375122, y = 0.4540798366069794, keywords = {"profession", "trainer", "first aid", "bandage"} },
{ name = "Druid", category = "classtrainer", x = 0.7922032475471497, y = 0.6870524883270264, keywords = {"trainer", "class", "druid"} },
{ name = "Barber", category = "barber", x = 0.6130751371383667, y = 0.6554045677185059, keywords = {"barber", "appearance", "haircut"} },
@@ -57,7 +60,17 @@ ns.STATIC_LOCATIONS = {
{ name = "Priest", category = "classtrainer", x = 0.7892311811447144, y = 0.6978873014450073, keywords = {"trainer", "class", "priest"} },
{ name = "Archaeology Trainer", category = "prof_archaeology", x = 0.8574860692024231, y = 0.2582310140132904, keywords = {"profession", "trainer", "archaeology", "artifact", "dig"} },
{ name = "Riding", category = "ridingtrainer", x = 0.7736784219741821, y = 0.677047073841095, keywords = {"riding", "mount", "trainer"} },
- { name = "Trade District Auction House", category = "auctionhouse", x = 0.6159144639968872, y = 0.7224318981170654, keywords = {"auction", "ah"} },
+ { name = "Auction House", category = "auctionhouse", x = 0.6159144639968872, y = 0.7224318981170654, keywords = {"auction", "ah"} },
+ { name = "Mailbox", category = "mailbox", x = 0.6203915435934758, y = 0.7459549778832446, keywords = {"mail", "mailbox"} },
+ { name = "Mailbox", category = "mailbox", x = 0.7453513005682457, y = 0.5606627266802539, keywords = {"mail", "mailbox"} },
+ { name = "Mailbox", category = "mailbox", x = 0.6319651296326152, y = 0.3160645084610606, keywords = {"mail", "mailbox"} },
+ { name = "Mailbox", category = "mailbox", x = 0.6106127373316832, y = 0.5048737603591674, keywords = {"mail", "mailbox"} },
+ { name = "Mailbox", category = "mailbox", x = 0.5511944215572009, y = 0.570221667545563, keywords = {"mail", "mailbox"} },
+ { name = "Mailbox", category = "mailbox", x = 0.3796705941357105, y = 0.3495751121252797, keywords = {"mail", "mailbox"} },
+ { name = "Mailbox", category = "mailbox", x = 0.5065343745894832, y = 0.7157498434722376, keywords = {"mail", "mailbox"} },
+ { name = "Mailbox", category = "mailbox", x = 0.569003059844468, y = 0.7177869582605291, keywords = {"mail", "mailbox"} },
+ { name = "Mailbox", category = "mailbox", x = 0.4982730493422765, y = 0.8637194336242191, keywords = {"mail", "mailbox"} },
+ { name = "Mailbox", category = "mailbox", x = 0.7581761591013239, y = 0.6375443493587201, keywords = {"mail", "mailbox"} },
},
[85] = { -- Orgrimmar
{ name = "Legacy PvP Vendors", category = "pvpvendor", x = 0.3907181451218901, y = 0.7160247475023476, keywords = {"pvp", "honor", "conquest", "vendor"} },
@@ -68,7 +81,7 @@ ns.STATIC_LOCATIONS = {
{ name = "Auction House", category = "auctionhouse", x = 0.5349350017752525, y = 0.7363798847906623, keywords = {"auction", "ah"} },
{ name = "Guild Vault", category = "bank", x = 0.490665680028789, y = 0.8229210799901849, keywords = {"bank", "vault", "storage"} },
{ name = "Personal Bank", category = "bank", x = 0.4886792057331894, y = 0.8235170427750655, keywords = {"bank", "vault", "storage"} },
- { name = "Inn", category = "innkeeper", x = 0.5422792184385153, y = 0.7836225401418597, keywords = {"inn", "innkeeper", "hearth", "rest"} },
+ { name = "Innkeeper", category = "innkeeper", x = 0.5422792184385153, y = 0.7836225401418597, keywords = {"inn", "innkeeper", "hearth", "rest"} },
{ name = "Trading Post", category = "tradingpost", x = 0.4855013309342473, y = 0.7614878981268949, keywords = {"tmog", "xmog", "trading post", "tender"} },
{ name = "Guild Vendor", category = "vendor", x = 0.4728320593471657, y = 0.7919164356998947, keywords = {"vendor", "merchant", "shop"} },
{ name = "Tabard Vendor", category = "vendor", x = 0.4714175288843486, y = 0.7892705878650561, keywords = {"vendor", "merchant", "shop"} },
@@ -96,7 +109,6 @@ ns.STATIC_LOCATIONS = {
{ name = "Monk", category = "classtrainer", x = 0.7567005753517151, y = 0.4360591769218445, keywords = {"trainer", "class", "monk"} },
{ name = "Fishing Trainer", category = "prof_fishing", x = 0.6658633947372437, y = 0.4195621311664581, keywords = {"profession", "trainer", "fishing"} },
{ name = "Transmogrification", category = "transmogrifier", x = 0.5770488977432251, y = 0.6514323353767395, keywords = {"transmog", "transmogrifier", "appearance"} },
- { name = "Orgrimmar Battlemasters", category = "battlemasters", x = 0.7444835305213928, y = 0.4525915086269379, keywords = {"pvp", "battlemaster", "arena", "battleground", "bg"} },
{ name = "Warlock", category = "classtrainer", x = 0.7461960315704346, y = 0.4737443327903748, keywords = {"trainer", "class", "warlock"} },
{ name = "Archaeology Trainer", category = "prof_archaeology", x = 0.4905875325202942, y = 0.705586850643158, keywords = {"profession", "trainer", "archaeology", "artifact", "dig"} },
{ name = "Engineering Trainer", category = "prof_engineering", x = 0.5698520541191101, y = 0.5631312131881714, keywords = {"profession", "trainer", "engineering"} },
@@ -122,6 +134,10 @@ ns.STATIC_LOCATIONS = {
{ name = "Demon Hunter", category = "classtrainer", x = 0.4876908957958221, y = 0.5688652396202087, keywords = {"trainer", "class", "demon hunter", "dh"} },
{ name = "Riding", category = "ridingtrainer", x = 0.613136351108551, y = 0.3460921943187714, keywords = {"riding", "mount", "trainer"} },
{ name = "Tailoring Trainer", category = "prof_tailoring", x = 0.5984829664230347, y = 0.589553952217102, keywords = {"profession", "trainer", "tailoring", "cloth"} },
+ { name = "Resonance Crystal Quartermaster", category = "quartermaster", x = 0.505689027556145, y = 0.4027231529896209, keywords = {"quartermaster", "vendor", "gear"} },
+ { name = "Innkeeper", category = "innkeeper", x = 0.3946381481066923, y = 0.4814661402414124, keywords = {"inn", "innkeeper", "hearth", "rest", "tavern"} },
+ { name = "Auction House", category = "auctionhouse", x = 0.4115980702118528, y = 0.4815211086083919, keywords = {"auction", "ah"} },
+ { name = "Decor Specialist", category = "decor", x = 0.3846251852799343, y = 0.7150224420721689, keywords = {"decor", "decoration", "housing", "furniture"} },
},
[87] = { -- Ironforge
{ name = "Inscription Trainer", category = "prof_inscription", x = 0.601420521736145, y = 0.4407650828361511, keywords = {"profession", "trainer", "inscription", "glyph"} },
@@ -154,12 +170,14 @@ ns.STATIC_LOCATIONS = {
{ name = "Barber", category = "barber", x = 0.2576498985290527, y = 0.5121992230415344, keywords = {"barber", "appearance", "haircut"} },
{ name = "Stable Master", category = "stablemaster", x = 0.6936774253845215, y = 0.8357920050621033, keywords = {"stable", "pet"} },
{ name = "Priest", category = "classtrainer", x = 0.2692385017871857, y = 0.1095127537846565, keywords = {"trainer", "class", "priest"} },
- { name = "The Inn", category = "innkeeper", x = 0.2010810524225235, y = 0.533050000667572, keywords = {"inn", "innkeeper", "hearth", "rest", "tavern"} },
+ { name = "Innkeeper", category = "innkeeper", x = 0.2010810524225235, y = 0.533050000667572, keywords = {"inn", "innkeeper", "hearth", "rest", "tavern"} },
{ name = "Bank of Ironforge", category = "bank", x = 0.3514816761016846, y = 0.6115733981132507, keywords = {"bank", "vault", "storage"} },
{ name = "Mailbox", category = "mailbox", x = 0.2111763656139374, y = 0.5239894986152649, keywords = {"mail", "mailbox"} },
+ { name = "Deeprun Tram", category = "tram", x = 0.7697482787140112, y = 0.5135215968859295, keywords = {"tram", "deeprun"} },
+ { name = "Decor Specialist", category = "decor", x = 0.2433828642934768, y = 0.4352018766676832, keywords = {"decor", "decoration", "housing", "furniture"} },
},
[88] = { -- Thunder Bluff
- { name = "Inn", category = "innkeeper", x = 0.4569665789604187, y = 0.641059160232544, keywords = {"inn", "innkeeper", "hearth", "rest", "tavern"} },
+ { name = "Innkeeper", category = "innkeeper", x = 0.4569665789604187, y = 0.641059160232544, keywords = {"inn", "innkeeper", "hearth", "rest", "tavern"} },
{ name = "Stable Master", category = "stablemaster", x = 0.4482096433639526, y = 0.6038814783096313, keywords = {"stable", "pet"} },
{ name = "Rogue", category = "classtrainer", x = 0.59992516040802, y = 0.8470568656921387, keywords = {"trainer", "class", "rogue"} },
{ name = "Tailoring Trainer", category = "prof_tailoring", x = 0.4309437274932861, y = 0.4400903582572937, keywords = {"profession", "trainer", "tailoring", "cloth"} },
@@ -189,6 +207,7 @@ ns.STATIC_LOCATIONS = {
{ name = "Blacksmithing Trainer", category = "prof_blacksmithing", x = 0.3945163488388062, y = 0.5601218938827515, keywords = {"profession", "trainer", "blacksmithing"} },
{ name = "Bank", category = "bank", x = 0.4718787968158722, y = 0.5860607624053955, keywords = {"bank", "vault", "storage"} },
{ name = "Mailbox", category = "mailbox", x = 0.4525092840194702, y = 0.5943952798843384, keywords = {"mail", "mailbox"} },
+ { name = "Zeppelin", category = "zeppelin", x = 0.1490411679770558, y = 0.2566726379360351, keywords = {"zeppelin", "airship", "travel"} },
},
[89] = { -- Darnassus
{ name = "Inscription Trainer", category = "prof_inscription", x = 0.5610339641571045, y = 0.3240092992782593, keywords = {"profession", "trainer", "inscription", "glyph"} },
@@ -221,11 +240,11 @@ ns.STATIC_LOCATIONS = {
{ name = "Engineering Trainer", category = "prof_engineering", x = 0.496679037809372, y = 0.3277494311332703, keywords = {"profession", "trainer", "engineering"} },
{ name = "Stable Master", category = "stablemaster", x = 0.4318239986896515, y = 0.2895421087741852, keywords = {"stable", "pet"} },
{ name = "Priest", category = "classtrainer", x = 0.4303822219371796, y = 0.7842159271240234, keywords = {"trainer", "class", "priest"} },
- { name = "The Inn", category = "innkeeper", x = 0.6267794370651245, y = 0.3225485384464264, keywords = {"inn", "innkeeper", "hearth", "rest", "tavern"} },
+ { name = "Innkeeper", category = "innkeeper", x = 0.6267794370651245, y = 0.3225485384464264, keywords = {"inn", "innkeeper", "hearth", "rest", "tavern"} },
{ name = "Mailbox", category = "mailbox", x = 0.4494788348674774, y = 0.5086278319358826, keywords = {"mail", "mailbox"} },
},
[90] = { -- Undercity
- { name = "Inn", category = "innkeeper", x = 0.6798121929168701, y = 0.3726189136505127, keywords = {"inn", "innkeeper", "hearth", "rest", "tavern"} },
+ { name = "Innkeeper", category = "innkeeper", x = 0.6798121929168701, y = 0.3726189136505127, keywords = {"inn", "innkeeper", "hearth", "rest", "tavern"} },
{ name = "Herbalism Trainer", category = "prof_herbalism", x = 0.5460033416748047, y = 0.4996925890445709, keywords = {"profession", "trainer", "herbalism", "herb"} },
{ name = "Locksmith", category = "classtrainer", x = 0.8547717332839966, y = 0.5908311009407043, keywords = {"trainer", "class", "warlock"} },
{ name = "Cooking Trainer", category = "prof_cooking", x = 0.6238527894020081, y = 0.439919501543045, keywords = {"profession", "trainer", "cooking", "food"} },
@@ -251,6 +270,7 @@ ns.STATIC_LOCATIONS = {
{ name = "Fishing Trainer", category = "prof_fishing", x = 0.8173749446868896, y = 0.3093972206115723, keywords = {"profession", "trainer", "fishing"} },
{ name = "Bank", category = "bank", x = 0.6678679585456848, y = 0.4410302937030792, keywords = {"bank", "vault", "storage"} },
{ name = "Mailbox", category = "mailbox", x = 0.6814745664596558, y = 0.3831481039524078, keywords = {"mail", "mailbox"} },
+ { name = "Decor Specialist", category = "decor", x = 0.6272032592338413, y = 0.4795431074048037, keywords = {"decor", "decoration", "housing", "furniture"} },
},
[103] = { -- The Exodar
{ name = "Inscription Trainer", category = "prof_inscription", x = 0.3961431086063385, y = 0.3940853476524353, keywords = {"profession", "trainer", "inscription", "glyph"} },
@@ -266,7 +286,7 @@ ns.STATIC_LOCATIONS = {
{ name = "Warlock", category = "classtrainer", x = 0.4886894822120667, y = 0.1091349571943283, keywords = {"trainer", "class", "warlock"} },
{ name = "Archaeology Trainer", category = "prof_archaeology", x = 0.3364339470863342, y = 0.6636090278625488, keywords = {"profession", "trainer", "archaeology", "artifact", "dig"} },
{ name = "Engineering Trainer", category = "prof_engineering", x = 0.5464122295379639, y = 0.9220924973487854, keywords = {"profession", "trainer", "engineering"} },
- { name = "Inn", category = "innkeeper", x = 0.5959975719451904, y = 0.2153959572315216, keywords = {"inn", "innkeeper", "hearth", "rest", "tavern"} },
+ { name = "Innkeeper", category = "innkeeper", x = 0.5959975719451904, y = 0.2153959572315216, keywords = {"inn", "innkeeper", "hearth", "rest", "tavern"} },
{ name = "Herbalism Trainer", category = "prof_herbalism", x = 0.2778580784797669, y = 0.6251523494720459, keywords = {"profession", "trainer", "herbalism", "herb"} },
{ name = "Cooking Trainer", category = "prof_cooking", x = 0.5565381050109863, y = 0.2681139707565308, keywords = {"profession", "trainer", "cooking", "food"} },
{ name = "Hunter", category = "classtrainer", x = 0.4654114842414856, y = 0.893653929233551, keywords = {"trainer", "class", "hunter"} },
@@ -298,10 +318,10 @@ ns.STATIC_LOCATIONS = {
{ name = "Alchemy Trainer", category = "prof_alchemy", x = 0.6706629991531372, y = 0.1810963451862335, keywords = {"profession", "trainer", "alchemy", "potion", "flask"} },
{ name = "Mage", category = "classtrainer", x = 0.5826275944709778, y = 0.1925737410783768, keywords = {"trainer", "class", "mage"} },
{ name = "Druid", category = "classtrainer", x = 0.7140853404998779, y = 0.557202160358429, keywords = {"trainer", "class", "druid"} },
- { name = "The Silvermoon City Inn.", category = "innkeeper", x = 0.80031418800354, y = 0.5956547260284424, keywords = {"inn", "innkeeper", "hearth", "rest", "tavern"} },
+ { name = "Innkeeper", category = "innkeeper", x = 0.80031418800354, y = 0.5956547260284424, keywords = {"inn", "innkeeper", "hearth", "rest", "tavern"} },
{ name = "Warlock", category = "classtrainer", x = 0.7610905766487122, y = 0.4362403452396393, keywords = {"trainer", "class", "warlock"} },
{ name = "Priest", category = "classtrainer", x = 0.5428659319877625, y = 0.2686304748058319, keywords = {"trainer", "class", "priest"} },
- { name = "Inn", category = "innkeeper", x = 0.80031418800354, y = 0.5956547260284424, keywords = {"inn", "innkeeper", "hearth", "rest", "tavern"} },
+ { name = "Innkeeper", category = "innkeeper", x = 0.80031418800354, y = 0.5956547260284424, keywords = {"inn", "innkeeper", "hearth", "rest", "tavern"} },
{ name = "Stable Master", category = "stablemaster", x = 0.8282662630081177, y = 0.3097585141658783, keywords = {"stable", "pet"} },
{ name = "Mailbox", category = "mailbox", x = 0.879634439945221, y = 0.5083430409431458, keywords = {"mail", "mailbox"} },
},
@@ -312,7 +332,7 @@ ns.STATIC_LOCATIONS = {
{ name = "Aldor Bank", category = "bank", x = 0.4876004159450531, y = 0.289349377155304, keywords = {"bank", "vault", "storage"} },
{ name = "Alchemy Trainer", category = "prof_alchemy", x = 0.4350685477256775, y = 0.1962425112724304, keywords = {"profession", "trainer", "alchemy", "potion", "flask"} },
{ name = "Cooking Trainer", category = "prof_cooking", x = 0.6265783905982971, y = 0.6817435622215271, keywords = {"profession", "trainer", "cooking", "food"} },
- { name = "Scryers Inn", category = "innkeeper", x = 0.5609787106513977, y = 0.8151568174362183, keywords = {"inn", "innkeeper", "hearth", "rest"} },
+ { name = "Innkeeper", category = "innkeeper", x = 0.5609787106513977, y = 0.8151568174362183, keywords = {"inn", "innkeeper", "hearth", "rest"} },
{ name = "Aldor Stable", category = "stablemaster", x = 0.2875014245510101, y = 0.4780189096927643, keywords = {"stable", "pet"} },
{ name = "Scryers Bank", category = "bank", x = 0.5927804112434387, y = 0.6066201329231262, keywords = {"bank", "vault", "storage"} },
{ name = "Leatherworking Trainer", category = "prof_leatherworking", x = 0.6727644205093384, y = 0.6741307377815247, keywords = {"profession", "trainer", "leatherworking", "leather"} },
@@ -324,7 +344,7 @@ ns.STATIC_LOCATIONS = {
{ name = "Mining Trainer", category = "prof_mining", x = 0.4373812079429627, y = 0.901329755783081, keywords = {"profession", "trainer", "mining", "ore"} },
{ name = "Class Trainer", category = "trainer", x = 0.5399067401885986, y = 0.4474839568138123, keywords = {"trainer", "class"} },
{ name = "Scryer Gem Merchant", category = "vendor", x = 0.5457209944725037, y = 0.8272597193717957, keywords = {"vendor", "merchant", "shop"} },
- { name = "Aldor Inn", category = "innkeeper", x = 0.2802973091602325, y = 0.4899273812770844, keywords = {"inn", "innkeeper", "hearth", "rest"} },
+ { name = "Innkeeper", category = "innkeeper", x = 0.2802973091602325, y = 0.4899273812770844, keywords = {"inn", "innkeeper", "hearth", "rest"} },
{ name = "Engineering Trainer", category = "prof_engineering", x = 0.4373812079429627, y = 0.901329755783081, keywords = {"profession", "trainer", "engineering"} },
{ name = "Jewelcrafting Trainer", category = "prof_jewelcrafting", x = 0.4373812079429627, y = 0.901329755783081, keywords = {"profession", "trainer", "jewelcrafting", "gem"} },
{ name = "Aldor Gem Merchant", category = "vendor", x = 0.3565923571586609, y = 0.1950026601552963, keywords = {"vendor", "merchant", "shop"} },
@@ -334,13 +354,13 @@ ns.STATIC_LOCATIONS = {
{ name = "Alliance Battlemasters", category = "battlemasters", x = 0.6702150702476501, y = 0.3407713770866394, keywords = {"pvp", "battlemaster", "arena", "battleground", "bg"} },
{ name = "Archaeology Trainer", category = "prof_archaeology", x = 0.6266931295394897, y = 0.7034234404563904, keywords = {"profession", "trainer", "archaeology", "artifact", "dig"} },
{ name = "Guild Services", category = "guildservices", x = 0.5892795920372009, y = 0.4632092118263245, keywords = {"guild", "tabard", "registrar"} },
- { name = "World's End Tavern", category = "innkeeper", x = 0.7443743944168091, y = 0.3264971375465393, keywords = {"inn", "innkeeper", "hearth", "rest", "tavern"} },
+ { name = "Innkeeper", category = "innkeeper", x = 0.7443743944168091, y = 0.3264971375465393, keywords = {"inn", "innkeeper", "hearth", "rest", "tavern"} },
},
[1670] = { -- Oribos
{ name = "Appearance Agitator", category = "barber", x = 0.6418470740318298, y = 0.6455227732658386, keywords = {"barber", "appearance", "haircut"} },
{ name = "Bank", category = "bank", x = 0.6057606935501099, y = 0.2979814112186432, keywords = {"bank", "vault", "storage"} },
{ name = "Item Upgrade", category = "upgradevendor", x = 0.3461781740188599, y = 0.5749000310897827, keywords = {"upgrade", "flightstone", "crest"} },
- { name = "Master of Conflict", category = "mythicplus", x = 0.3461781740188599, y = 0.5749000310897827, keywords = {"mythic", "keystone", "m+"} },
+ { name = "Master of Conflict", category = "pvpvendor", x = 0.3461781740188599, y = 0.5749000310897827, keywords = {"pvp", "vendor", "honor", "conquest"} },
{ name = "Portal to Stormwind", category = "portal", x = 0.2097126096487045, y = 0.4600186347961426, keywords = {"portal", "teleport", "mage"} },
{ name = "Stable Master", category = "stablemaster", x = 0.5919630527496338, y = 0.7525683641433716, keywords = {"stable", "pet"} },
{ name = "Transmogrification", category = "transmogrifier", x = 0.6433634757995605, y = 0.6947534084320068, keywords = {"transmog", "transmogrifier", "appearance"} },
@@ -350,7 +370,7 @@ ns.STATIC_LOCATIONS = {
{ name = "Enchanting Trainer", category = "trainer", x = 0.483364075422287, y = 0.2920195758342743, keywords = {"trainer", "class"} },
{ name = "Engineering Trainer", category = "trainer", x = 0.3799149990081787, y = 0.4457260370254517, keywords = {"trainer", "class"} },
{ name = "Fishing Trainer", category = "trainer", x = 0.474556028842926, y = 0.2393774390220642, keywords = {"trainer", "class"} },
- { name = "Inn", category = "innkeeper", x = 0.7051870822906494, y = 0.5000748634338379, keywords = {"inn", "innkeeper", "hearth", "rest"} },
+ { name = "Innkeeper", category = "innkeeper", x = 0.7051870822906494, y = 0.5000748634338379, keywords = {"inn", "innkeeper", "hearth", "rest"} },
{ name = "Inscription Trainer", category = "trainer", x = 0.3672410249710083, y = 0.3688085377216339, keywords = {"trainer", "class"} },
{ name = "Jewelcrafting Trainer", category = "trainer", x = 0.3525134027004242, y = 0.4154130816459656, keywords = {"trainer", "class"} },
{ name = "Leatherworking Trainer", category = "trainer", x = 0.4248446226119995, y = 0.2684046626091003, keywords = {"trainer", "class"} },
@@ -368,7 +388,7 @@ ns.STATIC_LOCATIONS = {
{ name = "Fishing Trainer", category = "prof_fishing", x = 0.437747597694397, y = 0.7454063892364502, keywords = {"profession", "trainer", "fishing"} },
{ name = "Crafting Orders", category = "craftingorders", x = 0.3558356165885925, y = 0.6120127439498901, keywords = {"crafting", "orders", "work order"} },
{ name = "Engineering Trainer", category = "prof_engineering", x = 0.4225976467132568, y = 0.4877831637859345, keywords = {"profession", "trainer", "engineering"} },
- { name = "Inn", category = "innkeeper", x = 0.4758386611938477, y = 0.4772845804691315, keywords = {"inn", "innkeeper", "hearth", "rest", "tavern"} },
+ { name = "Innkeeper", category = "innkeeper", x = 0.4758386611938477, y = 0.4772845804691315, keywords = {"inn", "innkeeper", "hearth", "rest", "tavern"} },
{ name = "Herbalism Trainer", category = "prof_herbalism", x = 0.3793559074401856, y = 0.6867225766181946, keywords = {"profession", "trainer", "herbalism", "herb"} },
{ name = "Item Upgrades", category = "upgradevendor", x = 0.4520639777183533, y = 0.3853875696659088, keywords = {"upgrade", "flightstone", "crest"} },
{ name = "Cooking Trainer", category = "prof_cooking", x = 0.4656898081302643, y = 0.4625963866710663, keywords = {"profession", "trainer", "cooking", "food"} },
@@ -394,7 +414,7 @@ ns.STATIC_LOCATIONS = {
{ name = "The Catalyst", category = "catalyst", x = 0.5006916236516065, y = 0.5379032248184877, keywords = {"catalyst", "tier", "upgrade", "revival"} },
{ name = "Trading Post", category = "tradingpost", x = 0.4481041519808515, y = 0.5564175485241932, keywords = {"tmog", "xmog", "trading post", "tender"} },
{ name = "Limited Time Trading Post", category = "tradingpost", x = 0.4769078164914132, y = 0.5201866703974088, keywords = {"tmog", "xmog", "trading post", "tender"} },
- { name = "Inn", category = "innkeeper", x = 0.4487001156877553, y = 0.4673510012048201, keywords = {"inn", "innkeeper", "hearth", "rest"} },
+ { name = "Innkeeper", category = "innkeeper", x = 0.4487001156877553, y = 0.4673510012048201, keywords = {"inn", "innkeeper", "hearth", "rest"} },
{ name = "Personal Bank", category = "bank", x = 0.5344711616237862, y = 0.4476868296660796, keywords = {"bank", "vault", "storage"} },
{ name = "Guild Vault", category = "bank", x = 0.5446903626858112, y = 0.4358468471675652, keywords = {"bank", "vault", "storage"} },
{ name = "Auction House", category = "auctionhouse", x = 0.5645526467105717, y = 0.4785488209149129, keywords = {"auction", "ah"} },
@@ -427,7 +447,7 @@ ns.STATIC_LOCATIONS = {
{ name = "Mining Trainer", category = "prof_mining", x = 0.526250422000885, y = 0.5255357027053833, keywords = {"profession", "trainer", "mining", "ore"} },
{ name = "Enchanting Trainer", category = "prof_enchanting", x = 0.5272791385650635, y = 0.7128501534461975, keywords = {"profession", "trainer", "enchanting"} },
{ name = "Engineering Trainer", category = "prof_engineering", x = 0.4908933043479919, y = 0.5615143775939941, keywords = {"profession", "trainer", "engineering"} },
- { name = "Challenger's Rest", category = "innkeeper", x = 0.5356837511062622, y = 0.387307196855545, keywords = {"inn", "innkeeper", "hearth", "rest"} },
+ { name = "Innkeeper", category = "innkeeper", x = 0.5356837511062622, y = 0.387307196855545, keywords = {"inn", "innkeeper", "hearth", "rest"} },
{ name = "Black Market Auction House", category = "auctionhouse", x = 0.6463407278060913, y = 0.5241857171058655, keywords = {"auction", "ah"} },
},
[2393] = { -- Silvermoon City
@@ -442,7 +462,7 @@ ns.STATIC_LOCATIONS = {
{ name = "Great Vault", category = "greatvault", x = 0.5027613043785095, y = 0.6507132649421692, keywords = {"vault", "great vault", "weekly", "chest"} },
{ name = "Engineering Trainer", category = "prof_engineering", x = 0.4352691471576691, y = 0.5402756333351135, keywords = {"profession", "trainer", "engineering"} },
{ name = "Black Market Auction House", category = "auctionhouse", x = 0.5185973644256592, y = 0.485391229391098, keywords = {"auction", "ah"} },
- { name = "Inn", category = "innkeeper", x = 0.5628485083580017, y = 0.7034760117530823, keywords = {"inn", "innkeeper", "hearth", "rest", "tavern"} },
+ { name = "Innkeeper", category = "innkeeper", x = 0.5628485083580017, y = 0.7034760117530823, keywords = {"inn", "innkeeper", "hearth", "rest", "tavern"} },
{ name = "Portal to the Timeways", category = "portal", x = 0.4210474193096161, y = 0.5829678773880005, keywords = {"portal", "teleport", "mage"} },
{ name = "The Catalyst", category = "catalyst", x = 0.4013126194477081, y = 0.6528624296188354, keywords = {"catalyst", "tier", "upgrade", "revival"} },
{ name = "Jewelcrafting Trainer", category = "prof_jewelcrafting", x = 0.4800084829330444, y = 0.5494590997695923, keywords = {"profession", "trainer", "jewelcrafting", "gem"} },
@@ -461,6 +481,19 @@ ns.STATIC_LOCATIONS = {
{ name = "Herbalism Trainer", category = "prof_herbalism", x = 0.4821820855140686, y = 0.5153673887252808, keywords = {"profession", "trainer", "herbalism", "herb"} },
{ name = "Tailoring Trainer", category = "prof_tailoring", x = 0.4819965362548828, y = 0.5414250493049622, keywords = {"profession", "trainer", "tailoring", "cloth"} },
{ name = "Auction House", category = "auctionhouse", x = 0.5084021687507629, y = 0.7571848034858704, keywords = {"auction", "ah"} },
+ { name = "Decor Specialist", category = "decor", x = 0.5596986357345336, y = 0.6570628685898381, keywords = {"decor", "decoration", "housing", "furniture"} },
+ { name = "Crafting Orders", category = "craftingorders", x = 0.4515396058559418, y = 0.5560497045516968, keywords = {"crafting", "orders", "work order"} },
+ { name = "Inn", category = "innkeeper", x = 0.5628485083580017, y = 0.7034760117530823, keywords = {"inn", "innkeeper", "hearth", "rest", "tavern"} },
+ { name = "Sanctum of Light", category = "landmark", x = 0.4544047117233276, y = 0.7033688426017761, keywords = {"sanctum", "light"} },
+ { name = "Rostrum of Transformation", category = "rostrum", x = 0.5219147801399231, y = 0.7374712824821472, keywords = {"rostrum", "transformation", "dragonriding"} },
+ { name = "Mailbox", category = "mailbox", x = 0.4985528588294983, y = 0.7411491870880127, keywords = {"mail", "mailbox"} },
+ { name = "Mailbox", category = "mailbox", x = 0.4948752820491791, y = 0.660243809223175, keywords = {"mail", "mailbox"} },
+ { name = "Mailbox", category = "mailbox", x = 0.5487621426582336, y = 0.7075653076171875, keywords = {"mail", "mailbox"} },
+ { name = "Mailbox", category = "mailbox", x = 0.2844708324402598, y = 0.6709848556545916, keywords = {"mail", "mailbox"} },
+ { name = "Mailbox", category = "mailbox", x = 0.3458690206233208, y = 0.8032694501555633, keywords = {"mail", "mailbox"} },
+ { name = "Mailbox", category = "mailbox", x = 0.695517930156462, y = 0.8206761373145117, keywords = {"mail", "mailbox"} },
+ { name = "Mailbox", category = "mailbox", x = 0.5750499589877152, y = 0.6998717990457972, keywords = {"mail", "mailbox"} },
+ { name = "Mailbox", category = "mailbox", x = 0.6702745588052893, y = 0.7012820215558285, keywords = {"mail", "mailbox"} },
},
}
From 0c41d91d7f6691ae95c81c98d4d13781ef58e18e Mon Sep 17 00:00:00 2001
From: wowaddonmaker
Date: Tue, 17 Mar 2026 06:19:51 -0400
Subject: [PATCH 09/10] Bump to 1.3.1 and update changelog and What's New
---
CHANGELOG.md | 17 +++++++++++++++++
EasyFind.toc | 2 +-
UI.lua | 17 ++++++++---------
3 files changed, 26 insertions(+), 10 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c7aa44a..c9f66c6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,23 @@ All notable changes to EasyFind will be documented in this file.
---
+## [1.3.1] - 2026-03-17
+
+### Added
+- **EasyFind-Only Navigation Options**: Minimap arrow glow and guide circle now have sub-options to restrict behavior to EasyFind-placed waypoints only, ignoring other sources
+- **Independent Map Navigation Modes**: Local and global map search bars now have separate direct-open toggles instead of a shared setting
+- **Expanded POI Coverage**: Added portals in Tirisfal Glades, decor specialists, and additional innkeepers, auction houses, and mailboxes across multiple zones
+
+### Changed
+- **Slash Commands**: `/ef` now opens options directly. `/ef toggle` (shorthand `/ef t`) replaces `/ef show`/`/ef hide`. Added `/ef help` for a command overview
+- **Options Panel Dropdowns**: Redesigned with WoW-style open/close arrows and indented child options
+
+### Fixed
+- **New Map POI Categories**: Decor specialists, crafting order NPCs, rostrum, pet and riding trainers, and training dummies now properly appear in map search results
+- **Tooltip Clarity**: Map search bar editbox now shows bar identity (Zone/Global Search) and the current mode. Mode toggle button describes only the toggle action
+
+---
+
## [1.3.0] - 2026-03-15
### Added
diff --git a/EasyFind.toc b/EasyFind.toc
index 260bf62..ef485c6 100644
--- a/EasyFind.toc
+++ b/EasyFind.toc
@@ -2,7 +2,7 @@
## Title: EasyFind
## Notes: Search and locate UI elements and map locations with ease
## Author: justawower
-## Version: 1.3.0
+## Version: 1.3.1
## IconTexture: 136460
## Category: UI
## AddonCompartmentFunc: EasyFind_OnAddonCompartmentClick
diff --git a/UI.lua b/UI.lua
index f442cbb..7805db5 100644
--- a/UI.lua
+++ b/UI.lua
@@ -4108,7 +4108,7 @@ function UI:ShowWhatsNew(version)
if _G["EasyFindWhatsNew"] then return end
local f = CreateFrame("Frame", "EasyFindWhatsNew", UIParent, "BackdropTemplate")
- f:SetSize(410, 300)
+ f:SetSize(410, 265)
f:SetPoint("CENTER")
f:SetFrameStrata("DIALOG")
f:SetFrameLevel(200)
@@ -4149,14 +4149,13 @@ function UI:ShowWhatsNew(version)
body:SetJustifyH("LEFT")
body:SetSpacing(4)
body:SetText(
- "|cffFFD100\226\128\162|r |cffffffffMount, Toy, and Pet Search|r\n" ..
- " Search your collected mounts, toys, and battle pets (see UI Search filter on bar). Click to summon or use\n" ..
- "|cffFFD100\226\128\162|r |cffffffffUnified Search Bar|r\n" ..
- " Map search results can now also appear in the UI search bar with filter toggles\n" ..
- "|cffFFD100\226\128\162|r |cffffffffMode Toggle Button|r\n" ..
- " Quick toggle between guide mode and fast mode right in the search bar (click magnifying lens)\n" ..
- "|cffFFD100\226\128\162|r |cffffffffFull Screen Map Support|r\n" ..
- " Map search bars now work in full screen map mode"
+ "|cffFFD100\226\128\162|r |cffffffffInstant Fast Mode Navigation|r\n" ..
+ " UI panels open without flickering in fast mode\n" ..
+ "|cffFFD100\226\128\162|r |cffffffffIndependent Fast Mode Toggles|r\n" ..
+ " Each search bar now has its own fast mode toggle\n" ..
+ "|cffFFD100\226\128\162|r |cffffffffEasyFind-Only Navigation Options|r\n" ..
+ " Restrict minimap glow and guide circle to EasyFind pins\n" ..
+ "|cffFFD100\226\128\162|r |cffffffffOptions Panel Overhaul|r"
)
-- Footer - anchored below body so it can't overlap
From b3eb4aa3b4ed4ac06d735ae9fc211fd30710c190 Mon Sep 17 00:00:00 2001
From: wowaddonmaker
Date: Tue, 17 Mar 2026 06:29:31 -0400
Subject: [PATCH 10/10] Fix luacheck warnings
---
.luacheckrc | 3 +++
Options.lua | 7 ++-----
StaticLocations.lua | 2 +-
3 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/.luacheckrc b/.luacheckrc
index c68c426..0cd60a8 100644
--- a/.luacheckrc
+++ b/.luacheckrc
@@ -78,6 +78,9 @@ read_globals = {
-- Cross-addon references
"EasyFindDevDB",
+ -- Blizzard settings frames
+ "SettingsPanel", "InterfaceOptionsFrame",
+
-- Constants, Enums, Mixins
"Enum", "Settings", "BackdropTemplateMixin",
"SOUNDKIT", "UIDROPDOWNMENU_OPEN_MENU", "UISpecialFrames",
diff --git a/Options.lua b/Options.lua
index 0f95325..7d26248 100644
--- a/Options.lua
+++ b/Options.lua
@@ -503,7 +503,6 @@ function Options:Initialize()
local FRAME_H = 380
local COL_LEFT = 4 -- Left column offset within content frames
local BTN_OFFSET = 105 -- Label LEFT to button LEFT (aligns selectors/keybinds)
- local TAB_Y = -42 -- Y where tab buttons start
local CONTENT_Y = -68 -- Y where tab content starts (below tab bar)
local CONTENT_H = 294 -- Fixed content area height
@@ -564,7 +563,6 @@ function Options:Initialize()
-- Tab system using WoW standard tab atlases (flipped for top attachment)
local tabFrames = {}
local tabButtons = {}
- local activeTabIndex
-- Flip a texture vertically for top-attached tabs
local function FlipV(tex)
@@ -591,7 +589,6 @@ function Options:Initialize()
tf:SetShown(i == index)
SetTabActive(tabButtons[i], i == index)
end
- activeTabIndex = index
end
optionsFrame.SwitchToTab = SwitchToTab
@@ -805,7 +802,7 @@ function Options:Initialize()
return box
end
- local cfBox = CreateURLBox(homeTab, "https://www.curseforge.com/wow/addons/easyfind", homeDesc, -6)
+ CreateURLBox(homeTab, "https://www.curseforge.com/wow/addons/easyfind", homeDesc, -6)
local sec3 = CreateTab("General")
@@ -1626,7 +1623,7 @@ function Options:Initialize()
.. "Copy the link below to share directly with friends:"
)
- local shareBox = CreateURLBox(feedbackTab, "https://www.curseforge.com/wow/addons/easyfind", enjoyDesc, -6)
+ CreateURLBox(feedbackTab, "https://www.curseforge.com/wow/addons/easyfind", enjoyDesc, -6)
-- Show Home tab by default
SwitchToTab(1)
diff --git a/StaticLocations.lua b/StaticLocations.lua
index 786bff3..a1db488 100644
--- a/StaticLocations.lua
+++ b/StaticLocations.lua
@@ -1,5 +1,5 @@
-- EasyFind Static Locations (auto-generated 2026-03-17 03:17)
-local ADDON_NAME, ns = ...
+local _, ns = ...
ns.STATIC_LOCATIONS = {
[18] = { -- Tirisfal Glades
{ name = "Portal to Stranglethorn Vale", category = "portal", x = 0.6142756296653066, y = 0.5879643697403164, keywords = {"portal", "teleport", "mage"} },