2424#include < ..\WinRTUtils\inc\Utils.h>
2525#include " GeneratedSettingsIndex.g.h"
2626
27+ #include < winrt/Windows.ApplicationModel.Resources.Core.h>
2728#include < LibraryResources.h>
29+ #include < ScopedResourceLoader.h>
2830#include < dwmapi.h>
2931#include < fmt/compile.h>
3032
@@ -55,6 +57,15 @@ static const std::wstring_view addProfileTag{ L"AddProfile" };
5557static const std::wstring_view colorSchemesTag{ L" ColorSchemes_Nav" };
5658static const std::wstring_view globalAppearanceTag{ L" GlobalAppearance_Nav" };
5759
60+ // Like RS_, but uses an ambient context to determine whether
61+ // to load the English verion of a resource or the localized one.
62+ #define RS_switchable_ (x ) RS_switchable_impl(context, USES_RESOURCE(x))
63+
64+ static winrt::hstring RS_switchable_impl (const winrt::Windows::ApplicationModel::Resources::Core::ResourceContext& context, std::wstring_view key)
65+ {
66+ return GetLibraryResourceLoader ().ResourceMap ().GetValue (key, context).ValueAsString ();
67+ }
68+
5869namespace winrt ::Microsoft::Terminal::Settings::Editor::implementation
5970{
6071 static Editor::ProfileViewModel _viewModelForProfile (const Model::Profile& profile, const Model::CascadiaSettings& appSettings, const Windows::UI::Core::CoreDispatcher& dispatcher)
@@ -155,6 +166,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
155166
156167 _breadcrumbs = single_threaded_observable_vector<IInspectable>();
157168 _UpdateSearchIndex ();
169+ extensionsVMImpl->LazyLoadExtensions ();
158170 }
159171
160172 // Method Description:
@@ -1080,11 +1092,92 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
10801092 }
10811093 else
10821094 {
1095+ // Package filtered search index objects into WinRT FilteredSearchResults for UI
10831096 for (const auto * indexEntry : *_filteredSearchIndex)
10841097 {
1085- results.push_back (winrt::make<FilteredSearchResult>(*indexEntry));
1098+ results.push_back (winrt::make<FilteredSearchResult>(indexEntry));
1099+ }
1100+
1101+ // TODO CARLOS: use the macro below for runtime objects once everything is verified to be working right
1102+ #define APPEND_RUNTIME_OBJECT_RESULTS (runtimeObjectList, runtimeObjectIdentifier, filteredSearchIndex, navigationArgOverride ) \
1103+ for (const auto & runtimeObj : runtimeObjectList) \
1104+ { \
1105+ if (til::contains_linguistic_insensitive (runtimeObjectIdentifier, sanitizedQuery)) \
1106+ { \
1107+ /* results.push_back(winrt::make<FilteredSearchResult>(, profile));*/ \
1108+ } \
1109+ \
1110+ for (const auto * indexEntry : filteredSearchIndex) \
1111+ { \
1112+ results.push_back (winrt::make<FilteredSearchResult>(indexEntry, navigationArgOverride)); \
1113+ } \
1114+ }
1115+
1116+ // Profiles
1117+ // APPEND_RUNTIME_OBJECT_RESULTS(_profileVMs, runtimeObj.Name(), _filteredSearchProfileIndex, runtimeObj)
1118+ for (const auto & profile : _profileVMs)
1119+ {
1120+ // TODO CARLOS: replace with fuzzy search
1121+ if (til::contains_linguistic_insensitive (profile.Name (), sanitizedQuery))
1122+ {
1123+ // TODO CARLOS: if name matches, link the top-level page
1124+ // can't do that rn because I need a LocalizedIndexEntry stored somewhere for that
1125+ // results.push_back(winrt::make<FilteredSearchResult>(, profile));
1126+ }
1127+
1128+ for (const auto * indexEntry : _filteredSearchProfileIndex)
1129+ {
1130+ results.push_back (winrt::make<FilteredSearchResult>(indexEntry, profile));
1131+ }
1132+ }
1133+
1134+ // New Tab Menu (Folder View)
1135+ // APPEND_RUNTIME_OBJECT_RESULTS(get_self<implementation::NewTabMenuViewModel>(_newTabMenuPageVM)->FolderTreeFlatList(), runtimeObj.Name(), _filteredSearchNTMFolderIndex, runtimeObj)
1136+ for (const auto & ntmFolder : get_self<implementation::NewTabMenuViewModel>(_newTabMenuPageVM)->FolderTreeFlatList ())
1137+ {
1138+ if (til::contains_linguistic_insensitive (ntmFolder.Name (), sanitizedQuery))
1139+ {
1140+ // TODO CARLOS: if name matches, link the top-level page
1141+ // can't do that rn because I need a LocalizedIndexEntry stored somewhere for that
1142+ // results.push_back(winrt::make<FilteredSearchResult>(, ntmFolder));
1143+ }
1144+
1145+ for (const auto * indexEntry : _filteredSearchNTMFolderIndex)
1146+ {
1147+ results.push_back (winrt::make<FilteredSearchResult>(indexEntry, ntmFolder));
1148+ }
1149+ }
1150+
1151+ // Color schemes
1152+ // APPEND_RUNTIME_OBJECT_RESULTS(_colorSchemesPageVM.AllColorSchemes(), runtimeObj.Name(), _filteredSearchColorSchemeIndex, winrt::box_value(runtimeObj.Name()))
1153+ for (const auto & scheme : _colorSchemesPageVM.AllColorSchemes ())
1154+ {
1155+ if (til::contains_linguistic_insensitive (scheme.Name (), sanitizedQuery))
1156+ {
1157+ // TODO CARLOS: if name matches, link the top-level page
1158+ // can't do that rn because I need a LocalizedIndexEntry stored somewhere for that
1159+ // results.push_back(winrt::make<FilteredSearchResult>(, scheme));
1160+ }
1161+
1162+ for (const auto * indexEntry : _filteredSearchColorSchemeIndex)
1163+ {
1164+ results.push_back (winrt::make<FilteredSearchResult>(indexEntry, winrt::box_value (scheme.Name ())));
1165+ }
1166+ }
1167+
1168+ // TODO CARLOS:
1169+ // - if match with extension name, go to extension page
1170+ for (const auto & extension : _extensionsVM.ExtensionPackages ())
1171+ {
1172+ if (til::contains_linguistic_insensitive (extension.Package ().DisplayName (), sanitizedQuery))
1173+ {
1174+ // TODO CARLOS: if name matches, link the top-level page
1175+ // can't do that rn because I need a LocalizedIndexEntry stored somewhere for that
1176+ // results.push_back(winrt::make<FilteredSearchResult>(, ntmFolder));
1177+ }
10861178 }
10871179 }
1180+ #undef APPEND_RUNTIME_OBJECT_RESULTS
10881181
10891182 // Update the UI with the results
10901183 const auto & searchBox = SettingsSearchBox ();
@@ -1097,16 +1190,35 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
10971190 {
10981191 auto filteredResults{ _filteredSearchIndex.write () };
10991192
1100- filteredResults->clear ();
1101- for (const auto & entry : _searchIndex)
1102- {
1103- // TODO CARLOS: replace with fuzzy search
1104- const auto & displayText = entry.DisplayText ;
1105- if (til::contains_linguistic_insensitive (displayText, queryText))
1193+ auto findMatchingResults = [&queryText](const std::vector<LocalizedIndexEntry>& searchIndex, std::vector<const LocalizedIndexEntry*>& filteredIndex) {
1194+ filteredIndex.clear ();
1195+ for (const auto & entry : searchIndex)
11061196 {
1107- filteredResults->push_back (&entry);
1197+ // TODO CARLOS: replace with fuzzy search
1198+ // Check for a match with DisplayText (i.e. "Globals_DefaultProfile/Header") and HelpText (i.e. "Globals_DefaultProfile/HelpText")
1199+ // in language neutral and current language
1200+ if (til::contains_linguistic_insensitive (entry.Entry ->DisplayTextLocalized , queryText) ||
1201+ (entry.Entry ->HelpTextLocalized .has_value () && til::contains_linguistic_insensitive (entry.Entry ->HelpTextLocalized .value (), queryText)) ||
1202+ (entry.DisplayTextNeutral .has_value () && til::contains_linguistic_insensitive (entry.DisplayTextNeutral .value (), queryText)) ||
1203+ (entry.HelpTextNeutral .has_value () && til::contains_linguistic_insensitive (entry.HelpTextNeutral .value (), queryText)))
1204+ {
1205+ filteredIndex.push_back (&entry);
1206+ }
11081207 }
1109- }
1208+ };
1209+
1210+ // build-time search index can be filtered and returned pretty much as-is
1211+ findMatchingResults (_searchIndex, *filteredResults);
1212+
1213+ // Profiles
1214+ findMatchingResults (_searchProfileIndex, _filteredSearchProfileIndex);
1215+
1216+ // New Tab Menu (Folder View)
1217+ findMatchingResults (_searchNTMFolderIndex, _filteredSearchNTMFolderIndex);
1218+
1219+ // Color schemes
1220+ findMatchingResults (_searchColorSchemeIndex, _filteredSearchColorSchemeIndex);
1221+
11101222 return _filteredSearchIndex.generation ();
11111223 }
11121224
@@ -1123,22 +1235,24 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
11231235
11241236 // Navigate to the target page
11251237 const auto & indexEntry{ chosenResult->SearchIndexEntry () };
1126- const auto & navigationParam{ indexEntry.NavigationParam };
1127- if (navigationParam.try_as <hstring>())
1238+ const auto & navigationArg{ chosenResult->NavigationArg () };
1239+ const auto & subpage{ indexEntry.Entry ->SubPage };
1240+ const auto & elementToFocus{ indexEntry.Entry ->ElementName };
1241+ if (navigationArg.try_as <hstring>())
11281242 {
1129- _Navigate (navigationParam .as <hstring>(), indexEntry. SubPage , indexEntry. ElementName );
1243+ _Navigate (navigationArg .as <hstring>(), subpage, elementToFocus );
11301244 }
1131- else if (const auto profileVM = navigationParam .try_as <ProfileViewModel>())
1245+ else if (const auto profileVM = navigationArg .try_as <ProfileViewModel>())
11321246 {
1133- _Navigate (*profileVM, indexEntry. SubPage , indexEntry. ElementName );
1247+ _Navigate (*profileVM, subpage, elementToFocus );
11341248 }
1135- else if (const auto ntmEntryVM = navigationParam .try_as <NewTabMenuEntryViewModel>())
1249+ else if (const auto ntmEntryVM = navigationArg .try_as <NewTabMenuEntryViewModel>())
11361250 {
1137- _Navigate (*ntmEntryVM, indexEntry. SubPage , indexEntry. ElementName );
1251+ _Navigate (*ntmEntryVM, subpage, elementToFocus );
11381252 }
1139- else if (const auto extPkgVM = navigationParam .try_as <ExtensionPackageViewModel>())
1253+ else if (const auto extPkgVM = navigationArg .try_as <ExtensionPackageViewModel>())
11401254 {
1141- _Navigate (*extPkgVM, indexEntry. SubPage , indexEntry. ElementName );
1255+ _Navigate (*extPkgVM, subpage, elementToFocus );
11421256 }
11431257 }
11441258 }
@@ -1150,54 +1264,94 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
11501264 // AutoSuggestBox will pass the chosen item to QuerySubmitted() via args.ChosenSuggestion()
11511265 }
11521266
1153- void MainPage::_UpdateSearchIndex ()
1267+ safe_void_coroutine MainPage::_UpdateSearchIndex ()
11541268 {
1155- _searchIndex = {} ;
1269+ co_await winrt::resume_background () ;
11561270
1157- // TODO CARLOS: delay evaluating resources to here.
1158- // - we also want to allow searching in English regardless or the selected language
1159- // - we still want to only show the native language though
1160- // - we should update the index to use USES_RESOURCE instead of RS_, then evaluate resources here
1271+ // These are the new index entries we are building.
1272+ // Don't actually modify the members until we're completely done here.
1273+ std::vector<LocalizedIndexEntry> localizedIndex;
1274+ std::vector<LocalizedIndexEntry> localizedProfileIndex;
1275+ std::vector<LocalizedIndexEntry> localizedNTMFolderIndex;
1276+ std::vector<LocalizedIndexEntry> localizedColorSchemeIndex;
1277+
1278+ // TODO CARLOS: actually use this
1279+ // copied from CommandPaletteItems.h
1280+ static bool shouldIncludeLanguageNeutralResources = [] {
1281+ try
1282+ {
1283+ const auto context{ winrt::Windows::ApplicationModel::Resources::Core::ResourceContext::GetForViewIndependentUse () };
1284+ const auto qualifiers{ context.QualifierValues () };
1285+ if (const auto language{ qualifiers.TryLookup (L" language" ) })
1286+ {
1287+ return !til::starts_with_insensitive_ascii (*language, L" en-" );
1288+ }
1289+ }
1290+ catch (...)
1291+ {
1292+ LOG_CAUGHT_EXCEPTION ();
1293+ }
1294+ return false ;
1295+ }();
11611296
1162- const auto buildIndex = LoadBuildTimeIndex ();
1163- _searchIndex.reserve (buildIndex.size ());
1164- _searchIndex.insert (_searchIndex.end (), buildIndex.begin (), buildIndex.end ());
1297+ const auto & buildIndex = LoadBuildTimeIndex ();
1298+ localizedIndex.reserve (buildIndex.size ());
1299+ for (const auto & entry : buildIndex)
1300+ {
1301+ // TODO CARLOS: properly populate LocalizedIndexEntry
1302+ LocalizedIndexEntry localizedEntry;
1303+ localizedEntry.Entry = &entry;
1304+ localizedIndex.push_back (localizedEntry);
1305+ }
11651306
11661307 // Load profiles
1167- for (const auto & profile : _profileVMs)
1308+ const auto & profileIndex = LoadProfileIndex ();
1309+ localizedProfileIndex.reserve (profileIndex.size ());
1310+ for (const auto & entry : profileIndex)
11681311 {
1169- const auto & profileIndex = LoadProfileIndex (profile);
1170- _searchIndex.reserve (_searchIndex.size () + profileIndex.size ());
1171- _searchIndex.insert (_searchIndex.end (), profileIndex.begin (), profileIndex.end ());
1312+ // TODO CARLOS: properly populate LocalizedIndexEntry
1313+ LocalizedIndexEntry localizedEntry;
1314+ localizedEntry.Entry = &entry;
1315+ localizedProfileIndex.push_back (localizedEntry);
11721316 }
11731317
11741318 // Load new tab menu
1175- for (const auto & folderVM : get_self<implementation::NewTabMenuViewModel>(_newTabMenuPageVM)->FolderTreeFlatList ())
1319+ const auto & ntmFolderIndex = LoadNTMFolderIndex ();
1320+ localizedNTMFolderIndex.reserve (ntmFolderIndex.size ());
1321+ for (const auto & entry : ntmFolderIndex)
11761322 {
1177- const auto & folderIndex = LoadNTMFolderIndex (folderVM);
1178- _searchIndex.reserve (_searchIndex.size () + folderIndex.size ());
1179- _searchIndex.insert (_searchIndex.end (), folderIndex.begin (), folderIndex.end ());
1323+ // TODO CARLOS: properly populate LocalizedIndexEntry
1324+ LocalizedIndexEntry localizedEntry;
1325+ localizedEntry.Entry = &entry;
1326+ localizedNTMFolderIndex.push_back (localizedEntry);
11801327 }
11811328
1182- // Load extensions
1183- // TODO CARLOS: annoying that we have to load the extensions to build the index. Can we defer this until the user searches?
1184- get_self<ExtensionsViewModel>(_extensionsVM)->LazyLoadExtensions ();
1185- for (const auto & extPkg : _extensionsVM.ExtensionPackages ())
1186- {
1187- const auto & extPkgIndex = LoadExtensionIndex (extPkg);
1188- _searchIndex.reserve (_searchIndex.size () + extPkgIndex.size ());
1189- _searchIndex.insert (_searchIndex.end (), extPkgIndex.begin (), extPkgIndex.end ());
1190- }
1329+ // Nothing to load for extensions.
1330+ // At query time, we'll search for matching extension names.
11911331
11921332 // Load color schemes
1193- for (const auto & schemeVM : _colorSchemesPageVM.AllColorSchemes ())
1333+ const auto & colorSchemesIndex = LoadColorSchemeIndex ();
1334+ localizedColorSchemeIndex.reserve (colorSchemesIndex.size ());
1335+ for (const auto & entry : colorSchemesIndex)
11941336 {
1195- const auto & schemeIndex = LoadColorSchemeIndex (schemeVM.Name ());
1196- _searchIndex.reserve (_searchIndex.size () + schemeIndex.size ());
1197- _searchIndex.insert (_searchIndex.end (), schemeIndex.begin (), schemeIndex.end ());
1337+ // TODO CARLOS: properly populate LocalizedIndexEntry
1338+ LocalizedIndexEntry localizedEntry;
1339+ localizedEntry.Entry = &entry;
1340+ localizedColorSchemeIndex.push_back (localizedEntry);
11981341 }
11991342
12001343 // Load actions
12011344 // TODO CARLOS: postpone until actions page is updated
1345+
1346+ _searchIndex = std::move (localizedIndex);
1347+ _searchProfileIndex = std::move (localizedProfileIndex);
1348+ _searchNTMFolderIndex = std::move (localizedNTMFolderIndex);
1349+ _searchColorSchemeIndex = std::move (localizedColorSchemeIndex);
1350+ }
1351+
1352+ const ScopedResourceLoader& EnglishOnlyResourceLoader () noexcept
1353+ {
1354+ static ScopedResourceLoader loader{ GetLibraryResourceLoader ().WithQualifier (L" language" , L" en-US" ) };
1355+ return loader;
12021356 }
12031357}
0 commit comments