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.setPosition(position); // Sync box position
|
||||||
box.setFillColor(sf::Color(0, 0, 0, 0));
|
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);
|
renderTexture.create(1, 1);
|
||||||
|
renderTextureSize = {1, 1};
|
||||||
|
|
||||||
// Initialize output sprite
|
// Initialize output sprite
|
||||||
output.setTextureRect(sf::IntRect(0, 0, 0, 0));
|
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.setPosition(position); // Sync box position
|
||||||
|
|
||||||
box.setFillColor(sf::Color(0,0,0,0));
|
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
|
// #228 - create renderTexture sized to game resolution (dynamically resized as needed)
|
||||||
renderTexture.create(1920, 1080); // TODO - renderTexture should be window size; above 1080p this will cause rendering errors
|
ensureRenderTextureSize();
|
||||||
|
|
||||||
// Only initialize sprite if texture is available
|
// Only initialize sprite if texture is available
|
||||||
if (ptex) {
|
if (ptex) {
|
||||||
|
|
@ -145,6 +146,9 @@ void UIGrid::render(sf::Vector2f offset, sf::RenderTarget& target)
|
||||||
// Check visibility
|
// Check visibility
|
||||||
if (!visible) return;
|
if (!visible) return;
|
||||||
|
|
||||||
|
// #228 - Ensure renderTexture matches current game resolution
|
||||||
|
ensureRenderTextureSize();
|
||||||
|
|
||||||
// TODO: Apply opacity to output sprite
|
// TODO: Apply opacity to output sprite
|
||||||
|
|
||||||
// Get cell dimensions - use texture if available, otherwise defaults
|
// 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()
|
PyObjectsEnum UIGrid::derived_type()
|
||||||
{
|
{
|
||||||
return PyObjectsEnum::UIGRID;
|
return PyObjectsEnum::UIGRID;
|
||||||
|
|
@ -2339,11 +2363,12 @@ PyObject* UIGrid::get_on_cell_enter(PyUIGridObject* self, void* closure) {
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// #230 - Cell hover callbacks now use PyCellHoverCallable (position-only)
|
||||||
int UIGrid::set_on_cell_enter(PyUIGridObject* self, PyObject* value, void* closure) {
|
int UIGrid::set_on_cell_enter(PyUIGridObject* self, PyObject* value, void* closure) {
|
||||||
if (value == Py_None) {
|
if (value == Py_None) {
|
||||||
self->data->on_cell_enter_callable.reset();
|
self->data->on_cell_enter_callable.reset();
|
||||||
} else {
|
} 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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -2357,11 +2382,12 @@ PyObject* UIGrid::get_on_cell_exit(PyUIGridObject* self, void* closure) {
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// #230 - Cell hover callbacks now use PyCellHoverCallable (position-only)
|
||||||
int UIGrid::set_on_cell_exit(PyUIGridObject* self, PyObject* value, void* closure) {
|
int UIGrid::set_on_cell_exit(PyUIGridObject* self, PyObject* value, void* closure) {
|
||||||
if (value == Py_None) {
|
if (value == Py_None) {
|
||||||
self->data->on_cell_exit_callable.reset();
|
self->data->on_cell_exit_callable.reset();
|
||||||
} else {
|
} 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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -2553,6 +2579,26 @@ static PyObject* createCellCallbackArgs(sf::Vector2i cell, const std::string& bu
|
||||||
return args;
|
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)
|
// 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) {
|
bool UIGrid::fireCellClick(sf::Vector2i cell, const std::string& button, const std::string& action) {
|
||||||
// Try property-assigned callback first
|
// Try property-assigned callback first
|
||||||
|
|
@ -2604,23 +2650,12 @@ bool UIGrid::fireCellClick(sf::Vector2i cell, const std::string& button, const s
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fire cell enter callback with full signature (cell_pos, button, action)
|
// #230 - Fire cell enter callback with position-only signature (cell_pos)
|
||||||
bool UIGrid::fireCellEnter(sf::Vector2i cell, const std::string& button, const std::string& action) {
|
bool UIGrid::fireCellEnter(sf::Vector2i cell) {
|
||||||
// Try property-assigned callback first
|
// Try property-assigned callback first (now PyCellHoverCallable)
|
||||||
if (on_cell_enter_callable && !on_cell_enter_callable->isNone()) {
|
if (on_cell_enter_callable && !on_cell_enter_callable->isNone()) {
|
||||||
PyObject* args = createCellCallbackArgs(cell, button, action);
|
on_cell_enter_callable->call(cell);
|
||||||
if (args) {
|
return true;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try Python subclass method
|
// 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) {
|
if (cell_callback_cache.has_on_cell_enter) {
|
||||||
PyObject* method = PyObject_GetAttrString(pyObj, "on_cell_enter");
|
PyObject* method = PyObject_GetAttrString(pyObj, "on_cell_enter");
|
||||||
if (method && PyCallable_Check(method)) {
|
if (method && PyCallable_Check(method)) {
|
||||||
PyObject* args = createCellCallbackArgs(cell, button, action);
|
// #230: Cell hover takes only (cell_pos)
|
||||||
|
PyObject* args = createCellHoverArgs(cell);
|
||||||
if (args) {
|
if (args) {
|
||||||
PyObject* result = PyObject_CallObject(method, args);
|
PyObject* result = PyObject_CallObject(method, args);
|
||||||
Py_DECREF(args);
|
Py_DECREF(args);
|
||||||
|
|
@ -2655,23 +2691,12 @@ bool UIGrid::fireCellEnter(sf::Vector2i cell, const std::string& button, const s
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fire cell exit callback with full signature (cell_pos, button, action)
|
// #230 - Fire cell exit callback with position-only signature (cell_pos)
|
||||||
bool UIGrid::fireCellExit(sf::Vector2i cell, const std::string& button, const std::string& action) {
|
bool UIGrid::fireCellExit(sf::Vector2i cell) {
|
||||||
// Try property-assigned callback first
|
// Try property-assigned callback first (now PyCellHoverCallable)
|
||||||
if (on_cell_exit_callable && !on_cell_exit_callable->isNone()) {
|
if (on_cell_exit_callable && !on_cell_exit_callable->isNone()) {
|
||||||
PyObject* args = createCellCallbackArgs(cell, button, action);
|
on_cell_exit_callable->call(cell);
|
||||||
if (args) {
|
return true;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try Python subclass method
|
// 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) {
|
if (cell_callback_cache.has_on_cell_exit) {
|
||||||
PyObject* method = PyObject_GetAttrString(pyObj, "on_cell_exit");
|
PyObject* method = PyObject_GetAttrString(pyObj, "on_cell_exit");
|
||||||
if (method && PyCallable_Check(method)) {
|
if (method && PyCallable_Check(method)) {
|
||||||
PyObject* args = createCellCallbackArgs(cell, button, action);
|
// #230: Cell hover takes only (cell_pos)
|
||||||
|
PyObject* args = createCellHoverArgs(cell);
|
||||||
if (args) {
|
if (args) {
|
||||||
PyObject* result = PyObject_CallObject(method, args);
|
PyObject* result = PyObject_CallObject(method, args);
|
||||||
Py_DECREF(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
|
// #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 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);
|
auto new_cell = screenToCell(mousepos);
|
||||||
|
|
||||||
// Check if cell changed
|
// Check if cell changed
|
||||||
if (new_cell != hovered_cell) {
|
if (new_cell != hovered_cell) {
|
||||||
// Fire exit callback for old cell
|
// Fire exit callback for old cell
|
||||||
if (hovered_cell.has_value()) {
|
if (hovered_cell.has_value()) {
|
||||||
fireCellExit(hovered_cell.value(), button, action);
|
fireCellExit(hovered_cell.value());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fire enter callback for new cell
|
// Fire enter callback for new cell
|
||||||
if (new_cell.has_value()) {
|
if (new_cell.has_value()) {
|
||||||
fireCellEnter(new_cell.value(), button, action);
|
fireCellEnter(new_cell.value());
|
||||||
}
|
}
|
||||||
|
|
||||||
hovered_cell = new_cell;
|
hovered_cell = new_cell;
|
||||||
|
|
|
||||||
19
src/UIGrid.h
19
src/UIGrid.h
|
|
@ -84,6 +84,10 @@ public:
|
||||||
std::shared_ptr<PyTexture> getTexture();
|
std::shared_ptr<PyTexture> getTexture();
|
||||||
sf::Sprite sprite, output;
|
sf::Sprite sprite, output;
|
||||||
sf::RenderTexture renderTexture;
|
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)
|
// Intermediate texture for camera_rotation (larger than viewport to hold rotated content)
|
||||||
sf::RenderTexture rotationTexture;
|
sf::RenderTexture rotationTexture;
|
||||||
|
|
@ -131,9 +135,10 @@ public:
|
||||||
TCOD_fov_algorithm_t fov_algorithm; // Default FOV algorithm (from mcrfpy.default_fov)
|
TCOD_fov_algorithm_t fov_algorithm; // Default FOV algorithm (from mcrfpy.default_fov)
|
||||||
int fov_radius; // Default FOV radius
|
int fov_radius; // Default FOV radius
|
||||||
|
|
||||||
// #142 - Grid cell mouse events
|
// #142, #230 - Grid cell mouse events
|
||||||
std::unique_ptr<PyClickCallable> on_cell_enter_callable;
|
// Cell hover callbacks take only (cell_pos); cell click still takes (cell_pos, button, action)
|
||||||
std::unique_ptr<PyClickCallable> on_cell_exit_callable;
|
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::unique_ptr<PyClickCallable> on_cell_click_callable;
|
||||||
std::optional<sf::Vector2i> hovered_cell; // Currently hovered cell or nullopt
|
std::optional<sf::Vector2i> hovered_cell; // Currently hovered cell or nullopt
|
||||||
std::optional<sf::Vector2i> last_clicked_cell; // Cell clicked during click_at
|
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
|
// Now takes button/action for consistent callback signatures
|
||||||
void updateCellHover(sf::Vector2f mousepos, const std::string& button, const std::string& action);
|
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
|
// Returns true if a callback was fired
|
||||||
bool fireCellClick(sf::Vector2i cell, const std::string& button, const std::string& action);
|
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 fireCellEnter(sf::Vector2i cell);
|
||||||
bool fireCellExit(sf::Vector2i cell, const std::string& button, const std::string& action);
|
bool fireCellExit(sf::Vector2i cell);
|
||||||
|
|
||||||
// Refresh cell callback cache for subclass method support
|
// Refresh cell callback cache for subclass method support
|
||||||
void refreshCellCallbackCache(PyObject* pyObj);
|
void refreshCellCallbackCache(PyObject* pyObj);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue