// data_table_drill — drill-down stack + breadcrumb de stages. // Sub-funcion extraida de modules/data_table/data_table.cpp (issue 0107c). // // Rangos de lineas del fuente original: // - make_drill_filter : lineas 699-706 // - apply_drill_step : lineas 708-718 // - undo_drill_step : lineas 720-730 // - drill_up : lineas 732-737 // - draw_stage_breadcrumb : lineas 1386-1483 // - drill_into : lineas 2898-2919 #include "viz/data_table_drill.h" #include "core/data_table_types.h" #include "imgui.h" #include #include #include namespace data_table { // --------------------------------------------------------------------------- // make_drill_filter: crea un Filter Op::Eq para col_idx con el valor dado. // --------------------------------------------------------------------------- Filter make_drill_filter(int col_idx, const std::string& value) { Filter f; f.col = col_idx; f.op = Op::Eq; f.value = value; return f; } // --------------------------------------------------------------------------- // apply_drill_step: inserta step.added en el stage en step.target_stage y // actualiza st.active_stage. Retorna true si el step se aplico correctamente. // --------------------------------------------------------------------------- bool apply_drill_step(State& st, const DrillStep& step) { if (step.target_stage < 0 || step.target_stage >= (int)st.stages.size()) return false; Stage& s = st.stages[step.target_stage]; int pos = step.filter_pos; if (pos < 0 || pos > (int)s.filters.size()) return false; s.filters.insert(s.filters.begin() + pos, step.added); st.active_stage = step.target_stage; return true; } // --------------------------------------------------------------------------- // undo_drill_step: elimina el filter insertado por apply_drill_step y restaura // st.active_stage a step.prev_active_stage. Retorna true si se deshizo. // --------------------------------------------------------------------------- bool undo_drill_step(State& st, const DrillStep& step) { if (step.target_stage < 0 || step.target_stage >= (int)st.stages.size()) return false; Stage& s = st.stages[step.target_stage]; int pos = step.filter_pos; if (pos < 0 || pos >= (int)s.filters.size()) return false; s.filters.erase(s.filters.begin() + pos); if (step.prev_active_stage >= 0 && step.prev_active_stage < (int)st.stages.size()) st.active_stage = step.prev_active_stage; return true; } // --------------------------------------------------------------------------- // drill_up: retrocede st.active_stage en 1 si hay stages previos. // Retorna true si se pudo retroceder. // --------------------------------------------------------------------------- bool drill_up(State& st) { if (st.stages.empty()) return false; if (st.active_stage <= 0) return false; st.active_stage -= 1; return true; } // --------------------------------------------------------------------------- // draw_stage_breadcrumb: barra de navegacion de drill con botones < > ^ y // selector de stages. Mutates st.drill_back/forward y st.active_stage. // --------------------------------------------------------------------------- void draw_stage_breadcrumb(State& st) { st.ensure_stage0(); // Drill history back/forward (fase 10). Botones al inicio. { bool can_back = !st.drill_back.empty(); ImGui::BeginDisabled(!can_back); if (ImGui::SmallButton("<##drill_back")) { DrillStep s = st.drill_back.back(); st.drill_back.pop_back(); if (undo_drill_step(st, s)) { st.drill_forward.push_back(s); } } ImGui::EndDisabled(); if (can_back && ImGui::IsItemHovered()) ImGui::SetTooltip("Drill back (%zu)", st.drill_back.size()); ImGui::SameLine(); bool can_fwd = !st.drill_forward.empty(); ImGui::BeginDisabled(!can_fwd); if (ImGui::SmallButton(">##drill_fwd")) { DrillStep s = st.drill_forward.back(); st.drill_forward.pop_back(); if (apply_drill_step(st, s)) { st.drill_back.push_back(s); } } ImGui::EndDisabled(); if (can_fwd && ImGui::IsItemHovered()) ImGui::SetTooltip("Drill forward (%zu)", st.drill_forward.size()); ImGui::SameLine(); bool can_up = (st.active_stage > 0); ImGui::BeginDisabled(!can_up); if (ImGui::SmallButton("^##drill_up")) drill_up(st); ImGui::EndDisabled(); if (can_up && ImGui::IsItemHovered()) ImGui::SetTooltip("Drill up (stage previo, sin perder filters)"); ImGui::SameLine(); ImGui::TextDisabled("|"); ImGui::SameLine(); } for (int si = 0; si < (int)st.stages.size(); ++si) { if (si > 0) { ImGui::SameLine(); ImGui::TextDisabled(">"); ImGui::SameLine(); } bool active = (si == st.active_stage); if (active) { ImGui::PushStyleColor(ImGuiCol_Button, IM_COL32( 80, 140, 200, 240)); ImGui::PushStyleColor(ImGuiCol_ButtonHovered, IM_COL32(100, 160, 220, 240)); ImGui::PushStyleColor(ImGuiCol_ButtonActive, IM_COL32( 60, 120, 180, 240)); } else { ImGui::PushStyleColor(ImGuiCol_Button, IM_COL32( 70, 70, 90, 200)); ImGui::PushStyleColor(ImGuiCol_ButtonHovered, IM_COL32( 90, 90, 120, 220)); ImGui::PushStyleColor(ImGuiCol_ButtonActive, IM_COL32( 55, 55, 75, 220)); } char label[256]; if (si == 0) { std::snprintf(label, sizeof(label), "Raw##stage%d", si); } else { const Stage& s = st.stages[si]; std::string desc; for (size_t i = 0; i < s.breakouts.size() && i < 2; ++i) { if (i > 0) desc += ", "; desc += s.breakouts[i]; } if (s.breakouts.size() > 2) desc += "..."; if (desc.empty()) std::snprintf(label, sizeof(label), "Stage %d##s%d", si, si); else std::snprintf(label, sizeof(label), "Stage %d: by %s##s%d", si, desc.c_str(), si); } if (ImGui::Button(label)) st.active_stage = si; ImGui::PopStyleColor(3); if (si > 0) { ImGui::SameLine(); char xlbl[32]; std::snprintf(xlbl, sizeof(xlbl), "x##rm_s%d", si); if (ImGui::SmallButton(xlbl)) { // borra ese stage y sucesores while ((int)st.stages.size() > si) st.stages.pop_back(); if (st.active_stage >= (int)st.stages.size()) st.active_stage = (int)st.stages.size() - 1; if (st.active_stage < 0) st.active_stage = 0; break; } } } ImGui::SameLine(); ImGui::TextDisabled(">"); ImGui::SameLine(); if (ImGui::SmallButton("+ Stage##add_stage")) { st.stages.push_back(Stage{}); st.active_stage = (int)st.stages.size() - 1; } } // --------------------------------------------------------------------------- // drill_into: API publica. Anade un filter Op::Eq sobre col_name=value al // stage (from_stage - 1) y cambia st.active_stage a ese stage previo. // Graba el step en st.drill_back y limpia st.drill_forward (rama nueva). // --------------------------------------------------------------------------- void drill_into(State& st, int from_stage, const std::string& col_name, const std::string& value, const std::vector& prev_input_headers) { if (from_stage <= 0 || from_stage >= (int)st.stages.size()) return; int target = from_stage - 1; int ci = -1; for (size_t i = 0; i < prev_input_headers.size(); ++i) { if (prev_input_headers[i] == col_name) { ci = (int)i; break; } } if (ci < 0) return; // Fase 10: graba step en drill_back, limpia forward (rama nueva). DrillStep step; step.target_stage = target; step.filter_pos = (int)st.stages[target].filters.size(); step.prev_active_stage = st.active_stage; step.added = make_drill_filter(ci, value); apply_drill_step(st, step); st.drill_back.push_back(step); st.drill_forward.clear(); } } // namespace data_table