############################################################################################### # $Id: dialog,v 1.1.1.1 2002/02/19 10:16:07 slmm Exp $ ############################################################################################### import pygame import pygame.display import pygame.surface import pygame.key from pygame.locals import * from widget import Widget import widget import properties class EditField(Widget): """ This class defines a simple edit field where the user may input some text while the widget has focus. """ # static images for the frame around the editable text frameicons = {} # names for the icons frameiconnames = [ 'topleft', 'top', 'topright', 'right', 'botright', 'bot', 'botleft', 'left' ] def __init__ (self, font, text="", width=100, position = (0,0), callbacks = None, color = (255,255,255), background = (0,0,0)): """Initializes the widget. Renders the font using the passed data.""" # firts call superclass constructor Widget.__init__ (self, position, callbacks) # store the text if text == None: self.text = "" else: self.text = text # store the cursor position self.cursor = len ( self.text ) # store all needed data so that we can create new surfaces later self.font = font self.color = color self.background = background self.width = width # render the text self.textrendered = font.render ( text, 1, color ) # create the background surface to fit the rendered text's height self.textsurface = pygame.Surface ( ( width, self.textrendered.get_height () + 3), HWSURFACE ).convert () # fill with the proper background color self.textsurface.fill ( background ) # create the surface with the border in it self.createBorder () # set our internal callbacks so that we can trap keys self.internal = {widget.KEYDOWN : self.keyDown } # define the valid keys we react to self.valid_keys = [ K_BACKSPACE, K_SPACE, K_EXCLAIM, K_QUOTEDBL, K_HASH, K_DOLLAR, K_AMPERSAND, K_QUOTE, K_LEFTPAREN, K_RIGHTPAREN, K_ASTERISK, K_PLUS, K_COMMA, K_MINUS, K_PERIOD, K_SLASH, K_0, K_1, K_2, K_3, K_4, K_5, K_6, K_7, K_8, K_9, K_COLON, K_SEMICOLON, K_LESS, K_EQUALS, K_GREATER, K_QUESTION, K_AT, K_LEFTBRACKET, K_BACKSLASH, K_RIGHTBRACKET, K_CARET, K_UNDERSCORE, K_BACKQUOTE, K_a, K_b, K_c, K_d, K_e, K_f, K_g, K_h, K_i, K_j, K_k, K_l, K_m, K_n, K_o, K_p, K_q, K_r, K_s, K_t, K_u, K_v, K_w, K_x, K_y, K_z, K_DELETE ] self.translations = { K_SPACE : ' ' } def setText (self, text): """Sets a new text to be rendered.""" # store new text if text == None: self.text = "" else: self.text = text # render a new surface self.textrendered = self.font.render_blended ( self.text, self.color ) def getText (self): """Returns the text of the editfield. The text is never None, but may be empty if the editfield was cleared.""" return self.text def paint (self, destination, force=0): """Method that paints the editfield. This method will simply blit out the surface of the widget onto the destination surface. """ # are we dirty or not? if not self.dirty and not force: # not dirty, nothing to do here return 0 # we're dirty, blit out the frame first destination.blit ( self.surface, (self.position [0], self.position [1]) ) # now the text background destination.blit ( self.textsurface, (self.position [0] + self.deltax, self.position [1] + self.deltay) ) # and the text if we have any if self.textrendered: # yep, we have it, get the width we should use if self.width < self.textrendered.get_size ()[0] + 3: width = self.width - 3 else: width = self.textrendered.get_size ()[0] # now blit out the text destination.blit ( self.textrendered, (self.position [0] + self.deltax + 3, self.position [1] + self.deltay+ 3) ) self.dirty = 0 # we did something, make sure the widget manager knows that return 1 def keyDown (self, event): """Callback triggered when the user presses a key inside the editfield. Gets the pressed key and modifies the internal string if needed. Renders a new surface too if needed.""" # get the table of pressed keys table = pygame.key.get_pressed () # handle backspace if table[K_BACKSPACE] == 1: # remove one character self.text = self.text [0:self.cursor - 1] # update cursor position self.cursor -= 1 elif table[K_DELETE] == 1: # remove one character from the right print "editfield.keyDown(): K_DELETE not handled" return else: # loop over all the pressed keys for key in range( len(table) ): # is the key pressed? if table[key] == 0: continue # do we have a translation for this key? if self.translations.has_key ( key ): # yep, use it instead of the direct value value = self.translations [key] else: # get the string value for the key value = pygame.key.name ( key ) # merge in the text self.text = self.text [0:self.cursor] + value + self.text[self.cursor:] # update cursor position self.cursor += 1 # do we still have any text left? if self.text == "": self.textrendered = None self.cursor = 0 else: # set the new surface self.textrendered = self.font.render ( self.text, 1, self.color ) # we're dirty now self.dirty = 1 def createBorder (self): """Creates the border for the widget. Uses the 8 static icons and creates a surface that is slightly larger than the text surface (self.textsurface). This method is one ugly thing of coordinates and offsets. Don't touch unless you know what you do.""" # load the frame icons self.loadFrameIcons () # get the delta values. These are used so that we know where to paint the text surface self.deltax = EditField.frameicons ['left'].get_width() self.deltay = EditField.frameicons ['top'].get_height() # get height and width of new surface size = self.textsurface.get_size () width = size[0] + self.deltax + EditField.frameicons ['right'].get_width() height = size[1] + self.deltay + EditField.frameicons ['bot'].get_height() # create the needed surface self.surface = pygame.Surface ( (width, height), HWSURFACE ).convert () # now some gory details. We need to blit stuff onto our frame surface to create the actual # frame. We first blit out the borders and the the corners. the corners then overpaint the # borders at proper positions top = EditField.frameicons ['top'] bot = EditField.frameicons ['bot'] left = EditField.frameicons ['left'] right = EditField.frameicons ['right'] topleft = EditField.frameicons ['topleft'] topright = EditField.frameicons ['topright'] botleft = EditField.frameicons ['botleft'] botright = EditField.frameicons ['botright'] # top/bottom borders x = 0 while x < width: # what width should be used? if x + top.get_size ()[0] < width: # full width of icon widthx = top.get_size ()[0] else: # use only the missing part widthx = width - x # blit it all out self.surface.blit ( top, ( x, 0)) self.surface.blit ( bot, ( x, height - bot.get_height () + 1 ) ) x += widthx # left/right borders y = 0 while y < height: # what height should be used? if y + left.get_size ()[1] < height: # full height of icon heighty = left.get_size ()[1] else: # use only the missing part heighty = height - y # blit it all out self.surface.blit ( left, ( 0, y )) self.surface.blit ( right, ( width - right.get_size ()[0], y )) y += heighty # top left corner self.surface.blit ( topleft, ( 0, 0 )) # top right corner self.surface.blit ( topright, ( width - topright.get_width (), 0 )) # bottom left corner self.surface.blit ( botleft, ( 0, height - botleft.get_height () )) # bottom right corner self.surface.blit ( botright, ( width - botright.get_width(), height - botright.get_height())) def loadFrameIcons (self): """Loads the needed icons for the frame from files. The icons are stored in static members so that all instances can share the same icons.""" # do we already have icons loaded? if EditField.frameicons != {}: return # base filename base = properties.path_dialogs + 'dialog-widgetFrm-' # loop and read all the icons for name in EditField.frameiconnames: # create the filename filename = base + name + '.png' # load it icon = pygame.image.load ( filename ).convert () # set transparency and store in the map icon.set_colorkey ( (255,255,255), RLEACCEL ) EditField.frameicons [name] = icon # Local Variables: # mode: auto-fill # fill-column: 100 # End: