Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions arcade/gui/widgets/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,47 @@ def remove(self, child: UIWidget) -> dict | None:
return c.data
return None

def get_child_data(self, child: UIWidget) -> dict | None:
"""Get the layout data for a child widget.
Returns the kwargs dict that was passed when the child was
added (e.g., ``anchor_x``, ``align_x`` for
:py:class:`UIAnchorLayout` children).
The returned dict is the *live* internal data. Modifying it
will affect the child's layout on the next
:py:meth:`do_layout` call.
Args:
child: The child widget to look up.
Returns:
The layout data dict, or ``None`` if *child* is not a
direct child of this widget.
"""
for entry in self._children:
if entry.child == child:
return entry.data
return None

def get_child_entry(self, index: int) -> tuple[UIWidget, dict]:
"""Get the child widget and its layout data by index.
Supports negative indices (e.g., ``-1`` for the last child),
following standard Python sequence semantics.
Args:
index: Position of the child (0-based, in add order).
Returns:
A ``(child_widget, layout_data_dict)`` tuple.
Raises:
IndexError: If *index* is out of range.
"""
entry = self._children[index]
return entry.child, entry.data

def clear(self):
"""Removes all children"""
for child in self.children:
Expand Down
116 changes: 116 additions & 0 deletions tests/unit/gui/test_child_data_access.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import pytest

from arcade.gui import UIDummy
from arcade.gui.widgets.layout import UIAnchorLayout, UIBoxLayout, UIGridLayout


def test_get_child_data_returns_correct_dict(window):
"""get_child_data() should return the kwargs dict passed to add()."""
layout = UIAnchorLayout(width=500, height=500, size_hint=None)
child = UIDummy(width=100, height=100)
layout.add(child, anchor_x="left", align_x=50, anchor_y="top", align_y=-10)

data = layout.get_child_data(child)

assert data is not None
assert data["anchor_x"] == "left"
assert data["align_x"] == 50
assert data["anchor_y"] == "top"
assert data["align_y"] == -10


def test_get_child_data_unknown_widget_returns_none(window):
"""get_child_data() should return None for a widget not in the layout."""
layout = UIAnchorLayout(width=500, height=500, size_hint=None)
stranger = UIDummy(width=50, height=50)

assert layout.get_child_data(stranger) is None


def test_get_child_data_modification_affects_layout(window):
"""Modifying the returned dict should change positioning on next do_layout()."""
layout = UIAnchorLayout(x=0, y=0, width=500, height=500, size_hint=None)
child = UIDummy(width=100, height=100)
layout.add(child, anchor_x="left", align_x=0, anchor_y="bottom", align_y=0)

layout.do_layout()
original_left = child.left

data = layout.get_child_data(child)
data["align_x"] = 50
layout.do_layout()

assert child.left == original_left + 50


def test_get_child_entry_returns_first_child(window):
"""get_child_entry(0) should return the first added child and its data."""
layout = UIAnchorLayout(width=500, height=500, size_hint=None)
first = UIDummy(width=100, height=100)
second = UIDummy(width=100, height=100)
layout.add(first, anchor_x="left")
layout.add(second, anchor_x="right")

child, data = layout.get_child_entry(0)

assert child is first
assert data["anchor_x"] == "left"


def test_get_child_entry_negative_index(window):
"""get_child_entry(-1) should return the last child."""
layout = UIAnchorLayout(width=500, height=500, size_hint=None)
first = UIDummy(width=100, height=100)
last = UIDummy(width=100, height=100)
layout.add(first, anchor_x="left")
layout.add(last, anchor_x="right")

child, data = layout.get_child_entry(-1)

assert child is last
assert data["anchor_x"] == "right"


def test_get_child_entry_out_of_range_raises(window):
"""get_child_entry() should raise IndexError for out-of-range indices."""
layout = UIAnchorLayout(width=500, height=500, size_hint=None)
layout.add(UIDummy(width=50, height=50))

with pytest.raises(IndexError):
layout.get_child_entry(5)


def test_works_with_box_layout(window):
"""get_child_data() should work with UIBoxLayout children."""
layout = UIBoxLayout(width=500, height=500, size_hint=None)
child = UIDummy(width=100, height=100)
layout.add(child)

data = layout.get_child_data(child)
assert data is not None


def test_works_with_grid_layout(window):
"""get_child_data() should work with UIGridLayout children."""
layout = UIGridLayout(column_count=2, row_count=2, size_hint=None)
child = UIDummy(width=100, height=100)
layout.add(child, column=0, row=0)

data = layout.get_child_data(child)
assert data is not None
assert data["column"] == 0
assert data["row"] == 0


def test_after_remove_and_readd_returns_new_data(window):
"""After remove() + add(), get_child_data() should return the new data."""
layout = UIAnchorLayout(width=500, height=500, size_hint=None)
child = UIDummy(width=100, height=100)

layout.add(child, anchor_x="left", align_x=10)
layout.remove(child)
layout.add(child, anchor_x="right", align_x=99)

data = layout.get_child_data(child)
assert data["anchor_x"] == "right"
assert data["align_x"] == 99
Loading