Refactor events.
authorRadek Brich <radek.brich@devl.cz>
Wed, 03 Sep 2014 19:14:43 +0200
changeset 111 b055add74b18
parent 110 cf3d49cdd6e2
child 112 ce2e67e7bbb8
Refactor events.
demos/03_application.py
tests/test_event_class.py
tuikit/core/application.py
tuikit/core/container.py
tuikit/core/events.py
tuikit/core/widget.py
tuikit/core/window.py
tuikit/driver/cursesw.py
tuikit/widgets/textbox.py
tuikit/widgets/textfield.py
--- a/demos/03_application.py	Wed Sep 03 19:13:37 2014 +0200
+++ b/demos/03_application.py	Wed Sep 03 19:14:43 2014 +0200
@@ -25,8 +25,8 @@
 app.root_window.add(field)
 app.root_window.focus_widget = field
 
-def on_keypress(keyname, char, mod):
-    if keyname == 'escape':
+def on_keypress(ev):
+    if ev.keyname == 'escape':
         app.stop()
 
 app.window_manager.sig_keypress.connect(on_keypress)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test_event_class.py	Wed Sep 03 19:14:43 2014 +0200
@@ -0,0 +1,13 @@
+#!/usr/bin/env python3
+
+import sys
+sys.path.append('..')
+
+from tuikit.core.events import KeypressEvent
+
+x = KeypressEvent('tab', '\t', set(['shift', 'ctrl']))
+print(repr(x))
+print(x.mod_key())
+
+x = KeypressEvent(None, 'c', set(['shift', 'ctrl']))
+print(x.mod_key())
--- a/tuikit/core/application.py	Wed Sep 03 19:13:37 2014 +0200
+++ b/tuikit/core/application.py	Wed Sep 03 19:14:43 2014 +0200
@@ -2,6 +2,7 @@
 from tuikit.core.theme import default_theme
 from tuikit.core.timer import Timer
 from tuikit.core.buffer import ProxyBuffer
+from tuikit.core.events import ResizeEvent
 
 import logging
 
@@ -56,7 +57,7 @@
     def main_loop(self):
         """The main loop."""
         self._started = True
-        self.window_manager.handle_event('resize', *self.driver.size)
+        self.window_manager.handle_event(ResizeEvent(*self.driver.size))
 
         screen = ProxyBuffer(self.driver)
         while not self._quit:
@@ -69,7 +70,7 @@
 
             self.timer.process_timeouts()
             for event in events:
-                self.window_manager.handle_event(event[0], *event[1:])
+                self.window_manager.handle_event(event)
 
         self._started = False
         self.log.info('=== End ===')
--- a/tuikit/core/container.py	Wed Sep 03 19:13:37 2014 +0200
+++ b/tuikit/core/container.py	Wed Sep 03 19:14:43 2014 +0200
@@ -61,17 +61,17 @@
 
     ## input events ##
 
-    def keypress(self, keyname, char, mod):
+    def keypress_event(self, ev):
         # First, handle the keypress event to focused child widget
         if self.focus_widget is not None:
-            if self.focus_widget.keypress(keyname, char, mod):
+            if self.focus_widget.keypress_event(ev):
                 return True
         # Next, handle default key behaviour by Container
-        if keyname == 'tab':
-            return self.focus_next(-1 if 'shift' in mod else 1)
+        if ev.keyname == 'tab':
+            return self.focus_next(-1 if 'shift' in ev.mods else 1)
         # Finally, handle default keys by Widget
         # and send keypress signal
-        if Widget.keypress(self, keyname, char, mod):
+        if Widget.keypress_event(self, ev):
             return True
 
     ## focus ##
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tuikit/core/events.py	Wed Sep 03 19:14:43 2014 +0200
@@ -0,0 +1,53 @@
+from collections import OrderedDict
+
+
+class Event:
+
+    """Base class for events."""
+
+    def __init__(self, event_name=None, arg_names=(), arg_values=()):
+        self.name = event_name
+        self.args = OrderedDict(zip(arg_names, arg_values))
+
+    def __getattr__(self, item):
+        if item in self.args:
+            return self.args[item]
+        else:
+            raise AttributeError(item)
+
+    def __getitem__(self, key):
+        return self.args[key]
+
+    def __repr__(self):
+        return '{}({})'.format(self.__class__.__name__,
+                               ', '.join("%s=%r" % (k,v) for k,v
+                                         in self.args.items()))
+
+
+class ResizeEvent(Event):
+
+    def __init__(self, w, h):
+        Event.__init__(self, 'resize', ('w', 'h'), (w, h))
+
+
+class KeypressEvent(Event):
+
+    def __init__(self, keyname, char, mods):
+        Event.__init__(self, 'keypress',
+                       ('keyname', 'char', 'mods'),
+                       (keyname, char, mods))
+
+    def mod_key(self, sep='+'):
+        """Return combined key with modifiers.
+
+        E.g. "shift+tab"
+
+        Order of modifiers is fixed: ctrl, alt, meta, shift
+
+        """
+        res = []
+        for mod in ('ctrl', 'alt', 'meta', 'shift'):
+            if mod in self.mods:
+                res.append(mod)
+        res.append(self.keyname or self.char)
+        return sep.join(res)
--- a/tuikit/core/widget.py	Wed Sep 03 19:13:37 2014 +0200
+++ b/tuikit/core/widget.py	Wed Sep 03 19:14:43 2014 +0200
@@ -103,9 +103,12 @@
         if self._cursor is not None:
             return Point(self._cursor)
 
-    ## input events ##
+    ## events ##
 
-    def keypress(self, keyname, char, mod):
+    def resize_event(self, ev):
+        self.resize(ev.w, ev.h)
+
+    def keypress_event(self, ev):
         """Keypress event handler.
 
         Override to accept keyboard input.
@@ -116,9 +119,9 @@
         if it does not consume the event.
 
         """
-        if self.sig_keypress(keyname, char, mod):
+        if self.sig_keypress(ev):
             return True
-        self.log.debug('Not consumed: keypress(%r, %r, %r)', keyname, char, mod)
+        self.log.debug('Not consumed: %s', ev)
 
     ## timeouts ##
 
--- a/tuikit/core/window.py	Wed Sep 03 19:13:37 2014 +0200
+++ b/tuikit/core/window.py	Wed Sep 03 19:14:43 2014 +0200
@@ -66,17 +66,17 @@
     def resize(self, w, h):
         self._widgets[0].resize(w, h)
 
-    def keypress(self, keyname, char, mod):
-        self.log.debug('keypress(%r, %r, %r)', keyname, char, mod)
-        return Container.keypress(self, keyname, char, mod)
+    def keypress_event(self, ev):
+        self.log.debug('%s', ev)
+        return Container.keypress_event(self, ev)
 
-    def handle_event(self, event_name, *args):
+    def handle_event(self, event):
         """Handle input event to managed windows."""
-        handler = getattr(self, event_name, None)
+        handler = getattr(self, event.name + '_event', None)
         if handler:
-            handler(*args)
+            handler(event)
         else:
-            raise Exception('Unknown event: %r %r' % (event_name, args))
+            raise Exception('Unknown event: %r' % event)
 
     def get_focused_widget(self):
         """Traverse the widget hierarchy to bottom
--- a/tuikit/driver/cursesw.py	Wed Sep 03 19:13:37 2014 +0200
+++ b/tuikit/driver/cursesw.py	Wed Sep 03 19:14:43 2014 +0200
@@ -3,6 +3,7 @@
 import logging
 
 from tuikit.driver.driver import Driver
+from tuikit.core.events import ResizeEvent, KeypressEvent
 
 
 class CursesWDriver(Driver):
@@ -187,13 +188,13 @@
             res += self._process_mouse()
         elif c == curses.KEY_RESIZE:
             self.size.h, self.size.w = self.stdscr.getmaxyx()
-            res += [('resize', self.size.w, self.size.h)]
+            res.append(ResizeEvent(self.size.w, self.size.h))
         elif isinstance(c, int):
-            keyname, mod = self._split_keyname_mod(self.key_map[c])
-            res += [('keypress', keyname, None, mod)]
+            keyname, mods = self._split_keyname_mods(self.key_map[c])
+            res.append(KeypressEvent(keyname, None, mods))
         else:
             keyname = self.key_names.get(c)
-            res += [('keypress', keyname, c, set())]
+            res.append(KeypressEvent(keyname, c, set()))
 
         return res
 
@@ -240,7 +241,7 @@
 
         return out
 
-    def _split_keyname_mod(self, keyname):
+    def _split_keyname_mods(self, keyname):
         """Parse keynames in form "shift+tab", return (keyname, mod)."""
         mod_set = set()
         if '+' in keyname:
--- a/tuikit/widgets/textbox.py	Wed Sep 03 19:13:37 2014 +0200
+++ b/tuikit/widgets/textbox.py	Wed Sep 03 19:14:43 2014 +0200
@@ -60,28 +60,38 @@
                 buffer.puts(line, 0, j)
             #self.cursor = (self._spot.x, self._spot.y)
 
-    def keypress(self, keyname, char, mod=0):
-        if keyname:
-            if keyname == 'left':        self.move_left()
-            if keyname == 'right':       self.move_right()
-            if keyname == 'home':        self.move_home()
-            if keyname == 'end':         self.move_end()
-            if keyname == 'up':          self.move_up()
-            if keyname == 'down':        self.move_down()
-            if keyname == 'pageup':      self.move_pageup()
-            if keyname == 'pagedown':    self.move_pagedown()
-            if keyname == 'backspace':   self.backspace()
-            if keyname == 'delete':      self.del_char()
-            if keyname == 'enter':       self.add_newline(move=True)
-            if mod == MOD_CTRL:
-                if keyname == 'home':    self.move_top()
-                if keyname == 'end':     self.move_bottom()
+    def keypress_event(self, ev):
+        if ev.keyname and not ev.mods:
+            consumed = True
+            if   ev.keyname == 'left':        self.move_left()
+            elif ev.keyname == 'right':       self.move_right()
+            elif ev.keyname == 'home':        self.move_home()
+            elif ev.keyname == 'end':         self.move_end()
+            elif ev.keyname == 'up':          self.move_up()
+            elif ev.keyname == 'down':        self.move_down()
+            elif ev.keyname == 'pageup':      self.move_pageup()
+            elif ev.keyname == 'pagedown':    self.move_pagedown()
+            elif ev.keyname == 'backspace':   self.backspace()
+            elif ev.keyname == 'delete':      self.del_char()
+            elif ev.keyname == 'enter':       self.add_newline(move=True)
+            else:
+                consumed = False
+            if consumed:
+                return True
+        if ev.mods:
+            consumed = True
+            mk = ev.mod_key()
+            if   mk == 'ctrl+home':    self.move_top()
+            elif mk == 'ctrl+end':     self.move_bottom()
+            else:
+                consumed = False
+            if consumed:
+                return True
 
-        if char:
-            self.add_char(char)
+        if ev.char and not ev.keyname:
+            self.add_char(ev.char)
             self.move_right()
-
-        #self.redraw()
+            return True
 
     def on_mousedown(self, ev):
         y = ev.wy
--- a/tuikit/widgets/textfield.py	Wed Sep 03 19:13:37 2014 +0200
+++ b/tuikit/widgets/textfield.py	Wed Sep 03 19:14:43 2014 +0200
@@ -45,30 +45,30 @@
 
             self._cursor = (1 + self.curspos - self.ofs, 0)
 
-    def keypress(self, keyname, char, mod=0):
+    def keypress_event(self, ev):
         consumed = True
-        if keyname == 'left':
+        if ev.keyname == 'left':
             self.move_left()
-        elif keyname == 'right':
+        elif ev.keyname == 'right':
             self.move_right()
-        elif keyname == 'backspace':
+        elif ev.keyname == 'backspace':
             if self.curspos > 0:
                 self.move_left()
                 self.del_char()
-        elif keyname == 'delete':
+        elif ev.keyname == 'delete':
             self.del_char()
         else:
             consumed = False
 
-        if not keyname and char:
-            self.add_char(char)
+        if not ev.keyname and ev.char:
+            self.add_char(ev.char)
             self.move_right()
             consumed = True
 
         if consumed:
             #self.redraw()
             return True
-        Widget.keypress(self, keyname, char, mod)
+        Widget.keypress_event(self, ev)
 
     def move_left(self):
         if self.curspos - self.ofs > 1 or (self.ofs == 0 and self.curspos == 1):