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, 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 children(&self) -> Vec<Tree> {
161 vec![Tree::new(&self.content)]
162 }
163
164 fn diff(&self, tree: &mut Tree) {
165 tree.diff_children(&[&self.content]);
166 }
167
168 fn update(
169 &mut self,
170 tree: &mut Tree,
171 event: &Event,
172 layout: Layout<'_>,
173 cursor: mouse::Cursor,
174 renderer: &Renderer,
175 clipboard: &mut dyn Clipboard,
176 shell: &mut Shell<'_, Message>,
177 viewport: &Rectangle,
178 ) {
179 if let Event::Window(window::Event::RedrawRequested(now)) = &event {
180 let state = tree.state.downcast_mut::<State<Key::Owned>>();
181
182 if state.has_popped_in && !self.key.eq(&state.last_key) {
183 state.has_popped_in = false;
184 state.should_notify_at = None;
185 state.last_key = self.key.to_owned();
186 }
187
188 let bounds = layout.bounds();
189 let top_left_distance = viewport.distance(bounds.position());
190
191 let bottom_right_distance =
192 viewport.distance(bounds.position() + Vector::from(bounds.size()));
193
194 let distance = top_left_distance.min(bottom_right_distance);
195
196 if self.on_show.is_none() {
197 if let Some(on_resize) = &self.on_resize {
198 let size = bounds.size();
199
200 if Some(size) != state.last_size {
201 state.last_size = Some(size);
202 shell.publish(on_resize(size));
203 }
204 }
205 } else if state.has_popped_in {
206 if distance <= self.anticipate.0 {
207 if let Some(on_resize) = &self.on_resize {
208 let size = bounds.size();
209
210 if Some(size) != state.last_size {
211 state.last_size = Some(size);
212 shell.publish(on_resize(size));
213 }
214 }
215 } else if self.on_hide.is_some() {
216 state.has_popped_in = false;
217 state.should_notify_at = Some((false, *now + self.delay));
218 }
219 } else if distance <= self.anticipate.0 {
220 let size = bounds.size();
221
222 state.has_popped_in = true;
223 state.should_notify_at = Some((true, *now + self.delay));
224 state.last_size = Some(size);
225 }
226
227 match &state.should_notify_at {
228 Some((has_popped_in, at)) if at <= now => {
229 if *has_popped_in {
230 if let Some(on_show) = &self.on_show {
231 shell.publish(on_show(layout.bounds().size()));
232 }
233 } else if let Some(on_hide) = self.on_hide.take() {
234 shell.publish(on_hide);
235 }
236
237 state.should_notify_at = None;
238 }
239 Some((_, at)) => {
240 shell.request_redraw_at(*at);
241 }
242 None => {}
243 }
244 }
245
246 self.content.as_widget_mut().update(
247 &mut tree.children[0],
248 event,
249 layout,
250 cursor,
251 renderer,
252 clipboard,
253 shell,
254 viewport,
255 );
256 }
257
258 fn size(&self) -> Size<Length> {
259 self.content.as_widget().size()
260 }
261
262 fn size_hint(&self) -> Size<Length> {
263 self.content.as_widget().size_hint()
264 }
265
266 fn layout(
267 &mut self,
268 tree: &mut Tree,
269 renderer: &Renderer,
270 limits: &layout::Limits,
271 ) -> layout::Node {
272 self.content
273 .as_widget_mut()
274 .layout(&mut tree.children[0], renderer, limits)
275 }
276
277 fn draw(
278 &self,
279 tree: &Tree,
280 renderer: &mut Renderer,
281 theme: &Theme,
282 style: &renderer::Style,
283 layout: layout::Layout<'_>,
284 cursor: mouse::Cursor,
285 viewport: &Rectangle,
286 ) {
287 self.content.as_widget().draw(
288 &tree.children[0],
289 renderer,
290 theme,
291 style,
292 layout,
293 cursor,
294 viewport,
295 );
296 }
297
298 fn operate(
299 &mut self,
300 tree: &mut Tree,
301 layout: core::Layout<'_>,
302 renderer: &Renderer,
303 operation: &mut dyn widget::Operation,
304 ) {
305 self.content
306 .as_widget_mut()
307 .operate(&mut tree.children[0], layout, renderer, operation);
308 }
309
310 fn mouse_interaction(
311 &self,
312 tree: &Tree,
313 layout: core::Layout<'_>,
314 cursor: mouse::Cursor,
315 viewport: &Rectangle,
316 renderer: &Renderer,
317 ) -> mouse::Interaction {
318 self.content.as_widget().mouse_interaction(
319 &tree.children[0],
320 layout,
321 cursor,
322 viewport,
323 renderer,
324 )
325 }
326
327 fn overlay<'b>(
328 &'b mut self,
329 tree: &'b mut Tree,
330 layout: core::Layout<'b>,
331 renderer: &Renderer,
332 viewport: &Rectangle,
333 translation: core::Vector,
334 ) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
335 self.content.as_widget_mut().overlay(
336 &mut tree.children[0],
337 layout,
338 renderer,
339 viewport,
340 translation,
341 )
342 }
343}
344
345impl<'a, Key, Message, Theme, Renderer> From<Sensor<'a, Key, Message, Theme, Renderer>>
346 for Element<'a, Message, Theme, Renderer>
347where
348 Message: 'a,
349 Key: self::Key + 'a,
350 Renderer: core::Renderer + 'a,
351 Theme: 'a,
352{
353 fn from(pop: Sensor<'a, Key, Message, Theme, Renderer>) -> Self {
354 Element::new(pop)
355 }
356}
357
358pub trait Key {
362 type Owned: 'static;
364
365 fn to_owned(&self) -> Self::Owned;
367
368 fn eq(&self, other: &Self::Owned) -> bool;
370}
371
372impl<T> Key for &T
373where
374 T: ToOwned + PartialEq<T::Owned> + ?Sized,
375 T::Owned: 'static,
376{
377 type Owned = T::Owned;
378
379 fn to_owned(&self) -> <Self as Key>::Owned {
380 ToOwned::to_owned(*self)
381 }
382
383 fn eq(&self, other: &Self::Owned) -> bool {
384 *self == other
385 }
386}
387
388struct OwnedKey<T>(T);
389
390impl<T> Key for OwnedKey<T>
391where
392 T: PartialEq + Clone + 'static,
393{
394 type Owned = T;
395
396 fn to_owned(&self) -> Self::Owned {
397 self.0.clone()
398 }
399
400 fn eq(&self, other: &Self::Owned) -> bool {
401 &self.0 == other
402 }
403}
404
405impl<T> PartialEq<T> for OwnedKey<T>
406where
407 T: PartialEq,
408{
409 fn eq(&self, other: &T) -> bool {
410 &self.0 == other
411 }
412}
413
414impl Key for () {
415 type Owned = ();
416
417 fn to_owned(&self) -> Self::Owned {}
418
419 fn eq(&self, _other: &Self::Owned) -> bool {
420 true
421 }
422}