img.rs

  1use crate::{
  2    BorrowWindow, Bounds, Element, InteractiveElement, InteractiveElementState, Interactivity,
  3    LayoutId, Pixels, RenderOnce, SharedString, StyleRefinement, Styled, ViewContext,
  4};
  5use futures::FutureExt;
  6use util::ResultExt;
  7
  8pub struct Img<V: 'static> {
  9    interactivity: Interactivity<V>,
 10    uri: Option<SharedString>,
 11    grayscale: bool,
 12}
 13
 14pub fn img<V: 'static>() -> Img<V> {
 15    Img {
 16        interactivity: Interactivity::default(),
 17        uri: None,
 18        grayscale: false,
 19    }
 20}
 21
 22impl<V> Img<V>
 23where
 24    V: 'static,
 25{
 26    pub fn uri(mut self, uri: impl Into<SharedString>) -> Self {
 27        self.uri = Some(uri.into());
 28        self
 29    }
 30
 31    pub fn grayscale(mut self, grayscale: bool) -> Self {
 32        self.grayscale = grayscale;
 33        self
 34    }
 35}
 36
 37impl<V> Element<V> for Img<V> {
 38    type State = InteractiveElementState;
 39
 40    fn layout(
 41        &mut self,
 42        _view_state: &mut V,
 43        element_state: Option<Self::State>,
 44        cx: &mut ViewContext<V>,
 45    ) -> (LayoutId, Self::State) {
 46        self.interactivity.layout(element_state, cx, |style, cx| {
 47            cx.request_layout(&style, None)
 48        })
 49    }
 50
 51    fn paint(
 52        self,
 53        bounds: Bounds<Pixels>,
 54        _view_state: &mut V,
 55        element_state: &mut Self::State,
 56        cx: &mut ViewContext<V>,
 57    ) {
 58        self.interactivity.paint(
 59            bounds,
 60            bounds.size,
 61            element_state,
 62            cx,
 63            |style, _scroll_offset, cx| {
 64                let corner_radii = style.corner_radii;
 65
 66                if let Some(uri) = self.uri.clone() {
 67                    // eprintln!(">>> image_cache.get({uri}");
 68                    let image_future = cx.image_cache.get(uri.clone());
 69                    // eprintln!("<<< image_cache.get({uri}");
 70                    if let Some(data) = image_future
 71                        .clone()
 72                        .now_or_never()
 73                        .and_then(|result| result.ok())
 74                    {
 75                        let corner_radii = corner_radii.to_pixels(bounds.size, cx.rem_size());
 76                        cx.with_z_index(1, |cx| {
 77                            cx.paint_image(bounds, corner_radii, data, self.grayscale)
 78                                .log_err()
 79                        });
 80                    } else {
 81                        cx.spawn(|_, mut cx| async move {
 82                            if image_future.await.ok().is_some() {
 83                                cx.on_next_frame(|cx| cx.notify());
 84                            }
 85                        })
 86                        .detach()
 87                    }
 88                }
 89            },
 90        )
 91    }
 92}
 93
 94impl<V: 'static> RenderOnce<V> for Img<V> {
 95    type Element = Self;
 96
 97    fn element_id(&self) -> Option<crate::ElementId> {
 98        self.interactivity.element_id.clone()
 99    }
100
101    fn render_once(self) -> Self::Element {
102        self
103    }
104}
105
106impl<V> Styled for Img<V> {
107    fn style(&mut self) -> &mut StyleRefinement {
108        &mut self.interactivity.base_style
109    }
110}
111
112impl<V> InteractiveElement<V> for Img<V> {
113    fn interactivity(&mut self) -> &mut Interactivity<V> {
114        &mut self.interactivity
115    }
116}