230 lines
8.9 KiB
Python
230 lines
8.9 KiB
Python
#!/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"]))
|