Add command history to single-player and multiplayer
The last 50 valid commands starting with / are saved to memory. Regular chat messages are not saved to prevent spam. The command history persists across single-player and multiplayer games. Press arrow up/down to show the previously typed commands within the current OpenRA session.
This commit is contained in:
committed by
Gustas Kažukauskas
parent
096ad0c413
commit
4a04f8df83
89
OpenRA.Mods.Common/Widgets/Logic/CommandHistory.cs
Normal file
89
OpenRA.Mods.Common/Widgets/Logic/CommandHistory.cs
Normal file
@@ -0,0 +1,89 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright (c) The OpenRA Developers and Contributors
|
||||
* 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, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
{
|
||||
public sealed class CommandHistory
|
||||
{
|
||||
const int MaxHistorySize = 50;
|
||||
static CommandHistory instance;
|
||||
static readonly object LockObject = new();
|
||||
readonly List<string> history = [];
|
||||
int currentIndex = -1;
|
||||
|
||||
CommandHistory() { }
|
||||
|
||||
public static CommandHistory Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (instance == null)
|
||||
{
|
||||
lock (LockObject)
|
||||
{
|
||||
instance ??= new CommandHistory();
|
||||
}
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
public void AddCommand(string command)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(command) || !command.StartsWith('/'))
|
||||
return;
|
||||
|
||||
var trimmedCommand = command.Trim();
|
||||
history.RemoveAll(c => c == trimmedCommand);
|
||||
history.Insert(0, trimmedCommand);
|
||||
if (history.Count > MaxHistorySize)
|
||||
history.RemoveAt(history.Count - 1);
|
||||
|
||||
currentIndex = -1;
|
||||
}
|
||||
|
||||
public string GetPrevious()
|
||||
{
|
||||
if (history.Count == 0)
|
||||
return null;
|
||||
|
||||
if (currentIndex < history.Count - 1)
|
||||
currentIndex++;
|
||||
|
||||
return history[currentIndex];
|
||||
}
|
||||
|
||||
public string GetNext()
|
||||
{
|
||||
if (history.Count == 0)
|
||||
return null;
|
||||
|
||||
if (currentIndex > 0)
|
||||
{
|
||||
currentIndex--;
|
||||
return history[currentIndex];
|
||||
}
|
||||
|
||||
if (currentIndex == 0)
|
||||
currentIndex = -1;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
currentIndex = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -49,6 +49,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
readonly Dictionary<TextNotificationPool, Widget> templates = [];
|
||||
|
||||
readonly TabCompletionLogic tabCompletion = new();
|
||||
readonly CommandHistory commandHistory = CommandHistory.Instance;
|
||||
|
||||
readonly string chatLineSound = ChromeMetrics.Get<string>("ChatLineSound");
|
||||
|
||||
@@ -159,11 +160,18 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
{
|
||||
var text = chatText.Text.Trim();
|
||||
var from = world.IsReplay ? null : orderManager.LocalClient.Name;
|
||||
var commandName = text[1..].Split(' ')[0].ToLowerInvariant();
|
||||
var isValidCommand = !string.IsNullOrEmpty(commandName) &&
|
||||
chatTraits.OfType<ChatCommands>().Any(trait => trait.Commands.ContainsKey(commandName));
|
||||
foreach (var trait in chatTraits)
|
||||
trait.OnChat(from, text);
|
||||
|
||||
if (isValidCommand)
|
||||
commandHistory.AddCommand(text);
|
||||
}
|
||||
}
|
||||
|
||||
commandHistory.Reset();
|
||||
chatText.Text = "";
|
||||
if (!isMenuChat)
|
||||
CloseChat();
|
||||
@@ -191,9 +199,40 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
else
|
||||
chatText.YieldKeyboardFocus();
|
||||
|
||||
commandHistory.Reset();
|
||||
return true;
|
||||
};
|
||||
|
||||
chatText.OnArrowUp = _ =>
|
||||
{
|
||||
var previousCommand = commandHistory.GetPrevious();
|
||||
if (previousCommand != null)
|
||||
{
|
||||
chatText.Text = previousCommand;
|
||||
chatText.CursorPosition = chatText.Text.Length;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
chatText.OnArrowDown = _ =>
|
||||
{
|
||||
var nextCommand = commandHistory.GetNext();
|
||||
if (nextCommand != null)
|
||||
{
|
||||
chatText.Text = nextCommand;
|
||||
chatText.CursorPosition = chatText.Text.Length;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
chatText.Text = "";
|
||||
commandHistory.Reset();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
chatAvailableIn = new CachedTransform<int, string>(x => FluentProvider.GetMessage(ChatAvailability, "seconds", x));
|
||||
|
||||
if (!isMenuChat)
|
||||
@@ -265,6 +304,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
public void OpenChat()
|
||||
{
|
||||
chatText.Text = "";
|
||||
commandHistory.Reset();
|
||||
chatChrome.Visible = true;
|
||||
chatScrollPanel.ScrollToBottom();
|
||||
if (!chatText.IsDisabled())
|
||||
|
||||
@@ -57,6 +57,8 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
public Func<KeyInput, bool> OnEnterKey = _ => false;
|
||||
public Func<KeyInput, bool> OnTabKey = _ => false;
|
||||
public Func<KeyInput, bool> OnEscKey = _ => false;
|
||||
public Func<KeyInput, bool> OnArrowUp = _ => false;
|
||||
public Func<KeyInput, bool> OnArrowDown = _ => false;
|
||||
public Func<bool> OnAltKey = () => false;
|
||||
public Action OnLoseFocus = () => { };
|
||||
public Action OnTextEdited = () => { };
|
||||
@@ -282,6 +284,16 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
|
||||
break;
|
||||
|
||||
case Keycode.UP:
|
||||
if (OnArrowUp(e))
|
||||
return true;
|
||||
break;
|
||||
|
||||
case Keycode.DOWN:
|
||||
if (OnArrowDown(e))
|
||||
return true;
|
||||
break;
|
||||
|
||||
case Keycode.HOME:
|
||||
ResetBlinkCycle();
|
||||
if (e.Modifiers.HasModifier(Modifiers.Shift))
|
||||
|
||||
Reference in New Issue
Block a user