diff --git a/API.lua b/API.lua index 1c94f14..ed48d9f 100644 --- a/API.lua +++ b/API.lua @@ -622,7 +622,7 @@ end -- EXTERNAL HIGHLIGHTS -- Allows external addons to highlight specific unit frames with -- a colored border overlay. These are separate from DF's internal --- selection/aggro/hover highlights and will not conflict. +-- selection/aggro/hover/focus highlights and will not conflict. -- ============================================================ local externalHighlights = {} diff --git a/Config.lua b/Config.lua index 66caca1..59711ae 100644 --- a/Config.lua +++ b/Config.lua @@ -1298,6 +1298,13 @@ DF.PartyDefaults = { hidePlayerFrame = false, showBlizzardSideMenu = true, + -- Focus Highlight + focusHighlightAlpha = 1, + focusHighlightColor = {r = 0, g = 0, b = 1, a = 1}, + focusHighlightInset = 0, + focusHighlightMode = "NONE", + focusHighlightThickness = 1, + -- Hover Highlight hoverHighlightAlpha = 0.8, hoverHighlightColor = {r = 1, g = 1, b = 1, a = 1}, @@ -1860,7 +1867,9 @@ DF.PartyDefaults = { testShowBossDebuffs = false, testShowDispelGlow = false, testShowExternalDef = false, + testShowFocus = false, testShowHealPrediction = false, + testShowHover = false, testShowIcons = true, testShowMissingBuff = false, testShowMyBuffIndicator = false, @@ -2616,6 +2625,13 @@ DF.RaidDefaults = { hidePlayerFrame = false, showBlizzardSideMenu = true, + -- Focus Highlight + focusHighlightAlpha = 1, + focusHighlightColor = {r = 0, g = 0, b = 1, a = 1}, + focusHighlightInset = 0, + focusHighlightMode = "NONE", + focusHighlightThickness = 1, + -- Hover Highlight hoverHighlightAlpha = 0.8, hoverHighlightColor = {r = 1, g = 1, b = 1, a = 1}, @@ -3096,8 +3112,10 @@ DF.RaidDefaults = { testShowAuras = false, testShowBossDebuffs = false, testShowDispelGlow = false, - testShowExternalDef = false, - testShowHealPrediction = false, + testShowExternalDef = false, + testShowFocus = false, + testShowHealPrediction = false, + testShowHover = false, testShowIcons = true, testShowMissingBuff = false, testShowMyBuffIndicator = false, diff --git a/Core.lua b/Core.lua index 38af411..a1b4e9f 100644 --- a/Core.lua +++ b/Core.lua @@ -884,6 +884,12 @@ function DF:LightweightUpdateHighlight(highlightType) inset = db.hoverHighlightInset or 0 alpha = db.hoverHighlightAlpha or 0.8 color = db.hoverHighlightColor or {r = 1, g = 1, b = 1} + elseif highlightType == "focus" then + highlight = frame.focusHighlight or frame.dfFocusHighlight + thickness = db.focusHighlightThickness or 2 + inset = db.focusHighlightInset or 0 + alpha = db.focusHighlightAlpha or 1 + color = db.focusHighlightColor or {r = 0, g = 0, b = 1} elseif highlightType == "aggro" then highlight = frame.aggroHighlight or frame.dfAggroHighlight thickness = db.aggroHighlightThickness or 2 @@ -930,6 +936,8 @@ function DF:LightweightUpdateHighlight(highlightType) highlight = frame.selectionHighlight or frame.dfSelectionHighlight elseif highlightType == "hover" then highlight = frame.hoverHighlight or frame.dfHoverHighlight + elseif highlightType == "focus" then + highlight = frame.focusHighlight or frame.dfFocusHighlight elseif highlightType == "aggro" then highlight = frame.aggroHighlight or frame.dfAggroHighlight end diff --git a/ExportCategories.lua b/ExportCategories.lua index 1c33fd3..72b4452 100644 --- a/ExportCategories.lua +++ b/ExportCategories.lua @@ -911,6 +911,13 @@ DF.ExportCategories = { "hoverHighlightMode", "hoverHighlightThickness", + -- Focus Highlight + "focusHighlightAlpha", + "focusHighlightColor", + "focusHighlightInset", + "focusHighlightMode", + "focusHighlightThickness", + -- Health Threshold Fading "healthFadeEnabled", "healthFadeAlpha", @@ -1055,6 +1062,8 @@ DF.ExportCategories = { "testShowHealPrediction", "testShowAggro", "testShowSelection", + "testShowHover", + "testShowFocus", "testShowOutOfRange", "testShowDispelGlow", "testShowExternalDef", diff --git a/Features/Highlights.lua b/Features/Highlights.lua index d26c5c5..349e0fb 100644 --- a/Features/Highlights.lua +++ b/Features/Highlights.lua @@ -232,10 +232,11 @@ local function GetOrCreateHighlight(frame, highlightType) ch:SetPoint("TOPLEFT", frame, "TOPLEFT", 0, 0) ch:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", 0, 0) ch:SetFrameStrata(frame:GetFrameStrata()) - -- Frame levels: Aggro = +9, Hover = +10, Selection = +11 + -- Frame levels: Aggro = +9, Hover = +10, Selection = +11, Focus = +12 local levelOffset = 9 if highlightType == "Hover" then levelOffset = 10 - elseif highlightType == "Selection" then levelOffset = 11 end + elseif highlightType == "Selection" then levelOffset = 11 + elseif highlightType == "Focus" then levelOffset = 12 end ch:SetFrameLevel(frame:GetFrameLevel() + levelOffset) ch:Hide() @@ -248,6 +249,7 @@ local function GetOrCreateHighlight(frame, highlightType) if self.dfSelectionHighlight then self.dfSelectionHighlight:Hide() end if self.dfHoverHighlight then self.dfHoverHighlight:Hide() end if self.dfAggroHighlight then self.dfAggroHighlight:Hide() end + if self.dfFocusHighlight then self.dfFocusHighlight:Hide() end end) frame.dfHighlightHooked = true end @@ -534,7 +536,7 @@ DF.UpdateHighlightStyleColor = UpdateHighlightStyleColor -- UPDATE HIGHLIGHTS FOR A FRAME -- ============================================================ -function DF:UpdateHighlights(frame, forceSelection, forceAggro) +function DF:UpdateHighlights(frame, forceSelection, forceAggro, forceFocus) if not frame then return end -- DEBUG: Track what's happening @@ -555,6 +557,7 @@ function DF:UpdateHighlights(frame, forceSelection, forceAggro) if frame.dfSelectionHighlight then frame.dfSelectionHighlight:Hide() end if frame.dfHoverHighlight then frame.dfHoverHighlight:Hide() end if frame.dfAggroHighlight then frame.dfAggroHighlight:Hide() end + if frame.dfFocusHighlight then frame.dfFocusHighlight:Hide() end return end @@ -570,6 +573,7 @@ function DF:UpdateHighlights(frame, forceSelection, forceAggro) if frame.dfSelectionHighlight then frame.dfSelectionHighlight:Hide() end if frame.dfHoverHighlight then frame.dfHoverHighlight:Hide() end if frame.dfAggroHighlight then frame.dfAggroHighlight:Hide() end + if frame.dfFocusHighlight then frame.dfFocusHighlight:Hide() end return end @@ -587,7 +591,7 @@ function DF:UpdateHighlights(frame, forceSelection, forceAggro) if debugHighlights then print(" inTestMode:", inTestMode and "true" or "false") end - if inTestMode and forceSelection == nil and forceAggro == nil then + if inTestMode and forceSelection == nil and forceAggro == nil and forceFocus == nil then -- Determine frame index for test mode local frameIndex = nil @@ -637,6 +641,8 @@ function DF:UpdateHighlights(frame, forceSelection, forceAggro) print(" frameIndex:", frameIndex or "nil") print(" db.testShowSelection:", db.testShowSelection and "true" or "false") print(" db.testShowAggro:", db.testShowAggro and "true" or "false") + print(" db.testShowHover:", db.testShowHover and "true" or "false") + print(" db.testShowFocus:", db.testShowFocus and "true" or "false") end -- Apply test mode highlights based on settings @@ -647,6 +653,9 @@ function DF:UpdateHighlights(frame, forceSelection, forceAggro) if db.testShowAggro then forceAggro = (frameIndex == 1) -- Second frame gets aggro end + if db.testShowFocus then + forceFocus = (frameIndex == 2) -- Third frame gets focus + end end if debugHighlights then @@ -753,6 +762,48 @@ function DF:UpdateHighlights(frame, forceSelection, forceAggro) SelectionAnimator_Remove(hoverHighlight) end + -- Check if unit is focused - can be overridden for test mode + local isFocused = forceFocus + if isFocused == nil then + isFocused = unit and UnitIsUnit(unit, "focus") + end + + -- Focus Highlight + local focusHighlight = GetOrCreateHighlight(frame, "Focus") + local focusMode = db.focusHighlightMode or "NONE" + local wantFocus = isFocused and focusMode ~= "NONE" + + if debugHighlights then + print(" wantFocus:", wantFocus and "true" or "false") + end + + if wantFocus then + local c = db.focusHighlightColor or {r = 0, g = 0, b = 1} + local focusThickness = db.focusHighlightThickness or 2 + local focusInset = db.focusHighlightInset or 0 + + -- Apply pixel-perfect adjustments (use PixelPerfectThickness to ensure min 1px) + if db.pixelPerfect then + focusThickness = DF:PixelPerfectThickness(focusThickness) + focusInset = DF:PixelPerfect(focusInset) + end + + ApplyHighlightStyle( + focusHighlight, + focusMode, + focusThickness, + focusInset, + c.r, c.g, c.b, + db.focusHighlightAlpha or 1, + db + ) + else + HideAnimatedBorder(focusHighlight) + HideGlowLayers(focusHighlight) + focusHighlight:Hide() + SelectionAnimator_Remove(focusHighlight) + end + -- Aggro Highlight local aggroHighlight = GetOrCreateHighlight(frame, "Aggro") local wantAggro = isAggro and aggroMode ~= "NONE" @@ -947,6 +998,7 @@ DF.UpdateAllHighlights = UpdateAllHighlights -- PERFORMANCE: Converted from 0.1s timer (~450 calls/sec) to event-driven. -- Events used: -- PLAYER_TARGET_CHANGED - Update all frames (old target loses selection, new gains) +-- PLAYER_FOCUS_CHANGED - Update all frames (old focus loses highlight, new gains) -- UNIT_THREAT_SITUATION_UPDATE - Update specific unit's frame for aggro changes -- PLAYER_REGEN_ENABLED - Safety refresh when leaving combat (clears any stuck highlights) -- GROUP_ROSTER_UPDATE - Safety refresh when group composition changes @@ -956,6 +1008,7 @@ DF.UpdateAllHighlights = UpdateAllHighlights local highlightEventFrame = CreateFrame("Frame") highlightEventFrame:RegisterEvent("PLAYER_TARGET_CHANGED") +highlightEventFrame:RegisterEvent("PLAYER_FOCUS_CHANGED") highlightEventFrame:RegisterEvent("UNIT_THREAT_SITUATION_UPDATE") highlightEventFrame:RegisterEvent("PLAYER_REGEN_ENABLED") highlightEventFrame:RegisterEvent("PLAYER_ENTERING_WORLD") @@ -968,6 +1021,11 @@ highlightEventFrame:SetScript("OnEvent", function(self, event, ...) -- Target changed - need to update ALL frames -- (old target loses selection highlight, new target gains it) UpdateAllHighlights() + + elseif event == "PLAYER_FOCUS_CHANGED" then + -- Focus changed - need to update ALL frames + -- (old focus loses focus highlight, new focus gains it) + UpdateAllHighlights() elseif event == "UNIT_THREAT_SITUATION_UPDATE" then -- Threat changed on specific unit - update that frame diff --git a/Locales/enUS.lua b/Locales/enUS.lua index 1f3565a..3aae027 100644 --- a/Locales/enUS.lua +++ b/Locales/enUS.lua @@ -665,6 +665,8 @@ L["Floating Bar"] = true L["Floating Bar Anchor"] = true L["Floating Bar Position"] = true L["Focus"] = true +L["Focus Highlight"] = true +L["Focus Settings"] = true L["Font"] = true L["Font Outline"] = true L["Font Settings"] = true diff --git a/Options/AutoProfiles.lua b/Options/AutoProfiles.lua index 4391227..5ade84e 100644 --- a/Options/AutoProfiles.lua +++ b/Options/AutoProfiles.lua @@ -169,6 +169,7 @@ local OVERRIDE_TAB_MAP = { {"statusIconFont", "indicators_icons", L["Icons"]}, {"selectionHighlight", "indicators_highlights", L["Highlights"]}, {"hoverHighlight", "indicators_highlights", L["Highlights"]}, + {"focusHighlight", "indicators_highlights", L["Highlights"]}, {"aggroHighlight", "indicators_highlights", L["Highlights"]}, {"aggro", "indicators_highlights", L["Highlights"]}, -- Pinned frames (prefix match for "pinned.N.setting" format) diff --git a/Options/Options.lua b/Options/Options.lua index b989beb..ca9d367 100644 --- a/Options/Options.lua +++ b/Options/Options.lua @@ -6914,7 +6914,7 @@ function DF:SetupGUIPages(GUI, CreateCategory, CreateSubTab, BuildPage) local pageHighlights = CreateSubTab("indicators", "indicators_highlights", L["Highlights"]) BuildPage(pageHighlights, function(self, db, Add, AddSpace, AddSyncPoint) -- Copy button at top - Add(CreateCopyButton(self.child, {"selectionHighlight", "hoverHighlight", "aggroHighlight", "aggro"}, L["Highlights"], "indicators_highlights"), 25, 2) + Add(CreateCopyButton(self.child, {"selectionHighlight", "hoverHighlight", "focusHighlight", "aggroHighlight", "aggro"}, L["Highlights"], "indicators_highlights"), 25, 2) AddSpace(10, "both") @@ -6987,6 +6987,32 @@ function DF:SetupGUIPages(GUI, CreateCategory, CreateSubTab, BuildPage) currentSection = nil AddSpace(10, "both") + -- ======================================== + -- FOCUS HIGHLIGHT SECTION + -- ======================================== + local focusSection = Add(GUI:CreateCollapsibleSection(self.child, L["Focus Highlight"], true), 36, "both") + currentSection = focusSection + + local function HideFocusOptions(d) return d.focusHighlightMode == "NONE" end + + local focusGroup = GUI:CreateSettingsGroup(self.child, 260) + focusGroup:AddWidget(GUI:CreateHeader(self.child, L["Focus Settings"]), 40) + focusGroup:AddWidget(GUI:CreateDropdown(self.child, L["Mode"], highlightModes, db, "focusHighlightMode", function() + self:RefreshStates() + end), 55) + local focusThick = focusGroup:AddWidget(GUI:CreateSlider(self.child, L["Thickness"], 1, 10, 1, db, "focusHighlightThickness", nil, function() DF:LightweightUpdateHighlight("focus") end, true), 55) + focusThick.hideOn = HideFocusOptions + local focusInset = focusGroup:AddWidget(GUI:CreateSlider(self.child, L["Inset"], -10, 10, 1, db, "focusHighlightInset", nil, function() DF:LightweightUpdateHighlight("focus") end, true), 55) + focusInset.hideOn = HideFocusOptions + local focusAlpha = focusGroup:AddWidget(GUI:CreateSlider(self.child, L["Alpha"], 0.1, 1.0, 0.05, db, "focusHighlightAlpha", nil, function() DF:LightweightUpdateHighlight("focus") end, true), 55) + focusAlpha.hideOn = HideFocusOptions + local focusCol = focusGroup:AddWidget(GUI:CreateColorPicker(self.child, L["Color"], db, "focusHighlightColor", false, nil, function() DF:LightweightUpdateHighlight("focus") end, true), 35) + focusCol.hideOn = HideFocusOptions + AddToSection(focusGroup, nil, 1) + + currentSection = nil + AddSpace(10, "both") + -- ======================================== -- AGGRO HIGHLIGHT SECTION -- ======================================== diff --git a/TRANSLATING.md b/TRANSLATING.md index e85ae73..4142cdf 100644 --- a/TRANSLATING.md +++ b/TRANSLATING.md @@ -110,7 +110,7 @@ These appear as GUI control labels, column headers, or dropdown options: | `"Group"` | Raid group | A WoW raid group (1-8), not a generic group. | | `"Handle"` | Drag handle | The UI element you click to drag/move the frame container. | | `"Health"` | Health points | A unit's hit points / life total. | -| `"Highlight"` | Visual overlay | A colored glow or border shown on hover/selection/aggro. | +| `"Highlight"` | Visual overlay | A colored glow or border shown on hover/selection/aggro/focus. | | `"Hook"` | Attachment method | How the pet frame connects to its owner frame. | | `"Horizontal"` | Direction | Left-to-right layout. | | `"Icon"` | Small image | A buff/debuff icon, role icon, or status icon. | diff --git a/TestMode/TestMode.lua b/TestMode/TestMode.lua index 42acb86..c9bebc9 100644 --- a/TestMode/TestMode.lua +++ b/TestMode/TestMode.lua @@ -1145,7 +1145,7 @@ function DF:UpdateTestFrame(frame, index, applyLayout) if ADEngine then ADEngine:ClearFrame(frame) end end - -- Update selection and aggro highlights for test mode + -- Update all highlights (selection, aggro, etc.) for test mode -- UpdateHighlights now handles test mode internally if DF.UpdateHighlights then DF:UpdateHighlights(frame) @@ -6770,6 +6770,9 @@ function DF:CreateTestPanel() panel.showAggroCheck = secHighlights:AddCheckbox("Aggro", "testShowAggro", function() if DF.UpdateAllTestHighlights then DF:UpdateAllTestHighlights() end end, "indicators_highlights") + panel.showFocusCheck = secHighlights:AddCheckbox("Focus", "testShowFocus", function() + if DF.UpdateAllTestHighlights then DF:UpdateAllTestHighlights() end + end, "indicators_highlights") -- ============================================================ -- PRESETS FOOTER @@ -6914,6 +6917,7 @@ function DF:CreateTestPanel() self.showIconsCheck:SetChecked(db.testShowIcons ~= false) self.showSelectionCheck:SetChecked(db.testShowSelection) self.showAggroCheck:SetChecked(db.testShowAggro) + self.showFocusCheck:SetChecked(db.testShowFocus) -- Buff/Debuff sliders local buffCount = db.testBuffCount or 3