# cpp/PATTERNS.md — App shell canonico

Patron obligatorio para apps C++ del registry (`cpp/apps/*`, `projects/*/apps/*`).
Cumplir estas reglas garantiza coherencia visual, theming uniforme, About/Settings
funcionando, paneles toggleables y layouts persistentes con cero codigo boilerplate.

## Checklist obligatorio

Antes de mergear una app, verificar uno por uno:

- [ ] **No `glfwInit` directo**. La app SOLO usa `fn::run_app(AppConfig{...}, render_fn)`.
      El framework gestiona GLFW + ImGui + ImPlot + theming + Settings + About + FPS overlay.
- [ ] **About registrado**. La app pasa `AppConfig::about = {name, version, description}`
      o llama explicitamente `fn_ui::about_window_set_info(...)` en su init.
- [ ] **Settings extras** (si aplica). Si la app expone settings propios (toggles,
      sliders, paths…), los registra con `fn_ui::settings_window_add_section("Mi App", cb)`.
- [ ] **Paneles toggleables** (si aplica). Si la app tiene >=1 panel:
      ```cpp
      static constexpr fn_ui::PanelToggle panels[] = {
          {"Inspector", "Ctrl+1", &show_inspector},
          {"Console",   "Ctrl+2", &show_console},
      };
      ```
      Pasarlo a `AppConfig::panels` + `AppConfig::panel_count = 2`.
- [ ] **Layouts persistentes** (si aplica). Si la app guarda layouts:
      implementa `fn_ui::LayoutCallbacks` y pasalas en `AppConfig::layouts_cb`.
- [ ] **GL loader** (si la app usa OpenGL >= 2.0 directamente). Pasar
      `AppConfig::init_gl_loader = true` para que `fn::run_app()` llame
      `fn::gfx::gl_loader_init()` tras crear el contexto.
- [ ] **Tokens en lugar de hex literales**. Usar `fn_tokens::colors`,
      `fn_tokens::spacing`, `fn_tokens::radius`. Nunca `IM_COL32(0x12,0x34,...)`,
      nunca `ImVec4(0.5f, 0.5f, 0.5f, 1.0f)` ad-hoc.
- [ ] **Componentes del registry, no raw ImGui con styling manual**. Evitar
      `ImGui::BeginTable / Selectable / BeginPopupModal / BeginChild` con estilos
      pegados a mano cuando ya existe primitiva:
        - `fn_ui::dashboard_grid` / `fn_ui::dashboard_panel` para layouts grid.
        - `fn_ui::tree_view` / `fn_ui::select` para listas seleccionables.
        - `fn_ui::modal_dialog` para popups modales.
- [ ] **Iconos via `TI_*`** (Tabler). Nunca emojis ni hex UTF-8 inline.
      Ver `cpp/functions/core/icons_tabler.h`.
- [ ] **Build incremental**. La app aparece en `cpp/CMakeLists.txt` con su
      `add_subdirectory(apps/<nombre>)`. Sin warnings nuevos.

## Esqueleto minimo

```cpp
#include "framework/app_base.h"
#include "core/icons_tabler.h"
#include "core/panel_menu.h"
#include "core/app_settings.h"
#include "core/tokens.h"
#include "imgui.h"

namespace {
bool show_inspector = true;
bool show_console   = false;

constexpr fn_ui::PanelToggle k_panels[] = {
    {"Inspector", "Ctrl+1", &show_inspector},
    {"Console",   "Ctrl+2", &show_console},
};
} // namespace

static void render_my_app() {
    ImGui::DockSpaceOverViewport(0, ImGui::GetMainViewport());
    if (show_inspector) {
        ImGui::Begin(TI_INFO_CIRCLE " Inspector", &show_inspector);
        ImGui::TextUnformatted("Inspector contents");
        ImGui::End();
    }
    if (show_console) {
        ImGui::Begin(TI_TERMINAL_2 " Console", &show_console);
        ImGui::TextUnformatted("Console contents");
        ImGui::End();
    }
}

int main() {
    fn::AppConfig cfg;
    cfg.title          = "My App";
    cfg.about          = {"My App", "0.1.0", "Demo de app shell canonica"};
    cfg.panels         = k_panels;
    cfg.panel_count    = sizeof(k_panels) / sizeof(k_panels[0]);
    cfg.init_gl_loader = false; // ponerlo en true si usas OpenGL directo
    return fn::run_app(cfg, render_my_app);
}
```

Con esto la app obtiene gratis: MainMenuBar (View/Settings/About), ventana About,
ventana Settings, FPS overlay configurable, theming `FnDark`, fuentes vectoriales
+ iconos Tabler mergeados, multi-viewport opcional.

## Anti-patrones

| Mal patron                            | Patron correcto                                |
|---------------------------------------|------------------------------------------------|
| `glfwInit()` en `main`                | `fn::run_app()`                                |
| `ImVec4(0.5,0.5,0.5,1)` ad-hoc        | `fn_tokens::colors::text_dim`                  |
| Crear menubar a mano en cada frame    | `AppConfig::panels` + `AppConfig::layouts_cb`  |
| `ImGui::Begin(u8"\xEF\xA0\x83 ...")`  | `ImGui::Begin(TI_HOME " ...")`                 |
| Settings dispersos por la app         | `settings_window_add_section()`                |
| About hardcoded en un `Begin/End`     | `AppConfig::about` o `about_window_set_info()` |
| Llamar `gl*` sin loader en Windows    | `AppConfig::init_gl_loader = true`             |

## Cuando NO usar `fn::run_app`

Solo si la app es:
- un test headless que no necesita ventana (usar `googletest` directo);
- un binario CLI sin UI (no es una "app C++" en este sentido).

En cualquier otro caso, usar `fn::run_app`. Si `AppConfig` no expone algo que
necesitas, **abrir un issue para extender el shell**, no duplicar boilerplate.
