Source code for kivyx.uix.recycleboxlayout

from itertools import accumulate
from kivy.uix.recyclelayout import RecycleLayout
from kivyx.uix.boxlayout import KXBoxLayout

__all__ = ('KXRecycleBoxLayout', )


[docs]class KXRecycleBoxLayout(RecycleLayout, KXBoxLayout): _rv_positions = None def __init__(self, **kwargs): super().__init__(**kwargs) self.funbind('children', self._trigger_layout) def _update_sizes(self, changed): is_horizontal = self.is_horizontal padding_left, padding_top, padding_right, padding_bottom = self.padding padding_x = padding_left + padding_right padding_y = padding_top + padding_bottom selfw = self.width selfh = self.height layout_w = max(0, selfw - padding_x) layout_h = max(0, selfh - padding_y) cx = self.x + padding_left cy = self.y + padding_bottom view_opts = self.view_opts remove_view = self.remove_view for (index, widget, (w, h), (wn, hn), (shw, shh), (shnw, shnh), (shw_min, shh_min), (shwn_min, shhn_min), (shw_max, shh_max), (shwn_max, shhn_max), ph, phn) in changed: if (is_horizontal and (shw != shnw or w != wn or shw_min != shwn_min or shw_max != shwn_max) or not is_horizontal and (shh != shnh or h != hn or shh_min != shhn_min or shh_max != shhn_max)): return True remove_view(widget, index) opt = view_opts[index] if is_horizontal: wo, ho = opt['size'] if shnh is not None: _, h = opt['size'] = [wo, shnh * layout_h] else: h = ho xo, yo = opt['pos'] for key, value in phn.items(): posy = value * layout_h if key == 'y': yo = posy + cy elif key == 'top': yo = posy - h elif key == 'center_y': yo = posy - (h / 2.) opt['pos'] = [xo, yo] else: wo, ho = opt['size'] if shnw is not None: w, _ = opt['size'] = [shnw * layout_w, ho] else: w = wo xo, yo = opt['pos'] for key, value in phn.items(): posx = value * layout_w if key == 'x': xo = posx + cx elif key == 'right': xo = posx - w elif key == 'center_x': xo = posx - (w / 2.) opt['pos'] = [xo, yo] return False def compute_layout(self, data, flags): super().compute_layout(data, flags) changed = self._changed_views if (changed is None or changed and not self._update_sizes(changed)): return self.clear_layout() self._rv_positions = None if not data: l, t, r, b = self.padding self.minimum_size = l + r, t + b return view_opts = self.view_opts n = len(view_opts) for i, x, y, w, h in self._iterate_layout( [(opt['size'], opt['size_hint'], opt['pos_hint'], opt['size_hint_min'], opt['size_hint_max']) for opt in reversed(view_opts)]): opt = view_opts[n - i - 1] shw, shh = opt['size_hint'] opt['pos'] = x, y wo, ho = opt['size'] # layout won't/shouldn't change previous size if size_hint is None # which is what w/h being None means. opt['size'] = [(wo if shw is None else w), (ho if shh is None else h)] spacing = self.spacing padding = self.padding is_horizontal = self.is_horizontal dim = not is_horizontal is_forward_direction = self.orientation in {'lr', 'bt'} self._rv_positions = tuple( accumulate(( ((self.x + padding[0]) if is_horizontal else (self.y + padding[3])) - spacing / 2., *(opt['size'][dim] + spacing for opt in ( view_opts[:-1] if is_forward_direction else reversed(view_opts[1:]))), )) )[1:] def get_view_index_at(self, pos): calc_pos = self._rv_positions if not calc_pos: return 0 pos = pos[not self.is_horizontal] idx = 0 for v in calc_pos: if pos < v: break idx += 1 return idx if (self.orientation in {'lr', 'bt'}) \ else (len(self.view_opts) - idx - 1) def compute_visible_views(self, data, viewport): if self._rv_positions is None or not data: return [] x, y, w, h = viewport at_idx = self.get_view_index_at a, b = at_idx((x, y)), at_idx((x + w, y + h)) if self.orientation in {'rl', 'tb'}: a, b = b, a return list(range(a, b + 1))