-- Helper function to generate ElvUI-style backdrop and shadow border function SFrames:ApplyBackdropStyle(frame, opts) opts = opts or {} local radius = tonumber(opts.cornerRadius) or 0 local showBorder = opts.showBorder ~= false local useRounded = radius and radius > 0 if useRounded then local edgeSize = math.max(8, math.min(18, math.floor(radius + 0.5))) frame:SetBackdrop({ bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", edgeFile = showBorder and "Interface\\Tooltips\\UI-Tooltip-Border" or nil, tile = true, tileSize = 16, edgeSize = edgeSize, insets = { left = 3, right = 3, top = 3, bottom = 3 } }) else frame:SetBackdrop({ bgFile = "Interface\\Buttons\\WHITE8X8", edgeFile = showBorder and "Interface\\Buttons\\WHITE8X8" or nil, tile = false, tileSize = 0, edgeSize = 1, insets = { left = 1, right = 1, top = 1, bottom = 1 } }) end local A = SFrames.ActiveTheme local bgAlpha = opts.bgAlpha if A and A.panelBg then frame:SetBackdropColor(A.panelBg[1], A.panelBg[2], A.panelBg[3], bgAlpha or A.panelBg[4] or 0.9) if showBorder then frame:SetBackdropBorderColor(A.panelBorder[1], A.panelBorder[2], A.panelBorder[3], A.panelBorder[4] or 1) else frame:SetBackdropBorderColor(0, 0, 0, 0) end else frame:SetBackdropColor(0.1, 0.1, 0.1, bgAlpha or 0.9) if showBorder then frame:SetBackdropBorderColor(0, 0, 0, 1) else frame:SetBackdropBorderColor(0, 0, 0, 0) end end end function SFrames:CreateBackdrop(frame) self:ApplyBackdropStyle(frame) end function SFrames:CreateRoundBackdrop(frame) frame:SetBackdrop({ bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", tile = true, tileSize = 16, edgeSize = 14, insets = { left = 3, right = 3, top = 3, bottom = 3 } }) local A = SFrames.ActiveTheme if A and A.panelBg then frame:SetBackdropColor(A.panelBg[1], A.panelBg[2], A.panelBg[3], A.panelBg[4] or 0.95) frame:SetBackdropBorderColor(A.panelBorder[1], A.panelBorder[2], A.panelBorder[3], A.panelBorder[4] or 0.9) else frame:SetBackdropColor(0.08, 0.08, 0.10, 0.95) frame:SetBackdropBorderColor(0.3, 0.3, 0.35, 1) end end function SFrames:CreateUnitBackdrop(frame) SFrames:CreateBackdrop(frame) frame:SetBackdropBorderColor(0, 0, 0, 1) end function SFrames:ApplyConfiguredUnitBackdrop(frame, prefix, isPortrait) if not frame then return end local db = SFramesDB or {} local bgAlphaKey = prefix .. (isPortrait and "PortraitBgAlpha" or "BgAlpha") self:ApplyBackdropStyle(frame, { showBorder = false, cornerRadius = 0, bgAlpha = tonumber(db[bgAlphaKey]) or (isPortrait and tonumber(db[prefix .. "BgAlpha"]) or nil), }) end -------------------------------------------------------------------------------- -- Frame Style Preset helpers -------------------------------------------------------------------------------- function SFrames:GetFrameStylePreset() return (SFramesDB and SFramesDB.frameStylePreset) or "classic" end function SFrames:IsGradientStyle() return self:GetFrameStylePreset() == "gradient" end -- Apply a gradient darkening overlay on a StatusBar. -- Uses a separate Texture on OVERLAY layer with purple-black vertex color -- and alpha gradient from 0 (left) to ~0.6 (right). -- This approach does NOT touch the StatusBar texture itself, -- so it survives SetStatusBarColor calls. function SFrames:ApplyGradientStyle(bar) if not bar then return end if not bar._gradOverlay then local ov = bar:CreateTexture(nil, "OVERLAY") ov:SetTexture("Interface\\Buttons\\WHITE8X8") bar._gradOverlay = ov end local ov = bar._gradOverlay ov:ClearAllPoints() ov:SetAllPoints(bar:GetStatusBarTexture()) -- Dark purple-ish tint color ov:SetVertexColor(0.04, 0.0, 0.08, 1) -- Alpha gradient: left fully transparent → right 65% opaque if ov.SetGradientAlpha then ov:SetGradientAlpha("HORIZONTAL", 1, 1, 1, 0, 1, 1, 1, 0.65) end ov:Show() end function SFrames:RemoveGradientStyle(bar) if not bar then return end if bar._gradOverlay then bar._gradOverlay:Hide() end end -- No-op wrappers kept for compatibility with unit files function SFrames:ApplyBarGradient(bar) -- Gradient is now persistent overlay; no per-color-update needed end function SFrames:ClearBarGradient(bar) self:RemoveGradientStyle(bar) end -- Strip backdrop from a frame (used in gradient style) function SFrames:ClearBackdrop(frame) if not frame then return end if frame.SetBackdrop then frame:SetBackdrop(nil) end end -- Generator for StatusBars function SFrames:CreateStatusBar(parent, name) local bar = CreateFrame("StatusBar", name, parent) bar:SetStatusBarTexture(SFrames:GetTexture()) if (not SFramesDB or SFramesDB.smoothBars ~= false) and SmoothBar then SmoothBar(bar) end return bar end function SFrames:ApplyStatusBarTexture(bar, settingKey, fallbackKey) if not bar or not bar.SetStatusBarTexture then return end bar:SetStatusBarTexture(self:ResolveBarTexture(settingKey, fallbackKey)) end -- Generator for FontStrings function SFrames:CreateFontString(parent, size, justifyH) local fs = parent:CreateFontString(nil, "OVERLAY") fs:SetFont(SFrames:GetFont(), size or 12, SFrames.Media.fontOutline) fs:SetJustifyH(justifyH or "CENTER") fs:SetTextColor(1, 1, 1) return fs end function SFrames:ApplyFontString(fs, size, fontSettingKey, fallbackFontKey, outlineSettingKey, fallbackOutlineKey) if not fs or not fs.SetFont then return end local fontPath = self:ResolveFont(fontSettingKey, fallbackFontKey) local outline = self:ResolveFontOutline(outlineSettingKey, fallbackOutlineKey) fs:SetFont(fontPath, size or 12, outline) end function SFrames:FormatCompactNumber(value) local num = tonumber(value) or 0 local sign = num < 0 and "-" or "" num = math.abs(num) if num >= 1000000 then return string.format("%s%.1fM", sign, num / 1000000) elseif num >= 10000 then return string.format("%s%.1fK", sign, num / 1000) end return sign .. tostring(math.floor(num + 0.5)) end function SFrames:FormatCompactPair(currentValue, maxValue) return self:FormatCompactNumber(currentValue) .. " / " .. self:FormatCompactNumber(maxValue) end -- Generator for 3D Portraits function SFrames:CreatePortrait(parent, name) local portrait = CreateFrame("PlayerModel", name, parent) return portrait end -------------------------------------------------------------------------------- -- Class Icon (circular class portraits from UI-Classes-Circles.tga) -------------------------------------------------------------------------------- local CLASS_ICON_PATH = "Interface\\AddOns\\Nanami-UI\\img\\UI-Classes-Circles" SFrames.CLASS_ICON_TCOORDS = { ["WARRIOR"] = { 0, 0.25, 0, 0.25 }, ["MAGE"] = { 0.25, 0.49609375, 0, 0.25 }, ["ROGUE"] = { 0.49609375, 0.7421875, 0, 0.25 }, ["DRUID"] = { 0.7421875, 0.98828125, 0, 0.25 }, ["HUNTER"] = { 0, 0.25, 0.25, 0.5 }, ["SHAMAN"] = { 0.25, 0.49609375, 0.25, 0.5 }, ["PRIEST"] = { 0.49609375, 0.7421875, 0.25, 0.5 }, ["WARLOCK"] = { 0.7421875, 0.98828125, 0.25, 0.5 }, ["PALADIN"] = { 0, 0.25, 0.5, 0.75 }, } function SFrames:CreateClassIcon(parent, size) local sz = size or 20 local overlay = CreateFrame("Frame", nil, parent) overlay:SetFrameLevel((parent:GetFrameLevel() or 0) + 3) overlay:SetWidth(sz) overlay:SetHeight(sz) local icon = overlay:CreateTexture(nil, "OVERLAY") icon:SetTexture(CLASS_ICON_PATH) icon:SetAllPoints(overlay) icon:Hide() icon.overlay = overlay return icon end function SFrames:SetClassIcon(icon, class) if not icon then return end local coords = self.CLASS_ICON_TCOORDS[class] if coords then icon:SetTexCoord(coords[1], coords[2], coords[3], coords[4]) icon:Show() if icon.overlay then icon.overlay:Show() end else icon:Hide() if icon.overlay then icon.overlay:Hide() end end end -------------------------------------------------------------------------------- -- UI Icon helpers (icon.tga sprite sheet) -- SFrames.ICON_PATH and SFrames.ICON_TCOORDS are defined in IconMap.lua -------------------------------------------------------------------------------- local ICON_SET_VALID = { ["icon"] = true, ["icon2"] = true, ["icon3"] = true, ["icon4"] = true, ["icon5"] = true, ["icon6"] = true, ["icon7"] = true, ["icon8"] = true, } local function GetIconPath() local set = SFramesDB and SFramesDB.Theme and SFramesDB.Theme.iconSet if set and ICON_SET_VALID[set] then return "Interface\\AddOns\\Nanami-UI\\img\\" .. set end return "Interface\\AddOns\\Nanami-UI\\img\\icon" end local ICON_TEX = GetIconPath() local ICON_TC_FALLBACK = { ["logo"] = { 0, 0.125, 0, 0.125 }, ["save"] = { 0.125, 0.25, 0, 0.125 }, ["close"] = { 0.25, 0.375, 0, 0.125 }, ["offline"] = { 0.375, 0.5, 0, 0.125 }, ["chat"] = { 0, 0.125, 0.125, 0.25 }, ["settings"] = { 0.125, 0.25, 0.125, 0.25 }, ["ai"] = { 0.25, 0.375, 0.125, 0.25 }, ["backpack"] = { 0.375, 0.5, 0.125, 0.25 }, ["exit"] = { 0, 0.125, 0.25, 0.375 }, ["party"] = { 0.125, 0.25, 0.25, 0.375 }, ["loot"] = { 0.25, 0.375, 0.25, 0.375 }, ["dragon"] = { 0.375, 0.5, 0.25, 0.375 }, ["casting"] = { 0, 0.125, 0.375, 0.5 }, ["attack"] = { 0.125, 0.25, 0.375, 0.5 }, ["damage"] = { 0.25, 0.375, 0.375, 0.5 }, ["latency"] = { 0.375, 0.5, 0.375, 0.5 }, ["admin"] = { 0, 0.125, 0.125, 0.25 }, ["bank"] = { 0.25, 0.375, 0.25, 0.375 }, ["perf"] = { 0.25, 0.375, 0.375, 0.5 }, } local ICON_TC = SFrames.ICON_TCOORDS or ICON_TC_FALLBACK function SFrames:CreateIcon(parent, iconKey, size) local sz = size or 16 local tex = parent:CreateTexture(nil, "ARTWORK") tex:SetTexture(GetIconPath()) tex:SetWidth(sz) tex:SetHeight(sz) local coords = ICON_TC[iconKey] if not coords and self.ICON_TCOORDS then coords = self.ICON_TCOORDS[iconKey] end if coords then tex:SetTexCoord(coords[1], coords[2], coords[3], coords[4]) end return tex end function SFrames:SetIcon(tex, iconKey) if not tex then return end tex:SetTexture(GetIconPath()) local coords = ICON_TC[iconKey] if not coords and self.ICON_TCOORDS then coords = self.ICON_TCOORDS[iconKey] end if coords then tex:SetTexCoord(coords[1], coords[2], coords[3], coords[4]) tex:Show() else tex:Hide() end end function SFrames:CreateIconButton(parent, iconKey, iconSize, label, width, height, onClick) local A = SFrames.ActiveTheme local btn = CreateFrame("Button", nil, parent) btn:SetWidth(width or 100) btn:SetHeight(height or 24) btn:SetBackdrop({ bgFile = "Interface\\Buttons\\WHITE8X8", edgeFile = "Interface\\Buttons\\WHITE8X8", tile = false, tileSize = 0, edgeSize = 1, insets = { left = 1, right = 1, top = 1, bottom = 1 } }) local bgC = (A and A.panelBg) or { 0.12, 0.06, 0.10, 0.9 } local bdC = (A and A.sepColor) or { 0.45, 0.25, 0.35, 0.8 } local hvC = (A and A.buttonHoverBg) or { 0.22, 0.12, 0.18, 0.95 } local hvB = (A and A.accent) or { 0.70, 0.40, 0.55, 1 } local dnC = (A and A.buttonDownBg) or { 0.08, 0.04, 0.06, 0.95 } local txC = (A and A.buttonText) or { 0.90, 0.76, 0.84 } local txH = (A and A.buttonActiveText) or { 1, 0.92, 0.96 } btn:SetBackdropColor(bgC[1], bgC[2], bgC[3], bgC[4] or 0.9) btn:SetBackdropBorderColor(bdC[1], bdC[2], bdC[3], bdC[4] or 0.8) local iSz = iconSize or 14 local ico = self:CreateIcon(btn, iconKey, iSz) ico:SetPoint("LEFT", btn, "LEFT", 6, 0) btn.icon = ico if label and label ~= "" then local fs = btn:CreateFontString(nil, "OVERLAY") fs:SetFont(self:GetFont(), 10, self.Media.fontOutline or "OUTLINE") fs:SetPoint("LEFT", ico, "RIGHT", 4, 0) fs:SetPoint("RIGHT", btn, "RIGHT", -6, 0) fs:SetJustifyH("LEFT") fs:SetTextColor(txC[1], txC[2], txC[3]) fs:SetText(label) btn.label = fs end btn:SetScript("OnEnter", function() this:SetBackdropColor(hvC[1], hvC[2], hvC[3], hvC[4] or 0.95) this:SetBackdropBorderColor(hvB[1], hvB[2], hvB[3], hvB[4] or 1) if this.label then this.label:SetTextColor(txH[1], txH[2], txH[3]) end end) btn:SetScript("OnLeave", function() this:SetBackdropColor(bgC[1], bgC[2], bgC[3], bgC[4] or 0.9) this:SetBackdropBorderColor(bdC[1], bdC[2], bdC[3], bdC[4] or 0.8) if this.label then this.label:SetTextColor(txC[1], txC[2], txC[3]) end end) btn:SetScript("OnMouseDown", function() this:SetBackdropColor(dnC[1], dnC[2], dnC[3], dnC[4] or 0.95) end) btn:SetScript("OnMouseUp", function() this:SetBackdropColor(hvC[1], hvC[2], hvC[3], hvC[4] or 0.95) end) if onClick then btn:SetScript("OnClick", onClick) end return btn end