Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion Robust.Client/UserInterface/Controls/RichTextLabel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ public class RichTextLabel : Control
private float _lineHeightScale = 1;
private bool _lineHeightOverride;

[ViewVariables]
public Label.AlignMode Align { get; set; } = Label.AlignMode.Left;

[ViewVariables(VVAccess.ReadWrite)]
public float LineHeightScale
{
Expand Down Expand Up @@ -105,7 +108,7 @@ protected override Vector2 MeasureOverride(Vector2 availableSize)
protected internal override void Draw(DrawingHandleScreen handle)
{
base.Draw(handle);
_entry?.Draw(_tagManager, handle, _getFont(), SizeBox, 0, new MarkupDrawingContext(), UIScale, LineHeightScale);
_entry?.Draw(_tagManager, handle, _getFont(), SizeBox, 0, new MarkupDrawingContext(), UIScale, LineHeightScale, Align);
}

[Pure]
Expand Down
91 changes: 84 additions & 7 deletions Robust.Client/UserInterface/RichTextEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ public RichTextEntry Update(MarkupTagManager tagManager, Font defaultFont, float
foreach (var node in Message)
{
nodeIndex++;
var text = ProcessNode(tagManager, node, context);
var text = ProcessNode(tagManager, node, context, _tagsAllowed);

if (!context.Font.TryPeek(out var font))
font = defaultFont;
Expand Down Expand Up @@ -208,22 +208,28 @@ public readonly void Draw(
float verticalOffset,
MarkupDrawingContext context,
float uiScale,
float lineHeightScale = 1)
float lineHeightScale = 1,
Robust.Client.UserInterface.Controls.Label.AlignMode align = Robust.Client.UserInterface.Controls.Label.AlignMode.Left)
{
context.Clear();
context.Color.Push(_defaultColor);
context.Font.Push(defaultFont);

var lineWidths = align == Robust.Client.UserInterface.Controls.Label.AlignMode.Left
? null
: CalculateLineWidths(in this, tagManager, defaultFont, uiScale);

var globalBreakCounter = 0;
var lineBreakIndex = 0;
var baseLine = drawBox.TopLeft + new Vector2(0, defaultFont.GetAscent(uiScale) + verticalOffset);
var currentLine = 0;
var baseLine = drawBox.TopLeft + new Vector2(GetAlignedOffset(currentLine), defaultFont.GetAscent(uiScale) + verticalOffset);
var controlYAdvance = 0f;

var nodeIndex = -1;
foreach (var node in Message)
{
nodeIndex++;
var text = ProcessNode(tagManager, node, context);
var text = ProcessNode(tagManager, node, context, _tagsAllowed);
if (!context.Color.TryPeek(out var color) || !context.Font.TryPeek(out var font))
{
color = _defaultColor;
Expand All @@ -235,7 +241,8 @@ public readonly void Draw(
if (lineBreakIndex < LineBreaks.Count &&
LineBreaks[lineBreakIndex] == globalBreakCounter)
{
baseLine = new Vector2(drawBox.Left, baseLine.Y + GetLineHeight(font, uiScale, lineHeightScale) + controlYAdvance);
currentLine += 1;
baseLine = new Vector2(drawBox.Left + GetAlignedOffset(currentLine), baseLine.Y + GetLineHeight(font, uiScale, lineHeightScale) + controlYAdvance);
controlYAdvance = 0;
lineBreakIndex += 1;
}
Expand All @@ -260,16 +267,86 @@ public readonly void Draw(
controlYAdvance = Math.Max(0f, (control.DesiredPixelSize.Y - GetLineHeight(font, uiScale, lineHeightScale)) * invertedScale);
baseLine += new Vector2(advanceX, 0);
}

float GetAlignedOffset(int lineIndex)
{
if (lineWidths == null || lineIndex < 0 || lineIndex >= lineWidths.Count)
return 0f;

var remainingWidth = drawBox.Width - lineWidths[lineIndex];
if (remainingWidth <= 0)
return 0f;

return align switch
{
Robust.Client.UserInterface.Controls.Label.AlignMode.Right => remainingWidth,
Robust.Client.UserInterface.Controls.Label.AlignMode.Center or Robust.Client.UserInterface.Controls.Label.AlignMode.Fill => remainingWidth / 2f,
_ => 0f,
};
}
}

private static List<int> CalculateLineWidths(in RichTextEntry entry, MarkupTagManager tagManager, Font defaultFont, float uiScale)
{
var widths = new List<int> { 0 };
var context = new MarkupDrawingContext();
context.Color.Push(entry._defaultColor);
context.Font.Push(defaultFont);

var globalBreakCounter = 0;
var lineBreakIndex = 0;
var currentLine = 0;
var nodeIndex = -1;

foreach (var node in entry.Message)
{
nodeIndex++;
var text = ProcessNode(tagManager, node, context, entry._tagsAllowed);
if (!context.Font.TryPeek(out var font))
font = defaultFont;

foreach (var rune in text.EnumerateRunes())
{
if (lineBreakIndex < entry.LineBreaks.Count &&
entry.LineBreaks[lineBreakIndex] == globalBreakCounter)
{
currentLine += 1;
widths.Add(0);
lineBreakIndex += 1;
}

if (font.TryGetCharMetrics(rune, uiScale, out var metrics))
widths[currentLine] += metrics.Advance;

globalBreakCounter += 1;
}

if (entry.Controls == null || !entry.Controls.TryGetValue(nodeIndex, out var control))
continue;

if (lineBreakIndex < entry.LineBreaks.Count &&
entry.LineBreaks[lineBreakIndex] == globalBreakCounter)
{
currentLine += 1;
widths.Add(0);
lineBreakIndex += 1;
}

control.Measure(new Vector2(entry.Width, entry.Height));
widths[currentLine] += control.DesiredPixelSize.X;
}

return widths;
}

private readonly string ProcessNode(MarkupTagManager tagManager, MarkupNode node, MarkupDrawingContext context)
private static string ProcessNode(MarkupTagManager tagManager, MarkupNode node, MarkupDrawingContext context, Type[]? tagsAllowed)
{
// If a nodes name is null it's a text node.
if (node.Name == null)
return node.Value.StringValue ?? "";

//Skip the node if there is no markup tag for it.
if (!tagManager.TryGetMarkupTagHandler(node.Name, _tagsAllowed, out var tag))
if (!tagManager.TryGetMarkupTagHandler(node.Name, tagsAllowed, out var tag))
return "";

if (!node.Closing)
Expand Down
Loading