Version

Quick search

Table Of Contents

Source code for kivy.uix.recycleview.layout

'''
RecycleView Layouts
===================

.. versionadded:: 1.10.0

The Layouts handle the presentation of views for the
:class:`~kivy.uix.recycleview.RecycleView`.

.. warning::
    This module is highly experimental, its API may change in the future and
    the documentation is not complete at this time.
'''
from kivy.compat import string_types
from kivy.factory import Factory
from kivy.properties import StringProperty, ObjectProperty
from kivy.uix.behaviors import CompoundSelectionBehavior
from kivy.uix.recycleview.views import RecycleDataViewBehavior, \
    _view_base_cache


[docs]class LayoutChangeException(Exception): pass
[docs]class LayoutSelectionBehavior(CompoundSelectionBehavior): '''The :class:`LayoutSelectionBehavior` can be combined with :class:`RecycleLayoutManagerBehavior` to allow its derived classes selection behaviors similarly to how :class:`~kivy.uix.behaviors.compoundselection.CompoundSelectionBehavior` can be used to add selection behaviors to normal layout. :class:`RecycleLayoutManagerBehavior` manages its children differently than normal layouts or widgets so this class adapts :class:`~kivy.uix.behaviors.compoundselection.CompoundSelectionBehavior` based selection to work with :class:`RecycleLayoutManagerBehavior` as well. Similarly to :class:`~kivy.uix.behaviors.compoundselection.CompoundSelectionBehavior`, one can select using the keyboard or touch, which calls :meth:`select_node` or :meth:`deselect_node`, or one can call these methods directly. When a item is selected or deselected :meth:`apply_selection` is called. See :meth:`apply_selection`. ''' key_selection = StringProperty(None, allownone=True) '''The key used to check whether a view of a data item can be selected with touch or the keyboard. :attr:`key_selection` is the key in data, which if present and ``True`` will enable selection for this item from the keyboard or with a touch. When None, the default, not item will be selectable. :attr:`key_selection` is a :class:`StringProperty` and defaults to None. .. note:: All data items can be selected directly using :meth:`select_node` or :meth:`deselect_node`, even if :attr:`key_selection` is False. ''' _selectable_nodes = [] _nodes_map = {} def __init__(self, **kwargs): self.nodes_order_reversed = False super(LayoutSelectionBehavior, self).__init__(**kwargs) def compute_sizes_from_data(self, data, flags): # overwrite this method so that when data changes we update # selectable nodes. key = self.key_selection if key is None: nodes = self._selectable_nodes = [] else: nodes = self._selectable_nodes = [ i for i, d in enumerate(data) if d.get(key)] self._nodes_map = {v: k for k, v in enumerate(nodes)} return super(LayoutSelectionBehavior, self).compute_sizes_from_data( data, flags)
[docs] def get_selectable_nodes(self): # the indices of the data is used as the nodes return self._selectable_nodes
[docs] def get_index_of_node(self, node, selectable_nodes): # the indices of the data is used as the nodes, so node return self._nodes_map[node]
[docs] def goto_node(self, key, last_node, last_node_idx): node, idx = super(LayoutSelectionBehavior, self).goto_node( key, last_node, last_node_idx) if node is not last_node: self.goto_view(node) return node, idx
[docs] def select_node(self, node): if super(LayoutSelectionBehavior, self).select_node(node): view = self.recycleview.view_adapter.get_visible_view(node) if view is not None: self.apply_selection(node, view, True)
[docs] def deselect_node(self, node): if super(LayoutSelectionBehavior, self).deselect_node(node): view = self.recycleview.view_adapter.get_visible_view(node) if view is not None: self.apply_selection(node, view, False)
[docs] def apply_selection(self, index, view, is_selected): '''Applies the selection to the view. This is called internally when a view is displayed and it needs to be shown as selected or as not selected. It is called when :meth:`select_node` or :meth:`deselect_node` is called or when a view needs to be refreshed. Its function is purely to update the view to reflect the selection state. So the function may be called multiple times even if the selection state may not have changed. If the view is a instance of :class:`~kivy.uix.recycleview.views.RecycleDataViewBehavior`, its :meth:`~kivy.uix.recycleview.views.RecycleDataViewBehavior.\ apply_selection` method will be called every time the view needs to refresh the selection state. Otherwise, the this method is responsible for applying the selection. :Parameters: `index`: int The index of the data item that is associated with the view. `view`: widget The widget that is the view of this data item. `is_selected`: bool Whether the item is selected. ''' viewclass = view.__class__ if viewclass not in _view_base_cache: _view_base_cache[viewclass] = isinstance(view, RecycleDataViewBehavior) if _view_base_cache[viewclass]: view.apply_selection(self.recycleview, index, is_selected)
def refresh_view_layout(self, index, layout, view, viewport): super(LayoutSelectionBehavior, self).refresh_view_layout( index, layout, view, viewport) self.apply_selection(index, view, index in self.selected_nodes)
[docs]class RecycleLayoutManagerBehavior(object): """A RecycleLayoutManagerBehavior is responsible for positioning views into the :attr:`RecycleView.data` within a :class:`RecycleView`. It adds new views into the data when it becomes visible to the user, and removes them when they leave the visible area. """ viewclass = ObjectProperty(None) '''See :attr:`RecyclerView.viewclass`. ''' key_viewclass = StringProperty(None) '''See :attr:`RecyclerView.key_viewclass`. ''' recycleview = ObjectProperty(None, allownone=True) asked_sizes = None def attach_recycleview(self, rv): self.recycleview = rv if rv: fbind = self.fbind # can be made more selective update than refresh_from_data which # causes a full update. But this likely affects most of the data. fbind('viewclass', rv.refresh_from_data) fbind('key_viewclass', rv.refresh_from_data) fbind('viewclass', rv._dispatch_prop_on_source, 'viewclass') fbind('key_viewclass', rv._dispatch_prop_on_source, 'key_viewclass') def detach_recycleview(self): self.clear_layout() rv = self.recycleview if rv: funbind = self.funbind funbind('viewclass', rv.refresh_from_data) funbind('key_viewclass', rv.refresh_from_data) funbind('viewclass', rv._dispatch_prop_on_source, 'viewclass') funbind('key_viewclass', rv._dispatch_prop_on_source, 'key_viewclass') self.recycleview = None def compute_sizes_from_data(self, data, flags): pass def compute_layout(self, data, flags): pass
[docs] def compute_visible_views(self, data, viewport): '''`viewport` is in coordinates of the layout manager. ''' pass
[docs] def set_visible_views(self, indices, data, viewport): '''`viewport` is in coordinates of the layout manager. ''' pass
[docs] def refresh_view_layout(self, index, layout, view, viewport): '''`See :meth:`~kivy.uix.recycleview.views.RecycleDataAdapter.\ refresh_view_layout`. ''' self.recycleview.view_adapter.refresh_view_layout( index, layout, view, viewport)
[docs] def get_view_index_at(self, pos): """Return the view `index` on which position, `pos`, falls. `pos` is in coordinates of the layout manager. """ pass
def remove_views(self): rv = self.recycleview if rv: adapter = rv.view_adapter if adapter: adapter.make_views_dirty() def remove_view(self, view, index): rv = self.recycleview if rv: adapter = rv.view_adapter if adapter: adapter.make_view_dirty(view, index) def clear_layout(self): rv = self.recycleview if rv: adapter = rv.view_adapter if adapter: adapter.invalidate()
[docs] def goto_view(self, index): '''Moves the views so that the view corresponding to `index` is visible. ''' pass
def on_viewclass(self, instance, value): # resolve the real class if it was a string. if isinstance(value, string_types): self.viewclass = getattr(Factory, value)