From cd445d8e1ad83795e56d27b39e3b4ef0f5ecfcaa Mon Sep 17 00:00:00 2001 From: Egutierrez Date: Wed, 29 Apr 2026 00:29:44 +0200 Subject: [PATCH 1/3] refactor(primitives_gallery): usar tree_view en sidebar (issue 0046) El sidebar agrupaba demos por categoria con un Selectable+PushStyleColor manual por item. Ahora usa fn_ui::tree_view con las categorias como ramas (default-open via SetNextItemOpen + ImGuiCond_FirstUseEver) y las demos como hojas seleccionables. Visualmente equivalente: separadores por categoria, item activo coloreado. Raw ImGui::Begin*/Selectable: 4 -> 3 (Selectable eliminado). --- cpp/apps/primitives_gallery/main.cpp | 53 +++++++++++++++------------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/cpp/apps/primitives_gallery/main.cpp b/cpp/apps/primitives_gallery/main.cpp index 82c6d477..c0e72250 100644 --- a/cpp/apps/primitives_gallery/main.cpp +++ b/cpp/apps/primitives_gallery/main.cpp @@ -14,6 +14,7 @@ #include "core/page_header.h" #include "core/toast.h" #include "core/app_menubar.h" +#include "core/tree_view.h" #include "demos.h" #include "demo.h" @@ -90,35 +91,37 @@ static const DemoEntry* find_demo(const std::string& id) { } 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; + // Agrupar por categoria como rama del tree_view (categorias abiertas por + // defecto). Cada demo es una hoja seleccionable. + int i = 0; + while (i < k_demo_count) { + const char* category = k_demos[i].category; + + // Default-open la rama la primera vez que se abre el sidebar. + ImGui::SetNextItemOpen(true, ImGuiCond_FirstUseEver); + if (fn_ui::tree_branch_begin(category, category, /*selected=*/false)) { + // Recorrer todas las demos consecutivas con esta misma categoria. + while (i < k_demo_count + && std::strcmp(k_demos[i].category, category) == 0) { + const auto& d = k_demos[i]; + const bool selected = (g_selected_id == d.id); + fn_ui::tree_leaf(d.id, d.label, selected); + if (fn_ui::tree_node_clicked()) { + g_selected_id = d.id; + } + i++; + } + fn_ui::tree_branch_end(); + } else { + // Rama colapsada — saltar todos sus items. + while (i < k_demo_count + && std::strcmp(k_demos[i].category, category) == 0) { + i++; + } } - - 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(); From 2aceccfd7e89bebc9804ba68e952914557f671f4 Mon Sep 17 00:00:00 2001 From: Egutierrez Date: Wed, 29 Apr 2026 00:29:50 +0200 Subject: [PATCH 2/3] refactor(shaders_lab): usar modal_dialog en save-as (issue 0046) El modal Save-as-generator usaba BeginPopupModal + InputText + Button crudo. Ahora usa fn_ui::modal_dialog_begin/end + fn_ui::text_input + fn_ui::button del registry. El error inline usa fn_tokens::colors::error en vez de ImVec4(1, 0.4, 0.4, 1). Anade modal_dialog.cpp, text_input.cpp y button.cpp al CMakeLists del app. Raw ImGui::Begin*/Selectable/BeginPopupModal: 11 -> 8. --- cpp/apps/shaders_lab/CMakeLists.txt | 4 +++ cpp/apps/shaders_lab/main.cpp | 40 ++++++++++++++++------------- 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/cpp/apps/shaders_lab/CMakeLists.txt b/cpp/apps/shaders_lab/CMakeLists.txt index 5e8a9d43..cc25d777 100644 --- a/cpp/apps/shaders_lab/CMakeLists.txt +++ b/cpp/apps/shaders_lab/CMakeLists.txt @@ -17,6 +17,10 @@ add_imgui_app(shaders_lab ${CMAKE_SOURCE_DIR}/functions/gfx/dag_node_previews.cpp ${CMAKE_SOURCE_DIR}/functions/gfx/shaderlab_db.cpp ${CMAKE_SOURCE_DIR}/functions/gfx/code_to_generator.cpp + # Primitivos UI usados por el modal Save-as-generator. + ${CMAKE_SOURCE_DIR}/functions/core/modal_dialog.cpp + ${CMAKE_SOURCE_DIR}/functions/core/text_input.cpp + ${CMAKE_SOURCE_DIR}/functions/core/button.cpp # fps_overlay, panel_menu, layouts_menu, app_menubar, layout_storage ya # viven en fn_framework. ) diff --git a/cpp/apps/shaders_lab/main.cpp b/cpp/apps/shaders_lab/main.cpp index c7ccb01f..c817fc9b 100644 --- a/cpp/apps/shaders_lab/main.cpp +++ b/cpp/apps/shaders_lab/main.cpp @@ -17,6 +17,10 @@ #include "core/panel_menu.h" #include "core/layouts_menu.h" #include "core/layout_storage.h" +#include "core/modal_dialog.h" +#include "core/text_input.h" +#include "core/button.h" +#include "core/tokens.h" #include "compiler.h" @@ -236,41 +240,41 @@ static void render() { // --- Code window --- if (g_show_code) { if (ImGui::Begin("Code", &g_show_code)) { - if (ImGui::Button("Save as generator...")) { + if (fn_ui::button("Save as generator...", fn_ui::ButtonVariant::Secondary)) { g_save_modal_open = true; g_save_err.clear(); - ImGui::OpenPopup("save_as_generator"); } - if (ImGui::BeginPopupModal("save_as_generator", &g_save_modal_open, - ImGuiWindowFlags_AlwaysAutoResize)) { - ImGui::Text("Guardar shader actual como nodo Gen del DAG."); + if (fn_ui::modal_dialog_begin("Save as generator", &g_save_modal_open, + ImVec2(420, 0))) { + ImGui::TextUnformatted("Guardar shader actual como nodo Gen del DAG."); ImGui::Spacing(); - ImGui::InputText("name (snake_case)", g_save_name, sizeof(g_save_name)); - ImGui::InputText("label", g_save_label, sizeof(g_save_label)); + fn_ui::text_input("name (snake_case)", g_save_name, sizeof(g_save_name), + "ej: my_shader"); + fn_ui::text_input("label", g_save_label, sizeof(g_save_label)); ImGui::InputTextMultiline("description", g_save_desc, sizeof(g_save_desc), ImVec2(380, 60)); - ImGui::InputText("tags (CSV)", g_save_tags, sizeof(g_save_tags)); + fn_ui::text_input("tags (CSV)", g_save_tags, sizeof(g_save_tags)); if (!g_save_err.empty()) { - ImGui::TextColored(ImVec4(1, 0.4f, 0.4f, 1), "%s", g_save_err.c_str()); + ImGui::PushStyleColor(ImGuiCol_Text, fn_tokens::colors::error); + ImGui::TextWrapped("%s", g_save_err.c_str()); + ImGui::PopStyleColor(); } - ImGui::Spacing(); - if (ImGui::Button("Save", ImVec2(120, 0))) { + ImGui::Separator(); + if (fn_ui::button("Cancel", fn_ui::ButtonVariant::Subtle)) { + g_save_modal_open = false; + } + ImGui::SameLine(); + if (fn_ui::button("Save", fn_ui::ButtonVariant::Primary)) { g_save_err = save_current_as_generator(); if (g_save_err.empty()) { g_save_modal_open = false; - ImGui::CloseCurrentPopup(); } } - ImGui::SameLine(); - if (ImGui::Button("Cancel", ImVec2(120, 0))) { - g_save_modal_open = false; - ImGui::CloseCurrentPopup(); - } - ImGui::EndPopup(); } + fn_ui::modal_dialog_end(); ImVec2 avail = ImGui::GetContentRegionAvail(); float footer_h = g_code_err.empty() ? 0.0f : ImGui::GetTextLineHeightWithSpacing() + 8.0f; From f906ffbec4450b95a664e4bde591775696446b26 Mon Sep 17 00:00:00 2001 From: Egutierrez Date: Wed, 29 Apr 2026 00:29:54 +0200 Subject: [PATCH 3/3] docs: cerrar issue 0046 --- dev/issues/{ => completed}/0046-cpp-refactor-raw-imgui.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename dev/issues/{ => completed}/0046-cpp-refactor-raw-imgui.md (100%) diff --git a/dev/issues/0046-cpp-refactor-raw-imgui.md b/dev/issues/completed/0046-cpp-refactor-raw-imgui.md similarity index 100% rename from dev/issues/0046-cpp-refactor-raw-imgui.md rename to dev/issues/completed/0046-cpp-refactor-raw-imgui.md