--[[----------------------------------------------------------------------- oUF: Stardust - a layout for the oUF framework Copyright (c) 2016 Andrew Mordecai <armordecai@protonmail.ch> This code is released under the zlib license; see LICENSE for details. -------------------------------------------------------------------------]] local _, Stardust = ... local config = Stardust.config local LibSharedMedia = LibStub("LibSharedMedia-3.0") local function abbrev(n) local v = abs(n) if v > 1000000000 then return format("%.0fb", n / 1000000000) elseif v > 10000000 then return format("%.0fm", n / 1000000) elseif v > 1000000 then return format("%.1fm", n / 1000000) elseif v > 10000 then return format("%.0fk", n / 1000) elseif v > 1000 then return format("%.1fk", n / 1000) else return n end end --------------------------- -- Frames --------------------------- function Stardust.OnEnter(self) self.isMouseOver = true UnitFrame_OnEnter(self) self:UpdateAllElements("OnEnter") end function Stardust.OnLeave(self) self.isMouseOver = false UnitFrame_OnLeave(self) self:UpdateAllElements("OnLeave") end --------------------------- -- Health --------------------------- function Stardust.PostUpdateHealth(element, unit, cur, max) element:SetValue(max - cur) element.value:SetText(element.__owner.isMouseOver and abbrev(cur) or cur < max and abbrev(-max + cur) or "") end --------------------------- -- Power / DruidMana / DemonicFury --------------------------- local UnitIsDead = UnitIsDead function Stardust.PostUpdatePower(element, unit, cur, max) if UnitIsDead(unit) then local t = oUF.colors.disconnected local r, g, b = t[1], t[2], t[3] local mu = element.bg.multiplier or 1 element:SetStatusBarColor(r, g, b) element.bg:SetVertexColor(r * mu, g * mu, b * mu) element.value:SetText("") element:SetValue(0) else element.value:SetText(max > 0 and element.__owner.isMouseOver and abbrev(cur) or "") end end --------------------------- -- Stagger --------------------------- function Stardust.PostUpdateStagger(element, maxHealth, stagger, staggerPercent, r, g, b) element.value:SetFormattedText("%d%%", staggerPercent) end --------------------------- -- ClassIcons --------------------------- function Stardust.ResizeClassIcons(element) local COUNT = element.__max local widthTotal = ((Stardust.config.width - 6) * 0.7) - (COUNT * 3) local widthEach = floor(widthTotal / COUNT + 0.5) for i = 1, COUNT do element[i]:SetWidth(widthEach) end end function Stardust.PostUpdateClassIcons(element, cur, max, maxChanged, event) if maxChanged then Stardust.ResizeClassIcons(element) if element.__owner.Stagger then element.__owner.Stagger:SetPoint("TOPLEFT", element[max], 3, 0) end end end --------------------------- -- CPoints / AuraStack / BurningEmbers --------------------------- function Stardust.PreUpdateCPoints(element) element.PreUpdate = nil Stardust.ResizeClassIcons(element) end function Stardust.PostUpdateCPoints(element, cur) if cur == 0 or element.__disabled then return end for i = 1, element.__max or #element do local bar = element[i] if i > cur then bar:SetValue(0) bar:Show() else bar:SetValue(1) end end end --------------------------- -- Runes --------------------------- function Stardust.PostUpdateRuneType(element, rune, index, alt) local r, g, b = rune:GetStatusBarColor() rune.bg:SetVertexColor(r * 0.35, g * 0.35, b * 0.35) end function Stardust.PostUpdateRune(element, rune, index, start, duration, ready) -- The number of runes isn't dynamic, so they only need to be resized once per frame. element.PostUpdateRune = nil Stardust.ResizeClassIcons(element) end --------------------------- -- Totems -- Partly adapted from oUF_Phanx --------------------------- local OnUpdateTotem do local ceil = ceil local unpack = unpack local SMOOTH_COLORS = oUF.colors.smooth local ColorGradient = oUF.ColorGradient function OnUpdateTotem(bar, elapsed) local t = bar.remaining - elapsed if t > 0 then bar.remaining = t bar:SetValue(t) if bar.__owner.isMouseOver then bar.value:SetText(ceil(t)) bar.value:SetTextColor(ColorGradient(t, bar.max, unpack(SMOOTH_COLORS))) else bar.value:SetText("") end else bar:SetValue(0) bar.value:SetText("") end end end function Stardust.PostUpdateTotem(element, index, _, name, start, duration) local bar = element[index] bar.remaining, bar.max = start + duration - GetTime(), duration if duration > 0 then bar:SetMinMaxValues(0, duration) bar:SetScript("OnUpdate", OnUpdateTotem) else bar:SetScript("OnUpdate", nil) bar:SetValue(0) bar.value:SetText("") end end function Stardust.PreUpdateTotems(element, index) -- The number of totems isn't dynamic, so they only need to be resized once per frame. element.PreUpdate = nil Stardust.ResizeClassIcons(element) end --------------------------- -- Status icons --------------------------- function Stardust.PostUpdateStatusIcon(element) if not element:IsShown() then return end local icons, x = element.__owner.StatusIcons, 2 for i = 1, #icons do local icon = icons[i] if icon == element then break elseif icon:IsShown() and icon:GetTexture() then x = x + icon:GetWidth() end end element:SetPoint("LEFT", element.__owner.Health, "TOPLEFT", x + element.offsetX, 0 + element.offsetY) end local updatingCombatOrResting function Stardust.PostUpdateCombat(element) if not updatingCombatOrResting then updatingCombatOrResting = true element.__owner.Resting:ForceUpdate() updatingCombatOrResting = nil end end function Stardust.PostUpdateResting(element) if not updatingCombatOrResting then updatingCombatOrResting = true element.__owner.Combat:ForceUpdate() updatingCombatOrResting = nil end end --------------------------- -- AFK text --------------------------- function Stardust.PostUpdateAFK(element, isAFK) local Power = element.__owner.Power if Power and Power.value then Power.value:SetShown(not isAFK) end end --------------------------- -- DispelIcon (redirected to frame.Shadows) --------------------------- Stardust.DispelIconStub = { SetTexture = nop, SetVertexColor = nop, Show = nop, Hide = nop, } function Stardust.PostUpdateDispelIcon(element, dispelType, r, g, b) local frame = element.__owner if not frame.__shadowSize then frame.__shadowSize = frame.Shadows[1]:GetWidth() end if dispelType then frame:SetShadowColor(r, g, b) frame:SetShadowSize(frame.__shadowSize * 2) else frame:SetShadowColor() frame:SetShadowSize(frame.__shadowSize) end end --------------------------- -- Auras --------------------------- function Stardust.PostCreateAuraIcon(element, button) button.cd:SetDrawBling(false) button.cd:SetDrawEdge(false) button.cd:SetReverse(true) button.icon:SetTexCoord(0.07, 0.93, 0.07, 0.93) button.count:SetFont(LibSharedMedia:Fetch("font", config.numberFont), config.numberSmall, "OUTLINE") button.count:SetShadowOffset(0, 0) button.count:SetTextColor(1, 1, 1) button.backdrop = button:CreateTexture(nil, "BACKGROUND") button.backdrop:SetPoint("BOTTOMLEFT", -1, -1) button.backdrop:SetPoint("TOPRIGHT", 1, 1) button.backdrop:SetTexture("Interface\\BUTTONS\\WHITE8X8") Stardust.AddShadow(button, 6) end function Stardust.PostUpdateAuraIcon(element, unit, button, index, offset) local _, _, _, _, dispelType, duration, timeLeft, _, isStealable, _, spellID, canApplyAura, isBossDebuff = UnitAura(unit, index, button.filter) if timeLeft > 600 then button.cd:Hide() end local color = button.isDebuff and dispelType and DebuffTypeColor[dispelType] if color then button.backdrop:SetVertexColor(color.r, color.g, color.b) else button.backdrop:SetVertexColor(0, 0, 0) end end --------------------------- -- Shadow --------------------------- local SHADOW_SIZE = 3 local SHADOW_COLOR = { 0, 0, 0 } local SHADOW_SEGMENTS = { -- 1 point, 2 coordLeft, 3 coordRight, 4 coordTop, 5 coordBottom, 6 offsetMultiplierX, 7 offsetMultiplierY { "TOPLEFT", 0, 1/3, 0, 1/3, -1, 1 }, { "TOPRIGHT", 2/3, 1, 0, 1/3, 1, 1 }, { "BOTTOMRIGHT", 2/3, 1, 2/3, 1, 1, -1 }, { "BOTTOMLEFT", 0, 1/3, 2/3, 1, -1, -1 }, { "TOP", 1/3, 2/3, 0, 1/3, 0, 1 }, { "RIGHT", 2/3, 1, 1/3, 2/3, 1, 0 }, { "BOTTOM", 1/3, 2/3, 2/3, 1, 0, -1 }, { "LEFT", 0, 1/3, 1/3, 2/3, -1, 0 }, } local function SetShadowColor(self, r, g, b, a) if not r or not g or not b then r, g, b = unpack(SHADOW_COLOR) end for i = 1, #self.Shadows do self.Shadows[i]:SetVertexColor(r, g, b, a or 1) end end local function SetShadowSize(self, size, offset) if not size then size = SHADOW_SIZE end local Shadows = self.Shadows for i = 1, #Shadows do Shadows[i]:SetSize(size, size) end local d = offset or floor(size * 2 / 3 + 0.5) Shadows[1]:SetPoint("TOPLEFT", -d, d) Shadows[2]:SetPoint("TOPRIGHT", d, d) Shadows[3]:SetPoint("BOTTOMRIGHT", d, -d) Shadows[4]:SetPoint("BOTTOMLEFT", -d, -d) local showH = self:GetWidth() > (2 * (size - d)) local showV = self:GetHeight() > (2 * (size - d)) Shadows[5]:SetShown(showH) Shadows[6]:SetShown(showV) Shadows[7]:SetShown(showH) Shadows[8]:SetShown(showV) end function Stardust.AddShadow(self, size, r, g, b, a) local Shadows = {} for i = 1, #SHADOW_SEGMENTS do local seg = SHADOW_SEGMENTS[i] local tex = self:CreateTexture(nil, "BACKGROUND") tex:SetTexture("Interface\\AddOns\\oUF_Stardust\\Textures\\glow") tex:SetTexCoord(seg[2], seg[3], seg[4], seg[5]) tex:SetVertexColor(0, 0, 0) Shadows[i] = tex end -- TOP Shadows[5]:SetPoint("LEFT", Shadows[1], "RIGHT") Shadows[5]:SetPoint("RIGHT", Shadows[2], "LEFT") -- RIGHT Shadows[6]:SetPoint("TOP", Shadows[2], "BOTTOM") Shadows[6]:SetPoint("BOTTOM", Shadows[3], "TOP") -- BOTTOM Shadows[7]:SetPoint("LEFT", Shadows[4], "RIGHT") Shadows[7]:SetPoint("RIGHT", Shadows[3], "LEFT") -- LEFT Shadows[8]:SetPoint("TOP", Shadows[1], "BOTTOM") Shadows[8]:SetPoint("BOTTOM", Shadows[4], "TOP") self.Shadows = Shadows self.SetBackdropBorderColor = SetShadowColor self.SetShadowColor = SetShadowColor self.SetShadowSize = SetShadowSize self:SetShadowColor(r, g, b, a) self:SetShadowSize(size) return Shadows end