Phase 4.3: Grid auto-creates GridView with rendering property sync

Grid.__init__() now auto-creates a GridView that shares the Grid's
data via aliasing shared_ptr. This enables the Grid/GridView split:

- PyUIGridObject gains a `view` member (shared_ptr<UIGridView>)
- Grid.view property exposes the auto-created GridView (read-only)
- Rendering property setters (center_x/y, zoom, camera_rotation, x, y,
  w, h) sync changes to the view automatically
- Grid still works as UIDrawable in scenes (no substitution) — backward
  compatible with all existing code and subclasses
- GridView.grid returns the original Grid with identity preservation
- Explicit GridViews (created by user) are independent of Grid's own
  rendering properties

Addresses #252. All 260 tests pass, no breaking changes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
John McCardle 2026-03-19 11:24:47 -04:00
commit a35352df4e
4 changed files with 197 additions and 1 deletions

View file

@ -1,4 +1,5 @@
#include "UIGrid.h"
#include "UIGridView.h" // #252: GridView shim
#include "UIGridPathfinding.h" // New pathfinding API
#include "GameEngine.h"
#include "McRFPy_API.h"
@ -996,6 +997,33 @@ int UIGrid::init(PyUIGridObject* self, PyObject* args, PyObject* kwds) {
// #184: Check if this is a Python subclass (for callback method support)
self->data->is_python_subclass = (PyObject*)Py_TYPE(self) != (PyObject*)&mcrfpydef::PyUIGridType;
// #252 shim: auto-create a GridView for rendering
// The GridView shares GridData (via aliasing shared_ptr) and copies rendering state
{
auto view = std::make_shared<UIGridView>();
// Share grid data (aliasing shared_ptr: shares UIGrid ownership, points to GridData base)
view->grid_data = std::shared_ptr<GridData>(
self->data, static_cast<GridData*>(self->data.get()));
// Copy rendering state from UIGrid to GridView
view->ptex = texture_ptr;
view->box.setPosition(self->data->box.getPosition());
view->box.setSize(self->data->box.getSize());
view->position = self->data->position;
view->center_x = self->data->center_x;
view->center_y = self->data->center_y;
view->zoom = self->data->zoom;
view->fill_color = self->data->fill_color;
view->camera_rotation = self->data->camera_rotation;
view->perspective_entity = self->data->perspective_entity;
view->perspective_enabled = self->data->perspective_enabled;
view->visible = self->data->visible;
view->opacity = self->data->opacity;
view->z_index = self->data->z_index;
view->name = self->data->name;
view->ensureRenderTextureSize();
self->view = view;
}
return 0; // Success
}
@ -1158,6 +1186,23 @@ int UIGrid::set_float_member(PyUIGridObject* self, PyObject* value, void* closur
self->data->zoom = val;
else if (member_ptr == 7) // camera_rotation
self->data->camera_rotation = val;
// #252 shim: sync rendering state to GridView
if (self->view) {
if (member_ptr == 0) // x
self->view->box.setPosition(val, self->view->box.getPosition().y);
else if (member_ptr == 1) // y
self->view->box.setPosition(self->view->box.getPosition().x, val);
else if (member_ptr == 2) // w
self->view->box.setSize(sf::Vector2f(val, self->view->box.getSize().y));
else if (member_ptr == 3) // h
self->view->box.setSize(sf::Vector2f(self->view->box.getSize().x, val));
else if (member_ptr == 4) self->view->center_x = val;
else if (member_ptr == 5) self->view->center_y = val;
else if (member_ptr == 6) self->view->zoom = val;
else if (member_ptr == 7) self->view->camera_rotation = val;
self->view->position = self->view->box.getPosition();
}
return 0;
}
// TODO (7DRL Day 2, item 5.) return Texture object
@ -2565,6 +2610,10 @@ PyGetSetDef UIGrid::getsetters[] = {
{"hovered_cell", (getter)UIGrid::get_hovered_cell, NULL,
"Currently hovered cell as (x, y) tuple, or None if not hovering.", NULL},
UIDRAWABLE_SHADER_GETSETTERS(PyObjectsEnum::UIGRID),
// #252 - GridView shim
{"view", (getter)UIGrid::get_view, NULL,
"Auto-created GridView for rendering (read-only). "
"When Grid is appended to a scene, this view is what actually renders.", NULL},
{NULL} /* Sentinel */
};
@ -2594,6 +2643,18 @@ PyObject* UIGrid::get_children(PyUIGridObject* self, void* closure)
return (PyObject*)o;
}
// #252 - get_view returns the auto-created GridView
PyObject* UIGrid::get_view(PyUIGridObject* self, void* closure)
{
if (!self->view) Py_RETURN_NONE;
auto type = &mcrfpydef::PyUIGridViewType;
auto obj = (PyUIGridViewObject*)type->tp_alloc(type, 0);
if (!obj) return PyErr_NoMemory();
obj->data = self->view;
obj->weakreflist = NULL;
return (PyObject*)obj;
}
PyObject* UIGrid::repr(PyUIGridObject* self)
{
std::ostringstream ss;