Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #1962 - UIGridLayout handles nested layouts #1963

Merged
merged 1 commit into from
Jan 30, 2024
Merged
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
163 changes: 82 additions & 81 deletions arcade/gui/widgets/layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,16 +54,16 @@ class UIAnchorLayout(UILayout):
default_anchor_y = "center"

def __init__(
self,
x: float = 0,
y: float = 0,
width: float = 100,
height: float = 100,
children: Iterable["UIWidget"] = tuple(),
size_hint=(1, 1),
size_hint_min=None,
size_hint_max=None,
**kwargs
self,
x: float = 0,
y: float = 0,
width: float = 1,
height: float = 1,
children: Iterable["UIWidget"] = tuple(),
size_hint=(1, 1),
size_hint_min=None,
size_hint_max=None,
**kwargs
):
super().__init__(
x=x,
Expand All @@ -82,14 +82,14 @@ def do_layout(self):
self._place_child(child, **data)

def add(
self,
child: W,
*,
anchor_x: Optional[str] = None,
align_x: float = 0,
anchor_y: Optional[str] = None,
align_y: float = 0,
**kwargs
self,
child: W,
*,
anchor_x: Optional[str] = None,
align_x: float = 0,
anchor_y: Optional[str] = None,
align_y: float = 0,
**kwargs
) -> W:
"""
Add a widget to the layout as a child. Added widgets will receive
Expand Down Expand Up @@ -119,12 +119,12 @@ def add(
)

def _place_child(
self,
child: UIWidget,
anchor_x: Optional[str] = None,
align_x: float = 0,
anchor_y: Optional[str] = None,
align_y: float = 0,
self,
child: UIWidget,
anchor_x: Optional[str] = None,
align_x: float = 0,
anchor_y: Optional[str] = None,
align_y: float = 0,
):
anchor_x = anchor_x or self.default_anchor_x
anchor_y = anchor_y or self.default_anchor_y
Expand Down Expand Up @@ -214,19 +214,19 @@ class UIBoxLayout(UILayout):
"""

def __init__(
self,
x=0,
y=0,
width=0,
height=0,
vertical=True,
align="center",
children: Iterable[UIWidget] = tuple(),
size_hint=(0, 0),
size_hint_max=None,
space_between=0,
style=None,
**kwargs
self,
x=0,
y=0,
width=1,
height=1,
vertical=True,
align="center",
children: Iterable[UIWidget] = tuple(),
size_hint=(0, 0),
size_hint_max=None,
space_between=0,
style=None,
**kwargs
):
super().__init__(
x=x,
Expand Down Expand Up @@ -321,12 +321,12 @@ def do_layout(self):
# Determine if some space is available for children to grow
available_height = max(0, self.height - self.size_hint_min[1])
total_size_hint_height = (
sum(
child.size_hint[1] or 0
for child in self.children
if child.size_hint
)
or 1
sum(
child.size_hint[1] or 0
for child in self.children
if child.size_hint
)
or 1
) # Prevent division by zero

for child in self.children:
Expand All @@ -343,7 +343,7 @@ def do_layout(self):

# Maximal growth to parent.width * shw
available_growth_height = min_height_value + available_height * (
sh_h / total_size_hint_height
sh_h / total_size_hint_height
)
max_growth_height = self.height * sh_h
new_rect = new_rect.resize(
Expand Down Expand Up @@ -388,12 +388,12 @@ def do_layout(self):
# Calculate if some space is available for children to grow.
available_width = max(0, self.width - self.size_hint_min[0])
total_size_hint_width = (
sum(
child.size_hint[0] or 0
for child in self.children
if child.size_hint
)
or 1
sum(
child.size_hint[0] or 0
for child in self.children
if child.size_hint
)
or 1
) # Prevent division by zero

# TODO Fix layout algorithm, handle size hints per dimension!
Expand All @@ -417,13 +417,13 @@ def do_layout(self):

# Maximal growth to parent.width * shw
available_growth_width = min_width_value + available_width * (
sh_w / total_size_hint_width
sh_w / total_size_hint_width
)
max_growth_width = self.width * sh_w
new_rect = new_rect.resize(
width=min(
available_growth_width, max_growth_width
) # This does not enforce the minimum width
) # This does not enforce the minimum width
)

if shmn_w is not None:
Expand Down Expand Up @@ -494,27 +494,28 @@ class UIGridLayout(UILayout):
"""

def __init__(
self,
x=0,
y=0,
align_horizontal="center",
align_vertical="center",
children: Iterable[UIWidget] = tuple(),
size_hint=(0, 0),
size_hint_max=None,
horizontal_spacing: int = 0,
vertical_spacing: int = 0,
column_count: int = 1,
row_count: int = 1,
style=None,
**kwargs
self,
*,
x=0,
y=0,
align_horizontal="center",
align_vertical="center",
children: Iterable[UIWidget] = tuple(),
size_hint=(0, 0),
size_hint_max=None,
horizontal_spacing: int = 0,
vertical_spacing: int = 0,
column_count: int = 1,
row_count: int = 1,
style=None,
**kwargs
):

super(UIGridLayout, self).__init__(
x=x,
y=y,
width=0,
height=0,
width=1,
height=1,
children=children,
size_hint=size_hint,
size_hint_max=size_hint_max,
Expand Down Expand Up @@ -608,23 +609,23 @@ def min_size(child: UIWidget) -> Tuple[float, float]:
base_height = self._padding_top + self._padding_bottom + 2 * self._border_width

content_height = (
sum(principal_height_ratio_list) + self.row_count * self._vertical_spacing
sum(principal_height_ratio_list) + self.row_count * self._vertical_spacing
)
content_width = (
sum(principal_width_ratio_list)
+ self.column_count * self._horizontal_spacing
sum(principal_width_ratio_list)
+ self.column_count * self._horizontal_spacing
)

self.size_hint_min = (base_width + content_width, base_height + content_height)

def add(
self,
child: W,
col_num: int = 0,
row_num: int = 0,
col_span: int = 1,
row_span: int = 1,
**kwargs
self,
child: W,
col_num: int = 0,
row_num: int = 0,
col_span: int = 1,
row_span: int = 1,
**kwargs
) -> W:
"""
Add a widget to the grid layout.
Expand Down Expand Up @@ -682,9 +683,9 @@ def do_layout(self):
max_height_per_row[row_num][col_num] = (child.height, row_span)

for row in child_sorted_row_wise[
row_num : row_num + row_span # noqa: E203
]:
row[col_num : col_num + col_span] = [child] * col_span # noqa: E203
row_num: row_num + row_span # noqa: E203
]:
row[col_num: col_num + col_span] = [child] * col_span # noqa: E203

principal_height_ratio_list = []
principal_width_ratio_list = []
Expand Down
47 changes: 46 additions & 1 deletion tests/unit/gui/test_layouting_gridlayout.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from arcade.gui import UIDummy
from arcade.gui import UIDummy, UIManager, UIBoxLayout, UIAnchorLayout
from arcade.gui.widgets import Rect
from arcade.gui.widgets.layout import UIGridLayout

Expand Down Expand Up @@ -240,6 +240,7 @@ def test_size_hint_and_spacing(window):
subject.do_layout()
assert dummy1.size == (100, 100)


def test_empty_cells(window):
dummy1 = UIDummy(width=100, height=100)

Expand All @@ -254,3 +255,47 @@ def test_empty_cells(window):
subject.do_layout()

assert dummy1.position == (0, 0)


def test_nested_grid_layouts(window):
ui = UIManager()
outer = UIGridLayout(row_count=1, column_count=1)
inner = UIGridLayout(row_count=1, column_count=1)

inner.add(UIDummy(), 0, 0)
outer.add(inner, 0, 0)
ui.add(outer)

ui._do_layout()

assert inner.rect.size == (100, 100)
assert outer.rect.size == (100, 100)


def test_nested_box_layouts(window):
ui = UIManager()
outer = UIGridLayout(row_count=1, column_count=1)
inner = UIBoxLayout()

inner.add(UIDummy())
outer.add(inner, 0, 0)
ui.add(outer)

ui._do_layout()

assert inner.rect.size == (100, 100)
assert outer.rect.size == (100, 100)


def test_nested_anchor_layouts(window):
ui = UIManager()
outer = UIGridLayout(row_count=1, column_count=1)
inner = UIAnchorLayout(size_hint_min=(100, 100))

outer.add(inner, 0, 0)
ui.add(outer)

ui._do_layout()

assert inner.rect.size == (100, 100)
assert outer.rect.size == (100, 100)
Loading