mirror of
https://github.com/microsoft/PowerToys.git
synced 2024-11-27 14:59:16 +08:00
Add infobars
This commit is contained in:
parent
d7683aa8c7
commit
dfb0b1810c
@ -2,8 +2,11 @@
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Metadata;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using Microsoft.CodeAnalysis;
|
||||
@ -14,19 +17,20 @@ namespace Settings.SourceGenerators
|
||||
public class SettingsSourceGenerator : ISourceGenerator
|
||||
{
|
||||
private GeneratorExecutionContext _context;
|
||||
private string[] _modules = new[] { "Hosts" };
|
||||
private string[] _modules = new[] { "Hosts", "FileLocksmith" };
|
||||
private StringBuilder _source;
|
||||
private string _projectPath;
|
||||
private bool _failed = false;
|
||||
|
||||
public void Execute(GeneratorExecutionContext context)
|
||||
{
|
||||
// Remove following comment for debugging
|
||||
/*#if DEBUG
|
||||
#if DEBUG
|
||||
if (!Debugger.IsAttached)
|
||||
{
|
||||
Debugger.Launch();
|
||||
}
|
||||
#endif*/
|
||||
#endif
|
||||
|
||||
_context = context;
|
||||
|
||||
@ -48,6 +52,11 @@ namespace Settings.Ui.VNext
|
||||
");
|
||||
|
||||
GeneratePopulateNavigationItemsFunction();
|
||||
if (_failed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GenerateSettingsPages();
|
||||
|
||||
_source.Append(@" }
|
||||
@ -65,18 +74,32 @@ namespace Settings.Ui.VNext
|
||||
{
|
||||
XmlDocument doc = new XmlDocument();
|
||||
doc.Load($"{_projectPath}/ConfigFiles/{item}.xml");
|
||||
string moduleName = doc.SelectSingleNode("ModuleSettings").Attributes["Name"].Value;
|
||||
string iconUri = doc.SelectSingleNode("ModuleSettings").Attributes["Icon"].Value;
|
||||
doc.Schemas.Add("http://schemas.microsoft.com/PowerToys/FileActionsMenu/ModuleDefinition", $"{_projectPath}/ConfigFiles/ModuleDefinition.xsd");
|
||||
doc.Validate((_, e) =>
|
||||
{
|
||||
_context.ReportDiagnostic(Diagnostic.Create(new DiagnosticDescriptor("PT0001", "XML Validation error", $"XML Validation error in {item}.xml: " + e.Message, "XML validation Error", DiagnosticSeverity.Error, true, "Error: " + e.Message), null));
|
||||
_failed = true;
|
||||
});
|
||||
|
||||
if (_failed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string moduleName = doc.GetNode("ModuleSettings").Attributes["Name"].Value;
|
||||
string iconUri = doc.GetNode("ModuleSettings").Attributes["Icon"].Value;
|
||||
|
||||
// Todo: Add NavHelper.NavigateTo property
|
||||
_source.Append(
|
||||
$@"
|
||||
NavigationViewItem navigationViewItem = new();
|
||||
var loader = ResourceLoaderInstance.ResourceLoader;
|
||||
navigationViewItem.Content = loader.GetString(""Shell_{moduleName}/Content"");
|
||||
navigationViewItem.Icon = new BitmapIcon() {{ UriSource = new Uri(""{iconUri}""), ShowAsMonochrome = false }};
|
||||
navigationView.MenuItems.Add(navigationViewItem);
|
||||
NavHelper.SetNavigateTo(navigationViewItem, typeof({moduleName}Page));
|
||||
{{
|
||||
NavigationViewItem navigationViewItem = new();
|
||||
var loader = ResourceLoaderInstance.ResourceLoader;
|
||||
navigationViewItem.Content = loader.GetString(""Shell_{moduleName}/Content"");
|
||||
navigationViewItem.Icon = new BitmapIcon() {{ UriSource = new Uri(""{iconUri}""), ShowAsMonochrome = false }};
|
||||
navigationView.MenuItems.Add(navigationViewItem);
|
||||
NavHelper.SetNavigateTo(navigationViewItem, typeof({moduleName}Page));
|
||||
}}
|
||||
");
|
||||
}
|
||||
}
|
||||
@ -90,14 +113,14 @@ namespace Settings.Ui.VNext
|
||||
XmlDocument doc = new XmlDocument();
|
||||
doc.Load($"{_projectPath}/ConfigFiles/{item}.xml");
|
||||
|
||||
string moduleName = doc.SelectSingleNode("ModuleSettings").Attributes["Name"].Value;
|
||||
string headerImage = doc.SelectSingleNode("ModuleSettings/Header").Attributes["Image"].Value;
|
||||
string headerPrimaryLinkName = doc.SelectSingleNode("ModuleSettings/Header/PrimaryLink").Attributes["Name"].Value;
|
||||
string headerPrimaryLinkUri = doc.SelectSingleNode("ModuleSettings/Header/PrimaryLink").Attributes["Uri"].Value;
|
||||
string moduleName = doc.GetNode("ModuleSettings").Attributes["Name"].Value;
|
||||
string headerImage = doc.GetNode("ModuleSettings/Header").Attributes["Image"].Value;
|
||||
string headerPrimaryLinkName = doc.GetNode("ModuleSettings/Header/PrimaryLink").Attributes["Name"].Value;
|
||||
string headerPrimaryLinkUri = doc.GetNode("ModuleSettings/Header/PrimaryLink").Attributes["Uri"].Value;
|
||||
|
||||
// Generate secondary links
|
||||
StringBuilder secondaryLinksSource = new StringBuilder();
|
||||
XmlNodeList secondaryLinks = doc.SelectNodes("ModuleSettings/Footer/SecondaryLink");
|
||||
XmlNodeList secondaryLinks = doc.GetNodes("ModuleSettings/Footer/SecondaryLink");
|
||||
|
||||
foreach (XmlNode link in secondaryLinks)
|
||||
{
|
||||
@ -114,8 +137,11 @@ namespace Settings.Ui.VNext
|
||||
");
|
||||
}
|
||||
|
||||
doc.SelectSingleNode("ModuleSettings/Header").RemoveAll();
|
||||
doc.SelectSingleNode("ModuleSettings/Footer").RemoveAll();
|
||||
doc.GetNode("ModuleSettings/Header").RemoveAll();
|
||||
doc.GetNode("ModuleSettings/Footer").RemoveAll();
|
||||
|
||||
StringBuilder content = new StringBuilder();
|
||||
GenerateSettingsPageElements(doc.GetNode("ModuleSettings").ChildNodes, content);
|
||||
|
||||
settingsPage.Append($@"// <auto-generated />
|
||||
using System;
|
||||
@ -123,6 +149,8 @@ using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Navigation;
|
||||
using Settings.Ui.VNext.Helpers;
|
||||
using Settings.Ui.VNext.Controls;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Settings.Ui.VNext
|
||||
{{
|
||||
@ -148,7 +176,15 @@ namespace Settings.Ui.VNext
|
||||
SecondaryLinks = new System.Collections.ObjectModel.ObservableCollection<Settings.Ui.VNext.Controls.PageLink>
|
||||
{{
|
||||
{secondaryLinksSource}
|
||||
}}
|
||||
}},
|
||||
ModuleContent =
|
||||
new StackPanel
|
||||
{{
|
||||
Children =
|
||||
{{
|
||||
{content}
|
||||
}}
|
||||
}},
|
||||
}};
|
||||
}}
|
||||
}}
|
||||
@ -184,6 +220,60 @@ namespace Settings.Ui.VNext
|
||||
#pragma warning restore RS1035 // Do not use APIs banned for analyzers
|
||||
}
|
||||
|
||||
public void GenerateSettingsPageElements(XmlNodeList elements, StringBuilder source)
|
||||
{
|
||||
foreach (XmlNode element in elements)
|
||||
{
|
||||
switch (element.Name)
|
||||
{
|
||||
case "Group":
|
||||
GenerateGroup(element, source);
|
||||
break;
|
||||
case "InfoBar":
|
||||
GenerateInfoBar(element, source);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void GenerateGroup(XmlNode element, StringBuilder source)
|
||||
{
|
||||
string name = element.Attributes["Name"].Value;
|
||||
|
||||
StringBuilder content = new StringBuilder();
|
||||
GenerateSettingsPageElements(element.ChildNodes, content);
|
||||
|
||||
source.Append(
|
||||
$@"
|
||||
new SettingsGroup
|
||||
{{
|
||||
Header = loader.GetString(""{name}/Header""),
|
||||
ItemsSource = new System.Collections.ObjectModel.ObservableCollection<UIElement>
|
||||
{{
|
||||
{content}
|
||||
}}
|
||||
}},
|
||||
");
|
||||
}
|
||||
|
||||
public void GenerateInfoBar(XmlNode element, StringBuilder source)
|
||||
{
|
||||
string severity = element.Attributes["Severity"].Value;
|
||||
string message = element.Attributes["Text"].Value;
|
||||
|
||||
source.Append(
|
||||
$@"
|
||||
new InfoBar
|
||||
{{
|
||||
Severity = InfoBarSeverity.{severity},
|
||||
Message = loader.GetString(""{message}/Title""),
|
||||
IsOpen = true,
|
||||
IsClosable = false,
|
||||
IsTabStop = true,
|
||||
}},
|
||||
");
|
||||
}
|
||||
|
||||
public void Initialize(GeneratorInitializationContext context)
|
||||
{
|
||||
}
|
||||
|
@ -0,0 +1,40 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Xml;
|
||||
|
||||
internal static class SettingsSourceGeneratorHelpers
|
||||
{
|
||||
public static XmlNode GetNode(this XmlDocument doc, string path)
|
||||
{
|
||||
XmlNamespaceManager namespaceManager = new XmlNamespaceManager(doc.NameTable);
|
||||
namespaceManager.AddNamespace("ns", "http://schemas.microsoft.com/PowerToys/FileActionsMenu/ModuleDefinition");
|
||||
|
||||
return doc.SelectSingleNode("ns:" + path.Replace("/", "/ns:"), namespaceManager);
|
||||
}
|
||||
|
||||
public static XmlNode GetNode(this XmlNode node, string path)
|
||||
{
|
||||
XmlNamespaceManager namespaceManager = new XmlNamespaceManager(node.OwnerDocument.NameTable);
|
||||
namespaceManager.AddNamespace("ns", "http://schemas.microsoft.com/PowerToys/FileActionsMenu/ModuleDefinition");
|
||||
|
||||
return node.SelectSingleNode("ns:" + path.Replace("/", "/ns:"), namespaceManager);
|
||||
}
|
||||
|
||||
public static XmlNodeList GetNodes(this XmlDocument doc, string path)
|
||||
{
|
||||
XmlNamespaceManager namespaceManager = new XmlNamespaceManager(doc.NameTable);
|
||||
namespaceManager.AddNamespace("ns", "http://schemas.microsoft.com/PowerToys/FileActionsMenu/ModuleDefinition");
|
||||
|
||||
return doc.SelectNodes("ns:" + path.Replace("/", "/ns:"), namespaceManager);
|
||||
}
|
||||
|
||||
public static XmlNodeList GetNodes(this XmlNode node, string path)
|
||||
{
|
||||
XmlNamespaceManager namespaceManager = new XmlNamespaceManager(node.OwnerDocument.NameTable);
|
||||
namespaceManager.AddNamespace("ns", "http://schemas.microsoft.com/PowerToys/FileActionsMenu/ModuleDefinition");
|
||||
|
||||
return node.SelectNodes("ns:" + path.Replace("/", "/ns:"), namespaceManager);
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<ModuleSettings Name="FileLocksmith" Icon="ms-appx:///Assets/Settings/Icons/FileLocksmith.png" Gpo="true" xmlns="http://schemas.microsoft.com/PowerToys/FileActionsMenu/ModuleDefinition">
|
||||
<Header Image="ms-appx:///Assets/Settings/Modules/FileLocksmith.png">
|
||||
<PrimaryLink Name="LearnMore_FileLocksmith" Uri="https://aka.ms/PowerToysOverview_FileLocksmith"/>
|
||||
</Header>
|
||||
<Footer>
|
||||
</Footer>
|
||||
<ActivationToggle></ActivationToggle>
|
||||
<Group Name="FileLocksmith_ShellIntegration">
|
||||
<ComboBox Name="FileLocksmith_Toggle_ContextMenu" SettingPropertyName="EnablesOnContextExtendedMenu">
|
||||
<Item Name="FileLocksmith_Toggle_StandardContextMenu" Value="False"/>
|
||||
<Item Name="FileLocksmith_Toggle_ExtendedContextMenu" Value="True"/>
|
||||
</ComboBox>
|
||||
<InfoBar Text="ExtendedContextMenuInfo" Severity="Informational"></InfoBar>
|
||||
</Group>
|
||||
</ModuleSettings>
|
@ -1,23 +1,84 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<xs:schema xmlns="http://schemas.microsoft.com/PowerToys/FileActionsMenu/ModuleDefinition" targetNamespace="http://schemas.microsoft.com/PowerToys/FileActionsMenu/ModuleDefinition" attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||
<xs:simpleType name="InfoBarSeverity">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="Informational" />
|
||||
<xs:enumeration value="Success" />
|
||||
<xs:enumeration value="Warning" />
|
||||
<xs:enumeration value="Error" />
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:group name="elements">
|
||||
<xs:choice>
|
||||
<xs:element name="InfoBar">
|
||||
<xs:complexType>
|
||||
<xs:attribute name="Text" type="xs:string" use="required"></xs:attribute>
|
||||
<xs:attribute name="Severity" type="InfoBarSeverity" use="required"></xs:attribute>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="ComboBox">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
An element that lets the user select a value from a list of options. One option has to be selected by default.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element maxOccurs="unbounded" name="Item">
|
||||
<xs:complexType>
|
||||
<xs:attribute name="Name" type="xs:string" use="required" />
|
||||
<xs:attribute name="Value" type="xs:string" use="required" />
|
||||
<xs:attribute name="Name" type="xs:string" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
The name of the option.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="Value" type="xs:string" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
The value of the option. Needs to be an enum value or true or false.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="Default" type="xs:boolean" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
If true, this option will be selected by default. Only one option can be selected by default.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="Name" type="xs:string" use="required" />
|
||||
<xs:attribute name="SettingPropertyName" type="xs:string" use="required" />
|
||||
<xs:attribute name="Icon" type="xs:string" use="optional" />
|
||||
<xs:attribute name="Name" type="xs:string" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
The name of the ComboBox.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="SettingPropertyName" type="xs:string" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
The name of the property in the settings object that will be updated when the user selects an option.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="Icon" type="xs:anyURI" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
The uri to the icon that will be displayed next to the ComboBox.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="Link">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
An element that represents a link displayed as a settings card.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexType>
|
||||
<xs:attribute name="Name" type="xs:string" use="required" />
|
||||
<xs:attribute name="ActionIcon" type="xs:string" use="required" />
|
||||
@ -25,6 +86,11 @@
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element maxOccurs="unbounded" name="Toggle">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
An element that represents a toggle displayed as a settings card.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexType>
|
||||
<xs:attribute name="Name" type="xs:string" use="required" />
|
||||
<xs:attribute name="Icon" type="xs:string" use="required" />
|
||||
@ -34,12 +100,21 @@
|
||||
<xs:element maxOccurs="unbounded" name="ActivationToggle">
|
||||
</xs:element>
|
||||
</xs:choice>
|
||||
|
||||
</xs:group>
|
||||
<xs:element name="ModuleSettings">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
Represents the settings for a module.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="Header" minOccurs="1" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
Contains the primary link to the documentation for the module.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="PrimaryLink" maxOccurs ="1">
|
||||
@ -53,6 +128,11 @@
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="Footer" minOccurs="1" maxOccurs="1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
Contains the secondary links to the documentation for the module displayed at the bottom of the settings page.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="SecondaryLink" minOccurs="0" maxOccurs="unbounded">
|
||||
@ -67,18 +147,47 @@
|
||||
<xs:choice maxOccurs="unbounded">
|
||||
<xs:group ref="elements" maxOccurs ="unbounded" />
|
||||
<xs:element maxOccurs="unbounded" name="Group">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
Represents a group of settings with a header.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:group ref="elements" maxOccurs="unbounded" />
|
||||
</xs:sequence>
|
||||
<xs:attribute name="Name" type="xs:string" use="required" />
|
||||
<xs:attribute name="Name" type="xs:string" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
The name of the group displayed as the header.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:choice>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="Name" type="xs:string" use="required" />
|
||||
<xs:attribute name="Icon" type="xs:anyURI" use="required" />
|
||||
<xs:attribute name="Gpo" type="xs:boolean" use="required" />
|
||||
<xs:attribute name="Name" type="xs:string" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
The name of the module.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="Icon" type="xs:anyURI" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
The uri to the icon that will be displayed in the header.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="Gpo" type="xs:boolean" use="required">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
If true, GPO related properties will be generated for this module.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:schema>
|
||||
|
@ -22,6 +22,7 @@
|
||||
<ProjectPriFileName>Settings.Ui.VNext.pri</ProjectPriFileName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
|
||||
</PropertyGroup>
|
||||
|
||||
|
@ -8,6 +8,7 @@ using ManagedCommon;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Automation.Peers;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Settings.Ui.VNext.Controls;
|
||||
using Settings.Ui.VNext.Helpers;
|
||||
using Settings.Ui.VNext.Services;
|
||||
using Settings.Ui.VNext.ViewModels;
|
||||
|
Loading…
Reference in New Issue
Block a user