diff options
author | ian <none@none> | 2006-04-14 21:40:16 +0000 |
---|---|---|
committer | ian <none@none> | 2006-04-14 21:40:16 +0000 |
commit | 350d3ec226ce4473f39f0b11bf6d85953d5493ee (patch) | |
tree | 4f14634695e4e7fbef4d78a3d48d119bb8d2e6c9 | |
parent | b088d64873152e39389694922f7e281a2d739e00 (diff) | |
download | urwid-350d3ec226ce4473f39f0b11bf6d85953d5493ee.tar.gz |
release 0.9.3release-0.9.3
--HG--
extra : convert_revision : 4422098fbd87b6ab1dd4f4a3f9dfec2ac5f87f10
-rwxr-xr-x | input_test.py | 12 | ||||
-rw-r--r-- | reference.html | 4 | ||||
-rw-r--r-- | setup.py | 2 | ||||
-rwxr-xr-x | test_urwid.py | 42 | ||||
-rwxr-xr-x | urwid/curses_display.py | 58 | ||||
-rw-r--r-- | urwid/escape.py | 25 | ||||
-rw-r--r-- | urwid/raw_display.py | 111 | ||||
-rw-r--r-- | urwid/util.py | 26 | ||||
-rwxr-xr-x | urwid/web_display.py | 15 | ||||
-rwxr-xr-x | urwid/widget.py | 15 |
10 files changed, 252 insertions, 58 deletions
diff --git a/input_test.py b/input_test.py index 1a77da6..dcd11d3 100755 --- a/input_test.py +++ b/input_test.py @@ -69,7 +69,7 @@ class KeyTest: if keys: self.ui.draw_screen((cols,rows), self.top.render((cols,rows),focus=True)) - keys = self.ui.get_input() + keys, raw = self.ui.get_input(raw_keys=True) if 'window resize' in keys: cols, rows = self.ui.get_cols_rows() if not keys: @@ -86,13 +86,19 @@ class KeyTest: else: t += ["'",('key',k),"' "] - self.l.append(urwid.Text(t)) + rawt = urwid.Text(", ".join(["%d"%r for r in raw])) + + self.l.append( + urwid.Columns([ + ('weight',2,urwid.Text(t)), + rawt]) + ) self.listbox.set_focus(len(self.l)-1,'above') def main(): - urwid.web_display.set_preferences('KeyTest') + urwid.web_display.set_preferences('Input Test') if urwid.web_display.handle_short_request(): return KeyTest().main() diff --git a/reference.html b/reference.html index a624658..224d09e 100644 --- a/reference.html +++ b/reference.html @@ -1279,7 +1279,7 @@ function for the preferences to take effect</tt></ <dl><dt><a name="Screen-get_cols_rows"><strong>get_cols_rows</strong></a>(self)</dt><dd><tt>Return the screen size.</tt></dd></dl> -<dl><dt><a name="Screen-get_input"><strong>get_input</strong></a>(self)</dt><dd><tt>Return pending input as a list.</tt></dd></dl> +<dl><dt><a name="Screen-get_input"><strong>get_input</strong></a>(self, raw_keys<font color="#909090">=False</font>)</dt><dd><tt>Return pending input as a list.</tt></dd></dl> <dl><dt><a name="Screen-register_palette"><strong>register_palette</strong></a>(self, l)</dt><dd><tt>Register a list of palette entries.<br> <br> @@ -1305,6 +1305,8 @@ unique id and handles cleanup when fn exits.< web_display.set_preferences(..) must be called before calling<br> this function for the preferences to take effect</tt></dd></dl> +<dl><dt><a name="Screen-set_mouse_tracking"><strong>set_mouse_tracking</strong></a>(self)</dt><dd><tt>Not yet implemented</tt></dd></dl> + </body> </html> @@ -23,7 +23,7 @@ from distutils.core import setup import os -release = "0.9.2" +release = "0.9.3" setup_d = { 'name':"urwid", diff --git a/test_urwid.py b/test_urwid.py index b915e3f..566c41f 100755 --- a/test_urwid.py +++ b/test_urwid.py @@ -20,6 +20,7 @@ # # Urwid web site: http://excess.org/urwid/ +from __future__ import nested_scopes import urwid @@ -324,23 +325,52 @@ class CalcTranslateWordTest3(CalcTranslateTest): [(3, None), (4, 0, 6), (0, 6)], [(2, None), (6, 7, 16), (0, 16)]] - +class CalcTranslateWordTest4(CalcTranslateTest): + text = ' Die Gedank' + width = 3 + mode = 'space' + result_left = [ + [(0, 0)], + [(3, 1, 4), (0, 4)], + [(3, 5, 8)], + [(3, 8, 11), (0, 11)]] + result_right = [ + [(3, None), (0, 0)], + [(3, 1, 4), (0, 4)], + [(3, 5, 8)], + [(3, 8, 11), (0, 11)]] + result_center = [ + [(2, None), (0, 0)], + [(3, 1, 4), (0, 4)], + [(3, 5, 8)], + [(3, 8, 11), (0, 11)]] + +class CalcTranslateWordTest5(CalcTranslateTest): + text = ' Word.' + width = 3 + mode = 'space' + result_left = [[(3, 0, 3)], [(3, 3, 6), (0, 6)]] + result_right = [[(3, 0, 3)], [(3, 3, 6), (0, 6)]] + result_center = [[(3, 0, 3)], [(3, 3, 6), (0, 6)]] class CalcTranslateClipTest(CalcTranslateTest): - text = "It's out of control!\nYou've got to\nturn it off!!!" + text = "It's out of control!\nYou've got to\n\nturn it off!!!" mode = 'clip' width = 14 result_left = [ [(20, 0, 20), (0, 20)], [(13, 21, 34), (0, 34)], - [(14, 35, 49), (0, 49)]] + [(0, 35)], + [(14, 36, 50), (0, 50)]] result_right = [ [(-6, None), (20, 0, 20), (0, 20)], [(1, None), (13, 21, 34), (0, 34)], - [(14, 35, 49), (0, 49)]] + [(14, None), (0, 35)], + [(14, 36, 50), (0, 50)]] result_center = [ [(-3, None), (20, 0, 20), (0, 20)], [(1, None), (13, 21, 34), (0, 34)], - [(14, 35, 49), (0, 49)]] + [(7, None), (0, 35)], + [(14, 36, 50), (0, 50)]] @@ -1762,6 +1792,8 @@ def test_main(): CalcTranslateWordTest, CalcTranslateWordTest2, CalcTranslateWordTest3, + CalcTranslateWordTest4, + CalcTranslateWordTest5, CalcTranslateClipTest, CalcPosTest, Pos2CoordsTest, diff --git a/urwid/curses_display.py b/urwid/curses_display.py index 004fda3..17f0422 100755 --- a/urwid/curses_display.py +++ b/urwid/curses_display.py @@ -23,6 +23,8 @@ Curses-based UI implementation """ +from __future__ import nested_scopes + import curses import sys @@ -70,6 +72,7 @@ class Screen: self._keyqueue = [] self.prev_input_resize = 0 self.set_input_timeouts() + self.last_bstate = 0 def register_palette( self, l ): """Register a list of palette entries. @@ -389,26 +392,47 @@ class Screen: def _encode_mouse_event(self): - # convert back to escape sequence + # convert to escape sequence + last = next = self.last_bstate (id,x,y,z,bstate) = curses.getmouse() - if bstate & curses.BUTTON1_PRESSED: b = 0 - elif bstate & curses.BUTTON2_PRESSED: b = 1 - elif bstate & curses.BUTTON3_PRESSED: b = 2 - elif bstate & curses.BUTTON4_PRESSED: b = 64 - else: - #assert 0, `bstate, bstate&curses.BUTTON1_RELEASED, \ - # bstate&curses.BUTTON2_RELEASED, \ - # bstate&curses.BUTTON3_RELEASED, \ - # bstate&curses.BUTTON4_RELEASED` - #x = bstate&curses.BUTTON1_RELEASED - #y = bstate&curses.BUTTON2_RELEASED - b = 3 - if bstate & curses.BUTTON_SHIFT: b |= 4 - if bstate & curses.BUTTON_ALT: b |= 8 - if bstate & curses.BUTTON_CTRL: b |= 16 + mod = 0 + if bstate & curses.BUTTON_SHIFT: mod |= 4 + if bstate & curses.BUTTON_ALT: mod |= 8 + if bstate & curses.BUTTON_CTRL: mod |= 16 + + l = [] + def append_button( b ): + b |= mod + l.extend([ 27, ord('['), ord('M'), b+32, x+33, y+33 ]) + + if bstate & curses.BUTTON1_PRESSED and last & 1 == 0: + append_button( 0 ) + next |= 1 + if bstate & curses.BUTTON2_PRESSED and last & 2 == 0: + append_button( 1 ) + next |= 2 + if bstate & curses.BUTTON3_PRESSED and last & 4 == 0: + append_button( 2 ) + next |= 4 + if bstate & curses.BUTTON4_PRESSED and last & 8 == 0: + append_button( 64 ) + next |= 8 + if bstate & curses.BUTTON1_RELEASED and last & 1: + append_button( 0 + escape.MOUSE_RELEASE_FLAG ) + next &= ~ 1 + if bstate & curses.BUTTON2_RELEASED and last & 2: + append_button( 1 + escape.MOUSE_RELEASE_FLAG ) + next &= ~ 2 + if bstate & curses.BUTTON3_RELEASED and last & 4: + append_button( 2 + escape.MOUSE_RELEASE_FLAG ) + next &= ~ 4 + if bstate & curses.BUTTON4_RELEASED and last & 8: + append_button( 64 + escape.MOUSE_RELEASE_FLAG ) + next &= ~ 8 - return [ 27, ord('['), ord('M'), b + 32, x + 33, y + 33 ] + self.last_bstate = next + return l def _dbg_instr(self): # messy input string (intended for debugging) diff --git a/urwid/escape.py b/urwid/escape.py index daecebe..1b25fd0 100644 --- a/urwid/escape.py +++ b/urwid/escape.py @@ -133,16 +133,29 @@ class KeyqueueTrie: if b & 8: prefix = prefix + "meta " if b & 16: prefix = prefix + "ctrl " + # 0->1, 1->2, 2->3, 64->4, 65->5 + button = ((b&64)/64*3) + (b & 3) + 1 + if b & 3 == 3: action = "release" - b = 0 + button = 0 + elif b & MOUSE_RELEASE_FLAG: + action = "release" + elif b & MOUSE_DRAG_FLAG: + action = "drag" else: action = "press" - b = ((b&64)/64*3) + (b & 3) + 1 - return ( (prefix + "mouse " + action, b, x, y), keys[3:] ) + return ( (prefix + "mouse " + action, button, x, y), keys[3:] ) + + +# This is added to button value to signal mouse release by curses_display +# and raw_display when we know which button was released. NON-STANDARD +MOUSE_RELEASE_FLAG = 2048 + +# xterm adds this to the button value to signal a mouse drag event +MOUSE_DRAG_FLAG = 32 - ################################################# # Build the input trie from input_sequences list @@ -275,8 +288,8 @@ def set_cursor_position( x, y ): HIDE_CURSOR = ESC+"[?25l" SHOW_CURSOR = ESC+"[?25h" -MOUSE_TRACKING_ON = ESC+"[?1000h" -MOUSE_TRACKING_OFF = ESC+"[?1000l" +MOUSE_TRACKING_ON = ESC+"[?1000h"+ESC+"[?1002h" +MOUSE_TRACKING_OFF = ESC+"[?1002l"+ESC+"[?1000l" _fg_attr = { 'default': "0;39", diff --git a/urwid/raw_display.py b/urwid/raw_display.py index bb3d367..1236e04 100644 --- a/urwid/raw_display.py +++ b/urwid/raw_display.py @@ -23,6 +23,8 @@ Direct terminal UI implementation """ +from __future__ import nested_scopes + import fcntl import termios import os @@ -31,6 +33,7 @@ import struct import sys import tty import signal +import popen2 import util import escape @@ -54,6 +57,9 @@ class Screen: self.screen_buf = None self.resized = False self.maxrow = None + self.gpm_mev = None + self.gpm_event_pending = False + self.last_bstate = 0 def register_palette( self, l ): """Register a list of palette entries. @@ -153,7 +159,22 @@ class Screen: click events along with keystrokes. """ sys.stdout.write(escape.MOUSE_TRACKING_ON) - + + self._start_gpm_tracking() + + def _start_gpm_tracking(self): + if not os.path.isfile("/usr/bin/mev"): + return + if not os.environ.get('TERM',"").lower().startswith("linux"): + return + m = popen2.Popen3("/usr/bin/mev -e 158") + self.gpm_mev = m + + def _stop_gpm_tracking(self): + os.kill(self.gpm_mev.pid, signal.SIGINT) + os.waitpid(self.gpm_mev.pid, 0) + self.gpm_mev = None + def run_wrapper(self,fn): """ Call fn and reset terminal on exit. """ @@ -166,6 +187,8 @@ class Screen: self.signal_restore() termios.tcsetattr(0, termios.TCSADRAIN, old_settings) move_cursor = "" + if self.gpm_mev: + self._stop_gpm_tracking() if self.maxrow is not None: move_cursor = escape.set_cursor_position( 0, self.maxrow) @@ -239,13 +262,13 @@ class Screen: raw = [] keys = [] - while key >= 0: - raw.append(key) - #if key==WINDOW_RESIZE: - # resize = True - # key = self._getch_nodelay() - # continue - keys.append(key) + while key >= 0 or self.gpm_event_pending: + if self.gpm_event_pending: + keys += self._encode_gpm_event() + + if key >= 0: + raw.append(key) + keys.append(key) key = self._getch_nodelay() processed = [] @@ -273,19 +296,87 @@ class Screen: def _getch(self, timeout): ready = None + fd_list = [0] + if self.gpm_mev is not None: + fd_list += [ self.gpm_mev.fromchild ] while True: try: - ready,w,err = select.select([0],[],[0], timeout) + ready,w,err = select.select( + fd_list,[],fd_list, timeout) break except select.error, e: if e.args[0] != 4: raise if self.resized: + ready = [] break - if ready: + if self.gpm_mev is not None: + if self.gpm_mev.fromchild in ready: + self.gpm_event_pending = True + if 0 in ready: return ord(os.read(0, 1)) return -1 + def _encode_gpm_event( self ): + self.gpm_event_pending = False + s = self.gpm_mev.fromchild.readline() + l = s.split(",") + if len(l) != 6: + # unexpected output, stop tracking + self._stop_gpm_tracking() + return [] + ev, x, y, ign, b, m = s.split(",") + ev = int( ev.split("x")[-1], 16) + x = int( x.split(" ")[-1] ) + y = int( y.lstrip().split(" ")[0] ) + b = int( b.split(" ")[-1] ) + m = int( m.split("x")[-1].rstrip(), 16 ) + + # convert to xterm-like escape sequence + + last = next = self.last_bstate + l = [] + + mod = 0 + if m & 1: mod |= 4 # shift + if m & 10: mod |= 8 # alt + if m & 4: mod |= 16 # ctrl + + def append_button( b ): + b |= mod + l.extend([ 27, ord('['), ord('M'), b+32, x+32, y+32 ]) + + if ev == 20: # press + if b & 4 and last & 1 == 0: + append_button( 0 ) + next |= 1 + if b & 2 and last & 2 == 0: + append_button( 1 ) + next |= 2 + if b & 1 and last & 4 == 0: + append_button( 2 ) + next |= 4 + elif ev == 146: # drag + if b & 4: + append_button( 0 + escape.MOUSE_DRAG_FLAG ) + elif b & 2: + append_button( 1 + escape.MOUSE_DRAG_FLAG ) + elif b & 1: + append_button( 2 + escape.MOUSE_DRAG_FLAG ) + else: # release + if b & 4 and last & 1: + append_button( 0 + escape.MOUSE_RELEASE_FLAG ) + next &= ~ 1 + if b & 2 and last & 2: + append_button( 1 + escape.MOUSE_RELEASE_FLAG ) + next &= ~ 2 + if b & 1 and last & 4: + append_button( 2 + escape.MOUSE_RELEASE_FLAG ) + next &= ~ 4 + + self.last_bstate = next + return l + def _getch_nodelay(self): return self._getch(0) diff --git a/urwid/util.py b/urwid/util.py index 387ca77..9be265e 100644 --- a/urwid/util.py +++ b/urwid/util.py @@ -184,9 +184,10 @@ class StandardTextLayout(TextLayout): if n_cr == -1: n_cr = len(text) sc = calc_width(text, p, n_cr) - b.append([(sc,p,n_cr), - # removed character hint - (0,n_cr)]) + l = [(0,n_cr)] + if p!=n_cr: + l = [(sc, p, n_cr)] + l + b.append(l) p = n_cr+1 return b @@ -234,9 +235,10 @@ class StandardTextLayout(TextLayout): prev = move_prev_char(text, p, prev) if text[prev] == " ": sc = calc_width(text,p,prev) - b.append([(sc,p,prev), - # removed character hint - (0,prev)]) + l = [(0,prev)] + if p!=prev: + l = [(sc,p,prev)] + l + b.append(l) p = prev+1 break if is_wide_char(text,prev): @@ -249,10 +251,16 @@ class StandardTextLayout(TextLayout): else: # unwrap previous line space if possible to # fit more text (we're breaking a word anyway) - if b and len(b[-1]) == 2: + if b and (len(b[-1]) == 2 or ( len(b[-1])==1 + and len(b[-1][0])==2 )): # look for removed space above - [(p_sc, p_off, p_end), - (h_sc, h_off)] = b[-1] + if len(b[-1]) == 1: + [(h_sc, h_off)] = b[-1] + p_sc = 0 + p_off = p_end = h_off + else: + [(p_sc, p_off, p_end), + (h_sc, h_off)] = b[-1] if (p_sc < width and h_sc==0 and text[h_off] == " "): # combine with previous line diff --git a/urwid/web_display.py b/urwid/web_display.py index 32b265b..522c713 100755 --- a/urwid/web_display.py +++ b/urwid/web_display.py @@ -613,6 +613,10 @@ class Screen: background = "light gray" self.palette[name] = (foreground, background, mono) + def set_mouse_tracking(self): + """Not yet implemented""" + pass + def run_wrapper(self,fn): """Start the application main loop. @@ -840,7 +844,7 @@ class Screen: """Return the screen size.""" return self.screen_size - def get_input(self): + def get_input(self, raw_keys=False): """Return pending input as a list.""" l = [] resized = False @@ -850,10 +854,15 @@ class Screen: [self.input_fd],[],[],0.5) except select.error, e: # return on interruptions - if e.args[0] == 4: return [] + if e.args[0] == 4: + if raw_keys: + return [],[] + return [] raise if not iready: + if raw_keys: + return [],[] return [] keydata = os.read(self.input_fd, MAX_READ) @@ -877,6 +886,8 @@ class Screen: if resized: l.append("window resize") + if raw_keys: + return l, [] return l diff --git a/urwid/widget.py b/urwid/widget.py index 6d7972e..8161ade 100755 --- a/urwid/widget.py +++ b/urwid/widget.py @@ -1516,8 +1516,12 @@ class Frame(BoxWidget): """Pass keypress to widget in focus.""" if self.focus_part == 'header' and self.header is not None: - return self.header.keypress((maxcol,),key) + if not self.header.selectable(): + return key + return self.header.keypress((maxcol,),key) if self.focus_part == 'footer' and self.footer is not None: + if not self.footer.selectable(): + return key return self.footer.keypress((maxcol,),key) if self.focus_part != 'body': return key @@ -1528,6 +1532,8 @@ class Frame(BoxWidget): remaining -= self.footer.rows((maxcol,)) if remaining <= 0: return key + if not self.body.selectable(): + return key return self.body.keypress( (maxcol, remaining), key ) @@ -1660,9 +1666,10 @@ class Pile(FlowWidget): """Pass the keypress to the widget in focus. Unhandled 'up' and 'down' keys may cause a focus change.""" - key = self.focus_item.keypress( (maxcol,), key ) - if key not in ('up', 'down'): - return key + if self.focus_item.selectable(): + key = self.focus_item.keypress( (maxcol,), key ) + if key not in ('up', 'down'): + return key i = self.widget_list.index(self.focus_item) if key == 'up': |