Mercurial > skaapsteker
view skaapsteker/widgets/text.py @ 257:3f8ce3452cd6
Add support for selecting dialogue choices (and for moving TextChoice around the screen).
author | Simon Cross <hodgestar@gmail.com> |
---|---|
date | Fri, 08 Apr 2011 01:36:44 +0200 |
parents | 30ae3c681507 |
children | a5a57abd5472 |
line wrap: on
line source
# -*- coding: utf-8 -*- import pygame from pygame.locals import KEYDOWN, K_UP, K_DOWN, K_RETURN from ..widgets import Widget from ..data import filepath fonts = { 'sans': 'DejaVuSans.ttf', } loaded_fonts = {} def load_font(name, size): if (name, size) not in loaded_fonts: fontfn = filepath('fonts/' + fonts[name]) fonts[(name, size)] = pygame.font.Font(fontfn, size) return fonts[(name, size)] # Probably belongs in a utils module def unindent_text(text): """Reformat docstring-style text blocks.""" text = text.split('\n') while text[0].strip() == '': text.pop(0) while text[-1].strip() == '': text.pop(-1) indent = len(text[0]) - len(text[0].lstrip()) indent_chars = text[0][:indent] for index, line in enumerate(text): if line.startswith(indent_chars): text[index] = line[indent:] return '\n'.join(text) class Text(Widget): def __init__(self, text, pos, font='sans', size=16, color='black', shadow=None, wrap=False): self.text = text if isinstance(pos, pygame.Rect): self.rect = pos else: self.rect = pygame.Rect(pos, (0, 0)) self.font = load_font(font, size) self.color = pygame.Color(color) self.shadow = None if shadow: self.shadow = pygame.Color(shadow) if wrap: if not isinstance(pos, pygame.Rect): raise Exception("Cannot wrap without dimensions") self._wrap() self._lines = [] offset = (0, 0) for line in self.text.split('\n'): if shadow is not None: self._lines.append(((offset[0] + 1, offset[1] + 1), self.font.render(line, True, self.shadow))) self._lines.append((offset, self.font.render(line, True, self.color))) offset = (offset[0], offset[1] + self.font.get_linesize()) self.rect.width = max(line[1].get_width() for line in self._lines) self.rect.height = self.font.get_linesize() * len(self._lines) if shadow is not None: self.rect.width += 1 self.rect.height += 1 def _wrap(self): unwrapped = [para.replace('\n', ' ') for para in self.text.split('\n\n')] text = [] for paragraph in unwrapped: words = paragraph.split(' ') nwords = len(words) from_ = 0 to = 0 while to < nwords: to += 1 line = ' '.join(words[from_:to]) if self.font.size(line)[0] <= self.rect.width: continue if to - from_ > 1: to -= 1 text.append(' '.join(words[from_:to])) from_ = to text.append(' '.join(words[from_:to])) text.append('') if text: text.pop(-1) self.text = '\n'.join(text) def draw(self, surface): pos = self.rect for offset, line in self._lines: surface.blit(line, pos.move(offset)) class TextChoice(Widget): """Render a list of options, and a selector. options can be text or a tuple of (text, data). When selected, calls everything registered in callbacks with (index, data) """ def __init__(self, options, pos, **kwargs): self.options = [] self.option_widgets = [] if isinstance(pos, pygame.Rect): self.rect = pos else: self.rect = pygame.Rect(pos, (0, 0)) pos = self.rect self.selector = Text(u'ยป ', pos.move(0, 0), **kwargs) self.selected = 0 self.callbacks = [] for option in options: if not isinstance(option, tuple): option = (option, None) self.options.append(option) text, data = option t = Text(text, pos.move(0, 0), **kwargs) self.option_widgets.append(t) self.rect.width = max(line.rect.width for line in self.option_widgets ) + self.selector.rect.width self.rect.height = sum(line.rect.height for line in self.option_widgets) def _update_rects(self): pos = self.rect.move(self.selector.rect.width, 0) for t in self.option_widgets: t.rect.topleft = pos.topleft pos = pos.move(0, t.rect.height) self.selector.rect.top = self.option_widgets[self.selected].rect.top self.selector.rect.left = self.rect.left def dispatch(self, ev): if ev.type is KEYDOWN: if ev.key == K_UP: self.selected -= 1 elif ev.key == K_DOWN: self.selected += 1 elif ev.key == K_RETURN: for callback in self.callbacks: callback(self.selected, self.options[self.selected][1]) self.selected %= len(self.option_widgets) def draw(self, surface): self._update_rects() for option in self.option_widgets: option.draw(surface) self.selector.draw(surface) class TextButton(Widget): def __init__(self, text, pos, margin=10, **kwargs): textpos = pygame.Rect((pos[0] + margin, pos[1] + margin), (0, 0)) self.text = Text(text, textpos, **kwargs) self.rect = pygame.Rect(pos, (0, 0)) self.rect.width = self.text.rect.width + 2 * margin self.rect.height = self.text.rect.height + 2 * margin def draw(self, surface, selected=False): self.text.draw(surface) pygame.draw.rect(surface, self.text.color, self.rect, 1) class ButtonSet(Widget): def __init__(self): self.options = [] self.selected = 0 self.callbacks = [] def append(self, widget, data=None): self.options.append((widget, data)) def dispatch(self, ev): if ev.type is KEYDOWN: if ev.key == K_UP: self.selected -= 1 elif ev.key == K_DOWN: self.selected += 1 elif ev.key == K_RETURN: for callback in self.callbacks: callback(self.selected, self.options[self.selected][1]) def draw(self, surface): for i, widget in enumerate(self.options): widget[0].draw(surface, i == self.selected)