Skip to content

Commit 02f3f87

Browse files
committed
port changes from #2953
1 parent df65161 commit 02f3f87

File tree

7 files changed

+118
-0
lines changed

7 files changed

+118
-0
lines changed

changes/2953.feature.rst

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Toga now has a layout debugging mode. If you set ``TOGA_DEBUG_LAYOUT=1`` in your app's runtime environment, widgets will be rendered with different background colors, making it easier to identify how space is being allocated by Toga's layout algorithm.

core/src/toga/widgets/base.py

+33
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
from __future__ import annotations
22

33
from builtins import id as identifier
4+
from os import environ
5+
from random import shuffle
46
from typing import TYPE_CHECKING, Any, TypeVar
57
from warnings import warn
68

@@ -17,10 +19,29 @@
1719
StyleT = TypeVar("StyleT", bound=BaseStyle)
1820

1921

22+
# based on colors from https://davidmathlogic.com/colorblind
23+
debug_background_palette = [
24+
"#d0e2ed", # very light blue
25+
"#b8d2e9", # light blue
26+
"#f8ccb0", # light orange
27+
"#f6d3be", # soft orange
28+
"#c7e7b2", # light green
29+
"#f0b2d6", # light pink
30+
"#e5dab0", # light yellow
31+
"#d5c2ea", # light lavender
32+
"#b2e4e5", # light teal
33+
"#e5e4af", # light cream
34+
"#bde2dc", # soft turquoise
35+
]
36+
shuffle(debug_background_palette)
37+
38+
2039
class Widget(Node):
2140
_MIN_WIDTH = 100
2241
_MIN_HEIGHT = 100
2342

43+
_debug_color_index = 0
44+
2445
def __init__(
2546
self,
2647
id: str | None = None,
@@ -34,6 +55,18 @@ def __init__(
3455
:param style: A style object. If no style is provided, a default style
3556
will be applied to the widget.
3657
"""
58+
# If the object has _USE_DEBUG_BACKGROUND=True and layout debug mode
59+
# is on, change bg color.BufferError
60+
if getattr(self, "_USE_DEBUG_BACKGROUND", False):
61+
if environ.get("TOGA_DEBUG_LAYOUT") == "1":
62+
Widget._debug_color_index += 1
63+
style = style if style else Pack()
64+
style.background_color = debug_background_palette[
65+
Widget._debug_color_index % len(debug_background_palette)
66+
]
67+
else:
68+
self._USE_DEBUG_BACKGROUND = False
69+
3770
super().__init__(style=style if style is not None else Pack())
3871

3972
self._id = str(id if id else identifier(self))

core/src/toga/widgets/box.py

+2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ def __init__(
2222
will be applied to the widget.
2323
:param children: An optional list of children for to add to the Box.
2424
"""
25+
# enable the debug background functionality
26+
self._USE_DEBUG_BACKGROUND = True
2527
super().__init__(id=id, style=style)
2628

2729
# Children need to be added *after* the impl has been created.

core/src/toga/widgets/optioncontainer.py

+2
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,8 @@ def __init__(
393393
<OptionContainerContentT>` to display in the OptionContainer.
394394
:param on_select: Initial :any:`on_select` handler.
395395
"""
396+
# enable the debug background functionality
397+
self._USE_DEBUG_BACKGROUND = True
396398
super().__init__(id=id, style=style)
397399
self._content = OptionList(self)
398400
self.on_select = None

core/src/toga/widgets/scrollcontainer.py

+2
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ def __init__(
4040
:param on_scroll: Initial :any:`on_scroll` handler.
4141
:param content: The content to display in the scroll window.
4242
"""
43+
# enable the debug background functionality
44+
self._USE_DEBUG_BACKGROUND = True
4345
super().__init__(id=id, style=style)
4446

4547
self._content: Widget | None = None

core/src/toga/widgets/splitcontainer.py

+2
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ def __init__(
4242
:param content: Initial :any:`SplitContainer content <SplitContainerContentT>`
4343
of the container. Defaults to both panels being empty.
4444
"""
45+
# enable the debug background functionality
46+
self._USE_DEBUG_BACKGROUND = True
4547
super().__init__(id=id, style=style)
4648
self._content: list[SplitContainerContentT] = [None, None]
4749

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
from pytest import MonkeyPatch
2+
from travertino.colors import color
3+
4+
import toga
5+
6+
7+
# test that a container-like widget in normal mode has a default background
8+
def test_box_no_background_in_normal_mode():
9+
"""A Box has no default background."""
10+
# Disable layout debug mode
11+
with MonkeyPatch.context() as mp:
12+
mp.setenv("TOGA_DEBUG_LAYOUT", "0")
13+
box = toga.Box()
14+
# assert that the bg is default
15+
assert hasattr(box.style, "background_color")
16+
assert not box.style.background_color
17+
18+
19+
# test that a non-container-like widget in layout debug mode has a default background
20+
def test_button_debug_background():
21+
"""A Button in layout debug mode has a default background."""
22+
# Enable layout debug mode
23+
with MonkeyPatch.context() as mp:
24+
mp.setenv("TOGA_DEBUG_LAYOUT", "1")
25+
button = toga.Button()
26+
# assert that the bg is default
27+
assert hasattr(button.style, "background_color")
28+
assert not button.style.background_color
29+
30+
31+
# test that a label in layout debug mode has a default background
32+
def test_label_no_debug_background():
33+
"""A Label in layout debug mode has a default background."""
34+
# Enable layout debug mode
35+
with MonkeyPatch.context() as mp:
36+
mp.setenv("TOGA_DEBUG_LAYOUT", "1")
37+
label = toga.Label("label")
38+
# assert that the bg is default
39+
assert hasattr(label.style, "background_color")
40+
assert not label.style.background_color
41+
42+
43+
# test that a container-like widget in layout debug mode has a non-default background
44+
# that matches the expected debug_background_palette
45+
def test_box_debug_backgrounds():
46+
"""A Box in layout debug mode has a non-default background."""
47+
# Enable layout debug mode
48+
with MonkeyPatch.context() as mp:
49+
mp.setenv("TOGA_DEBUG_LAYOUT", "1")
50+
51+
boxes = []
52+
debug_bg_palette_length = len(toga.widgets.base.debug_background_palette)
53+
# need enough for coverage of debug_background_palette array index rollover
54+
for i in range(debug_bg_palette_length + 3):
55+
boxes.append(toga.Box())
56+
57+
for counter, box in enumerate(boxes, start=1):
58+
index = counter % debug_bg_palette_length
59+
print(counter)
60+
assert hasattr(box.style, "background_color")
61+
assert box.style.background_color == color(
62+
toga.widgets.base.debug_background_palette[index]
63+
)
64+
65+
66+
# test that a scroll container widget in layout debug mode doesn't have
67+
# a default background
68+
def test_scroll_container_debug_background():
69+
"""A container widget has a default background."""
70+
# Disable layout debug mode
71+
with MonkeyPatch.context() as mp:
72+
mp.setenv("TOGA_DEBUG_LAYOUT", "1")
73+
sc = toga.ScrollContainer()
74+
# assert that the bg is not default
75+
assert hasattr(sc.style, "background_color")
76+
assert sc.style.background_color != color("white")

0 commit comments

Comments
 (0)