#!/usr/bin/env python # Copyright (C) 2010-2012 Sam Bull """ Base widget, all widgets inherit from this. """ import pygame from pygame.locals import Rect from _locals import * from _locals import (has_focus, is_active, add_widget, remove_widget_order, set_cursor, remove_cursor) try: import opengl except ImportError: print "OpenGL widgets disabled." class Simple(pygame.sprite.Sprite): """ Widget foundations all widgets should inherit from. This can also be used as a simple widget that does nothing, such as displaying an image. Attributes: image: The current surface that will be drawn to the screen. rect: The ``pygame.Rect`` used for the widget's position and size. rect_abs: A ``pygame.Rect`` using the absolute screen position. pos: The widget's position. Can be retrieved or assigned as a shortcut for rect.topleft. Also a shortcut for setting pos through config(). pos_abs: The widget's absolute screen position. """ # Widget settings _can_focus = False _modal = False _layered = False # Layered updates for dialog windows etc. _default_size = None _parent = None _available_images = () _extra_images = {} _settings_default = {} _fade = None # Alpha level when fading _fade_up = True _custom_image = False def __init__(self, surf=None, **kwargs): """ Args: surf: The surface that should be drawn to screen, of type: pygame.Surface: Use an existing surface. tuple,list: Contains size as (width,height), creates a new surface. str: Contains file name to load an image. dict: Contains multiple images to be loaded. The documentation will specify if a widget uses multiple images and what names to use. kwargs: Any number of keyword arguments matching those for config(). """ pygame.sprite.Sprite.__init__(self) # Implicitly pass attributes into _draw() # Used to reduce complexity for widget developers draw = self._draw self._draw = lambda d=self.get_draw(): draw(d) # Initialise attributes self._images = {} self._available_images = ("image",) + self._available_images self._settings = self._settings_default.copy() self.rect = Rect((0,0), (0,0)) # Use default size if none specified if surf is None: surf = self._default_size # Create base surfaces if not None. # If None, widget is expected to call this function later. if surf is not None: self._create_base_images(surf) self.config(init=None, **kwargs) def config(self, **kwargs): """ Update widget configuration and redraw the widget. Keyword Args: pos: ``tuple`` (x,y) Position to set widget to. """ if "pos" in kwargs: self.rect.topleft = kwargs["pos"] self._config(**kwargs) self._draw() def _config(self, **kwargs): """Widgets should overload for custom widget configuration.""" pass def add(self, order=None, fade=True): """ Add widget to screen. Args: order: Integer representing the order widget should receive focus when user presses TAB. The widget with the lowest order will receive focus first, then moving up with increasing values. fade: True if widget should fade in, False if not. """ added = add_widget(self, order) # Fade widget in if fade: self._fade_up = True if added and self._fade is None: self._fade = 1 self.image.set_alpha(self._fade) else: self._fade = None self.image.set_alpha(255) def remove(self, fade=True): """ Remove widget from screen. Args: fade: True if widget should fade out. """ if fade: # Fade widget out self._fade_up = False if self._fade == None: self._fade = 250 else: # Remove widget immediately self.kill() remove_widget_order(self) def active(self): """Return True if widget is active (onscreen).""" return is_active(self) def has_focus(self): """Return True if this widget has focus.""" return has_focus(self) def get_draw(self): """ Return appropriate draw module for pygame or OpenGL. Use like: ``draw = self.get_draw()`` ``draw.rect(self.image, ...)`` """ if not get_screen()._opengl: return pygame.draw else: return opengl.draw def update(self, time): """Placeholder for function that updates the widget per frame.""" pass def _event(self, event): """Placeholder for function that receives event for the widget.""" pass def _draw(self, draw): """Widgets should overload to draw default images.""" pass def _create_base_images(self, surf, parent=None): """ Creates the base surfaces to draw on, or uses existing images. If self._default_size is None, widget is expected to call this function manually when no size is given. """ Image = opengl.OpenGLImage if get_screen()._opengl else pygame.Surface def create_image(surf): """Return a created surface.""" if isinstance(surf, pygame.Surface): surf.set_colorkey(0) self._custom_image = True return surf elif isinstance(surf, (tuple,list)): self._custom_image = False if isinstance(surf[0], (tuple,list)): surf = (self.rect.w * surf[0][0] + surf[0][1], self.rect.h * surf[1][0] + surf[1][1]) surf = Image(surf) surf.set_colorkey(0) return surf elif isinstance(surf, str): self._custom_image = True return pygame.image.load(surf).convert_alpha() # Create base images if isinstance(surf, dict): for img in surf: assert (img in self._available_images or img in self._extra_images), "Incorrect image." self._images[img] = create_image(surf[img]) else: self._images["image"] = create_image(surf) # Copy other images, if any have not been supplied. assert "image" in self._images, "Must supply 'image'" for count, name in enumerate(self._available_images): if name not in self._images: img = self._images[self._available_images[count-1]] self._images[name] = img.copy() self.image = self._images["image"].copy() self.rect.size = self.image.get_size() # Set up extra images for name in self._extra_images: if name not in self._images: self._images[name] = create_image(self._extra_images[name]) def _change_focus(self, forward=True): """ Called when focus should be changed. Container widget should override this function. Args: forward: True if toggling focus forwards, False if backwards. Returns: True if widget should change focus from this widget. """ return True def _focus_enter(self, focus=0): """ Called when the widget gains focus. Args: focus: 1 if focused by keyboard, 2 if by mouse. """ pass def _focus_exit(self): """Called when the widget loses focus.""" pass def _dotted_rect(self, col=(255,255,255)): """Draw a dotted rectangle to show keyboard focus.""" self.image.lock() for i in range(0, self.rect.w, 3): # Draw horizontal lines self.image.set_at((i, 0), col) self.image.set_at((i, self.rect.h-1), col) for i in range(0, self.rect.h, 2): # Draw vertical lines self.image.set_at((0, i), col) self.image.set_at((self.rect.w-1, i), col) self.image.unlock() def _set_cursor(self, size, hotspot, xormasks, andmasks): set_cursor(self, size, hotspot, xormasks, andmasks) def _remove_cursor(self): remove_cursor(self) # --PROPERTIES-- @property def rect_abs(self): if self._parent is None: return self.rect else: p_abs = self._parent.pos_abs p = (self.rect.x + p_abs[0], self.rect.y + p_abs[1]) return Rect(p, self.rect.size) @property def pos(self): return self.rect.topleft @pos.setter def pos(self, value): self.rect.topleft = value @property def pos_abs(self): if self._parent is None: return self.rect.topleft else: p_abs = self._parent.pos_abs return (self.rect.x + p_abs[0], self.rect.y + p_abs[1])