iced_native/widget/
mouse_area.rs

1//! A container for capturing mouse events.
2
3use crate::event::{self, Event};
4use crate::layout;
5use crate::mouse;
6use crate::overlay;
7use crate::renderer;
8use crate::touch;
9use crate::widget::{tree, Operation, Tree};
10use crate::{
11    Clipboard, Element, Layout, Length, Point, Rectangle, Shell, Widget,
12};
13
14/// Emit messages on mouse events.
15#[allow(missing_debug_implementations)]
16pub struct MouseArea<'a, Message, Renderer> {
17    content: Element<'a, Message, Renderer>,
18    on_press: Option<Message>,
19    on_release: Option<Message>,
20    on_right_press: Option<Message>,
21    on_right_release: Option<Message>,
22    on_middle_press: Option<Message>,
23    on_middle_release: Option<Message>,
24}
25
26impl<'a, Message, Renderer> MouseArea<'a, Message, Renderer> {
27    /// The message to emit on a left button press.
28    #[must_use]
29    pub fn on_press(mut self, message: Message) -> Self {
30        self.on_press = Some(message);
31        self
32    }
33
34    /// The message to emit on a left button release.
35    #[must_use]
36    pub fn on_release(mut self, message: Message) -> Self {
37        self.on_release = Some(message);
38        self
39    }
40
41    /// The message to emit on a right button press.
42    #[must_use]
43    pub fn on_right_press(mut self, message: Message) -> Self {
44        self.on_right_press = Some(message);
45        self
46    }
47
48    /// The message to emit on a right button release.
49    #[must_use]
50    pub fn on_right_release(mut self, message: Message) -> Self {
51        self.on_right_release = Some(message);
52        self
53    }
54
55    /// The message to emit on a middle button press.
56    #[must_use]
57    pub fn on_middle_press(mut self, message: Message) -> Self {
58        self.on_middle_press = Some(message);
59        self
60    }
61
62    /// The message to emit on a middle button release.
63    #[must_use]
64    pub fn on_middle_release(mut self, message: Message) -> Self {
65        self.on_middle_release = Some(message);
66        self
67    }
68}
69
70/// Local state of the [`MouseArea`].
71#[derive(Default)]
72struct State {
73    // TODO: Support on_mouse_enter and on_mouse_exit
74}
75
76impl<'a, Message, Renderer> MouseArea<'a, Message, Renderer> {
77    /// Creates a [`MouseArea`] with the given content.
78    pub fn new(content: impl Into<Element<'a, Message, Renderer>>) -> Self {
79        MouseArea {
80            content: content.into(),
81            on_press: None,
82            on_release: None,
83            on_right_press: None,
84            on_right_release: None,
85            on_middle_press: None,
86            on_middle_release: None,
87        }
88    }
89}
90
91impl<'a, Message, Renderer> Widget<Message, Renderer>
92    for MouseArea<'a, Message, Renderer>
93where
94    Renderer: crate::Renderer,
95    Message: Clone,
96{
97    fn tag(&self) -> tree::Tag {
98        tree::Tag::of::<State>()
99    }
100
101    fn state(&self) -> tree::State {
102        tree::State::new(State::default())
103    }
104
105    fn children(&self) -> Vec<Tree> {
106        vec![Tree::new(&self.content)]
107    }
108
109    fn diff(&self, tree: &mut Tree) {
110        tree.diff_children(std::slice::from_ref(&self.content));
111    }
112
113    fn width(&self) -> Length {
114        self.content.as_widget().width()
115    }
116
117    fn height(&self) -> Length {
118        self.content.as_widget().height()
119    }
120
121    fn layout(
122        &self,
123        renderer: &Renderer,
124        limits: &layout::Limits,
125    ) -> layout::Node {
126        self.content.as_widget().layout(renderer, limits)
127    }
128
129    fn operate(
130        &self,
131        tree: &mut Tree,
132        layout: Layout<'_>,
133        renderer: &Renderer,
134        operation: &mut dyn Operation<Message>,
135    ) {
136        self.content.as_widget().operate(
137            &mut tree.children[0],
138            layout,
139            renderer,
140            operation,
141        );
142    }
143
144    fn on_event(
145        &mut self,
146        tree: &mut Tree,
147        event: Event,
148        layout: Layout<'_>,
149        cursor_position: Point,
150        renderer: &Renderer,
151        clipboard: &mut dyn Clipboard,
152        shell: &mut Shell<'_, Message>,
153    ) -> event::Status {
154        if let event::Status::Captured = self.content.as_widget_mut().on_event(
155            &mut tree.children[0],
156            event.clone(),
157            layout,
158            cursor_position,
159            renderer,
160            clipboard,
161            shell,
162        ) {
163            return event::Status::Captured;
164        }
165
166        update(self, &event, layout, cursor_position, shell)
167    }
168
169    fn mouse_interaction(
170        &self,
171        tree: &Tree,
172        layout: Layout<'_>,
173        cursor_position: Point,
174        viewport: &Rectangle,
175        renderer: &Renderer,
176    ) -> mouse::Interaction {
177        self.content.as_widget().mouse_interaction(
178            &tree.children[0],
179            layout,
180            cursor_position,
181            viewport,
182            renderer,
183        )
184    }
185
186    fn draw(
187        &self,
188        tree: &Tree,
189        renderer: &mut Renderer,
190        theme: &Renderer::Theme,
191        renderer_style: &renderer::Style,
192        layout: Layout<'_>,
193        cursor_position: Point,
194        viewport: &Rectangle,
195    ) {
196        self.content.as_widget().draw(
197            &tree.children[0],
198            renderer,
199            theme,
200            renderer_style,
201            layout,
202            cursor_position,
203            viewport,
204        );
205    }
206
207    fn overlay<'b>(
208        &'b mut self,
209        tree: &'b mut Tree,
210        layout: Layout<'_>,
211        renderer: &Renderer,
212    ) -> Option<overlay::Element<'b, Message, Renderer>> {
213        self.content.as_widget_mut().overlay(
214            &mut tree.children[0],
215            layout,
216            renderer,
217        )
218    }
219}
220
221impl<'a, Message, Renderer> From<MouseArea<'a, Message, Renderer>>
222    for Element<'a, Message, Renderer>
223where
224    Message: 'a + Clone,
225    Renderer: 'a + crate::Renderer,
226{
227    fn from(
228        area: MouseArea<'a, Message, Renderer>,
229    ) -> Element<'a, Message, Renderer> {
230        Element::new(area)
231    }
232}
233
234/// Processes the given [`Event`] and updates the [`State`] of an [`MouseArea`]
235/// accordingly.
236fn update<Message: Clone, Renderer>(
237    widget: &mut MouseArea<'_, Message, Renderer>,
238    event: &Event,
239    layout: Layout<'_>,
240    cursor_position: Point,
241    shell: &mut Shell<'_, Message>,
242) -> event::Status {
243    if !layout.bounds().contains(cursor_position) {
244        return event::Status::Ignored;
245    }
246
247    if let Some(message) = widget.on_press.as_ref() {
248        if let Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left))
249        | Event::Touch(touch::Event::FingerPressed { .. }) = event
250        {
251            shell.publish(message.clone());
252
253            return event::Status::Captured;
254        }
255    }
256
257    if let Some(message) = widget.on_release.as_ref() {
258        if let Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left))
259        | Event::Touch(touch::Event::FingerLifted { .. }) = event
260        {
261            shell.publish(message.clone());
262
263            return event::Status::Captured;
264        }
265    }
266
267    if let Some(message) = widget.on_right_press.as_ref() {
268        if let Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Right)) =
269            event
270        {
271            shell.publish(message.clone());
272
273            return event::Status::Captured;
274        }
275    }
276
277    if let Some(message) = widget.on_right_release.as_ref() {
278        if let Event::Mouse(mouse::Event::ButtonReleased(
279            mouse::Button::Right,
280        )) = event
281        {
282            shell.publish(message.clone());
283
284            return event::Status::Captured;
285        }
286    }
287
288    if let Some(message) = widget.on_middle_press.as_ref() {
289        if let Event::Mouse(mouse::Event::ButtonPressed(
290            mouse::Button::Middle,
291        )) = event
292        {
293            shell.publish(message.clone());
294
295            return event::Status::Captured;
296        }
297    }
298
299    if let Some(message) = widget.on_middle_release.as_ref() {
300        if let Event::Mouse(mouse::Event::ButtonReleased(
301            mouse::Button::Middle,
302        )) = event
303        {
304            shell.publish(message.clone());
305
306            return event::Status::Captured;
307        }
308    }
309
310    event::Status::Ignored
311}