Fancy speech bubbles - Names over speech bubbles, and 1:1 chat parity for emotes and LOOC! (#21999)
This commit is contained in:
@@ -1,8 +1,11 @@
|
||||
using System.Numerics;
|
||||
using Content.Client.Chat.Managers;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Chat;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
@@ -10,6 +13,10 @@ namespace Content.Client.Chat.UI
|
||||
{
|
||||
public abstract class SpeechBubble : Control
|
||||
{
|
||||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] protected readonly IConfigurationManager ConfigManager = default!;
|
||||
|
||||
public enum SpeechType : byte
|
||||
{
|
||||
Emote,
|
||||
@@ -34,10 +41,12 @@ namespace Content.Client.Chat.UI
|
||||
/// </summary>
|
||||
private const float EntityVerticalOffset = 0.5f;
|
||||
|
||||
private readonly IEyeManager _eyeManager;
|
||||
/// <summary>
|
||||
/// The default maximum width for speech bubbles.
|
||||
/// </summary>
|
||||
public const float SpeechMaxWidth = 256;
|
||||
|
||||
private readonly EntityUid _senderEntity;
|
||||
private readonly IChatManager _chatManager;
|
||||
private readonly IEntityManager _entityManager;
|
||||
|
||||
private float _timeLeft = TotalTime;
|
||||
|
||||
@@ -49,38 +58,36 @@ namespace Content.Client.Chat.UI
|
||||
// man down
|
||||
public event Action<EntityUid, SpeechBubble>? OnDied;
|
||||
|
||||
public static SpeechBubble CreateSpeechBubble(SpeechType type, string text, EntityUid senderEntity, IEyeManager eyeManager, IChatManager chatManager, IEntityManager entityManager)
|
||||
public static SpeechBubble CreateSpeechBubble(SpeechType type, ChatMessage message, EntityUid senderEntity)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case SpeechType.Emote:
|
||||
return new TextSpeechBubble(text, senderEntity, eyeManager, chatManager, entityManager, "emoteBox");
|
||||
return new TextSpeechBubble(message, senderEntity, "emoteBox");
|
||||
|
||||
case SpeechType.Say:
|
||||
return new TextSpeechBubble(text, senderEntity, eyeManager, chatManager, entityManager, "sayBox");
|
||||
return new FancyTextSpeechBubble(message, senderEntity, "sayBox");
|
||||
|
||||
case SpeechType.Whisper:
|
||||
return new TextSpeechBubble(text, senderEntity, eyeManager, chatManager, entityManager, "whisperBox");
|
||||
return new FancyTextSpeechBubble(message, senderEntity, "whisperBox");
|
||||
|
||||
case SpeechType.Looc:
|
||||
return new TextSpeechBubble(text, senderEntity, eyeManager, chatManager, entityManager, "emoteBox", Color.FromHex("#48d1cc"));
|
||||
return new TextSpeechBubble(message, senderEntity, "emoteBox", Color.FromHex("#48d1cc"));
|
||||
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
public SpeechBubble(string text, EntityUid senderEntity, IEyeManager eyeManager, IChatManager chatManager, IEntityManager entityManager, string speechStyleClass, Color? fontColor = null)
|
||||
public SpeechBubble(ChatMessage message, EntityUid senderEntity, string speechStyleClass, Color? fontColor = null)
|
||||
{
|
||||
_chatManager = chatManager;
|
||||
IoCManager.InjectDependencies(this);
|
||||
_senderEntity = senderEntity;
|
||||
_eyeManager = eyeManager;
|
||||
_entityManager = entityManager;
|
||||
|
||||
// Use text clipping so new messages don't overlap old ones being pushed up.
|
||||
RectClipContent = true;
|
||||
|
||||
var bubble = BuildBubble(text, speechStyleClass, fontColor);
|
||||
var bubble = BuildBubble(message, speechStyleClass, fontColor);
|
||||
|
||||
AddChild(bubble);
|
||||
|
||||
@@ -91,7 +98,7 @@ namespace Content.Client.Chat.UI
|
||||
_verticalOffsetAchieved = -ContentSize.Y;
|
||||
}
|
||||
|
||||
protected abstract Control BuildBubble(string text, string speechStyleClass, Color? fontColor = null);
|
||||
protected abstract Control BuildBubble(ChatMessage message, string speechStyleClass, Color? fontColor = null);
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
@@ -165,33 +172,47 @@ namespace Content.Client.Chat.UI
|
||||
_timeLeft = FadeTime;
|
||||
}
|
||||
}
|
||||
|
||||
protected FormattedMessage FormatSpeech(string message, Color? fontColor = null)
|
||||
{
|
||||
var msg = new FormattedMessage();
|
||||
if (fontColor != null)
|
||||
msg.PushColor(fontColor.Value);
|
||||
msg.AddMarkup(message);
|
||||
return msg;
|
||||
}
|
||||
|
||||
protected string ExtractSpeechSubstring(ChatMessage message, string tag)
|
||||
{
|
||||
var rawmsg = message.WrappedMessage;
|
||||
var tagStart = rawmsg.IndexOf($"[{tag}]");
|
||||
var tagEnd = rawmsg.IndexOf($"[/{tag}]");
|
||||
tagStart += tag.Length + 2;
|
||||
return rawmsg.Substring(tagStart, tagEnd - tagStart);
|
||||
}
|
||||
|
||||
protected FormattedMessage ExtractAndFormatSpeechSubstring(ChatMessage message, string tag, Color? fontColor = null)
|
||||
{
|
||||
return FormatSpeech(ExtractSpeechSubstring(message, tag), fontColor);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public sealed class TextSpeechBubble : SpeechBubble
|
||||
{
|
||||
public TextSpeechBubble(string text, EntityUid senderEntity, IEyeManager eyeManager, IChatManager chatManager, IEntityManager entityManager, string speechStyleClass, Color? fontColor = null)
|
||||
: base(text, senderEntity, eyeManager, chatManager, entityManager, speechStyleClass, fontColor)
|
||||
public TextSpeechBubble(ChatMessage message, EntityUid senderEntity, string speechStyleClass, Color? fontColor = null)
|
||||
: base(message, senderEntity, speechStyleClass, fontColor)
|
||||
{
|
||||
}
|
||||
|
||||
protected override Control BuildBubble(string text, string speechStyleClass, Color? fontColor = null)
|
||||
protected override Control BuildBubble(ChatMessage message, string speechStyleClass, Color? fontColor = null)
|
||||
{
|
||||
var label = new RichTextLabel
|
||||
{
|
||||
MaxWidth = 256,
|
||||
MaxWidth = SpeechMaxWidth,
|
||||
};
|
||||
|
||||
if (fontColor != null)
|
||||
{
|
||||
var msg = new FormattedMessage();
|
||||
msg.PushColor(fontColor.Value);
|
||||
msg.AddMarkup(text);
|
||||
label.SetMessage(msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
label.SetMessage(text);
|
||||
}
|
||||
label.SetMessage(FormatSpeech(message.WrappedMessage, fontColor));
|
||||
|
||||
var panel = new PanelContainer
|
||||
{
|
||||
@@ -203,4 +224,76 @@ namespace Content.Client.Chat.UI
|
||||
return panel;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class FancyTextSpeechBubble : SpeechBubble
|
||||
{
|
||||
|
||||
public FancyTextSpeechBubble(ChatMessage message, EntityUid senderEntity, string speechStyleClass, Color? fontColor = null)
|
||||
: base(message, senderEntity, speechStyleClass, fontColor)
|
||||
{
|
||||
}
|
||||
|
||||
protected override Control BuildBubble(ChatMessage message, string speechStyleClass, Color? fontColor = null)
|
||||
{
|
||||
if (!ConfigManager.GetCVar(CCVars.ChatEnableFancyBubbles))
|
||||
{
|
||||
var label = new RichTextLabel
|
||||
{
|
||||
MaxWidth = SpeechMaxWidth
|
||||
};
|
||||
|
||||
label.SetMessage(ExtractAndFormatSpeechSubstring(message, "BubbleContent", fontColor));
|
||||
|
||||
var unfanciedPanel = new PanelContainer
|
||||
{
|
||||
StyleClasses = { "speechBox", speechStyleClass },
|
||||
Children = { label },
|
||||
ModulateSelfOverride = Color.White.WithAlpha(0.75f)
|
||||
};
|
||||
return unfanciedPanel;
|
||||
}
|
||||
|
||||
var bubbleHeader = new RichTextLabel
|
||||
{
|
||||
Margin = new Thickness(1, 1, 1, 1)
|
||||
};
|
||||
|
||||
var bubbleContent = new RichTextLabel
|
||||
{
|
||||
MaxWidth = SpeechMaxWidth,
|
||||
Margin = new Thickness(2, 6, 2, 2)
|
||||
};
|
||||
|
||||
//We'll be honest. *Yes* this is hacky. Doing this in a cleaner way would require a bottom-up refactor of how saycode handles sending chat messages. -Myr
|
||||
bubbleHeader.SetMessage(ExtractAndFormatSpeechSubstring(message, "BubbleHeader", fontColor));
|
||||
bubbleContent.SetMessage(ExtractAndFormatSpeechSubstring(message, "BubbleContent", fontColor));
|
||||
|
||||
//As for below: Some day this could probably be converted to xaml. But that is not today. -Myr
|
||||
var mainPanel = new PanelContainer
|
||||
{
|
||||
StyleClasses = { "speechBox", speechStyleClass },
|
||||
Children = { bubbleContent },
|
||||
ModulateSelfOverride = Color.White.WithAlpha(0.75f),
|
||||
HorizontalAlignment = HAlignment.Center,
|
||||
VerticalAlignment = VAlignment.Bottom,
|
||||
Margin = new Thickness(4, 14, 4, 2)
|
||||
};
|
||||
|
||||
var headerPanel = new PanelContainer
|
||||
{
|
||||
StyleClasses = { "speechBox", speechStyleClass },
|
||||
Children = { bubbleHeader },
|
||||
ModulateSelfOverride = Color.White.WithAlpha(ConfigManager.GetCVar(CCVars.ChatFancyNameBackground) ? 0.75f : 0f),
|
||||
HorizontalAlignment = HAlignment.Center,
|
||||
VerticalAlignment = VAlignment.Top
|
||||
};
|
||||
|
||||
var panel = new PanelContainer
|
||||
{
|
||||
Children = { mainPanel, headerPanel }
|
||||
};
|
||||
|
||||
return panel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user