feat(chat): two densities that actually look different

Card density puts the sender on its own line with the body indented onto the text
column beneath it, and six pixels of air after each one. That air is what makes a
card read as a card, and it goes through the measured row height so the clipper
plans against it rather than around it.

System messages go italic in both densities. Nobody said them -- it is the game
talking -- and italics carry that in every palette. Colour would have been the
obvious alternative and is the wrong tool twice over: the rule this cycle runs on
says typography solves what typography can, and the channel colours already in
those chunks come from the game and are not ours to dim.

The italic face falls back to the game's own italic rather than to upright text
when the custom one is switched off, so the distinction survives either setting.
This commit is contained in:
2026-08-19 11:48:46 +02:00
parent 81e4c9367a
commit 63d1b34b00
+27 -4
View File
@@ -244,6 +244,16 @@ internal sealed class MessageList
// Same size as the body face, drawn heavier. With the game font selected
// there is no heavier variant, so the sender leans on channel colour alone.
// From the mockup: the space between two messages in card density.
private const float CardGapRaw = 6f;
// The italic handle is optional -- the setting can disable it -- so this
// falls back to the game's own italic rather than to upright text.
private Dalamud.Interface.ManagedFontAtlas.IFontHandle ItalicFace() =>
Plugin.Config.FontsEnabled && _fonts.ItalicFont is not null
? _fonts.ItalicFont
: _fonts.AxisItalic;
private Dalamud.Interface.ManagedFontAtlas.IFontHandle SenderFace() =>
Plugin.Config.FontsEnabled || Plugin.Config.UseHellionFont
? _fonts.SenderFont!
@@ -328,18 +338,25 @@ internal sealed class MessageList
// (ChatLogWindow.cs:1965: DrawChunks(message.Sender) + SameLine).
DrawTimestampCell(message);
if (message.Sender.Count > 0)
if (message.Sender.Count == 0)
{
using (SenderFace().Push())
// Nobody said this -- it is the game talking. Italics carry that in
// every palette, which colour would not: the channel colours already
// in these chunks come from the game and are not ours to override.
using (ItalicFace().Push())
_chunkRenderer.DrawChunks(
message.Sender,
message.Content,
wrap: true,
handler: _handler,
lineWidth: 0f
);
ImGui.SameLine(0f, 0f);
return;
}
using (SenderFace().Push())
_chunkRenderer.DrawChunks(message.Sender, wrap: true, handler: _handler, lineWidth: 0f);
ImGui.SameLine(0f, 0f);
_chunkRenderer.DrawChunks(message.Content, wrap: true, handler: _handler, lineWidth: 0f);
}
@@ -540,12 +557,14 @@ internal sealed class MessageList
if (message.Sender.Count == 0)
{
DrawTimestampCell(message);
using (ItalicFace().Push())
_chunkRenderer.DrawChunks(
message.Content,
wrap: true,
handler: _handler,
lineWidth: 0f
);
ImGui.Dummy(new Vector2(0f, CardGapRaw * StyleEngine.Metrics.Scale));
return;
}
@@ -557,6 +576,10 @@ internal sealed class MessageList
ImGui.Indent(_stampColumnWidth);
_chunkRenderer.DrawChunks(message.Content, wrap: true, handler: _handler, lineWidth: 0f);
ImGui.Unindent(_stampColumnWidth);
// Air between cards is what makes them read as cards. Measured into the
// row height, so the clipper plans against it.
ImGui.Dummy(new Vector2(0f, CardGapRaw * StyleEngine.Metrics.Scale));
}
private static string FormatTimestamp(DateTimeOffset date)