184 lines
7.1 KiB
Python
184 lines
7.1 KiB
Python
#!/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"]
|