pygo/sgc/widgets/toggle.py

184 lines
7.1 KiB
Python
Raw Normal View History

2012-04-14 22:38:47 +00:00
#!/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"]