.parent quirks #183

Closed
opened 2026-01-06 02:20:14 +00:00 by john · 4 comments
Owner

setting .parent seems to add an object as a child, but it doesn't clean it up from an existing parent; moreover, assigning the same parent multiple times results in several references to the same object appearing as children in the parent.

Desired behavior:

  • .parent should be None only when the object is not in any UIDrawable collection. .parent should return the Scene if the object is a top-level object in a Scene (i.e. being on a Scene is distinct from being "nowhere")
  • setting .parent, removing an object from a collection should consistently perform the other action: single parent, single collection membership.
  • setting .parent to None should have the same effect as removing the object from its parent's collection.
setting `.parent` seems to add an object as a child, but it doesn't clean it up from an existing parent; moreover, assigning the same parent multiple times results in several references to the same object appearing as children in the parent. Desired behavior: * `.parent` should be None only when the object is not in any UIDrawable collection. `.parent` should return the Scene if the object is a top-level object in a Scene (i.e. being on a Scene is distinct from being "nowhere") * setting `.parent`, removing an object from a collection should consistently perform the other action: single parent, single collection membership. * setting `.parent` to None should have the same effect as removing the object from its parent's collection.
Author
Owner

Fixed

The following issues have been addressed:

1. Duplicate parent assignments

Setting .parent multiple times to the same parent no longer creates duplicate children entries. The set_parent() function now checks if the drawable is already present in the target parent's collection before adding.

2. Grid parent handling

removeFromParent() now properly handles both UIFrame and UIGrid parent types. Previously only Frame parents were supported, so moving a drawable from a Grid to another parent (or setting .parent = None) wouldn't remove it from the Grid's children.

Bonus: Fixed #189 regression

While testing, discovered that accessing frame.children or grid.children was causing a segfault. This was because #189 made UICollection an internal type (not exported to module), but get_children() still tried to get it via PyObject_GetAttrString(module, "UICollection"). Fixed by using the type directly from the namespace.

Note: The issue's desired behavior for .parent returning Scene for top-level elements was descoped - .parent remains None for scene-level elements, which is consistent and simpler.

Regression test

Added tests/regression/issue_183_parent_quirks_test.py covering:

  • New drawable has parent None
  • Setting same parent twice doesn't duplicate
  • Setting parent removes from old collection
  • Setting parent to None removes from parent collection
  • Grid parent handling works correctly
  • Moving from Frame to Grid works
## Fixed The following issues have been addressed: ### 1. Duplicate parent assignments Setting `.parent` multiple times to the same parent no longer creates duplicate children entries. The `set_parent()` function now checks if the drawable is already present in the target parent's collection before adding. ### 2. Grid parent handling `removeFromParent()` now properly handles both UIFrame and UIGrid parent types. Previously only Frame parents were supported, so moving a drawable from a Grid to another parent (or setting `.parent = None`) wouldn't remove it from the Grid's children. ### Bonus: Fixed #189 regression While testing, discovered that accessing `frame.children` or `grid.children` was causing a segfault. This was because #189 made `UICollection` an internal type (not exported to module), but `get_children()` still tried to get it via `PyObject_GetAttrString(module, "UICollection")`. Fixed by using the type directly from the namespace. **Note:** The issue's desired behavior for `.parent` returning Scene for top-level elements was descoped - `.parent` remains `None` for scene-level elements, which is consistent and simpler. ### Regression test Added `tests/regression/issue_183_parent_quirks_test.py` covering: - New drawable has parent None - Setting same parent twice doesn't duplicate - Setting parent removes from old collection - Setting parent to None removes from parent collection - Grid parent handling works correctly - Moving from Frame to Grid works
Author
Owner

Scene Parent Tracking Implemented

Added scene-level parent tracking to complete the fix. The .parent property now correctly handles scene-level elements:

New Behaviors

  • Scene parent → None: Removes from scene's children
  • Scene parent → Frame/Grid: Removes from scene, adds to drawable's children
  • Frame/Grid parent → Scene: Removes from drawable, adds to scene's children (via scene.children.append())

Implementation Details

  • Added parent_scene string field to UIDrawable to track scene ownership
  • Added scene_name field to PyUICollectionObject for scene collections
  • setParentScene() clears drawable parent, setParent() clears scene parent (mutually exclusive)
  • removeFromParent() now handles both drawable and scene parent removal

Files Modified

  • src/UIDrawable.h - Added parent_scene field and setParentScene() declaration
  • src/UIDrawable.cpp - Implemented setParentScene(), updated removeFromParent() for scene removal
  • src/UICollection.cpp - Updated append() to set parent_scene for scene collections
  • src/McRFPy_API.cpp - Updated _sceneUI() to set scene_name on returned collection

Note on .parent return value

Per earlier discussion, .parent returns None for scene-level elements (not the Scene object itself). This keeps the type system simpler while still enabling proper parent management.

Test Results

All 9 tests pass:

  • 6 existing drawable parent tests
  • 3 new scene parent tests (scene→None, scene→frame, frame→scene)
## Scene Parent Tracking Implemented Added scene-level parent tracking to complete the fix. The `.parent` property now correctly handles scene-level elements: ### New Behaviors - **Scene parent → None**: Removes from scene's children - **Scene parent → Frame/Grid**: Removes from scene, adds to drawable's children - **Frame/Grid parent → Scene**: Removes from drawable, adds to scene's children (via `scene.children.append()`) ### Implementation Details - Added `parent_scene` string field to `UIDrawable` to track scene ownership - Added `scene_name` field to `PyUICollectionObject` for scene collections - `setParentScene()` clears drawable parent, `setParent()` clears scene parent (mutually exclusive) - `removeFromParent()` now handles both drawable and scene parent removal ### Files Modified - `src/UIDrawable.h` - Added `parent_scene` field and `setParentScene()` declaration - `src/UIDrawable.cpp` - Implemented `setParentScene()`, updated `removeFromParent()` for scene removal - `src/UICollection.cpp` - Updated `append()` to set `parent_scene` for scene collections - `src/McRFPy_API.cpp` - Updated `_sceneUI()` to set `scene_name` on returned collection ### Note on `.parent` return value Per earlier discussion, `.parent` returns `None` for scene-level elements (not the Scene object itself). This keeps the type system simpler while still enabling proper parent management. ### Test Results All 9 tests pass: - 6 existing drawable parent tests - 3 new scene parent tests (scene→None, scene→frame, frame→scene)
john closed this issue 2026-01-06 15:55:46 +00:00
Author
Owner

Implementation Complete

All three desired behaviors have been implemented:

1. .parent returns Scene for top-level elements ✓

  • Added parent_scene string field to UIDrawable for scene-level ownership tracking
  • get_parent() now looks up and returns the actual Scene object via PySceneClass::get_scene_by_name()
  • Elements not in any collection return None
  • Elements in Frame/Grid return the parent drawable
  • Elements in Scene return the Scene object

2. Single parent, single collection membership ✓

  • set_parent() calls removeFromParent() before adding to new parent
  • Duplicate check prevents adding same child twice to same parent
  • removeFromParent() handles Frame, Grid, and Scene parents

3. Setting .parent = None removes from collection ✓

  • Works for Frame/Grid children
  • Works for Scene children (removes from scene's ui_elements)

Files Modified

  • src/UIDrawable.h - Added parent_scene field, setParentScene() method
  • src/UIDrawable.cpp - Updated get_parent(), set_parent(), removeFromParent()
  • src/UICollection.cpp - Set parent_scene when appending to scene collection
  • src/McRFPy_API.cpp - Track scene name in UICollection for sceneUI()
  • src/PySceneObject.h/cpp - Added get_scene_by_name() lookup function

Regression Test

tests/regression/issue_183_parent_quirks_test.py - 10 comprehensive tests covering all behaviors

## Implementation Complete All three desired behaviors have been implemented: ### 1. `.parent` returns Scene for top-level elements ✓ - Added `parent_scene` string field to UIDrawable for scene-level ownership tracking - `get_parent()` now looks up and returns the actual Scene object via `PySceneClass::get_scene_by_name()` - Elements not in any collection return `None` - Elements in Frame/Grid return the parent drawable - Elements in Scene return the Scene object ### 2. Single parent, single collection membership ✓ - `set_parent()` calls `removeFromParent()` before adding to new parent - Duplicate check prevents adding same child twice to same parent - `removeFromParent()` handles Frame, Grid, and Scene parents ### 3. Setting `.parent = None` removes from collection ✓ - Works for Frame/Grid children - Works for Scene children (removes from scene's ui_elements) ### Files Modified - `src/UIDrawable.h` - Added `parent_scene` field, `setParentScene()` method - `src/UIDrawable.cpp` - Updated `get_parent()`, `set_parent()`, `removeFromParent()` - `src/UICollection.cpp` - Set `parent_scene` when appending to scene collection - `src/McRFPy_API.cpp` - Track scene name in UICollection for `sceneUI()` - `src/PySceneObject.h/cpp` - Added `get_scene_by_name()` lookup function ### Regression Test `tests/regression/issue_183_parent_quirks_test.py` - 10 comprehensive tests covering all behaviors
Author
Owner

Additional Enhancement: Direct .parent = scene Assignment

Added support for directly assigning a Scene to .parent:

scene = mcrfpy.Scene('game')
frame = mcrfpy.Frame(pos=(10,10), size=(50,50))

# This now works (was TypeError before)
frame.parent = scene

print(frame.parent)  # <Scene 'game'>
print(len(scene.children))  # 1

This also properly removes from existing parents when moving to a Scene:

frame2 = mcrfpy.Frame(pos=(0,0), size=(100,100))
child = mcrfpy.Caption(text='Test', pos=(5,5))
frame2.children.append(child)

# Move from Frame to Scene via direct assignment
child.parent = scene  # Removes from frame2, adds to scene

Updated set_parent() in src/UIDrawable.cpp to handle Scene type.

Regression test expanded to 12 tests covering all parent assignment scenarios.

## Additional Enhancement: Direct `.parent = scene` Assignment Added support for directly assigning a Scene to `.parent`: ```python scene = mcrfpy.Scene('game') frame = mcrfpy.Frame(pos=(10,10), size=(50,50)) # This now works (was TypeError before) frame.parent = scene print(frame.parent) # <Scene 'game'> print(len(scene.children)) # 1 ``` This also properly removes from existing parents when moving to a Scene: ```python frame2 = mcrfpy.Frame(pos=(0,0), size=(100,100)) child = mcrfpy.Caption(text='Test', pos=(5,5)) frame2.children.append(child) # Move from Frame to Scene via direct assignment child.parent = scene # Removes from frame2, adds to scene ``` Updated `set_parent()` in `src/UIDrawable.cpp` to handle Scene type. Regression test expanded to 12 tests covering all parent assignment scenarios.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
john/McRogueFace#183
No description provided.