#!/usr/bin/env python # Copyright (C) 2012 Michael Rochester, Sam Bull """ Toggle button, allows the user to change a boolean setting. """ import pygame from pygame.locals import * from _locals import * from _locals import focus from base_widget import Simple class Toggle(Simple): """ A toggle button, allowing the user to select between two states. Attributes: state: True if toggle button is switched on. Images: 'image': The background when the button is set to off. 'active': The background when the button is set to on. 'handle': The image used for the slider. """ _can_focus = True _default_size = (130,30) _available_images = ("active", "handle") _settings_default = {"state": False, "label": "", "label_col": Font.col, "on_col": (88, 158, 232), "off_col": (191, 191, 186), "on_label_col": (255,255,255), "off_label_col": (93,82,80)} _draw_rect = False _drag = None _handle_rect = None def _config(self, **kwargs): """ state: ``bool`` Sets the state of the button (False by default). label: ``str`` Text to be displayed next to the button. label_col: ``tuple`` (r,g,b) The colour for the label. on_col: ``tuple`` (r,g,b) The background colour when the button is set to the 'on' state. off_col: ``tuple`` (r,g,b) The background colour when the button is set to the 'off' state. on_label_col: ``tuple`` (r,g,b) The on/off text colour when the button is set to the 'on' state. off_label_col: ``tuple`` (r,g,b) The on/off text colour when the button is set to the 'off' state. """ if "init" in kwargs: self._handle_rect = Rect(0,0,0,0) if "state" in kwargs: self._settings["state"] = kwargs["state"] if "label" in kwargs: self._settings["label"] = kwargs["label"] if "label_col" in kwargs: self._settings["label_col"] = kwargs["label_col"] if "on_col" in kwargs: self._settings["on_col"] = kwargs["on_col"] if "off_col" in kwargs: self._settings["off_col"] = kwargs["off_col"] if "on_label_col" in kwargs: self._settings["on_label_col"] = kwargs["on_label_col"] if "off_label_col" in kwargs: self._settings["off_label_col"] = kwargs["off_label_col"] def _draw(self, draw): label = Simple(Font["widget"].render(self._settings["label"], True, self._settings["label_col"])) label.rect.centery = self.rect.h/2 # Calculate widget and handle rects self.box = Rect((label.rect.w + 10, 0), (self.rect.w - label.rect.w - 10, self.rect.h)) self._handle_rect.size = ((self.box.w/2) - 4, self.rect.h - 4) # Draw handle draw.rect(self._images["handle"], (245,245,244), ((0,2), self._handle_rect.size)) for x in range(2,5): # Grips draw.line(self._images["handle"], (232,232,229), ((self._handle_rect.w/6)*x, 10), ((self._handle_rect.w/6)*x, self.rect.h-10), 3) # Draw main images for img in ("off", "on"): image = "image" if (img == "off") else "active" draw.rect(self._images[image], self._settings[img+"_col"], self.box) # Render the labels col = self._settings[img+"_label_col"] on = Simple(Font["widget"].render("ON", True, col)) off = Simple(Font["widget"].render("OFF", True, col)) on.rect.center = (label.rect.w + 10 + self.box.w*.25 - 1, self.rect.h/2) off.rect.center = (label.rect.w + 10 + self.box.w*.75 + 1, self.rect.h/2) # Blit all text self._images[image].blit(label.image, label.pos) self._images[image].blit(on.image, on.pos) self._images[image].blit(off.image, off.pos) self._draw_button() def _event(self, event): if event.type == MOUSEBUTTONDOWN and event.button == 1: # If clicking handle if self._handle_rect.collidepoint( (event.pos[0]-self.pos_abs[0], event.pos[1]-self.pos_abs[1])): self._drag = (event.pos[0], event.pos[0] - self._handle_rect.x) elif event.type == MOUSEMOTION and event.buttons[0]: if self._drag is not None: # Move handle self._handle_rect.x = max(min(self.box.centerx + 3, event.pos[0] - self._drag[1]), self.box.x + 2) self._draw_button() elif event.type == MOUSEBUTTONUP and event.button == 1: if self._drag is not None: if abs(self._drag[0] - event.pos[0]) < 5: # Clicked self._settings["state"] = not self._settings["state"] else: # Dragged # Determine if dropped in on/off position if self._handle_rect.centerx < self.box.centerx: self._settings["state"] = False else: self._settings["state"] = True self._drag = None self._draw_button() elif self.rect_abs.collidepoint(event.pos): # Clicked outside of handle self._settings["state"] = not self._settings["state"] self._draw_button() elif event.type == KEYUP: if event.key in (K_RETURN, K_SPACE): self._settings["state"] = not self._settings["state"] self._draw_button() def _focus_enter(self, focus): """Draw dotted rect when focus is gained from keyboard.""" if focus == 1: self._draw_rect = True self._draw_button() def _focus_exit(self): """Stop drawing dotted rect when focus is lost.""" self._draw_rect = False self._draw_button() def _draw_button(self): """Render the widget and blit the handle in the correct place.""" if self._settings["state"] is False: self.image = self._images["image"].copy() else: self.image = self._images["active"].copy() if self._drag is None: # Fix handle in place when not dragging if self._settings["state"] is False: self._handle_rect.x = self.box.x + 2 else: self._handle_rect.x = self.box.centerx + 3 self.image.blit(self._images["handle"], self._handle_rect) # Draw dotted rectangle to show keyboard focus if self._draw_rect: self._dotted_rect() @property def state(self): return self._settings["state"]