Add Widget.spot property. TreeView: move spot with cursor node. ScrollView: scroll when spot moves.
--- 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