run method

Future<T?> run()

Starts the inline prompt loop and returns a Future containing the final result.

Implementation

Future<T?> run() async {
  final termSize = await terminal.size;
  final width = termSize.x;

  // Autosize the height dynamically if not explicitly specified.
  final computedHeight = height ?? widget.getIntrinsicHeight(width);

  // Create a temporary buffer and inline renderer.
  final buffer = Buffer.blank(width, computedHeight);
  final renderer = Renderer(
    width,
    computedHeight,
    mode: RenderingMode.inline,
  );

  // Mount the widget tree element persistently so states and focus configuration
  // are retained between keyboard event loop iterations.
  final rootElement = widget.createElement()..mount(null);

  void draw() {
    // Rebuild the element tree to consume any new state modifications.
    final el = rootElement;
    if (el is StatefulElement) {
      el.rebuild();
    } else if (el is StatelessElement) {
      el.rebuild();
    } else {
      try {
        (el as dynamic).rebuild();
      } catch (_) {}
    }

    buffer.clear();
    // Render the rebuilt element tree on our double buffer canvas.
    rootElement.render(buffer, Rect(0, 0, width, computedHeight));

    final sb = StringBuffer();
    renderer.render(buffer, sb);
    if (sb.isNotEmpty) {
      terminal.backend.write(sb.toString());
    }
  }

  // Connect the static repainter callback so that inner setState() invocations
  // (e.g. from typing inside a TextField) trigger screen updates.
  State.onNeedRepaint = draw;

  // Initial frame draw
  draw();

  try {
    await for (final event in terminal.events) {
      if (event is term.KeyEvent) {
        // Cleanly catch Ctrl+C to restore cursor and exit
        if (event.key.length == 1 && event.key.codeUnits[0] == 3) {
          terminal.showCursor();
          exit(0);
        }

        var isDone = false;

        // 1. Let custom interceptor handle the key event first
        if (onKeyEvent != null) {
          isDone = onKeyEvent!(event);
        }

        // 2. If not custom-intercepted, route the event down to the focused widgets
        if (!isDone) {
          _routeKeyEvent(rootElement, event);
        }

        // 3. Check if we should auto-complete on Enter
        if (completeOnEnter &&
            (event.key == 'enter' ||
                event.key == '\n' ||
                event.key == '\r')) {
          isDone = true;
        }

        // Force a redraw to reflect any selections or edits.
        draw();

        if (isDone) {
          break;
        }
      }
    }
  } finally {
    // Reset the repaint handler to avoid leaks.
    State.onNeedRepaint = null;
  }

  if (onComplete != null) {
    return onComplete!();
  }
  return null;
}