Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Label method to get position of a character #8299

Closed
KoBeWi opened this issue Oct 30, 2023 · 7 comments · Fixed by godotengine/godot#84185
Closed

Add Label method to get position of a character #8299

KoBeWi opened this issue Oct 30, 2023 · 7 comments · Fixed by godotengine/godot#84185
Milestone

Comments

@KoBeWi
Copy link
Member

KoBeWi commented Oct 30, 2023

Describe the project you are working on

Game with text.

Describe the problem or limitation you are having in your project

I have various control prompts, i.e. stuff like "Press A to jump". I want to display the button image inside text, but I have a custom node type that displays action's icon, automatically switching between keyboard and joypad etc. so I can't use RichTextLabel's images.

I want to use a text placeholder, e.g. "Press * to jump" and place the button at *. However I couldn't find a reliable way to get the position of a character. I tried some TextServer methods, but to get it work I'd have to reimplement half of Label's text shaping. It's not reasonable.

Describe the feature / enhancement and how it helps to overcome the problem or limitation

If Label had a method that returns a Vector2 position of a given character, I could use it to position my node.

var pos = $Label.get_character_position($Label.text.find("*"))
$Label/TextureRect.position = pos

Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams

Add Label.get_character_position(idx: int) method, which would return a local position of the specified character.

If this enhancement will not be used often, can it be worked around with a few lines of script?

No, text shaping is complex.

Is there a reason why this should be core and not an add-on in the asset library?

It could work as an addon, but it's difficult to implement.

@bruvzg
Copy link
Member

bruvzg commented Oct 30, 2023

I have various control prompts, i.e. stuff like "Press A to jump". I want to display the button image inside text, but I have a custom node type that displays action's icon, automatically switching between keyboard and joypad etc. so I can't use RichTextLabel's images.

The proper way to do something like this would be using TextServer shaped_text_add_object (to add embedded object to string) and shaped_text_get_object_rect (to receive rect after shaping) methods. This is what RTL is using for images.

Character position can be implemented in the same way as text selection code (with length of 1). But there are problems with getting character positions: some characters do not have defined position (control chars, parts of ligature), or might have multiple drawn elements with the different positions (composite characters with diacritics, elongations).

@KoBeWi
Copy link
Member Author

KoBeWi commented Oct 30, 2023

shaped_text_add_object() looks like it could work, but it requires shaped text RID, which Label does not expose. Also from what I gathered, there is separate RID for each line, so even getting line's RID is not that helpful, because line is not obvious when you use autowrapping. Getting line RID for a specific character index could be a replacement for the proposed method, if it's easier to implement.

Character position can be implemented in the same way as text selection code (with length of 1). But there are problems with getting character positions: some characters do not have defined position (control chars, parts of ligature), or might have multiple drawn elements with the different positions (composite characters with diacritics, elongations).

Given the purpose of this method, this is not really a concern.

@bruvzg
Copy link
Member

bruvzg commented Oct 30, 2023

but it requires shaped text RID, which Label does not expose. Also from what I gathered, there is separate RID for each line, so even getting line's RID is not that helpful

Yes, simple exposing RID won't work, since label will reset them on every change (and add_object should be used when setting text for shaping). It's like:

var paragraph

func _ready():
	var text = "test * text"
	var pos = text.find("*")

	# setup shaped text
	paragraph = TextParagraph.new()
	paragraph.add_string(text.substr(0, pos), get_theme_font("font"), get_theme_font_size("font_size"))
	paragraph.add_object("Button", Vector2(20, 20))
	paragraph.add_string(text.substr(pos + 1, -1), get_theme_font("font"), get_theme_font_size("font_size"))

	# move button	
	for i in range(paragraph.get_line_count()):
		for o in paragraph.get_line_objects(i):
			if o == "Button":
				var rect = paragraph.get_line_object_rect(i, o)
				$Button.set_position(rect.position)
				$Button.set_size(rect.size)
	queue_redraw()

func _draw():
	paragraph.draw(get_canvas_item(), Vector2())

@KoBeWi
Copy link
Member Author

KoBeWi commented Oct 30, 2023

Ok so the object does not have an index, it's just added at the end. Then it won't work for me, I want to use Label. With the method you provided I'd have to implement custom wrapping and other stuff .-.

@bruvzg
Copy link
Member

bruvzg commented Oct 30, 2023

I guess something like this should do it - godotengine/godot#84185 (returns rects not just positions).

@KoBeWi
Copy link
Member Author

KoBeWi commented Oct 30, 2023

Works perfectly, thanks.

@peachpieproductions
Copy link

Hey there, will this function be added to RichTextLabel? @bruvzg

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants