From 83a63a3093d206fa2d00c7bf278b78de0a060b92 Mon Sep 17 00:00:00 2001 From: John McCardle Date: Mon, 8 Apr 2024 22:45:00 -0400 Subject: [PATCH 01/14] doesn't compile, but UI.h/.cpp code has been divvy'd up. refs #43 @2h --- src/UI.cpp | 1038 ++++----- src/UI.h | 4748 +++++++++++++++++++++--------------------- src/UICaption.cpp | 22 + src/UICaption.h | 304 +++ src/UICollection.cpp | 2 + src/UICollection.h | 279 +++ src/UIDrawable.cpp | 13 + src/UIDrawable.h | 162 ++ src/UIEntity.cpp | 9 + src/UIEntity.h | 40 + src/UIFrame.cpp | 69 + src/UIFrame.h | 271 +++ src/UIGrid.cpp | 190 ++ src/UIGrid.h | 570 +++++ src/UIGridPoint.cpp | 6 + src/UIGridPoint.h | 212 ++ src/UISprite.cpp | 81 + src/UISprite.h | 223 ++ 18 files changed, 5329 insertions(+), 2910 deletions(-) create mode 100644 src/UICaption.cpp create mode 100644 src/UICaption.h create mode 100644 src/UICollection.cpp create mode 100644 src/UICollection.h create mode 100644 src/UIDrawable.cpp create mode 100644 src/UIDrawable.h create mode 100644 src/UIEntity.cpp create mode 100644 src/UIEntity.h create mode 100644 src/UIFrame.cpp create mode 100644 src/UIFrame.h create mode 100644 src/UIGrid.cpp create mode 100644 src/UIGrid.h create mode 100644 src/UIGridPoint.cpp create mode 100644 src/UIGridPoint.h create mode 100644 src/UISprite.cpp create mode 100644 src/UISprite.h diff --git a/src/UI.cpp b/src/UI.cpp index 5d43918..d56dc4a 100644 --- a/src/UI.cpp +++ b/src/UI.cpp @@ -1,527 +1,527 @@ -#include "UI.h" -#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); - */ - click_callable = std::make_unique(callable); -} - -void UIDrawable::click_unregister() -{ - /* - if (click_callable == NULL) return; - Py_DECREF(click_callable); - click_callable = NULL; - */ - click_callable.reset(); -} - -void UIDrawable::render() -{ - //std::cout << "Rendering base UIDrawable\n"; - render(sf::Vector2f()); -} -UIFrame::UIFrame(): +//#include "UI.h" +//#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); +// */ +// click_callable = std::make_unique(callable); +//} +// +//void UIDrawable::click_unregister() +//{ +// /* +// if (click_callable == NULL) return; +// Py_DECREF(click_callable); +// click_callable = NULL; +// */ +// click_callable.reset(); +//} +// +//void UIDrawable::render() +//{ +// //std::cout << "Rendering base UIDrawable\n"; +// render(sf::Vector2f()); +//} +//UIFrame::UIFrame(): //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; - _outlineColor = NULL; - _fillColor = NULL; - */ -} - -UIFrame::UIFrame(float _x, float _y, float _w, float _h): +//outline(0) +//{ +// children = std::make_shared>>(); +// box.setPosition(0, 0); +// box.setSize(sf::Vector2f(0, 0)); +// /* +// pyOutlineColor = NULL; +// pyFillColor = NULL; +// _outlineColor = NULL; +// _fillColor = NULL; +// */ +//} +// +//UIFrame::UIFrame(float _x, float _y, float _w, float _h): //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; - pyFillColor = NULL; - _outlineColor = NULL; - _fillColor = NULL; - */ -} - -UIFrame::~UIFrame() -{ - children.reset(); - /* - if (pyOutlineColor) Py_DECREF(pyOutlineColor); - else if (_outlineColor) delete _outlineColor; - if (pyFillColor) Py_DECREF(pyFillColor); - else if (_fillColor) delete _fillColor; - */ -} - -/* - sf::Color& fillColor(); // getter - void fillColor(sf::Color c); // C++ setter - void fillColor(PyObject* pyColor); // Python setter - - sf::Color& outlineColor(); // getter - void outlineColor(sf::Color c); // C++ setter - void outlineColor(PyObject* pyColor); // Python setter -*/ - - -PyObjectsEnum UIFrame::derived_type() -{ - return PyObjectsEnum::UIFRAME; -} - -void UIFrame::render(sf::Vector2f offset) -{ - //std::cout << "Rendering UIFrame w/ offset " << offset.x << ", " << offset.y << "\n"; - //std::cout << "position = " << x << ", " << y << "\n"; - box.move(offset); - Resources::game->getWindow().draw(box); - box.move(-offset); - //sf::RectangleShape box = sf::RectangleShape(sf::Vector2f(w,h)); - //sf::Vector2f pos = sf::Vector2f(x, y); - //box.setPosition(offset + pos); - //if (_fillColor) { box.setFillColor(fillColor()); } - //if (_outlineColor) { box.setOutlineColor(outlineColor()); } - //box.setOutlineThickness(outline); - //Resources::game->getWindow().draw(box); - for (auto drawable : *children) { - drawable->render(offset + box.getPosition()); - } -} - -void UICaption::render(sf::Vector2f offset) -{ - //std::cout << "Rendering Caption with offset\n"; - text.move(offset); - Resources::game->getWindow().draw(text); - text.move(-offset); -} - -UISprite::UISprite() {} -/* - // * tearing down the old IndexTexture way of life -UISprite::UISprite(IndexTexture* _itex, int _sprite_index, float x = 0.0, float y = 0.0, float s = 1.0) -: itex(_itex), sprite_index(_sprite_index) -{ - sprite.setTexture(_itex->texture); - sprite.setTextureRect(_itex->spriteCoordinates(_sprite_index)); - sprite.setPosition(sf::Vector2f(x, y)); - sprite.setScale(sf::Vector2f(s, s)); -} - -UISprite::UISprite(IndexTexture* _itex, int _sprite_index, sf::Vector2f pos, float s = 1.0) -: itex(_itex), sprite_index(_sprite_index) -{ - sprite.setTexture(_itex->texture); - sprite.setTextureRect(_itex->spriteCoordinates(_sprite_index)); - sprite.setPosition(pos); - sprite.setScale(sf::Vector2f(s, s)); -} -*/ - -UISprite::UISprite(std::shared_ptr _ptex, int _sprite_index, sf::Vector2f _pos, float _scale) -: ptex(_ptex), sprite_index(_sprite_index) -{ - sprite = ptex->sprite(sprite_index, _pos, sf::Vector2f(_scale, _scale)); -} - +//outline(0) +//{ +// box.setPosition(_x, _y); +// box.setSize(sf::Vector2f(_w, _h)); +// children = std::make_shared>>(); +// /* +// pyOutlineColor = NULL; +// pyFillColor = NULL; +// _outlineColor = NULL; +// _fillColor = NULL; +// */ +//} +// +//UIFrame::~UIFrame() +//{ +// children.reset(); +// /* +// if (pyOutlineColor) Py_DECREF(pyOutlineColor); +// else if (_outlineColor) delete _outlineColor; +// if (pyFillColor) Py_DECREF(pyFillColor); +// else if (_fillColor) delete _fillColor; +// */ +//} +// +///* +// sf::Color& fillColor(); // getter +// void fillColor(sf::Color c); // C++ setter +// void fillColor(PyObject* pyColor); // Python setter +// +// sf::Color& outlineColor(); // getter +// void outlineColor(sf::Color c); // C++ setter +// void outlineColor(PyObject* pyColor); // Python setter +//*/ +// +// +//PyObjectsEnum UIFrame::derived_type() +//{ +// return PyObjectsEnum::UIFRAME; +//} +// +//void UIFrame::render(sf::Vector2f offset) +//{ +// //std::cout << "Rendering UIFrame w/ offset " << offset.x << ", " << offset.y << "\n"; +// //std::cout << "position = " << x << ", " << y << "\n"; +// box.move(offset); +// Resources::game->getWindow().draw(box); +// box.move(-offset); +// //sf::RectangleShape box = sf::RectangleShape(sf::Vector2f(w,h)); +// //sf::Vector2f pos = sf::Vector2f(x, y); +// //box.setPosition(offset + pos); +// //if (_fillColor) { box.setFillColor(fillColor()); } +// //if (_outlineColor) { box.setOutlineColor(outlineColor()); } +// //box.setOutlineThickness(outline); +// //Resources::game->getWindow().draw(box); +// for (auto drawable : *children) { +// drawable->render(offset + box.getPosition()); +// } +//} +// +//void UICaption::render(sf::Vector2f offset) +//{ +// //std::cout << "Rendering Caption with offset\n"; +// text.move(offset); +// Resources::game->getWindow().draw(text); +// text.move(-offset); +//} +// +//UISprite::UISprite() {} +///* +// // * tearing down the old IndexTexture way of life +//UISprite::UISprite(IndexTexture* _itex, int _sprite_index, float x = 0.0, float y = 0.0, float s = 1.0) +//: itex(_itex), sprite_index(_sprite_index) +//{ +// sprite.setTexture(_itex->texture); +// sprite.setTextureRect(_itex->spriteCoordinates(_sprite_index)); +// sprite.setPosition(sf::Vector2f(x, y)); +// sprite.setScale(sf::Vector2f(s, s)); +//} +// +//UISprite::UISprite(IndexTexture* _itex, int _sprite_index, sf::Vector2f pos, float s = 1.0) +//: itex(_itex), sprite_index(_sprite_index) +//{ +// sprite.setTexture(_itex->texture); +// sprite.setTextureRect(_itex->spriteCoordinates(_sprite_index)); +// sprite.setPosition(pos); +// sprite.setScale(sf::Vector2f(s, s)); +//} +//*/ +// +//UISprite::UISprite(std::shared_ptr _ptex, int _sprite_index, sf::Vector2f _pos, float _scale) +//: ptex(_ptex), sprite_index(_sprite_index) +//{ +// sprite = ptex->sprite(sprite_index, _pos, sf::Vector2f(_scale, _scale)); +//} +// //void UISprite::update() //{ - //auto& tex = Resources::game->textures[texture_index]; - //sprite.setTexture(tex.texture); - //sprite.setScale(sf::Vector2f(scale, scale)); - //sprite.setPosition(sf::Vector2f(x, y)); - //std::cout << "Drawable position: " << x << ", " << y << " -> " << s.getPosition().x << ", " << s.getPosition().y << std::endl; - //sprite.setTextureRect(tex.spriteCoordinates(sprite_index)); +// //auto& tex = Resources::game->textures[texture_index]; +// //sprite.setTexture(tex.texture); +// //sprite.setScale(sf::Vector2f(scale, scale)); +// //sprite.setPosition(sf::Vector2f(x, y)); +// //std::cout << "Drawable position: " << x << ", " << y << " -> " << s.getPosition().x << ", " << s.getPosition().y << std::endl; +// //sprite.setTextureRect(tex.spriteCoordinates(sprite_index)); //} - -void UISprite::render(sf::Vector2f offset) -{ - sprite.move(offset); - Resources::game->getWindow().draw(sprite); - sprite.move(-offset); -} - +// +//void UISprite::render(sf::Vector2f offset) +//{ +// sprite.move(offset); +// Resources::game->getWindow().draw(sprite); +// sprite.move(-offset); +//} +// // 7DRL hack; needed to draw entities to UIGrid. TODO, apply this technique to all UIDrawables -void UISprite::render(sf::Vector2f offset, sf::RenderTexture& target) -{ - sprite.move(offset); - target.draw(sprite); - sprite.move(-offset); -} - -/* -void UISprite::setPosition(float x, float y) -{ - setPosition(sf::Vector2f(x, y)); -} -*/ - -void UISprite::setPosition(sf::Vector2f pos) -{ - sprite.setPosition(pos); -} - -void UISprite::setScale(sf::Vector2f s) -{ - sprite.setScale(s); -} - -void UISprite::setTexture(std::shared_ptr _ptex, int _sprite_index) -{ - ptex = _ptex; - if (_sprite_index != -1) // if you are changing textures, there's a good chance you need a new index too - sprite_index = _sprite_index; - sprite = ptex->sprite(sprite_index, sprite.getPosition(), sprite.getScale()); -} - -void UISprite::setSpriteIndex(int _sprite_index) -{ - sprite_index = _sprite_index; - sprite = ptex->sprite(sprite_index, sprite.getPosition(), sprite.getScale()); -} - -sf::Vector2f UISprite::getScale() -{ - return sprite.getScale(); -} - -sf::Vector2f UISprite::getPosition() -{ - return sprite.getPosition(); -} - -std::shared_ptr UISprite::getTexture() -{ - return ptex; -} - -int UISprite::getSpriteIndex() -{ - return sprite_index; -} - -PyObjectsEnum UICaption::derived_type() -{ - return PyObjectsEnum::UICAPTION; -} - -PyObjectsEnum UISprite::derived_type() -{ - return PyObjectsEnum::UISPRITE; -} - +//void UISprite::render(sf::Vector2f offset, sf::RenderTexture& target) +//{ +// sprite.move(offset); +// target.draw(sprite); +// sprite.move(-offset); +//} +// +///* +//void UISprite::setPosition(float x, float y) +//{ +// setPosition(sf::Vector2f(x, y)); +//} +//*/ +// +//void UISprite::setPosition(sf::Vector2f pos) +//{ +// sprite.setPosition(pos); +//} +// +//void UISprite::setScale(sf::Vector2f s) +//{ +// sprite.setScale(s); +//} +// +//void UISprite::setTexture(std::shared_ptr _ptex, int _sprite_index) +//{ +// ptex = _ptex; +// if (_sprite_index != -1) // if you are changing textures, there's a good chance you need a new index too +// sprite_index = _sprite_index; +// sprite = ptex->sprite(sprite_index, sprite.getPosition(), sprite.getScale()); +//} +// +//void UISprite::setSpriteIndex(int _sprite_index) +//{ +// sprite_index = _sprite_index; +// sprite = ptex->sprite(sprite_index, sprite.getPosition(), sprite.getScale()); +//} +// +//sf::Vector2f UISprite::getScale() +//{ +// return sprite.getScale(); +//} +// +//sf::Vector2f UISprite::getPosition() +//{ +// return sprite.getPosition(); +//} +// +//std::shared_ptr UISprite::getTexture() +//{ +// return ptex; +//} +// +//int UISprite::getSpriteIndex() +//{ +// return sprite_index; +//} +// +//PyObjectsEnum UICaption::derived_type() +//{ +// return PyObjectsEnum::UICAPTION; +//} +// +//PyObjectsEnum UISprite::derived_type() +//{ +// return PyObjectsEnum::UISPRITE; +//} +// // UIGrid support classes' methods - -UIGridPoint::UIGridPoint() -:color(1.0f, 1.0f, 1.0f), color_overlay(0.0f, 0.0f, 0.0f), walkable(false), transparent(false), - tilesprite(-1), tile_overlay(-1), uisprite(-1) -{ -} - -UIEntity::UIEntity() {} // this will not work lol. TODO remove default constructor by finding the shared pointer inits that use it - -UIEntity::UIEntity(UIGrid& grid) -: gridstate(grid.grid_x * grid.grid_y) -{ -} - +// +//UIGridPoint::UIGridPoint() +//:color(1.0f, 1.0f, 1.0f), color_overlay(0.0f, 0.0f, 0.0f), walkable(false), transparent(false), +// tilesprite(-1), tile_overlay(-1), uisprite(-1) +//{ +//} +// +//UIEntity::UIEntity() {} // this will not work lol. TODO remove default constructor by finding the shared pointer inits that use it +// +//UIEntity::UIEntity(UIGrid& grid) +//: gridstate(grid.grid_x * grid.grid_y) +//{ +//} +// // UIGrid methods - -UIGrid::UIGrid() -{ -} - -/* -UIGrid::UIGrid(int gx, int gy, std::shared_ptr _ptex, float _x, float _y, float _w, float _h) -: grid_x(gx), grid_y(gy), - zoom(1.0f), center_x((gx/2) * _ptex->sheet_width), center_y((gy/2) * _ptex->sheet_height), - itex(_itex), points(gx * gy) -{ - // set up blank list of entities - entities = std::make_shared>>(); - - box.setSize(sf::Vector2f(_w, _h)); - box.setPosition(sf::Vector2f(_x, _y)); - - box.setFillColor(sf::Color(0,0,0,0)); - renderTexture.create(_w, _h); - sprite.setTexture(_itex->texture); - output.setTextureRect( - sf::IntRect(0, 0, - box.getSize().x, box.getSize().y)); - output.setPosition(box.getPosition()); - // textures are upside-down inside renderTexture - output.setTexture(renderTexture.getTexture()); -} -*/ - -UIGrid::UIGrid(int gx, int gy, std::shared_ptr _ptex, sf::Vector2f _xy, sf::Vector2f _wh) -: grid_x(gx), grid_y(gy), - zoom(1.0f), center_x((gx/2) * _ptex->sprite_width), center_y((gy/2) * _ptex->sprite_height), - ptex(_ptex), points(gx * gy) -{ - // set up blank list of entities - entities = std::make_shared>>(); - - box.setSize(_wh); - box.setPosition(_xy); - - box.setFillColor(sf::Color(0,0,0,0)); - //renderTexture.create(_wh.x, _wh.y); - // create renderTexture with maximum theoretical size; sprite can resize to show whatever amount needs to be rendered - renderTexture.create(1920, 1080); // TODO - renderTexture should be window size; above 1080p this will cause rendering errors - - //sprite.setTexture(_itex->texture); - sprite = ptex->sprite(0); - - output.setTextureRect( - sf::IntRect(0, 0, - box.getSize().x, box.getSize().y)); - output.setPosition(box.getPosition()); - // textures are upside-down inside renderTexture - output.setTexture(renderTexture.getTexture()); - -} - -void UIGrid::update() -{ -} - -/* -void UIGrid::setSprite(int ti) -{ - //int tx = ti % itex->grid_width, ty = ti / itex->grid_width; - // sprite.setTextureRect(sf::IntRect(tx * itex->grid_size, ty * itex->grid_size, itex->grid_size, itex->grid_size)); - sprite = ptex->sprite(ti); -} -*/ - -void UIGrid::render(sf::Vector2f) -{ - output.setPosition(box.getPosition()); // output sprite can move; update position when drawing - // output size can change; update size when drawing - output.setTextureRect( - sf::IntRect(0, 0, - box.getSize().x, box.getSize().y)); - renderTexture.clear(sf::Color(8, 8, 8, 255)); // TODO - UIGrid needs a "background color" field - // sprites that are visible according to zoom, center_x, center_y, and box width - float center_x_sq = center_x / ptex->sprite_width; - float center_y_sq = center_y / ptex->sprite_height; - - float width_sq = box.getSize().x / (ptex->sprite_width * zoom); - float height_sq = box.getSize().y / (ptex->sprite_height * zoom); - float left_edge = center_x_sq - (width_sq / 2.0); - float top_edge = center_y_sq - (height_sq / 2.0); - - int left_spritepixels = center_x - (box.getSize().x / 2.0 / zoom); - int top_spritepixels = center_y - (box.getSize().y / 2.0 / zoom); - - //sprite.setScale(sf::Vector2f(zoom, zoom)); - sf::RectangleShape r; // for colors and overlays - r.setSize(sf::Vector2f(ptex->sprite_width * zoom, ptex->sprite_height * zoom)); - r.setOutlineThickness(0); - - int x_limit = left_edge + width_sq + 2; - if (x_limit > grid_x) x_limit = grid_x; - - int y_limit = top_edge + height_sq + 2; - if (y_limit > grid_y) y_limit = grid_y; - - // base layer - bottom color, tile sprite ("ground") - for (int x = (left_edge - 1 >= 0 ? left_edge - 1 : 0); - x < x_limit; //x < view_width; - x+=1) - { - //for (float y = (top_edge >= 0 ? top_edge : 0); - for (int y = (top_edge - 1 >= 0 ? top_edge - 1 : 0); - y < y_limit; //y < view_height; - y+=1) - { - auto pixel_pos = sf::Vector2f( - (x*ptex->sprite_width - left_spritepixels) * zoom, - (y*ptex->sprite_height - top_spritepixels) * zoom ); - - auto gridpoint = at(std::floor(x), std::floor(y)); - - //sprite.setPosition(pixel_pos); - - r.setPosition(pixel_pos); - r.setFillColor(gridpoint.color); - renderTexture.draw(r); - - // tilesprite - // if discovered but not visible, set opacity to 90% - // if not discovered... just don't draw it? - if (gridpoint.tilesprite != -1) { - sprite = ptex->sprite(gridpoint.tilesprite, pixel_pos, sf::Vector2f(zoom, zoom)); //setSprite(gridpoint.tilesprite);; - renderTexture.draw(sprite); - } - } - } - - // middle layer - entities - // disabling entity rendering until I can render their UISprite inside the rendertexture (not directly to window) - for (auto e : *entities) { - // TODO skip out-of-bounds entities (grid square not visible at all, check for partially on visible grid squares / floating point grid position) - //auto drawent = e->cGrid->indexsprite.drawable(); - auto& drawent = e->sprite; - //drawent.setScale(zoom, zoom); - drawent.setScale(sf::Vector2f(zoom, zoom)); - auto pixel_pos = sf::Vector2f( - (e->position.x*ptex->sprite_width - left_spritepixels) * zoom, - (e->position.y*ptex->sprite_height - top_spritepixels) * zoom ); - //drawent.setPosition(pixel_pos); - //renderTexture.draw(drawent); - drawent.render(pixel_pos, renderTexture); - } - - - // top layer - opacity for discovered / visible status (debug, basically) - /* // Disabled until I attach a "perspective" - for (int x = (left_edge - 1 >= 0 ? left_edge - 1 : 0); - x < x_limit; //x < view_width; - x+=1) - { - //for (float y = (top_edge >= 0 ? top_edge : 0); - for (int y = (top_edge - 1 >= 0 ? top_edge - 1 : 0); - y < y_limit; //y < view_height; - y+=1) - { - - auto pixel_pos = sf::Vector2f( - (x*itex->grid_size - left_spritepixels) * zoom, - (y*itex->grid_size - top_spritepixels) * zoom ); - - auto gridpoint = at(std::floor(x), std::floor(y)); - - sprite.setPosition(pixel_pos); - - r.setPosition(pixel_pos); - - // visible & discovered layers for testing purposes - if (!gridpoint.discovered) { - r.setFillColor(sf::Color(16, 16, 20, 192)); // 255 opacity for actual blackout - renderTexture.draw(r); - } else if (!gridpoint.visible) { - r.setFillColor(sf::Color(32, 32, 40, 128)); - renderTexture.draw(r); - } - - // overlay - - // uisprite - } - } - */ - - // grid lines for testing & validation - /* - sf::Vertex line[] = - { - sf::Vertex(sf::Vector2f(0, 0), sf::Color::Red), - sf::Vertex(box.getSize(), sf::Color::Red), - - }; - - renderTexture.draw(line, 2, sf::Lines); - sf::Vertex lineb[] = - { - sf::Vertex(sf::Vector2f(0, box.getSize().y), sf::Color::Blue), - sf::Vertex(sf::Vector2f(box.getSize().x, 0), sf::Color::Blue), - - }; - - renderTexture.draw(lineb, 2, sf::Lines); - */ - - // render to window - renderTexture.display(); - Resources::game->getWindow().draw(output); - -} - -UIGridPoint& UIGrid::at(int x, int y) -{ - return points[y * grid_x + x]; -} - -PyObjectsEnum UIGrid::derived_type() -{ - return PyObjectsEnum::UIGRID; -} - -std::shared_ptr UIGrid::getTexture() -{ - return ptex; -} +// +//UIGrid::UIGrid() +//{ +//} +// +///* +//UIGrid::UIGrid(int gx, int gy, std::shared_ptr _ptex, float _x, float _y, float _w, float _h) +//: grid_x(gx), grid_y(gy), +// zoom(1.0f), center_x((gx/2) * _ptex->sheet_width), center_y((gy/2) * _ptex->sheet_height), +// itex(_itex), points(gx * gy) +//{ +// // set up blank list of entities +// entities = std::make_shared>>(); +// +// box.setSize(sf::Vector2f(_w, _h)); +// box.setPosition(sf::Vector2f(_x, _y)); +// +// box.setFillColor(sf::Color(0,0,0,0)); +// renderTexture.create(_w, _h); +// sprite.setTexture(_itex->texture); +// output.setTextureRect( +// sf::IntRect(0, 0, +// box.getSize().x, box.getSize().y)); +// output.setPosition(box.getPosition()); +// // textures are upside-down inside renderTexture +// output.setTexture(renderTexture.getTexture()); +//} +//*/ +// +//UIGrid::UIGrid(int gx, int gy, std::shared_ptr _ptex, sf::Vector2f _xy, sf::Vector2f _wh) +//: grid_x(gx), grid_y(gy), +// zoom(1.0f), center_x((gx/2) * _ptex->sprite_width), center_y((gy/2) * _ptex->sprite_height), +// ptex(_ptex), points(gx * gy) +//{ +// // set up blank list of entities +// entities = std::make_shared>>(); +// +// box.setSize(_wh); +// box.setPosition(_xy); +// +// box.setFillColor(sf::Color(0,0,0,0)); +// //renderTexture.create(_wh.x, _wh.y); +// // create renderTexture with maximum theoretical size; sprite can resize to show whatever amount needs to be rendered +// renderTexture.create(1920, 1080); // TODO - renderTexture should be window size; above 1080p this will cause rendering errors +// +// //sprite.setTexture(_itex->texture); +// sprite = ptex->sprite(0); +// +// output.setTextureRect( +// sf::IntRect(0, 0, +// box.getSize().x, box.getSize().y)); +// output.setPosition(box.getPosition()); +// // textures are upside-down inside renderTexture +// output.setTexture(renderTexture.getTexture()); +// +//} +// +//void UIGrid::update() +//{ +//} +// +///* +//void UIGrid::setSprite(int ti) +//{ +// //int tx = ti % itex->grid_width, ty = ti / itex->grid_width; +// // sprite.setTextureRect(sf::IntRect(tx * itex->grid_size, ty * itex->grid_size, itex->grid_size, itex->grid_size)); +// sprite = ptex->sprite(ti); +//} +//*/ +// +//void UIGrid::render(sf::Vector2f) +//{ +// output.setPosition(box.getPosition()); // output sprite can move; update position when drawing +// // output size can change; update size when drawing +// output.setTextureRect( +// sf::IntRect(0, 0, +// box.getSize().x, box.getSize().y)); +// renderTexture.clear(sf::Color(8, 8, 8, 255)); // TODO - UIGrid needs a "background color" field +// // sprites that are visible according to zoom, center_x, center_y, and box width +// float center_x_sq = center_x / ptex->sprite_width; +// float center_y_sq = center_y / ptex->sprite_height; +// +// float width_sq = box.getSize().x / (ptex->sprite_width * zoom); +// float height_sq = box.getSize().y / (ptex->sprite_height * zoom); +// float left_edge = center_x_sq - (width_sq / 2.0); +// float top_edge = center_y_sq - (height_sq / 2.0); +// +// int left_spritepixels = center_x - (box.getSize().x / 2.0 / zoom); +// int top_spritepixels = center_y - (box.getSize().y / 2.0 / zoom); +// +// //sprite.setScale(sf::Vector2f(zoom, zoom)); +// sf::RectangleShape r; // for colors and overlays +// r.setSize(sf::Vector2f(ptex->sprite_width * zoom, ptex->sprite_height * zoom)); +// r.setOutlineThickness(0); +// +// int x_limit = left_edge + width_sq + 2; +// if (x_limit > grid_x) x_limit = grid_x; +// +// int y_limit = top_edge + height_sq + 2; +// if (y_limit > grid_y) y_limit = grid_y; +// +// // base layer - bottom color, tile sprite ("ground") +// for (int x = (left_edge - 1 >= 0 ? left_edge - 1 : 0); +// x < x_limit; //x < view_width; +// x+=1) +// { +// //for (float y = (top_edge >= 0 ? top_edge : 0); +// for (int y = (top_edge - 1 >= 0 ? top_edge - 1 : 0); +// y < y_limit; //y < view_height; +// y+=1) +// { +// auto pixel_pos = sf::Vector2f( +// (x*ptex->sprite_width - left_spritepixels) * zoom, +// (y*ptex->sprite_height - top_spritepixels) * zoom ); +// +// auto gridpoint = at(std::floor(x), std::floor(y)); +// +// //sprite.setPosition(pixel_pos); +// +// r.setPosition(pixel_pos); +// r.setFillColor(gridpoint.color); +// renderTexture.draw(r); +// +// // tilesprite +// // if discovered but not visible, set opacity to 90% +// // if not discovered... just don't draw it? +// if (gridpoint.tilesprite != -1) { +// sprite = ptex->sprite(gridpoint.tilesprite, pixel_pos, sf::Vector2f(zoom, zoom)); //setSprite(gridpoint.tilesprite);; +// renderTexture.draw(sprite); +// } +// } +// } +// +// // middle layer - entities +// // disabling entity rendering until I can render their UISprite inside the rendertexture (not directly to window) +// for (auto e : *entities) { +// // TODO skip out-of-bounds entities (grid square not visible at all, check for partially on visible grid squares / floating point grid position) +// //auto drawent = e->cGrid->indexsprite.drawable(); +// auto& drawent = e->sprite; +// //drawent.setScale(zoom, zoom); +// drawent.setScale(sf::Vector2f(zoom, zoom)); +// auto pixel_pos = sf::Vector2f( +// (e->position.x*ptex->sprite_width - left_spritepixels) * zoom, +// (e->position.y*ptex->sprite_height - top_spritepixels) * zoom ); +// //drawent.setPosition(pixel_pos); +// //renderTexture.draw(drawent); +// drawent.render(pixel_pos, renderTexture); +// } +// +// +// // top layer - opacity for discovered / visible status (debug, basically) +// /* // Disabled until I attach a "perspective" +// for (int x = (left_edge - 1 >= 0 ? left_edge - 1 : 0); +// x < x_limit; //x < view_width; +// x+=1) +// { +// //for (float y = (top_edge >= 0 ? top_edge : 0); +// for (int y = (top_edge - 1 >= 0 ? top_edge - 1 : 0); +// y < y_limit; //y < view_height; +// y+=1) +// { +// +// auto pixel_pos = sf::Vector2f( +// (x*itex->grid_size - left_spritepixels) * zoom, +// (y*itex->grid_size - top_spritepixels) * zoom ); +// +// auto gridpoint = at(std::floor(x), std::floor(y)); +// +// sprite.setPosition(pixel_pos); +// +// r.setPosition(pixel_pos); +// +// // visible & discovered layers for testing purposes +// if (!gridpoint.discovered) { +// r.setFillColor(sf::Color(16, 16, 20, 192)); // 255 opacity for actual blackout +// renderTexture.draw(r); +// } else if (!gridpoint.visible) { +// r.setFillColor(sf::Color(32, 32, 40, 128)); +// renderTexture.draw(r); +// } +// +// // overlay +// +// // uisprite +// } +// } +// */ +// +// // grid lines for testing & validation +// /* +// sf::Vertex line[] = +// { +// sf::Vertex(sf::Vector2f(0, 0), sf::Color::Red), +// sf::Vertex(box.getSize(), sf::Color::Red), +// +// }; +// +// renderTexture.draw(line, 2, sf::Lines); +// sf::Vertex lineb[] = +// { +// sf::Vertex(sf::Vector2f(0, box.getSize().y), sf::Color::Blue), +// sf::Vertex(sf::Vector2f(box.getSize().x, 0), sf::Color::Blue), +// +// }; +// +// renderTexture.draw(lineb, 2, sf::Lines); +// */ +// +// // render to window +// renderTexture.display(); +// Resources::game->getWindow().draw(output); +// +//} +// +//UIGridPoint& UIGrid::at(int x, int y) +//{ +// return points[y * grid_x + x]; +//} +// +//PyObjectsEnum UIGrid::derived_type() +//{ +// return PyObjectsEnum::UIGRID; +//} +// +//std::shared_ptr UIGrid::getTexture() +//{ +// return ptex; +//} diff --git a/src/UI.h b/src/UI.h index c845baf..73dd5bc 100644 --- a/src/UI.h +++ b/src/UI.h @@ -15,445 +15,411 @@ #include "PyVector.h" #include "PyFont.h" -enum PyObjectsEnum : int -{ - UIFRAME = 1, - UICAPTION, - UISPRITE, - UIGRID -}; +#include "UIDrawable.h" -class UIDrawable -{ -public: - //UIDrawable* parent; - void render(); - 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; - virtual PyObjectsEnum derived_type() = 0; - - // Mouse input handling - callable object, methods to find event's destination - //PyObject* click_callable; - std::unique_ptr click_callable; - virtual UIDrawable* click_at(sf::Vector2f point) = 0; - void click_register(PyObject*); - void click_unregister(); - - UIDrawable(); -}; - -//Python object types & forward declarations -/* -typedef struct { - PyObject_HEAD - sf::Color color; -} PyColorObject; -*/ - -/* // Moved to PyColor.h -typedef struct { - PyObject_HEAD - std::shared_ptr data; -} PyColorObject; -*/ - -class UIFrame: public UIDrawable -{ -public: - UIFrame(float, float, float, float); - 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, - 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 - void fillColor(PyColorObject* pyColor); // Python setter - - sf::Color outlineColor(); // getter - void outlineColor(sf::Color c); // C++ setter - void outlineColor(PyColorObject* pyColor); // Python setter - */ - -private: - //std::shared_ptr fillColor, outlineColor; - /* - sf::Color *_fillColor, *_outlineColor; - PyColorObject *pyFillColor, *pyOutlineColor; - */ -}; - -class UICaption: public UIDrawable -{ -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 -{ -private: - int sprite_index; - sf::Sprite sprite; -protected: - std::shared_ptr ptex; -public: - UISprite(); - //UISprite(IndexTexture*, int, float, float, float); - //UISprite(IndexTexture*, int, sf::Vector2f, float); - UISprite(std::shared_ptr, 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&); - //IndexTexture* itex; - //sf::Vector2f pos; - //float scale; - //void setPosition(float, float); - void setPosition(sf::Vector2f); - sf::Vector2f getPosition(); - void setScale(sf::Vector2f); - sf::Vector2f getScale(); - void setSpriteIndex(int); - int getSpriteIndex(); - - void setTexture(std::shared_ptr _ptex, int _sprite_index=-1); - std::shared_ptr getTexture(); - - PyObjectsEnum derived_type() override final; // { return PyObjectsEnum::UISprite; }; -}; +#include "UIFrame.h" +#include "UICaption.h" +#include "UISprite.h" +#include "UIGridPoint.h" +#include "UIEntity.h" +#include "UIGrid.h" +#include "UICollection.h" +// +//class UIFrame: public UIDrawable +//{ +//public: +// UIFrame(float, float, float, float); +// 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, +// 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 +// void fillColor(PyColorObject* pyColor); // Python setter +// +// sf::Color outlineColor(); // getter +// void outlineColor(sf::Color c); // C++ setter +// void outlineColor(PyColorObject* pyColor); // Python setter +// */ +// +//private: +// //std::shared_ptr fillColor, outlineColor; +// /* +// sf::Color *_fillColor, *_outlineColor; +// PyColorObject *pyFillColor, *pyOutlineColor; +// */ +//}; +// +//class UICaption: public UIDrawable +//{ +//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 +//{ +//private: +// int sprite_index; +// sf::Sprite sprite; +//protected: +// std::shared_ptr ptex; +//public: +// UISprite(); +// //UISprite(IndexTexture*, int, float, float, float); +// //UISprite(IndexTexture*, int, sf::Vector2f, float); +// UISprite(std::shared_ptr, 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&); +// //IndexTexture* itex; +// //sf::Vector2f pos; +// //float scale; +// //void setPosition(float, float); +// void setPosition(sf::Vector2f); +// sf::Vector2f getPosition(); +// void setScale(sf::Vector2f); +// sf::Vector2f getScale(); +// void setSpriteIndex(int); +// int getSpriteIndex(); +// +// void setTexture(std::shared_ptr _ptex, int _sprite_index=-1); +// std::shared_ptr getTexture(); +// +// PyObjectsEnum derived_type() override final; // { return PyObjectsEnum::UISprite; }; +//}; +// // UIGridPoint - revised grid data for each point -class UIGridPoint -{ -public: - sf::Color color, color_overlay; - bool walkable, transparent; - int tilesprite, tile_overlay, uisprite; - UIGridPoint(); -}; - +//class UIGridPoint +//{ +//public: +// sf::Color color, color_overlay; +// bool walkable, transparent; +// int tilesprite, tile_overlay, uisprite; +// UIGridPoint(); +//}; +// // UIGridPointState - entity-specific info for each cell -class UIGridPointState -{ -public: - bool visible, discovered; -}; - -class UIGrid; - +//class UIGridPointState +//{ +//public: +// bool visible, discovered; +//}; +// +//class UIGrid; +// // TODO: make UIEntity a drawable(?) Maybe just rely on UISprite/UIGrid to // somehow properly render the thing? Poorly designed interface -class UIEntity//: public UIDrawable -{ -public: - //PyObject* self; - std::shared_ptr grid; - std::vector gridstate; - UISprite sprite; - sf::Vector2f position; //(x,y) in grid coordinates; float for animation - void render(sf::Vector2f); //override final; - - UIEntity(); - UIEntity(UIGrid&); - -}; - -class UIGrid: public UIDrawable -{ -private: - std::shared_ptr ptex; -public: - UIGrid(); - //UIGrid(int, int, IndexTexture*, float, float, float, float); - UIGrid(int, int, std::shared_ptr, sf::Vector2f, sf::Vector2f); - void update(); - void render(sf::Vector2f) override final; - 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 - sf::RectangleShape box; - float center_x, center_y, zoom; - //IndexTexture* itex; - std::shared_ptr getTexture(); - sf::Sprite sprite, output; - sf::RenderTexture renderTexture; - std::vector points; - std::shared_ptr>> entities; -}; - -/* -template -struct CPythonSharedObject { - PyObject_HEAD - std::shared_ptr data; -}; - -typedef CPythonSharedObject PyUIFrameObject; -*/ - -typedef struct { - PyObject_HEAD - std::shared_ptr data; -} PyUIFrameObject; - -typedef struct { - PyObject_HEAD - std::shared_ptr data; - PyObject* font; -} PyUICaptionObject; - -typedef struct { - PyObject_HEAD - std::shared_ptr data; - //PyObject* texture; -} PyUISpriteObject; - -typedef struct { - PyObject_HEAD - std::shared_ptr>> data; -} PyUICollectionObject; - -typedef struct { - PyObject_HEAD - UIGridPoint* data; - std::shared_ptr grid; -} PyUIGridPointObject; - -typedef struct { - PyObject_HEAD - UIGridPointState* data; - std::shared_ptr grid; - std::shared_ptr entity; -} PyUIGridPointStateObject; - -typedef struct { - PyObject_HEAD - std::shared_ptr data; - //PyObject* texture; -} PyUIEntityObject; - -typedef struct { - PyObject_HEAD - std::shared_ptr data; - //PyObject* texture; -} PyUIGridObject; - -namespace mcrfpydef { - //PyObject* py_instance(std::shared_ptr source); - // This function segfaults on tp_alloc for an unknown reason, but works inline with mcrfpydef:: methods. - -#define RET_PY_INSTANCE(target) { \ -switch (target->derived_type()) \ -{ \ - case PyObjectsEnum::UIFRAME: \ - { \ - PyUIFrameObject* o = (PyUIFrameObject*)((&PyUIFrameType)->tp_alloc(&PyUIFrameType, 0)); \ - if (o) \ - { \ - auto p = std::static_pointer_cast(target); \ - o->data = p; \ - auto utarget = o->data; \ - } \ - return (PyObject*)o; \ - } \ - case PyObjectsEnum::UICAPTION: \ - { \ - PyUICaptionObject* o = (PyUICaptionObject*)((&PyUICaptionType)->tp_alloc(&PyUICaptionType, 0)); \ - if (o) \ - { \ - auto p = std::static_pointer_cast(target); \ - o->data = p; \ - auto utarget = o->data; \ - } \ - return (PyObject*)o; \ - } \ - case PyObjectsEnum::UISPRITE: \ - { \ - PyUISpriteObject* o = (PyUISpriteObject*)((&PyUISpriteType)->tp_alloc(&PyUISpriteType, 0)); \ - if (o) \ - { \ - auto p = std::static_pointer_cast(target); \ - o->data = p; \ - auto utarget = o->data; \ - } \ - return (PyObject*)o; \ - } \ - case PyObjectsEnum::UIGRID: \ - { \ - PyUIGridObject* o = (PyUIGridObject*)((&PyUIGridType)->tp_alloc(&PyUIGridType, 0)); \ - if (o) \ - { \ - auto p = std::static_pointer_cast(target); \ - o->data = p; \ - auto utarget = o->data; \ - } \ - return (PyObject*)o; \ - } \ -} \ -} +//class UIEntity//: public UIDrawable +//{ +//public: +// //PyObject* self; +// std::shared_ptr grid; +// std::vector gridstate; +// UISprite sprite; +// sf::Vector2f position; //(x,y) in grid coordinates; float for animation +// void render(sf::Vector2f); //override final; +// +// UIEntity(); +// UIEntity(UIGrid&); +// +//}; +// +//class UIGrid: public UIDrawable +//{ +//private: +// std::shared_ptr ptex; +//public: +// UIGrid(); +// //UIGrid(int, int, IndexTexture*, float, float, float, float); +// UIGrid(int, int, std::shared_ptr, sf::Vector2f, sf::Vector2f); +// void update(); +// void render(sf::Vector2f) override final; +// 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 +// sf::RectangleShape box; +// float center_x, center_y, zoom; +// //IndexTexture* itex; +// std::shared_ptr getTexture(); +// sf::Sprite sprite, output; +// sf::RenderTexture renderTexture; +// std::vector points; +// std::shared_ptr>> entities; +//}; +// +///* +//template +//struct CPythonSharedObject { +// PyObject_HEAD +// std::shared_ptr data; +//}; +// +//typedef CPythonSharedObject PyUIFrameObject; +//*/ +// +//typedef struct { +// PyObject_HEAD +// std::shared_ptr data; +//} PyUIFrameObject; +// +//typedef struct { +// PyObject_HEAD +// std::shared_ptr data; +// PyObject* font; +//} PyUICaptionObject; +// +//typedef struct { +// PyObject_HEAD +// std::shared_ptr data; +// //PyObject* texture; +//} PyUISpriteObject; +// +//typedef struct { +// PyObject_HEAD +// std::shared_ptr>> data; +//} PyUICollectionObject; +// +//typedef struct { +// PyObject_HEAD +// UIGridPoint* data; +// std::shared_ptr grid; +//} PyUIGridPointObject; +// +//typedef struct { +// PyObject_HEAD +// UIGridPointState* data; +// std::shared_ptr grid; +// std::shared_ptr entity; +//} PyUIGridPointStateObject; +// +//typedef struct { +// PyObject_HEAD +// std::shared_ptr data; +// //PyObject* texture; +//} PyUIEntityObject; +// +//typedef struct { +// PyObject_HEAD +// std::shared_ptr data; +// //PyObject* texture; +//} PyUIGridObject; +// +//namespace mcrfpydef { +// //PyObject* py_instance(std::shared_ptr source); +// // This function segfaults on tp_alloc for an unknown reason, but works inline with mcrfpydef:: methods. +// +//#define RET_PY_INSTANCE(target) { \ +//switch (target->derived_type()) \ +//{ \ +// case PyObjectsEnum::UIFRAME: \ +// { \ +// PyUIFrameObject* o = (PyUIFrameObject*)((&PyUIFrameType)->tp_alloc(&PyUIFrameType, 0)); \ +// if (o) \ +// { \ +// auto p = std::static_pointer_cast(target); \ +// o->data = p; \ +// auto utarget = o->data; \ +// } \ +// return (PyObject*)o; \ +// } \ +// case PyObjectsEnum::UICAPTION: \ +// { \ +// PyUICaptionObject* o = (PyUICaptionObject*)((&PyUICaptionType)->tp_alloc(&PyUICaptionType, 0)); \ +// if (o) \ +// { \ +// auto p = std::static_pointer_cast(target); \ +// o->data = p; \ +// auto utarget = o->data; \ +// } \ +// return (PyObject*)o; \ +// } \ +// case PyObjectsEnum::UISPRITE: \ +// { \ +// PyUISpriteObject* o = (PyUISpriteObject*)((&PyUISpriteType)->tp_alloc(&PyUISpriteType, 0)); \ +// if (o) \ +// { \ +// auto p = std::static_pointer_cast(target); \ +// o->data = p; \ +// auto utarget = o->data; \ +// } \ +// return (PyObject*)o; \ +// } \ +// case PyObjectsEnum::UIGRID: \ +// { \ +// PyUIGridObject* o = (PyUIGridObject*)((&PyUIGridType)->tp_alloc(&PyUIGridType, 0)); \ +// if (o) \ +// { \ +// auto p = std::static_pointer_cast(target); \ +// o->data = p; \ +// auto utarget = o->data; \ +// } \ +// return (PyObject*)o; \ +// } \ +//} \ +//} // end macro definition - - // Color Definitions - // struct, members, new, set_member, PyTypeObject - - /* for reference: the structs to implement - typedef struct { - PyObject_HEAD - std::shared_ptr data; - } PyColorObject; - - typedef struct { - PyObject_HEAD - std::shared_ptr data; - } PyUIFrameObject; - - typedef struct { - PyObject_HEAD - std::shared_ptr data; - } PyUICaptionObject; - - typedef struct { - PyObject_HEAD - std::shared_ptr data; - } PyUISpriteObject; - */ - +// +// // Color Definitions +// // struct, members, new, set_member, PyTypeObject +// +// /* for reference: the structs to implement +// typedef struct { +// PyObject_HEAD +// std::shared_ptr data; +// } PyColorObject; +// +// typedef struct { +// PyObject_HEAD +// std::shared_ptr data; +// } PyUIFrameObject; +// +// typedef struct { +// PyObject_HEAD +// std::shared_ptr data; +// } PyUICaptionObject; +// +// typedef struct { +// PyObject_HEAD +// std::shared_ptr data; +// } 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->borrow(); - break; - case PyObjectsEnum::UICAPTION: - ptr = ((PyUICaptionObject*)self)->data->click_callable->borrow(); - break; - case PyObjectsEnum::UISPRITE: - ptr = ((PyUISpriteObject*)self)->data->click_callable->borrow(); - break; - case PyObjectsEnum::UIGRID: - ptr = ((PyUIGridObject*)self)->data->click_callable->borrow(); - 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; -} - +//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->borrow(); +// break; +// case PyObjectsEnum::UICAPTION: +// ptr = ((PyUICaptionObject*)self)->data->click_callable->borrow(); +// break; +// case PyObjectsEnum::UISPRITE: +// ptr = ((PyUISpriteObject*)self)->data->click_callable->borrow(); +// break; +// case PyObjectsEnum::UIGRID: +// ptr = ((PyUIGridObject*)self)->data->click_callable->borrow(); +// 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 - * - */ - /* - typedef struct { - PyObject_HEAD - std::shared_ptr data; - } PyFontObject; - - static int PyFont_init(PyFontObject* self, PyObject* args, PyObject* kwds) - { - //std::cout << "Init called\n"; - static const char* keywords[] = { "filename", nullptr }; - char* filename; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", const_cast(keywords), &filename)) - { - return -1; - } - self->data->loadFromFile((std::string)filename); - return 0; - } - - static PyTypeObject PyFontType = { - //PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "mcrfpy.Font", - .tp_basicsize = sizeof(PyFontObject), - .tp_itemsize = 0, - .tp_flags = Py_TPFLAGS_DEFAULT, - .tp_doc = PyDoc_STR("SFML Font Object"), - .tp_init = (initproc)PyFont_init, - .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* - { - PyFontObject* self = (PyFontObject*)type->tp_alloc(type, 0); - self->data = std::make_shared(); - return (PyObject*)self; - } - }; - */ - - /* - * - * End PyFontType defs - * - */ - - +// +// +// /* +// * +// * Begin PyFontType defs +// * +// */ +// /* +// typedef struct { +// PyObject_HEAD +// std::shared_ptr data; +// } PyFontObject; +// +// static int PyFont_init(PyFontObject* self, PyObject* args, PyObject* kwds) +// { +// //std::cout << "Init called\n"; +// static const char* keywords[] = { "filename", nullptr }; +// char* filename; +// +// if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", const_cast(keywords), &filename)) +// { +// return -1; +// } +// self->data->loadFromFile((std::string)filename); +// return 0; +// } +// +// static PyTypeObject PyFontType = { +// //PyVarObject_HEAD_INIT(NULL, 0) +// .tp_name = "mcrfpy.Font", +// .tp_basicsize = sizeof(PyFontObject), +// .tp_itemsize = 0, +// .tp_flags = Py_TPFLAGS_DEFAULT, +// .tp_doc = PyDoc_STR("SFML Font Object"), +// .tp_init = (initproc)PyFont_init, +// .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* +// { +// PyFontObject* self = (PyFontObject*)type->tp_alloc(type, 0); +// self->data = std::make_shared(); +// return (PyObject*)self; +// } +// }; +// */ +// +// /* +// * +// * End PyFontType defs +// * +// */ +// +// // static PyObject* PyColor_get_member(PyColorObject* self, void* closure) // { // auto member_ptr = reinterpret_cast(closure); @@ -537,1978 +503,1978 @@ static int PyUIDrawable_set_click(PyUIGridObject* self, PyObject* value, void* c // return (PyObject*)self; // } // }; - - /* - * - * Begin template generation for PyUICaptionType - * - */ - - static PyObject* PyUICaption_get_float_member(PyUICaptionObject* self, void* closure) - { - auto member_ptr = reinterpret_cast(closure); - if (member_ptr == 0) - return PyFloat_FromDouble(self->data->text.getPosition().x); - else if (member_ptr == 1) - return PyFloat_FromDouble(self->data->text.getPosition().y); - else if (member_ptr == 4) - return PyFloat_FromDouble(self->data->text.getOutlineThickness()); - else if (member_ptr == 5) - return PyLong_FromLong(self->data->text.getCharacterSize()); - else - { - PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); - return nullptr; - } - } - - static int PyUICaption_set_float_member(PyUICaptionObject* self, PyObject* value, void* closure) - { - float val; - auto member_ptr = reinterpret_cast(closure); - if (PyFloat_Check(value)) - { - val = PyFloat_AsDouble(value); - } - else if (PyLong_Check(value)) - { - val = PyLong_AsLong(value); - } - else - { - PyErr_SetString(PyExc_TypeError, "Value must be an integer."); - return -1; - } - if (member_ptr == 0) //x - self->data->text.setPosition(val, self->data->text.getPosition().y); - else if (member_ptr == 1) //y - self->data->text.setPosition(self->data->text.getPosition().x, val); - else if (member_ptr == 4) //outline - self->data->text.setOutlineThickness(val); - else if (member_ptr == 5) // character size - self->data->text.setCharacterSize(val); - return 0; - } - - static PyObject* PyUICaption_get_vec_member(PyUICaptionObject* self, void* closure) - { - return PyVector(self->data->text.getPosition()).pyObject(); - } - - static int PyUICaption_set_vec_member(PyUICaptionObject* self, PyObject* value, void* closure) - { - self->data->text.setPosition(PyVector::fromPy(value)); - return 0; - } - - static PyObject* PyUICaption_get_color_member(PyUICaptionObject* self, void* closure) - { - // validate closure (should be impossible to be wrong, but it's thorough) - auto member_ptr = reinterpret_cast(closure); - if (member_ptr != 0 && member_ptr != 1) - { - PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); - return nullptr; - } - - // TODO: manually calling tp_alloc to create a PyColorObject seems like an antipattern - /* - PyTypeObject* colorType = &PyLinkedColorType; - PyObject* pyColor = colorType->tp_alloc(colorLinkedType, 0); - if (pyColor == NULL) - { - std::cout << "failure to allocate mcrfpy.LinkedColor / PyLinkedColorType" << std::endl; - return NULL; - } - PyColorObject* pyColorObj = reinterpret_cast(pyColor); - */ - - // fetch correct member data - sf::Color color; - //sf::Color (*cgetter)(); - //void (*csetter)(sf::Color); - //std::function csetter; - //std::function cgetter; - if (member_ptr == 0) - { - color = self->data->text.getFillColor(); - //return Py_BuildValue("(iii)", color.r, color.g, color.b); - //csetter = &self->data->text.setFillColor; - //cgetter = &self->data->text.getFillColor; - //csetter = [s = self->data](sf::Color c){s->text.setFillColor(c);}; - //cgetter = [s = self->data](){return s->text.getFillColor();}; - } - else if (member_ptr == 1) - { - color = self->data->text.getOutlineColor(); - //return Py_BuildValue("(iii)", color.r, color.g, color.b); - //csetter = &self->data->text.setOutlineColor; - //cgetter = &self->data->text.getOutlineColor; - //csetter = [s = self->data](sf::Color c){s->text.setOutlineColor(c);}; - //cgetter = [s = self->data](){return s->text.getOutlineColor();}; - } - - // initialize new mcrfpy.Color instance - //pyColorObj->data = std::make_shared(color); - //PyLinkedColor::fromPy(pyColorObj).set(color); - //auto linkedcolor = PyLinkedColor(csetter, cgetter, self->data, member_ptr); - //linkedcolor.set(color); // don't need to set a linked color! - - //return pyColor; - return PyColor(color).pyObject(); - } - - static int PyUICaption_set_color_member(PyUICaptionObject* self, PyObject* value, void* closure) - { - auto member_ptr = reinterpret_cast(closure); - //TODO: this logic of (PyColor instance OR tuple -> sf::color) should be encapsulated for reuse - int r, g, b, a; - if (PyObject_IsInstance(value, (PyObject*)&PyColorType)) - { - // get value from mcrfpy.Color instance - /* - PyColorObject* color = reinterpret_cast(value); - r = color->data->r; - g = color->data->g; - b = color->data->b; - a = color->data->a; - */ - //std::cout << "Build LinkedColor" << std::endl; - //auto lc = PyLinkedColor::fromPy(value); - auto c = ((PyColorObject*)value)->data; - //std::cout << "Fetch value" << std::endl; - //auto c = lc.get(); - r = c.r; g = c.g; b = c.b; a = c.a; - std::cout << "got " << int(r) << ", " << int(g) << ", " << int(b) << ", " << int(a) << std::endl; - } - else if (!PyTuple_Check(value) || PyTuple_Size(value) < 3 || PyTuple_Size(value) > 4) - { - // reject non-Color, non-tuple value - PyErr_SetString(PyExc_TypeError, "Value must be a tuple of 3 or 4 integers or an mcrfpy.Color object."); - return -1; - } - else // get value from tuples - { - r = PyLong_AsLong(PyTuple_GetItem(value, 0)); - g = PyLong_AsLong(PyTuple_GetItem(value, 1)); - b = PyLong_AsLong(PyTuple_GetItem(value, 2)); - a = 255; - - if (PyTuple_Size(value) == 4) - { - a = PyLong_AsLong(PyTuple_GetItem(value, 3)); - } - } - - if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255 || a < 0 || a > 255) - { - PyErr_SetString(PyExc_ValueError, "Color values must be between 0 and 255."); - return -1; - } - - if (member_ptr == 0) - { - self->data->text.setFillColor(sf::Color(r, g, b, a)); - } - else if (member_ptr == 1) - { - self->data->text.setOutlineColor(sf::Color(r, g, b, a)); - } - else - { - PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); - return -1; - } - - return 0; - } - - static PyObject* PyUICaption_get_text(PyUICaptionObject* self, void* closure) - { - Resources::caption_buffer = self->data->text.getString(); - return PyUnicode_FromString(Resources::caption_buffer.c_str()); - } - - static int PyUICaption_set_text(PyUICaptionObject* self, PyObject* value, void* closure) - { - PyObject* s = PyObject_Str(value); - PyObject * temp_bytes = PyUnicode_AsEncodedString(s, "UTF-8", "strict"); // Owned reference - if (temp_bytes != NULL) { - Resources::caption_buffer = PyBytes_AS_STRING(temp_bytes); // Borrowed pointer - Py_DECREF(temp_bytes); - } - self->data->text.setString(Resources::caption_buffer); - return 0; - } - - static PyGetSetDef PyUICaption_getsetters[] = { - {"x", (getter)PyUICaption_get_float_member, (setter)PyUICaption_set_float_member, "X coordinate of top-left corner", (void*)0}, - {"y", (getter)PyUICaption_get_float_member, (setter)PyUICaption_set_float_member, "Y coordinate of top-left corner", (void*)1}, - {"pos", (getter)PyUICaption_get_vec_member, (setter)PyUICaption_set_vec_member, "(x, y) vector", (void*)0}, - //{"w", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "width of the rectangle", (void*)2}, - //{"h", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "height of the rectangle", (void*)3}, - {"outline", (getter)PyUICaption_get_float_member, (setter)PyUICaption_set_float_member, "Thickness of the border", (void*)4}, - {"fill_color", (getter)PyUICaption_get_color_member, (setter)PyUICaption_set_color_member, "Fill color of the text", (void*)0}, - {"outline_color", (getter)PyUICaption_get_color_member, (setter)PyUICaption_set_color_member, "Outline color of the text", (void*)1}, - //{"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} - }; - - static PyObject* PyUICaption_repr(PyUICaptionObject* self) - { - std::ostringstream ss; - if (!self->data) ss << ""; - else { - auto text = self->data->text; - auto fc = text.getFillColor(); - auto oc = text.getOutlineColor(); - ss << ""; - } - std::string repr_str = ss.str(); - return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); - } - - static int PyUICaption_init(PyUICaptionObject* self, PyObject* args, PyObject* kwds) - { - //std::cout << "Init called\n"; - static const char* keywords[] = { "x", "y", "text", "font", "fill_color", "outline_color", nullptr }; - float x = 0.0f, y = 0.0f; - char* text; - PyObject* font, fill_color, outline_color; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ffzOOO", - const_cast(keywords), &x, &y, &text, &font, &fill_color, &outline_color)) - { - return -1; - } - - // check types for font, fill_color, outline_color - // - // Set Font - // - std::cout << PyUnicode_AsUTF8(PyObject_Repr(font)) << std::endl; - if (font != NULL && !PyObject_IsInstance(font, (PyObject*)&PyFontType)){ - PyErr_SetString(PyExc_TypeError, "font must be a mcrfpy.Font instance"); - return -1; - } else if (font != NULL) - { - auto font_obj = (PyFontObject*)font; - self->data->text.setFont(font_obj->data->font); - self->font = font; - Py_INCREF(font); - } else - { - // default font - //self->data->text.setFont(Resources::game->getFont()); - } - - // - // Set Color - // - self->data->text.setPosition(sf::Vector2f(x, y)); - self->data->text.setString((std::string)text); - self->data->text.setFillColor(sf::Color(0,0,0,255)); - self->data->text.setOutlineColor(sf::Color(128,128,128,255)); - - return 0; - } - - static PyTypeObject PyUICaptionType = { - //PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "mcrfpy.Caption", - .tp_basicsize = sizeof(PyUICaptionObject), - .tp_itemsize = 0, - .tp_dealloc = (destructor)[](PyObject* self) - { - PyUICaptionObject* obj = (PyUICaptionObject*)self; - // release reference to font object - if (obj->font) Py_DECREF(obj->font); - obj->data.reset(); - Py_TYPE(self)->tp_free(self); - }, - .tp_repr = (reprfunc)PyUICaption_repr, - //.tp_hash = NULL, - //.tp_iter - //.tp_iternext - .tp_flags = Py_TPFLAGS_DEFAULT, - .tp_doc = PyDoc_STR("docstring"), - //.tp_methods = PyUIFrame_methods, - //.tp_members = PyUIFrame_members, - .tp_getset = PyUICaption_getsetters, - //.tp_base = NULL, - .tp_init = (initproc)PyUICaption_init, - .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* - { - PyUICaptionObject* self = (PyUICaptionObject*)type->tp_alloc(type, 0); - if (self) self->data = std::make_shared(); - return (PyObject*)self; - } - }; - - /* - * - * End PyUICaptionType generation - * - */ - - - /* - * - * Begin template generation for PyUIFrameType - * - */ - - static PyObject* PyUIFrame_get_float_member(PyUIFrameObject* self, void* closure) - { - auto member_ptr = reinterpret_cast(closure); - if (member_ptr == 0) - return PyFloat_FromDouble(self->data->box.getPosition().x); - else if (member_ptr == 1) - return PyFloat_FromDouble(self->data->box.getPosition().y); - else if (member_ptr == 2) - return PyFloat_FromDouble(self->data->box.getSize().x); - else if (member_ptr == 3) - return PyFloat_FromDouble(self->data->box.getSize().y); - else if (member_ptr == 4) - return PyFloat_FromDouble(self->data->box.getOutlineThickness()); - else - { - PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); - return nullptr; - } - } - - static int PyUIFrame_set_float_member(PyUIFrameObject* self, PyObject* value, void* closure) - { - float val; - auto member_ptr = reinterpret_cast(closure); - if (PyFloat_Check(value)) - { - val = PyFloat_AsDouble(value); - } - else if (PyLong_Check(value)) - { - val = PyLong_AsLong(value); - } - else - { - PyErr_SetString(PyExc_TypeError, "Value must be an integer."); - return -1; - } - if (member_ptr == 0) //x - self->data->box.setPosition(val, self->data->box.getPosition().y); - else if (member_ptr == 1) //y - self->data->box.setPosition(self->data->box.getPosition().x, val); - else if (member_ptr == 2) //w - self->data->box.setSize(sf::Vector2f(val, self->data->box.getSize().y)); - else if (member_ptr == 3) //h - self->data->box.setSize(sf::Vector2f(self->data->box.getSize().x, val)); - else if (member_ptr == 4) //outline - self->data->box.setOutlineThickness(val); - return 0; - } - - static PyObject* PyUIFrame_get_color_member(PyUIFrameObject* self, void* closure) - { - // validate closure (should be impossible to be wrong, but it's thorough) - auto member_ptr = reinterpret_cast(closure); - if (member_ptr != 0 && member_ptr != 1) - { - PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); - return nullptr; - } - 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); - - // fetch correct member data - sf::Color color; - if (member_ptr == 0) - { - color = self->data->box.getFillColor(); - //return Py_BuildValue("(iii)", color.r, color.g, color.b); - } - else if (member_ptr == 1) - { - color = self->data->box.getOutlineColor(); - //return Py_BuildValue("(iii)", color.r, color.g, color.b); - } - - // initialize new mcrfpy.Color instance - //pyColorObj->data = std::make_shared(color); - //PyColor::fromPy(pyColorObj).set(color); - - // TODO - supposed to return Linked - return PyColor(color).pyObject(); - } - - static int PyUIFrame_set_color_member(PyUIFrameObject* self, PyObject* value, void* closure) - { - //TODO: this logic of (PyColor instance OR tuple -> sf::color) should be encapsulated for reuse - auto member_ptr = reinterpret_cast(closure); - int r, g, b, a; - if (PyObject_IsInstance(value, (PyObject*)&PyColorType)) - { - // get value from mcrfpy.Color instance - /* - PyColorObject* color = reinterpret_cast(value); - r = color->data->r; - g = color->data->g; - b = color->data->b; - a = color->data->a; - std::cout << "using color: " << r << " " << g << " " << b << " " << a << std::endl; - */ - sf::Color c = ((PyColorObject*)value)->data; - r = c.r; g = c.g; b = c.b; a = c.a; - } - else if (!PyTuple_Check(value) || PyTuple_Size(value) < 3 || PyTuple_Size(value) > 4) - { - // reject non-Color, non-tuple value - PyErr_SetString(PyExc_TypeError, "Value must be a tuple of 3 or 4 integers or an mcrfpy.Color object."); - return -1; - } - else // get value from tuples - { - r = PyLong_AsLong(PyTuple_GetItem(value, 0)); - g = PyLong_AsLong(PyTuple_GetItem(value, 1)); - b = PyLong_AsLong(PyTuple_GetItem(value, 2)); - a = 255; - - if (PyTuple_Size(value) == 4) - { - a = PyLong_AsLong(PyTuple_GetItem(value, 3)); - } - } - - if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255 || a < 0 || a > 255) - { - PyErr_SetString(PyExc_ValueError, "Color values must be between 0 and 255."); - return -1; - } - - if (member_ptr == 0) - { - self->data->box.setFillColor(sf::Color(r, g, b, a)); - } - else if (member_ptr == 1) - { - self->data->box.setOutlineColor(sf::Color(r, g, b, a)); - } - else - { - PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); - return -1; - } - - return 0; - } - - static PyObject* PyUIFrame_get_children(PyUIFrameObject*, void*); - // implementation after the PyUICollectionType definition - - static PyGetSetDef PyUIFrame_getsetters[] = { - {"x", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "X coordinate of top-left corner", (void*)0}, - {"y", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "Y coordinate of top-left corner", (void*)1}, - {"w", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "width of the rectangle", (void*)2}, - {"h", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "height of the rectangle", (void*)3}, - {"outline", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "Thickness of the border", (void*)4}, - {"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} - }; - - static PyObject* PyUIFrame_repr(PyUIFrameObject* self) - { - std::ostringstream ss; - if (!self->data) ss << ""; - else { - auto box = self->data->box; - auto fc = box.getFillColor(); - auto oc = box.getOutlineColor(); - ss << "data->children->size() << " child objects" << - ")>"; - } - std::string repr_str = ss.str(); - return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); - } - - static int PyUIFrame_init(PyUIFrameObject* self, PyObject* args, PyObject* kwds) - { - //std::cout << "Init called\n"; - static const char* keywords[] = { "x", "y", "w", "h", "fill_color", "outline_color", "outline", nullptr }; - float x = 0.0f, y = 0.0f, w = 0.0f, h=0.0f, outline=0.0f; - PyObject* fill_color = 0; - PyObject* outline_color = 0; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "ffff|OOf", const_cast(keywords), &x, &y, &w, &h, &fill_color, &outline_color, &outline)) - { - return -1; - } - - //self->data->x = x; - //self->data->y = y; - self->data->box.setPosition(sf::Vector2f(x, y)); - self->data->box.setSize(sf::Vector2f(w, h)); - self->data->box.setOutlineThickness(outline); - // getsetter abuse because I haven't standardized Color object parsing (TODO) - int err_val = 0; - if (fill_color && fill_color != Py_None) err_val = PyUIFrame_set_color_member(self, fill_color, (void*)0); - else self->data->box.setFillColor(sf::Color(0,0,0,255)); - if (err_val) return err_val; - if (outline_color && outline_color != Py_None) err_val = PyUIFrame_set_color_member(self, outline_color, (void*)1); - else self->data->box.setOutlineColor(sf::Color(128,128,128,255)); - if (err_val) return err_val; - return 0; - } - - static PyTypeObject PyUIFrameType = { - //PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "mcrfpy.Frame", - .tp_basicsize = sizeof(PyUIFrameObject), - .tp_itemsize = 0, - .tp_dealloc = (destructor)[](PyObject* self) - { - PyUIFrameObject* obj = (PyUIFrameObject*)self; - obj->data.reset(); - Py_TYPE(self)->tp_free(self); - }, - .tp_repr = (reprfunc)PyUIFrame_repr, - //.tp_hash = NULL, - //.tp_iter - //.tp_iternext - .tp_flags = Py_TPFLAGS_DEFAULT, - .tp_doc = PyDoc_STR("docstring"), - //.tp_methods = PyUIFrame_methods, - //.tp_members = PyUIFrame_members, - .tp_getset = PyUIFrame_getsetters, - //.tp_base = NULL, - .tp_init = (initproc)PyUIFrame_init, - .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* - { - PyUIFrameObject* self = (PyUIFrameObject*)type->tp_alloc(type, 0); - if (self) self->data = std::make_shared(); - return (PyObject*)self; - } - }; - - /* - * - * End auto-generated PyUIFrameType generation - * - */ - - /* - * - * Begin Python Class Instantiator (iterator helper) - * - */ - - /* // definition can't go in the header file - PyObject* py_instance(UIDrawable* obj) - { - - } - */ - - /* - * - * End Python Class Instantitator (iterator helper) - * - */ - - /* - * - * Begin template generation for PyUISpriteType - * - */ - - static PyObject* PyUISprite_get_float_member(PyUISpriteObject* self, void* closure) - { - auto member_ptr = reinterpret_cast(closure); - if (member_ptr == 0) - return PyFloat_FromDouble(self->data->getPosition().x); - else if (member_ptr == 1) - return PyFloat_FromDouble(self->data->getPosition().y); - else if (member_ptr == 2) - return PyFloat_FromDouble(self->data->getScale().x); // scale X and Y are identical, presently - else - { - PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); - return nullptr; - } - } - - - static int PyUISprite_set_float_member(PyUISpriteObject* self, PyObject* value, void* closure) - { - float val; - auto member_ptr = reinterpret_cast(closure); - if (PyFloat_Check(value)) - { - val = PyFloat_AsDouble(value); - } - else if (PyLong_Check(value)) - { - val = PyLong_AsLong(value); - } - else - { - PyErr_SetString(PyExc_TypeError, "Value must be a floating point number."); - return -1; - } - if (member_ptr == 0) //x - self->data->setPosition(sf::Vector2f(val, self->data->getPosition().y)); - else if (member_ptr == 1) //y - self->data->setPosition(sf::Vector2f(self->data->getPosition().x, val)); - else if (member_ptr == 2) // scale - self->data->setScale(sf::Vector2f(val, val)); - return 0; - } - - static PyObject* PyUISprite_get_int_member(PyUISpriteObject* self, void* closure) - { - auto member_ptr = reinterpret_cast(closure); - if (true) {} - else - { - PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); - return nullptr; - } - - return PyLong_FromDouble(self->data->getSpriteIndex()); - } - - - static int PyUISprite_set_int_member(PyUISpriteObject* self, PyObject* value, void* closure) - { - int val; - auto member_ptr = reinterpret_cast(closure); - if (PyLong_Check(value)) - { - val = PyLong_AsLong(value); - } - else - { - PyErr_SetString(PyExc_TypeError, "Value must be an integer."); - return -1; - } - //self->data->sprite_index = val; - //self->data->sprite.setTextureRect(self->data->itex->spriteCoordinates(val)); - self->data->setSpriteIndex(val); - return 0; - } - - static PyObject* PyUISprite_get_texture(PyUISpriteObject* self, void* closure) - { - return self->data->getTexture()->pyObject(); - } - - static int PyUISprite_set_texture(PyUISpriteObject* self, PyObject* value, void* closure) - { - return -1; - } - - static PyGetSetDef PyUISprite_getsetters[] = { - {"x", (getter)PyUISprite_get_float_member, (setter)PyUISprite_set_float_member, "X coordinate of top-left corner", (void*)0}, - {"y", (getter)PyUISprite_get_float_member, (setter)PyUISprite_set_float_member, "Y coordinate of top-left corner", (void*)1}, - {"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} - }; - - static PyObject* PyUISprite_repr(PyUISpriteObject* self) - { - std::ostringstream ss; - if (!self->data) ss << ""; - else { - //auto sprite = self->data->sprite; - ss << ""; - } - std::string repr_str = ss.str(); - return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); - } - - static int PyUISprite_init(PyUISpriteObject* self, PyObject* args, PyObject* kwds) - { - //std::cout << "Init called\n"; - static const char* keywords[] = { "x", "y", "texture", "sprite_index", "scale", nullptr }; - float x = 0.0f, y = 0.0f, scale = 1.0f; - int sprite_index; - PyObject* texture; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ffOif", - const_cast(keywords), &x, &y, &texture, &sprite_index, &scale)) - { - return -1; - } - - // check types for texture - // - // Set Texture - // - if (texture != NULL && !PyObject_IsInstance(texture, (PyObject*)&PyTextureType)){ - PyErr_SetString(PyExc_TypeError, "texture must be a mcrfpy.Texture instance"); - return -1; - } /*else if (texture != NULL) // to be removed: UIObjects don't manage texture references - { - self->texture = texture; - Py_INCREF(texture); - } else - { - // default tex? - }*/ - auto pytexture = (PyTextureObject*)texture; - self->data = std::make_shared(pytexture->data, sprite_index, sf::Vector2f(x, y), scale); - self->data->setPosition(sf::Vector2f(x, y)); - - return 0; - } - - static PyTypeObject PyUISpriteType = { - //PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "mcrfpy.Sprite", - .tp_basicsize = sizeof(PyUISpriteObject), - .tp_itemsize = 0, - .tp_dealloc = (destructor)[](PyObject* self) - { - PyUISpriteObject* obj = (PyUISpriteObject*)self; - // release reference to font object - //if (obj->texture) Py_DECREF(obj->texture); - obj->data.reset(); - Py_TYPE(self)->tp_free(self); - }, - .tp_repr = (reprfunc)PyUISprite_repr, - //.tp_hash = NULL, - //.tp_iter - //.tp_iternext - .tp_flags = Py_TPFLAGS_DEFAULT, - .tp_doc = PyDoc_STR("docstring"), - //.tp_methods = PyUIFrame_methods, - //.tp_members = PyUIFrame_members, - .tp_getset = PyUISprite_getsetters, - //.tp_base = NULL, - .tp_init = (initproc)PyUISprite_init, - .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* - { - PyUISpriteObject* self = (PyUISpriteObject*)type->tp_alloc(type, 0); - //if (self) self->data = std::make_shared(); - return (PyObject*)self; - } - }; - /* - * - * End template for PyUISpriteType - * - */ - - - - - /* - * - * PyUIGridPoint defs - * - */ - +// +// /* +// * +// * Begin template generation for PyUICaptionType +// * +// */ +// +// static PyObject* PyUICaption_get_float_member(PyUICaptionObject* self, void* closure) +// { +// auto member_ptr = reinterpret_cast(closure); +// if (member_ptr == 0) +// return PyFloat_FromDouble(self->data->text.getPosition().x); +// else if (member_ptr == 1) +// return PyFloat_FromDouble(self->data->text.getPosition().y); +// else if (member_ptr == 4) +// return PyFloat_FromDouble(self->data->text.getOutlineThickness()); +// else if (member_ptr == 5) +// return PyLong_FromLong(self->data->text.getCharacterSize()); +// else +// { +// PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); +// return nullptr; +// } +// } +// +// static int PyUICaption_set_float_member(PyUICaptionObject* self, PyObject* value, void* closure) +// { +// float val; +// auto member_ptr = reinterpret_cast(closure); +// if (PyFloat_Check(value)) +// { +// val = PyFloat_AsDouble(value); +// } +// else if (PyLong_Check(value)) +// { +// val = PyLong_AsLong(value); +// } +// else +// { +// PyErr_SetString(PyExc_TypeError, "Value must be an integer."); +// return -1; +// } +// if (member_ptr == 0) //x +// self->data->text.setPosition(val, self->data->text.getPosition().y); +// else if (member_ptr == 1) //y +// self->data->text.setPosition(self->data->text.getPosition().x, val); +// else if (member_ptr == 4) //outline +// self->data->text.setOutlineThickness(val); +// else if (member_ptr == 5) // character size +// self->data->text.setCharacterSize(val); +// return 0; +// } +// +// static PyObject* PyUICaption_get_vec_member(PyUICaptionObject* self, void* closure) +// { +// return PyVector(self->data->text.getPosition()).pyObject(); +// } +// +// static int PyUICaption_set_vec_member(PyUICaptionObject* self, PyObject* value, void* closure) +// { +// self->data->text.setPosition(PyVector::fromPy(value)); +// return 0; +// } +// +// static PyObject* PyUICaption_get_color_member(PyUICaptionObject* self, void* closure) +// { +// // validate closure (should be impossible to be wrong, but it's thorough) +// auto member_ptr = reinterpret_cast(closure); +// if (member_ptr != 0 && member_ptr != 1) +// { +// PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); +// return nullptr; +// } +// +// // TODO: manually calling tp_alloc to create a PyColorObject seems like an antipattern +// /* +// PyTypeObject* colorType = &PyLinkedColorType; +// PyObject* pyColor = colorType->tp_alloc(colorLinkedType, 0); +// if (pyColor == NULL) +// { +// std::cout << "failure to allocate mcrfpy.LinkedColor / PyLinkedColorType" << std::endl; +// return NULL; +// } +// PyColorObject* pyColorObj = reinterpret_cast(pyColor); +// */ +// +// // fetch correct member data +// sf::Color color; +// //sf::Color (*cgetter)(); +// //void (*csetter)(sf::Color); +// //std::function csetter; +// //std::function cgetter; +// if (member_ptr == 0) +// { +// color = self->data->text.getFillColor(); +// //return Py_BuildValue("(iii)", color.r, color.g, color.b); +// //csetter = &self->data->text.setFillColor; +// //cgetter = &self->data->text.getFillColor; +// //csetter = [s = self->data](sf::Color c){s->text.setFillColor(c);}; +// //cgetter = [s = self->data](){return s->text.getFillColor();}; +// } +// else if (member_ptr == 1) +// { +// color = self->data->text.getOutlineColor(); +// //return Py_BuildValue("(iii)", color.r, color.g, color.b); +// //csetter = &self->data->text.setOutlineColor; +// //cgetter = &self->data->text.getOutlineColor; +// //csetter = [s = self->data](sf::Color c){s->text.setOutlineColor(c);}; +// //cgetter = [s = self->data](){return s->text.getOutlineColor();}; +// } +// +// // initialize new mcrfpy.Color instance +// //pyColorObj->data = std::make_shared(color); +// //PyLinkedColor::fromPy(pyColorObj).set(color); +// //auto linkedcolor = PyLinkedColor(csetter, cgetter, self->data, member_ptr); +// //linkedcolor.set(color); // don't need to set a linked color! +// +// //return pyColor; +// return PyColor(color).pyObject(); +// } +// +// static int PyUICaption_set_color_member(PyUICaptionObject* self, PyObject* value, void* closure) +// { +// auto member_ptr = reinterpret_cast(closure); +// //TODO: this logic of (PyColor instance OR tuple -> sf::color) should be encapsulated for reuse +// int r, g, b, a; +// if (PyObject_IsInstance(value, (PyObject*)&PyColorType)) +// { +// // get value from mcrfpy.Color instance +// /* +// PyColorObject* color = reinterpret_cast(value); +// r = color->data->r; +// g = color->data->g; +// b = color->data->b; +// a = color->data->a; +// */ +// //std::cout << "Build LinkedColor" << std::endl; +// //auto lc = PyLinkedColor::fromPy(value); +// auto c = ((PyColorObject*)value)->data; +// //std::cout << "Fetch value" << std::endl; +// //auto c = lc.get(); +// r = c.r; g = c.g; b = c.b; a = c.a; +// std::cout << "got " << int(r) << ", " << int(g) << ", " << int(b) << ", " << int(a) << std::endl; +// } +// else if (!PyTuple_Check(value) || PyTuple_Size(value) < 3 || PyTuple_Size(value) > 4) +// { +// // reject non-Color, non-tuple value +// PyErr_SetString(PyExc_TypeError, "Value must be a tuple of 3 or 4 integers or an mcrfpy.Color object."); +// return -1; +// } +// else // get value from tuples +// { +// r = PyLong_AsLong(PyTuple_GetItem(value, 0)); +// g = PyLong_AsLong(PyTuple_GetItem(value, 1)); +// b = PyLong_AsLong(PyTuple_GetItem(value, 2)); +// a = 255; +// +// if (PyTuple_Size(value) == 4) +// { +// a = PyLong_AsLong(PyTuple_GetItem(value, 3)); +// } +// } +// +// if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255 || a < 0 || a > 255) +// { +// PyErr_SetString(PyExc_ValueError, "Color values must be between 0 and 255."); +// return -1; +// } +// +// if (member_ptr == 0) +// { +// self->data->text.setFillColor(sf::Color(r, g, b, a)); +// } +// else if (member_ptr == 1) +// { +// self->data->text.setOutlineColor(sf::Color(r, g, b, a)); +// } +// else +// { +// PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); +// return -1; +// } +// +// return 0; +// } +// +// static PyObject* PyUICaption_get_text(PyUICaptionObject* self, void* closure) +// { +// Resources::caption_buffer = self->data->text.getString(); +// return PyUnicode_FromString(Resources::caption_buffer.c_str()); +// } +// +// static int PyUICaption_set_text(PyUICaptionObject* self, PyObject* value, void* closure) +// { +// PyObject* s = PyObject_Str(value); +// PyObject * temp_bytes = PyUnicode_AsEncodedString(s, "UTF-8", "strict"); // Owned reference +// if (temp_bytes != NULL) { +// Resources::caption_buffer = PyBytes_AS_STRING(temp_bytes); // Borrowed pointer +// Py_DECREF(temp_bytes); +// } +// self->data->text.setString(Resources::caption_buffer); +// return 0; +// } +// +// static PyGetSetDef PyUICaption_getsetters[] = { +// {"x", (getter)PyUICaption_get_float_member, (setter)PyUICaption_set_float_member, "X coordinate of top-left corner", (void*)0}, +// {"y", (getter)PyUICaption_get_float_member, (setter)PyUICaption_set_float_member, "Y coordinate of top-left corner", (void*)1}, +// {"pos", (getter)PyUICaption_get_vec_member, (setter)PyUICaption_set_vec_member, "(x, y) vector", (void*)0}, +// //{"w", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "width of the rectangle", (void*)2}, +// //{"h", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "height of the rectangle", (void*)3}, +// {"outline", (getter)PyUICaption_get_float_member, (setter)PyUICaption_set_float_member, "Thickness of the border", (void*)4}, +// {"fill_color", (getter)PyUICaption_get_color_member, (setter)PyUICaption_set_color_member, "Fill color of the text", (void*)0}, +// {"outline_color", (getter)PyUICaption_get_color_member, (setter)PyUICaption_set_color_member, "Outline color of the text", (void*)1}, +// //{"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} +// }; +// +// static PyObject* PyUICaption_repr(PyUICaptionObject* self) +// { +// std::ostringstream ss; +// if (!self->data) ss << ""; +// else { +// auto text = self->data->text; +// auto fc = text.getFillColor(); +// auto oc = text.getOutlineColor(); +// ss << ""; +// } +// std::string repr_str = ss.str(); +// return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); +// } +// +// static int PyUICaption_init(PyUICaptionObject* self, PyObject* args, PyObject* kwds) +// { +// //std::cout << "Init called\n"; +// static const char* keywords[] = { "x", "y", "text", "font", "fill_color", "outline_color", nullptr }; +// float x = 0.0f, y = 0.0f; +// char* text; +// PyObject* font, fill_color, outline_color; +// +// if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ffzOOO", +// const_cast(keywords), &x, &y, &text, &font, &fill_color, &outline_color)) +// { +// return -1; +// } +// +// // check types for font, fill_color, outline_color +// // +// // Set Font +// // +// std::cout << PyUnicode_AsUTF8(PyObject_Repr(font)) << std::endl; +// if (font != NULL && !PyObject_IsInstance(font, (PyObject*)&PyFontType)){ +// PyErr_SetString(PyExc_TypeError, "font must be a mcrfpy.Font instance"); +// return -1; +// } else if (font != NULL) +// { +// auto font_obj = (PyFontObject*)font; +// self->data->text.setFont(font_obj->data->font); +// self->font = font; +// Py_INCREF(font); +// } else +// { +// // default font +// //self->data->text.setFont(Resources::game->getFont()); +// } +// +// // +// // Set Color +// // +// self->data->text.setPosition(sf::Vector2f(x, y)); +// self->data->text.setString((std::string)text); +// self->data->text.setFillColor(sf::Color(0,0,0,255)); +// self->data->text.setOutlineColor(sf::Color(128,128,128,255)); +// +// return 0; +// } +// +// static PyTypeObject PyUICaptionType = { +// //PyVarObject_HEAD_INIT(NULL, 0) +// .tp_name = "mcrfpy.Caption", +// .tp_basicsize = sizeof(PyUICaptionObject), +// .tp_itemsize = 0, +// .tp_dealloc = (destructor)[](PyObject* self) +// { +// PyUICaptionObject* obj = (PyUICaptionObject*)self; +// // release reference to font object +// if (obj->font) Py_DECREF(obj->font); +// obj->data.reset(); +// Py_TYPE(self)->tp_free(self); +// }, +// .tp_repr = (reprfunc)PyUICaption_repr, +// //.tp_hash = NULL, +// //.tp_iter +// //.tp_iternext +// .tp_flags = Py_TPFLAGS_DEFAULT, +// .tp_doc = PyDoc_STR("docstring"), +// //.tp_methods = PyUIFrame_methods, +// //.tp_members = PyUIFrame_members, +// .tp_getset = PyUICaption_getsetters, +// //.tp_base = NULL, +// .tp_init = (initproc)PyUICaption_init, +// .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* +// { +// PyUICaptionObject* self = (PyUICaptionObject*)type->tp_alloc(type, 0); +// if (self) self->data = std::make_shared(); +// return (PyObject*)self; +// } +// }; +// +// /* +// * +// * End PyUICaptionType generation +// * +// */ +// +// +// /* +// * +// * Begin template generation for PyUIFrameType +// * +// */ +// +// static PyObject* PyUIFrame_get_float_member(PyUIFrameObject* self, void* closure) +// { +// auto member_ptr = reinterpret_cast(closure); +// if (member_ptr == 0) +// return PyFloat_FromDouble(self->data->box.getPosition().x); +// else if (member_ptr == 1) +// return PyFloat_FromDouble(self->data->box.getPosition().y); +// else if (member_ptr == 2) +// return PyFloat_FromDouble(self->data->box.getSize().x); +// else if (member_ptr == 3) +// return PyFloat_FromDouble(self->data->box.getSize().y); +// else if (member_ptr == 4) +// return PyFloat_FromDouble(self->data->box.getOutlineThickness()); +// else +// { +// PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); +// return nullptr; +// } +// } +// +// static int PyUIFrame_set_float_member(PyUIFrameObject* self, PyObject* value, void* closure) +// { +// float val; +// auto member_ptr = reinterpret_cast(closure); +// if (PyFloat_Check(value)) +// { +// val = PyFloat_AsDouble(value); +// } +// else if (PyLong_Check(value)) +// { +// val = PyLong_AsLong(value); +// } +// else +// { +// PyErr_SetString(PyExc_TypeError, "Value must be an integer."); +// return -1; +// } +// if (member_ptr == 0) //x +// self->data->box.setPosition(val, self->data->box.getPosition().y); +// else if (member_ptr == 1) //y +// self->data->box.setPosition(self->data->box.getPosition().x, val); +// else if (member_ptr == 2) //w +// self->data->box.setSize(sf::Vector2f(val, self->data->box.getSize().y)); +// else if (member_ptr == 3) //h +// self->data->box.setSize(sf::Vector2f(self->data->box.getSize().x, val)); +// else if (member_ptr == 4) //outline +// self->data->box.setOutlineThickness(val); +// return 0; +// } +// +// static PyObject* PyUIFrame_get_color_member(PyUIFrameObject* self, void* closure) +// { +// // validate closure (should be impossible to be wrong, but it's thorough) +// auto member_ptr = reinterpret_cast(closure); +// if (member_ptr != 0 && member_ptr != 1) +// { +// PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); +// return nullptr; +// } +// 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); +// +// // fetch correct member data +// sf::Color color; +// if (member_ptr == 0) +// { +// color = self->data->box.getFillColor(); +// //return Py_BuildValue("(iii)", color.r, color.g, color.b); +// } +// else if (member_ptr == 1) +// { +// color = self->data->box.getOutlineColor(); +// //return Py_BuildValue("(iii)", color.r, color.g, color.b); +// } +// +// // initialize new mcrfpy.Color instance +// //pyColorObj->data = std::make_shared(color); +// //PyColor::fromPy(pyColorObj).set(color); +// +// // TODO - supposed to return Linked +// return PyColor(color).pyObject(); +// } +// +// static int PyUIFrame_set_color_member(PyUIFrameObject* self, PyObject* value, void* closure) +// { +// //TODO: this logic of (PyColor instance OR tuple -> sf::color) should be encapsulated for reuse +// auto member_ptr = reinterpret_cast(closure); +// int r, g, b, a; +// if (PyObject_IsInstance(value, (PyObject*)&PyColorType)) +// { +// // get value from mcrfpy.Color instance +// /* +// PyColorObject* color = reinterpret_cast(value); +// r = color->data->r; +// g = color->data->g; +// b = color->data->b; +// a = color->data->a; +// std::cout << "using color: " << r << " " << g << " " << b << " " << a << std::endl; +// */ +// sf::Color c = ((PyColorObject*)value)->data; +// r = c.r; g = c.g; b = c.b; a = c.a; +// } +// else if (!PyTuple_Check(value) || PyTuple_Size(value) < 3 || PyTuple_Size(value) > 4) +// { +// // reject non-Color, non-tuple value +// PyErr_SetString(PyExc_TypeError, "Value must be a tuple of 3 or 4 integers or an mcrfpy.Color object."); +// return -1; +// } +// else // get value from tuples +// { +// r = PyLong_AsLong(PyTuple_GetItem(value, 0)); +// g = PyLong_AsLong(PyTuple_GetItem(value, 1)); +// b = PyLong_AsLong(PyTuple_GetItem(value, 2)); +// a = 255; +// +// if (PyTuple_Size(value) == 4) +// { +// a = PyLong_AsLong(PyTuple_GetItem(value, 3)); +// } +// } +// +// if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255 || a < 0 || a > 255) +// { +// PyErr_SetString(PyExc_ValueError, "Color values must be between 0 and 255."); +// return -1; +// } +// +// if (member_ptr == 0) +// { +// self->data->box.setFillColor(sf::Color(r, g, b, a)); +// } +// else if (member_ptr == 1) +// { +// self->data->box.setOutlineColor(sf::Color(r, g, b, a)); +// } +// else +// { +// PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); +// return -1; +// } +// +// return 0; +// } +// +// static PyObject* PyUIFrame_get_children(PyUIFrameObject*, void*); +// // implementation after the PyUICollectionType definition +// +// static PyGetSetDef PyUIFrame_getsetters[] = { +// {"x", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "X coordinate of top-left corner", (void*)0}, +// {"y", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "Y coordinate of top-left corner", (void*)1}, +// {"w", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "width of the rectangle", (void*)2}, +// {"h", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "height of the rectangle", (void*)3}, +// {"outline", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "Thickness of the border", (void*)4}, +// {"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} +// }; +// +// static PyObject* PyUIFrame_repr(PyUIFrameObject* self) +// { +// std::ostringstream ss; +// if (!self->data) ss << ""; +// else { +// auto box = self->data->box; +// auto fc = box.getFillColor(); +// auto oc = box.getOutlineColor(); +// ss << "data->children->size() << " child objects" << +// ")>"; +// } +// std::string repr_str = ss.str(); +// return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); +// } +// +// static int PyUIFrame_init(PyUIFrameObject* self, PyObject* args, PyObject* kwds) +// { +// //std::cout << "Init called\n"; +// static const char* keywords[] = { "x", "y", "w", "h", "fill_color", "outline_color", "outline", nullptr }; +// float x = 0.0f, y = 0.0f, w = 0.0f, h=0.0f, outline=0.0f; +// PyObject* fill_color = 0; +// PyObject* outline_color = 0; +// +// if (!PyArg_ParseTupleAndKeywords(args, kwds, "ffff|OOf", const_cast(keywords), &x, &y, &w, &h, &fill_color, &outline_color, &outline)) +// { +// return -1; +// } +// +// //self->data->x = x; +// //self->data->y = y; +// self->data->box.setPosition(sf::Vector2f(x, y)); +// self->data->box.setSize(sf::Vector2f(w, h)); +// self->data->box.setOutlineThickness(outline); +// // getsetter abuse because I haven't standardized Color object parsing (TODO) +// int err_val = 0; +// if (fill_color && fill_color != Py_None) err_val = PyUIFrame_set_color_member(self, fill_color, (void*)0); +// else self->data->box.setFillColor(sf::Color(0,0,0,255)); +// if (err_val) return err_val; +// if (outline_color && outline_color != Py_None) err_val = PyUIFrame_set_color_member(self, outline_color, (void*)1); +// else self->data->box.setOutlineColor(sf::Color(128,128,128,255)); +// if (err_val) return err_val; +// return 0; +// } +// +// static PyTypeObject PyUIFrameType = { +// //PyVarObject_HEAD_INIT(NULL, 0) +// .tp_name = "mcrfpy.Frame", +// .tp_basicsize = sizeof(PyUIFrameObject), +// .tp_itemsize = 0, +// .tp_dealloc = (destructor)[](PyObject* self) +// { +// PyUIFrameObject* obj = (PyUIFrameObject*)self; +// obj->data.reset(); +// Py_TYPE(self)->tp_free(self); +// }, +// .tp_repr = (reprfunc)PyUIFrame_repr, +// //.tp_hash = NULL, +// //.tp_iter +// //.tp_iternext +// .tp_flags = Py_TPFLAGS_DEFAULT, +// .tp_doc = PyDoc_STR("docstring"), +// //.tp_methods = PyUIFrame_methods, +// //.tp_members = PyUIFrame_members, +// .tp_getset = PyUIFrame_getsetters, +// //.tp_base = NULL, +// .tp_init = (initproc)PyUIFrame_init, +// .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* +// { +// PyUIFrameObject* self = (PyUIFrameObject*)type->tp_alloc(type, 0); +// if (self) self->data = std::make_shared(); +// return (PyObject*)self; +// } +// }; +// +// /* +// * +// * End auto-generated PyUIFrameType generation +// * +// */ +// +// /* +// * +// * Begin Python Class Instantiator (iterator helper) +// * +// */ +// +// /* // definition can't go in the header file +// PyObject* py_instance(UIDrawable* obj) +// { +// +// } +// */ +// +// /* +// * +// * End Python Class Instantitator (iterator helper) +// * +// */ +// +// /* +// * +// * Begin template generation for PyUISpriteType +// * +// */ +// +// static PyObject* PyUISprite_get_float_member(PyUISpriteObject* self, void* closure) +// { +// auto member_ptr = reinterpret_cast(closure); +// if (member_ptr == 0) +// return PyFloat_FromDouble(self->data->getPosition().x); +// else if (member_ptr == 1) +// return PyFloat_FromDouble(self->data->getPosition().y); +// else if (member_ptr == 2) +// return PyFloat_FromDouble(self->data->getScale().x); // scale X and Y are identical, presently +// else +// { +// PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); +// return nullptr; +// } +// } +// +// +// static int PyUISprite_set_float_member(PyUISpriteObject* self, PyObject* value, void* closure) +// { +// float val; +// auto member_ptr = reinterpret_cast(closure); +// if (PyFloat_Check(value)) +// { +// val = PyFloat_AsDouble(value); +// } +// else if (PyLong_Check(value)) +// { +// val = PyLong_AsLong(value); +// } +// else +// { +// PyErr_SetString(PyExc_TypeError, "Value must be a floating point number."); +// return -1; +// } +// if (member_ptr == 0) //x +// self->data->setPosition(sf::Vector2f(val, self->data->getPosition().y)); +// else if (member_ptr == 1) //y +// self->data->setPosition(sf::Vector2f(self->data->getPosition().x, val)); +// else if (member_ptr == 2) // scale +// self->data->setScale(sf::Vector2f(val, val)); +// return 0; +// } +// +// static PyObject* PyUISprite_get_int_member(PyUISpriteObject* self, void* closure) +// { +// auto member_ptr = reinterpret_cast(closure); +// if (true) {} +// else +// { +// PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); +// return nullptr; +// } +// +// return PyLong_FromDouble(self->data->getSpriteIndex()); +// } +// +// +// static int PyUISprite_set_int_member(PyUISpriteObject* self, PyObject* value, void* closure) +// { +// int val; +// auto member_ptr = reinterpret_cast(closure); +// if (PyLong_Check(value)) +// { +// val = PyLong_AsLong(value); +// } +// else +// { +// PyErr_SetString(PyExc_TypeError, "Value must be an integer."); +// return -1; +// } +// //self->data->sprite_index = val; +// //self->data->sprite.setTextureRect(self->data->itex->spriteCoordinates(val)); +// self->data->setSpriteIndex(val); +// return 0; +// } +// +// static PyObject* PyUISprite_get_texture(PyUISpriteObject* self, void* closure) +// { +// return self->data->getTexture()->pyObject(); +// } +// +// static int PyUISprite_set_texture(PyUISpriteObject* self, PyObject* value, void* closure) +// { +// return -1; +// } +// +// static PyGetSetDef PyUISprite_getsetters[] = { +// {"x", (getter)PyUISprite_get_float_member, (setter)PyUISprite_set_float_member, "X coordinate of top-left corner", (void*)0}, +// {"y", (getter)PyUISprite_get_float_member, (setter)PyUISprite_set_float_member, "Y coordinate of top-left corner", (void*)1}, +// {"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} +// }; +// +// static PyObject* PyUISprite_repr(PyUISpriteObject* self) +// { +// std::ostringstream ss; +// if (!self->data) ss << ""; +// else { +// //auto sprite = self->data->sprite; +// ss << ""; +// } +// std::string repr_str = ss.str(); +// return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); +// } +// +// static int PyUISprite_init(PyUISpriteObject* self, PyObject* args, PyObject* kwds) +// { +// //std::cout << "Init called\n"; +// static const char* keywords[] = { "x", "y", "texture", "sprite_index", "scale", nullptr }; +// float x = 0.0f, y = 0.0f, scale = 1.0f; +// int sprite_index; +// PyObject* texture; +// +// if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ffOif", +// const_cast(keywords), &x, &y, &texture, &sprite_index, &scale)) +// { +// return -1; +// } +// +// // check types for texture +// // +// // Set Texture +// // +// if (texture != NULL && !PyObject_IsInstance(texture, (PyObject*)&PyTextureType)){ +// PyErr_SetString(PyExc_TypeError, "texture must be a mcrfpy.Texture instance"); +// return -1; +// } /*else if (texture != NULL) // to be removed: UIObjects don't manage texture references +// { +// self->texture = texture; +// Py_INCREF(texture); +// } else +// { +// // default tex? +// }*/ +// auto pytexture = (PyTextureObject*)texture; +// self->data = std::make_shared(pytexture->data, sprite_index, sf::Vector2f(x, y), scale); +// self->data->setPosition(sf::Vector2f(x, y)); +// +// return 0; +// } +// +// static PyTypeObject PyUISpriteType = { +// //PyVarObject_HEAD_INIT(NULL, 0) +// .tp_name = "mcrfpy.Sprite", +// .tp_basicsize = sizeof(PyUISpriteObject), +// .tp_itemsize = 0, +// .tp_dealloc = (destructor)[](PyObject* self) +// { +// PyUISpriteObject* obj = (PyUISpriteObject*)self; +// // release reference to font object +// //if (obj->texture) Py_DECREF(obj->texture); +// obj->data.reset(); +// Py_TYPE(self)->tp_free(self); +// }, +// .tp_repr = (reprfunc)PyUISprite_repr, +// //.tp_hash = NULL, +// //.tp_iter +// //.tp_iternext +// .tp_flags = Py_TPFLAGS_DEFAULT, +// .tp_doc = PyDoc_STR("docstring"), +// //.tp_methods = PyUIFrame_methods, +// //.tp_members = PyUIFrame_members, +// .tp_getset = PyUISprite_getsetters, +// //.tp_base = NULL, +// .tp_init = (initproc)PyUISprite_init, +// .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* +// { +// PyUISpriteObject* self = (PyUISpriteObject*)type->tp_alloc(type, 0); +// //if (self) self->data = std::make_shared(); +// return (PyObject*)self; +// } +// }; +// /* +// * +// * End template for PyUISpriteType +// * +// */ +// +// +// +// +// /* +// * +// * PyUIGridPoint defs +// * +// */ +// // TODO: question: are sfColor_to_PyObject and PyObject_to_sfColor duplicitive? How does UIFrame get/set colors? - +// // Utility function to convert sf::Color to PyObject* -static PyObject* sfColor_to_PyObject(sf::Color color) { - return Py_BuildValue("(iiii)", color.r, color.g, color.b, color.a); -} - +//static PyObject* sfColor_to_PyObject(sf::Color color) { +// return Py_BuildValue("(iiii)", color.r, color.g, color.b, color.a); +//} +// // Utility function to convert PyObject* to sf::Color -static sf::Color PyObject_to_sfColor(PyObject* obj) { - int r, g, b, a = 255; // Default alpha to fully opaque if not specified - if (!PyArg_ParseTuple(obj, "iii|i", &r, &g, &b, &a)) { - return sf::Color(); // Return default color on parse error - } - return sf::Color(r, g, b, a); -} - -static PyObject* PyUIGridPoint_get_color(PyUIGridPointObject* self, void* closure) { - if (reinterpret_cast(closure) == 0) { // color - return sfColor_to_PyObject(self->data->color); - } else { // color_overlay - return sfColor_to_PyObject(self->data->color_overlay); - } -} - -static int PyUIGridPoint_set_color(PyUIGridPointObject* self, PyObject* value, void* closure) { - sf::Color color = PyObject_to_sfColor(value); - if (reinterpret_cast(closure) == 0) { // color - self->data->color = color; - } else { // color_overlay - self->data->color_overlay = color; - } - return 0; -} - -static PyObject* PyUIGridPoint_get_bool_member(PyUIGridPointObject* self, void* closure) { - if (reinterpret_cast(closure) == 0) { // walkable - return PyBool_FromLong(self->data->walkable); - } else { // transparent - return PyBool_FromLong(self->data->transparent); - } -} - -static int PyUIGridPoint_set_bool_member(PyUIGridPointObject* self, PyObject* value, void* closure) { - if (value == Py_True) { - if (reinterpret_cast(closure) == 0) { // walkable - self->data->walkable = true; - } else { // transparent - self->data->transparent = true; - } - } else if (value == Py_False) { - if (reinterpret_cast(closure) == 0) { // walkable - self->data->walkable = false; - } else { // transparent - self->data->transparent = false; - } - } else { - PyErr_SetString(PyExc_ValueError, "Expected a boolean value"); - return -1; - } - return 0; -} - -static PyObject* PyUIGridPoint_get_int_member(PyUIGridPointObject* self, void* closure) { - switch(reinterpret_cast(closure)) { - case 0: return PyLong_FromLong(self->data->tilesprite); - case 1: return PyLong_FromLong(self->data->tile_overlay); - case 2: return PyLong_FromLong(self->data->uisprite); - default: PyErr_SetString(PyExc_RuntimeError, "Invalid closure"); return nullptr; - } -} - -static int PyUIGridPoint_set_int_member(PyUIGridPointObject* self, PyObject* value, void* closure) { - long val = PyLong_AsLong(value); - if (PyErr_Occurred()) return -1; - - switch(reinterpret_cast(closure)) { - case 0: self->data->tilesprite = val; break; - case 1: self->data->tile_overlay = val; break; - case 2: self->data->uisprite = val; break; - default: PyErr_SetString(PyExc_RuntimeError, "Invalid closure"); return -1; - } - return 0; -} - -static PyGetSetDef PyUIGridPoint_getsetters[] = { - {"color", (getter)PyUIGridPoint_get_color, (setter)PyUIGridPoint_set_color, "GridPoint color", (void*)0}, - {"color_overlay", (getter)PyUIGridPoint_get_color, (setter)PyUIGridPoint_set_color, "GridPoint color overlay", (void*)1}, - {"walkable", (getter)PyUIGridPoint_get_bool_member, (setter)PyUIGridPoint_set_bool_member, "Is the GridPoint walkable", (void*)0}, - {"transparent", (getter)PyUIGridPoint_get_bool_member, (setter)PyUIGridPoint_set_bool_member, "Is the GridPoint transparent", (void*)1}, - {"tilesprite", (getter)PyUIGridPoint_get_int_member, (setter)PyUIGridPoint_set_int_member, "Tile sprite index", (void*)0}, - {"tile_overlay", (getter)PyUIGridPoint_get_int_member, (setter)PyUIGridPoint_set_int_member, "Tile overlay sprite index", (void*)1}, - {"uisprite", (getter)PyUIGridPoint_get_int_member, (setter)PyUIGridPoint_set_int_member, "UI sprite index", (void*)2}, - {NULL} /* Sentinel */ -}; - -static PyTypeObject PyUIGridPointType = { - //PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "mcrfpy.GridPoint", - .tp_basicsize = sizeof(PyUIGridPointObject), - .tp_itemsize = 0, - // Methods omitted for brevity - .tp_flags = Py_TPFLAGS_DEFAULT, - .tp_doc = "UIGridPoint objects", - .tp_getset = PyUIGridPoint_getsetters, - //.tp_init = (initproc)PyUIGridPoint_init, // TODO Define the init function - .tp_new = PyType_GenericNew, -}; - - /* - * - * end PyUIGridPoint defs - * - */ - - - - /* - * - * PyUIGridPointState defs - * - */ - -static PyObject* PyUIGridPointState_get_bool_member(PyUIGridPointStateObject* self, void* closure) { - if (reinterpret_cast(closure) == 0) { // visible - return PyBool_FromLong(self->data->visible); - } else { // discovered - return PyBool_FromLong(self->data->discovered); - } -} - -static int PyUIGridPointState_set_bool_member(PyUIGridPointStateObject* self, PyObject* value, void* closure) { - if (!PyBool_Check(value)) { - PyErr_SetString(PyExc_TypeError, "Value must be a boolean"); - return -1; - } - - int truthValue = PyObject_IsTrue(value); - if (truthValue < 0) { - return -1; // PyObject_IsTrue returns -1 on error - } - - if (reinterpret_cast(closure) == 0) { // visible - self->data->visible = truthValue; - } else { // discovered - self->data->discovered = truthValue; - } - - return 0; -} - -static PyGetSetDef PyUIGridPointState_getsetters[] = { - {"visible", (getter)PyUIGridPointState_get_bool_member, (setter)PyUIGridPointState_set_bool_member, "Is the GridPointState visible", (void*)0}, - {"discovered", (getter)PyUIGridPointState_get_bool_member, (setter)PyUIGridPointState_set_bool_member, "Has the GridPointState been discovered", (void*)1}, - {NULL} /* Sentinel */ -}; - -static PyTypeObject PyUIGridPointStateType = { - //PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "mcrfpy.GridPointState", - .tp_basicsize = sizeof(PyUIGridPointStateObject), - .tp_itemsize = 0, - // Methods omitted for brevity - .tp_flags = Py_TPFLAGS_DEFAULT, - .tp_doc = "UIGridPointState objects", // TODO: Add PyUIGridPointState tp_init - .tp_getset = PyUIGridPointState_getsetters, - .tp_new = PyType_GenericNew, -}; - - - /* - * - * end PyUIGridPointState defs - * - */ - - /* - * - * PyUIEntity defs - * - */ - +//static sf::Color PyObject_to_sfColor(PyObject* obj) { +// int r, g, b, a = 255; // Default alpha to fully opaque if not specified +// if (!PyArg_ParseTuple(obj, "iii|i", &r, &g, &b, &a)) { +// return sf::Color(); // Return default color on parse error +// } +// return sf::Color(r, g, b, a); +//} +// +//static PyObject* PyUIGridPoint_get_color(PyUIGridPointObject* self, void* closure) { +// if (reinterpret_cast(closure) == 0) { // color +// return sfColor_to_PyObject(self->data->color); +// } else { // color_overlay +// return sfColor_to_PyObject(self->data->color_overlay); +// } +//} +// +//static int PyUIGridPoint_set_color(PyUIGridPointObject* self, PyObject* value, void* closure) { +// sf::Color color = PyObject_to_sfColor(value); +// if (reinterpret_cast(closure) == 0) { // color +// self->data->color = color; +// } else { // color_overlay +// self->data->color_overlay = color; +// } +// return 0; +//} +// +//static PyObject* PyUIGridPoint_get_bool_member(PyUIGridPointObject* self, void* closure) { +// if (reinterpret_cast(closure) == 0) { // walkable +// return PyBool_FromLong(self->data->walkable); +// } else { // transparent +// return PyBool_FromLong(self->data->transparent); +// } +//} +// +//static int PyUIGridPoint_set_bool_member(PyUIGridPointObject* self, PyObject* value, void* closure) { +// if (value == Py_True) { +// if (reinterpret_cast(closure) == 0) { // walkable +// self->data->walkable = true; +// } else { // transparent +// self->data->transparent = true; +// } +// } else if (value == Py_False) { +// if (reinterpret_cast(closure) == 0) { // walkable +// self->data->walkable = false; +// } else { // transparent +// self->data->transparent = false; +// } +// } else { +// PyErr_SetString(PyExc_ValueError, "Expected a boolean value"); +// return -1; +// } +// return 0; +//} +// +//static PyObject* PyUIGridPoint_get_int_member(PyUIGridPointObject* self, void* closure) { +// switch(reinterpret_cast(closure)) { +// case 0: return PyLong_FromLong(self->data->tilesprite); +// case 1: return PyLong_FromLong(self->data->tile_overlay); +// case 2: return PyLong_FromLong(self->data->uisprite); +// default: PyErr_SetString(PyExc_RuntimeError, "Invalid closure"); return nullptr; +// } +//} +// +//static int PyUIGridPoint_set_int_member(PyUIGridPointObject* self, PyObject* value, void* closure) { +// long val = PyLong_AsLong(value); +// if (PyErr_Occurred()) return -1; +// +// switch(reinterpret_cast(closure)) { +// case 0: self->data->tilesprite = val; break; +// case 1: self->data->tile_overlay = val; break; +// case 2: self->data->uisprite = val; break; +// default: PyErr_SetString(PyExc_RuntimeError, "Invalid closure"); return -1; +// } +// return 0; +//} +// +//static PyGetSetDef PyUIGridPoint_getsetters[] = { +// {"color", (getter)PyUIGridPoint_get_color, (setter)PyUIGridPoint_set_color, "GridPoint color", (void*)0}, +// {"color_overlay", (getter)PyUIGridPoint_get_color, (setter)PyUIGridPoint_set_color, "GridPoint color overlay", (void*)1}, +// {"walkable", (getter)PyUIGridPoint_get_bool_member, (setter)PyUIGridPoint_set_bool_member, "Is the GridPoint walkable", (void*)0}, +// {"transparent", (getter)PyUIGridPoint_get_bool_member, (setter)PyUIGridPoint_set_bool_member, "Is the GridPoint transparent", (void*)1}, +// {"tilesprite", (getter)PyUIGridPoint_get_int_member, (setter)PyUIGridPoint_set_int_member, "Tile sprite index", (void*)0}, +// {"tile_overlay", (getter)PyUIGridPoint_get_int_member, (setter)PyUIGridPoint_set_int_member, "Tile overlay sprite index", (void*)1}, +// {"uisprite", (getter)PyUIGridPoint_get_int_member, (setter)PyUIGridPoint_set_int_member, "UI sprite index", (void*)2}, +// {NULL} /* Sentinel */ +//}; +// +//static PyTypeObject PyUIGridPointType = { +// //PyVarObject_HEAD_INIT(NULL, 0) +// .tp_name = "mcrfpy.GridPoint", +// .tp_basicsize = sizeof(PyUIGridPointObject), +// .tp_itemsize = 0, +// // Methods omitted for brevity +// .tp_flags = Py_TPFLAGS_DEFAULT, +// .tp_doc = "UIGridPoint objects", +// .tp_getset = PyUIGridPoint_getsetters, +// //.tp_init = (initproc)PyUIGridPoint_init, // TODO Define the init function +// .tp_new = PyType_GenericNew, +//}; +// +// /* +// * +// * end PyUIGridPoint defs +// * +// */ +// +// +// +// /* +// * +// * PyUIGridPointState defs +// * +// */ +// +//static PyObject* PyUIGridPointState_get_bool_member(PyUIGridPointStateObject* self, void* closure) { +// if (reinterpret_cast(closure) == 0) { // visible +// return PyBool_FromLong(self->data->visible); +// } else { // discovered +// return PyBool_FromLong(self->data->discovered); +// } +//} +// +//static int PyUIGridPointState_set_bool_member(PyUIGridPointStateObject* self, PyObject* value, void* closure) { +// if (!PyBool_Check(value)) { +// PyErr_SetString(PyExc_TypeError, "Value must be a boolean"); +// return -1; +// } +// +// int truthValue = PyObject_IsTrue(value); +// if (truthValue < 0) { +// return -1; // PyObject_IsTrue returns -1 on error +// } +// +// if (reinterpret_cast(closure) == 0) { // visible +// self->data->visible = truthValue; +// } else { // discovered +// self->data->discovered = truthValue; +// } +// +// return 0; +//} +// +//static PyGetSetDef PyUIGridPointState_getsetters[] = { +// {"visible", (getter)PyUIGridPointState_get_bool_member, (setter)PyUIGridPointState_set_bool_member, "Is the GridPointState visible", (void*)0}, +// {"discovered", (getter)PyUIGridPointState_get_bool_member, (setter)PyUIGridPointState_set_bool_member, "Has the GridPointState been discovered", (void*)1}, +// {NULL} /* Sentinel */ +//}; +// +//static PyTypeObject PyUIGridPointStateType = { +// //PyVarObject_HEAD_INIT(NULL, 0) +// .tp_name = "mcrfpy.GridPointState", +// .tp_basicsize = sizeof(PyUIGridPointStateObject), +// .tp_itemsize = 0, +// // Methods omitted for brevity +// .tp_flags = Py_TPFLAGS_DEFAULT, +// .tp_doc = "UIGridPointState objects", // TODO: Add PyUIGridPointState tp_init +// .tp_getset = PyUIGridPointState_getsetters, +// .tp_new = PyType_GenericNew, +//}; +// +// +// /* +// * +// * end PyUIGridPointState defs +// * +// */ +// +// /* +// * +// * PyUIEntity defs +// * +// */ +// // TODO: sf::Vector2f convenience functions here might benefit from a PyVectorObject much like PyColorObject // Utility function to convert sf::Vector2f to PyObject* -static PyObject* sfVector2f_to_PyObject(sf::Vector2f vector) { - return Py_BuildValue("(ff)", vector.x, vector.y); -} - +//static PyObject* sfVector2f_to_PyObject(sf::Vector2f vector) { +// return Py_BuildValue("(ff)", vector.x, vector.y); +//} +// // Utility function to convert PyObject* to sf::Vector2f -static sf::Vector2f PyObject_to_sfVector2f(PyObject* obj) { - float x, y; - if (!PyArg_ParseTuple(obj, "ff", &x, &y)) { - return sf::Vector2f(); // TODO / reconsider this default: Return default vector on parse error - } - return sf::Vector2f(x, y); -} - +//static sf::Vector2f PyObject_to_sfVector2f(PyObject* obj) { +// float x, y; +// if (!PyArg_ParseTuple(obj, "ff", &x, &y)) { +// return sf::Vector2f(); // TODO / reconsider this default: Return default vector on parse error +// } +// return sf::Vector2f(x, y); +//} +// // Utility function to convert UIGridPointState to PyObject* -static PyObject* UIGridPointState_to_PyObject(const UIGridPointState& state) { - PyObject* obj = PyObject_New(PyObject, &PyUIGridPointStateType); - if (!obj) return PyErr_NoMemory(); - - // Assuming PyUIGridPointStateObject structure has a UIGridPointState* member called 'data' - //((PyUIGridPointStateObject*)obj)->data = new UIGridPointState(state); // Copy constructor // wontimplement / feat - don't use new, get shared_ptr working - - return obj; -} - +//static PyObject* UIGridPointState_to_PyObject(const UIGridPointState& state) { +// PyObject* obj = PyObject_New(PyObject, &PyUIGridPointStateType); +// if (!obj) return PyErr_NoMemory(); +// +// // Assuming PyUIGridPointStateObject structure has a UIGridPointState* member called 'data' +// //((PyUIGridPointStateObject*)obj)->data = new UIGridPointState(state); // Copy constructor // wontimplement / feat - don't use new, get shared_ptr working +// +// return obj; +//} +// // Function to convert std::vector to a Python list TODO need a PyUICollection style iterable -static PyObject* UIGridPointStateVector_to_PyList(const std::vector& vec) { - PyObject* list = PyList_New(vec.size()); - if (!list) return PyErr_NoMemory(); - - for (size_t i = 0; i < vec.size(); ++i) { - PyObject* obj = UIGridPointState_to_PyObject(vec[i]); - if (!obj) { // Cleanup on failure - Py_DECREF(list); - return NULL; - } - PyList_SET_ITEM(list, i, obj); // This steals a reference to obj - } - - return list; -} - -static PyObject* PyUIEntity_get_position(PyUIEntityObject* self, void* closure) { - return sfVector2f_to_PyObject(self->data->position); -} - -static int PyUIEntity_set_position(PyUIEntityObject* self, PyObject* value, void* closure) { - self->data->position = PyObject_to_sfVector2f(value); - return 0; -} - -static PyObject* PyUIEntity_get_gridstate(PyUIEntityObject* self, void* closure) { - // Assuming a function to convert std::vector to PyObject* list - return UIGridPointStateVector_to_PyList(self->data->gridstate); -} - -static PyObject* PyUIEntity_get_spritenumber(PyUIEntityObject* self, void* closure) { - return PyLong_FromDouble(self->data->sprite.getSpriteIndex()); -} - -static int PyUIEntity_set_spritenumber(PyUIEntityObject* self, PyObject* value, void* closure) { - int val; - if (PyLong_Check(value)) - val = PyLong_AsLong(value); - else - { - PyErr_SetString(PyExc_TypeError, "Value must be an integer."); - return -1; - } - //self->data->sprite.sprite_index = val; - self->data->sprite.setSpriteIndex(val); // todone - I don't like ".sprite.sprite" in this stack of UIEntity.UISprite.sf::Sprite - return 0; -} - -static PyObject* PyUIEntity_at(PyUIEntityObject* self, PyObject* o) -{ - int x, y; - if (!PyArg_ParseTuple(o, "ii", &x, &y)) { - PyErr_SetString(PyExc_TypeError, "UIEntity.at requires two integer arguments: (x, y)"); - return NULL; - } - - if (self->data->grid == NULL) { - PyErr_SetString(PyExc_ValueError, "Entity cannot access surroundings because it is not associated with a grid"); - return NULL; - } - - PyUIGridPointStateObject* obj = (PyUIGridPointStateObject*)((&PyUIGridPointStateType)->tp_alloc(&PyUIGridPointStateType, 0)); - //auto target = std::static_pointer_cast(target); - obj->data = &(self->data->gridstate[y + self->data->grid->grid_x * x]); - obj->grid = self->data->grid; - obj->entity = self->data; - return (PyObject*)obj; -} - -static PyMethodDef PyUIEntity_methods[] = { - {"at", (PyCFunction)PyUIEntity_at, METH_O}, - {NULL, NULL, 0, NULL} -}; - +//static PyObject* UIGridPointStateVector_to_PyList(const std::vector& vec) { +// PyObject* list = PyList_New(vec.size()); +// if (!list) return PyErr_NoMemory(); +// +// for (size_t i = 0; i < vec.size(); ++i) { +// PyObject* obj = UIGridPointState_to_PyObject(vec[i]); +// if (!obj) { // Cleanup on failure +// Py_DECREF(list); +// return NULL; +// } +// PyList_SET_ITEM(list, i, obj); // This steals a reference to obj +// } +// +// return list; +//} +// +//static PyObject* PyUIEntity_get_position(PyUIEntityObject* self, void* closure) { +// return sfVector2f_to_PyObject(self->data->position); +//} +// +//static int PyUIEntity_set_position(PyUIEntityObject* self, PyObject* value, void* closure) { +// self->data->position = PyObject_to_sfVector2f(value); +// return 0; +//} +// +//static PyObject* PyUIEntity_get_gridstate(PyUIEntityObject* self, void* closure) { +// // Assuming a function to convert std::vector to PyObject* list +// return UIGridPointStateVector_to_PyList(self->data->gridstate); +//} +// +//static PyObject* PyUIEntity_get_spritenumber(PyUIEntityObject* self, void* closure) { +// return PyLong_FromDouble(self->data->sprite.getSpriteIndex()); +//} +// +//static int PyUIEntity_set_spritenumber(PyUIEntityObject* self, PyObject* value, void* closure) { +// int val; +// if (PyLong_Check(value)) +// val = PyLong_AsLong(value); +// else +// { +// PyErr_SetString(PyExc_TypeError, "Value must be an integer."); +// return -1; +// } +// //self->data->sprite.sprite_index = val; +// self->data->sprite.setSpriteIndex(val); // todone - I don't like ".sprite.sprite" in this stack of UIEntity.UISprite.sf::Sprite +// return 0; +//} +// +//static PyObject* PyUIEntity_at(PyUIEntityObject* self, PyObject* o) +//{ +// int x, y; +// if (!PyArg_ParseTuple(o, "ii", &x, &y)) { +// PyErr_SetString(PyExc_TypeError, "UIEntity.at requires two integer arguments: (x, y)"); +// return NULL; +// } +// +// if (self->data->grid == NULL) { +// PyErr_SetString(PyExc_ValueError, "Entity cannot access surroundings because it is not associated with a grid"); +// return NULL; +// } +// +// PyUIGridPointStateObject* obj = (PyUIGridPointStateObject*)((&PyUIGridPointStateType)->tp_alloc(&PyUIGridPointStateType, 0)); +// //auto target = std::static_pointer_cast(target); +// obj->data = &(self->data->gridstate[y + self->data->grid->grid_x * x]); +// obj->grid = self->data->grid; +// obj->entity = self->data; +// return (PyObject*)obj; +//} +// +//static PyMethodDef PyUIEntity_methods[] = { +// {"at", (PyCFunction)PyUIEntity_at, METH_O}, +// {NULL, NULL, 0, NULL} +//}; +// // Define getters and setters -static PyGetSetDef PyUIEntity_getsetters[] = { - {"position", (getter)PyUIEntity_get_position, (setter)PyUIEntity_set_position, "Entity position", NULL}, - {"gridstate", (getter)PyUIEntity_get_gridstate, NULL, "Grid point states for the entity", NULL}, - {"sprite_number", (getter)PyUIEntity_get_spritenumber, (setter)PyUIEntity_set_spritenumber, "Sprite number (index) on the texture on the display", NULL}, - {NULL} /* Sentinel */ -}; - -static int PyUIEntity_init(PyUIEntityObject*, PyObject*, PyObject*); // forward declare - - - +//static PyGetSetDef PyUIEntity_getsetters[] = { +// {"position", (getter)PyUIEntity_get_position, (setter)PyUIEntity_set_position, "Entity position", NULL}, +// {"gridstate", (getter)PyUIEntity_get_gridstate, NULL, "Grid point states for the entity", NULL}, +// {"sprite_number", (getter)PyUIEntity_get_spritenumber, (setter)PyUIEntity_set_spritenumber, "Sprite number (index) on the texture on the display", NULL}, +// {NULL} /* Sentinel */ +//}; +// +//static int PyUIEntity_init(PyUIEntityObject*, PyObject*, PyObject*); // forward declare +// +// +// // Define the PyTypeObject for UIEntity -static PyTypeObject PyUIEntityType = { - //PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "mcrfpy.Entity", - .tp_basicsize = sizeof(PyUIEntityObject), - .tp_itemsize = 0, - // Methods omitted for brevity - .tp_flags = Py_TPFLAGS_DEFAULT, - .tp_doc = "UIEntity objects", - .tp_methods = PyUIEntity_methods, - .tp_getset = PyUIEntity_getsetters, - .tp_init = (initproc)PyUIEntity_init, - .tp_new = PyType_GenericNew, -}; - - - /* - * - * end PyUIEntity defs - * - */ - - /* - * - * PyUIGrid defs - * - */ - -static int PyUIGrid_init(PyUIGridObject* self, PyObject* args, PyObject* kwds) { - int grid_x, grid_y; - PyObject* textureObj; - float box_x, box_y, box_w, box_h; - - if (!PyArg_ParseTuple(args, "iiOffff", &grid_x, &grid_y, &textureObj, &box_x, &box_y, &box_w, &box_h)) { - return -1; // If parsing fails, return an error - } - - // Convert PyObject texture to IndexTexture* - // This requires the texture object to have been initialized similar to UISprite's texture handling - if (!PyObject_IsInstance(textureObj, (PyObject*)&PyTextureType)) { - PyErr_SetString(PyExc_TypeError, "texture must be a mcrfpy.Texture instance"); - return -1; - } - PyTextureObject* pyTexture = reinterpret_cast(textureObj); - // TODO (7DRL day 2, item 4.) use shared_ptr / PyTextureObject on UIGrid - //IndexTexture* texture = pyTexture->data.get(); - - // Initialize UIGrid - //self->data = new UIGrid(grid_x, grid_y, texture, sf::Vector2f(box_x, box_y), sf::Vector2f(box_w, box_h)); - self->data = std::make_shared(grid_x, grid_y, pyTexture->data, - sf::Vector2f(box_x, box_y), sf::Vector2f(box_w, box_h)); - return 0; // Success -} - -static PyObject* PyUIGrid_get_grid_size(PyUIGridObject* self, void* closure) { - return Py_BuildValue("(ii)", self->data->grid_x, self->data->grid_y); -} - -static PyObject* PyUIGrid_get_position(PyUIGridObject* self, void* closure) { - auto& box = self->data->box; - return Py_BuildValue("(ff)", box.getPosition().x, box.getPosition().y); -} - -static int PyUIGrid_set_position(PyUIGridObject* self, PyObject* value, void* closure) { - float x, y; - if (!PyArg_ParseTuple(value, "ff", &x, &y)) { - PyErr_SetString(PyExc_ValueError, "Position must be a tuple of two floats"); - return -1; - } - self->data->box.setPosition(x, y); - return 0; -} - -static PyObject* PyUIGrid_get_size(PyUIGridObject* self, void* closure) { - auto& box = self->data->box; - return Py_BuildValue("(ff)", box.getSize().x, box.getSize().y); -} - -static int PyUIGrid_set_size(PyUIGridObject* self, PyObject* value, void* closure) { - float w, h; - if (!PyArg_ParseTuple(value, "ff", &w, &h)) { - PyErr_SetString(PyExc_ValueError, "Size must be a tuple of two floats"); - return -1; - } - self->data->box.setSize(sf::Vector2f(w, h)); - return 0; -} - -static PyObject* PyUIGrid_get_center(PyUIGridObject* self, void* closure) { - return Py_BuildValue("(ff)", self->data->center_x, self->data->center_y); -} - -static int PyUIGrid_set_center(PyUIGridObject* self, PyObject* value, void* closure) { - float x, y; - if (!PyArg_ParseTuple(value, "ff", &x, &y)) { - PyErr_SetString(PyExc_ValueError, "Size must be a tuple of two floats"); - return -1; - } - self->data->center_x = x; - self->data->center_y = y; - return 0; -} - -static PyObject* PyUIGrid_get_float_member(PyUIGridObject* self, void* closure) -{ - auto member_ptr = reinterpret_cast(closure); - if (member_ptr == 0) // x - return PyFloat_FromDouble(self->data->box.getPosition().x); - else if (member_ptr == 1) // y - return PyFloat_FromDouble(self->data->box.getPosition().y); - else if (member_ptr == 2) // w - return PyFloat_FromDouble(self->data->box.getSize().x); - else if (member_ptr == 3) // h - return PyFloat_FromDouble(self->data->box.getSize().y); - else if (member_ptr == 4) // center_x - return PyFloat_FromDouble(self->data->center_x); - else if (member_ptr == 5) // center_y - return PyFloat_FromDouble(self->data->center_y); - else if (member_ptr == 6) // zoom - return PyFloat_FromDouble(self->data->zoom); - else - { - PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); - return nullptr; - } -} - - -static int PyUIGrid_set_float_member(PyUIGridObject* self, PyObject* value, void* closure) -{ - float val; - auto member_ptr = reinterpret_cast(closure); - if (PyFloat_Check(value)) - { - val = PyFloat_AsDouble(value); - } - else if (PyLong_Check(value)) - { - val = PyLong_AsLong(value); - } - else - { - PyErr_SetString(PyExc_TypeError, "Value must be a floating point number."); - return -1; - } - if (member_ptr == 0) // x - self->data->box.setPosition(val, self->data->box.getPosition().y); - else if (member_ptr == 1) // y - self->data->box.setPosition(self->data->box.getPosition().x, val); - else if (member_ptr == 2) // w - self->data->box.setSize(sf::Vector2f(val, self->data->box.getSize().y)); - else if (member_ptr == 3) // h - self->data->box.setSize(sf::Vector2f(self->data->box.getSize().x, val)); - else if (member_ptr == 4) // center_x - self->data->center_x = val; - else if (member_ptr == 5) // center_y - self->data->center_y = val; - else if (member_ptr == 6) // zoom - self->data->zoom = val; - return 0; -} +//static PyTypeObject PyUIEntityType = { +// //PyVarObject_HEAD_INIT(NULL, 0) +// .tp_name = "mcrfpy.Entity", +// .tp_basicsize = sizeof(PyUIEntityObject), +// .tp_itemsize = 0, +// // Methods omitted for brevity +// .tp_flags = Py_TPFLAGS_DEFAULT, +// .tp_doc = "UIEntity objects", +// .tp_methods = PyUIEntity_methods, +// .tp_getset = PyUIEntity_getsetters, +// .tp_init = (initproc)PyUIEntity_init, +// .tp_new = PyType_GenericNew, +//}; +// +// +// /* +// * +// * end PyUIEntity defs +// * +// */ +// +// /* +// * +// * PyUIGrid defs +// * +// */ +// +//static int PyUIGrid_init(PyUIGridObject* self, PyObject* args, PyObject* kwds) { +// int grid_x, grid_y; +// PyObject* textureObj; +// float box_x, box_y, box_w, box_h; +// +// if (!PyArg_ParseTuple(args, "iiOffff", &grid_x, &grid_y, &textureObj, &box_x, &box_y, &box_w, &box_h)) { +// return -1; // If parsing fails, return an error +// } +// +// // Convert PyObject texture to IndexTexture* +// // This requires the texture object to have been initialized similar to UISprite's texture handling +// if (!PyObject_IsInstance(textureObj, (PyObject*)&PyTextureType)) { +// PyErr_SetString(PyExc_TypeError, "texture must be a mcrfpy.Texture instance"); +// return -1; +// } +// PyTextureObject* pyTexture = reinterpret_cast(textureObj); +// // TODO (7DRL day 2, item 4.) use shared_ptr / PyTextureObject on UIGrid +// //IndexTexture* texture = pyTexture->data.get(); +// +// // Initialize UIGrid +// //self->data = new UIGrid(grid_x, grid_y, texture, sf::Vector2f(box_x, box_y), sf::Vector2f(box_w, box_h)); +// self->data = std::make_shared(grid_x, grid_y, pyTexture->data, +// sf::Vector2f(box_x, box_y), sf::Vector2f(box_w, box_h)); +// return 0; // Success +//} +// +//static PyObject* PyUIGrid_get_grid_size(PyUIGridObject* self, void* closure) { +// return Py_BuildValue("(ii)", self->data->grid_x, self->data->grid_y); +//} +// +//static PyObject* PyUIGrid_get_position(PyUIGridObject* self, void* closure) { +// auto& box = self->data->box; +// return Py_BuildValue("(ff)", box.getPosition().x, box.getPosition().y); +//} +// +//static int PyUIGrid_set_position(PyUIGridObject* self, PyObject* value, void* closure) { +// float x, y; +// if (!PyArg_ParseTuple(value, "ff", &x, &y)) { +// PyErr_SetString(PyExc_ValueError, "Position must be a tuple of two floats"); +// return -1; +// } +// self->data->box.setPosition(x, y); +// return 0; +//} +// +//static PyObject* PyUIGrid_get_size(PyUIGridObject* self, void* closure) { +// auto& box = self->data->box; +// return Py_BuildValue("(ff)", box.getSize().x, box.getSize().y); +//} +// +//static int PyUIGrid_set_size(PyUIGridObject* self, PyObject* value, void* closure) { +// float w, h; +// if (!PyArg_ParseTuple(value, "ff", &w, &h)) { +// PyErr_SetString(PyExc_ValueError, "Size must be a tuple of two floats"); +// return -1; +// } +// self->data->box.setSize(sf::Vector2f(w, h)); +// return 0; +//} +// +//static PyObject* PyUIGrid_get_center(PyUIGridObject* self, void* closure) { +// return Py_BuildValue("(ff)", self->data->center_x, self->data->center_y); +//} +// +//static int PyUIGrid_set_center(PyUIGridObject* self, PyObject* value, void* closure) { +// float x, y; +// if (!PyArg_ParseTuple(value, "ff", &x, &y)) { +// PyErr_SetString(PyExc_ValueError, "Size must be a tuple of two floats"); +// return -1; +// } +// self->data->center_x = x; +// self->data->center_y = y; +// return 0; +//} +// +//static PyObject* PyUIGrid_get_float_member(PyUIGridObject* self, void* closure) +//{ +// auto member_ptr = reinterpret_cast(closure); +// if (member_ptr == 0) // x +// return PyFloat_FromDouble(self->data->box.getPosition().x); +// else if (member_ptr == 1) // y +// return PyFloat_FromDouble(self->data->box.getPosition().y); +// else if (member_ptr == 2) // w +// return PyFloat_FromDouble(self->data->box.getSize().x); +// else if (member_ptr == 3) // h +// return PyFloat_FromDouble(self->data->box.getSize().y); +// else if (member_ptr == 4) // center_x +// return PyFloat_FromDouble(self->data->center_x); +// else if (member_ptr == 5) // center_y +// return PyFloat_FromDouble(self->data->center_y); +// else if (member_ptr == 6) // zoom +// return PyFloat_FromDouble(self->data->zoom); +// else +// { +// PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); +// return nullptr; +// } +//} +// +// +//static int PyUIGrid_set_float_member(PyUIGridObject* self, PyObject* value, void* closure) +//{ +// float val; +// auto member_ptr = reinterpret_cast(closure); +// if (PyFloat_Check(value)) +// { +// val = PyFloat_AsDouble(value); +// } +// else if (PyLong_Check(value)) +// { +// val = PyLong_AsLong(value); +// } +// else +// { +// PyErr_SetString(PyExc_TypeError, "Value must be a floating point number."); +// return -1; +// } +// if (member_ptr == 0) // x +// self->data->box.setPosition(val, self->data->box.getPosition().y); +// else if (member_ptr == 1) // y +// self->data->box.setPosition(self->data->box.getPosition().x, val); +// else if (member_ptr == 2) // w +// self->data->box.setSize(sf::Vector2f(val, self->data->box.getSize().y)); +// else if (member_ptr == 3) // h +// self->data->box.setSize(sf::Vector2f(self->data->box.getSize().x, val)); +// else if (member_ptr == 4) // center_x +// self->data->center_x = val; +// else if (member_ptr == 5) // center_y +// self->data->center_y = val; +// else if (member_ptr == 6) // zoom +// self->data->zoom = val; +// return 0; +//} // TODO (7DRL Day 2, item 5.) return Texture object -/* -static PyObject* PyUIGrid_get_texture(PyUIGridObject* self, void* closure) { - Py_INCREF(self->texture); - return self->texture; -} -*/ -static PyObject* PyUIGrid_get_texture(PyUIGridObject* self, void* closure) { - //return self->data->getTexture()->pyObject(); - PyTextureObject* obj = (PyTextureObject*)((&PyTextureType)->tp_alloc(&PyTextureType, 0)); - obj->data = self->data->getTexture(); - return (PyObject*)obj; -} - -static PyObject* PyUIGrid_at(PyUIGridObject* self, PyObject* o) -{ - int x, y; - if (!PyArg_ParseTuple(o, "ii", &x, &y)) { - PyErr_SetString(PyExc_TypeError, "UIGrid.at requires two integer arguments: (x, y)"); - return NULL; - } - if (x < 0 || x >= self->data->grid_x) { - PyErr_SetString(PyExc_ValueError, "x value out of range (0, Grid.grid_y)"); - return NULL; - } - if (y < 0 || y >= self->data->grid_y) { - PyErr_SetString(PyExc_ValueError, "y value out of range (0, Grid.grid_y)"); - return NULL; - } - - PyUIGridPointObject* obj = (PyUIGridPointObject*)((&PyUIGridPointType)->tp_alloc(&PyUIGridPointType, 0)); - //auto target = std::static_pointer_cast(target); - obj->data = &(self->data->points[x + self->data->grid_x * y]); - obj->grid = self->data; - return (PyObject*)obj; -} - -static PyMethodDef PyUIGrid_methods[] = { - {"at", (PyCFunction)PyUIGrid_at, METH_O}, - {NULL, NULL, 0, NULL} -}; - -static PyObject* PyUIGrid_get_children(PyUIGridObject* self, void* closure); // forward declare - -static PyGetSetDef PyUIGrid_getsetters[] = { - - // TODO - refactor into get_vector_member with field identifier values `(void*)n` - {"grid_size", (getter)PyUIGrid_get_grid_size, NULL, "Grid dimensions (grid_x, grid_y)", NULL}, - {"position", (getter)PyUIGrid_get_position, (setter)PyUIGrid_set_position, "Position of the grid (x, y)", NULL}, - {"size", (getter)PyUIGrid_get_size, (setter)PyUIGrid_set_size, "Size of the grid (width, height)", NULL}, - {"center", (getter)PyUIGrid_get_center, (setter)PyUIGrid_set_center, "Grid coordinate at the center of the Grid's view (pan)", NULL}, - - {"entities", (getter)PyUIGrid_get_children, NULL, "EntityCollection of entities on this grid", NULL}, - - {"x", (getter)PyUIGrid_get_float_member, (setter)PyUIGrid_set_float_member, "top-left corner X-coordinate", (void*)0}, - {"y", (getter)PyUIGrid_get_float_member, (setter)PyUIGrid_set_float_member, "top-left corner Y-coordinate", (void*)1}, - {"w", (getter)PyUIGrid_get_float_member, (setter)PyUIGrid_set_float_member, "visible widget width", (void*)2}, - {"h", (getter)PyUIGrid_get_float_member, (setter)PyUIGrid_set_float_member, "visible widget height", (void*)3}, - {"center_x", (getter)PyUIGrid_get_float_member, (setter)PyUIGrid_set_float_member, "center of the view X-coordinate", (void*)4}, - {"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 */ -}; - - -/* // TODO standard pointer would need deleted, but I opted for a shared pointer. tp_dealloc currently not even defined in the PyTypeObject -static void PyUIGrid_dealloc(PyUIGridObject* self) { - delete self->data; // Clean up the allocated UIGrid object - Py_TYPE(self)->tp_free((PyObject*)self); -} -*/ - - static PyTypeObject PyUIGridType = { - //PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "mcrfpy.Grid", - .tp_basicsize = sizeof(PyUIGridObject), - .tp_itemsize = 0, - //.tp_dealloc = (destructor)[](PyObject* self) - //{ - // PyUIGridObject* obj = (PyUIGridObject*)self; - // obj->data.reset(); - // Py_TYPE(self)->tp_free(self); - //}, - //TODO - PyUIGrid REPR def: - // .tp_repr = (reprfunc)PyUIGrid_repr, - //.tp_hash = NULL, - //.tp_iter - //.tp_iternext - .tp_flags = Py_TPFLAGS_DEFAULT, - .tp_doc = PyDoc_STR("docstring"), - .tp_methods = PyUIGrid_methods, - //.tp_members = PyUIGrid_members, - .tp_getset = PyUIGrid_getsetters, - //.tp_base = NULL, - .tp_init = (initproc)PyUIGrid_init, - .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* - { - PyUIGridObject* self = (PyUIGridObject*)type->tp_alloc(type, 0); - if (self) self->data = std::make_shared(); - return (PyObject*)self; - } - }; - - - /* - * - * end PyUIGrid defs - * - */ - +///* +//static PyObject* PyUIGrid_get_texture(PyUIGridObject* self, void* closure) { +// Py_INCREF(self->texture); +// return self->texture; +//} +//*/ +//static PyObject* PyUIGrid_get_texture(PyUIGridObject* self, void* closure) { +// //return self->data->getTexture()->pyObject(); +// PyTextureObject* obj = (PyTextureObject*)((&PyTextureType)->tp_alloc(&PyTextureType, 0)); +// obj->data = self->data->getTexture(); +// return (PyObject*)obj; +//} +// +//static PyObject* PyUIGrid_at(PyUIGridObject* self, PyObject* o) +//{ +// int x, y; +// if (!PyArg_ParseTuple(o, "ii", &x, &y)) { +// PyErr_SetString(PyExc_TypeError, "UIGrid.at requires two integer arguments: (x, y)"); +// return NULL; +// } +// if (x < 0 || x >= self->data->grid_x) { +// PyErr_SetString(PyExc_ValueError, "x value out of range (0, Grid.grid_y)"); +// return NULL; +// } +// if (y < 0 || y >= self->data->grid_y) { +// PyErr_SetString(PyExc_ValueError, "y value out of range (0, Grid.grid_y)"); +// return NULL; +// } +// +// PyUIGridPointObject* obj = (PyUIGridPointObject*)((&PyUIGridPointType)->tp_alloc(&PyUIGridPointType, 0)); +// //auto target = std::static_pointer_cast(target); +// obj->data = &(self->data->points[x + self->data->grid_x * y]); +// obj->grid = self->data; +// return (PyObject*)obj; +//} +// +//static PyMethodDef PyUIGrid_methods[] = { +// {"at", (PyCFunction)PyUIGrid_at, METH_O}, +// {NULL, NULL, 0, NULL} +//}; +// +//static PyObject* PyUIGrid_get_children(PyUIGridObject* self, void* closure); // forward declare +// +//static PyGetSetDef PyUIGrid_getsetters[] = { +// +// // TODO - refactor into get_vector_member with field identifier values `(void*)n` +// {"grid_size", (getter)PyUIGrid_get_grid_size, NULL, "Grid dimensions (grid_x, grid_y)", NULL}, +// {"position", (getter)PyUIGrid_get_position, (setter)PyUIGrid_set_position, "Position of the grid (x, y)", NULL}, +// {"size", (getter)PyUIGrid_get_size, (setter)PyUIGrid_set_size, "Size of the grid (width, height)", NULL}, +// {"center", (getter)PyUIGrid_get_center, (setter)PyUIGrid_set_center, "Grid coordinate at the center of the Grid's view (pan)", NULL}, +// +// {"entities", (getter)PyUIGrid_get_children, NULL, "EntityCollection of entities on this grid", NULL}, +// +// {"x", (getter)PyUIGrid_get_float_member, (setter)PyUIGrid_set_float_member, "top-left corner X-coordinate", (void*)0}, +// {"y", (getter)PyUIGrid_get_float_member, (setter)PyUIGrid_set_float_member, "top-left corner Y-coordinate", (void*)1}, +// {"w", (getter)PyUIGrid_get_float_member, (setter)PyUIGrid_set_float_member, "visible widget width", (void*)2}, +// {"h", (getter)PyUIGrid_get_float_member, (setter)PyUIGrid_set_float_member, "visible widget height", (void*)3}, +// {"center_x", (getter)PyUIGrid_get_float_member, (setter)PyUIGrid_set_float_member, "center of the view X-coordinate", (void*)4}, +// {"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 */ +//}; +// +// +///* // TODO standard pointer would need deleted, but I opted for a shared pointer. tp_dealloc currently not even defined in the PyTypeObject +//static void PyUIGrid_dealloc(PyUIGridObject* self) { +// delete self->data; // Clean up the allocated UIGrid object +// Py_TYPE(self)->tp_free((PyObject*)self); +//} +//*/ +// +// static PyTypeObject PyUIGridType = { +// //PyVarObject_HEAD_INIT(NULL, 0) +// .tp_name = "mcrfpy.Grid", +// .tp_basicsize = sizeof(PyUIGridObject), +// .tp_itemsize = 0, +// //.tp_dealloc = (destructor)[](PyObject* self) +// //{ +// // PyUIGridObject* obj = (PyUIGridObject*)self; +// // obj->data.reset(); +// // Py_TYPE(self)->tp_free(self); +// //}, +// //TODO - PyUIGrid REPR def: +// // .tp_repr = (reprfunc)PyUIGrid_repr, +// //.tp_hash = NULL, +// //.tp_iter +// //.tp_iternext +// .tp_flags = Py_TPFLAGS_DEFAULT, +// .tp_doc = PyDoc_STR("docstring"), +// .tp_methods = PyUIGrid_methods, +// //.tp_members = PyUIGrid_members, +// .tp_getset = PyUIGrid_getsetters, +// //.tp_base = NULL, +// .tp_init = (initproc)PyUIGrid_init, +// .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* +// { +// PyUIGridObject* self = (PyUIGridObject*)type->tp_alloc(type, 0); +// if (self) self->data = std::make_shared(); +// return (PyObject*)self; +// } +// }; +// +// +// /* +// * +// * end PyUIGrid defs +// * +// */ +// // PyUIEntity_init defined here because it depends on the PyUIGridType (to accept grid optional keyword argument) -static int PyUIEntity_init(PyUIEntityObject* self, PyObject* args, PyObject* kwds) { - static const char* keywords[] = { "x", "y", "texture", "sprite_index", "grid", nullptr }; - float x = 0.0f, y = 0.0f, scale = 1.0f; - int sprite_index = -1; - PyObject* texture = NULL; - PyObject* grid = NULL; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "ffOi|O", - const_cast(keywords), &x, &y, &texture, &sprite_index, &grid)) - { - return -1; - } - - // check types for texture - // - // Set Texture - // - if (texture != NULL && !PyObject_IsInstance(texture, (PyObject*)&PyTextureType)){ - PyErr_SetString(PyExc_TypeError, "texture must be a mcrfpy.Texture instance"); - return -1; - } /*else if (texture != NULL) // this section needs to go; texture isn't optional and isn't managed by the UI objects anymore - { - self->texture = texture; - Py_INCREF(texture); - } else - { - // default tex? - }*/ - - if (grid != NULL && !PyObject_IsInstance(grid, (PyObject*)&PyUIGridType)) { - PyErr_SetString(PyExc_TypeError, "grid must be a mcrfpy.Grid instance"); - return -1; - } - - auto pytexture = (PyTextureObject*)texture; - if (grid == NULL) - self->data = std::make_shared(); - else - self->data = std::make_shared(*((PyUIGridObject*)grid)->data); - - // TODO - PyTextureObjects and IndexTextures are a little bit of a mess with shared/unshared pointers - self->data->sprite = UISprite(pytexture->data, sprite_index, sf::Vector2f(0,0), 1.0); - self->data->position = sf::Vector2f(x, y); - if (grid != NULL) { - PyUIGridObject* pygrid = (PyUIGridObject*)grid; - self->data->grid = pygrid->data; - // todone - on creation of Entity with Grid assignment, also append it to the entity list - pygrid->data->entities->push_back(self->data); - } - return 0; -} - -/* - * - * Begin PyUIEntityCollectionIter defs - * - */ - typedef struct { - PyObject_HEAD - std::shared_ptr>> data; - int index; - int start_size; - } PyUIEntityCollectionIterObject; - - static int PyUIEntityCollectionIter_init(PyUIEntityCollectionIterObject* self, PyObject* args, PyObject* kwds) - { - PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required."); - return -1; - } - - static PyObject* PyUIEntityCollectionIter_next(PyUIEntityCollectionIterObject* self) - { - if (self->data->size() != self->start_size) - { - PyErr_SetString(PyExc_RuntimeError, "collection changed size during iteration"); - return NULL; - } - - if (self->index > self->start_size - 1) - { - PyErr_SetNone(PyExc_StopIteration); - return NULL; - } - self->index++; - auto vec = self->data.get(); - if (!vec) - { - PyErr_SetString(PyExc_RuntimeError, "the collection store returned a null pointer"); - return NULL; - } - // Advance list iterator since Entities are not stored in a vector (if this code even worked) - // vectors only: //auto target = (*vec)[self->index-1]; - //auto l_front = (*vec).begin(); - //std::advance(l_front, self->index-1); - - // TODO build PyObject* of the correct UIDrawable subclass to return - //return py_instance(target); - return NULL; - } - - static PyObject* PyUIEntityCollectionIter_repr(PyUIEntityCollectionIterObject* self) - { - std::ostringstream ss; - if (!self->data) ss << ""; - else { - ss << "data->size() << " child objects, @ index " << self->index << ")>"; - } - std::string repr_str = ss.str(); - return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); - } - static PyTypeObject PyUIEntityCollectionIterType = { - //PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "mcrfpy.UICollectionIter", - .tp_basicsize = sizeof(PyUIEntityCollectionIterObject), - .tp_itemsize = 0, - .tp_dealloc = (destructor)[](PyObject* self) - { - PyUIEntityCollectionIterObject* obj = (PyUIEntityCollectionIterObject*)self; - obj->data.reset(); - Py_TYPE(self)->tp_free(self); - }, - .tp_repr = (reprfunc)PyUIEntityCollectionIter_repr, - .tp_flags = Py_TPFLAGS_DEFAULT, - .tp_doc = PyDoc_STR("Iterator for a collection of UI objects"), - .tp_iternext = (iternextfunc)PyUIEntityCollectionIter_next, - //.tp_getset = PyUIEntityCollection_getset, - .tp_init = (initproc)PyUIEntityCollectionIter_init, // just raise an exception - .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* - { - PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required."); - return NULL; - } - }; - - /* - * - * End PyUIEntityCollectionIter defs - * - */ - -/* - * - * Begin PyUIEntityCollection defs - * - */ - typedef struct { - PyObject_HEAD - std::shared_ptr>> data; - std::shared_ptr grid; - } PyUIEntityCollectionObject; - - static Py_ssize_t PyUIEntityCollection_len(PyUIEntityCollectionObject* self) { - return self->data->size(); - } - - static PyObject* PyUIEntityCollection_getitem(PyUIEntityCollectionObject* self, Py_ssize_t index) { - // build a Python version of item at self->data[index] - // Copy pasted:: - auto vec = self->data.get(); - if (!vec) - { - PyErr_SetString(PyExc_RuntimeError, "the collection store returned a null pointer"); - return NULL; - } - while (index < 0) index += self->data->size(); - if (index > self->data->size() - 1) - { - PyErr_SetString(PyExc_IndexError, "EntityCollection index out of range"); - return NULL; - } - auto l_begin = (*vec).begin(); - std::advance(l_begin, index); - auto target = *l_begin; //auto target = (*vec)[index]; - //RET_PY_INSTANCE(target); - // construct and return an entity object that points directly into the UIGrid's entity vector - PyUIEntityObject* o = (PyUIEntityObject*)((&PyUIEntityType)->tp_alloc(&PyUIEntityType, 0)); - auto p = std::static_pointer_cast(target); - o->data = p; - return (PyObject*)o; - return NULL; - - - } - - static PySequenceMethods PyUIEntityCollection_sqmethods = { - .sq_length = (lenfunc)PyUIEntityCollection_len, - .sq_item = (ssizeargfunc)PyUIEntityCollection_getitem, - //.sq_item_by_index = PyUIEntityCollection_getitem - //.sq_slice - return a subset of the iterable - //.sq_ass_item - called when `o[x] = y` is executed (x is any object type) - //.sq_ass_slice - cool; no thanks, for now - //.sq_contains - called when `x in o` is executed - //.sq_ass_item_by_index - called when `o[x] = y` is executed (x is explictly an integer) - }; - - static PyObject* PyUIEntityCollection_append(PyUIEntityCollectionObject* self, PyObject* o) - { - // if not UIDrawable subclass, reject it - // self->data->push_back( c++ object inside o ); - - // this would be a great use case for .tp_base - if (!PyObject_IsInstance(o, (PyObject*)&PyUIEntityType)) - { - PyErr_SetString(PyExc_TypeError, "Only Entity objects can be added to EntityCollection"); - return NULL; - } - PyUIEntityObject* entity = (PyUIEntityObject*)o; - self->data->push_back(entity->data); - entity->data->grid = self->grid; - - Py_INCREF(Py_None); - return Py_None; - } - static PyObject* PyUIEntityCollection_remove(PyUIEntityCollectionObject* self, PyObject* o) - { - if (!PyLong_Check(o)) - { - PyErr_SetString(PyExc_TypeError, "UICollection.remove requires an integer index to remove"); - return NULL; - } - long index = PyLong_AsLong(o); - if (index >= self->data->size()) - { - PyErr_SetString(PyExc_ValueError, "Index out of range"); - return NULL; - } - else if (index < 0) - { - PyErr_SetString(PyExc_NotImplementedError, "reverse indexing is not implemented."); - return NULL; - } - - // release the shared pointer at correct part of the list - self->data->erase(std::next(self->data->begin(), index)); - Py_INCREF(Py_None); - return Py_None; - } - - static PyMethodDef PyUIEntityCollection_methods[] = { - {"append", (PyCFunction)PyUIEntityCollection_append, METH_O}, - //{"extend", (PyCFunction)PyUIEntityCollection_extend, METH_O}, // TODO - {"remove", (PyCFunction)PyUIEntityCollection_remove, METH_O}, - {NULL, NULL, 0, NULL} - }; - - static PyObject* PyUIEntityCollection_repr(PyUIEntityCollectionObject* self) - { - std::ostringstream ss; - if (!self->data) ss << ""; - else { - ss << "data->size() << " child objects)>"; - } - std::string repr_str = ss.str(); - return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); - } - - static int PyUIEntityCollection_init(PyUIEntityCollectionObject* self, PyObject* args, PyObject* kwds) - { - PyErr_SetString(PyExc_TypeError, "EntityCollection cannot be instantiated: a C++ data source is required."); - return -1; - } - - static PyObject* PyUIEntityCollection_iter(PyUIEntityCollectionObject* self) - { - PyUIEntityCollectionIterObject* iterObj; - iterObj = (PyUIEntityCollectionIterObject*)PyUIEntityCollectionIterType.tp_alloc(&PyUIEntityCollectionIterType, 0); - if (iterObj == NULL) { - return NULL; // Failed to allocate memory for the iterator object - } - - iterObj->data = self->data; - iterObj->index = 0; - iterObj->start_size = self->data->size(); - - return (PyObject*)iterObj; - } - static PyTypeObject PyUIEntityCollectionType = { - //PyVarObject_/HEAD_INIT(NULL, 0) - .tp_name = "mcrfpy.EntityCollection", - .tp_basicsize = sizeof(PyUIEntityCollectionObject), - .tp_itemsize = 0, - .tp_dealloc = (destructor)[](PyObject* self) - { - PyUIEntityCollectionObject* obj = (PyUIEntityCollectionObject*)self; - obj->data.reset(); - Py_TYPE(self)->tp_free(self); - }, - .tp_repr = (reprfunc)PyUIEntityCollection_repr, - .tp_as_sequence = &PyUIEntityCollection_sqmethods, - .tp_flags = Py_TPFLAGS_DEFAULT, - .tp_doc = PyDoc_STR("Iterable, indexable collection of Entities"), - .tp_iter = (getiterfunc)PyUIEntityCollection_iter, - .tp_methods = PyUIEntityCollection_methods, // append, remove - //.tp_getset = PyUIEntityCollection_getset, - .tp_init = (initproc)PyUIEntityCollection_init, // just raise an exception - .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* - { - // Does PyUIEntityCollectionType need __new__ if it's not supposed to be instantiable by the user? - // Should I just raise an exception? Or is the uninitialized shared_ptr enough of a blocker? - PyErr_SetString(PyExc_TypeError, "EntityCollection cannot be instantiated: a C++ data source is required."); - return NULL; - } - }; - - // Grid's get_children def must follow the EntityCollection def - static PyObject* PyUIGrid_get_children(PyUIGridObject* self, void* closure) - { - // create PyUICollection instance pointing to self->data->children - PyUIEntityCollectionObject* o = (PyUIEntityCollectionObject*)PyUIEntityCollectionType.tp_alloc(&PyUIEntityCollectionType, 0); - if (o) { - o->data = self->data->entities; // todone. / BUGFIX - entities isn't a shared pointer on UIGrid, what to do? -- I made it a sp>> - o->grid = self->data; - } - return (PyObject*)o; - } - - /* - * - * End PyUIEntityCollection defs - * - */ - - - - /* - * - * Begin PyUICollectionIter defs - * - */ - typedef struct { - PyObject_HEAD - std::shared_ptr>> data; - int index; - int start_size; - } PyUICollectionIterObject; - - static int PyUICollectionIter_init(PyUICollectionIterObject* self, PyObject* args, PyObject* kwds) - { - PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required."); - return -1; - } - - static PyObject* PyUICollectionIter_next(PyUICollectionIterObject* self) - { - if (self->data->size() != self->start_size) - { - PyErr_SetString(PyExc_RuntimeError, "collection changed size during iteration"); - return NULL; - } - - if (self->index > self->start_size - 1) - { - PyErr_SetNone(PyExc_StopIteration); - return NULL; - } - self->index++; - auto vec = self->data.get(); - if (!vec) - { - PyErr_SetString(PyExc_RuntimeError, "the collection store returned a null pointer"); - return NULL; - } - auto target = (*vec)[self->index-1]; - // TODO build PyObject* of the correct UIDrawable subclass to return - //return py_instance(target); - return NULL; - } - - static PyObject* PyUICollectionIter_repr(PyUICollectionIterObject* self) - { - std::ostringstream ss; - if (!self->data) ss << ""; - else { - ss << "data->size() << " child objects, @ index " << self->index << ")>"; - } - std::string repr_str = ss.str(); - return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); - } - - static PyTypeObject PyUICollectionIterType = { - //PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "mcrfpy.UICollectionIter", - .tp_basicsize = sizeof(PyUICollectionIterObject), - .tp_itemsize = 0, - .tp_dealloc = (destructor)[](PyObject* self) - { - PyUICollectionIterObject* obj = (PyUICollectionIterObject*)self; - obj->data.reset(); - Py_TYPE(self)->tp_free(self); - }, - .tp_repr = (reprfunc)PyUICollectionIter_repr, - .tp_flags = Py_TPFLAGS_DEFAULT, - .tp_doc = PyDoc_STR("Iterator for a collection of UI objects"), - .tp_iternext = (iternextfunc)PyUICollectionIter_next, - //.tp_getset = PyUICollection_getset, - .tp_init = (initproc)PyUICollectionIter_init, // just raise an exception - .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* - { - PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required."); - return NULL; - } - }; - - /* - * - * End PyUICollectionIter defs - * - */ - - - /* - * - * Begin PyUICollection defs - * - */ - - static Py_ssize_t PyUICollection_len(PyUICollectionObject* self) { - return self->data->size(); - } - - static PyObject* PyUICollection_getitem(PyUICollectionObject* self, Py_ssize_t index) { - // build a Python version of item at self->data[index] - // Copy pasted:: - auto vec = self->data.get(); - if (!vec) - { - PyErr_SetString(PyExc_RuntimeError, "the collection store returned a null pointer"); - return NULL; - } - while (index < 0) index += self->data->size(); - if (index > self->data->size() - 1) - { - PyErr_SetString(PyExc_IndexError, "UICollection index out of range"); - return NULL; - } - auto target = (*vec)[index]; - RET_PY_INSTANCE(target); - return NULL; - - - } - - static PySequenceMethods PyUICollection_sqmethods = { - .sq_length = (lenfunc)PyUICollection_len, - .sq_item = (ssizeargfunc)PyUICollection_getitem, - //.sq_item_by_index = PyUICollection_getitem - //.sq_slice - return a subset of the iterable - //.sq_ass_item - called when `o[x] = y` is executed (x is any object type) - //.sq_ass_slice - cool; no thanks, for now - //.sq_contains - called when `x in o` is executed - //.sq_ass_item_by_index - called when `o[x] = y` is executed (x is explictly an integer) - }; - - static PyObject* PyUICollection_append(PyUICollectionObject* self, PyObject* o) - { - // if not UIDrawable subclass, reject it - // self->data->push_back( c++ object inside o ); - - // this would be a great use case for .tp_base - if (!PyObject_IsInstance(o, (PyObject*)&PyUIFrameType) && - !PyObject_IsInstance(o, (PyObject*)&PyUISpriteType) && - !PyObject_IsInstance(o, (PyObject*)&PyUICaptionType) && - !PyObject_IsInstance(o, (PyObject*)&PyUIGridType) - ) - { - PyErr_SetString(PyExc_TypeError, "Only Frame, Caption, Sprite, and Grid objects can be added to UICollection"); - return NULL; - } - - if (PyObject_IsInstance(o, (PyObject*)&PyUIFrameType)) - { - PyUIFrameObject* frame = (PyUIFrameObject*)o; - self->data->push_back(frame->data); - } - if (PyObject_IsInstance(o, (PyObject*)&PyUICaptionType)) - { - PyUICaptionObject* caption = (PyUICaptionObject*)o; - self->data->push_back(caption->data); - } - if (PyObject_IsInstance(o, (PyObject*)&PyUISpriteType)) - { - PyUISpriteObject* sprite = (PyUISpriteObject*)o; - self->data->push_back(sprite->data); - } - if (PyObject_IsInstance(o, (PyObject*)&PyUIGridType)) - { - PyUIGridObject* grid = (PyUIGridObject*)o; - self->data->push_back(grid->data); - } - - Py_INCREF(Py_None); - return Py_None; - } - - static PyObject* PyUICollection_remove(PyUICollectionObject* self, PyObject* o) - { - if (!PyLong_Check(o)) - { - PyErr_SetString(PyExc_TypeError, "UICollection.remove requires an integer index to remove"); - return NULL; - } - long index = PyLong_AsLong(o); - if (index >= self->data->size()) - { - PyErr_SetString(PyExc_ValueError, "Index out of range"); - return NULL; - } - else if (index < 0) - { - PyErr_SetString(PyExc_NotImplementedError, "reverse indexing is not implemented."); - return NULL; - } - - // release the shared pointer at self->data[index]; - self->data->erase(self->data->begin() + index); - Py_INCREF(Py_None); - return Py_None; - } - - static PyMethodDef PyUICollection_methods[] = { - {"append", (PyCFunction)PyUICollection_append, METH_O}, - //{"extend", (PyCFunction)PyUICollection_extend, METH_O}, // TODO - {"remove", (PyCFunction)PyUICollection_remove, METH_O}, - {NULL, NULL, 0, NULL} - }; - - static PyObject* PyUICollection_repr(PyUICollectionObject* self) - { - std::ostringstream ss; - if (!self->data) ss << ""; - else { - ss << "data->size() << " child objects)>"; - } - std::string repr_str = ss.str(); - return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); - } - - static int PyUICollection_init(PyUICollectionObject* self, PyObject* args, PyObject* kwds) - { - PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required."); - return -1; - } - - static PyObject* PyUICollection_iter(PyUICollectionObject* self) - { - PyUICollectionIterObject* iterObj; - iterObj = (PyUICollectionIterObject*)PyUICollectionIterType.tp_alloc(&PyUICollectionIterType, 0); - if (iterObj == NULL) { - return NULL; // Failed to allocate memory for the iterator object - } - - iterObj->data = self->data; - iterObj->index = 0; - iterObj->start_size = self->data->size(); - - return (PyObject*)iterObj; - } - - /* - static PyGetSetDef PyUICollection_getsetters[] = { - {NULL} - }; - */ - - static PyTypeObject PyUICollectionType = { - //PyVarObject_/HEAD_INIT(NULL, 0) - .tp_name = "mcrfpy.UICollection", - .tp_basicsize = sizeof(PyUICollectionObject), - .tp_itemsize = 0, - .tp_dealloc = (destructor)[](PyObject* self) - { - PyUICollectionObject* obj = (PyUICollectionObject*)self; - obj->data.reset(); - Py_TYPE(self)->tp_free(self); - }, - .tp_repr = (reprfunc)PyUICollection_repr, - .tp_as_sequence = &PyUICollection_sqmethods, - .tp_flags = Py_TPFLAGS_DEFAULT, - .tp_doc = PyDoc_STR("Iterable, indexable collection of UI objects"), - .tp_iter = (getiterfunc)PyUICollection_iter, - .tp_methods = PyUICollection_methods, // append, remove - //.tp_getset = PyUICollection_getset, - .tp_init = (initproc)PyUICollection_init, // just raise an exception - .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* - { - // Does PyUICollectionType need __new__ if it's not supposed to be instantiable by the user? - // Should I just raise an exception? Or is the uninitialized shared_ptr enough of a blocker? - PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required."); - return NULL; - } - }; - /* - * - * End PyUICollection defs - * - */ - - static PyObject* PyUIFrame_get_children(PyUIFrameObject* self, void* closure) - { - // create PyUICollection instance pointing to self->data->children - PyUICollectionObject* o = (PyUICollectionObject*)PyUICollectionType.tp_alloc(&PyUICollectionType, 0); - if (o) - o->data = self->data->children; - return (PyObject*)o; - } - - -} // namespace mcrfpydef +//static int PyUIEntity_init(PyUIEntityObject* self, PyObject* args, PyObject* kwds) { +// static const char* keywords[] = { "x", "y", "texture", "sprite_index", "grid", nullptr }; +// float x = 0.0f, y = 0.0f, scale = 1.0f; +// int sprite_index = -1; +// PyObject* texture = NULL; +// PyObject* grid = NULL; +// +// if (!PyArg_ParseTupleAndKeywords(args, kwds, "ffOi|O", +// const_cast(keywords), &x, &y, &texture, &sprite_index, &grid)) +// { +// return -1; +// } +// +// // check types for texture +// // +// // Set Texture +// // +// if (texture != NULL && !PyObject_IsInstance(texture, (PyObject*)&PyTextureType)){ +// PyErr_SetString(PyExc_TypeError, "texture must be a mcrfpy.Texture instance"); +// return -1; +// } /*else if (texture != NULL) // this section needs to go; texture isn't optional and isn't managed by the UI objects anymore +// { +// self->texture = texture; +// Py_INCREF(texture); +// } else +// { +// // default tex? +// }*/ +// +// if (grid != NULL && !PyObject_IsInstance(grid, (PyObject*)&PyUIGridType)) { +// PyErr_SetString(PyExc_TypeError, "grid must be a mcrfpy.Grid instance"); +// return -1; +// } +// +// auto pytexture = (PyTextureObject*)texture; +// if (grid == NULL) +// self->data = std::make_shared(); +// else +// self->data = std::make_shared(*((PyUIGridObject*)grid)->data); +// +// // TODO - PyTextureObjects and IndexTextures are a little bit of a mess with shared/unshared pointers +// self->data->sprite = UISprite(pytexture->data, sprite_index, sf::Vector2f(0,0), 1.0); +// self->data->position = sf::Vector2f(x, y); +// if (grid != NULL) { +// PyUIGridObject* pygrid = (PyUIGridObject*)grid; +// self->data->grid = pygrid->data; +// // todone - on creation of Entity with Grid assignment, also append it to the entity list +// pygrid->data->entities->push_back(self->data); +// } +// return 0; +//} +// +///* +// * +// * Begin PyUIEntityCollectionIter defs +// * +// */ +// typedef struct { +// PyObject_HEAD +// std::shared_ptr>> data; +// int index; +// int start_size; +// } PyUIEntityCollectionIterObject; +// +// static int PyUIEntityCollectionIter_init(PyUIEntityCollectionIterObject* self, PyObject* args, PyObject* kwds) +// { +// PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required."); +// return -1; +// } +// +// static PyObject* PyUIEntityCollectionIter_next(PyUIEntityCollectionIterObject* self) +// { +// if (self->data->size() != self->start_size) +// { +// PyErr_SetString(PyExc_RuntimeError, "collection changed size during iteration"); +// return NULL; +// } +// +// if (self->index > self->start_size - 1) +// { +// PyErr_SetNone(PyExc_StopIteration); +// return NULL; +// } +// self->index++; +// auto vec = self->data.get(); +// if (!vec) +// { +// PyErr_SetString(PyExc_RuntimeError, "the collection store returned a null pointer"); +// return NULL; +// } +// // Advance list iterator since Entities are not stored in a vector (if this code even worked) +// // vectors only: //auto target = (*vec)[self->index-1]; +// //auto l_front = (*vec).begin(); +// //std::advance(l_front, self->index-1); +// +// // TODO build PyObject* of the correct UIDrawable subclass to return +// //return py_instance(target); +// return NULL; +// } +// +// static PyObject* PyUIEntityCollectionIter_repr(PyUIEntityCollectionIterObject* self) +// { +// std::ostringstream ss; +// if (!self->data) ss << ""; +// else { +// ss << "data->size() << " child objects, @ index " << self->index << ")>"; +// } +// std::string repr_str = ss.str(); +// return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); +// } +// static PyTypeObject PyUIEntityCollectionIterType = { +// //PyVarObject_HEAD_INIT(NULL, 0) +// .tp_name = "mcrfpy.UICollectionIter", +// .tp_basicsize = sizeof(PyUIEntityCollectionIterObject), +// .tp_itemsize = 0, +// .tp_dealloc = (destructor)[](PyObject* self) +// { +// PyUIEntityCollectionIterObject* obj = (PyUIEntityCollectionIterObject*)self; +// obj->data.reset(); +// Py_TYPE(self)->tp_free(self); +// }, +// .tp_repr = (reprfunc)PyUIEntityCollectionIter_repr, +// .tp_flags = Py_TPFLAGS_DEFAULT, +// .tp_doc = PyDoc_STR("Iterator for a collection of UI objects"), +// .tp_iternext = (iternextfunc)PyUIEntityCollectionIter_next, +// //.tp_getset = PyUIEntityCollection_getset, +// .tp_init = (initproc)PyUIEntityCollectionIter_init, // just raise an exception +// .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* +// { +// PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required."); +// return NULL; +// } +// }; +// +// /* +// * +// * End PyUIEntityCollectionIter defs +// * +// */ +// +///* +// * +// * Begin PyUIEntityCollection defs +// * +// */ +// typedef struct { +// PyObject_HEAD +// std::shared_ptr>> data; +// std::shared_ptr grid; +// } PyUIEntityCollectionObject; +// +// static Py_ssize_t PyUIEntityCollection_len(PyUIEntityCollectionObject* self) { +// return self->data->size(); +// } +// +// static PyObject* PyUIEntityCollection_getitem(PyUIEntityCollectionObject* self, Py_ssize_t index) { +// // build a Python version of item at self->data[index] +// // Copy pasted:: +// auto vec = self->data.get(); +// if (!vec) +// { +// PyErr_SetString(PyExc_RuntimeError, "the collection store returned a null pointer"); +// return NULL; +// } +// while (index < 0) index += self->data->size(); +// if (index > self->data->size() - 1) +// { +// PyErr_SetString(PyExc_IndexError, "EntityCollection index out of range"); +// return NULL; +// } +// auto l_begin = (*vec).begin(); +// std::advance(l_begin, index); +// auto target = *l_begin; //auto target = (*vec)[index]; +// //RET_PY_INSTANCE(target); +// // construct and return an entity object that points directly into the UIGrid's entity vector +// PyUIEntityObject* o = (PyUIEntityObject*)((&PyUIEntityType)->tp_alloc(&PyUIEntityType, 0)); +// auto p = std::static_pointer_cast(target); +// o->data = p; +// return (PyObject*)o; +// return NULL; +// +// +// } +// +// static PySequenceMethods PyUIEntityCollection_sqmethods = { +// .sq_length = (lenfunc)PyUIEntityCollection_len, +// .sq_item = (ssizeargfunc)PyUIEntityCollection_getitem, +// //.sq_item_by_index = PyUIEntityCollection_getitem +// //.sq_slice - return a subset of the iterable +// //.sq_ass_item - called when `o[x] = y` is executed (x is any object type) +// //.sq_ass_slice - cool; no thanks, for now +// //.sq_contains - called when `x in o` is executed +// //.sq_ass_item_by_index - called when `o[x] = y` is executed (x is explictly an integer) +// }; +// +// static PyObject* PyUIEntityCollection_append(PyUIEntityCollectionObject* self, PyObject* o) +// { +// // if not UIDrawable subclass, reject it +// // self->data->push_back( c++ object inside o ); +// +// // this would be a great use case for .tp_base +// if (!PyObject_IsInstance(o, (PyObject*)&PyUIEntityType)) +// { +// PyErr_SetString(PyExc_TypeError, "Only Entity objects can be added to EntityCollection"); +// return NULL; +// } +// PyUIEntityObject* entity = (PyUIEntityObject*)o; +// self->data->push_back(entity->data); +// entity->data->grid = self->grid; +// +// Py_INCREF(Py_None); +// return Py_None; +// } +// static PyObject* PyUIEntityCollection_remove(PyUIEntityCollectionObject* self, PyObject* o) +// { +// if (!PyLong_Check(o)) +// { +// PyErr_SetString(PyExc_TypeError, "UICollection.remove requires an integer index to remove"); +// return NULL; +// } +// long index = PyLong_AsLong(o); +// if (index >= self->data->size()) +// { +// PyErr_SetString(PyExc_ValueError, "Index out of range"); +// return NULL; +// } +// else if (index < 0) +// { +// PyErr_SetString(PyExc_NotImplementedError, "reverse indexing is not implemented."); +// return NULL; +// } +// +// // release the shared pointer at correct part of the list +// self->data->erase(std::next(self->data->begin(), index)); +// Py_INCREF(Py_None); +// return Py_None; +// } +// +// static PyMethodDef PyUIEntityCollection_methods[] = { +// {"append", (PyCFunction)PyUIEntityCollection_append, METH_O}, +// //{"extend", (PyCFunction)PyUIEntityCollection_extend, METH_O}, // TODO +// {"remove", (PyCFunction)PyUIEntityCollection_remove, METH_O}, +// {NULL, NULL, 0, NULL} +// }; +// +// static PyObject* PyUIEntityCollection_repr(PyUIEntityCollectionObject* self) +// { +// std::ostringstream ss; +// if (!self->data) ss << ""; +// else { +// ss << "data->size() << " child objects)>"; +// } +// std::string repr_str = ss.str(); +// return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); +// } +// +// static int PyUIEntityCollection_init(PyUIEntityCollectionObject* self, PyObject* args, PyObject* kwds) +// { +// PyErr_SetString(PyExc_TypeError, "EntityCollection cannot be instantiated: a C++ data source is required."); +// return -1; +// } +// +// static PyObject* PyUIEntityCollection_iter(PyUIEntityCollectionObject* self) +// { +// PyUIEntityCollectionIterObject* iterObj; +// iterObj = (PyUIEntityCollectionIterObject*)PyUIEntityCollectionIterType.tp_alloc(&PyUIEntityCollectionIterType, 0); +// if (iterObj == NULL) { +// return NULL; // Failed to allocate memory for the iterator object +// } +// +// iterObj->data = self->data; +// iterObj->index = 0; +// iterObj->start_size = self->data->size(); +// +// return (PyObject*)iterObj; +// } +// static PyTypeObject PyUIEntityCollectionType = { +// //PyVarObject_/HEAD_INIT(NULL, 0) +// .tp_name = "mcrfpy.EntityCollection", +// .tp_basicsize = sizeof(PyUIEntityCollectionObject), +// .tp_itemsize = 0, +// .tp_dealloc = (destructor)[](PyObject* self) +// { +// PyUIEntityCollectionObject* obj = (PyUIEntityCollectionObject*)self; +// obj->data.reset(); +// Py_TYPE(self)->tp_free(self); +// }, +// .tp_repr = (reprfunc)PyUIEntityCollection_repr, +// .tp_as_sequence = &PyUIEntityCollection_sqmethods, +// .tp_flags = Py_TPFLAGS_DEFAULT, +// .tp_doc = PyDoc_STR("Iterable, indexable collection of Entities"), +// .tp_iter = (getiterfunc)PyUIEntityCollection_iter, +// .tp_methods = PyUIEntityCollection_methods, // append, remove +// //.tp_getset = PyUIEntityCollection_getset, +// .tp_init = (initproc)PyUIEntityCollection_init, // just raise an exception +// .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* +// { +// // Does PyUIEntityCollectionType need __new__ if it's not supposed to be instantiable by the user? +// // Should I just raise an exception? Or is the uninitialized shared_ptr enough of a blocker? +// PyErr_SetString(PyExc_TypeError, "EntityCollection cannot be instantiated: a C++ data source is required."); +// return NULL; +// } +// }; +// +// // Grid's get_children def must follow the EntityCollection def +// static PyObject* PyUIGrid_get_children(PyUIGridObject* self, void* closure) +// { +// // create PyUICollection instance pointing to self->data->children +// PyUIEntityCollectionObject* o = (PyUIEntityCollectionObject*)PyUIEntityCollectionType.tp_alloc(&PyUIEntityCollectionType, 0); +// if (o) { +// o->data = self->data->entities; // todone. / BUGFIX - entities isn't a shared pointer on UIGrid, what to do? -- I made it a sp>> +// o->grid = self->data; +// } +// return (PyObject*)o; +// } +// +// /* +// * +// * End PyUIEntityCollection defs +// * +// */ +// +// +// +// /* +// * +// * Begin PyUICollectionIter defs +// * +// */ +// typedef struct { +// PyObject_HEAD +// std::shared_ptr>> data; +// int index; +// int start_size; +// } PyUICollectionIterObject; +// +// static int PyUICollectionIter_init(PyUICollectionIterObject* self, PyObject* args, PyObject* kwds) +// { +// PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required."); +// return -1; +// } +// +// static PyObject* PyUICollectionIter_next(PyUICollectionIterObject* self) +// { +// if (self->data->size() != self->start_size) +// { +// PyErr_SetString(PyExc_RuntimeError, "collection changed size during iteration"); +// return NULL; +// } +// +// if (self->index > self->start_size - 1) +// { +// PyErr_SetNone(PyExc_StopIteration); +// return NULL; +// } +// self->index++; +// auto vec = self->data.get(); +// if (!vec) +// { +// PyErr_SetString(PyExc_RuntimeError, "the collection store returned a null pointer"); +// return NULL; +// } +// auto target = (*vec)[self->index-1]; +// // TODO build PyObject* of the correct UIDrawable subclass to return +// //return py_instance(target); +// return NULL; +// } +// +// static PyObject* PyUICollectionIter_repr(PyUICollectionIterObject* self) +// { +// std::ostringstream ss; +// if (!self->data) ss << ""; +// else { +// ss << "data->size() << " child objects, @ index " << self->index << ")>"; +// } +// std::string repr_str = ss.str(); +// return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); +// } +// +// static PyTypeObject PyUICollectionIterType = { +// //PyVarObject_HEAD_INIT(NULL, 0) +// .tp_name = "mcrfpy.UICollectionIter", +// .tp_basicsize = sizeof(PyUICollectionIterObject), +// .tp_itemsize = 0, +// .tp_dealloc = (destructor)[](PyObject* self) +// { +// PyUICollectionIterObject* obj = (PyUICollectionIterObject*)self; +// obj->data.reset(); +// Py_TYPE(self)->tp_free(self); +// }, +// .tp_repr = (reprfunc)PyUICollectionIter_repr, +// .tp_flags = Py_TPFLAGS_DEFAULT, +// .tp_doc = PyDoc_STR("Iterator for a collection of UI objects"), +// .tp_iternext = (iternextfunc)PyUICollectionIter_next, +// //.tp_getset = PyUICollection_getset, +// .tp_init = (initproc)PyUICollectionIter_init, // just raise an exception +// .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* +// { +// PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required."); +// return NULL; +// } +// }; +// +// /* +// * +// * End PyUICollectionIter defs +// * +// */ +// +// +// /* +// * +// * Begin PyUICollection defs +// * +// */ +// +// static Py_ssize_t PyUICollection_len(PyUICollectionObject* self) { +// return self->data->size(); +// } +// +// static PyObject* PyUICollection_getitem(PyUICollectionObject* self, Py_ssize_t index) { +// // build a Python version of item at self->data[index] +// // Copy pasted:: +// auto vec = self->data.get(); +// if (!vec) +// { +// PyErr_SetString(PyExc_RuntimeError, "the collection store returned a null pointer"); +// return NULL; +// } +// while (index < 0) index += self->data->size(); +// if (index > self->data->size() - 1) +// { +// PyErr_SetString(PyExc_IndexError, "UICollection index out of range"); +// return NULL; +// } +// auto target = (*vec)[index]; +// RET_PY_INSTANCE(target); +// return NULL; +// +// +// } +// +// static PySequenceMethods PyUICollection_sqmethods = { +// .sq_length = (lenfunc)PyUICollection_len, +// .sq_item = (ssizeargfunc)PyUICollection_getitem, +// //.sq_item_by_index = PyUICollection_getitem +// //.sq_slice - return a subset of the iterable +// //.sq_ass_item - called when `o[x] = y` is executed (x is any object type) +// //.sq_ass_slice - cool; no thanks, for now +// //.sq_contains - called when `x in o` is executed +// //.sq_ass_item_by_index - called when `o[x] = y` is executed (x is explictly an integer) +// }; +// +// static PyObject* PyUICollection_append(PyUICollectionObject* self, PyObject* o) +// { +// // if not UIDrawable subclass, reject it +// // self->data->push_back( c++ object inside o ); +// +// // this would be a great use case for .tp_base +// if (!PyObject_IsInstance(o, (PyObject*)&PyUIFrameType) && +// !PyObject_IsInstance(o, (PyObject*)&PyUISpriteType) && +// !PyObject_IsInstance(o, (PyObject*)&PyUICaptionType) && +// !PyObject_IsInstance(o, (PyObject*)&PyUIGridType) +// ) +// { +// PyErr_SetString(PyExc_TypeError, "Only Frame, Caption, Sprite, and Grid objects can be added to UICollection"); +// return NULL; +// } +// +// if (PyObject_IsInstance(o, (PyObject*)&PyUIFrameType)) +// { +// PyUIFrameObject* frame = (PyUIFrameObject*)o; +// self->data->push_back(frame->data); +// } +// if (PyObject_IsInstance(o, (PyObject*)&PyUICaptionType)) +// { +// PyUICaptionObject* caption = (PyUICaptionObject*)o; +// self->data->push_back(caption->data); +// } +// if (PyObject_IsInstance(o, (PyObject*)&PyUISpriteType)) +// { +// PyUISpriteObject* sprite = (PyUISpriteObject*)o; +// self->data->push_back(sprite->data); +// } +// if (PyObject_IsInstance(o, (PyObject*)&PyUIGridType)) +// { +// PyUIGridObject* grid = (PyUIGridObject*)o; +// self->data->push_back(grid->data); +// } +// +// Py_INCREF(Py_None); +// return Py_None; +// } +// +// static PyObject* PyUICollection_remove(PyUICollectionObject* self, PyObject* o) +// { +// if (!PyLong_Check(o)) +// { +// PyErr_SetString(PyExc_TypeError, "UICollection.remove requires an integer index to remove"); +// return NULL; +// } +// long index = PyLong_AsLong(o); +// if (index >= self->data->size()) +// { +// PyErr_SetString(PyExc_ValueError, "Index out of range"); +// return NULL; +// } +// else if (index < 0) +// { +// PyErr_SetString(PyExc_NotImplementedError, "reverse indexing is not implemented."); +// return NULL; +// } +// +// // release the shared pointer at self->data[index]; +// self->data->erase(self->data->begin() + index); +// Py_INCREF(Py_None); +// return Py_None; +// } +// +// static PyMethodDef PyUICollection_methods[] = { +// {"append", (PyCFunction)PyUICollection_append, METH_O}, +// //{"extend", (PyCFunction)PyUICollection_extend, METH_O}, // TODO +// {"remove", (PyCFunction)PyUICollection_remove, METH_O}, +// {NULL, NULL, 0, NULL} +// }; +// +// static PyObject* PyUICollection_repr(PyUICollectionObject* self) +// { +// std::ostringstream ss; +// if (!self->data) ss << ""; +// else { +// ss << "data->size() << " child objects)>"; +// } +// std::string repr_str = ss.str(); +// return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); +// } +// +// static int PyUICollection_init(PyUICollectionObject* self, PyObject* args, PyObject* kwds) +// { +// PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required."); +// return -1; +// } +// +// static PyObject* PyUICollection_iter(PyUICollectionObject* self) +// { +// PyUICollectionIterObject* iterObj; +// iterObj = (PyUICollectionIterObject*)PyUICollectionIterType.tp_alloc(&PyUICollectionIterType, 0); +// if (iterObj == NULL) { +// return NULL; // Failed to allocate memory for the iterator object +// } +// +// iterObj->data = self->data; +// iterObj->index = 0; +// iterObj->start_size = self->data->size(); +// +// return (PyObject*)iterObj; +// } +// +// /* +// static PyGetSetDef PyUICollection_getsetters[] = { +// {NULL} +// }; +// */ +// +// static PyTypeObject PyUICollectionType = { +// //PyVarObject_/HEAD_INIT(NULL, 0) +// .tp_name = "mcrfpy.UICollection", +// .tp_basicsize = sizeof(PyUICollectionObject), +// .tp_itemsize = 0, +// .tp_dealloc = (destructor)[](PyObject* self) +// { +// PyUICollectionObject* obj = (PyUICollectionObject*)self; +// obj->data.reset(); +// Py_TYPE(self)->tp_free(self); +// }, +// .tp_repr = (reprfunc)PyUICollection_repr, +// .tp_as_sequence = &PyUICollection_sqmethods, +// .tp_flags = Py_TPFLAGS_DEFAULT, +// .tp_doc = PyDoc_STR("Iterable, indexable collection of UI objects"), +// .tp_iter = (getiterfunc)PyUICollection_iter, +// .tp_methods = PyUICollection_methods, // append, remove +// //.tp_getset = PyUICollection_getset, +// .tp_init = (initproc)PyUICollection_init, // just raise an exception +// .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* +// { +// // Does PyUICollectionType need __new__ if it's not supposed to be instantiable by the user? +// // Should I just raise an exception? Or is the uninitialized shared_ptr enough of a blocker? +// PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required."); +// return NULL; +// } +// }; +// /* +// * +// * End PyUICollection defs +// * +// */ +// +// static PyObject* PyUIFrame_get_children(PyUIFrameObject* self, void* closure) +// { +// // create PyUICollection instance pointing to self->data->children +// PyUICollectionObject* o = (PyUICollectionObject*)PyUICollectionType.tp_alloc(&PyUICollectionType, 0); +// if (o) +// o->data = self->data->children; +// return (PyObject*)o; +// } +// +// +//} // namespace mcrfpydef diff --git a/src/UICaption.cpp b/src/UICaption.cpp new file mode 100644 index 0000000..aa53a8a --- /dev/null +++ b/src/UICaption.cpp @@ -0,0 +1,22 @@ +#include "UICaption.h" + +UIDrawable* UICaption::click_at(sf::Vector2f point) +{ + if (click_callable) + { + if (text.getGlobalBounds().contains(point)) return this; + } + return NULL; +} + +void UICaption::render(sf::Vector2f offset) +{ + text.move(offset); + Resources::game->getWindow().draw(text); + text.move(-offset); +} + +PyObjectsEnum UICaption::derived_type() +{ + return PyObjectsEnum::UICAPTION; +} diff --git a/src/UICaption.h b/src/UICaption.h new file mode 100644 index 0000000..ee37e1c --- /dev/null +++ b/src/UICaption.h @@ -0,0 +1,304 @@ +#pragma once +#include "Common.h" +#include "Python.h" +#include "structmember.h" +#include "IndexTexture.h" +#include "Resources.h" +#include + +#include "PyCallable.h" +#include "PyTexture.h" +#include "PyColor.h" +#include "PyVector.h" +#include "PyFont.h" +#include "UIDrawable.h" + +class UICaption: public UIDrawable +{ +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; +}; + +typedef struct { + PyObject_HEAD + std::shared_ptr data; + PyObject* font; +} PyUICaptionObject; + +namespace mcrfpydef { + //TODO: add this method to class scope; move implementation to .cpp file + static PyObject* PyUICaption_get_float_member(PyUICaptionObject* self, void* closure) + { + auto member_ptr = reinterpret_cast(closure); + if (member_ptr == 0) + return PyFloat_FromDouble(self->data->text.getPosition().x); + else if (member_ptr == 1) + return PyFloat_FromDouble(self->data->text.getPosition().y); + else if (member_ptr == 4) + return PyFloat_FromDouble(self->data->text.getOutlineThickness()); + else if (member_ptr == 5) + return PyLong_FromLong(self->data->text.getCharacterSize()); + else + { + PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); + return nullptr; + } + } + + //TODO: add this method to class scope; move implementation to .cpp file + static int PyUICaption_set_float_member(PyUICaptionObject* self, PyObject* value, void* closure) + { + float val; + auto member_ptr = reinterpret_cast(closure); + if (PyFloat_Check(value)) + { + val = PyFloat_AsDouble(value); + } + else if (PyLong_Check(value)) + { + val = PyLong_AsLong(value); + } + else + { + PyErr_SetString(PyExc_TypeError, "Value must be an integer."); + return -1; + } + if (member_ptr == 0) //x + self->data->text.setPosition(val, self->data->text.getPosition().y); + else if (member_ptr == 1) //y + self->data->text.setPosition(self->data->text.getPosition().x, val); + else if (member_ptr == 4) //outline + self->data->text.setOutlineThickness(val); + else if (member_ptr == 5) // character size + self->data->text.setCharacterSize(val); + return 0; + } + + //TODO: add this method to class scope; move implementation to .cpp file + static PyObject* PyUICaption_get_vec_member(PyUICaptionObject* self, void* closure) + { + return PyVector(self->data->text.getPosition()).pyObject(); + } + + //TODO: add this method to class scope; move implementation to .cpp file + static int PyUICaption_set_vec_member(PyUICaptionObject* self, PyObject* value, void* closure) + { + self->data->text.setPosition(PyVector::fromPy(value)); + return 0; + } + + //TODO: add this method to class scope; move implementation to .cpp file + static PyObject* PyUICaption_get_color_member(PyUICaptionObject* self, void* closure) + { + // validate closure (should be impossible to be wrong, but it's thorough) + auto member_ptr = reinterpret_cast(closure); + if (member_ptr != 0 && member_ptr != 1) + { + PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); + return nullptr; + } + + // TODO: manually calling tp_alloc to create a PyColorObject seems like an antipattern + // fetch correct member data + sf::Color color; + + if (member_ptr == 0) + { + color = self->data->text.getFillColor(); + } + else if (member_ptr == 1) + { + color = self->data->text.getOutlineColor(); + } + + return PyColor(color).pyObject(); + } + + //TODO: add this method to class scope; move implementation to .cpp file + static int PyUICaption_set_color_member(PyUICaptionObject* self, PyObject* value, void* closure) + { + auto member_ptr = reinterpret_cast(closure); + //TODO: this logic of (PyColor instance OR tuple -> sf::color) should be encapsulated for reuse + int r, g, b, a; + if (PyObject_IsInstance(value, (PyObject*)&PyColorType)) + { + // get value from mcrfpy.Color instance + auto c = ((PyColorObject*)value)->data; + r = c.r; g = c.g; b = c.b; a = c.a; + std::cout << "got " << int(r) << ", " << int(g) << ", " << int(b) << ", " << int(a) << std::endl; + } + else if (!PyTuple_Check(value) || PyTuple_Size(value) < 3 || PyTuple_Size(value) > 4) + { + // reject non-Color, non-tuple value + PyErr_SetString(PyExc_TypeError, "Value must be a tuple of 3 or 4 integers or an mcrfpy.Color object."); + return -1; + } + else // get value from tuples + { + r = PyLong_AsLong(PyTuple_GetItem(value, 0)); + g = PyLong_AsLong(PyTuple_GetItem(value, 1)); + b = PyLong_AsLong(PyTuple_GetItem(value, 2)); + a = 255; + + if (PyTuple_Size(value) == 4) + { + a = PyLong_AsLong(PyTuple_GetItem(value, 3)); + } + } + + if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255 || a < 0 || a > 255) + { + PyErr_SetString(PyExc_ValueError, "Color values must be between 0 and 255."); + return -1; + } + + if (member_ptr == 0) + { + self->data->text.setFillColor(sf::Color(r, g, b, a)); + } + else if (member_ptr == 1) + { + self->data->text.setOutlineColor(sf::Color(r, g, b, a)); + } + else + { + PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); + return -1; + } + + return 0; + } + + //TODO: add this method to class scope; move implementation to .cpp file + static PyObject* PyUICaption_get_text(PyUICaptionObject* self, void* closure) + { + Resources::caption_buffer = self->data->text.getString(); + return PyUnicode_FromString(Resources::caption_buffer.c_str()); + } + + //TODO: add this method to class scope; move implementation to .cpp file + static int PyUICaption_set_text(PyUICaptionObject* self, PyObject* value, void* closure) + { + PyObject* s = PyObject_Str(value); + PyObject * temp_bytes = PyUnicode_AsEncodedString(s, "UTF-8", "strict"); // Owned reference + if (temp_bytes != NULL) { + Resources::caption_buffer = PyBytes_AS_STRING(temp_bytes); // Borrowed pointer + Py_DECREF(temp_bytes); + } + self->data->text.setString(Resources::caption_buffer); + return 0; + } + + //TODO: add this static array to class scope; move implementation to .cpp file + static PyGetSetDef PyUICaption_getsetters[] = { + {"x", (getter)PyUICaption_get_float_member, (setter)PyUICaption_set_float_member, "X coordinate of top-left corner", (void*)0}, + {"y", (getter)PyUICaption_get_float_member, (setter)PyUICaption_set_float_member, "Y coordinate of top-left corner", (void*)1}, + {"pos", (getter)PyUICaption_get_vec_member, (setter)PyUICaption_set_vec_member, "(x, y) vector", (void*)0}, + //{"w", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "width of the rectangle", (void*)2}, + //{"h", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "height of the rectangle", (void*)3}, + {"outline", (getter)PyUICaption_get_float_member, (setter)PyUICaption_set_float_member, "Thickness of the border", (void*)4}, + {"fill_color", (getter)PyUICaption_get_color_member, (setter)PyUICaption_set_color_member, "Fill color of the text", (void*)0}, + {"outline_color", (getter)PyUICaption_get_color_member, (setter)PyUICaption_set_color_member, "Outline color of the text", (void*)1}, + //{"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} + }; + + //TODO: add this method to class scope; move implementation to .cpp file + static PyObject* PyUICaption_repr(PyUICaptionObject* self) + { + std::ostringstream ss; + if (!self->data) ss << ""; + else { + auto text = self->data->text; + auto fc = text.getFillColor(); + auto oc = text.getOutlineColor(); + ss << ""; + } + std::string repr_str = ss.str(); + return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); + } + + //TODO: add this method to class scope; move implementation to .cpp file + static int PyUICaption_init(PyUICaptionObject* self, PyObject* args, PyObject* kwds) + { + //std::cout << "Init called\n"; + static const char* keywords[] = { "x", "y", "text", "font", "fill_color", "outline_color", nullptr }; + float x = 0.0f, y = 0.0f; + char* text; + PyObject* font, fill_color, outline_color; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ffzOOO", + const_cast(keywords), &x, &y, &text, &font, &fill_color, &outline_color)) + { + return -1; + } + + // check types for font, fill_color, outline_color + + std::cout << PyUnicode_AsUTF8(PyObject_Repr(font)) << std::endl; + if (font != NULL && !PyObject_IsInstance(font, (PyObject*)&PyFontType)){ + PyErr_SetString(PyExc_TypeError, "font must be a mcrfpy.Font instance"); + return -1; + } else if (font != NULL) + { + auto font_obj = (PyFontObject*)font; + self->data->text.setFont(font_obj->data->font); + self->font = font; + Py_INCREF(font); + } else + { + // default font + //self->data->text.setFont(Resources::game->getFont()); + } + + self->data->text.setPosition(sf::Vector2f(x, y)); + self->data->text.setString((std::string)text); + self->data->text.setFillColor(sf::Color(0,0,0,255)); + self->data->text.setOutlineColor(sf::Color(128,128,128,255)); + + return 0; + } + + static PyTypeObject PyUICaptionType = { + //PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "mcrfpy.Caption", + .tp_basicsize = sizeof(PyUICaptionObject), + .tp_itemsize = 0, + .tp_dealloc = (destructor)[](PyObject* self) + { + PyUICaptionObject* obj = (PyUICaptionObject*)self; + // release reference to font object + if (obj->font) Py_DECREF(obj->font); + obj->data.reset(); + Py_TYPE(self)->tp_free(self); + }, + .tp_repr = (reprfunc)PyUICaption_repr, + //.tp_hash = NULL, + //.tp_iter + //.tp_iternext + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_doc = PyDoc_STR("docstring"), + //.tp_methods = PyUIFrame_methods, + //.tp_members = PyUIFrame_members, + .tp_getset = PyUICaption_getsetters, + //.tp_base = NULL, + .tp_init = (initproc)PyUICaption_init, + .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* + { + PyUICaptionObject* self = (PyUICaptionObject*)type->tp_alloc(type, 0); + if (self) self->data = std::make_shared(); + return (PyObject*)self; + } + }; +} diff --git a/src/UICollection.cpp b/src/UICollection.cpp new file mode 100644 index 0000000..8133952 --- /dev/null +++ b/src/UICollection.cpp @@ -0,0 +1,2 @@ +#include "UICollection.h" + diff --git a/src/UICollection.h b/src/UICollection.h new file mode 100644 index 0000000..03136f0 --- /dev/null +++ b/src/UICollection.h @@ -0,0 +1,279 @@ +#pragma once +#include "Common.h" +#include "Python.h" +#include "structmember.h" +#include "IndexTexture.h" +#include "Resources.h" +#include + +#include "PyCallable.h" +#include "PyTexture.h" +#include "PyColor.h" +#include "PyVector.h" +#include "PyFont.h" + +#include "UIDrawable.h" +#include "UICaption.h" +#include "UIFrame.h" +#include "UISprite.h" +#include "UIGrid.h" + +namespace mcrfpydef { + + typedef struct { + PyObject_HEAD + std::shared_ptr>> data; + int index; + int start_size; + } PyUICollectionIterObject; + + //TODO: add this method to class scope; move implementation to .cpp file + static int PyUICollectionIter_init(PyUICollectionIterObject* self, PyObject* args, PyObject* kwds) + { + PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required."); + return -1; + } + + //TODO: add this method to class scope; move implementation to .cpp file + static PyObject* PyUICollectionIter_next(PyUICollectionIterObject* self) + { + if (self->data->size() != self->start_size) + { + PyErr_SetString(PyExc_RuntimeError, "collection changed size during iteration"); + return NULL; + } + + if (self->index > self->start_size - 1) + { + PyErr_SetNone(PyExc_StopIteration); + return NULL; + } + self->index++; + auto vec = self->data.get(); + if (!vec) + { + PyErr_SetString(PyExc_RuntimeError, "the collection store returned a null pointer"); + return NULL; + } + auto target = (*vec)[self->index-1]; + // TODO build PyObject* of the correct UIDrawable subclass to return + //return py_instance(target); + return NULL; + } + + //TODO: add this method to class scope; move implementation to .cpp file + static PyObject* PyUICollectionIter_repr(PyUICollectionIterObject* self) + { + std::ostringstream ss; + if (!self->data) ss << ""; + else { + ss << "data->size() << " child objects, @ index " << self->index << ")>"; + } + std::string repr_str = ss.str(); + return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); + } + + static PyTypeObject PyUICollectionIterType = { + //PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "mcrfpy.UICollectionIter", + .tp_basicsize = sizeof(PyUICollectionIterObject), + .tp_itemsize = 0, + .tp_dealloc = (destructor)[](PyObject* self) + { + PyUICollectionIterObject* obj = (PyUICollectionIterObject*)self; + obj->data.reset(); + Py_TYPE(self)->tp_free(self); + }, + .tp_repr = (reprfunc)PyUICollectionIter_repr, + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_doc = PyDoc_STR("Iterator for a collection of UI objects"), + .tp_iternext = (iternextfunc)PyUICollectionIter_next, + //.tp_getset = PyUICollection_getset, + .tp_init = (initproc)PyUICollectionIter_init, // just raise an exception + .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* + { + PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required."); + return NULL; + } + }; + + //TODO: add this method to class scope; move implementation to .cpp file + static Py_ssize_t PyUICollection_len(PyUICollectionObject* self) { + return self->data->size(); + } + + //TODO: add this method to class scope; move implementation to .cpp file + static PyObject* PyUICollection_getitem(PyUICollectionObject* self, Py_ssize_t index) { + // build a Python version of item at self->data[index] + // Copy pasted:: + auto vec = self->data.get(); + if (!vec) + { + PyErr_SetString(PyExc_RuntimeError, "the collection store returned a null pointer"); + return NULL; + } + while (index < 0) index += self->data->size(); + if (index > self->data->size() - 1) + { + PyErr_SetString(PyExc_IndexError, "UICollection index out of range"); + return NULL; + } + auto target = (*vec)[index]; + RET_PY_INSTANCE(target); + return NULL; + + + } + + //TODO: add this static array to class scope; move implementation to .cpp file + static PySequenceMethods PyUICollection_sqmethods = { + .sq_length = (lenfunc)PyUICollection_len, + .sq_item = (ssizeargfunc)PyUICollection_getitem, + //.sq_item_by_index = PyUICollection_getitem + //.sq_slice - return a subset of the iterable + //.sq_ass_item - called when `o[x] = y` is executed (x is any object type) + //.sq_ass_slice - cool; no thanks, for now + //.sq_contains - called when `x in o` is executed + //.sq_ass_item_by_index - called when `o[x] = y` is executed (x is explictly an integer) + }; + + //TODO: add this method to class scope; move implementation to .cpp file + static PyObject* PyUICollection_append(PyUICollectionObject* self, PyObject* o) + { + // if not UIDrawable subclass, reject it + // self->data->push_back( c++ object inside o ); + + // this would be a great use case for .tp_base + if (!PyObject_IsInstance(o, (PyObject*)&PyUIFrameType) && + !PyObject_IsInstance(o, (PyObject*)&PyUISpriteType) && + !PyObject_IsInstance(o, (PyObject*)&PyUICaptionType) && + !PyObject_IsInstance(o, (PyObject*)&PyUIGridType) + ) + { + PyErr_SetString(PyExc_TypeError, "Only Frame, Caption, Sprite, and Grid objects can be added to UICollection"); + return NULL; + } + + if (PyObject_IsInstance(o, (PyObject*)&PyUIFrameType)) + { + PyUIFrameObject* frame = (PyUIFrameObject*)o; + self->data->push_back(frame->data); + } + if (PyObject_IsInstance(o, (PyObject*)&PyUICaptionType)) + { + PyUICaptionObject* caption = (PyUICaptionObject*)o; + self->data->push_back(caption->data); + } + if (PyObject_IsInstance(o, (PyObject*)&PyUISpriteType)) + { + PyUISpriteObject* sprite = (PyUISpriteObject*)o; + self->data->push_back(sprite->data); + } + if (PyObject_IsInstance(o, (PyObject*)&PyUIGridType)) + { + PyUIGridObject* grid = (PyUIGridObject*)o; + self->data->push_back(grid->data); + } + + Py_INCREF(Py_None); + return Py_None; + } + + //TODO: add this method to class scope; move implementation to .cpp file + static PyObject* PyUICollection_remove(PyUICollectionObject* self, PyObject* o) + { + if (!PyLong_Check(o)) + { + PyErr_SetString(PyExc_TypeError, "UICollection.remove requires an integer index to remove"); + return NULL; + } + long index = PyLong_AsLong(o); + if (index >= self->data->size()) + { + PyErr_SetString(PyExc_ValueError, "Index out of range"); + return NULL; + } + else if (index < 0) + { + PyErr_SetString(PyExc_NotImplementedError, "reverse indexing is not implemented."); + return NULL; + } + + // release the shared pointer at self->data[index]; + self->data->erase(self->data->begin() + index); + Py_INCREF(Py_None); + return Py_None; + } + + //TODO: add this static array to class scope; move implementation to .cpp file + static PyMethodDef PyUICollection_methods[] = { + {"append", (PyCFunction)PyUICollection_append, METH_O}, + //{"extend", (PyCFunction)PyUICollection_extend, METH_O}, // TODO + {"remove", (PyCFunction)PyUICollection_remove, METH_O}, + {NULL, NULL, 0, NULL} + }; + + //TODO: add this method to class scope; move implementation to .cpp file + static PyObject* PyUICollection_repr(PyUICollectionObject* self) + { + std::ostringstream ss; + if (!self->data) ss << ""; + else { + ss << "data->size() << " child objects)>"; + } + std::string repr_str = ss.str(); + return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); + } + + //TODO: add this method to class scope; move implementation to .cpp file + static int PyUICollection_init(PyUICollectionObject* self, PyObject* args, PyObject* kwds) + { + PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required."); + return -1; + } + + //TODO: add this method to class scope; move implementation to .cpp file + static PyObject* PyUICollection_iter(PyUICollectionObject* self) + { + PyUICollectionIterObject* iterObj; + iterObj = (PyUICollectionIterObject*)PyUICollectionIterType.tp_alloc(&PyUICollectionIterType, 0); + if (iterObj == NULL) { + return NULL; // Failed to allocate memory for the iterator object + } + + iterObj->data = self->data; + iterObj->index = 0; + iterObj->start_size = self->data->size(); + + return (PyObject*)iterObj; + } + + static PyTypeObject PyUICollectionType = { + //PyVarObject_/HEAD_INIT(NULL, 0) + .tp_name = "mcrfpy.UICollection", + .tp_basicsize = sizeof(PyUICollectionObject), + .tp_itemsize = 0, + .tp_dealloc = (destructor)[](PyObject* self) + { + PyUICollectionObject* obj = (PyUICollectionObject*)self; + obj->data.reset(); + Py_TYPE(self)->tp_free(self); + }, + .tp_repr = (reprfunc)PyUICollection_repr, + .tp_as_sequence = &PyUICollection_sqmethods, + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_doc = PyDoc_STR("Iterable, indexable collection of UI objects"), + .tp_iter = (getiterfunc)PyUICollection_iter, + .tp_methods = PyUICollection_methods, // append, remove + //.tp_getset = PyUICollection_getset, + .tp_init = (initproc)PyUICollection_init, // just raise an exception + .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* + { + // Does PyUICollectionType need __new__ if it's not supposed to be instantiable by the user? + // Should I just raise an exception? Or is the uninitialized shared_ptr enough of a blocker? + PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required."); + return NULL; + } + }; + +} diff --git a/src/UIDrawable.cpp b/src/UIDrawable.cpp new file mode 100644 index 0000000..7cd1178 --- /dev/null +++ b/src/UIDrawable.cpp @@ -0,0 +1,13 @@ +#include "UIDrawable.h" + +UIDrawable::UIDrawable() { click_callable = NULL; } + +void UIDrawable::click_unregister() +{ + click_callable.reset(); +} + +void UIDrawable::render() +{ + render(sf::Vector2f()); +} diff --git a/src/UIDrawable.h b/src/UIDrawable.h new file mode 100644 index 0000000..5fb01e1 --- /dev/null +++ b/src/UIDrawable.h @@ -0,0 +1,162 @@ +#pragma once +#include "Common.h" +#include "Python.h" +#include "structmember.h" +#include "IndexTexture.h" +#include "Resources.h" +#include + +#include "PyCallable.h" +#include "PyTexture.h" +#include "PyColor.h" +#include "PyVector.h" +#include "PyFont.h" + +class UIFrame; class UICaption; class UISprite; class UIEntity; class UIGrid; + +enum PyObjectsEnum : int +{ + UIFRAME = 1, + UICAPTION, + UISPRITE, + UIGRID +}; + +class UIDrawable +{ +public: + void render(); + virtual void render(sf::Vector2f) = 0; + virtual PyObjectsEnum derived_type() = 0; + + // Mouse input handling - callable object, methods to find event's destination + std::unique_ptr click_callable; + virtual UIDrawable* click_at(sf::Vector2f point) = 0; + void click_register(PyObject*); + void click_unregister(); + + UIDrawable(); +}; + +typedef struct { + PyObject_HEAD + std::shared_ptr>> data; +} PyUICollectionObject; + +namespace mcrfpydef { + //PyObject* py_instance(std::shared_ptr source); + // This function segfaults on tp_alloc for an unknown reason, but works inline with mcrfpydef:: methods. + +#define RET_PY_INSTANCE(target) { \ +switch (target->derived_type()) \ +{ \ + case PyObjectsEnum::UIFRAME: \ + { \ + PyUIFrameObject* o = (PyUIFrameObject*)((&PyUIFrameType)->tp_alloc(&PyUIFrameType, 0)); \ + if (o) \ + { \ + auto p = std::static_pointer_cast(target); \ + o->data = p; \ + auto utarget = o->data; \ + } \ + return (PyObject*)o; \ + } \ + case PyObjectsEnum::UICAPTION: \ + { \ + PyUICaptionObject* o = (PyUICaptionObject*)((&PyUICaptionType)->tp_alloc(&PyUICaptionType, 0)); \ + if (o) \ + { \ + auto p = std::static_pointer_cast(target); \ + o->data = p; \ + auto utarget = o->data; \ + } \ + return (PyObject*)o; \ + } \ + case PyObjectsEnum::UISPRITE: \ + { \ + PyUISpriteObject* o = (PyUISpriteObject*)((&PyUISpriteType)->tp_alloc(&PyUISpriteType, 0)); \ + if (o) \ + { \ + auto p = std::static_pointer_cast(target); \ + o->data = p; \ + auto utarget = o->data; \ + } \ + return (PyObject*)o; \ + } \ + case PyObjectsEnum::UIGRID: \ + { \ + PyUIGridObject* o = (PyUIGridObject*)((&PyUIGridType)->tp_alloc(&PyUIGridType, 0)); \ + if (o) \ + { \ + auto p = std::static_pointer_cast(target); \ + o->data = p; \ + auto utarget = o->data; \ + } \ + return (PyObject*)o; \ + } \ +} \ +} +// end macro definition + +//TODO: add this method to class scope; move implementation to .cpp file +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->borrow(); + break; + case PyObjectsEnum::UICAPTION: + ptr = ((PyUICaptionObject*)self)->data->click_callable->borrow(); + break; + case PyObjectsEnum::UISPRITE: + ptr = ((PyUISpriteObject*)self)->data->click_callable->borrow(); + break; + case PyObjectsEnum::UIGRID: + ptr = ((PyUIGridObject*)self)->data->click_callable->borrow(); + 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; +} + +//TODO: add this method to class scope; move implementation to .cpp file +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; +} + +} diff --git a/src/UIEntity.cpp b/src/UIEntity.cpp new file mode 100644 index 0000000..96738d7 --- /dev/null +++ b/src/UIEntity.cpp @@ -0,0 +1,9 @@ +#include "UIEntity.h" + +UIEntity::UIEntity() {} // this will not work lol. TODO remove default constructor by finding the shared pointer inits that use it + +UIEntity::UIEntity(UIGrid& grid) +: gridstate(grid.grid_x * grid.grid_y) +{ +} + diff --git a/src/UIEntity.h b/src/UIEntity.h new file mode 100644 index 0000000..a99369b --- /dev/null +++ b/src/UIEntity.h @@ -0,0 +1,40 @@ +#pragma once +#include "Common.h" +#include "Python.h" +#include "structmember.h" +#include "IndexTexture.h" +#include "Resources.h" +#include + +#include "PyCallable.h" +#include "PyTexture.h" +#include "PyColor.h" +#include "PyVector.h" +#include "PyFont.h" + +#include "UIGridPoint.h" +#include "UIDrawable.h" + +class UIGrid; + +// TODO: make UIEntity a drawable +class UIEntity//: public UIDrawable +{ +public: + //PyObject* self; + std::shared_ptr grid; + std::vector gridstate; + UISprite sprite; + sf::Vector2f position; //(x,y) in grid coordinates; float for animation + void render(sf::Vector2f); //override final; + + UIEntity(); + UIEntity(UIGrid&); + +}; + +typedef struct { + PyObject_HEAD + std::shared_ptr data; + //PyObject* texture; +} PyUIEntityObject; diff --git a/src/UIFrame.cpp b/src/UIFrame.cpp new file mode 100644 index 0000000..6cebd9a --- /dev/null +++ b/src/UIFrame.cpp @@ -0,0 +1,69 @@ +#include "UIFrame.h" +#include "UICollection.h" + +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; +} + +UIFrame::UIFrame() +: outline(0) +{ + children = std::make_shared>>(); + box.setPosition(0, 0); + box.setSize(sf::Vector2f(0, 0)); +} + +UIFrame::UIFrame(float _x, float _y, float _w, float _h) +: outline(0) +{ + box.setPosition(_x, _y); + box.setSize(sf::Vector2f(_w, _h)); + children = std::make_shared>>(); +} + +UIFrame::~UIFrame() +{ + children.reset(); +} + +PyObjectsEnum UIFrame::derived_type() +{ + return PyObjectsEnum::UIFRAME; +} + +void UIFrame::render(sf::Vector2f offset) +{ + box.move(offset); + Resources::game->getWindow().draw(box); + box.move(-offset); + + for (auto drawable : *children) { + drawable->render(offset + box.getPosition()); + } +} + +namespace mcrfpydef { + + // TODO: move to class scope; but this should at least get us compiling + static PyObject* PyUIFrame_get_children(PyUIFrameObject* self, void* closure) + { + // create PyUICollection instance pointing to self->data->children + PyUICollectionObject* o = (PyUICollectionObject*)PyUICollectionType.tp_alloc(&PyUICollectionType, 0); + if (o) + o->data = self->data->children; + return (PyObject*)o; + } +} + diff --git a/src/UIFrame.h b/src/UIFrame.h new file mode 100644 index 0000000..c51f042 --- /dev/null +++ b/src/UIFrame.h @@ -0,0 +1,271 @@ +#pragma once +#include "Common.h" +#include "Python.h" +#include "structmember.h" +#include "IndexTexture.h" +#include "Resources.h" +#include + +#include "PyCallable.h" +#include "PyColor.h" +#include "PyVector.h" +#include "UIDrawable.h" + +class UIFrame: public UIDrawable +{ +public: + UIFrame(float, float, float, float); + UIFrame(); + ~UIFrame(); + sf::RectangleShape box; + float outline; + std::shared_ptr>> children; + void render(sf::Vector2f) override final; + void move(sf::Vector2f); + PyObjectsEnum derived_type() override final; + virtual UIDrawable* click_at(sf::Vector2f point) override final; +}; + +typedef struct { + PyObject_HEAD + std::shared_ptr data; +} PyUIFrameObject; + +namespace mcrfpydef { + //TODO: add this method to class scope; move implementation to .cpp file + static PyObject* PyUIFrame_get_float_member(PyUIFrameObject* self, void* closure) + { + auto member_ptr = reinterpret_cast(closure); + if (member_ptr == 0) + return PyFloat_FromDouble(self->data->box.getPosition().x); + else if (member_ptr == 1) + return PyFloat_FromDouble(self->data->box.getPosition().y); + else if (member_ptr == 2) + return PyFloat_FromDouble(self->data->box.getSize().x); + else if (member_ptr == 3) + return PyFloat_FromDouble(self->data->box.getSize().y); + else if (member_ptr == 4) + return PyFloat_FromDouble(self->data->box.getOutlineThickness()); + else + { + PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); + return nullptr; + } + } + + //TODO: add this method to class scope; move implementation to .cpp file + static int PyUIFrame_set_float_member(PyUIFrameObject* self, PyObject* value, void* closure) + { + float val; + auto member_ptr = reinterpret_cast(closure); + if (PyFloat_Check(value)) + { + val = PyFloat_AsDouble(value); + } + else if (PyLong_Check(value)) + { + val = PyLong_AsLong(value); + } + else + { + PyErr_SetString(PyExc_TypeError, "Value must be an integer."); + return -1; + } + if (member_ptr == 0) //x + self->data->box.setPosition(val, self->data->box.getPosition().y); + else if (member_ptr == 1) //y + self->data->box.setPosition(self->data->box.getPosition().x, val); + else if (member_ptr == 2) //w + self->data->box.setSize(sf::Vector2f(val, self->data->box.getSize().y)); + else if (member_ptr == 3) //h + self->data->box.setSize(sf::Vector2f(self->data->box.getSize().x, val)); + else if (member_ptr == 4) //outline + self->data->box.setOutlineThickness(val); + return 0; + } + + //TODO: add this method to class scope; move implementation to .cpp file + static PyObject* PyUIFrame_get_color_member(PyUIFrameObject* self, void* closure) + { + // validate closure (should be impossible to be wrong, but it's thorough) + auto member_ptr = reinterpret_cast(closure); + if (member_ptr != 0 && member_ptr != 1) + { + PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); + return nullptr; + } + 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); + + // fetch correct member data + sf::Color color; + if (member_ptr == 0) + { + color = self->data->box.getFillColor(); + //return Py_BuildValue("(iii)", color.r, color.g, color.b); + } + else if (member_ptr == 1) + { + color = self->data->box.getOutlineColor(); + //return Py_BuildValue("(iii)", color.r, color.g, color.b); + } + + return PyColor(color).pyObject(); + } + + //TODO: add this method to class scope; move implementation to .cpp file + static int PyUIFrame_set_color_member(PyUIFrameObject* self, PyObject* value, void* closure) + { + //TODO: this logic of (PyColor instance OR tuple -> sf::color) should be encapsulated for reuse + auto member_ptr = reinterpret_cast(closure); + int r, g, b, a; + if (PyObject_IsInstance(value, (PyObject*)&PyColorType)) + { + sf::Color c = ((PyColorObject*)value)->data; + r = c.r; g = c.g; b = c.b; a = c.a; + } + else if (!PyTuple_Check(value) || PyTuple_Size(value) < 3 || PyTuple_Size(value) > 4) + { + // reject non-Color, non-tuple value + PyErr_SetString(PyExc_TypeError, "Value must be a tuple of 3 or 4 integers or an mcrfpy.Color object."); + return -1; + } + else // get value from tuples + { + r = PyLong_AsLong(PyTuple_GetItem(value, 0)); + g = PyLong_AsLong(PyTuple_GetItem(value, 1)); + b = PyLong_AsLong(PyTuple_GetItem(value, 2)); + a = 255; + + if (PyTuple_Size(value) == 4) + { + a = PyLong_AsLong(PyTuple_GetItem(value, 3)); + } + } + + if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255 || a < 0 || a > 255) + { + PyErr_SetString(PyExc_ValueError, "Color values must be between 0 and 255."); + return -1; + } + + if (member_ptr == 0) + { + self->data->box.setFillColor(sf::Color(r, g, b, a)); + } + else if (member_ptr == 1) + { + self->data->box.setOutlineColor(sf::Color(r, g, b, a)); + } + else + { + PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); + return -1; + } + + return 0; + } + + //TODO: add this method to class scope; move implementation to .cpp file + static PyObject* PyUIFrame_get_children(PyUIFrameObject*, void*); // forward declaration until UICollection is defined + // implementation after the PyUICollectionType definition + + //TODO: add this static array to class scope; move implementation to .cpp file + static PyGetSetDef PyUIFrame_getsetters[] = { + {"x", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "X coordinate of top-left corner", (void*)0}, + {"y", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "Y coordinate of top-left corner", (void*)1}, + {"w", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "width of the rectangle", (void*)2}, + {"h", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "height of the rectangle", (void*)3}, + {"outline", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "Thickness of the border", (void*)4}, + {"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} + }; + + //TODO: add this method to class scope; move implementation to .cpp file + static PyObject* PyUIFrame_repr(PyUIFrameObject* self) + { + std::ostringstream ss; + if (!self->data) ss << ""; + else { + auto box = self->data->box; + auto fc = box.getFillColor(); + auto oc = box.getOutlineColor(); + ss << "data->children->size() << " child objects" << + ")>"; + } + std::string repr_str = ss.str(); + return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); + } + + //TODO: add this method to class scope; move implementation to .cpp file + static int PyUIFrame_init(PyUIFrameObject* self, PyObject* args, PyObject* kwds) + { + //std::cout << "Init called\n"; + static const char* keywords[] = { "x", "y", "w", "h", "fill_color", "outline_color", "outline", nullptr }; + float x = 0.0f, y = 0.0f, w = 0.0f, h=0.0f, outline=0.0f; + PyObject* fill_color = 0; + PyObject* outline_color = 0; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "ffff|OOf", const_cast(keywords), &x, &y, &w, &h, &fill_color, &outline_color, &outline)) + { + return -1; + } + + self->data->box.setPosition(sf::Vector2f(x, y)); + self->data->box.setSize(sf::Vector2f(w, h)); + self->data->box.setOutlineThickness(outline); + // getsetter abuse because I haven't standardized Color object parsing (TODO) + int err_val = 0; + if (fill_color && fill_color != Py_None) err_val = PyUIFrame_set_color_member(self, fill_color, (void*)0); + else self->data->box.setFillColor(sf::Color(0,0,0,255)); + if (err_val) return err_val; + if (outline_color && outline_color != Py_None) err_val = PyUIFrame_set_color_member(self, outline_color, (void*)1); + else self->data->box.setOutlineColor(sf::Color(128,128,128,255)); + if (err_val) return err_val; + return 0; + } + + static PyTypeObject PyUIFrameType = { + //PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "mcrfpy.Frame", + .tp_basicsize = sizeof(PyUIFrameObject), + .tp_itemsize = 0, + .tp_dealloc = (destructor)[](PyObject* self) + { + PyUIFrameObject* obj = (PyUIFrameObject*)self; + obj->data.reset(); + Py_TYPE(self)->tp_free(self); + }, + .tp_repr = (reprfunc)PyUIFrame_repr, + //.tp_hash = NULL, + //.tp_iter + //.tp_iternext + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_doc = PyDoc_STR("docstring"), + //.tp_methods = PyUIFrame_methods, + //.tp_members = PyUIFrame_members, + .tp_getset = PyUIFrame_getsetters, + //.tp_base = NULL, + .tp_init = (initproc)PyUIFrame_init, + .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* + { + PyUIFrameObject* self = (PyUIFrameObject*)type->tp_alloc(type, 0); + if (self) self->data = std::make_shared(); + return (PyObject*)self; + } + }; +} diff --git a/src/UIGrid.cpp b/src/UIGrid.cpp new file mode 100644 index 0000000..207114b --- /dev/null +++ b/src/UIGrid.cpp @@ -0,0 +1,190 @@ +#include "UIGrid.h" + +UIGrid::UIGrid() {} + +UIGrid::UIGrid(int gx, int gy, std::shared_ptr _ptex, sf::Vector2f _xy, sf::Vector2f _wh) +: grid_x(gx), grid_y(gy), + zoom(1.0f), center_x((gx/2) * _ptex->sprite_width), center_y((gy/2) * _ptex->sprite_height), + ptex(_ptex), points(gx * gy) +{ + entities = std::make_shared>>(); + + box.setSize(_wh); + box.setPosition(_xy); + + box.setFillColor(sf::Color(0,0,0,0)); + // create renderTexture with maximum theoretical size; sprite can resize to show whatever amount needs to be rendered + renderTexture.create(1920, 1080); // TODO - renderTexture should be window size; above 1080p this will cause rendering errors + + sprite = ptex->sprite(0); + + output.setTextureRect( + sf::IntRect(0, 0, + box.getSize().x, box.getSize().y)); + output.setPosition(box.getPosition()); + // textures are upside-down inside renderTexture + output.setTexture(renderTexture.getTexture()); + +} + +void UIGrid::update() {} + + +void UIGrid::render(sf::Vector2f) +{ + output.setPosition(box.getPosition()); // output sprite can move; update position when drawing + // output size can change; update size when drawing + output.setTextureRect( + sf::IntRect(0, 0, + box.getSize().x, box.getSize().y)); + renderTexture.clear(sf::Color(8, 8, 8, 255)); // TODO - UIGrid needs a "background color" field + // sprites that are visible according to zoom, center_x, center_y, and box width + float center_x_sq = center_x / ptex->sprite_width; + float center_y_sq = center_y / ptex->sprite_height; + + float width_sq = box.getSize().x / (ptex->sprite_width * zoom); + float height_sq = box.getSize().y / (ptex->sprite_height * zoom); + float left_edge = center_x_sq - (width_sq / 2.0); + float top_edge = center_y_sq - (height_sq / 2.0); + + int left_spritepixels = center_x - (box.getSize().x / 2.0 / zoom); + int top_spritepixels = center_y - (box.getSize().y / 2.0 / zoom); + + //sprite.setScale(sf::Vector2f(zoom, zoom)); + sf::RectangleShape r; // for colors and overlays + r.setSize(sf::Vector2f(ptex->sprite_width * zoom, ptex->sprite_height * zoom)); + r.setOutlineThickness(0); + + int x_limit = left_edge + width_sq + 2; + if (x_limit > grid_x) x_limit = grid_x; + + int y_limit = top_edge + height_sq + 2; + if (y_limit > grid_y) y_limit = grid_y; + + // base layer - bottom color, tile sprite ("ground") + for (int x = (left_edge - 1 >= 0 ? left_edge - 1 : 0); + x < x_limit; //x < view_width; + x+=1) + { + //for (float y = (top_edge >= 0 ? top_edge : 0); + for (int y = (top_edge - 1 >= 0 ? top_edge - 1 : 0); + y < y_limit; //y < view_height; + y+=1) + { + auto pixel_pos = sf::Vector2f( + (x*ptex->sprite_width - left_spritepixels) * zoom, + (y*ptex->sprite_height - top_spritepixels) * zoom ); + + auto gridpoint = at(std::floor(x), std::floor(y)); + + //sprite.setPosition(pixel_pos); + + r.setPosition(pixel_pos); + r.setFillColor(gridpoint.color); + renderTexture.draw(r); + + // tilesprite + // if discovered but not visible, set opacity to 90% + // if not discovered... just don't draw it? + if (gridpoint.tilesprite != -1) { + sprite = ptex->sprite(gridpoint.tilesprite, pixel_pos, sf::Vector2f(zoom, zoom)); //setSprite(gridpoint.tilesprite);; + renderTexture.draw(sprite); + } + } + } + + // middle layer - entities + // disabling entity rendering until I can render their UISprite inside the rendertexture (not directly to window) + for (auto e : *entities) { + // TODO skip out-of-bounds entities (grid square not visible at all, check for partially on visible grid squares / floating point grid position) + //auto drawent = e->cGrid->indexsprite.drawable(); + auto& drawent = e->sprite; + //drawent.setScale(zoom, zoom); + drawent.setScale(sf::Vector2f(zoom, zoom)); + auto pixel_pos = sf::Vector2f( + (e->position.x*ptex->sprite_width - left_spritepixels) * zoom, + (e->position.y*ptex->sprite_height - top_spritepixels) * zoom ); + //drawent.setPosition(pixel_pos); + //renderTexture.draw(drawent); + drawent.render(pixel_pos, renderTexture); + } + + + // top layer - opacity for discovered / visible status (debug, basically) + /* // Disabled until I attach a "perspective" + for (int x = (left_edge - 1 >= 0 ? left_edge - 1 : 0); + x < x_limit; //x < view_width; + x+=1) + { + //for (float y = (top_edge >= 0 ? top_edge : 0); + for (int y = (top_edge - 1 >= 0 ? top_edge - 1 : 0); + y < y_limit; //y < view_height; + y+=1) + { + + auto pixel_pos = sf::Vector2f( + (x*itex->grid_size - left_spritepixels) * zoom, + (y*itex->grid_size - top_spritepixels) * zoom ); + + auto gridpoint = at(std::floor(x), std::floor(y)); + + sprite.setPosition(pixel_pos); + + r.setPosition(pixel_pos); + + // visible & discovered layers for testing purposes + if (!gridpoint.discovered) { + r.setFillColor(sf::Color(16, 16, 20, 192)); // 255 opacity for actual blackout + renderTexture.draw(r); + } else if (!gridpoint.visible) { + r.setFillColor(sf::Color(32, 32, 40, 128)); + renderTexture.draw(r); + } + + // overlay + + // uisprite + } + } + */ + + // grid lines for testing & validation + /* + sf::Vertex line[] = + { + sf::Vertex(sf::Vector2f(0, 0), sf::Color::Red), + sf::Vertex(box.getSize(), sf::Color::Red), + + }; + + renderTexture.draw(line, 2, sf::Lines); + sf::Vertex lineb[] = + { + sf::Vertex(sf::Vector2f(0, box.getSize().y), sf::Color::Blue), + sf::Vertex(sf::Vector2f(box.getSize().x, 0), sf::Color::Blue), + + }; + + renderTexture.draw(lineb, 2, sf::Lines); + */ + + // render to window + renderTexture.display(); + Resources::game->getWindow().draw(output); + +} + +UIGridPoint& UIGrid::at(int x, int y) +{ + return points[y * grid_x + x]; +} + +PyObjectsEnum UIGrid::derived_type() +{ + return PyObjectsEnum::UIGRID; +} + +std::shared_ptr UIGrid::getTexture() +{ + return ptex; +} diff --git a/src/UIGrid.h b/src/UIGrid.h new file mode 100644 index 0000000..e55026b --- /dev/null +++ b/src/UIGrid.h @@ -0,0 +1,570 @@ +#pragma once +#include "Common.h" +#include "Python.h" +#include "structmember.h" +#include "IndexTexture.h" +#include "Resources.h" +#include + +#include "PyCallable.h" +#include "PyTexture.h" +#include "PyColor.h" +#include "PyVector.h" +#include "PyFont.h" + +#include "UIGridPoint.h" +#include "UIEntity.h" +#include "UIDrawable.h" + +class UIGrid: public UIDrawable +{ +private: + std::shared_ptr ptex; +public: + UIGrid(); + //UIGrid(int, int, IndexTexture*, float, float, float, float); + UIGrid(int, int, std::shared_ptr, sf::Vector2f, sf::Vector2f); + void update(); + void render(sf::Vector2f) override final; + 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 + sf::RectangleShape box; + float center_x, center_y, zoom; + //IndexTexture* itex; + std::shared_ptr getTexture(); + sf::Sprite sprite, output; + sf::RenderTexture renderTexture; + std::vector points; + std::shared_ptr>> entities; +}; + +typedef struct { + PyObject_HEAD + std::shared_ptr data; + //PyObject* texture; +} PyUIGridObject; + +namespace mcrfpydef { +//TODO: add this method to class scope; move implementation to .cpp file +static int PyUIGrid_init(PyUIGridObject* self, PyObject* args, PyObject* kwds) { + int grid_x, grid_y; + PyObject* textureObj; + float box_x, box_y, box_w, box_h; + + if (!PyArg_ParseTuple(args, "iiOffff", &grid_x, &grid_y, &textureObj, &box_x, &box_y, &box_w, &box_h)) { + return -1; // If parsing fails, return an error + } + + // Convert PyObject texture to IndexTexture* + // This requires the texture object to have been initialized similar to UISprite's texture handling + if (!PyObject_IsInstance(textureObj, (PyObject*)&PyTextureType)) { + PyErr_SetString(PyExc_TypeError, "texture must be a mcrfpy.Texture instance"); + return -1; + } + PyTextureObject* pyTexture = reinterpret_cast(textureObj); + // TODO (7DRL day 2, item 4.) use shared_ptr / PyTextureObject on UIGrid + //IndexTexture* texture = pyTexture->data.get(); + + // Initialize UIGrid + //self->data = new UIGrid(grid_x, grid_y, texture, sf::Vector2f(box_x, box_y), sf::Vector2f(box_w, box_h)); + self->data = std::make_shared(grid_x, grid_y, pyTexture->data, + sf::Vector2f(box_x, box_y), sf::Vector2f(box_w, box_h)); + return 0; // Success +} + +//TODO: add this method to class scope; move implementation to .cpp file +static PyObject* PyUIGrid_get_grid_size(PyUIGridObject* self, void* closure) { + return Py_BuildValue("(ii)", self->data->grid_x, self->data->grid_y); +} + +//TODO: add this method to class scope; move implementation to .cpp file +static PyObject* PyUIGrid_get_position(PyUIGridObject* self, void* closure) { + auto& box = self->data->box; + return Py_BuildValue("(ff)", box.getPosition().x, box.getPosition().y); +} + +//TODO: add this method to class scope; move implementation to .cpp file +static int PyUIGrid_set_position(PyUIGridObject* self, PyObject* value, void* closure) { + float x, y; + if (!PyArg_ParseTuple(value, "ff", &x, &y)) { + PyErr_SetString(PyExc_ValueError, "Position must be a tuple of two floats"); + return -1; + } + self->data->box.setPosition(x, y); + return 0; +} + +//TODO: add this method to class scope; move implementation to .cpp file +static PyObject* PyUIGrid_get_size(PyUIGridObject* self, void* closure) { + auto& box = self->data->box; + return Py_BuildValue("(ff)", box.getSize().x, box.getSize().y); +} + +//TODO: add this method to class scope; move implementation to .cpp file +static int PyUIGrid_set_size(PyUIGridObject* self, PyObject* value, void* closure) { + float w, h; + if (!PyArg_ParseTuple(value, "ff", &w, &h)) { + PyErr_SetString(PyExc_ValueError, "Size must be a tuple of two floats"); + return -1; + } + self->data->box.setSize(sf::Vector2f(w, h)); + return 0; +} + +//TODO: add this method to class scope; move implementation to .cpp file +static PyObject* PyUIGrid_get_center(PyUIGridObject* self, void* closure) { + return Py_BuildValue("(ff)", self->data->center_x, self->data->center_y); +} + +//TODO: add this method to class scope; move implementation to .cpp file +static int PyUIGrid_set_center(PyUIGridObject* self, PyObject* value, void* closure) { + float x, y; + if (!PyArg_ParseTuple(value, "ff", &x, &y)) { + PyErr_SetString(PyExc_ValueError, "Size must be a tuple of two floats"); + return -1; + } + self->data->center_x = x; + self->data->center_y = y; + return 0; +} + +//TODO: add this method to class scope; move implementation to .cpp file +static PyObject* PyUIGrid_get_float_member(PyUIGridObject* self, void* closure) +{ + auto member_ptr = reinterpret_cast(closure); + if (member_ptr == 0) // x + return PyFloat_FromDouble(self->data->box.getPosition().x); + else if (member_ptr == 1) // y + return PyFloat_FromDouble(self->data->box.getPosition().y); + else if (member_ptr == 2) // w + return PyFloat_FromDouble(self->data->box.getSize().x); + else if (member_ptr == 3) // h + return PyFloat_FromDouble(self->data->box.getSize().y); + else if (member_ptr == 4) // center_x + return PyFloat_FromDouble(self->data->center_x); + else if (member_ptr == 5) // center_y + return PyFloat_FromDouble(self->data->center_y); + else if (member_ptr == 6) // zoom + return PyFloat_FromDouble(self->data->zoom); + else + { + PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); + return nullptr; + } +} + +//TODO: add this method to class scope; move implementation to .cpp file +static int PyUIGrid_set_float_member(PyUIGridObject* self, PyObject* value, void* closure) +{ + float val; + auto member_ptr = reinterpret_cast(closure); + if (PyFloat_Check(value)) + { + val = PyFloat_AsDouble(value); + } + else if (PyLong_Check(value)) + { + val = PyLong_AsLong(value); + } + else + { + PyErr_SetString(PyExc_TypeError, "Value must be a floating point number."); + return -1; + } + if (member_ptr == 0) // x + self->data->box.setPosition(val, self->data->box.getPosition().y); + else if (member_ptr == 1) // y + self->data->box.setPosition(self->data->box.getPosition().x, val); + else if (member_ptr == 2) // w + self->data->box.setSize(sf::Vector2f(val, self->data->box.getSize().y)); + else if (member_ptr == 3) // h + self->data->box.setSize(sf::Vector2f(self->data->box.getSize().x, val)); + else if (member_ptr == 4) // center_x + self->data->center_x = val; + else if (member_ptr == 5) // center_y + self->data->center_y = val; + else if (member_ptr == 6) // zoom + self->data->zoom = val; + return 0; +} +// TODO (7DRL Day 2, item 5.) return Texture object +/* +static PyObject* PyUIGrid_get_texture(PyUIGridObject* self, void* closure) { + Py_INCREF(self->texture); + return self->texture; +} +*/ +//TODO: add this method to class scope; move implementation to .cpp file +static PyObject* PyUIGrid_get_texture(PyUIGridObject* self, void* closure) { + //return self->data->getTexture()->pyObject(); + PyTextureObject* obj = (PyTextureObject*)((&PyTextureType)->tp_alloc(&PyTextureType, 0)); + obj->data = self->data->getTexture(); + return (PyObject*)obj; +} + +//TODO: add this method to class scope; move implementation to .cpp file +static PyObject* PyUIGrid_at(PyUIGridObject* self, PyObject* o) +{ + int x, y; + if (!PyArg_ParseTuple(o, "ii", &x, &y)) { + PyErr_SetString(PyExc_TypeError, "UIGrid.at requires two integer arguments: (x, y)"); + return NULL; + } + if (x < 0 || x >= self->data->grid_x) { + PyErr_SetString(PyExc_ValueError, "x value out of range (0, Grid.grid_y)"); + return NULL; + } + if (y < 0 || y >= self->data->grid_y) { + PyErr_SetString(PyExc_ValueError, "y value out of range (0, Grid.grid_y)"); + return NULL; + } + + PyUIGridPointObject* obj = (PyUIGridPointObject*)((&PyUIGridPointType)->tp_alloc(&PyUIGridPointType, 0)); + //auto target = std::static_pointer_cast(target); + obj->data = &(self->data->points[x + self->data->grid_x * y]); + obj->grid = self->data; + return (PyObject*)obj; +} + +//TODO: add this static array to class scope; move implementation to .cpp file +static PyMethodDef PyUIGrid_methods[] = { + {"at", (PyCFunction)PyUIGrid_at, METH_O}, + {NULL, NULL, 0, NULL} +}; + +static PyObject* PyUIGrid_get_children(PyUIGridObject* self, void* closure); // forward declare + +static PyGetSetDef PyUIGrid_getsetters[] = { + + // TODO - refactor into get_vector_member with field identifier values `(void*)n` + {"grid_size", (getter)PyUIGrid_get_grid_size, NULL, "Grid dimensions (grid_x, grid_y)", NULL}, + {"position", (getter)PyUIGrid_get_position, (setter)PyUIGrid_set_position, "Position of the grid (x, y)", NULL}, + {"size", (getter)PyUIGrid_get_size, (setter)PyUIGrid_set_size, "Size of the grid (width, height)", NULL}, + {"center", (getter)PyUIGrid_get_center, (setter)PyUIGrid_set_center, "Grid coordinate at the center of the Grid's view (pan)", NULL}, + + {"entities", (getter)PyUIGrid_get_children, NULL, "EntityCollection of entities on this grid", NULL}, + + {"x", (getter)PyUIGrid_get_float_member, (setter)PyUIGrid_set_float_member, "top-left corner X-coordinate", (void*)0}, + {"y", (getter)PyUIGrid_get_float_member, (setter)PyUIGrid_set_float_member, "top-left corner Y-coordinate", (void*)1}, + {"w", (getter)PyUIGrid_get_float_member, (setter)PyUIGrid_set_float_member, "visible widget width", (void*)2}, + {"h", (getter)PyUIGrid_get_float_member, (setter)PyUIGrid_set_float_member, "visible widget height", (void*)3}, + {"center_x", (getter)PyUIGrid_get_float_member, (setter)PyUIGrid_set_float_member, "center of the view X-coordinate", (void*)4}, + {"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 */ +}; + + +/* // TODO standard pointer would need deleted, but I opted for a shared pointer. tp_dealloc currently not even defined in the PyTypeObject +static void PyUIGrid_dealloc(PyUIGridObject* self) { + delete self->data; // Clean up the allocated UIGrid object + Py_TYPE(self)->tp_free((PyObject*)self); +} +*/ + + static PyTypeObject PyUIGridType = { + //PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "mcrfpy.Grid", + .tp_basicsize = sizeof(PyUIGridObject), + .tp_itemsize = 0, + //.tp_dealloc = (destructor)[](PyObject* self) + //{ + // PyUIGridObject* obj = (PyUIGridObject*)self; + // obj->data.reset(); + // Py_TYPE(self)->tp_free(self); + //}, + //TODO - PyUIGrid REPR def: + // .tp_repr = (reprfunc)PyUIGrid_repr, + //.tp_hash = NULL, + //.tp_iter + //.tp_iternext + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_doc = PyDoc_STR("docstring"), + .tp_methods = PyUIGrid_methods, + //.tp_members = PyUIGrid_members, + .tp_getset = PyUIGrid_getsetters, + //.tp_base = NULL, + .tp_init = (initproc)PyUIGrid_init, + .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* + { + PyUIGridObject* self = (PyUIGridObject*)type->tp_alloc(type, 0); + if (self) self->data = std::make_shared(); + return (PyObject*)self; + } + }; + + typedef struct { + PyObject_HEAD + std::shared_ptr>> data; + int index; + int start_size; + } PyUIEntityCollectionIterObject; + + //TODO: add this method to class scope; move implementation to .cpp file; EntityCollection may need a larger refactor soon + static int PyUIEntityCollectionIter_init(PyUIEntityCollectionIterObject* self, PyObject* args, PyObject* kwds) + { + PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required."); + return -1; + } + + //TODO: add this method to class scope; move implementation to .cpp file; EntityCollection may need a larger refactor soon + static PyObject* PyUIEntityCollectionIter_next(PyUIEntityCollectionIterObject* self) + { + if (self->data->size() != self->start_size) + { + PyErr_SetString(PyExc_RuntimeError, "collection changed size during iteration"); + return NULL; + } + + if (self->index > self->start_size - 1) + { + PyErr_SetNone(PyExc_StopIteration); + return NULL; + } + self->index++; + auto vec = self->data.get(); + if (!vec) + { + PyErr_SetString(PyExc_RuntimeError, "the collection store returned a null pointer"); + return NULL; + } + // Advance list iterator since Entities are not stored in a vector (if this code even worked) + // vectors only: //auto target = (*vec)[self->index-1]; + //auto l_front = (*vec).begin(); + //std::advance(l_front, self->index-1); + + // TODO build PyObject* of the correct UIDrawable subclass to return + //return py_instance(target); + return NULL; + } + + //TODO: add this method to class scope; move implementation to .cpp file; EntityCollection may need a larger refactor soon + static PyObject* PyUIEntityCollectionIter_repr(PyUIEntityCollectionIterObject* self) + { + std::ostringstream ss; + if (!self->data) ss << ""; + else { + ss << "data->size() << " child objects, @ index " << self->index << ")>"; + } + std::string repr_str = ss.str(); + return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); + } + + static PyTypeObject PyUIEntityCollectionIterType = { + //PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "mcrfpy.UICollectionIter", + .tp_basicsize = sizeof(PyUIEntityCollectionIterObject), + .tp_itemsize = 0, + .tp_dealloc = (destructor)[](PyObject* self) + { + PyUIEntityCollectionIterObject* obj = (PyUIEntityCollectionIterObject*)self; + obj->data.reset(); + Py_TYPE(self)->tp_free(self); + }, + .tp_repr = (reprfunc)PyUIEntityCollectionIter_repr, + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_doc = PyDoc_STR("Iterator for a collection of UI objects"), + .tp_iternext = (iternextfunc)PyUIEntityCollectionIter_next, + //.tp_getset = PyUIEntityCollection_getset, + .tp_init = (initproc)PyUIEntityCollectionIter_init, // just raise an exception + .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* + { + PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required."); + return NULL; + } + }; + + + typedef struct { + PyObject_HEAD + std::shared_ptr>> data; + std::shared_ptr grid; + } PyUIEntityCollectionObject; + + //TODO: add this method to class scope; move implementation to .cpp file; EntityCollection may need a larger refactor soon + static Py_ssize_t PyUIEntityCollection_len(PyUIEntityCollectionObject* self) { + return self->data->size(); + } + + //TODO: add this method to class scope; move implementation to .cpp file; EntityCollection may need a larger refactor soon + static PyObject* PyUIEntityCollection_getitem(PyUIEntityCollectionObject* self, Py_ssize_t index) { + // build a Python version of item at self->data[index] + // Copy pasted:: + auto vec = self->data.get(); + if (!vec) + { + PyErr_SetString(PyExc_RuntimeError, "the collection store returned a null pointer"); + return NULL; + } + while (index < 0) index += self->data->size(); + if (index > self->data->size() - 1) + { + PyErr_SetString(PyExc_IndexError, "EntityCollection index out of range"); + return NULL; + } + auto l_begin = (*vec).begin(); + std::advance(l_begin, index); + auto target = *l_begin; //auto target = (*vec)[index]; + //RET_PY_INSTANCE(target); + // construct and return an entity object that points directly into the UIGrid's entity vector + PyUIEntityObject* o = (PyUIEntityObject*)((&PyUIEntityType)->tp_alloc(&PyUIEntityType, 0)); + auto p = std::static_pointer_cast(target); + o->data = p; + return (PyObject*)o; + return NULL; + + + } + + //TODO: add this static array to class scope; move implementation to .cpp file; EntityCollection may need a larger refactor soon + static PySequenceMethods PyUIEntityCollection_sqmethods = { + .sq_length = (lenfunc)PyUIEntityCollection_len, + .sq_item = (ssizeargfunc)PyUIEntityCollection_getitem, + //.sq_item_by_index = PyUIEntityCollection_getitem + //.sq_slice - return a subset of the iterable + //.sq_ass_item - called when `o[x] = y` is executed (x is any object type) + //.sq_ass_slice - cool; no thanks, for now + //.sq_contains - called when `x in o` is executed + //.sq_ass_item_by_index - called when `o[x] = y` is executed (x is explictly an integer) + }; + + //TODO: add this method to class scope; move implementation to .cpp file; EntityCollection may need a larger refactor soon + static PyObject* PyUIEntityCollection_append(PyUIEntityCollectionObject* self, PyObject* o) + { + // if not UIDrawable subclass, reject it + // self->data->push_back( c++ object inside o ); + + // this would be a great use case for .tp_base + if (!PyObject_IsInstance(o, (PyObject*)&PyUIEntityType)) + { + PyErr_SetString(PyExc_TypeError, "Only Entity objects can be added to EntityCollection"); + return NULL; + } + PyUIEntityObject* entity = (PyUIEntityObject*)o; + self->data->push_back(entity->data); + entity->data->grid = self->grid; + + Py_INCREF(Py_None); + return Py_None; + } + + //TODO: add this method to class scope; move implementation to .cpp file; EntityCollection may need a larger refactor soon + static PyObject* PyUIEntityCollection_remove(PyUIEntityCollectionObject* self, PyObject* o) + { + if (!PyLong_Check(o)) + { + PyErr_SetString(PyExc_TypeError, "UICollection.remove requires an integer index to remove"); + return NULL; + } + long index = PyLong_AsLong(o); + if (index >= self->data->size()) + { + PyErr_SetString(PyExc_ValueError, "Index out of range"); + return NULL; + } + else if (index < 0) + { + PyErr_SetString(PyExc_NotImplementedError, "reverse indexing is not implemented."); + return NULL; + } + + // release the shared pointer at correct part of the list + self->data->erase(std::next(self->data->begin(), index)); + Py_INCREF(Py_None); + return Py_None; + } + + //TODO: add this static array to class scope; move implementation to .cpp file; EntityCollection may need a larger refactor soon + static PyMethodDef PyUIEntityCollection_methods[] = { + {"append", (PyCFunction)PyUIEntityCollection_append, METH_O}, + //{"extend", (PyCFunction)PyUIEntityCollection_extend, METH_O}, // TODO + {"remove", (PyCFunction)PyUIEntityCollection_remove, METH_O}, + {NULL, NULL, 0, NULL} + }; + + //TODO: add this method to class scope; move implementation to .cpp file; EntityCollection may need a larger refactor soon + static PyObject* PyUIEntityCollection_repr(PyUIEntityCollectionObject* self) + { + std::ostringstream ss; + if (!self->data) ss << ""; + else { + ss << "data->size() << " child objects)>"; + } + std::string repr_str = ss.str(); + return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); + } + + //TODO: add this method to class scope; move implementation to .cpp file; EntityCollection may need a larger refactor soon + static int PyUIEntityCollection_init(PyUIEntityCollectionObject* self, PyObject* args, PyObject* kwds) + { + PyErr_SetString(PyExc_TypeError, "EntityCollection cannot be instantiated: a C++ data source is required."); + return -1; + } + + //TODO: add this method to class scope; move implementation to .cpp file; EntityCollection may need a larger refactor soon + static PyObject* PyUIEntityCollection_iter(PyUIEntityCollectionObject* self) + { + PyUIEntityCollectionIterObject* iterObj; + iterObj = (PyUIEntityCollectionIterObject*)PyUIEntityCollectionIterType.tp_alloc(&PyUIEntityCollectionIterType, 0); + if (iterObj == NULL) { + return NULL; // Failed to allocate memory for the iterator object + } + + iterObj->data = self->data; + iterObj->index = 0; + iterObj->start_size = self->data->size(); + + return (PyObject*)iterObj; + } + static PyTypeObject PyUIEntityCollectionType = { + //PyVarObject_/HEAD_INIT(NULL, 0) + .tp_name = "mcrfpy.EntityCollection", + .tp_basicsize = sizeof(PyUIEntityCollectionObject), + .tp_itemsize = 0, + .tp_dealloc = (destructor)[](PyObject* self) + { + PyUIEntityCollectionObject* obj = (PyUIEntityCollectionObject*)self; + obj->data.reset(); + Py_TYPE(self)->tp_free(self); + }, + .tp_repr = (reprfunc)PyUIEntityCollection_repr, + .tp_as_sequence = &PyUIEntityCollection_sqmethods, + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_doc = PyDoc_STR("Iterable, indexable collection of Entities"), + .tp_iter = (getiterfunc)PyUIEntityCollection_iter, + .tp_methods = PyUIEntityCollection_methods, // append, remove + //.tp_getset = PyUIEntityCollection_getset, + .tp_init = (initproc)PyUIEntityCollection_init, // just raise an exception + .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* + { + // Does PyUIEntityCollectionType need __new__ if it's not supposed to be instantiable by the user? + // Should I just raise an exception? Or is the uninitialized shared_ptr enough of a blocker? + PyErr_SetString(PyExc_TypeError, "EntityCollection cannot be instantiated: a C++ data source is required."); + return NULL; + } + }; + + //TODO: add this method to class scope; move implementation to .cpp file + // Grid's get_children def must follow the EntityCollection def + static PyObject* PyUIGrid_get_children(PyUIGridObject* self, void* closure) + { + // create PyUICollection instance pointing to self->data->children + PyUIEntityCollectionObject* o = (PyUIEntityCollectionObject*)PyUIEntityCollectionType.tp_alloc(&PyUIEntityCollectionType, 0); + if (o) { + o->data = self->data->entities; // todone. / BUGFIX - entities isn't a shared pointer on UIGrid, what to do? -- I made it a sp>> + o->grid = self->data; + } + return (PyObject*)o; + } + + +} diff --git a/src/UIGridPoint.cpp b/src/UIGridPoint.cpp new file mode 100644 index 0000000..18fffc7 --- /dev/null +++ b/src/UIGridPoint.cpp @@ -0,0 +1,6 @@ +#include "UIGridPoint.h" + +UIGridPoint::UIGridPoint() +: color(1.0f, 1.0f, 1.0f), color_overlay(0.0f, 0.0f, 0.0f), walkable(false), transparent(false), + tilesprite(-1), tile_overlay(-1), uisprite(-1) +{} diff --git a/src/UIGridPoint.h b/src/UIGridPoint.h new file mode 100644 index 0000000..e611ff7 --- /dev/null +++ b/src/UIGridPoint.h @@ -0,0 +1,212 @@ +#pragma once +#include "Common.h" +#include "Python.h" +#include "structmember.h" +#include "IndexTexture.h" +#include "Resources.h" +#include + +#include "PyCallable.h" +#include "PyTexture.h" +#include "PyColor.h" +#include "PyVector.h" +#include "PyFont.h" + +// UIGridPoint - revised grid data for each point +class UIGridPoint +{ +public: + sf::Color color, color_overlay; + bool walkable, transparent; + int tilesprite, tile_overlay, uisprite; + UIGridPoint(); +}; + +// UIGridPointState - entity-specific info for each cell +class UIGridPointState +{ +public: + bool visible, discovered; +}; + +typedef struct { + PyObject_HEAD + UIGridPoint* data; + std::shared_ptr grid; +} PyUIGridPointObject; + +typedef struct { + PyObject_HEAD + UIGridPointState* data; + std::shared_ptr grid; + std::shared_ptr entity; +} PyUIGridPointStateObject; + +namespace mcrfpydef { +// TODO: question: are sfColor_to_PyObject and PyObject_to_sfColor duplicitive? How does UIFrame get/set colors? + +//TODO: add this method to class scope; move implementation to .cpp file; reconsider for moving to "UIBase.h/.cpp" +// Utility function to convert sf::Color to PyObject* +static PyObject* sfColor_to_PyObject(sf::Color color) { + return Py_BuildValue("(iiii)", color.r, color.g, color.b, color.a); +} + +//TODO: add this method to class scope; move implementation to .cpp file; reconsider for moving to "UIBase.h/.cpp" +// Utility function to convert PyObject* to sf::Color +static sf::Color PyObject_to_sfColor(PyObject* obj) { + int r, g, b, a = 255; // Default alpha to fully opaque if not specified + if (!PyArg_ParseTuple(obj, "iii|i", &r, &g, &b, &a)) { + return sf::Color(); // Return default color on parse error + } + return sf::Color(r, g, b, a); +} + +//TODO: add this method to class scope; move implementation to .cpp file +static PyObject* PyUIGridPoint_get_color(PyUIGridPointObject* self, void* closure) { + if (reinterpret_cast(closure) == 0) { // color + return sfColor_to_PyObject(self->data->color); + } else { // color_overlay + return sfColor_to_PyObject(self->data->color_overlay); + } +} + +//TODO: add this method to class scope; move implementation to .cpp file +static int PyUIGridPoint_set_color(PyUIGridPointObject* self, PyObject* value, void* closure) { + sf::Color color = PyObject_to_sfColor(value); + if (reinterpret_cast(closure) == 0) { // color + self->data->color = color; + } else { // color_overlay + self->data->color_overlay = color; + } + return 0; +} + +//TODO: add this method to class scope; move implementation to .cpp file +static PyObject* PyUIGridPoint_get_bool_member(PyUIGridPointObject* self, void* closure) { + if (reinterpret_cast(closure) == 0) { // walkable + return PyBool_FromLong(self->data->walkable); + } else { // transparent + return PyBool_FromLong(self->data->transparent); + } +} + +//TODO: add this method to class scope; move implementation to .cpp file +static int PyUIGridPoint_set_bool_member(PyUIGridPointObject* self, PyObject* value, void* closure) { + if (value == Py_True) { + if (reinterpret_cast(closure) == 0) { // walkable + self->data->walkable = true; + } else { // transparent + self->data->transparent = true; + } + } else if (value == Py_False) { + if (reinterpret_cast(closure) == 0) { // walkable + self->data->walkable = false; + } else { // transparent + self->data->transparent = false; + } + } else { + PyErr_SetString(PyExc_ValueError, "Expected a boolean value"); + return -1; + } + return 0; +} + +//TODO: add this method to class scope; move implementation to .cpp file +static PyObject* PyUIGridPoint_get_int_member(PyUIGridPointObject* self, void* closure) { + switch(reinterpret_cast(closure)) { + case 0: return PyLong_FromLong(self->data->tilesprite); + case 1: return PyLong_FromLong(self->data->tile_overlay); + case 2: return PyLong_FromLong(self->data->uisprite); + default: PyErr_SetString(PyExc_RuntimeError, "Invalid closure"); return nullptr; + } +} + +//TODO: add this method to class scope; move implementation to .cpp file +static int PyUIGridPoint_set_int_member(PyUIGridPointObject* self, PyObject* value, void* closure) { + long val = PyLong_AsLong(value); + if (PyErr_Occurred()) return -1; + + switch(reinterpret_cast(closure)) { + case 0: self->data->tilesprite = val; break; + case 1: self->data->tile_overlay = val; break; + case 2: self->data->uisprite = val; break; + default: PyErr_SetString(PyExc_RuntimeError, "Invalid closure"); return -1; + } + return 0; +} + +//TODO: add this static array to class scope; move implementation to .cpp file +static PyGetSetDef PyUIGridPoint_getsetters[] = { + {"color", (getter)PyUIGridPoint_get_color, (setter)PyUIGridPoint_set_color, "GridPoint color", (void*)0}, + {"color_overlay", (getter)PyUIGridPoint_get_color, (setter)PyUIGridPoint_set_color, "GridPoint color overlay", (void*)1}, + {"walkable", (getter)PyUIGridPoint_get_bool_member, (setter)PyUIGridPoint_set_bool_member, "Is the GridPoint walkable", (void*)0}, + {"transparent", (getter)PyUIGridPoint_get_bool_member, (setter)PyUIGridPoint_set_bool_member, "Is the GridPoint transparent", (void*)1}, + {"tilesprite", (getter)PyUIGridPoint_get_int_member, (setter)PyUIGridPoint_set_int_member, "Tile sprite index", (void*)0}, + {"tile_overlay", (getter)PyUIGridPoint_get_int_member, (setter)PyUIGridPoint_set_int_member, "Tile overlay sprite index", (void*)1}, + {"uisprite", (getter)PyUIGridPoint_get_int_member, (setter)PyUIGridPoint_set_int_member, "UI sprite index", (void*)2}, + {NULL} /* Sentinel */ +}; + +static PyTypeObject PyUIGridPointType = { + //PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "mcrfpy.GridPoint", + .tp_basicsize = sizeof(PyUIGridPointObject), + .tp_itemsize = 0, + // Methods omitted for brevity + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_doc = "UIGridPoint objects", + .tp_getset = PyUIGridPoint_getsetters, + //.tp_init = (initproc)PyUIGridPoint_init, // TODO Define the init function + .tp_new = PyType_GenericNew, +}; + +//TODO: add this method to class scope; move implementation to .cpp file +static PyObject* PyUIGridPointState_get_bool_member(PyUIGridPointStateObject* self, void* closure) { + if (reinterpret_cast(closure) == 0) { // visible + return PyBool_FromLong(self->data->visible); + } else { // discovered + return PyBool_FromLong(self->data->discovered); + } +} + +//TODO: add this method to class scope; move implementation to .cpp file +static int PyUIGridPointState_set_bool_member(PyUIGridPointStateObject* self, PyObject* value, void* closure) { + if (!PyBool_Check(value)) { + PyErr_SetString(PyExc_TypeError, "Value must be a boolean"); + return -1; + } + + int truthValue = PyObject_IsTrue(value); + if (truthValue < 0) { + return -1; // PyObject_IsTrue returns -1 on error + } + + if (reinterpret_cast(closure) == 0) { // visible + self->data->visible = truthValue; + } else { // discovered + self->data->discovered = truthValue; + } + + return 0; +} + +//TODO: add this static array to class scope; move implementation to .cpp file +static PyGetSetDef PyUIGridPointState_getsetters[] = { + {"visible", (getter)PyUIGridPointState_get_bool_member, (setter)PyUIGridPointState_set_bool_member, "Is the GridPointState visible", (void*)0}, + {"discovered", (getter)PyUIGridPointState_get_bool_member, (setter)PyUIGridPointState_set_bool_member, "Has the GridPointState been discovered", (void*)1}, + {NULL} /* Sentinel */ +}; + +static PyTypeObject PyUIGridPointStateType = { + //PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "mcrfpy.GridPointState", + .tp_basicsize = sizeof(PyUIGridPointStateObject), + .tp_itemsize = 0, + // Methods omitted for brevity + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_doc = "UIGridPointState objects", // TODO: Add PyUIGridPointState tp_init + .tp_getset = PyUIGridPointState_getsetters, + .tp_new = PyType_GenericNew, +}; + +} diff --git a/src/UISprite.cpp b/src/UISprite.cpp new file mode 100644 index 0000000..1f06be1 --- /dev/null +++ b/src/UISprite.cpp @@ -0,0 +1,81 @@ +#include "UISprite.h" + +UIDrawable* UISprite::click_at(sf::Vector2f point) +{ + if (click_callable) + { + if(sprite.getGlobalBounds().contains(point)) return this; + } + return NULL; +} + +UISprite::UISprite() {} + +UISprite::UISprite(std::shared_ptr _ptex, int _sprite_index, sf::Vector2f _pos, float _scale) +: ptex(_ptex), sprite_index(_sprite_index) +{ + sprite = ptex->sprite(sprite_index, _pos, sf::Vector2f(_scale, _scale)); +} + +void UISprite::render(sf::Vector2f offset) +{ + sprite.move(offset); + Resources::game->getWindow().draw(sprite); + sprite.move(-offset); +} + +void UISprite::render(sf::Vector2f offset, sf::RenderTexture& target) +{ + sprite.move(offset); + target.draw(sprite); + sprite.move(-offset); +} + +void UISprite::setPosition(sf::Vector2f pos) +{ + sprite.setPosition(pos); +} + +void UISprite::setScale(sf::Vector2f s) +{ + sprite.setScale(s); +} + +void UISprite::setTexture(std::shared_ptr _ptex, int _sprite_index) +{ + ptex = _ptex; + if (_sprite_index != -1) // if you are changing textures, there's a good chance you need a new index too + sprite_index = _sprite_index; + sprite = ptex->sprite(sprite_index, sprite.getPosition(), sprite.getScale()); +} + +void UISprite::setSpriteIndex(int _sprite_index) +{ + sprite_index = _sprite_index; + sprite = ptex->sprite(sprite_index, sprite.getPosition(), sprite.getScale()); +} + +sf::Vector2f UISprite::getScale() +{ + return sprite.getScale(); +} + +sf::Vector2f UISprite::getPosition() +{ + return sprite.getPosition(); +} + +std::shared_ptr UISprite::getTexture() +{ + return ptex; +} + +int UISprite::getSpriteIndex() +{ + return sprite_index; +} + +PyObjectsEnum UISprite::derived_type() +{ + return PyObjectsEnum::UISPRITE; +} diff --git a/src/UISprite.h b/src/UISprite.h new file mode 100644 index 0000000..4397c2c --- /dev/null +++ b/src/UISprite.h @@ -0,0 +1,223 @@ +#pragma once +#include "Common.h" +#include "Python.h" +#include "structmember.h" +#include "IndexTexture.h" +#include "Resources.h" +#include + +#include "PyCallable.h" +#include "PyTexture.h" +#include "PyColor.h" +#include "PyVector.h" +#include "PyFont.h" +#include "UIDrawable.h" + +class UISprite: public UIDrawable +{ +private: + int sprite_index; + sf::Sprite sprite; +protected: + std::shared_ptr ptex; +public: + UISprite(); + UISprite(std::shared_ptr, int, sf::Vector2f, float); + void update(); + void render(sf::Vector2f) override final; + virtual UIDrawable* click_at(sf::Vector2f point) override final; + + void render(sf::Vector2f, sf::RenderTexture&); + + void setPosition(sf::Vector2f); + sf::Vector2f getPosition(); + void setScale(sf::Vector2f); + sf::Vector2f getScale(); + void setSpriteIndex(int); + int getSpriteIndex(); + + void setTexture(std::shared_ptr _ptex, int _sprite_index=-1); + std::shared_ptr getTexture(); + + PyObjectsEnum derived_type() override final; +}; + +typedef struct { + PyObject_HEAD + std::shared_ptr data; +} PyUISpriteObject; + +namespace mcrfpydef { + //TODO: add this method to class scope; move implementation to .cpp file + static PyObject* PyUISprite_get_float_member(PyUISpriteObject* self, void* closure) + { + auto member_ptr = reinterpret_cast(closure); + if (member_ptr == 0) + return PyFloat_FromDouble(self->data->getPosition().x); + else if (member_ptr == 1) + return PyFloat_FromDouble(self->data->getPosition().y); + else if (member_ptr == 2) + return PyFloat_FromDouble(self->data->getScale().x); // scale X and Y are identical, presently + else + { + PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); + return nullptr; + } + } + + //TODO: add this method to class scope; move implementation to .cpp file + static int PyUISprite_set_float_member(PyUISpriteObject* self, PyObject* value, void* closure) + { + float val; + auto member_ptr = reinterpret_cast(closure); + if (PyFloat_Check(value)) + { + val = PyFloat_AsDouble(value); + } + else if (PyLong_Check(value)) + { + val = PyLong_AsLong(value); + } + else + { + PyErr_SetString(PyExc_TypeError, "Value must be a floating point number."); + return -1; + } + if (member_ptr == 0) //x + self->data->setPosition(sf::Vector2f(val, self->data->getPosition().y)); + else if (member_ptr == 1) //y + self->data->setPosition(sf::Vector2f(self->data->getPosition().x, val)); + else if (member_ptr == 2) // scale + self->data->setScale(sf::Vector2f(val, val)); + return 0; + } + + //TODO: add this method to class scope; move implementation to .cpp file + static PyObject* PyUISprite_get_int_member(PyUISpriteObject* self, void* closure) + { + auto member_ptr = reinterpret_cast(closure); + if (true) {} + else + { + PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); + return nullptr; + } + + return PyLong_FromDouble(self->data->getSpriteIndex()); + } + + //TODO: add this method to class scope; move implementation to .cpp file + static int PyUISprite_set_int_member(PyUISpriteObject* self, PyObject* value, void* closure) + { + int val; + auto member_ptr = reinterpret_cast(closure); + if (PyLong_Check(value)) + { + val = PyLong_AsLong(value); + } + else + { + PyErr_SetString(PyExc_TypeError, "Value must be an integer."); + return -1; + } + self->data->setSpriteIndex(val); + return 0; + } + + //TODO: add this method to class scope; move implementation to .cpp file + static PyObject* PyUISprite_get_texture(PyUISpriteObject* self, void* closure) + { + return self->data->getTexture()->pyObject(); + } + + //TODO: add this method to class scope; move implementation to .cpp file + static int PyUISprite_set_texture(PyUISpriteObject* self, PyObject* value, void* closure) + { + return -1; + } + + //TODO: add this method to static array scope; move implementation to .cpp file + static PyGetSetDef PyUISprite_getsetters[] = { + {"x", (getter)PyUISprite_get_float_member, (setter)PyUISprite_set_float_member, "X coordinate of top-left corner", (void*)0}, + {"y", (getter)PyUISprite_get_float_member, (setter)PyUISprite_set_float_member, "Y coordinate of top-left corner", (void*)1}, + {"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} + }; + + //TODO: add this method to class scope; move implementation to .cpp file + static PyObject* PyUISprite_repr(PyUISpriteObject* self) + { + std::ostringstream ss; + if (!self->data) ss << ""; + else { + //auto sprite = self->data->sprite; + ss << ""; + } + std::string repr_str = ss.str(); + return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); + } + + //TODO: add this method to class scope; move implementation to .cpp file + static int PyUISprite_init(PyUISpriteObject* self, PyObject* args, PyObject* kwds) + { + //std::cout << "Init called\n"; + static const char* keywords[] = { "x", "y", "texture", "sprite_index", "scale", nullptr }; + float x = 0.0f, y = 0.0f, scale = 1.0f; + int sprite_index; + PyObject* texture; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ffOif", + const_cast(keywords), &x, &y, &texture, &sprite_index, &scale)) + { + return -1; + } + + // check types for texture + if (texture != NULL && !PyObject_IsInstance(texture, (PyObject*)&PyTextureType)){ + PyErr_SetString(PyExc_TypeError, "texture must be a mcrfpy.Texture instance"); + return -1; + } + auto pytexture = (PyTextureObject*)texture; + self->data = std::make_shared(pytexture->data, sprite_index, sf::Vector2f(x, y), scale); + self->data->setPosition(sf::Vector2f(x, y)); + + return 0; + } + + static PyTypeObject PyUISpriteType = { + //PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "mcrfpy.Sprite", + .tp_basicsize = sizeof(PyUISpriteObject), + .tp_itemsize = 0, + .tp_dealloc = (destructor)[](PyObject* self) + { + PyUISpriteObject* obj = (PyUISpriteObject*)self; + // release reference to font object + //if (obj->texture) Py_DECREF(obj->texture); + obj->data.reset(); + Py_TYPE(self)->tp_free(self); + }, + .tp_repr = (reprfunc)PyUISprite_repr, + //.tp_hash = NULL, + //.tp_iter + //.tp_iternext + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_doc = PyDoc_STR("docstring"), + //.tp_methods = PyUIFrame_methods, + //.tp_members = PyUIFrame_members, + .tp_getset = PyUISprite_getsetters, + //.tp_base = NULL, + .tp_init = (initproc)PyUISprite_init, + .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* + { + PyUISpriteObject* self = (PyUISpriteObject*)type->tp_alloc(type, 0); + //if (self) self->data = std::make_shared(); + return (PyObject*)self; + } + }; +} From c0201d989acb2dbd25ae80f6642c4b849db0c0b5 Mon Sep 17 00:00:00 2001 From: John McCardle Date: Mon, 8 Apr 2024 22:55:00 -0400 Subject: [PATCH 02/14] additional unsaved changes --- src/UIEntity.h | 141 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) diff --git a/src/UIEntity.h b/src/UIEntity.h index a99369b..edf06b1 100644 --- a/src/UIEntity.h +++ b/src/UIEntity.h @@ -38,3 +38,144 @@ typedef struct { std::shared_ptr data; //PyObject* texture; } PyUIEntityObject; + +namespace mcrfpydef { +//TODO: add this method to class scope; move implementation to .cpp file; reconsider for moving to "UIBase.h/.cpp" +// TODO: sf::Vector2f convenience functions here might benefit from a PyVectorObject much like PyColorObject +// Utility function to convert sf::Vector2f to PyObject* +static PyObject* sfVector2f_to_PyObject(sf::Vector2f vector) { + return Py_BuildValue("(ff)", vector.x, vector.y); +} + +//TODO: add this method to class scope; move implementation to .cpp file; reconsider for moving to "UIBase.h/.cpp" +// Utility function to convert PyObject* to sf::Vector2f +static sf::Vector2f PyObject_to_sfVector2f(PyObject* obj) { + float x, y; + if (!PyArg_ParseTuple(obj, "ff", &x, &y)) { + return sf::Vector2f(); // TODO / reconsider this default: Return default vector on parse error + } + return sf::Vector2f(x, y); +} + +//TODO: add this method to class scope; move implementation to .cpp file +// Utility function to convert UIGridPointState to PyObject* +static PyObject* UIGridPointState_to_PyObject(const UIGridPointState& state) { + PyObject* obj = PyObject_New(PyObject, &PyUIGridPointStateType); + if (!obj) return PyErr_NoMemory(); + + // Assuming PyUIGridPointStateObject structure has a UIGridPointState* member called 'data' + //((PyUIGridPointStateObject*)obj)->data = new UIGridPointState(state); // Copy constructor // wontimplement / feat - don't use new, get shared_ptr working + + return obj; +} +//TODO: add this method to class scope; move implementation to .cpp file +// Function to convert std::vector to a Python list TODO need a PyUICollection style iterable +static PyObject* UIGridPointStateVector_to_PyList(const std::vector& vec) { + PyObject* list = PyList_New(vec.size()); + if (!list) return PyErr_NoMemory(); + + for (size_t i = 0; i < vec.size(); ++i) { + PyObject* obj = UIGridPointState_to_PyObject(vec[i]); + if (!obj) { // Cleanup on failure + Py_DECREF(list); + return NULL; + } + PyList_SET_ITEM(list, i, obj); // This steals a reference to obj + } + + return list; +} + +//TODO: add this method to class scope; move implementation to .cpp file +static PyObject* PyUIEntity_get_position(PyUIEntityObject* self, void* closure) { + return sfVector2f_to_PyObject(self->data->position); +} + +//TODO: add this method to class scope; move implementation to .cpp file +static int PyUIEntity_set_position(PyUIEntityObject* self, PyObject* value, void* closure) { + self->data->position = PyObject_to_sfVector2f(value); + return 0; +} + +//TODO: add this method to class scope; move implementation to .cpp file +static PyObject* PyUIEntity_get_gridstate(PyUIEntityObject* self, void* closure) { + // Assuming a function to convert std::vector to PyObject* list + return UIGridPointStateVector_to_PyList(self->data->gridstate); +} + +//TODO: add this method to class scope; move implementation to .cpp file +static PyObject* PyUIEntity_get_spritenumber(PyUIEntityObject* self, void* closure) { + return PyLong_FromDouble(self->data->sprite.getSpriteIndex()); +} + +//TODO: add this method to class scope; move implementation to .cpp file +static int PyUIEntity_set_spritenumber(PyUIEntityObject* self, PyObject* value, void* closure) { + int val; + if (PyLong_Check(value)) + val = PyLong_AsLong(value); + else + { + PyErr_SetString(PyExc_TypeError, "Value must be an integer."); + return -1; + } + //self->data->sprite.sprite_index = val; + self->data->sprite.setSpriteIndex(val); // todone - I don't like ".sprite.sprite" in this stack of UIEntity.UISprite.sf::Sprite + return 0; +} + +//TODO: add this method to class scope; move implementation to .cpp file +static PyObject* PyUIEntity_at(PyUIEntityObject* self, PyObject* o) +{ + int x, y; + if (!PyArg_ParseTuple(o, "ii", &x, &y)) { + PyErr_SetString(PyExc_TypeError, "UIEntity.at requires two integer arguments: (x, y)"); + return NULL; + } + + if (self->data->grid == NULL) { + PyErr_SetString(PyExc_ValueError, "Entity cannot access surroundings because it is not associated with a grid"); + return NULL; + } + + PyUIGridPointStateObject* obj = (PyUIGridPointStateObject*)((&PyUIGridPointStateType)->tp_alloc(&PyUIGridPointStateType, 0)); + //auto target = std::static_pointer_cast(target); + obj->data = &(self->data->gridstate[y + self->data->grid->grid_x * x]); + obj->grid = self->data->grid; + obj->entity = self->data; + return (PyObject*)obj; +} + +//TODO: add this static array to class scope; move implementation to .cpp file +static PyMethodDef PyUIEntity_methods[] = { + {"at", (PyCFunction)PyUIEntity_at, METH_O}, + {NULL, NULL, 0, NULL} +}; + +//TODO: add this static array to class scope; move implementation to .cpp file +// Define getters and setters +static PyGetSetDef PyUIEntity_getsetters[] = { + {"position", (getter)PyUIEntity_get_position, (setter)PyUIEntity_set_position, "Entity position", NULL}, + {"gridstate", (getter)PyUIEntity_get_gridstate, NULL, "Grid point states for the entity", NULL}, + {"sprite_number", (getter)PyUIEntity_get_spritenumber, (setter)PyUIEntity_set_spritenumber, "Sprite number (index) on the texture on the display", NULL}, + {NULL} /* Sentinel */ +}; + +//TODO: add this method to class scope; forward declaration not required after .h/.cpp split +static int PyUIEntity_init(PyUIEntityObject*, PyObject*, PyObject*); // forward declare + + +// Define the PyTypeObject for UIEntity +static PyTypeObject PyUIEntityType = { + //PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "mcrfpy.Entity", + .tp_basicsize = sizeof(PyUIEntityObject), + .tp_itemsize = 0, + // Methods omitted for brevity + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_doc = "UIEntity objects", + .tp_methods = PyUIEntity_methods, + .tp_getset = PyUIEntity_getsetters, + .tp_init = (initproc)PyUIEntity_init, + .tp_new = PyType_GenericNew, +}; +} From aa7553a81807b017c64fbf860e54cb48fc136243 Mon Sep 17 00:00:00 2001 From: John McCardle Date: Tue, 9 Apr 2024 22:41:20 -0400 Subject: [PATCH 03/14] PyTexture clean up scribbles and experiments --- src/PyTexture.cpp | 26 +------------------------- 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/src/PyTexture.cpp b/src/PyTexture.cpp index 8f8d4df..eb4bfad 100644 --- a/src/PyTexture.cpp +++ b/src/PyTexture.cpp @@ -28,36 +28,12 @@ sf::Sprite PyTexture::sprite(int index, sf::Vector2f pos, sf::Vector2f s) PyObject* PyTexture::pyObject() { - // method 1: works but with type weirdness - //PyObject* obj = PyType_GenericAlloc(&mcrfpydef::PyTextureType, 0); - //Py_SET_TYPE(obj, &mcrfpydef::PyTextureType); - - // method 2: does not work (segfault on use of the mcrfpy.Texture object) - //PyObject* obj = PyTexture::pynew(&mcrfpydef::PyTextureType, Py_None, Py_None); - - // method 3: does not work (segfault on use of the mcrfpy.Texture object) std::cout << "Find type" << std::endl; auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Texture"); - //auto type = obj->ob_type; - //auto type = &mcrfpydef::PyTextureType; - //std::cout << "assigned value 0x" << std::hex << reinterpret_cast(type) << std::endl; - //std::cout << "Found PyTextureType: " << PyUnicode_AsUTF8(PyObject_Repr((PyObject*)type)) << std::endl; - //std::cout << "PyTextureType metatype: " << PyUnicode_AsUTF8(PyObject_Repr((PyObject_Type((PyObject*)type)))) << std::endl; - //std::cout << "tp_alloc: 0x" << std::hex << reinterpret_cast (type->tp_alloc) << std::endl << - // "tp_new: 0x" << std::hex << reinterpret_cast(type->tp_new) << std::endl; - //PyObject* obj = ((PyTypeObject*)type)->tp_new((PyTypeObject*)type, Py_None, Py_None); - PyObject* obj = PyTexture::pynew(type, Py_None, Py_None); - //Py_SET_TYPE(obj, type); + PyObject* obj = PyTexture::pynew(type, Py_None, Py_None); - // method 4: call the type object? - - std::cout << "Instantiated" << std::endl; - //Py_SET_TYPE(obj, &mcrfpydef::PyTextureType); - - //PyObject_CallFunction(&mcrfpydef::PyTextureType, try { ((PyTextureObject*)obj)->data = shared_from_this(); - std::cout << "Sideloaded texture: " << PyUnicode_AsUTF8(PyObject_Repr(obj)) << std::endl; } catch (std::bad_weak_ptr& e) { From 1b6e2a709bfa3adc38183e30f1cc999efd7ef7c9 Mon Sep 17 00:00:00 2001 From: John McCardle Date: Tue, 9 Apr 2024 22:42:02 -0400 Subject: [PATCH 04/14] Still not quite compiling; as predicted, a lot of interdependency and definition order bugs to untangle --- src/UIBase.h | 32 +++++++++++++++++++++ src/UICaption.cpp | 1 + src/UICaption.h | 14 +++++---- src/UIDrawable.cpp | 65 +++++++++++++++++++++++++++++++++++++++++ src/UIDrawable.h | 15 +++++++--- src/UIEntity.cpp | 72 ++++++++++++++++++++++++++++++++++++++++++++++ src/UIEntity.h | 24 +++++++++------- src/UIFrame.cpp | 10 +++++++ src/UIFrame.h | 21 ++++++++------ src/UIGrid.h | 13 +++++---- src/UIGridPoint.h | 2 ++ src/UISprite.h | 10 +++---- 12 files changed, 240 insertions(+), 39 deletions(-) create mode 100644 src/UIBase.h diff --git a/src/UIBase.h b/src/UIBase.h new file mode 100644 index 0000000..70a5872 --- /dev/null +++ b/src/UIBase.h @@ -0,0 +1,32 @@ +#pragma once + +class UIEntity; +typedef struct { + PyObject_HEAD + std::shared_ptr data; +} PyUIEntityObject; + +class UIFrame; +typedef struct { + PyObject_HEAD + std::shared_ptr data; +} PyUIFrameObject; + +class UICaption; +typedef struct { + PyObject_HEAD + std::shared_ptr data; + PyObject* font; +} PyUICaptionObject; + +class UIGrid; +typedef struct { + PyObject_HEAD + std::shared_ptr data; +} PyUIGridObject; + +class UISprite; +typedef struct { + PyObject_HEAD + std::shared_ptr data; +} PyUISpriteObject; diff --git a/src/UICaption.cpp b/src/UICaption.cpp index aa53a8a..68f74ab 100644 --- a/src/UICaption.cpp +++ b/src/UICaption.cpp @@ -1,4 +1,5 @@ #include "UICaption.h" +#include "GameEngine.h" UIDrawable* UICaption::click_at(sf::Vector2f point) { diff --git a/src/UICaption.h b/src/UICaption.h index ee37e1c..f403813 100644 --- a/src/UICaption.h +++ b/src/UICaption.h @@ -12,6 +12,7 @@ #include "PyVector.h" #include "PyFont.h" #include "UIDrawable.h" +#include "UIBase.h" class UICaption: public UIDrawable { @@ -22,11 +23,12 @@ public: virtual UIDrawable* click_at(sf::Vector2f point) override final; }; -typedef struct { - PyObject_HEAD - std::shared_ptr data; - PyObject* font; -} PyUICaptionObject; +//class UICaption; +//typedef struct { +// PyObject_HEAD +// std::shared_ptr data; +// PyObject* font; +//} PyUICaptionObject; namespace mcrfpydef { //TODO: add this method to class scope; move implementation to .cpp file @@ -205,7 +207,7 @@ namespace mcrfpydef { //{"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}, + {"click", (getter)UIDrawable::get_click, (setter)UIDrawable::set_click, "Object called with (x, y, button) when clicked", (void*)PyObjectsEnum::UICAPTION}, {NULL} }; diff --git a/src/UIDrawable.cpp b/src/UIDrawable.cpp index 7cd1178..cd898c4 100644 --- a/src/UIDrawable.cpp +++ b/src/UIDrawable.cpp @@ -1,4 +1,8 @@ #include "UIDrawable.h" +#include "UIFrame.h" +#include "UICaption.h" +#include "UISprite.h" +#include "UIGrid.h" UIDrawable::UIDrawable() { click_callable = NULL; } @@ -11,3 +15,64 @@ void UIDrawable::render() { render(sf::Vector2f()); } + +PyObject* UIDrawable::get_click(PyObject* 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->borrow(); + break; + case PyObjectsEnum::UICAPTION: + ptr = ((PyUICaptionObject*)self)->data->click_callable->borrow(); + break; + case PyObjectsEnum::UISPRITE: + ptr = ((PyUISpriteObject*)self)->data->click_callable->borrow(); + break; + case PyObjectsEnum::UIGRID: + ptr = ((PyUIGridObject*)self)->data->click_callable->borrow(); + 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; +} + +int UIDrawable::set_click(PyObject* 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; +} + + diff --git a/src/UIDrawable.h b/src/UIDrawable.h index 5fb01e1..640437f 100644 --- a/src/UIDrawable.h +++ b/src/UIDrawable.h @@ -12,6 +12,8 @@ #include "PyVector.h" #include "PyFont.h" +#include "Resources.h" + class UIFrame; class UICaption; class UISprite; class UIEntity; class UIGrid; enum PyObjectsEnum : int @@ -36,6 +38,9 @@ public: void click_unregister(); UIDrawable(); + + static PyObject* get_click(PyObject* self, void* closure); + static int set_click(PyObject* self, PyObject* value, void* closure); }; typedef struct { @@ -99,7 +104,8 @@ switch (target->derived_type()) \ // end macro definition //TODO: add this method to class scope; move implementation to .cpp file -static PyObject* PyUIDrawable_get_click(PyUIGridObject* self, void* closure) { +/* +static PyObject* PyUIDrawable_get_click(PyObject* self, void* closure) { PyObjectsEnum objtype = static_cast(reinterpret_cast(closure)); // trust me bro, it's an Enum PyObject* ptr; @@ -125,10 +131,11 @@ static PyObject* PyUIDrawable_get_click(PyUIGridObject* self, void* closure) { return ptr; else return Py_None; -} +}*/ //TODO: add this method to class scope; move implementation to .cpp file -static int PyUIDrawable_set_click(PyUIGridObject* self, PyObject* value, void* closure) { +/* +static int PyUIDrawable_set_click(PyObject* self, PyObject* value, void* closure) { PyObjectsEnum objtype = static_cast(reinterpret_cast(closure)); // trust me bro, it's an Enum UIDrawable* target; switch (objtype) @@ -158,5 +165,5 @@ static int PyUIDrawable_set_click(PyUIGridObject* self, PyObject* value, void* c } return 0; } - +*/ } diff --git a/src/UIEntity.cpp b/src/UIEntity.cpp index 96738d7..9e21eaf 100644 --- a/src/UIEntity.cpp +++ b/src/UIEntity.cpp @@ -7,3 +7,75 @@ UIEntity::UIEntity(UIGrid& grid) { } +PyObject* UIEntity::at(PyUIEntityObject* self, PyObject* o) { + int x, y; + if (!PyArg_ParseTuple(o, "ii", &x, &y)) { + PyErr_SetString(PyExc_TypeError, "UIEntity.at requires two integer arguments: (x, y)"); + return NULL; + } + + if (self->data->grid == NULL) { + PyErr_SetString(PyExc_ValueError, "Entity cannot access surroundings because it is not associated with a grid"); + return NULL; + } + + PyUIGridPointStateObject* obj = (PyUIGridPointStateObject*)((&PyUIGridPointStateType)->tp_alloc(&PyUIGridPointStateType, 0)); + //auto target = std::static_pointer_cast(target); + obj->data = &(self->data->gridstate[y + self->data->grid->grid_x * x]); + obj->grid = self->data->grid; + obj->entity = self->data; + return (PyObject*)obj; + +} + +int PyUIEntity::init(PyUIEntityObject* self, PyObject* args, PyObject* kwds) { + static const char* keywords[] = { "x", "y", "texture", "sprite_index", "grid", nullptr }; + float x = 0.0f, y = 0.0f, scale = 1.0f; + int sprite_index = -1; + PyObject* texture = NULL; + PyObject* grid = NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "ffOi|O", + const_cast(keywords), &x, &y, &texture, &sprite_index, &grid)) + { + return -1; + } + + // check types for texture + // + // Set Texture + // + if (texture != NULL && !PyObject_IsInstance(texture, (PyObject*)&mcrfpydef::PyTextureType)){ + PyErr_SetString(PyExc_TypeError, "texture must be a mcrfpy.Texture instance"); + return -1; + } /*else if (texture != NULL) // this section needs to go; texture isn't optional and isn't managed by the UI objects anymore + { + self->texture = texture; + Py_INCREF(texture); + } else + { + // default tex? + }*/ + + if (grid != NULL && !PyObject_IsInstance(grid, (PyObject*)&PyUIGridType)) { + PyErr_SetString(PyExc_TypeError, "grid must be a mcrfpy.Grid instance"); + return -1; + } + + auto pytexture = (PyTextureObject*)texture; + if (grid == NULL) + self->data = std::make_shared(); + else + self->data = std::make_shared(*((PyUIGridObject*)grid)->data); + + // TODO - PyTextureObjects and IndexTextures are a little bit of a mess with shared/unshared pointers + self->data->sprite = UISprite(pytexture->data, sprite_index, sf::Vector2f(0,0), 1.0); + self->data->position = sf::Vector2f(x, y); + if (grid != NULL) { + PyUIGridObject* pygrid = (PyUIGridObject*)grid; + self->data->grid = pygrid->data; + // todone - on creation of Entity with Grid assignment, also append it to the entity list + pygrid->data->entities->push_back(self->data); + } + return 0; +} diff --git a/src/UIEntity.h b/src/UIEntity.h index edf06b1..8088aff 100644 --- a/src/UIEntity.h +++ b/src/UIEntity.h @@ -14,9 +14,15 @@ #include "UIGridPoint.h" #include "UIDrawable.h" - +#include "UIBase.h" class UIGrid; +//class UIEntity; +//typedef struct { +// PyObject_HEAD +// std::shared_ptr data; +//} PyUIEntityObject; + // TODO: make UIEntity a drawable class UIEntity//: public UIDrawable { @@ -31,14 +37,10 @@ public: UIEntity(); UIEntity(UIGrid&); + static PyObject* at(PyUIEntityObject* self, PyObject* o); + static int init(PyUIEntityObject* self, PyObject* args, PyObject* kwds); }; -typedef struct { - PyObject_HEAD - std::shared_ptr data; - //PyObject* texture; -} PyUIEntityObject; - namespace mcrfpydef { //TODO: add this method to class scope; move implementation to .cpp file; reconsider for moving to "UIBase.h/.cpp" // TODO: sf::Vector2f convenience functions here might benefit from a PyVectorObject much like PyColorObject @@ -124,6 +126,7 @@ static int PyUIEntity_set_spritenumber(PyUIEntityObject* self, PyObject* value, } //TODO: add this method to class scope; move implementation to .cpp file +/* static PyObject* PyUIEntity_at(PyUIEntityObject* self, PyObject* o) { int x, y; @@ -144,10 +147,11 @@ static PyObject* PyUIEntity_at(PyUIEntityObject* self, PyObject* o) obj->entity = self->data; return (PyObject*)obj; } +*/ //TODO: add this static array to class scope; move implementation to .cpp file static PyMethodDef PyUIEntity_methods[] = { - {"at", (PyCFunction)PyUIEntity_at, METH_O}, + {"at", (PyCFunction)UIEntity::at, METH_O}, {NULL, NULL, 0, NULL} }; @@ -161,7 +165,7 @@ static PyGetSetDef PyUIEntity_getsetters[] = { }; //TODO: add this method to class scope; forward declaration not required after .h/.cpp split -static int PyUIEntity_init(PyUIEntityObject*, PyObject*, PyObject*); // forward declare +//static int PyUIEntity_init(PyUIEntityObject*, PyObject*, PyObject*); // forward declare // Define the PyTypeObject for UIEntity @@ -175,7 +179,7 @@ static PyTypeObject PyUIEntityType = { .tp_doc = "UIEntity objects", .tp_methods = PyUIEntity_methods, .tp_getset = PyUIEntity_getsetters, - .tp_init = (initproc)PyUIEntity_init, + .tp_init = (initproc)UIEntity::init, .tp_new = PyType_GenericNew, }; } diff --git a/src/UIFrame.cpp b/src/UIFrame.cpp index 6cebd9a..c9f42e8 100644 --- a/src/UIFrame.cpp +++ b/src/UIFrame.cpp @@ -54,6 +54,16 @@ void UIFrame::render(sf::Vector2f offset) } } +PyObject* PyUIFrame::get_children(PyUIFrameObject* self, void* closure) +{ + // create PyUICollection instance pointing to self->data->children + PyUICollectionObject* o = (PyUICollectionObject*)PyUICollectionType.tp_alloc(&PyUICollectionType, 0); + if (o) + o->data = self->data->children; + return (PyObject*)o; +} + + namespace mcrfpydef { // TODO: move to class scope; but this should at least get us compiling diff --git a/src/UIFrame.h b/src/UIFrame.h index c51f042..356f0cb 100644 --- a/src/UIFrame.h +++ b/src/UIFrame.h @@ -10,6 +10,14 @@ #include "PyColor.h" #include "PyVector.h" #include "UIDrawable.h" +#include "UIBase.h" + +//class UIFrame; +// +//typedef struct { +// PyObject_HEAD +// std::shared_ptr data; +//} PyUIFrameObject; class UIFrame: public UIDrawable { @@ -24,12 +32,9 @@ public: void move(sf::Vector2f); PyObjectsEnum derived_type() override final; virtual UIDrawable* click_at(sf::Vector2f point) override final; -}; -typedef struct { - PyObject_HEAD - std::shared_ptr data; -} PyUIFrameObject; + static PyObject* get_children(PyUIFrameObject* self, void* closure); +}; namespace mcrfpydef { //TODO: add this method to class scope; move implementation to .cpp file @@ -173,7 +178,7 @@ namespace mcrfpydef { } //TODO: add this method to class scope; move implementation to .cpp file - static PyObject* PyUIFrame_get_children(PyUIFrameObject*, void*); // forward declaration until UICollection is defined + //static PyObject* PyUIFrame_get_children(PyUIFrameObject*, void*); // forward declaration until UICollection is defined // implementation after the PyUICollectionType definition //TODO: add this static array to class scope; move implementation to .cpp file @@ -185,8 +190,8 @@ namespace mcrfpydef { {"outline", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "Thickness of the border", (void*)4}, {"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}, + {"children", (getter)UIFrame::get_children, NULL, "UICollection of objects on top of this one", NULL}, + {"click", (getter)UIDrawable::get_click, (setter)UIDrawable::set_click, "Object called with (x, y, button) when clicked", (void*)PyObjectsEnum::UIFRAME}, {NULL} }; diff --git a/src/UIGrid.h b/src/UIGrid.h index e55026b..a67fc2a 100644 --- a/src/UIGrid.h +++ b/src/UIGrid.h @@ -15,6 +15,7 @@ #include "UIGridPoint.h" #include "UIEntity.h" #include "UIDrawable.h" +#include "UIBase.h" class UIGrid: public UIDrawable { @@ -43,11 +44,11 @@ public: std::shared_ptr>> entities; }; -typedef struct { - PyObject_HEAD - std::shared_ptr data; - //PyObject* texture; -} PyUIGridObject; +//typedef struct { +// PyObject_HEAD +// std::shared_ptr data; +// //PyObject* texture; +//} PyUIGridObject; namespace mcrfpydef { //TODO: add this method to class scope; move implementation to .cpp file @@ -257,7 +258,7 @@ 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}, + {"click", (getter)UIDrawable::get_click, (setter)UIDrawable::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 */ diff --git a/src/UIGridPoint.h b/src/UIGridPoint.h index e611ff7..147f0d9 100644 --- a/src/UIGridPoint.h +++ b/src/UIGridPoint.h @@ -29,6 +29,8 @@ public: bool visible, discovered; }; +class UIGrid; + typedef struct { PyObject_HEAD UIGridPoint* data; diff --git a/src/UISprite.h b/src/UISprite.h index 4397c2c..dc9faf8 100644 --- a/src/UISprite.h +++ b/src/UISprite.h @@ -42,10 +42,10 @@ public: PyObjectsEnum derived_type() override final; }; -typedef struct { - PyObject_HEAD - std::shared_ptr data; -} PyUISpriteObject; +//typedef struct { +// PyObject_HEAD +// std::shared_ptr data; +//} PyUISpriteObject; namespace mcrfpydef { //TODO: add this method to class scope; move implementation to .cpp file @@ -143,7 +143,7 @@ namespace mcrfpydef { {"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}, + {"click", (getter)UIDrawable::get_click, (setter)UIDrawable::set_click, "Object called with (x, y, button) when clicked", (void*)PyObjectsEnum::UISPRITE}, {NULL} }; From c186d8c7f301f216f46ea52c92d60d15615c45ff Mon Sep 17 00:00:00 2001 From: John McCardle Date: Wed, 10 Apr 2024 23:10:15 -0400 Subject: [PATCH 05/14] We are compiling again! Started refactoring UICaption to be more idiomatic --- src/UI.h | 2457 -------------------------------------------- src/UICaption.cpp | 235 +++++ src/UICaption.h | 48 +- src/UIDrawable.cpp | 5 +- src/UIEntity.cpp | 7 +- src/UIEntity.h | 2 + src/UIFrame.cpp | 5 +- src/UIGrid.cpp | 10 + src/UIGridPoint.h | 1 + src/UISprite.cpp | 1 + src/UISprite.h | 1 + 11 files changed, 298 insertions(+), 2474 deletions(-) diff --git a/src/UI.h b/src/UI.h index 73dd5bc..61e4576 100644 --- a/src/UI.h +++ b/src/UI.h @@ -6,12 +6,9 @@ #include "Resources.h" #include -#define ui_h - #include "PyCallable.h" #include "PyTexture.h" #include "PyColor.h" -//#include "PyLinkedColor.h" #include "PyVector.h" #include "PyFont.h" @@ -24,2457 +21,3 @@ #include "UIEntity.h" #include "UIGrid.h" #include "UICollection.h" - -// -//class UIFrame: public UIDrawable -//{ -//public: -// UIFrame(float, float, float, float); -// 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, -// 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 -// void fillColor(PyColorObject* pyColor); // Python setter -// -// sf::Color outlineColor(); // getter -// void outlineColor(sf::Color c); // C++ setter -// void outlineColor(PyColorObject* pyColor); // Python setter -// */ -// -//private: -// //std::shared_ptr fillColor, outlineColor; -// /* -// sf::Color *_fillColor, *_outlineColor; -// PyColorObject *pyFillColor, *pyOutlineColor; -// */ -//}; -// -//class UICaption: public UIDrawable -//{ -//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 -//{ -//private: -// int sprite_index; -// sf::Sprite sprite; -//protected: -// std::shared_ptr ptex; -//public: -// UISprite(); -// //UISprite(IndexTexture*, int, float, float, float); -// //UISprite(IndexTexture*, int, sf::Vector2f, float); -// UISprite(std::shared_ptr, 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&); -// //IndexTexture* itex; -// //sf::Vector2f pos; -// //float scale; -// //void setPosition(float, float); -// void setPosition(sf::Vector2f); -// sf::Vector2f getPosition(); -// void setScale(sf::Vector2f); -// sf::Vector2f getScale(); -// void setSpriteIndex(int); -// int getSpriteIndex(); -// -// void setTexture(std::shared_ptr _ptex, int _sprite_index=-1); -// std::shared_ptr getTexture(); -// -// PyObjectsEnum derived_type() override final; // { return PyObjectsEnum::UISprite; }; -//}; -// -// UIGridPoint - revised grid data for each point -//class UIGridPoint -//{ -//public: -// sf::Color color, color_overlay; -// bool walkable, transparent; -// int tilesprite, tile_overlay, uisprite; -// UIGridPoint(); -//}; -// -// UIGridPointState - entity-specific info for each cell -//class UIGridPointState -//{ -//public: -// bool visible, discovered; -//}; -// -//class UIGrid; -// -// TODO: make UIEntity a drawable(?) Maybe just rely on UISprite/UIGrid to -// somehow properly render the thing? Poorly designed interface -//class UIEntity//: public UIDrawable -//{ -//public: -// //PyObject* self; -// std::shared_ptr grid; -// std::vector gridstate; -// UISprite sprite; -// sf::Vector2f position; //(x,y) in grid coordinates; float for animation -// void render(sf::Vector2f); //override final; -// -// UIEntity(); -// UIEntity(UIGrid&); -// -//}; -// -//class UIGrid: public UIDrawable -//{ -//private: -// std::shared_ptr ptex; -//public: -// UIGrid(); -// //UIGrid(int, int, IndexTexture*, float, float, float, float); -// UIGrid(int, int, std::shared_ptr, sf::Vector2f, sf::Vector2f); -// void update(); -// void render(sf::Vector2f) override final; -// 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 -// sf::RectangleShape box; -// float center_x, center_y, zoom; -// //IndexTexture* itex; -// std::shared_ptr getTexture(); -// sf::Sprite sprite, output; -// sf::RenderTexture renderTexture; -// std::vector points; -// std::shared_ptr>> entities; -//}; -// -///* -//template -//struct CPythonSharedObject { -// PyObject_HEAD -// std::shared_ptr data; -//}; -// -//typedef CPythonSharedObject PyUIFrameObject; -//*/ -// -//typedef struct { -// PyObject_HEAD -// std::shared_ptr data; -//} PyUIFrameObject; -// -//typedef struct { -// PyObject_HEAD -// std::shared_ptr data; -// PyObject* font; -//} PyUICaptionObject; -// -//typedef struct { -// PyObject_HEAD -// std::shared_ptr data; -// //PyObject* texture; -//} PyUISpriteObject; -// -//typedef struct { -// PyObject_HEAD -// std::shared_ptr>> data; -//} PyUICollectionObject; -// -//typedef struct { -// PyObject_HEAD -// UIGridPoint* data; -// std::shared_ptr grid; -//} PyUIGridPointObject; -// -//typedef struct { -// PyObject_HEAD -// UIGridPointState* data; -// std::shared_ptr grid; -// std::shared_ptr entity; -//} PyUIGridPointStateObject; -// -//typedef struct { -// PyObject_HEAD -// std::shared_ptr data; -// //PyObject* texture; -//} PyUIEntityObject; -// -//typedef struct { -// PyObject_HEAD -// std::shared_ptr data; -// //PyObject* texture; -//} PyUIGridObject; -// -//namespace mcrfpydef { -// //PyObject* py_instance(std::shared_ptr source); -// // This function segfaults on tp_alloc for an unknown reason, but works inline with mcrfpydef:: methods. -// -//#define RET_PY_INSTANCE(target) { \ -//switch (target->derived_type()) \ -//{ \ -// case PyObjectsEnum::UIFRAME: \ -// { \ -// PyUIFrameObject* o = (PyUIFrameObject*)((&PyUIFrameType)->tp_alloc(&PyUIFrameType, 0)); \ -// if (o) \ -// { \ -// auto p = std::static_pointer_cast(target); \ -// o->data = p; \ -// auto utarget = o->data; \ -// } \ -// return (PyObject*)o; \ -// } \ -// case PyObjectsEnum::UICAPTION: \ -// { \ -// PyUICaptionObject* o = (PyUICaptionObject*)((&PyUICaptionType)->tp_alloc(&PyUICaptionType, 0)); \ -// if (o) \ -// { \ -// auto p = std::static_pointer_cast(target); \ -// o->data = p; \ -// auto utarget = o->data; \ -// } \ -// return (PyObject*)o; \ -// } \ -// case PyObjectsEnum::UISPRITE: \ -// { \ -// PyUISpriteObject* o = (PyUISpriteObject*)((&PyUISpriteType)->tp_alloc(&PyUISpriteType, 0)); \ -// if (o) \ -// { \ -// auto p = std::static_pointer_cast(target); \ -// o->data = p; \ -// auto utarget = o->data; \ -// } \ -// return (PyObject*)o; \ -// } \ -// case PyObjectsEnum::UIGRID: \ -// { \ -// PyUIGridObject* o = (PyUIGridObject*)((&PyUIGridType)->tp_alloc(&PyUIGridType, 0)); \ -// if (o) \ -// { \ -// auto p = std::static_pointer_cast(target); \ -// o->data = p; \ -// auto utarget = o->data; \ -// } \ -// return (PyObject*)o; \ -// } \ -//} \ -//} -// end macro definition -// -// // Color Definitions -// // struct, members, new, set_member, PyTypeObject -// -// /* for reference: the structs to implement -// typedef struct { -// PyObject_HEAD -// std::shared_ptr data; -// } PyColorObject; -// -// typedef struct { -// PyObject_HEAD -// std::shared_ptr data; -// } PyUIFrameObject; -// -// typedef struct { -// PyObject_HEAD -// std::shared_ptr data; -// } PyUICaptionObject; -// -// typedef struct { -// PyObject_HEAD -// std::shared_ptr data; -// } 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->borrow(); -// break; -// case PyObjectsEnum::UICAPTION: -// ptr = ((PyUICaptionObject*)self)->data->click_callable->borrow(); -// break; -// case PyObjectsEnum::UISPRITE: -// ptr = ((PyUISpriteObject*)self)->data->click_callable->borrow(); -// break; -// case PyObjectsEnum::UIGRID: -// ptr = ((PyUIGridObject*)self)->data->click_callable->borrow(); -// 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 -// * -// */ -// /* -// typedef struct { -// PyObject_HEAD -// std::shared_ptr data; -// } PyFontObject; -// -// static int PyFont_init(PyFontObject* self, PyObject* args, PyObject* kwds) -// { -// //std::cout << "Init called\n"; -// static const char* keywords[] = { "filename", nullptr }; -// char* filename; -// -// if (!PyArg_ParseTupleAndKeywords(args, kwds, "s", const_cast(keywords), &filename)) -// { -// return -1; -// } -// self->data->loadFromFile((std::string)filename); -// return 0; -// } -// -// static PyTypeObject PyFontType = { -// //PyVarObject_HEAD_INIT(NULL, 0) -// .tp_name = "mcrfpy.Font", -// .tp_basicsize = sizeof(PyFontObject), -// .tp_itemsize = 0, -// .tp_flags = Py_TPFLAGS_DEFAULT, -// .tp_doc = PyDoc_STR("SFML Font Object"), -// .tp_init = (initproc)PyFont_init, -// .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* -// { -// PyFontObject* self = (PyFontObject*)type->tp_alloc(type, 0); -// self->data = std::make_shared(); -// return (PyObject*)self; -// } -// }; -// */ -// -// /* -// * -// * End PyFontType defs -// * -// */ -// -// -// static PyObject* PyColor_get_member(PyColorObject* self, void* closure) -// { -// auto member_ptr = reinterpret_cast(closure); -// if (member_ptr == 0) -// return PyLong_FromLong(self->data->r); -// else if (member_ptr == 1) -// return PyLong_FromLong(self->data->g); -// else if (member_ptr == 2) -// return PyLong_FromLong(self->data->b); -// else if (member_ptr == 3) -// return PyLong_FromLong(self->data->a); -// else -// { -// PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); -// return nullptr; -// } -// } -// -// static int PyColor_set_member(PyColorObject* self, PyObject* value, void* closure) -// { -// if (PyLong_Check(value)) -// { -// long int_val = PyLong_AsLong(value); -// if (int_val < 0) -// int_val = 0; -// else if (int_val > 255) -// int_val = 255; -// auto member_ptr = reinterpret_cast(closure); -// if (member_ptr == 0) -// self->data->r = static_cast(int_val); -// else if (member_ptr == 1) -// self->data->g = static_cast(int_val); -// else if (member_ptr == 2) -// self->data->b = static_cast(int_val); -// else if (member_ptr == 3) -// self->data->a = static_cast(int_val); -// } -// else -// { -// PyErr_SetString(PyExc_TypeError, "Value must be an integer."); -// return -1; -// } -// return 0; -// } -// -// static PyGetSetDef PyColor_getsetters[] = { -// {"r", (getter)PyColor_get_member, (setter)PyColor_set_member, "Red component", (void*)0}, -// {"g", (getter)PyColor_get_member, (setter)PyColor_set_member, "Green component", (void*)1}, -// {"b", (getter)PyColor_get_member, (setter)PyColor_set_member, "Blue component", (void*)2}, -// {"a", (getter)PyColor_get_member, (setter)PyColor_set_member, "Alpha component", (void*)3}, -// {NULL} -// }; -// -// -// static PyTypeObject PyColorType = { -// //PyVarObject_HEAD_INIT(NULL, 0) -// .tp_name = "mcrfpy.Color", -// .tp_basicsize = sizeof(PyColorObject), -// .tp_itemsize = 0, -// .tp_dealloc = (destructor)[](PyObject* self) -// { -// PyColorObject* obj = (PyColorObject*)self; -// obj->data.reset(); -// Py_TYPE(self)->tp_free(self); -// }, -// //.tp_repr = (reprfunc)PyUIFrame_repr, -// //.tp_hash = NULL, -// //.tp_iter -// //.tp_iternext -// .tp_flags = Py_TPFLAGS_DEFAULT, -// .tp_doc = PyDoc_STR("SFML Color object (RGBA)"), -// //.tp_methods = PyUIFrame_methods, -// //.tp_members = PyColor_members, -// .tp_getset = PyColor_getsetters, -// //.tp_base = NULL, -// //.tp_init = (initproc)PyUIFrame_init, -// .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* -// { -// PyColorObject* self = (PyColorObject*)type->tp_alloc(type, 0); -// if (self) self->data = std::make_shared(); -// return (PyObject*)self; -// } -// }; -// -// /* -// * -// * Begin template generation for PyUICaptionType -// * -// */ -// -// static PyObject* PyUICaption_get_float_member(PyUICaptionObject* self, void* closure) -// { -// auto member_ptr = reinterpret_cast(closure); -// if (member_ptr == 0) -// return PyFloat_FromDouble(self->data->text.getPosition().x); -// else if (member_ptr == 1) -// return PyFloat_FromDouble(self->data->text.getPosition().y); -// else if (member_ptr == 4) -// return PyFloat_FromDouble(self->data->text.getOutlineThickness()); -// else if (member_ptr == 5) -// return PyLong_FromLong(self->data->text.getCharacterSize()); -// else -// { -// PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); -// return nullptr; -// } -// } -// -// static int PyUICaption_set_float_member(PyUICaptionObject* self, PyObject* value, void* closure) -// { -// float val; -// auto member_ptr = reinterpret_cast(closure); -// if (PyFloat_Check(value)) -// { -// val = PyFloat_AsDouble(value); -// } -// else if (PyLong_Check(value)) -// { -// val = PyLong_AsLong(value); -// } -// else -// { -// PyErr_SetString(PyExc_TypeError, "Value must be an integer."); -// return -1; -// } -// if (member_ptr == 0) //x -// self->data->text.setPosition(val, self->data->text.getPosition().y); -// else if (member_ptr == 1) //y -// self->data->text.setPosition(self->data->text.getPosition().x, val); -// else if (member_ptr == 4) //outline -// self->data->text.setOutlineThickness(val); -// else if (member_ptr == 5) // character size -// self->data->text.setCharacterSize(val); -// return 0; -// } -// -// static PyObject* PyUICaption_get_vec_member(PyUICaptionObject* self, void* closure) -// { -// return PyVector(self->data->text.getPosition()).pyObject(); -// } -// -// static int PyUICaption_set_vec_member(PyUICaptionObject* self, PyObject* value, void* closure) -// { -// self->data->text.setPosition(PyVector::fromPy(value)); -// return 0; -// } -// -// static PyObject* PyUICaption_get_color_member(PyUICaptionObject* self, void* closure) -// { -// // validate closure (should be impossible to be wrong, but it's thorough) -// auto member_ptr = reinterpret_cast(closure); -// if (member_ptr != 0 && member_ptr != 1) -// { -// PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); -// return nullptr; -// } -// -// // TODO: manually calling tp_alloc to create a PyColorObject seems like an antipattern -// /* -// PyTypeObject* colorType = &PyLinkedColorType; -// PyObject* pyColor = colorType->tp_alloc(colorLinkedType, 0); -// if (pyColor == NULL) -// { -// std::cout << "failure to allocate mcrfpy.LinkedColor / PyLinkedColorType" << std::endl; -// return NULL; -// } -// PyColorObject* pyColorObj = reinterpret_cast(pyColor); -// */ -// -// // fetch correct member data -// sf::Color color; -// //sf::Color (*cgetter)(); -// //void (*csetter)(sf::Color); -// //std::function csetter; -// //std::function cgetter; -// if (member_ptr == 0) -// { -// color = self->data->text.getFillColor(); -// //return Py_BuildValue("(iii)", color.r, color.g, color.b); -// //csetter = &self->data->text.setFillColor; -// //cgetter = &self->data->text.getFillColor; -// //csetter = [s = self->data](sf::Color c){s->text.setFillColor(c);}; -// //cgetter = [s = self->data](){return s->text.getFillColor();}; -// } -// else if (member_ptr == 1) -// { -// color = self->data->text.getOutlineColor(); -// //return Py_BuildValue("(iii)", color.r, color.g, color.b); -// //csetter = &self->data->text.setOutlineColor; -// //cgetter = &self->data->text.getOutlineColor; -// //csetter = [s = self->data](sf::Color c){s->text.setOutlineColor(c);}; -// //cgetter = [s = self->data](){return s->text.getOutlineColor();}; -// } -// -// // initialize new mcrfpy.Color instance -// //pyColorObj->data = std::make_shared(color); -// //PyLinkedColor::fromPy(pyColorObj).set(color); -// //auto linkedcolor = PyLinkedColor(csetter, cgetter, self->data, member_ptr); -// //linkedcolor.set(color); // don't need to set a linked color! -// -// //return pyColor; -// return PyColor(color).pyObject(); -// } -// -// static int PyUICaption_set_color_member(PyUICaptionObject* self, PyObject* value, void* closure) -// { -// auto member_ptr = reinterpret_cast(closure); -// //TODO: this logic of (PyColor instance OR tuple -> sf::color) should be encapsulated for reuse -// int r, g, b, a; -// if (PyObject_IsInstance(value, (PyObject*)&PyColorType)) -// { -// // get value from mcrfpy.Color instance -// /* -// PyColorObject* color = reinterpret_cast(value); -// r = color->data->r; -// g = color->data->g; -// b = color->data->b; -// a = color->data->a; -// */ -// //std::cout << "Build LinkedColor" << std::endl; -// //auto lc = PyLinkedColor::fromPy(value); -// auto c = ((PyColorObject*)value)->data; -// //std::cout << "Fetch value" << std::endl; -// //auto c = lc.get(); -// r = c.r; g = c.g; b = c.b; a = c.a; -// std::cout << "got " << int(r) << ", " << int(g) << ", " << int(b) << ", " << int(a) << std::endl; -// } -// else if (!PyTuple_Check(value) || PyTuple_Size(value) < 3 || PyTuple_Size(value) > 4) -// { -// // reject non-Color, non-tuple value -// PyErr_SetString(PyExc_TypeError, "Value must be a tuple of 3 or 4 integers or an mcrfpy.Color object."); -// return -1; -// } -// else // get value from tuples -// { -// r = PyLong_AsLong(PyTuple_GetItem(value, 0)); -// g = PyLong_AsLong(PyTuple_GetItem(value, 1)); -// b = PyLong_AsLong(PyTuple_GetItem(value, 2)); -// a = 255; -// -// if (PyTuple_Size(value) == 4) -// { -// a = PyLong_AsLong(PyTuple_GetItem(value, 3)); -// } -// } -// -// if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255 || a < 0 || a > 255) -// { -// PyErr_SetString(PyExc_ValueError, "Color values must be between 0 and 255."); -// return -1; -// } -// -// if (member_ptr == 0) -// { -// self->data->text.setFillColor(sf::Color(r, g, b, a)); -// } -// else if (member_ptr == 1) -// { -// self->data->text.setOutlineColor(sf::Color(r, g, b, a)); -// } -// else -// { -// PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); -// return -1; -// } -// -// return 0; -// } -// -// static PyObject* PyUICaption_get_text(PyUICaptionObject* self, void* closure) -// { -// Resources::caption_buffer = self->data->text.getString(); -// return PyUnicode_FromString(Resources::caption_buffer.c_str()); -// } -// -// static int PyUICaption_set_text(PyUICaptionObject* self, PyObject* value, void* closure) -// { -// PyObject* s = PyObject_Str(value); -// PyObject * temp_bytes = PyUnicode_AsEncodedString(s, "UTF-8", "strict"); // Owned reference -// if (temp_bytes != NULL) { -// Resources::caption_buffer = PyBytes_AS_STRING(temp_bytes); // Borrowed pointer -// Py_DECREF(temp_bytes); -// } -// self->data->text.setString(Resources::caption_buffer); -// return 0; -// } -// -// static PyGetSetDef PyUICaption_getsetters[] = { -// {"x", (getter)PyUICaption_get_float_member, (setter)PyUICaption_set_float_member, "X coordinate of top-left corner", (void*)0}, -// {"y", (getter)PyUICaption_get_float_member, (setter)PyUICaption_set_float_member, "Y coordinate of top-left corner", (void*)1}, -// {"pos", (getter)PyUICaption_get_vec_member, (setter)PyUICaption_set_vec_member, "(x, y) vector", (void*)0}, -// //{"w", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "width of the rectangle", (void*)2}, -// //{"h", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "height of the rectangle", (void*)3}, -// {"outline", (getter)PyUICaption_get_float_member, (setter)PyUICaption_set_float_member, "Thickness of the border", (void*)4}, -// {"fill_color", (getter)PyUICaption_get_color_member, (setter)PyUICaption_set_color_member, "Fill color of the text", (void*)0}, -// {"outline_color", (getter)PyUICaption_get_color_member, (setter)PyUICaption_set_color_member, "Outline color of the text", (void*)1}, -// //{"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} -// }; -// -// static PyObject* PyUICaption_repr(PyUICaptionObject* self) -// { -// std::ostringstream ss; -// if (!self->data) ss << ""; -// else { -// auto text = self->data->text; -// auto fc = text.getFillColor(); -// auto oc = text.getOutlineColor(); -// ss << ""; -// } -// std::string repr_str = ss.str(); -// return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); -// } -// -// static int PyUICaption_init(PyUICaptionObject* self, PyObject* args, PyObject* kwds) -// { -// //std::cout << "Init called\n"; -// static const char* keywords[] = { "x", "y", "text", "font", "fill_color", "outline_color", nullptr }; -// float x = 0.0f, y = 0.0f; -// char* text; -// PyObject* font, fill_color, outline_color; -// -// if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ffzOOO", -// const_cast(keywords), &x, &y, &text, &font, &fill_color, &outline_color)) -// { -// return -1; -// } -// -// // check types for font, fill_color, outline_color -// // -// // Set Font -// // -// std::cout << PyUnicode_AsUTF8(PyObject_Repr(font)) << std::endl; -// if (font != NULL && !PyObject_IsInstance(font, (PyObject*)&PyFontType)){ -// PyErr_SetString(PyExc_TypeError, "font must be a mcrfpy.Font instance"); -// return -1; -// } else if (font != NULL) -// { -// auto font_obj = (PyFontObject*)font; -// self->data->text.setFont(font_obj->data->font); -// self->font = font; -// Py_INCREF(font); -// } else -// { -// // default font -// //self->data->text.setFont(Resources::game->getFont()); -// } -// -// // -// // Set Color -// // -// self->data->text.setPosition(sf::Vector2f(x, y)); -// self->data->text.setString((std::string)text); -// self->data->text.setFillColor(sf::Color(0,0,0,255)); -// self->data->text.setOutlineColor(sf::Color(128,128,128,255)); -// -// return 0; -// } -// -// static PyTypeObject PyUICaptionType = { -// //PyVarObject_HEAD_INIT(NULL, 0) -// .tp_name = "mcrfpy.Caption", -// .tp_basicsize = sizeof(PyUICaptionObject), -// .tp_itemsize = 0, -// .tp_dealloc = (destructor)[](PyObject* self) -// { -// PyUICaptionObject* obj = (PyUICaptionObject*)self; -// // release reference to font object -// if (obj->font) Py_DECREF(obj->font); -// obj->data.reset(); -// Py_TYPE(self)->tp_free(self); -// }, -// .tp_repr = (reprfunc)PyUICaption_repr, -// //.tp_hash = NULL, -// //.tp_iter -// //.tp_iternext -// .tp_flags = Py_TPFLAGS_DEFAULT, -// .tp_doc = PyDoc_STR("docstring"), -// //.tp_methods = PyUIFrame_methods, -// //.tp_members = PyUIFrame_members, -// .tp_getset = PyUICaption_getsetters, -// //.tp_base = NULL, -// .tp_init = (initproc)PyUICaption_init, -// .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* -// { -// PyUICaptionObject* self = (PyUICaptionObject*)type->tp_alloc(type, 0); -// if (self) self->data = std::make_shared(); -// return (PyObject*)self; -// } -// }; -// -// /* -// * -// * End PyUICaptionType generation -// * -// */ -// -// -// /* -// * -// * Begin template generation for PyUIFrameType -// * -// */ -// -// static PyObject* PyUIFrame_get_float_member(PyUIFrameObject* self, void* closure) -// { -// auto member_ptr = reinterpret_cast(closure); -// if (member_ptr == 0) -// return PyFloat_FromDouble(self->data->box.getPosition().x); -// else if (member_ptr == 1) -// return PyFloat_FromDouble(self->data->box.getPosition().y); -// else if (member_ptr == 2) -// return PyFloat_FromDouble(self->data->box.getSize().x); -// else if (member_ptr == 3) -// return PyFloat_FromDouble(self->data->box.getSize().y); -// else if (member_ptr == 4) -// return PyFloat_FromDouble(self->data->box.getOutlineThickness()); -// else -// { -// PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); -// return nullptr; -// } -// } -// -// static int PyUIFrame_set_float_member(PyUIFrameObject* self, PyObject* value, void* closure) -// { -// float val; -// auto member_ptr = reinterpret_cast(closure); -// if (PyFloat_Check(value)) -// { -// val = PyFloat_AsDouble(value); -// } -// else if (PyLong_Check(value)) -// { -// val = PyLong_AsLong(value); -// } -// else -// { -// PyErr_SetString(PyExc_TypeError, "Value must be an integer."); -// return -1; -// } -// if (member_ptr == 0) //x -// self->data->box.setPosition(val, self->data->box.getPosition().y); -// else if (member_ptr == 1) //y -// self->data->box.setPosition(self->data->box.getPosition().x, val); -// else if (member_ptr == 2) //w -// self->data->box.setSize(sf::Vector2f(val, self->data->box.getSize().y)); -// else if (member_ptr == 3) //h -// self->data->box.setSize(sf::Vector2f(self->data->box.getSize().x, val)); -// else if (member_ptr == 4) //outline -// self->data->box.setOutlineThickness(val); -// return 0; -// } -// -// static PyObject* PyUIFrame_get_color_member(PyUIFrameObject* self, void* closure) -// { -// // validate closure (should be impossible to be wrong, but it's thorough) -// auto member_ptr = reinterpret_cast(closure); -// if (member_ptr != 0 && member_ptr != 1) -// { -// PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); -// return nullptr; -// } -// 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); -// -// // fetch correct member data -// sf::Color color; -// if (member_ptr == 0) -// { -// color = self->data->box.getFillColor(); -// //return Py_BuildValue("(iii)", color.r, color.g, color.b); -// } -// else if (member_ptr == 1) -// { -// color = self->data->box.getOutlineColor(); -// //return Py_BuildValue("(iii)", color.r, color.g, color.b); -// } -// -// // initialize new mcrfpy.Color instance -// //pyColorObj->data = std::make_shared(color); -// //PyColor::fromPy(pyColorObj).set(color); -// -// // TODO - supposed to return Linked -// return PyColor(color).pyObject(); -// } -// -// static int PyUIFrame_set_color_member(PyUIFrameObject* self, PyObject* value, void* closure) -// { -// //TODO: this logic of (PyColor instance OR tuple -> sf::color) should be encapsulated for reuse -// auto member_ptr = reinterpret_cast(closure); -// int r, g, b, a; -// if (PyObject_IsInstance(value, (PyObject*)&PyColorType)) -// { -// // get value from mcrfpy.Color instance -// /* -// PyColorObject* color = reinterpret_cast(value); -// r = color->data->r; -// g = color->data->g; -// b = color->data->b; -// a = color->data->a; -// std::cout << "using color: " << r << " " << g << " " << b << " " << a << std::endl; -// */ -// sf::Color c = ((PyColorObject*)value)->data; -// r = c.r; g = c.g; b = c.b; a = c.a; -// } -// else if (!PyTuple_Check(value) || PyTuple_Size(value) < 3 || PyTuple_Size(value) > 4) -// { -// // reject non-Color, non-tuple value -// PyErr_SetString(PyExc_TypeError, "Value must be a tuple of 3 or 4 integers or an mcrfpy.Color object."); -// return -1; -// } -// else // get value from tuples -// { -// r = PyLong_AsLong(PyTuple_GetItem(value, 0)); -// g = PyLong_AsLong(PyTuple_GetItem(value, 1)); -// b = PyLong_AsLong(PyTuple_GetItem(value, 2)); -// a = 255; -// -// if (PyTuple_Size(value) == 4) -// { -// a = PyLong_AsLong(PyTuple_GetItem(value, 3)); -// } -// } -// -// if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255 || a < 0 || a > 255) -// { -// PyErr_SetString(PyExc_ValueError, "Color values must be between 0 and 255."); -// return -1; -// } -// -// if (member_ptr == 0) -// { -// self->data->box.setFillColor(sf::Color(r, g, b, a)); -// } -// else if (member_ptr == 1) -// { -// self->data->box.setOutlineColor(sf::Color(r, g, b, a)); -// } -// else -// { -// PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); -// return -1; -// } -// -// return 0; -// } -// -// static PyObject* PyUIFrame_get_children(PyUIFrameObject*, void*); -// // implementation after the PyUICollectionType definition -// -// static PyGetSetDef PyUIFrame_getsetters[] = { -// {"x", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "X coordinate of top-left corner", (void*)0}, -// {"y", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "Y coordinate of top-left corner", (void*)1}, -// {"w", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "width of the rectangle", (void*)2}, -// {"h", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "height of the rectangle", (void*)3}, -// {"outline", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "Thickness of the border", (void*)4}, -// {"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} -// }; -// -// static PyObject* PyUIFrame_repr(PyUIFrameObject* self) -// { -// std::ostringstream ss; -// if (!self->data) ss << ""; -// else { -// auto box = self->data->box; -// auto fc = box.getFillColor(); -// auto oc = box.getOutlineColor(); -// ss << "data->children->size() << " child objects" << -// ")>"; -// } -// std::string repr_str = ss.str(); -// return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); -// } -// -// static int PyUIFrame_init(PyUIFrameObject* self, PyObject* args, PyObject* kwds) -// { -// //std::cout << "Init called\n"; -// static const char* keywords[] = { "x", "y", "w", "h", "fill_color", "outline_color", "outline", nullptr }; -// float x = 0.0f, y = 0.0f, w = 0.0f, h=0.0f, outline=0.0f; -// PyObject* fill_color = 0; -// PyObject* outline_color = 0; -// -// if (!PyArg_ParseTupleAndKeywords(args, kwds, "ffff|OOf", const_cast(keywords), &x, &y, &w, &h, &fill_color, &outline_color, &outline)) -// { -// return -1; -// } -// -// //self->data->x = x; -// //self->data->y = y; -// self->data->box.setPosition(sf::Vector2f(x, y)); -// self->data->box.setSize(sf::Vector2f(w, h)); -// self->data->box.setOutlineThickness(outline); -// // getsetter abuse because I haven't standardized Color object parsing (TODO) -// int err_val = 0; -// if (fill_color && fill_color != Py_None) err_val = PyUIFrame_set_color_member(self, fill_color, (void*)0); -// else self->data->box.setFillColor(sf::Color(0,0,0,255)); -// if (err_val) return err_val; -// if (outline_color && outline_color != Py_None) err_val = PyUIFrame_set_color_member(self, outline_color, (void*)1); -// else self->data->box.setOutlineColor(sf::Color(128,128,128,255)); -// if (err_val) return err_val; -// return 0; -// } -// -// static PyTypeObject PyUIFrameType = { -// //PyVarObject_HEAD_INIT(NULL, 0) -// .tp_name = "mcrfpy.Frame", -// .tp_basicsize = sizeof(PyUIFrameObject), -// .tp_itemsize = 0, -// .tp_dealloc = (destructor)[](PyObject* self) -// { -// PyUIFrameObject* obj = (PyUIFrameObject*)self; -// obj->data.reset(); -// Py_TYPE(self)->tp_free(self); -// }, -// .tp_repr = (reprfunc)PyUIFrame_repr, -// //.tp_hash = NULL, -// //.tp_iter -// //.tp_iternext -// .tp_flags = Py_TPFLAGS_DEFAULT, -// .tp_doc = PyDoc_STR("docstring"), -// //.tp_methods = PyUIFrame_methods, -// //.tp_members = PyUIFrame_members, -// .tp_getset = PyUIFrame_getsetters, -// //.tp_base = NULL, -// .tp_init = (initproc)PyUIFrame_init, -// .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* -// { -// PyUIFrameObject* self = (PyUIFrameObject*)type->tp_alloc(type, 0); -// if (self) self->data = std::make_shared(); -// return (PyObject*)self; -// } -// }; -// -// /* -// * -// * End auto-generated PyUIFrameType generation -// * -// */ -// -// /* -// * -// * Begin Python Class Instantiator (iterator helper) -// * -// */ -// -// /* // definition can't go in the header file -// PyObject* py_instance(UIDrawable* obj) -// { -// -// } -// */ -// -// /* -// * -// * End Python Class Instantitator (iterator helper) -// * -// */ -// -// /* -// * -// * Begin template generation for PyUISpriteType -// * -// */ -// -// static PyObject* PyUISprite_get_float_member(PyUISpriteObject* self, void* closure) -// { -// auto member_ptr = reinterpret_cast(closure); -// if (member_ptr == 0) -// return PyFloat_FromDouble(self->data->getPosition().x); -// else if (member_ptr == 1) -// return PyFloat_FromDouble(self->data->getPosition().y); -// else if (member_ptr == 2) -// return PyFloat_FromDouble(self->data->getScale().x); // scale X and Y are identical, presently -// else -// { -// PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); -// return nullptr; -// } -// } -// -// -// static int PyUISprite_set_float_member(PyUISpriteObject* self, PyObject* value, void* closure) -// { -// float val; -// auto member_ptr = reinterpret_cast(closure); -// if (PyFloat_Check(value)) -// { -// val = PyFloat_AsDouble(value); -// } -// else if (PyLong_Check(value)) -// { -// val = PyLong_AsLong(value); -// } -// else -// { -// PyErr_SetString(PyExc_TypeError, "Value must be a floating point number."); -// return -1; -// } -// if (member_ptr == 0) //x -// self->data->setPosition(sf::Vector2f(val, self->data->getPosition().y)); -// else if (member_ptr == 1) //y -// self->data->setPosition(sf::Vector2f(self->data->getPosition().x, val)); -// else if (member_ptr == 2) // scale -// self->data->setScale(sf::Vector2f(val, val)); -// return 0; -// } -// -// static PyObject* PyUISprite_get_int_member(PyUISpriteObject* self, void* closure) -// { -// auto member_ptr = reinterpret_cast(closure); -// if (true) {} -// else -// { -// PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); -// return nullptr; -// } -// -// return PyLong_FromDouble(self->data->getSpriteIndex()); -// } -// -// -// static int PyUISprite_set_int_member(PyUISpriteObject* self, PyObject* value, void* closure) -// { -// int val; -// auto member_ptr = reinterpret_cast(closure); -// if (PyLong_Check(value)) -// { -// val = PyLong_AsLong(value); -// } -// else -// { -// PyErr_SetString(PyExc_TypeError, "Value must be an integer."); -// return -1; -// } -// //self->data->sprite_index = val; -// //self->data->sprite.setTextureRect(self->data->itex->spriteCoordinates(val)); -// self->data->setSpriteIndex(val); -// return 0; -// } -// -// static PyObject* PyUISprite_get_texture(PyUISpriteObject* self, void* closure) -// { -// return self->data->getTexture()->pyObject(); -// } -// -// static int PyUISprite_set_texture(PyUISpriteObject* self, PyObject* value, void* closure) -// { -// return -1; -// } -// -// static PyGetSetDef PyUISprite_getsetters[] = { -// {"x", (getter)PyUISprite_get_float_member, (setter)PyUISprite_set_float_member, "X coordinate of top-left corner", (void*)0}, -// {"y", (getter)PyUISprite_get_float_member, (setter)PyUISprite_set_float_member, "Y coordinate of top-left corner", (void*)1}, -// {"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} -// }; -// -// static PyObject* PyUISprite_repr(PyUISpriteObject* self) -// { -// std::ostringstream ss; -// if (!self->data) ss << ""; -// else { -// //auto sprite = self->data->sprite; -// ss << ""; -// } -// std::string repr_str = ss.str(); -// return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); -// } -// -// static int PyUISprite_init(PyUISpriteObject* self, PyObject* args, PyObject* kwds) -// { -// //std::cout << "Init called\n"; -// static const char* keywords[] = { "x", "y", "texture", "sprite_index", "scale", nullptr }; -// float x = 0.0f, y = 0.0f, scale = 1.0f; -// int sprite_index; -// PyObject* texture; -// -// if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ffOif", -// const_cast(keywords), &x, &y, &texture, &sprite_index, &scale)) -// { -// return -1; -// } -// -// // check types for texture -// // -// // Set Texture -// // -// if (texture != NULL && !PyObject_IsInstance(texture, (PyObject*)&PyTextureType)){ -// PyErr_SetString(PyExc_TypeError, "texture must be a mcrfpy.Texture instance"); -// return -1; -// } /*else if (texture != NULL) // to be removed: UIObjects don't manage texture references -// { -// self->texture = texture; -// Py_INCREF(texture); -// } else -// { -// // default tex? -// }*/ -// auto pytexture = (PyTextureObject*)texture; -// self->data = std::make_shared(pytexture->data, sprite_index, sf::Vector2f(x, y), scale); -// self->data->setPosition(sf::Vector2f(x, y)); -// -// return 0; -// } -// -// static PyTypeObject PyUISpriteType = { -// //PyVarObject_HEAD_INIT(NULL, 0) -// .tp_name = "mcrfpy.Sprite", -// .tp_basicsize = sizeof(PyUISpriteObject), -// .tp_itemsize = 0, -// .tp_dealloc = (destructor)[](PyObject* self) -// { -// PyUISpriteObject* obj = (PyUISpriteObject*)self; -// // release reference to font object -// //if (obj->texture) Py_DECREF(obj->texture); -// obj->data.reset(); -// Py_TYPE(self)->tp_free(self); -// }, -// .tp_repr = (reprfunc)PyUISprite_repr, -// //.tp_hash = NULL, -// //.tp_iter -// //.tp_iternext -// .tp_flags = Py_TPFLAGS_DEFAULT, -// .tp_doc = PyDoc_STR("docstring"), -// //.tp_methods = PyUIFrame_methods, -// //.tp_members = PyUIFrame_members, -// .tp_getset = PyUISprite_getsetters, -// //.tp_base = NULL, -// .tp_init = (initproc)PyUISprite_init, -// .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* -// { -// PyUISpriteObject* self = (PyUISpriteObject*)type->tp_alloc(type, 0); -// //if (self) self->data = std::make_shared(); -// return (PyObject*)self; -// } -// }; -// /* -// * -// * End template for PyUISpriteType -// * -// */ -// -// -// -// -// /* -// * -// * PyUIGridPoint defs -// * -// */ -// -// TODO: question: are sfColor_to_PyObject and PyObject_to_sfColor duplicitive? How does UIFrame get/set colors? -// -// Utility function to convert sf::Color to PyObject* -//static PyObject* sfColor_to_PyObject(sf::Color color) { -// return Py_BuildValue("(iiii)", color.r, color.g, color.b, color.a); -//} -// -// Utility function to convert PyObject* to sf::Color -//static sf::Color PyObject_to_sfColor(PyObject* obj) { -// int r, g, b, a = 255; // Default alpha to fully opaque if not specified -// if (!PyArg_ParseTuple(obj, "iii|i", &r, &g, &b, &a)) { -// return sf::Color(); // Return default color on parse error -// } -// return sf::Color(r, g, b, a); -//} -// -//static PyObject* PyUIGridPoint_get_color(PyUIGridPointObject* self, void* closure) { -// if (reinterpret_cast(closure) == 0) { // color -// return sfColor_to_PyObject(self->data->color); -// } else { // color_overlay -// return sfColor_to_PyObject(self->data->color_overlay); -// } -//} -// -//static int PyUIGridPoint_set_color(PyUIGridPointObject* self, PyObject* value, void* closure) { -// sf::Color color = PyObject_to_sfColor(value); -// if (reinterpret_cast(closure) == 0) { // color -// self->data->color = color; -// } else { // color_overlay -// self->data->color_overlay = color; -// } -// return 0; -//} -// -//static PyObject* PyUIGridPoint_get_bool_member(PyUIGridPointObject* self, void* closure) { -// if (reinterpret_cast(closure) == 0) { // walkable -// return PyBool_FromLong(self->data->walkable); -// } else { // transparent -// return PyBool_FromLong(self->data->transparent); -// } -//} -// -//static int PyUIGridPoint_set_bool_member(PyUIGridPointObject* self, PyObject* value, void* closure) { -// if (value == Py_True) { -// if (reinterpret_cast(closure) == 0) { // walkable -// self->data->walkable = true; -// } else { // transparent -// self->data->transparent = true; -// } -// } else if (value == Py_False) { -// if (reinterpret_cast(closure) == 0) { // walkable -// self->data->walkable = false; -// } else { // transparent -// self->data->transparent = false; -// } -// } else { -// PyErr_SetString(PyExc_ValueError, "Expected a boolean value"); -// return -1; -// } -// return 0; -//} -// -//static PyObject* PyUIGridPoint_get_int_member(PyUIGridPointObject* self, void* closure) { -// switch(reinterpret_cast(closure)) { -// case 0: return PyLong_FromLong(self->data->tilesprite); -// case 1: return PyLong_FromLong(self->data->tile_overlay); -// case 2: return PyLong_FromLong(self->data->uisprite); -// default: PyErr_SetString(PyExc_RuntimeError, "Invalid closure"); return nullptr; -// } -//} -// -//static int PyUIGridPoint_set_int_member(PyUIGridPointObject* self, PyObject* value, void* closure) { -// long val = PyLong_AsLong(value); -// if (PyErr_Occurred()) return -1; -// -// switch(reinterpret_cast(closure)) { -// case 0: self->data->tilesprite = val; break; -// case 1: self->data->tile_overlay = val; break; -// case 2: self->data->uisprite = val; break; -// default: PyErr_SetString(PyExc_RuntimeError, "Invalid closure"); return -1; -// } -// return 0; -//} -// -//static PyGetSetDef PyUIGridPoint_getsetters[] = { -// {"color", (getter)PyUIGridPoint_get_color, (setter)PyUIGridPoint_set_color, "GridPoint color", (void*)0}, -// {"color_overlay", (getter)PyUIGridPoint_get_color, (setter)PyUIGridPoint_set_color, "GridPoint color overlay", (void*)1}, -// {"walkable", (getter)PyUIGridPoint_get_bool_member, (setter)PyUIGridPoint_set_bool_member, "Is the GridPoint walkable", (void*)0}, -// {"transparent", (getter)PyUIGridPoint_get_bool_member, (setter)PyUIGridPoint_set_bool_member, "Is the GridPoint transparent", (void*)1}, -// {"tilesprite", (getter)PyUIGridPoint_get_int_member, (setter)PyUIGridPoint_set_int_member, "Tile sprite index", (void*)0}, -// {"tile_overlay", (getter)PyUIGridPoint_get_int_member, (setter)PyUIGridPoint_set_int_member, "Tile overlay sprite index", (void*)1}, -// {"uisprite", (getter)PyUIGridPoint_get_int_member, (setter)PyUIGridPoint_set_int_member, "UI sprite index", (void*)2}, -// {NULL} /* Sentinel */ -//}; -// -//static PyTypeObject PyUIGridPointType = { -// //PyVarObject_HEAD_INIT(NULL, 0) -// .tp_name = "mcrfpy.GridPoint", -// .tp_basicsize = sizeof(PyUIGridPointObject), -// .tp_itemsize = 0, -// // Methods omitted for brevity -// .tp_flags = Py_TPFLAGS_DEFAULT, -// .tp_doc = "UIGridPoint objects", -// .tp_getset = PyUIGridPoint_getsetters, -// //.tp_init = (initproc)PyUIGridPoint_init, // TODO Define the init function -// .tp_new = PyType_GenericNew, -//}; -// -// /* -// * -// * end PyUIGridPoint defs -// * -// */ -// -// -// -// /* -// * -// * PyUIGridPointState defs -// * -// */ -// -//static PyObject* PyUIGridPointState_get_bool_member(PyUIGridPointStateObject* self, void* closure) { -// if (reinterpret_cast(closure) == 0) { // visible -// return PyBool_FromLong(self->data->visible); -// } else { // discovered -// return PyBool_FromLong(self->data->discovered); -// } -//} -// -//static int PyUIGridPointState_set_bool_member(PyUIGridPointStateObject* self, PyObject* value, void* closure) { -// if (!PyBool_Check(value)) { -// PyErr_SetString(PyExc_TypeError, "Value must be a boolean"); -// return -1; -// } -// -// int truthValue = PyObject_IsTrue(value); -// if (truthValue < 0) { -// return -1; // PyObject_IsTrue returns -1 on error -// } -// -// if (reinterpret_cast(closure) == 0) { // visible -// self->data->visible = truthValue; -// } else { // discovered -// self->data->discovered = truthValue; -// } -// -// return 0; -//} -// -//static PyGetSetDef PyUIGridPointState_getsetters[] = { -// {"visible", (getter)PyUIGridPointState_get_bool_member, (setter)PyUIGridPointState_set_bool_member, "Is the GridPointState visible", (void*)0}, -// {"discovered", (getter)PyUIGridPointState_get_bool_member, (setter)PyUIGridPointState_set_bool_member, "Has the GridPointState been discovered", (void*)1}, -// {NULL} /* Sentinel */ -//}; -// -//static PyTypeObject PyUIGridPointStateType = { -// //PyVarObject_HEAD_INIT(NULL, 0) -// .tp_name = "mcrfpy.GridPointState", -// .tp_basicsize = sizeof(PyUIGridPointStateObject), -// .tp_itemsize = 0, -// // Methods omitted for brevity -// .tp_flags = Py_TPFLAGS_DEFAULT, -// .tp_doc = "UIGridPointState objects", // TODO: Add PyUIGridPointState tp_init -// .tp_getset = PyUIGridPointState_getsetters, -// .tp_new = PyType_GenericNew, -//}; -// -// -// /* -// * -// * end PyUIGridPointState defs -// * -// */ -// -// /* -// * -// * PyUIEntity defs -// * -// */ -// -// TODO: sf::Vector2f convenience functions here might benefit from a PyVectorObject much like PyColorObject -// Utility function to convert sf::Vector2f to PyObject* -//static PyObject* sfVector2f_to_PyObject(sf::Vector2f vector) { -// return Py_BuildValue("(ff)", vector.x, vector.y); -//} -// -// Utility function to convert PyObject* to sf::Vector2f -//static sf::Vector2f PyObject_to_sfVector2f(PyObject* obj) { -// float x, y; -// if (!PyArg_ParseTuple(obj, "ff", &x, &y)) { -// return sf::Vector2f(); // TODO / reconsider this default: Return default vector on parse error -// } -// return sf::Vector2f(x, y); -//} -// -// Utility function to convert UIGridPointState to PyObject* -//static PyObject* UIGridPointState_to_PyObject(const UIGridPointState& state) { -// PyObject* obj = PyObject_New(PyObject, &PyUIGridPointStateType); -// if (!obj) return PyErr_NoMemory(); -// -// // Assuming PyUIGridPointStateObject structure has a UIGridPointState* member called 'data' -// //((PyUIGridPointStateObject*)obj)->data = new UIGridPointState(state); // Copy constructor // wontimplement / feat - don't use new, get shared_ptr working -// -// return obj; -//} -// -// Function to convert std::vector to a Python list TODO need a PyUICollection style iterable -//static PyObject* UIGridPointStateVector_to_PyList(const std::vector& vec) { -// PyObject* list = PyList_New(vec.size()); -// if (!list) return PyErr_NoMemory(); -// -// for (size_t i = 0; i < vec.size(); ++i) { -// PyObject* obj = UIGridPointState_to_PyObject(vec[i]); -// if (!obj) { // Cleanup on failure -// Py_DECREF(list); -// return NULL; -// } -// PyList_SET_ITEM(list, i, obj); // This steals a reference to obj -// } -// -// return list; -//} -// -//static PyObject* PyUIEntity_get_position(PyUIEntityObject* self, void* closure) { -// return sfVector2f_to_PyObject(self->data->position); -//} -// -//static int PyUIEntity_set_position(PyUIEntityObject* self, PyObject* value, void* closure) { -// self->data->position = PyObject_to_sfVector2f(value); -// return 0; -//} -// -//static PyObject* PyUIEntity_get_gridstate(PyUIEntityObject* self, void* closure) { -// // Assuming a function to convert std::vector to PyObject* list -// return UIGridPointStateVector_to_PyList(self->data->gridstate); -//} -// -//static PyObject* PyUIEntity_get_spritenumber(PyUIEntityObject* self, void* closure) { -// return PyLong_FromDouble(self->data->sprite.getSpriteIndex()); -//} -// -//static int PyUIEntity_set_spritenumber(PyUIEntityObject* self, PyObject* value, void* closure) { -// int val; -// if (PyLong_Check(value)) -// val = PyLong_AsLong(value); -// else -// { -// PyErr_SetString(PyExc_TypeError, "Value must be an integer."); -// return -1; -// } -// //self->data->sprite.sprite_index = val; -// self->data->sprite.setSpriteIndex(val); // todone - I don't like ".sprite.sprite" in this stack of UIEntity.UISprite.sf::Sprite -// return 0; -//} -// -//static PyObject* PyUIEntity_at(PyUIEntityObject* self, PyObject* o) -//{ -// int x, y; -// if (!PyArg_ParseTuple(o, "ii", &x, &y)) { -// PyErr_SetString(PyExc_TypeError, "UIEntity.at requires two integer arguments: (x, y)"); -// return NULL; -// } -// -// if (self->data->grid == NULL) { -// PyErr_SetString(PyExc_ValueError, "Entity cannot access surroundings because it is not associated with a grid"); -// return NULL; -// } -// -// PyUIGridPointStateObject* obj = (PyUIGridPointStateObject*)((&PyUIGridPointStateType)->tp_alloc(&PyUIGridPointStateType, 0)); -// //auto target = std::static_pointer_cast(target); -// obj->data = &(self->data->gridstate[y + self->data->grid->grid_x * x]); -// obj->grid = self->data->grid; -// obj->entity = self->data; -// return (PyObject*)obj; -//} -// -//static PyMethodDef PyUIEntity_methods[] = { -// {"at", (PyCFunction)PyUIEntity_at, METH_O}, -// {NULL, NULL, 0, NULL} -//}; -// -// Define getters and setters -//static PyGetSetDef PyUIEntity_getsetters[] = { -// {"position", (getter)PyUIEntity_get_position, (setter)PyUIEntity_set_position, "Entity position", NULL}, -// {"gridstate", (getter)PyUIEntity_get_gridstate, NULL, "Grid point states for the entity", NULL}, -// {"sprite_number", (getter)PyUIEntity_get_spritenumber, (setter)PyUIEntity_set_spritenumber, "Sprite number (index) on the texture on the display", NULL}, -// {NULL} /* Sentinel */ -//}; -// -//static int PyUIEntity_init(PyUIEntityObject*, PyObject*, PyObject*); // forward declare -// -// -// -// Define the PyTypeObject for UIEntity -//static PyTypeObject PyUIEntityType = { -// //PyVarObject_HEAD_INIT(NULL, 0) -// .tp_name = "mcrfpy.Entity", -// .tp_basicsize = sizeof(PyUIEntityObject), -// .tp_itemsize = 0, -// // Methods omitted for brevity -// .tp_flags = Py_TPFLAGS_DEFAULT, -// .tp_doc = "UIEntity objects", -// .tp_methods = PyUIEntity_methods, -// .tp_getset = PyUIEntity_getsetters, -// .tp_init = (initproc)PyUIEntity_init, -// .tp_new = PyType_GenericNew, -//}; -// -// -// /* -// * -// * end PyUIEntity defs -// * -// */ -// -// /* -// * -// * PyUIGrid defs -// * -// */ -// -//static int PyUIGrid_init(PyUIGridObject* self, PyObject* args, PyObject* kwds) { -// int grid_x, grid_y; -// PyObject* textureObj; -// float box_x, box_y, box_w, box_h; -// -// if (!PyArg_ParseTuple(args, "iiOffff", &grid_x, &grid_y, &textureObj, &box_x, &box_y, &box_w, &box_h)) { -// return -1; // If parsing fails, return an error -// } -// -// // Convert PyObject texture to IndexTexture* -// // This requires the texture object to have been initialized similar to UISprite's texture handling -// if (!PyObject_IsInstance(textureObj, (PyObject*)&PyTextureType)) { -// PyErr_SetString(PyExc_TypeError, "texture must be a mcrfpy.Texture instance"); -// return -1; -// } -// PyTextureObject* pyTexture = reinterpret_cast(textureObj); -// // TODO (7DRL day 2, item 4.) use shared_ptr / PyTextureObject on UIGrid -// //IndexTexture* texture = pyTexture->data.get(); -// -// // Initialize UIGrid -// //self->data = new UIGrid(grid_x, grid_y, texture, sf::Vector2f(box_x, box_y), sf::Vector2f(box_w, box_h)); -// self->data = std::make_shared(grid_x, grid_y, pyTexture->data, -// sf::Vector2f(box_x, box_y), sf::Vector2f(box_w, box_h)); -// return 0; // Success -//} -// -//static PyObject* PyUIGrid_get_grid_size(PyUIGridObject* self, void* closure) { -// return Py_BuildValue("(ii)", self->data->grid_x, self->data->grid_y); -//} -// -//static PyObject* PyUIGrid_get_position(PyUIGridObject* self, void* closure) { -// auto& box = self->data->box; -// return Py_BuildValue("(ff)", box.getPosition().x, box.getPosition().y); -//} -// -//static int PyUIGrid_set_position(PyUIGridObject* self, PyObject* value, void* closure) { -// float x, y; -// if (!PyArg_ParseTuple(value, "ff", &x, &y)) { -// PyErr_SetString(PyExc_ValueError, "Position must be a tuple of two floats"); -// return -1; -// } -// self->data->box.setPosition(x, y); -// return 0; -//} -// -//static PyObject* PyUIGrid_get_size(PyUIGridObject* self, void* closure) { -// auto& box = self->data->box; -// return Py_BuildValue("(ff)", box.getSize().x, box.getSize().y); -//} -// -//static int PyUIGrid_set_size(PyUIGridObject* self, PyObject* value, void* closure) { -// float w, h; -// if (!PyArg_ParseTuple(value, "ff", &w, &h)) { -// PyErr_SetString(PyExc_ValueError, "Size must be a tuple of two floats"); -// return -1; -// } -// self->data->box.setSize(sf::Vector2f(w, h)); -// return 0; -//} -// -//static PyObject* PyUIGrid_get_center(PyUIGridObject* self, void* closure) { -// return Py_BuildValue("(ff)", self->data->center_x, self->data->center_y); -//} -// -//static int PyUIGrid_set_center(PyUIGridObject* self, PyObject* value, void* closure) { -// float x, y; -// if (!PyArg_ParseTuple(value, "ff", &x, &y)) { -// PyErr_SetString(PyExc_ValueError, "Size must be a tuple of two floats"); -// return -1; -// } -// self->data->center_x = x; -// self->data->center_y = y; -// return 0; -//} -// -//static PyObject* PyUIGrid_get_float_member(PyUIGridObject* self, void* closure) -//{ -// auto member_ptr = reinterpret_cast(closure); -// if (member_ptr == 0) // x -// return PyFloat_FromDouble(self->data->box.getPosition().x); -// else if (member_ptr == 1) // y -// return PyFloat_FromDouble(self->data->box.getPosition().y); -// else if (member_ptr == 2) // w -// return PyFloat_FromDouble(self->data->box.getSize().x); -// else if (member_ptr == 3) // h -// return PyFloat_FromDouble(self->data->box.getSize().y); -// else if (member_ptr == 4) // center_x -// return PyFloat_FromDouble(self->data->center_x); -// else if (member_ptr == 5) // center_y -// return PyFloat_FromDouble(self->data->center_y); -// else if (member_ptr == 6) // zoom -// return PyFloat_FromDouble(self->data->zoom); -// else -// { -// PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); -// return nullptr; -// } -//} -// -// -//static int PyUIGrid_set_float_member(PyUIGridObject* self, PyObject* value, void* closure) -//{ -// float val; -// auto member_ptr = reinterpret_cast(closure); -// if (PyFloat_Check(value)) -// { -// val = PyFloat_AsDouble(value); -// } -// else if (PyLong_Check(value)) -// { -// val = PyLong_AsLong(value); -// } -// else -// { -// PyErr_SetString(PyExc_TypeError, "Value must be a floating point number."); -// return -1; -// } -// if (member_ptr == 0) // x -// self->data->box.setPosition(val, self->data->box.getPosition().y); -// else if (member_ptr == 1) // y -// self->data->box.setPosition(self->data->box.getPosition().x, val); -// else if (member_ptr == 2) // w -// self->data->box.setSize(sf::Vector2f(val, self->data->box.getSize().y)); -// else if (member_ptr == 3) // h -// self->data->box.setSize(sf::Vector2f(self->data->box.getSize().x, val)); -// else if (member_ptr == 4) // center_x -// self->data->center_x = val; -// else if (member_ptr == 5) // center_y -// self->data->center_y = val; -// else if (member_ptr == 6) // zoom -// self->data->zoom = val; -// return 0; -//} -// TODO (7DRL Day 2, item 5.) return Texture object -///* -//static PyObject* PyUIGrid_get_texture(PyUIGridObject* self, void* closure) { -// Py_INCREF(self->texture); -// return self->texture; -//} -//*/ -//static PyObject* PyUIGrid_get_texture(PyUIGridObject* self, void* closure) { -// //return self->data->getTexture()->pyObject(); -// PyTextureObject* obj = (PyTextureObject*)((&PyTextureType)->tp_alloc(&PyTextureType, 0)); -// obj->data = self->data->getTexture(); -// return (PyObject*)obj; -//} -// -//static PyObject* PyUIGrid_at(PyUIGridObject* self, PyObject* o) -//{ -// int x, y; -// if (!PyArg_ParseTuple(o, "ii", &x, &y)) { -// PyErr_SetString(PyExc_TypeError, "UIGrid.at requires two integer arguments: (x, y)"); -// return NULL; -// } -// if (x < 0 || x >= self->data->grid_x) { -// PyErr_SetString(PyExc_ValueError, "x value out of range (0, Grid.grid_y)"); -// return NULL; -// } -// if (y < 0 || y >= self->data->grid_y) { -// PyErr_SetString(PyExc_ValueError, "y value out of range (0, Grid.grid_y)"); -// return NULL; -// } -// -// PyUIGridPointObject* obj = (PyUIGridPointObject*)((&PyUIGridPointType)->tp_alloc(&PyUIGridPointType, 0)); -// //auto target = std::static_pointer_cast(target); -// obj->data = &(self->data->points[x + self->data->grid_x * y]); -// obj->grid = self->data; -// return (PyObject*)obj; -//} -// -//static PyMethodDef PyUIGrid_methods[] = { -// {"at", (PyCFunction)PyUIGrid_at, METH_O}, -// {NULL, NULL, 0, NULL} -//}; -// -//static PyObject* PyUIGrid_get_children(PyUIGridObject* self, void* closure); // forward declare -// -//static PyGetSetDef PyUIGrid_getsetters[] = { -// -// // TODO - refactor into get_vector_member with field identifier values `(void*)n` -// {"grid_size", (getter)PyUIGrid_get_grid_size, NULL, "Grid dimensions (grid_x, grid_y)", NULL}, -// {"position", (getter)PyUIGrid_get_position, (setter)PyUIGrid_set_position, "Position of the grid (x, y)", NULL}, -// {"size", (getter)PyUIGrid_get_size, (setter)PyUIGrid_set_size, "Size of the grid (width, height)", NULL}, -// {"center", (getter)PyUIGrid_get_center, (setter)PyUIGrid_set_center, "Grid coordinate at the center of the Grid's view (pan)", NULL}, -// -// {"entities", (getter)PyUIGrid_get_children, NULL, "EntityCollection of entities on this grid", NULL}, -// -// {"x", (getter)PyUIGrid_get_float_member, (setter)PyUIGrid_set_float_member, "top-left corner X-coordinate", (void*)0}, -// {"y", (getter)PyUIGrid_get_float_member, (setter)PyUIGrid_set_float_member, "top-left corner Y-coordinate", (void*)1}, -// {"w", (getter)PyUIGrid_get_float_member, (setter)PyUIGrid_set_float_member, "visible widget width", (void*)2}, -// {"h", (getter)PyUIGrid_get_float_member, (setter)PyUIGrid_set_float_member, "visible widget height", (void*)3}, -// {"center_x", (getter)PyUIGrid_get_float_member, (setter)PyUIGrid_set_float_member, "center of the view X-coordinate", (void*)4}, -// {"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 */ -//}; -// -// -///* // TODO standard pointer would need deleted, but I opted for a shared pointer. tp_dealloc currently not even defined in the PyTypeObject -//static void PyUIGrid_dealloc(PyUIGridObject* self) { -// delete self->data; // Clean up the allocated UIGrid object -// Py_TYPE(self)->tp_free((PyObject*)self); -//} -//*/ -// -// static PyTypeObject PyUIGridType = { -// //PyVarObject_HEAD_INIT(NULL, 0) -// .tp_name = "mcrfpy.Grid", -// .tp_basicsize = sizeof(PyUIGridObject), -// .tp_itemsize = 0, -// //.tp_dealloc = (destructor)[](PyObject* self) -// //{ -// // PyUIGridObject* obj = (PyUIGridObject*)self; -// // obj->data.reset(); -// // Py_TYPE(self)->tp_free(self); -// //}, -// //TODO - PyUIGrid REPR def: -// // .tp_repr = (reprfunc)PyUIGrid_repr, -// //.tp_hash = NULL, -// //.tp_iter -// //.tp_iternext -// .tp_flags = Py_TPFLAGS_DEFAULT, -// .tp_doc = PyDoc_STR("docstring"), -// .tp_methods = PyUIGrid_methods, -// //.tp_members = PyUIGrid_members, -// .tp_getset = PyUIGrid_getsetters, -// //.tp_base = NULL, -// .tp_init = (initproc)PyUIGrid_init, -// .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* -// { -// PyUIGridObject* self = (PyUIGridObject*)type->tp_alloc(type, 0); -// if (self) self->data = std::make_shared(); -// return (PyObject*)self; -// } -// }; -// -// -// /* -// * -// * end PyUIGrid defs -// * -// */ -// -// PyUIEntity_init defined here because it depends on the PyUIGridType (to accept grid optional keyword argument) -//static int PyUIEntity_init(PyUIEntityObject* self, PyObject* args, PyObject* kwds) { -// static const char* keywords[] = { "x", "y", "texture", "sprite_index", "grid", nullptr }; -// float x = 0.0f, y = 0.0f, scale = 1.0f; -// int sprite_index = -1; -// PyObject* texture = NULL; -// PyObject* grid = NULL; -// -// if (!PyArg_ParseTupleAndKeywords(args, kwds, "ffOi|O", -// const_cast(keywords), &x, &y, &texture, &sprite_index, &grid)) -// { -// return -1; -// } -// -// // check types for texture -// // -// // Set Texture -// // -// if (texture != NULL && !PyObject_IsInstance(texture, (PyObject*)&PyTextureType)){ -// PyErr_SetString(PyExc_TypeError, "texture must be a mcrfpy.Texture instance"); -// return -1; -// } /*else if (texture != NULL) // this section needs to go; texture isn't optional and isn't managed by the UI objects anymore -// { -// self->texture = texture; -// Py_INCREF(texture); -// } else -// { -// // default tex? -// }*/ -// -// if (grid != NULL && !PyObject_IsInstance(grid, (PyObject*)&PyUIGridType)) { -// PyErr_SetString(PyExc_TypeError, "grid must be a mcrfpy.Grid instance"); -// return -1; -// } -// -// auto pytexture = (PyTextureObject*)texture; -// if (grid == NULL) -// self->data = std::make_shared(); -// else -// self->data = std::make_shared(*((PyUIGridObject*)grid)->data); -// -// // TODO - PyTextureObjects and IndexTextures are a little bit of a mess with shared/unshared pointers -// self->data->sprite = UISprite(pytexture->data, sprite_index, sf::Vector2f(0,0), 1.0); -// self->data->position = sf::Vector2f(x, y); -// if (grid != NULL) { -// PyUIGridObject* pygrid = (PyUIGridObject*)grid; -// self->data->grid = pygrid->data; -// // todone - on creation of Entity with Grid assignment, also append it to the entity list -// pygrid->data->entities->push_back(self->data); -// } -// return 0; -//} -// -///* -// * -// * Begin PyUIEntityCollectionIter defs -// * -// */ -// typedef struct { -// PyObject_HEAD -// std::shared_ptr>> data; -// int index; -// int start_size; -// } PyUIEntityCollectionIterObject; -// -// static int PyUIEntityCollectionIter_init(PyUIEntityCollectionIterObject* self, PyObject* args, PyObject* kwds) -// { -// PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required."); -// return -1; -// } -// -// static PyObject* PyUIEntityCollectionIter_next(PyUIEntityCollectionIterObject* self) -// { -// if (self->data->size() != self->start_size) -// { -// PyErr_SetString(PyExc_RuntimeError, "collection changed size during iteration"); -// return NULL; -// } -// -// if (self->index > self->start_size - 1) -// { -// PyErr_SetNone(PyExc_StopIteration); -// return NULL; -// } -// self->index++; -// auto vec = self->data.get(); -// if (!vec) -// { -// PyErr_SetString(PyExc_RuntimeError, "the collection store returned a null pointer"); -// return NULL; -// } -// // Advance list iterator since Entities are not stored in a vector (if this code even worked) -// // vectors only: //auto target = (*vec)[self->index-1]; -// //auto l_front = (*vec).begin(); -// //std::advance(l_front, self->index-1); -// -// // TODO build PyObject* of the correct UIDrawable subclass to return -// //return py_instance(target); -// return NULL; -// } -// -// static PyObject* PyUIEntityCollectionIter_repr(PyUIEntityCollectionIterObject* self) -// { -// std::ostringstream ss; -// if (!self->data) ss << ""; -// else { -// ss << "data->size() << " child objects, @ index " << self->index << ")>"; -// } -// std::string repr_str = ss.str(); -// return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); -// } -// static PyTypeObject PyUIEntityCollectionIterType = { -// //PyVarObject_HEAD_INIT(NULL, 0) -// .tp_name = "mcrfpy.UICollectionIter", -// .tp_basicsize = sizeof(PyUIEntityCollectionIterObject), -// .tp_itemsize = 0, -// .tp_dealloc = (destructor)[](PyObject* self) -// { -// PyUIEntityCollectionIterObject* obj = (PyUIEntityCollectionIterObject*)self; -// obj->data.reset(); -// Py_TYPE(self)->tp_free(self); -// }, -// .tp_repr = (reprfunc)PyUIEntityCollectionIter_repr, -// .tp_flags = Py_TPFLAGS_DEFAULT, -// .tp_doc = PyDoc_STR("Iterator for a collection of UI objects"), -// .tp_iternext = (iternextfunc)PyUIEntityCollectionIter_next, -// //.tp_getset = PyUIEntityCollection_getset, -// .tp_init = (initproc)PyUIEntityCollectionIter_init, // just raise an exception -// .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* -// { -// PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required."); -// return NULL; -// } -// }; -// -// /* -// * -// * End PyUIEntityCollectionIter defs -// * -// */ -// -///* -// * -// * Begin PyUIEntityCollection defs -// * -// */ -// typedef struct { -// PyObject_HEAD -// std::shared_ptr>> data; -// std::shared_ptr grid; -// } PyUIEntityCollectionObject; -// -// static Py_ssize_t PyUIEntityCollection_len(PyUIEntityCollectionObject* self) { -// return self->data->size(); -// } -// -// static PyObject* PyUIEntityCollection_getitem(PyUIEntityCollectionObject* self, Py_ssize_t index) { -// // build a Python version of item at self->data[index] -// // Copy pasted:: -// auto vec = self->data.get(); -// if (!vec) -// { -// PyErr_SetString(PyExc_RuntimeError, "the collection store returned a null pointer"); -// return NULL; -// } -// while (index < 0) index += self->data->size(); -// if (index > self->data->size() - 1) -// { -// PyErr_SetString(PyExc_IndexError, "EntityCollection index out of range"); -// return NULL; -// } -// auto l_begin = (*vec).begin(); -// std::advance(l_begin, index); -// auto target = *l_begin; //auto target = (*vec)[index]; -// //RET_PY_INSTANCE(target); -// // construct and return an entity object that points directly into the UIGrid's entity vector -// PyUIEntityObject* o = (PyUIEntityObject*)((&PyUIEntityType)->tp_alloc(&PyUIEntityType, 0)); -// auto p = std::static_pointer_cast(target); -// o->data = p; -// return (PyObject*)o; -// return NULL; -// -// -// } -// -// static PySequenceMethods PyUIEntityCollection_sqmethods = { -// .sq_length = (lenfunc)PyUIEntityCollection_len, -// .sq_item = (ssizeargfunc)PyUIEntityCollection_getitem, -// //.sq_item_by_index = PyUIEntityCollection_getitem -// //.sq_slice - return a subset of the iterable -// //.sq_ass_item - called when `o[x] = y` is executed (x is any object type) -// //.sq_ass_slice - cool; no thanks, for now -// //.sq_contains - called when `x in o` is executed -// //.sq_ass_item_by_index - called when `o[x] = y` is executed (x is explictly an integer) -// }; -// -// static PyObject* PyUIEntityCollection_append(PyUIEntityCollectionObject* self, PyObject* o) -// { -// // if not UIDrawable subclass, reject it -// // self->data->push_back( c++ object inside o ); -// -// // this would be a great use case for .tp_base -// if (!PyObject_IsInstance(o, (PyObject*)&PyUIEntityType)) -// { -// PyErr_SetString(PyExc_TypeError, "Only Entity objects can be added to EntityCollection"); -// return NULL; -// } -// PyUIEntityObject* entity = (PyUIEntityObject*)o; -// self->data->push_back(entity->data); -// entity->data->grid = self->grid; -// -// Py_INCREF(Py_None); -// return Py_None; -// } -// static PyObject* PyUIEntityCollection_remove(PyUIEntityCollectionObject* self, PyObject* o) -// { -// if (!PyLong_Check(o)) -// { -// PyErr_SetString(PyExc_TypeError, "UICollection.remove requires an integer index to remove"); -// return NULL; -// } -// long index = PyLong_AsLong(o); -// if (index >= self->data->size()) -// { -// PyErr_SetString(PyExc_ValueError, "Index out of range"); -// return NULL; -// } -// else if (index < 0) -// { -// PyErr_SetString(PyExc_NotImplementedError, "reverse indexing is not implemented."); -// return NULL; -// } -// -// // release the shared pointer at correct part of the list -// self->data->erase(std::next(self->data->begin(), index)); -// Py_INCREF(Py_None); -// return Py_None; -// } -// -// static PyMethodDef PyUIEntityCollection_methods[] = { -// {"append", (PyCFunction)PyUIEntityCollection_append, METH_O}, -// //{"extend", (PyCFunction)PyUIEntityCollection_extend, METH_O}, // TODO -// {"remove", (PyCFunction)PyUIEntityCollection_remove, METH_O}, -// {NULL, NULL, 0, NULL} -// }; -// -// static PyObject* PyUIEntityCollection_repr(PyUIEntityCollectionObject* self) -// { -// std::ostringstream ss; -// if (!self->data) ss << ""; -// else { -// ss << "data->size() << " child objects)>"; -// } -// std::string repr_str = ss.str(); -// return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); -// } -// -// static int PyUIEntityCollection_init(PyUIEntityCollectionObject* self, PyObject* args, PyObject* kwds) -// { -// PyErr_SetString(PyExc_TypeError, "EntityCollection cannot be instantiated: a C++ data source is required."); -// return -1; -// } -// -// static PyObject* PyUIEntityCollection_iter(PyUIEntityCollectionObject* self) -// { -// PyUIEntityCollectionIterObject* iterObj; -// iterObj = (PyUIEntityCollectionIterObject*)PyUIEntityCollectionIterType.tp_alloc(&PyUIEntityCollectionIterType, 0); -// if (iterObj == NULL) { -// return NULL; // Failed to allocate memory for the iterator object -// } -// -// iterObj->data = self->data; -// iterObj->index = 0; -// iterObj->start_size = self->data->size(); -// -// return (PyObject*)iterObj; -// } -// static PyTypeObject PyUIEntityCollectionType = { -// //PyVarObject_/HEAD_INIT(NULL, 0) -// .tp_name = "mcrfpy.EntityCollection", -// .tp_basicsize = sizeof(PyUIEntityCollectionObject), -// .tp_itemsize = 0, -// .tp_dealloc = (destructor)[](PyObject* self) -// { -// PyUIEntityCollectionObject* obj = (PyUIEntityCollectionObject*)self; -// obj->data.reset(); -// Py_TYPE(self)->tp_free(self); -// }, -// .tp_repr = (reprfunc)PyUIEntityCollection_repr, -// .tp_as_sequence = &PyUIEntityCollection_sqmethods, -// .tp_flags = Py_TPFLAGS_DEFAULT, -// .tp_doc = PyDoc_STR("Iterable, indexable collection of Entities"), -// .tp_iter = (getiterfunc)PyUIEntityCollection_iter, -// .tp_methods = PyUIEntityCollection_methods, // append, remove -// //.tp_getset = PyUIEntityCollection_getset, -// .tp_init = (initproc)PyUIEntityCollection_init, // just raise an exception -// .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* -// { -// // Does PyUIEntityCollectionType need __new__ if it's not supposed to be instantiable by the user? -// // Should I just raise an exception? Or is the uninitialized shared_ptr enough of a blocker? -// PyErr_SetString(PyExc_TypeError, "EntityCollection cannot be instantiated: a C++ data source is required."); -// return NULL; -// } -// }; -// -// // Grid's get_children def must follow the EntityCollection def -// static PyObject* PyUIGrid_get_children(PyUIGridObject* self, void* closure) -// { -// // create PyUICollection instance pointing to self->data->children -// PyUIEntityCollectionObject* o = (PyUIEntityCollectionObject*)PyUIEntityCollectionType.tp_alloc(&PyUIEntityCollectionType, 0); -// if (o) { -// o->data = self->data->entities; // todone. / BUGFIX - entities isn't a shared pointer on UIGrid, what to do? -- I made it a sp>> -// o->grid = self->data; -// } -// return (PyObject*)o; -// } -// -// /* -// * -// * End PyUIEntityCollection defs -// * -// */ -// -// -// -// /* -// * -// * Begin PyUICollectionIter defs -// * -// */ -// typedef struct { -// PyObject_HEAD -// std::shared_ptr>> data; -// int index; -// int start_size; -// } PyUICollectionIterObject; -// -// static int PyUICollectionIter_init(PyUICollectionIterObject* self, PyObject* args, PyObject* kwds) -// { -// PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required."); -// return -1; -// } -// -// static PyObject* PyUICollectionIter_next(PyUICollectionIterObject* self) -// { -// if (self->data->size() != self->start_size) -// { -// PyErr_SetString(PyExc_RuntimeError, "collection changed size during iteration"); -// return NULL; -// } -// -// if (self->index > self->start_size - 1) -// { -// PyErr_SetNone(PyExc_StopIteration); -// return NULL; -// } -// self->index++; -// auto vec = self->data.get(); -// if (!vec) -// { -// PyErr_SetString(PyExc_RuntimeError, "the collection store returned a null pointer"); -// return NULL; -// } -// auto target = (*vec)[self->index-1]; -// // TODO build PyObject* of the correct UIDrawable subclass to return -// //return py_instance(target); -// return NULL; -// } -// -// static PyObject* PyUICollectionIter_repr(PyUICollectionIterObject* self) -// { -// std::ostringstream ss; -// if (!self->data) ss << ""; -// else { -// ss << "data->size() << " child objects, @ index " << self->index << ")>"; -// } -// std::string repr_str = ss.str(); -// return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); -// } -// -// static PyTypeObject PyUICollectionIterType = { -// //PyVarObject_HEAD_INIT(NULL, 0) -// .tp_name = "mcrfpy.UICollectionIter", -// .tp_basicsize = sizeof(PyUICollectionIterObject), -// .tp_itemsize = 0, -// .tp_dealloc = (destructor)[](PyObject* self) -// { -// PyUICollectionIterObject* obj = (PyUICollectionIterObject*)self; -// obj->data.reset(); -// Py_TYPE(self)->tp_free(self); -// }, -// .tp_repr = (reprfunc)PyUICollectionIter_repr, -// .tp_flags = Py_TPFLAGS_DEFAULT, -// .tp_doc = PyDoc_STR("Iterator for a collection of UI objects"), -// .tp_iternext = (iternextfunc)PyUICollectionIter_next, -// //.tp_getset = PyUICollection_getset, -// .tp_init = (initproc)PyUICollectionIter_init, // just raise an exception -// .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* -// { -// PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required."); -// return NULL; -// } -// }; -// -// /* -// * -// * End PyUICollectionIter defs -// * -// */ -// -// -// /* -// * -// * Begin PyUICollection defs -// * -// */ -// -// static Py_ssize_t PyUICollection_len(PyUICollectionObject* self) { -// return self->data->size(); -// } -// -// static PyObject* PyUICollection_getitem(PyUICollectionObject* self, Py_ssize_t index) { -// // build a Python version of item at self->data[index] -// // Copy pasted:: -// auto vec = self->data.get(); -// if (!vec) -// { -// PyErr_SetString(PyExc_RuntimeError, "the collection store returned a null pointer"); -// return NULL; -// } -// while (index < 0) index += self->data->size(); -// if (index > self->data->size() - 1) -// { -// PyErr_SetString(PyExc_IndexError, "UICollection index out of range"); -// return NULL; -// } -// auto target = (*vec)[index]; -// RET_PY_INSTANCE(target); -// return NULL; -// -// -// } -// -// static PySequenceMethods PyUICollection_sqmethods = { -// .sq_length = (lenfunc)PyUICollection_len, -// .sq_item = (ssizeargfunc)PyUICollection_getitem, -// //.sq_item_by_index = PyUICollection_getitem -// //.sq_slice - return a subset of the iterable -// //.sq_ass_item - called when `o[x] = y` is executed (x is any object type) -// //.sq_ass_slice - cool; no thanks, for now -// //.sq_contains - called when `x in o` is executed -// //.sq_ass_item_by_index - called when `o[x] = y` is executed (x is explictly an integer) -// }; -// -// static PyObject* PyUICollection_append(PyUICollectionObject* self, PyObject* o) -// { -// // if not UIDrawable subclass, reject it -// // self->data->push_back( c++ object inside o ); -// -// // this would be a great use case for .tp_base -// if (!PyObject_IsInstance(o, (PyObject*)&PyUIFrameType) && -// !PyObject_IsInstance(o, (PyObject*)&PyUISpriteType) && -// !PyObject_IsInstance(o, (PyObject*)&PyUICaptionType) && -// !PyObject_IsInstance(o, (PyObject*)&PyUIGridType) -// ) -// { -// PyErr_SetString(PyExc_TypeError, "Only Frame, Caption, Sprite, and Grid objects can be added to UICollection"); -// return NULL; -// } -// -// if (PyObject_IsInstance(o, (PyObject*)&PyUIFrameType)) -// { -// PyUIFrameObject* frame = (PyUIFrameObject*)o; -// self->data->push_back(frame->data); -// } -// if (PyObject_IsInstance(o, (PyObject*)&PyUICaptionType)) -// { -// PyUICaptionObject* caption = (PyUICaptionObject*)o; -// self->data->push_back(caption->data); -// } -// if (PyObject_IsInstance(o, (PyObject*)&PyUISpriteType)) -// { -// PyUISpriteObject* sprite = (PyUISpriteObject*)o; -// self->data->push_back(sprite->data); -// } -// if (PyObject_IsInstance(o, (PyObject*)&PyUIGridType)) -// { -// PyUIGridObject* grid = (PyUIGridObject*)o; -// self->data->push_back(grid->data); -// } -// -// Py_INCREF(Py_None); -// return Py_None; -// } -// -// static PyObject* PyUICollection_remove(PyUICollectionObject* self, PyObject* o) -// { -// if (!PyLong_Check(o)) -// { -// PyErr_SetString(PyExc_TypeError, "UICollection.remove requires an integer index to remove"); -// return NULL; -// } -// long index = PyLong_AsLong(o); -// if (index >= self->data->size()) -// { -// PyErr_SetString(PyExc_ValueError, "Index out of range"); -// return NULL; -// } -// else if (index < 0) -// { -// PyErr_SetString(PyExc_NotImplementedError, "reverse indexing is not implemented."); -// return NULL; -// } -// -// // release the shared pointer at self->data[index]; -// self->data->erase(self->data->begin() + index); -// Py_INCREF(Py_None); -// return Py_None; -// } -// -// static PyMethodDef PyUICollection_methods[] = { -// {"append", (PyCFunction)PyUICollection_append, METH_O}, -// //{"extend", (PyCFunction)PyUICollection_extend, METH_O}, // TODO -// {"remove", (PyCFunction)PyUICollection_remove, METH_O}, -// {NULL, NULL, 0, NULL} -// }; -// -// static PyObject* PyUICollection_repr(PyUICollectionObject* self) -// { -// std::ostringstream ss; -// if (!self->data) ss << ""; -// else { -// ss << "data->size() << " child objects)>"; -// } -// std::string repr_str = ss.str(); -// return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); -// } -// -// static int PyUICollection_init(PyUICollectionObject* self, PyObject* args, PyObject* kwds) -// { -// PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required."); -// return -1; -// } -// -// static PyObject* PyUICollection_iter(PyUICollectionObject* self) -// { -// PyUICollectionIterObject* iterObj; -// iterObj = (PyUICollectionIterObject*)PyUICollectionIterType.tp_alloc(&PyUICollectionIterType, 0); -// if (iterObj == NULL) { -// return NULL; // Failed to allocate memory for the iterator object -// } -// -// iterObj->data = self->data; -// iterObj->index = 0; -// iterObj->start_size = self->data->size(); -// -// return (PyObject*)iterObj; -// } -// -// /* -// static PyGetSetDef PyUICollection_getsetters[] = { -// {NULL} -// }; -// */ -// -// static PyTypeObject PyUICollectionType = { -// //PyVarObject_/HEAD_INIT(NULL, 0) -// .tp_name = "mcrfpy.UICollection", -// .tp_basicsize = sizeof(PyUICollectionObject), -// .tp_itemsize = 0, -// .tp_dealloc = (destructor)[](PyObject* self) -// { -// PyUICollectionObject* obj = (PyUICollectionObject*)self; -// obj->data.reset(); -// Py_TYPE(self)->tp_free(self); -// }, -// .tp_repr = (reprfunc)PyUICollection_repr, -// .tp_as_sequence = &PyUICollection_sqmethods, -// .tp_flags = Py_TPFLAGS_DEFAULT, -// .tp_doc = PyDoc_STR("Iterable, indexable collection of UI objects"), -// .tp_iter = (getiterfunc)PyUICollection_iter, -// .tp_methods = PyUICollection_methods, // append, remove -// //.tp_getset = PyUICollection_getset, -// .tp_init = (initproc)PyUICollection_init, // just raise an exception -// .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* -// { -// // Does PyUICollectionType need __new__ if it's not supposed to be instantiable by the user? -// // Should I just raise an exception? Or is the uninitialized shared_ptr enough of a blocker? -// PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required."); -// return NULL; -// } -// }; -// /* -// * -// * End PyUICollection defs -// * -// */ -// -// static PyObject* PyUIFrame_get_children(PyUIFrameObject* self, void* closure) -// { -// // create PyUICollection instance pointing to self->data->children -// PyUICollectionObject* o = (PyUICollectionObject*)PyUICollectionType.tp_alloc(&PyUICollectionType, 0); -// if (o) -// o->data = self->data->children; -// return (PyObject*)o; -// } -// -// -//} // namespace mcrfpydef diff --git a/src/UICaption.cpp b/src/UICaption.cpp index 68f74ab..ffe3976 100644 --- a/src/UICaption.cpp +++ b/src/UICaption.cpp @@ -21,3 +21,238 @@ PyObjectsEnum UICaption::derived_type() { return PyObjectsEnum::UICAPTION; } + +PyObject* UICaption::get_float_member(PyUICaptionObject* self, void* closure) +{ + auto member_ptr = reinterpret_cast(closure); + if (member_ptr == 0) + return PyFloat_FromDouble(self->data->text.getPosition().x); + else if (member_ptr == 1) + return PyFloat_FromDouble(self->data->text.getPosition().y); + else if (member_ptr == 4) + return PyFloat_FromDouble(self->data->text.getOutlineThickness()); + else if (member_ptr == 5) + return PyLong_FromLong(self->data->text.getCharacterSize()); + else + { + PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); + return nullptr; + } +} + +int UICaption::set_float_member(PyUICaptionObject* self, PyObject* value, void* closure) +{ + float val; + auto member_ptr = reinterpret_cast(closure); + if (PyFloat_Check(value)) + { + val = PyFloat_AsDouble(value); + } + else if (PyLong_Check(value)) + { + val = PyLong_AsLong(value); + } + else + { + PyErr_SetString(PyExc_TypeError, "Value must be an integer."); + return -1; + } + if (member_ptr == 0) //x + self->data->text.setPosition(val, self->data->text.getPosition().y); + else if (member_ptr == 1) //y + self->data->text.setPosition(self->data->text.getPosition().x, val); + else if (member_ptr == 4) //outline + self->data->text.setOutlineThickness(val); + else if (member_ptr == 5) // character size + self->data->text.setCharacterSize(val); + return 0; +} + +PyObject* UICaption::get_vec_member(PyUICaptionObject* self, void* closure) +{ + return PyVector(self->data->text.getPosition()).pyObject(); +} + +int UICaption::set_vec_member(PyUICaptionObject* self, PyObject* value, void* closure) +{ + self->data->text.setPosition(PyVector::fromPy(value)); + return 0; +} + +PyObject* UICaption::get_color_member(PyUICaptionObject* self, void* closure) +{ + // TODO: migrate this code to a switch statement - validate closure & return values in one tighter, more extensible structure + + // validate closure (should be impossible to be wrong, but it's thorough) + auto member_ptr = reinterpret_cast(closure); + if (member_ptr != 0 && member_ptr != 1) + { + PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); + return nullptr; + } + + // TODO: manually calling tp_alloc to create a PyColorObject seems like an antipattern + // fetch correct member data + sf::Color color; + + if (member_ptr == 0) + { + color = self->data->text.getFillColor(); + } + else if (member_ptr == 1) + { + color = self->data->text.getOutlineColor(); + } + + return PyColor(color).pyObject(); +} + +int UICaption::set_color_member(PyUICaptionObject* self, PyObject* value, void* closure) +{ + auto member_ptr = reinterpret_cast(closure); + //TODO: this logic of (PyColor instance OR tuple -> sf::color) should be encapsulated for reuse + int r, g, b, a; + if (PyObject_IsInstance(value, (PyObject*)&mcrfpydef::PyColorType)) + { + // get value from mcrfpy.Color instance + auto c = ((PyColorObject*)value)->data; + r = c.r; g = c.g; b = c.b; a = c.a; + std::cout << "got " << int(r) << ", " << int(g) << ", " << int(b) << ", " << int(a) << std::endl; + } + else if (!PyTuple_Check(value) || PyTuple_Size(value) < 3 || PyTuple_Size(value) > 4) + { + // reject non-Color, non-tuple value + PyErr_SetString(PyExc_TypeError, "Value must be a tuple of 3 or 4 integers or an mcrfpy.Color object."); + return -1; + } + else // get value from tuples + { + r = PyLong_AsLong(PyTuple_GetItem(value, 0)); + g = PyLong_AsLong(PyTuple_GetItem(value, 1)); + b = PyLong_AsLong(PyTuple_GetItem(value, 2)); + a = 255; + + if (PyTuple_Size(value) == 4) + { + a = PyLong_AsLong(PyTuple_GetItem(value, 3)); + } + } + + if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255 || a < 0 || a > 255) + { + PyErr_SetString(PyExc_ValueError, "Color values must be between 0 and 255."); + return -1; + } + + if (member_ptr == 0) + { + self->data->text.setFillColor(sf::Color(r, g, b, a)); + } + else if (member_ptr == 1) + { + self->data->text.setOutlineColor(sf::Color(r, g, b, a)); + } + else + { + PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); + return -1; + } + + return 0; +} + + +//TODO: evaluate use of Resources::caption_buffer... can't I do this with a std::string? +PyObject* UICaption::get_text(PyUICaptionObject* self, void* closure) +{ + Resources::caption_buffer = self->data->text.getString(); + return PyUnicode_FromString(Resources::caption_buffer.c_str()); +} + +int UICaption::set_text(PyUICaptionObject* self, PyObject* value, void* closure) +{ + PyObject* s = PyObject_Str(value); + PyObject * temp_bytes = PyUnicode_AsEncodedString(s, "UTF-8", "strict"); // Owned reference + if (temp_bytes != NULL) { + Resources::caption_buffer = PyBytes_AS_STRING(temp_bytes); // Borrowed pointer + Py_DECREF(temp_bytes); + } + self->data->text.setString(Resources::caption_buffer); + return 0; +} + +PyGetSetDef UICaption::getsetters[] = { + {"x", (getter)UICaption::get_float_member, (setter)UICaption::set_float_member, "X coordinate of top-left corner", (void*)0}, + {"y", (getter)UICaption::get_float_member, (setter)UICaption::set_float_member, "Y coordinate of top-left corner", (void*)1}, + {"pos", (getter)UICaption::get_vec_member, (setter)UICaption::set_vec_member, "(x, y) vector", (void*)0}, + //{"w", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "width of the rectangle", (void*)2}, + //{"h", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "height of the rectangle", (void*)3}, + {"outline", (getter)UICaption::get_float_member, (setter)UICaption::set_float_member, "Thickness of the border", (void*)4}, + {"fill_color", (getter)UICaption::get_color_member, (setter)UICaption::set_color_member, "Fill color of the text", (void*)0}, + {"outline_color", (getter)UICaption::get_color_member, (setter)UICaption::set_color_member, "Outline color of the text", (void*)1}, + //{"children", (getter)PyUIFrame_get_children, NULL, "UICollection of objects on top of this one", NULL}, + {"text", (getter)UICaption::get_text, (setter)UICaption::set_text, "The text displayed", NULL}, + {"size", (getter)UICaption::get_float_member, (setter)UICaption::set_float_member, "Text size (integer) in points", (void*)5}, + {"click", (getter)UIDrawable::get_click, (setter)UIDrawable::set_click, "Object called with (x, y, button) when clicked", (void*)PyObjectsEnum::UICAPTION}, + {NULL} +}; + +PyObject* UICaption::repr(PyUICaptionObject* self) +{ + std::ostringstream ss; + if (!self->data) ss << ""; + else { + auto text = self->data->text; + auto fc = text.getFillColor(); + auto oc = text.getOutlineColor(); + ss << ""; + } + std::string repr_str = ss.str(); + return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); +} + +int UICaption::init(PyUICaptionObject* self, PyObject* args, PyObject* kwds) +{ + using namespace mcrfpydef; + static const char* keywords[] = { "x", "y", "text", "font", "fill_color", "outline_color", nullptr }; + float x = 0.0f, y = 0.0f; + char* text; + PyObject* font, fill_color, outline_color; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ffzOOO", + const_cast(keywords), &x, &y, &text, &font, &fill_color, &outline_color)) + { + return -1; + } + + // check types for font, fill_color, outline_color + + std::cout << PyUnicode_AsUTF8(PyObject_Repr(font)) << std::endl; + if (font != NULL && !PyObject_IsInstance(font, (PyObject*)&PyFontType)){ + PyErr_SetString(PyExc_TypeError, "font must be a mcrfpy.Font instance"); + return -1; + } else if (font != NULL) + { + auto font_obj = (PyFontObject*)font; + self->data->text.setFont(font_obj->data->font); + self->font = font; + Py_INCREF(font); + } else + { + // default font + //self->data->text.setFont(Resources::game->getFont()); + } + + self->data->text.setPosition(sf::Vector2f(x, y)); + self->data->text.setString((std::string)text); + self->data->text.setFillColor(sf::Color(0,0,0,255)); + self->data->text.setOutlineColor(sf::Color(128,128,128,255)); + + return 0; +} + diff --git a/src/UICaption.h b/src/UICaption.h index f403813..1a1a40d 100644 --- a/src/UICaption.h +++ b/src/UICaption.h @@ -19,19 +19,25 @@ class UICaption: public UIDrawable public: sf::Text text; void render(sf::Vector2f) override final; - PyObjectsEnum derived_type() override final; // { return PyObjectsEnum::UICaption; }; + PyObjectsEnum derived_type() override final; virtual UIDrawable* click_at(sf::Vector2f point) override final; -}; -//class UICaption; -//typedef struct { -// PyObject_HEAD -// std::shared_ptr data; -// PyObject* font; -//} PyUICaptionObject; + static PyObject* get_float_member(PyUICaptionObject* self, void* closure); + static int set_float_member(PyUICaptionObject* self, PyObject* value, void* closure); + static PyObject* get_vec_member(PyUICaptionObject* self, void* closure); + static int set_vec_member(PyUICaptionObject* self, PyObject* value, void* closure); + static PyObject* get_color_member(PyUICaptionObject* self, void* closure); + static int set_color_member(PyUICaptionObject* self, PyObject* value, void* closure); + static PyObject* get_text(PyUICaptionObject* self, void* closure); + static int set_text(PyUICaptionObject* self, PyObject* value, void* closure); + static PyGetSetDef getsetters[]; + static PyObject* repr(PyUICaptionObject* self); + static int init(PyUICaptionObject* self, PyObject* args, PyObject* kwds); +}; namespace mcrfpydef { //TODO: add this method to class scope; move implementation to .cpp file + /* static PyObject* PyUICaption_get_float_member(PyUICaptionObject* self, void* closure) { auto member_ptr = reinterpret_cast(closure); @@ -49,8 +55,10 @@ namespace mcrfpydef { return nullptr; } } + */ //TODO: add this method to class scope; move implementation to .cpp file + /* static int PyUICaption_set_float_member(PyUICaptionObject* self, PyObject* value, void* closure) { float val; @@ -78,8 +86,10 @@ namespace mcrfpydef { self->data->text.setCharacterSize(val); return 0; } + */ //TODO: add this method to class scope; move implementation to .cpp file + /* static PyObject* PyUICaption_get_vec_member(PyUICaptionObject* self, void* closure) { return PyVector(self->data->text.getPosition()).pyObject(); @@ -91,8 +101,10 @@ namespace mcrfpydef { self->data->text.setPosition(PyVector::fromPy(value)); return 0; } + */ //TODO: add this method to class scope; move implementation to .cpp file + /* static PyObject* PyUICaption_get_color_member(PyUICaptionObject* self, void* closure) { // validate closure (should be impossible to be wrong, but it's thorough) @@ -118,8 +130,10 @@ namespace mcrfpydef { return PyColor(color).pyObject(); } + */ //TODO: add this method to class scope; move implementation to .cpp file + /* static int PyUICaption_set_color_member(PyUICaptionObject* self, PyObject* value, void* closure) { auto member_ptr = reinterpret_cast(closure); @@ -173,8 +187,10 @@ namespace mcrfpydef { return 0; } + */ //TODO: add this method to class scope; move implementation to .cpp file + /* static PyObject* PyUICaption_get_text(PyUICaptionObject* self, void* closure) { Resources::caption_buffer = self->data->text.getString(); @@ -193,8 +209,10 @@ namespace mcrfpydef { self->data->text.setString(Resources::caption_buffer); return 0; } + */ //TODO: add this static array to class scope; move implementation to .cpp file + /* static PyGetSetDef PyUICaption_getsetters[] = { {"x", (getter)PyUICaption_get_float_member, (setter)PyUICaption_set_float_member, "X coordinate of top-left corner", (void*)0}, {"y", (getter)PyUICaption_get_float_member, (setter)PyUICaption_set_float_member, "Y coordinate of top-left corner", (void*)1}, @@ -210,8 +228,10 @@ namespace mcrfpydef { {"click", (getter)UIDrawable::get_click, (setter)UIDrawable::set_click, "Object called with (x, y, button) when clicked", (void*)PyObjectsEnum::UICAPTION}, {NULL} }; + */ //TODO: add this method to class scope; move implementation to .cpp file + /* static PyObject* PyUICaption_repr(PyUICaptionObject* self) { std::ostringstream ss; @@ -230,8 +250,10 @@ namespace mcrfpydef { std::string repr_str = ss.str(); return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); } + */ //TODO: add this method to class scope; move implementation to .cpp file + /* static int PyUICaption_init(PyUICaptionObject* self, PyObject* args, PyObject* kwds) { //std::cout << "Init called\n"; @@ -271,21 +293,24 @@ namespace mcrfpydef { return 0; } + */ static PyTypeObject PyUICaptionType = { //PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "mcrfpy.Caption", .tp_basicsize = sizeof(PyUICaptionObject), .tp_itemsize = 0, + // TODO - move tp_dealloc to .cpp file as static function (UICaption::dealloc) .tp_dealloc = (destructor)[](PyObject* self) { PyUICaptionObject* obj = (PyUICaptionObject*)self; + // TODO - reevaluate with PyFont usage; UICaption does not own the font // release reference to font object if (obj->font) Py_DECREF(obj->font); obj->data.reset(); Py_TYPE(self)->tp_free(self); }, - .tp_repr = (reprfunc)PyUICaption_repr, + .tp_repr = (reprfunc)UICaption::repr, //.tp_hash = NULL, //.tp_iter //.tp_iternext @@ -293,9 +318,10 @@ namespace mcrfpydef { .tp_doc = PyDoc_STR("docstring"), //.tp_methods = PyUIFrame_methods, //.tp_members = PyUIFrame_members, - .tp_getset = PyUICaption_getsetters, + .tp_getset = UICaption::getsetters, //.tp_base = NULL, - .tp_init = (initproc)PyUICaption_init, + .tp_init = (initproc)UICaption::init, + // TODO - move tp_new to .cpp file as a static function (UICaption::new) .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* { PyUICaptionObject* self = (PyUICaptionObject*)type->tp_alloc(type, 0); diff --git a/src/UIDrawable.cpp b/src/UIDrawable.cpp index cd898c4..a710095 100644 --- a/src/UIDrawable.cpp +++ b/src/UIDrawable.cpp @@ -75,4 +75,7 @@ int UIDrawable::set_click(PyObject* self, PyObject* value, void* closure) { return 0; } - +void UIDrawable::click_register(PyObject* callable) +{ + click_callable = std::make_unique(callable); +} diff --git a/src/UIEntity.cpp b/src/UIEntity.cpp index 9e21eaf..abfb8b9 100644 --- a/src/UIEntity.cpp +++ b/src/UIEntity.cpp @@ -1,4 +1,5 @@ #include "UIEntity.h" +#include "UIGrid.h" UIEntity::UIEntity() {} // this will not work lol. TODO remove default constructor by finding the shared pointer inits that use it @@ -19,7 +20,7 @@ PyObject* UIEntity::at(PyUIEntityObject* self, PyObject* o) { return NULL; } - PyUIGridPointStateObject* obj = (PyUIGridPointStateObject*)((&PyUIGridPointStateType)->tp_alloc(&PyUIGridPointStateType, 0)); + PyUIGridPointStateObject* obj = (PyUIGridPointStateObject*)((&mcrfpydef::PyUIGridPointStateType)->tp_alloc(&mcrfpydef::PyUIGridPointStateType, 0)); //auto target = std::static_pointer_cast(target); obj->data = &(self->data->gridstate[y + self->data->grid->grid_x * x]); obj->grid = self->data->grid; @@ -28,7 +29,7 @@ PyObject* UIEntity::at(PyUIEntityObject* self, PyObject* o) { } -int PyUIEntity::init(PyUIEntityObject* self, PyObject* args, PyObject* kwds) { +int UIEntity::init(PyUIEntityObject* self, PyObject* args, PyObject* kwds) { static const char* keywords[] = { "x", "y", "texture", "sprite_index", "grid", nullptr }; float x = 0.0f, y = 0.0f, scale = 1.0f; int sprite_index = -1; @@ -57,7 +58,7 @@ int PyUIEntity::init(PyUIEntityObject* self, PyObject* args, PyObject* kwds) { // default tex? }*/ - if (grid != NULL && !PyObject_IsInstance(grid, (PyObject*)&PyUIGridType)) { + if (grid != NULL && !PyObject_IsInstance(grid, (PyObject*)&mcrfpydef::PyUIGridType)) { PyErr_SetString(PyExc_TypeError, "grid must be a mcrfpy.Grid instance"); return -1; } diff --git a/src/UIEntity.h b/src/UIEntity.h index 8088aff..1f37aa6 100644 --- a/src/UIEntity.h +++ b/src/UIEntity.h @@ -15,6 +15,8 @@ #include "UIGridPoint.h" #include "UIDrawable.h" #include "UIBase.h" +#include "UISprite.h" + class UIGrid; //class UIEntity; diff --git a/src/UIFrame.cpp b/src/UIFrame.cpp index c9f42e8..3452a58 100644 --- a/src/UIFrame.cpp +++ b/src/UIFrame.cpp @@ -1,5 +1,6 @@ #include "UIFrame.h" #include "UICollection.h" +#include "GameEngine.h" UIDrawable* UIFrame::click_at(sf::Vector2f point) { @@ -54,10 +55,10 @@ void UIFrame::render(sf::Vector2f offset) } } -PyObject* PyUIFrame::get_children(PyUIFrameObject* self, void* closure) +PyObject* UIFrame::get_children(PyUIFrameObject* self, void* closure) { // create PyUICollection instance pointing to self->data->children - PyUICollectionObject* o = (PyUICollectionObject*)PyUICollectionType.tp_alloc(&PyUICollectionType, 0); + PyUICollectionObject* o = (PyUICollectionObject*)mcrfpydef::PyUICollectionType.tp_alloc(&mcrfpydef::PyUICollectionType, 0); if (o) o->data = self->data->children; return (PyObject*)o; diff --git a/src/UIGrid.cpp b/src/UIGrid.cpp index 207114b..69f2375 100644 --- a/src/UIGrid.cpp +++ b/src/UIGrid.cpp @@ -1,4 +1,5 @@ #include "UIGrid.h" +#include "GameEngine.h" UIGrid::UIGrid() {} @@ -188,3 +189,12 @@ std::shared_ptr UIGrid::getTexture() { return ptex; } + +UIDrawable* UIGrid::click_at(sf::Vector2f point) +{ + if (click_callable) + { + if(box.getGlobalBounds().contains(point)) return this; + } + return NULL; +} diff --git a/src/UIGridPoint.h b/src/UIGridPoint.h index 147f0d9..4827bd9 100644 --- a/src/UIGridPoint.h +++ b/src/UIGridPoint.h @@ -30,6 +30,7 @@ public: }; class UIGrid; +class UIEntity; typedef struct { PyObject_HEAD diff --git a/src/UISprite.cpp b/src/UISprite.cpp index 1f06be1..47acc11 100644 --- a/src/UISprite.cpp +++ b/src/UISprite.cpp @@ -1,4 +1,5 @@ #include "UISprite.h" +#include "GameEngine.h" UIDrawable* UISprite::click_at(sf::Vector2f point) { diff --git a/src/UISprite.h b/src/UISprite.h index dc9faf8..ce22be8 100644 --- a/src/UISprite.h +++ b/src/UISprite.h @@ -12,6 +12,7 @@ #include "PyVector.h" #include "PyFont.h" #include "UIDrawable.h" +#include "UIBase.h" class UISprite: public UIDrawable { From 8efa25878f8f2b3a670693bbe197af618d373360 Mon Sep 17 00:00:00 2001 From: John McCardle Date: Wed, 10 Apr 2024 23:41:14 -0400 Subject: [PATCH 06/14] remove a lot of stuff --- src/UI.cpp | 527 ------------------------------------------------ src/UICaption.h | 261 +----------------------- 2 files changed, 1 insertion(+), 787 deletions(-) delete mode 100644 src/UI.cpp diff --git a/src/UI.cpp b/src/UI.cpp deleted file mode 100644 index d56dc4a..0000000 --- a/src/UI.cpp +++ /dev/null @@ -1,527 +0,0 @@ -//#include "UI.h" -//#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); -// */ -// click_callable = std::make_unique(callable); -//} -// -//void UIDrawable::click_unregister() -//{ -// /* -// if (click_callable == NULL) return; -// Py_DECREF(click_callable); -// click_callable = NULL; -// */ -// click_callable.reset(); -//} -// -//void UIDrawable::render() -//{ -// //std::cout << "Rendering base UIDrawable\n"; -// render(sf::Vector2f()); -//} -//UIFrame::UIFrame(): -//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; -// _outlineColor = NULL; -// _fillColor = NULL; -// */ -//} -// -//UIFrame::UIFrame(float _x, float _y, float _w, float _h): -//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; -// pyFillColor = NULL; -// _outlineColor = NULL; -// _fillColor = NULL; -// */ -//} -// -//UIFrame::~UIFrame() -//{ -// children.reset(); -// /* -// if (pyOutlineColor) Py_DECREF(pyOutlineColor); -// else if (_outlineColor) delete _outlineColor; -// if (pyFillColor) Py_DECREF(pyFillColor); -// else if (_fillColor) delete _fillColor; -// */ -//} -// -///* -// sf::Color& fillColor(); // getter -// void fillColor(sf::Color c); // C++ setter -// void fillColor(PyObject* pyColor); // Python setter -// -// sf::Color& outlineColor(); // getter -// void outlineColor(sf::Color c); // C++ setter -// void outlineColor(PyObject* pyColor); // Python setter -//*/ -// -// -//PyObjectsEnum UIFrame::derived_type() -//{ -// return PyObjectsEnum::UIFRAME; -//} -// -//void UIFrame::render(sf::Vector2f offset) -//{ -// //std::cout << "Rendering UIFrame w/ offset " << offset.x << ", " << offset.y << "\n"; -// //std::cout << "position = " << x << ", " << y << "\n"; -// box.move(offset); -// Resources::game->getWindow().draw(box); -// box.move(-offset); -// //sf::RectangleShape box = sf::RectangleShape(sf::Vector2f(w,h)); -// //sf::Vector2f pos = sf::Vector2f(x, y); -// //box.setPosition(offset + pos); -// //if (_fillColor) { box.setFillColor(fillColor()); } -// //if (_outlineColor) { box.setOutlineColor(outlineColor()); } -// //box.setOutlineThickness(outline); -// //Resources::game->getWindow().draw(box); -// for (auto drawable : *children) { -// drawable->render(offset + box.getPosition()); -// } -//} -// -//void UICaption::render(sf::Vector2f offset) -//{ -// //std::cout << "Rendering Caption with offset\n"; -// text.move(offset); -// Resources::game->getWindow().draw(text); -// text.move(-offset); -//} -// -//UISprite::UISprite() {} -///* -// // * tearing down the old IndexTexture way of life -//UISprite::UISprite(IndexTexture* _itex, int _sprite_index, float x = 0.0, float y = 0.0, float s = 1.0) -//: itex(_itex), sprite_index(_sprite_index) -//{ -// sprite.setTexture(_itex->texture); -// sprite.setTextureRect(_itex->spriteCoordinates(_sprite_index)); -// sprite.setPosition(sf::Vector2f(x, y)); -// sprite.setScale(sf::Vector2f(s, s)); -//} -// -//UISprite::UISprite(IndexTexture* _itex, int _sprite_index, sf::Vector2f pos, float s = 1.0) -//: itex(_itex), sprite_index(_sprite_index) -//{ -// sprite.setTexture(_itex->texture); -// sprite.setTextureRect(_itex->spriteCoordinates(_sprite_index)); -// sprite.setPosition(pos); -// sprite.setScale(sf::Vector2f(s, s)); -//} -//*/ -// -//UISprite::UISprite(std::shared_ptr _ptex, int _sprite_index, sf::Vector2f _pos, float _scale) -//: ptex(_ptex), sprite_index(_sprite_index) -//{ -// sprite = ptex->sprite(sprite_index, _pos, sf::Vector2f(_scale, _scale)); -//} -// -//void UISprite::update() -//{ -// //auto& tex = Resources::game->textures[texture_index]; -// //sprite.setTexture(tex.texture); -// //sprite.setScale(sf::Vector2f(scale, scale)); -// //sprite.setPosition(sf::Vector2f(x, y)); -// //std::cout << "Drawable position: " << x << ", " << y << " -> " << s.getPosition().x << ", " << s.getPosition().y << std::endl; -// //sprite.setTextureRect(tex.spriteCoordinates(sprite_index)); -//} -// -//void UISprite::render(sf::Vector2f offset) -//{ -// sprite.move(offset); -// Resources::game->getWindow().draw(sprite); -// sprite.move(-offset); -//} -// -// 7DRL hack; needed to draw entities to UIGrid. TODO, apply this technique to all UIDrawables -//void UISprite::render(sf::Vector2f offset, sf::RenderTexture& target) -//{ -// sprite.move(offset); -// target.draw(sprite); -// sprite.move(-offset); -//} -// -///* -//void UISprite::setPosition(float x, float y) -//{ -// setPosition(sf::Vector2f(x, y)); -//} -//*/ -// -//void UISprite::setPosition(sf::Vector2f pos) -//{ -// sprite.setPosition(pos); -//} -// -//void UISprite::setScale(sf::Vector2f s) -//{ -// sprite.setScale(s); -//} -// -//void UISprite::setTexture(std::shared_ptr _ptex, int _sprite_index) -//{ -// ptex = _ptex; -// if (_sprite_index != -1) // if you are changing textures, there's a good chance you need a new index too -// sprite_index = _sprite_index; -// sprite = ptex->sprite(sprite_index, sprite.getPosition(), sprite.getScale()); -//} -// -//void UISprite::setSpriteIndex(int _sprite_index) -//{ -// sprite_index = _sprite_index; -// sprite = ptex->sprite(sprite_index, sprite.getPosition(), sprite.getScale()); -//} -// -//sf::Vector2f UISprite::getScale() -//{ -// return sprite.getScale(); -//} -// -//sf::Vector2f UISprite::getPosition() -//{ -// return sprite.getPosition(); -//} -// -//std::shared_ptr UISprite::getTexture() -//{ -// return ptex; -//} -// -//int UISprite::getSpriteIndex() -//{ -// return sprite_index; -//} -// -//PyObjectsEnum UICaption::derived_type() -//{ -// return PyObjectsEnum::UICAPTION; -//} -// -//PyObjectsEnum UISprite::derived_type() -//{ -// return PyObjectsEnum::UISPRITE; -//} -// -// UIGrid support classes' methods -// -//UIGridPoint::UIGridPoint() -//:color(1.0f, 1.0f, 1.0f), color_overlay(0.0f, 0.0f, 0.0f), walkable(false), transparent(false), -// tilesprite(-1), tile_overlay(-1), uisprite(-1) -//{ -//} -// -//UIEntity::UIEntity() {} // this will not work lol. TODO remove default constructor by finding the shared pointer inits that use it -// -//UIEntity::UIEntity(UIGrid& grid) -//: gridstate(grid.grid_x * grid.grid_y) -//{ -//} -// -// UIGrid methods -// -//UIGrid::UIGrid() -//{ -//} -// -///* -//UIGrid::UIGrid(int gx, int gy, std::shared_ptr _ptex, float _x, float _y, float _w, float _h) -//: grid_x(gx), grid_y(gy), -// zoom(1.0f), center_x((gx/2) * _ptex->sheet_width), center_y((gy/2) * _ptex->sheet_height), -// itex(_itex), points(gx * gy) -//{ -// // set up blank list of entities -// entities = std::make_shared>>(); -// -// box.setSize(sf::Vector2f(_w, _h)); -// box.setPosition(sf::Vector2f(_x, _y)); -// -// box.setFillColor(sf::Color(0,0,0,0)); -// renderTexture.create(_w, _h); -// sprite.setTexture(_itex->texture); -// output.setTextureRect( -// sf::IntRect(0, 0, -// box.getSize().x, box.getSize().y)); -// output.setPosition(box.getPosition()); -// // textures are upside-down inside renderTexture -// output.setTexture(renderTexture.getTexture()); -//} -//*/ -// -//UIGrid::UIGrid(int gx, int gy, std::shared_ptr _ptex, sf::Vector2f _xy, sf::Vector2f _wh) -//: grid_x(gx), grid_y(gy), -// zoom(1.0f), center_x((gx/2) * _ptex->sprite_width), center_y((gy/2) * _ptex->sprite_height), -// ptex(_ptex), points(gx * gy) -//{ -// // set up blank list of entities -// entities = std::make_shared>>(); -// -// box.setSize(_wh); -// box.setPosition(_xy); -// -// box.setFillColor(sf::Color(0,0,0,0)); -// //renderTexture.create(_wh.x, _wh.y); -// // create renderTexture with maximum theoretical size; sprite can resize to show whatever amount needs to be rendered -// renderTexture.create(1920, 1080); // TODO - renderTexture should be window size; above 1080p this will cause rendering errors -// -// //sprite.setTexture(_itex->texture); -// sprite = ptex->sprite(0); -// -// output.setTextureRect( -// sf::IntRect(0, 0, -// box.getSize().x, box.getSize().y)); -// output.setPosition(box.getPosition()); -// // textures are upside-down inside renderTexture -// output.setTexture(renderTexture.getTexture()); -// -//} -// -//void UIGrid::update() -//{ -//} -// -///* -//void UIGrid::setSprite(int ti) -//{ -// //int tx = ti % itex->grid_width, ty = ti / itex->grid_width; -// // sprite.setTextureRect(sf::IntRect(tx * itex->grid_size, ty * itex->grid_size, itex->grid_size, itex->grid_size)); -// sprite = ptex->sprite(ti); -//} -//*/ -// -//void UIGrid::render(sf::Vector2f) -//{ -// output.setPosition(box.getPosition()); // output sprite can move; update position when drawing -// // output size can change; update size when drawing -// output.setTextureRect( -// sf::IntRect(0, 0, -// box.getSize().x, box.getSize().y)); -// renderTexture.clear(sf::Color(8, 8, 8, 255)); // TODO - UIGrid needs a "background color" field -// // sprites that are visible according to zoom, center_x, center_y, and box width -// float center_x_sq = center_x / ptex->sprite_width; -// float center_y_sq = center_y / ptex->sprite_height; -// -// float width_sq = box.getSize().x / (ptex->sprite_width * zoom); -// float height_sq = box.getSize().y / (ptex->sprite_height * zoom); -// float left_edge = center_x_sq - (width_sq / 2.0); -// float top_edge = center_y_sq - (height_sq / 2.0); -// -// int left_spritepixels = center_x - (box.getSize().x / 2.0 / zoom); -// int top_spritepixels = center_y - (box.getSize().y / 2.0 / zoom); -// -// //sprite.setScale(sf::Vector2f(zoom, zoom)); -// sf::RectangleShape r; // for colors and overlays -// r.setSize(sf::Vector2f(ptex->sprite_width * zoom, ptex->sprite_height * zoom)); -// r.setOutlineThickness(0); -// -// int x_limit = left_edge + width_sq + 2; -// if (x_limit > grid_x) x_limit = grid_x; -// -// int y_limit = top_edge + height_sq + 2; -// if (y_limit > grid_y) y_limit = grid_y; -// -// // base layer - bottom color, tile sprite ("ground") -// for (int x = (left_edge - 1 >= 0 ? left_edge - 1 : 0); -// x < x_limit; //x < view_width; -// x+=1) -// { -// //for (float y = (top_edge >= 0 ? top_edge : 0); -// for (int y = (top_edge - 1 >= 0 ? top_edge - 1 : 0); -// y < y_limit; //y < view_height; -// y+=1) -// { -// auto pixel_pos = sf::Vector2f( -// (x*ptex->sprite_width - left_spritepixels) * zoom, -// (y*ptex->sprite_height - top_spritepixels) * zoom ); -// -// auto gridpoint = at(std::floor(x), std::floor(y)); -// -// //sprite.setPosition(pixel_pos); -// -// r.setPosition(pixel_pos); -// r.setFillColor(gridpoint.color); -// renderTexture.draw(r); -// -// // tilesprite -// // if discovered but not visible, set opacity to 90% -// // if not discovered... just don't draw it? -// if (gridpoint.tilesprite != -1) { -// sprite = ptex->sprite(gridpoint.tilesprite, pixel_pos, sf::Vector2f(zoom, zoom)); //setSprite(gridpoint.tilesprite);; -// renderTexture.draw(sprite); -// } -// } -// } -// -// // middle layer - entities -// // disabling entity rendering until I can render their UISprite inside the rendertexture (not directly to window) -// for (auto e : *entities) { -// // TODO skip out-of-bounds entities (grid square not visible at all, check for partially on visible grid squares / floating point grid position) -// //auto drawent = e->cGrid->indexsprite.drawable(); -// auto& drawent = e->sprite; -// //drawent.setScale(zoom, zoom); -// drawent.setScale(sf::Vector2f(zoom, zoom)); -// auto pixel_pos = sf::Vector2f( -// (e->position.x*ptex->sprite_width - left_spritepixels) * zoom, -// (e->position.y*ptex->sprite_height - top_spritepixels) * zoom ); -// //drawent.setPosition(pixel_pos); -// //renderTexture.draw(drawent); -// drawent.render(pixel_pos, renderTexture); -// } -// -// -// // top layer - opacity for discovered / visible status (debug, basically) -// /* // Disabled until I attach a "perspective" -// for (int x = (left_edge - 1 >= 0 ? left_edge - 1 : 0); -// x < x_limit; //x < view_width; -// x+=1) -// { -// //for (float y = (top_edge >= 0 ? top_edge : 0); -// for (int y = (top_edge - 1 >= 0 ? top_edge - 1 : 0); -// y < y_limit; //y < view_height; -// y+=1) -// { -// -// auto pixel_pos = sf::Vector2f( -// (x*itex->grid_size - left_spritepixels) * zoom, -// (y*itex->grid_size - top_spritepixels) * zoom ); -// -// auto gridpoint = at(std::floor(x), std::floor(y)); -// -// sprite.setPosition(pixel_pos); -// -// r.setPosition(pixel_pos); -// -// // visible & discovered layers for testing purposes -// if (!gridpoint.discovered) { -// r.setFillColor(sf::Color(16, 16, 20, 192)); // 255 opacity for actual blackout -// renderTexture.draw(r); -// } else if (!gridpoint.visible) { -// r.setFillColor(sf::Color(32, 32, 40, 128)); -// renderTexture.draw(r); -// } -// -// // overlay -// -// // uisprite -// } -// } -// */ -// -// // grid lines for testing & validation -// /* -// sf::Vertex line[] = -// { -// sf::Vertex(sf::Vector2f(0, 0), sf::Color::Red), -// sf::Vertex(box.getSize(), sf::Color::Red), -// -// }; -// -// renderTexture.draw(line, 2, sf::Lines); -// sf::Vertex lineb[] = -// { -// sf::Vertex(sf::Vector2f(0, box.getSize().y), sf::Color::Blue), -// sf::Vertex(sf::Vector2f(box.getSize().x, 0), sf::Color::Blue), -// -// }; -// -// renderTexture.draw(lineb, 2, sf::Lines); -// */ -// -// // render to window -// renderTexture.display(); -// Resources::game->getWindow().draw(output); -// -//} -// -//UIGridPoint& UIGrid::at(int x, int y) -//{ -// return points[y * grid_x + x]; -//} -// -//PyObjectsEnum UIGrid::derived_type() -//{ -// return PyObjectsEnum::UIGRID; -//} -// -//std::shared_ptr UIGrid::getTexture() -//{ -// return ptex; -//} diff --git a/src/UICaption.h b/src/UICaption.h index 1a1a40d..1e93699 100644 --- a/src/UICaption.h +++ b/src/UICaption.h @@ -33,270 +33,11 @@ public: static PyGetSetDef getsetters[]; static PyObject* repr(PyUICaptionObject* self); static int init(PyUICaptionObject* self, PyObject* args, PyObject* kwds); + }; namespace mcrfpydef { - //TODO: add this method to class scope; move implementation to .cpp file - /* - static PyObject* PyUICaption_get_float_member(PyUICaptionObject* self, void* closure) - { - auto member_ptr = reinterpret_cast(closure); - if (member_ptr == 0) - return PyFloat_FromDouble(self->data->text.getPosition().x); - else if (member_ptr == 1) - return PyFloat_FromDouble(self->data->text.getPosition().y); - else if (member_ptr == 4) - return PyFloat_FromDouble(self->data->text.getOutlineThickness()); - else if (member_ptr == 5) - return PyLong_FromLong(self->data->text.getCharacterSize()); - else - { - PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); - return nullptr; - } - } - */ - - //TODO: add this method to class scope; move implementation to .cpp file - /* - static int PyUICaption_set_float_member(PyUICaptionObject* self, PyObject* value, void* closure) - { - float val; - auto member_ptr = reinterpret_cast(closure); - if (PyFloat_Check(value)) - { - val = PyFloat_AsDouble(value); - } - else if (PyLong_Check(value)) - { - val = PyLong_AsLong(value); - } - else - { - PyErr_SetString(PyExc_TypeError, "Value must be an integer."); - return -1; - } - if (member_ptr == 0) //x - self->data->text.setPosition(val, self->data->text.getPosition().y); - else if (member_ptr == 1) //y - self->data->text.setPosition(self->data->text.getPosition().x, val); - else if (member_ptr == 4) //outline - self->data->text.setOutlineThickness(val); - else if (member_ptr == 5) // character size - self->data->text.setCharacterSize(val); - return 0; - } - */ - - //TODO: add this method to class scope; move implementation to .cpp file - /* - static PyObject* PyUICaption_get_vec_member(PyUICaptionObject* self, void* closure) - { - return PyVector(self->data->text.getPosition()).pyObject(); - } - - //TODO: add this method to class scope; move implementation to .cpp file - static int PyUICaption_set_vec_member(PyUICaptionObject* self, PyObject* value, void* closure) - { - self->data->text.setPosition(PyVector::fromPy(value)); - return 0; - } - */ - - //TODO: add this method to class scope; move implementation to .cpp file - /* - static PyObject* PyUICaption_get_color_member(PyUICaptionObject* self, void* closure) - { - // validate closure (should be impossible to be wrong, but it's thorough) - auto member_ptr = reinterpret_cast(closure); - if (member_ptr != 0 && member_ptr != 1) - { - PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); - return nullptr; - } - - // TODO: manually calling tp_alloc to create a PyColorObject seems like an antipattern - // fetch correct member data - sf::Color color; - - if (member_ptr == 0) - { - color = self->data->text.getFillColor(); - } - else if (member_ptr == 1) - { - color = self->data->text.getOutlineColor(); - } - - return PyColor(color).pyObject(); - } - */ - - //TODO: add this method to class scope; move implementation to .cpp file - /* - static int PyUICaption_set_color_member(PyUICaptionObject* self, PyObject* value, void* closure) - { - auto member_ptr = reinterpret_cast(closure); - //TODO: this logic of (PyColor instance OR tuple -> sf::color) should be encapsulated for reuse - int r, g, b, a; - if (PyObject_IsInstance(value, (PyObject*)&PyColorType)) - { - // get value from mcrfpy.Color instance - auto c = ((PyColorObject*)value)->data; - r = c.r; g = c.g; b = c.b; a = c.a; - std::cout << "got " << int(r) << ", " << int(g) << ", " << int(b) << ", " << int(a) << std::endl; - } - else if (!PyTuple_Check(value) || PyTuple_Size(value) < 3 || PyTuple_Size(value) > 4) - { - // reject non-Color, non-tuple value - PyErr_SetString(PyExc_TypeError, "Value must be a tuple of 3 or 4 integers or an mcrfpy.Color object."); - return -1; - } - else // get value from tuples - { - r = PyLong_AsLong(PyTuple_GetItem(value, 0)); - g = PyLong_AsLong(PyTuple_GetItem(value, 1)); - b = PyLong_AsLong(PyTuple_GetItem(value, 2)); - a = 255; - - if (PyTuple_Size(value) == 4) - { - a = PyLong_AsLong(PyTuple_GetItem(value, 3)); - } - } - - if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255 || a < 0 || a > 255) - { - PyErr_SetString(PyExc_ValueError, "Color values must be between 0 and 255."); - return -1; - } - - if (member_ptr == 0) - { - self->data->text.setFillColor(sf::Color(r, g, b, a)); - } - else if (member_ptr == 1) - { - self->data->text.setOutlineColor(sf::Color(r, g, b, a)); - } - else - { - PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); - return -1; - } - - return 0; - } - */ - - //TODO: add this method to class scope; move implementation to .cpp file - /* - static PyObject* PyUICaption_get_text(PyUICaptionObject* self, void* closure) - { - Resources::caption_buffer = self->data->text.getString(); - return PyUnicode_FromString(Resources::caption_buffer.c_str()); - } - - //TODO: add this method to class scope; move implementation to .cpp file - static int PyUICaption_set_text(PyUICaptionObject* self, PyObject* value, void* closure) - { - PyObject* s = PyObject_Str(value); - PyObject * temp_bytes = PyUnicode_AsEncodedString(s, "UTF-8", "strict"); // Owned reference - if (temp_bytes != NULL) { - Resources::caption_buffer = PyBytes_AS_STRING(temp_bytes); // Borrowed pointer - Py_DECREF(temp_bytes); - } - self->data->text.setString(Resources::caption_buffer); - return 0; - } - */ - - //TODO: add this static array to class scope; move implementation to .cpp file - /* - static PyGetSetDef PyUICaption_getsetters[] = { - {"x", (getter)PyUICaption_get_float_member, (setter)PyUICaption_set_float_member, "X coordinate of top-left corner", (void*)0}, - {"y", (getter)PyUICaption_get_float_member, (setter)PyUICaption_set_float_member, "Y coordinate of top-left corner", (void*)1}, - {"pos", (getter)PyUICaption_get_vec_member, (setter)PyUICaption_set_vec_member, "(x, y) vector", (void*)0}, - //{"w", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "width of the rectangle", (void*)2}, - //{"h", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "height of the rectangle", (void*)3}, - {"outline", (getter)PyUICaption_get_float_member, (setter)PyUICaption_set_float_member, "Thickness of the border", (void*)4}, - {"fill_color", (getter)PyUICaption_get_color_member, (setter)PyUICaption_set_color_member, "Fill color of the text", (void*)0}, - {"outline_color", (getter)PyUICaption_get_color_member, (setter)PyUICaption_set_color_member, "Outline color of the text", (void*)1}, - //{"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)UIDrawable::get_click, (setter)UIDrawable::set_click, "Object called with (x, y, button) when clicked", (void*)PyObjectsEnum::UICAPTION}, - {NULL} - }; - */ - - //TODO: add this method to class scope; move implementation to .cpp file - /* - static PyObject* PyUICaption_repr(PyUICaptionObject* self) - { - std::ostringstream ss; - if (!self->data) ss << ""; - else { - auto text = self->data->text; - auto fc = text.getFillColor(); - auto oc = text.getOutlineColor(); - ss << ""; - } - std::string repr_str = ss.str(); - return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); - } - */ - - //TODO: add this method to class scope; move implementation to .cpp file - /* - static int PyUICaption_init(PyUICaptionObject* self, PyObject* args, PyObject* kwds) - { - //std::cout << "Init called\n"; - static const char* keywords[] = { "x", "y", "text", "font", "fill_color", "outline_color", nullptr }; - float x = 0.0f, y = 0.0f; - char* text; - PyObject* font, fill_color, outline_color; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ffzOOO", - const_cast(keywords), &x, &y, &text, &font, &fill_color, &outline_color)) - { - return -1; - } - - // check types for font, fill_color, outline_color - - std::cout << PyUnicode_AsUTF8(PyObject_Repr(font)) << std::endl; - if (font != NULL && !PyObject_IsInstance(font, (PyObject*)&PyFontType)){ - PyErr_SetString(PyExc_TypeError, "font must be a mcrfpy.Font instance"); - return -1; - } else if (font != NULL) - { - auto font_obj = (PyFontObject*)font; - self->data->text.setFont(font_obj->data->font); - self->font = font; - Py_INCREF(font); - } else - { - // default font - //self->data->text.setFont(Resources::game->getFont()); - } - - self->data->text.setPosition(sf::Vector2f(x, y)); - self->data->text.setString((std::string)text); - self->data->text.setFillColor(sf::Color(0,0,0,255)); - self->data->text.setOutlineColor(sf::Color(128,128,128,255)); - - return 0; - } - */ - static PyTypeObject PyUICaptionType = { - //PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "mcrfpy.Caption", .tp_basicsize = sizeof(PyUICaptionObject), .tp_itemsize = 0, From 714965da4544b8848306ad9b281f48b2378567a5 Mon Sep 17 00:00:00 2001 From: John McCardle Date: Fri, 12 Apr 2024 14:15:00 -0400 Subject: [PATCH 07/14] eliminate extra includes on UICaption --- src/UICaption.cpp | 3 +++ src/UICaption.h | 11 ----------- src/UIDrawable.h | 2 +- 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/UICaption.cpp b/src/UICaption.cpp index ffe3976..9d8c21d 100644 --- a/src/UICaption.cpp +++ b/src/UICaption.cpp @@ -1,5 +1,8 @@ #include "UICaption.h" #include "GameEngine.h" +#include "PyColor.h" +#include "PyVector.h" +#include "PyFont.h" UIDrawable* UICaption::click_at(sf::Vector2f point) { diff --git a/src/UICaption.h b/src/UICaption.h index 1e93699..bf01a96 100644 --- a/src/UICaption.h +++ b/src/UICaption.h @@ -1,18 +1,7 @@ #pragma once #include "Common.h" #include "Python.h" -#include "structmember.h" -#include "IndexTexture.h" -#include "Resources.h" -#include - -#include "PyCallable.h" -#include "PyTexture.h" -#include "PyColor.h" -#include "PyVector.h" -#include "PyFont.h" #include "UIDrawable.h" -#include "UIBase.h" class UICaption: public UIDrawable { diff --git a/src/UIDrawable.h b/src/UIDrawable.h index 640437f..d1a6b6e 100644 --- a/src/UIDrawable.h +++ b/src/UIDrawable.h @@ -13,7 +13,7 @@ #include "PyFont.h" #include "Resources.h" - +#include "UIBase.h" class UIFrame; class UICaption; class UISprite; class UIEntity; class UIGrid; enum PyObjectsEnum : int From 9efe998a3396182b468969b9a495164bd95100a3 Mon Sep 17 00:00:00 2001 From: John McCardle Date: Sat, 13 Apr 2024 00:17:43 -0400 Subject: [PATCH 08/14] some work on UICaption and UICollection; fixing segfaults resulting from mcrfpydef namepace TypeObject usage --- src/UICaption.cpp | 4 +- src/UICollection.cpp | 201 +++++++++++++++++++++++++++++++++++++++++++ src/UICollection.h | 77 ++++++++++------- src/UIDrawable.h | 7 ++ 4 files changed, 257 insertions(+), 32 deletions(-) diff --git a/src/UICaption.cpp b/src/UICaption.cpp index 9d8c21d..21c9c36 100644 --- a/src/UICaption.cpp +++ b/src/UICaption.cpp @@ -115,7 +115,7 @@ int UICaption::set_color_member(PyUICaptionObject* self, PyObject* value, void* auto member_ptr = reinterpret_cast(closure); //TODO: this logic of (PyColor instance OR tuple -> sf::color) should be encapsulated for reuse int r, g, b, a; - if (PyObject_IsInstance(value, (PyObject*)&mcrfpydef::PyColorType)) + if (PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Color") /*(PyObject*)&mcrfpydef::PyColorType)*/)) { // get value from mcrfpy.Color instance auto c = ((PyColorObject*)value)->data; @@ -236,7 +236,7 @@ int UICaption::init(PyUICaptionObject* self, PyObject* args, PyObject* kwds) // check types for font, fill_color, outline_color std::cout << PyUnicode_AsUTF8(PyObject_Repr(font)) << std::endl; - if (font != NULL && !PyObject_IsInstance(font, (PyObject*)&PyFontType)){ + if (font != NULL && !PyObject_IsInstance(font, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Font")/*(PyObject*)&PyFontType)*/)){ PyErr_SetString(PyExc_TypeError, "font must be a mcrfpy.Font instance"); return -1; } else if (font != NULL) diff --git a/src/UICollection.cpp b/src/UICollection.cpp index 8133952..7aa16cb 100644 --- a/src/UICollection.cpp +++ b/src/UICollection.cpp @@ -1,2 +1,203 @@ #include "UICollection.h" +#include "UIFrame.h" +#include "UICaption.h" +#include "UISprite.h" +#include "UIGrid.h" +#include "McRFPy_API.h" + +using namespace mcrfpydef; + +int UICollectionIter::init(PyUICollectionIterObject* self, PyObject* args, PyObject* kwds) +{ + PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required."); + return -1; +} + +PyObject* UICollectionIter::next(PyUICollectionIterObject* self) +{ + if (self->data->size() != self->start_size) + { + PyErr_SetString(PyExc_RuntimeError, "collection changed size during iteration"); + return NULL; + } + + if (self->index > self->start_size - 1) + { + PyErr_SetNone(PyExc_StopIteration); + return NULL; + } + self->index++; + auto vec = self->data.get(); + if (!vec) + { + PyErr_SetString(PyExc_RuntimeError, "the collection store returned a null pointer"); + return NULL; + } + auto target = (*vec)[self->index-1]; + // TODO build PyObject* of the correct UIDrawable subclass to return + //return py_instance(target); + return NULL; +} + +PyObject* UICollectionIter::repr(PyUICollectionIterObject* self) +{ + std::ostringstream ss; + if (!self->data) ss << ""; + else { + ss << "data->size() << " child objects, @ index " << self->index << ")>"; + } + std::string repr_str = ss.str(); + return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); +} + +Py_ssize_t UICollection::len(PyUICollectionObject* self) { + return self->data->size(); +} + +PyObject* UICollection::getitem(PyUICollectionObject* self, Py_ssize_t index) { + // build a Python version of item at self->data[index] + // Copy pasted:: + auto vec = self->data.get(); + if (!vec) + { + PyErr_SetString(PyExc_RuntimeError, "the collection store returned a null pointer"); + return NULL; + } + while (index < 0) index += self->data->size(); + if (index > self->data->size() - 1) + { + PyErr_SetString(PyExc_IndexError, "UICollection index out of range"); + return NULL; + } + auto target = (*vec)[index]; + RET_PY_INSTANCE(target); +return NULL; + + +} + +PySequenceMethods UICollection::sqmethods = { + .sq_length = (lenfunc)UICollection::len, + .sq_item = (ssizeargfunc)UICollection::getitem, + //.sq_item_by_index = PyUICollection_getitem + //.sq_slice - return a subset of the iterable + //.sq_ass_item - called when `o[x] = y` is executed (x is any object type) + //.sq_ass_slice - cool; no thanks, for now + //.sq_contains - called when `x in o` is executed + //.sq_ass_item_by_index - called when `o[x] = y` is executed (x is explictly an integer) +}; + +/* Idiomatic way to fetch complete types from the API rather than referencing their PyTypeObject struct + +auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Texture"); + +I never identified why `using namespace mcrfpydef;` doesn't solve the segfault issue. +The horrible macro in UIDrawable was originally a workaround for this, but as I interact with the types outside of the monster UI.h, a more general (and less icky) solution is required. + +*/ + +PyObject* UICollection::append(PyUICollectionObject* self, PyObject* o) +{ + // if not UIDrawable subclass, reject it + // self->data->push_back( c++ object inside o ); + + // this would be a great use case for .tp_base + if (!PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Frame")) && + !PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Sprite")) && + !PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Caption")) && + !PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Grid")) + ) + { + PyErr_SetString(PyExc_TypeError, "Only Frame, Caption, Sprite, and Grid objects can be added to UICollection"); + return NULL; + } + + if (PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Frame"))) + { + PyUIFrameObject* frame = (PyUIFrameObject*)o; + self->data->push_back(frame->data); + } + if (PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Caption"))) + { + PyUICaptionObject* caption = (PyUICaptionObject*)o; + self->data->push_back(caption->data); + } + if (PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Sprite"))) + { + PyUISpriteObject* sprite = (PyUISpriteObject*)o; + self->data->push_back(sprite->data); + } + if (PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Grid"))) + { + PyUIGridObject* grid = (PyUIGridObject*)o; + self->data->push_back(grid->data); + } + + Py_INCREF(Py_None); + return Py_None; +} + +PyObject* UICollection::remove(PyUICollectionObject* self, PyObject* o) +{ + if (!PyLong_Check(o)) + { + PyErr_SetString(PyExc_TypeError, "UICollection.remove requires an integer index to remove"); + return NULL; + } + long index = PyLong_AsLong(o); + if (index >= self->data->size()) + { + PyErr_SetString(PyExc_ValueError, "Index out of range"); + return NULL; + } + else if (index < 0) + { + PyErr_SetString(PyExc_NotImplementedError, "reverse indexing is not implemented."); + return NULL; + } + + // release the shared pointer at self->data[index]; + self->data->erase(self->data->begin() + index); + Py_INCREF(Py_None); + return Py_None; +} + +PyMethodDef UICollection::methods[] = { + {"append", (PyCFunction)UICollection::append, METH_O}, + //{"extend", (PyCFunction)PyUICollection_extend, METH_O}, // TODO + {"remove", (PyCFunction)UICollection::remove, METH_O}, + {NULL, NULL, 0, NULL} +}; + +PyObject* UICollection::repr(PyUICollectionObject* self) +{ + std::ostringstream ss; + if (!self->data) ss << ""; + else { + ss << "data->size() << " child objects)>"; + } + std::string repr_str = ss.str(); + return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); +} + +int UICollection::init(PyUICollectionObject* self, PyObject* args, PyObject* kwds) +{ + PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required."); + return -1; +} + +PyObject* UICollection::iter(PyUICollectionObject* self) +{ + PyUICollectionIterObject* iterObj; + iterObj = (PyUICollectionIterObject*)PyUICollectionIterType.tp_alloc(&PyUICollectionIterType, 0); + if (iterObj == NULL) { + return NULL; // Failed to allocate memory for the iterator object + } + + iterObj->data = self->data; + iterObj->index = 0; + iterObj->start_size = self->data->size(); + + return (PyObject*)iterObj; +} diff --git a/src/UICollection.h b/src/UICollection.h index 03136f0..9b6f6da 100644 --- a/src/UICollection.h +++ b/src/UICollection.h @@ -1,40 +1,47 @@ #pragma once #include "Common.h" #include "Python.h" -#include "structmember.h" -#include "IndexTexture.h" -#include "Resources.h" -#include - -#include "PyCallable.h" -#include "PyTexture.h" -#include "PyColor.h" -#include "PyVector.h" -#include "PyFont.h" #include "UIDrawable.h" -#include "UICaption.h" -#include "UIFrame.h" -#include "UISprite.h" -#include "UIGrid.h" + +class UICollectionIter +{ + // really more of a namespace: all the members are public and static. But being consistent with other UI objects +public: + static int init(PyUICollectionIterObject* self, PyObject* args, PyObject* kwds); + static PyObject* next(PyUICollectionIterObject* self); + static PyObject* repr(PyUICollectionIterObject* self); +}; + +class UICollection +{ + // asdf +public: + static Py_ssize_t len(PyUICollectionObject* self); + static PyObject* getitem(PyUICollectionObject* self, Py_ssize_t index); + static PySequenceMethods sqmethods; + static PyObject* append(PyUICollectionObject* self, PyObject* o); + static PyObject* remove(PyUICollectionObject* self, PyObject* o); + static PyMethodDef methods[]; + static PyObject* repr(PyUICollectionObject* self); + static int init(PyUICollectionObject* self, PyObject* args, PyObject* kwds); + static PyObject* iter(PyUICollectionObject* self); +}; namespace mcrfpydef { - typedef struct { - PyObject_HEAD - std::shared_ptr>> data; - int index; - int start_size; - } PyUICollectionIterObject; + //TODO: add this method to class scope; move implementation to .cpp file +/* static int PyUICollectionIter_init(PyUICollectionIterObject* self, PyObject* args, PyObject* kwds) { PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required."); return -1; } - +*/ //TODO: add this method to class scope; move implementation to .cpp file +/* static PyObject* PyUICollectionIter_next(PyUICollectionIterObject* self) { if (self->data->size() != self->start_size) @@ -60,8 +67,10 @@ namespace mcrfpydef { //return py_instance(target); return NULL; } +*/ //TODO: add this method to class scope; move implementation to .cpp file +/* static PyObject* PyUICollectionIter_repr(PyUICollectionIterObject* self) { std::ostringstream ss; @@ -72,31 +81,34 @@ namespace mcrfpydef { std::string repr_str = ss.str(); return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); } +*/ static PyTypeObject PyUICollectionIterType = { //PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "mcrfpy.UICollectionIter", .tp_basicsize = sizeof(PyUICollectionIterObject), .tp_itemsize = 0, + //TODO - as static method, not inline lambda def, please .tp_dealloc = (destructor)[](PyObject* self) { PyUICollectionIterObject* obj = (PyUICollectionIterObject*)self; obj->data.reset(); Py_TYPE(self)->tp_free(self); }, - .tp_repr = (reprfunc)PyUICollectionIter_repr, + .tp_repr = (reprfunc)UICollectionIter::repr, .tp_flags = Py_TPFLAGS_DEFAULT, .tp_doc = PyDoc_STR("Iterator for a collection of UI objects"), - .tp_iternext = (iternextfunc)PyUICollectionIter_next, + .tp_iternext = (iternextfunc)UICollectionIter::next, //.tp_getset = PyUICollection_getset, - .tp_init = (initproc)PyUICollectionIter_init, // just raise an exception + .tp_init = (initproc)UICollectionIter::init, // just raise an exception + //TODO - as static method, not inline lambda def, please .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* { PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required."); return NULL; } }; - +/* //TODO: add this method to class scope; move implementation to .cpp file static Py_ssize_t PyUICollection_len(PyUICollectionObject* self) { return self->data->size(); @@ -206,6 +218,7 @@ namespace mcrfpydef { } //TODO: add this static array to class scope; move implementation to .cpp file + static PyMethodDef PyUICollection_methods[] = { {"append", (PyCFunction)PyUICollection_append, METH_O}, //{"extend", (PyCFunction)PyUICollection_extend, METH_O}, // TODO @@ -248,25 +261,29 @@ namespace mcrfpydef { return (PyObject*)iterObj; } +*/ + static PyTypeObject PyUICollectionType = { //PyVarObject_/HEAD_INIT(NULL, 0) .tp_name = "mcrfpy.UICollection", .tp_basicsize = sizeof(PyUICollectionObject), .tp_itemsize = 0, + //TODO - as static method, not inline lambda def, please .tp_dealloc = (destructor)[](PyObject* self) { PyUICollectionObject* obj = (PyUICollectionObject*)self; obj->data.reset(); Py_TYPE(self)->tp_free(self); }, - .tp_repr = (reprfunc)PyUICollection_repr, - .tp_as_sequence = &PyUICollection_sqmethods, + .tp_repr = (reprfunc)UICollection::repr, + .tp_as_sequence = &UICollection::sqmethods, .tp_flags = Py_TPFLAGS_DEFAULT, .tp_doc = PyDoc_STR("Iterable, indexable collection of UI objects"), - .tp_iter = (getiterfunc)PyUICollection_iter, - .tp_methods = PyUICollection_methods, // append, remove + .tp_iter = (getiterfunc)UICollection::iter, + .tp_methods = UICollection::methods, // append, remove //.tp_getset = PyUICollection_getset, - .tp_init = (initproc)PyUICollection_init, // just raise an exception + .tp_init = (initproc)UICollection::init, // just raise an exception + //TODO - as static method, not inline lambda def, please .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* { // Does PyUICollectionType need __new__ if it's not supposed to be instantiable by the user? diff --git a/src/UIDrawable.h b/src/UIDrawable.h index d1a6b6e..4e636c5 100644 --- a/src/UIDrawable.h +++ b/src/UIDrawable.h @@ -48,6 +48,13 @@ typedef struct { std::shared_ptr>> data; } PyUICollectionObject; +typedef struct { + PyObject_HEAD + std::shared_ptr>> data; + int index; + int start_size; +} PyUICollectionIterObject; + namespace mcrfpydef { //PyObject* py_instance(std::shared_ptr source); // This function segfaults on tp_alloc for an unknown reason, but works inline with mcrfpydef:: methods. From 76693acd289a378a8a2dffe9e9657084d5db3ce3 Mon Sep 17 00:00:00 2001 From: John McCardle Date: Sat, 13 Apr 2024 00:18:37 -0400 Subject: [PATCH 09/14] delete leftover comments --- src/UICollection.h | 210 +-------------------------------------------- 1 file changed, 1 insertion(+), 209 deletions(-) diff --git a/src/UICollection.h b/src/UICollection.h index 9b6f6da..cf4b559 100644 --- a/src/UICollection.h +++ b/src/UICollection.h @@ -15,7 +15,7 @@ public: class UICollection { - // asdf + // really more of a namespace: all the members are public and static. But being consistent with other UI objects public: static Py_ssize_t len(PyUICollectionObject* self); static PyObject* getitem(PyUICollectionObject* self, Py_ssize_t index); @@ -29,60 +29,6 @@ public: }; namespace mcrfpydef { - - - - //TODO: add this method to class scope; move implementation to .cpp file -/* - static int PyUICollectionIter_init(PyUICollectionIterObject* self, PyObject* args, PyObject* kwds) - { - PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required."); - return -1; - } -*/ - //TODO: add this method to class scope; move implementation to .cpp file -/* - static PyObject* PyUICollectionIter_next(PyUICollectionIterObject* self) - { - if (self->data->size() != self->start_size) - { - PyErr_SetString(PyExc_RuntimeError, "collection changed size during iteration"); - return NULL; - } - - if (self->index > self->start_size - 1) - { - PyErr_SetNone(PyExc_StopIteration); - return NULL; - } - self->index++; - auto vec = self->data.get(); - if (!vec) - { - PyErr_SetString(PyExc_RuntimeError, "the collection store returned a null pointer"); - return NULL; - } - auto target = (*vec)[self->index-1]; - // TODO build PyObject* of the correct UIDrawable subclass to return - //return py_instance(target); - return NULL; - } -*/ - - //TODO: add this method to class scope; move implementation to .cpp file -/* - static PyObject* PyUICollectionIter_repr(PyUICollectionIterObject* self) - { - std::ostringstream ss; - if (!self->data) ss << ""; - else { - ss << "data->size() << " child objects, @ index " << self->index << ")>"; - } - std::string repr_str = ss.str(); - return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); - } -*/ - static PyTypeObject PyUICollectionIterType = { //PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "mcrfpy.UICollectionIter", @@ -108,160 +54,6 @@ namespace mcrfpydef { return NULL; } }; -/* - //TODO: add this method to class scope; move implementation to .cpp file - static Py_ssize_t PyUICollection_len(PyUICollectionObject* self) { - return self->data->size(); - } - - //TODO: add this method to class scope; move implementation to .cpp file - static PyObject* PyUICollection_getitem(PyUICollectionObject* self, Py_ssize_t index) { - // build a Python version of item at self->data[index] - // Copy pasted:: - auto vec = self->data.get(); - if (!vec) - { - PyErr_SetString(PyExc_RuntimeError, "the collection store returned a null pointer"); - return NULL; - } - while (index < 0) index += self->data->size(); - if (index > self->data->size() - 1) - { - PyErr_SetString(PyExc_IndexError, "UICollection index out of range"); - return NULL; - } - auto target = (*vec)[index]; - RET_PY_INSTANCE(target); - return NULL; - - - } - - //TODO: add this static array to class scope; move implementation to .cpp file - static PySequenceMethods PyUICollection_sqmethods = { - .sq_length = (lenfunc)PyUICollection_len, - .sq_item = (ssizeargfunc)PyUICollection_getitem, - //.sq_item_by_index = PyUICollection_getitem - //.sq_slice - return a subset of the iterable - //.sq_ass_item - called when `o[x] = y` is executed (x is any object type) - //.sq_ass_slice - cool; no thanks, for now - //.sq_contains - called when `x in o` is executed - //.sq_ass_item_by_index - called when `o[x] = y` is executed (x is explictly an integer) - }; - - //TODO: add this method to class scope; move implementation to .cpp file - static PyObject* PyUICollection_append(PyUICollectionObject* self, PyObject* o) - { - // if not UIDrawable subclass, reject it - // self->data->push_back( c++ object inside o ); - - // this would be a great use case for .tp_base - if (!PyObject_IsInstance(o, (PyObject*)&PyUIFrameType) && - !PyObject_IsInstance(o, (PyObject*)&PyUISpriteType) && - !PyObject_IsInstance(o, (PyObject*)&PyUICaptionType) && - !PyObject_IsInstance(o, (PyObject*)&PyUIGridType) - ) - { - PyErr_SetString(PyExc_TypeError, "Only Frame, Caption, Sprite, and Grid objects can be added to UICollection"); - return NULL; - } - - if (PyObject_IsInstance(o, (PyObject*)&PyUIFrameType)) - { - PyUIFrameObject* frame = (PyUIFrameObject*)o; - self->data->push_back(frame->data); - } - if (PyObject_IsInstance(o, (PyObject*)&PyUICaptionType)) - { - PyUICaptionObject* caption = (PyUICaptionObject*)o; - self->data->push_back(caption->data); - } - if (PyObject_IsInstance(o, (PyObject*)&PyUISpriteType)) - { - PyUISpriteObject* sprite = (PyUISpriteObject*)o; - self->data->push_back(sprite->data); - } - if (PyObject_IsInstance(o, (PyObject*)&PyUIGridType)) - { - PyUIGridObject* grid = (PyUIGridObject*)o; - self->data->push_back(grid->data); - } - - Py_INCREF(Py_None); - return Py_None; - } - - //TODO: add this method to class scope; move implementation to .cpp file - static PyObject* PyUICollection_remove(PyUICollectionObject* self, PyObject* o) - { - if (!PyLong_Check(o)) - { - PyErr_SetString(PyExc_TypeError, "UICollection.remove requires an integer index to remove"); - return NULL; - } - long index = PyLong_AsLong(o); - if (index >= self->data->size()) - { - PyErr_SetString(PyExc_ValueError, "Index out of range"); - return NULL; - } - else if (index < 0) - { - PyErr_SetString(PyExc_NotImplementedError, "reverse indexing is not implemented."); - return NULL; - } - - // release the shared pointer at self->data[index]; - self->data->erase(self->data->begin() + index); - Py_INCREF(Py_None); - return Py_None; - } - - //TODO: add this static array to class scope; move implementation to .cpp file - - static PyMethodDef PyUICollection_methods[] = { - {"append", (PyCFunction)PyUICollection_append, METH_O}, - //{"extend", (PyCFunction)PyUICollection_extend, METH_O}, // TODO - {"remove", (PyCFunction)PyUICollection_remove, METH_O}, - {NULL, NULL, 0, NULL} - }; - - //TODO: add this method to class scope; move implementation to .cpp file - static PyObject* PyUICollection_repr(PyUICollectionObject* self) - { - std::ostringstream ss; - if (!self->data) ss << ""; - else { - ss << "data->size() << " child objects)>"; - } - std::string repr_str = ss.str(); - return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); - } - - //TODO: add this method to class scope; move implementation to .cpp file - static int PyUICollection_init(PyUICollectionObject* self, PyObject* args, PyObject* kwds) - { - PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required."); - return -1; - } - - //TODO: add this method to class scope; move implementation to .cpp file - static PyObject* PyUICollection_iter(PyUICollectionObject* self) - { - PyUICollectionIterObject* iterObj; - iterObj = (PyUICollectionIterObject*)PyUICollectionIterType.tp_alloc(&PyUICollectionIterType, 0); - if (iterObj == NULL) { - return NULL; // Failed to allocate memory for the iterator object - } - - iterObj->data = self->data; - iterObj->index = 0; - iterObj->start_size = self->data->size(); - - return (PyObject*)iterObj; - } - -*/ static PyTypeObject PyUICollectionType = { //PyVarObject_/HEAD_INIT(NULL, 0) From 567218cd7bb58c87fd980166010752a0e913ef2d Mon Sep 17 00:00:00 2001 From: John McCardle Date: Thu, 18 Apr 2024 21:23:49 -0400 Subject: [PATCH 10/14] UIEntity fixes for the UI.h split: There are segfaults in cos_play, I may have missed a type usage or something --- src/UIEntity.cpp | 89 ++++++++++++++++++++++++++++++++++++++++++++++-- src/UIEntity.h | 27 +++++++++++---- 2 files changed, 107 insertions(+), 9 deletions(-) diff --git a/src/UIEntity.cpp b/src/UIEntity.cpp index abfb8b9..57aeeae 100644 --- a/src/UIEntity.cpp +++ b/src/UIEntity.cpp @@ -1,5 +1,6 @@ #include "UIEntity.h" #include "UIGrid.h" +#include "McRFPy_API.h" UIEntity::UIEntity() {} // this will not work lol. TODO remove default constructor by finding the shared pointer inits that use it @@ -19,8 +20,11 @@ PyObject* UIEntity::at(PyUIEntityObject* self, PyObject* o) { PyErr_SetString(PyExc_ValueError, "Entity cannot access surroundings because it is not associated with a grid"); return NULL; } - + /* PyUIGridPointStateObject* obj = (PyUIGridPointStateObject*)((&mcrfpydef::PyUIGridPointStateType)->tp_alloc(&mcrfpydef::PyUIGridPointStateType, 0)); + */ + auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "GridPointState"); + auto obj = (PyUIGridPointStateObject*)type->tp_alloc(type, 0); //auto target = std::static_pointer_cast(target); obj->data = &(self->data->gridstate[y + self->data->grid->grid_x * x]); obj->grid = self->data->grid; @@ -46,7 +50,7 @@ int UIEntity::init(PyUIEntityObject* self, PyObject* args, PyObject* kwds) { // // Set Texture // - if (texture != NULL && !PyObject_IsInstance(texture, (PyObject*)&mcrfpydef::PyTextureType)){ + if (texture != NULL && !PyObject_IsInstance(texture, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Texture"))){ PyErr_SetString(PyExc_TypeError, "texture must be a mcrfpy.Texture instance"); return -1; } /*else if (texture != NULL) // this section needs to go; texture isn't optional and isn't managed by the UI objects anymore @@ -58,7 +62,7 @@ int UIEntity::init(PyUIEntityObject* self, PyObject* args, PyObject* kwds) { // default tex? }*/ - if (grid != NULL && !PyObject_IsInstance(grid, (PyObject*)&mcrfpydef::PyUIGridType)) { + if (grid != NULL && !PyObject_IsInstance(grid, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Grid"))) { PyErr_SetString(PyExc_TypeError, "grid must be a mcrfpy.Grid instance"); return -1; } @@ -80,3 +84,82 @@ int UIEntity::init(PyUIEntityObject* self, PyObject* args, PyObject* kwds) { } return 0; } + + + +PyObject* UIEntity::get_spritenumber(PyUIEntityObject* self, void* closure) { + return PyLong_FromDouble(self->data->sprite.getSpriteIndex()); +} + +PyObject* sfVector2f_to_PyObject(sf::Vector2f vector) { + return Py_BuildValue("(ff)", vector.x, vector.y); +} + +sf::Vector2f PyObject_to_sfVector2f(PyObject* obj) { + float x, y; + if (!PyArg_ParseTuple(obj, "ff", &x, &y)) { + return sf::Vector2f(); // TODO / reconsider this default: Return default vector on parse error + } + return sf::Vector2f(x, y); +} + +// TODO - deprecate / remove this helper +PyObject* UIGridPointState_to_PyObject(const UIGridPointState& state) { + return PyObject_New(PyObject, (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "GridPointState")); +} + +PyObject* UIGridPointStateVector_to_PyList(const std::vector& vec) { + PyObject* list = PyList_New(vec.size()); + if (!list) return PyErr_NoMemory(); + + for (size_t i = 0; i < vec.size(); ++i) { + PyObject* obj = UIGridPointState_to_PyObject(vec[i]); + if (!obj) { // Cleanup on failure + Py_DECREF(list); + return NULL; + } + PyList_SET_ITEM(list, i, obj); // This steals a reference to obj + } + + return list; +} + +PyObject* UIEntity::get_position(PyUIEntityObject* self, void* closure) { + return sfVector2f_to_PyObject(self->data->position); +} + +int UIEntity::set_position(PyUIEntityObject* self, PyObject* value, void* closure) { + self->data->position = PyObject_to_sfVector2f(value); + return 0; +} + +PyObject* UIEntity::get_gridstate(PyUIEntityObject* self, void* closure) { + // Assuming a function to convert std::vector to PyObject* list + return UIGridPointStateVector_to_PyList(self->data->gridstate); +} + +int UIEntity::set_spritenumber(PyUIEntityObject* self, PyObject* value, void* closure) { + int val; + if (PyLong_Check(value)) + val = PyLong_AsLong(value); + else + { + PyErr_SetString(PyExc_TypeError, "Value must be an integer."); + return -1; + } + //self->data->sprite.sprite_index = val; + self->data->sprite.setSpriteIndex(val); // todone - I don't like ".sprite.sprite" in this stack of UIEntity.UISprite.sf::Sprite + return 0; +} + +PyMethodDef UIEntity::methods[] = { + {"at", (PyCFunction)UIEntity::at, METH_O}, + {NULL, NULL, 0, NULL} +}; + +PyGetSetDef UIEntity::getsetters[] = { + {"position", (getter)UIEntity::get_position, (setter)UIEntity::set_position, "Entity position", NULL}, + {"gridstate", (getter)UIEntity::get_gridstate, NULL, "Grid point states for the entity", NULL}, + {"sprite_number", (getter)UIEntity::get_spritenumber, (setter)UIEntity::set_spritenumber, "Sprite number (index) on the texture on the display", NULL}, + {NULL} /* Sentinel */ +}; diff --git a/src/UIEntity.h b/src/UIEntity.h index 1f37aa6..da14cb2 100644 --- a/src/UIEntity.h +++ b/src/UIEntity.h @@ -25,6 +25,12 @@ class UIGrid; // std::shared_ptr data; //} PyUIEntityObject; +// helper methods with no namespace requirement +static PyObject* sfVector2f_to_PyObject(sf::Vector2f vector); +static sf::Vector2f PyObject_to_sfVector2f(PyObject* obj); +static PyObject* UIGridPointState_to_PyObject(const UIGridPointState& state); +static PyObject* UIGridPointStateVector_to_PyList(const std::vector& vec); + // TODO: make UIEntity a drawable class UIEntity//: public UIDrawable { @@ -41,9 +47,18 @@ public: static PyObject* at(PyUIEntityObject* self, PyObject* o); static int init(PyUIEntityObject* self, PyObject* args, PyObject* kwds); + + static PyObject* get_position(PyUIEntityObject* self, void* closure); + static int set_position(PyUIEntityObject* self, PyObject* value, void* closure); + static PyObject* get_gridstate(PyUIEntityObject* self, void* closure); + static PyObject* get_spritenumber(PyUIEntityObject* self, void* closure); + static int set_spritenumber(PyUIEntityObject* self, PyObject* value, void* closure); + static PyMethodDef methods[]; + static PyGetSetDef getsetters[]; }; namespace mcrfpydef { +/* //TODO: add this method to class scope; move implementation to .cpp file; reconsider for moving to "UIBase.h/.cpp" // TODO: sf::Vector2f convenience functions here might benefit from a PyVectorObject much like PyColorObject // Utility function to convert sf::Vector2f to PyObject* @@ -128,7 +143,7 @@ static int PyUIEntity_set_spritenumber(PyUIEntityObject* self, PyObject* value, } //TODO: add this method to class scope; move implementation to .cpp file -/* + static PyObject* PyUIEntity_at(PyUIEntityObject* self, PyObject* o) { int x, y; @@ -149,7 +164,7 @@ static PyObject* PyUIEntity_at(PyUIEntityObject* self, PyObject* o) obj->entity = self->data; return (PyObject*)obj; } -*/ + //TODO: add this static array to class scope; move implementation to .cpp file static PyMethodDef PyUIEntity_methods[] = { @@ -163,13 +178,13 @@ static PyGetSetDef PyUIEntity_getsetters[] = { {"position", (getter)PyUIEntity_get_position, (setter)PyUIEntity_set_position, "Entity position", NULL}, {"gridstate", (getter)PyUIEntity_get_gridstate, NULL, "Grid point states for the entity", NULL}, {"sprite_number", (getter)PyUIEntity_get_spritenumber, (setter)PyUIEntity_set_spritenumber, "Sprite number (index) on the texture on the display", NULL}, - {NULL} /* Sentinel */ + {NULL} // Sentinel }; //TODO: add this method to class scope; forward declaration not required after .h/.cpp split //static int PyUIEntity_init(PyUIEntityObject*, PyObject*, PyObject*); // forward declare - +*/ // Define the PyTypeObject for UIEntity static PyTypeObject PyUIEntityType = { //PyVarObject_HEAD_INIT(NULL, 0) @@ -179,8 +194,8 @@ static PyTypeObject PyUIEntityType = { // Methods omitted for brevity .tp_flags = Py_TPFLAGS_DEFAULT, .tp_doc = "UIEntity objects", - .tp_methods = PyUIEntity_methods, - .tp_getset = PyUIEntity_getsetters, + .tp_methods = UIEntity::methods, + .tp_getset = UIEntity::getsetters, .tp_init = (initproc)UIEntity::init, .tp_new = PyType_GenericNew, }; From 5d6af324bf2a6772a681a62b9dbe399b9c4c78d8 Mon Sep 17 00:00:00 2001 From: John McCardle Date: Thu, 18 Apr 2024 22:14:57 -0400 Subject: [PATCH 11/14] UIFrame - moving static method into class namespace; no type object access --- src/UIFrame.cpp | 205 ++++++++++++++++++++++++++++++++++++++++++--- src/UIFrame.h | 218 +++--------------------------------------------- 2 files changed, 206 insertions(+), 217 deletions(-) diff --git a/src/UIFrame.cpp b/src/UIFrame.cpp index 3452a58..8c9561c 100644 --- a/src/UIFrame.cpp +++ b/src/UIFrame.cpp @@ -58,23 +58,208 @@ void UIFrame::render(sf::Vector2f offset) PyObject* UIFrame::get_children(PyUIFrameObject* self, void* closure) { // create PyUICollection instance pointing to self->data->children - PyUICollectionObject* o = (PyUICollectionObject*)mcrfpydef::PyUICollectionType.tp_alloc(&mcrfpydef::PyUICollectionType, 0); + //PyUICollectionObject* o = (PyUICollectionObject*)mcrfpydef::PyUICollectionType.tp_alloc(&mcrfpydef::PyUICollectionType, 0); + auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "UICollection"); + auto o = (PyUICollectionObject*)type->tp_alloc(type, 0); if (o) o->data = self->data->children; return (PyObject*)o; } -namespace mcrfpydef { - - // TODO: move to class scope; but this should at least get us compiling - static PyObject* PyUIFrame_get_children(PyUIFrameObject* self, void* closure) +PyObject* UIFrame::get_float_member(PyUIFrameObject* self, void* closure) +{ + auto member_ptr = reinterpret_cast(closure); + if (member_ptr == 0) + return PyFloat_FromDouble(self->data->box.getPosition().x); + else if (member_ptr == 1) + return PyFloat_FromDouble(self->data->box.getPosition().y); + else if (member_ptr == 2) + return PyFloat_FromDouble(self->data->box.getSize().x); + else if (member_ptr == 3) + return PyFloat_FromDouble(self->data->box.getSize().y); + else if (member_ptr == 4) + return PyFloat_FromDouble(self->data->box.getOutlineThickness()); + else { - // create PyUICollection instance pointing to self->data->children - PyUICollectionObject* o = (PyUICollectionObject*)PyUICollectionType.tp_alloc(&PyUICollectionType, 0); - if (o) - o->data = self->data->children; - return (PyObject*)o; + PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); + return nullptr; } } +int UIFrame::set_float_member(PyUIFrameObject* self, PyObject* value, void* closure) +{ + float val; + auto member_ptr = reinterpret_cast(closure); + if (PyFloat_Check(value)) + { + val = PyFloat_AsDouble(value); + } + else if (PyLong_Check(value)) + { + val = PyLong_AsLong(value); + } + else + { + PyErr_SetString(PyExc_TypeError, "Value must be an integer."); + return -1; + } + if (member_ptr == 0) //x + self->data->box.setPosition(val, self->data->box.getPosition().y); + else if (member_ptr == 1) //y + self->data->box.setPosition(self->data->box.getPosition().x, val); + else if (member_ptr == 2) //w + self->data->box.setSize(sf::Vector2f(val, self->data->box.getSize().y)); + else if (member_ptr == 3) //h + self->data->box.setSize(sf::Vector2f(self->data->box.getSize().x, val)); + else if (member_ptr == 4) //outline + self->data->box.setOutlineThickness(val); + return 0; +} + +PyObject* UIFrame::get_color_member(PyUIFrameObject* self, void* closure) +{ + // validate closure (should be impossible to be wrong, but it's thorough) + auto member_ptr = reinterpret_cast(closure); + if (member_ptr != 0 && member_ptr != 1) + { + PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); + return nullptr; + } + //PyTypeObject* colorType = &PyColorType; + auto colorType = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Color"); + 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); + + // fetch correct member data + sf::Color color; + if (member_ptr == 0) + { + color = self->data->box.getFillColor(); + //return Py_BuildValue("(iii)", color.r, color.g, color.b); + } + else if (member_ptr == 1) + { + color = self->data->box.getOutlineColor(); + //return Py_BuildValue("(iii)", color.r, color.g, color.b); + } + + return PyColor(color).pyObject(); +} + +int UIFrame::set_color_member(PyUIFrameObject* self, PyObject* value, void* closure) +{ + //TODO: this logic of (PyColor instance OR tuple -> sf::color) should be encapsulated for reuse + auto member_ptr = reinterpret_cast(closure); + int r, g, b, a; + if (PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Color"))) + { + sf::Color c = ((PyColorObject*)value)->data; + r = c.r; g = c.g; b = c.b; a = c.a; + } + else if (!PyTuple_Check(value) || PyTuple_Size(value) < 3 || PyTuple_Size(value) > 4) + { + // reject non-Color, non-tuple value + PyErr_SetString(PyExc_TypeError, "Value must be a tuple of 3 or 4 integers or an mcrfpy.Color object."); + return -1; + } + else // get value from tuples + { + r = PyLong_AsLong(PyTuple_GetItem(value, 0)); + g = PyLong_AsLong(PyTuple_GetItem(value, 1)); + b = PyLong_AsLong(PyTuple_GetItem(value, 2)); + a = 255; + + if (PyTuple_Size(value) == 4) + { + a = PyLong_AsLong(PyTuple_GetItem(value, 3)); + } + } + + if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255 || a < 0 || a > 255) + { + PyErr_SetString(PyExc_ValueError, "Color values must be between 0 and 255."); + return -1; + } + + if (member_ptr == 0) + { + self->data->box.setFillColor(sf::Color(r, g, b, a)); + } + else if (member_ptr == 1) + { + self->data->box.setOutlineColor(sf::Color(r, g, b, a)); + } + else + { + PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); + return -1; + } + + return 0; +} + +PyGetSetDef UIFrame::getsetters[] = { + {"x", (getter)UIFrame::get_float_member, (setter)UIFrame::set_float_member, "X coordinate of top-left corner", (void*)0}, + {"y", (getter)UIFrame::get_float_member, (setter)UIFrame::set_float_member, "Y coordinate of top-left corner", (void*)1}, + {"w", (getter)UIFrame::get_float_member, (setter)UIFrame::set_float_member, "width of the rectangle", (void*)2}, + {"h", (getter)UIFrame::get_float_member, (setter)UIFrame::set_float_member, "height of the rectangle", (void*)3}, + {"outline", (getter)UIFrame::get_float_member, (setter)UIFrame::set_float_member, "Thickness of the border", (void*)4}, + {"fill_color", (getter)UIFrame::get_color_member, (setter)UIFrame::set_color_member, "Fill color of the rectangle", (void*)0}, + {"outline_color", (getter)UIFrame::get_color_member, (setter)UIFrame::set_color_member, "Outline color of the rectangle", (void*)1}, + {"children", (getter)UIFrame::get_children, NULL, "UICollection of objects on top of this one", NULL}, + {"click", (getter)UIDrawable::get_click, (setter)UIDrawable::set_click, "Object called with (x, y, button) when clicked", (void*)PyObjectsEnum::UIFRAME}, + {NULL} +}; + +PyObject* UIFrame::repr(PyUIFrameObject* self) +{ + std::ostringstream ss; + if (!self->data) ss << ""; + else { + auto box = self->data->box; + auto fc = box.getFillColor(); + auto oc = box.getOutlineColor(); + ss << "data->children->size() << " child objects" << + ")>"; + } + std::string repr_str = ss.str(); + return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); +} + +int UIFrame::init(PyUIFrameObject* self, PyObject* args, PyObject* kwds) +{ + //std::cout << "Init called\n"; + const char* keywords[] = { "x", "y", "w", "h", "fill_color", "outline_color", "outline", nullptr }; + float x = 0.0f, y = 0.0f, w = 0.0f, h=0.0f, outline=0.0f; + PyObject* fill_color = 0; + PyObject* outline_color = 0; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "ffff|OOf", const_cast(keywords), &x, &y, &w, &h, &fill_color, &outline_color, &outline)) + { + return -1; + } + + self->data->box.setPosition(sf::Vector2f(x, y)); + self->data->box.setSize(sf::Vector2f(w, h)); + self->data->box.setOutlineThickness(outline); + // getsetter abuse because I haven't standardized Color object parsing (TODO) + int err_val = 0; + if (fill_color && fill_color != Py_None) err_val = UIFrame::set_color_member(self, fill_color, (void*)0); + else self->data->box.setFillColor(sf::Color(0,0,0,255)); + if (err_val) return err_val; + if (outline_color && outline_color != Py_None) err_val = UIFrame::set_color_member(self, outline_color, (void*)1); + else self->data->box.setOutlineColor(sf::Color(128,128,128,255)); + if (err_val) return err_val; + return 0; +} diff --git a/src/UIFrame.h b/src/UIFrame.h index 356f0cb..e937ac0 100644 --- a/src/UIFrame.h +++ b/src/UIFrame.h @@ -34,215 +34,19 @@ public: virtual UIDrawable* click_at(sf::Vector2f point) override final; static PyObject* get_children(PyUIFrameObject* self, void* closure); + + static PyObject* get_float_member(PyUIFrameObject* self, void* closure); + static int set_float_member(PyUIFrameObject* self, PyObject* value, void* closure); + static PyObject* get_color_member(PyUIFrameObject* self, void* closure); + static int set_color_member(PyUIFrameObject* self, PyObject* value, void* closure); + static PyGetSetDef getsetters[]; + static PyObject* repr(PyUIFrameObject* self); + static int init(PyUIFrameObject* self, PyObject* args, PyObject* kwds); }; namespace mcrfpydef { //TODO: add this method to class scope; move implementation to .cpp file - static PyObject* PyUIFrame_get_float_member(PyUIFrameObject* self, void* closure) - { - auto member_ptr = reinterpret_cast(closure); - if (member_ptr == 0) - return PyFloat_FromDouble(self->data->box.getPosition().x); - else if (member_ptr == 1) - return PyFloat_FromDouble(self->data->box.getPosition().y); - else if (member_ptr == 2) - return PyFloat_FromDouble(self->data->box.getSize().x); - else if (member_ptr == 3) - return PyFloat_FromDouble(self->data->box.getSize().y); - else if (member_ptr == 4) - return PyFloat_FromDouble(self->data->box.getOutlineThickness()); - else - { - PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); - return nullptr; - } - } - //TODO: add this method to class scope; move implementation to .cpp file - static int PyUIFrame_set_float_member(PyUIFrameObject* self, PyObject* value, void* closure) - { - float val; - auto member_ptr = reinterpret_cast(closure); - if (PyFloat_Check(value)) - { - val = PyFloat_AsDouble(value); - } - else if (PyLong_Check(value)) - { - val = PyLong_AsLong(value); - } - else - { - PyErr_SetString(PyExc_TypeError, "Value must be an integer."); - return -1; - } - if (member_ptr == 0) //x - self->data->box.setPosition(val, self->data->box.getPosition().y); - else if (member_ptr == 1) //y - self->data->box.setPosition(self->data->box.getPosition().x, val); - else if (member_ptr == 2) //w - self->data->box.setSize(sf::Vector2f(val, self->data->box.getSize().y)); - else if (member_ptr == 3) //h - self->data->box.setSize(sf::Vector2f(self->data->box.getSize().x, val)); - else if (member_ptr == 4) //outline - self->data->box.setOutlineThickness(val); - return 0; - } - - //TODO: add this method to class scope; move implementation to .cpp file - static PyObject* PyUIFrame_get_color_member(PyUIFrameObject* self, void* closure) - { - // validate closure (should be impossible to be wrong, but it's thorough) - auto member_ptr = reinterpret_cast(closure); - if (member_ptr != 0 && member_ptr != 1) - { - PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); - return nullptr; - } - 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); - - // fetch correct member data - sf::Color color; - if (member_ptr == 0) - { - color = self->data->box.getFillColor(); - //return Py_BuildValue("(iii)", color.r, color.g, color.b); - } - else if (member_ptr == 1) - { - color = self->data->box.getOutlineColor(); - //return Py_BuildValue("(iii)", color.r, color.g, color.b); - } - - return PyColor(color).pyObject(); - } - - //TODO: add this method to class scope; move implementation to .cpp file - static int PyUIFrame_set_color_member(PyUIFrameObject* self, PyObject* value, void* closure) - { - //TODO: this logic of (PyColor instance OR tuple -> sf::color) should be encapsulated for reuse - auto member_ptr = reinterpret_cast(closure); - int r, g, b, a; - if (PyObject_IsInstance(value, (PyObject*)&PyColorType)) - { - sf::Color c = ((PyColorObject*)value)->data; - r = c.r; g = c.g; b = c.b; a = c.a; - } - else if (!PyTuple_Check(value) || PyTuple_Size(value) < 3 || PyTuple_Size(value) > 4) - { - // reject non-Color, non-tuple value - PyErr_SetString(PyExc_TypeError, "Value must be a tuple of 3 or 4 integers or an mcrfpy.Color object."); - return -1; - } - else // get value from tuples - { - r = PyLong_AsLong(PyTuple_GetItem(value, 0)); - g = PyLong_AsLong(PyTuple_GetItem(value, 1)); - b = PyLong_AsLong(PyTuple_GetItem(value, 2)); - a = 255; - - if (PyTuple_Size(value) == 4) - { - a = PyLong_AsLong(PyTuple_GetItem(value, 3)); - } - } - - if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255 || a < 0 || a > 255) - { - PyErr_SetString(PyExc_ValueError, "Color values must be between 0 and 255."); - return -1; - } - - if (member_ptr == 0) - { - self->data->box.setFillColor(sf::Color(r, g, b, a)); - } - else if (member_ptr == 1) - { - self->data->box.setOutlineColor(sf::Color(r, g, b, a)); - } - else - { - PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); - return -1; - } - - return 0; - } - - //TODO: add this method to class scope; move implementation to .cpp file - //static PyObject* PyUIFrame_get_children(PyUIFrameObject*, void*); // forward declaration until UICollection is defined - // implementation after the PyUICollectionType definition - - //TODO: add this static array to class scope; move implementation to .cpp file - static PyGetSetDef PyUIFrame_getsetters[] = { - {"x", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "X coordinate of top-left corner", (void*)0}, - {"y", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "Y coordinate of top-left corner", (void*)1}, - {"w", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "width of the rectangle", (void*)2}, - {"h", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "height of the rectangle", (void*)3}, - {"outline", (getter)PyUIFrame_get_float_member, (setter)PyUIFrame_set_float_member, "Thickness of the border", (void*)4}, - {"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)UIFrame::get_children, NULL, "UICollection of objects on top of this one", NULL}, - {"click", (getter)UIDrawable::get_click, (setter)UIDrawable::set_click, "Object called with (x, y, button) when clicked", (void*)PyObjectsEnum::UIFRAME}, - {NULL} - }; - - //TODO: add this method to class scope; move implementation to .cpp file - static PyObject* PyUIFrame_repr(PyUIFrameObject* self) - { - std::ostringstream ss; - if (!self->data) ss << ""; - else { - auto box = self->data->box; - auto fc = box.getFillColor(); - auto oc = box.getOutlineColor(); - ss << "data->children->size() << " child objects" << - ")>"; - } - std::string repr_str = ss.str(); - return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); - } - - //TODO: add this method to class scope; move implementation to .cpp file - static int PyUIFrame_init(PyUIFrameObject* self, PyObject* args, PyObject* kwds) - { - //std::cout << "Init called\n"; - static const char* keywords[] = { "x", "y", "w", "h", "fill_color", "outline_color", "outline", nullptr }; - float x = 0.0f, y = 0.0f, w = 0.0f, h=0.0f, outline=0.0f; - PyObject* fill_color = 0; - PyObject* outline_color = 0; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "ffff|OOf", const_cast(keywords), &x, &y, &w, &h, &fill_color, &outline_color, &outline)) - { - return -1; - } - - self->data->box.setPosition(sf::Vector2f(x, y)); - self->data->box.setSize(sf::Vector2f(w, h)); - self->data->box.setOutlineThickness(outline); - // getsetter abuse because I haven't standardized Color object parsing (TODO) - int err_val = 0; - if (fill_color && fill_color != Py_None) err_val = PyUIFrame_set_color_member(self, fill_color, (void*)0); - else self->data->box.setFillColor(sf::Color(0,0,0,255)); - if (err_val) return err_val; - if (outline_color && outline_color != Py_None) err_val = PyUIFrame_set_color_member(self, outline_color, (void*)1); - else self->data->box.setOutlineColor(sf::Color(128,128,128,255)); - if (err_val) return err_val; - return 0; - } static PyTypeObject PyUIFrameType = { //PyVarObject_HEAD_INIT(NULL, 0) @@ -255,7 +59,7 @@ namespace mcrfpydef { obj->data.reset(); Py_TYPE(self)->tp_free(self); }, - .tp_repr = (reprfunc)PyUIFrame_repr, + .tp_repr = (reprfunc)UIFrame::repr, //.tp_hash = NULL, //.tp_iter //.tp_iternext @@ -263,9 +67,9 @@ namespace mcrfpydef { .tp_doc = PyDoc_STR("docstring"), //.tp_methods = PyUIFrame_methods, //.tp_members = PyUIFrame_members, - .tp_getset = PyUIFrame_getsetters, + .tp_getset = UIFrame::getsetters, //.tp_base = NULL, - .tp_init = (initproc)PyUIFrame_init, + .tp_init = (initproc)UIFrame::init, .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* { PyUIFrameObject* self = (PyUIFrameObject*)type->tp_alloc(type, 0); From 2cb7339535990d91e5d256bca8533ec1c32bb11a Mon Sep 17 00:00:00 2001 From: John McCardle Date: Fri, 19 Apr 2024 21:19:25 -0400 Subject: [PATCH 12/14] UIGrid.h/.cpp cleanup. I have reservations about the UIEntityCollection[Iter] classes + methods living there, but not enough to fix it right now. --- src/UIGrid.cpp | 408 ++++++++++++++++++++++++++++++++++++++ src/UIGrid.h | 519 +++++++------------------------------------------ 2 files changed, 474 insertions(+), 453 deletions(-) diff --git a/src/UIGrid.cpp b/src/UIGrid.cpp index 69f2375..4a0097d 100644 --- a/src/UIGrid.cpp +++ b/src/UIGrid.cpp @@ -1,5 +1,6 @@ #include "UIGrid.h" #include "GameEngine.h" +#include "McRFPy_API.h" UIGrid::UIGrid() {} @@ -198,3 +199,410 @@ UIDrawable* UIGrid::click_at(sf::Vector2f point) } return NULL; } + + +int UIGrid::init(PyUIGridObject* self, PyObject* args, PyObject* kwds) { + int grid_x, grid_y; + PyObject* textureObj; + float box_x, box_y, box_w, box_h; + + if (!PyArg_ParseTuple(args, "iiOffff", &grid_x, &grid_y, &textureObj, &box_x, &box_y, &box_w, &box_h)) { + return -1; // If parsing fails, return an error + } + + // Convert PyObject texture to IndexTexture* + // This requires the texture object to have been initialized similar to UISprite's texture handling + + //if (!PyObject_IsInstance(textureObj, (PyObject*)&PyTextureType)) { + if (!PyObject_IsInstance(textureObj, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Texture"))) { + PyErr_SetString(PyExc_TypeError, "texture must be a mcrfpy.Texture instance"); + return -1; + } + PyTextureObject* pyTexture = reinterpret_cast(textureObj); + // TODO (7DRL day 2, item 4.) use shared_ptr / PyTextureObject on UIGrid + //IndexTexture* texture = pyTexture->data.get(); + + // Initialize UIGrid + //self->data = new UIGrid(grid_x, grid_y, texture, sf::Vector2f(box_x, box_y), sf::Vector2f(box_w, box_h)); + self->data = std::make_shared(grid_x, grid_y, pyTexture->data, + sf::Vector2f(box_x, box_y), sf::Vector2f(box_w, box_h)); + return 0; // Success +} + +PyObject* UIGrid::get_grid_size(PyUIGridObject* self, void* closure) { + return Py_BuildValue("(ii)", self->data->grid_x, self->data->grid_y); +} + +PyObject* UIGrid::get_position(PyUIGridObject* self, void* closure) { + auto& box = self->data->box; + return Py_BuildValue("(ff)", box.getPosition().x, box.getPosition().y); +} + +int UIGrid::set_position(PyUIGridObject* self, PyObject* value, void* closure) { + float x, y; + if (!PyArg_ParseTuple(value, "ff", &x, &y)) { + PyErr_SetString(PyExc_ValueError, "Position must be a tuple of two floats"); + return -1; + } + self->data->box.setPosition(x, y); + return 0; +} + +PyObject* UIGrid::get_size(PyUIGridObject* self, void* closure) { + auto& box = self->data->box; + return Py_BuildValue("(ff)", box.getSize().x, box.getSize().y); +} + +int UIGrid::set_size(PyUIGridObject* self, PyObject* value, void* closure) { + float w, h; + if (!PyArg_ParseTuple(value, "ff", &w, &h)) { + PyErr_SetString(PyExc_ValueError, "Size must be a tuple of two floats"); + return -1; + } + self->data->box.setSize(sf::Vector2f(w, h)); + return 0; +} + +PyObject* UIGrid::get_center(PyUIGridObject* self, void* closure) { + return Py_BuildValue("(ff)", self->data->center_x, self->data->center_y); +} + +int UIGrid::set_center(PyUIGridObject* self, PyObject* value, void* closure) { + float x, y; + if (!PyArg_ParseTuple(value, "ff", &x, &y)) { + PyErr_SetString(PyExc_ValueError, "Size must be a tuple of two floats"); + return -1; + } + self->data->center_x = x; + self->data->center_y = y; + return 0; +} + +PyObject* UIGrid::get_float_member(PyUIGridObject* self, void* closure) +{ + auto member_ptr = reinterpret_cast(closure); + if (member_ptr == 0) // x + return PyFloat_FromDouble(self->data->box.getPosition().x); + else if (member_ptr == 1) // y + return PyFloat_FromDouble(self->data->box.getPosition().y); + else if (member_ptr == 2) // w + return PyFloat_FromDouble(self->data->box.getSize().x); + else if (member_ptr == 3) // h + return PyFloat_FromDouble(self->data->box.getSize().y); + else if (member_ptr == 4) // center_x + return PyFloat_FromDouble(self->data->center_x); + else if (member_ptr == 5) // center_y + return PyFloat_FromDouble(self->data->center_y); + else if (member_ptr == 6) // zoom + return PyFloat_FromDouble(self->data->zoom); + else + { + PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); + return nullptr; + } +} + +int UIGrid::set_float_member(PyUIGridObject* self, PyObject* value, void* closure) +{ + float val; + auto member_ptr = reinterpret_cast(closure); + if (PyFloat_Check(value)) + { + val = PyFloat_AsDouble(value); + } + else if (PyLong_Check(value)) + { + val = PyLong_AsLong(value); + } + else + { + PyErr_SetString(PyExc_TypeError, "Value must be a floating point number."); + return -1; + } + if (member_ptr == 0) // x + self->data->box.setPosition(val, self->data->box.getPosition().y); + else if (member_ptr == 1) // y + self->data->box.setPosition(self->data->box.getPosition().x, val); + else if (member_ptr == 2) // w + self->data->box.setSize(sf::Vector2f(val, self->data->box.getSize().y)); + else if (member_ptr == 3) // h + self->data->box.setSize(sf::Vector2f(self->data->box.getSize().x, val)); + else if (member_ptr == 4) // center_x + self->data->center_x = val; + else if (member_ptr == 5) // center_y + self->data->center_y = val; + else if (member_ptr == 6) // zoom + self->data->zoom = val; + return 0; +} +// TODO (7DRL Day 2, item 5.) return Texture object +/* +PyObject* UIGrid::get_texture(PyUIGridObject* self, void* closure) { + Py_INCREF(self->texture); + return self->texture; +} +*/ + +PyObject* UIGrid::get_texture(PyUIGridObject* self, void* closure) { + //return self->data->getTexture()->pyObject(); + // PyObject_GetAttrString(McRFPy_API::mcrf_module, "GridPointState") + //PyTextureObject* obj = (PyTextureObject*)((&PyTextureType)->tp_alloc(&PyTextureType, 0)); + auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Texture"); + auto obj = (PyTextureObject*)type->tp_alloc(type, 0); + obj->data = self->data->getTexture(); + return (PyObject*)obj; +} + +PyObject* UIGrid::py_at(PyUIGridObject* self, PyObject* o) +{ + int x, y; + if (!PyArg_ParseTuple(o, "ii", &x, &y)) { + PyErr_SetString(PyExc_TypeError, "UIGrid.at requires two integer arguments: (x, y)"); + return NULL; + } + if (x < 0 || x >= self->data->grid_x) { + PyErr_SetString(PyExc_ValueError, "x value out of range (0, Grid.grid_y)"); + return NULL; + } + if (y < 0 || y >= self->data->grid_y) { + PyErr_SetString(PyExc_ValueError, "y value out of range (0, Grid.grid_y)"); + return NULL; + } + + //PyUIGridPointObject* obj = (PyUIGridPointObject*)((&PyUIGridPointType)->tp_alloc(&PyUIGridPointType, 0)); + auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "GridPoint"); + auto obj = (PyUIGridPointObject*)type->tp_alloc(type, 0); + //auto target = std::static_pointer_cast(target); + obj->data = &(self->data->points[x + self->data->grid_x * y]); + obj->grid = self->data; + return (PyObject*)obj; +} + +PyMethodDef UIGrid::methods[] = { + {"at", (PyCFunction)UIGrid::py_at, METH_O}, + {NULL, NULL, 0, NULL} +}; + + +PyGetSetDef UIGrid::getsetters[] = { + + // TODO - refactor into get_vector_member with field identifier values `(void*)n` + {"grid_size", (getter)UIGrid::get_grid_size, NULL, "Grid dimensions (grid_x, grid_y)", NULL}, + {"position", (getter)UIGrid::get_position, (setter)UIGrid::set_position, "Position of the grid (x, y)", NULL}, + {"size", (getter)UIGrid::get_size, (setter)UIGrid::set_size, "Size of the grid (width, height)", NULL}, + {"center", (getter)UIGrid::get_center, (setter)UIGrid::set_center, "Grid coordinate at the center of the Grid's view (pan)", NULL}, + + {"entities", (getter)UIGrid::get_children, NULL, "EntityCollection of entities on this grid", NULL}, + + {"x", (getter)UIGrid::get_float_member, (setter)UIGrid::set_float_member, "top-left corner X-coordinate", (void*)0}, + {"y", (getter)UIGrid::get_float_member, (setter)UIGrid::set_float_member, "top-left corner Y-coordinate", (void*)1}, + {"w", (getter)UIGrid::get_float_member, (setter)UIGrid::set_float_member, "visible widget width", (void*)2}, + {"h", (getter)UIGrid::get_float_member, (setter)UIGrid::set_float_member, "visible widget height", (void*)3}, + {"center_x", (getter)UIGrid::get_float_member, (setter)UIGrid::set_float_member, "center of the view X-coordinate", (void*)4}, + {"center_y", (getter)UIGrid::get_float_member, (setter)UIGrid::set_float_member, "center of the view Y-coordinate", (void*)5}, + {"zoom", (getter)UIGrid::get_float_member, (setter)UIGrid::set_float_member, "zoom factor for displaying the Grid", (void*)6}, + + {"click", (getter)UIDrawable::get_click, (setter)UIDrawable::set_click, "Object called with (x, y, button) when clicked", (void*)PyObjectsEnum::UIGRID}, + + {"texture", (getter)UIGrid::get_texture, NULL, "Texture of the grid", NULL}, //TODO 7DRL-day2-item5 + {NULL} /* Sentinel */ +}; + +PyObject* UIGrid::get_children(PyUIGridObject* self, void* closure) +{ + // create PyUICollection instance pointing to self->data->children + //PyUIEntityCollectionObject* o = (PyUIEntityCollectionObject*)PyUIEntityCollectionType.tp_alloc(&PyUIEntityCollectionType, 0); + auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "EntityCollection"); + auto o = (PyUIEntityCollectionObject*)type->tp_alloc(type, 0); + if (o) { + o->data = self->data->entities; // todone. / BUGFIX - entities isn't a shared pointer on UIGrid, what to do? -- I made it a sp>> + o->grid = self->data; + } + return (PyObject*)o; +} + +/* // TODO standard pointer would need deleted, but I opted for a shared pointer. tp_dealloc currently not even defined in the PyTypeObject +void PyUIGrid_dealloc(PyUIGridObject* self) { + delete self->data; // Clean up the allocated UIGrid object + Py_TYPE(self)->tp_free((PyObject*)self); +} +*/ + +int UIEntityCollectionIter::init(PyUIEntityCollectionIterObject* self, PyObject* args, PyObject* kwds) +{ + PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required."); + return -1; +} + +PyObject* UIEntityCollectionIter::next(PyUIEntityCollectionIterObject* self) +{ + if (self->data->size() != self->start_size) + { + PyErr_SetString(PyExc_RuntimeError, "collection changed size during iteration"); + return NULL; + } + + if (self->index > self->start_size - 1) + { + PyErr_SetNone(PyExc_StopIteration); + return NULL; + } + self->index++; + auto vec = self->data.get(); + if (!vec) + { + PyErr_SetString(PyExc_RuntimeError, "the collection store returned a null pointer"); + return NULL; + } + // Advance list iterator since Entities are not stored in a vector (if this code even worked) + // vectors only: //auto target = (*vec)[self->index-1]; + //auto l_front = (*vec).begin(); + //std::advance(l_front, self->index-1); + + // TODO build PyObject* of the correct UIDrawable subclass to return + //return py_instance(target); + return NULL; +} + +PyObject* UIEntityCollectionIter::repr(PyUIEntityCollectionIterObject* self) +{ + std::ostringstream ss; + if (!self->data) ss << ""; + else { + ss << "data->size() << " child objects, @ index " << self->index << ")>"; + } + std::string repr_str = ss.str(); + return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); +} + +Py_ssize_t UIEntityCollection::len(PyUIEntityCollectionObject* self) { + return self->data->size(); +} + +PyObject* UIEntityCollection::getitem(PyUIEntityCollectionObject* self, Py_ssize_t index) { + // build a Python version of item at self->data[index] + // Copy pasted:: + auto vec = self->data.get(); + if (!vec) + { + PyErr_SetString(PyExc_RuntimeError, "the collection store returned a null pointer"); + return NULL; + } + while (index < 0) index += self->data->size(); + if (index > self->data->size() - 1) + { + PyErr_SetString(PyExc_IndexError, "EntityCollection index out of range"); + return NULL; + } + auto l_begin = (*vec).begin(); + std::advance(l_begin, index); + auto target = *l_begin; //auto target = (*vec)[index]; + //RET_PY_INSTANCE(target); + // construct and return an entity object that points directly into the UIGrid's entity vector + //PyUIEntityObject* o = (PyUIEntityObject*)((&PyUIEntityType)->tp_alloc(&PyUIEntityType, 0)); + auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Entity"); + auto o = (PyUIEntityObject*)type->tp_alloc(type, 0); + auto p = std::static_pointer_cast(target); + o->data = p; + return (PyObject*)o; +return NULL; + + +} + +PySequenceMethods UIEntityCollection::sqmethods = { + .sq_length = (lenfunc)UIEntityCollection::len, + .sq_item = (ssizeargfunc)UIEntityCollection::getitem, + //.sq_item_by_index = UIEntityCollection::getitem + //.sq_slice - return a subset of the iterable + //.sq_ass_item - called when `o[x] = y` is executed (x is any object type) + //.sq_ass_slice - cool; no thanks, for now + //.sq_contains - called when `x in o` is executed + //.sq_ass_item_by_index - called when `o[x] = y` is executed (x is explictly an integer) +}; + +PyObject* UIEntityCollection::append(PyUIEntityCollectionObject* self, PyObject* o) +{ + // if not UIDrawable subclass, reject it + // self->data->push_back( c++ object inside o ); + + // this would be a great use case for .tp_base + //if (!PyObject_IsInstance(o, (PyObject*)&PyUIEntityType)) + if (!PyObject_IsInstance(o, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Entity"))) + { + PyErr_SetString(PyExc_TypeError, "Only Entity objects can be added to EntityCollection"); + return NULL; + } + PyUIEntityObject* entity = (PyUIEntityObject*)o; + self->data->push_back(entity->data); + entity->data->grid = self->grid; + + Py_INCREF(Py_None); + return Py_None; +} + +PyObject* UIEntityCollection::remove(PyUIEntityCollectionObject* self, PyObject* o) +{ + if (!PyLong_Check(o)) + { + PyErr_SetString(PyExc_TypeError, "UICollection.remove requires an integer index to remove"); + return NULL; + } + long index = PyLong_AsLong(o); + if (index >= self->data->size()) + { + PyErr_SetString(PyExc_ValueError, "Index out of range"); + return NULL; + } + else if (index < 0) + { + PyErr_SetString(PyExc_NotImplementedError, "reverse indexing is not implemented."); + return NULL; + } + + // release the shared pointer at correct part of the list + self->data->erase(std::next(self->data->begin(), index)); + Py_INCREF(Py_None); + return Py_None; +} + +PyMethodDef UIEntityCollection::methods[] = { + {"append", (PyCFunction)UIEntityCollection::append, METH_O}, + //{"extend", (PyCFunction)UIEntityCollection::extend, METH_O}, // TODO + {"remove", (PyCFunction)UIEntityCollection::remove, METH_O}, + {NULL, NULL, 0, NULL} +}; + +PyObject* UIEntityCollection::repr(PyUIEntityCollectionObject* self) +{ + std::ostringstream ss; + if (!self->data) ss << ""; + else { + ss << "data->size() << " child objects)>"; + } + std::string repr_str = ss.str(); + return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); +} + +int UIEntityCollection::init(PyUIEntityCollectionObject* self, PyObject* args, PyObject* kwds) +{ + PyErr_SetString(PyExc_TypeError, "EntityCollection cannot be instantiated: a C++ data source is required."); + return -1; +} + +PyObject* UIEntityCollection::iter(PyUIEntityCollectionObject* self) +{ + //PyUIEntityCollectionIterObject* iterObj; + //iterObj = (PyUIEntityCollectionIterObject*)PyUIEntityCollectionIterType.tp_alloc(&PyUIEntityCollectionIterType, 0); + auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "EntityCollectionIter"); + auto iterObj = (PyUIEntityCollectionIterObject*)type->tp_alloc(type, 0); + if (iterObj == NULL) { + return NULL; // Failed to allocate memory for the iterator object + } + + iterObj->data = self->data; + iterObj->index = 0; + iterObj->start_size = self->data->size(); + + return (PyObject*)iterObj; +} diff --git a/src/UIGrid.h b/src/UIGrid.h index a67fc2a..62be061 100644 --- a/src/UIGrid.h +++ b/src/UIGrid.h @@ -42,235 +42,61 @@ public: sf::RenderTexture renderTexture; std::vector points; std::shared_ptr>> entities; + + static int init(PyUIGridObject* self, PyObject* args, PyObject* kwds); + static PyObject* get_grid_size(PyUIGridObject* self, void* closure); + static PyObject* get_position(PyUIGridObject* self, void* closure); + static int set_position(PyUIGridObject* self, PyObject* value, void* closure); + static PyObject* get_size(PyUIGridObject* self, void* closure); + static int set_size(PyUIGridObject* self, PyObject* value, void* closure); + static PyObject* get_center(PyUIGridObject* self, void* closure); + static int set_center(PyUIGridObject* self, PyObject* value, void* closure); + static PyObject* get_float_member(PyUIGridObject* self, void* closure); + static int set_float_member(PyUIGridObject* self, PyObject* value, void* closure); + static PyObject* get_texture(PyUIGridObject* self, void* closure); + static PyObject* py_at(PyUIGridObject* self, PyObject* o); + static PyMethodDef methods[]; + static PyGetSetDef getsetters[]; + static PyObject* get_children(PyUIGridObject* self, void* closure); + }; -//typedef struct { -// PyObject_HEAD -// std::shared_ptr data; -// //PyObject* texture; -//} PyUIGridObject; +typedef struct { + PyObject_HEAD + std::shared_ptr>> data; + std::shared_ptr grid; +} PyUIEntityCollectionObject; + +class UIEntityCollection { +public: + static PySequenceMethods sqmethods; + static PyObject* append(PyUIEntityCollectionObject* self, PyObject* o); + static PyObject* remove(PyUIEntityCollectionObject* self, PyObject* o); + static PyMethodDef methods[]; + static PyObject* repr(PyUIEntityCollectionObject* self); + static int init(PyUIEntityCollectionObject* self, PyObject* args, PyObject* kwds); + static PyObject* iter(PyUIEntityCollectionObject* self); + static Py_ssize_t len(PyUIEntityCollectionObject* self); + static PyObject* getitem(PyUIEntityCollectionObject* self, Py_ssize_t index); +}; + +typedef struct { + PyObject_HEAD + std::shared_ptr>> data; + int index; + int start_size; +} PyUIEntityCollectionIterObject; + +class UIEntityCollectionIter { +public: + static int init(PyUIEntityCollectionIterObject* self, PyObject* args, PyObject* kwds); + static PyObject* next(PyUIEntityCollectionIterObject* self); + static PyObject* repr(PyUIEntityCollectionIterObject* self); + static PyObject* getitem(PyUIEntityCollectionObject* self, Py_ssize_t index); + +}; namespace mcrfpydef { -//TODO: add this method to class scope; move implementation to .cpp file -static int PyUIGrid_init(PyUIGridObject* self, PyObject* args, PyObject* kwds) { - int grid_x, grid_y; - PyObject* textureObj; - float box_x, box_y, box_w, box_h; - - if (!PyArg_ParseTuple(args, "iiOffff", &grid_x, &grid_y, &textureObj, &box_x, &box_y, &box_w, &box_h)) { - return -1; // If parsing fails, return an error - } - - // Convert PyObject texture to IndexTexture* - // This requires the texture object to have been initialized similar to UISprite's texture handling - if (!PyObject_IsInstance(textureObj, (PyObject*)&PyTextureType)) { - PyErr_SetString(PyExc_TypeError, "texture must be a mcrfpy.Texture instance"); - return -1; - } - PyTextureObject* pyTexture = reinterpret_cast(textureObj); - // TODO (7DRL day 2, item 4.) use shared_ptr / PyTextureObject on UIGrid - //IndexTexture* texture = pyTexture->data.get(); - - // Initialize UIGrid - //self->data = new UIGrid(grid_x, grid_y, texture, sf::Vector2f(box_x, box_y), sf::Vector2f(box_w, box_h)); - self->data = std::make_shared(grid_x, grid_y, pyTexture->data, - sf::Vector2f(box_x, box_y), sf::Vector2f(box_w, box_h)); - return 0; // Success -} - -//TODO: add this method to class scope; move implementation to .cpp file -static PyObject* PyUIGrid_get_grid_size(PyUIGridObject* self, void* closure) { - return Py_BuildValue("(ii)", self->data->grid_x, self->data->grid_y); -} - -//TODO: add this method to class scope; move implementation to .cpp file -static PyObject* PyUIGrid_get_position(PyUIGridObject* self, void* closure) { - auto& box = self->data->box; - return Py_BuildValue("(ff)", box.getPosition().x, box.getPosition().y); -} - -//TODO: add this method to class scope; move implementation to .cpp file -static int PyUIGrid_set_position(PyUIGridObject* self, PyObject* value, void* closure) { - float x, y; - if (!PyArg_ParseTuple(value, "ff", &x, &y)) { - PyErr_SetString(PyExc_ValueError, "Position must be a tuple of two floats"); - return -1; - } - self->data->box.setPosition(x, y); - return 0; -} - -//TODO: add this method to class scope; move implementation to .cpp file -static PyObject* PyUIGrid_get_size(PyUIGridObject* self, void* closure) { - auto& box = self->data->box; - return Py_BuildValue("(ff)", box.getSize().x, box.getSize().y); -} - -//TODO: add this method to class scope; move implementation to .cpp file -static int PyUIGrid_set_size(PyUIGridObject* self, PyObject* value, void* closure) { - float w, h; - if (!PyArg_ParseTuple(value, "ff", &w, &h)) { - PyErr_SetString(PyExc_ValueError, "Size must be a tuple of two floats"); - return -1; - } - self->data->box.setSize(sf::Vector2f(w, h)); - return 0; -} - -//TODO: add this method to class scope; move implementation to .cpp file -static PyObject* PyUIGrid_get_center(PyUIGridObject* self, void* closure) { - return Py_BuildValue("(ff)", self->data->center_x, self->data->center_y); -} - -//TODO: add this method to class scope; move implementation to .cpp file -static int PyUIGrid_set_center(PyUIGridObject* self, PyObject* value, void* closure) { - float x, y; - if (!PyArg_ParseTuple(value, "ff", &x, &y)) { - PyErr_SetString(PyExc_ValueError, "Size must be a tuple of two floats"); - return -1; - } - self->data->center_x = x; - self->data->center_y = y; - return 0; -} - -//TODO: add this method to class scope; move implementation to .cpp file -static PyObject* PyUIGrid_get_float_member(PyUIGridObject* self, void* closure) -{ - auto member_ptr = reinterpret_cast(closure); - if (member_ptr == 0) // x - return PyFloat_FromDouble(self->data->box.getPosition().x); - else if (member_ptr == 1) // y - return PyFloat_FromDouble(self->data->box.getPosition().y); - else if (member_ptr == 2) // w - return PyFloat_FromDouble(self->data->box.getSize().x); - else if (member_ptr == 3) // h - return PyFloat_FromDouble(self->data->box.getSize().y); - else if (member_ptr == 4) // center_x - return PyFloat_FromDouble(self->data->center_x); - else if (member_ptr == 5) // center_y - return PyFloat_FromDouble(self->data->center_y); - else if (member_ptr == 6) // zoom - return PyFloat_FromDouble(self->data->zoom); - else - { - PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); - return nullptr; - } -} - -//TODO: add this method to class scope; move implementation to .cpp file -static int PyUIGrid_set_float_member(PyUIGridObject* self, PyObject* value, void* closure) -{ - float val; - auto member_ptr = reinterpret_cast(closure); - if (PyFloat_Check(value)) - { - val = PyFloat_AsDouble(value); - } - else if (PyLong_Check(value)) - { - val = PyLong_AsLong(value); - } - else - { - PyErr_SetString(PyExc_TypeError, "Value must be a floating point number."); - return -1; - } - if (member_ptr == 0) // x - self->data->box.setPosition(val, self->data->box.getPosition().y); - else if (member_ptr == 1) // y - self->data->box.setPosition(self->data->box.getPosition().x, val); - else if (member_ptr == 2) // w - self->data->box.setSize(sf::Vector2f(val, self->data->box.getSize().y)); - else if (member_ptr == 3) // h - self->data->box.setSize(sf::Vector2f(self->data->box.getSize().x, val)); - else if (member_ptr == 4) // center_x - self->data->center_x = val; - else if (member_ptr == 5) // center_y - self->data->center_y = val; - else if (member_ptr == 6) // zoom - self->data->zoom = val; - return 0; -} -// TODO (7DRL Day 2, item 5.) return Texture object -/* -static PyObject* PyUIGrid_get_texture(PyUIGridObject* self, void* closure) { - Py_INCREF(self->texture); - return self->texture; -} -*/ -//TODO: add this method to class scope; move implementation to .cpp file -static PyObject* PyUIGrid_get_texture(PyUIGridObject* self, void* closure) { - //return self->data->getTexture()->pyObject(); - PyTextureObject* obj = (PyTextureObject*)((&PyTextureType)->tp_alloc(&PyTextureType, 0)); - obj->data = self->data->getTexture(); - return (PyObject*)obj; -} - -//TODO: add this method to class scope; move implementation to .cpp file -static PyObject* PyUIGrid_at(PyUIGridObject* self, PyObject* o) -{ - int x, y; - if (!PyArg_ParseTuple(o, "ii", &x, &y)) { - PyErr_SetString(PyExc_TypeError, "UIGrid.at requires two integer arguments: (x, y)"); - return NULL; - } - if (x < 0 || x >= self->data->grid_x) { - PyErr_SetString(PyExc_ValueError, "x value out of range (0, Grid.grid_y)"); - return NULL; - } - if (y < 0 || y >= self->data->grid_y) { - PyErr_SetString(PyExc_ValueError, "y value out of range (0, Grid.grid_y)"); - return NULL; - } - - PyUIGridPointObject* obj = (PyUIGridPointObject*)((&PyUIGridPointType)->tp_alloc(&PyUIGridPointType, 0)); - //auto target = std::static_pointer_cast(target); - obj->data = &(self->data->points[x + self->data->grid_x * y]); - obj->grid = self->data; - return (PyObject*)obj; -} - -//TODO: add this static array to class scope; move implementation to .cpp file -static PyMethodDef PyUIGrid_methods[] = { - {"at", (PyCFunction)PyUIGrid_at, METH_O}, - {NULL, NULL, 0, NULL} -}; - -static PyObject* PyUIGrid_get_children(PyUIGridObject* self, void* closure); // forward declare - -static PyGetSetDef PyUIGrid_getsetters[] = { - - // TODO - refactor into get_vector_member with field identifier values `(void*)n` - {"grid_size", (getter)PyUIGrid_get_grid_size, NULL, "Grid dimensions (grid_x, grid_y)", NULL}, - {"position", (getter)PyUIGrid_get_position, (setter)PyUIGrid_set_position, "Position of the grid (x, y)", NULL}, - {"size", (getter)PyUIGrid_get_size, (setter)PyUIGrid_set_size, "Size of the grid (width, height)", NULL}, - {"center", (getter)PyUIGrid_get_center, (setter)PyUIGrid_set_center, "Grid coordinate at the center of the Grid's view (pan)", NULL}, - - {"entities", (getter)PyUIGrid_get_children, NULL, "EntityCollection of entities on this grid", NULL}, - - {"x", (getter)PyUIGrid_get_float_member, (setter)PyUIGrid_set_float_member, "top-left corner X-coordinate", (void*)0}, - {"y", (getter)PyUIGrid_get_float_member, (setter)PyUIGrid_set_float_member, "top-left corner Y-coordinate", (void*)1}, - {"w", (getter)PyUIGrid_get_float_member, (setter)PyUIGrid_set_float_member, "visible widget width", (void*)2}, - {"h", (getter)PyUIGrid_get_float_member, (setter)PyUIGrid_set_float_member, "visible widget height", (void*)3}, - {"center_x", (getter)PyUIGrid_get_float_member, (setter)PyUIGrid_set_float_member, "center of the view X-coordinate", (void*)4}, - {"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)UIDrawable::get_click, (setter)UIDrawable::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 */ -}; - - -/* // TODO standard pointer would need deleted, but I opted for a shared pointer. tp_dealloc currently not even defined in the PyTypeObject -static void PyUIGrid_dealloc(PyUIGridObject* self) { - delete self->data; // Clean up the allocated UIGrid object - Py_TYPE(self)->tp_free((PyObject*)self); -} -*/ static PyTypeObject PyUIGridType = { //PyVarObject_HEAD_INIT(NULL, 0) @@ -284,17 +110,17 @@ static void PyUIGrid_dealloc(PyUIGridObject* self) { // Py_TYPE(self)->tp_free(self); //}, //TODO - PyUIGrid REPR def: - // .tp_repr = (reprfunc)PyUIGrid_repr, + // .tp_repr = (reprfunc)UIGrid::repr, //.tp_hash = NULL, //.tp_iter //.tp_iternext .tp_flags = Py_TPFLAGS_DEFAULT, .tp_doc = PyDoc_STR("docstring"), - .tp_methods = PyUIGrid_methods, - //.tp_members = PyUIGrid_members, - .tp_getset = PyUIGrid_getsetters, + .tp_methods = UIGrid::methods, + //.tp_members = UIGrid::members, + .tp_getset = UIGrid::getsetters, //.tp_base = NULL, - .tp_init = (initproc)PyUIGrid_init, + .tp_init = (initproc)UIGrid::init, .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* { PyUIGridObject* self = (PyUIGridObject*)type->tp_alloc(type, 0); @@ -303,63 +129,6 @@ static void PyUIGrid_dealloc(PyUIGridObject* self) { } }; - typedef struct { - PyObject_HEAD - std::shared_ptr>> data; - int index; - int start_size; - } PyUIEntityCollectionIterObject; - - //TODO: add this method to class scope; move implementation to .cpp file; EntityCollection may need a larger refactor soon - static int PyUIEntityCollectionIter_init(PyUIEntityCollectionIterObject* self, PyObject* args, PyObject* kwds) - { - PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required."); - return -1; - } - - //TODO: add this method to class scope; move implementation to .cpp file; EntityCollection may need a larger refactor soon - static PyObject* PyUIEntityCollectionIter_next(PyUIEntityCollectionIterObject* self) - { - if (self->data->size() != self->start_size) - { - PyErr_SetString(PyExc_RuntimeError, "collection changed size during iteration"); - return NULL; - } - - if (self->index > self->start_size - 1) - { - PyErr_SetNone(PyExc_StopIteration); - return NULL; - } - self->index++; - auto vec = self->data.get(); - if (!vec) - { - PyErr_SetString(PyExc_RuntimeError, "the collection store returned a null pointer"); - return NULL; - } - // Advance list iterator since Entities are not stored in a vector (if this code even worked) - // vectors only: //auto target = (*vec)[self->index-1]; - //auto l_front = (*vec).begin(); - //std::advance(l_front, self->index-1); - - // TODO build PyObject* of the correct UIDrawable subclass to return - //return py_instance(target); - return NULL; - } - - //TODO: add this method to class scope; move implementation to .cpp file; EntityCollection may need a larger refactor soon - static PyObject* PyUIEntityCollectionIter_repr(PyUIEntityCollectionIterObject* self) - { - std::ostringstream ss; - if (!self->data) ss << ""; - else { - ss << "data->size() << " child objects, @ index " << self->index << ")>"; - } - std::string repr_str = ss.str(); - return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); - } - static PyTypeObject PyUIEntityCollectionIterType = { //PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "mcrfpy.UICollectionIter", @@ -371,12 +140,12 @@ static void PyUIGrid_dealloc(PyUIGridObject* self) { obj->data.reset(); Py_TYPE(self)->tp_free(self); }, - .tp_repr = (reprfunc)PyUIEntityCollectionIter_repr, + .tp_repr = (reprfunc)UIEntityCollectionIter::repr, .tp_flags = Py_TPFLAGS_DEFAULT, .tp_doc = PyDoc_STR("Iterator for a collection of UI objects"), - .tp_iternext = (iternextfunc)PyUIEntityCollectionIter_next, - //.tp_getset = PyUIEntityCollection_getset, - .tp_init = (initproc)PyUIEntityCollectionIter_init, // just raise an exception + .tp_iternext = (iternextfunc)UIEntityCollectionIter::next, + //.tp_getset = UIEntityCollection::getset, + .tp_init = (initproc)UIEntityCollectionIter::init, // just raise an exception .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* { PyErr_SetString(PyExc_TypeError, "UICollection cannot be instantiated: a C++ data source is required."); @@ -384,148 +153,6 @@ static void PyUIGrid_dealloc(PyUIGridObject* self) { } }; - - typedef struct { - PyObject_HEAD - std::shared_ptr>> data; - std::shared_ptr grid; - } PyUIEntityCollectionObject; - - //TODO: add this method to class scope; move implementation to .cpp file; EntityCollection may need a larger refactor soon - static Py_ssize_t PyUIEntityCollection_len(PyUIEntityCollectionObject* self) { - return self->data->size(); - } - - //TODO: add this method to class scope; move implementation to .cpp file; EntityCollection may need a larger refactor soon - static PyObject* PyUIEntityCollection_getitem(PyUIEntityCollectionObject* self, Py_ssize_t index) { - // build a Python version of item at self->data[index] - // Copy pasted:: - auto vec = self->data.get(); - if (!vec) - { - PyErr_SetString(PyExc_RuntimeError, "the collection store returned a null pointer"); - return NULL; - } - while (index < 0) index += self->data->size(); - if (index > self->data->size() - 1) - { - PyErr_SetString(PyExc_IndexError, "EntityCollection index out of range"); - return NULL; - } - auto l_begin = (*vec).begin(); - std::advance(l_begin, index); - auto target = *l_begin; //auto target = (*vec)[index]; - //RET_PY_INSTANCE(target); - // construct and return an entity object that points directly into the UIGrid's entity vector - PyUIEntityObject* o = (PyUIEntityObject*)((&PyUIEntityType)->tp_alloc(&PyUIEntityType, 0)); - auto p = std::static_pointer_cast(target); - o->data = p; - return (PyObject*)o; - return NULL; - - - } - - //TODO: add this static array to class scope; move implementation to .cpp file; EntityCollection may need a larger refactor soon - static PySequenceMethods PyUIEntityCollection_sqmethods = { - .sq_length = (lenfunc)PyUIEntityCollection_len, - .sq_item = (ssizeargfunc)PyUIEntityCollection_getitem, - //.sq_item_by_index = PyUIEntityCollection_getitem - //.sq_slice - return a subset of the iterable - //.sq_ass_item - called when `o[x] = y` is executed (x is any object type) - //.sq_ass_slice - cool; no thanks, for now - //.sq_contains - called when `x in o` is executed - //.sq_ass_item_by_index - called when `o[x] = y` is executed (x is explictly an integer) - }; - - //TODO: add this method to class scope; move implementation to .cpp file; EntityCollection may need a larger refactor soon - static PyObject* PyUIEntityCollection_append(PyUIEntityCollectionObject* self, PyObject* o) - { - // if not UIDrawable subclass, reject it - // self->data->push_back( c++ object inside o ); - - // this would be a great use case for .tp_base - if (!PyObject_IsInstance(o, (PyObject*)&PyUIEntityType)) - { - PyErr_SetString(PyExc_TypeError, "Only Entity objects can be added to EntityCollection"); - return NULL; - } - PyUIEntityObject* entity = (PyUIEntityObject*)o; - self->data->push_back(entity->data); - entity->data->grid = self->grid; - - Py_INCREF(Py_None); - return Py_None; - } - - //TODO: add this method to class scope; move implementation to .cpp file; EntityCollection may need a larger refactor soon - static PyObject* PyUIEntityCollection_remove(PyUIEntityCollectionObject* self, PyObject* o) - { - if (!PyLong_Check(o)) - { - PyErr_SetString(PyExc_TypeError, "UICollection.remove requires an integer index to remove"); - return NULL; - } - long index = PyLong_AsLong(o); - if (index >= self->data->size()) - { - PyErr_SetString(PyExc_ValueError, "Index out of range"); - return NULL; - } - else if (index < 0) - { - PyErr_SetString(PyExc_NotImplementedError, "reverse indexing is not implemented."); - return NULL; - } - - // release the shared pointer at correct part of the list - self->data->erase(std::next(self->data->begin(), index)); - Py_INCREF(Py_None); - return Py_None; - } - - //TODO: add this static array to class scope; move implementation to .cpp file; EntityCollection may need a larger refactor soon - static PyMethodDef PyUIEntityCollection_methods[] = { - {"append", (PyCFunction)PyUIEntityCollection_append, METH_O}, - //{"extend", (PyCFunction)PyUIEntityCollection_extend, METH_O}, // TODO - {"remove", (PyCFunction)PyUIEntityCollection_remove, METH_O}, - {NULL, NULL, 0, NULL} - }; - - //TODO: add this method to class scope; move implementation to .cpp file; EntityCollection may need a larger refactor soon - static PyObject* PyUIEntityCollection_repr(PyUIEntityCollectionObject* self) - { - std::ostringstream ss; - if (!self->data) ss << ""; - else { - ss << "data->size() << " child objects)>"; - } - std::string repr_str = ss.str(); - return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); - } - - //TODO: add this method to class scope; move implementation to .cpp file; EntityCollection may need a larger refactor soon - static int PyUIEntityCollection_init(PyUIEntityCollectionObject* self, PyObject* args, PyObject* kwds) - { - PyErr_SetString(PyExc_TypeError, "EntityCollection cannot be instantiated: a C++ data source is required."); - return -1; - } - - //TODO: add this method to class scope; move implementation to .cpp file; EntityCollection may need a larger refactor soon - static PyObject* PyUIEntityCollection_iter(PyUIEntityCollectionObject* self) - { - PyUIEntityCollectionIterObject* iterObj; - iterObj = (PyUIEntityCollectionIterObject*)PyUIEntityCollectionIterType.tp_alloc(&PyUIEntityCollectionIterType, 0); - if (iterObj == NULL) { - return NULL; // Failed to allocate memory for the iterator object - } - - iterObj->data = self->data; - iterObj->index = 0; - iterObj->start_size = self->data->size(); - - return (PyObject*)iterObj; - } static PyTypeObject PyUIEntityCollectionType = { //PyVarObject_/HEAD_INIT(NULL, 0) .tp_name = "mcrfpy.EntityCollection", @@ -537,14 +164,14 @@ static void PyUIGrid_dealloc(PyUIGridObject* self) { obj->data.reset(); Py_TYPE(self)->tp_free(self); }, - .tp_repr = (reprfunc)PyUIEntityCollection_repr, - .tp_as_sequence = &PyUIEntityCollection_sqmethods, + .tp_repr = (reprfunc)UIEntityCollection::repr, + .tp_as_sequence = &UIEntityCollection::sqmethods, .tp_flags = Py_TPFLAGS_DEFAULT, .tp_doc = PyDoc_STR("Iterable, indexable collection of Entities"), - .tp_iter = (getiterfunc)PyUIEntityCollection_iter, - .tp_methods = PyUIEntityCollection_methods, // append, remove - //.tp_getset = PyUIEntityCollection_getset, - .tp_init = (initproc)PyUIEntityCollection_init, // just raise an exception + .tp_iter = (getiterfunc)UIEntityCollection::iter, + .tp_methods = UIEntityCollection::methods, // append, remove + //.tp_getset = UIEntityCollection::getset, + .tp_init = (initproc)UIEntityCollection::init, // just raise an exception .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* { // Does PyUIEntityCollectionType need __new__ if it's not supposed to be instantiable by the user? @@ -554,18 +181,4 @@ static void PyUIGrid_dealloc(PyUIGridObject* self) { } }; - //TODO: add this method to class scope; move implementation to .cpp file - // Grid's get_children def must follow the EntityCollection def - static PyObject* PyUIGrid_get_children(PyUIGridObject* self, void* closure) - { - // create PyUICollection instance pointing to self->data->children - PyUIEntityCollectionObject* o = (PyUIEntityCollectionObject*)PyUIEntityCollectionType.tp_alloc(&PyUIEntityCollectionType, 0); - if (o) { - o->data = self->data->entities; // todone. / BUGFIX - entities isn't a shared pointer on UIGrid, what to do? -- I made it a sp>> - o->grid = self->data; - } - return (PyObject*)o; - } - - } From ec0374ef50ecbbcc48b9901d296dfa48d57605bd Mon Sep 17 00:00:00 2001 From: John McCardle Date: Fri, 19 Apr 2024 21:37:39 -0400 Subject: [PATCH 13/14] UIGridPoint.h/.cpp reorganization --- src/UIGridPoint.cpp | 128 +++++++++++++++++++++++++++++ src/UIGridPoint.h | 195 ++++++++------------------------------------ 2 files changed, 164 insertions(+), 159 deletions(-) diff --git a/src/UIGridPoint.cpp b/src/UIGridPoint.cpp index 18fffc7..b30d493 100644 --- a/src/UIGridPoint.cpp +++ b/src/UIGridPoint.cpp @@ -4,3 +4,131 @@ UIGridPoint::UIGridPoint() : color(1.0f, 1.0f, 1.0f), color_overlay(0.0f, 0.0f, 0.0f), walkable(false), transparent(false), tilesprite(-1), tile_overlay(-1), uisprite(-1) {} + +// Utility function to convert sf::Color to PyObject* +PyObject* sfColor_to_PyObject(sf::Color color) { + return Py_BuildValue("(iiii)", color.r, color.g, color.b, color.a); +} + +// Utility function to convert PyObject* to sf::Color +sf::Color PyObject_to_sfColor(PyObject* obj) { + int r, g, b, a = 255; // Default alpha to fully opaque if not specified + if (!PyArg_ParseTuple(obj, "iii|i", &r, &g, &b, &a)) { + return sf::Color(); // Return default color on parse error + } + return sf::Color(r, g, b, a); +} + +PyObject* UIGridPoint::get_color(PyUIGridPointObject* self, void* closure) { + if (reinterpret_cast(closure) == 0) { // color + return sfColor_to_PyObject(self->data->color); + } else { // color_overlay + return sfColor_to_PyObject(self->data->color_overlay); + } +} + +int UIGridPoint::set_color(PyUIGridPointObject* self, PyObject* value, void* closure) { + sf::Color color = PyObject_to_sfColor(value); + if (reinterpret_cast(closure) == 0) { // color + self->data->color = color; + } else { // color_overlay + self->data->color_overlay = color; + } + return 0; +} + +PyObject* UIGridPoint::get_bool_member(PyUIGridPointObject* self, void* closure) { + if (reinterpret_cast(closure) == 0) { // walkable + return PyBool_FromLong(self->data->walkable); + } else { // transparent + return PyBool_FromLong(self->data->transparent); + } +} + +int UIGridPoint::set_bool_member(PyUIGridPointObject* self, PyObject* value, void* closure) { + if (value == Py_True) { + if (reinterpret_cast(closure) == 0) { // walkable + self->data->walkable = true; + } else { // transparent + self->data->transparent = true; + } + } else if (value == Py_False) { + if (reinterpret_cast(closure) == 0) { // walkable + self->data->walkable = false; + } else { // transparent + self->data->transparent = false; + } + } else { + PyErr_SetString(PyExc_ValueError, "Expected a boolean value"); + return -1; + } + return 0; +} + +PyObject* UIGridPoint::get_int_member(PyUIGridPointObject* self, void* closure) { + switch(reinterpret_cast(closure)) { + case 0: return PyLong_FromLong(self->data->tilesprite); + case 1: return PyLong_FromLong(self->data->tile_overlay); + case 2: return PyLong_FromLong(self->data->uisprite); + default: PyErr_SetString(PyExc_RuntimeError, "Invalid closure"); return nullptr; + } +} + +int UIGridPoint::set_int_member(PyUIGridPointObject* self, PyObject* value, void* closure) { + long val = PyLong_AsLong(value); + if (PyErr_Occurred()) return -1; + + switch(reinterpret_cast(closure)) { + case 0: self->data->tilesprite = val; break; + case 1: self->data->tile_overlay = val; break; + case 2: self->data->uisprite = val; break; + default: PyErr_SetString(PyExc_RuntimeError, "Invalid closure"); return -1; + } + return 0; +} + +PyGetSetDef UIGridPoint::getsetters[] = { + {"color", (getter)UIGridPoint::get_color, (setter)UIGridPoint::set_color, "GridPoint color", (void*)0}, + {"color_overlay", (getter)UIGridPoint::get_color, (setter)UIGridPoint::set_color, "GridPoint color overlay", (void*)1}, + {"walkable", (getter)UIGridPoint::get_bool_member, (setter)UIGridPoint::set_bool_member, "Is the GridPoint walkable", (void*)0}, + {"transparent", (getter)UIGridPoint::get_bool_member, (setter)UIGridPoint::set_bool_member, "Is the GridPoint transparent", (void*)1}, + {"tilesprite", (getter)UIGridPoint::get_int_member, (setter)UIGridPoint::set_int_member, "Tile sprite index", (void*)0}, + {"tile_overlay", (getter)UIGridPoint::get_int_member, (setter)UIGridPoint::set_int_member, "Tile overlay sprite index", (void*)1}, + {"uisprite", (getter)UIGridPoint::get_int_member, (setter)UIGridPoint::set_int_member, "UI sprite index", (void*)2}, + {NULL} /* Sentinel */ +}; + +PyObject* UIGridPointState::get_bool_member(PyUIGridPointStateObject* self, void* closure) { + if (reinterpret_cast(closure) == 0) { // visible + return PyBool_FromLong(self->data->visible); + } else { // discovered + return PyBool_FromLong(self->data->discovered); + } +} + +int UIGridPointState::set_bool_member(PyUIGridPointStateObject* self, PyObject* value, void* closure) { + if (!PyBool_Check(value)) { + PyErr_SetString(PyExc_TypeError, "Value must be a boolean"); + return -1; + } + + int truthValue = PyObject_IsTrue(value); + if (truthValue < 0) { + return -1; // PyObject_IsTrue returns -1 on error + } + + if (reinterpret_cast(closure) == 0) { // visible + self->data->visible = truthValue; + } else { // discovered + self->data->discovered = truthValue; + } + + return 0; +} + +PyGetSetDef UIGridPointState::getsetters[] = { + {"visible", (getter)UIGridPointState::get_bool_member, (setter)UIGridPointState::set_bool_member, "Is the GridPointState visible", (void*)0}, + {"discovered", (getter)UIGridPointState::get_bool_member, (setter)UIGridPointState::set_bool_member, "Has the GridPointState been discovered", (void*)1}, + {NULL} /* Sentinel */ +}; + diff --git a/src/UIGridPoint.h b/src/UIGridPoint.h index 4827bd9..36d3561 100644 --- a/src/UIGridPoint.h +++ b/src/UIGridPoint.h @@ -12,25 +12,13 @@ #include "PyVector.h" #include "PyFont.h" -// UIGridPoint - revised grid data for each point -class UIGridPoint -{ -public: - sf::Color color, color_overlay; - bool walkable, transparent; - int tilesprite, tile_overlay, uisprite; - UIGridPoint(); -}; - -// UIGridPointState - entity-specific info for each cell -class UIGridPointState -{ -public: - bool visible, discovered; -}; +static PyObject* sfColor_to_PyObject(sf::Color color); +static sf::Color PyObject_to_sfColor(PyObject* obj); class UIGrid; class UIEntity; +class UIGridPoint; +class UIGridPointState; typedef struct { PyObject_HEAD @@ -45,111 +33,37 @@ typedef struct { std::shared_ptr entity; } PyUIGridPointStateObject; -namespace mcrfpydef { -// TODO: question: are sfColor_to_PyObject and PyObject_to_sfColor duplicitive? How does UIFrame get/set colors? +// UIGridPoint - revised grid data for each point +class UIGridPoint +{ +public: + sf::Color color, color_overlay; + bool walkable, transparent; + int tilesprite, tile_overlay, uisprite; + UIGridPoint(); -//TODO: add this method to class scope; move implementation to .cpp file; reconsider for moving to "UIBase.h/.cpp" -// Utility function to convert sf::Color to PyObject* -static PyObject* sfColor_to_PyObject(sf::Color color) { - return Py_BuildValue("(iiii)", color.r, color.g, color.b, color.a); -} - -//TODO: add this method to class scope; move implementation to .cpp file; reconsider for moving to "UIBase.h/.cpp" -// Utility function to convert PyObject* to sf::Color -static sf::Color PyObject_to_sfColor(PyObject* obj) { - int r, g, b, a = 255; // Default alpha to fully opaque if not specified - if (!PyArg_ParseTuple(obj, "iii|i", &r, &g, &b, &a)) { - return sf::Color(); // Return default color on parse error - } - return sf::Color(r, g, b, a); -} - -//TODO: add this method to class scope; move implementation to .cpp file -static PyObject* PyUIGridPoint_get_color(PyUIGridPointObject* self, void* closure) { - if (reinterpret_cast(closure) == 0) { // color - return sfColor_to_PyObject(self->data->color); - } else { // color_overlay - return sfColor_to_PyObject(self->data->color_overlay); - } -} - -//TODO: add this method to class scope; move implementation to .cpp file -static int PyUIGridPoint_set_color(PyUIGridPointObject* self, PyObject* value, void* closure) { - sf::Color color = PyObject_to_sfColor(value); - if (reinterpret_cast(closure) == 0) { // color - self->data->color = color; - } else { // color_overlay - self->data->color_overlay = color; - } - return 0; -} - -//TODO: add this method to class scope; move implementation to .cpp file -static PyObject* PyUIGridPoint_get_bool_member(PyUIGridPointObject* self, void* closure) { - if (reinterpret_cast(closure) == 0) { // walkable - return PyBool_FromLong(self->data->walkable); - } else { // transparent - return PyBool_FromLong(self->data->transparent); - } -} - -//TODO: add this method to class scope; move implementation to .cpp file -static int PyUIGridPoint_set_bool_member(PyUIGridPointObject* self, PyObject* value, void* closure) { - if (value == Py_True) { - if (reinterpret_cast(closure) == 0) { // walkable - self->data->walkable = true; - } else { // transparent - self->data->transparent = true; - } - } else if (value == Py_False) { - if (reinterpret_cast(closure) == 0) { // walkable - self->data->walkable = false; - } else { // transparent - self->data->transparent = false; - } - } else { - PyErr_SetString(PyExc_ValueError, "Expected a boolean value"); - return -1; - } - return 0; -} - -//TODO: add this method to class scope; move implementation to .cpp file -static PyObject* PyUIGridPoint_get_int_member(PyUIGridPointObject* self, void* closure) { - switch(reinterpret_cast(closure)) { - case 0: return PyLong_FromLong(self->data->tilesprite); - case 1: return PyLong_FromLong(self->data->tile_overlay); - case 2: return PyLong_FromLong(self->data->uisprite); - default: PyErr_SetString(PyExc_RuntimeError, "Invalid closure"); return nullptr; - } -} - -//TODO: add this method to class scope; move implementation to .cpp file -static int PyUIGridPoint_set_int_member(PyUIGridPointObject* self, PyObject* value, void* closure) { - long val = PyLong_AsLong(value); - if (PyErr_Occurred()) return -1; - - switch(reinterpret_cast(closure)) { - case 0: self->data->tilesprite = val; break; - case 1: self->data->tile_overlay = val; break; - case 2: self->data->uisprite = val; break; - default: PyErr_SetString(PyExc_RuntimeError, "Invalid closure"); return -1; - } - return 0; -} - -//TODO: add this static array to class scope; move implementation to .cpp file -static PyGetSetDef PyUIGridPoint_getsetters[] = { - {"color", (getter)PyUIGridPoint_get_color, (setter)PyUIGridPoint_set_color, "GridPoint color", (void*)0}, - {"color_overlay", (getter)PyUIGridPoint_get_color, (setter)PyUIGridPoint_set_color, "GridPoint color overlay", (void*)1}, - {"walkable", (getter)PyUIGridPoint_get_bool_member, (setter)PyUIGridPoint_set_bool_member, "Is the GridPoint walkable", (void*)0}, - {"transparent", (getter)PyUIGridPoint_get_bool_member, (setter)PyUIGridPoint_set_bool_member, "Is the GridPoint transparent", (void*)1}, - {"tilesprite", (getter)PyUIGridPoint_get_int_member, (setter)PyUIGridPoint_set_int_member, "Tile sprite index", (void*)0}, - {"tile_overlay", (getter)PyUIGridPoint_get_int_member, (setter)PyUIGridPoint_set_int_member, "Tile overlay sprite index", (void*)1}, - {"uisprite", (getter)PyUIGridPoint_get_int_member, (setter)PyUIGridPoint_set_int_member, "UI sprite index", (void*)2}, - {NULL} /* Sentinel */ + static int set_int_member(PyUIGridPointObject* self, PyObject* value, void* closure); + static PyGetSetDef getsetters[]; + static PyObject* get_color(PyUIGridPointObject* self, void* closure); + static PyObject* get_int_member(PyUIGridPointObject* self, void* closure); + static int set_bool_member(PyUIGridPointObject* self, PyObject* value, void* closure); + static PyObject* get_bool_member(PyUIGridPointObject* self, void* closure); + static int set_color(PyUIGridPointObject* self, PyObject* value, void* closure); }; +// UIGridPointState - entity-specific info for each cell +class UIGridPointState +{ +public: + bool visible, discovered; + + static PyObject* get_bool_member(PyUIGridPointStateObject* self, void* closure); + static int set_bool_member(PyUIGridPointStateObject* self, PyObject* value, void* closure); + static PyGetSetDef getsetters[]; +}; + +namespace mcrfpydef { + static PyTypeObject PyUIGridPointType = { //PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "mcrfpy.GridPoint", @@ -157,49 +71,12 @@ static PyTypeObject PyUIGridPointType = { .tp_itemsize = 0, // Methods omitted for brevity .tp_flags = Py_TPFLAGS_DEFAULT, - .tp_doc = "UIGridPoint objects", - .tp_getset = PyUIGridPoint_getsetters, + .tp_doc = "UIGridPoint object", + .tp_getset = UIGridPoint::getsetters, //.tp_init = (initproc)PyUIGridPoint_init, // TODO Define the init function .tp_new = PyType_GenericNew, }; -//TODO: add this method to class scope; move implementation to .cpp file -static PyObject* PyUIGridPointState_get_bool_member(PyUIGridPointStateObject* self, void* closure) { - if (reinterpret_cast(closure) == 0) { // visible - return PyBool_FromLong(self->data->visible); - } else { // discovered - return PyBool_FromLong(self->data->discovered); - } -} - -//TODO: add this method to class scope; move implementation to .cpp file -static int PyUIGridPointState_set_bool_member(PyUIGridPointStateObject* self, PyObject* value, void* closure) { - if (!PyBool_Check(value)) { - PyErr_SetString(PyExc_TypeError, "Value must be a boolean"); - return -1; - } - - int truthValue = PyObject_IsTrue(value); - if (truthValue < 0) { - return -1; // PyObject_IsTrue returns -1 on error - } - - if (reinterpret_cast(closure) == 0) { // visible - self->data->visible = truthValue; - } else { // discovered - self->data->discovered = truthValue; - } - - return 0; -} - -//TODO: add this static array to class scope; move implementation to .cpp file -static PyGetSetDef PyUIGridPointState_getsetters[] = { - {"visible", (getter)PyUIGridPointState_get_bool_member, (setter)PyUIGridPointState_set_bool_member, "Is the GridPointState visible", (void*)0}, - {"discovered", (getter)PyUIGridPointState_get_bool_member, (setter)PyUIGridPointState_set_bool_member, "Has the GridPointState been discovered", (void*)1}, - {NULL} /* Sentinel */ -}; - static PyTypeObject PyUIGridPointStateType = { //PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "mcrfpy.GridPointState", @@ -207,8 +84,8 @@ static PyTypeObject PyUIGridPointStateType = { .tp_itemsize = 0, // Methods omitted for brevity .tp_flags = Py_TPFLAGS_DEFAULT, - .tp_doc = "UIGridPointState objects", // TODO: Add PyUIGridPointState tp_init - .tp_getset = PyUIGridPointState_getsetters, + .tp_doc = "UIGridPointState object", // TODO: Add PyUIGridPointState tp_init + .tp_getset = UIGridPointState::getsetters, .tp_new = PyType_GenericNew, }; From 6aa151aba33d676c7c1290467bb3c6360820c816 Mon Sep 17 00:00:00 2001 From: John McCardle Date: Fri, 19 Apr 2024 21:43:58 -0400 Subject: [PATCH 14/14] UISprite.h/.cpp cleanup --- src/UISprite.cpp | 133 +++++++++++++++++++++++++++++++++++++++ src/UISprite.h | 157 +++++------------------------------------------ 2 files changed, 148 insertions(+), 142 deletions(-) diff --git a/src/UISprite.cpp b/src/UISprite.cpp index 47acc11..1f3a214 100644 --- a/src/UISprite.cpp +++ b/src/UISprite.cpp @@ -80,3 +80,136 @@ PyObjectsEnum UISprite::derived_type() { return PyObjectsEnum::UISPRITE; } + +PyObject* UISprite::get_float_member(PyUISpriteObject* self, void* closure) +{ + auto member_ptr = reinterpret_cast(closure); + if (member_ptr == 0) + return PyFloat_FromDouble(self->data->getPosition().x); + else if (member_ptr == 1) + return PyFloat_FromDouble(self->data->getPosition().y); + else if (member_ptr == 2) + return PyFloat_FromDouble(self->data->getScale().x); // scale X and Y are identical, presently + else + { + PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); + return nullptr; + } +} + +int UISprite::set_float_member(PyUISpriteObject* self, PyObject* value, void* closure) +{ + float val; + auto member_ptr = reinterpret_cast(closure); + if (PyFloat_Check(value)) + { + val = PyFloat_AsDouble(value); + } + else if (PyLong_Check(value)) + { + val = PyLong_AsLong(value); + } + else + { + PyErr_SetString(PyExc_TypeError, "Value must be a floating point number."); + return -1; + } + if (member_ptr == 0) //x + self->data->setPosition(sf::Vector2f(val, self->data->getPosition().y)); + else if (member_ptr == 1) //y + self->data->setPosition(sf::Vector2f(self->data->getPosition().x, val)); + else if (member_ptr == 2) // scale + self->data->setScale(sf::Vector2f(val, val)); + return 0; +} + +PyObject* UISprite::get_int_member(PyUISpriteObject* self, void* closure) +{ + auto member_ptr = reinterpret_cast(closure); + if (true) {} + else + { + PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); + return nullptr; + } + + return PyLong_FromDouble(self->data->getSpriteIndex()); +} + +int UISprite::set_int_member(PyUISpriteObject* self, PyObject* value, void* closure) +{ + int val; + auto member_ptr = reinterpret_cast(closure); + if (PyLong_Check(value)) + { + val = PyLong_AsLong(value); + } + else + { + PyErr_SetString(PyExc_TypeError, "Value must be an integer."); + return -1; + } + self->data->setSpriteIndex(val); + return 0; +} + +PyObject* UISprite::get_texture(PyUISpriteObject* self, void* closure) +{ + return self->data->getTexture()->pyObject(); +} + +int UISprite::set_texture(PyUISpriteObject* self, PyObject* value, void* closure) +{ + return -1; +} + +PyGetSetDef UISprite::getsetters[] = { + {"x", (getter)UISprite::get_float_member, (setter)UISprite::set_float_member, "X coordinate of top-left corner", (void*)0}, + {"y", (getter)UISprite::get_float_member, (setter)UISprite::set_float_member, "Y coordinate of top-left corner", (void*)1}, + {"scale", (getter)UISprite::get_float_member, (setter)UISprite::set_float_member, "Size factor", (void*)2}, + {"sprite_number", (getter)UISprite::get_int_member, (setter)UISprite::set_int_member, "Which sprite on the texture is shown", NULL}, + {"texture", (getter)UISprite::get_texture, (setter)UISprite::set_texture, "Texture object", NULL}, + {"click", (getter)UIDrawable::get_click, (setter)UIDrawable::set_click, "Object called with (x, y, button) when clicked", (void*)PyObjectsEnum::UISPRITE}, + {NULL} +}; + +PyObject* UISprite::repr(PyUISpriteObject* self) +{ + std::ostringstream ss; + if (!self->data) ss << ""; + else { + //auto sprite = self->data->sprite; + ss << ""; + } + std::string repr_str = ss.str(); + return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); +} + +int UISprite::init(PyUISpriteObject* self, PyObject* args, PyObject* kwds) +{ + //std::cout << "Init called\n"; + static const char* keywords[] = { "x", "y", "texture", "sprite_index", "scale", nullptr }; + float x = 0.0f, y = 0.0f, scale = 1.0f; + int sprite_index; + PyObject* texture; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ffOif", + const_cast(keywords), &x, &y, &texture, &sprite_index, &scale)) + { + return -1; + } + + // check types for texture + //if (texture != NULL && !PyObject_IsInstance(texture, (PyObject*)&PyTextureType)){ + if (texture != NULL && !PyObject_IsInstance(texture, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Texture"))){ + PyErr_SetString(PyExc_TypeError, "texture must be a mcrfpy.Texture instance"); + return -1; + } + auto pytexture = (PyTextureObject*)texture; + self->data = std::make_shared(pytexture->data, sprite_index, sf::Vector2f(x, y), scale); + self->data->setPosition(sf::Vector2f(x, y)); + + return 0; +} diff --git a/src/UISprite.h b/src/UISprite.h index ce22be8..c589408 100644 --- a/src/UISprite.h +++ b/src/UISprite.h @@ -41,6 +41,18 @@ public: std::shared_ptr getTexture(); PyObjectsEnum derived_type() override final; + + + static PyObject* get_float_member(PyUISpriteObject* self, void* closure); + static int set_float_member(PyUISpriteObject* self, PyObject* value, void* closure); + static PyObject* get_int_member(PyUISpriteObject* self, void* closure); + static int set_int_member(PyUISpriteObject* self, PyObject* value, void* closure); + static PyObject* get_texture(PyUISpriteObject* self, void* closure); + static int set_texture(PyUISpriteObject* self, PyObject* value, void* closure); + static PyGetSetDef getsetters[]; + static PyObject* repr(PyUISpriteObject* self); + static int init(PyUISpriteObject* self, PyObject* args, PyObject* kwds); + }; //typedef struct { @@ -49,146 +61,7 @@ public: //} PyUISpriteObject; namespace mcrfpydef { - //TODO: add this method to class scope; move implementation to .cpp file - static PyObject* PyUISprite_get_float_member(PyUISpriteObject* self, void* closure) - { - auto member_ptr = reinterpret_cast(closure); - if (member_ptr == 0) - return PyFloat_FromDouble(self->data->getPosition().x); - else if (member_ptr == 1) - return PyFloat_FromDouble(self->data->getPosition().y); - else if (member_ptr == 2) - return PyFloat_FromDouble(self->data->getScale().x); // scale X and Y are identical, presently - else - { - PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); - return nullptr; - } - } - //TODO: add this method to class scope; move implementation to .cpp file - static int PyUISprite_set_float_member(PyUISpriteObject* self, PyObject* value, void* closure) - { - float val; - auto member_ptr = reinterpret_cast(closure); - if (PyFloat_Check(value)) - { - val = PyFloat_AsDouble(value); - } - else if (PyLong_Check(value)) - { - val = PyLong_AsLong(value); - } - else - { - PyErr_SetString(PyExc_TypeError, "Value must be a floating point number."); - return -1; - } - if (member_ptr == 0) //x - self->data->setPosition(sf::Vector2f(val, self->data->getPosition().y)); - else if (member_ptr == 1) //y - self->data->setPosition(sf::Vector2f(self->data->getPosition().x, val)); - else if (member_ptr == 2) // scale - self->data->setScale(sf::Vector2f(val, val)); - return 0; - } - - //TODO: add this method to class scope; move implementation to .cpp file - static PyObject* PyUISprite_get_int_member(PyUISpriteObject* self, void* closure) - { - auto member_ptr = reinterpret_cast(closure); - if (true) {} - else - { - PyErr_SetString(PyExc_AttributeError, "Invalid attribute"); - return nullptr; - } - - return PyLong_FromDouble(self->data->getSpriteIndex()); - } - - //TODO: add this method to class scope; move implementation to .cpp file - static int PyUISprite_set_int_member(PyUISpriteObject* self, PyObject* value, void* closure) - { - int val; - auto member_ptr = reinterpret_cast(closure); - if (PyLong_Check(value)) - { - val = PyLong_AsLong(value); - } - else - { - PyErr_SetString(PyExc_TypeError, "Value must be an integer."); - return -1; - } - self->data->setSpriteIndex(val); - return 0; - } - - //TODO: add this method to class scope; move implementation to .cpp file - static PyObject* PyUISprite_get_texture(PyUISpriteObject* self, void* closure) - { - return self->data->getTexture()->pyObject(); - } - - //TODO: add this method to class scope; move implementation to .cpp file - static int PyUISprite_set_texture(PyUISpriteObject* self, PyObject* value, void* closure) - { - return -1; - } - - //TODO: add this method to static array scope; move implementation to .cpp file - static PyGetSetDef PyUISprite_getsetters[] = { - {"x", (getter)PyUISprite_get_float_member, (setter)PyUISprite_set_float_member, "X coordinate of top-left corner", (void*)0}, - {"y", (getter)PyUISprite_get_float_member, (setter)PyUISprite_set_float_member, "Y coordinate of top-left corner", (void*)1}, - {"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)UIDrawable::get_click, (setter)UIDrawable::set_click, "Object called with (x, y, button) when clicked", (void*)PyObjectsEnum::UISPRITE}, - {NULL} - }; - - //TODO: add this method to class scope; move implementation to .cpp file - static PyObject* PyUISprite_repr(PyUISpriteObject* self) - { - std::ostringstream ss; - if (!self->data) ss << ""; - else { - //auto sprite = self->data->sprite; - ss << ""; - } - std::string repr_str = ss.str(); - return PyUnicode_DecodeUTF8(repr_str.c_str(), repr_str.size(), "replace"); - } - - //TODO: add this method to class scope; move implementation to .cpp file - static int PyUISprite_init(PyUISpriteObject* self, PyObject* args, PyObject* kwds) - { - //std::cout << "Init called\n"; - static const char* keywords[] = { "x", "y", "texture", "sprite_index", "scale", nullptr }; - float x = 0.0f, y = 0.0f, scale = 1.0f; - int sprite_index; - PyObject* texture; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ffOif", - const_cast(keywords), &x, &y, &texture, &sprite_index, &scale)) - { - return -1; - } - - // check types for texture - if (texture != NULL && !PyObject_IsInstance(texture, (PyObject*)&PyTextureType)){ - PyErr_SetString(PyExc_TypeError, "texture must be a mcrfpy.Texture instance"); - return -1; - } - auto pytexture = (PyTextureObject*)texture; - self->data = std::make_shared(pytexture->data, sprite_index, sf::Vector2f(x, y), scale); - self->data->setPosition(sf::Vector2f(x, y)); - - return 0; - } static PyTypeObject PyUISpriteType = { //PyVarObject_HEAD_INIT(NULL, 0) @@ -203,7 +76,7 @@ namespace mcrfpydef { obj->data.reset(); Py_TYPE(self)->tp_free(self); }, - .tp_repr = (reprfunc)PyUISprite_repr, + .tp_repr = (reprfunc)UISprite::repr, //.tp_hash = NULL, //.tp_iter //.tp_iternext @@ -211,9 +84,9 @@ namespace mcrfpydef { .tp_doc = PyDoc_STR("docstring"), //.tp_methods = PyUIFrame_methods, //.tp_members = PyUIFrame_members, - .tp_getset = PyUISprite_getsetters, + .tp_getset = UISprite::getsetters, //.tp_base = NULL, - .tp_init = (initproc)PyUISprite_init, + .tp_init = (initproc)UISprite::init, .tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject* { PyUISpriteObject* self = (PyUISpriteObject*)type->tp_alloc(type, 0);