---
name: layout_storage_sqlite
kind: function
lang: cpp
domain: core
version: "1.0.0"
purity: impure
signature: "bool fn_ui::layout_storage_init(sqlite3* db); std::vector<std::string> fn_ui::layout_storage_list(sqlite3* db); bool fn_ui::layout_storage_save(sqlite3* db, const std::string& name, const std::string& blob); std::string fn_ui::layout_storage_load_blob(sqlite3* db, const std::string& name); bool fn_ui::layout_storage_delete(sqlite3* db, const std::string& name); bool fn_ui::layout_storage_exists(sqlite3* db, const std::string& name)"
description: "Primitivas CRUD de bajo nivel para persistir layouts de ImGui (blobs INI) en una tabla SQLite 'ui_layouts'. La app construye el LayoutCallbacks de layouts_menu envolviendo estas primitivas junto a ImGui::Save/LoadIniSettingsToMemory."
tags: [imgui, sqlite, layouts, persistence, crud, dockspace]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: "error_go_core"
imports: [sqlite3]
tested: false
tests: []
test_file_path: ""
file_path: "cpp/functions/core/layout_storage_sqlite.cpp"
params:
  - name: db
    desc: "Conexion SQLite abierta. No debe ser nullptr."
  - name: name
    desc: "Nombre del layout (clave primaria en ui_layouts)."
  - name: blob
    desc: "Contenido INI serializado con ImGui::SaveIniSettingsToMemory."
output: "Las funciones bool retornan true en exito, false en error SQLite. load_blob retorna string vacia si el layout no existe o hay error. list retorna vector vacio en error. Ningun error se propaga como excepcion."
---

# layout_storage_sqlite

Seis primitivas CRUD que gestionan la tabla `ui_layouts` en una base de datos SQLite existente. La app es responsable de abrir/cerrar la conexion y de cablear las primitivas con los callbacks de `fn_ui::LayoutCallbacks`.

## Schema de la tabla

```sql
CREATE TABLE IF NOT EXISTS ui_layouts (
    name       TEXT PRIMARY KEY,
    blob       TEXT NOT NULL,
    created_at TEXT NOT NULL,
    updated_at TEXT NOT NULL
);
```

## Upsert con created_at preservado

`layout_storage_save` usa `INSERT ... ON CONFLICT(name) DO UPDATE SET blob=..., updated_at=...`. Esto preserva el `created_at` original en cada sobreescritura.

## Ejemplo de uso completo (cablear con LayoutCallbacks)

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

static sqlite3*    g_db = nullptr;
static std::string g_active_layout;
static std::string g_pending_blob;

void app_init(const std::string& db_path) {
    sqlite3_open(db_path.c_str(), &g_db);
    fn_ui::layout_storage_init(g_db); // crea ui_layouts si no existe
}

fn_ui::LayoutCallbacks make_layout_callbacks() {
    fn_ui::LayoutCallbacks cb;

    cb.list = []() { return fn_ui::layout_storage_list(g_db); };

    cb.on_apply = [](const std::string& name) {
        std::string blob = fn_ui::layout_storage_load_blob(g_db, name);
        if (!blob.empty()) { g_pending_blob = blob; g_active_layout = name; }
    };

    cb.on_save = [](const std::string& name) {
        std::size_t sz = 0;
        const char* ini = ImGui::SaveIniSettingsToMemory(&sz);
        fn_ui::layout_storage_save(g_db, name, std::string(ini, sz));
        g_active_layout = name;
    };

    cb.on_delete = [](const std::string& name) {
        fn_ui::layout_storage_delete(g_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;
}

// Al inicio de cada frame, antes de NewFrame():
if (!g_pending_blob.empty()) {
    ImGui::LoadIniSettingsFromMemory(g_pending_blob.c_str(), g_pending_blob.size());
    g_pending_blob.clear();
}
```

## Notas

- `layout_storage_init` es idempotente: seguro llamarlo multiples veces, incluida cada vez que se abre la app.
- En errores SQLite las funciones devuelven `false` / `""` / vector vacio — no lanzan excepciones.
- La conexion SQLite puede ser compartida con otras tablas de la misma BD (ej. junto a `shaderlab_db` que usa su propia conexion global). Cada app gestiona su conexion.
- `blob` es texto plano (formato INI de ImGui): no se hace compresion ni encoding adicional.
