diff --git a/src/PyScene.cpp b/src/PyScene.cpp index c5ae5d6..a05a395 100644 --- a/src/PyScene.cpp +++ b/src/PyScene.cpp @@ -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> 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 } } } diff --git a/src/UIFrame.cpp b/src/UIFrame.cpp index a46e31b..7a3c842 100644 --- a/src/UIFrame.cpp +++ b/src/UIFrame.cpp @@ -11,18 +11,31 @@ UIDrawable* UIFrame::click_at(sf::Vector2f point) { - for (auto e: *children) - { - auto p = e->click_at(point + box.getPosition()); - if (p) - return p; + // Check bounds first (optimization) + float x = box.getPosition().x, y = box.getPosition().y, w = box.getSize().x, h = box.getSize().y; + if (point.x < x || point.y < y || point.x >= x+w || point.y >= y+h) { + return nullptr; } - if (click_callable) - { - float x = box.getPosition().x, y = box.getPosition().y, w = box.getSize().x, h = box.getSize().y; - if (point.x > x && point.y > y && point.x < x+w && point.y < y+h) return this; + + // Transform to local coordinates for children + sf::Vector2f localPoint = point - box.getPosition(); + + // Check children in reverse order (top to bottom, highest z-index first) + for (auto it = children->rbegin(); it != children->rend(); ++it) { + auto& child = *it; + if (!child->visible) continue; + + if (auto target = child->click_at(localPoint)) { + return target; + } } - return NULL; + + // No child handled it, check if we have a handler + if (click_callable) { + return this; + } + + return nullptr; } UIFrame::UIFrame() diff --git a/src/UIGrid.cpp b/src/UIGrid.cpp index 1cdad6c..7a592e2 100644 --- a/src/UIGrid.cpp +++ b/src/UIGrid.cpp @@ -265,11 +265,62 @@ std::shared_ptr UIGrid::getTexture() UIDrawable* UIGrid::click_at(sf::Vector2f point) { - if (click_callable) - { - if(box.getGlobalBounds().contains(point)) return this; + // Check grid bounds first + if (!box.getGlobalBounds().contains(point)) { + return nullptr; } - return NULL; + + // Transform to local coordinates + sf::Vector2f localPoint = point - box.getPosition(); + + // Get cell dimensions + int cell_width = ptex ? ptex->sprite_width : DEFAULT_CELL_WIDTH; + int cell_height = ptex ? ptex->sprite_height : DEFAULT_CELL_HEIGHT; + + // Calculate visible area parameters (from render function) + float center_x_sq = center_x / cell_width; + float center_y_sq = center_y / cell_height; + float width_sq = box.getSize().x / (cell_width * zoom); + float height_sq = box.getSize().y / (cell_height * zoom); + + int left_spritepixels = center_x - (box.getSize().x / 2.0 / zoom); + int top_spritepixels = center_y - (box.getSize().y / 2.0 / zoom); + + // Convert click position to grid coordinates + float grid_x = (localPoint.x / zoom + left_spritepixels) / cell_width; + float grid_y = (localPoint.y / zoom + top_spritepixels) / cell_height; + + // Check entities in reverse order (assuming they should be checked top to bottom) + // Note: entities list is not sorted by z-index currently, but we iterate in reverse + // to match the render order assumption + if (entities) { + for (auto it = entities->rbegin(); it != entities->rend(); ++it) { + auto& entity = *it; + if (!entity || !entity->sprite.visible) continue; + + // Check if click is within entity's grid cell + // Entities occupy a 1x1 grid cell centered on their position + float dx = grid_x - entity->position.x; + float dy = grid_y - entity->position.y; + + if (dx >= -0.5f && dx < 0.5f && dy >= -0.5f && dy < 0.5f) { + // Click is within the entity's cell + // Check if entity sprite has a click handler + // For now, we return the entity's sprite as the click target + // Note: UIEntity doesn't derive from UIDrawable, so we check its sprite + if (entity->sprite.click_callable) { + return &entity->sprite; + } + } + } + } + + // No entity handled it, check if grid itself has handler + if (click_callable) { + return this; + } + + return nullptr; }