class SelectableText(Text): selected = widget.causes_redraw("_selected") selected_color = widget.auto_reconfig("_selected_color", g.resolve_color_alias) _selected_color = widget.causes_redraw("__selected_color") unselected_color = widget.auto_reconfig("_unselected_color", g.resolve_color_alias) unselected_color = widget.causes_redraw("__unselected_color") def __init__(self, parent, pos, size, border_color=None, unselected_color=None, selected_color=None, **kwargs): super(SelectableText, self).__init__(parent, pos, size, **kwargs) self.border_color = border_color or "text_border" self.selected_color = selected_color or "text_background_selected" self.unselected_color = unselected_color or "text_background_unselected" self.selected = False def redraw(self): if self.selected: self.background_color = self.selected_color else: self.background_color = self.unselected_color super(SelectableText, self).redraw()
class ImageButton(Button): image = widget.auto_reconfig("_image", g.resolve_image_alias) def __init__(self, *args, **kwargs): image_surface = kwargs.pop("image", None) super(ImageButton, self).__init__(*args, **kwargs) self.image = image.Image(self, (-.5, -.5), (-.9, -.9), anchor=constants.MID_CENTER, image=image_surface)
class Image(widget.Widget): image = widget.auto_reconfig("_image", g.resolve_image_alias) _image = widget.causes_rebuild("__image") def __init__(self, parent, pos, size=(1, 1), anchor=constants.TOP_LEFT, image=None): super(Image, self).__init__(parent, pos, size, anchor) self.old_size = None self.image = image def _calc_size(self): size = list(super(Image, self)._calc_size()) if size[0] == size[1] == 0: raise ValueError, "One image dimension must be specified!" image_size = self.image.get_size() ratio = image_size[0] / float(image_size[1]) if size[0] == 0: size[0] = int(size[1] * ratio) elif size[1] == 0: size[1] = int(size[0] / ratio) return tuple(size) def rescale(self): self.scaled_image = scale(self.image, self.real_size) def resize(self): super(Image, self).resize() if self.real_size != self.old_size: self.rescale() self.old_size = self.real_size def redraw(self): super(Image, self).redraw() self.surface.blit(self.scaled_image, (0, 0))
class ProgressText(SelectableText): progress = widget.causes_redraw("_progress") progress_color = widget.auto_reconfig("_progress_color", g.resolve_color_alias) _progress_color = widget.causes_redraw("__progress_color") def __init__(self, parent, pos, size, *args, **kwargs): self.parent = parent self.progress = kwargs.pop("progress", 0) self.progress_color = kwargs.pop("progress", "progress_background_progress") kwargs.setdefault("border_color", "progress_border") kwargs.setdefault("selected_color", "progress_background_selected") kwargs.setdefault("unselected_color", "progress_background_unselected") super(ProgressText, self).__init__(parent, pos, size, **kwargs) def redraw(self): super(ProgressText, self).redraw() width, height = self.real_size self.surface.fill(self.progress_color, (0, 0, width * self.progress, height)) self.draw_borders()
class Slider(button.Button): slider_pos = widget.causes_rebuild("_slider_pos") slider_max = widget.causes_rebuild("_slider_max") slider_size = widget.causes_rebuild("_slider_size") horizontal = widget.causes_rebuild("_horizontal") slider_color = widget.auto_reconfig("_slider_color", g.resolve_color_alias) _slider_color = widget.causes_redraw("__slider_color") def __init__(self, parent, pos=(-1, 0), size=(-.1, -1), anchor=constants.TOP_RIGHT, borders=constants.ALL, border_color=None, background_color=None, slider_color=None, slider_pos=0, slider_max=10, slider_size=5, horizontal=False, **kwargs): kwargs.setdefault("priority", 80) super(Slider, self).__init__(parent, pos, size, anchor=anchor, **kwargs) border_color = border_color or "white" background_color = background_color or "dark_blue" slider_color = slider_color or "light_blue" self.borders = borders self.border_color = border_color self.background_color = background_color self.selected_color = background_color self.unselected_color = background_color self.slider_color = slider_color self.slider_pos = slider_pos self.slider_max = slider_max self.slider_size = slider_size self.horizontal = horizontal self.drag_state = None self.button = button.Button(self, pos=None, size=None, anchor=constants.TOP_LEFT, border_color=border_color, selected_color=slider_color, unselected_color=slider_color, priority=self.priority - 5) def redraw(self): super(Slider, self).redraw() self.button.selected_color = self.slider_color self.button.unselected_color = self.slider_color def add_hooks(self): super(Slider, self).add_hooks() self.parent.add_handler(constants.DRAG, self.handle_drag) self.parent.add_handler(constants.CLICK, self.handle_click, 50) def remove_hooks(self): super(Slider, self).remove_hooks() self.parent.remove_handler(constants.DRAG, self.handle_drag) self.parent.remove_handler(constants.CLICK, self.handle_click) def _calc_length(self, items): return items / float(self.slider_size + self.slider_max) def rebuild(self): super(Slider, self).rebuild() self.needs_resize = True def resize(self): super(Slider, self).resize() bar_start = self._calc_length(self.slider_pos) bar_length = self._calc_length(self.slider_size) if self.horizontal: self.button.pos = (-bar_start, 0) self.button.size = (-bar_length, -1) borders = [constants.TOP, constants.BOTTOM] self.button.resize() real_pos = self.button.real_pos[0] real_size = self.button.real_size[0] if real_pos == 0: borders.append(constants.LEFT) if real_pos + real_size == self.real_size[0]: borders.append(constants.RIGHT) self.button.borders = tuple(borders) else: self.button.pos = (0, -bar_start) self.button.size = (-1, -bar_length) borders = [constants.LEFT, constants.RIGHT] self.button.resize() real_pos = self.button.real_pos[1] real_size = self.button.real_size[1] if real_pos == 0: borders.append(constants.TOP) if real_pos + real_size == self.real_size[1]: borders.append(constants.BOTTOM) self.button.borders = tuple(borders) def handle_drag(self, event): if not self.visible: return if self.drag_state == None: self.start_pos = tuple(event.pos[i] - event.rel[i] for i in range(2)) self.start_slider_pos = self.slider_pos if self.button.is_over(self.start_pos): self.drag_state = True else: self.drag_state = False if self.drag_state == True: if self.horizontal: dir = 0 else: dir = 1 mouse_pos = pygame.mouse.get_pos() rel = mouse_pos[dir] - self.start_pos[dir] unit = self._calc_length(1) * self.real_size[dir] movement = int((rel + (unit / 2.)) // unit) new_pos = self.safe_pos(self.start_slider_pos + movement) self.slider_pos = new_pos raise constants.Handled def safe_pos(self, value): return max(0, min(self.slider_max, value)) def handle_click(self, event): if self.drag_state == True: self.drag_state = None if not self.is_over(pygame.mouse.get_pos()): raise constants.Handled else: self.drag_state = None def jump(self, go_lower, big_jump=False, tiny_jump=False): if big_jump: jump_dist = max(1, self.slider_max // 2) elif tiny_jump: jump_dist = max(1, self.slider_max // 100) else: jump_dist = max(1, self.slider_size - 1) if go_lower: self.slider_pos = self.safe_pos(self.slider_pos - jump_dist) else: self.slider_pos = self.safe_pos(self.slider_pos + jump_dist) def activated(self, event): assert event.type == pygame.MOUSEBUTTONUP if self.horizontal: self.jump(go_lower=(event.pos[0] < self.button.collision_rect[0])) else: self.jump(go_lower=event.pos[1] < self.button.collision_rect[1]) raise constants.Handled
class Text(widget.BorderedWidget): text = widget.call_on_change("_text", resize_redraw) shrink_factor = widget.call_on_change("_shrink_factor", resize_redraw) underline = widget.call_on_change("_underline", resize_redraw) wrap = widget.call_on_change("_wrap", resize_redraw) bold = widget.call_on_change("_bold", resize_redraw) align = widget.causes_redraw("_align") valign = widget.causes_redraw("_valign") color = widget.auto_reconfig("_color", g.resolve_color_alias) _color = widget.causes_redraw("__color") base_font = widget.auto_reconfig("_base_font", g.resolve_font_alias) _base_font = widget.call_on_change("__base_font", resize_redraw) def __init__(self, parent, pos, size=(0, .05), anchor=constants.TOP_LEFT, text=None, base_font=None, shrink_factor=1, color=None, align=constants.CENTER, valign=constants.MID, underline=-1, wrap=True, bold=False, text_size=36, **kwargs): kwargs.setdefault("background_color", "text_background") kwargs.setdefault("border_color", "text_border") super(Text, self).__init__(parent, pos, size, anchor, **kwargs) self.text = text self.base_font = base_font or "normal" self.color = color or "text" self.shrink_factor = shrink_factor self.underline = underline self.align = align self.valign = valign self.wrap = wrap self.bold = bold self.text_size = text_size max_size = property(lambda self: min( len(self.base_font) - 1, convert_font_size(self.text_size))) font = property(lambda self: self._font) def pick_font(self, dimensions): nice_size = self.pick_font_size(dimensions, False) mean_size = self.pick_font_size(dimensions) size = max(nice_size, mean_size - convert_font_size(5)) return self.base_font[size] def font_bisect(self, test_font): left = 0 right = (self.max_size or len(self.base_font) - 1) + 1 def test_size(size): font = self.base_font[size] font.set_bold(self.bold) result = test_font(font) font.set_bold(False) return result return do_bisect(left, right, test_size) def pick_font_size(self, dimensions, break_words=True): if dimensions[0]: width = int((dimensions[0] - 4) * self.shrink_factor) else: width = None height = int((dimensions[1] - 4) * self.shrink_factor) basic_line_count = self.text.count("\n") + 1 def test_size(test_font): too_wide = False if width: if self.wrap: try: lines = split_wrap(self.text, test_font, width, break_words) except WrapError: lines = [] too_wide = True else: lines = split_wrap(self.text, test_font, 0) for line in lines: if test_font.size(line)[0] > width: too_wide = True break line_count = len(lines) else: line_count = basic_line_count too_tall = (test_font.get_linesize() * line_count) > height return not (too_tall or too_wide) return self.font_bisect(test_size) def size_using_font(self, font, width=0): #Calculate the size of the text block. raw_width, raw_height = size_of_block(self.text, font, width) #Adjust for shrink_factor and borders. width = int(raw_width / self.shrink_factor) + 4 height = int(raw_height / self.shrink_factor) + 4 return width, height def calc_text_size(self, initial_dimensions): if not (initial_dimensions[0] and initial_dimensions[1]): if not self.max_size: raise ValueError("No font size given, but a dimension is 0.") max_font = self.base_font[self.max_size] if initial_dimensions[0] == initial_dimensions[1] == 0: # No size specified, use the natural size of the max font. width, height = self.size_using_font(max_font) return (width, height), max_font elif not initial_dimensions[1]: # Width specified, use the size of the max font, word-wrapped. text_width = int( (initial_dimensions[0] - 4) * self.shrink_factor) width, height = self.size_using_font(max_font, width=text_width) return (initial_dimensions[0], height), max_font else: # Height specified. Try the natural size of the max font. width, height = self.size_using_font(max_font) if height <= initial_dimensions[1]: return (width, initial_dimensions[1]), max_font else: # Too tall. Run a binary search to find the largest font # size that fits. def test_size(font): width, height = self.size_using_font(font) width, raw_height = size_of_block(self.text, font) height = int(raw_height / self.shrink_factor) + 4 return height <= initial_dimensions[1] font_size = self.font_bisect(test_size) font = self.base_font[font_size] width, height = self.size_using_font(font) return (width, initial_dimensions[1]), font else: # Both sizes specified. Search for a usable font size. return initial_dimensions, self.pick_font(initial_dimensions) def _calc_size(self): base_size = list(super(Text, self)._calc_size()) if self.text is None: return tuple(base_size) else: # Determine the true size and font of the text area. text_size, font = self.calc_text_size(base_size) self._font = font return tuple(text_size) def redraw(self): super(Text, self).redraw() if self.text != None: self.print_text() def print_text(self): # Mark the character to be underlined (if any). no_underline = [self.color, None, False] underline = [self.color, None, True] styles = [no_underline + [0]] if 0 <= self.underline < len(self.text): styles.insert(0, underline + [self.underline + 1]) if self.underline != 0: styles.insert(0, no_underline + [self.underline]) self.font.set_bold(self.bold) # Print the string itself. print_string(self.surface, self.text, (3, 2), self.font, styles, self.align, self.valign, self.real_size, self.wrap) self.font.set_bold(False)