feat(table): vista tabla por tipo de entidad (issue 0004)

- entity_ops: entity_list_rows (bulk pull id/name/type_ref/status/updated_at).
- AppState::TableRow + cache + filtros (search substring + show_all toggle).
- views_table: tabs por type_ref (alfabetico) o tabla unica con todos los
  tipos. ImGui::BeginTable con sort + clipper para >10k filas. Click en
  Selectable selecciona el nodo en el viewport (clear + add via
  graph_viewport_*).
- views_table_refresh_indices: degree + node_idx por user_data hash.
- main.cpp: panel "Table" en g_panels; cache build tras load_input y
  reload_after_mutation.
This commit is contained in:
2026-05-01 01:05:03 +02:00
parent 078947a2b8
commit 84afa4ce70
6 changed files with 339 additions and 1 deletions
+44
View File
@@ -253,6 +253,26 @@ static bool load_input() {
ge::views_reset_visibility(g_app);
ge::views_apply_visibility(g_app);
// Cache de la vista tabla (issue 0004) — pull bulk + neighbors desde grafo.
{
std::vector<ge::EntityRowSnapshot> snap;
if (g_input.uri && ge::entity_list_rows(g_input.uri, &snap)) {
g_app.table_rows.clear();
g_app.table_rows.reserve(snap.size());
for (auto& s : snap) {
ge::AppState::TableRow tr;
tr.id = std::move(s.id);
tr.name = std::move(s.name);
tr.type_ref = std::move(s.type_ref);
tr.status = std::move(s.status);
tr.updated_at = std::move(s.updated_at);
g_app.table_rows.push_back(std::move(tr));
}
ge::views_table_refresh_indices(g_app);
g_app.table_cache_dirty = false;
}
}
// Inspector: refresca caches (tags distintas, lista de tipos) y limpia
// cualquier draft anterior. El draft se cargara cuando el usuario
// seleccione un nodo en el render loop.
@@ -451,6 +471,7 @@ static fn_ui::PanelToggle g_panels[] = {
{"Stats", nullptr, &g_app.panel_stats},
{"Note", nullptr, &g_app.panel_note},
{"Types", nullptr, &g_app.panel_type_editor},
{"Table", nullptr, &g_app.panel_table},
};
static void render() {
@@ -668,6 +689,23 @@ static void render() {
ge::views_reset_visibility(g_app);
ge::views_apply_visibility(g_app);
// Refresh table cache (issue 0004).
std::vector<ge::EntityRowSnapshot> snap;
if (ge::entity_list_rows(g_input.uri, &snap)) {
g_app.table_rows.clear();
g_app.table_rows.reserve(snap.size());
for (auto& s : snap) {
ge::AppState::TableRow tr;
tr.id = std::move(s.id);
tr.name = std::move(s.name);
tr.type_ref = std::move(s.type_ref);
tr.status = std::move(s.status);
tr.updated_at = std::move(s.updated_at);
g_app.table_rows.push_back(std::move(tr));
}
ge::views_table_refresh_indices(g_app);
}
// Restablece posiciones guardadas. Los nodos nuevos no tienen
// posicion en el layout_store y caen en (0,0).
int restored = ge::layout_store_load(g_graph_hash, g_graph);
@@ -939,6 +977,12 @@ static void render() {
ge::views_type_editor(g_app);
ge::views_type_editor_delete_modal(g_app);
// Table view (issue 0004) — flotante, dockeable.
ImGui::SetNextWindowPos (ImVec2(vp->WorkPos.x + W * 0.15f, top + 60.0f),
ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSize(ImVec2(820.0f, 520.0f), ImGuiCond_FirstUseEver);
ge::views_table(g_app);
g_first_render = false;
}