diff --git a/CHANGELOG.md b/CHANGELOG.md index 97d8ef61b5..72a58dab7b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ Arcade [PyPi Release History](https://pypi.org/project/arcade/#history) page. - Fix a bug, where the caret of UIInputText was misplaced after resizing the widget - Use incremental layout for UIScrollArea to improve performance of changing text - Refactored and improved focus handling + - UIBoxLayout ignores widgets with `visible=None` ## 3.3.2 diff --git a/arcade/context.py b/arcade/context.py index b727fa1795..52e0cedae5 100644 --- a/arcade/context.py +++ b/arcade/context.py @@ -330,8 +330,8 @@ def bind_window_block(self) -> None: gl.GL_UNIFORM_BUFFER, 0, self._window_block.buffer.id, - 0, - 128, # 32 x 32bit floats (two mat4) + 0, # type: ignore + 128, # 32 x 32bit floats (two mat4) # type: ignore ) @property diff --git a/arcade/gui/widgets/__init__.py b/arcade/gui/widgets/__init__.py index 734b3e43e0..f275509e22 100644 --- a/arcade/gui/widgets/__init__.py +++ b/arcade/gui/widgets/__init__.py @@ -127,7 +127,9 @@ class UIWidget(EventDispatcher, ABC): """A weak reference to the parent UIManager or UIWidget, which does not prevent garbage collection of the parent.""" rect = Property(LBWH(0, 0, 1, 1)) - visible = Property(True) + visible = Property[bool | None](True) + """If True, the widget is visible and will be rendered. If None, + the widget should also be skipped by layouts.""" focused = Property(False) focus_mode: FocusMode = FocusMode.NONE diff --git a/arcade/gui/widgets/layout.py b/arcade/gui/widgets/layout.py index 410f85159c..bdf2512178 100644 --- a/arcade/gui/widgets/layout.py +++ b/arcade/gui/widgets/layout.py @@ -315,7 +315,9 @@ def _update_size_hints(self): self._size_hint_requires_update = False required_space_between = max(0, len(self.children) - 1) * self._space_between - min_child_sizes = [UILayout.min_size_of(child) for child in self.children] + min_child_sizes = [ + UILayout.min_size_of(child) for child in self.children if child.visible is not None + ] if len(self.children) == 0: width = 0 @@ -360,10 +362,14 @@ def do_layout(self): if not self.children: return + children_to_render = [ + (child, data) for child, data in self._children if child.visible is not None + ] + # main axis constraints = [ _C.from_widget_height(child) if self.vertical else _C.from_widget_width(child) - for child, _ in self._children + for child, _ in children_to_render ] available_space = ( @@ -374,14 +380,14 @@ def do_layout(self): # orthogonal axis constraints = [ _C.from_widget_width(child) if self.vertical else _C.from_widget_height(child) - for child, _ in self._children + for child, _ in children_to_render ] orthogonal_sizes = _box_orthogonal_algorithm( constraints, self.content_width if self.vertical else self.content_height ) for (child, data), main_size, ortho_size in zip( - self._children, main_sizes, orthogonal_sizes + children_to_render, main_sizes, orthogonal_sizes ): # apply calculated sizes, condition regarding existing size_hint # are already covered in calculation input diff --git a/tests/unit/gui/test_layouting_boxlayout.py b/tests/unit/gui/test_layouting_boxlayout.py index 20aadc3d24..894d24155e 100644 --- a/tests/unit/gui/test_layouting_boxlayout.py +++ b/tests/unit/gui/test_layouting_boxlayout.py @@ -128,6 +128,28 @@ def test_do_layout_vertical_space_between(window): assert element_2.top == 300 +def test_do_layout_ignores_child_with_visible_None(window): + # add two 100x100 Dummy widgets + element_1 = UIDummy() + element_1.visible = None + element_2 = UIDummy() + + group = UIBoxLayout(vertical=True, children=[element_1, element_2]) + assert group.size_hint_min == (100, 100) + group.rect = LBWH(100, 200, 100, 100) + + group._do_layout() + + # element 1 in initial position + assert element_1.bottom == 0 + assert element_1.left == 0 + + # element 2 fills the group + assert element_2.top == 300 + assert element_2.bottom == 200 + assert element_2.left == 100 + + # Horizontal def test_do_layout_horizontal_with_initial_children(window): # add two 100x100 Dummy widgets