tuikit/widgets/scrollview.py
changeset 114 26c02bd94bd9
parent 77 fc1989059e19
child 117 8680c2333546
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tuikit/widgets/scrollview.py	Sun Feb 15 12:48:23 2015 +0100
@@ -0,0 +1,134 @@
+from tuikit.core.container import Container
+from tuikit.layouts.offset import OffsetLayout
+
+#from tuikit.layout import AnchorLayout, OffsetLayout
+#from tuikit.scrollbar import VScrollbar, HScrollbar
+#from tuikit.common import Borders
+
+
+class Viewport(Container):
+
+    """Viewport for partial view on widgets.
+
+    Viewport is special type of Container, which shows partial view
+    on its managed widgets. This view can be moved to show different part
+    and thus allow "scrolling" over the content. Complete view with scrollbars
+    is implemented in ScrollView.
+
+    """
+
+    def __init__(self):
+        Container.__init__(self, OffsetLayout)
+
+
+class Scrollview(Container):
+
+    def __init__(self):
+        Container.__init__(self)
+
+
+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)
+