def layout_width(triples, justify=False): """ Returns the width of the given list of triples. """ rv = 0 curts = None cur = "" rtl = renpy.config.rtl for type, ts, i in triples: if ts is not curts: if cur: if rtl: cur, dir = log2vis(cur, ON) rv += curts.get_width(cur) curts = ts cur = i elif justify and type == "space": cur += i if rtl: cur, dir = log2vis(cur, ON) rv += curts.get_width(cur) cur = "" else: cur += i if curts: if rtl: cur, dir = log2vis(cur, ON) rv += curts.get_width(cur) return rv
def rtl_paragraph(self, p): """ Given a paragraph (a list of segment, text tuples) handles RTL and ligaturization. This returns the reversed RTL paragraph, which differers from the LTR one. It also returns a flag that is True if this is an rtl paragraph. """ direction = ON l = [] for ts, s in p: s, direction = log2vis(s, direction) l.append((ts, s)) rtl = (direction == RTL or direction == WRTL) return l, rtl
def rtl_paragraph(self, p): """ Given a paragraph (a list of segment, text tuples) handles RTL and ligaturization. This returns the reversed RTL paragraph, which differers from the LTR one. It also returns a flag that is True if this is an rtl paragraph. """ direction = ON l = [ ] for ts, s in p: s, direction = log2vis(s, direction) l.append((ts, s)) rtl = (direction == RTL or direction == WRTL) return l, rtl
def layout(self, width, time): """ This lays out the text of this widget. It sets self.laidout, self.laidout_lineheights, self.laidout_width, and self.laidout_height. """ if self.laidout and self.width == width: return if self.tokens is None: self.update() # Set this, so caching works. self.width = width # We are building this list of triples, which will be passed # to text_layout. triples = [] # text style list - a stack of text styles. tsl = [TextStyle()] # The default style. (Duplicated in {a}, {st}) tsl[-1].font = self.style.font tsl[-1].size = self.style.size tsl[-1].bold = self.style.bold tsl[-1].italic = self.style.italic tsl[-1].underline = self.style.underline tsl[-1].strikethrough = self.style.strikethrough tsl[-1].color = None tsl[-1].black_color = None tsl[-1].hyperlink = None tsl[-1].update() self.laidout_hyperlinks = [] # W0201 # if not self.text: # text = " " # else: # text = self.text # for i in re.split(r'( |\{[^{}]+\}|\{\{|\n)', text): tokens = [] for l in self.tokens: tokens.extend(l) ti = iter(tokens) for kind, i in ti: # Newline. if kind == "newline": triples.append(("newline", tsl[-1], "")) continue elif kind == "tag": # Are we closing a tag? if i.startswith("/"): tsl.pop() if not tsl: raise Exception( "Closing tag %s does not match an open tag." % i) continue if i == "w": # Automatically closes. continue elif i == "nw": # Automatically closes. continue elif i.startswith("w="): # Automatically closes. continue elif i == "fast": # Automatically closes. triples.append(("start", tsl[-1], "")) continue elif i.startswith("a="): m = re.match(r'a=(.*)', i) if not m: raise Exception( 'Hyperlink tag %s could not be parsed.' % i) # TODO: check to see if we need to be focused. target = m.group(1) hls = renpy.config.hyperlink_styler(target) old_prefix = hls.prefix link = len(self.laidout_hyperlinks) if renpy.display.focus.argument == link: if self.activated: hls.set_prefix("activate_") else: hls.set_prefix("hover_") else: hls.set_prefix("idle_") tsl.append(TextStyle()) tsl[-1].font = hls.font tsl[-1].size = hls.size tsl[-1].bold = hls.bold tsl[-1].italic = hls.italic tsl[-1].underline = hls.underline tsl[-1].strikethrough = hls.strikethrough tsl[-1].color = hls.color tsl[-1].black_color = hls.black_color tsl[-1].hyperlink = link tsl[-1].update() self.laidout_hyperlinks.append(target) hls.set_prefix(old_prefix) continue # Otherwise, we're opening a new tag. tsl.append(TextStyle(tsl[-1])) if i == "b": tsl[-1].bold = True tsl[-1].update() elif i == "i": tsl[-1].italic = True tsl[-1].update() elif i == "u": tsl[-1].underline = True tsl[-1].update() elif i == "s": tsl[-1].strikethrough = True tsl[-1].update() elif i == "plain": tsl[-1].bold = False tsl[-1].italic = False tsl[-1].underline = False tsl[-1].update() elif i[0] == "=": style = getattr(renpy.store.style, i[1:]) tsl[-1].font = style.font tsl[-1].size = style.size tsl[-1].bold = style.bold tsl[-1].italic = style.italic tsl[-1].underline = style.underline tsl[-1].strikethrough = style.strikethrough tsl[-1].color = style.color tsl[-1].black_color = style.black_color tsl[-1].update() elif i.startswith("font"): m = re.match(r'font=(.*)', i) if not m: raise Exception('Font tag %s could not be parsed.' % i) tsl[-1].font = m.group(1) tsl[-1].update() elif i.startswith("size"): m = re.match(r'size=(\+|-|)(\d+)', i) if not m: raise Exception('Size tag %s could not be parsed.' % i) if m.group(1) == '+': tsl[-1].size += int(m.group(2)) elif m.group(1) == '-': tsl[-1].size -= int(m.group(2)) else: tsl[-1].size = int(m.group(2)) tsl[-1].update() elif i.startswith("color"): m = re.match(r'color=(\#?[a-fA-F0-9]+)', i) if not m: raise Exception('Color tag %s could not be parsed.' % i) tsl[-1].color = color(m.group(1)) tsl[-1].update() else: raise Exception( "Text tag %s was not recognized. Case and spacing matter here." % i) # Since the kind can change. if kind == "tag": continue elif kind == "space": # Spaces always get appended to the end of a line. So they # will never show up at the start of a line, unless they're # after a newline or at the start of a string. triples.append(("space", tsl[-1], i)) continue elif kind == "word": triples.append(("word", tsl[-1], i)) elif kind == "widget": pass else: raise Exception("Unknown text token kind %s." % kind) if kind == "widget": wstyle = WidgetStyle(tsl[-1], i, width, time) triples.append(("word", wstyle, i)) # We're done matching tags. if len(tsl) != 1: Exception("A tag was left open at the end of the text.") # Give text_layout a list of triples, get back a list of lists of # triples, one per line. linetriples, lines_last = renpy.config.text_layout( triples, width, self.style) # Now, we need to go through these lines, to generate the data # we need to render text. self.laidout = [] # W0201 self.laidout_lineheights = [] # W0201 self.laidout_linewidths = [] # W0201 self.laidout_length = 0 # W0201 self.laidout_start = 0 # W0201 self.laidout_width = self.style.min_width # W0201 self.laidout_height = 0 # W0201 self.laidout_lines_last = lines_last # W0201 # Add something to empty lines. for l in linetriples: if not l: l.append(('word', tsl[-1], ' ')) justify = self.style.justify for l in linetriples: line = [] oldts = None cur = None for kind, ts, i in l: if kind == "start": self.laidout_start = self.laidout_length continue try: self.laidout_length += len(i) except: self.laidout_length += ts.length(i) if ts is not oldts: if oldts is not None: line.append((oldts, cur)) oldts = ts cur = i else: cur += i if justify and kind == "space": if cur: line.append((oldts, cur)) cur = "" line.append((SpacerStyle(), "")) if oldts: line.append((oldts, cur)) if renpy.config.rtl: rtl_line = [] # RTL direction. line_direction = ON for ts, i in line: if isinstance(ts, TextStyle): i, line_direction = log2vis(i, line_direction) rtl_line.append((ts, i)) if line_direction == RTL or line_direction == WRTL: rtl_line.reverse() line = rtl_line width = 0 height = 0 for ts, i in line: # This is a special case to handle mostly-blank lines introduced # by newlines. if len(line) == 1 and i == "": i = " " w, h = ts.sizes(i) width += w height = max(height, h) self.laidout.append(line) self.laidout_linewidths.append(width) self.laidout_lineheights.append(height) self.laidout_width = max(width, self.laidout_width) self.laidout_height += height + self.style.line_spacing # For the newline. self.laidout_length += 1
def layout(self, width, time): """ This lays out the text of this widget. It sets self.laidout, self.laidout_lineheights, self.laidout_width, and self.laidout_height. """ self.update() if self.laidout and self.width == width and self.layout_generation == layout_generation: return self.layout_generation = layout_generation # Set this, so caching works. self.width = width # We are building this list of triples, which will be passed # to text_layout. triples = [ ] # text style list - a stack of text styles. tsl = [ TextStyle() ] # The default style. (Duplicated in {a}, {st}) tsl[-1].font = self.style.font tsl[-1].size = self.style.size tsl[-1].bold = self.style.bold tsl[-1].italic = self.style.italic tsl[-1].underline = self.style.underline tsl[-1].strikethrough = self.style.strikethrough tsl[-1].color = None tsl[-1].black_color = None tsl[-1].hyperlink = None tsl[-1].update() self.laidout_hyperlinks = [ ] # W0201 tokens = [ ] for l in self.tokens: tokens.extend(l) ti = iter(tokens) for kind, i in ti: # Newline. if kind == "newline": triples.append(("newline", tsl[-1], "")) continue elif kind == "tag": # Are we closing a tag? if i.startswith("/"): tsl.pop() if not tsl: raise Exception("Closing tag %s does not match an open tag." % i) continue if i == "w" or i.startswith("w="): # Automatically closes. continue elif i == "p" or i.startswith("p="): # Automatically closes. triples.append(("newline", tsl[-1], "\n")) continue elif i == "nw": # Automatically closes. continue elif i.startswith("w="): # Automatically closes. continue elif i == "fast": # Automatically closes. continue elif i == "_start": # Automatically closes. triples.append(("start", tsl[-1], "")) continue elif i.startswith("a="): # TODO: check to see if we need to be focused. target = i[2:] hyperlink_styler = self.style.hyperlink_functions[0] if hyperlink_styler: hls = hyperlink_styler(target) else: hls = self.style old_prefix = hls.prefix link = len(self.laidout_hyperlinks) if renpy.display.focus.argument == link: if self.activated: hls.set_prefix("activate_") else: hls.set_prefix("hover_") else: hls.set_prefix("idle_") tsl.append(TextStyle()) tsl[-1].font = hls.font tsl[-1].size = hls.size tsl[-1].bold = hls.bold tsl[-1].italic = hls.italic tsl[-1].underline = hls.underline tsl[-1].strikethrough = hls.strikethrough tsl[-1].color = hls.color tsl[-1].black_color = hls.black_color tsl[-1].hyperlink = link tsl[-1].update() self.laidout_hyperlinks.append(target) hls.set_prefix(old_prefix) continue # Otherwise, we're opening a new tag. tsl.append(TextStyle(tsl[-1])) if i == "b": tsl[-1].bold = True tsl[-1].update() elif i == "i": tsl[-1].italic = True tsl[-1].update() elif i == "u": tsl[-1].underline = True tsl[-1].update() elif i == "s": tsl[-1].strikethrough = True tsl[-1].update() elif i == "plain": tsl[-1].bold = False tsl[-1].italic = False tsl[-1].underline = False tsl[-1].update() elif i[0] == "=": style = getattr(renpy.store.style, i[1:]) tsl[-1].font = style.font tsl[-1].size = style.size tsl[-1].bold = style.bold tsl[-1].italic = style.italic tsl[-1].underline = style.underline tsl[-1].strikethrough = style.strikethrough tsl[-1].color = style.color tsl[-1].black_color = style.black_color tsl[-1].update() elif i.startswith("font="): tsl[-1].font = i[5:] tsl[-1].update() elif i.startswith("size="): m = re.match(r'size=(\+|-|)(\d+)', i) if not m: raise Exception('Size tag %s could not be parsed.' % i) if m.group(1) == '+': tsl[-1].size += int(m.group(2)) elif m.group(1) == '-': tsl[-1].size -= int(m.group(2)) else: tsl[-1].size = int(m.group(2)) tsl[-1].update() elif i.startswith("color="): tsl[-1].color = color(i[6:]) tsl[-1].update() else: raise Exception("Text tag %s was not recognized. Case and spacing matter here." % i) # Since the kind can change. if kind == "tag": continue elif kind == "space": # Spaces always get appended to the end of a line. So they # will never show up at the start of a line, unless they're # after a newline or at the start of a string. triples.append(("space", tsl[-1], i)) continue elif kind == "word": triples.append(("word", tsl[-1], i)) elif kind == "widget": pass else: raise Exception("Unknown text token kind %s." % kind) if kind == "widget": wstyle = WidgetStyle(tsl[-1], i, width, time) triples.append(("word", wstyle, i)) # We're done matching tags. if len(tsl) != 1: Exception("A tag was left open at the end of the text.") # Give text_layout a list of triples, get back a list of lists of # triples, one per line. linetriples, lines_last = renpy.config.text_layout(triples, width, self.style) # Now, we need to go through these lines, to generate the data # we need to render text. self.laidout = [ ] # W0201 self.laidout_lineheights = [ ] # W0201 self.laidout_linewidths = [ ] # W0201 self.laidout_length = 0 # W0201 self.laidout_start = 0 # W0201 self.laidout_width = self.style.min_width # W0201 self.laidout_height = 0 # W0201 self.laidout_lines_last = lines_last # W0201 # Add something to empty lines. for l in linetriples: if not l: l.append(('word', tsl[-1], ' ')) justify = self.style.justify for l in linetriples: line = [ ] oldts = None cur = None for kind, ts, i in l: if kind == "start": self.laidout_start = self.laidout_length continue try: self.laidout_length += len(i) except: self.laidout_length += ts.length(i) if ts is not oldts: if oldts is not None: line.append((oldts, cur)) oldts = ts cur = i else: cur += i if justify and kind == "space": if cur: line.append((oldts, cur)) cur = "" line.append((SpacerStyle(), "")) if oldts: line.append((oldts, cur)) if renpy.config.rtl: rtl_line = [ ] # RTL direction. line_direction = ON for ts, i in line: if isinstance(ts, TextStyle): i, line_direction = log2vis(i, line_direction) rtl_line.append((ts, i)) if line_direction == RTL or line_direction == WRTL: rtl_line.reverse() line = rtl_line width = 0 height = 0 for ts, i in line: # This is a special case to handle mostly-blank lines introduced # by newlines. if len(line) == 1 and i == "": i = " " w, h = ts.sizes(i) width += w height = max(height, h) self.laidout.append(line) self.laidout_linewidths.append(width) self.laidout_lineheights.append(height) self.laidout_width = max(width, self.laidout_width) self.laidout_height += height + max(self.style.line_spacing, 0) # For the newline. self.laidout_length += 1