gpui: Maintain `img` aspect ratio when `max_width` is set (#25632)

Jason Lee created

Release Notes:

- Fixed Markdown preview to display image with max width 100%.

## Before

<img width="1202" alt="image"
src="https://github.com/user-attachments/assets/359628df-8746-456f-a768-b3428923c937"
/>
<img width="750" alt="SCR-20250226-napv"
src="https://github.com/user-attachments/assets/f6154516-470e-41b2-84f5-ef0612c447ad"
/>


## After

<img width="1149" alt="image"
src="https://github.com/user-attachments/assets/2279347d-9c69-4a47-bb62-ccc8e55a98f6"
/>
<img width="520" alt="SCR-20250226-ngyz"
src="https://github.com/user-attachments/assets/03af5f14-1935-472e-822f-4c7f62630780"
/>

Change summary

crates/gpui/examples/image/image.rs              | 21 ++++++++++++++---
crates/gpui/src/elements/img.rs                  |  2 +
crates/markdown_preview/src/markdown_renderer.rs | 16 ++++++++-----
3 files changed, 29 insertions(+), 10 deletions(-)

Detailed changes

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

@@ -74,6 +74,9 @@ struct ImageShowcase {
 impl Render for ImageShowcase {
     fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
         div()
+            .id("main")
+            .overflow_y_scroll()
+            .p_5()
             .size_full()
             .flex()
             .flex_col()
@@ -116,9 +119,21 @@ impl Render for ImageShowcase {
                         div()
                             .flex_col()
                             .child("Auto Height")
-                            .child(img("https://picsum.photos/480/640").w(px(180.))),
+                            .child(img("https://picsum.photos/800/400").w(px(180.))),
                     ),
             )
+            .child(
+                div()
+                    .flex()
+                    .flex_col()
+                    .justify_center()
+                    .items_center()
+                    .w_full()
+                    .border_1()
+                    .border_color(rgb(0xC0C0C0))
+                    .child("image with max width 100%")
+                    .child(img("https://picsum.photos/800/400").max_w_full()),
+            )
     }
 }
 
@@ -164,9 +179,7 @@ fn main() {
                 cx.new(|_| ImageShowcase {
                     // Relative path to your root project path
                     local_resource: manifest_dir.join("examples/image/app-icon.png").into(),
-
-                    remote_resource: "https://picsum.photos/512/512".into(),
-
+                    remote_resource: "https://picsum.photos/800/400".into(),
                     asset_resource: "image/color.svg".into(),
                 })
             })

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

@@ -301,6 +301,8 @@ impl Element for Img {
                             }
 
                             let image_size = data.size(frame_index);
+                            style.aspect_ratio =
+                                Some(image_size.width.0 as f32 / image_size.height.0 as f32);
 
                             if let Length::Auto = style.size.width {
                                 style.size.width = match style.size.height {

crates/markdown_preview/src/markdown_renderer.rs 🔗

@@ -513,12 +513,16 @@ fn render_markdown_text(parsed_new: &MarkdownParagraph, cx: &mut RenderContext)
                 let image_element = div()
                     .id(element_id)
                     .cursor_pointer()
-                    .child(img(ImageSource::Resource(image_resource)).with_fallback({
-                        let alt_text = image.alt_text.clone();
-                        {
-                            move || div().children(alt_text.clone()).into_any_element()
-                        }
-                    }))
+                    .child(
+                        img(ImageSource::Resource(image_resource))
+                            .max_w_full()
+                            .with_fallback({
+                                let alt_text = image.alt_text.clone();
+                                {
+                                    move || div().children(alt_text.clone()).into_any_element()
+                                }
+                            }),
+                    )
                     .tooltip({
                         let link = image.link.clone();
                         move |_, cx| {