pygo/sgc/widgets/label.py

230 lines
8.9 KiB
Python
Raw Normal View History

2012-04-14 22:38:47 +00:00
#!/usr/bin/env python
# Copyright (C) 2010-2012 Sam Bull, Michael Rochester
"""
Label to display information to the user.
"""
import pygame.mouse
from pygame.locals import *
from _locals import *
from base_widget import Simple
class Label(Simple):
"""
Label
Attributes:
text: ``str`` displayed in label. Can be assigned as a shortcut for
``config(label=)`` with no second paramenter.
"""
_settings_default = {"text": "", "col": Font.col, "font": Font["widget"],
"col_selection": (118, 45, 215)}
_over = False
_select = None # Starting point of selection
__cursor_pos = 0
def _config(self, **kwargs):
"""
text: Either ``str`` containing text to be displayed or
``tuple`` containing two strings. First string is text to
be displayed, second string is rect attribute to be used
for position. Defaults to 'topleft' if not passing a tuple.
col: ``tuple`` (r,g,b) Text colour.
font: Font object the label will render with.
selectable: ``bool`` True if the text should be selectable.
col_selection: ``tuple`` (r,g,b) Colour of selection rectangle.
"""
if "init" in kwargs:
strings = pygame.cursors.textmarker_strings
cursor = pygame.cursors.compile(strings)
size = (len(strings[0]), len(strings))
hotspot = (size[0]/2, size[1]/2)
self._cursor = (size, hotspot) + cursor
if "text" in kwargs:
if isinstance(kwargs["text"], str):
self._settings["text"] = kwargs["text"]
else:
self._settings["text"] = kwargs["text"][0]
self._temp_pos = kwargs["text"][1]
if "col" in kwargs:
self._settings["col"] = kwargs["col"]
if "font" in kwargs:
self._settings["font"] = kwargs["font"]
if "selectable" in kwargs:
self._can_focus = kwargs["selectable"]
if "col_selection" in kwargs:
self._settings["col_selection"] = kwargs["col_selection"]
def _draw(self, draw):
if hasattr(self, "_temp_pos"):
pos = getattr(self.rect, self._temp_pos)
# Split into lines
text = []
for line in self._settings["text"].split("\n"):
text.append(self._settings["font"].render(line, True,
self._settings["col"]))
# Dynamically set size
h = 0
for line in text:
h += line.get_size()[1]
w = max(text, key=lambda x: x.get_size()[0])
self._create_base_images((w.get_size()[0], h))
# Blit each line
y = 0
for line in text:
self._images["image"].blit(line, (0,y))
y += line.get_size()[1]
# Copy position attribute over
if hasattr(self, "_temp_pos"):
setattr(self.rect, self._temp_pos, pos)
del self._temp_pos
self.image = self._images["image"].copy()
# Store as tuple of (pos, width) tuples.
if self._can_focus:
chars = []
p = 0
for c in range(1, len(self._settings["text"])+1):
char = self._settings["font"].render(self._settings["text"][:c],
True, (0,0,0))
chars.append((p, char.get_size()[0] - p))
p = char.get_size()[0]
chars.append((p, 0))
self._chars = tuple(chars)
def _event(self, event):
if event.type == MOUSEBUTTONDOWN and event.button == 1:
# Begin drawing selection
if pygame.key.get_mods() & KMOD_SHIFT and self._select is None:
self._select = self._cursor_pos
self._cursor_pos = self._mouse_cursor(event.pos)
if not pygame.key.get_mods() & KMOD_SHIFT:
self._select = self._cursor_pos
elif event.type == MOUSEMOTION and event.buttons[0]:
# Continue drawing selection while mouse held down
self._cursor_pos = self._mouse_cursor(event.pos)
elif event.type == MOUSEBUTTONUP:
# Set cursor position with mouse click
self._cursor_pos = self._mouse_cursor(event.pos)
if self._select == self._cursor_pos:
self._select = None
elif event.type == KEYDOWN:
if event.key == K_ESCAPE:
self._select = None
elif event.key == K_LEFT:
if not event.mod & KMOD_SHIFT:
self._select = None # Break selection
elif self._select is None:
# Reset selection if not selecting
self._select = self._cursor_pos
self._cursor_pos -= 1
# Remove selection when cursor is at same position
if self._select == self._cursor_pos:
self._select = None
elif event.key == K_RIGHT:
if not event.mod & KMOD_SHIFT:
self._select = None # Break selection
elif self._select is None:
self._select = self._cursor_pos
self._cursor_pos += 1
if self._select == self._cursor_pos:
self._select = None
elif event.mod & KMOD_CTRL:
if event.key == K_a: # Select all
self._select = 0
self._cursor_pos = len(self._settings["text"])
elif event.key == K_c and self._select is not None: # Copy
select = self._select_fix()
string = "".join(
self._settings["text"][select[0]:select[1]])
try:
pygame.scrap.put(SCRAP_TEXT, string)
except pygame.error:
print "Please run 'pygame.scrap.init()'" \
" to use the clipboard."
def update(self, time):
if self._can_focus:
# Change cursor when mouse not held down
if not pygame.mouse.get_pressed()[0]:
if not self._over and \
self.rect_abs.collidepoint(pygame.mouse.get_pos()):
self._over = True
self._set_cursor(*self._cursor)
elif self._over and \
not self.rect_abs.collidepoint(pygame.mouse.get_pos()):
self._over = False
self._remove_cursor()
if self.has_focus():
self.image = self._images["image"].copy()
draw = self.get_draw()
if self._select is None:
# Draw cursor in box
x = self._chars[self._cursor_pos][0] - 1
draw.line(self.image, (0,0,1), (x, 2), (x, self.rect.h-2))
else:
select = self._select_fix()
# Semi-transparent selection rectangle
w = (self._chars[select[1]][0] - self._chars[select[0]][0])
selection = Simple((w, self.rect.h - 2))
selection.pos = (self._chars[select[0]][0], 1)
selection.image.fill(self._settings["col_selection"])
selection.image.set_alpha(100)
# Border around selection rectangle
selection_b = Simple((selection.rect.w+2,
selection.rect.h+2))
draw.rect(selection_b.image,
self._settings["col_selection"],
selection_b.rect, 1)
self.image.blit(selection.image, selection.pos)
self.image.blit(selection_b.image, (selection.rect.x-1,
selection.rect.y-1))
@property
def text(self):
return self._settings["text"]
@text.setter
def text(self, value):
self._settings["text"] = value
self._draw()
def _mouse_cursor(self, mouse_pos):
"""Return the text cursor position of the mouse."""
pos = mouse_pos[0] - self.rect_abs.x
for index, (p,w) in enumerate(self._chars):
if pos <= p + w/2:
break
return index
def _select_fix(self):
"""If selection is right-to-left then reverse positions."""
if self._select > self._cursor_pos:
return (self._cursor_pos, self._select)
else:
return (self._select, self._cursor_pos)
def _focus_exit(self):
"""Cancel any selection when focus is lost."""
self.image = self._images["image"].copy()
@property
def _cursor_pos(self):
return self.__cursor_pos
@_cursor_pos.setter
def _cursor_pos(self, value):
# Keep cursor position within text
self.__cursor_pos = min(max(value, 0), len(self._settings["text"]))