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 Renderer: core::Renderer,
145 W: Widget<Message, Theme, 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 && let Some(message) = on_resize(size)
197 {
198 state.last_size = Some(size);
199 shell.publish(message);
200 }
201 }
202 } else if state.has_popped_in {
203 if distance <= self.anticipate.0 {
204 if let Some(on_resize) = &self.on_resize {
205 let size = bounds.size();
206
207 if Some(size) != state.last_size
208 && let Some(message) = on_resize(size)
209 {
210 state.last_size = Some(size);
211 shell.publish(message);
212 }
213 }
214 } else if self.on_hide.is_some() {
215 state.has_popped_in = false;
216 state.should_notify_at = Some((false, *now + self.delay));
217 }
218 } else if distance <= self.anticipate.0 {
219 let size = bounds.size();
220
221 state.has_popped_in = true;
222 state.should_notify_at = Some((true, *now + self.delay));
223 state.last_size = Some(size);
224 }
225
226 match &state.should_notify_at {
227 Some((has_popped_in, at)) if at <= now => {
228 if *has_popped_in {
229 if let Some(on_show) = &self.on_show {
230 shell.publish(on_show(layout.bounds().size()));
231 }
232 } else if let Some(on_hide) = self.on_hide.take() {
233 shell.publish(on_hide);
234 }
235
236 state.should_notify_at = None;
237 }
238 Some((_, at)) => {
239 shell.request_redraw_at(*at);
240 }
241 None => {}
242 }
243 }
244
245 self.content.update(
246 &mut tree.children[0],
247 event,
248 layout,
249 cursor,
250 renderer,
251 shell,
252 viewport,
253 );
254 }
255
256 fn size(&self) -> Size<Length> {
257 self.content.size()
258 }
259
260 fn layout(&mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits) {
261 self.content.layout(&mut tree.children[0], renderer, limits);
262
263 tree.size = tree.children[0].size;
264 }
265
266 fn draw(
267 &self,
268 tree: &Tree,
269 renderer: &mut Renderer,
270 theme: &Theme,
271 style: &renderer::Style,
272 layout: layout::Layout,
273 cursor: mouse::Cursor,
274 viewport: &Rectangle,
275 ) {
276 self.content.draw(
277 &tree.children[0],
278 renderer,
279 theme,
280 style,
281 layout,
282 cursor,
283 viewport,
284 );
285 }
286
287 fn operate(
288 &mut self,
289 tree: &mut Tree,
290 layout: core::Layout,
291 viewport: &Rectangle,
292 renderer: &Renderer,
293 operation: &mut dyn widget::Operation,
294 ) {
295 self.content
296 .operate(&mut tree.children[0], layout, viewport, renderer, operation);
297 }
298
299 fn mouse_interaction(
300 &self,
301 tree: &Tree,
302 layout: core::Layout,
303 cursor: mouse::Cursor,
304 viewport: &Rectangle,
305 renderer: &Renderer,
306 ) -> mouse::Interaction {
307 self.content
308 .mouse_interaction(&tree.children[0], layout, cursor, viewport, renderer)
309 }
310
311 fn overlay<'b>(
312 &'b mut self,
313 tree: &'b mut Tree,
314 layout: core::Layout,
315 renderer: &Renderer,
316 viewport: &Rectangle,
317 translation: core::Vector,
318 window: Size,
319 ) -> Vec<overlay::Element<'b, Message, Theme, Renderer>> {
320 self.content.overlay(
321 &mut tree.children[0],
322 layout,
323 renderer,
324 viewport,
325 translation,
326 window,
327 )
328 }
329}
330
331pub trait Key {
335 type Owned: 'static;
337
338 fn to_owned(&self) -> Self::Owned;
340
341 fn eq(&self, other: &Self::Owned) -> bool;
343}
344
345impl<T> Key for &T
346where
347 T: ToOwned + PartialEq<T::Owned> + ?Sized,
348 T::Owned: 'static,
349{
350 type Owned = T::Owned;
351
352 fn to_owned(&self) -> <Self as Key>::Owned {
353 ToOwned::to_owned(*self)
354 }
355
356 fn eq(&self, other: &Self::Owned) -> bool {
357 *self == other
358 }
359}
360
361struct OwnedKey<T>(T);
362
363impl<T> Key for OwnedKey<T>
364where
365 T: PartialEq + Clone + 'static,
366{
367 type Owned = T;
368
369 fn to_owned(&self) -> Self::Owned {
370 self.0.clone()
371 }
372
373 fn eq(&self, other: &Self::Owned) -> bool {
374 &self.0 == other
375 }
376}
377
378impl<T> PartialEq<T> for OwnedKey<T>
379where
380 T: PartialEq,
381{
382 fn eq(&self, other: &T) -> bool {
383 &self.0 == other
384 }
385}
386
387impl Key for () {
388 type Owned = ();
389
390 fn to_owned(&self) -> Self::Owned {}
391
392 fn eq(&self, _other: &Self::Owned) -> bool {
393 true
394 }
395}