Repository: napari/napari

Issue:
Shapes layer: when constrained resizing of shape with shift pressed: shape changes when shift is released
## 🐛 Bug

If you use shift to fix the proportions do a constrained resize of a shape, then if you **release shift** before you release the mouse button, the shape will snap to your current mouse location, changing shape!

https://user-images.githubusercontent.com/76622105/227728322-0afac9d3-2d52-45e5-8565-c1a891fd9861.mp4

Not sure why the keypresses arn't showing in the video, but I think you can see the snap behavior.

## To Reproduce

Steps to reproduce the behavior:

1. Make a recatangle on a shapes layer
2. Use shift to resize proportionally
3. Release shift before releasing mouse

<!-- If you have a code sample, error messages, stack traces, please provide it here as well -->

## Expected behavior

The shape should keep it's shape and not snap to the mouse location.

## Environment

```
napari: 0.5.0a2.dev51+gfc038f36
Platform: macOS-13.2.1-arm64-arm-64bit
System: MacOS 13.2.1
Python: 3.10.9 | packaged by conda-forge | (main, Feb 2 2023, 20:26:08) [Clang 14.0.6 ]
Qt: 5.15.6
PyQt5: 5.15.7
NumPy: 1.24.2
SciPy: 1.10.1
Dask: 2023.3.1
VisPy: 0.12.2.dev45
magicgui: 0.7.2
superqt: unknown
in-n-out: 0.1.7
app-model: 0.1.2
npe2: 0.6.2.dev26+gd7329b7.d20230324

OpenGL:
- GL version: 2.1 Metal - 83
- MAX_TEXTURE_SIZE: 16384

Screens:
- screen 1: resolution 1680x1050, scale 2.0

Settings path:
- /Users/piotrsobolewski/Library/Application Support/napari/napari-dev_a1eb8b76ba95fa16ad06e26097b46b8455dfbf0b/settings.yaml

```

## Additional context

<!-- Add any other context about the problem here. -->



Discussion:
Hi! I was checking this and I think this could be fixed by removing these lines at `napari\napari\layers\shapes\_shapes_key_bindings.py`:

https://github.com/napari/napari/blob/8a793b9f2215a8aeadb2c6fa9147371e42fd7439/napari/layers/shapes/_shapes_key_bindings.py#L36-L37

And adding some validations to break the `while` loop depending on the `Shift` presence in the `mouse_move` event at `napari\napari\layers\shapes\_shapes__bindings.py`:

https://github.com/napari/napari/blob/8a793b9f2215a8aeadb2c6fa9147371e42fd7439/napari/layers/shapes/_shapes_mouse_bindings.py#L59-L74

So adding a validation that could look something like:

```python
    # on move
    shift_on_move = shift_on_press
    while event.type == 'mouse_move':
        old_shift_on_move = shift_on_move
        shift_on_move = 'Shift' in event.modifiers
        if (shift_on_press or old_shift_on_move) and not shift_on_move:
            # stop move handling if `Shift` was being pressed but now is not
            break
        coordinates = layer.world_to_data(event.position)
```

With the changes above I get the following behavior:

![shape_fixed_resize](https://user-images.githubusercontent.com/16781833/229190582-516dffb3-5e5d-4fe2-a5a1-ee7697ea5dd8.gif)


Based on the vid, this looks like what I'd expect: order of releasing shift/mouse shouldn't matter. Maybe other folks have a different expectation?
More feedback will be awesome since in part I'm not totally sure if there is a reason why there is some moving logic on shift release at:

https://github.com/napari/napari/blob/8a793b9f2215a8aeadb2c6fa9147371e42fd7439/napari/layers/shapes/_shapes_key_bindings.py#L34-L37

Happy to check changes in the implementation in case the expected behavior needs to be different :+1:
@dalthviz Sorry about the delay, I've had some RL things going on.
I commented out those `on key release` lines and...
...I get the behavior **I'd** expect!

That said, my wife who uses photoshop and illustrator says that my expectation is **wrong** 😭 
Shift should toggle proportionality and as it turns out that the current live behavior works exactly like in Photoshop. You need not start with shift held down, you can press shift at any time to get proportional resize—and release Shift to lose it.

So I guess this is a non-issue—I just need to learn better key vs mouse discipline.

Thank you for checking the implementation ideas here and the new info @psobolewskiPhD ! And that makes sense! It's nice to have a more clear picture of why this could be working like that :+1: 
I renamed, because it's not a bug.
I'm leaning to closing this? Fiji behavior is slightly different than ours: the snap to mouse doesn't happen until you move the mouse after releasing shift.
I asked my wife to recheck and indeed that's actually the case in Photoshop too:
when you let go of shift, before mouse button proportion stay constrained until you move the mouse at all, then the shape will snap to wherever the mouse pointer is.

I think that splits the difference nicely? 
As far as I can tell it can be accomplished by deleting:
https://github.com/napari/napari/blob/8a793b9f2215a8aeadb2c6fa9147371e42fd7439/napari/layers/shapes/_shapes_key_bindings.py#L36-L37
which is what force-moves the shape to the mouse on release.
What do you think @dalthviz ?

Resolving patch:
diff --git a/napari/layers/shapes/_shapes_key_bindings.py b/napari/layers/shapes/_shapes_key_bindings.py
--- a/napari/layers/shapes/_shapes_key_bindings.py
+++ b/napari/layers/shapes/_shapes_key_bindings.py
@@ -33,8 +33,6 @@ def hold_to_lock_aspect_ratio(layer: Shapes):
 
     # on key release
     layer._fixed_aspect = False
-    if layer._is_moving:
-        _move(layer, layer._moving_coordinates)
 
 
 def register_shapes_action(description: str, repeatable: bool = False):


Test patch: