img.rs

  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 {}