From c89c515ccf941a62bf2ec501095fad57d72cb1f7 Mon Sep 17 00:00:00 2001
From: bruvzg <7645683+bruvzg@users.noreply.github.com>
Date: Thu, 18 Nov 2021 23:36:22 +0200
Subject: [PATCH] [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.
---
doc/classes/CanvasItem.xml | 2 +-
doc/classes/FontData.xml | 198 ++++--------------
doc/classes/LineEdit.xml | 2 +-
doc/classes/TextEdit.xml | 2 +-
doc/classes/TextServer.xml | 15 ++
doc/classes/TextServerExtension.xml | 16 ++
editor/import/dynamicfont_import_settings.cpp | 140 +++++++++++++
editor/import/dynamicfont_import_settings.h | 15 +-
.../import/resource_importer_dynamicfont.cpp | 3 +
modules/text_server_adv/text_server_adv.cpp | 50 ++++-
modules/text_server_adv/text_server_adv.h | 5 +
modules/text_server_fb/text_server_fb.cpp | 18 ++
modules/text_server_fb/text_server_fb.h | 4 +
scene/gui/button.cpp | 6 +-
scene/gui/graph_node.cpp | 6 +-
scene/gui/label.cpp | 6 +-
scene/gui/line_edit.cpp | 6 +-
scene/gui/line_edit.h | 2 +-
scene/gui/link_button.cpp | 6 +-
scene/gui/text_edit.cpp | 6 +-
scene/gui/text_edit.h | 2 +-
scene/resources/font.cpp | 121 +++--------
scene/resources/font.h | 3 +
servers/text/text_server_extension.cpp | 15 ++
servers/text/text_server_extension.h | 5 +
servers/text_server.cpp | 32 ++-
servers/text_server.h | 3 +
27 files changed, 403 insertions(+), 286 deletions(-)
diff --git a/doc/classes/CanvasItem.xml b/doc/classes/CanvasItem.xml
index a7b2434defd..44845947b1e 100644
--- a/doc/classes/CanvasItem.xml
+++ b/doc/classes/CanvasItem.xml
@@ -115,7 +115,7 @@
- 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.
diff --git a/doc/classes/FontData.xml b/doc/classes/FontData.xml
index 0c8386784e6..36976f70837 100644
--- a/doc/classes/FontData.xml
+++ b/doc/classes/FontData.xml
@@ -79,12 +79,6 @@
Returns text server font cache entry resource id.
-
-
-
- Returns contents of the dynamic font source file.
-
-
@@ -93,30 +87,6 @@
Returns font descent (number of pixels below the baseline).
-
-
-
- Returns font fixed size.
-
-
-
-
-
- Returns font family name.
-
-
-
-
-
- Returns font style flags, see [enum TextServer.FontStyle].
-
-
-
-
-
- Returns font style name.
-
-
@@ -180,12 +150,6 @@
Returns rectangle in the cache texture containing the glyph.
-
-
-
- Returns the font hinting mode. Used by dynamic fonts only.
-
-
@@ -216,24 +180,6 @@
Returns list of language support overrides.
-
-
-
- Returns the width of the range around the shape between the minimum and maximum representable signed distance.
-
-
-
-
-
- Returns source font size used to generate MSDF textures.
-
-
-
-
-
- Returns font oversampling factor, if set to [code]0.0[/code] global oversampling factor is used instead. Used by dynamic fonts only.
-
-
@@ -346,18 +292,6 @@
Returns [code]true[/code] if a Unicode [code]char[/code] is available in the font.
-
-
-
- Returns [code]true[/code] if font 8-bit anitialiased glyph rendering is supported and enabled.
-
-
-
-
-
- Returns [code]true[/code] if auto-hinting is supported and preferred over font built-in hinting. Used by dynamic fonts only.
-
-
@@ -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).
-
-
-
- Returns [code]true[/code] if glyphs of all sizes are rendered using single multichannel signed distance field generated from the dynamic font vector data.
-
-
@@ -455,13 +383,6 @@
Renders the range of characters to the font cache texture.
-
-
-
-
- If set to [code]true[/code], 8-bit antialiased glyph rendering is used, otherwise 1-bit rendering is used. Used by dynamic fonts only.
-
-
@@ -471,13 +392,6 @@
Sets the font ascent (number of pixels above the baseline).
-
-
-
-
- Sets font source data, e.g contents of the dynamic font source file.
-
-
@@ -487,41 +401,6 @@
Sets the font descent (number of pixels below the baseline).
-
-
-
-
- Sets the fixed size for the font.
-
-
-
-
-
-
- Sets the font family name.
-
-
-
-
-
-
- Sets the font style flags, see [enum TextServer.FontStyle].
-
-
-
-
-
-
- Sets the font style name.
-
-
-
-
-
-
- If set to [code]true[/code] auto-hinting is preferred over font built-in hinting.
-
-
@@ -573,13 +452,6 @@
Sets rectangle in the cache texture containing the glyph.
-
-
-
-
- Sets font hinting mode. Used by dynamic fonts only.
-
-
@@ -598,35 +470,6 @@
Adds override for [method is_language_supported].
-
-
-
-
- Sets the width of the range around the shape between the minimum and maximum representable signed distance.
-
-
-
-
-
-
- Sets source font size used to generate MSDF textures.
-
-
-
-
-
-
- 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.
-
-
-
-
-
-
- Sets font oversampling factor, if set to [code]0.0[/code] global oversampling factor is used instead. Used by dynamic fonts only.
-
-
@@ -701,4 +544,45 @@
+
+
+ If set to [code]true[/code], font 8-bit anitialiased glyph rendering is supported and enabled.
+
+
+ Contents of the dynamic font source file.
+
+
+ Font size, used only for the bitmap fonts.
+
+
+ Font family name.
+
+
+ Font style flags, see [enum TextServer.FontStyle].
+
+
+ If set to [code]true[/code], auto-hinting is supported and preffered over font built-in hinting. Used by dynamic fonts only.
+
+
+ Font hinting mode. Used by dynamic fonts only.
+
+
+ The width of the range around the shape between the minimum and maximum representable signed distance.
+
+
+ Source font size used to generate MSDF textures.
+
+
+ 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.
+
+
+ Font OpenType feature set override.
+
+
+ Font oversampling factor, if set to [code]0.0[/code] global oversampling factor is used instead. Used by dynamic fonts only.
+
+
+ Font style name.
+
+
diff --git a/doc/classes/LineEdit.xml b/doc/classes/LineEdit.xml
index c3e3088d69f..b376ab5cb05 100644
--- a/doc/classes/LineEdit.xml
+++ b/doc/classes/LineEdit.xml
@@ -174,7 +174,7 @@
If [code]true[/code], the [LineEdit] will always show the caret, even if focus is lost.
-
+
Allow moving caret, selecting and removing the individual composite character components.
[b]Note:[/b] [kbd]Backspace[/kbd] is always removing individual composite character components.
diff --git a/doc/classes/TextEdit.xml b/doc/classes/TextEdit.xml
index 26ad20e85b0..e120f79236a 100644
--- a/doc/classes/TextEdit.xml
+++ b/doc/classes/TextEdit.xml
@@ -942,7 +942,7 @@
Duration (in seconds) of a caret's blinking cycle.
-
+
Allow moving caret, selecting and removing the individual composite character components.
[b]Note:[/b] [kbd]Backspace[/kbd] is always removing individual composite character components.
diff --git a/doc/classes/TextServer.xml b/doc/classes/TextServer.xml
index 443716435aa..64989eb98b3 100644
--- a/doc/classes/TextServer.xml
+++ b/doc/classes/TextServer.xml
@@ -261,6 +261,13 @@
Returns font family name.
+
+
+
+
+ Returns font OpenType feature set override.
+
+
@@ -663,6 +670,14 @@
Sets the font family name.
+
+
+
+
+
+ Sets font OpenType feature set override.
+
+
diff --git a/doc/classes/TextServerExtension.xml b/doc/classes/TextServerExtension.xml
index fb4ac630ba0..aa6c93456b4 100644
--- a/doc/classes/TextServerExtension.xml
+++ b/doc/classes/TextServerExtension.xml
@@ -261,6 +261,13 @@
Returns font family name.
+
+
+
+
+ Returns font OpenType feature set override.
+
+
@@ -550,6 +557,7 @@
+ If set to [code]true[/code] auto-hinting is preffered over font built-in hinting.
@@ -670,6 +678,14 @@
Sets the font family name.
+
+
+
+
+
+ Sets font OpenType feature set override.
+
+
diff --git a/editor/import/dynamicfont_import_settings.cpp b/editor/import/dynamicfont_import_settings.cpp
index c8d8cd8ee19..3151496bec9 100644
--- a/editor/import/dynamicfont_import_settings.cpp
+++ b/editor/import/dynamicfont_import_settings.cpp
@@ -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::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();
diff --git a/editor/import/dynamicfont_import_settings.h b/editor/import/dynamicfont_import_settings.h
index be8cf7ad3dc..89665ae4764 100644
--- a/editor/import/dynamicfont_import_settings.h
+++ b/editor/import/dynamicfont_import_settings.h
@@ -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);
diff --git a/editor/import/resource_importer_dynamicfont.cpp b/editor/import/resource_importer_dynamicfont.cpp
index 54e95f905ea..11f563a9827 100644
--- a/editor/import/resource_importer_dynamicfont.cpp
+++ b/editor/import/resource_importer_dynamicfont.cpp
@@ -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()));
r_options->push_back(ImportOption(PropertyInfo(Variant::PACKED_STRING_ARRAY, "preload/glyph_ranges"), Vector()));
@@ -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);
diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp
index f1945f62cbc..0127795a864 100644
--- a/modules/text_server_adv/text_server_adv.cpp
+++ b/modules/text_server_adv/text_server_adv.cpp
@@ -2841,6 +2841,24 @@ Vector 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 &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 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 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;
diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h
index fb9446da9fd..6ff9817dcf8 100644
--- a/modules/text_server_adv/text_server_adv.h
+++ b/modules/text_server_adv/text_server_adv.h
@@ -187,6 +187,7 @@ class TextServerAdvanced : public TextServer {
Set supported_scripts;
Dictionary supported_features;
Dictionary supported_varaitions;
+ Dictionary feature_overrides;
// Language/script support override.
Map 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 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 &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 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;
diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp
index 1f7c5427be5..f52a7a95ab3 100644
--- a/modules/text_server_fb/text_server_fb.cpp
+++ b/modules/text_server_fb/text_server_fb.cpp
@@ -1995,6 +1995,24 @@ Vector 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();
}
diff --git a/modules/text_server_fb/text_server_fb.h b/modules/text_server_fb/text_server_fb.h
index d47fa44f8f7..9d1be50b047 100644
--- a/modules/text_server_fb/text_server_fb.h
+++ b/modules/text_server_fb/text_server_fb.h
@@ -150,6 +150,7 @@ class TextServerFallback : public TextServer {
bool face_init = false;
Dictionary supported_varaitions;
+ Dictionary feature_overrides;
// Language/script support override.
Map 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 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;
diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp
index 25e931c2870..8cb01c00be7 100644
--- a/scene/gui/button.cpp
+++ b/scene/gui/button.cpp
@@ -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 *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));
}
diff --git a/scene/gui/graph_node.cpp b/scene/gui/graph_node.cpp
index 1a270ff9425..d6569e3de46 100644
--- a/scene/gui/graph_node.cpp
+++ b/scene/gui/graph_node.cpp
@@ -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 *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));
diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp
index 54c4835ccf9..edd206a1caa 100644
--- a/scene/gui/label.cpp
+++ b/scene/gui/label.cpp
@@ -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 *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));
}
diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp
index f000f64caf9..ec18101d2f3 100644
--- a/scene/gui/line_edit.cpp
+++ b/scene/gui/line_edit.cpp
@@ -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 *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));
}
diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h
index 029ff07d134..0c313f71c27 100644
--- a/scene/gui/line_edit.h
+++ b/scene/gui/line_edit.h
@@ -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;
diff --git a/scene/gui/link_button.cpp b/scene/gui/link_button.cpp
index 1890e461e95..67ffa2c7ed6 100644
--- a/scene/gui/link_button.cpp
+++ b/scene/gui/link_button.cpp
@@ -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 *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));
}
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 817a4453a89..939582f2f59 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -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 *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));
}
diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h
index d51ac8dffca..57b48b5f520 100644
--- a/scene/gui/text_edit.h
+++ b/scene/gui/text_edit.h
@@ -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;
diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp
index 9bd98237ff0..d9e0c301def 100644
--- a/scene/resources/font.cpp
+++ b/scene/resources/font.cpp
@@ -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 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 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 *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 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 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);
diff --git a/scene/resources/font.h b/scene/resources/font.h
index 9c3672bd691..1b4ecc73ce7 100644
--- a/scene/resources/font.h
+++ b/scene/resources/font.h
@@ -198,6 +198,9 @@ public:
virtual void remove_script_support_override(const String &p_script);
virtual Vector 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;
diff --git a/servers/text/text_server_extension.cpp b/servers/text/text_server_extension.cpp
index 2b0510680e1..a51b62e7309 100644
--- a/servers/text/text_server_extension.cpp
+++ b/servers/text/text_server_extension.cpp
@@ -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 TextServerExtension::font_get_script_support_overrides(RID p_font
return Vector();
}
+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)) {
diff --git a/servers/text/text_server_extension.h b/servers/text/text_server_extension.h
index 5c97401118e..9b456c2dd74 100644
--- a/servers/text/text_server_extension.h
+++ b/servers/text/text_server_extension.h
@@ -285,6 +285,11 @@ public:
GDVIRTUAL2(_font_remove_script_support_override, RID, const String &);
GDVIRTUAL1R(Vector, _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);
diff --git a/servers/text_server.cpp b/servers/text_server.cpp
index d547bcc75e0..143cda985d3 100644
--- a/servers/text_server.cpp
+++ b/servers/text_server.cpp
@@ -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 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) {
diff --git a/servers/text_server.h b/servers/text_server.h
index e9c5248866b..d5dccc0edbb 100644
--- a/servers/text_server.h
+++ b/servers/text_server.h
@@ -350,6 +350,9 @@ public:
virtual void font_remove_script_support_override(RID p_font_rid, const String &p_script) = 0;
virtual Vector 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;