#pragma once #include "Common.h" #include "Python.h" #include "structmember.h" #include "IndexTexture.h" #include "Resources.h" #include #include #include "PyCallable.h" #include "PyTexture.h" #include "PyColor.h" #include "PyVector.h" #include "PyFont.h" #include "Resources.h" #include "UIBase.h" class UIFrame; class UICaption; class UISprite; class UIEntity; class UIGrid; enum PyObjectsEnum : int { UIFRAME = 1, UICAPTION, UISPRITE, UIGRID, UILINE, UICIRCLE, UIARC }; class UIDrawable { public: void render(); //virtual void render(sf::Vector2f) = 0; virtual void render(sf::Vector2f, sf::RenderTarget&) = 0; virtual PyObjectsEnum derived_type() = 0; // Mouse input handling - callable objects for click, enter, exit, move events std::unique_ptr click_callable; std::unique_ptr on_enter_callable; // #140 std::unique_ptr on_exit_callable; // #140 std::unique_ptr on_move_callable; // #141 virtual UIDrawable* click_at(sf::Vector2f point) = 0; void click_register(PyObject*); void click_unregister(); // #140 - Mouse enter/exit callbacks void on_enter_register(PyObject*); void on_enter_unregister(); void on_exit_register(PyObject*); void on_exit_unregister(); // #141 - Mouse move callback void on_move_register(PyObject*); void on_move_unregister(); // #140 - Hovered state (set by GameEngine) bool hovered = false; UIDrawable(); virtual ~UIDrawable(); // Copy constructor and assignment operator UIDrawable(const UIDrawable& other); UIDrawable& operator=(const UIDrawable& other); // Move constructor and assignment operator UIDrawable(UIDrawable&& other) noexcept; UIDrawable& operator=(UIDrawable&& other) noexcept; static PyObject* get_click(PyObject* self, void* closure); static int set_click(PyObject* self, PyObject* value, void* closure); // #140 - Python API for on_enter/on_exit callbacks static PyObject* get_on_enter(PyObject* self, void* closure); static int set_on_enter(PyObject* self, PyObject* value, void* closure); static PyObject* get_on_exit(PyObject* self, void* closure); static int set_on_exit(PyObject* self, PyObject* value, void* closure); static PyObject* get_hovered(PyObject* self, void* closure); // #141 - Python API for on_move callback static PyObject* get_on_move(PyObject* self, void* closure); static int set_on_move(PyObject* self, PyObject* value, void* closure); static PyObject* get_int(PyObject* self, void* closure); static int set_int(PyObject* self, PyObject* value, void* closure); static PyObject* get_name(PyObject* self, void* closure); static int set_name(PyObject* self, PyObject* value, void* closure); // Common position getters/setters for Python API static PyObject* get_float_member(PyObject* self, void* closure); static int set_float_member(PyObject* self, PyObject* value, void* closure); static PyObject* get_pos(PyObject* self, void* closure); static int set_pos(PyObject* self, PyObject* value, void* closure); // Z-order for rendering (lower values rendered first, higher values on top) int z_index = 0; // Notification for z_index changes void notifyZIndexChanged(); // Name for finding elements std::string name; // Position in pixel coordinates (moved from derived classes) sf::Vector2f position; // Parent-child hierarchy (#122) std::weak_ptr parent; // Set the parent of this drawable (called by collections when adding) void setParent(std::shared_ptr new_parent); // Get the parent drawable (returns nullptr if no parent or expired) std::shared_ptr getParent() const; // Remove this drawable from its current parent's children void removeFromParent(); // Get the global (screen) position by walking up the parent chain (#102) sf::Vector2f get_global_position() const; // Python API for parent/global_position static PyObject* get_parent(PyObject* self, void* closure); static int set_parent(PyObject* self, PyObject* value, void* closure); static PyObject* get_global_pos(PyObject* self, void* closure); // Python API for hit testing (#138) static PyObject* get_bounds_py(PyObject* self, void* closure); static PyObject* get_global_bounds_py(PyObject* self, void* closure); // New properties for Phase 1 bool visible = true; // #87 - visibility flag float opacity = 1.0f; // #88 - opacity (0.0 = transparent, 1.0 = opaque) // New virtual methods for Phase 1 virtual sf::FloatRect get_bounds() const = 0; // #89 - get bounding box virtual void move(float dx, float dy) = 0; // #98 - move by offset virtual void resize(float w, float h) = 0; // #98 - resize to dimensions // Hit testing (#138) sf::FloatRect get_global_bounds() const; // Bounds in screen coordinates bool contains_point(float x, float y) const; // Hit test using global bounds // Called when position changes to allow derived classes to sync virtual void onPositionChanged() {} // Animation support virtual bool setProperty(const std::string& name, float value) { return false; } virtual bool setProperty(const std::string& name, int value) { return false; } virtual bool setProperty(const std::string& name, const sf::Color& value) { return false; } virtual bool setProperty(const std::string& name, const sf::Vector2f& value) { return false; } virtual bool setProperty(const std::string& name, const std::string& value) { return false; } virtual bool getProperty(const std::string& name, float& value) const { return false; } virtual bool getProperty(const std::string& name, int& value) const { return false; } virtual bool getProperty(const std::string& name, sf::Color& value) const { return false; } virtual bool getProperty(const std::string& name, sf::Vector2f& value) const { return false; } virtual bool getProperty(const std::string& name, std::string& value) const { return false; } // Python object cache support uint64_t serial_number = 0; protected: // RenderTexture support (opt-in) std::unique_ptr render_texture; sf::Sprite render_sprite; bool use_render_texture = false; bool render_dirty = true; // Enable RenderTexture for this drawable void enableRenderTexture(unsigned int width, unsigned int height); void updateRenderTexture(); public: // #144: Dirty flag system - content vs composite // content_dirty: THIS drawable's texture needs rebuild (color/text/sprite changed) // composite_dirty: Parent needs to re-composite children (position changed) // Mark content as dirty - texture needs rebuild, propagates up void markDirty(); // Legacy method - calls markContentDirty void markContentDirty(); // Mark only composite as dirty - position changed, texture still valid // Only notifies parent, doesn't set own render_dirty void markCompositeDirty(); // Check if this drawable needs redraw bool isDirty() const { return render_dirty; } bool isCompositeDirty() const { return composite_dirty; } // Clear dirty flags (called after rendering) void clearDirty() { render_dirty = false; composite_dirty = false; } protected: bool composite_dirty = true; // #144: Needs re-composite (child positions changed) }; typedef struct { PyObject_HEAD std::shared_ptr>> data; std::weak_ptr owner; // #122: Parent drawable (for Frame.children, Grid.children) } PyUICollectionObject; typedef struct { PyObject_HEAD std::shared_ptr>> data; int index; int start_size; } PyUICollectionIterObject; namespace mcrfpydef { // DEPRECATED: RET_PY_INSTANCE macro has been replaced with template functions in PyObjectUtils.h // The macro was difficult to debug and used static type references that could cause initialization order issues. // Use PyObjectUtils::convertDrawableToPython() or PyObjectUtils::createPyObject() instead. //TODO: add this method to class scope; move implementation to .cpp file /* static PyObject* PyUIDrawable_get_click(PyObject* self, void* closure) { PyObjectsEnum objtype = static_cast(reinterpret_cast(closure)); // trust me bro, it's an Enum PyObject* ptr; switch (objtype) { case PyObjectsEnum::UIFRAME: ptr = ((PyUIFrameObject*)self)->data->click_callable->borrow(); break; case PyObjectsEnum::UICAPTION: ptr = ((PyUICaptionObject*)self)->data->click_callable->borrow(); break; case PyObjectsEnum::UISPRITE: ptr = ((PyUISpriteObject*)self)->data->click_callable->borrow(); break; case PyObjectsEnum::UIGRID: ptr = ((PyUIGridObject*)self)->data->click_callable->borrow(); break; default: PyErr_SetString(PyExc_TypeError, "no idea how you did that; invalid UIDrawable derived instance for _get_click"); return NULL; } if (ptr && ptr != Py_None) return ptr; else return Py_None; }*/ //TODO: add this method to class scope; move implementation to .cpp file /* static int PyUIDrawable_set_click(PyObject* self, PyObject* value, void* closure) { PyObjectsEnum objtype = static_cast(reinterpret_cast(closure)); // trust me bro, it's an Enum UIDrawable* target; switch (objtype) { case PyObjectsEnum::UIFRAME: target = (((PyUIFrameObject*)self)->data.get()); break; case PyObjectsEnum::UICAPTION: target = (((PyUICaptionObject*)self)->data.get()); break; case PyObjectsEnum::UISPRITE: target = (((PyUISpriteObject*)self)->data.get()); break; case PyObjectsEnum::UIGRID: target = (((PyUIGridObject*)self)->data.get()); break; default: PyErr_SetString(PyExc_TypeError, "no idea how you did that; invalid UIDrawable derived instance for _set_click"); return -1; } if (value == Py_None) { target->click_unregister(); } else { target->click_register(value); } return 0; } */ }