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}