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