Issues (229)

client/pygameui/SkinableTheme.py (13 issues)

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):
0 ignored issues
show
The variable filename does not seem to be defined for all execution paths.
Loading history...
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:
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable sounds does not seem to be defined.
Loading history...
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:
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable error does not seem to be defined.
Loading history...
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):
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable xrange does not seem to be defined.
Loading history...
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)
0 ignored issues
show
The variable points does not seem to be defined for all execution paths.
Loading history...
472
    return widget.rect
473
474 View Code Duplication
def drawTitleButton(surface, widget):
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
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):
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable xrange does not seem to be defined.
Loading history...
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):
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
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):
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable xrange does not seem to be defined.
Loading history...
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):
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable xrange does not seem to be defined.
Loading history...
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
0 ignored issues
show
The variable title_width does not seem to be defined for all execution paths.
Loading history...
809
        title_text_r.height = title_height
0 ignored issues
show
The variable title_height does not seem to be defined for all execution paths.
Loading history...
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)
0 ignored issues
show
The variable title_img does not seem to be defined for all execution paths.
Loading history...
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