Add origin to Buffer. Use it to simplify hierarchical drawing.
authorRadek Brich <radek.brich@devl.cz>
Fri, 28 Mar 2014 10:44:29 +0100
changeset 94 e50dae408fe9
parent 93 c1e79acb9fcb
child 95 05392e369ede
Add origin to Buffer. Use it to simplify hierarchical drawing.
tuikit/core/buffer.py
tuikit/core/container.py
tuikit/core/coords.py
tuikit/core/widget.py
tuikit/core/window.py
tuikit/widgets/button.py
tuikit/widgets/label.py
--- a/tuikit/core/buffer.py	Thu Mar 27 08:03:51 2014 +0100
+++ b/tuikit/core/buffer.py	Fri Mar 28 10:44:29 2014 +0100
@@ -1,5 +1,5 @@
 from tuikit.core.signal import Signal
-from tuikit.core.coords import Size, Rect
+from tuikit.core.coords import Point, Size, Rect
 from tuikit.core.theme import default_theme
 
 from contextlib import contextmanager
@@ -93,6 +93,7 @@
 
     @property
     def clip_rect(self):
+        """Return clipping rectangle in buffer coordinates."""
         try:
             return self._clip_rect
         except AttributeError:
@@ -125,7 +126,42 @@
             self.clip_rect = original_rect
 
 
-class Buffer(BufferOperationsMixin, BufferClippingMixin):
+class BufferOriginMixin:
+
+    @property
+    def origin(self):
+        """Origin coordinates for `putch`.
+
+        Drawing is relative to origin.
+
+        """
+        try:
+            return self._origin
+        except AttributeError:
+            self._origin = Point(0, 0)
+            return self._origin
+
+    @origin.setter
+    def origin(self, value):
+        self.origin.update(value)
+
+    def reset_origin(self):
+        self.origin.update(0, 0)
+
+    def move_origin(self, relx, rely):
+        self.origin.move(relx, rely)
+
+    @contextmanager
+    def moved_origin(self, relx, rely):
+        ox, oy = self.origin
+        try:
+            self.origin.move(relx, rely)
+            yield
+        finally:
+            self.origin.update(ox, oy)
+
+
+class Buffer(BufferOperationsMixin, BufferClippingMixin, BufferOriginMixin):
 
     """Rectangular character buffer.
 
@@ -145,6 +181,7 @@
 
     @property
     def size(self):
+        """Width and height of buffer, in characters."""
         return self._size.readonly()
 
     def resize(self, w, h):
@@ -182,12 +219,18 @@
         self._current_attr = attr
 
     def putch(self, x, y, ch):
-        """Set character on xy coords to c."""
+        """Set character on `x`, `y` coords to `ch`.
+
+        Coords are adjusted by origin.
+
+        """
+        x += self.origin.x
+        y += self.origin.y
         if self.testxy(x, y):
             self._data[y * self._size.w + x].set(ch, self._current_attr)
 
 
-class ProxyBuffer(BufferOperationsMixin, BufferClippingMixin):
+class ProxyBuffer(BufferOperationsMixin, BufferClippingMixin, BufferOriginMixin):
 
     """Special buffer which proxies the operations
     to another buffer or buffer-like class."""
@@ -223,7 +266,13 @@
         self.target.setattr(attr_desc)
 
     def putch(self, x, y, ch):
-        """Set character on xy coords to c."""
+        """Set character on `x`, `y` coords to `ch`.
+
+        Coords are adjusted by origin.
+
+        """
+        x += self.origin.x
+        y += self.origin.y
         if self.testxy(x, y):
             self.target.putch(x, y, ch)
 
--- a/tuikit/core/container.py	Thu Mar 27 08:03:51 2014 +0100
+++ b/tuikit/core/container.py	Fri Mar 28 10:44:29 2014 +0100
@@ -28,13 +28,14 @@
         Widget.resize(self, w, h)
         self.layout.resize()
 
-    def draw(self, buffer, x=0, y=0):
+    def draw(self, buffer):
         """Draw child widgets."""
+        Widget.draw(self, buffer)
         for child in self.children:
-            cx = x + child.x
-            cy = y + child.y
-            with buffer.clip(cx, cy, child.width, child.height):
-                child.draw(buffer, cx, cy)
+            with buffer.moved_origin(child.x, child.y):
+                with buffer.clip(buffer.origin.x, buffer.origin.y,
+                                 child.width, child.height):
+                    child.draw(buffer)
 
     def set_theme(self, theme):
         Widget.set_theme(self, theme)
--- a/tuikit/core/coords.py	Thu Mar 27 08:03:51 2014 +0100
+++ b/tuikit/core/coords.py	Fri Mar 28 10:44:29 2014 +0100
@@ -16,6 +16,13 @@
     def __repr__(self):
         return 'Point(x={0.x},y={0.y})'.format(self)
 
+    def move(self, relx, rely):
+        self.x += relx
+        self.y += rely
+
+    def moved(self, relx, rely):
+        return Point(self.x + relx, self.y + rely)
+
     def update(self, *args, **kwargs):
         """Update point.
 
@@ -129,6 +136,10 @@
         return (self.x <= x < self.x + self.w
             and self.y <= y < self.y + self.h)
 
+    def moved(self, relx, rely):
+        """Return new Rect with same size, moved by `relx`, `rely`."""
+        return Rect(self.x + relx, self.y + rely, self.w, self.h)
+
     def intersect(self, other):
         x1 = max(self.x, other.x)
         y1 = max(self.y, other.y)
--- a/tuikit/core/widget.py	Thu Mar 27 08:03:51 2014 +0100
+++ b/tuikit/core/widget.py	Fri Mar 28 10:44:29 2014 +0100
@@ -1,6 +1,8 @@
 from tuikit.core.coords import Point, Size
 from tuikit.core.theme import default_theme
 
+import logging
+
 
 class Widget:
 
@@ -9,8 +11,10 @@
     _num_instances = 0
 
     def __init__(self):
+        self._num_instances += 1
+        self._log = logging.getLogger(__name__)
+
         #: Widget name is used for logging etc. Not visible anywhere.
-        self._num_instances += 1
         self.name = '{}{}'.format(
             self.__class__.__name__,
             self._num_instances)
@@ -60,14 +64,28 @@
     def resize(self, w, h):
         self._size.update(w, h)
 
-    ## appearance ##
+    ## drawing, looks ##
 
-    def draw(self, buffer, x, y):
-        pass
+    def draw(self, buffer):
+        """Draw self into buffer."""
+        self._log.debug('%s draw into %r at %s (exposed %s)',
+                        self.name, buffer, buffer.origin, self.exposed(buffer))
 
     def set_theme(self, theme):
         self.theme = theme
 
+    @staticmethod
+    def exposed(buffer):
+        """Exposed part of widget.
+
+        Only this area needs to be drawn.
+
+        Returns exposed Rect in widget's local coordinates,
+        where 0,0 is left top corner of widget to be drawn.
+
+        """
+        return buffer.clip_rect.moved(-buffer.origin.x, -buffer.origin.y)
+
     ## timeouts ##
 
     def add_timeout(self, delay, callback, *args):
--- a/tuikit/core/window.py	Thu Mar 27 08:03:51 2014 +0100
+++ b/tuikit/core/window.py	Fri Mar 28 10:44:29 2014 +0100
@@ -38,13 +38,14 @@
         self.redraw()
 
     def redraw(self):
+        self.buffer.reset_origin()
         Container.draw(self, self.buffer)
         self.buffer.puts(10, 5, '{0.w} {0.h}'.format(self.size))
         self.buffer.frame()
 
-    def draw(self, buffer, x=0, y=0):
-        """Draw this window into `buffer` at x/y coords."""
-        buffer.draw(self.buffer, x, y)
+    def draw(self, buffer):
+        """Draw this window into `buffer`."""
+        buffer.draw(self.buffer)
 
 
 class WindowManager(Container):
--- a/tuikit/widgets/button.py	Thu Mar 27 08:03:51 2014 +0100
+++ b/tuikit/widgets/button.py	Fri Mar 28 10:44:29 2014 +0100
@@ -51,24 +51,25 @@
             return self.color_highlighted
         return self.color
 
-    def draw(self, buffer, x, y):
+    def draw(self, buffer):
+        Widget.draw(self, buffer)
         pad = self.width - len(self.label) - len(self.prefix) - len(self.suffix)
         lpad, rpad = self._divide_padding(pad)
         with buffer.attr(self._get_color()):
             # prefix
-            buffer.puts(x, y, self.prefix)
+            buffer.puts(0, 0, self.prefix)
             pos = len(self.prefix)
             # left pad
-            buffer.puts(x + pos, y, ' ' * lpad)
+            buffer.puts(pos, 0, ' ' * lpad)
             pos += lpad
             # label
-            buffer.puts(x + pos, y, self.label)
+            buffer.puts(pos, 0, self.label)
             pos += len(self.label)
             # right pad
-            buffer.puts(x + pos, y, ' ' * rpad)
+            buffer.puts(pos, 0, ' ' * rpad)
             pos += rpad
             # suffix
-            buffer.puts(x + pos, y, self.suffix)
+            buffer.puts(pos, 0, self.suffix)
 
     def on_mousedown(self, ev):
         self.highlight = True
--- a/tuikit/widgets/label.py	Thu Mar 27 08:03:51 2014 +0100
+++ b/tuikit/widgets/label.py	Fri Mar 28 10:44:29 2014 +0100
@@ -12,6 +12,6 @@
     def set_theme(self, theme):
         self.color = self.theme.normal
 
-    def draw(self, buffer, x, y):
+    def draw(self, buffer):
         with buffer.attr(self.color):
-            buffer.puts(x, y, self.label)
+            buffer.puts(0, 0, self.label)