renderContainerContent function

String renderContainerContent({
  1. required String contentStr,
  2. EdgeInsets? padding,
  3. EdgeInsets? margin,
  4. num? width,
  5. num? height,
  6. Color? background,
  7. Color? foreground,
  8. Color? color,
  9. Decoration? decoration,
  10. Decoration? foregroundDecoration,
  11. Alignment? alignment,
  12. HorizontalAlign align = HorizontalAlign.left,
  13. VerticalAlign verticalAlign = VerticalAlign.top,
})

Implementation

String renderContainerContent({
  required String contentStr,
  EdgeInsets? padding,
  EdgeInsets? margin,
  num? width,
  num? height,
  Color? background,
  Color? foreground,
  Color? color,
  Decoration? decoration,
  Decoration? foregroundDecoration,
  Alignment? alignment,
  HorizontalAlign align = HorizontalAlign.left,
  VerticalAlign verticalAlign = VerticalAlign.top,
}) {
  final span = TuiTrace.begin(
    'renderContainerContent',
    tag: TraceTag.paint,
    extra: 'w=$width h=$height',
  );
  final contentWidth = Layout.getWidth(contentStr);
  final contentHeight = Layout.getHeight(contentStr);

  // Extract BoxDecoration fields.
  final boxDecoration = decoration is BoxDecoration ? decoration : null;
  final border = boxDecoration?.border;
  final borderRadius = boxDecoration?.borderRadius;
  final gradient = boxDecoration?.gradient;

  // Compute border edge sizes (each edge consumes cells).
  final borderLeft = border != null && border.isVisible
      ? border.getLeftSize()
      : 0;
  final borderRight = border != null && border.isVisible
      ? border.getRightSize()
      : 0;
  final borderTop = border != null && border.isVisible
      ? border.getTopSize()
      : 0;
  final borderBottom = border != null && border.isVisible
      ? border.getBottomSize()
      : 0;
  final borderH = borderLeft + borderRight;
  final borderV = borderTop + borderBottom;
  final hasBorder = border != null && border.isVisible;
  final hasGradient = gradient != null && gradient.colors.length >= 2;

  final padLeft = roundClamp(padding?.left ?? 0);
  final padRight = roundClamp(padding?.right ?? 0);
  final padTop = roundClamp(padding?.top ?? 0);
  final padBottom = roundClamp(padding?.bottom ?? 0);

  final marginLeft = roundClamp(margin?.left ?? 0);
  final marginRight = roundClamp(margin?.right ?? 0);
  final marginTop = roundClamp(margin?.top ?? 0);
  final marginBottom = roundClamp(margin?.bottom ?? 0);

  // Inner width/height includes border + padding + content.
  final paddedWidth = contentWidth + padLeft + padRight + borderH;
  final paddedHeight = contentHeight + padTop + padBottom + borderV;

  final resolvedWidth = resolveDimension(width);
  final resolvedHeight = resolveDimension(height);
  final innerWidth = resolvedWidth ?? paddedWidth;
  final innerHeight = resolvedHeight ?? paddedHeight;

  final targetWidth = innerWidth + marginLeft + marginRight;
  final targetHeight = innerHeight + marginTop + marginBottom;

  if (targetWidth == 0 || targetHeight == 0) {
    span.end(extra: 'empty');
    return '';
  }

  final bgColor = colorToUvColor(color ?? decoration?.color ?? background);
  final fgColor = colorToUvColor(foregroundDecoration?.color ?? foreground);
  final bgStyle = UvStyle(bg: bgColor, fg: fgColor);
  final hasVisualStyle =
      bgColor != null || fgColor != null || hasBorder || hasGradient;
  if (!hasVisualStyle &&
      padLeft == 0 &&
      padRight == 0 &&
      padTop == 0 &&
      padBottom == 0 &&
      marginLeft == 0 &&
      marginRight == 0 &&
      marginTop == 0 &&
      marginBottom == 0 &&
      targetWidth == contentWidth &&
      targetHeight == contentHeight) {
    span.end(extra: 'passthrough size=${targetWidth}x$targetHeight');
    return contentStr;
  }

  // Compute alignment and content offset (inside border + padding).
  final resolvedAlign = alignment == null
      ? align
      : horizontalFromAlignment(alignment);
  final resolvedVertical = alignment == null
      ? verticalAlign
      : verticalFromAlignment(alignment);

  final availableWidth = math.max(0, innerWidth - padLeft - padRight - borderH);
  final availableHeight = math.max(
    0,
    innerHeight - padTop - padBottom - borderV,
  );

  final alignedX = resolvedWidth != null
      ? offsetForHorizontal(resolvedAlign, availableWidth, contentWidth)
      : 0;
  final alignedY = resolvedHeight != null
      ? offsetForVertical(resolvedVertical, availableHeight, contentHeight)
      : 0;

  final offsetX = marginLeft + borderLeft + padLeft + alignedX;
  final offsetY = marginTop + borderTop + padTop + alignedY;

  if (!hasVisualStyle &&
      offsetX >= 0 &&
      offsetY >= 0 &&
      !needsPlainContainerCanvasComposition(contentStr)) {
    final result = renderPlainContainerContent(
      content: contentStr,
      contentHeight: contentHeight,
      targetWidth: targetWidth,
      targetHeight: targetHeight,
      marginLeft: marginLeft,
      marginTop: marginTop,
      padLeft: padLeft,
      padTop: padTop,
      alignedX: alignedX,
      alignedY: alignedY,
    );
    span.end(extra: 'plain size=${targetWidth}x$targetHeight');
    return result;
  }

  final canvas = Canvas(targetWidth, targetHeight);

  // Fill inner area (inside margin, including border) with background cells.
  if (!bgStyle.isZero) {
    final bgCell = Cell(content: ' ', width: 1, style: bgStyle);
    for (var y = marginTop; y < marginTop + innerHeight; y++) {
      for (var x = marginLeft; x < marginLeft + innerWidth; x++) {
        canvas.setCell(x, y, bgCell.clone());
      }
    }
  }

  // Apply gradient to background cells (inside border, row-by-row).
  if (hasGradient) {
    final gradientAreaTop = marginTop + borderTop;
    final gradientAreaHeight = math.max(0, innerHeight - borderV);
    if (gradientAreaHeight > 0) {
      final gradientColors = blend1D(
        gradientAreaHeight,
        gradient.colors,
        hasDarkBackground: hasDarkBackground,
      );
      for (var row = 0; row < gradientAreaHeight; row++) {
        final rowColor = colorToUvColor(gradientColors[row]);
        final rowStyle = UvStyle(bg: rowColor, fg: fgColor);
        final rowCell = Cell(content: ' ', width: 1, style: rowStyle);
        final y = gradientAreaTop + row;
        for (
          var x = marginLeft + borderLeft;
          x < marginLeft + innerWidth - borderRight;
          x++
        ) {
          canvas.setCell(x, y, rowCell.clone());
        }
      }
    }
  }

  // Draw content.
  if (contentStr.isNotEmpty) {
    // Use the gradient-aware bg style for the content area: if gradient is
    // active, the row-specific bg is already on the canvas cells and
    // _drawStyledContent merges bg from bgStyle only when the source cell
    // has no bg. We pass the base bgStyle here; for gradient containers the
    // canvas already has per-row bg so the merge is harmless.
    drawStyledContent(
      canvas,
      contentStr,
      offsetX,
      offsetY,
      bgStyle,
      contentWidth: contentWidth,
      contentHeight: contentHeight,
    );
  }

  // Draw border characters onto the canvas.
  if (hasBorder) {
    final bx = marginLeft; // border area origin x
    final by = marginTop; // border area origin y
    final bw = innerWidth; // border area width
    final bh = innerHeight; // border area height

    // Resolve corner characters (apply borderRadius).
    var cornerTL = border.topLeft;
    var cornerTR = border.topRight;
    var cornerBL = border.bottomLeft;
    var cornerBR = border.bottomRight;
    if (borderRadius != null) {
      if (borderRadius.topLeft > 0) cornerTL = '╭';
      if (borderRadius.topRight > 0) cornerTR = '╮';
      if (borderRadius.bottomLeft > 0) cornerBL = '╰';
      if (borderRadius.bottomRight > 0) cornerBR = '╯';
    }

    final borderStyle = UvStyle(bg: bgColor, fg: fgColor);

    // Top-left corner.
    if (cornerTL.isNotEmpty) {
      canvas.setCell(
        bx,
        by,
        Cell(content: cornerTL, width: 1, style: borderStyle),
      );
    }
    // Top-right corner.
    if (cornerTR.isNotEmpty) {
      canvas.setCell(
        bx + bw - 1,
        by,
        Cell(content: cornerTR, width: 1, style: borderStyle),
      );
    }
    // Bottom-left corner.
    if (cornerBL.isNotEmpty) {
      canvas.setCell(
        bx,
        by + bh - 1,
        Cell(content: cornerBL, width: 1, style: borderStyle),
      );
    }
    // Bottom-right corner.
    if (cornerBR.isNotEmpty) {
      canvas.setCell(
        bx + bw - 1,
        by + bh - 1,
        Cell(content: cornerBR, width: 1, style: borderStyle),
      );
    }

    // Top edge.
    if (border.top.isNotEmpty) {
      for (var x = bx + borderLeft; x < bx + bw - borderRight; x++) {
        canvas.setCell(
          x,
          by,
          Cell(content: border.top, width: 1, style: borderStyle),
        );
      }
    }

    // Bottom edge.
    if (border.bottom.isNotEmpty) {
      for (var x = bx + borderLeft; x < bx + bw - borderRight; x++) {
        canvas.setCell(
          x,
          by + bh - 1,
          Cell(content: border.bottom, width: 1, style: borderStyle),
        );
      }
    }

    // Left edge.
    if (border.left.isNotEmpty) {
      for (var y = by + borderTop; y < by + bh - borderBottom; y++) {
        canvas.setCell(
          bx,
          y,
          Cell(content: border.left, width: 1, style: borderStyle),
        );
      }
    }

    // Right edge.
    if (border.right.isNotEmpty) {
      for (var y = by + borderTop; y < by + bh - borderBottom; y++) {
        canvas.setCell(
          bx + bw - 1,
          y,
          Cell(content: border.right, width: 1, style: borderStyle),
        );
      }
    }
  }

  var result = canvas.render();
  if (resolvedWidth != null ||
      resolvedHeight != null ||
      marginLeft > 0 ||
      marginRight > 0 ||
      marginTop > 0 ||
      marginBottom > 0) {
    result = padToWidth(result, targetWidth, targetHeight);
  }
  span.end(extra: 'size=${targetWidth}x$targetHeight');
  return result;
}