Repository: beeware/toga

Issue:
ImageView doesn't shrink images to fit
### Describe the bug

The documentation for ImageView says that a `flex=1` image will be expanded or shrunk to fit the available space. It definitely expands images to fit, but a large image will not be *shrunk* to fit.

### Steps to reproduce

Use the following app code:
```py
        main_box = toga.Box(style=Pack(direction=ROW))

        main_box.add(toga.ImageView("image.png", style=Pack(flex=1)))

        self.main_window = toga.MainWindow(title=self.formal_name)
        self.main_window.content = main_box
        self.main_window.show()
```

if image.png is smaller than the window, it will be expanded; if image.py is larger than the window, the window will be expanded to fit the natural size of the window (or, on mobile, show "content doesn't fit window" warnings).


### Expected behavior

An ImageView with `flex=1` should shrink large images to fit the available space, preserving aspect ratio.

### Screenshots

_No response_

### Environment

- Operating System: all (tested on macOS and iOS)
- Python version: All
- Software versions:
  - Toga: 0.4.0


### Logs

_No response_

### Additional context

The underlying problem appears to be that the intrinsic constraint has been set to `at_least(size)` in each axis, rather than `at_least(0)`. This enforces a minimum size that can't be shrunk.


Discussion:

Resolving patch:
diff --git a/core/src/toga/widgets/imageview.py b/core/src/toga/widgets/imageview.py
--- a/core/src/toga/widgets/imageview.py
+++ b/core/src/toga/widgets/imageview.py
@@ -42,22 +42,22 @@ def rehint_imageview(image, style, scale=1):
             width = int(style.width * scale)
             height = int(style.width * scale / aspect_ratio)
             if style.flex:
-                height = at_least(height)
+                height = at_least(0)
         elif style.height != NONE:
             # Explicit height, implicit width. Preserve aspect ratio.
             aspect_ratio = image.width / image.height
             width = int(style.height * scale * aspect_ratio)
             height = int(style.height * scale)
             if style.flex:
-                width = at_least(width)
+                width = at_least(0)
         else:
             # Use the image's actual size.
             aspect_ratio = image.width / image.height
             width = int(image.width * scale)
             height = int(image.height * scale)
             if style.flex:
-                width = at_least(width)
-                height = at_least(height)
+                width = at_least(0)
+                height = at_least(0)
     else:
         # No image. Hinted size is 0.
         width = 0


Test patch:
diff --git a/core/tests/widgets/test_imageview.py b/core/tests/widgets/test_imageview.py
--- a/core/tests/widgets/test_imageview.py
+++ b/core/tests/widgets/test_imageview.py
@@ -138,14 +138,14 @@ def test_set_image_none(app):
         (dict(style=Pack(width=37, height=42)), 37, 42, None),
         (dict(style=Pack(width=37, height=42), scale=2), 74, 84, None),
         # Intrinsic image size, flex widget
-        (dict(style=Pack(flex=1)), at_least(144), at_least(72), 2),
-        (dict(style=Pack(flex=1), scale=2), at_least(288), at_least(144), 2),
+        (dict(style=Pack(flex=1)), at_least(0), at_least(0), 2),
+        (dict(style=Pack(flex=1), scale=2), at_least(0), at_least(0), 2),
         # Fixed width, flex widget
-        (dict(style=Pack(width=150, flex=1)), 150, at_least(75), 2),
-        (dict(style=Pack(width=150, flex=1), scale=2), 300, at_least(150), 2),
+        (dict(style=Pack(width=150, flex=1)), 150, at_least(0), 2),
+        (dict(style=Pack(width=150, flex=1), scale=2), 300, at_least(0), 2),
         # Fixed height, flex widget
-        (dict(style=Pack(height=80, flex=1)), at_least(160), 80, 2),
-        (dict(style=Pack(height=80, flex=1), scale=2), at_least(320), 160, 2),
+        (dict(style=Pack(height=80, flex=1)), at_least(0), 80, 2),
+        (dict(style=Pack(height=80, flex=1), scale=2), at_least(0), 160, 2),
         # Explicit image size, flex widget
         (dict(style=Pack(width=37, height=42, flex=1)), 37, 42, None),
         (dict(style=Pack(width=37, height=42, flex=1), scale=2), 74, 84, None),