feat(style): name the four type roles

Body, Sender, Header, Meta. Three of them share the base size, which looks like
an oversight and is not: the sender is set apart by weight and the header by
small caps with wide tracking. Neither of those is a size, and solving them with
size instead would turn the log into a ransom note. Only meta steps down, because
it is meant to be skipped over rather than read.

Factors sit in a static array rather than as consts. The master spec puts
typography under theme control, and ThemeTypography is already the declared
extension point for it -- a const would wall that off before anyone gets there.

No caller yet. FontManager takes the first one in the next commit; that is the
one place in this cycle where a piece lands before its call site, and it closes
inside the same block.
This commit is contained in:
2026-08-19 09:44:31 +02:00
parent 58830aecec
commit 6d2bb95528
+42
View File
@@ -0,0 +1,42 @@
namespace HellionChat.Ui.StyleEngine;
internal enum TypeRole
{
Body,
Sender,
Header,
Meta,
}
// Named sizes derived from one base, so a role means the same thing wherever it
// is drawn.
//
// Three of the four share the base size. That is deliberate: what sets the
// sender apart is weight and what sets the header apart is small caps with wide
// tracking, and neither is a size. Solving those with size instead would make
// the log look like a ransom note. Only the meta role -- timestamps and the
// header's trailing detail -- steps down, because it is meant to be skipped over
// rather than read.
//
// The factors are defaults, not constants. The master spec puts typography under
// theme control rather than user control, and ThemeTypography already exists as
// the extension point for exactly that. A const would wall it off. What is
// deliberately absent either way is a user-facing slider per role.
internal static class TypeScale
{
// Below this a timestamp stops being readable at any display scale.
internal const float MinPt = 7f;
private static readonly float[] Factors =
[
1.00f, // Body -- the reference every other role is stated against
1.00f, // Sender -- set apart by weight, see FontManager.SenderWeight
1.00f, // Header -- set apart by small caps and tracking
0.82f, // Meta -- timestamps, and the world and clock in the header
];
internal static float FactorOf(TypeRole role) => Factors[(int)role];
internal static float SizePtOf(TypeRole role, float basePt) =>
TypeScaleMath.Resolve(basePt, FactorOf(role), MinPt);
}