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 Dictionary<TextNotificationPool, Widget> templates = [];
|
||||||
|
|
||||||
readonly TabCompletionLogic tabCompletion = new();
|
readonly TabCompletionLogic tabCompletion = new();
|
||||||
|
readonly CommandHistory commandHistory = CommandHistory.Instance;
|
||||||
|
|
||||||
readonly string chatLineSound = ChromeMetrics.Get<string>("ChatLineSound");
|
readonly string chatLineSound = ChromeMetrics.Get<string>("ChatLineSound");
|
||||||
|
|
||||||
@@ -159,11 +160,18 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
|||||||
{
|
{
|
||||||
var text = chatText.Text.Trim();
|
var text = chatText.Text.Trim();
|
||||||
var from = world.IsReplay ? null : orderManager.LocalClient.Name;
|
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)
|
foreach (var trait in chatTraits)
|
||||||
trait.OnChat(from, text);
|
trait.OnChat(from, text);
|
||||||
|
|
||||||
|
if (isValidCommand)
|
||||||
|
commandHistory.AddCommand(text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
commandHistory.Reset();
|
||||||
chatText.Text = "";
|
chatText.Text = "";
|
||||||
if (!isMenuChat)
|
if (!isMenuChat)
|
||||||
CloseChat();
|
CloseChat();
|
||||||
@@ -191,9 +199,40 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
|||||||
else
|
else
|
||||||
chatText.YieldKeyboardFocus();
|
chatText.YieldKeyboardFocus();
|
||||||
|
|
||||||
|
commandHistory.Reset();
|
||||||
return true;
|
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));
|
chatAvailableIn = new CachedTransform<int, string>(x => FluentProvider.GetMessage(ChatAvailability, "seconds", x));
|
||||||
|
|
||||||
if (!isMenuChat)
|
if (!isMenuChat)
|
||||||
@@ -265,6 +304,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
|||||||
public void OpenChat()
|
public void OpenChat()
|
||||||
{
|
{
|
||||||
chatText.Text = "";
|
chatText.Text = "";
|
||||||
|
commandHistory.Reset();
|
||||||
chatChrome.Visible = true;
|
chatChrome.Visible = true;
|
||||||
chatScrollPanel.ScrollToBottom();
|
chatScrollPanel.ScrollToBottom();
|
||||||
if (!chatText.IsDisabled())
|
if (!chatText.IsDisabled())
|
||||||
|
|||||||
@@ -57,6 +57,8 @@ namespace OpenRA.Mods.Common.Widgets
|
|||||||
public Func<KeyInput, bool> OnEnterKey = _ => false;
|
public Func<KeyInput, bool> OnEnterKey = _ => false;
|
||||||
public Func<KeyInput, bool> OnTabKey = _ => false;
|
public Func<KeyInput, bool> OnTabKey = _ => false;
|
||||||
public Func<KeyInput, bool> OnEscKey = _ => 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 Func<bool> OnAltKey = () => false;
|
||||||
public Action OnLoseFocus = () => { };
|
public Action OnLoseFocus = () => { };
|
||||||
public Action OnTextEdited = () => { };
|
public Action OnTextEdited = () => { };
|
||||||
@@ -282,6 +284,16 @@ namespace OpenRA.Mods.Common.Widgets
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case Keycode.UP:
|
||||||
|
if (OnArrowUp(e))
|
||||||
|
return true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Keycode.DOWN:
|
||||||
|
if (OnArrowDown(e))
|
||||||
|
return true;
|
||||||
|
break;
|
||||||
|
|
||||||
case Keycode.HOME:
|
case Keycode.HOME:
|
||||||
ResetBlinkCycle();
|
ResetBlinkCycle();
|
||||||
if (e.Modifiers.HasModifier(Modifiers.Shift))
|
if (e.Modifiers.HasModifier(Modifiers.Shift))
|
||||||
|
|||||||
Reference in New Issue
Block a user