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

@ -27,6 +27,7 @@
#include "SpatialHash.h"
#include "UIEntityCollection.h" // EntityCollection types (extracted from UIGrid)
#include "GridData.h" // #252 - Data layer base class
#include "UIGridView.h" // #252 - GridView shim
// Forward declaration for pathfinding
class DijkstraMap;
@ -152,6 +153,7 @@ public:
static int set_on_cell_click(PyUIGridObject* self, PyObject* value, void* closure);
static PyObject* get_hovered_cell(PyUIGridObject* self, void* closure);
static PyObject* get_view(PyUIGridObject* self, void* closure); // #252 shim
static PyObject* py_add_layer(PyUIGridObject* self, PyObject* args);
static PyObject* py_remove_layer(PyUIGridObject* self, PyObject* args);
static PyObject* get_layers(PyUIGridObject* self, void* closure);
@ -189,6 +191,7 @@ namespace mcrfpydef {
obj->data->on_cell_exit_callable.reset();
obj->data->on_cell_click_callable.reset();
}
obj->view.reset(); // #252: release GridView shim
obj->data.reset();
Py_TYPE(self)->tp_free(self);
},
@ -300,7 +303,11 @@ namespace mcrfpydef {
.tp_new = [](PyTypeObject* type, PyObject* args, PyObject* kwds) -> PyObject*
{
PyUIGridObject* self = (PyUIGridObject*)type->tp_alloc(type, 0);
if (self) self->data = std::make_shared<UIGrid>();
if (self) {
self->data = std::make_shared<UIGrid>();
// Placement-new the shared_ptr<UIGridView> (tp_alloc zero-fills, not construct)
new (&self->view) std::shared_ptr<UIGridView>();
}
return (PyObject*)self;
}
};