You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

803 lines
21 KiB

-------------------------------------------------------------------------------
-- Module Declaration
--
local plugin = BigWigs:NewPlugin("Messages")
if not plugin then return end
-------------------------------------------------------------------------------
-- Locals
--
local media = LibStub("LibSharedMedia-3.0")
local sink = LibStub("LibSink-2.0")
local FONT = media.MediaType and media.MediaType.FONT or "font"
local labels = {}
local colorModule = nil
local normalMessageAnchor, normalMessageFrame = nil, nil
local emphMessageAnchor, emphMessageFrame, emphMessageText = nil, nil, nil
local labelsPrimaryPoint, labelsSecondaryPoint = nil, nil
local db = nil
local L = BigWigsAPI:GetLocale("BigWigs: Plugins")
plugin.displayName = L.messages
--------------------------------------------------------------------------------
-- Profile
--
plugin.defaultDB = {
fontName = plugin:GetDefaultFont(),
emphFontName = plugin:GetDefaultFont(),
monochrome = false,
emphMonochrome = false,
outline = "THICKOUTLINE",
emphOutline = "THICKOUTLINE",
align = "CENTER",
fontSize = 20,
emphFontSize = 48,
chat = false,
useicons = true,
classcolor = true,
growUpwards = true,
displaytime = 3,
fadetime = 2,
emphUppercase = true,
disabled = false,
emphDisabled = false,
-- Designed by default to grow up into the errors frame (which should be disabled in the BossBlock plugin in 99% of situations)
-- Should not enter the RaidWarningFrame by default (since we grow upwards), which we don't want to block view of
-- By order from top to bottom:
-- >> UIErrorsFrame (anchored to top of UIParent)
-- >> Our message frame (placed at bottom of UIErrorsFrame, growing upwards)
-- >> RaidWarningFrame (anchored to bottom of UIErrorsFrame)
-- >> RaidBossEmoteFrame (anchored to bottom of RaidWarningFrame)
-- 122 (UIErrorsFrame Y position) + 60 (UIErrorsFrame height) = 182
-- Worth noting: RaidWarningFrame height = 70 & RaidBossEmoteFrame height = 80
normalPosition = {"TOP", "TOP", 0, -182},
emphPosition = {"CENTER", "CENTER", 0, 0},
}
local function updateProfile()
db = plugin.db.profile
for k, v in next, db do
local defaultType = type(plugin.defaultDB[k])
if defaultType == "nil" then
db[k] = nil
elseif type(v) ~= defaultType then
db[k] = plugin.defaultDB[k]
end
end
if db.outline ~= "NONE" and db.outline ~= "OUTLINE" and db.outline ~= "THICKOUTLINE" then
db.outline = plugin.defaultDB.outline
end
if db.emphOutline ~= "NONE" and db.emphOutline ~= "OUTLINE" and db.emphOutline ~= "THICKOUTLINE" then
db.emphOutline = plugin.defaultDB.emphOutline
end
if db.align ~= "LEFT" and db.align ~= "CENTER" and db.align ~= "RIGHT" then
db.align = plugin.defaultDB.align
end
if db.fontSize < 10 or db.fontSize > 200 then
db.fontSize = plugin.defaultDB.fontSize
end
if db.emphFontSize < 20 or db.emphFontSize > 200 then
db.emphFontSize = plugin.defaultDB.emphFontSize
end
if db.displaytime < 1 or db.displaytime > 10 then
db.displaytime = plugin.defaultDB.displaytime
end
if db.fadetime < 1 or db.fadetime > 10 then
db.fadetime = plugin.defaultDB.fadetime
end
local emphFlags = nil
if db.emphMonochrome and db.emphOutline ~= "NONE" then
emphFlags = "MONOCHROME," .. db.emphOutline
elseif db.emphMonochrome then
emphFlags = "MONOCHROME"
elseif db.emphOutline ~= "NONE" then
emphFlags = db.emphOutline
end
emphMessageText:SetFont(media:Fetch(FONT, db.emphFontName), db.emphFontSize, emphFlags)
normalMessageAnchor:RefixPosition()
emphMessageAnchor:RefixPosition()
normalMessageFrame:ClearAllPoints()
local align = db.align == "CENTER" and "" or db.align
if db.growUpwards then
labelsPrimaryPoint, labelsSecondaryPoint = "BOTTOM"..align, "TOP"..align
else
labelsPrimaryPoint, labelsSecondaryPoint = "TOP"..align, "BOTTOM"..align
end
normalMessageFrame:SetPoint(labelsPrimaryPoint, normalMessageAnchor, labelsSecondaryPoint)
local flags = nil
if db.monochrome and db.outline ~= "NONE" then
flags = "MONOCHROME," .. db.outline
elseif db.monochrome then
flags = "MONOCHROME"
elseif db.outline ~= "NONE" then
flags = db.outline
end
for i = 1, 4 do
local font = labels[i]
font.animFade:SetStartDelay(db.displaytime)
font.icon.animFade:SetStartDelay(db.displaytime)
font.animFade:SetDuration(db.fadetime)
font.icon.animFade:SetDuration(db.fadetime)
font.icon:SetSize(db.fontSize, db.fontSize)
font:SetHeight(db.fontSize)
font:SetFont(media:Fetch(FONT, db.fontName), db.fontSize, flags)
end
end
--------------------------------------------------------------------------------
-- Anchors & Frames
--
local function showAnchors()
normalMessageAnchor:Show()
emphMessageAnchor:Show()
end
local function hideAnchors()
normalMessageAnchor:Hide()
emphMessageAnchor:Hide()
end
do
local function OnDragStart(self)
self:StartMoving()
end
local function OnDragStop(self)
self:StopMovingOrSizing()
local point, _, relPoint, x, y = self:GetPoint()
plugin.db.profile[self.position] = {point, relPoint, x, y}
plugin:UpdateGUI() -- Update X/Y if GUI is open.
end
local function RefixPosition(self)
self:ClearAllPoints()
local point, relPoint = plugin.db.profile[self.position][1], plugin.db.profile[self.position][2]
local x, y = plugin.db.profile[self.position][3], plugin.db.profile[self.position][4]
self:SetPoint(point, UIParent, relPoint, x, y)
end
local function createAnchor(position, title, titleSize, width, height, saveHeader)
local display = CreateFrame("Frame", nil, UIParent)
display:EnableMouse(true)
display:SetClampedToScreen(true)
display:SetMovable(true)
display:RegisterForDrag("LeftButton")
display:SetWidth(width)
display:SetHeight(height)
display:SetFrameStrata("HIGH")
display:SetFixedFrameStrata(true)
display:SetFrameLevel(5)
display:SetFixedFrameLevel(true)
display:SetScript("OnDragStart", OnDragStart)
display:SetScript("OnDragStop", OnDragStop)
display.RefixPosition = RefixPosition
local point, relPoint = plugin.defaultDB[position][1], plugin.defaultDB[position][2]
local x, y = plugin.defaultDB[position][3], plugin.defaultDB[position][4]
display:SetPoint(point, UIParent, relPoint, x, y)
display.position = position
display:Hide()
local bg = display:CreateTexture()
bg:SetAllPoints(display)
bg:SetColorTexture(0, 0, 0, 0.3)
local header = display:CreateFontString()
header:SetFont(plugin:GetDefaultFont(titleSize))
header:SetShadowOffset(1, -1)
header:SetTextColor(1,0.82,0,1)
header:SetText(title)
header:SetPoint("CENTER", display, "CENTER")
header:SetJustifyV("MIDDLE")
header:SetJustifyH("CENTER")
if saveHeader then
display.header = header
end
return display
end
normalMessageAnchor = createAnchor("normalPosition", L.messages, 12, 200, 20)
emphMessageAnchor = createAnchor("emphPosition", L.emphasizedMessages, 48, 650, 80, true)
normalMessageFrame = CreateFrame("Frame", nil, UIParent)
normalMessageFrame:SetWidth(2000)
normalMessageFrame:SetHeight(80)
normalMessageFrame:SetFrameStrata("FULLSCREEN_DIALOG")
normalMessageFrame:SetFixedFrameStrata(true)
normalMessageFrame:SetFrameLevel(20) -- Behind GUI (level 100) & behind emphasized messages (level 30)
normalMessageFrame:SetFixedFrameLevel(true)
local function FontFinish(self)
self:GetParent():Hide()
if not labels[1]:IsShown() and not labels[2]:IsShown() and not labels[3]:IsShown() and not labels[4]:IsShown() then
normalMessageFrame:Hide()
end
end
local function IconFinish(self)
self:GetParent():Hide()
end
for i = 1, 4 do
local fs = normalMessageFrame:CreateFontString()
fs:SetWidth(0)
fs:SetHeight(0)
fs.elapsed = 0
fs:Hide()
fs.anim = fs:CreateAnimationGroup()
fs.anim:SetScript("OnFinished", FontFinish)
fs.animFade = fs.anim:CreateAnimation("Alpha")
fs.animFade:SetFromAlpha(1)
fs.animFade:SetToAlpha(0)
local icon = normalMessageFrame:CreateTexture()
icon:SetPoint("RIGHT", fs, "LEFT")
icon:SetTexCoord(0.07, 0.93, 0.07, 0.93)
icon:Hide()
fs.icon = icon
icon.anim = icon:CreateAnimationGroup()
icon.anim:SetScript("OnFinished", IconFinish)
icon.animFade = icon.anim:CreateAnimation("Alpha")
icon.animFade:SetFromAlpha(1)
icon.animFade:SetToAlpha(0)
labels[i] = fs
end
end
--------------------------------------------------------------------------------
-- Options
--
plugin.pluginOptions = {
type = "group",
name = "|TInterface\\AddOns\\BigWigs\\Media\\Icons\\Menus\\Messages:20|t ".. L.messages,
childGroups = "tab",
get = function(info) return plugin.db.profile[info[#info]] end,
set = function(info, value)
plugin.db.profile[info[#info]] = value
updateProfile()
end,
order = 2,
args = {
anchorsButton = {
type = "execute",
name = function()
local BL = BigWigsAPI:GetLocale("BigWigs")
if BigWigsOptions:InConfigureMode() then
return BL.toggleAnchorsBtnHide
else
return BL.toggleAnchorsBtnShow
end
end,
desc = function()
local BL = BigWigsAPI:GetLocale("BigWigs")
if BigWigsOptions:InConfigureMode() then
return BL.toggleAnchorsBtnHide_desc
else
return BL.toggleAnchorsBtnShow_desc
end
end,
func = function()
if not BigWigs:IsEnabled() then BigWigs:Enable() end
if BigWigsOptions:InConfigureMode() then
plugin:SendMessage("BigWigs_StopConfigureMode")
else
plugin:SendMessage("BigWigs_StartConfigureMode")
end
end,
width = 1.5,
order = 0.2,
},
testButton = {
type = "execute",
name = BigWigsAPI:GetLocale("BigWigs").testBarsBtn,
desc = BigWigsAPI:GetLocale("BigWigs").testBarsBtn_desc,
func = function()
BigWigs:Test()
end,
width = 1.5,
order = 0.4,
},
normal = {
type = "group",
name = L.messages,
order = 1,
args = {
fontName = {
type = "select",
name = L.font,
order = 1,
values = media:List(FONT),
itemControl = "DDI-Font",
get = function()
for i, v in next, media:List(FONT) do
if v == plugin.db.profile.fontName then return i end
end
end,
set = function(_, value)
local list = media:List(FONT)
plugin.db.profile.fontName = list[value]
updateProfile()
end,
width = 2,
},
outline = {
type = "select",
name = L.outline,
order = 2,
values = {
NONE = L.none,
OUTLINE = L.thin,
THICKOUTLINE = L.thick,
},
},
fontSize = {
type = "range",
name = L.fontSize,
desc = L.fontSizeDesc,
order = 3,
max = 200, softMax = 72,
min = 10,
step = 1,
width = 2,
},
monochrome = {
type = "toggle",
name = L.monochrome,
desc = L.monochromeDesc,
order = 4,
},
align = {
type = "select",
name = L.align,
values = {
L.left,
L.center,
L.right,
},
style = "radio",
order = 5,
get = function() return plugin.db.profile.align == "LEFT" and 1 or plugin.db.profile.align == "RIGHT" and 3 or 2 end,
set = function(_, value)
plugin.db.profile.align = value == 1 and "LEFT" or value == 3 and "RIGHT" or "CENTER"
updateProfile()
end,
},
useicons = {
type = "toggle",
name = L.useIcons,
desc = L.useIconsDesc,
order = 6,
},
classcolor = {
type = "toggle",
name = L.classColors,
desc = L.classColorsDesc,
order = 7,
},
growUpwards = {
type = "toggle",
name = L.growingUpwards,
desc = L.growingUpwardsDesc,
order = 8,
},
displaytime = {
type = "range",
name = L.displayTime,
desc = L.displayTimeDesc,
min = 1,
max = 10,
step = 0.5,
order = 9,
},
fadetime = {
type = "range",
name = L.fadeTime,
desc = L.fadeTimeDesc,
min = 1,
max = 10,
step = 0.5,
order = 10,
},
newline1 = {
type = "description",
name = "\n",
order = 11,
},
chat = {
type = "toggle",
name = L.chatFrameMessages,
desc = L.chatFrameMessagesDesc,
order = 12,
width = 2,
},
disabled = {
type = "toggle",
name = L.disabled,
--desc = "XXX",
order = 13,
confirm = function(_, value)
if value then
return L.disableDesc:format(L.messages)
end
end,
},
header1 = {
type = "header",
name = "",
order = 14,
},
reset = {
type = "execute",
name = L.resetAll,
desc = L.resetMessagesDesc,
func = function() plugin.db:ResetProfile() end,
order = 15,
},
},
},
emphasized = {
type = "group",
name = L.emphasizedMessages,
order = 2,
args = {
heading = {
type = "description",
name = L.emphasizedDesc.. "\n\n",
order = 1,
width = "full",
fontSize = "medium",
},
emphFontName = {
type = "select",
name = L.font,
order = 2,
values = media:List(FONT),
itemControl = "DDI-Font",
get = function()
for i, v in next, media:List(FONT) do
if v == plugin.db.profile.emphFontName then return i end
end
end,
set = function(_, value)
local list = media:List(FONT)
plugin.db.profile.emphFontName = list[value]
updateProfile()
end,
},
emphOutline = {
type = "select",
name = L.outline,
order = 3,
values = {
NONE = L.none,
OUTLINE = L.thin,
THICKOUTLINE = L.thick,
},
},
emphFontSize = {
type = "range",
name = L.fontSize,
desc = L.fontSizeDesc,
order = 4,
softMax = 100, max = 200, min = 20, step = 1,
},
emphMonochrome = {
type = "toggle",
name = L.monochrome,
desc = L.monochromeDesc,
order = 5,
},
emphUppercase = {
type = "toggle",
name = L.uppercase,
desc = L.uppercaseDesc,
order = 6,
width = 2,
hidden = function() -- Hide this option for CJK languages
local loc = GetLocale()
if loc == "zhCN" or loc == "zhTW" or loc == "koKR" then
return true
end
end,
},
emphDisabled = {
type = "toggle",
name = L.disabled,
--desc = "XXX",
order = 7,
confirm = function(_, value)
if value then
return L.disableDesc:format(L.emphasizedMessages)
end
end,
},
},
},
exactPositioning = {
type = "group",
name = L.positionExact,
order = 3,
childGroups = "tab",
args = {
normalPositioning = {
type = "group",
name = L.messages,
order = 1,
inline = true,
args = {
posx = {
type = "range",
name = L.positionX,
desc = L.positionDesc,
min = -2048,
max = 2048,
step = 1,
order = 1,
width = "full",
get = function()
return plugin.db.profile.normalPosition[3]
end,
set = function(_, value)
plugin.db.profile.normalPosition[3] = value
normalMessageAnchor:RefixPosition()
end,
},
posy = {
type = "range",
name = L.positionY,
desc = L.positionDesc,
min = -2048,
max = 2048,
step = 1,
order = 2,
width = "full",
get = function()
return plugin.db.profile.normalPosition[4]
end,
set = function(_, value)
plugin.db.profile.normalPosition[4] = value
normalMessageAnchor:RefixPosition()
end,
},
},
},
emphPositioning = {
type = "group",
name = L.emphasizedMessages,
order = 2,
inline = true,
args = {
posx = {
type = "range",
name = L.positionX,
desc = L.positionDesc,
min = -2048,
max = 2048,
step = 1,
order = 1,
width = "full",
get = function()
return plugin.db.profile.emphPosition[3]
end,
set = function(_, value)
plugin.db.profile.emphPosition[3] = value
emphMessageAnchor:RefixPosition()
end,
},
posy = {
type = "range",
name = L.positionY,
desc = L.positionDesc,
min = -2048,
max = 2048,
step = 1,
order = 2,
width = "full",
get = function()
return plugin.db.profile.emphPosition[4]
end,
set = function(_, value)
plugin.db.profile.emphPosition[4] = value
emphMessageAnchor:RefixPosition()
end,
},
},
},
},
},
},
}
-------------------------------------------------------------------------------
-- Initialization
--
function plugin:OnRegister()
sink.RegisterSink(self, "BigWigsEmphasized", L.bwEmphasized, L.emphasizedSinkDescription, "EmphasizedPrint")
sink.RegisterSink(self, "BigWigs", "BigWigs", L.sinkDescription, "Print")
end
function plugin:OnPluginEnable()
colorModule = BigWigs:GetPlugin("Colors", true)
self:RegisterMessage("BigWigs_ProfileUpdate", updateProfile)
updateProfile()
self:RegisterMessage("BigWigs_Message")
self:RegisterMessage("BigWigs_StartConfigureMode", showAnchors)
self:RegisterMessage("BigWigs_StopConfigureMode", hideAnchors)
end
-------------------------------------------------------------------------------
-- Event Handlers
--
do
local floor = math.floor
local scaleUpTime, scaleDownTime = 0.2, 0.4
local function bounceAnimation(anim, elapsed)
local self = anim:GetParent()
self.elapsed = self.elapsed + elapsed
local min = db.fontSize
local max = min + 10
if self.elapsed <= scaleUpTime then
self:SetTextHeight(floor(min + ((max - min) * self.elapsed / scaleUpTime)))
elseif self.elapsed <= scaleDownTime then
self:SetTextHeight(floor(max - ((max - min) * (self.elapsed - scaleUpTime) / (scaleDownTime - scaleUpTime))))
else
self:SetTextHeight(min)
anim:SetScript("OnUpdate", nil)
end
end
local function getNextSlotDown()
for i = 4, 1, -1 do
labels[i]:ClearAllPoints()
end
-- move 4 -> 1
local old = labels[4]
labels[4] = labels[3]
labels[3] = labels[2]
labels[2] = labels[1]
labels[1] = old
-- reposition
old:SetPoint(labelsPrimaryPoint)
for i = 2, 4 do
labels[i]:SetPoint(labelsPrimaryPoint, labels[i - 1], labelsSecondaryPoint)
end
-- new message at 1
return old
end
local function getNextSlotUp()
for i = 1, 4 do
labels[i]:ClearAllPoints()
end
-- move 1 -> 4
local old = labels[1]
labels[1] = labels[2]
labels[2] = labels[3]
labels[3] = labels[4]
labels[4] = old
-- reposition
old:SetPoint(labelsPrimaryPoint)
for i = 3, 1, -1 do
labels[i]:SetPoint(labelsPrimaryPoint, labels[i + 1], labelsSecondaryPoint)
end
-- new message at 4
return old
end
function plugin:Print(_, text, r, g, b, _, _, _, _, _, icon)
normalMessageFrame:Show()
local slot = db.growUpwards and getNextSlotUp() or getNextSlotDown()
local slotIcon = slot.icon
slot:SetText(text)
slot:SetTextColor(r, g, b, 1)
if icon then
slotIcon:SetTexture(icon)
slotIcon.anim:Stop()
slotIcon:Show()
slotIcon.anim:Play()
else
slotIcon:Hide()
end
slot.anim:Stop()
slot:SetAlpha(1)
slotIcon:SetAlpha(1)
slot.elapsed = 0
slot.anim:SetScript("OnUpdate", bounceAnimation)
slot:Show()
slot.anim:Play()
end
end
do
emphMessageFrame = CreateFrame("Frame", nil, UIParent)
emphMessageFrame:SetFrameStrata("FULLSCREEN_DIALOG")
emphMessageFrame:SetFixedFrameStrata(true)
emphMessageFrame:SetFrameLevel(30) -- Behind GUI (level 100)
emphMessageFrame:SetFixedFrameLevel(true)
emphMessageFrame:SetPoint("CENTER", emphMessageAnchor, "CENTER")
emphMessageFrame:SetWidth(2000)
emphMessageFrame:SetHeight(80)
emphMessageFrame:Hide()
emphMessageText = emphMessageFrame:CreateFontString()
emphMessageText:SetPoint("CENTER", emphMessageFrame, "CENTER")
local updater = emphMessageFrame:CreateAnimationGroup()
updater:SetScript("OnFinished", function()
emphMessageFrame:Hide()
emphMessageAnchor.header:Show() -- Show the header again, for config mode
end)
local anim = updater:CreateAnimation("Alpha")
anim:SetFromAlpha(1)
anim:SetToAlpha(0)
anim:SetDuration(2)
anim:SetStartDelay(1.1)
function plugin:EmphasizedPrint(_, text, r, g, b)
emphMessageAnchor.header:Hide() -- Hide the header, for config mode
emphMessageText:SetText(text)
emphMessageText:SetTextColor(r, g, b)
updater:Stop()
emphMessageFrame:Show()
updater:Play()
end
end
do
local unpack, type = unpack, type
local format, upper, gsub = string.format, string.upper, string.gsub
function plugin:BigWigs_Message(event, module, key, text, color, icon, emphasized)
if not text then return end
local r, g, b = 1, 1, 1 -- Default to white.
if type(color) == "table" then
if color.r and color.g and color.b then
r, g, b = color.r, color.g, color.b
else
r, g, b = unpack(color)
end
elseif colorModule then
r, g, b = colorModule:GetColor(color, module, key)
end
if not db.useicons then icon = nil end
if emphasized and not db.emphDisabled then
if db.emphUppercase then
text = upper(text)
text = gsub(text, "(:%d+|)T", "%1t") -- Fix texture paths that need to end in lowercase |t
end
self:EmphasizedPrint(nil, text, r, g, b)
elseif not db.disabled then
self:Print(nil, text, r, g, b, nil, nil, nil, nil, nil, icon)
end
if db.chat then
-- http://www.wowpedia.org/UI_escape_sequences
-- |TTexturePath:size1:size2:xoffset:yoffset:dimx:dimy:coordx1:coordx2:coordy1:coordy2:red:green:blue|t
if icon then
text = format("|T%s:15:15:0:0:64:64:4:60:4:60|t%s", icon, text)
end
DEFAULT_CHAT_FRAME:AddMessage(text, r, g, b)
end
end
end