Skip to content

Commit 570df7a

Browse files
Copiloteruvanos
andcommitted
Add size_hint warnings for GUI layouts when fixed width/height conflicts with active size_hint
Co-authored-by: eruvanos <9437863+eruvanos@users.noreply.github.com>
1 parent 63be4b4 commit 570df7a

4 files changed

Lines changed: 185 additions & 32 deletions

File tree

arcade/gui/widgets/layout.py

Lines changed: 62 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,47 @@
1414

1515
W = TypeVar("W", bound="UIWidget")
1616

17+
_NO_EXPLICIT_SIZE = object()
18+
"""Sentinel value to detect when width/height was not explicitly provided by the user."""
19+
20+
21+
def _warn_if_size_hint_overrides_fixed_size(
22+
class_name: str,
23+
width,
24+
height,
25+
size_hint,
26+
) -> None:
27+
"""Warn when a fixed width/height is given but the size_hint will override it.
28+
29+
Layouts have non-None size_hint by default, which causes the parent layout to
30+
resize them, overriding any fixed width/height given by the developer.
31+
32+
Args:
33+
class_name: Name of the layout class, used in the warning message.
34+
width: The width argument passed to __init__, or ``_NO_EXPLICIT_SIZE`` if
35+
width was not explicitly provided.
36+
height: The height argument passed to __init__, or ``_NO_EXPLICIT_SIZE`` if
37+
height was not explicitly provided.
38+
size_hint: The size_hint argument passed to __init__.
39+
"""
40+
sh_w = size_hint[0] if size_hint is not None else None
41+
sh_h = size_hint[1] if size_hint is not None else None
42+
43+
if width is not _NO_EXPLICIT_SIZE and sh_w is not None:
44+
warnings.warn(
45+
f"{class_name} was given a fixed width, but size_hint_x is {sh_w!r}. "
46+
f"The size_hint will override the fixed width. "
47+
f"Set size_hint=(None, ...) to use a fixed width.",
48+
stacklevel=3,
49+
)
50+
if height is not _NO_EXPLICIT_SIZE and sh_h is not None:
51+
warnings.warn(
52+
f"{class_name} was given a fixed height, but size_hint_y is {sh_h!r}. "
53+
f"The size_hint will override the fixed height. "
54+
f"Set size_hint=(..., None) to use a fixed height.",
55+
stacklevel=3,
56+
)
57+
1758

1859
class UIAnchorLayout(UILayout):
1960
"""Places children based on anchor values.
@@ -73,19 +114,22 @@ def __init__(
73114
*,
74115
x: float = 0,
75116
y: float = 0,
76-
width: float = 1,
77-
height: float = 1,
117+
width: float = _NO_EXPLICIT_SIZE,
118+
height: float = _NO_EXPLICIT_SIZE,
78119
children: Iterable[UIWidget] = tuple(),
79120
size_hint=(1, 1),
80121
size_hint_min=None,
81122
size_hint_max=None,
82123
**kwargs,
83124
):
125+
_warn_if_size_hint_overrides_fixed_size(
126+
type(self).__name__, width, height, size_hint
127+
)
84128
super().__init__(
85129
x=x,
86130
y=y,
87-
width=width,
88-
height=height,
131+
width=1 if width is _NO_EXPLICIT_SIZE else width,
132+
height=1 if height is _NO_EXPLICIT_SIZE else height,
89133
children=children,
90134
size_hint=size_hint,
91135
size_hint_min=size_hint_min,
@@ -241,8 +285,8 @@ def __init__(
241285
*,
242286
x=0,
243287
y=0,
244-
width=1,
245-
height=1,
288+
width=_NO_EXPLICIT_SIZE,
289+
height=_NO_EXPLICIT_SIZE,
246290
vertical=True,
247291
align="center",
248292
children: Iterable[UIWidget] = tuple(),
@@ -252,11 +296,14 @@ def __init__(
252296
style=None,
253297
**kwargs,
254298
):
299+
_warn_if_size_hint_overrides_fixed_size(
300+
type(self).__name__, width, height, size_hint
301+
)
255302
super().__init__(
256303
x=x,
257304
y=y,
258-
width=width,
259-
height=height,
305+
width=1 if width is _NO_EXPLICIT_SIZE else width,
306+
height=1 if height is _NO_EXPLICIT_SIZE else height,
260307
children=children,
261308
size_hint=size_hint,
262309
size_hint_max=size_hint_max,
@@ -487,8 +534,8 @@ def __init__(
487534
*,
488535
x=0,
489536
y=0,
490-
width=1,
491-
height=1,
537+
width=_NO_EXPLICIT_SIZE,
538+
height=_NO_EXPLICIT_SIZE,
492539
align_horizontal="center",
493540
align_vertical="center",
494541
children: Iterable[UIWidget] = tuple(),
@@ -500,11 +547,14 @@ def __init__(
500547
row_count: int = 1,
501548
**kwargs,
502549
):
550+
_warn_if_size_hint_overrides_fixed_size(
551+
type(self).__name__, width, height, size_hint
552+
)
503553
super().__init__(
504554
x=x,
505555
y=y,
506-
width=width,
507-
height=height,
556+
width=1 if width is _NO_EXPLICIT_SIZE else width,
557+
height=1 if height is _NO_EXPLICIT_SIZE else height,
508558
children=children,
509559
size_hint=size_hint,
510560
size_hint_max=size_hint_max,
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
"""Tests that layouts warn when explicit width/height conflicts with active size_hint."""
2+
import pytest
3+
4+
from arcade.gui import UIBoxLayout
5+
from arcade.gui.widgets.layout import UIAnchorLayout, UIGridLayout
6+
7+
8+
def test_anchor_layout_warns_when_width_given_with_default_size_hint(window):
9+
"""UIAnchorLayout should warn when width is given but size_hint_x is active."""
10+
with pytest.warns(UserWarning, match="size_hint_x"):
11+
UIAnchorLayout(width=500)
12+
13+
14+
def test_anchor_layout_warns_when_height_given_with_default_size_hint(window):
15+
"""UIAnchorLayout should warn when height is given but size_hint_y is active."""
16+
with pytest.warns(UserWarning, match="size_hint_y"):
17+
UIAnchorLayout(height=500)
18+
19+
20+
def test_anchor_layout_no_warning_when_size_hint_none(window):
21+
"""UIAnchorLayout should not warn when size_hint=None is explicitly set."""
22+
# No warning expected
23+
UIAnchorLayout(width=500, height=500, size_hint=None)
24+
25+
26+
def test_anchor_layout_no_warning_when_no_explicit_size(window):
27+
"""UIAnchorLayout should not warn when width/height are not explicitly given."""
28+
# No warning expected
29+
UIAnchorLayout(size_hint=(1, 1))
30+
31+
32+
def test_anchor_layout_no_warning_when_size_hint_x_none(window):
33+
"""UIAnchorLayout should not warn for width when size_hint_x is None."""
34+
# No width warning expected (only height warning)
35+
with pytest.warns(UserWarning, match="size_hint_y"):
36+
UIAnchorLayout(width=500, height=500, size_hint=(None, 1))
37+
38+
39+
def test_anchor_layout_no_warning_when_size_hint_y_none(window):
40+
"""UIAnchorLayout should not warn for height when size_hint_y is None."""
41+
# No height warning expected (only width warning)
42+
with pytest.warns(UserWarning, match="size_hint_x"):
43+
UIAnchorLayout(width=500, height=500, size_hint=(1, None))
44+
45+
46+
def test_box_layout_warns_when_width_given_with_default_size_hint(window):
47+
"""UIBoxLayout should warn when width is given but size_hint_x is active."""
48+
with pytest.warns(UserWarning, match="size_hint_x"):
49+
UIBoxLayout(width=200)
50+
51+
52+
def test_box_layout_warns_when_height_given_with_default_size_hint(window):
53+
"""UIBoxLayout should warn when height is given but size_hint_y is active."""
54+
with pytest.warns(UserWarning, match="size_hint_y"):
55+
UIBoxLayout(height=200)
56+
57+
58+
def test_box_layout_no_warning_when_size_hint_none(window):
59+
"""UIBoxLayout should not warn when size_hint=None is explicitly set."""
60+
# No warning expected
61+
UIBoxLayout(width=200, height=200, size_hint=None)
62+
63+
64+
def test_box_layout_no_warning_when_no_explicit_size(window):
65+
"""UIBoxLayout should not warn when width/height are not explicitly given."""
66+
# No warning expected
67+
UIBoxLayout(size_hint=(0, 0))
68+
69+
70+
def test_grid_layout_warns_when_width_given_with_default_size_hint(window):
71+
"""UIGridLayout should warn when width is given but size_hint_x is active."""
72+
with pytest.warns(UserWarning, match="size_hint_x"):
73+
UIGridLayout(width=200)
74+
75+
76+
def test_grid_layout_warns_when_height_given_with_default_size_hint(window):
77+
"""UIGridLayout should warn when height is given but size_hint_y is active."""
78+
with pytest.warns(UserWarning, match="size_hint_y"):
79+
UIGridLayout(height=200)
80+
81+
82+
def test_grid_layout_no_warning_when_size_hint_none(window):
83+
"""UIGridLayout should not warn when size_hint=None is explicitly set."""
84+
# No warning expected
85+
UIGridLayout(width=200, height=200, size_hint=None)
86+
87+
88+
def test_grid_layout_no_warning_when_no_explicit_size(window):
89+
"""UIGridLayout should not warn when width/height are not explicitly given."""
90+
# No warning expected
91+
UIGridLayout(size_hint=(0, 0))
92+
93+
94+
def test_warning_message_includes_class_name(window):
95+
"""Warning should include the layout class name for clear identification."""
96+
with pytest.warns(UserWarning, match="UIBoxLayout"):
97+
UIBoxLayout(width=200)
98+
99+
with pytest.warns(UserWarning, match="UIAnchorLayout"):
100+
UIAnchorLayout(width=200)
101+
102+
with pytest.warns(UserWarning, match="UIGridLayout"):
103+
UIGridLayout(width=200)

tests/unit/gui/test_layouting_anchorlayout.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
def test_place_widget(window):
99
dummy = UIDummy(width=100, height=200)
1010

11-
subject = UIAnchorLayout(x=0, y=0, width=500, height=500)
11+
subject = UIAnchorLayout(x=0, y=0, width=500, height=500, size_hint=None)
1212

1313
subject.add(
1414
dummy,
@@ -30,7 +30,7 @@ def test_place_widget_relative_to_own_content_rect(window):
3030
dummy = UIDummy(width=100, height=200)
3131

3232
subject = (
33-
UIAnchorLayout(x=0, y=0, width=500, height=500)
33+
UIAnchorLayout(x=0, y=0, width=500, height=500, size_hint=None)
3434
.with_border(width=2)
3535
.with_padding(left=50, top=100)
3636
)
@@ -68,7 +68,7 @@ def test_place_box_layout(window, ui):
6868

6969

7070
def test_grow_child_half(window):
71-
subject = UIAnchorLayout(width=400, height=400)
71+
subject = UIAnchorLayout(width=400, height=400, size_hint=None)
7272
dummy = subject.add(UIDummy(width=100, height=100, size_hint=(0.5, 0.5)))
7373

7474
subject._do_layout()
@@ -78,7 +78,7 @@ def test_grow_child_half(window):
7878

7979

8080
def test_grow_child_full_width(window):
81-
subject = UIAnchorLayout(width=400, height=400)
81+
subject = UIAnchorLayout(width=400, height=400, size_hint=None)
8282
dummy = subject.add(UIDummy(width=100, height=100, size_hint=(1, 0.5)))
8383

8484
subject._do_layout()
@@ -88,7 +88,7 @@ def test_grow_child_full_width(window):
8888

8989

9090
def test_grow_child_full_height(window):
91-
subject = UIAnchorLayout(width=400, height=400)
91+
subject = UIAnchorLayout(width=400, height=400, size_hint=None)
9292
dummy = subject.add(UIDummy(width=100, height=100, size_hint=(0.5, 1)))
9393

9494
subject._do_layout()
@@ -98,7 +98,7 @@ def test_grow_child_full_height(window):
9898

9999

100100
def test_grow_child_to_max_size(window):
101-
subject = UIAnchorLayout(width=400, height=400)
101+
subject = UIAnchorLayout(width=400, height=400, size_hint=None)
102102
dummy = subject.add(UIDummy(width=100, height=100, size_hint=(1, 1), size_hint_max=(200, 150)))
103103

104104
subject._do_layout()
@@ -108,7 +108,7 @@ def test_grow_child_to_max_size(window):
108108

109109

110110
def test_shrink_child_to_min_size(window):
111-
subject = UIAnchorLayout(width=400, height=400)
111+
subject = UIAnchorLayout(width=400, height=400, size_hint=None)
112112
dummy = subject.add(
113113
UIDummy(width=100, height=100, size_hint=(0.1, 0.1), size_hint_min=(200, 150))
114114
)
@@ -121,7 +121,7 @@ def test_shrink_child_to_min_size(window):
121121

122122
def test_children_can_grow_out_of_bounce(window):
123123
"""This tests behavior, which is used for scrolling."""
124-
subject = UIAnchorLayout(width=400, height=400)
124+
subject = UIAnchorLayout(width=400, height=400, size_hint=None)
125125
dummy = subject.add(UIDummy(width=100, height=100, size_hint=(2, 2)))
126126

127127
subject._do_layout()
@@ -132,7 +132,7 @@ def test_children_can_grow_out_of_bounce(window):
132132

133133
def test_children_limited_to_layout_size_when_enforced(window):
134134
"""This tests behavior, which is used for scrolling."""
135-
subject = UIAnchorLayout(width=400, height=400)
135+
subject = UIAnchorLayout(width=400, height=400, size_hint=None)
136136
subject._restrict_child_size = True
137137
dummy = subject.add(UIDummy(width=100, height=100, size_hint=(2, 2)))
138138

@@ -143,7 +143,7 @@ def test_children_limited_to_layout_size_when_enforced(window):
143143

144144

145145
def test_only_adjust_size_if_size_hint_is_given_for_dimension(window):
146-
subject = UIAnchorLayout(width=400, height=400)
146+
subject = UIAnchorLayout(width=400, height=400, size_hint=None)
147147
dummy = subject.add(
148148
UIDummy(width=100, height=100, size_hint=(2, None), size_hint_min=(None, 200))
149149
)

0 commit comments

Comments
 (0)