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

[Complex Text Layouts] Implement TextServer interface. #41100

Merged
merged 8 commits into from Nov 28, 2020

Conversation

bruvzg
Copy link
Member

@bruvzg bruvzg commented Aug 7, 2020

Part of godotengine/godot-proposals#1180 implementation.

Depends on #40999

TODO:

  • TextServer interface. ✔️
    • TextServer Fallback implementation (based on existing Font implementations) ✔️
      • Dynamic font data ✔️
      • Bitmap font data ✔️
    • TextServer ICU + HarfBuzz implementation. ✔️
      • Dynamic font data ✔️
      • Bitmap font data ✔️
    • TextServer documentation ✔️
    • TextServer unit test + test project ✔️
  • Font class refactoring ✔️
  • Simple controls (everything except Label, LineEdit, TextEdit, RichTextLabel) TS base text ✔️
  • Interface mirroring ✔️
  • Add BiDi override for path LineEdits and CodeEdit ✔️
  • Complex controls ✔️
    • Label ✔️
    • LineEdit ✔️
    • TextEdit + CodeEdit ✔️
    • RichTextLabel (probably will be done as the separate PR)
  • Editor tools for Font substitution config and TS data export ✔️
  • Update controls / font documentation ✔️
  • Bump MessageQuery size to 2048 KB to handle additional LineEdit / TextEdit popup submenus (superseded by MessageQueue: Change default queue size to 4096 KB #42296) ✔️

Breaking changes:

  • BitmapFont, DynamicFont and DyamicFontData are removed and replaced with universal Font + FontData, actual implementation is handled by TextServer.
  • Font size and outline parameters are no longer font properties and can be passed to draw functions directly, theme properties added accordingly.
  • Font and CanvasItem API changes:
    • Added draw_multiline_string function.
    • Added additional parameters to all draw functions.
    • New font fallback API similar to DynamicFont fallback (single list of FontData).
Major Font API changes: Old:
	Size2 get_string_size(const String &p_string) const;
	void draw(RID p_canvas_item, const Point2 &p_pos, const String &p_text, const Color &p_modulate = Color(1, 1, 1), int p_clip_w = -1, const Color &p_outline_modulate = Color(1, 1, 1)) const;

	void draw_halign(RID p_canvas_item, const Point2 &p_pos, HAlign p_align, float p_width, const String &p_text, const Color &p_modulate = Color(1, 1, 1), const Color &p_outline_modulate = Color(1, 1, 1)) const;

	Size2 get_wordwrap_string_size(const String &p_string, float p_width) const;

	Size2 get_char_size(char32_t p_char, char32_t p_next = 0) const;
	float draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next = 0, const Color &p_modulate = Color(1, 1, 1), bool p_outline = false) const;

New:

	Size2 get_string_size(const String &p_text, int p_size = -1) const;
	void draw_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, float p_width = -1, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint8_t p_flags = TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const;

	Size2 get_multiline_string_size(const String &p_text, float p_width = -1, int p_size = -1, uint8_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND) const;
	void draw_multiline_string(RID p_canvas_item, const Point2 &p_pos, const String &p_text, HAlign p_align = HALIGN_LEFT, float p_width = -1, int p_max_lines = -1, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0), uint8_t p_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND) const;

	Size2 get_char_size(char32_t p_char, char32_t p_next = 0, int p_size = -1) const;
	float draw_char(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, char32_t p_next = 0, int p_size = -1, const Color &p_modulate = Color(1, 1, 1), int p_outline_size = 0, const Color &p_outline_modulate = Color(1, 1, 1, 0)) const;

TextServer interface structure:

ts_map

Test projects:

GDNative plugin template: https://github.com/bruvzg/gdnative_text_server_template

Current status:

  • Backend: Working.
  • Controls: All controls except RichTextLabel and containers support mirroring and use TexeServer base text rendering.

Screenshot 2020-09-10 at 15 33 07

Screenshot 2020-10-29 at 13 37 53

Input:

Input of the characters outside BMP (codes > 0xFFFF, emoji, rare/ancient scripts, e.t.c.) is fully tested on macOS, Windows 10, Android and Wine under macOS.

Should work on Linux (input is received as UTF-8 string), but might need some more testing with different input methods.

On iOS, it should work as well (input is received as UTF-16 string), but I can't test it since I do not have any iOS device, and simulator currently do not have all Metal features supported to run Godot.

Screenshots - click to expand

android
macos
ubuntu
win_10

Fixed and related issues / PRs:

Previous implementations: #21791, #10546

Fixes #40688
Fixes #28371
Fixes #3081
Fixes #9961
Fixes #982

@bruvzg bruvzg added this to the 4.0 milestone Aug 7, 2020
@bruvzg bruvzg force-pushed the ctl_text_server_interface branch 4 times, most recently from 94e903b to 756fa32 Compare August 12, 2020 12:54
@bruvzg bruvzg force-pushed the ctl_text_server_interface branch 10 times, most recently from 3ad959b to 31a02c8 Compare August 26, 2020 11:07
@bruvzg bruvzg force-pushed the ctl_text_server_interface branch 11 times, most recently from e2ddb26 to 72e373f Compare September 3, 2020 09:10
@reduz
Copy link
Member

reduz commented Nov 27, 2020

This is an incredible piece of work. I think its really good, probably best to leave it up to the community to use and give feedback on usage but I think it should be good enough. My main fear is backwards compatibility with older scenes, but I guess it should work?

@reduz
Copy link
Member

reduz commented Nov 27, 2020

We are discussing a way to do compatibility filters between older and newer Godot versions, so there might be a way I guess.

@akien-mga akien-mga merged commit a6751e6 into godotengine:master Nov 28, 2020
@akien-mga
Copy link
Member

Here we go! A huge thank you for this amazing feature!

@aaronfranke
Copy link
Member

aaronfranke commented Dec 15, 2020

bool Control::is_layout_rtl() const {
	if (data.layout_dir == LAYOUT_DIRECTION_INHERITED) {
		Window *parent_window = Object::cast_to<Window>(get_parent());
		Control *parent_control = get_parent_control();
		if (parent_control) {
			return parent_control->is_layout_rtl();
		} else if (parent_window) {
			return parent_window->is_layout_rtl();
		} else {
			if (GLOBAL_GET("display/window/force_right_to_left_layout_direction")) {
				return true;
			}

It seems that this can fail in the project manager. GLOBAL_GET will fail in the project manager because there is no ProjectSettings singleton, and Control::is_layout_rtl is used in the project manager. So if data.layout_dir == LAYOUT_DIRECTION_INHERITED and neither parent_control or parent_window exist, the project manager will crash.

[1] /lib/x86_64-linux-gnu/libc.so.6(+0x46210) [0x7f92652e4210] (??:0)                                                                          
[2] Object::get(StringName const&, bool*) const (/home/aaronfranke/workspace/godot/core/object/object.cpp:445)
[3] Control::is_layout_rtl() const (/home/aaronfranke/workspace/godot/scene/gui/control.cpp:442 (discriminator 4))
[4] BoxContainer::_resort() (/home/aaronfranke/workspace/godot/scene/gui/box_container.cpp:47 (discriminator 2))
[5] BoxContainer::set_alignment(BoxContainer::AlignMode) (/home/aaronfranke/workspace/godot/scene/gui/box_container.cpp:310)

Another possible point of failure is if data.layout_dir == LAYOUT_DIRECTION_LOCALE, here:

	} else if (data.layout_dir == LAYOUT_DIRECTION_LOCALE) {
		if (GLOBAL_GET("display/window/force_right_to_left_layout_direction")) {
			return true;
		}

@KoBeWi
Copy link
Member

KoBeWi commented Mar 22, 2021

So I noticed it just now, but why does this PR introduce theme font sizes instead of using theme constants for this purpose?

@bruvzg
Copy link
Member Author

bruvzg commented Mar 22, 2021

So I noticed it just now, but why does this PR introduce theme font sizes instead of using theme constants for this purpose?

Font size have some extra fallback logic for the Theme and global default values, constants use 0 when net set.

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