mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-02-24 04:00:02 +01:00
Add a command for quickly creating a new extension (#423)
This adds a new form page for quickly creating a new extension. * `ExtensionTemplate/` should be zipped up into `template.zip`. * fill in that form, then when you submit the form, we'll unzip the template. * You should be able to just open that solution up and just **go** * I moved the built-in commands lower in the list. The only visible commands it exposes are "Open CmdPal settings" and "Create new extension" (the others are hidden fallbacks, so it doesn't really matter) * To mitigate that the settings command is lower in the list, I added it to the "page title" spot in the command bar (only on the root view). Closes #311
This commit is contained in:
Binary file not shown.
@@ -3,14 +3,10 @@
|
||||
<packageSources>
|
||||
<clear />
|
||||
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
|
||||
<add key="SDKLocalSource" value=".\deps\Microsoft.CommandPalette.Extensions" />
|
||||
</packageSources>
|
||||
<packageSourceMapping>
|
||||
<packageSource key="nuget.org">
|
||||
<package pattern="*" />
|
||||
</packageSource>
|
||||
<packageSource key="SDKLocalSource">
|
||||
<package pattern="Microsoft.CommandPalette.Extensions" />
|
||||
</packageSource>
|
||||
</packageSourceMapping>
|
||||
</configuration>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// 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.
|
||||
|
||||
@@ -7,7 +7,7 @@ using System.Threading.Tasks;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.WinGet;
|
||||
namespace Microsoft.CmdPal.Common;
|
||||
|
||||
public partial class ExtensionHostInstance
|
||||
{
|
||||
Binary file not shown.
@@ -2,7 +2,6 @@
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Microsoft.CmdPal.UI.ViewModels.Commands;
|
||||
using Microsoft.CmdPal.UI.ViewModels.Messages;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
@@ -18,10 +17,12 @@ public partial class BuiltInsCommandProvider : CommandProvider
|
||||
private readonly QuitCommand quitCommand = new();
|
||||
private readonly FallbackReloadItem _fallbackReloadItem = new();
|
||||
private readonly FallbackLogItem _fallbackLogItem = new();
|
||||
private readonly NewExtensionPage _newExtension = new();
|
||||
|
||||
public override ICommandItem[] TopLevelCommands() =>
|
||||
[
|
||||
new CommandItem(openSettings) { Subtitle = "Open Command Palette settings" },
|
||||
new CommandItem(_newExtension) { Title = _newExtension.Title, Subtitle = "Creates a project for a new Command Palette extension" },
|
||||
];
|
||||
|
||||
public override IFallbackCommandItem[] FallbackCommands() =>
|
||||
@@ -37,4 +38,6 @@ public partial class BuiltInsCommandProvider : CommandProvider
|
||||
DisplayName = "Built-in commands";
|
||||
Icon = IconHelpers.FromRelativePath("Assets\\StoreLogo.scale-200.png");
|
||||
}
|
||||
|
||||
public override void InitializeWithHost(IExtensionHost host) => BuiltinsExtensionHost.Instance.Initialize(host);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
// 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.Diagnostics.CodeAnalysis;
|
||||
using System.IO.Compression;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using Microsoft.CmdPal.Common;
|
||||
using Microsoft.CmdPal.UI.ViewModels.Messages;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using Windows.Foundation;
|
||||
|
||||
namespace Microsoft.CmdPal.UI.ViewModels.BuiltinCommands;
|
||||
|
||||
public partial class BuiltinsExtensionHost
|
||||
{
|
||||
internal static ExtensionHostInstance Instance { get; } = new();
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
// 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.Diagnostics.CodeAnalysis;
|
||||
using System.IO.Compression;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using Microsoft.CmdPal.UI.ViewModels.Messages;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using Windows.Foundation;
|
||||
|
||||
namespace Microsoft.CmdPal.UI.ViewModels.BuiltinCommands;
|
||||
|
||||
internal sealed partial class CreatedExtensionForm : NewExtensionFormBase
|
||||
{
|
||||
public CreatedExtensionForm(string name, string displayName, string path)
|
||||
{
|
||||
TemplateJson = CardTemplate;
|
||||
DataJson = $$"""
|
||||
{
|
||||
"name": {{JsonSerializer.Serialize(name)}},
|
||||
"directory": {{JsonSerializer.Serialize(path)}},
|
||||
"displayName": {{JsonSerializer.Serialize(displayName)}}
|
||||
}
|
||||
""";
|
||||
_name = name;
|
||||
_displayName = displayName;
|
||||
_path = path;
|
||||
}
|
||||
|
||||
public override ICommandResult SubmitForm(string inputs, string data)
|
||||
{
|
||||
var dataInput = JsonNode.Parse(data)?.AsObject();
|
||||
if (dataInput == null)
|
||||
{
|
||||
return CommandResult.KeepOpen();
|
||||
}
|
||||
|
||||
var verb = dataInput["x"]?.AsValue()?.ToString() ?? string.Empty;
|
||||
return verb switch
|
||||
{
|
||||
"sln" => OpenSolution(),
|
||||
"dir" => OpenDirectory(),
|
||||
"new" => CreateNew(),
|
||||
_ => CommandResult.KeepOpen(),
|
||||
};
|
||||
}
|
||||
|
||||
private ICommandResult OpenSolution()
|
||||
{
|
||||
string[] parts = [_path, _name, $"{_name}.sln"];
|
||||
var pathToSolution = Path.Combine(parts);
|
||||
ShellHelpers.OpenInShell(pathToSolution);
|
||||
return CommandResult.GoHome();
|
||||
}
|
||||
|
||||
private ICommandResult OpenDirectory()
|
||||
{
|
||||
string[] parts = [_path, _name];
|
||||
var pathToDir = Path.Combine(parts);
|
||||
ShellHelpers.OpenInShell(pathToDir);
|
||||
return CommandResult.GoHome();
|
||||
}
|
||||
|
||||
private ICommandResult CreateNew()
|
||||
{
|
||||
RaiseFormSubmit(null);
|
||||
return CommandResult.KeepOpen();
|
||||
}
|
||||
|
||||
private static readonly string CardTemplate = """
|
||||
{
|
||||
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
|
||||
"type": "AdaptiveCard",
|
||||
"version": "1.6",
|
||||
"body": [
|
||||
{
|
||||
"type": "TextBlock",
|
||||
"text": "Successfully created your new extension!",
|
||||
"size": "large",
|
||||
"weight": "bolder",
|
||||
"style": "heading",
|
||||
"wrap": true
|
||||
},
|
||||
{
|
||||
"type": "TextBlock",
|
||||
"text": "Your new extension \"${displayName}\" was created in:",
|
||||
"wrap": true
|
||||
},
|
||||
{
|
||||
"type": "TextBlock",
|
||||
"text": "${directory}",
|
||||
"fontType": "monospace"
|
||||
},
|
||||
{
|
||||
"type": "TextBlock",
|
||||
"text": "Next steps",
|
||||
"style": "heading",
|
||||
"wrap": true
|
||||
},
|
||||
{
|
||||
"type": "TextBlock",
|
||||
"text": "Now that your extension project has been created, open the solution up in Visual Studio to start writing your extension code.",
|
||||
"wrap": true
|
||||
},
|
||||
{
|
||||
"type": "TextBlock",
|
||||
"text": "Navigate to `${name}Page.cs` to start adding items to the list, or to `${name}CommandsProvider.cs` to add new commands.",
|
||||
"wrap": true
|
||||
},
|
||||
{
|
||||
"type": "TextBlock",
|
||||
"text": "Once you're ready to test deploy the package locally with Visual Studio, then run the \"Reload\" command in the Command Palette to load your new extension.",
|
||||
"wrap": true
|
||||
}
|
||||
],
|
||||
"actions": [
|
||||
{
|
||||
"type": "Action.Submit",
|
||||
"title": "Open Solution",
|
||||
"data": {
|
||||
"x": "sln"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Action.Submit",
|
||||
"title": "Open directory",
|
||||
"data": {
|
||||
"x": "dir"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Action.Submit",
|
||||
"title": "Create another",
|
||||
"data": {
|
||||
"x": "new"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
""";
|
||||
|
||||
private readonly string _name;
|
||||
private readonly string _displayName;
|
||||
private readonly string _path;
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
// 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.IO.Compression;
|
||||
using System.Text.Json.Nodes;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
namespace Microsoft.CmdPal.UI.ViewModels.BuiltinCommands;
|
||||
|
||||
internal sealed partial class NewExtensionForm : NewExtensionFormBase
|
||||
{
|
||||
private static readonly string _creatingText = "Creating new extension...";
|
||||
private readonly StatusMessage _creatingMessage = new()
|
||||
{
|
||||
Message = _creatingText,
|
||||
Progress = new ProgressState() { IsIndeterminate = true },
|
||||
};
|
||||
|
||||
public NewExtensionForm()
|
||||
{
|
||||
TemplateJson = $$"""
|
||||
{
|
||||
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
|
||||
"type": "AdaptiveCard",
|
||||
"version": "1.6",
|
||||
"body": [
|
||||
{
|
||||
"type": "TextBlock",
|
||||
"text": "Create your new extension",
|
||||
"size": "large"
|
||||
},
|
||||
{
|
||||
"type": "TextBlock",
|
||||
"text": "Use this page to create a new extension project.",
|
||||
"wrap": true
|
||||
},
|
||||
{
|
||||
"type": "TextBlock",
|
||||
"text": "Extension name",
|
||||
"weight": "bolder",
|
||||
"size": "default"
|
||||
},
|
||||
{
|
||||
"type": "TextBlock",
|
||||
"text": "This is the name of your new extension project. It should be a valid C# class name. Best practice is to also include the word 'Extension' in the name.",
|
||||
"wrap": true
|
||||
},
|
||||
{
|
||||
"type": "Input.Text",
|
||||
"label": "Extension name",
|
||||
"isRequired": true,
|
||||
"errorMessage": "Extension name is required, without spaces",
|
||||
"id": "ExtensionName",
|
||||
"placeholder": "ExtensionName",
|
||||
"regex": "^[^\\s]+$"
|
||||
},
|
||||
{
|
||||
"type": "TextBlock",
|
||||
"text": "Display name",
|
||||
"weight": "bolder",
|
||||
"size": "default"
|
||||
},
|
||||
{
|
||||
"type": "TextBlock",
|
||||
"text": "The name of your extension as users will see it.",
|
||||
"wrap": true
|
||||
},
|
||||
{
|
||||
"type": "Input.Text",
|
||||
"label": "Display name",
|
||||
"isRequired": true,
|
||||
"errorMessage": "Display name is required",
|
||||
"id": "DisplayName",
|
||||
"placeholder": "My new extension"
|
||||
},
|
||||
{
|
||||
"type": "TextBlock",
|
||||
"text": "Output path",
|
||||
"weight": "bolder",
|
||||
"size": "default"
|
||||
},
|
||||
{
|
||||
"type": "TextBlock",
|
||||
"text": "Where should the new extension be created? This path will be created if it doesn't exist",
|
||||
"wrap": true
|
||||
},
|
||||
{
|
||||
"type": "Input.Text",
|
||||
"label": "Output path",
|
||||
"isRequired": true,
|
||||
"errorMessage": "Output path is required",
|
||||
"id": "OutputPath",
|
||||
"placeholder": "C:\\users\\me\\dev"
|
||||
}
|
||||
],
|
||||
"actions": [
|
||||
{
|
||||
"type": "Action.Submit",
|
||||
"title": "Create extension",
|
||||
"associatedInputs": "auto"
|
||||
}
|
||||
]
|
||||
}
|
||||
""";
|
||||
}
|
||||
|
||||
public override CommandResult SubmitForm(string payload)
|
||||
{
|
||||
var formInput = JsonNode.Parse(payload)?.AsObject();
|
||||
if (formInput == null)
|
||||
{
|
||||
return CommandResult.KeepOpen();
|
||||
}
|
||||
|
||||
var extensionName = formInput["ExtensionName"]?.AsValue()?.ToString() ?? string.Empty;
|
||||
var displayName = formInput["DisplayName"]?.AsValue()?.ToString() ?? string.Empty;
|
||||
var outputPath = formInput["OutputPath"]?.AsValue()?.ToString() ?? string.Empty;
|
||||
|
||||
_creatingMessage.State = MessageState.Info;
|
||||
_creatingMessage.Message = _creatingText;
|
||||
_creatingMessage.Progress = new ProgressState() { IsIndeterminate = true };
|
||||
BuiltinsExtensionHost.Instance.ShowStatus(_creatingMessage);
|
||||
|
||||
try
|
||||
{
|
||||
CreateExtension(extensionName, displayName, outputPath);
|
||||
|
||||
// _creatingMessage.Progress = null;
|
||||
// _creatingMessage.State = MessageState.Success;
|
||||
// _creatingMessage.Message = $"Successfully created extension";
|
||||
BuiltinsExtensionHost.Instance.HideStatus(_creatingMessage);
|
||||
|
||||
// BuiltinsExtensionHost.Instance.HideStatus(_creatingMessage);
|
||||
RaiseFormSubmit(new CreatedExtensionForm(extensionName, displayName, outputPath));
|
||||
|
||||
// _toast.Message.State = MessageState.Success;
|
||||
// _toast.Message.Message = $"Successfully created extension";
|
||||
// _toast.Show();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
BuiltinsExtensionHost.Instance.HideStatus(_creatingMessage);
|
||||
|
||||
_creatingMessage.State = MessageState.Error;
|
||||
_creatingMessage.Message = $"Error: {e.Message}";
|
||||
|
||||
// _toast.Show();
|
||||
}
|
||||
|
||||
// _ = Task.Run(() =>
|
||||
// {
|
||||
// Thread.Sleep(2500);
|
||||
// BuiltinsExtensionHost.Instance.HideStatus(_creatingMessage);
|
||||
// });
|
||||
return CommandResult.KeepOpen();
|
||||
}
|
||||
|
||||
private void CreateExtension(string extensionName, string newDisplayName, string outputPath)
|
||||
{
|
||||
var newGuid = Guid.NewGuid().ToString();
|
||||
|
||||
// Unzip `template.zip` to a temp dir:
|
||||
var tempDir = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
|
||||
|
||||
// Console.WriteLine($"Extracting to {tempDir}");
|
||||
|
||||
// Does the output path exist?
|
||||
if (!Directory.Exists(outputPath))
|
||||
{
|
||||
Directory.CreateDirectory(outputPath);
|
||||
}
|
||||
|
||||
var assetsPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory.ToString(), "Microsoft.CmdPal.UI.ViewModels\\Assets\\template.zip");
|
||||
ZipFile.ExtractToDirectory(assetsPath, tempDir);
|
||||
|
||||
var files = Directory.GetFiles(tempDir, "*", SearchOption.AllDirectories);
|
||||
foreach (var file in files)
|
||||
{
|
||||
var text = File.ReadAllText(file);
|
||||
|
||||
Console.WriteLine($" Processing {file}");
|
||||
|
||||
// Replace all the instances of `FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF` with a new random guid:
|
||||
text = text.Replace("FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF", newGuid);
|
||||
|
||||
// Then replace all the `TemplateCmdPalExtension` with `extensionName`
|
||||
text = text.Replace("TemplateCmdPalExtension", extensionName);
|
||||
|
||||
// Then replace all the `TemplateDisplayName` with `newDisplayName`
|
||||
text = text.Replace("TemplateDisplayName", newDisplayName);
|
||||
|
||||
// We're going to write the file to the same relative location in the output path
|
||||
var relativePath = Path.GetRelativePath(tempDir, file);
|
||||
|
||||
var newFileName = Path.Combine(outputPath, relativePath);
|
||||
|
||||
// if the file name had `TemplateCmdPalExtension` in it, replace it with `extensionName`
|
||||
newFileName = newFileName.Replace("TemplateCmdPalExtension", extensionName);
|
||||
|
||||
// Make sure the directory exists
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(newFileName)!);
|
||||
|
||||
File.WriteAllText(newFileName, text);
|
||||
|
||||
Console.WriteLine($" Wrote {newFileName}");
|
||||
|
||||
// Delete the old file
|
||||
File.Delete(file);
|
||||
}
|
||||
|
||||
// Delete the temp dir
|
||||
Directory.Delete(tempDir, true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// 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.Diagnostics.CodeAnalysis;
|
||||
using System.IO.Compression;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using Microsoft.CmdPal.UI.ViewModels.Messages;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using Windows.Foundation;
|
||||
|
||||
namespace Microsoft.CmdPal.UI.ViewModels.BuiltinCommands;
|
||||
|
||||
internal abstract partial class NewExtensionFormBase : FormContent
|
||||
{
|
||||
public event TypedEventHandler<NewExtensionFormBase, NewExtensionFormBase?>? FormSubmitted;
|
||||
|
||||
protected void RaiseFormSubmit(NewExtensionFormBase? next) => FormSubmitted?.Invoke(this, next);
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// 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 Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
namespace Microsoft.CmdPal.UI.ViewModels.BuiltinCommands;
|
||||
|
||||
public partial class NewExtensionPage : ContentPage
|
||||
{
|
||||
private NewExtensionForm _inputForm = new();
|
||||
private NewExtensionFormBase? _resultForm;
|
||||
|
||||
public override IContent[] GetContent()
|
||||
{
|
||||
return _resultForm != null ? [_resultForm] : [_inputForm];
|
||||
}
|
||||
|
||||
public NewExtensionPage()
|
||||
{
|
||||
Name = "Open";
|
||||
Title = "Create a new extension";
|
||||
Icon = new IconInfo("\uEA86"); // Puzzle
|
||||
|
||||
_inputForm.FormSubmitted += FormSubmitted;
|
||||
}
|
||||
|
||||
private void FormSubmitted(NewExtensionFormBase sender, NewExtensionFormBase? args)
|
||||
{
|
||||
if (_resultForm != null)
|
||||
{
|
||||
_resultForm.FormSubmitted -= FormSubmitted;
|
||||
}
|
||||
|
||||
_resultForm = args;
|
||||
if (_resultForm != null)
|
||||
{
|
||||
_resultForm.FormSubmitted += FormSubmitted;
|
||||
}
|
||||
else
|
||||
{
|
||||
_inputForm = new();
|
||||
_inputForm.FormSubmitted += FormSubmitted;
|
||||
}
|
||||
|
||||
RaiseItemsChanged(1);
|
||||
}
|
||||
}
|
||||
@@ -83,19 +83,22 @@ public partial class ContentFormViewModel(IFormContent _form, IPageContext conte
|
||||
var dataString = (action as AdaptiveSubmitAction)?.DataJson.Stringify() ?? string.Empty;
|
||||
var inputString = inputs.Stringify();
|
||||
|
||||
try
|
||||
_ = Task.Run(() =>
|
||||
{
|
||||
var model = _formModel.Unsafe!;
|
||||
if (model != null)
|
||||
try
|
||||
{
|
||||
var result = model.SubmitForm(inputString, dataString);
|
||||
WeakReferenceMessenger.Default.Send<HandleCommandResultMessage>(new(new(result)));
|
||||
var model = _formModel.Unsafe!;
|
||||
if (model != null)
|
||||
{
|
||||
var result = model.SubmitForm(inputString, dataString);
|
||||
WeakReferenceMessenger.Default.Send<HandleCommandResultMessage>(new(new(result)));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PageContext.ShowException(ex);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
PageContext.ShowException(ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,4 +34,8 @@
|
||||
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="Assets\template.zip" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -85,7 +85,6 @@ public partial class App : Application
|
||||
services.AddSingleton<ICommandProvider, ShellCommandsProvider>();
|
||||
services.AddSingleton<ICommandProvider, CalculatorCommandProvider>();
|
||||
services.AddSingleton<ICommandProvider, IndexerCommandsProvider>();
|
||||
services.AddSingleton<ICommandProvider, BuiltInsCommandProvider>();
|
||||
services.AddSingleton<ICommandProvider, BookmarksCommandProvider>();
|
||||
services.AddSingleton<ICommandProvider, WindowWalkerCommandsProvider>();
|
||||
services.AddSingleton<ICommandProvider, WebSearchCommandsProvider>();
|
||||
@@ -94,6 +93,7 @@ public partial class App : Application
|
||||
services.AddSingleton<ICommandProvider, WindowsSettingsCommandsProvider>();
|
||||
services.AddSingleton<ICommandProvider, RegistryCommandsProvider>();
|
||||
services.AddSingleton<ICommandProvider, WindowsServicesCommandsProvider>();
|
||||
services.AddSingleton<ICommandProvider, BuiltInsCommandProvider>();
|
||||
|
||||
// Models
|
||||
services.AddSingleton<TopLevelCommandManager>();
|
||||
|
||||
@@ -20,6 +20,10 @@
|
||||
x:Key="StringNotEmptyToVisibilityConverter"
|
||||
EmptyValue="Collapsed"
|
||||
NotEmptyValue="Visible" />
|
||||
<converters:BoolToVisibilityConverter
|
||||
x:Key="BoolToInvertedVisibilityConverter"
|
||||
FalseValue="Visible"
|
||||
TrueValue="Collapsed" />
|
||||
|
||||
<cmdpalUI:MessageStateToSeverityConverter x:Key="MessageStateToSeverityConverter" />
|
||||
|
||||
@@ -65,7 +69,8 @@
|
||||
<Grid
|
||||
x:Name="IconRoot"
|
||||
Margin="8,0,0,0"
|
||||
Tapped="PageIcon_Tapped">
|
||||
Tapped="PageIcon_Tapped"
|
||||
Visibility="{x:Bind CurrentPageViewModel.IsNested, Mode=OneWay}">
|
||||
|
||||
<cpcontrols:IconBox
|
||||
x:Name="IconBorder"
|
||||
@@ -105,12 +110,32 @@
|
||||
</Flyout>
|
||||
</Grid.ContextFlyout>
|
||||
</Grid>
|
||||
<Button
|
||||
x:Name="SettingsIconButton"
|
||||
Margin="0,0,0,0"
|
||||
Style="{StaticResource SubtleButtonStyle}"
|
||||
Tapped="SettingsIcon_Tapped"
|
||||
Visibility="{x:Bind CurrentPageViewModel.IsNested, Mode=OneWay, Converter={StaticResource BoolToInvertedVisibilityConverter}}">
|
||||
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<FontIcon
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="16"
|
||||
Glyph="" />
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="Settings" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="12"
|
||||
Text="{x:Bind CurrentPageViewModel.Title, Mode=OneWay}" />
|
||||
Text="{x:Bind CurrentPageViewModel.Title, Mode=OneWay}"
|
||||
Visibility="{x:Bind CurrentPageViewModel.IsNested, Mode=OneWay}" />
|
||||
<!-- TO DO: Replace with ItemsRepeater and bind "Primary commands"? -->
|
||||
<StackPanel
|
||||
Grid.Column="2"
|
||||
|
||||
@@ -113,4 +113,10 @@ public sealed partial class CommandBar : UserControl,
|
||||
showOptions: new FlyoutShowOptions() { ShowMode = FlyoutShowMode.Standard });
|
||||
}
|
||||
}
|
||||
|
||||
private void SettingsIcon_Tapped(object sender, TappedRoutedEventArgs e)
|
||||
{
|
||||
WeakReferenceMessenger.Default.Send<OpenSettingsMessage>();
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<UserControl
|
||||
x:Class="Microsoft.CmdPal.UI.Controls.ContentFormControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
@@ -13,11 +13,10 @@
|
||||
mc:Ignorable="d">
|
||||
|
||||
<UserControl.Resources>
|
||||
<ResourceDictionary>
|
||||
<converters:StringVisibilityConverter
|
||||
x:Key="StringNotEmptyToVisibilityConverter"
|
||||
EmptyValue="Collapsed"
|
||||
NotEmptyValue="Visible" />
|
||||
<ResourceDictionary x:Name="CardOverrideStyles">
|
||||
<Style x:Key="Adaptive.TextBlock" TargetType="TextBlock">
|
||||
<Setter Property="IsTextSelectionEnabled" Value="True" />
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
|
||||
|
||||
@@ -24,7 +24,13 @@ public sealed partial class ContentFormControl : UserControl
|
||||
|
||||
static ContentFormControl()
|
||||
{
|
||||
_renderer = new AdaptiveCardRenderer();
|
||||
// We can't use `CardOverrideStyles` here yet, because we haven't called InitializeComponent once.
|
||||
// But also, the default value isn't `null` here. It's... some other default empty value.
|
||||
// So clear it out so that we know when the first time we get created is
|
||||
_renderer = new AdaptiveCardRenderer()
|
||||
{
|
||||
OverrideStyles = null,
|
||||
};
|
||||
}
|
||||
|
||||
public ContentFormControl()
|
||||
@@ -33,6 +39,14 @@ public sealed partial class ContentFormControl : UserControl
|
||||
var lightTheme = ActualTheme == Microsoft.UI.Xaml.ElementTheme.Light;
|
||||
_renderer.HostConfig = lightTheme ? AdaptiveCardsConfig.Light : AdaptiveCardsConfig.Dark;
|
||||
|
||||
// 5% bodgy: if we set this multiple times over the lifetime of the app,
|
||||
// then the second call will explode, because "CardOverrideStyles is already the child of another element".
|
||||
// SO only set this once.
|
||||
if (_renderer.OverrideStyles == null)
|
||||
{
|
||||
_renderer.OverrideStyles = CardOverrideStyles;
|
||||
}
|
||||
|
||||
// TODO in the future, we should handle ActualThemeChanged and replace
|
||||
// our rendered card with one for that theme. But today is not that day
|
||||
}
|
||||
|
||||
@@ -166,6 +166,11 @@
|
||||
<Content Include="$(PkgAdaptiveCards_ObjectModel_WinUI3)\$(AdaptiveCardsNative)\AdaptiveCards.ObjectModel.WinUI3.dll" Link="AdaptiveCards.ObjectModel.WinUI3.dll" CopyToOutputDirectory="PreserveNewest" />
|
||||
<Content Include="$(PkgAdaptiveCards_Rendering_WinUI3)\$(AdaptiveCardsNative)\AdaptiveCards.Rendering.WinUI3.dll" Link="AdaptiveCards.Rendering.WinUI3.dll" CopyToOutputDirectory="PreserveNewest" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Update="..\Microsoft.CmdPal.UI.ViewModels\Assets\template.zip">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Styles\Settings.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
|
||||
@@ -27,7 +27,8 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\extensionsdk\Microsoft.CommandPalette.Extensions.Toolkit\Microsoft.CommandPalette.Extensions.Toolkit.csproj" />
|
||||
<ProjectReference Include="..\..\extensionsdk\Microsoft.CommandPalette.Extensions.Toolkit\Microsoft.CommandPalette.Extensions.Toolkit.csproj" />
|
||||
<ProjectReference Include="..\..\Microsoft.CmdPal.Common\Microsoft.CmdPal.Common.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<!--
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Microsoft.CmdPal.Common;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.WinGet;
|
||||
|
||||
public partial class WinGetExtensionHost
|
||||
|
||||
Reference in New Issue
Block a user