| Total Complexity | 208 | 
| Total Lines | 1003 | 
| Duplicated Lines | 7.58 % | 
| Changes | 0 | ||
Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like pygameui.SkinableTheme often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
| 1 | #  | 
            ||
| 2 | # Copyright 2001 - 2016 Ludek Smid [http://www.ospace.net/]  | 
            ||
| 3 | #  | 
            ||
| 4 | # This file is part of Pygame.UI.  | 
            ||
| 5 | #  | 
            ||
| 6 | # Pygame.UI is free software; you can redistribute it and/or modify  | 
            ||
| 7 | # it under the terms of the Lesser GNU General Public License as published by  | 
            ||
| 8 | # the Free Software Foundation; either version 2.1 of the License, or  | 
            ||
| 9 | # (at your option) any later version.  | 
            ||
| 10 | #  | 
            ||
| 11 | # Pygame.UI is distributed in the hope that it will be useful,  | 
            ||
| 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of  | 
            ||
| 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the  | 
            ||
| 14 | # Lesser GNU General Public License for more details.  | 
            ||
| 15 | #  | 
            ||
| 16 | # You should have received a copy of the Lesser GNU General Public License  | 
            ||
| 17 | # along with Pygame.UI; if not, write to the Free Software  | 
            ||
| 18 | # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA  | 
            ||
| 19 | #  | 
            ||
| 20 | |||
| 21 | import string  | 
            ||
| 22 | import pygame  | 
            ||
| 23 | import Const  | 
            ||
| 24 | import Fonts  | 
            ||
| 25 | import os, os.path, sys, ConfigParser  | 
            ||
| 26 | from ige import log  | 
            ||
| 27 | import time  | 
            ||
| 28 | import resources  | 
            ||
| 29 | |||
| 30 | try:  | 
            ||
| 31 | import _winreg  | 
            ||
| 32 | except ImportError:  | 
            ||
| 33 | pass  | 
            ||
| 34 | |||
| 35 | skinDir = file = os.path.join(os.path.dirname(sys.modules[__name__].__file__), "DefaultSkin")  | 
            ||
| 36 | config = None  | 
            ||
| 37 | boxes = {} | 
            ||
| 38 | sounds = {} | 
            ||
| 39 | gridParams = None  | 
            ||
| 40 | soundEnabled = True  | 
            ||
| 41 | soundVolume = True  | 
            ||
| 42 | musicEnabled = True  | 
            ||
| 43 | musicVolume = True  | 
            ||
| 44 | themeMusic = None  | 
            ||
| 45 | |||
| 46 | class Box:  | 
            ||
| 47 | """Holds all boxed graphics"""  | 
            ||
| 48 | pass  | 
            ||
| 49 | |||
| 50 | def init():  | 
            ||
| 51 | global themeMusic  | 
            ||
| 52 | try:  | 
            ||
| 53 |         themeMusic = config.get("general","music") | 
            ||
| 54 | except ConfigParser.Error:  | 
            ||
| 55 | themeMusic = None  | 
            ||
| 56 | pass  | 
            ||
| 57 | # setSkin(skinDir)  | 
            ||
| 58 | |||
| 59 | def initMixer():  | 
            ||
| 60 | global soundEnabled  | 
            ||
| 61 | global musicEnabled  | 
            ||
| 62 | if (soundEnabled == False) and (musicEnabled == False):  | 
            ||
| 63 | pygame.mixer.init(44100, -16, 2, 4096)  | 
            ||
| 64 | |||
| 65 | def closeMixer():  | 
            ||
| 66 | global soundEnabled  | 
            ||
| 67 | global musicEnabled  | 
            ||
| 68 | if (soundEnabled == False) and (musicEnabled == False):  | 
            ||
| 69 | pygame.mixer.quit()  | 
            ||
| 70 | |||
| 71 | def enableSound(enable):  | 
            ||
| 72 | global soundEnabled  | 
            ||
| 73 | if (enable == True) :  | 
            ||
| 74 | initMixer()  | 
            ||
| 75 | soundEnabled = enable  | 
            ||
| 76 | if (enable == False) :  | 
            ||
| 77 | closeMixer()  | 
            ||
| 78 | |||
| 79 | def setVolume(volume):  | 
            ||
| 80 | global soundVolume  | 
            ||
| 81 | soundVolume = volume  | 
            ||
| 82 | |||
| 83 | def enableMusic(enable):  | 
            ||
| 84 | global musicEnabled  | 
            ||
| 85 | if (enable == True) :  | 
            ||
| 86 | initMixer()  | 
            ||
| 87 | else:  | 
            ||
| 88 | closeMixer()  | 
            ||
| 89 | musicEnabled = enable  | 
            ||
| 90 | if musicEnabled == True:  | 
            ||
| 91 | loadMusic(None)  | 
            ||
| 92 | playMusic()  | 
            ||
| 93 | else:  | 
            ||
| 94 | stopMusic()  | 
            ||
| 95 | time.sleep(1)  | 
            ||
| 96 | closeMixer()  | 
            ||
| 97 | |||
| 98 | def setMusicVolume(volume):  | 
            ||
| 99 | global musicVolume  | 
            ||
| 100 | global musicEnabled  | 
            ||
| 101 | musicVolume = volume  | 
            ||
| 102 | try:  | 
            ||
| 103 | if musicEnabled :  | 
            ||
| 104 | pygame.mixer.music.set_volume(volume)  | 
            ||
| 105 | except:  | 
            ||
| 106 |         log.warning("Cannot set music volume") | 
            ||
| 107 | |||
| 108 | def setSkin(directory = skinDir):  | 
            ||
| 109 | global skinDir, config, gridParams  | 
            ||
| 110 | skinDir = directory  | 
            ||
| 111 | # load skin specification  | 
            ||
| 112 | config = ConfigParser.ConfigParser()  | 
            ||
| 113 | config.read(os.path.join(skinDir, "config.ini"))  | 
            ||
| 114 | # grid  | 
            ||
| 115 |     w, h = config.get("general", "grid").split(",") | 
            ||
| 116 | gridParams = (int(w), int(h))  | 
            ||
| 117 | # basic colors  | 
            ||
| 118 | global themeForeground, themeBackground, themeTitleLine1, themeTitleLine2  | 
            ||
| 119 | global themeHighlightbck, themeHighlightfrg, themeCritical, themeMajor  | 
            ||
| 120 | global themeMinor, themeNone, themeDisabled, themeIcons  | 
            ||
| 121 | global themeMusic  | 
            ||
| 122 |     themeForeground = hex2color(config.get("general", "foreground")) | 
            ||
| 123 |     themeIcons = hex2color(config.get("general", "icons")) | 
            ||
| 124 |     themeBackground = hex2color(config.get("general", "background")) | 
            ||
| 125 |     themeTitleLine1 = hex2color(config.get("general", "line1")) | 
            ||
| 126 |     themeTitleLine2 = hex2color(config.get("general", "line2")) | 
            ||
| 127 |     themeHighlightbck = hex2color(config.get("general", "highlightbck")) | 
            ||
| 128 |     themeHighlightfrg = hex2color(config.get("general", "highlightfrg")) | 
            ||
| 129 |     themeCritical = hex2color(config.get("general", "critical")) | 
            ||
| 130 |     themeMajor = hex2color(config.get("general", "major")) | 
            ||
| 131 |     themeMinor = hex2color(config.get("general", "minor")) | 
            ||
| 132 |     themeNone = hex2color(config.get("general", "none")) | 
            ||
| 133 |     themeDisabled = hex2color(config.get("general", "disabled")) | 
            ||
| 134 | try:  | 
            ||
| 135 |         themeMusic = config.get("general","music") | 
            ||
| 136 | except ConfigParser.Error:  | 
            ||
| 137 | themeMusic = None  | 
            ||
| 138 | # create elements  | 
            ||
| 139 | createFont()  | 
            ||
| 140 | for section in config.sections():  | 
            ||
| 141 |         if section.endswith("box"): | 
            ||
| 142 | createBox(section)  | 
            ||
| 143 | |||
| 144 | def createFont():  | 
            ||
| 145 | # create font  | 
            ||
| 146 |     for fontName in config.get("general", "fonts").split(","): | 
            ||
| 147 | section = "%s font" % fontName  | 
            ||
| 148 | fontType = config.get(section, "type")  | 
            ||
| 149 |         log.debug("Loading font", fontName, fontType) | 
            ||
| 150 | if fontType == "windowsttf":  | 
            ||
| 151 | if os.name == "nt":  | 
            ||
| 152 | # get "Fonts" folder location  | 
            ||
| 153 | handle = _winreg.OpenKey(_winreg.HKEY_CURRENT_USER, 'Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders')  | 
            ||
| 154 | path, valueType = _winreg.QueryValueEx(handle, 'Fonts')  | 
            ||
| 155 | handle.Close()  | 
            ||
| 156 | filename = os.path.join(path, config.get(section, "file"))  | 
            ||
| 157 | else:  | 
            ||
| 158 | continue  | 
            ||
| 159 | elif fontType == "ttf":  | 
            ||
| 160 | filename = resources.get(config.get(section, "file"))  | 
            ||
| 161 | elif fontType == "default":  | 
            ||
| 162 | filename = None  | 
            ||
| 163 | # load font  | 
            ||
| 164 | if filename == None or os.path.exists(filename):  | 
            ||
| 
                                                                                                    
                        
                         | 
                |||
| 165 |             Fonts.initFont('small', filename, config.getint(section, "small")) | 
            ||
| 166 |             Fonts.initFont('small-bold', filename, config.getint(section, "small"), bold = 1) | 
            ||
| 167 |             Fonts.initFont('small-italic', filename, config.getint(section, "small"), italic = 1) | 
            ||
| 168 |             Fonts.initFont('normal', filename, config.getint(section, "normal")) | 
            ||
| 169 |             Fonts.initFont('normal-bold', filename, config.getint(section, "normal"), bold = 1) | 
            ||
| 170 |             Fonts.initFont('normal-italic', filename, config.getint(section, "normal"), italic = 1) | 
            ||
| 171 |             Fonts.initFont('large', filename, config.getint(section, "large")) | 
            ||
| 172 |             Fonts.initFont('large-bold', filename, config.getint(section, "large"), bold = 1) | 
            ||
| 173 |             Fonts.initFont('large-italic', filename, config.getint(section, "large"), italic = 1) | 
            ||
| 174 | return  | 
            ||
| 175 | |||
| 176 | def createBox(section):  | 
            ||
| 177 | global boxes  | 
            ||
| 178 | boxName = section[:-4]  | 
            ||
| 179 | for option in config.options(section):  | 
            ||
| 180 |         if option.startswith("sound-"): | 
            ||
| 181 | createSounds(section, option)  | 
            ||
| 182 | continue  | 
            ||
| 183 | box = Box()  | 
            ||
| 184 |         opt = config.get(section, option).split(",") | 
            ||
| 185 | filename, x, y, r, b = opt[:5]  | 
            ||
| 186 | foreground, tl, tt, tr, tb = opt[5:]  | 
            ||
| 187 | if foreground == "-":  | 
            ||
| 188 | box.foreground = None  | 
            ||
| 189 | else:  | 
            ||
| 190 | box.foreground = hex2color(foreground)  | 
            ||
| 191 | box.cMargins = int(tl), int(tt), int(tr), int(tb)  | 
            ||
| 192 | # graphical reprezentation  | 
            ||
| 193 | x, y, r, b = int(x), int(y), int(r), int(b)  | 
            ||
| 194 | box.img = pygame.image.load(os.path.join(skinDir, filename)).convert_alpha()  | 
            ||
| 195 | w, h = box.img.get_size()  | 
            ||
| 196 | box.margins = x, y, r, b  | 
            ||
| 197 | box.rect = pygame.Rect(x, y, w - r, h - b)  | 
            ||
| 198 | box.topleft = box.img.subsurface(0, 0, x, y)  | 
            ||
| 199 | box.bottomleft = box.img.subsurface(0, h - b, x, b)  | 
            ||
| 200 | box.topright = box.img.subsurface(w - r, 0, r, y)  | 
            ||
| 201 | box.bottomright = box.img.subsurface(w - r, h - b, r, b)  | 
            ||
| 202 | box.top = box.img.subsurface(x, 0, w - r - x, y)  | 
            ||
| 203 | box.bottom = box.img.subsurface(x, h - b, w - r - x, b)  | 
            ||
| 204 | box.left = box.img.subsurface(0, y, x, h - b - r)  | 
            ||
| 205 | box.right = box.img.subsurface(w - r, y, r, h - b - r)  | 
            ||
| 206 | box.center = box.img.subsurface(x, y, w - r - x, h - b - y)  | 
            ||
| 207 | boxes["%s-%s" % (boxName, option)] = box  | 
            ||
| 208 | |||
| 209 | def createSounds(section, option):  | 
            ||
| 210 | global sounds  | 
            ||
| 211 | name = "%s-%s" % (section[:-4], option[6:])  | 
            ||
| 212 | filename = os.path.join(skinDir, config.get(section, option))  | 
            ||
| 213 | try:  | 
            ||
| 214 |         sounds[name] = {} | 
            ||
| 215 | sounds[name]["fname"] = filename  | 
            ||
| 216 | if soundEnabled:  | 
            ||
| 217 | sounds[name]["sound"] = pygame.mixer.Sound(filename)  | 
            ||
| 218 | else:  | 
            ||
| 219 | sounds[name]["sound"] = None  | 
            ||
| 220 | except pygame.error:  | 
            ||
| 221 |         log.warning("Cannot create sound", name, filename) | 
            ||
| 222 | |||
| 223 | def playSound(style):  | 
            ||
| 224 | if soundEnabled and style in sounds:  | 
            ||
| 225 | try:  | 
            ||
| 226 | if sounds[style]["sound"] == None:  | 
            ||
| 227 | filename = sounds[style]["fname"]  | 
            ||
| 228 | sounds[style]["sound"] = pygame.mixer.Sound(filename)  | 
            ||
| 229 | sounds[style]["sound"].set_volume(soundVolume)  | 
            ||
| 230 | sounds[style]["sound"].play()  | 
            ||
| 231 | except pygame.error:  | 
            ||
| 232 |             log.warning("Cannot play sound", style) | 
            ||
| 233 | |||
| 234 | def loadMusic(file):  | 
            ||
| 235 | if musicEnabled and pygame.mixer.music.get_busy() == False:  | 
            ||
| 236 | global themeMusic  | 
            ||
| 237 | if file != None:  | 
            ||
| 238 | musicFile = "res.ext/music/" + file  | 
            ||
| 239 | elif themeMusic != None:  | 
            ||
| 240 | musicFile = "res.ext/music/" + themeMusic  | 
            ||
| 241 | else:  | 
            ||
| 242 | musicFile = "res.ext/music/riddleofsteel.ogg"  | 
            ||
| 243 | if os.path.exists(musicFile):  | 
            ||
| 244 | try:  | 
            ||
| 245 | pygame.mixer.music.load(musicFile)  | 
            ||
| 246 | except pygame.error:  | 
            ||
| 247 |                 log.warning("Cannot load music ",musicFile) | 
            ||
| 248 | |||
| 249 | def playMusic():  | 
            ||
| 250 | if musicEnabled:  | 
            ||
| 251 | try:  | 
            ||
| 252 | if pygame.mixer.music.get_busy() == False:  | 
            ||
| 253 | pygame.mixer.music.play(-1)  | 
            ||
| 254 | except pygame.error:  | 
            ||
| 255 |             log.warning("Cannot play music") | 
            ||
| 256 | |||
| 257 | def stopMusic():  | 
            ||
| 258 | try:  | 
            ||
| 259 | if pygame.mixer.music.get_busy() == True:  | 
            ||
| 260 | pygame.mixer.music.fadeout(1000)  | 
            ||
| 261 | except pygame.error:  | 
            ||
| 262 |         log.warning("Cannot stop music") | 
            ||
| 263 | except error:  | 
            ||
| 264 | pass  | 
            ||
| 265 | |||
| 266 | def getGridParams():  | 
            ||
| 267 | return gridParams  | 
            ||
| 268 | |||
| 269 | def drawBox(surface, widget, style):  | 
            ||
| 270 | box = boxes[style]  | 
            ||
| 271 | rect = widget.rect  | 
            ||
| 272 | l, t, r, b = box.margins  | 
            ||
| 273 | oldClip = surface.get_clip()  | 
            ||
| 274 | # corners  | 
            ||
| 275 | surface.blit(box.topleft, rect.topleft)  | 
            ||
| 276 | surface.blit(box.topright, (rect.right - r, rect.top))  | 
            ||
| 277 | surface.blit(box.bottomleft, (rect.left, rect.bottom - b))  | 
            ||
| 278 | surface.blit(box.bottomright, (rect.right - r, rect.bottom - b))  | 
            ||
| 279 | # center  | 
            ||
| 280 | surface.set_clip(rect.left + l, rect.top + t, rect.width - l - r, rect.height - t - b)  | 
            ||
| 281 | w, h = box.center.get_size()  | 
            ||
| 282 | for x in xrange(rect.left + l, rect.left + rect.width - r -l, w):  | 
            ||
| 283 | for y in xrange(rect.top + t, rect.top + rect.height - t - b, h):  | 
            ||
| 284 | surface.blit(box.center, (x, y))  | 
            ||
| 285 | # top + bottom  | 
            ||
| 286 | surface.set_clip(rect.left + l, rect.top, rect.width - l - r, rect.height)  | 
            ||
| 287 | w = box.top.get_width()  | 
            ||
| 288 | y1 = rect.top  | 
            ||
| 289 | y2 = rect.bottom - b  | 
            ||
| 290 | for x in xrange(rect.left + l, rect.left + rect.width - r - l, w):  | 
            ||
| 291 | surface.blit(box.top, (x, y1))  | 
            ||
| 292 | surface.blit(box.bottom, [x, y2])  | 
            ||
| 293 | # left + right  | 
            ||
| 294 | surface.set_clip(rect.left, rect.top + t, rect.width, rect.height - t - b)  | 
            ||
| 295 | h = box.left.get_height()  | 
            ||
| 296 | for y in xrange(rect.top + t, rect.top + rect.height - t - b + h, h):  | 
            ||
| 297 | surface.blit(box.left, (rect.left, y))  | 
            ||
| 298 | surface.blit(box.right, (rect.right - r, y))  | 
            ||
| 299 | # restore  | 
            ||
| 300 | surface.set_clip(oldClip)  | 
            ||
| 301 | |||
| 302 | def drawTextAndIcons(surface, widget, style):  | 
            ||
| 303 | box = boxes[style]  | 
            ||
| 304 | rect = widget.rect  | 
            ||
| 305 | l, t, r, b = box.cMargins  | 
            ||
| 306 | oldClip = surface.get_clip()  | 
            ||
| 307 | rect = pygame.Rect(rect.left + l, rect.top + t, rect.width - l - r, rect.height - t - b)  | 
            ||
| 308 | surface.set_clip(rect)  | 
            ||
| 309 | # icons  | 
            ||
| 310 | if widget.icons:  | 
            ||
| 311 | for img, align in widget.icons:  | 
            ||
| 312 | r = pygame.Rect(rect)  | 
            ||
| 313 | if align & Const.ALIGN_W:  | 
            ||
| 314 | rect.left += img.get_width()  | 
            ||
| 315 | rect.width -= img.get_width()  | 
            ||
| 316 | pass  | 
            ||
| 317 | elif align & Const.ALIGN_E:  | 
            ||
| 318 | r.left += rect.width - img.get_width()  | 
            ||
| 319 | rect.width -= img.get_width()  | 
            ||
| 320 | else:  | 
            ||
| 321 | r.left += (rect.width - img.get_width()) / 2  | 
            ||
| 322 | if align & Const.ALIGN_N: pass  | 
            ||
| 323 | elif align & Const.ALIGN_S: r.top += rect.height - img.get_height()  | 
            ||
| 324 | else: r.top += (rect.height - img.get_height()) / 2  | 
            ||
| 325 | surface.blit(img, r)  | 
            ||
| 326 | # text  | 
            ||
| 327 | if widget.text != None:  | 
            ||
| 328 | font = widget.font or themeDefaultFont  | 
            ||
| 329 | foreground = box.foreground or widget.foreground or themeForeground  | 
            ||
| 330 | background = widget.background  | 
            ||
| 331 | |||
| 332 | img = Fonts.renderText(font, widget.text, 1, foreground, background)  | 
            ||
| 333 | r = pygame.Rect(rect)  | 
            ||
| 334 | if widget.align & Const.ALIGN_W: pass  | 
            ||
| 335 | elif widget.align & Const.ALIGN_E: r.left += rect.width - img.get_width()  | 
            ||
| 336 | else: r.left += (rect.width - img.get_width()) / 2  | 
            ||
| 337 | if widget.align & Const.ALIGN_N: pass  | 
            ||
| 338 | elif widget.align & Const.ALIGN_S: r.top += rect.height - img.get_height()  | 
            ||
| 339 | else: r.top += (rect.height - img.get_height()) / 2  | 
            ||
| 340 | surface.blit(img, r)  | 
            ||
| 341 | surface.set_clip(oldClip)  | 
            ||
| 342 | |||
| 343 | |||
| 344 | def playButtonSound(widget):  | 
            ||
| 345 | if widget.pressed:  | 
            ||
| 346 | style = "%s-up" % (widget.style or "button")  | 
            ||
| 347 | else:  | 
            ||
| 348 | style = "%s-down" % (widget.style or "button")  | 
            ||
| 349 | playSound(style)  | 
            ||
| 350 | |||
| 351 | def drawButton(surface, widget):  | 
            ||
| 352 | if not widget.enabled:  | 
            ||
| 353 | s2 = "disabled"  | 
            ||
| 354 | elif widget.focused:  | 
            ||
| 355 | s2 = "focused"  | 
            ||
| 356 | else:  | 
            ||
| 357 | s2 = "enabled"  | 
            ||
| 358 | if widget.pressed:  | 
            ||
| 359 | s3 = "down"  | 
            ||
| 360 | elif widget.highlighted and widget.enabled:  | 
            ||
| 361 | s3 = "highlighted"  | 
            ||
| 362 | else:  | 
            ||
| 363 | s3 = "up"  | 
            ||
| 364 | if widget.toggle:  | 
            ||
| 365 | style = "%s-%s-%s" % (widget.style or "togglebutton", s2, s3)  | 
            ||
| 366 | else:  | 
            ||
| 367 | style = "%s-%s-%s" % (widget.style or "button", s2, s3)  | 
            ||
| 368 | drawBox(surface, widget, style)  | 
            ||
| 369 | drawTextAndIcons(surface, widget, style)  | 
            ||
| 370 | return widget.rect  | 
            ||
| 371 | |||
| 372 | def drawCheck(surface, widget):  | 
            ||
| 373 | if not widget.enabled:  | 
            ||
| 374 | s2 = "disabled"  | 
            ||
| 375 | elif widget.focused:  | 
            ||
| 376 | s2 = "focused"  | 
            ||
| 377 | else:  | 
            ||
| 378 | s2 = "enabled"  | 
            ||
| 379 | if widget.checked:  | 
            ||
| 380 | s3 = "on"  | 
            ||
| 381 | else:  | 
            ||
| 382 | s3 = "off"  | 
            ||
| 383 | style = "%s-%s-%s" % (widget.style or "check", s2, s3)  | 
            ||
| 384 | drawBox(surface, widget, style)  | 
            ||
| 385 | drawTextAndIcons(surface, widget, style)  | 
            ||
| 386 | return widget.rect  | 
            ||
| 387 | |||
| 388 | def drawLabel(surface, widget, highlight = 0):  | 
            ||
| 389 | if not widget.enabled:  | 
            ||
| 390 | style = "%s-disabled" % (widget.style or "label")  | 
            ||
| 391 | else:  | 
            ||
| 392 | if highlight:  | 
            ||
| 393 | style = "%s-highlight" % (widget.style or "label")  | 
            ||
| 394 | else:  | 
            ||
| 395 | style = "%s-clean" % (widget.style or "label")  | 
            ||
| 396 | drawBox(surface, widget, style)  | 
            ||
| 397 | drawTextAndIcons(surface, widget, style)  | 
            ||
| 398 | return widget.rect  | 
            ||
| 399 | |||
| 400 | ## utils  | 
            ||
| 401 | def hex2color(text):  | 
            ||
| 402 | if len(text) == 8:  | 
            ||
| 403 | return int(text[0:2], 16), int(text[2:4], 16), int(text[4:6], 16), int(text[6:8], 16)  | 
            ||
| 404 | else:  | 
            ||
| 405 | return int(text[0:2], 16), int(text[2:4], 16), int(text[4:6], 16)  | 
            ||
| 406 | |||
| 407 | ##  | 
            ||
| 408 | ## OLD CODE (for compatibility only)  | 
            ||
| 409 | ##  | 
            ||
| 410 | |||
| 411 | themeForeground = 0x00, 0xd0, 0x00  | 
            ||
| 412 | # themeWindowBck = 0x20, 0x30, 0x20  | 
            ||
| 413 | themeBackground = 0x20, 0x40, 0x20  | 
            ||
| 414 | themeHighlightbck = 0x40, 0x60, 0x40  | 
            ||
| 415 | themeHighlightfrg = 0x40, 0xf0, 0x40  | 
            ||
| 416 | # themeGaugecolor = 0x00, 0x80, 0x00  | 
            ||
| 417 | themeDefaultFont = 'normal'  | 
            ||
| 418 | themeBoldFont = 'normal-bold'  | 
            ||
| 419 | themeItalicFont = 'normal-italic'  | 
            ||
| 420 | # themeSelectionFrg = 0x00, 0xd0, 0x00  | 
            ||
| 421 | # themeSelectionBck = 0x40, 0x80, 0x40  | 
            ||
| 422 | themeTitleLine1 = 0x30, 0x50, 0x30  | 
            ||
| 423 | themeTitleLine2 = 0x40, 0x60, 0x40  | 
            ||
| 424 | themeSliderMin = 18  | 
            ||
| 425 | themeCritical = 0xFF, 0x80, 0x80  | 
            ||
| 426 | themeMajor = 0xFF, 0xFF, 0x00  | 
            ||
| 427 | themeMinor = 0xFF, 0xFF, 0xFF  | 
            ||
| 428 | themeNone = 0xC0, 0xC0, 0xC0  | 
            ||
| 429 | themeDisabled = 0x80, 0x80, 0x80  | 
            ||
| 430 | |||
| 431 | |||
| 432 | def getDRect(rect):  | 
            ||
| 433 | rect = pygame.Rect(rect)  | 
            ||
| 434 | rect.left += 1  | 
            ||
| 435 | rect.top += 0  | 
            ||
| 436 | rect.width -= 3  | 
            ||
| 437 | rect.height -= 2  | 
            ||
| 438 | return rect  | 
            ||
| 439 | |||
| 440 | def drawArrowButton(surface, widget):  | 
            ||
| 441 | if not widget.enabled:  | 
            ||
| 442 | s2 = "disabled"  | 
            ||
| 443 | elif widget.focused:  | 
            ||
| 444 | s2 = "focused"  | 
            ||
| 445 | else:  | 
            ||
| 446 | s2 = "enabled"  | 
            ||
| 447 | if widget.pressed:  | 
            ||
| 448 | s3 = "down"  | 
            ||
| 449 | else:  | 
            ||
| 450 | s3 = "up"  | 
            ||
| 451 | if widget.toggle:  | 
            ||
| 452 | style = "%s-%s-%s" % (widget.style or "togglebutton", s2, s3)  | 
            ||
| 453 | else:  | 
            ||
| 454 | style = "%s-%s-%s" % (widget.style or "button", s2, s3)  | 
            ||
| 455 | drawBox(surface, widget, style)  | 
            ||
| 456 | # arrow  | 
            ||
| 457 | fg = boxes[style].foreground or widget.foreground or themeForeground  | 
            ||
| 458 | r = getDRect(widget.rect)  | 
            ||
| 459 | r.left += 3  | 
            ||
| 460 | r.top += 3  | 
            ||
| 461 | r.width -= 6  | 
            ||
| 462 | r.height -= 6  | 
            ||
| 463 | if widget.direction == Const.ALIGN_N:  | 
            ||
| 464 | points = (r.midtop, r.bottomright, r.bottomleft)  | 
            ||
| 465 | elif widget.direction == Const.ALIGN_S:  | 
            ||
| 466 | points = (r.midbottom, r.topleft, r.topright)  | 
            ||
| 467 | elif widget.direction == Const.ALIGN_E:  | 
            ||
| 468 | points = (r.midright, r.topleft, r.bottomleft)  | 
            ||
| 469 | elif widget.direction == Const.ALIGN_W:  | 
            ||
| 470 | points = (r.midleft, r.topright, r.bottomright)  | 
            ||
| 471 | pygame.draw.lines(surface, fg, 1, points)  | 
            ||
| 472 | return widget.rect  | 
            ||
| 473 | |||
| 474 | View Code Duplication | def drawTitleButton(surface, widget):  | 
            |
| 475 | rect = getDRect(widget.rect)  | 
            ||
| 476 | rect.height += 1  | 
            ||
| 477 | rect.width += 1  | 
            ||
| 478 | oldClip = surface.get_clip()  | 
            ||
| 479 | surface.set_clip(rect)  | 
            ||
| 480 | foreground = widget.foreground or themeForeground  | 
            ||
| 481 | font = widget.font or themeBoldFont  | 
            ||
| 482 | # nicer background  | 
            ||
| 483 | surface.fill(themeTitleLine1, rect)  | 
            ||
| 484 | x1 = rect.left  | 
            ||
| 485 | x2 = rect.right  | 
            ||
| 486 | for y in xrange(rect.top, rect.bottom, 2):  | 
            ||
| 487 | pygame.draw.line(surface, themeTitleLine2, (x1, y), (x2, y), 1)  | 
            ||
| 488 | # icon  | 
            ||
| 489 | if widget.icons:  | 
            ||
| 490 | for img, align in widget.icons:  | 
            ||
| 491 | r = getDRect(rect)  | 
            ||
| 492 | if align & Const.ALIGN_W: r.left += 1  | 
            ||
| 493 | elif align & Const.ALIGN_E: r.left += rect.width - img.get_width()  | 
            ||
| 494 | else: r.left += (rect.width - img.get_width()) / 2  | 
            ||
| 495 | if align & Const.ALIGN_N: r.top += 1  | 
            ||
| 496 | elif align & Const.ALIGN_S: r.top += rect.height - img.get_height()  | 
            ||
| 497 | else: r.top += (rect.height - img.get_height()) / 2  | 
            ||
| 498 | surface.blit(img, r)  | 
            ||
| 499 | # text  | 
            ||
| 500 | if widget.text != None:  | 
            ||
| 501 | if widget.pressed:  | 
            ||
| 502 | foreground = themeHighlightfrg  | 
            ||
| 503 | img = Fonts.renderText(font, widget.text, 1, foreground)  | 
            ||
| 504 | r = getDRect(rect)  | 
            ||
| 505 | if widget.align & Const.ALIGN_W: r.left += 2  | 
            ||
| 506 | elif widget.align & Const.ALIGN_E: r.left += rect.width - img.get_width() - 1  | 
            ||
| 507 | else: r.left += (rect.width - img.get_width()) / 2  | 
            ||
| 508 | if widget.align & Const.ALIGN_N: r.top += 2  | 
            ||
| 509 | elif widget.align & Const.ALIGN_S: r.top += rect.height - img.get_height() - 1  | 
            ||
| 510 | else: r.top += (rect.height - img.get_height()) / 2  | 
            ||
| 511 | surface.blit(img, r)  | 
            ||
| 512 | surface.set_clip(oldClip)  | 
            ||
| 513 | |||
| 514 | View Code Duplication | def drawTitle(surface, widget):  | 
            |
| 515 | rect = getDRect(widget.rect)  | 
            ||
| 516 | rect.height += 1  | 
            ||
| 517 | rect.width += 1  | 
            ||
| 518 | oldClip = surface.get_clip()  | 
            ||
| 519 | surface.set_clip(rect)  | 
            ||
| 520 | foreground = widget.foreground or themeForeground  | 
            ||
| 521 | font = widget.font or themeDefaultFont  | 
            ||
| 522 | # nicer background  | 
            ||
| 523 | surface.fill(themeTitleLine1, rect)  | 
            ||
| 524 | x1 = rect.left  | 
            ||
| 525 | x2 = rect.right  | 
            ||
| 526 | for y in xrange(rect.top, rect.bottom, 2):  | 
            ||
| 527 | pygame.draw.line(surface, themeTitleLine2, (x1, y), (x2, y), 1)  | 
            ||
| 528 | # icon  | 
            ||
| 529 | if widget.icons:  | 
            ||
| 530 | for img, align in widget.icons:  | 
            ||
| 531 | r = getDRect(rect)  | 
            ||
| 532 | if align & Const.ALIGN_W: r.left += 1  | 
            ||
| 533 | elif align & Const.ALIGN_E: r.left += rect.width - img.get_width()  | 
            ||
| 534 | else: r.left += (rect.width - img.get_width()) / 2  | 
            ||
| 535 | if align & Const.ALIGN_N: r.top += 1  | 
            ||
| 536 | elif align & Const.ALIGN_S: r.top += rect.height - img.get_height()  | 
            ||
| 537 | else: r.top += (rect.height - img.get_height()) / 2  | 
            ||
| 538 | surface.blit(img, r)  | 
            ||
| 539 | # text  | 
            ||
| 540 | if widget.text != None:  | 
            ||
| 541 | img = Fonts.renderText(font, widget.text, 1, foreground)  | 
            ||
| 542 | r = getDRect(rect)  | 
            ||
| 543 | if widget.align & Const.ALIGN_W: r.left += 2  | 
            ||
| 544 | elif widget.align & Const.ALIGN_E: r.left += rect.width - img.get_width() - 1  | 
            ||
| 545 | else: r.left += (rect.width - img.get_width()) / 2  | 
            ||
| 546 | if widget.align & Const.ALIGN_N: r.top += 2  | 
            ||
| 547 | elif widget.align & Const.ALIGN_S: r.top += rect.height - img.get_height() - 1  | 
            ||
| 548 | else: r.top += (rect.height - img.get_height()) / 2  | 
            ||
| 549 | surface.blit(img, r)  | 
            ||
| 550 | surface.set_clip(oldClip)  | 
            ||
| 551 | |||
| 552 | def drawEntry(surface, widget):  | 
            ||
| 553 | rect = getDRect(widget.rect)  | 
            ||
| 554 | oldClip = surface.get_clip()  | 
            ||
| 555 | drawBox(surface, widget, "entry-enabled")  | 
            ||
| 556 | surface.set_clip(rect)  | 
            ||
| 557 | foreground = widget.foreground or themeForeground  | 
            ||
| 558 | font = widget.font or themeDefaultFont  | 
            ||
| 559 | # text  | 
            ||
| 560 | if widget.showChar and widget.text:  | 
            ||
| 561 | text = widget.showChar * len(widget.text)  | 
            ||
| 562 | else:  | 
            ||
| 563 | text = widget.text  | 
            ||
| 564 | |||
| 565 | if text:  | 
            ||
| 566 | textToCursor = text[:widget.cursorPos]  | 
            ||
| 567 | else:  | 
            ||
| 568 | textToCursor = ''  | 
            ||
| 569 | |||
| 570 | textSize = Fonts.getTextSize(font, text)  | 
            ||
| 571 | |||
| 572 | r = getDRect(rect)  | 
            ||
| 573 | |||
| 574 | # rendered text is longer than we can display  | 
            ||
| 575 | if textSize[0] > r.width:  | 
            ||
| 576 | text2 = textToCursor  | 
            ||
| 577 | textToIdx = widget.cursorPos  | 
            ||
| 578 | textFromIdx = 0  | 
            ||
| 579 | text2Size = Fonts.getTextSize(font, text2)  | 
            ||
| 580 | |||
| 581 | if text2Size[0] > r.width:  | 
            ||
| 582 | # if text to cursor is longer then width  | 
            ||
| 583 | # then delete some chars  | 
            ||
| 584 | while text2Size[0] > r.width:  | 
            ||
| 585 | text2 = text[textFromIdx:textToIdx]  | 
            ||
| 586 | textFromIdx += 1  | 
            ||
| 587 | text2Size = Fonts.getTextSize(font, text2)  | 
            ||
| 588 | else:  | 
            ||
| 589 | # if text to cursor is shorter then width  | 
            ||
| 590 | # then add some chars  | 
            ||
| 591 | while text2Size[0] < r.width:  | 
            ||
| 592 | text2 = text[textFromIdx:textToIdx]  | 
            ||
| 593 | textToIdx += 1  | 
            ||
| 594 | text2Size = Fonts.getTextSize(font, text2)  | 
            ||
| 595 | |||
| 596 | text = text2  | 
            ||
| 597 | |||
| 598 | img = Fonts.renderText(font, text, 1, foreground)  | 
            ||
| 599 | |||
| 600 | if widget.align & Const.ALIGN_E:  | 
            ||
| 601 | r.left += rect.width - img.get_width() - 2  | 
            ||
| 602 | elif not widget.align & Const.ALIGN_W:  | 
            ||
| 603 | r.left += (rect.width - img.get_width()) / 2  | 
            ||
| 604 | |||
| 605 | if widget.align & Const.ALIGN_N:  | 
            ||
| 606 | r.top += 2  | 
            ||
| 607 | elif widget.align & Const.ALIGN_S:  | 
            ||
| 608 | r.top += rect.height - img.get_height() - 1  | 
            ||
| 609 | else:  | 
            ||
| 610 | r.top += (rect.height - img.get_height()) / 2  | 
            ||
| 611 | |||
| 612 | surface.blit(img, r)  | 
            ||
| 613 | |||
| 614 | if widget.focused and widget.app.cursorOn:  | 
            ||
| 615 | offset = Fonts.getTextSize(font, textToCursor)  | 
            ||
| 616 | if offset[0] < r.width:  | 
            ||
| 617 | # draw cursor in middle of displayed text  | 
            ||
| 618 | r.move_ip(offset[0], 0)  | 
            ||
| 619 | else:  | 
            ||
| 620 | # draw cursor at end of drawed surface  | 
            ||
| 621 | r.left += img.get_width()  | 
            ||
| 622 | pygame.draw.line(surface, foreground, r.topleft, r.bottomleft, 1)  | 
            ||
| 623 | |||
| 624 | surface.set_clip(oldClip)  | 
            ||
| 625 | |||
| 626 | def drawDecoratedWindow(surface, window):  | 
            ||
| 627 | surface.fill(themeBackground)  | 
            ||
| 628 | wa = surface.get_clip()  | 
            ||
| 629 | # title  | 
            ||
| 630 | if window.title:  | 
            ||
| 631 | font = window.font or 'large-bold'  | 
            ||
| 632 | if window.focused:  | 
            ||
| 633 | color = themeForeground  | 
            ||
| 634 | else:  | 
            ||
| 635 | color = themeBackground  | 
            ||
| 636 | text = Fonts.renderText(font, window.title, 1, color)  | 
            ||
| 637 | r = surface.get_clip()  | 
            ||
| 638 | r.height = getGridParams()[1]  | 
            ||
| 639 | # nicer background  | 
            ||
| 640 | surface.fill(themeTitleLine1, r)  | 
            ||
| 641 | if window.focused:  | 
            ||
| 642 | x1 = r.left  | 
            ||
| 643 | x2 = r.right  | 
            ||
| 644 | for y in xrange(r.top, r.bottom, 2):  | 
            ||
| 645 | pygame.draw.line(surface, themeTitleLine2, (x1, y), (x2, y), 1)  | 
            ||
| 646 | r.move_ip(3, (r.height - text.get_height()) / 2)  | 
            ||
| 647 | surface.blit(text, r)  | 
            ||
| 648 | wa.top += r.height  | 
            ||
| 649 | wa.height -= r.height  | 
            ||
| 650 | # lines around the window  | 
            ||
| 651 | if not window.titleOnly:  | 
            ||
| 652 | r = surface.get_clip()  | 
            ||
| 653 | r.width -= 1  | 
            ||
| 654 | r.height -= 1  | 
            ||
| 655 | pygame.draw.lines(surface, themeTitleLine1, 1,  | 
            ||
| 656 | (r.topleft, r.topright, r.bottomright, r.bottomleft))  | 
            ||
| 657 | r.top += 1  | 
            ||
| 658 | r.left += 1  | 
            ||
| 659 | r.width -= 2  | 
            ||
| 660 | r.height -= 2  | 
            ||
| 661 | pygame.draw.lines(surface, themeTitleLine2, 1,  | 
            ||
| 662 | (r.topleft, r.topright, r.bottomright, r.bottomleft))  | 
            ||
| 663 | wa.top += 2  | 
            ||
| 664 | wa.height -= 3  | 
            ||
| 665 | wa.left += 2  | 
            ||
| 666 | wa.width -= 3  | 
            ||
| 667 | return wa  | 
            ||
| 668 | |||
| 669 | def drawPlainWindow(surface, window):  | 
            ||
| 670 | surface.fill(themeBackground)  | 
            ||
| 671 | return surface.get_clip()  | 
            ||
| 672 | |||
| 673 | def drawScrollSlider(surface, widget):  | 
            ||
| 674 | foreground = widget.foreground or themeForeground  | 
            ||
| 675 | r = getDRect(widget.rect)  | 
            ||
| 676 | r.left -= 1  | 
            ||
| 677 | r.width += 1  | 
            ||
| 678 | # draw frame  | 
            ||
| 679 | surface.fill(themeBackground, r)  | 
            ||
| 680 | pygame.draw.lines(surface, themeHighlightbck, 1,  | 
            ||
| 681 | (r.topleft, r.topright, r.bottomright, r.bottomleft))  | 
            ||
| 682 | |||
| 683 | # size of slider  | 
            ||
| 684 | r.top += 2  | 
            ||
| 685 | r.left += 2  | 
            ||
| 686 | r.height -= 3  | 
            ||
| 687 | r.width -= 3  | 
            ||
| 688 | if r.width > r.height:  | 
            ||
| 689 | # horizontal slider  | 
            ||
| 690 | # number of items  | 
            ||
| 691 | width = widget.max - widget.min  | 
            ||
| 692 | # proportional size of slider  | 
            ||
| 693 | slider = widget.shown * r.width / width  | 
            ||
| 694 | trunc = 0  | 
            ||
| 695 | if slider < themeSliderMin:  | 
            ||
| 696 | # slider has lesser size then minimal size for slider  | 
            ||
| 697 | # so we store difference between minimal slider size  | 
            ||
| 698 | # and size, which should slider have  | 
            ||
| 699 | trunc = themeSliderMin - slider  | 
            ||
| 700 | # set minimal slider size  | 
            ||
| 701 | slider = themeSliderMin  | 
            ||
| 702 | if slider > r.width:  | 
            ||
| 703 | # if slider is longer then drawing area, cut it  | 
            ||
| 704 | slider = r.width  | 
            ||
| 705 | |||
| 706 | # compute drawing position withing slider  | 
            ||
| 707 | pos = (widget.position - widget.min) * (r.width - trunc) / width  | 
            ||
| 708 | |||
| 709 | # set drawing area width to slider size  | 
            ||
| 710 | r.width = slider  | 
            ||
| 711 | # move draving area to slider position  | 
            ||
| 712 | r.left += pos  | 
            ||
| 713 | else:  | 
            ||
| 714 | # vertical slider  | 
            ||
| 715 | # number of items  | 
            ||
| 716 | height = widget.max - widget.min  | 
            ||
| 717 | # proportional size of slider  | 
            ||
| 718 | slider = widget.shown * r.height / height  | 
            ||
| 719 | trunc = 0  | 
            ||
| 720 | if slider < themeSliderMin:  | 
            ||
| 721 | # slider has lesser size then minimal size for slider  | 
            ||
| 722 | # so we store difference between minimal slider size  | 
            ||
| 723 | # and size, which should slider have  | 
            ||
| 724 | trunc = themeSliderMin - slider  | 
            ||
| 725 | # set minimal slider size  | 
            ||
| 726 | slider = themeSliderMin  | 
            ||
| 727 | if slider > r.height:  | 
            ||
| 728 | # if slider is longer then drawing area, cut it  | 
            ||
| 729 | slider = r.height  | 
            ||
| 730 | |||
| 731 | # compute drawing position withing slider  | 
            ||
| 732 | pos = (widget.position - widget.min) * (r.height - trunc) / height  | 
            ||
| 733 | |||
| 734 | # set drawing area height to slider size  | 
            ||
| 735 | r.height = slider  | 
            ||
| 736 | # move draving area to slider position  | 
            ||
| 737 | r.top += pos  | 
            ||
| 738 | |||
| 739 | # draw slider  | 
            ||
| 740 | surface.fill(themeHighlightbck, r)  | 
            ||
| 741 | |||
| 742 | if widget.dragging:  | 
            ||
| 743 | # slider is dragged by mouse  | 
            ||
| 744 | # we are drawing lines, so we must shorten width and height  | 
            ||
| 745 | # of slider drawing area to be 'inside' rectangle  | 
            ||
| 746 | r.width -= 1  | 
            ||
| 747 | r.height -= 1  | 
            ||
| 748 | # draw lines around slider  | 
            ||
| 749 | pygame.draw.lines(surface, themeHighlightfrg, 1,  | 
            ||
| 750 | (r.topleft, r.topright, r.bottomright, r.bottomleft))  | 
            ||
| 751 | |||
| 752 | # shorten drawing area one pixel inside  | 
            ||
| 753 | r.left += 1  | 
            ||
| 754 | r.top += 1  | 
            ||
| 755 | r.width -= 2 # one pixel from left and one from right  | 
            ||
| 756 | r.height -= 2 # one pixel from top and one from bottom  | 
            ||
| 757 | # draw lines inside slider  | 
            ||
| 758 | pygame.draw.lines(surface, foreground, 1,  | 
            ||
| 759 | (r.topleft, r.topright, r.bottomright, r.bottomleft))  | 
            ||
| 760 | else:  | 
            ||
| 761 | pass  | 
            ||
| 762 | |||
| 763 | # return last drawing area  | 
            ||
| 764 | return r  | 
            ||
| 765 | |||
| 766 | def drawTooltip(surface, widget):  | 
            ||
| 767 | # position rectangle is used only for fetching position  | 
            ||
| 768 | pos_r = getDRect(widget.rect)  | 
            ||
| 769 | |||
| 770 | # to know what we are dealing with, we have to render all the text first  | 
            ||
| 771 | foreground = widget.foreground or themeForeground  | 
            ||
| 772 | title_font = widget.font or "small"  | 
            ||
| 773 | body_font = widget.font or themeDefaultFont  | 
            ||
| 774 |     if not body_font.endswith('-italic'): | 
            ||
| 775 | body_font = body_font + "-italic"  | 
            ||
| 776 | |||
| 777 | if widget.title:  | 
            ||
| 778 | title_img = Fonts.renderText(title_font, widget.title, 1, foreground)  | 
            ||
| 779 | title_width = title_img.get_width()  | 
            ||
| 780 | title_height = title_img.get_height()  | 
            ||
| 781 | max_width = title_width  | 
            ||
| 782 | max_height = title_height  | 
            ||
| 783 | else:  | 
            ||
| 784 | max_width = 0  | 
            ||
| 785 | max_height = 0  | 
            ||
| 786 | |||
| 787 | text_images = []  | 
            ||
| 788 | if widget.text:  | 
            ||
| 789 | for line in string.split(widget.text, '\n'):  | 
            ||
| 790 | line_img = Fonts.renderText(body_font, line, 1, foreground)  | 
            ||
| 791 | text_images += [line_img]  | 
            ||
| 792 | max_height += line_img.get_height()  | 
            ||
| 793 | max_width = max(max_width, line_img.get_width())  | 
            ||
| 794 | |||
| 795 | # now we have to decide how to fit the tooltip fully into the window  | 
            ||
| 796 | screen_width, screen_height = pygame.display.get_surface().get_size()  | 
            ||
| 797 | envelope = 8 # so we don't overflow the screen  | 
            ||
| 798 | pos_r.left += 20  | 
            ||
| 799 | if pos_r.left + max_width > screen_width:  | 
            ||
| 800 | pos_r.left = max(0, screen_width - max_width - envelope)  | 
            ||
| 801 | if pos_r.top + max_height > screen_height:  | 
            ||
| 802 | pos_r.top = max(0, screen_height - max_height - envelope)  | 
            ||
| 803 | |||
| 804 | # title  | 
            ||
| 805 | title_r = getDRect(pos_r)  | 
            ||
| 806 | title_text_r = getDRect(title_r)  | 
            ||
| 807 | if widget.title:  | 
            ||
| 808 | title_text_r.width = title_width  | 
            ||
| 809 | title_text_r.height = title_height  | 
            ||
| 810 | # making 2 pixel free space around text  | 
            ||
| 811 | title_text_r.top += 2  | 
            ||
| 812 | title_text_r.left += 2  | 
            ||
| 813 | title_r.width = title_text_r.width + 4  | 
            ||
| 814 | title_r.height = title_text_r.height + 4  | 
            ||
| 815 | else:  | 
            ||
| 816 | # have to zero the widget  | 
            ||
| 817 | title_r.width = 0  | 
            ||
| 818 | title_r.height = 0  | 
            ||
| 819 | |||
| 820 | # body tooltip  | 
            ||
| 821 | body_r = getDRect(pos_r)  | 
            ||
| 822 | if widget.text:  | 
            ||
| 823 | body_r.top += title_r.height  | 
            ||
| 824 | body_text_r = getDRect(body_r)  | 
            ||
| 825 | body_height = 0  | 
            ||
| 826 | body_width = 0  | 
            ||
| 827 | for line_img in text_images:  | 
            ||
| 828 | body_height += line_img.get_height()  | 
            ||
| 829 | body_width = max(body_width, line_img.get_width())  | 
            ||
| 830 | body_text_r.height = body_height  | 
            ||
| 831 | body_text_r.width = body_width  | 
            ||
| 832 | # making 2 pixel free space around text  | 
            ||
| 833 | body_text_r.top += 2  | 
            ||
| 834 | body_text_r.left += 2  | 
            ||
| 835 | body_r.width = body_text_r.width + 4  | 
            ||
| 836 | body_r.height = body_text_r.height + 4  | 
            ||
| 837 | |||
| 838 | # let's draw!  | 
            ||
| 839 | # title  | 
            ||
| 840 | if widget.title:  | 
            ||
| 841 | surface.fill(themeBackground, title_r)  | 
            ||
| 842 | pygame.draw.lines(surface, themeForeground, 0,  | 
            ||
| 843 | (title_r.bottomleft, title_r.topleft, title_r.topright, title_r.bottomright))  | 
            ||
| 844 | surface.blit(title_img, title_text_r)  | 
            ||
| 845 | |||
| 846 | # body  | 
            ||
| 847 | if text_images:  | 
            ||
| 848 | surface.fill(themeBackground, body_r)  | 
            ||
| 849 | pygame.draw.lines(surface, themeForeground, 0,  | 
            ||
| 850 | (body_r.topright, body_r.bottomright, body_r.bottomleft, body_r.topleft))  | 
            ||
| 851 | for img in text_images:  | 
            ||
| 852 | surface.blit(img, body_text_r)  | 
            ||
| 853 | body_text_r.top += img.get_height()  | 
            ||
| 854 | body_text_r.height -= img.get_height()  | 
            ||
| 855 | |||
| 856 | # finishing touches  | 
            ||
| 857 | if text_images and widget.title:  | 
            ||
| 858 | pygame.draw.line(surface, themeForeground, body_r.topright, title_r.bottomright)  | 
            ||
| 859 | elif widget.title:  | 
            ||
| 860 | # closing bottom of title  | 
            ||
| 861 | pygame.draw.line(surface, themeForeground, title_r.bottomleft, title_r.bottomright)  | 
            ||
| 862 | elif text_images:  | 
            ||
| 863 | # closing top of body  | 
            ||
| 864 | pygame.draw.line(surface, themeForeground, body_r.topleft, body_r.topright)  | 
            ||
| 865 | |||
| 866 | |||
| 867 | # there is probably bug in pygameui, having one pixel off evaluation of rects  | 
            ||
| 868 | title_r.height += 1  | 
            ||
| 869 | title_r.width += 1  | 
            ||
| 870 | body_r.width += 1  | 
            ||
| 871 | body_r.height += 1  | 
            ||
| 872 | |||
| 873 | return title_r, body_r  | 
            ||
| 874 | |||
| 875 | def drawScrollbar(surface, widget):  | 
            ||
| 876 | r = pygame.Rect(widget.rect)  | 
            ||
| 877 | surface.fill(themeBackground, r)  | 
            ||
| 878 | return r  | 
            ||
| 879 | |||
| 880 | def drawListbox(surface, widget):  | 
            ||
| 881 | r = pygame.Rect(widget.rect)  | 
            ||
| 882 | surface.fill(widget.background or themeBackground, r)  | 
            ||
| 883 | return r  | 
            ||
| 884 | |||
| 885 | def drawCursor(surface, pos):  | 
            ||
| 886 | surface.fill((0xff, 0xff, 0xff), (pos, (2, 2)))  | 
            ||
| 887 | |||
| 888 | def drawProgressBar(surface, widget):  | 
            ||
| 889 | foreground = widget.foreground or themeForeground  | 
            ||
| 890 | r = getDRect(widget.rect)  | 
            ||
| 891 | # frame  | 
            ||
| 892 | surface.fill(themeBackground, r)  | 
            ||
| 893 | pygame.draw.lines(surface, themeHighlightbck, 1,  | 
            ||
| 894 | (r.topleft, r.topright, r.bottomright, r.bottomleft))  | 
            ||
| 895 | r.top += 2  | 
            ||
| 896 | r.left += 2  | 
            ||
| 897 | r.height -= 3  | 
            ||
| 898 | r.width -= 3  | 
            ||
| 899 | perc = float(widget.value - widget.min) / (widget.max - widget.min)  | 
            ||
| 900 | if r.width > r.height:  | 
            ||
| 901 | # horizontal  | 
            ||
| 902 | r.width = int(r.width * perc)  | 
            ||
| 903 | else:  | 
            ||
| 904 | # vertical  | 
            ||
| 905 | r.top = r.bottom - int(r.height * perc)  | 
            ||
| 906 | surface.fill(themeHighlightbck, r)  | 
            ||
| 907 | |||
| 908 | def getTextDrawLines(widget):  | 
            ||
| 909 | r = getDRect(widget.rect)  | 
            ||
| 910 | img = Fonts.renderText(widget.font or 'normal', ' ', 1, widget.foreground or themeForeground)  | 
            ||
| 911 | return r.height / img.get_height()  | 
            ||
| 912 | |||
| 913 | def isTextBeyondEnd(widget, text):  | 
            ||
| 914 | r = getDRect(widget.rect)  | 
            ||
| 915 | size = Fonts.getTextSize(widget.font or 'normal', text)  | 
            ||
| 916 | return size[0] >= r.right  | 
            ||
| 917 | |||
| 918 | def drawText(surface, widget):  | 
            ||
| 919 | oldClip = surface.get_clip()  | 
            ||
| 920 | surface.set_clip(widget.rect)  | 
            ||
| 921 | fore = foreground = widget.foreground or themeForeground  | 
            ||
| 922 | back = background = widget.background or themeBackground  | 
            ||
| 923 | font = widget.font or 'normal'  | 
            ||
| 924 | r = getDRect(widget.rect)  | 
            ||
| 925 | drawBox(surface, widget, "entry-enabled")  | 
            ||
| 926 | line = 0  | 
            ||
| 927 | x = r.left  | 
            ||
| 928 | y = r.top  | 
            ||
| 929 | img = Fonts.renderText(font, ' ', 1, foreground)  | 
            ||
| 930 | row = 0  | 
            ||
| 931 | for para in widget.text:  | 
            ||
| 932 | if row < widget.offsetRow:  | 
            ||
| 933 | row += 1  | 
            ||
| 934 | continue  | 
            ||
| 935 | onlyword = False  | 
            ||
| 936 | column = 0  | 
            ||
| 937 | charIdx = 0  | 
            ||
| 938 | firstY = y  | 
            ||
| 939 | previous_width = 0  | 
            ||
| 940 | for char in para:  | 
            ||
| 941 | fore = foreground  | 
            ||
| 942 | back = None  | 
            ||
| 943 | if widget.selection and widget.selection.first <= (row, column) < widget.selection.last:  | 
            ||
| 944 | # switch colors for foreground/background  | 
            ||
| 945 | fore = background  | 
            ||
| 946 | back = foreground  | 
            ||
| 947 | |||
| 948 | # simple hack to add word wrapping  | 
            ||
| 949 | # get words from the current char to end of paragraph  | 
            ||
| 950 |             words = para[charIdx:].split(' ') | 
            ||
| 951 | # compute length of rendered first word  | 
            ||
| 952 | remainingWordSize = Fonts.getTextSize(font, words[0])  | 
            ||
| 953 | # if word doesn't fit to current line,  | 
            ||
| 954 | # move to next line  | 
            ||
| 955 | if x + remainingWordSize[0] + 10 > r.right:  | 
            ||
| 956 | if x == (r.left + previous_width) or onlyword: # only word on line, and still too large! Render as much as we can, then move to next line.  | 
            ||
| 957 | onlyword = True  | 
            ||
| 958 | if x + 10 > r.right:  | 
            ||
| 959 | if not r.left + remainingWordSize[0] + 10 > r.right: # finally, end of word  | 
            ||
| 960 | onlyword = False  | 
            ||
| 961 | x = r.left;  | 
            ||
| 962 | y += remainingWordSize[1]  | 
            ||
| 963 | if y + img.get_height() > r.bottom:  | 
            ||
| 964 | surface.set_clip(oldClip)  | 
            ||
| 965 | return  | 
            ||
| 966 | else:  | 
            ||
| 967 | x = r.left;  | 
            ||
| 968 | y += remainingWordSize[1]  | 
            ||
| 969 | if y + img.get_height() > r.bottom:  | 
            ||
| 970 | surface.set_clip(oldClip)  | 
            ||
| 971 | return  | 
            ||
| 972 | # render next char  | 
            ||
| 973 | img = Fonts.renderText(font, char, 1, fore, back)  | 
            ||
| 974 | # compute next char position  | 
            ||
| 975 | previous_width = img.get_width()  | 
            ||
| 976 | newX = x + previous_width  | 
            ||
| 977 | |||
| 978 | surface.blit(img, (x, y))  | 
            ||
| 979 | column += 1  | 
            ||
| 980 | charIdx += 1  | 
            ||
| 981 | |||
| 982 | if widget.editable and row == widget.cursorRow and \  | 
            ||
| 983 | widget.focused and widget.app.cursorOn and \  | 
            ||
| 984 | column == widget.cursorColumn:  | 
            ||
| 985 | pygame.draw.line(surface, foreground, (newX, y), (newX, y + img.get_height()), 1)  | 
            ||
| 986 | |||
| 987 | x = newX  | 
            ||
| 988 | |||
| 989 | # draw cursor in case of zero length paragraph or begining of line  | 
            ||
| 990 | if (len(para) == 0 or widget.cursorColumn == 0) and \  | 
            ||
| 991 | widget.editable and row == widget.cursorRow and \  | 
            ||
| 992 | widget.focused and widget.app.cursorOn:  | 
            ||
| 993 | pygame.draw.line(surface, foreground, (r.left, firstY), (r.left, firstY + img.get_height()), 1)  | 
            ||
| 994 | |||
| 995 | x = r.left  | 
            ||
| 996 | y += img.get_height()  | 
            ||
| 997 | row += 1  | 
            ||
| 998 | line += 1  | 
            ||
| 999 | if y + img.get_height() > r.bottom:  | 
            ||
| 1000 | surface.set_clip(oldClip)  | 
            ||
| 1001 | return  | 
            ||
| 1002 | surface.set_clip(oldClip)  | 
            ||
| 1003 |