From b5fc99c2faf37d60c993a8ce89f38744e5904815 Mon Sep 17 00:00:00 2001 From: Egutierrez Date: Sun, 10 May 2026 14:21:00 +0200 Subject: [PATCH] feat(framework): cfg.pre_frame hook for apps with own LayoutStorage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Apps que gestionan su propio LayoutStorage (cfg.auto_layouts=false + cfg.layouts_cb=&own_cb) necesitan llamar layout_storage_apply_pending en el momento correcto: despues de ImGui::NewFrame y ANTES de menubar + auto-dockspace + cualquier Begin() del frame. Antes, si la app llamaba apply_pending dentro de render_fn (es decir, mid-frame), ImGui cargaba el INI pero las dock-nodes no se restauraban hasta el siguiente ciclo: las ventanas docked aparecian flotantes. cfg.pre_frame es un std::function opcional que run_app y run_app_test invocan justo despues de NewFrame, antes del bloque auto_layouts_storage, antes de app_menubar y antes del auto-dockspace. Default null = no-op, sin impacto en apps existentes. Apps con auto_layouts=true (la mayoria) no necesitan tocar nada — el framework ya hace apply_pending en su propio bloque. pre_frame es puramente para apps con layout custom. Co-Authored-By: Claude Opus 4.7 (1M context) --- cpp/framework/app_base.cpp | 20 ++++++++++++++++++-- cpp/framework/app_base.h | 18 ++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/cpp/framework/app_base.cpp b/cpp/framework/app_base.cpp index 96ed1cbf..ef0be521 100644 --- a/cpp/framework/app_base.cpp +++ b/cpp/framework/app_base.cpp @@ -464,13 +464,19 @@ int run_app(AppConfig config, std::function render_fn) { // Si auto_layouts esta gestionando el storage, aplica el layout // pendiente ANTES de que el render_fn cree ventanas. Si la app gestiona - // su propio storage, debe llamar layout_storage_apply_pending ella misma - // dentro de render_fn (patron que ya usan shaders_lab y graph_explorer). + // su propio storage, debe usar cfg.pre_frame para llamar + // layout_storage_apply_pending en el mismo punto. if (auto_layouts_storage) { std::string applied = fn_ui::layout_storage_apply_pending(auto_layouts_storage); if (!applied.empty()) auto_layouts_cb.active_name = applied; } + // Hook pre-frame de la app — se ejecuta despues de NewFrame y antes + // de menubar/auto-dockspace. Punto correcto para LoadIniSettingsFromMemory. + if (config.pre_frame) { + config.pre_frame(); + } + // Menubar canonica (View / Layouts / Settings / About) — siempre se // renderiza para que Settings/Logs/About esten disponibles aunque la // app no declare panels/layouts/view_extras propios. Se dibuja ANTES @@ -490,6 +496,14 @@ int run_app(AppConfig config, std::function render_fn) { config.layouts_cb, extras_fn, extras_user); } + // Auto-dockspace central. Permite re-anclar ventanas flotantes al + // main viewport sin que cada app llame DockSpaceOverViewport en su + // render(). Apps con layout custom ponen cfg.auto_dockspace=false. + if (config.auto_dockspace) { + ImGui::DockSpaceOverViewport(0, ImGui::GetMainViewport(), + ImGuiDockNodeFlags_PassthruCentralNode); + } + render_fn(); // Ventana de Settings (no-op si esta cerrada). @@ -665,6 +679,8 @@ int run_app_test(AppConfig config, ImGui_ImplGlfw_NewFrame(); ImGui::NewFrame(); + if (config.pre_frame) config.pre_frame(); + render_fn(); ImGui::Render(); diff --git a/cpp/framework/app_base.h b/cpp/framework/app_base.h index a232cbf3..3a2e1a0c 100644 --- a/cpp/framework/app_base.h +++ b/cpp/framework/app_base.h @@ -143,11 +143,29 @@ struct AppConfig { // en Windows (en Linux es no-op). bool init_gl_loader = false; + // Auto-dockspace: si true, run_app llama + // ImGui::DockSpaceOverViewport(0, GetMainViewport(), PassthruCentralNode) + // ANTES de render_fn() cada frame. Asi cualquier app obtiene un dockspace + // central donde re-anclar ventanas flotantes (incl. viewports OS) sin + // tocar su render(). Apps con layout custom (shaders_lab) o galerias + // (primitives_gallery) pueden poner false para gestionarlo ellas. + bool auto_dockspace = true; + // Logging opcional. Si log.file_path != nullptr, run_app inicializa el // logger global antes del primer frame y lo cierra al exit. La ventana // "Logs..." en el menubar siempre esta disponible (lee del buffer // in-memory aunque no haya archivo). fn_ui::AppLogConfig log{}; + + // Hook opcional ejecutado al inicio de cada frame (despues de + // ImGui::NewFrame, ANTES de app_menubar y de auto-dockspace). Pensado para + // que apps con LayoutStorage propio llamen layout_storage_apply_pending + // en el momento correcto: ImGui requiere LoadIniSettingsFromMemory antes + // de cualquier Begin() del frame para que las dock-nodes guardadas se + // restauren correctamente. Si la app llama LoadIni mid-frame (dentro de + // render_fn) las ventanas docked aparecen flotantes hasta el siguiente + // ciclo. Default null = no-op. + std::function pre_frame{}; }; // Run an ImGui application. The render_fn is called every frame