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