diff --git a/HellionChat/Ui/Components/InputBar.cs b/HellionChat/Ui/Components/InputBar.cs index cb78a47..bd3b4f0 100644 --- a/HellionChat/Ui/Components/InputBar.cs +++ b/HellionChat/Ui/Components/InputBar.cs @@ -311,6 +311,11 @@ internal sealed class InputBar } } + // -1 means "not browsing". Per input bar, not shared: the history itself is + // global across the main window and every pop-out, but where each of them is + // in it is not. + private int _historyCursor = -1; + private void DrawInputField(Tab? activeTab) { if (Activate) @@ -328,7 +333,8 @@ internal sealed class InputBar ImGuiInputTextFlags.EnterReturnsTrue | ImGuiInputTextFlags.CallbackEdit | ImGuiInputTextFlags.CallbackCompletion - | ImGuiInputTextFlags.CallbackAlways, + | ImGuiInputTextFlags.CallbackAlways + | ImGuiInputTextFlags.CallbackHistory, SlashCommandCallback ) ) @@ -360,6 +366,39 @@ internal sealed class InputBar return 0; } + // Up and down walk the sent-message history, the way 1.5.6 did. ImGui + // only raises this event when CallbackHistory is set on the field, which + // is why the arrows did nothing at all before: the service and the + // cursor maths were both here and tested, with no caller and no flag. + if (data.EventFlag == ImGuiInputTextFlags.CallbackHistory) + { + var direction = + data.EventKey == ImGuiKey.UpArrow + ? CompactInputHistoryNavigator.Direction.Up + : CompactInputHistoryNavigator.Direction.Down; + + var (cursor, replacement) = CompactInputHistoryNavigator.Navigate( + direction, + _historyCursor, + _pendingMessage, + () => InputHistoryService.Count, + InputHistoryService.Push, + InputHistoryService.GetByCursor + ); + + _historyCursor = cursor; + if (replacement is null) + return 0; + + // The buffer belongs to ImGui inside a callback; writing the managed + // field here would be overwritten on the way out. + data.DeleteChars(0, data.BufTextLen); + if (replacement.Length > 0) + data.InsertChars(0, replacement); + + return 0; + } + if (data.EventFlag == ImGuiInputTextFlags.CallbackCompletion) { // CursorPos is a BYTE offset into the UTF-8 buffer. We decode the @@ -381,6 +420,13 @@ internal sealed class InputBar // CallbackEdit (or any remaining event): v1.5.6 character-level slash // detection keeps CommandHelpWindow in sync with what the user is // typing without a per-frame poll. + // + // Typing also ends the history walk. Without this, down-arrow after + // editing a recalled line would jump to the next entry and throw the + // edit away. + if (data.EventFlag == ImGuiInputTextFlags.CallbackEdit) + _historyCursor = -1; + _commandHelpWindow.IsOpen = false; var text = Encoding.UTF8.GetString(data.BufTextSpan); @@ -455,6 +501,12 @@ internal sealed class InputBar return; } ChatBox.SendMessageUnsafe(bytes); + + // Pushed before the buffer is cleared, and the trimmed form is what + // goes in: the history is for recalling what you typed, not the + // whitespace around it. + InputHistoryService.Push(text); + _historyCursor = -1; _pendingMessage = string.Empty; // 1.5.6 parity (1d3b429:ChatLogWindow.cs:1558): clear the temp channel