Update event propagation, keypress event, focusing.
authorRadek Brich <radek.brich@devl.cz>
Sun, 16 Dec 2012 20:49:54 +0100
changeset 34 e3beacd5e536
parent 33 45f1b6d590bd
child 35 f62c8269ded6
Update event propagation, keypress event, focusing.
demo_checkbox.py
demo_editor.py
tuikit/application.py
tuikit/button.py
tuikit/checkbox.py
tuikit/combobox.py
tuikit/container.py
tuikit/driver_curses.py
tuikit/editbox.py
tuikit/editfield.py
tuikit/emitter.py
tuikit/layout.py
tuikit/menu.py
tuikit/menubar.py
tuikit/pager.py
tuikit/scrollbar.py
tuikit/tableview.py
tuikit/textedit.py
tuikit/treeview.py
tuikit/widget.py
tuikit/window.py
--- a/demo_checkbox.py	Fri Dec 14 10:32:43 2012 +0100
+++ b/demo_checkbox.py	Sun Dec 16 20:49:54 2012 +0100
@@ -10,7 +10,7 @@
 class MyApplication(Application):
     def __init__(self):
         Application.__init__(self)
-        self.top.connect('keypress', self.globalkeypress)
+        self.top.connect('keypress', self.on_keypress)
 
         vert = VerticalLayout(homogeneous=False)
         self.top.layout(vert)
@@ -22,7 +22,7 @@
             cbox = Checkbox('checkbox ' + str(i))
             self.top.add(cbox)
         
-    def globalkeypress(self, keyname, char):
+    def on_keypress(self, keyname, char):
         if keyname == 'escape' or char == 'q':
             self.terminate()
 
--- a/demo_editor.py	Fri Dec 14 10:32:43 2012 +0100
+++ b/demo_editor.py	Sun Dec 16 20:49:54 2012 +0100
@@ -17,7 +17,7 @@
 class MyApplication(Application):
     def __init__(self):
         Application.__init__(self)
-        self.top.connect('keypress', self.globalkeypress)
+        self.top.connect('keypress', self.on_keypress)
 
         #edit = EditField(50, 'DlouhyTest12')
         #self.top.add(edit)
@@ -48,7 +48,7 @@
         self.button.label = 'YES'
 
 
-    def globalkeypress(self, keyname, char):
+    def on_keypress(self, keyname, char):
         if keyname == 'escape':
             self.terminate()
         if keyname == 'f1':
--- a/tuikit/application.py	Fri Dec 14 10:32:43 2012 +0100
+++ b/tuikit/application.py	Sun Dec 16 20:49:54 2012 +0100
@@ -15,25 +15,15 @@
         '''Create top window.'''
         Container.__init__(self)
 
-        self.focuswidget = None
-
         self.top = self
 
         self.timeout = []
         self.timelast = None
 
-        self.connect('draw', self.on_draw)
 
-
-    def keypress(self, keyname, char):
-        if self.handle('keypress', keyname, char):
-            return
-        if self.focuswidget and self.focuswidget != self:
-            self.focuswidget.emit('keypress', keyname, char)
-
-
-    def on_draw(self, screen, x, y):
+    def draw(self, screen, x=0, y=0):
         screen.erase()
+        super().draw(screen, x, y)
 
 
     def add_timeout(self, s, func):
@@ -105,7 +95,7 @@
         '''The main loop.'''
         self.applytheme()
         self.top.size = self.driver.size  # link top widget size to screen size
-        self.top.handle('resize')
+        self.top.emit('resize')
 
         while True:
             self.top.draw(self.driver)
--- a/tuikit/button.py	Fri Dec 14 10:32:43 2012 +0100
+++ b/tuikit/button.py	Sun Dec 16 20:49:54 2012 +0100
@@ -22,6 +22,8 @@
         #: How should label be aligned if button has excess space - center | left | right
         self.align = 'center'
 
+        self.allow_focus = True
+
         self.bg = 'button'
         self.bghi = 'button-active'
         self.highlight = False
@@ -30,15 +32,11 @@
         self.sizereq.w = w
         self.sizereq.h = h
 
-        self.connect('draw', self.on_draw)
-        self.connect('mousedown', self.on_mousedown)
-        self.connect('mouseup', self.on_mouseup)
-        self.connect('keypress', self.on_keypress)
-
         self.add_events('click')
 
 
     def on_draw(self, screen, x, y):
+        super().on_draw(screen, x, y)
         pad = self.width - len(self.label) - len(self.prefix) - len(self.suffix)
         lpad, rpad = self._divide_padding(pad)
         screen.pushcolor(self.getcolor())
@@ -60,21 +58,24 @@
 
 
     def on_mousedown(self, ev):
+        super().on_mousedown(ev)
         self.highlight = True
         self.redraw()
 
 
     def on_mouseup(self, ev):
+        super().on_mouseup(ev)
         self.highlight = False
         self.redraw()
 
         if self.enclose(ev.px, ev.py):
-            self.handle('click')
+            self.emit('click')
 
 
     def on_keypress(self, keyname, char):
+        super().on_keypress(keyname, char)
         if keyname == 'enter':
-            self.handle('click')
+            self.emit('click')
 
 
     def getcolor(self):
--- a/tuikit/checkbox.py	Fri Dec 14 10:32:43 2012 +0100
+++ b/tuikit/checkbox.py	Sun Dec 16 20:49:54 2012 +0100
@@ -4,23 +4,22 @@
 
 
 class Checkbox(Button):
-    
+
     '''Checkbox - button.'''
-    
+
     def __init__(self, label=''):
         Button.__init__(self, label)
-        
+
         self.checked = False
-        
+
         self.prefix = '[ ] '
         self.suffix = ''
         self.align = 'left'
         self.bg = 'normal'
         self.bghi = 'active'
 
-        self.connect('click', self.on_click)
-    
     def on_click(self):
+        super().on_click()
         if self.checked:
             self.checked = False
             self.prefix = '[ ] '
--- a/tuikit/combobox.py	Fri Dec 14 10:32:43 2012 +0100
+++ b/tuikit/combobox.py	Sun Dec 16 20:49:54 2012 +0100
@@ -9,12 +9,14 @@
 class ComboBox(Container):
     def __init__(self, width=15, value='', items=[]):
         Container.__init__(self, width, 1)
-        
+
+        self.allow_focus = True
+
         self.colorprefix = 'combo:'
-        
+
         self._edit = EditField(width - 3, value)
         self.add(self._edit)
-        
+
         self._btn = Button('v')
         self._btn.prefix = ''
         self._btn.suffix = ''
@@ -22,12 +24,12 @@
         self._btn.width = 3
         self._btn.connect('click', self._on_btn_click)
         self.add(self._btn)
-        
+
         self._menu = Menu(items)
         self._menu.hide()
         self._menu.allowlayout = False
         #self.top.add(self._menu)
-    
+
     def _on_btn_click(self):
         pass
 
--- a/tuikit/container.py	Fri Dec 14 10:32:43 2012 +0100
+++ b/tuikit/container.py	Sun Dec 16 20:49:54 2012 +0100
@@ -1,10 +1,10 @@
 # -*- coding: utf-8 -*-
 
-import logging
-
 from tuikit.widget import Widget
 from tuikit.common import Borders
 
+import logging
+
 
 class Container(Widget):
 
@@ -17,8 +17,11 @@
         #: List of child widgets.
         self.children = []
 
+        self.focuschild = None
         self.mousechild = None
 
+        self.allow_focus = True
+
         #: Width of borders (left, top, right, bottom).
         #: Child widgets are placed within borders.
         self.borders = Borders()
@@ -32,8 +35,6 @@
 
         self.trapfocus = False  # if True, tab cycles inside container
 
-        self.connect('resize', self.on_resize)
-
 
     def add(self, widget, **kw):
         '''Add widget into this container.'''
@@ -41,6 +42,8 @@
         widget.parent = self
         widget.settop(self.top)
         widget.hints.update(kw)
+        if self.focuschild is None and widget.canfocus():
+            self.focuschild = widget
 
 
     def layout(self, layout):
@@ -58,39 +61,43 @@
 
 
     def focusnext(self):
-        i = self.children.index(self.top.focuswidget)
-        while True:
-            i += 1
-            if i >= len(self.children):
-                i = 0
-            if self.children[i].canfocus():
-                self.children[i].setfocus()
-                break
-        log = logging.getLogger('tuikit')
-        log.debug(str(self.top.focuswidget.__class__))
+        idx_current = self.children.index(self.focuschild)
+        idx_new = idx_current + 1
+        cycled = False
+        while idx_current != idx_new:
+            if idx_new >= len(self.children):
+                idx_new = 0
+                cycled = True
+            if self.children[idx_new].canfocus():
+                self.children[idx_new].setfocus()
+                return self.trapfocus or not cycled
+            idx_new += 1
 
-
-    def keypress(self, keyname, char):
+    def on_keypress(self, keyname, char):
+        super().on_keypress(keyname, char)
+        if self.focuschild is not None:
+            handled = self.focuschild.emit('keypress', keyname, char)
+            if handled:
+                return True
         if keyname == 'tab':
-            self.focusnext()
-            return
-        Widget.keypress(self, keyname, char)
+            return self.focusnext()
 
 
     def on_resize(self):
+        super().on_resize()
         for child in self.children:
-            child.handle('resize')
+            child.emit('resize')
 
 
-    def draw(self, driver, x=0, y=0):
+    def draw(self, driver, x, y):
         if self.hidden:
-            return
+            return True
 
         driver.clipstack.push(x, y, self.width, self.height)
         if self.colorprefix:
             driver.pushcolorprefix(self.colorprefix)
 
-        Widget.draw(self, driver, x, y)
+        super().draw(driver, x, y)
 
         for child in [x for x in self.children if not x.allowlayout]:
             child.draw(driver, x + child.x, y + child.y)
@@ -108,53 +115,41 @@
         driver.clipstack.pop()
 
 
-    def mousedown(self, ev):
+    def on_mousedown(self, ev):
+        super().on_mousedown(ev)
         handled = False
         for child in reversed(self.children):
             if child.enclose(ev.wx, ev.wy):
                 childev = ev.childevent(child)
-                child.mousedown(childev)
+                child.emit('mousedown', childev)
                 self.mousechild = child
                 handled = True
                 break
-        if not handled:
-            self.setfocus()
-            self.handle('mousedown', ev)
+        return handled
 
-
-    def mouseup(self, ev):
+    def on_mouseup(self, ev):
+        super().on_mouseup(ev)
         if self.mousechild:
             childev = ev.childevent(self.mousechild)
-            self.mousechild.mouseup(childev)
+            self.mousechild.emit('mouseup', childev)
             self.mousechild = None
-        else:
-            self.handle('mouseup', ev)
-        #handled = False
-        #for child in self.children:
-            #if child.enclose(ev.wx, ev.wy):
-                #childev = ev.childevent(child)
-                #child.mouseup(childev)
-                #self.mousechild = child
-                #handled = True
-        #if not handled:
-            #self.handle('mouseup', ev)
+            return True
 
-
-    def mousemove(self, ev):
+    def on_mousemove(self, ev):
+        super().on_mousemove(ev)
         if self.mousechild:
             childev = ev.childevent(self.mousechild)
-            self.mousechild.mousemove(childev)
-        else:
-            self.handle('mousemove', ev)
+            self.mousechild.emit('mousemove', childev)
+            return True
 
-
-    def mousewheel(self, ev):
+    def on_mousewheel(self, ev):
+        super().on_mousewheel(ev)
         handled = False
         for child in reversed(self.children):
             if child.enclose(ev.wx, ev.wy):
                 childev = ev.childevent(child)
-                child.mousewheel(childev)
+                child.emit('mousewheel', childev)
                 handled = True
                 break
-        if not handled:
-            self.handle('mousewheel', ev)
+        return handled
+
--- a/tuikit/driver_curses.py	Fri Dec 14 10:32:43 2012 +0100
+++ b/tuikit/driver_curses.py	Sun Dec 16 20:49:54 2012 +0100
@@ -144,7 +144,7 @@
         else:
             attr = 0
         self.screen.attrset(attr)
-    
+
 
     ## drawing ##
 
@@ -252,7 +252,7 @@
 
             if c == curses.KEY_MOUSE:
                 res += self.process_mouse()
-            
+
             elif c == curses.KEY_RESIZE:
                 self.size.h, self.size.w = self.screen.getmaxyx()
                 res.append(('resize',))
@@ -333,7 +333,7 @@
         consumed.append(c)
 
         while True:
-            self.log.debug('c=%s len=%s', c, len(codes))
+         #   self.log.debug('c=%s len=%s', c, len(codes))
             for code in codes:
                 if c == code[len(consumed)-1]:
                     if len(code) - 1 == len(consumed):
@@ -341,7 +341,7 @@
                     else:
                         matchingcodes += [code]
 
-            self.log.debug('matching=%s', len(matchingcodes))
+         #   self.log.debug('matching=%s', len(matchingcodes))
 
             # match found, or no matching code found -> stop
             if len(matchingcodes) == 0:
--- a/tuikit/editbox.py	Fri Dec 14 10:32:43 2012 +0100
+++ b/tuikit/editbox.py	Sun Dec 16 20:49:54 2012 +0100
@@ -7,6 +7,8 @@
     def __init__(self, width=20, height=20, text=''):
         Widget.__init__(self, width, height)
 
+        self.allow_focus = True
+
         self.xofs = 0
         self.yofs = 0
 
@@ -18,16 +20,13 @@
         self.sline = 0
         self.spos = 0
 
-        self.connect('draw', self.on_draw)
-        self.connect('keypress', self.on_keypress)
-        self.connect('mousewheel', self.on_mousewheel)
-
         self.add_events('scroll', 'areasize')
 
         self.set_text(text)
 
 
     def on_draw(self, screen, x, y):
+        super().on_draw(screen, x, y)
         for j in range(self.height):
             if self.yofs + j >= len(self.lines):
                 break
@@ -38,10 +37,11 @@
                 #line = line[:self.width]
             screen.puts(x, y + j, line)
 
-        screen.showcursor(x + self.get_cpos() - self.xofs, y + self.cline - self.yofs)
+        self.cursor = (self.get_cpos() - self.xofs, self.cline - self.yofs)
 
 
     def on_keypress(self, keyname, char):
+        super().on_keypress(keyname, char)
         if keyname:
             if keyname == 'left':
                 self.move_left()
@@ -87,6 +87,7 @@
 
 
     def on_mousewheel(self, ev):
+        super().on_mousewheel(ev)
         # up
         if ev.button == 4:
             self.move_up()
@@ -98,7 +99,7 @@
 
     def set_text(self, text):
         self.lines = text.split('\n')
-        self.handle('areasize')
+        self.emit('areasize')
 
 
     def get_text(self):
@@ -121,7 +122,7 @@
         if yofs < 0:
             yofs = 0
         self.yofs = yofs
-        self.handle('scroll')
+        self.emit('scroll')
 
 
     def move_left(self):
@@ -206,7 +207,7 @@
         cpos = self.get_cpos()
         self.lines[self.cline] = ln[cpos:]
         self.lines.insert(self.cline, ln[:cpos])
-        self.handle('areasize')
+        self.emit('areasize')
 
 
     def del_char(self):
@@ -216,7 +217,7 @@
             if self.cline + 1 < len(self.lines):
                 self.lines[self.cline] = self.lines[self.cline] + self.lines[self.cline+1]
                 del self.lines[self.cline+1]
-                self.handle('areasize')
+                self.emit('areasize')
         else:
             self.lines[self.cline] = ln[:cpos] + ln[cpos+1:]
 
--- a/tuikit/editfield.py	Fri Dec 14 10:32:43 2012 +0100
+++ b/tuikit/editfield.py	Sun Dec 16 20:49:54 2012 +0100
@@ -9,6 +9,8 @@
     def __init__(self, width=10, value=''):
         Widget.__init__(self, width, 1)
 
+        self.allow_focus = True
+
         self.code = locale.getpreferredencoding()
         if not isinstance(value, str):
             value = str(value, self.code)
--- a/tuikit/emitter.py	Fri Dec 14 10:32:43 2012 +0100
+++ b/tuikit/emitter.py	Sun Dec 16 20:49:54 2012 +0100
@@ -1,5 +1,8 @@
 # -*- coding: utf-8 -*-
 
+import logging
+
+
 class Emitter:
 
     """Event emitter mixin class."""
@@ -16,6 +19,7 @@
             self._event_handlers = dict()
         for event_name in event_names:
             self._event_handlers[event_name] = []
+            setattr(Emitter, 'on_' + event_name, lambda *args, **kwargs: False)
 
     def connect(self, event_name, handler):
         """Connect event handler to event name.
@@ -54,7 +58,7 @@
         else:
             raise KeyError('Unknown event: %s', event_name)
 
-    def handle(self, event_name, *args, **kwargs):
+    def emit(self, event_name, *args, **kwargs):
         """Emit the event.
 
         Call all handlers from event's handler list,
@@ -64,21 +68,15 @@
         False otherwise.
 
         """
-        handled = False
+        logging.getLogger('tuikit').debug('Emit "%s" on %s %s',
+            event_name,
+            self.__class__.__name__,
+            getattr(self, 'name', None) or id(self))
+        handled = getattr(self, 'on_' + event_name)(*args, **kwargs)
+        if handled:
+            return True
         for handler in self._event_handlers[event_name]:
-            res = handler(*args, **kwargs)
-            if res:
-                handled = True
-        return handled
+            handled = handler(*args, **kwargs)
+            if handled:
+                return True
 
-    def emit(self, event, *args, **kwargs):
-        """Emit event.
-
-        This is used by original event source when the event is detected.
-
-        """
-        try:
-            getattr(self, event)(*args, **kwargs)
-        except AttributeError:
-            self.handle(event, *args, **kwargs)
-
--- a/tuikit/layout.py	Fri Dec 14 10:32:43 2012 +0100
+++ b/tuikit/layout.py	Sun Dec 16 20:49:54 2012 +0100
@@ -36,8 +36,8 @@
         # available space
         space1 = c.size[ax1] - b1 - c.borders[ax1+2]
         space2 = c.size[ax2] - b2 - c.borders[ax2+2]
-        
-        # all space minus spacing 
+
+        # all space minus spacing
         space_to_divide = space1 - (len(children) - 1) * self.spacing
         if not self.homogeneous:
             # reduce by space acquired by children
@@ -47,7 +47,7 @@
         else:
             # all children are implicitly expanded
             expanded_num = len(children)
-        
+
         if expanded_num:
             # reserved space for each expanded child
             space_child = space_to_divide / expanded_num
@@ -58,7 +58,7 @@
             child.position[ax2] = b2
             child.size[ax1] = child.sizereq[ax1]
             child.size[ax2] = space2
-            
+
             if child.hint('expand') or self.homogeneous:
                 maxsize = int(round(space_child + math.modf(offset)[0], 2))
                 offset += space_child + self.spacing
@@ -72,8 +72,8 @@
                     child.position[ax1] += int((maxsize - child.size[ax1])/2)
             else:
                 offset += child.size[ax1]
-            
-            child.handle('resize')
+
+            child.emit('resize')
         c.redraw()
 
 
@@ -81,7 +81,7 @@
     def resize(self):
         ax1 = 1 # primary dimension - y
         ax2 = 0 # secondary dimension - x
-        self._resize(ax1, ax2)        
+        self._resize(ax1, ax2)
 
 
 class HorizontalLayout(LinearLayout):
@@ -212,7 +212,7 @@
                     w.y = coly
                     w.width = colw[coln]
                     w.height = rowh
-                    w.handle('resize')
+                    w.emit('resize')
 
                     colspan = col['colspan']
                     if colspan > 1:
--- a/tuikit/menu.py	Fri Dec 14 10:32:43 2012 +0100
+++ b/tuikit/menu.py	Sun Dec 16 20:49:54 2012 +0100
@@ -9,6 +9,8 @@
         self.width = max([len(x[0]) for x in items if x is not None]) + 4
         self.height = len(items) + 2
 
+        self.allow_focus = True
+
         self.bg = 'menu'
         self.highlight = 'menu-active'
         self.items = items
@@ -88,7 +90,7 @@
     def run_selected(self):
         if self.selected and self.selected[1] is not None:
             if isinstance(self.selected[1], str):
-                self.handle('activate', self.selected[1])
+                self.emit('activate', self.selected[1])
             elif isinstance(self.selected[1], Widget):
                 self.selected[1].show()
                 self.selected[1].setfocus()
--- a/tuikit/menubar.py	Fri Dec 14 10:32:43 2012 +0100
+++ b/tuikit/menubar.py	Sun Dec 16 20:49:54 2012 +0100
@@ -1,13 +1,14 @@
 # -*- coding: utf-8 -*-
 
 from tuikit.widget import Widget
-import logging
 
 
 class MenuBar(Widget):
     def __init__(self, items = []):
         Widget.__init__(self, 0, 1)
 
+        self.allow_focus = True
+
         self.bg = 'menu'
         self.highlight = 'menu-active'
 
@@ -87,7 +88,6 @@
 
 
     def on_unfocus(self, newfocus):
-        #logging.getLogger('tuikit').debug('unfocus')
         if self.selected and newfocus == self.selected[1]:
             return
         self.unselect()
--- a/tuikit/pager.py	Fri Dec 14 10:32:43 2012 +0100
+++ b/tuikit/pager.py	Sun Dec 16 20:49:54 2012 +0100
@@ -53,5 +53,5 @@
         child.hidden = False
         self.selected = child
 
-        self.handle('resize')
+        self.emit('resize')
 
--- a/tuikit/scrollbar.py	Fri Dec 14 10:32:43 2012 +0100
+++ b/tuikit/scrollbar.py	Sun Dec 16 20:49:54 2012 +0100
@@ -16,11 +16,6 @@
         self.dragging = False
         self.move = None
 
-        self.connect('draw', self.on_draw)
-        self.connect('mousedown', self.on_mousedown)
-        self.connect('mouseup', self.on_mouseup)
-        self.connect('mousemove', self.on_mousemove)
-
         self.add_events('change')
 
 
@@ -32,6 +27,7 @@
 
 
     def on_draw(self, screen, x, y):
+        super().on_draw(screen, x, y)
         screen.putch(x, y, screen.unigraph.UP_ARROW)
         for i in range(y + 1, y + self.height - 1):
             screen.putch(x, i, screen.unigraph.LIGHT_SHADE)
@@ -40,6 +36,7 @@
 
 
     def on_mousedown(self, ev):
+        super().on_mousedown(ev)
         self.dragging = False
         self.move = None
         # arrow buttons
@@ -48,7 +45,7 @@
                 self.move_up()
             else:
                 self.move_down()
-            self.top.add_timeout(self.interval * 2, self.on_timeout)
+            self.top.add_timeout(self.interval * 2, self._timeout)
             return
         # thumb bar
         if ev.wy == 1 + self.thumbpos:
@@ -57,27 +54,29 @@
 
 
     def on_mouseup(self, ev):
+        super().on_mouseup(ev)
         if self.dragging:
             self.drag(ev.wy)
             self.dragging = False
             return
         if self.move:
-            self.top.remove_timeout(self.on_timeout)
+            self.top.remove_timeout(self._timeout)
             self.move = None
             return
 
 
     def on_mousemove(self, ev):
+        super().on_mousemove(ev)
         if self.dragging:
             self.drag(ev.wy)
 
 
-    def on_timeout(self):
+    def _timeout(self):
         if self.move == 'up':
             self.move_up()
         if self.move == 'down':
             self.move_down()
-        self.top.add_timeout(self.interval, self.on_timeout)
+        self.top.add_timeout(self.interval, self._timeout)
 
 
     def move_up(self):
@@ -85,7 +84,7 @@
             self.setpos(self.pos - 1)
         self.move = 'up'
         self.redraw()
-        self.handle('change')
+        self.emit('change')
 
 
     def move_down(self):
@@ -93,7 +92,7 @@
             self.setpos(self.pos + 1)
         self.move = 'down'
         self.redraw()
-        self.handle('change')
+        self.emit('change')
 
 
     def drag(self, wy):
@@ -105,5 +104,5 @@
         if self.pos != newpos:
             self.setpos(newpos)
             self.redraw()
-            self.handle('change')
+            self.emit('change')
 
--- a/tuikit/tableview.py	Fri Dec 14 10:32:43 2012 +0100
+++ b/tuikit/tableview.py	Sun Dec 16 20:49:54 2012 +0100
@@ -67,6 +67,8 @@
     def __init__(self, model=None, width=20, height=20):
         Widget.__init__(self, width, height)
 
+        self.allow_focus = True
+
         # model
         self._model = None
         self.setmodel(model)
@@ -80,9 +82,6 @@
         self.acell = Coords()
         '''Active cell (cursor).'''
 
-        self.connect('draw', self.on_draw)
-        self.connect('keypress', self.on_keypress)
-
         self.add_events('scroll', 'areasize')
 
     def getmodel(self):
@@ -158,6 +157,7 @@
             x += col.size + self.spacing
 
     def on_draw(self, screen, x, y):
+        super().on_draw(screen, x, y)
         screen.pushcolor('normal')
         self.rowcount = self.model.getcount()
         numrows = min(self.rowcount - self.offset.y, self.size.h - self.headsize)
@@ -174,6 +174,7 @@
         screen.popcolor()
 
     def on_keypress(self, keyname, char):
+        super().on_keypress(keyname, char)
         if keyname:
             if keyname == 'up':  self.move_up()
             if keyname == 'down':  self.move_down()
@@ -189,7 +190,7 @@
         if yofs < 0:
             yofs = 0
         self.offset.y = yofs
-        self.handle('scroll')
+        self.emit('scroll')
 
     def move_up(self):
         if self.acell.y > 0:
--- a/tuikit/textedit.py	Fri Dec 14 10:32:43 2012 +0100
+++ b/tuikit/textedit.py	Sun Dec 16 20:49:54 2012 +0100
@@ -24,9 +24,6 @@
 
         self.on_editbox_areasize()
 
-        self.connect('draw', self.on_draw)
-
-
     def settext(self, text):
         self.editbox.set_text(text)
 
@@ -36,6 +33,7 @@
 
 
     def on_draw(self, screen, x, y):
+        super().on_draw(screen, x, y)
         screen.frame(x, y, self.width, self.height)
 
 
@@ -51,6 +49,6 @@
 
 
     def on_vscroll_change(self):
-        self.editbox.yofs = self.vscroll.pos
+        self.editbox.set_yofs(self.vscroll.pos)
         self.editbox.redraw()
 
--- a/tuikit/treeview.py	Fri Dec 14 10:32:43 2012 +0100
+++ b/tuikit/treeview.py	Sun Dec 16 20:49:54 2012 +0100
@@ -3,6 +3,8 @@
 from tuikit.emitter import Emitter
 from tuikit.widget import Widget
 
+import logging
+
 
 class TreeIter:
     def __init__(self, root, collapsed=[]):
@@ -93,13 +95,15 @@
         for name in names:
             node.append(TreeNode(node, name))
 
-        self.handle('change')
+        self.emit('change')
 
 
 class TreeView(Widget):
     def __init__(self, model=None, width=20, height=20):
         Widget.__init__(self, width, height)
 
+        self.allow_focus = True
+
         # cursor
         self.cnode = None
 
@@ -109,9 +113,6 @@
 
         self.collapsed = []
 
-        self.connect('draw', self.on_draw)
-        self.connect('keypress', self.on_keypress)
-
     def __iter__(self):
         return TreeIter(self._model.root, self.collapsed)
 
@@ -155,6 +156,7 @@
                 pass
 
     def on_draw(self, screen, x, y):
+        super().on_draw(screen, x, y)
         screen.pushcolor('normal')
 
         lines = 0  # bit array, bit 0 - draw vertical line on first column, etc.
@@ -190,6 +192,7 @@
         screen.popcolor()
 
     def on_keypress(self, keyname, char):
+        super().on_keypress(keyname, char)
         if keyname:
             if keyname == 'up':    self.move_up()
             if keyname == 'down':  self.move_down()
--- a/tuikit/widget.py	Fri Dec 14 10:32:43 2012 +0100
+++ b/tuikit/widget.py	Sun Dec 16 20:49:54 2012 +0100
@@ -1,7 +1,5 @@
 # -*- coding: utf-8 -*-
 
-import logging
-
 from tuikit.emitter import Emitter
 from tuikit.common import Coords, Size
 
@@ -11,6 +9,9 @@
     '''Base class for all widgets.'''
 
     def __init__(self, width = 10, height = 10):
+        #: Widget name is used for logging etc. Not visible anywhere.
+        self.name = None
+
         #: Parent widget.
         self.parent = None
 
@@ -39,6 +40,9 @@
         #: When false, the widget is not considered in layout.
         self.allowlayout = True
 
+        #: Allow keyboard focus for this widget.
+        self.allow_focus = False
+
         #: Dictionary containing optional parameters for layout managers etc.
         self.hints = {}
 
@@ -62,7 +66,6 @@
             'mouseup',
             'mousemove',
             'mousewheel')
-        self.connect('resize', self.on_resize)
 
 
     @property
@@ -72,7 +75,6 @@
     @x.setter
     def x(self, value):
         self.position.x = value
-        #self.handle('resize')
 
     @property
     def y(self):
@@ -81,7 +83,6 @@
     @y.setter
     def y(self, value):
         self.position.y = value
-        #self.handle('resize')
 
 
     @property
@@ -91,7 +92,7 @@
     @width.setter
     def width(self, value):
         self.size.w = value
-        self.handle('resize')
+        self.emit('resize')
 
     @property
     def height(self):
@@ -100,7 +101,7 @@
     @height.setter
     def height(self, value):
         self.size.h = value
-        self.handle('resize')
+        self.emit('resize')
 
 
     def settop(self, top):
@@ -109,92 +110,52 @@
 
     ### events
 
-
-    def on_resize(self):
-        log = logging.getLogger('tuikit')
-        log.debug('%r: resize', self)
-
-
     def redraw(self, parent=True):
         self._redraw = True
         if parent and self.parent:
             self.parent._redraw = True
 
-
-    def draw(self, screen, x=0, y=0):
+    def draw(self, driver, x, y):
         if self.hidden:
-            return
+            return True
 
-        self.handle('draw', screen, x, y)
+        self.emit('draw', driver, x, y)
 
         if self.hasfocus():
             if self.cursor:
                 cx, cy = self.cursor
-                screen.showcursor(x + cx, y + cy)
+                driver.showcursor(x + cx, y + cy)
             else:
-                screen.hidecursor()
-
+                driver.hidecursor()
 
-    def keypress(self, keyname, char):
-        handled = self.handle('keypress', keyname, char)
-        if not handled and self.parent and self.parent != self.top:
-            self.parent.emit('keypress', keyname, char)
-
-
-    def mousedown(self, ev):
+    def on_mousedown(self, ev):
+        super().on_mousedown(ev)
         self.setfocus()
-        self.handle('mousedown', ev)
-
-
-    def mouseup(self, ev):
-        self.handle('mouseup', ev)
-
-
-    def mousemove(self, ev):
-        self.handle('mousemove', ev)
-
-
-    def mousewheel(self, ev):
-        self.handle('mousewheel', ev)
 
 
     ### focus
 
 
     def canfocus(self):
-        return self.is_connected('keypress')
+        return not self.hidden and self.allow_focus
 
 
     def hasfocus(self):
-        return self.top.focuswidget == self
+        if self.parent is None:
+            return True
+        return (self.parent.hasfocus() \
+            and self.parent.focuschild == self)
 
 
     def setfocus(self):
         if self.hasfocus() or not self.canfocus():
             return
-        if self.top.focuswidget:
-            self.top.focuswidget.resetfocus()
-        self.top.focuswidget = self
+        if self.parent.focuschild:
+            self.parent.focuschild.emit('unfocus', self)
+        self.parent.focuschild = self
         self.emit('focus')
 
 
-    def resetfocus(self):
-        if self.top.focuswidget != self:
-            return
-        self.top.focuswidget = None
-        self.emit('unfocus')
-
-
-    def focus(self):
-        '''handle focus event'''
-        self.handle('focus')
-
-
-    def unfocus(self, newfocus=None):
-        '''handle unfocus event'''
-        self.handle('unfocus', newfocus)
-
-
     ###
 
     def hint(self, key):
--- a/tuikit/window.py	Fri Dec 14 10:32:43 2012 +0100
+++ b/tuikit/window.py	Sun Dec 16 20:49:54 2012 +0100
@@ -3,6 +3,8 @@
 from tuikit.container import Container
 from tuikit.button import Button
 
+import logging
+
 
 class Window(Container):
 
@@ -15,12 +17,6 @@
         '''Create window of requested size.'''
         Container.__init__(self, width, height)
 
-        self.connect('draw', self.on_draw)
-        self.connect('mousedown', self.on_mousedown)
-        self.connect('mouseup', self.on_mouseup)
-        self.connect('mousemove', self.on_mousemove)
-        self.connect('resize', self.on_resize)
-
         #: Window title.
         self.title = ''
 
@@ -60,6 +56,7 @@
 
 
     def on_draw(self, screen, x, y):
+        super().on_draw(screen, x, y)
         screen.pushcolor('normal')
         screen.frame(x, y, self.width, self.height)
 
@@ -79,6 +76,10 @@
 
 
     def on_mousedown(self, ev):
+        handled = super().on_mousedown(ev)
+        if handled:
+            return True
+
         self.dragstart = (ev.wx, ev.wy)
         if self.resizable and ev.wx >= self.width - 1 and ev.wy >= self.height - 1:
             self.resizing = True
@@ -90,11 +91,15 @@
 
 
     def on_mouseup(self, ev):
+        handled = super().on_mouseup(ev)
+        if handled:
+            return True
+
         if self.resizing:
             self.width = self.origsize[0] + ev.wx - self.dragstart[0]
             self.height = self.origsize[1] + ev.wy - self.dragstart[1]
             self.resizing = False
-            self.handle('resize')
+            self.emit('resize')
         elif self.moving:
             self.x = ev.px - self.dragstart[0]
             self.y = ev.py - self.dragstart[1]
@@ -104,6 +109,10 @@
 
 
     def on_mousemove(self, ev):
+        handled = super().on_mousemove(ev)
+        if handled:
+            return True
+
         if ev.px == self.x + self.dragstart[0] \
         and ev.py == self.y + self.dragstart[1]:
             return
@@ -114,7 +123,7 @@
         if self.resizing:
             self.width = self.origsize[0] + ev.wx - self.dragstart[0]
             self.height = self.origsize[1] + ev.wy - self.dragstart[1]
-            self.handle('resize')
+            self.emit('resize')
         elif self.moving:
             self.x = ev.px - self.dragstart[0]
             self.y = ev.py - self.dragstart[1]
@@ -123,6 +132,7 @@
 
 
     def on_resize(self):
+        super().on_resize()
         self.closebtn.x = self.width - 5