1use crate as gpui2;
2use crate::{
3 style::{Style, StyleHelpers, Styleable},
4 Element,
5};
6use futures::FutureExt;
7use gpui::geometry::vector::Vector2F;
8use gpui::scene;
9use gpui2_macros::IntoElement;
10use refineable::RefinementCascade;
11use util::arc_cow::ArcCow;
12use util::ResultExt;
13
14#[derive(IntoElement)]
15pub struct Img {
16 style: RefinementCascade<Style>,
17 uri: Option<ArcCow<'static, str>>,
18}
19
20pub fn img() -> Img {
21 Img {
22 style: RefinementCascade::default(),
23 uri: None,
24 }
25}
26
27impl Img {
28 pub fn uri(mut self, uri: impl Into<ArcCow<'static, str>>) -> Self {
29 self.uri = Some(uri.into());
30 self
31 }
32}
33
34impl<V: 'static> Element<V> for Img {
35 type PaintState = ();
36
37 fn layout(
38 &mut self,
39 _: &mut V,
40 cx: &mut crate::ViewContext<V>,
41 ) -> anyhow::Result<(gpui::LayoutId, Self::PaintState)>
42 where
43 Self: Sized,
44 {
45 let style = self.computed_style();
46 let layout_id = cx.add_layout_node(style, [])?;
47 Ok((layout_id, ()))
48 }
49
50 fn paint(
51 &mut self,
52 _: &mut V,
53 parent_origin: Vector2F,
54 layout: &gpui::Layout,
55 _: &mut Self::PaintState,
56 cx: &mut crate::ViewContext<V>,
57 ) where
58 Self: Sized,
59 {
60 let style = self.computed_style();
61 let bounds = layout.bounds + parent_origin;
62
63 style.paint_background(bounds, cx);
64
65 if let Some(uri) = &self.uri {
66 let image_future = cx.image_cache.get(uri.clone());
67 if let Some(data) = image_future
68 .clone()
69 .now_or_never()
70 .and_then(ResultExt::log_err)
71 {
72 let rem_size = cx.rem_size();
73 cx.scene().push_image(scene::Image {
74 bounds,
75 border: gpui::Border {
76 color: style.border_color.unwrap_or_default().into(),
77 top: style.border_widths.top.to_pixels(rem_size),
78 right: style.border_widths.right.to_pixels(rem_size),
79 bottom: style.border_widths.bottom.to_pixels(rem_size),
80 left: style.border_widths.left.to_pixels(rem_size),
81 },
82 corner_radii: style.corner_radii.to_gpui(bounds.size(), rem_size),
83 grayscale: false,
84 data,
85 })
86 } else {
87 cx.spawn(|this, mut cx| async move {
88 if image_future.await.log_err().is_some() {
89 this.update(&mut cx, |_, cx| cx.notify()).ok();
90 }
91 })
92 .detach();
93 }
94 }
95 }
96}
97
98impl Styleable for Img {
99 type Style = Style;
100
101 fn style_cascade(&mut self) -> &mut RefinementCascade<Self::Style> {
102 &mut self.style
103 }
104
105 fn declared_style(&mut self) -> &mut <Self::Style as refineable::Refineable>::Refinement {
106 self.style.base()
107 }
108}
109
110impl StyleHelpers for Img {}