ソースを参照

[Font] Add an import option to pre-render all glyphs required for the translation.

bruvzg 2 年 前
コミット
35528b800c

+ 33 - 0
core/string/optimized_translation.cpp

@@ -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);

+ 1 - 0
core/string/optimized_translation.h

@@ -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() {}

+ 13 - 0
core/string/translation.cpp

@@ -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);

+ 1 - 0
core/string/translation.h

@@ -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() {}
 };

+ 17 - 0
core/string/translation_po.cpp

@@ -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.
 

+ 1 - 0
core/string/translation_po.h

@@ -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;

+ 6 - 0
doc/classes/Translation.xml

@@ -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;">

+ 8 - 0
doc/classes/Tree.xml

@@ -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">

+ 159 - 18
editor/import/dynamic_font_import_settings.cpp

@@ -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"));

+ 14 - 2
editor/import/dynamic_font_import_settings.h

@@ -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;

+ 7 - 0
scene/gui/tree.cpp

@@ -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);

+ 1 - 0
scene/gui/tree.h

@@ -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);