// Copyright (c) Microsoft Corporation. // Licensed under the MIT license. #include "pch.h" #include "EnumEntry.h" #include "GlobalAppearance.h" #include "GlobalAppearance.g.cpp" #include "GlobalAppearancePageNavigationState.g.cpp" #include using namespace winrt; using namespace winrt::Windows::UI::Xaml; using namespace winrt::Windows::UI::Xaml::Navigation; using namespace winrt::Windows::UI::Xaml::Controls; using namespace winrt::Microsoft::Terminal::Settings::Model; using namespace winrt::Windows::Foundation::Collections; namespace winrt::Microsoft::Terminal::Settings::Editor::implementation { // For ComboBox an empty SelectedItem string denotes no selection. // What we want instead is for "Use system language" to be selected by default. // --> "und" is synonymous for "Use system language". constexpr std::wstring_view systemLanguageTag{ L"und" }; GlobalAppearance::GlobalAppearance() { InitializeComponent(); INITIALIZE_BINDABLE_ENUM_SETTING(Theme, ElementTheme, winrt::Windows::UI::Xaml::ElementTheme, L"Globals_Theme", L"Content"); INITIALIZE_BINDABLE_ENUM_SETTING(TabWidthMode, TabViewWidthMode, winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode, L"Globals_TabWidthMode", L"Content"); } void GlobalAppearance::OnNavigatedTo(const NavigationEventArgs& e) { _State = e.Parameter().as(); } winrt::hstring GlobalAppearance::LanguageDisplayConverter(const winrt::hstring& tag) { if (tag == systemLanguageTag) { return RS_(L"Globals_LanguageDefault"); } winrt::Windows::Globalization::Language language{ tag }; return language.NativeName(); } // Returns the list of languages the user may override the application language with. // The returned list are BCP 47 language tags like {"und", "en-US", "de-DE", "es-ES", ...}. // "und" is short for "undefined" and is synonymous for "Use system language" in this code. winrt::Windows::Foundation::Collections::IObservableVector GlobalAppearance::LanguageList() { if (_languageList) { return _languageList; } // In order to return the language list this code does the following: // [1] Get all possible languages we want to allow the user to choose. // We have to acquire languages from multiple sources, creating duplicates. See below at [1]. // [2] Sort languages by their ASCII tags, forcing the UI in a consistent/stable order. // I wanted to sort the localized language names initially, but it turned out to be complex. // [3] Remove potential duplicates in our language list from [1]. // We don't want to have en-US twice in the list, do we? // [4] Optionally remove unwanted language tags (like pseudo-localizations). std::vector tags; // [1]: { // ManifestLanguages contains languages the app ships with. // // Languages is a computed list that merges the ManifestLanguages with the // user's ranked list of preferred languages taken from the system settings. // As is tradition the API documentation is incomplete though, as it can also // contain regional language variants. If our app supports en-US, but the user // has en-GB or en-DE in their system's preferred language list, Languages will // contain those as well, as they're variants from a supported language. We should // allow a user to select those, as regional formattings can vary significantly. const std::array tagSources{ winrt::Windows::Globalization::ApplicationLanguages::ManifestLanguages(), winrt::Windows::Globalization::ApplicationLanguages::Languages() }; // tags will hold all the flattened results from tagSources. // We resize() the vector to the proper size in order to efficiently GetMany() all items. tags.resize(std::accumulate( tagSources.begin(), tagSources.end(), // tags[0] will be "und" - the "Use system language" item // tags[1..n] will contain tags from tagSources. // --> totalTags is offset by 1 1, [](uint32_t sum, const auto& v) -> uint32_t { return sum + v.Size(); })); // As per the function definition, the first item // is always "Use system language" ("und"). auto data = tags.data(); *data++ = systemLanguageTag; // Finally GetMany() all the tags from tagSources. for (const auto& v : tagSources) { const auto size = v.Size(); v.GetMany(0, winrt::array_view(data, size)); data += size; } } // NOTE: The size of tags is always >0, due to tags[0] being hardcoded to "und". const auto tagsBegin = ++tags.begin(); const auto tagsEnd = tags.end(); // [2]: std::sort(tagsBegin, tagsEnd); // I'd love for both, std::unique and std::remove_if, to occur in a single loop, // but the code turned out to be complex and even less maintainable, so I gave up. { // [3] part 1: auto it = std::unique(tagsBegin, tagsEnd); // The qps- languages are useful for testing ("pseudo-localization"). // --> Leave them in if debug features are enabled. if (!_State.Globals().DebugFeaturesEnabled()) { // [4] part 1: it = std::remove_if(tagsBegin, it, [](const winrt::hstring& tag) -> bool { return til::starts_with(tag, L"qps-"); }); } // [3], [4] part 2 (completing the so called "erase-remove idiom"): tags.erase(it, tagsEnd); } _languageList = winrt::single_threaded_observable_vector(std::move(tags)); return _languageList; } winrt::Windows::Foundation::IInspectable GlobalAppearance::CurrentLanguage() { if (_currentLanguage.empty()) { _currentLanguage = winrt::Windows::Globalization::ApplicationLanguages::PrimaryLanguageOverride(); if (_currentLanguage.empty()) { _currentLanguage = systemLanguageTag; } } return winrt::box_value(_currentLanguage); } void GlobalAppearance::CurrentLanguage(const winrt::Windows::Foundation::IInspectable& tag) { _currentLanguage = winrt::unbox_value(tag); const auto globals = _State.Globals(); if (_currentLanguage == systemLanguageTag) { globals.ClearLanguage(); } else { globals.Language(_currentLanguage); } } }