diff --git a/main.cpp b/main.cpp index 23e1c46..7de09a1 100644 --- a/main.cpp +++ b/main.cpp @@ -216,11 +216,13 @@ static bool load_input() { g_app.types_dirty = false; g_app.types_save_error.clear(); - // Restablecer viewport state (preserva camara user-visible) + // Restablecer viewport state (preserva camara user-visible). Physics + // arrancan en pausa para que las posiciones guardadas no se pierdan; + // el usuario las activa con el boton Physics de la toolbar. g_viewport.selection.clear(); g_viewport.hovered_node = -1; g_viewport.selected_node = -1; - g_viewport.layout_running = true; + g_viewport.layout_running = false; g_viewport.layout_energy = 0.0f; // Posicionar nodos: si todos tienen (x,y)=0, aplicar layout circular como diff --git a/views.cpp b/views.cpp index d45f204..d0fb5a7 100644 --- a/views.cpp +++ b/views.cpp @@ -458,14 +458,46 @@ void views_toolbar(AppState& app) { } toolbar_separator(); - ImGui::TextUnformatted("Layout:"); + // Layout: ▾ — dropdown con todos los layouts + acciones. + { + char btn[96]; + std::snprintf(btn, sizeof(btn), TI_LAYOUT_GRID " Layout: %s", + k_layout_names[app.layout_mode % k_layout_count]); + if (button(btn, ButtonVariant::Secondary)) { + ImGui::OpenPopup("##layout_menu"); + } + if (ImGui::BeginPopup("##layout_menu")) { + ImGui::TextDisabled("Apply layout"); + ImGui::Separator(); + for (int i = 0; i < k_layout_count; ++i) { + bool is_cur = (i == app.layout_mode); + if (ImGui::MenuItem(k_layout_names[i], nullptr, is_cur)) { + app.layout_mode = i; + ++app.apply_layout_tick; + } + } + ImGui::Separator(); + if (ImGui::MenuItem(TI_LAYOUT_GRID " Reset positions (unpin + restart)")) { + app.want_unpin_all = true; + ++app.apply_layout_tick; + } + if (ImGui::MenuItem(TI_DEVICE_FLOPPY " Save current layout")) { + app.want_save_layout = true; + } + ImGui::EndPopup(); + } + } ImGui::SameLine(); - ImGui::SetNextItemWidth(140); - int idx = app.layout_mode; - if (ImGui::Combo("##layout", &idx, k_layout_names, k_layout_count)) { - if (idx != app.layout_mode) { - app.layout_mode = idx; - ++app.apply_layout_tick; + + // Physics ▶ / ⏸ — toggle visible. Solo afecta a layout 'force'. + if (app.viewport) { + const bool running = app.viewport->layout_running; + const char* lbl = running ? TI_PLAYER_PAUSE " Physics: ON" + : TI_PLAYER_PLAY " Physics: OFF"; + ButtonVariant var = running ? ButtonVariant::Primary + : ButtonVariant::Subtle; + if (button(lbl, var)) { + app.viewport->layout_running = !running; } } toolbar_separator(); @@ -476,25 +508,14 @@ void views_toolbar(AppState& app) { if (button(TI_ARROWS_MAXIMIZE " Fit view", ButtonVariant::Subtle)) { app.want_fit = true; } - if (button(TI_DEVICE_FLOPPY " Save layout", ButtonVariant::Subtle)) { - app.want_save_layout = true; - } if (button(TI_REFRESH " Reload", ButtonVariant::Subtle)) { app.want_reload = true; } - if (button(TI_LAYOUT_GRID " Reset layout", ButtonVariant::Subtle)) { - app.want_unpin_all = true; - ++app.apply_layout_tick; - } toolbar_separator(); ImGui::Checkbox("GPU layout", &app.use_gpu); ImGui::SameLine(); ImGui::Checkbox("Labels", &app.labels_enabled); - if (app.viewport) { - ImGui::SameLine(); - ImGui::Checkbox("Run layout", &app.viewport->layout_running); - } toolbar_end(); } diff --git a/views.h b/views.h index 41c2663..62d4024 100644 --- a/views.h +++ b/views.h @@ -24,7 +24,10 @@ struct AppState { // Layout activo — default grid (1) para que los grafos cargados de // operations.db se distribuyan ordenadamente al abrir. - int layout_mode = 1; // 0=force, 1=grid, 2=circular, 3=radial, 4=hierarchical, 5=fixed + // Default: fixed (5) — respeta posiciones guardadas y physics off por + // defecto. El usuario activa fisicas con el boton Physics y/o cambia + // layout desde el dropdown Layout en la toolbar. + int layout_mode = 5; // 0=force, 1=grid, 2=circular, 3=radial, 4=hierarchical, 5=fixed int apply_layout_tick = 0; // se incrementa cuando hay que reaplicar layout bool want_unpin_all = false; // Reset layout: limpia NF_PINNED y reaplica