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:
2026-05-03 14:57:20 +02:00
parent eff273a2d4
commit b67da92e18
5 changed files with 99 additions and 8 deletions
+27 -1
View File
@@ -782,10 +782,32 @@ bool entity_list_rows(const char* db_path,
if (db) sqlite3_close(db);
return false;
}
const char* sql =
// Detecta si existe la columna `group_id` (issue 0035a). En BDs viejas
// sin la columna, el campo queda vacio y nada cambia.
bool has_group_id = false;
{
sqlite3_stmt* pst = nullptr;
if (sqlite3_prepare_v2(db, "PRAGMA table_info(entities)", -1, &pst, nullptr) == SQLITE_OK) {
while (sqlite3_step(pst) == SQLITE_ROW) {
const unsigned char* name = sqlite3_column_text(pst, 1);
if (name && std::strcmp((const char*)name, "group_id") == 0) {
has_group_id = true;
break;
}
}
sqlite3_finalize(pst);
}
}
const char* sql_with =
"SELECT id, COALESCE(name,''), COALESCE(type_ref,''), "
" COALESCE(status,''), COALESCE(updated_at,''), "
" COALESCE(group_id,'') "
"FROM entities ORDER BY type_ref, name";
const char* sql_without =
"SELECT id, COALESCE(name,''), COALESCE(type_ref,''), "
" COALESCE(status,''), COALESCE(updated_at,'') "
"FROM entities ORDER BY type_ref, name";
const char* sql = has_group_id ? sql_with : sql_without;
sqlite3_stmt* st = nullptr;
if (sqlite3_prepare_v2(db, sql, -1, &st, nullptr) != SQLITE_OK) {
sqlite3_close(db);
@@ -803,6 +825,10 @@ bool entity_list_rows(const char* db_path,
r.type_ref = a2 ? (const char*)a2 : "";
r.status = a3 ? (const char*)a3 : "";
r.updated_at = a4 ? (const char*)a4 : "";
if (has_group_id) {
const unsigned char* a5 = sqlite3_column_text(st, 5);
r.group_id = a5 ? (const char*)a5 : "";
}
out->push_back(std::move(r));
}
sqlite3_finalize(st);