#!/usr/bin/env python # Copyright (C) 2010-2012 Sam Bull """ Container widget, can be inherited to implement more complex behaviour. """ import pygame.sprite from pygame.locals import * from _locals import * from _locals import Focus from base_widget import Simple class Container(Simple): """ Container widget. Handles focus and events of a group of widgets packed into a single container. If ``surf`` is not given, container will be the right size to fit all widgets. """ _can_focus = True _settings_default = {"border": 0, "col": 0, "widgets": None} _focus = None _order = None def _config(self, **kwargs): """ widgets: ``list`` Contains widgets to be added at creation time. The order of widgets in the list denotes order they receive focus when user hits :kbd:`TAB`. border: ``int`` Number of pixels to space around edges when ``surf`` is not given. col: ``tuple`` (r,g,b) Colour for background, 0 is transparent. """ if "border" in kwargs: self._settings["border"] = kwargs["border"] if "col" in kwargs: self._settings["col"] = kwargs["col"] if "widgets" in kwargs: self._settings["widgets"] = pygame.sprite.Group() self._focus = Focus() self._order = [] pad = self._settings["border"] for w in kwargs["widgets"]: w._parent = self w.pos = (w.rect.x + pad, w.rect.y + pad) self._settings["widgets"].add(w) if w._can_focus: self._order.append(w) if not hasattr(self, "image"): width = max(kwargs["widgets"], key=lambda w: w.rect.right) width = width.rect.right + pad height = max(kwargs["widgets"], key=lambda w: w.rect.bottom) height = height.rect.bottom + pad self._create_base_images((width, height)) def update(self, time): """Update widgets each frame.""" self.image.fill(self._settings["col"]) self._settings["widgets"].update(time) for w in self._settings["widgets"]: self.image.blit(w.image, w.pos) def _event(self, event): """Handle focus and send events to sub-widgets.""" if event.type == MOUSEBUTTONDOWN: hit = False for widget in self._settings["widgets"]: # Check if user clicked a widget if widget._can_focus: if widget.rect_abs.collidepoint(event.pos): self._focus.add(2, widget) hit = True break # Lose focus if clicking away from widgets if not hit: self._focus.empty() elif event.type == KEYDOWN and event.key == K_TAB: # Focus number for current focused widget if self._focus.sprite not in self._order: curr_num = None else: curr_num = self._order.index(self._focus.sprite) if not event.mod & KMOD_SHIFT: # Move focus to next widget # Next focus number in the list if curr_num is None: # If nothing focused, focus first widget new_num = 0 elif not self._focus.sprite._change_focus(True): # Test for container widgets new_num = curr_num elif curr_num >= len(self._order)-1: new_num = 0 else: new_num = curr_num + 1 else: # Shift key - move focus to previous widget if curr_num is None: new_num = -1 elif not self._focus.sprite._change_focus(False): new_num = curr_num elif curr_num <= 0: new_num = -1 else: new_num = curr_num - 1 if curr_num != new_num: self._focus.add(1, self._order[new_num]) if self._focus: self._focus.sprite._event(event) def _change_focus(self, forward=True): """Override Simple and check if focus should leave yet.""" if self._focus and not self._focus.sprite._change_focus(forward): return False if not self._focus: return False num = self._order.index(self._focus.sprite) if forward and num < len(self._order)-1: return False if not forward and num > 0: return False return True def _focus_exit(self): self._focus.empty()