Skip to main content

iced_widget/
opaque.rs

1//! Wraps the given widget and captures any mouse button presses inside the bounds of
2//! the widget—effectively making it _opaque_.
3//!
4//! This widget is meant to be used to mark elements in a [`Stack`], or inside an
5//! overlay—like the popup of a [`Popover`]—to avoid mouse events from passing
6//! through to the elements below.
7//!
8//! By default, only mouse button presses are captured. Use
9//! [`Opaque::capture_scroll`] to also capture scroll wheel events, so that
10//! scrolling does not pass through to the elements below.
11//!
12//! [`Stack`]: crate::Stack
13//! [`Popover`]: crate::popover::Popover
14//!
15//! # Example
16//! ```no_run
17//! # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; }
18//! # use iced::widget::Widget;
19//! # pub type State = ();
20//! use iced::widget::{button, opaque, stack, text};
21//!
22//! #[derive(Clone)]
23//! enum Message {
24//!     ButtonPressed,
25//! }
26//!
27//! fn view(state: &State) -> impl Widget<Message> {
28//!     stack![
29//!         // This button lies below the opaque top layer: mouse events over
30//!         // the top layer do not pass through to it.
31//!         button("Click me!").on_press(Message::ButtonPressed),
32//!         opaque(text("I am opaque")),
33//!     ]
34//! }
35//! ```
36use crate::core::layout;
37use crate::core::mouse;
38use crate::core::overlay;
39use crate::core::renderer;
40use crate::core::widget::tree::{self, Tree};
41use crate::core::widget::{self, Operation, Widget};
42use crate::core::{Element, Event, Layout, Length, Rectangle, Shell, Size, Vector};
43
44/// Wraps the given widget and captures any mouse button presses inside the bounds of
45/// the widget—effectively making it _opaque_.
46///
47/// This widget is meant to be used to mark elements in a [`Stack`], or inside an
48/// overlay—like the popup of a [`Popover`]—to avoid mouse events from passing
49/// through to the elements below.
50///
51/// By default, only mouse button presses are captured. Use
52/// [`Self::capture_scroll`] to also capture scroll wheel events, so that
53/// scrolling does not pass through to the elements below.
54///
55/// [`Stack`]: crate::Stack
56/// [`Popover`]: crate::popover::Popover
57///
58/// # Example
59/// ```no_run
60/// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; }
61/// # use iced::widget::Widget;
62/// # pub type State = ();
63/// use iced::widget::{button, opaque, stack, text};
64///
65/// #[derive(Clone)]
66/// enum Message {
67///     ButtonPressed,
68/// }
69///
70/// fn view(state: &State) -> impl Widget<Message> {
71///     stack![
72///         // This button lies below the opaque top layer: mouse events over
73///         // the top layer do not pass through to it.
74///         button("Click me!").on_press(Message::ButtonPressed),
75///         opaque(text("I am opaque")),
76///     ]
77/// }
78/// ```
79pub struct Opaque<W> {
80    content: W,
81    capture_scroll: bool,
82}
83
84impl<W> Opaque<W> {
85    /// Creates a new [`Opaque`].
86    pub fn new(content: W) -> Self {
87        Opaque {
88            content,
89            capture_scroll: false,
90        }
91    }
92
93    /// Sets whether the scroll wheel should be captured when the cursor is
94    /// over the bounds of the [`Opaque`].
95    ///
96    /// By default, only mouse button presses are captured. Enabling this flag
97    /// also captures scroll wheel events, preventing them from passing through
98    /// to the elements below.
99    pub fn capture_scroll(mut self, capture: bool) -> Self {
100        self.capture_scroll = capture;
101        self
102    }
103
104    /// Returns the wrapped content.
105    pub fn into_inner(self) -> W {
106        self.content
107    }
108}
109
110impl<W> widget::Meta for Opaque<W> {}
111
112impl<W, Message, Theme, Renderer> Widget<Message, Theme, Renderer> for Opaque<W>
113where
114    W: Widget<Message, Theme, Renderer>,
115{
116    fn tag(&self) -> tree::Tag {
117        self.content.tag()
118    }
119
120    fn state(&self) -> tree::State {
121        self.content.state()
122    }
123
124    fn diff(&mut self, tree: &mut Tree) {
125        tree.diff_children(std::slice::from_mut(&mut self.content));
126    }
127
128    fn size(&self) -> Size<Length> {
129        self.content.size()
130    }
131
132    fn layout(&mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits) {
133        self.content.layout(&mut tree.children[0], renderer, limits);
134
135        tree.size = tree.children[0].size;
136    }
137
138    fn draw(
139        &self,
140        tree: &Tree,
141        renderer: &mut Renderer,
142        theme: &Theme,
143        style: &renderer::Style,
144        layout: Layout,
145        cursor: mouse::Cursor,
146        viewport: &Rectangle,
147    ) {
148        let (layout, tree) = layout.iter(&tree.children).next().unwrap();
149
150        self.content
151            .draw(tree, renderer, theme, style, layout, cursor, viewport);
152    }
153
154    fn operate(
155        &mut self,
156        tree: &mut Tree,
157        layout: Layout,
158        viewport: &Rectangle,
159        renderer: &Renderer,
160        operation: &mut dyn Operation,
161    ) {
162        let (layout, tree) = layout.iter_mut(&mut tree.children).next().unwrap();
163
164        self.content
165            .operate(tree, layout, viewport, renderer, operation);
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        shell: &mut Shell<'_, Message>,
176        viewport: &Rectangle,
177    ) {
178        let (layout, tree) = layout.iter_mut(&mut tree.children).next().unwrap();
179
180        let is_mouse_press = matches!(event, Event::Mouse(mouse::Event::ButtonPressed(_)));
181        let is_wheel_scroll = matches!(event, Event::Mouse(mouse::Event::WheelScrolled { .. }));
182
183        self.content
184            .update(tree, event, layout, cursor, renderer, shell, viewport);
185
186        if (is_mouse_press || (self.capture_scroll && is_wheel_scroll))
187            && cursor.is_over(layout.bounds())
188        {
189            shell.capture_event();
190        }
191    }
192
193    fn mouse_interaction(
194        &self,
195        tree: &Tree,
196        layout: Layout,
197        cursor: mouse::Cursor,
198        viewport: &Rectangle,
199        renderer: &Renderer,
200    ) -> mouse::Interaction {
201        let (layout, tree) = layout.iter(&tree.children).next().unwrap();
202
203        let interaction = self
204            .content
205            .mouse_interaction(tree, layout, cursor, viewport, renderer);
206
207        if interaction == mouse::Interaction::None && cursor.is_over(layout.bounds()) {
208            mouse::Interaction::Idle
209        } else {
210            interaction
211        }
212    }
213
214    fn overlay<'b>(
215        &'b mut self,
216        tree: &'b mut Tree,
217        layout: Layout,
218        renderer: &Renderer,
219        viewport: &Rectangle,
220        translation: Vector,
221        window: Size,
222    ) -> Vec<overlay::Element<'b, Message, Theme, Renderer>> {
223        let (layout, tree) = layout.iter_mut(&mut tree.children).next().unwrap();
224
225        self.content
226            .overlay(tree, layout, renderer, viewport, translation, window)
227    }
228}
229
230impl<'a, W, Message, Theme, Renderer> From<Opaque<W>> for Element<'a, Message, Theme, Renderer>
231where
232    Message: 'a,
233    Theme: 'a,
234    Renderer: 'a,
235    W: Widget<Message, Theme, Renderer> + 'a,
236{
237    fn from(opaque: Opaque<W>) -> Element<'a, Message, Theme, Renderer> {
238        opaque._boxed()
239    }
240}