-
-
Notifications
You must be signed in to change notification settings - Fork 690
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
add a layout debug mode that sets background colors #2953
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +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. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,8 @@ | ||
from __future__ import annotations | ||
|
||
from builtins import id as identifier | ||
from os import environ | ||
from random import shuffle | ||
from typing import TYPE_CHECKING, Any, TypeVar | ||
from warnings import warn | ||
|
||
|
@@ -17,10 +19,29 @@ | |
StyleT = TypeVar("StyleT", bound=BaseStyle) | ||
|
||
|
||
# based on colors from https://davidmathlogic.com/colorblind | ||
debug_background_palette = [ | ||
"#d0e2ed", # very light blue | ||
"#b8d2e9", # light blue | ||
"#f8ccb0", # light orange | ||
"#f6d3be", # soft orange | ||
"#c7e7b2", # light green | ||
"#f0b2d6", # light pink | ||
"#e5dab0", # light yellow | ||
"#d5c2ea", # light lavender | ||
"#b2e4e5", # light teal | ||
"#e5e4af", # light cream | ||
"#bde2dc", # soft turquoise | ||
] | ||
shuffle(debug_background_palette) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why programmatically shuffle? We definitely need an order which cycles good contrast (i.e., we don't have "light blue" next to "very light blue"), but that order doesn't need to be random. Plus, randomisation takes more time at startup, and a randomised order could be jarring for as successive runs won't give the same output. |
||
|
||
|
||
class Widget(Node): | ||
_MIN_WIDTH = 100 | ||
_MIN_HEIGHT = 100 | ||
|
||
_debug_color_index = 0 | ||
|
||
def __init__( | ||
self, | ||
id: str | None = None, | ||
|
@@ -34,6 +55,18 @@ def __init__( | |
:param style: A style object. If no style is provided, a default style | ||
will be applied to the widget. | ||
""" | ||
# If the object has _USE_DEBUG_BACKGROUND=True and layout debug mode | ||
# is on, change bg color.BufferError | ||
if getattr(self, "_USE_DEBUG_BACKGROUND", False): | ||
if environ.get("TOGA_DEBUG_LAYOUT") == "1": | ||
Widget._debug_color_index += 1 | ||
style = style if style else Pack() | ||
style.background_color = debug_background_palette[ | ||
Widget._debug_color_index % len(debug_background_palette) | ||
] | ||
else: | ||
self._USE_DEBUG_BACKGROUND = False | ||
|
||
super().__init__(style=style if style is not None else Pack()) | ||
|
||
self._id = str(id if id else identifier(self)) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We try to keep the test structure matching the widget layout; or, if things are getting complex, matching the feature by the name used in the code. In this case, if there's too many tests to add to |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,76 @@ | ||||||||||||||||||||
from pytest import MonkeyPatch | ||||||||||||||||||||
from travertino.colors import color | ||||||||||||||||||||
|
||||||||||||||||||||
import toga | ||||||||||||||||||||
|
||||||||||||||||||||
|
||||||||||||||||||||
# test that a container-like widget in normal mode has a default background | ||||||||||||||||||||
def test_box_no_background_in_normal_mode(): | ||||||||||||||||||||
"""A Box has no default background.""" | ||||||||||||||||||||
# Disable layout debug mode | ||||||||||||||||||||
with MonkeyPatch.context() as mp: | ||||||||||||||||||||
mp.setenv("TOGA_DEBUG_LAYOUT", "0") | ||||||||||||||||||||
Comment on lines
+8
to
+12
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Monkeypatch can be used as a fixture:
Suggested change
|
||||||||||||||||||||
box = toga.Box() | ||||||||||||||||||||
# assert that the bg is default | ||||||||||||||||||||
assert hasattr(box.style, "background_color") | ||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This assertion essentially can't fail. Toga's style framework will ensure that |
||||||||||||||||||||
assert not box.style.background_color | ||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A check of an actual value, rather than "falseness" would be preferable here - that avoids any odd situations where something like |
||||||||||||||||||||
|
||||||||||||||||||||
|
||||||||||||||||||||
# test that a non-container-like widget in layout debug mode has a default background | ||||||||||||||||||||
def test_button_debug_background(): | ||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good idea testing a widget other than Box; - but also worth adding at test for (a) a container widget, and (b) a widget where the debug background won't be applied. |
||||||||||||||||||||
"""A Button in layout debug mode has a default background.""" | ||||||||||||||||||||
# Enable layout debug mode | ||||||||||||||||||||
with MonkeyPatch.context() as mp: | ||||||||||||||||||||
mp.setenv("TOGA_DEBUG_LAYOUT", "1") | ||||||||||||||||||||
button = toga.Button() | ||||||||||||||||||||
# assert that the bg is default | ||||||||||||||||||||
assert hasattr(button.style, "background_color") | ||||||||||||||||||||
assert not button.style.background_color | ||||||||||||||||||||
Comment on lines
+27
to
+28
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As above:
The same pattern applies to the subseqeunt tests as well. |
||||||||||||||||||||
|
||||||||||||||||||||
|
||||||||||||||||||||
# test that a label in layout debug mode has a default background | ||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why test label and button? Or, if it's important to check every widget (arguable, it is), is there any reason we shouldn't encode this as a test in |
||||||||||||||||||||
def test_label_no_debug_background(): | ||||||||||||||||||||
"""A Label in layout debug mode has a default background.""" | ||||||||||||||||||||
# Enable layout debug mode | ||||||||||||||||||||
with MonkeyPatch.context() as mp: | ||||||||||||||||||||
mp.setenv("TOGA_DEBUG_LAYOUT", "1") | ||||||||||||||||||||
label = toga.Label("label") | ||||||||||||||||||||
# assert that the bg is default | ||||||||||||||||||||
assert hasattr(label.style, "background_color") | ||||||||||||||||||||
assert not label.style.background_color | ||||||||||||||||||||
|
||||||||||||||||||||
|
||||||||||||||||||||
# test that a container-like widget in layout debug mode has a non-default background | ||||||||||||||||||||
# that matches the expected debug_background_palette | ||||||||||||||||||||
def test_box_debug_backgrounds(): | ||||||||||||||||||||
"""A Box in layout debug mode has a non-default background.""" | ||||||||||||||||||||
# Enable layout debug mode | ||||||||||||||||||||
with MonkeyPatch.context() as mp: | ||||||||||||||||||||
mp.setenv("TOGA_DEBUG_LAYOUT", "1") | ||||||||||||||||||||
|
||||||||||||||||||||
boxes = [] | ||||||||||||||||||||
debug_bg_palette_length = len(toga.widgets.base.debug_background_palette) | ||||||||||||||||||||
# need enough for coverage of debug_background_palette array index rollover | ||||||||||||||||||||
for i in range(debug_bg_palette_length + 3): | ||||||||||||||||||||
boxes.append(toga.Box()) | ||||||||||||||||||||
|
||||||||||||||||||||
for counter, box in enumerate(boxes, start=1): | ||||||||||||||||||||
index = counter % debug_bg_palette_length | ||||||||||||||||||||
print(counter) | ||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||
assert hasattr(box.style, "background_color") | ||||||||||||||||||||
assert box.style.background_color == color( | ||||||||||||||||||||
toga.widgets.base.debug_background_palette[index] | ||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unless I'm missing something, this test doesn't account for the state of This means either the test needs to monkeypatch |
||||||||||||||||||||
) | ||||||||||||||||||||
|
||||||||||||||||||||
|
||||||||||||||||||||
# test that a scroll container widget in layout debug mode doesn't have | ||||||||||||||||||||
# a default background | ||||||||||||||||||||
def test_scroll_container_debug_background(): | ||||||||||||||||||||
"""A container widget has a default background.""" | ||||||||||||||||||||
# Disable layout debug mode | ||||||||||||||||||||
with MonkeyPatch.context() as mp: | ||||||||||||||||||||
mp.setenv("TOGA_DEBUG_LAYOUT", "1") | ||||||||||||||||||||
sc = toga.ScrollContainer() | ||||||||||||||||||||
# assert that the bg is not default | ||||||||||||||||||||
assert hasattr(sc.style, "background_color") | ||||||||||||||||||||
assert sc.style.background_color != color("white") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this is a list of constants, it should be treated as a constant by naming convention: