img.rs

  1use crate::{
  2    BorrowWindow, Bounds, Element, LayoutId, Pixels, Result, SharedString, Style, Styled,
  3    ViewContext,
  4};
  5use futures::FutureExt;
  6use refineable::RefinementCascade;
  7use std::marker::PhantomData;
  8use util::ResultExt;
  9
 10pub struct Img<S> {
 11    style: RefinementCascade<Style>,
 12    uri: Option<SharedString>,
 13    grayscale: bool,
 14    state_type: PhantomData<S>,
 15}
 16
 17pub fn img<S>() -> Img<S> {
 18    Img {
 19        style: RefinementCascade::default(),
 20        uri: None,
 21        grayscale: false,
 22        state_type: PhantomData,
 23    }
 24}
 25
 26impl<S> Img<S> {
 27    pub fn uri(mut self, uri: impl Into<SharedString>) -> Self {
 28        self.uri = Some(uri.into());
 29        self
 30    }
 31
 32    pub fn grayscale(mut self, grayscale: bool) -> Self {
 33        self.grayscale = grayscale;
 34        self
 35    }
 36}
 37
 38impl<S: Send + Sync + 'static> Element for Img<S> {
 39    type State = S;
 40    type FrameState = ();
 41
 42    fn layout(
 43        &mut self,
 44        _: &mut Self::State,
 45        cx: &mut ViewContext<Self::State>,
 46    ) -> anyhow::Result<(LayoutId, Self::FrameState)>
 47    where
 48        Self: Sized,
 49    {
 50        let style = self.computed_style();
 51        let layout_id = cx.request_layout(style, [])?;
 52        Ok((layout_id, ()))
 53    }
 54
 55    fn paint(
 56        &mut self,
 57        bounds: Bounds<Pixels>,
 58        _: &mut Self::State,
 59        _: &mut Self::FrameState,
 60        cx: &mut ViewContext<Self::State>,
 61    ) -> Result<()> {
 62        let style = self.computed_style();
 63
 64        style.paint(bounds, cx);
 65
 66        if let Some(uri) = self.uri.clone() {
 67            let image_future = cx.image_cache.get(uri);
 68            if let Some(data) = image_future
 69                .clone()
 70                .now_or_never()
 71                .and_then(ResultExt::log_err)
 72            {
 73                let corner_radii = style.corner_radii.to_pixels(bounds.size, cx.rem_size());
 74                cx.stack(1, |cx| {
 75                    cx.paint_image(bounds, corner_radii, data, self.grayscale)
 76                })?;
 77            } else {
 78                cx.spawn(|_, mut cx| async move {
 79                    if image_future.await.log_err().is_some() {
 80                        cx.on_next_frame(|cx| cx.notify());
 81                    }
 82                })
 83                .detach()
 84            }
 85        }
 86        Ok(())
 87    }
 88}
 89
 90impl<S> Styled for Img<S> {
 91    type Style = Style;
 92
 93    fn style_cascade(&mut self) -> &mut RefinementCascade<Self::Style> {
 94        &mut self.style
 95    }
 96
 97    fn declared_style(&mut self) -> &mut <Self::Style as refineable::Refineable>::Refinement {
 98        self.style.base()
 99    }
100}