1use crate::core;
39use crate::core::layout;
40use crate::core::mouse;
41use crate::core::overlay;
42use crate::core::renderer;
43use crate::core::widget;
44use crate::core::window;
45use crate::core::{Event, Layout, Length, Point, Rectangle, Shell, Size, Vector, Widget};
46
47pub struct Sticky<W> {
89 content: W,
90}
91
92impl<W> Sticky<W> {
93 pub fn new(content: W) -> Self {
95 Self { content }
96 }
97}
98
99struct State {
100 is_stuck: bool,
101}
102
103impl<W> widget::Meta for Sticky<W> {}
104
105impl<W, Message, Theme, Renderer> Widget<Message, Theme, Renderer> for Sticky<W>
106where
107 Renderer: core::Renderer,
108 W: Widget<Message, Theme, Renderer>,
109{
110 fn tag(&self) -> widget::tree::Tag {
111 widget::tree::Tag::of::<State>()
112 }
113
114 fn state(&self) -> widget::tree::State {
115 widget::tree::State::new(State { is_stuck: false })
116 }
117
118 fn diff(&mut self, tree: &mut widget::Tree) {
119 tree.diff_children(&mut [&mut self.content]);
120 }
121
122 fn size(&self) -> Size<Length> {
123 self.content.size()
124 }
125
126 fn layout(&mut self, tree: &mut widget::Tree, renderer: &Renderer, limits: &layout::Limits) {
127 self.content.layout(&mut tree.children[0], renderer, limits);
128
129 tree.size = tree.children[0].size;
130 }
131
132 fn operate(
133 &mut self,
134 tree: &mut widget::Tree,
135 layout: Layout,
136 viewport: &Rectangle,
137 renderer: &Renderer,
138 operation: &mut dyn widget::Operation,
139 ) {
140 let state = tree.state.downcast_ref::<State>();
141
142 if state.is_stuck {
143 return;
144 }
145
146 self.content
147 .operate(&mut tree.children[0], layout, viewport, renderer, operation);
148 }
149
150 fn update(
151 &mut self,
152 tree: &mut widget::Tree,
153 event: &Event,
154 layout: Layout,
155 cursor: mouse::Cursor,
156 renderer: &Renderer,
157 shell: &mut Shell<'_, Message>,
158 viewport: &Rectangle,
159 ) {
160 let state = tree.state.downcast_mut::<State>();
161
162 if let Event::Window(window::Event::RedrawRequested(_)) = event {
163 let is_stuck = if let Some(parent) = layout.parent()
164 && parent.intersects(viewport)
165 && !layout.bounds().is_within(viewport)
166 {
167 true
168 } else {
169 false
170 };
171
172 if is_stuck != state.is_stuck {
173 state.is_stuck = is_stuck;
174 shell.invalidate_overlay();
175 return; }
177 }
178
179 if state.is_stuck {
180 return;
181 }
182
183 self.content.update(
184 &mut tree.children[0],
185 event,
186 layout,
187 cursor,
188 renderer,
189 shell,
190 viewport,
191 );
192 }
193
194 fn mouse_interaction(
195 &self,
196 tree: &widget::Tree,
197 layout: Layout,
198 cursor: mouse::Cursor,
199 viewport: &Rectangle,
200 renderer: &Renderer,
201 ) -> mouse::Interaction {
202 let state = tree.state.downcast_ref::<State>();
203
204 if state.is_stuck {
205 return mouse::Interaction::None;
206 }
207
208 self.content
209 .mouse_interaction(&tree.children[0], layout, cursor, viewport, renderer)
210 }
211
212 fn draw(
213 &self,
214 tree: &widget::Tree,
215 renderer: &mut Renderer,
216 theme: &Theme,
217 style: &renderer::Style,
218 layout: Layout,
219 cursor: mouse::Cursor,
220 viewport: &Rectangle,
221 ) {
222 let state = tree.state.downcast_ref::<State>();
223
224 if state.is_stuck {
225 return;
226 }
227
228 self.content.draw(
229 &tree.children[0],
230 renderer,
231 theme,
232 style,
233 layout,
234 cursor,
235 viewport,
236 );
237 }
238
239 fn overlay<'b>(
240 &'b mut self,
241 tree: &'b mut widget::Tree,
242 layout: Layout,
243 renderer: &Renderer,
244 viewport: &Rectangle,
245 translation: Vector,
246 window: Size,
247 ) -> Vec<overlay::Element<'b, Message, Theme, Renderer>> {
248 let bounds = layout.bounds();
249 let parent = layout.parent();
250 let state = tree.state.downcast_ref::<State>();
251
252 if let Some(parent) = parent
253 && state.is_stuck
254 {
255 let viewport = *viewport + translation;
256 let bounds = bounds + translation;
257 let parent = parent + translation;
258
259 let position = Point::new(
260 stuck_axis(
261 bounds.x,
262 bounds.width,
263 viewport.x,
264 viewport.width,
265 (parent.x, parent.x + parent.width),
266 ),
267 stuck_axis(
268 bounds.y,
269 bounds.height,
270 viewport.y,
271 viewport.height,
272 (parent.y, parent.y + parent.height),
273 ),
274 );
275
276 vec![overlay::Element::new(Box::new(Overlay {
277 content: &mut self.content,
278 tree: &mut tree.children[0],
279 layout: layout.move_to(position),
280 viewport,
281 window,
282 }))]
283 } else {
284 self.content.overlay(
285 &mut tree.children[0],
286 layout,
287 renderer,
288 viewport,
289 translation,
290 window,
291 )
292 }
293 }
294}
295
296fn stuck_axis(
309 position: f32,
310 size: f32,
311 viewport: f32,
312 viewport_size: f32,
313 parent: (f32, f32),
314) -> f32 {
315 let viewport_max = viewport + viewport_size - size;
316 let (parent_min, parent_max) = parent;
317 let parent_max = parent_max - size;
318
319 let min = viewport.max(parent_min);
320 let max = viewport_max.min(parent_max);
321
322 if min <= max {
323 position.clamp(min, max)
324 } else if parent_max < viewport {
325 parent_max
327 } else {
328 parent_min
330 }
331}
332
333struct Overlay<'b, W> {
334 content: &'b mut W,
335 tree: &'b mut widget::Tree,
336 layout: Layout,
337 viewport: Rectangle,
338 window: Size,
339}
340
341impl<W> Overlay<'_, W> {
342 fn bounds(&self) -> Rectangle {
343 self.viewport
344 .intersection(&self.layout.bounds())
345 .unwrap_or(self.layout.bounds())
346 }
347}
348
349impl<W, Message, Theme, Renderer> core::Overlay<Message, Theme, Renderer> for Overlay<'_, W>
350where
351 Renderer: core::Renderer,
352 W: Widget<Message, Theme, Renderer>,
353{
354 fn operate(&mut self, renderer: &Renderer, operation: &mut dyn widget::Operation) {
355 let bounds = self.bounds();
356
357 self.content
358 .operate(self.tree, self.layout, &bounds, renderer, operation);
359 }
360
361 fn update(
362 &mut self,
363 event: &Event,
364 cursor: mouse::Cursor,
365 renderer: &Renderer,
366 shell: &mut Shell<'_, Message>,
367 ) {
368 let bounds = self.bounds();
369
370 self.content.update(
371 self.tree,
372 event,
373 self.layout,
374 cursor,
375 renderer,
376 shell,
377 &bounds,
378 );
379 }
380
381 fn mouse_interaction(&self, cursor: mouse::Cursor, renderer: &Renderer) -> mouse::Interaction {
382 let bounds = self.bounds();
383
384 let interaction =
385 self.content
386 .mouse_interaction(self.tree, self.layout, cursor, &bounds, renderer);
387
388 if interaction == mouse::Interaction::None && cursor.is_over(bounds) {
389 mouse::Interaction::Idle
390 } else {
391 interaction
392 }
393 }
394
395 fn draw(
396 &self,
397 renderer: &mut Renderer,
398 theme: &Theme,
399 style: &renderer::Style,
400 cursor: mouse::Cursor,
401 ) {
402 let bounds = self.bounds();
403
404 renderer.with_layer(bounds, |renderer| {
405 self.content.draw(
406 self.tree,
407 renderer,
408 theme,
409 style,
410 self.layout,
411 cursor,
412 &bounds,
413 );
414 });
415 }
416
417 fn overlay<'c>(
418 &'c mut self,
419 renderer: &Renderer,
420 ) -> Vec<overlay::Element<'c, Message, Theme, Renderer>> {
421 self.content.overlay(
422 self.tree,
423 self.layout,
424 renderer,
425 &self.viewport,
426 Vector::ZERO,
427 self.window,
428 )
429 }
430}