Skip to content

Commit d7bdc73

Browse files
committed
Added support for splitting very long words that span more than a full line
Fixes #67
1 parent 0d3e543 commit d7bdc73

File tree

2 files changed

+56
-11
lines changed

2 files changed

+56
-11
lines changed

example/example.gui_script

+8-4
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ local function create_complex_example()
5757
[hash("spineboy")] = hash("spine-spineboy"),
5858
},
5959
},
60-
width = 460,
60+
width = 480,
6161
position = vmath.vector3(0, 0, 0),
6262
parent = gui.get_node("bg"),
6363
color = vmath.vector4(0.95, 0.95, 1.0, 1.0),
@@ -197,13 +197,17 @@ local function create_language_example()
197197
end
198198

199199

200-
local function create_nobreak_example()
200+
local function create_linebreak_example()
201201
local settings_nobr = { position = vmath.vector3(0, 600, 0), align = richtext.ALIGN_LEFT, width = 245 }
202202
local words1 = richtext.create("The image at the end should end up on a new line <nobr><img=smileys:cyclops/></nobr>", "Roboto-Regular", settings_nobr)
203203

204204
local settings_right = { position = vmath.vector3(320, 600, 0), align = richtext.ALIGN_LEFT, width = 245 }
205205
local words2 = richtext.create("The image at the end should end up on a new line <img=smileys:cyclops/>", "Roboto-Regular", settings_right)
206-
return merge(words1, words2)
206+
207+
local settings_nobr = { position = vmath.vector3(0, 900, 0), align = richtext.ALIGN_LEFT, width = 245 }
208+
local longword = richtext.create("THIS IS AVERYLONGWORDWITHOUTANYLINEBREAKS", "Roboto-Regular", settings_nobr)
209+
210+
return merge(words1, words2, longword)
207211
end
208212

209213
local function create_overlapping_tags_example()
@@ -320,7 +324,7 @@ function init(self)
320324
{ name = "CHARACTERS", fn = create_characters_example },
321325
{ name = "SPINE", fn = create_spine_example },
322326
{ name = "LANGUAGE", fn = create_language_example },
323-
{ name = "NO BREAK", fn = create_nobreak_example },
327+
{ name = "LINEBREAK", fn = create_linebreak_example },
324328
{ name = "CLICKABLE", fn = create_clickable_words_example },
325329
{ name = "OVERLAPPING", fn = create_overlapping_tags_example },
326330
{ name = "HTML ENTITIES", fn = create_html_entities_example },

richtext/richtext.lua

+48-7
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,27 @@ local function measure_node(word, font, previous_word)
296296
return metrics, combined_metrics, node
297297
end
298298

299+
local function split_word(word, font, max_width)
300+
local one = deepcopy(word)
301+
local two = deepcopy(word)
302+
local text = word.text
303+
local metrics = get_text_metrics(one, font)
304+
local char_count = utf8.len(text)
305+
local split_index = math.floor(char_count * (max_width / metrics.total_width))
306+
local rest = ""
307+
while split_index > 1 do
308+
one.text = utf8.sub(text, 1, split_index)
309+
one.linebreak = true
310+
metrics = get_text_metrics(one, font)
311+
if metrics.width <= max_width then
312+
rest = utf8.sub(text, split_index + 1)
313+
break
314+
end
315+
split_index = split_index - 1
316+
end
317+
two.text = rest
318+
return one, two
319+
end
299320

300321

301322
--- Create rich text gui nodes from text
@@ -364,7 +385,8 @@ function M.create(text, font, settings)
364385
local paragraph_spacing = 0
365386
local position = vmath.vector3(settings.position)
366387
local word_count = #words
367-
for i = 1, word_count do
388+
local i = 1
389+
repeat
368390
local word = words[i]
369391
if word.image then
370392
text_metrics.img_count = text_metrics.img_count + 1
@@ -390,12 +412,29 @@ function M.create(text, font, settings)
390412
local word_metrics, combined_metrics, node = measure_node(word, font_for_word, previous_word)
391413
local should_create_node = true
392414

393-
-- does the word fit on the line or does it overflow?
394-
local overflow = (settings.width and ((combined_metrics
395-
and (line_width - previous_word.metrics.total_width + combined_metrics.width)
396-
or (line_width + word_metrics.width)
397-
) > settings.width))
415+
-- check if the line overflows due to this word
416+
local overflow = false
417+
if settings.width then
418+
if combined_metrics then
419+
overflow = (line_width - previous_word.metrics.total_width + combined_metrics.width) > settings.width
420+
else
421+
overflow = (line_width + word_metrics.width) > settings.width
422+
end
398423

424+
-- if we overflow and the word is longer than a full line we
425+
-- split the word and add the first part to the current line
426+
if overflow and word.text and word_metrics.width > settings.width then
427+
local remaining_width = settings.width - line_width
428+
local one, two = split_word(word, font_for_word, remaining_width)
429+
word_metrics, combined_metrics, node = measure_node(one, font_for_word, previous_word)
430+
words[i] = one
431+
word = one
432+
table.insert(words, i + 1, two)
433+
word_count = word_count + 1
434+
overflow = false
435+
end
436+
end
437+
399438
if overflow and not word.nobr then
400439
-- overflow, position the words that fit on the line
401440
text_metrics.height = text_metrics.height + (line_height * line_increment_before * settings.line_spacing)
@@ -465,7 +504,9 @@ function M.create(text, font, settings)
465504
line_width = 0
466505
paragraph_spacing = 0
467506
end
468-
end
507+
508+
i = i + 1
509+
until i > word_count
469510

470511
-- position remaining words
471512
if #line_words > 0 then

0 commit comments

Comments
 (0)