Skip to content
Draft
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
92 changes: 92 additions & 0 deletions Terminal.Gui/Text/FormattedText.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Drawing;

namespace Terminal.Gui.Text;

/// <summary>
/// Represents the result of text formatting, containing formatted lines, size requirements, and metadata.
/// </summary>
public sealed class FormattedText
{
/// <summary>
/// Initializes a new instance of the <see cref="FormattedText"/> class.
/// </summary>
/// <param name="lines">The formatted text lines.</param>
/// <param name="requiredSize">The size required to display the text.</param>
/// <param name="hotKey">The HotKey found in the text, if any.</param>
/// <param name="hotKeyPosition">The position of the HotKey in the original text.</param>
public FormattedText(
IReadOnlyList<FormattedLine> lines,
Size requiredSize,
Key hotKey = default,

Check warning on line 23 in Terminal.Gui/Text/FormattedText.cs

View workflow job for this annotation

GitHub Actions / build / Build Debug & Release

Cannot convert null literal to non-nullable reference type.

Check warning on line 23 in Terminal.Gui/Text/FormattedText.cs

View workflow job for this annotation

GitHub Actions / build / Build Debug & Release

Cannot convert null literal to non-nullable reference type.

Check warning on line 23 in Terminal.Gui/Text/FormattedText.cs

View workflow job for this annotation

GitHub Actions / build / Build Debug & Release

Cannot convert null literal to non-nullable reference type.

Check warning on line 23 in Terminal.Gui/Text/FormattedText.cs

View workflow job for this annotation

GitHub Actions / build / Build Debug & Release

Cannot convert null literal to non-nullable reference type.

Check warning on line 23 in Terminal.Gui/Text/FormattedText.cs

View workflow job for this annotation

GitHub Actions / Build Debug & Release

Cannot convert null literal to non-nullable reference type.

Check warning on line 23 in Terminal.Gui/Text/FormattedText.cs

View workflow job for this annotation

GitHub Actions / Build Debug & Release

Cannot convert null literal to non-nullable reference type.
int hotKeyPosition = -1)
{
Lines = lines ?? throw new ArgumentNullException(nameof(lines));
RequiredSize = requiredSize;
HotKey = hotKey;
HotKeyPosition = hotKeyPosition;
}

/// <summary>Gets the formatted text lines.</summary>
public IReadOnlyList<FormattedLine> Lines { get; }

/// <summary>Gets the size required to display the formatted text.</summary>
public Size RequiredSize { get; }

/// <summary>Gets the HotKey found in the text, if any.</summary>
public Key HotKey { get; }

/// <summary>Gets the position of the HotKey in the original text (-1 if no HotKey).</summary>
public int HotKeyPosition { get; }

/// <summary>Gets a value indicating whether the text contains a HotKey.</summary>
public bool HasHotKey => HotKeyPosition >= 0;
}

/// <summary>
/// Represents a single formatted line of text.
/// </summary>
public sealed class FormattedLine
{
/// <summary>
/// Initializes a new instance of the <see cref="FormattedLine"/> class.
/// </summary>
/// <param name="runs">The text runs that make up this line.</param>
/// <param name="width">The display width of this line.</param>
public FormattedLine(IReadOnlyList<FormattedRun> runs, int width)
{
Runs = runs;
Width = width;
}

/// <summary>Gets the text runs that make up this line.</summary>
public IReadOnlyList<FormattedRun> Runs { get; }

/// <summary>Gets the display width of this line.</summary>
public int Width { get; }
}

/// <summary>
/// Represents a run of text with consistent formatting.
/// </summary>
public sealed class FormattedRun
{
/// <summary>
/// Initializes a new instance of the <see cref="FormattedRun"/> class.
/// </summary>
/// <param name="text">The text content of this run.</param>
/// <param name="isHotKey">Whether this run represents a HotKey.</param>
public FormattedRun(string text, bool isHotKey = false)
{
Text = text;
IsHotKey = isHotKey;
}

/// <summary>Gets the text content of this run.</summary>
public string Text { get; }

/// <summary>Gets a value indicating whether this run represents a HotKey.</summary>
public bool IsHotKey { get; }
}
52 changes: 52 additions & 0 deletions Terminal.Gui/Text/ITextFormatter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#nullable enable
using System.Drawing;

namespace Terminal.Gui.Text;

/// <summary>
/// Interface for text formatting. Separates formatting concerns from rendering.
/// </summary>
public interface ITextFormatter
{
/// <summary>Gets or sets the text to be formatted.</summary>
string Text { get; set; }

/// <summary>Gets or sets the size constraint for formatting.</summary>
Size? ConstrainToSize { get; set; }

/// <summary>Gets or sets the horizontal text alignment.</summary>
Alignment Alignment { get; set; }

/// <summary>Gets or sets the vertical text alignment.</summary>
Alignment VerticalAlignment { get; set; }

/// <summary>Gets or sets the text direction.</summary>
TextDirection Direction { get; set; }

/// <summary>Gets or sets whether word wrap is enabled.</summary>
bool WordWrap { get; set; }

/// <summary>Gets or sets whether multi-line text is allowed.</summary>
bool MultiLine { get; set; }

/// <summary>Gets or sets the HotKey specifier character.</summary>
Rune HotKeySpecifier { get; set; }

/// <summary>Gets or sets the tab width.</summary>
int TabWidth { get; set; }

/// <summary>Gets or sets whether trailing spaces are preserved in word-wrapped lines.</summary>
bool PreserveTrailingSpaces { get; set; }

/// <summary>
/// Formats the text and returns the formatted result.
/// </summary>
/// <returns>The formatted text result containing lines, size, and metadata.</returns>
FormattedText Format();

/// <summary>
/// Gets the size required to display the formatted text.
/// </summary>
/// <returns>The size required for the formatted text.</returns>
Size GetFormattedSize();
}
40 changes: 40 additions & 0 deletions Terminal.Gui/Text/ITextRenderer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#nullable enable

namespace Terminal.Gui.Text;

/// <summary>
/// Interface for rendering formatted text to the console.
/// </summary>
public interface ITextRenderer
{
/// <summary>
/// Draws the formatted text to the console driver.
/// </summary>
/// <param name="formattedText">The formatted text to draw.</param>
/// <param name="screen">The screen bounds for drawing.</param>
/// <param name="normalColor">The color for normal text.</param>
/// <param name="hotColor">The color for HotKey text.</param>
/// <param name="fillRemaining">Whether to fill remaining area with spaces.</param>
/// <param name="maximum">The maximum container bounds.</param>
/// <param name="driver">The console driver to use for drawing.</param>
void Draw(
FormattedText formattedText,
Rectangle screen,
Attribute normalColor,
Attribute hotColor,
bool fillRemaining = false,
Rectangle maximum = default,
IConsoleDriver? driver = null);

/// <summary>
/// Gets the region that would be drawn by the formatted text.
/// </summary>
/// <param name="formattedText">The formatted text.</param>
/// <param name="screen">The screen bounds.</param>
/// <param name="maximum">The maximum container bounds.</param>
/// <returns>A region representing the areas that would be drawn.</returns>
Region GetDrawRegion(
FormattedText formattedText,
Rectangle screen,
Rectangle maximum = default);
}
43 changes: 43 additions & 0 deletions Terminal.Gui/Text/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Text Formatting in Terminal.Gui

This directory contains text formatting and processing classes for Terminal.Gui.

## Classes

### TextFormatter

The main text formatting class that handles:
- Text alignment (horizontal and vertical)
- Text direction support (left-to-right, right-to-left, top-to-bottom, etc.)
- Word wrapping
- Multi-line text support
- HotKey processing
- Wide character (Unicode) support

**Known Issues**: The current `TextFormatter` implementation has several architectural problems that are planned to be addressed in a future rewrite:

1. **Format/Draw Coupling**: The `Draw()` method does significant formatting work, making `FormatAndGetSize()` unreliable
2. **Performance**: `Format()` is called multiple times during layout operations
3. **Complex Alignment**: Alignment logic is embedded in drawing code instead of using the `Aligner` engine
4. **Poor Extensibility**: Adding new features requires modifying the monolithic class
5. **No Interface**: Prevents multiple text formatter implementations

See [TextFormatter Rewrite Issue](https://github.com/gui-cs/Terminal.Gui/issues/3469) for details.

### Other Classes

- `TextDirection`: Enumeration for text direction support
- `StringExtensions`: Extension methods for string processing
- `RuneExtensions`: Extension methods for Unicode Rune processing
- `NerdFonts`: Support for Nerd Fonts icons

## Future Plans

A complete rewrite of `TextFormatter` is planned that will:
- Separate formatting from rendering concerns
- Provide an interface-based architecture for extensibility
- Improve performance with better caching
- Support multiple text formats (HTML, Attributed Text, etc.)
- Use the `Aligner` engine for proper alignment

This is a major architectural change planned for a future release.
Loading
Loading