diff options
121 files changed, 3557 insertions, 2133 deletions
diff --git a/core/core_constants.cpp b/core/core_constants.cpp index 6f26288eb7..2f5fd05e6a 100644 --- a/core/core_constants.cpp +++ b/core/core_constants.cpp @@ -584,6 +584,7 @@ void register_global_constants() { BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_INT_IS_OBJECTID); BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_INT_IS_POINTER); BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_ARRAY_TYPE); + BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_LOCALE_ID); BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_MAX); BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_NONE); diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp index 8fe1ac29b3..7d42a9c8d2 100644 --- a/core/io/resource_loader.cpp +++ b/core/io/resource_loader.cpp @@ -806,38 +806,26 @@ String ResourceLoader::_path_remap(const String &p_path, bool *r_translation_rem // To find the path of the remapped resource, we extract the locale name after // the last ':' to match the project locale. - // We also fall back in case of regional locales as done in TranslationServer::translate - // (e.g. 'ru_RU' -> 'ru' if the former has no specific mapping). String locale = TranslationServer::get_singleton()->get_locale(); ERR_FAIL_COND_V_MSG(locale.length() < 2, p_path, "Could not remap path '" + p_path + "' for translation as configured locale '" + locale + "' is invalid."); - String lang = TranslationServer::get_language_code(locale); Vector<String> &res_remaps = *translation_remaps.getptr(new_path); - bool near_match = false; + int best_score = 0; for (int i = 0; i < res_remaps.size(); i++) { int split = res_remaps[i].rfind(":"); if (split == -1) { continue; } - String l = res_remaps[i].substr(split + 1).strip_edges(); - if (l == locale) { // Exact match. - new_path = res_remaps[i].left(split); - break; - } else if (near_match) { - continue; // Already found near match, keep going for potential exact match. - } - - // No exact match (e.g. locale 'ru_RU' but remap is 'ru'), let's look further - // for a near match (same language code, i.e. first 2 or 3 letters before - // regional code, if included). - if (TranslationServer::get_language_code(l) == lang) { - // Language code matches, that's a near match. Keep looking for exact match. - near_match = true; + int score = TranslationServer::get_singleton()->compare_locales(locale, l); + if (score >= best_score) { new_path = res_remaps[i].left(split); - continue; + best_score = score; + if (score == 10) { + break; // Exact match, skip the rest. + } } } diff --git a/core/object/object.h b/core/object/object.h index 602bd3cda1..63130a1aef 100644 --- a/core/object/object.h +++ b/core/object/object.h @@ -94,6 +94,7 @@ enum PropertyHint { PROPERTY_HINT_INT_IS_OBJECTID, PROPERTY_HINT_ARRAY_TYPE, PROPERTY_HINT_INT_IS_POINTER, + PROPERTY_HINT_LOCALE_ID, PROPERTY_HINT_MAX, // When updating PropertyHint, also sync the hardcoded list in VisualScriptEditorVariableEdit }; diff --git a/core/string/locales.h b/core/string/locales.h new file mode 100644 index 0000000000..9ef6dee5ac --- /dev/null +++ b/core/string/locales.h @@ -0,0 +1,1196 @@ +/*************************************************************************/ +/* locales.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef LOCALES_H +#define LOCALES_H + +// Windows has some weird locale identifiers which do not honor the ISO 639-1 +// standardized nomenclature. Whenever those don't conflict with existing ISO +// identifiers, we override them. +// +// Reference: +// - https://msdn.microsoft.com/en-us/library/windows/desktop/ms693062(v=vs.85).aspx + +static const char *locale_renames[][2] = { + { "in", "id" }, // Indonesian + { "iw", "he" }, // Hebrew + { "no", "nb" }, // Norwegian Bokmål + { nullptr, nullptr } +}; + +// Additional script information to preferred scripts. +// Language code, script code, default country, supported countries. +// Reference: +// - https://lh.2xlibre.net/locales/ +// - https://www.localeplanet.com/icu/index.html +// - https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-lcid/70feba9f-294e-491e-b6eb-56532684c37f + +static const char *locale_scripts[][4] = { + { "az", "Latn", "", "AZ" }, + { "az", "Arab", "", "IR" }, + { "bs", "Latn", "", "BA" }, + { "ff", "Latn", "", "BF,CM,GH,GM,GN,GW,LR,MR,NE,NG,SL,SN" }, + { "pa", "Arab", "PK", "PK" }, + { "pa", "Guru", "IN", "IN" }, + { "sd", "Arab", "PK", "PK" }, + { "sd", "Deva", "IN", "IN" }, + { "shi", "Tfng", "", "MA" }, + { "sr", "Cyrl", "", "BA,RS,XK" }, + { "sr", "Latn", "", "ME" }, + { "uz", "Latn", "", "UZ" }, + { "uz", "Arab", "AF", "AF" }, + { "vai", "Vaii", "", "LR" }, + { "yue", "Hans", "CN", "CN" }, + { "yue", "Hant", "HK", "HK" }, + { "zh", "Hans", "CN", "CN,SG" }, + { "zh", "Hant", "TW", "HK,MO,TW" }, + { nullptr, nullptr, nullptr, nullptr } +}; + +// Additional mapping for outdated, temporary or exceptionally reserved country codes. +// Reference: +// - https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2 +// - https://www.iso.org/obp/ui/#search/code/ + +static const char *country_renames[][2] = { + { "BU", "MM" }, // Burma, name changed to Myanmar. + { "KV", "XK" }, // Kosovo (temporary FIPS code to European Commission code), no official ISO code assigned. + { "TP", "TL" }, // East Timor, name changed to Timor-Leste. + { "UK", "GB" }, // United Kingdom, exceptionally reserved code. + { nullptr, nullptr } +}; + +// Country code, country name. +// Reference: +// - https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2 +// - https://www.iso.org/obp/ui/#search/code/ + +static const char *country_names[][2] = { + { "AC", "Ascension Island" }, // Exceptionally reserved. + { "AD", "Andorra" }, + { "AE", "United Arab Emirates" }, + { "AF", "Afghanistan" }, + { "AG", "Antigua and Barbuda" }, + { "AI", "Anguilla" }, + { "AL", "Albania" }, + { "AM", "Armenia" }, + { "AN", "Netherlands Antilles" }, // Transitionally reserved, divided into BQ, CW and SX. + { "AO", "Angola" }, + { "AQ", "Antarctica" }, + { "AR", "Argentina" }, + { "AS", "American Samoa" }, + { "AT", "Austria" }, + { "AU", "Australia" }, + { "AW", "Aruba" }, + { "AX", "Åland Islands" }, + { "AZ", "Azerbaijan" }, + { "BA", "Bosnia and Herzegovina" }, + { "BB", "Barbados" }, + { "BD", "Bangladesh" }, + { "BE", "Belgium" }, + { "BF", "Burkina Faso" }, + { "BG", "Bulgaria" }, + { "BH", "Bahrain" }, + { "BI", "Burundi" }, + { "BJ", "Benin" }, + { "BL", "St. Barthélemy" }, + { "BM", "Bermuda" }, + { "BN", "Brunei" }, + { "BO", "Bolivia" }, + { "BQ", "Caribbean Netherlands" }, + { "BR", "Brazil" }, + { "BS", "Bahamas" }, + { "BT", "Bhutan" }, + { "BV", "Bouvet Island" }, + { "BW", "Botswana" }, + { "BY", "Belarus" }, + { "BZ", "Belize" }, + { "CA", "Canada" }, + { "CC", "Cocos (Keeling) Islands" }, + { "CD", "Congo - Kinshasa" }, + { "CF", "Central African Republic" }, + { "CG", "Congo - Brazzaville" }, + { "CH", "Switzerland" }, + { "CI", "Côte d'Ivoire" }, + { "CK", "Cook Islands" }, + { "CL", "Chile" }, + { "CM", "Cameroon" }, + { "CN", "China" }, + { "CO", "Colombia" }, + { "CP", "Clipperton Island" }, // Exceptionally reserved. + { "CR", "Costa Rica" }, + { "CQ", "Island of Sark" }, // Exceptionally reserved. + { "CU", "Cuba" }, + { "CV", "Cabo Verde" }, + { "CW", "Curaçao" }, + { "CX", "Christmas Island" }, + { "CY", "Cyprus" }, + { "CZ", "Czechia" }, + { "DE", "Germany" }, + { "DG", "Diego Garcia" }, // Exceptionally reserved. + { "DJ", "Djibouti" }, + { "DK", "Denmark" }, + { "DM", "Dominica" }, + { "DO", "Dominican Republic" }, + { "DZ", "Algeria" }, + { "EA", "Ceuta and Melilla" }, // Exceptionally reserved. + { "EC", "Ecuador" }, + { "EE", "Estonia" }, + { "EG", "Egypt" }, + { "EH", "Western Sahara" }, + { "ER", "Eritrea" }, + { "ES", "Spain" }, + { "ET", "Ethiopia" }, + { "EU", "European Union" }, // Exceptionally reserved. + { "EZ", "Eurozone" }, // Exceptionally reserved. + { "FI", "Finland" }, + { "FJ", "Fiji" }, + { "FK", "Falkland Islands" }, + { "FM", "Micronesia" }, + { "FO", "Faroe Islands" }, + { "FR", "France" }, + { "FX", "France, Metropolitan" }, // Exceptionally reserved. + { "GA", "Gabon" }, + { "GB", "United Kingdom" }, + { "GD", "Grenada" }, + { "GE", "Georgia" }, + { "GF", "French Guiana" }, + { "GG", "Guernsey" }, + { "GH", "Ghana" }, + { "GI", "Gibraltar" }, + { "GL", "Greenland" }, + { "GM", "Gambia" }, + { "GN", "Guinea" }, + { "GP", "Guadeloupe" }, + { "GQ", "Equatorial Guinea" }, + { "GR", "Greece" }, + { "GS", "South Georgia and South Sandwich Islands" }, + { "GT", "Guatemala" }, + { "GU", "Guam" }, + { "GW", "Guinea-Bissau" }, + { "GY", "Guyana" }, + { "HK", "Hong Kong" }, + { "HM", "Heard Island and McDonald Islands" }, + { "HN", "Honduras" }, + { "HR", "Croatia" }, + { "HT", "Haiti" }, + { "HU", "Hungary" }, + { "IC", "Canary Islands" }, // Exceptionally reserved. + { "ID", "Indonesia" }, + { "IE", "Ireland" }, + { "IL", "Israel" }, + { "IM", "Isle of Man" }, + { "IN", "India" }, + { "IO", "British Indian Ocean Territory" }, + { "IQ", "Iraq" }, + { "IR", "Iran" }, + { "IS", "Iceland" }, + { "IT", "Italy" }, + { "JE", "Jersey" }, + { "JM", "Jamaica" }, + { "JO", "Jordan" }, + { "JP", "Japan" }, + { "KE", "Kenya" }, + { "KG", "Kyrgyzstan" }, + { "KH", "Cambodia" }, + { "KI", "Kiribati" }, + { "KM", "Comoros" }, + { "KN", "St. Kitts and Nevis" }, + { "KP", "North Korea" }, + { "KR", "South Korea" }, + { "KW", "Kuwait" }, + { "KY", "Cayman Islands" }, + { "KZ", "Kazakhstan" }, + { "LA", "Laos" }, + { "LB", "Lebanon" }, + { "LC", "St. Lucia" }, + { "LI", "Liechtenstein" }, + { "LK", "Sri Lanka" }, + { "LR", "Liberia" }, + { "LS", "Lesotho" }, + { "LT", "Lithuania" }, + { "LU", "Luxembourg" }, + { "LV", "Latvia" }, + { "LY", "Libya" }, + { "MA", "Morocco" }, + { "MC", "Monaco" }, + { "MD", "Moldova" }, + { "ME", "Montenegro" }, + { "MF", "St. Martin" }, + { "MG", "Madagascar" }, + { "MH", "Marshall Islands" }, + { "MK", "North Macedonia" }, + { "ML", "Mali" }, + { "MM", "Myanmar" }, + { "MN", "Mongolia" }, + { "MO", "Macao" }, + { "MP", "Northern Mariana Islands" }, + { "MQ", "Martinique" }, + { "MR", "Mauritania" }, + { "MS", "Montserrat" }, + { "MT", "Malta" }, + { "MU", "Mauritius" }, + { "MV", "Maldives" }, + { "MW", "Malawi" }, + { "MX", "Mexico" }, + { "MY", "Malaysia" }, + { "MZ", "Mozambique" }, + { "NA", "Namibia" }, + { "NC", "New Caledonia" }, + { "NE", "Niger" }, + { "NF", "Norfolk Island" }, + { "NG", "Nigeria" }, + { "NI", "Nicaragua" }, + { "NL", "Netherlands" }, + { "NO", "Norway" }, + { "NP", "Nepal" }, + { "NR", "Nauru" }, + { "NU", "Niue" }, + { "NZ", "New Zealand" }, + { "OM", "Oman" }, + { "PA", "Panama" }, + { "PE", "Peru" }, + { "PF", "French Polynesia" }, + { "PG", "Papua New Guinea" }, + { "PH", "Philippines" }, + { "PK", "Pakistan" }, + { "PL", "Poland" }, + { "PM", "St. Pierre and Miquelon" }, + { "PN", "Pitcairn Islands" }, + { "PR", "Puerto Rico" }, + { "PS", "Palestine" }, + { "PT", "Portugal" }, + { "PW", "Palau" }, + { "PY", "Paraguay" }, + { "QA", "Qatar" }, + { "RE", "Réunion" }, + { "RO", "Romania" }, + { "RS", "Serbia" }, + { "RU", "Russia" }, + { "RW", "Rwanda" }, + { "SA", "Saudi Arabia" }, + { "SB", "Solomon Islands" }, + { "SC", "Seychelles" }, + { "SD", "Sudan" }, + { "SE", "Sweden" }, + { "SG", "Singapore" }, + { "SH", "St. Helena, Ascension and Tristan da Cunha" }, + { "SI", "Slovenia" }, + { "SJ", "Svalbard and Jan Mayen" }, + { "SK", "Slovakia" }, + { "SL", "Sierra Leone" }, + { "SM", "San Marino" }, + { "SN", "Senegal" }, + { "SO", "Somalia" }, + { "SR", "Suriname" }, + { "SS", "South Sudan" }, + { "ST", "Sao Tome and Principe" }, + { "SV", "El Salvador" }, + { "SX", "Sint Maarten" }, + { "SY", "Syria" }, + { "SZ", "Eswatini" }, + { "TA", "Tristan da Cunha" }, // Exceptionally reserved. + { "TC", "Turks and Caicos Islands" }, + { "TD", "Chad" }, + { "TF", "French Southern Territories" }, + { "TG", "Togo" }, + { "TH", "Thailand" }, + { "TJ", "Tajikistan" }, + { "TK", "Tokelau" }, + { "TL", "Timor-Leste" }, + { "TM", "Turkmenistan" }, + { "TN", "Tunisia" }, + { "TO", "Tonga" }, + { "TR", "Turkey" }, + { "TT", "Trinidad and Tobago" }, + { "TV", "Tuvalu" }, + { "TW", "Taiwan" }, + { "TZ", "Tanzania" }, + { "UA", "Ukraine" }, + { "UG", "Uganda" }, + { "UM", "U.S. Outlying Islands" }, + { "US", "United States of America" }, + { "UY", "Uruguay" }, + { "UZ", "Uzbekistan" }, + { "VA", "Holy See" }, + { "VC", "St. Vincent and the Grenadines" }, + { "VE", "Venezuela" }, + { "VG", "British Virgin Islands" }, + { "VI", "U.S. Virgin Islands" }, + { "VN", "Viet Nam" }, + { "VU", "Vanuatu" }, + { "WF", "Wallis and Futuna" }, + { "WS", "Samoa" }, + { "XK", "Kosovo" }, // Temporary code, no official ISO code assigned. + { "YE", "Yemen" }, + { "YT", "Mayotte" }, + { "ZA", "South Africa" }, + { "ZM", "Zambia" }, + { "ZW", "Zimbabwe" }, + { nullptr, nullptr } +}; + +// Languages code, language name. +// Reference: +// - https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes +// - https://www.localeplanet.com/icu/index.html +// - https://lh.2xlibre.net/locales/ + +static const char *language_list[][2] = { + { "aa", "Afar" }, + { "ab", "Abkhazian" }, + { "ace", "Achinese" }, + { "ach", "Acoli" }, + { "ada", "Adangme" }, + { "ady", "Adyghe" }, + { "ae", "Avestan" }, + { "aeb", "Tunisian Arabic" }, + { "af", "Afrikaans" }, + { "afh", "Afrihili" }, + { "agq", "Aghem" }, + { "ain", "Ainu" }, + { "agr", "Aguaruna" }, + { "ak", "Akan" }, + { "akk", "Akkadian" }, + { "akz", "Alabama" }, + { "ale", "Aleut" }, + { "aln", "Gheg Albanian" }, + { "alt", "Southern Altai" }, + { "am", "Amharic" }, + { "an", "Aragonese" }, + { "ang", "Old English" }, + { "anp", "Angika" }, + { "ar", "Arabic" }, + { "arc", "Aramaic" }, + { "arn", "Mapudungun" }, + { "aro", "Araona" }, + { "arp", "Arapaho" }, + { "arq", "Algerian Arabic" }, + { "ars", "Najdi Arabic" }, + { "arw", "Arawak" }, + { "ary", "Moroccan Arabic" }, + { "arz", "Egyptian Arabic" }, + { "as", "Assamese" }, + { "asa", "Asu" }, + { "ase", "American Sign Language" }, + { "ast", "Asturian" }, + { "av", "Avaric" }, + { "avk", "Kotava" }, + { "awa", "Awadhi" }, + { "ayc", "Southern Aymara" }, + { "ay", "Aymara" }, + { "az", "Azerbaijani" }, + { "ba", "Bashkir" }, + { "bal", "Baluchi" }, + { "ban", "Balinese" }, + { "bar", "Bavarian" }, + { "bas", "Bassa" }, + { "bax", "Bamun" }, + { "bbc", "Batak Toba" }, + { "bbj", "Ghomala" }, + { "be", "Belarusian" }, + { "bej", "Beja" }, + { "bem", "Bemba" }, + { "ber", "Berber" }, + { "bew", "Betawi" }, + { "bez", "Bena" }, + { "bfd", "Bafut" }, + { "bfq", "Badaga" }, + { "bg", "Bulgarian" }, + { "bhb", "Bhili" }, + { "bgn", "Western Balochi" }, + { "bho", "Bhojpuri" }, + { "bi", "Bislama" }, + { "bik", "Bikol" }, + { "bin", "Bini" }, + { "bjn", "Banjar" }, + { "bkm", "Kom" }, + { "bla", "Siksika" }, + { "bm", "Bambara" }, + { "bn", "Bengali" }, + { "bo", "Tibetan" }, + { "bpy", "Bishnupriya" }, + { "bqi", "Bakhtiari" }, + { "br", "Breton" }, + { "brh", "Brahui" }, + { "brx", "Bodo" }, + { "bs", "Bosnian" }, + { "bss", "Akoose" }, + { "bua", "Buriat" }, + { "bug", "Buginese" }, + { "bum", "Bulu" }, + { "byn", "Bilin" }, + { "byv", "Medumba" }, + { "ca", "Catalan" }, + { "cad", "Caddo" }, + { "car", "Carib" }, + { "cay", "Cayuga" }, + { "cch", "Atsam" }, + { "ccp", "Chakma" }, + { "ce", "Chechen" }, + { "ceb", "Cebuano" }, + { "cgg", "Chiga" }, + { "ch", "Chamorro" }, + { "chb", "Chibcha" }, + { "chg", "Chagatai" }, + { "chk", "Chuukese" }, + { "chm", "Mari" }, + { "chn", "Chinook Jargon" }, + { "cho", "Choctaw" }, + { "chp", "Chipewyan" }, + { "chr", "Cherokee" }, + { "chy", "Cheyenne" }, + { "cic", "Chickasaw" }, + { "ckb", "Central Kurdish" }, + { "csb", "Kashubian" }, + { "cmn", "Mandarin Chinese" }, + { "co", "Corsican" }, + { "cop", "Coptic" }, + { "cps", "Capiznon" }, + { "cr", "Cree" }, + { "crh", "Crimean Tatar" }, + { "crs", "Seselwa Creole French" }, + { "cs", "Czech" }, + { "csb", "Kashubian" }, + { "cu", "Church Slavic" }, + { "cv", "Chuvash" }, + { "cy", "Welsh" }, + { "da", "Danish" }, + { "dak", "Dakota" }, + { "dar", "Dargwa" }, + { "dav", "Taita" }, + { "de", "German" }, + { "del", "Delaware" }, + { "den", "Slave" }, + { "dgr", "Dogrib" }, + { "din", "Dinka" }, + { "dje", "Zarma" }, + { "doi", "Dogri" }, + { "dsb", "Lower Sorbian" }, + { "dtp", "Central Dusun" }, + { "dua", "Duala" }, + { "dum", "Middle Dutch" }, + { "dv", "Dhivehi" }, + { "dyo", "Jola-Fonyi" }, + { "dyu", "Dyula" }, + { "dz", "Dzongkha" }, + { "dzg", "Dazaga" }, + { "ebu", "Embu" }, + { "ee", "Ewe" }, + { "efi", "Efik" }, + { "egl", "Emilian" }, + { "egy", "Ancient Egyptian" }, + { "eka", "Ekajuk" }, + { "el", "Greek" }, + { "elx", "Elamite" }, + { "en", "English" }, + { "enm", "Middle English" }, + { "eo", "Esperanto" }, + { "es", "Spanish" }, + { "esu", "Central Yupik" }, + { "et", "Estonian" }, + { "eu", "Basque" }, + { "ewo", "Ewondo" }, + { "ext", "Extremaduran" }, + { "fa", "Persian" }, + { "fan", "Fang" }, + { "fat", "Fanti" }, + { "ff", "Fulah" }, + { "fi", "Finnish" }, + { "fil", "Filipino" }, + { "fit", "Tornedalen Finnish" }, + { "fj", "Fijian" }, + { "fo", "Faroese" }, + { "fon", "Fon" }, + { "fr", "French" }, + { "frc", "Cajun French" }, + { "frm", "Middle French" }, + { "fro", "Old French" }, + { "frp", "Arpitan" }, + { "frr", "Northern Frisian" }, + { "frs", "Eastern Frisian" }, + { "fur", "Friulian" }, + { "fy", "Western Frisian" }, + { "ga", "Irish" }, + { "gaa", "Ga" }, + { "gag", "Gagauz" }, + { "gan", "Gan Chinese" }, + { "gay", "Gayo" }, + { "gba", "Gbaya" }, + { "gbz", "Zoroastrian Dari" }, + { "gd", "Scottish Gaelic" }, + { "gez", "Geez" }, + { "gil", "Gilbertese" }, + { "gl", "Galician" }, + { "glk", "Gilaki" }, + { "gmh", "Middle High German" }, + { "gn", "Guarani" }, + { "goh", "Old High German" }, + { "gom", "Goan Konkani" }, + { "gon", "Gondi" }, + { "gor", "Gorontalo" }, + { "got", "Gothic" }, + { "grb", "Grebo" }, + { "grc", "Ancient Greek" }, + { "gsw", "Swiss German" }, + { "gu", "Gujarati" }, + { "guc", "Wayuu" }, + { "gur", "Frafra" }, + { "guz", "Gusii" }, + { "gv", "Manx" }, + { "gwi", "Gwichʼin" }, + { "ha", "Hausa" }, + { "hai", "Haida" }, + { "hak", "Hakka Chinese" }, + { "haw", "Hawaiian" }, + { "he", "Hebrew" }, + { "hi", "Hindi" }, + { "hif", "Fiji Hindi" }, + { "hil", "Hiligaynon" }, + { "hit", "Hittite" }, + { "hmn", "Hmong" }, + { "ho", "Hiri Motu" }, + { "hne", "Chhattisgarhi" }, + { "hr", "Croatian" }, + { "hsb", "Upper Sorbian" }, + { "hsn", "Xiang Chinese" }, + { "ht", "Haitian" }, + { "hu", "Hungarian" }, + { "hup", "Hupa" }, + { "hus", "Huastec" }, + { "hy", "Armenian" }, + { "hz", "Herero" }, + { "ia", "Interlingua" }, + { "iba", "Iban" }, + { "ibb", "Ibibio" }, + { "id", "Indonesian" }, + { "ie", "Interlingue" }, + { "ig", "Igbo" }, + { "ii", "Sichuan Yi" }, + { "ik", "Inupiaq" }, + { "ilo", "Iloko" }, + { "inh", "Ingush" }, + { "io", "Ido" }, + { "is", "Icelandic" }, + { "it", "Italian" }, + { "iu", "Inuktitut" }, + { "izh", "Ingrian" }, + { "ja", "Japanese" }, + { "jam", "Jamaican Creole English" }, + { "jbo", "Lojban" }, + { "jgo", "Ngomba" }, + { "jmc", "Machame" }, + { "jpr", "Judeo-Persian" }, + { "jrb", "Judeo-Arabic" }, + { "jut", "Jutish" }, + { "jv", "Javanese" }, + { "ka", "Georgian" }, + { "kaa", "Kara-Kalpak" }, + { "kab", "Kabyle" }, + { "kac", "Kachin" }, + { "kaj", "Jju" }, + { "kam", "Kamba" }, + { "kaw", "Kawi" }, + { "kbd", "Kabardian" }, + { "kbl", "Kanembu" }, + { "kcg", "Tyap" }, + { "kde", "Makonde" }, + { "kea", "Kabuverdianu" }, + { "ken", "Kenyang" }, + { "kfo", "Koro" }, + { "kg", "Kongo" }, + { "kgp", "Kaingang" }, + { "kha", "Khasi" }, + { "kho", "Khotanese" }, + { "khq", "Koyra Chiini" }, + { "khw", "Khowar" }, + { "ki", "Kikuyu" }, + { "kiu", "Kirmanjki" }, + { "kj", "Kuanyama" }, + { "kk", "Kazakh" }, + { "kkj", "Kako" }, + { "kl", "Kalaallisut" }, + { "kln", "Kalenjin" }, + { "km", "Central Khmer" }, + { "kmb", "Kimbundu" }, + { "kn", "Kannada" }, + { "ko", "Korean" }, + { "koi", "Komi-Permyak" }, + { "kok", "Konkani" }, + { "kos", "Kosraean" }, + { "kpe", "Kpelle" }, + { "kr", "Kanuri" }, + { "krc", "Karachay-Balkar" }, + { "kri", "Krio" }, + { "krj", "Kinaray-a" }, + { "krl", "Karelian" }, + { "kru", "Kurukh" }, + { "ks", "Kashmiri" }, + { "ksb", "Shambala" }, + { "ksf", "Bafia" }, + { "ksh", "Colognian" }, + { "ku", "Kurdish" }, + { "kum", "Kumyk" }, + { "kut", "Kutenai" }, + { "kv", "Komi" }, + { "kw", "Cornish" }, + { "ky", "Kirghiz" }, + { "lag", "Langi" }, + { "la", "Latin" }, + { "lad", "Ladino" }, + { "lag", "Langi" }, + { "lah", "Lahnda" }, + { "lam", "Lamba" }, + { "lb", "Luxembourgish" }, + { "lez", "Lezghian" }, + { "lfn", "Lingua Franca Nova" }, + { "lg", "Ganda" }, + { "li", "Limburgan" }, + { "lij", "Ligurian" }, + { "liv", "Livonian" }, + { "lkt", "Lakota" }, + { "lmo", "Lombard" }, + { "ln", "Lingala" }, + { "lo", "Lao" }, + { "lol", "Mongo" }, + { "lou", "Louisiana Creole" }, + { "loz", "Lozi" }, + { "lrc", "Northern Luri" }, + { "lt", "Lithuanian" }, + { "ltg", "Latgalian" }, + { "lu", "Luba-Katanga" }, + { "lua", "Luba-Lulua" }, + { "lui", "Luiseno" }, + { "lun", "Lunda" }, + { "luo", "Luo" }, + { "lus", "Mizo" }, + { "luy", "Luyia" }, + { "lv", "Latvian" }, + { "lzh", "Literary Chinese" }, + { "lzz", "Laz" }, + { "mad", "Madurese" }, + { "maf", "Mafa" }, + { "mag", "Magahi" }, + { "mai", "Maithili" }, + { "mak", "Makasar" }, + { "man", "Mandingo" }, + { "mas", "Masai" }, + { "mde", "Maba" }, + { "mdf", "Moksha" }, + { "mdr", "Mandar" }, + { "men", "Mende" }, + { "mer", "Meru" }, + { "mfe", "Morisyen" }, + { "mg", "Malagasy" }, + { "mga", "Middle Irish" }, + { "mgh", "Makhuwa-Meetto" }, + { "mgo", "Metaʼ" }, + { "mh", "Marshallese" }, + { "mhr", "Eastern Mari" }, + { "mi", "Māori" }, + { "mic", "Mi'kmaq" }, + { "min", "Minangkabau" }, + { "miq", "Mískito" }, + { "mjw", "Karbi" }, + { "mk", "Macedonian" }, + { "ml", "Malayalam" }, + { "mn", "Mongolian" }, + { "mnc", "Manchu" }, + { "mni", "Manipuri" }, + { "mnw", "Mon" }, + { "mos", "Mossi" }, + { "moh", "Mohawk" }, + { "mr", "Marathi" }, + { "mrj", "Western Mari" }, + { "ms", "Malay" }, + { "mt", "Maltese" }, + { "mua", "Mundang" }, + { "mus", "Muscogee" }, + { "mwl", "Mirandese" }, + { "mwr", "Marwari" }, + { "mwv", "Mentawai" }, + { "my", "Burmese" }, + { "mye", "Myene" }, + { "myv", "Erzya" }, + { "mzn", "Mazanderani" }, + { "na", "Nauru" }, + { "nah", "Nahuatl" }, + { "nan", "Min Nan Chinese" }, + { "nap", "Neapolitan" }, + { "naq", "Nama" }, + { "nan", "Min Nan Chinese" }, + { "nb", "Norwegian Bokmål" }, + { "nd", "North Ndebele" }, + { "nds", "Low German" }, + { "ne", "Nepali" }, + { "new", "Newari" }, + { "nhn", "Central Nahuatl" }, + { "ng", "Ndonga" }, + { "nia", "Nias" }, + { "niu", "Niuean" }, + { "njo", "Ao Naga" }, + { "nl", "Dutch" }, + { "nmg", "Kwasio" }, + { "nn", "Norwegian Nynorsk" }, + { "nnh", "Ngiemboon" }, + { "nog", "Nogai" }, + { "non", "Old Norse" }, + { "nov", "Novial" }, + { "nqo", "N'ko" }, + { "nr", "South Ndebele" }, + { "nso", "Pedi" }, + { "nus", "Nuer" }, + { "nv", "Navajo" }, + { "nwc", "Classical Newari" }, + { "ny", "Nyanja" }, + { "nym", "Nyamwezi" }, + { "nyn", "Nyankole" }, + { "nyo", "Nyoro" }, + { "nzi", "Nzima" }, + { "oc", "Occitan" }, + { "oj", "Ojibwa" }, + { "om", "Oromo" }, + { "or", "Odia" }, + { "os", "Ossetic" }, + { "osa", "Osage" }, + { "ota", "Ottoman Turkish" }, + { "pa", "Panjabi" }, + { "pag", "Pangasinan" }, + { "pal", "Pahlavi" }, + { "pam", "Pampanga" }, + { "pap", "Papiamento" }, + { "pau", "Palauan" }, + { "pcd", "Picard" }, + { "pcm", "Nigerian Pidgin" }, + { "pdc", "Pennsylvania German" }, + { "pdt", "Plautdietsch" }, + { "peo", "Old Persian" }, + { "pfl", "Palatine German" }, + { "phn", "Phoenician" }, + { "pi", "Pali" }, + { "pl", "Polish" }, + { "pms", "Piedmontese" }, + { "pnt", "Pontic" }, + { "pon", "Pohnpeian" }, + { "pr", "Pirate" }, + { "prg", "Prussian" }, + { "pro", "Old Provençal" }, + { "prs", "Dari" }, + { "ps", "Pushto" }, + { "pt", "Portuguese" }, + { "qu", "Quechua" }, + { "quc", "K'iche" }, + { "qug", "Chimborazo Highland Quichua" }, + { "quy", "Ayacucho Quechua" }, + { "quz", "Cusco Quechua" }, + { "raj", "Rajasthani" }, + { "rap", "Rapanui" }, + { "rar", "Rarotongan" }, + { "rgn", "Romagnol" }, + { "rif", "Riffian" }, + { "rm", "Romansh" }, + { "rn", "Rundi" }, + { "ro", "Romanian" }, + { "rof", "Rombo" }, + { "rom", "Romany" }, + { "rtm", "Rotuman" }, + { "ru", "Russian" }, + { "rue", "Rusyn" }, + { "rug", "Roviana" }, + { "rup", "Aromanian" }, + { "rw", "Kinyarwanda" }, + { "rwk", "Rwa" }, + { "sa", "Sanskrit" }, + { "sad", "Sandawe" }, + { "sah", "Sakha" }, + { "sam", "Samaritan Aramaic" }, + { "saq", "Samburu" }, + { "sas", "Sasak" }, + { "sat", "Santali" }, + { "saz", "Saurashtra" }, + { "sba", "Ngambay" }, + { "sbp", "Sangu" }, + { "sc", "Sardinian" }, + { "scn", "Sicilian" }, + { "sco", "Scots" }, + { "sd", "Sindhi" }, + { "sdc", "Sassarese Sardinian" }, + { "sdh", "Southern Kurdish" }, + { "se", "Northern Sami" }, + { "see", "Seneca" }, + { "seh", "Sena" }, + { "sei", "Seri" }, + { "sel", "Selkup" }, + { "ses", "Koyraboro Senni" }, + { "sg", "Sango" }, + { "sga", "Old Irish" }, + { "sgs", "Samogitian" }, + { "sh", "Serbo-Croatian" }, + { "shi", "Tachelhit" }, + { "shn", "Shan" }, + { "shs", "Shuswap" }, + { "shu", "Chadian Arabic" }, + { "si", "Sinhala" }, + { "sid", "Sidamo" }, + { "sk", "Slovak" }, + { "sl", "Slovenian" }, + { "sli", "Lower Silesian" }, + { "sly", "Selayar" }, + { "sm", "Samoan" }, + { "sma", "Southern Sami" }, + { "smj", "Lule Sami" }, + { "smn", "Inari Sami" }, + { "sms", "Skolt Sami" }, + { "sn", "Shona" }, + { "snk", "Soninke" }, + { "so", "Somali" }, + { "sog", "Sogdien" }, + { "son", "Songhai" }, + { "sq", "Albanian" }, + { "sr", "Serbian" }, + { "srn", "Sranan Tongo" }, + { "srr", "Serer" }, + { "ss", "Swati" }, + { "ssy", "Saho" }, + { "st", "Southern Sotho" }, + { "stq", "Saterland Frisian" }, + { "su", "Sundanese" }, + { "suk", "Sukuma" }, + { "sus", "Susu" }, + { "sux", "Sumerian" }, + { "sv", "Swedish" }, + { "sw", "Swahili" }, + { "swb", "Comorian" }, + { "swc", "Congo Swahili" }, + { "syc", "Classical Syriac" }, + { "syr", "Syriac" }, + { "szl", "Silesian" }, + { "ta", "Tamil" }, + { "tcy", "Tulu" }, + { "te", "Telugu" }, + { "tem", "Timne" }, + { "teo", "Teso" }, + { "ter", "Tereno" }, + { "tet", "Tetum" }, + { "tg", "Tajik" }, + { "th", "Thai" }, + { "the", "Chitwania Tharu" }, + { "ti", "Tigrinya" }, + { "tig", "Tigre" }, + { "tiv", "Tiv" }, + { "tk", "Turkmen" }, + { "tkl", "Tokelau" }, + { "tkr", "Tsakhur" }, + { "tl", "Tagalog" }, + { "tlh", "Klingon" }, + { "tli", "Tlingit" }, + { "tly", "Talysh" }, + { "tmh", "Tamashek" }, + { "tn", "Tswana" }, + { "to", "Tongan" }, + { "tog", "Nyasa Tonga" }, + { "tpi", "Tok Pisin" }, + { "tr", "Turkish" }, + { "tru", "Turoyo" }, + { "trv", "Taroko" }, + { "ts", "Tsonga" }, + { "tsd", "Tsakonian" }, + { "tsi", "Tsimshian" }, + { "tt", "Tatar" }, + { "ttt", "Muslim Tat" }, + { "tum", "Tumbuka" }, + { "tvl", "Tuvalu" }, + { "tw", "Twi" }, + { "twq", "Tasawaq" }, + { "ty", "Tahitian" }, + { "tyv", "Tuvinian" }, + { "tzm", "Central Atlas Tamazight" }, + { "udm", "Udmurt" }, + { "ug", "Uyghur" }, + { "uga", "Ugaritic" }, + { "uk", "Ukrainian" }, + { "umb", "Umbundu" }, + { "unm", "Unami" }, + { "ur", "Urdu" }, + { "uz", "Uzbek" }, + { "vai", "Vai" }, + { "ve", "Venda" }, + { "vec", "Venetian" }, + { "vep", "Veps" }, + { "vi", "Vietnamese" }, + { "vls", "West Flemish" }, + { "vmf", "Main-Franconian" }, + { "vo", "Volapük" }, + { "vot", "Votic" }, + { "vro", "Võro" }, + { "vun", "Vunjo" }, + { "wa", "Walloon" }, + { "wae", "Walser" }, + { "wal", "Wolaytta" }, + { "war", "Waray" }, + { "was", "Washo" }, + { "wbp", "Warlpiri" }, + { "wo", "Wolof" }, + { "wuu", "Wu Chinese" }, + { "xal", "Kalmyk" }, + { "xh", "Xhosa" }, + { "xmf", "Mingrelian" }, + { "xog", "Soga" }, + { "yao", "Yao" }, + { "yap", "Yapese" }, + { "yav", "Yangben" }, + { "ybb", "Yemba" }, + { "yi", "Yiddish" }, + { "yo", "Yoruba" }, + { "yrl", "Nheengatu" }, + { "yue", "Yue Chinese" }, + { "yuw", "Papua New Guinea" }, + { "za", "Zhuang" }, + { "zap", "Zapotec" }, + { "zbl", "Blissymbols" }, + { "zea", "Zeelandic" }, + { "zen", "Zenaga" }, + { "zgh", "Standard Moroccan Tamazight" }, + { "zh", "Chinese" }, + { "zu", "Zulu" }, + { "zun", "Zuni" }, + { "zza", "Zaza" }, + { nullptr, nullptr } +}; + +// Additional regional variants. +// Variant name, supported languages. + +static const char *locale_variants[][2] = { + { "valencia", "ca" }, + { "iqtelif", "tt" }, + { "saaho", "aa" }, + { "tradnl", "es" }, + { nullptr, nullptr }, +}; + +// Script names and codes (excludes typographic variants, special codes, reserved codes and aliases for combined scripts). +// Reference: +// - https://en.wikipedia.org/wiki/ISO_15924 + +static const char *script_list[][2] = { + { "Adlam", "Adlm" }, + { "Afaka", "Afak" }, + { "Caucasian Albanian", "Aghb" }, + { "Ahom", "Ahom" }, + { "Arabic", "Arab" }, + { "Imperial Aramaic", "Armi" }, + { "Armenian", "Armn" }, + { "Avestan", "Avst" }, + { "Balinese", "Bali" }, + { "Bamum", "Bamu" }, + { "Bassa Vah", "Bass" }, + { "Batak", "Batk" }, + { "Bengali", "Beng" }, + { "Bhaiksuki", "Bhks" }, + { "Blissymbols", "Blis" }, + { "Bopomofo", "Bopo" }, + { "Brahmi", "Brah" }, + { "Braille", "Brai" }, + { "Buginese", "Bugi" }, + { "Buhid", "Buhd" }, + { "Chakma", "Cakm" }, + { "Unified Canadian Aboriginal", "Cans" }, + { "Carian", "Cari" }, + { "Cham", "Cham" }, + { "Cherokee", "Cher" }, + { "Chorasmian", "Chrs" }, + { "Cirth", "Cirt" }, + { "Coptic", "Copt" }, + { "Cypro-Minoan", "Cpmn" }, + { "Cypriot", "Cprt" }, + { "Cyrillic", "Cyrl" }, + { "Devanagari", "Deva" }, + { "Dives Akuru", "Diak" }, + { "Dogra", "Dogr" }, + { "Deseret", "Dsrt" }, + { "Duployan", "Dupl" }, + { "Egyptian demotic", "Egyd" }, + { "Egyptian hieratic", "Egyh" }, + { "Egyptian hieroglyphs", "Egyp" }, + { "Elbasan", "Elba" }, + { "Elymaic", "Elym" }, + { "Ethiopic", "Ethi" }, + { "Khutsuri", "Geok" }, + { "Georgian", "Geor" }, + { "Glagolitic", "Glag" }, + { "Gunjala Gondi", "Gong" }, + { "Masaram Gondi", "Gonm" }, + { "Gothic", "Goth" }, + { "Grantha", "Gran" }, + { "Greek", "Grek" }, + { "Gujarati", "Gujr" }, + { "Gurmukhi", "Guru" }, + { "Hangul", "Hang" }, + { "Han", "Hani" }, + { "Hanunoo", "Hano" }, + { "Simplified", "Hans" }, + { "Traditional", "Hant" }, + { "Hatran", "Hatr" }, + { "Hebrew", "Hebr" }, + { "Hiragana", "Hira" }, + { "Anatolian Hieroglyphs", "Hluw" }, + { "Pahawh Hmong", "Hmng" }, + { "Nyiakeng Puachue Hmong", "Hmnp" }, + { "Old Hungarian", "Hung" }, + { "Indus", "Inds" }, + { "Old Italic", "Ital" }, + { "Javanese", "Java" }, + { "Jurchen", "Jurc" }, + { "Kayah Li", "Kali" }, + { "Katakana", "Kana" }, + { "Kharoshthi", "Khar" }, + { "Khmer", "Khmr" }, + { "Khojki", "Khoj" }, + { "Khitan large script", "Kitl" }, + { "Khitan small script", "Kits" }, + { "Kannada", "Knda" }, + { "Kpelle", "Kpel" }, + { "Kaithi", "Kthi" }, + { "Tai Tham", "Lana" }, + { "Lao", "Laoo" }, + { "Latin", "Latn" }, + { "Leke", "Leke" }, + { "Lepcha", "Lepc" }, + { "Limbu", "Limb" }, + { "Linear A", "Lina" }, + { "Linear B", "Linb" }, + { "Lisu", "Lisu" }, + { "Loma", "Loma" }, + { "Lycian", "Lyci" }, + { "Lydian", "Lydi" }, + { "Mahajani", "Mahj" }, + { "Makasar", "Maka" }, + { "Mandaic", "Mand" }, + { "Manichaean", "Mani" }, + { "Marchen", "Marc" }, + { "Mayan Hieroglyphs", "Maya" }, + { "Medefaidrin", "Medf" }, + { "Mende Kikakui", "Mend" }, + { "Meroitic Cursive", "Merc" }, + { "Meroitic Hieroglyphs", "Mero" }, + { "Malayalam", "Mlym" }, + { "Modi", "Modi" }, + { "Mongolian", "Mong" }, + { "Moon", "Moon" }, + { "Mro", "Mroo" }, + { "Meitei Mayek", "Mtei" }, + { "Multani", "Mult" }, + { "Myanmar (Burmese)", "Mymr" }, + { "Nandinagari", "Nand" }, + { "Old North Arabian", "Narb" }, + { "Nabataean", "Nbat" }, + { "Newa", "Newa" }, + { "Naxi Dongba", "Nkdb" }, + { "Nakhi Geba", "Nkgb" }, + { "N'ko", "Nkoo" }, + { "Nüshu", "Nshu" }, + { "Ogham", "Ogam" }, + { "Ol Chiki", "Olck" }, + { "Old Turkic", "Orkh" }, + { "Oriya", "Orya" }, + { "Osage", "Osge" }, + { "Osmanya", "Osma" }, + { "Old Uyghur", "Ougr" }, + { "Palmyrene", "Palm" }, + { "Pau Cin Hau", "Pauc" }, + { "Proto-Cuneiform", "Pcun" }, + { "Proto-Elamite", "Pelm" }, + { "Old Permic", "Perm" }, + { "Phags-pa", "Phag" }, + { "Inscriptional Pahlavi", "Phli" }, + { "Psalter Pahlavi", "Phlp" }, + { "Book Pahlavi", "Phlv" }, + { "Phoenician", "Phnx" }, + { "Klingon", "Piqd" }, + { "Miao", "Plrd" }, + { "Inscriptional Parthian", "Prti" }, + { "Proto-Sinaitic", "Psin" }, + { "Ranjana", "Ranj" }, + { "Rejang", "Rjng" }, + { "Hanifi Rohingya", "Rohg" }, + { "Rongorongo", "Roro" }, + { "Runic", "Runr" }, + { "Samaritan", "Samr" }, + { "Sarati", "Sara" }, + { "Old South Arabian", "Sarb" }, + { "Saurashtra", "Saur" }, + { "SignWriting", "Sgnw" }, + { "Shavian", "Shaw" }, + { "Sharada", "Shrd" }, + { "Shuishu", "Shui" }, + { "Siddham", "Sidd" }, + { "Khudawadi", "Sind" }, + { "Sinhala", "Sinh" }, + { "Sogdian", "Sogd" }, + { "Old Sogdian", "Sogo" }, + { "Sora Sompeng", "Sora" }, + { "Soyombo", "Soyo" }, + { "Sundanese", "Sund" }, + { "Syloti Nagri", "Sylo" }, + { "Syriac", "Syrc" }, + { "Tagbanwa", "Tagb" }, + { "Takri", "Takr" }, + { "Tai Le", "Tale" }, + { "New Tai Lue", "Talu" }, + { "Tamil", "Taml" }, + { "Tangut", "Tang" }, + { "Tai Viet", "Tavt" }, + { "Telugu", "Telu" }, + { "Tengwar", "Teng" }, + { "Tifinagh", "Tfng" }, + { "Tagalog", "Tglg" }, + { "Thaana", "Thaa" }, + { "Thai", "Thai" }, + { "Tibetan", "Tibt" }, + { "Tirhuta", "Tirh" }, + { "Tangsa", "Tnsa" }, + { "Toto", "Toto" }, + { "Ugaritic", "Ugar" }, + { "Vai", "Vaii" }, + { "Visible Speech", "Visp" }, + { "Vithkuqi", "Vith" }, + { "Warang Citi", "Wara" }, + { "Wancho", "Wcho" }, + { "Woleai", "Wole" }, + { "Old Persian", "Xpeo" }, + { "Cuneiform", "Xsux" }, + { "Yezidi", "Yezi" }, + { "Yi", "Yiii" }, + { "Zanabazar Square", "Zanb" }, + { nullptr, nullptr } +}; + +#endif // LOCALES_H diff --git a/core/string/translation.cpp b/core/string/translation.cpp index 615ecf2432..5d7e583de1 100644 --- a/core/string/translation.cpp +++ b/core/string/translation.cpp @@ -33,793 +33,12 @@ #include "core/config/project_settings.h" #include "core/io/resource_loader.h" #include "core/os/os.h" +#include "core/string/locales.h" #ifdef TOOLS_ENABLED #include "main/main.h" #endif -// ISO 639-1 language codes (and a couple of three-letter ISO 639-2 codes), -// with the addition of glibc locales with their regional identifiers. -// This list must match the language names (in English) of locale_names. -// -// References: -// - https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes -// - https://lh.2xlibre.net/locales/ -// - https://iso639-3.sil.org/ - -static const char *locale_list[] = { - "aa", // Afar - "aa_DJ", // Afar (Djibouti) - "aa_ER", // Afar (Eritrea) - "aa_ET", // Afar (Ethiopia) - "af", // Afrikaans - "af_ZA", // Afrikaans (South Africa) - "agr_PE", // Aguaruna (Peru) - "ak_GH", // Akan (Ghana) - "am_ET", // Amharic (Ethiopia) - "an_ES", // Aragonese (Spain) - "anp_IN", // Angika (India) - "ar", // Arabic - "ar_AE", // Arabic (United Arab Emirates) - "ar_BH", // Arabic (Bahrain) - "ar_DZ", // Arabic (Algeria) - "ar_EG", // Arabic (Egypt) - "ar_IN", // Arabic (India) - "ar_IQ", // Arabic (Iraq) - "ar_JO", // Arabic (Jordan) - "ar_KW", // Arabic (Kuwait) - "ar_LB", // Arabic (Lebanon) - "ar_LY", // Arabic (Libya) - "ar_MA", // Arabic (Morocco) - "ar_OM", // Arabic (Oman) - "ar_QA", // Arabic (Qatar) - "ar_SA", // Arabic (Saudi Arabia) - "ar_SD", // Arabic (Sudan) - "ar_SS", // Arabic (South Soudan) - "ar_SY", // Arabic (Syria) - "ar_TN", // Arabic (Tunisia) - "ar_YE", // Arabic (Yemen) - "as_IN", // Assamese (India) - "ast_ES", // Asturian (Spain) - "ayc_PE", // Southern Aymara (Peru) - "ay_PE", // Aymara (Peru) - "az", // Azerbaijani - "az_AZ", // Azerbaijani (Azerbaijan) - "be", // Belarusian - "be_BY", // Belarusian (Belarus) - "bem_ZM", // Bemba (Zambia) - "ber_DZ", // Berber languages (Algeria) - "ber_MA", // Berber languages (Morocco) - "bg", // Bulgarian - "bg_BG", // Bulgarian (Bulgaria) - "bhb_IN", // Bhili (India) - "bho_IN", // Bhojpuri (India) - "bi_TV", // Bislama (Tuvalu) - "bn", // Bengali - "bn_BD", // Bengali (Bangladesh) - "bn_IN", // Bengali (India) - "bo", // Tibetan - "bo_CN", // Tibetan (China) - "bo_IN", // Tibetan (India) - "br", // Breton - "br_FR", // Breton (France) - "brx_IN", // Bodo (India) - "bs_BA", // Bosnian (Bosnia and Herzegovina) - "byn_ER", // Bilin (Eritrea) - "ca", // Catalan - "ca_AD", // Catalan (Andorra) - "ca_ES", // Catalan (Spain) - "ca_FR", // Catalan (France) - "ca_IT", // Catalan (Italy) - "ce_RU", // Chechen (Russia) - "chr_US", // Cherokee (United States) - "cmn_TW", // Mandarin Chinese (Taiwan) - "crh_UA", // Crimean Tatar (Ukraine) - "csb_PL", // Kashubian (Poland) - "cs", // Czech - "cs_CZ", // Czech (Czech Republic) - "cv_RU", // Chuvash (Russia) - "cy_GB", // Welsh (United Kingdom) - "da", // Danish - "da_DK", // Danish (Denmark) - "de", // German - "de_AT", // German (Austria) - "de_BE", // German (Belgium) - "de_CH", // German (Switzerland) - "de_DE", // German (Germany) - "de_IT", // German (Italy) - "de_LU", // German (Luxembourg) - "doi_IN", // Dogri (India) - "dv_MV", // Dhivehi (Maldives) - "dz_BT", // Dzongkha (Bhutan) - "el", // Greek - "el_CY", // Greek (Cyprus) - "el_GR", // Greek (Greece) - "en", // English - "en_AG", // English (Antigua and Barbuda) - "en_AU", // English (Australia) - "en_BW", // English (Botswana) - "en_CA", // English (Canada) - "en_DK", // English (Denmark) - "en_GB", // English (United Kingdom) - "en_HK", // English (Hong Kong) - "en_IE", // English (Ireland) - "en_IL", // English (Israel) - "en_IN", // English (India) - "en_NG", // English (Nigeria) - "en_NZ", // English (New Zealand) - "en_PH", // English (Philippines) - "en_SG", // English (Singapore) - "en_US", // English (United States) - "en_ZA", // English (South Africa) - "en_ZM", // English (Zambia) - "en_ZW", // English (Zimbabwe) - "eo", // Esperanto - "es", // Spanish - "es_AR", // Spanish (Argentina) - "es_BO", // Spanish (Bolivia) - "es_CL", // Spanish (Chile) - "es_CO", // Spanish (Colombia) - "es_CR", // Spanish (Costa Rica) - "es_CU", // Spanish (Cuba) - "es_DO", // Spanish (Dominican Republic) - "es_EC", // Spanish (Ecuador) - "es_ES", // Spanish (Spain) - "es_GT", // Spanish (Guatemala) - "es_HN", // Spanish (Honduras) - "es_MX", // Spanish (Mexico) - "es_NI", // Spanish (Nicaragua) - "es_PA", // Spanish (Panama) - "es_PE", // Spanish (Peru) - "es_PR", // Spanish (Puerto Rico) - "es_PY", // Spanish (Paraguay) - "es_SV", // Spanish (El Salvador) - "es_US", // Spanish (United States) - "es_UY", // Spanish (Uruguay) - "es_VE", // Spanish (Venezuela) - "et", // Estonian - "et_EE", // Estonian (Estonia) - "eu", // Basque - "eu_ES", // Basque (Spain) - "fa", // Persian - "fa_IR", // Persian (Iran) - "ff_SN", // Fulah (Senegal) - "fi", // Finnish - "fi_FI", // Finnish (Finland) - "fil", // Filipino - "fil_PH", // Filipino (Philippines) - "fo_FO", // Faroese (Faroe Islands) - "fr", // French - "fr_BE", // French (Belgium) - "fr_CA", // French (Canada) - "fr_CH", // French (Switzerland) - "fr_FR", // French (France) - "fr_LU", // French (Luxembourg) - "fur_IT", // Friulian (Italy) - "fy_DE", // Western Frisian (Germany) - "fy_NL", // Western Frisian (Netherlands) - "ga", // Irish - "ga_IE", // Irish (Ireland) - "gd_GB", // Scottish Gaelic (United Kingdom) - "gez_ER", // Geez (Eritrea) - "gez_ET", // Geez (Ethiopia) - "gl", // Galician - "gl_ES", // Galician (Spain) - "gu_IN", // Gujarati (India) - "gv_GB", // Manx (United Kingdom) - "hak_TW", // Hakka Chinese (Taiwan) - "ha_NG", // Hausa (Nigeria) - "he", // Hebrew - "he_IL", // Hebrew (Israel) - "hi", // Hindi - "hi_IN", // Hindi (India) - "hne_IN", // Chhattisgarhi (India) - "hr", // Croatian - "hr_HR", // Croatian (Croatia) - "hsb_DE", // Upper Sorbian (Germany) - "ht_HT", // Haitian (Haiti) - "hu", // Hungarian - "hu_HU", // Hungarian (Hungary) - "hus_MX", // Huastec (Mexico) - "hy_AM", // Armenian (Armenia) - "ia_FR", // Interlingua (France) - "id", // Indonesian - "id_ID", // Indonesian (Indonesia) - "ig_NG", // Igbo (Nigeria) - "ik_CA", // Inupiaq (Canada) - "is", // Icelandic - "is_IS", // Icelandic (Iceland) - "it", // Italian - "it_CH", // Italian (Switzerland) - "it_IT", // Italian (Italy) - "iu_CA", // Inuktitut (Canada) - "ja", // Japanese - "ja_JP", // Japanese (Japan) - "kab_DZ", // Kabyle (Algeria) - "ka", // Georgian - "ka_GE", // Georgian (Georgia) - "kk_KZ", // Kazakh (Kazakhstan) - "kl_GL", // Kalaallisut (Greenland) - "km", // Central Khmer - "km_KH", // Central Khmer (Cambodia) - "kn_IN", // Kannada (India) - "kok_IN", // Konkani (India) - "ko", // Korean - "ko_KR", // Korean (South Korea) - "ks_IN", // Kashmiri (India) - "ku", // Kurdish - "ku_TR", // Kurdish (Turkey) - "kw_GB", // Cornish (United Kingdom) - "ky_KG", // Kirghiz (Kyrgyzstan) - "lb_LU", // Luxembourgish (Luxembourg) - "lg_UG", // Ganda (Uganda) - "li_BE", // Limburgan (Belgium) - "li_NL", // Limburgan (Netherlands) - "lij_IT", // Ligurian (Italy) - "ln_CD", // Lingala (Congo) - "lo_LA", // Lao (Laos) - "lt", // Lithuanian - "lt_LT", // Lithuanian (Lithuania) - "lv", // Latvian - "lv_LV", // Latvian (Latvia) - "lzh_TW", // Literary Chinese (Taiwan) - "mag_IN", // Magahi (India) - "mai_IN", // Maithili (India) - "mg_MG", // Malagasy (Madagascar) - "mh_MH", // Marshallese (Marshall Islands) - "mhr_RU", // Eastern Mari (Russia) - "mi", // Māori - "mi_NZ", // Māori (New Zealand) - "miq_NI", // Mískito (Nicaragua) - "mk", // Macedonian - "mk_MK", // Macedonian (Macedonia) - "ml", // Malayalam - "ml_IN", // Malayalam (India) - "mni_IN", // Manipuri (India) - "mn_MN", // Mongolian (Mongolia) - "mr", // Marathi - "mr_IN", // Marathi (India) - "ms", // Malay - "ms_MY", // Malay (Malaysia) - "mt", // Maltese - "mt_MT", // Maltese (Malta) - "my_MM", // Burmese (Myanmar) - "myv_RU", // Erzya (Russia) - "nah_MX", // Nahuatl languages (Mexico) - "nan_TW", // Min Nan Chinese (Taiwan) - "nb", // Norwegian Bokmål - "nb_NO", // Norwegian Bokmål (Norway) - "nds_DE", // Low German (Germany) - "nds_NL", // Low German (Netherlands) - "ne_NP", // Nepali (Nepal) - "nhn_MX", // Central Nahuatl (Mexico) - "niu_NU", // Niuean (Niue) - "niu_NZ", // Niuean (New Zealand) - "nl", // Dutch - "nl_AW", // Dutch (Aruba) - "nl_BE", // Dutch (Belgium) - "nl_NL", // Dutch (Netherlands) - "nn", // Norwegian Nynorsk - "nn_NO", // Norwegian Nynorsk (Norway) - "nr_ZA", // South Ndebele (South Africa) - "nso_ZA", // Pedi (South Africa) - "oc_FR", // Occitan (France) - "om", // Oromo - "om_ET", // Oromo (Ethiopia) - "om_KE", // Oromo (Kenya) - "or", // Oriya - "or_IN", // Oriya (India) - "os_RU", // Ossetian (Russia) - "pa_IN", // Panjabi (India) - "pap", // Papiamento - "pap_AN", // Papiamento (Netherlands Antilles) - "pap_AW", // Papiamento (Aruba) - "pap_CW", // Papiamento (Curaçao) - "pa_PK", // Panjabi (Pakistan) - "pl", // Polish - "pl_PL", // Polish (Poland) - "pr", // Pirate - "ps_AF", // Pushto (Afghanistan) - "pt", // Portuguese - "pt_BR", // Portuguese (Brazil) - "pt_PT", // Portuguese (Portugal) - "quy_PE", // Ayacucho Quechua (Peru) - "quz_PE", // Cusco Quechua (Peru) - "raj_IN", // Rajasthani (India) - "ro", // Romanian - "ro_RO", // Romanian (Romania) - "ru", // Russian - "ru_RU", // Russian (Russia) - "ru_UA", // Russian (Ukraine) - "rw_RW", // Kinyarwanda (Rwanda) - "sa_IN", // Sanskrit (India) - "sat_IN", // Santali (India) - "sc_IT", // Sardinian (Italy) - "sco", // Scots - "sd_IN", // Sindhi (India) - "se_NO", // Northern Sami (Norway) - "sgs_LT", // Samogitian (Lithuania) - "shs_CA", // Shuswap (Canada) - "sid_ET", // Sidamo (Ethiopia) - "si", // Sinhala - "si_LK", // Sinhala (Sri Lanka) - "sk", // Slovak - "sk_SK", // Slovak (Slovakia) - "sl", // Slovenian - "sl_SI", // Slovenian (Slovenia) - "so", // Somali - "so_DJ", // Somali (Djibouti) - "so_ET", // Somali (Ethiopia) - "so_KE", // Somali (Kenya) - "so_SO", // Somali (Somalia) - "son_ML", // Songhai languages (Mali) - "sq", // Albanian - "sq_AL", // Albanian (Albania) - "sq_KV", // Albanian (Kosovo) - "sq_MK", // Albanian (Macedonia) - "sr", // Serbian - "sr_Cyrl", // Serbian (Cyrillic) - "sr_Latn", // Serbian (Latin) - "sr_ME", // Serbian (Montenegro) - "sr_RS", // Serbian (Serbia) - "ss_ZA", // Swati (South Africa) - "st_ZA", // Southern Sotho (South Africa) - "sv", // Swedish - "sv_FI", // Swedish (Finland) - "sv_SE", // Swedish (Sweden) - "sw_KE", // Swahili (Kenya) - "sw_TZ", // Swahili (Tanzania) - "szl_PL", // Silesian (Poland) - "ta", // Tamil - "ta_IN", // Tamil (India) - "ta_LK", // Tamil (Sri Lanka) - "tcy_IN", // Tulu (India) - "te", // Telugu - "te_IN", // Telugu (India) - "tg_TJ", // Tajik (Tajikistan) - "the_NP", // Chitwania Tharu (Nepal) - "th", // Thai - "th_TH", // Thai (Thailand) - "ti", // Tigrinya - "ti_ER", // Tigrinya (Eritrea) - "ti_ET", // Tigrinya (Ethiopia) - "tig_ER", // Tigre (Eritrea) - "tk_TM", // Turkmen (Turkmenistan) - "tl_PH", // Tagalog (Philippines) - "tn_ZA", // Tswana (South Africa) - "tr", // Turkish - "tr_CY", // Turkish (Cyprus) - "tr_TR", // Turkish (Turkey) - "ts_ZA", // Tsonga (South Africa) - "tt", // Tatar - "tt_RU", // Tatar (Russia) - "tzm", // Central Atlas Tamazight - "tzm_MA", // Central Atlas Tamazight (Marrocos) - "ug_CN", // Uighur (China) - "uk", // Ukrainian - "uk_UA", // Ukrainian (Ukraine) - "unm_US", // Unami (United States) - "ur", // Urdu - "ur_IN", // Urdu (India) - "ur_PK", // Urdu (Pakistan) - "uz", // Uzbek - "uz_UZ", // Uzbek (Uzbekistan) - "ve_ZA", // Venda (South Africa) - "vi", // Vietnamese - "vi_VN", // Vietnamese (Vietnam) - "wa_BE", // Walloon (Belgium) - "wae_CH", // Walser (Switzerland) - "wal_ET", // Wolaytta (Ethiopia) - "wo_SN", // Wolof (Senegal) - "xh_ZA", // Xhosa (South Africa) - "yi_US", // Yiddish (United States) - "yo_NG", // Yoruba (Nigeria) - "yue_HK", // Yue Chinese (Hong Kong) - "zh", // Chinese - "zh_CN", // Chinese (China) - "zh_HK", // Chinese (Hong Kong) - "zh_SG", // Chinese (Singapore) - "zh_TW", // Chinese (Taiwan) - "zu_ZA", // Zulu (South Africa) - nullptr -}; - -static const char *locale_names[] = { - "Afar", - "Afar (Djibouti)", - "Afar (Eritrea)", - "Afar (Ethiopia)", - "Afrikaans", - "Afrikaans (South Africa)", - "Aguaruna (Peru)", - "Akan (Ghana)", - "Amharic (Ethiopia)", - "Aragonese (Spain)", - "Angika (India)", - "Arabic", - "Arabic (United Arab Emirates)", - "Arabic (Bahrain)", - "Arabic (Algeria)", - "Arabic (Egypt)", - "Arabic (India)", - "Arabic (Iraq)", - "Arabic (Jordan)", - "Arabic (Kuwait)", - "Arabic (Lebanon)", - "Arabic (Libya)", - "Arabic (Morocco)", - "Arabic (Oman)", - "Arabic (Qatar)", - "Arabic (Saudi Arabia)", - "Arabic (Sudan)", - "Arabic (South Soudan)", - "Arabic (Syria)", - "Arabic (Tunisia)", - "Arabic (Yemen)", - "Assamese (India)", - "Asturian (Spain)", - "Southern Aymara (Peru)", - "Aymara (Peru)", - "Azerbaijani", - "Azerbaijani (Azerbaijan)", - "Belarusian", - "Belarusian (Belarus)", - "Bemba (Zambia)", - "Berber languages (Algeria)", - "Berber languages (Morocco)", - "Bulgarian", - "Bulgarian (Bulgaria)", - "Bhili (India)", - "Bhojpuri (India)", - "Bislama (Tuvalu)", - "Bengali", - "Bengali (Bangladesh)", - "Bengali (India)", - "Tibetan", - "Tibetan (China)", - "Tibetan (India)", - "Breton", - "Breton (France)", - "Bodo (India)", - "Bosnian (Bosnia and Herzegovina)", - "Bilin (Eritrea)", - "Catalan", - "Catalan (Andorra)", - "Catalan (Spain)", - "Catalan (France)", - "Catalan (Italy)", - "Chechen (Russia)", - "Cherokee (United States)", - "Mandarin Chinese (Taiwan)", - "Crimean Tatar (Ukraine)", - "Kashubian (Poland)", - "Czech", - "Czech (Czech Republic)", - "Chuvash (Russia)", - "Welsh (United Kingdom)", - "Danish", - "Danish (Denmark)", - "German", - "German (Austria)", - "German (Belgium)", - "German (Switzerland)", - "German (Germany)", - "German (Italy)", - "German (Luxembourg)", - "Dogri (India)", - "Dhivehi (Maldives)", - "Dzongkha (Bhutan)", - "Greek", - "Greek (Cyprus)", - "Greek (Greece)", - "English", - "English (Antigua and Barbuda)", - "English (Australia)", - "English (Botswana)", - "English (Canada)", - "English (Denmark)", - "English (United Kingdom)", - "English (Hong Kong)", - "English (Ireland)", - "English (Israel)", - "English (India)", - "English (Nigeria)", - "English (New Zealand)", - "English (Philippines)", - "English (Singapore)", - "English (United States)", - "English (South Africa)", - "English (Zambia)", - "English (Zimbabwe)", - "Esperanto", - "Spanish", - "Spanish (Argentina)", - "Spanish (Bolivia)", - "Spanish (Chile)", - "Spanish (Colombia)", - "Spanish (Costa Rica)", - "Spanish (Cuba)", - "Spanish (Dominican Republic)", - "Spanish (Ecuador)", - "Spanish (Spain)", - "Spanish (Guatemala)", - "Spanish (Honduras)", - "Spanish (Mexico)", - "Spanish (Nicaragua)", - "Spanish (Panama)", - "Spanish (Peru)", - "Spanish (Puerto Rico)", - "Spanish (Paraguay)", - "Spanish (El Salvador)", - "Spanish (United States)", - "Spanish (Uruguay)", - "Spanish (Venezuela)", - "Estonian", - "Estonian (Estonia)", - "Basque", - "Basque (Spain)", - "Persian", - "Persian (Iran)", - "Fulah (Senegal)", - "Finnish", - "Finnish (Finland)", - "Filipino", - "Filipino (Philippines)", - "Faroese (Faroe Islands)", - "French", - "French (Belgium)", - "French (Canada)", - "French (Switzerland)", - "French (France)", - "French (Luxembourg)", - "Friulian (Italy)", - "Western Frisian (Germany)", - "Western Frisian (Netherlands)", - "Irish", - "Irish (Ireland)", - "Scottish Gaelic (United Kingdom)", - "Geez (Eritrea)", - "Geez (Ethiopia)", - "Galician", - "Galician (Spain)", - "Gujarati (India)", - "Manx (United Kingdom)", - "Hakka Chinese (Taiwan)", - "Hausa (Nigeria)", - "Hebrew", - "Hebrew (Israel)", - "Hindi", - "Hindi (India)", - "Chhattisgarhi (India)", - "Croatian", - "Croatian (Croatia)", - "Upper Sorbian (Germany)", - "Haitian (Haiti)", - "Hungarian", - "Hungarian (Hungary)", - "Huastec (Mexico)", - "Armenian (Armenia)", - "Interlingua (France)", - "Indonesian", - "Indonesian (Indonesia)", - "Igbo (Nigeria)", - "Inupiaq (Canada)", - "Icelandic", - "Icelandic (Iceland)", - "Italian", - "Italian (Switzerland)", - "Italian (Italy)", - "Inuktitut (Canada)", - "Japanese", - "Japanese (Japan)", - "Kabyle (Algeria)", - "Georgian", - "Georgian (Georgia)", - "Kazakh (Kazakhstan)", - "Kalaallisut (Greenland)", - "Central Khmer", - "Central Khmer (Cambodia)", - "Kannada (India)", - "Konkani (India)", - "Korean", - "Korean (South Korea)", - "Kashmiri (India)", - "Kurdish", - "Kurdish (Turkey)", - "Cornish (United Kingdom)", - "Kirghiz (Kyrgyzstan)", - "Luxembourgish (Luxembourg)", - "Ganda (Uganda)", - "Limburgan (Belgium)", - "Limburgan (Netherlands)", - "Ligurian (Italy)", - "Lingala (Congo)", - "Lao (Laos)", - "Lithuanian", - "Lithuanian (Lithuania)", - "Latvian", - "Latvian (Latvia)", - "Literary Chinese (Taiwan)", - "Magahi (India)", - "Maithili (India)", - "Malagasy (Madagascar)", - "Marshallese (Marshall Islands)", - "Eastern Mari (Russia)", - "Māori", - "Māori (New Zealand)", - "Mískito (Nicaragua)", - "Macedonian", - "Macedonian (Macedonia)", - "Malayalam", - "Malayalam (India)", - "Manipuri (India)", - "Mongolian (Mongolia)", - "Marathi", - "Marathi (India)", - "Malay", - "Malay (Malaysia)", - "Maltese", - "Maltese (Malta)", - "Burmese (Myanmar)", - "Erzya (Russia)", - "Nahuatl languages (Mexico)", - "Min Nan Chinese (Taiwan)", - "Norwegian Bokmål", - "Norwegian Bokmål (Norway)", - "Low German (Germany)", - "Low German (Netherlands)", - "Nepali (Nepal)", - "Central Nahuatl (Mexico)", - "Niuean (Niue)", - "Niuean (New Zealand)", - "Dutch", - "Dutch (Aruba)", - "Dutch (Belgium)", - "Dutch (Netherlands)", - "Norwegian Nynorsk", - "Norwegian Nynorsk (Norway)", - "South Ndebele (South Africa)", - "Pedi (South Africa)", - "Occitan (France)", - "Oromo", - "Oromo (Ethiopia)", - "Oromo (Kenya)", - "Oriya", - "Oriya (India)", - "Ossetian (Russia)", - "Panjabi (India)", - "Papiamento", - "Papiamento (Netherlands Antilles)", - "Papiamento (Aruba)", - "Papiamento (Curaçao)", - "Panjabi (Pakistan)", - "Polish", - "Polish (Poland)", - "Pirate", - "Pushto (Afghanistan)", - "Portuguese", - "Portuguese (Brazil)", - "Portuguese (Portugal)", - "Ayacucho Quechua (Peru)", - "Cusco Quechua (Peru)", - "Rajasthani (India)", - "Romanian", - "Romanian (Romania)", - "Russian", - "Russian (Russia)", - "Russian (Ukraine)", - "Kinyarwanda (Rwanda)", - "Sanskrit (India)", - "Santali (India)", - "Sardinian (Italy)", - "Scots (Scotland)", - "Sindhi (India)", - "Northern Sami (Norway)", - "Samogitian (Lithuania)", - "Shuswap (Canada)", - "Sidamo (Ethiopia)", - "Sinhala", - "Sinhala (Sri Lanka)", - "Slovak", - "Slovak (Slovakia)", - "Slovenian", - "Slovenian (Slovenia)", - "Somali", - "Somali (Djibouti)", - "Somali (Ethiopia)", - "Somali (Kenya)", - "Somali (Somalia)", - "Songhai languages (Mali)", - "Albanian", - "Albanian (Albania)", - "Albanian (Kosovo)", - "Albanian (Macedonia)", - "Serbian", - "Serbian (Cyrillic)", - "Serbian (Latin)", - "Serbian (Montenegro)", - "Serbian (Serbia)", - "Swati (South Africa)", - "Southern Sotho (South Africa)", - "Swedish", - "Swedish (Finland)", - "Swedish (Sweden)", - "Swahili (Kenya)", - "Swahili (Tanzania)", - "Silesian (Poland)", - "Tamil", - "Tamil (India)", - "Tamil (Sri Lanka)", - "Tulu (India)", - "Telugu", - "Telugu (India)", - "Tajik (Tajikistan)", - "Chitwania Tharu (Nepal)", - "Thai", - "Thai (Thailand)", - "Tigrinya", - "Tigrinya (Eritrea)", - "Tigrinya (Ethiopia)", - "Tigre (Eritrea)", - "Turkmen (Turkmenistan)", - "Tagalog (Philippines)", - "Tswana (South Africa)", - "Turkish", - "Turkish (Cyprus)", - "Turkish (Turkey)", - "Tsonga (South Africa)", - "Tatar", - "Tatar (Russia)", - "Central Atlas Tamazight", - "Central Atlas Tamazight (Marrocos)", - "Uighur (China)", - "Ukrainian", - "Ukrainian (Ukraine)", - "Unami (United States)", - "Urdu", - "Urdu (India)", - "Urdu (Pakistan)", - "Uzbek", - "Uzbek (Uzbekistan)", - "Venda (South Africa)", - "Vietnamese", - "Vietnamese (Vietnam)", - "Walloon (Belgium)", - "Walser (Switzerland)", - "Wolaytta (Ethiopia)", - "Wolof (Senegal)", - "Xhosa (South Africa)", - "Yiddish (United States)", - "Yoruba (Nigeria)", - "Yue Chinese (Hong Kong)", - "Chinese", - "Chinese (China)", - "Chinese (Hong Kong)", - "Chinese (Singapore)", - "Chinese (Taiwan)", - "Zulu (South Africa)", - nullptr -}; - -// Windows has some weird locale identifiers which do not honor the ISO 639-1 -// standardized nomenclature. Whenever those don't conflict with existing ISO -// identifiers, we override them. -// -// Reference: -// - https://msdn.microsoft.com/en-us/library/windows/desktop/ms693062(v=vs.85).aspx - -static const char *locale_renames[][2] = { - { "in", "id" }, // Indonesian - { "iw", "he" }, // Hebrew - { "no", "nb" }, // Norwegian Bokmål - { "C", "en" }, // "C" is the simple/default/untranslated Computer locale. - // ASCII-only, English, no currency symbols. Godot treats this as "en". - // See https://unix.stackexchange.com/a/87763/164141 "The C locale is"... - { nullptr, nullptr } -}; - -/////////////////////////////////////////////// - Dictionary Translation::_get_messages() const { Dictionary d; for (const KeyValue<StringName, StringName> &E : translation_map) { @@ -849,17 +68,7 @@ void Translation::_set_messages(const Dictionary &p_messages) { } void Translation::set_locale(const String &p_locale) { - String univ_locale = TranslationServer::standardize_locale(p_locale); - - if (!TranslationServer::is_locale_valid(univ_locale)) { - String trimmed_locale = TranslationServer::get_language_code(univ_locale); - - ERR_FAIL_COND_MSG(!TranslationServer::is_locale_valid(trimmed_locale), "Invalid locale: " + trimmed_locale + "."); - - locale = trimmed_locale; - } else { - locale = univ_locale; - } + locale = TranslationServer::get_singleton()->standardize_locale(p_locale); if (OS::get_singleton()->get_main_loop() && TranslationServer::get_singleton()->get_loaded_locales().has(this)) { OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED); @@ -1004,124 +213,314 @@ static _character_accent_pair _character_to_accented[] = { { 'z', U"ź" }, }; -bool TranslationServer::is_locale_valid(const String &p_locale) { - const char **ptr = locale_list; +static _FORCE_INLINE_ bool is_upper_case(char32_t c) { + return (c >= 'A' && c <= 'Z'); +} - while (*ptr) { - if (*ptr == p_locale) { - return true; +static _FORCE_INLINE_ bool is_lower_case(char32_t c) { + return (c >= 'a' && c <= 'z'); +} + +Vector<TranslationServer::LocaleScriptInfo> TranslationServer::locale_script_info; + +Map<String, String> TranslationServer::language_map; +Map<String, String> TranslationServer::script_map; +Map<String, String> TranslationServer::locale_rename_map; +Map<String, String> TranslationServer::country_name_map; +Map<String, String> TranslationServer::variant_map; +Map<String, String> TranslationServer::country_rename_map; + +void TranslationServer::init_locale_info() { + // Init locale info. + language_map.clear(); + int idx = 0; + while (language_list[idx][0] != nullptr) { + language_map[language_list[idx][0]] = String::utf8(language_list[idx][1]); + idx++; + } + + // Init locale-script map. + locale_script_info.clear(); + idx = 0; + while (locale_scripts[idx][0] != nullptr) { + LocaleScriptInfo info; + info.name = locale_scripts[idx][0]; + info.script = locale_scripts[idx][1]; + info.default_country = locale_scripts[idx][2]; + Vector<String> supported_countries = String(locale_scripts[idx][3]).split(",", false); + for (int i = 0; i < supported_countries.size(); i++) { + info.supported_countries.insert(supported_countries[i]); } - ptr++; + locale_script_info.push_back(info); + idx++; } - return false; -} + // Init supported script list. + script_map.clear(); + idx = 0; + while (script_list[idx][0] != nullptr) { + script_map[script_list[idx][1]] = String::utf8(script_list[idx][0]); + idx++; + } -String TranslationServer::standardize_locale(const String &p_locale) { - // Replaces '-' with '_' for macOS Sierra-style locales - String univ_locale = p_locale.replace("-", "_"); + // Init regional variant map. + variant_map.clear(); + idx = 0; + while (locale_variants[idx][0] != nullptr) { + variant_map[locale_variants[idx][0]] = locale_variants[idx][1]; + idx++; + } - // Handles known non-ISO locale names used e.g. on Windows - int idx = 0; + // Init locale renames. + locale_rename_map.clear(); + idx = 0; while (locale_renames[idx][0] != nullptr) { - if (locale_renames[idx][0] == univ_locale) { - univ_locale = locale_renames[idx][1]; - break; + if (!String(locale_renames[idx][1]).is_empty()) { + locale_rename_map[locale_renames[idx][0]] = locale_renames[idx][1]; } idx++; } - return univ_locale; + // Init country names. + country_name_map.clear(); + idx = 0; + while (country_names[idx][0] != nullptr) { + country_name_map[String(country_names[idx][0])] = String::utf8(country_names[idx][1]); + idx++; + } + + // Init country renames. + country_rename_map.clear(); + idx = 0; + while (country_renames[idx][0] != nullptr) { + if (!String(country_renames[idx][1]).is_empty()) { + country_rename_map[country_renames[idx][0]] = country_renames[idx][1]; + } + idx++; + } } -String TranslationServer::get_language_code(const String &p_locale) { - ERR_FAIL_COND_V_MSG(p_locale.length() < 2, p_locale, "Invalid locale '" + p_locale + "'."); - // Most language codes are two letters, but some are three, - // so we have to look for a regional code separator ('_' or '-') - // to extract the left part. - // For example we get 'nah_MX' as input and should return 'nah'. - int split = p_locale.find("_"); - if (split == -1) { - split = p_locale.find("-"); +String TranslationServer::standardize_locale(const String &p_locale) const { + // Replaces '-' with '_' for macOS style locales. + String univ_locale = p_locale.replace("-", "_"); + + // Extract locale elements. + String lang, script, country, variant; + Vector<String> locale_elements = univ_locale.get_slice("@", 0).split("_"); + lang = locale_elements[0]; + if (locale_elements.size() >= 2) { + if (locale_elements[1].length() == 4 && is_upper_case(locale_elements[1][0]) && is_lower_case(locale_elements[1][1]) && is_lower_case(locale_elements[1][2]) && is_lower_case(locale_elements[1][3])) { + script = locale_elements[1]; + } + if (locale_elements[1].length() == 2 && is_upper_case(locale_elements[1][0]) && is_upper_case(locale_elements[1][1])) { + country = locale_elements[1]; + } } - if (split == -1) { // No separator, so the locale is already only a language code. - return p_locale; + if (locale_elements.size() >= 3) { + if (locale_elements[2].length() == 2 && is_upper_case(locale_elements[2][0]) && is_upper_case(locale_elements[2][1])) { + country = locale_elements[2]; + } else if (variant_map.has(locale_elements[2].to_lower()) && variant_map[locale_elements[2].to_lower()] == lang) { + variant = locale_elements[2].to_lower(); + } + } + if (locale_elements.size() >= 4) { + if (variant_map.has(locale_elements[3].to_lower()) && variant_map[locale_elements[3].to_lower()] == lang) { + variant = locale_elements[3].to_lower(); + } } - return p_locale.left(split); -} -void TranslationServer::set_locale(const String &p_locale) { - String univ_locale = standardize_locale(p_locale); + // Try extract script and variant from the extra part. + Vector<String> script_extra = univ_locale.get_slice("@", 1).split(";"); + for (int i = 0; i < script_extra.size(); i++) { + if (script_extra[i].to_lower() == "cyrillic") { + script = "Cyrl"; + break; + } else if (script_extra[i].to_lower() == "latin") { + script = "Latn"; + break; + } else if (script_extra[i].to_lower() == "devanagari") { + script = "Deva"; + break; + } else if (variant_map.has(script_extra[i].to_lower()) && variant_map[script_extra[i].to_lower()] == lang) { + variant = script_extra[i].to_lower(); + } + } - if (!is_locale_valid(univ_locale)) { - String trimmed_locale = get_language_code(univ_locale); - print_verbose(vformat("Unsupported locale '%s', falling back to '%s'.", p_locale, trimmed_locale)); + // Handles known non-ISO language names used e.g. on Windows. + if (locale_rename_map.has(lang)) { + lang = locale_rename_map[lang]; + } - if (!is_locale_valid(trimmed_locale)) { - ERR_PRINT(vformat("Unsupported locale '%s', falling back to 'en'.", trimmed_locale)); - locale = "en"; - } else { - locale = trimmed_locale; + // Handle country renames. + if (country_rename_map.has(country)) { + country = country_rename_map[country]; + } + + // Remove unsupported script codes. + if (!script_map.has(script)) { + script = ""; + } + + // Add script code base on language and country codes for some ambiguous cases. + if (script.is_empty()) { + for (int i = 0; i < locale_script_info.size(); i++) { + const LocaleScriptInfo &info = locale_script_info[i]; + if (info.name == lang) { + if (country.is_empty() || info.supported_countries.has(country)) { + script = info.script; + break; + } + } + } + } + if (!script.is_empty() && country.is_empty()) { + // Add conntry code based on script for some ambiguous cases. + for (int i = 0; i < locale_script_info.size(); i++) { + const LocaleScriptInfo &info = locale_script_info[i]; + if (info.name == lang && info.script == script) { + country = info.default_country; + break; + } + } + } + + // Combine results. + String locale = lang; + if (!script.is_empty()) { + locale = locale + "_" + script; + } + if (!country.is_empty()) { + locale = locale + "_" + country; + } + if (!variant.is_empty()) { + locale = locale + "_" + variant; + } + return locale; +} + +int TranslationServer::compare_locales(const String &p_locale_a, const String &p_locale_b) const { + String locale_a = standardize_locale(p_locale_a); + String locale_b = standardize_locale(p_locale_b); + + if (locale_a == locale_b) { + // Exact match. + return 10; + } + + Vector<String> locale_a_elements = locale_a.split("_"); + Vector<String> locale_b_elements = locale_b.split("_"); + if (locale_a_elements[0] == locale_b_elements[0]) { + // Matching language, both locales have extra parts. + // Return number of matching elements. + int matching_elements = 1; + for (int i = 1; i < locale_a_elements.size(); i++) { + for (int j = 1; j < locale_b_elements.size(); j++) { + if (locale_a_elements[i] == locale_b_elements[j]) { + matching_elements++; + } + } } + return matching_elements; } else { - locale = univ_locale; + // No match. + return 0; } +} - if (OS::get_singleton()->get_main_loop()) { - OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED); +String TranslationServer::get_locale_name(const String &p_locale) const { + String locale = standardize_locale(p_locale); + + String lang, script, country; + Vector<String> locale_elements = locale.split("_"); + lang = locale_elements[0]; + if (locale_elements.size() >= 2) { + if (locale_elements[1].length() == 4 && is_upper_case(locale_elements[1][0]) && is_lower_case(locale_elements[1][1]) && is_lower_case(locale_elements[1][2]) && is_lower_case(locale_elements[1][3])) { + script = locale_elements[1]; + } + if (locale_elements[1].length() == 2 && is_upper_case(locale_elements[1][0]) && is_upper_case(locale_elements[1][1])) { + country = locale_elements[1]; + } + } + if (locale_elements.size() >= 3) { + if (locale_elements[2].length() == 2 && is_upper_case(locale_elements[2][0]) && is_upper_case(locale_elements[2][1])) { + country = locale_elements[2]; + } } - ResourceLoader::reload_translation_remaps(); + String name = language_map[lang]; + if (!script.is_empty()) { + name = name + " (" + script_map[script] + ")"; + } + if (!country.is_empty()) { + name = name + ", " + country_name_map[country]; + } + return name; } -String TranslationServer::get_locale() const { - return locale; +Vector<String> TranslationServer::get_all_languages() const { + Vector<String> languages; + + for (const Map<String, String>::Element *E = language_map.front(); E; E = E->next()) { + languages.push_back(E->key()); + } + + return languages; } -String TranslationServer::get_locale_name(const String &p_locale) const { - if (!locale_name_map.has(p_locale)) { - return String(); +String TranslationServer::get_language_name(const String &p_language) const { + return language_map[p_language]; +} + +Vector<String> TranslationServer::get_all_scripts() const { + Vector<String> scripts; + + for (const Map<String, String>::Element *E = script_map.front(); E; E = E->next()) { + scripts.push_back(E->key()); } - return locale_name_map[p_locale]; + + return scripts; } -Array TranslationServer::get_loaded_locales() const { - Array locales; - for (const Set<Ref<Translation>>::Element *E = translations.front(); E; E = E->next()) { - const Ref<Translation> &t = E->get(); - ERR_FAIL_COND_V(t.is_null(), Array()); - String l = t->get_locale(); +String TranslationServer::get_script_name(const String &p_script) const { + return script_map[p_script]; +} - if (!locales.has(l)) { - locales.push_back(l); - } +Vector<String> TranslationServer::get_all_countries() const { + Vector<String> countries; + + for (const Map<String, String>::Element *E = country_name_map.front(); E; E = E->next()) { + countries.push_back(E->key()); } - locales.sort(); - return locales; + return countries; } -Vector<String> TranslationServer::get_all_locales() { - Vector<String> locales; +String TranslationServer::get_country_name(const String &p_country) const { + return country_name_map[p_country]; +} - const char **ptr = locale_list; +void TranslationServer::set_locale(const String &p_locale) { + locale = standardize_locale(p_locale); - while (*ptr) { - locales.push_back(*ptr); - ptr++; + if (OS::get_singleton()->get_main_loop()) { + OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED); } - return locales; + ResourceLoader::reload_translation_remaps(); } -Vector<String> TranslationServer::get_all_locale_names() { - Vector<String> locales; +String TranslationServer::get_locale() const { + return locale; +} - const char **ptr = locale_names; +Array TranslationServer::get_loaded_locales() const { + Array locales; + for (const Set<Ref<Translation>>::Element *E = translations.front(); E; E = E->next()) { + const Ref<Translation> &t = E->get(); + ERR_FAIL_COND_V(t.is_null(), Array()); + String l = t->get_locale(); - while (*ptr) { - locales.push_back(String::utf8(*ptr)); - ptr++; + locales.push_back(l); } return locales; @@ -1137,23 +536,20 @@ void TranslationServer::remove_translation(const Ref<Translation> &p_translation Ref<Translation> TranslationServer::get_translation_object(const String &p_locale) { Ref<Translation> res; - String lang = get_language_code(p_locale); - bool near_match_found = false; + int best_score = 0; for (const Set<Ref<Translation>>::Element *E = translations.front(); E; E = E->next()) { const Ref<Translation> &t = E->get(); ERR_FAIL_COND_V(t.is_null(), nullptr); String l = t->get_locale(); - // Exact match. - if (l == p_locale) { - return t; - } - - // If near match found, keep that match, but keep looking to try to look for perfect match. - if (get_language_code(l) == lang && !near_match_found) { + int score = compare_locales(p_locale, l); + if (score >= best_score) { res = t; - near_match_found = true; + best_score = score; + if (score == 10) { + break; // Exact match, skip the rest. + } } } return res; @@ -1212,52 +608,27 @@ StringName TranslationServer::translate_plural(const StringName &p_message, cons } StringName TranslationServer::_get_message_from_translations(const StringName &p_message, const StringName &p_context, const String &p_locale, bool plural, const String &p_message_plural, int p_n) const { - // Locale can be of the form 'll_CC', i.e. language code and regional code, - // e.g. 'en_US', 'en_GB', etc. It might also be simply 'll', e.g. 'en'. - // To find the relevant translation, we look for those with locale starting - // with the language code, and then if any is an exact match for the long - // form. If not found, we fall back to a near match (another locale with - // same language code). - - // Note: ResourceLoader::_path_remap reproduces this locale near matching - // logic, so be sure to propagate changes there when changing things here. - StringName res; - String lang = get_language_code(p_locale); - bool near_match = false; + int best_score = 0; for (const Set<Ref<Translation>>::Element *E = translations.front(); E; E = E->next()) { const Ref<Translation> &t = E->get(); ERR_FAIL_COND_V(t.is_null(), p_message); String l = t->get_locale(); - bool exact_match = (l == p_locale); - if (!exact_match) { - if (near_match) { - continue; // Only near-match once, but keep looking for exact matches. + int score = compare_locales(p_locale, l); + if (score >= best_score) { + StringName r; + if (!plural) { + res = t->get_message(p_message, p_context); + } else { + res = t->get_plural_message(p_message, p_message_plural, p_n, p_context); } - if (get_language_code(l) != lang) { - continue; // Language code does not match. + best_score = score; + if (score == 10) { + break; // Exact match, skip the rest. } } - - StringName r; - if (!plural) { - r = t->get_message(p_message, p_context); - } else { - r = t->get_plural_message(p_message, p_message_plural, p_n, p_context); - } - - if (!r) { - continue; - } - res = r; - - if (exact_match) { - break; - } else { - near_match = true; - } } return res; @@ -1308,18 +679,7 @@ void TranslationServer::setup() { pseudolocalization_skip_placeholders_enabled = GLOBAL_DEF("internationalization/pseudolocalization/skip_placeholders", true); #ifdef TOOLS_ENABLED - { - String options = ""; - int idx = 0; - while (locale_list[idx]) { - if (idx > 0) { - options += ","; - } - options += locale_list[idx]; - idx++; - } - ProjectSettings::get_singleton()->set_custom_property_info("internationalization/locale/fallback", PropertyInfo(Variant::STRING, "internationalization/locale/fallback", PROPERTY_HINT_ENUM, options)); - } + ProjectSettings::get_singleton()->set_custom_property_info("internationalization/locale/fallback", PropertyInfo(Variant::STRING, "internationalization/locale/fallback", PROPERTY_HINT_LOCALE_ID, "")); #endif } @@ -1574,6 +934,18 @@ void TranslationServer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_locale", "locale"), &TranslationServer::set_locale); ClassDB::bind_method(D_METHOD("get_locale"), &TranslationServer::get_locale); + ClassDB::bind_method(D_METHOD("compare_locales", "locale_a", "locale_b"), &TranslationServer::compare_locales); + ClassDB::bind_method(D_METHOD("standardize_locale", "locale"), &TranslationServer::standardize_locale); + + ClassDB::bind_method(D_METHOD("get_all_languages"), &TranslationServer::get_all_languages); + ClassDB::bind_method(D_METHOD("get_language_name", "language"), &TranslationServer::get_language_name); + + ClassDB::bind_method(D_METHOD("get_all_scripts"), &TranslationServer::get_all_scripts); + ClassDB::bind_method(D_METHOD("get_script_name", "script"), &TranslationServer::get_script_name); + + ClassDB::bind_method(D_METHOD("get_all_countries"), &TranslationServer::get_all_countries); + ClassDB::bind_method(D_METHOD("get_country_name", "country"), &TranslationServer::get_country_name); + ClassDB::bind_method(D_METHOD("get_locale_name", "locale"), &TranslationServer::get_locale_name); ClassDB::bind_method(D_METHOD("translate", "message", "context"), &TranslationServer::translate, DEFVAL("")); @@ -1606,8 +978,5 @@ void TranslationServer::load_translations() { TranslationServer::TranslationServer() { singleton = this; - - for (int i = 0; locale_list[i]; ++i) { - locale_name_map.insert(locale_list[i], String::utf8(locale_names[i])); - } + init_locale_info(); } diff --git a/core/string/translation.h b/core/string/translation.h index e95d15e2ab..947ca4c6d8 100644 --- a/core/string/translation.h +++ b/core/string/translation.h @@ -78,8 +78,6 @@ class TranslationServer : public Object { Ref<Translation> tool_translation; Ref<Translation> doc_translation; - Map<String, String> locale_name_map; - bool enabled = true; bool pseudolocalization_enabled = false; @@ -109,6 +107,23 @@ class TranslationServer : public Object { static void _bind_methods(); + struct LocaleScriptInfo { + String name; + String script; + String default_country; + Set<String> supported_countries; + }; + static Vector<LocaleScriptInfo> locale_script_info; + + static Map<String, String> language_map; + static Map<String, String> script_map; + static Map<String, String> locale_rename_map; + static Map<String, String> country_name_map; + static Map<String, String> country_rename_map; + static Map<String, String> variant_map; + + void init_locale_info(); + public: _FORCE_INLINE_ static TranslationServer *get_singleton() { return singleton; } @@ -119,6 +134,15 @@ public: String get_locale() const; Ref<Translation> get_translation_object(const String &p_locale); + Vector<String> get_all_languages() const; + String get_language_name(const String &p_language) const; + + Vector<String> get_all_scripts() const; + String get_script_name(const String &p_script) const; + + Vector<String> get_all_countries() const; + String get_country_name(const String &p_country) const; + String get_locale_name(const String &p_locale) const; Array get_loaded_locales() const; @@ -136,11 +160,9 @@ public: void set_editor_pseudolocalization(bool p_enabled); void reload_pseudolocalization(); - static Vector<String> get_all_locales(); - static Vector<String> get_all_locale_names(); - static bool is_locale_valid(const String &p_locale); - static String standardize_locale(const String &p_locale); - static String get_language_code(const String &p_locale); + String standardize_locale(const String &p_locale) const; + + int compare_locales(const String &p_locale_a, const String &p_locale_b) const; String get_tool_locale(); void set_tool_translation(const Ref<Translation> &p_translation); diff --git a/core/variant/variant_parser.cpp b/core/variant/variant_parser.cpp index 571181f665..57875bf50f 100644 --- a/core/variant/variant_parser.cpp +++ b/core/variant/variant_parser.cpp @@ -1649,12 +1649,13 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str dict.get_key_list(&keys); keys.sort(); + if (keys.is_empty()) { // Avoid unnecessary line break. + p_store_string_func(p_store_string_ud, "{}"); + break; + } + p_store_string_func(p_store_string_ud, "{\n"); for (List<Variant>::Element *E = keys.front(); E; E = E->next()) { - /* - if (!_check_type(dict[E->get()])) - continue; - */ write(E->get(), p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud, recursion_count); p_store_string_func(p_store_string_ud, ": "); write(dict[E->get()], p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud, recursion_count); diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml index be7ac1164e..d8c9ca08e8 100644 --- a/doc/classes/@GlobalScope.xml +++ b/doc/classes/@GlobalScope.xml @@ -2493,7 +2493,10 @@ </constant> <constant name="PROPERTY_HINT_ARRAY_TYPE" value="39" enum="PropertyHint"> </constant> - <constant name="PROPERTY_HINT_MAX" value="41" enum="PropertyHint"> + <constant name="PROPERTY_HINT_LOCALE_ID" value="41" enum="PropertyHint"> + Hints that a string property is a locale code. Editing it will show a locale dialog for picking language and country. + </constant> + <constant name="PROPERTY_HINT_MAX" value="42" enum="PropertyHint"> </constant> <constant name="PROPERTY_USAGE_NONE" value="0" enum="PropertyUsageFlags"> </constant> diff --git a/doc/classes/ArrayMesh.xml b/doc/classes/ArrayMesh.xml index c986947dfb..fa980d9f75 100644 --- a/doc/classes/ArrayMesh.xml +++ b/doc/classes/ArrayMesh.xml @@ -62,8 +62,7 @@ <argument index="0" name="primitive" type="int" enum="Mesh.PrimitiveType" /> <argument index="1" name="arrays" type="Array" /> <argument index="2" name="blend_shapes" type="Array" default="[]" /> - <argument index="3" name="lods" type="Dictionary" default="{ -}" /> + <argument index="3" name="lods" type="Dictionary" default="{}" /> <argument index="4" name="compress_flags" type="int" default="0" /> <description> Creates a new surface. diff --git a/doc/classes/CodeEdit.xml b/doc/classes/CodeEdit.xml index 697b0443f1..4a9d79978c 100644 --- a/doc/classes/CodeEdit.xml +++ b/doc/classes/CodeEdit.xml @@ -442,7 +442,7 @@ <member name="auto_brace_completion_highlight_matching" type="bool" setter="set_highlight_matching_braces_enabled" getter="is_highlight_matching_braces_enabled" default="false"> Highlight mismatching brace pairs. </member> - <member name="auto_brace_completion_pairs" type="Dictionary" setter="set_auto_brace_completion_pairs" getter="get_auto_brace_completion_pairs" default="{"\"": "\"","'": "'","(": ")","[": "]","{": "}"}"> + <member name="auto_brace_completion_pairs" type="Dictionary" setter="set_auto_brace_completion_pairs" getter="get_auto_brace_completion_pairs" default="{ "\"": "\"", "'": "'", "(": ")", "[": "]", "{": "}" }"> Sets the brace pairs to be autocompleted. </member> <member name="code_completion_enabled" type="bool" setter="set_code_completion_enabled" getter="is_code_completion_enabled" default="false"> diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml index f34b8c342f..c527922cd5 100644 --- a/doc/classes/DisplayServer.xml +++ b/doc/classes/DisplayServer.xml @@ -321,11 +321,6 @@ [b]Note:[/b] This method is implemented on Linux, macOS and Windows. </description> </method> - <method name="mouse_get_absolute_position" qualifiers="const"> - <return type="Vector2i" /> - <description> - </description> - </method> <method name="mouse_get_button_state" qualifiers="const"> <return type="int" enum="MouseButton" /> <description> diff --git a/doc/classes/EditorInspector.xml b/doc/classes/EditorInspector.xml index 39589138fa..27dab3d422 100644 --- a/doc/classes/EditorInspector.xml +++ b/doc/classes/EditorInspector.xml @@ -13,6 +13,11 @@ <member name="horizontal_scroll_mode" type="int" setter="set_horizontal_scroll_mode" getter="get_horizontal_scroll_mode" overrides="ScrollContainer" enum="ScrollContainer.ScrollMode" default="0" /> </members> <signals> + <signal name="edited_object_changed"> + <description> + Emitted when the object being edited by the inspector has changed. + </description> + </signal> <signal name="object_id_selected"> <argument index="0" name="id" type="int" /> <description> diff --git a/doc/classes/Environment.xml b/doc/classes/Environment.xml index 2090672acc..dab9d7b054 100644 --- a/doc/classes/Environment.xml +++ b/doc/classes/Environment.xml @@ -168,7 +168,8 @@ </member> <member name="sdfgi_cascade0_distance" type="float" setter="set_sdfgi_cascade0_distance" getter="get_sdfgi_cascade0_distance" default="12.8"> </member> - <member name="sdfgi_cascades" type="int" setter="set_sdfgi_cascades" getter="get_sdfgi_cascades" enum="Environment.SDFGICascades" default="1"> + <member name="sdfgi_cascades" type="int" setter="set_sdfgi_cascades" getter="get_sdfgi_cascades" default="6"> + The number of cascades to use for SDFGI (between 1 and 8). A higher number of cascades allows displaying SDFGI further away while preserving detail up close, at the cost of performance. When using SDFGI on small-scale levels, [member sdfgi_cascades] can often be decreased between [code]1[/code] and [code]4[/code] to improve performance. </member> <member name="sdfgi_enabled" type="bool" setter="set_sdfgi_enabled" getter="is_sdfgi_enabled" default="false"> If [code]true[/code], enables signed distance field global illumination for meshes that have their [member GeometryInstance3D.gi_mode] set to [constant GeometryInstance3D.GI_MODE_BAKED]. SDFGI is a real-time global illumination technique that works well with procedurally generated and user-built levels, including in situations where geometry is created during gameplay. The signed distance field is automatically generated around the camera as it moves. Dynamic lights are supported, but dynamic occluders and emissive surfaces are not. @@ -371,12 +372,6 @@ <constant name="GLOW_BLEND_MODE_MIX" value="4" enum="GlowBlendMode"> Mixes the glow with the underlying color to avoid increasing brightness as much while still maintaining a glow effect. </constant> - <constant name="SDFGI_CASCADES_4" value="0" enum="SDFGICascades"> - </constant> - <constant name="SDFGI_CASCADES_6" value="1" enum="SDFGICascades"> - </constant> - <constant name="SDFGI_CASCADES_8" value="2" enum="SDFGICascades"> - </constant> <constant name="SDFGI_Y_SCALE_DISABLED" value="0" enum="SDFGIYScale"> </constant> <constant name="SDFGI_Y_SCALE_75_PERCENT" value="1" enum="SDFGIYScale"> diff --git a/doc/classes/FlowContainer.xml b/doc/classes/FlowContainer.xml new file mode 100644 index 0000000000..482d879b09 --- /dev/null +++ b/doc/classes/FlowContainer.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="FlowContainer" inherits="Container" version="4.0"> + <brief_description> + Base class for flow containers. + </brief_description> + <description> + Arranges child [Control] nodes vertically or horizontally in a left-to-right or top-to-bottom flow. + A line is filled with [Control] nodes until no more fit on the same line, similar to text in an autowrapped label. + </description> + <tutorials> + </tutorials> + <methods> + <method name="get_line_count" qualifiers="const"> + <return type="int" /> + <description> + Returns the current line count. + </description> + </method> + </methods> +</class> diff --git a/doc/classes/HFlowContainer.xml b/doc/classes/HFlowContainer.xml new file mode 100644 index 0000000000..8ee2da69b7 --- /dev/null +++ b/doc/classes/HFlowContainer.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="HFlowContainer" inherits="FlowContainer" version="4.0"> + <brief_description> + Horizontal flow container. + </brief_description> + <description> + Horizontal version of [FlowContainer]. + </description> + <tutorials> + </tutorials> + <theme_items> + <theme_item name="hseparation" data_type="constant" type="int" default="4"> + The horizontal separation of children nodes. + </theme_item> + <theme_item name="vseparation" data_type="constant" type="int" default="4"> + The vertical separation of children nodes. + </theme_item> + </theme_items> +</class> diff --git a/doc/classes/Image.xml b/doc/classes/Image.xml index e7bb1b9825..60d4b664d2 100644 --- a/doc/classes/Image.xml +++ b/doc/classes/Image.xml @@ -452,7 +452,7 @@ </method> </methods> <members> - <member name="data" type="Dictionary" setter="_set_data" getter="_get_data" default="{"data": PackedByteArray(),"format": "Lum8","height": 0,"mipmaps": false,"width": 0}"> + <member name="data" type="Dictionary" setter="_set_data" getter="_get_data" default="{ "data": PackedByteArray(), "format": "Lum8", "height": 0, "mipmaps": false, "width": 0 }"> Holds all the image's color data in a given format. See [enum Format] constants. </member> </members> diff --git a/doc/classes/ImporterMesh.xml b/doc/classes/ImporterMesh.xml index ab344f908c..4c323156c9 100644 --- a/doc/classes/ImporterMesh.xml +++ b/doc/classes/ImporterMesh.xml @@ -23,8 +23,7 @@ <argument index="0" name="primitive" type="int" enum="Mesh.PrimitiveType" /> <argument index="1" name="arrays" type="Array" /> <argument index="2" name="blend_shapes" type="Array" default="[]" /> - <argument index="3" name="lods" type="Dictionary" default="{ -}" /> + <argument index="3" name="lods" type="Dictionary" default="{}" /> <argument index="4" name="material" type="Material" default="null" /> <argument index="5" name="name" type="String" default="""" /> <argument index="6" name="flags" type="int" default="0" /> @@ -178,7 +177,7 @@ </method> </methods> <members> - <member name="_data" type="Dictionary" setter="_set_data" getter="_get_data" default="{"surfaces": []}"> + <member name="_data" type="Dictionary" setter="_set_data" getter="_get_data" default="{ "surfaces": [] }"> </member> </members> </class> diff --git a/doc/classes/PackedScene.xml b/doc/classes/PackedScene.xml index d3a770b35b..4bba6563bb 100644 --- a/doc/classes/PackedScene.xml +++ b/doc/classes/PackedScene.xml @@ -104,7 +104,7 @@ </method> </methods> <members> - <member name="_bundled" type="Dictionary" setter="_set_bundled_scene" getter="_get_bundled_scene" default="{"conn_count": 0,"conns": PackedInt32Array(),"editable_instances": [],"names": PackedStringArray(),"node_count": 0,"node_paths": [],"nodes": PackedInt32Array(),"variants": [],"version": 2}"> + <member name="_bundled" type="Dictionary" setter="_set_bundled_scene" getter="_get_bundled_scene" default="{ "conn_count": 0, "conns": PackedInt32Array(), "editable_instances": [], "names": PackedStringArray(), "node_count": 0, "node_paths": [], "nodes": PackedInt32Array(), "variants": [], "version": 2 }"> A dictionary representation of the scene contents. Available keys include "rnames" and "variants" for resources, "node_count", "nodes", "node_paths" for nodes, "editable_instances" for base scene children overrides, "conn_count" and "conns" for signal connections, and "version" for the format style of the PackedScene. </member> diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 357186b917..bcd576d7ab 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -196,9 +196,6 @@ <member name="application/boot_splash/bg_color" type="Color" setter="" getter="" default="Color(0.14, 0.14, 0.14, 1)"> Background color for the boot splash. </member> - <member name="application/boot_splash/fullsize" type="bool" setter="" getter="" default="true"> - If [code]true[/code], scale the boot splash image to the full window size (preserving the aspect ratio) when the engine starts. If [code]false[/code], the engine will leave it at the default pixel size. - </member> <member name="application/boot_splash/image" type="String" setter="" getter="" default=""""> Path to an image used as the boot splash. If left empty, the default Godot Engine splash will be displayed instead. [b]Note:[/b] Only effective if [member application/boot_splash/show_image] is [code]true[/code]. @@ -206,6 +203,9 @@ <member name="application/boot_splash/show_image" type="bool" setter="" getter="" default="true"> If [code]true[/code], displays the image specified in [member application/boot_splash/image] when the engine starts. If [code]false[/code], only displays the plain color specified in [member application/boot_splash/bg_color]. </member> + <member name="application/boot_splash/stretch_mode" type="int" setter="" getter="" default="1"> + Specifies how the splash image will be stretched. See [enum RenderingServer.SplashStretchMode] constants for more information. + </member> <member name="application/boot_splash/use_filter" type="bool" setter="" getter="" default="true"> If [code]true[/code], applies linear filtering when scaling the image (recommended for high-resolution artwork). If [code]false[/code], uses nearest-neighbor interpolation (recommended for pixel art). </member> diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml index 5127455b4d..9c41b7472b 100644 --- a/doc/classes/RenderingServer.xml +++ b/doc/classes/RenderingServer.xml @@ -1001,7 +1001,7 @@ <return type="void" /> <argument index="0" name="env" type="RID" /> <argument index="1" name="enable" type="bool" /> - <argument index="2" name="cascades" type="int" enum="RenderingServer.EnvironmentSDFGICascades" /> + <argument index="2" name="cascades" type="int" /> <argument index="3" name="min_cell_size" type="float" /> <argument index="4" name="y_scale" type="int" enum="RenderingServer.EnvironmentSDFGIYScale" /> <argument index="5" name="use_occlusion" type="bool" /> @@ -1838,8 +1838,7 @@ <argument index="1" name="primitive" type="int" enum="RenderingServer.PrimitiveType" /> <argument index="2" name="arrays" type="Array" /> <argument index="3" name="blend_shapes" type="Array" default="[]" /> - <argument index="4" name="lods" type="Dictionary" default="{ -}" /> + <argument index="4" name="lods" type="Dictionary" default="{}" /> <argument index="5" name="compress_format" type="int" default="0" /> <description> </description> @@ -2658,10 +2657,10 @@ <return type="void" /> <argument index="0" name="image" type="Image" /> <argument index="1" name="color" type="Color" /> - <argument index="2" name="scale" type="bool" /> + <argument index="2" name="stretch_mode" type="int" enum="RenderingServer.SplashStretchMode" /> <argument index="3" name="use_filter" type="bool" default="true" /> <description> - Sets a boot image. The color defines the background color. If [code]scale[/code] is [code]true[/code], the image will be scaled to fit the screen size. If [code]use_filter[/code] is [code]true[/code], the image will be scaled with linear interpolation. If [code]use_filter[/code] is [code]false[/code], the image will be scaled with nearest-neighbor interpolation. + Sets a boot image. The color defines the background color. The value of [code]stretch_mode[/code] indicates how the image will be stretched (see [enum SplashStretchMode] for possible values). If [code]use_filter[/code] is [code]true[/code], the image will be scaled with linear interpolation. If [code]use_filter[/code] is [code]false[/code], the image will be scaled with nearest-neighbor interpolation. </description> </method> <method name="set_debug_generate_wireframes"> @@ -4180,12 +4179,6 @@ <constant name="ENV_SSIL_QUALITY_ULTRA" value="4" enum="EnvironmentSSILQuality"> Highest quality screen-space indirect lighting. Uses the adaptive target setting which can be dynamically adjusted to smoothly balance performance and visual quality. </constant> - <constant name="ENV_SDFGI_CASCADES_4" value="0" enum="EnvironmentSDFGICascades"> - </constant> - <constant name="ENV_SDFGI_CASCADES_6" value="1" enum="EnvironmentSDFGICascades"> - </constant> - <constant name="ENV_SDFGI_CASCADES_8" value="2" enum="EnvironmentSDFGICascades"> - </constant> <constant name="ENV_SDFGI_Y_SCALE_DISABLED" value="0" enum="EnvironmentSDFGIYScale"> </constant> <constant name="ENV_SDFGI_Y_SCALE_75_PERCENT" value="1" enum="EnvironmentSDFGIYScale"> @@ -4511,6 +4504,24 @@ </constant> <constant name="RENDERING_INFO_VIDEO_MEM_USED" value="5" enum="RenderingInfo"> </constant> + <constant name="SPLASH_STRETCH_MODE_DISABLED" value="0" enum="SplashStretchMode"> + The splash image uses its default pixel size. + </constant> + <constant name="SPLASH_STRETCH_MODE_KEEP" value="1" enum="SplashStretchMode"> + If the window width is greater than its height, the splash image will be stretched to have the same height as the window. Otherwise, the image will be stretched to have the same width as the window. Both cases keep the original image's aspect ratio. + </constant> + <constant name="SPLASH_STRETCH_MODE_KEEP_WIDTH" value="2" enum="SplashStretchMode"> + The splash image is stretched to have the same width as the window. It keeps the image's aspect ratio. + </constant> + <constant name="SPLASH_STRETCH_MODE_KEEP_HEIGHT" value="3" enum="SplashStretchMode"> + The splash image is stretched to have the same height as the window. It keeps the image's aspect ratio. + </constant> + <constant name="SPLASH_STRETCH_MODE_COVER" value="4" enum="SplashStretchMode"> + The splash image covers the window while keeping the aspect ratio. + </constant> + <constant name="SPLASH_STRETCH_MODE_EXPAND" value="5" enum="SplashStretchMode"> + The splash image covers the window without keeping the aspect ratio. + </constant> <constant name="FEATURE_SHADERS" value="0" enum="Features"> Hardware supports shaders. This enum is currently unused in Godot 3.x. </constant> diff --git a/doc/classes/TextLine.xml b/doc/classes/TextLine.xml index 1eaccf4604..bf1abf86ae 100644 --- a/doc/classes/TextLine.xml +++ b/doc/classes/TextLine.xml @@ -24,8 +24,7 @@ <argument index="0" name="text" type="String" /> <argument index="1" name="fonts" type="Font" /> <argument index="2" name="size" type="int" /> - <argument index="3" name="opentype_features" type="Dictionary" default="{ -}" /> + <argument index="3" name="opentype_features" type="Dictionary" default="{}" /> <argument index="4" name="language" type="String" default="""" /> <description> Adds text span and font to draw it. diff --git a/doc/classes/TextParagraph.xml b/doc/classes/TextParagraph.xml index ff193a0e4b..3d3c0dd6ff 100644 --- a/doc/classes/TextParagraph.xml +++ b/doc/classes/TextParagraph.xml @@ -24,8 +24,7 @@ <argument index="0" name="text" type="String" /> <argument index="1" name="fonts" type="Font" /> <argument index="2" name="size" type="int" /> - <argument index="3" name="opentype_features" type="Dictionary" default="{ -}" /> + <argument index="3" name="opentype_features" type="Dictionary" default="{}" /> <argument index="4" name="language" type="String" default="""" /> <description> Adds text span and font to draw it. @@ -259,8 +258,7 @@ <argument index="1" name="fonts" type="Font" /> <argument index="2" name="size" type="int" /> <argument index="3" name="dropcap_margins" type="Rect2" default="Rect2(0, 0, 0, 0)" /> - <argument index="4" name="opentype_features" type="Dictionary" default="{ -}" /> + <argument index="4" name="opentype_features" type="Dictionary" default="{}" /> <argument index="5" name="language" type="String" default="""" /> <description> Sets drop cap, overrides previously set drop cap. Drop cap (dropped capital) is a decorative element at the beginning of a paragraph that is larger than the rest of the text. diff --git a/doc/classes/TextServer.xml b/doc/classes/TextServer.xml index 5be6113dda..61024ef0c8 100644 --- a/doc/classes/TextServer.xml +++ b/doc/classes/TextServer.xml @@ -913,8 +913,7 @@ <argument index="1" name="text" type="String" /> <argument index="2" name="fonts" type="Array" /> <argument index="3" name="size" type="int" /> - <argument index="4" name="opentype_features" type="Dictionary" default="{ -}" /> + <argument index="4" name="opentype_features" type="Dictionary" default="{}" /> <argument index="5" name="language" type="String" default="""" /> <description> Adds text span and font to draw it to the text buffer. diff --git a/doc/classes/TranslationServer.xml b/doc/classes/TranslationServer.xml index a1b4404079..c90cb2987c 100644 --- a/doc/classes/TranslationServer.xml +++ b/doc/classes/TranslationServer.xml @@ -24,6 +24,46 @@ Clears the server from all translations. </description> </method> + <method name="compare_locales" qualifiers="const"> + <return type="int" /> + <argument index="0" name="locale_a" type="String" /> + <argument index="1" name="locale_b" type="String" /> + <description> + Compares two locales and return similarity score between [code]0[/code](no match) and [code]10[/code](full match). + </description> + </method> + <method name="get_all_countries" qualifiers="const"> + <return type="PackedStringArray" /> + <description> + Returns array of known country codes. + </description> + </method> + <method name="get_all_languages" qualifiers="const"> + <return type="PackedStringArray" /> + <description> + Returns array of known language codes. + </description> + </method> + <method name="get_all_scripts" qualifiers="const"> + <return type="PackedStringArray" /> + <description> + Returns array of known script codes. + </description> + </method> + <method name="get_country_name" qualifiers="const"> + <return type="String" /> + <argument index="0" name="country" type="String" /> + <description> + Returns readable country name for the [code]country[/code] code. + </description> + </method> + <method name="get_language_name" qualifiers="const"> + <return type="String" /> + <argument index="0" name="language" type="String" /> + <description> + Returns readable language name for the [code]language[/code] code. + </description> + </method> <method name="get_loaded_locales" qualifiers="const"> <return type="Array" /> <description> @@ -44,6 +84,13 @@ Returns a locale's language and its variant (e.g. [code]"en_US"[/code] would return [code]"English (United States)"[/code]). </description> </method> + <method name="get_script_name" qualifiers="const"> + <return type="String" /> + <argument index="0" name="script" type="String" /> + <description> + Returns readable script name for the [code]script[/code] code. + </description> + </method> <method name="get_translation_object"> <return type="Translation" /> <argument index="0" name="locale" type="String" /> @@ -80,6 +127,13 @@ If translations have been loaded beforehand for the new locale, they will be applied. </description> </method> + <method name="standardize_locale" qualifiers="const"> + <return type="String" /> + <argument index="0" name="locale" type="String" /> + <description> + Retunrs [code]locale[/code] string standardized to match known locales (e.g. [code]en-US[/code] would be matched to [code]en_US[/code]). + </description> + </method> <method name="translate" qualifiers="const"> <return type="StringName" /> <argument index="0" name="message" type="StringName" /> diff --git a/doc/classes/VFlowContainer.xml b/doc/classes/VFlowContainer.xml new file mode 100644 index 0000000000..f58075a140 --- /dev/null +++ b/doc/classes/VFlowContainer.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<class name="VFlowContainer" inherits="FlowContainer" version="4.0"> + <brief_description> + Vertical flow container. + </brief_description> + <description> + Vertical version of [FlowContainer]. + </description> + <tutorials> + </tutorials> + <theme_items> + <theme_item name="hseparation" data_type="constant" type="int" default="4"> + The horizontal separation of children nodes. + </theme_item> + <theme_item name="vseparation" data_type="constant" type="int" default="4"> + The vertical separation of children nodes. + </theme_item> + </theme_items> +</class> diff --git a/doc/classes/Window.xml b/doc/classes/Window.xml index 75161d3c5b..0e4e3a65f8 100644 --- a/doc/classes/Window.xml +++ b/doc/classes/Window.xml @@ -368,6 +368,10 @@ <description> </description> </signal> + <signal name="theme_changed"> + <description> + </description> + </signal> <signal name="visibility_changed"> <description> </description> diff --git a/drivers/gles3/rasterizer_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp index 32ead8aa7e..f7b33763ce 100644 --- a/drivers/gles3/rasterizer_gles3.cpp +++ b/drivers/gles3/rasterizer_gles3.cpp @@ -298,54 +298,89 @@ void RasterizerGLES3::blit_render_targets_to_screen(DisplayServer::WindowID p_sc } } -void RasterizerGLES3::set_boot_image(const Ref<Image> &p_image, const Color &p_color, bool p_scale, bool p_use_filter) { - if (p_image.is_null() || p_image->is_empty()) +void RasterizerGLES3::set_boot_image(const Ref<Image> &p_image, const Color &p_color, RenderingServer::SplashStretchMode p_stretch_mode, bool p_use_filter) { + if (p_image.is_null() || p_image->is_empty()) { return; + } - Size2i win_size = DisplayServer::get_singleton()->screen_get_size(); + Size2 window_size = DisplayServer::get_singleton()->screen_get_size(); glBindFramebuffer(GL_FRAMEBUFFER, 0); - glViewport(0, 0, win_size.width, win_size.height); + glViewport(0, 0, window_size.width, window_size.height); glDisable(GL_BLEND); glDepthMask(GL_FALSE); - if (false) { - // if (OS::get_singleton()->get_window_per_pixel_transparency_enabled()) { - glClearColor(0.0, 0.0, 0.0, 0.0); - } else { - glClearColor(p_color.r, p_color.g, p_color.b, 1.0); - } + glClearColor(p_color.r, p_color.g, p_color.b, 1.0); glClear(GL_COLOR_BUFFER_BIT); canvas.canvas_begin(); RID texture = storage.texture_create(); + // FIXME: Handle p_filter. //storage.texture_allocate(texture, p_image->get_width(), p_image->get_height(), 0, p_image->get_format(), VS::TEXTURE_TYPE_2D, p_use_filter ? VS::TEXTURE_FLAG_FILTER : 0); storage._texture_allocate_internal(texture, p_image->get_width(), p_image->get_height(), 0, p_image->get_format(), RenderingDevice::TEXTURE_TYPE_2D); storage.texture_set_data(texture, p_image); + // Stretch code synced with RendererCompositorRD. Rect2 imgrect(0, 0, p_image->get_width(), p_image->get_height()); Rect2 screenrect; - if (p_scale) { - if (win_size.width > win_size.height) { - //scale horizontally - screenrect.size.y = win_size.height; - screenrect.size.x = imgrect.size.x * win_size.height / imgrect.size.y; - screenrect.position.x = (win_size.width - screenrect.size.x) / 2; - - } else { - //scale vertically - screenrect.size.x = win_size.width; - screenrect.size.y = imgrect.size.y * win_size.width / imgrect.size.x; - screenrect.position.y = (win_size.height - screenrect.size.y) / 2; - } - } else { - screenrect = imgrect; - screenrect.position += ((Size2(win_size.width, win_size.height) - screenrect.size) / 2.0).floor(); + switch (p_stretch_mode) { + case RenderingServer::SPLASH_STRETCH_MODE_DISABLED: { + screenrect = imgrect; + screenrect.position += ((window_size - screenrect.size) / 2.0).floor(); + } break; + case RenderingServer::SPLASH_STRETCH_MODE_KEEP: { + if (window_size.width > window_size.height) { + // Scale horizontally. + screenrect.size.y = window_size.height; + screenrect.size.x = imgrect.size.x * window_size.height / imgrect.size.y; + screenrect.position.x = (window_size.width - screenrect.size.x) / 2; + } else { + // Scale vertically. + screenrect.size.x = window_size.width; + screenrect.size.y = imgrect.size.y * window_size.width / imgrect.size.x; + screenrect.position.y = (window_size.height - screenrect.size.y) / 2; + } + } break; + case RenderingServer::SPLASH_STRETCH_MODE_KEEP_WIDTH: { + // Scale vertically. + screenrect.size.x = window_size.width; + screenrect.size.y = imgrect.size.y * window_size.width / imgrect.size.x; + screenrect.position.y = (window_size.height - screenrect.size.y) / 2; + } break; + case RenderingServer::SPLASH_STRETCH_MODE_KEEP_HEIGHT: { + // Scale horizontally. + screenrect.size.y = window_size.height; + screenrect.size.x = imgrect.size.x * window_size.height / imgrect.size.y; + screenrect.position.x = (window_size.width - screenrect.size.x) / 2; + } break; + case RenderingServer::SPLASH_STRETCH_MODE_COVER: { + double window_aspect = (double)window_size.width / window_size.height; + double img_aspect = imgrect.size.x / imgrect.size.y; + + if (window_aspect > img_aspect) { + // Scale vertically. + screenrect.size.x = window_size.width; + screenrect.size.y = imgrect.size.y * window_size.width / imgrect.size.x; + screenrect.position.y = (window_size.height - screenrect.size.y) / 2; + } else { + // Scale horizontally. + screenrect.size.y = window_size.height; + screenrect.size.x = imgrect.size.x * window_size.height / imgrect.size.y; + screenrect.position.x = (window_size.width - screenrect.size.x) / 2; + } + } break; + case RenderingServer::SPLASH_STRETCH_MODE_EXPAND: { + screenrect.size.x = window_size.width; + screenrect.size.y = window_size.height; + } break; } + // FIXME: Actually draw the image after binding it, using screenrect for scaling. + RasterizerStorageGLES3::Texture *t = storage.texture_owner.get_or_null(texture); glActiveTexture(GL_TEXTURE0 + storage.config.max_texture_image_units - 1); glBindTexture(GL_TEXTURE_2D, t->tex_id); + //canvas->draw_generic_textured_rect(screenrect, Rect2(0, 0, 1, 1)); glBindTexture(GL_TEXTURE_2D, 0); canvas.canvas_end(); diff --git a/drivers/gles3/rasterizer_gles3.h b/drivers/gles3/rasterizer_gles3.h index a641e189c5..24e9e99ae2 100644 --- a/drivers/gles3/rasterizer_gles3.h +++ b/drivers/gles3/rasterizer_gles3.h @@ -57,7 +57,7 @@ public: RendererCanvasRender *get_canvas() { return &canvas; } RendererSceneRender *get_scene() { return &scene; } - void set_boot_image(const Ref<Image> &p_image, const Color &p_color, bool p_scale, bool p_use_filter = true); + void set_boot_image(const Ref<Image> &p_image, const Color &p_color, RenderingServer::SplashStretchMode p_stretch_mode, bool p_use_filter = true); void initialize(); void begin_frame(double frame_step); diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index 9e7d4f5435..31dda16b26 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -234,7 +234,7 @@ void RasterizerSceneGLES3::environment_set_ssil(RID p_env, bool p_enable, float void RasterizerSceneGLES3::environment_set_ssil_quality(RS::EnvironmentSSILQuality p_quality, bool p_half_size, float p_adaptive_target, int p_blur_passes, float p_fadeout_from, float p_fadeout_to) { } -void RasterizerSceneGLES3::environment_set_sdfgi(RID p_env, bool p_enable, RS::EnvironmentSDFGICascades p_cascades, float p_min_cell_size, RS::EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_probe_bias) { +void RasterizerSceneGLES3::environment_set_sdfgi(RID p_env, bool p_enable, int p_cascades, float p_min_cell_size, RS::EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_probe_bias) { } void RasterizerSceneGLES3::environment_set_sdfgi_ray_count(RS::EnvironmentSDFGIRayCount p_ray_count) { diff --git a/drivers/gles3/rasterizer_scene_gles3.h b/drivers/gles3/rasterizer_scene_gles3.h index b73c053bc7..0f8c2e88b4 100644 --- a/drivers/gles3/rasterizer_scene_gles3.h +++ b/drivers/gles3/rasterizer_scene_gles3.h @@ -126,7 +126,7 @@ public: void environment_set_ssil(RID p_env, bool p_enable, float p_radius, float p_intensity, float p_sharpness, float p_normal_rejection) override; void environment_set_ssil_quality(RS::EnvironmentSSILQuality p_quality, bool p_half_size, float p_adaptive_target, int p_blur_passes, float p_fadeout_from, float p_fadeout_to) override; - void environment_set_sdfgi(RID p_env, bool p_enable, RS::EnvironmentSDFGICascades p_cascades, float p_min_cell_size, RS::EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_probe_bias) override; + void environment_set_sdfgi(RID p_env, bool p_enable, int p_cascades, float p_min_cell_size, RS::EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_probe_bias) override; void environment_set_sdfgi_ray_count(RS::EnvironmentSDFGIRayCount p_ray_count) override; void environment_set_sdfgi_frames_to_converge(RS::EnvironmentSDFGIFramesToConverge p_frames) override; diff --git a/drivers/vulkan/rendering_device_vulkan.cpp b/drivers/vulkan/rendering_device_vulkan.cpp index 4d742a36d9..c6592b300b 100644 --- a/drivers/vulkan/rendering_device_vulkan.cpp +++ b/drivers/vulkan/rendering_device_vulkan.cpp @@ -8372,11 +8372,11 @@ void RenderingDeviceVulkan::_free_internal(RID p_id) { } else if (uniform_set_owner.owns(p_id)) { UniformSet *uniform_set = uniform_set_owner.get_or_null(p_id); frames[frame].uniform_sets_to_dispose_of.push_back(*uniform_set); + uniform_set_owner.free(p_id); + if (uniform_set->invalidated_callback != nullptr) { - uniform_set->invalidated_callback(p_id, uniform_set->invalidated_callback_userdata); + uniform_set->invalidated_callback(uniform_set->invalidated_callback_userdata); } - - uniform_set_owner.free(p_id); } else if (render_pipeline_owner.owns(p_id)) { RenderPipeline *pipeline = render_pipeline_owner.get_or_null(p_id); frames[frame].render_pipelines_to_dispose_of.push_back(*pipeline); diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index 13e9d58744..973f74d6cc 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -3375,7 +3375,13 @@ Node *AnimationTrackEditor::get_root() const { } void AnimationTrackEditor::update_keying() { - bool keying_enabled = is_visible_in_tree() && animation.is_valid(); + bool keying_enabled = false; + + EditorHistory *editor_history = EditorNode::get_singleton()->get_editor_history(); + if (is_visible_in_tree() && animation.is_valid() && editor_history->get_path_size() > 0) { + Object *obj = ObjectDB::get_instance(editor_history->get_path_object(0)); + keying_enabled = Object::cast_to<Node>(obj) != nullptr; + } if (keying_enabled == keying) { return; @@ -4525,8 +4531,6 @@ void AnimationTrackEditor::_notification(int p_what) { if (p_what == NOTIFICATION_VISIBILITY_CHANGED) { update_keying(); - EditorNode::get_singleton()->update_keying(); - emit_signal(SNAME("keying_changed")); } } diff --git a/editor/doc_tools.cpp b/editor/doc_tools.cpp index a71e16b66c..6acf654b04 100644 --- a/editor/doc_tools.cpp +++ b/editor/doc_tools.cpp @@ -350,7 +350,7 @@ void DocTools::generate(bool p_basic_types) { List<PropertyInfo> properties; List<PropertyInfo> own_properties; if (name == "ProjectSettings") { - //special case for project settings, so settings can be documented + // Special case for project settings, so settings can be documented. ProjectSettings::get_singleton()->get_property_list(&properties); own_properties = properties; } else { @@ -358,9 +358,12 @@ void DocTools::generate(bool p_basic_types) { ClassDB::get_property_list(name, &own_properties, true); } + properties.sort(); + own_properties.sort(); + List<PropertyInfo>::Element *EO = own_properties.front(); for (const PropertyInfo &E : properties) { - bool inherited = EO == nullptr; + bool inherited = true; if (EO && EO->get() == E) { inherited = false; EO = EO->next(); @@ -410,7 +413,7 @@ void DocTools::generate(bool p_basic_types) { //used to track uninitialized values using valgrind //print_line("getting default value for " + String(name) + "." + String(E.name)); if (default_value_valid && default_value.get_type() != Variant::OBJECT) { - prop.default_value = default_value.get_construct_string().replace("\n", ""); + prop.default_value = default_value.get_construct_string().replace("\n", " "); } StringName setter = ClassDB::get_property_setter(name, E.name); @@ -522,7 +525,7 @@ void DocTools::generate(bool p_basic_types) { int darg_idx = i - (E.arguments.size() - E.default_arguments.size()); if (darg_idx >= 0) { Variant default_arg = E.default_arguments[darg_idx]; - argument.default_value = default_arg.get_construct_string(); + argument.default_value = default_arg.get_construct_string().replace("\n", " "); } method.arguments.push_back(argument); @@ -585,7 +588,7 @@ void DocTools::generate(bool p_basic_types) { tid.name = E; tid.type = "Color"; tid.data_type = "color"; - tid.default_value = Variant(Theme::get_default()->get_color(E, cname)).get_construct_string(); + tid.default_value = Variant(Theme::get_default()->get_color(E, cname)).get_construct_string().replace("\n", " "); c.theme_properties.push_back(tid); } @@ -757,7 +760,7 @@ void DocTools::generate(bool p_basic_types) { int darg_idx = mi.default_arguments.size() - mi.arguments.size() + j; if (darg_idx >= 0) { Variant default_arg = mi.default_arguments[darg_idx]; - ad.default_value = default_arg.get_construct_string(); + ad.default_value = default_arg.get_construct_string().replace("\n", " "); } method.arguments.push_back(ad); @@ -801,7 +804,7 @@ void DocTools::generate(bool p_basic_types) { DocData::PropertyDoc property; property.name = pi.name; property.type = Variant::get_type_name(pi.type); - property.default_value = v.get(pi.name).get_construct_string(); + property.default_value = v.get(pi.name).get_construct_string().replace("\n", " "); c.properties.push_back(property); } @@ -813,7 +816,7 @@ void DocTools::generate(bool p_basic_types) { DocData::ConstantDoc constant; constant.name = E; Variant value = Variant::get_constant_value(Variant::Type(i), E); - constant.value = value.get_type() == Variant::INT ? itos(value) : value.get_construct_string(); + constant.value = value.get_type() == Variant::INT ? itos(value) : value.get_construct_string().replace("\n", " "); constant.is_value_valid = true; c.constants.push_back(constant); } @@ -930,7 +933,7 @@ void DocTools::generate(bool p_basic_types) { int darg_idx = j - (mi.arguments.size() - mi.default_arguments.size()); if (darg_idx >= 0) { Variant default_arg = mi.default_arguments[darg_idx]; - ad.default_value = default_arg.get_construct_string(); + ad.default_value = default_arg.get_construct_string().replace("\n", " "); } md.arguments.push_back(ad); diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp index 06e3a63f4a..28cf2ee75f 100644 --- a/editor/editor_help.cpp +++ b/editor/editor_help.cpp @@ -44,18 +44,25 @@ DocTools *EditorHelp::doc = nullptr; -void EditorHelp::_init_colors() { - title_color = get_theme_color(SNAME("accent_color"), SNAME("Editor")); - text_color = get_theme_color(SNAME("default_color"), SNAME("RichTextLabel")); - headline_color = get_theme_color(SNAME("headline_color"), SNAME("EditorHelp")); - base_type_color = title_color.lerp(text_color, 0.5); - comment_color = text_color * Color(1, 1, 1, 0.6); - symbol_color = comment_color; - value_color = text_color * Color(1, 1, 1, 0.6); - qualifier_color = text_color * Color(1, 1, 1, 0.8); - type_color = get_theme_color(SNAME("accent_color"), SNAME("Editor")).lerp(text_color, 0.5); - class_desc->add_theme_color_override("selection_color", get_theme_color(SNAME("accent_color"), SNAME("Editor")) * Color(1, 1, 1, 0.4)); - class_desc->add_theme_constant_override("line_separation", Math::round(5 * EDSCALE)); +void EditorHelp::_update_theme() { + text_color = get_theme_color("text_color", "EditorHelp"); + title_color = get_theme_color("title_color", "EditorHelp"); + headline_color = get_theme_color("headline_color", "EditorHelp"); + comment_color = get_theme_color("comment_color", "EditorHelp"); + symbol_color = get_theme_color("symbol_color", "EditorHelp"); + value_color = get_theme_color("value_color", "EditorHelp"); + qualifier_color = get_theme_color("qualifier_color", "EditorHelp"); + type_color = get_theme_color("type_color", "EditorHelp"); + + class_desc->add_theme_color_override("selection_color", get_theme_color("selection_color", "EditorHelp")); + class_desc->add_theme_constant_override("line_separation", get_theme_constant("line_separation", "EditorHelp")); + class_desc->add_theme_constant_override("table_hseparation", get_theme_constant("table_hseparation", "EditorHelp")); + class_desc->add_theme_constant_override("table_vseparation", get_theme_constant("table_vseparation", "EditorHelp")); + + doc_font = get_theme_font(SNAME("doc"), SNAME("EditorFonts")); + doc_bold_font = get_theme_font(SNAME("doc_bold"), SNAME("EditorFonts")); + doc_title_font = get_theme_font(SNAME("doc_title"), SNAME("EditorFonts")); + doc_code_font = get_theme_font(SNAME("doc_source"), SNAME("EditorFonts")); } void EditorHelp::_search(bool p_search_previous) { @@ -184,8 +191,7 @@ void EditorHelp::_add_type(const String &p_type, const String &p_enum) { t = p_enum.get_slice(".", 0); } } - const Color text_color = get_theme_color(SNAME("default_color"), SNAME("RichTextLabel")); - const Color type_color = get_theme_color(SNAME("accent_color"), SNAME("Editor")).lerp(text_color, 0.5); + class_desc->push_color(type_color); bool add_array = false; if (can_ref) { @@ -496,16 +502,11 @@ void EditorHelp::_update_doc() { method_line.clear(); section_line.clear(); - _init_colors(); - - DocData::ClassDoc cd = doc->class_list[edited_class]; //make a copy, so we can sort without worrying - - Ref<Font> doc_font = get_theme_font(SNAME("doc"), SNAME("EditorFonts")); - Ref<Font> doc_bold_font = get_theme_font(SNAME("doc_bold"), SNAME("EditorFonts")); - Ref<Font> doc_title_font = get_theme_font(SNAME("doc_title"), SNAME("EditorFonts")); - Ref<Font> doc_code_font = get_theme_font(SNAME("doc_source"), SNAME("EditorFonts")); + _update_theme(); String link_color_text = title_color.to_html(false); + DocData::ClassDoc cd = doc->class_list[edited_class]; // Make a copy, so we can sort without worrying. + // Class name section_line.push_back(Pair<String, int>(TTR("Top"), 0)); class_desc->push_font(doc_title_font); @@ -1476,11 +1477,9 @@ static void _add_text_to_rt(const String &p_bbcode, RichTextLabel *p_rt) { Ref<Font> doc_kbd_font = p_rt->get_theme_font(SNAME("doc_keyboard"), SNAME("EditorFonts")); Color headline_color = p_rt->get_theme_color(SNAME("headline_color"), SNAME("EditorHelp")); - Color accent_color = p_rt->get_theme_color(SNAME("accent_color"), SNAME("Editor")); - Color property_color = p_rt->get_theme_color(SNAME("property_color"), SNAME("Editor")); - Color link_color = accent_color.lerp(headline_color, 0.8); - Color code_color = accent_color.lerp(headline_color, 0.6); - Color kbd_color = accent_color.lerp(property_color, 0.6); + Color link_color = p_rt->get_theme_color(SNAME("link_color"), SNAME("EditorHelp")); + Color code_color = p_rt->get_theme_color(SNAME("code_color"), SNAME("EditorHelp")); + Color kbd_color = p_rt->get_theme_color(SNAME("kbd_color"), SNAME("EditorHelp")); String bbcode = p_bbcode.dedent().replace("\t", "").replace("\r", "").strip_edges(); @@ -1941,16 +1940,16 @@ void EditorHelpBit::_bind_methods() { void EditorHelpBit::_notification(int p_what) { switch (p_what) { + case NOTIFICATION_ENTER_TREE: + case NOTIFICATION_THEME_CHANGED: { + rich_text->add_theme_color_override("selection_color", get_theme_color(SNAME("selection_color"), SNAME("EditorHelp"))); + } break; + case NOTIFICATION_READY: { rich_text->clear(); _add_text_to_rt(text, rich_text); } break; - case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { - rich_text->add_theme_color_override("selection_color", get_theme_color(SNAME("accent_color"), SNAME("Editor")) * Color(1, 1, 1, 0.4)); - } break; - default: - break; } } @@ -1964,7 +1963,6 @@ EditorHelpBit::EditorHelpBit() { rich_text = memnew(RichTextLabel); add_child(rich_text); rich_text->connect("meta_clicked", callable_mp(this, &EditorHelpBit::_meta_clicked)); - rich_text->add_theme_color_override("selection_color", get_theme_color(SNAME("accent_color"), SNAME("Editor")) * Color(1, 1, 1, 0.4)); rich_text->set_override_selected_font_color(false); set_custom_minimum_size(Size2(0, 70 * EDSCALE)); } diff --git a/editor/editor_help.h b/editor/editor_help.h index eb879c6d39..377ae05a08 100644 --- a/editor/editor_help.h +++ b/editor/editor_help.h @@ -126,17 +126,21 @@ class EditorHelp : public VBoxContainer { String base_path; - Color title_color; Color text_color; + Color title_color; Color headline_color; - Color base_type_color; - Color type_color; Color comment_color; Color symbol_color; Color value_color; Color qualifier_color; + Color type_color; + + Ref<Font> doc_font; + Ref<Font> doc_bold_font; + Ref<Font> doc_title_font; + Ref<Font> doc_code_font; - void _init_colors(); + void _update_theme(); void _help_callback(const String &p_topic); void _add_text(const String &p_bbcode); diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index 75e518e050..1e1f25b6d1 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -2905,6 +2905,7 @@ void EditorInspector::edit(Object *p_object) { object->connect("property_list_changed", callable_mp(this, &EditorInspector::_changed_callback)); update_tree(); } + emit_signal("edited_object_changed"); } void EditorInspector::set_keying(bool p_active) { @@ -3543,6 +3544,7 @@ void EditorInspector::_bind_methods() { ADD_SIGNAL(MethodInfo("object_id_selected", PropertyInfo(Variant::INT, "id"))); ADD_SIGNAL(MethodInfo("property_edited", PropertyInfo(Variant::STRING, "property"))); ADD_SIGNAL(MethodInfo("property_toggled", PropertyInfo(Variant::STRING, "property"), PropertyInfo(Variant::BOOL, "checked"))); + ADD_SIGNAL(MethodInfo("edited_object_changed")); ADD_SIGNAL(MethodInfo("restart_requested")); } diff --git a/editor/editor_locale_dialog.cpp b/editor/editor_locale_dialog.cpp new file mode 100644 index 0000000000..5c4ece7065 --- /dev/null +++ b/editor/editor_locale_dialog.cpp @@ -0,0 +1,563 @@ +/*************************************************************************/ +/* editor_locale_dialog.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "editor_locale_dialog.h" + +#include "editor/editor_node.h" +#include "editor/editor_scale.h" +#include "scene/gui/check_button.h" +#include "scene/gui/line_edit.h" +#include "scene/gui/option_button.h" +#include "scene/gui/tree.h" + +static _FORCE_INLINE_ bool is_upper_case(char32_t c) { + return (c >= 'A' && c <= 'Z'); +} + +static _FORCE_INLINE_ bool is_lower_case(char32_t c) { + return (c >= 'a' && c <= 'z'); +} + +void EditorLocaleDialog::_bind_methods() { + ADD_SIGNAL(MethodInfo("locale_selected", PropertyInfo(Variant::STRING, "locale"))); +} + +void EditorLocaleDialog::ok_pressed() { + if (edit_filters->is_pressed()) { + return; // Do not update, if in filter edit mode. + } + + String locale; + if (lang_code->get_text().is_empty()) { + return; // Language code is required. + } + locale = lang_code->get_text(); + + if (!script_code->get_text().is_empty()) { + locale += "_" + script_code->get_text(); + } + if (!country_code->get_text().is_empty()) { + locale += "_" + country_code->get_text(); + } + if (!variant_code->get_text().is_empty()) { + locale += "_" + variant_code->get_text(); + } + + emit_signal(SNAME("locale_selected"), TranslationServer::get_singleton()->standardize_locale(locale)); + hide(); +} + +void EditorLocaleDialog::_item_selected() { + if (updating_lists) { + return; + } + + if (edit_filters->is_pressed()) { + return; // Do not update, if in filter edit mode. + } + + TreeItem *l = lang_list->get_selected(); + if (l) { + lang_code->set_text(l->get_metadata(0).operator String()); + } + + TreeItem *s = script_list->get_selected(); + if (s) { + script_code->set_text(s->get_metadata(0).operator String()); + } + + TreeItem *c = cnt_list->get_selected(); + if (c) { + country_code->set_text(c->get_metadata(0).operator String()); + } +} + +void EditorLocaleDialog::_toggle_advanced(bool p_checked) { + if (!p_checked) { + script_code->set_text(""); + variant_code->set_text(""); + } + _update_tree(); +} + +void EditorLocaleDialog::_post_popup() { + ConfirmationDialog::_post_popup(); + + if (!locale_set) { + lang_code->set_text(""); + script_code->set_text(""); + country_code->set_text(""); + variant_code->set_text(""); + } + edit_filters->set_pressed(false); + _update_tree(); +} + +void EditorLocaleDialog::_filter_lang_option_changed() { + TreeItem *t = lang_list->get_edited(); + String lang = t->get_metadata(0); + bool checked = t->is_checked(0); + + Variant prev; + Array f_lang_all; + + if (ProjectSettings::get_singleton()->has_setting("internationalization/locale/language_filter")) { + f_lang_all = ProjectSettings::get_singleton()->get("internationalization/locale/language_filter"); + prev = f_lang_all; + } + + int l_idx = f_lang_all.find(lang); + + if (checked) { + if (l_idx == -1) { + f_lang_all.append(lang); + } + } else { + if (l_idx != -1) { + f_lang_all.remove_at(l_idx); + } + } + + f_lang_all.sort(); + + undo_redo->create_action(TTR("Changed Locale Language Filter")); + undo_redo->add_do_property(ProjectSettings::get_singleton(), "internationalization/locale/language_filter", f_lang_all); + undo_redo->add_undo_property(ProjectSettings::get_singleton(), "internationalization/locale/language_filter", prev); + undo_redo->commit_action(); +} + +void EditorLocaleDialog::_filter_script_option_changed() { + TreeItem *t = script_list->get_edited(); + String script = t->get_metadata(0); + bool checked = t->is_checked(0); + + Variant prev; + Array f_script_all; + + if (ProjectSettings::get_singleton()->has_setting("internationalization/locale/script_filter")) { + f_script_all = ProjectSettings::get_singleton()->get("internationalization/locale/script_filter"); + prev = f_script_all; + } + + int l_idx = f_script_all.find(script); + + if (checked) { + if (l_idx == -1) { + f_script_all.append(script); + } + } else { + if (l_idx != -1) { + f_script_all.remove_at(l_idx); + } + } + + f_script_all.sort(); + + undo_redo->create_action(TTR("Changed Locale Script Filter")); + undo_redo->add_do_property(ProjectSettings::get_singleton(), "internationalization/locale/script_filter", f_script_all); + undo_redo->add_undo_property(ProjectSettings::get_singleton(), "internationalization/locale/script_filter", prev); + undo_redo->commit_action(); +} + +void EditorLocaleDialog::_filter_cnt_option_changed() { + TreeItem *t = cnt_list->get_edited(); + String cnt = t->get_metadata(0); + bool checked = t->is_checked(0); + + Variant prev; + Array f_cnt_all; + + if (ProjectSettings::get_singleton()->has_setting("internationalization/locale/country_filter")) { + f_cnt_all = ProjectSettings::get_singleton()->get("internationalization/locale/country_filter"); + prev = f_cnt_all; + } + + int l_idx = f_cnt_all.find(cnt); + + if (checked) { + if (l_idx == -1) { + f_cnt_all.append(cnt); + } + } else { + if (l_idx != -1) { + f_cnt_all.remove_at(l_idx); + } + } + + f_cnt_all.sort(); + + undo_redo->create_action(TTR("Changed Locale Country Filter")); + undo_redo->add_do_property(ProjectSettings::get_singleton(), "internationalization/locale/country_filter", f_cnt_all); + undo_redo->add_undo_property(ProjectSettings::get_singleton(), "internationalization/locale/country_filter", prev); + undo_redo->commit_action(); +} + +void EditorLocaleDialog::_filter_mode_changed(int p_mode) { + int f_mode = filter_mode->get_selected_id(); + Variant prev; + + if (ProjectSettings::get_singleton()->has_setting("internationalization/locale/locale_filter_mode")) { + prev = ProjectSettings::get_singleton()->get("internationalization/locale/locale_filter_mode"); + } + + undo_redo->create_action(TTR("Changed Locale Filter Mode")); + undo_redo->add_do_property(ProjectSettings::get_singleton(), "internationalization/locale/locale_filter_mode", f_mode); + undo_redo->add_undo_property(ProjectSettings::get_singleton(), "internationalization/locale/locale_filter_mode", prev); + undo_redo->commit_action(); + + _update_tree(); +} + +void EditorLocaleDialog::_edit_filters(bool p_checked) { + _update_tree(); +} + +void EditorLocaleDialog::_update_tree() { + updating_lists = true; + + int filter = SHOW_ALL_LOCALES; + if (ProjectSettings::get_singleton()->has_setting("internationalization/locale/locale_filter_mode")) { + filter = ProjectSettings::get_singleton()->get("internationalization/locale/locale_filter_mode"); + } + Array f_lang_all; + if (ProjectSettings::get_singleton()->has_setting("internationalization/locale/language_filter")) { + f_lang_all = ProjectSettings::get_singleton()->get("internationalization/locale/language_filter"); + } + Array f_cnt_all; + if (ProjectSettings::get_singleton()->has_setting("internationalization/locale/country_filter")) { + f_cnt_all = ProjectSettings::get_singleton()->get("internationalization/locale/country_filter"); + } + Array f_script_all; + if (ProjectSettings::get_singleton()->has_setting("internationalization/locale/script_filter")) { + f_script_all = ProjectSettings::get_singleton()->get("internationalization/locale/script_filter"); + } + bool is_edit_mode = edit_filters->is_pressed(); + + filter_mode->select(filter); + + // Hide text advanced edit and disable OK button if in filter edit mode. + advanced->set_visible(!is_edit_mode); + hb_locale->set_visible(!is_edit_mode && advanced->is_pressed()); + vb_script_list->set_visible(advanced->is_pressed()); + get_ok_button()->set_disabled(is_edit_mode); + + // Update language list. + lang_list->clear(); + TreeItem *l_root = lang_list->create_item(nullptr); + lang_list->set_hide_root(true); + + Vector<String> languages = TranslationServer::get_singleton()->get_all_languages(); + for (const String &E : languages) { + if (is_edit_mode || (filter == SHOW_ALL_LOCALES) || f_lang_all.has(E) || f_lang_all.is_empty()) { + const String &lang = TranslationServer::get_singleton()->get_language_name(E); + TreeItem *t = lang_list->create_item(l_root); + if (is_edit_mode) { + t->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); + t->set_editable(0, true); + t->set_checked(0, f_lang_all.has(E)); + } else if (lang_code->get_text() == E) { + t->select(0); + } + t->set_text(0, vformat("%s [%s]", lang, E)); + t->set_metadata(0, E); + } + } + + // Update script list. + script_list->clear(); + TreeItem *s_root = script_list->create_item(nullptr); + script_list->set_hide_root(true); + + if (!is_edit_mode) { + TreeItem *t = script_list->create_item(s_root); + t->set_text(0, "[Default]"); + t->set_metadata(0, ""); + } + + Vector<String> scripts = TranslationServer::get_singleton()->get_all_scripts(); + for (const String &E : scripts) { + if (is_edit_mode || (filter == SHOW_ALL_LOCALES) || f_script_all.has(E) || f_script_all.is_empty()) { + const String &script = TranslationServer::get_singleton()->get_script_name(E); + TreeItem *t = script_list->create_item(s_root); + if (is_edit_mode) { + t->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); + t->set_editable(0, true); + t->set_checked(0, f_script_all.has(E)); + } else if (script_code->get_text() == E) { + t->select(0); + } + t->set_text(0, vformat("%s [%s]", script, E)); + t->set_metadata(0, E); + } + } + + // Update country list. + cnt_list->clear(); + TreeItem *c_root = cnt_list->create_item(nullptr); + cnt_list->set_hide_root(true); + + if (!is_edit_mode) { + TreeItem *t = cnt_list->create_item(c_root); + t->set_text(0, "[Default]"); + t->set_metadata(0, ""); + } + + Vector<String> countries = TranslationServer::get_singleton()->get_all_countries(); + for (const String &E : countries) { + if (is_edit_mode || (filter == SHOW_ALL_LOCALES) || f_cnt_all.has(E) || f_cnt_all.is_empty()) { + const String &cnt = TranslationServer::get_singleton()->get_country_name(E); + TreeItem *t = cnt_list->create_item(c_root); + if (is_edit_mode) { + t->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); + t->set_editable(0, true); + t->set_checked(0, f_cnt_all.has(E)); + } else if (country_code->get_text() == E) { + t->select(0); + } + t->set_text(0, vformat("%s [%s]", cnt, E)); + t->set_metadata(0, E); + } + } + updating_lists = false; +} + +void EditorLocaleDialog::set_locale(const String &p_locale) { + const String &locale = TranslationServer::get_singleton()->standardize_locale(p_locale); + if (locale.is_empty()) { + locale_set = false; + + lang_code->set_text(""); + script_code->set_text(""); + country_code->set_text(""); + variant_code->set_text(""); + } else { + locale_set = true; + + Vector<String> locale_elements = p_locale.split("_"); + lang_code->set_text(locale_elements[0]); + if (locale_elements.size() >= 2) { + if (locale_elements[1].length() == 4 && is_upper_case(locale_elements[1][0]) && is_lower_case(locale_elements[1][1]) && is_lower_case(locale_elements[1][2]) && is_lower_case(locale_elements[1][3])) { + script_code->set_text(locale_elements[1]); + advanced->set_pressed(true); + } + if (locale_elements[1].length() == 2 && is_upper_case(locale_elements[1][0]) && is_upper_case(locale_elements[1][1])) { + country_code->set_text(locale_elements[1]); + } + } + if (locale_elements.size() >= 3) { + if (locale_elements[2].length() == 2 && is_upper_case(locale_elements[2][0]) && is_upper_case(locale_elements[2][1])) { + country_code->set_text(locale_elements[2]); + } else { + variant_code->set_text(locale_elements[2].to_lower()); + advanced->set_pressed(true); + } + } + if (locale_elements.size() >= 4) { + variant_code->set_text(locale_elements[3].to_lower()); + advanced->set_pressed(true); + } + } +} + +void EditorLocaleDialog::popup_locale_dialog() { + popup_centered_clamped(Size2(1050, 700) * EDSCALE, 0.8); +} + +EditorLocaleDialog::EditorLocaleDialog() { + undo_redo = EditorNode::get_undo_redo(); + + set_title(TTR("Select a Locale")); + + VBoxContainer *vb = memnew(VBoxContainer); + { + HBoxContainer *hb_filter = memnew(HBoxContainer); + { + filter_mode = memnew(OptionButton); + filter_mode->add_item(TTR("Show All Locales"), SHOW_ALL_LOCALES); + filter_mode->set_h_size_flags(Control::SIZE_EXPAND_FILL); + filter_mode->add_item(TTR("Show Selected Locales Only"), SHOW_ONLY_SELECTED_LOCALES); + filter_mode->select(0); + filter_mode->connect("item_selected", callable_mp(this, &EditorLocaleDialog::_filter_mode_changed)); + hb_filter->add_child(filter_mode); + } + { + edit_filters = memnew(CheckButton); + edit_filters->set_text("Edit Filters"); + edit_filters->set_toggle_mode(true); + edit_filters->set_pressed(false); + edit_filters->connect("toggled", callable_mp(this, &EditorLocaleDialog::_edit_filters)); + hb_filter->add_child(edit_filters); + } + { + advanced = memnew(CheckButton); + advanced->set_text("Advanced"); + advanced->set_toggle_mode(true); + advanced->set_pressed(false); + advanced->connect("toggled", callable_mp(this, &EditorLocaleDialog::_toggle_advanced)); + hb_filter->add_child(advanced); + } + vb->add_child(hb_filter); + } + { + HBoxContainer *hb_lists = memnew(HBoxContainer); + hb_lists->set_v_size_flags(Control::SIZE_EXPAND_FILL); + { + VBoxContainer *vb_lang_list = memnew(VBoxContainer); + vb_lang_list->set_h_size_flags(Control::SIZE_EXPAND_FILL); + { + Label *lang_lbl = memnew(Label); + lang_lbl->set_text(TTR("Language:")); + vb_lang_list->add_child(lang_lbl); + } + { + lang_list = memnew(Tree); + lang_list->set_v_size_flags(Control::SIZE_EXPAND_FILL); + lang_list->connect("cell_selected", callable_mp(this, &EditorLocaleDialog::_item_selected)); + lang_list->set_columns(1); + lang_list->connect("item_edited", callable_mp(this, &EditorLocaleDialog::_filter_lang_option_changed)); + vb_lang_list->add_child(lang_list); + } + hb_lists->add_child(vb_lang_list); + } + { + vb_script_list = memnew(VBoxContainer); + vb_script_list->set_h_size_flags(Control::SIZE_EXPAND_FILL); + { + Label *script_lbl = memnew(Label); + script_lbl->set_text(TTR("Script:")); + vb_script_list->add_child(script_lbl); + } + { + script_list = memnew(Tree); + script_list->set_v_size_flags(Control::SIZE_EXPAND_FILL); + script_list->connect("cell_selected", callable_mp(this, &EditorLocaleDialog::_item_selected)); + script_list->set_columns(1); + script_list->connect("item_edited", callable_mp(this, &EditorLocaleDialog::_filter_script_option_changed)); + vb_script_list->add_child(script_list); + } + hb_lists->add_child(vb_script_list); + } + { + VBoxContainer *vb_cnt_list = memnew(VBoxContainer); + vb_cnt_list->set_h_size_flags(Control::SIZE_EXPAND_FILL); + { + Label *cnt_lbl = memnew(Label); + cnt_lbl->set_text(TTR("Country:")); + vb_cnt_list->add_child(cnt_lbl); + } + { + cnt_list = memnew(Tree); + cnt_list->set_v_size_flags(Control::SIZE_EXPAND_FILL); + cnt_list->connect("cell_selected", callable_mp(this, &EditorLocaleDialog::_item_selected)); + cnt_list->set_columns(1); + cnt_list->connect("item_edited", callable_mp(this, &EditorLocaleDialog::_filter_cnt_option_changed)); + vb_cnt_list->add_child(cnt_list); + } + hb_lists->add_child(vb_cnt_list); + } + vb->add_child(hb_lists); + } + { + hb_locale = memnew(HBoxContainer); + hb_locale->set_h_size_flags(Control::SIZE_EXPAND_FILL); + { + { + VBoxContainer *vb_language = memnew(VBoxContainer); + vb_language->set_h_size_flags(Control::SIZE_EXPAND_FILL); + { + Label *language_lbl = memnew(Label); + language_lbl->set_text(TTR("Language")); + vb_language->add_child(language_lbl); + } + { + lang_code = memnew(LineEdit); + lang_code->set_max_length(3); + lang_code->set_tooltip("Language"); + vb_language->add_child(lang_code); + } + hb_locale->add_child(vb_language); + } + { + VBoxContainer *vb_script = memnew(VBoxContainer); + vb_script->set_h_size_flags(Control::SIZE_EXPAND_FILL); + { + Label *script_lbl = memnew(Label); + script_lbl->set_text(TTR("Script")); + vb_script->add_child(script_lbl); + } + { + script_code = memnew(LineEdit); + script_code->set_max_length(4); + script_code->set_tooltip("Script"); + vb_script->add_child(script_code); + } + hb_locale->add_child(vb_script); + } + { + VBoxContainer *vb_country = memnew(VBoxContainer); + vb_country->set_h_size_flags(Control::SIZE_EXPAND_FILL); + { + Label *country_lbl = memnew(Label); + country_lbl->set_text(TTR("Country")); + vb_country->add_child(country_lbl); + } + { + country_code = memnew(LineEdit); + country_code->set_max_length(2); + country_code->set_tooltip("Country"); + vb_country->add_child(country_code); + } + hb_locale->add_child(vb_country); + } + { + VBoxContainer *vb_variant = memnew(VBoxContainer); + vb_variant->set_h_size_flags(Control::SIZE_EXPAND_FILL); + { + Label *variant_lbl = memnew(Label); + variant_lbl->set_text(TTR("Variant")); + vb_variant->add_child(variant_lbl); + } + { + variant_code = memnew(LineEdit); + variant_code->set_h_size_flags(Control::SIZE_EXPAND_FILL); + variant_code->set_placeholder("Variant"); + variant_code->set_tooltip("Variant"); + vb_variant->add_child(variant_code); + } + hb_locale->add_child(vb_variant); + } + } + vb->add_child(hb_locale); + } + add_child(vb); + _update_tree(); + + get_ok_button()->set_text(TTR("Select")); +} diff --git a/editor/editor_locale_dialog.h b/editor/editor_locale_dialog.h new file mode 100644 index 0000000000..7a4828e83a --- /dev/null +++ b/editor/editor_locale_dialog.h @@ -0,0 +1,93 @@ +/*************************************************************************/ +/* editor_locale_dialog.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef EDITOR_LOCALE_DIALOG_H +#define EDITOR_LOCALE_DIALOG_H + +#include "core/string/translation.h" +#include "scene/gui/dialogs.h" + +class Button; +class HBoxContainer; +class VBoxContainer; +class LineEdit; +class Tree; +class OptionButton; +class UndoRedo; + +class EditorLocaleDialog : public ConfirmationDialog { + GDCLASS(EditorLocaleDialog, ConfirmationDialog); + + enum LocaleFilter { + SHOW_ALL_LOCALES, + SHOW_ONLY_SELECTED_LOCALES, + }; + + HBoxContainer *hb_locale = nullptr; + VBoxContainer *vb_script_list = nullptr; + OptionButton *filter_mode = nullptr; + Button *edit_filters = nullptr; + Button *advanced = nullptr; + LineEdit *lang_code = nullptr; + LineEdit *script_code = nullptr; + LineEdit *country_code = nullptr; + LineEdit *variant_code = nullptr; + Tree *lang_list = nullptr; + Tree *script_list = nullptr; + Tree *cnt_list = nullptr; + + UndoRedo *undo_redo = nullptr; + + bool locale_set = false; + bool updating_lists = false; + +protected: + static void _bind_methods(); + virtual void _post_popup() override; + virtual void ok_pressed() override; + + void _item_selected(); + void _filter_lang_option_changed(); + void _filter_script_option_changed(); + void _filter_cnt_option_changed(); + void _filter_mode_changed(int p_mode); + void _edit_filters(bool p_checked); + void _toggle_advanced(bool p_checked); + + void _update_tree(); + +public: + EditorLocaleDialog(); + + void set_locale(const String &p_locale); + void popup_locale_dialog(); +}; + +#endif // EDITOR_LOCALE_DIALOG_H diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index cc92d391d9..e64f60c58d 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -2307,7 +2307,6 @@ void EditorNode::_edit_current(bool p_skip_foreign) { } inspector_dock->update(current_obj); - inspector_dock->update_keying(); } void EditorNode::_run(bool p_current, const String &p_custom) { diff --git a/editor/editor_node.h b/editor/editor_node.h index ff56040297..487bde3cb4 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -884,7 +884,6 @@ public: void edit_current() { _edit_current(); }; - void update_keying() const { inspector_dock->update_keying(); }; bool has_scenes_in_session(); int execute_and_show_output(const String &p_title, const String &p_path, const List<String> &p_arguments, bool p_close_on_ok = true, bool p_close_on_errors = false); diff --git a/editor/editor_plugin.cpp b/editor/editor_plugin.cpp index aeca340cb1..6579b1eb31 100644 --- a/editor/editor_plugin.cpp +++ b/editor/editor_plugin.cpp @@ -421,14 +421,10 @@ void EditorPlugin::add_control_to_container(CustomControlContainer p_location, C } break; case CONTAINER_SPATIAL_EDITOR_SIDE_LEFT: { - Node3DEditor::get_singleton()->get_palette_split()->add_child(p_control); - Node3DEditor::get_singleton()->get_palette_split()->move_child(p_control, 0); - + Node3DEditor::get_singleton()->add_control_to_left_panel(p_control); } break; case CONTAINER_SPATIAL_EDITOR_SIDE_RIGHT: { - Node3DEditor::get_singleton()->get_palette_split()->add_child(p_control); - Node3DEditor::get_singleton()->get_palette_split()->move_child(p_control, 1); - + Node3DEditor::get_singleton()->add_control_to_right_panel(p_control); } break; case CONTAINER_SPATIAL_EDITOR_BOTTOM: { Node3DEditor::get_singleton()->get_shader_split()->add_child(p_control); @@ -439,14 +435,10 @@ void EditorPlugin::add_control_to_container(CustomControlContainer p_location, C } break; case CONTAINER_CANVAS_EDITOR_SIDE_LEFT: { - CanvasItemEditor::get_singleton()->get_palette_split()->add_child(p_control); - CanvasItemEditor::get_singleton()->get_palette_split()->move_child(p_control, 0); - + CanvasItemEditor::get_singleton()->add_control_to_left_panel(p_control); } break; case CONTAINER_CANVAS_EDITOR_SIDE_RIGHT: { - CanvasItemEditor::get_singleton()->get_palette_split()->add_child(p_control); - CanvasItemEditor::get_singleton()->get_palette_split()->move_child(p_control, 1); - + CanvasItemEditor::get_singleton()->add_control_to_right_panel(p_control); } break; case CONTAINER_CANVAS_EDITOR_BOTTOM: { CanvasItemEditor::get_singleton()->get_bottom_split()->add_child(p_control); @@ -481,10 +473,11 @@ void EditorPlugin::remove_control_from_container(CustomControlContainer p_locati Node3DEditor::get_singleton()->remove_control_from_menu_panel(p_control); } break; - case CONTAINER_SPATIAL_EDITOR_SIDE_LEFT: + case CONTAINER_SPATIAL_EDITOR_SIDE_LEFT: { + Node3DEditor::get_singleton()->remove_control_from_left_panel(p_control); + } break; case CONTAINER_SPATIAL_EDITOR_SIDE_RIGHT: { - Node3DEditor::get_singleton()->get_palette_split()->remove_child(p_control); - + Node3DEditor::get_singleton()->remove_control_from_right_panel(p_control); } break; case CONTAINER_SPATIAL_EDITOR_BOTTOM: { Node3DEditor::get_singleton()->get_shader_split()->remove_child(p_control); @@ -494,10 +487,11 @@ void EditorPlugin::remove_control_from_container(CustomControlContainer p_locati CanvasItemEditor::get_singleton()->remove_control_from_menu_panel(p_control); } break; - case CONTAINER_CANVAS_EDITOR_SIDE_LEFT: + case CONTAINER_CANVAS_EDITOR_SIDE_LEFT: { + CanvasItemEditor::get_singleton()->remove_control_from_left_panel(p_control); + } break; case CONTAINER_CANVAS_EDITOR_SIDE_RIGHT: { - CanvasItemEditor::get_singleton()->get_palette_split()->remove_child(p_control); - + CanvasItemEditor::get_singleton()->remove_control_from_right_panel(p_control); } break; case CONTAINER_CANVAS_EDITOR_BOTTOM: { CanvasItemEditor::get_singleton()->get_bottom_split()->remove_child(p_control); diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index 97a38b9200..6bb4b5e81b 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -343,6 +343,64 @@ EditorPropertyTextEnum::EditorPropertyTextEnum() { add_focusable(cancel_button); } +//////////////////// LOCALE //////////////////////// + +void EditorPropertyLocale::_locale_selected(const String &p_locale) { + emit_changed(get_edited_property(), p_locale); + update_property(); +} + +void EditorPropertyLocale::_locale_pressed() { + if (!dialog) { + dialog = memnew(EditorLocaleDialog); + dialog->connect("locale_selected", callable_mp(this, &EditorPropertyLocale::_locale_selected)); + add_child(dialog); + } + + String locale_code = get_edited_object()->get(get_edited_property()); + dialog->set_locale(locale_code); + dialog->popup_locale_dialog(); +} + +void EditorPropertyLocale::update_property() { + String locale_code = get_edited_object()->get(get_edited_property()); + locale->set_text(locale_code); + locale->set_tooltip(locale_code); +} + +void EditorPropertyLocale::setup(const String &p_hint_text) { +} + +void EditorPropertyLocale::_notification(int p_what) { + if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) { + locale_edit->set_icon(get_theme_icon(SNAME("Translation"), SNAME("EditorIcons"))); + } +} + +void EditorPropertyLocale::_locale_focus_exited() { + _locale_selected(locale->get_text()); +} + +void EditorPropertyLocale::_bind_methods() { +} + +EditorPropertyLocale::EditorPropertyLocale() { + HBoxContainer *locale_hb = memnew(HBoxContainer); + add_child(locale_hb); + locale = memnew(LineEdit); + locale_hb->add_child(locale); + locale->connect("text_submitted", callable_mp(this, &EditorPropertyLocale::_locale_selected)); + locale->connect("focus_exited", callable_mp(this, &EditorPropertyLocale::_locale_focus_exited)); + locale->set_h_size_flags(SIZE_EXPAND_FILL); + + locale_edit = memnew(Button); + locale_edit->set_clip_text(true); + locale_hb->add_child(locale_edit); + add_focusable(locale); + dialog = nullptr; + locale_edit->connect("pressed", callable_mp(this, &EditorPropertyLocale::_locale_pressed)); +} + ///////////////////// PATH ///////////////////////// void EditorPropertyPath::_set_read_only(bool p_read_only) { @@ -3379,6 +3437,10 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_ EditorPropertyClassName *editor = memnew(EditorPropertyClassName); editor->setup("Object", p_hint_text); return editor; + } else if (p_hint == PROPERTY_HINT_LOCALE_ID) { + EditorPropertyLocale *editor = memnew(EditorPropertyLocale); + editor->setup(p_hint_text); + return editor; } else if (p_hint == PROPERTY_HINT_DIR || p_hint == PROPERTY_HINT_FILE || p_hint == PROPERTY_HINT_SAVE_FILE || p_hint == PROPERTY_HINT_GLOBAL_DIR || p_hint == PROPERTY_HINT_GLOBAL_FILE) { Vector<String> extensions = p_hint_text.split(","); bool global = p_hint == PROPERTY_HINT_GLOBAL_DIR || p_hint == PROPERTY_HINT_GLOBAL_FILE; diff --git a/editor/editor_properties.h b/editor/editor_properties.h index e62f6823a3..fdb0360d6b 100644 --- a/editor/editor_properties.h +++ b/editor/editor_properties.h @@ -33,6 +33,7 @@ #include "editor/create_dialog.h" #include "editor/editor_inspector.h" +#include "editor/editor_locale_dialog.h" #include "editor/editor_resource_picker.h" #include "editor/editor_spin_slider.h" #include "editor/property_selector.h" @@ -153,6 +154,26 @@ public: EditorPropertyPath(); }; +class EditorPropertyLocale : public EditorProperty { + GDCLASS(EditorPropertyLocale, EditorProperty); + EditorLocaleDialog *dialog; + LineEdit *locale; + Button *locale_edit; + + void _locale_selected(const String &p_locale); + void _locale_pressed(); + void _locale_focus_exited(); + +protected: + static void _bind_methods(); + void _notification(int p_what); + +public: + void setup(const String &p_hit_string); + virtual void update_property() override; + EditorPropertyLocale(); +}; + class EditorPropertyClassName : public EditorProperty { GDCLASS(EditorPropertyClassName, EditorProperty); diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index b662eb8b1c..b9291bcd0f 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -339,7 +339,6 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { { String lang_hint = "en"; String host_lang = OS::get_singleton()->get_locale(); - host_lang = TranslationServer::standardize_locale(host_lang); // Skip locales if Text server lack required features. Vector<String> locales_to_skip; @@ -365,6 +364,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { } String best; + int best_score = 0; for (const String &locale : get_editor_locales()) { // Skip locales which we can't render properly (see above comment). // Test against language code without regional variants (e.g. ur_PK). @@ -376,16 +376,16 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { lang_hint += ","; lang_hint += locale; - if (host_lang == locale) { - best = locale; - } - - if (best.is_empty() && host_lang.begins_with(locale)) { + int score = TranslationServer::get_singleton()->compare_locales(host_lang, locale); + if (score >= best_score) { best = locale; + best_score = score; + if (score == 10) { + break; // Exact match, skip the rest. + } } } - - if (best.is_empty()) { + if (best_score == 0) { best = "en"; } diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp index 5b7ea65b04..8e87ddee80 100644 --- a/editor/editor_themes.cpp +++ b/editor/editor_themes.cpp @@ -1123,6 +1123,12 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { theme->set_constant("margin_bottom", "MarginContainer", 0); theme->set_constant("hseparation", "GridContainer", default_margin_size * EDSCALE); theme->set_constant("vseparation", "GridContainer", default_margin_size * EDSCALE); + theme->set_constant("hseparation", "FlowContainer", default_margin_size * EDSCALE); + theme->set_constant("vseparation", "FlowContainer", default_margin_size * EDSCALE); + theme->set_constant("hseparation", "HFlowContainer", default_margin_size * EDSCALE); + theme->set_constant("vseparation", "HFlowContainer", default_margin_size * EDSCALE); + theme->set_constant("hseparation", "VFlowContainer", default_margin_size * EDSCALE); + theme->set_constant("vseparation", "VFlowContainer", default_margin_size * EDSCALE); // Window @@ -1213,7 +1219,22 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { theme->set_stylebox("focus", "RichTextLabel", make_empty_stylebox()); theme->set_stylebox("normal", "RichTextLabel", style_tree_bg); + // Editor help. + theme->set_color("title_color", "EditorHelp", accent_color); theme->set_color("headline_color", "EditorHelp", mono_color); + theme->set_color("text_color", "EditorHelp", font_color); + theme->set_color("comment_color", "EditorHelp", font_color * Color(1, 1, 1, 0.6)); + theme->set_color("symbol_color", "EditorHelp", font_color * Color(1, 1, 1, 0.6)); + theme->set_color("value_color", "EditorHelp", font_color * Color(1, 1, 1, 0.6)); + theme->set_color("qualifier_color", "EditorHelp", font_color * Color(1, 1, 1, 0.8)); + theme->set_color("type_color", "EditorHelp", accent_color.lerp(font_color, 0.5)); + theme->set_color("selection_color", "EditorHelp", accent_color * Color(1, 1, 1, 0.4)); + theme->set_color("link_color", "EditorHelp", accent_color.lerp(mono_color, 0.8)); + theme->set_color("code_color", "EditorHelp", accent_color.lerp(mono_color, 0.6)); + theme->set_color("kbd_color", "EditorHelp", accent_color.lerp(property_color, 0.6)); + theme->set_constant("line_separation", "EditorHelp", Math::round(6 * EDSCALE)); + theme->set_constant("table_hseparation", "EditorHelp", 16 * EDSCALE); + theme->set_constant("table_vseparation", "EditorHelp", 6 * EDSCALE); // Panel theme->set_stylebox("panel", "Panel", make_flat_stylebox(dark_color_1, 6, 4, 6, 4, corner_width)); @@ -1421,6 +1442,11 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { style_info_3d_viewport->set_border_width_all(0); theme->set_stylebox("Information3dViewport", "EditorStyles", style_info_3d_viewport); + // Asset Library. + theme->set_stylebox("panel", "AssetLib", style_content_panel); + theme->set_color("status_color", "AssetLib", Color(0.5, 0.5, 0.5)); + theme->set_icon("dismiss", "AssetLib", theme->get_icon("Close", "EditorIcons")); + // Theme editor. theme->set_color("preview_picker_overlay_color", "ThemeEditor", Color(0.1, 0.1, 0.1, 0.25)); Color theme_preview_picker_bg_color = accent_color; diff --git a/editor/icons/HFlowContainer.svg b/editor/icons/HFlowContainer.svg new file mode 100644 index 0000000000..0ab03f686e --- /dev/null +++ b/editor/icons/HFlowContainer.svg @@ -0,0 +1 @@ +<svg height="16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M3 1a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2H3zm0 2h10v10H3V3zm2 1c-.554 0-1 .446-1 1s.446 1 1 1h2c.554 0 1-.446 1-1s-.446-1-1-1H5zm4.996 0c-.554 0-1 .446-1 1s.446 1 1 1H11c.554 0 1-.446 1-1s-.446-1-1-1H9.996zM5 7c-.554 0-1 .446-1 1s.446 1 1 1 1-.446 1-1-.446-1-1-1zm3 0c-.554 0-1 .446-1 1s.446 1 1 1h3c.554 0 1-.446 1-1s-.446-1-1-1H8zm-3.004 3c-.554 0-1 .446-1 1s.446 1 1 1H9c.554 0 1-.446 1-1s-.446-1-1-1H4.996z" style="fill:#8eef97;fill-opacity:1"/></svg> diff --git a/editor/icons/VFlowContainer.svg b/editor/icons/VFlowContainer.svg new file mode 100644 index 0000000000..9023bf2245 --- /dev/null +++ b/editor/icons/VFlowContainer.svg @@ -0,0 +1 @@ +<svg height="16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M3 1a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2H3zm0 2h10v10H3V3zm7.998.998c-.554 0-1 .446-1 1v4.004c0 .554.446 1 1 1s1-.446 1-1V4.998c0-.554-.446-1-1-1zm-6 .004c-.554 0-1 .446-1 1v2c0 .554.446 1 1 1s1-.446 1-1v-2c0-.554-.446-1-1-1zm3 0c-.554 0-1 .446-1 1s.446 1 1 1 1-.446 1-1-.446-1-1-1zm0 3c-.554 0-1 .446-1 1v3c0 .554.446 1 1 1s1-.446 1-1v-3c0-.554-.446-1-1-1zm-3 1.996c-.554 0-1 .446-1 1v1.004c0 .554.446 1 1 1s1-.446 1-1V9.998c0-.554-.446-1-1-1z" style="fill:#8eef97;fill-opacity:1"/></svg> diff --git a/editor/import/dynamicfont_import_settings.cpp b/editor/import/dynamicfont_import_settings.cpp index 3151496bec..f4b1468314 100644 --- a/editor/import/dynamicfont_import_settings.cpp +++ b/editor/import/dynamicfont_import_settings.cpp @@ -442,398 +442,6 @@ void DynamicFontImportSettings::_add_glyph_range_item(int32_t p_start, int32_t p } /*************************************************************************/ -/* Languages and scripts */ -/*************************************************************************/ - -struct CodeInfo { - String name; - String code; -}; - -static CodeInfo langs[] = { - { U"Custom", U"xx" }, - { U"-", U"-" }, - { U"Abkhazian", U"ab" }, - { U"Afar", U"aa" }, - { U"Afrikaans", U"af" }, - { U"Akan", U"ak" }, - { U"Albanian", U"sq" }, - { U"Amharic", U"am" }, - { U"Arabic", U"ar" }, - { U"Aragonese", U"an" }, - { U"Armenian", U"hy" }, - { U"Assamese", U"as" }, - { U"Avaric", U"av" }, - { U"Avestan", U"ae" }, - { U"Aymara", U"ay" }, - { U"Azerbaijani", U"az" }, - { U"Bambara", U"bm" }, - { U"Bashkir", U"ba" }, - { U"Basque", U"eu" }, - { U"Belarusian", U"be" }, - { U"Bengali", U"bn" }, - { U"Bihari", U"bh" }, - { U"Bislama", U"bi" }, - { U"Bosnian", U"bs" }, - { U"Breton", U"br" }, - { U"Bulgarian", U"bg" }, - { U"Burmese", U"my" }, - { U"Catalan", U"ca" }, - { U"Chamorro", U"ch" }, - { U"Chechen", U"ce" }, - { U"Chichewa", U"ny" }, - { U"Chinese", U"zh" }, - { U"Chuvash", U"cv" }, - { U"Cornish", U"kw" }, - { U"Corsican", U"co" }, - { U"Cree", U"cr" }, - { U"Croatian", U"hr" }, - { U"Czech", U"cs" }, - { U"Danish", U"da" }, - { U"Divehi", U"dv" }, - { U"Dutch", U"nl" }, - { U"Dzongkha", U"dz" }, - { U"English", U"en" }, - { U"Esperanto", U"eo" }, - { U"Estonian", U"et" }, - { U"Ewe", U"ee" }, - { U"Faroese", U"fo" }, - { U"Fijian", U"fj" }, - { U"Finnish", U"fi" }, - { U"French", U"fr" }, - { U"Fulah", U"ff" }, - { U"Galician", U"gl" }, - { U"Georgian", U"ka" }, - { U"German", U"de" }, - { U"Greek", U"el" }, - { U"Guarani", U"gn" }, - { U"Gujarati", U"gu" }, - { U"Haitian", U"ht" }, - { U"Hausa", U"ha" }, - { U"Hebrew", U"he" }, - { U"Herero", U"hz" }, - { U"Hindi", U"hi" }, - { U"Hiri Motu", U"ho" }, - { U"Hungarian", U"hu" }, - { U"Interlingua", U"ia" }, - { U"Indonesian", U"id" }, - { U"Interlingue", U"ie" }, - { U"Irish", U"ga" }, - { U"Igbo", U"ig" }, - { U"Inupiaq", U"ik" }, - { U"Ido", U"io" }, - { U"Icelandic", U"is" }, - { U"Italian", U"it" }, - { U"Inuktitut", U"iu" }, - { U"Japanese", U"ja" }, - { U"Javanese", U"jv" }, - { U"Kalaallisut", U"kl" }, - { U"Kannada", U"kn" }, - { U"Kanuri", U"kr" }, - { U"Kashmiri", U"ks" }, - { U"Kazakh", U"kk" }, - { U"Central Khmer", U"km" }, - { U"Kikuyu", U"ki" }, - { U"Kinyarwanda", U"rw" }, - { U"Kirghiz", U"ky" }, - { U"Komi", U"kv" }, - { U"Kongo", U"kg" }, - { U"Korean", U"ko" }, - { U"Kurdish", U"ku" }, - { U"Kuanyama", U"kj" }, - { U"Latin", U"la" }, - { U"Luxembourgish", U"lb" }, - { U"Ganda", U"lg" }, - { U"Limburgan", U"li" }, - { U"Lingala", U"ln" }, - { U"Lao", U"lo" }, - { U"Lithuanian", U"lt" }, - { U"Luba-Katanga", U"lu" }, - { U"Latvian", U"lv" }, - { U"Man", U"gv" }, - { U"Macedonian", U"mk" }, - { U"Malagasy", U"mg" }, - { U"Malay", U"ms" }, - { U"Malayalam", U"ml" }, - { U"Maltese", U"mt" }, - { U"Maori", U"mi" }, - { U"Marathi", U"mr" }, - { U"Marshallese", U"mh" }, - { U"Mongolian", U"mn" }, - { U"Nauru", U"na" }, - { U"Navajo", U"nv" }, - { U"North Ndebele", U"nd" }, - { U"Nepali", U"ne" }, - { U"Ndonga", U"ng" }, - { U"Norwegian Bokmål", U"nb" }, - { U"Norwegian Nynorsk", U"nn" }, - { U"Norwegian", U"no" }, - { U"Sichuan Yi, Nuosu", U"ii" }, - { U"South Ndebele", U"nr" }, - { U"Occitan", U"oc" }, - { U"Ojibwa", U"oj" }, - { U"Church Slavic", U"cu" }, - { U"Oromo", U"om" }, - { U"Oriya", U"or" }, - { U"Ossetian", U"os" }, - { U"Punjabi", U"pa" }, - { U"Pali", U"pi" }, - { U"Persian", U"fa" }, - { U"Polish", U"pl" }, - { U"Pashto", U"ps" }, - { U"Portuguese", U"pt" }, - { U"Quechua", U"qu" }, - { U"Romansh", U"rm" }, - { U"Rundi", U"rn" }, - { U"Romanian", U"ro" }, - { U"Russian", U"ru" }, - { U"Sanskrit", U"sa" }, - { U"Sardinian", U"sc" }, - { U"Sindhi", U"sd" }, - { U"Northern Sami", U"se" }, - { U"Samoan", U"sm" }, - { U"Sango", U"sg" }, - { U"Serbian", U"sr" }, - { U"Gaelic", U"gd" }, - { U"Shona", U"sn" }, - { U"Sinhala", U"si" }, - { U"Slovak", U"sk" }, - { U"Slovenian", U"sl" }, - { U"Somali", U"so" }, - { U"Southern Sotho", U"st" }, - { U"Spanish", U"es" }, - { U"Sundanese", U"su" }, - { U"Swahili", U"sw" }, - { U"Swati", U"ss" }, - { U"Swedish", U"sv" }, - { U"Tamil", U"ta" }, - { U"Telugu", U"te" }, - { U"Tajik", U"tg" }, - { U"Thai", U"th" }, - { U"Tigrinya", U"ti" }, - { U"Tibetan", U"bo" }, - { U"Turkmen", U"tk" }, - { U"Tagalog", U"tl" }, - { U"Tswana", U"tn" }, - { U"Tonga", U"to" }, - { U"Turkish", U"tr" }, - { U"Tsonga", U"ts" }, - { U"Tatar", U"tt" }, - { U"Twi", U"tw" }, - { U"Tahitian", U"ty" }, - { U"Uighur", U"ug" }, - { U"Ukrainian", U"uk" }, - { U"Urdu", U"ur" }, - { U"Uzbek", U"uz" }, - { U"Venda", U"ve" }, - { U"Vietnamese", U"vi" }, - { U"Volapük", U"vo" }, - { U"Walloon", U"wa" }, - { U"Welsh", U"cy" }, - { U"Wolof", U"wo" }, - { U"Western Frisian", U"fy" }, - { U"Xhosa", U"xh" }, - { U"Yiddish", U"yi" }, - { U"Yoruba", U"yo" }, - { U"Zhuang", U"za" }, - { U"Zulu", U"zu" }, - { String(), String() } -}; - -static CodeInfo scripts[] = { - { U"Custom", U"Qaaa" }, - { U"-", U"-" }, - { U"Adlam", U"Adlm" }, - { U"Afaka", U"Afak" }, - { U"Caucasian Albanian", U"Aghb" }, - { U"Ahom", U"Ahom" }, - { U"Arabic", U"Arab" }, - { U"Imperial Aramaic", U"Armi" }, - { U"Armenian", U"Armn" }, - { U"Avestan", U"Avst" }, - { U"Balinese", U"Bali" }, - { U"Bamum", U"Bamu" }, - { U"Bassa Vah", U"Bass" }, - { U"Batak", U"Batk" }, - { U"Bengali", U"Beng" }, - { U"Bhaiksuki", U"Bhks" }, - { U"Blissymbols", U"Blis" }, - { U"Bopomofo", U"Bopo" }, - { U"Brahmi", U"Brah" }, - { U"Braille", U"Brai" }, - { U"Buginese", U"Bugi" }, - { U"Buhid", U"Buhd" }, - { U"Chakma", U"Cakm" }, - { U"Unified Canadian Aboriginal", U"Cans" }, - { U"Carian", U"Cari" }, - { U"Cham", U"Cham" }, - { U"Cherokee", U"Cher" }, - { U"Chorasmian", U"Chrs" }, - { U"Cirth", U"Cirt" }, - { U"Coptic", U"Copt" }, - { U"Cypro-Minoan", U"Cpmn" }, - { U"Cypriot", U"Cprt" }, - { U"Cyrillic", U"Cyrl" }, - { U"Devanagari", U"Deva" }, - { U"Dives Akuru", U"Diak" }, - { U"Dogra", U"Dogr" }, - { U"Deseret", U"Dsrt" }, - { U"Duployan", U"Dupl" }, - { U"Egyptian demotic", U"Egyd" }, - { U"Egyptian hieratic", U"Egyh" }, - { U"Egyptian hieroglyphs", U"Egyp" }, - { U"Elbasan", U"Elba" }, - { U"Elymaic", U"Elym" }, - { U"Ethiopic", U"Ethi" }, - { U"Khutsuri", U"Geok" }, - { U"Georgian", U"Geor" }, - { U"Glagolitic", U"Glag" }, - { U"Gunjala Gondi", U"Gong" }, - { U"Masaram Gondi", U"Gonm" }, - { U"Gothic", U"Goth" }, - { U"Grantha", U"Gran" }, - { U"Greek", U"Grek" }, - { U"Gujarati", U"Gujr" }, - { U"Gurmukhi", U"Guru" }, - { U"Hangul", U"Hang" }, - { U"Han", U"Hani" }, - { U"Hanunoo", U"Hano" }, - { U"Hatran", U"Hatr" }, - { U"Hebrew", U"Hebr" }, - { U"Hiragana", U"Hira" }, - { U"Anatolian Hieroglyphs", U"Hluw" }, - { U"Pahawh Hmong", U"Hmng" }, - { U"Nyiakeng Puachue Hmong", U"Hmnp" }, - { U"Old Hungarian", U"Hung" }, - { U"Indus", U"Inds" }, - { U"Old Italic", U"Ital" }, - { U"Javanese", U"Java" }, - { U"Jurchen", U"Jurc" }, - { U"Kayah Li", U"Kali" }, - { U"Katakana", U"Kana" }, - { U"Kharoshthi", U"Khar" }, - { U"Khmer", U"Khmr" }, - { U"Khojki", U"Khoj" }, - { U"Khitan large script", U"Kitl" }, - { U"Khitan small script", U"Kits" }, - { U"Kannada", U"Knda" }, - { U"Kpelle", U"Kpel" }, - { U"Kaithi", U"Kthi" }, - { U"Tai Tham", U"Lana" }, - { U"Lao", U"Laoo" }, - { U"Latin", U"Latn" }, - { U"Leke", U"Leke" }, - { U"Lepcha", U"Lepc" }, - { U"Limbu", U"Limb" }, - { U"Linear A", U"Lina" }, - { U"Linear B", U"Linb" }, - { U"Lisu", U"Lisu" }, - { U"Loma", U"Loma" }, - { U"Lycian", U"Lyci" }, - { U"Lydian", U"Lydi" }, - { U"Mahajani", U"Mahj" }, - { U"Makasar", U"Maka" }, - { U"Mandaic", U"Mand" }, - { U"Manichaean", U"Mani" }, - { U"Marchen", U"Marc" }, - { U"Mayan Hieroglyphs", U"Maya" }, - { U"Medefaidrin", U"Medf" }, - { U"Mende Kikakui", U"Mend" }, - { U"Meroitic Cursive", U"Merc" }, - { U"Meroitic Hieroglyphs", U"Mero" }, - { U"Malayalam", U"Mlym" }, - { U"Modi", U"Modi" }, - { U"Mongolian", U"Mong" }, - { U"Moon", U"Moon" }, - { U"Mro", U"Mroo" }, - { U"Meitei Mayek", U"Mtei" }, - { U"Multani", U"Mult" }, - { U"Myanmar (Burmese)", U"Mymr" }, - { U"Nandinagari", U"Nand" }, - { U"Old North Arabian", U"Narb" }, - { U"Nabataean", U"Nbat" }, - { U"Newa", U"Newa" }, - { U"Naxi Dongba", U"Nkdb" }, - { U"Nakhi Geba", U"Nkgb" }, - { U"N’Ko", U"Nkoo" }, - { U"Nüshu", U"Nshu" }, - { U"Ogham", U"Ogam" }, - { U"Ol Chiki", U"Olck" }, - { U"Old Turkic", U"Orkh" }, - { U"Oriya", U"Orya" }, - { U"Osage", U"Osge" }, - { U"Osmanya", U"Osma" }, - { U"Old Uyghur", U"Ougr" }, - { U"Palmyrene", U"Palm" }, - { U"Pau Cin Hau", U"Pauc" }, - { U"Proto-Cuneiform", U"Pcun" }, - { U"Proto-Elamite", U"Pelm" }, - { U"Old Permic", U"Perm" }, - { U"Phags-pa", U"Phag" }, - { U"Inscriptional Pahlavi", U"Phli" }, - { U"Psalter Pahlavi", U"Phlp" }, - { U"Book Pahlavi", U"Phlv" }, - { U"Phoenician", U"Phnx" }, - { U"Klingon", U"Piqd" }, - { U"Miao", U"Plrd" }, - { U"Inscriptional Parthian", U"Prti" }, - { U"Proto-Sinaitic", U"Psin" }, - { U"Ranjana", U"Ranj" }, - { U"Rejang", U"Rjng" }, - { U"Hanifi Rohingya", U"Rohg" }, - { U"Rongorongo", U"Roro" }, - { U"Runic", U"Runr" }, - { U"Samaritan", U"Samr" }, - { U"Sarati", U"Sara" }, - { U"Old South Arabian", U"Sarb" }, - { U"Saurashtra", U"Saur" }, - { U"SignWriting", U"Sgnw" }, - { U"Shavian", U"Shaw" }, - { U"Sharada", U"Shrd" }, - { U"Shuishu", U"Shui" }, - { U"Siddham", U"Sidd" }, - { U"Khudawadi", U"Sind" }, - { U"Sinhala", U"Sinh" }, - { U"Sogdian", U"Sogd" }, - { U"Old Sogdian", U"Sogo" }, - { U"Sora Sompeng", U"Sora" }, - { U"Soyombo", U"Soyo" }, - { U"Sundanese", U"Sund" }, - { U"Syloti Nagri", U"Sylo" }, - { U"Syriac", U"Syrc" }, - { U"Tagbanwa", U"Tagb" }, - { U"Takri", U"Takr" }, - { U"Tai Le", U"Tale" }, - { U"New Tai Lue", U"Talu" }, - { U"Tamil", U"Taml" }, - { U"Tangut", U"Tang" }, - { U"Tai Viet", U"Tavt" }, - { U"Telugu", U"Telu" }, - { U"Tengwar", U"Teng" }, - { U"Tifinagh", U"Tfng" }, - { U"Tagalog", U"Tglg" }, - { U"Thaana", U"Thaa" }, - { U"Thai", U"Thai" }, - { U"Tibetan", U"Tibt" }, - { U"Tirhuta", U"Tirh" }, - { U"Tangsa", U"Tnsa" }, - { U"Toto", U"Toto" }, - { U"Ugaritic", U"Ugar" }, - { U"Vai", U"Vaii" }, - { U"Visible Speech", U"Visp" }, - { U"Vithkuqi", U"Vith" }, - { U"Warang Citi", U"Wara" }, - { U"Wancho", U"Wcho" }, - { U"Woleai", U"Wole" }, - { U"Old Persian", U"Xpeo" }, - { U"Cuneiform", U"Xsux" }, - { U"Yezidi", U"Yezi" }, - { U"Yi", U"Yiii" }, - { U"Zanabazar Square", U"Zanb" }, - { String(), String() } -}; - -/*************************************************************************/ /* Page 1 callbacks: Rendering Options */ /*************************************************************************/ @@ -1159,19 +767,17 @@ void DynamicFontImportSettings::_range_update(int32_t p_start, int32_t p_end) { /*************************************************************************/ void DynamicFontImportSettings::_lang_add() { - menu_langs->set_position(lang_list->get_screen_position() + lang_list->get_local_mouse_position()); - menu_langs->reset_size(); - menu_langs->popup(); + locale_select->popup_locale_dialog(); } -void DynamicFontImportSettings::_lang_add_item(int p_option) { +void DynamicFontImportSettings::_lang_add_item(const String &p_locale) { TreeItem *lang_item = lang_list->create_item(lang_list_root); ERR_FAIL_NULL(lang_item); lang_item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); lang_item->set_editable(0, true); lang_item->set_checked(0, false); - lang_item->set_text(1, langs[p_option].code); + lang_item->set_text(1, p_locale); lang_item->set_editable(1, true); lang_item->add_button(2, lang_list->get_theme_icon("Remove", "EditorIcons"), BUTTON_REMOVE_VAR, false, TTR("Remove")); lang_item->set_button_color(2, 0, Color(1, 1, 1, 0.75)); @@ -1230,7 +836,7 @@ void DynamicFontImportSettings::_script_add_item(int p_option) { script_item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); script_item->set_editable(0, true); script_item->set_checked(0, false); - script_item->set_text(1, scripts[p_option].code); + script_item->set_text(1, script_codes[p_option]); script_item->set_editable(1, true); script_item->add_button(2, lang_list->get_theme_icon("Remove", "EditorIcons"), BUTTON_REMOVE_VAR, false, TTR("Remove")); script_item->set_button_color(2, 0, Color(1, 1, 1, 0.75)); @@ -1688,26 +1294,15 @@ DynamicFontImportSettings::DynamicFontImportSettings() { // Popup menus - menu_langs = memnew(PopupMenu); - menu_langs->set_name("Language"); - for (int i = 0; !langs[i].name.is_empty(); i++) { - if (langs[i].name == "-") { - menu_langs->add_separator(); - } else { - menu_langs->add_item(langs[i].name + " (" + langs[i].code + ")", i); - } - } - add_child(menu_langs); - menu_langs->connect("id_pressed", callable_mp(this, &DynamicFontImportSettings::_lang_add_item)); + locale_select = memnew(EditorLocaleDialog); + locale_select->connect("locale_selected", callable_mp(this, &DynamicFontImportSettings::_lang_add_item)); + add_child(locale_select); menu_scripts = memnew(PopupMenu); menu_scripts->set_name("Script"); - for (int i = 0; !scripts[i].name.is_empty(); i++) { - if (scripts[i].name == "-") { - menu_scripts->add_separator(); - } else { - menu_scripts->add_item(scripts[i].name + " (" + scripts[i].code + ")", i); - } + script_codes = TranslationServer::get_singleton()->get_all_scripts(); + for (int i = 0; i < script_codes.size(); i++) { + menu_scripts->add_item(TranslationServer::get_singleton()->get_script_name(script_codes[i]) + " (" + script_codes[i] + ")", i); } add_child(menu_scripts); menu_scripts->connect("id_pressed", callable_mp(this, &DynamicFontImportSettings::_script_add_item)); diff --git a/editor/import/dynamicfont_import_settings.h b/editor/import/dynamicfont_import_settings.h index 89665ae476..5d37f58b9b 100644 --- a/editor/import/dynamicfont_import_settings.h +++ b/editor/import/dynamicfont_import_settings.h @@ -33,6 +33,7 @@ #include "editor/editor_file_dialog.h" #include "editor/editor_inspector.h" +#include "editor/editor_locale_dialog.h" #include "editor/import/resource_importer_dynamicfont.h" @@ -67,6 +68,9 @@ class DynamicFontImportSettings : public ConfirmationDialog { List<ResourceImporter::ImportOption> options_variations; List<ResourceImporter::ImportOption> options_general; + EditorLocaleDialog *locale_select; + Vector<String> script_codes; + // Root layout Label *label_warn = nullptr; TabContainer *main_pages = nullptr; @@ -122,7 +126,6 @@ class DynamicFontImportSettings : public ConfirmationDialog { 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; @@ -142,7 +145,7 @@ class DynamicFontImportSettings : public ConfirmationDialog { Label *label_ot = nullptr; void _lang_add(); - void _lang_add_item(int p_option); + void _lang_add_item(const String &p_locale); void _lang_remove(Object *p_item, int p_column, int p_id); void _script_add(); diff --git a/editor/import/resource_importer_csv_translation.cpp b/editor/import/resource_importer_csv_translation.cpp index 448b318c64..f0ee14bdcb 100644 --- a/editor/import/resource_importer_csv_translation.cpp +++ b/editor/import/resource_importer_csv_translation.cpp @@ -99,8 +99,7 @@ Error ResourceImporterCSVTranslation::import(const String &p_source_file, const Vector<Ref<Translation>> translations; for (int i = 1; i < line.size(); i++) { - String locale = line[i]; - ERR_FAIL_COND_V_MSG(!TranslationServer::is_locale_valid(locale), ERR_PARSE_ERROR, "Error importing CSV translation: '" + locale + "' is not a valid locale."); + String locale = TranslationServer::get_singleton()->standardize_locale(line[i]); locales.push_back(locale); Ref<Translation> translation; diff --git a/editor/inspector_dock.cpp b/editor/inspector_dock.cpp index f56e868286..ce4e51e54c 100644 --- a/editor/inspector_dock.cpp +++ b/editor/inspector_dock.cpp @@ -382,20 +382,6 @@ void InspectorDock::_menu_expandall() { inspector->expand_all_folding(); } -void InspectorDock::_property_keyed(const String &p_keyed, const Variant &p_value, bool p_advance) { - AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_value_key(p_keyed, p_value, p_advance); -} - -void InspectorDock::_transform_keyed(Object *sp, const String &p_sub, const Transform3D &p_key) { - Node3D *s = Object::cast_to<Node3D>(sp); - if (!s) { - return; - } - AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_transform_key(s, p_sub, Animation::TYPE_POSITION_3D, p_key.origin); - AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_transform_key(s, p_sub, Animation::TYPE_ROTATION_3D, p_key.basis.get_rotation_quaternion()); - AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_transform_key(s, p_sub, Animation::TYPE_SCALE_3D, p_key.basis.get_scale()); -} - void InspectorDock::_warning_pressed() { warning_dialog->popup_centered(); } @@ -440,9 +426,6 @@ void InspectorDock::_notification(int p_what) { } void InspectorDock::_bind_methods() { - ClassDB::bind_method("update_keying", &InspectorDock::update_keying); - ClassDB::bind_method("_transform_keyed", &InspectorDock::_transform_keyed); // Still used by some connect_compat. - ClassDB::bind_method("_unref_resource", &InspectorDock::_unref_resource); ClassDB::bind_method("_paste_resource", &InspectorDock::_paste_resource); ClassDB::bind_method("_copy_resource", &InspectorDock::_copy_resource); @@ -547,22 +530,6 @@ void InspectorDock::go_back() { _edit_back(); } -void InspectorDock::update_keying() { - bool valid = false; - - if (AnimationPlayerEditor::get_singleton()->get_track_editor()->has_keying()) { - EditorHistory *editor_history = EditorNode::get_singleton()->get_editor_history(); - if (editor_history->get_path_size() >= 1) { - Object *obj = ObjectDB::get_instance(editor_history->get_path_object(0)); - if (Object::cast_to<Node>(obj)) { - valid = true; - } - } - } - - inspector->set_keying(valid); -} - InspectorDock::InspectorDock(EditorNode *p_editor, EditorData &p_editor_data) { set_name("Inspector"); @@ -716,7 +683,6 @@ InspectorDock::InspectorDock(EditorNode *p_editor, EditorData &p_editor_data) { inspector->set_use_filter(true); // TODO: check me inspector->connect("resource_selected", callable_mp(this, &InspectorDock::_resource_selected)); - inspector->connect("property_keyed", callable_mp(this, &InspectorDock::_property_keyed)); } InspectorDock::~InspectorDock() { diff --git a/editor/inspector_dock.h b/editor/inspector_dock.h index 94e4f67348..2f120c93b4 100644 --- a/editor/inspector_dock.h +++ b/editor/inspector_dock.h @@ -117,16 +117,12 @@ class InspectorDock : public VBoxContainer { void _select_history(int p_idx); void _prepare_history(); - void _property_keyed(const String &p_keyed, const Variant &p_value, bool p_advance); - void _transform_keyed(Object *sp, const String &p_sub, const Transform3D &p_key); - protected: static void _bind_methods(); void _notification(int p_what); public: void go_back(); - void update_keying(); void edit_resource(const Ref<Resource> &p_resource); void open_resource(const String &p_type); void clear(); diff --git a/editor/localization_editor.cpp b/editor/localization_editor.cpp index a902b070f4..1e9e2fc09b 100644 --- a/editor/localization_editor.cpp +++ b/editor/localization_editor.cpp @@ -32,6 +32,7 @@ #include "core/string/translation.h" #include "editor_node.h" +#include "editor_scale.h" #include "editor_translation_parser.h" #include "pot_generator.h" #include "scene/gui/control.h" @@ -175,10 +176,27 @@ void LocalizationEditor::_translation_res_select() { if (updating_translations) { return; } - call_deferred(SNAME("update_translations")); } +void LocalizationEditor::_translation_res_option_popup(bool p_arrow_clicked) { + TreeItem *ed = translation_remap_options->get_edited(); + ERR_FAIL_COND(!ed); + + locale_select->set_locale(ed->get_tooltip(1)); + locale_select->popup_locale_dialog(); +} + +void LocalizationEditor::_translation_res_option_selected(const String &p_locale) { + TreeItem *ed = translation_remap_options->get_edited(); + ERR_FAIL_COND(!ed); + + ed->set_text(1, TranslationServer::get_singleton()->get_locale_name(p_locale)); + ed->set_tooltip(1, p_locale); + + LocalizationEditor::_translation_res_option_changed(); +} + void LocalizationEditor::_translation_res_option_changed() { if (updating_translations) { return; @@ -198,20 +216,11 @@ void LocalizationEditor::_translation_res_option_changed() { String key = k->get_metadata(0); int idx = ed->get_metadata(0); String path = ed->get_metadata(1); - int which = ed->get_range(1); - - Vector<String> langs = TranslationServer::get_all_locales(); - - ERR_FAIL_INDEX(which, langs.size()); + String locale = ed->get_tooltip(1); ERR_FAIL_COND(!remaps.has(key)); PackedStringArray r = remaps[key]; - ERR_FAIL_INDEX(idx, r.size()); - if (translation_locales_idxs_remap.size() > which) { - r.set(idx, path + ":" + langs[translation_locales_idxs_remap[which]]); - } else { - r.set(idx, path + ":" + langs[which]); - } + r.set(idx, path + ":" + locale); remaps[key] = r; updating_translations = true; @@ -289,86 +298,6 @@ void LocalizationEditor::_translation_res_option_delete(Object *p_item, int p_co undo_redo->commit_action(); } -void LocalizationEditor::_translation_filter_option_changed() { - int sel_id = translation_locale_filter_mode->get_selected_id(); - TreeItem *t = translation_filter->get_edited(); - String locale = t->get_tooltip(0); - bool checked = t->is_checked(0); - - Variant prev; - Array f_locales_all; - - if (ProjectSettings::get_singleton()->has_setting("internationalization/locale/locale_filter")) { - f_locales_all = ProjectSettings::get_singleton()->get("internationalization/locale/locale_filter"); - prev = f_locales_all; - - if (f_locales_all.size() != 2) { - f_locales_all.clear(); - f_locales_all.append(sel_id); - f_locales_all.append(Array()); - } - } else { - f_locales_all.append(sel_id); - f_locales_all.append(Array()); - } - - Array f_locales = f_locales_all[1]; - int l_idx = f_locales.find(locale); - - if (checked) { - if (l_idx == -1) { - f_locales.append(locale); - } - } else { - if (l_idx != -1) { - f_locales.remove_at(l_idx); - } - } - - f_locales.sort(); - - undo_redo->create_action(TTR("Changed Locale Filter")); - undo_redo->add_do_property(ProjectSettings::get_singleton(), "internationalization/locale/locale_filter", f_locales_all); - undo_redo->add_undo_property(ProjectSettings::get_singleton(), "internationalization/locale/locale_filter", prev); - undo_redo->add_do_method(this, "update_translations"); - undo_redo->add_undo_method(this, "update_translations"); - undo_redo->add_do_method(this, "emit_signal", localization_changed); - undo_redo->add_undo_method(this, "emit_signal", localization_changed); - undo_redo->commit_action(); -} - -void LocalizationEditor::_translation_filter_mode_changed(int p_mode) { - int sel_id = translation_locale_filter_mode->get_selected_id(); - - Variant prev; - Array f_locales_all; - - if (ProjectSettings::get_singleton()->has_setting("internationalization/locale/locale_filter")) { - f_locales_all = ProjectSettings::get_singleton()->get("internationalization/locale/locale_filter"); - prev = f_locales_all; - - if (f_locales_all.size() != 2) { - f_locales_all.clear(); - f_locales_all.append(sel_id); - f_locales_all.append(Array()); - } else { - f_locales_all[0] = sel_id; - } - } else { - f_locales_all.append(sel_id); - f_locales_all.append(Array()); - } - - undo_redo->create_action(TTR("Changed Locale Filter Mode")); - undo_redo->add_do_property(ProjectSettings::get_singleton(), "internationalization/locale/locale_filter", f_locales_all); - undo_redo->add_undo_property(ProjectSettings::get_singleton(), "internationalization/locale/locale_filter", prev); - undo_redo->add_do_method(this, "update_translations"); - undo_redo->add_undo_method(this, "update_translations"); - undo_redo->add_do_method(this, "emit_signal", localization_changed); - undo_redo->add_undo_method(this, "emit_signal", localization_changed); - undo_redo->commit_action(); -} - void LocalizationEditor::_pot_add(const PackedStringArray &p_paths) { PackedStringArray pot_translations = ProjectSettings::get_singleton()->get("internationalization/locale/translations_pot_files"); for (int i = 0; i < p_paths.size(); i++) { @@ -452,64 +381,6 @@ void LocalizationEditor::update_translations() { } } - Vector<String> langs = TranslationServer::get_all_locales(); - Vector<String> names = TranslationServer::get_all_locale_names(); - - // Update filter tab - Array l_filter_all; - - bool is_arr_empty = true; - if (ProjectSettings::get_singleton()->has_setting("internationalization/locale/locale_filter")) { - l_filter_all = ProjectSettings::get_singleton()->get("internationalization/locale/locale_filter"); - - if (l_filter_all.size() == 2) { - translation_locale_filter_mode->select(l_filter_all[0]); - is_arr_empty = false; - } - } - if (is_arr_empty) { - l_filter_all.append(0); - l_filter_all.append(Array()); - translation_locale_filter_mode->select(0); - } - - int filter_mode = l_filter_all[0]; - Array l_filter = l_filter_all[1]; - - int s = names.size(); - bool is_short_list_when_show_all_selected = filter_mode == SHOW_ALL_LOCALES && translation_filter_treeitems.size() < s; - bool is_full_list_when_show_only_selected = filter_mode == SHOW_ONLY_SELECTED_LOCALES && translation_filter_treeitems.size() == s; - bool should_recreate_locales_list = is_short_list_when_show_all_selected || is_full_list_when_show_only_selected; - - if (!translation_locales_list_created || should_recreate_locales_list) { - translation_locales_list_created = true; - translation_filter->clear(); - root = translation_filter->create_item(nullptr); - translation_filter->set_hide_root(true); - translation_filter_treeitems.clear(); - for (int i = 0; i < s; i++) { - String n = names[i]; - String l = langs[i]; - bool is_checked = l_filter.has(l); - if (filter_mode == SHOW_ONLY_SELECTED_LOCALES && !is_checked) { - continue; - } - - TreeItem *t = translation_filter->create_item(root); - t->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); - t->set_text(0, vformat("[%s] %s", l, n)); - t->set_editable(0, true); - t->set_tooltip(0, l); - t->set_checked(0, is_checked); - translation_filter_treeitems.push_back(t); - } - } else { - for (int i = 0; i < translation_filter_treeitems.size(); i++) { - TreeItem *t = translation_filter_treeitems[i]; - t->set_checked(0, l_filter.has(t->get_tooltip(0))); - } - } - // Update translation remaps. String remap_selected; if (translation_remap->get_selected()) { @@ -524,32 +395,6 @@ void LocalizationEditor::update_translations() { translation_remap_options->set_hide_root(true); translation_res_option_add_button->set_disabled(true); - translation_locales_idxs_remap.clear(); - translation_locales_idxs_remap.resize(l_filter.size()); - int fl_idx_count = translation_locales_idxs_remap.size(); - - String langnames = ""; - int l_idx = 0; - for (int i = 0; i < names.size(); i++) { - if (filter_mode == SHOW_ONLY_SELECTED_LOCALES && fl_idx_count != 0) { - if (l_filter.size() > 0) { - if (l_filter.find(langs[i]) != -1) { - if (langnames.length() > 0) { - langnames += ","; - } - langnames += vformat("[%s] %s", langs[i], names[i]); - translation_locales_idxs_remap.write[l_idx] = i; - l_idx++; - } - } - } else { - if (i > 0) { - langnames += ","; - } - langnames += vformat("[%s] %s", langs[i], names[i]); - } - } - if (ProjectSettings::get_singleton()->has_setting("internationalization/locale/translation_remaps")) { Dictionary remaps = ProjectSettings::get_singleton()->get("internationalization/locale/translation_remaps"); List<Variant> rk; @@ -584,21 +429,11 @@ void LocalizationEditor::update_translations() { t2->set_tooltip(0, path); t2->set_metadata(0, j); t2->add_button(0, get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")), 0, false, TTR("Remove")); - t2->set_cell_mode(1, TreeItem::CELL_MODE_RANGE); - t2->set_text(1, langnames); + t2->set_cell_mode(1, TreeItem::CELL_MODE_CUSTOM); + t2->set_text(1, TranslationServer::get_singleton()->get_locale_name(locale)); t2->set_editable(1, true); t2->set_metadata(1, path); - int idx = langs.find(locale); - if (idx < 0) { - idx = 0; - } - - int f_idx = translation_locales_idxs_remap.find(idx); - if (f_idx != -1 && fl_idx_count > 0 && filter_mode == SHOW_ONLY_SELECTED_LOCALES) { - t2->set_range(1, f_idx); - } else { - t2->set_range(1, idx); - } + t2->set_tooltip(1, locale); } } } @@ -637,9 +472,6 @@ LocalizationEditor::LocalizationEditor() { updating_translations = false; localization_changed = "localization_changed"; - translation_locales_idxs_remap = Vector<int>(); - translation_locales_list_created = false; - TabContainer *translations = memnew(TabContainer); translations->set_tab_alignment(TabContainer::ALIGNMENT_LEFT); translations->set_v_size_flags(Control::SIZE_EXPAND_FILL); @@ -669,6 +501,10 @@ LocalizationEditor::LocalizationEditor() { translation_list->set_v_size_flags(Control::SIZE_EXPAND_FILL); tmc->add_child(translation_list); + locale_select = memnew(EditorLocaleDialog); + locale_select->connect("locale_selected", callable_mp(this, &LocalizationEditor::_translation_res_option_selected)); + add_child(locale_select); + translation_file_open = memnew(EditorFileDialog); translation_file_open->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILES); translation_file_open->connect("files_selected", callable_mp(this, &LocalizationEditor::_translation_add)); @@ -731,10 +567,11 @@ LocalizationEditor::LocalizationEditor() { translation_remap_options->set_column_expand(0, true); translation_remap_options->set_column_clip_content(0, true); translation_remap_options->set_column_expand(1, false); - translation_remap_options->set_column_clip_content(1, true); - translation_remap_options->set_column_custom_minimum_width(1, 200); + translation_remap_options->set_column_clip_content(1, false); + translation_remap_options->set_column_custom_minimum_width(1, 250); translation_remap_options->connect("item_edited", callable_mp(this, &LocalizationEditor::_translation_res_option_changed)); translation_remap_options->connect("button_pressed", callable_mp(this, &LocalizationEditor::_translation_res_option_delete)); + translation_remap_options->connect("custom_popup_edited", callable_mp(this, &LocalizationEditor::_translation_res_option_popup)); tmc->add_child(translation_remap_options); translation_res_option_file_open_dialog = memnew(EditorFileDialog); @@ -745,32 +582,6 @@ LocalizationEditor::LocalizationEditor() { { VBoxContainer *tvb = memnew(VBoxContainer); - tvb->set_name(TTR("Locales Filter")); - translations->add_child(tvb); - - VBoxContainer *tmc = memnew(VBoxContainer); - tmc->set_v_size_flags(Control::SIZE_EXPAND_FILL); - tvb->add_child(tmc); - - translation_locale_filter_mode = memnew(OptionButton); - translation_locale_filter_mode->add_item(TTR("Show All Locales"), SHOW_ALL_LOCALES); - translation_locale_filter_mode->add_item(TTR("Show Selected Locales Only"), SHOW_ONLY_SELECTED_LOCALES); - translation_locale_filter_mode->select(0); - translation_locale_filter_mode->connect("item_selected", callable_mp(this, &LocalizationEditor::_translation_filter_mode_changed)); - tmc->add_margin_child(TTR("Filter mode:"), translation_locale_filter_mode); - - Label *l = memnew(Label(TTR("Locales:"))); - l->set_theme_type_variation("HeaderSmall"); - tmc->add_child(l); - translation_filter = memnew(Tree); - translation_filter->set_v_size_flags(Control::SIZE_EXPAND_FILL); - translation_filter->set_columns(1); - translation_filter->connect("item_edited", callable_mp(this, &LocalizationEditor::_translation_filter_option_changed)); - tmc->add_child(translation_filter); - } - - { - VBoxContainer *tvb = memnew(VBoxContainer); tvb->set_name(TTR("POT Generation")); translations->add_child(tvb); diff --git a/editor/localization_editor.h b/editor/localization_editor.h index 4c77aca397..cad07dd336 100644 --- a/editor/localization_editor.h +++ b/editor/localization_editor.h @@ -33,18 +33,15 @@ #include "core/object/undo_redo.h" #include "editor_file_dialog.h" +#include "editor_locale_dialog.h" #include "scene/gui/tree.h" class LocalizationEditor : public VBoxContainer { GDCLASS(LocalizationEditor, VBoxContainer); - enum LocaleFilter { - SHOW_ALL_LOCALES, - SHOW_ONLY_SELECTED_LOCALES, - }; - Tree *translation_list; + EditorLocaleDialog *locale_select; EditorFileDialog *translation_file_open; Button *translation_res_option_add_button; @@ -52,11 +49,6 @@ class LocalizationEditor : public VBoxContainer { EditorFileDialog *translation_res_option_file_open_dialog; Tree *translation_remap; Tree *translation_remap_options; - Tree *translation_filter; - bool translation_locales_list_created; - OptionButton *translation_locale_filter_mode; - Vector<TreeItem *> translation_filter_treeitems; - Vector<int> translation_locales_idxs_remap; Tree *translation_pot_list; EditorFileDialog *pot_file_open_dialog; @@ -78,9 +70,8 @@ class LocalizationEditor : public VBoxContainer { void _translation_res_option_add(const PackedStringArray &p_paths); void _translation_res_option_changed(); void _translation_res_option_delete(Object *p_item, int p_column, int p_button); - - void _translation_filter_option_changed(); - void _translation_filter_mode_changed(int p_mode); + void _translation_res_option_popup(bool p_arrow_clicked); + void _translation_res_option_selected(const String &p_locale); void _pot_add(const PackedStringArray &p_paths); void _pot_delete(Object *p_item, int p_column, int p_button); diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp index d7c0ba7540..dcc549ec2a 100644 --- a/editor/plugins/animation_player_editor_plugin.cpp +++ b/editor/plugins/animation_player_editor_plugin.cpp @@ -301,7 +301,6 @@ void AnimationPlayerEditor::_animation_selected(int p_which) { autoplay->set_pressed(current == player->get_autoplay()); AnimationPlayerEditor::get_singleton()->get_track_editor()->update_keying(); - EditorNode::get_singleton()->update_keying(); _animation_key_editor_seek(timeline_position, false); } @@ -829,7 +828,6 @@ void AnimationPlayerEditor::_update_player() { if (!player) { AnimationPlayerEditor::get_singleton()->get_track_editor()->update_keying(); - EditorNode::get_singleton()->update_keying(); return; } @@ -1795,11 +1793,39 @@ AnimationPlayerEditor::~AnimationPlayerEditor() { void AnimationPlayerEditorPlugin::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { + Node3DEditor::get_singleton()->connect("transform_key_request", callable_mp(this, &AnimationPlayerEditorPlugin::_transform_key_request)); + editor->get_inspector()->connect("property_keyed", callable_mp(this, &AnimationPlayerEditorPlugin::_property_keyed)); + anim_editor->get_track_editor()->connect("keying_changed", callable_mp(this, &AnimationPlayerEditorPlugin::_update_keying)); + editor->get_inspector()->connect("edited_object_changed", callable_mp(anim_editor->get_track_editor(), &AnimationTrackEditor::update_keying)); set_force_draw_over_forwarding_enabled(); } break; } } +void AnimationPlayerEditorPlugin::_property_keyed(const String &p_keyed, const Variant &p_value, bool p_advance) { + if (!anim_editor->get_track_editor()->has_keying()) { + return; + } + anim_editor->get_track_editor()->insert_value_key(p_keyed, p_value, p_advance); +} + +void AnimationPlayerEditorPlugin::_transform_key_request(Object *sp, const String &p_sub, const Transform3D &p_key) { + if (!anim_editor->get_track_editor()->has_keying()) { + return; + } + Node3D *s = Object::cast_to<Node3D>(sp); + if (!s) { + return; + } + anim_editor->get_track_editor()->insert_transform_key(s, p_sub, Animation::TYPE_POSITION_3D, p_key.origin); + anim_editor->get_track_editor()->insert_transform_key(s, p_sub, Animation::TYPE_ROTATION_3D, p_key.basis.get_rotation_quaternion()); + anim_editor->get_track_editor()->insert_transform_key(s, p_sub, Animation::TYPE_SCALE_3D, p_key.basis.get_scale()); +} + +void AnimationPlayerEditorPlugin::_update_keying() { + editor->get_inspector()->set_keying(anim_editor->get_track_editor()->has_keying()); +} + void AnimationPlayerEditorPlugin::edit(Object *p_object) { anim_editor->set_undo_redo(&get_undo_redo()); if (!p_object) { diff --git a/editor/plugins/animation_player_editor_plugin.h b/editor/plugins/animation_player_editor_plugin.h index 626d31f439..06dca11aff 100644 --- a/editor/plugins/animation_player_editor_plugin.h +++ b/editor/plugins/animation_player_editor_plugin.h @@ -255,6 +255,10 @@ class AnimationPlayerEditorPlugin : public EditorPlugin { protected: void _notification(int p_what); + void _property_keyed(const String &p_keyed, const Variant &p_value, bool p_advance); + void _transform_key_request(Object *sp, const String &p_sub, const Transform3D &p_key); + void _update_keying(); + public: virtual Dictionary get_state() const override { return anim_editor->get_state(); } virtual void set_state(const Dictionary &p_state) override { anim_editor->set_state(p_state); } diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp index 31ef13a2eb..5fb3040b75 100644 --- a/editor/plugins/asset_library_editor_plugin.cpp +++ b/editor/plugins/asset_library_editor_plugin.cpp @@ -369,11 +369,11 @@ void EditorAssetLibraryItemDownload::_http_download_completed(int p_status, int download_error->set_text(TTR("Asset Download Error:") + "\n" + error_text); download_error->popup_centered(); // Let the user retry the download. - retry->show(); + retry_button->show(); return; } - install->set_disabled(false); + install_button->set_disabled(false); status->set_text(TTR("Success!")); // Make the progress bar invisible but don't reflow other Controls around it. progress->set_modulate(Color(0, 0, 0, 0)); @@ -381,7 +381,7 @@ void EditorAssetLibraryItemDownload::_http_download_completed(int p_status, int set_process(false); // Automatically prompt for installation once the download is completed. - _install(); + install(); } void EditorAssetLibraryItemDownload::configure(const String &p_title, int p_asset_id, const Ref<Texture2D> &p_preview, const String &p_download_url, const String &p_sha256_hash) { @@ -400,8 +400,9 @@ void EditorAssetLibraryItemDownload::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { - panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("TabContainer"))); - dismiss->set_normal_texture(get_theme_icon(SNAME("Close"), SNAME("EditorIcons"))); + panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("AssetLib"))); + status->add_theme_color_override("font_color", get_theme_color(SNAME("status_color"), SNAME("AssetLib"))); + dismiss_button->set_normal_texture(get_theme_icon(SNAME("dismiss"), SNAME("AssetLib"))); } break; case NOTIFICATION_PROCESS: { // Make the progress bar visible again when retrying the download. @@ -461,7 +462,7 @@ void EditorAssetLibraryItemDownload::_close() { queue_delete(); } -void EditorAssetLibraryItemDownload::_install() { +void EditorAssetLibraryItemDownload::install() { String file = download->get_download_file(); if (external_install) { @@ -475,7 +476,7 @@ void EditorAssetLibraryItemDownload::_install() { void EditorAssetLibraryItemDownload::_make_request() { // Hide the Retry button if we've just pressed it. - retry->hide(); + retry_button->hide(); download->cancel_request(); download->set_download_file(EditorPaths::get_singleton()->get_cache_dir().plus_file("tmp_asset_" + itos(asset_id)) + ".zip"); @@ -499,6 +500,8 @@ EditorAssetLibraryItemDownload::EditorAssetLibraryItemDownload() { HBoxContainer *hb = memnew(HBoxContainer); panel->add_child(hb); icon = memnew(TextureRect); + icon->set_stretch_mode(TextureRect::STRETCH_KEEP_ASPECT_CENTERED); + icon->set_v_size_flags(0); hb->add_child(icon); VBoxContainer *vb = memnew(VBoxContainer); @@ -511,9 +514,9 @@ EditorAssetLibraryItemDownload::EditorAssetLibraryItemDownload() { title_hb->add_child(title); title->set_h_size_flags(Control::SIZE_EXPAND_FILL); - dismiss = memnew(TextureButton); - dismiss->connect("pressed", callable_mp(this, &EditorAssetLibraryItemDownload::_close)); - title_hb->add_child(dismiss); + dismiss_button = memnew(TextureButton); + dismiss_button->connect("pressed", callable_mp(this, &EditorAssetLibraryItemDownload::_close)); + title_hb->add_child(dismiss_button); title->set_clip_text(true); @@ -521,7 +524,6 @@ EditorAssetLibraryItemDownload::EditorAssetLibraryItemDownload() { status = memnew(Label(TTR("Idle"))); vb->add_child(status); - status->add_theme_color_override("font_color", Color(0.5, 0.5, 0.5)); progress = memnew(ProgressBar); vb->add_child(progress); @@ -529,19 +531,19 @@ EditorAssetLibraryItemDownload::EditorAssetLibraryItemDownload() { vb->add_child(hb2); hb2->add_spacer(); - install = memnew(Button); - install->set_text(TTR("Install...")); - install->set_disabled(true); - install->connect("pressed", callable_mp(this, &EditorAssetLibraryItemDownload::_install)); + install_button = memnew(Button); + install_button->set_text(TTR("Install...")); + install_button->set_disabled(true); + install_button->connect("pressed", callable_mp(this, &EditorAssetLibraryItemDownload::install)); - retry = memnew(Button); - retry->set_text(TTR("Retry")); - retry->connect("pressed", callable_mp(this, &EditorAssetLibraryItemDownload::_make_request)); + retry_button = memnew(Button); + retry_button->set_text(TTR("Retry")); + retry_button->connect("pressed", callable_mp(this, &EditorAssetLibraryItemDownload::_make_request)); // Only show the Retry button in case of a failure. - retry->hide(); + retry_button->hide(); - hb2->add_child(retry); - hb2->add_child(install); + hb2->add_child(retry_button); + hb2->add_child(install_button); set_custom_minimum_size(Size2(310, 0) * EDSCALE); download = memnew(HTTPRequest); @@ -640,14 +642,10 @@ void EditorAssetLibrary::unhandled_key_input(const Ref<InputEvent> &p_event) { void EditorAssetLibrary::_install_asset() { ERR_FAIL_COND(!description); - for (int i = 0; i < downloads_hb->get_child_count(); i++) { - EditorAssetLibraryItemDownload *d = Object::cast_to<EditorAssetLibraryItemDownload>(downloads_hb->get_child(i)); - if (d && d->get_asset_id() == description->get_asset_id()) { - if (EditorNode::get_singleton() != nullptr) { - EditorNode::get_singleton()->show_warning(TTR("Download for this asset is already in progress!")); - } - return; - } + EditorAssetLibraryItemDownload *d = _get_asset_in_progress(description->get_asset_id()); + if (d) { + d->install(); + return; } EditorAssetLibraryItemDownload *download = memnew(EditorAssetLibraryItemDownload); @@ -1265,6 +1263,13 @@ void EditorAssetLibrary::_http_request_completed(int p_status, int p_code, const description->configure(r["title"], r["asset_id"], category_map[r["category_id"]], r["category_id"], r["author"], r["author_id"], r["cost"], r["version"], r["version_string"], r["description"], r["download_url"], r["browse_url"], r["download_hash"]); + EditorAssetLibraryItemDownload *download_item = _get_asset_in_progress(description->get_asset_id()); + if (download_item) { + description->get_ok_button()->set_text(TTR("Install")); + } else { + description->get_ok_button()->set_text(TTR("Download")); + } + if (r.has("icon_url") && !r["icon_url"].operator String().is_empty()) { _request_image(description->get_instance_id(), r["icon_url"], IMAGE_QUEUE_ICON, 0); } @@ -1322,6 +1327,17 @@ void EditorAssetLibrary::_manage_plugins() { ProjectSettingsEditor::get_singleton()->set_plugins_page(); } +EditorAssetLibraryItemDownload *EditorAssetLibrary::_get_asset_in_progress(int p_asset_id) const { + for (int i = 0; i < downloads_hb->get_child_count(); i++) { + EditorAssetLibraryItemDownload *d = Object::cast_to<EditorAssetLibraryItemDownload>(downloads_hb->get_child(i)); + if (d && d->get_asset_id() == p_asset_id) { + return d; + } + } + + return nullptr; +} + void EditorAssetLibrary::_install_external_asset(String p_zip_path, String p_title) { emit_signal(SNAME("install_asset"), p_zip_path, p_title); } diff --git a/editor/plugins/asset_library_editor_plugin.h b/editor/plugins/asset_library_editor_plugin.h index 8d6c0eb76e..058aafc221 100644 --- a/editor/plugins/asset_library_editor_plugin.h +++ b/editor/plugins/asset_library_editor_plugin.h @@ -39,6 +39,7 @@ #include "scene/gui/grid_container.h" #include "scene/gui/line_edit.h" #include "scene/gui/link_button.h" +#include "scene/gui/margin_container.h" #include "scene/gui/option_button.h" #include "scene/gui/panel_container.h" #include "scene/gui/progress_bar.h" @@ -126,16 +127,16 @@ public: EditorAssetLibraryItemDescription(); }; -class EditorAssetLibraryItemDownload : public Control { - GDCLASS(EditorAssetLibraryItemDownload, Control); +class EditorAssetLibraryItemDownload : public MarginContainer { + GDCLASS(EditorAssetLibraryItemDownload, MarginContainer); PanelContainer *panel; TextureRect *icon; Label *title; ProgressBar *progress; - Button *install; - Button *retry; - TextureButton *dismiss; + Button *install_button; + Button *retry_button; + TextureButton *dismiss_button; AcceptDialog *download_error; HTTPRequest *download; @@ -152,7 +153,6 @@ class EditorAssetLibraryItemDownload : public Control { EditorAssetInstaller *asset_installer; void _close(); - void _install(); void _make_request(); void _http_download_completed(int p_status, int p_code, const PackedStringArray &headers, const PackedByteArray &p_data); @@ -164,6 +164,7 @@ public: void set_external_install(bool p_enable) { external_install = p_enable; } int get_asset_id() { return asset_id; } void configure(const String &p_title, int p_asset_id, const Ref<Texture2D> &p_preview, const String &p_download_url, const String &p_sha256_hash); + void install(); EditorAssetLibraryItemDownload(); }; @@ -287,6 +288,7 @@ class EditorAssetLibrary : public PanelContainer { void _api_request(const String &p_request, RequestType p_request_type, const String &p_arguments = ""); void _http_request_completed(int p_status, int p_code, const PackedStringArray &headers, const PackedByteArray &p_data); void _filter_debounce_timer_timeout(); + EditorAssetLibraryItemDownload *_get_asset_in_progress(int p_asset_id) const; void _repository_changed(int p_repository_id); void _support_toggled(int p_support); diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 970d53a137..14f488f096 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -5129,8 +5129,22 @@ void CanvasItemEditor::remove_control_from_menu_panel(Control *p_control) { hbc_context_menu->remove_child(p_control); } -HSplitContainer *CanvasItemEditor::get_palette_split() { - return palette_split; +void CanvasItemEditor::add_control_to_left_panel(Control *p_control) { + left_panel_split->add_child(p_control); + left_panel_split->move_child(p_control, 0); +} + +void CanvasItemEditor::add_control_to_right_panel(Control *p_control) { + right_panel_split->add_child(p_control); + right_panel_split->move_child(p_control, 1); +} + +void CanvasItemEditor::remove_control_from_left_panel(Control *p_control) { + left_panel_split->remove_child(p_control); +} + +void CanvasItemEditor::remove_control_from_right_panel(Control *p_control) { + right_panel_split->remove_child(p_control); } VSplitContainer *CanvasItemEditor::get_bottom_split() { @@ -5221,12 +5235,16 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { add_child(bottom_split); bottom_split->set_v_size_flags(Control::SIZE_EXPAND_FILL); - palette_split = memnew(HSplitContainer); - bottom_split->add_child(palette_split); - palette_split->set_v_size_flags(Control::SIZE_EXPAND_FILL); + left_panel_split = memnew(HSplitContainer); + bottom_split->add_child(left_panel_split); + left_panel_split->set_v_size_flags(Control::SIZE_EXPAND_FILL); + + right_panel_split = memnew(HSplitContainer); + left_panel_split->add_child(right_panel_split); + right_panel_split->set_v_size_flags(Control::SIZE_EXPAND_FILL); viewport_scrollable = memnew(Control); - palette_split->add_child(viewport_scrollable); + right_panel_split->add_child(viewport_scrollable); viewport_scrollable->set_mouse_filter(MOUSE_FILTER_PASS); viewport_scrollable->set_clip_contents(true); viewport_scrollable->set_v_size_flags(Control::SIZE_EXPAND_FILL); diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h index 8bba5130d4..d58fb17356 100644 --- a/editor/plugins/canvas_item_editor_plugin.h +++ b/editor/plugins/canvas_item_editor_plugin.h @@ -527,7 +527,8 @@ private: void _update_override_camera_button(bool p_game_running); - HSplitContainer *palette_split; + HSplitContainer *left_panel_split; + HSplitContainer *right_panel_split; VSplitContainer *bottom_split; void _update_context_menu_stylebox(); @@ -571,7 +572,12 @@ public: void add_control_to_menu_panel(Control *p_control); void remove_control_from_menu_panel(Control *p_control); - HSplitContainer *get_palette_split(); + void add_control_to_left_panel(Control *p_control); + void remove_control_from_left_panel(Control *p_control); + + void add_control_to_right_panel(Control *p_control); + void remove_control_from_right_panel(Control *p_control); + VSplitContainer *get_bottom_split(); Control *get_viewport_control() { return viewport; } diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index ef9c6a65e7..44f8d1a2bb 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -6880,8 +6880,46 @@ VSplitContainer *Node3DEditor::get_shader_split() { return shader_split; } -HSplitContainer *Node3DEditor::get_palette_split() { - return palette_split; +void Node3DEditor::add_control_to_left_panel(Control *p_control) { + left_panel_split->add_child(p_control); + left_panel_split->move_child(p_control, 0); +} + +void Node3DEditor::add_control_to_right_panel(Control *p_control) { + right_panel_split->add_child(p_control); + right_panel_split->move_child(p_control, 1); +} + +void Node3DEditor::remove_control_from_left_panel(Control *p_control) { + left_panel_split->remove_child(p_control); +} + +void Node3DEditor::remove_control_from_right_panel(Control *p_control) { + right_panel_split->remove_child(p_control); +} + +void Node3DEditor::move_control_to_left_panel(Control *p_control) { + ERR_FAIL_NULL(p_control); + if (p_control->get_parent() == left_panel_split) { + return; + } + + ERR_FAIL_COND(p_control->get_parent() != right_panel_split); + right_panel_split->remove_child(p_control); + + add_control_to_left_panel(p_control); +} + +void Node3DEditor::move_control_to_right_panel(Control *p_control) { + ERR_FAIL_NULL(p_control); + if (p_control->get_parent() == right_panel_split) { + return; + } + + ERR_FAIL_COND(p_control->get_parent() != left_panel_split); + left_panel_split->remove_child(p_control); + + add_control_to_right_panel(p_control); } void Node3DEditor::_request_gizmo(Object *p_obj) { @@ -7534,13 +7572,17 @@ Node3DEditor::Node3DEditor(EditorNode *p_editor) { /* REST OF MENU */ - palette_split = memnew(HSplitContainer); - palette_split->set_v_size_flags(SIZE_EXPAND_FILL); - vbc->add_child(palette_split); + left_panel_split = memnew(HSplitContainer); + left_panel_split->set_v_size_flags(SIZE_EXPAND_FILL); + vbc->add_child(left_panel_split); + + right_panel_split = memnew(HSplitContainer); + right_panel_split->set_v_size_flags(SIZE_EXPAND_FILL); + left_panel_split->add_child(right_panel_split); shader_split = memnew(VSplitContainer); shader_split->set_h_size_flags(SIZE_EXPAND_FILL); - palette_split->add_child(shader_split); + right_panel_split->add_child(shader_split); viewport_base = memnew(Node3DEditorViewportContainer); shader_split->add_child(viewport_base); viewport_base->set_v_size_flags(SIZE_EXPAND_FILL); @@ -8001,7 +8043,6 @@ Node3DEditorPlugin::Node3DEditorPlugin(EditorNode *p_node) { editor->get_main_control()->add_child(spatial_editor); spatial_editor->hide(); - spatial_editor->connect("transform_key_request", Callable(editor->get_inspector_dock(), "_transform_keyed")); } Node3DEditorPlugin::~Node3DEditorPlugin() { diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h index da560f4d83..20a782c8a8 100644 --- a/editor/plugins/node_3d_editor_plugin.h +++ b/editor/plugins/node_3d_editor_plugin.h @@ -527,7 +527,8 @@ private: Node3DEditorViewportContainer *viewport_base; Node3DEditorViewport *viewports[VIEWPORTS_COUNT]; VSplitContainer *shader_split; - HSplitContainer *palette_split; + HSplitContainer *left_panel_split; + HSplitContainer *right_panel_split; ///// @@ -801,8 +802,16 @@ public: void add_control_to_menu_panel(Control *p_control); void remove_control_from_menu_panel(Control *p_control); + void add_control_to_left_panel(Control *p_control); + void remove_control_from_left_panel(Control *p_control); + + void add_control_to_right_panel(Control *p_control); + void remove_control_from_right_panel(Control *p_control); + + void move_control_to_left_panel(Control *p_control); + void move_control_to_right_panel(Control *p_control); + VSplitContainer *get_shader_split(); - HSplitContainer *get_palette_split(); Node3D *get_single_selected_node() { return selected; } bool is_current_selected_gizmo(const EditorNode3DGizmo *p_gizmo); diff --git a/editor/plugins/skeleton_3d_editor_plugin.cpp b/editor/plugins/skeleton_3d_editor_plugin.cpp index e1b27cb045..169ce29438 100644 --- a/editor/plugins/skeleton_3d_editor_plugin.cpp +++ b/editor/plugins/skeleton_3d_editor_plugin.cpp @@ -149,6 +149,9 @@ void BoneTransformEditor::set_target(const String &p_prop) { void BoneTransformEditor::_property_keyed(const String &p_path, bool p_advance) { AnimationTrackEditor *te = AnimationPlayerEditor::get_singleton()->get_track_editor(); + if (!te->has_keying()) { + return; + } PackedStringArray split = p_path.split("/"); if (split.size() == 3 && split[0] == "bones") { int bone_idx = split[1].to_int(); diff --git a/editor/property_editor.cpp b/editor/property_editor.cpp index 481ff1a781..c971bb6473 100644 --- a/editor/property_editor.cpp +++ b/editor/property_editor.cpp @@ -506,12 +506,16 @@ bool CustomPropertyEditor::edit(Object *p_owner, const String &p_name, Variant:: } break; case Variant::STRING: { - if (hint == PROPERTY_HINT_FILE || hint == PROPERTY_HINT_GLOBAL_FILE) { + if (hint == PROPERTY_HINT_LOCALE_ID) { + List<String> names; + names.push_back(TTR("Locale...")); + names.push_back(TTR("Clear")); + config_action_buttons(names); + } else if (hint == PROPERTY_HINT_FILE || hint == PROPERTY_HINT_GLOBAL_FILE) { List<String> names; names.push_back(TTR("File...")); names.push_back(TTR("Clear")); config_action_buttons(names); - } else if (hint == PROPERTY_HINT_DIR || hint == PROPERTY_HINT_GLOBAL_DIR) { List<String> names; names.push_back(TTR("Dir...")); @@ -1034,6 +1038,14 @@ void CustomPropertyEditor::_file_selected(String p_file) { } } +void CustomPropertyEditor::_locale_selected(String p_locale) { + if (type == Variant::STRING && hint == PROPERTY_HINT_LOCALE_ID) { + v = p_locale; + emit_signal(SNAME("variant_changed")); + hide(); + } +} + void CustomPropertyEditor::_type_create_selected(int p_idx) { if (type == Variant::INT || type == Variant::FLOAT) { float newval = 0; @@ -1177,7 +1189,8 @@ void CustomPropertyEditor::_action_pressed(int p_which) { case Variant::STRING: { if (hint == PROPERTY_HINT_MULTILINE_TEXT) { hide(); - + } else if (hint == PROPERTY_HINT_LOCALE_ID) { + locale->popup_locale_dialog(); } else if (hint == PROPERTY_HINT_FILE || hint == PROPERTY_HINT_GLOBAL_FILE) { if (p_which == 0) { if (hint == PROPERTY_HINT_FILE) { @@ -1821,6 +1834,12 @@ CustomPropertyEditor::CustomPropertyEditor() { file->connect("file_selected", callable_mp(this, &CustomPropertyEditor::_file_selected)); file->connect("dir_selected", callable_mp(this, &CustomPropertyEditor::_file_selected)); + locale = memnew(EditorLocaleDialog); + value_vbox->add_child(locale); + locale->hide(); + + locale->connect("locale_selected", callable_mp(this, &CustomPropertyEditor::_locale_selected)); + error = memnew(ConfirmationDialog); error->set_title(TTR("Error!")); value_vbox->add_child(error); diff --git a/editor/property_editor.h b/editor/property_editor.h index 9d88aaf26d..298acb3c01 100644 --- a/editor/property_editor.h +++ b/editor/property_editor.h @@ -32,6 +32,7 @@ #define PROPERTY_EDITOR_H #include "editor/editor_file_dialog.h" +#include "editor/editor_locale_dialog.h" #include "editor/scene_tree_editor.h" #include "scene/gui/button.h" #include "scene/gui/check_box.h" @@ -97,6 +98,7 @@ class CustomPropertyEditor : public PopupPanel { PopupMenu *menu; SceneTreeDialog *scene_tree; EditorFileDialog *file; + EditorLocaleDialog *locale; ConfirmationDialog *error; String name; Variant::Type type; @@ -136,6 +138,7 @@ class CustomPropertyEditor : public PopupPanel { void _text_edit_changed(); void _file_selected(String p_file); + void _locale_selected(String p_locale); void _modified(String p_string); real_t _parse_real_expression(String text); diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index 9d382e160c..c9acb7b668 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -445,8 +445,11 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { break; } + bool was_empty = false; if (!node_clipboard.is_empty()) { _clear_clipboard(); + } else { + was_empty = true; } clipboard_source_scene = editor->get_edited_scene()->get_scene_file_path(); @@ -464,81 +467,13 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { if (p_tool == TOOL_CUT) { _delete_confirm(true); } - } break; - case TOOL_PASTE: { - if (node_clipboard.is_empty() || !edited_scene) { - break; - } - - bool has_cycle = false; - if (!edited_scene->get_scene_file_path().is_empty()) { - for (Node *E : node_clipboard) { - if (edited_scene->get_scene_file_path() == E->get_scene_file_path()) { - has_cycle = true; - break; - } - } - } - - if (has_cycle) { - current_option = -1; - accept->set_text(TTR("Can't paste root node into the same scene.")); - accept->popup_centered(); - break; - } - Node *paste_parent = edited_scene; - List<Node *> selection = editor_selection->get_selected_node_list(); - if (selection.size() > 0) { - paste_parent = selection.back()->get(); + if (was_empty) { + _update_create_root_dialog(); } - - Node *owner = paste_parent->get_owner(); - if (!owner) { - owner = paste_parent; - } - - editor_data->get_undo_redo().create_action(TTR("Paste Node(s)")); - editor_data->get_undo_redo().add_do_method(editor_selection, "clear"); - - Map<RES, RES> resource_remap; - String target_scene = editor->get_edited_scene()->get_scene_file_path(); - if (target_scene != clipboard_source_scene) { - if (!clipboard_resource_remap.has(target_scene)) { - Map<RES, RES> remap; - for (Node *E : node_clipboard) { - _create_remap_for_node(E, remap); - } - clipboard_resource_remap[target_scene] = remap; - } - resource_remap = clipboard_resource_remap[target_scene]; - } - - for (Node *node : node_clipboard) { - Map<const Node *, Node *> duplimap; - - Node *dup = node->duplicate_from_editor(duplimap, resource_remap); - - ERR_CONTINUE(!dup); - - editor_data->get_undo_redo().add_do_method(paste_parent, "add_child", dup, true); - - for (KeyValue<const Node *, Node *> &E2 : duplimap) { - Node *d = E2.value; - editor_data->get_undo_redo().add_do_method(d, "set_owner", owner); - } - - editor_data->get_undo_redo().add_do_method(dup, "set_owner", owner); - editor_data->get_undo_redo().add_do_method(editor_selection, "add_node", dup); - editor_data->get_undo_redo().add_undo_method(paste_parent, "remove_child", dup); - editor_data->get_undo_redo().add_do_reference(dup); - - if (node_clipboard.size() == 1) { - editor_data->get_undo_redo().add_do_method(editor, "push_item", dup); - } - } - - editor_data->get_undo_redo().commit_action(); + } break; + case TOOL_PASTE: { + paste_nodes(); } break; case TOOL_REPLACE: { if (!profile_allow_editing) { @@ -1306,6 +1241,12 @@ void SceneTreeDock::_notification(int p_what) { button_custom->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons"))); button_custom->connect("pressed", callable_bind(callable_mp(this, &SceneTreeDock::_tool_selected), TOOL_NEW, false)); + button_clipboard = memnew(Button); + node_shortcuts->add_child(button_clipboard); + button_clipboard->set_text(TTR("Paste From Clipboard")); + button_clipboard->set_icon(get_theme_icon(SNAME("ActionPaste"), SNAME("EditorIcons"))); + button_clipboard->connect("pressed", callable_bind(callable_mp(this, &SceneTreeDock::_tool_selected), TOOL_PASTE, false)); + node_shortcuts->add_spacer(); create_root_dialog->add_child(node_shortcuts); _update_create_root_dialog(); @@ -1330,6 +1271,7 @@ void SceneTreeDock::_notification(int p_what) { button_3d->set_icon(get_theme_icon(SNAME("Node3D"), SNAME("EditorIcons"))); button_ui->set_icon(get_theme_icon(SNAME("Control"), SNAME("EditorIcons"))); button_custom->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons"))); + button_clipboard->set_icon(get_theme_icon(SNAME("ActionPaste"), SNAME("EditorIcons"))); filter->set_right_icon(get_theme_icon(SNAME("Search"), SNAME("EditorIcons"))); filter->set_clear_button_enabled(true); @@ -2740,10 +2682,10 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) { } if (profile_allow_editing) { - menu->add_shortcut(ED_GET_SHORTCUT("scene_tree/cut_node"), TOOL_CUT); - menu->add_shortcut(ED_GET_SHORTCUT("scene_tree/copy_node"), TOOL_COPY); + menu->add_icon_shortcut(get_theme_icon(SNAME("ActionCut"), SNAME("EditorIcons")), ED_GET_SHORTCUT("scene_tree/cut_node"), TOOL_CUT); + menu->add_icon_shortcut(get_theme_icon(SNAME("ActionCopy"), SNAME("EditorIcons")), ED_GET_SHORTCUT("scene_tree/copy_node"), TOOL_COPY); if (selection.size() == 1 && !node_clipboard.is_empty()) { - menu->add_shortcut(ED_GET_SHORTCUT("scene_tree/paste_node"), TOOL_PASTE); + menu->add_icon_shortcut(get_theme_icon(SNAME("ActionPaste"), SNAME("EditorIcons")), ED_GET_SHORTCUT("scene_tree/paste_node"), TOOL_PASTE); } menu->add_separator(); } @@ -3022,6 +2964,108 @@ void SceneTreeDock::open_instance_child_dialog() { _tool_selected(TOOL_INSTANTIATE, true); } +List<Node *> SceneTreeDock::paste_nodes() { + List<Node *> pasted_nodes; + + if (node_clipboard.is_empty()) { + return pasted_nodes; + } + + bool has_cycle = false; + if (edited_scene && !edited_scene->get_scene_file_path().is_empty()) { + for (Node *E : node_clipboard) { + if (edited_scene->get_scene_file_path() == E->get_scene_file_path()) { + has_cycle = true; + break; + } + } + } + + if (has_cycle) { + current_option = -1; + accept->set_text(TTR("Can't paste root node into the same scene.")); + accept->popup_centered(); + return pasted_nodes; + } + + Node *paste_parent = edited_scene; + List<Node *> selection = editor_selection->get_selected_node_list(); + if (selection.size() > 0) { + paste_parent = selection.back()->get(); + } + + Node *owner = nullptr; + if (paste_parent) { + owner = paste_parent->get_owner(); + } + if (!owner) { + owner = paste_parent; + } + + UndoRedo &ur = editor_data->get_undo_redo(); + ur.create_action(TTR("Paste Node(s)")); + ur.add_do_method(editor_selection, "clear"); + + Map<RES, RES> resource_remap; + String target_scene; + if (edited_scene) { + target_scene = edited_scene->get_scene_file_path(); + } + if (target_scene != clipboard_source_scene) { + if (!clipboard_resource_remap.has(target_scene)) { + Map<RES, RES> remap; + for (Node *E : node_clipboard) { + _create_remap_for_node(E, remap); + } + clipboard_resource_remap[target_scene] = remap; + } + resource_remap = clipboard_resource_remap[target_scene]; + } + + for (Node *node : node_clipboard) { + Map<const Node *, Node *> duplimap; + + Node *dup = node->duplicate_from_editor(duplimap, resource_remap); + ERR_CONTINUE(!dup); + + pasted_nodes.push_back(dup); + + if (!paste_parent) { + paste_parent = dup; + owner = dup; + ur.add_do_method(editor, "set_edited_scene", dup); + } else { + ur.add_do_method(paste_parent, "add_child", dup, true); + } + + for (KeyValue<const Node *, Node *> &E2 : duplimap) { + Node *d = E2.value; + if (d != dup) { + ur.add_do_method(d, "set_owner", owner); + } + } + + if (dup != owner) { + ur.add_do_method(dup, "set_owner", owner); + } + ur.add_do_method(editor_selection, "add_node", dup); + + if (dup == paste_parent) { + ur.add_undo_method(editor, "set_edited_scene", (Object *)nullptr); + } else { + ur.add_undo_method(paste_parent, "remove_child", dup); + } + ur.add_do_reference(dup); + + if (node_clipboard.size() == 1) { + ur.add_do_method(editor, "push_item", dup); + } + } + + ur.commit_action(); + return pasted_nodes; +} + void SceneTreeDock::add_remote_tree_editor(Control *p_remote) { ERR_FAIL_COND(remote_tree != nullptr); add_child(p_remote); @@ -3121,6 +3165,7 @@ void SceneTreeDock::_update_create_root_dialog() { beginner_nodes->show(); favorite_nodes->hide(); } + button_clipboard->set_visible(!node_clipboard.is_empty()); } } diff --git a/editor/scene_tree_dock.h b/editor/scene_tree_dock.h index ffaf34cfdc..f442d3fc6b 100644 --- a/editor/scene_tree_dock.h +++ b/editor/scene_tree_dock.h @@ -123,6 +123,7 @@ class SceneTreeDock : public VBoxContainer { Button *button_3d; Button *button_ui; Button *button_custom; + Button *button_clipboard; HBoxContainer *button_hb; Button *edit_local, *edit_remote; @@ -308,6 +309,8 @@ public: void open_add_child_dialog(); void open_instance_child_dialog(); + List<Node *> paste_nodes(); + ScriptCreateDialog *get_script_create_dialog() { return script_create_dialog; } SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSelection *p_editor_selection, EditorData &p_editor_data); diff --git a/main/main.cpp b/main/main.cpp index 8b58641461..add37def8c 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -1728,11 +1728,15 @@ Error Main::setup2(Thread::ID p_main_tid_override) { if (show_logo) { //boot logo! const bool boot_logo_image = GLOBAL_DEF("application/boot_splash/show_image", true); const String boot_logo_path = String(GLOBAL_DEF("application/boot_splash/image", String())).strip_edges(); - const bool boot_logo_scale = GLOBAL_DEF("application/boot_splash/fullsize", true); + const RenderingServer::SplashStretchMode boot_stretch_mode = + (RenderingServer::SplashStretchMode)(int)GLOBAL_DEF("application/boot_splash/stretch_mode", RenderingServer::SPLASH_STRETCH_MODE_KEEP); const bool boot_logo_filter = GLOBAL_DEF("application/boot_splash/use_filter", true); + + ProjectSettings::get_singleton()->set_custom_property_info("application/boot_splash/stretch_mode", + PropertyInfo(Variant::INT, "application/boot_splash/stretch_mode", + PROPERTY_HINT_ENUM, "Disabled,Keep,Keep Width,Keep Height,Cover,Expand")); // Sync with RenderingServer::SplashStretchMode. ProjectSettings::get_singleton()->set_custom_property_info("application/boot_splash/image", - PropertyInfo(Variant::STRING, - "application/boot_splash/image", + PropertyInfo(Variant::STRING, "application/boot_splash/image", PROPERTY_HINT_FILE, "*.png")); Ref<Image> boot_logo; @@ -1760,9 +1764,8 @@ Error Main::setup2(Thread::ID p_main_tid_override) { const Color boot_bg_color = GLOBAL_DEF("application/boot_splash/bg_color", boot_splash_bg_color); #endif if (boot_logo.is_valid()) { - RenderingServer::get_singleton()->set_boot_image(boot_logo, boot_bg_color, boot_logo_scale, - boot_logo_filter); - + RenderingServer::get_singleton()->set_boot_image(boot_logo, boot_bg_color, + boot_stretch_mode, boot_logo_filter); } else { #ifndef NO_DEFAULT_BOOT_LOGO MAIN_PRINT("Main: Create bootsplash"); @@ -1775,7 +1778,7 @@ Error Main::setup2(Thread::ID p_main_tid_override) { MAIN_PRINT("Main: ClearColor"); RenderingServer::get_singleton()->set_default_clear_color(boot_bg_color); MAIN_PRINT("Main: Image"); - RenderingServer::get_singleton()->set_boot_image(splash, boot_bg_color, false); + RenderingServer::get_singleton()->set_boot_image(splash, boot_bg_color, RenderingServer::SPLASH_STRETCH_MODE_DISABLED); #endif } diff --git a/modules/gdnative/include/nativescript/godot_nativescript.h b/modules/gdnative/include/nativescript/godot_nativescript.h index eea898475b..879291c2e0 100644 --- a/modules/gdnative/include/nativescript/godot_nativescript.h +++ b/modules/gdnative/include/nativescript/godot_nativescript.h @@ -79,6 +79,7 @@ typedef enum { GODOT_PROPERTY_HINT_PROPERTY_OF_BASE_TYPE, ///< a property of a base type GODOT_PROPERTY_HINT_PROPERTY_OF_INSTANCE, ///< a property of an instance GODOT_PROPERTY_HINT_PROPERTY_OF_SCRIPT, ///< a property of a script & base + GODOT_PROPERTY_HINT_LOCALE_ID, GODOT_PROPERTY_HINT_MAX, } godot_nativescript_property_hint; diff --git a/modules/gridmap/grid_map_editor_plugin.cpp b/modules/gridmap/grid_map_editor_plugin.cpp index 5ef48f8645..320901787d 100644 --- a/modules/gridmap/grid_map_editor_plugin.cpp +++ b/modules/gridmap/grid_map_editor_plugin.cpp @@ -1459,10 +1459,10 @@ void GridMapEditorPlugin::_notification(int p_what) { if (p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) { switch ((int)EditorSettings::get_singleton()->get("editors/grid_map/editor_side")) { case 0: { // Left. - Node3DEditor::get_singleton()->get_palette_split()->move_child(grid_map_editor, 0); + Node3DEditor::get_singleton()->move_control_to_left_panel(grid_map_editor); } break; case 1: { // Right. - Node3DEditor::get_singleton()->get_palette_split()->move_child(grid_map_editor, 1); + Node3DEditor::get_singleton()->move_control_to_right_panel(grid_map_editor); } break; } } @@ -1498,10 +1498,10 @@ GridMapEditorPlugin::GridMapEditorPlugin(EditorNode *p_node) { grid_map_editor = memnew(GridMapEditor(editor)); switch ((int)EditorSettings::get_singleton()->get("editors/grid_map/editor_side")) { case 0: { // Left. - add_control_to_container(CONTAINER_SPATIAL_EDITOR_SIDE_LEFT, grid_map_editor); + Node3DEditor::get_singleton()->add_control_to_left_panel(grid_map_editor); } break; case 1: { // Right. - add_control_to_container(CONTAINER_SPATIAL_EDITOR_SIDE_RIGHT, grid_map_editor); + Node3DEditor::get_singleton()->add_control_to_right_panel(grid_map_editor); } break; } grid_map_editor->hide(); diff --git a/modules/webrtc/doc_classes/WebRTCPeerConnection.xml b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml index 618fe14137..2f2c2c379d 100644 --- a/modules/webrtc/doc_classes/WebRTCPeerConnection.xml +++ b/modules/webrtc/doc_classes/WebRTCPeerConnection.xml @@ -33,8 +33,7 @@ <method name="create_data_channel"> <return type="WebRTCDataChannel" /> <argument index="0" name="label" type="String" /> - <argument index="1" name="options" type="Dictionary" default="{ -}" /> + <argument index="1" name="options" type="Dictionary" default="{}" /> <description> Returns a new [WebRTCDataChannel] (or [code]null[/code] on failure) with given [code]label[/code] and optionally configured via the [code]options[/code] dictionary. This method can only be called when the connection is in state [constant STATE_NEW]. There are two ways to create a working data channel: either call [method create_data_channel] on only one of the peer and listen to [signal data_channel_received] on the other, or call [method create_data_channel] on both peers, with the same values, and the [code]negotiated[/code] option set to [code]true[/code]. @@ -70,8 +69,7 @@ </method> <method name="initialize"> <return type="int" enum="Error" /> - <argument index="0" name="configuration" type="Dictionary" default="{ -}" /> + <argument index="0" name="configuration" type="Dictionary" default="{}" /> <description> Re-initialize this peer connection, closing any previously active connection, and going back to state [constant STATE_NEW]. A dictionary of [code]options[/code] can be passed to configure the peer connection. Valid [code]options[/code] are: diff --git a/platform/linuxbsd/display_server_x11.cpp b/platform/linuxbsd/display_server_x11.cpp index c4f7a3a646..318d014ee5 100644 --- a/platform/linuxbsd/display_server_x11.cpp +++ b/platform/linuxbsd/display_server_x11.cpp @@ -365,21 +365,6 @@ void DisplayServerX11::mouse_warp_to_position(const Point2i &p_to) { } Point2i DisplayServerX11::mouse_get_position() const { - int root_x, root_y; - int win_x, win_y; - unsigned int mask_return; - Window window_returned; - - Bool result = XQueryPointer(x11_display, RootWindow(x11_display, DefaultScreen(x11_display)), &window_returned, - &window_returned, &root_x, &root_y, &win_x, &win_y, - &mask_return); - if (result == True) { - return Point2i(root_x, root_y); - } - return Point2i(); -} - -Point2i DisplayServerX11::mouse_get_absolute_position() const { int number_of_screens = XScreenCount(x11_display); for (int i = 0; i < number_of_screens; i++) { Window root, child; diff --git a/platform/linuxbsd/display_server_x11.h b/platform/linuxbsd/display_server_x11.h index 1dcedabb1a..8929f528d6 100644 --- a/platform/linuxbsd/display_server_x11.h +++ b/platform/linuxbsd/display_server_x11.h @@ -289,7 +289,6 @@ public: virtual void mouse_warp_to_position(const Point2i &p_to) override; virtual Point2i mouse_get_position() const override; - virtual Point2i mouse_get_absolute_position() const override; virtual MouseButton mouse_get_button_state() const override; virtual void clipboard_set(const String &p_text) override; diff --git a/platform/osx/display_server_osx.h b/platform/osx/display_server_osx.h index afc2754b0e..d609a84e50 100644 --- a/platform/osx/display_server_osx.h +++ b/platform/osx/display_server_osx.h @@ -98,6 +98,8 @@ public: NSTimeInterval last_warp = 0; bool ignore_warp = false; + float display_max_scale = 1.f; + Vector<KeyEvent> key_event_buffer; int key_event_pos; @@ -214,7 +216,6 @@ public: virtual void mouse_warp_to_position(const Point2i &p_to) override; virtual Point2i mouse_get_position() const override; - virtual Point2i mouse_get_absolute_position() const override; virtual MouseButton mouse_get_button_state() const override; virtual void clipboard_set(const String &p_text) override; diff --git a/platform/osx/display_server_osx.mm b/platform/osx/display_server_osx.mm index 52cabfd821..e3b4333ec8 100644 --- a/platform/osx/display_server_osx.mm +++ b/platform/osx/display_server_osx.mm @@ -158,12 +158,7 @@ static NSCursor *_cursorFromSelector(SEL selector, SEL fallback = nil) { } if (wd.transient_parent != DisplayServerOSX::INVALID_WINDOW_ID) { - DisplayServerOSX::WindowData &pwd = DS_OSX->windows[wd.transient_parent]; - [pwd.window_object makeKeyAndOrderFront:nil]; // Move focus back to parent. DS_OSX->window_set_transient(window_id, DisplayServerOSX::INVALID_WINDOW_ID); - } else if ((window_id != DisplayServerOSX::MAIN_WINDOW_ID) && (DS_OSX->windows.size() == 1)) { - DisplayServerOSX::WindowData &pwd = DS_OSX->windows[DisplayServerOSX::MAIN_WINDOW_ID]; - [pwd.window_object makeKeyAndOrderFront:nil]; // Move focus back to main window if there is no parent or other windows left. } #if defined(GLES3_ENABLED) @@ -2001,10 +1996,6 @@ void DisplayServerOSX::mouse_warp_to_position(const Point2i &p_to) { } Point2i DisplayServerOSX::mouse_get_position() const { - return last_mouse_pos; -} - -Point2i DisplayServerOSX::mouse_get_absolute_position() const { _THREAD_SAFE_METHOD_ const NSPoint mouse_pos = [NSEvent mouseLocation]; @@ -2071,10 +2062,8 @@ int DisplayServerOSX::get_screen_count() const { // to convert between OS X native screen coordinates and the ones expected by Godot static bool displays_arrangement_dirty = true; -static bool displays_scale_dirty = true; static void displays_arrangement_changed(CGDirectDisplayID display_id, CGDisplayChangeSummaryFlags flags, void *user_info) { displays_arrangement_dirty = true; - displays_scale_dirty = true; } Point2i DisplayServerOSX::_get_screens_origin() const { @@ -2185,15 +2174,8 @@ float DisplayServerOSX::screen_get_scale(int p_screen) const { float DisplayServerOSX::screen_get_max_scale() const { _THREAD_SAFE_METHOD_ - static float scale = 1.f; - if (displays_scale_dirty) { - int screen_count = get_screen_count(); - for (int i = 0; i < screen_count; i++) { - scale = fmax(scale, screen_get_scale(i)); - } - displays_scale_dirty = false; - } - return scale; + // Note: Do not update max display scale on screen configuration change, existing editor windows can't be rescaled on the fly. + return display_max_scale; } Rect2i DisplayServerOSX::screen_get_usable_rect(int p_screen) const { @@ -2380,8 +2362,24 @@ int DisplayServerOSX::window_get_current_screen(WindowID p_window) const { void DisplayServerOSX::window_set_current_screen(int p_screen, WindowID p_window) { _THREAD_SAFE_METHOD_ + + ERR_FAIL_COND(!windows.has(p_window)); + WindowData &wd = windows[p_window]; + + bool was_fullscreen = false; + if (wd.fullscreen) { + // Temporary exit fullscreen mode to move window. + [wd.window_object toggleFullScreen:nil]; + was_fullscreen = true; + } + Point2i wpos = window_get_position(p_window) - screen_get_position(window_get_current_screen(p_window)); window_set_position(wpos + screen_get_position(p_screen), p_window); + + if (was_fullscreen) { + // Re-enter fullscreen mode. + [wd.window_object toggleFullScreen:nil]; + } } void DisplayServerOSX::window_set_transient(WindowID p_window, WindowID p_parent) { @@ -2404,7 +2402,7 @@ void DisplayServerOSX::window_set_transient(WindowID p_window, WindowID p_parent wd_window.transient_parent = INVALID_WINDOW_ID; wd_parent.transient_children.erase(p_window); - [wd_parent.window_object removeChildWindow:wd_window.window_object]; + [wd_window.window_object setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; } else { ERR_FAIL_COND(!windows.has(p_parent)); ERR_FAIL_COND_MSG(wd_window.transient_parent != INVALID_WINDOW_ID, "Window already has a transient parent"); @@ -2413,7 +2411,7 @@ void DisplayServerOSX::window_set_transient(WindowID p_window, WindowID p_parent wd_window.transient_parent = p_parent; wd_parent.transient_children.insert(p_window); - [wd_parent.window_object addChildWindow:wd_window.window_object ordered:NSWindowAbove]; + [wd_window.window_object setCollectionBehavior:NSWindowCollectionBehaviorFullScreenAuxiliary]; } } @@ -2423,7 +2421,9 @@ Point2i DisplayServerOSX::window_get_position(WindowID p_window) const { ERR_FAIL_COND_V(!windows.has(p_window), Point2i()); const WindowData &wd = windows[p_window]; - NSRect nsrect = [wd.window_object frame]; + // Use content rect position (without titlebar / window border). + const NSRect contentRect = [wd.window_view frame]; + const NSRect nsrect = [wd.window_object convertRectToScreen:contentRect]; Point2i pos; // Return the position of the top-left corner, for OS X the y starts at the bottom @@ -2451,7 +2451,16 @@ void DisplayServerOSX::window_set_position(const Point2i &p_position, WindowID p position += _get_screens_origin(); position /= screen_get_max_scale(); - [wd.window_object setFrameTopLeftPoint:NSMakePoint(position.x, position.y)]; + // Remove titlebar / window border size. + const NSRect contentRect = [wd.window_view frame]; + const NSRect windowRect = [wd.window_object frame]; + const NSRect nsrect = [wd.window_object convertRectToScreen:contentRect]; + Point2i offset; + offset.x = (nsrect.origin.x - windowRect.origin.x); + offset.y = (nsrect.origin.y + nsrect.size.height); + offset.y -= (windowRect.origin.y + windowRect.size.height); + + [wd.window_object setFrameTopLeftPoint:NSMakePoint(position.x - offset.x, position.y - offset.y)]; _update_window(wd); _get_mouse_pos(wd, [wd.window_object mouseLocationOutsideOfEventStream]); @@ -3699,7 +3708,11 @@ DisplayServerOSX::DisplayServerOSX(const String &p_rendering_driver, WindowMode keyboard_layout_dirty = true; displays_arrangement_dirty = true; - displays_scale_dirty = true; + + int screen_count = get_screen_count(); + for (int i = 0; i < screen_count; i++) { + display_max_scale = fmax(display_max_scale, screen_get_scale(i)); + } // Register to be notified on keyboard layout changes CFNotificationCenterAddObserver(CFNotificationCenterGetDistributedCenter(), diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index 1271e64945..bcddae45d8 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -674,8 +674,20 @@ void DisplayServerWindows::window_set_current_screen(int p_screen, WindowID p_wi ERR_FAIL_COND(!windows.has(p_window)); ERR_FAIL_INDEX(p_screen, get_screen_count()); - Vector2 ofs = window_get_position(p_window) - screen_get_position(window_get_current_screen(p_window)); - window_set_position(ofs + screen_get_position(p_screen), p_window); + const WindowData &wd = windows[p_window]; + if (wd.fullscreen) { + int cs = window_get_current_screen(p_window); + if (cs == p_screen) { + return; + } + Point2 pos = screen_get_position(p_screen); + Size2 size = screen_get_size(p_screen); + + MoveWindow(wd.hWnd, pos.x, pos.y, size.width, size.height, TRUE); + } else { + Vector2 ofs = window_get_position(p_window) - screen_get_position(window_get_current_screen(p_window)); + window_set_position(ofs + screen_get_position(p_screen), p_window); + } } Point2i DisplayServerWindows::window_get_position(WindowID p_window) const { diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp index 046b9c18c7..552dd28513 100644 --- a/scene/gui/button.cpp +++ b/scene/gui/button.cpp @@ -569,7 +569,7 @@ void Button::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT, "", PROPERTY_USAGE_DEFAULT_INTL), "set_text", "get_text"); ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction"); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "language"), "set_language", "get_language"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_button_icon", "get_button_icon"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flat"), "set_flat", "is_flat"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_text"), "set_clip_text", "get_clip_text"); diff --git a/scene/gui/flow_container.cpp b/scene/gui/flow_container.cpp new file mode 100644 index 0000000000..d1ac60b325 --- /dev/null +++ b/scene/gui/flow_container.cpp @@ -0,0 +1,252 @@ +/*************************************************************************/ +/* flow_container.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "scene/gui/container.h" + +#include "flow_container.h" + +struct _LineData { + int child_count = 0; + int min_line_height = 0; + int min_line_length = 0; + int stretch_avail = 0; + float stretch_ratio_total = 0; +}; + +void FlowContainer::_resort() { + int separation_horizontal = get_theme_constant(SNAME("hseparation")); + int separation_vertical = get_theme_constant(SNAME("vseparation")); + + bool rtl = is_layout_rtl(); + + Map<Control *, Size2i> children_minsize_cache; + + Vector<_LineData> lines_data; + + Vector2i ofs; + int line_height = 0; + int line_length = 0; + float line_stretch_ratio_total = 0; + int current_container_size = vertical ? get_rect().size.y : get_rect().size.x; + int children_in_current_line = 0; + + // First pass for line wrapping and minimum size calculation. + for (int i = 0; i < get_child_count(); i++) { + Control *child = Object::cast_to<Control>(get_child(i)); + if (!child || !child->is_visible_in_tree()) { + continue; + } + if (child->is_set_as_top_level()) { + continue; + } + + Size2i child_msc = child->get_combined_minimum_size(); + + if (vertical) { /* VERTICAL */ + if (children_in_current_line > 0) { + ofs.y += separation_vertical; + } + if (ofs.y + child_msc.y > current_container_size) { + line_length = ofs.y - separation_vertical; + lines_data.push_back(_LineData{ children_in_current_line, line_height, line_length, current_container_size - line_length, line_stretch_ratio_total }); + + // Move in new column (vertical line). + ofs.x += line_height + separation_horizontal; + ofs.y = 0; + line_height = 0; + line_stretch_ratio_total = 0; + children_in_current_line = 0; + } + + line_height = MAX(line_height, child_msc.x); + if (child->get_v_size_flags() & SIZE_EXPAND) { + line_stretch_ratio_total += child->get_stretch_ratio(); + } + ofs.y += child_msc.y; + + } else { /* HORIZONTAL */ + if (children_in_current_line > 0) { + ofs.x += separation_horizontal; + } + if (ofs.x + child_msc.x > current_container_size) { + line_length = ofs.x - separation_horizontal; + lines_data.push_back(_LineData{ children_in_current_line, line_height, line_length, current_container_size - line_length, line_stretch_ratio_total }); + + // Move in new line. + ofs.y += line_height + separation_vertical; + ofs.x = 0; + line_height = 0; + line_stretch_ratio_total = 0; + children_in_current_line = 0; + } + + line_height = MAX(line_height, child_msc.y); + if (child->get_h_size_flags() & SIZE_EXPAND) { + line_stretch_ratio_total += child->get_stretch_ratio(); + } + ofs.x += child_msc.x; + } + + children_minsize_cache[child] = child_msc; + children_in_current_line++; + } + line_length = vertical ? (ofs.y) : (ofs.x); + lines_data.push_back(_LineData{ children_in_current_line, line_height, line_length, current_container_size - line_length, line_stretch_ratio_total }); + + // Second pass for in-line expansion and alignment. + + int current_line_idx = 0; + int child_idx_in_line = 0; + + ofs.x = 0; + ofs.y = 0; + + for (int i = 0; i < get_child_count(); i++) { + Control *child = Object::cast_to<Control>(get_child(i)); + if (!child || !child->is_visible_in_tree()) { + continue; + } + if (child->is_set_as_top_level()) { + continue; + } + Size2i child_size = children_minsize_cache[child]; + + _LineData line_data = lines_data[current_line_idx]; + if (child_idx_in_line >= lines_data[current_line_idx].child_count) { + current_line_idx++; + child_idx_in_line = 0; + if (vertical) { + ofs.x += line_data.min_line_height + separation_horizontal; + ofs.y = 0; + } else { + ofs.x = 0; + ofs.y += line_data.min_line_height + separation_vertical; + } + line_data = lines_data[current_line_idx]; + } + + if (vertical) { /* VERTICAL */ + if (child->get_h_size_flags() & (SIZE_FILL | SIZE_SHRINK_CENTER | SIZE_SHRINK_END)) { + child_size.width = line_data.min_line_height; + } + + if (child->get_v_size_flags() & SIZE_EXPAND) { + int stretch = line_data.stretch_avail * child->get_stretch_ratio() / line_data.stretch_ratio_total; + child_size.height += stretch; + } + + } else { /* HORIZONTAL */ + if (child->get_v_size_flags() & (SIZE_FILL | SIZE_SHRINK_CENTER | SIZE_SHRINK_END)) { + child_size.height = line_data.min_line_height; + } + + if (child->get_h_size_flags() & SIZE_EXPAND) { + int stretch = line_data.stretch_avail * child->get_stretch_ratio() / line_data.stretch_ratio_total; + child_size.width += stretch; + } + } + + Rect2 child_rect = Rect2(ofs, child_size); + if (rtl) { + child_rect.position.x = get_rect().size.x - child_rect.position.x - child_rect.size.width; + } + + fit_child_in_rect(child, child_rect); + + if (vertical) { /* VERTICAL */ + ofs.y += child_size.height + separation_vertical; + } else { /* HORIZONTAL */ + ofs.x += child_size.width + separation_horizontal; + } + + child_idx_in_line++; + } + cached_size = (vertical ? ofs.x : ofs.y) + line_height; + cached_line_count = lines_data.size(); +} + +Size2 FlowContainer::get_minimum_size() const { + Size2i minimum; + + for (int i = 0; i < get_child_count(); i++) { + Control *c = Object::cast_to<Control>(get_child(i)); + if (!c) { + continue; + } + if (c->is_set_as_top_level()) { + continue; + } + + if (!c->is_visible()) { + continue; + } + + Size2i size = c->get_combined_minimum_size(); + + if (vertical) { /* VERTICAL */ + minimum.height = MAX(minimum.height, size.height); + minimum.width = cached_size; + + } else { /* HORIZONTAL */ + minimum.width = MAX(minimum.width, size.width); + minimum.height = cached_size; + } + } + + return minimum; +} + +void FlowContainer::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_SORT_CHILDREN: { + _resort(); + update_minimum_size(); + } break; + case NOTIFICATION_THEME_CHANGED: { + update_minimum_size(); + } break; + case NOTIFICATION_TRANSLATION_CHANGED: + case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: { + queue_sort(); + } break; + } +} + +int FlowContainer::get_line_count() const { + return cached_line_count; +} + +FlowContainer::FlowContainer(bool p_vertical) { + vertical = p_vertical; +} + +void FlowContainer::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_line_count"), &FlowContainer::get_line_count); +} diff --git a/scene/gui/flow_container.h b/scene/gui/flow_container.h new file mode 100644 index 0000000000..e3ed423ae1 --- /dev/null +++ b/scene/gui/flow_container.h @@ -0,0 +1,76 @@ +/*************************************************************************/ +/* flow_container.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef FLOW_CONTAINER_H +#define FLOW_CONTAINER_H + +class Container; + +class FlowContainer : public Container { + GDCLASS(FlowContainer, Container); + +private: + int cached_size = 0; + int cached_line_count = 0; + + bool vertical = false; + + void _resort(); + +protected: + void _notification(int p_what); + + static void _bind_methods(); + +public: + int get_line_count() const; + + virtual Size2 get_minimum_size() const override; + + FlowContainer(bool p_vertical = false); +}; + +class HFlowContainer : public FlowContainer { + GDCLASS(HFlowContainer, FlowContainer); + +public: + HFlowContainer() : + FlowContainer(false) {} +}; + +class VFlowContainer : public FlowContainer { + GDCLASS(VFlowContainer, FlowContainer); + +public: + VFlowContainer() : + FlowContainer(true) {} +}; + +#endif // FLOW_CONTAINER_H diff --git a/scene/gui/graph_node.cpp b/scene/gui/graph_node.cpp index d6569e3de4..30f6cf4a14 100644 --- a/scene/gui/graph_node.cpp +++ b/scene/gui/graph_node.cpp @@ -1022,7 +1022,7 @@ void GraphNode::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::STRING, "title"), "set_title", "get_title"); ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction"); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "language"), "set_language", "get_language"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "position_offset"), "set_position_offset", "get_position_offset"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_close"), "set_show_close_button", "is_close_button_visible"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "resizable"), "set_resizable", "is_resizable"); diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp index a3878fe7ed..fab420d593 100644 --- a/scene/gui/label.cpp +++ b/scene/gui/label.cpp @@ -916,7 +916,7 @@ void Label::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT, "", PROPERTY_USAGE_DEFAULT_INTL), "set_text", "get_text"); ADD_GROUP("Locale", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction"); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "language"), "set_language", "get_language"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language"); ADD_PROPERTY(PropertyInfo(Variant::INT, "horizontal_alignment", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_horizontal_alignment", "get_horizontal_alignment"); ADD_PROPERTY(PropertyInfo(Variant::INT, "vertical_alignment", PROPERTY_HINT_ENUM, "Top,Center,Bottom,Fill"), "set_vertical_alignment", "get_vertical_alignment"); ADD_PROPERTY(PropertyInfo(Variant::INT, "autowrap_mode", PROPERTY_HINT_ENUM, "Off,Arbitrary,Word,Word (Smart)"), "set_autowrap_mode", "get_autowrap_mode"); diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index f7d6850a88..88953fa7db 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -2344,7 +2344,7 @@ void LineEdit::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "right_icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_right_icon", "get_right_icon"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flat"), "set_flat", "is_flat"); ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction"); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "language"), "set_language", "get_language"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_control_chars"), "set_draw_control_chars", "get_draw_control_chars"); ADD_GROUP("Structured Text", "structured_text_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "structured_text_bidi_override", PROPERTY_HINT_ENUM, "Default,URI,File,Email,List,None,Custom"), "set_structured_text_bidi_override", "get_structured_text_bidi_override"); diff --git a/scene/gui/link_button.cpp b/scene/gui/link_button.cpp index 67ffa2c7ed..0ff05faf85 100644 --- a/scene/gui/link_button.cpp +++ b/scene/gui/link_button.cpp @@ -308,7 +308,7 @@ void LinkButton::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::STRING, "text"), "set_text", "get_text"); ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction"); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "language"), "set_language", "get_language"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language"); ADD_PROPERTY(PropertyInfo(Variant::INT, "underline", PROPERTY_HINT_ENUM, "Always,On Hover,Never"), "set_underline_mode", "get_underline_mode"); ADD_GROUP("Structured Text", "structured_text_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "structured_text_bidi_override", PROPERTY_HINT_ENUM, "Default,URI,File,Email,List,None,Custom"), "set_structured_text_bidi_override", "get_structured_text_bidi_override"); diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 863555120d..669bdab637 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -254,7 +254,12 @@ void RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font> for (int i = 0; i < col_count; i++) { remaining_width -= table->columns[i].min_width; if (table->columns[i].max_width > table->columns[i].min_width) { + // If the column can grow, allow it to grow. table->columns.write[i].expand = true; + } else { + // Otherwise make it shrink as much as possible, so that other columns can grow if needs be. + // We keep the max width as is to spread the remaining space between the columns later. + table->columns.write[i].min_width = 0; } if (table->columns[i].expand) { total_ratio += table->columns[i].expand_ratio; @@ -264,7 +269,7 @@ void RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font> // Assign actual widths. for (int i = 0; i < col_count; i++) { table->columns.write[i].width = table->columns[i].min_width; - if (table->columns[i].expand && total_ratio > 0) { + if (table->columns[i].expand && total_ratio > 0 && remaining_width > 0) { table->columns.write[i].width += table->columns[i].expand_ratio * remaining_width / total_ratio; } table->total_width += table->columns[i].width + hseparation; @@ -325,13 +330,16 @@ void RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font> table->columns.write[column].width = MAX(table->columns.write[column].width, ceil(frame->lines[i].text_buf->get_size().x)); if (i > 0) { - frame->lines.write[i].offset.y = frame->lines[i - 1].offset.y + frame->lines[i - 1].text_buf->get_size().y; + frame->lines.write[i].offset.y = frame->lines[i - 1].offset.y + frame->lines[i - 1].text_buf->get_size().y + get_theme_constant(SNAME("line_separation")); } else { frame->lines.write[i].offset.y = 0; } frame->lines.write[i].offset += offset; - float h = frame->lines[i].text_buf->get_size().y; + float h = frame->lines[i].text_buf->get_size().y + (frame->lines[i].text_buf->get_line_count() - 1) * get_theme_constant(SNAME("line_separation")); + if (i > 0) { + h += get_theme_constant(SNAME("line_separation")); + } if (frame->min_size_over.y > 0) { h = MAX(h, frame->min_size_over.y); } @@ -499,7 +507,12 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> for (int i = 0; i < col_count; i++) { remaining_width -= table->columns[i].min_width; if (table->columns[i].max_width > table->columns[i].min_width) { + // If the column can grow, allow it to grow. table->columns.write[i].expand = true; + } else { + // Otherwise make it shrink as much as possible, so that other columns can grow if needs be. + // We keep the max width as is to spread the remaining space between the columns later. + table->columns.write[i].min_width = 0; } if (table->columns[i].expand) { total_ratio += table->columns[i].expand_ratio; @@ -509,7 +522,7 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> // Assign actual widths. for (int i = 0; i < col_count; i++) { table->columns.write[i].width = table->columns[i].min_width; - if (table->columns[i].expand && total_ratio > 0) { + if (table->columns[i].expand && total_ratio > 0 && remaining_width > 0) { table->columns.write[i].width += table->columns[i].expand_ratio * remaining_width / total_ratio; } table->total_width += table->columns[i].width + hseparation; @@ -570,13 +583,16 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> table->columns.write[column].width = MAX(table->columns.write[column].width, ceil(frame->lines[i].text_buf->get_size().x)); if (i > 0) { - frame->lines.write[i].offset.y = frame->lines[i - 1].offset.y + frame->lines[i - 1].text_buf->get_size().y; + frame->lines.write[i].offset.y = frame->lines[i - 1].offset.y + frame->lines[i - 1].text_buf->get_size().y + get_theme_constant(SNAME("line_separation")); } else { frame->lines.write[i].offset.y = 0; } frame->lines.write[i].offset += offset; - float h = frame->lines[i].text_buf->get_size().y; + float h = frame->lines[i].text_buf->get_size().y + (frame->lines[i].text_buf->get_line_count() - 1) * get_theme_constant(SNAME("line_separation")); + if (i > 0) { + h += get_theme_constant(SNAME("line_separation")); + } if (frame->min_size_over.y > 0) { h = MAX(h, frame->min_size_over.y); } @@ -622,11 +638,12 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> } int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Color &p_base_color, int p_outline_size, const Color &p_outline_color, const Color &p_font_shadow_color, int p_shadow_outline_size, const Point2 &p_shadow_ofs, int &r_processed_glyphs) { - Vector2 off; - ERR_FAIL_COND_V(p_frame == nullptr, 0); ERR_FAIL_COND_V(p_line < 0 || p_line >= p_frame->lines.size(), 0); + Vector2 off; + int line_spacing = get_theme_constant(SNAME("line_separation")); + Line &l = p_frame->lines.write[p_line]; Item *it_from = l.from; @@ -712,6 +729,10 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o Size2 ctrl_size = get_size(); // Draw text. for (int line = 0; line < l.text_buf->get_line_count(); line++) { + if (line > 0) { + off.y += line_spacing; + } + RID rid = l.text_buf->get_line_rid(line); if (p_ofs.y + off.y >= ctrl_size.height) { break; @@ -4207,7 +4228,7 @@ void RichTextLabel::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "custom_effects", PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "RichTextEffect"), (PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE)), "set_effects", "get_effects"); ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction"); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "language"), "set_language", "get_language"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language"); ADD_GROUP("Structured Text", "structured_text_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "structured_text_bidi_override", PROPERTY_HINT_ENUM, "Default,URI,File,Email,List,None,Custom"), "set_structured_text_bidi_override", "get_structured_text_bidi_override"); diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 2b10ead8f6..671c2d951a 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -5171,7 +5171,7 @@ void TextEdit::_bind_methods() { /* Inspector */ ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT), "set_text", "get_text"); ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction"); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "language"), "set_language", "get_language"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editable"), "set_editable", "is_editable"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "context_menu_enabled"), "set_context_menu_enabled", "is_context_menu_enabled"); diff --git a/scene/main/window.cpp b/scene/main/window.cpp index 43de4187d4..532b457843 100644 --- a/scene/main/window.cpp +++ b/scene/main/window.cpp @@ -281,6 +281,11 @@ void Window::_clear_window() { DisplayServer::get_singleton()->delete_sub_window(window_id); window_id = DisplayServer::INVALID_WINDOW_ID; + // If closing window was focused and has a parent, return focus. + if (focused && transient_parent) { + transient_parent->grab_focus(); + } + _update_viewport_size(); RS::get_singleton()->viewport_set_update_mode(get_viewport_rid(), RS::VIEWPORT_UPDATE_DISABLED); } @@ -1574,6 +1579,7 @@ void Window::_bind_methods() { ADD_SIGNAL(MethodInfo("go_back_requested")); ADD_SIGNAL(MethodInfo("visibility_changed")); ADD_SIGNAL(MethodInfo("about_to_popup")); + ADD_SIGNAL(MethodInfo("theme_changed")); BIND_CONSTANT(NOTIFICATION_VISIBILITY_CHANGED); diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index e6b73b7780..1a3a25c2fc 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -91,6 +91,7 @@ #include "scene/gui/control.h" #include "scene/gui/dialogs.h" #include "scene/gui/file_dialog.h" +#include "scene/gui/flow_container.h" #include "scene/gui/graph_edit.h" #include "scene/gui/graph_node.h" #include "scene/gui/grid_container.h" @@ -353,6 +354,9 @@ void register_scene_types() { GDREGISTER_CLASS(CenterContainer); GDREGISTER_CLASS(ScrollContainer); GDREGISTER_CLASS(PanelContainer); + GDREGISTER_VIRTUAL_CLASS(FlowContainer); + GDREGISTER_CLASS(HFlowContainer); + GDREGISTER_CLASS(VFlowContainer); OS::get_singleton()->yield(); // may take time to init @@ -390,6 +394,7 @@ void register_scene_types() { GDREGISTER_VIRTUAL_CLASS(SplitContainer); GDREGISTER_CLASS(HSplitContainer); GDREGISTER_CLASS(VSplitContainer); + GDREGISTER_CLASS(GraphNode); GDREGISTER_CLASS(GraphEdit); diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp index 61cd834867..c22f16cbde 100644 --- a/scene/resources/default_theme/default_theme.cpp +++ b/scene/resources/default_theme/default_theme.cpp @@ -974,6 +974,10 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const theme->set_constant("separation", "VSplitContainer", 12 * scale); theme->set_constant("autohide", "HSplitContainer", 1 * scale); theme->set_constant("autohide", "VSplitContainer", 1 * scale); + theme->set_constant("hseparation", "HFlowContainer", 4 * scale); + theme->set_constant("vseparation", "HFlowContainer", 4 * scale); + theme->set_constant("hseparation", "VFlowContainer", 4 * scale); + theme->set_constant("vseparation", "VFlowContainer", 4 * scale); Ref<StyleBoxTexture> sb_pc = make_stylebox(tab_container_bg_png, 4, 4, 4, 4, 7, 7, 7, 7); theme->set_stylebox("panel", "PanelContainer", sb_pc); diff --git a/scene/resources/environment.cpp b/scene/resources/environment.cpp index 7e71ad8986..0afe040f33 100644 --- a/scene/resources/environment.cpp +++ b/scene/resources/environment.cpp @@ -496,13 +496,13 @@ bool Environment::is_sdfgi_enabled() const { return sdfgi_enabled; } -void Environment::set_sdfgi_cascades(SDFGICascades p_cascades) { - ERR_FAIL_INDEX(p_cascades, SDFGI_CASCADES_8 + 1); +void Environment::set_sdfgi_cascades(int p_cascades) { + ERR_FAIL_COND_MSG(p_cascades < 1 || p_cascades > 8, "Invalid number of SDFGI cascades (must be between 1 and 8)."); sdfgi_cascades = p_cascades; _update_sdfgi(); } -Environment::SDFGICascades Environment::get_sdfgi_cascades() const { +int Environment::get_sdfgi_cascades() const { return sdfgi_cascades; } @@ -517,9 +517,7 @@ float Environment::get_sdfgi_min_cell_size() const { void Environment::set_sdfgi_max_distance(float p_distance) { p_distance /= 64.0; - int cc[3] = { 4, 6, 8 }; - int cascades = cc[sdfgi_cascades]; - for (int i = 0; i < cascades; i++) { + for (int i = 0; i < sdfgi_cascades; i++) { p_distance *= 0.5; //halve for each cascade } sdfgi_min_cell_size = p_distance; @@ -529,9 +527,7 @@ void Environment::set_sdfgi_max_distance(float p_distance) { float Environment::get_sdfgi_max_distance() const { float md = sdfgi_min_cell_size; md *= 64.0; - int cc[3] = { 4, 6, 8 }; - int cascades = cc[sdfgi_cascades]; - for (int i = 0; i < cascades; i++) { + for (int i = 0; i < sdfgi_cascades; i++) { md *= 2.0; } return md; @@ -612,7 +608,7 @@ void Environment::_update_sdfgi() { RS::get_singleton()->environment_set_sdfgi( environment, sdfgi_enabled, - RS::EnvironmentSDFGICascades(sdfgi_cascades), + sdfgi_cascades, sdfgi_min_cell_size, RS::EnvironmentSDFGIYScale(sdfgi_y_scale), sdfgi_use_occlusion, @@ -1303,7 +1299,7 @@ void Environment::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sdfgi_use_occlusion"), "set_sdfgi_use_occlusion", "is_sdfgi_using_occlusion"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sdfgi_read_sky_light"), "set_sdfgi_read_sky_light", "is_sdfgi_reading_sky_light"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sdfgi_bounce_feedback", PROPERTY_HINT_RANGE, "0,1.99,0.01"), "set_sdfgi_bounce_feedback", "get_sdfgi_bounce_feedback"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "sdfgi_cascades", PROPERTY_HINT_ENUM, "4 Cascades,6 Cascades,8 Cascades"), "set_sdfgi_cascades", "get_sdfgi_cascades"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "sdfgi_cascades", PROPERTY_HINT_RANGE, "1,8,1"), "set_sdfgi_cascades", "get_sdfgi_cascades"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sdfgi_min_cell_size", PROPERTY_HINT_RANGE, "0.01,64,0.01"), "set_sdfgi_min_cell_size", "get_sdfgi_min_cell_size"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sdfgi_cascade0_distance", PROPERTY_HINT_RANGE, "0.1,16384,0.1,or_greater"), "set_sdfgi_cascade0_distance", "get_sdfgi_cascade0_distance"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sdfgi_max_distance", PROPERTY_HINT_RANGE, "0.1,16384,0.1,or_greater"), "set_sdfgi_max_distance", "get_sdfgi_max_distance"); @@ -1480,10 +1476,6 @@ void Environment::_bind_methods() { BIND_ENUM_CONSTANT(GLOW_BLEND_MODE_REPLACE); BIND_ENUM_CONSTANT(GLOW_BLEND_MODE_MIX); - BIND_ENUM_CONSTANT(SDFGI_CASCADES_4); - BIND_ENUM_CONSTANT(SDFGI_CASCADES_6); - BIND_ENUM_CONSTANT(SDFGI_CASCADES_8); - BIND_ENUM_CONSTANT(SDFGI_Y_SCALE_DISABLED); BIND_ENUM_CONSTANT(SDFGI_Y_SCALE_75_PERCENT); BIND_ENUM_CONSTANT(SDFGI_Y_SCALE_50_PERCENT); diff --git a/scene/resources/environment.h b/scene/resources/environment.h index 98e755c336..3f05315013 100644 --- a/scene/resources/environment.h +++ b/scene/resources/environment.h @@ -70,12 +70,6 @@ public: TONE_MAPPER_ACES, }; - enum SDFGICascades { - SDFGI_CASCADES_4, - SDFGI_CASCADES_6, - SDFGI_CASCADES_8, - }; - enum SDFGIYScale { SDFGI_Y_SCALE_DISABLED, SDFGI_Y_SCALE_75_PERCENT, @@ -153,7 +147,7 @@ private: // SDFGI bool sdfgi_enabled = false; - SDFGICascades sdfgi_cascades = SDFGI_CASCADES_6; + int sdfgi_cascades = 6; float sdfgi_min_cell_size = 0.2; SDFGIYScale sdfgi_y_scale = SDFGI_Y_SCALE_DISABLED; bool sdfgi_use_occlusion = false; @@ -320,8 +314,8 @@ public: // SDFGI void set_sdfgi_enabled(bool p_enabled); bool is_sdfgi_enabled() const; - void set_sdfgi_cascades(SDFGICascades p_cascades); - SDFGICascades get_sdfgi_cascades() const; + void set_sdfgi_cascades(int p_cascades); + int get_sdfgi_cascades() const; void set_sdfgi_min_cell_size(float p_size); float get_sdfgi_min_cell_size() const; void set_sdfgi_max_distance(float p_distance); @@ -433,7 +427,6 @@ VARIANT_ENUM_CAST(Environment::BGMode) VARIANT_ENUM_CAST(Environment::AmbientSource) VARIANT_ENUM_CAST(Environment::ReflectionSource) VARIANT_ENUM_CAST(Environment::ToneMapper) -VARIANT_ENUM_CAST(Environment::SDFGICascades) VARIANT_ENUM_CAST(Environment::SDFGIYScale) VARIANT_ENUM_CAST(Environment::GlowBlendMode) diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp index 402e67a0f1..f9a4eba978 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -175,14 +175,12 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { #endif } } else { - Object *obj = nullptr; + //node belongs to this scene and must be created + Object *obj = ClassDB::instantiate(snames[n.type]); - if (ClassDB::is_class_enabled(snames[n.type])) { - //node belongs to this scene and must be created - obj = ClassDB::instantiate(snames[n.type]); - } + node = Object::cast_to<Node>(obj); - if (!Object::cast_to<Node>(obj)) { + if (!node) { if (obj) { memdelete(obj); obj = nullptr; @@ -203,9 +201,9 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { if (!obj) { obj = memnew(Node); } - } - node = Object::cast_to<Node>(obj); + node = Object::cast_to<Node>(obj); + } } if (node) { diff --git a/servers/display_server.cpp b/servers/display_server.cpp index e9f15ab535..eb12b5ffc0 100644 --- a/servers/display_server.cpp +++ b/servers/display_server.cpp @@ -139,10 +139,6 @@ void DisplayServer::mouse_warp_to_position(const Point2i &p_to) { WARN_PRINT("Mouse warping is not supported by this display server."); } -Point2i DisplayServer::mouse_get_absolute_position() const { - ERR_FAIL_V_MSG(Point2i(), "Mouse is not supported by this display server."); -} - Point2i DisplayServer::mouse_get_position() const { ERR_FAIL_V_MSG(Point2i(), "Mouse is not supported by this display server."); } @@ -359,7 +355,6 @@ void DisplayServer::_bind_methods() { ClassDB::bind_method(D_METHOD("mouse_warp_to_position", "position"), &DisplayServer::mouse_warp_to_position); ClassDB::bind_method(D_METHOD("mouse_get_position"), &DisplayServer::mouse_get_position); - ClassDB::bind_method(D_METHOD("mouse_get_absolute_position"), &DisplayServer::mouse_get_absolute_position); ClassDB::bind_method(D_METHOD("mouse_get_button_state"), &DisplayServer::mouse_get_button_state); ClassDB::bind_method(D_METHOD("clipboard_set", "clipboard"), &DisplayServer::clipboard_set); diff --git a/servers/display_server.h b/servers/display_server.h index d896572b88..2fb9b5946b 100644 --- a/servers/display_server.h +++ b/servers/display_server.h @@ -156,7 +156,6 @@ public: virtual void mouse_warp_to_position(const Point2i &p_to); virtual Point2i mouse_get_position() const; - virtual Point2i mouse_get_absolute_position() const; virtual MouseButton mouse_get_button_state() const; virtual void clipboard_set(const String &p_text); diff --git a/servers/rendering/rasterizer_dummy.h b/servers/rendering/rasterizer_dummy.h index f02a01c97d..5b12b757a3 100644 --- a/servers/rendering/rasterizer_dummy.h +++ b/servers/rendering/rasterizer_dummy.h @@ -120,7 +120,7 @@ public: void environment_set_ssil(RID p_env, bool p_enable, float p_radius, float p_intensity, float p_sharpness, float p_normal_rejection) override {} void environment_set_ssil_quality(RS::EnvironmentSSILQuality p_quality, bool p_half_size, float p_adaptive_target, int p_blur_passes, float p_fadeout_from, float p_fadeout_to) override {} - void environment_set_sdfgi(RID p_env, bool p_enable, RS::EnvironmentSDFGICascades p_cascades, float p_min_cell_size, RS::EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_probe_bias) override {} + void environment_set_sdfgi(RID p_env, bool p_enable, int p_cascades, float p_min_cell_size, RS::EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_probe_bias) override {} void environment_set_sdfgi_ray_count(RS::EnvironmentSDFGIRayCount p_ray_count) override {} void environment_set_sdfgi_frames_to_converge(RS::EnvironmentSDFGIFramesToConverge p_frames) override {} @@ -763,7 +763,7 @@ public: RendererCanvasRender *get_canvas() override { return &canvas; } RendererSceneRender *get_scene() override { return &scene; } - void set_boot_image(const Ref<Image> &p_image, const Color &p_color, bool p_scale, bool p_use_filter = true) override {} + void set_boot_image(const Ref<Image> &p_image, const Color &p_color, RenderingServer::SplashStretchMode p_stretch_mode, bool p_use_filter = true) override {} void initialize() override {} void begin_frame(double frame_step) override { diff --git a/servers/rendering/renderer_compositor.h b/servers/rendering/renderer_compositor.h index f245af9a4a..96a3c8af9f 100644 --- a/servers/rendering/renderer_compositor.h +++ b/servers/rendering/renderer_compositor.h @@ -76,7 +76,7 @@ public: virtual RendererCanvasRender *get_canvas() = 0; virtual RendererSceneRender *get_scene() = 0; - virtual void set_boot_image(const Ref<Image> &p_image, const Color &p_color, bool p_scale, bool p_use_filter = true) = 0; + virtual void set_boot_image(const Ref<Image> &p_image, const Color &p_color, RenderingServer::SplashStretchMode p_stretch_mode, bool p_use_filter = true) = 0; virtual void initialize() = 0; virtual void begin_frame(double frame_step) = 0; diff --git a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp index a27ea75017..7987a98b0e 100644 --- a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp @@ -463,7 +463,6 @@ SceneShaderForwardClustered::MaterialData::~MaterialData() { RendererStorageRD::MaterialData *SceneShaderForwardClustered::_create_material_func(ShaderData *p_shader) { MaterialData *material_data = memnew(MaterialData); material_data->shader_data = p_shader; - material_data->last_frame = false; //update will happen later anyway so do nothing. return material_data; } diff --git a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h index 8e7bbad63e..33049fad9c 100644 --- a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h +++ b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h @@ -189,7 +189,6 @@ public: } struct MaterialData : public RendererStorageRD::MaterialData { - uint64_t last_frame; ShaderData *shader_data; RID uniform_set; uint64_t last_pass = 0; diff --git a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp index 1613a307ec..0b99948063 100644 --- a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp +++ b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp @@ -452,7 +452,6 @@ SceneShaderForwardMobile::MaterialData::~MaterialData() { RendererStorageRD::MaterialData *SceneShaderForwardMobile::_create_material_func(ShaderData *p_shader) { MaterialData *material_data = memnew(MaterialData); material_data->shader_data = p_shader; - material_data->last_frame = false; //update will happen later anyway so do nothing. return material_data; } diff --git a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h index c136afd9f3..92db15e3b0 100644 --- a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h +++ b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h @@ -163,7 +163,6 @@ public: } struct MaterialData : public RendererStorageRD::MaterialData { - uint64_t last_frame; ShaderData *shader_data; RID uniform_set; uint64_t last_pass = 0; diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp index 7e188926e0..0f3daef371 100644 --- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp @@ -1377,14 +1377,6 @@ void RendererCanvasRenderRD::canvas_render_items(RID p_to_render_target, Item *p if (md->shader_data->uses_time) { time_used = true; } - if (md->last_frame != RendererCompositorRD::singleton->get_frame_number()) { - md->last_frame = RendererCompositorRD::singleton->get_frame_number(); - if (!RD::get_singleton()->uniform_set_is_valid(md->uniform_set)) { - // uniform set may be gone because a dependency was erased. In this case, it will happen - // if a texture is deleted, so just re-create it. - storage->material_force_update_textures(material, RendererStorageRD::SHADER_TYPE_2D); - } - } } } @@ -2240,7 +2232,6 @@ RendererCanvasRenderRD::MaterialData::~MaterialData() { RendererStorageRD::MaterialData *RendererCanvasRenderRD::_create_material_func(ShaderData *p_shader) { MaterialData *material_data = memnew(MaterialData); material_data->shader_data = p_shader; - material_data->last_frame = false; //update will happen later anyway so do nothing. return material_data; } diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h index b409264c9a..84f64b6fda 100644 --- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h +++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h @@ -200,7 +200,6 @@ class RendererCanvasRenderRD : public RendererCanvasRender { } struct MaterialData : public RendererStorageRD::MaterialData { - uint64_t last_frame; ShaderData *shader_data; RID uniform_set; diff --git a/servers/rendering/renderer_rd/renderer_compositor_rd.cpp b/servers/rendering/renderer_rd/renderer_compositor_rd.cpp index 2f8ef696cd..009a39749c 100644 --- a/servers/rendering/renderer_rd/renderer_compositor_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_compositor_rd.cpp @@ -159,7 +159,7 @@ void RendererCompositorRD::finalize() { RD::get_singleton()->free(blit.sampler); } -void RendererCompositorRD::set_boot_image(const Ref<Image> &p_image, const Color &p_color, bool p_scale, bool p_use_filter) { +void RendererCompositorRD::set_boot_image(const Ref<Image> &p_image, const Color &p_color, RenderingServer::SplashStretchMode p_stretch_mode, bool p_use_filter) { RD::get_singleton()->prepare_screen_for_drawing(); RID texture = storage->texture_allocate(); @@ -182,22 +182,56 @@ void RendererCompositorRD::set_boot_image(const Ref<Image> &p_image, const Color Rect2 imgrect(0, 0, p_image->get_width(), p_image->get_height()); Rect2 screenrect; - if (p_scale) { - if (window_size.width > window_size.height) { - //scale horizontally + switch (p_stretch_mode) { + case RenderingServer::SPLASH_STRETCH_MODE_DISABLED: { + screenrect = imgrect; + screenrect.position += ((window_size - screenrect.size) / 2.0).floor(); + } break; + case RenderingServer::SPLASH_STRETCH_MODE_KEEP: { + if (window_size.width > window_size.height) { + // Scale horizontally. + screenrect.size.y = window_size.height; + screenrect.size.x = imgrect.size.x * window_size.height / imgrect.size.y; + screenrect.position.x = (window_size.width - screenrect.size.x) / 2; + } else { + // Scale vertically. + screenrect.size.x = window_size.width; + screenrect.size.y = imgrect.size.y * window_size.width / imgrect.size.x; + screenrect.position.y = (window_size.height - screenrect.size.y) / 2; + } + } break; + case RenderingServer::SPLASH_STRETCH_MODE_KEEP_WIDTH: { + // Scale vertically. + screenrect.size.x = window_size.width; + screenrect.size.y = imgrect.size.y * window_size.width / imgrect.size.x; + screenrect.position.y = (window_size.height - screenrect.size.y) / 2; + } break; + case RenderingServer::SPLASH_STRETCH_MODE_KEEP_HEIGHT: { + // Scale horizontally. screenrect.size.y = window_size.height; screenrect.size.x = imgrect.size.x * window_size.height / imgrect.size.y; screenrect.position.x = (window_size.width - screenrect.size.x) / 2; - - } else { - //scale vertically + } break; + case RenderingServer::SPLASH_STRETCH_MODE_COVER: { + double window_aspect = (double)window_size.width / window_size.height; + double img_aspect = imgrect.size.x / imgrect.size.y; + + if (window_aspect > img_aspect) { + // Scale vertically. + screenrect.size.x = window_size.width; + screenrect.size.y = imgrect.size.y * window_size.width / imgrect.size.x; + screenrect.position.y = (window_size.height - screenrect.size.y) / 2; + } else { + // Scale horizontally. + screenrect.size.y = window_size.height; + screenrect.size.x = imgrect.size.x * window_size.height / imgrect.size.y; + screenrect.position.x = (window_size.width - screenrect.size.x) / 2; + } + } break; + case RenderingServer::SPLASH_STRETCH_MODE_EXPAND: { screenrect.size.x = window_size.width; - screenrect.size.y = imgrect.size.y * window_size.width / imgrect.size.x; - screenrect.position.y = (window_size.height - screenrect.size.y) / 2; - } - } else { - screenrect = imgrect; - screenrect.position += ((window_size - screenrect.size) / 2.0).floor(); + screenrect.size.y = window_size.height; + } break; } screenrect.position /= window_size; diff --git a/servers/rendering/renderer_rd/renderer_compositor_rd.h b/servers/rendering/renderer_rd/renderer_compositor_rd.h index 9a992d5819..6cfd6fa11b 100644 --- a/servers/rendering/renderer_rd/renderer_compositor_rd.h +++ b/servers/rendering/renderer_rd/renderer_compositor_rd.h @@ -90,7 +90,7 @@ public: RendererCanvasRender *get_canvas() { return canvas; } RendererSceneRender *get_scene() { return scene; } - void set_boot_image(const Ref<Image> &p_image, const Color &p_color, bool p_scale, bool p_use_filter); + void set_boot_image(const Ref<Image> &p_image, const Color &p_color, RenderingServer::SplashStretchMode p_stretch_mode, bool p_use_filter); void initialize(); void begin_frame(double frame_step); diff --git a/servers/rendering/renderer_rd/renderer_scene_environment_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_environment_rd.cpp index 7ea117ef33..0d0d7513d0 100644 --- a/servers/rendering/renderer_rd/renderer_scene_environment_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_scene_environment_rd.cpp @@ -68,7 +68,7 @@ void RendererSceneEnvironmentRD::set_glow(bool p_enable, Vector<float> p_levels, glow_hdr_luminance_cap = p_hdr_luminance_cap; } -void RendererSceneEnvironmentRD::set_sdfgi(bool p_enable, RS::EnvironmentSDFGICascades p_cascades, float p_min_cell_size, RS::EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_probe_bias) { +void RendererSceneEnvironmentRD::set_sdfgi(bool p_enable, int p_cascades, float p_min_cell_size, RS::EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_probe_bias) { sdfgi_enabled = p_enable; sdfgi_cascades = p_cascades; sdfgi_min_cell_size = p_min_cell_size; diff --git a/servers/rendering/renderer_rd/renderer_scene_environment_rd.h b/servers/rendering/renderer_rd/renderer_scene_environment_rd.h index 9e36a61870..629d224b49 100644 --- a/servers/rendering/renderer_rd/renderer_scene_environment_rd.h +++ b/servers/rendering/renderer_rd/renderer_scene_environment_rd.h @@ -133,7 +133,7 @@ public: /// SDFGI bool sdfgi_enabled = false; - RS::EnvironmentSDFGICascades sdfgi_cascades; + int sdfgi_cascades = 6; float sdfgi_min_cell_size = 0.2; bool sdfgi_use_occlusion = false; float sdfgi_bounce_feedback = 0.0; @@ -155,7 +155,7 @@ public: void set_ambient_light(const Color &p_color, RS::EnvironmentAmbientSource p_ambient, float p_energy, float p_sky_contribution, RS::EnvironmentReflectionSource p_reflection_source); void set_tonemap(RS::EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white, bool p_auto_exposure, float p_min_luminance, float p_max_luminance, float p_auto_exp_speed, float p_auto_exp_scale); void set_glow(bool p_enable, Vector<float> p_levels, float p_intensity, float p_strength, float p_mix, float p_bloom_threshold, RS::EnvironmentGlowBlendMode p_blend_mode, float p_hdr_bleed_threshold, float p_hdr_bleed_scale, float p_hdr_luminance_cap); - void set_sdfgi(bool p_enable, RS::EnvironmentSDFGICascades p_cascades, float p_min_cell_size, RS::EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_probe_bias); + void set_sdfgi(bool p_enable, int p_cascades, float p_min_cell_size, RS::EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_probe_bias); void set_fog(bool p_enable, const Color &p_light_color, float p_light_energy, float p_sun_scatter, float p_density, float p_height, float p_height_density, float p_fog_aerial_perspective); void set_volumetric_fog(bool p_enable, float p_density, const Color &p_scatterin, const Color &p_emission, float p_emission_energy, float p_anisotropy, float p_length, float p_detail_spread, float p_gi_inject, bool p_temporal_reprojection, float p_temporal_reprojection_amount, float p_ambient_inject); void set_ssr(bool p_enable, int p_max_steps, float p_fade_int, float p_fade_out, float p_depth_tolerance); diff --git a/servers/rendering/renderer_rd/renderer_scene_gi_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_gi_rd.cpp index 73cb088f6a..3069b1c379 100644 --- a/servers/rendering/renderer_rd/renderer_scene_gi_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_scene_gi_rd.cpp @@ -42,14 +42,13 @@ const Vector3i RendererSceneGIRD::SDFGI::Cascade::DIRTY_ALL = Vector3i(0x7FFFFFF void RendererSceneGIRD::SDFGI::create(RendererSceneEnvironmentRD *p_env, const Vector3 &p_world_position, uint32_t p_requested_history_size, RendererSceneGIRD *p_gi) { storage = p_gi->storage; gi = p_gi; - cascade_mode = p_env->sdfgi_cascades; + num_cascades = p_env->sdfgi_cascades; min_cell_size = p_env->sdfgi_min_cell_size; uses_occlusion = p_env->sdfgi_use_occlusion; y_scale_mode = p_env->sdfgi_y_scale; static const float y_scale[3] = { 1.0, 1.5, 2.0 }; y_mult = y_scale[y_scale_mode]; - static const int cascasde_size[3] = { 4, 6, 8 }; - cascades.resize(cascasde_size[cascade_mode]); + cascades.resize(num_cascades); probe_axis_count = SDFGI::PROBE_DIVISOR + 1; solid_cell_ratio = gi->sdfgi_solid_cell_ratio; solid_cell_count = uint32_t(float(cascade_size * cascade_size * cascade_size) * solid_cell_ratio); @@ -716,7 +715,10 @@ void RendererSceneGIRD::SDFGI::create(RendererSceneEnvironmentRD *p_env, const V u.uniform_type = RD::UNIFORM_TYPE_IMAGE; u.binding = 13; RID parent_average; - if (i < cascades.size() - 1) { + if (cascades.size() == 1) { + // If there is only one SDFGI cascade, we can't use the previous cascade for blending. + parent_average = cascades[i].lightprobe_average_tex; + } else if (i < cascades.size() - 1) { parent_average = cascades[i + 1].lightprobe_average_tex; } else { parent_average = cascades[i - 1].lightprobe_average_tex; //to use something, but it won't be used diff --git a/servers/rendering/renderer_rd/renderer_scene_gi_rd.h b/servers/rendering/renderer_rd/renderer_scene_gi_rd.h index a407199d0a..5e55262798 100644 --- a/servers/rendering/renderer_rd/renderer_scene_gi_rd.h +++ b/servers/rendering/renderer_rd/renderer_scene_gi_rd.h @@ -495,7 +495,7 @@ public: float solid_cell_ratio = 0; uint32_t solid_cell_count = 0; - RS::EnvironmentSDFGICascades cascade_mode; + int num_cascades = 6; float min_cell_size = 0; uint32_t probe_axis_count = 0; //amount of probes per axis, this is an odd number because it encloses endpoints diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp index f6d58cb094..33f5a178e0 100644 --- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp @@ -65,7 +65,7 @@ void RendererSceneRenderRD::sdfgi_update(RID p_render_buffers, RID p_environment static const uint32_t history_frames_to_converge[RS::ENV_SDFGI_CONVERGE_MAX] = { 5, 10, 15, 20, 25, 30 }; uint32_t requested_history_size = history_frames_to_converge[gi.sdfgi_frames_to_converge]; - if (rb->sdfgi && (rb->sdfgi->cascade_mode != env->sdfgi_cascades || rb->sdfgi->min_cell_size != env->sdfgi_min_cell_size || requested_history_size != rb->sdfgi->history_size || rb->sdfgi->uses_occlusion != env->sdfgi_use_occlusion || rb->sdfgi->y_scale_mode != env->sdfgi_y_scale)) { + if (rb->sdfgi && (rb->sdfgi->num_cascades != env->sdfgi_cascades || rb->sdfgi->min_cell_size != env->sdfgi_min_cell_size || requested_history_size != rb->sdfgi->history_size || rb->sdfgi->uses_occlusion != env->sdfgi_use_occlusion || rb->sdfgi->y_scale_mode != env->sdfgi_y_scale)) { //configuration changed, erase rb->sdfgi->erase(); memdelete(rb->sdfgi); @@ -303,7 +303,7 @@ void RendererSceneRenderRD::environment_glow_set_use_high_quality(bool p_enable) glow_high_quality = p_enable; } -void RendererSceneRenderRD::environment_set_sdfgi(RID p_env, bool p_enable, RS::EnvironmentSDFGICascades p_cascades, float p_min_cell_size, RS::EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_probe_bias) { +void RendererSceneRenderRD::environment_set_sdfgi(RID p_env, bool p_enable, int p_cascades, float p_min_cell_size, RS::EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_probe_bias) { RendererSceneEnvironmentRD *env = environment_owner.get_or_null(p_env); ERR_FAIL_COND(!env); @@ -3993,7 +3993,6 @@ RendererStorageRD::ShaderData *RendererSceneRenderRD::_create_fog_shader_funcs() RendererStorageRD::MaterialData *RendererSceneRenderRD::_create_fog_material_func(FogShaderData *p_shader) { FogMaterialData *material_data = memnew(FogMaterialData); material_data->shader_data = p_shader; - material_data->last_frame = false; //update will happen later anyway so do nothing. return material_data; } diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.h b/servers/rendering/renderer_rd/renderer_scene_render_rd.h index 6432ca99f0..276cb8f229 100644 --- a/servers/rendering/renderer_rd/renderer_scene_render_rd.h +++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.h @@ -942,7 +942,6 @@ private: }; struct FogMaterialData : public RendererStorageRD::MaterialData { - uint64_t last_frame; FogShaderData *shader_data; RID uniform_set; bool uniform_set_updated; @@ -1093,7 +1092,7 @@ public: bool environment_is_ssr_enabled(RID p_env) const; bool environment_is_sdfgi_enabled(RID p_env) const; - virtual void environment_set_sdfgi(RID p_env, bool p_enable, RS::EnvironmentSDFGICascades p_cascades, float p_min_cell_size, RS::EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_probe_bias) override; + virtual void environment_set_sdfgi(RID p_env, bool p_enable, int p_cascades, float p_min_cell_size, RS::EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_probe_bias) override; virtual void environment_set_sdfgi_ray_count(RS::EnvironmentSDFGIRayCount p_ray_count) override; virtual void environment_set_sdfgi_frames_to_converge(RS::EnvironmentSDFGIFramesToConverge p_frames) override; virtual void environment_set_sdfgi_frames_to_update_light(RS::EnvironmentSDFGIFramesToUpdateLight p_update) override; diff --git a/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp index f0419b7907..856ea5e74d 100644 --- a/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp @@ -750,7 +750,6 @@ RendererStorageRD::ShaderData *RendererSceneSkyRD::_create_sky_shader_funcs() { RendererStorageRD::MaterialData *RendererSceneSkyRD::_create_sky_material_func(SkyShaderData *p_shader) { SkyMaterialData *material_data = memnew(SkyMaterialData); material_data->shader_data = p_shader; - material_data->last_frame = false; //update will happen later anyway so do nothing. return material_data; } diff --git a/servers/rendering/renderer_rd/renderer_scene_sky_rd.h b/servers/rendering/renderer_rd/renderer_scene_sky_rd.h index 46d376e667..d81a415c2d 100644 --- a/servers/rendering/renderer_rd/renderer_scene_sky_rd.h +++ b/servers/rendering/renderer_rd/renderer_scene_sky_rd.h @@ -228,7 +228,6 @@ public: } sky_shader; struct SkyMaterialData : public RendererStorageRD::MaterialData { - uint64_t last_frame; SkyShaderData *shader_data; RID uniform_set; bool uniform_set_updated; diff --git a/servers/rendering/renderer_rd/renderer_storage_rd.cpp b/servers/rendering/renderer_rd/renderer_storage_rd.cpp index 19075fab86..2e63ac57d9 100644 --- a/servers/rendering/renderer_rd/renderer_storage_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_storage_rd.cpp @@ -2962,24 +2962,19 @@ bool RendererStorageRD::MaterialData::update_parameters_uniform_set(const Map<St return true; } -void RendererStorageRD::_material_uniform_set_erased(const RID &p_set, void *p_material) { +void RendererStorageRD::_material_uniform_set_erased(void *p_material) { RID rid = *(RID *)p_material; Material *material = base_singleton->material_owner.get_or_null(rid); if (material) { + if (material->data) { + // Uniform set may be gone because a dependency was erased. This happens + // if a texture is deleted, so re-create it. + base_singleton->_material_queue_update(material, false, true); + } material->dependency.changed_notify(DEPENDENCY_CHANGED_MATERIAL); } } -void RendererStorageRD::material_force_update_textures(RID p_material, ShaderType p_shader_type) { - Material *material = material_owner.get_or_null(p_material); - if (material->shader_type != p_shader_type) { - return; - } - if (material->data) { - material->data->update_parameters(material->params, false, true); - } -} - void RendererStorageRD::_update_queued_materials() { while (material_update_list.first()) { Material *material = material_update_list.first()->self(); @@ -5882,8 +5877,6 @@ RendererStorageRD::ShaderData *RendererStorageRD::_create_particles_shader_func( } bool RendererStorageRD::ParticlesMaterialData::update_parameters(const Map<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty) { - uniform_set_updated = true; - return update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set, base_singleton->particles_shader.shader.version_get_shader(shader_data->version, 0), 3); } @@ -5894,7 +5887,6 @@ RendererStorageRD::ParticlesMaterialData::~ParticlesMaterialData() { RendererStorageRD::MaterialData *RendererStorageRD::_create_particles_material_func(ParticlesShaderData *p_shader) { ParticlesMaterialData *material_data = memnew(ParticlesMaterialData); material_data->shader_data = p_shader; - material_data->last_frame = false; //update will happen later anyway so do nothing. return material_data; } diff --git a/servers/rendering/renderer_rd/renderer_storage_rd.h b/servers/rendering/renderer_rd/renderer_storage_rd.h index 8c04274c3f..43bbcf6520 100644 --- a/servers/rendering/renderer_rd/renderer_storage_rd.h +++ b/servers/rendering/renderer_rd/renderer_storage_rd.h @@ -177,7 +177,7 @@ public: Vector<RID> texture_cache; }; typedef MaterialData *(*MaterialDataRequestFunction)(ShaderData *); - static void _material_uniform_set_erased(const RID &p_set, void *p_material); + static void _material_uniform_set_erased(void *p_material); enum DefaultRDTexture { DEFAULT_RD_TEXTURE_WHITE, @@ -910,10 +910,8 @@ private: } struct ParticlesMaterialData : public MaterialData { - uint64_t last_frame = 0; ParticlesShaderData *shader_data = nullptr; RID uniform_set; - bool uniform_set_updated = false; virtual void set_render_priority(int p_priority) {} virtual void set_next_pass(RID p_pass) {} @@ -1448,7 +1446,6 @@ public: void material_get_instance_shader_parameters(RID p_material, List<InstanceShaderParam> *r_parameters); void material_update_dependency(RID p_material, DependencyTracker *p_instance); - void material_force_update_textures(RID p_material, ShaderType p_shader_type); void material_set_data_request_function(ShaderType p_shader_type, MaterialDataRequestFunction p_function); diff --git a/servers/rendering/renderer_scene.h b/servers/rendering/renderer_scene.h index 20ca49cd71..426d22f83e 100644 --- a/servers/rendering/renderer_scene.h +++ b/servers/rendering/renderer_scene.h @@ -149,7 +149,7 @@ public: virtual void environment_set_ssil(RID p_env, bool p_enable, float p_radius, float p_intensity, float p_sharpness, float p_normal_rejection) = 0; virtual void environment_set_ssil_quality(RS::EnvironmentSSILQuality p_quality, bool p_half_size, float p_adaptive_target, int p_blur_passes, float p_fadeout_from, float p_fadeout_to) = 0; - virtual void environment_set_sdfgi(RID p_env, bool p_enable, RS::EnvironmentSDFGICascades p_cascades, float p_min_cell_size, RS::EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_probe_bias) = 0; + virtual void environment_set_sdfgi(RID p_env, bool p_enable, int p_cascades, float p_min_cell_size, RS::EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_probe_bias) = 0; virtual void environment_set_sdfgi_ray_count(RS::EnvironmentSDFGIRayCount p_ray_count) = 0; virtual void environment_set_sdfgi_frames_to_converge(RS::EnvironmentSDFGIFramesToConverge p_frames) = 0; diff --git a/servers/rendering/renderer_scene_cull.h b/servers/rendering/renderer_scene_cull.h index 071d88233f..1e770ef66c 100644 --- a/servers/rendering/renderer_scene_cull.h +++ b/servers/rendering/renderer_scene_cull.h @@ -1127,7 +1127,7 @@ public: PASS2(environment_set_volumetric_fog_volume_size, int, int) PASS1(environment_set_volumetric_fog_filter_active, bool) - PASS11(environment_set_sdfgi, RID, bool, RS::EnvironmentSDFGICascades, float, RS::EnvironmentSDFGIYScale, bool, float, bool, float, float, float) + PASS11(environment_set_sdfgi, RID, bool, int, float, RS::EnvironmentSDFGIYScale, bool, float, bool, float, float, float) PASS1(environment_set_sdfgi_ray_count, RS::EnvironmentSDFGIRayCount) PASS1(environment_set_sdfgi_frames_to_converge, RS::EnvironmentSDFGIFramesToConverge) PASS1(environment_set_sdfgi_frames_to_update_light, RS::EnvironmentSDFGIFramesToUpdateLight) diff --git a/servers/rendering/renderer_scene_render.h b/servers/rendering/renderer_scene_render.h index f99d34d292..c34a46d166 100644 --- a/servers/rendering/renderer_scene_render.h +++ b/servers/rendering/renderer_scene_render.h @@ -140,7 +140,7 @@ public: virtual void environment_set_ssil(RID p_env, bool p_enable, float p_radius, float p_intensity, float p_sharpness, float p_normal_rejection) = 0; virtual void environment_set_ssil_quality(RS::EnvironmentSSILQuality p_quality, bool p_half_size, float p_adaptive_target, int p_blur_passes, float p_fadeout_from, float p_fadeout_to) = 0; - virtual void environment_set_sdfgi(RID p_env, bool p_enable, RS::EnvironmentSDFGICascades p_cascades, float p_min_cell_size, RS::EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_probe_bias) = 0; + virtual void environment_set_sdfgi(RID p_env, bool p_enable, int p_cascades, float p_min_cell_size, RS::EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_probe_bias) = 0; virtual void environment_set_sdfgi_ray_count(RS::EnvironmentSDFGIRayCount p_ray_count) = 0; virtual void environment_set_sdfgi_frames_to_converge(RS::EnvironmentSDFGIFramesToConverge p_frames) = 0; diff --git a/servers/rendering/rendering_device.h b/servers/rendering/rendering_device.h index 3e74741de0..655a32a805 100644 --- a/servers/rendering/rendering_device.h +++ b/servers/rendering/rendering_device.h @@ -742,7 +742,7 @@ public: virtual RID uniform_set_create(const Vector<Uniform> &p_uniforms, RID p_shader, uint32_t p_shader_set) = 0; virtual bool uniform_set_is_valid(RID p_uniform_set) = 0; - typedef void (*UniformSetInvalidatedCallback)(const RID &, void *); + typedef void (*UniformSetInvalidatedCallback)(void *); virtual void uniform_set_set_invalidation_callback(RID p_uniform_set, UniformSetInvalidatedCallback p_callback, void *p_userdata) = 0; virtual Error buffer_update(RID p_buffer, uint32_t p_offset, uint32_t p_size, const void *p_data, uint32_t p_post_barrier = BARRIER_MASK_ALL) = 0; diff --git a/servers/rendering/rendering_server_default.cpp b/servers/rendering/rendering_server_default.cpp index d7e9d210db..734206c70b 100644 --- a/servers/rendering/rendering_server_default.cpp +++ b/servers/rendering/rendering_server_default.cpp @@ -273,9 +273,9 @@ Vector<RenderingServer::FrameProfileArea> RenderingServerDefault::get_frame_prof /* TESTING */ -void RenderingServerDefault::set_boot_image(const Ref<Image> &p_image, const Color &p_color, bool p_scale, bool p_use_filter) { +void RenderingServerDefault::set_boot_image(const Ref<Image> &p_image, const Color &p_color, RenderingServer::SplashStretchMode p_stretch_mode, bool p_use_filter) { redraw_request(); - RSG::rasterizer->set_boot_image(p_image, p_color, p_scale, p_use_filter); + RSG::rasterizer->set_boot_image(p_image, p_color, p_stretch_mode, p_use_filter); } void RenderingServerDefault::set_default_clear_color(const Color &p_color) { diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h index ead49f053c..dabea9175e 100644 --- a/servers/rendering/rendering_server_default.h +++ b/servers/rendering/rendering_server_default.h @@ -643,7 +643,7 @@ public: FUNC2(environment_set_volumetric_fog_volume_size, int, int) FUNC1(environment_set_volumetric_fog_filter_active, bool) - FUNC11(environment_set_sdfgi, RID, bool, EnvironmentSDFGICascades, float, EnvironmentSDFGIYScale, bool, float, bool, float, float, float) + FUNC11(environment_set_sdfgi, RID, bool, int, float, EnvironmentSDFGIYScale, bool, float, bool, float, float, float) FUNC1(environment_set_sdfgi_ray_count, EnvironmentSDFGIRayCount) FUNC1(environment_set_sdfgi_frames_to_converge, EnvironmentSDFGIFramesToConverge) FUNC1(environment_set_sdfgi_frames_to_update_light, EnvironmentSDFGIFramesToUpdateLight) @@ -907,7 +907,7 @@ public: virtual double get_frame_setup_time_cpu() const override; - virtual void set_boot_image(const Ref<Image> &p_image, const Color &p_color, bool p_scale, bool p_use_filter = true) override; + virtual void set_boot_image(const Ref<Image> &p_image, const Color &p_color, RenderingServer::SplashStretchMode p_stretch_mode, bool p_use_filter = true) override; virtual void set_default_clear_color(const Color &p_color) override; virtual bool has_feature(Features p_feature) const override; diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index 020c850f46..786cd8ad92 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -2388,10 +2388,6 @@ void RenderingServer::_bind_methods() { BIND_ENUM_CONSTANT(ENV_SSIL_QUALITY_HIGH); BIND_ENUM_CONSTANT(ENV_SSIL_QUALITY_ULTRA); - BIND_ENUM_CONSTANT(ENV_SDFGI_CASCADES_4); - BIND_ENUM_CONSTANT(ENV_SDFGI_CASCADES_6); - BIND_ENUM_CONSTANT(ENV_SDFGI_CASCADES_8); - BIND_ENUM_CONSTANT(ENV_SDFGI_Y_SCALE_DISABLED); BIND_ENUM_CONSTANT(ENV_SDFGI_Y_SCALE_75_PERCENT); BIND_ENUM_CONSTANT(ENV_SDFGI_Y_SCALE_50_PERCENT); @@ -2734,7 +2730,7 @@ void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("get_test_texture"), &RenderingServer::get_test_texture); ClassDB::bind_method(D_METHOD("get_white_texture"), &RenderingServer::get_white_texture); - ClassDB::bind_method(D_METHOD("set_boot_image", "image", "color", "scale", "use_filter"), &RenderingServer::set_boot_image, DEFVAL(true)); + ClassDB::bind_method(D_METHOD("set_boot_image", "image", "color", "stretch_mode", "use_filter"), &RenderingServer::set_boot_image, DEFVAL(true)); ClassDB::bind_method(D_METHOD("set_default_clear_color", "color"), &RenderingServer::set_default_clear_color); ClassDB::bind_method(D_METHOD("has_feature", "feature"), &RenderingServer::has_feature); @@ -2755,6 +2751,13 @@ void RenderingServer::_bind_methods() { BIND_ENUM_CONSTANT(RENDERING_INFO_BUFFER_MEM_USED); BIND_ENUM_CONSTANT(RENDERING_INFO_VIDEO_MEM_USED); + BIND_ENUM_CONSTANT(SPLASH_STRETCH_MODE_DISABLED); + BIND_ENUM_CONSTANT(SPLASH_STRETCH_MODE_KEEP); + BIND_ENUM_CONSTANT(SPLASH_STRETCH_MODE_KEEP_WIDTH); + BIND_ENUM_CONSTANT(SPLASH_STRETCH_MODE_KEEP_HEIGHT); + BIND_ENUM_CONSTANT(SPLASH_STRETCH_MODE_COVER); + BIND_ENUM_CONSTANT(SPLASH_STRETCH_MODE_EXPAND); + BIND_ENUM_CONSTANT(FEATURE_SHADERS); BIND_ENUM_CONSTANT(FEATURE_MULTITHREADED); diff --git a/servers/rendering_server.h b/servers/rendering_server.h index 5dbec04665..21728af8c4 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -1041,19 +1041,13 @@ public: virtual void environment_set_ssil_quality(EnvironmentSSILQuality p_quality, bool p_half_size, float p_adaptive_target, int p_blur_passes, float p_fadeout_from, float p_fadeout_to) = 0; - enum EnvironmentSDFGICascades { - ENV_SDFGI_CASCADES_4, - ENV_SDFGI_CASCADES_6, - ENV_SDFGI_CASCADES_8, - }; - enum EnvironmentSDFGIYScale { ENV_SDFGI_Y_SCALE_DISABLED, ENV_SDFGI_Y_SCALE_75_PERCENT, ENV_SDFGI_Y_SCALE_50_PERCENT }; - virtual void environment_set_sdfgi(RID p_env, bool p_enable, EnvironmentSDFGICascades p_cascades, float p_min_cell_size, EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_probe_bias) = 0; + virtual void environment_set_sdfgi(RID p_env, bool p_enable, int p_cascades, float p_min_cell_size, EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, float p_bounce_feedback, bool p_read_sky, float p_energy, float p_normal_bias, float p_probe_bias) = 0; enum EnvironmentSDFGIRayCount { ENV_SDFGI_RAY_COUNT_4, @@ -1516,7 +1510,16 @@ public: virtual void mesh_add_surface_from_mesh_data(RID p_mesh, const Geometry3D::MeshData &p_mesh_data); virtual void mesh_add_surface_from_planes(RID p_mesh, const Vector<Plane> &p_planes); - virtual void set_boot_image(const Ref<Image> &p_image, const Color &p_color, bool p_scale, bool p_use_filter = true) = 0; + enum SplashStretchMode { + SPLASH_STRETCH_MODE_DISABLED, + SPLASH_STRETCH_MODE_KEEP, + SPLASH_STRETCH_MODE_KEEP_WIDTH, + SPLASH_STRETCH_MODE_KEEP_HEIGHT, + SPLASH_STRETCH_MODE_COVER, + SPLASH_STRETCH_MODE_EXPAND, + }; + + virtual void set_boot_image(const Ref<Image> &p_image, const Color &p_color, RenderingServer::SplashStretchMode p_stretch_mode, bool p_use_filter = true) = 0; virtual void set_default_clear_color(const Color &p_color) = 0; enum Features { @@ -1608,7 +1611,6 @@ VARIANT_ENUM_CAST(RenderingServer::EnvironmentToneMapper); VARIANT_ENUM_CAST(RenderingServer::EnvironmentSSRRoughnessQuality); VARIANT_ENUM_CAST(RenderingServer::EnvironmentSSAOQuality); VARIANT_ENUM_CAST(RenderingServer::EnvironmentSSILQuality); -VARIANT_ENUM_CAST(RenderingServer::EnvironmentSDFGICascades); VARIANT_ENUM_CAST(RenderingServer::EnvironmentSDFGIFramesToConverge); VARIANT_ENUM_CAST(RenderingServer::EnvironmentSDFGIRayCount); VARIANT_ENUM_CAST(RenderingServer::EnvironmentSDFGIFramesToUpdateLight); @@ -1631,6 +1633,7 @@ VARIANT_ENUM_CAST(RenderingServer::CanvasLightShadowFilter); VARIANT_ENUM_CAST(RenderingServer::CanvasOccluderPolygonCullMode); VARIANT_ENUM_CAST(RenderingServer::GlobalVariableType); VARIANT_ENUM_CAST(RenderingServer::RenderingInfo); +VARIANT_ENUM_CAST(RenderingServer::SplashStretchMode); VARIANT_ENUM_CAST(RenderingServer::Features); VARIANT_ENUM_CAST(RenderingServer::CanvasTextureChannel); VARIANT_ENUM_CAST(RenderingServer::BakeChannels); |