feat(message-list): wire ChunkRenderer ctor + AttachPayloadHandler setter
H integrates the chunk-render pipeline into MessageList: - Extends ctor to 4 params (themes, resolver, fonts, chunkRenderer); TokenResolver preserved as load-bearing dep - Adds private PayloadHandler? _handler field + internal AttachPayloadHandler(PayloadHandler) setter - Switches DrawCompactRow/DrawCardRow render-path to _chunkRenderer.DrawChunks(message.Content, wrap, handler, 0f) instead of plain TextUnformatted Setter-injection for PayloadHandler is the §6.2 cycle-resolution (PayloadHandler → MainWindow → MessageList → PayloadHandler ctor-cycle broken by post-construction wiring). G's HostedService.StartAsync will call AttachPayloadHandler after both singletons resolve. Also extends MessageList DI-reg in PluginHostFactory.cs with the ChunkRenderer arg (4th GetRequiredService).
This commit is contained in:
@@ -132,7 +132,8 @@ internal static class PluginHostFactory
|
|||||||
services.AddSingleton(sp => new Ui.Components.MessageList(
|
services.AddSingleton(sp => new Ui.Components.MessageList(
|
||||||
sp.GetRequiredService<ThemeRegistry>(),
|
sp.GetRequiredService<ThemeRegistry>(),
|
||||||
sp.GetRequiredService<Ui.StyleEngine.TokenResolver>(),
|
sp.GetRequiredService<Ui.StyleEngine.TokenResolver>(),
|
||||||
sp.GetRequiredService<FontManager>()
|
sp.GetRequiredService<FontManager>(),
|
||||||
|
sp.GetRequiredService<Ui.Components.ChunkRenderer>()
|
||||||
));
|
));
|
||||||
services.AddSingleton(_ => new Ui.Components.SymbolPicker());
|
services.AddSingleton(_ => new Ui.Components.SymbolPicker());
|
||||||
services.AddSingleton(sp => new Ui.Components.InputBar(
|
services.AddSingleton(sp => new Ui.Components.InputBar(
|
||||||
|
|||||||
@@ -12,8 +12,7 @@ namespace HellionChat.Ui.Components;
|
|||||||
// rows have a constant line height; card mode falls back to a linear
|
// rows have a constant line height; card mode falls back to a linear
|
||||||
// render with a per-message height cache and an IsItemVisible skip path
|
// render with a per-message height cache and an IsItemVisible skip path
|
||||||
// so off-screen rows place a Dummy of the cached height rather than
|
// so off-screen rows place a Dummy of the cached height rather than
|
||||||
// running the full render again. Text-only rendering for now — full
|
// running the full render again.
|
||||||
// chunk/payload rendering re-attaches in a later cycle.
|
|
||||||
internal sealed class MessageList
|
internal sealed class MessageList
|
||||||
{
|
{
|
||||||
private const float CompactRowHeight = 18f;
|
private const float CompactRowHeight = 18f;
|
||||||
@@ -21,12 +20,28 @@ internal sealed class MessageList
|
|||||||
private readonly ThemeRegistry _themes;
|
private readonly ThemeRegistry _themes;
|
||||||
private readonly TokenResolver _resolver;
|
private readonly TokenResolver _resolver;
|
||||||
private readonly FontManager _fonts;
|
private readonly FontManager _fonts;
|
||||||
|
private readonly ChunkRenderer _chunkRenderer;
|
||||||
|
|
||||||
public MessageList(ThemeRegistry themes, TokenResolver resolver, FontManager fonts)
|
private PayloadHandler? _handler;
|
||||||
|
|
||||||
|
// §6.2: setter-injection breaks the PayloadHandler → MainWindow → MessageList → PayloadHandler 3-cycle.
|
||||||
|
// Wired by PayloadHandlerInitHostedService.StartAsync after both singletons exist.
|
||||||
|
internal void AttachPayloadHandler(PayloadHandler handler)
|
||||||
|
{
|
||||||
|
_handler = handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MessageList(
|
||||||
|
ThemeRegistry themes,
|
||||||
|
TokenResolver resolver,
|
||||||
|
FontManager fonts,
|
||||||
|
ChunkRenderer chunkRenderer
|
||||||
|
)
|
||||||
{
|
{
|
||||||
_themes = themes;
|
_themes = themes;
|
||||||
_resolver = resolver;
|
_resolver = resolver;
|
||||||
_fonts = fonts;
|
_fonts = fonts;
|
||||||
|
_chunkRenderer = chunkRenderer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Draw(Tab tab)
|
public void Draw(Tab tab)
|
||||||
@@ -40,10 +55,6 @@ internal sealed class MessageList
|
|||||||
// No own ImRaii.Child here — MainWindow already wraps the message
|
// No own ImRaii.Child here — MainWindow already wraps the message
|
||||||
// area in one. Nesting would give the window two stacked scrolls
|
// area in one. Nesting would give the window two stacked scrolls
|
||||||
// and a runaway content-height computation.
|
// and a runaway content-height computation.
|
||||||
var theme = _themes.Active;
|
|
||||||
var textAbgr = ColourUtil.RgbaToAbgr(theme.Colors.TextPrimary);
|
|
||||||
var mutedAbgr = ColourUtil.RgbaToAbgr(theme.Colors.TextMuted);
|
|
||||||
|
|
||||||
using var messages = tab.Messages.GetReadOnly(3);
|
using var messages = tab.Messages.GetReadOnly(3);
|
||||||
var compact = Plugin.Config.UseCompactDensity;
|
var compact = Plugin.Config.UseCompactDensity;
|
||||||
|
|
||||||
@@ -53,15 +64,15 @@ internal sealed class MessageList
|
|||||||
var pinnedToBottom = ImGui.GetScrollY() >= ImGui.GetScrollMaxY() - 1f;
|
var pinnedToBottom = ImGui.GetScrollY() >= ImGui.GetScrollMaxY() - 1f;
|
||||||
|
|
||||||
if (compact)
|
if (compact)
|
||||||
DrawCompact(messages, textAbgr, mutedAbgr);
|
DrawCompact(messages);
|
||||||
else
|
else
|
||||||
DrawCard(tab, messages, textAbgr, mutedAbgr);
|
DrawCard(tab, messages);
|
||||||
|
|
||||||
if (pinnedToBottom)
|
if (pinnedToBottom)
|
||||||
ImGui.SetScrollHereY(1f);
|
ImGui.SetScrollHereY(1f);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawCompact(IReadOnlyList<Message> messages, uint textAbgr, uint mutedAbgr)
|
private void DrawCompact(IReadOnlyList<Message> messages)
|
||||||
{
|
{
|
||||||
unsafe
|
unsafe
|
||||||
{
|
{
|
||||||
@@ -72,7 +83,7 @@ internal sealed class MessageList
|
|||||||
while (clipper.Step())
|
while (clipper.Step())
|
||||||
{
|
{
|
||||||
for (var i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
|
for (var i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
|
||||||
DrawCompactRow(messages[i], textAbgr, mutedAbgr);
|
DrawCompactRow(messages[i]);
|
||||||
}
|
}
|
||||||
clipper.End();
|
clipper.End();
|
||||||
}
|
}
|
||||||
@@ -83,18 +94,18 @@ internal sealed class MessageList
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawCompactRow(Message message, uint textAbgr, uint mutedAbgr)
|
private void DrawCompactRow(Message message)
|
||||||
{
|
{
|
||||||
var timestamp = FormatTimestamp(message.Date);
|
var timestamp = FormatTimestamp(message.Date);
|
||||||
var sender = message.SenderSource.TextValue;
|
var sender = message.SenderSource.TextValue;
|
||||||
var content = message.ContentSource.TextValue;
|
ImGui.TextUnformatted(
|
||||||
var line = string.IsNullOrEmpty(sender)
|
string.IsNullOrEmpty(sender) ? timestamp : $"{timestamp} {sender}: "
|
||||||
? $"{timestamp} {content}"
|
);
|
||||||
: $"{timestamp} {sender}: {content}";
|
ImGui.SameLine();
|
||||||
ImGui.TextUnformatted(line);
|
_chunkRenderer.DrawChunks(message.Content, wrap: true, handler: _handler, lineWidth: 0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawCard(Tab tab, IReadOnlyList<Message> messages, uint textAbgr, uint mutedAbgr)
|
private void DrawCard(Tab tab, IReadOnlyList<Message> messages)
|
||||||
{
|
{
|
||||||
var tabId = tab.Identifier;
|
var tabId = tab.Identifier;
|
||||||
for (var i = 0; i < messages.Count; i++)
|
for (var i = 0; i < messages.Count; i++)
|
||||||
@@ -127,11 +138,8 @@ internal sealed class MessageList
|
|||||||
{
|
{
|
||||||
var timestamp = FormatTimestamp(message.Date);
|
var timestamp = FormatTimestamp(message.Date);
|
||||||
var sender = message.SenderSource.TextValue;
|
var sender = message.SenderSource.TextValue;
|
||||||
var content = message.ContentSource.TextValue;
|
|
||||||
ImGui.TextUnformatted(string.IsNullOrEmpty(sender) ? timestamp : $"{timestamp} {sender}");
|
ImGui.TextUnformatted(string.IsNullOrEmpty(sender) ? timestamp : $"{timestamp} {sender}");
|
||||||
ImGui.PushTextWrapPos(0f);
|
_chunkRenderer.DrawChunks(message.Content, wrap: true, handler: _handler, lineWidth: 0f);
|
||||||
ImGui.TextUnformatted(content);
|
|
||||||
ImGui.PopTextWrapPos();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string FormatTimestamp(DateTimeOffset date)
|
private static string FormatTimestamp(DateTimeOffset date)
|
||||||
|
|||||||
Reference in New Issue
Block a user