Wordwrap: prevent infinite loop + cache result
Previously a line that was too long but didn't contain spaces would cause an infinite loop and hang Blender. Now we just hard-break each line when there are no spaces to be found. The result of word_wrap() is now also cached, so that redraws are more efficient.
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import functools
|
||||
import os.path
|
||||
|
||||
import blf
|
||||
@@ -249,15 +250,18 @@ def _after_submission_text() -> str:
|
||||
return text
|
||||
|
||||
|
||||
def word_wrap(string: str, width_in_px: int) -> str:
|
||||
@functools.lru_cache()
|
||||
def word_wrap(string: str, width_in_px: int, *, depth=0) -> str:
|
||||
"""Word-wrapping with variable character width.
|
||||
|
||||
Newlines in the input string are kept in the output.
|
||||
"""
|
||||
|
||||
assert depth < 3
|
||||
|
||||
if '\n' in string:
|
||||
# If the string already consists of multiple lines, wrap each line individually.
|
||||
return '\n'.join(word_wrap(line, width_in_px)
|
||||
return '\n'.join(word_wrap(line, width_in_px, depth=depth + 1)
|
||||
for line in string.splitlines(keepends=False))
|
||||
|
||||
# Do an estimate of the maximum number of characters to fit on a line.
|
||||
@@ -271,10 +275,19 @@ def word_wrap(string: str, width_in_px: int) -> str:
|
||||
line_width = blf.dimensions(font_id, candidate_line)[0]
|
||||
|
||||
# Keep removing the last word until the line fits the width.
|
||||
while line_width >= width_in_px:
|
||||
marker = len(candidate_line) - 1
|
||||
while not candidate_line[marker].isspace():
|
||||
marker -= 1
|
||||
break_at_space = True
|
||||
while candidate_line and line_width >= width_in_px:
|
||||
if break_at_space:
|
||||
marker = len(candidate_line) - 1
|
||||
while marker > 0 and not candidate_line[marker].isspace():
|
||||
marker -= 1
|
||||
if marker <= 0:
|
||||
# This line was unbreakable by whitespace. Let's just hard-break it.
|
||||
break_at_space = False
|
||||
continue
|
||||
else:
|
||||
marker = len(candidate_line) - 1
|
||||
|
||||
candidate_line = candidate_line[:marker]
|
||||
line_width = blf.dimensions(font_id, candidate_line)[0]
|
||||
|
||||
|
Reference in New Issue
Block a user