image_viewer: Make preview background checkered cover only the image size (#40078)

Smit Barmase created

This makes it easier to see the image bounds for images with transparent
backgrounds.

<img width="2560" height="1377" alt="png"
src="https://github.com/user-attachments/assets/e1555576-39a2-4240-b9d3-67574df76f0d"
/>

Release Notes:

- Updated image preview background checkboxes to match the actual image
size, making it easier to see the bounds of images with transparent
backgrounds.

Change summary

crates/image_viewer/src/image_viewer.rs | 135 ++++++++++++++------------
1 file changed, 71 insertions(+), 64 deletions(-)

Detailed changes

crates/image_viewer/src/image_viewer.rs 🔗

@@ -293,72 +293,79 @@ impl Focusable for ImageView {
 impl Render for ImageView {
     fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let image = self.image_item.read(cx).image.clone();
-        let checkered_background = |bounds: Bounds<Pixels>,
-                                    _,
-                                    window: &mut Window,
-                                    _cx: &mut App| {
-            let square_size = 32.0;
-
-            let start_y = bounds.origin.y.into();
-            let height: f32 = bounds.size.height.into();
-            let start_x = bounds.origin.x.into();
-            let width: f32 = bounds.size.width.into();
-
-            let mut y = start_y;
-            let mut x = start_x;
-            let mut color_swapper = true;
-            // draw checkerboard pattern
-            while y <= start_y + height {
-                // Keeping track of the grid in order to be resilient to resizing
-                let start_swap = color_swapper;
-                while x <= start_x + width {
-                    let rect =
-                        Bounds::new(point(px(x), px(y)), size(px(square_size), px(square_size)));
-
-                    let color = if color_swapper {
-                        opaque_grey(0.6, 0.4)
-                    } else {
-                        opaque_grey(0.7, 0.4)
-                    };
-
-                    window.paint_quad(fill(rect, color));
-                    color_swapper = !color_swapper;
-                    x += square_size;
+        let checkered_background =
+            |bounds: Bounds<Pixels>, _, window: &mut Window, _cx: &mut App| {
+                let square_size: f32 = 32.0;
+
+                let start_y = bounds.origin.y.into();
+                let height: f32 = bounds.size.height.into();
+                let start_x = bounds.origin.x.into();
+                let width: f32 = bounds.size.width.into();
+
+                let mut y = start_y;
+                let mut x = start_x;
+                let mut color_swapper = true;
+                // draw checkerboard pattern
+                while y < start_y + height {
+                    // Keeping track of the grid in order to be resilient to resizing
+                    let start_swap = color_swapper;
+                    while x < start_x + width {
+                        // Clamp square dimensions to not exceed bounds
+                        let square_width = square_size.min(start_x + width - x);
+                        let square_height = square_size.min(start_y + height - y);
+
+                        let rect = Bounds::new(
+                            point(px(x), px(y)),
+                            size(px(square_width), px(square_height)),
+                        );
+
+                        let color = if color_swapper {
+                            opaque_grey(0.6, 0.4)
+                        } else {
+                            opaque_grey(0.7, 0.4)
+                        };
+
+                        window.paint_quad(fill(rect, color));
+                        color_swapper = !color_swapper;
+                        x += square_size;
+                    }
+                    x = start_x;
+                    color_swapper = !start_swap;
+                    y += square_size;
                 }
-                x = start_x;
-                color_swapper = !start_swap;
-                y += square_size;
-            }
-        };
+            };
 
-        let checkered_background = canvas(|_, _, _| (), checkered_background)
-            .border_2()
-            .border_color(cx.theme().styles.colors.border)
-            .size_full()
-            .absolute()
-            .top_0()
-            .left_0();
-
-        div()
-            .track_focus(&self.focus_handle(cx))
-            .size_full()
-            .child(checkered_background)
-            .child(
-                div()
-                    .flex()
-                    .justify_center()
-                    .items_center()
-                    .w_full()
-                    // TODO: In browser based Tailwind & Flex this would be h-screen and we'd use w-full
-                    .h_full()
-                    .child(
-                        img(image)
-                            .object_fit(ObjectFit::ScaleDown)
-                            .max_w_full()
-                            .max_h_full()
-                            .id("img"),
-                    ),
-            )
+        div().track_focus(&self.focus_handle(cx)).size_full().child(
+            div()
+                .flex()
+                .justify_center()
+                .items_center()
+                .w_full()
+                // TODO: In browser based Tailwind & Flex this would be h-screen and we'd use w-full
+                .h_full()
+                .child(
+                    div()
+                        .relative()
+                        .max_w_full()
+                        .max_h_full()
+                        .child(
+                            canvas(|_, _, _| (), checkered_background)
+                                .border_2()
+                                .border_color(cx.theme().styles.colors.border)
+                                .size_full()
+                                .absolute()
+                                .top_0()
+                                .left_0(),
+                        )
+                        .child(
+                            img(image)
+                                .object_fit(ObjectFit::ScaleDown)
+                                .max_w_full()
+                                .max_h_full()
+                                .id("img"),
+                        ),
+                ),
+        )
     }
 }