U
    PeRR                     @   s   d Z dZddlZddlmZ ddlmZ ddlmZ ddlmZ ddl	m
Z
 G d	d
 d
eZG dd deZedefi ZG dd deZdS )a]  
.. _motionevent:

Motion Event
============

The :class:`MotionEvent` is the base class used for events provided by
pointing devices (touch and non-touch). This class defines all the properties
and methods needed to handle 2D and 3D movements but has many more
capabilities.

Usually you would never need to create the :class:`MotionEvent` yourself as
this is the role of the :mod:`~kivy.input.providers`.

Flow of the motion events
-------------------------

1. The :class:`MotionEvent` 's are gathered from input providers by
   :class:`~kivy.base.EventLoopBase`.
2. Post processing is performed by registered processors
   :mod:`~kivy.input.postproc`.
3. :class:`~kivy.base.EventLoopBase` dispatches all motion events using
   `on_motion` event to all registered listeners including the
   :class:`~kivy.core.window.WindowBase`.
4. Once received in :meth:`~kivy.core.window.WindowBase.on_motion` events
   (touch or non-touch) are all registered managers. If a touch event is not
   handled by at least one manager, then it is dispatched through
   :meth:`~kivy.core.window.WindowBase.on_touch_down`,
   :meth:`~kivy.core.window.WindowBase.on_touch_move` and
   :meth:`~kivy.core.window.WindowBase.on_touch_up`.
5. Widgets receive events in :meth:`~kivy.uix.widget.Widget.on_motion` method
   (if passed by a manager) or on `on_touch_xxx` methods.

Motion events and event managers
--------------------------------

A motion event is a touch event if its :attr:`MotionEvent.is_touch` is set to
`True`. Beside `is_touch` attribute, :attr:`MotionEvent.type_id` can be used to
check for event's general type. Currently two types are dispatched by
input providers: "touch" and "hover".

Event managers can be used to dispatch any motion event throughout the widget
tree and a manager uses `type_id` to specify which event types it want to
receive. See :mod:`~kivy.eventmanager` to learn how to define and register
an event manager.

A manager can also assign a new `type_id` to
:attr:`MotionEvent.type_id` before dispatching it to the widgets. This useful
when dispatching a specific event::

    class MouseTouchManager(EventManagerBase):

        type_ids = ('touch',)

        def dispatch(self, etype, me):
            accepted = False
            if me.device == 'mouse':
                me.push() # Save current type_id and other values
                me.type_id = 'mouse_touch'
                self.window.transform_motion_event_2d(me)
                # Dispatch mouse touch event to widgets which registered
                # to receive 'mouse_touch'
                for widget in self.window.children[:]:
                    if widget.dispatch('on_motion', etype, me):
                        accepted = True
                        break
                me.pop() # Restore
            return accepted

Listening to a motion event
---------------------------

If you want to receive all motion events, touch or not, you can bind the
MotionEvent from the :class:`~kivy.core.window.Window` to your own callback::

    def on_motion(self, etype, me):
        # will receive all motion events.
        pass

    Window.bind(on_motion=on_motion)

You can also listen to changes of the mouse position by watching
:attr:`~kivy.core.window.WindowBase.mouse_pos`.

Profiles
--------

The :class:`MotionEvent` stores device specific information in various
properties listed in the :attr:`~MotionEvent.profile`.
For example, you can receive a MotionEvent that has an angle, a fiducial
ID, or even a shape. You can check the :attr:`~MotionEvent.profile`
attribute to see what is currently supported by the MotionEvent provider.

This is a short list of the profile values supported by default. Please check
the :attr:`MotionEvent.profile` property to see what profile values are
available.

============== ================================================================
Profile value   Description
-------------- ----------------------------------------------------------------
angle          2D angle. Accessed via the `a` property.
button         Mouse button ('left', 'right', 'middle', 'scrollup' or
               'scrolldown'). Accessed via the `button` property.
markerid       Marker or Fiducial ID. Accessed via the `fid` property.
pos            2D position. Accessed via the `x`, `y` or `pos` properties.
pos3d          3D position. Accessed via the `x`, `y` or `z` properties.
pressure       Pressure of the contact. Accessed via the `pressure` property.
shape          Contact shape. Accessed via the `shape` property .
============== ================================================================

If you want to know whether the current :class:`MotionEvent` has an angle::

    def on_touch_move(self, touch):
        if 'angle' in touch.profile:
            print('The touch angle is', touch.a)

If you want to select only the fiducials::

    def on_touch_move(self, touch):
        if 'markerid' not in touch.profile:
            return

)MotionEvent    N)	isroutine)copy)time)MODE_DEFAULT_DISPATCH)Vectorc                       s$   e Zd Z fddZdd Z  ZS )EnhancedDictionaryc                    s6   z|  |W S  tk
r0   tt| | Y S X d S N)__getitem__KeyErrorsuperr   __getattr__)selfattr	__class__ :/tmp/pip-unpacked-wheel-xzebddm3/kivy/input/motionevent.pyr      s    zEnhancedDictionary.__getattr__c                 C   s   |  || d S r	   )__setitem__)r   r   valuer   r   r   __setattr__   s    zEnhancedDictionary.__setattr__)__name__
__module____qualname__r   r   __classcell__r   r   r   r   r      s   r   c                       s   e Zd Z fddZ  ZS )MotionEventMetaclassc                    s\   g }|D ]}t |dr||j qd|kr:||d  t||d< tt| | |||S )N	__attrs__)hasattrextendr   tupler   r   __new__)Zmcsnamebasesattrsr   baser   r   r   r       s    
 zMotionEventMetaclass.__new__)r   r   r   r    r   r   r   r   r   r      s   r   r   c                   @   s   e Zd ZdZdZdZd1ddZdd	 Zd2d
dZdd Z	dd Z
dd Zd3ddZdd Zd4ddZdd Zdd Zdd Zdd  Zd!d" Zed#d$ Zed%d& Zed'd( Zed)d* Zd+d, Zd-d. Zed/d0 ZdS )5r   zAbstract class that represents an input event.

    :Parameters:
        `id`: str
            unique ID of the MotionEvent
        `args`: list
            list of parameters, passed to the depack() function
    r   )'device
push_attrspush_attrs_stackis_touchtype_ididdispatch_modeshapeprofilesxsyszosxosyoszpsxpsypszdsxdsydszxyzoxoyozpxpypzdxdydz
time_startis_double_tapdouble_tap_timeis_triple_taptriple_tap_timeudFNc                 C   sh  | j tkrtdt jd7  _|| _|| _t| _g | _d| _	tj| _
|| _g | _d | _d| _d | _d | _g | _|| _d | _d| _d| _d| _d | _d | _d | _d | _d | _d | _d | _d | _d | _d| _ d| _!d| _"d | _#d | _$d | _%d | _&d | _'d | _(d | _)d | _*d | _+d| _,t- | _.| j.| _/d| _0d| _1d| _2d| _3d| _4t5 | _6d	| _7d	| _8d| _9| :| d S )
Nzclass MotionEvent is abstract   )r:   r;   r<   rC   rD   rE   r=   r>   r?   r@   rA   rB   posr)   r+   F        )rN   rN   r   T);r   r   NotImplementedError_MotionEvent__uniq_idr(   r)   r   r+   r'   r&   uidr%   	grab_listgrab_exclusive_classZ
grab_stateZgrab_currentbuttonr-   r*   r,   r.   r/   r0   r1   r2   r3   r4   r5   r6   r7   r8   r9   r:   r;   r<   r=   r>   r?   r@   rA   rB   rC   rD   rE   rM   r   rF   time_updatetime_endrG   rI   rH   rJ   r   rK   sync_with_dispatch_keep_prev_pos_first_dispatch_donedepack)r   r%   r*   argsr(   r)   r   r   r   __init__   sn    
zMotionEvent.__init__c                 C   sn   | j dks| jr@| js@| j | _ | _| j | _| _| j | _	| _
| j| j | _| j| j | _| j| j
 | _dS )z*Depack `args` into attributes of the classN)r1   rX   rZ   r.   r4   r/   r2   r5   r0   r3   r6   r7   r8   r9   r   r\   r   r   r   r[   k  s    
zMotionEvent.depackc                 C   s8   | j dk	rtdt|j}|r(|| _ | j| dS )a  Grab this motion event.

        If this event is a touch you can grab it if you want to receive
        subsequent :meth:`~kivy.uix.widget.Widget.on_touch_move` and
        :meth:`~kivy.uix.widget.Widget.on_touch_up` events, even if the touch
        is not dispatched by the parent:

        .. code-block:: python

            def on_touch_down(self, touch):
                touch.grab(self)

            def on_touch_move(self, touch):
                if touch.grab_current is self:
                    # I received my grabbed touch
                else:
                    # it's a normal touch

            def on_touch_up(self, touch):
                if touch.grab_current is self:
                    # I receive my grabbed touch, I must ungrab it!
                    touch.ungrab(self)
                else:
                    # it's a normal touch
                    pass

        .. versionchanged:: 2.1.0
            Allowed grab for non-touch events.
        Nz(Event is exclusive and cannot be grabbed)rT   	Exceptionweakrefref__self__rS   append)r   class_instanceZ	exclusiver   r   r   graby  s    
zMotionEvent.grabc                 C   s6   t |j}| j|krd| _|| jkr2| j| dS )z2Ungrab a previously grabbed motion event.
        N)r`   ra   rb   rT   rS   remove)r   rd   r   r   r   ungrab  s
    

zMotionEvent.ungrabc                 C   s   d| _ d| _dS )zNotify that dispatch to the listeners is done.

        Called by the :meth:`EventLoopBase.post_dispatch_input`.

        .. versionadded:: 2.1.0
        TN)rY   rZ   r   r   r   r   dispatch_done  s    zMotionEvent.dispatch_donec                 C   sb   | j r0| jrL| j| j| j  | _| _| _d| _n| j| j| j  | _| _| _t | _	| 
| dS )z"Move to another position.
        FN)rX   rY   r.   r/   r0   r4   r5   r6   r   rV   r[   r^   r   r   r   move  s    zMotionEvent.moveNonec                 C   s~  t d|d t d|d  }}| j}	|	| j| j|||\| _| _|	| j| j|||\| _| _	|	| j
| j|||\| _| _|dkrdnt d|d }
| j|
 | _| j|
 | _| j|
 | _|rB|dks|dkr|  j|8  _|  j	|8  _	|  j|8  _nJ|dkrB|| j|  ||  }|  j|7  _|  j	|7  _	|  j|7  _| j| j | _| j| j | _| j| j | _| j| jf| _dS )zScale position for the screen.

        .. versionchanged:: 2.1.0
            Max value for `x`, `y` and `z` is changed respectively to `w` - 1,
            `h` - 1 and `p` - 1.
        r   rL   NZpanZbelow_targetZscale)maxto_absolute_posr.   r/   r:   r;   r1   r2   r=   r>   r4   r5   r@   rA   r0   r<   r3   r?   r6   rB   rC   rD   rE   rM   )r   whprotationZsmodeZkheightx_maxy_maxabsoluteZz_maxoffsetr   r   r   scale_for_screen  s.    
zMotionEvent.scale_for_screenc                 C   s   |dkr|| || fS |dkr4|| d| | fS |dkrTd| | d| | fS |dkrpd| | || fS t d| dS )a  Transforms normalized (0-1) coordinates `nx` and `ny` to absolute
        coordinates using `x_max`, `y_max` and `rotation`.

        :raises:
            `ValueError`: If `rotation` is not one of: 0, 90, 180 or 270

        .. versionadded:: 2.1.0
        r   Z   rL      i  z7Invalid rotation %s, valid values are 0, 90, 180 or 270N)
ValueError)r   Znxnyrr   rs   rq   r   r   r   rm     s    	zMotionEvent.to_absolute_posc                    s4   |dkr j } fdd|D } j||f dS )z9Push attribute values in `attrs` onto the stack.
        Nc                    s   g | ]}t  |qS r   )getattr).0r:   rh   r   r   
<listcomp>  s     z$MotionEvent.push.<locals>.<listcomp>)r&   r'   rc   )r   r#   valuesr   rh   r   push  s    zMotionEvent.pushc                 C   s8   | j  \}}tt|D ]}t| || ||  qdS )z.Pop attributes values from the stack.
        N)r'   poprangelensetattr)r   r#   r~   ir   r   r   r     s    zMotionEvent.popc                 C   sh   || j | j \| _ | _| _|| j| j\| _| _|| j| j\| _| _| j | j | _| j| j | _dS )zWApply a transformation on x, y, z, px, py, pz,
        ox, oy, oz, dx, dy, dz.
        N)	r:   r;   rM   r@   rA   r=   r>   rC   rD   )r   Z	transformr   r   r   apply_transform_2d  s
    zMotionEvent.apply_transform_2dc                 C   s&   | j D ]}||t| | qdS )z3Copy some attribute to another motion event object.N)r   r   r   __getattribute__)r   tor   r   r   r   copy_to  s    
zMotionEvent.copy_toc                 C   s   t | j|jS )z4Return the distance between the two events.
        )r   rM   distance)r   Zother_touchr   r   r   r     s    zMotionEvent.distancec                 C   s   t  | _d S r	   )r   rW   rh   r   r   r   update_time_end  s    zMotionEvent.update_time_endc                 C   s   | j | jfS )ztReturn delta between last position and current position, in the
        screen coordinate system (self.dx, self.dy).)rC   rD   rh   r   r   r   dpos  s    zMotionEvent.dposc                 C   s   | j | jfS )zkReturn the initial position of the motion event in the screen
        coordinate system (self.ox, self.oy).)r=   r>   rh   r   r   r   opos!  s    zMotionEvent.oposc                 C   s   | j | jfS )zlReturn the previous position of the motion event in the screen
        coordinate system (self.px, self.py).)r@   rA   rh   r   r   r   ppos'  s    zMotionEvent.pposc                 C   s   | j | jfS )zMReturn the position in the 0-1 coordinate system (self.sx, self.sy).
        )r.   r/   rh   r   r   r   spos-  s    zMotionEvent.sposc                 C   s:   t | j}|dd dddd}d|| j| jf S )N.rO   > 'z<%s spos=%s pos=%s>)strr   splitreplacer   rM   )r   basename	classnamer   r   r   __str__3  s    
zMotionEvent.__str__c                 C   s\   g }t | D ]8}t| |}|d dkr(qt|r2q|d||f  qd| jjd|f S )Nr   _z%s="%s"z<%s %s> )dirr{   r   rc   r   r   join)r   outr:   vr   r   r   __repr__8  s    
zMotionEvent.__repr__c                 G   s   d| j kod| jkS )zcReturns True if the touch event is a mousewheel scrolling

        .. versionadded:: 1.6.0
        rU   Zscroll)r-   rU   r^   r   r   r   is_mouse_scrollingE  s    zMotionEvent.is_mouse_scrolling)FN)F)Nr   rk   r   )N)r   r   r   __doc__rQ   r   r]   r[   re   rg   ri   rj   rv   rm   r   r   r   r   r   r   propertyr   r   r   r   r   r   r   r   r   r   r   r      sD   	
 $
%	
    
#





)r   __all__r`   inspectr   r   r   Zkivy.eventmanagerr   Zkivy.vectorr   dictr   typer   objectZMotionEventBaser   r   r   r   r   <module>   s   |