diff urwid_satext/sat_widgets.py @ 90:f5992b2a0dbf

FocusFrame refactoring: it's based on Urwid.Frame again, no more Pile, and it's simplified a lot. Now it just add FOCUS_* keys management
author Goffi <goffi@goffi.org>
date Mon, 08 Sep 2014 15:42:25 +0200
parents 2141f07b5fdd
children b447a9c6f1d3
line wrap: on
line diff
--- a/urwid_satext/sat_widgets.py	Mon Sep 08 15:42:25 2014 +0200
+++ b/urwid_satext/sat_widgets.py	Mon Sep 08 15:42:25 2014 +0200
@@ -25,6 +25,8 @@
 from urwid.util import is_mouse_press #XXX: is_mouse_press is not included in urwid in 1.0.0
 from .keys import action_key_map as a_key
 
+FOCUS_KEYS = (a_key['FOCUS_SWITCH'], a_key['FOCUS_UP'], a_key['FOCUS_DOWN'])
+
 
 class AdvancedEdit(urwid.Edit):
     """Edit box with some custom improvments
@@ -1087,101 +1089,62 @@
         return urwid.CanvasJoin(render)
 
 
-class FocusFrame(urwid.Pile):
+class FocusFrame(urwid.Frame):
     """Frame-like which manage 'tab' key"""
-    _sizing = frozenset(['box'])
-
-    def __init__(self, body, header=None, footer=None, focus_part='body'):
-        self._header = self._footer = None
-        super(FocusFrame, self).__init__([body])
-        self.header = header
-        self._body = body
-        self.footer = footer
-        self.focus_position = focus_part
-
-    def _focus_part_recalc(self):
-        self._FOCUS_PARTS=[]
-        if self._header is not None:
-            self._FOCUS_PARTS.append('header')
-        self._FOCUS_PARTS.append('body')
-        if self._footer is not None:
-            self._FOCUS_PARTS.append('footer')
-        assert(len(self._FOCUS_PARTS) == len(self.contents))
-
-    @property
-    def header(self):
-        return self._header
-
-    @header.setter
-    def header(self, widget):
-        content = (widget, ('pack', None))
-        if widget is None:
-            if self._header is not None:
-                del self.contents[0]
-        elif self._header is None:
-            self.contents.insert(0, content)
-        else:
-            self.contents[0] = content
-        self._header = widget
-        self._focus_part_recalc()
-
-    @property
-    def body(self):
-        return self._body
-
-    @body.setter
-    def body(self, widget):
-        assert widget is not None
-        idx = self._FOCUS_PARTS.index('body')
-        self.contents[idx] = (widget, ("weight", 1))
-
-    @property
-    def footer(self):
-        return self._footer
-
-    @footer.setter
-    def footer(self, widget):
-        content = (widget, ('pack', None))
-        if widget is None:
-            if self._footer is not None:
-                del self.contents[-1]
-        elif self._footer is None:
-            self.contents.append(content)
-        else:
-            self.contents[-1] = content
-        self._footer = widget
-        self._focus_part_recalc()
-
-    @property
-    def focus_position_named(self):
-        return self._FOCUS_PARTS[self.int_focus_position]
-
-    @property
-    def focus_position(self):
-        return super(FocusFrame, self).focus_position
-
-    @focus_position.setter
-    def focus_position(self, position):
-        if isinstance(position, basestring):
-            try:
-                position = self._FOCUS_PARTS.index(position)
-            except IndexError:
-               raise IndexError("This FocusFrame has no %s" % position)
-        urwid.Pile.focus_position.__set__(self, position)
 
     def keypress(self, size, key):
         ret = super(FocusFrame, self).keypress(size, key)
         if not ret:
             return
 
-        if key == a_key['FOCUS_SWITCH']:
+        if key in FOCUS_KEYS:
+            direction = 1 if key in (a_key['FOCUS_SWITCH'], a_key['FOCUS_UP']) else -1
+            rotate = key == a_key['FOCUS_SWITCH']
+
+            selectables = [] # keep positions which exists and have a selectable widget
+            for position in reversed(self):
+                if self.contents[position][0].selectable():
+                    selectables.append(position)
+            if not selectables:
+                # no widget is selectable, we just return
+                return
+            idx = selectables.index(self.focus_position) + direction
+            if not rotate and (idx < 0 or idx >= len(selectables)):
+                # if we don't rotate, we stay where we are on the first and last position
+                return
             try:
-                self.focus_position -= 1
+                self.focus_position = selectables[idx]
             except IndexError:
-                self.focus_position = 2
+                # happen if idx > len(selectables)
+                self.focus_position = selectables[0]
+            return
 
         return ret
 
+    def get_cursor_coords(self, size):
+        """Return the cursor coordinates of the focus widget."""
+        if not self.selectable():
+            return None
+        if not hasattr(self.focus, 'get_cursor_coords'):
+            return None
+        maxcol, maxrow = size
+        try:
+            if self.focus_position != 'body':
+                # only body is a box widget
+                size = (maxcol,)
+            col, row = self.focus.get_cursor_coords(size)
+        except TypeError:
+            return None
+        if self.focus_position == 'header':
+            return (col, row)
+        if self.focus_position == 'body':
+            header_rows = self.header.rows((maxcol,))
+            return (col, row + header_rows)
+        if self.focus_position == 'footer':
+            footer_rows = self.footer.rows((maxcol,))
+            return (col, row + (maxrow - footer_rows))
+        raise Exception('This line should not be reached')
+
 
 class TabsContainer(urwid.WidgetWrap):
     """ Container which can contain multiple box widgets associated to named tabs """
@@ -1191,8 +1154,8 @@
         self._current_tab = None
         self._buttons_cont = ColumnsRoller()
         self.tabs = []
-        self.__frame = FocusFrame(urwid.Filler(urwid.Text('')),urwid.Pile([self._buttons_cont,urwid.Divider(u"─")]))
-        urwid.WidgetWrap.__init__(self, self.__frame)
+        self._frame = FocusFrame(urwid.Filler(urwid.Text('')),urwid.Pile([self._buttons_cont,urwid.Divider(u"─")]))
+        urwid.WidgetWrap.__init__(self, self._frame)
 
     def keypress(self, size, key):
         return self._w.keypress(size,key)
@@ -1209,7 +1172,7 @@
         if tab[0] != tab_name:
             log.error(_("INTERNAL ERROR: Tab not found"))
             assert(False)
-        self.__frame.body = tab[1]
+        self._frame.body = tab[1]
         button.set_label(('title',button.get_label()))
         if self._current_tab:
             self._current_tab.set_label(self._current_tab.get_label())