Repository: kivy/kivy

Issue:
touch.pos for on_touch_down / on_touch_up differ when not moving the mouse
I don't understand if this is a bug or a feature:
https://gist.github.com/johanneshk/5bfa9a3983e81a20ed16ed1ff0d80257
Click on the red square and observe the console prints: For me touch.pos for on_touch_down and on_touch_up differ. Why? If you replace ScrollView with RelativeLayout in L.48 it both positions coincide (imo the correct behavior). 
Example:

down <**main**.Element object at 0x7f9b507ebf50> touch.pos (21.0, **32.0**) to window (221.0, 562.0) True
up <**main**.Element object at 0x7f9b507ebf50> touch.pos (21.0, **-448.0**) to window (221.0, 82.0) False

The y-coordinates is shifted in some way I don't understand.



Discussion:
If you click-hold for a second, releasing the mouse button produces an extra "down" event, which probably should not be happening. The output looks like this:

```
down <__main__.Element object at 0x0000000003E118D0> touch.pos (27.0, -503.0) to window (227.0, 27.0) False
down <__main__.Element object at 0x0000000003E118D0> touch.pos (27.0, 27.0) to window (227.0, 557.0) True
up <__main__.Element object at 0x0000000003E118D0> touch.pos (27.0, -503.0) to window (227.0, 27.0) False
```

Huh... so the position in the second 'down' (which is actually triggered upon button release) is correct. Unfortunately I don't really understand how touch events are digested by ScrollView (but in my opinion the culprit is somewhere in there). So we seem to have two issues:
- why is another 'down' touch event dispatched when releasing the button
- why is the position different

I am working on a retina screen and this behavior causes buttons to never trigger the `on_release` event. The `touch.pos` when `on_touch_down` occurs is converted `to_local` so `collide_point` is `True`. However, when the mouse is released in the exact same position, the `touch.pos` is in absolute coordinates which are outside of the button's `right` and `top` coordinates. For example, here are the button coordinates:

```
>>> self
<ui_cell.Button object at 0x11c599ba8>
>>> print('x:{}, right:{}, y:{}, top:{}'.format(self.x, self.right, self.y, self.top))
x:0, right:216.0, y:0, top:38
```

I put a breakpoint at `button.py:137` (the `on_touch_down` function) and print the touch position:

```
>>> print(touch.pos)
(72.0, 18.0)
```

I put another breakpoint at `button.py:161` (the `on_touch_up` function) and print the touch position there:

```
>>> print(touch.pos)
(510.0, 1178.0)
```

As a result, the touch is outside of the button bounds and the `on_release` is never dispatched.

Looking at the stacktrace for the `on_touch_down`, I see:

```
MainThread - pid_12811_id_4354658824    
    on_touch_down [button.py:137]   
    on_touch_down [widget.py:457]   
    on_touch_down [widget.py:457]   
    simulate_touch_down [scrollview.py:579] 
    on_scroll_start [scrollview.py:669] 
    on_touch_down [scrollview.py:584]   
    on_touch_down [dropdown.py:302] 
    on_touch_down [__init__.py:1090]    
    on_motion [__init__.py:1074]    
    post_dispatch_input [base.py:221]   
    dispatch_input [base.py:315]    
    idle [base.py:330]  
    _mainloop [window_sdl2.py:390]  
    mainloop [window_sdl2.py:637]   
    runTouchApp [base.py:487]   
    run [app.py:828]    
    <module> [main.py:359]  
    execfile [_pydev_execfile.py:25]    
    run [pydevd.py:937] 
    <module> [pydevd.py:1530]   
```

The `simulate_touch_down` function is where the touch event is converted to local. I should note that my buttons are implemented in a `dropdown`. The conversion happens in `scrollview.py:578`:

```
    def simulate_touch_down(self, touch):
        # at this point the touch is in parent coords
        touch.push()
        touch.apply_transform_2d(self.to_local)
        ret = super(ScrollView, self).on_touch_down(touch)
        touch.pop()
        return ret
```

However, looking at the stacktrace for `on_touch_up`, this same function is never applied to the touch so the coordinates stay in absolute values. Here's the stacktrace for `on_touch_up`:

```
MainThread - pid_12788_id_4360036872    
    on_touch_up [button.py:161] 
    post_dispatch_input [base.py:281]   
    dispatch_input [base.py:315]    
    idle [base.py:330]  
    _mainloop [window_sdl2.py:390]  
    mainloop [window_sdl2.py:637]   
    runTouchApp [base.py:487]   
    run [app.py:828]    
    <module> [main.py:359]  
    execfile [_pydev_execfile.py:25]    
    run [pydevd.py:937] 
    <module> [pydevd.py:1530]   
```

@uclatommy thanks for your input! And printing the stack trace is a great idea (I was wondering how to best follow the touch events through the code and did it by hand / not that successful...).

I'd guess that you found the culprit: The scrollview/touch_up not being converted: If you replace the ScrollView in your widget stack with a RelativeLayout, it should work as expected. 

My guess: The 'simulate_touch_down' function is somehow used to distinguish between touch_down events and drag events (to shift the content of the scrollview). Since this isn't necessary for the on_touch_up events these checks aren't carried out. But somehow the touch_up position isn't converted to local coordinates?

@johanneshk The `DropDown` class is a subclass of `ScrollView` so that's why the stacktrace looks like that. It's just the way Kivy is implementing the dropdown so there's no getting around the scrollview without editing kivy code. Each drop down element is a button. The `on_touch_down` will call `super().on_touch_down`, which results in that longer stacktrace. But the `on_touch_up` will not call `super().on_touch_up` so it never allows the parent classes to handle the touch event and the touch coordinates never get converted `to_local`. I'm not sure what the Kivy team intended, so I'm not sure what's _supposed_ to happen, otherwise, I'd consider submiting a pull request.

I'm going to handle it by just overriding the button's `on_touch_up` to convert the coordinates myself. It's a temporary fix until the Kivy team addresses the problem. Good luck. Hopefully the kivy team will address this sooner rather than later or maybe someone more familiar with the matter can put together a pull request.

OK, maybe my PR has some side effects: FileChooserListView seems to be behaving differently... I'm not sure if this is caused by this PR or some other of my local changes. 


Resolving patch:
diff --git a/kivy/uix/scrollview.py b/kivy/uix/scrollview.py
--- a/kivy/uix/scrollview.py
+++ b/kivy/uix/scrollview.py
@@ -596,6 +596,7 @@ def on_scroll_start(self, touch, check_children=True):
             touch.push()
             touch.apply_transform_2d(self.to_local)
             if self.dispatch_children('on_scroll_start', touch):
+                touch.pop()
                 return True
             touch.pop()
 
@@ -730,6 +731,7 @@ def on_scroll_move(self, touch):
         touch.push()
         touch.apply_transform_2d(self.to_local)
         if self.dispatch_children('on_scroll_move', touch):
+            touch.pop()
             return True
         touch.pop()
 
@@ -810,6 +812,7 @@ def on_touch_up(self, touch):
             touch.push()
             touch.apply_transform_2d(self.to_local)
             if super(ScrollView, self).on_touch_up(touch):
+                touch.pop()
                 return True
             touch.pop()
             return False
@@ -828,6 +831,7 @@ def on_scroll_stop(self, touch, check_children=True):
             touch.push()
             touch.apply_transform_2d(self.to_local)
             if self.dispatch_children('on_scroll_stop', touch):
+                touch.pop()
                 return True
             touch.pop()
 


Test patch: