| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186 |
- #nullable enable
- using System;
- using System.Collections.Generic;
- using System.Linq;
- namespace Terminal.Gui.Text;
- /// <summary>
- /// Standard implementation of <see cref="ITextRenderer"/> that renders formatted text to the console.
- /// </summary>
- public class StandardTextRenderer : ITextRenderer
- {
- /// <inheritdoc />
- 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++;
- }
- }
- /// <inheritdoc />
- 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)
- );
- }
- }
|