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