Commits

Henning Schröder committed 42df9a7

autopep8, lots of fixes and additions

  • Participants
  • Parent commits fc89226

Comments (0)

Files changed (11)

File tcore/__init__.py

 from collections import namedtuple
 
 
-
 def dispatch(*types):
     context = sys._getframe(1).f_locals
     dispatch_map = context.setdefault("_dispatch_map", {})
+
     def wrapper(func):
         key = (func.__name__,)
         dispatch_map[key + types] = func
+        # TODO: create automatic wrapper from str to unicode and vice-versa
         def inner(self, *args, **kwargs):
             arg_types = tuple([type(a) for a in args])
             real_func = dispatch_map.get(key + arg_types, None)
             if real_func is None:
-                raise TypeError("Not method %s with signature %s found" % (func.__name__, str(arg_types)))
+                raise TypeError("Not method %s with signature %s found" %
+                                (func.__name__, str(arg_types)))
             return real_func(self, *args, **kwargs)
+        inner.__name__ = func.__name__
+        inner.__doc__ = func.__doc__
         return inner
     return wrapper
 
 
+def returns_unicode(func):
+    def inner(*args, **kwargs):
+        result = func(*args, **kwargs)
+        if isinstance(result, str):
+            return result.decode("utf-8")
+        return result
+    inner.__name__ = func.__name__
+    inner.__doc__ = func.__doc__
+    return inner
+
+
+def cast_unicode(*flags):
+    def wrapper(func):
+        def inner(*args, **kwargs):
+            if kwargs:
+                raise RuntimeError("keyword arguments not supported right now for %r" % func)
+            args = list(args)
+            for i, (a, f) in enumerate(zip(args, flags)):
+                if f and isinstance(a, str):
+                    args[i] = a.decode("utf-8")
+            return func(*args)
+        return inner
+    return wrapper
 
 
 class TSize(object):
     __slots__ = ("width", "height")
 
-
     @dispatch(int, int)
     def __init__(self, width, height):
         self.width = width
         self.height = height
 
-
     def __cmp__(self, other):
         return cmp((self.width, self.height), (other.width, other.height))
 
 
-
 class TPoint(object):
     __slots__ = ("x", "y")
 
-
-    @dispatch(int,int)
+    @dispatch(int, int)
     def __init__(self, x, y):
         self.x = x
         self.y = y
 
-
     def __cmp__(self, other):
         return cmp((self.x, self.y), (other.x, other.y))
 
-    
     def __add__(self, other):
-        return Point(self.x + other.x, self.y + other.y)
+        return self.__class__(self.x + other.x, self.y + other.y)
 
-    
     def __sub__(self, other):
-        return Point(self.x - other.x, self.y - other.y)
+        return self.__class__(self.x - other.x, self.y - other.y)
 
-    
     def manhattanLength(self):
-        return self.x**2 + self.y**2
+        return self.x ** 2 + self.y ** 2
 
-        
-        
+    def trueLength(self):
+        return self.manhattanLength() ** 0.5
+
+
 class TRect(object):
     __slots__ = ("_topLeft", "_size")
 
-    
-    
     @dispatch(TPoint, TSize)
     def __init__(self, point, size):
         self._topLeft = point
         self._size = size
 
+    @dispatch(int, int, int, int)
+    def __init__(self, x1, y1, width, height):
+        self._topLeft = TPoint(x1, y1)
+        self._size = TSize(width, height)
 
-    @dispatch(int,int,int,int)
-    def __init__(self, x1, y1, width, height):
-        self._topLeft = TPoint(x1,y1)
-        self._size = TSize(width,height)
-
-        
     def __cmp__(self, other):
         return cmp((self._topLeft, self._size), (other._topLeft, other._size))
-        
 
     @property
     def size(self):
         return self._size
-    
-    
+
     @property
     def topLeft(self):
         return self._topLeft
 
-        
     @property
     def bottomLeft(self):
         x, y = self.topLeft.x, self.topLeft.y
         return TPoint(x + self.width, y + self.height)
 
-
     @property
     def width(self):
         return self._size.width
-    
-    
+
     @property
     def height(self):
         return self._size.height
 
-    
     @property
-    def left(seft):
+    def left(self):
         return self._topLeft.x
 
-    
     @property
     def top(self):
         return self._topLeft.y
 
-
     def inner(self, radius=1):
         tl = self._topLeft
         sz = self._size
         return TRect(
-           TPoint(tl.x+radius, tl.y+radius), 
-           TSize(sz.width-1-radius, sz.height-radius))
-
+            TPoint(tl.x + radius, tl.y + radius),
+            TSize(sz.width - 1 - radius, sz.height - radius))
 
 
 class TEvent(object):
 
-    
     def __init__(self, kind):
         self.kind = kind
         self.accepted = False
         self.ignored = False
 
-    
     def accept(self):
         self.accepted = True
-    
-    
+
     def ignore(self):
         self.ignored = True
 
 
+class TInvokeEvent(TEvent):
 
-class TInvokeEvent(TEvent):
-    
     def __init__(self, slot, args):
         super(TInvokeEvent, self).__init__(-1)
         self.slot = slot
         self.args = args
 
 
-        
-
 class TEventLoop(object):
     _instance = None
 
     def __init__(self):
         self.queue = []
 
-
     @classmethod
     def instance(cls):
         if not cls._instance:
             cls._instance = cls()
         return cls._instance
 
-
     def postEvent(self, obj, event):
         self.queue.insert(0, (obj, event))
 
-
     def processEvent(self):
         if self.queue:
             obj, event = self.queue.pop()
         return False
 
 
-    
+class Property(object):
 
-class Property(object):
-    
-    
     def __init__(self, getter=None, setter=None, doc=None):
         self.getter = getter
         self.setter = setter
         self.doc = doc
-        
-        
+
     def __get__(self, obj, cls):
         if obj is None:
             return self
         self.getter(obj)
-        
 
     def __set__(self, obj, value):
         self.setter(obj, value)
 
-    
-    
-
 
 class ObjectSignal(object):
 
-
     def __init__(self, sender):
         self.sender = sender
         self.slots = WeakSet()
-        
-        
+
     def connect(self, slot):
         self.slots.add(slot)
-        
-        
+
     def emit(self, *args, **kwargs):
         direct = kwargs.pop("direct", False)
         if kwargs:
 
         # Object.sender = None
 
-
+    def __call__(self, *args, **kwargs):
+        #return self.emit(*args, **kwargs)
+        raise RuntimeError("You should call emit on signal")
 
 class Signal(object):
-    
 
     def __get__(self, obj, cls):
         signals = obj.__dict__.setdefault("__signals__", {})
         return objsig
 
 
-
-
-
 class TObject(object):
     _object_instances = WeakSet()
-    
+
     _sender = None
 
-
     destroyed = Signal()
 
-
-
     def __init__(self, parent=None, **kwargs):
         TObject._object_instances.add(self)
         self._children = set()
         self._parent = None
         self._event_filters = set()
         self._signals_blocked = False
-    
+
         if parent:
-            self.setParent(parent)        
-            
+            self.setParent(parent)
+
         for key, value in kwargs.items():
             obj = getattr(self.__class__, key)
             if isinstance(obj, Property):
             else:
                 raise TypeError("Invalid keyword argument %r" % key)
 
-
-
     @classmethod
     def sender(cls):
         return cls._sender
-    
 
-    
     def signalsBlocked(self):
         return self._signals_blocked
-    
-    
+
     def blockSignals(self, flag):
         self._signals_blcoked = flag
 
-
     def delete(self):
         self.setParent(None)
         for child in self.children():
             child.delete()
-        self.destoyed.emit(self)            
+        self.destoyed.emit(self)
         TObject._object_instances.remove(self)
-        
-        
+
     __del__ = delete
 
-
     def _add_child(self, child):
         old_parent = child.parent()
         if old_parent:
             old_parent._remove_child(child)
         self._children.add(child)
         child._parent = self
-        
-        
+
     def _remove_child(self, child):
         self._children.remove(child)
         child._parent = None
-        
-            
+
     def setParent(self, new_parent):
         if not new_parent:
             old_parent = self.parent()
         else:
             new_parent._add_child(self)
 
-            
     def children(self):
         return list(self._children)
-    
-    
+
     def parent(self):
         return self._parent
-    
-    
+
     def dump_object_tree(self):
         def dump(obj, level=0):
-            print " " * (level*2), obj
+            print " " * (level * 2), obj
             for child in obj.children():
-                dump(child, level+1)
+                dump(child, level + 1)
         dump(self)
 
-    
     def installEventFilter(self, receiver_object):
         self._event_filters.add(receiver_object)
 
-
     def postEvent(self, event):
         TEventLoop.instance().postEvent(self, event)
 
-
     def event(self, event):
         for receiver_object in self._event_filters:
             if receiver_object.eventFilter(self, event):
         if isinstance(event, TInvokeEvent):
             event.slot(*event.args)
 
-
     def eventFilter(self, obj, event):
         pass
-    
 
 
-    
 if __name__ == "__main__":
     @dispatch(int)
     def test(v):
         print v, "is int"
-        
+
     @dispatch(float)
     def test(v):
         print v, "is float"
-        
 
     test(1)
     test(1.0)
-    
-    
-    
 from tcore import TPoint, TSize
-from tgui import TApplication
+from tgui import TApplication, TAction
 from tgui import TCheckBox, TPushButton
-from tgui import TLabel, TLineEdit, TMenu
+from tgui import TLabel, TLineEdit, TMenu, TMenuBar
 from tgui import TWidget
 
 
 def test():
-	l.setText("clicked!")
+    l.setText("clicked!")
 
 
 def exit():
-	print e.Text
-	app.quit()
+    print e.Text
+    app.quit()
 
 
 app = TApplication()
 app.desktop().show()
 
-win = TWidget(Pos=(4,4), Size=(50,10), WindowTitle="Foo bar")
+win = TWidget(Pos=(4, 4), Size=(70, 15), WindowTitle="Foo bar")
 
-c = TCheckBox(win, Text="Is this working?", Pos=(2,3), Size=(10,10))
+c = TCheckBox(win, Text="Is this working?", Pos=(2, 3), Size=(10, 10))
 
 
-l = TLabel(win, Text="Hello, world", Pos=(2,1))
+l = TLabel(win, Text="Hello, world", Pos=(2, 1))
 
-b = TPushButton(win, Text="OK", Pos=(4,5))
+b = TPushButton(win, Text="OK", Pos=(4, 5))
 b.clicked.connect(test)
 
-
 q = TPushButton(win, "Quit")
 q.move(20, 5)
 q.clicked.connect(exit)
 
-e = TLineEdit(win, Text="edit me..", Pos=(3,4), Size=(18,1))
+e = TLineEdit(win, Text="edit me..", Pos=(3, 4), Size=(18, 1))
 
-m = TMenu(win, Pos=(22, 2), Size=(5,5))
+m = TMenu(win, Pos=(40, 2), Size=(5, 5))
+m.setMenuAction(TAction(Text="Menu"))
 m.addAction("foo")
 m.addAction("bar")
 
+bar = TMenuBar(app.desktop())
+bar.addMenu(m)
+
+
 win.show()
 
 app.run()
 
+print m.activeAction()

File tgui/__init__.py

 # -*- coding: utf-8 -*-
 #from tcore import TSize, TPoint, TRect, TEvent, TEventLoop, TObject, dispatch, Signal, Property
-from tgui.base import (TEventTypes, TKey, TFocusPolicy, TKeyEvent, TPaintEvent, TCloseEvent, THideEvent, TShowEvent,
-                       TResizeEvent, TFocusEvent, TMargins, TColor, TPalette, elide,
-                       TPainter, TPaintEngine, TColorScheme, debug)
+from tgui.base import (
+    TEventTypes, TKey, TFocusPolicy, TKeyEvent, TPaintEvent, TCloseEvent, THideEvent, TShowEvent,
+    TResizeEvent, TFocusEvent, TMargins, TColor, TPalette, elide,
+    TPainter, TColorScheme)
+from tgui.cursesengine import debug, TPaintEngine, TInputEngine
 from tgui.tapplication import TInputContext, TApplication, TDesktopWidget
 from tgui.buttons import TAbstractButton, TPushButton, TToolButton, TCheckBox, TRadioButton
 from tgui.widgets import AbstractTextWidgetMixin, TLineEdit, TMenu, TMenuBar, TLabel
-from tgui.twidget import  TAction, TWidget
-
-
+from tgui.twidget import TAction, TWidget

File tgui/base.py

 # -*- coding: utf-8 -*-
-from exceptions import NotImplementedError
-
 from tcore import TEvent, TPoint, dispatch, TRect
 from tgui.palette import TColorScheme
-from tgui.cursesengine import TPaintEngine, debug # will be imported from other modules from here
+# will be imported from other modules from here
+from tgui.cursesengine import TPaintEngine
 
 
 class TEventTypes:
     Key_Right = "right"
     Key_Up = "up"
     Key_Down = "down"
+    Key_Delete = "delete"
+    Key_Home = "home"
+    Key_End = "end"
 
 
 class TFocusPolicy:
 
 class TKeyEvent(TEvent):
 
-
     def __init__(self, key, text=None, modifiers=None):
         super(TKeyEvent, self).__init__(TEventTypes.KeyPress)
         self.key = key
 
 class TPaintEvent(TEvent):
 
-
     def __init__(self, rect=None):
         super(TPaintEvent, self).__init__(TEventTypes.Paint)
         self.rect = rect
 
 class TCloseEvent(TEvent):
 
-
     def __init__(self):
         super(TCloseEvent, self).__init__(TEventTypes.Close)
 
 
 class THideEvent(TEvent):
 
-
     def __init__(self):
         super(THideEvent, self).__init__(TEventTypes.Hide)
 
 
 class TShowEvent(TEvent):
 
-
     def __init__(self):
         super(TShowEvent, self).__init__(TEventTypes.Show)
 
 
 class TResizeEvent(TEvent):
 
-
     def __init__(self, size):
         super(TResizeEvent, self).__init__(TEventTypes.Resize)
         self.size = size
 
 class TFocusEvent(TEvent):
 
-
     def __init__(self, kind, reason=None):
         super(TFocusEvent, self).__init__(kind)
         self.reason = reason
 
-
     def gotFocus(self):
         return self.kind == TEventTypes.FocusIn
 
-
     def lostFocus(self):
         return self.kind == TEventTypes.FocusOut
 
 
     __slots__ = ("top", "left", "right", "bottom")
 
-
     def __init__(self, left, top, right, bottom):
         self.top = top
         self.left = left
         self.bottom = bottom
 
 
-class TColor:
+class TColor(object):
+
+    __slots__ = ("value",)
+
     def __init__(self, value):
         self.value = value
 
 TColor.WHITE = TColor("white")
 TColor.RED = TColor("dark red")
 TColor.GREEN = TColor("dark green")
-
+TColor.BLUE = TColor("blue")
 
 
 class TPalette(object):
 
-
     def base(self):
         return TColorScheme(TColor.RED.value, TColor.GRAY.value)
 
     window = base
 
-
     def text(self):
         return TColorScheme(TColor.BLACK.value, TColor.GRAY.value)
 
     windowText = text
 
+    def highlighted(self):
+        return TColorScheme(TColor.BLUE.value, TColor.GRAY.value)
+
+    def disabled(self):
+        return TColorScheme(TColor.WHITE.value, TColor.GRAY.value)
+
+
+
+class TCursor(object):
+    HIDDEN = 0
+    NORMAL = 1
+    BLOCK = 2
+
 
 def elide(text, width):
     if len(text) > width:
-        text = text[:width-3] + "..."
+        text = text[:width - 3] + "..."
     return text
 
 
 class TPainter(object):
 
-
     def __init__(self, obj=None):
+        assert isinstance(TPaintEngine.instance, TPaintEngine)
+        self.engine = TPaintEngine.instance
         self.stack = []
         if obj:
             self.begin(obj)
-        self.engine = TPaintEngine.instance
-        #self.engine.hide_cursor()
-
 
     def begin(self, obj):
+        (self._x, self._y) = self.engine.currentxy()
         self.stack.append(obj)
         self.obj = obj
 
-
     def end(self):
+        from tgui.twidget import TWidget # prevent recursive imports
+        assert isinstance(self.obj, TWidget)
+        if self.obj:
+            if self.obj.hasFocus():
+                pos = self.obj.mapToGlobal(self.obj.pos())
+                self.engine.gotoxy(pos.x, pos.y)
+                (self._x, self._y) = self.engine.currentxy()
+                if self.obj.cursor is TCursor.BLOCK:
+                    self.engine.show_cursor(block=True)
+                elif self.obj.cursor is TCursor.NORMAL:
+                    self.engine.show_cursor(block=False)
+                else:
+                    self.engine.hide_cursor()
         self.stack.pop()
         if self.stack:
             self.obj = self.stack[-1]
         else:
             self.obj = None
+        self.engine.gotoxy(self._x, self._y)
 
+    def __enter__(self):
+        self.begin()
+        return self
+
+    def __exit__(self, _exc_type, _exc_val, _exc_tb):
+        self.end()
+
+    def __del__(self):
+        self.end()
 
     def drawRect(self, rect):
         pal = self.obj.palette()
         y2 = y1 + height - 1
         self.engine.rectangle(x1, y1, x2, y2, pal.window())
 
-
-
     def drawLine(self, start_pos, end_pos):
         x1, y1 = start_pos.x, start_pos.y
         x2, y2 = end_pos.x, end_pos.y
             width = x2 - x1
             self.engine.text(x1, y1, "-" * width)
         elif x1 == x2:
-            for y in range(y1, y2+1):
+            for y in range(y1, y2 + 1):
                 self.engine.text(x1, y, "|")
         else:
-            raise NotImplementedError("Only horizontal and vertial lines supported right now")
-
+            raise NotImplementedError(
+                "Only horizontal and vertical lines supported right now")
 
     @dispatch(TPoint, str)
     def drawText(self, pos, text):
-        gpos = self.obj.mapToGlobal(TPoint(0,0))
+        self.drawText(pos, text.decode("utf-8"))
+
+    @dispatch(TPoint, unicode)
+    def drawText(self, pos, text):
+        gpos = self.obj.mapToGlobal(TPoint(0, 0))
         pal = self.obj.palette()
         self.engine.text(gpos.x + pos.x, gpos.y + pos.y, text, pal.text())
 
-
     @dispatch(int, int, str)
     def drawText(self, x, y, text):
         self.drawText(TPoint(x, y), text)
 
+    @dispatch(int, int, unicode)
+    def drawText(self, x, y, text):
+        self.drawText(TPoint(x, y), text)
 
     @dispatch(TRect, str)
     def fillRect(self, rect, char):
+        self.fillRect(rect, char.decode("utf-8"))
+
+    @dispatch(TRect, unicode)
+    def fillRect(self, rect, char):
+        assert len(char) == 1
         pal = self.obj.palette()
         gpos = self.obj.mapToGlobal(TPoint(0, 0))
         x1 = gpos.x + rect.topLeft.x
         y1 = gpos.y + rect.topLeft.y
         width = rect.size.width
         height = rect.size.height
-        x2 = x1 + width - 1
+        #x2 = x1 + width - 1
         y2 = y1 + height - 1
         for y in range(y1, y2):
-            self.engine.text(x1, y, char*width, pal.window())
+            self.engine.text(x1, y, char * width, pal.window())

File tgui/buttons.py

 
 class TAbstractButton(TWidget, AbstractTextWidgetMixin):
 
-
     clicked = Signal()
 
-
     def __init__(self, parent=None, text="", checked=False, checkable=False, **kwargs):
         AbstractTextWidgetMixin.__init__(self, text)
         self._checked = checked
         self._checkable = checkable
         TWidget.__init__(self, parent, **kwargs)
 
-
     def setCheckable(self, flag):
         self._checkable = flag
 
-
     def isCheckable(self):
         return self._checkable
 
     Checkable = Property(isCheckable, setCheckable)
 
-
     def setChecked(self, flag):
         self._checked = flag
         self.update()
 
-
     def isChecked(self):
         return self._checked
 
-
     Checked = Property(isChecked, setChecked)
 
-
     def keyPressEvent(self, event):
         if event.key == TKey.Key_Return:
             if self.isCheckable():
 
     stateChanged = Signal()
 
-
     def __init__(self, parent, **kwargs):
         super(TCheckBox, self).__init__(parent, checkable=True, **kwargs)
 
-
     def setChecked(self, flag):
         super(TCheckBox, self).setChecked(flag)
         self.stateChanged.emit(flag)
 
-
     def paintEvent(self, event):
         painter = TPainter(self)
         checked = self._checked and "x" or " "
 
 class TPushButton(TAbstractButton):
 
-
     def paintEvent(self, event):
         painter = TPainter(self)
         painter.drawText(self.pos(), "< %s >" % self.text())
 
 class TRadioButton(TAbstractButton):
 
-
     def __init__(self, parent):
         super(TRadioButton, self).__init__(self, parent, checkable=True)
 
-
     def paintEvent(self, event):
         painter = TPainter(self)
         checked = self._checked and "x" or " "
 
 
 class TToolButton(TAbstractButton):
-    pass
+    pass

File tgui/cursesengine.py

 # http://gnosis.cx/publish/programming/charming_python_6.html
 import sys
 import curses
-
-from keymap import keymap
-from palette import TColorScheme
+import _curses
+import fcntl
+import termios
+import struct
+from tgui.keymap import keymap
+from tgui.palette import TColorScheme
 
 
 def debug(*args):
     open("/tmp/debug.log", "a").write("%s\n" % s)
 
 
-    
+def terminal_size():
+    # Taken from http://stackoverflow.com/a/566752
+    height, width, hp, wp = struct.unpack('HHHH',
+                                          fcntl.ioctl(0, termios.TIOCGWINSZ,
+                                                      struct.pack('HHHH', 0, 0, 0, 0)))
+    return width, height
+
+
 class BasePaintEngine(object):
-    
 
     def rectangle(self, x1, y1, x2, y2):
-        self.engine.text(x1, y1, "+")                 # top-left
-        self.engine.text(x2, y1, "+")                 # top-right
-        self.engine.text(x1 + 1, y1, "-" * (width-2)) # top line
-        self.engine.text(x1, y2, "+")                 # bottomleft
-        self.engine.text(x2, y2, "+")                 # bottomright
-        self.engine.text(x1+1, y2, "-" * (width-2))   # bottom line
-        for y in range(y1+1, y2):                     # side lines
-            self.engine.text(x1, y, "|")              #  (left)
-            self.engine.text(x2, y, "|")              #  (right)
+        width = abs(x2 - y1)
+        self.engine.text(x1, y1, "+")                    # top-left
+        self.engine.text(x2, y1, "+")                    # top-right
+        self.engine.text(x1 + 1, y1, "-" * (width - 2))  # top line
+        self.engine.text(x1, y2, "+")                    # bottomleft
+        self.engine.text(x2, y2, "+")                    # bottomright
+        self.engine.text(x1 + 1, y2, "-" * (width - 2))    # bottom line
+        for y in range(y1 + 1, y2):                        # side lines
+            self.engine.text(x1, y, "|")  # (left)
+            self.engine.text(x2, y, "|")  # (right)
 
 
-
-
-class TPaintEngine(BasePaintEngine):
+class TInputEngine(object):
     instance = None
-    
 
     def __init__(self):
-        if TPaintEngine.instance:
-            raise RuntimeError("Only one console instance allowed")
-        TPaintEngine.instance = self
-        sys.excepthook = self.excepthook
-        self.window = curses.initscr()
-        #if curses.has_colors():
-        curses.start_color()
-        curses.noecho()
-        #curses.cbreak()    
-        curses.raw() # intercept everything
-        # In keypad mode, escape sequences for special keys
-        # (like the cursor keys) will be interpreted and
-        # a special value like curses.KEY_LEFT will be returned
-        self.window.keypad(1)
-
-        
-        # Frame the interface area at fixed VT100 size
-        self.width = 80
-        self.height= 25
-        self.screen = self.window.subwin(self.height, self.width, 0, 0)
-        #self.screen.box()
-        #self.screen.hline(2, 1, curses.ACS_HLINE, 77)
-        self.screen.refresh()
-    
-
-        
-    def excepthook(self, *args):
-        self.close()
-        sys.__excepthook__(*args)
-        sys.exit(1)
-
-
-    def __del__(self):
-        self.close()
-        
-
-    def close(self):
-        if self.window:
-            self.window.keypad(0)
-        curses.echo()
-        #curses.nocbreak()
-        curses.endwin()
-
+        if TInputEngine.instance:
+            raise RuntimeError("Only one TInputEngine instance allowed")
+        TInputEngine.instance = self
+        if not TPaintEngine.instance:
+            raise RuntimeError(
+                "A TPaintEngine instance is requird by TnputEngine")
+        self.screen = TPaintEngine.instance.screen
+        self._mouse_tracking_enabled = False
 
     def getchar(self, wait=False):
+        KEY_RESIZE = 410  # curses.KEY_RESIZE (sometimes not defined)
+        KEY_MOUSE = 409   # curses.KEY_MOUSE
+        curses.doupdate() # woraround to get screen resize
         if not wait:
             self.screen.nodelay(1)
             while 1:
                 except _curses.error:
                     pass
         return self.screen.getch()
-    
-    
+
     def readkeys(self):
         l = []
         while True:
             l.append(chr(key))
         return "".join(l)
 
-        
     def xreadkeys(self):
         umlauts = {
             '\xc3\xa4': u'ä',
             '\xc3\xbc': u'ü',
             '\xc3\x9c': u'Ü',
             '\xc3\x9f': u'ß',
-            }
-
+        }
+        # TODO: complete the list
         keys = self.readkeys()
-        
         if keys in umlauts:
             return False, umlauts[keys]
         if keys == "\x7f":
                 return True, xkeys
             elif len(keys) == 2:
                 return True, "meta %s" % keys[1]
-        elif len(keys) ==  1:
-            code = ord(keys[0]) 
-            if code < 32 and code not in (9,10):
+        elif len(keys) == 1:
+            code = ord(keys[0])
+            if code < 32 and code not in (9, 10):
                 return True, "ctrl %s" % chr(ord('A') + code - 1) + str(code)
         return False, keys
-        
+
+
+    def set_mouse_tracking(self, flag=True):
+        flag = bool(flag)
+        if flag == self._mouse_tracking_enabled:
+            return
+        if flag:
+            curses.mousemask(0
+                | curses.BUTTON1_PRESSED | curses.BUTTON1_RELEASED
+                | curses.BUTTON2_PRESSED | curses.BUTTON2_RELEASED
+                | curses.BUTTON3_PRESSED | curses.BUTTON3_RELEASED
+                | curses.BUTTON4_PRESSED | curses.BUTTON4_RELEASED
+                | curses.BUTTON1_DOUBLE_CLICKED | curses.BUTTON1_TRIPLE_CLICKED
+                | curses.BUTTON2_DOUBLE_CLICKED | curses.BUTTON2_TRIPLE_CLICKED
+                | curses.BUTTON3_DOUBLE_CLICKED | curses.BUTTON3_TRIPLE_CLICKED
+                | curses.BUTTON4_DOUBLE_CLICKED | curses.BUTTON4_TRIPLE_CLICKED
+                | curses.BUTTON_SHIFT | curses.BUTTON_ALT
+                | curses.BUTTON_CTRL)
+        else:
+            raise NotImplementedError(self.set_mouse_tracking)
+        self._mouse_tracking_enabled = flag
+
+
+class TPaintEngine(BasePaintEngine):
+    instance = None
+
+    def __init__(self, width=None, height=None):
+        if TPaintEngine.instance:
+            raise RuntimeError("Only one TPaintEngine instance allowed")
+        TPaintEngine.instance = self
+        sys.excepthook = self.excepthook
+        self._x = 0
+        self._y = 0
+        self.cursor_state = True
+        self.window = curses.initscr()
+        # if curses.has_colors():
+        curses.start_color()
+        curses.noecho()
+        # curses.cbreak()
+        curses.raw()  # intercept everything
+        # In keypad mode, escape sequences for special keys
+        # (like the cursor keys) will be interpreted and
+        # a special value like curses.KEY_LEFT will be returned
+        self.window.keypad(1)
+
+        term_width, term_height = terminal_size()
+        self.width = width or term_width
+        self.height = height or term_height
+        self.screen = self.window.subwin(self.height, self.width, 0, 0)
+        # self.screen.box()
+        #self.screen.hline(2, 1, curses.ACS_HLINE, 77)
+        self.screen.refresh()
+
+    def excepthook(self, *args):
+        self.close()
+        sys.__excepthook__(*args)
+        sys.exit(1)
+
+    def __del__(self):
+        self.close()
+
+    def close(self):
+        if self.window:
+            self.window.keypad(0)
+        curses.echo()
+        # curses.nocbreak()
+        curses.endwin()
 
     def refresh(self):
         self.screen.refresh()
-    
-    
-    def text(self, column, line, string, style=curses.A_NORMAL):
+
+    def text(self, column, row, value, style=curses.A_NORMAL):
+        if isinstance(value, unicode):
+            value = value.encode("utf-8")
+        else:
+            if not isinstance(value, str):
+                value = str(value)
         if isinstance(style, TColorScheme):
             style = style.color_pair()
-        self.screen.addstr(line, column, string, style)
+        self.screen.addstr(row, column, value, style)
+        self.window.move(self._y, self._x)
 
-
-    def page(self):
+    def clear(self):  # XXX: better name?
         self.window.erase()
-        
 
     def gotoxy(self, x, y):
-        self.window.move(x, y)
-        self.text(x,y, "")
+        self._x = x
+        self._y = y
+        #self.window.move(y, x)
+        self.text(x, y, "")
 
-        
-    def show_cursor(self):
-        curses.curs_set(2)
-
+    def show_cursor(self, block=True):
+        if block:
+            curses.curs_set(2)
+            self.cursor_state = 2
+        else:
+            curses.curs_set(1)
+            self.cursor_state = 1
 
     def hide_cursor(self):
         curses.curs_set(0)
+        self.cursor_state = 0
 
+    def currentxy(self):
+        (y, x) = self.window.getyx()
+        return (x, y)
 
     def rectangle(self, ulx, uly, lrx, lry, style=curses.A_NORMAL):
         if isinstance(style, TColorScheme):
             style = style.color_pair()
         # taken from curses.textpad
-        self.window.vline(uly+1, ulx, curses.ACS_VLINE, lry - uly - 1, style)
-        self.window.hline(uly, ulx+1, curses.ACS_HLINE, lrx - ulx - 1, style)
-        self.window.hline(lry, ulx+1, curses.ACS_HLINE, lrx - ulx - 1, style)
-        self.window.vline(uly+1, lrx, curses.ACS_VLINE, lry - uly - 1, style)
+        self.window.vline(uly + 1, ulx, curses.ACS_VLINE, lry - uly - 1, style)
+        self.window.hline(uly, ulx + 1, curses.ACS_HLINE, lrx - ulx - 1, style)
+        self.window.hline(lry, ulx + 1, curses.ACS_HLINE, lrx - ulx - 1, style)
+        self.window.vline(uly + 1, lrx, curses.ACS_VLINE, lry - uly - 1, style)
         self.window.addch(uly, ulx, curses.ACS_ULCORNER, style)
         self.window.addch(uly, lrx, curses.ACS_URCORNER, style)
         self.window.addch(lry, lrx, curses.ACS_LRCORNER, style)
         self.window.addch(lry, ulx, curses.ACS_LLCORNER, style)
 
 
-
-
-if __name__=='__main__':
-    c = TPaintEngine()
-    c.text(1,1, "hello, world!")
+if __name__ == '__main__':
+    paint = TPaintEngine()
+    inp = TInputEngine()
+    paint.text(1, 1, "hello, world!")
     i = 0
     while True:
-        extended, keys = c.xreadkeys()
+        extended, keys = inp.xreadkeys()
         if keys or extended:
             i += 1
-            c.text(1, i, "%r  %s   " % (keys, extended))
-    c.close()
-
+            paint.text(1, i, "%r  %s   " % (keys, extended))
+    paint.close()

File tgui/generate_keymap.py

 # -*- coding: utf-8 -*-
 
-def escape_modifier( digit ):
+
+def escape_modifier(digit):
     mode = ord(digit) - ord("1")
-    return "shift "*(mode&1) + "meta "*((mode&2)/2) + "ctrl "*((mode&4)/4)
+    return "shift " * (mode & 1) + "meta " * ((mode & 2) / 2) + "ctrl " * ((mode & 4) / 4)
 
 
-keys = [('[A','up'),('[B','down'),('[C','right'),('[D','left'),
-    ('[E','5'),('[F','end'),('[G','5'),('[H','home'),
-    ('[1~','home'),('[2~','insert'),('[3~','delete'),('[4~','end'),
-    ('[5~','page up'),('[6~','page down'),
-    ('[7~','home'),('[8~','end'),
-    
-    ('[[A','f1'),('[[B','f2'),('[[C','f3'),('[[D','f4'),('[[E','f5'),
-    
-    ('[11~','f1'),('[12~','f2'),('[13~','f3'),('[14~','f4'),
-    ('[15~','f5'),('[17~','f6'),('[18~','f7'),('[19~','f8'),
-    ('[20~','f9'),('[21~','f10'),('[23~','f11'),('[24~','f12'),
-    ('[25~','f13'),('[26~','f14'),('[28~','f15'),('[29~','f16'),
-    ('[31~','f17'),('[32~','f18'),('[33~','f19'),('[34~','f20'),
-    
-    ('OA','up'),('OB','down'),('OC','right'),('OD','left'),
-    ('OH','home'),('OF','end'),
-    ('OP','f1'),('OQ','f2'),('OR','f3'),('OS','f4'),
-    ('Oo','/'),('Oj','*'),('Om','-'),('Ok','+'),
-    
-    ('[Z','shift tab')
-    ] + [
+keys = [('[A', 'up'), ('[B', 'down'), ('[C', 'right'), ('[D', 'left'),
+        ('[E', '5'), ('[F', 'end'), ('[G', '5'), ('[H', 'home'),
+        ('[1~', 'home'), ('[2~', 'insert'), ('[3~', 'delete'), ('[4~', 'end'),
+        ('[5~', 'page up'), ('[6~', 'page down'),
+        ('[7~', 'home'), ('[8~', 'end'),
+
+        ('[[A', 'f1'), ('[[B', 'f2'), (
+            '[[C', 'f3'), ('[[D', 'f4'), ('[[E', 'f5'),
+
+        ('[11~', 'f1'), ('[12~', 'f2'), ('[13~', 'f3'), ('[14~', 'f4'),
+        ('[15~', 'f5'), ('[17~', 'f6'), ('[18~', 'f7'), ('[19~', 'f8'),
+        ('[20~', 'f9'), ('[21~', 'f10'), ('[23~', 'f11'), ('[24~', 'f12'),
+        ('[25~', 'f13'), ('[26~', 'f14'), ('[28~', 'f15'), ('[29~', 'f16'),
+        ('[31~', 'f17'), ('[32~', 'f18'), ('[33~', 'f19'), ('[34~', 'f20'),
+
+        ('OA', 'up'), ('OB', 'down'), ('OC', 'right'), ('OD', 'left'),
+        ('OH', 'home'), ('OF', 'end'),
+        ('OP', 'f1'), ('OQ', 'f2'), ('OR', 'f3'), ('OS', 'f4'),
+        ('Oo', '/'), ('Oj', '*'), ('Om', '-'), ('Ok', '+'),
+
+        ('[Z', 'shift tab')
+        ] + [
     #('%s' % chr(i) , 'ctrl %s' % chr(ord('A') + i)) for i in range(27)
-    ] + [
+] + [
     # modified cursor keys + home, end, 5 -- [#X and [1;#X forms
-    (prefix+digit+letter, escape_modifier(digit) + key)
-       for prefix in "[","[1;"
-         for digit in "12345678"
-           for letter,key in zip("ABCDEFGH",
-              ('up','down','right','left','5','end','5','home'))
-    ] + [ 
+    (prefix + digit + letter, escape_modifier(digit) + key)
+    for prefix in "[", "[1;"
+    for digit in "12345678"
+    for letter, key in zip("ABCDEFGH",
+                           ('up', 'down', 'right', 'left', '5', 'end', '5', 'home'))
+] + [
     # modified F1-F4 keys -- O#X form
-    ("O"+digit+letter, escape_modifier(digit) + key)
-      for digit in "12345678"
-        for letter,key in zip("PQRS",('f1','f2','f3','f4'))
-    ] + [ 
+    ("O" + digit + letter, escape_modifier(digit) + key)
+    for digit in "12345678"
+    for letter, key in zip("PQRS", ('f1', 'f2', 'f3', 'f4'))
+] + [
     # modified F1-F13 keys -- [XX;#~ form
-    ("["+str(num)+";"+digit+"~", escape_modifier(digit) + key)
+    ("[" + str(num) + ";" + digit + "~", escape_modifier(digit) + key)
     for digit in "12345678"
-    for num,key in zip(
-    (11,12,13,14,15,17,18,19,20,21,23,24,25,26,28,29,31,32,33,34),
-    ('f1','f2','f3','f4','f5','f6','f7','f8','f9','f10','f11',
-    'f12','f13','f14','f15','f16','f17','f18','f19','f20'))
-    ] 
+    for num, key in zip(
+        (11, 12, 13, 14, 15, 17, 18, 19, 20, 21,
+         23, 24, 25, 26, 28, 29, 31, 32, 33, 34),
+        ('f1', 'f2', 'f3', 'f4', 'f5', 'f6', 'f7', 'f8', 'f9', 'f10', 'f11',
+         'f12', 'f13', 'f14', 'f15', 'f16', 'f17', 'f18', 'f19', 'f20'))
+]
 
-    
-	
+
 if __name__ == "__main__":
     f = open("keymap.py", "w")
     print >>f, "# -*- coding: utf-8 -*-"
     for chars, name in keys:
         print >>f, "    %r: %r," % (chars, name)
     print >>f, "}"
-    f.close()
+    f.close()

File tgui/palette.py

 import curses
 
 colors = {
-    'default':        -1,
-    'black':          curses.COLOR_BLACK,  
-    'dark red':       curses.COLOR_RED,    
-    'dark green':     curses.COLOR_GREEN,  
-    'brown':          curses.COLOR_YELLOW, 
-    'dark blue':      curses.COLOR_BLUE,   
+    'default': -1,
+    'black':          curses.COLOR_BLACK,
+    'dark red':       curses.COLOR_RED,
+    'dark green':     curses.COLOR_GREEN,
+    'brown':          curses.COLOR_YELLOW,
+    'dark blue':      curses.COLOR_BLUE,
     'dark magenta':   curses.COLOR_MAGENTA,
-    'dark cyan':      curses.COLOR_CYAN,   
-    'light gray':     curses.COLOR_WHITE,  
-    'dark gray':      curses.COLOR_BLACK,  
-    'light red':      curses.COLOR_RED,    
-    'light green':    curses.COLOR_GREEN,  
-    'yellow':         curses.COLOR_YELLOW, 
-    'light blue':     curses.COLOR_BLUE,   
+    'dark cyan':      curses.COLOR_CYAN,
+    'light gray':     curses.COLOR_WHITE,
+    'dark gray':      curses.COLOR_BLACK,
+    'light red':      curses.COLOR_RED,
+    'light green':    curses.COLOR_GREEN,
+    'yellow':         curses.COLOR_YELLOW,
+    'light blue':     curses.COLOR_BLUE,
     'light magenta':  curses.COLOR_MAGENTA,
-    'light cyan':     curses.COLOR_CYAN,   
-    'white':          curses.COLOR_WHITE,  
-    }
-                                                                    
+    'light cyan':     curses.COLOR_CYAN,
+    'white':          curses.COLOR_WHITE,
+}
 
 
 class TColorScheme(object):
     registered = {}
 
-
     def __init__(self, foreground, background):
         key = (foreground, background)
         found = self.registered.get(key, None)
         else:
             self.num = found
 
-        
     def color_pair(self):
         return curses.color_pair(self.num)
 
-

File tgui/tapplication.py

 # -*- coding: utf-8 -*-
 import sys
 
-from tcore import TObject, Signal, TEventLoop, TPoint, TSize
+from tcore import TObject, Signal, TEventLoop, TPoint, TSize, Property
 from tgui.base import TKeyEvent, TFocusEvent, TEventTypes, TPainter
 from tgui.twidget import TWidget
-from tgui.cursesengine import TPaintEngine
-
+from tgui.cursesengine import TPaintEngine, TInputEngine
 
 
 class TInputContext(TObject):
 
-
     def __init__(self, parent=None):
         super(TInputContext, self).__init__(parent)
-        self.xreadkeys = TPaintEngine.instance.xreadkeys
-
+        self.xreadkeys = TInputEngine.instance.xreadkeys
 
     def poll(self):
         extended, keys = self.xreadkeys()
                 event = TKeyEvent(key=keys, text=keys)
             self.focusWidget().postEvent(event)
 
-
     def setFocusWidget(self, widget):
         old_focus_widget = self.focusWidget()
-
         if widget != old_focus_widget:
-            cls.instance._focus_widget = widget
+            TApplication.instance._focus_widget = widget
             if old_focus_widget:
                 old_focus_widget.postEvent(TFocusEvent(TEventTypes.FocusOut))
             widget.postEvent(TFocusEvent(TEventTypes.FocusIn))
             widget.activateWindow()
 
-
     def focusWidget(self):
         return TApplication.instance.focusWidget()
 
+    FocusWidget = Property(focusWidget, setFocusWidget)
+
 
 class TApplication(TObject):
 
     instance = None
-
     focusChanged = Signal()
 
-
     def __init__(self):
         super(TApplication, self).__init__()
         self._active_window = None
         self.event_loop = TEventLoop.instance()
         TApplication.instance = self
         self._desktop = TDesktopWidget()
+        self.input_engine = TInputEngine()
         self.input_context = TInputContext()
 
-
     def quit(self, exitcode=0):
         TPaintEngine.instance.close()
         sys.exit(exitcode)
 
-
     @classmethod
     def desktop(cls):
         return cls.instance._desktop
 
-
     @classmethod
     def activeWindow(cls):
         if cls.instance._active_window:
             return cls.instance._active_window
 
-
     @classmethod
     def setActiveWindow(cls, widget):
         window = widget.window()
         if cls.instance._active_window != window:
             cls.instance._active_window = window
 
-
     @classmethod
     def allWidgets(cls):
         found = list(cls.topLevelWidgets())
                     found.append(child)
         return found
 
-
     @classmethod
     def topLevelWidgets(cls):
         l = []
                 l.append(obj)
         return l
 
-
     @classmethod
     def focusWidget(cls):
         return cls.instance.activeWindow().focusWidget()
 
-
     def postEvent(self, receiver_object, event):
         self.event_loop.postEvent(receiver_object, event)
 
-
     def sendEvent(self, receiver_object, event):
         self.event_loop.sendEvent(receiver_object, event)
 
-
     def notify(self, receiver_object, event):
         self.event_loop.notify(receiver_object, event)
 
-
     def processEvent(self):
         return self.event_loop.processEvent()
 
-
     def run(self):
-        #self._desktop.show()
-        win = self.activeWindow().focusNextChild()
+        # self._desktop.show()
+        _win = self.activeWindow().focusNextChild()
         while True:
             if self.processEvent():
                 TPaintEngine.instance.refresh()
 
 class TDesktopWidget(TWidget):
 
-
     def __init__(self):
-        self.engine = TPaintEngine()
+        self.paint_engine = TPaintEngine()
         super(TDesktopWidget, self).__init__()
         self._update_geom(TPoint(0, 0), self.sizeHint())
 
-
     def sizeHint(self):
-        return TSize(self.engine.width, self.engine.height)
-
+        return TSize(self.paint_engine.width, self.paint_engine.height)
 
     def paintEvent(self, event):
         painter = TPainter(self)
-        painter.fillRect(self.geometry(), " ")
+        painter.fillRect(self.geometry(), " ")

File tgui/twidget.py

 # -*- coding: utf-8 -*-
-from exceptions import ValueError
-
 from tcore import TPoint, dispatch, TRect, TObject, Signal, Property, TSize
-from tgui.base import (TMargins, TFocusPolicy, TPalette, TResizeEvent, TFocusEvent, TEventTypes, THideEvent,
-                       TCloseEvent, TPaintEvent, TShowEvent, TKeyEvent, TKey, elide, TPaintEngine, TPainter)
+from tgui.base import (
+    TMargins, TFocusPolicy, TPalette, TResizeEvent, TFocusEvent, TEventTypes, THideEvent,
+    TCloseEvent, TPaintEvent, TShowEvent, TKeyEvent, TKey, elide, TPaintEngine, TPainter, TCursor)
 
 
 def tApp():
     changed = Signal()
     triggered = Signal()
 
-
     def __init__(self, parent=None, **kwargs):
         self._text = ""
         self._checked = False
         super(TAction, self).__init__(parent, **kwargs)
 
-
     def text(self):
         return self._text
 
-
     def setText(self, text):
         self._text = text
         self.changed.emit()
 
-
     Text = Property(text, setText)
 
-
     def setChecked(self, flag):
         self._checked = flag
         self.changed.emit()
 
-
     def isChecked(self):
         return self._checked
 
-
     Checked = Property(isChecked, setChecked)
 
-
     def trigger(self):
         self.triggered.emit()
 
 
 class TWidget(TObject):
 
-
     def __init__(self, parent=None, **kwargs):
         self._contents_margins = TMargins(0, 0, 0, 0)
         self._focus_policy = TFocusPolicy.TabFocus
         self._palette = TPalette()
         self._windowTitle = ""
         self._actions = []
+        self._cursor = TCursor.NORMAL
+        self._cursorPosition = TPoint(0, 0)
         super(TWidget, self).__init__(parent, **kwargs)
         if not parent:
             self.activateWindow()
 
+    def cursor(self):
+        return self._cursor
+
+    def setCursor(self, cursor):
+        self._cursor = cursor
+
+    Cursor = Property(cursor, setCursor)
+
+    def cursorPosition(self):
+        return self._cursorPosition
+
+    @dispatch(TPoint)
+    def setCursorPosition(self, x, y):
+        self.setCursorPosition(TPoint(x, y))
+
+    @dispatch(TPoint)
+    def setCursorPosition(self, pos):
+        old_pos = self._cursorPosition
+        if pos != old_pos:
+            self._cursorPosition = pos
+            self.cursorPositionChanged.emit(old_pos, pos)
+            if self.hasFocus() and self._cursor is not TCursor.HIDDEN:
+                gpos = self.mapToGlobal(self.pos()) + pos
+                TPaintEngine.instance.gotoxy(gpos.x, gpos.y)
+                TPaintEngine.instance.show_cursor()
+
+    CursorPosition = Property(cursorPosition, setCursorPosition)
+
+    def moveCursorRelative(self, dx=0, dy=0):
+        pos = self.pos()
+        new_pos = TPoint(pos.x + dx, pos.y + dy)
+        self.setCursorPosition(new_pos)
 
     def actions(self):
         return self._actions[:]
 
-
     @dispatch(str)
     def addAction(self, title):
         return self.addAction(TAction(self, Text=title))
 
-
     @dispatch(TAction)
     def addAction(self, action):
         self._actions.append(action)
         self.update()
         return action
 
-
     def windowTitle(self):
         return self._windowTitle
 
-
     def setWindowTitle(self, title):
         self._windowTitle = title or ""
         if not self.parent():
             self.update()
 
-
     WindowTitle = Property(windowTitle, setWindowTitle)
 
-
-    @dispatch(int,int,int,int)
+    @dispatch(int, int, int, int)
     def setContentsMargins(self, left, top, right, bottom):
         self.setContentsMargins(TMargins(left, top, right, bottom))
 
-
     @dispatch(TMargins)
     def setContentsMargins(self, margins):
         old_margins = self._contents_margins
             self.postEvent(TResizeEvent(self.size))
             self.update()
 
-
     def activateWindow(self):
         tApp().setActiveWindow(self)
 
-
     def _focusable_widgets(self):
         l = []
         for child in self.window().children():
             if not isinstance(child, TWidget):
                 continue
-            if 0: #child.children():
-                pass # XXX: l.extend(child._focusable_widgets())
+            if 0:  # child.children():
+                pass  # XXX: l.extend(child._focusable_widgets())
             elif child.focusPolicy() != TFocusPolicy.NoFocus:
                 l.append(child)
         return l
 
-
     def nextInFocusChain(self):
         focus_widget = self.focusWidget()
         widgets = self._focusable_widgets()
                 idx = 0
             return widgets[idx % len(widgets)]
 
-
     def focusNextChild(self):
         widget = self.window().nextInFocusChain()
         if widget:
             return True
         return False
 
-
     def previousInFocusChain(self):
         focus_widget = self.focusWidget()
         widgets = self._focusable_widgets()
                 idx = -1
             return widgets[idx]
 
-
     def setPalette(self, pal):
         self._palette = pal
 
-
     def palette(self):
         return self._palette
 
-
     def focusWidget(self):
         if self.parent():
             return self.window().focusWidget()
         if self._focus_widget:
             return self._focus_widget
 
-
-
     def focusPreviousChild(self):
         widget = self.window().previousInFocusChain()
         if widget:
-            widet.setFocus()
+            widget.setFocus()
             return True
         return False
 
-
     def setFocusPolicy(self, policy):
         self._focus_policy = policy
 
-
     def focusPolicy(self):
         return self._focus_policy
 
-
     def setFocus(self, reason=None):
         if self._focus_policy == TFocusPolicy.NoFocus:
             return
         win._focus_widget = self
         self.postEvent(TFocusEvent(TEventTypes.FocusIn))
 
-
     def hasFocus(self):
         return tApp().focusWidget() == self
 
-
     def clearFocus(self):
         if self.hasFocus():
             self.postEvent(TFocusEvent())
 
-
-
     def sizeHint(self):
-        return TSize(1,1)
-
+        return TSize(1, 1)
 
     def window(self):
         obj = self
             parent = obj.parent()
         return obj
 
-
     def close(self):
         if self.parent() == None:
             self.postEvent(THideEvent())
         else:
             return False
 
-
     def show(self):
         self.setVisible(True)
 
-
     def hide(self):
         self.setVisible(False)
 
-
     def setVisible(self, flag):
         self._visible = flag
         self.postEvent(TPaintEvent())
             if isinstance(child, TWidget):
                 child.setVisible(flag)
 
-
     def isVisible(self):
         return self._visible
 
     Visible = Property(isVisible, setVisible)
 
-
     def _update_geom(self, pos, size, silent=False):
         x1 = pos.x
         y1 = pos.y
         if not silent:
             self.postEvent(TResizeEvent(size))
 
-
     def size(self):
         (x1, y1, x2, y2) = self._geom
         width, height = x2 - y1, y2 - y1
         return TSize(width, height)
 
-
     def width(self):
         return self.size().width
 
-
     def height(self):
         return self.size().height
 
-
-    def left(seft):
+    def left(self):
         return self.pos().x
 
-
     def top(self):
         return self.pos().y
 
-
     @dispatch(TSize)
     def resize(self, size):
         (x1, y1, x2, y2) = self._geom
         width, height = size.width, size.height
         self._update_geom(TPoint(x1, y1), TSize(width, height))
 
-
-    @dispatch(int,int)
+    @dispatch(int, int)
     def resize(self, width, height):
         self.resize(TSize(width, height))
 
-
     @dispatch(tuple)
     def resize(self, (width, height)):
         self.resize(TSize(width, height))
 
-
     Size = Property(size, resize)
 
-
     def pos(self):
         (x1, y1, x2, y2) = self._geom
         return TPoint(x1, y1)
 
-
     @dispatch(TPoint)
     def move(self, pos):
         self._update_geom(pos, self.size())
 
-
-
-    @dispatch(int,int)
+    @dispatch(int, int)
     def move(self, x, y):
         self.move(TPoint(x, y))
 
-
     @dispatch(tuple)
-    def move(self, (x,y)):
-        self.move(TPoint(x,y))
-
+    def move(self, (x, y)):
+        self.move(TPoint(x, y))
 
     Pos = Property(pos, move)
 
-
     def geometry(self):
         return TRect(self.pos(), self.size())
 
-
     @dispatch(TPoint, TSize)
     def setGeometry(self, pos, size):
         self._update_geom(pos, size)
 
-
-
-    @dispatch(int,int,int,int)
+    @dispatch(int, int, int, int)
     def setGeometry(self, x1, y1, width, height):
         self._update_geom(TPoint(x1, y1), TSize(width, height))
 
-
     @dispatch(tuple)
     def setGeometry(self, (x1, y1, width, height)):
         self._update_geom(TPoint(x1, y1), TSize(width, height))
 
-
     @dispatch(TRect)
     def setGeometry(self, rect):
         self._update_geom(rect.topLeft, rect.size)
 
-
     Geometry = Property(geometry, setGeometry)
 
-
     def mapToParent(self, pos):
         parent = self.parent()
         if not parent:
             ppos = parent.pos()
             return TPoint(ppos.x + pos.x, ppos.y + pos.y)
 
-
     def mapToGlobal(self, pos):
         obj = self
         while obj:
             obj = obj.parent()
         return pos
 
-
     def event(self, event):
         super(TWidget, self).event(event)
         if event.accepted or event.ignored:
             else:
                 self.focusOutEvent(event)
 
-
     def showEvent(self, event):
         if not self.parent():
             tApp().setActiveWindow(self)
 
-
     def focusInEvent(self, event):
         pos = self.mapToGlobal(self.pos())
         TPaintEngine.instance.gotoxy(pos.x, pos.y)
         TPaintEngine.instance.show_cursor()
 
-
     def focusOutEvent(self, event):
         pass
 
-
     def keyPressEvent(self, event):
         if event.key == TKey.Key_Tab:
             widget = self.nextInFocusChain()
             widget.setFocus()
             event.accept()
 
-
-
     def paintEvent(self, event):
         painter = TPainter(self)
         if self.parent():
             painter.fillRect(self.geometry(), " ")
         else:
+            # This is a dialog/window
             rect = self.geometry()
             painter.drawRect(rect)
             painter.fillRect(rect.inner(), " ")
             pos = self.pos()
             painter.drawText(pos.x + self.width() - 4, pos.y, "[*]")
-            painter.drawText(pos.x + 2, pos.y, " %s " % elide(self.windowTitle(), self.width() - 4) )
+            painter.drawText(pos.x + 2, pos.y, " %s " %
+                             elide(self.windowTitle(), self.width() - 4))
         for child in self.children():
             if isinstance(child, TWidget) and child.isVisible():
                 child.postEvent(event)
 
-
     def resizeEvent(self, event):
         pass
 
-
     def update(self, rect=None):
         self.postEvent(TPaintEvent(rect))
 
-
     def repaint(self, rect=None):
         event = TPaintEvent(rect)
         self.paintEvent(event)
-
-

File tgui/widgets.py

 # -*- coding: utf-8 -*-
-from tcore import TSize, Property, Signal
-from tgui.base import TFocusPolicy, elide, TKey, TPaintEngine, TPainter
+from tcore import TSize, Property, Signal, TPoint, dispatch
+from tgui.base import TFocusPolicy, elide, TKey, TPaintEngine, TPainter, TCursor
 from tgui.twidget import TWidget, TAction
 
 
 class AbstractTextWidgetMixin(object):
 
-
     def __init__(self, text=""):
         self._text = text
 
-
     def sizeHint(self):
         return TSize(min(70, len(self._text)), 1)
 
-
     def setText(self, text):
         self._text = text
         self.resize(self.sizeHint())
         self.update()
 
-
     def text(self):
         return self._text
 
-
     Text = Property(text, setText)
 
 
 class TLabel(TWidget, AbstractTextWidgetMixin):
 
-
     def __init__(self, parent=None, **kwargs):
         AbstractTextWidgetMixin.__init__(self)
         TWidget.__init__(self, parent, **kwargs)
         self.setFocusPolicy(TFocusPolicy.NoFocus)
 
-
     def paintEvent(self, event):
         painter = TPainter(self)
         painter.drawText(self.pos(), elide(self.text(), self.width()))
 
 class TFrame(TWidget):
 
-
     def __init__(self, parent):
         super(TWidget, self).__init__(parent)
         self.setContentsMargins(1, 1, -1, -1)
 
-
     def paintEvent(self, event):
         TWidget.paintEvent(event)
         painter = TPainter(self)
             child.paintEvent(event)
 
 
-class TDialog(TWidget):
-
+class TDialog(TFrame):
 
     def __init__(self):
         super(TWidget, self).__init__()
     cursorPositionChanged = Signal()
     textChanged = Signal()
 
-
     def __init__(self, parent, **kwargs):
-        self._text = ""
-        self._cursorPosition = 0
-        self._cursor_offset = 0
+        self._text = u""
+        self._display_offset = 0
+        #kwargs["Cursor"] = kwargs.get("Cursor", TCursor.NORMAL)
+        self.setCursor(TCursor.BLOCK)
         super(TLineEdit, self).__init__(parent, **kwargs)
 
-
     def text(self):
         return self._text
 
+    @dispatch(str)
+    def setText(self, text):
+        return self.setText(text.decode("utf-8"))
 
+    @dispatch(unicode)
     def setText(self, text):
         old_text = self._text
         if old_text != text:
             self.update()
             self.textChanged.emit(text)
 
-
     Text = Property(text, setText)
 
-
-    def cursorPosition(self):
-        return self._cursorPosition
-
-
-    def setCursorPosition(self, pos):
-        old_pos = self._cursorPosition
-        if pos != old_pos:
-            self._cursorPosition = pos
-            pos = self.mapToGlobal(self.pos())
-            TPaintEngine.instance.gotoxy(pos.x + self.cursorPosition() + 1, pos.y)
-            TPaintEngine.instance.show_cursor()
-            self.cursorPositionChanged.emit(old_pos, pos)
-
-
-    CursorPosition = Property(cursorPosition, setCursorPosition)
-
-
-    def focusInEvent(self, event):
-        pos = self.mapToGlobal(self.pos())
-        TPaintEngine.instance.gotoxy(pos.x + self.cursorPosition() + 1, pos.y)
-        TPaintEngine.instance.show_cursor()
-
-
     def paintEvent(self, event):
         painter = TPainter(self)
-        text = self.text()[self._cursor_offset:self._cursor_offset+self.width()]
-        display_text = "[%s]" % text.ljust(self.width(), " ")
-        painter.drawText(self.pos(), display_text)
-
+        text = self.text()[
+            self._display_offset:self._display_offset + self.width()]
+        display_width = self.width() - 2
+        if len(text) > display_width:
+            display_text = text[:display_width]
+        else:
+            display_text = text.ljust(display_width, " ")
+        painter.drawText(self.pos(), u"[%s]" % display_text)
 
     def keyPressEvent(self, event):
         if event.key in (TKey.Key_Tab, TKey.Key_Backtab,
             pass
         elif event.key or event.text:
             text = self.text()
-            cpos = self.cursorPosition()
-            tpos = self._cursor_offset + cpos
+            cursor_x = self.cursorPosition().x
+            tpos = self._display_offset + cursor_x
             if event.text:
                 new_text = text[:tpos] + event.key + text[tpos:]
                 self.setText(new_text)
-                if cpos == self.width()-2:
-                    self._cursor_offset += 1
-                    self.setCursorPosition(cpos) # XXX
+                if cursor_x == self.width() - 2:
+                    self._display_offset += 1
+                    pass  # XXX
                 else:
-                    self.setCursorPosition(cpos + 1)
+                    self.moveCursorRelative(1)
             elif event.key == TKey.Key_Backspace:
-                new_text = text[:tpos-1] + text[tpos:]
+                new_text = text[:tpos - 1] + text[tpos:]
                 self.setText(new_text)
-                if tpos > 0 and cpos == 0:
-                    self._cursor_offset -= 1
-                    self.setCursorPosition(cpos) # XXX
-                elif cpos > 0:
-                    self.setCursorPosition(cpos - 1)
+                if tpos > 0 and cursor_x == 0:
+                    self._display_offset -= 1
+                    pass  # TODO
+                elif cursor_x > 0:
+                    self.moveCursorRelative(-1)
                 else:
-                    self.setCursorPosition(cpos) # XXX
+                    pass  # TODO
             elif event.key == TKey.Key_Left:
-                if cpos > 0:
-                    self.setCursorPosition(cpos - 1)
+                if cursor_x > 0:
+                    self.moveCursorRelative(-1)
                 elif tpos > 0:
-                    self._cursor_offset -= 1
-                    self.setCursorPosition(cpos) # XXX
+                    self._display_offset -= 1
+                    pass  # TODO
             elif event.key == TKey.Key_Right:
-                if cpos < len(self.text()):
-                    self.setCursorPosition(cpos + 1)
+                if cursor_x < len(self.text()):
+                    self.moveCursorRelative(1)
                 elif len(self.text()) > self.width():