---
name: app_menubar
kind: component
lang: cpp
domain: core
version: "1.0.0"
purity: pure
signature: "bool fn_ui::app_menubar(const fn_ui::PanelToggle* panels, size_t count, fn_ui::LayoutCallbacks* layouts_cb)"
description: "MainMenuBar ImGui completa con menu View (toggles de paneles) y menu Layouts (guardar/aplicar layouts persistentes). Punto de entrada unificado para la menubar de cualquier app fn_ui."
tags: [imgui, ui, menu, panels, layouts, dockspace, menubar]
uses_functions: ["app_about_cpp_core", "app_settings_cpp_core", "layouts_menu_cpp_core", "panel_menu_cpp_core", "log_window_cpp_core"]
uses_types: []
returns: []
returns_optional: false
error_type: ""
imports: [imgui]
tested: false
tests: []
test_file_path: ""
file_path: "cpp/functions/core/app_menubar.cpp"
framework: imgui
params:
  - name: panels
    desc: "Array de PanelToggle para el menu View. nullptr o count==0 omite el menu View."
  - name: count
    desc: "Numero de elementos en panels."
  - name: layouts_cb
    desc: "Puntero a LayoutCallbacks cableado contra el backend de persistencia (ej. layout_storage_sqlite). nullptr omite el menu Layouts."
output: "true si el usuario togglo algun panel o disparo alguna accion de layouts (apply/save/delete/reset) en este frame."
---

# app_menubar

Combina `panel_menu_items` y `layouts_menu_items` en una sola `MainMenuBar`. Las apps llaman esta unica funcion para obtener menubar con View+Layouts sin boilerplate.

## Ejemplo de uso completo

```cpp
#include "core/app_menubar.h"
#include "core/layout_storage_sqlite.h"
#include <imgui.h>
#include <sqlite3.h>

// Estado de la app
static bool show_code    = true;
static bool show_preview = true;
static std::string g_active_layout;
static std::string g_pending_blob;

// Paneles para el menu View
fn_ui::PanelToggle g_panels[] = {
    { "Code Editor", "Ctrl+1", &show_code    },
    { "Preview",     "Ctrl+2", &show_preview },
};

// Inicializar SQLite + tabla ui_layouts (una vez al arrancar)
sqlite3* db = nullptr;
sqlite3_open("my_app.db", &db);
fn_ui::layout_storage_init(db);

// Construir LayoutCallbacks cableado contra SQLite
fn_ui::LayoutCallbacks g_layout_cb;
g_layout_cb.list = [&]() { return fn_ui::layout_storage_list(db); };
g_layout_cb.on_apply = [&](const std::string& name) {
    std::string blob = fn_ui::layout_storage_load_blob(db, name);
    if (!blob.empty()) { g_pending_blob = blob; g_active_layout = name; }
};
g_layout_cb.on_save = [&](const std::string& name) {
    std::size_t sz = 0;
    const char* ini = ImGui::SaveIniSettingsToMemory(&sz);
    fn_ui::layout_storage_save(db, name, std::string(ini, sz));
    g_active_layout = name;
};
g_layout_cb.on_delete = [&](const std::string& name) {
    fn_ui::layout_storage_delete(db, name);
    if (g_active_layout == name) g_active_layout.clear();
};
g_layout_cb.on_reset = [&]() {
    ImGui::LoadIniSettingsFromMemory("", 0);
    ImGui::MarkIniSettingsDirty();
    g_active_layout.clear();
};

// Loop de render:
while (!glfwWindowShouldClose(window)) {
    // Aplicar layout pendiente al inicio del frame
    if (!g_pending_blob.empty()) {
        ImGui::LoadIniSettingsFromMemory(g_pending_blob.c_str(), g_pending_blob.size());
        g_pending_blob.clear();
    }

    ImGui_ImplOpenGL3_NewFrame();
    ImGui_ImplGlfw_NewFrame();
    ImGui::NewFrame();

    // Actualizar active_name y dibujar menubar
    g_layout_cb.active_name = g_active_layout;
    fn_ui::app_menubar(g_panels, std::size(g_panels), &g_layout_cb);

    ImGui::DockSpaceOverViewport();

    // Renderizar paneles...
    if (show_code && ImGui::Begin("Code Editor", &show_code)) { /* ... */ }
    ImGui::End();

    ImGui::Render();
    // ...
}
```

## Notas

- Ambos menus son opcionales: pasar `nullptr` en `panels`/`layouts_cb` para omitir el correspondiente.
- El orden de menus es fijo: View primero, Layouts segundo. Si se necesita un orden diferente, componer `panel_menu_items` y `layouts_menu_items` manualmente dentro de un `BeginMainMenuBar` propio.
- `g_layout_cb.active_name` debe actualizarse cada frame antes de llamar `app_menubar`.
- `ImGui::LoadIniSettingsFromMemory` debe diferirse al inicio del frame; no llamarlo desde dentro del callback `on_apply`.

## Notas — Settings menu (auto, sesion 2026-04-25)

`app_menubar` ahora siempre añade un tercer item `Settings...` (MenuItem, no submenu) al final, tras `View` y `Layouts`. El click abre la ventana flotante de `app_settings` (Display: toggle FPS overlay; Typography: combo de fuente Karla/Roboto/DroidSans/Cousine/ProggyClean + slider de tamaño 10..32 px; secciones extra registrables por la app via `settings_window_add_section(...)`).

El render efectivo de la ventana ocurre al final del frame en `fn::run_app` via `settings_window_render()` — `app_menubar` solo dispara la apertura. Apps que NO usan `fn::run_app` deben llamar manualmente `settings_window_render()` despues del `render_fn`.

Apps sin paneles ni layouts (gallery, chart_demo, registry_dashboard) usan `fn_ui::app_menubar(nullptr, 0, nullptr)` solo para exponer el item `Settings...` en la menubar.

`uses_functions` ahora incluye `app_settings_cpp_core` (no añadido al frontmatter para evitar re-indexar; documentado aqui).

## Notas — Settings submenu con About (sesion 2026-04-28)

El item plano `Settings...` pasa a ser un `BeginMenu("Settings")` con dos subitems separados por un separator:

- `Settings...` → abre ventana de `app_settings` (Display + Typography + secciones extra).
- `About...` → abre ventana de `app_about_cpp_core` (project + version + description). La info se setea via `fn_ui::about_window_set_info(project, version, description)` desde el init de la app antes de `fn::run_app`.

`fn::run_app` ahora llama tambien `fn_ui::about_window_render()` al final del frame, justo despues de `settings_window_render()`. Apps que no usan `fn::run_app` deben llamar ambos manualmente.

Cambio retro-compatible: las apps que solo invocan `fn_ui::app_menubar(nullptr, 0, nullptr)` no necesitan tocar nada — el menu `Settings` aparece con About vacio (defaults `"fn_registry app"` / sin version) hasta que llamen `about_window_set_info`.

## Notas — Logs item (sesion 2026-05-01)

El submenu `Settings` añade un tercer item `Logs...` entre `Settings...` y `About...` que abre la ventana de `log_window_cpp_core`. La ventana muestra el ring buffer in-memory de `logger_cpp_core` con filtros por nivel + busqueda + autoscroll.

Funciona aunque la app no haya activado el logger en disco: la ventana siempre tiene el buffer in-memory disponible. Para activar tambien la persistencia a archivo, la app declara `AppLogConfig` en `AppConfig.log` (ver `logger_cpp_core`).

`fn::run_app` invoca `fn_ui::log_window_render()` al final del frame, justo despues de `settings_window_render()` y antes de `about_window_render()`.
