# $Id: DefaultEngine.py,v 1.6.2.9 2007/03/23 11:57:14 marcusva Exp $ # # Copyright (c) 2006-2007, Marcus von Appen # All rights reserved. # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # * Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. """Default drawing engine.""" import os.path import pygame, pygame.transform from ocempgui.draw import Draw, String, Image from ocempgui.widgets.StyleInformation import StyleInformation from ocempgui.widgets.Constants import * class DefaultEngine (object): """DefaultEngine (style) -> DefaultEngine Default drawing engine for OcempGUI's widget system. Drawing engines are used by the widgets of OcempGUI to draw certain parts of widgets, such as the background, borders and shadows on a surface. Attributes: style - The Style instance, which makes use of the engine. """ __slots__ = ["style"] def __init__ (self, style): self.style = style def get_icon_path (self, size): """D.get_icon_path (size) -> path Gets the absolute path to the icons for a specific size. """ return os.path.join (os.path.dirname (__file__), "icons", size) def draw_rect (self, width, height, state, cls=None, style=None): """D.draw_rect (...) -> Surface Creates a rectangle surface based on the style information. The rectangle will have the passed width and height and will be filled with the 'bgcolor'[state] value of the passed style. If no style is passed, the method will try to retrieve a style using the get_style() method. """ self_style = self.style if not style: style = self_style.get_style (cls) image = self_style.get_style_entry (cls, style, "image", state) color = self_style.get_style_entry (cls, style, "bgcolor", state) surface = Draw.draw_rect (width, height, color) if image: img = pygame.transform.scale (Image.load_image (image), (width, height)) surface.blit (img, (0, 0)) return surface def draw_border (self, surface, state, cls=None, style=None, bordertype=BORDER_FLAT, rect=None, space=0): """D.draw_border (...) -> None Draws a border on the passed using a specific border type. Dependant on the border type, this method will draw a rectangle border on the passed surface. The 'rect' argument indicates the drawing area at which's edges the border will be drawn. This can differ from the real surface rect. A None value (the default) will draw the border around the surface. 'space' denotes, how many pixels will be left between each border pixel before drawing the next one. A value of 0 thus causes the method to draw a solid border while other values will draw dashed borders. The BORDER_RAISED, BORDER_SUNKEN, BORDER_ETCHED_IN and BORDER_ETCHED_OUT border types use the 'lightcolor', 'darkcolor' and 'shadow' style entries for drawing, BORDER_FLAT uses the 'bordercolor' entry. If no style is passed, the method will try to retrieve a style using the get_style() method. Raises a ValueError, if the passed bordertype argument is not a value of the BORDER_TYPES tuple. """ if bordertype not in BORDER_TYPES: raise ValueError ("bordertype must be a value from BORDER_TYPES") # Do nothing in case, that the border type is none. if bordertype == BORDER_NONE: return surface self_style = self.style if not style: style = self_style.get_style (cls) # The spacing for the lines. space == 0 actually means a solid line, # spacing == 1 a dashed one with 1px spaces, etc. # Add one pixel for the slicing later on. space += 1 # Maybe pixel3d should be used here, but it does not support 24 # bit color depths. array = pygame.surfarray.array3d (surface) # Create the area, the border should surround. # We will use the passed padding for it. r = rect if r == None: r = surface.get_rect () # Dependant on the border style, we will care about the used # colors. 3D effect such as sunken or raised make use of the # light/darkcolor style keys, the flat one uses the fgcolor. If # it is a 3D effect, we are going to use the shadow key to # determine, how many lines the distinction shall have. # # The drawing is done as follows: # * First fill the upper row of the (n,m) matrix with the given # color and color only every wanted pixel. # * The color the bottom row of the matrix in the same way. # * The left column will be colored as well. # * The same again with the right column. if bordertype == BORDER_FLAT: color = self_style.get_style_entry (cls, style, "bordercolor", state) array[r.left:r.right:space, r.top] = color array[r.left:r.right:space, r.bottom - 1] = color array[r.left, r.top:r.bottom:space] = color array[r.right - 1, r.top:r.bottom:space] = color elif bordertype in (BORDER_SUNKEN, BORDER_RAISED): shadow = self_style.get_style_entry (cls, style, "shadow") if shadow < 1: return surface # No shadow wanted. # Create the colors. color1 = self_style.get_style_entry (cls, style, "lightcolor", state) color2 = self_style.get_style_entry (cls, style, "darkcolor", state) if bordertype == BORDER_SUNKEN: color1, color2 = color2, color1 # By default we will create bevel edges, for which the # topleft colors take the most place. Thus the bottomleft # array slices will be reduced continously. for i in xrange (shadow): array[r.left + i:r.right - i:space, r.top + i] = color1 array[r.left + i:r.right - i:space, r.bottom - (i + 1)] = color2 array[r.left + i, r.top + i:r.bottom - i:space] = color1 array[r.right - (i + 1), r.top + i + 1:r.bottom - i:space] = color2 elif bordertype in (BORDER_ETCHED_IN, BORDER_ETCHED_OUT): shadow = self_style.get_style_entry (cls, style, "shadow") if shadow < 1: return surface # No shadow wanted. color1 = self_style.get_style_entry (cls, style, "lightcolor", state) color2 = self_style.get_style_entry (cls, style, "darkcolor", state) if bordertype == BORDER_ETCHED_OUT: color1, color2 = color2, color1 s = shadow # First (inner) rectangle. array[r.left + s:r.right:space, r.top + s:r.top + 2 * s] = color1 array[r.left + s:r.right:space,r.bottom - s:r.bottom] = color1 array[r.left + s:r.left + 2 * s, r.top + s:r.bottom:space] = color1 array[r.right - s:r.right, r.top + s:r.bottom:space] = color1 # Second (outer) rectangle. array[r.left:r.right - s:space, r.top:r.top + s] = color2 array[r.left:r.right - s:space, r.bottom - 2*s:r.bottom - s] = color2 array[r.left:r.left + s, r.top:r.bottom - s:space] = color2 array[r.right - 2 * s:r.right - s, r.top:r.bottom - s:space] = color2 # Blit the new surface. pygame.surfarray.blit_array (surface, array) def draw_dropshadow (self, surface, cls=None, style=None): """D.draw_dropshadow (...) -> None Draws a drop shadow on the surface. The drop shadow will be drawn on the right and bottom side of the surface and usually uses a black and grey color. """ if not style: style = self.style.get_style (cls) shadow = self.style.get_style_entry (cls, style, "shadow") color = self.style.get_style_entry (cls, style, "shadowcolor") if shadow < 2: shadow = 2 half = shadow / 2 start = max (half, 3) rect = surface.get_rect () array = pygame.surfarray.array3d (surface) # Right and bottom inner shadow. array[rect.left + start:rect.right - half, rect.bottom - shadow:rect.bottom - half] = color[0] array[rect.right - shadow:rect.right - half, rect.top + start:rect.bottom - half] = color[0] # Right and bottom outer shadow. array[rect.left + start:rect.right - half, rect.bottom - half:rect.bottom] = color[1] array[rect.right - half:rect.right, rect.top + start:rect.bottom] = color[1] pygame.surfarray.blit_array (surface, array) def draw_slider (self, width, height, state, cls=None, style=None): """D.draw_slider (...) -> Surface Creates a rectangle surface with a grip look. TODO: At the moment, this method creates a simple rectangle surface with raised border. In future versions it will create a surface with a grip look. """ if not style: style = self.style.get_style (cls) # Create the surface. surface = self.draw_rect (width, height, state, cls, style) self.draw_border (surface, state, cls, style, BORDER_RAISED) rect = surface.get_rect () return surface def draw_string (self, text, state, cls=None, style=None): """D.draw_string (...) -> Surface Creates a string surface based on the style information. Creates a transparent string surface from the provided text based on the style information. The method makes use of the 'font' style entry to determine the font and size and uses the 'fgcolor' style entry for the color. If no style is passed, the method will try to retrieve a style using the get_style() method. """ self_style = self.style if not style: style = self_style.get_style (cls) name = self_style.get_style_entry (cls, style, "font", "name") size = self_style.get_style_entry (cls, style, "font", "size") alias = self_style.get_style_entry (cls, style, "font", "alias") st = self_style.get_style_entry (cls, style, "font", "style") color = self_style.get_style_entry (cls, style, "fgcolor", state) return String.draw_string (text, name, size, alias, color, st) def draw_string_with_mnemonic (self, text, state, mnemonic, cls=None, style=None): """D.draw_string_with_mnemonic (...) -> Surface Creates a string surface with an additional underline. This method basically does the same as the draw_string() method, but additionally underlines the character specified with the 'mnemonic' index argument using the 'fgcolor' style entry.. If no style is passed, the method will try to retrieve a style using the get_style() method. """ self_style = self.style if not style: style = self_style.get_style (cls) name = self_style.get_style_entry (cls, style, "font", "name") size = self_style.get_style_entry (cls, style, "font", "size") alias = self_style.get_style_entry (cls, style, "font", "alias") st = self_style.get_style_entry (cls, style, "font", "style") fgcolor = self_style.get_style_entry (cls, style, "fgcolor", state) font = String.create_font (name, size, st) surface = String.draw_string (text, name, size, alias, fgcolor) left = font.size (text[:mnemonic]) right = font.size (text[mnemonic + 1:]) height = surface.get_rect ().height - 2 width = surface.get_rect ().width Draw.draw_line (surface, fgcolor, (left[0], height), (width - right[0], height), 1) return surface def draw_arrow (self, surface, arrowtype, state, cls=None, style=None): """D.draw_arrow (...) -> None Draws an arrow on a surface. Draws an arrow with on a surface using the passed arrowtype as arrow direction. The method uses a third of the surface width (or height for ARROW_TOP/ARROW_DOWN) as arrow width and places it on the center of the surface. It also uses the 'fgcolor' style entry as arrow color. If no style is passed, the method will try to retrieve a style using the get_style() method. Raises a ValueError, if the passed arrowtype argument is not a value of the ARROW_TYPES tuple. """ if arrowtype not in ARROW_TYPES: raise TypeError ("arrowtype must be a value of ARROW_TYPES") if not style: style = self.style.get_style (cls) color = self.style.get_style_entry (cls, style, "fgcolor", state) rect = surface.get_rect () array = pygame.surfarray.array3d (surface) if arrowtype in (ARROW_LEFT, ARROW_RIGHT): arrow_width = rect.width / 3 center = rect.centery if center % 2 == 0: center -= 1 if arrowtype == ARROW_LEFT: for i in xrange (arrow_width): col = arrow_width + i array[col:col + arrow_width - i:1, center + i] = color array[col:col + arrow_width - i:1, center - i] = color elif arrowtype == ARROW_RIGHT: for i in xrange (arrow_width): col = rect.width - arrow_width - i - 1 array[col:col - arrow_width + i:-1, center + i] = color array[col:col - arrow_width + i:-1, center - i] = color elif arrowtype in (ARROW_UP, ARROW_DOWN): arrow_height = rect.height / 3 center = rect.centerx if center % 2 == 0: center -= 1 if arrowtype == ARROW_UP: for i in xrange (arrow_height): row = arrow_height + i array[center + i, row:row + arrow_height - i:1] = color array[center - i, row:row + arrow_height - i:1] = color elif arrowtype == ARROW_DOWN: for i in xrange (arrow_height): row = rect.height - arrow_height - i - 1 array[center + i, row:row - arrow_height + i:-1] = color array[center - i, row:row - arrow_height + i:-1] = color # Blit the new surface. pygame.surfarray.blit_array (surface, array) def draw_check (self, surface, rect, checked, state, cls=None, style=None): """D.draw_check (...) -> None Creates a surface with a check box. Creates a surface with check box using a width and height of 14 pixels. The method uses a sunken border effect and makes use of the 'lightcolor' and 'darkcolor' style entries for the border. Dependant on the passed 'state' argument, the method will either use fixed color values of (255, 255, 255) for the background and (0, 0, 0) for the check, which is only drawn, if the 'checked' argument evaluates to True. If the 'state' argument is set to STATE_INSENSITIVE the 'bgcolor' and 'fgcolor' style entries are used instead. If no style is passed, the method will try to retrieve a style using the get_style() method. """ self_style = self.style if not style: style = self_style.get_style (cls) # Some colors we need. bg = (255, 255, 255) # Default background color. check = (0, 0, 0) # Check color. sh = (150, 150, 150) # Check shadow to make it look smooth. array = pygame.surfarray.array3d (surface) # Draw the borders and fill the rest. dark = self_style.get_style_entry (cls, style, "darkcolor",state) light = self_style.get_style_entry (cls, style, "lightcolor", state) array[rect.left:rect.left + 2, rect.top:rect.bottom] = dark array[rect.left:rect.right, rect.top:rect.top + 2] = dark array[rect.right - 2:rect.right, rect.top:rect.bottom] = light array[rect.left:rect.right, rect.bottom - 2:rect.bottom] = light array[rect.left, rect.bottom - 2] = dark array[rect.right - 2, rect.top] = dark if state == STATE_INSENSITIVE: array[rect.left + 2:rect.right - 2, rect.top + 2:rect.bottom - 2] = \ self_style.get_style_entry (cls, style, "bgcolor", state) check = self_style.get_style_entry (cls, style, "fgcolor", state) sh = check else: array[rect.left + 2:rect.right - 2, rect.top + 2:rect.bottom - 2] = bg if checked: # Place a check into the drawn box by direct pixel # manipulation. # TODO: provide a handy matrix for this, so it can be merged # and changed quickly and vary in size. # # 11 13 # 0 1 2 3 4 5 6 7 8 9 10 12 # ----------------------------- # 0 |* * * * * * * * * * * * * *| # 1 |* * * * * * * * * * * * * *| # 2 |* * 0 0 0 0 0 0 0 0 0 0 * *| # 3 |* * 0 0 0 0 0 0 0 0 # # * *| # 4 |* * 0 0 0 0 0 0 0 # # # * *| # 5 |* * 0 0 0 0 0 0 # # # 0 * *| # 6 |* * 0 0 # # 0 # # # 0 0 * *| # 7 |* * 0 # # # 0 # # 0 0 0 * *| # 8 |* * 0 0 # # # # # 0 0 0 * *| # 9 |* * 0 0 0 # # # 0 0 0 0 * *| # 10 |* * 0 0 0 0 0 # 0 0 0 0 * *| # 11 |* * 0 0 0 0 0 0 0 0 0 0 * *| # 12 |* * * * * * * * * * * * * *| # 13 |* * * * * * * * * * * * * *| # ----------------------------- # * = border shadow # 0 = unset # # = set with a specific color. # array[rect.left + 3, rect.top + 7] = sh array[rect.left + 4, rect.top + 6] = sh array[rect.left + 4, rect.top + 7:rect.top + 9] = check array[rect.left + 5, rect.top + 6] = sh array[rect.left + 5, rect.top + 7:rect.top + 9] = check array[rect.left + 5, rect.top + 9] = sh array[rect.left + 6, rect.top + 7] = sh array[rect.left + 6, rect.top + 8:rect.top + 10] = check array[rect.left + 7, rect.top + 6] = sh array[rect.left + 7, rect.top + 7:rect.top + 11] = check array[rect.left + 8, rect.top + 5:rect.top + 9] = check array[rect.left + 9, rect.top + 4:rect.top + 7] = check array[rect.left + 10, rect.top + 3:rect.top + 6] = check array[rect.left + 11, rect.top + 3:rect.top + 5] = sh pygame.surfarray.blit_array (surface, array) def draw_radio (self, surface, rect, checked, state, cls=None, style=None): """D.draw_radio (...) -> None Creates a surface with a radio check box. Creates a surface with radio check box using a width and height of 14 pixels. The method uses a sunken border effect and makes use of the 'lightcolor' and 'darkcolor' style entries for the border. Dependant on the passed 'state' argument, the method will either use fixed color values of (255, 255, 255) for the background and (0, 0, 0) for the check, which is only drawn, if the 'checked' argument evaluates to True. If the 'state' argument is set to STATE_INSENSITIVE the 'bgcolor' and 'fgcolor' style entries are used instead. If no style is passed, the method will try to retrieve a style using the get_style() method. """ if not style: style = self.style.get_style (cls) # We need some colors for the radio check. sh1 = (0, 0, 0) # Border topleft. sh2 = (150, 150, 150) # Border shadow top- and bottomleft. sh3 = (255, 255, 255) # Outer border shadow bottomleft. bg = (255, 255, 255) # Background color for the check. check = (0, 0, 0) # Color of the radio check. if state == STATE_INSENSITIVE: bg = self.style.get_style_entry (cls, style, "bgcolor", state) check = self.style.get_style_entry (cls, style, "fgcolor", state) sh1 = check sh2 = self.style.get_style_entry (cls, style, "fgcolor", state) sh3 = (240, 240, 240) # The complete radio check will be drawn by manipulating pixels # of the box. # TODO: provide a handy matrix for this, so it can be merged # and changed quickly and vary in size. # 11 13 # 0 1 2 3 4 5 6 7 8 9 10 12 # ----------------------------- # 0 |x x x x x x x x x x x x x x| # 1 |x x x x x * * * * x x x x x| # 2 |x x x * * s s s s s * x x x| # 3 |x x * s s 0 0 0 0 0 s * x x| # 4 |x x * s 0 0 0 0 0 0 0 * x x| # 5 |x * s 0 0 0 # # # 0 0 0 * x| # 6 |x * s 0 0 # # # # # 0 0 * 8| # 7 |x * s 0 0 # # # # # 0 0 * 8| # 8 |x * s 0 0 # # # # # 0 0 * 8| # 9 |x x s 0 0 0 # # # 0 0 0 * 8| # 10 |x x * s 0 0 0 0 0 0 0 * 8 x| # 11 |x x x * * 0 0 0 0 0 * * 8 x| # 12 |x x x x x * * * * * 8 8 x x| # 13 |x x x x x x 8 8 8 8 x x x x| # ----------------------------- # x = default background color # * = border shadow (sh2) # s = topleft border (sh1) # 0 = background color (bg) # 8 = border shadow 2 (sh3) # # = check color (check) # array = pygame.surfarray.array3d (surface) array[rect.left + 1, rect.top + 5:rect.top + 9] = sh2 array[rect.left + 2, rect.top + 3:rect.top + 5] = sh2 array[rect.left + 2, rect.top + 5:rect.top + 10] = sh1 array[rect.left + 2, rect.top + 10] = sh2 array[rect.left + 3:rect.left + 5, rect.top + 2] = sh2 array[rect.left + 3, rect.top + 3:rect.top + 5] = sh1 array[rect.left + 3:rect.left + 12, rect.top + 5:rect.top + 10] = bg array[rect.left + 3, rect.top + 10] = sh1 array[rect.left + 3:rect.left + 5, rect.top + 11] = sh2 array[rect.left + 4, rect.top + 3] = sh1 array[rect.left + 4:rect.left + 11, rect.top + 4] = bg array[rect.left + 4:rect.left + 11, rect.top + 10] = bg array[rect.left + 5:rect.left + 9, rect.top + 1] = sh2 array[rect.left + 5:rect.left + 10, rect.top + 2] = sh1 array[rect.left + 5:rect.left + 10, rect.top + 3] = bg array[rect.left + 5:rect.left + 10, rect.top + 11] = bg array[rect.left + 5:rect.left + 10, rect.top + 12] = sh2 array[rect.left + 6:rect.left + 10, rect.top + 13] = sh3 array[rect.left + 10, rect.top + 2] = sh2 array[rect.left + 10, rect.top + 3] = sh1 array[rect.left + 10:rect.left + 12, rect.top + 11] = sh2 array[rect.left + 10:rect.left + 12, rect.top + 12] = sh3 array[rect.left + 11, rect.top + 3:rect.top + 5] = sh2 array[rect.left + 11, rect.top + 10] = sh2 array[rect.left + 12, rect.top + 5:rect.top + 10] = sh2 array[rect.left + 12, rect.top + 10:rect.top + 12] = sh3 array[rect.left + 13, rect.top + 6:rect.top + 10] = sh3 if checked: array[rect.left + 5:rect.left + 10, rect.top + 6:rect.top + 9] = check array[rect.left + 6:rect.left + 9, rect.top + 5] = check array[rect.left + 6:rect.left + 9, rect.top + 9] = check pygame.surfarray.blit_array (surface, array) def draw_caption (self, width, title=None, state=STATE_NORMAL, cls=None, style=None): """D.draw_caption (...) -> Surface Creates and a caption bar suitable for Window objects. Creates a rectangle surface with a flat border and the passed 'title' text argument. The method uses a fixed color value of (124, 153, 173) for the surface. The passed 'width' will be ignored, if the size of the title text exceeds it. Instead the width of the title text plus an additional spacing of 4 pixels will be used. The height of the surface relies on the title text height plus an additional spacing of 4 pixels. If no style is passed, the method will try to retrieve a style using the get_style() method. """ if not title: title = "" if not style: style = self.style.get_style (cls) # Create the window title. surface_text = self.draw_string (title, state, cls, style) rect_text = surface_text.get_rect () # Add two pixels for the border and 2 extra ones for spacing. cap_height = rect_text.height + 4 if width < rect_text.width + 4: width = rect_text.width + 4 color = None if state == STATE_ACTIVE: color = StyleInformation.get ("CAPTION_ACTIVE_COLOR") else: color = StyleInformation.get ("CAPTION_INACTIVE_COLOR") # Create the complete surface. surface = Draw.draw_rect (width, cap_height, color) self.draw_border (surface, state, cls, style, StyleInformation.get ("CAPTION_BORDER")) surface.blit (surface_text, (2, 2)) return surface def draw_caret (self, surface, x, y, thickness, state, cls=None, style=None): """D.draw_caret (...) -> None Draws a caret line on a surface. Draws a vertical caret line onto the passed surface. The position of the caret will be set using the 'x' argument. The length of the caret line can be adjusted using the 'y' argument. The thickness of the caret line is set by the 'thickness' argument. This method makes use of the 'fgcolor' style entry for the color of the caret line. If no style is passed, the method will try to retrieve a style using the get_style() method. """ if not style: style = self.style.get_style (cls) rect = surface.get_rect () ax = (rect.topleft[0] + x, rect.topleft[1] + y) bx = (rect.bottomleft[0] + x, rect.bottomleft[1] - y) Draw.draw_line (surface, self.style.get_style_entry (cls, style, "fgcolor", state), ax, bx, thickness) def draw_label (self, label): """D.draw_label (...) -> Surface Creates the surface for the passed Label widget. """ cls = label.__class__ st = label.style width = 2 * label.padding height = 2 * label.padding labels = [] rtext = None if not label.multiline: # Draw a single line. if label.mnemonic[0] != -1: rtext = self.draw_string_with_mnemonic (label.text, label.state, label.mnemonic[0], cls, st) else: rtext = self.draw_string (label.text, label.state, cls, st) rect = rtext.get_rect () width += rect.width height += rect.height labels.append ((rtext, rect)) else: # Multiple lines. cur = 0 # Current postion marker for the mnemonic. lines = label.get_lines () mnemonic = label.mnemonic[0] last = len(lines) - 1 for i, line in enumerate(lines): if (mnemonic != -1) and (cur <= mnemonic) and \ ((cur + len (line)) > mnemonic): rtext = self.draw_string_with_mnemonic (line, label.state, mnemonic - cur, cls, st) else: rtext = self.draw_string (line, label.state, cls, st) rect = rtext.get_rect () if width < rect.width: width = rect.width + 2 * label.padding if (label.linespace != 0) and (i != last): # Do not consider linespace for the last line rect.height += label.linespace height += rect.height labels.append ((rtext, rect)) cur += len (line) + 1 # Guarantee size. width, height = label.check_sizes (width, height) surface = self.draw_rect (width, height, label.state, cls, st) blit = surface.blit posy = label.padding totalheight = 0 totalwidth = 0 for rtext, rect in labels: totalheight += rect.height totalwidth = max (totalwidth, rect.width) # Blit all in the center using centered justification. posx = (width - totalwidth) / 2 posy = (height - totalheight) / 2 for rtext, rect in labels: rect.topleft = posx, posy posy += rect.height if label.align & ALIGN_TOP == ALIGN_TOP: posy = label.padding for rtext, rect in labels: rect.top = posy posy += rect.height elif label.align & ALIGN_BOTTOM == ALIGN_BOTTOM: posy = height - label.padding - totalheight for rtext, rect in labels: rect.top = posy posy += rect.height if label.align & ALIGN_LEFT == ALIGN_LEFT: for rtext, rect in labels: rect.left = label.padding elif label.align & ALIGN_RIGHT == ALIGN_RIGHT: right = width - label.padding for rtext, rect in labels: rect.right = right else: for rtext, rect in labels: rect.left = (width - rect.width) / 2 for rtext, rect in labels: blit (rtext, rect) return surface def draw_button (self, button): """D.draw_button (...) -> Surface Creates the surface for the passed Button widget. """ cls = button.__class__ border = self.style.get_border_size (cls, button.style, button.border) active = StyleInformation.get ("ACTIVE_BORDER") border_active = self.style.get_border_size (cls, button.style, active) # Create the absolute sizes of the surface. This includes the # padding as well as the shadow and additional pixels for the # dashed border. width = 2 * (button.padding + border + border_active) height = 2 * (button.padding + border + border_active) if button.child: width += button.child.width height += button.child.height # Guarantee size. width, height = button.check_sizes (width, height) surface = self.draw_rect (width, height, button.state, cls, button.style) self.draw_border (surface, button.state, cls, button.style, button.border) # Draw a dashed border around the label, if the button has # focus. if button.focus: r = None if button.child: r = button.child.rect r.x -= border_active r.y -= border_active r.width += 2 * border_active r.height += 2 * border_active else: adj = border + 2 * border_active r = surface.get_rect () r.topleft = (adj, adj) r.width -= 2 * adj r.height -= 2 * adj self.draw_border (surface, button.state, cls,button.style, active, r, StyleInformation.get ("ACTIVE_BORDER_SPACE")) return surface def draw_checkbutton (self, button): """D.draw_checkbutton (...) -> Surface Creates the surface for the passed CheckButton widget. """ cls = button.__class__ active = StyleInformation.get ("ACTIVE_BORDER") border = self.style.get_border_size (cls, button.style, active) checksize = StyleInformation.get ("CHECK_SIZE") # Create the absolute sizes of the surface, including the # padding. width = 2 * (button.padding + border) height = 2 * (button.padding + border) if button.child: width += button.child.width height += button.child.height # Create check box. rect_check = pygame.Rect (button.padding, button.padding, checksize, checksize) # The layout looks like: # ---------------- # | X | child | # ---------------- # Check Child # Thus we have to add a specific spacing between the child and the # check. By default we will use the given StyleInformation value. width += rect_check.width + StyleInformation.get ("CHECK_SPACING") if height < rect_check.height: # Do not forget to add the padding! height = rect_check.height + 2 * button.padding # Guarantee size. width, height = button.check_sizes (width, height) # The surface on which both components will be placed. surface = self.draw_rect (width, height, button.state, cls, button.style) rect_surface = surface.get_rect () # Draw the check on the surface. rect_check.centery = rect_surface.centery self.draw_check (surface, rect_check, button.active, button.state, cls, button.style) # Draw a dashed border around the label, if the button has # focus. if button.focus: r = None if button.child: r = button.child.rect r.x -= border r.y -= border r.width += 2 * border r.height += 2 * border else: r = rect_surface r.topleft = (border, border) r.width -= 2 * border r.height -= 2 * border self.draw_border (surface, button.state, cls, button.style, active, r, StyleInformation.get ("ACTIVE_BORDER_SPACE")) return surface def draw_entry (self, entry): """D.draw_entry (...) -> Surface Creates the surface for the passed Entry widget. """ cls = entry.__class__ border = self.style.get_border_size (cls, entry.style, entry.border) # Peek the style so we can calculate the font. st = entry.style or self.style.get_style (cls) fn = self.style.get_style_entry (cls, st, "font", "name") sz = self.style.get_style_entry (cls, st, "font", "size") fs = self.style.get_style_entry (cls, st, "font", "style") font = String.create_font (fn, sz, fs) height = font.get_height () + 2 * (entry.padding + border) width, height = entry.check_sizes (0, height) # Main surface. surface = self.draw_rect (width, height, entry.state, cls, entry.style) self.draw_border (surface, entry.state, cls, entry.style, entry.border) return surface def draw_imagebutton (self, button): """D.draw_imagebutton (button) -> Surface Creates the surface for the passed ImageButton widget. """ cls = button.__class__ border = self.style.get_border_size (cls, button.style, button.border) active = StyleInformation.get ("ACTIVE_BORDER") border_active = self.style.get_border_size (cls, button.style, active) spacing = StyleInformation.get ("IMAGEBUTTON_SPACING") width = 2 * (button.padding + border + border_active) height = 2 * (button.padding + border + border_active) rect_child = None if button.child: rect_child = button.child.rect width += button.child.width height += button.child.height rect_img = None if button.picture: rect_img = button.picture.get_rect () width += rect_img.width if button.child: width += spacing needed_he = rect_img.height + \ 2 * (button.padding + border + border_active) if height < needed_he: height = needed_he # Guarantee size. width, height = button.check_sizes (width, height) surface = self.draw_rect (width, height, button.state, cls, button.style) rect = surface.get_rect () self.draw_border (surface, button.state, cls, button.style, button.border) # Dashed border. if button.focus: r = None if rect_img and rect_child: rect_img.center = rect.center rect_img.right -= button.child.rect.width / 2 + spacing rect_child.center = rect.center rect_child.left = rect_img.right + spacing r = rect_img.union (rect_child) elif rect_img: rect_img.center = rect.center r = rect_img elif rect_child: rect_child.center = rect.center r = rect_child if r: r.x -= border_active r.y -= border_active r.width += 2 * border_active r.height += 2 * border_active else: adj = border + 2 * border_active r = rect r.topleft = (adj, adj) r.width -= 2 * adj r.height -= 2 * adj self.draw_border (surface, button.state, cls, button.style, active, r, StyleInformation.get ("ACTIVE_BORDER_SPACE")) return surface def draw_radiobutton (self, button): """D.draw_radiobutton (button) -> Surface Creates the surface for the passed RadioButton widget. """ cls = button.__class__ active = StyleInformation.get ("ACTIVE_BORDER") border = self.style.get_border_size (cls, button.style, active) radiosize = StyleInformation.get ("RADIO_SIZE") # Create the absolute sizes of the surface, including the # padding and. width = 2 * (button.padding + border) height = 2 * (button.padding + border) if button.child: width += button.child.width height += button.child.height # Create radio box. rect_radio = pygame.Rect (button.padding, button.padding, radiosize, radiosize) # The layout looks like: # ---------------- # | X | child | # ---------------- # Check Child # Thus we have to add a specific spacing between the child and the # check. By default we will use the given StyleInformation value. width += rect_radio.width + StyleInformation.get ("RADIO_SPACING") if height < rect_radio.height: # Do not forget to add the padding! height = rect_radio.height + 2 * button.padding # Guarantee size. width, height = button.check_sizes (width, height) # The surface on which both components will be placed. surface = self.draw_rect (width, height, button.state, cls, button.style) rect_surface = surface.get_rect () rect_radio.centery = rect_surface.centery self.draw_radio (surface, rect_radio, button.active, button.state, cls, button.style) # Draw a dashed border around the label, if the button has # focus. if button.focus: r = None if button.child: r = button.child.rect r.x -= border r.y -= border r.width += 2 * border r.height += 2 * border else: r = rect_surface r.topleft = (border, border) r.width -= 2 * border r.height -= 2 * border self.draw_border (surface, button.state, cls, button.style, active, r, StyleInformation.get ("ACTIVE_BORDER_SPACE")) return surface def draw_progressbar (self, bar): """D.draw.progressbar (...) -> Surface Creates the surface for the passed ProgressBar widget. """ cls = bar.__class__ border_type = StyleInformation.get ("PROGRESSBAR_BORDER") border = self.style.get_border_size (cls, bar.style, border_type) st = bar.style or self.style.get_style (cls) # Guarantee size. width, height = bar.check_sizes (0, 0) surface = self.draw_rect (width, height, bar.state, cls, st) # Status area. width -= 2 * border height -= 2 * border width = int (width * bar.value / 100) # Draw the progress. sf_progress = Draw.draw_rect (width, height, StyleInformation.get ("PROGRESS_COLOR")) surface.blit (sf_progress, (border, border)) self.draw_border (surface, bar.state, cls, st, border_type) return surface def draw_frame (self, frame): """D.draw_frame (...) -> Surface Creates the surface for the passed Frame widget. """ cls = frame.__class__ # Guarantee size. width, height = frame.calculate_size () width, height = frame.check_sizes (width, height) surface = self.draw_rect (width, height, frame.state, cls, frame.style) rect = surface.get_rect () if frame.widget: rect.y = frame.widget.height / 2 rect.height -= frame.widget.height / 2 self.draw_border (surface, frame.state, cls, frame.style, frame.border, rect) return surface def draw_table (self, table): """D.draw_table (...) -> Surface Creates the surface for the passed Table widget. """ cls = table.__class__ width, height = table.calculate_size () width, height = table.check_sizes (width, height) return self.draw_rect (width, height, table.state, cls, table.style) def draw_scale (self, scale, orientation): """D.draw_scale (...) -> Surface Creates the surface for the passed Scale widget. If the passed orientation argument is ORIENTATION_VERTICAL, the vertical slider information (VSCALE_SLIDER_SIZE) is used, otherwise the horizontal (HSCALE_SLIDER_SIZE). """ cls = scale.__class__ # Use a default value for the slider, if not set in the style. slider = None if orientation == ORIENTATION_VERTICAL: slider = StyleInformation.get ("VSCALE_SLIDER_SIZE") else: slider = StyleInformation.get ("HSCALE_SLIDER_SIZE") width, height = scale.check_sizes (0, 0) if width < slider[0]: width = slider[0] if height < slider[1]: height = slider[1] # The slider line in the middle will be a third of the complete # width/height. To center it correctly, we need an odd value. if (orientation == ORIENTATION_VERTICAL) and (height % 2 == 0): width += 1 elif width % 2 == 0: height += 1 # Main surface to draw on. We do not want to have any resizing, # thus we are doing this in two steps. surface = self.draw_rect (width, height, scale.state, cls, scale.style) rect = None if orientation == ORIENTATION_VERTICAL: r = pygame.Rect (width / 3, 0, width / 3, height) r.centery = height / 2 else: r = pygame.Rect (0, height / 3, width, height /3) r.centerx = width / 2 self.draw_border (surface, scale.state, cls, scale.style, StyleInformation.get ("SCALE_BORDER"), r) return surface def draw_scrollbar (self, scrollbar, orientation): """D.draw_scrollbar (...) -> Surface Creates the surface for the passed ScrollBar widget. If the passed orientation argument is ORIENTATION_VERTICAL, the vertical slider information (VSCALE_SLIDER_SIZE) is used, otherwise the horizontal (HSCALE_SLIDER_SIZE). """ cls = scrollbar.__class__ border_type = StyleInformation.get ("SCROLLBAR_BORDER") border = self.style.get_border_size (cls, scrollbar.style, border_type) # We use a temporary state here, so that just the buttons will # have the typical sunken effect. tmp_state = scrollbar.state if scrollbar.state == STATE_ACTIVE: tmp_state = STATE_NORMAL # Guarantee size. width, height = scrollbar.check_sizes (0, 0) size_button = (0, 0) if orientation == ORIENTATION_VERTICAL: size_button = StyleInformation.get ("VSCROLLBAR_BUTTON_SIZE") if width < (size_button[0] + 2 * border): width = size_button[0] + 2 * border if height < 2 * (size_button[1] + border): height = 2 * (size_button[1] + 2 * border) else: size_button = StyleInformation.get ("HSCROLLBAR_BUTTON_SIZE") if width < 2 * (size_button[0] + border): width = 2 * (size_button[0] + border) if height < (size_button[1] + 2 * border): height = size_button[1] + 2 * border surface = self.draw_rect (width, height, tmp_state, cls, scrollbar.style) self.draw_border (surface, tmp_state, cls, scrollbar.style, border_type) return surface def draw_scrolledwindow (self, window): """D.draw_scrolledwindow (...) -> Surface Creates the Surface for the passed ScrolledWindow widget. """ cls = window.__class__ width, height = window.check_sizes (0, 0) surface = self.draw_rect (width, height, window.state, cls, window.style) return surface def draw_viewport (self, viewport): """D.draw_viewport (...) -> Surface Creates the Surface for the passed ViewPort widget. """ cls = viewport.__class__ width, height = viewport.check_sizes (0, 0) surface = self.draw_rect (width, height, viewport.state, cls, viewport.style) self.draw_border (surface, viewport.state, cls, viewport.style, StyleInformation.get ("VIEWPORT_BORDER")) return surface def draw_statusbar (self, statusbar): """D.draw_statusbar (...) -> Surface Creates the Surface for the passed StatusBar widget. """ cls = statusbar.__class__ border_type = StyleInformation.get ("STATUSBAR_BORDER") # Guarantee size. width, height = statusbar.calculate_size () width, height = statusbar.check_sizes (width, height) # Create the main surface surface = self.draw_rect (width, height, statusbar.state, cls, statusbar.style) self.draw_border (surface, statusbar.state, cls, statusbar.style, border_type) return surface def draw_imagemap (self, imagemap): """D.draw_imagemap (...) -> Surface Creates the Surface for the passed ImageMap widget. """ cls = imagemap.__class__ border_type = StyleInformation.get ("IMAGEMAP_BORDER") border = self.style.get_border_size (cls, imagemap.style, border_type) rect_image = imagemap.picture.get_rect () width = rect_image.width + 2 * border height = rect_image.height + 2 * border # Guarantee size. width, height = imagemap.check_sizes (width, height) surface = self.draw_rect (width, height, imagemap.state, cls, imagemap.style) self.draw_border (surface, imagemap.state, cls, imagemap.style, border_type) return surface def draw_textlistitem (self, viewport, item): """D.draw_textlistitem (...) -> Surface Creates the Surface for the passed TextListItem. """ text = item.text or "" return self.draw_string (text, viewport.state, item.__class__, item.style) def draw_filelistitem (self, viewport, item): """D.draw_filelistitem (...) -> Surface Creates the Surface for the passed FileListItem. """ style = viewport.style or self.style.get_style (viewport.__class__) color = StyleInformation.get ("SELECTION_COLOR") style = item.style if not style: style = self.style.get_style (item.__class__) rect_icon = None if item.icon: rect_icon = item.icon.get_rect () else: rect_icon = pygame.Rect (0, 0, 0, 0) sf_text = self.draw_textlistitem (viewport, item) rect_text = sf_text.get_rect () width = rect_icon.width + 2 + rect_text.width height = rect_icon.height if height < rect_text.height: height = rect_text.height surface = None if item.selected: surface = Draw.draw_rect (width, height, color) else: surface = self.draw_rect (width, height, viewport.state, item.__class__, item.style) rect = surface.get_rect () if item.icon: rect_icon.centery = rect.centery surface.blit (item.icon, rect_icon) rect_text.centery = rect.centery surface.blit (sf_text, (rect_icon.width + 2, rect_text.y)) return surface def draw_window (self, window): """D.draw_window (...) -> Surface Creates the Surface for the passed Window. """ cls = window.__class__ style = window.style or self.style.get_style (cls) border_type = StyleInformation.get ("WINDOW_BORDER") border = self.style.get_border_size (cls, style, border_type) dropshadow = self.style.get_style_entry (cls, style, "shadow") width = 2 * (window.padding + border) + dropshadow height = 2 * (window.padding + border) + dropshadow if window.child: width += window.child.width height += window.child.height width, height = window.check_sizes (width, height) # Calculate the height of the caption bar. fn = self.style.get_style_entry (cls, style, "font", "name") sz = self.style.get_style_entry (cls, style, "font", "size") fs = self.style.get_style_entry (cls, style, "font", "style") font = String.create_font (fn, sz, fs) height_caption = font.get_height () + 2 * self.style.get_border_size \ (cls, style, StyleInformation.get ("CAPTION_BORDER")) height += height_caption # Create the surface. surface = self.draw_rect (width, height, window.state, cls, style) self.draw_border (surface, window.state, cls, style, border_type, pygame.Rect (0, 0, width - dropshadow, height - dropshadow)) if dropshadow > 0: self.draw_dropshadow (surface, cls, style) return surface def draw_graph2d (self, graph): """D.draw_graph2d (...) -> Surface Creates the Surface for the passed Graph2D """ cls = graph.__class__ width, height = graph.check_sizes (0, 0) surface = self.draw_rect (width, height, graph.state, cls, graph.style) # Draw dashed border, if focused. if graph.focus: self.draw_border (surface, graph.state, cls, graph.style, StyleInformation.get ("ACTIVE_BORDER"), space = \ StyleInformation.get ("ACTIVE_BORDER_SPACE")) return surface def draw_imagelabel (self, label): """D.draw_imagelabel (...) -> Surface Creates the surface for the passed ImageLabel widget. """ cls = label.__class__ border = self.style.get_border_size (cls, label.style, label.border) width = 2 * (border + label.padding) height = 2 * (border + label.padding) rect_img = label.picture.get_rect () width += rect_img.width height += rect_img.height # Guarantee size. width, height = label.check_sizes (width, height) surface = self.draw_rect (width, height, label.state, cls, label.style) self.draw_border (surface, label.state, cls, label.style, label.border) return surface def draw_tooltipwindow (self, window): """D.draw_tooltipwindow (...) -> Surface Creates the surface for the passed TooltipWindow widget. """ cls = window.__class__ style = window.style or self.style.get_style (cls) border_type = StyleInformation.get ("TOOLTIPWINDOW_BORDER") border = self.style.get_border_size (cls, style, border_type) color = StyleInformation.get ("TOOLTIPWINDOW_COLOR") width = 2 * (window.padding + border) height = 2 * (window.padding + border) rtext = self.draw_string (window.text, window.state, cls, style) rect = rtext.get_rect () width += rect.width height += rect.height # Guarantee size. width, height = window.check_sizes (width, height) surface = Draw.draw_rect (width, height, color) surface.blit (rtext, (window.padding, window.padding)) self.draw_border (surface, window.state, cls, style, border_type) return surface def draw_box (self, box): """D.draw_box (...) -> Surface Creates the surface for the passed Box widget. """ cls = box.__class__ # Guarantee size. width, height = box.check_sizes (0, 0) return self.draw_rect (width, height, box.state, cls, box.style) def draw_alignment (self, alignment): """D.draw_alignment (...) -> Surface Creates the surface for the passed Alignment widget. """ # Guarantee size. width, height = 0, 0 if alignment.child: width += alignment.child.width height += alignment.child.height width, height = alignment.check_sizes (width, height) return self.draw_rect (width, height, alignment.state, alignment.__class__, alignment.style)