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