run method

void run({
  1. bool fullScreenMode = false,
})

Initializes and runs the app.

  1. Reads terminal size using TerminalFunctions.
  2. Optionally switches the terminal into fullscreen mode if fullScreenMode is true, using the entire screen.
  3. Creates a CanvasBuffer for rendering.
  4. Initializes input handling, focus, and interactivity.
  5. Measures layout and renders the component tree.

The fullScreenMode parameter controls whether the app runs in fullscreen or uses the current terminal viewport.

Implementation

void run({bool fullScreenMode = false}) async {
  final AppInstance appInstance = createInstance() as AppInstance;

  final rawTerminalWidth = TerminalFunctions.hasTerminal
      ? TerminalFunctions.terminalWidth
      : 80;
  final rawTerminalHeight = TerminalFunctions.hasTerminal
      ? TerminalFunctions.terminalHeight
      : 20;

  final LayoutEngine engine = LayoutEngine(
    rootInstance: appInstance,
    children: appInstance.childrenInstance,
    direction: direction,
    bounds: Rect(
      x: 0,
      y: 0,
      width: rawTerminalWidth,
      height: rawTerminalHeight,
    ),
  );

  final layoutHeight = engine.fitHeight();
  final layoutWidth = engine.fitWidth();

  final terminalWidth = min(rawTerminalWidth, layoutWidth);
  final terminalHeight = min(rawTerminalHeight, layoutHeight);

  final buffer = CanvasBuffer(
    width: terminalWidth,
    height: terminalHeight,
    isFullscreen: fullScreenMode,
  );

  final RenderManager renderer = RenderManager(buffer: buffer);
  final Context context = Context();

  final InputDispatcher dispatcher = InputDispatcher(renderer: renderer);
  final InputManager inputManager = InputManager(dispatcher: dispatcher);
  final supportsCursor = await inputManager.isCursorSupported();
  if (supportsCursor) {
    Logger.trace(App._tag, "Supports cursor fetching cursor position");
    final point = await inputManager.fetchCursorPosition();
    buffer.setTerminalOffset(point.x + 1, point.y + 1);
    context.setInitialCursorPosition(point.x, point.y);

    inputManager.returnedCursorPositionX = point.x + 1;
    inputManager.returnedCursorPositionY = point.y + terminalHeight + 2;
  } else {
    Logger.warn(
      App._tag,
      "Terminal doesn't support cursor. Ignore if this is a test environment",
    );
  }

  final FocusManager focusManager = FocusManager(context: context);
  final ComponentInputHandler componentInputHandler = ComponentInputHandler(
    focusManager,
  );

  final InteractableRegistry registry = InteractableRegistry();

  registry.registerInteractables(appInstance, focusManager, renderer);

  final List<InputHandler> handlers = [
    focusManager,
    CommandModeHandler(),
    componentInputHandler,
  ];

  for (var handler in handlers) {
    dispatcher.registerHandler(handler);
  }

  for (final handler in InputRegistry.handlers) {
    Logger.trace(App._tag, 'Registering handler $handler');
    dispatcher.registerHandler(handler);
  }

  final measuredSize = appInstance.measure(
    Size(width: rawTerminalWidth, height: rawTerminalHeight),
  );

  final bounds = Rect(
    x: 0,
    y: 0,
    width: measuredSize.width,
    height: measuredSize.height,
  );

  Logger.trace(App._tag, 'Writing Components to Canvas Buffer');
  appInstance.render(buffer, bounds);
  Logger.trace(App._tag, 'READY');
  buffer.render();

  Timer.periodic(Duration(milliseconds: 16), (timer) {
    if (appInstance.shouldRebuild || renderer.needsRecompute) {
      appInstance.shouldRebuild = false;
      appInstance._childrenInstance =
          appInstance.initialChildren; // reassign to reset any tree state

      buffer.clear();

      focusManager.reset();
      registry.registerInteractables(appInstance, focusManager, renderer);

      final engine = LayoutEngine(
        rootInstance: appInstance,
        children: appInstance.childrenInstance,
        direction: direction,
        bounds: Rect(x: 0, y: 0, width: bounds.width, height: bounds.height),
      );

      final int termWidth = min(engine.fitWidth(), rawTerminalWidth);
      final int termHeight = min(engine.fitHeight(), rawTerminalHeight);

      var (x, y) = buffer.getTerminalOffset();

      inputManager.returnedCursorPositionX = x + 1;
      inputManager.returnedCursorPositionY = y + termWidth + 2;

      buffer.updateDimensions(termWidth, termHeight);

      final items = engine.compute(
        Size(width: bounds.width, height: bounds.height),
      );

      renderer.requestRecompute(bounds);
      renderer.needsRecompute = false;
      appInstance._renderPositionedComponents(buffer, items);
      buffer.render();
    }
  });
}