popout: opt-in ChatInputBar with focus-aware keybind routing
This commit is contained in:
@@ -465,12 +465,21 @@ internal unsafe class KeybindManager : IDisposable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// v0.6.0 — central dispatch for ChatTabForward/Backward so Task 25
|
// v0.6.0 — central dispatch for ChatTabForward/Backward. If a pop-out
|
||||||
// can extend it with focus-aware routing to pop-out ChatInputBars.
|
// window currently has its compact input focused, the keybind is
|
||||||
// Right now both windows share the main ChatLogWindow.ChangeTabDelta,
|
// forwarded into that pop-out's ChatInputBar so the user navigates
|
||||||
// identical to v0.5.x behavior.
|
// tabs in the window they are typing in. Otherwise the main window
|
||||||
|
// handles it (= v0.5.x behavior).
|
||||||
private void DispatchTabDelta(int delta)
|
private void DispatchTabDelta(int delta)
|
||||||
{
|
{
|
||||||
|
foreach (var popout in Plugin.ChatLogWindow.ActivePopouts)
|
||||||
|
{
|
||||||
|
if (popout.HasFocusedInputBar && popout.InputBar != null)
|
||||||
|
{
|
||||||
|
popout.InputBar.HandleKeybindForward(delta);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
Plugin.ChatLogWindow.ChangeTabDelta(delta);
|
Plugin.ChatLogWindow.ChangeTabDelta(delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,11 @@ namespace ChatTwo;
|
|||||||
// Hellion Chat — v0.6.0 shared input history. Replaces the embedded
|
// Hellion Chat — v0.6.0 shared input history. Replaces the embedded
|
||||||
// ChatLogWindow.InputBacklog so that pop-out windows with their own
|
// ChatLogWindow.InputBacklog so that pop-out windows with their own
|
||||||
// ChatInputBar can navigate the same Up/Down history as the main window.
|
// ChatInputBar can navigate the same Up/Down history as the main window.
|
||||||
// Newest entry at index 0; consecutive duplicates are collapsed.
|
// Index semantics are kept identical to the v0.5.x InputBacklog:
|
||||||
|
// index 0 = oldest entry
|
||||||
|
// index Count - 1 = newest entry
|
||||||
|
// Push performs move-to-newest deduplication: existing entries are
|
||||||
|
// removed before the new one is appended at the end.
|
||||||
public static class InputHistoryService
|
public static class InputHistoryService
|
||||||
{
|
{
|
||||||
private const int MaxSize = 30;
|
private const int MaxSize = 30;
|
||||||
@@ -13,6 +17,8 @@ public static class InputHistoryService
|
|||||||
|
|
||||||
public static IReadOnlyList<string> Entries => _entries;
|
public static IReadOnlyList<string> Entries => _entries;
|
||||||
|
|
||||||
|
public static int Count => _entries.Count;
|
||||||
|
|
||||||
public static void Push(string entry)
|
public static void Push(string entry)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(entry))
|
if (string.IsNullOrWhiteSpace(entry))
|
||||||
@@ -20,14 +26,20 @@ public static class InputHistoryService
|
|||||||
|
|
||||||
var trimmed = entry.Trim();
|
var trimmed = entry.Trim();
|
||||||
|
|
||||||
// Drop consecutive duplicates so spamming the same line does not
|
// Move-to-newest: existing entries are removed before the append
|
||||||
// pollute the history with repeats.
|
// so the same line typed twice does not occupy two history slots.
|
||||||
if (_entries.Count > 0 && _entries[0] == trimmed)
|
for (var i = 0; i < _entries.Count; i++)
|
||||||
return;
|
{
|
||||||
|
if (_entries[i] == trimmed)
|
||||||
|
{
|
||||||
|
_entries.RemoveAt(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_entries.Insert(0, trimmed);
|
_entries.Add(trimmed);
|
||||||
if (_entries.Count > MaxSize)
|
if (_entries.Count > MaxSize)
|
||||||
_entries.RemoveAt(_entries.Count - 1);
|
_entries.RemoveAt(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string? GetByCursor(int cursor)
|
public static string? GetByCursor(int cursor)
|
||||||
@@ -36,6 +48,4 @@ public static class InputHistoryService
|
|||||||
return null;
|
return null;
|
||||||
return _entries[cursor];
|
return _entries[cursor];
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int Count => _entries.Count;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1490,6 +1490,13 @@ public sealed class ChatLogWindow : Window
|
|||||||
|
|
||||||
internal readonly List<bool> PopOutDocked = [];
|
internal readonly List<bool> PopOutDocked = [];
|
||||||
internal readonly HashSet<Guid> PopOutWindows = [];
|
internal readonly HashSet<Guid> PopOutWindows = [];
|
||||||
|
|
||||||
|
// v0.6.0 — live enumeration of all active Popout windows so the
|
||||||
|
// KeybindManager can find a focused ChatInputBar to forward tab-cycle
|
||||||
|
// keybinds to. Filter on IsOpen prevents touching closed-but-still-
|
||||||
|
// registered popouts.
|
||||||
|
internal IEnumerable<Popout> ActivePopouts =>
|
||||||
|
Plugin.WindowSystem.Windows.OfType<Popout>().Where(p => p.IsOpen);
|
||||||
private void AddPopOutsToDraw()
|
private void AddPopOutsToDraw()
|
||||||
{
|
{
|
||||||
HandlerLender.ResetCounter();
|
HandlerLender.ResetCounter();
|
||||||
|
|||||||
+33
-1
@@ -15,6 +15,13 @@ internal class Popout : Window
|
|||||||
private long FrameTime; // set every frame
|
private long FrameTime; // set every frame
|
||||||
private long LastActivityTime = Environment.TickCount64;
|
private long LastActivityTime = Environment.TickCount64;
|
||||||
|
|
||||||
|
// v0.6.0 — optional input bar inside the pop-out window. Lazy-allocated
|
||||||
|
// when the user enables Tab.PopOutInputEnabled and torn down when the
|
||||||
|
// toggle is turned off (independent text buffer is intentionally
|
||||||
|
// discarded — see v0.6.0 spec edge-case P1).
|
||||||
|
public ChatInputBar? InputBar { get; private set; }
|
||||||
|
public bool HasFocusedInputBar => InputBar?.IsFocused ?? false;
|
||||||
|
|
||||||
public Popout(ChatLogWindow chatLogWindow, Tab tab, int idx) : base($"{tab.Name}##popout")
|
public Popout(ChatLogWindow chatLogWindow, Tab tab, int idx) : base($"{tab.Name}##popout")
|
||||||
{
|
{
|
||||||
ChatLogWindow = chatLogWindow;
|
ChatLogWindow = chatLogWindow;
|
||||||
@@ -93,8 +100,33 @@ internal class Popout : Window
|
|||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// v0.6.0 — pop-out optional input bar. Reserve height first so the
|
||||||
|
// message log draws into the right region; only shown when the
|
||||||
|
// per-tab toggle is on. Toggle-OFF resets InputBar so the next
|
||||||
|
// toggle-ON gives a fresh buffer (no stale text persists).
|
||||||
|
var inputEnabled = Tab.PopOutInputEnabled;
|
||||||
|
if (!inputEnabled && InputBar != null)
|
||||||
|
{
|
||||||
|
InputBar = null;
|
||||||
|
}
|
||||||
|
if (inputEnabled)
|
||||||
|
{
|
||||||
|
InputBar ??= new ChatInputBar(ChatLogWindow.Plugin, ChatLogWindow, () => Tab);
|
||||||
|
}
|
||||||
|
|
||||||
|
var inputBarHeight = inputEnabled
|
||||||
|
? ImGui.GetFrameHeightWithSpacing() + ImGui.GetStyle().ItemSpacing.Y
|
||||||
|
: 0f;
|
||||||
|
|
||||||
var handler = ChatLogWindow.HandlerLender.Borrow();
|
var handler = ChatLogWindow.HandlerLender.Borrow();
|
||||||
ChatLogWindow.DrawMessageLog(Tab, handler, ImGui.GetContentRegionAvail().Y, false);
|
var logHeight = ImGui.GetContentRegionAvail().Y - inputBarHeight;
|
||||||
|
ChatLogWindow.DrawMessageLog(Tab, handler, logHeight, false);
|
||||||
|
|
||||||
|
if (inputEnabled && InputBar != null)
|
||||||
|
{
|
||||||
|
ImGui.Separator();
|
||||||
|
InputBar.RenderCompact();
|
||||||
|
}
|
||||||
|
|
||||||
if (ImGui.IsWindowHovered(ImGuiHoveredFlags.ChildWindows))
|
if (ImGui.IsWindowHovered(ImGuiHoveredFlags.ChildWindows))
|
||||||
LastActivityTime = FrameTime;
|
LastActivityTime = FrameTime;
|
||||||
|
|||||||
Reference in New Issue
Block a user