beeware / toga

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

Discrepancy in display of some widgets when given explicit height #1831

Open freakboy3742 opened 1 year ago

freakboy3742 commented 1 year ago

Describe the bug

There are some widgets whose native implementations technically allow for an explicit height to be set, even though that definition doesn't make sense in practice.

Steps to reproduce

Run the testbed/widgets/test_progressbar.py::test_flex_horizontal_widget_size testbed test. This test exercises various size constraints on the ProgressBar widget. One of those tests sets an explicit height=200.

Alternatively, run the progressbar example, but add an explicit style directive height=200 to any of the progress bars (the pbar_style definition ~L22 is a convenient place).

The correct behaviour is seen on Cocoa and GTK. The widget renders at the system default height.

However, on Winforms and iOS, the progress bar is drawn the full 200px height.

The Android behavior is a hybrid; running indeterminate progress bars appear taller, but not the full 200px allocation.

On all platforms, the alignment is arguable; according to Pack, the widget should be at the top of the box by default.

Expected behavior

The progress bar widget should render at the system default height, aligned to the top of a 200 pixel-tall box.

Screenshots

Cocoa (correct except for alignment):

Screenshot 2023-03-27 at 12 40 46 pm

Winforms (incorrect):

Screenshot 2023-03-27 at 12 40 22 pm

Android (partially correct):

Screenshot 2023-03-27 at 12 39 46 pm

Environment

Logs

No response

Additional context

I believe the root cause here is Pack's interpretation of intrinsic height.

At time of writing (mid development of #1825), Toga is correctly applying a fixed intrinsic height to widgets. However, Pack is then overriding this to set a fixed height allocation for the widget.

The behavior we're seeing then comes down to the platform. On Cocoa, setting the height of the progress bar makes Cocoa allocate a box the requested height, but the actual rendering is fixed to a specific height, centralised inside the available space. On Winforms, the rendering always uses the requested height, so the bar is drawn "fat"

However - there is an inherent conflict with the interpretation of a widget like Button. Button also has a fixed intrinsic height; but if an explicit height is given, there is a meaningful interpretation of this definition that works on all platforms. It doesn't make sense to make Button have flexible intrinsic height, as we don't want buttons to be tall by default in flexible layouts.

The only fix I can think of at present is to modify at_least() to have an autoexpand flag; this would allow us to define a button's height to be at_least(default_height, autoexpand=False), whereas most flexible widgets will use the default interpretation.

freakboy3742 commented 1 year ago

As an interesting data point - the default CSS behaviour maps very closely to what we're observing here - on Cocoa (Safari) and GTK, we get a thin bar, centered on the available vertical space; on Winforms and Chrome (which could be considered an analog of Android) we get a "fat" bar that fills a substantial amount of the available vertical space. So - there's an argument to be made that the rendering appearance is platform dependent, and as-expected; with a general note of usage being that explicitly setting the height of a progress bar is a bad idea.

freakboy3742 commented 1 year ago

Another possible fix - instead of modifying at_least, correct this at the level of set_bounds(). The current behaviour occurs because set_bounds() is setting the size of the bounding box for the widget; in the case of these "non-expanding" widgets, we should probably be setting the actual height to the default height of the widget, and adjusting the vertical offset to half the difference between the allocated height and the default height.