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