tuikit/scrollview.py
author Radek Brich <radek.brich@devl.cz>
Sun, 22 Feb 2015 09:53:13 +0100
changeset 119 dd91747504dd
parent 77 fc1989059e19
permissions -rw-r--r--
Redraw widgets on request. Add scrollbar demo.

# -*- coding: utf-8 -*-

from tuikit.layout import AnchorLayout, OffsetLayout
from tuikit.scrollbar import VScrollbar, HScrollbar
from tuikit.common import Borders


class OffsetView(AnchorLayout):
    def __init__(self):
        AnchorLayout.__init__(self)
        self._default_size.update(20, 20)
        self._inner = OffsetLayout()
        AnchorLayout.add(self, self._inner, halign='fill', valign='fill', margin=Borders(r=1))

    def add(self, widget, **kwargs):
        self._inner.add(widget, **kwargs)

    def on_draw(self, ev):
        ev.driver.fill_clip()


class Scrolling:
    def __init__(self):
        self.vscroll = VScrollbar()
        self.vscroll.add_handler('change', self._on_vscroll_change)
        AnchorLayout.add(self, self.vscroll, halign='right', valign='fill')

        self.hscroll = HScrollbar()
        self.hscroll.add_handler('change', self._on_hscroll_change)
        AnchorLayout.add(self, self.hscroll, halign='fill', valign='bottom')

    def on_resize(self, ev):
        self._update_scroll_max()

    def _connect_child(self, widget):
        widget.add_handler('sizereq', self._on_child_sizereq)
        widget.add_handler('spotmove', self._on_child_spotmove)
        widget.add_handler('scrollreq', self._on_child_scrollreq)

    def _on_child_sizereq(self, ev):
        self._update_scroll_max()

    def _on_child_spotmove(self, ev):
        child = ev.originator
        # x
        spot_x = child.x - self._inner.offset.x + child.spot.x
        if spot_x < self.hscroll.scroll_pos:
            self.hscroll.scroll_pos = spot_x
        if spot_x > (self._inner.width - 1) + self.hscroll.scroll_pos:
            self.hscroll.scroll_pos = spot_x - (self._inner.width - 1)
        # y
        spot_y = child.y - self._inner.offset.y + child.spot.y
        if spot_y < self.vscroll.scroll_pos:
            self.vscroll.scroll_pos = spot_y
        if spot_y > (self._inner.height - 1) + self.vscroll.scroll_pos:
            self.vscroll.scroll_pos = spot_y - (self._inner.height - 1)

    def _on_child_scrollreq(self, ev):
        new_scroll_pos = self.vscroll.scroll_pos + ev.data
        if new_scroll_pos > self.vscroll.scroll_max:
            self.vscroll.scroll_pos = self.vscroll.scroll_max
        elif new_scroll_pos < 0:
            self.vscroll.scroll_pos = 0
        else:
            self.vscroll.scroll_pos = new_scroll_pos

    def _on_vscroll_change(self, ev):
        self._inner.offset.y = - self.vscroll.scroll_pos

    def _on_hscroll_change(self, ev):
        self._inner.offset.x = - self.hscroll.scroll_pos

    def _update_scroll_max(self):
        max_width = 0
        max_height = 0
        for child in self._inner.children:
            posx, posy = child.hint_value('position')
            child_width = posx + child.sizereq.w
            if child_width > max_width:
                max_width = child_width
            child_height = posy + child.sizereq.h
            if child_height > max_height:
                max_height = child_height
        max_width += 1
        self.hscroll.scroll_max = max_width - self._inner.width
        self.vscroll.scroll_max = max_height - self._inner.height
        hscroll_hide = max_width < self._inner.width
        vscroll_hide = max_height < self._inner.height
        self._toggle_scrollers(hscroll_hide, vscroll_hide)

    def _toggle_scrollers(self, hscroll_hide, vscroll_hide):
        if hscroll_hide:
            self.hscroll.hide()
        else:
            self.hscroll.show()
        if vscroll_hide:
            self.vscroll.hide()
        else:
            self.vscroll.show()
        # self._inner.need_resize()


class ScrollView(OffsetView, Scrolling):
    """Scrolling view

    Shows scrollbars when needed.
    Scrolling area is determined according to child widget's size request.

    """

    def __init__(self):
        OffsetView.__init__(self)
        Scrolling.__init__(self)

    def add(self, widget, **kwargs):
        OffsetView.add(self, widget, **kwargs)
        self._connect_child(widget)

    def _toggle_scrollers(self, hscroll_hide, vscroll_hide):
        bpad = int(not hscroll_hide)
        self.vscroll.update_hint('margin', b=bpad)
        rpad = int(not vscroll_hide)
        self.hscroll.update_hint('margin', r=rpad)
        self._inner.update_hint('margin', b=bpad, r=rpad)
        Scrolling._toggle_scrollers(self, hscroll_hide, vscroll_hide)