TextNode
Renders styled text with optional word wrapping, colors, and formatting.
Basic Usage
csharp
new TextNode("Hello, World!")Styling
csharp
new TextNode("Styled text")
.WithForeground(Color.Cyan)
.WithBackground(Color.Blue)
.Bold()
.Italic()
.Underline();Text Alignment
Control horizontal text alignment within the available space:
csharp
// Left aligned (default)
new TextNode("Left aligned")
// Center aligned
new TextNode("Centered text").AlignCenter()
// Right aligned
new TextNode("Right aligned").AlignRight()
// Or use the general method
new TextNode("Aligned text").Align(TextAlignment.Center)Alignment works with both single-line and multi-line text. Each line is aligned independently.
Word Wrapping
Word wrapping is enabled by default. Text will wrap at word boundaries when it exceeds the available width.
csharp
// Word wrap enabled (default)
new TextNode("This long text will wrap to multiple lines when needed")
// Disable word wrap (truncate instead)
new TextNode("This text will be truncated if too long")
.NoWrap();Multi-line Text
TextNode supports newlines in content:
csharp
new TextNode("Line 1\nLine 2\nLine 3")Size Constraints
TextNode defaults to HeightConstraint = Auto and WidthConstraint = Fill:
csharp
// Default: fills width, auto height based on content
new TextNode("Content")
// Fixed height (may clip content)
new TextNode("Content").Height(3)
// Fixed width (triggers wrapping or truncation)
new TextNode("Content").Width(40)API Reference
Constructor
csharp
public TextNode(string content)Properties
| Property | Type | Default | Description |
|---|---|---|---|
Content | string | - | The text content |
Foreground | Color? | null | Foreground color |
Background | Color? | null | Background color |
IsBold | bool | false | Whether text is bold |
IsItalic | bool | false | Whether text is italic |
IsUnderline | bool | false | Whether text is underlined |
WordWrap | bool | true | Whether to wrap text |
Alignment | TextAlignment | Left | Horizontal text alignment |
Fluent Methods
| Method | Description |
|---|---|
.WithForeground(Color) | Set foreground color |
.WithBackground(Color) | Set background color |
.Bold() | Make text bold |
.Italic() | Make text italic |
.Underline() | Make text underlined |
.NoWrap() | Disable word wrapping |
.Align(TextAlignment) | Set horizontal alignment |
.AlignCenter() | Center text horizontally |
.AlignRight() | Right-align text |
Source Code
View TextNode implementation
csharp
// Copyright (c) Petabridge, LLC. All rights reserved.
// Licensed under the Apache 2.0 license. See LICENSE file in the project root for full license information.
using Termina.Components.Streaming;
using Termina.Rendering;
using Termina.Terminal;
namespace Termina.Layout;
/// <summary>
/// Horizontal text alignment within a layout node.
/// </summary>
public enum TextAlignment
{
/// <summary>
/// Align text to the left edge (default).
/// </summary>
Left,
/// <summary>
/// Center text horizontally.
/// </summary>
Center,
/// <summary>
/// Align text to the right edge.
/// </summary>
Right
}
/// <summary>
/// A layout node that renders text.
/// </summary>
public sealed class TextNode : LayoutNode
{
private readonly string[] _lines;
/// <summary>
/// The text content.
/// </summary>
public string Content { get; }
/// <summary>
/// Foreground color.
/// </summary>
public Color? Foreground { get; private set; }
/// <summary>
/// Background color.
/// </summary>
public Color? Background { get; private set; }
/// <summary>
/// Whether text is bold.
/// </summary>
public bool IsBold { get; private set; }
/// <summary>
/// Whether text is italic.
/// </summary>
public bool IsItalic { get; private set; }
/// <summary>
/// Whether text is underlined.
/// </summary>
public bool IsUnderline { get; private set; }
/// <summary>
/// Whether text should wrap to multiple lines when it exceeds the available width.
/// Default is true.
/// </summary>
public bool WordWrap { get; private set; } = true;
/// <summary>
/// Horizontal text alignment. Default is Left.
/// </summary>
public TextAlignment Alignment { get; private set; } = TextAlignment.Left;
public TextNode(string content)
{
Content = content ?? "";
_lines = Content.Split('\n');
// Default to auto height based on content
HeightConstraint = new SizeConstraint.Auto();
WidthConstraint = new SizeConstraint.Fill();
}
/// <summary>
/// Set foreground color.
/// </summary>
public TextNode WithForeground(Color color)
{
Foreground = color;
return this;
}
/// <summary>
/// Set background color.
/// </summary>
public TextNode WithBackground(Color color)
{
Background = color;
return this;
}
/// <summary>
/// Make text bold.
/// </summary>
public TextNode Bold()
{
IsBold = true;
return this;
}
/// <summary>
/// Make text italic.
/// </summary>
public TextNode Italic()
{
IsItalic = true;
return this;
}
/// <summary>
/// Make text underlined.
/// </summary>
public TextNode Underline()
{
IsUnderline = true;
return this;
}
/// <summary>
/// Disable word wrapping (text will be truncated instead of wrapped).
/// </summary>
public TextNode NoWrap()
{
WordWrap = false;
return this;
}
/// <summary>
/// Set horizontal text alignment.
/// </summary>
public TextNode Align(TextAlignment alignment)
{
Alignment = alignment;
return this;
}
/// <summary>
/// Center text horizontally.
/// </summary>
public TextNode AlignCenter()
{
Alignment = TextAlignment.Center;
return this;
}
/// <summary>
/// Align text to the right.
/// </summary>
public TextNode AlignRight()
{
Alignment = TextAlignment.Right;
return this;
}
/// <inheritdoc />
public override Size Measure(Size available)
{
var maxLineWidth = _lines.Max(l => l.Length);
var width = WidthConstraint.Compute(available.Width, maxLineWidth, available.Width);
// Calculate height based on whether word wrap is enabled
int height;
if (WordWrap && width > 0)
{
// Calculate total wrapped line count
height = WordWrapper.CalculateTotalWrappedLineCount(_lines, width);
}
else
{
height = _lines.Length;
}
var measuredHeight = HeightConstraint.Compute(available.Height, height, available.Height);
return new Size(width, measuredHeight);
}
/// <inheritdoc />
public override void Render(IRenderContext context, Rect bounds)
{
if (!bounds.HasArea)
return;
// Create a sub-context for this node's bounds so all coordinates are relative
var textContext = context.CreateSubContext(bounds);
// Apply colors
if (Foreground.HasValue)
textContext.SetForeground(Foreground.Value);
if (Background.HasValue)
textContext.SetBackground(Background.Value);
// Get lines to render (wrapped or original)
var linesToRender = WordWrap && bounds.Width > 0
? WordWrapper.WrapLines(_lines, bounds.Width)
: _lines.ToList();
// Render each line
for (var i = 0; i < linesToRender.Count && i < bounds.Height; i++)
{
var line = linesToRender[i];
// Truncate if still too long (shouldn't happen with wrapping, but safety check)
var displayLine = line.Length > bounds.Width
? line[..bounds.Width]
: line;
// Calculate horizontal offset based on alignment
var x = Alignment switch
{
TextAlignment.Center => Math.Max(0, (bounds.Width - displayLine.Length) / 2),
TextAlignment.Right => Math.Max(0, bounds.Width - displayLine.Length),
_ => 0 // Left alignment
};
textContext.WriteAt(x, i, displayLine);
}
// Reset colors
if (Foreground.HasValue || Background.HasValue)
textContext.ResetColors();
}
}