A comment that reads "MUST stay in lockstep with TryGetActiveCrossfade (K8)" helps nobody outside the plan that used to have a K8 in it, and the plans are not in this repo. Same for "Spec FR-4", "plan §B.2", "Sub-Task 4.4" and the F/R/M/A/S round codes scattered through the style engine and the self-tests. Personal names go too. "tester feedback from Jin (v1.4.7)" and "Flo decision 2026-06-15" carry the reason fine without naming anyone -- the version and the reason are the parts a reader can act on, and a public repo should not need a cast list to be read. The rule applied throughout: keep the why, drop the reference. Version numbers stay, since those resolve through the changelog. 77 files. ChunkUtil also carried 281 lines of commented-out code -- an older ToChunks variant and two helpers with no callers, inherited and never removed. Deleted; git remembers them.
115 lines
4.0 KiB
C#
115 lines
4.0 KiB
C#
using Dalamud.Plugin.Ipc;
|
|
using HellionChat.Code;
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
namespace HellionChat.Ipc;
|
|
|
|
using ChatInputState = (
|
|
bool InputVisible,
|
|
bool InputFocused,
|
|
bool HasText,
|
|
bool IsTyping,
|
|
int TextLength,
|
|
ChatType ChannelType
|
|
);
|
|
|
|
internal sealed class TypingIpc : IDisposable
|
|
{
|
|
private Plugin Plugin { get; }
|
|
|
|
private ICallGateProvider<ChatInputState> StateQueryGate { get; }
|
|
private ICallGateProvider<ChatInputState, object?> StateChangedGate { get; }
|
|
|
|
// v1.4.9: ChatTwo IPC compatibility mirror. Some third-party plugins
|
|
// have a no-fork policy and subscribe only to ChatTwo.*-prefixed IPC
|
|
// gates. HellionChat replaces ChatTwo (conflict detection prevents
|
|
// parallel loading), so mirroring the ChatTwo provider slots lets those
|
|
// plugins keep working without code changes on their side. The tuple
|
|
// shape is textually identical to ChatTwo's IPC surface (same member
|
|
// order, same underlying types — ChatType is `ushort` in both repos)
|
|
// so Dalamud's IPC marshalling matches across plugin boundaries.
|
|
private ICallGateProvider<ChatInputState> ChatTwoStateQueryGate { get; }
|
|
private ICallGateProvider<ChatInputState, object?> ChatTwoStateChangedGate { get; }
|
|
|
|
private ChatInputState LastState;
|
|
private bool HasState;
|
|
|
|
private readonly Ui.Components.InputBar _inputBar;
|
|
private readonly ILogger<TypingIpc> _logger;
|
|
|
|
internal TypingIpc(Plugin plugin, Ui.Components.InputBar inputBar, ILogger<TypingIpc> logger)
|
|
{
|
|
Plugin = plugin;
|
|
_inputBar = inputBar;
|
|
_logger = logger;
|
|
|
|
StateQueryGate = Plugin.Interface.GetIpcProvider<ChatInputState>(
|
|
"HellionChat.GetChatInputState"
|
|
);
|
|
StateChangedGate = Plugin.Interface.GetIpcProvider<ChatInputState, object?>(
|
|
"HellionChat.ChatInputStateChanged"
|
|
);
|
|
|
|
// v1.4.9: ChatTwo-prefixed compatibility slots (see class-level comment).
|
|
ChatTwoStateQueryGate = Plugin.Interface.GetIpcProvider<ChatInputState>(
|
|
"ChatTwo.GetChatInputState"
|
|
);
|
|
ChatTwoStateChangedGate = Plugin.Interface.GetIpcProvider<ChatInputState, object?>(
|
|
"ChatTwo.ChatInputStateChanged"
|
|
);
|
|
|
|
StateQueryGate.RegisterFunc(GetState);
|
|
ChatTwoStateQueryGate.RegisterFunc(GetState);
|
|
}
|
|
|
|
private ChatInputState BuildState()
|
|
{
|
|
var usedChannel = Plugin.CurrentTab.CurrentChannel;
|
|
var inputChannel = usedChannel.UseTempChannel
|
|
? usedChannel.TempChannel
|
|
: usedChannel.Channel;
|
|
var channelType = inputChannel.ToChatType();
|
|
|
|
// MainWindow is Phase-1-resolved and never reassigned;
|
|
// the `?.` is defense-in-depth for pre-Phase-1 IPC-pulls.
|
|
var mainWindowOpen = Plugin.MainWindow?.IsOpen ?? false;
|
|
|
|
// Stale-state guard: InputBar's focus and pending-buffer fields are
|
|
// only written by DrawInputField. Closing MainWindow freezes them, so
|
|
// gate all four state fields on mainWindowOpen.
|
|
var inputFocused = mainWindowOpen && _inputBar.IsFocused;
|
|
var hasText = mainWindowOpen && _inputBar.PendingLength > 0;
|
|
var textLength = mainWindowOpen ? _inputBar.PendingLength : 0;
|
|
|
|
return (
|
|
InputVisible: mainWindowOpen,
|
|
InputFocused: inputFocused,
|
|
HasText: hasText,
|
|
IsTyping: hasText,
|
|
TextLength: textLength,
|
|
ChannelType: channelType
|
|
);
|
|
}
|
|
|
|
internal ChatInputState GetState() => BuildState();
|
|
|
|
internal void Update()
|
|
{
|
|
var state = BuildState();
|
|
if (HasState && state.Equals(LastState))
|
|
return;
|
|
|
|
HasState = true;
|
|
LastState = state;
|
|
StateChangedGate.SendMessage(state);
|
|
// v1.4.9: mirror on ChatTwo-prefixed slot for no-fork-policy plugins.
|
|
ChatTwoStateChangedGate.SendMessage(state);
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
StateQueryGate.UnregisterFunc();
|
|
ChatTwoStateQueryGate.UnregisterFunc();
|
|
}
|
|
}
|