Mouse input prototype

This commit is contained in:
John McCardle 2024-03-06 21:12:26 -05:00
parent 6a1edfe182
commit 9587218b28
4 changed files with 200 additions and 98 deletions

View File

@ -4,14 +4,45 @@
PyScene::PyScene(GameEngine* g) : Scene(g) 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::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) 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() void PyScene::sRender()
@ -26,6 +57,4 @@ void PyScene::sRender()
} }
game->getWindow().display(); game->getWindow().display();
McRFPy_API::REPL();
} }

View File

@ -12,4 +12,6 @@ public:
void update() override final; void update() override final;
void doAction(std::string, std::string) override final; void doAction(std::string, std::string) override final;
void sRender() override final; void sRender() override final;
void do_mouse_input(std::string, std::string);
}; };

View File

@ -2,15 +2,88 @@
#include "Resources.h" #include "Resources.h"
#include "GameEngine.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() void UIDrawable::render()
{ {
//std::cout << "Rendering base UIDrawable\n"; //std::cout << "Rendering base UIDrawable\n";
render(sf::Vector2f()); render(sf::Vector2f());
} }
UIFrame::UIFrame(): 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<std::vector<std::shared_ptr<UIDrawable>>>(); children = std::make_shared<std::vector<std::shared_ptr<UIDrawable>>>();
box.setPosition(0, 0);
box.setSize(sf::Vector2f(0, 0));
/* /*
pyOutlineColor = NULL; pyOutlineColor = NULL;
pyFillColor = 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): 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<std::vector<std::shared_ptr<UIDrawable>>>(); children = std::make_shared<std::vector<std::shared_ptr<UIDrawable>>>();
/* /*
pyOutlineColor = NULL; pyOutlineColor = NULL;
@ -52,6 +128,7 @@ UIFrame::~UIFrame()
void outlineColor(PyObject* pyColor); // Python setter void outlineColor(PyObject* pyColor); // Python setter
*/ */
PyObjectsEnum UIFrame::derived_type() PyObjectsEnum UIFrame::derived_type()
{ {
return PyObjectsEnum::UIFRAME; return PyObjectsEnum::UIFRAME;
@ -386,93 +463,3 @@ PyObjectsEnum UIGrid::derived_type()
{ {
return PyObjectsEnum::UIGRID; return PyObjectsEnum::UIGRID;
} }
PyObject* DEFUNCT_py_instance(std::shared_ptr<UIDrawable> 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<PyUICollectionObject*>(_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<PyColorObject*>(pyColor);
pyColorObj->data = std::make_shared<sf::Color>();
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<UIFrame>(source);
std::cout << "casted. Assigning...\n" << std::flush;
//o->data = std::make_shared<UIFrame>();
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<UICaption>(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<UISprite>(source);
*/
break;
}
default:
return NULL;
break;
}
return newobj;
}

View File

@ -5,7 +5,7 @@
#include "IndexTexture.h" #include "IndexTexture.h"
#include <list> #include <list>
enum PyObjectsEnum enum PyObjectsEnum : int
{ {
UIFRAME = 1, UIFRAME = 1,
UICAPTION, UICAPTION,
@ -21,9 +21,17 @@ public:
virtual void render(sf::Vector2f) = 0; virtual void render(sf::Vector2f) = 0;
//virtual sf::Rect<int> aabb(); // not sure I care about this yet //virtual sf::Rect<int> aabb(); // not sure I care about this yet
//virtual sf::Vector2i position(); //virtual sf::Vector2i position();
bool handle_event(/* ??? click, scroll, keystroke*/); //bool handle_event(/* ??? click, scroll, keystroke*/);
std::string action; //std::string action;
virtual PyObjectsEnum derived_type() = 0; 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 //Python object types & forward declarations
@ -46,14 +54,17 @@ public:
UIFrame(); UIFrame();
~UIFrame(); ~UIFrame();
sf::RectangleShape box; 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 //Simulate RectangleShape
float x, y, w, h, outline; //float x, y, w, h,
float outline;
std::shared_ptr<std::vector<std::shared_ptr<UIDrawable>>> children; std::shared_ptr<std::vector<std::shared_ptr<UIDrawable>>> children;
void render(sf::Vector2f) override final; void render(sf::Vector2f) override final;
void move(sf::Vector2f); void move(sf::Vector2f);
PyObjectsEnum derived_type() override final; // { return PyObjectsEnum::UIFrame; }; PyObjectsEnum derived_type() override final; // { return PyObjectsEnum::UIFrame; };
virtual UIDrawable* click_at(sf::Vector2f point) override final;
/* /*
sf::Color fillColor(); // getter sf::Color fillColor(); // getter
void fillColor(sf::Color c); // C++ setter void fillColor(sf::Color c); // C++ setter
@ -78,6 +89,7 @@ public:
sf::Text text; sf::Text text;
void render(sf::Vector2f) override final; void render(sf::Vector2f) override final;
PyObjectsEnum derived_type() override final; // { return PyObjectsEnum::UICaption; }; PyObjectsEnum derived_type() override final; // { return PyObjectsEnum::UICaption; };
virtual UIDrawable* click_at(sf::Vector2f point) override final;
}; };
class UISprite: public UIDrawable class UISprite: public UIDrawable
@ -88,6 +100,7 @@ public:
UISprite(IndexTexture*, int, sf::Vector2f, float); UISprite(IndexTexture*, int, sf::Vector2f, float);
void update(); void update();
void render(sf::Vector2f) override final; 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`) // 7DRL hack - TODO apply RenderTexture concept to all UIDrawables (via `sf::RenderTarget`)
void render(sf::Vector2f, sf::RenderTexture&); void render(sf::Vector2f, sf::RenderTexture&);
@ -148,6 +161,7 @@ public:
UIGridPoint& at(int, int); UIGridPoint& at(int, int);
PyObjectsEnum derived_type() override final; PyObjectsEnum derived_type() override final;
void setSprite(int); void setSprite(int);
virtual UIDrawable* click_at(sf::Vector2f point) override final;
int grid_x, grid_y; int grid_x, grid_y;
//int grid_size; // grid sizes are implied by IndexTexture now //int grid_size; // grid sizes are implied by IndexTexture now
@ -297,6 +311,71 @@ switch (target->derived_type()) \
} PyUISpriteObject; } PyUISpriteObject;
*/ */
//
// Clickable / Callable Object Assignment
//
static PyObject* PyUIDrawable_get_click(PyUIGridObject* self, void* closure) {
PyObjectsEnum objtype = static_cast<PyObjectsEnum>(reinterpret_cast<long>(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<PyObjectsEnum>(reinterpret_cast<long>(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 * 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}, //{"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}, {"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}, {"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} {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}, {"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}, {"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}, {"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} {NULL}
}; };
@ -1108,6 +1189,7 @@ switch (target->derived_type()) \
{"scale", (getter)PyUISprite_get_float_member, (setter)PyUISprite_set_float_member, "Size factor", (void*)2}, {"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}, {"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}, {"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} {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}, {"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}, {"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 //{"texture", (getter)PyUIGrid_get_texture, NULL, "Texture of the grid", NULL}, //TODO 7DRL-day2-item5
{NULL} /* Sentinel */ {NULL} /* Sentinel */
}; };