Add Widget.spot property. TreeView: move spot with cursor node. ScrollView: scroll when spot moves.
authorRadek Brich <radek.brich@devl.cz>
Wed, 02 Jan 2013 11:48:36 +0100
changeset 44 d77f1ae3786c
parent 43 369c8ef5070a
child 45 43b2279b06e1
Add Widget.spot property. TreeView: move spot with cursor node. ScrollView: scroll when spot moves.
tuikit/scrollview.py
tuikit/treeview.py
tuikit/widget.py
--- a/tuikit/scrollview.py	Wed Jan 02 00:16:12 2013 +0100
+++ b/tuikit/scrollview.py	Wed Jan 02 11:48:36 2013 +0100
@@ -8,8 +8,7 @@
     """Scrolling view
 
     Shows scrollbars when needed.
-    Scrolling area is determined based on child widgets
-    size request.
+    Scrolling area is determined according to child widget's size request.
 
     """
 
@@ -25,6 +24,7 @@
         super().add(widget, **kwargs)
         if widget != self.vscroll:
             widget.connect('sizereq', self._on_child_sizereq)
+            widget.connect('spotmove', self._on_child_spotmove)
 
     def _handle_resize(self, ev):
         super()._handle_resize(ev)
@@ -38,6 +38,14 @@
     def _on_child_sizereq(self, ev):
         self._update_vscroll_max()
 
+    def _on_child_spotmove(self, ev):
+        child = ev.originator
+        spot = child.y + child.spot.y
+        if spot < self.vscroll.pos:
+            self.vscroll.pos = spot
+        if spot > (self.size.h - 1) + self.vscroll.pos:
+            self.vscroll.pos = spot - (self.size.h - 1)
+
     def _update_vscroll_max(self):
         max_height = 0
         for child in self.children:
--- a/tuikit/treeview.py	Wed Jan 02 00:16:12 2013 +0100
+++ b/tuikit/treeview.py	Wed Jan 02 11:48:36 2013 +0100
@@ -17,17 +17,17 @@
 
     """
 
-    def __init__(self, root, collapsed=[]):
+    def __init__(self, root, collapsed_nodes=[]):
         self._node = root
         self._index = 0
         self._stack = []
-        self._collapsed = collapsed
+        self._collapsed_nodes = collapsed_nodes
 
     def __next__(self):
         node = None
         while node is None:
             try:
-                if self._node in self._collapsed:
+                if self._node in self._collapsed_nodes:
                     raise IndexError()
                 node = self._node.children[self._index]
                 if node is None:
@@ -177,16 +177,15 @@
 
     def __init__(self, model=None, width=20, height=20):
         Widget.__init__(self, width, height)
-
         self.allow_focus = True
 
-        # cursor
-        self.cnode = None
-
         # model
         self._model = None
 
-        self.collapsed = []
+        # cursor
+        self._cursor_node = None
+
+        self.collapsed_nodes = []
 
         self.add_events(
             'expand', TreeEvent,  # node expanded, event carries the affected node
@@ -196,7 +195,7 @@
             self.model = model
 
     def __iter__(self):
-        return TreeIter(self._model.root, self.collapsed)
+        return TreeIter(self._model.root, self.collapsed_nodes)
 
     @property
     def model(self):
@@ -211,29 +210,38 @@
         if self._model:
             self._model.connect('node_added', self.on_model_node_added)
             try:
-                self.cnode = self._model.root.children[0]
+                self.cursor_node = self._model.root.children[0]
             except IndexError:
                 pass
             self._update_sizereq()
 
     def on_model_node_added(self, ev):
-        if self.cnode is None:
-            self.cnode = ev.node
+        if self.cursor_node is None:
+            self.cursor_node = ev.node
         self._update_sizereq()
         self.redraw()
 
+    @property
+    def cursor_node(self):
+        return self._cursor_node
+
+    @cursor_node.setter
+    def cursor_node(self, value):
+        self._cursor_node = value
+        self._update_spot()
+
     def collapse(self, path, collapse=True):
         node = self._model.find(path)
         self.collapse_node(node, collapse)
 
     def collapse_node(self, node, collapse=True):
         if collapse:
-            if not node in self.collapsed and len(node.children) > 0:
-                self.collapsed.append(node)
+            if not node in self.collapsed_nodes and len(node.children) > 0:
+                self.collapsed_nodes.append(node)
                 self.emit('collapse', node)
         else:
             try:
-                self.collapsed.remove(node)
+                self.collapsed_nodes.remove(node)
                 self.emit('expand', node)
             except ValueError:
                 pass
@@ -262,12 +270,12 @@
                 lines &= ~(1 << level-1)
             # draw lines and titles
             head = ''.join(head)
-            if node in self.collapsed:
+            if node in self.collapsed_nodes:
                 sep = '+'
             else:
                 sep = ' '
             ev.driver.puts(ev.x, y, head + sep + str(node))
-            if node is self.cnode:
+            if node is self.cursor_node:
                 ev.driver.pushcolor('active')
                 ev.driver.puts(ev.x + len(head), y, sep + str(node) + ' ')
                 ev.driver.popcolor()
@@ -291,7 +299,7 @@
         i = parent.children.index(node)
         if i > 0:
             node = parent.children[i-1]
-            while node not in self.collapsed and len(node.children) > 0:
+            while node not in self.collapsed_nodes and len(node.children) > 0:
                 node = node.children[-1]
             return node
         else:
@@ -300,7 +308,7 @@
             return parent
 
     def next_node(self, node):
-        if node in self.collapsed or len(node.children) == 0:
+        if node in self.collapsed_nodes or len(node.children) == 0:
             # next sibling
             parent = node.parent
             while parent is not None:
@@ -315,24 +323,24 @@
             return node.children[0]
 
     def move_up(self):
-        prev = self.prev_node(self.cnode)
+        prev = self.prev_node(self.cursor_node)
         if prev is not None:
-            self.cnode = prev
+            self.cursor_node = prev
             return True
         return False
 
     def move_down(self):
-        node = self.next_node(self.cnode)
+        node = self.next_node(self.cursor_node)
         if node is not None:
-            self.cnode = node
+            self.cursor_node = node
             return True
         return False
 
     def move_left(self):
-        self.collapse_node(self.cnode, True)
+        self.collapse_node(self.cursor_node, True)
 
     def move_right(self):
-        self.collapse_node(self.cnode, False)
+        self.collapse_node(self.cursor_node, False)
 
     def _update_sizereq(self):
         height = 0
@@ -340,3 +348,11 @@
             height = num
         self.sizereq.h = height
 
+    def _update_spot(self):
+        """Update spot to current position of cursor node."""
+        for num, (_level, _index, _count, node) in enumerate(self):
+            if node is self.cursor_node:
+                self._spot.x = _level * 2
+                self._spot.y = num
+        self.emit('spotmove')
+
--- a/tuikit/widget.py	Wed Jan 02 00:16:12 2013 +0100
+++ b/tuikit/widget.py	Wed Jan 02 11:48:36 2013 +0100
@@ -50,24 +50,30 @@
         #: Hidden widget does not affect layout.
         self.hidden = False
 
-        # cursor
+        #: Cursor is position where text input will occur,
+        #: i.e. classic blinking cursor in console.
+        #: None means no cursor (hidden).
         self.cursor = None
 
+        # See spot property.
+        self._spot = Coords()
+
         # redraw request
         self._redraw = True
 
         # event handlers
         self.add_events(
             'resize', Event,
-            'sizereq', Event,
             'draw', DrawEvent,
-            'focus', FocusEvent,
-            'unfocus', FocusEvent,
             'keypress', KeyboardEvent,
             'mousedown', MouseEvent,
             'mouseup', MouseEvent,
             'mousemove', MouseEvent,
-            'mousewheel', MouseEvent)
+            'mousewheel', MouseEvent,
+            'sizereq', Event,
+            'spotmove', Event,
+            'focus', FocusEvent,
+            'unfocus', FocusEvent)
 
 
     @property
@@ -116,6 +122,17 @@
         return self._sizereq
 
     @property
+    def spot(self):
+        """Spot of visibility.
+
+        This is one point which should be always visible.
+        It affects scrolling (moving spot in widget placed in ScrollView scrolls the view).
+
+        """
+        return self._spot
+
+
+    @property
     def top(self):
         """Top widget (same for every widget in one application)."""
         return self._top