mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-07 11:46:30 +02:00
Settings: Settings search fixes (#41381)
<!-- Enter a brief description/summary of your PR here. What does it fix/what does it change/how was it tested (even manually, if necessary)? --> ## Summary of the Pull Request Fix 3 issues <!-- Please review the items on the PR checklist before submitting--> ## PR Checklist - [x] Closes: #41369, #41374, #41380 - [ ] **Communication:** I've discussed this with core contributors already. If the work hasn't been agreed, this work might be rejected - [ ] **Tests:** Added/updated and all pass - [ ] **Localization:** All end-user-facing strings can be localized - [ ] **Dev docs:** Added/updated - [ ] **New binaries:** Added on the required places - [ ] [JSON for signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json) for new binaries - [ ] [WXS for installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs) for new binaries and localization folder - [ ] [YML for CI pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml) for new test projects - [ ] [YML for signed pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml) - [ ] **Documentation updated:** If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys) and link it here: #xxx <!-- Provide a more detailed description of the PR, other things fixed, or any additional comments/features here --> ## Detailed Description of the Pull Request / Additional comments <!-- Describe how you validated the behavior. Add automated tests wherever possible, but list manual validation steps taken as well --> ## Validation Steps Performed https://github.com/user-attachments/assets/0e0df9fb-5aca-4b26-9d53-e6ddc49cab04 --------- Co-authored-by: Niels Laute <niels.laute@live.nl> Co-authored-by: Jiří Polášek <me@jiripolasek.com>
This commit is contained in:
@@ -19,6 +19,13 @@ namespace Microsoft.PowerToys.Tools.XamlIndexBuilder
|
||||
"ShellPage.xaml",
|
||||
};
|
||||
|
||||
// Hardcoded panel-to-page mapping (temporary until generic panel host mapping is needed)
|
||||
// Key: panel file base name (without .xaml), Value: owning page base name
|
||||
private static readonly Dictionary<string, string> PanelPageMapping = new(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
{ "MouseJumpPanel", "MouseUtilsPage" },
|
||||
};
|
||||
|
||||
private static JsonSerializerOptions serializeOption = new()
|
||||
{
|
||||
WriteIndented = true,
|
||||
@@ -33,32 +40,117 @@ namespace Microsoft.PowerToys.Tools.XamlIndexBuilder
|
||||
Environment.Exit(1);
|
||||
}
|
||||
|
||||
string xamlDirectory = args[0];
|
||||
string xamlRootDirectory = args[0];
|
||||
string outputFile = args[1];
|
||||
|
||||
if (!Directory.Exists(xamlDirectory))
|
||||
if (!Directory.Exists(xamlRootDirectory))
|
||||
{
|
||||
Debug.WriteLine($"Error: Directory '{xamlDirectory}' does not exist.");
|
||||
Debug.WriteLine($"Error: Directory '{xamlRootDirectory}' does not exist.");
|
||||
Environment.Exit(1);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var searchableElements = new List<SettingEntry>();
|
||||
var xamlFiles = Directory.GetFiles(xamlDirectory, "*.xaml", SearchOption.AllDirectories);
|
||||
var processedFiles = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
foreach (var xamlFile in xamlFiles)
|
||||
void ScanDirectory(string root)
|
||||
{
|
||||
var fileName = Path.GetFileName(xamlFile);
|
||||
if (ExcludedXamlFiles.Contains(fileName))
|
||||
if (!Directory.Exists(root))
|
||||
{
|
||||
// Skip ShellPage.xaml as it contains many elements not relevant for search
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
|
||||
Debug.WriteLine($"Processing: {fileName}");
|
||||
var elements = ExtractSearchableElements(xamlFile);
|
||||
searchableElements.AddRange(elements);
|
||||
Debug.WriteLine($"[XamlIndexBuilder] Scanning root: {root}");
|
||||
var xamlFilesLocal = Directory.GetFiles(root, "*.xaml", SearchOption.AllDirectories);
|
||||
foreach (var xamlFile in xamlFilesLocal)
|
||||
{
|
||||
var fullPath = Path.GetFullPath(xamlFile);
|
||||
if (processedFiles.Contains(fullPath))
|
||||
{
|
||||
continue; // already handled (can happen if overlapping directories)
|
||||
}
|
||||
|
||||
var fileName = Path.GetFileName(xamlFile);
|
||||
if (ExcludedXamlFiles.Contains(fileName))
|
||||
{
|
||||
continue; // explicitly excluded
|
||||
}
|
||||
|
||||
Debug.WriteLine($"Processing: {fileName}");
|
||||
var elements = ExtractSearchableElements(xamlFile);
|
||||
|
||||
// Apply hardcoded panel mapping override
|
||||
var baseName = Path.GetFileNameWithoutExtension(xamlFile);
|
||||
if (PanelPageMapping.TryGetValue(baseName, out var hostPage))
|
||||
{
|
||||
for (int i = 0; i < elements.Count; i++)
|
||||
{
|
||||
var entry = elements[i];
|
||||
entry.PageTypeName = hostPage;
|
||||
elements[i] = entry;
|
||||
}
|
||||
}
|
||||
|
||||
searchableElements.AddRange(elements);
|
||||
processedFiles.Add(fullPath);
|
||||
}
|
||||
}
|
||||
|
||||
// Scan well-known subdirectories under the provided root
|
||||
var subDirs = new[] { "Views", "Panels" };
|
||||
foreach (var sub in subDirs)
|
||||
{
|
||||
ScanDirectory(Path.Combine(xamlRootDirectory, sub));
|
||||
}
|
||||
|
||||
// Fallback: also scan root directly (in case some XAML lives at root level)
|
||||
ScanDirectory(xamlRootDirectory);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Explicit include section: add specific XAML files that we always want indexed
|
||||
// even if future logic excludes them or they live outside typical scan patterns.
|
||||
// Add future files to the ExplicitExtraXamlFiles array below.
|
||||
// -----------------------------------------------------------------------------
|
||||
string[] explicitExtraXamlFiles = new[]
|
||||
{
|
||||
"MouseJumpPanel.xaml", // Mouse Jump settings panel
|
||||
};
|
||||
|
||||
foreach (var extraFileName in explicitExtraXamlFiles)
|
||||
{
|
||||
try
|
||||
{
|
||||
var matches = Directory.GetFiles(xamlRootDirectory, extraFileName, SearchOption.AllDirectories);
|
||||
foreach (var match in matches)
|
||||
{
|
||||
var full = Path.GetFullPath(match);
|
||||
if (processedFiles.Contains(full))
|
||||
{
|
||||
continue; // already processed in general scan
|
||||
}
|
||||
|
||||
Debug.WriteLine($"Processing (explicit include): {extraFileName}");
|
||||
var elements = ExtractSearchableElements(full);
|
||||
var baseName = Path.GetFileNameWithoutExtension(full);
|
||||
if (PanelPageMapping.TryGetValue(baseName, out var hostPage))
|
||||
{
|
||||
for (int i = 0; i < elements.Count; i++)
|
||||
{
|
||||
var entry = elements[i];
|
||||
entry.PageTypeName = hostPage;
|
||||
elements[i] = entry;
|
||||
}
|
||||
}
|
||||
|
||||
searchableElements.AddRange(elements);
|
||||
processedFiles.Add(full);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"Explicit include failed for {extraFileName}: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
searchableElements = searchableElements.OrderBy(e => e.PageTypeName).ThenBy(e => e.ElementName).ToList();
|
||||
@@ -97,15 +189,15 @@ namespace Microsoft.PowerToys.Tools.XamlIndexBuilder
|
||||
.Where(e => e.Name.LocalName == "SettingsPageControl")
|
||||
.Where(e => e.Attribute(x + "Uid") != null);
|
||||
|
||||
// Extract SettingsCard elements
|
||||
// Extract SettingsCard elements (support both Name and x:Name)
|
||||
var settingsElements = doc.Descendants()
|
||||
.Where(e => e.Name.LocalName == "SettingsCard")
|
||||
.Where(e => e.Attribute("Name") != null || e.Attribute(x + "Uid") != null);
|
||||
.Where(e => e.Attribute("Name") != null || e.Attribute(x + "Name") != null || e.Attribute(x + "Uid") != null);
|
||||
|
||||
// Extract SettingsExpander elements
|
||||
// Extract SettingsExpander elements (support both Name and x:Name)
|
||||
var settingsExpanderElements = doc.Descendants()
|
||||
.Where(e => e.Name.LocalName == "SettingsExpander")
|
||||
.Where(e => e.Attribute("Name") != null || e.Attribute(x + "Uid") != null);
|
||||
.Where(e => e.Attribute("Name") != null || e.Attribute(x + "Name") != null || e.Attribute(x + "Uid") != null);
|
||||
|
||||
// Process SettingsPageControl elements
|
||||
foreach (var element in settingsPageElements)
|
||||
@@ -185,8 +277,12 @@ namespace Microsoft.PowerToys.Tools.XamlIndexBuilder
|
||||
|
||||
public static string GetElementName(XElement element, XNamespace x)
|
||||
{
|
||||
// Get Name attribute (we call it ElementName in our indexing system)
|
||||
var name = element.Attribute("Name")?.Value;
|
||||
if (string.IsNullOrEmpty(name))
|
||||
{
|
||||
name = element.Attribute(x + "Name")?.Value;
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
@@ -211,6 +307,11 @@ namespace Microsoft.PowerToys.Tools.XamlIndexBuilder
|
||||
if (expanderParent?.Name.LocalName == "SettingsExpander")
|
||||
{
|
||||
var expanderName = expanderParent.Attribute("Name")?.Value;
|
||||
if (string.IsNullOrEmpty(expanderName))
|
||||
{
|
||||
expanderName = expanderParent.Attribute(x + "Name")?.Value;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(expanderName))
|
||||
{
|
||||
return expanderName;
|
||||
@@ -221,6 +322,11 @@ namespace Microsoft.PowerToys.Tools.XamlIndexBuilder
|
||||
{
|
||||
// Direct child of SettingsExpander
|
||||
var expanderName = current.Attribute("Name")?.Value;
|
||||
if (string.IsNullOrEmpty(expanderName))
|
||||
{
|
||||
expanderName = current.Attribute(x + "Name")?.Value;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(expanderName))
|
||||
{
|
||||
return expanderName;
|
||||
|
||||
@@ -29,16 +29,15 @@
|
||||
<!-- Remove UI library reference to avoid pulling WindowsDesktop runtime (WindowsBase) -->
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- Fallback to dotnet if not provided by the environment -->
|
||||
<DotNetExe Condition="'$(DotNetExe)' == ''">dotnet</DotNetExe>
|
||||
<XamlViewsDir Condition="'$(XamlViewsDir)' == ''">$(MSBuildProjectDirectory)\..\Settings.UI\SettingsXAML\Views</XamlViewsDir>
|
||||
<XamlRootDir Condition="'$(XamlRootDir)' == ''">$(MSBuildProjectDirectory)\..\Settings.UI\SettingsXAML</XamlRootDir>
|
||||
<XamlRootDir Condition="'$(XamlViewsDir)' != ''">$([System.IO.Path]::GetDirectoryName('$(XamlViewsDir)'))</XamlRootDir>
|
||||
<GeneratedJsonFile Condition="'$(GeneratedJsonFile)' == ''">$(MSBuildProjectDirectory)\..\Settings.UI\Assets\Settings\search.index.json</GeneratedJsonFile>
|
||||
</PropertyGroup>
|
||||
<Target Name="GenerateSearchIndexSelf" AfterTargets="Build">
|
||||
<RemoveDir Directories="$(MSBuildProjectDirectory)\obj\ARM64;$(MSBuildProjectDirectory)\obj\x64;$(MSBuildProjectDirectory)\bin" />
|
||||
<MakeDir Directories="$([System.IO.Path]::GetDirectoryName('$(GeneratedJsonFile)'))" />
|
||||
<Message Importance="high" Text="[XamlIndexBuilder] Generating search index. Views='$(XamlViewsDir)'; Out='$(GeneratedJsonFile)'; Tool='$(TargetPath)'; DotNet='$(DotNetExe)'." />
|
||||
<!-- Execute via dotnet so host architecture doesn't need to match -->
|
||||
<Exec Command=""$(DotNetExe)" "$(TargetPath)" "$(XamlViewsDir)" "$(GeneratedJsonFile)"" />
|
||||
<Message Importance="high" Text="[XamlIndexBuilder] Generating search index. Root='$(XamlRootDir)'; Out='$(GeneratedJsonFile)'; Tool='$(TargetPath)'; DotNet='$(DotNetExe)'." />
|
||||
<Exec Command=""$(DotNetExe)" "$(TargetPath)" "$(XamlRootDir)" "$(GeneratedJsonFile)"" />
|
||||
</Target>
|
||||
</Project>
|
||||
|
||||
Reference in New Issue
Block a user