Add compound Color and Vector animation targets (pos, fill_color), closes #218

UIFrame, UICaption, and UISprite now accept "pos" as an alias for "position"
in the animation property system. UICaption and UISprite gain Vector2f
setProperty/getProperty overrides enabling animate("pos", (x, y), duration).
Color compound animation (fill_color, outline_color) was already supported.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
John McCardle 2026-04-10 02:05:55 -04:00
commit 061b29a07a
6 changed files with 144 additions and 6 deletions

View file

@ -803,6 +803,32 @@ bool UICaption::getProperty(const std::string& name, float& value) const {
return false;
}
bool UICaption::setProperty(const std::string& name, const sf::Vector2f& value) {
if (name == "position" || name == "pos") {
position = value;
text.setPosition(position);
markDirty();
return true;
} else if (name == "origin") {
origin = value;
text.setOrigin(origin);
markDirty();
return true;
}
return false;
}
bool UICaption::getProperty(const std::string& name, sf::Vector2f& value) const {
if (name == "position" || name == "pos") {
value = position;
return true;
} else if (name == "origin") {
value = origin;
return true;
}
return false;
}
bool UICaption::getProperty(const std::string& name, sf::Color& value) const {
if (name == "fill_color") {
value = text.getFillColor();
@ -843,7 +869,7 @@ bool UICaption::hasProperty(const std::string& name) const {
return true;
}
// Vector2f properties
if (name == "origin") {
if (name == "origin" || name == "position" || name == "pos") {
return true;
}
// #106: Check for shader uniform properties

View file

@ -22,10 +22,12 @@ public:
// Property system for animations
bool setProperty(const std::string& name, float value) override;
bool setProperty(const std::string& name, const sf::Color& value) override;
bool setProperty(const std::string& name, const sf::Vector2f& value) override;
bool setProperty(const std::string& name, const std::string& value) override;
bool getProperty(const std::string& name, float& value) const override;
bool getProperty(const std::string& name, sf::Color& value) const override;
bool getProperty(const std::string& name, sf::Vector2f& value) const override;
bool getProperty(const std::string& name, std::string& value) const override;
bool hasProperty(const std::string& name) const override;

View file

@ -924,7 +924,7 @@ bool UIFrame::setProperty(const std::string& name, const sf::Color& value) {
}
bool UIFrame::setProperty(const std::string& name, const sf::Vector2f& value) {
if (name == "position") {
if (name == "position" || name == "pos") {
position = value;
box.setPosition(position); // Keep box in sync
markDirty();
@ -1019,7 +1019,7 @@ bool UIFrame::getProperty(const std::string& name, sf::Color& value) const {
}
bool UIFrame::getProperty(const std::string& name, sf::Vector2f& value) const {
if (name == "position") {
if (name == "position" || name == "pos") {
value = position;
return true;
} else if (name == "size") {
@ -1048,7 +1048,7 @@ bool UIFrame::hasProperty(const std::string& name) const {
return true;
}
// Vector2f properties
if (name == "position" || name == "size" || name == "origin") {
if (name == "position" || name == "pos" || name == "size" || name == "origin") {
return true;
}
// #106: Check for shader uniform properties

View file

@ -756,6 +756,32 @@ bool UISprite::getProperty(const std::string& name, float& value) const {
return false;
}
bool UISprite::setProperty(const std::string& name, const sf::Vector2f& value) {
if (name == "position" || name == "pos") {
position = value;
sprite.setPosition(position);
markDirty();
return true;
} else if (name == "origin") {
origin = value;
sprite.setOrigin(origin);
markDirty();
return true;
}
return false;
}
bool UISprite::getProperty(const std::string& name, sf::Vector2f& value) const {
if (name == "position" || name == "pos") {
value = position;
return true;
} else if (name == "origin") {
value = origin;
return true;
}
return false;
}
bool UISprite::getProperty(const std::string& name, int& value) const {
if (name == "sprite_index") {
value = sprite_index;
@ -781,7 +807,7 @@ bool UISprite::hasProperty(const std::string& name) const {
return true;
}
// Vector2f properties
if (name == "origin") {
if (name == "origin" || name == "position" || name == "pos") {
return true;
}
// #106: Check for shader uniform properties

View file

@ -60,8 +60,10 @@ public:
// Property system for animations
bool setProperty(const std::string& name, float value) override;
bool setProperty(const std::string& name, int value) override;
bool setProperty(const std::string& name, const sf::Vector2f& value) override;
bool getProperty(const std::string& name, float& value) const override;
bool getProperty(const std::string& name, int& value) const override;
bool getProperty(const std::string& name, sf::Vector2f& value) const override;
bool hasProperty(const std::string& name) const override;

View file

@ -0,0 +1,82 @@
"""Test compound (Color and Vector) animation targets - issue #218"""
import mcrfpy
import sys
PASS = True
def check(name, condition):
global PASS
if not condition:
print(f"FAIL: {name}")
PASS = False
else:
print(f" ok: {name}")
# Create a scene with test objects
scene = mcrfpy.Scene("test_compound_anim")
ui = scene.children
frame = mcrfpy.Frame(pos=(10, 20), size=(100, 100),
fill_color=mcrfpy.Color(255, 0, 0))
ui.append(frame)
cap = mcrfpy.Caption(text="hello", pos=(50, 60))
ui.append(cap)
tex = mcrfpy.Texture("assets/kenney_tinydungeon.png", 16, 16)
sprite = mcrfpy.Sprite(pos=(70, 80), texture=tex)
ui.append(sprite)
mcrfpy.current_scene = scene
# Test 1: Frame "pos" animation property recognized
check("Frame hasProperty 'pos'", True) # would fail at animate() if not
frame.animate("pos", (200, 300), 0.5, mcrfpy.Easing.LINEAR)
# Test 2: Frame "position" animation property still works
frame2 = mcrfpy.Frame(pos=(0, 0), size=(50, 50))
ui.append(frame2)
frame2.animate("position", (100, 100), 0.5, mcrfpy.Easing.LINEAR)
# Test 3: Frame "fill_color" compound animation
frame.animate("fill_color", (0, 255, 0, 255), 0.5, mcrfpy.Easing.LINEAR)
# Test 4: Frame "outline_color" compound animation
frame.animate("outline_color", (128, 128, 128), 0.5, mcrfpy.Easing.LINEAR)
# Test 5: Caption "pos" animation
cap.animate("pos", (200, 200), 0.5, mcrfpy.Easing.LINEAR)
# Test 6: Caption "position" animation
cap.animate("position", (300, 300), 0.5, mcrfpy.Easing.LINEAR)
# Test 7: Caption "fill_color" compound animation
cap.animate("fill_color", (0, 0, 255), 0.5, mcrfpy.Easing.LINEAR)
# Test 8: Sprite "pos" animation
sprite.animate("pos", (200, 200), 0.5, mcrfpy.Easing.LINEAR)
# Test 9: Sprite "position" animation
sprite.animate("position", (300, 300), 0.5, mcrfpy.Easing.LINEAR)
# Test 10: Frame "size" compound animation
frame.animate("size", (200, 200), 0.5, mcrfpy.Easing.LINEAR)
# Test 11: Step time forward and verify position changed
initial_x = frame.x
for _ in range(10):
mcrfpy.step(0.06)
check("Frame moved from pos animation", frame.x != initial_x)
# Test 12: Verify Caption position changed
check("Caption pos changed", cap.x != 50)
# Test 13: Verify Sprite position changed
check("Sprite pos changed", sprite.x != 70)
if PASS:
print("PASS")
sys.exit(0)
else:
sys.exit(1)