Skip to main content

iced_widget/
hover.rs

1//! Displays a widget on top of another one, only when the base widget is
2//! hovered.
3
4use crate::core;
5use crate::core::layout::{self, Layout};
6use crate::core::mouse;
7use crate::core::overlay;
8use crate::core::renderer;
9use crate::core::widget::operation;
10use crate::core::widget::tree::{self, Tree};
11use crate::core::widget::{Meta, Operation};
12use crate::core::window;
13use crate::core::{Event, Length, Rectangle, Shell, Size, Vector, Widget};
14
15/// A widget that displays another widget on top of it.
16///
17/// This works analogously to a [`stack`](crate::Stack), but it will only
18/// display the layer on top when the cursor is over the base. It can be
19/// useful for removing visual clutter.
20pub struct Hover<W, V> {
21    base: W,
22    top: V,
23    is_top_focused: bool,
24    is_top_overlay_active: bool,
25    is_hovered: bool,
26}
27
28impl<W, V> Hover<W, V> {
29    /// Creates a new [`Hover`] widget with the given base and top widgets.
30    pub fn new(base: W, top: V) -> Self {
31        Self {
32            base,
33            top,
34            is_top_focused: false,
35            is_top_overlay_active: false,
36            is_hovered: false,
37        }
38    }
39}
40
41impl<W, V> Meta for Hover<W, V> {}
42
43impl<W, V, Message, Theme, Renderer> Widget<Message, Theme, Renderer> for Hover<W, V>
44where
45    Renderer: core::Renderer,
46    W: Widget<Message, Theme, Renderer>,
47    V: Widget<Message, Theme, Renderer>,
48{
49    fn tag(&self) -> tree::Tag {
50        struct Tag;
51        tree::Tag::of::<Tag>()
52    }
53
54    fn diff(&mut self, tree: &mut Tree) {
55        if tree.children.len() != 2 {
56            tree.children = vec![Tree::new(&self.base), Tree::new(&self.top)];
57        }
58
59        tree.children[0].diff(&mut self.base);
60        tree.children[1].diff(&mut self.top);
61    }
62
63    fn size(&self) -> Size<Length> {
64        self.base.size()
65    }
66
67    fn layout(&mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits) {
68        self.base.layout(&mut tree.children[0], renderer, limits);
69
70        let base_size = tree.children[0].size;
71
72        self.top.layout(
73            &mut tree.children[1],
74            renderer,
75            &layout::Limits::new(Size::ZERO, base_size),
76        );
77
78        tree.size = base_size;
79    }
80
81    fn draw(
82        &self,
83        tree: &Tree,
84        renderer: &mut Renderer,
85        theme: &Theme,
86        style: &renderer::Style,
87        layout: Layout,
88        cursor: mouse::Cursor,
89        viewport: &Rectangle,
90    ) {
91        if let Some(bounds) = layout.bounds().intersection(viewport) {
92            let mut children = layout.iter(&tree.children);
93
94            let (base_layout, base_tree) = children.next().unwrap();
95
96            self.base.draw(
97                base_tree,
98                renderer,
99                theme,
100                style,
101                base_layout,
102                cursor,
103                viewport,
104            );
105
106            if cursor.is_over(layout.bounds()) || self.is_top_focused || self.is_top_overlay_active
107            {
108                let (top_layout, top_tree) = children.next().unwrap();
109
110                renderer.with_layer(bounds, |renderer| {
111                    self.top.draw(
112                        top_tree, renderer, theme, style, top_layout, cursor, viewport,
113                    );
114                });
115            }
116        }
117    }
118
119    fn operate(
120        &mut self,
121        tree: &mut Tree,
122        layout: Layout,
123        viewport: &Rectangle,
124        renderer: &Renderer,
125        operation: &mut dyn Operation,
126    ) {
127        let mut children = layout.iter_mut(&mut tree.children);
128
129        let (base_layout, base_tree) = children.next().unwrap();
130        let (top_layout, top_tree) = children.next().unwrap();
131
132        self.base
133            .operate(base_tree, base_layout, viewport, renderer, operation);
134        self.top
135            .operate(top_tree, top_layout, viewport, renderer, operation);
136    }
137
138    fn update(
139        &mut self,
140        tree: &mut Tree,
141        event: &Event,
142        layout: Layout,
143        cursor: mouse::Cursor,
144        renderer: &Renderer,
145        shell: &mut Shell<'_, Message>,
146        viewport: &Rectangle,
147    ) {
148        let mut children = layout.iter_mut(&mut tree.children);
149
150        let (base_layout, base_tree) = children.next().unwrap();
151        let (top_layout, top_tree) = children.next().unwrap();
152
153        let is_hovered = cursor.is_over(layout.bounds());
154
155        if matches!(event, Event::Window(window::Event::RedrawRequested(_))) {
156            let mut count_focused = operation::focusable::count();
157
158            self.top.operate(
159                top_tree,
160                top_layout,
161                viewport,
162                renderer,
163                &mut operation::black_box(&mut count_focused),
164            );
165
166            self.is_top_focused = match count_focused.finish() {
167                operation::Outcome::Some(count) => count.focused.is_some(),
168                _ => false,
169            };
170
171            self.is_hovered = is_hovered;
172        } else if is_hovered != self.is_hovered {
173            shell.request_redraw();
174        }
175
176        let is_visible = is_hovered || self.is_top_focused || self.is_top_overlay_active;
177
178        if matches!(
179            event,
180            Event::Mouse(mouse::Event::CursorMoved { .. } | mouse::Event::ButtonReleased(_))
181        ) || is_visible
182        {
183            let redraw_request = shell.redraw_request();
184
185            self.top.update(
186                top_tree, event, top_layout, cursor, renderer, shell, viewport,
187            );
188
189            // Ignore redraw requests of invisible content
190            if !is_visible {
191                Shell::replace_redraw_request(shell, redraw_request);
192            }
193
194            if shell.is_event_captured() {
195                return;
196            }
197        };
198
199        self.base.update(
200            base_tree,
201            event,
202            base_layout,
203            cursor,
204            renderer,
205            shell,
206            viewport,
207        );
208    }
209
210    fn mouse_interaction(
211        &self,
212        tree: &Tree,
213        layout: Layout,
214        cursor: mouse::Cursor,
215        viewport: &Rectangle,
216        renderer: &Renderer,
217    ) -> mouse::Interaction {
218        let mut children = layout.iter(&tree.children).rev();
219
220        let (top_layout, top_tree) = children.next().unwrap();
221        let interaction = self
222            .top
223            .mouse_interaction(top_tree, top_layout, cursor, viewport, renderer);
224
225        if interaction != mouse::Interaction::None {
226            return interaction;
227        }
228
229        let (base_layout, base_tree) = children.next().unwrap();
230        self.base
231            .mouse_interaction(base_tree, base_layout, cursor, viewport, renderer)
232    }
233
234    fn overlay<'b>(
235        &'b mut self,
236        tree: &'b mut Tree,
237        layout: Layout,
238        renderer: &Renderer,
239        viewport: &Rectangle,
240        translation: Vector,
241        window: Size,
242    ) -> Vec<overlay::Element<'b, Message, Theme, Renderer>> {
243        let mut children = layout.iter_mut(&mut tree.children);
244
245        let (base_layout, base_tree) = children.next().unwrap();
246        let (top_layout, top_tree) = children.next().unwrap();
247
248        let base_overlays = self.base.overlay(
249            base_tree,
250            base_layout,
251            renderer,
252            viewport,
253            translation,
254            window,
255        );
256        let top_overlays = self.top.overlay(
257            top_tree,
258            top_layout,
259            renderer,
260            viewport,
261            translation,
262            window,
263        );
264
265        self.is_top_overlay_active = !top_overlays.is_empty();
266
267        base_overlays.into_iter().chain(top_overlays).collect()
268    }
269}