1use crate::core::layout;
3use crate::core::mouse;
4use crate::core::overlay;
5use crate::core::renderer;
6use crate::core::time::{Duration, Instant};
7use crate::core::widget;
8use crate::core::widget::tree::{self, Tree};
9use crate::core::window;
10use crate::core::{
11 self, Element, Event, Layout, Length, Pixels, Rectangle, Shell, Size, Vector, Widget,
12};
13
14pub struct Sensor<'a, Key, Message, Theme = crate::Theme, Renderer = crate::Renderer> {
18 content: Element<'a, Message, Theme, Renderer>,
19 key: Key,
20 on_show: Option<Box<dyn Fn(Size) -> Message + 'a>>,
21 on_resize: Option<Box<dyn Fn(Size) -> Message + 'a>>,
22 on_hide: Option<Message>,
23 anticipate: Pixels,
24 delay: Duration,
25}
26
27impl<'a, Message, Theme, Renderer> Sensor<'a, (), Message, Theme, Renderer>
28where
29 Renderer: core::Renderer,
30{
31 pub fn new(content: impl Into<Element<'a, Message, Theme, Renderer>>) -> Self {
33 Self {
34 content: content.into(),
35 key: (),
36 on_show: None,
37 on_resize: None,
38 on_hide: None,
39 anticipate: Pixels::ZERO,
40 delay: Duration::ZERO,
41 }
42 }
43}
44
45impl<'a, Key, Message, Theme, Renderer> Sensor<'a, Key, Message, Theme, Renderer>
46where
47 Key: self::Key,
48 Renderer: core::Renderer,
49{
50 pub fn on_show(mut self, on_show: impl Fn(Size) -> Message + 'a) -> Self {
54 self.on_show = Some(Box::new(on_show));
55 self
56 }
57
58 pub fn on_resize(mut self, on_resize: impl Fn(Size) -> Message + 'a) -> Self {
62 self.on_resize = Some(Box::new(on_resize));
63 self
64 }
65
66 pub fn on_hide(mut self, on_hide: Message) -> Self {
68 self.on_hide = Some(on_hide);
69 self
70 }
71
72 pub fn key<K>(self, key: K) -> Sensor<'a, impl self::Key, Message, Theme, Renderer>
76 where
77 K: Clone + PartialEq + 'static,
78 {
79 Sensor {
80 content: self.content,
81 key: OwnedKey(key),
82 on_show: self.on_show,
83 on_resize: self.on_resize,
84 on_hide: self.on_hide,
85 anticipate: self.anticipate,
86 delay: self.delay,
87 }
88 }
89
90 pub fn key_ref<K>(self, key: &'a K) -> Sensor<'a, &'a K, Message, Theme, Renderer>
94 where
95 K: ToOwned + PartialEq<K::Owned> + ?Sized,
96 K::Owned: 'static,
97 {
98 Sensor {
99 content: self.content,
100 key,
101 on_show: self.on_show,
102 on_resize: self.on_resize,
103 on_hide: self.on_hide,
104 anticipate: self.anticipate,
105 delay: self.delay,
106 }
107 }
108
109 pub fn anticipate(mut self, distance: impl Into<Pixels>) -> Self {
115 self.anticipate = distance.into();
116 self
117 }
118
119 pub fn delay(mut self, delay: impl Into<Duration>) -> Self {
128 self.delay = delay.into();
129 self
130 }
131}
132
133#[derive(Debug, Clone)]
134struct State<Key> {
135 has_popped_in: bool,
136 should_notify_at: Option<(bool, Instant)>,
137 last_size: Option<Size>,
138 last_key: Key,
139}
140
141impl<Key, Message, Theme, Renderer> Widget<Message, Theme, Renderer>
142 for Sensor<'_, Key, Message, Theme, Renderer>
143where
144 Key: self::Key,
145 Renderer: core::Renderer,
146{
147 fn tag(&self) -> tree::Tag {
148 tree::Tag::of::<State<Key::Owned>>()
149 }
150
151 fn state(&self) -> tree::State {
152 tree::State::new(State {
153 has_popped_in: false,
154 should_notify_at: None,
155 last_size: None,
156 last_key: self.key.to_owned(),
157 })
158 }
159
160 fn diff(&mut self, tree: &mut Tree) {
161 tree.diff_children(std::slice::from_mut(&mut self.content));
162 }
163
164 fn update(
165 &mut self,
166 tree: &mut Tree,
167 event: &Event,
168 layout: Layout<'_>,
169 cursor: mouse::Cursor,
170 renderer: &Renderer,
171 shell: &mut Shell<'_, Message>,
172 viewport: &Rectangle,
173 ) {
174 if let Event::Window(window::Event::RedrawRequested(now)) = &event {
175 let state = tree.state.downcast_mut::<State<Key::Owned>>();
176
177 if state.has_popped_in && !self.key.eq(&state.last_key) {
178 state.has_popped_in = false;
179 state.should_notify_at = None;
180 state.last_key = self.key.to_owned();
181 }
182
183 let bounds = layout.bounds();
184 let top_left_distance = viewport.distance(bounds.position());
185
186 let bottom_right_distance =
187 viewport.distance(bounds.position() + Vector::from(bounds.size()));
188
189 let distance = top_left_distance.min(bottom_right_distance);
190
191 if self.on_show.is_none() {
192 if let Some(on_resize) = &self.on_resize {
193 let size = bounds.size();
194
195 if Some(size) != state.last_size {
196 state.last_size = Some(size);
197 shell.publish(on_resize(size));
198 }
199 }
200 } else if state.has_popped_in {
201 if distance <= self.anticipate.0 {
202 if let Some(on_resize) = &self.on_resize {
203 let size = bounds.size();
204
205 if Some(size) != state.last_size {
206 state.last_size = Some(size);
207 shell.publish(on_resize(size));
208 }
209 }
210 } else if self.on_hide.is_some() {
211 state.has_popped_in = false;
212 state.should_notify_at = Some((false, *now + self.delay));
213 }
214 } else if distance <= self.anticipate.0 {
215 let size = bounds.size();
216
217 state.has_popped_in = true;
218 state.should_notify_at = Some((true, *now + self.delay));
219 state.last_size = Some(size);
220 }
221
222 match &state.should_notify_at {
223 Some((has_popped_in, at)) if at <= now => {
224 if *has_popped_in {
225 if let Some(on_show) = &self.on_show {
226 shell.publish(on_show(layout.bounds().size()));
227 }
228 } else if let Some(on_hide) = self.on_hide.take() {
229 shell.publish(on_hide);
230 }
231
232 state.should_notify_at = None;
233 }
234 Some((_, at)) => {
235 shell.request_redraw_at(*at);
236 }
237 None => {}
238 }
239 }
240
241 self.content.as_widget_mut().update(
242 &mut tree.children[0],
243 event,
244 layout,
245 cursor,
246 renderer,
247 shell,
248 viewport,
249 );
250 }
251
252 fn size(&self) -> Size<Length> {
253 self.content.as_widget().size()
254 }
255
256 fn layout(
257 &mut self,
258 tree: &mut Tree,
259 renderer: &Renderer,
260 limits: &layout::Limits,
261 ) -> layout::Node {
262 self.content
263 .as_widget_mut()
264 .layout(&mut tree.children[0], renderer, limits)
265 }
266
267 fn draw(
268 &self,
269 tree: &Tree,
270 renderer: &mut Renderer,
271 theme: &Theme,
272 style: &renderer::Style,
273 layout: layout::Layout<'_>,
274 cursor: mouse::Cursor,
275 viewport: &Rectangle,
276 ) {
277 self.content.as_widget().draw(
278 &tree.children[0],
279 renderer,
280 theme,
281 style,
282 layout,
283 cursor,
284 viewport,
285 );
286 }
287
288 fn operate(
289 &mut self,
290 tree: &mut Tree,
291 layout: core::Layout<'_>,
292 renderer: &Renderer,
293 operation: &mut dyn widget::Operation,
294 ) {
295 self.content
296 .as_widget_mut()
297 .operate(&mut tree.children[0], layout, renderer, operation);
298 }
299
300 fn mouse_interaction(
301 &self,
302 tree: &Tree,
303 layout: core::Layout<'_>,
304 cursor: mouse::Cursor,
305 viewport: &Rectangle,
306 renderer: &Renderer,
307 ) -> mouse::Interaction {
308 self.content.as_widget().mouse_interaction(
309 &tree.children[0],
310 layout,
311 cursor,
312 viewport,
313 renderer,
314 )
315 }
316
317 fn overlay<'b>(
318 &'b mut self,
319 tree: &'b mut Tree,
320 layout: core::Layout<'b>,
321 renderer: &Renderer,
322 viewport: &Rectangle,
323 translation: core::Vector,
324 ) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
325 self.content.as_widget_mut().overlay(
326 &mut tree.children[0],
327 layout,
328 renderer,
329 viewport,
330 translation,
331 )
332 }
333}
334
335impl<'a, Key, Message, Theme, Renderer> From<Sensor<'a, Key, Message, Theme, Renderer>>
336 for Element<'a, Message, Theme, Renderer>
337where
338 Message: 'a,
339 Key: self::Key + 'a,
340 Renderer: core::Renderer + 'a,
341 Theme: 'a,
342{
343 fn from(pop: Sensor<'a, Key, Message, Theme, Renderer>) -> Self {
344 Element::new(pop)
345 }
346}
347
348pub trait Key {
352 type Owned: 'static;
354
355 fn to_owned(&self) -> Self::Owned;
357
358 fn eq(&self, other: &Self::Owned) -> bool;
360}
361
362impl<T> Key for &T
363where
364 T: ToOwned + PartialEq<T::Owned> + ?Sized,
365 T::Owned: 'static,
366{
367 type Owned = T::Owned;
368
369 fn to_owned(&self) -> <Self as Key>::Owned {
370 ToOwned::to_owned(*self)
371 }
372
373 fn eq(&self, other: &Self::Owned) -> bool {
374 *self == other
375 }
376}
377
378struct OwnedKey<T>(T);
379
380impl<T> Key for OwnedKey<T>
381where
382 T: PartialEq + Clone + 'static,
383{
384 type Owned = T;
385
386 fn to_owned(&self) -> Self::Owned {
387 self.0.clone()
388 }
389
390 fn eq(&self, other: &Self::Owned) -> bool {
391 &self.0 == other
392 }
393}
394
395impl<T> PartialEq<T> for OwnedKey<T>
396where
397 T: PartialEq,
398{
399 fn eq(&self, other: &T) -> bool {
400 &self.0 == other
401 }
402}
403
404impl Key for () {
405 type Owned = ();
406
407 fn to_owned(&self) -> Self::Owned {}
408
409 fn eq(&self, _other: &Self::Owned) -> bool {
410 true
411 }
412}