Feat: Add chat bubble customization - #1933
Conversation
A new Chat Bubbles page on the Chat module restyles the bubbles the game already draws: background colour and opacity, border size and colour, font size and colour, padding, maximum width and a vertical nudge. Off by default, and opt-in behind a confirmation, because switching it on also switches Blizzard's own bubbles on. Blizzard's bubbles are not forbidden outside instances, so rather than replace them the feature rides on them: their switches stay on, the chrome is blanked, and a styled frame is hung on the frame that carries their position, which the engine has already placed over the speaker's head. No nameplate is involved and the player's own lines work too. Inside an instance those frames ARE forbidden to us and can be neither read nor blanked, so the feature stays away entirely for the stay and Blizzard's own bubbles keep working. Blizzard's switches are borrowed, never kept. chatBubbles is taken over while at least one of Say, Yell, NPCs and Emotes is ticked. Party and Raid each have a switch of their own, taken over only while that channel is ticked, and those two ticks are seeded from the live switches the first time the feature is enabled so group bubbles never turn up in a chat that had none. Every switch is handed back on disable, on entering an instance, and at PLAYER_LOGOUT, which is the only hand-back that survives the addon being disabled outright. The bookkeeping lives in memory only, so no profile or account export can carry one player's switch values to somebody else. A bubble is claimed in the same frame it appears: an OnShow hook reads its text and matches it against the chat lines still waiting, with a timed sweep behind it for the cases a hook cannot cover, and a stamp on both sides so a bubble that was already standing cannot be claimed by a later line that happens to match it. What gets drawn is the bubble's own text, so whatever the client formatted into it, an emote's name prefix or a filled token, carries over unchanged. Access to Blizzard's frames is guarded throughout, because one can be reclassified as forbidden underneath us, and every comparison against a chat string is secret-value safe. Nothing runs while the feature is off: no frames, no event registrations, no hooks on Blizzard's frames and no CVar writes.
…ry channel The bubble drew every channel in a single configured colour, so a yell and a whisper from an NPC came out looking the same. A cog beside the Font row's colour swatch now carries one toggle, Follow Blizzard Default Color, off by default so nothing changes for anyone who does not go looking for it. The colour is read off Blizzard's own FontString when the bubble is claimed, not derived from a table of our own mapping chat events to chat types. The engine has already answered the question per channel, and reading its answer covers channels we never enumerate. The read is guarded like every other access to a Blizzard frame and secret-tested, since a restricted frame answers with numbers SetTextColor cannot take; if it comes back empty the bubble falls back to the configured colour rather than to nothing. The captured colour is kept for the life of the claim, so toggling the option restyles bubbles already on screen through RefreshStyle, and it is cleared when the frame goes back to the pool so no speaker inherits the previous one's colour. While the option is on the colour swatch greys out and says why, with the gate's own wording kept for the case where the whole feature is off.
|
Yessss I'll use this a lot since chat bubble customization only works outside of instances, I think it'd be cool if this module included a "automatically hide chat bubbles in instances"(essentially toggling the |
Our own bubbles never draw inside a dungeon, raid, scenario or battleground: the engine's bubble frames are forbidden to addons there, which is why the feature suspends itself for the stay. Until now that left Blizzard's own bubbles showing at whatever the player had set. This option switches all three of them off for as long as the instance lasts and back on the way out. Off by default. CVars are the only lever available, not a shortcut. A forbidden frame can be neither read nor hidden, and C_ChatBubbles offers nothing to disable, so there is no addon-side way to suppress a bubble the engine has decided to draw there. ApplyCVar now takes the value a switch has to sit at rather than a flag saying we want it on, which is the whole mechanical change: hiding means forcing one off, and the existing take-over bookkeeping then covers the rest. Each switch keeps the snapshot it was first taken over on, so the pass that runs on leaving the instance hands it back off that same snapshot, and a logout inside one restores it just as well.
Thank you for the suggestion. I added that to the PR. :) |
Every chat event the bubble renderer listens to is declared SecretInChatMessagingLockdown in Blizzard's own ChatInfoDocumentation, and none of their text payloads carries NeverSecret, so arg1 is a secret string whenever that lockdown is in effect. That is a state of the chat system rather than a property of the map, so it reaches the open world, where the feature is not suspended. Comparing a secret to nil raises, so the existence test is type() everywhere now and the secret guard runs ahead of any comparison: the chat handler, OnBlizzShow, Sweep, BlizzTextColor, MatchPending, and both SafeEq and SafeContains, whose contract explicitly allows a secret on either side and which raised on their own first line. Guard the remaining unguarded reads of Blizzard's frames the same way. BlizzParts resolved outer.GetChildren and child.String outside its pcall, and the second of those ran on every call rather than once per frame. Anchor let PP.Point reach SetPoint against a frame that may have been reclassified as forbidden since the claim, which RefreshStyle repeats per slider step. HookOuter called HookScript unguarded. A failed anchor now hands the bubble back instead of leaving a shown frame with no point at all and Blizzard's chrome blanked behind it. Release the claimed bubbles on PLAYER_LOGOUT as well, so the chrome held at alpha 0 goes back with the CVars rather than only through SetActive(false).
Only EllesmereUILocales/_keys.txt conflicted. It is generated by .tools/extract-locale-keys.sh, so it was resolved by regenerating it over the merged tree rather than by hand-merging the two key lists: upstream added three keys and this branch adds one, which the header count on either side cannot express. The result is upstream's list plus "Only works outside of Instances", 785 keys.
What does this PR do?
Adds a Chat Bubbles page to the Chat module that restyles the chat bubbles the game already draws over players' and NPCs' heads. Background colour and opacity, border size and colour, font size and colour, padding, maximum width and a vertical offset are all configurable, and the bubble text follows the Chat page's own font and outline pickers rather than only the global chat font. The page is off by default and asks for confirmation before it is switched on, because enabling it also switches Blizzard's own bubble settings on.
The text colour can either be a single colour of your own or Blizzard's, picked per channel: a cog beside the Font row's colour swatch carries a Follow Blizzard Default Color toggle, off by default. With it on, each bubble takes the colour the game itself gave that line, so a yell reads as a yell, and the swatch greys out to say so.
The Hide Chat Bubbles in Instances option allows users to disable Blizzard’s default chat bubbles while inside instances. When enabled, it disables the three relevant CVars: ChatBubbles, ChatBubblesParty, and ChatBubblesRaid.
After leaving the instance, the CVars are automatically restored to their previous values.
Rather than drawing its own bubbles on nameplates, EllesmereUI rides on Blizzard's. Their bubbles stay switched on, their chrome is hidden, and a styled frame is anchored to the frame that already carries their position. Every bubble therefore stays exactly where the game put it, including the one over the player's own head, and nameplates are neither involved nor required to be visible. Inside instances the engine's bubble frames are forbidden to addons and can be neither read nor hidden, so the feature stays out of the way there and Blizzard's own bubbles keep working as usual; the options page says so in a banner above the settings.
Channels are picked individually. Say, Yell, NPCs and Emotes share Blizzard's main bubble switch, while Party (which also covers instance chat) and Raid each have a switch of their own. Those switches are borrowed, never kept: the main one is held while at least one of its four channels is ticked, Party and Raid only while their own channel is ticked, and the Party and Raid ticks are seeded from the player's live settings the first time the feature is enabled, so no group bubbles turn up in a chat that had none. Every switch is put back the way it was found when a channel is unticked, when the feature is switched off, when entering an instance, and at logout, which is the only hand-back that still works if the addon is later disabled outright. Guild chat is not offered, because Blizzard draws no bubble for it and there is nothing to restyle.
How was it tested?
Tested in game on the live client.
Screenshots
Checklist
OnShow/OnHidehooks on the engine's own frames and there is noOnUpdateanywhere. The single timer is a short bounded fallback that runs only while a chat line is still waiting for its bubble, at most eight ticks, coalesced into one chain no matter how much is being said, and it stops on its own.HookScript/hooksecurefunconly, neverSetScripton Blizzard frames