feat(Grid): add customizable background_color property (#50)
- Added sf::Color background_color member with default dark gray - Python property getter/setter for background_color - Animation support for individual color components (r/g/b/a) - Replaces hardcoded clear color in render method - Test demonstrates color changes and property access Closes #50
After Width: | Height: | Size: 31 KiB |
After Width: | Height: | Size: 31 KiB |
After Width: | Height: | Size: 31 KiB |
After Width: | Height: | Size: 31 KiB |
After Width: | Height: | Size: 31 KiB |
After Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
|
@ -982,4 +982,41 @@ When the window was closed externally via the X button, the cleanup order was in
|
||||||
- Moved template functions and macros from `UIDrawable_methods.h` into `UIBase.h`
|
- Moved template functions and macros from `UIDrawable_methods.h` into `UIBase.h`
|
||||||
- Created `UIEntityPyMethods.h` for UIEntity-specific implementations
|
- Created `UIEntityPyMethods.h` for UIEntity-specific implementations
|
||||||
- Removed the now-unnecessary `UIDrawable_methods.h`
|
- Removed the now-unnecessary `UIDrawable_methods.h`
|
||||||
- Result: Better code organization with Python binding code in appropriate headers
|
- Result: Better code organization with Python binding code in appropriate headers
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 6: Rendering Revolution
|
||||||
|
|
||||||
|
### Task: Grid Background Colors (#50)
|
||||||
|
|
||||||
|
**Status**: Completed
|
||||||
|
**Date**: 2025-07-06
|
||||||
|
|
||||||
|
**Goal**: Add background_color property to UIGrid for customizable grid backgrounds
|
||||||
|
|
||||||
|
**Implementation**:
|
||||||
|
1. Added `sf::Color background_color` member to UIGrid class
|
||||||
|
2. Initialized with default dark gray (8, 8, 8, 255) in constructors
|
||||||
|
3. Replaced hardcoded clear color with `renderTexture.clear(background_color)`
|
||||||
|
4. Added Python property getter/setter:
|
||||||
|
- `grid.background_color` returns Color object
|
||||||
|
- Can set with any Color object
|
||||||
|
5. Added animation support via property system:
|
||||||
|
- `background_color.r/g/b/a` can be animated individually
|
||||||
|
- Proper clamping to 0-255 range
|
||||||
|
|
||||||
|
**Technical Details**:
|
||||||
|
- Background renders before grid tiles and entities
|
||||||
|
- Animation support through existing property system
|
||||||
|
- Type-safe Color object validation
|
||||||
|
- No performance impact (just changes clear color)
|
||||||
|
|
||||||
|
**Test Results**:
|
||||||
|
- Default background color (8, 8, 8) works correctly
|
||||||
|
- Setting background_color property changes render
|
||||||
|
- Individual color components can be modified
|
||||||
|
- Color cycling demonstration successful
|
||||||
|
|
||||||
|
**Result**: Grid backgrounds are now customizable, allowing for themed dungeons, environmental effects, and visual polish. This was a perfect warm-up task for Phase 6.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
|
@ -6,7 +6,8 @@
|
||||||
// UIDrawable methods now in UIBase.h
|
// UIDrawable methods now in UIBase.h
|
||||||
|
|
||||||
UIGrid::UIGrid()
|
UIGrid::UIGrid()
|
||||||
: grid_x(0), grid_y(0), zoom(1.0f), center_x(0.0f), center_y(0.0f), ptex(nullptr)
|
: grid_x(0), grid_y(0), zoom(1.0f), center_x(0.0f), center_y(0.0f), ptex(nullptr),
|
||||||
|
background_color(8, 8, 8, 255) // Default dark gray background
|
||||||
{
|
{
|
||||||
// Initialize entities list
|
// Initialize entities list
|
||||||
entities = std::make_shared<std::list<std::shared_ptr<UIEntity>>>();
|
entities = std::make_shared<std::list<std::shared_ptr<UIEntity>>>();
|
||||||
|
@ -30,7 +31,8 @@ UIGrid::UIGrid()
|
||||||
UIGrid::UIGrid(int gx, int gy, std::shared_ptr<PyTexture> _ptex, sf::Vector2f _xy, sf::Vector2f _wh)
|
UIGrid::UIGrid(int gx, int gy, std::shared_ptr<PyTexture> _ptex, sf::Vector2f _xy, sf::Vector2f _wh)
|
||||||
: grid_x(gx), grid_y(gy),
|
: grid_x(gx), grid_y(gy),
|
||||||
zoom(1.0f),
|
zoom(1.0f),
|
||||||
ptex(_ptex), points(gx * gy)
|
ptex(_ptex), points(gx * gy),
|
||||||
|
background_color(8, 8, 8, 255) // Default dark gray background
|
||||||
{
|
{
|
||||||
// Use texture dimensions if available, otherwise use defaults
|
// Use texture dimensions if available, otherwise use defaults
|
||||||
int cell_width = _ptex ? _ptex->sprite_width : DEFAULT_CELL_WIDTH;
|
int cell_width = _ptex ? _ptex->sprite_width : DEFAULT_CELL_WIDTH;
|
||||||
|
@ -76,7 +78,7 @@ void UIGrid::render(sf::Vector2f offset, sf::RenderTarget& target)
|
||||||
output.setTextureRect(
|
output.setTextureRect(
|
||||||
sf::IntRect(0, 0,
|
sf::IntRect(0, 0,
|
||||||
box.getSize().x, box.getSize().y));
|
box.getSize().x, box.getSize().y));
|
||||||
renderTexture.clear(sf::Color(8, 8, 8, 255)); // TODO - UIGrid needs a "background color" field
|
renderTexture.clear(background_color);
|
||||||
|
|
||||||
// Get cell dimensions - use texture if available, otherwise defaults
|
// Get cell dimensions - use texture if available, otherwise defaults
|
||||||
int cell_width = ptex ? ptex->sprite_width : DEFAULT_CELL_WIDTH;
|
int cell_width = ptex ? ptex->sprite_width : DEFAULT_CELL_WIDTH;
|
||||||
|
@ -649,6 +651,29 @@ PyObject* UIGrid::py_at(PyUIGridObject* self, PyObject* args, PyObject* kwds)
|
||||||
return (PyObject*)obj;
|
return (PyObject*)obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PyObject* UIGrid::get_background_color(PyUIGridObject* self, void* closure)
|
||||||
|
{
|
||||||
|
auto& color = self->data->background_color;
|
||||||
|
auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Color");
|
||||||
|
PyObject* args = Py_BuildValue("(iiii)", color.r, color.g, color.b, color.a);
|
||||||
|
PyObject* obj = PyObject_CallObject((PyObject*)type, args);
|
||||||
|
Py_DECREF(args);
|
||||||
|
Py_DECREF(type);
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
int UIGrid::set_background_color(PyUIGridObject* self, PyObject* value, void* closure)
|
||||||
|
{
|
||||||
|
if (!PyObject_IsInstance(value, PyObject_GetAttrString(McRFPy_API::mcrf_module, "Color"))) {
|
||||||
|
PyErr_SetString(PyExc_TypeError, "background_color must be a Color object");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyColorObject* color = (PyColorObject*)value;
|
||||||
|
self->data->background_color = color->data;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
PyMethodDef UIGrid::methods[] = {
|
PyMethodDef UIGrid::methods[] = {
|
||||||
{"at", (PyCFunction)UIGrid::py_at, METH_VARARGS | METH_KEYWORDS},
|
{"at", (PyCFunction)UIGrid::py_at, METH_VARARGS | METH_KEYWORDS},
|
||||||
{NULL, NULL, 0, NULL}
|
{NULL, NULL, 0, NULL}
|
||||||
|
@ -687,6 +712,7 @@ PyGetSetDef UIGrid::getsetters[] = {
|
||||||
{"click", (getter)UIDrawable::get_click, (setter)UIDrawable::set_click, "Object called with (x, y, button) when clicked", (void*)PyObjectsEnum::UIGRID},
|
{"click", (getter)UIDrawable::get_click, (setter)UIDrawable::set_click, "Object called with (x, y, button) when clicked", (void*)PyObjectsEnum::UIGRID},
|
||||||
|
|
||||||
{"texture", (getter)UIGrid::get_texture, NULL, "Texture of the grid", NULL}, //TODO 7DRL-day2-item5
|
{"texture", (getter)UIGrid::get_texture, NULL, "Texture of the grid", NULL}, //TODO 7DRL-day2-item5
|
||||||
|
{"background_color", (getter)UIGrid::get_background_color, (setter)UIGrid::set_background_color, "Background color of the grid", NULL},
|
||||||
{"z_index", (getter)UIDrawable::get_int, (setter)UIDrawable::set_int, "Z-order for rendering (lower values rendered first)", (void*)PyObjectsEnum::UIGRID},
|
{"z_index", (getter)UIDrawable::get_int, (setter)UIDrawable::set_int, "Z-order for rendering (lower values rendered first)", (void*)PyObjectsEnum::UIGRID},
|
||||||
{"name", (getter)UIDrawable::get_name, (setter)UIDrawable::set_name, "Name for finding elements", (void*)PyObjectsEnum::UIGRID},
|
{"name", (getter)UIDrawable::get_name, (setter)UIDrawable::set_name, "Name for finding elements", (void*)PyObjectsEnum::UIGRID},
|
||||||
UIDRAWABLE_GETSETTERS,
|
UIDRAWABLE_GETSETTERS,
|
||||||
|
@ -1455,6 +1481,22 @@ bool UIGrid::setProperty(const std::string& name, float value) {
|
||||||
z_index = static_cast<int>(value);
|
z_index = static_cast<int>(value);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
else if (name == "background_color.r") {
|
||||||
|
background_color.r = static_cast<uint8_t>(std::max(0.0f, std::min(255.0f, value)));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (name == "background_color.g") {
|
||||||
|
background_color.g = static_cast<uint8_t>(std::max(0.0f, std::min(255.0f, value)));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (name == "background_color.b") {
|
||||||
|
background_color.b = static_cast<uint8_t>(std::max(0.0f, std::min(255.0f, value)));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (name == "background_color.a") {
|
||||||
|
background_color.a = static_cast<uint8_t>(std::max(0.0f, std::min(255.0f, value)));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1510,6 +1552,22 @@ bool UIGrid::getProperty(const std::string& name, float& value) const {
|
||||||
value = static_cast<float>(z_index);
|
value = static_cast<float>(z_index);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
else if (name == "background_color.r") {
|
||||||
|
value = static_cast<float>(background_color.r);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (name == "background_color.g") {
|
||||||
|
value = static_cast<float>(background_color.g);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (name == "background_color.b") {
|
||||||
|
value = static_cast<float>(background_color.b);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (name == "background_color.a") {
|
||||||
|
value = static_cast<float>(background_color.a);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -51,6 +51,9 @@ public:
|
||||||
std::vector<UIGridPoint> points;
|
std::vector<UIGridPoint> points;
|
||||||
std::shared_ptr<std::list<std::shared_ptr<UIEntity>>> entities;
|
std::shared_ptr<std::list<std::shared_ptr<UIEntity>>> entities;
|
||||||
|
|
||||||
|
// Background rendering
|
||||||
|
sf::Color background_color;
|
||||||
|
|
||||||
// Property system for animations
|
// Property system for animations
|
||||||
bool setProperty(const std::string& name, float value) override;
|
bool setProperty(const std::string& name, float value) override;
|
||||||
bool setProperty(const std::string& name, const sf::Vector2f& value) override;
|
bool setProperty(const std::string& name, const sf::Vector2f& value) override;
|
||||||
|
@ -70,6 +73,8 @@ public:
|
||||||
static PyObject* get_float_member(PyUIGridObject* self, void* closure);
|
static PyObject* get_float_member(PyUIGridObject* self, void* closure);
|
||||||
static int set_float_member(PyUIGridObject* self, PyObject* value, void* closure);
|
static int set_float_member(PyUIGridObject* self, PyObject* value, void* closure);
|
||||||
static PyObject* get_texture(PyUIGridObject* self, void* closure);
|
static PyObject* get_texture(PyUIGridObject* self, void* closure);
|
||||||
|
static PyObject* get_background_color(PyUIGridObject* self, void* closure);
|
||||||
|
static int set_background_color(PyUIGridObject* self, PyObject* value, void* closure);
|
||||||
static PyObject* py_at(PyUIGridObject* self, PyObject* args, PyObject* kwds);
|
static PyObject* py_at(PyUIGridObject* self, PyObject* args, PyObject* kwds);
|
||||||
static PyMethodDef methods[];
|
static PyMethodDef methods[];
|
||||||
static PyGetSetDef getsetters[];
|
static PyGetSetDef getsetters[];
|
||||||
|
|