class FastText(Text): """ Reduces font searches by assuming a monospace font and single-line text. """ text = widget.call_on_change("_text", text_changed) old_len = 0 maybe_needs_refont = False
class UpdateSlider(Slider): def _on_slider_move(self): self.update_func(self.slider_pos) _slider_pos = widget.call_on_change("__slider_pos", _on_slider_move) def __init__(self, *args, **kwargs): self.update_func = kwargs.pop("update_func", lambda value: None) super(UpdateSlider, self).__init__(*args, **kwargs)
class UpdateEditableText(EditableText): def _on_text_change(self): self.update_func(self.text) _text = widget.call_on_change("__text", _on_text_change) def __init__(self, *args, **kwargs): self.update_func = kwargs.pop("update_func", lambda value: None) super(UpdateEditableText, self).__init__(*args, **kwargs)
class ChunkedText(Text): def update_text(self): self.text = "".join(self.chunks) chunks = widget.call_on_change("__chunks", update_text) def __init__(self, *args, **kwargs): chunks = kwargs.pop("chunks", ()) super(ChunkedText, self).__init__(*args, **kwargs) self.chunks = chunks
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", "resolved", g.resolve_color_alias) resolved_color = widget.causes_redraw("_resolved_color") base_font = widget.auto_reconfig("_base_font", "resolved", g.resolve_font_alias) resolved_base_font = widget.call_on_change("_resolved_base_font", resize_redraw) def __init__(self, parent, pos, size=(0, .05), anchor=constants.TOP_LEFT, text=None, base_font=None, shrink_factor=0.875, 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.resolved_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.resolved_base_font[size] def font_bisect(self, test_font): left = 0 right = (self.max_size or len(self.resolved_base_font)-1) + 1 def test_size(size): font = self.resolved_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] * self.shrink_factor) else: width = None height = int(dimensions[1] * 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.resolved_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.resolved_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.resolved_color, None, False] underline = [self.resolved_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)