Repository: matplotlib/matplotlib
Pull request: Don't drag draggables on scroll events
## PR summary
Fixes https://github.com/matplotlib/matplotlib/issues/29142; replaces https://github.com/matplotlib/matplotlib/pull/29270. The solution here is to set a custom picker on `DraggableBase` that doesn't fire if the event is a scroll event.


## PR checklist
<!-- Please mark any checkboxes that do not apply to this PR as [N/A].-->

- [x] "closes #0000" is in the body of the PR description to [link the related issue](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue)
- [ ] new and changed code is [tested](https://matplotlib.org/devdocs/devel/testing.html)
- [ ] *Plotting related* features are demonstrated in an [example](https://matplotlib.org/devdocs/devel/document.html#write-examples-and-tutorials)
- [ ] *New Features* and *API Changes* are noted with a [directive and release note](https://matplotlib.org/devdocs/devel/api_changes.html#announce-changes-deprecations-and-new-features)
- [ ] Documentation complies with [general](https://matplotlib.org/devdocs/devel/document.html#write-rest-pages) and [docstring](https://matplotlib.org/devdocs/devel/document.html#write-docstrings) guidelines

<!--We understand that PRs can sometimes be overwhelming, especially as the
reviews start coming in.  Please let us know if the reviews are unclear or
the recommended next step seems overly demanding, if you would like help in
addressing a reviewer's comments, or if you have been waiting too long to hear
back on your PR.-->


Problem statement:
[MNT]: Draggable legend gets stuck on cursor after scroll event
### Summary

Draggable legend gets stuck to cursor after scrolling while hovering the artist. To 'unstick' it, it is necessary to left click. I believe the expected behavior is to only drag the legend while holding the left mouse button. If that is not the case, please close this issue.

### Proposed fix

Sample code:
```python
import matplotlib.pyplot as plt

plt.plot([],[], label='scroll over me, then move the cursor')
plt.legend(draggable=True)
plt.show()
```

I believe the issue is that the class `DraggableBase` (at line 1425 of matplotlib/offsetbox.py) uses a 'pick' event to grab the artist, which I believe includes scrolling events. It might be possible to return from a false positive pick by checking that the pick event is a mouse click instead of scrolling at `DraggableBase.on_pick`. Another alternative is to change the pick event to a `button_press` event.

Hints from issue discussion:
Perhaps related to #29066 too?
 I am not sure to be honest. I don't know much about the backends. But the problem described doesn't appear to be the same. In fact I tried implementing my solution inside offsetbox.py and it worked as predicted. Scroll events no longer make legend stick to cursor.
 
 This is the proposed modification (first 3 lines of DraggableBase.on_pick):
 ```python
def on_pick(self, evt):
        # Return if mouseevent is a scroll (this is the modification)
        if evt.mouseevent.button in ('up','down'):
            return
        if self._check_still_parented() and evt.artist == self.ref_artist:
            self.mouse_x = evt.mouseevent.x
            self.mouse_y = evt.mouseevent.y
            self.got_artist = True
            if self._use_blit:
                self.ref_artist.set_animated(True)
                self.canvas.draw()
                self.background = \
                    self.canvas.copy_from_bbox(self.ref_artist.figure.bbox)
                self.ref_artist.draw(
                    self.ref_artist.figure._get_renderer())
                self.canvas.blit()
            self.save_offset()
```
Oh, I see.  OK, I agree your proposed patch seems reasonable (perhaps rather check `evt.name == "scroll_event"`, which is more explicit, though).
Hi can I work on solving this issue?
Is the thread author responsible for giving permission? If so, yes.

Commit message: Move picker to method

Patch to review:
diff --git a/lib/matplotlib/offsetbox.py b/lib/matplotlib/offsetbox.py
index 56b10c785ff9..28f91553819e 100644
--- a/lib/matplotlib/offsetbox.py
+++ b/lib/matplotlib/offsetbox.py
@@ -1486,7 +1486,7 @@ def finalize_offset(self):
     def __init__(self, ref_artist, use_blit=False):
         self.ref_artist = ref_artist
         if not ref_artist.pickable():
-            ref_artist.set_picker(True)
+            ref_artist.set_picker(self._picker)
         self.got_artist = False
         self._use_blit = use_blit and self.canvas.supports_blit
         callbacks = self.canvas.callbacks
@@ -1500,6 +1500,10 @@ def __init__(self, ref_artist, use_blit=False):
             ]
         ]
 
+    @staticmethod
+    def _picker(artist, mouseevent):
+        return (artist.contains(mouseevent) and mouseevent.name != "scroll_event"), {}
+
     # A property, not an attribute, to maintain picklability.
     canvas = property(lambda self: self.ref_artist.get_figure(root=True).canvas)
     cids = property(lambda self: [


Code review comments:
diff_hunk: @@ -1500,6 +1500,10 @@ def __init__(self, ref_artist, use_blit=False):
             ]
         ]
 
+    @staticmethod
+    def _picker(artist, mouseevent):
+        return (artist.contains(mouseevent) and mouseevent.name != "scroll_event"), {}
path: lib/matplotlib/offsetbox.py
text: @author:
```suggestion
        # A custom picker to prevent dragging on mouse scroll events
        return (artist.contains(mouseevent) and mouseevent.name != "scroll_event"), {}
```