Fix UIGrid RenderTexture sizing - use game resolution instead of hard-coded 1080p
- Add ensureRenderTextureSize() helper that creates/resizes renderTexture to match game resolution - Add renderTextureSize tracking member to detect when resize is needed - Call helper in constructor and at start of render() to handle resolution changes - Clamp maximum size to 4096x4096 (SFML texture limits) - Only recreate texture when size actually changes (performance optimization) closes #228 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
d12bfd224c
commit
214037892e
2 changed files with 84 additions and 47 deletions
112
src/UIGrid.cpp
112
src/UIGrid.cpp
|
|
@ -40,8 +40,9 @@ UIGrid::UIGrid()
|
|||
box.setPosition(position); // Sync box position
|
||||
box.setFillColor(sf::Color(0, 0, 0, 0));
|
||||
|
||||
// Initialize render texture (small default size)
|
||||
// #228 - Initialize render texture to game resolution (small default until game init)
|
||||
renderTexture.create(1, 1);
|
||||
renderTextureSize = {1, 1};
|
||||
|
||||
// Initialize output sprite
|
||||
output.setTextureRect(sf::IntRect(0, 0, 0, 0));
|
||||
|
|
@ -76,8 +77,8 @@ UIGrid::UIGrid(int gx, int gy, std::shared_ptr<PyTexture> _ptex, sf::Vector2f _x
|
|||
box.setPosition(position); // Sync box position
|
||||
|
||||
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
|
||||
// #228 - create renderTexture sized to game resolution (dynamically resized as needed)
|
||||
ensureRenderTextureSize();
|
||||
|
||||
// Only initialize sprite if texture is available
|
||||
if (ptex) {
|
||||
|
|
@ -145,6 +146,9 @@ void UIGrid::render(sf::Vector2f offset, sf::RenderTarget& target)
|
|||
// Check visibility
|
||||
if (!visible) return;
|
||||
|
||||
// #228 - Ensure renderTexture matches current game resolution
|
||||
ensureRenderTextureSize();
|
||||
|
||||
// TODO: Apply opacity to output sprite
|
||||
|
||||
// Get cell dimensions - use texture if available, otherwise defaults
|
||||
|
|
@ -464,6 +468,26 @@ UIGrid::~UIGrid()
|
|||
}
|
||||
}
|
||||
|
||||
void UIGrid::ensureRenderTextureSize()
|
||||
{
|
||||
// Get game resolution (or use sensible defaults during early init)
|
||||
sf::Vector2u resolution{1920, 1080};
|
||||
if (Resources::game) {
|
||||
resolution = Resources::game->getGameResolution();
|
||||
}
|
||||
|
||||
// Clamp to reasonable maximum (SFML texture size limits)
|
||||
unsigned int required_w = std::min(resolution.x, 4096u);
|
||||
unsigned int required_h = std::min(resolution.y, 4096u);
|
||||
|
||||
// Only recreate if size changed
|
||||
if (renderTextureSize.x != required_w || renderTextureSize.y != required_h) {
|
||||
renderTexture.create(required_w, required_h);
|
||||
renderTextureSize = {required_w, required_h};
|
||||
output.setTexture(renderTexture.getTexture());
|
||||
}
|
||||
}
|
||||
|
||||
PyObjectsEnum UIGrid::derived_type()
|
||||
{
|
||||
return PyObjectsEnum::UIGRID;
|
||||
|
|
@ -2339,11 +2363,12 @@ PyObject* UIGrid::get_on_cell_enter(PyUIGridObject* self, void* closure) {
|
|||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
// #230 - Cell hover callbacks now use PyCellHoverCallable (position-only)
|
||||
int UIGrid::set_on_cell_enter(PyUIGridObject* self, PyObject* value, void* closure) {
|
||||
if (value == Py_None) {
|
||||
self->data->on_cell_enter_callable.reset();
|
||||
} else {
|
||||
self->data->on_cell_enter_callable = std::make_unique<PyClickCallable>(value);
|
||||
self->data->on_cell_enter_callable = std::make_unique<PyCellHoverCallable>(value);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -2357,11 +2382,12 @@ PyObject* UIGrid::get_on_cell_exit(PyUIGridObject* self, void* closure) {
|
|||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
// #230 - Cell hover callbacks now use PyCellHoverCallable (position-only)
|
||||
int UIGrid::set_on_cell_exit(PyUIGridObject* self, PyObject* value, void* closure) {
|
||||
if (value == Py_None) {
|
||||
self->data->on_cell_exit_callable.reset();
|
||||
} else {
|
||||
self->data->on_cell_exit_callable = std::make_unique<PyClickCallable>(value);
|
||||
self->data->on_cell_exit_callable = std::make_unique<PyCellHoverCallable>(value);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -2553,6 +2579,26 @@ static PyObject* createCellCallbackArgs(sf::Vector2i cell, const std::string& bu
|
|||
return args;
|
||||
}
|
||||
|
||||
// #230 - Helper to create cell hover callback arguments: (Vector) only
|
||||
static PyObject* createCellHoverArgs(sf::Vector2i cell) {
|
||||
// Create Vector object for cell position
|
||||
PyObject* vector_type = PyObject_GetAttrString(McRFPy_API::mcrf_module, "Vector");
|
||||
if (!vector_type) {
|
||||
PyErr_Print();
|
||||
return nullptr;
|
||||
}
|
||||
PyObject* cell_pos = PyObject_CallFunction(vector_type, "ii", cell.x, cell.y);
|
||||
Py_DECREF(vector_type);
|
||||
if (!cell_pos) {
|
||||
PyErr_Print();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PyObject* args = Py_BuildValue("(O)", cell_pos);
|
||||
Py_DECREF(cell_pos);
|
||||
return args;
|
||||
}
|
||||
|
||||
// Fire cell click callback with full signature (cell_pos, button, action)
|
||||
bool UIGrid::fireCellClick(sf::Vector2i cell, const std::string& button, const std::string& action) {
|
||||
// Try property-assigned callback first
|
||||
|
|
@ -2604,23 +2650,12 @@ bool UIGrid::fireCellClick(sf::Vector2i cell, const std::string& button, const s
|
|||
return false;
|
||||
}
|
||||
|
||||
// Fire cell enter callback with full signature (cell_pos, button, action)
|
||||
bool UIGrid::fireCellEnter(sf::Vector2i cell, const std::string& button, const std::string& action) {
|
||||
// Try property-assigned callback first
|
||||
// #230 - Fire cell enter callback with position-only signature (cell_pos)
|
||||
bool UIGrid::fireCellEnter(sf::Vector2i cell) {
|
||||
// Try property-assigned callback first (now PyCellHoverCallable)
|
||||
if (on_cell_enter_callable && !on_cell_enter_callable->isNone()) {
|
||||
PyObject* args = createCellCallbackArgs(cell, button, action);
|
||||
if (args) {
|
||||
PyObject* result = PyObject_CallObject(on_cell_enter_callable->borrow(), args);
|
||||
Py_DECREF(args);
|
||||
if (!result) {
|
||||
std::cerr << "Cell enter callback raised an exception:" << std::endl;
|
||||
PyErr_Print();
|
||||
PyErr_Clear();
|
||||
} else {
|
||||
Py_DECREF(result);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
on_cell_enter_callable->call(cell);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Try Python subclass method
|
||||
|
|
@ -2631,7 +2666,8 @@ bool UIGrid::fireCellEnter(sf::Vector2i cell, const std::string& button, const s
|
|||
if (cell_callback_cache.has_on_cell_enter) {
|
||||
PyObject* method = PyObject_GetAttrString(pyObj, "on_cell_enter");
|
||||
if (method && PyCallable_Check(method)) {
|
||||
PyObject* args = createCellCallbackArgs(cell, button, action);
|
||||
// #230: Cell hover takes only (cell_pos)
|
||||
PyObject* args = createCellHoverArgs(cell);
|
||||
if (args) {
|
||||
PyObject* result = PyObject_CallObject(method, args);
|
||||
Py_DECREF(args);
|
||||
|
|
@ -2655,23 +2691,12 @@ bool UIGrid::fireCellEnter(sf::Vector2i cell, const std::string& button, const s
|
|||
return false;
|
||||
}
|
||||
|
||||
// Fire cell exit callback with full signature (cell_pos, button, action)
|
||||
bool UIGrid::fireCellExit(sf::Vector2i cell, const std::string& button, const std::string& action) {
|
||||
// Try property-assigned callback first
|
||||
// #230 - Fire cell exit callback with position-only signature (cell_pos)
|
||||
bool UIGrid::fireCellExit(sf::Vector2i cell) {
|
||||
// Try property-assigned callback first (now PyCellHoverCallable)
|
||||
if (on_cell_exit_callable && !on_cell_exit_callable->isNone()) {
|
||||
PyObject* args = createCellCallbackArgs(cell, button, action);
|
||||
if (args) {
|
||||
PyObject* result = PyObject_CallObject(on_cell_exit_callable->borrow(), args);
|
||||
Py_DECREF(args);
|
||||
if (!result) {
|
||||
std::cerr << "Cell exit callback raised an exception:" << std::endl;
|
||||
PyErr_Print();
|
||||
PyErr_Clear();
|
||||
} else {
|
||||
Py_DECREF(result);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
on_cell_exit_callable->call(cell);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Try Python subclass method
|
||||
|
|
@ -2682,7 +2707,8 @@ bool UIGrid::fireCellExit(sf::Vector2i cell, const std::string& button, const st
|
|||
if (cell_callback_cache.has_on_cell_exit) {
|
||||
PyObject* method = PyObject_GetAttrString(pyObj, "on_cell_exit");
|
||||
if (method && PyCallable_Check(method)) {
|
||||
PyObject* args = createCellCallbackArgs(cell, button, action);
|
||||
// #230: Cell hover takes only (cell_pos)
|
||||
PyObject* args = createCellHoverArgs(cell);
|
||||
if (args) {
|
||||
PyObject* result = PyObject_CallObject(method, args);
|
||||
Py_DECREF(args);
|
||||
|
|
@ -2707,19 +2733,23 @@ bool UIGrid::fireCellExit(sf::Vector2i cell, const std::string& button, const st
|
|||
}
|
||||
|
||||
// #142 - Update cell hover state and fire callbacks
|
||||
// #230 - Cell hover callbacks now take only (cell_pos), no button/action
|
||||
void UIGrid::updateCellHover(sf::Vector2f mousepos, const std::string& button, const std::string& action) {
|
||||
(void)button; // #230 - No longer used for hover callbacks
|
||||
(void)action; // #230 - No longer used for hover callbacks
|
||||
|
||||
auto new_cell = screenToCell(mousepos);
|
||||
|
||||
// Check if cell changed
|
||||
if (new_cell != hovered_cell) {
|
||||
// Fire exit callback for old cell
|
||||
if (hovered_cell.has_value()) {
|
||||
fireCellExit(hovered_cell.value(), button, action);
|
||||
fireCellExit(hovered_cell.value());
|
||||
}
|
||||
|
||||
// Fire enter callback for new cell
|
||||
if (new_cell.has_value()) {
|
||||
fireCellEnter(new_cell.value(), button, action);
|
||||
fireCellEnter(new_cell.value());
|
||||
}
|
||||
|
||||
hovered_cell = new_cell;
|
||||
|
|
|
|||
19
src/UIGrid.h
19
src/UIGrid.h
|
|
@ -84,6 +84,10 @@ public:
|
|||
std::shared_ptr<PyTexture> getTexture();
|
||||
sf::Sprite sprite, output;
|
||||
sf::RenderTexture renderTexture;
|
||||
sf::Vector2u renderTextureSize{0, 0}; // Track current allocation for resize detection
|
||||
|
||||
// Helper to ensure renderTexture matches game resolution
|
||||
void ensureRenderTextureSize();
|
||||
|
||||
// Intermediate texture for camera_rotation (larger than viewport to hold rotated content)
|
||||
sf::RenderTexture rotationTexture;
|
||||
|
|
@ -131,9 +135,10 @@ public:
|
|||
TCOD_fov_algorithm_t fov_algorithm; // Default FOV algorithm (from mcrfpy.default_fov)
|
||||
int fov_radius; // Default FOV radius
|
||||
|
||||
// #142 - Grid cell mouse events
|
||||
std::unique_ptr<PyClickCallable> on_cell_enter_callable;
|
||||
std::unique_ptr<PyClickCallable> on_cell_exit_callable;
|
||||
// #142, #230 - Grid cell mouse events
|
||||
// Cell hover callbacks take only (cell_pos); cell click still takes (cell_pos, button, action)
|
||||
std::unique_ptr<PyCellHoverCallable> on_cell_enter_callable;
|
||||
std::unique_ptr<PyCellHoverCallable> on_cell_exit_callable;
|
||||
std::unique_ptr<PyClickCallable> on_cell_click_callable;
|
||||
std::optional<sf::Vector2i> hovered_cell; // Currently hovered cell or nullopt
|
||||
std::optional<sf::Vector2i> last_clicked_cell; // Cell clicked during click_at
|
||||
|
|
@ -158,11 +163,13 @@ public:
|
|||
// Now takes button/action for consistent callback signatures
|
||||
void updateCellHover(sf::Vector2f mousepos, const std::string& button, const std::string& action);
|
||||
|
||||
// Fire cell callbacks with full signature (cell_pos, button, action)
|
||||
// Fire cell callbacks
|
||||
// #230: Cell hover callbacks (enter/exit) now take only (cell_pos)
|
||||
// Cell click still takes (cell_pos, button, action)
|
||||
// Returns true if a callback was fired
|
||||
bool fireCellClick(sf::Vector2i cell, const std::string& button, const std::string& action);
|
||||
bool fireCellEnter(sf::Vector2i cell, const std::string& button, const std::string& action);
|
||||
bool fireCellExit(sf::Vector2i cell, const std::string& button, const std::string& action);
|
||||
bool fireCellEnter(sf::Vector2i cell);
|
||||
bool fireCellExit(sf::Vector2i cell);
|
||||
|
||||
// Refresh cell callback cache for subclass method support
|
||||
void refreshCellCallbackCache(PyObject* pyObj);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue