feat(main): wire 0011 — context menu + triggers + sync de windows
- Context menu del viewport detecta type_ref='Table' y anade Expand/ Collapse table. Toggle escribe metadata.expanded en BD y resincroniza table_windows. - Triggers want_promote_row -> tableview_promote_row + reload + focus inspector con la entidad recien creada. - want_demote_entity -> tableview_demote_row + reload. - want_focus_entity: resuelve entity_id -> node_idx via FNV1a, centra camara, abre inspector. - want_import -> tableview_ingest_file + tableview_create + reload. - Loop por table_windows page_dirty -> tableview_count + (si columns vacios) descubre+persiste columnas + tableview_page. - Cierre via X de ventana detectado leyendo open=false; bajamos expanded en BD y borramos del mapa. - Sync de table_windows tras load_input y reload_after_mutation. - views_table_window + views_import_dataset_modal llamados en render().
This commit is contained in:
@@ -264,6 +264,9 @@ static bool load_input() {
|
||||
std::fprintf(stdout,
|
||||
"[graph_explorer] table counts refreshed: %zu tables, %lld total rows\n",
|
||||
g_app.table_node_counts.size(), (long long)total_rows);
|
||||
// Sync de windows expandidas (issue 0011) — reabre las que el
|
||||
// usuario tenia abiertas en la sesion previa (metadata.expanded=true).
|
||||
ge::views_table_windows_sync(g_app, g_input.uri);
|
||||
}
|
||||
|
||||
// Cache de la vista tabla (issue 0004) — pull bulk + neighbors desde grafo.
|
||||
@@ -420,6 +423,28 @@ static void render_context_menu() {
|
||||
ImGui::TextDisabled("%s", lbl && *lbl ? lbl : "(unnamed)");
|
||||
ImGui::Separator();
|
||||
|
||||
// Detectar si el nodo es Table y resolver entity_id para opciones tabla.
|
||||
bool is_table = false;
|
||||
if (n.type_id < (uint16_t)g_graph.type_count) {
|
||||
const EntityType& t = g_graph.types[n.type_id];
|
||||
if (t.name && std::strcmp(t.name, "Table") == 0) is_table = true;
|
||||
}
|
||||
const char* sql_id = ge::entity_index_lookup(g_idx, n.user_data);
|
||||
|
||||
if (is_table && sql_id) {
|
||||
// Determinar estado expanded actual sin ir a BD: mira table_windows.
|
||||
bool currently_expanded =
|
||||
g_app.table_windows.find(sql_id) != g_app.table_windows.end();
|
||||
const char* lbl_exp = currently_expanded
|
||||
? TI_X " Collapse table"
|
||||
: TI_TABLE " Expand table";
|
||||
if (ImGui::MenuItem(lbl_exp)) {
|
||||
g_app.want_toggle_expanded = true;
|
||||
g_app.toggle_expanded_id = sql_id;
|
||||
}
|
||||
ImGui::Separator();
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("Change type")) {
|
||||
// Construye un set ordenado y deduplicado: tipos del grafo + defaults.
|
||||
// Asi evitamos colisiones de ID en ImGui ("person" en grafo y default).
|
||||
@@ -614,6 +639,7 @@ static void render() {
|
||||
load_input();
|
||||
}
|
||||
|
||||
|
||||
// ---- Type Editor (issue 0007) ----
|
||||
if (g_app.want_types_save) {
|
||||
g_app.want_types_save = false;
|
||||
@@ -705,6 +731,9 @@ static void render() {
|
||||
// Refresh Table node counts (issue 0010).
|
||||
ge::tableview_refresh_counts(g_input.uri, &g_app.table_node_counts);
|
||||
|
||||
// Sincroniza windows (issue 0011) por si una Table aparecio o desaparecio.
|
||||
ge::views_table_windows_sync(g_app, g_input.uri);
|
||||
|
||||
// Refresh table cache (issue 0004).
|
||||
std::vector<ge::EntityRowSnapshot> snap;
|
||||
if (ge::entity_list_rows(g_input.uri, &snap)) {
|
||||
@@ -802,6 +831,132 @@ static void render() {
|
||||
g_app.want_change_type = false;
|
||||
}
|
||||
|
||||
// ---- Table node UI fase 2 (issue 0011) ----
|
||||
if (g_app.want_toggle_expanded && !g_app.toggle_expanded_id.empty()
|
||||
&& !g_input_path.empty()) {
|
||||
std::string id = g_app.toggle_expanded_id;
|
||||
bool currently = g_app.table_windows.find(id) != g_app.table_windows.end();
|
||||
ge::tableview_set_expanded(g_input_path.c_str(), id.c_str(), !currently);
|
||||
ge::views_table_windows_sync(g_app, g_input_path.c_str());
|
||||
g_app.want_toggle_expanded = false;
|
||||
g_app.toggle_expanded_id.clear();
|
||||
}
|
||||
// Cierre via X de la ventana -> bajar expanded en BD.
|
||||
for (auto it = g_app.table_windows.begin(); it != g_app.table_windows.end(); ) {
|
||||
if (!it->second.open && !g_input_path.empty()) {
|
||||
ge::tableview_set_expanded(g_input_path.c_str(),
|
||||
it->first.c_str(), false);
|
||||
it = g_app.table_windows.erase(it);
|
||||
} else ++it;
|
||||
}
|
||||
// Refrescar la pagina si alguna window esta dirty.
|
||||
for (auto& kv : g_app.table_windows) {
|
||||
auto& w = kv.second;
|
||||
if (!w.page_dirty) continue;
|
||||
const auto& m = w.meta;
|
||||
ge::tableview_count(m.duckdb_path_abs.c_str(), m.table_name.c_str(),
|
||||
m.filter_sql.empty() ? nullptr : m.filter_sql.c_str(),
|
||||
&w.total_rows);
|
||||
if (m.columns.empty()) {
|
||||
std::vector<std::string> cols;
|
||||
if (ge::tableview_list_columns(m.duckdb_path_abs.c_str(),
|
||||
m.table_name.c_str(), &cols)) {
|
||||
ge::tableview_set_columns(g_input_path.c_str(),
|
||||
m.entity_id.c_str(), cols);
|
||||
w.meta.columns = cols;
|
||||
}
|
||||
}
|
||||
ge::tableview_page(m.duckdb_path_abs.c_str(), m.table_name.c_str(),
|
||||
m.id_column.c_str(), w.meta.columns,
|
||||
m.filter_sql.empty() ? nullptr : m.filter_sql.c_str(),
|
||||
g_input_path.c_str(), m.row_type.c_str(),
|
||||
w.offset, 200, &w.page);
|
||||
w.page_dirty = false;
|
||||
}
|
||||
if (g_app.want_promote_row && !g_app.promote_table_id.empty()
|
||||
&& !g_input_path.empty()) {
|
||||
ge::TableMetadata m;
|
||||
if (ge::tableview_get_metadata(g_input_path.c_str(),
|
||||
g_app.promote_table_id.c_str(), &m)) {
|
||||
char new_id[128] = {};
|
||||
if (ge::tableview_promote_row(g_input_path.c_str(),
|
||||
m.duckdb_path_abs.c_str(),
|
||||
m.table_name.c_str(),
|
||||
g_app.promote_row_id.c_str(),
|
||||
m.row_type.c_str(),
|
||||
m.label_column.c_str(),
|
||||
new_id, sizeof(new_id))) {
|
||||
std::fprintf(stdout, "[promote] %s -> %s\n",
|
||||
g_app.promote_row_id.c_str(), new_id);
|
||||
auto it = g_app.table_windows.find(g_app.promote_table_id);
|
||||
if (it != g_app.table_windows.end()) it->second.page_dirty = true;
|
||||
reload_after_mutation();
|
||||
g_app.want_focus_entity = true;
|
||||
g_app.focus_entity_id = new_id;
|
||||
}
|
||||
}
|
||||
g_app.want_promote_row = false;
|
||||
g_app.promote_table_id.clear();
|
||||
g_app.promote_row_id.clear();
|
||||
}
|
||||
if (g_app.want_demote_entity && !g_app.demote_entity_id.empty()
|
||||
&& !g_input_path.empty()) {
|
||||
if (ge::tableview_demote_row(g_input_path.c_str(),
|
||||
g_app.demote_entity_id.c_str())) {
|
||||
std::fprintf(stdout, "[demote] %s\n", g_app.demote_entity_id.c_str());
|
||||
for (auto& kv : g_app.table_windows) kv.second.page_dirty = true;
|
||||
reload_after_mutation();
|
||||
}
|
||||
g_app.want_demote_entity = false;
|
||||
g_app.demote_entity_id.clear();
|
||||
}
|
||||
if (g_app.want_focus_entity && !g_app.focus_entity_id.empty()) {
|
||||
for (int i = 0; i < g_graph.node_count; ++i) {
|
||||
const char* sid = ge::entity_index_lookup(
|
||||
g_idx, g_graph.nodes[i].user_data);
|
||||
if (sid && g_app.focus_entity_id == sid) {
|
||||
g_app.filter_focus_target = i;
|
||||
graph_viewport_clear_selection(g_graph, g_viewport);
|
||||
graph_viewport_add_to_selection(g_graph, g_viewport, i);
|
||||
g_app.panel_inspector = true;
|
||||
ge::views_inspector_load_draft(g_app, i, sid);
|
||||
g_app.insp_node_idx = i;
|
||||
g_app.insp_entity_id = sid;
|
||||
break;
|
||||
}
|
||||
}
|
||||
g_app.want_focus_entity = false;
|
||||
g_app.focus_entity_id.clear();
|
||||
}
|
||||
if (g_app.want_import) {
|
||||
g_app.want_import = false;
|
||||
g_app.import_error.clear();
|
||||
std::string duck_abs = ge::tableview_resolve_path(
|
||||
g_input_path.c_str(), g_app.import_duckdb_buf);
|
||||
std::string err;
|
||||
if (!ge::tableview_ingest_file(duck_abs.c_str(),
|
||||
g_app.import_path_buf,
|
||||
g_app.import_table_buf,
|
||||
ge::INGEST_AUTO, &err)) {
|
||||
g_app.import_error = "Ingest failed: " + err;
|
||||
} else {
|
||||
char new_id[80] = {};
|
||||
if (ge::tableview_create(g_input_path.c_str(),
|
||||
g_app.import_table_buf,
|
||||
g_app.import_duckdb_buf,
|
||||
g_app.import_table_buf,
|
||||
g_app.import_row_type_buf,
|
||||
new_id, sizeof(new_id))) {
|
||||
std::fprintf(stdout, "[import] %s -> %s\n",
|
||||
g_app.import_path_buf, new_id);
|
||||
g_app.show_import_modal = false;
|
||||
reload_after_mutation();
|
||||
} else {
|
||||
g_app.import_error = "Tabla DuckDB creada pero no se pudo registrar el nodo.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Inspector (issue 0008): sync draft con seleccion + save/discard ----
|
||||
{
|
||||
const auto& sel = g_viewport.selection;
|
||||
@@ -1001,6 +1156,10 @@ static void render() {
|
||||
ImGui::SetNextWindowSize(ImVec2(820.0f, 520.0f), ImGuiCond_FirstUseEver);
|
||||
ge::views_table(g_app);
|
||||
|
||||
// Table node windows (issue 0011) — una por Table expandida.
|
||||
ge::views_table_window(g_app);
|
||||
ge::views_import_dataset_modal(g_app);
|
||||
|
||||
g_first_render = false;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user