Repository: napari/napari

Issue:
Dragging shapes drags canvas (sometimes)
## 🐛 Bug



<!-- A clear and concise description of what the bug is. -->

## To Reproduce

Steps to reproduce the behavior:

(will edit later am in a meeting and just want to have areminder) 

$ python examples/add_image_transformed.py
create label layer, 
create shapes layer
create multiple ellipses
copy one ellipse, 
past it
go to select mode, 
drag past ellipse.

Canvas drags with it: 

![flash](https://user-images.githubusercontent.com/335567/118198666-352ee000-b406-11eb-8df4-5221a25a262a.gif)

I can't always reproduce it (am on 336772a7d) 

## Expected behavior

Drag only ellipse

## Environment

 - Please copy and paste the information at napari info option in help menubar here:

 - Any other relevant information:

## Additional context

<!-- Add any other context about the problem here. -->



Discussion:
Bisected to #2441 

Seem like `viewer.camera.interactive` is reset to True on Ctrl-C/Ctrl-V
A little bit more background from the interactive discussion after the meeting: the labels layer has a bunch of extra "modifier shortcuts", including "temporarily switch to fill mode while holding Ctrl". Somehow, hitting Ctrl while in the shapes layer is getting passed through to the labels layer as the modifier (bad point 1), and then the on-release shortcut is switching `viewer.camera.interactive` back on, rather than switching the mode back to pan_zoom. (which would anyway also do the `viewer.camera.interactive` bit.)

So the bit that broke is that somehow updating the selection model let the Ctrl modifier through to the labels layer.
A shorter reproducer:

```python
import napari
import numpy as np


image = np.random.random((5, 5))
labels = np.zeros((5, 5), dtype=int)

viewer = napari.Viewer()
viewer.add_image(image)
viewer.add_labels(labels)
pts = viewer.add_points()
pts.mode = 'select'


napari.run()
```

Then:
- drag (nothing bad happens, just get the selection box)
- press and release Ctrl
- drag again — now dragging moves the camera.
Ok if you look at 
```python
[type(a) for a in viewer.window.qt_viewer._key_map_handler.keymap_providers]
```
you can see that right now it looks like we're accumulating layers inside our `keymap_providers` but we should be adding/ removing the active one each time it is changing, see https://github.com/napari/napari/blob/master/napari/_qt/qt_viewer.py#L382-L399

I would have expected that list to always be length 2, just the viewer and active layer. Now it has all the layers. This is evidently quite fragile now and broken. It even has layers that should have been deleted, which could be causing bad leaks! Maybe we should overhaul this entirely. I know that was already planned.

@tlambert03 what are your thoughts on fixing here?
Ok the problem is #2441 also removed `QtViewer._active_layer` here which was needed. I can fix now. It's probably a bad pattern that should change, but I think I can fix bug

Resolving patch:
diff --git a/napari/_qt/qt_viewer.py b/napari/_qt/qt_viewer.py
--- a/napari/_qt/qt_viewer.py
+++ b/napari/_qt/qt_viewer.py
@@ -387,12 +387,11 @@ def _on_active_change(self, event=None):
         event : napari.utils.event.Event
             The napari event that triggered this method.
         """
-        active_layer = self.viewer.layers.selection.active
-        if active_layer in self._key_map_handler.keymap_providers:
-            self._key_map_handler.keymap_providers.remove(active_layer)
-
-        if active_layer is not None:
-            self._key_map_handler.keymap_providers.insert(0, active_layer)
+        self._key_map_handler.keymap_providers = (
+            [self.viewer]
+            if self.viewer.layers.selection.active is None
+            else [self.viewer.layers.selection.active, self.viewer]
+        )
 
         # If a QtAboutKeyBindings exists, update its text.
         if self._key_bindings_dialog is not None:


Test patch:
diff --git a/napari/_qt/_tests/test_qt_viewer.py b/napari/_qt/_tests/test_qt_viewer.py
--- a/napari/_qt/_tests/test_qt_viewer.py
+++ b/napari/_qt/_tests/test_qt_viewer.py
@@ -301,3 +301,32 @@ def test_points_layer_display_correct_slice_on_scale(make_napari_viewer):
     layer = viewer.layers[1]
     indices, scale = layer._slice_data(layer._slice_indices)
     np.testing.assert_equal(indices, [0])
+
+
+def test_active_keybindings(make_napari_viewer):
+    """Test instantiating viewer."""
+    viewer = make_napari_viewer()
+    view = viewer.window.qt_viewer
+
+    # Check only keybinding is Viewer
+    assert len(view._key_map_handler.keymap_providers) == 1
+    assert view._key_map_handler.keymap_providers[0] == viewer
+
+    # Add a layer and check it is keybindings are active
+    data = np.random.random((10, 15))
+    layer_image = viewer.add_image(data)
+    assert viewer.layers.selection.active == layer_image
+    assert len(view._key_map_handler.keymap_providers) == 2
+    assert view._key_map_handler.keymap_providers[0] == layer_image
+
+    # Add a layer and check it is keybindings become active
+    layer_image_2 = viewer.add_image(data)
+    assert viewer.layers.selection.active == layer_image_2
+    assert len(view._key_map_handler.keymap_providers) == 2
+    assert view._key_map_handler.keymap_providers[0] == layer_image_2
+
+    # Change active layer and check it is keybindings become active
+    viewer.layers.selection.active = layer_image
+    assert viewer.layers.selection.active == layer_image
+    assert len(view._key_map_handler.keymap_providers) == 2
+    assert view._key_map_handler.keymap_providers[0] == layer_image