#!/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"]))