Add constant rich text placeholders
CI / build-and-test (push) Canceled after 0s

This commit is contained in:
2026-08-30 23:23:48 +02:00
parent 28469bf547
commit 527c186090
15 changed files with 529 additions and 15 deletions
+59 -4
View File
@@ -23,7 +23,7 @@ public sealed class QuestTemplateRenderer : ITemplateRenderer, ITemplatePreviewR
private static IReadOnlyDictionary<string, PlaceholderValue> ValidateData(LoadedTemplate template,
ITemplateDataProvider provider)
{
var values = provider.GetValues();
var values = TemplateDataResolver.Resolve(template.Manifest, provider.GetValues());
var validation = new TemplateLoader().Validate(template, values.ToDictionary(x => x.Key, x => x.Value.Type));
if (!validation.IsValid) throw new TemplateValidationException(validation);
return values;
@@ -71,11 +71,12 @@ public sealed class QuestTemplateRenderer : ITemplateRenderer, ITemplatePreviewR
.TranslateY(UnitConverter.Points(text.Y, unit))
.Width(UnitConverter.Points(Math.Max(0, template.Layout.Width - text.X), unit))
.Height(QuestTemplateRenderer.ParseFloat(text.Attributes, "size", 11) * 1.8f);
RenderText(textContainer, ResolveContent(text.Content, text.Placeholder, text.Format, values), text.Attributes);
RenderResolvedText(textContainer, text.Content, text.Placeholder, text.Format,
values, template.Manifest, text.Attributes);
break;
case TextBoxElement box:
RenderText(Position(root, box, unit).Shrink(),
ResolveContent(box.Content, box.Placeholder, box.Format, values), box.Attributes);
RenderResolvedText(Position(root, box, unit).Shrink(), box.Content, box.Placeholder, box.Format,
values, template.Manifest, box.Attributes);
break;
case TableElement table:
if (values.GetValueOrDefault(table.Placeholder) is TableValue tableValue)
@@ -104,9 +105,63 @@ public sealed class QuestTemplateRenderer : ITemplateRenderer, ITemplatePreviewR
descriptor.FontSize(ParseFloat(attributes, "size", 11));
if (ParseBool(attributes, "bold")) descriptor.SemiBold();
if (ParseBool(attributes, "italic")) descriptor.Italic();
if (ParseBool(attributes, "underline")) descriptor.Underline();
if (attributes.TryGetValue("color", out var color)) descriptor.FontColor(color);
}
private static void RenderResolvedText(IContainer container, string content, string? placeholder, string? format,
IReadOnlyDictionary<string, PlaceholderValue> values, TemplateManifest manifest,
IReadOnlyDictionary<string, string> elementAttributes)
{
var attributes = TextAttributes(manifest, placeholder, elementAttributes);
var definition = placeholder is null ? null
: manifest.Placeholders.FirstOrDefault(x => x.Name.Equals(placeholder, StringComparison.Ordinal));
if (definition is { IsConstant: true, Type: PlaceholderType.Text or PlaceholderType.Multiline })
{
RenderRichText(container, TemplateRichText.Parse(definition.ConstantValue ?? ""), values, attributes);
return;
}
RenderText(container, ResolveContent(content, placeholder, format, values), attributes);
}
private static void RenderRichText(IContainer container, IReadOnlyList<TemplateRichTextRun> runs,
IReadOnlyDictionary<string, PlaceholderValue> values, IReadOnlyDictionary<string, string> attributes)
{
var aligned = attributes.GetValueOrDefault("align", "left").ToLowerInvariant() switch
{ "center" => container.AlignCenter(), "right" => container.AlignRight(), _ => container.AlignLeft() };
var fontSize = ParseFloat(attributes, "size", 11);
var globalBold = ParseBool(attributes, "bold");
var globalItalic = ParseBool(attributes, "italic");
var globalUnderline = ParseBool(attributes, "underline");
attributes.TryGetValue("color", out var color);
aligned.Text(text =>
{
foreach (var run in runs)
{
var content = run.Placeholder is null ? run.Text
: values.TryGetValue(run.Placeholder, out var value) ? Format(value, run.Format) : "";
var span = text.Span(content).FontSize(fontSize);
if (globalBold || run.Bold) span.SemiBold();
if (globalItalic || run.Italic) span.Italic();
if (globalUnderline || run.Underline) span.Underline();
if (color is not null) span.FontColor(color);
}
});
}
private static IReadOnlyDictionary<string, string> TextAttributes(TemplateManifest manifest, string? placeholder,
IReadOnlyDictionary<string, string> elementAttributes)
{
if (placeholder is null) return elementAttributes;
var definition = manifest.Placeholders.FirstOrDefault(x => x.Name.Equals(placeholder, StringComparison.Ordinal));
if (definition is null || (!definition.Bold && !definition.Italic && !definition.Underline)) return elementAttributes;
var result = new Dictionary<string, string>(elementAttributes, StringComparer.OrdinalIgnoreCase);
if (definition.Bold) result["bold"] = "true";
if (definition.Italic) result["italic"] = "true";
if (definition.Underline) result["underline"] = "true";
return result;
}
private static string ResolveContent(string content, string? placeholder, string? format,
IReadOnlyDictionary<string, PlaceholderValue> values)
{