1use crate::core::layout;
3use crate::core::mouse;
4use crate::core::overlay;
5use crate::core::renderer;
6use crate::core::touch;
7use crate::core::widget::{Meta, Operation, Tree, tree};
8use crate::core::{Event, Layout, Length, Point, Rectangle, Shell, Size, Vector, Widget};
9
10pub struct MouseArea<'a, Message, W = crate::Element<'a, Message>> {
12 content: W,
13 on_press: Option<Message>,
14 on_release: Option<Message>,
15 on_double_click: Option<Message>,
16 on_right_press: Option<Message>,
17 on_right_release: Option<Message>,
18 on_middle_press: Option<Message>,
19 on_middle_release: Option<Message>,
20 on_scroll: Option<Box<dyn Fn(mouse::ScrollDelta) -> Message + 'a>>,
21 on_enter: Option<Message>,
22 on_move: Option<Box<dyn Fn(Point) -> Message + 'a>>,
23 on_exit: Option<Message>,
24 interaction: Option<mouse::Interaction>,
25}
26
27impl<'a, Message, W> MouseArea<'a, Message, W> {
28 #[must_use]
30 pub fn on_press(mut self, message: Message) -> Self {
31 self.on_press = Some(message);
32 self
33 }
34
35 #[must_use]
37 pub fn on_release(mut self, message: Message) -> Self {
38 self.on_release = Some(message);
39 self
40 }
41
42 #[must_use]
53 pub fn on_double_click(mut self, message: Message) -> Self {
54 self.on_double_click = Some(message);
55 self
56 }
57
58 #[must_use]
60 pub fn on_right_press(mut self, message: Message) -> Self {
61 self.on_right_press = Some(message);
62 self
63 }
64
65 #[must_use]
67 pub fn on_right_release(mut self, message: Message) -> Self {
68 self.on_right_release = Some(message);
69 self
70 }
71
72 #[must_use]
74 pub fn on_middle_press(mut self, message: Message) -> Self {
75 self.on_middle_press = Some(message);
76 self
77 }
78
79 #[must_use]
81 pub fn on_middle_release(mut self, message: Message) -> Self {
82 self.on_middle_release = Some(message);
83 self
84 }
85
86 #[must_use]
88 pub fn on_scroll(mut self, on_scroll: impl Fn(mouse::ScrollDelta) -> Message + 'a) -> Self {
89 self.on_scroll = Some(Box::new(on_scroll));
90 self
91 }
92
93 #[must_use]
95 pub fn on_enter(mut self, message: Message) -> Self {
96 self.on_enter = Some(message);
97 self
98 }
99
100 #[must_use]
102 pub fn on_move(mut self, on_move: impl Fn(Point) -> Message + 'a) -> Self {
103 self.on_move = Some(Box::new(on_move));
104 self
105 }
106
107 #[must_use]
109 pub fn on_exit(mut self, message: Message) -> Self {
110 self.on_exit = Some(message);
111 self
112 }
113
114 #[must_use]
116 pub fn interaction(mut self, interaction: mouse::Interaction) -> Self {
117 self.interaction = Some(interaction);
118 self
119 }
120}
121
122#[derive(Default)]
124struct State {
125 is_hovered: bool,
126 bounds: Rectangle,
127 cursor_position: Option<Point>,
128 previous_click: Option<mouse::Click>,
129}
130
131impl<'a, Message, W> MouseArea<'a, Message, W> {
132 pub fn new(content: W) -> Self {
134 MouseArea {
135 content,
136 on_press: None,
137 on_release: None,
138 on_double_click: None,
139 on_right_press: None,
140 on_right_release: None,
141 on_middle_press: None,
142 on_middle_release: None,
143 on_scroll: None,
144 on_enter: None,
145 on_move: None,
146 on_exit: None,
147 interaction: None,
148 }
149 }
150}
151
152impl<Message, W> Meta for MouseArea<'_, Message, W> {}
153
154impl<Message, W, Theme, Renderer> Widget<Message, Theme, Renderer> for MouseArea<'_, Message, W>
155where
156 Renderer: renderer::Renderer,
157 Message: Clone,
158 W: Widget<Message, Theme, Renderer>,
159{
160 fn tag(&self) -> tree::Tag {
161 tree::Tag::of::<State>()
162 }
163
164 fn state(&self) -> tree::State {
165 tree::State::new(State::default())
166 }
167
168 fn diff(&mut self, tree: &mut Tree) {
169 tree.diff_children(std::slice::from_mut(&mut self.content));
170 }
171
172 fn size(&self) -> Size<Length> {
173 self.content.size()
174 }
175
176 fn layout(&mut self, tree: &mut Tree, renderer: &Renderer, limits: &layout::Limits) {
177 self.content.layout(&mut tree.children[0], renderer, limits);
178
179 tree.size = tree.children[0].size;
180 }
181
182 fn operate(
183 &mut self,
184 tree: &mut Tree,
185 layout: Layout,
186 viewport: &Rectangle,
187 renderer: &Renderer,
188 operation: &mut dyn Operation,
189 ) {
190 self.content
191 .operate(&mut tree.children[0], layout, viewport, renderer, operation);
192 }
193
194 fn update(
195 &mut self,
196 tree: &mut Tree,
197 event: &Event,
198 layout: Layout,
199 cursor: mouse::Cursor,
200 renderer: &Renderer,
201 shell: &mut Shell<'_, Message>,
202 viewport: &Rectangle,
203 ) {
204 self.content.update(
205 &mut tree.children[0],
206 event,
207 layout,
208 cursor,
209 renderer,
210 shell,
211 viewport,
212 );
213
214 if shell.is_event_captured() {
215 return;
216 }
217
218 update(self, tree, event, layout, cursor, shell);
219 }
220
221 fn mouse_interaction(
222 &self,
223 tree: &Tree,
224 layout: Layout,
225 cursor: mouse::Cursor,
226 viewport: &Rectangle,
227 renderer: &Renderer,
228 ) -> mouse::Interaction {
229 let content_interaction =
230 self.content
231 .mouse_interaction(&tree.children[0], layout, cursor, viewport, renderer);
232
233 match (self.interaction, content_interaction) {
234 (Some(interaction), mouse::Interaction::None) if cursor.is_over(layout.bounds()) => {
235 interaction
236 }
237 _ => content_interaction,
238 }
239 }
240
241 fn draw(
242 &self,
243 tree: &Tree,
244 renderer: &mut Renderer,
245 theme: &Theme,
246 renderer_style: &renderer::Style,
247 layout: Layout,
248 cursor: mouse::Cursor,
249 viewport: &Rectangle,
250 ) {
251 self.content.draw(
252 &tree.children[0],
253 renderer,
254 theme,
255 renderer_style,
256 layout,
257 cursor,
258 viewport,
259 );
260 }
261
262 fn overlay<'b>(
263 &'b mut self,
264 tree: &'b mut Tree,
265 layout: Layout,
266 renderer: &Renderer,
267 viewport: &Rectangle,
268 translation: Vector,
269 window: Size,
270 ) -> Vec<overlay::Element<'b, Message, Theme, Renderer>> {
271 self.content.overlay(
272 &mut tree.children[0],
273 layout,
274 renderer,
275 viewport,
276 translation,
277 window,
278 )
279 }
280}
281
282fn update<Message: Clone, W>(
285 widget: &mut MouseArea<'_, Message, W>,
286 tree: &mut Tree,
287 event: &Event,
288 layout: Layout,
289 cursor: mouse::Cursor,
290 shell: &mut Shell<'_, Message>,
291) {
292 let state: &mut State = tree.state.downcast_mut();
293
294 let cursor_position = cursor.position();
295 let bounds = layout.bounds();
296
297 if state.cursor_position != cursor_position || state.bounds != bounds {
298 let was_hovered = state.is_hovered;
299
300 state.is_hovered = cursor.is_over(layout.bounds());
301 state.cursor_position = cursor_position;
302 state.bounds = bounds;
303
304 match (
305 widget.on_enter.as_ref(),
306 widget.on_move.as_ref(),
307 widget.on_exit.as_ref(),
308 ) {
309 (Some(on_enter), _, _) if state.is_hovered && !was_hovered => {
310 shell.publish(on_enter.clone());
311 }
312 (_, Some(on_move), _) if state.is_hovered => {
313 if let Some(position) = cursor.position_in(layout.bounds()) {
314 shell.publish(on_move(position));
315 }
316 }
317 (_, _, Some(on_exit)) if !state.is_hovered && was_hovered => {
318 shell.publish(on_exit.clone());
319 }
320 _ => {}
321 }
322 }
323
324 if !cursor.is_over(layout.bounds()) {
325 return;
326 }
327
328 match event {
329 Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left))
330 | Event::Touch(touch::Event::FingerPressed { .. }) => {
331 if let Some(message) = widget.on_press.as_ref() {
332 shell.publish(message.clone());
333 shell.capture_event();
334 }
335
336 if let Some(position) = cursor_position
337 && let Some(message) = widget.on_double_click.as_ref()
338 {
339 let new_click =
340 mouse::Click::new(position, mouse::Button::Left, state.previous_click);
341
342 if new_click.kind() == mouse::click::Kind::Double {
343 shell.publish(message.clone());
344 }
345
346 state.previous_click = Some(new_click);
347
348 shell.capture_event();
351 }
352 }
353 Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left))
354 | Event::Touch(touch::Event::FingerLifted { .. }) => {
355 if let Some(message) = widget.on_release.as_ref() {
356 shell.publish(message.clone());
357 }
358 }
359 Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Right)) => {
360 if let Some(message) = widget.on_right_press.as_ref() {
361 shell.publish(message.clone());
362 shell.capture_event();
363 }
364 }
365 Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Right)) => {
366 if let Some(message) = widget.on_right_release.as_ref() {
367 shell.publish(message.clone());
368 }
369 }
370 Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Middle)) => {
371 if let Some(message) = widget.on_middle_press.as_ref() {
372 shell.publish(message.clone());
373 shell.capture_event();
374 }
375 }
376 Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Middle)) => {
377 if let Some(message) = widget.on_middle_release.as_ref() {
378 shell.publish(message.clone());
379 }
380 }
381 Event::Mouse(mouse::Event::WheelScrolled { delta }) => {
382 if let Some(on_scroll) = widget.on_scroll.as_ref() {
383 shell.publish(on_scroll(*delta));
384 shell.capture_event();
385 }
386 }
387 _ => {}
388 }
389}