diff --git a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj
index aaf689a4dc..838e64024c 100644
--- a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj
+++ b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj
@@ -298,6 +298,7 @@
+
diff --git a/OpenRA.Mods.RA/Widgets/Logic/IngameChatLogic.cs b/OpenRA.Mods.RA/Widgets/Logic/IngameChatLogic.cs
index f9ba129837..2e26f35b41 100644
--- a/OpenRA.Mods.RA/Widgets/Logic/IngameChatLogic.cs
+++ b/OpenRA.Mods.RA/Widgets/Logic/IngameChatLogic.cs
@@ -8,7 +8,6 @@
*/
#endregion
-using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
@@ -31,8 +30,7 @@ namespace OpenRA.Mods.RA.Widgets.Logic
readonly List chatTraits;
- readonly List commandNames;
- readonly List playerNames;
+ readonly TabCompletionLogic tabCompletion = new TabCompletionLogic();
bool teamChat;
@@ -47,8 +45,8 @@ namespace OpenRA.Mods.RA.Widgets.Logic
var disableTeamChat = world.LocalPlayer == null || world.LobbyInfo.IsSinglePlayer || !players.Any(p => p.IsAlliedWith(world.LocalPlayer));
teamChat = !disableTeamChat;
- commandNames = chatTraits.OfType().SelectMany(x => x.Commands.Keys).Select(x => "/" + x).ToList();
- playerNames = orderManager.LobbyInfo.Clients.Select(c => c.Name).ToList();
+ tabCompletion.Commands = chatTraits.OfType().SelectMany(x => x.Commands.Keys).ToList();
+ tabCompletion.Names = orderManager.LobbyInfo.Clients.Select(c => c.Name).Distinct().ToList();
var chatPanel = (ContainerWidget)widget;
chatOverlay = chatPanel.Get("CHAT_OVERLAY");
@@ -87,7 +85,12 @@ namespace OpenRA.Mods.RA.Widgets.Logic
CloseChat();
return true;
};
- chatText.OnTabKey = AutoCompleteText;
+ chatText.OnTabKey = () =>
+ {
+ chatText.Text = tabCompletion.Complete(chatText.Text);
+ chatText.CursorPosition = chatText.Text.Length;
+ return true;
+ };
chatText.OnEscKey = () => { CloseChat(); return true; };
@@ -178,43 +181,5 @@ namespace OpenRA.Mods.RA.Widgets.Logic
Sound.PlayNotification(modRules, null, "Sounds", "ChatLine", null);
}
-
- bool AutoCompleteText()
- {
- if (string.IsNullOrEmpty(chatText.Text))
- return false;
-
- if (chatText.Text.LastOrDefault() == ' ')
- return false;
-
- var suggestion = "";
-
- if (chatText.Text.StartsWith("/"))
- {
- suggestion = commandNames.FirstOrDefault(x => x.StartsWith(chatText.Text));
- if (suggestion == null)
- return false;
- }
- else
- {
- var oneWord = !chatText.Text.Contains(' ');
- var toComplete = oneWord
- ? chatText.Text
- : chatText.Text.Substring(chatText.Text.LastIndexOf(' ') + 1);
-
- suggestion = playerNames.FirstOrDefault(x => x.StartsWith(toComplete, StringComparison.InvariantCultureIgnoreCase));
- if (suggestion == null)
- return false;
-
- if (oneWord)
- suggestion += ": ";
- else
- suggestion = chatText.Text.Substring(0, chatText.Text.Length - toComplete.Length) + suggestion;
- }
-
- chatText.Text = suggestion;
- chatText.CursorPosition = chatText.Text.Length;
- return true;
- }
}
}
diff --git a/OpenRA.Mods.RA/Widgets/Logic/LobbyLogic.cs b/OpenRA.Mods.RA/Widgets/Logic/LobbyLogic.cs
index 4d444d5d49..0f54f8c71a 100644
--- a/OpenRA.Mods.RA/Widgets/Logic/LobbyLogic.cs
+++ b/OpenRA.Mods.RA/Widgets/Logic/LobbyLogic.cs
@@ -51,7 +51,7 @@ namespace OpenRA.Mods.RA.Widgets.Logic
readonly ColorPreviewManagerWidget colorPreview;
- List playerNames;
+ readonly TabCompletionLogic tabCompletion = new TabCompletionLogic();
// Listen for connection failures
void ConnectionStateChanged(OrderManager om)
@@ -509,7 +509,12 @@ namespace OpenRA.Mods.RA.Widgets.Logic
chatLabel.Text = teamChat ? "Team:" : "Chat:";
return true;
};
- chatTextField.OnTabKey = AutoCompleteText;
+ chatTextField.OnTabKey = () =>
+ {
+ chatTextField.Text = tabCompletion.Complete(chatTextField.Text);
+ chatTextField.CursorPosition = chatTextField.Text.Length;
+ return true;
+ };
chatPanel = lobby.Get("CHAT_DISPLAY");
chatTemplate = chatPanel.Get("CHAT_TEMPLATE");
@@ -770,7 +775,7 @@ namespace OpenRA.Mods.RA.Widgets.Logic
while (players.Children.Count > idx)
players.RemoveChild(players.Children[idx]);
- playerNames = orderManager.LobbyInfo.Clients.Select(c => c.Name).ToList();
+ tabCompletion.Names = orderManager.LobbyInfo.Clients.Select(c => c.Name).Distinct().ToList();
}
void OnGameStart()
@@ -779,35 +784,6 @@ namespace OpenRA.Mods.RA.Widgets.Logic
onStart();
}
- bool AutoCompleteText()
- {
- var chatText = lobby.Get("CHAT_TEXTFIELD");
- if (chatText == null || string.IsNullOrEmpty(chatText.Text))
- return false;
-
- if (chatText.Text.LastOrDefault() == ' ')
- return false;
-
- var suggestion = "";
- var oneWord = !chatText.Text.Contains(' ');
- var toComplete = oneWord
- ? chatText.Text
- : chatText.Text.Substring(chatText.Text.LastIndexOf(' ') + 1);
-
- suggestion = playerNames.FirstOrDefault(x => x.StartsWith(toComplete, StringComparison.InvariantCultureIgnoreCase));
- if (suggestion == null)
- return false;
-
- if (oneWord)
- suggestion += ": ";
- else
- suggestion = chatText.Text.Substring(0, chatText.Text.Length - toComplete.Length) + suggestion;
-
- chatText.Text = suggestion;
- chatText.CursorPosition = chatText.Text.Length;
- return true;
- }
-
class DropDownOption
{
public string Title;
diff --git a/OpenRA.Mods.RA/Widgets/Logic/TabCompletionLogic.cs b/OpenRA.Mods.RA/Widgets/Logic/TabCompletionLogic.cs
new file mode 100644
index 0000000000..8dfbaf2218
--- /dev/null
+++ b/OpenRA.Mods.RA/Widgets/Logic/TabCompletionLogic.cs
@@ -0,0 +1,77 @@
+#region Copyright & License Information
+/*
+ * Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
+ * This file is part of OpenRA, which is free software. It is made
+ * available to you under the terms of the GNU General Public License
+ * as published by the Free Software Foundation. For more information,
+ * see COPYING.
+ */
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace OpenRA.Mods.RA.Widgets.Logic
+{
+ class TabCompletionLogic
+ {
+ IList candidates = new List();
+ int currentCandidateIndex = 0;
+ string lastCompleted;
+ string prefix;
+ string suffix;
+
+ public IList Commands { get; set; }
+
+ public IList Names { get; set; }
+
+ public string Complete(string text)
+ {
+ if (string.IsNullOrWhiteSpace(text))
+ return text;
+
+ if (lastCompleted == text)
+ {
+ lastCompleted = prefix + candidates[++currentCandidateIndex % candidates.Count] + suffix;
+ return lastCompleted;
+ }
+
+ var toComplete = "";
+ if (text.StartsWith("/") && Commands != null)
+ {
+ prefix = "/";
+ suffix = "";
+ toComplete = text.Substring(1);
+ candidates = Commands.Where(x => x.StartsWith(toComplete, StringComparison.InvariantCultureIgnoreCase)).ToList();
+ }
+ else if (Names != null)
+ {
+ var oneWord = text.Contains(' ');
+ if (oneWord)
+ {
+ prefix = text.Substring(0, text.LastIndexOf(' ') + 1);
+ suffix = "";
+ toComplete = text.Substring(prefix.Length);
+ }
+ else
+ {
+ prefix = "";
+ suffix = ": ";
+ toComplete = text;
+ }
+ candidates = Names.Where(x => x.StartsWith(toComplete, StringComparison.InvariantCultureIgnoreCase)).ToList();
+ }
+ else
+ return text;
+
+ currentCandidateIndex = 0;
+
+ if (candidates.Count == 0)
+ return text;
+
+ lastCompleted = prefix + candidates[currentCandidateIndex] + suffix;
+ return lastCompleted;
+ }
+ }
+}