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/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
+
+
+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
+
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)