fix: improve click handling with proper z-order and coordinate transforms

- UIFrame: Fix coordinate transformation (subtract parent pos, not add)
- UIFrame: Check children in reverse order (highest z-index first)
- UIFrame: Skip invisible elements entirely
- PyScene: Sort elements by z-index before checking clicks
- PyScene: Stop at first element that handles the click
- UIGrid: Implement entity click detection with grid coordinate transform
- UIGrid: Check entities in reverse order, return sprite as target

Click events now correctly respect z-order (top elements get priority),
handle coordinate transforms for nested frames, and support clicking
on grid entities. Elements without click handlers are transparent to
clicks, allowing elements below to receive them.

Note: Click testing requires non-headless mode due to PyScene limitation.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
John McCardle 2025-07-06 12:11:13 -04:00
commit 1c7195a748
3 changed files with 90 additions and 33 deletions

View file

@ -29,26 +29,19 @@ void PyScene::do_mouse_input(std::string button, std::string type)
auto unscaledmousepos = sf::Mouse::getPosition(game->getWindow());
auto mousepos = game->getWindow().mapPixelToCoords(unscaledmousepos);
UIDrawable* target;
for (auto d: *ui_elements)
{
target = d->click_at(sf::Vector2f(mousepos));
if (target)
{
/*
PyObject* args = Py_BuildValue("(iiss)", (int)mousepos.x, (int)mousepos.y, button.c_str(), type.c_str());
PyObject* retval = PyObject_Call(target->click_callable, args, NULL);
if (!retval)
{
std::cout << "click_callable has raised an exception. It's going to STDERR and being dropped:" << std::endl;
PyErr_Print();
PyErr_Clear();
} else if (retval != Py_None)
{
std::cout << "click_callable returned a non-None value. It's not an error, it's just not being saved or used." << std::endl;
}
*/
// Create a sorted copy by z-index (highest first)
std::vector<std::shared_ptr<UIDrawable>> sorted_elements(*ui_elements);
std::sort(sorted_elements.begin(), sorted_elements.end(),
[](const auto& a, const auto& b) { return a->z_index > b->z_index; });
// Check elements in z-order (top to bottom)
for (const auto& element : sorted_elements) {
if (!element->visible) continue;
if (auto target = element->click_at(sf::Vector2f(mousepos))) {
target->click_callable->call(mousepos, button, type);
return; // Stop after first handler
}
}
}