From 421f3ee14b7deec640aaa5405b5250f92c9b19a4 Mon Sep 17 00:00:00 2001 From: Christopher Tse Date: Tue, 31 Mar 2020 22:41:18 +1100 Subject: [PATCH] Moved files back to top level directory --- BlizzChatIntegration.lua | 183 +++++++++++++++++++++++++++++++++++ ChatBubblePool.lua | 240 ++++++++++++++++++++++++++++++++++++++++++++++ MainFrame.lua | 33 +++++++ MainFrame.xml | 51 ++++++++++ RoleplayChatBubbles.toc | 8 ++ TotalRP3.lua | 108 +++++++++++++++++++++ 6 files changed, 623 insertions(+) create mode 100644 BlizzChatIntegration.lua create mode 100644 ChatBubblePool.lua create mode 100644 MainFrame.lua create mode 100644 MainFrame.xml create mode 100644 RoleplayChatBubbles.toc create mode 100644 TotalRP3.lua diff --git a/BlizzChatIntegration.lua b/BlizzChatIntegration.lua new file mode 100644 index 0000000..a175441 --- /dev/null +++ b/BlizzChatIntegration.lua @@ -0,0 +1,183 @@ +-- Author : Chrono +-- Create Date : 3/30/2020 8:27:47 PM + +local ADDON_NAME, Import = ... + +--This is an invisible frame that is created to receive OnUpdate calls +--Attached to the WorldFrame so it receives events even when the UI is hidden +local Timer = CreateFrame("Frame","RPChatBubble-Timer",WorldFrame) +Timer:SetFrameStrata("TOOLTIP") -- higher strata is called last + +--Alias functions +Timer.Start = Timer.Show +Timer.Stop = function(self) + Timer:Hide() + Timer.elapsed = 0 +end + +Timer:Stop() + +local numBubbles = 0; +local messageToSender = {}; + +local MANAGED_CHANNELS = { + "CHAT_MSG_SAY", "CHAT_MSG_YELL", "CHAT_MSG_MONSTER_SAY", "CHAT_MSG_MONSTER_YELL" +}; + +local function getPadding(numSpaces) + local str = ">"; + for i=1,numSpaces,1 do + str = "-" .. str + end + return str +end + +local function printTable(t, depth) + local padding = getPadding(depth) + for key, value in pairs(t) do + if type(value) == "table" then + print(padding .. key .. " = (table):"); + printTable(value, depth + 1); + else + print(padding .. key .. " ("..type(value)..") = " .. tostring(value) ); + end + end +end + +local function getChatBubbleText(chatBubble) + for i = 1, chatBubble:GetNumRegions() do + local region = select(i, chatBubble:GetRegions()) + if region:GetObjectType() == "FontString" then + return region:GetText() + end + end +end + +local function getNamedPoint(chatBubble,pointName) + for i = 1, chatBubble:GetNumPoints() do + local point, relativeTo, relativePoint, xOfs, yOfs = chatBubble:GetPoint(i); + if point == pointName then + return relativeTo, relativePoint, xOfs, yOfs; + end + end +end + +local function skinBubble(chatBubble) + local message = getChatBubbleText(chatBubble); + local name = messageToSender[message] + + local NameText = CreateFrame("EditBox","BlizzBoxNameText",chatBubble); + NameText:SetFrameStrata("MEDIUM"); --This is the default but better to be explicit + --NameText:SetMultiLine(true); + NameText:SetAutoFocus(false); + --NameText:EnableMouse(false); + NameText:SetSize(700,11); + --NameText:SetPoint("CENTER"); + NameText:SetPoint("BOTTOMLEFT",chatBubble,"TOPLEFT",13,2); + NameText:SetFontObject("GameFontNormal"); + NameText:SetText(name); + --local tex = NameText:CreateTexture(nil,"ARTWORK"); + --tex:SetAllPoints() + --tex:SetTexture(255,255,255); + NameText.stringMeasure = NameText:CreateFontString(nil,"OVERLAY","GameFontNormal"); + NameText.stringMeasure:SetText(name); + + local NameBg = CreateFrame("Frame","BlizzBubbleNameBG",NameText); + NameBg:SetPoint("TOPLEFT",-1,14); + NameBg:SetPoint("BOTTOMLEFT",-1,-2); + NameBg:SetWidth(NameText.stringMeasure:GetStringWidth()); + NameBg:SetFrameStrata("BACKGROUND"); + + + local midTex = NameBg:CreateTexture("nameBoxBackgroundTex-middle","BACKGROUND"); + midTex:SetTexture("Interface/CHATFRAME/ChatFrameTab-BGMid.blp"); + midTex:SetPoint("TOPLEFT",8,0); + midTex:SetPoint("BOTTOMRIGHT",-7,0); + local leftTex = NameBg:CreateTexture("nameBoxBackgroundTex-left","BACKGROUND"); + leftTex:SetTexture("Interface/CHATFRAME/ChatFrameTab-BGLeft.blp"); + leftTex:SetPoint("TOPRIGHT",midTex,"TOPLEFT"); + leftTex:SetPoint("BOTTOMRIGHT",midTex,"BOTTOMLEFT"); + local rightTex = NameBg:CreateTexture("nameBoxBackgroundTex-right","BACKGROUND"); + rightTex:SetTexture("Interface/CHATFRAME/ChatFrameTab-BGRight.blp"); + rightTex:SetPoint("TOPLEFT",midTex,"TOPRIGHT"); + rightTex:SetPoint("BOTTOMLEFT",midTex,"BOTTOMRIGHT"); + + local relativeTo, relativePoint, xOfs, yOfs = getNamedPoint(chatBubble,"BOTTOMRIGHT"); + chatBubble.string = relativeTo; + chatBubble.defaultXOfs = xOfs; + chatBubble.fixWidth = function(self) + local nameWidth = NameText.stringMeasure:GetWidth(); + NameBg:SetWidth(nameWidth); + local stringWidth = self.string:GetStringWidth(); + local expectedWidth = stringWidth + 32; + local requiredWidthForName = nameWidth + 14 + 2 + 16; + local defaultXOfs = self.defaultXOfs; + local relativeTo, relativePoint, xOfs, yOfs = getNamedPoint(self,"BOTTOMRIGHT"); + local currHeight = self:GetHeight(); + if ( expectedWidth < requiredWidthForName ) then + local diff = requiredWidthForName - expectedWidth; + self:SetPoint("BOTTOMRIGHT",relativeTo,relativePoint,defaultXOfs+diff,yOfs); + else + self:SetPoint("BOTTOMRIGHT",relativeTo,relativePoint,defaultXOfs,yOfs); + end + end + chatBubble:fixWidth(); + + chatBubble.nameText = NameText; + chatBubble.SetName = function(self,text) + NameText:SetText(text) + NameText.stringMeasure:SetText(text); + self:fixWidth(); + end; + chatBubble.rpSkinned = true; + numBubbles = numBubbles + 1; +end + +local function checkBubbles(chatBubbles) + --chatBubbles is an indexed array of frames + for _, chatBubble in pairs(chatBubbles) do + if not chatBubble.rpSkinned then + skinBubble(chatBubble) + else + local message = getChatBubbleText(chatBubble) + chatBubble:SetName(messageToSender[message]) + end + end +end + +Timer:SetScript("OnUpdate", function(self, elapsed) + self.elapsed = self.elapsed + elapsed + -- 0.01 Seconds after the chat message happened... + if self.elapsed > 0.01 then + self:Stop(); + --This returns all chat bubbles created through default Blizz's UI. Custom chat bubbles aren't seen here + chatBubbles = C_ChatBubbles:GetAllChatBubbles() + checkBubbles(chatBubbles) + end +end) + +local function onChatMessage(_, event, message, sender, ...) + local name = GetColoredName(event, message, sender, ...); + messageToSender[message] = name; + --At the time of the chat event, the chat bubble hasn't been created yet. So we'll wait 0.01 seconds before looking for chat bubbles to skin. + Timer:Start(); + return false, message, sender, ... +end + +local function resetChatHandler(self) + for _, channel in pairs(MANAGED_CHANNELS) do + ChatFrame_RemoveMessageEventFilter(channel, onChatMessage) + ChatFrame_AddMessageEventFilter(channel, onChatMessage); + end +end + +local function onStart(self) + for _, channel in pairs(MANAGED_CHANNELS) do + ChatFrame_AddMessageEventFilter(channel, onChatMessage); + end +end + +Import.modules.BlizzChatIntegration = {}; +Import.modules.BlizzChatIntegration.name = "BlizzChatIntegration"; +Import.modules.BlizzChatIntegration.OnStart = onStart; +Import.modules.BlizzChatIntegration.ResetChatHandler = resetChatHandler \ No newline at end of file diff --git a/ChatBubblePool.lua b/ChatBubblePool.lua new file mode 100644 index 0000000..e6ffcf6 --- /dev/null +++ b/ChatBubblePool.lua @@ -0,0 +1,240 @@ +-- Author : Christopher Tse +-- Create Date : 3/28/2020 1:37:28 PM + +local ADDON_NAME, Import = ...; + +local pool = {} + +Import.ChatBubblePool = {}; +local ChatBubblePool = Import.ChatBubblePool + +local function adjustChatBubbleWidth(chatBubble) + local editBox = chatBubble.editBox; + local strWidth = editBox.stringMeasure:GetStringWidth(); + local bg = editBox.background; + local padding = bg.padding; + local nameBox = chatBubble.nameBox + local nameBoxWidth = nameBox:GetFullWidth(); + local minWidth = 64; + if ( nameBoxWidth ~= nil) then + local nameBoxMargin = nameBox.margin.L + nameBox.margin.R; + minWidth = max(64, nameBoxWidth + nameBoxMargin); + end + local maxWidth = chatBubble:GetWidth() + if ( strWidth < minWidth ) then + bg:SetWidth(minWidth + padding) + elseif ( minWidth < strWidth and strWidth < maxWidth ) then + bg:SetWidth(strWidth + padding) + else + bg:SetWidth(maxWidth + padding ) + end +end + +local function adjustNameBoxWidth(chatBubble) + local nameBox = chatBubble.nameBox; + local nameBoxBg = nameBox.background; + local strWidth = nameBox.stringMeasure:GetStringWidth(); + local minWidth = 32; + local padding = nameBox.padding.L + nameBox.padding.R; + --The max width usually won't be reached because of the character limit on the name box + local maxWidth = chatBubble:GetWidth() - padding - nameBox.margin.L + if ( strWidth < minWidth ) then + nameBoxBg:SetWidth(minWidth + padding); + elseif ( minWidth < strWidth and strWidth < maxWidth ) then + nameBoxBg:SetWidth(strWidth + padding); + else + nameBoxBg:SetWidth(maxWidth + padding); + end +end + +local function pickNameColor(chatBubble) + local r, g, b = chatBubble:GetNameColor() + ColorPickerFrame:SetColorRGB(r,g,b); + ColorPickerFrame.hasOpacity = false; + ColorPickerFrame.func = function(self) chatBubble:SetNameColor(ColorPickerFrame:GetColorRGB()) end; + ColorPickerFrame.cancelFunc = function(self) chatBubble:SetNameColor(r,g,b) end; + ColorPickerFrame:Show(); +end + +local function closeBubble(chatBubble) + chatBubble:Hide(); + chatBubble:SetMessage(""); + chatBubble:SetName(""); + chatBubble.nameBox:SetAlpha(0.01) + chatBubble:ClearAllPoints(); + chatBubble:SetPoint("TOPLEFT",WorldFrame,"CENTER",-chatBubble.center.x,-chatBubble.center.y); + chatBubble.isAvailable = true; +end + +function ChatBubblePool.getChatBubble() + for index, chatBubble in ipairs(pool) do + if chatBubble.isAvailable then + chatBubble:Show() + chatBubble.isAvailable = false; + return chatBubble + end + end + + -- If we got here, there isn't any available chat bubble so create a new one + local frameName = "RPChatBubble" .. #pool + + local newChatBubble = CreateFrame("Frame",frameName,nil) + newChatBubble:SetWidth(300) + newChatBubble:SetHeight(300) + newChatBubble:SetMovable(true) + newChatBubble:SetFrameStrata("LOW") + newChatBubble.isAvailable = false + table.insert(pool, newChatBubble); + + --chatBubbleTail:EnableMouse(true) + --chatBubbleTail:SetMovable(true) + --chatBubbleTail:RegisterForDrag("LeftButton") + --chatBubbleTail:SetScript("OnDragStart", function(self) self:StartMoving() end) + --chatBubbleTail:SetScript("OnDragStop", function(self) self:StopMovingOrSizing() end) + + local editBox = CreateFrame("EditBox",frameName.."-EditBox",newChatBubble); + editBox:SetPoint("TOPLEFT",newChatBubble); + editBox:SetPoint("TOPRIGHT",newChatBubble); + editBox:SetMultiLine(true); + editBox:SetAutoFocus(false); + editBox:SetFontObject("ChatBubbleFont"); + editBox:SetScript("OnEnterPressed", function(self) self:ClearFocus() end); + editBox:SetScript("OnEscapePressed", function(self) self:ClearFocus() end); + --Apparently, the below code stops the user from being able to change the cursor location + --editBox:EnableMouse(true) + --editBox:SetScript("OnMouseDown", function(self) newChatBubble:StartMoving() end ) + --editBox:SetScript("OnMouseUp", function(self) newChatBubble:StopMovingOrSizing() end ) + + newChatBubble.editBox = editBox; + --This is a hack that centers the newChatBubble using the center of the editbox + newChatBubble.center = { x=editBox:GetWidth()/2, y=editBox:GetHeight()/2 }; + newChatBubble:SetPoint("TOPLEFT",WorldFrame,"CENTER",-newChatBubble.center.x,-newChatBubble.center.y); + + local chatBubbleBackground = CreateFrame("Frame",frameName.."Background",editBox); + chatBubbleBackground:SetBackdrop({ + bgFile="Interface\\Tooltips\\CHATBUBBLE-BACKGROUND.BLP", + edgeFile="Interface\\Tooltips\\CHATBUBBLE-BACKDROP.BLP", + tile=true, tileSize=16, edgeSize=16, + insets={left=16, right=16, top=16, bottom=16} + }) + chatBubbleBackground:EnableMouse(true) + chatBubbleBackground:SetPoint("TOPLEFT",editBox,"TOPLEFT",-16,16) + chatBubbleBackground:SetPoint("BOTTOMLEFT",editBox,"BOTTOMLEFT",-16,-16) + chatBubbleBackground.padding = 32; + chatBubbleBackground:SetWidth(64 + chatBubbleBackground.padding) + chatBubbleBackground:SetFrameStrata("BACKGROUND") + chatBubbleBackground:EnableMouse(true) + chatBubbleBackground:SetScript("OnMouseDown", function(self) newChatBubble:StartMoving() end ) + chatBubbleBackground:SetScript("OnMouseUp", function(self) newChatBubble:StopMovingOrSizing() end ) + editBox.background = chatBubbleBackground; + + --This part of the code makes the editbox and the background grow up to 300px as the text grows. + --We use an invisible FontString to measure the length of the text inside the edit box. + editBox.stringMeasure = editBox:CreateFontString(nil,"OVERLAY","ChatBubbleFont"); + editBox.stringMeasure:SetAlpha(0); + editBox:SetScript("OnTextChanged", function(self) + editBox.stringMeasure:SetText(self:GetText()); + adjustChatBubbleWidth(newChatBubble); + end) + + local closeButton = CreateFrame("Button",frameName.."-CloseButton",chatBubbleBackground,"UIPanelCloseButton") + closeButton:SetPoint("CENTER",chatBubbleBackground,"TOPRIGHT",-4,-4); + closeButton:SetScript("OnClick",function(self) closeBubble(newChatBubble) end); + closeButton:SetScript("OnEnter",function(self) closeButton:SetAlpha(1) end); + closeButton:SetScript("OnLeave",function(self) closeButton:SetAlpha(0.1) end); + closeButton:SetAlpha(0.1); + + local nameBoxFrame = CreateFrame("Frame",frameName.."-NameBoxFrame",newChatBubble) + nameBoxFrame:SetSize(250,18); + nameBoxFrame:SetPoint("BOTTOMLEFT",chatBubbleBackground,"TOPLEFT"); + + local nameBox = CreateFrame("EditBox",frameName.."-NameBox",nameBoxFrame); + nameBox:SetFontObject("GameFontNormal"); + nameBox:SetMaxLetters(25); + nameBox.margin = {L=10, R=0, T=4, D=4}; + nameBox.padding = {L=10, R=10}; + nameBox:SetPoint("TOPLEFT",nameBoxFrame,nameBox.margin.L,-nameBox.margin.T); + nameBox:SetPoint("BOTTOMRIGHT",nameBoxFrame,-nameBox.margin.R,nameBox.margin.D); + nameBox:SetAutoFocus(false); + nameBox:SetMultiLine(true); --It's not actually multiline, but this stops the name from scrolling off if the user selects too much of the text. + --The max letters should prevent the edit box from ever reaching more than one line + nameBox:SetScript("OnEnterPressed", function(self) self:ClearFocus() end); + nameBox:SetScript("OnTabPressed", function(self) editBox:SetFocus() end); + nameBox:SetAlpha(0); + nameBox:SetScript("OnEditFocusGained", function(self) self:SetAlpha(1) end); + nameBox:SetScript("OnEditFocusLost", function(self) if self:GetText() == "" then self:SetAlpha(0.01) end end); + newChatBubble.nameBox = nameBox; + + local nameBoxBackground = CreateFrame("Frame",frameName.."-NameBoxBackground",nameBox); + local paddingL = nameBox.padding.L; + nameBoxBackground:SetPoint("BOTTOMLEFT",nameBox,"BOTTOMLEFT",-paddingL,-nameBox.margin.D) + nameBoxBackground:SetPoint("TOPLEFT",nameBox,"TOPLEFT",-paddingL,nameBox.margin.T + 12); + nameBoxBackground:SetWidth(32); + nameBoxBackground:SetFrameStrata("BACKGROUND"); + nameBox.background = nameBoxBackground + + local nameBoxMouseCatcher = CreateFrame("Button",frameName.."-NameBoxMouseCatcher",nameBox); + nameBoxMouseCatcher:SetPoint("BOTTOMLEFT",nameBoxBackground); + nameBoxMouseCatcher:SetPoint("TOPRIGHT",nameBoxBackground); + nameBoxMouseCatcher:SetScript("OnEnter", function(self) if nameBox:GetText() == "" and not nameBox:HasFocus() then nameBox:SetAlpha(0.5) end end); + nameBoxMouseCatcher:SetScript("OnLeave", function(self) if nameBox:GetText() == "" and not nameBox:HasFocus() then nameBox:SetAlpha(0) end end); + nameBoxMouseCatcher:SetScript("OnClick", function(self) nameBox:SetFocus() end); + nameBoxMouseCatcher:SetScript("OnMouseDown", function(self) newChatBubble:StartMoving() end ) + nameBoxMouseCatcher:SetScript("OnMouseUp", function(self) newChatBubble:StopMovingOrSizing() end ) + + local nameBoxColorPicker = CreateFrame("Button",frameName.."-ColorPickerButton",newChatBubble); + nameBoxColorPicker:SetSize(16,16); + nameBoxColorPicker:SetFrameStrata("MEDIUM") -- Needs to be higher than the EditBox to override it + nameBox.colorPickerTex = nameBoxColorPicker:CreateTexture(frameName.."-ColorPickerButton-color","ARTWORK") + nameBox.colorPickerTex:SetPoint("TOPLEFT",2,-2); + nameBox.colorPickerTex:SetPoint("BOTTOMRIGHT",-2,2); + nameBox.colorPickerTex:SetColorTexture(nameBox:GetTextColor()); + local cpBorderTex = nameBoxColorPicker:CreateTexture(frameName.."-ColorPickerButton-border","BORDER"); + cpBorderTex:SetAllPoints(); + cpBorderTex:SetColorTexture(0.1,0.1,0.1); + nameBoxColorPicker:SetPoint("BOTTOMLEFT",nameBoxBackground,"BOTTOMRIGHT"); + nameBoxColorPicker:SetAlpha(0.01); + nameBoxColorPicker:EnableMouse(true); + nameBoxColorPicker:SetScript("OnEnter", function(self) if nameBox:GetText() ~= "" then self:SetAlpha(1); end; end); + nameBoxColorPicker:SetScript("OnLeave", function(self) self:SetAlpha(0.01) end); + nameBoxColorPicker:SetScript("OnClick", function(self) pickNameColor(newChatBubble) end); + + nameBox.stringMeasure = nameBox:CreateFontString(nil,"OVERLAY","GameFontNormal"); + --nameBox.stringMeasure:SetAlpha(0); + nameBox.GetFullWidth = function(self) return nameBoxBackground:GetWidth() end; + nameBox:SetScript("OnTextChanged", function(self) + nameBox.stringMeasure:SetText(self:GetText()); + adjustNameBoxWidth(newChatBubble) + adjustChatBubbleWidth(newChatBubble) + end); + + local midTex = nameBoxBackground:CreateTexture("nameBoxBackgroundTex-middle","BACKGROUND"); + midTex:SetTexture("Interface/CHATFRAME/ChatFrameTab-BGMid.blp"); + midTex:SetPoint("TOPLEFT",16,0); + midTex:SetPoint("BOTTOMRIGHT",-16,0); + local leftTex = nameBoxBackground:CreateTexture("nameBoxBackgroundTex-left","BACKGROUND"); + leftTex:SetTexture("Interface/CHATFRAME/ChatFrameTab-BGLeft.blp"); + leftTex:SetPoint("TOPRIGHT",midTex,"TOPLEFT"); + leftTex:SetPoint("BOTTOMRIGHT",midTex,"BOTTOMLEFT"); + local rightTex = nameBoxBackground:CreateTexture("nameBoxBackgroundTex-right","BACKGROUND"); + rightTex:SetTexture("Interface/CHATFRAME/ChatFrameTab-BGRight.blp"); + rightTex:SetPoint("TOPLEFT",midTex,"TOPRIGHT"); + rightTex:SetPoint("BOTTOMLEFT",midTex,"BOTTOMRIGHT"); + + local chatBubbleTail = CreateFrame("Frame",frameName.."-tail",chatBubbleBackground) + chatBubbleTail:SetBackdrop({ + bgFile="Interface\\Tooltips\\CHATBUBBLE-TAIL.BLP" + }) + chatBubbleTail:SetSize(16,16) + chatBubbleTail:SetPoint("TOPLEFT",chatBubbleBackground,"BOTTOMLEFT",8,3) + + --Functions for outside use + newChatBubble.GetName = nameBox.GetText; + newChatBubble.SetName = function(self,name) nameBox:SetText(name); if (name ~= "" ) then nameBox:SetAlpha(1); end; end; + newChatBubble.GetMessage = editBox.GetText; + newChatBubble.SetMessage = function(self,message) editBox:SetText(message) end; + newChatBubble.GetNameColor = function(self) return nameBox:GetTextColor() end; + newChatBubble.SetNameColor = function(self,r,g,b) nameBox:SetTextColor(r,g,b) nameBox.colorPickerTex:SetColorTexture(r,g,b) end; + + return newChatBubble +end \ No newline at end of file diff --git a/MainFrame.lua b/MainFrame.lua new file mode 100644 index 0000000..68547f9 --- /dev/null +++ b/MainFrame.lua @@ -0,0 +1,33 @@ +-- Author : Christopher Tse +-- Create Date : 3/28/2020 11:43:45 AM + +local ADDON_NAME, Import = ...; + +local mainFrame; +local ChatBubblePool = Import.ChatBubblePool + +function RPChatBubbles_createChatBubble() + return ChatBubblePool.getChatBubble() +end + +function RPChatBubbles_OnLoad(self, event,...) + self:SetClampedToScreen(true); + self:RegisterEvent("ADDON_LOADED"); +end + +function RPChatBubbles_OnEvent(self, event, ...) + if event == "ADDON_LOADED" and ... == ADDON_NAME then + self:RegisterForDrag("LeftButton"); + self:SetScript("OnDragStart", function(self) + self:StartMoving(); + end); + self:SetScript("OnDragStop", function(self) + self:StopMovingOrSizing(); + end); + for moduleName, moduleStructure in pairs(Import.modules) do + moduleStructure:OnStart(); + end + end +end + +Import.modules = {}; \ No newline at end of file diff --git a/MainFrame.xml b/MainFrame.xml new file mode 100644 index 0000000..45e7653 --- /dev/null +++ b/MainFrame.xml @@ -0,0 +1,51 @@ + +