From fa37ab8eb55d99ca4b724b8d6ac9c679ece5558d Mon Sep 17 00:00:00 2001 From: Xurkon Date: Tue, 9 Jun 2026 21:52:55 -0500 Subject: [PATCH] feat(tooltip): ElvUI style without ElvUI; source in secondary only; fix learner source label - ElvUI tooltip style: new 'ElvUI tooltip style' option (General tab, on by default) skins GameTooltip/WorldMapTooltip/ItemRefTooltip/shopping tooltips and the secondary learner frame with ElvUI's transparent flat look (dark bg + thin 1px border) when ElvUI is not installed. Corrected the secondary frame fallback that used the chunky WoW border. No-op when ElvUI is loaded. - Source attribution now shows ONLY inside the secondary learner tooltip when 'Use secondary learner tooltip' is enabled; removed from the main NPC/object/ item tooltip and gated off entirely when the secondary tooltip is disabled (map-pin source gated the same way). - Fixed learner-learned pins mislabelled 'AscensionDB': GetPinDataSource is now mode-aware and returns 'Learner' in learner mode when the entity has a learner record, even if AscensionDB also curates it (curated coords are discarded by GetNPC/GetObject in learner mode anyway). Test-neutral (145 successes / same 7 pre-existing failures + 1 error). --- CHANGELOG.md | 4 ++ Database/QuestieDB.lua | 33 ++++++++----- .../GeneralTab/QuestieOptionsGeneral.lua | 22 ++++++++- Modules/Options/QuestieOptionsDefaults.lua | 1 + Modules/QuestieLearner.lua | 29 +++++++---- Modules/Tooltips/MapIconTooltip.lua | 3 ++ Modules/Tooltips/Tooltip.lua | 48 +++++++++++++++++++ Modules/Tooltips/TooltipHandler.lua | 21 ++------ 8 files changed, 121 insertions(+), 40 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d44279c..44887e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,12 +22,16 @@ ### Features +- **[Tooltip - ElvUI Style Without ElvUI]** Added "ElvUI tooltip style" (General tab, on by default) which skins Questie's tooltips — the NPC/item/object and world-map-pin tooltips plus the secondary learner tooltip — with ElvUI's transparent flat look (dark `0.06/0.06/0.06/0.8` background and a thin 1px border) even when ElvUI is not installed, so they match instead of falling back to the chunky default WoW border. `QuestieTooltips:SkinDefaultTooltips` applies it to `GameTooltip`, `WorldMapTooltip`, `ItemRefTooltip` and the shopping tooltips (re-asserted on show), and the secondary learner frame's fallback style was corrected to the same thin border. No-op when ElvUI is loaded, since ElvUI skins the frames itself. + - **[Map - Looted Object Pins Disappear On Loot]** When you open/loot a quest object node (e.g. Fell Wood piles), that specific node's map/minimap pin now disappears immediately even if the objective is not yet fully collected, instead of all node pins staying until turn-in. On `LOOT_OPENED`, `QuestieQuest:RemoveLootedObjectivePins` finds the active object-objective spawn nearest the player's world position (within ~12 yards, since you stand on the node to loot it), records it as consumed in `Questie.db.char.lootedObjectSpawns`, and redraws just that objective so clustering recomputes with the looted node skipped. `_DetermineIconsToDraw` skips consumed object spawns, so removal persists across redraws and `/reload`. The looted history is cleared on quest accept (`QuestieQuest:ClearLootedSpawns`), so abandoning and re-doing the quest — including after an Ascension prestige — shows every node again. Scoped to `object`-type objectives; monster/kill pins are unchanged. Works in every data source mode (auto / learner / static / none): the suppression is centralized in the single shared `_DetermineIconsToDraw` draw path that all redraw routes funnel through (core `UpdateQuest`, the learner's debounced `_DoFlushActiveQuestPins`, slider redraws), and it operates on the already mode-resolved `objective.spawnList`. Matching is radius-based (1.5 zone units) rather than exact-coordinate so that in learner/auto mode a spawn the learner re-adds at the player's position for the just-looted node is also suppressed instead of re-appearing. - **[Tooltip - Data Source Attribution (Accurate)]** Re-added the "Show data source" tooltip option (General tab, off by default) that adds a `Source:` line to NPC, object, item and world-map-pin tooltips. Unlike the previous version (removed in an earlier build for guessing the source from the global data-source mode), this is accurate per-pin/per-entity and never guesses: each pin is tagged with its real provenance at creation via the new `QuestieDB.GetPinDataSource(entityType, id, spawnData)` helper — `Learner` (per-spawn `isLearned` flag or a learner record), `AscensionDB` (a curated `ascensionOverrideKeys` override for the id), `Townsfolk` (manual/menu notes), or the base `Questie DB` — with a `Comms` overlay appended when comms holds data for the entity. Objective pins are tagged per-spawn in `_DetermineIconsToDraw`; available/finisher pins are quest-scoped (defaulted in `DrawWorldIcon`); manual notes are tagged in `DrawManualIcon`. World-map pins read the per-pin tag (`MapIconTooltip`); unit/object/item hovers derive it per-id at the render layer (`TooltipHandler` via `QuestieTooltips:GetDataSourceLine`). When a pin's source genuinely can't be determined, no line is shown rather than a misleading one. ### Bug Fixes +- **[Tooltip - Data Source In Secondary Tooltip Only]** The `Source:` attribution line now appears only inside the secondary learner tooltip (when "Use secondary learner tooltip" is enabled) and is never added to the main NPC/object/item tooltip; with the secondary tooltip disabled it does not appear at all. The map-pin source line is gated the same way. The "Show data source" option now depends on the secondary-tooltip option being enabled. +- **[Tooltip - Learner Pin Mislabelled As AscensionDB]** A spawn freshly learned via the learner was shown as `Source: AscensionDB` instead of `Learner`. `QuestieDB.GetPinDataSource` resolved AscensionDB-curated ownership before checking the learner, but in learner-only mode the displayed spawns are the learner's (curated coords are discarded by `GetNPC`/`GetObject`). It is now mode-aware: in learner mode, an entity with a learner record reports `Learner` even when AscensionDB also curates it. - **[Learner - Learner-Only Mode No Longer Draws AscensionDB/Static Pins]** In learner-only mode the map/minimap drew curated AscensionDB (and static) spawns for every quest NPC/object the player had not personally recorded, instead of only the learner's own spawns. `QuestieDB:GetNPC`/`GetObject` fall back to the `npcDataOverrides`/`objectDataOverrides` entry for metadata when there is no learner record, and that entry's spawns (AscensionDB-curated for un-recorded entities) were being used as pins. Both functions now track whether a real learner record exists and, in learner mode, strip the spawns when it does not — so learner-only mode shows exclusively what the learner has recorded (the metadata fallback for names/tooltips is kept). Added a regression test. Recorded entities still show their learner spawns; auto/static/none modes are unchanged. - **[Map - Quest Type Filters Now Apply To Minimap]** (#11) Quest-type filters (Available Dungeon/Raid/PvP/Repeatable/Event Quests, plus other `ShouldBeHidden` rules like untracked/unexplored and `enableMiniMapIcons`) were honored on the world map but not the minimap. The world-map icon stayed hidden once `ShouldBeHidden` filtered it, but the minimap icon's per-frame `FadeLogic` re-showed any hidden icon as soon as the player came within range, so e.g. a filtered dungeon quest still appeared on the minimap. `FadeLogic` now re-checks `ShouldBeHidden` before re-showing, so the same filters apply to both maps (fixed in both the world-icon and manual-icon minimap fade paths). - **[Libs/AceComm - Long Comm Prefix Login Error]** (#12) Fixed a recurring login Lua error `AceComm:RegisterComm(...): prefix length is limited to 16 characters` on servers like ChromieCraft. The cause is a third-party addon (e.g. AtlasLoot) registering an AceComm prefix longer than the client's 16-character limit; because Questie's bundled AceComm is the LibStub winner and its `xpcall` polyfill wraps AceAddon's `OnEnable`, the upstream hard `error()` surfaced through Questie's frames every login. Our bundled `AceComm:RegisterComm` now degrades gracefully for over-long prefixes (which can never work on the client anyway): it warns once and skips the registration instead of throwing, so the offending addon's `OnEnable` is no longer aborted and the error popup is gone. diff --git a/Database/QuestieDB.lua b/Database/QuestieDB.lua index 9acec60..2e93624 100644 --- a/Database/QuestieDB.lua +++ b/Database/QuestieDB.lua @@ -2626,22 +2626,33 @@ function QuestieDB.GetPinDataSource(entityType, id, spawnData) end if not id then return nil end - -- AscensionDB-curated entity (has at least one curated override field for this id). + local settings = _GetLearnerSettings() + local mode = settings and settings.dataSourceMode or "auto" + + -- Does the learner have a record for this entity? + local learner = Questie.dbLearner and Questie.dbLearner.global + local bucket = learner and ((entityType == "OBJECT" and learner.objects) + or (entityType == "QUEST" and learner.quests) + or (entityType == "ITEM" and learner.items) + or learner.npcs) + local hasLearner = bucket and bucket[id] ~= nil + + -- In learner-only mode the displayed spawns ARE the learner's (GetNPC/GetObject + -- return the learner record and discard curated coords), so the learner is the + -- authoritative source even for AscensionDB-curated entities. Without this, a + -- freshly-learned curated NPC was mislabelled "AscensionDB". + if mode == "learner" and hasLearner then + return "Learner" + end + + -- AscensionDB-curated entity (authoritative in auto/static modes). local ascKeys = QuestieDB.ascensionOverrideKeys and QuestieDB.ascensionOverrideKeys[entityType] if ascKeys and ascKeys[id] and next(ascKeys[id]) then return "AscensionDB" end - -- Learner-known entity (learned data exists for this id). - local learner = Questie.dbLearner and Questie.dbLearner.global - if learner then - local bucket = (entityType == "OBJECT" and learner.objects) - or (entityType == "QUEST" and learner.quests) - or (entityType == "ITEM" and learner.items) - or learner.npcs - if bucket and bucket[id] then - return "Learner" - end + if hasLearner then + return "Learner" end return "Questie DB" diff --git a/Modules/Options/GeneralTab/QuestieOptionsGeneral.lua b/Modules/Options/GeneralTab/QuestieOptionsGeneral.lua index 94deee0..daa4108 100644 --- a/Modules/Options/GeneralTab/QuestieOptionsGeneral.lua +++ b/Modules/Options/GeneralTab/QuestieOptionsGeneral.lua @@ -562,14 +562,32 @@ function QuestieOptions.tabs.general:Initialize() type = "toggle", order = 8.605, name = function() return l10n('Show data source'); end, - desc = function() return l10n('Adds a "Source:" line to NPC, object and map-pin tooltips showing where the data came from (Questie DB, AscensionDB, Learner, Townsfolk, Comms). Only shown when the source is actually known.'); end, + desc = function() return l10n('Adds a "Source:" line (Questie DB, AscensionDB, Learner, Townsfolk, Comms) inside the secondary learner tooltip. Requires "Use secondary learner tooltip"; never shown in the main tooltip and not shown at all when the secondary tooltip is disabled. Only shown when the source is actually known.'); end, width = 1.5, - disabled = function() return not Questie.db.profile.enableTooltips; end, + disabled = function() return not (Questie.db.profile.enableTooltips and Questie.db.profile.learnerTooltipUseSecondary == true); end, get = function() return Questie.db.profile.enableTooltipsSource == true end, set = function(_, value) Questie.db.profile.enableTooltipsSource = value end }, + elvuiStyleTooltips = { + type = "toggle", + order = 8.606, + name = function() return l10n('ElvUI tooltip style'); end, + desc = function() return l10n('Skin Questie tooltips (NPC/item/map-pin and the secondary learner tooltip) with the ElvUI transparent style even when ElvUI is not installed. Has no effect when ElvUI is loaded, since ElvUI skins them itself. Takes full effect after a reload.'); end, + width = 1.5, + disabled = function() return not Questie.db.profile.enableTooltips; end, + get = function() return Questie.db.profile.elvuiStyleTooltips ~= false end, + set = function(_, value) + Questie.db.profile.elvuiStyleTooltips = value + if value then + local QuestieTooltips = QuestieLoader:ImportModule("QuestieTooltips") + if QuestieTooltips and QuestieTooltips.SkinDefaultTooltips then + QuestieTooltips:SkinDefaultTooltips() + end + end + end + }, partyOnlyToggle = { type = "toggle", order = 8.61, diff --git a/Modules/Options/QuestieOptionsDefaults.lua b/Modules/Options/QuestieOptionsDefaults.lua index e436ed3..cb0e1b3 100644 --- a/Modules/Options/QuestieOptionsDefaults.lua +++ b/Modules/Options/QuestieOptionsDefaults.lua @@ -88,6 +88,7 @@ function QuestieOptionsDefaults:Load() enableTooltipsQuestID = false, enableTooltipsQuestLevel = true, enableTooltipsSource = false, + elvuiStyleTooltips = true, showQuestXpAtMaxLevel = true, enableTooltipsNextInChain = true, learnerTooltips = true, diff --git a/Modules/QuestieLearner.lua b/Modules/QuestieLearner.lua index ada1f3a..d79e82f 100644 --- a/Modules/QuestieLearner.lua +++ b/Modules/QuestieLearner.lua @@ -4615,17 +4615,19 @@ local function _ApplyElvUIStyleTooltip(frame) return end end - -- ElvUI default "Transparent" template — see ElvUI/Core/Toolkit.lua:82 - -- and ElvUI/Settings/Profile.lua:29-31. Hard-coded so the secondary - -- frame looks the same with or without ElvUI. + -- ElvUI "Transparent" template — see ElvUI/Core/Toolkit.lua SetTemplate and + -- ElvUI/Settings/Profile.lua:29-31. ElvUI uses a FLAT texture for both the + -- background and the border with a 1px (E.mult) edge, NOT the chunky default + -- WoW tooltip border — so use a solid texture and edgeSize 1 to match. if not frame.SetBackdrop then return end + local FLAT = "Interface\\ChatFrame\\ChatFrameBackground" -- solid 1x1 texture on 3.3.5 frame:SetBackdrop({ - bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", - edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", - tile = true, - tileSize = 16, - edgeSize = 12, - insets = {left = 3, right = 3, top = 3, bottom = 3}, + bgFile = FLAT, + edgeFile = FLAT, + tile = false, + tileSize = 0, + edgeSize = 1, + insets = {left = 0, right = 0, top = 0, bottom = 0}, }) -- (0.06, 0.06, 0.06, 0.8) — ElvUI's default backdropfadecolor frame:SetBackdropColor(0.06, 0.06, 0.06, 0.8) @@ -4779,6 +4781,15 @@ local function _AddLearnedSpawnTooltipLine(unitToken) for _, pair in ipairs(lines) do rendered[#rendered + 1] = pair[1] .. ": " .. pair[2] end + -- Data-source attribution belongs ONLY in this secondary frame, and only when the + -- source option is enabled. It is never added to the main tooltip; when the secondary + -- learner tooltip is disabled it does not appear at all. + local QuestieTooltips = QuestieLoader:ImportModule("QuestieTooltips") + local sourceLine = QuestieTooltips and QuestieTooltips.GetDataSourceLine + and QuestieTooltips:GetDataSourceLine("m_" .. npcId) + if sourceLine then + rendered[#rendered + 1] = sourceLine + end -- Spacer after learner section rendered[#rendered + 1] = " " _ShowLearnerTooltipFrame(GameTooltip, rendered) diff --git a/Modules/Tooltips/MapIconTooltip.lua b/Modules/Tooltips/MapIconTooltip.lua index be8c7c7..96d5247 100644 --- a/Modules/Tooltips/MapIconTooltip.lua +++ b/Modules/Tooltips/MapIconTooltip.lua @@ -107,6 +107,9 @@ end --- the label is accurate, plus a Comms overlay when comms holds data for this id. local function _GetWorldMapDataSourceLine(pinData) if not Questie.db.profile.enableTooltipsSource then return nil end + -- Source attribution is tied to the secondary learner tooltip: only show it when that + -- option is enabled, and never otherwise (matches the unit-hover behaviour). + if Questie.db.profile.learnerTooltipUseSecondary ~= true then return nil end local src = pinData and pinData.DataSource local parts = {} if src then tinsert(parts, src) end diff --git a/Modules/Tooltips/Tooltip.lua b/Modules/Tooltips/Tooltip.lua index 4997de2..a20e7f6 100644 --- a/Modules/Tooltips/Tooltip.lua +++ b/Modules/Tooltips/Tooltip.lua @@ -123,6 +123,9 @@ end ---@param key string @"m_" | "o_" | "i_" local function _GetTooltipSourceLine(key) if not Questie.db.profile.enableTooltipsSource then return nil end + -- Source attribution is shown only inside the secondary learner tooltip; never when + -- that option is disabled (the only caller is the secondary frame, but gate here too). + if Questie.db.profile.learnerTooltipUseSecondary ~= true then return nil end if not key then return nil end local prefix = key:sub(1, 2) local id = tonumber(key:sub(3)) @@ -668,6 +671,49 @@ _InitObjectiveTexts = function(objectivesText, objectiveIndex, playerName) return objectivesText end +-- Apply ElvUI's "Transparent" tooltip look (flat dark background + thin 1px border) so +-- Questie's tooltips match the ElvUI style even when ElvUI is not installed. ElvUI uses a +-- FLAT texture for both the background and the border with a 1px edge (see +-- ElvUI/Core/Toolkit.lua SetTemplate + Settings/Profile.lua:29-31), not the chunky default +-- WoW tooltip border. +local _ELV_FLAT = "Interface\\ChatFrame\\ChatFrameBackground" -- solid 1x1 texture on 3.3.5 +local function _ApplyElvUIStyle(frame) + if not frame or not frame.SetBackdrop then return end + frame:SetBackdrop({ + bgFile = _ELV_FLAT, + edgeFile = _ELV_FLAT, + tile = false, + tileSize = 0, + edgeSize = 1, + insets = { left = 0, right = 0, top = 0, bottom = 0 }, + }) + frame:SetBackdropColor(0.06, 0.06, 0.06, 0.8) -- ElvUI backdropfadecolor + frame:SetBackdropBorderColor(0, 0, 0, 1) -- ElvUI bordercolor (black) +end + +-- Skins the standard tooltip frames Questie writes into. No-op when ElvUI is loaded (it +-- skins them itself) or when the option is disabled. +function QuestieTooltips:SkinDefaultTooltips() + if (not Questie.db) or (not Questie.db.profile) or Questie.db.profile.elvuiStyleTooltips == false then return end + if IsAddOnLoaded("ElvUI") then return end + for _, name in ipairs({ "GameTooltip", "ItemRefTooltip", "ShoppingTooltip1", "ShoppingTooltip2", "WorldMapTooltip" }) do + local frame = _G[name] + if frame then + _ApplyElvUIStyle(frame) + -- The default UI re-applies its template backdrop (e.g. item-quality borders) on + -- some shows; re-assert our flat look on show so it doesn't revert. + if not frame.__questieElvHook then + frame.__questieElvHook = true + frame:HookScript("OnShow", function(self) + if Questie.db.profile.elvuiStyleTooltips ~= false and not IsAddOnLoaded("ElvUI") then + _ApplyElvUIStyle(self) + end + end) + end + end + end +end + function QuestieTooltips:Initialize() -- For the clicked item frame. ItemRefTooltip:HookScript("OnTooltipSetItem", _QuestieTooltips.AddItemDataToTooltip) @@ -750,6 +796,8 @@ function QuestieTooltips:Initialize() end end end) + + QuestieTooltips:SkinDefaultTooltips() end return QuestieTooltips diff --git a/Modules/Tooltips/TooltipHandler.lua b/Modules/Tooltips/TooltipHandler.lua index 096cd8f..16efba4 100644 --- a/Modules/Tooltips/TooltipHandler.lua +++ b/Modules/Tooltips/TooltipHandler.lua @@ -329,11 +329,9 @@ function _QuestieTooltips:AddUnitDataToTooltip() end end - -- Data-source attribution (opt-in; internally gated and nil when source unknown). - local npcSourceLine = QuestieTooltips:GetDataSourceLine("m_" .. npcId) - if npcSourceLine then - GameTooltip:AddLine(npcSourceLine) - end + -- Data-source attribution is rendered by QuestieLearner's secondary tooltip frame + -- (only when the secondary learner tooltip is enabled), never inline in the main + -- tooltip — see _AddLearnedSpawnTooltipLine. local npcNum = tonumber(npcId) if npcNum then @@ -400,11 +398,6 @@ function _QuestieTooltips:AddItemDataToTooltip() self:AddLine(v) end end - -- Data-source attribution (opt-in; internally gated and nil when source unknown). - local itemSourceLine = QuestieTooltips:GetDataSourceLine("i_" .. (itemId or 0)) - if itemSourceLine then - self:AddLine(itemSourceLine) - end QuestieTooltips.lastGametooltipCount = _QuestieTooltips:CountTooltip() end lastItemId = itemId; @@ -459,14 +452,6 @@ function _QuestieTooltips:AddObjectDataToTooltip(name) end end end - -- Data-source attribution for the hovered object (opt-in; nil when unknown). - local firstObjectId = lookup[1] - if firstObjectId then - local objSourceLine = QuestieTooltips:GetDataSourceLine("o_" .. firstObjectId) - if objSourceLine then - GameTooltip:AddLine(objSourceLine) - end - end if QuestieTooltips.ResizeTooltip then QuestieTooltips:ResizeTooltip(GameTooltip) end