summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIan Ward <ian@excess.org>2016-12-21 10:44:29 -0500
committerGitHub <noreply@github.com>2016-12-21 10:44:29 -0500
commitfee50e77e85fb0fc5f7d664ae13c0e139d711a00 (patch)
treed127e4cbd623a79761e80c81a253c2af61e29d14
parentdc8e8b3fdb0bed9fe6cf8781b4c0b1396b7d406f (diff)
parente47c6b27ee2ed2d290ab31ac415ff3b84fbfee6a (diff)
downloadurwid-fee50e77e85fb0fc5f7d664ae13c0e139d711a00.tar.gz
Merge pull request #210 from rndusr/listbox-body-property
Make ListBox.body an invalidating property
-rw-r--r--urwid/listbox.py126
1 files changed, 69 insertions, 57 deletions
diff --git a/urwid/listbox.py b/urwid/listbox.py
index 5370e23..72ce2d5 100644
--- a/urwid/listbox.py
+++ b/urwid/listbox.py
@@ -278,13 +278,9 @@ class ListBox(Widget, WidgetContainerMixin):
widgets to be displayed inside the list box
:type body: ListWalker
"""
- if getattr(body, 'get_focus', None):
- self.body = body
- else:
- self.body = PollingListWalker(body)
-
+ self.body = body
try:
- connect_signal(self.body, "modified", self._invalidate)
+ connect_signal(self._body, "modified", self._invalidate)
except NameError:
# our list walker has no modified signal so we must not
# cache our canvases because we don't know when our
@@ -310,6 +306,22 @@ class ListBox(Widget, WidgetContainerMixin):
self.set_focus_valign_pending = None
+ def _get_body(self):
+ return self._body
+
+ def _set_body(self, body):
+ if getattr(body, 'get_focus', None):
+ self._body = body
+ else:
+ self._body = PollingListWalker(body)
+ self._invalidate()
+
+ body = property(_get_body, _set_body, doc="""
+ a ListWalker subclass such as :class:`SimpleFocusListWalker` that contains
+ widgets to be displayed inside the list box
+ """)
+
+
def calculate_visible(self, size, focus=False ):
"""
Returns the widgets that would be displayed in
@@ -339,7 +351,7 @@ class ListBox(Widget, WidgetContainerMixin):
self._set_focus_complete( (maxcol, maxrow), focus )
# 1. start with the focus widget
- focus_widget, focus_pos = self.body.get_focus()
+ focus_widget, focus_pos = self._body.get_focus()
if focus_widget is None: #list box is empty?
return None,None,None
top_pos = focus_pos
@@ -377,7 +389,7 @@ class ListBox(Widget, WidgetContainerMixin):
fill_above = []
top_pos = pos
while fill_lines > 0:
- prev, pos = self.body.get_prev( pos )
+ prev, pos = self._body.get_prev( pos )
if prev is None: # run out of widgets above?
offset_rows -= fill_lines
break
@@ -399,7 +411,7 @@ class ListBox(Widget, WidgetContainerMixin):
fill_lines = maxrow - focus_rows - offset_rows + inset_rows
fill_below = []
while fill_lines > 0:
- next, pos = self.body.get_next( pos )
+ next, pos = self._body.get_next( pos )
if next is None: # run out of widgets below?
break
@@ -426,7 +438,7 @@ class ListBox(Widget, WidgetContainerMixin):
trim_top = 0
pos = top_pos
while fill_lines > 0:
- prev, pos = self.body.get_prev( pos )
+ prev, pos = self._body.get_prev( pos )
if prev is None:
break
@@ -505,7 +517,7 @@ class ListBox(Widget, WidgetContainerMixin):
if rows < maxrow:
bottom_pos = focus_pos
if fill_below: bottom_pos = fill_below[-1][1]
- if trim_bottom != 0 or self.body.get_next(bottom_pos) != (None,None):
+ if trim_bottom != 0 or self._body.get_next(bottom_pos) != (None,None):
raise ListBoxError, "Listbox contents too short! Probably urwid's fault (please report): %r" % ((top,middle,bottom),)
final_canvas.pad_trim_top_bottom(0, maxrow - rows)
@@ -551,7 +563,7 @@ class ListBox(Widget, WidgetContainerMixin):
"""
Set the focus position and try to keep the old focus in view.
- :param position: a position compatible with :meth:`self.body.set_focus`
+ :param position: a position compatible with :meth:`self._body.set_focus`
:param coming_from: set to 'above' or 'below' if you know that
old position is above or below the new position.
:type coming_from: str
@@ -559,12 +571,12 @@ class ListBox(Widget, WidgetContainerMixin):
if coming_from not in ('above', 'below', None):
raise ListBoxError("coming_from value invalid: %r" %
(coming_from,))
- focus_widget, focus_pos = self.body.get_focus()
+ focus_widget, focus_pos = self._body.get_focus()
if focus_widget is None:
raise IndexError("Can't set focus, ListBox is empty")
self.set_focus_pending = coming_from, focus_widget, focus_pos
- self.body.set_focus(position)
+ self._body.set_focus(position)
def get_focus(self):
"""
@@ -572,13 +584,13 @@ class ListBox(Widget, WidgetContainerMixin):
compatibility. You may also use the new standard container
properties :attr:`focus` and :attr:`focus_position` to read these values.
"""
- return self.body.get_focus()
+ return self._body.get_focus()
def _get_focus(self):
"""
Return the widget in focus according to our :obj:`list walker <ListWalker>`.
"""
- return self.body.get_focus()[0]
+ return self._body.get_focus()[0]
focus = property(_get_focus,
doc="the child widget in focus or None when ListBox is empty")
@@ -588,7 +600,7 @@ class ListBox(Widget, WidgetContainerMixin):
of value returned depends on the :obj:`list walker <ListWalker>`.
"""
- w, pos = self.body.get_focus()
+ w, pos = self._body.get_focus()
if w is None:
raise IndexError, "No focus_position, ListBox is empty"
return pos
@@ -605,22 +617,22 @@ class ListBox(Widget, WidgetContainerMixin):
return ListBoxContents()
def _contents__getitem__(self, key):
# try list walker protocol v2 first
- getitem = getattr(self.body, '__getitem__', None)
+ getitem = getattr(self._body, '__getitem__', None)
if getitem:
try:
return (getitem(key), None)
except (IndexError, KeyError):
raise KeyError("ListBox.contents key not found: %r" % (key,))
# fall back to v1
- w, old_focus = self.body.get_focus()
+ w, old_focus = self._body.get_focus()
try:
try:
- self.body.set_focus(key)
- return self.body.get_focus()[0]
+ self._body.set_focus(key)
+ return self._body.get_focus()[0]
except (IndexError, KeyError):
raise KeyError("ListBox.contents key not found: %r" % (key,))
finally:
- self.body.set_focus(old_focus)
+ self._body.set_focus(old_focus)
contents = property(lambda self: self._contents, doc="""
An object that allows reading widgets from the ListBox's list
walker as a `(widget, options)` tuple. `None` is currently the only
@@ -652,7 +664,7 @@ class ListBox(Widget, WidgetContainerMixin):
self.set_focus_valign_pending = None
self.set_focus_pending = None
- focus_widget, focus_pos = self.body.get_focus()
+ focus_widget, focus_pos = self._body.get_focus()
if focus_widget is None:
return
@@ -687,7 +699,7 @@ class ListBox(Widget, WidgetContainerMixin):
new_row_offset = row_offset + focus_rows
for widget, pos, rows in fill_below:
if widget.selectable():
- self.body.set_focus(pos)
+ self._body.set_focus(pos)
self.shift_focus((maxcol, maxrow),
new_row_offset)
return
@@ -709,13 +721,13 @@ class ListBox(Widget, WidgetContainerMixin):
self.set_focus_pending = None
# new position
- new_focus_widget, position = self.body.get_focus()
+ new_focus_widget, position = self._body.get_focus()
if focus_pos == position:
# do nothing
return
# restore old focus temporarily
- self.body.set_focus(focus_pos)
+ self._body.set_focus(focus_pos)
middle,top,bottom=self.calculate_visible((maxcol,maxrow),focus)
focus_offset, focus_widget, focus_pos, focus_rows, cursor=middle
@@ -739,8 +751,8 @@ class ListBox(Widget, WidgetContainerMixin):
offset += rows
# failed to find widget among visible widgets
- self.body.set_focus( position )
- widget, position = self.body.get_focus()
+ self._body.set_focus( position )
+ widget, position = self._body.get_focus()
rows = widget.rows((maxcol,), focus)
if coming_from=='below':
@@ -776,7 +788,7 @@ class ListBox(Widget, WidgetContainerMixin):
self.offset_rows = offset_inset
self.inset_fraction = (0,1)
else:
- target, _ignore = self.body.get_focus()
+ target, _ignore = self._body.get_focus()
tgt_rows = target.rows( (maxcol,), True )
if offset_inset + tgt_rows <= 0:
raise ListBoxError, "Invalid offset_inset: %r, only %r rows in target!" %(offset_inset, tgt_rows)
@@ -789,7 +801,7 @@ class ListBox(Widget, WidgetContainerMixin):
# TODO: should this not be private?
(maxcol, maxrow) = size
- widget, old_pos = self.body.get_focus()
+ widget, old_pos = self._body.get_focus()
if widget is None: return
pref_col = None
@@ -813,7 +825,7 @@ class ListBox(Widget, WidgetContainerMixin):
See also :meth:`.set_focus`.
:param size: see :meth:`Widget.render` for details
- :param position: a position compatible with :meth:`self.body.set_focus`
+ :param position: a position compatible with :meth:`self._body.set_focus`
:param offset_inset: either the number of rows between the
top of the listbox and the start of the focus widget (+ve
value) or the number of lines of the focus widget hidden off
@@ -840,8 +852,8 @@ class ListBox(Widget, WidgetContainerMixin):
self.update_pref_col_from_focus((maxcol,maxrow))
self._invalidate()
- self.body.set_focus(position)
- target, _ignore = self.body.get_focus()
+ self._body.set_focus(position)
+ target, _ignore = self._body.get_focus()
tgt_rows = target.rows( (maxcol,), True)
if snap_rows is None:
snap_rows = maxrow - 1
@@ -920,7 +932,7 @@ class ListBox(Widget, WidgetContainerMixin):
def get_focus_offset_inset(self, size):
"""Return (offset rows, inset rows) for focus widget."""
(maxcol, maxrow) = size
- focus_widget, pos = self.body.get_focus()
+ focus_widget, pos = self._body.get_focus()
focus_rows = focus_widget.rows((maxcol,), True)
offset_rows = self.offset_rows
inset_rows = 0
@@ -938,7 +950,7 @@ class ListBox(Widget, WidgetContainerMixin):
"""Shift the focus widget so that its cursor is visible."""
(maxcol, maxrow) = size
- focus_widget, pos = self.body.get_focus()
+ focus_widget, pos = self._body.get_focus()
if focus_widget is None:
return
if not focus_widget.selectable():
@@ -978,7 +990,7 @@ class ListBox(Widget, WidgetContainerMixin):
if self.set_focus_pending or self.set_focus_valign_pending:
self._set_focus_complete( (maxcol,maxrow), focus=True )
- focus_widget, pos = self.body.get_focus()
+ focus_widget, pos = self._body.get_focus()
if focus_widget is None: # empty listbox, can't do anything
return key
@@ -1038,7 +1050,7 @@ class ListBox(Widget, WidgetContainerMixin):
while row_offset > 0:
# need to scroll in another candidate widget
- widget, pos = self.body.get_prev(pos)
+ widget, pos = self._body.get_prev(pos)
if widget is None:
# cannot scroll any further
return True # keypress not handled
@@ -1068,7 +1080,7 @@ class ListBox(Widget, WidgetContainerMixin):
# choose another focus
if widget is None:
# try harder to get prev widget
- widget, pos = self.body.get_prev(pos)
+ widget, pos = self._body.get_prev(pos)
if widget is None:
return # can't do anything
rows = widget.rows((maxcol,), True)
@@ -1116,7 +1128,7 @@ class ListBox(Widget, WidgetContainerMixin):
while row_offset < maxrow:
# need to scroll in another candidate widget
- widget, pos = self.body.get_next(pos)
+ widget, pos = self._body.get_next(pos)
if widget is None:
# cannot scroll any further
return True # keypress not handled
@@ -1150,7 +1162,7 @@ class ListBox(Widget, WidgetContainerMixin):
# choose another focus
if widget is None:
# try harder to get next widget
- widget, pos = self.body.get_next(pos)
+ widget, pos = self._body.get_next(pos)
if widget is None:
return # can't do anything
else:
@@ -1220,7 +1232,7 @@ class ListBox(Widget, WidgetContainerMixin):
# add newly visible ones, including within snap_rows
snap_region_start = len(t)
while row_offset > -snap_rows:
- widget, pos = self.body.get_prev(pos)
+ widget, pos = self._body.get_prev(pos)
if widget is None: break
rows = widget.rows((maxcol,))
row_offset -= rows
@@ -1274,7 +1286,7 @@ class ListBox(Widget, WidgetContainerMixin):
(self.pref_col, pref_row), snap_rows )
# if we're as far up as we can scroll, take this one
- if (fill_above and self.body.get_prev(fill_above[-1][1])
+ if (fill_above and self._body.get_prev(fill_above[-1][1])
== (None,None) ):
pass #return
@@ -1306,7 +1318,7 @@ class ListBox(Widget, WidgetContainerMixin):
if fill_above and focus_widget.selectable():
# if we're at the top and have a selectable, return
- if self.body.get_prev(fill_above[-1][1]) == (None,None):
+ if self._body.get_prev(fill_above[-1][1]) == (None,None):
pass #return
# if still none found choose the topmost widget
@@ -1343,7 +1355,7 @@ class ListBox(Widget, WidgetContainerMixin):
if not t:
return
_ign1, _ign2, pos, _ign3 = t[-1]
- widget, pos = self.body.get_prev(pos)
+ widget, pos = self._body.get_prev(pos)
if widget is None:
# no dice, we're stuck here
return
@@ -1405,7 +1417,7 @@ class ListBox(Widget, WidgetContainerMixin):
# add newly visible ones, including within snap_rows
snap_region_start = len(t)
while row_offset < maxrow+snap_rows:
- widget, pos = self.body.get_next(pos)
+ widget, pos = self._body.get_next(pos)
if widget is None: break
rows = widget.rows((maxcol,))
t.append( (row_offset, widget, pos, rows) )
@@ -1519,7 +1531,7 @@ class ListBox(Widget, WidgetContainerMixin):
if not t:
return
_ign1, _ign2, pos, _ign3 = t[-1]
- widget, pos = self.body.get_next(pos)
+ widget, pos = self._body.get_next(pos)
if widget is None:
# no dice, we're stuck here
return
@@ -1594,14 +1606,14 @@ class ListBox(Widget, WidgetContainerMixin):
row_offset += rows
if row_offset < maxrow:
l.append('bottom')
- elif self.body.get_next(pos) == (None,None):
+ elif self._body.get_next(pos) == (None,None):
l.append('bottom')
if trim_top == 0:
row_offset, w, pos, rows, c = middle
for w, pos, rows in above:
row_offset -= rows
- if self.body.get_prev(pos) == (None,None):
+ if self._body.get_prev(pos) == (None,None):
l.insert(0, 'top')
return l
@@ -1610,28 +1622,28 @@ class ListBox(Widget, WidgetContainerMixin):
"""
Return an iterator over the positions in this ListBox.
- If self.body does not implement positions() then iterate
+ If self._body does not implement positions() then iterate
from the focus widget down to the bottom, then from above
the focus up to the top. This is the best we can do with
a minimal list walker implementation.
"""
- positions_fn = getattr(self.body, 'positions', None)
+ positions_fn = getattr(self._body, 'positions', None)
if positions_fn:
for pos in positions_fn():
yield pos
return
- focus_widget, focus_pos = self.body.get_focus()
+ focus_widget, focus_pos = self._body.get_focus()
if focus_widget is None:
return
pos = focus_pos
while True:
yield pos
- w, pos = self.body.get_next(pos)
+ w, pos = self._body.get_next(pos)
if not w: break
pos = focus_pos
while True:
- w, pos = self.body.get_prev(pos)
+ w, pos = self._body.get_prev(pos)
if not w: break
yield pos
@@ -1645,24 +1657,24 @@ class ListBox(Widget, WidgetContainerMixin):
reverse of what `__iter__()` produces, but this is the best we can
do with a minimal list walker implementation.
"""
- positions_fn = getattr(self.body, 'positions', None)
+ positions_fn = getattr(self._body, 'positions', None)
if positions_fn:
for pos in positions_fn(reverse=True):
yield pos
return
- focus_widget, focus_pos = self.body.get_focus()
+ focus_widget, focus_pos = self._body.get_focus()
if focus_widget is None:
return
pos = focus_pos
while True:
- w, pos = self.body.get_prev(pos)
+ w, pos = self._body.get_prev(pos)
if not w: break
yield pos
pos = focus_pos
while True:
yield pos
- w, pos = self.body.get_next(pos)
+ w, pos = self._body.get_next(pos)
if not w: break