performPaint method

  1. @override
void performPaint(
  1. Buffer buffer,
  2. Offset offset
)
override

Hook for subclasses to implement actual painting.

Implementation

@override
void performPaint(Buffer buffer, Offset offset) {
  final modal = widget as ModalOverlay;
  final w = size.width;
  final h = size.height;
  if (w <= 0 || h <= 0) return;

  // 1. Check/force focus to modal if it got lost/not set
  bool anyFocused = false;
  for (final node in modal.modalFocusNodes) {
    if (node.isFocused) {
      anyFocused = true;
      break;
    }
  }
  if (!anyFocused && modal.modalFocusNodes.isNotEmpty) {
    modal.modalFocusNodes.first.requestFocus();
  }

  // 2. Draw the background scrim (dimming the cells beneath the modal)
  for (var y = 0; y < h; y++) {
    for (var x = 0; x < w; x++) {
      final targetX = (offset.dx + x).toInt();
      final targetY = (offset.dy + y).toInt();
      final isInsideDialog =
          x >= modal.dialogBounds.x &&
          x < modal.dialogBounds.x + modal.dialogBounds.width &&
          y >= modal.dialogBounds.y &&
          y < modal.dialogBounds.y + modal.dialogBounds.height;
      if (!isInsideDialog) {
        final modifiers = buffer.getModifiers(targetX, targetY);
        buffer.setAttributes(
          targetX,
          targetY,
          modifiers: modifiers | Modifier.dim,
        );
      }
    }
  }

  // 3. Draw the central dialog box inside dialogBounds
  final dialogX = offset.dx + modal.dialogBounds.x;
  final dialogY = offset.dy + modal.dialogBounds.y;
  final dw = modal.dialogBounds.width;
  final dh = modal.dialogBounds.height;

  if (dw < 2 || dh < 2) {
    for (var y = 0; y < dh; y++) {
      for (var x = 0; x < dw; x++) {
        buffer.setAttributes(
          (dialogX + x).toInt(),
          (dialogY + y).toInt(),
          char: ' ',
          fg: modal.borderStyle.foreground?.argb,
          bg: modal.borderStyle.background?.argb,
          modifiers: modal.borderStyle.modifiers,
        );
      }
    }
    return;
  }

  // Draw top border
  final topBorder =
      modal.borderChars[0] +
      modal.borderChars[1] * (dw - 2) +
      modal.borderChars[2];
  buffer.writeString(dialogX, dialogY, topBorder, modal.borderStyle);

  // Overlay title
  if (modal.title.isNotEmpty) {
    final titleChars = modal.title.characters;
    final maxTitleLen = dw - 4;
    String displayedTitle;
    if (titleChars.length > maxTitleLen) {
      final cutLen = dw - 7;
      if (cutLen > 0) {
        displayedTitle = ' ${titleChars.take(cutLen).toString()}... ';
      } else {
        displayedTitle = '';
      }
    } else {
      displayedTitle = ' ${modal.title} ';
    }

    if (displayedTitle.isNotEmpty) {
      final dispChars = displayedTitle.characters;
      final titleX = max(
        1,
        min(dw - 2, ((dw - dispChars.length) / 2).floor()),
      );
      buffer.writeString(
        dialogX + titleX,
        dialogY,
        displayedTitle,
        modal.titleStyle,
      );
    }
  }

  // Side borders
  for (var y = 1; y < dh - 1; y++) {
    buffer.writeString(
      dialogX,
      dialogY + y,
      modal.borderChars[3],
      modal.borderStyle,
    );
    buffer.writeString(
      dialogX + dw - 1,
      dialogY + y,
      modal.borderChars[5],
      modal.borderStyle,
    );
  }

  // Bottom border
  final bottomBorder =
      modal.borderChars[6] +
      modal.borderChars[7] * (dw - 2) +
      modal.borderChars[8];
  buffer.writeString(
    dialogX,
    dialogY + dh - 1,
    bottomBorder,
    modal.borderStyle,
  );

  // Render child content viewport
  final contentArea = Rect(dialogX + 1, dialogY + 1, dw - 2, dh - 2);
  final contentViewport = Viewport(buffer, contentArea);
  contentViewport.fillAttributes(
    char: ' ',
    fg: modal.backgroundStyle.foreground?.argb,
    bg: modal.backgroundStyle.background?.argb,
    modifiers: modal.backgroundStyle.modifiers,
  );

  if (childElement != null) {
    childElement!.paint(contentViewport, Offset.zero);
  }
}