From 8ff64ca176b332dbf0355824b709915757fc3db2 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 25 Jul 2022 08:19:01 +0400 Subject: [PATCH 1/5] Add a default way to store arrays in the settings.cfg --- apps/openmw/mwgui/postprocessorhud.cpp | 9 +++---- apps/openmw/mwrender/postprocessor.cpp | 5 +--- components/settings/settings.cpp | 34 ++++++++++++++++++++++++++ components/settings/settings.hpp | 3 +++ 4 files changed, 41 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwgui/postprocessorhud.cpp b/apps/openmw/mwgui/postprocessorhud.cpp index 31449bbee6..1653d2f1d5 100644 --- a/apps/openmw/mwgui/postprocessorhud.cpp +++ b/apps/openmw/mwgui/postprocessorhud.cpp @@ -23,7 +23,7 @@ namespace { auto* processor = MWBase::Environment::get().getWorld()->getPostProcessor(); - std::ostringstream chain; + std::vector chain; for (size_t i = 1; i < processor->getTechniques().size(); ++i) { @@ -32,13 +32,10 @@ namespace if (!technique || technique->getDynamic()) continue; - chain << technique->getName(); - - if (i < processor-> getTechniques().size() - 1) - chain << ","; + chain.push_back(technique->getName()); } - Settings::Manager::setString("chain", "Post Processing", chain.str()); + Settings::Manager::setStringArray("chain", "Post Processing", chain); } } diff --git a/apps/openmw/mwrender/postprocessor.cpp b/apps/openmw/mwrender/postprocessor.cpp index 0991ede7f2..4902dc8ef8 100644 --- a/apps/openmw/mwrender/postprocessor.cpp +++ b/apps/openmw/mwrender/postprocessor.cpp @@ -830,8 +830,7 @@ namespace MWRender mTechniques.clear(); - std::vector techniqueStrings; - Misc::StringUtils::split(Settings::Manager::getString("chain", "Post Processing"), techniqueStrings, ","); + std::vector techniqueStrings = Settings::Manager::getStringArray("chain", "Post Processing"); const std::string mainIdentifier = "main"; @@ -844,8 +843,6 @@ namespace MWRender for (auto& techniqueName : techniqueStrings) { - Misc::StringUtils::trim(techniqueName); - if (techniqueName.empty() || Misc::StringUtils::ciEqual(techniqueName, mainIdentifier)) continue; diff --git a/components/settings/settings.cpp b/components/settings/settings.cpp index ff4a40d707..dfd124f3cf 100644 --- a/components/settings/settings.cpp +++ b/components/settings/settings.cpp @@ -88,6 +88,22 @@ std::string Manager::getString(std::string_view setting, std::string_view catego throw std::runtime_error(error); } +std::vector Manager::getStringArray(std::string_view setting, std::string_view category) +{ + // TODO: it is unclear how to handle empty value - + // there is no difference between empty serialized array + // and a serialized array which has one empty value + std::vector values; + const std::string& value = getString(setting, category); + if (value.empty()) + return values; + + Misc::StringUtils::split(value, values, ","); + for (auto& item : values) + Misc::StringUtils::trim(item); + return values; +} + float Manager::getFloat(std::string_view setting, std::string_view category) { const std::string& value = getString(setting, category); @@ -168,6 +184,24 @@ void Manager::setString(std::string_view setting, std::string_view category, con mChangedSettings.insert(std::move(key)); } +void Manager::setStringArray(std::string_view setting, std::string_view category, const std::vector &value) +{ + std::stringstream stream; + + // TODO: escape delimeters, new line characters, etc. + for (size_t i = 0; i < value.size(); ++i) + { + std::string item = value[i]; + Misc::StringUtils::trim(item); + stream << item; + + if (i < value.size() - 1) + stream << ","; + } + + setString(setting, category, stream.str()); +} + void Manager::setInt(std::string_view setting, std::string_view category, const int value) { std::ostringstream stream; diff --git a/components/settings/settings.hpp b/components/settings/settings.hpp index 32157bd3d2..178b8d3ebc 100644 --- a/components/settings/settings.hpp +++ b/components/settings/settings.hpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -63,6 +64,7 @@ namespace Settings static float getFloat(std::string_view setting, std::string_view category); static double getDouble(std::string_view setting, std::string_view category); static std::string getString(std::string_view setting, std::string_view category); + static std::vector getStringArray(std::string_view setting, std::string_view category); static bool getBool(std::string_view setting, std::string_view category); static osg::Vec2f getVector2(std::string_view setting, std::string_view category); static osg::Vec3f getVector3(std::string_view setting, std::string_view category); @@ -72,6 +74,7 @@ namespace Settings static void setFloat(std::string_view setting, std::string_view category, float value); static void setDouble(std::string_view setting, std::string_view category, double value); static void setString(std::string_view setting, std::string_view category, const std::string& value); + static void setStringArray(std::string_view setting, std::string_view category, const std::vector &value); static void setBool(std::string_view setting, std::string_view category, bool value); static void setVector2(std::string_view setting, std::string_view category, osg::Vec2f value); static void setVector3(std::string_view setting, std::string_view category, osg::Vec3f value); From cb64f49ba1b6d08da6b0cfbd659d1352dd92316a Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Mon, 25 Jul 2022 08:54:05 +0400 Subject: [PATCH 2/5] Allow to configure locales via settings menu --- apps/openmw/mwgui/settingswindow.cpp | 74 +++++++++++++++++++ apps/openmw/mwgui/settingswindow.hpp | 7 ++ apps/openmw/mwlua/luamanagerimp.cpp | 4 +- .../reference/modding/settings/general.rst | 2 +- files/data/l10n/SettingsMenu/en.yaml | 6 ++ files/data/l10n/SettingsMenu/ru.yaml | 6 ++ .../data/mygui/openmw_settings_window.layout | 22 ++++++ 7 files changed, 117 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index d38763663a..1a63f98a8f 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -5,6 +5,8 @@ #include #include +#include + #include #include #include @@ -18,12 +20,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -254,6 +258,8 @@ namespace MWGui getWidget(mWaterTextureSize, "WaterTextureSize"); getWidget(mWaterReflectionDetail, "WaterReflectionDetail"); getWidget(mWaterRainRippleDetail, "WaterRainRippleDetail"); + getWidget(mPrimaryLanguage, "PrimaryLanguage"); + getWidget(mSecondaryLanguage, "SecondaryLanguage"); getWidget(mLightingMethodButton, "LightingMethodButton"); getWidget(mLightsResetButton, "LightsResetButton"); getWidget(mMaxLights, "MaxLights"); @@ -297,6 +303,9 @@ namespace MWGui mKeyboardSwitch->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onKeyboardSwitchClicked); mControllerSwitch->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onControllerSwitchClicked); + mPrimaryLanguage->eventComboChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onPrimaryLanguageChanged); + mSecondaryLanguage->eventComboChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onSecondaryLanguageChanged); + computeMinimumWindowSize(); center(); @@ -353,6 +362,55 @@ namespace MWGui mScriptFilter->eventEditTextChange += MyGUI::newDelegate(this, &SettingsWindow::onScriptFilterChange); mScriptList->eventListMouseItemActivate += MyGUI::newDelegate(this, &SettingsWindow::onScriptListSelection); + + std::vector availableLanguages; + const VFS::Manager* vfs = MWBase::Environment::get().getResourceSystem()->getVFS(); + for (const auto& path : vfs->getRecursiveDirectoryIterator("l10n/")) + { + if (Misc::getFileExtension(path) == "yaml") + { + std::string localeName(Misc::stemFile(path)); + if (std::find(availableLanguages.begin(), availableLanguages.end(), localeName) == availableLanguages.end()) + availableLanguages.push_back(localeName); + } + } + + std::sort (availableLanguages.begin(), availableLanguages.end()); + + std::vector currentLocales = Settings::Manager::getStringArray("preferred locales", "General"); + if (currentLocales.empty()) + currentLocales.push_back("en"); + + icu::Locale primaryLocale(currentLocales[0].c_str()); + + mPrimaryLanguage->removeAllItems(); + mSecondaryLanguage->removeAllItems(); + + size_t i = 0, primaryLocaleIndex = MyGUI::ITEM_NONE, secondaryLocaleIndex = MyGUI::ITEM_NONE; + for (const auto& language : availableLanguages) + { + icu::Locale locale(language.c_str()); + + icu::UnicodeString str(language.c_str()); + locale.getDisplayName(primaryLocale, str); + std::string localeString; + str.toUTF8String(localeString); + mPrimaryLanguage->addItem(localeString); + mSecondaryLanguage->addItem(localeString); + + if (language == currentLocales[0]) + primaryLocaleIndex = i; + if (currentLocales.size() > 1 && language == currentLocales[1]) + secondaryLocaleIndex = i; + + i++; + } + + mPrimaryLanguage->setUserData(availableLanguages); + mSecondaryLanguage->setUserData(availableLanguages); + + mPrimaryLanguage->setIndexSelected(primaryLocaleIndex); + mSecondaryLanguage->setIndexSelected(secondaryLocaleIndex); } void SettingsWindow::onTabChanged(MyGUI::TabControl* /*_sender*/, size_t /*index*/) @@ -454,6 +512,22 @@ namespace MWGui apply(); } + void SettingsWindow::onLanguageChanged(size_t langPriority, MyGUI::ComboBox* _sender, size_t pos) + { + if (pos == MyGUI::ITEM_NONE) + return; + + MWBase::Environment::get().getWindowManager()->interactiveMessageBox("#{SettingsMenu:ChangeRequiresRestart}", {"#{sOK}"}, true); + const auto languageNames = _sender->getUserData>(); + + std::vector currentLocales = Settings::Manager::getStringArray("preferred locales", "General"); + if (currentLocales.size() <= langPriority) + currentLocales.resize(langPriority + 1, "en"); + currentLocales[langPriority] = languageNames->at(pos); + + Settings::Manager::setStringArray("preferred locales", "General", currentLocales); + } + void SettingsWindow::onWindowModeChanged(MyGUI::ComboBox* _sender, size_t pos) { if (pos == MyGUI::ITEM_NONE) diff --git a/apps/openmw/mwgui/settingswindow.hpp b/apps/openmw/mwgui/settingswindow.hpp index 6bc6b7c610..451ba7a7a5 100644 --- a/apps/openmw/mwgui/settingswindow.hpp +++ b/apps/openmw/mwgui/settingswindow.hpp @@ -40,6 +40,9 @@ namespace MWGui MyGUI::ComboBox* mLightingMethodButton; MyGUI::Button* mLightsResetButton; + MyGUI::ComboBox* mPrimaryLanguage; + MyGUI::ComboBox* mSecondaryLanguage; + // controls MyGUI::ScrollView* mControlsBox; MyGUI::Button* mResetControlsButton; @@ -72,6 +75,10 @@ namespace MWGui void onLightsResetButtonClicked(MyGUI::Widget* _sender); void onMaxLightsChanged(MyGUI::ComboBox* _sender, size_t pos); + void onPrimaryLanguageChanged(MyGUI::ComboBox* _sender, size_t pos) { onLanguageChanged(0, _sender, pos); } + void onSecondaryLanguageChanged(MyGUI::ComboBox* _sender, size_t pos) { onLanguageChanged(1, _sender, pos); } + void onLanguageChanged(size_t langPriority, MyGUI::ComboBox* _sender, size_t pos); + void onWindowModeChanged(MyGUI::ComboBox* _sender, size_t pos); void onRebindAction(MyGUI::Widget* _sender); diff --git a/apps/openmw/mwlua/luamanagerimp.cpp b/apps/openmw/mwlua/luamanagerimp.cpp index 1678743257..fba78e47de 100644 --- a/apps/openmw/mwlua/luamanagerimp.cpp +++ b/apps/openmw/mwlua/luamanagerimp.cpp @@ -58,9 +58,7 @@ namespace MWLua void LuaManager::initL10n() { mL10n.init(); - std::vector preferredLocales; - Misc::StringUtils::split(Settings::Manager::getString("preferred locales", "General"), preferredLocales, ", "); - mL10n.setPreferredLocales(preferredLocales); + mL10n.setPreferredLocales(Settings::Manager::getStringArray("preferred locales", "General")); } void LuaManager::init() diff --git a/docs/source/reference/modding/settings/general.rst b/docs/source/reference/modding/settings/general.rst index 39045e7733..a7a64e04b8 100644 --- a/docs/source/reference/modding/settings/general.rst +++ b/docs/source/reference/modding/settings/general.rst @@ -85,7 +85,7 @@ will match "en"), so is recommended that you include the country codes where pos since if the country code isn't specified the generic language-code only locale might refer to any of the country-specific variants. -This setting can only be configured by editing the settings configuration file. +Two highest priority locales may be assigned via the Localization tab of the in-game options. log buffer size --------------- diff --git a/files/data/l10n/SettingsMenu/en.yaml b/files/data/l10n/SettingsMenu/en.yaml index da3eeaceb7..d57377ceef 100644 --- a/files/data/l10n/SettingsMenu/en.yaml +++ b/files/data/l10n/SettingsMenu/en.yaml @@ -8,6 +8,8 @@ Controller: "Controller" FieldOfView: "Field of View" FrameRateHint: "Hint: press F3 to show\nthe current frame rate." InvertXAxis: "Invert X Axis" +Language: "Language" +LanguageNote: "Note: these settings do not affect strings from ESM files." LightingMethod: "Lighting Method" LightingMethodLegacy: "Legacy" LightingMethodShaders: "Shaders" @@ -27,6 +29,8 @@ MaxLightsTooltip: "Default: 8\nMaximum number of lights per object.\n\nA low num MouseAndKeyboard: "Mouse/Keyboard" PostProcessing: "Post Processing" PostProcessingTooltip: "Tweaked via Post Processor HUD, see input bindings." +PrimaryLanguage: "Primary Language" +PrimaryLanguageTooltip: "Localization files for this language have the highest priority." RainRippleDetail: "Rain ripple detail" RainRippleDetailDense: "Dense" RainRippleDetailSimple: "Simple" @@ -41,6 +45,8 @@ ReflectionShaderDetailWorld: "World" Refraction: "Refraction" Screenshot: "Screenshot" Scripts: "Scripts" +SecondaryLanguage: "Secondary Language" +SecondaryLanguageTooltip: "Localization files for this language may be used if the primary language files lack the necessary lines." TextureFiltering: "Texture Filtering" TextureFilteringBilinear: "Bilinear" TextureFilteringDisabled: "None" diff --git a/files/data/l10n/SettingsMenu/ru.yaml b/files/data/l10n/SettingsMenu/ru.yaml index 1662153f7c..5108702adf 100644 --- a/files/data/l10n/SettingsMenu/ru.yaml +++ b/files/data/l10n/SettingsMenu/ru.yaml @@ -8,6 +8,8 @@ Controller: "Геймпад" FieldOfView: "Поле зрения" FrameRateHint: "Подсказка: нажмите F3, чтобы показать\nтекущую частоту смены кадров." InvertXAxis: "Инвертировать ось X" +Language: "Язык" +LanguageNote: "Примечание: эти настройки не затрагивают строки из ESM-файлов." LightingMethod: "Способ освещения" LightingMethodLegacy: "Устаревший" LightingMethodShaders: "Шейдеры" @@ -27,6 +29,8 @@ MaxLightsTooltip: "Значение по умолчанию: 8\nМаксимал MouseAndKeyboard: "Мышь/Клавиатура" PostProcessing: "Постобработка" PostProcessingTooltip: "Настраивается через меню настроек постобработки, см. привязки клавиш." +PrimaryLanguage: "Основной язык" +PrimaryLanguageTooltip: "Язык, строки на котором будут использоваться в первую очередь." RainRippleDetail: "Капли дождя на воде" RainRippleDetailDense: "Плотные" RainRippleDetailSimple: "Упрощенные" @@ -41,6 +45,8 @@ ReflectionShaderDetailWorld: "Мир" Refraction: "Рефракция" Screenshot: "Снимок экрана" Scripts: "Скрипты" +SecondaryLanguage: "Дополнительный язык" +SecondaryLanguageTooltip: "Язык, строки на котором будут использоваться, если соответствующие строки на основном языке не найдены." TextureFiltering: "Фильтрация текстур" TextureFilteringBilinear: "Билинейная" TextureFilteringDisabled: "Отключена" diff --git a/files/data/mygui/openmw_settings_window.layout b/files/data/mygui/openmw_settings_window.layout index dfb291ea60..0d6d68d6c7 100644 --- a/files/data/mygui/openmw_settings_window.layout +++ b/files/data/mygui/openmw_settings_window.layout @@ -670,6 +670,28 @@ + + + + + + + + + + + + + + + + + + + + + + From cf905819950c811b6bf1270f3486623b876bcb2c Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 27 Jul 2022 09:51:14 +0400 Subject: [PATCH 3/5] Set ComboBox value prior to interactive MessageBox --- apps/openmw/mwgui/settingswindow.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 1a63f98a8f..4592847dd4 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -505,6 +505,8 @@ namespace MWGui if (pos == MyGUI::ITEM_NONE) return; + _sender->setCaptionWithReplacing(_sender->getItemNameAt(_sender->getIndexSelected())); + MWBase::Environment::get().getWindowManager()->interactiveMessageBox("#{SettingsMenu:ChangeRequiresRestart}", {"#{sOK}"}, true); const auto settingsNames = _sender->getUserData>(); @@ -517,6 +519,8 @@ namespace MWGui if (pos == MyGUI::ITEM_NONE) return; + _sender->setCaptionWithReplacing(_sender->getItemNameAt(_sender->getIndexSelected())); + MWBase::Environment::get().getWindowManager()->interactiveMessageBox("#{SettingsMenu:ChangeRequiresRestart}", {"#{sOK}"}, true); const auto languageNames = _sender->getUserData>(); From 4ac3c347ad62695700e2430c664eb7e6680e4d2b Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 27 Jul 2022 10:51:36 +0400 Subject: [PATCH 4/5] Add a None item for cases when there is no secondary localization --- apps/openmw/mwgui/settingswindow.cpp | 31 +++++++++++++++------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 4592847dd4..9d09d4662c 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -384,9 +384,13 @@ namespace MWGui icu::Locale primaryLocale(currentLocales[0].c_str()); mPrimaryLanguage->removeAllItems(); - mSecondaryLanguage->removeAllItems(); + mPrimaryLanguage->setIndexSelected(MyGUI::ITEM_NONE); - size_t i = 0, primaryLocaleIndex = MyGUI::ITEM_NONE, secondaryLocaleIndex = MyGUI::ITEM_NONE; + mSecondaryLanguage->removeAllItems(); + mSecondaryLanguage->addItem(MyGUI::LanguageManager::getInstance().replaceTags("#{sNone}"), std::string()); + mSecondaryLanguage->setIndexSelected(0); + + size_t i = 0; for (const auto& language : availableLanguages) { icu::Locale locale(language.c_str()); @@ -395,22 +399,17 @@ namespace MWGui locale.getDisplayName(primaryLocale, str); std::string localeString; str.toUTF8String(localeString); - mPrimaryLanguage->addItem(localeString); - mSecondaryLanguage->addItem(localeString); + + mPrimaryLanguage->addItem(localeString, language); + mSecondaryLanguage->addItem(localeString, language); if (language == currentLocales[0]) - primaryLocaleIndex = i; + mPrimaryLanguage->setIndexSelected(i); if (currentLocales.size() > 1 && language == currentLocales[1]) - secondaryLocaleIndex = i; + mSecondaryLanguage->setIndexSelected(i + 1); i++; } - - mPrimaryLanguage->setUserData(availableLanguages); - mSecondaryLanguage->setUserData(availableLanguages); - - mPrimaryLanguage->setIndexSelected(primaryLocaleIndex); - mSecondaryLanguage->setIndexSelected(secondaryLocaleIndex); } void SettingsWindow::onTabChanged(MyGUI::TabControl* /*_sender*/, size_t /*index*/) @@ -522,12 +521,16 @@ namespace MWGui _sender->setCaptionWithReplacing(_sender->getItemNameAt(_sender->getIndexSelected())); MWBase::Environment::get().getWindowManager()->interactiveMessageBox("#{SettingsMenu:ChangeRequiresRestart}", {"#{sOK}"}, true); - const auto languageNames = _sender->getUserData>(); std::vector currentLocales = Settings::Manager::getStringArray("preferred locales", "General"); if (currentLocales.size() <= langPriority) currentLocales.resize(langPriority + 1, "en"); - currentLocales[langPriority] = languageNames->at(pos); + + const auto& languageCode = *_sender->getItemDataAt(pos); + if (!languageCode.empty()) + currentLocales[langPriority] = *_sender->getItemDataAt(pos); + else + currentLocales.resize(1); Settings::Manager::setStringArray("preferred locales", "General", currentLocales); } From 351ce94f565d88794efc613b09a7f4be81b9c1b3 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Wed, 27 Jul 2022 13:03:24 +0400 Subject: [PATCH 5/5] Make ComboBox usage consistent --- apps/openmw/mwgui/settingswindow.cpp | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 9d09d4662c..eab0f572b0 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -508,8 +508,7 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->interactiveMessageBox("#{SettingsMenu:ChangeRequiresRestart}", {"#{sOK}"}, true); - const auto settingsNames = _sender->getUserData>(); - Settings::Manager::setString("lighting method", "Shaders", settingsNames->at(pos)); + Settings::Manager::setString("lighting method", "Shaders", *_sender->getItemDataAt(pos)); apply(); } @@ -528,7 +527,7 @@ namespace MWGui const auto& languageCode = *_sender->getItemDataAt(pos); if (!languageCode.empty()) - currentLocales[langPriority] = *_sender->getItemDataAt(pos); + currentLocales[langPriority] = languageCode; else currentLocales.resize(1); @@ -749,17 +748,13 @@ namespace MWGui SceneUtil::LightingMethod::SingleUBO, }; - std::vector userData; for (const auto& method : methods) { if (!MWBase::Environment::get().getResourceSystem()->getSceneManager()->isSupportedLightingMethod(method)) continue; - mLightingMethodButton->addItem(lightingMethodToStr(method)); - userData.emplace_back(SceneUtil::LightManager::getLightingMethodString(method)); + mLightingMethodButton->addItem(lightingMethodToStr(method), SceneUtil::LightManager::getLightingMethodString(method)); } - - mLightingMethodButton->setUserData(userData); mLightingMethodButton->setIndexSelected(mLightingMethodButton->findItemIndexWith(lightingMethodStr)); }