5.0 KiB
5.0 KiB
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
glfwInitdirecto. La app SOLO usafn::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 explicitamentefn_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 aAppConfig::panels+AppConfig::panel_count = 2. - Layouts persistentes (si aplica). Si la app guarda layouts:
implementa
fn_ui::LayoutCallbacksy pasalas enAppConfig::layouts_cb. - GL loader (si la app usa OpenGL >= 2.0 directamente). Pasar
AppConfig::init_gl_loader = truepara quefn::run_app()llamefn::gfx::gl_loader_init()tras crear el contexto. - Tokens en lugar de hex literales. Usar
fn_tokens::colors,fn_tokens::spacing,fn_tokens::radius. NuncaIM_COL32(0x12,0x34,...), nuncaImVec4(0.5f, 0.5f, 0.5f, 1.0f)ad-hoc. - Componentes del registry, no raw ImGui con styling manual. Evitar
ImGui::BeginTable / Selectable / BeginPopupModal / BeginChildcon estilos pegados a mano cuando ya existe primitiva: -fn_ui::dashboard_grid/fn_ui::dashboard_panelpara layouts grid. -fn_ui::tree_view/fn_ui::selectpara listas seleccionables. -fn_ui::modal_dialogpara popups modales. - Iconos via
TI_*(Tabler). Nunca emojis ni hex UTF-8 inline. Vercpp/functions/core/icons_tabler.h. - Build incremental. La app aparece en
cpp/CMakeLists.txtcon suadd_subdirectory(apps/<nombre>). Sin warnings nuevos.
Esqueleto minimo
#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
googletestdirecto); - 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.