diff --git a/AUTHORS b/AUTHORS index 7c6aa72cf8..01787fff5a 100644 --- a/AUTHORS +++ b/AUTHORS @@ -19,6 +19,7 @@ Previous developers included: Also thanks to: * Adam Valy (Tschokky) * Akseli Virtanen (RAGEQUIT) + * Alexander Fast (mizipzor) * Allen262 * Andrew Aldridge (i80and) * Andrew Perkins diff --git a/OpenRA.Game/Widgets/TextFieldWidget.cs b/OpenRA.Game/Widgets/TextFieldWidget.cs index 9501cf1545..fe26485bd4 100644 --- a/OpenRA.Game/Widgets/TextFieldWidget.cs +++ b/OpenRA.Game/Widgets/TextFieldWidget.cs @@ -30,6 +30,7 @@ namespace OpenRA.Widgets public Func OnEnterKey = () => false; public Func OnTabKey = () => false; public Func OnEscKey = () => false; + public Func OnAltKey = () => false; public Action OnLoseFocus = () => { }; public Action OnTextEdited = () => { }; public int CursorPosition { get; set; } @@ -121,6 +122,9 @@ namespace OpenRA.Widgets if (e.Key == Keycode.ESCAPE && OnEscKey()) return true; + if (e.Key == Keycode.LALT && OnAltKey()) + return true; + if (e.Key == Keycode.LEFT) { if (CursorPosition > 0) diff --git a/OpenRA.Mods.RA/Widgets/Logic/IngameChatLogic.cs b/OpenRA.Mods.RA/Widgets/Logic/IngameChatLogic.cs index 76159a33ca..f9ba129837 100644 --- a/OpenRA.Mods.RA/Widgets/Logic/IngameChatLogic.cs +++ b/OpenRA.Mods.RA/Widgets/Logic/IngameChatLogic.cs @@ -8,6 +8,7 @@ */ #endregion +using System; using System.Collections.Generic; using System.Drawing; using System.Linq; @@ -30,6 +31,9 @@ namespace OpenRA.Mods.RA.Widgets.Logic readonly List chatTraits; + readonly List commandNames; + readonly List playerNames; + bool teamChat; [ObjectCreator.UseCtor] @@ -43,6 +47,9 @@ 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(); + var chatPanel = (ContainerWidget)widget; chatOverlay = chatPanel.Get("CHAT_OVERLAY"); chatOverlayDisplay = chatOverlay.Get("CHAT_DISPLAY"); @@ -57,7 +64,7 @@ namespace OpenRA.Mods.RA.Widgets.Logic chatMode.IsDisabled = () => disableTeamChat; chatText = chatChrome.Get("CHAT_TEXTFIELD"); - chatText.OnTabKey = () => + chatText.OnAltKey = () => { if (!disableTeamChat) teamChat ^= true; @@ -71,18 +78,23 @@ namespace OpenRA.Mods.RA.Widgets.Logic orderManager.IssueOrder(Order.Chat(team, chatText.Text.Trim())); else if (chatTraits != null) - chatTraits.All(x => x.OnChat(orderManager.LocalClient.Name, chatText.Text.Trim())); + { + var text = chatText.Text.Trim(); + foreach (var trait in chatTraits) + trait.OnChat(orderManager.LocalClient.Name, text); + } CloseChat(); return true; }; + chatText.OnTabKey = AutoCompleteText; chatText.OnEscKey = () => { CloseChat(); return true; }; var chatClose = chatChrome.Get("CHAT_CLOSE"); - chatClose.OnClick += () => CloseChat(); + chatClose.OnClick += CloseChat; - chatPanel.OnKeyPress = (e) => + chatPanel.OnKeyPress = e => { if (e.Event == KeyInputEvent.Up) return false; @@ -166,5 +178,43 @@ 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; + } } -} \ No newline at end of file +} diff --git a/OpenRA.Mods.RA/Widgets/Logic/LobbyLogic.cs b/OpenRA.Mods.RA/Widgets/Logic/LobbyLogic.cs index 900c0a7049..4d444d5d49 100644 --- a/OpenRA.Mods.RA/Widgets/Logic/LobbyLogic.cs +++ b/OpenRA.Mods.RA/Widgets/Logic/LobbyLogic.cs @@ -35,18 +35,23 @@ namespace OpenRA.Mods.RA.Widgets.Logic enum PanelType { Players, Options, Kick, ForceStart } PanelType panel = PanelType.Players; - Widget lobby; + readonly Widget lobby; + readonly Widget editablePlayerTemplate; + readonly Widget nonEditablePlayerTemplate; + readonly Widget emptySlotTemplate; + readonly Widget editableSpectatorTemplate; + readonly Widget nonEditableSpectatorTemplate; + readonly Widget newSpectatorTemplate; - Widget editablePlayerTemplate, nonEditablePlayerTemplate, emptySlotTemplate, - editableSpectatorTemplate, nonEditableSpectatorTemplate, newSpectatorTemplate; + readonly ScrollPanelWidget chatPanel; + readonly Widget chatTemplate; - ScrollPanelWidget chatPanel; - Widget chatTemplate; + readonly ScrollPanelWidget players; + readonly Dictionary countryNames; - ScrollPanelWidget players; - Dictionary countryNames; + readonly ColorPreviewManagerWidget colorPreview; - ColorPreviewManagerWidget colorPreview; + List playerNames; // Listen for connection failures void ConnectionStateChanged(OrderManager om) @@ -498,13 +503,13 @@ namespace OpenRA.Mods.RA.Widgets.Logic chatTextField.Text = ""; return true; }; - - chatTextField.OnTabKey = () => + chatTextField.OnAltKey = () => { teamChat ^= true; chatLabel.Text = teamChat ? "Team:" : "Chat:"; return true; }; + chatTextField.OnTabKey = AutoCompleteText; chatPanel = lobby.Get("CHAT_DISPLAY"); chatTemplate = chatPanel.Get("CHAT_TEMPLATE"); @@ -764,6 +769,8 @@ 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(); } void OnGameStart() @@ -772,6 +779,35 @@ 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;