Can I modify existing text in a RichTextLabel without accumulating arbitrarily large memory overhead?

Godot Version

4.6.2

Question

I am logging through a RichTextLabel and a custom logger, which works fine. However, I would like the last (newest) message to always be a different colour than the previous (older) messages. Now, what I can do is to clear the label every time a message comes in, then put the text back in the “old message” colour, then add the new message in the “new message” colour. That seems like a terrible way to do it, but I cannot see a way to change properties of all existing text in a RichTextLabel otherwise.

What is the correct way to achieve this?

Did you try just appending the text with the new message:

If I understand you correctly, you can simply add a color bracket to the string. Something like:

var log_array:Array # array of logged Strings
var log_index:int # the current Array index

if log_index == 0:
	label.text = "[color=red]" + log_array[log_index] + "[/color] # newest message is red
else:
	label.text = log_array[log_index] # other messages are default color

Is that what you mean?

Yes, that is something I can do. However, it requires me to keep all the text that was ever put into the Label in a separate variable. I thought the Label might be able to do this without keeping track of the text in two places.

Yes, if you want that specific feature, you have to completely rewrite the entire RichTextLabel text every time a message updates. But that is never going to be your performance bottleneck in a game. It’s almost always going to be graphics. Keep in mind your RichTextLabel is being rendered to the screen in every draw call anyway, which defaults to 60 times per second.

You can leverage the Godot way of doing things to solve this problem. RichTextLabels are Control nodes. Just make each message its own RichTextLabel, and put them in a VBoxContainer. When you put out a new message, edit the color of the previous label, then add the latest message. A lot less code in the end.

In my game, Katamari Mech Spacey I created a system like this, which you can see here.

Each message appears in the window, and then dissolves over time. It has a maximum queue of 5 messages, and deletes them faster if you get a lot at once. It also supports displaying icons. I talk a little about making it in my Katamari Mech Spacey Day 2 DevLog.

That is a good idea. I was a big fan of scrolling and everything just working out of the box with the RichTextLabel, but of course a container can also do that with a little more work.

In case you want another way to do it. As I suggested you can just append the text and set the

color as each log message comes in.

Alternatively a bbcode string can be passed to append text (append_text("Normal [color=yellow]Yellow Text[/color] Normal") instead of stacking it like in the example below.

The below image shows two rich text labels side by side, each has it’s message printed in the same color, however the rich text label on the right has it’s previous text set to cadet blue before printing the new message.

So a function to add text, of a certain color might look like this

func add_text(text,color):
	# if you want to change all current text to a single color
	# before adding the text enable the following line which will set
	# the color to Cadetblue before adding the new text, color can be any bbcode color
	#rich_text_label.text = "[color=Cadetblue]" + rich_text_label.get_parsed_text() + "[/color]"
	rich_text_label.push_color(color)
	rich_text_label.append_text(text)
	rich_text_label.pop()

Thank you, I was looking exactly for get_parsed_text.