1
0
mirror of https://github.com/godotengine/godot synced 2024-07-05 17:18:46 +00:00

[TextServer] Improve ligature cursor handling.

Fix mid-grapheme hit test.
Fix OpenType features property handling, add default features override option.
Enable mid-grapheme cursor by default.
This commit is contained in:
bruvzg 2021-11-18 23:36:22 +02:00
parent 5a61822d7c
commit c89c515ccf
No known key found for this signature in database
GPG Key ID: 7960FCF39844EC38
27 changed files with 403 additions and 286 deletions

View File

@ -115,7 +115,7 @@
<argument index="4" name="outline" type="float" default="0.0" />
<argument index="5" name="pixel_range" type="float" default="4.0" />
<description>
Draws a textured rectangle region of the multi-channel signed distance field texture at a given position, optionally modulated by a color. See [method FontData.set_multichannel_signed_distance_field] for more information and caveats about MSDF font rendering.
Draws a textured rectangle region of the multi-channel signed distance field texture at a given position, optionally modulated by a color. See [member FontData.multichannel_signed_distance_field] for more information and caveats about MSDF font rendering.
If [code]outline[/code] is positive, each alpha channel value of pixel in region is set to maximum value of true distance in the [code]outline[/code] radius.
Value of the [code]pixel_range[/code] should the same that was used during distance field texture generation.
</description>

View File

@ -79,12 +79,6 @@
Returns text server font cache entry resource id.
</description>
</method>
<method name="get_data" qualifiers="const">
<return type="PackedByteArray" />
<description>
Returns contents of the dynamic font source file.
</description>
</method>
<method name="get_descent" qualifiers="const">
<return type="float" />
<argument index="0" name="cache_index" type="int" />
@ -93,30 +87,6 @@
Returns font descent (number of pixels below the baseline).
</description>
</method>
<method name="get_fixed_size" qualifiers="const">
<return type="int" />
<description>
Returns font fixed size.
</description>
</method>
<method name="get_font_name" qualifiers="const">
<return type="String" />
<description>
Returns font family name.
</description>
</method>
<method name="get_font_style" qualifiers="const">
<return type="int" />
<description>
Returns font style flags, see [enum TextServer.FontStyle].
</description>
</method>
<method name="get_font_style_name" qualifiers="const">
<return type="String" />
<description>
Returns font style name.
</description>
</method>
<method name="get_glyph_advance" qualifiers="const">
<return type="Vector2" />
<argument index="0" name="cache_index" type="int" />
@ -180,12 +150,6 @@
Returns rectangle in the cache texture containing the glyph.
</description>
</method>
<method name="get_hinting" qualifiers="const">
<return type="int" enum="TextServer.Hinting" />
<description>
Returns the font hinting mode. Used by dynamic fonts only.
</description>
</method>
<method name="get_kerning" qualifiers="const">
<return type="Vector2" />
<argument index="0" name="cache_index" type="int" />
@ -216,24 +180,6 @@
Returns list of language support overrides.
</description>
</method>
<method name="get_msdf_pixel_range" qualifiers="const">
<return type="int" />
<description>
Returns the width of the range around the shape between the minimum and maximum representable signed distance.
</description>
</method>
<method name="get_msdf_size" qualifiers="const">
<return type="int" />
<description>
Returns source font size used to generate MSDF textures.
</description>
</method>
<method name="get_oversampling" qualifiers="const">
<return type="float" />
<description>
Returns font oversampling factor, if set to [code]0.0[/code] global oversampling factor is used instead. Used by dynamic fonts only.
</description>
</method>
<method name="get_scale" qualifiers="const">
<return type="float" />
<argument index="0" name="cache_index" type="int" />
@ -346,18 +292,6 @@
Returns [code]true[/code] if a Unicode [code]char[/code] is available in the font.
</description>
</method>
<method name="is_antialiased" qualifiers="const">
<return type="bool" />
<description>
Returns [code]true[/code] if font 8-bit anitialiased glyph rendering is supported and enabled.
</description>
</method>
<method name="is_force_autohinter" qualifiers="const">
<return type="bool" />
<description>
Returns [code]true[/code] if auto-hinting is supported and preferred over font built-in hinting. Used by dynamic fonts only.
</description>
</method>
<method name="is_language_supported" qualifiers="const">
<return type="bool" />
<argument index="0" name="language" type="String" />
@ -365,12 +299,6 @@
Returns [code]true[/code], if font supports given language ([url=https://en.wikipedia.org/wiki/ISO_639-1]ISO 639[/url] code).
</description>
</method>
<method name="is_multichannel_signed_distance_field" qualifiers="const">
<return type="bool" />
<description>
Returns [code]true[/code] if glyphs of all sizes are rendered using single multichannel signed distance field generated from the dynamic font vector data.
</description>
</method>
<method name="is_script_supported" qualifiers="const">
<return type="bool" />
<argument index="0" name="script" type="String" />
@ -455,13 +383,6 @@
Renders the range of characters to the font cache texture.
</description>
</method>
<method name="set_antialiased">
<return type="void" />
<argument index="0" name="antialiased" type="bool" />
<description>
If set to [code]true[/code], 8-bit antialiased glyph rendering is used, otherwise 1-bit rendering is used. Used by dynamic fonts only.
</description>
</method>
<method name="set_ascent">
<return type="void" />
<argument index="0" name="cache_index" type="int" />
@ -471,13 +392,6 @@
Sets the font ascent (number of pixels above the baseline).
</description>
</method>
<method name="set_data">
<return type="void" />
<argument index="0" name="data" type="PackedByteArray" />
<description>
Sets font source data, e.g contents of the dynamic font source file.
</description>
</method>
<method name="set_descent">
<return type="void" />
<argument index="0" name="cache_index" type="int" />
@ -487,41 +401,6 @@
Sets the font descent (number of pixels below the baseline).
</description>
</method>
<method name="set_fixed_size">
<return type="void" />
<argument index="0" name="fixed_size" type="int" />
<description>
Sets the fixed size for the font.
</description>
</method>
<method name="set_font_name">
<return type="void" />
<argument index="0" name="name" type="String" />
<description>
Sets the font family name.
</description>
</method>
<method name="set_font_style">
<return type="void" />
<argument index="0" name="style" type="int" />
<description>
Sets the font style flags, see [enum TextServer.FontStyle].
</description>
</method>
<method name="set_font_style_name">
<return type="void" />
<argument index="0" name="name" type="String" />
<description>
Sets the font style name.
</description>
</method>
<method name="set_force_autohinter">
<return type="void" />
<argument index="0" name="force_autohinter" type="bool" />
<description>
If set to [code]true[/code] auto-hinting is preferred over font built-in hinting.
</description>
</method>
<method name="set_glyph_advance">
<return type="void" />
<argument index="0" name="cache_index" type="int" />
@ -573,13 +452,6 @@
Sets rectangle in the cache texture containing the glyph.
</description>
</method>
<method name="set_hinting">
<return type="void" />
<argument index="0" name="hinting" type="int" enum="TextServer.Hinting" />
<description>
Sets font hinting mode. Used by dynamic fonts only.
</description>
</method>
<method name="set_kerning">
<return type="void" />
<argument index="0" name="cache_index" type="int" />
@ -598,35 +470,6 @@
Adds override for [method is_language_supported].
</description>
</method>
<method name="set_msdf_pixel_range">
<return type="void" />
<argument index="0" name="msdf_pixel_range" type="int" />
<description>
Sets the width of the range around the shape between the minimum and maximum representable signed distance.
</description>
</method>
<method name="set_msdf_size">
<return type="void" />
<argument index="0" name="msdf_size" type="int" />
<description>
Sets source font size used to generate MSDF textures.
</description>
</method>
<method name="set_multichannel_signed_distance_field">
<return type="void" />
<argument index="0" name="msdf" type="bool" />
<description>
If set to [code]true[/code], glyphs of all sizes are rendered using single multichannel signed distance field (MSDF) generated from the dynamic font vector data. MSDF rendering allows displaying the font at any scaling factor without blurriness, and without incurring a CPU cost when the font size changes (since the font no longer needs to be rasterized on the CPU). As a downside, font hinting is not available with MSDF. The lack of font hinting may result in less crisp and less readable fonts at small sizes.
[b]Note:[/b] MSDF font rendering does not render glyphs with overlapping shapes correctly. Overlapping shapes are not valid per the OpenType standard, but are still commonly found in many font files, especially those converted by Google Fonts. To avoid issues with overlapping glyphs, consider downloading the font file directly from the type foundry instead of relying on Google Fonts.
</description>
</method>
<method name="set_oversampling">
<return type="void" />
<argument index="0" name="oversampling" type="float" />
<description>
Sets font oversampling factor, if set to [code]0.0[/code] global oversampling factor is used instead. Used by dynamic fonts only.
</description>
</method>
<method name="set_scale">
<return type="void" />
<argument index="0" name="cache_index" type="int" />
@ -701,4 +544,45 @@
</description>
</method>
</methods>
<members>
<member name="antialiased" type="bool" setter="set_antialiased" getter="is_antialiased" default="true">
If set to [code]true[/code], font 8-bit anitialiased glyph rendering is supported and enabled.
</member>
<member name="data" type="PackedByteArray" setter="set_data" getter="get_data" default="PackedByteArray()">
Contents of the dynamic font source file.
</member>
<member name="fixed_size" type="int" setter="set_fixed_size" getter="get_fixed_size" default="0">
Font size, used only for the bitmap fonts.
</member>
<member name="font_name" type="String" setter="set_font_name" getter="get_font_name" default="&quot;&quot;">
Font family name.
</member>
<member name="font_style" type="int" setter="set_font_style" getter="get_font_style" default="0">
Font style flags, see [enum TextServer.FontStyle].
</member>
<member name="force_autohinter" type="bool" setter="set_force_autohinter" getter="is_force_autohinter" default="false">
If set to [code]true[/code], auto-hinting is supported and preffered over font built-in hinting. Used by dynamic fonts only.
</member>
<member name="hinting" type="int" setter="set_hinting" getter="get_hinting" enum="TextServer.Hinting" default="1">
Font hinting mode. Used by dynamic fonts only.
</member>
<member name="msdf_pixel_range" type="int" setter="set_msdf_pixel_range" getter="get_msdf_pixel_range" default="16">
The width of the range around the shape between the minimum and maximum representable signed distance.
</member>
<member name="msdf_size" type="int" setter="set_msdf_size" getter="get_msdf_size" default="48">
Source font size used to generate MSDF textures.
</member>
<member name="multichannel_signed_distance_field" type="bool" setter="set_multichannel_signed_distance_field" getter="is_multichannel_signed_distance_field" default="false">
If set to [code]true[/code], glyphs of all sizes are rendered using single multichannel signed distance field generated from the dynamic font vector data.
</member>
<member name="opentype_feature_overrides" type="Dictionary" setter="set_opentype_feature_overrides" getter="get_opentype_feature_overrides" default="{}">
Font OpenType feature set override.
</member>
<member name="oversampling" type="float" setter="set_oversampling" getter="get_oversampling" default="0.0">
Font oversampling factor, if set to [code]0.0[/code] global oversampling factor is used instead. Used by dynamic fonts only.
</member>
<member name="style_name" type="String" setter="set_font_style_name" getter="get_font_style_name" default="&quot;&quot;">
Font style name.
</member>
</members>
</class>

View File

@ -174,7 +174,7 @@
<member name="caret_force_displayed" type="bool" setter="set_caret_force_displayed" getter="is_caret_force_displayed" default="false">
If [code]true[/code], the [LineEdit] will always show the caret, even if focus is lost.
</member>
<member name="caret_mid_grapheme" type="bool" setter="set_caret_mid_grapheme_enabled" getter="is_caret_mid_grapheme_enabled" default="false">
<member name="caret_mid_grapheme" type="bool" setter="set_caret_mid_grapheme_enabled" getter="is_caret_mid_grapheme_enabled" default="true">
Allow moving caret, selecting and removing the individual composite character components.
[b]Note:[/b] [kbd]Backspace[/kbd] is always removing individual composite character components.
</member>

View File

@ -942,7 +942,7 @@
<member name="caret_blink_speed" type="float" setter="set_caret_blink_speed" getter="get_caret_blink_speed" default="0.65">
Duration (in seconds) of a caret's blinking cycle.
</member>
<member name="caret_mid_grapheme" type="bool" setter="set_caret_mid_grapheme_enabled" getter="is_caret_mid_grapheme_enabled" default="false">
<member name="caret_mid_grapheme" type="bool" setter="set_caret_mid_grapheme_enabled" getter="is_caret_mid_grapheme_enabled" default="true">
Allow moving caret, selecting and removing the individual composite character components.
[b]Note:[/b] [kbd]Backspace[/kbd] is always removing individual composite character components.
</member>

View File

@ -261,6 +261,13 @@
Returns font family name.
</description>
</method>
<method name="font_get_opentype_feature_overrides" qualifiers="const">
<return type="Dictionary" />
<argument index="0" name="font_rid" type="RID" />
<description>
Returns font OpenType feature set override.
</description>
</method>
<method name="font_get_oversampling" qualifiers="const">
<return type="float" />
<argument index="0" name="font_rid" type="RID" />
@ -663,6 +670,14 @@
Sets the font family name.
</description>
</method>
<method name="font_set_opentype_feature_overrides">
<return type="void" />
<argument index="0" name="font_rid" type="RID" />
<argument index="1" name="overrides" type="Dictionary" />
<description>
Sets font OpenType feature set override.
</description>
</method>
<method name="font_set_oversampling">
<return type="void" />
<argument index="0" name="font_rid" type="RID" />

View File

@ -261,6 +261,13 @@
Returns font family name.
</description>
</method>
<method name="_font_get_opentype_feature_overrides" qualifiers="virtual const">
<return type="Dictionary" />
<argument index="0" name="font_rid" type="RID" />
<description>
Returns font OpenType feature set override.
</description>
</method>
<method name="_font_get_oversampling" qualifiers="virtual const">
<return type="float" />
<argument index="0" name="font_rid" type="RID" />
@ -550,6 +557,7 @@
<argument index="0" name="font_rid" type="RID" />
<argument index="1" name="force_autohinter" type="bool" />
<description>
If set to [code]true[/code] auto-hinting is preffered over font built-in hinting.
</description>
</method>
<method name="_font_set_global_oversampling" qualifiers="virtual">
@ -670,6 +678,14 @@
Sets the font family name.
</description>
</method>
<method name="_font_set_opentype_feature_overrides" qualifiers="virtual">
<return type="void" />
<argument index="0" name="font_rid" type="RID" />
<argument index="1" name="overrides" type="Dictionary" />
<description>
Sets font OpenType feature set override.
</description>
</method>
<method name="_font_set_oversampling" qualifiers="virtual">
<return type="void" />
<argument index="0" name="font_rid" type="RID" />

View File

@ -1185,6 +1185,38 @@ void DynamicFontImportSettings::_lang_remove(Object *p_item, int p_column, int p
memdelete(lang_item);
}
void DynamicFontImportSettings::_ot_add() {
menu_ot->set_position(ot_list->get_screen_transform().xform(ot_list->get_local_mouse_position()));
menu_ot->set_size(Vector2(1, 1));
menu_ot->popup();
}
void DynamicFontImportSettings::_ot_add_item(int p_option) {
String name = TS->tag_to_name(p_option);
for (TreeItem *ot_item = ot_list_root->get_first_child(); ot_item; ot_item = ot_item->get_next()) {
if (ot_item->get_text(0) == name) {
return;
}
}
TreeItem *ot_item = ot_list->create_item(ot_list_root);
ERR_FAIL_NULL(ot_item);
ot_item->set_text(0, name);
ot_item->set_editable(0, false);
ot_item->set_text(1, "1");
ot_item->set_editable(1, true);
ot_item->add_button(2, ot_list->get_theme_icon("Remove", "EditorIcons"), BUTTON_REMOVE_VAR, false, TTR("Remove"));
ot_item->set_button_color(2, 0, Color(1, 1, 1, 0.75));
}
void DynamicFontImportSettings::_ot_remove(Object *p_item, int p_column, int p_id) {
TreeItem *ot_item = (TreeItem *)p_item;
ERR_FAIL_NULL(ot_item);
ot_list_root->remove_child(ot_item);
memdelete(ot_item);
}
void DynamicFontImportSettings::_script_add() {
menu_scripts->set_position(script_list->get_screen_position() + script_list->get_local_mouse_position());
menu_scripts->reset_size();
@ -1230,6 +1262,7 @@ void DynamicFontImportSettings::_notification(int p_what) {
add_lang->set_icon(add_var->get_theme_icon("Add", "EditorIcons"));
add_script->set_icon(add_var->get_theme_icon("Add", "EditorIcons"));
add_var->set_icon(add_var->get_theme_icon("Add", "EditorIcons"));
add_ot->set_icon(add_var->get_theme_icon("Add", "EditorIcons"));
}
}
@ -1317,6 +1350,14 @@ void DynamicFontImportSettings::_re_import() {
main_settings["preload/glyph_ranges"] = ranges;
}
Dictionary ot_ov;
for (TreeItem *ot_item = ot_list_root->get_first_child(); ot_item; ot_item = ot_item->get_next()) {
String tag = ot_item->get_text(0);
int32_t value = ot_item->get_text(1).to_int();
ot_ov[tag] = value;
}
main_settings["opentype_feature_overrides"] = ot_ov;
if (OS::get_singleton()->is_stdout_verbose()) {
print_line("Import settings:");
for (Map<StringName, Variant>::Element *E = main_settings.front(); E; E = E->next()) {
@ -1373,6 +1414,7 @@ void DynamicFontImportSettings::open_settings(const String &p_path) {
vars_list->clear();
lang_list->clear();
script_list->clear();
ot_list->clear();
selected_chars.clear();
selected_glyphs.clear();
@ -1381,6 +1423,7 @@ void DynamicFontImportSettings::open_settings(const String &p_path) {
vars_list_root = vars_list->create_item();
lang_list_root = lang_list->create_item();
script_list_root = script_list->create_item();
ot_list_root = ot_list->create_item();
options_variations.clear();
Dictionary var_list = dfont_main->get_supported_variation_list();
@ -1546,6 +1589,23 @@ void DynamicFontImportSettings::open_settings(const String &p_path) {
script_item->set_editable(1, true);
script_item->add_button(2, lang_list->get_theme_icon("Remove", "EditorIcons"), BUTTON_REMOVE_VAR, false, TTR("Remove"));
}
} else if (key == "opentype_feature_overrides") {
Dictionary features = config->get_value("params", key);
for (const Variant *ftr = features.next(nullptr); ftr != nullptr; ftr = features.next(ftr)) {
TreeItem *ot_item = ot_list->create_item(ot_list_root);
ERR_FAIL_NULL(ot_item);
int32_t value = features[*ftr];
if (ftr->get_type() == Variant::STRING) {
ot_item->set_text(0, *ftr);
} else {
ot_item->set_text(0, TS->tag_to_name(*ftr));
}
ot_item->set_editable(0, false);
ot_item->set_text(1, itos(value));
ot_item->set_editable(1, true);
ot_item->add_button(2, ot_list->get_theme_icon("Remove", "EditorIcons"), BUTTON_REMOVE_VAR, false, TTR("Remove"));
ot_item->set_button_color(2, 0, Color(1, 1, 1, 0.75));
}
} else {
Variant value = config->get_value("params", key);
import_settings_data->defaults[key] = value;
@ -1570,6 +1630,39 @@ void DynamicFontImportSettings::open_settings(const String &p_path) {
font_preview_label->add_theme_font_override("font", font_preview);
font_preview_label->update();
menu_ot->clear();
menu_ot_ss->clear();
menu_ot_cv->clear();
menu_ot_cu->clear();
bool have_ss = false;
bool have_cv = false;
bool have_cu = false;
Dictionary features = font_preview->get_feature_list();
for (const Variant *ftr = features.next(nullptr); ftr != nullptr; ftr = features.next(ftr)) {
String ftr_name = TS->tag_to_name(*ftr);
if (ftr_name.begins_with("stylistic_set_")) {
menu_ot_ss->add_item(ftr_name.capitalize(), (int32_t)*ftr);
have_ss = true;
} else if (ftr_name.begins_with("character_variant_")) {
menu_ot_cv->add_item(ftr_name.capitalize(), (int32_t)*ftr);
have_cv = true;
} else if (ftr_name.begins_with("custom_")) {
menu_ot_cu->add_item(ftr_name.replace("custom_", ""), (int32_t)*ftr);
have_cu = true;
} else {
menu_ot->add_item(ftr_name.capitalize(), (int32_t)*ftr);
}
}
if (have_ss) {
menu_ot->add_submenu_item(RTR("Stylistic Sets"), "SSMenu");
}
if (have_cv) {
menu_ot->add_submenu_item(RTR("Character Variants"), "CVMenu");
}
if (have_cu) {
menu_ot->add_submenu_item(RTR("Custom"), "CUMenu");
}
_variations_validate();
popup_centered_ratio();
@ -1619,6 +1712,25 @@ DynamicFontImportSettings::DynamicFontImportSettings() {
add_child(menu_scripts);
menu_scripts->connect("id_pressed", callable_mp(this, &DynamicFontImportSettings::_script_add_item));
menu_ot = memnew(PopupMenu);
add_child(menu_ot);
menu_ot->connect("id_pressed", callable_mp(this, &DynamicFontImportSettings::_ot_add_item));
menu_ot_cv = memnew(PopupMenu);
menu_ot_cv->set_name("CVMenu");
menu_ot->add_child(menu_ot_cv);
menu_ot_cv->connect("id_pressed", callable_mp(this, &DynamicFontImportSettings::_ot_add_item));
menu_ot_ss = memnew(PopupMenu);
menu_ot_ss->set_name("SSMenu");
menu_ot->add_child(menu_ot_ss);
menu_ot_ss->connect("id_pressed", callable_mp(this, &DynamicFontImportSettings::_ot_add_item));
menu_ot_cu = memnew(PopupMenu);
menu_ot_cu->set_name("CUMenu");
menu_ot->add_child(menu_ot_cu);
menu_ot_cu->connect("id_pressed", callable_mp(this, &DynamicFontImportSettings::_ot_add_item));
Color warn_color = (EditorNode::get_singleton()) ? EditorNode::get_singleton()->get_gui_base()->get_theme_color("warning_color", "Editor") : Color(1, 1, 0);
// Root layout
@ -1897,6 +2009,34 @@ DynamicFontImportSettings::DynamicFontImportSettings() {
script_list->connect("button_pressed", callable_mp(this, &DynamicFontImportSettings::_script_remove));
script_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
HBoxContainer *hb_ot = memnew(HBoxContainer);
page5_vb->add_child(hb_ot);
label_ot = memnew(Label);
label_ot->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
label_ot->set_h_size_flags(Control::SIZE_EXPAND_FILL);
label_ot->set_text(TTR("OpenType feature overrides"));
hb_ot->add_child(label_ot);
add_ot = memnew(Button);
hb_ot->add_child(add_ot);
add_ot->set_tooltip(TTR("Add feature override"));
add_ot->set_icon(add_var->get_theme_icon("Add", "EditorIcons"));
add_ot->connect("pressed", callable_mp(this, &DynamicFontImportSettings::_ot_add));
ot_list = memnew(Tree);
page5_vb->add_child(ot_list);
ot_list->set_hide_root(true);
ot_list->set_columns(3);
ot_list->set_column_expand(0, true);
ot_list->set_column_custom_minimum_width(0, 80 * EDSCALE);
ot_list->set_column_expand(1, true);
ot_list->set_column_custom_minimum_width(1, 80 * EDSCALE);
ot_list->set_column_expand(2, false);
ot_list->set_column_custom_minimum_width(2, 50 * EDSCALE);
ot_list->connect("button_pressed", callable_mp(this, &DynamicFontImportSettings::_ot_remove));
ot_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
// Common
import_settings_data.instantiate();

View File

@ -120,18 +120,27 @@ class DynamicFontImportSettings : public ConfirmationDialog {
Label *page5_description = nullptr;
Button *add_lang = nullptr;
Button *add_script = nullptr;
Button *add_ot = nullptr;
PopupMenu *menu_langs = nullptr;
PopupMenu *menu_scripts = nullptr;
PopupMenu *menu_ot = nullptr;
PopupMenu *menu_ot_ss = nullptr;
PopupMenu *menu_ot_cv = nullptr;
PopupMenu *menu_ot_cu = nullptr;
Tree *lang_list = nullptr;
TreeItem *lang_list_root = nullptr;
Label *label_langs = nullptr;
Tree *script_list = nullptr;
TreeItem *script_list_root = nullptr;
Label *label_langs = nullptr;
Label *label_script = nullptr;
Tree *ot_list = nullptr;
TreeItem *ot_list_root = nullptr;
Label *label_ot = nullptr;
void _lang_add();
void _lang_add_item(int p_option);
void _lang_remove(Object *p_item, int p_column, int p_id);
@ -140,6 +149,10 @@ class DynamicFontImportSettings : public ConfirmationDialog {
void _script_add_item(int p_option);
void _script_remove(Object *p_item, int p_column, int p_id);
void _ot_add();
void _ot_add_item(int p_option);
void _ot_remove(Object *p_item, int p_column, int p_id);
// Common
void _add_glyph_range_item(int32_t p_start, int32_t p_end, const String &p_name);

View File

@ -107,6 +107,7 @@ void ResourceImporterDynamicFont::get_import_options(const String &p_path, List<
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "oversampling", PROPERTY_HINT_RANGE, "0,10,0.1"), 0.0));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "compress"), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::DICTIONARY, "opentype_feature_overrides"), Dictionary()));
r_options->push_back(ImportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "preload/char_ranges"), Vector<String>()));
r_options->push_back(ImportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "preload/glyph_ranges"), Vector<String>()));
@ -174,6 +175,7 @@ Error ResourceImporterDynamicFont::import(const String &p_source_file, const Str
bool msdf = p_options["multichannel_signed_distance_field"];
int px_range = p_options["msdf_pixel_range"];
int px_size = p_options["msdf_size"];
Dictionary ot_ov = p_options["opentype_feature_overrides"];
bool autohinter = p_options["force_autohinter"];
int hinting = p_options["hinting"];
@ -190,6 +192,7 @@ Error ResourceImporterDynamicFont::import(const String &p_source_file, const Str
font->set_multichannel_signed_distance_field(msdf);
font->set_msdf_pixel_range(px_range);
font->set_msdf_size(px_size);
font->set_opentype_feature_overrides(ot_ov);
font->set_fixed_size(0);
font->set_force_autohinter(autohinter);
font->set_hinting((TextServer::Hinting)hinting);

View File

@ -2841,6 +2841,24 @@ Vector<String> TextServerAdvanced::font_get_script_support_overrides(RID p_font_
return out;
}
void TextServerAdvanced::font_set_opentype_feature_overrides(RID p_font_rid, const Dictionary &p_overrides) {
FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
Vector2i size = _get_size(fd, 16);
ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
fd->feature_overrides = p_overrides;
}
Dictionary TextServerAdvanced::font_get_opentype_feature_overrides(RID p_font_rid) const {
FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Dictionary());
MutexLock lock(fd->mutex);
return fd->feature_overrides;
}
Dictionary TextServerAdvanced::font_supported_feature_list(RID p_font_rid) const {
FontDataAdvanced *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Dictionary());
@ -4231,6 +4249,24 @@ Glyph TextServerAdvanced::_shape_single_glyph(ShapedTextDataAdvanced *p_sd, char
return gl;
}
_FORCE_INLINE_ void TextServerAdvanced::_add_featuers(const Dictionary &p_source, Vector<hb_feature_t> &r_ftrs) {
for (const Variant *ftr = p_source.next(nullptr); ftr != nullptr; ftr = p_source.next(ftr)) {
int32_t values = p_source[*ftr];
if (values >= 0) {
hb_feature_t feature;
if (ftr->get_type() == Variant::STRING) {
feature.tag = name_to_tag(*ftr);
} else {
feature.tag = *ftr;
}
feature.value = values;
feature.start = 0;
feature.end = -1;
r_ftrs.push_back(feature);
}
}
}
void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int32_t p_start, int32_t p_end, hb_script_t p_script, hb_direction_t p_direction, Vector<RID> p_fonts, int p_span, int p_fb_index) {
int fs = p_sd->spans[p_span].font_size;
if (p_fb_index >= p_fonts.size()) {
@ -4287,17 +4323,9 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int32_t p_star
hb_buffer_add_utf32(p_sd->hb_buffer, (const uint32_t *)p_sd->text.ptr(), p_sd->text.length(), p_start, p_end - p_start);
Vector<hb_feature_t> ftrs;
for (const Variant *ftr = p_sd->spans[p_span].features.next(nullptr); ftr != nullptr; ftr = p_sd->spans[p_span].features.next(ftr)) {
double values = p_sd->spans[p_span].features[*ftr];
if (values >= 0) {
hb_feature_t feature;
feature.tag = *ftr;
feature.value = values;
feature.start = 0;
feature.end = -1;
ftrs.push_back(feature);
}
}
_add_featuers(font_get_opentype_feature_overrides(f), ftrs);
_add_featuers(p_sd->spans[p_span].features, ftrs);
hb_shape(hb_font, p_sd->hb_buffer, ftrs.is_empty() ? nullptr : &ftrs[0], ftrs.size());
unsigned int glyph_count = 0;

View File

@ -187,6 +187,7 @@ class TextServerAdvanced : public TextServer {
Set<uint32_t> supported_scripts;
Dictionary supported_features;
Dictionary supported_varaitions;
Dictionary feature_overrides;
// Language/script support override.
Map<String, bool> language_support_overrides;
@ -272,6 +273,7 @@ class TextServerAdvanced : public TextServer {
bool _shape_substr(ShapedTextDataAdvanced *p_new_sd, const ShapedTextDataAdvanced *p_sd, int p_start, int p_length) const;
void _shape_run(ShapedTextDataAdvanced *p_sd, int32_t p_start, int32_t p_end, hb_script_t p_script, hb_direction_t p_direction, Vector<RID> p_fonts, int p_span, int p_fb_index);
Glyph _shape_single_glyph(ShapedTextDataAdvanced *p_sd, char32_t p_char, hb_script_t p_script, hb_direction_t p_direction, RID p_font, int p_font_size);
_FORCE_INLINE_ void _add_featuers(const Dictionary &p_source, Vector<hb_feature_t> &r_ftrs);
// HarfBuzz bitmap font interface.
@ -447,6 +449,9 @@ public:
virtual void font_remove_script_support_override(RID p_font_rid, const String &p_script) override;
virtual Vector<String> font_get_script_support_overrides(RID p_font_rid) override;
virtual void font_set_opentype_feature_overrides(RID p_font_rid, const Dictionary &p_overrides) override;
virtual Dictionary font_get_opentype_feature_overrides(RID p_font_rid) const override;
virtual Dictionary font_supported_feature_list(RID p_font_rid) const override;
virtual Dictionary font_supported_variation_list(RID p_font_rid) const override;

View File

@ -1995,6 +1995,24 @@ Vector<String> TextServerFallback::font_get_script_support_overrides(RID p_font_
return out;
}
void TextServerFallback::font_set_opentype_feature_overrides(RID p_font_rid, const Dictionary &p_overrides) {
FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND(!fd);
MutexLock lock(fd->mutex);
Vector2i size = _get_size(fd, 16);
ERR_FAIL_COND(!_ensure_cache_for_size(fd, size));
fd->feature_overrides = p_overrides;
}
Dictionary TextServerFallback::font_get_opentype_feature_overrides(RID p_font_rid) const {
FontDataFallback *fd = font_owner.get_or_null(p_font_rid);
ERR_FAIL_COND_V(!fd, Dictionary());
MutexLock lock(fd->mutex);
return fd->feature_overrides;
}
Dictionary TextServerFallback::font_supported_feature_list(RID p_font_rid) const {
return Dictionary();
}

View File

@ -150,6 +150,7 @@ class TextServerFallback : public TextServer {
bool face_init = false;
Dictionary supported_varaitions;
Dictionary feature_overrides;
// Language/script support override.
Map<String, bool> language_support_overrides;
@ -357,6 +358,9 @@ public:
virtual void font_remove_script_support_override(RID p_font_rid, const String &p_script) override;
virtual Vector<String> font_get_script_support_overrides(RID p_font_rid) override;
virtual void font_set_opentype_feature_overrides(RID p_font_rid, const Dictionary &p_overrides) override;
virtual Dictionary font_get_opentype_feature_overrides(RID p_font_rid) const override;
virtual Dictionary font_supported_feature_list(RID p_font_rid) const override;
virtual Dictionary font_supported_variation_list(RID p_font_rid) const override;

View File

@ -497,7 +497,7 @@ bool Button::_set(const StringName &p_name, const Variant &p_value) {
if (str.begins_with("opentype_features/")) {
String name = str.get_slicec('/', 1);
int32_t tag = TS->name_to_tag(name);
double value = p_value;
int value = p_value;
if (value == -1) {
if (opentype_features.has(tag)) {
opentype_features.erase(tag);
@ -505,7 +505,7 @@ bool Button::_set(const StringName &p_name, const Variant &p_value) {
update();
}
} else {
if ((double)opentype_features[tag] != value) {
if (!opentype_features.has(tag) || (int)opentype_features[tag] != value) {
opentype_features[tag] = value;
_shape();
update();
@ -537,7 +537,7 @@ bool Button::_get(const StringName &p_name, Variant &r_ret) const {
void Button::_get_property_list(List<PropertyInfo> *p_list) const {
for (const Variant *ftr = opentype_features.next(nullptr); ftr != nullptr; ftr = opentype_features.next(ftr)) {
String name = TS->tag_to_name(*ftr);
p_list->push_back(PropertyInfo(Variant::FLOAT, "opentype_features/" + name));
p_list->push_back(PropertyInfo(Variant::INT, "opentype_features/" + name));
}
p_list->push_back(PropertyInfo(Variant::NIL, "opentype_features/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
}

View File

@ -46,7 +46,7 @@ bool GraphNode::_set(const StringName &p_name, const Variant &p_value) {
if (str.begins_with("opentype_features/")) {
String name = str.get_slicec('/', 1);
int32_t tag = TS->name_to_tag(name);
double value = p_value;
int value = p_value;
if (value == -1) {
if (opentype_features.has(tag)) {
opentype_features.erase(tag);
@ -54,7 +54,7 @@ bool GraphNode::_set(const StringName &p_name, const Variant &p_value) {
update();
}
} else {
if ((double)opentype_features[tag] != value) {
if (!opentype_features.has(tag) || (int)opentype_features[tag] != value) {
opentype_features[tag] = value;
_shape();
update();
@ -153,7 +153,7 @@ bool GraphNode::_get(const StringName &p_name, Variant &r_ret) const {
void GraphNode::_get_property_list(List<PropertyInfo> *p_list) const {
for (const Variant *ftr = opentype_features.next(nullptr); ftr != nullptr; ftr = opentype_features.next(ftr)) {
String name = TS->tag_to_name(*ftr);
p_list->push_back(PropertyInfo(Variant::FLOAT, "opentype_features/" + name));
p_list->push_back(PropertyInfo(Variant::INT, "opentype_features/" + name));
}
p_list->push_back(PropertyInfo(Variant::NIL, "opentype_features/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));

View File

@ -811,7 +811,7 @@ bool Label::_set(const StringName &p_name, const Variant &p_value) {
if (str.begins_with("opentype_features/")) {
String name = str.get_slicec('/', 1);
int32_t tag = TS->name_to_tag(name);
double value = p_value;
int value = p_value;
if (value == -1) {
if (opentype_features.has(tag)) {
opentype_features.erase(tag);
@ -819,7 +819,7 @@ bool Label::_set(const StringName &p_name, const Variant &p_value) {
update();
}
} else {
if ((double)opentype_features[tag] != value) {
if (!opentype_features.has(tag) || (int)opentype_features[tag] != value) {
opentype_features[tag] = value;
dirty = true;
update();
@ -851,7 +851,7 @@ bool Label::_get(const StringName &p_name, Variant &r_ret) const {
void Label::_get_property_list(List<PropertyInfo> *p_list) const {
for (const Variant *ftr = opentype_features.next(nullptr); ftr != nullptr; ftr = opentype_features.next(ftr)) {
String name = TS->tag_to_name(*ftr);
p_list->push_back(PropertyInfo(Variant::FLOAT, "opentype_features/" + name));
p_list->push_back(PropertyInfo(Variant::INT, "opentype_features/" + name));
}
p_list->push_back(PropertyInfo(Variant::NIL, "opentype_features/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
}

View File

@ -2164,7 +2164,7 @@ bool LineEdit::_set(const StringName &p_name, const Variant &p_value) {
if (str.begins_with("opentype_features/")) {
String name = str.get_slicec('/', 1);
int32_t tag = TS->name_to_tag(name);
double value = p_value;
int value = p_value;
if (value == -1) {
if (opentype_features.has(tag)) {
opentype_features.erase(tag);
@ -2172,7 +2172,7 @@ bool LineEdit::_set(const StringName &p_name, const Variant &p_value) {
update();
}
} else {
if ((double)opentype_features[tag] != value) {
if (!opentype_features.has(tag) || (int)opentype_features[tag] != value) {
opentype_features[tag] = value;
_shape();
update();
@ -2204,7 +2204,7 @@ bool LineEdit::_get(const StringName &p_name, Variant &r_ret) const {
void LineEdit::_get_property_list(List<PropertyInfo> *p_list) const {
for (const Variant *ftr = opentype_features.next(nullptr); ftr != nullptr; ftr = opentype_features.next(ftr)) {
String name = TS->tag_to_name(*ftr);
p_list->push_back(PropertyInfo(Variant::FLOAT, "opentype_features/" + name));
p_list->push_back(PropertyInfo(Variant::INT, "opentype_features/" + name));
}
p_list->push_back(PropertyInfo(Variant::NIL, "opentype_features/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
}

View File

@ -97,7 +97,7 @@ private:
PopupMenu *menu_dir = nullptr;
PopupMenu *menu_ctl = nullptr;
bool caret_mid_grapheme_enabled = false;
bool caret_mid_grapheme_enabled = true;
int caret_column = 0;
int scroll_offset = 0;

View File

@ -240,7 +240,7 @@ bool LinkButton::_set(const StringName &p_name, const Variant &p_value) {
if (str.begins_with("opentype_features/")) {
String name = str.get_slicec('/', 1);
int32_t tag = TS->name_to_tag(name);
double value = p_value;
int value = p_value;
if (value == -1) {
if (opentype_features.has(tag)) {
opentype_features.erase(tag);
@ -248,7 +248,7 @@ bool LinkButton::_set(const StringName &p_name, const Variant &p_value) {
update();
}
} else {
if ((double)opentype_features[tag] != value) {
if (!opentype_features.has(tag) || (int)opentype_features[tag] != value) {
opentype_features[tag] = value;
_shape();
update();
@ -280,7 +280,7 @@ bool LinkButton::_get(const StringName &p_name, Variant &r_ret) const {
void LinkButton::_get_property_list(List<PropertyInfo> *p_list) const {
for (const Variant *ftr = opentype_features.next(nullptr); ftr != nullptr; ftr = opentype_features.next(ftr)) {
String name = TS->tag_to_name(*ftr);
p_list->push_back(PropertyInfo(Variant::FLOAT, "opentype_features/" + name));
p_list->push_back(PropertyInfo(Variant::INT, "opentype_features/" + name));
}
p_list->push_back(PropertyInfo(Variant::NIL, "opentype_features/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
}

View File

@ -5228,7 +5228,7 @@ bool TextEdit::_set(const StringName &p_name, const Variant &p_value) {
if (str.begins_with("opentype_features/")) {
String name = str.get_slicec('/', 1);
int32_t tag = TS->name_to_tag(name);
double value = p_value;
int value = p_value;
if (value == -1) {
if (opentype_features.has(tag)) {
opentype_features.erase(tag);
@ -5237,7 +5237,7 @@ bool TextEdit::_set(const StringName &p_name, const Variant &p_value) {
update();
}
} else {
if ((double)opentype_features[tag] != value) {
if (!opentype_features.has(tag) || (int)opentype_features[tag] != value) {
opentype_features[tag] = value;
text.set_font_features(opentype_features);
text.invalidate_all();
@ -5270,7 +5270,7 @@ bool TextEdit::_get(const StringName &p_name, Variant &r_ret) const {
void TextEdit::_get_property_list(List<PropertyInfo> *p_list) const {
for (const Variant *ftr = opentype_features.next(nullptr); ftr != nullptr; ftr = opentype_features.next(ftr)) {
String name = TS->tag_to_name(*ftr);
p_list->push_back(PropertyInfo(Variant::FLOAT, "opentype_features/" + name));
p_list->push_back(PropertyInfo(Variant::INT, "opentype_features/" + name));
}
p_list->push_back(PropertyInfo(Variant::NIL, "opentype_features/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
}

View File

@ -374,7 +374,7 @@ private:
bool move_caret_on_right_click = true;
bool caret_mid_grapheme_enabled = false;
bool caret_mid_grapheme_enabled = true;
bool drag_action = false;
bool drag_caret_force_displayed = false;

View File

@ -184,6 +184,9 @@ void FontData::_bind_methods() {
ClassDB::bind_method(D_METHOD("remove_script_support_override", "script"), &FontData::remove_script_support_override);
ClassDB::bind_method(D_METHOD("get_script_support_overrides"), &FontData::get_script_support_overrides);
ClassDB::bind_method(D_METHOD("set_opentype_feature_overrides", "overrides"), &FontData::set_opentype_feature_overrides);
ClassDB::bind_method(D_METHOD("get_opentype_feature_overrides"), &FontData::get_opentype_feature_overrides);
ClassDB::bind_method(D_METHOD("has_char", "char"), &FontData::has_char);
ClassDB::bind_method(D_METHOD("get_supported_chars"), &FontData::get_supported_chars);
@ -191,49 +194,25 @@ void FontData::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_supported_feature_list"), &FontData::get_supported_feature_list);
ClassDB::bind_method(D_METHOD("get_supported_variation_list"), &FontData::get_supported_variation_list);
ADD_PROPERTY(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_data", "get_data");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "antialiased", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_antialiased", "is_antialiased");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "font_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_font_name", "get_font_name");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "style_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_font_style_name", "get_font_style_name");
ADD_PROPERTY(PropertyInfo(Variant::INT, "font_style", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_font_style", "get_font_style");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "multichannel_signed_distance_field", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_multichannel_signed_distance_field", "is_multichannel_signed_distance_field");
ADD_PROPERTY(PropertyInfo(Variant::INT, "msdf_pixel_range", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_msdf_pixel_range", "get_msdf_pixel_range");
ADD_PROPERTY(PropertyInfo(Variant::INT, "msdf_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_msdf_size", "get_msdf_size");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "force_autohinter", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_force_autohinter", "is_force_autohinter");
ADD_PROPERTY(PropertyInfo(Variant::INT, "hinting", PROPERTY_HINT_ENUM, "None,Light,Normal", PROPERTY_USAGE_STORAGE), "set_hinting", "get_hinting");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "oversampling", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_oversampling", "get_oversampling");
ADD_PROPERTY(PropertyInfo(Variant::INT, "fixed_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_fixed_size", "get_fixed_size");
ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "opentype_feature_overrides", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE), "set_opentype_feature_overrides", "get_opentype_feature_overrides");
}
bool FontData::_set(const StringName &p_name, const Variant &p_value) {
Vector<String> tokens = p_name.operator String().split("/");
if (tokens.size() == 1) {
if (tokens[0] == "data") {
set_data(p_value);
return true;
} else if (tokens[0] == "antialiased") {
set_antialiased(p_value);
return true;
} else if (tokens[0] == "font_name") {
set_font_name(p_value);
return true;
} else if (tokens[0] == "style_name") {
set_font_style_name(p_value);
return true;
} else if (tokens[0] == "font_style") {
set_font_style(p_value);
return true;
} else if (tokens[0] == "multichannel_signed_distance_field") {
set_multichannel_signed_distance_field(p_value);
return true;
} else if (tokens[0] == "msdf_pixel_range") {
set_msdf_pixel_range(p_value);
return true;
} else if (tokens[0] == "msdf_size") {
set_msdf_size(p_value);
return true;
} else if (tokens[0] == "fixed_size") {
set_fixed_size(p_value);
return true;
} else if (tokens[0] == "hinting") {
set_hinting((TextServer::Hinting)p_value.operator int());
return true;
} else if (tokens[0] == "force_autohinter") {
set_force_autohinter(p_value);
return true;
} else if (tokens[0] == "oversampling") {
set_oversampling(p_value);
return true;
}
} else if (tokens.size() == 2 && tokens[0] == "language_support_override") {
if (tokens.size() == 2 && tokens[0] == "language_support_override") {
String lang = tokens[1];
set_language_support_override(lang, p_value);
return true;
@ -309,45 +288,7 @@ bool FontData::_set(const StringName &p_name, const Variant &p_value) {
bool FontData::_get(const StringName &p_name, Variant &r_ret) const {
Vector<String> tokens = p_name.operator String().split("/");
if (tokens.size() == 1) {
if (tokens[0] == "data") {
r_ret = get_data();
return true;
} else if (tokens[0] == "antialiased") {
r_ret = is_antialiased();
return true;
} else if (tokens[0] == "font_name") {
r_ret = get_font_name();
return true;
} else if (tokens[0] == "style_name") {
r_ret = get_font_style_name();
return true;
} else if (tokens[0] == "font_style") {
r_ret = get_font_style();
return true;
} else if (tokens[0] == "multichannel_signed_distance_field") {
r_ret = is_multichannel_signed_distance_field();
return true;
} else if (tokens[0] == "msdf_pixel_range") {
r_ret = get_msdf_pixel_range();
return true;
} else if (tokens[0] == "msdf_size") {
r_ret = get_msdf_size();
return true;
} else if (tokens[0] == "fixed_size") {
r_ret = get_fixed_size();
return true;
} else if (tokens[0] == "hinting") {
r_ret = get_hinting();
return true;
} else if (tokens[0] == "force_autohinter") {
r_ret = is_force_autohinter();
return true;
} else if (tokens[0] == "oversampling") {
r_ret = get_oversampling();
return true;
}
} else if (tokens.size() == 2 && tokens[0] == "language_support_override") {
if (tokens.size() == 2 && tokens[0] == "language_support_override") {
String lang = tokens[1];
r_ret = get_language_support_override(lang);
return true;
@ -422,20 +363,6 @@ bool FontData::_get(const StringName &p_name, Variant &r_ret) const {
}
void FontData::_get_property_list(List<PropertyInfo> *p_list) const {
p_list->push_back(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
p_list->push_back(PropertyInfo(Variant::STRING, "font_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
p_list->push_back(PropertyInfo(Variant::STRING, "style_name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
p_list->push_back(PropertyInfo(Variant::INT, "font_style", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
p_list->push_back(PropertyInfo(Variant::BOOL, "antialiased", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
p_list->push_back(PropertyInfo(Variant::BOOL, "multichannel_signed_distance_field", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
p_list->push_back(PropertyInfo(Variant::INT, "msdf_pixel_range", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
p_list->push_back(PropertyInfo(Variant::INT, "msdf_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
p_list->push_back(PropertyInfo(Variant::INT, "fixed_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
p_list->push_back(PropertyInfo(Variant::INT, "hinting", PROPERTY_HINT_ENUM, "None,Light,Normal", PROPERTY_USAGE_STORAGE));
p_list->push_back(PropertyInfo(Variant::BOOL, "force_autohinter", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
p_list->push_back(PropertyInfo(Variant::FLOAT, "oversampling", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
Vector<String> lang_over = get_language_support_overrides();
for (int i = 0; i < lang_over.size(); i++) {
p_list->push_back(PropertyInfo(Variant::BOOL, "language_support_override/" + lang_over[i], PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE));
@ -1077,6 +1004,16 @@ Vector<String> FontData::get_script_support_overrides() const {
return TS->font_get_script_support_overrides(cache[0]);
}
void FontData::set_opentype_feature_overrides(const Dictionary &p_overrides) {
_ensure_rid(0);
TS->font_set_opentype_feature_overrides(cache[0], p_overrides);
}
Dictionary FontData::get_opentype_feature_overrides() const {
_ensure_rid(0);
return TS->font_get_opentype_feature_overrides(cache[0]);
}
bool FontData::has_char(char32_t p_char) const {
_ensure_rid(0);
return TS->font_has_char(cache[0], p_char);

View File

@ -198,6 +198,9 @@ public:
virtual void remove_script_support_override(const String &p_script);
virtual Vector<String> get_script_support_overrides() const;
virtual void set_opentype_feature_overrides(const Dictionary &p_overrides);
virtual Dictionary get_opentype_feature_overrides() const;
// Base font properties.
virtual bool has_char(char32_t p_char) const;
virtual String get_supported_chars() const;

View File

@ -174,6 +174,9 @@ void TextServerExtension::_bind_methods() {
GDVIRTUAL_BIND(_font_remove_script_support_override, "font_rid", "script");
GDVIRTUAL_BIND(_font_get_script_support_overrides, "font_rid");
GDVIRTUAL_BIND(_font_set_opentype_feature_overrides, "font_rid", "overrides");
GDVIRTUAL_BIND(_font_get_opentype_feature_overrides, "font_rid");
GDVIRTUAL_BIND(_font_supported_feature_list, "font_rid");
GDVIRTUAL_BIND(_font_supported_variation_list, "font_rid");
@ -869,6 +872,18 @@ Vector<String> TextServerExtension::font_get_script_support_overrides(RID p_font
return Vector<String>();
}
void TextServerExtension::font_set_opentype_feature_overrides(RID p_font_rid, const Dictionary &p_overrides) {
GDVIRTUAL_CALL(_font_set_opentype_feature_overrides, p_font_rid, p_overrides);
}
Dictionary TextServerExtension::font_get_opentype_feature_overrides(RID p_font_rid) const {
Dictionary ret;
if (GDVIRTUAL_CALL(_font_get_opentype_feature_overrides, p_font_rid, ret)) {
return ret;
}
return Dictionary();
}
Dictionary TextServerExtension::font_supported_feature_list(RID p_font_rid) const {
Dictionary ret;
if (GDVIRTUAL_CALL(_font_supported_feature_list, p_font_rid, ret)) {

View File

@ -285,6 +285,11 @@ public:
GDVIRTUAL2(_font_remove_script_support_override, RID, const String &);
GDVIRTUAL1R(Vector<String>, _font_get_script_support_overrides, RID);
virtual void font_set_opentype_feature_overrides(RID p_font_rid, const Dictionary &p_overrides) override;
virtual Dictionary font_get_opentype_feature_overrides(RID p_font_rid) const override;
GDVIRTUAL2(_font_set_opentype_feature_overrides, RID, const Dictionary &);
GDVIRTUAL1RC(Dictionary, _font_get_opentype_feature_overrides, RID);
virtual Dictionary font_supported_feature_list(RID p_font_rid) const override;
virtual Dictionary font_supported_variation_list(RID p_font_rid) const override;
GDVIRTUAL1RC(Dictionary, _font_supported_feature_list, RID);

View File

@ -327,6 +327,9 @@ void TextServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("font_remove_script_support_override", "font_rid", "script"), &TextServer::font_remove_script_support_override);
ClassDB::bind_method(D_METHOD("font_get_script_support_overrides", "font_rid"), &TextServer::font_get_script_support_overrides);
ClassDB::bind_method(D_METHOD("font_set_opentype_feature_overrides", "font_rid", "overrides"), &TextServer::font_set_opentype_feature_overrides);
ClassDB::bind_method(D_METHOD("font_get_opentype_feature_overrides", "font_rid"), &TextServer::font_get_opentype_feature_overrides);
ClassDB::bind_method(D_METHOD("font_supported_feature_list", "font_rid"), &TextServer::font_supported_feature_list);
ClassDB::bind_method(D_METHOD("font_supported_variation_list", "font_rid"), &TextServer::font_supported_variation_list);
@ -989,9 +992,9 @@ Vector<Vector2> TextServer::shaped_text_get_selection(RID p_shaped, int p_start,
}
real_t char_adv = advance / (real_t)(glyphs[i].end - glyphs[i].start);
if ((glyphs[i].flags & GRAPHEME_IS_RTL) == GRAPHEME_IS_RTL) {
ranges.push_back(Vector2(off, off + char_adv * (start - glyphs[i].start)));
ranges.push_back(Vector2(off, off + char_adv * (glyphs[i].end - start)));
} else {
ranges.push_back(Vector2(off + char_adv * (glyphs[i].end - start), off + advance));
ranges.push_back(Vector2(off + char_adv * (start - glyphs[i].start), off + advance));
}
}
// Selection range is within grapheme.
@ -1099,6 +1102,31 @@ int TextServer::shaped_text_hit_test_position(RID p_shaped, float p_coords) cons
return glyphs[i].start;
}
}
// Ligature, handle mid-grapheme hit.
if (p_coords >= off && p_coords < off + advance && glyphs[i].end > glyphs[i].start + 1) {
int cnt = glyphs[i].end - glyphs[i].start;
real_t char_adv = advance / (real_t)(cnt);
real_t sub_off = off;
for (int j = 0; j < cnt; j++) {
// Place caret to the left of clicked sub-grapheme.
if (p_coords >= sub_off && p_coords < sub_off + char_adv / 2) {
if ((glyphs[i].flags & GRAPHEME_IS_RTL) == GRAPHEME_IS_RTL) {
return glyphs[i].end - j;
} else {
return glyphs[i].start + j;
}
}
// Place caret to the right of clicked sub-grapheme.
if (p_coords >= sub_off + char_adv / 2 && p_coords < sub_off + char_adv) {
if ((glyphs[i].flags & GRAPHEME_IS_RTL) == GRAPHEME_IS_RTL) {
return glyphs[i].start + (cnt - 1) - j;
} else {
return glyphs[i].end - (cnt - 1) + j;
}
}
sub_off += char_adv;
}
}
// Place caret to the left of clicked grapheme.
if (p_coords >= off && p_coords < off + advance / 2) {
if ((glyphs[i].flags & GRAPHEME_IS_RTL) == GRAPHEME_IS_RTL) {

View File

@ -350,6 +350,9 @@ public:
virtual void font_remove_script_support_override(RID p_font_rid, const String &p_script) = 0;
virtual Vector<String> font_get_script_support_overrides(RID p_font_rid) = 0;
virtual void font_set_opentype_feature_overrides(RID p_font_rid, const Dictionary &p_overrides) = 0;
virtual Dictionary font_get_opentype_feature_overrides(RID p_font_rid) const = 0;
virtual Dictionary font_supported_feature_list(RID p_font_rid) const = 0;
virtual Dictionary font_supported_variation_list(RID p_font_rid) const = 0;