feat(rendering): implement RenderTexture base infrastructure and UIFrame clipping (#6)
- Added RenderTexture support to UIDrawable base class - std::unique_ptr<sf::RenderTexture> for opt-in rendering - Dirty flag system for optimization - enableRenderTexture() and markDirty() methods - Implemented clip_children property for UIFrame - Python-accessible boolean property - Automatic RenderTexture creation when enabled - Proper coordinate transformation for nested frames - Updated UIFrame::render() for clipping support - Renders to RenderTexture when clip_children=true - Handles nested clipping correctly - Only re-renders when dirty flag is set - Added comprehensive dirty flag propagation - All property setters mark frame as dirty - Size changes recreate RenderTexture - Animation system integration - Created tests for clipping functionality - Basic clipping test with visual verification - Advanced nested clipping test - Dynamic resize handling test This is Phase 1 of the RenderTexture overhaul, providing the foundation for advanced rendering effects like blur, glow, and viewport rendering.
This commit is contained in:
parent
5e4224a4f8
commit
967ebcf478
7 changed files with 503 additions and 19 deletions
134
tests/test_frame_clipping.py
Normal file
134
tests/test_frame_clipping.py
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Test UIFrame clipping functionality"""
|
||||
|
||||
import mcrfpy
|
||||
from mcrfpy import Color, Frame, Caption, Vector
|
||||
import sys
|
||||
|
||||
def test_clipping(runtime):
|
||||
"""Test that clip_children property works correctly"""
|
||||
mcrfpy.delTimer("test_clipping")
|
||||
|
||||
print("Testing UIFrame clipping functionality...")
|
||||
|
||||
# Create test scene
|
||||
scene = mcrfpy.sceneUI("test")
|
||||
|
||||
# Create parent frame with clipping disabled (default)
|
||||
parent1 = Frame(50, 50, 200, 150,
|
||||
fill_color=Color(100, 100, 200),
|
||||
outline_color=Color(255, 255, 255),
|
||||
outline=2)
|
||||
parent1.name = "parent1"
|
||||
scene.append(parent1)
|
||||
|
||||
# Create parent frame with clipping enabled
|
||||
parent2 = Frame(300, 50, 200, 150,
|
||||
fill_color=Color(200, 100, 100),
|
||||
outline_color=Color(255, 255, 255),
|
||||
outline=2)
|
||||
parent2.name = "parent2"
|
||||
parent2.clip_children = True
|
||||
scene.append(parent2)
|
||||
|
||||
# Add captions to both frames
|
||||
caption1 = Caption(10, 10, "This text should overflow the frame bounds")
|
||||
caption1.font_size = 16
|
||||
caption1.fill_color = Color(255, 255, 255)
|
||||
parent1.children.append(caption1)
|
||||
|
||||
caption2 = Caption(10, 10, "This text should be clipped to frame bounds")
|
||||
caption2.font_size = 16
|
||||
caption2.fill_color = Color(255, 255, 255)
|
||||
parent2.children.append(caption2)
|
||||
|
||||
# Add child frames that extend beyond parent bounds
|
||||
child1 = Frame(150, 100, 100, 100,
|
||||
fill_color=Color(50, 255, 50),
|
||||
outline_color=Color(0, 0, 0),
|
||||
outline=1)
|
||||
parent1.children.append(child1)
|
||||
|
||||
child2 = Frame(150, 100, 100, 100,
|
||||
fill_color=Color(50, 255, 50),
|
||||
outline_color=Color(0, 0, 0),
|
||||
outline=1)
|
||||
parent2.children.append(child2)
|
||||
|
||||
# Add caption to show clip state
|
||||
status = Caption(50, 250,
|
||||
f"Left frame: clip_children={parent1.clip_children}\n"
|
||||
f"Right frame: clip_children={parent2.clip_children}")
|
||||
status.font_size = 14
|
||||
status.fill_color = Color(255, 255, 255)
|
||||
scene.append(status)
|
||||
|
||||
# Add instructions
|
||||
instructions = Caption(50, 300,
|
||||
"Left: Children should overflow (no clipping)\n"
|
||||
"Right: Children should be clipped to frame bounds\n"
|
||||
"Press 'c' to toggle clipping on left frame")
|
||||
instructions.font_size = 12
|
||||
instructions.fill_color = Color(200, 200, 200)
|
||||
scene.append(instructions)
|
||||
|
||||
# Take screenshot
|
||||
from mcrfpy import Window, automation
|
||||
automation.screenshot("frame_clipping_test.png")
|
||||
|
||||
print(f"Parent1 clip_children: {parent1.clip_children}")
|
||||
print(f"Parent2 clip_children: {parent2.clip_children}")
|
||||
|
||||
# Test toggling clip_children
|
||||
parent1.clip_children = True
|
||||
print(f"After toggle - Parent1 clip_children: {parent1.clip_children}")
|
||||
|
||||
# Verify the property setter works
|
||||
try:
|
||||
parent1.clip_children = "not a bool" # Should raise TypeError
|
||||
print("ERROR: clip_children accepted non-boolean value")
|
||||
except TypeError as e:
|
||||
print(f"PASS: clip_children correctly rejected non-boolean: {e}")
|
||||
|
||||
# Test with animations
|
||||
def animate_frames(runtime):
|
||||
mcrfpy.delTimer("animate")
|
||||
# Animate child frames to show clipping in action
|
||||
# Note: For now, just move the frames manually to demonstrate clipping
|
||||
parent1.children[1].x = 50 # Move child frame
|
||||
parent2.children[1].x = 50 # Move child frame
|
||||
|
||||
# Take another screenshot after starting animation
|
||||
mcrfpy.setTimer("screenshot2", take_second_screenshot, 500)
|
||||
|
||||
def take_second_screenshot(runtime):
|
||||
mcrfpy.delTimer("screenshot2")
|
||||
automation.screenshot("frame_clipping_animated.png")
|
||||
print("\nTest completed successfully!")
|
||||
print("Screenshots saved:")
|
||||
print(" - frame_clipping_test.png (initial state)")
|
||||
print(" - frame_clipping_animated.png (with animation)")
|
||||
sys.exit(0)
|
||||
|
||||
# Start animation after a short delay
|
||||
mcrfpy.setTimer("animate", animate_frames, 100)
|
||||
|
||||
# Main execution
|
||||
print("Creating test scene...")
|
||||
mcrfpy.createScene("test")
|
||||
mcrfpy.setScene("test")
|
||||
|
||||
# Set up keyboard handler to toggle clipping
|
||||
def handle_keypress(key, modifiers):
|
||||
if key == "c":
|
||||
scene = mcrfpy.sceneUI("test")
|
||||
parent1 = scene[0] # First frame
|
||||
parent1.clip_children = not parent1.clip_children
|
||||
print(f"Toggled parent1 clip_children to: {parent1.clip_children}")
|
||||
|
||||
mcrfpy.keypressScene(handle_keypress)
|
||||
|
||||
# Schedule the test
|
||||
mcrfpy.setTimer("test_clipping", test_clipping, 100)
|
||||
|
||||
print("Test scheduled, running...")
|
||||
103
tests/test_frame_clipping_advanced.py
Normal file
103
tests/test_frame_clipping_advanced.py
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Advanced test for UIFrame clipping with nested frames"""
|
||||
|
||||
import mcrfpy
|
||||
from mcrfpy import Color, Frame, Caption, Vector
|
||||
import sys
|
||||
|
||||
def test_nested_clipping(runtime):
|
||||
"""Test nested frames with clipping"""
|
||||
mcrfpy.delTimer("test_nested_clipping")
|
||||
|
||||
print("Testing advanced UIFrame clipping with nested frames...")
|
||||
|
||||
# Create test scene
|
||||
scene = mcrfpy.sceneUI("test")
|
||||
|
||||
# Create outer frame with clipping enabled
|
||||
outer = Frame(50, 50, 400, 300,
|
||||
fill_color=Color(50, 50, 150),
|
||||
outline_color=Color(255, 255, 255),
|
||||
outline=3)
|
||||
outer.name = "outer"
|
||||
outer.clip_children = True
|
||||
scene.append(outer)
|
||||
|
||||
# Create inner frame that extends beyond outer bounds
|
||||
inner = Frame(200, 150, 300, 200,
|
||||
fill_color=Color(150, 50, 50),
|
||||
outline_color=Color(255, 255, 0),
|
||||
outline=2)
|
||||
inner.name = "inner"
|
||||
inner.clip_children = True # Also enable clipping on inner frame
|
||||
outer.children.append(inner)
|
||||
|
||||
# Add content to inner frame that extends beyond its bounds
|
||||
for i in range(5):
|
||||
caption = Caption(10, 30 * i, f"Line {i+1}: This text should be double-clipped")
|
||||
caption.font_size = 14
|
||||
caption.fill_color = Color(255, 255, 255)
|
||||
inner.children.append(caption)
|
||||
|
||||
# Add a child frame to inner that extends way out
|
||||
deeply_nested = Frame(250, 100, 200, 150,
|
||||
fill_color=Color(50, 150, 50),
|
||||
outline_color=Color(255, 0, 255),
|
||||
outline=2)
|
||||
deeply_nested.name = "deeply_nested"
|
||||
inner.children.append(deeply_nested)
|
||||
|
||||
# Add status text
|
||||
status = Caption(50, 380,
|
||||
"Nested clipping test:\n"
|
||||
"- Blue outer frame clips red inner frame\n"
|
||||
"- Red inner frame clips green deeply nested frame\n"
|
||||
"- All text should be clipped to frame bounds")
|
||||
status.font_size = 12
|
||||
status.fill_color = Color(200, 200, 200)
|
||||
scene.append(status)
|
||||
|
||||
# Test render texture size handling
|
||||
print(f"Outer frame size: {outer.w}x{outer.h}")
|
||||
print(f"Inner frame size: {inner.w}x{inner.h}")
|
||||
|
||||
# Dynamically resize frames to test RenderTexture recreation
|
||||
def resize_test(runtime):
|
||||
mcrfpy.delTimer("resize_test")
|
||||
print("Resizing frames to test RenderTexture recreation...")
|
||||
outer.w = 450
|
||||
outer.h = 350
|
||||
inner.w = 350
|
||||
inner.h = 250
|
||||
print(f"New outer frame size: {outer.w}x{outer.h}")
|
||||
print(f"New inner frame size: {inner.w}x{inner.h}")
|
||||
|
||||
# Take screenshot after resize
|
||||
mcrfpy.setTimer("screenshot_resize", take_resize_screenshot, 500)
|
||||
|
||||
def take_resize_screenshot(runtime):
|
||||
mcrfpy.delTimer("screenshot_resize")
|
||||
from mcrfpy import automation
|
||||
automation.screenshot("frame_clipping_resized.png")
|
||||
print("\nAdvanced test completed!")
|
||||
print("Screenshots saved:")
|
||||
print(" - frame_clipping_resized.png (after resize)")
|
||||
sys.exit(0)
|
||||
|
||||
# Take initial screenshot
|
||||
from mcrfpy import automation
|
||||
automation.screenshot("frame_clipping_nested.png")
|
||||
print("Initial screenshot saved: frame_clipping_nested.png")
|
||||
|
||||
# Schedule resize test
|
||||
mcrfpy.setTimer("resize_test", resize_test, 1000)
|
||||
|
||||
# Main execution
|
||||
print("Creating advanced test scene...")
|
||||
mcrfpy.createScene("test")
|
||||
mcrfpy.setScene("test")
|
||||
|
||||
# Schedule the test
|
||||
mcrfpy.setTimer("test_nested_clipping", test_nested_clipping, 100)
|
||||
|
||||
print("Advanced test scheduled, running...")
|
||||
Loading…
Add table
Add a link
Reference in a new issue