merge: quick/physics-toggle-layout-dropdown — Layout dropdown + Physics toggle

This commit is contained in:
2026-05-01 16:54:27 +02:00
3 changed files with 47 additions and 21 deletions
+4 -2
View File
@@ -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
+39 -18
View File
@@ -458,14 +458,46 @@ void views_toolbar(AppState& app) {
}
toolbar_separator();
ImGui::TextUnformatted("Layout:");
// Layout: <name> ▾ — 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();
}
+4 -1
View File
@@ -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