feat(views): chart panels con tamano fijo, altura de row constante, gitignore imgui.ini

- Nuevo chart_panel_begin/end local (reemplaza dashboard_panel con
  AutoResizeY): BeginChild con altura explicita pasada por parametro,
  NoScrollbar | NoScrollWithMouse. Rompe el feedback loop plot <-> panel
  que causaba deslizamiento lateral y scrollbar fugaz.
- Altura de charts fija 260px (antes GetContentRegionAvail().y * 0.35).
  Sin esto, redimensionar la ventana propaga cambios de altura a todos
  los plots y se ve vibracion.
- KPIs reorganizados en ImGui::BeginTable 4 cols x 2 rows. Las celdas
  de tabla propagan ancho constrained, necesario para que el BeginChild
  interno del kpi_card (card v1.2 compacta 78px) ocupe exactamente la
  celda.
- imgui.ini al .gitignore: estado local de la ventana, no versionable.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-24 21:31:26 +02:00
parent 646b9463c0
commit d6bdab89e8
2 changed files with 89 additions and 38 deletions
+1
View File
@@ -2,3 +2,4 @@ operations.db
operations.db-wal
operations.db-shm
*.exe
imgui.ini
+72 -22
View File
@@ -59,42 +59,93 @@ void draw_kpi_row(const RegistryStats& stats) {
}
}
void draw_charts(RegistryData& data, float height) {
dashboard_grid_begin(4, 8.0f);
// 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));
if (dashboard_panel_begin("By Language", 0, height)) {
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) {
// 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;
// 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<int>(labels.size()));
bar_chart("##lang", labels.data(), data.lang_values.data(),
static_cast<int>(labels.size()), 0.67f, plot_h);
chart_panel_end();
}
dashboard_panel_end();
dashboard_grid_next();
if (dashboard_panel_begin("By Domain", 0, height)) {
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<int>(labels.size()));
bar_chart("##domain", labels.data(), data.domain_values.data(),
static_cast<int>(labels.size()), 0.67f, plot_h);
chart_panel_end();
}
dashboard_panel_end();
dashboard_grid_next();
if (dashboard_panel_begin("Purity", 0, height)) {
ImGui::TableSetColumnIndex(2);
{
ImVec2 sz(ImGui::GetContentRegionAvail().x, height);
chart_panel_begin("Purity", sz);
const char* labels[] = {"Pure", "Impure"};
float values[] = {static_cast<float>(data.stats.pure_functions),
static_cast<float>(data.stats.impure_functions)};
pie_chart("##purity", labels, values, 2);
chart_panel_end();
}
dashboard_panel_end();
dashboard_grid_next();
if (dashboard_panel_begin("Kind", 0, height)) {
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<int>(labels.size()));
pie_chart("##kind", labels.data(), data.kind_values.data(),
static_cast<int>(labels.size()));
chart_panel_end();
}
dashboard_panel_end();
dashboard_grid_end();
ImGui::EndTable();
}
}
void draw_recent_functions(const std::vector<FunctionRow>& 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));