tuikit/container.py
author Radek Brich <radek.brich@devl.cz>
Sat, 05 Jan 2013 00:37:11 +0100
changeset 46 2b43a7f38c34
parent 45 43b2279b06e1
child 62 2f61931520c9
permissions -rw-r--r--
Minor updates. Replace super() with direct class reference. Add demo_colors.

# -*- coding: utf-8 -*-

from tuikit.widget import Widget
from tuikit.common import Borders, Coords


class Container(Widget):

    '''Container widget. Base for any widget which can contain other widgets.'''

    def __init__(self, width = 10, height = 10):
        '''Create container of requested size.'''
        Widget.__init__(self, width, height)

        #: List of child widgets.
        self.children = []

        #: Offset for child widgets
        self.offset = Coords()

        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()

        self._layout = None

        self.widthrequest = (None, None)
        self.heightrequest = (None, None)

        self.colorprefix = None

        self.trap_focus = False  # if True, tab cycles inside container


    def add(self, widget, **kwargs):
        '''Add widget into this container.'''
        self.children.append(widget)
        widget.parent = self
        widget.top = self.top
        widget.hints.update(kwargs)
        if self.focuschild is None and widget.can_focus():
            self.focuschild = widget

    @property
    def layout(self):
        return self._layout

    @layout.setter
    def layout(self, value):
        """Layout manager for placing child widgets."""
        self._layout = value
        self._layout.container = self


    def _set_top(self, value):
        self._top = value
        for child in self.children:
            child.top = value


    def focus_next(self):
        """Focus next child.

        Sets focus to next child, if there is one
        which can be focused. Cycles from last child
        to first when needed. Return value depends on
        this cycling:

         * False means there wasn't any child to focus
           before end of list. Focus was either not changed
           or first child was focused.

         * True when focus is set to next child in normal
           way or when self.trap_focus is set.

        Return value is supposed to be returned from keypress
        event - in that case, True stops event propagation.

        """
        idx_current = self.children.index(self.focuschild)
        idx_new = idx_current
        cycled = False
        while True:
            idx_new += 1
            if idx_new >= len(self.children):
                idx_new = 0
                cycled = True
            if idx_current == idx_new:
                return False
            if self.children[idx_new].can_focus():
                self.children[idx_new].set_focus()
                return self.trap_focus or not cycled

    def draw(self, driver, x, y):
        """Draw the container and its children.

        This method should not be overriden by subclasses,
        use on_draw instead.

        """
        if self.hidden:
            return True

        driver.clipstack.push(x, y, self.width, self.height)
        if self.colorprefix:
            driver.pushcolorprefix(self.colorprefix)

        Widget.draw(self, driver, x, y)

        for child in [ch for ch in self.children if not ch.allow_layout]:
            child.draw(driver, x + child.x, y + child.y)

        l, t, r, b = self.borders
        driver.clipstack.push(x+l, y+t, self.width-l-r, self.height-t-b)

        for child in [ch for ch in self.children if ch.allow_layout]:
            child.draw(driver,
                x + self.offset.x + child.x,
                y + self.offset.y + child.y)

        driver.clipstack.pop()

        if self.colorprefix:
            driver.popcolorprefix()
        driver.clipstack.pop()

    def on_resize(self, ev):
        for child in self.children:
            child.emit('resize')

    def on_keypress(self, ev):
        if self.focuschild is not None:
            handled = self.focuschild.emit('keypress', ev)
            if handled:
                return True
        if ev.keyname == 'tab':
            return self.focus_next()

    def on_mousedown(self, ev):
        handled = False
        for child in reversed(self.children):
            if child.enclose(ev.wx, ev.wy):
                childev = ev.childevent(child)
                child.emit('mousedown', childev)
                self.mousechild = child
                handled = True
                break
        return handled

    def on_mouseup(self, ev):
        if self.mousechild:
            childev = ev.childevent(self.mousechild)
            self.mousechild.emit('mouseup', childev)
            self.mousechild = None
            return True

    def on_mousemove(self, ev):
        if self.mousechild:
            childev = ev.childevent(self.mousechild)
            self.mousechild.emit('mousemove', childev)
            return True

    def on_mousewheel(self, ev):
        handled = False
        for child in reversed(self.children):
            if child.enclose(ev.wx, ev.wy):
                childev = ev.childevent(child)
                child.emit('mousewheel', childev)
                handled = True
                break
        return handled