#!/usr/bin/env python # Copyright (C) 2010-2012 Sam Bull """ Button widget, allows input from the user clicking the button. """ import pygame from pygame.locals import * from _locals import * from base_widget import Simple class Button(Simple): """ A clickable button. Images: 'image': The default button state. 'over': The image used when the cursor is hovering over the button. 'down': The image used when the user is clicking down on the button. """ _can_focus = True _default_size = (110, 50) _available_images = ("over", "down") _settings_default = {"label": ("",), "col": (127, 127, 169), "label_col": Font.col} _state = None _draw_rect = False def _config(self, **kwargs): """ label: ``str`` Text to display on the button. col: ``tuple`` (r,g,b) The central colour used if no image is provided. If you want to avoid the colours saturating keep the RGB values below 200. label_col: ``tuple`` (r,g,b) The text colour for the button's label. """ # Label in middle of button if "label" in kwargs: # Save string as first argument self._settings["label"] = [kwargs["label"]] self._draw_label() if "col" in kwargs: self._settings["col"] = kwargs["col"] if "label_col" in kwargs: self._settings["label_col"] = kwargs["label_col"] self._draw_label() def _draw_label(self): # Clear previous renderings del self._settings["label"][1:] label = self._settings["label"][0].split("\n") for count, line in enumerate(label): lbl = Simple(Font["widget"].render(line, True, self._settings["label_col"])) self._settings["label"].append(lbl) y = (self.rect.h - (lbl.rect.h * len(label))) / 2 + \ (lbl.rect.h * count) lbl.rect.midtop = (self.rect.w/2, y) def _draw(self, draw): # Frames around edge of button x = min(self.image.get_size()) / 8 self._frame_lt = ((0,0), (self.rect.w,0), (self.rect.w-x,x), (x,x), (x,self.rect.h-x), (0,self.rect.h)) self._frame_rb = ((self.rect.w,self.rect.h), (0,self.rect.h), (x,self.rect.h-x), (self.rect.w-x,self.rect.h-x), (self.rect.w-x,x), (self.rect.w,0)) cols = {} cols["image"] = self._settings["col"] cols["over"] = [min(c*1.1, 255) for c in self._settings["col"]] cols["down"] = [c*0.8 for c in self._settings["col"]] for img in cols: if not self._custom_image: self._images[img].fill(cols[img]) # Draw a frame around the edges of the button frame_lt_c = [min(c*1.3,255) for c in cols[img]] frame_rb_c = [c*0.8 for c in cols[img]] draw.polygon(self._images[img], frame_lt_c, self._frame_lt) draw.polygon(self._images[img], frame_rb_c, self._frame_rb) # Blit label onto button for line in self._settings["label"][1:]: self._images[img].blit(line.image, line.pos) self._draw_button() def activate(self): """ Called when the button is activated. Emits an event with attribute 'gui_type' == "activate". Override this function to use as a callback handler. """ ev = pygame.event.Event(GUI, {"gui_type": "activate", "widget_type": self.__class__, "widget":self}) pygame.event.post(ev) def update(self, time): """Update the button each frame.""" if self.rect_abs.collidepoint(pygame.mouse.get_pos()): if self._state not in ("over","down"): # Draw over state self._state = "over" self._draw_button() elif self._state not in ("off","down"): # Draw normal state self._state = "off" self._draw_button() def _event(self, event): """Respond to events.""" if event.type == MOUSEBUTTONDOWN and event.button == 1: # Draw down state self._state = "down" self._draw_button() elif event.type == MOUSEBUTTONUP and event.button == 1: self._state = None # If releasing mouse on button, call function if self.rect_abs.collidepoint(event.pos): self.activate() elif event.type == KEYDOWN: if event.key in (K_SPACE, K_RETURN): self._state = "down" self._draw_button() self.activate() elif event.type == KEYUP: if event.key in (K_SPACE, K_RETURN): self._state = None def _focus_enter(self, focus): """Draw rectangle when focus is gained from keyboard.""" if focus == 1: self._draw_rect = True self._draw_button() def _focus_exit(self): """Stop drawing rectangle when focus is lost.""" self._draw_rect = False self._draw_button() def _draw_button(self): """Draw the button.""" if self._state == "off": self.image = self._images["image"].copy() elif self._state == "over": self.image = self._images["over"].copy() elif self._state == "down": self.image = self._images["down"].copy() # Draw dotted rectangle to show keyboard focus if self._draw_rect: self._dotted_rect()