Use Point for mouse events, add them to Container and Widget.
authorRadek Brich <radek.brich@devl.cz>
Wed, 03 Sep 2014 08:53:44 +0200
changeset 106 abcadb7e2ef1
parent 105 70c9cfc70cd6
child 107 1822c37b2688
Use Point for mouse events, add them to Container and Widget.
demos/04_texteditor.py
tuikit/core/container.py
tuikit/core/coords.py
tuikit/core/widget.py
tuikit/driver/curses.py
--- a/demos/04_texteditor.py	Mon Sep 01 08:55:40 2014 +0200
+++ b/demos/04_texteditor.py	Wed Sep 03 08:53:44 2014 +0200
@@ -8,6 +8,7 @@
 
 
 class MyApplication(Application):
+
     def __init__(self):
         Application.__init__(self)
         #self.top.add_handler('keypress', self.on_top_keypress)
--- a/tuikit/core/container.py	Mon Sep 01 08:55:40 2014 +0200
+++ b/tuikit/core/container.py	Wed Sep 03 08:53:44 2014 +0200
@@ -16,6 +16,7 @@
         #: List of child widgets.
         self.children = []
         self.focus_child = None
+        self.mouse_child = None
         self.layout = layout_class()
 
     def add(self, widget):
@@ -67,3 +68,20 @@
     def keypress(self, keyname, char, mod=0):
         if self.focus_child:
             self.focus_child.keypress(keyname, char, mod)
+
+    def mousedown(self, button, pos):
+        self.mouse_child = None
+        for child in reversed(self.children):
+            if pos in child.boundaries:
+                child.mousedown(button, pos - child.pos)
+                self.mouse_child = child
+
+    def mouseup(self, button, pos):
+        if self.mouse_child:
+            self.mouse_child.mouseup(button, pos - self.mouse_child.pos)
+
+    def mousemove(self, button, pos, relpos):
+        if self.mouse_child:
+            self.mouse_child.mousemove(button,
+                pos - self.mouse_child.pos, relpos)
+
--- a/tuikit/core/coords.py	Mon Sep 01 08:55:40 2014 +0200
+++ b/tuikit/core/coords.py	Wed Sep 03 08:53:44 2014 +0200
@@ -1,3 +1,4 @@
+
 class Point:
 
     """Point in cartesian space.
@@ -11,12 +12,6 @@
         self.y = 0
         self.update(*args, **kwargs)
 
-    def __getitem__(self, key):
-        return (self.x, self.y)[key]
-
-    def __repr__(self):
-        return 'Point(x={0.x},y={0.y})'.format(self)
-
     def move(self, relx, rely):
         self.x += relx
         self.y += rely
@@ -46,6 +41,48 @@
             else:
                 raise ValueError('Bad keyword arg: %r' % key)
 
+    # sequence interface
+
+    def __len__(self):
+        return 2
+
+    def __getitem__(self, key):
+        return (self.x, self.y)[key]
+
+    # point arithmetics
+
+    def __add__(self, other):
+        return Point(self.x + other[0], self.y + other[1])
+
+    def __sub__(self, other):
+        return Point(self.x - other[0], self.y - other[1])
+
+    def __eq__(self, other):
+        """Comparison operator.
+
+        Point can be compared to any sequence of at least two elements:
+
+        >>> p = Point(1, 2)
+        >>> p == Point(1, 2)
+        True
+        >>> p == (1, 2)
+        True
+        >>> p == (0, 0)
+        False
+        >>> p == None
+        False
+
+        """
+        try:
+            return self.x == other[0] and self.y == other[1]
+        except (TypeError, IndexError):
+            return False
+
+    # string representation
+
+    def __repr__(self):
+        return 'Point(x={0.x},y={0.y})'.format(self)
+
 
 class Size:
 
--- a/tuikit/core/widget.py	Mon Sep 01 08:55:40 2014 +0200
+++ b/tuikit/core/widget.py	Wed Sep 03 08:53:44 2014 +0200
@@ -70,6 +70,10 @@
     def resize(self, w, h):
         self._size.update(w, h)
 
+    @property
+    def boundaries(self):
+        return Rect._make(self.pos, self._size)
+
     ## drawing, looks ##
 
     def draw(self, buffer):
@@ -105,6 +109,18 @@
         self._log.debug('keypress(keyname=%r, char=%r, mod=%r)',
                         keyname, char, mod)
 
+    def mousedown(self, button, pos):
+        self._log.debug('mousedown(btn=%r, pos=%r)',
+                        button, pos)
+
+    def mouseup(self, button, pos):
+        self._log.debug('mouseup(btn=%r, pos=%r)',
+                        button, pos)
+
+    def mousemove(self, button, pos, relpos):
+        self._log.debug('mousemove(btn=%r, pos=%r, relpos=%r)',
+                        button, pos, relpos)
+
     ## timeouts ##
 
     def add_timeout(self, delay, callback, *args):
--- a/tuikit/driver/curses.py	Mon Sep 01 08:55:40 2014 +0200
+++ b/tuikit/driver/curses.py	Wed Sep 03 08:53:44 2014 +0200
@@ -3,6 +3,7 @@
 import logging
 
 from tuikit.driver.driver import Driver
+from tuikit.core.coords import Point
 
 
 class CursesDriver(Driver):
@@ -87,7 +88,7 @@
         self.colorstack = [] # pushcolor/popcolor puts or gets attributes from this
         self.inputqueue = []
         self.mbtnstack = []
-        self._mouse_last_pos = (None, None)
+        self._mouse_last_pos = None  # Point
         self._mouse_last_bstate = None
 
     ## initialization, finalization ##
@@ -279,15 +280,15 @@
         except curses.error:
             return []
 
+        pos = Point(x, y)
         out = []
 
         if bstate & curses.REPORT_MOUSE_POSITION:
-            if self._mouse_last_pos != (x, y):
-                if self._mouse_last_pos[0] is not None:
-                    relx = x - (self._mouse_last_pos[0] or 0)
-                    rely = y - (self._mouse_last_pos[1] or 0)
-                    out += [('mousemove', 0, x, y, relx, rely)]
-                self._mouse_last_pos = (x, y)
+            if self._mouse_last_pos != pos:
+                if self._mouse_last_pos:
+                    relpos = pos - self._mouse_last_pos
+                    out += [('mousemove', 0, pos, relpos)]
+                self._mouse_last_pos = pos
 
         # we are interested only in changes, not buttons already pressed before event
         if self._mouse_last_bstate is not None:
@@ -299,21 +300,21 @@
             self._mouse_last_bstate = bstate
 
         if bstate & curses.BUTTON1_PRESSED:
-            out += [('mousedown', 1, x, y)]
+            out += [('mousedown', 1, pos)]
         if bstate & curses.BUTTON2_PRESSED:
-            out += [('mousedown', 2, x, y)]
+            out += [('mousedown', 2, pos)]
         if bstate & curses.BUTTON3_PRESSED:
-            out += [('mousedown', 3, x, y)]
+            out += [('mousedown', 3, pos)]
         if bstate & curses.BUTTON1_RELEASED:
-            out += [('mouseup', 1, x, y)]
+            out += [('mouseup', 1, pos)]
         if bstate & curses.BUTTON2_RELEASED:
-            out += [('mouseup', 2, x, y)]
+            out += [('mouseup', 2, pos)]
         if bstate & curses.BUTTON3_RELEASED:
-            out += [('mouseup', 3, x, y)]
+            out += [('mouseup', 3, pos)]
 
         # reset last pos when pressed/released
         if len(out) > 0 and out[-1][0] in ('mousedown', 'mouseup'):
-            self._mouse_last_pos = (None, None)
+            self._mouse_last_pos = None
 
         return out
 
@@ -394,6 +395,7 @@
         t = self._inputqueue_get_wait()
         x = self._inputqueue_get_wait() - 0x21
         y = self._inputqueue_get_wait() - 0x21
+        pos = Point(x, y)
 
         out = []
 
@@ -402,25 +404,24 @@
             btn = t - 0x1f
             if not btn in self.mbtnstack:
                 self.mbtnstack.append(btn)
-                self._mouse_last_pos = (None, None)
-                out += [('mousedown', btn, x, y)]
+                self._mouse_last_pos = None
+                out += [('mousedown', btn, pos)]
             else:
                 # mouse move
-                if self._mouse_last_pos != (x, y):
-                    if self._mouse_last_pos[0] is not None:
-                        relx = x - self._mouse_last_pos[0]
-                        rely = y - self._mouse_last_pos[1]
-                        out += [('mousemove', btn, x, y, relx, rely)]
-                    self._mouse_last_pos = (x, y)
+                if self._mouse_last_pos != pos:
+                    if self._mouse_last_pos:
+                        relpos = pos - self._mouse_last_pos
+                        out += [('mousemove', btn, pos, relpos)]
+                    self._mouse_last_pos = pos
         elif t == 0x23:
             # button release
             btn = self.mbtnstack.pop()
             self._mouse_last_pos = (None, None)
-            out += [('mouseup', btn, x, y)]
+            out += [('mouseup', btn, pos)]
         elif t in (0x60, 0x61):
             # wheel up, down
             btn = 4 + t - 0x60
-            out += [('mousewheel', btn, x, y)]
+            out += [('mousewheel', btn, pos)]
         else:
             raise Exception('Unknown mouse event: %x' % t)