feat: Implement comprehensive mouse event system
Implements multiple mouse event improvements for UI elements: - Mouse enter/exit events (#140): on_enter, on_exit callbacks and hovered property for all UIDrawable types (Frame, Caption, Sprite, Grid) - Headless click events (#111): Track simulated mouse position for automation testing in headless mode - Mouse move events (#141): on_move callback fires continuously while mouse is within element bounds - Grid cell events (#142): on_cell_enter, on_cell_exit, on_cell_click callbacks with cell coordinates (x, y), plus hovered_cell property Includes comprehensive tests for all new functionality. Closes #140, closes #111, closes #141, closes #142 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
6d5a5e9e16
commit
6c496b8732
14 changed files with 1353 additions and 27 deletions
|
|
@ -6,6 +6,14 @@
|
|||
#include <sstream>
|
||||
#include <unordered_map>
|
||||
|
||||
// #111 - Static member for simulated mouse position in headless mode
|
||||
sf::Vector2i McRFPy_Automation::simulated_mouse_pos(0, 0);
|
||||
|
||||
// #111 - Get simulated mouse position for headless mode
|
||||
sf::Vector2i McRFPy_Automation::getSimulatedMousePosition() {
|
||||
return simulated_mouse_pos;
|
||||
}
|
||||
|
||||
// Helper function to get game engine
|
||||
GameEngine* McRFPy_Automation::getGameEngine() {
|
||||
return McRFPy_API::game;
|
||||
|
|
@ -106,10 +114,17 @@ sf::Keyboard::Key McRFPy_Automation::stringToKey(const std::string& keyName) {
|
|||
void McRFPy_Automation::injectMouseEvent(sf::Event::EventType type, int x, int y, sf::Mouse::Button button) {
|
||||
auto engine = getGameEngine();
|
||||
if (!engine) return;
|
||||
|
||||
|
||||
// #111 - Track simulated mouse position for headless mode
|
||||
if (type == sf::Event::MouseMoved ||
|
||||
type == sf::Event::MouseButtonPressed ||
|
||||
type == sf::Event::MouseButtonReleased) {
|
||||
simulated_mouse_pos = sf::Vector2i(x, y);
|
||||
}
|
||||
|
||||
sf::Event event;
|
||||
event.type = type;
|
||||
|
||||
|
||||
switch (type) {
|
||||
case sf::Event::MouseMoved:
|
||||
event.mouseMove.x = x;
|
||||
|
|
@ -130,7 +145,7 @@ void McRFPy_Automation::injectMouseEvent(sf::Event::EventType type, int x, int y
|
|||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
engine->processEvent(event);
|
||||
}
|
||||
|
||||
|
|
@ -219,18 +234,22 @@ PyObject* McRFPy_Automation::_screenshot(PyObject* self, PyObject* args) {
|
|||
PyObject* McRFPy_Automation::_position(PyObject* self, PyObject* args) {
|
||||
auto engine = getGameEngine();
|
||||
if (!engine || !engine->getRenderTargetPtr()) {
|
||||
return Py_BuildValue("(ii)", 0, 0);
|
||||
return Py_BuildValue("(ii)", simulated_mouse_pos.x, simulated_mouse_pos.y);
|
||||
}
|
||||
|
||||
// In headless mode, we'd need to track the simulated mouse position
|
||||
// For now, return the actual mouse position relative to window if available
|
||||
|
||||
// In headless mode, return the simulated mouse position (#111)
|
||||
if (engine->isHeadless()) {
|
||||
return Py_BuildValue("(ii)", simulated_mouse_pos.x, simulated_mouse_pos.y);
|
||||
}
|
||||
|
||||
// In windowed mode, return the actual mouse position relative to window
|
||||
if (auto* window = dynamic_cast<sf::RenderWindow*>(engine->getRenderTargetPtr())) {
|
||||
sf::Vector2i pos = sf::Mouse::getPosition(*window);
|
||||
return Py_BuildValue("(ii)", pos.x, pos.y);
|
||||
}
|
||||
|
||||
// In headless mode, return simulated position (TODO: track this)
|
||||
return Py_BuildValue("(ii)", 0, 0);
|
||||
|
||||
// Fallback to simulated position
|
||||
return Py_BuildValue("(ii)", simulated_mouse_pos.x, simulated_mouse_pos.y);
|
||||
}
|
||||
|
||||
// Get screen size
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue