Add origin to Buffer. Use it to simplify hierarchical drawing.
--- 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)