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