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