Skip to content

Commit

Permalink
Merge pull request #2950 from HalfWhitt/fix-visibility
Browse files Browse the repository at this point in the history
Fix visibility for descendants of hidden widgets
  • Loading branch information
freakboy3742 authored Nov 18, 2024
2 parents 57698f0 + f8bf6c5 commit eea1b98
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 2 deletions.
1 change: 1 addition & 0 deletions changes/2950.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed a bug that allowed descendents of hidden widgets to be visible.
9 changes: 9 additions & 0 deletions core/src/toga/style/pack.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,15 @@ def apply(self, prop: str, value: object) -> None:
elif prop == "background_color":
self._applicator.set_background_color(value)
elif prop == "visibility":
if value == VISIBLE:
# If visibility is being set to VISIBLE, look up the chain to see if
# an ancestor is hidden.
widget = self._applicator.widget
while widget := widget.parent:
if widget.style._hidden:
value = HIDDEN
break

self._applicator.set_hidden(value == HIDDEN)
elif prop in (
"font_family",
Expand Down
48 changes: 47 additions & 1 deletion core/tests/style/pack/test_apply.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@
LEFT,
RIGHT,
RTL,
VISIBLE,
Pack,
)

from .utils import ExampleNode
from .utils import ExampleNode, ExampleParentNode


def test_set_default_right_textalign_when_rtl():
Expand Down Expand Up @@ -72,3 +73,48 @@ def test_set_visibility_hidden():
root = ExampleNode("app", style=Pack(visibility=HIDDEN))
root.style.reapply()
root._impl.set_hidden.assert_called_once_with(True)


def test_set_visibility_inherited():
"""Nodes should be hidden when an ancestor is hidden."""
grandparent = ExampleParentNode("grandparent", style=Pack())
parent = ExampleParentNode("parent", style=Pack())
child = ExampleNode("child", style=Pack())
grandparent.add(parent)
parent.add(child)

def assert_hidden_called(grandparent_value, parent_value, child_value):
for node, value in [
(grandparent, grandparent_value),
(parent, parent_value),
(child, child_value),
]:
if value is None:
node._impl.set_hidden.assert_not_called()
else:
node._impl.set_hidden.assert_called_once_with(value)

node._impl.set_hidden.reset_mock()

# Hiding grandparent should hide all.
grandparent.style.visibility = HIDDEN
assert_hidden_called(True, True, True)

# Just setting child or parent to VISIBLE won't trigger an apply, because that's
# their default value. So first, set them to hidden.
parent.style.visibility = HIDDEN
assert_hidden_called(None, True, True)

child.style.visibility = HIDDEN
assert_hidden_called(None, None, True)

# Then set them to visible. They should still not actually be shown.
parent.style.visibility = VISIBLE
assert_hidden_called(None, True, True)

child.style.visibility = VISIBLE
assert_hidden_called(None, None, True)

# Show grandparent again; the other two should reappear.
grandparent.style.visibility = VISIBLE
assert_hidden_called(False, False, False)
7 changes: 7 additions & 0 deletions core/tests/style/pack/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,13 @@ def refresh(self):
pass


class ExampleParentNode(ExampleNode):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

self._children = []


class ExampleViewport:
def __init__(self, width, height):
self.height = height
Expand Down
5 changes: 4 additions & 1 deletion testbed/tests/widgets/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,10 @@ async def test_visibility(widget, probe):
assert child_probe.is_hidden
assert grandchild_probe.is_hidden

# Mark the child style as visible.
# Mark the child style as visible. Mark it as hidden first, so that a change is
# registered and the property is applied. However, regardless, the widget
# won't be visible because it has an ancestor that is hidden.
child.style.visibility = HIDDEN
child.style.visibility = VISIBLE
await probe.redraw("Child style of widget should be visible")

Expand Down

0 comments on commit eea1b98

Please sign in to comment.