| 595 | } |
| 596 | |
| 597 | void PyMenuRegistry::execute_menu_callback(const std::string& idname, int callback_index) { |
| 598 | PyMenuClassInfo* target = nullptr; |
| 599 | { |
| 600 | std::lock_guard lock(mutex_); |
| 601 | for (auto& mc : menu_classes_) { |
| 602 | if (mc.idname == idname) { |
| 603 | target = &mc; |
| 604 | break; |
| 605 | } |
| 606 | } |
| 607 | } |
| 608 | |
| 609 | if (!target || !target->menu_instance.is_valid()) |
| 610 | return; |
| 611 | |
| 612 | nb::gil_scoped_acquire gil; |
| 613 | |
| 614 | try { |
| 615 | bool should_draw = true; |
| 616 | if (nb::hasattr(target->menu_class, "poll")) |
| 617 | should_draw = nb::cast<bool>(target->menu_class.attr("poll")(nb::none())); |
| 618 | |
| 619 | if (!should_draw) |
| 620 | return; |
| 621 | |
| 622 | if (has_schema_menu_items(*target)) { |
| 623 | int next_callback_index = 0; |
| 624 | execute_schema_callback( |
| 625 | get_schema_items(*target), target->menu_instance, |
| 626 | callback_index, next_callback_index); |
| 627 | } else if (nb::hasattr(target->menu_instance, "draw")) { |
| 628 | warn_legacy_menu_draw_once(idname); |
| 629 | vis::gui::MenuDropdownContent dummy; |
| 630 | PyUILayout layout(1); |
| 631 | layout.setCollecting(&dummy); |
| 632 | layout.setExecuteAtIndex(callback_index); |
| 633 | target->menu_instance.attr("draw")(layout); |
| 634 | } |
| 635 | } catch (const std::exception& e) { |
| 636 | LOG_ERROR("Menu '{}' callback execution error: {}", idname, e.what()); |
| 637 | } |
| 638 | } |
| 639 | |
| 640 | void register_ui_menus(nb::module_& m) { |
| 641 | nb::enum_<MenuLocation>(m, "MenuLocation") |
no test coverage detected