diff --git a/src/PyScene.cpp b/src/PyScene.cpp index 40c4b43..66571fc 100644 --- a/src/PyScene.cpp +++ b/src/PyScene.cpp @@ -4,14 +4,45 @@ PyScene::PyScene(GameEngine* g) : Scene(g) { + // mouse events + registerAction(ActionCode::MOUSEBUTTON + sf::Mouse::Left, "left"); + registerAction(ActionCode::MOUSEBUTTON + sf::Mouse::Right, "right"); + registerAction(ActionCode::MOUSEWHEEL + ActionCode::WHEEL_DEL, "wheel_up"); + registerAction(ActionCode::MOUSEWHEEL + ActionCode::WHEEL_NEG + ActionCode::WHEEL_DEL, "wheel_down"); + + registerAction(ActionCode::KEY + sf::Keyboard::Escape, "debug_menu"); } void PyScene::update() { } +void PyScene::do_mouse_input(std::string button, std::string type) +{ + auto mousepos = sf::Mouse::getPosition(game->getWindow()); + UIDrawable* target; + for (auto d: *ui_elements) + { + target = d->click_at(sf::Vector2f(mousepos)); + if (target) + { + PyObject* args = Py_BuildValue("(iiss)", mousepos.x, mousepos.y, button.c_str(), type.c_str()); + PyObject_Call(target->click_callable, args, NULL); + } + } +} + void PyScene::doAction(std::string name, std::string type) { + if (ACTIONPY) { + McRFPy_API::doAction(name.substr(0, name.size() - 3)); + } + else if (name.compare("left") == 0 || name.compare("rclick") == 0 || name.compare("wheel_up") == 0 || name.compare("wheel_down") == 0) { + do_mouse_input(name, type); + } + else if ACTIONONCE("debug_menu") { + McRFPy_API::REPL(); + } } void PyScene::sRender() @@ -26,6 +57,4 @@ void PyScene::sRender() } game->getWindow().display(); - - McRFPy_API::REPL(); } diff --git a/src/PyScene.h b/src/PyScene.h index 8b2b8f9..e502fc2 100644 --- a/src/PyScene.h +++ b/src/PyScene.h @@ -12,4 +12,6 @@ public: void update() override final; void doAction(std::string, std::string) override final; void sRender() override final; + + void do_mouse_input(std::string, std::string); }; diff --git a/src/UI.cpp b/src/UI.cpp index 4ff2077..eeb2354 100644 --- a/src/UI.cpp +++ b/src/UI.cpp @@ -2,15 +2,88 @@ #include "Resources.h" #include "GameEngine.h" +/* //callability fields & methods + PyObject* click_callable; + virtual UIDrawable* click_at(sf::Vector2f point); + void click_register(PyObject*); + void click_unregister(); +*/ + +UIDrawable::UIDrawable() { click_callable = NULL; } + +UIDrawable* UIFrame::click_at(sf::Vector2f point) +{ + for (auto e: *children) + { + auto p = e->click_at(point + box.getPosition()); + if (p) + return p; + } + 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; + } + return NULL; +} + +UIDrawable* UICaption::click_at(sf::Vector2f point) +{ + if (click_callable) + { + if (text.getGlobalBounds().contains(point)) return this; + } + return NULL; +} + +UIDrawable* UISprite::click_at(sf::Vector2f point) +{ + if (click_callable) + { + if(sprite.getGlobalBounds().contains(point)) return this; + } + return NULL; +} + +UIDrawable* UIGrid::click_at(sf::Vector2f point) +{ + if (click_callable) + { + if(box.getGlobalBounds().contains(point)) return this; + } + return NULL; +} + +void UIDrawable::click_register(PyObject* callable) +{ + if (click_callable) + { + // decrement reference before overwriting + Py_DECREF(click_callable); + } + click_callable = callable; + Py_INCREF(click_callable); +} + +void UIDrawable::click_unregister() +{ + if (click_callable == NULL) return; + Py_DECREF(click_callable); + click_callable = NULL; +} + void UIDrawable::render() { //std::cout << "Rendering base UIDrawable\n"; render(sf::Vector2f()); } UIFrame::UIFrame(): -x(0), y(0), w(0), h(0), outline(0) +//x(0), y(0), w(0), h(0), +outline(0) { children = std::make_shared>>(); + box.setPosition(0, 0); + box.setSize(sf::Vector2f(0, 0)); /* pyOutlineColor = NULL; pyFillColor = NULL; @@ -20,8 +93,11 @@ x(0), y(0), w(0), h(0), outline(0) } UIFrame::UIFrame(float _x, float _y, float _w, float _h): -x(_x), y(_y), w(_w), h(_h), outline(0) +//x(_x), y(_y), w(_w), h(_h), +outline(0) { + box.setPosition(_x, _y); + box.setSize(sf::Vector2f(_w, _h)); children = std::make_shared>>(); /* pyOutlineColor = NULL; @@ -52,6 +128,7 @@ UIFrame::~UIFrame() void outlineColor(PyObject* pyColor); // Python setter */ + PyObjectsEnum UIFrame::derived_type() { return PyObjectsEnum::UIFRAME; @@ -386,93 +463,3 @@ PyObjectsEnum UIGrid::derived_type() { return PyObjectsEnum::UIGRID; } - -PyObject* DEFUNCT_py_instance(std::shared_ptr source) -{ - // takes a UI drawable, calls its derived_type virtual function, and builds a Python object based on the return value. - using namespace mcrfpydef; - - PyObject* newobj = NULL; - std::cout << "py_instance called\n"; - switch (source->derived_type()) - { - case PyObjectsEnum::UIFRAME: - { - std::cout << "UIFRAME case\n" << std::flush; - PyTypeObject* UIFrameType = &PyUIFrameType; - //std::cout << "tp_alloc\n" << std::flush; - //PyObject* _o = UIFrameType->tp_alloc(UIFrameType, 0); - //std::cout << "reinterpret_cast\n" << std::flush; - //auto o = reinterpret_cast(_o); - //PyUIFrameObject* o = (PyUIFrameObject*)PyObject_New(PyUIFrameObject, UIFrameType); - - PyUIFrameObject* o = (PyUIFrameObject*)(UIFrameType->tp_alloc(UIFrameType, 0)); - //PyUIFrameObject* o = PyObject_New(PyUIFrameObject, UIFrameType); - - /* - // backtracking the problem: instantiate a PyColor of (255, 0, 0) for testing - PyTypeObject* colorType = &PyColorType; - PyObject* pyColor = colorType->tp_alloc(colorType, 0); - if (pyColor == NULL) - { - std::cout << "failure to allocate mcrfpy.Color / PyColorType" << std::endl; - return NULL; - } - PyColorObject* pyColorObj = reinterpret_cast(pyColor); - pyColorObj->data = std::make_shared(); - pyColorObj->data-> r = 255; - return (PyObject*)pyColorObj; - */ - - - std::cout << "pointer check: " << o << "\n" << std::flush; - if (o) - { - std::cout << "Casting data...\n" << std::flush; - auto p = std::static_pointer_cast(source); - std::cout << "casted. Assigning...\n" << std::flush; - //o->data = std::make_shared(); - - o->data = p; - //std::cout << "assigned.\n" << std::flush; - auto usource = o->data; //(UIFrame*)source.get(); - std::cout << "Loaded data into object. " << usource->box.getPosition().x << " " << usource->box.getPosition().y << " " << - usource->box.getSize().x << " " << usource->box.getSize().y << std::endl; - } - else - { - std::cout << "Allocation failed.\n" << std::flush; - } - newobj = (PyObject*)o; - break; - - } - case PyObjectsEnum::UICAPTION: - { - std::cout << "UICAPTION case\n"; - PyErr_SetString(PyExc_NotImplementedError, "UICaption class not implemented in Python yet."); - /* not yet implemented - PyUICaptionObject* o = (PyUICaptionObject*)PyUICaptionType.tp_alloc(&PyUICaptionType, 0); - if (o) - o->data = std::static_pointer_cast(source); - */ - break; - } - case PyObjectsEnum::UISPRITE: - { - std::cout << "UISPRITE case\n"; - PyErr_SetString(PyExc_NotImplementedError, "UISprite class not implemented in Python yet."); - /* - PyUISpriteObject* o = (PyUISpriteObject*)PyUISpriteType.tp_alloc(&PyUISpriteType, 0); - if (o) - o->data = std::static_pointer_cast(source); - */ - break; - } - default: - return NULL; - break; - } - - return newobj; -} diff --git a/src/UI.h b/src/UI.h index 2c655db..fee24fe 100644 --- a/src/UI.h +++ b/src/UI.h @@ -5,7 +5,7 @@ #include "IndexTexture.h" #include -enum PyObjectsEnum +enum PyObjectsEnum : int { UIFRAME = 1, UICAPTION, @@ -21,9 +21,17 @@ public: virtual void render(sf::Vector2f) = 0; //virtual sf::Rect aabb(); // not sure I care about this yet //virtual sf::Vector2i position(); - bool handle_event(/* ??? click, scroll, keystroke*/); - std::string action; + //bool handle_event(/* ??? click, scroll, keystroke*/); + //std::string action; virtual PyObjectsEnum derived_type() = 0; + + // Mouse input handling - callable object, methods to find event's destination + PyObject* click_callable; + virtual UIDrawable* click_at(sf::Vector2f point) = 0; + void click_register(PyObject*); + void click_unregister(); + + UIDrawable(); }; //Python object types & forward declarations @@ -46,14 +54,17 @@ public: UIFrame(); ~UIFrame(); sf::RectangleShape box; + // todone: why does UIFrame have x,y,w,h AND a box? Which one should be used for bounds checks? (floats removed) //Simulate RectangleShape - float x, y, w, h, outline; + //float x, y, w, h, + float outline; std::shared_ptr>> children; void render(sf::Vector2f) override final; void move(sf::Vector2f); PyObjectsEnum derived_type() override final; // { return PyObjectsEnum::UIFrame; }; + virtual UIDrawable* click_at(sf::Vector2f point) override final; /* sf::Color fillColor(); // getter void fillColor(sf::Color c); // C++ setter @@ -78,6 +89,7 @@ public: sf::Text text; void render(sf::Vector2f) override final; PyObjectsEnum derived_type() override final; // { return PyObjectsEnum::UICaption; }; + virtual UIDrawable* click_at(sf::Vector2f point) override final; }; class UISprite: public UIDrawable @@ -88,6 +100,7 @@ public: UISprite(IndexTexture*, int, sf::Vector2f, float); void update(); void render(sf::Vector2f) override final; + virtual UIDrawable* click_at(sf::Vector2f point) override final; // 7DRL hack - TODO apply RenderTexture concept to all UIDrawables (via `sf::RenderTarget`) void render(sf::Vector2f, sf::RenderTexture&); @@ -148,6 +161,7 @@ public: UIGridPoint& at(int, int); PyObjectsEnum derived_type() override final; void setSprite(int); + virtual UIDrawable* click_at(sf::Vector2f point) override final; int grid_x, grid_y; //int grid_size; // grid sizes are implied by IndexTexture now @@ -297,6 +311,71 @@ switch (target->derived_type()) \ } PyUISpriteObject; */ +// +// Clickable / Callable Object Assignment +// +static PyObject* PyUIDrawable_get_click(PyUIGridObject* self, void* closure) { + PyObjectsEnum objtype = static_cast(reinterpret_cast(closure)); // trust me bro, it's an Enum + PyObject* ptr; + + switch (objtype) + { + case PyObjectsEnum::UIFRAME: + ptr = ((PyUIFrameObject*)self)->data->click_callable; + break; + case PyObjectsEnum::UICAPTION: + ptr = ((PyUICaptionObject*)self)->data->click_callable; + break; + case PyObjectsEnum::UISPRITE: + ptr = ((PyUISpriteObject*)self)->data->click_callable; + break; + case PyObjectsEnum::UIGRID: + ptr = ((PyUIGridObject*)self)->data->click_callable; + break; + default: + PyErr_SetString(PyExc_TypeError, "no idea how you did that; invalid UIDrawable derived instance for _get_click"); + return NULL; + } + if (ptr && ptr != Py_None) + return ptr; + else + return Py_None; +} + +static int PyUIDrawable_set_click(PyUIGridObject* self, PyObject* value, void* closure) { + PyObjectsEnum objtype = static_cast(reinterpret_cast(closure)); // trust me bro, it's an Enum + UIDrawable* target; + switch (objtype) + { + case PyObjectsEnum::UIFRAME: + target = (((PyUIFrameObject*)self)->data.get()); + break; + case PyObjectsEnum::UICAPTION: + target = (((PyUICaptionObject*)self)->data.get()); + break; + case PyObjectsEnum::UISPRITE: + target = (((PyUISpriteObject*)self)->data.get()); + break; + case PyObjectsEnum::UIGRID: + target = (((PyUIGridObject*)self)->data.get()); + break; + default: + PyErr_SetString(PyExc_TypeError, "no idea how you did that; invalid UIDrawable derived instance for _set_click"); + return -1; + } + + if (value == Py_None) + { + target->click_unregister(); + } else { + target->click_register(value); + } + return 0; +} + +// End Clickability implementation + + /* * * Begin PyFontType defs @@ -598,6 +677,7 @@ switch (target->derived_type()) \ //{"children", (getter)PyUIFrame_get_children, NULL, "UICollection of objects on top of this one", NULL}, {"text", (getter)PyUICaption_get_text, (setter)PyUICaption_set_text, "The text displayed", NULL}, {"size", (getter)PyUICaption_get_float_member, (setter)PyUICaption_set_float_member, "Text size (integer) in points", (void*)5}, + {"click", (getter)PyUIDrawable_get_click, (setter)PyUIDrawable_set_click, "Object called with (x, y, button) when clicked", (void*)PyObjectsEnum::UICAPTION}, {NULL} }; @@ -865,6 +945,7 @@ switch (target->derived_type()) \ {"fill_color", (getter)PyUIFrame_get_color_member, (setter)PyUIFrame_set_color_member, "Fill color of the rectangle", (void*)0}, {"outline_color", (getter)PyUIFrame_get_color_member, (setter)PyUIFrame_set_color_member, "Outline color of the rectangle", (void*)1}, {"children", (getter)PyUIFrame_get_children, NULL, "UICollection of objects on top of this one", NULL}, + {"click", (getter)PyUIDrawable_get_click, (setter)PyUIDrawable_set_click, "Object called with (x, y, button) when clicked", (void*)PyObjectsEnum::UIFRAME}, {NULL} }; @@ -1108,6 +1189,7 @@ switch (target->derived_type()) \ {"scale", (getter)PyUISprite_get_float_member, (setter)PyUISprite_set_float_member, "Size factor", (void*)2}, {"sprite_number", (getter)PyUISprite_get_int_member, (setter)PyUISprite_set_int_member, "Which sprite on the texture is shown", NULL}, {"texture", (getter)PyUISprite_get_texture, (setter)PyUISprite_set_texture, "Texture object", NULL}, + {"click", (getter)PyUIDrawable_get_click, (setter)PyUIDrawable_set_click, "Object called with (x, y, button) when clicked", (void*)PyObjectsEnum::UISPRITE}, {NULL} }; @@ -1710,6 +1792,8 @@ static PyGetSetDef PyUIGrid_getsetters[] = { {"center_y", (getter)PyUIGrid_get_float_member, (setter)PyUIGrid_set_float_member, "center of the view Y-coordinate", (void*)5}, {"zoom", (getter)PyUIGrid_get_float_member, (setter)PyUIGrid_set_float_member, "zoom factor for displaying the Grid", (void*)6}, + {"click", (getter)PyUIDrawable_get_click, (setter)PyUIDrawable_set_click, "Object called with (x, y, button) when clicked", (void*)PyObjectsEnum::UIGRID}, + //{"texture", (getter)PyUIGrid_get_texture, NULL, "Texture of the grid", NULL}, //TODO 7DRL-day2-item5 {NULL} /* Sentinel */ };