Merge pull request #68448 from bruvzg/font_imp_tr

[Font] Add an import option to pre-render all glyphs required for the translation.
This commit is contained in:
Rémi Verschelde 2022-11-14 11:12:01 +01:00
commit 5b3a03bf5c
No known key found for this signature in database
GPG key ID: C3336907360768E1
12 changed files with 261 additions and 20 deletions

View file

@ -267,6 +267,39 @@ StringName OptimizedTranslation::get_message(const StringName &p_src_text, const
}
}
Vector<String> OptimizedTranslation::get_translated_message_list() const {
Vector<String> msgs;
const int *htr = hash_table.ptr();
const uint32_t *htptr = (const uint32_t *)&htr[0];
const int *btr = bucket_table.ptr();
const uint32_t *btptr = (const uint32_t *)&btr[0];
const uint8_t *sr = strings.ptr();
const char *sptr = (const char *)&sr[0];
for (int i = 0; i < hash_table.size(); i++) {
uint32_t p = htptr[i];
if (p != 0xFFFFFFFF) {
const Bucket &bucket = *(const Bucket *)&btptr[p];
for (int j = 0; j < bucket.size; j++) {
if (bucket.elem[j].comp_size == bucket.elem[j].uncomp_size) {
String rstr;
rstr.parse_utf8(&sptr[bucket.elem[j].str_offset], bucket.elem[j].uncomp_size);
msgs.push_back(rstr);
} else {
CharString uncomp;
uncomp.resize(bucket.elem[j].uncomp_size + 1);
smaz_decompress(&sptr[bucket.elem[j].str_offset], bucket.elem[j].comp_size, uncomp.ptrw(), bucket.elem[j].uncomp_size);
String rstr;
rstr.parse_utf8(uncomp.get_data());
msgs.push_back(rstr);
}
}
}
}
return msgs;
}
StringName OptimizedTranslation::get_plural_message(const StringName &p_src_text, const StringName &p_plural_text, int p_n, const StringName &p_context) const {
// The use of plurals translation is not yet supported in OptimizedTranslation.
return get_message(p_src_text, p_context);

View file

@ -81,6 +81,7 @@ protected:
public:
virtual StringName get_message(const StringName &p_src_text, const StringName &p_context = "") const override; //overridable for other implementations
virtual StringName get_plural_message(const StringName &p_src_text, const StringName &p_plural_text, int p_n, const StringName &p_context = "") const override;
virtual Vector<String> get_translated_message_list() const override;
void generate(const Ref<Translation> &p_from);
OptimizedTranslation() {}

View file

@ -59,6 +59,18 @@ Vector<String> Translation::_get_message_list() const {
return msgs;
}
Vector<String> Translation::get_translated_message_list() const {
Vector<String> msgs;
msgs.resize(translation_map.size());
int idx = 0;
for (const KeyValue<StringName, StringName> &E : translation_map) {
msgs.set(idx, E.value);
idx += 1;
}
return msgs;
}
void Translation::_set_messages(const Dictionary &p_messages) {
List<Variant> keys;
p_messages.get_key_list(&keys);
@ -140,6 +152,7 @@ void Translation::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_plural_message", "src_message", "src_plural_message", "n", "context"), &Translation::get_plural_message, DEFVAL(""));
ClassDB::bind_method(D_METHOD("erase_message", "src_message", "context"), &Translation::erase_message, DEFVAL(""));
ClassDB::bind_method(D_METHOD("get_message_list"), &Translation::_get_message_list);
ClassDB::bind_method(D_METHOD("get_translated_message_list"), &Translation::get_translated_message_list);
ClassDB::bind_method(D_METHOD("get_message_count"), &Translation::get_message_count);
ClassDB::bind_method(D_METHOD("_set_messages", "messages"), &Translation::_set_messages);
ClassDB::bind_method(D_METHOD("_get_messages"), &Translation::_get_messages);

View file

@ -64,6 +64,7 @@ public:
virtual void erase_message(const StringName &p_src_text, const StringName &p_context = "");
virtual void get_message_list(List<StringName> *r_messages) const;
virtual int get_message_count() const;
virtual Vector<String> get_translated_message_list() const;
Translation() {}
};

View file

@ -103,6 +103,23 @@ void TranslationPO::_set_messages(const Dictionary &p_messages) {
}
}
Vector<String> TranslationPO::get_translated_message_list() const {
Vector<String> msgs;
for (const KeyValue<StringName, HashMap<StringName, Vector<StringName>>> &E : translation_map) {
if (E.key != StringName()) {
continue;
}
for (const KeyValue<StringName, Vector<StringName>> &E2 : E.value) {
for (const StringName &E3 : E2.value) {
msgs.push_back(E3);
}
}
}
return msgs;
}
Vector<String> TranslationPO::_get_message_list() const {
// Return all keys in translation_map.

View file

@ -70,6 +70,7 @@ protected:
static void _bind_methods();
public:
Vector<String> get_translated_message_list() const override;
void get_message_list(List<StringName> *r_messages) const override;
int get_message_count() const override;
void add_message(const StringName &p_src_text, const StringName &p_xlated_text, const StringName &p_context = "") override;

View file

@ -88,6 +88,12 @@
The number [param n] is the number or quantity of the plural object. It will be used to guide the translation system to fetch the correct plural form for the selected language.
</description>
</method>
<method name="get_translated_message_list" qualifiers="const">
<return type="PackedStringArray" />
<description>
Returns all the messages (translated text).
</description>
</method>
</methods>
<members>
<member name="locale" type="String" setter="set_locale" getter="get_locale" default="&quot;en&quot;">

View file

@ -293,6 +293,14 @@
Sets language code of column title used for line-breaking and text shaping algorithms, if left empty current locale is used instead.
</description>
</method>
<method name="set_selected">
<return type="void" />
<param index="0" name="item" type="TreeItem" />
<param index="1" name="column" type="int" />
<description>
Selects the specified [TreeItem] and column.
</description>
</method>
</methods>
<members>
<member name="allow_reselect" type="bool" setter="set_allow_reselect" getter="get_allow_reselect" default="false">

View file

@ -30,6 +30,7 @@
#include "dynamic_font_import_settings.h"
#include "core/config/project_settings.h"
#include "editor/editor_file_dialog.h"
#include "editor/editor_file_system.h"
#include "editor/editor_inspector.h"
@ -528,6 +529,12 @@ void DynamicFontImportSettings::_variation_selected() {
label_glyphs->set_text(TTR("Preloaded glyphs: ") + itos(import_variation_data->selected_glyphs.size()));
_range_selected();
_change_text_opts();
btn_fill->set_disabled(false);
btn_fill_locales->set_disabled(false);
} else {
btn_fill->set_disabled(true);
btn_fill_locales->set_disabled(true);
}
}
@ -551,6 +558,15 @@ void DynamicFontImportSettings::_variation_remove(Object *p_item, int p_column,
}
_variations_validate();
vars_item = vars_list->get_selected();
if (vars_item) {
btn_fill->set_disabled(false);
btn_fill_locales->set_disabled(false);
} else {
btn_fill->set_disabled(true);
btn_fill_locales->set_disabled(true);
}
}
void DynamicFontImportSettings::_variation_changed(const String &p_edited_property) {
@ -623,6 +639,27 @@ void DynamicFontImportSettings::_change_text_opts() {
text_edit->add_theme_font_override("font", font_main_text);
}
void DynamicFontImportSettings::_glyph_update_lbl() {
Ref<DynamicFontImportSettingsData> import_variation_data;
TreeItem *vars_item = vars_list->get_selected();
if (vars_item) {
import_variation_data = vars_item->get_metadata(0);
}
if (import_variation_data.is_null()) {
return;
}
int linked_glyphs = 0;
for (const char32_t &c : import_variation_data->selected_chars) {
if (import_variation_data->selected_glyphs.has(font_main->get_glyph_index(16, c))) {
linked_glyphs++;
}
}
int unlinked_glyphs = import_variation_data->selected_glyphs.size() - linked_glyphs;
label_glyphs->set_text(TTR("Preloaded glyphs:") + " " + itos(unlinked_glyphs + import_variation_data->selected_chars.size()));
}
void DynamicFontImportSettings::_glyph_clear() {
Ref<DynamicFontImportSettingsData> import_variation_data;
@ -635,7 +672,7 @@ void DynamicFontImportSettings::_glyph_clear() {
}
import_variation_data->selected_glyphs.clear();
label_glyphs->set_text(TTR("Preloaded glyphs:") + " " + itos(import_variation_data->selected_glyphs.size()));
_glyph_update_lbl();
_range_selected();
}
@ -662,7 +699,7 @@ void DynamicFontImportSettings::_glyph_text_selected() {
}
}
TS->free_rid(text_rid);
label_glyphs->set_text(TTR("Preloaded glyphs:") + " " + itos(import_variation_data->selected_glyphs.size()));
_glyph_update_lbl();
}
_range_selected();
}
@ -699,7 +736,7 @@ void DynamicFontImportSettings::_glyph_selected() {
item->clear_custom_bg_color(glyph_table->get_selected_column());
}
}
label_glyphs->set_text(TTR("Preloaded glyphs:") + " " + itos(import_variation_data->selected_glyphs.size()));
_glyph_update_lbl();
item = glyph_tree->get_selected();
ERR_FAIL_NULL(item);
@ -800,7 +837,7 @@ void DynamicFontImportSettings::_edit_range(int32_t p_start, int32_t p_end) {
col = 0;
}
}
label_glyphs->set_text(TTR("Preloaded glyphs:") + " " + itos(import_variation_data->selected_glyphs.size()));
_glyph_update_lbl();
}
bool DynamicFontImportSettings::_char_update(int32_t p_char) {
@ -947,10 +984,73 @@ void DynamicFontImportSettings::_re_import() {
EditorFileSystem::get_singleton()->reimport_file_with_custom_parameters(base_path, "font_data_dynamic", main_settings);
}
void DynamicFontImportSettings::_locale_edited() {
TreeItem *item = locale_tree->get_selected();
ERR_FAIL_NULL(item);
item->set_checked(0, !item->is_checked(0));
}
void DynamicFontImportSettings::_process_locales() {
Ref<DynamicFontImportSettingsData> import_variation_data;
TreeItem *vars_item = vars_list->get_selected();
if (vars_item) {
import_variation_data = vars_item->get_metadata(0);
}
if (import_variation_data.is_null()) {
return;
}
for (int i = 0; i < locale_root->get_child_count(); i++) {
TreeItem *item = locale_root->get_child(i);
if (item) {
if (item->is_checked(0)) {
String locale = item->get_text(0);
Ref<Translation> tr = ResourceLoader::load(locale);
if (tr.is_valid()) {
Vector<String> messages = tr->get_translated_message_list();
for (const String &E : messages) {
RID text_rid = TS->create_shaped_text();
if (text_rid.is_valid()) {
TS->shaped_text_add_string(text_rid, E, font_main->get_rids(), 16, Dictionary(), tr->get_locale());
TS->shaped_text_shape(text_rid);
const Glyph *gl = TS->shaped_text_get_glyphs(text_rid);
const int gl_size = TS->shaped_text_get_glyph_count(text_rid);
for (int j = 0; j < gl_size; j++) {
if (gl[j].font_rid.is_valid() && gl[j].index != 0) {
import_variation_data->selected_glyphs.insert(gl[j].index);
}
}
TS->free_rid(text_rid);
}
}
}
}
}
}
_glyph_update_lbl();
_range_selected();
}
void DynamicFontImportSettings::open_settings(const String &p_path) {
// Load base font data.
Vector<uint8_t> font_data = FileAccess::get_file_as_array(p_path);
// Load project locale list.
locale_tree->clear();
locale_root = locale_tree->create_item();
ERR_FAIL_NULL(locale_root);
Vector<String> translations = GLOBAL_GET("internationalization/locale/translations");
for (const String &E : translations) {
TreeItem *item = locale_tree->create_item(locale_root);
ERR_FAIL_NULL(item);
item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
item->set_text(0, E);
}
// Load font for preview.
font_preview.instantiate();
font_preview->set_data(font_data);
@ -1003,10 +1103,11 @@ void DynamicFontImportSettings::open_settings(const String &p_path) {
int gww = get_theme_font(SNAME("font"))->get_string_size("00000").x + 50;
glyph_table->set_column_custom_minimum_width(0, gww);
glyph_table->clear();
vars_list->clear();
glyph_tree->set_selected(glyph_root->get_child(0));
vars_list_root = vars_list->create_item();
import_settings_data->settings.clear();
@ -1080,6 +1181,10 @@ void DynamicFontImportSettings::open_settings(const String &p_path) {
import_variation_data_custom->selected_glyphs.insert(c);
}
}
if (preload_configurations.is_empty()) {
_variation_add(); // Add default variation.
}
vars_list->set_selected(vars_list_root->get_child(0));
} else {
Variant value = config->get_value("params", key);
import_settings_data->defaults[key] = value;
@ -1269,11 +1374,57 @@ DynamicFontImportSettings::DynamicFontImportSettings() {
inspector_vars->connect("property_edited", callable_mp(this, &DynamicFontImportSettings::_variation_changed));
page2_side_vb->add_child(inspector_vars);
VBoxContainer *preload_pages_vb = memnew(VBoxContainer);
page2_hb->add_child(preload_pages_vb);
preload_pages = memnew(TabContainer);
preload_pages->set_tab_alignment(TabBar::ALIGNMENT_CENTER);
preload_pages->set_v_size_flags(Control::SIZE_EXPAND_FILL);
preload_pages->set_h_size_flags(Control::SIZE_EXPAND_FILL);
page2_hb->add_child(preload_pages);
preload_pages_vb->add_child(preload_pages);
HBoxContainer *gl_hb = memnew(HBoxContainer);
preload_pages_vb->add_child(gl_hb);
gl_hb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
label_glyphs = memnew(Label);
gl_hb->add_child(label_glyphs);
label_glyphs->set_text(TTR("Preloaded glyphs:") + " " + itos(0));
label_glyphs->set_custom_minimum_size(Size2(50 * EDSCALE, 0));
Button *btn_clear = memnew(Button);
gl_hb->add_child(btn_clear);
btn_clear->set_text(TTR("Clear Glyph List"));
btn_clear->connect("pressed", callable_mp(this, &DynamicFontImportSettings::_glyph_clear));
VBoxContainer *page2_0_vb = memnew(VBoxContainer);
page2_0_vb->set_name(TTR("Glyphs from the Translations"));
preload_pages->add_child(page2_0_vb);
page2_0_description = memnew(Label);
page2_0_description->set_text(TTR("Select translations to add all required glyphs to pre-render list:"));
page2_0_description->set_h_size_flags(Control::SIZE_EXPAND_FILL);
page2_0_description->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART);
page2_0_vb->add_child(page2_0_description);
locale_tree = memnew(Tree);
page2_0_vb->add_child(locale_tree);
locale_tree->set_columns(1);
locale_tree->set_hide_root(true);
locale_tree->set_column_expand(0, true);
locale_tree->connect("item_activated", callable_mp(this, &DynamicFontImportSettings::_locale_edited));
locale_tree->set_column_custom_minimum_width(0, 120 * EDSCALE);
locale_tree->set_v_size_flags(Control::SIZE_EXPAND_FILL);
locale_root = locale_tree->create_item();
HBoxContainer *locale_hb = memnew(HBoxContainer);
page2_0_vb->add_child(locale_hb);
locale_hb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
btn_fill_locales = memnew(Button);
locale_hb->add_child(btn_fill_locales);
btn_fill_locales->set_text(TTR("Shape all Strings in the Translations and Add Glyphs"));
btn_fill_locales->connect("pressed", callable_mp(this, &DynamicFontImportSettings::_process_locales));
// Page 2.1 layout: Text to select glyphs
VBoxContainer *page2_1_vb = memnew(VBoxContainer);
@ -1281,7 +1432,7 @@ DynamicFontImportSettings::DynamicFontImportSettings() {
preload_pages->add_child(page2_1_vb);
page2_1_description = memnew(Label);
page2_1_description->set_text(TTR("Enter a text to shape and add all required glyphs to pre-render list:"));
page2_1_description->set_text(TTR("Enter a text and select OpenType features to shape and add all required glyphs to pre-render list:"));
page2_1_description->set_h_size_flags(Control::SIZE_EXPAND_FILL);
page2_1_description->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART);
page2_1_vb->add_child(page2_1_description);
@ -1307,21 +1458,11 @@ DynamicFontImportSettings::DynamicFontImportSettings() {
page2_1_vb->add_child(text_hb);
text_hb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
label_glyphs = memnew(Label);
text_hb->add_child(label_glyphs);
label_glyphs->set_text(TTR("Preloaded glyphs:") + " " + itos(0));
label_glyphs->set_custom_minimum_size(Size2(50 * EDSCALE, 0));
Button *btn_fill = memnew(Button);
btn_fill = memnew(Button);
text_hb->add_child(btn_fill);
btn_fill->set_text(TTR("Shape Text and Add Glyphs"));
btn_fill->connect("pressed", callable_mp(this, &DynamicFontImportSettings::_glyph_text_selected));
Button *btn_clear = memnew(Button);
text_hb->add_child(btn_clear);
btn_clear->set_text(TTR("Clear Glyph List"));
btn_clear->connect("pressed", callable_mp(this, &DynamicFontImportSettings::_glyph_clear));
// Page 2.2 layout: Character map
VBoxContainer *page2_2_vb = memnew(VBoxContainer);
page2_2_vb->set_name(TTR("Glyphs from the Character Map"));

View file

@ -118,18 +118,30 @@ class DynamicFontImportSettings : public ConfirmationDialog {
TabContainer *preload_pages = nullptr;
Label *label_glyphs = nullptr;
void _glyph_clear();
void _glyph_update_lbl();
// Page 2.0 layout: Translations
Label *page2_0_description = nullptr;
Tree *locale_tree = nullptr;
TreeItem *locale_root = nullptr;
Button *btn_fill_locales = nullptr;
void _locale_edited();
void _process_locales();
// Page 2.1 layout: Text to select glyphs
Label *page2_1_description = nullptr;
Label *label_glyphs = nullptr;
TextEdit *text_edit = nullptr;
EditorInspector *inspector_text = nullptr;
Button *btn_fill = nullptr;
List<ResourceImporter::ImportOption> options_text;
Ref<DynamicFontImportSettingsData> text_settings_data;
void _change_text_opts();
void _glyph_text_selected();
void _glyph_clear();
// Page 2.2 layout: Character map
Label *page2_2_description = nullptr;

View file

@ -4338,6 +4338,12 @@ TreeItem *Tree::get_selected() const {
return selected_item;
}
void Tree::set_selected(TreeItem *p_item, int p_column) {
ERR_FAIL_INDEX(p_column, columns.size());
ERR_FAIL_COND(!p_item);
select_single_item(p_item, get_root(), p_column);
}
int Tree::get_selected_column() const {
return selected_col;
}
@ -5156,6 +5162,7 @@ void Tree::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_root_hidden"), &Tree::is_root_hidden);
ClassDB::bind_method(D_METHOD("get_next_selected", "from"), &Tree::get_next_selected);
ClassDB::bind_method(D_METHOD("get_selected"), &Tree::get_selected);
ClassDB::bind_method(D_METHOD("set_selected", "item", "column"), &Tree::set_selected);
ClassDB::bind_method(D_METHOD("get_selected_column"), &Tree::get_selected_column);
ClassDB::bind_method(D_METHOD("get_pressed_button"), &Tree::get_pressed_button);
ClassDB::bind_method(D_METHOD("set_select_mode", "mode"), &Tree::set_select_mode);

View file

@ -666,6 +666,7 @@ public:
bool is_root_hidden() const;
TreeItem *get_next_selected(TreeItem *p_item);
TreeItem *get_selected() const;
void set_selected(TreeItem *p_item, int p_column = 0);
int get_selected_column() const;
int get_pressed_button() const;
void set_select_mode(SelectMode p_mode);