#nullable enable using System; using System.Collections.Generic; using System.Linq; namespace Terminal.Gui.Text; /// /// Standard implementation of that renders formatted text to the console. /// public class StandardTextRenderer : ITextRenderer { /// public void Draw( FormattedText formattedText, Rectangle screen, Attribute normalColor, Attribute hotColor, bool fillRemaining = false, Rectangle maximum = default, IConsoleDriver? driver = null) { if (driver is null) { driver = Application.Driver; } if (driver is null || formattedText.Lines.Count == 0) { return; } driver.SetAttribute(normalColor); // Calculate effective drawing area Rectangle maxScreen = CalculateMaxScreen(screen, maximum); if (maxScreen.Width == 0 || maxScreen.Height == 0) { return; } // TODO: Implement alignment using the Aligner engine instead of custom logic // For now, use simplified alignment int startY = screen.Y; int lineIndex = 0; foreach (var line in formattedText.Lines) { if (lineIndex >= maxScreen.Height) { break; } int y = startY + lineIndex; if (y >= maxScreen.Bottom || y < maxScreen.Top) { lineIndex++; continue; } int x = screen.X; // Draw each run in the line foreach (var run in line.Runs) { if (string.IsNullOrEmpty(run.Text)) { continue; } // Set appropriate color driver.SetAttribute(run.IsHotKey ? hotColor : normalColor); // Draw the run text driver.Move(x, y); foreach (var rune in run.Text.EnumerateRunes()) { if (x >= maxScreen.Right) { break; } if (x >= maxScreen.Left) { driver.AddRune(rune); } x += Math.Max(rune.GetColumns(), 1); } } // Fill remaining space if requested if (fillRemaining && x < maxScreen.Right) { driver.SetAttribute(normalColor); while (x < maxScreen.Right) { driver.Move(x, y); driver.AddRune(' '); x++; } } lineIndex++; } } /// public Region GetDrawRegion( FormattedText formattedText, Rectangle screen, Rectangle maximum = default) { var region = new Region(); if (formattedText.Lines.Count == 0) { return region; } Rectangle maxScreen = CalculateMaxScreen(screen, maximum); if (maxScreen.Width == 0 || maxScreen.Height == 0) { return region; } int startY = screen.Y; int lineIndex = 0; foreach (var line in formattedText.Lines) { if (lineIndex >= maxScreen.Height) { break; } int y = startY + lineIndex; if (y >= maxScreen.Bottom || y < maxScreen.Top) { lineIndex++; continue; } int x = screen.X; int lineWidth = 0; // Calculate total width of the line foreach (var run in line.Runs) { if (!string.IsNullOrEmpty(run.Text)) { lineWidth += run.Text.GetColumns(); } } if (lineWidth > 0 && x < maxScreen.Right) { int rightBound = Math.Min(x + lineWidth, maxScreen.Right); region.Union(new Rectangle(x, y, rightBound - x, 1)); } lineIndex++; } return region; } private static Rectangle CalculateMaxScreen(Rectangle screen, Rectangle maximum) { if (maximum == default) { return screen; } return new Rectangle( Math.Max(maximum.X, screen.X), Math.Max(maximum.Y, screen.Y), Math.Max(Math.Min(maximum.Width, maximum.Right - screen.Left), 0), Math.Max(Math.Min(maximum.Height, maximum.Bottom - screen.Top), 0) ); } }