Defold-RichText

Have you required RichText?

Okay, some time later, I have successfully figured out how to “require” a module. That information should probably be on the “working with library projects” page, and it would be helpful if there was just one name for an engine add-on, instead of switching between module/dependency/library.

Next question:
for a normal text node, it’s okay to use (in update)

		local text= (self.minutes.."."..self.seconds)
		local s= gui.get_node("timernode")
		gui.set_text(s, text)

and the node constantly updates itself as the minutes and seconds go down. How can you do this is a rich text node?

You can get all nodes from richtext and update the one you want, but I’d suggest that you delete and recreate the nodes. If it’s not too much text to process it’s not really going to have much effect on performance.

okay, so here is the code I use to generate the rich text node:

	self.minutes = "minutes"
	self.seconds = "seconds"
	local sampletext = self.minutes.."<size=0.7>".."."..self.seconds
	print(sampletext) 
	self.richtextnode = richtext.create(sampletext, "win96", settings)
	pprint(self.richtextnode)

What is the name of the rich text node in that case (or, how do i access it)? gui.get_node(self.richtextnode) doesn’t seem to work and i can’t find anything in the API.

richtext.create() returns a table structire containing data about the words, tags, nodes etc. There’s a bunch of examples in the repo showing how you can use Defold-RichText:

I’d suggest doing something like this:

local text = ("%d<size=0.7>%d"):format(23, 59)
local words, metrics = richtext.create(text, "Roboto-Regular", { position = vmath.vector3(0, 390, 0) })
gui.set_text(words[1].node, "foobar") -- change text of first word
3 Likes

Many thanks Britzl. I am not a fluent user of lua so there’s a lot I’ll have to learn from your examples! But I am confident I can get it sorted now.

1 Like

You are going to kill me! But i finally found this link https://www.defold.com/ref/label/ and was able to use metrics[width] and make two labels, the second one changing position dynamically according to the width of the first one’s text (previously i was sure I could only get the label size, which is here https://www.defold.com/manuals/label/, rather than the text width).

Still, I learned about GUI scripts, which I had never used before, and also, I learned a little more about lua, which is always handy.

2 Likes

How can we stop truncated nodes at the end, when the whole text appears and we want to wait for a while with this or wait for user input to proceed?

I’m not sure I understand what you mean. Did you check the examples project in the github repo?

Yes, I’m modifying the example project :wink: I mean an effect like this:

richtext_truncated

This gif is looped, but at the end, the text stops and shows on the screen until I click/touch anywhere to proceed.

I did it by canceling the timer:

if length == metrics.char_count then
    delay.cancel_all()
end

I used delay.cancel_all() because when I assigned this timer to local id and called cancel(id) I’m getting an error:

ERROR:DEFAULT: Error running delay callback: /example/example.gui_script:79: bad argument #1 to ‘cancel’ (number expected, got nil)

I don’t know why it shows, because cancel(id) is used like in the Timer example and id is assigned like this:

local id = delay.repeating(0.05, function()

You need to declare that id, then use it. Like this (using the native timer, same principle using my timer extension):

local id = nil
id = timer.delay(0.1, true, function()
    if length == metrics.char_count then
        timer.cancel(id)
    end
end)
3 Likes

Good to know it, thanks! :wink: It’s working now

2 Likes

Hi.
Is there a chance this awesome Rich Text feature would present the text in pages? Something like this: https://www.youtube.com/watch?v=3XkauVYS6o4

I´m developing a CYOA engine in Unity because of that per-page feature, but gladly shift into defold if I could deploy pages easily.

Thx!

You can do it quite easily by either pre-generating text nodes with rich text for all the pages and using gui.set_enabled() to show/hide the parents of each page, or by deleting and re-generating nodes on each page flip (which is rather efficient in defold). We actually use both approaches in our game (which is 50% page-flipping through various documents) and it works great for us.

Anyway, it’s not a feature of RichText, but you can build it on top of it.

1 Like

Thx for the tip. :slight_smile:
One thing that aproach failed to acomplish is adapting to the text. In my engine users can change text size and font, so the same text would not fit in the same amount of pages.
Besides, text is not pre-existent, it is built on the fly with users actions and may change way hard on its length. :frowning:

I’m not sure I understand the question. You want to show multiple pages of styled text with some Next/Prev buttons to navigate? And the text is not static but generated based on user actions? And since the generated text can change in size depending on user action and choice of text size and font you don’t know where each page ends?

RichText can take a string of text containing special markup and generate text nodes for these and position them according to constraints such as max line length. This is quite fast and there would be no noticeable delay should you do this every time a page needs to be shown.

So the problem is how to know where to insert page breaks? If RichText was to have a function that would show text up until a certain height is reached and return where the page break was then I guess that would solve the problem, wouldn’t it?

2 Likes

Yes to the firts paragraph. There is a long string of text which may change via font size, font type and/or new substrings added or removed via code. So, fully dynamic. The unity solution manage all the interal stuff so I only have to say “show page 3” at any moment and it will show the actual page 3, whatever it is.

I guess the aproach of the last paragraph may do the trick, if it returns like an array of “pages”, since visible text will not always start at the begining of the string but at the begining of the actual showed page.

it might be a bit more complicated than that, since you can’t always split the text if a tag spans two lines. I think the best solution would be to leave rich text as is and make a function that, given a words table, queries their positions and selects only the ones in a given height range, moves them up and deletes the other ones.

Yes, this should be fairly easy at least.

This would be quite a bit more tricky.

I think RichText could use an option/function that will parse all of the text but not create any nodes and return the data as a table. You could then iterate over the table and figure out what to keep, remove the rest and pass the modified table back to RichText and only ask it to create the nodes.

I’ve been trying this out the last couple days for a new project. It’s pretty awesome, thanks!

I’ve run into a bit of a snag though. RichText can’t seem to handle text like this:

<color=red><b>some text</color></b>

where the closing tags aren’t in exactly the reverse order of the opening tags. Would it be possible to make it handle those cases?

I’m working with text from the internet, so my only options are for RichText to handle it, or to make my parser somehow reorder the tags correctly.