britzl / defold-richtext Goto Github PK
View Code? Open in Web Editor NEWDefold-RichText is a system to create styled text based on an HTML inspired markup language
License: MIT License
Defold-RichText is a system to create styled text based on an HTML inspired markup language
License: MIT License
Here's a minimal reproductible example:
local text = "<size=0.5>The brown fox jumps over the lazy dog. The brown fox jumps over the lazy dog.\n\nThe brown fox jumps over the lazy dog. The brown fox jumps over the lazy dog.</size>"
local words, metrics = richtext.create(text, "Roboto", settings)
And here's how it looks (notice the extra newline in the middle of the second paragraph):
I want to be able to insert a list of colors by name and then be able to easily use them in text. Color names like "abyssal_green" or "lotus_red" with associated color codes.
What I am doing currently is
local richtext_color = require "richtext.color"
local function richtext_insert_colors()
richtext_color.COLORS["abyssal_green"] = richtext_color.parse_hex("#05f0bc") -- #14ffb9
richtext_color.COLORS["lotus_red"] = richtext_color.parse_hex("#ff0946")
end
Maybe make it a richtext main function to add a color to the list by name.
richtext.add_color("name", "code")
Where code is auto detected to be RGB/RGBA/hex/hexa, and if the color already existed it would silently replace it.
Hello.
If font is a big size, then message "click text" work, when I click in up left corner. Only up left corner of word, but not whole word. It reproduce on font Roboto-Regular with 72 pt in example project.
Not working:
<color=0.25,0.41,0.88,1>I
<color=red>need to get out
Add support for horizontally centering each line of text
Add the ability to apply custom tags and then provide a function to return all nodes with a specific tag/class
This currently has no effect: <size=2><img=foo:bar/></size>
The same for a <spine>
tag does though.
Reasoning is that if you use an img within a truncate based on the metrics.char_count you will likely cut off the final char as the actual truncated chars will be one short by the end of it.
Use utf8.reverse() on the text before using it.
Use either a config value dir=rtl|ltr
or a new <dir=rtl|ltr>
tag
What's happening here? There are two icons. One is displayed like how it should be, the second looks incorrect. I tried to reset the size but it did not seem to help.
local text = [[<img=icons:document/><size=1>Kalipi</size><br/><size=0.7>Well, hello there! He he he... ho ho ho...</size><br/><size=0.7>What do we have here?</size><size=1.0></size><img=icons:document/>]]
Adding some size 1 text doesn't help either
Putting second on its own line also doesn't help
Defold-richtext does not set text node outline color. For runtime created nodes default outline color is black (for nodes created in editor this color is white). Because of this, text created by defold-richtext looks in some cases thinner then text in other text nodes (at least for bitmap fonts).
Would be useful when doing per character effects
https://www.twitch.tv/finestko/clip/PrettiestPopularToadSquadGoals
An effect some visual novels has which looks nice.
Allow RichText to handle text like this:
“<color=red><b>some text</color></b>
”
...where the tags are not closed in the same order that they are opened.
Or they could actually overlap, like this:
"<i>this is italic <b>bold italic : </i>bold regular</b> back to normal.
"
this is italic bold italic : bold regular back to normal.
I guess the parser would have to store the current styling state with the order of opened tags, so it could handle any tag that came next, rather than only searching for consecutive open-close pairs.
Maybe add a <scale>
tag? Or use the <size>
tag? (this would be a breaking change)
Would be a useful option
= No breaking space is a common HTML character entity. Maybe worth adding support for it to complement the <nobr>
tag?
It should be possible to get myparams
from this tag: <mytag=myparams>
So <size=1>Lara</size><br/><size=0.7>Well, hello there!</size>
could be truncated to
<size=1>Lara</size><br/><size=0.7>Well, h</size>
for whatever function helper name could be.
This is so RPG style per char text showing can be easily done.
A linebreak seem to disappear when using the b tag.
Passing in this to richtext.create() gives correct results (there is one empty line between the lines):
Line 1\n\nLine 2
but this generates two lines without the empty line in between:
<b>Line 1</b>\n\nLine 2
It would be useful if it were somehow possible to add tags like <wait=1/> to be able to skip timer.delay repeats based on those kinds of tags. Maybe being able to check the next word/tag see if it's a wait tag and live edit it so it's at or below 0?
This would be useful to add a little time between line breaks / add suspense in text.
Not really an issue, but a feature request. While it´s cool to have text left/center/right aligned, I miss being able to justify it or have a function to manage space between words.
I know, a simple autojustify may create the "space river" effect in the text but... Is there any chance the feature will be included in this cool addon of defold?
local text = [[
<size=1>Lara</size><br/>
<size=0.7>Well, hello there!</size><br/>
<size=0.7>What do we have here?</size><img=icons:waiting/>]]
Becomes
It should be
Which requires
local text = [[<size=1>Lara</size><br/><size=0.7>Well, hello there!</size><br/><size=0.7>What do we have here?</size><img=icons:waiting/>]]
It would be nice to be able to format in scripts with multiline strings so it's a little easier to manage.
If I do
local last_word = richtext.truncate(words, length, {words = false})
then
pprint(last_word.tags)
for the text
text...,,,,,,,,,,,,,,,,,,,,,,,,,,,,<br/>
I will get
DEBUG:SCRIPT:
{ --[[0000019E1A974C90]]
br = true
}
DEBUG:SCRIPT:
{ --[[0000019E1A974C90]]
br = true
}
DEBUG:SCRIPT:
{ --[[0000019E1A974C90]]
br = true
}
etc.
Much like the deprecated <nobr>Long line with no breaks</nobr>
tag or <span style="white-space: nowrap">Long line with no breaks</span>
Forum request: https://forum.defold.com/t/defold-richtext/17239/36?u=britzl
An easy way of rebuilding richtext bodies as elements within are changed?
Stuff like this happens:
Printing out the generated words
table, I notice there's some empty text nodes, including one at the very end, which creates the extra newline (see words[7]
and words[8]
in the output below):
The text and options fed to richtext.create()
:
DEBUG:SCRIPT: "We'll be bringing in a subject. They're a trained actor, but should be fully in character for the duration. I'm to be considered invisible and inaudible by the actor, but I'll be guiding you along.<nobr> <img=t:advance/></nobr>",
dialogue,
{ --[[0x136f546e0]]
combine_words = true,
align = hash: [ALIGN_CENTER],
fonts = { --[[0x12070b0e0]]
timer = { --[[0x12070bd80]]
regular = hash: [timer]
},
title = { --[[0x12070bbd0]]
bold_italic = hash: [title_bolditalic],
regular = hash: [title],
italic = hash: [title_italic],
bold = hash: [title_bold]
},
dialogue_bold = { --[[0x12070b6e0]]
bold_italic = hash: [dialogue_italic],
regular = hash: [dialogue_bold],
italic = hash: [dialogue_bolditalic],
bold = hash: [dialogue]
},
dialogue = { --[[0x12070b130]]
bold_italic = hash: [dialogue_bolditalic],
regular = hash: [dialogue],
italic = hash: [dialogue_italic],
bold = hash: [dialogue_bold]
},
document_serif = { --[[0x12070b670]]
bold_italic = hash: [document_serif_bolditalic],
regular = hash: [document_serif],
italic = hash: [document_serif_italic],
bold = hash: [document_serif_bold]
},
document = { --[[0x12070b7a0]]
regular = hash: [document],
italic = hash: [document_italic],
bold = hash: [document_bold]
},
dialogue_small = { --[[0x12070b370]]
bold_italic = hash: [dialogue_bolditalic_small],
regular = hash: [dialogue_small],
italic = hash: [dialogue_italic_small],
bold = hash: [dialogue_bold_small]
},
dialogue_italic = { --[[0x12070b5b0]]
bold_italic = hash: [dialogue_bold],
regular = hash: [dialogue_italic],
italic = hash: [dialogue],
bold = hash: [dialogue_bolditalic]
}
},
layers = { --[[0x1207049c0]]
fonts = { --[[0x12066cd40]]
hash: [timer] = hash: [tutor_timer],
hash: [title_bolditalic] = hash: [tutor_title_bolditalic],
hash: [document_serif_italic] = hash: [tutor_document_serif_italic],
hash: [title_bold] = hash: [tutor_title_bold],
hash: [title_italic] = hash: [tutor_title_italic],
hash: [dialogue_bold_small] = hash: [tutor_dialogue_bold_small],
hash: [dialogue_bolditalic_small] = hash: [tutor_dialogue_bolditalic_small],
hash: [dialogue_italic_small] = hash: [tutor_dialogue_italic_small],
hash: [dialogue_small] = hash: [tutor_dialogue_small],
hash: [dialogue_italic] = hash: [tutor_dialogue_italic],
hash: [dialogue] = hash: [tutor_dialogue],
hash: [dialogue_bolditalic] = hash: [tutor_dialogue_bolditalic],
hash: [dialogue_bold] = hash: [tutor_dialogue_bold],
hash: [document] = hash: [tutor_document],
hash: [document_italic] = hash: [tutor_document_italic],
hash: [document_bold] = hash: [tutor_document_bold],
hash: [document_serif] = hash: [tutor_document_serif],
hash: [document_serif_bold] = hash: [tutor_document_serif_bold],
hash: [title] = hash: [tutor_title],
hash: [document_serif_bolditalic] = hash: [tutor_document_serif_bolditalic]
},
spinescenes = { } --[[0x136d608f0]],
images = { } --[[0x136d608a0]]
},
parent = @(398.80001831055, 137.89999389648, 0),
width = 1255
}
And the words
table:
DEBUG:SCRIPT:
{ --[[0x136f54830]]
1 = { --[[0x136f51100]]
outline = vmath.vector4(0, 0, 0, 0),
size = 1,
color = vmath.vector4(1, 1, 1, 1),
metrics = { --[[0x136e547b0]]
width = 1156,
max_descent = 13,
max_ascent = 48,
height = 61,
total_width = 1167
},
text = "We'll be bringing in a subject. They're a trained ",
tags = { } --[[0x136f555f0]],
node = We'll be bringing in a subject. They're a trained @(-583.5, 0, 0),
font = "dialogue",
shadow = vmath.vector4(0, 0, 0, 0)
},
2 = { --[[0x136f38750]]
outline = vmath.vector4(0, 0, 0, 0),
size = 1,
color = vmath.vector4(1, 1, 1, 1),
metrics = { --[[0x136de9360]]
width = 1093,
max_descent = 13,
max_ascent = 48,
height = 61,
total_width = 1104
},
text = "actor, but should be fully in character for the ",
tags = { ... } --[[0x136f555f0]],
node = actor, but should be fully in character for the @(-552, -61, 0),
font = "dialogue",
shadow = vmath.vector4(0, 0, 0, 0)
},
3 = { --[[0x136f39d60]]
outline = vmath.vector4(0, 0, 0, 0),
size = 1,
color = vmath.vector4(1, 1, 1, 1),
metrics = { --[[0x136da5680]]
width = 1057,
max_descent = 13,
max_ascent = 48,
height = 61,
total_width = 1068
},
text = "duration. I'm to be considered invisible and ",
tags = { ... } --[[0x136f555f0]],
node = duration. I'm to be considered invisible and @(-534, -122, 0),
font = "dialogue",
shadow = vmath.vector4(0, 0, 0, 0)
},
4 = { --[[0x136f016b0]]
outline = vmath.vector4(0, 0, 0, 0),
size = 1,
color = vmath.vector4(1, 1, 1, 1),
metrics = { --[[0x136d28390]]
width = 1248,
max_descent = 13,
max_ascent = 48,
height = 61,
total_width = 1248
},
text = "inaudible by the actor, but I'll be guiding you along.",
tags = { ... } --[[0x136f555f0]],
node = inaudible by the actor, but I'll be guiding you along.@(-654.5, -183, 0),
font = "dialogue",
shadow = vmath.vector4(0, 0, 0, 0)
},
5 = { --[[0x136ed7d60]]
outline = vmath.vector4(0, 0, 0, 0),
size = 1,
color = vmath.vector4(1, 1, 1, 1),
font = "dialogue",
text = " ",
nobr = true,
node = @(593.5, -183, 0),
metrics = { --[[0x136d1c5c0]]
width = 0,
max_descent = 13,
max_ascent = 48,
height = 0,
total_width = 11
},
tags = { --[[0x136ee3fa0]]
nobr = true,
img = "t:advance"
},
linebreak = false,
tag = "nobr",
shadow = vmath.vector4(0, 0, 0, 0)
},
6 = { --[[0x136ece3a0]]
outline = vmath.vector4(0, 0, 0, 0),
size = 1,
color = vmath.vector4(1, 1, 1, 1),
font = "dialogue",
text = "",
nobr = true,
image = { --[[0x136eda4d0]]
anim = "advance",
texture = "t"
},
node = box@(604.5, -183, 0),
tags = { --[[0x136ee3fa0]]
nobr = true,
img = "t:advance"
},
metrics = { --[[0x136d01700]]
width = 50,
height = 50,
total_width = 50
},
tag = "img",
shadow = vmath.vector4(0, 0, 0, 0)
},
7 = { --[[0x136ec67c0]]
outline = vmath.vector4(0, 0, 0, 0),
size = 1,
color = vmath.vector4(1, 1, 1, 1),
font = "dialogue",
text = "",
nobr = true,
node = @(654.5, -183, 0),
metrics = { --[[0x136d03b70]]
width = 0,
max_descent = 13,
max_ascent = 48,
height = 61,
total_width = 0
},
tags = { --[[0x136ec5860]]
nobr = true
},
linebreak = false,
tag = "nobr",
shadow = vmath.vector4(0, 0, 0, 0)
},
8 = { --[[0x136ebeab0]]
outline = vmath.vector4(0, 0, 0, 0),
size = 1,
color = vmath.vector4(1, 1, 1, 1),
font = "dialogue",
text = "",
node = @(0, -244, 0),
tags = { } --[[0x136ec8680]],
metrics = { --[[0x136d05070]]
width = 0,
max_descent = 13,
max_as...
[Output truncated]
A way to add sprite textures inbetween text to add emojis or other kinds of images such as resource icons.
Allow adjustment to padding between words? So negative y margin/padding could move lines closer together with less of a gap.
I have the following scenario:
I have a text bubble with some arbitrary text, which needs to have a small caret image prepended. Something like this:
The caret must never be orphaned alone on a line. I tried adding <nobr>
before the last word, but I'm left with situations like this, which are invalid:
Jennifer has <i>logged <nobr>in</i> <img=interludes:advance/></nobr>
add text shadow color, outline color tags.
add text shadow color, outline color as field in settings
I don't know if RichText is at fault, but we've been having this bug happen once or twice a day on one of our expo machines at Gamescom. RichText is the main shared codepath between all the occurence points of this bug, so I said I'd share.
We haven't had it ever happen on any other PC.
The machine is a MacBookPro10,1, 8GB RAM, Mojave 10.14.5, NVIDIA GT650M, connected to an external display.
Nothing in the console.
Either of these:
<img=texture:image>
</img=texture:image>
Come up with a simple to use system that allows a developer to chose which tags to assign to which layers to reduce the number of draw calls needed
Currently you can only do left, center and right. Would it be possible to add in the others such as gui.PIVOT_SE, gui.PIVOT_NW, etc
Box nodes. Perhaps using <img>
tag?
A way to break words within special tags into individually characters and then be able to iterate over them to produce different effects.
Maybe something like
Taste the <char class="rainbow">RAINBOW!</char> or <char class="doom">don't</char>
Would return two char numeric tables with a node for each character within the char tag allowing the char tables to be stored and their nodes iterated over to produce different effects.
I don't think that RichText should to handle this case explicitly, I'm just wondering if you have any ideas how to solve this for my project.
I'm dealing with quite a bit of text, generally filling the screen. I'm using the combine_words option, and most of the time, most of the text is plain, so there is no issue at all. But sometimes I will have text that definitely exceeds the 1024 max node limit per gui. I'm making a MUD client (an interface for online text-adventure games), so I'm not in control of the text that I get, only what I do with it. I know I need to split things between multiple GUI components, I'm just not sure what the best way to split it up is.
Some ways I've thought about:
I added this tag to a custom version of RichText for my needs in a visual novel style project. It would probably be useful for others but I'm not sure if it should be a part of the main project. These are the main changes.
elseif tag == "wait" then
settings.wait = tonumber(params)
if is_empty then
-- empty tag, ie tag without content
-- example <br/> and <img=texture:image/>
local tag_settings = parse_tag(name, params)
merge_tags(word_settings, tag_settings)
local add_this_word = ""
if tag_settings.wait ~= nil and tag_settings.wait > 0 then
local wait_word = "\226\128\139"
for i=1, tag_settings.wait do
add_this_word = add_this_word .. wait_word
end
end
add_word(add_this_word, word_settings, all_words)
elseif not is_endtag then
Then the tag is used like <wait=30>
which inserts 30 zero width characters. This is useful in visual novel projects where you have text appearing once character at a time and you want to add small delays between actions.
Like texture it would be useful for animated emoticons with lower space requirements
Allow the user to setup a callback function to run if the user wants to use tags which are not defined in the normal extension.
Example for a possible action tag would be
<action=particles:text_emitter>Make this pretty</action>
and
<action=sound:ding/>
The main thing the callback needs to be used for is defining how the data of the tag is handled so in the sound case the "sound:ding" would need to be handled when doing parse_tag.
local parse = require("richtext.parse")
parse.callback = function(tag, params, settings)
if tag == "action" then
local action_type, action_option = params:match("(.-):(.*)")
settings.action = {
type = action_type,
option = action_option
}
end
end
.... in parse.lua below other tag definitions
elseif M.callback then
M.callback(tag, params, settings)
end
for expample
rnode = richtext.create('<color=red>my text</color>')
.... somewhere deep in code ....
rnode.text += '<color=green>new text</color>'
The spacing between words that rich-text applies is not always consistent with the one that Defold applies. This causes lines of text to be more compact than the reported metrics and stuff like this happens, where a lot of extra space is being unused and the line is not well center-aligned:
I think the correct approach for combine_words would be to attempt to combine the words during layout (preferably between getting word metrics and actually creating the node) and measure the whole combined string, not just words individually.
I can try to write a PR for that, as I would really want to have more accurate word spacing in Interrogation, both for improving readability and improving text density.
Return height of generated text from richtext.create()
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.