diff --git a/.luacheckrc b/.luacheckrc
index 1aebee159..bc819c12c 100644
--- a/.luacheckrc
+++ b/.luacheckrc
@@ -72,6 +72,10 @@ globals = {
"C_PlayerInfo",
"C_TradeSkillUI",
"GetItemLevelColor",
+ "GetAverageItemLevel",
+ "GetTexCoordsForRoleSmallCircle",
+ "FormatShortDate",
+ "BreakUpLargeNumbers",
"HelpTip",
"BattlePassSplashFrame",
"C_BattlePass",
diff --git a/Core/API.lua b/Core/API.lua
index 7cfe475d7..ae9bbb8cf 100644
--- a/Core/API.lua
+++ b/Core/API.lua
@@ -15,8 +15,16 @@ local GetTalentTabInfo = GetTalentTabInfo
local RequestBattlefieldScoreData = RequestBattlefieldScoreData
local UnitGroupRolesAssigned = UnitGroupRolesAssigned
local UnitHasVehicleUI = UnitHasVehicleUI
+local UnitExists = UnitExists
+local UnitGUID = UnitGUID
+local UnitThreatSituation = UnitThreatSituation
local IsInInstance = IsInInstance
+local IsInGroup = IsInGroup
+local IsInRaid = IsInRaid
local IsSpellKnown = IsSpellKnown
+local GetNumRaidMembers = GetNumRaidMembers
+local GetNumPartyMembers = GetNumPartyMembers
+local GetPartyAssignment = GetPartyAssignment
local MAX_TALENT_TABS = MAX_TALENT_TABS
local NONE = NONE
@@ -109,6 +117,78 @@ function E:GetTalentSpecInfo(isInspect)
return specIdx, specName, specIcon
end
+E.GroupRoles = {}
+E.GroupUnitsByRole = {
+ TANK = {},
+ HEALER = {},
+ DAMAGER = {},
+ NONE = {}
+}
+
+function E:UnitTankedByGroup(unit)
+ for _, unitToken in next, E.GroupUnitsByRole.TANK do
+ if E:GetThreatSituation(unit, unitToken) == 3 then
+ return unitToken
+ end
+ end
+end
+
+function E:GetThreatSituation(unit, feedbackUnit)
+ if not unit or not UnitExists(unit) then return end
+
+ if feedbackUnit and feedbackUnit ~= unit and UnitExists(feedbackUnit) then
+ return UnitThreatSituation(feedbackUnit, unit)
+ else
+ return UnitThreatSituation(unit)
+ end
+end
+
+function E:PARTY_MEMBERS_CHANGED()
+ local isInGroup = IsInGroup()
+ E.IsInGroup = isInGroup
+
+ wipe(E.GroupRoles)
+
+ for _, units in next, E.GroupUnitsByRole do
+ wipe(units)
+ end
+
+ if E.IsInGroup then
+ for i = 1, GetNumPartyMembers() do
+ local unit = "party"..i
+ local guid = UnitGUID(unit)
+ local role = guid and ((GetPartyAssignment("MAINTANK", unit) and "TANK" or "NONE") or UnitGroupRolesAssigned(unit))
+ if role then
+ E.GroupRoles[guid] = role
+ E.GroupUnitsByRole[role][guid] = unit
+ end
+ end
+ end
+end
+
+function E:RAID_ROSTER_UPDATE()
+ local isInRaid = IsInRaid()
+ E.IsInGroup = isInRaid
+
+ wipe(E.GroupRoles)
+
+ for _, units in next, E.GroupUnitsByRole do
+ wipe(units)
+ end
+
+ if E.IsInGroup then
+ for i = 1, GetNumRaidMembers() do
+ local unit = "raid"..i
+ local guid = UnitGUID(unit)
+ local role = guid and ((GetPartyAssignment("MAINTANK", unit) and "TANK" or "NONE") or UnitGroupRolesAssigned(unit))
+ if role then
+ E.GroupRoles[guid] = role
+ E.GroupUnitsByRole[role][guid] = unit
+ end
+ end
+ end
+end
+
function E:CheckRole(event)
local talentTree = self:GetTalentSpecInfo()
local role
@@ -127,6 +207,8 @@ function E:CheckRole(event)
if not role then role = "Melee" end
+ self.myrole = self:GetPlayerRole()
+
if self.Role ~= role then
self.Role = role
self.TalentTree = talentTree
@@ -472,6 +554,10 @@ end
function E:LoadAPI()
self:RegisterEvent("PLAYER_LEVEL_UP")
self:RegisterEvent("PLAYER_ENTERING_WORLD")
+ self:RegisterEvent("PARTY_MEMBERS_CHANGED")
+ self:RegisterEvent("RAID_ROSTER_UPDATE")
+ self:PARTY_MEMBERS_CHANGED()
+ self:RAID_ROSTER_UPDATE()
self:RegisterEvent("SPELL_UPDATE_USABLE", "CheckRole")
self:RegisterEvent("ACTIVE_TALENT_GROUP_CHANGED", "CheckRole")
self:RegisterEvent("PLAYER_TALENT_UPDATE", "CheckRole")
diff --git a/Core/Animation.lua b/Core/Animation.lua
index 5e43656f0..84048e5c9 100644
--- a/Core/Animation.lua
+++ b/Core/Animation.lua
@@ -9,6 +9,12 @@ local random, next, unpack, strsub = random, next, unpack, strsub
E.AnimShake = {{-9,7,-7,12}, {-5,9,-9,5}, {-5,7,-7,5}, {-9,9,-9,9}, {-5,7,-7,5}, {-9,7,-9,5}}
E.AnimShakeH = {-5,5,-2,5,-2,5}
+E.AnimElastic = {
+ function(anim) anim:Stop() anim.elastic[2]:Play() end,
+ function(anim) anim:Stop() if anim.loop then anim.elastic[1]:Play() end end,
+ function(anim) anim:Stop() anim.elastic[4]:Play() end,
+ function(anim) anim:Stop() if anim.loop then anim.elastic[3]:Play() end end
+}
function E:FlashLoopFinished(requested)
if not requested then self:Play() end
@@ -63,6 +69,22 @@ function E:SetUpAnimGroup(obj, Type, ...)
shake.path[4]:SetOrder(4)
shake.path[5]:SetOrder(5)
shake.path[6]:SetOrder(6)
+ elseif Type == "Elastic" then
+ local width, height, duration, loop = ...
+ local elastic = _G.CreateAnimationGroup(obj)
+ obj.elastic = elastic
+
+ for i = 1, 4 do
+ local anim = elastic:CreateAnimation(i < 3 and "width" or "height")
+ anim:SetChange((i==1 and width*0.45) or (i==2 and width) or (i==3 and height*0.45) or height)
+ anim:SetEasing("inout-elastic")
+ anim:SetDuration(duration)
+ anim:SetScript("OnFinished", E.AnimElastic[i])
+ anim.elastic = elastic
+ anim.loop = loop
+
+ elastic[i] = anim
+ end
else
local x, y, duration, customName = ...
if not customName then customName = "anim" end
@@ -291,4 +313,20 @@ function E:UIFrameFadeRemoveFrame(frame)
FADEFRAMES[frame] = nil
end
-end
\ No newline at end of file
+end
+
+function E:Elasticize(obj, width, height)
+ if not obj.elastic then
+ E:SetUpAnimGroup(obj, "Elastic", width or obj:GetWidth(), height or obj:GetHeight(), 2, false)
+ end
+
+ obj.elastic[1]:Play()
+ obj.elastic[3]:Play()
+end
+
+function E:StopElasticize(obj)
+ if obj.elastic then
+ obj.elastic[1]:Stop(true)
+ obj.elastic[3]:Stop(true)
+ end
+end
diff --git a/Core/Config.lua b/Core/Config.lua
index a0ea89348..c79f2304a 100644
--- a/Core/Config.lua
+++ b/Core/Config.lua
@@ -6,14 +6,18 @@ local _G = _G
local unpack = unpack
local type, ipairs, tonumber = type, ipairs, tonumber
local floor, select = floor, select
+local min = min
--WoW API / Variables
local CreateFrame = CreateFrame
local IsAddOnLoaded = IsAddOnLoaded
local InCombatLockdown = InCombatLockdown
local EditBox_ClearFocus = EditBox_ClearFocus
local RESET = RESET
+local GetTime = GetTime
+local C_Timer = C_Timer
local selectedValue, grid = "ALL"
+local statusTextHooked = {}
E.ConfigModeLayouts = {
"ALL",
@@ -506,4 +510,1270 @@ function E:CreateMoverPopup()
end)
S:HandleNextPrevButton(rightButton)
rightButton:SetSize(22, 22)
-end
\ No newline at end of file
+end --=================== Хром окна настроек (перенесено из ElvUI-development) ===================
+local hooksecurefunc = hooksecurefunc
+local next, sort, gsub, wipe = next, sort, gsub, wipe
+local strsplit, strmatch, strtrim, strlower = strsplit, strmatch, strtrim, strlower
+local pairs, tinsert, tContains = pairs, tinsert, tContains
+local EditBox_HighlightText = EditBox_HighlightText
+local GetAddOnInfo = GetAddOnInfo
+local LoadAddOn = LoadAddOn
+local GetMouseFocus = GetMouseFocus
+local UIParent = UIParent
+
+if not E.ConfigTooltip then
+ E.ConfigTooltip = CreateFrame('GameTooltip', 'ElvUI_ConfigTooltip', UIParent, 'GameTooltipTemplate')
+end
+
+function E:Config_ResetSettings()
+ E.configSavedPositionTop, E.configSavedPositionLeft = nil, nil
+ E.global.general.AceGUI = E:CopyTable({}, E.DF.global.general.AceGUI)
+end
+
+function E:Config_GetPosition()
+ return E.configSavedPositionTop, E.configSavedPositionLeft
+end
+
+function E:Config_GetSize()
+ return E.global.general.AceGUI.width, E.global.general.AceGUI.height
+end
+
+function E:Config_GetStatus(frame)
+ local status = frame and frame.obj and frame.obj.status
+ local selected = status and status.groups and status.groups.selected
+
+ return status, selected
+end
+
+function E:Config_UpdateSize(reset)
+ local frame = E:Config_GetWindow()
+ if not frame then return end
+
+ local maxWidth, maxHeight = self.UIParent:GetSize()
+ if frame.SetResizeBounds then
+ frame:SetResizeBounds(800, 600, maxWidth-50, maxHeight-50)
+ else
+ frame:SetMinResize(800, 600)
+ frame:SetMaxResize(maxWidth-50, maxHeight-50)
+ end
+
+ self.Libs.AceConfigDialog:SetDefaultSize('ElvUI', E:Config_GetDefaultSize())
+
+ local status = E:Config_GetStatus(frame)
+ if status then
+ if reset then
+ E:Config_ResetSettings()
+
+ status.top, status.left = E:Config_GetPosition()
+ status.width, status.height = E:Config_GetDefaultSize()
+
+ frame.obj:ApplyStatus()
+ else
+ local top, left = E:Config_GetPosition()
+ if top and left then
+ status.top, status.left = top, left
+
+ frame.obj:ApplyStatus()
+ end
+ end
+
+ E:Config_UpdateLeftScroller(frame)
+ end
+end
+
+function E:Config_GetDefaultSize()
+ local width, height = E:Config_GetSize()
+ local maxWidth, maxHeight = E.UIParent:GetSize()
+ width, height = min(maxWidth-50, width), min(maxHeight-50, height)
+ return width, height
+end
+
+function E:Config_StopMoving()
+ local frame = self
+ if not (frame and frame.obj and frame.obj.status) then
+ frame = self and self.GetParent and self:GetParent()
+ end
+
+ local status = frame and E:Config_GetStatus(frame)
+ if status then
+ E.configSavedPositionTop, E.configSavedPositionLeft = E:Round(frame:GetTop(), 2), E:Round(frame:GetLeft(), 2)
+ E.global.general.AceGUI.width, E.global.general.AceGUI.height = E:Round(frame:GetWidth(), 2), E:Round(frame:GetHeight(), 2)
+ E:Config_UpdateLeftScroller(frame)
+ end
+end
+
+function E:Config_ButtonOnEnter()
+ local name = self.info and self.info.name
+ if type(name) == 'function' then name = name() end
+
+ if not self.desc and not name then return end
+
+ local current = self:GetText()
+ E.ConfigTooltip:SetOwner(self, 'ANCHOR_TOPRIGHT', 0, 2)
+
+ -- показать полное имя, если текст кнопки обрезан
+ if name and name ~= current then
+ E.ConfigTooltip:AddLine(E:StripString(name), 1, 1, 1, true)
+ end
+
+ if self.desc then
+ E.ConfigTooltip:AddLine(self.desc, 1, 1, 1, true)
+ end
+
+ E.ConfigTooltip:Show()
+end
+
+function E:Config_ButtonOnLeave()
+ E.ConfigTooltip:Hide()
+end
+
+function E:Config_RepositionOnEnter()
+ if self.highlight then
+ self.highlight:Show()
+ else
+ local r, g, b = unpack(E.media.rgbvaluecolor)
+ self.texture:SetVertexColor(r, g, b, 1)
+ end
+
+ E.Config_ButtonOnEnter(self)
+end
+
+function E:Config_RepositionOnLeave()
+ if self.highlight then
+ self.highlight:Hide()
+ else
+ self.texture:SetVertexColor(1, 1, 1, 0.8)
+ end
+
+ E.Config_ButtonOnLeave()
+end
+
+function E:Config_PreviousLocation(editbox)
+ local _, selected = E:Config_GetStatus(editbox.frame)
+ if selected ~= 'search' then
+ editbox.selected = selected or nil
+ end
+end
+
+function E:Config_SearchUpdate(userInput)
+ if not userInput then return end
+
+ local C = E.Config[1]
+ C:Search_ClearResults()
+
+ local text = self:GetText()
+ if strmatch(text, '%S+') then
+ C.SearchText = strtrim(strlower(text))
+
+ C:Search_Config()
+ C:Search_AddResults()
+
+ local ACD = E.Libs.AceConfigDialog
+ if ACD then
+ ACD:SelectGroup('ElvUI', 'search') -- чтобы обновить окно настроек
+ end
+ end
+end
+
+function E:Config_SearchClear()
+ if not self.ClearFocus then
+ self = self:GetParent()
+ end
+
+ local C = E.Config[1]
+ C:Search_ClearResults()
+
+ local _, selected = E:Config_GetStatus(self.frame)
+ if selected == 'search' then
+ local ACD = E.Libs.AceConfigDialog
+ if ACD then
+ ACD:SelectGroup('ElvUI', self.selected or 'general') -- остаемся в выбранном разделе или возвращаемся в "Общее"
+ end
+ end
+
+ self:SetText("")
+ EditBox_ClearFocus(self)
+end
+
+function E:Config_SearchFocusGained()
+ EditBox_HighlightText(self)
+ E:Config_PreviousLocation(self)
+end
+
+function E:Config_SearchFocusLost()
+ EditBox_ClearFocus(self)
+end
+
+function E:Config_SearchOnEvent()
+ local frame = self:HasFocus() and GetMouseFocus()
+ if frame and (frame ~= self and frame ~= self.clearButton) then
+ EditBox_ClearFocus(self)
+ end
+end
+
+function E:Config_SliderOnMouseWheel(offset)
+ local _, maxValue = self:GetMinMaxValues()
+ if maxValue == 0 then return end
+
+ local newValue = self:GetValue() - offset
+ if newValue < 0 then newValue = 0 end
+ if newValue > maxValue then return end
+
+ self:SetValue(newValue)
+ self.buttons:Point('TOPLEFT', 0, newValue * 30)
+end
+
+function E:Config_SliderOnValueChanged(value)
+ self:SetValue(value)
+ self.buttons:Point('TOPLEFT', 0, value * 30)
+end
+
+function E:Config_TruncateButtonText(btn)
+ local fs = btn:GetFontString()
+ if not fs then return end
+
+ -- в Sirus у шрифта может ещё не быть гарнитуры; ставим её до замера,
+ -- иначе SetText падает с "Font not set"
+ local _, fontHeight = fs:GetFont()
+ if not fontHeight and fs.FontTemplate then
+ fs:FontTemplate(nil, 12)
+ end
+
+ fs:SetWordWrap(false)
+ fs:SetJustifyH('CENTER')
+
+ local maxWidth = btn:GetWidth() and (btn:GetWidth() - 20) or 0
+ if maxWidth <= 0 then return end
+
+ local name = btn:GetText() or ''
+ if name:gsub('|c[fF][fF]%x%x%x%x%x%x',''):gsub('|r',''):gsub('%s','') == '' then return end
+
+ fs:SetText(name)
+
+ if fs:GetStringWidth() > maxWidth then
+ local cut = #name
+ local truncated = name
+ while cut > 0 and fs:GetStringWidth() > maxWidth do
+ cut = cut - 1
+ truncated = name:sub(1, cut)..'...'
+ fs:SetText(truncated)
+ end
+ btn:SetText(truncated)
+ end
+end
+
+function E:Config_SetButtonText(btn, noColor)
+ local name = btn.info.name
+ if type(name) == 'function' then name = name() end
+
+ if noColor then
+ name = name:gsub('|c[fF][fF]%x%x%x%x%x%x',''):gsub('|r','')
+ end
+
+ btn:SetText(name)
+end
+
+function E:Config_CreateSeparatorLine(frame, lastButton)
+ local line = frame.leftHolder.buttons:CreateTexture()
+ line:SetTexture(E.Media.Textures.White8x8)
+ line:SetVertexColor(1, .82, 0, .4)
+ line:Size(179, 2)
+ line:Point('TOP', lastButton, 'BOTTOM', 0, -6)
+ line.separator = true
+ return line
+end
+
+function E:Config_SetButtonColor(btn, disabled)
+ btn:SetEnabled(not disabled)
+
+ if not btn:GetFontString() then return end
+
+ if disabled then
+ btn:GetFontString():SetTextColor(1, 1, 1)
+ E:Config_SetButtonText(btn, true)
+
+ if btn.SetBackdropColor then
+ btn:SetBackdropColor(1, .82, 0, 0.4)
+ btn:SetBackdropBorderColor(1, .82, 0, 1)
+ end
+ else
+ btn:GetFontString():SetTextColor(1, .82, 0)
+ E:Config_SetButtonText(btn)
+
+ if btn.SetBackdropColor then
+ local r1, g1, b1 = unpack(E.media.backdropcolor)
+ btn:SetBackdropColor(r1, g1, b1, 1)
+
+ local r2, g2, b2 = unpack(E.media.bordercolor)
+ btn:SetBackdropBorderColor(r2, g2, b2, 1)
+ end
+ end
+end
+
+function E:Config_UpdateSliderPosition(btn)
+ local left = btn and btn.frame and btn.frame.leftHolder
+ if not (left and left.slider) then return end
+
+ -- скролл двигает кнопки вверх на 30px за шаг; считаем нужную позицию
+ -- по живой геометрии кнопки, а не по сохраненному sliderValue, и не
+ -- трогаем скролл, если выбранная запись уже полностью видна
+ local btns = left.buttons
+ local slider = left.slider
+ local value = slider:GetValue()
+ local step = 30
+
+ local viewBottom = btns:GetBottom()
+ local viewTop = btns:GetTop() - value * step
+ local btnBottom = btn:GetBottom()
+ local btnTop = btn:GetTop()
+ if not (viewBottom and viewTop and btnBottom and btnTop) then return end
+
+ if btnBottom >= viewBottom and btnTop <= viewTop then
+ -- кнопка уже полностью видна, скролл не трогаем
+ return
+ end
+
+ -- подводим нижний край выбранной кнопки к нижнему краю области просмотра
+ local needed = value + (viewBottom - btnBottom) / step
+ local _, maxValue = slider:GetMinMaxValues()
+ if needed < 0 then needed = 0 end
+ if needed > maxValue then needed = maxValue end
+ if needed ~= value then
+ E.Config_SliderOnValueChanged(slider, needed)
+ end
+end
+
+function E:Config_CreateFrame(info, frame, unskinned, frameType, ...)
+ local element = CreateFrame(frameType, ...)
+ element.frame = frame
+ element.desc = info.desc
+ element.info = info
+
+ if frameType == 'Button' then
+ if not unskinned then
+ S:HandleButton(element)
+ end
+
+ element:SetScript('OnClick', info.func)
+
+ if element then
+ -- кнопки только с иконкой (например "Reposition Window") не должны
+ -- показывать текст поверх текстуры: не задаем подпись и скрываем
+ -- любой fontstring, который может создать клиент (SetText('') его не чистит)
+ if info.texture then
+ local textureFontString = element:GetFontString()
+ if textureFontString then
+ textureFontString:SetText('')
+ textureFontString:Hide()
+ end
+ else
+ E:Config_SetButtonText(element)
+ end
+
+ E:Config_SetButtonColor(element, element.info.key == 'general')
+ element:HookScript('OnEnter', E.Config_ButtonOnEnter)
+ element:HookScript('OnLeave', E.Config_ButtonOnLeave)
+
+ -- в Sirus у шаблонных кнопок нет шрифта на сыром fontstring, пока его
+ -- не применит механика кнопок; задаем заранее, чтобы корректно замерить
+ -- ширину и обрезать текст без ошибки "Font not set" (только нижние кнопки)
+ if not info.key then
+ local btext = element:GetFontString()
+ if btext and btext.FontTemplate then
+ btext:FontTemplate()
+ end
+ end
+
+ -- ширина = текст + 40, высота 22. Нижние кнопки ограничиваем по ширине,
+ -- чтобы русские подписи не налезали на строку поиска
+ local width = element:GetTextWidth() + 40
+ if not info.key then
+ width = min(width, 160)
+ end
+ element:Size(width, 22)
+
+ if not info.key then
+ E:Config_TruncateButtonText(element)
+ end
+ end
+ elseif frameType == 'EditBox' then
+ element:FontTemplate()
+ element:SetAutoFocus(false)
+
+ S:HandleSearchBox(element, unskinned)
+
+ element:HookScript('OnTextChanged', info.update)
+ element:SetScript('OnEscapePressed', info.clear)
+ element:SetScript('OnEditFocusLost', info.focusLost)
+ element:SetScript('OnEditFocusGained', info.focusGained)
+ element.clearButton:HookScript('OnClick', info.clear)
+
+ element:Size(220, 22)
+ end
+
+ return element
+end
+
+function E:Config_DialogOpened(name)
+ if name ~= 'ElvUI' then return end
+
+ local frame = E:Config_GetWindow()
+ if frame and frame.leftHolder then
+ E:Config_WindowOpened(frame)
+ end
+end
+
+function E:Config_UpdateLeftButtons()
+ local frame = E:Config_GetWindow()
+ if not (frame and frame.leftHolder) then return end
+
+ local _, selected = E:Config_GetStatus(frame)
+ for _, btn in next, frame.leftHolder.buttons do
+ if type(btn) == 'table' and btn.IsObjectType and btn:IsObjectType('Button') then
+ local enabled = btn.info.key == selected
+ E:Config_SetButtonColor(btn, enabled)
+
+ if enabled then
+ E:Config_UpdateSliderPosition(btn)
+ end
+ end
+ end
+end
+
+function E:Config_UpdateLeftScroller(frame)
+ local left = frame and frame.leftHolder
+ if not left then return end
+
+ local btns = left.buttons
+ local bottom = btns:GetBottom()
+ if not bottom then return end
+ btns:Point('TOPLEFT', 0, 0)
+
+ local max = 0
+ for _, btn in next, btns do
+ local button = type(btn) == 'table' and btn.IsObjectType and btn:IsObjectType('Button')
+ if button then
+ btn.sliderValue = nil
+
+ local btm = btn:GetBottom()
+ if btm then
+ if bottom > btm then
+ max = max + 1
+ btn.sliderValue = max
+ end
+ end
+ end
+ end
+
+ local slider = left.slider
+ slider:SetMinMaxValues(0, max)
+ slider:SetValue(0)
+
+ if max == 0 then
+ slider.thumb.holder:Hide()
+ else
+ slider.thumb.holder:Show()
+ end
+end
+
+function E:Config_SaveOldFramelevel(frame)
+ if not frame.oldFramelevel then
+ frame.oldFramelevel = frame:GetFrameLevel()
+ end
+end
+
+function E:Config_RestoreOldFramelevel(frame)
+ if frame.oldFramelevel then
+ frame:SetFrameLevel(frame.oldFramelevel)
+
+ frame.oldFramelevel = nil
+ end
+end
+
+function E:Config_SaveOldPosition(frame)
+ if frame.GetNumPoints and not frame.oldPosition then
+ frame.oldPosition = {}
+
+ for i = 1, frame:GetNumPoints() do
+ tinsert(frame.oldPosition, { frame:GetPoint(i) })
+ end
+ end
+end
+
+function E:Config_RestoreOldPosition(frame)
+ local position = frame.oldPosition
+ if not position then return end
+
+ frame:ClearAllPoints()
+
+ for i = 1, #position do
+ frame:Point(unpack(position[i]))
+ end
+
+ frame.oldPosition = nil
+end
+
+function E:Config_HandleLeftButton(info, frame, unskinned, buttons, last, index)
+ local btn = E:Config_CreateFrame(info, frame, unskinned, 'Button', nil, buttons, 'UIPanelButtonTemplate')
+
+ -- группы плагинов (не из базовых настроек) визуально вложены
+ local submenu = (info.order or 0) >= 6 and not tContains(E.OriginalOptions, info.key)
+
+ btn:Width(submenu and 164 or 176)
+
+ if btn.GetFontString and btn:GetFontString() then
+ local fs = btn:GetFontString()
+ if fs.FontTemplate then
+ fs:FontTemplate(nil, 11)
+ end
+ end
+
+ E:Config_TruncateButtonText(btn)
+
+ if not last then
+ btn:Point('TOP', buttons, 'TOP', submenu and 11 or -1, 0)
+ elseif last.IsObjectType and last:IsObjectType('FontString') then
+ -- смещение по X нулевое: отступ для плагинов дает заголовок секции "Плагины"
+ btn:Point('TOP', last, 'BOTTOM', 0, -4)
+ else
+ btn:Point('TOP', last, 'BOTTOM', 0, (last.separator and -6) or -4)
+ end
+
+ buttons[index] = btn
+
+ return btn
+end
+
+function E:Config_StripNameColor(name)
+ if type(name) == 'function' then
+ name = name()
+ end
+
+ return E:StripString(name)
+end
+
+local function Config_SortButtons(a, b)
+ local A1, B1 = a[1], b[1]
+ if A1 and B1 then
+ if A1 == B1 then
+ local A3, B3 = a[3], b[3]
+ if A3 and B3 and (A3.name and B3.name) then
+ return E:Config_StripNameColor(A3.name) < E:Config_StripNameColor(B3.name)
+ end
+ end
+
+ return A1 < B1
+ end
+end
+
+function E:Config_CreateLeftButtons(frame, unskinned, options)
+ local opts = {}
+ -- группы плагинов идут в конец списка, визуально вложенные
+ local pluginHeaderShown = false
+ for key, info in pairs(options) do
+ if not tContains(E.OriginalOptions, key) then
+ info.order = 100 + (info.order or 0)
+ end
+ if key == 'profiles' then
+ info.desc = nil
+ end
+ tinsert(opts, {info.order, key, info})
+ end
+ sort(opts, Config_SortButtons)
+
+ local buttons, last, order = frame.leftHolder.buttons
+ for index, opt in ipairs(opts) do
+ local info = opt[3]
+ local key = opt[2]
+
+ if (order == 2 or order == 5 or order == 20) and order < opt[1] then
+ last = E:Config_CreateSeparatorLine(frame, last)
+ end
+
+ -- первой группе плагинов выводим заголовок "Плагины" (не кликабельный)
+ if (opt[1] or 0) >= 100 and not pluginHeaderShown then
+ pluginHeaderShown = true
+ last = E:Config_CreateSectionLabel(frame, last, L["Plugins"])
+ end
+
+ order = opt[1]
+
+ info.key = key
+ info.func = function()
+ local ACD = E.Libs.AceConfigDialog
+ if ACD then ACD:SelectGroup('ElvUI', key) end
+ end
+
+ if key ~= 'search' then
+ last = E:Config_HandleLeftButton(info, frame, unskinned, buttons, last, index)
+ end
+ end
+end
+
+function E:Config_CreateSectionLabel(frame, lastButton, text)
+ local label = frame.leftHolder.buttons:CreateFontString(nil, 'OVERLAY')
+ label:FontTemplate(nil, 11, 'OUTLINE')
+ label:SetTextColor(1, .82, 0)
+ label:SetText(text)
+ label:Point('TOPLEFT', lastButton, 'BOTTOMLEFT', 12, -10)
+ label:Width(164)
+ label:SetJustifyH('LEFT')
+ return label
+end
+
+function E:Config_CloseClicked()
+ if self.originalClose then
+ self.originalClose:Click()
+ end
+end
+
+function E:Config_CloseWindow()
+ local ACD = E.Libs.AceConfigDialog
+ if ACD then ACD:Close('ElvUI') end
+
+ E.ConfigTooltip:Hide()
+end
+
+function E:Config_OpenWindow()
+ local ACD = E.Libs.AceConfigDialog
+ if ACD then ACD:Open('ElvUI') end
+
+ E.ConfigTooltip:Hide()
+end
+
+function E:Config_GetWindow()
+ local ACD = E.Libs.AceConfigDialog
+ local ConfigOpen = ACD and ACD.OpenFrames and ACD.OpenFrames.ElvUI
+ return ConfigOpen and ConfigOpen.frame
+end
+
+local ConfigLogoWidth, ConfigLogoHeight = 128, 64
+
+local function ConfigLogoAnimating(logo)
+ if not (logo and logo.elastic) then return false end
+
+ for i = 1, 4 do
+ if logo.elastic[i] and logo.elastic[i]:IsPlaying() then
+ return true
+ end
+ end
+
+ return false
+end
+
+local function ConfigLogoSettle(logo)
+ if not logo then return end
+ if not ConfigLogoAnimating(logo) then return end
+
+ for i = 1, 4 do
+ if logo.elastic and logo.elastic[i] then
+ logo.elastic[i]:Stop()
+ end
+ end
+
+ logo:SetSize(ConfigLogoWidth, ConfigLogoHeight)
+end
+
+local function ConfigLogoElasticize(logo)
+ if not logo then return end
+
+ logo:SetSize(ConfigLogoWidth, ConfigLogoHeight)
+ logo:Show()
+ pcall(E.Elasticize, E, logo, ConfigLogoWidth, ConfigLogoHeight)
+
+ local token = GetTime()
+ logo.elasticToken = token
+
+ C_Timer:After(4.5, function()
+ if logo.elasticToken == token then
+ ConfigLogoSettle(logo)
+ end
+ end)
+end
+
+local ConfigLogoTop
+local function ConfigLogoUpdate(_, r, g, b)
+ if ConfigLogoTop then
+ ConfigLogoTop:SetVertexColor(r, g, b)
+ end
+
+ if ElvUIMoverNudgeWindow and ElvUIMoverNudgeWindow.shadow then
+ ElvUIMoverNudgeWindow.shadow:SetBackdropBorderColor(r, g, b, 0.9)
+ end
+end
+E.valueColorUpdateFuncs[ConfigLogoUpdate] = true
+
+function E:Config_WindowClosed()
+ if not self.bottomHolder then return end
+
+ local frame = E:Config_GetWindow()
+ if not frame or frame ~= self then
+ self.bottomHolder:Hide()
+ self.leftHolder:Hide()
+ self.topHolder:Hide()
+ self.leftHolder.slider:Hide()
+ self.closeButton:Hide()
+ self.originalClose:Show()
+
+ ConfigLogoTop = nil
+
+ ConfigLogoSettle(self.leftHolder.LogoTop)
+ ConfigLogoSettle(self.leftHolder.LogoBottom)
+
+ E:Config_RestoreOldPosition(self.topHolder.version)
+ E:Config_RestoreOldPosition(self.obj.content)
+ E:Config_RestoreOldPosition(self.obj.titlebg)
+
+ local unskinned = not E.private.skins.ace3.enable
+ local statusParent = self.statusText and self.statusText.parent
+ if statusParent then
+ statusParent:Show()
+
+ E:Config_RestoreOldPosition(statusParent)
+
+ if unskinned then
+ E:Config_RestoreOldFramelevel(statusParent)
+ end
+ end
+
+ if E.ShowPopup then
+ E:StaticPopup_Show('CONFIG_RL')
+ E.ShowPopup = nil
+ end
+ end
+end
+
+function E:Config_ContentPlacement(frame, content, unskinned, statusShown)
+ content:ClearAllPoints()
+ content:Point('TOPLEFT', frame, 'TOPLEFT', unskinned and 13 or 7, -(frame.bottomHolder:GetHeight() + (unskinned and 46 or 41)))
+ content:Point('BOTTOMRIGHT', frame, 'BOTTOMRIGHT', -(unskinned and 18 or 8), (statusShown and (unskinned and 32 or 25)) or (unskinned and 12) or 2)
+end
+
+function E:Config_SetStatusText(text)
+ if not ConfigLogoTop or not self.parent then return end
+
+ local shown = text and text ~= ''
+ self.parent:SetShown(shown)
+
+ E:Config_ContentPlacement(self.frame, self.content, not E.private.skins.ace3.enable, shown)
+end
+
+-- На всякий случай прячем дерево AceConfigDialog для корневой группы ElvUI,
+-- даже если хук скина Ace3 не сработал (своё левое меню рисует хром окна).
+function E:Config_HideAceTree(frame)
+ if not (frame and frame.obj) then return end
+
+ local function walk(widget)
+ if not widget then return end
+ if widget.treeframe and widget.userdata and widget.userdata.option
+ and widget.userdata.option.childGroups == 'ElvUI_HiddenTree'
+ and widget.treeframe:IsShown() then
+ widget.treeframe:Hide()
+ end
+ if widget.children then
+ for _, child in ipairs(widget.children) do
+ walk(child)
+ end
+ end
+ end
+
+ walk(frame.obj)
+end
+
+function E:Config_WindowOpened(frame)
+ E:Config_HideAceTree(frame)
+
+ if frame and frame.bottomHolder and not ConfigLogoTop then
+ frame.bottomHolder:Show()
+ frame.leftHolder:Show()
+ frame.topHolder:Show()
+ frame.leftHolder.slider:Show()
+ frame.closeButton:Show()
+ frame.originalClose:Hide()
+
+ local logoColor = E.media.rgbvaluecolor or {1, .82, 0}
+
+ -- декоративный отскок логотипа, как в оригинальном ElvUI; анимации могут
+ -- застрять на крошечном размере, поэтому перед показом размер сбрасывается
+ -- и возвращается после окончания отскока
+ for _, logo in next, { frame.leftHolder.LogoTop, frame.leftHolder.LogoBottom } do
+ if logo then
+ logo:SetVertexColor(unpack(logoColor))
+ logo:SetDesaturated(false)
+ ConfigLogoElasticize(logo)
+ end
+ end
+ ConfigLogoTop = frame.leftHolder.LogoTop
+
+ local unskinned = not E.private.skins.ace3.enable
+ local version = frame.topHolder.version
+ E:Config_SaveOldPosition(version)
+ version:ClearAllPoints()
+ version:Point('LEFT', frame.topHolder, 'LEFT', unskinned and 8 or 6, unskinned and -4 or 0)
+
+ local content = frame.obj.content
+ E:Config_SaveOldPosition(content)
+ E:Config_ContentPlacement(frame, content, unskinned)
+
+ local titlebg = frame.obj.titlebg
+ E:Config_SaveOldPosition(titlebg)
+ titlebg:ClearAllPoints()
+ titlebg:SetPoint('TOPLEFT', frame)
+ titlebg:SetPoint('TOPRIGHT', frame)
+ titlebg:SetTexture(nil) -- убрать стандартную шапку диалога, свою рисует хром
+
+ local statusParent = frame.statusText and frame.statusText.parent
+ if statusParent then
+ if unskinned then -- чтобы стрелка ресайза работала корректно
+ E:Config_SaveOldFramelevel(statusParent)
+
+ statusParent:OffsetFrameLevel(-1)
+ end
+
+ E:Config_SaveOldPosition(statusParent)
+
+ statusParent:ClearAllPoints()
+ statusParent:Point('TOPLEFT', frame.leftHolder, 'BOTTOMRIGHT', unskinned and 11 or 1, unskinned and 38 or 22)
+ statusParent:Point('BOTTOMRIGHT', frame, -2, 2)
+ end
+ end
+end
+
+function E:Config_CreateBottomButtons(frame, unskinned)
+ local C = E.Config[1]
+
+ local last, search
+ for index, info in ipairs({
+ {
+ var = 'Install',
+ name = L["Install"],
+ desc = L["Run the installation process."],
+ func = function()
+ E:Install()
+ E:ToggleOptions()
+ end
+ },
+ {
+ var = 'ShowStatusReport',
+ name = L["Status"],
+ desc = L["Shows a frame with needed info for support."],
+ func = function()
+ E:ShowStatusReport()
+ E:ToggleOptions()
+ E.StatusReportToggled = true
+ end
+ },
+ {
+ var = 'ToggleAnchors',
+ name = L["Movers"],
+ desc = L["Unlock various elements of the UI to be repositioned."],
+ func = function()
+ E:ToggleMoveMode()
+ E.ConfigurationToggled = true
+ end
+ },
+ {
+ editBox = 'InputBoxTemplate',
+ clear = E.Config_SearchClear,
+ update = E.Config_SearchUpdate,
+ focusLost = E.Config_SearchFocusLost,
+ focusGained = E.Config_SearchFocusGained,
+ event = E.Config_SearchOnEvent,
+ var = 'Search',
+ name = L["Search"]
+ },
+ {
+ var = 'WhatsNew',
+ name = L["Whats New"],
+ hidden = function()
+ return C.SearchText ~= "" or next(C.SearchCache)
+ end,
+ func = function()
+ if search then
+ E:Config_PreviousLocation(search)
+ end
+
+ C:Search_ClearResults()
+ C:Search_Config(nil, nil, nil, true)
+ C:Search_AddResults()
+
+ local ACD = E.Libs.AceConfigDialog
+ if ACD then
+ ACD:SelectGroup('ElvUI', 'search') -- чтобы обновить окно настроек
+ end
+ end
+ },
+ {
+ texture = true,
+ var = 'RepositionWindow',
+ name = L["Reposition Window"],
+ desc = L["Reset the size and position of this frame."],
+ func = function() E:Config_UpdateSize(true) end
+ }
+ }) do
+ local element
+ if info.var == 'RepositionWindow' then
+ element = E:Config_CreateFrame(info, frame, true, 'Button', nil, frame.bottomHolder)
+ element:Size(unskinned and 34 or 18)
+
+ local texture = element:CreateTexture()
+ texture:SetTexture(unskinned and [[Interface\ChatFrame\UI-ChatIcon-Maximize-Up]] or E.Media.Textures.Resize2)
+ texture:SetAllPoints()
+ element.texture = texture
+
+ if unskinned then
+ local highlight = element:CreateTexture()
+ highlight:SetTexture([[Interface\Buttons\UI-Common-MouseHilight]])
+ highlight:SetBlendMode('ADD')
+ highlight:SetAllPoints()
+ highlight:Hide()
+ element.highlight = highlight
+ else
+ texture:SetVertexColor(1, 1, 1, 0.8)
+ end
+
+ element:HookScript('OnEnter', E.Config_RepositionOnEnter)
+ element:HookScript('OnLeave', E.Config_RepositionOnLeave)
+ elseif info.editBox then
+ element = E:Config_CreateFrame(info, frame, unskinned, 'EditBox', nil, frame.bottomHolder, info.editbox)
+ else
+ element = E:Config_CreateFrame(info, frame, unskinned, 'Button', nil, frame.bottomHolder, 'UIPanelButtonTemplate')
+ end
+
+ if not search and (info.var == 'Search') then
+ search = element
+
+ search:RegisterEvent('CURSOR_UPDATE')
+ search:SetScript('OnEvent', info.event)
+ search:SetScript('OnMouseDown', info.event)
+ end
+
+ local offset = unskinned and 14 or 10
+
+ if not last then
+ element:Point('BOTTOMLEFT', frame.bottomHolder, 'BOTTOMLEFT', unskinned and 24 or offset, offset)
+ elseif info.var == 'RepositionWindow' then
+ element:Point('TOPRIGHT', frame.topHolder, 'TOPRIGHT', -(unskinned and 46 or 32), -(unskinned and 4 or 2))
+ elseif index == 4 then -- кнопка поиска
+ element:Point('BOTTOMRIGHT', frame.bottomHolder, 'BOTTOMRIGHT', -(unskinned and 24 or offset), offset)
+ elseif index > 4 then
+ element:Point('RIGHT', last, 'LEFT', -(index == 5 and (unskinned and 16 or 20) or (unskinned and 6 or 12)), 0)
+ else
+ element:Point('LEFT', last, 'RIGHT', unskinned and 6 or 12, 0)
+ end
+
+ last = element
+
+ frame.bottomHolder[info.var] = element
+ end
+end
+
+local pageNodes = {}
+function E:Config_GetToggleMode(frame, msg)
+ local pages, msgStr
+ if msg and msg ~= "" then
+ pages = {strsplit(',', msg)}
+ msgStr = gsub(msg, ',', '\001')
+ end
+
+ local empty = pages ~= nil
+ if not frame or empty then
+ if empty then
+ local ACD = E.Libs.AceConfigDialog
+ local pageCount, index, mainSel = #pages
+ if pageCount > 1 then
+ wipe(pageNodes)
+ index = 0
+
+ local main, mainNode, mainSelStr, sub, subNode, subSel
+ for i = 1, pageCount do
+ if i == 1 then
+ main = pages[i] and ACD and ACD.Status and ACD.Status.ElvUI
+ mainSel = main and main.status and main.status.groups and main.status.groups.selected
+ mainSelStr = mainSel and ('^'..E:EscapeString(mainSel)..'\001')
+ mainNode = main and main.children and main.children[pages[i]]
+ pageNodes[index+1], pageNodes[index+2] = main, mainNode
+ else
+ sub = pages[i] and pageNodes[i] and ((i == pageCount and pageNodes[i]) or pageNodes[i].children[pages[i]])
+ subSel = sub and sub.status and sub.status.groups and sub.status.groups.selected
+ subNode = (mainSelStr and msgStr:match(mainSelStr..E:EscapeString(pages[i])..'$') and (subSel and subSel == pages[i])) or ((i == pageCount and not subSel) and mainSel and mainSel == msgStr)
+ pageNodes[index+1], pageNodes[index+2] = sub, subNode
+ end
+ index = index + 2
+ end
+ else
+ local main = pages[1] and ACD and ACD.Status and ACD.Status.ElvUI
+ mainSel = main and main.status and main.status.groups and main.status.groups.selected
+ end
+
+ if frame and ((not index and mainSel and mainSel == msg) or (index and pageNodes and pageNodes[index])) then
+ return 'Close'
+ else
+ return 'Open', pages
+ end
+ else
+ return 'Open'
+ end
+ else
+ return 'Close'
+ end
+end
+
+function E:ToggleOptions(msg)
+ if InCombatLockdown() and E.db.general.showWhenInCombat == false then
+ E:Print(ERR_NOT_IN_COMBAT)
+ E:RegisterEvent("PLAYER_REGEN_ENABLED")
+ return
+ end
+
+ if not IsAddOnLoaded('ElvUI_OptionsUI') then
+ local noConfig
+ local _, _, _, _, reason = GetAddOnInfo("ElvUI_OptionsUI")
+ if reason ~= "MISSING" and reason ~= "DISABLED" then
+ E.GUIFrame = false
+ LoadAddOn("ElvUI_OptionsUI")
+
+ -- по какой-то причине GetAddOnInfo возвращает "DEMAND_LOADED", даже если
+ -- аддон выключен; пробуем загрузить и проверяем результат сразу после
+ if not IsAddOnLoaded("ElvUI_OptionsUI") then noConfig = true end
+
+ -- проверяем версию ElvUI_OptionsUI, если он реально включен
+ if (not noConfig) and GetAddOnMetadata("ElvUI_OptionsUI", "Version") ~= "1.35" then
+ E:StaticPopup_Show("CLIENT_UPDATE_REQUEST")
+ end
+ else
+ noConfig = true
+ end
+
+ if noConfig then
+ E:Print("|cffff0000Error -- Addon 'ElvUI_OptionsUI' не найден или выключен.|r")
+ return
+ end
+ end
+
+ local frame = E:Config_GetWindow()
+ local mode, pages = E:Config_GetToggleMode(frame, msg)
+
+ local ACD = E.Libs.AceConfigDialog
+ if ACD then
+ if not ACD.OpenHookedElvUI then
+ hooksecurefunc(ACD, 'Open', E.Config_DialogOpened)
+ ACD.OpenHookedElvUI = true
+ end
+
+ ACD[mode](ACD, 'ElvUI')
+ end
+
+ if not frame then
+ frame = E:Config_GetWindow()
+ end
+
+ if mode == 'Open' and frame then
+ local ACR = E.Libs.AceConfigRegistry
+ if ACR and not ACR.NotifyHookedElvUI then
+ hooksecurefunc(ACR, 'NotifyChange', E.Config_UpdateLeftButtons)
+ ACR.NotifyHookedElvUI = true
+ E:Config_UpdateSize()
+ end
+
+ local unskinned = not E.private.skins.ace3.enable
+ if not frame.bottomHolder then -- окно было закрыто или ещё не открывалось
+ frame:HookScript('OnHide', E.Config_WindowClosed)
+
+ for _, child in next, { frame:GetChildren() } do
+ local button = child:IsObjectType('Button')
+ if button and child:GetText() == _G.CLOSE then
+ frame.originalClose = child
+ child:Hide()
+ elseif button or child:IsObjectType('Frame') then
+ if unskinned and child.GetBackdrop then
+ local info = child:GetBackdrop()
+ if info and info.edgeFile == [[Interface\Tooltips\UI-Tooltip-Border]] then
+ child:SetBackdrop(nil)
+ end
+ end
+
+ local point = not unskinned and not frame.resizeArrow and child:GetPoint()
+ if point == 'BOTTOMRIGHT' then
+ for _, region in next, { child:GetRegions() } do
+ local texture = region:IsObjectType('Texture') and region:GetTexture()
+ if texture == [[Interface\Tooltips\UI-Tooltip-Border]] then
+ if not child.resizeTexture then
+ region:SetTexture(E.Media.Textures.ArrowUp)
+ region:SetTexCoord(0, 1, 0, 1)
+ region:SetRotation(-2.35)
+ region:SetAllPoints()
+
+ child.resizeTexture = region
+ elseif texture then -- это меньшая текстура, она не нужна
+ region:SetAlpha(0)
+ end
+ end
+ end
+
+ child:Size(24)
+ child:Point('BOTTOMRIGHT', 1, -1)
+ child:SetFrameLevel(200)
+
+ frame.resizeArrow = child
+ end
+
+ if child:HasScript('OnMouseUp') then
+ child:HookScript('OnMouseUp', E.Config_StopMoving)
+ end
+ end
+ end
+
+ local close = CreateFrame('Button', nil, frame, 'UIPanelCloseButton')
+ close:SetScript('OnClick', E.Config_CloseClicked)
+ close:SetFrameLevel(1000)
+ close:Point('TOPRIGHT', unskinned and -12 or 1, unskinned and -12 or 2)
+ close:Size(unskinned and 30 or 32)
+ close.originalClose = frame.originalClose
+ frame.closeButton = close
+
+ local statusText = frame.obj.statustext
+ if statusText then
+ frame.statusText = statusText
+
+ statusText.parent = statusText:GetParent()
+ statusText.content = frame.obj.content
+ statusText.frame = frame
+
+ if not statusTextHooked[statusText] then
+ statusTextHooked[statusText] = true
+
+ hooksecurefunc(statusText, 'SetText', E.Config_SetStatusText)
+ end
+ end
+
+ local left = CreateFrame('Frame', nil, frame)
+ left:Point('TOPLEFT', unskinned and 10 or 2, unskinned and -6 or -2)
+ left:Point('BOTTOMRIGHT', frame, 'BOTTOMLEFT', 182, 2)
+ frame.leftHolder = left
+
+ local top = CreateFrame('Frame', nil, frame)
+ top.version = frame.obj.titletext
+ top:Point('TOPRIGHT', frame, -2, 0)
+ top:Point('TOPLEFT', left, 'TOPRIGHT', 1, 0)
+ top:Height(24)
+ frame.topHolder = top
+
+ local bottom = CreateFrame('Frame', nil, frame)
+ bottom:Point('TOPLEFT', top, 'BOTTOMLEFT', unskinned and -15 or 0, -(unskinned and 15 or 1))
+ bottom:Point('TOPRIGHT', top, 'BOTTOMRIGHT', unskinned and 10 or 0, -(unskinned and 15 or 1))
+ bottom:Height(37)
+ frame.bottomHolder = bottom
+
+ local LogoBottom = left:CreateTexture()
+ LogoBottom:SetTexture(E.Media.Textures.LogoBottomSmall or E.Media.Textures.Logo)
+ LogoBottom:Point('CENTER', left, 'TOP', unskinned and 10 or 0, unskinned and -40 or -36)
+ LogoBottom:Size(128, 64)
+ left.LogoBottom = LogoBottom
+
+ local LogoTop = left:CreateTexture()
+ LogoTop:SetTexture(E.Media.Textures.LogoTopSmall or E.Media.Textures.Logo)
+ LogoTop:Point('CENTER', left, 'TOP', unskinned and 10 or 0, unskinned and -40 or -36)
+ LogoTop:Size(128, 64)
+ left.LogoTop = LogoTop
+
+ local buttonsHolder = CreateFrame('Frame', nil, left)
+ buttonsHolder:Point('TOPLEFT', unskinned and 4 or 1, -70)
+ buttonsHolder:Point('BOTTOMRIGHT', unskinned and 6 or 1, unskinned and 10 or 0)
+ left.buttonsHolder = buttonsHolder
+
+ local buttonsScrollFrame = CreateFrame('ScrollFrame', nil, buttonsHolder) -- в 3.3.5 нет SetClipsChildren, поэтому используем ScrollFrame
+ buttonsScrollFrame:SetAllPoints(buttonsHolder)
+ left.buttonsScrollFrame = buttonsScrollFrame
+
+ local buttonsScrollFrameChild = CreateFrame('Frame', nil, buttonsScrollFrame)
+ buttonsScrollFrame:SetScrollChild(buttonsScrollFrameChild)
+ buttonsScrollFrameChild:SetAllPoints(buttonsScrollFrame)
+ left.buttonsScrollFrameChild = buttonsScrollFrameChild
+
+ local buttons = CreateFrame('Frame', nil, buttonsScrollFrameChild)
+ buttons:Point('BOTTOMRIGHT')
+ buttons:Point('TOPLEFT', 0, 0)
+ left.buttons = buttons
+
+ local slider = CreateFrame('Slider', nil, frame)
+ slider:SetThumbTexture(E.Media.Textures.White8x8)
+ slider:EnableMouseWheel(true)
+ slider:SetScript('OnMouseWheel', E.Config_SliderOnMouseWheel)
+ slider:SetScript('OnValueChanged', E.Config_SliderOnValueChanged)
+ slider:SetOrientation('VERTICAL')
+ slider:SetValueStep(1)
+ slider:SetValue(0)
+ slider:Width(192)
+ slider:Point('TOPLEFT', buttons, 'TOPLEFT', 0, 0)
+ slider:Point('BOTTOMRIGHT', left, 'BOTTOMRIGHT', unskinned and 3 or 0, unskinned and 10 or 1)
+ slider.buttons = buttons
+ left.slider = slider
+
+ local thumb = slider:GetThumbTexture()
+ thumb:Point('LEFT', left, 'RIGHT', unskinned and 6 or 2, 0)
+ thumb:Size(8, 12)
+ thumb:SetAlpha(0) -- скрываем, он под нескинованными кнопками
+ left.slider.thumb = thumb
+
+ local thumbHolder = CreateFrame('Frame', nil, left)
+ thumbHolder:SetFrameLevel(200)
+ thumbHolder:SetAllPoints(thumb)
+ thumb.holder = thumbHolder
+
+ local thumbTexture = thumbHolder:CreateTexture()
+ thumbTexture:SetTexture(E.media.blankTex)
+ thumbTexture:SetDrawLayer('OVERLAY')
+ thumbTexture:SetVertexColor(1, 1, 1, 0.5)
+ thumbTexture:SetAllPoints()
+ thumbHolder.texture = thumbTexture
+
+ if not unskinned then
+ local statusParent = statusText and statusText.parent
+ if statusParent then
+ statusParent:Hide()
+ statusParent:SetTemplate('Transparent')
+ end
+
+ bottom:SetTemplate('Transparent')
+ left:SetTemplate('Transparent')
+ top:SetTemplate('Transparent')
+
+ S:HandleCloseButton(close)
+ else
+ for _, region in next, { frame:GetRegions() } do
+ local texture = region:IsObjectType('Texture') and region:GetTexture()
+ if texture == [[Interface\DialogFrame\UI-DialogBox-Header]] then
+ region:SetAlpha(0)
+ end
+ end
+ end
+
+ E:Config_CreateLeftButtons(frame, unskinned, E.Options.args)
+ E:Config_CreateBottomButtons(frame, unskinned)
+ E:Config_UpdateLeftScroller(frame)
+ E:Config_WindowOpened(frame)
+ end
+
+ if ACD and pages then
+ ACD:SelectGroup('ElvUI', unpack(pages))
+ end
+
+ if not E.GUIFrame then
+ E.GUIFrame = frame
+ ElvUIGUIFrame = E.GUIFrame
+ hooksecurefunc(frame, 'StopMovingOrSizing', E.Config_StopMoving)
+ end
+ end
+
+ GameTooltip:Hide()
+end
+
+-- алиасы, чтобы старые вызовы Sirus (PixelPerfect, OptionsUI Core, команды) продолжали работать
+E.ToggleOptionsUI = E.ToggleOptions
+E.UpdateConfigSize = E.Config_UpdateSize
+E.GetConfigDefaultSize = E.Config_GetDefaultSize
+E.GetConfigSize = E.Config_GetSize
+E.GetConfigPosition = E.Config_GetPosition
+E.ResetConfigSettings = E.Config_ResetSettings
+E.ConfigStopMovingOrSizing = E.Config_StopMoving
diff --git a/Core/Toolkit.lua b/Core/Toolkit.lua
index 1b08a15a0..c40199c8e 100644
--- a/Core/Toolkit.lua
+++ b/Core/Toolkit.lua
@@ -344,6 +344,13 @@ local function GetNamedChild(frame, childName, index)
return _G[name..childName..(index or "")]
end
+local function OffsetFrameLevel(frame, offset, secondary)
+ if not secondary then secondary = frame end
+
+ local level = secondary:GetFrameLevel()
+ frame:SetFrameLevel(level + (offset or 0))
+end
+
local function addapi(object)
local mt = getmetatable(object).__index
if not object.Size then mt.Size = Size end
@@ -361,6 +368,7 @@ local function addapi(object)
if not object.StyleButton then mt.StyleButton = StyleButton end
if not object.CreateCloseButton then mt.CreateCloseButton = CreateCloseButton end
if not object.GetNamedChild then mt.GetNamedChild = GetNamedChild end
+ if not object.OffsetFrameLevel then mt.OffsetFrameLevel = OffsetFrameLevel end
end
local handled = {["Frame"] = true}
diff --git a/Init.lua b/Init.lua
index 4777bbccf..5bae4c21d 100644
--- a/Init.lua
+++ b/Init.lua
@@ -6,18 +6,14 @@ To load the AddOn engine add this to the top of your file:
]]
--
--Lua functions
-local _G, min, pairs, strsplit, unpack, wipe, type, tcopy = _G, min, pairs, strsplit, unpack, wipe, type, table.copy
+local _G, pairs, type, tcopy = _G, pairs, type, table.copy
--WoW API / Variables
-local hooksecurefunc = hooksecurefunc
local CreateFrame = CreateFrame
-local GetAddOnInfo = GetAddOnInfo
local GetAddOnMetadata = GetAddOnMetadata
local GetTime = GetTime
local HideUIPanel = HideUIPanel
-local InCombatLockdown = InCombatLockdown
local IsAddOnLoaded = IsAddOnLoaded
-local LoadAddOn = LoadAddOn
local ReloadUI = ReloadUI
local ERR_NOT_IN_COMBAT = ERR_NOT_IN_COMBAT
@@ -33,7 +29,7 @@ local AddOnName, Engine = ...
local AddOn = AceAddon:NewAddon(AddOnName, "AceConsole-3.0", "AceEvent-3.0", "AceTimer-3.0", "AceHook-3.0")
AddOn.callbacks = AddOn.callbacks or CallbackHandler:New(AddOn)
AddOn.DF = {profile = {}, global = {}}; AddOn.privateVars = {profile = {}} -- Defaults
-AddOn.Options = {type = "group", name = AddOnName, args = {}}
+AddOn.Options = {type = "group", name = AddOnName, args = {}, childGroups = 'ElvUI_HiddenTree'}
Engine[1] = AddOn
Engine[2] = {}
@@ -290,167 +286,3 @@ end
function AddOn:OnProfileReset()
AddOn:StaticPopup_Show("RESET_PROFILE_PROMPT")
end
-
-function AddOn:ResetConfigSettings()
- AddOn.configSavedPositionTop, AddOn.configSavedPositionLeft = nil, nil
- AddOn.global.general.AceGUI = AddOn:CopyTable({}, AddOn.DF.global.general.AceGUI)
-end
-
-function AddOn:GetConfigPosition()
- return AddOn.configSavedPositionTop, AddOn.configSavedPositionLeft
-end
-
-function AddOn:GetConfigSize()
- return AddOn.global.general.AceGUI.width, AddOn.global.general.AceGUI.height
-end
-
-function AddOn:UpdateConfigSize(reset)
- local frame = self.GUIFrame
- if not frame then return end
-
- local maxWidth, maxHeight = self.UIParent:GetSize()
- frame:SetMinResize(600, 500)
- frame:SetMaxResize(maxWidth-50, maxHeight-50)
-
- self.Libs.AceConfigDialog:SetDefaultSize(AddOnName, self:GetConfigDefaultSize())
-
- local status = frame.obj and frame.obj.status
- if status then
- if reset then
- self:ResetConfigSettings()
-
- status.top, status.left = self:GetConfigPosition()
- status.width, status.height = self:GetConfigDefaultSize()
-
- frame.obj:ApplyStatus()
- else
- local top, left = self:GetConfigPosition()
- if top and left then
- status.top, status.left = top, left
-
- frame.obj:ApplyStatus()
- end
- end
- end
-end
-
-function AddOn:GetConfigDefaultSize()
- local width, height = AddOn:GetConfigSize()
- local maxWidth, maxHeight = AddOn.UIParent:GetSize()
- width, height = min(maxWidth - 50, width), min(maxHeight - 50, height)
- return width, height
-end
-
-function AddOn:ConfigStopMovingOrSizing()
- if self.obj and self.obj.status then
- AddOn.configSavedPositionTop, AddOn.configSavedPositionLeft = AddOn:Round(self:GetTop(), 2), AddOn:Round(self:GetLeft(), 2)
- AddOn.global.general.AceGUI.width, AddOn.global.general.AceGUI.height = AddOn:Round(self:GetWidth(), 2), AddOn:Round(self:GetHeight(), 2)
- end
-end
-
-local pageNodes = {}
--- local E, L, V, P, G = unpack(select(2, ...)); --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
-function AddOn:ToggleOptionsUI(msg)
- if InCombatLockdown() and AddOn.db.general.showWhenInCombat == false then
- self:Print(ERR_NOT_IN_COMBAT)
- self:RegisterEvent("PLAYER_REGEN_ENABLED")
- return
- end
-
- if not IsAddOnLoaded("ElvUI_OptionsUI") then
- local noConfig
- local _, _, _, _, reason = GetAddOnInfo("ElvUI_OptionsUI")
- if reason ~= "MISSING" and reason ~= "DISABLED" then
- self.GUIFrame = false
- LoadAddOn("ElvUI_OptionsUI")
-
- --For some reason, GetAddOnInfo reason is "DEMAND_LOADED" even if the addon is disabled.
- --Workaround: Try to load addon and check if it is loaded right after.
- if not IsAddOnLoaded("ElvUI_OptionsUI") then noConfig = true end
-
- -- version check elvui options if it's actually enabled
- if (not noConfig) and GetAddOnMetadata("ElvUI_OptionsUI", "Version") ~= "1.35" then
- self:StaticPopup_Show("CLIENT_UPDATE_REQUEST")
- end
- else
- noConfig = true
- end
-
- if noConfig then
- self:Print("|cffff0000Error -- Addon 'ElvUI_OptionsUI' не найден или выключен.|r")
- return
- end
- end
-
- local ACD = self.Libs.AceConfigDialog
- local ConfigOpen = ACD and ACD.OpenFrames and ACD.OpenFrames[AddOnName]
-
- local pages, msgStr
- if msg and msg ~= "" then
- pages = {strsplit(",", msg)}
- msgStr = gsub(msg, ",","\001")
- end
-
- local mode = "Close"
- if not ConfigOpen or (pages ~= nil) then
- if pages ~= nil then
- local pageCount, index, mainSel = #pages
- if pageCount > 1 then
- wipe(pageNodes)
- index = 0
-
- local main, mainNode, mainSelStr, sub, subNode, subSel
- for i = 1, pageCount do
- if i == 1 then
- main = pages[i] and ACD and ACD.Status and ACD.Status.ElvUI
- mainSel = main and main.status and main.status.groups and main.status.groups.selected
- mainSelStr = mainSel and ("^"..self:EscapeString(mainSel).."\001")
- mainNode = main and main.children and main.children[pages[i]]
- pageNodes[index + 1], pageNodes[index + 2] = main, mainNode
- else
- sub = pages[i] and pageNodes[i] and ((i == pageCount and pageNodes[i]) or pageNodes[i].children[pages[i]])
- subSel = sub and sub.status and sub.status.groups and sub.status.groups.selected
- subNode = (mainSelStr and msgStr:match(mainSelStr..self:EscapeString(pages[i]).."$") and (subSel and subSel == pages[i])) or ((i == pageCount and not subSel) and mainSel and mainSel == msgStr)
- pageNodes[index + 1], pageNodes[index + 2] = sub, subNode
- end
- index = index + 2
- end
- else
- local main = pages[1] and ACD and ACD.Status and ACD.Status.ElvUI
- mainSel = main and main.status and main.status.groups and main.status.groups.selected
- end
-
- if ConfigOpen and ((not index and mainSel and mainSel == msg) or (index and pageNodes and pageNodes[index])) then
- mode = "Close"
- else
- mode = "Open"
- end
- else
- mode = "Open"
- end
- end
-
- if ACD then
- ACD[mode](ACD, AddOnName)
- end
-
- if mode == "Open" then
- ConfigOpen = ACD and ACD.OpenFrames and ACD.OpenFrames[AddOnName]
- if ConfigOpen then
- local frame = ConfigOpen.frame
- if frame and not self.GUIFrame then
- self.GUIFrame = frame
- ElvUIGUIFrame = self.GUIFrame
-
- self:UpdateConfigSize()
- hooksecurefunc(frame, "StopMovingOrSizing", AddOn.ConfigStopMovingOrSizing)
- end
- end
-
- if ACD and pages then
- ACD:SelectGroup(AddOnName, unpack(pages))
- end
- end
-
- GameTooltip:Hide() --Just in case you're mouseovered something and it closes.
-end
diff --git a/Locales/enUS.lua b/Locales/enUS.lua
index 2454bf38c..933f633c7 100644
--- a/Locales/enUS.lua
+++ b/Locales/enUS.lua
@@ -13,6 +13,9 @@ L["A raid marker feature is available by pressing Escape -> Keybinds scroll to t
L["A setting you have changed will change an option for this character only. This setting that you have changed will be uneffected by changing user profiles. Changing this setting requires that you reload your User Interface."] = true
L["ABOVE_THREAT_FORMAT"] = "%s: %.0f%% [%.0f%% above |cff%02x%02x%02x%s|r]"
L["AFK"] = true
+L["Current Target:"] = true
+L["Threat Bar"] = true
+L["Tank Colors"] = true
L["Accepting this will reset the UnitFrame settings for %s. Are you sure?"] = true
L["Accepting this will reset your Filter Priority lists for all auras on NamePlates. Are you sure?"] = true
L["Accepting this will reset your Filter Priority lists for all auras on UnitFrames. Are you sure?"] = true
diff --git a/Locales/ruRU.lua b/Locales/ruRU.lua
index 0b024adbd..19570808c 100644
--- a/Locales/ruRU.lua
+++ b/Locales/ruRU.lua
@@ -327,6 +327,40 @@ L["silverabbrev"] = "|cffc7c7cfс|r"
L["whispers"] = "шепчет"
L["yells"] = "кричит"
L["|cFFE30000Lua error recieved. You can view the error message when you exit combat."] = "|cFFE30000Обнаружена ошибка lua. Вы получите отчет о ней после завершения боя."
+-- Перенесено из ElvUI-development
+L["Ammo/Shard Counter"] = "Счетчик патронов/осколков"
+L["Completed XP:"] = "Завершено опыта:"
+L["Color the border of items by quality."] = "Окрашивать рамку предметов по качеству."
+L["Current Difficulty"] = "Текущая сложность"
+L["Display Item Level"] = "Показывать уровень предмета"
+L["Displays item level on equippable items."] = "Показывает уровень предмета на надеваемых предметах."
+L["Energy Regen"] = "Восст. энергии"
+L["Equipment Sets"] = "Наборы экипировки"
+L["Guild Bank"] = "Банк гильдии"
+L["Heal Power"] = "Сила исцеления"
+L["In Combat"] = "В бою"
+L["Item Count"] = "Количество предметов"
+L["Item Level Font"] = "Шрифт уровня предмета"
+L["Item Level Position"] = "Позиция уровня предмета"
+L["Item Level Threshold"] = "Порог уровня предмета"
+L["Item Quality"] = "Качество предмета"
+L["Max Level"] = "Макс. уровень"
+L["Mov. Speed"] = "Скор. движения"
+L["New Mail"] = "Новая почта"
+L["No Mail"] = "Нет почты"
+L["No Set Equipped"] = "Набор не надет"
+L["Out of Combat"] = "Вне боя"
+L["Position"] = "Позиция"
+L["Primary Stat"] = "Основная характеристика"
+L["Quest Log"] = "Список заданий"
+L["Spell Haste"] = "Скорость заклинаний"
+L["Spell Hit"] = "Меткость заклинаний"
+L["The minimum item level required for it to be shown."] = "Минимальный уровень предмета для отображения."
+L["Total Gold:"] = "Всего золота:"
+L["Total XP:"] = "Всего опыта:"
+L["X-Offset"] = "Смещение X"
+L["Y-Offset"] = "Смещение Y"
+L["iLvL"] = "Ур. предм."
----------------------------------
L["RED_ENABLE"] = "|cFFff3333Включить|r"
@@ -469,6 +503,9 @@ L["Realm"] = "Область"
L["Status"] = "Статус"
L["Target"] = "Цель"
L["Threat"] = "Угроза"
+L["Current Target:"] = "Текущая цель:"
+L["Threat Bar"] = "Полоса угрозы"
+L["Tank Colors"] = "Цвета танка"
L["Miscellaneous"] = "Разное"
L["Collections"] = "Коллекции"
L["EncounterJ"] = "Приключения"
@@ -516,6 +553,14 @@ L["PowerCostDisplay"] = "Предсказание траты ресурса"
L["PowerCostDisplaydesc"] = "Предсказание траты ресурса на панели ресурса, удар героя у воина, применение заклинаний и т.д"
L["Zodiac:"] = "Зодиак:"
L["Speed"] = "Скорость"
+L["Custom StatusBar"] = "Своя текстура"
+L["StatusBar Texture"] = "Текстура полосы"
+L["Click Through"] = "Пропускать клики"
+L["Frame Strata"] = "Слой фрейма"
+L["Frame Level"] = "Уровень фрейма"
+L["Display Text"] = "Показывать текст"
+L["Transparent"] = "Прозрачный"
+L["Automatic"] = "Автоматически"
--~afkwords
diff --git a/Media/SharedMedia.lua b/Media/SharedMedia.lua
index 45f28580d..b9c2d2486 100644
--- a/Media/SharedMedia.lua
+++ b/Media/SharedMedia.lua
@@ -190,6 +190,11 @@ E.Media = {
Leader = M..[[Textures\Leader.tga]],
LevelUpTex = M..[[Textures\LevelUpTex.blp]],
Logo = M..[[Textures\Logo.tga]],
+ LogoBottom = M..[[Textures\LogoBottom.tga]],
+ LogoBottomSmall = M..[[Textures\LogoBottomSmall.tga]],
+ LogoTop = M..[[Textures\LogoTop.tga]],
+ LogoTopSmall = M..[[Textures\LogoTopSmall.tga]],
+ Resize2 = M..[[Textures\Resize2.tga]],
Mail = M..[[Textures\Mail.tga]],
Melli = M..[[Textures\Melli.tga]],
Minimalist = M..[[Textures\Minimalist.tga]],
diff --git a/Media/Textures/LogoBottom.tga b/Media/Textures/LogoBottom.tga
new file mode 100644
index 000000000..f4454c233
Binary files /dev/null and b/Media/Textures/LogoBottom.tga differ
diff --git a/Media/Textures/LogoBottomSmall.tga b/Media/Textures/LogoBottomSmall.tga
new file mode 100644
index 000000000..6c7a61b5b
Binary files /dev/null and b/Media/Textures/LogoBottomSmall.tga differ
diff --git a/Media/Textures/LogoTop.tga b/Media/Textures/LogoTop.tga
new file mode 100644
index 000000000..6bfe33170
Binary files /dev/null and b/Media/Textures/LogoTop.tga differ
diff --git a/Media/Textures/LogoTopSmall.tga b/Media/Textures/LogoTopSmall.tga
new file mode 100644
index 000000000..61e4608a5
Binary files /dev/null and b/Media/Textures/LogoTopSmall.tga differ
diff --git a/Media/Textures/Resize2.tga b/Media/Textures/Resize2.tga
new file mode 100644
index 000000000..5180b5f5f
Binary files /dev/null and b/Media/Textures/Resize2.tga differ
diff --git a/Modules/ActionBars/ActionBars.lua b/Modules/ActionBars/ActionBars.lua
index b49682486..9d7de4279 100644
--- a/Modules/ActionBars/ActionBars.lua
+++ b/Modules/ActionBars/ActionBars.lua
@@ -600,6 +600,9 @@ function AB:StyleButton(button, noBackdrop, useMasque)
local buttonCooldown = _G[name.."Cooldown"]
local color = self.db.fontColor
+ local hotkeyColor = self.db.useHotkeyColor and self.db.hotkeyColor or color
+ local countColor = self.db.useCountColor and self.db.countColor or color
+ local macroColor = self.db.useMacroColor and self.db.macroColor or color
local countPosition = self.db.countTextPosition or "BOTTOMRIGHT"
local countXOffset = self.db.countTextXOffset or 0
local countYOffset = self.db.countTextYOffset or 2
@@ -616,14 +619,14 @@ function AB:StyleButton(button, noBackdrop, useMasque)
count:ClearAllPoints()
count:Point(countPosition, countXOffset, countYOffset)
count:FontTemplate(LSM:Fetch("font", self.db.font), self.db.fontSize, self.db.fontOutline)
- count:SetTextColor(color.r, color.g, color.b)
+ count:SetTextColor(countColor.r, countColor.g, countColor.b)
end
if macroText then
macroText:ClearAllPoints()
macroText:Point("BOTTOM", 0, 1)
macroText:FontTemplate(LSM:Fetch("font", self.db.font), self.db.fontSize, self.db.fontOutline)
- macroText:SetTextColor(color.r, color.g, color.b)
+ macroText:SetTextColor(macroColor.r, macroColor.g, macroColor.b)
end
if not button.noBackdrop and not button.backdrop and not button.useMasque then
@@ -639,7 +642,7 @@ function AB:StyleButton(button, noBackdrop, useMasque)
if self.db.hotkeytext or self.db.useRangeColorText then
hotkey:FontTemplate(LSM:Fetch("font", self.db.font), self.db.fontSize, self.db.fontOutline)
if button.config and (button.config.outOfRangeColoring ~= "hotkey") then
- button.hotkey:SetTextColor(color.r, color.g, color.b)
+ button.hotkey:SetTextColor(hotkeyColor.r, hotkeyColor.g, hotkeyColor.b)
end
end
diff --git a/Modules/Bags/Bags.lua b/Modules/Bags/Bags.lua
index 7d5db2b7e..8f41355b1 100644
--- a/Modules/Bags/Bags.lua
+++ b/Modules/Bags/Bags.lua
@@ -598,6 +598,8 @@ function B:Layout(isBank)
newBag = (bagID ~= -1 or bagID ~= 0) and B.db.split["bag"..bagID] or false
end
+ local bagShown = B.db.shownBags and B.db.shownBags["bag"..bagID] ~= false
+
--Bag Containers
if (not isBank) or (isBank and (bagID ~= -1) and (numContainerSlots >= 1) and ((i - 1 <= numContainerSlots))) then
if not f.ContainerHolder[i] then
@@ -657,6 +659,20 @@ function B:Layout(isBank)
end
f.ContainerHolder[i].iconTexture:SetInside()
f.ContainerHolder[i].iconTexture:SetTexCoord(unpack(E.TexCoords))
+
+ f.ContainerHolder[i].isBank = isBank
+
+ if bagID ~= 0 and bagID ~= -1 then
+ f.ContainerHolder[i].shownIcon = f.ContainerHolder[i]:CreateTexture(nil, "OVERLAY", nil, 1)
+ f.ContainerHolder[i].shownIcon:Size(16)
+ f.ContainerHolder[i].shownIcon:Point("BOTTOMLEFT", 1, 1)
+ B:SetBagShownTexture(f.ContainerHolder[i].shownIcon, bagShown)
+ f.ContainerHolder[i]:HookScript("OnClick", function(holder)
+ if IsShiftKeyDown() and not CursorHasItem() then
+ B:ToggleContainer(holder)
+ end
+ end)
+ end
end
f.ContainerHolder:Size(((buttonSize + buttonSpacing) * (isBank and i - 1 or i)) + buttonSpacing, buttonSize + (buttonSpacing * 2))
@@ -679,7 +695,7 @@ function B:Layout(isBank)
--Bag Slots
local numSlots = GetContainerNumSlots(bagID)
- if numSlots > 0 then
+ if numSlots > 0 and bagShown then
if not f.Bags[bagID] then
f.Bags[bagID] = CreateFrame("Frame", f:GetName().."Bag"..bagID, f.holderFrame)
-- f.Bags[bagID]:SetBagID(bagID)
@@ -1523,6 +1539,102 @@ function B:ToggleBags(id)
end
end
+function B:SetBagShownTexture(icon, shown)
+ if not icon then return end
+ local texture = shown and (_G.READY_CHECK_READY_TEXTURE or READY_TEX) or (_G.READY_CHECK_NOT_READY_TEXTURE or NOT_READY_TEX)
+ icon:SetTexture(texture)
+end
+
+function B:IsBagShown(bagID)
+ return bagID and B.db.shownBags["bag"..bagID] ~= false
+end
+
+function B:SetBagShown(bagID, shown)
+ B.db.shownBags["bag"..bagID] = shown
+end
+
+function B:ToggleContainer(holder)
+ if not holder then return end
+
+ local swap = not B:IsBagShown(holder.id)
+
+ B:SetBagShown(holder.id, swap)
+ B:SetBagShownTexture(holder.shownIcon, swap)
+
+ if B:AnyBagsShown() then
+ B:Layout(holder.isBank)
+ return true
+ else
+ B:CloseAllBags()
+ end
+end
+
+function B:AnyBagsShown()
+ if not B.BagFrame then return true end
+
+ for _, bagID in ipairs(B.BagFrame.BagIDs) do
+ if B.db.shownBags["bag"..bagID] ~= false then
+ return true
+ end
+ end
+end
+
+function B:CloseAllBags()
+ if not B.BagFrame or not B.BagFrame:IsShown() then return false end
+
+ B:CloseBags()
+ return true
+end
+
+B.AutoToggleEvents = {
+ AUCTION_HOUSE_SHOW = "auctionHouse",
+ AUCTION_HOUSE_CLOSED = "auctionHouse",
+ TRADE_SKILL_SHOW = "professions",
+ TRADE_SKILL_CLOSE = "professions",
+ TRADE_SHOW = "trade",
+ TRADE_CLOSED = "trade"
+}
+
+B.AutoToggleClose = {
+ AUCTION_HOUSE_CLOSED = true,
+ TRADE_SKILL_CLOSE = true,
+ TRADE_CLOSED = true
+}
+
+function B:HandleOpenAllBags(frame)
+ if not frame then return end
+
+ local mail = frame == _G.MailFrame and frame:IsShown()
+ local vendor = frame == _G.MerchantFrame and frame:IsShown()
+
+ if (not mail and not vendor) or (mail and B.db.autoToggle.mail) or (vendor and B.db.autoToggle.vendor) then
+ B:OpenBags()
+ else
+ B:CloseBags()
+ end
+end
+
+function B:AutoToggleFunction()
+ local option = B.AutoToggleEvents[self]
+ if not option then return end
+
+ if B.db.autoToggle[option] and not B.AutoToggleClose[self] then
+ B:OpenBags()
+ else
+ B:CloseBags()
+ end
+end
+
+function B:SetupAutoToggle()
+ for event in next, B.AutoToggleEvents do
+ if B.db.autoToggle.enable then
+ B:RegisterEvent(event, B.AutoToggleFunction)
+ else
+ B:UnregisterEvent(event)
+ end
+ end
+end
+
function B:ToggleBackpack()
if IsOptionFrameOpen() then return end
@@ -1576,7 +1688,9 @@ function B:OpenBank()
--Call :Layout first so all elements are created before we update
B:Layout(true)
- B:OpenBags()
+ if not B.db.autoToggle or B.db.autoToggle.bank then
+ B:OpenBags()
+ end
B:UpdateTokens()
B.BankFrame:Show()
@@ -1906,7 +2020,7 @@ function B:Initialize()
B.BagFrame = B:ContructContainerFrame("ElvUI_ContainerFrame")
--Hook onto Blizzard Functions
- B:SecureHook("OpenAllBags", "ToggleBackpack")
+ B:SecureHook("OpenAllBags", "HandleOpenAllBags")
B:SecureHook("CloseAllBags", "CloseBags")
B:SecureHook("ToggleBag", "ToggleBags")
B:SecureHook("OpenBackpack", "OpenBags")
@@ -1924,6 +2038,16 @@ function B:Initialize()
B:RegisterEvent("BANKFRAME_CLOSED", "CloseBank")
B:RegisterEvent("PLAYERBANKBAGSLOTS_CHANGED")
B:RegisterEvent("GUILDBANKBAGSLOTS_CHANGED")
+ B:SetupAutoToggle()
+
+ local guildBankFrame = _G.GuildBankFrame
+ if guildBankFrame and B.db.autoToggle.guildBank then
+ guildBankFrame:HookScript("OnShow", function()
+ if guildBankFrame:IsShown() then
+ B:OpenBags()
+ end
+ end)
+ end
end
local function InitializeCallback()
diff --git a/Modules/Blizzard/Blizzard.lua b/Modules/Blizzard/Blizzard.lua
index 207bc4eb0..8c5e011f6 100644
--- a/Modules/Blizzard/Blizzard.lua
+++ b/Modules/Blizzard/Blizzard.lua
@@ -19,8 +19,8 @@ function B:ADDON_LOADED(_, addon)
ChatFrameEditBox:Insert(GetTradeSkillListLink())
end)
-
- self:UnregisterEvent("ADDON_LOADED")
+ elseif addon == "Blizzard_GuildBankUI" then
+ self:ImproveGuildBank()
end
end
diff --git a/Modules/Blizzard/GuildBank.lua b/Modules/Blizzard/GuildBank.lua
new file mode 100644
index 000000000..3e2c66be4
--- /dev/null
+++ b/Modules/Blizzard/GuildBank.lua
@@ -0,0 +1,106 @@
+local E, L, V, P, G = unpack(select(2, ...)) --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
+local BL = E:GetModule("Blizzard")
+local LSM = E.Libs.LSM
+
+--Lua functions
+local _G = _G
+local unpack = unpack
+--WoW API / Variables
+local GetCurrentGuildBankTab = GetCurrentGuildBankTab
+local GetGuildBankItemLink = GetGuildBankItemLink
+local GetItemInfo = GetItemInfo
+local GetItemQualityColor = GetItemQualityColor
+local hooksecurefunc = hooksecurefunc
+
+local NUM_SLOTS_PER_GUILDBANK_GROUP = 14
+local NUM_GUILDBANK_COLUMNS = 7
+
+local function IsItemEligibleForItemLevelDisplay(classID, subclassID, itemEquipLoc, rarity)
+ return (rarity and rarity > 1)
+ and (itemEquipLoc ~= nil and itemEquipLoc ~= "" and itemEquipLoc ~= "INVTYPE_AMMO" and itemEquipLoc ~= "INVTYPE_BAG" and itemEquipLoc ~= "INVTYPE_QUIVER" and itemEquipLoc ~= "INVTYPE_TABARD")
+ and (classID and subclassID)
+end
+
+function BL:GuildBank_ItemLevel(button)
+ local db = E.db.general.guildBank
+ if not db then return end
+
+ if not button.itemLevel then
+ button.itemLevel = button:CreateFontString(nil, "ARTWORK", nil, 1)
+ end
+
+ button.itemLevel:ClearAllPoints()
+ button.itemLevel:Point(db.itemLevelPosition, db.itemLevelxOffset, db.itemLevelyOffset)
+ button.itemLevel:FontTemplate(LSM:Fetch("font", db.itemLevelFont), db.itemLevelFontSize, db.itemLevelFontOutline)
+
+ local ilvl, r, g, b
+ local tab = db.itemLevel and GetCurrentGuildBankTab()
+ local itemlink = tab and GetGuildBankItemLink(tab, button:GetID())
+ if itemlink then
+ local _, _, rarity, itemLevel, _, _, _, _, itemEquipLoc, _, _, classID, subclassID = GetItemInfo(itemlink)
+ if rarity and rarity > 1 then
+ r, g, b = GetItemQualityColor(rarity)
+ end
+
+ if rarity and rarity > 1 and db.itemQuality then
+ button:SetBackdropBorderColor(r, g, b)
+ else
+ button:SetBackdropBorderColor(unpack(E.media.bordercolor))
+ end
+
+ local canShowItemLevel = IsItemEligibleForItemLevelDisplay(classID, subclassID, itemEquipLoc, rarity)
+ if canShowItemLevel and db.itemLevel then
+ local custom = db.itemLevelCustomColorEnable and db.itemLevelCustomColor
+ if custom then
+ r, g, b = custom.r, custom.g, custom.b
+ elseif rarity and rarity > 1 then -- иначе это уже сделано выше
+ r, g, b = GetItemQualityColor(rarity)
+ end
+
+ ilvl = itemLevel
+ end
+ else
+ button:SetBackdropBorderColor(unpack(E.media.bordercolor))
+ end
+
+ button.itemLevel:SetText(ilvl and ilvl >= db.itemLevelThreshold and ilvl or "")
+ button.itemLevel:SetTextColor(r or 1, g or 1, b or 1)
+end
+
+function BL:GuildBank_CountText(button)
+ local db = E.db.general.guildBank
+ if not db then return end
+
+ button.Count = _G[button:GetName().."Count"]
+ button.Count:ClearAllPoints()
+ button.Count:Point(db.countPosition, db.countxOffset, db.countyOffset)
+ button.Count:FontTemplate(LSM:Fetch("font", db.countFont), db.countFontSize, db.countFontOutline)
+ button.Count:SetTextColor(db.countFontColor.r, db.countFontColor.g, db.countFontColor.b)
+end
+
+function BL:GuildBank_Update()
+ local frame = _G.GuildBankFrame
+ if not frame or not frame:IsShown() then return end
+
+ if frame.mode ~= "bank" then
+ frame.inset:Point("BOTTOMRIGHT", -29, 62)
+ return
+ else
+ frame.inset:Point("BOTTOMRIGHT", -8, 62)
+
+ _G.GuildBankColumn1:Point("TOPLEFT", 20, -70)
+ end
+
+ for i = 1, NUM_GUILDBANK_COLUMNS do
+ for x = 1, NUM_SLOTS_PER_GUILDBANK_GROUP do
+ local button = _G["GuildBankColumn"..i.."Button"..x]
+
+ BL:GuildBank_ItemLevel(button)
+ BL:GuildBank_CountText(button)
+ end
+ end
+end
+
+function BL:ImproveGuildBank()
+ hooksecurefunc("GuildBankFrame_Update", BL.GuildBank_Update)
+end
diff --git a/Modules/Blizzard/Load_Blizzard.xml b/Modules/Blizzard/Load_Blizzard.xml
index 27ef0f806..5667e9e3f 100644
--- a/Modules/Blizzard/Load_Blizzard.xml
+++ b/Modules/Blizzard/Load_Blizzard.xml
@@ -9,4 +9,5 @@
+
\ No newline at end of file
diff --git a/Modules/DataBars/DataBars.lua b/Modules/DataBars/DataBars.lua
index 7f96a055b..55f45244e 100644
--- a/Modules/DataBars/DataBars.lua
+++ b/Modules/DataBars/DataBars.lua
@@ -1,11 +1,25 @@
local E, L, V, P, G = unpack(select(2, ...)); --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
local mod = E:GetModule("DataBars")
+local LSM = E.Libs.LSM
+
+function mod:GetBarOrientation(db)
+ if db.orientation == "AUTOMATIC" then
+ return db.height > db.width and "VERTICAL" or "HORIZONTAL"
+ end
+
+ return db.orientation
+end
+
+function mod:GetBarTexture()
+ return self.db.customTexture and LSM:Fetch("statusbar", self.db.statusbar) or E.media.normTex
+end
function mod.OnLeave(self)
if (self == ElvUI_ExperienceBar and mod.db.experience.mouseover)
or (self == ElvUI_PetExperienceBar and mod.db.petExperience.mouseover)
or (self == ElvUI_ReputationBar and mod.db.reputation.mouseover)
or (self == ElvUI_HonorBar and mod.db.honor and mod.db.honor.mouseover)
+ or (self == ElvUI_ThreatBar and mod.db.threat and mod.db.threat.mouseover)
then
E:UIFrameFadeOut(self, 1, self:GetAlpha(), 0)
end
@@ -52,7 +66,7 @@ end
function mod:UpdateBarBubbles(bar, db)
if db.showBubbles then
- local vertical = db.orientation ~= "HORIZONTAL"
+ local vertical = self:GetBarOrientation(db) ~= "HORIZONTAL"
local width = vertical and db.width or 1
local height = not vertical and db.height or 1
local offset = (vertical and db.height or db.width) / 20
@@ -76,6 +90,9 @@ function mod:UpdateDataBarDimensions()
if self.honorBar then
self:UpdateHonorDimensions()
end
+ if self.threatBar then
+ self:UpdateThreatDimensions()
+ end
end
function mod:ToggleAll()
@@ -85,6 +102,9 @@ function mod:ToggleAll()
if self.honorBar then
self:EnableDisable_HonorBar()
end
+ if self.threatBar then
+ self:ThreatBar_Toggle()
+ end
end
function mod:UpdateAll()
@@ -99,10 +119,217 @@ function mod:Initialize()
self:ExperienceBar_Load()
self:PetExperienceBar_Load()
self:ReputationBar_Load()
+ self:ThreatBar_Load()
+end
+
+local ElvUF = E.oUF
+
+local next = next
+local wipe = wipe
+local strmatch = strmatch
+
+local UnitAffectingCombat = UnitAffectingCombat
+local UnitDetailedThreatSituation = UnitDetailedThreatSituation
+local UnitIsPlayer = UnitIsPlayer
+local UnitReaction = UnitReaction
+local UnitExists = UnitExists
+local UnitIsUnit = UnitIsUnit
+local UnitClass = UnitClass
+local UnitName = UnitName
+local UNKNOWN = UNKNOWN
+
+local tankStatus = {[0] = 3, 2, 1, 0}
+
+function mod:ThreatBar_GetLargestThreatOnList(percent)
+ local largestValue, largestUnit = 0, nil
+ for unit, threatPercent in next, self.threatBar.list do
+ if threatPercent > largestValue then
+ largestValue = threatPercent
+ largestUnit = unit
+ end
+ end
+
+ return (percent - largestValue), largestUnit
+end
+
+function mod:ThreatBar_GetColor(unit)
+ local unitReaction = UnitReaction(unit, "player")
+ local _, unitClass = UnitClass(unit)
+ if UnitIsPlayer(unit) then
+ local class = E:ClassColor(unitClass)
+ if not class then return 194, 194, 194 end
+ return class.r * 255, class.g * 255, class.b * 255
+ elseif unitReaction then
+ local reaction = ElvUF.colors.reaction[unitReaction]
+ if reaction then
+ return reaction[1] * 255, reaction[2] * 255, reaction[3] * 255
+ end
+ return 194, 194, 194
+ else
+ return 194, 194, 194
+ end
+end
+
+function mod:ThreatBar_OnEnter()
+ if mod.db.threat.mouseover then
+ E:UIFrameFadeIn(self, 0.4, self:GetAlpha(), 1)
+ end
+
+ GameTooltip:ClearLines()
+ GameTooltip:SetOwner(self, "ANCHOR_CURSOR", 0, -4)
+ GameTooltip:AddLine(L["Threat Bar"])
+ GameTooltip:AddLine(" ")
+ GameTooltip:AddDoubleLine(L["Current Target:"], UnitName("target") or UNKNOWN, 1, 1, 1)
+ GameTooltip:Show()
+end
+
+function mod:ThreatBar_OnClick()
+ if UnitExists("target") and not UnitIsUnit("target", "player") then
+ TargetUnit("player")
+ end
+end
+
+function mod:ThreatBar_Update(event, unit)
+ if not mod.db.threat.enable then return end
+ if (event == "UNIT_THREAT_LIST_UPDATE" or event == "UNIT_FLAGS") and unit and unit ~= "player" and unit ~= "pet" and not strmatch(unit, "^party") and not strmatch(unit, "^raid") then return end
+
+ local bar = mod.threatBar
+ if not bar then return end
+
+ local petExists = UnitExists("pet")
+ local showBar = false
+
+ if UnitAffectingCombat("player") and (petExists or E.IsInGroup) then
+ local _, status, percent = UnitDetailedThreatSituation("player", "target")
+
+ if percent then
+ local name, isTank = UnitName("target") or UNKNOWN, E.myrole == "TANK"
+ showBar = true
+
+ local leadPercent, largestUnit
+ if percent == 100 then
+ if petExists then
+ _, _, bar.list.pet = UnitDetailedThreatSituation("pet", "target")
+ end
+
+ for guid, role in next, E.GroupRoles do
+ local unitID = E.GroupUnitsByRole[role][guid]
+ if unitID and not UnitIsUnit(unitID, "player") then
+ _, _, bar.list[unitID] = UnitDetailedThreatSituation(unitID, "target")
+ end
+ end
+
+ leadPercent, largestUnit = mod:ThreatBar_GetLargestThreatOnList(percent)
+ end
+
+ if largestUnit and leadPercent > 0 then
+ local r, g, b = mod:ThreatBar_GetColor(largestUnit)
+ bar.text:SetFormattedText(L["ABOVE_THREAT_FORMAT"], name, percent, leadPercent, r, g, b, UnitName(largestUnit) or UNKNOWN)
+ bar.statusBar:SetValue(isTank and leadPercent or percent)
+ else
+ bar.text:SetFormattedText("%s: %.0f%%", name, percent)
+ bar.statusBar:SetValue(percent)
+ end
+
+ local r, g, b = GetThreatStatusColor(isTank and bar.db.tankStatus and tankStatus[status] or status)
+ if r then
+ bar.statusBar:SetStatusBarColor(r, g, b, 0.8)
+ end
+ end
+ end
+
+ if not showBar then
+ bar:Hide()
+ else
+ bar:Show()
+
+ if mod.db.threat.hideInVehicle then
+ E:RegisterObjectForVehicleLock(bar, E.UIParent)
+ else
+ E:UnregisterObjectForVehicleLock(bar)
+ end
+ end
+
+ bar.text:SetShown(bar.db.displayText)
+
+ wipe(bar.list)
+end
+
+function mod:ThreatBar_Toggle()
+ local bar = self.threatBar
+ if not bar then return end
+
+ bar.db = self.db.threat
+
+ E:SetSmoothing(bar.statusBar, bar.db.smoothbars)
+
+ if bar.db.enable then
+ bar.eventFrame:RegisterEvent("PLAYER_TARGET_CHANGED")
+ bar.eventFrame:RegisterEvent("UNIT_THREAT_LIST_UPDATE")
+ bar.eventFrame:RegisterEvent("RAID_ROSTER_UPDATE")
+ bar.eventFrame:RegisterEvent("PARTY_MEMBERS_CHANGED")
+ bar.eventFrame:RegisterEvent("UNIT_FLAGS")
+ bar.eventFrame:RegisterEvent("UNIT_PET")
+
+ self:ThreatBar_Update()
+ E:EnableMover(bar.mover:GetName())
+ else
+ bar.eventFrame:UnregisterEvent("PLAYER_TARGET_CHANGED")
+ bar.eventFrame:UnregisterEvent("UNIT_THREAT_LIST_UPDATE")
+ bar.eventFrame:UnregisterEvent("RAID_ROSTER_UPDATE")
+ bar.eventFrame:UnregisterEvent("PARTY_MEMBERS_CHANGED")
+ bar.eventFrame:UnregisterEvent("UNIT_FLAGS")
+ bar.eventFrame:UnregisterEvent("UNIT_PET")
+
+ bar:Hide()
+ E:DisableMover(bar.mover:GetName())
+ end
+end
+
+function mod:UpdateThreatDimensions()
+ local db = self.db.threat
+ if not self.threatBar then return end
+
+ self.threatBar:SetWidth(db.width)
+ self.threatBar:SetHeight(db.height)
+ self.threatBar:SetAlpha(db.mouseover and 0 or 1)
+ self.threatBar:SetTemplate(self.db.transparent and "Transparent" or nil)
+ self.threatBar:EnableMouse(not db.clickThrough)
+ self.threatBar:SetFrameLevel(db.frameLevel)
+ self.threatBar:SetFrameStrata(db.frameStrata)
+
+ local orientation = self:GetBarOrientation(db)
+
+ self.threatBar.statusBar:SetOrientation(orientation)
+ self.threatBar.statusBar:SetRotatesTexture(orientation ~= "HORIZONTAL")
+ self.threatBar.statusBar:SetStatusBarTexture(self:GetBarTexture())
+
+ self.threatBar.text:FontTemplate(LSM:Fetch("font", db.font), db.textSize, db.fontOutline)
+ self.threatBar.text:ClearAllPoints()
+ self.threatBar.text:Point(db.anchorPoint, db.xOffset, db.yOffset)
+ self.threatBar.text:SetShown(db.displayText)
+end
+
+function mod:ThreatBar_Load()
+ self.threatBar = self:CreateBar("ElvUI_ThreatBar", mod.ThreatBar_OnEnter, mod.ThreatBar_OnClick, "TOPRIGHT", E.UIParent, "TOPRIGHT", -3, -245)
+ self.threatBar.statusBar:SetMinMaxValues(0, 100)
+ self.threatBar.list = {}
+ self.threatBar.db = self.db.threat
+
+ self.threatBar.eventFrame = CreateFrame("Frame")
+ self.threatBar.eventFrame:Hide()
+ self.threatBar.eventFrame:SetScript("OnEvent", function(_, event, unit) self:ThreatBar_Update(event, unit) end)
+
+ E:CreateMover(self.threatBar, "ThreatBarMover", L["Threat Bar"], nil, nil, nil, nil, nil, "databars,threat")
+
+ self:ThreatBar_Toggle()
+ self:UpdateThreatDimensions()
end
local function InitializeCallback()
mod:Initialize()
end
+E:RegisterCallback("StaggeredUpdate", mod.UpdateAll, mod)
+
E:RegisterModule(mod:GetName(), InitializeCallback)
\ No newline at end of file
diff --git a/Modules/DataBars/Experience.lua b/Modules/DataBars/Experience.lua
index c07ce2dc2..027661237 100644
--- a/Modules/DataBars/Experience.lua
+++ b/Modules/DataBars/Experience.lua
@@ -164,29 +164,43 @@ function mod:ExperienceBar_OnClick(button)
end
function mod:ExperienceBar_UpdateDimensions()
- self.expBar:Size(self.db.experience.width, self.db.experience.height)
- self.expBar:SetAlpha(self.db.experience.mouseover and 0 or 1)
-
- self.expBar.text:FontTemplate(LSM:Fetch("font", self.db.experience.font), self.db.experience.textSize, self.db.experience.fontOutline)
-
- self.expBar.statusBar:SetOrientation(self.db.experience.orientation)
- self.expBar.statusBar:SetRotatesTexture(self.db.experience.orientation ~= "HORIZONTAL")
-
- self.expBar.rested:SetOrientation(self.db.experience.orientation)
- self.expBar.rested:SetRotatesTexture(self.db.experience.orientation ~= "HORIZONTAL")
-
- self.expBar.questBar:SetOrientation(self.db.experience.orientation)
- self.expBar.questBar:SetRotatesTexture(self.db.experience.orientation ~= "HORIZONTAL")
-
- local color = self.db.experience.questXP.color
+ local db = self.db.experience
+ self.expBar:Size(db.width, db.height)
+ self.expBar:SetAlpha(db.mouseover and 0 or 1)
+ self.expBar:SetTemplate(self.db.transparent and "Transparent" or nil)
+ self.expBar:EnableMouse(not db.clickThrough)
+ self.expBar:SetFrameLevel(db.frameLevel)
+ self.expBar:SetFrameStrata(db.frameStrata)
+
+ local orientation = self:GetBarOrientation(db)
+ local texture = self:GetBarTexture()
+
+ self.expBar.text:FontTemplate(LSM:Fetch("font", db.font), db.textSize, db.fontOutline)
+ self.expBar.text:ClearAllPoints()
+ self.expBar.text:Point(db.anchorPoint, db.xOffset, db.yOffset)
+ self.expBar.text:SetShown(db.displayText)
+
+ self.expBar.statusBar:SetOrientation(orientation)
+ self.expBar.statusBar:SetRotatesTexture(orientation ~= "HORIZONTAL")
+ self.expBar.statusBar:SetStatusBarTexture(texture)
+
+ self.expBar.rested:SetOrientation(orientation)
+ self.expBar.rested:SetRotatesTexture(orientation ~= "HORIZONTAL")
+ self.expBar.rested:SetStatusBarTexture(texture)
+
+ self.expBar.questBar:SetOrientation(orientation)
+ self.expBar.questBar:SetRotatesTexture(orientation ~= "HORIZONTAL")
+ self.expBar.questBar:SetStatusBarTexture(texture)
+
+ local color = db.questXP.color
self.expBar.questBar:SetStatusBarColor(color.r, color.g, color.b, color.a)
if self.expBar.bubbles then
- self:UpdateBarBubbles(self.expBar, self.db.experience)
- elseif self.db.experience.showBubbles then
+ self:UpdateBarBubbles(self.expBar, db)
+ elseif db.showBubbles then
local bubbles = self:CreateBarBubbles(self.expBar)
bubbles:SetFrameLevel(5)
- self:UpdateBarBubbles(self.expBar, self.db.experience)
+ self:UpdateBarBubbles(self.expBar, db)
end
end
diff --git a/Modules/DataBars/Honor_Sirus.lua b/Modules/DataBars/Honor_Sirus.lua
index 9b736e9db..8cd233719 100644
--- a/Modules/DataBars/Honor_Sirus.lua
+++ b/Modules/DataBars/Honor_Sirus.lua
@@ -86,22 +86,25 @@ function DB:HonorBar_OnClick()
end
function DB:UpdateHonorDimensions()
- self.honorBar:SetWidth(self.db.honor.width)
- self.honorBar:SetHeight(self.db.honor.height)
- self.honorBar.statusBar:SetOrientation(self.db.honor.orientation)
- self.honorBar.text:FontTemplate(LSM:Fetch("font", self.db.honor.font), self.db.honor.textSize, self.db.honor.fontOutline)
-
- if DB.db.honor.orientation == "HORIZONTAL" then
- self.honorBar.statusBar:SetRotatesTexture(false)
- else
- self.honorBar.statusBar:SetRotatesTexture(true)
- end
-
- if self.db.honor.mouseover then
- self.honorBar:SetAlpha(0)
- else
- self.honorBar:SetAlpha(1)
- end
+ local db = self.db.honor
+ self.honorBar:SetWidth(db.width)
+ self.honorBar:SetHeight(db.height)
+ self.honorBar:SetAlpha(db.mouseover and 0 or 1)
+ self.honorBar:SetTemplate(self.db.transparent and "Transparent" or nil)
+ self.honorBar:EnableMouse(not db.clickThrough)
+ self.honorBar:SetFrameLevel(db.frameLevel)
+ self.honorBar:SetFrameStrata(db.frameStrata)
+
+ local orientation = self:GetBarOrientation(db)
+
+ self.honorBar.statusBar:SetOrientation(orientation)
+ self.honorBar.statusBar:SetRotatesTexture(orientation ~= "HORIZONTAL")
+ self.honorBar.statusBar:SetStatusBarTexture(self:GetBarTexture())
+
+ self.honorBar.text:FontTemplate(LSM:Fetch("font", db.font), db.textSize, db.fontOutline)
+ self.honorBar.text:ClearAllPoints()
+ self.honorBar.text:Point(db.anchorPoint, db.xOffset, db.yOffset)
+ self.honorBar.text:SetShown(db.displayText)
end
function DB:EnableDisable_HonorBar()
diff --git a/Modules/DataBars/PetExperience.lua b/Modules/DataBars/PetExperience.lua
index 4661be7c1..d8d8b9ef2 100644
--- a/Modules/DataBars/PetExperience.lua
+++ b/Modules/DataBars/PetExperience.lua
@@ -77,20 +77,31 @@ end
function mod:PetExperienceBar_UpdateDimensions()
if E.myclass ~= "HUNTER" then return end
- self.petExpBar:Size(self.db.petExperience.width, self.db.petExperience.height)
- self.petExpBar:SetAlpha(self.db.petExperience.mouseover and 0 or 1)
+ local db = self.db.petExperience
+ self.petExpBar:Size(db.width, db.height)
+ self.petExpBar:SetAlpha(db.mouseover and 0 or 1)
+ self.petExpBar:SetTemplate(self.db.transparent and "Transparent" or nil)
+ self.petExpBar:EnableMouse(not db.clickThrough)
+ self.petExpBar:SetFrameLevel(db.frameLevel)
+ self.petExpBar:SetFrameStrata(db.frameStrata)
- self.petExpBar.text:FontTemplate(LSM:Fetch("font", self.db.petExperience.font), self.db.petExperience.textSize, self.db.petExperience.fontOutline)
+ local orientation = self:GetBarOrientation(db)
- self.petExpBar.statusBar:SetOrientation(self.db.petExperience.orientation)
- self.petExpBar.statusBar:SetRotatesTexture(self.db.petExperience.orientation ~= "HORIZONTAL")
+ self.petExpBar.text:FontTemplate(LSM:Fetch("font", db.font), db.textSize, db.fontOutline)
+ self.petExpBar.text:ClearAllPoints()
+ self.petExpBar.text:Point(db.anchorPoint, db.xOffset, db.yOffset)
+ self.petExpBar.text:SetShown(db.displayText)
+
+ self.petExpBar.statusBar:SetOrientation(orientation)
+ self.petExpBar.statusBar:SetRotatesTexture(orientation ~= "HORIZONTAL")
+ self.petExpBar.statusBar:SetStatusBarTexture(self:GetBarTexture())
if self.petExpBar.bubbles then
- self:UpdateBarBubbles(self.petExpBar, self.db.petExperience)
- elseif self.db.petExperience.showBubbles then
+ self:UpdateBarBubbles(self.petExpBar, db)
+ elseif db.showBubbles then
local bubbles = self:CreateBarBubbles(self.petExpBar)
bubbles:SetFrameLevel(5)
- self:UpdateBarBubbles(self.petExpBar, self.db.petExperience)
+ self:UpdateBarBubbles(self.petExpBar, db)
end
end
diff --git a/Modules/DataBars/Reputation.lua b/Modules/DataBars/Reputation.lua
index 8c44e750f..5ead1d67e 100644
--- a/Modules/DataBars/Reputation.lua
+++ b/Modules/DataBars/Reputation.lua
@@ -87,20 +87,31 @@ function mod:ReputationBar_OnClick()
end
function mod:ReputationBar_UpdateDimensions()
- self.repBar:Size(self.db.reputation.width, self.db.reputation.height)
- self.repBar:SetAlpha(self.db.reputation.mouseover and 0 or 1)
+ local db = self.db.reputation
+ self.repBar:Size(db.width, db.height)
+ self.repBar:SetAlpha(db.mouseover and 0 or 1)
+ self.repBar:SetTemplate(self.db.transparent and "Transparent" or nil)
+ self.repBar:EnableMouse(not db.clickThrough)
+ self.repBar:SetFrameLevel(db.frameLevel)
+ self.repBar:SetFrameStrata(db.frameStrata)
- self.repBar.text:FontTemplate(LSM:Fetch("font", self.db.reputation.font), self.db.reputation.textSize, self.db.reputation.fontOutline)
+ local orientation = self:GetBarOrientation(db)
- self.repBar.statusBar:SetOrientation(self.db.reputation.orientation)
- self.repBar.statusBar:SetRotatesTexture(self.db.reputation.orientation ~= "HORIZONTAL")
+ self.repBar.text:FontTemplate(LSM:Fetch("font", db.font), db.textSize, db.fontOutline)
+ self.repBar.text:ClearAllPoints()
+ self.repBar.text:Point(db.anchorPoint, db.xOffset, db.yOffset)
+ self.repBar.text:SetShown(db.displayText)
+
+ self.repBar.statusBar:SetOrientation(orientation)
+ self.repBar.statusBar:SetRotatesTexture(orientation ~= "HORIZONTAL")
+ self.repBar.statusBar:SetStatusBarTexture(self:GetBarTexture())
if self.repBar.bubbles then
- self:UpdateBarBubbles(self.repBar, self.db.reputation)
- elseif self.db.reputation.showBubbles then
+ self:UpdateBarBubbles(self.repBar, db)
+ elseif db.showBubbles then
local bubbles = self:CreateBarBubbles(self.repBar)
bubbles:SetFrameLevel(5)
- self:UpdateBarBubbles(self.repBar, self.db.reputation)
+ self:UpdateBarBubbles(self.repBar, db)
end
end
diff --git a/Modules/DataTexts/Agility.lua b/Modules/DataTexts/Agility.lua
new file mode 100644
index 000000000..be753d49b
--- /dev/null
+++ b/Modules/DataTexts/Agility.lua
@@ -0,0 +1,30 @@
+local E, L, V, P, G = unpack(select(2, ...)) --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
+local DT = E:GetModule("DataTexts")
+
+--Lua functions
+local join = string.join
+--WoW API / Variables
+local UnitStat = UnitStat
+
+local ITEM_MOD_AGILITY_SHORT = ITEM_MOD_AGILITY_SHORT
+local LE_UNIT_STAT_AGILITY = 2
+
+local displayString = ""
+local lastPanel
+
+local function OnEvent(self)
+ lastPanel = self
+
+ self.text:SetFormattedText(displayString, UnitStat("player", LE_UNIT_STAT_AGILITY))
+end
+
+local function ValueColorUpdate(hex)
+ displayString = join("", ITEM_MOD_AGILITY_SHORT, ": ", hex, "%d|r")
+
+ if lastPanel ~= nil then
+ OnEvent(lastPanel)
+ end
+end
+E.valueColorUpdateFuncs[ValueColorUpdate] = true
+
+DT:RegisterDatatext("Agility", {"UNIT_STATS", "UNIT_AURA"}, OnEvent, nil, nil, nil, nil, ITEM_MOD_AGILITY_SHORT)
diff --git a/Modules/DataTexts/Ammo.lua b/Modules/DataTexts/Ammo.lua
new file mode 100644
index 000000000..8c32855b8
--- /dev/null
+++ b/Modules/DataTexts/Ammo.lua
@@ -0,0 +1,179 @@
+local E, L, V, P, G = unpack(select(2, ...)) --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
+local DT = E:GetModule("DataTexts")
+
+--Lua functions
+local _G = _G
+local select, wipe = select, wipe
+local format, join, strmatch = string.format, string.join, string.match
+--WoW API / Variables
+local GetAuctionItemSubClasses = GetAuctionItemSubClasses
+local GetItemInfo = GetItemInfo
+local GetItemCount = GetItemCount
+local GetInventoryItemCount = GetInventoryItemCount
+local GetInventoryItemID = GetInventoryItemID
+local ContainerIDToInventoryID = ContainerIDToInventoryID
+local GetContainerNumSlots = GetContainerNumSlots
+local GetContainerNumFreeSlots = GetContainerNumFreeSlots
+local GetContainerItemInfo = GetContainerItemInfo
+local GetItemQualityColor = GetItemQualityColor
+
+local NUM_BAG_SLOTS = NUM_BAG_SLOTS
+local NUM_BAG_FRAMES = NUM_BAG_FRAMES
+local INVTYPE_AMMO = INVTYPE_AMMO
+local INVSLOT_RANGED = INVSLOT_RANGED
+local INVSLOT_AMMO = INVSLOT_AMMO
+local NOT_APPLICABLE = NOT_APPLICABLE
+local CURRENTLY_EQUIPPED = CURRENTLY_EQUIPPED
+
+local QUIVER = select(1, GetAuctionItemSubClasses(8))
+local POUCH = select(2, GetAuctionItemSubClasses(8))
+local SOULBAG = select(2, GetAuctionItemSubClasses(3))
+
+local iconString = "|T%s:24:24:0:0:64:64:4:55:4:55|t"
+local displayString = ""
+local itemName = {}
+
+local waitingItemID
+local function OnEvent(self, event, ...)
+ local name, count, itemID, itemEquipLoc
+
+ if event == "GET_ITEM_INFO_RECEIVED" then
+ itemID = ...
+
+ if itemID ~= waitingItemID then return end
+ waitingItemID = nil
+
+ if not itemName[itemID] then
+ itemName[itemID] = GetItemInfo(itemID)
+ end
+
+ self:UnregisterEvent("GET_ITEM_INFO_RECEIVED")
+ end
+
+ if E.myclass == "WARLOCK" then
+ itemID = 6265 -- осколок души
+ name, count = itemName[itemID] or GetItemInfo(itemID), GetItemCount(itemID)
+
+ if name and not itemName[itemID] then
+ itemName[itemID] = name
+ end
+
+ self.text:SetFormattedText(displayString, name or "Soul Shard", count or 0)
+ else
+ local RangeItemID = GetInventoryItemID("player", INVSLOT_RANGED)
+ if RangeItemID then
+ itemEquipLoc = select(9, GetItemInfo(RangeItemID))
+ end
+
+ if itemEquipLoc == "INVTYPE_THROWN" then
+ itemID, count = RangeItemID, GetInventoryItemCount("player", INVSLOT_RANGED)
+ else
+ itemID, count = GetInventoryItemID("player", INVSLOT_AMMO), GetInventoryItemCount("player", INVSLOT_AMMO)
+ end
+
+ if (itemID and itemID > 0) and (count and count > 0) then
+ if itemID then
+ name = itemName[itemID] or GetItemInfo(itemID)
+ end
+ if name and not itemName[itemID] then
+ itemName[itemID] = name
+ end
+ self.text:SetFormattedText(displayString, name or INVTYPE_AMMO, count or 0)
+ else
+ self.text:SetFormattedText(displayString, INVTYPE_AMMO, 0)
+ end
+ end
+
+ if not name then
+ waitingItemID = itemID
+ self:RegisterEvent("GET_ITEM_INFO_RECEIVED")
+ end
+end
+
+local itemCount = {}
+local totalItemCount = 0
+local function OnEnter(self)
+ DT:SetupTooltip(self)
+
+ DT.tooltip:ClearLines()
+
+ if E.myclass == "HUNTER" or E.myclass == "ROGUE" or E.myclass == "WARRIOR" then
+ wipe(itemCount)
+ totalItemCount = 0
+ DT.tooltip:AddLine(INVTYPE_AMMO)
+
+ for containerIndex = 0, NUM_BAG_FRAMES do
+ for slotIndex = 1, GetContainerNumSlots(containerIndex) do
+ local texture, count, _, _, _, _, link = GetContainerItemInfo(containerIndex, slotIndex)
+ if link then
+ local name, _, quality, _, _, _, _, _, equipLoc = GetItemInfo(link)
+ local itemID = strmatch(link, "item:(%d+)")
+ if equipLoc == "INVTYPE_AMMO" or equipLoc == "INVTYPE_THROWN" then
+ if not itemCount[itemID] then
+ DT.tooltip:AddDoubleLine(join("", format(iconString, texture), " ", name), count or 0, GetItemQualityColor(quality or 1))
+ itemCount[itemID] = count or 0
+ totalItemCount = totalItemCount + 1
+ end
+ end
+ end
+ end
+ end
+
+ if totalItemCount == 0 then
+ DT.tooltip:AddLine(NOT_APPLICABLE)
+ end
+
+ local itemID = GetInventoryItemID("player", 18) -- оружие дальнего боя
+ if itemID then
+ local name, _, quality, _, _, _, _, _, equipLoc, texture = GetItemInfo(itemID)
+ local count = GetItemCount(itemID)
+ itemCount[itemID] = count
+ if equipLoc == "INVTYPE_RANGED" or equipLoc == "INVTYPE_THROWN" then
+ DT.tooltip:AddLine(" ")
+ DT.tooltip:AddLine(CURRENTLY_EQUIPPED)
+ DT.tooltip:AddDoubleLine(join("", format(iconString, texture), " ", name), count, GetItemQualityColor(quality or 1))
+ end
+ end
+ end
+
+ for i = 1, NUM_BAG_SLOTS do
+ local itemID = GetInventoryItemID("player", ContainerIDToInventoryID(i))
+ if itemID then
+ local name, _, quality, _, _, itemType, itemSubType, _, _, texture = GetItemInfo(itemID)
+ if (itemSubType == QUIVER or itemSubType == POUCH or itemSubType == SOULBAG) or (itemType == "Container" and (itemSubType == QUIVER or itemSubType == POUCH or itemSubType == SOULBAG)) then
+ local free, total = GetContainerNumFreeSlots(i), GetContainerNumSlots(i)
+ local used = total - free
+
+ DT.tooltip:AddLine(itemSubType)
+ DT.tooltip:AddDoubleLine(join("", format(iconString, texture), " ", name), format("%d / %d", used, total), GetItemQualityColor(quality or 1))
+ end
+ end
+ end
+
+ DT.tooltip:Show()
+end
+
+local function OnClick(_, btn)
+ if btn == "LeftButton" then
+ if not E.private.bags.enable then
+ for i = 1, NUM_BAG_SLOTS do
+ local itemID = GetInventoryItemID("player", ContainerIDToInventoryID(i))
+ if itemID then
+ local itemType, itemSubType = select(6, GetItemInfo(itemID))
+ if (itemSubType == QUIVER or itemSubType == POUCH or itemSubType == SOULBAG) or (itemType == "Container" and (itemSubType == QUIVER or itemSubType == POUCH or itemSubType == SOULBAG)) then
+ _G.ToggleBag(i)
+ end
+ end
+ end
+ else
+ _G.ToggleAllBags()
+ end
+ end
+end
+
+local function ValueColorUpdate(hex)
+ displayString = join("", "%s: ", hex, "%d|r")
+end
+E.valueColorUpdateFuncs[ValueColorUpdate] = true
+
+DT:RegisterDatatext("Ammo", {"BAG_UPDATE", "UNIT_INVENTORY_CHANGED"}, OnEvent, nil, OnClick, OnEnter, nil, L["Ammo/Shard Counter"])
diff --git a/Modules/DataTexts/CallToArms.lua b/Modules/DataTexts/CallToArms.lua
new file mode 100644
index 000000000..6ef1a1936
--- /dev/null
+++ b/Modules/DataTexts/CallToArms.lua
@@ -0,0 +1,125 @@
+local E, L, V, P, G = unpack(select(2, ...)) --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
+local DT = E:GetModule("DataTexts")
+
+--Lua functions
+local _G = _G
+local format, join = string.format, string.join
+--WoW API / Variables
+local GetLFGRandomDungeonInfo = GetLFGRandomDungeonInfo
+local GetLFGDungeonRewards = GetLFGDungeonRewards
+local GetNumRandomDungeons = GetNumRandomDungeons
+local ToggleFrame = ToggleFrame
+
+local BATTLEGROUND_HOLIDAY = BATTLEGROUND_HOLIDAY
+local DUNGEONS = DUNGEONS
+local NOT_APPLICABLE = NOT_APPLICABLE
+
+local function RoleIcon(role)
+ local left, right, top, bottom = GetTexCoordsForRoleSmallCircle(role)
+ return format("|TInterface\\LFGFrame\\UI-LFG-ICON-PORTRAITROLES:14:14:0:0:64:64:%s:%s:%s:%s|t", left, right, top, bottom)
+end
+
+local TANK_ICON = RoleIcon("TANK")
+local HEALER_ICON = RoleIcon("HEALER")
+local DPS_ICON = RoleIcon("DAMAGER")
+
+local displayString = ""
+local enteredFrame = false
+
+local function MakeIconString(tank, healer, damage)
+ local str = ""
+ if tank then
+ str = str..TANK_ICON
+ end
+ if healer then
+ str = str..HEALER_ICON
+ end
+ if damage then
+ str = str..DPS_ICON
+ end
+
+ return str
+end
+
+local function OnEvent(self)
+ local tankReward = false
+ local healerReward = false
+ local dpsReward = false
+ local unavailable = true
+
+ for i = 1, GetNumRandomDungeons() do
+ local id = GetLFGRandomDungeonInfo(i)
+ local eligible, forTank, forHealer, forDamage, itemCount = GetLFGDungeonRewards(id)
+ if eligible and forTank and itemCount > 0 then tankReward = true; unavailable = false end
+ if eligible and forHealer and itemCount > 0 then healerReward = true; unavailable = false end
+ if eligible and forDamage and itemCount > 0 then dpsReward = true; unavailable = false end
+ end
+
+ local stat = unavailable and NOT_APPLICABLE or MakeIconString(tankReward, healerReward, dpsReward)
+ self.text:SetFormattedText(displayString, BATTLEGROUND_HOLIDAY..": ", stat)
+end
+
+local function OnClick()
+ ToggleFrame(_G.LFDParentFrame)
+end
+
+local function OnEnter(self)
+ DT:SetupTooltip(self)
+ enteredFrame = true
+
+ local numCTA = 0
+ local addTooltipHeader = true
+ for i = 1, GetNumRandomDungeons() do
+ local id, name = GetLFGRandomDungeonInfo(i)
+ local tankReward = false
+ local healerReward = false
+ local dpsReward = false
+ local unavailable = true
+
+ local eligible, forTank, forHealer, forDamage, itemCount = GetLFGDungeonRewards(id)
+ if eligible then unavailable = false end
+ if eligible and forTank and itemCount > 0 then tankReward = true end
+ if eligible and forHealer and itemCount > 0 then healerReward = true end
+ if eligible and forDamage and itemCount > 0 then dpsReward = true end
+
+ if not unavailable then
+ local rolesString = MakeIconString(tankReward, healerReward, dpsReward)
+ if rolesString ~= "" then
+ if addTooltipHeader then
+ DT.tooltip:AddLine(DUNGEONS)
+ addTooltipHeader = false
+ end
+ DT.tooltip:AddDoubleLine(name..":", rolesString, 1, 1, 1)
+ end
+ if tankReward or healerReward or dpsReward then numCTA = numCTA + 1 end
+ end
+ end
+
+ DT.tooltip:Show()
+end
+
+local updateInterval = 10
+local function Update(self, elapsed)
+ if self.timeSinceUpdate and self.timeSinceUpdate > updateInterval then
+ OnEvent(self)
+
+ if enteredFrame then
+ OnEnter(self)
+ end
+
+ self.timeSinceUpdate = 0
+ else
+ self.timeSinceUpdate = (self.timeSinceUpdate or 0) + elapsed
+ end
+end
+
+local function OnLeave()
+ enteredFrame = false
+end
+
+local function ValueColorUpdate(hex)
+ displayString = join("", "%s", hex, "%s|r")
+end
+E.valueColorUpdateFuncs[ValueColorUpdate] = true
+
+DT:RegisterDatatext("CallToArms", {"LFG_UPDATE", "LFG_QUEUE_STATUS_UPDATE", "LFG_PROPOSAL_UPDATE", "LFG_PROPOSAL_SHOW", "LFG_PROPOSAL_FAILED", "LFG_PROPOSAL_SUCCEEDED", "LFG_ROLE_CHECK_SHOW", "LFG_ROLE_CHECK_HIDE", "LFG_BOOT_PROPOSAL_UPDATE", "LFG_ROLE_UPDATE", "LFG_UPDATE_RANDOM_INFO"}, OnEvent, Update, OnClick, OnEnter, OnLeave, BATTLEGROUND_HOLIDAY)
diff --git a/Modules/DataTexts/CombatIndicator.lua b/Modules/DataTexts/CombatIndicator.lua
new file mode 100644
index 000000000..ca395675e
--- /dev/null
+++ b/Modules/DataTexts/CombatIndicator.lua
@@ -0,0 +1,25 @@
+local E, L, V, P, G = unpack(select(2, ...)) --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
+local DT = E:GetModule("DataTexts")
+
+--Lua functions
+
+local inCombat, outOfCombat = "", ""
+
+local function OnEvent(self, event)
+ if event == "PLAYER_REGEN_DISABLED" then
+ self.text:SetText(inCombat)
+ else
+ self.text:SetText(outOfCombat)
+ end
+end
+
+local function ValueColorUpdate()
+ -- строки с фиксированными цветами, как в стандартном ElvUI (зеленый/красный)
+ inCombat = E:RGBToHex(1, 0.13, 0.13)..L["In Combat"].."|r"
+ outOfCombat = E:RGBToHex(0.2, 1, 0.2)..L["Out of Combat"].."|r"
+end
+E.valueColorUpdateFuncs[ValueColorUpdate] = true
+
+ValueColorUpdate()
+
+DT:RegisterDatatext("CombatIndicator", {"PLAYER_REGEN_DISABLED", "PLAYER_REGEN_ENABLED"}, OnEvent, nil, nil, nil, nil, L["Combat Indicator"])
diff --git a/Modules/DataTexts/Currencies.lua b/Modules/DataTexts/Currencies.lua
new file mode 100644
index 000000000..3cb243549
--- /dev/null
+++ b/Modules/DataTexts/Currencies.lua
@@ -0,0 +1,53 @@
+local E, L, V, P, G = unpack(select(2, ...)) --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
+local DT = E:GetModule("DataTexts")
+
+--Lua functions
+local _G = _G
+local format = string.format
+--WoW API / Variables
+local GetMoney = GetMoney
+local GetCurrencyListSize = GetCurrencyListSize
+local GetCurrencyListInfo = GetCurrencyListInfo
+local ToggleCharacter = ToggleCharacter
+
+local iconString = "|T%s:20:20:0:0:64:64:4:60:4:60|t"
+local goldText = ""
+local lastPanel
+
+local function OnClick()
+ ToggleCharacter("TokenFrame")
+end
+
+local function OnEvent(self)
+ lastPanel = self
+
+ goldText = E:FormatMoney(GetMoney(), "BLIZZARD", true)
+ self.text:SetText(goldText)
+end
+
+local function OnEnter(self)
+ DT:SetupTooltip(self)
+
+ DT.tooltip:ClearLines()
+
+ local numCurrency = GetCurrencyListSize()
+ for i = 1, numCurrency do
+ local name, isHeader, _, _, _, count, _, icon = GetCurrencyListInfo(i)
+ if not isHeader and name and count and count > 0 then
+ DT.tooltip:AddDoubleLine(format("%s %s", format(iconString, icon or ""), name), E:ShortValue(count), 1, 1, 1)
+ end
+ end
+
+ DT.tooltip:AddLine(" ")
+ DT.tooltip:AddDoubleLine(L["Gold"]..":", goldText, nil, nil, nil, 1, 1, 1)
+ DT.tooltip:Show()
+end
+
+local function ValueColorUpdate()
+ if lastPanel ~= nil then
+ OnEvent(lastPanel)
+ end
+end
+E.valueColorUpdateFuncs[ValueColorUpdate] = true
+
+DT:RegisterDatatext("Currencies", {"PLAYER_MONEY", "SEND_MAIL_MONEY_CHANGED", "SEND_MAIL_COD_CHANGED", "PLAYER_TRADE_MONEY", "TRADE_MONEY_CHANGED", "CURRENCY_DISPLAY_UPDATE"}, OnEvent, nil, OnClick, OnEnter, nil, _G.CURRENCY)
diff --git a/Modules/DataTexts/Date.lua b/Modules/DataTexts/Date.lua
new file mode 100644
index 000000000..0a321829b
--- /dev/null
+++ b/Modules/DataTexts/Date.lua
@@ -0,0 +1,36 @@
+local E, L, V, P, G = unpack(select(2, ...)) --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
+local DT = E:GetModule("DataTexts")
+
+--Lua functions
+local _G = _G
+local date = date
+--WoW API / Variables
+local FormatShortDate = FormatShortDate
+
+local displayString = "%s"
+local lastPanel
+
+local function OnClick()
+ if InCombatLockdown() then E:Print(ERR_NOT_IN_COMBAT) return end
+
+ _G.GameTimeFrame:Click()
+end
+
+local function OnEvent(self)
+ lastPanel = self
+
+ local dateTable = date("*t")
+
+ self.text:SetText(FormatShortDate(dateTable.day, dateTable.month, dateTable.year):gsub("([/.])", displayString))
+end
+
+local function ValueColorUpdate(hex)
+ displayString = hex.."%1|r"
+
+ if lastPanel ~= nil then
+ OnEvent(lastPanel)
+ end
+end
+E.valueColorUpdateFuncs[ValueColorUpdate] = true
+
+DT:RegisterDatatext("Date", {"UPDATE_INSTANCE_INFO"}, OnEvent, nil, OnClick)
diff --git a/Modules/DataTexts/Defense.lua b/Modules/DataTexts/Defense.lua
new file mode 100644
index 000000000..bd31f1efc
--- /dev/null
+++ b/Modules/DataTexts/Defense.lua
@@ -0,0 +1,29 @@
+local E, L, V, P, G = unpack(select(2, ...)) --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
+local DT = E:GetModule("DataTexts")
+
+--Lua functions
+local join = string.join
+--WoW API / Variables
+local UnitDefense = UnitDefense
+
+local DEFENSE = DEFENSE
+
+local displayString = ""
+local lastPanel
+
+local function OnEvent(self)
+ lastPanel = self
+
+ self.text:SetFormattedText(displayString, UnitDefense("player"))
+end
+
+local function ValueColorUpdate(hex)
+ displayString = join("", DEFENSE, ": ", hex, "%.f|r")
+
+ if lastPanel ~= nil then
+ OnEvent(lastPanel)
+ end
+end
+E.valueColorUpdateFuncs[ValueColorUpdate] = true
+
+DT:RegisterDatatext("Defense", {"UNIT_STATS", "UNIT_AURA", "SKILL_LINES_CHANGED"}, OnEvent, nil, nil, nil, nil, DEFENSE)
diff --git a/Modules/DataTexts/Difficulty.lua b/Modules/DataTexts/Difficulty.lua
new file mode 100644
index 000000000..750f4b9fd
--- /dev/null
+++ b/Modules/DataTexts/Difficulty.lua
@@ -0,0 +1,78 @@
+local E, L, V, P, G = unpack(select(2, ...)) --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
+local DT = E:GetModule("DataTexts")
+
+--Lua functions
+local _G = _G
+--WoW API / Variables
+local GetDungeonDifficulty = GetDungeonDifficulty
+local GetRaidDifficulty = GetRaidDifficulty
+local SetDungeonDifficulty = SetDungeonDifficulty
+local SetRaidDifficulty = SetRaidDifficulty
+local GetInstanceInfo = GetInstanceInfo
+local GetZoneText = GetZoneText
+local ResetInstances = ResetInstances
+local CreateFrame = CreateFrame
+
+local heroicTex = [[|Tinterface\lfgframe\ui-lfg-icon-heroic:20:20:0:0:64:64:0:36:0:36|t]]
+local dungTex = [[|Tinterface\icons\spell_arcane_teleportstormwind:20:20:0:0:64:64:4:60:4:60|t]]
+local raidTex = [[|Tinterface\icons\spell_arcane_teleportshattrath:20:20:0:0:64:64:4:60:4:60|t]]
+
+local dropdown = CreateFrame("Frame", "ElvUI_DifficultyDropDown", E.UIParent)
+
+local lastPanel
+
+local Refresh
+
+local function GetDifficultyText(isRaid)
+ local difficulty = isRaid and GetRaidDifficulty() or GetDungeonDifficulty()
+ return _G["PLAYER_DIFFICULTY"..difficulty] or ""
+end
+
+local RightClickMenu = {
+ { text = _G.DUNGEON_DIFFICULTY.." - ".._G.PLAYER_DIFFICULTY1, func = function() SetDungeonDifficulty(1) Refresh() end },
+ { text = _G.DUNGEON_DIFFICULTY.." - ".._G.PLAYER_DIFFICULTY2, func = function() SetDungeonDifficulty(2) Refresh() end },
+ { text = _G.DUNGEON_DIFFICULTY.." - ".._G.PLAYER_DIFFICULTY3, func = function() SetDungeonDifficulty(3) Refresh() end },
+ { text = "" },
+ { text = _G.RAID_DIFFICULTY.." - ".._G.PLAYER_DIFFICULTY1, func = function() SetRaidDifficulty(1) Refresh() end },
+ { text = _G.RAID_DIFFICULTY.." - ".._G.PLAYER_DIFFICULTY2, func = function() SetRaidDifficulty(2) Refresh() end },
+ { text = _G.RAID_DIFFICULTY.." - ".._G.PLAYER_DIFFICULTY3, func = function() SetRaidDifficulty(3) Refresh() end },
+ { text = "" },
+ { text = _G.RESET_INSTANCES, func = function() ResetInstances() end },
+}
+
+local function OnEvent(self)
+ lastPanel = self
+
+ local _, instanceType, difficultyID, _, maxPlayers = GetInstanceInfo()
+
+ if instanceType == "none" then
+ self.text:SetFormattedText("%s %s %s %s", dungTex, GetDifficultyText(false), raidTex, GetDifficultyText(true))
+ else
+ self.text:SetFormattedText("%s: %s %s %s", GetZoneText(), maxPlayers, _G.PLAYER, difficultyID > 1 and heroicTex or "")
+ end
+end
+
+Refresh = function()
+ if lastPanel then
+ OnEvent(lastPanel)
+ end
+end
+
+local function OnClick(self, button)
+ if button == "RightButton" then
+ E:DropDown(RightClickMenu, dropdown)
+ end
+end
+
+local function OnEnter(self)
+ DT:SetupTooltip(self)
+
+ DT.tooltip:AddLine(L["Current Difficulty"])
+ DT.tooltip:AddLine(" ")
+ DT.tooltip:AddDoubleLine(_G.DUNGEON_DIFFICULTY, GetDifficultyText(false), 1, 1, 1)
+ DT.tooltip:AddDoubleLine(_G.RAID_DIFFICULTY, GetDifficultyText(true), 1, 1, 1)
+
+ DT.tooltip:Show()
+end
+
+DT:RegisterDatatext("Difficulty", {"CHAT_MSG_SYSTEM", "ZONE_CHANGED", "ZONE_CHANGED_INDOORS", "ZONE_CHANGED_NEW_AREA", "PLAYER_ENTERING_WORLD"}, OnEvent, nil, OnClick, OnEnter, nil, "Difficulty")
diff --git a/Modules/DataTexts/Dodge.lua b/Modules/DataTexts/Dodge.lua
new file mode 100644
index 000000000..1a72447e1
--- /dev/null
+++ b/Modules/DataTexts/Dodge.lua
@@ -0,0 +1,29 @@
+local E, L, V, P, G = unpack(select(2, ...)) --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
+local DT = E:GetModule("DataTexts")
+
+--Lua functions
+local join = string.join
+--WoW API / Variables
+local GetDodgeChance = GetDodgeChance
+
+local DODGE = DODGE
+
+local displayString = ""
+local lastPanel
+
+local function OnEvent(self)
+ lastPanel = self
+
+ self.text:SetFormattedText(displayString, GetDodgeChance())
+end
+
+local function ValueColorUpdate(hex)
+ displayString = join("", DODGE, ": ", hex, "%.2f%%|r")
+
+ if lastPanel ~= nil then
+ OnEvent(lastPanel)
+ end
+end
+E.valueColorUpdateFuncs[ValueColorUpdate] = true
+
+DT:RegisterDatatext("Dodge", {"UNIT_STATS", "UNIT_AURA", "SKILL_LINES_CHANGED"}, OnEvent, nil, nil, nil, nil, DODGE)
diff --git a/Modules/DataTexts/EnergyRegen.lua b/Modules/DataTexts/EnergyRegen.lua
new file mode 100644
index 000000000..8acc6474c
--- /dev/null
+++ b/Modules/DataTexts/EnergyRegen.lua
@@ -0,0 +1,27 @@
+local E, L, V, P, G = unpack(select(2, ...)) --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
+local DT = E:GetModule("DataTexts")
+
+--Lua functions
+local join = string.join
+--WoW API / Variables
+local GetPowerRegen = GetPowerRegen
+
+local displayString = ""
+local lastPanel
+
+local function OnEvent(self)
+ lastPanel = self
+
+ self.text:SetFormattedText(displayString, GetPowerRegen())
+end
+
+local function ValueColorUpdate(hex)
+ displayString = join("", L["Energy Regen"], ": ", hex, "%.1f|r")
+
+ if lastPanel ~= nil then
+ OnEvent(lastPanel)
+ end
+end
+E.valueColorUpdateFuncs[ValueColorUpdate] = true
+
+DT:RegisterDatatext("EnergyRegen", {"UNIT_STATS", "UNIT_AURA", "PLAYER_REGEN_DISABLED", "PLAYER_REGEN_ENABLED"}, OnEvent, nil, nil, nil, nil, L["Energy Regen"])
diff --git a/Modules/DataTexts/EquipmentSets.lua b/Modules/DataTexts/EquipmentSets.lua
new file mode 100644
index 000000000..e1de52fb5
--- /dev/null
+++ b/Modules/DataTexts/EquipmentSets.lua
@@ -0,0 +1,95 @@
+local E, L, V, P, G = unpack(select(2, ...)) --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
+local DT = E:GetModule("DataTexts")
+
+--Lua functions
+local format = string.format
+local tinsert = table.insert
+local pairs = pairs
+local wipe = table.wipe
+--WoW API / Variables
+local GetNumEquipmentSets = GetNumEquipmentSets
+local GetEquipmentSetInfo = GetEquipmentSetInfo
+local GetEquipmentSetItemIDs = GetEquipmentSetItemIDs
+local GetInventoryItemID = GetInventoryItemID
+local UseEquipmentSet = UseEquipmentSet
+local CreateFrame = CreateFrame
+
+local eqSets = {}
+local displayString = ""
+local hexColor = ""
+local lastPanel
+
+local dropdown = CreateFrame("Frame", "ElvUI_EquipmentSetsDropDown", E.UIParent)
+
+local function OnEnter(self)
+ DT:SetupTooltip(self)
+
+ DT.tooltip:AddLine(L["Equipment Sets"])
+ DT.tooltip:AddLine(" ")
+
+ for _, set in pairs(eqSets) do
+ DT.tooltip:AddLine(set.text, set.isEquipped and .2 or 1, set.isEquipped and 1 or .2, .2)
+ end
+
+ DT.tooltip:Show()
+end
+
+local function OnClick(self, button)
+ if button == "LeftButton" then
+ E:DropDown(eqSets, dropdown)
+ end
+end
+
+local function OnEvent(self, event)
+ lastPanel = self
+
+ if event == "ELVUI_FORCE_UPDATE" or event == "ELVUI_FORCE_RUN" or event == "EQUIPMENT_SETS_CHANGED" or event == "PLAYER_EQUIPMENT_CHANGED" then
+ wipe(eqSets)
+ end
+
+ local numSets = GetNumEquipmentSets()
+ local activeSetIndex
+ for i = 1, numSets do
+ local name, iconFileID = GetEquipmentSetInfo(i)
+ local items = GetEquipmentSetItemIDs(name)
+ local isEquipped = true
+
+ for slot, itemID in pairs(items) do
+ if itemID then
+ local equippedItemID = GetInventoryItemID("player", slot)
+ equippedItemID = equippedItemID == nil and 0 or equippedItemID
+ if equippedItemID ~= itemID then
+ isEquipped = false
+ break
+ end
+ end
+ end
+
+ if event == "ELVUI_FORCE_UPDATE" or event == "ELVUI_FORCE_RUN" or event == "EQUIPMENT_SETS_CHANGED" or event == "PLAYER_EQUIPMENT_CHANGED" then
+ tinsert(eqSets, { text = format("|T%s:20:20:0:0:64:64:4:60:4:60|t %s", iconFileID, name), func = function() UseEquipmentSet(name) end, isEquipped = isEquipped })
+ end
+
+ if isEquipped then
+ activeSetIndex = i
+ end
+ end
+
+ local set = eqSets[activeSetIndex]
+ if not activeSetIndex then
+ self.text:SetText(L["No Set Equipped"])
+ elseif set then
+ self.text:SetFormattedText(displayString, set.text)
+ end
+end
+
+local function ValueColorUpdate(hex)
+ hexColor = hex
+ displayString = hexColor.."%s|r"
+
+ if lastPanel ~= nil then
+ OnEvent(lastPanel, "ELVUI_COLOR_UPDATE")
+ end
+end
+E.valueColorUpdateFuncs[ValueColorUpdate] = true
+
+DT:RegisterDatatext("Equipment Sets", {"EQUIPMENT_SETS_CHANGED", "PLAYER_EQUIPMENT_CHANGED", "EQUIPMENT_SWAP_FINISHED"}, OnEvent, nil, OnClick, OnEnter, nil, L["Equipment Sets"])
diff --git a/Modules/DataTexts/Experience.lua b/Modules/DataTexts/Experience.lua
new file mode 100644
index 000000000..268e6a09b
--- /dev/null
+++ b/Modules/DataTexts/Experience.lua
@@ -0,0 +1,59 @@
+local E, L, V, P, G = unpack(select(2, ...)) --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
+local DT = E:GetModule("DataTexts")
+
+--Lua functions
+local _G = _G
+local format = string.format
+--WoW API / Variables
+local UnitXP = UnitXP
+local UnitXPMax = UnitXPMax
+local GetXPExhaustion = GetXPExhaustion
+
+local CurrentXP, XPToLevel, RestedXP, PercentRested
+local PercentXP, RemainXP, RemainTotal, RemainBars
+local displayString = ""
+
+local function OnEvent(self)
+ if E.mylevel >= MAX_PLAYER_LEVEL then
+ self.text:SetText(L["Max Level"])
+ return
+ end
+
+ CurrentXP, XPToLevel, RestedXP = UnitXP("player"), UnitXPMax("player"), GetXPExhaustion()
+
+ local remainXP = XPToLevel - CurrentXP
+ local remainPercent = E:Round(remainXP / XPToLevel, 4)
+
+ -- эти значения также используются в OnEnter
+ RemainTotal, RemainBars = remainPercent * 100, remainPercent * 20
+ PercentXP, RemainXP = E:Round(CurrentXP / XPToLevel, 4) * 100, E:ShortValue(remainXP)
+
+ displayString = format("%s - %.2f%%", E:ShortValue(CurrentXP), PercentXP)
+
+ if RestedXP and RestedXP > 0 then
+ PercentRested = E:Round(RestedXP / XPToLevel, 4) * 100
+ displayString = displayString..format(" R:%s [%.2f%%]", E:ShortValue(RestedXP), PercentRested)
+ end
+
+ self.text:SetText(displayString)
+end
+
+local function OnEnter(self)
+ if E.mylevel >= MAX_PLAYER_LEVEL then return end
+
+ DT:SetupTooltip(self)
+
+ DT.tooltip:AddDoubleLine(L["Experience"], format("%s %d", L["Level"], E.mylevel))
+ DT.tooltip:AddLine(" ")
+
+ DT.tooltip:AddDoubleLine(L["XP:"], format(" %s / %s (%.2f%%)", E:ShortValue(CurrentXP), E:ShortValue(XPToLevel), PercentXP), 1, 1, 1)
+ DT.tooltip:AddDoubleLine(L["Remaining:"], format(" %s (%.2f%% - %.2f "..L["Bars"]..")", RemainXP, RemainTotal, RemainBars), 1, 1, 1)
+
+ if RestedXP and RestedXP > 0 then
+ DT.tooltip:AddDoubleLine(L["Rested:"], format("+%s (%.2f%%)", E:ShortValue(RestedXP), PercentRested), 1, 1, 1)
+ end
+
+ DT.tooltip:Show()
+end
+
+DT:RegisterDatatext("Experience", {"PLAYER_XP_UPDATE", "DISABLE_XP_GAIN", "ENABLE_XP_GAIN", "UPDATE_EXHAUSTION", "PLAYER_LEVEL_UP", "PLAYER_ENTERING_WORLD"}, OnEvent, nil, nil, OnEnter, nil, _G.COMBAT_XP_GAIN)
diff --git a/Modules/DataTexts/HealPower.lua b/Modules/DataTexts/HealPower.lua
new file mode 100644
index 000000000..bdcb7774c
--- /dev/null
+++ b/Modules/DataTexts/HealPower.lua
@@ -0,0 +1,27 @@
+local E, L, V, P, G = unpack(select(2, ...)) --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
+local DT = E:GetModule("DataTexts")
+
+--Lua functions
+local join = string.join
+--WoW API / Variables
+local GetSpellBonusHealing = GetSpellBonusHealing
+
+local displayString = ""
+local lastPanel
+
+local function OnEvent(self)
+ lastPanel = self
+
+ self.text:SetFormattedText(displayString, L["HP"], GetSpellBonusHealing())
+end
+
+local function ValueColorUpdate(hex)
+ displayString = join("", "%s: ", hex, "%d|r")
+
+ if lastPanel ~= nil then
+ OnEvent(lastPanel)
+ end
+end
+E.valueColorUpdateFuncs[ValueColorUpdate] = true
+
+DT:RegisterDatatext("HealPower", {"UNIT_STATS", "UNIT_AURA"}, OnEvent, nil, nil, nil, nil, L["Heal Power"])
diff --git a/Modules/DataTexts/Intellect.lua b/Modules/DataTexts/Intellect.lua
new file mode 100644
index 000000000..166aa9029
--- /dev/null
+++ b/Modules/DataTexts/Intellect.lua
@@ -0,0 +1,29 @@
+local E, L, V, P, G = unpack(select(2, ...)) --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
+local DT = E:GetModule("DataTexts")
+
+--Lua functions
+local join = string.join
+--WoW API / Variables
+local UnitStat = UnitStat
+
+local ITEM_MOD_INTELLECT_SHORT = ITEM_MOD_INTELLECT_SHORT
+
+local displayString = ""
+local lastPanel
+
+local function OnEvent(self)
+ lastPanel = self
+
+ self.text:SetFormattedText(displayString, UnitStat("player", 4))
+end
+
+local function ValueColorUpdate(hex)
+ displayString = join("", ITEM_MOD_INTELLECT_SHORT, ": ", hex, "%.f|r")
+
+ if lastPanel ~= nil then
+ OnEvent(lastPanel)
+ end
+end
+E.valueColorUpdateFuncs[ValueColorUpdate] = true
+
+DT:RegisterDatatext("Intellect", {"UNIT_STATS", "UNIT_AURA"}, OnEvent, nil, nil, nil, nil, ITEM_MOD_INTELLECT_SHORT)
diff --git a/Modules/DataTexts/ItemLevel.lua b/Modules/DataTexts/ItemLevel.lua
new file mode 100644
index 000000000..fee087100
--- /dev/null
+++ b/Modules/DataTexts/ItemLevel.lua
@@ -0,0 +1,60 @@
+local E, L, V, P, G = unpack(select(2, ...)) --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
+local DT = E:GetModule("DataTexts")
+
+--Lua functions
+local ipairs = ipairs
+local format = string.format
+--WoW API / Variables
+local GetInventoryItemLink = GetInventoryItemLink
+local GetInventoryItemTexture = GetInventoryItemTexture
+local GetAverageItemLevel = GetAverageItemLevel
+local GetItemLevelColor = GetItemLevelColor
+local GetItemInfo = GetItemInfo
+local GetItemQualityColor = GetItemQualityColor
+
+local displayString = ""
+local iconString = "|T%s:24:24:0:0:50:50:4:46:4:46|t %s"
+local slotID = { 1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 }
+local r, g, b, avg = 1, 1, 1, 0
+local lastPanel
+
+local function OnEvent(self)
+ lastPanel = self
+
+ avg = GetAverageItemLevel()
+ r, g, b = GetItemLevelColor(avg):GetRGB()
+
+ self.text:SetFormattedText(displayString, avg or 0)
+end
+
+local function OnEnter(self)
+ DT:SetupTooltip(self)
+
+ DT.tooltip:AddDoubleLine(L["Item Level"], format("%0.2f", avg), 1, 1, 1, r, g, b)
+ DT.tooltip:AddLine(" ")
+
+ for _, k in ipairs(slotID) do
+ local link = GetInventoryItemLink("player", k)
+ if link then
+ local _, _, rarity, ilvl = GetItemInfo(link)
+ if ilvl then
+ local icon = GetInventoryItemTexture("player", k)
+ local slotR, slotG, slotB = GetItemQualityColor(rarity or 1)
+ DT.tooltip:AddDoubleLine(format(iconString, icon, link), ilvl, 1, 1, 1, slotR, slotG, slotB)
+ end
+ end
+ end
+
+ DT.tooltip:Show()
+end
+
+local function ValueColorUpdate(hex)
+ displayString = format("|cFFFFFFFF%s|r: %s%%s|r", L["iLvL"], hex)
+
+ if lastPanel ~= nil then
+ OnEvent(lastPanel)
+ end
+end
+E.valueColorUpdateFuncs[ValueColorUpdate] = true
+
+DT:RegisterDatatext("Item Level", {"UNIT_INVENTORY_CHANGED", "PLAYER_EQUIPMENT_CHANGED"}, OnEvent, nil, nil, OnEnter, nil, L["Item Level"])
diff --git a/Modules/DataTexts/Load_DataTexts.xml b/Modules/DataTexts/Load_DataTexts.xml
index 0bd231743..e3a20af62 100644
--- a/Modules/DataTexts/Load_DataTexts.xml
+++ b/Modules/DataTexts/Load_DataTexts.xml
@@ -37,4 +37,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Modules/DataTexts/Location.lua b/Modules/DataTexts/Location.lua
new file mode 100644
index 000000000..3cb42b6e7
--- /dev/null
+++ b/Modules/DataTexts/Location.lua
@@ -0,0 +1,58 @@
+local E, L, V, P, G = unpack(select(2, ...)) --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
+local DT = E:GetModule("DataTexts")
+
+--Lua functions
+local _G = _G
+--WoW API / Variables
+local GetZonePVPInfo = GetZonePVPInfo
+local GetSubZoneText = GetSubZoneText
+local GetCurrentMapContinent = GetCurrentMapContinent
+local GetMapContinents = GetMapContinents
+local GetZoneText = GetZoneText
+local IsInInstance = IsInInstance
+local ToggleFrame = ToggleFrame
+
+local NOT_APPLICABLE = NOT_APPLICABLE
+
+local colors = { -- цвета взяты из ZoneText.lua Близзард
+ none = {r = 1, g = 1, b = 0},
+ arena = {r = 1.0, g = 0.1, b = 0.1},
+ combat = {r = 1.0, g = 0.1, b = 0.1},
+ contested = {r = 1.0, g = 0.7, b = 0.1},
+ friendly = {r = 0.1, g = 1.0, b = 0.1},
+ hostile = {r = 1.0, g = 0.1, b = 0.1},
+ instance = {r = 1.0, g = 0.1, b = 0.1},
+ sanctuary = {r = 0.4, g = 0.8, b = 0.9},
+}
+
+local function GetStatus()
+ return IsInInstance() and colors.instance or colors[GetZonePVPInfo()] or colors.none
+end
+
+local function OnEvent(self)
+ local zone = GetZoneText()
+ local subZone = GetSubZoneText()
+ -- в WotLK имя континента берется из GetMapContinents() по индексу GetCurrentMapContinent()
+ -- (так же делает WorldMapFrame_LoadContinents). Индекс может быть 0/-1 (карта мира или инстанс)
+ local continentID = GetCurrentMapContinent()
+ local continent = (continentID and continentID > 0) and select(continentID, GetMapContinents()) or ""
+
+ if zone == "" and subZone == "" and continent == "" then
+ self.text:SetText(NOT_APPLICABLE)
+ return
+ end
+
+ local color = GetStatus()
+ local first = continent ~= "" and zone ~= "" and ": " or ""
+ local second = (zone ~= "" or continent ~= "") and subZone ~= "" and ": " or ""
+
+ self.text:SetFormattedText("%s%s%s%s%s%s|r", E:RGBToHex(color.r, color.g, color.b), continent, first, zone, second, subZone)
+end
+
+local function OnClick()
+ if InCombatLockdown() then E:Print(ERR_NOT_IN_COMBAT) return end
+
+ ToggleFrame(_G.WorldMapFrame)
+end
+
+DT:RegisterDatatext("Location", {"LOADING_SCREEN_DISABLED", "ZONE_CHANGED_NEW_AREA", "ZONE_CHANGED_INDOORS", "ZONE_CHANGED"}, OnEvent, nil, OnClick, nil, nil, L["Location"])
diff --git a/Modules/DataTexts/Mail.lua b/Modules/DataTexts/Mail.lua
new file mode 100644
index 000000000..fc9414627
--- /dev/null
+++ b/Modules/DataTexts/Mail.lua
@@ -0,0 +1,48 @@
+local E, L, V, P, G = unpack(select(2, ...)) --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
+local DT = E:GetModule("DataTexts")
+
+--Lua functions
+local next = next
+local pairs = pairs
+local join = string.join
+--WoW API / Variables
+local HasNewMail = HasNewMail
+local GetLatestThreeSenders = GetLatestThreeSenders
+local HAVE_MAIL_FROM = HAVE_MAIL_FROM
+local MAIL_LABEL = MAIL_LABEL
+
+local displayString = ""
+local lastPanel
+
+local function OnEvent(self)
+ lastPanel = self
+
+ self.text:SetFormattedText(displayString, HasNewMail() and L["New Mail"] or L["No Mail"])
+end
+
+local function OnEnter(self)
+ DT:SetupTooltip(self)
+
+ local senders = { GetLatestThreeSenders() }
+ if not next(senders) then return end
+
+ DT.tooltip:AddLine(HasNewMail() and HAVE_MAIL_FROM or MAIL_LABEL, 1, 1, 1)
+ DT.tooltip:AddLine(" ")
+
+ for _, sender in pairs(senders) do
+ DT.tooltip:AddLine(sender)
+ end
+
+ DT.tooltip:Show()
+end
+
+local function ValueColorUpdate(hex)
+ displayString = join("", hex, "%s|r")
+
+ if lastPanel ~= nil then
+ OnEvent(lastPanel)
+ end
+end
+E.valueColorUpdateFuncs[ValueColorUpdate] = true
+
+DT:RegisterDatatext("Mail", {"MAIL_INBOX_UPDATE", "UPDATE_PENDING_MAIL", "MAIL_CLOSED", "MAIL_SHOW"}, OnEvent, nil, nil, OnEnter, nil, MAIL_LABEL)
diff --git a/Modules/DataTexts/MicroBar.lua b/Modules/DataTexts/MicroBar.lua
new file mode 100644
index 000000000..009d444c4
--- /dev/null
+++ b/Modules/DataTexts/MicroBar.lua
@@ -0,0 +1,50 @@
+local E, L, V, P, G = unpack(select(2, ...)) --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
+local DT = E:GetModule("DataTexts")
+
+--Lua functions
+local _G = _G
+local join = string.join
+--WoW API / Variables
+local ToggleFrame = ToggleFrame
+local CreateFrame = CreateFrame
+
+local displayString = ""
+local lastPanel
+
+local dropdown = CreateFrame("Frame", "ElvUI_MicroBarDropDown", E.UIParent)
+
+local RightClickMenuList = {
+ { text = L["Character"], func = function() ToggleFrame(_G.CharacterFrame) end },
+ { text = L["Spellbook"], func = function() ToggleFrame(_G.SpellBookFrame) end },
+ { text = L["Talents"], func = function() ToggleFrame(_G.PlayerTalentFrame) end },
+ { text = L["Quest Log"], func = function() ToggleFrame(_G.QuestLogFrame) end },
+ { text = L["Social"], func = function() ToggleFrame(_G.CharacterFrame) end },
+ { text = L["World Map"], func = function() ToggleFrame(_G.WorldMapFrame) end },
+ { text = L["Help"], func = function() ToggleFrame(_G.HelpFrame) end },
+ { text = L["Game Menu"], func = function() ToggleFrame(_G.GameMenuFrame) end },
+}
+
+local function OnEvent(self)
+ lastPanel = self
+
+ self.text:SetFormattedText(displayString, L["Micro Bar"])
+end
+
+local function OnClick(self, button)
+ if button == "LeftButton" then
+ ToggleFrame(_G.GameMenuFrame)
+ else
+ E:DropDown(RightClickMenuList, dropdown)
+ end
+end
+
+local function ValueColorUpdate(hex)
+ displayString = join("", hex, "%s|r")
+
+ if lastPanel ~= nil then
+ OnEvent(lastPanel)
+ end
+end
+E.valueColorUpdateFuncs[ValueColorUpdate] = true
+
+DT:RegisterDatatext("Micro Bar", nil, OnEvent, nil, OnClick, nil, nil, L["Micro Bar"])
diff --git a/Modules/DataTexts/MovementSpeed.lua b/Modules/DataTexts/MovementSpeed.lua
new file mode 100644
index 000000000..c46ddd504
--- /dev/null
+++ b/Modules/DataTexts/MovementSpeed.lua
@@ -0,0 +1,53 @@
+local E, L, V, P, G = unpack(select(2, ...)) --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
+local DT = E:GetModule("DataTexts")
+
+--Lua functions
+local join = string.join
+--WoW API / Variables
+local IsFalling = IsFalling
+local GetUnitSpeed = GetUnitSpeed
+
+local BASE_MOVEMENT_SPEED = 7
+
+local displayString = ""
+local lastPanel
+local beforeFalling, wasFlying
+
+local function UpdateSpeed(self)
+ local unitSpeed = GetUnitSpeed("player")
+ local speed = unitSpeed
+ wasFlying = false
+
+ if IsFalling() and wasFlying and beforeFalling then
+ speed = beforeFalling
+ else
+ beforeFalling = speed
+ end
+
+ local percent = speed / BASE_MOVEMENT_SPEED * 100
+ self.text:SetFormattedText(displayString, percent)
+end
+
+local function OnUpdate(self, elapsed)
+ self.timeSinceLastUpdate = (self.timeSinceLastUpdate or 0) + elapsed
+ if self.timeSinceLastUpdate >= 1 then
+ UpdateSpeed(self)
+ self.timeSinceLastUpdate = 0
+ end
+end
+
+local function OnEvent(self)
+ lastPanel = self
+ self:SetScript("OnUpdate", OnUpdate)
+end
+
+local function ValueColorUpdate(hex)
+ displayString = join("", L["Mov. Speed"], ": ", hex, "%.0f%%|r")
+
+ if lastPanel ~= nil then
+ UpdateSpeed(lastPanel)
+ end
+end
+E.valueColorUpdateFuncs[ValueColorUpdate] = true
+
+DT:RegisterDatatext("MovementSpeed", {"UNIT_STATS", "UNIT_AURA", "UNIT_SPELL_HASTE"}, OnEvent, nil, nil, nil, nil, L["Movement Speed"])
diff --git a/Modules/DataTexts/Parry.lua b/Modules/DataTexts/Parry.lua
new file mode 100644
index 000000000..b648348cf
--- /dev/null
+++ b/Modules/DataTexts/Parry.lua
@@ -0,0 +1,29 @@
+local E, L, V, P, G = unpack(select(2, ...)) --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
+local DT = E:GetModule("DataTexts")
+
+--Lua functions
+local join = string.join
+--WoW API / Variables
+local GetParryChance = GetParryChance
+
+local PARRY = PARRY
+
+local displayString = ""
+local lastPanel
+
+local function OnEvent(self)
+ lastPanel = self
+
+ self.text:SetFormattedText(displayString, GetParryChance())
+end
+
+local function ValueColorUpdate(hex)
+ displayString = join("", PARRY, ": ", hex, "%.2f%%|r")
+
+ if lastPanel ~= nil then
+ OnEvent(lastPanel)
+ end
+end
+E.valueColorUpdateFuncs[ValueColorUpdate] = true
+
+DT:RegisterDatatext("Parry", {"UNIT_STATS", "UNIT_AURA", "SKILL_LINES_CHANGED"}, OnEvent, nil, nil, nil, nil, PARRY)
diff --git a/Modules/DataTexts/PrimaryStat.lua b/Modules/DataTexts/PrimaryStat.lua
new file mode 100644
index 000000000..7126266d1
--- /dev/null
+++ b/Modules/DataTexts/PrimaryStat.lua
@@ -0,0 +1,52 @@
+local E, L, V, P, G = unpack(select(2, ...)) --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
+local DT = E:GetModule("DataTexts")
+
+--Lua functions
+local _G = _G
+local join = string.join
+--WoW API / Variables
+local UnitStat = UnitStat
+
+local NOT_APPLICABLE = NOT_APPLICABLE
+
+-- индекс основной характеристики (LE_UNIT_STAT_*) по классу и ветке талантов; в 3.3.5a нет GetSpecializationInfo
+local primaryStatByClassTree = {
+ WARRIOR = {1, 1, 1}, -- Сила
+ PALADIN = {4, 1, 1}, -- Интеллект, Сила, Сила
+ HUNTER = {2, 2, 2}, -- Ловкость
+ ROGUE = {2, 2, 2}, -- Ловкость
+ PRIEST = {4, 4, 4}, -- Интеллект
+ DEATHKNIGHT = {1, 1, 1}, -- Сила
+ SHAMAN = {4, 2, 4}, -- Интеллект, Ловкость, Интеллект
+ MAGE = {4, 4, 4}, -- Интеллект
+ WARLOCK = {4, 4, 4}, -- Интеллект
+ DRUID = {4, 2, 4}, -- Интеллект, Ловкость, Интеллект
+}
+
+local displayString = ""
+local lastPanel
+
+local function OnEvent(self)
+ lastPanel = self
+
+ local specIndex = E:GetTalentSpecInfo()
+ local statID = specIndex and primaryStatByClassTree[E.myclass] and primaryStatByClassTree[E.myclass][specIndex]
+
+ local name = statID and _G["SPELL_STAT"..statID.."_NAME"]
+ if name then
+ self.text:SetFormattedText(displayString, name..": ", UnitStat("player", statID))
+ else
+ self.text:SetText(NOT_APPLICABLE)
+ end
+end
+
+local function ValueColorUpdate(hex)
+ displayString = join("", "%s", hex, "%.f|r")
+
+ if lastPanel ~= nil then
+ OnEvent(lastPanel)
+ end
+end
+E.valueColorUpdateFuncs[ValueColorUpdate] = true
+
+DT:RegisterDatatext("Primary Stat", {"UNIT_STATS", "UNIT_AURA", "ACTIVE_TALENT_GROUP_CHANGED", "PLAYER_TALENT_UPDATE", "CHARACTER_POINTS_CHANGED"}, OnEvent, nil, nil, nil, nil, L["Primary Stat"])
diff --git a/Modules/DataTexts/Quests.lua b/Modules/DataTexts/Quests.lua
new file mode 100644
index 000000000..67b548d1d
--- /dev/null
+++ b/Modules/DataTexts/Quests.lua
@@ -0,0 +1,90 @@
+local E, L, V, P, G = unpack(select(2, ...)) --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
+local DT = E:GetModule("DataTexts")
+
+--Lua functions
+local _G = _G
+local format = string.format
+local join = string.join
+--WoW API / Variables
+local UnitXPMax = UnitXPMax
+local MouseIsOver = MouseIsOver
+local IsShiftKeyDown = IsShiftKeyDown
+local GetQuestLogTitle = GetQuestLogTitle
+local GetQuestLogRewardXP = GetQuestLogRewardXP
+local SelectQuestLogEntry = SelectQuestLogEntry
+local GetQuestLogRewardMoney = GetQuestLogRewardMoney
+local GetNumQuestLogEntries = GetNumQuestLogEntries
+local BreakUpLargeNumbers = BreakUpLargeNumbers
+
+local MAX_QUESTLOG_QUESTS = MAX_QUESTLOG_QUESTS -- 25 в WotLK
+local QUESTS_LABEL = QUESTS_LABEL
+local COMPLETE = COMPLETE
+local INCOMPLETE = INCOMPLETE
+
+local displayString = ""
+local numEntries, numQuests, xpToLevel = 0, 0, 0
+
+local function GetQuestInfo(questIndex)
+ local info = {}
+ info.title, info.level, info.questTag, info.suggestedGroup, info.isHeader, info.isCollapsed, info.isComplete, info.isDaily, info.questID = GetQuestLogTitle(questIndex)
+ SelectQuestLogEntry(questIndex)
+
+ return info
+end
+
+local function OnEnter(self)
+ DT:SetupTooltip(self)
+
+ local totalMoney, totalXP, completedXP = 0, 0, 0
+ local isShiftDown = IsShiftKeyDown()
+
+ DT.tooltip:AddLine(QUESTS_LABEL)
+ DT.tooltip:AddLine(" ")
+
+ for questIndex = 1, numEntries do
+ local info = GetQuestInfo(questIndex)
+ if info and not info.isHeader then
+ local xp = GetQuestLogRewardXP()
+ local money = GetQuestLogRewardMoney()
+ local isComplete = info.isComplete
+
+ totalMoney = totalMoney + money
+ totalXP = totalXP + xp
+ completedXP = completedXP + (isComplete and xp or 0)
+
+ DT.tooltip:AddDoubleLine(info.title, isShiftDown and format("%s (%.2f%%)", BreakUpLargeNumbers(xp), (xpToLevel > 0 and (xp / xpToLevel) * 100) or 0) or (isComplete and COMPLETE or INCOMPLETE), 1, 1, 1, isComplete and .2 or 1, isComplete and 1 or .2, .2)
+ end
+ end
+
+ if completedXP > 0 then
+ DT.tooltip:AddLine(" ")
+ DT.tooltip:AddDoubleLine(L["Completed XP:"], format("%s (%.2f%%)", BreakUpLargeNumbers(completedXP), (xpToLevel > 0 and (completedXP / xpToLevel) * 100) or 0), nil, nil, nil, 1, 1, 1)
+ end
+
+ DT.tooltip:AddLine(" ")
+ DT.tooltip:AddDoubleLine(L["Total Gold:"], E:FormatMoney(totalMoney, "SMART"), nil, nil, nil, 1, 1, 1)
+ DT.tooltip:AddDoubleLine(L["Total XP:"], format("%s (%.2f%%)", BreakUpLargeNumbers(totalXP), (xpToLevel > 0 and (totalXP / xpToLevel) * 100) or 0), nil, nil, nil, 1, 1, 1)
+ DT.tooltip:Show()
+end
+
+local function OnClick()
+ _G.ToggleFrame(_G.QuestLogFrame)
+end
+
+local function OnEvent(self)
+ numEntries, numQuests = GetNumQuestLogEntries()
+ xpToLevel = UnitXPMax("player")
+
+ self.text:SetFormattedText(displayString, numQuests, MAX_QUESTLOG_QUESTS)
+
+ if MouseIsOver(self) then
+ OnEnter(self)
+ end
+end
+
+local function ValueColorUpdate(hex)
+ displayString = join("", QUESTS_LABEL, ": ", hex, "%d|r", "/", hex, "%d|r")
+end
+E.valueColorUpdateFuncs[ValueColorUpdate] = true
+
+DT:RegisterDatatext("Quests", {"QUEST_ACCEPTED", "QUEST_LOG_UPDATE", "MODIFIER_STATE_CHANGED"}, OnEvent, nil, OnClick, OnEnter, nil, L["Quest Log"])
diff --git a/Modules/DataTexts/Reputation.lua b/Modules/DataTexts/Reputation.lua
new file mode 100644
index 000000000..81b9d47c1
--- /dev/null
+++ b/Modules/DataTexts/Reputation.lua
@@ -0,0 +1,86 @@
+local E, L, V, P, G = unpack(select(2, ...)) --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
+local DT = E:GetModule("DataTexts")
+
+--Lua functions
+local _G = _G
+local format = string.format
+--WoW API / Variables
+local ToggleCharacter = ToggleCharacter
+local GetWatchedFactionInfo = GetWatchedFactionInfo
+local GetFactionInfo = GetFactionInfo
+local GetNumFactions = GetNumFactions
+
+local NOT_APPLICABLE = NOT_APPLICABLE
+local REPUTATION = REPUTATION
+local STANDING = STANDING
+
+-- в WotLK нет GetWatchedFactionIndex(); ищем индекс, сверяя имя
+-- отслеживаемой фракции из GetWatchedFactionInfo() со списком фракций
+local function GetWatchedFactionIndex()
+ local name = GetWatchedFactionInfo()
+ if not name then return end
+
+ for i = 1, GetNumFactions() do
+ if GetFactionInfo(i) == name then
+ return i
+ end
+ end
+end
+
+local function GetWatchedFactionData()
+ local index = GetWatchedFactionIndex()
+ if not index then return end
+
+ local name, _, reaction, min, max, value = GetFactionInfo(index)
+ return name, reaction, min, max, value
+end
+
+local function OnEvent(self)
+ local name, reaction, min, max, value = GetWatchedFactionData()
+ if not name then
+ self.text:SetText(NOT_APPLICABLE)
+ return
+ end
+
+ local isCapped = reaction == 8
+
+ local color = _G.FACTION_BAR_COLORS[reaction]
+ local standingLabel = E:RGBToHex(color.r, color.g, color.b).._G["FACTION_STANDING_LABEL"..reaction].."|r"
+
+ -- защита от деления на ноль
+ local maxMinDiff = max - min
+ if maxMinDiff == 0 then
+ maxMinDiff = 1
+ end
+
+ local text
+ if isCapped then
+ text = format("%s: [%s]", name, standingLabel)
+ else
+ text = format("%s: %d%% [%s]", name, ((value - min) / (maxMinDiff) * 100), standingLabel)
+ end
+
+ self.text:SetText(text)
+end
+
+local function OnEnter(self)
+ DT:SetupTooltip(self)
+
+ local name, reaction, min, max, value = GetWatchedFactionData()
+ if name then
+ DT.tooltip:AddLine(name)
+ DT.tooltip:AddLine(" ")
+
+ DT.tooltip:AddDoubleLine(STANDING..":", _G["FACTION_STANDING_LABEL"..reaction], 1, 1, 1)
+ if reaction ~= 8 then
+ DT.tooltip:AddDoubleLine(REPUTATION..":", format("%d / %d (%d%%)", value - min, max - min, (value - min) / ((max - min == 0) and max or (max - min)) * 100), 1, 1, 1)
+ end
+ DT.tooltip:Show()
+ end
+end
+
+local function OnClick()
+ ToggleCharacter("ReputationFrame")
+end
+
+DT:RegisterDatatext("Reputation", {"UPDATE_FACTION", "COMBAT_TEXT_UPDATE"}, OnEvent, nil, OnClick, OnEnter, nil, REPUTATION)
diff --git a/Modules/DataTexts/Speed.lua b/Modules/DataTexts/Speed.lua
new file mode 100644
index 000000000..a11efeb8f
--- /dev/null
+++ b/Modules/DataTexts/Speed.lua
@@ -0,0 +1,44 @@
+local E, L, V, P, G = unpack(select(2, ...)) --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
+local DT = E:GetModule("DataTexts")
+
+--Lua functions
+local format, join = string.format, string.join
+--WoW API / Variables
+local GetCombatRating = GetCombatRating
+local GetCombatRatingBonus = GetCombatRatingBonus
+local UnitAttackSpeed = UnitAttackSpeed
+
+local SPEED = SPEED
+local CR_HIT_TAKEN_SPELL = CR_HIT_TAKEN_SPELL
+local CR_SPEED_TOOLTIP = CR_SPEED_TOOLTIP
+local FONT_COLOR_CODE_CLOSE = FONT_COLOR_CODE_CLOSE
+local HIGHLIGHT_FONT_COLOR_CODE = HIGHLIGHT_FONT_COLOR_CODE
+local PAPERDOLLFRAME_TOOLTIP_FORMAT = PAPERDOLLFRAME_TOOLTIP_FORMAT
+
+local displayString = ""
+local lastPanel
+
+local function OnEnter(self)
+ DT:SetupTooltip(self)
+
+ DT.tooltip:AddDoubleLine(HIGHLIGHT_FONT_COLOR_CODE..format(PAPERDOLLFRAME_TOOLTIP_FORMAT, SPEED).." "..format("%.2f%%", UnitAttackSpeed("player"))..FONT_COLOR_CODE_CLOSE, nil, 1, 1, 1)
+ DT.tooltip:AddLine(format(CR_SPEED_TOOLTIP, GetCombatRating(CR_HIT_TAKEN_SPELL), GetCombatRatingBonus(CR_HIT_TAKEN_SPELL)), nil, nil, nil, true)
+ DT.tooltip:Show()
+end
+
+local function OnEvent(self)
+ lastPanel = self
+
+ self.text:SetFormattedText(displayString, UnitAttackSpeed("player"))
+end
+
+local function ValueColorUpdate(hex)
+ displayString = join("", SPEED, ": ", hex, "%.2f%%|r")
+
+ if lastPanel ~= nil then
+ OnEvent(lastPanel)
+ end
+end
+E.valueColorUpdateFuncs[ValueColorUpdate] = true
+
+DT:RegisterDatatext("Speed", {"UNIT_STATS", "UNIT_AURA", "PLAYER_DAMAGE_DONE_MODS"}, OnEvent, nil, nil, OnEnter, nil, SPEED)
diff --git a/Modules/DataTexts/SpellHaste.lua b/Modules/DataTexts/SpellHaste.lua
new file mode 100644
index 000000000..664ca89ac
--- /dev/null
+++ b/Modules/DataTexts/SpellHaste.lua
@@ -0,0 +1,28 @@
+local E, L, V, P, G = unpack(select(2, ...)) --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
+local DT = E:GetModule("DataTexts")
+
+--Lua functions
+local join = string.join
+--WoW API / Variables
+local GetCombatRatingBonus = GetCombatRatingBonus
+local CR_HASTE_SPELL = CR_HASTE_SPELL
+
+local displayString = ""
+local lastPanel
+
+local function OnEvent(self)
+ lastPanel = self
+
+ self.text:SetFormattedText(displayString, GetCombatRatingBonus(CR_HASTE_SPELL) or 0)
+end
+
+local function ValueColorUpdate(hex)
+ displayString = join("", L["Spell Haste"], ": ", hex, "%.2f%%|r")
+
+ if lastPanel ~= nil then
+ OnEvent(lastPanel)
+ end
+end
+E.valueColorUpdateFuncs[ValueColorUpdate] = true
+
+DT:RegisterDatatext("Spell Haste", {"UNIT_STATS", "UNIT_AURA"}, OnEvent, nil, nil, nil, nil, L["Spell Haste"])
diff --git a/Modules/DataTexts/SpellHit.lua b/Modules/DataTexts/SpellHit.lua
new file mode 100644
index 000000000..9d143d751
--- /dev/null
+++ b/Modules/DataTexts/SpellHit.lua
@@ -0,0 +1,30 @@
+local E, L, V, P, G = unpack(select(2, ...)) --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
+local DT = E:GetModule("DataTexts")
+
+--Lua functions
+local join = string.join
+--WoW API / Variables
+-- в WotLK нет GetSpellHitModifier(); бонус рейтинга и есть полное значение
+-- меткости, которое отдает клиент (как в строке "Меткость" окна персонажа)
+local GetCombatRatingBonus = GetCombatRatingBonus
+local CR_HIT_SPELL = CR_HIT_SPELL
+
+local displayString = ""
+local lastPanel
+
+local function OnEvent(self)
+ lastPanel = self
+
+ self.text:SetFormattedText(displayString, GetCombatRatingBonus(CR_HIT_SPELL))
+end
+
+local function ValueColorUpdate(hex)
+ displayString = join("", L["Spell Hit"], ": ", hex, "%.2f%%|r")
+
+ if lastPanel ~= nil then
+ OnEvent(lastPanel)
+ end
+end
+E.valueColorUpdateFuncs[ValueColorUpdate] = true
+
+DT:RegisterDatatext("Spell Hit", {"UNIT_STATS", "UNIT_AURA"}, OnEvent, nil, nil, nil, nil, L["Spell Hit"])
diff --git a/Modules/DataTexts/Spirit.lua b/Modules/DataTexts/Spirit.lua
new file mode 100644
index 000000000..9324e582a
--- /dev/null
+++ b/Modules/DataTexts/Spirit.lua
@@ -0,0 +1,29 @@
+local E, L, V, P, G = unpack(select(2, ...)) --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
+local DT = E:GetModule("DataTexts")
+
+--Lua functions
+local join = string.join
+--WoW API / Variables
+local UnitStat = UnitStat
+
+local ITEM_MOD_SPIRIT_SHORT = ITEM_MOD_SPIRIT_SHORT
+
+local displayString = ""
+local lastPanel
+
+local function OnEvent(self)
+ lastPanel = self
+
+ self.text:SetFormattedText(displayString, UnitStat("player", 5))
+end
+
+local function ValueColorUpdate(hex)
+ displayString = join("", ITEM_MOD_SPIRIT_SHORT, ": ", hex, "%.f|r")
+
+ if lastPanel ~= nil then
+ OnEvent(lastPanel)
+ end
+end
+E.valueColorUpdateFuncs[ValueColorUpdate] = true
+
+DT:RegisterDatatext("Spirit", {"UNIT_STATS", "UNIT_AURA"}, OnEvent, nil, nil, nil, nil, ITEM_MOD_SPIRIT_SHORT)
diff --git a/Modules/DataTexts/Stamina.lua b/Modules/DataTexts/Stamina.lua
new file mode 100644
index 000000000..f2f60f57a
--- /dev/null
+++ b/Modules/DataTexts/Stamina.lua
@@ -0,0 +1,29 @@
+local E, L, V, P, G = unpack(select(2, ...)) --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
+local DT = E:GetModule("DataTexts")
+
+--Lua functions
+local join = string.join
+--WoW API / Variables
+local UnitStat = UnitStat
+
+local ITEM_MOD_STAMINA_SHORT = ITEM_MOD_STAMINA_SHORT
+
+local displayString = ""
+local lastPanel
+
+local function OnEvent(self)
+ lastPanel = self
+
+ self.text:SetFormattedText(displayString, UnitStat("player", 3))
+end
+
+local function ValueColorUpdate(hex)
+ displayString = join("", ITEM_MOD_STAMINA_SHORT, ": ", hex, "%d|r")
+
+ if lastPanel ~= nil then
+ OnEvent(lastPanel)
+ end
+end
+E.valueColorUpdateFuncs[ValueColorUpdate] = true
+
+DT:RegisterDatatext("Stamina", {"UNIT_STATS", "UNIT_AURA"}, OnEvent, nil, nil, nil, nil, ITEM_MOD_STAMINA_SHORT)
diff --git a/Modules/DataTexts/Strength.lua b/Modules/DataTexts/Strength.lua
new file mode 100644
index 000000000..8306c55d2
--- /dev/null
+++ b/Modules/DataTexts/Strength.lua
@@ -0,0 +1,29 @@
+local E, L, V, P, G = unpack(select(2, ...)) --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
+local DT = E:GetModule("DataTexts")
+
+--Lua functions
+local join = string.join
+--WoW API / Variables
+local UnitStat = UnitStat
+
+local ITEM_MOD_STRENGTH_SHORT = ITEM_MOD_STRENGTH_SHORT
+
+local displayString = ""
+local lastPanel
+
+local function OnEvent(self)
+ lastPanel = self
+
+ self.text:SetFormattedText(displayString, UnitStat("player", 1))
+end
+
+local function ValueColorUpdate(hex)
+ displayString = join("", ITEM_MOD_STRENGTH_SHORT, ": ", hex, "%d|r")
+
+ if lastPanel ~= nil then
+ OnEvent(lastPanel)
+ end
+end
+E.valueColorUpdateFuncs[ValueColorUpdate] = true
+
+DT:RegisterDatatext("Strength", {"UNIT_STATS", "UNIT_AURA"}, OnEvent, nil, nil, nil, nil, ITEM_MOD_STRENGTH_SHORT)
diff --git a/Modules/DataTexts/Volume.lua b/Modules/DataTexts/Volume.lua
new file mode 100644
index 000000000..4ee79bb21
--- /dev/null
+++ b/Modules/DataTexts/Volume.lua
@@ -0,0 +1,159 @@
+local E, L, V, P, G = unpack(select(2, ...)) --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
+local DT = E:GetModule("DataTexts")
+
+--Lua functions
+local _G = _G
+local tonumber = tonumber
+local format = string.format
+local ipairs = ipairs
+local tinsert = table.insert
+--WoW API / Variables
+local GetCVar = GetCVar
+local GetCVarBool = GetCVarBool
+local IsShiftKeyDown = IsShiftKeyDown
+local ToggleFrame = ToggleFrame
+local CreateFrame = CreateFrame
+
+local Sound_GameSystem_GetOutputDriverNameByIndex = Sound_GameSystem_GetOutputDriverNameByIndex
+local Sound_GameSystem_GetNumOutputDrivers = Sound_GameSystem_GetNumOutputDrivers
+local Sound_GameSystem_RestartSoundSystem = Sound_GameSystem_RestartSoundSystem
+
+local Sound_CVars = {
+ Sound_MasterVolume = true,
+ Sound_SFXVolume = true,
+ Sound_AmbienceVolume = true,
+ Sound_MusicVolume = true
+}
+
+local AudioStreams = {
+ { Name = _G.MASTER_VOLUME, Volume = "Sound_MasterVolume", Enabled = "Sound_EnableAllSound" },
+ { Name = _G.SOUND_VOLUME, Volume = "Sound_SFXVolume", Enabled = "Sound_EnableSFX" },
+ { Name = _G.AMBIENCE_VOLUME, Volume = "Sound_AmbienceVolume", Enabled = "Sound_EnableAmbience" },
+ { Name = _G.MUSIC_VOLUME, Volume = "Sound_MusicVolume", Enabled = "Sound_EnableMusic" }
+}
+
+local panelText
+local activeIndex = 1
+local activeStream = AudioStreams[activeIndex]
+local menu = {}
+local toggleMenu = {}
+local deviceMenu = {}
+
+local dropdown = CreateFrame("Frame", "ElvUI_VolumeDropDown", E.UIParent)
+
+local function GetStreamString(stream, tooltip)
+ if not stream then stream = AudioStreams[1] end
+
+ local color = GetCVarBool(AudioStreams[1].Enabled) and GetCVarBool(stream.Enabled) and "00FF00" or "FF3333"
+ local level = (GetCVar(stream.Volume) or 0) * 100
+
+ return (tooltip and format("|cFF%s%.f%%|r", color, level)) or format("%s: |cFF%s%.f%%|r", stream.Name, color, level)
+end
+
+local function SelectStream(_, arg1)
+ activeIndex = arg1
+ activeStream = AudioStreams[activeIndex]
+
+ if panelText then
+ panelText:SetText(GetStreamString(activeStream))
+ end
+end
+
+local function ToggleStream(_, arg1)
+ local Stream = AudioStreams[arg1]
+
+ E:SetCVar(Stream.Enabled, GetCVarBool(Stream.Enabled) and 0 or 1, "ELVUI_VOLUME")
+
+ if panelText then
+ panelText:SetText(GetStreamString(activeStream))
+ end
+end
+
+for Index, Stream in ipairs(AudioStreams) do
+ tinsert(menu, { text = Stream.Name, func = function() SelectStream(nil, Index) end })
+ tinsert(toggleMenu, { text = Stream.Name, func = function() ToggleStream(nil, Index) end })
+end
+
+local function SelectSoundOutput(_, arg1)
+ E:SetCVar("Sound_OutputDriverIndex", arg1, "ELVUI_VOLUME")
+ Sound_GameSystem_RestartSoundSystem()
+end
+
+for i = 0, (Sound_GameSystem_GetNumOutputDrivers and Sound_GameSystem_GetNumOutputDrivers() or 0) - 1 do
+ tinsert(deviceMenu, { text = Sound_GameSystem_GetOutputDriverNameByIndex(i), func = function() SelectSoundOutput(nil, i) end })
+end
+
+local function OnEnter(self)
+ DT:SetupTooltip(self)
+
+ DT.tooltip:AddLine(L["Active Output Audio Device"], 1, 1, 1)
+ DT.tooltip:AddLine(Sound_GameSystem_GetOutputDriverNameByIndex(GetCVar("Sound_OutputDriverIndex")))
+ DT.tooltip:AddLine(" ")
+ DT.tooltip:AddLine(L["Volume Streams"], 1, 1, 1)
+
+ for _, Stream in ipairs(AudioStreams) do
+ DT.tooltip:AddDoubleLine(Stream.Name, GetStreamString(Stream, true))
+ end
+
+ DT.tooltip:AddLine(" ")
+
+ DT.tooltip:AddLine(L["|cFFffffffLeft Click:|r Select Volume Stream"])
+ DT.tooltip:AddLine(L["|cFFffffffMiddle Click:|r Toggle Mute Master Stream"])
+ DT.tooltip:AddLine(L["|cFFffffffRight Click:|r Toggle Volume Stream"])
+ DT.tooltip:AddLine(L["|cFFffffffShift + Left Click:|r Open System Audio Panel"])
+ DT.tooltip:AddLine(L["|cFFffffffShift + Right Click:|r Select Output Audio Device"])
+
+ DT.tooltip:Show()
+end
+
+local function onMouseWheel(_, delta)
+ local vol = GetCVar(activeStream.Volume)
+ local scale = 100
+
+ if IsShiftKeyDown() then
+ scale = 10
+ end
+
+ vol = tonumber(vol) + (delta / scale)
+
+ if vol >= 1 then
+ vol = 1
+ elseif vol <= 0 then
+ vol = 0
+ end
+
+ E:SetCVar(activeStream.Volume, vol, "ELVUI_VOLUME")
+ panelText:SetText(GetStreamString(activeStream))
+end
+
+local function OnEvent(self, event, arg1)
+ activeStream = AudioStreams[activeIndex]
+ panelText = self.text
+
+ local force = event == "ELVUI_FORCE_UPDATE" or event == "ELVUI_FORCE_RUN"
+ if force or (event == "CVAR_UPDATE" and (Sound_CVars[arg1] or arg1 == "ELVUI_VOLUME")) then
+ if force then
+ self:EnableMouseWheel(true)
+ self:SetScript("OnMouseWheel", onMouseWheel)
+ end
+
+ panelText:SetText(GetStreamString(activeStream))
+ end
+end
+
+local function OnClick(self, button)
+ if button == "LeftButton" then
+ if IsShiftKeyDown() then
+ ToggleFrame(_G.AudioOptionsFrame)
+ return
+ end
+
+ E:DropDown(menu, dropdown)
+ elseif button == "MiddleButton" then
+ E:SetCVar(AudioStreams[1].Enabled, GetCVarBool(AudioStreams[1].Enabled) and 0 or 1, "ELVUI_VOLUME")
+ elseif button == "RightButton" then
+ E:DropDown(IsShiftKeyDown() and deviceMenu or toggleMenu, dropdown)
+ end
+end
+
+DT:RegisterDatatext(L["Volume"], {"CVAR_UPDATE"}, OnEvent, nil, OnClick, OnEnter)
diff --git a/Modules/Nameplates/Elements/Auras.lua b/Modules/Nameplates/Elements/Auras.lua
index c1eb11cc1..049e42ba5 100644
--- a/Modules/Nameplates/Elements/Auras.lua
+++ b/Modules/Nameplates/Elements/Auras.lua
@@ -7,12 +7,16 @@ local LSM = E.Libs.LSM
local _G = _G
local wipe = wipe
local unpack = unpack
+local ipairs = ipairs
local CreateFrame = CreateFrame
local strsplit = strsplit
+local UnitExists = UnitExists
local UnitIsFriend = UnitIsFriend
local UnitCanAttack = UnitCanAttack
local UnitIsUnit = UnitIsUnit
local UnitName = UnitName
+local UnitPower = UnitPower
+local UnitPowerMax = UnitPowerMax
local ceil, min = math.ceil, math.min
function NP:GetAuraIconSize(db)
@@ -95,6 +99,16 @@ local function NP_ShouldTrackPower(nameplate)
return false
end
+local function NP_ShouldTrackHealth(nameplate)
+ if not nameplate then return false end
+ local db = NP:PlateDB(nameplate)
+ if not db or db.nameOnly then return false end
+ if db.health and db.health.enable then return true end
+
+ local hText = db.health and db.health.text
+ return hText and hText.enable and hText.textFormat and hText.textFormat ~= ''
+end
+
local function NP_ShouldTrackName(nameplate)
if not nameplate then return false end
local db = NP:PlateDB(nameplate)
@@ -115,9 +129,32 @@ end
function NP:UpdatePlatePower(nameplate)
if not nameplate or not nameplate.unit then return end
-
- if nameplate.Power and nameplate.Power.ForceUpdate and nameplate:IsElementEnabled('Power') then
- nameplate.Power:ForceUpdate()
+ local u = nameplate.unit
+ if not UnitExists(u) then return end
+ local pw = nameplate.Power
+ if pw then
+ local cur = UnitPower(u)
+ local max = UnitPowerMax(u)
+ if max and max > 0 then
+ local changed = false
+ if pw._np_max ~= max then
+ pw._np_max = max
+ pw:SetMinMaxValues(0, max)
+ changed = true
+ end
+ if pw._np_cur ~= cur then
+ pw._np_cur = cur
+ changed = true
+ end
+ if changed then
+ if nameplate.PowerValueChangeCallbacks then
+ for _, cb in ipairs(nameplate.PowerValueChangeCallbacks) do
+ cb(NP, nameplate, cur, max)
+ end
+ end
+ NP:SetBarValue(pw, cur)
+ end
+ end
end
local db = NP:PlateDB(nameplate)
@@ -201,6 +238,12 @@ function NP:CollectPlateUnitEvents(nameplate)
end
end
+ if NP_ShouldTrackHealth(nameplate) then
+ add('UNIT_HEALTH')
+ add('UNIT_MAXHEALTH')
+ add('UNIT_MAXPOWER')
+ end
+
local db = NP:PlateDB(nameplate)
if db.eliteIcon and db.eliteIcon.enable then
add('UNIT_CLASSIFICATION_CHANGED')
@@ -255,6 +298,11 @@ function NP.PlateUnitEvent_OnEvent(buffs, event, unit)
if nameplate.ClassificationIndicator and nameplate:IsElementEnabled('ClassificationIndicator') then
nameplate:UpdateAllElements('UNIT_CLASSIFICATION_CHANGED')
end
+ elseif event == 'UNIT_HEALTH' or event == 'UNIT_MAXHEALTH' or event == 'UNIT_MAXPOWER' then
+ NP:UpdatePlateHealth(nameplate)
+ if event == 'UNIT_MAXPOWER' then
+ NP:UpdatePlatePower(nameplate)
+ end
elseif NP_PLATE_POWER_EVENT_SET[event] then
NP:UpdatePlatePower(nameplate)
end
diff --git a/Modules/Nameplates/Nameplates.lua b/Modules/Nameplates/Nameplates.lua
index 760ef2502..0484356bd 100644
--- a/Modules/Nameplates/Nameplates.lua
+++ b/Modules/Nameplates/Nameplates.lua
@@ -29,15 +29,17 @@ local UnitIsUnit = UnitIsUnit
local UnitLevel = UnitLevel
local UnitHealth = UnitHealth
local UnitHealthMax = UnitHealthMax
-local UnitPower = UnitPower
-local UnitPowerMax = UnitPowerMax
local UnitName = UnitName
local UnitReaction = UnitReaction
local hooksecurefunc = hooksecurefunc
-local SetBarValue = (PixelUtil and PixelUtil.SetStatusBarValue)
- and function(bar, v) PixelUtil.SetStatusBarValue(bar, v) end
- or function(bar, v) bar:SetValue(v) end
+function NP:SetBarValue(bar, value)
+ if PixelUtil and PixelUtil.SetStatusBarValue then
+ PixelUtil.SetStatusBarValue(bar, value)
+ else
+ bar:SetValue(value)
+ end
+end
local function HideSelf(self) self:Hide() end
@@ -54,11 +56,11 @@ NP.TARGET_LEVEL_FLOOR = 4500
do
local f = CreateFrame('Frame')
local elapsed = 0
- local tagsElapsed = 0
+ local reactionElapsed = 0
local scaleElapsed = 0
- local HEALTH_INTERVAL = 0.2
- local TAGS_INTERVAL = 0.5
- local SCALE_INTERVAL = 0.05
+ local CHECK_INTERVAL = 0.2
+ local REACTION_INTERVAL = 0.5
+ local SCALE_INTERVAL = 0.05
f:SetScript('OnUpdate', function(_, dt)
scaleElapsed = scaleElapsed + dt
if scaleElapsed >= SCALE_INTERVAL then
@@ -70,12 +72,12 @@ do
end
end
- elapsed = elapsed + dt
- tagsElapsed = tagsElapsed + dt
- if elapsed < HEALTH_INTERVAL then return end
- local doTags = tagsElapsed >= TAGS_INTERVAL
+ elapsed = elapsed + dt
+ reactionElapsed = reactionElapsed + dt
+ if elapsed < CHECK_INTERVAL then return end
elapsed = 0
- if doTags then tagsElapsed = 0 end
+ local doReaction = reactionElapsed >= REACTION_INTERVAL
+ if doReaction then reactionElapsed = 0 end
if NP.watchMouseover then
NP:RefreshPlatesOnMouseoverChanged()
@@ -84,63 +86,9 @@ do
for plate in pairs(NP.Plates) do
local u = plate.unit
if u and UnitExists(u) then
- if doTags and UnitReaction('player', u) ~= plate.reaction then
+ if doReaction and UnitReaction('player', u) ~= plate.reaction then
NP:RefreshPlateReaction(plate)
end
- local h = plate.Health
- if h then
- local cur = UnitHealth(u)
- local max = UnitHealthMax(u)
- if max and max > 0 then
- local changed = false
- if h._np_max ~= max then
- h._np_max = max
- h:SetMinMaxValues(0, max)
- changed = true
- end
- if h._np_cur ~= cur then
- h._np_cur = cur
- changed = true
- end
- if changed then
- -- fire before SetValue so Cutaway reads the previous value
- if plate.HealthValueChangeCallbacks then
- for _, cb in ipairs(plate.HealthValueChangeCallbacks) do
- cb(NP, plate, cur, max)
- end
- end
- SetBarValue(h, cur)
- end
- end
- end
- local pw = plate.Power
- if pw and pw:IsShown() then
- local cur = UnitPower(u)
- local max = UnitPowerMax(u)
- if max and max > 0 then
- local changed = false
- if pw._np_max ~= max then
- pw._np_max = max
- pw:SetMinMaxValues(0, max)
- changed = true
- end
- if pw._np_cur ~= cur then
- pw._np_cur = cur
- changed = true
- end
- if changed then
- if plate.PowerValueChangeCallbacks then
- for _, cb in ipairs(plate.PowerValueChangeCallbacks) do
- cb(NP, plate, cur, max)
- end
- end
- SetBarValue(pw, cur)
- end
- end
- end
- if doTags then
- plate:UpdateTags()
- end
if not plate.appliedFrameLevelBoost then
if plate._npTargetBoost then
@@ -172,6 +120,48 @@ do
end)
end
+-- обновление здоровья по событиям (вместо опроса в OnUpdate). Вызывается из
+-- PlateUnitEvent_OnEvent на UNIT_HEALTH / UNIT_MAXHEALTH / UNIT_MAXPOWER
+-- и один раз при добавлении таблички
+function NP:UpdatePlateHealth(nameplate)
+ if not nameplate or not nameplate.unit then return end
+ local u = nameplate.unit
+ if not UnitExists(u) then return end
+ local h = nameplate.Health
+ if not h then return end
+ local cur = UnitHealth(u)
+ local max = UnitHealthMax(u)
+ if not max or max <= 0 then return end
+ local changed = false
+ if h._np_max ~= max then
+ h._np_max = max
+ h:SetMinMaxValues(0, max)
+ changed = true
+ end
+ if h._np_cur ~= cur then
+ h._np_cur = cur
+ changed = true
+ end
+ if changed then
+ -- вызываем до SetValue, чтобы Cutaway прочитал предыдущее значение
+ if nameplate.HealthValueChangeCallbacks then
+ for _, cb in ipairs(nameplate.HealthValueChangeCallbacks) do
+ cb(NP, nameplate, cur, max)
+ end
+ end
+ NP:SetBarValue(h, cur)
+ end
+
+ -- обновляем тег текста здоровья (может содержать [health:*] и теги ресурса)
+ local db = NP:PlateDB(nameplate)
+ local hText = db.health and db.health.text
+ if hText and hText.enable and hText.textFormat and hText.textFormat ~= ''
+ and nameplate.Health.Text and nameplate.Health.Text.UpdateTag
+ then
+ nameplate.Health.Text:UpdateTag()
+ end
+end
+
do
local empty = {}
function NP:PlateDB(nameplate)
@@ -182,6 +172,70 @@ do
end
end
+--[[ Иконки тотемов (минимальный порт элемента IconFrame из dev-версии,
+встроен сюда, чтобы не создавать отдельный файл). Показывает иконку
+тотема над его табличкой. ]]--
+local totemSpellIDs = {
+ -- Воздух
+ 8177, 10595, 10600, 10601, 25574, 58746, 58749, 6495, 8512, 3738,
+ -- Земля
+ 2062, 2484, 5730, 6390, 6391, 6392, 10427, 10428, 25525, 58580, 58581, 58582,
+ 8071, 8154, 8155, 10406, 10407, 10408, 25508, 25509, 58751, 58753,
+ 8075, 8160, 8161, 10442, 25361, 25528, 57622, 58643, 8143,
+ -- Огонь
+ 2894, 8227, 8249, 10526, 16387, 25557, 58649, 58652, 58656,
+ 8181, 10478, 10479, 25560, 58741, 58745, 8190, 10585, 10586, 10587, 25552, 58731, 58734,
+ 3599, 6363, 6364, 6365, 10437, 10438, 25533, 58699, 58703, 58704,
+ 30706, 57720, 57721, 57722,
+ -- Вода
+ 8170, 8184, 10537, 10538, 25563, 58737, 58739, 5394, 6375, 6377, 10462, 10463, 25567, 58755, 58756, 58757,
+ 5675, 10495, 10496, 10497, 25570, 58771, 58773, 58774, 16190,
+ -- Другое
+ 724, -- Светильник (Lightwell)
+}
+
+NP.TotemIcons = {}
+for _, spellID in ipairs(totemSpellIDs) do
+ local name, _, texture = GetSpellInfo(spellID)
+ if name then
+ NP.TotemIcons[name] = texture
+ -- таблички тотемов в WotLK обычно используют имя заклинания без ранга
+ local baseName = name:gsub('%s+[IVX]+$', '')
+ if baseName ~= name then
+ NP.TotemIcons[baseName] = texture
+ end
+ end
+end
+
+function NP:Construct_TotemIcon(nameplate)
+ local icon = nameplate:CreateTexture(nameplate:GetName()..'TotemIcon', 'OVERLAY', nil, 4)
+ icon:SetTexCoord(unpack(E.TexCoords))
+ icon:CreateBackdrop(nil, nil, nil, true, true)
+ icon:Hide()
+ return icon
+end
+
+function NP:Update_TotemIcon(nameplate)
+ if not nameplate then return end
+ local icon = nameplate.TotemIcon
+ local db = NP:PlateDB(nameplate)
+ local texture = db and db.iconFrame and db.iconFrame.enable and NP.TotemIcons[nameplate.UnitName]
+ if texture and icon then
+ icon:SetTexture(texture)
+ icon:SetSize(db.iconFrame.size, db.iconFrame.size)
+ icon:ClearAllPoints()
+ local parent = (db.iconFrame.parent and db.iconFrame.parent ~= 'Nameplate') and nameplate[db.iconFrame.parent] or nameplate
+ icon:SetPoint(E.InversePoints[db.iconFrame.position], parent, db.iconFrame.position, db.iconFrame.xOffset, db.iconFrame.yOffset)
+ icon:Show()
+ -- фон это отдельная дочерняя рамка таблички, поэтому скрытие только
+ -- текстуры оставит пустую рамку; держим их синхронно
+ if icon.backdrop then icon.backdrop:Show() end
+ elseif icon then
+ icon:Hide()
+ if icon.backdrop then icon.backdrop:Hide() end
+ end
+end
+
local NP_ENGINE_CVARS = {
loadDistance = { cvar = 'nameplateMaxDistance', driver = true },
predictedHealthAndPower = { cvar = 'nameplatePredictedHealthAndPower', bool = true },
@@ -351,6 +405,10 @@ function NP:UpdateCVars()
NP:SetEngineCVar('nameplateShowOnlyNames', '0')
+ -- таблички тотемов (на них завязан TotemIcon)
+ NP:SetEngineCVar('nameplateShowEnemyTotems', '1')
+ NP:SetEngineCVar('nameplateShowFriendlyTotems', '1')
+
-- transparency is owned by Style Filters (e.g. ElvUI_NonTarget); pin engine alpha neutral so it can't double-dim
NP:SetEngineCVar('nameplateSelectedAlpha', '1')
NP:SetEngineCVar('nameplateNotSelectedAlpha', '1')
@@ -434,6 +492,18 @@ function NP:UpdatePlateSize(nameplate)
end
end
+function NP:SetNamePlateSizes()
+ local setSelf = C_NamePlate and C_NamePlate.SetNamePlateSelfSize
+ if not setSelf then return end
+
+ local plateSize = NP.db and NP.db.plateSize
+ if not plateSize then return end
+
+ C_NamePlate.SetNamePlateSelfSize(plateSize.personalWidth, plateSize.personalHeight)
+ C_NamePlate.SetNamePlateEnemySize(plateSize.enemyWidth, plateSize.enemyHeight)
+ C_NamePlate.SetNamePlateFriendlySize(plateSize.friendlyWidth, plateSize.friendlyHeight)
+end
+
function NP:Style(unit)
self.isNamePlate = true
NP:StylePlate(self, unit)
@@ -471,6 +541,7 @@ function NP:StylePlate(nameplate)
nameplate.Level = NP:Construct_Level(textFrame)
nameplate.ClassificationIndicator = NP:Construct_ClassificationIndicator(nameplate.RaisedElement)
+ nameplate.TotemIcon = NP:Construct_TotemIcon(nameplate)
nameplate.Castbar = NP:Construct_Castbar(nameplate)
nameplate.Portrait = NP:Construct_Portrait(nameplate.RaisedElement)
nameplate.PvPIndicator = NP:Construct_PvPIndicator(nameplate.RaisedElement)
@@ -521,6 +592,7 @@ function NP:UpdatePlate(nameplate, updateBase)
NP:Update_ClassPower(nameplate)
NP:Update_Auras(nameplate)
NP:Update_ClassificationIndicator(nameplate)
+ NP:Update_TotemIcon(nameplate)
NP:Update_TargetIndicator(nameplate)
NP:Update_ThreatIndicator(nameplate)
else
@@ -630,6 +702,11 @@ function NP:NamePlateCallBack(nameplate, event, unit)
NP:UpdatePlateMouseoverState(nameplate)
NP:RegisterAuraUnitEvents(nameplate, unit)
+ NP:UpdatePlateHealth(nameplate)
+ NP:UpdatePlatePower(nameplate)
+
+ NP:Update_TotemIcon(nameplate)
+
NP:StyleFilterEventWatch(nameplate)
NP:StyleFilterSetVariables(nameplate)
@@ -1030,6 +1107,7 @@ function NP:ConfigureAll()
if not E.private.nameplates.enable then return end
NP:UpdateCVars()
+ NP:SetNamePlateSizes()
NP:StyleFilterConfigure()
NP:Update_StatusBars()
NP:ConfigurePlates()
@@ -1072,6 +1150,7 @@ function NP:Initialize()
NP:RegisterEvent('GROUP_ROSTER_UPDATE')
NP:RegisterEvent('PLAYER_TARGET_CHANGED', 'RefreshPlatesOnTargetChanged')
NP:RegisterEvent('UPDATE_MOUSEOVER_UNIT', 'RefreshPlatesOnMouseoverChanged')
+ NP:RegisterEvent('CVAR_UPDATE', 'SetNamePlateSizes')
if E.myclass == 'ROGUE' or E.myclass == 'DRUID' then
NP:RegisterEvent('UNIT_COMBO_POINTS', 'ClassPower_UNIT_COMBO_POINTS')
diff --git a/Modules/Skins/Addons/Ace3.lua b/Modules/Skins/Addons/Ace3.lua
index bffcc8524..b4809e61a 100644
--- a/Modules/Skins/Addons/Ace3.lua
+++ b/Modules/Skins/Addons/Ace3.lua
@@ -2,287 +2,382 @@ local E, _, V, P, G = unpack(select(2, ...)); --Import: Engine, Locales, Private
local S = E:GetModule("Skins")
--Lua functions
-local select = select
+local next = next
+local gsub = gsub
+local ipairs = ipairs
+local format = format
+local unpack = unpack
+local tinsert = tinsert
+local strmatch = strmatch
+
--WoW API / Variables
+local UIParent = UIParent
+local RaiseFrameLevel = RaiseFrameLevel
+local LowerFrameLevel = LowerFrameLevel
local hooksecurefunc = hooksecurefunc
-
--- functions that were overwritten, we need these to
--- finish the function call when our code executes!
-local oldRegisterAsWidget, oldRegisterAsContainer
+local getmetatable = getmetatable
+local setmetatable = setmetatable
+local rawset = rawset
-- these do *not* need to match the current lib minor version
-- these numbers are used to not attempt skinning way older
-- versions of AceGUI and AceConfigDialog.
-local minorGUI, minorConfigDialog = 1, 76
-
-function S:Ace3_SkinDropdownPullout()
- if self and self.obj then
- local pullout = self.obj.pullout
- local dropdown = self.obj.dropdown
+local minorGUI, minorConfigDialog = 36, 76
- if pullout and pullout.frame then
- if pullout.frame.template and pullout.slider.template then return end
+function S:Ace3_BackdropColor()
+ self:SetBackdropColor(0, 0, 0, 0.25)
+end
- if not pullout.frame.template then
- pullout.frame:SetTemplate("Default", true)
+function S:Ace3_SkinDropdown()
+ if self and self.obj then
+ local pullout = self.obj.dropdown
+ if pullout then
+ if pullout.frame then
+ pullout.frame:SetTemplate(nil, true)
+ else
+ pullout:SetTemplate(nil, true)
end
- if not pullout.slider.template then
- pullout.slider:SetTemplate("Default")
- pullout.slider:Point("TOPRIGHT", pullout.frame, "TOPRIGHT", -10, -10)
- pullout.slider:Point("BOTTOMRIGHT", pullout.frame, "BOTTOMRIGHT", -10, 10)
- if pullout.slider:GetThumbTexture() then
- pullout.slider:SetThumbTexture(E.Media.Textures.Melli)
- pullout.slider:GetThumbTexture():SetVertexColor(1, 0.82, 0, 0.8)
- pullout.slider:GetThumbTexture():Size(10, 14)
- end
- end
- elseif dropdown then
- dropdown:SetTemplate("Default", true)
-
- if dropdown.slider then
- dropdown.slider:SetTemplate("Default")
- dropdown.slider:Point("TOPRIGHT", dropdown, "TOPRIGHT", -10, -10)
- dropdown.slider:Point("BOTTOMRIGHT", dropdown, "BOTTOMRIGHT", -10, 10)
-
- if dropdown.slider:GetThumbTexture() then
- dropdown.slider:SetThumbTexture(E.Media.Textures.Melli)
- dropdown.slider:GetThumbTexture():SetVertexColor(1, 0.82, 0, 0.8)
- dropdown.slider:GetThumbTexture():Size(10, 14)
- end
- end
+ if pullout.slider then
+ pullout.slider:SetTemplate()
+ pullout.slider:SetThumbTexture(E.Media.Textures.White8x8)
- if TYPE == "LSM30_Sound" then
- local frame = self.obj.frame
- local width = frame:GetWidth()
- dropdown:Point("TOPLEFT", frame, "BOTTOMLEFT")
- dropdown:Point("TOPRIGHT", frame, "BOTTOMRIGHT", width < 160 and (160 - width) or 30, 0)
+ local t = pullout.slider:GetThumbTexture()
+ t:SetVertexColor(1, .82, 0, 0.8)
end
end
end
end
-function S:Ace3_CheckBoxIsEnableSwitch(widget)
- local text = widget.text and widget.text:GetText()
- if text then
- local enabled, disabled = text == S.Ace3_L.GREEN_ENABLE, text == S.Ace3_L.RED_ENABLE
- local isSwitch = (text == S.Ace3_L.Enable) or enabled or disabled
+function S:Ace3_CheckBoxIsEnable(widget)
+ local text = widget and widget.text and widget.text:GetText()
+ if text and S.Ace3_EnableMatch then return strmatch(text, S.Ace3_EnableMatch) end
+end
- return isSwitch
+-- убеждаемся, что данные подсветки "Enable" доступны до запуска хуков.
+-- Виджеты могут создаваться раньше, чем скин закончит инициализацию,
+-- и хук не должен молча пропускать покраску галочки.
+local function Ace3_EnsureEnableColoring()
+ if S.Ace3_L then return end
+
+ pcall(function()
+ local locale = (E.global and E.global.general and E.global.general.locale) or 'enUS'
+ local ACL = E.Libs and E.Libs.ACL
+ if ACL then
+ S:Ace3_ColorizeEnable(ACL:GetLocale('ElvUI', locale))
+ end
+ end)
+
+ if not S.Ace3_L then
+ S:Ace3_ColorizeEnable({ Enable = 'Enable' })
end
end
-function S:Ace3_RegisterAsWidget(widget)
- if not E.private.skins.ace3.enable then
- return oldRegisterAsWidget(self, widget)
+function S:Ace3_CheckBoxSetDesaturated(value)
+ local widget = self:GetParent():GetParent().obj
+ if value == true then
+ self:SetVertexColor(.6, .6, .6, .8)
+ elseif S:Ace3_CheckBoxIsEnable(widget) then
+ if widget.checked then
+ self:SetVertexColor(0.2, 1.0, 0.2, 1.0)
+ else
+ self:SetVertexColor(1.0, 0.2, 0.2, 1.0)
+ end
+ else
+ self:SetVertexColor(1, .82, 0, 0.8)
end
+end
- local TYPE = widget.type
- if TYPE == "MultiLineEditBox" then
- local frame = widget.frame
- local scrollBG = widget.scrollBG or select(2, frame:GetChildren()) or frame:GetChildren()
- local scrollBar = widget.scrollBar or _G[widget.scrollframe:GetName().."ScrollBar"]
+function S:Ace3_CheckBoxSetDisabled(disabled)
+ Ace3_EnsureEnableColoring()
+ if S:Ace3_CheckBoxIsEnable(self) then
+ local tristateOrDisabled = disabled or (self.tristate and self.checked == nil)
+ self:SetLabel((tristateOrDisabled and S.Ace3_L.Enable) or (self.checked and S.Ace3_EnableOn) or S.Ace3_EnableOff)
+ end
+end
- if not scrollBG.template then
- scrollBG:SetTemplate()
- end
+function S:Ace3_EditBoxSetTextInsets(l, r, t, b)
+ if l == 0 then self:SetTextInsets(3, r, t, b) end
+end
- S:HandleButton(widget.button)
- S:HandleScrollBar(scrollBar)
- scrollBG:Point("TOPRIGHT", scrollBar, "TOPLEFT", -3, 19)
- widget.scrollFrame:Point("BOTTOMRIGHT", scrollBG, "BOTTOMRIGHT", -4, 8)
- elseif TYPE == "CheckBox" then
- local check = widget.check
- local checkbg = widget.checkbg
- local highlight = widget.highlight
+function S:Ace3_EditBoxSetPoint(a, b, c, d, e)
+ if d == 7 then
+ self:Point(a, b, c, 0, e)
+ end
+end
- checkbg:CreateBackdrop()
- checkbg.backdrop:SetInside(widget.checkbg, 4, 4)
- checkbg.backdrop:SetFrameLevel(widget.checkbg.backdrop:GetFrameLevel() + 1)
- checkbg:SetTexture()
- checkbg.SetTexture = E.noop
+function S:Ace3_CheckBoxSetType(type)
+ if type == 'radio' then
+ self.checkbg:SetSize(20, 20)
+ end
+end
- check:SetParent(checkbg.backdrop)
+function S:Ace3_TabSetSelected(selected)
+ local bd = self.backdrop
+ if not bd then return end
- highlight:SetTexture()
- highlight.SetTexture = E.noop
+ if selected then
+ bd:SetBackdropBorderColor(1, .82, 0, 1)
+ bd:SetBackdropColor(1, .82, 0, 0.4)
- hooksecurefunc(widget, "SetDisabled", function(w, value)
- local isSwitch = S:Ace3_CheckBoxIsEnableSwitch(w)
+ if not self.wasRaised then
+ RaiseFrameLevel(self)
+ self.wasRaised = true
+ end
+ else
+ local br, bg, bb = unpack(E.media.bordercolor)
+ bd:SetBackdropBorderColor(br, bg, bb, 1)
- if value then
- if isSwitch then
- w:SetLabel(S.Ace3_L.RED_ENABLE)
- end
- end
- end)
+ local bdr, bdg, bdb = unpack(E.media.backdropcolor)
+ bd:SetBackdropColor(bdr, bdg, bdb, 1)
- hooksecurefunc(widget, "SetValue", function(w, value)
- local isSwitch = S:Ace3_CheckBoxIsEnableSwitch(w)
+ if self.wasRaised then
+ LowerFrameLevel(self)
+ self.wasRaised = nil
+ end
+ end
+end
- if isSwitch then
- w:SetLabel(value and S.Ace3_L.GREEN_ENABLE or S.Ace3_L.RED_ENABLE)
- end
+local buttonSetPointInProgress
+function S:Ace3_ButtonSetPoint(point, anchor, point2, xOffset, yOffset, skip)
+ -- Point из тулкита Sirus передает в SetPoint только 5 аргументов, поэтому
+ -- флаг skip из dev-версии теряется и хук срабатывал бы бесконечно
+ -- (переполнение стека). Защищаемся флагом повторного входа.
+ if not skip and point2 == 'TOPRIGHT' and not buttonSetPointInProgress then
+ buttonSetPointInProgress = true
+ pcall(function()
+ self:Point(point, anchor, point2, xOffset + 2, yOffset)
end)
+ buttonSetPointInProgress = nil
+ end
+end
- if E.private.skins.checkBoxSkin then
- checkbg.backdrop:SetInside(widget.checkbg, 5, 5)
- check:SetTexture(E.Media.Textures.Melli)
- check.SetTexture = E.noop
- check:SetInside(widget.checkbg.backdrop)
+function S:Ace3_SkinButton(button)
+ if not button.isSkinned then
+ S:HandleButton(button, true)
- hooksecurefunc(check, "SetDesaturated", function(chk, value)
- if value == true then
- chk:SetDesaturated(false)
- end
- end)
+ hooksecurefunc(button, 'SetPoint', S.Ace3_ButtonSetPoint)
+ end
+end
- hooksecurefunc(widget, "SetDisabled", function(w, value)
- local isSwitch = S:Ace3_CheckBoxIsEnableSwitch(w)
+function S:Ace3_SkinCheckBox(widget, check, checkbg, highlight)
+ if not checkbg.backdrop then
+ checkbg:CreateBackdrop(nil, nil, nil, nil, nil, nil, nil, nil, true)
+ checkbg.backdrop:SetInside(widget.checkbg, 4, 4)
- if value then
- if isSwitch then
- check:SetVertexColor(1.0, 0.2, 0.2, 1.0)
- else
- check:SetVertexColor(0.6, 0.6, 0.6, 0.8)
- end
- end
- end)
+ checkbg:SetTexture()
+ highlight:SetTexture()
- hooksecurefunc(widget, "SetValue", function(w, value)
- local isSwitch = S:Ace3_CheckBoxIsEnableSwitch(w)
+ check:SetParent(checkbg.backdrop)
- if value then
- if isSwitch then
- check:SetVertexColor(0.2, 1.0, 0.2, 1.0)
- else
- check:SetVertexColor(1, 0.82, 0, 0.8)
- end
- else
- if w.tristate and value == nil then
- check:SetVertexColor(0.6, 0.6, 0.6, 0.8)
- end
+ hooksecurefunc(widget, 'SetDisabled', S.Ace3_CheckBoxSetDisabled)
+ hooksecurefunc(widget, 'SetType', S.Ace3_CheckBoxSetType)
+
+ -- AceConfigDialog может задать текст метки после SetDisabled, поэтому
+ -- красим "Enable" и при изменении текста. Защита от цикла SetLabel -> SetLabel.
+ if not widget.__ace3LabelHooked then
+ widget.__ace3LabelHooked = true
+ local coloring
+ hooksecurefunc(widget, 'SetLabel', function(_, label)
+ if coloring then return end
+ Ace3_EnsureEnableColoring()
+ if S:Ace3_CheckBoxIsEnable(widget) then
+ coloring = true
+ local disabled = widget.disabled
+ local tristateOrDisabled = disabled or (widget.tristate and widget.checked == nil)
+ widget:SetLabel((tristateOrDisabled and S.Ace3_L.Enable) or (widget.checked and S.Ace3_EnableOn) or S.Ace3_EnableOff)
+ coloring = nil
end
end)
- else
- check:SetOutside(widget.checkbg.backdrop, 3, 3)
end
- elseif TYPE == "Dropdown" then
- local frame = widget.dropdown
- local button = widget.button
- local text = widget.text
- frame:StripTextures()
- S:HandleNextPrevButton(button, nil, {1, 0.8, 0})
+ if E.private.skins.checkBoxSkin then
+ S.Ace3_CheckBoxSetDesaturated(check, check:GetDesaturation())
+ hooksecurefunc(check, 'SetDesaturated', S.Ace3_CheckBoxSetDesaturated)
+
+ checkbg.backdrop:SetInside(widget.checkbg, 5, 5)
+ check:SetInside(widget.checkbg.backdrop)
- if not frame.backdrop then
- frame:CreateBackdrop()
+ check:SetTexture(E.Media.Textures.Melli)
+ check.SetTexture = E.noop
+ else
+ check:SetOutside(checkbg.backdrop, 3, 3)
end
- frame.backdrop:Point("TOPLEFT", 15, -2)
- frame.backdrop:Point("BOTTOMRIGHT", -21, 0)
+ checkbg.SetTexture = E.noop
+ highlight.SetTexture = E.noop
+ end
+end
- widget.label:ClearAllPoints()
- widget.label:Point("BOTTOMLEFT", frame.backdrop, "TOPLEFT", 2, 0)
+function S:Ace3_SkinTab(tab)
+ if not tab.backdrop then
+ tab:StripTextures()
+ tab:CreateBackdrop(nil, true, true)
+ tab.backdrop:Point('TOPLEFT', 10, -3)
+ tab.backdrop:Point('BOTTOMRIGHT', -10, 0)
+
+ if tab.text and tab.text.Point then -- возможна проблема с Pally Power
+ -- центрируем подпись внутри вкладки (в dev-версии это делается через
+ -- LEFT+RIGHT и центрированное выравнивание; тут делаем явно, чтобы
+ -- стандартный текст кнопок Sirus не прижимался влево)
+ tab.text:ClearAllPoints()
+ tab.text:SetJustifyH('CENTER')
+ tab.text:SetJustifyV('MIDDLE')
+ tab.text:Point('CENTER', tab, 'CENTER', 0, -1)
+ end
- button:ClearAllPoints()
- button:Point("TOPLEFT", frame.backdrop, "TOPRIGHT", -22, -2)
- button:Point("BOTTOMRIGHT", frame.backdrop, "BOTTOMRIGHT", -2, 2)
- button:SetParent(frame.backdrop)
+ hooksecurefunc(tab, 'SetSelected', S.Ace3_TabSetSelected)
+ end
+end
- text:ClearAllPoints()
- text:SetJustifyH("RIGHT")
- text:Point("RIGHT", button, "LEFT", -3, 0)
- text:Point("LEFT", frame.backdrop, "LEFT", 2, 0)
- text:SetParent(frame.backdrop)
- elseif TYPE == "LSM30_Font" or TYPE == "LSM30_Sound" or TYPE == "LSM30_Border" or TYPE == "LSM30_Background" or TYPE == "LSM30_Statusbar" then
- local frame = widget.frame
- local button = frame.dropButton
- local text = frame.text
+function S:Ace3_SkinEditBox(editbox, button, frame)
+ if not editbox.backdrop then
+ S:HandleEditBox(editbox)
+ S:HandleButton(button)
- frame:StripTextures()
+ button:Point('RIGHT', editbox.backdrop, 'RIGHT', -2, 0)
- S:HandleNextPrevButton(button, nil, {1, 0.8, 0})
+ hooksecurefunc(editbox, 'SetTextInsets', S.Ace3_EditBoxSetTextInsets)
+ hooksecurefunc(editbox, 'SetPoint', S.Ace3_EditBoxSetPoint)
- if not frame.backdrop then
- frame:CreateBackdrop()
- end
+ editbox.backdrop:Point('TOPLEFT', 0, -2)
+ editbox.backdrop:Point('BOTTOMRIGHT', -1, 0)
- frame.label:ClearAllPoints()
- frame.label:Point("BOTTOMLEFT", frame.backdrop, "TOPLEFT", 2, 0)
+ editbox.backdrop:SetParent(frame)
+ editbox:SetParent(editbox.backdrop)
+ end
+end
- text:ClearAllPoints()
- text:Point("RIGHT", button, "LEFT", -2, 0)
- text:Point("LEFT", frame.backdrop, "LEFT", 2, 0)
+local nextPrevColor = {1, .8, 0}
+function S:Ace3_RegisterAsWidget(widget)
+ local TYPE = widget.type
+ if TYPE == 'MultiLineEditBox' or TYPE == 'MultiLineEditBox-ElvUI' then
+ local scrollbar = widget.scrollBar
+ if scrollbar then
+ S:HandleButton(widget.button)
+ S:HandleScrollBar(scrollbar)
+
+ local bg = widget.scrollBG
+ if bg then
+ bg:SetTemplate()
+ bg:Point('TOPRIGHT', scrollbar, 'TOPLEFT', -2, 19)
+ bg:Point('BOTTOMLEFT', widget.button, 'TOPLEFT')
+
+ scrollbar:Point('RIGHT', widget.frame, 'RIGHT', -4)
+ widget.scrollFrame:Point('BOTTOMRIGHT', bg, 'BOTTOMRIGHT', -4, 8)
+ end
+ end
+ elseif TYPE == 'CheckBox' then
+ S:Ace3_SkinCheckBox(widget, widget.check, widget.checkbg, widget.highlight)
+ elseif TYPE == 'Dropdown' or TYPE == 'Dropdown-ElvUI' or TYPE == 'LQDropdown' then
+ local frame = widget.dropdown
- button:ClearAllPoints()
- button:Point("TOPLEFT", frame.backdrop, "TOPRIGHT", -22, -2)
- button:Point("BOTTOMRIGHT", frame.backdrop, "BOTTOMRIGHT", -2, 2)
+ frame:StripTextures()
+ frame:CreateBackdrop()
+ frame.backdrop:Point('TOPLEFT', 15, -2)
+ frame.backdrop:Point('BOTTOMRIGHT', -21, 0)
+
+ local label = widget.label
+ if label then
+ label:ClearAllPoints()
+ label:Point('BOTTOMLEFT', frame.backdrop, 'TOPLEFT', 2, 0)
+ end
- frame.backdrop:Point("TOPLEFT", 0, -21)
- frame.backdrop:Point("BOTTOMRIGHT", -4, -1)
+ local button = widget.button
+ if button then
+ S:HandleNextPrevButton(button, nil, nextPrevColor)
- if TYPE == "LSM30_Sound" then
- widget.soundbutton:SetParent(frame.backdrop)
- widget.soundbutton:ClearAllPoints()
- widget.soundbutton:Point("LEFT", frame.backdrop, "LEFT", 2, 0)
- elseif TYPE == "LSM30_Statusbar" then
- widget.bar:SetParent(frame.backdrop)
- widget.bar:ClearAllPoints()
- widget.bar:Point("TOPLEFT", frame.backdrop, "TOPLEFT", 2, -2)
- widget.bar:Point("BOTTOMRIGHT", button, "BOTTOMLEFT", -1, 0)
+ button:ClearAllPoints()
+ button:Point('TOPLEFT', frame.backdrop, 'TOPRIGHT', -22, -2)
+ button:Point('BOTTOMRIGHT', frame.backdrop, 'BOTTOMRIGHT', -2, 2)
+ button:SetParent(frame.backdrop)
end
- button:SetParent(frame.backdrop)
- text:SetParent(frame.backdrop)
+ local text = widget.text
+ if text then
+ text:ClearAllPoints()
+ text:SetJustifyH('RIGHT')
+ text:Point('RIGHT', button, 'LEFT', -3, 0)
+ text:Point('LEFT', frame.backdrop, 'LEFT', 2, 0)
+ text:SetParent(frame.backdrop)
+ end
+ elseif TYPE == 'LSM30_Font' or TYPE == 'LSM30_Sound' or TYPE == 'LSM30_Border' or TYPE == 'LSM30_Background' or TYPE == 'LSM30_Statusbar' then
+ local frame = widget.frame
- button:HookScript("OnClick", S.Ace3_SkinDropdownPullout)
- elseif TYPE == "EditBox" then
- local frame = widget.editbox
- local button = widget.button
- S:HandleEditBox(frame)
- S:HandleButton(button)
+ frame:StripTextures()
+ frame:CreateBackdrop(nil, nil, nil, nil, nil, nil, nil, nil, true)
+ frame.backdrop:Point('TOPLEFT', 0, -21)
+ frame.backdrop:Point('BOTTOMRIGHT', -4, -1)
+
+ local label = frame.label
+ if label then
+ label:ClearAllPoints()
+ label:Point('BOTTOMLEFT', frame.backdrop, 'TOPLEFT', 2, 0)
+ end
- hooksecurefunc(frame, "SetTextInsets", function(fr, l, r, t, b)
- if l == 0 then
- fr:SetTextInsets(3, r, t, b)
+ local button = frame.dropButton
+ if button then
+ local text = frame.text
+ if text then
+ text:ClearAllPoints()
+ text:Point('RIGHT', button, 'LEFT', -2, 0)
+ text:Point('LEFT', frame.backdrop, 'LEFT', 2, 0)
+ text:SetParent(frame.backdrop)
end
- end)
- button:Point("RIGHT", frame.backdrop, "RIGHT", -2, 0)
+ if TYPE == 'LSM30_Statusbar' then
+ S:HandleNextPrevButton(button, nil, nextPrevColor, true)
- hooksecurefunc(frame, "SetPoint", function(fr, a, b, c, d, e)
- if d == 7 then
- fr:Point(a, b, c, 0, e)
+ local bar = widget.bar
+ if bar then
+ bar:SetParent(frame.backdrop)
+ bar:ClearAllPoints()
+ bar:Point('TOPLEFT', frame.backdrop, 'TOPLEFT', 1, -1)
+ bar:Point('BOTTOMRIGHT', frame.backdrop, 'BOTTOMRIGHT', -1, 1)
+ end
+ else
+ S:HandleNextPrevButton(button, nil, nextPrevColor)
+
+ local soundbutton = TYPE == 'LSM30_Sound' and widget.soundbutton
+ if soundbutton then
+ soundbutton:SetParent(frame.backdrop)
+ soundbutton:ClearAllPoints()
+ soundbutton:Point('LEFT', frame.backdrop, 'LEFT', 2, 0)
+ end
end
- end)
- frame.backdrop:Point("TOPLEFT", 0, -2)
- frame.backdrop:Point("BOTTOMRIGHT", -1, 0)
- frame.backdrop:SetParent(widget.frame)
- frame:SetParent(frame.backdrop)
- elseif TYPE == "Button" or TYPE == "Button-ElvUI" then
- local frame = widget.frame
- S:HandleButton(frame, true, nil, true)
- frame.backdrop:SetInside()
+ button:ClearAllPoints()
+ button:Point('TOPLEFT', frame.backdrop, 'TOPRIGHT', -22, -2)
+ button:Point('BOTTOMRIGHT', frame.backdrop, 'BOTTOMRIGHT', -2, 2)
+ button:SetParent(frame.backdrop)
+ button:HookScript('OnClick', S.Ace3_SkinDropdown)
+ end
+ elseif TYPE == 'EditBox' or TYPE == 'EditBox-ElvUI' then
+ S:Ace3_SkinEditBox(widget.editbox, widget.button, widget.frame)
+ elseif TYPE == 'Button' or TYPE == 'Button-ElvUI' then
+ S:Ace3_SkinButton(widget.frame)
+ elseif TYPE == 'Slider' or TYPE == 'Slider-ElvUI' then
+ local slider = widget.slider
+ S:HandleSliderFrame(slider)
- widget.text:SetParent(frame.backdrop)
- elseif TYPE == "Slider" or TYPE == "Slider-ElvUI" then
- local frame = widget.slider
local editbox = widget.editbox
- local lowtext = widget.lowtext
- local hightext = widget.hightext
-
- S:HandleSliderFrame(frame)
+ if editbox then
+ editbox:SetTemplate()
+ editbox:Height(15)
+ editbox:Point('TOP', slider, 'BOTTOM', 0, -1)
+ end
- editbox:SetTemplate()
- editbox:Height(15)
- editbox:Point("TOP", frame, "BOTTOM", 0, -1)
+ local lowtext = widget.lowtext
+ if lowtext then
+ lowtext:Point('TOPLEFT', slider, 'BOTTOMLEFT', 2, -2)
+ end
- lowtext:Point("TOPLEFT", frame, "BOTTOMLEFT", 2, -2)
- hightext:Point("TOPRIGHT", frame, "BOTTOMRIGHT", -2, -2)
+ local hightext = widget.hightext
+ if hightext then
+ hightext:Point('TOPRIGHT', slider, 'BOTTOMRIGHT', -2, -2)
+ end
- hooksecurefunc(widget, "SetDisabled", function(w, disabled)
+ hooksecurefunc(widget, 'SetDisabled', function(w, disabled)
local thumbTex = w.slider:GetThumbTexture()
if disabled then
thumbTex:SetVertexColor(0.6, 0.6, 0.6, 0.8)
@@ -290,173 +385,275 @@ function S:Ace3_RegisterAsWidget(widget)
thumbTex:SetVertexColor(1, 0.82, 0, 0.8)
end
end)
- elseif TYPE == "Keybinding" then
+ elseif TYPE == 'Keybinding' then
local button = widget.button
- local msgframe = widget.msgframe
- local msg = widget.msgframe.msg
- S:HandleButton(button)
- msgframe:StripTextures()
- msgframe:CreateBackdrop("Default", true)
- msgframe.backdrop:SetInside()
- msgframe:SetToplevel(true)
-
- msg:ClearAllPoints()
- msg:Point("LEFT", 10, 0)
- msg:Point("RIGHT", -10, 0)
- msg:SetJustifyV("MIDDLE")
- msg:Width(msg:GetWidth() + 10)
- elseif (TYPE == "ColorPicker" or TYPE == "ColorPicker-ElvUI") then
- local frame = widget.frame
- local colorSwatch = widget.colorSwatch
-
- if not frame.backdrop then
- frame:CreateBackdrop()
+ if button then
+ S:HandleButton(button, true)
end
+ local msgframe = widget.msgframe
+ if msgframe then
+ msgframe:StripTextures()
+ msgframe:SetTemplate('Transparent')
+
+ local msg = msgframe.msg
+ if msg then
+ msg:ClearAllPoints()
+ msg:Point('CENTER')
+ end
+ end
+ elseif TYPE == 'ColorPicker' or TYPE == 'ColorPicker-ElvUI' then
+ local frame = widget.frame
+ frame:CreateBackdrop()
frame.backdrop:Size(24, 16)
frame.backdrop:ClearAllPoints()
- frame.backdrop:Point("LEFT", frame, "LEFT", 4, 0)
- frame.backdrop:SetBackdropColor(0, 0, 0, 0)
- frame.backdrop.SetBackdropColor = E.noop
+ frame.backdrop:Point('LEFT', frame, 'LEFT', 4, 0)
- colorSwatch:SetTexture(E.media.blankTex)
- colorSwatch:ClearAllPoints()
- colorSwatch:SetParent(frame.backdrop)
- colorSwatch:SetInside(frame.backdrop)
+ local colorSwatch = widget.colorSwatch
+ if colorSwatch then
+ colorSwatch:SetTexture(E.Media.Textures.White8x8)
+ colorSwatch:ClearAllPoints()
+ colorSwatch:SetParent(frame.backdrop)
+ colorSwatch:SetInside(frame.backdrop)
+
+ local bg = colorSwatch.background
+ if bg then
+ bg:SetTexture(0, 0, 0, 0)
+ end
- if colorSwatch.background then
- colorSwatch.background:SetTexture(0, 0, 0, 0)
+ local checkers = colorSwatch.checkers
+ if checkers then
+ checkers:ClearAllPoints()
+ checkers:SetParent(frame.backdrop)
+ checkers:SetInside(frame.backdrop)
+ end
end
+ elseif TYPE == 'Icon' then
+ widget.frame:StripTextures()
+ elseif TYPE == 'Dropdown-Pullout' then
+ local frame = widget.frame
+ if frame then
+ frame:SetTemplate(nil, true)
+ end
+
+ local slider = widget.slider
+ if slider then
+ slider:SetTemplate()
+ slider:SetThumbTexture(E.Media.Textures.White8x8)
- if colorSwatch.checkers then
- colorSwatch.checkers:ClearAllPoints()
- colorSwatch.checkers:SetDrawLayer("ARTWORK")
- colorSwatch.checkers:SetParent(frame.backdrop)
- colorSwatch.checkers:SetInside(frame.backdrop)
+ local thumb = slider:GetThumbTexture()
+ if thumb then
+ thumb:SetVertexColor(1, .82, 0, 0.8)
+ end
end
- elseif TYPE == "Icon" then
- widget.frame:StripTextures()
- elseif TYPE == "Dropdown-Pullout" then
- local pullout = widget
- if pullout.frame then
- pullout.frame:SetTemplate(nil, true)
+ end
+end
+
+function S:Ace3_CreateTab(id)
+ local tab = self.old_CreateTab(self, id)
+ S:Ace3_SkinTab(tab)
+
+ return tab
+end
+
+function S:Ace3_RefreshTree(scrollToSelection)
+ self.old_RefreshTree(self, scrollToSelection)
+
+ local tree = self.tree
+ if not tree then return end
+
+ local border = self.border
+ local treeframe = self.treeframe
+ if border and treeframe then
+ border:ClearAllPoints()
+
+ local userdata = self.userdata
+ local dataoption = userdata and userdata.option
+ if dataoption and dataoption.childGroups == 'ElvUI_HiddenTree' then
+ border:Point('TOPLEFT', treeframe, 'TOPRIGHT', 1, 13)
+ border:Point('BOTTOMRIGHT', self.frame, 'BOTTOMRIGHT', 6, 0)
+
+ treeframe:Point('TOPLEFT', 0, 0)
+
+ if treeframe:IsShown() then
+ treeframe:Hide()
+ end
+
+ return -- дальше не идем
else
- pullout:SetTemplate(nil, true)
- end
+ border:Point('TOPLEFT', treeframe, 'TOPRIGHT')
+ border:Point('BOTTOMRIGHT', self.frame)
- if pullout.slider then
- pullout.slider:SetTemplate()
- pullout.slider:SetThumbTexture(E.Media.Textures.White8x8)
- pullout.slider:GetThumbTexture():SetVertexColor(1, .82, 0, 0.8)
+ treeframe:Point('TOPLEFT', 0, -2)
+
+ if not treeframe:IsShown() then
+ treeframe:Show()
+ end
end
end
- return oldRegisterAsWidget(self, widget)
+ if not E.private.skins.ace3.enable then return end
+
+ local lines = self.lines
+ local buttons = self.buttons
+ if lines and buttons then
+ local status = self.status or self.localstatus
+ local offset = status.scrollvalue
+ local groupstatus = status.groups
+
+ for i = offset + 1, #lines do
+ local button = buttons[i - offset]
+ if button then
+ if button.highlight then
+ button.highlight:SetVertexColor(1.0, 0.9, 0.0, 0.8)
+ end
+
+ local line = lines[i]
+ local unique = line and line.uniquevalue
+ if unique and groupstatus[unique] then
+ button.toggle:SetNormalTexture(E.Media.Textures.Minus)
+ button.toggle:SetPushedTexture(E.Media.Textures.Minus)
+ else
+ button.toggle:SetNormalTexture(E.Media.Textures.Plus)
+ button.toggle:SetPushedTexture(E.Media.Textures.Plus)
+ end
+
+ button.toggle:SetHighlightTexture(E.ClearTexture)
+ end
+ end
+ end
end
function S:Ace3_RegisterAsContainer(widget)
- if not E.private.skins.ace3.enable then
- return oldRegisterAsContainer(self, widget)
- end
local TYPE = widget.type
- if TYPE == "ScrollFrame" then
+ if TYPE == 'ScrollFrame' then
S:HandleScrollBar(widget.scrollbar)
- widget.scrollbar:Point("TOPLEFT", widget.scrollframe, "TOPRIGHT", 8, -16)
- widget.scrollbar:Point("BOTTOMLEFT", widget.scrollframe, "BOTTOMRIGHT", 8, 16)
- elseif TYPE == "InlineGroup" or TYPE == "TreeGroup" or TYPE == "TabGroup" or TYPE == "Frame" or TYPE == "DropdownGroup" or TYPE == "Window" then
+ elseif TYPE == 'InlineGroup' or TYPE == 'TreeGroup' or TYPE == 'TabGroup' or TYPE == 'Frame' or TYPE == 'DropdownGroup' or TYPE == 'Window' then
local frame = widget.content:GetParent()
- if TYPE == "Frame" then
+ if TYPE == 'Frame' then
frame:StripTextures()
- for i = 1, frame:GetNumChildren() do
- local child = select(i, frame:GetChildren())
- if child:IsObjectType("Button") and child:GetText() then
+
+ for _, child in next, { frame:GetChildren() } do
+ if child:IsObjectType('Button') and child:GetText() then
S:HandleButton(child)
else
child:StripTextures()
end
end
- elseif TYPE == "Window" then
+ elseif TYPE == 'Window' then
frame:StripTextures()
+
S:HandleCloseButton(frame.obj.closebutton)
end
- if TYPE == "InlineGroup" then
- frame:SetTemplate("Transparent")
+ frame:SetTemplate('Transparent')
+
+ if TYPE == 'InlineGroup' then -- 'Window' это другой тип
frame.ignoreBackdropColors = true
- frame:SetBackdropColor(0, 0, 0, 0.25)
- else
- frame:SetTemplate("Transparent")
+ S.Ace3_BackdropColor(frame)
end
if widget.treeframe then
- widget.treeframe:SetTemplate("Transparent")
- frame:Point("TOPLEFT", widget.treeframe, "TOPRIGHT", 1, 0)
-
- local oldRefreshTree = widget.RefreshTree
- widget.RefreshTree = function(wdg, scrollToSelection)
- oldRefreshTree(wdg, scrollToSelection)
- if not wdg.tree then return end
- local status = wdg.status or wdg.localstatus
- local groupstatus = status.groups
- local lines = wdg.lines
- local buttons = wdg.buttons
- local offset = status.scrollvalue
-
- for i = offset + 1, #lines do
- local button = buttons[i - offset]
- if button then
- button.highlight:SetTexture(E.Media.Textures.Highlight)
- button.highlight:SetVertexColor(1, 0.82, 0, 0.35)
- button.highlight:SetPoint("TOPLEFT", 0, 0)
- button.highlight:Point("BOTTOMRIGHT", 0, 1)
-
- button.toggle:SetHighlightTexture("")
-
- if groupstatus[lines[i].uniquevalue] then
- button.toggle:SetNormalTexture(E.Media.Textures.Minus)
- button.toggle:SetPushedTexture(E.Media.Textures.Minus)
- else
- button.toggle:SetNormalTexture(E.Media.Textures.Plus)
- button.toggle:SetPushedTexture(E.Media.Textures.Plus)
- end
- end
- end
- end
+ widget.treeframe:SetTemplate('Transparent')
end
- if TYPE == "TabGroup" then
- local oldCreateTab = widget.CreateTab
- widget.CreateTab = function(wdg, id)
- local tab = oldCreateTab(wdg, id)
- tab:StripTextures()
- tab:CreateBackdrop("Transparent")
- tab.backdrop:Point("TOPLEFT", 10, -3)
- tab.backdrop:Point("BOTTOMRIGHT", -10, 0)
-
- tab:SetHitRectInsets(10, 10, 3, 0)
+ if TYPE == 'TabGroup' then
+ if not widget.old_CreateTab then
+ widget.old_CreateTab = widget.CreateTab
+ widget.CreateTab = S.Ace3_CreateTab
+ end
- return tab
+ if widget.tabs then
+ for _, n in next, widget.tabs do
+ S:Ace3_SkinTab(n)
+ end
end
end
if widget.scrollbar then
S:HandleScrollBar(widget.scrollbar)
- widget.scrollbar:Point("TOPRIGHT", -4, -23)
- widget.scrollbar:Point("BOTTOMRIGHT", -4, 23)
end
- elseif TYPE == "SimpleGroup" then
+ elseif TYPE == 'SimpleGroup' then
local frame = widget.content:GetParent()
- frame:SetTemplate("Transparent", nil, true)
+ frame:SetTemplate('Transparent', nil, true)
frame.ignoreBackdropColors = true
frame:SetBackdropColor(0, 0, 0, 0.25)
end
- return oldRegisterAsContainer(self, widget)
+ if widget.sizer_se then
+ for _, Region in next, { widget.sizer_se:GetRegions() } do
+ if Region:IsObjectType('Texture') then
+ Region:SetTexture([[Interface\Tooltips\UI-Tooltip-Border]])
+ end
+ end
+ end
end
function S:Ace3_StyleTooltip()
- if not self then return end
- self:SetTemplate("Transparent", nil, true)
+ if E.private.skins.blizzard.enable and E.private.skins.blizzard.tooltip then
+ self:SetTemplate('Transparent')
+ end
+end
+
+function S:Ace3_StylePopup()
+ if E.private.skins.ace3.enable then
+ self:SetTemplate('Transparent', nil, true)
+ self:GetChildren():StripTextures()
+
+ S:HandleButton(self.accept, true)
+ S:HandleButton(self.cancel, true)
+ end
+end
+
+-- последние сырые реализации методов регистрации AceGUI. Обертки ниже всегда
+-- вызывают ТЕКУЩУЮ реализацию, поэтому новая копия библиотеки (загруженная
+-- другим аддоном после бампа minor в LibStub) продолжает работать
+S.Ace3_Impl = {}
+
+S.Ace3_Wrappers = {
+ RegisterAsContainer = function(s, w, ...)
+ local impl = S.Ace3_Impl.RegisterAsContainer
+ if impl then
+ -- скин не должен ломать вызов библиотеки. Передаем ПОЛНЫЙ список
+ -- аргументов (self + widget): S.Ace3_RegisterAsContainer объявлена
+ -- с двоеточием и ждет (self, widget)
+ local rest = { s, w, ... }
+ pcall(function()
+ if E.private and E.private.skins and E.private.skins.ace3.enable then
+ S.Ace3_RegisterAsContainer(unpack(rest))
+ end
+
+ if w and w.treeframe and not w.old_RefreshTree then
+ w.old_RefreshTree = w.RefreshTree
+ w.RefreshTree = S.Ace3_RefreshTree
+ end
+ end)
+
+ return impl(s, w, ...)
+ end
+ end,
+ RegisterAsWidget = function(...)
+ local impl = S.Ace3_Impl.RegisterAsWidget
+ if impl then
+ local rest = { ... }
+ pcall(function()
+ if E.private and E.private.skins and E.private.skins.ace3.enable then
+ S.Ace3_RegisterAsWidget(unpack(rest))
+ end
+ end)
+
+ return impl(...)
+ end
+ end,
+}
+
+function S:Ace3_MetaTable(lib)
+ local t = getmetatable(lib)
+ if t then
+ t.__newindex = S.Ace3_MetaIndex
+ else
+ setmetatable(lib, {__newindex = S.Ace3_MetaIndex})
+ end
end
function S:Ace3_SkinTooltip(lib, minor) -- lib: AceConfigDialog or AceGUI
@@ -465,34 +662,158 @@ function S:Ace3_SkinTooltip(lib, minor) -- lib: AceConfigDialog or AceGUI
-- inside of its own function.
if not lib or (minor and minor < minorConfigDialog) then return end
- if lib.tooltip and not S:IsHooked(lib.tooltip, "OnShow") then
- S:SecureHookScript(lib.tooltip, "OnShow", S.Ace3_StyleTooltip)
+ if not lib.tooltip then
+ S:Ace3_MetaTable(lib)
+ else
+ if lib.tooltip and not S:IsHooked(lib.tooltip, 'OnShow') then
+ S:SecureHookScript(lib.tooltip, 'OnShow', S.Ace3_StyleTooltip)
+ end
+ if lib.popup and not S:IsHooked(lib.popup, 'OnShow') then
+ S:SecureHookScript(lib.popup, 'OnShow', S.Ace3_StylePopup)
+ end
end
+end
+
+function S:Ace3_MetaIndex(k, v)
+ if k == 'tooltip' then
+ rawset(self, k, v)
+
+ S:SecureHookScript(v, 'OnShow', S.Ace3_StyleTooltip)
+ elseif k == 'popup' then
+ rawset(self, k, v)
+
+ S:SecureHookScript(v, 'OnShow', S.Ace3_StylePopup)
+ elseif k == 'RegisterAsContainer' or k == 'RegisterAsWidget' then
+ -- обновляем реализацию, которую должна вызывать обертка. Обнуление
+ -- (временный nil в HookAce3) не должно оставить сломанную обертку:
+ -- методы обязаны работать для всех аддонов, использующих AceGUI
+ -- (Gladdy, Spy, Details и др.). Клиент Sirus не позволяет вешать поля
+ -- на функции, поэтому обертки опознаются по идентичности
+ if type(v) == 'function' and v ~= S.Ace3_Wrappers[k] then
+ S.Ace3_Impl[k] = v
+ end
- if lib.popup and not lib.popup.template then -- StaticPopup
- lib.popup:SetTemplate("Transparent")
- lib.popup:GetChildren():StripTextures()
- S:HandleButton(lib.popup.accept, true)
- S:HandleButton(lib.popup.cancel, true)
+ rawset(self, k, S.Ace3_Impl[k] and S.Ace3_Wrappers[k] or v)
+ else
+ rawset(self, k, v)
end
end
-function S:HookAce3(lib, minor) -- lib: AceGUI
+function S:Ace3_ColorizeEnable(L)
+ S.Ace3_L = L
+
+ -- особая подсветка галочки "Enable"
+ S.Ace3_EnableMatch = '^|?c?[Ff]?[Ff]?%x?%x?%x?%x?%x?%x?' .. E:EscapeString(S.Ace3_L.Enable) .. '|?r?$'
+ S.Ace3_EnableOff = format('|cffff3333%s|r', S.Ace3_L.Enable)
+ S.Ace3_EnableOn = format('|cff33ff33%s|r', S.Ace3_L.Enable)
+end
+
+local lastMinor = 0
+function S:HookAce3(lib, minor, early) -- lib: AceGUI
if not lib or (not minor or minor < minorGUI) then return end
+ -- обновляем реализации, которые вызывают наши обертки. Новая копия
+ -- библиотеки могла перезаписать нашу обертку после бампа minor
+ local curContainer, curWidget = lib.RegisterAsContainer, lib.RegisterAsWidget
+ if curContainer and curContainer ~= S.Ace3_Wrappers.RegisterAsContainer then
+ S.Ace3_Impl.RegisterAsContainer = curContainer
+ end
+ if curWidget and curWidget ~= S.Ace3_Wrappers.RegisterAsWidget then
+ S.Ace3_Impl.RegisterAsWidget = curWidget
+ end
+
+ local oldMinor = lastMinor
+ if lastMinor < minor then
+ lastMinor = minor
+ end
+ if early or oldMinor ~= minor then
+ lib.RegisterAsContainer = nil
+ lib.RegisterAsWidget = nil
+ end
+
+ if not lib.RegisterAsWidget then
+ S:Ace3_MetaTable(lib)
+ end
+
if not S.Ace3_L then
- S.Ace3_L = E.Libs.ACL:GetLocale("ElvUI", E.global.general.locale or "enUS")
+ -- E.global заполняется только в OnInitialize, поэтому тут его может ещё
+ -- не быть. Сбой локали не должен прерывать хук, иначе методы регистрации
+ -- пропадут для всех остальных аддонов
+ local locale = E.global and E.global.general and E.global.general.locale or "enUS"
+ pcall(function()
+ S.Ace3_L = E.Libs.ACL:GetLocale("ElvUI", locale)
+ S:Ace3_ColorizeEnable(S.Ace3_L)
+ end)
+ end
+
+ -- никогда не оставляем методы регистрации пустыми: (пере)устанавливаем
+ -- обертки. RegisterAsContainer/RegisterAsWidget вызываются каждым
+ -- конструктором виджета AceGUI, nil упадет у всех пользователей AceGUI
+ if S.Ace3_Impl.RegisterAsContainer then
+ lib.RegisterAsContainer = S.Ace3_Wrappers.RegisterAsContainer
+ end
+ if S.Ace3_Impl.RegisterAsWidget then
+ lib.RegisterAsWidget = S.Ace3_Wrappers.RegisterAsWidget
+ end
+end
+
+do -- ранняя загрузка скинов
+ local Libraries = {
+ ['AceGUI'] = true,
+ ['AceConfigDialog'] = true,
+ ['AceConfigDialog-3.0-ElvUI'] = true,
+ }
+
+ S.EarlyAceWidgets = {}
+ S.EarlyAceTooltips = {}
+
+ local LibStub = LibStub
+ local numEnding = '%-[%d%.]+$'
+ function S:LibStub_NewLib(major)
+ local early = not E.initialized
+ local n = gsub(major, numEnding, '')
+ if Libraries[n] then
+ if n == 'AceGUI' then
+ S:HookAce3(LibStub.libs[major], LibStub.minors[major], early)
+ if early then
+ tinsert(S.EarlyAceTooltips, major)
+ else
+ S:Ace3_SkinTooltip(LibStub.libs[major])
+ end
+ elseif n == 'AceConfigDialog' or n == 'AceConfigDialog-3.0-ElvUI' then
+ if early then
+ tinsert(S.EarlyAceTooltips, major)
+ else
+ S:Ace3_SkinTooltip(LibStub.libs[major], LibStub.minors[major])
+ end
+ end
+ end
+ end
+
+ local findWidget
+ local function earlyWidget(y)
+ if y.children then findWidget(y.children) end
+ if y.frame and (y.base and y.base.Release) then
+ tinsert(S.EarlyAceWidgets, y)
+ end
end
- if lib.RegisterAsWidget ~= S.Ace3_RegisterAsWidget then
- oldRegisterAsWidget = lib.RegisterAsWidget
- lib.RegisterAsWidget = S.Ace3_RegisterAsWidget
+ findWidget = function(x)
+ for _, y in ipairs(x) do
+ earlyWidget(y)
+ end
end
- if lib.RegisterAsContainer ~= S.Ace3_RegisterAsContainer then
- oldRegisterAsContainer = lib.RegisterAsContainer
- lib.RegisterAsContainer = S.Ace3_RegisterAsContainer
+ for n in next, LibStub.libs do
+ if n == 'AceGUI-3.0' then
+ for _, x in next, { UIParent:GetChildren() } do
+ if x and x.obj then earlyWidget(x.obj) end
+ end
+ end
+ if Libraries[gsub(n, numEnding, '')] then
+ S:LibStub_NewLib(n)
+ end
end
- S:Ace3_SkinTooltip(lib)
-end
\ No newline at end of file
+ hooksecurefunc(LibStub, 'NewLibrary', S.LibStub_NewLib)
+end
diff --git a/Modules/Skins/Blizzard/CombatLog.lua b/Modules/Skins/Blizzard/CombatLog.lua
new file mode 100644
index 000000000..ee01ca301
--- /dev/null
+++ b/Modules/Skins/Blizzard/CombatLog.lua
@@ -0,0 +1,50 @@
+local E, L, V, P, G = unpack(select(2, ...)) --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
+local S = E:GetModule("Skins")
+local CH = E:GetModule("Chat")
+local LSM = E.Libs.LSM
+
+--Lua functions
+local _G = _G
+local ipairs = ipairs
+local hooksecurefunc = hooksecurefunc
+
+local function StyleButtons()
+ for index in ipairs(_G.Blizzard_CombatLog_Filters.filters) do
+ local button = _G["CombatLogQuickButtonFrameButton"..index]
+ local text = button and button:GetFontString()
+ if text then
+ text:FontTemplate(LSM:Fetch("font", CH.db.tabFont), CH.db.tabFontSize, CH.db.tabFontOutline)
+ end
+ end
+end
+
+-- credit: Aftermathh, edited by Simpy
+S:AddCallbackForAddon("Blizzard_CombatLog", "Skin_CombatLog", function()
+ if not E.private.chat.enable then return end
+ -- окно всегда включено вместе с чатом, тут обрабатываем только верхнюю панель лога боя
+
+ hooksecurefunc("Blizzard_CombatLog_Update_QuickButtons", StyleButtons)
+ StyleButtons()
+
+ local bar = _G.CombatLogQuickButtonFrame_Custom
+ if not bar then return end
+
+ bar:StripTextures()
+ bar:SetTemplate("Transparent")
+
+ bar:ClearAllPoints()
+ bar:Point("BOTTOMLEFT", _G.ChatFrame2, "TOPLEFT", -3, 2)
+ bar:Point("BOTTOMRIGHT", _G.ChatFrame2, "TOPRIGHT", 3, 0)
+
+ local progress = _G.CombatLogQuickButtonFrame_CustomProgressBar
+ progress:SetStatusBarTexture(E.media.normTex)
+ progress:SetInside(bar)
+
+ S:HandleNextPrevButton(_G.CombatLogQuickButtonFrame_CustomAdditionalFilterButton)
+ _G.CombatLogQuickButtonFrame_CustomAdditionalFilterButton:ClearAllPoints()
+ _G.CombatLogQuickButtonFrame_CustomAdditionalFilterButton:Point("TOPRIGHT", bar, "TOPRIGHT", -2, -2)
+ _G.CombatLogQuickButtonFrame_CustomAdditionalFilterButton:SetHitRectInsets(0, 0, 0, 0)
+ _G.CombatLogQuickButtonFrame_CustomAdditionalFilterButton:OffsetFrameLevel(2, bar)
+ _G.CombatLogQuickButtonFrame_CustomAdditionalFilterButton:Size(20)
+ _G.CombatLogQuickButtonFrame_CustomTexture:Hide()
+end)
diff --git a/Modules/Skins/Blizzard/Debug.lua b/Modules/Skins/Blizzard/Debug.lua
index ebf2dd735..cab2b8c89 100644
--- a/Modules/Skins/Blizzard/Debug.lua
+++ b/Modules/Skins/Blizzard/Debug.lua
@@ -2,8 +2,12 @@ local E, L, V, P, G = unpack(select(2, ...)) --Import: Engine, Locales, PrivateD
local S = E:GetModule("Skins")
--Lua functions
+local _G = _G
local unpack = unpack
+local ipairs = ipairs
+local hooksecurefunc = hooksecurefunc
--WoW API / Variables
+local PlaySound = PlaySound
S:AddCallbackForAddon("Blizzard_DebugTools", "Skin_Blizzard_DebugTools", function()
if not E.private.skins.blizzard.enable or not E.private.skins.blizzard.debug then return end
@@ -57,4 +61,115 @@ S:AddCallbackForAddon("Blizzard_DebugTools", "Skin_Blizzard_DebugTools", functio
end)
S:HandleCloseButton(EventTraceFrameCloseButton, EventTraceFrame)
+end)
+
+S:AddCallbackForAddon("ViragDevTool", "Skin_ViragDevTool", function()
+ local ViragDevTool = _G.ViragDevTool
+ local color = E:ClassColor(E.myclass)
+
+ local frames = {
+ _G.ViragDevToolFrame,
+ _G.ViragDevToolFrameSideBar,
+ _G.ViragDevToolOptionsMainFrame,
+ _G.ViragDevToolFrameScrollFrame,
+ _G.ViragDevToolFrameSideBarScrollFrame,
+ }
+
+ for _, frame in ipairs(frames) do
+ if frame then
+ frame:StripTextures()
+ frame:SetTemplate("Transparent")
+ if frame:IsObjectType("ScrollFrame") then
+ frame:StripTextures()
+ end
+ end
+ end
+
+ local sideButtons = {
+ _G.ViragDevToolFrameSideBarHistoryButton,
+ _G.ViragDevToolFrameSideBarEventsButton,
+ _G.ViragDevToolFrameSideBarLogButton,
+ _G.ViragDevToolFrameClearButton,
+ _G.ViragDevToolFrameAddGlobalButton,
+ _G.ViragDevToolFrameFrameStack,
+ _G.ViragDevToolFrameHelpButton,
+ _G.ViragDevToolFrameFNCallLabelButton,
+
+ _G.VDTFrameColorReset,
+ }
+
+ hooksecurefunc(ViragDevTool, "UpdateSideBarUI", function(self)
+ local mainFrame = self.wndRef
+ local sideFrame = mainFrame.sideFrame
+
+ for _, button in ipairs(sideButtons) do
+ local buttonChecked = button:GetName().."Checked"
+ local checked = _G[buttonChecked]
+ if button and not checked then
+ S:HandleButton(button, true, nil, nil, true)
+ else
+ button:StripTextures(true)
+
+ S:HandleButton(button, nil, nil, nil, true)
+
+ checked:SetVertexColor(color.r, color.g, color.b)
+ button:OffsetFrameLevel(2)
+ if button:GetChecked() then
+ button.backdrop:SetBackdropColor(color.r, color.g, color.b)
+ else
+ button.backdrop:SetBackdropColor(unpack(E.media.backdropfadecolor))
+ end
+ end
+ end
+
+ for i = 1, sideFrame:GetNumChildren() do
+ local button = _G["VDTColorPickerFrameItem"..i.."Button"]
+ if button then
+ S:HandleButton(button, true, nil, nil, true)
+
+ button.colorTexture:SetTexture(button:GetFontString():GetTextColor())
+ end
+ end
+ end)
+
+ E:Delay(0.1, function()
+ for i = 1, 23 do
+ local actionButton = _G["ViragDevToolFrameSideBarScrollFrameButton"..i.."ActionButton"]
+ if actionButton then
+ S:HandleCloseButton(actionButton)
+ end
+ end
+ end)
+
+ local frame = _G.ViragDevToolFrameSideBar
+ local button = _G.ViragDevToolFrameToggleSideBarButton
+ S:HandleNextPrevButton(button, frame:IsShown() and "right" or "left")
+
+ hooksecurefunc(ViragDevTool, "ToggleSidebar", function(self)
+ local isShown = self.settings.isSideBarOpen
+ local normal, disabled, pushed = button:GetNormalTexture(), button:GetDisabledTexture(), button:GetPushedTexture()
+ local rotation = isShown and S.ArrowRotation.right or S.ArrowRotation.left
+
+ normal:SetRotation(rotation)
+ pushed:SetRotation(rotation)
+ disabled:SetRotation(rotation)
+
+ PlaySound(isShown and 620 or 621) -- звук открытия/закрытия журнала заданий
+ end)
+
+ local resizeButton = _G.ViragDevToolFrameResizeButton
+ local normal, pushed = resizeButton:GetNormalTexture(), resizeButton:GetPushedTexture()
+
+ S:HandleNextPrevButton(resizeButton)
+
+ normal:SetRotation(-2.35)
+ pushed:SetRotation(-2.35)
+
+ S:HandleEditBox(_G.ViragDevToolFrameSideBarTextArea, "Transparent")
+ S:HandleEditBox(_G.ViragDevToolFrameTextArea, "Transparent")
+
+ S:HandleScrollBar(_G.ViragDevToolFrameScrollFrameScrollBar)
+ S:HandleScrollBar(_G.ViragDevToolFrameSideBarScrollFrameScrollBar)
+
+ S:HandleCloseButton(_G.ViragDevToolFrameCloseWndButton)
end)
\ No newline at end of file
diff --git a/Modules/Skins/Blizzard/LFD.lua b/Modules/Skins/Blizzard/LFD.lua
index a0d14f5c6..22083a3e5 100644
--- a/Modules/Skins/Blizzard/LFD.lua
+++ b/Modules/Skins/Blizzard/LFD.lua
@@ -217,7 +217,7 @@ local function LoadSkin()
hooksecurefunc("LFDQueueFrameRandom_UpdateFrame", function()
local dungeonID = LFDQueueFrame.type
- if not dungeonID then return end
+ if type(dungeonID) ~= "number" then return end
local _, _, _, _, _, numRewards = GetLFGDungeonRewards(dungeonID)
for i = 1, numRewards do
diff --git a/Modules/Skins/Blizzard/Load_Blizzard.xml b/Modules/Skins/Blizzard/Load_Blizzard.xml
index e87a17ec8..fd159461a 100644
--- a/Modules/Skins/Blizzard/Load_Blizzard.xml
+++ b/Modules/Skins/Blizzard/Load_Blizzard.xml
@@ -12,6 +12,7 @@
+
diff --git a/Modules/Skins/Blizzard_Sirus/Sirus_BattlePass.lua b/Modules/Skins/Blizzard_Sirus/Sirus_BattlePass.lua
index 5d884eb78..7803c11bb 100644
--- a/Modules/Skins/Blizzard_Sirus/Sirus_BattlePass.lua
+++ b/Modules/Skins/Blizzard_Sirus/Sirus_BattlePass.lua
@@ -146,12 +146,15 @@ local function CleanPageButton(btn)
end)
end
-local function ReskinPKBTButton(btn)
+local function ReskinPKBTButton(btn, noBackdrop)
if not btn or not btn.IsObjectType or not btn:IsObjectType("Button") then
return
end
- local function clearTextures(b)
+ -- трогаем только именованный хром PKBT. Безопасно запускать после создания
+ -- фона ElvUI; общая зачистка регионов ниже не должна идти после него,
+ -- иначе фоновые текстуры попадут в GetRegions и будут стерты
+ local function clearChrome(b)
if b.Left then
b.Left:SetAlpha(0)
end
@@ -182,33 +185,31 @@ local function ReskinPKBTButton(btn)
if b.SetDisabledTexture then
b:SetDisabledTexture("")
end
- for i = 1, (b:GetNumRegions() or 0) do
- local r = select(i, b:GetRegions())
- if r and r.IsObjectType and r:IsObjectType("Texture") then
- r:SetTexture()
- r:SetAlpha(0)
- end
- end
+ -- контент (иконка/текст в WidgetHolder, Price, PurchaseNote) должен
+ -- оставаться видимым, иначе кнопка станет пустой. Glow это просто хром
if b.Glow then
b.Glow:Hide()
end
- if b.WidgetHolder then
- b.WidgetHolder:Hide()
- end
- if b.Price then
- b.Price:Hide()
- end
- if b.PurchaseNote then
- b.PurchaseNote:Hide()
- end
end
if not btn._Elv_BaseSkinned then
- S:HandleButton(btn, true)
+ -- стираем все лишние атласы ДО того, как S:HandleButton создаст фон
+ -- ElvUI, чтобы общая зачистка не задела сам фон
+ for i = 1, (btn:GetNumRegions() or 0) do
+ local r = select(i, btn:GetRegions())
+ if r and r.IsObjectType and r:IsObjectType("Texture") then
+ r:SetTexture()
+ r:SetAlpha(0)
+ end
+ end
+
+ -- noBackdrop пропускает фон ElvUI (кнопки страницы заданий нормально
+ -- выглядят прозрачными, остаются только текст и иконка)
+ S:HandleButton(btn, true, nil, false, noBackdrop)
btn._Elv_BaseSkinned = true
end
- clearTextures(btn)
+ clearChrome(btn)
ApplyElvUIFontForce(btn)
@@ -216,26 +217,34 @@ local function ReskinPKBTButton(btn)
btn._Elv_ClearHooks = true
if btn.SetThreeSliceAtlas then
hooksecurefunc(btn, "SetThreeSliceAtlas", function(self)
- clearTextures(self)
+ clearChrome(self)
end)
end
if btn.SetNormalAtlas then
hooksecurefunc(btn, "SetNormalAtlas", function(self)
- clearTextures(self)
+ clearChrome(self)
end)
end
if btn.SetHighlightAtlas then
hooksecurefunc(btn, "SetHighlightAtlas", function(self)
- clearTextures(self)
+ clearChrome(self)
end)
end
if btn.SetPushedAtlas then
hooksecurefunc(btn, "SetPushedAtlas", function(self)
- clearTextures(self)
+ clearChrome(self)
+ end)
+ end
+ -- смена состояния (OnShow/OnEnable/OnDisable/SetChecked) снова накладывает
+ -- атласы напрямую через UpdateButton, минуя SetThreeSliceAtlas;
+ -- вешаем хук, чтобы хром оставался скрытым
+ if btn.UpdateButton then
+ hooksecurefunc(btn, "UpdateButton", function(self)
+ clearChrome(self)
end)
end
btn:HookScript("OnShow", function(self)
- clearTextures(self)
+ clearChrome(self)
ApplyElvUIFontForce(self)
end)
end
@@ -428,7 +437,8 @@ local function HandleBattlePassFrame()
-- end
-- S:HandleFrame(child)
if child.ActionButton then
- ReskinPKBTButton(child.ActionButton)
+ -- кнопки действий заданий: без фона ElvUI, только текст/иконка
+ ReskinPKBTButton(child.ActionButton, true)
child.ActionButton:Show()
end
SkinAllQuestActionButtons(child)
@@ -459,46 +469,42 @@ local function HandleBattlePassFrame()
if _G.BattlePassLevelCardMixin and not S._Elv_LevelCardButtonsHooked then
S._Elv_LevelCardButtonsHooked = true
hooksecurefunc(_G.BattlePassLevelCardMixin, "SetTypeState", function(self)
- local freeButton = self.FreeFrame and self.FreeFrame.ActionButton
- local premButton = self.PremiumFrame and self.PremiumFrame.ActionButton
- if freeButton then
- S:HandleButton(freeButton)
- -- ReskinPKBTButton(freeButton)
- -- freeButton:Show()
- end
- if premButton then
- S:HandleButton(premButton)
- -- ReskinPKBTButton(premButton)
- -- premButton:Show()
- end
- end)
- hooksecurefunc(_G.BattlePassLevelCardMixin, "SetState", function(self)
- if self.SetScript then
- self:SetScript("OnUpdate", nil)
- end
- local fb = self.FreeFrame and self.FreeFrame.ActionButton
- local pb = self.PremiumFrame and self.PremiumFrame.ActionButton
- if fb then
- S:HandleButton(fb)
- fb:Show()
- end
- if pb then
- S:HandleButton(pb)
- pb:Show()
- end
- end)
- hooksecurefunc(_G.BattlePassLevelCardMixin, "OnLeave", function(self)
- local fb = self.FreeFrame and self.FreeFrame.ActionButton
- local pb = self.PremiumFrame and self.PremiumFrame.ActionButton
- if fb then
- S:HandleButton(fb)
- fb:Show()
- end
- if pb then
- S:HandleButton(pb)
- pb:Show()
- end
- end)
+ local freeButton = self.FreeFrame and self.FreeFrame.ActionButton
+ local premButton = self.PremiumFrame and self.PremiumFrame.ActionButton
+ if freeButton then
+ ReskinPKBTButton(freeButton)
+ end
+ if premButton then
+ ReskinPKBTButton(premButton)
+ end
+ end)
+ hooksecurefunc(_G.BattlePassLevelCardMixin, "SetState", function(self)
+ if self.SetScript then
+ self:SetScript("OnUpdate", nil)
+ end
+ local fb = self.FreeFrame and self.FreeFrame.ActionButton
+ local pb = self.PremiumFrame and self.PremiumFrame.ActionButton
+ if fb then
+ ReskinPKBTButton(fb)
+ fb:Show()
+ end
+ if pb then
+ ReskinPKBTButton(pb)
+ pb:Show()
+ end
+ end)
+ hooksecurefunc(_G.BattlePassLevelCardMixin, "OnLeave", function(self)
+ local fb = self.FreeFrame and self.FreeFrame.ActionButton
+ local pb = self.PremiumFrame and self.PremiumFrame.ActionButton
+ if fb then
+ ReskinPKBTButton(fb)
+ fb:Show()
+ end
+ if pb then
+ ReskinPKBTButton(pb)
+ pb:Show()
+ end
+ end)
end
if main.ScrollFrame and main.ScrollFrame.buttons then
@@ -506,14 +512,10 @@ local function HandleBattlePassFrame()
local fb = card.FreeFrame and card.FreeFrame.ActionButton
local pb = card.PremiumFrame and card.PremiumFrame.ActionButton
if fb then
- S:HandleButton(fb)
- -- ReskinPKBTButton(fb)
- -- fb:Show()
+ ReskinPKBTButton(fb)
end
if pb then
- S:HandleButton(pb)
- -- ReskinPKBTButton(pb)
- -- pb:Show()
+ ReskinPKBTButton(pb)
end
end
end
@@ -570,7 +572,7 @@ local function HandleBattlePassFrame()
end
if main.PurchasePremiumButton then
- S:HandleButton(main.PurchasePremiumButton)
+ ReskinPKBTButton(main.PurchasePremiumButton)
end
ApplyElvUIFont(main)
end
@@ -603,22 +605,20 @@ local function HandleBattlePassFrame()
if f.PurchasePremiumDialog then
local d = f.PurchasePremiumDialog
- S:HandleFrame(BattlePassFramePurchasePremiumDialog)
S:HandleFrame(d)
if d.CloseButton then
S:HandleCloseButton(d.CloseButton)
end
if d.PurchaseButton then
- S:HandleButton(d.PurchaseButton)
+ -- кнопка цены (PKBT_GoldButtonMultiWidgetPriceTemplate): виджет цены не скрывается
+ ReskinPKBTButton(d.PurchaseButton)
end
ApplyElvUIFont(d)
end
if f.PurchaseExperienceDialog then
local d = f.PurchaseExperienceDialog
- S:HandleFrame(BattlePassFramePurchaseLevelExperienceDialog)
- -- d:StripTextures(true)
- -- d:SetTemplate("Transparent")
+ S:HandleFrame(d)
if d.CloseButton then
S:HandleCloseButton(d.CloseButton)
end
@@ -719,14 +719,13 @@ local function HandleBattlePassFrame()
if f.PurchaseLevelExperienceDialog then
local d = f.PurchaseLevelExperienceDialog
- S:HandleFrame(BattlePassFramePurchaseLevelExperienceDialog)
d:StripTextures(true)
d:SetTemplate("Transparent")
if d.CloseButton then
S:HandleCloseButton(d.CloseButton)
end
if d.PurchaseButton then
- S:HandleButton(d.PurchaseButton)
+ ReskinPKBTButton(d.PurchaseButton)
end
ApplyElvUIFont(d)
end
@@ -745,10 +744,10 @@ local function HandleBattlePassFrame()
d.backdrop:SetBackdropBorderColor(0, 0, 0, 0)
end
if d.OkButton then
- S:HandleButton(d.OkButton)
+ ReskinPKBTButton(d.OkButton)
end
if d.CancelButton then
- S:HandleButton(d.CancelButton)
+ ReskinPKBTButton(d.CancelButton)
end
ApplyElvUIFont(d)
end
diff --git a/Modules/Skins/Blizzard_Sirus/Sirus_Store.lua b/Modules/Skins/Blizzard_Sirus/Sirus_Store.lua
index fd6621042..7a43b7d93 100644
--- a/Modules/Skins/Blizzard_Sirus/Sirus_Store.lua
+++ b/Modules/Skins/Blizzard_Sirus/Sirus_Store.lua
@@ -1,6 +1,10 @@
local E, L, V, P, G = unpack(select(2, ...)); --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
local S = E:GetModule("Skins")
+local _G = _G
+
+local GetScreenWidth, GetScreenHeight = GetScreenWidth, GetScreenHeight
+
local function GetStoreFrameScale()
local parentScale = UIParent and UIParent:GetScale() or 1
if parentScale > 0 and parentScale < 0.7 and (GetScreenWidth() >= 2560 or GetScreenHeight() >= 1440) then
@@ -10,600 +14,776 @@ local function GetStoreFrameScale()
return parentScale
end
---Lua functions
---WoW API / Variables
--- local function HookSubButtons()
-
--- if StoreRefreshTransmogListButton then
--- S:HandleButton(StoreRefreshTransmogListButton)
--- end
--- for _,v in pairs(StoreFrame.SubCategoryFrames or {}) do
--- S:HandleButton(v,true)
--- v.SelectedTexture:SetTexture(.9, .8, .1, .3)
--- end
--- end
-local function LoadSkin()
- if E.private.skins.blizzard.enable ~= true or E.private.skins.blizzard.store ~= true then return end
+------------------------------------------------------------------------
+-- Общие помощники PKBT (тот же подход, что в скине BattlePass)
+------------------------------------------------------------------------
+
+local function ApplyElvUIFont(frame)
+ if not frame or not frame.GetNumRegions then return end
+ for i = 1, (frame:GetNumRegions() or 0) do
+ local r = select(i, frame:GetRegions())
+ if r and r.GetObjectType and r:GetObjectType() == "FontString" and r.FontTemplate then
+ local _, size, flags = r:GetFont()
+ if not size or size < 1 then
+ r:FontTemplate(nil, nil, flags)
+ else
+ r:FontTemplate(nil, size, flags)
+ end
+ end
+ end
+ local numChildren = frame:GetNumChildren() or 0
+ if numChildren > 0 then
+ for i = 1, numChildren do
+ local child = select(i, frame:GetChildren())
+ if child then ApplyElvUIFont(child) end
+ end
+ end
+end
- StoreFrame:HookScript("OnShow", function()
- StoreFrame:SetScale(GetStoreFrameScale())
- end)
- StoreFrame:EnableMouse(true)
- StoreFrame:SetMovable(true)
- StoreFrame:RegisterForDrag("LeftButton")
- StoreFrame:SetScript("OnDragStart", function(self)
- self:StartMoving()
- end)
- StoreFrame:SetScript("OnDragStop", function(self)
- self:StopMovingOrSizing()
- local frame_x, frame_y = self:GetCenter()
- frame_x = frame_x*UIParent:GetScale() - GetScreenWidth() / 2
- frame_y = frame_y*UIParent:GetScale() - GetScreenHeight() / 2
- self:ClearAllPoints()
- self:SetPoint("CENTER", UIParent, "CENTER", frame_x, frame_y)
+local function ApplyElvUIFontForce(frame)
+ if not frame or not frame.GetObjectType then return end
+ for i = 1, (frame:GetNumRegions() or 0) do
+ local r = select(i, frame:GetRegions())
+ if r and r.GetObjectType and r:GetObjectType() == "FontString" and r.SetFont then
+ local _, size, flags = r:GetFont()
+ r:SetFont(E.media.normFont or (select(1, GameFontNormal:GetFont())), (size and size >= 1) and size or 12, flags or "")
+ end
+ end
+ for i = 1, (frame:GetNumChildren() or 0) do
+ local child = select(i, frame:GetChildren())
+ if child then ApplyElvUIFontForce(child) end
+ end
+end
+
+-- цвета текста PKBT рассчитаны на светлый фон магазина и нечитаемы на
+-- темном фоне ElvUI (темно-коричневые, бежевые и серо-коричневые). Приводим
+-- только их к белому: золотые акценты, чистый белый, нейтральные серые
+-- (отключенные состояния) и смысловые цвета не трогаем
+local function NormalizePKBTTextColors(frame)
+ if not frame or not frame.GetObjectType then return end
+
+ local objectType = frame:GetObjectType()
+ if (objectType == "FontString" or objectType == "SimpleHTML") and frame.GetTextColor and frame.SetTextColor then
+ local r, g, b = frame:GetTextColor()
+ if r and g and b then
+ -- темный текст рассчитан на светлый фон
+ local luminance = r * 0.3 + g * 0.6 + b * 0.1
+ -- приглушенная тепло-серая гамма PKBT (r > g > b, почти ровный тон):
+ -- бежевый и серо-коричневый текст, который теряется на темном фоне
+ local mutedWarm = r > b and g >= 0.8 * r and b >= 0.7 * g
+ if luminance < 0.4 or mutedWarm then
+ frame:SetTextColor(1, 1, 1)
+ end
+ end
+ end
+
+ if frame.GetNumRegions then
+ for i = 1, frame:GetNumRegions() do
+ local region = select(i, frame:GetRegions())
+ if region then NormalizePKBTTextColors(region) end
+ end
+ end
+ -- GetNumChildren есть только у Frame; проверяем, чтобы рекурсия не упала,
+ -- когда дойдет до региона Texture/FontString
+ if frame.GetNumChildren then
+ for i = 1, frame:GetNumChildren() do
+ local child = select(i, frame:GetChildren())
+ if child then NormalizePKBTTextColors(child) end
+ end
+ end
+end
+
+-- Убираем с кнопки трехслойный хром/атласы PKBT и приводим её к виду ElvUI.
+-- Хуки держат кнопку чистой, когда клиент меняет атласы.
+-- ВАЖНО: контент кнопки живет в WidgetHolder (AddText/AddTextureAtlas)
+-- и в виджетах Price/PurchaseNote, поэтому их не трогаем, иначе кнопка
+-- станет пустой. Только именованный хром PKBT: общая зачистка регионов не
+-- должна идти после создания фона, иначе фоновые текстуры будут стерты.
+local function ClearPKBTChrome(b)
+ if not b then return end
+ if b.Left then b.Left:SetAlpha(0) end
+ if b.Right then b.Right:SetAlpha(0) end
+ if b.Center then b.Center:SetAlpha(0) end
+ if b.LeftHighlight then b.LeftHighlight:SetAlpha(0) end
+ if b.RightHighlight then b.RightHighlight:SetAlpha(0) end
+ if b.CenterHighlight then b.CenterHighlight:SetAlpha(0) end
+ if b.SetNormalTexture then b:SetNormalTexture("") end
+ if b.SetHighlightTexture then b:SetHighlightTexture("") end
+ if b.SetPushedTexture then b:SetPushedTexture("") end
+ if b.SetDisabledTexture then b:SetDisabledTexture("") end
+ if b.Glow then b.Glow:Hide() end
+end
+
+-- клиент заново накладывает атласы при каждой смене состояния
+-- (OnShow/OnEnable/OnDisable/SetChecked идут через UpdateButton, который
+-- задает атласы напрямую и минует SetThreeSliceAtlas), поэтому вешаем
+-- хуки на все эти методы, чтобы хром оставался скрытым
+local function HookClearPKBTChrome(btn)
+ if btn._Elv_ClearHooks then return end
+ btn._Elv_ClearHooks = true
+
+ for _, method in ipairs({
+ "SetThreeSliceAtlas", "SetNormalAtlas", "SetHighlightAtlas", "SetPushedAtlas",
+ "SetCheckedAtlas", "SetDisabledAtlas", "UpdateButton",
+ }) do
+ if btn[method] then
+ hooksecurefunc(btn, method, function(self) ClearPKBTChrome(self) end)
+ end
+ end
+
+ btn:HookScript("OnShow", function(self)
+ ClearPKBTChrome(self)
+ ApplyElvUIFontForce(self)
end)
+end
+
+local function ReskinPKBTButton(btn)
+ if not btn or not btn.IsObjectType or not btn:IsObjectType("Button") then return end
+
+ if not btn._Elv_BaseSkinned then
+ -- стираем все лишние атласы ДО того, как S:HandleButton создаст фон
+ -- ElvUI, чтобы общая зачистка не задела сам фон
+ for i = 1, (btn:GetNumRegions() or 0) do
+ local r = select(i, btn:GetRegions())
+ if r and r.IsObjectType and r:IsObjectType("Texture") then
+ r:SetTexture()
+ r:SetAlpha(0)
+ end
+ end
+
+ S:HandleButton(btn, true)
+ btn._Elv_BaseSkinned = true
+ end
+
+ ClearPKBTChrome(btn)
+
+ ApplyElvUIFontForce(btn)
+
+ HookClearPKBTChrome(btn)
+end
+
+------------------------------------------------------------------------
+-- Части скина магазина
+------------------------------------------------------------------------
+
+-- У кнопок категорий остается текстура иконки: ReskinPKBTButton стирает ВСЕ
+-- прямые регионы (включая иконку), а S:HandleIcon не возвращает текстуры,
+-- поэтому тут strip=false и чистим только именованный хром
+local function SkinStoreCategoryButton(btn)
+ if not btn or not btn.IsObjectType or not btn:IsObjectType("Button") then return end
+ if btn._ElvCategorySkinned then return end
+ btn._ElvCategorySkinned = true
+
+ S:HandleButton(btn, false)
+ ClearPKBTChrome(btn)
+ ApplyElvUIFont(btn)
+ HookClearPKBTChrome(btn)
+
+ -- иконки категорий верхнего уровня задаются через SetTexture (UpdateInfo),
+ -- стандартная обрезка иконки ElvUI тут безопасна и нужна
+ if btn.Icon then
+ btn.Icon:SetTexCoord(unpack(E.TexCoords))
+ end
+ if btn.ButtonText then
+ -- только шрифт: цвета состояний задает UpdateState клиента
+ -- (белый / зеленый при наведении / золотой у выбранной / серый у выключенной)
+ btn.ButtonText:FontTemplate(nil, nil, "OUTLINE")
+ end
+ if btn.NewIcon then btn.NewIcon:Hide() end
+end
+
+local function SkinStoreSubCategoryButton(btn)
+ if not btn or not btn.IsObjectType or not btn:IsObjectType("Button") then return end
+ if btn._ElvSubCategorySkinned then return end
+ btn._ElvSubCategorySkinned = true
+
+ S:HandleButton(btn, false)
+ ClearPKBTChrome(btn)
+ ApplyElvUIFont(btn)
+ HookClearPKBTChrome(btn)
+
+ -- иконки подкатегорий ВСЕГДА атласы (SetAtlas в UpdateInfo); сохраненные
+ -- texcoord нужны, иначе иконка отрисуется криво или пустой, поэтому
+ -- SetTexCoord тут не вызываем
+ if btn.ButtonText then
+ -- только шрифт: цвета состояний задает UpdateState клиента
+ -- (белый / зеленый при наведении / золотой у выбранной / серый у выключенной)
+ btn.ButtonText:FontTemplate(nil, nil, "OUTLINE")
+ end
+ if btn.NewIcon then btn.NewIcon:Hide() end
+end
+
+-- кнопки сумок на верхних панелях (Vote / Referral / Loyality). Иконка сумки
+-- это и есть обычный атлас кнопки (PKBT-Store-Bag-Portrait), поэтому обычный
+-- HandleCheckBox сотрет её и оставит кнопку пустой. Чистим только состояния
+-- хрома (нажатое/выключенное/наведение/выбранное)
+local function SkinStoreBagButton(btn)
+ if not btn or not btn.IsObjectType or not btn:IsObjectType("CheckButton") then return end
+ if btn._ElvBagSkinned then return end
+ btn._ElvBagSkinned = true
+
+ local function clearChrome(b)
+ if b.SetPushedTexture then b:SetPushedTexture("") end
+ if b.SetDisabledTexture then b:SetDisabledTexture("") end
+ if b.SetHighlightTexture then b:SetHighlightTexture("") end
+ if b.SetCheckedTexture then b:SetCheckedTexture("") end
+ if b.SetDisabledCheckedTexture then b:SetDisabledCheckedTexture("") end
+ end
+
+ clearChrome(btn)
+
+ if not btn._ElvBagHooked then
+ btn._ElvBagHooked = true
+ for _, method in ipairs({ "SetPushedAtlas", "SetDisabledAtlas", "SetHighlightAtlas", "SetCheckedAtlas", "SetDisabledCheckedAtlas" }) do
+ if btn[method] then
+ hooksecurefunc(btn, method, function(self) clearChrome(self) end)
+ end
+ end
+ end
+end
+
+-- вкладки сохраняют иконку и текст; убираем только трехслойный хром
+local function SkinStoreTabButton(btn)
+ if not btn or not btn.IsObjectType or not btn:IsObjectType("Button") then return end
+
+ local function clearChrome(b)
+ if b.Left then b.Left:SetAlpha(0) end
+ if b.Center then b.Center:SetAlpha(0) end
+ if b.Right then b.Right:SetAlpha(0) end
+ if b.LeftHighlight then b.LeftHighlight:SetAlpha(0) end
+ if b.RightHighlight then b.RightHighlight:SetAlpha(0) end
+ if b.CenterHighlight then b.CenterHighlight:SetAlpha(0) end
+ if b.SetNormalTexture then b:SetNormalTexture("") end
+ if b.SetHighlightTexture then b:SetHighlightTexture("") end
+ if b.SetPushedTexture then b:SetPushedTexture("") end
+ if b.SetDisabledTexture then b:SetDisabledTexture("") end
+ end
+
+ -- без strip: сохраняем иконку вкладки и остальные текстуры
+ S:HandleButton(btn, false)
+ clearChrome(btn)
+ ApplyElvUIFont(btn)
+
+ if not btn._Elv_TabHooked then
+ btn._Elv_TabHooked = true
+ if btn.SetThreeSliceAtlas then
+ hooksecurefunc(btn, "SetThreeSliceAtlas", function(self) clearChrome(self) end)
+ end
+ if btn.SetNormalAtlas then
+ hooksecurefunc(btn, "SetNormalAtlas", function(self) clearChrome(self) end)
+ end
+ if btn.SetHighlightAtlas then
+ hooksecurefunc(btn, "SetHighlightAtlas", function(self) clearChrome(self) end)
+ end
+ if btn.SetPushedAtlas then
+ hooksecurefunc(btn, "SetPushedAtlas", function(self) clearChrome(self) end)
+ end
+ btn:HookScript("OnShow", function(self)
+ clearChrome(self)
+ ApplyElvUIFont(self)
+ end)
+ end
+end
+
+local function SkinStoreRowButton(row)
+ if not row then return end
+ if row._ElvRowSkinned then return end
+ row._ElvRowSkinned = true
+
+ if row.BackgroundLeft then row.BackgroundLeft:SetAlpha(0) end
+ if row.BackgroundRight then row.BackgroundRight:SetAlpha(0) end
+ if row.BackgroundCenter then row.BackgroundCenter:SetAlpha(0) end
+ if row.NineSliceSelection then row.NineSliceSelection:Hide() end
+ if row.NineSliceHighlight then row.NineSliceHighlight:Hide() end
+
+ row:CreateBackdrop("Transparent")
+
+ local ht = (row.GetHighlightTexture and row:GetHighlightTexture()) or row.HighlightTexture
+ if not ht then
+ ht = row:CreateTexture(nil, "HIGHLIGHT")
+ ht:SetAllPoints(row)
+ row:SetHighlightTexture(ht)
+ end
+ ht:SetTexture(E.Media.Textures.Highlight)
+ ht:SetTexCoord(0, 1, 0, 1)
+ ht:SetVertexColor(1, 1, 1, 0.25)
+
+ ApplyElvUIFont(row)
+
+ for i = 1, (row:GetNumChildren() or 0) do
+ local child = select(i, row:GetChildren())
+ if child then
+ ApplyElvUIFont(child)
+ if child.Icon then
+ S:HandleIcon(child.Icon)
+ child.Icon:SetTexCoord(unpack(E.TexCoords))
+ end
+ if child.Border then child.Border:SetAlpha(0) end
+ if child.IconBorder then child.IconBorder:SetAlpha(0) end
+ if child.Price then child.Price:StripTextures() end
+ end
+ end
+end
+
+-- поле поиска фильтра это пулируемый PKBT_EditBoxTemplate, чей хром
+-- (BackgroundLeft/Right/Center) отсутствует в S.Blizzard.Regions, поэтому
+-- S:HandleEditBox его не убирает; скрываем явно. Кнопка очистки (X)
+-- остается рабочей, убираем только ее текстуры состояний
+local function SkinStoreFilterEditBox(editbox)
+ if not editbox or not editbox.IsObjectType or not editbox:IsObjectType("EditBox") then return end
+ if editbox._ElvFilterEditBoxSkinned then return end
+ editbox._ElvFilterEditBoxSkinned = true
+
+ S:HandleEditBox(editbox)
+
+ if editbox.BackgroundLeft then editbox.BackgroundLeft:SetAlpha(0) end
+ if editbox.BackgroundRight then editbox.BackgroundRight:SetAlpha(0) end
+ if editbox.BackgroundCenter then editbox.BackgroundCenter:SetAlpha(0) end
+
+ if editbox.ClearButton then
+ if editbox.ClearButton.SetHighlightTexture then editbox.ClearButton:SetHighlightTexture("") end
+ if editbox.ClearButton.SetPushedTexture then editbox.ClearButton:SetPushedTexture("") end
+ if editbox.ClearButton.SetDisabledTexture then editbox.ClearButton:SetDisabledTexture("") end
+ end
+
+ ApplyElvUIFont(editbox)
+end
+
+local function SkinStoreList(view)
+ if not view then return end
+
+ local list = view.List
+ if list then
+ if not list._ElvSkinned then
+ list._ElvSkinned = true
+ list:StripTextures(true)
+ list:CreateBackdrop("Transparent")
+ end
+ if list.Scroll then
+ if list.Scroll.ScrollBar then
+ S:HandleScrollBar(list.Scroll.ScrollBar)
+ end
+ for _, row in ipairs(list.Scroll.buttons or {}) do
+ SkinStoreRowButton(row)
+ end
+ end
+ ApplyElvUIFont(list)
+ end
+
+ local filter = view.Filter
+ if filter then
+ if not filter._ElvSkinned then
+ filter._ElvSkinned = true
+ filter:StripTextures(true)
+ -- вложенная рамка PKBT это дочерняя рамка (StripTextures её не
+ -- берет); фон панели обеспечивает фон ElvUI
+ if filter.NineSliceInset then filter.NineSliceInset:Hide() end
+ filter:CreateBackdrop("Transparent")
+ end
+ if filter.Scroll then
+ -- у панели фильтра свой скроллбар справа (тот же
+ -- PKBT_UIPanelScrollBarTemplate, что и в списке предметов), скинуем и его
+ if filter.Scroll.ScrollBar then
+ S:HandleScrollBar(filter.Scroll.ScrollBar)
+ end
+ local scrollChild = filter.Scroll.ScrollChild
+ if scrollChild then
+ if scrollChild.ResetButton then ReskinPKBTButton(scrollChild.ResetButton) end
+ for i = 1, (scrollChild:GetNumChildren() or 0) do
+ local child = select(i, scrollChild:GetChildren())
+ if child then
+ if child.IsObjectType and child:IsObjectType("CheckButton") then
+ S:HandleCheckBox(child)
+ elseif child.IsObjectType and child:IsObjectType("EditBox") then
+ SkinStoreFilterEditBox(child)
+ end
+ ApplyElvUIFont(child)
+ end
+ end
+ end
+ end
+ ApplyElvUIFont(filter)
+ end
+
+ local header = view.PageHeader
+ if header then
+ if not header._ElvSkinned then
+ header._ElvSkinned = true
+ header:StripTextures(true)
+ header:CreateBackdrop("Transparent")
+ end
+ if header.RefreshButton then ReskinPKBTButton(header.RefreshButton) end
+ if header.Title then header.Title:FontTemplate(nil, 18, "OUTLINE") end
+ ApplyElvUIFont(header)
+ end
+end
+
+local function SkinStoreDialog(dialog)
+ if not dialog or not dialog.IsObjectType or not dialog:IsObjectType("Frame") then return end
+ if dialog._ElvDialogSkinned then return end
+ dialog._ElvDialogSkinned = true
+
+ dialog:StripTextures(true)
+
+ if dialog.NineSlice then dialog.NineSlice:Hide() end
+ if dialog.Background then dialog.Background:SetAlpha(0) end
+ if dialog.VignetteTopLeft then dialog.VignetteTopLeft:SetAlpha(0) end
+ if dialog.VignetteTopRight then dialog.VignetteTopRight:SetAlpha(0) end
+ if dialog.VignetteBottomLeft then dialog.VignetteBottomLeft:SetAlpha(0) end
+ if dialog.VignetteBottomRight then dialog.VignetteBottomRight:SetAlpha(0) end
+
+ if dialog.TitleContainer then
+ dialog.TitleContainer:StripTextures(true)
+ if dialog.TitleContainer.TitleText then
+ dialog.TitleContainer.TitleText:FontTemplate(nil, 18, "OUTLINE")
+ dialog.TitleContainer.TitleText:SetTextColor(unpack(E.media.rgbvaluecolor))
+ end
+ end
+
+ if dialog.CloseButton then S:HandleCloseButton(dialog.CloseButton) end
+
+ dialog:SetTemplate("Transparent")
+
+ for _, key in ipairs({ "PurchaseButton", "BuyButton", "ActionButton", "AgreeButton", "InviteButton", "InfoButton", "OkButton", "CancelButton", "AcceptButton", "BackButton", "DetailsButton" }) do
+ local btn = dialog[key]
+ -- контент цены/виджетов ReskinPKBTButton не скрывает
+ if btn then ReskinPKBTButton(btn) end
+ end
+
+ ApplyElvUIFont(dialog)
+ NormalizePKBTTextColors(dialog)
+
+ -- динамический контент (виджет товара, опции, шаги рефералки) пересоздается
+ -- при каждом показе диалога и заново накладывает коричневый текст PKBT;
+ -- приводим цвета к белому после каждого показа
+ if not dialog._ElvDialogHooked then
+ dialog._ElvDialogHooked = true
+ dialog:HookScript("OnShow", function(self)
+ NormalizePKBTTextColors(self)
+ end)
+ end
+end
+
+local function HandleStoreFrame()
+ local f = _G.StoreFrame
+ if not f then return end
+
+ if not f._ElvMainSkinned then
+ f._ElvMainSkinned = true
+
+ -- хром панели PKBT заменяем фоном ElvUI
+ f:StripTextures(true)
+ if f.NineSlice then f.NineSlice:Hide() end
+ if f.DecorOverlay then f.DecorOverlay:Hide() end
+ if f.TopTileStreaks then f.TopTileStreaks:SetAlpha(0) end
+
+ f:CreateBackdrop("Transparent")
+
+ if f.CloseButton then S:HandleCloseButton(f.CloseButton) end
+
+ if f.TitleContainer then
+ f.TitleContainer:StripTextures(true)
+ if f.TitleContainer.TitleText then
+ f.TitleContainer.TitleText:FontTemplate(nil, 20, "OUTLINE")
+ f.TitleContainer.TitleText:SetTextColor(unpack(E.media.rgbvaluecolor))
+ end
+ end
+ end
+
+ -- верхняя панель (информация об аккаунте + валюты + прогресс)
+ local top = f.TopPanel
+ if top then
+ if not top._ElvSkinned then
+ top._ElvSkinned = true
+ top:StripTextures(true)
+ top:CreateBackdrop("Transparent")
+ end
+
+ if top.AccountPanel then
+ local portrait = top.AccountPanel.PortraitContainer
+ if portrait and portrait.Ring then
+ portrait.Ring:SetAlpha(0)
+ end
+ ApplyElvUIFont(top.AccountPanel)
+ end
+
+ for _, panel in ipairs({ top.BalancePanel, top.VotePanel }) do
+ if panel then
+ if panel.Divider then panel.Divider:Hide() end
+ if panel.Button then ReskinPKBTButton(panel.Button) end
+ if panel.BrowseButton then SkinStoreBagButton(panel.BrowseButton) end
+ ApplyElvUIFont(panel)
+ end
+ end
+
+ for _, panel in ipairs({ top.LoyalityPanel, top.ReferralPanel }) do
+ if panel then
+ if panel.Divider then panel.Divider:Hide() end
+ if panel.StatusBar then S:HandleStatusBar(panel.StatusBar) end
+ if panel.BrowseButton then SkinStoreBagButton(panel.BrowseButton) end
+ if panel.AddButton then ReskinPKBTButton(panel.AddButton) end
+ ApplyElvUIFont(panel)
+ end
+ end
+ end
+ -- левая панель (навигация + премиум + трекер подписки)
+ local left = f.LeftPanel
+ if left then
+ if left.NavPanel then
+ local nav = left.NavPanel
+ if not nav._ElvSkinned then
+ nav._ElvSkinned = true
+ nav:StripTextures(true)
+ -- вложенную рамку PKBT (её правый край служит разделителем между
+ -- колонкой навигации и контентом) убираем, разделитель в стиле
+ -- ElvUI дает фон
+ if nav.NineSliceInset then nav.NineSliceInset:Hide() end
+ nav:CreateBackdrop("Transparent")
+ end
+ ApplyElvUIFont(nav)
+ end
+
+ if left.PremiumPanel then
+ local premium = left.PremiumPanel
+ if not premium._ElvSkinned then
+ premium._ElvSkinned = true
+ premium:StripTextures(true)
+ if premium.NineSliceInset then premium.NineSliceInset:Hide() end
+ premium:CreateBackdrop("Transparent")
+ end
+ if premium.Purchase then ReskinPKBTButton(premium.Purchase) end
+ end
+
+ if left.SubscriptionTracker then
+ local tracker = left.SubscriptionTracker
+ if not tracker._ElvSkinned then
+ tracker._ElvSkinned = true
+ tracker:StripTextures(true)
+ tracker:CreateBackdrop("Transparent")
+ end
+ if tracker.Artwork then tracker.Artwork:Hide() end
+ if tracker.ActionButton then ReskinPKBTButton(tracker.ActionButton) end
+ ApplyElvUIFont(tracker)
+ end
+ end
+
+ -- область контента
+ local content = f.Content
+ if content then
+ if not content._ElvSkinned then
+ content._ElvSkinned = true
+ content:StripTextures(true)
+ if content.NineSliceInset then content.NineSliceInset:Hide() end
+ content:CreateBackdrop("Transparent")
+ end
+ ApplyElvUIFont(content)
+ end
+
+ -- диалоги
+ for _, dialog in ipairs({ f.LinkDialog, f.AgreementDialog, f.ReferralInviteDialog, f.PremiumPurchaseDialog, f.ProductPurchaseDialogSecondary }) do
+ SkinStoreDialog(dialog)
+ end
+
+ if f.dialogFramePool then
+ for dialog in f.dialogFramePool:EnumerateActive() do
+ SkinStoreDialog(dialog)
+ end
+ end
+
+ -- окно промокода
+ local promo = _G.PromoCodeFrame
+ if promo and not promo._ElvSkinned then
+ promo._ElvSkinned = true
+ promo:StripTextures(true)
+ if promo.NineSlice then promo.NineSlice:Hide() end
+ if promo.Background then promo.Background:SetAlpha(0) end
+ promo:SetTemplate("Transparent")
+ if promo.CloseButton then S:HandleCloseButton(promo.CloseButton) end
+ if promo.Content then
+ if promo.Content.Code and promo.Content.Code.EditBox then
+ S:HandleEditBox(promo.Content.Code.EditBox)
+ end
+ if promo.Content.ActionButton then ReskinPKBTButton(promo.Content.ActionButton) end
+ if promo.Content.Scroll and promo.Content.Scroll.ScrollBar then
+ S:HandleScrollBar(promo.Content.Scroll.ScrollBar)
+ end
+ ApplyElvUIFont(promo.Content)
+ NormalizePKBTTextColors(promo)
+ end
+ end
+
+ -- та же правка читаемости для главного окна магазина: бежевый/коричневый
+ -- текст PKBT (подписи аккаунта/премиума, текст трекера и т.д.) на темном фоне
+ NormalizePKBTTextColors(f)
+end
+
+local function HookStore()
+ -- ВАЖНО: StoreFrame и его дети при логине Mixin() копируют методы на свои
+ -- экземпляры, поэтому hooksecurefunc на таблицах миксинов для этих
+ -- экземпляров не сработает: хуки нужно вешать на сами рамки. Миксины
+ -- безопасно хукать только для рамок, создаваемых ПОСЛЕ этого кода
+ -- (SubCategoryMenu, карточки рекомендаций), так как те копируют уже
+ -- захученный метод при создании
+ local storeFrame = _G.StoreFrame
+ if storeFrame then
+ if not S._Elv_StoreInstanceHooked then
+ S._Elv_StoreInstanceHooked = true
+
+ hooksecurefunc(storeFrame, "UpdateCategoryButtons", function(self)
+ for _, button in ipairs(self.categoryButtons or {}) do
+ SkinStoreCategoryButton(button)
+ end
+ end)
+
+ hooksecurefunc(storeFrame, "ShowDialogWidget", function(self, widgetType, parent, preShowCallback)
+ local dialog = self:GetDialogWidget(widgetType)
+ if dialog then SkinStoreDialog(dialog) end
+ end)
+
+ hooksecurefunc(storeFrame, "ShowGenericDialog", function(self)
+ if self.dialogFramePool then
+ for dialog in self.dialogFramePool:EnumerateActive() do
+ SkinStoreDialog(dialog)
+ end
+ end
+ end)
+ end
+
+ local itemListView = storeFrame.ItemListView
+ if itemListView and not S._Elv_StoreListViewHooked then
+ S._Elv_StoreListViewHooked = true
+
+ local function SkinListView(self)
+ SkinStoreList(self)
+ end
+
+ hooksecurefunc(itemListView, "UpdateViewTable", SkinListView)
+ hooksecurefunc(itemListView, "OnItemScrollUpdate", SkinListView)
+ hooksecurefunc(itemListView, "OnShow", SkinListView)
+ -- опции фильтра (поле поиска, галочки) пулируются и создаются
+ -- в UpdateFilters, поэтому перескиниваем сразу после их (пере)создания
+ hooksecurefunc(itemListView, "UpdateFilters", SkinListView)
+ end
+
+ local pageCollections = storeFrame.Content and storeFrame.Content.PageCollections
+ if pageCollections and not S._Elv_StoreTabsHooked then
+ S._Elv_StoreTabsHooked = true
+ hooksecurefunc(pageCollections, "UpdateTabs", function(self)
+ for _, btn in pairs(self.tabButtons or {}) do
+ if btn then SkinStoreTabButton(btn) end
+ end
+ end)
+ end
+
+ local specialOffer = storeFrame.Content and storeFrame.Content.PageMain and storeFrame.Content.PageMain.SpecialPanel and storeFrame.Content.PageMain.SpecialPanel.Banner and storeFrame.Content.PageMain.SpecialPanel.Banner.Offer
+ if specialOffer and not S._Elv_StoreSpecialOfferHooked then
+ S._Elv_StoreSpecialOfferHooked = true
+ hooksecurefunc(specialOffer, "OnShow", function(self)
+ if self.PurchaseButton then ReskinPKBTButton(self.PurchaseButton) end
+ if self.DetailsButton then ReskinPKBTButton(self.DetailsButton) end
+ if self.ActionButton then ReskinPKBTButton(self.ActionButton) end
+ end)
+ end
+
+ local refundView = storeFrame.RefundView
+ if refundView and not S._Elv_StoreRefundHooked then
+ S._Elv_StoreRefundHooked = true
+ hooksecurefunc(refundView, "OnShow", function(self)
+ if self.RefundButton then ReskinPKBTButton(self.RefundButton) end
+ if self.Scroll then
+ for _, row in ipairs(self.Scroll.buttons or {}) do
+ if not row._ElvRefundRowSkinned then
+ row._ElvRefundRowSkinned = true
+ row:StripTextures()
+ row:CreateBackdrop("Transparent")
+ if row.CheckButton then S:HandleCheckBox(row.CheckButton) end
+ if row.Item and row.Item.Icon then S:HandleIcon(row.Item.Icon) end
+ if row.Price then row.Price:StripTextures() end
+ ApplyElvUIFont(row)
+ end
+ end
+ end
+ end)
+ end
+ end
+
+ -- рамки, создаваемые в рантайме: SubCategoryMenu (создается вместе с кнопками
+ -- категорий) и карточки рекомендаций (из пула) копируют методы миксинов при
+ -- создании, поэтому хуки на миксинах для них срабатывают
+ if _G.StoreCategorySubMenuMixin and not S._Elv_StoreSubMenuHooked then
+ S._Elv_StoreSubMenuHooked = true
+
+ hooksecurefunc(_G.StoreCategorySubMenuMixin, "UpdateSubCategories", function(self)
+ for _, button in ipairs(self.subCategoryButtons or {}) do
+ SkinStoreSubCategoryButton(button)
+ end
+ end)
+ end
+
+ if _G.StoreRecommendationMixin and not S._Elv_StoreRecommendationHooked then
+ S._Elv_StoreRecommendationHooked = true
+ hooksecurefunc(_G.StoreRecommendationMixin, "OnShow", function(self)
+ if self.PurchaseButton then ReskinPKBTButton(self.PurchaseButton) end
+ if self.DetailsButton then ReskinPKBTButton(self.DetailsButton) end
+ end)
+ end
+end
+
+-- скин работает по принципу best-effort: ошибка тут не должна прервать
+-- ApplySkin до HookStore (иначе у магазина не будет хуков скина вообще)
+-- или уйти в диспетчер колбэков загрузчика скинов. Ловим ошибку и
+-- показываем её через стандартный обработчик
+local function SafeSkinCall(fn, ...)
+ if not fn then return end
+ local ok, err = pcall(fn, ...)
+ if not ok then
+ local handler = geterrorhandler()
+ if handler then handler(err) end
+ end
+end
- if true then return end
- -- local goldBorderList = {
- -- "TopLeft",
- -- "TopRight",
- -- "BottomLeft",
- -- "BottomRight",
- -- "Top",
- -- "Left",
- -- "Right",
- -- "Bottom",
- -- }
-
- -- local function RemoveGoldBorder(frame)
- -- for _, parentKey in pairs(goldBorderList) do
- -- local region = frame[parentKey]
- -- if region then
- -- region:Hide()
- -- end
- -- end
- -- end
-
- -- StoreFrame:SetParent(UIParent)
- -- StoreFrame:SetScale(1)
-
- -- StoreFrame:StripTextures()
- -- StoreFrame:SetTemplate("Transparent")
- -- StoreFrame:SetFrameStrata("DIALOG")
-
- -- StoreFrameLeftInset:StripTextures()
- -- -- StoreFrameLeftInset:SetTemplate("Transparent")
-
- -- StoreFrameRightInset:StripTextures()
- -- -- StoreFrameRightInset:SetTemplate("Transparent")
-
- -- StoreFrameTopInset:StripTextures()
- -- --StoreFrameTopInset:SetTemplate("Transparent")
-
- -- StoreRefundButton:SetPoint("BOTTOM", 0, 8)
- -- S:HandleButton(StoreRefundButton)
-
- -- for i = 1, 4 do
- -- local button = _G["StoreMoneyButton"..i]
- -- button:CreateBackdrop("Transparent")
- -- button.backdrop:Point("TOPLEFT", 28, -7)
- -- button.backdrop:Point("BOTTOMRIGHT", -10, 7)
-
- -- button.Background:SetAlpha(0)
- -- button.Highlight:SetTexture(1, 1, 1)
- -- button.Highlight:SetVertexColor(1, 1, 1, .3)
- -- button.Highlight:SetInside(button.backdrop)
- -- button.Selected:SetTexture(0.9, 0.8, 0.1)
- -- button.Selected:SetVertexColor(1, 1, 1, .3)
- -- button.Selected:SetInside(button.backdrop)
-
- -- button.Icon:Size(20)
- -- button.Icon:ClearAllPoints()
- -- button.Icon:Point("LEFT", 6, 0)
- -- button.Icon:SetDrawLayer("BORDER")
- -- button.Icon:SetTexCoord(0.296875, 0.703125, 0.3125, 0.71875)
-
- -- button.iconBackdrop = CreateFrame("Frame", nil, button)
- -- button.iconBackdrop:SetTemplate()
- -- button.iconBackdrop:SetOutside(button.Icon)
- -- button.Icon:SetParent(button.iconBackdrop)
- -- end
-
- -- StorePremiumButtons:SetSize(143, 36)
- -- StorePremiumButtons:ClearAllPoints()
- -- StorePremiumButtons:SetPoint("LEFT", StoreMoneyButton4, "RIGHT", 1, 0)
- -- StorePremiumButtons:CreateBackdrop("Transparent")
- -- StorePremiumButtons.backdrop:Point("TOPLEFT", 28, -7)
- -- StorePremiumButtons.backdrop:Point("BOTTOMRIGHT", -10, 7)
-
- -- StorePremiumButtons.Background:SetAlpha(0)
- -- StorePremiumButtons.Border:SetAlpha(0)
- -- StorePremiumButtons.BorderHighlight:SetAlpha(0)
- -- StorePremiumButtons.IconBorder:SetAlpha(0)
- -- StorePremiumButtons.IconBorderHighlight:SetAlpha(0)
- -- StorePremiumButtons.Text:SetPoint("CENTER", 6, 0)
-
- -- StorePremiumButtons.Icon:Size(20)
- -- StorePremiumButtons.Icon:Point("LEFT", 6, 0)
- -- StorePremiumButtons.Icon:SetTexture("INTERFACE\\ICONS\\VIP")
- -- StorePremiumButtons.Icon:SetTexCoord(unpack(E.TexCoords))
-
- -- StorePremiumButtons.iconBackdrop = CreateFrame("Frame", nil, StorePremiumButtons)
- -- StorePremiumButtons.iconBackdrop:SetTemplate()
- -- StorePremiumButtons.iconBackdrop:SetOutside(StorePremiumButtons.Icon)
- -- StorePremiumButtons.Icon:SetParent(StorePremiumButtons.iconBackdrop)
-
- -- S:HandleCloseButton(StoreFrameCloseButton)
-
- -- -- StoreItemListFrame
- -- S:HandleScrollBar(StoreItemListScrollFrameScrollBar)
- -- StoreItemListScrollFrameScrollBar.BG:SetAlpha(0)
-
- -- for i = 1, #StoreItemListScrollFrame.buttons do
- -- local button = StoreItemListScrollFrame.buttons[i]
-
- -- button.Background:SetAlpha(0)
- -- button.Shadow:SetAlpha(0)
- -- button.IconBorder:SetAlpha(0)
-
- -- button:SetTemplate("Transparent")
-
- -- S:HandleIcon(button.Icon)
- -- button.Count:SetParent(button.backdrop)
-
- -- button.Highlight = button:GetHighlightTexture()
- -- button.Highlight:SetTexture(E.Media.Textures.Highlight)
- -- button.Highlight:SetTexCoord(0, 1, 0, 1)
- -- button.Highlight:SetInside()
- -- end
-
- -- local function StoreFrame_UpdateItemList()
- -- local buttons = StoreItemListScrollFrame.buttons
-
- -- for i = 1, #buttons do
- -- local button = buttons[i]
- -- local data = button.data
- -- if data then
- -- if data.Quality then
- -- local r, g, b = GetItemQualityColor(data.Quality)
- -- button.backdrop:SetBackdropBorderColor(r, g, b)
- -- button.Highlight:SetVertexColor(r, g, b, .35)
- -- else
- -- button.backdrop:SetBackdropBorderColor(unpack(E.media.bordercolor))
- -- button.Highlight:SetVertexColor(1, 1, 1, .35)
- -- end
-
- -- SetPortraitToTexture(button.Icon, "")
- -- button.Icon:SetTexture(data.Texture)
- -- button.Icon:SetTexCoord(unpack(E.TexCoords))
- -- else
- -- -- local r, g, b = GetItemQualityColor(data.Quality)
- -- button.backdrop:SetBackdropBorderColor(unpack(E.media.bordercolor))
- -- button.Highlight:SetVertexColor(1, 1, 1, .35)
- -- end
- -- end
- -- end
- -- hooksecurefunc(StoreItemListScrollFrame, "update", StoreFrame_UpdateItemList)
- -- hooksecurefunc("StoreFrame_UpdateItemList", StoreFrame_UpdateItemList)
-
- -- S:HandleCheckBox(StoreShowAllItemCheckButton)
-
- -- local sortButtons = {
- -- "StoreItemListFrameContainerResetSort",
- -- "StoreItemListFrameContainerSortName",
- -- "StoreItemListFrameContainerSortDiscount",
- -- "StoreItemListFrameContainerSortItemlevel",
- -- "StoreItemListFrameContainerSortPVP",
- -- "StoreItemListFrameContainerSortPrice"
- -- }
-
- -- for i = 1, #sortButtons do
- -- local button = _G[sortButtons[i]]
- -- if button then
- -- button:StripTextures()
- -- button:StyleButton()
- -- button:CreateBackdrop()
- -- button.backdrop:Point("TOPLEFT", 3, -1)
- -- button.backdrop:Point("BOTTOMRIGHT", -3, 5)
- -- button.hover:SetInside(button.backdrop)
- -- button.pushed:SetInside(button.backdrop)
- -- end
- -- end
-
- -- -- StoreSpecialOfferFrame
- -- StoreSpecialOfferTopFrame:StripTextures()
- -- StoreSpecialOfferBanner:SetTemplate()
- -- S:HandleButton(StoreSpecialOfferBanner.LeftPanel.BuyButton)
- -- if SpecialOfferCustomBanners then
- -- for _, frame in pairs(SpecialOfferCustomBanners) do
- -- frame = _G[frame]
- -- if frame and frame.BuyButton then
- -- S:HandleButton(frame.BuyButton)
- -- end
- -- end
- -- end
-
- -- S:HandleNextPrevButton(StoreSpecialOfferBanner.NavigationBar.PrevPageButton, nil, nil, true)
- -- StoreSpecialOfferBanner.NavigationBar.PrevPageButton:Size(32)
- -- S:HandleNextPrevButton(StoreSpecialOfferBanner.NavigationBar.NextPageButton, nil, nil, true)
- -- StoreSpecialOfferBanner.NavigationBar.NextPageButton:Size(32)
-
- -- StoreSpecialOfferBottomFrame:StripTextures()
-
- -- -- for i = 1, 4 do
- -- -- local button = _G["StoreSpecialOfferCardButton"..i]
- -- -- if button then
- -- --
- -- -- end
- -- -- end
-
- -- -- StoreSubCategorySelectFrame
- -- StoreSubCategorySelectFrame:StripTextures()
-
- -- StoreSubCategorySelectContainer.Background:SetAlpha(0)
- -- StoreSubCategorySelectContainer.HeaderText:SetTextColor(1, 1, 1)
-
- -- local slots = {"HeadSlot", "NeckSlot", "ShoulderSlot", "BackSlot", "ChestSlot", "WristSlot",
- -- "HandsSlot", "WaistSlot", "LegsSlot", "FeetSlot", "Finger0Slot", "Trinket0Slot",
- -- "MainHandSlot", "SecondaryHandSlot", "RangedSlot"
- -- }
-
- -- for _, slot in pairs(slots) do
- -- local button = _G["StoreSubCategorySelectContainer"..slot]
- -- S:HandleIcon(button.Icon)
- -- button.backdrop:SetFrameLevel(button:GetFrameLevel() + 2)
-
- -- button.Background:SetWidth(124)
- -- button.Background:SetTexture(E.Media.Textures.Highlight)
- -- button.Background:SetVertexColor(1, 1, 1, .3)
-
- -- button.IconBorder:SetAlpha(0)
- -- button.IconBorderHighlight:SetAlpha(0)
-
- -- button.BackgroundHighlight:SetWidth(124)
- -- button.BackgroundHighlight:SetTexture(E.Media.Textures.Highlight)
- -- button.BackgroundHighlight:SetVertexColor(.9, .8, .1, .3)
-
- -- button.Text:ClearAllPoints()
-
- -- local id = button:GetID()
- -- if id >= 7 and id <= 13 then
- -- button.Background:SetTexCoord(0, .7, 0, 1)
- -- button.BackgroundHighlight:SetTexCoord(0, .7, 0, 1)
- -- button.Text:SetJustifyH("RIGHT")
- -- button.Text:SetPoint("RIGHT", button.Icon, "LEFT", -5, 0)
- -- else
- -- button.Background:SetTexCoord(.3, 1, 0, 1)
- -- button.BackgroundHighlight:SetTexCoord(.3, 1, 0, 1)
- -- button.Text:SetJustifyH("LEFT")
- -- button.Text:SetPoint("LEFT", button.Icon, "RIGHT", 5, 0)
- -- end
- -- end
-
- -- -- StoreItemCardFrame
- -- S:HandleNextPrevButton(StoreItemCardFrameNavigationBarPrevPageButton, nil, nil, true)
- -- StoreItemCardFrameNavigationBarPrevPageButton:Size(32)
- -- S:HandleNextPrevButton(StoreItemCardFrameNavigationBarNextPageButton, nil, nil, true)
- -- StoreItemCardFrameNavigationBarNextPageButton:Size(32)
-
- -- S:HandleButton(StoreRefreshMountListButton)
-
- -- -- StoreModelPreviewFrame
- -- StoreModelPreviewFrame:StripTextures()
- -- StoreModelPreviewFrame.Inset:StripTextures()
- -- StoreModelPreviewFrame:SetTemplate("Transparent")
-
- -- StoreModelPreviewFrame.Display:StripTextures()
- -- StoreModelPreviewFrame.Display.ShadowOverlay:SetAlpha(0)
- -- StoreModelPreviewFrame.Display:SetTemplate()
-
- -- S:HandleRotateButton(StorePreviewModelFrameRotateLeftButton)
- -- S:HandleRotateButton(StorePreviewModelFrameRotateRightButton)
- -- StorePreviewModelFrameRotateRightButton:SetPoint("TOPLEFT", StorePreviewModelFrameRotateLeftButton, "TOPRIGHT", 3, 0)
-
- -- S:HandleCloseButton(StoreModelPreviewFrameCloseButton) -- WTF?
- -- S:HandleButton(StoreModelPreviewFrame.CloseButton, true) -- WTF?
-
- -- --StoreConfirmationFrame
- -- RemoveGoldBorder(StoreConfirmationFrame)
- -- S:HandleCloseButton(StoreConfirmationFrame.CloseButton)
-
- -- S:HandleIcon(StoreConfirmationFrame.Art.Icon)
- -- StoreConfirmationFrame.Art.IconBorder:SetAlpha(0)
- -- StoreConfirmationFrame.Art.backdrop:SetFrameLevel(StoreConfirmationFrame.Art:GetFrameLevel() + 5)
- -- StoreConfirmationFrame.Art.Icon:SetDrawLayer("OVERLAY")
-
- -- S:HandleCheckBox(StoreConfirmationSendGiftCheckButton)
- -- StoreConfirmationSendGiftCheckButton.Text:Point("LEFT", StoreConfirmationSendGiftCheckButton, "RIGHT", -2, 0)
-
- -- StoreConfirmationGiftFrameCharacterName:Height(18)
- -- S:HandleEditBox(StoreConfirmationGiftFrameCharacterName)
- -- StoreConfirmationGiftFrame.CharacterName.Left:SetAlpha(0)
- -- StoreConfirmationGiftFrame.CharacterName.Right:SetAlpha(0)
- -- StoreConfirmationGiftFrame.CharacterName.Middle:SetAlpha(0)
- -- select(10, StoreConfirmationGiftFrame.CharacterName:GetRegions()):SetAlpha(0)
-
- -- S:HandleDropDownBox(StoreConfirmationGiftFrameSelectedStyleDropDown)
-
- -- StoreConfirmationGiftFrame.MessageFrame:StripTextures()
- -- StoreConfirmationGiftFrame.MessageFrame:CreateBackdrop()
-
- -- StoreConfirmationFrame:CreateBackdrop("Transparent")
- -- StoreConfirmationFrame.backdrop:SetOutside(StoreConfirmationFrame.ParchmentTop, nil, nil, StoreConfirmationFrame.BlueGlow)
-
- -- S:HandleButton(StoreConfirmationFrame.BuyButton)
- -- S:HandleButton(StoreConfirmationFrame.BackButton)
-
- -- StoreConfirmationFrame:HookScript("OnShow", function(self)
- -- SetPortraitToTexture(self.Art.Icon, "")
- -- self.Art.Icon:SetTexture(self.data.Texture)
- -- end)
-
- -- -- StoreErrorFrame
- -- RemoveGoldBorder(StoreErrorFrame)
- -- StoreErrorFrame:CreateBackdrop("Transparent")
- -- StoreErrorFrame.backdrop:SetOutside(StoreErrorFrame.ParchmentMiddle)
-
- -- S:HandleButton(StoreErrorFrame.AcceptButton)
-
- -- -- StoreBuyPremiumFrame
- -- RemoveGoldBorder(StoreBuyPremiumFrame)
- -- S:HandleCloseButton(StoreBuyPremiumFrame.CloseButton)
-
- -- StoreBuyPremiumFrame:CreateBackdrop("Transparent")
- -- StoreBuyPremiumFrame.backdrop:SetOutside(StoreBuyPremiumFrame.ParchmentTop, nil, nil, StoreBuyPremiumFrame.BlueGlow)
-
- -- for i = 1, 4 do
- -- local button = _G["SelectPremiumButton"..i]
- -- if button then
- -- button:Size(24)
- -- button:SetFrameLevel(StoreBuyPremiumFrame:GetFrameLevel() + 5)
- -- S:HandleCheckBox(button)
- -- end
- -- end
-
- -- S:HandleButton(StoreBuyPremiumFrame.BuyButton)
-
- -- -- StoreSubscribeFrame
- -- StoreSubscribeFrame:StripTextures()
- -- StoreSubscribeContainer.HeaderBackground:SetAlpha(0)
- -- StoreSubscribeContainer.BackgroundColor:SetAlpha(0)
- -- StoreSubscribeContainer.HeaderBackgroundAlpha:SetAlpha(0)
- -- StoreSubscribeContainer.HeaderText:SetTextColor(1, 1, 1)
-
- -- local function StoreSubscribeItemTemplate(button)
- -- if button then
- -- button:SetTemplate()
- -- button:StyleButton()
-
- -- button.iconTexture:SetDrawLayer("BORDER")
- -- button.iconTexture:SetTexCoord(unpack(E.TexCoords))
- -- button.iconTexture:SetInside()
-
- -- button.slotFrameCollected:Kill()
- -- button.glow:Kill()
- -- button.glow2:Kill()
- -- button.CountBackground:Kill()
- -- button.count:FontTemplate(nil, nil, "OUTLINE")
- -- button.count:ClearAllPoints()
- -- button.count:SetPoint("BOTTOMRIGHT", button, 5, 0)
- -- end
- -- end
-
- -- for i = 1, 3 do
- -- local button = _G["StoreSubscribeItemButton"..i]
- -- StoreSubscribeItemTemplate(button)
- -- end
-
- -- StoreSubscribeItemTemplate(StoreSubscribeSubItemButton1)
-
- -- hooksecurefunc("StoreSubscribeSetup", function()
- -- for i = 1, 3 do
- -- local button = _G["StoreSubscribeItemButton"..i]
- -- if button and button.Link then
- -- local _, _, quality = GetItemInfo(button.Link)
-
- -- if quality then
- -- button:SetBackdropBorderColor(GetItemQualityColor(quality))
- -- else
- -- button:SetBackdropBorderColor(unpack(E.media.bordercolor))
- -- end
- -- end
- -- end
-
- -- local subItemButton = StoreSubscribeSubItemButton1
- -- if subItemButton and subItemButton.Link then
- -- local _, _, quality = GetItemInfo(subItemButton.Link)
-
- -- if quality then
- -- subItemButton:SetBackdropBorderColor(GetItemQualityColor(quality))
- -- else
- -- subItemButton:SetBackdropBorderColor(unpack(E.media.bordercolor))
- -- end
- -- end
- -- end)
-
- -- S:HandleButton(StoreSubscribeContainer.BuyButton1)
- -- S:HandleButton(StoreSubscribeContainer.BuyButton2)
- -- S:HandleButton(StoreSubscribeContainer.BuyButton3)
-
- -- -- StoreDressUPFrame
- -- StoreDressUPFrame:StripTextures()
- -- StoreDressUPFrame.Inset:StripTextures()
- -- StoreDressUPFrame:SetTemplate("Transparent")
- -- S:HandleCloseButton(StoreDressUPFrameCloseButton)
- -- StoreDressUPFrame.Display.YesMountsTex:SetAlpha(0)
- -- StoreDressUPFrame.Display.ShadowOverlay:StripTextures()
- -- StoreDressUPFrame.Display.DressUPModel:SetTemplate("Transparent")
- -- S:HandleControlFrame(StoreDressUPFrame.Display.DressUPModel.controlFrame)
-
- -- S:HandleButton(StoreDressUPFrame.CloseButton, true)
- -- S:HandleButton(StoreDressUPFrame.ResetButton, true)
- -- S:HandleButton(StoreFrameLeftInsetInviteFriendButton, true)
- -- S:HandleButton(StoreFrameLeftInsetReferDetailsButton, true)
- -- StoreReferDetailsFrame:HookScript("OnShow", function()
- -- S:HandleButton(StoreReferDetailsFrameBanner1InviteFriendButton, true)
- -- S:HandleCloseButton(StoreReferDetailsFrame.CloseButton)
- -- end)
- -- -- StoreTransmogrifyFrame
- -- S:HandleEditBox(StoreTransmogrifyFrame.LeftContainer.searchBox)
-
- -- StoreTransmogrifyFrame.LeftContainer:StripTextures()
- -- S:HandleButton(StoreTransmogrifyFrame.LeftContainer.FilterButton)
- -- StoreTransmogrifyFrame.LeftContainer.FilterButton:Point("LEFT", StoreTransmogrifyFrame.LeftContainer.searchBox, "RIGHT", 3, 0)
- -- StoreTransmogrifyFrame.LeftContainer.FilterButton:StripTextures(nil, true)
- -- StoreTransmogrifyFrame.LeftContainer.FilterButton.Icon:SetAlpha(1)
-
- -- S:HandleScrollBar(StoreTransmogrifyFrameLeftContainerScrollFrameScrollBar)
- -- local up = StoreTransmogrifyFrameLeftContainerScrollFrameScrollBarScrollUpButton
- -- local upNormal, upDisabled, upPushed = up:GetNormalTexture(), up:GetDisabledTexture(), up:GetPushedTexture()
- -- upNormal:SetRotation(S.ArrowRotation.up)
- -- upPushed:SetRotation(S.ArrowRotation.up)
- -- upDisabled:SetRotation(S.ArrowRotation.up)
- -- local down = StoreTransmogrifyFrameLeftContainerScrollFrameScrollBarScrollDownButton
- -- local downNormal, downDisabled, downPushed = down:GetNormalTexture(), down:GetDisabledTexture(), down:GetPushedTexture()
- -- downNormal:SetRotation(S.ArrowRotation.down)
- -- downPushed:SetRotation(S.ArrowRotation.down)
- -- downDisabled:SetRotation(S.ArrowRotation.down)
-
- -- for i = 1, #StoreTransmogrifyFrameLeftContainerScrollFrame.buttons do
- -- local button = StoreTransmogrifyFrameLeftContainerScrollFrame.buttons[i]
- -- button.Background:SetAlpha(0)
- -- button.IconBorder:SetAlpha(0)
- -- S:HandleIcon(button.Icon)
- -- button.Icon:SetDrawLayer("BORDER")
- -- button.NewItems:SetParent(button.backdrop)
-
- -- local highlight = button:GetHighlightTexture()
- -- button:SetHighlightTexture(E.Media.Textures.Highlight)
- -- highlight:SetTexCoord(0, 1, 0, 1)
- -- highlight:SetVertexColor(1, 1, 1, .35)
- -- highlight:SetInside()
-
- -- button.selectedTexture:SetTexture(E.Media.Textures.Highlight)
- -- button.selectedTexture:SetTexCoord(0, 1, 0, 1)
- -- button.selectedTexture:SetVertexColor(1, .8, .1, .35)
- -- button.selectedTexture:SetInside()
- -- end
-
- -- StoreTransmogrifyFrame.RightContainer:StripTextures()
- -- StoreTransmogrifyFrame.RightContainer.Background:Kill()
- -- StoreTransmogrifyFrame.RightContainer.ShadowOverlay:StripTextures()
- -- StoreTransmogrifyFrame.RightContainer.ContentFrame.OverlayElements.IconRowBackground:SetAlpha(0)
- -- S:HandleCheckBox(StoreTransmogrifyFrame.RightContainer.ContentFrame.OverlayElements.ShowShoulders)
-
- -- S:HandleButton(StoreTransmogrifyFrame.RightContainer.ContentFrame.BuyButton)
-
- -- for i = 1, 9 do
- -- local button = StoreTransmogrifyFrame.RightContainer.ContentFrame["ItemButton"..i]
- -- button:SetTemplate()
- -- button.IconBorder:Hide()
- -- button.Icon:SetTexCoord(unpack(E.TexCoords))
- -- button.Icon:SetInside()
- -- end
-
- -- hooksecurefunc("StoreTransmogrifyButtonSetIconBorder", function(button, quality)
- -- local backdrop = button.backdrop or button
- -- backdrop:SetBackdropBorderColor(GetItemQualityColor(quality or 1))
- -- end)
-
- -- -- StoreTransmogrifySubCategoryFrame
- -- StoreTransmogrifySubCategoryFrame.Background:SetAlpha(0)
-
- -- for i = 1, 4 do
- -- local frame = _G["StoreTransmogrifySubCategoryFrameCategory"..i]
- -- frame:SetTemplate("Transparent")
- -- frame.BackgroundTexture:SetAlpha(0)
- -- S:HandleButton(frame.Button)
- -- end
-
- -- -- Temp
- -- local categoryIcons = {
- -- [1] = "Interface\\Icons\\achievement_guildperk_mrpopularity",
- -- [2] = "Interface\\Icons\\inv_helmet_25",
- -- [3] = "Interface\\Icons\\Ability_Mount_RidingHorse",
- -- [4] = "Interface\\Icons\\inv_egg_03",
- -- [5] = "Interface\\Icons\\Spell_Fire_Fire",
- -- [6] = "Interface\\Icons\\INV_Scroll_03",
- -- [7] = "Interface\\Icons\\inv_misc_note_02",
- -- [8] = "Interface\\Icons\\inv_misc_bag_10",
- -- [9] = "Interface\\Icons\\inv_crate_01",
- -- [10] = "Interface\\Icons\\INV_Shirt_Blue_01",
- -- }
-
- -- hooksecurefunc("StoreFrame_UpdateCategories", function(self)
- -- for i, button in pairs(self.CategoryFrames) do
- -- if not button.isElvUI then
- -- button:Size(176 + 10, 36 + 2)
- -- button:SetTemplate("Transparent")
- -- button:StyleButton()
-
- -- button.SelectedTexture:SetTexture(.9, .8, .1, .3)
- -- button.SelectedTexture:SetInside()
- -- button.HighlightTexture:SetTexture(1, 1, 1, .3)
- -- button.HighlightTexture:SetInside()
- -- button.ColoredTexture:SetTexture(.9, .1, .1, .3)
- -- button.ColoredTexture:SetInside()
-
- -- button.Category:Hide()
- -- button.PulseTexture:Hide()
- -- button.NewItems:Hide()
- -- button.Text:Point("LEFT", 37, 0)
-
- -- button.Icon:Point("LEFT", 5, 0)
- -- button.Icon:Size(28)
- -- S:HandleIcon(button.Icon)
- -- button.backdrop:SetFrameLevel(button:GetFrameLevel() + 1)
-
- -- if i == 1 then
- -- button:Point("TOP", 0, -9)
- -- else
- -- button:Point("TOPLEFT", self.CategoryFrames[i - 1], "BOTTOMLEFT", 0, -3)
- -- end
-
- -- button.isElvUI = true
- -- end
-
- -- if categoryIcons[i] then
- -- button.Icon:SetTexture(categoryIcons[i])
- -- button.Icon:SetTexCoord(unpack(E.TexCoords))
- -- else
- -- button.Icon:SetTexCoord(0.38, 0.60, 0.38, 0.60)
- -- end
- -- end
- -- end)
-
- -- PromoCodeFrame:HookScript("OnShow",function()
- -- PromoCodeFrame:StripTextures()
- -- PromoCodeFrame:CreateBackdrop("Transparent")
- -- S:HandleButton(PromoCodeFrameActionButton)
- -- S:HandleEditBox(PromoCodeFrameContainerPromoCodeEditBoxFrame)
- -- S:HandleCloseButton(PromoCodeFrame.CloseButton)
- -- PromoCodeFrameHeaderFrame.description:SetTextColor(1, 1, 1)
-
- -- end)
-
- -- hooksecurefunc("StoreSelectCategory",HookSubButtons)
-
- -- LootCasePreviewFrame:HookScript("OnShow",function()
- -- LootCasePreviewFrame:StripTextures()
-
- -- LootCasePreviewFrame:CreateBackdrop("Transparent")
- -- LootCasePreviewFrameScrollFrame:StripTextures()
- -- LootCasePreviewFrameScrollFrame:CreateBackdrop("Transparent")
- -- LootCasePreviewFrameScrollFrameScrollChild:StripTextures()
- -- LootCasePreviewFrameInset:StripTextures()
- -- -- LootCasePreviewFrameScrollFrame:CreateBackdrop("Transparent")
- -- S:HandleScrollBar(LootCasePreviewFrameScrollFrameScrollBar)
- -- S:HandleCloseButton(LootCasePreviewFrameCloseButton)
- -- LootCasePreviewFrameCloseButton:Show()
- -- for _, v in pairs(LootCasePreviewFrameScrollFrame.buttons) do
- -- if v then
- -- S:HandleButton(v,true)
- -- v.Icon:SetTexCoord(unpack(E.TexCoords))
- -- end
- -- end
- -- end)
+local function LoadSkin()
+ if E.private.skins.blizzard.enable ~= true or E.private.skins.blizzard.store ~= true then return end
+
+ local function ApplySkin()
+ StoreFrame:HookScript("OnShow", function()
+ StoreFrame:SetScale(GetStoreFrameScale())
+ end)
+ StoreFrame:EnableMouse(true)
+ StoreFrame:SetMovable(true)
+ StoreFrame:RegisterForDrag("LeftButton")
+ StoreFrame:SetScript("OnDragStart", function(self)
+ self:StartMoving()
+ end)
+ StoreFrame:SetScript("OnDragStop", function(self)
+ self:StopMovingOrSizing()
+ local frame_x, frame_y = self:GetCenter()
+ frame_x = frame_x*UIParent:GetScale() - GetScreenWidth() / 2
+ frame_y = frame_y*UIParent:GetScale() - GetScreenHeight() / 2
+ self:ClearAllPoints()
+ self:SetPoint("CENTER", UIParent, "CENTER", frame_x, frame_y)
+ end)
+
+ SafeSkinCall(HandleStoreFrame)
+ SafeSkinCall(HookStore)
+ StoreFrame:HookScript("OnShow", function()
+ SafeSkinCall(HandleStoreFrame)
+ end)
+ end
+
+ if _G.StoreFrame then
+ ApplySkin()
+ else
+ local f = CreateFrame("Frame")
+ f:RegisterEvent("PLAYER_LOGIN")
+ f:SetScript("OnEvent", function(self)
+ if _G.StoreFrame then
+ ApplySkin()
+ self:UnregisterAllEvents()
+ end
+ end)
+ end
end
-S:AddCallback("Sirus_Store", LoadSkin)
\ No newline at end of file
+
+S:AddCallback("Sirus_Store", LoadSkin)
diff --git a/Modules/Skins/Skins.lua b/Modules/Skins/Skins.lua
index 4def03a71..55132b303 100644
--- a/Modules/Skins/Skins.lua
+++ b/Modules/Skins/Skins.lua
@@ -1222,6 +1222,76 @@ function S:HandleEditBox(frame)
end
end
+function S:HandleSearchBox(frame, unskinned)
+ frame:SetTextInsets(16, 20, 0, 0)
+
+ frame.Instructions = frame:CreateFontString(nil, 'ARTWORK', 'GameFontDisableSmall')
+ frame.Instructions:SetText(SEARCH)
+ frame.Instructions:SetPoint('TOPLEFT', frame, 'TOPLEFT', 15, 0)
+ frame.Instructions:SetPoint('BOTTOMRIGHT', frame, 'BOTTOMRIGHT', -20, 0)
+ frame.Instructions:SetTextColor(0.35, 0.35, 0.35)
+ frame.Instructions:SetJustifyH('LEFT')
+ frame.Instructions:SetJustifyV('MIDDLE')
+
+ frame.searchIcon = frame:CreateTexture(nil, 'OVERLAY')
+ frame.searchIcon:SetTexture([[Interface\Common\UI-Searchbox-Icon]])
+ frame.searchIcon:SetVertexColor(0.6, 0.6, 0.6)
+ frame.searchIcon:Size(14)
+ frame.searchIcon:Point('LEFT', 0, -2)
+
+ frame.clearButton = CreateFrame('Button', nil, frame)
+ frame.clearButton:Size(14)
+ frame.clearButton:Point('RIGHT', -3, 0)
+
+ frame.clearButton.texture = frame.clearButton:CreateTexture()
+ frame.clearButton.texture:SetTexture([[Interface\FriendsFrame\ClearBroadcastIcon]])
+ frame.clearButton.texture:SetAlpha(0.5)
+ frame.clearButton.texture:Size(16)
+ frame.clearButton.texture:Point('CENTER', 0, 0)
+
+ frame.clearButton:SetScript('OnEnter', function(self) self.texture:SetAlpha(1.0) end)
+ frame.clearButton:SetScript('OnLeave', function(self) self.texture:SetAlpha(0.5) end)
+ frame.clearButton:SetScript('OnMouseDown', function(self) if self:IsEnabled() then self.texture:Point('CENTER', 1, -1) end end)
+ frame.clearButton:SetScript('OnMouseUp', function(self) self.texture:Point('CENTER') end)
+ frame.clearButton:SetScript('OnClick', function(self)
+ local editBox = self:GetParent()
+ editBox:SetText('')
+ editBox:ClearFocus()
+ end)
+
+ frame:SetScript('OnShow', nil)
+ frame:SetScript('OnEditFocusLost', function(self)
+ if self:GetText() == '' then
+ self.searchIcon:SetVertexColor(0.6, 0.6, 0.6)
+ self.clearButton:Hide()
+ end
+ end)
+ frame:SetScript('OnEditFocusGained', function(self)
+ self.searchIcon:SetVertexColor(1.0, 1.0, 1.0)
+ self.clearButton:Show()
+ end)
+ frame:HookScript('OnTextChanged', function(self)
+ if not self:HasFocus() and self:GetText() == '' then
+ self.searchIcon:SetVertexColor(0.6, 0.6, 0.6)
+ self.clearButton:Hide()
+ else
+ self.searchIcon:SetVertexColor(1.0, 1.0, 1.0)
+ self.clearButton:Show()
+ end
+ if self:GetText() == '' then
+ self.Instructions:Show()
+ else
+ self.Instructions:Hide()
+ end
+ end)
+
+ if not unskinned or frame.backdrop then return end
+
+ frame.backdrop = frame:CreateTexture(nil, 'BACKGROUND')
+ frame.backdrop:SetTexture([[Interface\Common\Common-Input-Border]])
+ frame.backdrop:Point('TOPLEFT', -5, 0)
+ frame.backdrop:Point('BOTTOMRIGHT', 2, -12)
+end
function S:HandleInsetFrame(frame)
assert(frame, "doesn't exist!")
@@ -1426,6 +1496,20 @@ function S:Initialize()
S:SkinAce3()
+ -- ранняя обработка скинов (заполняется до загрузки ElvUI из файла Ace3)
+ if S.db.ace3.enable and S.EarlyAceWidgets then
+ for _, n in next, S.EarlyAceWidgets do
+ if n.SetLayout then
+ S:Ace3_RegisterAsContainer(n)
+ else
+ S:Ace3_RegisterAsWidget(n)
+ end
+ end
+ for _, n in next, S.EarlyAceTooltips do
+ S:Ace3_SkinTooltip(LibStub(n, true))
+ end
+ end
+
--Fire event for all skins that doesn't rely on a Blizzard addon
for index, event in ipairs(self.nonAddonCallbacks.CallPriority) do
self.nonAddonCallbacks[event] = nil
diff --git a/Settings/Profile.lua b/Settings/Profile.lua
index 49b87d519..ecfe6e3a3 100644
--- a/Settings/Profile.lua
+++ b/Settings/Profile.lua
@@ -130,6 +130,26 @@ P.general = {
height = 0,
spacing = 4
},
+ guildBank = {
+ itemQuality = true,
+ itemLevel = true,
+ itemLevelThreshold = 1,
+ itemLevelFont = "Homespun",
+ itemLevelFontSize = 10,
+ itemLevelFontOutline = "MONOCHROMEOUTLINE",
+ itemLevelCustomColorEnable = false,
+ itemLevelCustomColor = { r = 1, g = 1, b = 1 },
+ itemLevelPosition = "BOTTOMRIGHT",
+ itemLevelxOffset = 0,
+ itemLevelyOffset = 2,
+ countFont = "Homespun",
+ countFontSize = 10,
+ countFontOutline = "MONOCHROMEOUTLINE",
+ countFontColor = { r = 1, g = 1, b = 1 },
+ countPosition = "BOTTOMRIGHT",
+ countxOffset = 0,
+ countyOffset = 2,
+ },
reminder = {
enable = false,
durations = true,
@@ -143,9 +163,10 @@ P.general = {
},
kittys = false
}
-
---DataBars
P.databars = {
+ transparent = true,
+ statusbar = "ElvUI Norm",
+ customTexture = false,
experience = {
enable = true,
width = 10,
@@ -160,6 +181,13 @@ P.databars = {
hideInVehicle = false,
hideInCombat = false,
showBubbles = false,
+ clickThrough = false,
+ frameLevel = 1,
+ frameStrata = "LOW",
+ displayText = true,
+ anchorPoint = "CENTER",
+ xOffset = 0,
+ yOffset = 0,
questXP = {
color = { r = 0, g = 1, b = 0, a = 0.4 },
tooltip = true,
@@ -180,7 +208,14 @@ P.databars = {
hideAtMaxLevel = true,
hideInVehicle = false,
hideInCombat = false,
- showBubbles = false
+ showBubbles = false,
+ clickThrough = false,
+ frameLevel = 1,
+ frameStrata = "LOW",
+ displayText = true,
+ anchorPoint = "CENTER",
+ xOffset = 0,
+ yOffset = 0
},
reputation = {
enable = false,
@@ -194,7 +229,35 @@ P.databars = {
orientation = "VERTICAL",
hideInVehicle = false,
hideInCombat = false,
- showBubbles = false
+ showBubbles = false,
+ clickThrough = false,
+ frameLevel = 1,
+ frameStrata = "LOW",
+ displayText = true,
+ anchorPoint = "CENTER",
+ xOffset = 0,
+ yOffset = 0
+ },
+ threat = {
+ enable = true,
+ width = 222,
+ height = 10,
+ textFormat = "NONE",
+ textSize = 11,
+ font = "PT Sans Narrow",
+ fontOutline = "SHADOW",
+ mouseover = false,
+ orientation = "AUTOMATIC",
+ showBubbles = false,
+ clickThrough = false,
+ frameLevel = 1,
+ frameStrata = "LOW",
+ displayText = true,
+ tankStatus = true,
+ smoothbars = true,
+ anchorPoint = "CENTER",
+ xOffset = 0,
+ yOffset = 0
}
}
@@ -254,6 +317,17 @@ P.bags = {
questItem = { r = 1, g = 0.30, b = 0.30 }
}
},
+ shownBags = {},
+ autoToggle = {
+ enable = true,
+ bank = true,
+ mail = true,
+ vendor = true,
+ soulBind = true,
+ auctionHouse = true,
+ professions = false,
+ guildBank = false
+ },
vendorGrays = {
enable = false,
interval = 0.2,
@@ -487,6 +561,15 @@ local NP_EliteIcon = {
yOffset = 0,
}
+local NP_IconFrame = {
+ enable = false,
+ size = 24,
+ parent = 'Nameplate',
+ position = 'CENTER',
+ xOffset = 0,
+ yOffset = 42,
+}
+
local NP_PvPIndicator = {
enable = true,
size = 24,
@@ -704,6 +787,7 @@ for unit, data in next, P.nameplates.units do
end
if unit:find('_NPC') then
data.eliteIcon = CopyTable(NP_EliteIcon)
+ data.iconFrame = CopyTable(NP_IconFrame)
end
end
end
@@ -721,6 +805,9 @@ P.nameplates.units.PLAYER.nameOnly = false
P.nameplates.units.PLAYER.buffs.priority = 'Blacklist,blockNoDuration,Personal'
P.nameplates.units.PLAYER.debuffs.priority = 'Blacklist,blockNoDuration,Personal'
+P.nameplates.units.FRIENDLY_NPC.iconFrame.enable = true
+P.nameplates.units.ENEMY_NPC.iconFrame.enable = true
+
P.nameplates.units.FRIENDLY_PLAYER.health.enable = true
P.nameplates.units.FRIENDLY_PLAYER.level.enable = true
P.nameplates.units.FRIENDLY_PLAYER.buffs.priority = 'Blacklist,blockNoDuration,Personal,TurtleBuffs'
@@ -4180,6 +4267,12 @@ P.actionbar = {
fontSize = 10,
fontOutline = "MONOCHROMEOUTLINE",
fontColor = { r = 1, g = 1, b = 1 },
+ useHotkeyColor = false,
+ hotkeyColor = { r = 1, g = 1, b = 1 },
+ useCountColor = false,
+ countColor = { r = 1, g = 1, b = 1 },
+ useMacroColor = false,
+ macroColor = { r = 1, g = 1, b = 1 },
macrotext = false,
hotkeytext = true,
@@ -4443,7 +4536,14 @@ P.databars.honor = {
mouseover = false,
orientation = "VERTICAL",
hideInVehicle = false,
- hideInCombat = false
+ hideInCombat = false,
+ clickThrough = false,
+ frameLevel = 1,
+ frameStrata = "LOW",
+ displayText = true,
+ anchorPoint = "CENTER",
+ xOffset = 0,
+ yOffset = 0
}
-- Sirus