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
introduced
by
![]() |
|||
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
|
|||
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
|
|||
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
|
|||
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
|
|||
472 | return widget.rect |
||
473 | |||
474 | View Code Duplication | def drawTitleButton(surface, widget): |
|
0 ignored issues
–
show
|
|||
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
|
|||
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
|
|||
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
|
|||
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
|
|||
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
|
|||
809 | title_text_r.height = title_height |
||
0 ignored issues
–
show
|
|||
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
|
|||
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 |