#pragma once #include "Python.h" #include "McRFPy_Doc.h" #include "PyPositionHelper.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 // move method implementation (#98) template static PyObject* UIDrawable_move(T* self, PyObject* args, PyObject* kwds) { float dx, dy; if (!PyPosition_ParseFloat(args, kwds, &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, PyObject* kwds) { float w, h; if (!PyPosition_ParseFloat(args, kwds, &w, &h)) { return NULL; } self->data->resize(w, h); Py_RETURN_NONE; } // animate method implementation - shorthand for creating and starting animations // This free function is implemented in UIDrawable.cpp // We use a free function instead of UIDrawable::animate_helper to avoid incomplete type issues class UIDrawable; PyObject* UIDrawable_animate_impl(std::shared_ptr target, PyObject* args, PyObject* kwds); template static PyObject* UIDrawable_animate(T* self, PyObject* args, PyObject* kwds) { return UIDrawable_animate_impl(self->data, args, kwds); } // Macro to add common UIDrawable methods to a method array (without animate - for base types) // #185: Removed get_bounds method - use .bounds property instead #define UIDRAWABLE_METHODS_BASE \ {"move", (PyCFunction)UIDrawable_move, METH_VARARGS | METH_KEYWORDS, \ MCRF_METHOD(Drawable, move, \ MCRF_SIG("(dx, dy) or (delta)", "None"), \ MCRF_DESC("Move the element by a relative offset."), \ MCRF_ARGS_START \ MCRF_ARG("dx", "Horizontal offset in pixels (or use delta)") \ MCRF_ARG("dy", "Vertical offset in pixels (or use delta)") \ MCRF_ARG("delta", "Offset as tuple, list, or Vector: (dx, dy)") \ MCRF_NOTE("Accepts move(dx, dy), move((dx, dy)), move(Vector), or move(pos=(dx, dy)).") \ )}, \ {"resize", (PyCFunction)UIDrawable_resize, METH_VARARGS | METH_KEYWORDS, \ MCRF_METHOD(Drawable, resize, \ MCRF_SIG("(width, height) or (size)", "None"), \ MCRF_DESC("Resize the element to new dimensions."), \ MCRF_ARGS_START \ MCRF_ARG("width", "New width in pixels (or use size)") \ MCRF_ARG("height", "New height in pixels (or use size)") \ MCRF_ARG("size", "Size as tuple, list, or Vector: (width, height)") \ MCRF_NOTE("Accepts resize(w, h), resize((w, h)), resize(Vector), or resize(pos=(w, h)).") \ )} // Macro to add common UIDrawable methods to a method array (includes animate for UIDrawable derivatives) #define UIDRAWABLE_METHODS \ UIDRAWABLE_METHODS_BASE, \ {"animate", (PyCFunction)UIDrawable_animate, METH_VARARGS | METH_KEYWORDS, \ MCRF_METHOD(Drawable, animate, \ MCRF_SIG("(property: str, target: Any, duration: float, easing=None, delta=False, callback=None, conflict_mode='replace')", "Animation"), \ MCRF_DESC("Create and start an animation on this drawable's property."), \ MCRF_ARGS_START \ MCRF_ARG("property", "Name of the property to animate (e.g., 'x', 'fill_color', 'opacity')") \ MCRF_ARG("target", "Target value - type depends on property (float, tuple for color/vector, etc.)") \ MCRF_ARG("duration", "Animation duration in seconds") \ MCRF_ARG("easing", "Easing function: Easing enum value, string name, or None for linear") \ MCRF_ARG("delta", "If True, target is relative to current value; if False, target is absolute") \ MCRF_ARG("callback", "Optional callable invoked when animation completes") \ MCRF_ARG("conflict_mode", "'replace' (default), 'queue', or 'error' if property already animating") \ MCRF_RETURNS("Animation object for monitoring progress") \ MCRF_RAISES("ValueError", "If property name is not valid for this drawable type") \ MCRF_NOTE("This is a convenience method that creates an Animation, starts it, and adds it to the AnimationManager.") \ )}, \ {"realign", (PyCFunction)UIDrawable::py_realign, METH_NOARGS, \ MCRF_METHOD(Drawable, realign, \ MCRF_SIG("()", "None"), \ MCRF_DESC("Reapply alignment relative to parent, useful for responsive layouts."), \ MCRF_NOTE("Call this to recalculate position after parent changes size. " \ "For elements with align=None, this has no effect.") \ )} // Legacy macro for backwards compatibility - same as UIDRAWABLE_METHODS #define UIDRAWABLE_METHODS_FULL UIDRAWABLE_METHODS // Macro for handling alignment in constructors // Usage: UIDRAWABLE_PROCESS_ALIGNMENT(self->data, align_obj, margin, horiz_margin, vert_margin) // Returns -1 on error (suitable for use in tp_init functions) #define UIDRAWABLE_PROCESS_ALIGNMENT(self_data, align_obj, margin, horiz_margin, vert_margin) \ do { \ if ((align_obj) && (align_obj) != Py_None) { \ AlignmentType _align; \ if (!PyAlignment::from_arg(align_obj, &_align)) { \ return -1; \ } \ if (!UIDrawable::validateMargins(_align, margin, horiz_margin, vert_margin)) { \ return -1; \ } \ (self_data)->align_type = _align; \ (self_data)->align_margin = margin; \ (self_data)->align_horiz_margin = horiz_margin; \ (self_data)->align_vert_margin = vert_margin; \ } \ } while (0) // 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 box as (pos, size) tuple of Vectors. Returns (Vector(x, y), Vector(width, height))." \ ), (void*)type_enum}, \ {"global_bounds", (getter)UIDrawable::get_global_bounds_py, NULL, \ MCRF_PROPERTY(global_bounds, \ "Bounding box as (pos, size) tuple of Vectors in screen coordinates. Returns (Vector(x, y), Vector(width, height))." \ ), (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 (pos: Vector, button: str, action: str) 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 (pos: Vector, button: str, action: str) 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 (pos: Vector, button: str, action: str) for each mouse movement while inside. " \ "Performance note: Called frequently during movement - keep handlers fast." \ ), (void*)type_enum} // Alignment system - automatic positioning relative to parent bounds #define UIDRAWABLE_ALIGNMENT_GETSETTERS(type_enum) \ {"align", (getter)UIDrawable::get_align, (setter)UIDrawable::set_align, \ MCRF_PROPERTY(align, \ "Alignment relative to parent bounds (Alignment enum or None). " \ "When set, position is automatically calculated when parent is assigned or resized. " \ "Set to None to disable alignment and use manual positioning." \ ), (void*)type_enum}, \ {"margin", (getter)UIDrawable::get_margin, (setter)UIDrawable::set_margin, \ MCRF_PROPERTY(margin, \ "General margin from edge when aligned (float). " \ "Applied to both horizontal and vertical edges unless overridden. " \ "Invalid for CENTER alignment (raises ValueError)." \ ), (void*)type_enum}, \ {"horiz_margin", (getter)UIDrawable::get_horiz_margin, (setter)UIDrawable::set_horiz_margin, \ MCRF_PROPERTY(horiz_margin, \ "Horizontal margin override (float, 0 = use general margin). " \ "Invalid for vertically-centered alignments (TOP_CENTER, BOTTOM_CENTER, CENTER)." \ ), (void*)type_enum}, \ {"vert_margin", (getter)UIDrawable::get_vert_margin, (setter)UIDrawable::set_vert_margin, \ MCRF_PROPERTY(vert_margin, \ "Vertical margin override (float, 0 = use general margin). " \ "Invalid for horizontally-centered alignments (CENTER_LEFT, CENTER_RIGHT, CENTER)." \ ), (void*)type_enum} // Rotation support - rotation angle and transform origin #define UIDRAWABLE_ROTATION_GETSETTERS(type_enum) \ {"rotation", (getter)UIDrawable::get_rotation, (setter)UIDrawable::set_rotation, \ MCRF_PROPERTY(rotation, \ "Rotation angle in degrees (clockwise around origin). " \ "Animatable property." \ ), (void*)type_enum}, \ {"origin", (getter)UIDrawable::get_origin, (setter)UIDrawable::set_origin, \ MCRF_PROPERTY(origin, \ "Transform origin as Vector (pivot point for rotation). " \ "Default (0,0) is top-left; set to (w/2, h/2) to rotate around center." \ ), (void*)type_enum}, \ {"rotate_with_camera", (getter)UIDrawable::get_rotate_with_camera, (setter)UIDrawable::set_rotate_with_camera, \ MCRF_PROPERTY(rotate_with_camera, \ "Whether to rotate visually with parent Grid's camera_rotation (bool). " \ "False (default): stay screen-aligned. True: tilt with camera. " \ "Only affects children of UIGrid; ignored for other parents." \ ), (void*)type_enum} // #106: Shader support - GPU-accelerated visual effects #define UIDRAWABLE_SHADER_GETSETTERS(type_enum) \ {"shader", (getter)UIDrawable::get_shader, (setter)UIDrawable::set_shader, \ MCRF_PROPERTY(shader, \ "Shader for GPU visual effects (Shader or None). " \ "When set, the drawable is rendered through the shader program. " \ "Set to None to disable shader effects." \ ), (void*)type_enum}, \ {"uniforms", (getter)UIDrawable::get_uniforms, NULL, \ MCRF_PROPERTY(uniforms, \ "Collection of shader uniforms (read-only access to collection). " \ "Set uniforms via dict-like syntax: drawable.uniforms['name'] = value. " \ "Supports float, vec2/3/4 tuples, PropertyBinding, and CallableBinding." \ ), (void*)type_enum} // UIEntity specializations are defined in UIEntity.cpp after UIEntity class is complete