summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--reference.html100
-rw-r--r--setup.py4
-rw-r--r--tutorial.html646
-rwxr-xr-xurwid/curses_display.py5
-rwxr-xr-xurwid/html_fragment.py5
-rw-r--r--urwid/listbox.py85
-rwxr-xr-xurwid/widget.py71
7 files changed, 869 insertions, 47 deletions
diff --git a/reference.html b/reference.html
index fa39600..551fb85 100644
--- a/reference.html
+++ b/reference.html
@@ -14,11 +14,12 @@
<div style="text-align: center;">
<a href="http://excess.org/urwid/">Urwid Home Page</a> /
<a href="http://excess.org/urwid/examples.html">Example Screenshots</a> /
-<a href="http://excess.org/urwid/wexamples.html">Wide Character Screenshots</a> /
+<a href="http://excess.org/urwid/wexamples.zh-cn.html">Wide Character Screenshots</a> /
+<a href="tutorial.html">Tutorial</a> /
Reference
</div>
<br>
-<div class="l1">User interface library wrapper</div><div class="l2"><a href="#curses_display.Screen">curses_display.Screen</a></div><div class="l1">Top-level widgets</div><div class="l2"><a href="#BoxWidget">BoxWidget</a></div><div class="l2"><a href="#Frame">Frame</a></div><div class="l2"><a href="#ListBox">ListBox</a></div><div class="l2"><a href="#SimpleListWalker">SimpleListWalker</a></div><div class="l1">Content widgets</div><div class="l2"><a href="#FlowWidget">FlowWidget</a></div><div class="l2"><a href="#Text">Text</a></div><div class="l2"><a href="#Edit">Edit</a></div><div class="l2"><a href="#IntEdit">IntEdit</a></div><div class="l1">Composite widgets</div><div class="l2"><a href="#Columns">Columns</a></div><div class="l2"><a href="#Pile">Pile</a></div><div class="l1">Decorations</div><div class="l2"><a href="#AttrWrap">AttrWrap</a></div><div class="l2"><a href="#Divider">Divider</a></div><div class="l1">Canvas painting</div><div class="l2"><a href="#canvas.Canvas">canvas.Canvas</a></div><div class="l2"><a href="#-canvas.CanvasCombine">canvas.CanvasCombine</a></div><div class="l2"><a href="#-canvas.CanvasJoin">canvas.CanvasJoin</a></div><div class="l1">Custom formatting rules</div><div class="l2"><a href="#-util.register_align_mode">util.register_align_mode</a></div><div class="l2"><a href="#-util.register_wrap_mode">util.register_wrap_mode</a></div><div class="l2"><a href="#-util.set_double_byte_encoding">util.set_double_byte_encoding</a></div><div class="l1">Screen capture</div><div class="l2"><a href="#-html_fragment.screenshot_init">html_fragment.screenshot_init</a></div><div class="l2"><a href="#-html_fragment.screenshot_collect">html_fragment.screenshot_collect</a></div><div class="l2"><a href="#html_fragment.HtmlGenerator">html_fragment.HtmlGenerator</a></div><h2>User interface library wrapper</h2><h3><strong>curses_display.Screen</strong> = <a name="curses_display.Screen">class Screen</a></h3>Methods defined here:<br>
+<table width="100%"><tr><td width="33%" valign="top"><div class="l1">User interface wrapper</div><div class="l2"><a href="#curses_display.Screen">curses_display.Screen</a></div><div class="l1">Top-level widgets</div><div class="l2"><a href="#BoxWidget">BoxWidget</a></div><div class="l2"><a href="#Frame">Frame</a></div><div class="l2"><a href="#Filler">Filler</a></div><div class="l2"><a href="#ListBox">ListBox</a></div><div class="l2"><a href="#SimpleListWalker">SimpleListWalker</a></div><div class="l1">Decorations</div><div class="l2"><a href="#AttrWrap">AttrWrap</a></div><div class="l2"><a href="#Divider">Divider</a></div></td><td width="33%" valign="top"><div class="l1">Content widgets</div><div class="l2"><a href="#FlowWidget">FlowWidget</a></div><div class="l2"><a href="#Text">Text</a></div><div class="l2"><a href="#Edit">Edit</a></div><div class="l2"><a href="#IntEdit">IntEdit</a></div><div class="l1">Composite widgets</div><div class="l2"><a href="#Columns">Columns</a></div><div class="l2"><a href="#Pile">Pile</a></div><div class="l1">Canvas painting</div><div class="l2"><a href="#Canvas">Canvas</a></div><div class="l2"><a href="#-CanvasCombine">CanvasCombine</a></div><div class="l2"><a href="#-CanvasJoin">CanvasJoin</a></div></td><td width="33%" valign="top"><div class="l1">Custom formatting rules</div><div class="l2"><a href="#-util.register_align_mode">util.register_align_mode</a></div><div class="l2"><a href="#-util.register_wrap_mode">util.register_wrap_mode</a></div><div class="l2"><a href="#-util.set_double_byte_encoding">util.set_double_byte_encoding</a></div><div class="l1">Screen capture</div><div class="l2"><a href="#-html_fragment.screenshot_init">html_fragment.screenshot_init</a></div><div class="l2"><a href="#-html_fragment.screenshot_collect">html_fragment.screenshot_collect</a></div><div class="l2"><a href="#html_fragment.HtmlGenerator">html_fragment.HtmlGenerator</a></div></td></tr></table><h2>User interface wrapper</h2><h3><strong>curses_display.Screen</strong> = <a name="curses_display.Screen">class Screen</a></h3>Methods defined here:<br>
<dl><dt><a name="Screen-__init__"><strong>__init__</strong></a>(self)</dt></dl>
<dl><dt><a name="Screen-draw_screen"><strong>draw_screen</strong></a>(self, (cols, rows), r)</dt><dd><tt>Paint&nbsp;screen&nbsp;with&nbsp;rendered&nbsp;canvas.</tt></dd></dl>
@@ -53,7 +54,8 @@ Double-byte&nbsp;characters:&nbsp;&nbsp;"\xa1\xea",&nbsp;"\xb2\xd4"</tt></dd></d
<dl><dt><a name="Screen-register_palette"><strong>register_palette</strong></a>(self, l)</dt><dd><tt>Register&nbsp;a&nbsp;list&nbsp;of&nbsp;palette&nbsp;entries.<br>
&nbsp;<br>
-l&nbsp;--&nbsp;list&nbsp;of&nbsp;(name,&nbsp;foreground,&nbsp;background)&nbsp;or<br>
+l&nbsp;--&nbsp;list&nbsp;of&nbsp;(name,&nbsp;foreground,&nbsp;background,&nbsp;mono),<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(name,&nbsp;foreground,&nbsp;background)&nbsp;or<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(name,&nbsp;same_as_other_name)&nbsp;palette&nbsp;entries.<br>
&nbsp;<br>
calls&nbsp;self.<strong>register_palette_entry</strong>&nbsp;for&nbsp;each&nbsp;item&nbsp;in&nbsp;l</tt></dd></dl>
@@ -90,6 +92,16 @@ footer&nbsp;--&nbsp;a&nbsp;flow&nbsp;widget&nbsp;for&nbsp;below&nbsp;the&nbsp;bo
<dl><dt><a name="Frame-render"><strong>render</strong></a>(self, (maxcol, maxrow), focus<font color="#909090">=False</font>)</dt><dd><tt>Render&nbsp;frame&nbsp;and&nbsp;return&nbsp;it.</tt></dd></dl>
+<h3><a name="Filler">class <strong>Filler</strong></a>(BoxWidget)</h3>Methods defined here:<br>
+<dl><dt><a name="Filler-__init__"><strong>__init__</strong></a>(self, body, valign<font color="#909090">='middle'</font>)</dt><dd><tt>body&nbsp;--&nbsp;a&nbsp;flow&nbsp;widget&nbsp;to&nbsp;be&nbsp;filled&nbsp;around<br>
+valign&nbsp;--&nbsp;vertical&nbsp;alignment:&nbsp;"top",&nbsp;"middle"&nbsp;or&nbsp;"bottom"</tt></dd></dl>
+
+<dl><dt><a name="Filler-body_position"><strong>body_position</strong></a>(self, (maxcol, maxrow), focus, rows<font color="#909090">=None</font>, cy<font color="#909090">=None</font>)</dt><dd><tt>Return&nbsp;the&nbsp;row&nbsp;offset&nbsp;(+ve)&nbsp;or&nbsp;reduction&nbsp;(-ve)&nbsp;of&nbsp;self.<strong>body</strong>.</tt></dd></dl>
+
+<dl><dt><a name="Filler-keypress"><strong>keypress</strong></a>(self, (maxcol, maxrow), key)</dt><dd><tt>Pass&nbsp;keypress&nbsp;to&nbsp;self.<strong>body</strong>.</tt></dd></dl>
+
+<dl><dt><a name="Filler-render"><strong>render</strong></a>(self, (maxcol, maxrow), focus<font color="#909090">=False</font>)</dt><dd><tt>Render&nbsp;self.<strong>body</strong>&nbsp;with&nbsp;space&nbsp;around&nbsp;it&nbsp;to&nbsp;fill&nbsp;box&nbsp;size.</tt></dd></dl>
+
<h3><a name="ListBox">class <strong>ListBox</strong></a>(BoxWidget)</h3>Methods defined here:<br>
<dl><dt><a name="ListBox-__init__"><strong>__init__</strong></a>(self, body)</dt><dd><tt>body&nbsp;--&nbsp;list&nbsp;or&nbsp;a&nbsp;SimpleListWalker-like&nbsp;object&nbsp;that&nbsp;contains<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;widgets&nbsp;to&nbsp;be&nbsp;displayed&nbsp;inside&nbsp;the&nbsp;list&nbsp;box</tt></dd></dl>
@@ -127,6 +139,8 @@ snap_rows&nbsp;--&nbsp;the&nbsp;maximum&nbsp;number&nbsp;of&nbsp;extra&nbsp;rows
convenience&nbsp;function&nbsp;for&nbsp;checking&nbsp;whether&nbsp;the&nbsp;top&nbsp;and&nbsp;bottom<br>
of&nbsp;the&nbsp;list&nbsp;are&nbsp;visible</tt></dd></dl>
+<dl><dt><a name="ListBox-get_focus"><strong>get_focus</strong></a>(self)</dt><dd><tt>Return&nbsp;a&nbsp;(focus&nbsp;widget,&nbsp;focus&nbsp;position)&nbsp;tuple.</tt></dd></dl>
+
<dl><dt><a name="ListBox-keypress"><strong>keypress</strong></a>(self, (maxcol, maxrow), key)</dt><dd><tt>Move&nbsp;selection&nbsp;through&nbsp;the&nbsp;list&nbsp;elements&nbsp;scrolling&nbsp;when&nbsp;<br>
necessary.&nbsp;'up'&nbsp;and&nbsp;'down'&nbsp;are&nbsp;first&nbsp;passed&nbsp;to&nbsp;widget&nbsp;in&nbsp;focus<br>
in&nbsp;case&nbsp;that&nbsp;widget&nbsp;can&nbsp;handle&nbsp;them.&nbsp;'page&nbsp;up'&nbsp;and&nbsp;'page&nbsp;down'<br>
@@ -140,6 +154,12 @@ Keystrokes&nbsp;handled&nbsp;by&nbsp;this&nbsp;widget&nbsp;are:<br>
<dl><dt><a name="ListBox-render"><strong>render</strong></a>(self, (maxcol, maxrow), focus<font color="#909090">=False</font>)</dt><dd><tt>Render&nbsp;listbox&nbsp;and&nbsp;return&nbsp;canvas.</tt></dd></dl>
+<dl><dt><a name="ListBox-set_focus"><strong>set_focus</strong></a>(self, position, coming_from<font color="#909090">=None</font>)</dt><dd><tt>Set&nbsp;the&nbsp;focus&nbsp;position&nbsp;and&nbsp;try&nbsp;to&nbsp;keep&nbsp;the&nbsp;old&nbsp;focus&nbsp;in&nbsp;view.<br>
+&nbsp;<br>
+position&nbsp;--&nbsp;a&nbsp;position&nbsp;compatible&nbsp;with&nbsp;self.<strong>body</strong>.set_focus<br>
+coming_from&nbsp;--&nbsp;set&nbsp;to&nbsp;'above'&nbsp;or&nbsp;'below'&nbsp;if&nbsp;you&nbsp;know&nbsp;that<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;old&nbsp;position&nbsp;is&nbsp;above&nbsp;or&nbsp;below&nbsp;the&nbsp;new&nbsp;position.</tt></dd></dl>
+
<dl><dt><a name="ListBox-shift_focus"><strong>shift_focus</strong></a>(self, (maxcol, maxrow), offset_inset)</dt><dd><tt>Move&nbsp;the&nbsp;location&nbsp;of&nbsp;the&nbsp;current&nbsp;focus&nbsp;relative&nbsp;to&nbsp;the&nbsp;top.<br>
&nbsp;<br>
offset_inset&nbsp;--&nbsp;either&nbsp;the&nbsp;number&nbsp;of&nbsp;rows&nbsp;between&nbsp;the&nbsp;<br>
@@ -162,6 +182,40 @@ offset_inset&nbsp;--&nbsp;either&nbsp;the&nbsp;number&nbsp;of&nbsp;rows&nbsp;bet
<dl><dt><a name="SimpleListWalker-set_focus"><strong>set_focus</strong></a>(self, position)</dt><dd><tt>Set&nbsp;focus&nbsp;position.</tt></dd></dl>
+<h2>Decorations</h2><h3><a name="AttrWrap">class <strong>AttrWrap</strong></a></h3>Methods defined here:<br>
+<dl><dt><a name="AttrWrap-__init__"><strong>__init__</strong></a>(self, w, attr, focus_attr<font color="#909090">=None</font>)</dt><dd><tt>w&nbsp;--&nbsp;widget&nbsp;to&nbsp;wrap<br>
+attr&nbsp;--&nbsp;attribute&nbsp;to&nbsp;apply&nbsp;to&nbsp;w<br>
+focus_attr&nbsp;--&nbsp;attribute&nbsp;to&nbsp;apply&nbsp;when&nbsp;in&nbsp;focus,&nbsp;if&nbsp;None&nbsp;use&nbsp;attr<br>
+&nbsp;<br>
+Copy&nbsp;w.get_cursor_coords,&nbsp;w.move_cursor_to_coords,&nbsp;<br>
+w.get_pref_col&nbsp;functions&nbsp;to&nbsp;this&nbsp;widget&nbsp;if&nbsp;they&nbsp;exist.</tt></dd></dl>
+
+<dl><dt><a name="AttrWrap-keypress"><strong>keypress</strong></a>(self, maxvals, key)</dt><dd><tt>Pass&nbsp;keypress&nbsp;to&nbsp;self.<strong>w</strong>.</tt></dd></dl>
+
+<dl><dt><a name="AttrWrap-render"><strong>render</strong></a>(self, size, focus<font color="#909090">=False</font>)</dt><dd><tt>Render&nbsp;self.<strong>w</strong>&nbsp;and&nbsp;apply&nbsp;attribute.&nbsp;Return&nbsp;canvas.<br>
+&nbsp;<br>
+size&nbsp;--&nbsp;(maxcol,)&nbsp;if&nbsp;self.<strong>w</strong>&nbsp;contains&nbsp;a&nbsp;flow&nbsp;widget&nbsp;or<br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(maxcol,&nbsp;maxrow)&nbsp;if&nbsp;it&nbsp;contains&nbsp;a&nbsp;box&nbsp;widget.</tt></dd></dl>
+
+<dl><dt><a name="AttrWrap-rows"><strong>rows</strong></a>(self, (maxcol,), focus<font color="#909090">=False</font>)</dt><dd><tt>Return&nbsp;the&nbsp;rows&nbsp;needed&nbsp;for&nbsp;self.<strong>w</strong>.</tt></dd></dl>
+
+<dl><dt><a name="AttrWrap-selectable"><strong>selectable</strong></a>(self)</dt><dd><tt>Return&nbsp;the&nbsp;selectable&nbsp;value&nbsp;of&nbsp;self.<strong>w</strong>.</tt></dd></dl>
+
+<h3><a name="Divider">class <strong>Divider</strong></a>(FlowWidget)</h3>Methods defined here:<br>
+<dl><dt><a name="Divider-__init__"><strong>__init__</strong></a>(self, div_char<font color="#909090">=' '</font>, top<font color="#909090">=0</font>, bottom<font color="#909090">=0</font>)</dt><dd><tt>div_char&nbsp;--&nbsp;character&nbsp;to&nbsp;repeat&nbsp;across&nbsp;line<br>
+top&nbsp;--&nbsp;number&nbsp;of&nbsp;blank&nbsp;lines&nbsp;above<br>
+bottom&nbsp;--&nbsp;number&nbsp;of&nbsp;blank&nbsp;lines&nbsp;below</tt></dd></dl>
+
+<dl><dt><a name="Divider-render"><strong>render</strong></a>(self, (maxcol,), focus<font color="#909090">=False</font>)</dt><dd><tt>Render&nbsp;the&nbsp;divider&nbsp;as&nbsp;a&nbsp;canvas&nbsp;and&nbsp;return&nbsp;it.</tt></dd></dl>
+
+<dl><dt><a name="Divider-rows"><strong>rows</strong></a>(self, (maxcol,), focus<font color="#909090">=False</font>)</dt><dd><tt>Return&nbsp;the&nbsp;number&nbsp;of&nbsp;lines&nbsp;that&nbsp;will&nbsp;be&nbsp;rendered.</tt></dd></dl>
+
+<hr>
+Methods inherited from FlowWidget:<br>
+<dl><dt><a name="Divider-keypress"><strong>keypress</strong></a>(self, (maxcol,), key)</dt><dd><tt>Return&nbsp;key.&nbsp;&nbsp;No&nbsp;keys&nbsp;are&nbsp;handled&nbsp;by&nbsp;default.</tt></dd></dl>
+
+<dl><dt><a name="Divider-selectable"><strong>selectable</strong></a>(self)</dt><dd><tt>Return&nbsp;False.&nbsp;&nbsp;Not&nbsp;selectable&nbsp;by&nbsp;default.</tt></dd></dl>
+
<h2>Content widgets</h2><h3><a name="FlowWidget">class <strong>FlowWidget</strong></a></h3>Methods defined here:<br>
<dl><dt><a name="FlowWidget-keypress"><strong>keypress</strong></a>(self, (maxcol,), key)</dt><dd><tt>Return&nbsp;key.&nbsp;&nbsp;No&nbsp;keys&nbsp;are&nbsp;handled&nbsp;by&nbsp;default.</tt></dd></dl>
@@ -430,41 +484,7 @@ Methods inherited from FlowWidget:<br>
<dl><dt><a name="Pile-selectable"><strong>selectable</strong></a>(self)</dt><dd><tt>Return&nbsp;False.&nbsp;&nbsp;Not&nbsp;selectable&nbsp;by&nbsp;default.</tt></dd></dl>
-<h2>Decorations</h2><h3><a name="AttrWrap">class <strong>AttrWrap</strong></a></h3>Methods defined here:<br>
-<dl><dt><a name="AttrWrap-__init__"><strong>__init__</strong></a>(self, w, attr, focus_attr<font color="#909090">=None</font>)</dt><dd><tt>w&nbsp;--&nbsp;widget&nbsp;to&nbsp;wrap<br>
-attr&nbsp;--&nbsp;attribute&nbsp;to&nbsp;apply&nbsp;to&nbsp;w<br>
-focus_attr&nbsp;--&nbsp;attribute&nbsp;to&nbsp;apply&nbsp;when&nbsp;in&nbsp;focus,&nbsp;if&nbsp;None&nbsp;use&nbsp;attr<br>
-&nbsp;<br>
-Copy&nbsp;w.get_cursor_coords,&nbsp;w.move_cursor_to_coords,&nbsp;<br>
-w.get_pref_col&nbsp;functions&nbsp;to&nbsp;this&nbsp;widget&nbsp;if&nbsp;they&nbsp;exist.</tt></dd></dl>
-
-<dl><dt><a name="AttrWrap-keypress"><strong>keypress</strong></a>(self, maxvals, key)</dt><dd><tt>Pass&nbsp;keypress&nbsp;to&nbsp;self.<strong>w</strong>.</tt></dd></dl>
-
-<dl><dt><a name="AttrWrap-render"><strong>render</strong></a>(self, size, focus<font color="#909090">=False</font>)</dt><dd><tt>Render&nbsp;self.<strong>w</strong>&nbsp;and&nbsp;apply&nbsp;attribute.&nbsp;Return&nbsp;canvas.<br>
-&nbsp;<br>
-size&nbsp;--&nbsp;(maxcol,)&nbsp;if&nbsp;self.<strong>w</strong>&nbsp;contains&nbsp;a&nbsp;flow&nbsp;widget&nbsp;or<br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(maxcol,&nbsp;maxrow)&nbsp;if&nbsp;it&nbsp;contains&nbsp;a&nbsp;box&nbsp;widget.</tt></dd></dl>
-
-<dl><dt><a name="AttrWrap-rows"><strong>rows</strong></a>(self, (maxcol,), focus<font color="#909090">=False</font>)</dt><dd><tt>Return&nbsp;the&nbsp;rows&nbsp;needed&nbsp;for&nbsp;self.<strong>w</strong>.</tt></dd></dl>
-
-<dl><dt><a name="AttrWrap-selectable"><strong>selectable</strong></a>(self)</dt><dd><tt>Return&nbsp;the&nbsp;selectable&nbsp;value&nbsp;of&nbsp;self.<strong>w</strong>.</tt></dd></dl>
-
-<h3><a name="Divider">class <strong>Divider</strong></a>(FlowWidget)</h3>Methods defined here:<br>
-<dl><dt><a name="Divider-__init__"><strong>__init__</strong></a>(self, div_char<font color="#909090">=' '</font>, top<font color="#909090">=0</font>, bottom<font color="#909090">=0</font>)</dt><dd><tt>div_char&nbsp;--&nbsp;character&nbsp;to&nbsp;repeat&nbsp;across&nbsp;line<br>
-top&nbsp;--&nbsp;number&nbsp;of&nbsp;blank&nbsp;lines&nbsp;above<br>
-bottom&nbsp;--&nbsp;number&nbsp;of&nbsp;blank&nbsp;lines&nbsp;below</tt></dd></dl>
-
-<dl><dt><a name="Divider-render"><strong>render</strong></a>(self, (maxcol,), focus<font color="#909090">=False</font>)</dt><dd><tt>Render&nbsp;the&nbsp;divider&nbsp;as&nbsp;a&nbsp;canvas&nbsp;and&nbsp;return&nbsp;it.</tt></dd></dl>
-
-<dl><dt><a name="Divider-rows"><strong>rows</strong></a>(self, (maxcol,), focus<font color="#909090">=False</font>)</dt><dd><tt>Return&nbsp;the&nbsp;number&nbsp;of&nbsp;lines&nbsp;that&nbsp;will&nbsp;be&nbsp;rendered.</tt></dd></dl>
-
-<hr>
-Methods inherited from FlowWidget:<br>
-<dl><dt><a name="Divider-keypress"><strong>keypress</strong></a>(self, (maxcol,), key)</dt><dd><tt>Return&nbsp;key.&nbsp;&nbsp;No&nbsp;keys&nbsp;are&nbsp;handled&nbsp;by&nbsp;default.</tt></dd></dl>
-
-<dl><dt><a name="Divider-selectable"><strong>selectable</strong></a>(self)</dt><dd><tt>Return&nbsp;False.&nbsp;&nbsp;Not&nbsp;selectable&nbsp;by&nbsp;default.</tt></dd></dl>
-
-<h2>Canvas painting</h2><h3><strong>canvas.Canvas</strong> = <a name="canvas.Canvas">class Canvas</a></h3>Methods defined here:<br>
+<h2>Canvas painting</h2><h3><a name="Canvas">class <strong>Canvas</strong></a></h3>Methods defined here:<br>
<dl><dt><a name="Canvas-__init__"><strong>__init__</strong></a>(self, text<font color="#909090">=None</font>, attr<font color="#909090">=None</font>, cursor<font color="#909090">=None</font>)</dt><dd><tt>text&nbsp;--&nbsp;list&nbsp;of&nbsp;strings,&nbsp;one&nbsp;for&nbsp;each&nbsp;line<br>
attr&nbsp;--&nbsp;list&nbsp;of&nbsp;run&nbsp;length&nbsp;encoded&nbsp;attributes&nbsp;for&nbsp;text<br>
cursor&nbsp;--&nbsp;(x,y)&nbsp;of&nbsp;cursor&nbsp;or&nbsp;None</tt></dd></dl>
@@ -482,8 +502,8 @@ count&nbsp;--&nbsp;number&nbsp;of&nbsp;lines&nbsp;to&nbsp;keep,&nbsp;or&nbsp;Non
&nbsp;<br>
end&nbsp;--&nbsp;number&nbsp;of&nbsp;lines&nbsp;to&nbsp;remove&nbsp;from&nbsp;the&nbsp;end</tt></dd></dl>
-<dl><dt><a name="-canvas.CanvasCombine"><strong>canvas.CanvasCombine</strong></a> = CanvasCombine(l)</dt><dd><tt>Stack&nbsp;canvases&nbsp;in&nbsp;l&nbsp;vertically&nbsp;and&nbsp;return&nbsp;resulting&nbsp;canvas.</tt></dd></dl>
-<dl><dt><a name="-canvas.CanvasJoin"><strong>canvas.CanvasJoin</strong></a> = CanvasJoin(l)</dt><dd><tt>Join&nbsp;canvases&nbsp;in&nbsp;l&nbsp;horizontally.&nbsp;Return&nbsp;result.<br>
+<dl><dt><a name="-CanvasCombine"><strong>CanvasCombine</strong></a>(l)</dt><dd><tt>Stack&nbsp;canvases&nbsp;in&nbsp;l&nbsp;vertically&nbsp;and&nbsp;return&nbsp;resulting&nbsp;canvas.</tt></dd></dl>
+<dl><dt><a name="-CanvasJoin"><strong>CanvasJoin</strong></a>(l)</dt><dd><tt>Join&nbsp;canvases&nbsp;in&nbsp;l&nbsp;horizontally.&nbsp;Return&nbsp;result.<br>
&nbsp;<br>
l&nbsp;--&nbsp;[canvas1,&nbsp;colnum2,&nbsp;canvas2,&nbsp;...&nbsp;,colnumN,&nbsp;canvasN]<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;colnumX&nbsp;is&nbsp;the&nbsp;column&nbsp;number&nbsp;to&nbsp;start&nbsp;for&nbsp;canvasX</tt></dd></dl>
diff --git a/setup.py b/setup.py
index 3519ee0..d8d733a 100644
--- a/setup.py
+++ b/setup.py
@@ -23,7 +23,7 @@ from distutils.core import setup
import os
-release = "0.8.4" #os.popen("make -s release").read().strip()
+release = "0.8.5" #os.popen("make -s release").read().strip()
setup_d = {
'name':"urwid",
@@ -35,7 +35,7 @@ setup_d = {
'license':"LGPL",
'keywords':"curses ui widget scroll listbox interface text layout",
'platforms':"unix-like",
- 'description':"A curses-based UI library featuring fluid interface resizing, CJK suppport, multiple text layouts, simple attribute markup, powerful scrolling list boxes and flexible edit boxes.",
+ 'description':"A curses-based UI library featuring fluid interface resizing, CJK support, multiple text layouts, simple attribute markup, powerful scrolling list boxes and flexible edit boxes.",
'long_description':"""
Urwid is a curses-based user interface library. It includes many features
useful for text console application developers including:
diff --git a/tutorial.html b/tutorial.html
new file mode 100644
index 0000000..7191728
--- /dev/null
+++ b/tutorial.html
@@ -0,0 +1,646 @@
+<html>
+<head>
+<title>Urwid Tutorial</title>
+<style type="text/css">
+ h1 { text-align: center; }
+ h2 { margin: 40px 0 0 0; padding: 10px; background: #6d96e8;}
+ h3 { margin: 0 0 3px 0; padding: 12px 6px 6px 6px; background: #efef96;}
+ .code { background: #dddddd; padding: 5px; margin: 7px 20px; }
+ .l1 { margin: 12px 0 0 0; }
+ .l2 { margin-left: 20px; }
+ .shot { padding: 5px 20px 5px 0px; float: left; }
+</style>
+<body>
+<h1>Urwid Tutorial</h1>
+
+<div style="text-align: center;">
+<a href="http://excess.org/urwid/">Urwid Home Page</a> /
+<a href="http://excess.org/urwid/examples.html">Example Screenshots</a> /
+<a href="http://excess.org/urwid/wexamples.zh-cn.html">Wide Character Screenshots</a> /
+Tutorial /
+<a href="reference.html">Reference</a>
+</div>
+<br>
+<b>Note:</b> This tutorial requires Urwid 1.8.5 or later.<br>
+
+<table width="100%"><tr><td width="50%" valign="top">
+<div class="l1">1. Hello World</div>
+<div class="l2"><a href="#1.1">1.1. Minimal Urwid Application</a></div>
+<div class="l2"><a href="#1.2">1.2. Text and Filler Widgets</a></div>
+<div class="l2"><a href="#1.3">1.3. AttrWrap Widgets and Text Attributes</a></div>
+<div class="l2"><a href="#1.4">1.4. Live Resizing</a></div>
+</td><td width="50%" valign="top">
+<div class="l1">2. Conversation</div>
+<div class="l2"><a href="#2.1">2.1. Edit Widgets</a></div>
+<div class="l2"><a href="#2.2">2.2. Frame and ListBox Widgets</a></div>
+<div class="l2"><a href="#2.3">2.3. ListBox Widgets Continued</a></div>
+</td></tr></table>
+
+<h2>1. Hello World</h2>
+
+<h3><a name="1.1">1.1. Minimal Urwid Application</a></h3>
+
+This program displays the string "Hello World" in the top left corner
+of the screen and waits for a keypress before exiting.
+
+<pre class="code">
+import urwid.curses_display
+import urwid
+
+ui = urwid.curses_display.Screen()
+
+def run():
+ canvas = urwid.Canvas( [&quot;Hello World&quot;] )
+ ui.draw_screen( (20, 1), canvas )
+
+ while not ui.get_input():
+ pass
+
+ui.run_wrapper( run )
+</pre>
+
+<ul>
+<li>The <a href="reference.html#curses_display.Screen">curses_display.Screen</a>
+class provides access to the curses library. Its member function
+<a href="reference.html#Screen-run_wrapper">run_wrapper</a> initializes
+curses full-screen mode and then calls the "run" function passed. It will
+also take care of restoring the screen when the "run" function exits.
+<li>A <a href="reference.html#Canvas">Canvas</a> is created
+containing one row with the string "Hello World".
+<li>The canvas is passed to the
+<a href="reference.html#Screen-draw_screen">draw_screen</a> function along
+with a fixed screen size of 20 columns and 1 row. It is likely that the
+terminal window this program is run from is larger than 20 by 1, so the
+text will appear in the top left corner.
+<li>The <a href="reference.html#Screen-get_input">get_input</a> function
+is then called until it returns something. It must be called in a loop
+because it will time out after one second and return an empty list.
+</ul>
+
+Creating canvases directly is generally only done when
+writing custom widget classes. Note that the draw_screen
+function must be passed a canvas and a screen size that matches it.
+
+<div align="center"><pre><span style="color:silver;background:black">Hello World </span>
+</pre></div>
+<br>
+<br>
+
+<h3><a name="1.2">1.2. Text and Filler Widgets</a></h3>
+
+This program displays the string "Hello World" in the center of the screen
+and waits for a keypress before exiting.
+
+<pre class="code">
+import urwid.curses_display
+import urwid
+
+ui = urwid.curses_display.Screen()
+
+def run():
+ cols, rows = ui.get_cols_rows()
+
+ txt = urwid.Text(&quot;Hello World&quot;, align=&quot;center&quot;)
+ fill = urwid.Filler( txt )
+
+ canvas = fill.render( (cols, rows) )
+ ui.draw_screen( (cols, rows), canvas )
+
+ while not ui.get_input():
+ pass
+
+ui.run_wrapper( run )
+</pre>
+
+<ul>
+<li><a href="reference.html#Screen-get_cols_rows">get_cols_rows</a>
+is used to get the dimensions from the terminal and store them as "cols"
+and "rows".
+<li>A <a href="reference.html#Text">Text</a> widget is created containing
+the string "Hello World". It is set to display with "center" alignment.
+Text widgets are a kind of <a href="reference.html#FlowWidget">FlowWidget</a>.
+Flow widgets can fill one or more rows, depending on their content and
+the number of columns available. Text widgets use more than one row when
+they contain newline characters or when the text must be split across rows.
+<li>A <a href="reference.html#Filler">Filler</a> widget is created to
+wrap the text widget. Filler widgets are a kind of
+<a href="reference.html#BoxWidget">BoxWidget</a>. Box widgets have a fixed
+number of columns and rows displayed. This widget will pad the "Hello World"
+text widget until it fills the required number of rows.
+<li>A canvas is created by calling the
+<a href="reference.html#Filler-render">render</a> function on the topmost
+widget. The filler render function will call the render function
+of the "Hello World" text widget and combine its canvas with
+padding rows to fill the terminal window.
+</ul>
+
+Flow widgets and box widgets are not interchangeable. The first parameter
+of the render function of a box widget is a two-element tuple (columns,
+rows) and the first parameter of the render function of a flow widget is
+a one-element tuple (columns, ).
+This difference makes sure that when the wrong type of widget is used,
+such as a box widget inside a filler widget, a ValueError exception will
+be thrown.
+
+<div align="center"><pre><span style="color:silver;background:black"> </span>
+<span style="color:silver;background:black"> </span>
+<span style="color:silver;background:black"> </span>
+<span style="color:silver;background:black"> </span><span style="color:silver;background:black">Hello World </span>
+<span style="color:silver;background:black"> </span>
+<span style="color:silver;background:black"> </span>
+<span style="color:silver;background:black"> </span>
+</pre></div>
+<br>
+<br>
+
+<h3><a name="1.3">1.3. AttrWrap Widgets and Text Attributes</a></h3>
+
+This program displays the string "Hello World" in the center of the screen.
+It uses different attributes used for the text, the space on either side
+of the text and the space above and below the text. It then and waits for
+a keypress before exiting.
+
+<pre class="code">
+import urwid.curses_display
+import urwid
+
+ui = urwid.curses_display.Screen()
+
+ui.register_palette( [
+ ('banner', 'black', 'light gray', ('standout', 'underline')),
+ ('streak', 'black', 'dark red', 'standout'),
+ ('bg', 'black', 'dark blue'),
+ ] )
+
+def run():
+ cols, rows = ui.get_cols_rows()
+
+ txt = urwid.Text(('banner', &quot; Hello World &quot;), align=&quot;center&quot;)
+ wrap1 = urwid.AttrWrap( txt, 'streak' )
+ fill = urwid.Filler( wrap1 )
+ wrap2 = urwid.AttrWrap( fill, 'bg' )
+
+ canvas = wrap2.render( (cols, rows) )
+ ui.draw_screen( (cols, rows), canvas )
+
+ while not ui.get_input():
+ pass
+
+ui.run_wrapper( run )
+</pre>
+
+<ul>
+<li>After creating the
+<a href="reference.html#curses_display.Screen">curses_display.Screen</a> object
+and before calling <a href="reference.html#Screen-run_wrapper">run_wrapper</a>,
+<a href="reference.html#Screen-register_palette">register_palette</a> is called
+to set up some attributes:
+ <ul>
+ <li>"banner" is black text on a light gray background, or reversed attributes
+ and underlined in monochrome mode
+ <li>"streak" is black text on a dark red background, or reversed attributes
+ in monochrome mode
+ <li>"bg" is black text on a dark blue background, or normal in
+ monochrome mode
+ </ul>
+<li>A <a href="reference.html#Text">Text</a> widget is created containing
+the string " Hello World " with attribute "banner". The attributes of text
+in a Text widget is set by using a (attribute, text) tuple instead of a
+simple text string.
+<li>An <a href="reference.html#AttrWrap">AttrWrap</a> widget is created to
+wrap the text widget with attribute "streak". AttrWrap widgets will set
+the attribute of everything that they wrap that does not already have an
+attribute set. In this case the text has an attribute, so only the areas
+around the text used for alignment will be have the new attribute.
+<li>A <a href="reference.html#Filler">Filler</a> widget is created to
+wrap the AttrWrap widget and fill the rows above and below it.
+<li>A second <a href="reference.html#AttrWrap">AttrWrap</a> widget is created to
+wrap the filler widget with attribute "bg".
+<li>A canvas is created by calling the
+<a href="reference.html#AttrWrap-render">render</a> function on the topmost
+widget.
+</ul>
+
+AttrWrap widgets will behave like flow widgets or box widgets depending on
+how they are called. The filler widget treats the first AttrWrap widget as
+a flow widget when calling its render function, so the AttrWrap widget calls
+the text widget's render function the same way. The second AttrWrap is
+used as the topmost widget and treated as a box widget, so it calls the
+filler render function in the same way.
+
+<div align="center"><pre><span style="color:black;background:#0000c0"> </span>
+<span style="color:black;background:#0000c0"> </span>
+<span style="color:black;background:#0000c0"> </span>
+<span style="color:black;background:#c00000"> </span><span style="color:black;background:silver"> Hello World </span><span style="color:black;background:#c00000"> </span>
+<span style="color:black;background:#0000c0"> </span>
+<span style="color:black;background:#0000c0"> </span>
+<span style="color:black;background:#0000c0"> </span>
+</pre></div>
+<br>
+<br>
+
+<h3><a name="1.4">1.4. Live Resizing</a></h3>
+
+This program displays the string "Hello World" in the center of the screen.
+It uses different attributes used for the text, the space on either side
+of the text and the space above and below the text. When the window is
+resized it will repaint the screen, and it will exit when "Q" is pressed.
+
+<pre class="code">
+import urwid.curses_display
+import urwid
+
+ui = urwid.curses_display.Screen()
+
+ui.register_palette( [
+ ('banner', 'black', 'light gray', ('standout', 'underline')),
+ ('streak', 'black', 'dark red', 'standout'),
+ ('bg', 'black', 'dark blue'),
+ ] )
+
+def run():
+ cols, rows = ui.get_cols_rows()
+
+ txt = urwid.Text(('banner', &quot; Hello World &quot;), align=&quot;center&quot;)
+ wrap1 = urwid.AttrWrap( txt, 'streak' )
+ fill = urwid.Filler( wrap1 )
+ wrap2 = urwid.AttrWrap( fill, 'bg' )
+
+ while True:
+ canvas = wrap2.render( (cols, rows) )
+ ui.draw_screen( (cols, rows), canvas )
+
+ keys = ui.get_input()
+ if &quot;q&quot; in keys or &quot;Q&quot; in keys:
+ break
+ if &quot;window resize&quot; in keys:
+ cols, rows = ui.get_cols_rows()
+
+ui.run_wrapper( run )
+</pre>
+
+The <a href="reference.html#Screen-get_input">get_input</a> function will
+return "window resize" among keys pressed when the window is resized. It
+is a good idea to check for uppercase and lowercase letters on input
+because it is easy to users to forget that Caps Lock is on.
+
+<div class="shot"><pre><span style="color:black;background:#0000c0"> </span>
+<span style="color:black;background:#0000c0"> </span>
+<span style="color:black;background:#0000c0"> </span>
+<span style="color:black;background:#c00000"> </span><span style="color:black;background:silver"> Hello World </span><span style="color:black;background:#c00000"> </span>
+<span style="color:black;background:#0000c0"> </span>
+<span style="color:black;background:#0000c0"> </span>
+<span style="color:black;background:#0000c0"> </span>
+</pre></div>
+<div class="shot"><pre><span style="color:black;background:#0000c0"> </span>
+<span style="color:black;background:#0000c0"> </span>
+<span style="color:black;background:#0000c0"> </span>
+<span style="color:black;background:#c00000"> </span><span style="color:black;background:silver"> Hello</span><span style="color:black;background:#c00000"> </span>
+<span style="color:black;background:#c00000"> </span><span style="color:black;background:silver">World </span><span style="color:black;background:#c00000"> </span>
+<span style="color:black;background:#0000c0"> </span>
+<span style="color:black;background:#0000c0"> </span>
+<span style="color:black;background:#0000c0"> </span>
+<span style="color:black;background:#0000c0"> </span>
+</pre></div>
+<div class="shot"><pre><span style="color:black;background:#0000c0"> </span>
+<span style="color:black;background:#c00000"> </span><span style="color:black;background:silver"> Hello World </span><span style="color:black;background:#c00000"> </span>
+<span style="color:black;background:#0000c0"> </span>
+</pre></div>
+<div class="shot"><pre><span style="color:black;background:#c00000"> </span><span style="color:black;background:silver"> Hello World </span><span style="color:black;background:#c00000"> </span>
+<span style="color:black;background:#0000c0"> </span>
+</pre></div>
+<br clear="left">
+<br>
+
+<h2>2. Conversation</h2>
+
+<h3><a name="2.1">2.1. Edit Widgets</a></h3>
+
+This program asks for your name then responds "Nice to meet you, (your name)."
+
+<pre class="code">
+import urwid.curses_display
+import urwid
+
+ui = urwid.curses_display.Screen()
+
+def run():
+ cols, rows = ui.get_cols_rows()
+
+ ask = urwid.Edit(&quot;What is your name?\n&quot;)
+ fill = urwid.Filler( ask )
+ reply = None
+
+ while True:
+ canvas = fill.render( (cols, rows), focus=True )
+ ui.draw_screen( (cols, rows), canvas )
+
+ keys = ui.get_input()
+ for k in keys:
+ if k == &quot;window resize&quot;:
+ cols, rows = ui.get_cols_rows()
+ continue
+ if reply is not None:
+ return
+ if k == &quot;enter&quot;:
+ reply = urwid.Text( &quot;Nice to meet you,\n&quot;+
+ ask.edit_text+&quot;.&quot; )
+ fill.body = reply
+ fill.keypress( (cols, rows), k )
+
+ui.run_wrapper( run )
+</pre>
+
+<ul>
+<li>An <a href="reference.html#Edit">Edit</a> widget is created with the
+caption "What is your name?". A newline at the end of the caption makes
+the user input start on the next row.
+<li>A <a href="reference.html#Filler">Filler</a> widget is created to wrap
+the edit widget. Its <a href="reference.html#Filler-render">render</a>
+function is called to create the canvas. The render function is called with
+the optional parameter "focus" set to True. This parameter allows the
+wrapped Edit widget to render its cursor.
+<li>Keys are processed one at a time. Most keys are sent to the Filler widget's
+<a href="reference.html#Filler-keypress">keypress</a> function which will
+call the Edit widget's <a href="reference.html#Edit-keypress">keypress</a>
+function to handle the key.
+<li>Once the ENTER key is pressed the wrapped object in the Filler widget
+is changed to a reply text.
+<li>Any keypress then causes the program to exit.
+</ul>
+
+The Edit widget has many capabilities. It lets you make corrections and move
+the cursor around with the HOME, END and arrow keys. It is based on the Text
+widget so it supports the same wrapping and alignment modes.
+
+<div class="shot"><pre><span style="color:silver;background:black"> </span>
+<span style="color:silver;background:black"> </span>
+<span style="color:silver;background:black">What is your name? </span>
+<span style="color:silver;background:black"><u> </u> </span>
+<span style="color:silver;background:black"> </span>
+<span style="color:silver;background:black"> </span>
+<span style="color:silver;background:black"> </span>
+</pre></div>
+<div class="shot"><pre><span style="color:silver;background:black"> </span>
+<span style="color:silver;background:black"> </span>
+<span style="color:silver;background:black">What is your name? </span>
+<span style="color:silver;background:black">Arthur, King of the </span>
+<span style="color:silver;background:black">Britons<u> </u> </span>
+<span style="color:silver;background:black"> </span>
+<span style="color:silver;background:black"> </span>
+</pre></div>
+<div class="shot"><pre><span style="color:silver;background:black"> </span>
+<span style="color:silver;background:black"> </span>
+<span style="color:silver;background:black">Nice to meet you, </span>
+<span style="color:silver;background:black">Arthur, King of the </span>
+<span style="color:silver;background:black">Britons. </span>
+<span style="color:silver;background:black"> </span>
+<span style="color:silver;background:black"> </span>
+</pre></div>
+<br clear="left">
+<br>
+
+<h3><a name="2.2">2.2. Frame and ListBox Widgets</a></h3>
+
+This program asks for your name and responds "Nice to meet you, (your name)"
+while you type your name. F1 exits.
+
+<pre class="code">
+import urwid.curses_display
+import urwid
+
+class Conversation:
+ def __init__(self):
+ self.items = [ self.new_question() ]
+ self.listbox = urwid.ListBox( self.items )
+ instruct = urwid.Text(&quot;Press F1 to exit.&quot;)
+ header = urwid.AttrWrap( instruct, 'header' )
+ self.top = urwid.Frame(self.listbox, header)
+
+ def main(self):
+ self.ui = urwid.curses_display.Screen()
+ self.ui.register_palette([
+ ('header', 'black', 'dark cyan', 'standout'),
+ ('I say', 'white', 'black', 'bold'),
+ ])
+ self.ui.run_wrapper( self.run )
+
+ def run(self):
+ size = self.ui.get_cols_rows()
+
+ while True:
+ self.draw_screen( size )
+ keys = self.ui.get_input()
+ if &quot;f1&quot; in keys:
+ break
+ for k in keys:
+ if k == &quot;window resize&quot;:
+ size = self.ui.get_cols_rows()
+ continue
+ self.top.keypress( size, k )
+ if keys:
+ name = self.items[0].edit_text
+ self.items[1:2] = [self.new_answer(name)]
+
+ def draw_screen(self, size):
+ canvas = self.top.render( size, focus=True )
+ self.ui.draw_screen( size, canvas )
+
+ def new_question(self):
+ return urwid.Edit(('I say',&quot;What is your name?\n&quot;))
+
+ def new_answer(self, name):
+ return urwid.Text(('I say',&quot;Nice to meet you, &quot;+name+&quot;\n&quot;))
+
+
+Conversation().main()
+</pre>
+
+<ul>
+<li>In the __init__ function a list called self.items is created. It
+contains an Edit widget with the caption "What is your name?".
+<li>A <a href="reference.html#ListBox">ListBox</a> widget called
+self.listbox is created that is passed the self.items list.
+This ListBox widget will display and scroll through the widgets in that list.
+ListBox widgets default to using the first item in the list as the focus.
+<li>A <a href="reference.html#Frame">Frame</a> widget called self.top
+is created that contains self.listbox and some header text. Frame widgets
+wrap around a box widget and may have header and footer flow widgets.
+The header and footer are always displayed. The contained box widget uses the
+remaining space in between.
+<li>When a key is pressed the reply text is inserted or updated in
+self.items. This updated text will be displayed by self.listbox.
+</ul>
+
+When changing the contents of ListBox widgets remember to use in-place
+editing operations on the list, eg. "list = list + [something]" will not work,
+use "list += [something]" instead.
+
+<div class="shot"><pre><span style="color:black;background:teal">Press F1 to exit. </span>
+<span style="color:white;background:black">What is your name?</span><span style="color:silver;background:black"> </span>
+<span style="color:silver;background:black"><u> </u> </span>
+<span style="color:silver;background:black"> </span>
+<span style="color:silver;background:black"> </span>
+<span style="color:silver;background:black"> </span>
+<span style="color:silver;background:black"> </span>
+</pre></div>
+<div class="shot"><pre><span style="color:black;background:teal">Press F1 to exit. </span>
+<span style="color:white;background:black">What is your name?</span><span style="color:silver;background:black"> </span>
+<span style="color:silver;background:black">Tim t<u> </u> </span>
+<span style="color:white;background:black">Nice to meet you, Tim</span>
+<span style="color:white;background:black">t</span><span style="color:silver;background:black"> </span>
+<span style="color:silver;background:black"> </span>
+<span style="color:silver;background:black"> </span>
+</pre></div>
+<div class="shot"><pre><span style="color:black;background:teal">Press F1 to exit. </span>
+<span style="color:white;background:black">What is your name?</span><span style="color:silver;background:black"> </span>
+<span style="color:silver;background:black">Tim the Ench<u> </u> </span>
+<span style="color:white;background:black">Nice to meet you, Tim</span>
+<span style="color:white;background:black">the Ench</span><span style="color:silver;background:black"> </span>
+<span style="color:silver;background:black"> </span>
+<span style="color:silver;background:black"> </span>
+</pre></div>
+<div class="shot"><pre><span style="color:black;background:teal">Press F1 to exit. </span>
+<span style="color:white;background:black">What is your name?</span><span style="color:silver;background:black"> </span>
+<span style="color:silver;background:black">Tim the Enchanter<u> </u> </span>
+<span style="color:white;background:black">Nice to meet you, Tim</span>
+<span style="color:white;background:black">the Enchanter</span><span style="color:silver;background:black"> </span>
+<span style="color:silver;background:black"> </span>
+<span style="color:silver;background:black"> </span>
+</pre></div>
+<br clear="left">
+<br>
+
+<h3><a name="2.3">2.3. ListBox Widgets Continued</a></h3>
+
+This program asks for your name and responds "Nice to meet you, (your name)."
+It then asks again, and again. Old values may be changed and the responses
+will be updated when you press ENTER. F1 exits.
+<br><br>
+Update the <a href="#2.2">2.2 program</a> with this code:
+<pre class="code">
+ def run(self):
+ size = self.ui.get_cols_rows()
+
+ while True:
+ self.draw_screen( size )
+ keys = self.ui.get_input()
+ if &quot;f1&quot; in keys:
+ break
+ for k in keys:
+ if k == &quot;window resize&quot;:
+ size = self.ui.get_cols_rows()
+ continue
+ self.keypress( size, k )
+
+ def keypress(self, size, k):
+ if k == &quot;enter&quot;:
+ widget, pos = self.listbox.get_focus()
+ if not hasattr(widget,'edit_text'):
+ return
+
+ answer = self.new_answer( widget.edit_text )
+
+ if pos == len(self.items)-1:
+ self.items.append( answer )
+ self.items.append( self.new_question() )
+ else:
+ self.items[pos+1:pos+2] = [answer]
+
+ self.listbox.set_focus( pos+2, coming_from='above' )
+ widget, pos = self.listbox.get_focus()
+ widget.set_edit_pos(0)
+ else:
+ self.top.keypress( size, k )
+</pre>
+
+<ul>
+<li>In this version only the ENTER key receives special attention. When the
+user presses ENTER:
+ <ul>
+ <li>The widget in focus and its current position is retrieved by calling the
+ <a href="reference.html#ListBox-get_focus">get_focus</a> function.
+ <li>If the widget in focus does not have an edit_text attribute, then it
+ is not one of the Edit widgets we are interested in.
+ One of the Text widgets might receive focus
+ if it covers the entire visible area of the ListBox widget and there is
+ no Edit widget to take focus. While this is unlikely, it would cause the
+ program to crash.
+ <li>If the current position is at the end of the list, a response is
+ appended followed by a new question. If the current position is not at the
+ end then a previous response is replaced with an updated one.
+ <li>The focus is moved down two positions to the next question by calling
+ <a href="reference.html#ListBox-set_focus">set_focus</a>.
+ <li>Finally, the cursor position within the new focus widget it moved to
+ the far left by calling
+ <a href="reference.html#Edit-set_edit_pos">set_edit_pos</a>.
+ </ul>
+<li>All other keys are passed to the top widget to handle. The ListBox widget
+does most of the hard work:
+ <ul>
+ <li>UP and DOWN will change the focus and/or scroll the widgets in the list
+ box.
+ <li>PAGE UP and PAGE DOWN will try to move the focus one screen up or down.
+ <li>The cursor's column is maintained as best as possible when moving
+ from one Edit widget to another.
+ </ul>
+</ul>
+
+The ListBox widget tries to do the most sensible thing when scrolling and
+changing focus. When the widgets displayed are all unselectable
+the ListBox widget will always scroll. When some widgets
+are selectable it will try changing focus before scrolling, possibly
+scrolling a few lines to bring in a full selectable widget. When all the
+widgets are selectable it will only scroll when the cursor reaches the
+top or bottom edge.
+
+<div class="shot"><pre><span style="color:black;background:teal">Press F1 to exit. </span>
+<span style="color:white;background:black">What is your name?</span><span style="color:silver;background:black"> </span>
+<span style="color:silver;background:black">Abe </span>
+<span style="color:white;background:black">Nice to meet you, Abe</span><span style="color:silver;background:black"> </span>
+<span style="color:silver;background:black"> </span>
+<span style="color:white;background:black">What is your name?</span><span style="color:silver;background:black"> </span>
+<span style="color:silver;background:black">Bob<u> </u> </span>
+<span style="color:silver;background:black"> </span>
+<span style="color:silver;background:black"> </span>
+<span style="color:silver;background:black"> </span>
+<span style="color:silver;background:black"> </span>
+<span style="color:silver;background:black"> </span>
+<span style="color:silver;background:black"> </span>
+</pre></div>
+<div class="shot"><pre><span style="color:black;background:teal">Press F1 to exit. </span>
+<span style="color:white;background:black">Nice to meet you, Abe</span><span style="color:silver;background:black"> </span>
+<span style="color:silver;background:black"> </span>
+<span style="color:white;background:black">What is your name?</span><span style="color:silver;background:black"> </span>
+<span style="color:silver;background:black">Bob </span>
+<span style="color:white;background:black">Nice to meet you, Bob</span><span style="color:silver;background:black"> </span>
+<span style="color:silver;background:black"> </span>
+<span style="color:white;background:black">What is your name?</span><span style="color:silver;background:black"> </span>
+<span style="color:silver;background:black">Carl </span>
+<span style="color:white;background:black">Nice to meet you, Carl</span><span style="color:silver;background:black"> </span>
+<span style="color:silver;background:black"> </span>
+<span style="color:white;background:black">What is your name?</span><span style="color:silver;background:black"> </span>
+<span style="color:silver;background:black"><u> </u> </span>
+</pre></div>
+<div class="shot"><pre><span style="color:black;background:teal">Press F1 to exit. </span>
+<span style="color:white;background:black">Nice to meet you, Bob</span><span style="color:silver;background:black"> </span>
+<span style="color:silver;background:black"> </span>
+<span style="color:white;background:black">What is your name?</span><span style="color:silver;background:black"> </span>
+<span style="color:silver;background:black">Carl </span>
+<span style="color:white;background:black">Nice to meet you, Carl</span><span style="color:silver;background:black"> </span>
+<span style="color:silver;background:black"> </span>
+<span style="color:white;background:black">What is your name?</span><span style="color:silver;background:black"> </span>
+<span style="color:silver;background:black">Dave </span>
+<span style="color:white;background:black">Nice to meet you, Dave</span><span style="color:silver;background:black"> </span>
+<span style="color:silver;background:black"> </span>
+<span style="color:white;background:black">What is your name?</span><span style="color:silver;background:black"> </span>
+<span style="color:silver;background:black"><u> </u> </span>
+</pre></div>
+<br clear="left">
+<br>
+
+
+
+
+</body>
+</html>
diff --git a/urwid/curses_display.py b/urwid/curses_display.py
index 5426060..7c04eec 100755
--- a/urwid/curses_display.py
+++ b/urwid/curses_display.py
@@ -95,7 +95,8 @@ class Screen:
def register_palette( self, l ):
"""Register a list of palette entries.
- l -- list of (name, foreground, background) or
+ l -- list of (name, foreground, background, mono),
+ (name, foreground, background) or
(name, same_as_other_name) palette entries.
calls self.register_palette_entry for each item in l
@@ -443,6 +444,8 @@ class Screen:
for y in range(len(lines)):
line = lines[y].translate( _trans_table )
+ if len(line) < cols:
+ line += " "*(cols-len(line))
if y == rows-1:
# don't draw in the lower right corner
line = line[:cols-1]
diff --git a/urwid/html_fragment.py b/urwid/html_fragment.py
index e72ed69..e934139 100755
--- a/urwid/html_fragment.py
+++ b/urwid/html_fragment.py
@@ -140,11 +140,12 @@ class HtmlGenerator:
if cols > col:
fg,bg,mono = "light gray", "black", None
+ end = line[col:]+" "*(cols-len(line))
if y == cy:
- l.append( html_span( " "*(cols-col),
+ l.append( html_span( end,
fg, bg, cx-col ))
else:
- l.append( html_span( " "*(cols-col),
+ l.append( html_span( end,
fg, bg ))
l.append("\n")
diff --git a/urwid/listbox.py b/urwid/listbox.py
index 47c5d9a..493db01 100644
--- a/urwid/listbox.py
+++ b/urwid/listbox.py
@@ -98,6 +98,10 @@ class ListBox(BoxWidget):
# pref_col is the preferred column for the cursor when moving
# between widgets that use the cursor (edit boxes etc.)
self.pref_col = 0
+
+ # variable for delayed focus change used by set_focus
+ self.set_focus_pending = None
+
def calculate_visible(self, (maxcol, maxrow), focus=False ):
""" Return (middle,top,bottom) or None,None,None.
@@ -112,8 +116,12 @@ class ListBox(BoxWidget):
list of (widget, position, rows) tuples below focus
in order from top to bottom )
"""
-
- # 1. start by rendering focus widget
+
+ # 0. set the focus if a change is pending
+ if self.set_focus_pending:
+ self._set_focus_complete( (maxcol, maxrow), focus )
+
+ # 1. start with the focus widget
focus_widget, focus_pos = self.body.get_focus()
if focus_widget is None: #list box is empty?
return None,None,None
@@ -282,7 +290,77 @@ class ListBox(BoxWidget):
+ def set_focus(self, position, coming_from=None):
+ """
+ Set the focus position and try to keep the old focus in view.
+
+ position -- a position compatible with self.body.set_focus
+ coming_from -- set to 'above' or 'below' if you know that
+ old position is above or below the new position.
+ """
+ assert coming_from in ('above', 'below', None)
+ focus_widget, focus_pos = self.body.get_focus()
+
+ self.set_focus_pending = coming_from, focus_widget, focus_pos
+ self.body.set_focus( position )
+
+ def get_focus(self):
+ """
+ Return a (focus widget, focus position) tuple.
+ """
+ return self.body.get_focus()
+
+ def _set_focus_complete(self, (maxcol, maxrow), focus):
+ """
+ Finish setting the position now that we have maxcol & maxrow.
+ """
+ coming_from, focus_widget, focus_pos = self.set_focus_pending
+ self.set_focus_pending = None
+
+ # new position
+ 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)
+ middle,top,bottom=self.calculate_visible((maxcol,maxrow),focus)
+ focus_offset, focus_widget, focus_pos, focus_rows, cursor=middle
+ trim_top, fill_above = top
+ trim_bottom, fill_below = bottom
+
+ offset = focus_offset
+ for widget, pos, rows in fill_above:
+ offset -= rows
+ if pos == position:
+ self.change_focus((maxcol, maxrow), pos,
+ offset, 'below' )
+ return
+
+ offset = focus_offset + focus_rows
+ for widget, pos, rows in fill_below:
+ if pos == position:
+ self.change_focus((maxcol, maxrow), pos,
+ offset, 'above' )
+ return
+ offset += rows
+
+ # failed to find widget among visible widgets
+ self.body.set_focus( position )
+ widget, position = self.body.get_focus()
+ rows = widget.rows((maxcol,), focus)
+
+ if coming_from=='below':
+ offset = 0
+ elif coming_from=='above':
+ offset = maxrow-rows
+ else:
+ offset = maxrow-rows/2
+ self.shift_focus((maxcol, maxrow), offset)
+
+
def shift_focus(self, (maxcol,maxrow), offset_inset ):
"""Move the location of the current focus relative to the top.
@@ -434,6 +512,9 @@ class ListBox(BoxWidget):
'page down' move cursor down one listbox length
"""
+ if self.set_focus_pending:
+ self._set_focus_complete( (maxcol,maxrow), focus=True )
+
focus_widget, pos = self.body.get_focus()
if focus_widget is None: # empty listbox, can't do anything
return key
diff --git a/urwid/widget.py b/urwid/widget.py
index d57b7de..9bbc366 100755
--- a/urwid/widget.py
+++ b/urwid/widget.py
@@ -534,6 +534,77 @@ class IntEdit(Edit):
return 0
+class Filler(BoxWidget):
+ def __init__(self, body, valign="middle"):
+ """
+ body -- a flow widget to be filled around
+ valign -- vertical alignment: "top", "middle" or "bottom"
+ """
+ self.body = body
+ assert valign in ("top","middle","bottom")
+ self.valign = valign
+
+ def render(self, (maxcol,maxrow), focus=False):
+ """Render self.body with space around it to fill box size."""
+ c = self.body.render( (maxcol,), focus )
+
+ cy = None
+ if c.cursor is not None:
+ cx, cy = c.cursor
+
+ pos = self.body_position((maxcol,maxrow), focus, c.rows(), cy )
+
+ # need to trim top of self.body
+ if pos < 0:
+ c.trim( -pos, maxrow )
+ return c
+ top = Canvas(["" for i in range(pos)])
+
+ # may need to trim bottom of self.body
+ if pos + c.rows() >= maxrow:
+ return CanvasCombine( [top, c] ).trim(0, maxrow)
+
+ # add padding to bottom
+ bottom = Canvas(["" for i in range(maxrow-c.rows()-pos)])
+ return CanvasCombine( [top, c, bottom] )
+
+
+ def keypress(self, (maxcol,maxrow), key):
+ """Pass keypress to self.body."""
+ return self.body.keypress( (maxcol,), key )
+
+
+ def body_position(self, (maxcol, maxrow), focus, rows=None, cy=None):
+ """
+ Return the row offset (+ve) or reduction (-ve) of self.body.
+ """
+
+ if cy is None and focus:
+ if hasattr(self.body,'get_cursor_coords'):
+ cx, cy = self.body.get_cursor_coords((maxcol,))
+
+ # need to trim rows on top
+ if cy is not None and cy >= maxrow:
+ return maxrow-cy-1
+
+ if self.valign == "top":
+ return 0
+
+ if rows is None:
+ rows = self.body.rows((maxcol,), focus)
+ if rows >= maxrow:
+ return 0
+
+ remaining = maxrow - rows
+
+ if self.valign == "middle":
+ return remaining / 2
+ else: # valign == "bottom"
+ return remaining
+
+
+
+
class Frame(BoxWidget):