Skip to content

feat: Add a callback style reflect decorator#1263

Open
kghost wants to merge 2 commits into
ArthurSonzogni:mainfrom
kghost:main
Open

feat: Add a callback style reflect decorator#1263
kghost wants to merge 2 commits into
ArthurSonzogni:mainfrom
kghost:main

Conversation

@kghost
Copy link
Copy Markdown
Contributor

@kghost kghost commented May 10, 2026

This pull request adds a callback-style reflect decorator to FTXUI.

The reflect decorator force user to update the layout at next epoch,
this may cause problem if the user want to update the layout immediately.
This change adds a callback style reflect decorator, which will call the
callback immediately with the layout information. But here comes a risk
that it may or may not cause infinite update and render loop.

A better solution is to defer the callback after the render process, I
am not sure how to achieve it yet, leave it for future discussion.

The reflect decorator force user to update the layout at next epoch,
this may cause problem if the user want to update the layout immediately.
This change adds a callback style reflect decorator, which will call the
callback immediately with the layout information. But here comes a risk
that it may or may not cause infinite update and render loop.
@ArthurSonzogni
Copy link
Copy Markdown
Owner

Thanks!

A better solution is to defer the callback after the render process, I
am not sure how to achieve it yet, leave it for future discussion.

What about:

App::PostEventOrExecute(callback);

It will post the callback in the task queue to be executed after the current task.

@kghost
Copy link
Copy Markdown
Contributor Author

kghost commented May 22, 2026

Why App::PostEventOrExecute is not feasible for reflect

While using App::PostEventOrExecute(callback) seems like a direct way to defer execution, it introduces two major issues:

1. Circular Library Dependency (Compile/Link Error)

FTXUI is structured as separate modules with a strict hierarchical dependency:

[ Screen ] <--- [ DOM ] <--- [ Component ]
  • The dom library (where reflect resides) is a dependency of the component library.
  • App and PostEventOrExecute are located in the component library.

If src/ftxui/dom/reflect.cpp references App, it creates a circular dependency (DOM depends on Component, and Component depends on DOM). This breaks the modular architecture and will cause build/link failures, especially when building FTXUI as shared libraries.

2. Lack of Deferral in Non-Interactive Contexts

If a developer is rendering a static layout (e.g., generating a static image or string using a Screen without running an interactive App), App::Active() returns nullptr.

In this case, App::PostEventOrExecute falls back to immediate execution:

if (auto* app = App::Active()) {
  app->Post(std::move(closure));
} else {
  closure(); // Runs immediately!
}

If the callback is run immediately during the layout pass of a static screen, the deferral is bypassed entirely. This can still trigger immediate state mutations mid-layout, leading to the same infinite loops or inconsistent states.


Recommended Alternative: Screen Post-Render Queue

Adding a post-render callback queue to the Screen class:

  1. Keeps dependencies clean: dom can safely call Screen methods without violating any library boundaries.
  2. Works universally: Deferral is guaranteed to run after the entire render process completes, regardless of whether it is an interactive App or a static Screen.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants