Split chat lines into pools

- Add a common class for passing around chat lines
- Add wrapper methods for adding chat lines
- Combine repeated chat lines in the display widget
This commit is contained in:
Ivaylo Draganov
2020-02-29 22:31:29 +02:00
committed by Paul Chote
parent 0a02bd524a
commit 6af354ff99
11 changed files with 175 additions and 88 deletions

View File

@@ -194,7 +194,7 @@ namespace OpenRA.Mods.Common.Scripting
return;
var c = color.HasValue ? color.Value : Color.White;
TextNotificationsManager.AddChatLine(prefix, c, text);
TextNotificationsManager.AddChatLine(prefix, text, c);
}
[Desc("Display a system message to the player. If 'prefix' is nil the default system prefix is used.")]

View File

@@ -28,7 +28,8 @@ namespace OpenRA.Mods.Common.Widgets
public readonly int Space = 4;
const int LogLength = 9;
List<ChatLine> recentLines = new List<ChatLine>();
List<TextNotification> recentLines = new List<TextNotification>();
List<int> lineExpirations = new List<int>();
public override Rectangle EventBounds => Rectangle.Empty;
@@ -47,9 +48,9 @@ namespace OpenRA.Mods.Common.Widgets
var inset = 0;
string name = null;
if (!string.IsNullOrEmpty(line.Name))
if (!string.IsNullOrEmpty(line.Prefix))
{
name = line.Name + ":";
name = line.Prefix + ":";
inset = font.Measure(name).X + 5;
}
@@ -76,12 +77,12 @@ namespace OpenRA.Mods.Common.Widgets
if (UseContrast)
font.DrawTextWithContrast(name, namePos,
line.NameColor, BackgroundColorDark, BackgroundColorLight, 1);
line.PrefixColor, BackgroundColorDark, BackgroundColorLight, 1);
else if (UseShadow)
font.DrawTextWithShadow(name, namePos,
line.NameColor, BackgroundColorDark, BackgroundColorLight, 1);
line.PrefixColor, BackgroundColorDark, BackgroundColorLight, 1);
else
font.DrawText(name, namePos, line.NameColor);
font.DrawText(name, namePos, line.PrefixColor);
}
if (UseContrast)
@@ -99,21 +100,34 @@ namespace OpenRA.Mods.Common.Widgets
Game.Renderer.DisableScissor();
}
public void AddLine(string name, Color nameColor, string text, Color textColor)
public void AddLine(TextNotification chatLine)
{
recentLines.Add(new ChatLine(name, nameColor, text, textColor, Game.LocalTick + RemoveTime));
recentLines.Add(chatLine);
lineExpirations.Add(Game.LocalTick + RemoveTime);
if (Notification != null)
Game.Sound.Play(SoundType.UI, Notification);
while (recentLines.Count > LogLength)
recentLines.RemoveAt(0);
RemoveLine();
}
public void RemoveMostRecentLine()
{
if (recentLines.Count == 0)
return;
recentLines.RemoveAt(recentLines.Count - 1);
lineExpirations.RemoveAt(lineExpirations.Count - 1);
}
public void RemoveLine()
{
if (recentLines.Count > 0)
recentLines.RemoveAt(0);
if (recentLines.Count == 0)
return;
recentLines.RemoveAt(0);
lineExpirations.RemoveAt(0);
}
public override void Tick()
@@ -122,25 +136,8 @@ namespace OpenRA.Mods.Common.Widgets
return;
// This takes advantage of the fact that recentLines is ordered by expiration, from sooner to later
while (recentLines.Count > 0 && Game.LocalTick >= recentLines[0].Expiration)
recentLines.RemoveAt(0);
}
}
class ChatLine
{
public readonly Color NameColor;
public readonly Color TextColor;
public readonly string Name, Text;
public readonly int Expiration;
public ChatLine(string name, Color nameColor, string text, Color textColor, int expiration)
{
Name = name;
Text = text;
Expiration = expiration;
NameColor = nameColor;
TextColor = textColor;
while (recentLines.Count > 0 && Game.LocalTick >= lineExpirations[0])
RemoveLine();
}
}
}

View File

@@ -41,6 +41,9 @@ namespace OpenRA.Mods.Common.Widgets.Logic
readonly string chatLineSound = ChromeMetrics.Get<string>("ChatLineSound");
TextNotification lastLine;
int repetitions;
[ObjectCreator.UseCtor]
public IngameChatLogic(Widget widget, OrderManager orderManager, World world, ModData modData, bool isMenuChat, Dictionary<string, MiniYaml> logicArgs)
{
@@ -195,10 +198,10 @@ namespace OpenRA.Mods.Common.Widgets.Logic
chatScrollPanel.RemoveChildren();
chatScrollPanel.ScrollToBottom();
foreach (var chatLine in orderManager.ChatCache)
AddChatLine(chatLine.Name, chatLine.Color, chatLine.Text, chatLine.TextColor, true);
foreach (var chatLine in orderManager.NotificationsCache)
AddChatLine(chatLine, true);
orderManager.AddChatLine += AddChatLineWrapper;
orderManager.AddTextNotification += AddChatLineWrapper;
chatText.IsDisabled = () => world.IsReplay && !Game.Settings.Debug.EnableDebugCommandsInReplays;
@@ -245,38 +248,58 @@ namespace OpenRA.Mods.Common.Widgets.Logic
Ui.ResetTooltips();
}
public void AddChatLineWrapper(string name, Color nameColor, string text, Color textColor)
public void AddChatLineWrapper(TextNotification chatLine)
{
chatOverlayDisplay?.AddLine(name, nameColor, text, textColor);
var chatLineToDisplay = chatLine;
if (chatLine.CanIncrementOnDuplicate() && chatLine.Equals(lastLine))
{
repetitions++;
chatLineToDisplay = new TextNotification(
chatLine.Pool,
chatLine.Prefix,
$"{chatLine.Text} ({repetitions + 1})",
chatLine.PrefixColor,
chatLine.TextColor);
chatScrollPanel.RemoveChild(chatScrollPanel.Children[chatScrollPanel.Children.Count - 1]);
chatOverlayDisplay?.RemoveMostRecentLine();
}
else
repetitions = 0;
lastLine = chatLine;
chatOverlayDisplay?.AddLine(chatLineToDisplay);
// HACK: Force disable the chat notification sound for the in-menu chat dialog
// This works around our inability to disable the sounds for the in-game dialog when it is hidden
AddChatLine(name, nameColor, text, textColor, chatOverlay == null);
AddChatLine(chatLineToDisplay, chatOverlay == null);
}
void AddChatLine(string @from, Color nameColor, string text, Color textColor, bool suppressSound)
void AddChatLine(TextNotification chatLine, bool suppressSound)
{
var template = chatTemplate.Clone();
var nameLabel = template.Get<LabelWidget>("NAME");
var textLabel = template.Get<LabelWidget>("TEXT");
var name = "";
if (!string.IsNullOrEmpty(from))
name = from + ":";
if (!string.IsNullOrEmpty(chatLine.Prefix))
name = chatLine.Prefix + ":";
var font = Game.Renderer.Fonts[nameLabel.Font];
var nameSize = font.Measure(from);
var nameSize = font.Measure(chatLine.Prefix);
nameLabel.GetColor = () => nameColor;
nameLabel.GetColor = () => chatLine.PrefixColor;
nameLabel.GetText = () => name;
nameLabel.Bounds.Width = nameSize.X;
textLabel.GetColor = () => textColor;
textLabel.GetColor = () => chatLine.TextColor;
textLabel.Bounds.X += nameSize.X;
textLabel.Bounds.Width -= nameSize.X;
// Hack around our hacky wordwrap behavior: need to resize the widget to fit the text
text = WidgetUtils.WrapText(text, textLabel.Bounds.Width, font);
var text = WidgetUtils.WrapText(chatLine.Text, textLabel.Bounds.Width, font);
textLabel.GetText = () => text;
var dh = font.Measure(text).Y - textLabel.Bounds.Height;
if (dh > 0)
@@ -299,7 +322,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
{
if (!disposed)
{
orderManager.AddChatLine -= AddChatLineWrapper;
orderManager.AddTextNotification -= AddChatLineWrapper;
disposed = true;
}

View File

@@ -121,7 +121,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
services = modData.Manifest.Get<WebServices>();
orderManager.AddChatLine += AddChatLine;
orderManager.AddTextNotification += AddChatLine;
Game.LobbyInfoChanged += UpdateCurrentMap;
Game.LobbyInfoChanged += UpdatePlayerList;
Game.LobbyInfoChanged += UpdateDiscordStatus;
@@ -466,7 +466,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
if (disposing && !disposed)
{
disposed = true;
orderManager.AddChatLine -= AddChatLine;
orderManager.AddTextNotification -= AddChatLine;
Game.LobbyInfoChanged -= UpdateCurrentMap;
Game.LobbyInfoChanged -= UpdatePlayerList;
Game.LobbyInfoChanged -= UpdateDiscordStatus;
@@ -489,10 +489,10 @@ namespace OpenRA.Mods.Common.Widgets.Logic
panel = PanelType.Players;
}
void AddChatLine(string name, Color nameColor, string text, Color textColor)
void AddChatLine(TextNotification chatLine)
{
var template = (ContainerWidget)chatTemplate.Clone();
LobbyUtils.SetupChatLine(template, DateTime.Now, name, nameColor, text, textColor);
LobbyUtils.SetupChatLine(template, DateTime.Now, chatLine);
var scrolledToBottom = lobbyChatPanel.ScrolledToBottom;
lobbyChatPanel.AddChild(template);

View File

@@ -651,28 +651,28 @@ namespace OpenRA.Mods.Common.Widgets.Logic
HideChildWidget(parent, "STATUS_IMAGE");
}
public static void SetupChatLine(ContainerWidget template, DateTime time, string name, Color nameColor, string text, Color textColor)
public static void SetupChatLine(ContainerWidget template, DateTime time, TextNotification chatLine)
{
var nameLabel = template.Get<LabelWidget>("NAME");
var timeLabel = template.Get<LabelWidget>("TIME");
var textLabel = template.Get<LabelWidget>("TEXT");
var nameText = name + ":";
var nameText = chatLine.Prefix + ":";
var font = Game.Renderer.Fonts[nameLabel.Font];
var nameSize = font.Measure(nameText);
timeLabel.GetText = () => $"{time.Hour:D2}:{time.Minute:D2}";
nameLabel.GetColor = () => nameColor;
nameLabel.GetColor = () => chatLine.PrefixColor;
nameLabel.GetText = () => nameText;
nameLabel.Bounds.Width = nameSize.X;
textLabel.GetColor = () => textColor;
textLabel.GetColor = () => chatLine.TextColor;
textLabel.Bounds.X += nameSize.X;
textLabel.Bounds.Width -= nameSize.X;
// Hack around our hacky wordwrap behavior: need to resize the widget to fit the text
text = WidgetUtils.WrapText(text, textLabel.Bounds.Width, font);
var text = WidgetUtils.WrapText(chatLine.Text, textLabel.Bounds.Width, font);
textLabel.GetText = () => text;
var dh = font.Measure(text).Y - textLabel.Bounds.Height;
if (dh > 0)

View File

@@ -248,12 +248,12 @@ namespace OpenRA.Mods.Common.Widgets
// Check if selecting actors on the screen has selected new units
if (ownUnitsOnScreen.Count > World.Selection.Actors.Count())
TextNotificationsManager.AddSystemLine("Selected across screen");
TextNotificationsManager.AddFeedbackLine("Selected across screen");
else
{
// Select actors in the world that have highest selection priority
ownUnitsOnScreen = SelectActorsInWorld(World, null, eligiblePlayers).SubsetWithHighestSelectionPriority(e.Modifiers).ToList();
TextNotificationsManager.AddSystemLine("Selected across map");
TextNotificationsManager.AddFeedbackLine("Selected across map");
}
World.Selection.Combine(World, ownUnitsOnScreen, false, false);
@@ -280,12 +280,12 @@ namespace OpenRA.Mods.Common.Widgets
// Check if selecting actors on the screen has selected new units
if (newSelection.Count > World.Selection.Actors.Count())
TextNotificationsManager.AddSystemLine("Selected across screen");
TextNotificationsManager.AddFeedbackLine("Selected across screen");
else
{
// Select actors in the world that have the same selection class as one of the already selected actors
newSelection = SelectActorsInWorld(World, selectedClasses, eligiblePlayers).ToList();
TextNotificationsManager.AddSystemLine("Selected across map");
TextNotificationsManager.AddFeedbackLine("Selected across map");
}
World.Selection.Combine(World, newSelection, true, false);