beeware / toga

A Python native, OS native GUI toolkit.
https://toga.readthedocs.io/en/latest/
BSD 3-Clause "New" or "Revised" License
4.29k stars 668 forks source link

Add visual layout debugging features #2856

Open hyuri opened 3 days ago

hyuri commented 3 days ago

What is the problem or limitation you are having?

Currently, we don't have an easy way to visualize and debut our layouts. So the only way to do that is by changing the background colors of different boxes, buttons and labels. That way we can visually see how the widgets are relating to each other. But it's manual, messy and tedious.

Describe the solution you'd like

Add a sort of debug "layout wireframes" property to App, that, when True, renders all widgets with a bounding box/border around them.

Going even further, another debug property can be added: "layout depth", that turns off all colors and instead assigns a different shade of gray to each widget based on its depth in the hierarchy. The main window black, with inner widgets in progressively lighter shades of gray, all the way to the top elements like buttons and labels.

So we would be able to easily visualize boxes inside of boxes and how they relate to each other.

That way, we have an easy way of visualizing what's going on in the layout and can spot bugs — especially some that are hard to catch unless you visualize how the widgets are glueing together.

Describe alternatives you've considered

None

Additional context

No response

freakboy3742 commented 2 days ago

There's definitely an idea in here worth pursuing. I agree that layout issues can be difficult to resolve, and I'll openly admit that manually setting background colors is my go-to technique for debugging layout issues (along with a couple of others).

The broader concept of "app debug mode" is also something that has come up on a couple of occasions. For example, having exceptions pop up as dialogs might be nice for production apps, but that would be a bit distracting when doing app development.

Some implementation questions:

  1. How do we turn on debug mode? A TOGA_DEBUG environment variable (or similar) would be the obvious choice, but I'm open to other suggestions. Would debug mode automatically turn on layout helpers, or just enable a "develop" menu that exposes options like toggling layout helpers? Or would TOGA_DEBUG_LAYOUT be a separate setting?

  2. What would the exact manifestation of layout backgrounds be? I'm not sure borders would be a good idea, as they could easily alter layout (pushing everything by a pixel); colors introduce an accessibility angle - subtle shades of grey in particular would be indistinguishable by my son, for example. I'd suggest cycling between 3-4 colors, specifically selected from a colorblind-aware palette - https://davidmathlogic.com/colorblind/ has a couple of candidate palettes (although we possibly want to use lightened variants, rather than strong bold backgrounds).

  3. How would "debug" backgrounds interact with explicitly specified background colors? An override? A color mix?

  4. Would layout helper background colors apply to all widgets? Or just widgets where physical extents aren't necessarily obvious (Box obviously, but others like TextInput and Switch would also be candidates)

hyuri commented 2 days ago

The broader concept of "app debug mode" is also something that has come up on a couple of occasions. For example, having exceptions pop up as dialogs might be nice for production apps, but that would be a bit distracting when doing app development.

These exception dialogs for production would be a great addition indeed.

How do we turn on debug mode? A TOGA_DEBUG environment variable (or similar) would be the obvious choice, but I'm open to other suggestions.

The environment variable sounds reasonable. Does a --toga_debug flag to briefcase dev also make sense?

Would debug mode automatically turn on layout helpers, or just enable a "develop" menu that exposes options like toggling layout helpers? Or would TOGA_DEBUG_LAYOUT be a separate setting?

To me, it makes sense to have both settings.

What would the exact manifestation of layout backgrounds be? I'm not sure borders would be a good idea, as they could easily alter layout (pushing everything by a pixel); colors introduce an accessibility angle - subtle shades of grey in particular would be indistinguishable by my son, for example. I'd suggest cycling between 3-4 colors, specifically selected from a colorblind-aware palette - https://davidmathlogic.com/colorblind/ has a couple of candidate palettes (although we possibly want to use lightened variants, rather than strong bold backgrounds).

On borders: When this "debug mode" is active, could widget sizes be reduced by the border size, to compensate for it — so reduce all widgets by 1 pixel if border is 1 px? Or have the border be inwards instead of outwards — in other words: be drawn on top of the widget's outer-most pixels instead of after it?

On colors: Good call. I'm not knowledgeable on this and I don't want to offer mistaken suggestions, so maybe you are better positioned to decide on what the rights colors should be and how they should be used. Would the different shades work in addition to the colors, or should shades be avoided altogether?

How would "debug" backgrounds interact with explicitly specified background colors? An override? A color mix?

How about it overrides all colors by default, but we have a "debug_skip_color" property in Widget to tell the debug mode to not override the colors in that specific widget? Like !important in CSS.

Would layout helper background colors apply to all widgets? Or just widgets where physical extents aren't necessarily obvious (Box obviously, but others like TextInput and Switch would also be candidates)

My intuition says all widgets by default. Could we have an option for that? Like "TOGA_DEBUG_CONTAINERS_ONLY"?.

freakboy3742 commented 2 days ago

The broader concept of "app debug mode" is also something that has come up on a couple of occasions. For example, having exceptions pop up as dialogs might be nice for production apps, but that would be a bit distracting when doing app development.

These exception dialogs for production would be a great addition indeed.

To be clear - Briefcase already does have crash dialogs, but only for exceptions that are true app-crashers. Toga will go to great lengths to not crash, and output to console instead - it's these outputs that I'm suggesting as possible dialogs in non-debug mode.

How do we turn on debug mode? A TOGA_DEBUG environment variable (or similar) would be the obvious choice, but I'm open to other suggestions.

The environment variable sounds reasonable. Does a --toga_debug flag to briefcase dev also make sense?

I'd rather not add Toga specific flags to Briefcase - while the two tools work well together, we're deliberately trying to keep them decoupled. However, a generic --debug/DEBUG flag/envvar might be acceptable.,

Would debug mode automatically turn on layout helpers, or just enable a "develop" menu that exposes options like toggling layout helpers? Or would TOGA_DEBUG_LAYOUT be a separate setting?

To me, it makes sense to have both settings.

The more I think about it, I think a "debug" menu makes more sense. Having layout debug permanently on is going to be a distraction; being able to toggle it is going to be useful. I can also think of any number of things that we might want to include as debug utilities, like a widget tree inspector, or maybe even a performance monitor. Having a menu item that is added in case of debug makes all these possible, rather than an explosion of environment variables.

What would the exact manifestation of layout backgrounds be? I'm not sure borders would be a good idea, as they could easily alter layout (pushing everything by a pixel); colors introduce an accessibility angle - subtle shades of grey in particular would be indistinguishable by my son, for example. I'd suggest cycling between 3-4 colors, specifically selected from a colorblind-aware palette - https://davidmathlogic.com/colorblind/ has a couple of candidate palettes (although we possibly want to use lightened variants, rather than strong bold backgrounds).

On borders: When this "debug mode" is active, could widget sizes be reduced by the border size, to compensate for it — so reduce all widgets by 1 pixel if border is 1 px? Or have the border be inwards instead of outwards — in other words: be drawn on top of the widget's outer-most pixels instead of after it?

Oh sure - could be... but then we've got to debug our debug code... and that's complexity I'd rather avoid.

On colors: Good call. I'm not knowledgeable on this and I don't want to offer mistaken suggestions, so maybe you are better positioned to decide on what the rights colors should be and how they should be used. Would the different shades work in addition to the colors, or should shades be avoided altogether?

The biggest concern from an accessibility standpoint is that telling 2 subtly different shades of yellow apart is hard even if you do have good eyesight. I think the best we're going to be able to do is make the colors as distinct as possible, and cycle as often as possible. We might need to make the periodicity of colors cycline configurable to avoid issues where the exact depth of a widget heirarchy matches the color cycle, obscuring the diagnostic assistance. Which... is another argument for a debug menu, as we could control the cycle size there.

How would "debug" backgrounds interact with explicitly specified background colors? An override? A color mix?

How about it overrides all colors by default, but we have a "debug_skip_color" property in Widget to tell the debug mode to not override the colors in that specific widget? Like !important in CSS.

I'm not wild about the prospect of writing user-space code that has to account for debug properties.

Would layout helper background colors apply to all widgets? Or just widgets where physical extents aren't necessarily obvious (Box obviously, but others like TextInput and Switch would also be candidates)

My intuition says all widgets by default. Could we have an option for that? Like "TOGA_DEBUG_CONTAINERS_ONLY"?.

The problem here is that some widgets (e.g., Table) use background color for a purpose that won't shed any light on layout problems. Table will always fill the allocated space, in both axes; and the boundaries of the widget is obvious. It's only the box/label/switch style widgets where there's ambiguity.

hyuri commented 1 day ago

The more I think about it, I think a "debug" menu makes more sense. Having layout debug permanently on is going to be a distraction; being able to toggle it is going to be useful. I can also think of any number of things that we might want to include as debug utilities, like a widget tree inspector, or maybe even a performance monitor. Having a menu item that is added in case of debug makes all these possible, rather than an explosion of environment variables.

Great. But, to be clear, would the "debug layout" toggle, accessible in this debug menu, preserve state between runs until we flick it again? When debugging the layout, we might go back and forth multiple times between tweaking the code and running to see results, so having to access the menu and toggle it on every time will probably quickly get annoying.

The biggest concern from an accessibility standpoint is that telling 2 subtly different shades of yellow apart is hard even if you do have good eyesight. I think the best we're going to be able to do is make the colors as distinct as possible, and cycle as often as possible. We might need to make the periodicity of colors cycline configurable to avoid issues where the exact depth of a widget heirarchy matches the color cycle, obscuring the diagnostic assistance. Which... is another argument for a debug menu, as we could control the cycle size there.

Got it. Extra: What if the colors are configurable as well? Or maybe only a handful of presets, like "full spectrum/rainbow", "gray scale", "green-red", "yellow-blue"? Or other presets that better serve different people with different needs, as it seems there are different cases with different sensitivities.

The problem here is that some widgets (e.g., Table) use background color for a purpose that won't shed any light on layout problems. Table will always fill the allocated space, in both axes; and the boundaries of the widget is obvious. It's only the box/label/switch style widgets where there's ambiguity.

Right. So maybe only things like boxes, labels, buttons and switches — at least as a first step?

hyuri commented 1 day ago

Another idea: Overlay diagonal lines in padding areas, so we can visualize padding as well.

freakboy3742 commented 5 hours ago

Great. But, to be clear, would the "debug layout" toggle, accessible in this debug menu, preserve state between runs until we flick it again? When debugging the layout, we might go back and forth multiple times between tweaking the code and running to see results, so having to access the menu and toggle it on every time will probably quickly get annoying.

I hadn't considered whether the option would be persistent... I guess it could be, though. There's an interesting overlap here with #90 - that feature request requires a generic mechanism for storing user settings; and the enable/disable state of layout debug would probably fit into that territory.

It would also benefit from the addition of menu items that support toggle behaviours (i.e., the 'tick' marker next to the menu item indicating something is enabled).

The biggest concern from an accessibility standpoint is that telling 2 subtly different shades of yellow apart is hard even if you do have good eyesight. I think the best we're going to be able to do is make the colors as distinct as possible, and cycle as often as possible. We might need to make the periodicity of colors cycline configurable to avoid issues where the exact depth of a widget heirarchy matches the color cycle, obscuring the diagnostic assistance. Which... is another argument for a debug menu, as we could control the cycle size there.

Got it. Extra: What if the colors are configurable as well? Or maybe only a handful of presets, like "full spectrum/rainbow", "gray scale", "green-red", "yellow-blue"? Or other presets that better serve different people with different needs, as it seems there are different cases with different sensitivities.

It's not that difficult to pick a palette that is differentiable by all forms of color blindness. It takes some care, to be sure - but it's not impossible.

Another idea: Overlay diagonal lines in padding areas, so we can visualize padding as well.

At that point, we're getting into the territory where the complexity of implementation should be starting to become a concern. If there's a simple way to do it... maybe - but carrying around a bunch of heavyweight code for debugging purposes isn't especially desirable.