gpui: Fix img element to auto size when only have width or height (#17994)

Jason Lee created

Release Notes:

- N/A

---

We may only want to set the height of an image to limit the size and
make the width adaptive.

In HTML, we will only set width or height, and the other side will adapt
and maintain the original image ratio.

I changed this because I had a logo image that only to be limited in
height, and then I found that setting the height of the `img` alone
would not display correctly.

I also tried to set `ObjectFit` in this Demo, but it seems that none of
them can achieve the same effect as "After".

## Before
<img width="809" alt="before 2024-09-18 164029"
src="https://github.com/user-attachments/assets/7ba559ed-e53b-43e6-a072-93c8ba5b14ee">

## After
<img width="749" alt="after 2024-09-18 172003"
src="https://github.com/user-attachments/assets/51ee2eba-76b3-400a-abbf-de0e9c4021e2">

Change summary

crates/gpui/examples/image/image.rs | 54 ++++++++++++++++++++++--------
crates/gpui/src/elements/img.rs     | 36 ++++++++++++++------
2 files changed, 65 insertions(+), 25 deletions(-)

Detailed changes

crates/gpui/examples/image/image.rs 🔗

@@ -69,25 +69,51 @@ struct ImageShowcase {
 impl Render for ImageShowcase {
     fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
         div()
-            .flex()
-            .flex_row()
             .size_full()
+            .flex()
+            .flex_col()
             .justify_center()
             .items_center()
             .gap_8()
             .bg(rgb(0xFFFFFF))
-            .child(ImageContainer::new(
-                "Image loaded from a local file",
-                self.local_resource.clone(),
-            ))
-            .child(ImageContainer::new(
-                "Image loaded from a remote resource",
-                self.remote_resource.clone(),
-            ))
-            .child(ImageContainer::new(
-                "Image loaded from an asset",
-                self.asset_resource.clone(),
-            ))
+            .child(
+                div()
+                    .flex()
+                    .flex_row()
+                    .justify_center()
+                    .items_center()
+                    .gap_8()
+                    .child(ImageContainer::new(
+                        "Image loaded from a local file",
+                        self.local_resource.clone(),
+                    ))
+                    .child(ImageContainer::new(
+                        "Image loaded from a remote resource",
+                        self.remote_resource.clone(),
+                    ))
+                    .child(ImageContainer::new(
+                        "Image loaded from an asset",
+                        self.asset_resource.clone(),
+                    )),
+            )
+            .child(
+                div()
+                    .flex()
+                    .flex_row()
+                    .gap_8()
+                    .child(
+                        div()
+                            .flex_col()
+                            .child("Auto Width")
+                            .child(img("https://picsum.photos/800/400").h(px(180.))),
+                    )
+                    .child(
+                        div()
+                            .flex_col()
+                            .child("Auto Height")
+                            .child(img("https://picsum.photos/480/640").w(px(180.))),
+                    ),
+            )
     }
 }
 

crates/gpui/src/elements/img.rs 🔗

@@ -1,7 +1,7 @@
 use crate::{
     px, AbsoluteLength, AppContext, Asset, Bounds, DefiniteLength, Element, ElementId,
     GlobalElementId, Hitbox, Image, InteractiveElement, Interactivity, IntoElement, LayoutId,
-    Length, ObjectFit, Pixels, RenderImage, SharedString, SharedUri, Size, StyleRefinement, Styled,
+    Length, ObjectFit, Pixels, RenderImage, SharedString, SharedUri, StyleRefinement, Styled,
     SvgSize, UriOrPath, WindowContext,
 };
 use futures::{AsyncReadExt, Future};
@@ -187,16 +187,30 @@ impl Element for Img {
 
                         let image_size = data.size(frame_index);
 
-                        if let (Length::Auto, Length::Auto) = (style.size.width, style.size.height)
-                        {
-                            style.size = Size {
-                                width: Length::Definite(DefiniteLength::Absolute(
-                                    AbsoluteLength::Pixels(px(image_size.width.0 as f32)),
-                                )),
-                                height: Length::Definite(DefiniteLength::Absolute(
-                                    AbsoluteLength::Pixels(px(image_size.height.0 as f32)),
-                                )),
-                            }
+                        if let Length::Auto = style.size.width {
+                            style.size.width = match style.size.height {
+                                Length::Definite(DefiniteLength::Absolute(
+                                    AbsoluteLength::Pixels(height),
+                                )) => Length::Definite(
+                                    px(image_size.width.0 as f32 * height.0
+                                        / image_size.height.0 as f32)
+                                    .into(),
+                                ),
+                                _ => Length::Definite(px(image_size.width.0 as f32).into()),
+                            };
+                        }
+
+                        if let Length::Auto = style.size.height {
+                            style.size.height = match style.size.width {
+                                Length::Definite(DefiniteLength::Absolute(
+                                    AbsoluteLength::Pixels(width),
+                                )) => Length::Definite(
+                                    px(image_size.height.0 as f32 * width.0
+                                        / image_size.width.0 as f32)
+                                    .into(),
+                                ),
+                                _ => Length::Definite(px(image_size.height.0 as f32).into()),
+                            };
                         }
 
                         if global_id.is_some() && data.frame_count() > 1 {