Skip to main content

kozan_core/html/
html_image_element.rs

1//! `HTMLImageElement` — an image element.
2//!
3//! Chrome equivalent: `HTMLImageElement` (DOM) + `LayoutImage` (layout).
4//! A replaced element — its content is an external image resource.
5//!
6//! # Intrinsic sizing
7//!
8//! Before the image loads, intrinsic size is unknown (returns None).
9//! After load, the natural width/height from the decoded image.
10//! The layout system uses these for CSS sizing resolution.
11
12use super::replaced::{IntrinsicSizing, ReplacedElement};
13use crate::Handle;
14use kozan_macros::{Element, Props};
15
16/// An image element (`<img>`).
17///
18/// Chrome equivalent: `HTMLImageElement`.
19/// Replaced element — content from external image resource.
20#[derive(Copy, Clone, Element)]
21#[element(tag = "img", data = ImageData)]
22pub struct HtmlImageElement(Handle);
23
24/// Element-specific data for `<img>`.
25#[derive(Default, Clone, Props)]
26#[props(element = HtmlImageElement)]
27#[non_exhaustive]
28pub struct ImageData {
29    /// The image URL.
30    #[prop]
31    pub src: String,
32    /// Alternative text for accessibility.
33    #[prop]
34    pub alt: String,
35    /// The intrinsic width in CSS pixels (from decoded image or attribute).
36    #[prop]
37    pub natural_width: f32,
38    /// The intrinsic height in CSS pixels (from decoded image or attribute).
39    #[prop]
40    pub natural_height: f32,
41}
42
43impl ReplacedElement for HtmlImageElement {
44    fn intrinsic_sizing(&self) -> IntrinsicSizing {
45        let w = self.natural_width();
46        let h = self.natural_height();
47
48        if w > 0.0 && h > 0.0 {
49            IntrinsicSizing::from_size(w, h)
50        } else {
51            // Image not loaded yet — no intrinsic size.
52            IntrinsicSizing::default()
53        }
54    }
55}
56
57#[cfg(test)]
58mod tests {
59    use super::*;
60    use crate::dom::document::Document;
61
62    #[test]
63    fn image_src_and_alt() {
64        let doc = Document::new();
65        let img = doc.create::<HtmlImageElement>();
66
67        img.set_src("photo.jpg");
68        img.set_alt("A photo");
69
70        assert_eq!(img.src(), "photo.jpg");
71        assert_eq!(img.alt(), "A photo");
72    }
73
74    #[test]
75    fn image_intrinsic_sizing() {
76        let doc = Document::new();
77        let img = doc.create::<HtmlImageElement>();
78
79        // No size yet.
80        let sizing = img.intrinsic_sizing();
81        assert!(sizing.width.is_none());
82
83        // Simulate image load.
84        img.set_natural_width(800.0);
85        img.set_natural_height(600.0);
86
87        let sizing = img.intrinsic_sizing();
88        assert_eq!(sizing.width, Some(800.0));
89        assert_eq!(sizing.height, Some(600.0));
90        assert!((sizing.aspect_ratio.unwrap() - 1.333).abs() < 0.01);
91    }
92}