From eecbb9d29317c6cf69f2072c7247253a41cb40a1 Mon Sep 17 00:00:00 2001 From: gwigz Date: Sat, 13 Jun 2026 23:27:36 +0100 Subject: [PATCH 1/2] Add IRC-style chat colors Optionally give every other local-chat speaker a stable, deterministic color (HSL from a hash of their UUID) and dim their displayed name relative to its text color, IRC-style. Ported from the slv fork, dropping the bundled LEAP/viewer-api theme endpoints. Adapted for Alchemy: the logic lives on ALAvatarGroups (the central avatar color controller) as getIRCChatColor/getIRCNameColor, and only overrides the "other agent" case so self, system, object and owner-say messages keep the colors already configured in Preferences > Colors > Chat. Muted speakers stay greyed. Exposed there as an enable toggle plus saturation / lightness / name-dimming sliders. Co-Authored-By: Claude Opus 4.8 (1M context) --- indra/newview/alavatargroups.cpp | 97 +++++++++++++++++++ indra/newview/alavatargroups.h | 14 +++ .../newview/app_settings/settings_alchemy.xml | 55 +++++++++++ indra/newview/llchathistory.cpp | 15 +++ indra/newview/llviewerchat.cpp | 3 + indra/newview/llvoavatar.cpp | 5 +- .../xui/en/panel_preferences_colors.xml | 83 ++++++++++++++++ 7 files changed, 271 insertions(+), 1 deletion(-) diff --git a/indra/newview/alavatargroups.cpp b/indra/newview/alavatargroups.cpp index b376427ac2a..f90f123ce40 100644 --- a/indra/newview/alavatargroups.cpp +++ b/indra/newview/alavatargroups.cpp @@ -26,6 +26,7 @@ // lib includes #include "llavatarname.h" #include "llavatarnamecache.h" +#include "llchat.h" #include "lluicolor.h" #include "lluicolortable.h" #include "lluuid.h" @@ -34,6 +35,7 @@ // viewer includes #include "llagent.h" #include "llcallingcard.h" +#include "llinstantmessage.h" // SYSTEM_FROM #include "llmutelist.h" #include "llviewercontrol.h" #include "rlvactions.h" @@ -294,3 +296,98 @@ std::string ALAvatarGroups::getAvatarColorName(const LLUUID& id, std::string_vie return out_color_name; } + +bool ALAvatarGroups::getIRCChatColor(const LLChat& chat, LLUIColor& color) +{ + static LLCachedControl enabled(gSavedSettings, "AlchemyChatIRCColorsEnabled", false); + if (!enabled) + { + return false; + } + + // Only override "other" speakers; self, system, objects and owner-say keep + // their user-configured chat colors from the existing switch. + if (chat.mSourceType == CHAT_SOURCE_AGENT + && chat.mFromID.notNull() + && chat.mFromID != gAgentID + && SYSTEM_FROM != chat.mFromName + // A stable per-avatar color would defeat @shownames, so skip it while + // restricted from seeing this speaker's name. + && RlvActions::canShowName(RlvActions::SNC_DEFAULT, chat.mFromID)) + { + color = deterministicAgentColor(chat.mFromID); + return true; + } + + return false; +} + +bool ALAvatarGroups::getIRCNameColor(const LLChat& chat, const LLUIColor& chat_color, LLUIColor& color) +{ + static LLCachedControl enabled(gSavedSettings, "AlchemyChatIRCColorsEnabled", false); + if (!enabled) + { + return false; + } + + // Keep the default name styling while restricted from seeing this speaker's + // name, matching getIRCChatColor. + if (!RlvActions::canShowName(RlvActions::SNC_DEFAULT, chat.mFromID)) + { + return false; + } + + color = dimNameColor(chat_color.get()); + return true; +} + +bool ALAvatarGroups::getIRCNameTagColor(const LLUUID& id, LLColor4& color) +{ + static LLCachedControl enabled(gSavedSettings, "AlchemyChatIRCColorsEnabled", false); + static LLCachedControl name_tags(gSavedSettings, "AlchemyChatIRCColorNameTags", false); + if (!enabled || !name_tags) + { + return false; + } + + // Mirror the chat-color override: only other agents get a per-avatar color, + // self keeps the user-configured name tag colors. Skip while restricted from + // seeing this avatar's name so the color can't defeat @shownames. The name + // dimming applied to chat names is applied here too so tags match. + if (id.notNull() && id != gAgentID + && RlvActions::canShowName(RlvActions::SNC_DEFAULT, id)) + { + color = dimNameColor(deterministicAgentColor(id)); + return true; + } + + return false; +} + +LLColor4 ALAvatarGroups::deterministicAgentColor(const LLUUID& id) +{ + static LLCachedControl saturation(gSavedSettings, "AlchemyChatIRCAgentSaturation", 0.7f); + static LLCachedControl lightness(gSavedSettings, "AlchemyChatIRCAgentLightness", 0.9f); + + LLColor4 color; + color.setHSL(static_cast(id.getCRC32() % 360) / 360.f, saturation(), lightness()); + color.mV[VALPHA] = 1.f; + + return color; +} + +LLColor4 ALAvatarGroups::dimNameColor(const LLColor4& color) +{ + static LLCachedControl scale(gSavedSettings, "AlchemyChatIRCNameLightnessScale", 0.8f); + + F32 hue = 0.f; + F32 saturation = 0.f; + F32 lightness = 0.f; + color.calcHSL(&hue, &saturation, &lightness); + + LLColor4 result; + result.setHSL(hue, saturation, lightness * scale()); + result.mV[VALPHA] = 1.f; + + return result; +} diff --git a/indra/newview/alavatargroups.h b/indra/newview/alavatargroups.h index 625d703b1e0..f1b54b871ac 100644 --- a/indra/newview/alavatargroups.h +++ b/indra/newview/alavatargroups.h @@ -24,7 +24,9 @@ #include #include +class LLChat; class LLColor4; +class LLUIColor; class LLUUID; class ALAvatarGroups final : public LLSingleton < ALAvatarGroups > @@ -54,4 +56,16 @@ class ALAvatarGroups final : public LLSingleton < ALAvatarGroups > LLColor4 getAvatarColor(const LLUUID& id, LLColor4 default_color, EColorType color_type); std::string getAvatarColorName(const LLUUID& id, std::string_view color_name, EColorType color_type); + + // IRC-style chat coloring: when enabled, gives every other speaker a stable + // deterministic color and dims the displayed name relative to its text color. + // Other message categories (self, system, objects, owner-say) keep their + // user-configured chat colors. + bool getIRCChatColor(const LLChat& chat, LLUIColor& color); + bool getIRCNameColor(const LLChat& chat, const LLUIColor& chat_color, LLUIColor& color); + bool getIRCNameTagColor(const LLUUID& id, LLColor4& color); + +private: + LLColor4 deterministicAgentColor(const LLUUID& id); + LLColor4 dimNameColor(const LLColor4& color); }; diff --git a/indra/newview/app_settings/settings_alchemy.xml b/indra/newview/app_settings/settings_alchemy.xml index b323c0f9012..a650046e898 100644 --- a/indra/newview/app_settings/settings_alchemy.xml +++ b/indra/newview/app_settings/settings_alchemy.xml @@ -387,6 +387,61 @@ Value /tp2cam + AlchemyChatIRCColorsEnabled + + Comment + Use IRC-style deterministic colors for local chat names and text. + Persist + 1 + Type + Boolean + Value + 0 + + AlchemyChatIRCColorNameTags + + Comment + Overwrite each other agent's name tag with their IRC-style per-agent chat color. + Persist + 1 + Type + Boolean + Value + 0 + + AlchemyChatIRCAgentSaturation + + Comment + Saturation of the per-agent IRC-style chat colors (0-1). + Persist + 1 + Type + F32 + Value + 0.7 + + AlchemyChatIRCAgentLightness + + Comment + Lightness of the per-agent IRC-style chat colors (0-1). + Persist + 1 + Type + F32 + Value + 0.9 + + AlchemyChatIRCNameLightnessScale + + Comment + Scales the lightness of the displayed name relative to its IRC-style chat color (0-1). + Persist + 1 + Type + F32 + Value + 0.8 + AlchemyChatMarkUnnamedObjects Comment diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp index 39a31fb630f..c9e733f8db2 100644 --- a/indra/newview/llchathistory.cpp +++ b/indra/newview/llchathistory.cpp @@ -69,6 +69,7 @@ #include "llviewercontrol.h" #include "llviewermenu.h" #include "llviewerobjectlist.h" +#include "alavatargroups.h" // [SL:KB] - Patch: Chat-Alerts | Checked: 2012-07-10 (Catznip-3.3) #include "llaudioengine.h" #include "lltextparser.h" @@ -1338,6 +1339,7 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL LLUIColor txt_color = LLUIColorTable::instance().getColor("White"); LLUIColor name_color = LLUIColorTable::instance().getColor("ChatHeaderDisplayNameColor"); // LLViewerChat::getChatColor(chat, txt_color, alpha); + const bool irc_name_color = ALAvatarGroups::instance().getIRCNameColor(chat, txt_color, name_color); LLFontGL* fontp = LLViewerChat::getChatFont(); std::string font_name = LLFontGL::nameFromFont(fontp); @@ -1463,6 +1465,11 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL static LLUIColor link_color = LLUIColorTable::instance().getColor("HTMLLinkColor"); link_params.color = link_color; link_params.readonly_color = link_color; + if (irc_name_color) + { + link_params.color = name_color; + link_params.readonly_color = name_color; + } link_params.is_link = true; link_params.link_href = url; @@ -1500,6 +1507,14 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL { LLStyle::Params link_params(body_message_params); link_params.overwriteFrom(LLStyleMap::instance().lookupAgent(chat.mFromID)); + if (irc_name_color) + { + // Keep our dimmed name color instead of letting the agent + // SLURL re-parse to the default HTMLLinkColor link style. + link_params.use_default_link_style = false; + link_params.color = name_color; + link_params.readonly_color = name_color; + } if (use_irssi_text_chat_history) { diff --git a/indra/newview/llviewerchat.cpp b/indra/newview/llviewerchat.cpp index 5c0e0358ab5..b3942f29605 100644 --- a/indra/newview/llviewerchat.cpp +++ b/indra/newview/llviewerchat.cpp @@ -51,6 +51,8 @@ void LLViewerChat::getChatColor(const LLChat& chat, LLUIColor& r_color, F32& r_c } else { + if (!ALAvatarGroups::instance().getIRCChatColor(chat, r_color)) + { switch(chat.mSourceType) { case CHAT_SOURCE_SYSTEM: @@ -96,6 +98,7 @@ void LLViewerChat::getChatColor(const LLChat& chat, LLUIColor& r_color, F32& r_c default: r_color = LLUIColorTable::instance().getColor("White"); } + } if (!chat.mPosAgent.isExactlyZero()) { diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 7334477a8c2..49f790b46e8 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -3999,7 +3999,10 @@ LLColor4 LLVOAvatar::getNameTagColor(bool is_friend) #endif static LLUIColor name_tag_match = LLUIColorTable::instance().getColor("NameTagMatch"); LLColor4 color_name = name_tag_match; - color_name = ALAvatarGroups::instance().getAvatarColor(getID(), color_name, ALAvatarGroups::COLOR_NAMETAG); + if (!ALAvatarGroups::instance().getIRCNameTagColor(getID(), color_name)) + { + color_name = ALAvatarGroups::instance().getAvatarColor(getID(), color_name, ALAvatarGroups::COLOR_NAMETAG); + } return color_name; } diff --git a/indra/newview/skins/default/xui/en/panel_preferences_colors.xml b/indra/newview/skins/default/xui/en/panel_preferences_colors.xml index b3f40a9cf6d..792ceb19c72 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_colors.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_colors.xml @@ -814,6 +814,89 @@ width="95"> Lindens + + IRC-style chat colors: + + + + + + From 84e3cb41bb1b50e51ea8a71f335a3dac84c8dc24 Mon Sep 17 00:00:00 2001 From: gwigz Date: Sun, 14 Jun 2026 15:57:55 +0100 Subject: [PATCH 2/2] Split IRC-name lightness, and add chat preview --- indra/newview/alavatargroups.cpp | 25 +-- indra/newview/alavatargroups.h | 4 +- .../newview/app_settings/settings_alchemy.xml | 6 +- indra/newview/llchathistory.cpp | 2 +- indra/newview/llfloaterpreference.cpp | 163 ++++++++++++++++++ indra/newview/llfloaterpreference.h | 22 +++ .../default/xui/en/floater_preferences.xml | 2 +- .../xui/en/panel_preferences_colors.xml | 62 +++++-- 8 files changed, 251 insertions(+), 35 deletions(-) diff --git a/indra/newview/alavatargroups.cpp b/indra/newview/alavatargroups.cpp index f90f123ce40..ab7bb5bc55d 100644 --- a/indra/newview/alavatargroups.cpp +++ b/indra/newview/alavatargroups.cpp @@ -322,7 +322,7 @@ bool ALAvatarGroups::getIRCChatColor(const LLChat& chat, LLUIColor& color) return false; } -bool ALAvatarGroups::getIRCNameColor(const LLChat& chat, const LLUIColor& chat_color, LLUIColor& color) +bool ALAvatarGroups::getIRCNameColor(const LLChat& chat, LLUIColor& color) { static LLCachedControl enabled(gSavedSettings, "AlchemyChatIRCColorsEnabled", false); if (!enabled) @@ -337,7 +337,7 @@ bool ALAvatarGroups::getIRCNameColor(const LLChat& chat, const LLUIColor& chat_c return false; } - color = dimNameColor(chat_color.get()); + color = nameColor(chat.mFromID); return true; } @@ -353,11 +353,11 @@ bool ALAvatarGroups::getIRCNameTagColor(const LLUUID& id, LLColor4& color) // Mirror the chat-color override: only other agents get a per-avatar color, // self keeps the user-configured name tag colors. Skip while restricted from // seeing this avatar's name so the color can't defeat @shownames. The name - // dimming applied to chat names is applied here too so tags match. + // lightness applied to chat names is applied here too so tags match. if (id.notNull() && id != gAgentID && RlvActions::canShowName(RlvActions::SNC_DEFAULT, id)) { - color = dimNameColor(deterministicAgentColor(id)); + color = nameColor(id); return true; } @@ -376,17 +376,18 @@ LLColor4 ALAvatarGroups::deterministicAgentColor(const LLUUID& id) return color; } -LLColor4 ALAvatarGroups::dimNameColor(const LLColor4& color) +LLColor4 ALAvatarGroups::nameColor(const LLUUID& id) { - static LLCachedControl scale(gSavedSettings, "AlchemyChatIRCNameLightnessScale", 0.8f); - - F32 hue = 0.f; - F32 saturation = 0.f; - F32 lightness = 0.f; - color.calcHSL(&hue, &saturation, &lightness); + static LLCachedControl saturation(gSavedSettings, "AlchemyChatIRCAgentSaturation", 0.7f); + static LLCachedControl name_lightness(gSavedSettings, "AlchemyChatIRCNameLightness", 0.7f); + // Same hue and saturation as the per-agent chat color, but with the name's + // own independent lightness. Built from the same inputs as the chat color + // rather than derived from it, so the chat Lightness slider doesn't bleed + // into the name (a very light/dark chat color loses saturation, and hue at + // the extremes, when round-tripped through RGB). LLColor4 result; - result.setHSL(hue, saturation, lightness * scale()); + result.setHSL(static_cast(id.getCRC32() % 360) / 360.f, saturation(), name_lightness()); result.mV[VALPHA] = 1.f; return result; diff --git a/indra/newview/alavatargroups.h b/indra/newview/alavatargroups.h index f1b54b871ac..1ea38945765 100644 --- a/indra/newview/alavatargroups.h +++ b/indra/newview/alavatargroups.h @@ -62,10 +62,10 @@ class ALAvatarGroups final : public LLSingleton < ALAvatarGroups > // Other message categories (self, system, objects, owner-say) keep their // user-configured chat colors. bool getIRCChatColor(const LLChat& chat, LLUIColor& color); - bool getIRCNameColor(const LLChat& chat, const LLUIColor& chat_color, LLUIColor& color); + bool getIRCNameColor(const LLChat& chat, LLUIColor& color); bool getIRCNameTagColor(const LLUUID& id, LLColor4& color); private: LLColor4 deterministicAgentColor(const LLUUID& id); - LLColor4 dimNameColor(const LLColor4& color); + LLColor4 nameColor(const LLUUID& id); }; diff --git a/indra/newview/app_settings/settings_alchemy.xml b/indra/newview/app_settings/settings_alchemy.xml index a650046e898..0cf4980b668 100644 --- a/indra/newview/app_settings/settings_alchemy.xml +++ b/indra/newview/app_settings/settings_alchemy.xml @@ -431,16 +431,16 @@ Value 0.9 - AlchemyChatIRCNameLightnessScale + AlchemyChatIRCNameLightness Comment - Scales the lightness of the displayed name relative to its IRC-style chat color (0-1). + Lightness of the displayed name in IRC-style chat colors; shares the chat color's hue and saturation (0-1). Persist 1 Type F32 Value - 0.8 + 0.7 AlchemyChatMarkUnnamedObjects diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp index c9e733f8db2..1c567b58e81 100644 --- a/indra/newview/llchathistory.cpp +++ b/indra/newview/llchathistory.cpp @@ -1339,7 +1339,7 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL LLUIColor txt_color = LLUIColorTable::instance().getColor("White"); LLUIColor name_color = LLUIColorTable::instance().getColor("ChatHeaderDisplayNameColor"); // LLViewerChat::getChatColor(chat, txt_color, alpha); - const bool irc_name_color = ALAvatarGroups::instance().getIRCNameColor(chat, txt_color, name_color); + const bool irc_name_color = ALAvatarGroups::instance().getIRCNameColor(chat, name_color); LLFontGL* fontp = LLViewerChat::getChatFont(); std::string font_name = LLFontGL::nameFromFont(fontp); diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index 3b286af56b1..7b21bb68c6e 100644 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -34,7 +34,12 @@ #include "llfloaterpreference.h" +#include "alavatargroups.h" #include "llaudioengine.h" +#include "llchat.h" +#include "llstyle.h" +#include "lluicolortable.h" +#include "llviewerchat.h" #include "message.h" #include "llfloaterautoreplacesettings.h" #include "llagent.h" @@ -74,6 +79,8 @@ #include "llscrolllistitem.h" #include "llsliderctrl.h" #include "lltabcontainer.h" +#include "lltextbox.h" +#include "lltexteditor.h" #include "lltrans.h" #include "lluri.h" #include "llviewercontrol.h" @@ -3066,6 +3073,162 @@ class LLPanelPreferencePrivacy : public LLPanelPreference static LLPanelInjector t_pref_graph("panel_preference_graphics"); static LLPanelInjector t_pref_privacy("panel_preference_privacy"); static LLPanelInjector t_pref_sound("panel_preference_sound"); +static LLPanelInjector t_pref_colors("panel_preference_colors"); + +bool LLPanelPreferenceColors::postBuild() +{ + mPreviewEditor = getChild("irc_chat_preview"); + updatePreview(); + return LLPanelPreference::postBuild(); +} + +void LLPanelPreferenceColors::draw() +{ + // The chat-color swatches commit straight into LLUIColorTable (no settings + // signal to listen to), so poll a cheap signature of everything the preview + // depends on and re-render only when it actually changes. + static const char* const color_names[] = { + "UserChatColor", "AgentChatColor", "FriendChatColor", "SystemChatColor", + "ObjectChatColor", "llOwnerSayChatColor", "LindenChatColor", "HTMLLinkColor", + }; + std::string signature; + for (const char* name : color_names) + { + const LLColor4& c = LLUIColorTable::instance().getColor(name).get(); + signature += llformat("%.3f,%.3f,%.3f;", c.mV[VRED], c.mV[VGREEN], c.mV[VBLUE]); + } + signature += llformat("%d;%.3f;%.3f;%.3f;%d", + gSavedSettings.getBOOL("AlchemyChatIRCColorsEnabled") ? 1 : 0, + gSavedSettings.getF32("AlchemyChatIRCAgentSaturation"), + gSavedSettings.getF32("AlchemyChatIRCAgentLightness"), + gSavedSettings.getF32("AlchemyChatIRCNameLightness"), + gSavedSettings.getBOOL("Use24HourClock") ? 1 : 0); + + if (signature != mPreviewSignature) + { + mPreviewSignature = signature; + updatePreview(); + } + + LLPanelPreference::draw(); +} + +void LLPanelPreferenceColors::updatePreview() +{ + if (!mPreviewEditor) + return; + + // One line per chat category the panel above exposes. Each carries the base + // color the real getChatColor() switch would pick (friend/Linden can't be + // detected from a fake id, so we set it explicitly), plus a stable id so the + // per-avatar IRC color stays constant between refreshes. The IRC override and + // name dimming are then applied through the real ALAvatarGroups calls, so the + // preview tracks the live colors and settings exactly like in-world local chat. + enum EKind { K_SYSTEM, K_SELF, K_RESIDENT, K_FRIEND, K_LINDEN, K_OBJECT, K_OWNER }; + static const struct + { + EKind kind; + const char* color; // base text color name + const char* id; // stable per-avatar id (empty for system/self) + S32 hour; // mock 24h timestamp, formatted per Use24HourClock + S32 minute; + const char* name; // speaker name ("" hides the name prefix) + const char* text; + } samples[] = { + { K_SYSTEM, "SystemChatColor", "", 13, 42, "", "Welcome to Quirk Island. Mind the dancing llamas." }, + { K_SELF, "UserChatColor", "", 13, 43, "You", "ok who moved my virtual cheese" }, + { K_RESIDENT, "AgentChatColor", "a1a1a1a1-0000-0000-0000-000000000001", 13, 44, "Bartholomew Bumblecrash", "anyone else seeing the sky do the wobble thing?" }, + { K_RESIDENT, "AgentChatColor", "f6f6f6f6-0000-0000-0000-000000000006", 13, 45, "Wandering Pixel", "https://marketplace.secondlife.com/" }, + { K_FRIEND, "FriendChatColor", "b2b2b2b2-0000-0000-0000-000000000002", 13, 46, "Glittertoes McSparkle", "omg hi!! brb taming a baby dragon" }, + { K_LINDEN, "LindenChatColor", "c3c3c3c3-0000-0000-0000-000000000003", 13, 47, "Governor Linden", "Rolling restart in 5, hold onto your hats." }, + { K_OBJECT, "ObjectChatColor", "d4d4d4d4-0000-0000-0000-000000000004", 13, 48, "Object", "Welcome to Help Island!" }, + { K_OWNER, "llOwnerSayChatColor", "e5e5e5e5-0000-0000-0000-000000000005", 13, 49, "Animation HUD", "Memory Free: 2167 KiB :)" }, + }; + + static const LLUIColor time_color = LLUIColorTable::instance().getColor("ChatHeaderTimestampColor"); + const bool use_24h = gSavedSettings.getBOOL("Use24HourClock"); + + mPreviewEditor->clear(); // clear before re-appending + + bool prepend_newline = false; // newline before every line except the first + for (const auto& s : samples) + { + LLChat chat; + chat.mFromName = s.name; + chat.mText = s.text; + switch (s.kind) + { + case K_SYSTEM: + chat.mSourceType = CHAT_SOURCE_SYSTEM; + break; + case K_SELF: + chat.mSourceType = CHAT_SOURCE_AGENT; + chat.mFromID = gAgentID; + break; + case K_OBJECT: + chat.mSourceType = CHAT_SOURCE_OBJECT; + chat.mFromID = LLUUID(s.id); + break; + case K_OWNER: + chat.mSourceType = CHAT_SOURCE_OBJECT; + chat.mChatType = CHAT_TYPE_OWNER; + chat.mFromID = LLUUID(s.id); + break; + default: // resident / friend / Linden are all "other agents" + chat.mSourceType = CHAT_SOURCE_AGENT; + chat.mFromID = LLUUID(s.id); + break; + } + + // Base category color, then the live IRC overrides (mirrors getChatColor + + // getIRCNameColor in llchathistory.cpp). getIRCChatColor only recolors other + // agents, so self/system/object/owner keep their configured color. Names are + // clickable SLURLs in chat, so by default they take the link color; when IRC + // coloring is on, getIRCNameColor overrides that with the per-agent name color. + LLUIColor txt_color = LLUIColorTable::instance().getColor(s.color); + ALAvatarGroups::instance().getIRCChatColor(chat, txt_color); + + LLUIColor name_color = LLUIColorTable::instance().getColor("HTMLLinkColor"); + ALAvatarGroups::instance().getIRCNameColor(chat, name_color); + + // Timestamp prefix, like nearby chat: "[hh:mm] ", honoring the + // Use24HourClock pref the way LLFloaterIMSessionTab::appendTime() does. + std::string time_str; + if (use_24h) + { + time_str = llformat("%d:%02d", s.hour, s.minute); + } + else + { + S32 h12 = s.hour % 12; + if (h12 == 0) h12 = 12; + time_str = llformat("%d:%02d %s", h12, s.minute, s.hour < 12 ? "AM" : "PM"); + } + + LLStyle::Params time_style; + time_style.color(time_color); + time_style.readonly_color(time_color); + mPreviewEditor->appendText("[" + time_str + "] ", prepend_newline, time_style); + prepend_newline = false; // keep the rest of the line attached to the timestamp + + if (!chat.mFromName.empty()) + { + LLStyle::Params name_style; + name_style.color(name_color); + name_style.readonly_color(name_color); + mPreviewEditor->appendText(chat.mFromName + ": ", prepend_newline, name_style); + } + + // parse_urls is set on the widget, so any URL in the text is linkified + // (and colored HTMLLinkColor) automatically by appendText. + LLStyle::Params text_style; + text_style.color(txt_color); + text_style.readonly_color(txt_color); + mPreviewEditor->appendText(chat.mText, prepend_newline, text_style); + + prepend_newline = true; + } +} bool LLPanelPreferenceSound::postBuild() { diff --git a/indra/newview/llfloaterpreference.h b/indra/newview/llfloaterpreference.h index 310bd4bb6ef..567c2adc4cc 100644 --- a/indra/newview/llfloaterpreference.h +++ b/indra/newview/llfloaterpreference.h @@ -53,6 +53,7 @@ class LLScrollListCell; class LLSliderCtrl; class LLSD; class LLTextBox; +class LLTextEditor; struct skin_t; namespace ll @@ -388,6 +389,27 @@ class LLPanelPreferenceSound : public LLPanelPreference boost::signals2::scoped_connection mDevicesChangedConn; }; +// Colors > Chat preferences panel. Renders a live mock-chat preview that +// re-colors whenever a chat-color swatch or any AlchemyChatIRC* setting +// changes, using the real chat-color path so it matches in-world local chat. +class LLPanelPreferenceColors : public LLPanelPreference +{ + LOG_CLASS(LLPanelPreferenceColors); +public: + bool postBuild() override; + void draw() override; + +private: + void updatePreview(); + + LLTextEditor* mPreviewEditor = nullptr; + + // Signature of the colors/settings the preview depends on. The chat-color + // swatches commit straight into LLUIColorTable (no settings signal), so + // draw() polls this and re-renders only when it actually changes. + std::string mPreviewSignature; +}; + class LLPanelPreferenceControls : public LLPanelPreference, public LLKeyBindResponderInterface { LOG_CLASS(LLPanelPreferenceControls); diff --git a/indra/newview/skins/default/xui/en/floater_preferences.xml b/indra/newview/skins/default/xui/en/floater_preferences.xml index e012770cd2c..9fa6f983a7a 100644 --- a/indra/newview/skins/default/xui/en/floater_preferences.xml +++ b/indra/newview/skins/default/xui/en/floater_preferences.xml @@ -144,7 +144,7 @@ help_topic="preferences_msgs_tab" name="msgs" /> - + + Compact Chat Preview: + +