cosmic_files/
mouse_area.rs

1//! A container for capturing mouse events.
2
3use std::time::Instant;
4
5use crate::tab::DOUBLE_CLICK_DURATION;
6use cosmic::iced::core::border::Border;
7use cosmic::iced::core::event::Event;
8use cosmic::iced::core::mouse::{self, click};
9use cosmic::iced::core::renderer::{self, Quad, Renderer as _};
10use cosmic::iced::core::widget::{Operation, Tree, tree};
11use cosmic::iced::core::{
12    Clipboard, Color, Layout, Length, Point, Rectangle, Shell, Size, Vector, Widget, layout,
13    overlay, touch,
14};
15use cosmic::widget::Id;
16use cosmic::{Element, Renderer, Theme};
17
18/// Emit messages on mouse events.
19#[allow(missing_debug_implementations)]
20pub struct MouseArea<'a, Message> {
21    id: Id,
22    content: Element<'a, Message>,
23    on_auto_scroll: Option<Box<dyn OnAutoScroll<'a, Message>>>,
24    on_drag: Option<Box<dyn OnDrag<'a, Message>>>,
25    on_double_click: Option<Box<dyn OnMouseButton<'a, Message>>>,
26    on_press: Option<Box<dyn OnMouseButton<'a, Message>>>,
27    on_drag_end: Option<Box<dyn OnMouseButton<'a, Message>>>,
28    on_release: Option<Box<dyn OnMouseButton<'a, Message>>>,
29    on_resize: Option<Box<dyn OnResize<'a, Message>>>,
30    on_right_press: Option<Box<dyn OnMouseButton<'a, Message>>>,
31    on_right_press_no_capture: bool,
32    on_right_press_window_position: bool,
33    on_right_release: Option<Box<dyn OnMouseButton<'a, Message>>>,
34    on_middle_press: Option<Box<dyn OnMouseButton<'a, Message>>>,
35    on_middle_release: Option<Box<dyn OnMouseButton<'a, Message>>>,
36    on_back_press: Option<Box<dyn OnMouseButton<'a, Message>>>,
37    on_back_release: Option<Box<dyn OnMouseButton<'a, Message>>>,
38    on_forward_press: Option<Box<dyn OnMouseButton<'a, Message>>>,
39    on_forward_release: Option<Box<dyn OnMouseButton<'a, Message>>>,
40    on_scroll: Option<Box<dyn OnScroll<'a, Message>>>,
41    on_enter: Option<Box<dyn OnEnterExit<'a, Message>>>,
42    on_exit: Option<Box<dyn OnEnterExit<'a, Message>>>,
43    show_drag_rect: bool,
44}
45
46impl<'a, Message> MouseArea<'a, Message> {
47    /// The message to emit when auto scroll changes.
48    #[must_use]
49    pub fn on_auto_scroll(mut self, message: impl OnAutoScroll<'a, Message>) -> Self {
50        self.on_auto_scroll = Some(Box::new(message));
51        self
52    }
53
54    /// The message to emit when a drag is initiated.
55    #[must_use]
56    pub fn on_drag(mut self, message: impl OnDrag<'a, Message>) -> Self {
57        self.on_drag = Some(Box::new(message));
58        self
59    }
60
61    /// The message to emit when a drag ends.
62    #[must_use]
63    pub fn on_drag_end(mut self, message: impl OnMouseButton<'a, Message>) -> Self {
64        self.on_drag_end = Some(Box::new(message));
65        self
66    }
67
68    /// The message to emit on a double click.
69    #[must_use]
70    pub fn on_double_click(mut self, message: impl OnMouseButton<'a, Message>) -> Self {
71        self.on_double_click = Some(Box::new(message));
72        self
73    }
74
75    /// The message to emit on a left button press.
76    #[must_use]
77    pub fn on_press(mut self, message: impl OnMouseButton<'a, Message>) -> Self {
78        self.on_press = Some(Box::new(message));
79        self
80    }
81
82    /// The message to emit on a left button release.
83    #[must_use]
84    pub fn on_release(mut self, message: impl OnMouseButton<'a, Message>) -> Self {
85        self.on_release = Some(Box::new(message));
86        self
87    }
88
89    /// The message to emit on resizing.
90    #[must_use]
91    pub fn on_resize(mut self, message: impl OnResize<'a, Message>) -> Self {
92        self.on_resize = Some(Box::new(message));
93        self
94    }
95
96    /// The message to emit on a right button press.
97    #[must_use]
98    pub fn on_right_press(mut self, message: impl OnMouseButton<'a, Message>) -> Self {
99        self.on_right_press = Some(Box::new(message));
100        self
101    }
102
103    /// on_right_press will not capture input
104    #[must_use]
105    pub fn on_right_press_no_capture(mut self) -> Self {
106        self.on_right_press_no_capture = true;
107        self
108    }
109
110    /// Only on wayland, on_right_press will provide window position instead of widget relative
111    #[must_use]
112    pub fn wayland_on_right_press_window_position(self) -> Self {
113        #[cfg(feature = "wayland")]
114        {
115            self.on_right_press_window_position = true;
116        }
117        self
118    }
119
120    /// on_right_press will provide window position instead of widget relative
121    #[must_use]
122    pub fn on_right_press_window_position(mut self) -> Self {
123        self.on_right_press_window_position = true;
124        self
125    }
126
127    /// The message to emit on a right button release.
128    #[must_use]
129    pub fn on_right_release(mut self, message: impl OnMouseButton<'a, Message>) -> Self {
130        self.on_right_release = Some(Box::new(message));
131        self
132    }
133
134    /// The message to emit on a middle button press.
135    #[must_use]
136    pub fn on_middle_press(mut self, message: impl OnMouseButton<'a, Message>) -> Self {
137        self.on_middle_press = Some(Box::new(message));
138        self
139    }
140
141    /// The message to emit on a middle button release.
142    #[must_use]
143    pub fn on_middle_release(mut self, message: impl OnMouseButton<'a, Message>) -> Self {
144        self.on_middle_release = Some(Box::new(message));
145        self
146    }
147
148    /// The message to emit on a back button press.
149    #[must_use]
150    pub fn on_back_press(mut self, message: impl OnMouseButton<'a, Message>) -> Self {
151        self.on_back_press = Some(Box::new(message));
152        self
153    }
154
155    /// The message to emit on a back button release.
156    #[must_use]
157    pub fn on_back_release(mut self, message: impl OnMouseButton<'a, Message>) -> Self {
158        self.on_back_release = Some(Box::new(message));
159        self
160    }
161
162    /// The message to emit on a forward button press.
163    #[must_use]
164    pub fn on_forward_press(mut self, message: impl OnMouseButton<'a, Message>) -> Self {
165        self.on_forward_press = Some(Box::new(message));
166        self
167    }
168
169    /// The message to emit on a forward button release.
170    #[must_use]
171    pub fn on_forward_release(mut self, message: impl OnMouseButton<'a, Message>) -> Self {
172        self.on_forward_release = Some(Box::new(message));
173        self
174    }
175
176    /// The message to emit on a scroll.
177    #[must_use]
178    pub fn on_scroll(mut self, message: impl OnScroll<'a, Message>) -> Self {
179        self.on_scroll = Some(Box::new(message));
180        self
181    }
182
183    /// The message to emit when a mouse enters the area.
184    #[must_use]
185    pub fn on_enter(mut self, message: impl OnEnterExit<'a, Message>) -> Self {
186        self.on_enter = Some(Box::new(message));
187        self
188    }
189
190    /// The message to emit when a mouse exits the area.
191    #[must_use]
192    pub fn on_exit(mut self, message: impl OnEnterExit<'a, Message>) -> Self {
193        self.on_exit = Some(Box::new(message));
194        self
195    }
196
197    #[must_use]
198    pub const fn show_drag_rect(mut self, show_drag_rect: bool) -> Self {
199        self.show_drag_rect = show_drag_rect;
200        self
201    }
202
203    /// Sets the widget's unique identifier.
204    #[must_use]
205    pub fn with_id(mut self, id: Id) -> Self {
206        self.id = id;
207        self
208    }
209}
210
211pub trait OnAutoScroll<'a, Message>: Fn(Option<f32>) -> Message + 'a {}
212impl<'a, Message, F> OnAutoScroll<'a, Message> for F where F: Fn(Option<f32>) -> Message + 'a {}
213
214pub trait OnMouseButton<'a, Message>: Fn(Option<Point>) -> Message + 'a {}
215impl<'a, Message, F> OnMouseButton<'a, Message> for F where F: Fn(Option<Point>) -> Message + 'a {}
216
217pub trait OnDrag<'a, Message>: Fn(Option<Rectangle>) -> Message + 'a {}
218impl<'a, Message, F> OnDrag<'a, Message> for F where F: Fn(Option<Rectangle>) -> Message + 'a {}
219
220pub trait OnResize<'a, Message>: Fn(Rectangle) -> Message + 'a {}
221impl<'a, Message, F> OnResize<'a, Message> for F where F: Fn(Rectangle) -> Message + 'a {}
222
223pub trait OnScroll<'a, Message>: Fn(mouse::ScrollDelta) -> Option<Message> + 'a {}
224impl<'a, Message, F> OnScroll<'a, Message> for F where
225    F: Fn(mouse::ScrollDelta) -> Option<Message> + 'a
226{
227}
228
229pub trait OnEnterExit<'a, Message>: Fn() -> Message + 'a {}
230impl<'a, Message, F> OnEnterExit<'a, Message> for F where F: Fn() -> Message + 'a {}
231
232/// Local state of the [`MouseArea`].
233#[derive(Default)]
234struct State {
235    last_auto_scroll: Option<f32>,
236    last_position: Option<Point>,
237    last_virtual_position: Option<Point>,
238    drag_initiated: Option<Point>,
239    prev_click: Option<(mouse::Click, Instant)>,
240    viewport: Option<Rectangle>,
241}
242
243impl State {
244    fn drag_rect(&self, cursor: mouse::Cursor) -> Option<Rectangle> {
245        if let Some(drag_source) = self.drag_initiated
246            && let Some(position) = cursor.position().or(self.last_virtual_position)
247            && position.distance(drag_source) > 1.0
248        {
249            let min_x = drag_source.x.min(position.x);
250            let max_x = drag_source.x.max(position.x);
251            let min_y = drag_source.y.min(position.y);
252            let max_y = drag_source.y.max(position.y);
253            return Some(Rectangle::new(
254                Point::new(min_x, min_y),
255                Size::new(max_x - min_x, max_y - min_y),
256            ));
257        }
258        None
259    }
260
261    fn click(&mut self, pos: Point) -> mouse::Click {
262        let now = Instant::now();
263
264        let new = if let Some((prev_click, prev_time)) = self.prev_click.take() {
265            if now.duration_since(prev_time) < DOUBLE_CLICK_DURATION {
266                match prev_click.kind() {
267                    mouse::click::Kind::Single => {
268                        mouse::Click::new(pos, mouse::Button::Left, Some(prev_click))
269                    }
270                    mouse::click::Kind::Double => {
271                        mouse::Click::new(pos, mouse::Button::Left, Some(prev_click))
272                    }
273                    mouse::click::Kind::Triple => {
274                        mouse::Click::new(pos, mouse::Button::Left, Some(prev_click))
275                    }
276                }
277            } else {
278                mouse::Click::new(pos, mouse::Button::Left, None)
279            }
280        } else {
281            mouse::Click::new(pos, mouse::Button::Left, None)
282        };
283        self.prev_click = Some((new, now));
284        new
285    }
286}
287
288impl<'a, Message> MouseArea<'a, Message> {
289    /// Creates a [`MouseArea`] with the given content.
290    pub fn new(content: impl Into<Element<'a, Message>>) -> Self {
291        MouseArea {
292            id: Id::unique(),
293            content: content.into(),
294            on_auto_scroll: None,
295            on_drag: None,
296            on_drag_end: None,
297            on_double_click: None,
298            on_press: None,
299            on_release: None,
300            on_resize: None,
301            on_right_press: None,
302            on_right_press_no_capture: false,
303            on_right_press_window_position: false,
304            on_right_release: None,
305            on_middle_press: None,
306            on_middle_release: None,
307            on_back_press: None,
308            on_back_release: None,
309            on_forward_press: None,
310            on_forward_release: None,
311            on_enter: None,
312            on_exit: None,
313            on_scroll: None,
314            show_drag_rect: false,
315        }
316    }
317}
318
319impl<Message> Widget<Message, Theme, Renderer> for MouseArea<'_, Message>
320where
321    Message: Clone,
322{
323    fn tag(&self) -> tree::Tag {
324        tree::Tag::of::<State>()
325    }
326
327    fn state(&self) -> tree::State {
328        tree::State::new(State::default())
329    }
330
331    fn children(&self) -> Vec<Tree> {
332        vec![Tree::new(&self.content)]
333    }
334
335    fn diff(&mut self, tree: &mut Tree) {
336        tree.diff_children(std::slice::from_mut(&mut self.content));
337    }
338
339    fn size(&self) -> Size<Length> {
340        self.content.as_widget().size()
341    }
342
343    fn layout(
344        &mut self,
345        tree: &mut Tree,
346        renderer: &Renderer,
347        limits: &layout::Limits,
348    ) -> layout::Node {
349        self.content
350            .as_widget_mut()
351            .layout(&mut tree.children[0], renderer, limits)
352    }
353
354    fn operate(
355        &mut self,
356        tree: &mut Tree,
357        layout: Layout<'_>,
358        renderer: &Renderer,
359        operation: &mut dyn Operation,
360    ) {
361        self.content
362            .as_widget_mut()
363            .operate(&mut tree.children[0], layout, renderer, operation);
364    }
365
366    fn update(
367        &mut self,
368        tree: &mut Tree,
369        event: &Event,
370        layout: Layout<'_>,
371        cursor: mouse::Cursor,
372        renderer: &Renderer,
373        clipboard: &mut dyn Clipboard,
374        shell: &mut Shell<'_, Message>,
375        viewport: &Rectangle,
376    ) {
377        self.content.as_widget_mut().update(
378            &mut tree.children[0],
379            event,
380            layout,
381            cursor,
382            renderer,
383            clipboard,
384            shell,
385            viewport,
386        );
387
388        if shell.is_event_captured() {
389            return;
390        }
391
392        update(
393            self,
394            event,
395            layout,
396            cursor,
397            shell,
398            tree.state.downcast_mut::<State>(),
399            viewport,
400        );
401    }
402
403    fn mouse_interaction(
404        &self,
405        tree: &Tree,
406        layout: Layout<'_>,
407        cursor: mouse::Cursor,
408        viewport: &Rectangle,
409        renderer: &Renderer,
410    ) -> mouse::Interaction {
411        self.content.as_widget().mouse_interaction(
412            &tree.children[0],
413            layout,
414            cursor,
415            viewport,
416            renderer,
417        )
418    }
419
420    fn draw(
421        &self,
422        tree: &Tree,
423        renderer: &mut Renderer,
424        theme: &Theme,
425        renderer_style: &renderer::Style,
426        layout: Layout<'_>,
427        cursor: mouse::Cursor,
428        viewport: &Rectangle,
429    ) {
430        self.content.as_widget().draw(
431            &tree.children[0],
432            renderer,
433            theme,
434            renderer_style,
435            layout,
436            cursor,
437            viewport,
438        );
439
440        if self.show_drag_rect {
441            let state = tree.state.downcast_ref::<State>();
442            if let Some(bounds) = state.drag_rect(cursor) {
443                let cosmic = theme.cosmic();
444                let mut bg_color = cosmic.accent_color();
445                //TODO: get correct alpha
446                bg_color.alpha = 0.2;
447                renderer.start_layer(*viewport);
448                renderer.fill_quad(
449                    Quad {
450                        bounds,
451                        border: Border {
452                            color: cosmic.accent_color().into(),
453                            width: 1.0,
454                            radius: cosmic.radius_xs().into(),
455                        },
456                        ..Default::default()
457                    },
458                    Color::from(bg_color),
459                );
460                renderer.end_layer();
461            }
462        }
463    }
464
465    fn overlay<'b>(
466        &'b mut self,
467        tree: &'b mut Tree,
468        layout: Layout<'b>,
469        renderer: &Renderer,
470        viewport: &Rectangle,
471        translation: Vector,
472    ) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
473        self.content.as_widget_mut().overlay(
474            &mut tree.children[0],
475            layout,
476            renderer,
477            viewport,
478            translation,
479        )
480    }
481
482    fn drag_destinations(
483        &self,
484        state: &Tree,
485        layout: Layout<'_>,
486        renderer: &Renderer,
487        dnd_rectangles: &mut cosmic::iced::core::clipboard::DndDestinationRectangles,
488    ) {
489        self.content.as_widget().drag_destinations(
490            &state.children[0],
491            layout,
492            renderer,
493            dnd_rectangles,
494        );
495    }
496
497    fn id(&self) -> Option<Id> {
498        Some(self.id.clone())
499    }
500
501    fn set_id(&mut self, id: Id) {
502        self.id = id;
503    }
504}
505
506impl<'a, Message> From<MouseArea<'a, Message>> for Element<'a, Message>
507where
508    Message: 'a + Clone,
509    Renderer: 'a + renderer::Renderer,
510    Theme: 'a,
511{
512    fn from(area: MouseArea<'a, Message>) -> Self {
513        Element::new(area)
514    }
515}
516
517/// Processes the given [`Event`] and updates the [`State`] of an [`MouseArea`]
518/// accordingly.
519fn update<Message: Clone>(
520    widget: &mut MouseArea<'_, Message>,
521    event: &Event,
522    layout: Layout<'_>,
523    cursor: mouse::Cursor,
524    shell: &mut Shell<'_, Message>,
525    state: &mut State,
526    viewport: &Rectangle,
527) {
528    let offset = layout.virtual_offset();
529    let layout_bounds = layout.bounds();
530
531    let viewport_changed = state.viewport != Some(*viewport);
532
533    if let Some(message) = widget.on_resize.as_ref()
534        && viewport_changed
535    {
536        shell.publish(message(*viewport));
537    }
538
539    state.viewport = Some(*viewport);
540
541    let should_check_hover = viewport_changed
542        || matches!(
543            event,
544            Event::Mouse(mouse::Event::CursorMoved { .. })
545                | Event::Mouse(mouse::Event::WheelScrolled { .. })
546        );
547
548    if should_check_hover {
549        let position_in = cursor.position_in(layout_bounds);
550        match (position_in, state.last_position) {
551            (None, Some(_)) => {
552                if let Some(message) = widget.on_exit.as_ref() {
553                    shell.publish(message());
554                }
555            }
556            (Some(_), None) => {
557                if let Some(message) = widget.on_enter.as_ref() {
558                    shell.publish(message());
559                }
560            }
561            _ => {}
562        }
563        state.last_position = position_in;
564    }
565
566    if let Event::Mouse(mouse::Event::CursorMoved { position }) = event {
567        let virtual_position = Point::new(
568            viewport.x - layout_bounds.x + position.x,
569            viewport.y - layout_bounds.y + position.y,
570        );
571        state.last_virtual_position = Some(virtual_position);
572
573        if let Some(message) = widget.on_auto_scroll.as_ref() {
574            let auto_scroll = if state.drag_initiated.is_some() {
575                let bottom = viewport.y;
576                let top = viewport.y + viewport.height;
577                if virtual_position.y < bottom {
578                    Some(virtual_position.y - bottom)
579                } else if virtual_position.y > top {
580                    Some(virtual_position.y - top)
581                } else {
582                    None
583                }
584            } else {
585                None
586            };
587            if state.last_auto_scroll != auto_scroll {
588                shell.publish(message(auto_scroll));
589                state.last_auto_scroll = auto_scroll;
590            }
591        }
592    }
593
594    if state.drag_initiated.is_none() && !cursor.is_over(layout_bounds) {
595        return;
596    }
597
598    if let Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left))
599    | Event::Touch(touch::Event::FingerPressed { .. }) = event
600    {
601        let click = state.click(cursor.position_in(layout_bounds).unwrap_or_default());
602        match click.kind() {
603            click::Kind::Single => {
604                if let Some(message) = widget.on_press.as_ref() {
605                    shell.publish(message(cursor.position_in(layout_bounds)));
606                }
607            }
608            click::Kind::Double => {
609                if let Some(message) = widget.on_double_click.as_ref() {
610                    shell.publish(message(cursor.position_in(layout_bounds)));
611                }
612            }
613            click::Kind::Triple => {
614                // TODO what to do here
615                if let Some(message) = widget.on_press.as_ref() {
616                    shell.publish(message(cursor.position_in(layout_bounds)));
617                }
618            }
619        }
620        if widget.on_drag.is_some() {
621            state.drag_initiated = cursor.position();
622        }
623
624        if widget.on_press.is_some() {
625            shell.capture_event();
626            return;
627        }
628    }
629
630    let distance_dragged = state
631        .drag_initiated
632        .map(|initiated| initiated.distance(cursor.position().unwrap_or_default()))
633        .unwrap_or_default();
634    if matches!(
635        event,
636        Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left))
637            | Event::Touch(touch::Event::FingerLifted { .. })
638    ) && distance_dragged > 1.0
639    {
640        state.drag_initiated = None;
641        state.prev_click = None;
642        if let Some(message) = widget.on_drag_end.as_ref() {
643            shell.publish(message(cursor.position_in(layout_bounds)));
644        }
645    }
646
647    let recent_click = state
648        .prev_click
649        .as_ref()
650        .is_some_and(|(_, i)| Instant::now().duration_since(*i) <= DOUBLE_CLICK_DURATION);
651    if matches!(
652        event,
653        Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left))
654            | Event::Touch(touch::Event::FingerLifted { .. })
655    ) && state.prev_click.is_some()
656    {
657        if !recent_click {
658            state.prev_click = None;
659            return;
660        }
661        state.drag_initiated = None;
662        if let Some(message) = widget.on_release.as_ref() {
663            shell.publish(message(cursor.position_in(layout_bounds)));
664
665            shell.capture_event();
666            return;
667        }
668    }
669
670    if let Some(message) = widget.on_right_press.as_ref()
671        && matches!(
672            event,
673            Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Right))
674        )
675    {
676        let point_opt = if widget.on_right_press_window_position {
677            cursor.position_over(layout_bounds).map(|mut p| {
678                p.x -= offset.x;
679                p.y -= offset.y;
680                p
681            })
682        } else {
683            cursor.position_in(layout_bounds)
684        };
685        shell.publish(message(point_opt));
686
687        if widget.on_right_press_no_capture {
688            return;
689        }
690        shell.capture_event();
691        return;
692    }
693
694    if let Some(message) = widget.on_right_release.as_ref()
695        && matches!(
696            event,
697            Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Right))
698        )
699    {
700        shell.publish(message(cursor.position_in(layout_bounds)));
701
702        shell.capture_event();
703        return;
704    }
705
706    if let Some(message) = widget.on_middle_press.as_ref()
707        && matches!(
708            event,
709            Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Middle))
710        )
711    {
712        shell.publish(message(cursor.position_in(layout_bounds)));
713
714        shell.capture_event();
715        return;
716    }
717
718    if let Some(message) = widget.on_middle_release.as_ref()
719        && matches!(
720            event,
721            Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Middle))
722        )
723    {
724        shell.publish(message(cursor.position_in(layout_bounds)));
725
726        shell.capture_event();
727        return;
728    }
729
730    if let Some(message) = widget.on_back_press.as_ref()
731        && matches!(
732            event,
733            Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Back))
734        )
735    {
736        shell.publish(message(cursor.position_in(layout_bounds)));
737
738        shell.capture_event();
739        return;
740    }
741
742    if let Some(message) = widget.on_back_release.as_ref()
743        && matches!(
744            event,
745            Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Back))
746        )
747    {
748        shell.publish(message(cursor.position_in(layout_bounds)));
749
750        shell.capture_event();
751        return;
752    }
753
754    if let Some(message) = widget.on_forward_press.as_ref()
755        && matches!(
756            event,
757            Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Forward))
758        )
759    {
760        shell.publish(message(cursor.position_in(layout_bounds)));
761
762        shell.capture_event();
763        return;
764    }
765
766    if let Some(message) = widget.on_forward_release.as_ref()
767        && matches!(
768            event,
769            Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Forward))
770        )
771    {
772        shell.publish(message(cursor.position_in(layout_bounds)));
773
774        shell.capture_event();
775        return;
776    }
777
778    if let Some(on_scroll) = widget.on_scroll.as_ref()
779        && let Event::Mouse(mouse::Event::WheelScrolled { delta }) = event
780        && let Some(message) = on_scroll(*delta)
781    {
782        shell.publish(message);
783        shell.capture_event();
784        return;
785    }
786
787    if let Some((message, drag_rect)) = widget.on_drag.as_ref().zip(state.drag_rect(cursor)) {
788        shell.publish(message(drag_rect.intersection(&layout_bounds).map(
789            |mut rect| {
790                rect.x -= layout_bounds.x;
791                rect.y -= layout_bounds.y;
792                rect
793            },
794        )));
795    }
796}