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 &mut self,
194 tree: &mut Tree,
195 renderer: &Renderer,
196 limits: &layout::Limits,
197 ) -> layout::Node {
198 self.content.as_widget_mut().layout(
199 &mut tree.children[0],
200 renderer,
201 limits,
202 )
203 }
204
205 fn operate(
206 &mut self,
207 tree: &mut Tree,
208 layout: Layout<'_>,
209 renderer: &Renderer,
210 operation: &mut dyn Operation,
211 ) {
212 self.content.as_widget_mut().operate(
213 &mut tree.children[0],
214 layout,
215 renderer,
216 operation,
217 );
218 }
219
220 fn update(
221 &mut self,
222 tree: &mut Tree,
223 event: &Event,
224 layout: Layout<'_>,
225 cursor: mouse::Cursor,
226 renderer: &Renderer,
227 clipboard: &mut dyn Clipboard,
228 shell: &mut Shell<'_, Message>,
229 viewport: &Rectangle,
230 ) {
231 self.content.as_widget_mut().update(
232 &mut tree.children[0],
233 event,
234 layout,
235 cursor,
236 renderer,
237 clipboard,
238 shell,
239 viewport,
240 );
241
242 if shell.is_event_captured() {
243 return;
244 }
245
246 update(self, tree, event, layout, cursor, shell);
247 }
248
249 fn mouse_interaction(
250 &self,
251 tree: &Tree,
252 layout: Layout<'_>,
253 cursor: mouse::Cursor,
254 viewport: &Rectangle,
255 renderer: &Renderer,
256 ) -> mouse::Interaction {
257 let content_interaction = self.content.as_widget().mouse_interaction(
258 &tree.children[0],
259 layout,
260 cursor,
261 viewport,
262 renderer,
263 );
264
265 match (self.interaction, content_interaction) {
266 (Some(interaction), mouse::Interaction::None)
267 if cursor.is_over(layout.bounds()) =>
268 {
269 interaction
270 }
271 _ => content_interaction,
272 }
273 }
274
275 fn draw(
276 &self,
277 tree: &Tree,
278 renderer: &mut Renderer,
279 theme: &Theme,
280 renderer_style: &renderer::Style,
281 layout: Layout<'_>,
282 cursor: mouse::Cursor,
283 viewport: &Rectangle,
284 ) {
285 self.content.as_widget().draw(
286 &tree.children[0],
287 renderer,
288 theme,
289 renderer_style,
290 layout,
291 cursor,
292 viewport,
293 );
294 }
295
296 fn overlay<'b>(
297 &'b mut self,
298 tree: &'b mut Tree,
299 layout: Layout<'b>,
300 renderer: &Renderer,
301 viewport: &Rectangle,
302 translation: Vector,
303 ) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
304 self.content.as_widget_mut().overlay(
305 &mut tree.children[0],
306 layout,
307 renderer,
308 viewport,
309 translation,
310 )
311 }
312}
313
314impl<'a, Message, Theme, Renderer> From<MouseArea<'a, Message, Theme, Renderer>>
315 for Element<'a, Message, Theme, Renderer>
316where
317 Message: 'a + Clone,
318 Theme: 'a,
319 Renderer: 'a + renderer::Renderer,
320{
321 fn from(
322 area: MouseArea<'a, Message, Theme, Renderer>,
323 ) -> Element<'a, Message, Theme, Renderer> {
324 Element::new(area)
325 }
326}
327
328fn update<Message: Clone, Theme, Renderer>(
331 widget: &mut MouseArea<'_, Message, Theme, Renderer>,
332 tree: &mut Tree,
333 event: &Event,
334 layout: Layout<'_>,
335 cursor: mouse::Cursor,
336 shell: &mut Shell<'_, Message>,
337) {
338 let state: &mut State = tree.state.downcast_mut();
339
340 let cursor_position = cursor.position();
341 let bounds = layout.bounds();
342
343 if state.cursor_position != cursor_position || state.bounds != bounds {
344 let was_hovered = state.is_hovered;
345
346 state.is_hovered = cursor.is_over(layout.bounds());
347 state.cursor_position = cursor_position;
348 state.bounds = bounds;
349
350 match (
351 widget.on_enter.as_ref(),
352 widget.on_move.as_ref(),
353 widget.on_exit.as_ref(),
354 ) {
355 (Some(on_enter), _, _) if state.is_hovered && !was_hovered => {
356 shell.publish(on_enter.clone());
357 }
358 (_, Some(on_move), _) if state.is_hovered => {
359 if let Some(position) = cursor.position_in(layout.bounds()) {
360 shell.publish(on_move(position));
361 }
362 }
363 (_, _, Some(on_exit)) if !state.is_hovered && was_hovered => {
364 shell.publish(on_exit.clone());
365 }
366 _ => {}
367 }
368 }
369
370 if !cursor.is_over(layout.bounds()) {
371 return;
372 }
373
374 match event {
375 Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left))
376 | Event::Touch(touch::Event::FingerPressed { .. }) => {
377 if let Some(message) = widget.on_press.as_ref() {
378 shell.publish(message.clone());
379 shell.capture_event();
380 }
381
382 if let Some(position) = cursor_position
383 && let Some(message) = widget.on_double_click.as_ref()
384 {
385 let new_click = mouse::Click::new(
386 position,
387 mouse::Button::Left,
388 state.previous_click,
389 );
390
391 if new_click.kind() == mouse::click::Kind::Double {
392 shell.publish(message.clone());
393 }
394
395 state.previous_click = Some(new_click);
396
397 shell.capture_event();
400 }
401 }
402 Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left))
403 | Event::Touch(touch::Event::FingerLifted { .. }) => {
404 if let Some(message) = widget.on_release.as_ref() {
405 shell.publish(message.clone());
406 }
407 }
408 Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Right)) => {
409 if let Some(message) = widget.on_right_press.as_ref() {
410 shell.publish(message.clone());
411 shell.capture_event();
412 }
413 }
414 Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Right)) => {
415 if let Some(message) = widget.on_right_release.as_ref() {
416 shell.publish(message.clone());
417 }
418 }
419 Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Middle)) => {
420 if let Some(message) = widget.on_middle_press.as_ref() {
421 shell.publish(message.clone());
422 shell.capture_event();
423 }
424 }
425 Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Middle)) => {
426 if let Some(message) = widget.on_middle_release.as_ref() {
427 shell.publish(message.clone());
428 }
429 }
430 Event::Mouse(mouse::Event::WheelScrolled { delta }) => {
431 if let Some(on_scroll) = widget.on_scroll.as_ref() {
432 shell.publish(on_scroll(*delta));
433 shell.capture_event();
434 }
435 }
436 _ => {}
437 }
438}