feat(primitives_gallery): wire text_editor + file_watcher demo
- demos_text_editor.cpp: split horizontal con editor GLSL precargado a la izquierda (boton Save to /tmp/fn_demo.glsl + dirty indicator) y panel de eventos a la derecha (path, active flag, lista scrollable, boton clear). Watcher activo sobre /tmp/fn_demo.glsl; reintenta el add() tras el primer Save si el archivo no existia al iniciar. - demos.h: declaracion de gallery::demo_text_editor() - main.cpp: entry "text_editor"/"text_editor + watcher" en categoria Core - CMakeLists.txt: anade demos_text_editor.cpp + sources de text_editor, file_watcher y vendor TextEditor.cpp + include path de imgui_text_edit Nota: la primitives_gallery NO se construye en este branch (sus deps — button.cpp, toolbar.cpp, etc. — son untracked en master). El subdirectorio se anade pero protegido por FN_BUILD_GALLERY=OFF para no romper builds. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,159 @@
|
||||
// primitives_gallery — catalogo visual interactivo de los primitivos UI
|
||||
// del registry (cpp/functions/core y cpp/functions/viz).
|
||||
//
|
||||
// Sidebar izquierdo con lista de primitivos agrupados por dominio; panel
|
||||
// derecho renderiza la demo del item seleccionado (+ snippet de codigo).
|
||||
//
|
||||
// Rol: smoke test visual + documentacion viva + build gate en CI.
|
||||
// NO se conecta a sqlite_api ni a ningun backend. Datos sinteticos.
|
||||
|
||||
#include "app_base.h"
|
||||
#include "imgui.h"
|
||||
#include "core/fullscreen_window.h"
|
||||
#include "core/tokens.h"
|
||||
#include "core/page_header.h"
|
||||
#include "core/toast.h"
|
||||
#include "core/app_menubar.h"
|
||||
#include "gfx/gl_loader.h"
|
||||
|
||||
#include "demos.h"
|
||||
#include "demo.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
struct DemoEntry {
|
||||
const char* id; // id estable, apto para comparar seleccion
|
||||
const char* label; // texto en sidebar
|
||||
const char* category; // "Core" o "Viz"
|
||||
void (*fn)(); // puntero a la demo_xxx
|
||||
};
|
||||
|
||||
static const DemoEntry k_demos[] = {
|
||||
// Core
|
||||
{"button", "button", "Core", &gallery::demo_button},
|
||||
{"icon_button", "icon_button", "Core", &gallery::demo_icon_button},
|
||||
{"toolbar", "toolbar", "Core", &gallery::demo_toolbar},
|
||||
{"modal_dialog", "modal_dialog", "Core", &gallery::demo_modal},
|
||||
{"text_input", "text_input", "Core", &gallery::demo_text_input},
|
||||
{"select", "select", "Core", &gallery::demo_select},
|
||||
{"toast", "toast + inbox", "Core", &gallery::demo_toast},
|
||||
{"tree_view", "tree_view", "Core", &gallery::demo_tree_view},
|
||||
{"badge", "badge", "Core", &gallery::demo_badge},
|
||||
{"empty_state", "empty_state", "Core", &gallery::demo_empty_state},
|
||||
{"page_header", "page_header", "Core", &gallery::demo_page_header},
|
||||
{"dashboard_panel", "dashboard_panel", "Core", &gallery::demo_dashboard_panel},
|
||||
{"kpi_card", "kpi_card", "Core", &gallery::demo_kpi_card},
|
||||
{"text_editor", "text_editor + watcher", "Core", &gallery::demo_text_editor},
|
||||
// Viz
|
||||
{"bar_chart", "bar_chart", "Viz", &gallery::demo_bar_chart},
|
||||
{"pie_chart", "pie_chart", "Viz", &gallery::demo_pie_chart},
|
||||
{"line_plot", "line_plot", "Viz", &gallery::demo_line_plot},
|
||||
{"scatter_plot", "scatter_plot", "Viz", &gallery::demo_scatter_plot},
|
||||
{"histogram", "histogram", "Viz", &gallery::demo_histogram},
|
||||
{"sparkline", "sparkline", "Viz", &gallery::demo_sparkline},
|
||||
{"graph_viewport", "graph_viewport", "Viz", &gallery::demo_graph},
|
||||
// Gfx (shaders_lab core)
|
||||
{"shader_canvas", "shader_canvas", "Gfx", &gallery::demo_shader_canvas},
|
||||
};
|
||||
static constexpr int k_demo_count = sizeof(k_demos) / sizeof(k_demos[0]);
|
||||
|
||||
static std::string g_selected_id = "button";
|
||||
|
||||
static const DemoEntry* find_demo(const std::string& id) {
|
||||
for (int i = 0; i < k_demo_count; i++) {
|
||||
if (id == k_demos[i].id) return &k_demos[i];
|
||||
}
|
||||
return &k_demos[0];
|
||||
}
|
||||
|
||||
static void draw_sidebar() {
|
||||
using namespace fn_tokens;
|
||||
ImGui::BeginChild("##gallery_sidebar", ImVec2(220, 0),
|
||||
ImGuiChildFlags_Borders);
|
||||
|
||||
const char* current_category = nullptr;
|
||||
for (int i = 0; i < k_demo_count; i++) {
|
||||
const auto& d = k_demos[i];
|
||||
if (!current_category || std::strcmp(current_category, d.category) != 0) {
|
||||
if (current_category) ImGui::Dummy(ImVec2(0, spacing::sm));
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, colors::text_dim);
|
||||
ImGui::TextUnformatted(d.category);
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::Separator();
|
||||
current_category = d.category;
|
||||
}
|
||||
|
||||
const bool selected = (g_selected_id == d.id);
|
||||
ImGui::PushStyleColor(ImGuiCol_Header, selected ? colors::surface_hover : ImVec4(0,0,0,0));
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, colors::surface_hover);
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderActive, colors::surface);
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, selected ? colors::primary : colors::text);
|
||||
|
||||
char label[96];
|
||||
std::snprintf(label, sizeof(label), "%s##sel_%s", d.label, d.id);
|
||||
if (ImGui::Selectable(label, selected)) {
|
||||
g_selected_id = d.id;
|
||||
}
|
||||
|
||||
ImGui::PopStyleColor(4);
|
||||
}
|
||||
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
static void render() {
|
||||
static bool init_done = false;
|
||||
if (!init_done) {
|
||||
fn_tokens::apply_dark_theme();
|
||||
// En Linux es no-op; en Windows resuelve los punteros GL via wglGetProcAddress.
|
||||
// Imprescindible antes de invocar primitivos que usen OpenGL 2.0+ (graph_viewport,
|
||||
// shader_canvas, etc).
|
||||
fn::gfx::gl_loader_init();
|
||||
init_done = true;
|
||||
}
|
||||
|
||||
// MainMenuBar (solo Settings — la gallery no tiene paneles toggleables ni layouts)
|
||||
fn_ui::app_menubar(nullptr, 0, nullptr);
|
||||
|
||||
fullscreen_window_begin("##gallery");
|
||||
|
||||
page_header_begin("Primitives Gallery",
|
||||
"Visual catalog of fn_registry C++ UI primitives");
|
||||
page_header_end();
|
||||
|
||||
if (ImGui::BeginTable("##layout", 2,
|
||||
ImGuiTableFlags_Resizable | ImGuiTableFlags_SizingFixedFit)) {
|
||||
ImGui::TableSetupColumn("sidebar", ImGuiTableColumnFlags_WidthFixed, 220.0f);
|
||||
ImGui::TableSetupColumn("content", ImGuiTableColumnFlags_WidthStretch);
|
||||
ImGui::TableNextRow();
|
||||
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
draw_sidebar();
|
||||
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
ImGui::BeginChild("##gallery_content", ImVec2(0, 0),
|
||||
ImGuiChildFlags_Borders,
|
||||
ImGuiWindowFlags_HorizontalScrollbar);
|
||||
const DemoEntry* d = find_demo(g_selected_id);
|
||||
if (d && d->fn) d->fn();
|
||||
ImGui::EndChild();
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
fullscreen_window_end();
|
||||
|
||||
// Toasts se renderizan encima para que el demo de toast funcione aqui tambien.
|
||||
fn_ui::toast_render();
|
||||
}
|
||||
|
||||
int main(int /*argc*/, char** /*argv*/) {
|
||||
return fn::run_app(
|
||||
{.title = "fn_registry · Primitives Gallery",
|
||||
.width = 1400, .height = 900, .viewports = true},
|
||||
render
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user