---
name: layouts_menu
kind: component
lang: cpp
domain: core
version: "1.0.0"
purity: pure
signature: "bool fn_ui::layouts_menu_items(const char* menu_label, fn_ui::LayoutCallbacks& cb)"
description: "Menu ImGui de gestion de layouts de ventana (guardar, aplicar, borrar, reset). Se dibuja como BeginMenu..EndMenu para componer dentro de una MainMenuBar propia. Toda la persistencia se delega en callbacks."
tags: [imgui, ui, menu, layouts, dockspace, persistence]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: ""
imports: [imgui]
tested: false
tests: []
test_file_path: ""
file_path: "cpp/functions/core/layouts_menu.cpp"
framework: imgui
params:
  - name: menu_label
    desc: "Texto del menu desplegable, p.ej. 'Layouts'."
  - name: cb
    desc: "LayoutCallbacks con los cinco hooks (list, on_apply, on_save, on_delete, on_reset) y el campo active_name para marcar el layout activo. Callbacks nulos se saltan silenciosamente."
output: "true si el usuario disparo alguna accion (aplicar layout, guardar, borrar o reset) en este frame."
notes: "consumido por cpp/apps/shaders_lab/main.cpp"
---

# layouts_menu

Renderiza un menu desplegable `Layouts` dentro de una `MainMenuBar` existente. Toda la logica de persistencia se delega en el struct `LayoutCallbacks` — la funcion solo gestiona la UI.

## Estructura del menu

```
Layouts
  * mi_layout         <- activo (marker "* ")
    otro_layout       <- inactivo (espacio "  ")
  ─────────────────
  Save current as...  <- abre popup con InputText
  Delete >            <- submenu con todos los layouts
  ─────────────────
  Reset to default
```

## Ejemplo de uso completo

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

// Estado de la app
static std::string g_active_layout;
static std::string g_pending_blob;  // diferir LoadIni al inicio del frame

// Construir callbacks cableadas contra SQLite
fn_ui::LayoutCallbacks make_layout_callbacks(sqlite3* db) {
    fn_ui::LayoutCallbacks cb;

    cb.list = [db]() -> std::vector<std::string> {
        return fn_ui::layout_storage_list(db);
    };

    cb.on_apply = [db](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;
        }
    };

    cb.on_save = [db](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;
    };

    cb.on_delete = [db](const std::string& name) {
        fn_ui::layout_storage_delete(db, name);
        if (g_active_layout == name) g_active_layout.clear();
    };

    cb.on_reset = []() {
        ImGui::LoadIniSettingsFromMemory("", 0);
        ImGui::MarkIniSettingsDirty();
        g_active_layout.clear();
    };

    return cb;
}

// Dentro del loop de render (inicio de frame):
if (!g_pending_blob.empty()) {
    ImGui::LoadIniSettingsFromMemory(g_pending_blob.c_str(), g_pending_blob.size());
    g_pending_blob.clear();
}

// Usar dentro de BeginMainMenuBar:
auto cb = make_layout_callbacks(db);
cb.active_name = g_active_layout;

if (ImGui::BeginMainMenuBar()) {
    fn_ui::layouts_menu_items("Layouts", cb);
    ImGui::EndMainMenuBar();
}
```

## Notas

- El popup "Save current as..." usa un buffer estatico de 64 chars. El boton Save esta deshabilitado mientras el nombre este vacio.
- El submenu Delete muestra `(no layouts)` si la lista esta vacia (item deshabilitado).
- `cb.active_name` debe setearse cada frame antes de llamar la funcion; la funcion no guarda estado entre frames.
- `ImGui::LoadIniSettingsFromMemory` debe llamarse al inicio del frame siguiente al `on_apply`, no desde dentro del callback (puede corromper el estado de ImGui si se llama a mitad de un frame de render). Usar un string `g_pending_blob` como se muestra en el ejemplo.
