Skip to content

Commit 2abb584

Browse files
authored
Implement paragraphs (#54)
* Implement paragraphs * Add newline before opening <p>
1 parent b81e043 commit 2abb584

File tree

4 files changed

+42
-3
lines changed

4 files changed

+42
-3
lines changed

README.md

+5-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ This is a <b>bold <i>italic</i></b> statement!
2828
The following tags are supported:
2929

3030
| Tag | Description | Example |
31-
|---------| ------------------------------------------------|---------------------------------------------|
31+
|---------|------------------------------------------------|---------------------------------------------|
3232
| a | Create a "hyperlink" that generates a message | `<a=message_id>Foobar</a>` |
3333
| | when clicked (see `richtext.on_click`) | |
3434
| b | The text should be bold | `<b>Foobar</b>` |
@@ -51,6 +51,9 @@ The following tags are supported:
5151
| nobr | Prevent the text from breaking | `Words <nobr>inside tag</nobr> won't break` |
5252
| size | Change text size, relative to default size | `<size=2>Twice as large</size>` |
5353
| spine | Display spine model | `<spine=scene:anim/>` |
54+
| p | Adds extra spacing below the line where this | `<p>A paragraph</p>\nSome other text` |
55+
| | tag ends. Adds a newline before its opening | `<p=2.5>This has 2.5 lines of spacing<p>` |
56+
| | tag if it doesn't already exist. | |
5457

5558
### Line breaks
5659
Note that there is no need for the HTML `<br/>` tag since line breaks (i.e. `\n`) are parsed and presented by the system. Note that a single `<br>` (ie without a closing or empty tag) isn't supported (even though most browsers accept it).
@@ -150,6 +153,7 @@ The `settings` table can contain the following values:
150153
* `outline` (vector4) - The default outline color of text. Will be transparent if not specified.
151154
* `align` (hash) - One of `richtext.ALIGN_LEFT`, `richtext.ALIGN_CENTER`, `richtext.ALIGN_RIGHT` and `richtext.ALIGN_JUSTIFY`. Defaults to `richtext.ALIGN_LEFT`. Defines how the words of a line of text are positioned in relation the provided `position`. Width must be specified for `richtext.ALIGN_JUSTIFY`.
152155
* `line_spacing` (number) - Value to multiply line height with. Set to a value lower than 1.0 to reduce space between lines and a value higher than 1.0 to increase space between lines. Defaults to 1.0.
156+
* `paragraph_spacing` (number) - Space to leave after lines with where `<p>` tags end. Relative to the line height. Defaults to 0.5 lines.
153157
* `image_pixel_grid_snap` (boolean) - Set to true to position image on full pixels (positions rounded to nearest integer) to avoid effects of anti-aliasing. Defaults to false.
154158
* `combine_words` (boolean) - Set to true to combine words with the same style on a line into a single node. This is useful for very long texts where the maximum number of nodes would exceed the limit set in the GUI. The disadvantage is that any per word operations will not work since the words have been combined.
155159

example/example.gui_script

+7
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@ local function create_long_text_example()
1717
return richtext.create(sherlock, "Roboto-Regular", settings)
1818
end
1919

20+
local function create_paragraph_example()
21+
local settings = { position = vmath.vector3(10, 1075, 0), width = 600 }
22+
local text = "<p>This is some rather long text that wraps around and has 0.5 lines of space after its last line.</p>\n<p=2.5>This one has 2.5 lines of space.</p>\nThis is just regular text."
23+
return richtext.create(text, "Roboto-Regular", settings)
24+
end
25+
2026
local function create_complex_example()
2127
local settings = {
2228
fonts = {
@@ -301,6 +307,7 @@ function init(self)
301307
{ name = "OVERLAPPING", fn = create_overlapping_tags_example },
302308
{ name = "HTML ENTITIES", fn = create_html_entities_example },
303309
{ name = "FADE IN", fn = create_fade_in_example },
310+
{ name = "PARAGRAPHS", fn = create_paragraph_example },
304311
}
305312

306313
self.back = gui.get_node("back/bg")

richtext/parse.lua

+14
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ local function parse_tag(tag, params)
3737
}
3838
elseif tag == "nobr" then
3939
settings.nobr = true
40+
elseif tag == "p" then
41+
settings.paragraph = tonumber(params) or true
4042
end
4143

4244
return settings
@@ -189,6 +191,18 @@ function M.parse(text, default_settings)
189191
if not found then print(("Found end tag '%s' without matching start tag"):format(name)) end
190192
end
191193

194+
if name == "p" then
195+
local last_word = all_words[#all_words]
196+
if last_word then
197+
if not is_endtag then
198+
last_word.linebreak = true
199+
end
200+
if is_endtag or is_empty then
201+
last_word.paragraph_end = true
202+
end
203+
end
204+
end
205+
192206
-- parse text after the tag on the next iteration
193207
text = after_tag
194208
end

richtext/richtext.lua

+16-2
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,7 @@ function M.create(text, font, settings)
323323
settings.outline = settings.outline or V4_ZERO
324324
settings.position = settings.position or V3_ZERO
325325
settings.line_spacing = settings.line_spacing or 1
326+
settings.paragraph_spacing = settings.paragraph_spacing or 0.5
326327
settings.image_pixel_grid_snap = settings.image_pixel_grid_snap or false
327328
settings.combine_words = settings.combine_words or false
328329
if settings.align == M.ALIGN_JUSTIFY and not settings.width then
@@ -347,6 +348,7 @@ function M.create(text, font, settings)
347348
local line_words = {}
348349
local line_width = 0
349350
local line_height = 0
351+
local paragraph_spacing = 0
350352
local position = vmath.vector3(settings.position)
351353
local word_count = #words
352354
for i = 1, word_count do
@@ -387,9 +389,10 @@ function M.create(text, font, settings)
387389

388390
-- update text metrics
389391
text_metrics.width = math.max(text_metrics.width, line_width)
390-
text_metrics.height = text_metrics.height + (line_height * settings.line_spacing)
392+
text_metrics.height = text_metrics.height + (line_height * settings.line_spacing) + paragraph_spacing
391393
line_width = word_metrics.total_width
392394
line_height = word_metrics.height
395+
paragraph_spacing = 0
393396
else
394397
-- the word fits on the line, add it and update text metrics
395398
if combined_metrics then
@@ -418,6 +421,16 @@ function M.create(text, font, settings)
418421
word.delete = true
419422
end
420423

424+
if word.paragraph_end then
425+
local paragraph = word.paragraph
426+
if paragraph then
427+
paragraph_spacing = math.max(
428+
paragraph_spacing,
429+
line_height * (paragraph == true and settings.paragraph_spacing or paragraph)
430+
)
431+
end
432+
end
433+
421434
-- handle line break
422435
if word.linebreak then
423436
-- position all words on the line up until the linebreak
@@ -426,9 +439,10 @@ function M.create(text, font, settings)
426439
position_words(line_words, line_width, line_height, position, settings)
427440

428441
-- update text metrics
429-
text_metrics.height = text_metrics.height + (line_height * settings.line_spacing)
442+
text_metrics.height = text_metrics.height + (line_height * settings.line_spacing) + paragraph_spacing
430443
line_height = word_metrics.height
431444
line_width = 0
445+
paragraph_spacing = 0
432446
end
433447
end
434448

0 commit comments

Comments
 (0)