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 @@