view mamba/widgets/base.py @ 97:c65046b5bafd

eat focus adjusting key events
author Stefano Rivera <stefano@rivera.za.net>
date Sun, 11 Sep 2011 19:24:59 +0200
parents 7a17c5b74148
children 2b6626f417f2
line wrap: on
line source

import collections

import pygame
from pygame.constants import K_UP, K_DOWN
from pygame.locals import MOUSEMOTION, MOUSEBUTTONUP, MOUSEBUTTONDOWN, KEYDOWN


class Widget(object):

    def __init__(self, rect):
        if not isinstance(rect, pygame.Rect):
            rect = pygame.Rect(rect, (0, 0))
        self.rect = rect
        self.focussable = False
        self.focussed = False
        self.callbacks = collections.defaultdict(list)

    def add_callback(self, eventtype, callback, *args):
        self.callbacks[eventtype].append((callback, args))

    def event(self, ev):
        for callback, args in self.callbacks[ev.type]:
            if callback(ev, self, *args):
                return True
        return False

    def draw(self, surface):
        "Override me"
        pass


class Container(Widget):

    def __init__(self, rect, root=False):
        super(Container, self).__init__(rect)
        self.children = []
        self.root = root
        self.focussable = True
        self.focussed_child = None

    def event(self, ev):
        """Push an event down through the tree, and fire our own event as a
        last resort
        """
        if ev.type in (MOUSEMOTION, MOUSEBUTTONUP, MOUSEBUTTONDOWN):
            for child in self.children:
                if child.rect.collidepoint(ev.pos):
                    if child.event(ev):
                        return True
        if ev.type == KEYDOWN:
            for child in self.children:
                if child.focussed:
                    if child.event(ev):
                        return True
        if super(Container, self).event(ev):
            return True
        if self.root and ev.type == KEYDOWN and ev.key in (K_UP, K_DOWN):
            return self.adjust_focus(1 if ev.key == K_DOWN else -1)

    def add(self, widget):
        widget.parent = self
        self.children.append(widget)

    def adjust_focus(self, direction):
        """Try and adjust focus in direction (integer)
        """
        if self.focussed_child is not None:
            child = self.children[self.focussed_child]
            if isinstance(child, Container):
                if child.adjust_focus(direction):
                    return True
            child.focussed = False

        current = self.focussed_child
        if current is None:
            current = -1 if direction > 0 else len(self.children)
        if direction > 0:
            possibles = list(enumerate(self.children))[current + 1:]
        else:
            possibles = list(enumerate(self.children))[:current]
            possibles.reverse()
        for i, child in possibles:
            if child.focussable:
                child.focussed = True
                if isinstance(child, Container):
                    if child.adjust_focus(direction):
                        self.focussed_child = i
                        return True
                    child.focussed = False
                    continue
                else:
                    self.focussed_child = i
                    child.focussed = True
                    return True
        else:
            if self.root:
                # At the end, mark the last one as focussed, again
                child = self.children[self.focussed_child]
                if isinstance(child, Container):
                    if child.adjust_focus(-direction):
                        return True
                child.focussed = True
            else:
                self.focussed_child = None
            return False

    def draw(self, surface):
        if self.root and not self.focussed:
            self.focussed = True
            self.adjust_focus(1)
        for child in self.children:
            child.draw(surface)