#pragma once #include "Python.h" #include "McRFPy_Doc.h" #include class UIEntity; typedef struct { PyObject_HEAD std::shared_ptr data; PyObject* weakreflist; // Weak reference support } PyUIEntityObject; class UIFrame; typedef struct { PyObject_HEAD std::shared_ptr data; PyObject* weakreflist; // Weak reference support } PyUIFrameObject; class UICaption; typedef struct { PyObject_HEAD std::shared_ptr data; PyObject* font; PyObject* weakreflist; // Weak reference support } PyUICaptionObject; class UIGrid; typedef struct { PyObject_HEAD std::shared_ptr data; PyObject* weakreflist; // Weak reference support } PyUIGridObject; class UISprite; typedef struct { PyObject_HEAD std::shared_ptr data; PyObject* weakreflist; // Weak reference support } PyUISpriteObject; // Common Python method implementations for UIDrawable-derived classes // These template functions provide shared functionality for Python bindings // get_bounds method implementation (#89) template static PyObject* UIDrawable_get_bounds(T* self, PyObject* Py_UNUSED(args)) { auto bounds = self->data->get_bounds(); return Py_BuildValue("(ffff)", bounds.left, bounds.top, bounds.width, bounds.height); } // move method implementation (#98) template static PyObject* UIDrawable_move(T* self, PyObject* args) { float dx, dy; if (!PyArg_ParseTuple(args, "ff", &dx, &dy)) { return NULL; } self->data->move(dx, dy); Py_RETURN_NONE; } // resize method implementation (#98) template static PyObject* UIDrawable_resize(T* self, PyObject* args) { float w, h; if (!PyArg_ParseTuple(args, "ff", &w, &h)) { return NULL; } self->data->resize(w, h); Py_RETURN_NONE; } // Macro to add common UIDrawable methods to a method array #define UIDRAWABLE_METHODS \ {"get_bounds", (PyCFunction)UIDrawable_get_bounds, METH_NOARGS, \ MCRF_METHOD(Drawable, get_bounds, \ MCRF_SIG("()", "tuple"), \ MCRF_DESC("Get the bounding rectangle of this drawable element."), \ MCRF_RETURNS("tuple: (x, y, width, height) representing the element's bounds") \ MCRF_NOTE("The bounds are in screen coordinates and account for current position and size.") \ )}, \ {"move", (PyCFunction)UIDrawable_move, METH_VARARGS, \ MCRF_METHOD(Drawable, move, \ MCRF_SIG("(dx: float, dy: float)", "None"), \ MCRF_DESC("Move the element by a relative offset."), \ MCRF_ARGS_START \ MCRF_ARG("dx", "Horizontal offset in pixels") \ MCRF_ARG("dy", "Vertical offset in pixels") \ MCRF_NOTE("This modifies the x and y position properties by the given amounts.") \ )}, \ {"resize", (PyCFunction)UIDrawable_resize, METH_VARARGS, \ MCRF_METHOD(Drawable, resize, \ MCRF_SIG("(width: float, height: float)", "None"), \ MCRF_DESC("Resize the element to new dimensions."), \ MCRF_ARGS_START \ MCRF_ARG("width", "New width in pixels") \ MCRF_ARG("height", "New height in pixels") \ MCRF_NOTE("For Caption and Sprite, this may not change actual size if determined by content.") \ )} // Property getters/setters for visible and opacity template static PyObject* UIDrawable_get_visible(T* self, void* closure) { return PyBool_FromLong(self->data->visible); } template static int UIDrawable_set_visible(T* self, PyObject* value, void* closure) { if (!PyBool_Check(value)) { PyErr_SetString(PyExc_TypeError, "visible must be a boolean"); return -1; } self->data->visible = PyObject_IsTrue(value); return 0; } template static PyObject* UIDrawable_get_opacity(T* self, void* closure) { return PyFloat_FromDouble(self->data->opacity); } template static int UIDrawable_set_opacity(T* self, PyObject* value, void* closure) { float opacity; if (PyFloat_Check(value)) { opacity = PyFloat_AsDouble(value); } else if (PyLong_Check(value)) { opacity = PyLong_AsDouble(value); } else { PyErr_SetString(PyExc_TypeError, "opacity must be a number"); return -1; } // Clamp to valid range if (opacity < 0.0f) opacity = 0.0f; if (opacity > 1.0f) opacity = 1.0f; self->data->opacity = opacity; return 0; } // Macro to add common UIDrawable properties to a getsetters array #define UIDRAWABLE_GETSETTERS \ {"visible", (getter)UIDrawable_get_visible, (setter)UIDrawable_set_visible, \ MCRF_PROPERTY(visible, \ "Whether the object is visible (bool). " \ "Invisible objects are not rendered or clickable." \ ), NULL}, \ {"opacity", (getter)UIDrawable_get_opacity, (setter)UIDrawable_set_opacity, \ MCRF_PROPERTY(opacity, \ "Opacity level (0.0 = transparent, 1.0 = opaque). " \ "Automatically clamped to valid range [0.0, 1.0]." \ ), NULL} // #122 & #102: Macro for parent/global_position properties (requires closure with type enum) // These need the PyObjectsEnum value in closure, so they're added separately in each class #define UIDRAWABLE_PARENT_GETSETTERS(type_enum) \ {"parent", (getter)UIDrawable::get_parent, (setter)UIDrawable::set_parent, \ MCRF_PROPERTY(parent, \ "Parent drawable. " \ "Get: Returns the parent Frame/Grid if nested, or None if at scene level. " \ "Set: Assign a Frame/Grid to reparent, or None to remove from parent." \ ), (void*)type_enum}, \ {"global_position", (getter)UIDrawable::get_global_pos, NULL, \ MCRF_PROPERTY(global_position, \ "Global screen position (read-only). " \ "Calculates absolute position by walking up the parent chain." \ ), (void*)type_enum}, \ {"bounds", (getter)UIDrawable::get_bounds_py, NULL, \ MCRF_PROPERTY(bounds, \ "Bounding rectangle (x, y, width, height) in local coordinates." \ ), (void*)type_enum}, \ {"global_bounds", (getter)UIDrawable::get_global_bounds_py, NULL, \ MCRF_PROPERTY(global_bounds, \ "Bounding rectangle (x, y, width, height) in screen coordinates." \ ), (void*)type_enum}, \ {"on_enter", (getter)UIDrawable::get_on_enter, (setter)UIDrawable::set_on_enter, \ MCRF_PROPERTY(on_enter, \ "Callback for mouse enter events. " \ "Called with (x, y, button, action) when mouse enters this element's bounds." \ ), (void*)type_enum}, \ {"on_exit", (getter)UIDrawable::get_on_exit, (setter)UIDrawable::set_on_exit, \ MCRF_PROPERTY(on_exit, \ "Callback for mouse exit events. " \ "Called with (x, y, button, action) when mouse leaves this element's bounds." \ ), (void*)type_enum}, \ {"hovered", (getter)UIDrawable::get_hovered, NULL, \ MCRF_PROPERTY(hovered, \ "Whether mouse is currently over this element (read-only). " \ "Updated automatically by the engine during mouse movement." \ ), (void*)type_enum}, \ {"on_move", (getter)UIDrawable::get_on_move, (setter)UIDrawable::set_on_move, \ MCRF_PROPERTY(on_move, \ "Callback for mouse movement within bounds. " \ "Called with (x, y, button, action) for each mouse movement while inside. " \ "Performance note: Called frequently during movement - keep handlers fast." \ ), (void*)type_enum} // UIEntity specializations are defined in UIEntity.cpp after UIEntity class is complete