img.rs

  1use crate::{
  2    div, Active, Anonymous, AnyElement, BorrowWindow, Bounds, Click, Div, DivState, Element,
  3    ElementFocusability, ElementId, ElementIdentity, EventListeners, Focus, Focusable, Hover,
  4    Identified, Interactive, IntoAnyElement, LayoutId, NonFocusable, Pixels, SharedString,
  5    StyleRefinement, Styled, ViewContext,
  6};
  7use futures::FutureExt;
  8use util::ResultExt;
  9
 10pub struct Img<
 11    V: 'static + Send + Sync,
 12    I: ElementIdentity = Anonymous,
 13    F: ElementFocusability = NonFocusable,
 14> {
 15    base: Div<V, I, F>,
 16    uri: Option<SharedString>,
 17    grayscale: bool,
 18}
 19
 20pub fn img<V>() -> Img<V, Anonymous, NonFocusable>
 21where
 22    V: 'static + Send + Sync,
 23{
 24    Img {
 25        base: div(),
 26        uri: None,
 27        grayscale: false,
 28    }
 29}
 30
 31impl<V, I, F> Img<V, I, F>
 32where
 33    V: 'static + Send + Sync,
 34    I: ElementIdentity,
 35    F: ElementFocusability,
 36{
 37    pub fn uri(mut self, uri: impl Into<SharedString>) -> Self {
 38        self.uri = Some(uri.into());
 39        self
 40    }
 41
 42    pub fn grayscale(mut self, grayscale: bool) -> Self {
 43        self.grayscale = grayscale;
 44        self
 45    }
 46}
 47
 48impl<V, F> Img<V, Anonymous, F>
 49where
 50    V: 'static + Send + Sync,
 51    F: ElementFocusability,
 52{
 53    pub fn id(self, id: impl Into<ElementId>) -> Img<V, Identified, F> {
 54        Img {
 55            base: self.base.id(id),
 56            uri: self.uri,
 57            grayscale: self.grayscale,
 58        }
 59    }
 60}
 61
 62impl<V, I, F> IntoAnyElement<V> for Img<V, I, F>
 63where
 64    V: 'static + Send + Sync,
 65    I: ElementIdentity,
 66    F: ElementFocusability,
 67{
 68    fn into_any(self) -> AnyElement<V> {
 69        AnyElement::new(self)
 70    }
 71}
 72
 73impl<V, I, F> Element for Img<V, I, F>
 74where
 75    V: Send + Sync + 'static,
 76    I: ElementIdentity,
 77    F: ElementFocusability,
 78{
 79    type ViewState = V;
 80    type ElementState = DivState;
 81
 82    fn id(&self) -> Option<crate::ElementId> {
 83        self.base.id()
 84    }
 85
 86    fn initialize(
 87        &mut self,
 88        view_state: &mut V,
 89        element_state: Option<Self::ElementState>,
 90        cx: &mut ViewContext<V>,
 91    ) -> Self::ElementState {
 92        self.base.initialize(view_state, element_state, cx)
 93    }
 94
 95    fn layout(
 96        &mut self,
 97        view_state: &mut V,
 98        element_state: &mut Self::ElementState,
 99        cx: &mut ViewContext<Self::ViewState>,
100    ) -> LayoutId {
101        self.base.layout(view_state, element_state, cx)
102    }
103
104    fn paint(
105        &mut self,
106        bounds: Bounds<Pixels>,
107        view: &mut V,
108        element_state: &mut Self::ElementState,
109        cx: &mut ViewContext<V>,
110    ) {
111        cx.stack(0, |cx| {
112            self.base.paint(bounds, view, element_state, cx);
113        });
114
115        let style = self.base.compute_style(bounds, element_state, cx);
116        let corner_radii = style.corner_radii;
117
118        if let Some(uri) = self.uri.clone() {
119            let image_future = cx.image_cache.get(uri);
120            if let Some(data) = image_future
121                .clone()
122                .now_or_never()
123                .and_then(ResultExt::log_err)
124            {
125                let corner_radii = corner_radii.to_pixels(bounds.size, cx.rem_size());
126                cx.stack(1, |cx| {
127                    cx.paint_image(bounds, corner_radii, data, self.grayscale)
128                        .log_err()
129                });
130            } else {
131                cx.spawn(|_, mut cx| async move {
132                    if image_future.await.log_err().is_some() {
133                        cx.on_next_frame(|cx| cx.notify());
134                    }
135                })
136                .detach()
137            }
138        }
139    }
140}
141
142impl<V, I, F> Styled for Img<V, I, F>
143where
144    V: 'static + Send + Sync,
145    I: ElementIdentity,
146    F: ElementFocusability,
147{
148    fn style(&mut self) -> &mut StyleRefinement {
149        self.base.style()
150    }
151}
152
153impl<V, I, F> Interactive for Img<V, I, F>
154where
155    V: 'static + Send + Sync,
156    I: ElementIdentity,
157    F: ElementFocusability,
158{
159    fn listeners(&mut self) -> &mut EventListeners<V> {
160        self.base.listeners()
161    }
162}
163
164impl<V, I, F> Hover for Img<V, I, F>
165where
166    V: 'static + Send + Sync,
167    I: ElementIdentity,
168    F: ElementFocusability,
169{
170    fn set_hover_style(&mut self, group: Option<SharedString>, style: StyleRefinement) {
171        self.base.set_hover_style(group, style);
172    }
173}
174
175impl<V, F> Click for Img<V, Identified, F>
176where
177    V: 'static + Send + Sync,
178    F: ElementFocusability,
179{
180}
181
182impl<V, F> Active for Img<V, Identified, F>
183where
184    V: 'static + Send + Sync,
185    F: ElementFocusability,
186{
187    fn set_active_style(&mut self, group: Option<SharedString>, style: StyleRefinement) {
188        self.base.set_active_style(group, style)
189    }
190}
191
192impl<V, I> Focus for Img<V, I, Focusable>
193where
194    V: 'static + Send + Sync,
195    I: ElementIdentity,
196{
197    fn set_focus_style(&mut self, style: StyleRefinement) {
198        self.base.set_focus_style(style)
199    }
200
201    fn set_focus_in_style(&mut self, style: StyleRefinement) {
202        self.base.set_focus_in_style(style)
203    }
204
205    fn set_in_focus_style(&mut self, style: StyleRefinement) {
206        self.base.set_in_focus_style(style)
207    }
208
209    fn handle(&self) -> &crate::FocusHandle {
210        self.base.handle()
211    }
212}