diff --git a/.gitignore b/.gitignore index c794538..f7b2944 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ operations.db operations.db-wal operations.db-shm *.exe +imgui.ini diff --git a/views.cpp b/views.cpp index 638fe11..4fc2ec7 100644 --- a/views.cpp +++ b/views.cpp @@ -59,42 +59,93 @@ void draw_kpi_row(const RegistryStats& stats) { } } +// Chart panel con tamano FIJO (no AutoResizeY) para evitar el feedback loop +// con ImPlot que provocaba deslizamiento lateral de las barras y scrollbar +// intermitente. Usa los mismos tokens que dashboard_panel para consistencia +// visual, pero con tamano determinista. +static bool chart_panel_begin(const char* title, const ImVec2& size) { + using namespace fn_tokens; + ImGui::PushStyleColor(ImGuiCol_ChildBg, colors::surface); + ImGui::PushStyleColor(ImGuiCol_Border, colors::border); + ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, radius::md); + ImGui::PushStyleVar(ImGuiStyleVar_ChildBorderSize, 1.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(spacing::md, spacing::md)); + + ImGui::BeginChild(title, size, ImGuiChildFlags_Borders, + ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse); + + ImGui::PushStyleColor(ImGuiCol_Text, colors::text_muted); + ImGui::TextUnformatted(title); + ImGui::PopStyleColor(); + ImGui::Separator(); + return true; +} + +static void chart_panel_end() { + ImGui::EndChild(); + ImGui::PopStyleVar(3); + ImGui::PopStyleColor(2); +} + void draw_charts(RegistryData& data, float height) { - dashboard_grid_begin(4, 8.0f); + // ImGui::BeginTable para reparto equitativo de ancho en 4 columnas y que + // cada chart_panel tenga ancho constrained via GetContentRegionAvail. + const ImGuiTableFlags flags = ImGuiTableFlags_SizingStretchSame + | ImGuiTableFlags_NoPadOuterX; - if (dashboard_panel_begin("By Language", 0, height)) { - auto labels = to_cstr(data.lang_labels); - if (!labels.empty()) - bar_chart("##lang", labels.data(), data.lang_values.data(), static_cast(labels.size())); + // Altura util dentro del panel (height total - title row - separator - padding). + // El plot recibe exactamente esto, asi que no hay redimensionado recursivo. + const float plot_h = height - 48.0f; + + if (ImGui::BeginTable("##chart_grid", 4, flags)) { + ImGui::TableNextRow(); + + ImGui::TableSetColumnIndex(0); + { + ImVec2 sz(ImGui::GetContentRegionAvail().x, height); + chart_panel_begin("By Language", sz); + auto labels = to_cstr(data.lang_labels); + if (!labels.empty()) + bar_chart("##lang", labels.data(), data.lang_values.data(), + static_cast(labels.size()), 0.67f, plot_h); + chart_panel_end(); + } + + ImGui::TableSetColumnIndex(1); + { + ImVec2 sz(ImGui::GetContentRegionAvail().x, height); + chart_panel_begin("By Domain", sz); + auto labels = to_cstr(data.domain_labels); + if (!labels.empty()) + bar_chart("##domain", labels.data(), data.domain_values.data(), + static_cast(labels.size()), 0.67f, plot_h); + chart_panel_end(); + } + + ImGui::TableSetColumnIndex(2); + { + ImVec2 sz(ImGui::GetContentRegionAvail().x, height); + chart_panel_begin("Purity", sz); + const char* labels[] = {"Pure", "Impure"}; + float values[] = {static_cast(data.stats.pure_functions), + static_cast(data.stats.impure_functions)}; + pie_chart("##purity", labels, values, 2); + chart_panel_end(); + } + + ImGui::TableSetColumnIndex(3); + { + ImVec2 sz(ImGui::GetContentRegionAvail().x, height); + chart_panel_begin("Kind", sz); + auto labels = to_cstr(data.kind_labels); + if (!labels.empty()) + pie_chart("##kind", labels.data(), data.kind_values.data(), + static_cast(labels.size())); + chart_panel_end(); + } + + ImGui::EndTable(); } - dashboard_panel_end(); - dashboard_grid_next(); - - if (dashboard_panel_begin("By Domain", 0, height)) { - auto labels = to_cstr(data.domain_labels); - if (!labels.empty()) - bar_chart("##domain", labels.data(), data.domain_values.data(), static_cast(labels.size())); - } - dashboard_panel_end(); - dashboard_grid_next(); - - if (dashboard_panel_begin("Purity", 0, height)) { - const char* labels[] = {"Pure", "Impure"}; - float values[] = {static_cast(data.stats.pure_functions), - static_cast(data.stats.impure_functions)}; - pie_chart("##purity", labels, values, 2); - } - dashboard_panel_end(); - dashboard_grid_next(); - - if (dashboard_panel_begin("Kind", 0, height)) { - auto labels = to_cstr(data.kind_labels); - if (!labels.empty()) - pie_chart("##kind", labels.data(), data.kind_values.data(), static_cast(labels.size())); - } - dashboard_panel_end(); - - dashboard_grid_end(); } void draw_recent_functions(const std::vector& funcs) { @@ -211,11 +262,10 @@ void draw_dashboard(RegistryData& data) { draw_kpi_row(data.stats); ImGui::Dummy(ImVec2(0, fn_tokens::spacing::md)); - // Charts — 35% de la altura restante - float remaining = ImGui::GetContentRegionAvail().y; - float chart_h = remaining * 0.35f; - if (chart_h < 150.0f) chart_h = 150.0f; - + // Charts — altura FIJA en pixeles (no depende del resize de la ventana). + // Antes usabamos remaining*0.35, pero eso recalculaba todo el layout al + // redimensionar, provocando vibracion visible en los plots. + constexpr float chart_h = 260.0f; draw_charts(data, chart_h); ImGui::Dummy(ImVec2(0, fn_tokens::spacing::md));