tuikit/core/container.py
changeset 109 105b1affc3c2
parent 97 0c2e0c09ba5c
child 111 b055add74b18
--- a/tuikit/core/container.py	Fri Mar 28 19:58:59 2014 +0100
+++ b/tuikit/core/container.py	Wed Sep 03 19:08:21 2014 +0200
@@ -14,19 +14,22 @@
     def __init__(self, layout_class=FixedLayout):
         Widget.__init__(self)
         #: List of child widgets.
-        self.children = []
-        self.focus_child = None
+        self._widgets = []
+        #: Widget with keyboard focus
+        self.focus_widget = None
+        #: If True, tab cycles inside container
+        self.trap_focus = False
         self.layout = layout_class()
 
     def add(self, widget):
         """Add widget into container."""
-        self.children.append(widget)
+        self._widgets.append(widget)
         widget.parent = self
         widget.window = self.window
         widget.set_theme(self.theme)
         self.layout.add(widget)
-        if self.focus_child is None:
-            self.focus_child = widget
+        if self.focus_widget is None and widget.can_focus():
+            self.focus_widget = widget
 
     def resize(self, w, h):
         Widget.resize(self, w, h)
@@ -35,7 +38,7 @@
     def draw(self, buffer):
         """Draw child widgets."""
         Widget.draw(self, buffer)
-        for child in self.children:
+        for child in self._widgets:
             with buffer.moved_origin(child.x, child.y):
                 with buffer.clip(buffer.origin.x, buffer.origin.y,
                                  child.width, child.height):
@@ -43,21 +46,75 @@
 
     def set_theme(self, theme):
         Widget.set_theme(self, theme)
-        for child in self.children:
+        for child in self._widgets:
             child.set_theme(theme)
 
     @property
     def cursor(self):
-        if self.focus_child:
-            cursor = self.focus_child.cursor
+        if self.focus_widget:
+            cursor = self.focus_widget.cursor
             if cursor is not None:
-                return cursor.moved(*self.focus_child.pos)
+                return cursor.moved(*self.focus_widget.pos)
         else:
             if self._cursor is not None:
                 return Point(self._cursor)
 
     ## input events ##
 
-    def keypress(self, keyname, char, mod=0):
-        if self.focus_child:
-            self.focus_child.keypress(keyname, char, mod)
+    def keypress(self, keyname, char, mod):
+        # First, handle the keypress event to focused child widget
+        if self.focus_widget is not None:
+            if self.focus_widget.keypress(keyname, char, mod):
+                return True
+        # Next, handle default key behaviour by Container
+        if keyname == 'tab':
+            return self.focus_next(-1 if 'shift' in mod else 1)
+        # Finally, handle default keys by Widget
+        # and send keypress signal
+        if Widget.keypress(self, keyname, char, mod):
+            return True
+
+    ## focus ##
+
+    def focus_next(self, step=1):
+        """Focus next child.
+
+        Sets focus to next child, if there is one
+        which can be focused. Cycles from last child
+        to first when needed. Return value depends on
+        this cycling:
+
+         * False means there wasn't any child to focus
+           before end of list. Focus was either not changed
+           or first child was focused.
+
+         * True when focus is set to next child in normal
+           way or when self.trap_focus is set.
+
+        Return value is supposed to be returned from keypress
+        event - in that case, True stops event propagation.
+
+        """
+        if self.focus_widget is None:
+            idx_current = 0
+        else:
+            idx_current = self._widgets.index(self.focus_widget)
+        idx_new = idx_current
+        cycled = False
+        while True:
+            idx_new += step
+            if idx_new >= len(self._widgets):
+                idx_new = 0
+                cycled = True
+            if idx_new < 0:  # for focus_previous
+                idx_new = len(self._widgets) - 1
+                cycled = True
+            if idx_current == idx_new:
+                return False
+            if self._widgets[idx_new].can_focus():
+                self.focus_widget = self._widgets[idx_new]
+                return self.trap_focus or not cycled
+
+    def focus_previous(self):
+        """Focus previous child."""
+        self.focus_next(-1)