feat(0035d): doble click en Group abre tableview filtrado por group_id
- entity_ops: EntityRowSnapshot.group_id + SQL con COALESCE(group_id,'') + deteccion via PRAGMA para BDs viejas sin la columna. - views.h: TableRow.group_id + AppState.table_filter_group_id / table_filter_group_name (RAM-only). - main.cpp: dispatch en want_open_note — si type_ref == "Group", setea filtro de grupo + abre panel Table en vez de Note. Reset de search buf y col_filters al entrar al drill-in para que el usuario vea todo el contenido del grupo. - views.cpp: build_visible compone group_id con search/tabs/col_filters (AND). types_present se reduce a tipos presentes en el grupo cuando hay drill-in activo. Header pintado en amarillo con TI_FOLDER + contador + boton "Clear group filter". Al cerrarse el panel se limpia el filtro automaticamente. Tests: pytest 35 passed (WSL) / 24 passed + 11 skipped (Windows). Refs: issues/0035d-tableview-drill-in.md
This commit is contained in:
@@ -1733,12 +1733,41 @@ void render_one_table(AppState& app, std::vector<int>& visible_indices) {
|
||||
} // namespace
|
||||
|
||||
void views_table(AppState& app) {
|
||||
if (!app.panel_table) return;
|
||||
// Si el panel se cierra, limpiamos el filtro de grupo (RAM-only).
|
||||
if (!app.panel_table) {
|
||||
if (!app.table_filter_group_id.empty()) {
|
||||
app.table_filter_group_id.clear();
|
||||
app.table_filter_group_name.clear();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!ImGui::Begin("Table", &app.panel_table)) {
|
||||
ImGui::End();
|
||||
return;
|
||||
}
|
||||
|
||||
// Breadcrumb de drill-in por grupo (issue 0035d).
|
||||
if (!app.table_filter_group_id.empty()) {
|
||||
// Contar filas que pertenecen a este grupo (sin aplicar otros filtros).
|
||||
size_t n_group = 0;
|
||||
for (const auto& r : app.table_rows) {
|
||||
if (r.group_id == app.table_filter_group_id) ++n_group;
|
||||
}
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.85f, 0.75f, 0.35f, 1.0f));
|
||||
ImGui::Text(TI_FOLDER " Group: %s (%zu)",
|
||||
app.table_filter_group_name.empty()
|
||||
? app.table_filter_group_id.c_str()
|
||||
: app.table_filter_group_name.c_str(),
|
||||
n_group);
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::SameLine();
|
||||
if (fn_ui::button("Clear group filter", fn_ui::ButtonVariant::Subtle)) {
|
||||
app.table_filter_group_id.clear();
|
||||
app.table_filter_group_name.clear();
|
||||
}
|
||||
ImGui::Separator();
|
||||
}
|
||||
|
||||
// Toolbar superior: search + show all.
|
||||
ImGui::SetNextItemWidth(220);
|
||||
ImGui::InputTextWithHint("##tsearch", TI_SEARCH " filter name/id...",
|
||||
@@ -1777,12 +1806,15 @@ void views_table(AppState& app) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Indices por tipo.
|
||||
// Indices por tipo. Si hay filtro de grupo (issue 0035d) acotamos los
|
||||
// types tabulables a los presentes dentro del grupo.
|
||||
std::vector<std::string> types_present;
|
||||
types_present.reserve(8);
|
||||
{
|
||||
std::unordered_set<std::string> seen;
|
||||
for (const auto& r : app.table_rows) {
|
||||
if (!app.table_filter_group_id.empty()
|
||||
&& r.group_id != app.table_filter_group_id) continue;
|
||||
if (seen.insert(r.type_ref).second) types_present.push_back(r.type_ref);
|
||||
}
|
||||
std::sort(types_present.begin(), types_present.end());
|
||||
@@ -1793,6 +1825,10 @@ void views_table(AppState& app) {
|
||||
v.reserve(app.table_rows.size());
|
||||
for (size_t i = 0; i < app.table_rows.size(); ++i) {
|
||||
const auto& r = app.table_rows[i];
|
||||
// Drill-in por grupo (issue 0035d): si hay filtro activo, solo
|
||||
// pasan filas cuyo group_id coincide.
|
||||
if (!app.table_filter_group_id.empty()
|
||||
&& r.group_id != app.table_filter_group_id) continue;
|
||||
if (type_filter && r.type_ref != type_filter) continue;
|
||||
if (app.table_search_buf[0]
|
||||
&& !ci_contains(r.name, app.table_search_buf)
|
||||
|
||||
Reference in New Issue
Block a user