From 15c788b087e9c9e0c7eb29be25f28183e1a6981c Mon Sep 17 00:00:00 2001
From: wowaddonmaker
Date: Sat, 21 Mar 2026 14:13:55 -0400
Subject: [PATCH 1/2] Add table of contents and rare mob screenshots to README
---
README.md | 32 +++++++++++++++++++++++++++++---
1 file changed, 29 insertions(+), 3 deletions(-)
diff --git a/README.md b/README.md
index bcfdad1..16db2bc 100644
--- a/README.md
+++ b/README.md
@@ -12,6 +12,22 @@ EasyFind lets you search for any panel, tab, or setting in WoW's interface and a
Direct open skips straight to the panel instantly
+## Table of Contents
+
+- [Features](#features)
+ - [UI Search](#ui-search)
+ - [Map Search](#map-search)
+ - [Search Features](#search-features)
+ - [Two Navigation Modes](#two-navigation-modes)
+- [How to Use](#how-to-use)
+- [First-Time Setup](#first-time-setup)
+- [Slash Commands](#slash-commands)
+- [Keybinds](#keybinds)
+- [Options](#options)
+- [Moving the Search Bars](#moving-the-search-bars)
+- [Feedback](#feedback)
+- [Links](#links)
+
## Features
### UI Search
@@ -39,9 +55,8 @@ Find and navigate to any interface element such as:
Currencies show your current amounts inline, and reputations display progress bars with renown level, friendship rank, or traditional standing.
-
-
-Currency amounts and reputation progress bars in search results
+
+Reputation progress bars (left) and currency amounts (right) in search results
### Map Search
@@ -60,6 +75,17 @@ Locate places and NPCs across Azeroth:
* Chromie (Timewalking Campaigns)
* Rare mobs (with optional auto-tracking that always shows active rares on the map)
+
+
+
+Search "rare" to see all active rares, or select a specific one to track it
+
+
+
+
+Rares category with auto-track toggle in the filter menu
+
+
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.
From d8fdc2e35cec49efae832c74123a8655f364da09 Mon Sep 17 00:00:00 2001
From: wowaddonmaker
Date: Thu, 19 Mar 2026 02:43:48 -0400
Subject: [PATCH 2/2] Switch results sizing from row count to pixel height
---
Core.lua | 23 ++++-
MapSearch.lua | 5 +-
Options.lua | 14 +--
Rescaler.lua | 242 +++++++++++++++++++++++++++++++-------------------
UI.lua | 86 ++++++++++++++----
Utils.lua | 9 +-
6 files changed, 255 insertions(+), 124 deletions(-)
diff --git a/Core.lua b/Core.lua
index 0e1c3e0..5ce882b 100644
--- a/Core.lua
+++ b/Core.lua
@@ -24,7 +24,7 @@ EasyFind.db = {}
-- SavedVariables version. Increment when changing DB schema.
-- Each migration runs once: if saved dbVersion < DB_VERSION, run all steps in order.
-local DB_VERSION = 1
+local DB_VERSION = 3
-- SavedVariables defaults - new keys are auto-merged for existing users
local DB_DEFAULTS = {
@@ -38,7 +38,7 @@ local DB_DEFAULTS = {
mapSearchWidth = 0.88,
uiSearchWidth = 0.88,
uiResultsScale = 1.0,
- uiResultsWidth = 300,
+ uiResultsWidth = 350,
mapResultsScale = 1.0,
mapResultsWidth = 300,
searchBarOpacity = 0.75, -- ns.DEFAULT_OPACITY
@@ -59,8 +59,8 @@ local DB_DEFAULTS = {
resultsTheme = "Retail", -- "Classic" or "Retail"
indicatorStyle = "EasyFind Arrow", -- Indicator texture style
indicatorColor = "Yellow", -- Indicator color preset
- uiMaxResults = 10, -- Maximum visible UI search results (3-24)
- mapMaxResults = 6, -- Maximum visible map search results (3-24)
+ uiResultsHeight = 280, -- Visible height of UI search results panel in pixels
+ mapResultsHeight = 168, -- Visible height of map search results panel in pixels
showTruncationMessage = true, -- Show "more results available" message when truncated
hardResultsCap = false, -- Hard cap on results (no "more results" message)
staticOpacity = false, -- Keep opacity constant while moving
@@ -118,6 +118,21 @@ local DB_MIGRATIONS = {
end
if db.uiResultsWidth == 1.0 then db.uiResultsWidth = 300 end
end,
+ -- [2] = Widen default UI results panel from 300 to 350
+ [2] = function(db)
+ if db.uiResultsWidth == 300 then db.uiResultsWidth = 350 end
+ end,
+ -- [3] = Replace row-count settings with pixel height
+ [3] = function(db)
+ if not db.uiResultsHeight then
+ db.uiResultsHeight = db.uiMaxResults and (db.uiMaxResults * 28) or 280
+ end
+ if not db.mapResultsHeight then
+ db.mapResultsHeight = db.mapMaxResults and (db.mapMaxResults * 26 + 12) or 168
+ end
+ db.uiMaxResults = nil
+ db.mapMaxResults = nil
+ end,
}
-- Fields that are runtime-only and must not persist in SavedVariables
diff --git a/MapSearch.lua b/MapSearch.lua
index 0fb6451..0dbba9b 100644
--- a/MapSearch.lua
+++ b/MapSearch.lua
@@ -6234,16 +6234,15 @@ function MapSearch:ShowResults(results)
-- Must compute maxVisibleHeight (including screen-space clamping) BEFORE the
-- button loop, otherwise willScroll can be false while hasScroll ends up true
-- and the scrollbar overlaps full-width buttons.
- local maxVisibleRows = EasyFind.db.mapMaxResults or 6
local scrollBar = resultsFrame.scrollBar
- local maxVisibleHeight = maxVisibleRows * 26 + 12
+ local maxVisibleHeight = EasyFind.db.mapResultsHeight or 168
local preAnchor = activeSearchFrame or searchFrame
if isGlobalSearch and globalSearchFrame then
preAnchor = globalSearchFrame
end
local screenH = UIParent:GetHeight()
if resultsAbove then
- local available = (preAnchor:GetTop() or (screenH / 2)) - 16
+ local available = screenH - (preAnchor:GetTop() or (screenH / 2)) - 16
if available > 0 and maxVisibleHeight > available then
maxVisibleHeight = available
end
diff --git a/Options.lua b/Options.lua
index 21d31dd..4b3465c 100644
--- a/Options.lua
+++ b/Options.lua
@@ -1674,7 +1674,7 @@ function Options:DoResetAll()
EasyFind.db.mapSearchWidth = 0.88
EasyFind.db.uiSearchWidth = 0.88
EasyFind.db.uiResultsScale = 1.0
- EasyFind.db.uiResultsWidth = 300
+ EasyFind.db.uiResultsWidth = 350
EasyFind.db.mapResultsScale = 1.0
EasyFind.db.mapResultsWidth = 300
EasyFind.db.searchBarOpacity = DEFAULT_OPACITY
@@ -1691,8 +1691,8 @@ function Options:DoResetAll()
EasyFind.db.globalMapDirectOpen = false
EasyFind.db.smartShow = false
EasyFind.db.resultsTheme = "Retail"
- EasyFind.db.uiMaxResults = 10
- EasyFind.db.mapMaxResults = 6
+ EasyFind.db.uiResultsHeight = 280
+ EasyFind.db.mapResultsHeight = 168
EasyFind.db.pinsCollapsed = false
EasyFind.db.staticOpacity = false
EasyFind.db.indicatorStyle = "EasyFind Arrow"
@@ -1840,9 +1840,9 @@ function Options:DoResetUI()
EasyFind.db.uiSearchScale = 1.0
EasyFind.db.uiSearchWidth = 0.88
EasyFind.db.uiResultsScale = 1.0
- EasyFind.db.uiResultsWidth = 300
+ EasyFind.db.uiResultsWidth = 350
EasyFind.db.uiSearchPosition = nil
- EasyFind.db.uiMaxResults = 10
+ EasyFind.db.uiResultsHeight = 280
EasyFind.db.uiSearchFilters = { ui = true, mounts = false, toys = false, pets = false, map = false }
EasyFind.db.uiMapSearchLocal = true
@@ -1884,7 +1884,7 @@ function Options:DoResetMap()
EasyFind.db.globalSearchPosition = nil
EasyFind.db.mapSearchPositionMax = nil
EasyFind.db.globalSearchPositionMax = nil
- EasyFind.db.mapMaxResults = 6
+ EasyFind.db.mapResultsHeight = 168
EasyFind.db.mapPinHighlight = true
EasyFind.db.blinkingPins = false
EasyFind.db.minimapArrowGlow = true
@@ -1942,7 +1942,7 @@ function Options:DoResetUIPositions()
EasyFind.db.uiSearchScale = 1.0
EasyFind.db.uiSearchWidth = 0.88
EasyFind.db.uiResultsScale = 1.0
- EasyFind.db.uiResultsWidth = 300
+ EasyFind.db.uiResultsWidth = 350
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
diff --git a/Rescaler.lua b/Rescaler.lua
index efb79a6..f2f014b 100644
--- a/Rescaler.lua
+++ b/Rescaler.lua
@@ -18,8 +18,9 @@ local HANDLE_SIZE = 10
local GLOW_OUTSET = 6
local PREVIEW_ROW_H = 26
local PREVIEW_PAD = 16
-local MIN_ROWS = 3
-local MAX_ROWS = 24
+local PREVIEW_MAX_ROWS = 24
+local MIN_HEIGHT = 80
+local MAX_HEIGHT = 700
local MIN_FONT = 0.5
local MAX_FONT = 2.0
@@ -32,19 +33,20 @@ local resultsOverlay = nil -- glow around results
local donePanel = nil -- instruction + Done button
local previewResults = nil -- fake results frame for preview
-local function GetMaxRows()
- if activeMode == "ui" then return EasyFind.db.uiMaxResults or 10 end
- return EasyFind.db.mapMaxResults or 6
+local function GetResultsHeight()
+ if activeMode == "ui" then return EasyFind.db.uiResultsHeight or 280 end
+ return EasyFind.db.mapResultsHeight or 168
end
-local function SetMaxRows(rows)
- if activeMode == "ui" then EasyFind.db.uiMaxResults = rows
- else EasyFind.db.mapMaxResults = rows end
+local function SetResultsHeight(h)
+ h = mmax(MIN_HEIGHT, mmin(MAX_HEIGHT, mfloor(h + 0.5)))
+ if activeMode == "ui" then EasyFind.db.uiResultsHeight = h
+ else EasyFind.db.mapResultsHeight = h end
end
-local function GetDefaultMaxRows()
- if activeMode == "ui" then return 10 end
- return 6
+local function GetDefaultResultsHeight()
+ if activeMode == "ui" then return 280 end
+ return 168
end
local function GetFontScale()
@@ -71,6 +73,22 @@ local function ClampWidth(v)
return mmax(MIN_WIDTH, mmin(MAX_WIDTH, v))
end
+local function GetScreenMaxHeight(anchorAbove)
+ if not activeSearchBar then return MAX_HEIGHT end
+ local available
+ if anchorAbove then
+ local screenTop = UIParent:GetTop() or UIParent:GetHeight()
+ local barTop = activeSearchBar:GetTop() or (screenTop / 2)
+ available = screenTop - barTop - 16
+ else
+ available = (activeSearchBar:GetBottom() or (UIParent:GetHeight() / 2)) - 16
+ end
+ if available > 0 then
+ return mmax(MIN_HEIGHT, mmin(MAX_HEIGHT, mfloor(available)))
+ end
+ return MAX_HEIGHT
+end
+
local function AddResetButton(editBox, onConfirm)
local btn = CreateFrame("Button", nil, editBox:GetParent())
btn:SetSize(editBox:GetWidth(), 16)
@@ -222,14 +240,14 @@ end
-- Preview results (fake rows to show results area)
-local function CreatePreviewResults(parent, targetFrame, width, visibleRows, anchorAbove, leftAligned)
+local function CreatePreviewResults(parent, targetFrame, width, heightPx, anchorAbove, leftAligned)
local fontScale = GetFontScale()
local rowH = PREVIEW_ROW_H * fontScale
local frame = CreateFrame("Frame", nil, parent, "BackdropTemplate")
frame:SetFrameStrata("FULLSCREEN_DIALOG")
frame:SetFrameLevel(190)
frame:SetWidth(width)
- frame:SetHeight(visibleRows * rowH + PREVIEW_PAD)
+ frame:SetHeight(heightPx)
if leftAligned then
if anchorAbove then
@@ -254,8 +272,9 @@ local function CreatePreviewResults(parent, targetFrame, width, visibleRows, anc
frame:SetBackdropColor(0.1, 0.1, 0.1, 0.85)
frame:SetClipsChildren(true)
+ local nVisible = mfloor((heightPx - PREVIEW_PAD) / rowH)
frame.rows = {}
- for i = 1, MAX_ROWS do
+ for i = 1, PREVIEW_MAX_ROWS do
local row = frame:CreateFontString(nil, "OVERLAY", "GameFontDisable")
row:SetPoint("LEFT", frame, "LEFT", 12, 0)
row:SetPoint("RIGHT", frame, "RIGHT", -12, 0)
@@ -269,16 +288,17 @@ local function CreatePreviewResults(parent, targetFrame, width, visibleRows, anc
local path, baseSize, flags = GameFontDisable:GetFont()
row:SetFont(path, baseSize * fontScale, flags)
end
- row:SetShown(i <= visibleRows)
+ row:SetShown(i <= nVisible)
frame.rows[i] = row
end
- frame.SetVisibleRows = function(self, n)
- n = mmax(MIN_ROWS, mmin(MAX_ROWS, n))
- local rowH = PREVIEW_ROW_H * GetFontScale()
- self:SetHeight(n * rowH + PREVIEW_PAD)
- for i = 1, MAX_ROWS do
- self.rows[i]:SetShown(i <= n)
+ frame.SetPreviewHeight = function(self, h)
+ h = mmax(MIN_HEIGHT, mmin(MAX_HEIGHT, mfloor(h + 0.5)))
+ self:SetHeight(h)
+ local scaledRowH = PREVIEW_ROW_H * GetFontScale()
+ local nVis = mfloor((h - PREVIEW_PAD) / scaledRowH)
+ for i = 1, PREVIEW_MAX_ROWS do
+ self.rows[i]:SetShown(i <= nVis)
end
end
@@ -286,8 +306,9 @@ local function CreatePreviewResults(parent, targetFrame, width, visibleRows, anc
local scale = GetFontScale()
local path, baseSize, flags = GameFontDisable:GetFont()
local scaledRowH = PREVIEW_ROW_H * scale
- local rows = GetMaxRows()
- for i = 1, MAX_ROWS do
+ local h = GetResultsHeight()
+ local nVis = mfloor((h - PREVIEW_PAD) / scaledRowH)
+ for i = 1, PREVIEW_MAX_ROWS do
local row = self.rows[i]
row:SetFont(path, baseSize * scale, flags)
row:SetHeight(scaledRowH)
@@ -295,8 +316,9 @@ local function CreatePreviewResults(parent, targetFrame, width, visibleRows, anc
row:SetPoint("LEFT", self, "LEFT", 12, 0)
row:SetPoint("RIGHT", self, "RIGHT", -12, 0)
row:SetPoint("TOP", self, "TOP", 0, -8 - (i - 1) * scaledRowH)
+ row:SetShown(i <= nVis)
end
- self:SetHeight(rows * scaledRowH + PREVIEW_PAD)
+ self:SetHeight(h)
end
return frame
@@ -351,9 +373,9 @@ local function SetupWidthDrag(handle, getWidth, setWidth, widthLabel, side)
end)
end
--- Corner drag handler (width + rows combo)
+-- Corner drag handler (width + height combo)
-local function SetupCornerDrag(handle, preview, getWidth, setWidth, widthLabel, rowsLabel, anchorAbove)
+local function SetupCornerDrag(handle, preview, getWidth, setWidth, widthLabel, heightLabel, anchorAbove)
handle:SetScript("OnDragStart", function(self)
self.dragging = true
local cx, cy = GetCursorPosition()
@@ -361,8 +383,8 @@ local function SetupCornerDrag(handle, preview, getWidth, setWidth, widthLabel,
self.startX = cx / es
self.startY = cy / es
self.startWidth = getWidth()
- self.startRows = GetMaxRows()
- self.scaledRowH = PREVIEW_ROW_H * GetFontScale()
+ self.startHeight = GetResultsHeight()
+ self.maxH = GetScreenMaxHeight(anchorAbove)
end)
handle:SetScript("OnDragStop", function(self)
self.dragging = false
@@ -383,23 +405,22 @@ local function SetupCornerDrag(handle, preview, getWidth, setWidth, widthLabel,
local dy = self.startY - cy
if anchorAbove then dy = -dy end
- local rowDelta = mfloor(dy / self.scaledRowH + 0.5)
- local rows = mmax(MIN_ROWS, mmin(MAX_ROWS, self.startRows + rowDelta))
- SetMaxRows(rows)
- preview:SetVisibleRows(rows)
- if rowsLabel and not rowsLabel:HasFocus() then rowsLabel:SetText(rows) end
+ local newH = mmax(MIN_HEIGHT, mmin(self.maxH, mfloor(self.startHeight + dy + 0.5)))
+ SetResultsHeight(newH)
+ preview:SetPreviewHeight(newH)
+ if heightLabel and not heightLabel:HasFocus() then heightLabel:SetText(newH) end
end)
end
--- Row count drag handler
+-- Height drag handler
-local function SetupRowsDrag(handle, preview, rowsBox, anchorAbove)
+local function SetupHeightDrag(handle, preview, heightBox, anchorAbove)
handle:SetScript("OnDragStart", function(self)
self.dragging = true
local _, cy = GetCursorPosition()
self.startY = cy / UIParent:GetEffectiveScale()
- self.startRows = GetMaxRows()
- self.scaledRowH = PREVIEW_ROW_H * GetFontScale()
+ self.startHeight = GetResultsHeight()
+ self.maxH = GetScreenMaxHeight(anchorAbove)
end)
handle:SetScript("OnDragStop", function(self)
self.dragging = false
@@ -410,11 +431,10 @@ local function SetupRowsDrag(handle, preview, rowsBox, anchorAbove)
cy = cy / UIParent:GetEffectiveScale()
local dy = self.startY - cy
if anchorAbove then dy = -dy end
- local rowDelta = mfloor(dy / self.scaledRowH + 0.5)
- local rows = mmax(MIN_ROWS, mmin(MAX_ROWS, self.startRows + rowDelta))
- SetMaxRows(rows)
- preview:SetVisibleRows(rows)
- if not rowsBox:HasFocus() then rowsBox:SetText(rows) end
+ local newH = mmax(MIN_HEIGHT, mmin(self.maxH, mfloor(self.startHeight + dy + 0.5)))
+ SetResultsHeight(newH)
+ preview:SetPreviewHeight(newH)
+ if not heightBox:HasFocus() then heightBox:SetText(newH) end
end)
end
@@ -445,12 +465,13 @@ local function SetupFontDrag(handle, fontLabel, preview)
SetFontScale(newFont)
ApplyFontUpdate()
- -- Scale preview row text and height to match
+ -- Update preview row fonts; height stays fixed (font size doesn't change result height)
if preview and preview.rows then
local path, baseSize, flags = GameFontDisable:GetFont()
local scaledRowH = PREVIEW_ROW_H * newFont
- local rows = GetMaxRows()
- for i = 1, MAX_ROWS do
+ local h = GetResultsHeight()
+ local nVis = mfloor((h - PREVIEW_PAD) / scaledRowH)
+ for i = 1, PREVIEW_MAX_ROWS do
local row = preview.rows[i]
row:SetFont(path, baseSize * newFont, flags)
row:SetHeight(scaledRowH)
@@ -458,8 +479,9 @@ local function SetupFontDrag(handle, fontLabel, preview)
row:SetPoint("LEFT", preview, "LEFT", 12, 0)
row:SetPoint("RIGHT", preview, "RIGHT", -12, 0)
row:SetPoint("TOP", preview, "TOP", 0, -8 - (i - 1) * scaledRowH)
+ row:SetShown(i <= nVis)
end
- preview:SetHeight(rows * scaledRowH + PREVIEW_PAD)
+ preview:SetHeight(h)
end
if not fontLabel:HasFocus() then
@@ -533,7 +555,7 @@ local function BuildBarOverlay(parent, targetFrame, mode)
return overlay
end
-local function BuildResultsOverlay(parent, targetFrame)
+local function BuildResultsOverlay(parent, targetFrame, anchorAbove)
local overlay = CreateGlowOverlay("EasyFindRescaleResultsGlow", parent, targetFrame)
overlay:SetPoint("TOPLEFT", targetFrame, "TOPLEFT", -GLOW_OUTSET, GLOW_OUTSET)
overlay:SetPoint("BOTTOMRIGHT", targetFrame, "BOTTOMRIGHT", GLOW_OUTSET, -GLOW_OUTSET)
@@ -546,8 +568,12 @@ local function BuildResultsOverlay(parent, targetFrame)
local widthBox = CreateDimLabel(overlay, "LEFT", "RIGHT", 8, 0, "Width:")
overlay.widthBox = widthBox
- -- Corner handle (bottom-right)
- local scaleHandle = CreateScaleHandle(overlay, "BOTTOMRIGHT", 0, 0, false, false)
+ -- Height drag edge and corner handle: top edge when above, bottom edge when below
+ local heightEdge = anchorAbove and "TOP" or "BOTTOM"
+ local cornerPoint = anchorAbove and "TOPRIGHT" or "BOTTOMRIGHT"
+ local flipV = anchorAbove
+
+ local scaleHandle = CreateScaleHandle(overlay, cornerPoint, 0, 0, false, flipV)
overlay.scaleHandle = scaleHandle
-- Width drag handles
@@ -556,30 +582,36 @@ local function BuildResultsOverlay(parent, targetFrame)
overlay.leftHandle = leftHandle
overlay.rightHandle = rightHandle
- -- Bottom drag handle (for row count)
- local bottomHandle = CreateHandle(overlay, "BOTTOM", 0, 0, nil, false)
- overlay.bottomHandle = bottomHandle
-
- -- Rows field (below bottom edge)
- local rowsBox = CreateFrame("EditBox", nil, overlay, "InputBoxTemplate")
- rowsBox:SetSize(40, 20)
- rowsBox:SetAutoFocus(false)
- rowsBox:SetMaxLetters(2)
- rowsBox:SetJustifyH("CENTER")
- rowsBox:SetFontObject("GameFontHighlightSmall")
-
- local rowsPfx = overlay:CreateFontString(nil, "OVERLAY", "GameFontDisableSmall")
- rowsPfx:SetPoint("TOP", overlay, "BOTTOM", 0, -4)
- rowsPfx:SetText("Rows:")
- rowsPfx:SetTextColor(GOLD_COLOR[1], GOLD_COLOR[2], GOLD_COLOR[3], 0.7)
- rowsBox:SetPoint("LEFT", rowsPfx, "RIGHT", 6, 0)
+ -- Height drag handle (top or bottom edge)
+ local heightHandle = CreateHandle(overlay, heightEdge, 0, 0, nil, false)
+ overlay.heightHandle = heightHandle
+
+ -- Height field (outside the resize edge)
+ local heightBox = CreateFrame("EditBox", nil, overlay, "InputBoxTemplate")
+ heightBox:SetSize(50, 20)
+ heightBox:SetAutoFocus(false)
+ heightBox:SetMaxLetters(3)
+ heightBox:SetJustifyH("CENTER")
+ heightBox:SetFontObject("GameFontHighlightSmall")
+
+ local heightPfx = overlay:CreateFontString(nil, "OVERLAY", "GameFontDisableSmall")
+ heightPfx:SetText("Height:")
+ heightPfx:SetTextColor(GOLD_COLOR[1], GOLD_COLOR[2], GOLD_COLOR[3], 0.7)
+ if anchorAbove then
+ heightPfx:SetPoint("BOTTOM", overlay, "TOP", 0, 4)
+ else
+ heightPfx:SetPoint("TOP", overlay, "BOTTOM", 0, -4)
+ end
+ heightBox:SetPoint("LEFT", heightPfx, "RIGHT", 6, 0)
- local rowsSuffix = rowsBox:CreateFontString(nil, "OVERLAY", "GameFontDisableSmall")
- rowsSuffix:SetPoint("LEFT", rowsBox, "RIGHT", 2, 0)
- rowsSuffix:SetText("rows")
- rowsSuffix:SetTextColor(GOLD_COLOR[1], GOLD_COLOR[2], GOLD_COLOR[3], 0.7)
+ local heightSuffix = heightBox:CreateFontString(nil, "OVERLAY", "GameFontDisableSmall")
+ heightSuffix:SetPoint("LEFT", heightBox, "RIGHT", 2, 0)
+ heightSuffix:SetText("px")
+ heightSuffix:SetTextColor(GOLD_COLOR[1], GOLD_COLOR[2], GOLD_COLOR[3], 0.7)
- overlay.rowsBox = rowsBox
+ overlay.heightBox = heightBox
+ overlay.heightPfx = heightPfx
+ overlay.anchorAbove = anchorAbove
return overlay
end
@@ -755,9 +787,9 @@ function Rescaler:Enter(mode)
local resultsAbove = (mode == "ui" and EasyFind.db.uiResultsAbove)
or (mode == "map" and EasyFind.db.mapResultsAbove)
local previewW = getResultsWidth()
- local currentRows = GetMaxRows()
+ local currentH = mmin(GetResultsHeight(), GetScreenMaxHeight(resultsAbove))
local leftAligned = (mode == "map")
- previewResults = CreatePreviewResults(bg, searchBar, previewW, currentRows, resultsAbove, leftAligned)
+ previewResults = CreatePreviewResults(bg, searchBar, previewW, currentH, resultsAbove, leftAligned)
previewResults:SetScale(getResultsScale())
previewResults:Show()
@@ -766,9 +798,24 @@ function Rescaler:Enter(mode)
barOverlay:Show()
-- Results overlay (around the preview)
- resultsOverlay = BuildResultsOverlay(bg, previewResults)
+ resultsOverlay = BuildResultsOverlay(bg, previewResults, resultsAbove)
resultsOverlay:Show()
+ -- If height label is near screen edge, flip it inside the overlay
+ local resizeEdge = resultsAbove and resultsOverlay:GetTop() or resultsOverlay:GetBottom()
+ local screenLimit = resultsAbove and UIParent:GetTop() or 0
+ local nearEdge = resultsAbove and (resizeEdge and screenLimit and (screenLimit - resizeEdge) < 40)
+ or (not resultsAbove and resizeEdge and resizeEdge < 40)
+ if nearEdge then
+ resultsOverlay.heightPfx:ClearAllPoints()
+ if resultsAbove then
+ resultsOverlay.heightPfx:SetPoint("TOPLEFT", resultsOverlay, "TOPLEFT", GLOW_OUTSET + 4, -8)
+ else
+ resultsOverlay.heightPfx:SetPoint("BOTTOMLEFT", resultsOverlay, "BOTTOMLEFT", GLOW_OUTSET + 4, 8)
+ end
+ resultsOverlay.heightInside = true
+ end
+
-- Wire bar width drag
SetupWidthDrag(barOverlay.leftHandle, getBarWidth, setBarWidth, barOverlay.widthBox, "LEFT")
SetupWidthDrag(barOverlay.rightHandle, getBarWidth, setBarWidth, barOverlay.widthBox, "RIGHT")
@@ -828,35 +875,47 @@ function Rescaler:Enter(mode)
resultsOverlay.widthBox:SetText(mfloor(defW + 0.5))
end)
- -- Wire results corner (width + rows combo)
- resultsOverlay.rowsBox:SetText(currentRows)
- resultsOverlay.rowsBox:SetScript("OnEnterPressed", function(self)
+ -- Wire results corner (width + height combo)
+ resultsOverlay.heightBox:SetText(currentH)
+ resultsOverlay.heightBox:SetScript("OnEnterPressed", function(self)
local val = tonumber(self:GetText())
if val then
- val = mmax(MIN_ROWS, mmin(MAX_ROWS, mfloor(val + 0.5)))
- SetMaxRows(val)
- previewResults:SetVisibleRows(val)
+ val = mmax(MIN_HEIGHT, mmin(GetScreenMaxHeight(resultsAbove), mfloor(val + 0.5)))
+ SetResultsHeight(val)
+ previewResults:SetPreviewHeight(val)
self:SetText(val)
end
self:ClearFocus()
end)
- resultsOverlay.rowsBox:SetScript("OnEscapePressed", function(self)
- self:SetText(GetMaxRows())
+ resultsOverlay.heightBox:SetScript("OnEscapePressed", function(self)
+ self:SetText(GetResultsHeight())
self:ClearFocus()
end)
- AddResetButton(resultsOverlay.rowsBox, function()
- local def = GetDefaultMaxRows()
- SetMaxRows(def)
- previewResults:SetVisibleRows(def)
- resultsOverlay.rowsBox:SetText(def)
+ local heightReset = AddResetButton(resultsOverlay.heightBox, function()
+ local def = GetDefaultResultsHeight()
+ SetResultsHeight(def)
+ previewResults:SetPreviewHeight(def)
+ resultsOverlay.heightBox:SetText(def)
end)
+ if resultsOverlay.anchorAbove then
+ heightReset:ClearAllPoints()
+ heightReset:SetPoint("BOTTOM", resultsOverlay.heightBox, "TOP", 0, 2)
+ end
+ if resultsOverlay.heightInside then
+ heightReset:ClearAllPoints()
+ if resultsOverlay.anchorAbove then
+ heightReset:SetPoint("TOP", resultsOverlay.heightBox, "BOTTOM", 0, -2)
+ else
+ heightReset:SetPoint("BOTTOM", resultsOverlay.heightBox, "TOP", 0, 2)
+ end
+ end
SetupCornerDrag(resultsOverlay.scaleHandle, previewResults, getResultsWidth, function(w)
setResultsWidth(w)
previewResults:SetWidth(w)
- end, resultsOverlay.widthBox, resultsOverlay.rowsBox, resultsAbove)
+ end, resultsOverlay.widthBox, resultsOverlay.heightBox, resultsAbove)
- -- Wire results bottom edge (row count)
- SetupRowsDrag(resultsOverlay.bottomHandle, previewResults, resultsOverlay.rowsBox, resultsAbove)
+ -- Wire results bottom edge (height)
+ SetupHeightDrag(resultsOverlay.heightHandle, previewResults, resultsOverlay.heightBox, resultsAbove)
-- Done panel
donePanel = CreateDonePanel(bg)
@@ -891,7 +950,7 @@ function Rescaler:Exit(reopenOptions)
resultsOverlay.leftHandle:SetScript("OnUpdate", nil)
resultsOverlay.rightHandle:SetScript("OnUpdate", nil)
resultsOverlay.scaleHandle:SetScript("OnUpdate", nil)
- resultsOverlay.bottomHandle:SetScript("OnUpdate", nil)
+ resultsOverlay.heightHandle:SetScript("OnUpdate", nil)
resultsOverlay:Hide()
resultsOverlay = nil
end
@@ -918,6 +977,7 @@ function Rescaler:Exit(reopenOptions)
if ns.UI then
if ns.UI.UpdateScale then ns.UI:UpdateScale() end
if ns.UI.UpdateWidth then ns.UI:UpdateWidth() end
+ if ns.UI.RefreshResults then ns.UI:RefreshResults() end
end
elseif activeMode == "map" then
if ns.MapSearch then
diff --git a/UI.lua b/UI.lua
index b7719a9..6929e97 100644
--- a/UI.lua
+++ b/UI.lua
@@ -27,6 +27,7 @@ local GetCursorPosition = GetCursorPosition
local wipe = wipe
local LIGHTNING_BOLT_TEX = "Interface\\AddOns\\EasyFind\\textures\\lightning-bolt"
+local REP_BAR_WIDTH = 100
local searchFrame
local resultsFrame
@@ -34,6 +35,7 @@ local resultButtons = {}
local MAX_BUTTON_POOL = 50 -- Maximum buttons (scroll handles overflow beyond this)
local inCombat = false
local selectingResult = false -- guard: suppress OnTextChanged re-renders during SelectResult
+local deferredRepRefreshPending = false -- deferred re-render to let IsTruncated() settle
-- PIN HELPERS
@@ -190,7 +192,7 @@ THEMES["Classic"] = {
rowHeight = 22,
indentPx = 20,
lineWidth = 2,
- resultsWidth = 380,
+ resultsWidth = 350,
resultsPadTop = 8,
resultsPadBot = 8,
btnWidth = 360,
@@ -233,7 +235,7 @@ THEMES["Retail"] = {
rowHeight = 28,
indentPx = 20, -- matches INDENT_PX so tree lines align
lineWidth = 2,
- resultsWidth = 390,
+ resultsWidth = 350,
resultsPadTop = 10,
resultsPadBot = 10,
resultsPadLeft = 12,
@@ -1615,6 +1617,7 @@ function UI:CreateResultButton(index)
tabText:SetPoint("LEFT", headerTab, "LEFT", 10, 0)
tabText:SetPoint("RIGHT", toggleBtn, "LEFT", -4, 0)
tabText:SetJustifyH("LEFT")
+ tabText:SetMaxLines(1)
tabText:SetTextColor(0.60, 0.58, 0.55, 1.0) -- muted gray (normal state)
resultRow.tabText = tabText
@@ -1752,7 +1755,6 @@ function UI:CreateResultButton(index)
-- Right-aligned reputation standing bar
-- Structure: repBar (dark bg + border) → repClip (clips fill) → repFillFrame (colored, same shape)
-- repBar → repTextOverlay (text on top of everything)
- local REP_BAR_WIDTH = 100
local repBarBackdrop = {
bgFile = "Interface\\BUTTONS\\WHITE8X8",
edgeFile = TOOLTIP_BORDER,
@@ -2260,10 +2262,9 @@ function UI:ShowHierarchicalResults(hierarchical, preserveScroll)
local count = mmin(#visible, MAX_BUTTON_POOL)
-- Pre-compute whether scrolling will be needed so buttons can be narrower.
- -- Use >= to reserve scrollbar space when at the limit, since extra spacing
- -- (pinned gaps, section separators) can push content height past the max.
- local maxVisibleRows = EasyFind.db.uiMaxResults or 10
- local willScroll = #visible >= maxVisibleRows
+ -- Rough estimate: if all rows at base height exceed the limit, a scrollbar likely appears.
+ local maxVisibleHeight = EasyFind.db.uiResultsHeight or 280
+ local willScroll = #visible * rowH > maxVisibleHeight
local scrollInset = 0
if willScroll and resultsFrame.scrollBar then
scrollInset = resultsFrame.scrollBar:GetWidth()
@@ -2313,6 +2314,7 @@ function UI:ShowHierarchicalResults(hierarchical, preserveScroll)
local yOffset = 0
local pinEndYOffset = 0
local catSepYPositions = {}
+ local hasSideBySideRepBar = false
for i = 1, MAX_BUTTON_POOL do
local resultRow = resultButtons[i]
if i <= count then
@@ -2837,7 +2839,7 @@ function UI:ShowHierarchicalResults(hierarchical, preserveScroll)
if resultRow.repFill.SetBackdropColor then
resultRow.repFill:SetBackdropColor(barR, barG, barB, 1.0)
end
- resultRow.repClip:SetWidth(mmax(fill * 100, 0.1))
+ resultRow.repClip:SetWidth(mmax(fill * REP_BAR_WIDTH, 0.1))
resultRow.repBarText:SetText(standingText)
if entry.isPathNode and theme.showHeaderTab then
@@ -2848,18 +2850,44 @@ function UI:ShowHierarchicalResults(hierarchical, preserveScroll)
resultRow.tabText:SetPoint("LEFT", resultRow.headerTab, "LEFT", 10, 0)
resultRow.tabText:SetPoint("RIGHT", resultRow.repBar, "LEFT", -4, 0)
elseif entry.isPathNode then
- -- Classic theme: rep bar on right, text between icon and bar
+ -- Side-by-side by default; IsTruncated() reflects the previous frame's
+ -- layout, which is accurate for stable results. A deferred re-render
+ -- corrects it after first render or scale/width changes.
+ local indentPixels = depth * indPx + 4
resultRow.repBar:ClearAllPoints()
resultRow.repBar:SetPoint("RIGHT", resultRow, "RIGHT", -6, 0)
+ resultRow.text:ClearAllPoints()
+ resultRow.text:SetPoint("LEFT", resultRow.icon, "RIGHT", 4, 0)
+ resultRow.text:SetPoint("RIGHT", resultRow.repBar, "LEFT", -4, 0)
+ if resultRow.text:IsTruncated() then
+ resultRow.text:ClearAllPoints()
+ resultRow.text:SetPoint("TOPLEFT", resultRow, "TOPLEFT", indentPixels, -3)
+ resultRow.text:SetPoint("TOPRIGHT", resultRow, "TOPRIGHT", -6, -3)
+ resultRow.repBar:ClearAllPoints()
+ resultRow.repBar:SetPoint("BOTTOM", resultRow, "BOTTOM", 0, 5)
+ resultRow:SetHeight(rowH + 25)
+ else
+ hasSideBySideRepBar = true
+ end
else
- -- Leaf: default position, hide icon, anchor text to bar
- resultRow.repBar:ClearAllPoints()
- resultRow.repBar:SetPoint("RIGHT", resultRow, "RIGHT", -6, 0)
+ -- Leaf: side-by-side by default, stack only if text truncates
SetRowIcon(resultRow, "hidden", nil, theme.iconSize)
local indentPixels = depth * indPx + 4
+ resultRow.repBar:ClearAllPoints()
+ resultRow.repBar:SetPoint("RIGHT", resultRow, "RIGHT", -6, 0)
resultRow.text:ClearAllPoints()
resultRow.text:SetPoint("LEFT", resultRow, "LEFT", indentPixels, 0)
resultRow.text:SetPoint("RIGHT", resultRow.repBar, "LEFT", -4, 0)
+ if resultRow.text:IsTruncated() then
+ resultRow.text:ClearAllPoints()
+ resultRow.text:SetPoint("TOPLEFT", resultRow, "TOPLEFT", indentPixels, -3)
+ resultRow.text:SetPoint("TOPRIGHT", resultRow, "TOPRIGHT", -6, -3)
+ resultRow.repBar:ClearAllPoints()
+ resultRow.repBar:SetPoint("BOTTOM", resultRow, "BOTTOM", 0, 5)
+ resultRow:SetHeight(rowH + 25)
+ else
+ hasSideBySideRepBar = true
+ end
iconSet = true
end
resultRow.repBar:Show()
@@ -2969,7 +2997,7 @@ function UI:ShowHierarchicalResults(hierarchical, preserveScroll)
end
-- Measure text height and expand row if text wraps
- local actualH = rowH
+ local actualH = resultRow:GetHeight()
local textObj
if theme.showHeaderTab and entry.isPathNode and resultRow.headerTab:IsShown() then
textObj = resultRow.tabText
@@ -2979,7 +3007,7 @@ function UI:ShowHierarchicalResults(hierarchical, preserveScroll)
if textObj then
local textHeight = textObj:GetStringHeight()
local minH = textHeight / ns.SEARCHBAR_FILL
- if minH > rowH then
+ if minH > actualH then
actualH = minH
resultRow:SetHeight(actualH)
if resultRow.headerTab:IsShown() then
@@ -3049,10 +3077,20 @@ function UI:ShowHierarchicalResults(hierarchical, preserveScroll)
-- Calculate total content height vs max visible height
local totalContentHeight = yOffset
- local maxVisibleHeight = maxVisibleRows * rowH
local hasScroll = totalContentHeight > maxVisibleHeight
local visibleHeight = hasScroll and maxVisibleHeight or totalContentHeight
+ -- If scrollbar appeared but we didn't reserve space for it (e.g. stacked rep rows
+ -- pushed content past maxVisibleHeight), retroactively narrow all visible rows
+ -- so they don't bleed into the scrollbar.
+ if hasScroll and scrollInset == 0 and resultsFrame.scrollBar then
+ local scrollBarW = resultsFrame.scrollBar:GetWidth()
+ scrollInset = scrollBarW
+ for i = 1, count do
+ resultButtons[i]:SetWidth(resultButtons[i]:GetWidth() - scrollBarW)
+ end
+ end
+
-- Size the results frame and scroll child
resultsFrame:SetHeight(padT + theme.resultsPadBot + visibleHeight)
resultsFrame.scrollChild:SetWidth(resultsFrame:GetWidth() - scrollInset)
@@ -3089,6 +3127,17 @@ function UI:ShowHierarchicalResults(hierarchical, preserveScroll)
resultsFrame:Show()
+ -- If any rep bar row is in side-by-side mode, schedule one deferred re-render so
+ -- IsTruncated() can reflect the layout we just set (it reads the previous frame's state).
+ if hasSideBySideRepBar and not deferredRepRefreshPending then
+ deferredRepRefreshPending = true
+ local selfRef = self
+ ns.Utils.SafeAfter(0, function()
+ deferredRepRefreshPending = false
+ selfRef:RefreshResults()
+ end)
+ end
+
-- Reset keyboard selection whenever results change
selectedIndex = 0
toggleFocused = false
@@ -4024,6 +4073,7 @@ end
function UI:UpdateResultsScale()
if resultsFrame then
resultsFrame:SetScale(EasyFind.db.uiResultsScale or 1.0)
+ self:RefreshResults()
end
end
@@ -4044,6 +4094,12 @@ function UI:UpdateResultsWidth()
end
end
+function UI:RefreshResults()
+ if cachedHierarchical and resultsFrame and resultsFrame:IsShown() then
+ self:ShowHierarchicalResults(cachedHierarchical, true)
+ end
+end
+
function UI:UpdateOpacity()
if not searchFrame then return end
local alpha = EasyFind.db.searchBarOpacity or DEFAULT_OPACITY
diff --git a/Utils.lua b/Utils.lua
index 7a8d325..aa05a70 100644
--- a/Utils.lua
+++ b/Utils.lua
@@ -116,7 +116,7 @@ ns.RESULT_ICON_SIZE = 18
ns.SEARCHBAR_HEIGHT = 30 -- base search bar frame height (before font scaling)
ns.SEARCHBAR_FILL = 0.55 -- fraction of bar height filled by text/icon
ns.SEARCHBAR_ICON_SCALE = 0.75 -- icon size relative to editBox height (font glyphs are shorter than line height)
-ns.CLEAR_BTN_SIZE = 18 -- base clear button size (before font scaling)
+ns.CLEAR_BTN_SIZE = 12 -- base clear button size (before font scaling)
local EasyFindSearchFont = CreateFont("EasyFindSearchFont")
local baseFont = Game15Font_Shadow or GameFontNormal
EasyFindSearchFont:CopyFontObject(baseFont)
@@ -128,6 +128,7 @@ ns.SEARCHBAR_FONT = "EasyFindSearchFont"
-- Fill is tinted black with tunable opacity; border uses Blizzard's action bar gray.
local SEARCH_TEX_FILL = "Interface\\AddOns\\EasyFind\\Textures\\SearchBarFill"
local SEARCH_TEX_BORDER = "Interface\\AddOns\\EasyFind\\Textures\\SearchBarBorder"
+local CLEAR_BTN_TEX = "Interface\\AddOns\\EasyFind\\Textures\\clear-button"
local SEARCH_CAP_W = 8
local SEARCH_TEX_W = 64
local TC_LEFT = {0, SEARCH_CAP_W / SEARCH_TEX_W, 0, 1}
@@ -681,13 +682,13 @@ function Utils.CreateClearButton(parent, globalName)
local normal = btn:CreateTexture(nil, "ARTWORK")
normal:SetAllPoints()
- normal:SetTexture("Interface\\FriendsFrame\\ClearBroadcastIcon")
+ normal:SetTexture(CLEAR_BTN_TEX)
btn:SetNormalTexture(normal)
local highlight = btn:CreateTexture(nil, "HIGHLIGHT")
highlight:SetAllPoints()
- highlight:SetTexture("Interface\\FriendsFrame\\ClearBroadcastIcon")
- highlight:SetVertexColor(1.2, 1.2, 1.2, 1)
+ highlight:SetTexture(CLEAR_BTN_TEX)
+ highlight:SetVertexColor(1.3, 1.3, 1.3, 1)
highlight:SetBlendMode("ADD")
btn:SetHighlightTexture(highlight)