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, Clipboard, Element, Event, Layout, Length, Pixels, Rectangle, Shell,
12 Size, Vector, Widget,
13};
14
15pub struct Sensor<
19 'a,
20 Key,
21 Message,
22 Theme = crate::Theme,
23 Renderer = crate::Renderer,
24> {
25 content: Element<'a, Message, Theme, Renderer>,
26 key: Key,
27 on_show: Option<Box<dyn Fn(Size) -> Message + 'a>>,
28 on_resize: Option<Box<dyn Fn(Size) -> Message + 'a>>,
29 on_hide: Option<Message>,
30 anticipate: Pixels,
31 delay: Duration,
32}
33
34impl<'a, Message, Theme, Renderer> Sensor<'a, (), Message, Theme, Renderer>
35where
36 Renderer: core::Renderer,
37{
38 pub fn new(
40 content: impl Into<Element<'a, Message, Theme, Renderer>>,
41 ) -> Self {
42 Self {
43 content: content.into(),
44 key: (),
45 on_show: None,
46 on_resize: None,
47 on_hide: None,
48 anticipate: Pixels::ZERO,
49 delay: Duration::ZERO,
50 }
51 }
52}
53
54impl<'a, Key, Message, Theme, Renderer>
55 Sensor<'a, Key, Message, Theme, Renderer>
56where
57 Key: self::Key,
58 Renderer: core::Renderer,
59{
60 pub fn on_show(mut self, on_show: impl Fn(Size) -> Message + 'a) -> Self {
64 self.on_show = Some(Box::new(on_show));
65 self
66 }
67
68 pub fn on_resize(
72 mut self,
73 on_resize: impl Fn(Size) -> Message + 'a,
74 ) -> Self {
75 self.on_resize = Some(Box::new(on_resize));
76 self
77 }
78
79 pub fn on_hide(mut self, on_hide: Message) -> Self {
81 self.on_hide = Some(on_hide);
82 self
83 }
84
85 pub fn key<K>(
89 self,
90 key: K,
91 ) -> Sensor<'a, impl self::Key, Message, Theme, Renderer>
92 where
93 K: Clone + PartialEq + 'static,
94 {
95 Sensor {
96 content: self.content,
97 key: OwnedKey(key),
98 on_show: self.on_show,
99 on_resize: self.on_resize,
100 on_hide: self.on_hide,
101 anticipate: self.anticipate,
102 delay: self.delay,
103 }
104 }
105
106 pub fn key_ref<K>(
110 self,
111 key: &'a K,
112 ) -> Sensor<'a, &'a K, Message, Theme, Renderer>
113 where
114 K: ToOwned + PartialEq<K::Owned> + ?Sized,
115 K::Owned: 'static,
116 {
117 Sensor {
118 content: self.content,
119 key,
120 on_show: self.on_show,
121 on_resize: self.on_resize,
122 on_hide: self.on_hide,
123 anticipate: self.anticipate,
124 delay: self.delay,
125 }
126 }
127
128 pub fn anticipate(mut self, distance: impl Into<Pixels>) -> Self {
134 self.anticipate = distance.into();
135 self
136 }
137
138 pub fn delay(mut self, delay: impl Into<Duration>) -> Self {
147 self.delay = delay.into();
148 self
149 }
150}
151
152#[derive(Debug, Clone)]
153struct State<Key> {
154 has_popped_in: bool,
155 should_notify_at: Option<(bool, Instant)>,
156 last_size: Option<Size>,
157 last_key: Key,
158}
159
160impl<Key, Message, Theme, Renderer> Widget<Message, Theme, Renderer>
161 for Sensor<'_, Key, Message, Theme, Renderer>
162where
163 Key: self::Key,
164 Renderer: core::Renderer,
165{
166 fn tag(&self) -> tree::Tag {
167 tree::Tag::of::<State<Key::Owned>>()
168 }
169
170 fn state(&self) -> tree::State {
171 tree::State::new(State {
172 has_popped_in: false,
173 should_notify_at: None,
174 last_size: None,
175 last_key: self.key.to_owned(),
176 })
177 }
178
179 fn children(&self) -> Vec<Tree> {
180 vec![Tree::new(&self.content)]
181 }
182
183 fn diff(&self, tree: &mut Tree) {
184 tree.diff_children(&[&self.content]);
185 }
186
187 fn update(
188 &mut self,
189 tree: &mut Tree,
190 event: &Event,
191 layout: Layout<'_>,
192 cursor: mouse::Cursor,
193 renderer: &Renderer,
194 clipboard: &mut dyn Clipboard,
195 shell: &mut Shell<'_, Message>,
196 viewport: &Rectangle,
197 ) {
198 if let Event::Window(window::Event::RedrawRequested(now)) = &event {
199 let state = tree.state.downcast_mut::<State<Key::Owned>>();
200
201 if state.has_popped_in && !self.key.eq(&state.last_key) {
202 state.has_popped_in = false;
203 state.should_notify_at = None;
204 state.last_key = self.key.to_owned();
205 }
206
207 let bounds = layout.bounds();
208 let top_left_distance = viewport.distance(bounds.position());
209
210 let bottom_right_distance = viewport
211 .distance(bounds.position() + Vector::from(bounds.size()));
212
213 let distance = top_left_distance.min(bottom_right_distance);
214
215 if self.on_show.is_none() {
216 if let Some(on_resize) = &self.on_resize {
217 let size = bounds.size();
218
219 if Some(size) != state.last_size {
220 state.last_size = Some(size);
221 shell.publish(on_resize(size));
222 }
223 }
224 } else if state.has_popped_in {
225 if distance <= self.anticipate.0 {
226 if let Some(on_resize) = &self.on_resize {
227 let size = bounds.size();
228
229 if Some(size) != state.last_size {
230 state.last_size = Some(size);
231 shell.publish(on_resize(size));
232 }
233 }
234 } else if self.on_hide.is_some() {
235 state.has_popped_in = false;
236 state.should_notify_at = Some((false, *now + self.delay));
237 }
238 } else if distance <= self.anticipate.0 {
239 let size = bounds.size();
240
241 state.has_popped_in = true;
242 state.should_notify_at = Some((true, *now + self.delay));
243 state.last_size = Some(size);
244 }
245
246 match &state.should_notify_at {
247 Some((has_popped_in, at)) if at <= now => {
248 if *has_popped_in {
249 if let Some(on_show) = &self.on_show {
250 shell.publish(on_show(layout.bounds().size()));
251 }
252 } else if let Some(on_hide) = self.on_hide.take() {
253 shell.publish(on_hide);
254 }
255
256 state.should_notify_at = None;
257 }
258 Some((_, at)) => {
259 shell.request_redraw_at(*at);
260 }
261 None => {}
262 }
263 }
264
265 self.content.as_widget_mut().update(
266 &mut tree.children[0],
267 event,
268 layout,
269 cursor,
270 renderer,
271 clipboard,
272 shell,
273 viewport,
274 );
275 }
276
277 fn size(&self) -> Size<Length> {
278 self.content.as_widget().size()
279 }
280
281 fn size_hint(&self) -> Size<Length> {
282 self.content.as_widget().size_hint()
283 }
284
285 fn layout(
286 &mut self,
287 tree: &mut Tree,
288 renderer: &Renderer,
289 limits: &layout::Limits,
290 ) -> layout::Node {
291 self.content.as_widget_mut().layout(
292 &mut tree.children[0],
293 renderer,
294 limits,
295 )
296 }
297
298 fn draw(
299 &self,
300 tree: &Tree,
301 renderer: &mut Renderer,
302 theme: &Theme,
303 style: &renderer::Style,
304 layout: layout::Layout<'_>,
305 cursor: mouse::Cursor,
306 viewport: &Rectangle,
307 ) {
308 self.content.as_widget().draw(
309 &tree.children[0],
310 renderer,
311 theme,
312 style,
313 layout,
314 cursor,
315 viewport,
316 );
317 }
318
319 fn operate(
320 &mut self,
321 tree: &mut Tree,
322 layout: core::Layout<'_>,
323 renderer: &Renderer,
324 operation: &mut dyn widget::Operation,
325 ) {
326 self.content.as_widget_mut().operate(
327 &mut tree.children[0],
328 layout,
329 renderer,
330 operation,
331 );
332 }
333
334 fn mouse_interaction(
335 &self,
336 tree: &Tree,
337 layout: core::Layout<'_>,
338 cursor: mouse::Cursor,
339 viewport: &Rectangle,
340 renderer: &Renderer,
341 ) -> mouse::Interaction {
342 self.content.as_widget().mouse_interaction(
343 &tree.children[0],
344 layout,
345 cursor,
346 viewport,
347 renderer,
348 )
349 }
350
351 fn overlay<'b>(
352 &'b mut self,
353 tree: &'b mut Tree,
354 layout: core::Layout<'b>,
355 renderer: &Renderer,
356 viewport: &Rectangle,
357 translation: core::Vector,
358 ) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
359 self.content.as_widget_mut().overlay(
360 &mut tree.children[0],
361 layout,
362 renderer,
363 viewport,
364 translation,
365 )
366 }
367}
368
369impl<'a, Key, Message, Theme, Renderer>
370 From<Sensor<'a, Key, Message, Theme, Renderer>>
371 for Element<'a, Message, Theme, Renderer>
372where
373 Message: 'a,
374 Key: self::Key + 'a,
375 Renderer: core::Renderer + 'a,
376 Theme: 'a,
377{
378 fn from(pop: Sensor<'a, Key, Message, Theme, Renderer>) -> Self {
379 Element::new(pop)
380 }
381}
382
383pub trait Key {
387 type Owned: 'static;
389
390 fn to_owned(&self) -> Self::Owned;
392
393 fn eq(&self, other: &Self::Owned) -> bool;
395}
396
397impl<T> Key for &T
398where
399 T: ToOwned + PartialEq<T::Owned> + ?Sized,
400 T::Owned: 'static,
401{
402 type Owned = T::Owned;
403
404 fn to_owned(&self) -> <Self as Key>::Owned {
405 ToOwned::to_owned(*self)
406 }
407
408 fn eq(&self, other: &Self::Owned) -> bool {
409 *self == other
410 }
411}
412
413struct OwnedKey<T>(T);
414
415impl<T> Key for OwnedKey<T>
416where
417 T: PartialEq + Clone + 'static,
418{
419 type Owned = T;
420
421 fn to_owned(&self) -> Self::Owned {
422 self.0.clone()
423 }
424
425 fn eq(&self, other: &Self::Owned) -> bool {
426 &self.0 == other
427 }
428}
429
430impl<T> PartialEq<T> for OwnedKey<T>
431where
432 T: PartialEq,
433{
434 fn eq(&self, other: &T) -> bool {
435 &self.0 == other
436 }
437}
438
439impl Key for () {
440 type Owned = ();
441
442 fn to_owned(&self) -> Self::Owned {}
443
444 fn eq(&self, _other: &Self::Owned) -> bool {
445 true
446 }
447}