U
    PeM=                     @   s|   d Z ddlmZ ddlmZ ddlmZ dZi Zee	Z
dadZdd ZG d	d
 d
eZG dd deZG dd deZdS )a  
RecycleView Views
=================

.. versionadded:: 1.10.0

The adapter part of the RecycleView which together with the layout is the
view part of the model-view-controller pattern.

The view module handles converting the data to a view using the adapter class
which is then displayed by the layout. A view can be any Widget based class.
However, inheriting from RecycleDataViewBehavior adds methods for converting
the data to a view.

TODO:
    * Make view caches specific to each view class type.

    )ObjectProperty)EventDispatcher)defaultdict)RecycleDataViewBehaviorRecycleKVIDsDataViewBehaviorRecycleDataAdapteri  c                  C   sF   t d tt } t D ](\}}ttdt||  8 a|| d= qdS )zETrims _cached_views cache to half the size of `_max_cache_size`.
       r   N)_max_cache_sizelen_cached_viewsitems_cache_countmax)max_sizeclsZ	instances r   >/tmp/pip-unpacked-wheel-xzebddm3/kivy/uix/recycleview/views.py_clean_cache+   s    r   c                   @   s(   e Zd ZdZdd Zdd Zdd ZdS )	r   zA optional base class for data views (:attr:`RecycleView`.viewclass).
    If a view inherits from this class, the class's functions will be called
    when the view needs to be updated due to a data change or layout update.
    c                 C   s0   t j}| D ]\}}||krt| || qdS )a  Called by the :class:`RecycleAdapter` when the view is initially
        populated with the values from the `data` dictionary for this item.

        Any pos or size info should be removed because they are set
        subsequently with :attr:`refresh_view_layout`.

        :Parameters:

            `rv`: :class:`RecycleView` instance
                The :class:`RecycleView` that caused the update.
            `data`: dict
                The data dict used to populate this view.
        N)r   _sizing_attrsr   setattr)selfrvindexdatasizing_attrskeyvaluer   r   r   refresh_view_attrs<   s    z*RecycleDataViewBehavior.refresh_view_attrsc           	      C   sb   | d\}}|dkr&|dk	r@|| _n|dkr6|| _n
||f| _| D ]\}}t| || qHdS )a  Called when the view's size is updated by the layout manager,
        :class:`RecycleLayoutManagerBehavior`.

        :Parameters:

            `rv`: :class:`RecycleView` instance
                The :class:`RecycleView` that caused the update.
            `viewport`: 4-tuple
                The coordinates of the bottom left and width height in layout
                manager coordinates. This may be larger than this view item.

        :raises:
            `LayoutChangeException`: If the sizing or data changed during a
            call to this method, raising a `LayoutChangeException` exception
            will force a refresh. Useful when data changed and we don't want
            to layout further since it'll be overwritten again soon.
        sizeN)popheightwidthr   r   r   )	r   r   r   layoutviewportwhnamer   r   r   r   refresh_view_layoutO   s    
z+RecycleDataViewBehavior.refresh_view_layoutc                 C   s   d S )Nr   )r   r   r   Zis_selectedr   r   r   apply_selectionn   s    z'RecycleDataViewBehavior.apply_selectionN)__name__
__module____qualname____doc__r   r'   r(   r   r   r   r   r   6   s   r   c                   @   s   e Zd ZdZdd ZdS )r   a  Similar to :class:`RecycleDataViewBehavior`, except that the data keys
    can signify properties of an object named with an id in the root KV rule.

    E.g. given a KV rule::

        <MyRule@RecycleKVIDsDataViewBehavior+BoxLayout>:
            Label:
                id: name
            Label:
                id: value

    Then setting the data list with
    ``rv.data = [{'name.text': 'Kivy user', 'value.text': '12'}]`` would
    automatically set the corresponding labels.

    So, if the key doesn't have a period, the named property of the root widget
    will be set to the corresponding value. If there is a period, the named
    property of the widget with the id listed before the period will be set to
    the corresponding value.

    .. versionadded:: 2.0.0
    c           	      C   sv   t j}| D ]b\}}||kr|d^}}|rdt|dkrLtd| dt| j| |d | qt| || qd S )N.   z
Data key "z" has more than one periodr   )r   r   r   splitr
   
ValueErrorr   ids)	r   r   r   r   r   r   r   r&   r1   r   r   r   r      s    
z/RecycleKVIDsDataViewBehavior.refresh_view_attrsN)r)   r*   r+   r,   r   r   r   r   r   r   r   s   r   c                       s   e Zd ZdZedddZi ZeeZ	ddddd	d
dddddddddddddhZ
 fddZdd Zdd Zdd Zd d! Zd"d# Zd$d% Zd&d' Zd(d) Zd*d+ Zd,d- Zd.d/ Z  ZS )0r   a  The class that converts data to a view.

    --- Internal details ---
    A view can have 3 states.

        * It can be completely in sync with the data, which
          occurs when the view is displayed. These are stored in :attr:`views`.
        * It can be dirty, which occurs when the view is in sync with the data,
          except for the size/pos parameters which is controlled by the layout.
          This occurs when the view is not currently displayed but the data has
          not changed. These views are stored in :attr:`dirty_views`.
        * Finally the view can be dead which occurs when the data changes and
          the view was not updated or when a view is just created. Such views
          are typically added to the internal cache.

    Typically what happens is that the layout manager lays out the data
    and then asks for views, using :meth:`set_visible_views,` for some specific
    data items that it displays.

    These views are gotten from the current views, dirty or global cache. Then
    depending on the view state :meth:`refresh_view_attrs` is called to bring
    the view up to date with the data (except for sizing parameters). Finally,
    the layout manager gets these views, updates their size and displays them.
    NT)Z	allownoner   r!   r    Z	size_hintZsize_hint_xZsize_hint_yposxycenterZcenter_xZcenter_yZpos_hintZsize_hint_minZsize_hint_min_xZsize_hint_min_yZsize_hint_maxZsize_hint_max_xZsize_hint_max_yc                    s&   i | _ tt| _tt| jf | dS )z
        Fix for issue https://github.com/kivy/kivy/issues/5913:
        Scrolling RV A, then Scrolling RV B, content of A and B seemed
        to be getting mixed up
        N)viewsr   dictdirty_viewssuperr   __init__)r   kwargs	__class__r   r   r:      s    
zRecycleDataAdapter.__init__c                 C   s
   || _ dS )zAssociates a :class:`~kivy.uix.recycleview.RecycleViewBehavior`
        with this instance. It is stored in :attr:`recycleview`.
        Nrecycleview)r   r   r   r   r   attach_recycleview   s    z%RecycleDataAdapter.attach_recycleviewc                 C   s
   d| _ dS )zRemoves the :class:`~kivy.uix.recycleview.RecycleViewBehavior`
        associated with this instance and clears :attr:`recycleview`.
        Nr>   )r   r   r   r   detach_recycleview   s    z%RecycleDataAdapter.detach_recycleviewc                 C   s$   |dkrdS | }|  ||| |S )z(internal) Creates and initializes the view for the data at `index`.

        The returned view is synced with the data, except for the pos/size
        information.
        N)r   )r   r   	data_item	viewclassviewr   r   r   create_view   s
    zRecycleDataAdapter.create_viewc                 C   s   | j }|dkrdS d}d}||krr|| }||kr>||}qt| rZt|  d }}q|r| d d }}nt| rt|  d }}|dkr| |||}|dkrdS |r| ||| |S )az  (internal) Returns a view instance for the data at `index`

        It looks through the various caches and finally creates a view if it
        doesn't exist. The returned view is synced with the data, except for
        the pos/size information.

        If found in the cache it's removed from the source
        before returning. It doesn't check the current views.
        NFTr.   )r8   r   r   popitemrE   r   )r   r   rB   rC   r8   stalerD   Zdirty_classr   r   r   get_view   s,    zRecycleDataAdapter.get_viewc                 C   sf   |j }|tkrt|tt|< t| r6|| j|| n,tj}| D ]\}}||krDt	||| qDdS )ai  (internal) Syncs the view and brings it up to date with the data.

        This method calls :meth:`RecycleDataViewBehavior.refresh_view_attrs`
        if the view inherits from :class:`RecycleDataViewBehavior`. See that
        method for more details.

        .. note::
            Any sizing and position info is skipped when syncing with the data.
        N)
r=   _view_base_cache
isinstancer   r   r?   r   r   r   r   )r   r   rB   rD   rC   r   r   r   r   r   r   r     s    
z%RecycleDataAdapter.refresh_view_attrsc           	      C   s   |j tkrt|tt|j < t|j  r8|| j||| n^|d\}}|dkr^|dk	rx||_n|dkrn||_n
||f|_	|
 D ]\}}t||| qdS )a  Updates the sizing information of the view.

        viewport is in coordinates of the layout manager.

        This method calls :meth:`RecycleDataViewBehavior.refresh_view_attrs`
        if the view inherits from :class:`RecycleDataViewBehavior`. See that
        method for more details.

        .. note::
            Any sizing and position info is skipped when syncing with the data.
        r   N)r=   rI   rJ   r   r'   r?   r   r    r!   r   r   r   )	r   r   r"   rD   r#   r$   r%   r&   r   r   r   r   r'   %  s*    
 

   
z&RecycleDataAdapter.refresh_view_layoutc                 C   s   | j |= || j|j |< dS )zw(internal) Used to flag this view as dirty, ready to be used for
        others. See :meth:`make_views_dirty`.
        N)r6   r8   r=   )r   rD   r   r   r   r   make_view_dirtyF  s    z"RecycleDataAdapter.make_view_dirtyc                 C   s>   | j }|sdS | j}| D ]\}}|||j |< qi | _ dS )a$  Makes all the current views dirty.

        Dirty views are still in sync with the corresponding data. However, the
        size information may go out of sync. Therefore a dirty view can be
        reused by the same index by just updating the sizing information.

        Once the underlying data of this index changes, the view should be
        removed from the dirty views and moved to the global cache with
        :meth:`invalidate`.

        This is typically called when the layout manager needs to re-layout all
        the data.
        N)r6   r8   r   r=   )r   r6   r8   r   rD   r   r   r   make_views_dirtyM  s    z#RecycleDataAdapter.make_views_dirtyc                 C   s|   | j  D ]}t|j | td7 aq
| j D ]&\}}t| |  tt	|7 aq2tt
krht  i | _ | j  dS )at  Moves all the current views into the global cache.

        As opposed to making a view dirty where the view is in sync with the
        data except for sizing information, this will completely disconnect the
        view from the data, as it is assumed the data has gone out of sync with
        the view.

        This is typically called when the data changes.
        r.   N)r6   valuesr   r=   appendr   r8   r   extendr
   r	   r   clear)r   rD   r   r6   r   r   r   
invalidated  s    
zRecycleDataAdapter.invalidatec                 C   s   i }| j }g }g }| j}|D ]h}	||	d}
|
dk	rL|
||	< ||	|
f q||	||	 ||	 d }
|
dkrnq|
||	< ||	|
f q| }|   || _ |||fS )av  Gets a 3-tuple of the new, remaining, and old views for the current
        viewport.

        The new views are synced to the data except for the size/pos
        properties.
        The old views need to be removed from the layout, and the new views
        added.

        The new views are not necessarily *new*, but are all the currently
        visible views.
        NrC   )r6   rH   r   rN   r   rL   )r   indicesr   ZviewclassesZvisible_viewsZprevious_viewsZret_newZ
ret_remainrH   r   rD   Z	old_viewsr   r   r   set_visible_views|  s*    

z$RecycleDataAdapter.set_visible_viewsc                 C   s   | j |S )zReturns the currently visible view associated with ``index``.

        If no view is currently displayed for ``index`` it returns ``None``.
        )r6   get)r   r   r   r   r   get_visible_view  s    z#RecycleDataAdapter.get_visible_view)r)   r*   r+   r,   r   r?   r6   r   r7   r8   r   r:   r@   rA   rE   rH   r   r'   rK   rL   rQ   rS   rU   __classcell__r   r   r<   r   r      sH                  
)!&r   N)r,   Zkivy.propertiesr   Z
kivy.eventr   collectionsr   __all__rI   listr   r   r	   r   objectr   r   r   r   r   r   r   <module>   s   <&