McRogueFace/src/UIDrawable.h

280 lines
10 KiB
C++

#pragma once
#include "Common.h"
#include "Python.h"
#include "structmember.h"
#include "IndexTexture.h"
#include "Resources.h"
#include <list>
#include <memory>
#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<PyClickCallable> click_callable;
std::unique_ptr<PyClickCallable> on_enter_callable; // #140
std::unique_ptr<PyClickCallable> on_exit_callable; // #140
std::unique_ptr<PyClickCallable> 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<UIDrawable> parent;
// Set the parent of this drawable (called by collections when adding)
void setParent(std::shared_ptr<UIDrawable> new_parent);
// Get the parent drawable (returns nullptr if no parent or expired)
std::shared_ptr<UIDrawable> 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<sf::RenderTexture> 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<std::vector<std::shared_ptr<UIDrawable>>> data;
std::weak_ptr<UIDrawable> owner; // #122: Parent drawable (for Frame.children, Grid.children)
} PyUICollectionObject;
typedef struct {
PyObject_HEAD
std::shared_ptr<std::vector<std::shared_ptr<UIDrawable>>> 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<T>() 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<PyObjectsEnum>(reinterpret_cast<long>(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<PyObjectsEnum>(reinterpret_cast<long>(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;
}
*/
}