Clean up hints - add methods update_hint, hint_value to Widget. Split ScrollView into OffsetView and Scrolling components. Reimplement ScrollWindow using Scrolling component.
authorRadek Brich <radek.brich@devl.cz>
Fri, 01 Feb 2013 09:34:15 +0100
changeset 75 2430c643838a
parent 74 23767a33a781
child 76 fa5301e58eca
Clean up hints - add methods update_hint, hint_value to Widget. Split ScrollView into OffsetView and Scrolling components. Reimplement ScrollWindow using Scrolling component.
tuikit/common.py
tuikit/container.py
tuikit/layout.py
tuikit/scrollbar.py
tuikit/scrollview.py
tuikit/scrollwindow.py
tuikit/widget.py
--- a/tuikit/common.py	Fri Feb 01 00:24:02 2013 +0100
+++ b/tuikit/common.py	Fri Feb 01 09:34:15 2013 +0100
@@ -25,6 +25,9 @@
     def selected(self):
         return self._selected
 
+    def get_value(self):
+        return self._selected
+
     def __repr__(self):
         return self.__class__.__name__ + '(%r)' % self._selected
 
--- a/tuikit/container.py	Fri Feb 01 00:24:02 2013 +0100
+++ b/tuikit/container.py	Fri Feb 01 09:34:15 2013 +0100
@@ -40,12 +40,9 @@
         self.children.append(widget)
         widget.parent = self
         widget.top = self.top
-        widget.hints.update({k:v() for k,v in self._hint_class.items()})
+        widget.reset_hints()
         for key in kwargs:
-            try:
-                widget.hints[key].update(kwargs[key])
-            except AttributeError:
-                widget.hints[key] = widget.hints[key].__class__(kwargs[key])
+            widget.update_hint(key, kwargs[key])
         if self.focuschild is None and widget.can_focus():
             widget.set_focus()
 
--- a/tuikit/layout.py	Fri Feb 01 00:24:02 2013 +0100
+++ b/tuikit/layout.py	Fri Feb 01 09:34:15 2013 +0100
@@ -52,9 +52,9 @@
         for child in self._get_children():
             reqw = max(child.sizereq.w, child.sizemin.w)
             reqh = max(child.sizereq.h, child.sizemin.h)
-            ha = child.hints['halign'].selected
-            va = child.hints['valign'].selected
-            margin = child.hints['margin']
+            ha = child.hint_value('halign')
+            va = child.hint_value('valign')
+            margin = child.hint_value('margin')
             if ha == 'left':
                 x = margin.l
                 w = reqw
@@ -94,10 +94,10 @@
         """
         if not child in self.children:
             raise ValueError('AnchorLayout.move(): Cannot move foreign child.')
-        margin = child.hints['margin']
+        margin = child.hint_value('margin')
         newx = None
         if x is not None:
-            ha = child.hints['halign'].selected
+            ha = child.hint_value('halign')
             ofsx = x - child.x
             if ha == 'left':
                 margin.l += ofsx
@@ -107,7 +107,7 @@
                 newx = self.width - margin.r - child.sizereq.w
         newy = None
         if y is not None:
-            va = child.hints['valign'].selected
+            va = child.hint_value('valign')
             ofsy = y - child.y
             if va == 'top':
                 margin.t += ofsy
@@ -142,7 +142,7 @@
             # reduce by space acquired by children
             space_to_divide -= sum([child.sizereq[ax1] for child in children])
             # number of children with expanded hint
-            expanded_num = len([ch for ch in children if ch.hints['expand']])
+            expanded_num = len([ch for ch in children if ch.hint_value('expand')])
         else:
             # all children are implicitly expanded
             expanded_num = len(children)
@@ -161,14 +161,14 @@
             size[ax2] = space2
 
             offset += self.spacing
-            if child.hints['expand'] or self.homogeneous:
+            if child.hint_value('expand') or self.homogeneous:
                 maxsize = int(round(space_child + math.modf(offset)[0], 2))
                 offset += space_child
                 if not self.homogeneous:
                     maxsize += child.sizereq[ax1]
                     offset += child.sizereq[ax1]
 
-                if child.hints['fill']:
+                if child.hint_value('fill'):
                     size[ax1] = maxsize
                 else:
                     pos[ax1] += int((maxsize - size[ax1])/2)
@@ -216,12 +216,12 @@
             child._view_size.update(w=self.width, h=self.height)
 
     def move_child(self, child, x, y):
-        child.hints('position').update(x, y)
+        child.update_hint('position', x, y)
 
     def _update_children_position(self):
         ox, oy = self._offset
         for child in self.children:
-            x, y = child.hints['position']
+            x, y = child.hint_value('position')
             child._pos.update(x=x+ox, y=y+oy)
 
 
--- a/tuikit/scrollbar.py	Fri Feb 01 00:24:02 2013 +0100
+++ b/tuikit/scrollbar.py	Fri Feb 01 09:34:15 2013 +0100
@@ -38,6 +38,9 @@
     @scroll_max.setter
     def scroll_max(self, value):
         self._scroll_max = value
+        if self._scroll_pos > value:
+            self._scroll_pos = value
+            self.emit('change')
         self._update_thumb_pos()
 
     @property
--- a/tuikit/scrollview.py	Fri Feb 01 00:24:02 2013 +0100
+++ b/tuikit/scrollview.py	Fri Feb 01 09:34:15 2013 +0100
@@ -1,11 +1,96 @@
 # -*- coding: utf-8 -*-
 
 from tuikit.layout import AnchorLayout, OffsetLayout
-from tuikit.scrollbar import VScrollbar
+from tuikit.scrollbar import VScrollbar, HScrollbar
 from tuikit.common import Borders
 
 
-class ScrollView(AnchorLayout):
+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)
+
+
+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:
+            child_width = child.x - self._inner.offset.x + child.sizereq.w
+            if child_width > max_width:
+                max_width = child_width
+            child_height = child.y - self._inner.offset.y + child.sizereq.h
+            if child_height > max_height:
+                max_height = child_height
+        max_width += 1
+        if max_width < self._inner.width:
+            self.hscroll.hide()
+        else:
+            self.hscroll.scroll_max = max_width - self._inner.width
+            self.hscroll.show()
+        if max_height < self._inner.height:
+            self.vscroll.hide()
+        else:
+            self.vscroll.scroll_max = max_height - self._inner.height
+            self.vscroll.show()
+
+
+class ScrollView(OffsetView, Scrolling):
     """Scrolling view
 
     Shows scrollbars when needed.
@@ -14,47 +99,10 @@
     """
 
     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))
-
-        self.vscroll = VScrollbar()
-        self.vscroll.add_handler('change', self._on_vscroll_change)
-        AnchorLayout.add(self, self.vscroll, halign='right', valign='fill')
+        OffsetView.__init__(self)
+        Scrolling.__init__(self)
 
     def add(self, widget, **kwargs):
-        self.inner.add(widget, **kwargs)
-        widget.add_handler('sizereq', self._on_child_sizereq)
-        widget.add_handler('spotmove', self._on_child_spotmove)
-
-    def on_resize(self, ev):
-        self._update_vscroll_max()
-
-    def _on_vscroll_change(self, ev):
-        self.inner.offset.y = - self.vscroll.scroll_pos
-
-    def _on_child_sizereq(self, ev):
-        self._update_vscroll_max()
+        OffsetView.add(self, widget, **kwargs)
+        self._connect_child(widget)
 
-    def _on_child_spotmove(self, ev):
-        child = ev.originator
-        spot = child.y - self.inner.offset.y + child.spot.y
-        if spot < self.vscroll.scroll_pos:
-            self.vscroll.scroll_pos = spot
-        if spot > (self.height - 1) + self.vscroll.scroll_pos:
-            self.vscroll.scroll_pos = spot - (self.height - 1)
-
-    def _update_vscroll_max(self):
-        max_height = 0
-        for child in self.inner.children:
-            child_height = child.y - self.inner.offset.y + child.sizereq.h
-            if child_height > max_height:
-                max_height = child_height
-        if max_height < self.height:
-            self.vscroll.hide()
-        else:
-            self.vscroll.scroll_max = max_height - self.height
-            self.vscroll.show()
-
--- a/tuikit/scrollwindow.py	Fri Feb 01 00:24:02 2013 +0100
+++ b/tuikit/scrollwindow.py	Fri Feb 01 09:34:15 2013 +0100
@@ -1,82 +1,17 @@
 from tuikit.window import Window
-from tuikit.layout import OffsetLayout, AnchorLayout
-from tuikit.scrollbar import VScrollbar, HScrollbar
-from tuikit.common import Borders
+from tuikit.scrollview import Scrolling
+from tuikit.layout import OffsetLayout
 
 
-class ScrollWindow(Window):
+class ScrollWindow(Window, Scrolling):
     def __init__(self):
         Window.__init__(self, inner_layout=OffsetLayout)
+        Scrolling.__init__(self)
 
-        self.vscroll = VScrollbar()
-        self.vscroll.add_handler('change', self._on_vscroll_change)
-        AnchorLayout.add(self, self.vscroll, halign='right', valign='fill', margin=Borders(t=1, b=1))
-
-        self.hscroll = HScrollbar()
-        self.hscroll.add_handler('change', self._on_hscroll_change)
-        AnchorLayout.add(self, self.hscroll, halign='fill', valign='bottom', margin=Borders(l=1, r=2))
+        self.vscroll.update_hint('margin', t=1, b=1)
+        self.hscroll.update_hint('margin', l=1, r=2)
 
     def add(self, widget, **kwargs):
         Window.add(self, widget, **kwargs)
-        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_resize(self, ev):
-        self._update_scroll_max()
-
-    def _on_child_sizereq(self, ev):
-        self._update_scroll_max()
-
-    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 _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)
+        self._connect_child(widget)
 
-    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 _update_scroll_max(self):
-        max_width = 0
-        max_height = 0
-        for child in self._inner.children:
-            child_width = child.x - self._inner.offset.x + child.sizereq.w
-            if child_width > max_width:
-                max_width = child_width
-            child_height = child.y - self._inner.offset.y + child.sizereq.h
-            if child_height > max_height:
-                max_height = child_height
-        max_width += 1
-        if max_width < self._inner.width:
-            self.hscroll.hide()
-        else:
-            self.hscroll.scroll_max = max_width - self._inner.width
-            self.hscroll.show()
-        if max_height < self._inner.height:
-            self.vscroll.hide()
-        else:
-            self.vscroll.scroll_max = max_height - self._inner.height
-            self.vscroll.show()
-
--- a/tuikit/widget.py	Fri Feb 01 00:24:02 2013 +0100
+++ b/tuikit/widget.py	Fri Feb 01 09:34:15 2013 +0100
@@ -51,7 +51,7 @@
         self.allow_focus = False
 
         #: Dictionary containing optional parameters for layout managers etc.
-        self.hints = {}
+        self._hints = {}
 
         #: Hidden widget does not affect layout.
         self.hidden = False
@@ -167,6 +167,35 @@
         """Real setter for top. Allows override."""
         self._top = value
 
+    def reset_hints(self):
+        """Reset all hints to their initial value.
+
+        This must be called at before any call to update_hint.
+
+        """
+        self._hints.update({k:v() for k,v in self.parent._hint_class.items()})
+
+    def update_hint(self, hint_name, *args, **kwargs):
+        """Set or update hint value.
+
+        Args after hint_name are forwarded to update() method or initializer
+        of hint's class.
+
+        """
+        if hint_name not in self._hints:
+            raise ValueError('Hint %r is not registered.' % hint_name)
+        try:
+            # try update
+            self._hints[hint_name].update(*args, **kwargs)
+        except AttributeError:
+            # if update does not exist, call initializer instead
+            self._hints[hint_name] = self._hints[hint_name].__class__(*args, **kwargs)
+
+    def hint_value(self, hint_name):
+        try:
+            return self._hints[hint_name].get_value()
+        except AttributeError:
+            return self._hints[hint_name]
 
     ### events