feat(ui): add InputBar with channel pill and symbol picker overlay
Channel pill picks Token.AccentEmber when the tab is a tell (matched by IsTempTab + a set TellTarget) and Token.AccentPrimary otherwise, so the tinted background reads as the channel type at a glance. SymbolPicker runs as an overlay popup — the inserted fragment splices straight into the pending buffer up to a 500-char cap. Send wiring and the settings button arrive when the main window assembles the components.
This commit is contained in:
@@ -135,6 +135,12 @@ internal static class PluginHostFactory
|
||||
sp.GetRequiredService<FontManager>()
|
||||
));
|
||||
services.AddSingleton(_ => new Ui.Components.SymbolPicker());
|
||||
services.AddSingleton(sp => new Ui.Components.InputBar(
|
||||
sp.GetRequiredService<Ui.Components.SymbolPicker>(),
|
||||
sp.GetRequiredService<FontManager>(),
|
||||
sp.GetRequiredService<ThemeRegistry>(),
|
||||
sp.GetRequiredService<Ui.StyleEngine.TokenResolver>()
|
||||
));
|
||||
services.AddSingleton(sp => new Integrations.FailedTellNotifier(
|
||||
sp.GetRequiredService<ILogger<Integrations.FailedTellNotifier>>()
|
||||
));
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
using System.Numerics;
|
||||
using Dalamud.Bindings.ImGui;
|
||||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.Utility.Raii;
|
||||
using HellionChat.Code;
|
||||
using HellionChat.Themes;
|
||||
using HellionChat.Ui.StyleEngine;
|
||||
using HellionChat.Util;
|
||||
|
||||
namespace HellionChat.Ui.Components;
|
||||
|
||||
// Bottom input row: channel pill, text field, quick buttons. Channel pill
|
||||
// recolours by tab type — cyan accent for a normal channel, ember accent
|
||||
// for a tell. Send wiring lands when the main window assembles the
|
||||
// components; for now this layer only handles buffer state and the symbol
|
||||
// picker overlay.
|
||||
internal sealed class InputBar
|
||||
{
|
||||
private const float Height = 32f;
|
||||
private const float PillHeight = 22f;
|
||||
private const float PillPaddingX = 8f;
|
||||
private const int BufferCapacity = 500;
|
||||
|
||||
private readonly SymbolPicker _symbolPicker;
|
||||
private readonly FontManager _fonts;
|
||||
private readonly ThemeRegistry _themes;
|
||||
private readonly TokenResolver _resolver;
|
||||
|
||||
private string _pendingMessage = string.Empty;
|
||||
|
||||
public InputBar(
|
||||
SymbolPicker symbolPicker,
|
||||
FontManager fonts,
|
||||
ThemeRegistry themes,
|
||||
TokenResolver resolver
|
||||
)
|
||||
{
|
||||
_symbolPicker = symbolPicker;
|
||||
_fonts = fonts;
|
||||
_themes = themes;
|
||||
_resolver = resolver;
|
||||
}
|
||||
|
||||
public string PendingMessage => _pendingMessage;
|
||||
|
||||
public void ClearBuffer() => _pendingMessage = string.Empty;
|
||||
|
||||
public void Draw(Tab? activeTab)
|
||||
{
|
||||
if (!_fonts.FontsReady)
|
||||
{
|
||||
ImGui.Dummy(new Vector2(0, Height));
|
||||
return;
|
||||
}
|
||||
|
||||
var theme = _themes.Active;
|
||||
var isTell = activeTab is { IsTempTab: true, TellTarget: { } target } && target.IsSet();
|
||||
var pillToken = isTell ? Token.AccentEmber : Token.AccentPrimary;
|
||||
var pillRgba = _resolver.Resolve(pillToken, theme.Colors);
|
||||
var pillAbgr = ColourUtil.RgbaToAbgr(pillRgba);
|
||||
var pillTextAbgr = ColourUtil.RgbaToAbgr(theme.Colors.TextPrimary);
|
||||
|
||||
DrawChannelPill(activeTab, isTell, pillAbgr, pillTextAbgr);
|
||||
ImGui.SameLine();
|
||||
DrawInputField();
|
||||
ImGui.SameLine();
|
||||
DrawQuickButtons();
|
||||
|
||||
// SymbolPicker popup is rendered last so it can splice its fragment
|
||||
// straight into the pending buffer.
|
||||
var inserted = _symbolPicker.DrawAndConsume();
|
||||
if (inserted is not null && _pendingMessage.Length + inserted.Length <= BufferCapacity)
|
||||
_pendingMessage += inserted;
|
||||
}
|
||||
|
||||
private static string ResolvePillLabel(Tab? tab, bool isTell)
|
||||
{
|
||||
if (isTell && tab?.TellTarget is { } t && t.IsSet())
|
||||
return $"→ {t.Name}";
|
||||
if (tab?.Channel is { } ch)
|
||||
return ch.ToChatType().Name();
|
||||
return "—";
|
||||
}
|
||||
|
||||
private void DrawChannelPill(Tab? tab, bool isTell, uint pillAbgr, uint textAbgr)
|
||||
{
|
||||
var label = ResolvePillLabel(tab, isTell);
|
||||
var labelSize = ImGui.CalcTextSize(label);
|
||||
var width = labelSize.X + PillPaddingX * 2;
|
||||
var origin = ImGui.GetCursorScreenPos();
|
||||
var dl = ImGui.GetWindowDrawList();
|
||||
var max = origin + new Vector2(width, PillHeight);
|
||||
|
||||
dl.AddRectFilled(origin, max, pillAbgr, 6f);
|
||||
dl.AddText(origin + new Vector2(PillPaddingX, 3f), textAbgr, label);
|
||||
|
||||
// Reserve the layout slot so SameLine after the pill knows the width.
|
||||
ImGui.Dummy(new Vector2(width, PillHeight));
|
||||
}
|
||||
|
||||
private void DrawInputField()
|
||||
{
|
||||
ImGui.SetNextItemWidth(-90f);
|
||||
ImGui.InputText("##hellion-input", ref _pendingMessage, BufferCapacity);
|
||||
}
|
||||
|
||||
private void DrawQuickButtons()
|
||||
{
|
||||
using (_fonts.FontAwesome.Push())
|
||||
{
|
||||
if (ImGui.Button(FontAwesomeIcon.SmileBeam.ToIconString()))
|
||||
_symbolPicker.OpenPopup();
|
||||
if (ImGui.IsItemHovered())
|
||||
{
|
||||
using (ImRaii.DefaultFont())
|
||||
ImGui.SetTooltip("Insert symbol");
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
if (ImGui.Button(FontAwesomeIcon.Cog.ToIconString()))
|
||||
{
|
||||
// Settings toggle wires up when the plugin window registers
|
||||
// its open handler; no-op until then so the button is
|
||||
// visible without dragging a half-finished settings call
|
||||
// into the component.
|
||||
}
|
||||
if (ImGui.IsItemHovered())
|
||||
{
|
||||
using (ImRaii.DefaultFont())
|
||||
ImGui.SetTooltip("Settings");
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
var hidden = Plugin.Config.HideChat;
|
||||
var visIcon = hidden ? FontAwesomeIcon.EyeSlash : FontAwesomeIcon.Eye;
|
||||
if (ImGui.Button(visIcon.ToIconString()))
|
||||
Plugin.Config.HideChat = !hidden;
|
||||
if (ImGui.IsItemHovered())
|
||||
{
|
||||
using (ImRaii.DefaultFont())
|
||||
ImGui.SetTooltip(hidden ? "Unhide chat" : "Hide chat");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user