mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-15 19:27:56 +01:00
Continuing part deux
This commit is contained in:
@@ -69,7 +69,7 @@
|
||||
This is present due to a bug in CsWinRT where WPF projects cause the analyzer to fail.
|
||||
-->
|
||||
<PackageVersion Include="Microsoft.Windows.CsWinRT" Version="2.2.0" />
|
||||
<PackageVersion Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.6901" />
|
||||
<PackageVersion Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.7175" />
|
||||
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="1.8.250907003" />
|
||||
<PackageVersion Include="Microsoft.WindowsAppSDK.AI" Version="1.8.37" />
|
||||
<PackageVersion Include="Microsoft.WindowsAppSDK.Runtime" Version="1.8.250907003" />
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 18
|
||||
VisualStudioVersion = 18.0.11222.15 d18.0
|
||||
VisualStudioVersion = 18.0.11222.15
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "runner", "src\runner\runner.vcxproj", "{9412D5C6-2CF2-4FC2-A601-B55508EA9B27}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
@@ -844,6 +844,16 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UI", "UI", "{4F881A97-423A-
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.CommandPalette.UI", "src\modules\Deux\UI\Microsoft.CommandPalette.UI\Microsoft.CommandPalette.UI.csproj", "{9EBE6DE4-58CD-CA14-7A21-B1E9DED261DB}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.CommandPalette.UI.ViewModels", "src\modules\Deux\UI\Microsoft.CommandPalette.UI.ViewModels\Microsoft.CommandPalette.UI.ViewModels.csproj", "{9D1D4795-6E0A-44E7-9C91-61CA07421F2E}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SDK", "SDK", "{ED6F2337-189A-4E98-A481-316347D319BF}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.CommandPalette.Extensions", "src\modules\Deux\SDK\Microsoft.CommandPalette.Extensions\Microsoft.CommandPalette.Extensions.vcxproj", "{7997DAD4-31D6-496B-95DB-6C028D699370}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.CommandPalette.Extensions.Toolkit", "src\modules\Deux\SDK\Microsoft.CommandPalette.Extensions.Toolkit\Microsoft.CommandPalette.Extensions.Toolkit.csproj", "{7FA183E0-ADB2-8F18-0E6B-A724BB0D1675}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.CommandPalette.UI.Models", "src\modules\Deux\UI\Microsoft.CommandPalette.UI.Models\Microsoft.CommandPalette.UI.Models.csproj", "{D985116D-16D6-9BE7-0371-9E7EAA2FF2B5}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|ARM64 = Debug|ARM64
|
||||
@@ -3074,6 +3084,38 @@ Global
|
||||
{9EBE6DE4-58CD-CA14-7A21-B1E9DED261DB}.Release|x64.ActiveCfg = Release|x64
|
||||
{9EBE6DE4-58CD-CA14-7A21-B1E9DED261DB}.Release|x64.Build.0 = Release|x64
|
||||
{9EBE6DE4-58CD-CA14-7A21-B1E9DED261DB}.Release|x64.Deploy.0 = Release|x64
|
||||
{9D1D4795-6E0A-44E7-9C91-61CA07421F2E}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{9D1D4795-6E0A-44E7-9C91-61CA07421F2E}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{9D1D4795-6E0A-44E7-9C91-61CA07421F2E}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{9D1D4795-6E0A-44E7-9C91-61CA07421F2E}.Debug|x64.Build.0 = Debug|x64
|
||||
{9D1D4795-6E0A-44E7-9C91-61CA07421F2E}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{9D1D4795-6E0A-44E7-9C91-61CA07421F2E}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{9D1D4795-6E0A-44E7-9C91-61CA07421F2E}.Release|x64.ActiveCfg = Release|x64
|
||||
{9D1D4795-6E0A-44E7-9C91-61CA07421F2E}.Release|x64.Build.0 = Release|x64
|
||||
{7997DAD4-31D6-496B-95DB-6C028D699370}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{7997DAD4-31D6-496B-95DB-6C028D699370}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{7997DAD4-31D6-496B-95DB-6C028D699370}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{7997DAD4-31D6-496B-95DB-6C028D699370}.Debug|x64.Build.0 = Debug|x64
|
||||
{7997DAD4-31D6-496B-95DB-6C028D699370}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{7997DAD4-31D6-496B-95DB-6C028D699370}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{7997DAD4-31D6-496B-95DB-6C028D699370}.Release|x64.ActiveCfg = Release|x64
|
||||
{7997DAD4-31D6-496B-95DB-6C028D699370}.Release|x64.Build.0 = Release|x64
|
||||
{7FA183E0-ADB2-8F18-0E6B-A724BB0D1675}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{7FA183E0-ADB2-8F18-0E6B-A724BB0D1675}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{7FA183E0-ADB2-8F18-0E6B-A724BB0D1675}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{7FA183E0-ADB2-8F18-0E6B-A724BB0D1675}.Debug|x64.Build.0 = Debug|x64
|
||||
{7FA183E0-ADB2-8F18-0E6B-A724BB0D1675}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{7FA183E0-ADB2-8F18-0E6B-A724BB0D1675}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{7FA183E0-ADB2-8F18-0E6B-A724BB0D1675}.Release|x64.ActiveCfg = Release|x64
|
||||
{7FA183E0-ADB2-8F18-0E6B-A724BB0D1675}.Release|x64.Build.0 = Release|x64
|
||||
{D985116D-16D6-9BE7-0371-9E7EAA2FF2B5}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{D985116D-16D6-9BE7-0371-9E7EAA2FF2B5}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{D985116D-16D6-9BE7-0371-9E7EAA2FF2B5}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{D985116D-16D6-9BE7-0371-9E7EAA2FF2B5}.Debug|x64.Build.0 = Debug|x64
|
||||
{D985116D-16D6-9BE7-0371-9E7EAA2FF2B5}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{D985116D-16D6-9BE7-0371-9E7EAA2FF2B5}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{D985116D-16D6-9BE7-0371-9E7EAA2FF2B5}.Release|x64.ActiveCfg = Release|x64
|
||||
{D985116D-16D6-9BE7-0371-9E7EAA2FF2B5}.Release|x64.Build.0 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -3410,6 +3452,11 @@ Global
|
||||
{023C058E-537A-4AB6-900A-36437EC17410} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
|
||||
{4F881A97-423A-4905-B219-677AD0184059} = {023C058E-537A-4AB6-900A-36437EC17410}
|
||||
{9EBE6DE4-58CD-CA14-7A21-B1E9DED261DB} = {4F881A97-423A-4905-B219-677AD0184059}
|
||||
{9D1D4795-6E0A-44E7-9C91-61CA07421F2E} = {4F881A97-423A-4905-B219-677AD0184059}
|
||||
{ED6F2337-189A-4E98-A481-316347D319BF} = {023C058E-537A-4AB6-900A-36437EC17410}
|
||||
{7997DAD4-31D6-496B-95DB-6C028D699370} = {ED6F2337-189A-4E98-A481-316347D319BF}
|
||||
{7FA183E0-ADB2-8F18-0E6B-A724BB0D1675} = {ED6F2337-189A-4E98-A481-316347D319BF}
|
||||
{D985116D-16D6-9BE7-0371-9E7EAA2FF2B5} = {4F881A97-423A-4905-B219-677AD0184059}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public sealed partial class AnonymousCommand : InvokableCommand
|
||||
{
|
||||
private readonly Action? _action;
|
||||
|
||||
public ICommandResult Result { get; set; } = CommandResult.Dismiss();
|
||||
|
||||
public AnonymousCommand(Action? action)
|
||||
{
|
||||
Name = Properties.Resources.AnonymousCommand_Invoke;
|
||||
_action = action;
|
||||
}
|
||||
|
||||
public override ICommandResult Invoke()
|
||||
{
|
||||
if (_action is not null)
|
||||
{
|
||||
_action();
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// 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 Windows.Foundation;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
// TODO! We probably want to have OnPropertyChanged raise the event
|
||||
// asynchronously, so as to not block the extension app while it's being
|
||||
// processed in the host app.
|
||||
// (also consider this for ItemsChanged in ListPage)
|
||||
public partial class BaseObservable : INotifyPropChanged
|
||||
{
|
||||
public event TypedEventHandler<object, IPropChangedEventArgs>? PropChanged;
|
||||
|
||||
protected void OnPropertyChanged(string propertyName)
|
||||
{
|
||||
try
|
||||
{
|
||||
// TODO #181 - This is dangerous! If the original host goes away,
|
||||
// this can crash as we try to invoke the handlers from that process.
|
||||
// However, just catching it seems to still raise the event on the
|
||||
// new host?
|
||||
PropChanged?.Invoke(this, new PropChangedEventArgs(propertyName));
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
// 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.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public sealed class ChoiceSetSetting : Setting<string>
|
||||
{
|
||||
public partial class Choice
|
||||
{
|
||||
[JsonPropertyName("value")]
|
||||
public string Value { get; set; }
|
||||
|
||||
[JsonPropertyName("title")]
|
||||
public string Title { get; set; }
|
||||
|
||||
public Choice(string title, string value)
|
||||
{
|
||||
Value = value;
|
||||
Title = title;
|
||||
}
|
||||
}
|
||||
|
||||
public List<Choice> Choices { get; set; }
|
||||
|
||||
private ChoiceSetSetting()
|
||||
: base()
|
||||
{
|
||||
Choices = [];
|
||||
}
|
||||
|
||||
public ChoiceSetSetting(string key, List<Choice> choices)
|
||||
: base(key, choices.ElementAt(0).Value)
|
||||
{
|
||||
Choices = choices;
|
||||
}
|
||||
|
||||
public ChoiceSetSetting(string key, string label, string description, List<Choice> choices)
|
||||
: base(key, label, description, choices.ElementAt(0).Value)
|
||||
{
|
||||
Choices = choices;
|
||||
}
|
||||
|
||||
public override Dictionary<string, object> ToDictionary()
|
||||
{
|
||||
return new Dictionary<string, object>
|
||||
{
|
||||
{ "type", "Input.ChoiceSet" },
|
||||
{ "title", Label },
|
||||
{ "id", Key },
|
||||
{ "label", Description },
|
||||
{ "choices", Choices },
|
||||
{ "value", Value ?? string.Empty },
|
||||
{ "isRequired", IsRequired },
|
||||
{ "errorMessage", ErrorMessage },
|
||||
};
|
||||
}
|
||||
|
||||
public static ChoiceSetSetting LoadFromJson(JsonObject jsonObject) => new() { Value = jsonObject["value"]?.GetValue<string>() ?? string.Empty };
|
||||
|
||||
public override void Update(JsonObject payload)
|
||||
{
|
||||
// If the key doesn't exist in the payload, don't do anything
|
||||
if (payload[Key] is not null)
|
||||
{
|
||||
Value = payload[Key]?.GetValue<string>();
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToState() => $"\"{Key}\": {JsonSerializer.Serialize(Value, JsonSerializationContext.Default.String)}";
|
||||
}
|
||||
@@ -0,0 +1,298 @@
|
||||
// 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.Runtime.InteropServices;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
// shamelessly from https://github.com/PowerShell/PowerShell/blob/master/src/Microsoft.PowerShell.Commands.Management/commands/management/Clipboard.cs
|
||||
public static partial class ClipboardHelper
|
||||
{
|
||||
private static readonly bool? _clipboardSupported = true;
|
||||
|
||||
// Used if an external clipboard is not available, e.g. if xclip is missing.
|
||||
// This is useful for testing in CI as well.
|
||||
private static string? _internalClipboard;
|
||||
|
||||
public static string GetText()
|
||||
{
|
||||
if (_clipboardSupported == false)
|
||||
{
|
||||
return _internalClipboard ?? string.Empty;
|
||||
}
|
||||
|
||||
var tool = string.Empty;
|
||||
var args = string.Empty;
|
||||
var clipboardText = string.Empty;
|
||||
|
||||
ExecuteOnStaThread(() => GetTextImpl(out clipboardText));
|
||||
return clipboardText;
|
||||
}
|
||||
|
||||
public static void SetText(string text)
|
||||
{
|
||||
if (_clipboardSupported == false)
|
||||
{
|
||||
_internalClipboard = text;
|
||||
return;
|
||||
}
|
||||
|
||||
var tool = string.Empty;
|
||||
var args = string.Empty;
|
||||
ExecuteOnStaThread(() => SetClipboardData(Tuple.Create(text, CF_UNICODETEXT)));
|
||||
return;
|
||||
}
|
||||
|
||||
public static void SetRtf(string plainText, string rtfText)
|
||||
{
|
||||
if (s_CF_RTF == 0)
|
||||
{
|
||||
s_CF_RTF = RegisterClipboardFormat("Rich Text Format");
|
||||
}
|
||||
|
||||
ExecuteOnStaThread(() => SetClipboardData(
|
||||
Tuple.Create(plainText, CF_UNICODETEXT),
|
||||
Tuple.Create(rtfText, s_CF_RTF)));
|
||||
}
|
||||
|
||||
#pragma warning disable SA1310 // Field names should not contain underscore
|
||||
private const uint GMEM_MOVEABLE = 0x0002;
|
||||
private const uint GMEM_ZEROINIT = 0x0040;
|
||||
#pragma warning restore SA1310 // Field names should not contain underscore
|
||||
private const uint GHND = GMEM_MOVEABLE | GMEM_ZEROINIT;
|
||||
|
||||
[LibraryImport("kernel32.dll")]
|
||||
private static partial IntPtr GlobalAlloc(uint flags, UIntPtr dwBytes);
|
||||
|
||||
[LibraryImport("kernel32.dll")]
|
||||
private static partial IntPtr GlobalFree(IntPtr hMem);
|
||||
|
||||
[LibraryImport("kernel32.dll")]
|
||||
private static partial IntPtr GlobalLock(IntPtr hMem);
|
||||
|
||||
[LibraryImport("kernel32.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static partial bool GlobalUnlock(IntPtr hMem);
|
||||
|
||||
[LibraryImport("kernel32.dll", EntryPoint = "RtlMoveMemory")]
|
||||
private static partial void CopyMemory(IntPtr dest, IntPtr src, uint count);
|
||||
|
||||
[LibraryImport("user32.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static partial bool IsClipboardFormatAvailable(uint format);
|
||||
|
||||
[LibraryImport("user32.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static partial bool OpenClipboard(IntPtr hWndNewOwner);
|
||||
|
||||
[LibraryImport("user32.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static partial bool CloseClipboard();
|
||||
|
||||
[LibraryImport("user32.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static partial bool EmptyClipboard();
|
||||
|
||||
[LibraryImport("user32.dll")]
|
||||
private static partial IntPtr GetClipboardData(uint format);
|
||||
|
||||
[LibraryImport("user32.dll")]
|
||||
private static partial IntPtr SetClipboardData(uint format, IntPtr data);
|
||||
|
||||
[LibraryImport("user32.dll", StringMarshalling = StringMarshalling.Utf16)]
|
||||
private static partial uint RegisterClipboardFormat(string lpszFormat);
|
||||
|
||||
#pragma warning disable SA1310 // Field names should not contain underscore
|
||||
private const uint CF_TEXT = 1;
|
||||
private const uint CF_UNICODETEXT = 13;
|
||||
|
||||
#pragma warning disable SA1308 // Variable names should not be prefixed
|
||||
private static uint s_CF_RTF;
|
||||
#pragma warning restore SA1308 // Variable names should not be prefixed
|
||||
#pragma warning restore SA1310 // Field names should not contain underscore
|
||||
|
||||
private static bool GetTextImpl(out string text)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (IsClipboardFormatAvailable(CF_UNICODETEXT))
|
||||
{
|
||||
if (OpenClipboard(IntPtr.Zero))
|
||||
{
|
||||
var data = GetClipboardData(CF_UNICODETEXT);
|
||||
if (data != IntPtr.Zero)
|
||||
{
|
||||
data = GlobalLock(data);
|
||||
text = Marshal.PtrToStringUni(data) ?? string.Empty;
|
||||
GlobalUnlock(data);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (IsClipboardFormatAvailable(CF_TEXT))
|
||||
{
|
||||
if (OpenClipboard(IntPtr.Zero))
|
||||
{
|
||||
var data = GetClipboardData(CF_TEXT);
|
||||
if (data != IntPtr.Zero)
|
||||
{
|
||||
data = GlobalLock(data);
|
||||
text = Marshal.PtrToStringAnsi(data) ?? string.Empty;
|
||||
GlobalUnlock(data);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore exceptions
|
||||
}
|
||||
finally
|
||||
{
|
||||
CloseClipboard();
|
||||
}
|
||||
|
||||
text = string.Empty;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool SetClipboardData(params Tuple<string, uint>[] data)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!OpenClipboard(IntPtr.Zero))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
EmptyClipboard();
|
||||
|
||||
foreach (var d in data)
|
||||
{
|
||||
if (!SetSingleClipboardData(d.Item1, d.Item2))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
CloseClipboard();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool SetSingleClipboardData(string text, uint format)
|
||||
{
|
||||
var hGlobal = IntPtr.Zero;
|
||||
var data = IntPtr.Zero;
|
||||
|
||||
try
|
||||
{
|
||||
uint bytes;
|
||||
if (format == s_CF_RTF || format == CF_TEXT)
|
||||
{
|
||||
bytes = (uint)(text.Length + 1);
|
||||
data = Marshal.StringToHGlobalAnsi(text);
|
||||
}
|
||||
else if (format == CF_UNICODETEXT)
|
||||
{
|
||||
bytes = (uint)((text.Length + 1) * 2);
|
||||
data = Marshal.StringToHGlobalUni(text);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not yet supported format.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (data == IntPtr.Zero)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
hGlobal = GlobalAlloc(GHND, (UIntPtr)bytes);
|
||||
if (hGlobal == IntPtr.Zero)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var dataCopy = GlobalLock(hGlobal);
|
||||
if (dataCopy == IntPtr.Zero)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
CopyMemory(dataCopy, data, bytes);
|
||||
GlobalUnlock(hGlobal);
|
||||
|
||||
if (SetClipboardData(format, hGlobal) != IntPtr.Zero)
|
||||
{
|
||||
// The clipboard owns this memory now, so don't free it.
|
||||
hGlobal = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore failures
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (data != IntPtr.Zero)
|
||||
{
|
||||
Marshal.FreeHGlobal(data);
|
||||
}
|
||||
|
||||
if (hGlobal != IntPtr.Zero)
|
||||
{
|
||||
GlobalFree(hGlobal);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void ExecuteOnStaThread(Func<bool> action)
|
||||
{
|
||||
const int RetryCount = 5;
|
||||
var tries = 0;
|
||||
|
||||
if (Thread.CurrentThread.GetApartmentState() == ApartmentState.STA)
|
||||
{
|
||||
while (tries++ < RetryCount && !action())
|
||||
{
|
||||
// wait until RetryCount or action
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Exception? exception = null;
|
||||
var thread = new Thread(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
while (tries++ < RetryCount && !action())
|
||||
{
|
||||
// wait until RetryCount or action
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
exception = e;
|
||||
}
|
||||
});
|
||||
|
||||
thread.SetApartmentState(ApartmentState.STA);
|
||||
thread.Start();
|
||||
thread.Join();
|
||||
|
||||
if (exception is not null)
|
||||
{
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public sealed class ColorHelpers
|
||||
{
|
||||
public static OptionalColor FromArgb(byte a, byte r, byte g, byte b) => new(true, new(r, g, b, a));
|
||||
|
||||
public static OptionalColor FromRgb(byte r, byte g, byte b) => new(true, new(r, g, b, 255));
|
||||
|
||||
public static OptionalColor Transparent() => new(true, new(0, 0, 0, 0));
|
||||
|
||||
public static OptionalColor NoColor() => new(false, new(0, 0, 0, 0));
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class Command : BaseObservable, ICommand
|
||||
{
|
||||
public virtual string Name
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
OnPropertyChanged(nameof(Name));
|
||||
}
|
||||
}
|
||||
|
||||
= string.Empty;
|
||||
|
||||
public virtual string Id { get; set; } = string.Empty;
|
||||
|
||||
public virtual IconInfo Icon
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
OnPropertyChanged(nameof(Icon));
|
||||
}
|
||||
}
|
||||
|
||||
= new();
|
||||
|
||||
IIconInfo ICommand.Icon => Icon;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class CommandContextItem : CommandItem, ICommandContextItem
|
||||
{
|
||||
public virtual bool IsCritical { get; set; }
|
||||
|
||||
public virtual KeyChord RequestedShortcut { get; set; }
|
||||
|
||||
public CommandContextItem(ICommand command)
|
||||
: base(command)
|
||||
{
|
||||
}
|
||||
|
||||
public CommandContextItem(
|
||||
string title,
|
||||
string subtitle = "",
|
||||
string name = "",
|
||||
Action? action = null,
|
||||
ICommandResult? result = null)
|
||||
{
|
||||
var c = new AnonymousCommand(action);
|
||||
if (!string.IsNullOrEmpty(name))
|
||||
{
|
||||
c.Name = name;
|
||||
}
|
||||
|
||||
if (result is not null)
|
||||
{
|
||||
c.Result = result;
|
||||
}
|
||||
|
||||
Command = c;
|
||||
|
||||
Title = title;
|
||||
Subtitle = subtitle;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class CommandItem : BaseObservable, ICommandItem
|
||||
{
|
||||
private ICommand? _command;
|
||||
private WeakEventListener<CommandItem, object, IPropChangedEventArgs>? _commandListener;
|
||||
private string _title = string.Empty;
|
||||
|
||||
public virtual IIconInfo? Icon
|
||||
{
|
||||
get => field;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
OnPropertyChanged(nameof(Icon));
|
||||
}
|
||||
}
|
||||
|
||||
public virtual string Title
|
||||
{
|
||||
get => !string.IsNullOrEmpty(_title) ? _title : _command?.Name ?? string.Empty;
|
||||
|
||||
set
|
||||
{
|
||||
_title = value;
|
||||
OnPropertyChanged(nameof(Title));
|
||||
}
|
||||
}
|
||||
|
||||
public virtual string Subtitle
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
OnPropertyChanged(nameof(Subtitle));
|
||||
}
|
||||
}
|
||||
|
||||
= string.Empty;
|
||||
|
||||
public virtual ICommand? Command
|
||||
{
|
||||
get => _command;
|
||||
set
|
||||
{
|
||||
if (_commandListener is not null)
|
||||
{
|
||||
_commandListener.Detach();
|
||||
_commandListener = null;
|
||||
}
|
||||
|
||||
_command = value;
|
||||
|
||||
if (value is not null)
|
||||
{
|
||||
_commandListener = new(this, OnCommandPropertyChanged, listener => value.PropChanged -= listener.OnEvent);
|
||||
value.PropChanged += _commandListener.OnEvent;
|
||||
}
|
||||
|
||||
OnPropertyChanged(nameof(Command));
|
||||
if (string.IsNullOrEmpty(_title))
|
||||
{
|
||||
OnPropertyChanged(nameof(Title));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnCommandPropertyChanged(CommandItem instance, object source, IPropChangedEventArgs args)
|
||||
{
|
||||
// command's name affects Title only if Title wasn't explicitly set
|
||||
if (args.PropertyName == nameof(ICommand.Name) && string.IsNullOrEmpty(_title))
|
||||
{
|
||||
instance.OnPropertyChanged(nameof(Title));
|
||||
}
|
||||
}
|
||||
|
||||
public virtual IContextItem[] MoreCommands
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
OnPropertyChanged(nameof(MoreCommands));
|
||||
}
|
||||
}
|
||||
|
||||
= [];
|
||||
|
||||
public CommandItem()
|
||||
: this(new NoOpCommand())
|
||||
{
|
||||
}
|
||||
|
||||
public CommandItem(ICommand command)
|
||||
{
|
||||
Command = command;
|
||||
}
|
||||
|
||||
public CommandItem(ICommandItem other)
|
||||
{
|
||||
Command = other.Command;
|
||||
Subtitle = other.Subtitle;
|
||||
Icon = (IconInfo?)other.Icon;
|
||||
MoreCommands = other.MoreCommands;
|
||||
}
|
||||
|
||||
public CommandItem(
|
||||
string title,
|
||||
string subtitle = "",
|
||||
string name = "",
|
||||
Action? action = null,
|
||||
ICommandResult? result = null)
|
||||
{
|
||||
var c = new AnonymousCommand(action);
|
||||
if (!string.IsNullOrEmpty(name))
|
||||
{
|
||||
c.Name = name;
|
||||
}
|
||||
|
||||
if (result is not null)
|
||||
{
|
||||
c.Result = result;
|
||||
}
|
||||
|
||||
Command = c;
|
||||
|
||||
Title = title;
|
||||
Subtitle = subtitle;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
// 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 Windows.Foundation;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public abstract partial class CommandProvider : ICommandProvider, ICommandProvider2
|
||||
{
|
||||
public virtual string Id { get; protected set; } = string.Empty;
|
||||
|
||||
public virtual string DisplayName { get; protected set; } = string.Empty;
|
||||
|
||||
public virtual IconInfo Icon { get; protected set; } = new IconInfo();
|
||||
|
||||
public event TypedEventHandler<object, IItemsChangedEventArgs>? ItemsChanged;
|
||||
|
||||
public abstract ICommandItem[] TopLevelCommands();
|
||||
|
||||
public virtual IFallbackCommandItem[]? FallbackCommands() => null;
|
||||
|
||||
public virtual ICommand? GetCommand(string id) => null;
|
||||
|
||||
public virtual ICommandSettings? Settings { get; protected set; }
|
||||
|
||||
public virtual bool Frozen { get; protected set; } = true;
|
||||
|
||||
IIconInfo ICommandProvider.Icon => Icon;
|
||||
|
||||
public virtual void InitializeWithHost(IExtensionHost host) => ExtensionHost.Initialize(host);
|
||||
|
||||
#pragma warning disable CA1816 // Dispose methods should call SuppressFinalize
|
||||
public virtual void Dispose()
|
||||
{
|
||||
}
|
||||
#pragma warning restore CA1816 // Dispose methods should call SuppressFinalize
|
||||
|
||||
protected void RaiseItemsChanged(int totalItems = -1)
|
||||
{
|
||||
try
|
||||
{
|
||||
// TODO #181 - This is the same thing that BaseObservable has to deal with.
|
||||
ItemsChanged?.Invoke(this, new ItemsChangedEventArgs(totalItems));
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is used to manually populate the WinRT type cache in CmdPal with
|
||||
/// any interfaces that might not follow a straight linear path of requires.
|
||||
///
|
||||
/// You don't need to call this as an extension author.
|
||||
/// </summary>
|
||||
/// <returns>an array of objects that implement all the leaf interfaces we support</returns>
|
||||
public object[] GetApiExtensionStubs()
|
||||
{
|
||||
return [new SupportCommandsWithProperties()];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A stub class which implements IExtendedAttributesProvider. Just marshalling this
|
||||
/// across the ABI will be enough for CmdPal to store IExtendedAttributesProvider in
|
||||
/// its type cache.
|
||||
/// </summary>
|
||||
private sealed partial class SupportCommandsWithProperties : IExtendedAttributesProvider
|
||||
{
|
||||
public IDictionary<string, object>? GetProperties() => null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class CommandResult : ICommandResult
|
||||
{
|
||||
public ICommandResultArgs? Args { get; private set; }
|
||||
|
||||
public CommandResultKind Kind { get; private set; } = CommandResultKind.Dismiss;
|
||||
|
||||
public static CommandResult Dismiss()
|
||||
{
|
||||
return new CommandResult()
|
||||
{
|
||||
Kind = CommandResultKind.Dismiss,
|
||||
};
|
||||
}
|
||||
|
||||
public static CommandResult GoHome()
|
||||
{
|
||||
return new CommandResult()
|
||||
{
|
||||
Kind = CommandResultKind.GoHome,
|
||||
Args = null,
|
||||
};
|
||||
}
|
||||
|
||||
public static CommandResult GoBack()
|
||||
{
|
||||
return new CommandResult()
|
||||
{
|
||||
Kind = CommandResultKind.GoBack,
|
||||
Args = null,
|
||||
};
|
||||
}
|
||||
|
||||
public static CommandResult Hide()
|
||||
{
|
||||
return new CommandResult()
|
||||
{
|
||||
Kind = CommandResultKind.Hide,
|
||||
Args = null,
|
||||
};
|
||||
}
|
||||
|
||||
public static CommandResult KeepOpen()
|
||||
{
|
||||
return new CommandResult()
|
||||
{
|
||||
Kind = CommandResultKind.KeepOpen,
|
||||
Args = null,
|
||||
};
|
||||
}
|
||||
|
||||
public static CommandResult GoToPage(GoToPageArgs args)
|
||||
{
|
||||
return new CommandResult()
|
||||
{
|
||||
Kind = CommandResultKind.GoToPage,
|
||||
Args = args,
|
||||
};
|
||||
}
|
||||
|
||||
public static CommandResult ShowToast(ToastArgs args)
|
||||
{
|
||||
return new CommandResult()
|
||||
{
|
||||
Kind = CommandResultKind.ShowToast,
|
||||
Args = args,
|
||||
};
|
||||
}
|
||||
|
||||
public static CommandResult ShowToast(string message)
|
||||
{
|
||||
return new CommandResult()
|
||||
{
|
||||
Kind = CommandResultKind.ShowToast,
|
||||
Args = new ToastArgs() { Message = message },
|
||||
};
|
||||
}
|
||||
|
||||
public static CommandResult Confirm(ConfirmationArgs args)
|
||||
{
|
||||
return new CommandResult()
|
||||
{
|
||||
Kind = CommandResultKind.Confirm,
|
||||
Args = args,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
// 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;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
namespace Microsoft.CmdPal.Common.Commands;
|
||||
|
||||
public sealed partial class ConfirmableCommand : InvokableCommand
|
||||
{
|
||||
private readonly IInvokableCommand? _command;
|
||||
|
||||
public Func<bool>? IsConfirmationRequired { get; init; }
|
||||
|
||||
public required string ConfirmationTitle { get; init; }
|
||||
|
||||
public required string ConfirmationMessage { get; init; }
|
||||
|
||||
public required IInvokableCommand Command
|
||||
{
|
||||
get => _command!;
|
||||
init
|
||||
{
|
||||
if (_command is INotifyPropChanged oldNotifier)
|
||||
{
|
||||
oldNotifier.PropChanged -= InnerCommand_PropChanged;
|
||||
}
|
||||
|
||||
_command = value;
|
||||
|
||||
if (_command is INotifyPropChanged notifier)
|
||||
{
|
||||
notifier.PropChanged += InnerCommand_PropChanged;
|
||||
}
|
||||
|
||||
OnPropertyChanged(nameof(Name));
|
||||
OnPropertyChanged(nameof(Id));
|
||||
OnPropertyChanged(nameof(Icon));
|
||||
}
|
||||
}
|
||||
|
||||
public override string Name
|
||||
{
|
||||
get => (_command as Command)?.Name ?? base.Name;
|
||||
set
|
||||
{
|
||||
if (_command is Command cmd)
|
||||
{
|
||||
cmd.Name = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
base.Name = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override string Id
|
||||
{
|
||||
get => (_command as Command)?.Id ?? base.Id;
|
||||
set
|
||||
{
|
||||
var previous = Id;
|
||||
if (_command is Command cmd)
|
||||
{
|
||||
cmd.Id = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
base.Id = value;
|
||||
}
|
||||
|
||||
if (previous != Id)
|
||||
{
|
||||
OnPropertyChanged(nameof(Id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override IconInfo Icon
|
||||
{
|
||||
get => (_command as Command)?.Icon ?? base.Icon;
|
||||
set
|
||||
{
|
||||
if (_command is Command cmd)
|
||||
{
|
||||
cmd.Icon = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
base.Icon = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ConfirmableCommand()
|
||||
{
|
||||
// Allow init-only construction
|
||||
}
|
||||
|
||||
[SetsRequiredMembers]
|
||||
public ConfirmableCommand(IInvokableCommand command, string confirmationTitle, string confirmationMessage, Func<bool>? isConfirmationRequired = null)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(command);
|
||||
ArgumentException.ThrowIfNullOrWhiteSpace(confirmationMessage);
|
||||
ArgumentNullException.ThrowIfNull(confirmationMessage);
|
||||
|
||||
IsConfirmationRequired = isConfirmationRequired;
|
||||
ConfirmationTitle = confirmationTitle;
|
||||
ConfirmationMessage = confirmationMessage;
|
||||
Command = command;
|
||||
}
|
||||
|
||||
private void InnerCommand_PropChanged(object sender, IPropChangedEventArgs args)
|
||||
{
|
||||
var property = args.PropertyName;
|
||||
|
||||
if (string.IsNullOrEmpty(property) || property == nameof(Name))
|
||||
{
|
||||
OnPropertyChanged(nameof(Name));
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(property) || property == nameof(Id))
|
||||
{
|
||||
OnPropertyChanged(nameof(Id));
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(property) || property == nameof(Icon))
|
||||
{
|
||||
OnPropertyChanged(nameof(Icon));
|
||||
}
|
||||
}
|
||||
|
||||
public override ICommandResult Invoke()
|
||||
{
|
||||
var showConfirmationDialog = IsConfirmationRequired?.Invoke() ?? true;
|
||||
if (showConfirmationDialog)
|
||||
{
|
||||
return CommandResult.Confirm(new ConfirmationArgs
|
||||
{
|
||||
Title = ConfirmationTitle,
|
||||
Description = ConfirmationMessage,
|
||||
PrimaryCommand = Command,
|
||||
IsPrimaryCommandCritical = true,
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
return Command.Invoke(this) ?? CommandResult.Dismiss();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
// 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.Globalization;
|
||||
using System.Text;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit.Properties;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class CopyPathCommand : InvokableCommand
|
||||
{
|
||||
internal static IconInfo CopyPath { get; } = new("\uE8c8"); // Copy
|
||||
|
||||
private static readonly CompositeFormat CopyFailedFormat = CompositeFormat.Parse(Resources.copy_failed);
|
||||
|
||||
private readonly string _path;
|
||||
|
||||
public CommandResult Result { get; set; } = CommandResult.ShowToast(Resources.CopyPathTextCommand_Result);
|
||||
|
||||
public CopyPathCommand(string fullPath)
|
||||
{
|
||||
this._path = fullPath;
|
||||
this.Name = Resources.CopyPathTextCommand_Name;
|
||||
this.Icon = CopyPath;
|
||||
}
|
||||
|
||||
public override CommandResult Invoke()
|
||||
{
|
||||
try
|
||||
{
|
||||
ClipboardHelper.SetText(_path);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ExtensionHost.LogMessage(new LogMessage("Copy failed: " + ex.Message) { State = MessageState.Error });
|
||||
return CommandResult.ShowToast(
|
||||
new ToastArgs
|
||||
{
|
||||
Message = string.Format(CultureInfo.CurrentCulture, CopyFailedFormat, ex.Message),
|
||||
Result = CommandResult.KeepOpen(),
|
||||
});
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class CopyTextCommand : InvokableCommand
|
||||
{
|
||||
public virtual string Text { get; set; }
|
||||
|
||||
public virtual CommandResult Result { get; set; } = CommandResult.ShowToast(Properties.Resources.CopyTextCommand_CopiedToClipboard);
|
||||
|
||||
public CopyTextCommand(string text)
|
||||
{
|
||||
Text = text;
|
||||
Name = Properties.Resources.CopyTextCommand_Copy;
|
||||
Icon = new IconInfo("\uE8C8");
|
||||
}
|
||||
|
||||
public override ICommandResult Invoke()
|
||||
{
|
||||
ClipboardHelper.SetText(Text);
|
||||
return Result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class NoOpCommand : InvokableCommand
|
||||
{
|
||||
public override ICommandResult Invoke() => CommandResult.KeepOpen();
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
// 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.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit.Properties;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class OpenFileCommand : InvokableCommand
|
||||
{
|
||||
internal static IconInfo OpenFile { get; } = new("\uE8E5"); // OpenFile
|
||||
|
||||
private readonly string _fullPath;
|
||||
|
||||
public CommandResult Result { get; set; } = CommandResult.Dismiss();
|
||||
|
||||
public OpenFileCommand(string fullPath)
|
||||
{
|
||||
this._fullPath = fullPath;
|
||||
this.Name = Resources.OpenFileCommand_Name;
|
||||
this.Icon = OpenFile;
|
||||
}
|
||||
|
||||
public override CommandResult Invoke()
|
||||
{
|
||||
using (var process = new Process())
|
||||
{
|
||||
process.StartInfo.FileName = _fullPath;
|
||||
process.StartInfo.UseShellExecute = true;
|
||||
|
||||
try
|
||||
{
|
||||
process.Start();
|
||||
}
|
||||
catch (Win32Exception ex)
|
||||
{
|
||||
ExtensionHost.LogMessage($"Unable to open {_fullPath}\n{ex}");
|
||||
}
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
// 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.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit.Properties;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class OpenInConsoleCommand : InvokableCommand
|
||||
{
|
||||
internal static IconInfo OpenInConsoleIcon { get; } = new("\uE756"); // "CommandPrompt"
|
||||
|
||||
private readonly string _path;
|
||||
private bool _isDirectory;
|
||||
|
||||
public OpenInConsoleCommand(string fullPath)
|
||||
{
|
||||
this._path = fullPath;
|
||||
this.Name = Resources.OpenInConsoleCommand_Name;
|
||||
this.Icon = OpenInConsoleIcon;
|
||||
}
|
||||
|
||||
public static OpenInConsoleCommand FromDirectory(string directory) => new(directory) { _isDirectory = true };
|
||||
|
||||
public static OpenInConsoleCommand FromFile(string file) => new(file);
|
||||
|
||||
public override CommandResult Invoke()
|
||||
{
|
||||
using (var process = new Process())
|
||||
{
|
||||
process.StartInfo.WorkingDirectory = _isDirectory ? _path : Path.GetDirectoryName(_path);
|
||||
process.StartInfo.FileName = "cmd.exe";
|
||||
|
||||
try
|
||||
{
|
||||
process.Start();
|
||||
}
|
||||
catch (Win32Exception ex)
|
||||
{
|
||||
ExtensionHost.LogMessage(new LogMessage($"Unable to open '{_path}'\n{ex.Message}\n{ex.StackTrace}") { State = MessageState.Error });
|
||||
}
|
||||
}
|
||||
|
||||
return CommandResult.Dismiss();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
// 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.Runtime.InteropServices;
|
||||
using ManagedCsWin32;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit.Properties;
|
||||
using Windows.Win32.UI.WindowsAndMessaging;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class OpenPropertiesCommand : InvokableCommand
|
||||
{
|
||||
internal static IconInfo OpenPropertiesIcon { get; } = new("\uE90F");
|
||||
|
||||
private readonly string _path;
|
||||
|
||||
private static unsafe bool ShowFileProperties(string filename)
|
||||
{
|
||||
var propertiesPtr = Marshal.StringToHGlobalUni("properties");
|
||||
var filenamePtr = Marshal.StringToHGlobalUni(filename);
|
||||
|
||||
try
|
||||
{
|
||||
var info = new Shell32.SHELLEXECUTEINFOW
|
||||
{
|
||||
CbSize = (uint)sizeof(Shell32.SHELLEXECUTEINFOW),
|
||||
LpVerb = propertiesPtr,
|
||||
LpFile = filenamePtr,
|
||||
Show = (int)SHOW_WINDOW_CMD.SW_SHOW,
|
||||
FMask = global::Windows.Win32.PInvoke.SEE_MASK_INVOKEIDLIST,
|
||||
};
|
||||
|
||||
return Shell32.ShellExecuteEx(ref info);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.FreeHGlobal(filenamePtr);
|
||||
Marshal.FreeHGlobal(propertiesPtr);
|
||||
}
|
||||
}
|
||||
|
||||
public OpenPropertiesCommand(string fullPath)
|
||||
{
|
||||
this._path = fullPath;
|
||||
this.Name = Resources.OpenPropertiesCommand_Name;
|
||||
this.Icon = OpenPropertiesIcon;
|
||||
}
|
||||
|
||||
public override CommandResult Invoke()
|
||||
{
|
||||
try
|
||||
{
|
||||
ShowFileProperties(_path);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ExtensionHost.LogMessage(new LogMessage($"Error showing file properties '{_path}'\n{ex.Message}\n{ex.StackTrace}") { State = MessageState.Error });
|
||||
}
|
||||
|
||||
return CommandResult.Dismiss();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class OpenUrlCommand : InvokableCommand
|
||||
{
|
||||
private readonly string _target;
|
||||
|
||||
public CommandResult Result { get; set; } = CommandResult.KeepOpen();
|
||||
|
||||
public OpenUrlCommand(string target)
|
||||
{
|
||||
_target = target;
|
||||
Name = Properties.Resources.OpenUrlCommand_Open;
|
||||
Icon = new IconInfo("\uE8A7");
|
||||
}
|
||||
|
||||
public override CommandResult Invoke()
|
||||
{
|
||||
ShellHelpers.OpenInShell(_target);
|
||||
return Result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
// 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.Runtime.InteropServices;
|
||||
using ManagedCsWin32;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit.Properties;
|
||||
using Windows.Win32.UI.WindowsAndMessaging;
|
||||
|
||||
namespace Microsoft.CmdPal.Core.Common.Commands;
|
||||
|
||||
public partial class OpenWithCommand : InvokableCommand
|
||||
{
|
||||
internal static IconInfo OpenWithIcon { get; } = new("\uE7AC");
|
||||
|
||||
private readonly string _path;
|
||||
|
||||
private static unsafe bool OpenWith(string filename)
|
||||
{
|
||||
var filenamePtr = Marshal.StringToHGlobalUni(filename);
|
||||
var verbPtr = Marshal.StringToHGlobalUni("openas");
|
||||
|
||||
try
|
||||
{
|
||||
var info = new Shell32.SHELLEXECUTEINFOW
|
||||
{
|
||||
CbSize = (uint)sizeof(Shell32.SHELLEXECUTEINFOW),
|
||||
LpVerb = verbPtr,
|
||||
LpFile = filenamePtr,
|
||||
Show = (int)SHOW_WINDOW_CMD.SW_SHOWNORMAL,
|
||||
FMask = global::Windows.Win32.PInvoke.SEE_MASK_INVOKEIDLIST,
|
||||
};
|
||||
|
||||
return Shell32.ShellExecuteEx(ref info);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Marshal.FreeHGlobal(filenamePtr);
|
||||
Marshal.FreeHGlobal(verbPtr);
|
||||
}
|
||||
}
|
||||
|
||||
public OpenWithCommand(string fullPath)
|
||||
{
|
||||
this._path = fullPath;
|
||||
this.Name = Resources.OpenWithCommand_Name;
|
||||
this.Icon = OpenWithIcon;
|
||||
}
|
||||
|
||||
public override CommandResult Invoke()
|
||||
{
|
||||
OpenWith(_path);
|
||||
|
||||
return CommandResult.GoHome();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// 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;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class ShowFileInFolderCommand : InvokableCommand
|
||||
{
|
||||
private readonly string _path;
|
||||
private static readonly IconInfo Ico = new("\uE838");
|
||||
|
||||
public CommandResult Result { get; set; } = CommandResult.Dismiss();
|
||||
|
||||
public ShowFileInFolderCommand(string path)
|
||||
{
|
||||
_path = path;
|
||||
Name = Properties.Resources.ShowFileInFolderCommand_ShowInFolder;
|
||||
Icon = Ico;
|
||||
}
|
||||
|
||||
public override CommandResult Invoke()
|
||||
{
|
||||
if (Path.Exists(_path))
|
||||
{
|
||||
try
|
||||
{
|
||||
var argument = "/select, \"" + _path + "\"";
|
||||
Process.Start("explorer.exe", argument);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class ConfirmationArgs : IConfirmationArgs
|
||||
{
|
||||
public virtual string? Title { get; set; }
|
||||
|
||||
public virtual string? Description { get; set; }
|
||||
|
||||
public virtual ICommand? PrimaryCommand { get; set; }
|
||||
|
||||
public virtual bool IsPrimaryCommandCritical { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// 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 Windows.Foundation;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public abstract partial class ContentPage : Page, IContentPage
|
||||
{
|
||||
public event TypedEventHandler<object, IItemsChangedEventArgs>? ItemsChanged;
|
||||
|
||||
public virtual IDetails? Details
|
||||
{
|
||||
get => field;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
OnPropertyChanged(nameof(Details));
|
||||
}
|
||||
}
|
||||
|
||||
public virtual IContextItem[] Commands { get; set; } = [];
|
||||
|
||||
public abstract IContent[] GetContent();
|
||||
|
||||
protected void RaiseItemsChanged(int totalItems = -1)
|
||||
{
|
||||
try
|
||||
{
|
||||
// TODO #181 - This is the same thing that BaseObservable has to deal with.
|
||||
ItemsChanged?.Invoke(this, new ItemsChangedEventArgs(totalItems));
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class Details : BaseObservable, IDetails
|
||||
{
|
||||
public virtual IIconInfo HeroImage
|
||||
{
|
||||
get => field;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
OnPropertyChanged(nameof(HeroImage));
|
||||
}
|
||||
}
|
||||
|
||||
= new IconInfo();
|
||||
|
||||
public virtual string Title
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
OnPropertyChanged(nameof(Title));
|
||||
}
|
||||
}
|
||||
|
||||
= string.Empty;
|
||||
|
||||
public virtual string Body
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
OnPropertyChanged(nameof(Body));
|
||||
}
|
||||
}
|
||||
|
||||
= string.Empty;
|
||||
|
||||
public virtual IDetailsElement[] Metadata
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
OnPropertyChanged(nameof(Metadata));
|
||||
}
|
||||
}
|
||||
|
||||
= [];
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class DetailsCommands : IDetailsCommands
|
||||
{
|
||||
public ICommand[]? Commands { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class DetailsElement : IDetailsElement
|
||||
{
|
||||
public virtual string Key { get; set; } = string.Empty;
|
||||
|
||||
public virtual IDetailsData? Data { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class DetailsLink : IDetailsLink
|
||||
{
|
||||
public virtual Uri? Link { get; set; }
|
||||
|
||||
public virtual string Text { get; set; } = string.Empty;
|
||||
|
||||
public DetailsLink()
|
||||
{
|
||||
}
|
||||
|
||||
public DetailsLink(string url)
|
||||
: this(url, url)
|
||||
{
|
||||
}
|
||||
|
||||
public DetailsLink(string url, string text)
|
||||
{
|
||||
if (Uri.TryCreate(url, default(UriCreationOptions), out var newUri))
|
||||
{
|
||||
Link = newUri;
|
||||
}
|
||||
|
||||
Text = text;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class DetailsSeparator : IDetailsSeparator
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class DetailsTags : IDetailsTags
|
||||
{
|
||||
public ITag[] Tags { get; set; } = [];
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public abstract class DynamicListPage : ListPage, IDynamicListPage
|
||||
{
|
||||
public override string SearchText
|
||||
{
|
||||
get => base.SearchText;
|
||||
set
|
||||
{
|
||||
var oldSearch = base.SearchText;
|
||||
SetSearchNoUpdate(value);
|
||||
UpdateSearchText(oldSearch, value);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void UpdateSearchText(string oldSearch, string newSearch);
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class ExtensionHost
|
||||
{
|
||||
public static IExtensionHost? Host { get; private set; }
|
||||
|
||||
public static void Initialize(IExtensionHost host) => Host = host;
|
||||
|
||||
/// <summary>
|
||||
/// Fire-and-forget a log message to the Command Palette host app. Since
|
||||
/// the host is in another process, we do this in a try/catch in a
|
||||
/// background thread, as to not block the calling thread, nor explode if
|
||||
/// the host app is gone.
|
||||
/// </summary>
|
||||
/// <param name="message">The log message to send</param>
|
||||
public static void LogMessage(ILogMessage message)
|
||||
{
|
||||
if (Host is not null)
|
||||
{
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await Host.LogMessage(message);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static void LogMessage(string message)
|
||||
{
|
||||
var logMessage = new LogMessage() { Message = message };
|
||||
LogMessage(logMessage);
|
||||
}
|
||||
|
||||
public static void ShowStatus(IStatusMessage message, StatusContext context)
|
||||
{
|
||||
if (Host is not null)
|
||||
{
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await Host.ShowStatus(message, context);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static void HideStatus(IStatusMessage message)
|
||||
{
|
||||
if (Host is not null)
|
||||
{
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await Host.HideStatus(message);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
// 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.Runtime.InteropServices;
|
||||
using System.Runtime.InteropServices.Marshalling;
|
||||
using Windows.Win32;
|
||||
using Windows.Win32.Foundation;
|
||||
using WinRT;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions;
|
||||
|
||||
[ComVisible(true)]
|
||||
[GeneratedComClass]
|
||||
internal sealed partial class ExtensionInstanceManager : IClassFactory
|
||||
{
|
||||
#pragma warning disable SA1310 // Field names should not contain underscore
|
||||
|
||||
private const int E_NOINTERFACE = unchecked((int)0x80004002);
|
||||
|
||||
private const int CLASS_E_NOAGGREGATION = unchecked((int)0x80040110);
|
||||
|
||||
private const int E_ACCESSDENIED = unchecked((int)0x80070005);
|
||||
|
||||
// Known constant ignored by win32metadata and cswin32 projections.
|
||||
// https://github.com/microsoft/win32metadata/blob/main/generation/WinSDK/RecompiledIdlHeaders/um/processthreadsapi.h
|
||||
private static readonly HANDLE CURRENT_THREAD_PSEUDO_HANDLE = (HANDLE)(IntPtr)(-6);
|
||||
|
||||
private static readonly Guid IID_IUnknown = Guid.Parse("00000000-0000-0000-C000-000000000046");
|
||||
|
||||
#pragma warning restore SA1310 // Field names should not contain underscore
|
||||
|
||||
private readonly Func<IExtension> _createExtension;
|
||||
|
||||
private readonly bool _restrictToMicrosoftExtensionHosts;
|
||||
|
||||
private readonly Guid _clsid;
|
||||
|
||||
public ExtensionInstanceManager(Func<IExtension> createExtension, bool restrictToMicrosoftExtensionHosts, Guid clsid)
|
||||
{
|
||||
_createExtension = createExtension;
|
||||
_restrictToMicrosoftExtensionHosts = restrictToMicrosoftExtensionHosts;
|
||||
_clsid = clsid;
|
||||
}
|
||||
|
||||
public void CreateInstance(
|
||||
[MarshalAs(UnmanagedType.Interface)] object pUnkOuter,
|
||||
Guid riid,
|
||||
out IntPtr ppvObject)
|
||||
{
|
||||
if (_restrictToMicrosoftExtensionHosts && !IsMicrosoftExtensionHost())
|
||||
{
|
||||
Marshal.ThrowExceptionForHR(E_ACCESSDENIED);
|
||||
}
|
||||
|
||||
ppvObject = IntPtr.Zero;
|
||||
|
||||
if (pUnkOuter is not null)
|
||||
{
|
||||
Marshal.ThrowExceptionForHR(CLASS_E_NOAGGREGATION);
|
||||
}
|
||||
|
||||
if (riid == _clsid || riid == IID_IUnknown)
|
||||
{
|
||||
// Create the instance of the .NET object
|
||||
var managed = _createExtension();
|
||||
var ins = MarshalInspectable<object>.FromManaged(managed);
|
||||
ppvObject = ins;
|
||||
}
|
||||
else
|
||||
{
|
||||
// The object that ppvObject points to does not support the
|
||||
// interface identified by riid.
|
||||
Marshal.ThrowExceptionForHR(E_NOINTERFACE);
|
||||
}
|
||||
}
|
||||
|
||||
public void LockServer([MarshalAs(UnmanagedType.Bool)] bool fLock)
|
||||
{
|
||||
}
|
||||
|
||||
private unsafe bool IsMicrosoftExtensionHost()
|
||||
{
|
||||
if (PInvoke.CoImpersonateClient() != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
uint buffer = 0;
|
||||
if (PInvoke.GetPackageFamilyNameFromToken(CURRENT_THREAD_PSEUDO_HANDLE, &buffer, null) != WIN32_ERROR.ERROR_INSUFFICIENT_BUFFER)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var value = new char[buffer];
|
||||
fixed (char* p = value)
|
||||
{
|
||||
if (PInvoke.GetPackageFamilyNameFromToken(CURRENT_THREAD_PSEUDO_HANDLE, &buffer, p) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (PInvoke.CoRevertToSelf() != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var valueStr = new string(value);
|
||||
return valueStr switch
|
||||
{
|
||||
"Microsoft.Windows.CmdPal_8wekyb3d8bbwe\0" or "Microsoft.Windows.CmdPal.Canary_8wekyb3d8bbwe\0" or "Microsoft.Windows.CmdPal.Dev_8wekyb3d8bbwe\0" or "Microsoft.Windows.DevHome_8wekyb3d8bbwe\0" or "Microsoft.Windows.DevHome.Canary_8wekyb3d8bbwe\0" or "Microsoft.Windows.DevHome.Dev_8wekyb3d8bbwe\0" or "Microsoft.WindowsTerminal\0" or "Microsoft.WindowsTerminal_8wekyb3d8bbwe\0" or "WindowsTerminalDev_8wekyb3d8bbwe\0" or "Microsoft.WindowsTerminalPreview_8wekyb3d8bbwe\0" => true,
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// https://docs.microsoft.com/windows/win32/api/unknwn/nn-unknwn-iclassfactory
|
||||
[GeneratedComInterface]
|
||||
[Guid("00000001-0000-0000-C000-000000000046")]
|
||||
internal partial interface IClassFactory
|
||||
{
|
||||
void CreateInstance(
|
||||
[MarshalAs(UnmanagedType.Interface)] object pUnkOuter,
|
||||
Guid riid,
|
||||
out IntPtr ppvObject);
|
||||
|
||||
void LockServer([MarshalAs(UnmanagedType.Bool)] bool fLock);
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
// 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;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.InteropServices.Marshalling;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions;
|
||||
|
||||
public sealed partial class ExtensionServer : IDisposable
|
||||
{
|
||||
private readonly HashSet<int> registrationCookies = [];
|
||||
private ExtensionInstanceManager? _extensionInstanceManager;
|
||||
private ComWrappers? _comWrappers;
|
||||
|
||||
public void RegisterExtension<T>(Func<T> createExtension, bool restrictToMicrosoftExtensionHosts = false)
|
||||
where T : IExtension
|
||||
{
|
||||
Trace.WriteLine($"Registering class object:");
|
||||
Trace.Indent();
|
||||
Trace.WriteLine($"CLSID: {typeof(T).GUID:B}");
|
||||
Trace.WriteLine($"Type: {typeof(T)}");
|
||||
|
||||
int cookie;
|
||||
var clsid = typeof(T).GUID;
|
||||
var wrappedCallback = () => (IExtension)createExtension();
|
||||
_extensionInstanceManager ??= new ExtensionInstanceManager(wrappedCallback, restrictToMicrosoftExtensionHosts, typeof(T).GUID);
|
||||
_comWrappers ??= new StrategyBasedComWrappers();
|
||||
|
||||
var f = _comWrappers.GetOrCreateComInterfaceForObject(_extensionInstanceManager, CreateComInterfaceFlags.None);
|
||||
|
||||
var hr = Ole32.CoRegisterClassObject(
|
||||
ref clsid,
|
||||
f,
|
||||
Ole32.CLSCTX_LOCAL_SERVER,
|
||||
Ole32.REGCLS_MULTIPLEUSE | Ole32.REGCLS_SUSPENDED,
|
||||
out cookie);
|
||||
|
||||
if (hr < 0)
|
||||
{
|
||||
Marshal.ThrowExceptionForHR(hr);
|
||||
}
|
||||
|
||||
registrationCookies.Add(cookie);
|
||||
Trace.WriteLine($"Cookie: {cookie}");
|
||||
Trace.Unindent();
|
||||
|
||||
hr = Ole32.CoResumeClassObjects();
|
||||
if (hr < 0)
|
||||
{
|
||||
Marshal.ThrowExceptionForHR(hr);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning disable CA1822 // Mark members as static
|
||||
public void Run() =>
|
||||
|
||||
// TODO : We need to handle lifetime management of the server.
|
||||
// For details around ref counting and locking of out-of-proc COM servers, see
|
||||
// https://docs.microsoft.com/windows/win32/com/out-of-process-server-implementation-helpers
|
||||
Console.ReadLine();
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Trace.WriteLine($"Revoking class object registrations:");
|
||||
Trace.Indent();
|
||||
foreach (var cookie in registrationCookies)
|
||||
{
|
||||
Trace.WriteLine($"Cookie: {cookie}");
|
||||
var hr = Ole32.CoRevokeClassObject(cookie);
|
||||
Debug.Assert(hr >= 0, $"CoRevokeClassObject failed ({hr:x}). Cookie: {cookie}");
|
||||
}
|
||||
|
||||
Trace.Unindent();
|
||||
}
|
||||
|
||||
private sealed class Ole32
|
||||
{
|
||||
#pragma warning disable SA1310 // Field names should not contain underscore
|
||||
// https://docs.microsoft.com/windows/win32/api/wtypesbase/ne-wtypesbase-clsctx
|
||||
public const int CLSCTX_LOCAL_SERVER = 0x4;
|
||||
|
||||
// https://docs.microsoft.com/windows/win32/api/combaseapi/ne-combaseapi-regcls
|
||||
public const int REGCLS_MULTIPLEUSE = 1;
|
||||
public const int REGCLS_SUSPENDED = 4;
|
||||
#pragma warning restore SA1310 // Field names should not contain underscore
|
||||
|
||||
// https://docs.microsoft.com/windows/win32/api/combaseapi/nf-combaseapi-coregisterclassobject
|
||||
[DllImport(nameof(Ole32))]
|
||||
public static extern int CoRegisterClassObject(ref Guid guid, IntPtr obj, int context, int flags, out int register);
|
||||
|
||||
// https://docs.microsoft.com/windows/win32/api/combaseapi/nf-combaseapi-coresumeclassobjects
|
||||
[DllImport(nameof(Ole32))]
|
||||
public static extern int CoResumeClassObjects();
|
||||
|
||||
// https://docs.microsoft.com/windows/win32/api/combaseapi/nf-combaseapi-corevokeclassobject
|
||||
[DllImport(nameof(Ole32))]
|
||||
public static extern int CoRevokeClassObject(int register);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class FallbackCommandItem : CommandItem, IFallbackCommandItem, IFallbackHandler
|
||||
{
|
||||
private readonly IFallbackHandler? _fallbackHandler;
|
||||
|
||||
public FallbackCommandItem(string displayTitle)
|
||||
{
|
||||
DisplayTitle = displayTitle;
|
||||
}
|
||||
|
||||
public FallbackCommandItem(ICommand command, string displayTitle)
|
||||
: base(command)
|
||||
{
|
||||
DisplayTitle = displayTitle;
|
||||
if (command is IFallbackHandler f)
|
||||
{
|
||||
_fallbackHandler = f;
|
||||
}
|
||||
}
|
||||
|
||||
public IFallbackHandler? FallbackHandler
|
||||
{
|
||||
get => _fallbackHandler ?? this;
|
||||
init => _fallbackHandler = value;
|
||||
}
|
||||
|
||||
public virtual string DisplayTitle { get; }
|
||||
|
||||
public virtual void UpdateQuery(string query) => _fallbackHandler?.UpdateQuery(query);
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class Filter : BaseObservable, IFilter
|
||||
{
|
||||
public virtual IIconInfo Icon
|
||||
{
|
||||
get => field;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
OnPropertyChanged(nameof(Icon));
|
||||
}
|
||||
}
|
||||
|
||||
= new IconInfo();
|
||||
|
||||
public virtual string Id
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
OnPropertyChanged(nameof(Id));
|
||||
}
|
||||
}
|
||||
|
||||
= string.Empty;
|
||||
|
||||
public virtual string Name
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
OnPropertyChanged(nameof(Name));
|
||||
}
|
||||
}
|
||||
|
||||
= string.Empty;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public abstract partial class Filters : BaseObservable, IFilters
|
||||
{
|
||||
public string CurrentFilterId
|
||||
{
|
||||
get => field;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
OnPropertyChanged(nameof(CurrentFilterId));
|
||||
}
|
||||
}
|
||||
|
||||
= string.Empty;
|
||||
|
||||
// This method should be overridden in derived classes to provide the actual filters.
|
||||
public abstract IFilterItem[] GetFilters();
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// 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 Windows.Foundation.Collections;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an icon that is a font glyph.
|
||||
/// This is used for icons that are defined by a specific font face,
|
||||
/// such as Wingdings.
|
||||
///
|
||||
/// Note that Command Palette will default to using the Segoe Fluent Icons,
|
||||
/// Segoe MDL2 Assets font for glyphs in the Segoe UI Symbol range, or Segoe
|
||||
/// UI for any other glyphs. This class is only needed if you want a non-Segoe
|
||||
/// font icon.
|
||||
/// </summary>
|
||||
public partial class FontIconData : IconData, IExtendedAttributesProvider
|
||||
{
|
||||
public string FontFamily { get; set; }
|
||||
|
||||
public FontIconData(string glyph, string fontFamily)
|
||||
: base(glyph)
|
||||
{
|
||||
FontFamily = fontFamily;
|
||||
}
|
||||
|
||||
public IDictionary<string, object>? GetProperties() => new ValueSet()
|
||||
{
|
||||
{ "FontFamily", FontFamily },
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class FormContent : BaseObservable, IFormContent
|
||||
{
|
||||
public virtual string DataJson
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
OnPropertyChanged(nameof(DataJson));
|
||||
}
|
||||
}
|
||||
|
||||
= string.Empty;
|
||||
|
||||
public virtual string StateJson
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
OnPropertyChanged(nameof(StateJson));
|
||||
}
|
||||
}
|
||||
|
||||
= string.Empty;
|
||||
|
||||
public virtual string TemplateJson
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
OnPropertyChanged(nameof(TemplateJson));
|
||||
}
|
||||
}
|
||||
|
||||
= string.Empty;
|
||||
|
||||
public virtual ICommandResult SubmitForm(string inputs, string data) => SubmitForm(inputs);
|
||||
|
||||
public virtual ICommandResult SubmitForm(string inputs) => CommandResult.KeepOpen();
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
// Inspired by the fuzzy.rs from edit.exe
|
||||
public static class FuzzyStringMatcher
|
||||
{
|
||||
private const int NOMATCH = 0;
|
||||
|
||||
public static int ScoreFuzzy(string needle, string haystack, bool allowNonContiguousMatches = true)
|
||||
{
|
||||
var (s, _) = ScoreFuzzyWithPositions(needle, haystack, allowNonContiguousMatches);
|
||||
return s;
|
||||
}
|
||||
|
||||
public static (int Score, List<int> Positions) ScoreFuzzyWithPositions(string needle, string haystack, bool allowNonContiguousMatches)
|
||||
{
|
||||
if (string.IsNullOrEmpty(haystack) || string.IsNullOrEmpty(needle))
|
||||
{
|
||||
return (NOMATCH, new List<int>());
|
||||
}
|
||||
|
||||
var target = haystack.ToCharArray();
|
||||
var query = needle.ToCharArray();
|
||||
|
||||
if (target.Length < query.Length)
|
||||
{
|
||||
return (NOMATCH, new List<int>());
|
||||
}
|
||||
|
||||
var targetUpper = FoldCase(haystack);
|
||||
var queryUpper = FoldCase(needle);
|
||||
var targetUpperChars = targetUpper.ToCharArray();
|
||||
var queryUpperChars = queryUpper.ToCharArray();
|
||||
|
||||
var area = query.Length * target.Length;
|
||||
var scores = new int[area];
|
||||
var matches = new int[area];
|
||||
|
||||
for (var qi = 0; qi < query.Length; qi++)
|
||||
{
|
||||
var qiOffset = qi * target.Length;
|
||||
var qiPrevOffset = qi > 0 ? (qi - 1) * target.Length : 0;
|
||||
|
||||
for (var ti = 0; ti < target.Length; ti++)
|
||||
{
|
||||
var currentIndex = qiOffset + ti;
|
||||
var diagIndex = (qi > 0 && ti > 0) ? qiPrevOffset + ti - 1 : 0;
|
||||
var leftScore = ti > 0 ? scores[currentIndex - 1] : 0;
|
||||
var diagScore = (qi > 0 && ti > 0) ? scores[diagIndex] : 0;
|
||||
var matchSeqLen = (qi > 0 && ti > 0) ? matches[diagIndex] : 0;
|
||||
|
||||
var score = (diagScore == 0 && qi != 0) ? 0 :
|
||||
ComputeCharScore(
|
||||
query[qi],
|
||||
queryUpperChars[qi],
|
||||
ti != 0 ? target[ti - 1] : null,
|
||||
target[ti],
|
||||
targetUpperChars[ti],
|
||||
matchSeqLen);
|
||||
|
||||
var isValidScore = score != 0 && diagScore + score >= leftScore &&
|
||||
(allowNonContiguousMatches || qi > 0 ||
|
||||
targetUpperChars.Skip(ti).Take(queryUpperChars.Length).SequenceEqual(queryUpperChars));
|
||||
|
||||
if (isValidScore)
|
||||
{
|
||||
matches[currentIndex] = matchSeqLen + 1;
|
||||
scores[currentIndex] = diagScore + score;
|
||||
}
|
||||
else
|
||||
{
|
||||
matches[currentIndex] = NOMATCH;
|
||||
scores[currentIndex] = leftScore;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var positions = new List<int>();
|
||||
if (query.Length > 0 && target.Length > 0)
|
||||
{
|
||||
var qi = query.Length - 1;
|
||||
var ti = target.Length - 1;
|
||||
|
||||
while (true)
|
||||
{
|
||||
var index = (qi * target.Length) + ti;
|
||||
if (matches[index] == NOMATCH)
|
||||
{
|
||||
if (ti == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
ti--;
|
||||
}
|
||||
else
|
||||
{
|
||||
positions.Add(ti);
|
||||
if (qi == 0 || ti == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
qi--;
|
||||
ti--;
|
||||
}
|
||||
}
|
||||
|
||||
positions.Reverse();
|
||||
}
|
||||
|
||||
return (scores[area - 1], positions);
|
||||
}
|
||||
|
||||
private static string FoldCase(string input)
|
||||
{
|
||||
return input.ToUpperInvariant();
|
||||
}
|
||||
|
||||
private static int ComputeCharScore(
|
||||
char query,
|
||||
char queryLower,
|
||||
char? targetPrev,
|
||||
char targetCurr,
|
||||
char targetLower,
|
||||
int matchSeqLen)
|
||||
{
|
||||
if (!ConsiderAsEqual(queryLower, targetLower))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
var score = 1; // Character match bonus
|
||||
|
||||
if (matchSeqLen > 0)
|
||||
{
|
||||
score += matchSeqLen * 5; // Consecutive match bonus
|
||||
}
|
||||
|
||||
if (query == targetCurr)
|
||||
{
|
||||
score += 1; // Same case bonus
|
||||
}
|
||||
|
||||
if (targetPrev.HasValue)
|
||||
{
|
||||
var sepBonus = ScoreSeparator(targetPrev.Value);
|
||||
if (sepBonus > 0)
|
||||
{
|
||||
score += sepBonus;
|
||||
}
|
||||
else if (char.IsUpper(targetCurr) && matchSeqLen == 0)
|
||||
{
|
||||
score += 2; // CamelCase bonus
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
score += 8; // Start of word bonus
|
||||
}
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
private static bool ConsiderAsEqual(char a, char b)
|
||||
{
|
||||
return a == b || (a == '/' && b == '\\') || (a == '\\' && b == '/');
|
||||
}
|
||||
|
||||
private static int ScoreSeparator(char ch)
|
||||
{
|
||||
return ch switch
|
||||
{
|
||||
'/' or '\\' => 5,
|
||||
'_' or '-' or '.' or ' ' or '\'' or '"' or ':' => 4,
|
||||
_ => 0,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class GalleryGridLayout : BaseObservable, IGalleryGridLayout
|
||||
{
|
||||
public virtual bool ShowTitle
|
||||
{
|
||||
get => field;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
OnPropertyChanged(nameof(ShowTitle));
|
||||
}
|
||||
}
|
||||
|
||||
= true;
|
||||
|
||||
public virtual bool ShowSubtitle
|
||||
{
|
||||
get => field;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
OnPropertyChanged(nameof(ShowSubtitle));
|
||||
}
|
||||
}
|
||||
|
||||
= true;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// 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;
|
||||
|
||||
[assembly: SuppressMessage("Usage", "CsWinRT1028:Class is not marked partial", Justification = "Type is not passed across the WinRT ABI", Scope = "type", Target = "~T:WinRT._EventSource_global__Windows_Foundation_TypedEventHandler_object__global__Microsoft_CommandPalette_Extensions_IPropChangedEventArgs_.EventState")]
|
||||
[assembly: SuppressMessage("Usage", "CsWinRT1028:Class is not marked partial", Justification = "Type is not passed across the WinRT ABI", Scope = "type", Target = "~T:WinRT._EventSource_global__Windows_Foundation_TypedEventHandler_object__global__Microsoft_CommandPalette_Extensions_IItemsChangedEventArgs_.EventState")]
|
||||
@@ -0,0 +1,12 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class GoToPageArgs : IGoToPageArgs
|
||||
{
|
||||
public required string PageId { get; set; }
|
||||
|
||||
public NavigationMode NavigationMode { get; set; } = NavigationMode.Push;
|
||||
}
|
||||
@@ -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.Text.Json.Nodes;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
internal interface ISettingsForm
|
||||
{
|
||||
public string ToForm();
|
||||
|
||||
public void Update(JsonObject payload);
|
||||
|
||||
public Dictionary<string, object> ToDictionary();
|
||||
|
||||
public string ToDataIdentifier();
|
||||
|
||||
public string ToState();
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// 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 Windows.Storage.Streams;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class IconData : IIconData
|
||||
{
|
||||
public IRandomAccessStreamReference? Data { get; set; }
|
||||
|
||||
public string? Icon { get; set; } = string.Empty;
|
||||
|
||||
public IconData(string? icon)
|
||||
{
|
||||
Icon = icon;
|
||||
}
|
||||
|
||||
public IconData(IRandomAccessStreamReference data)
|
||||
{
|
||||
Data = data;
|
||||
}
|
||||
|
||||
internal IconData()
|
||||
: this(string.Empty)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public sealed class IconHelpers
|
||||
{
|
||||
public static IconInfo FromRelativePath(string path) => new(Path.Combine(AppDomain.CurrentDomain.BaseDirectory.ToString(), path));
|
||||
|
||||
public static IconInfo FromRelativePaths(string lightIcon, string darkIcon) =>
|
||||
new(
|
||||
new(Path.Combine(AppDomain.CurrentDomain.BaseDirectory.ToString(), lightIcon)),
|
||||
new(Path.Combine(AppDomain.CurrentDomain.BaseDirectory.ToString(), darkIcon)));
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
// 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 Windows.Storage.Streams;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class IconInfo : IIconInfo
|
||||
{
|
||||
public virtual IconData Dark { get; set; } = new();
|
||||
|
||||
public virtual IconData Light { get; set; } = new();
|
||||
|
||||
IIconData IIconInfo.Dark => Dark;
|
||||
|
||||
IIconData IIconInfo.Light => Light;
|
||||
|
||||
public IconInfo(string? icon)
|
||||
{
|
||||
Dark = Light = new(icon);
|
||||
}
|
||||
|
||||
public IconInfo(IconData light, IconData dark)
|
||||
{
|
||||
Light = light;
|
||||
Dark = dark;
|
||||
}
|
||||
|
||||
public IconInfo(IconData icon)
|
||||
{
|
||||
Light = icon;
|
||||
Dark = icon;
|
||||
}
|
||||
|
||||
internal IconInfo()
|
||||
: this(string.Empty)
|
||||
{
|
||||
}
|
||||
|
||||
public static IconInfo FromStream(IRandomAccessStream stream)
|
||||
{
|
||||
var data = new IconData(RandomAccessStreamReference.CreateFromStream(stream));
|
||||
return new IconInfo(data, data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public abstract partial class InvokableCommand : Command, IInvokableCommand
|
||||
{
|
||||
public virtual ICommandResult Invoke() => CommandResult.KeepOpen();
|
||||
|
||||
public virtual ICommandResult Invoke(object? sender) => Invoke();
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// 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 Windows.Foundation;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class ItemsChangedEventArgs : IItemsChangedEventArgs
|
||||
{
|
||||
public int TotalItems { get; protected set; }
|
||||
|
||||
public ItemsChangedEventArgs(int totalItems = -1)
|
||||
{
|
||||
TotalItems = totalItems;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// 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.Text.Json.Serialization;
|
||||
using static Microsoft.CommandPalette.Extensions.Toolkit.ChoiceSetSetting;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
[JsonSerializable(typeof(float))]
|
||||
[JsonSerializable(typeof(int))]
|
||||
[JsonSerializable(typeof(string))]
|
||||
[JsonSerializable(typeof(bool))]
|
||||
[JsonSerializable(typeof(Choice))]
|
||||
[JsonSerializable(typeof(List<Choice>))]
|
||||
[JsonSerializable(typeof(List<ChoiceSetSetting>))]
|
||||
[JsonSerializable(typeof(Dictionary<string, object>), TypeInfoPropertyName = "Dictionary")]
|
||||
[JsonSerializable(typeof(List<Dictionary<string, object>>))]
|
||||
[JsonSourceGenerationOptions(UseStringEnumConverter = true, WriteIndented = true)]
|
||||
internal sealed partial class JsonSerializationContext : JsonSerializerContext
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
// 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.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public abstract class JsonSettingsManager
|
||||
{
|
||||
public Settings Settings { get; } = new();
|
||||
|
||||
public string FilePath { get; init; } = string.Empty;
|
||||
|
||||
private static readonly JsonSerializerOptions _serializerOptions = new()
|
||||
{
|
||||
WriteIndented = true,
|
||||
};
|
||||
|
||||
public virtual void LoadSettings()
|
||||
{
|
||||
if (string.IsNullOrEmpty(FilePath))
|
||||
{
|
||||
throw new InvalidOperationException($"You must set a valid {nameof(FilePath)} before calling {nameof(LoadSettings)}");
|
||||
}
|
||||
|
||||
var filePath = FilePath;
|
||||
if (!File.Exists(filePath))
|
||||
{
|
||||
ExtensionHost.LogMessage(new LogMessage() { Message = "The provided settings file does not exist" });
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Read the JSON content from the file
|
||||
var jsonContent = File.ReadAllText(filePath);
|
||||
|
||||
// Is it valid JSON?
|
||||
if (JsonNode.Parse(jsonContent) is JsonObject savedSettings)
|
||||
{
|
||||
Settings.Update(jsonContent);
|
||||
}
|
||||
else
|
||||
{
|
||||
ExtensionHost.LogMessage(new LogMessage() { Message = "Failed to parse settings file as JsonObject." });
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ExtensionHost.LogMessage(new LogMessage() { Message = ex.ToString() });
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void SaveSettings()
|
||||
{
|
||||
if (string.IsNullOrEmpty(FilePath))
|
||||
{
|
||||
throw new InvalidOperationException($"You must set a valid {nameof(FilePath)} before calling {nameof(SaveSettings)}");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Serialize the main dictionary to JSON and save it to the file
|
||||
var settingsJson = Settings.ToJson();
|
||||
|
||||
// Is it valid JSON?
|
||||
if (JsonNode.Parse(settingsJson) is JsonObject newSettings)
|
||||
{
|
||||
// Now, read the existing content from the file
|
||||
var oldContent = File.Exists(FilePath) ? File.ReadAllText(FilePath) : "{}";
|
||||
|
||||
// Is it valid JSON?
|
||||
if (JsonNode.Parse(oldContent) is JsonObject savedSettings)
|
||||
{
|
||||
foreach (var item in newSettings)
|
||||
{
|
||||
savedSettings[item.Key] = item.Value is not null ? item.Value.DeepClone() : null;
|
||||
}
|
||||
|
||||
var serialized = savedSettings.ToJsonString(_serializerOptions);
|
||||
File.WriteAllText(FilePath, serialized);
|
||||
}
|
||||
else
|
||||
{
|
||||
ExtensionHost.LogMessage(new LogMessage() { Message = "Failed to parse settings file as JsonObject." });
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ExtensionHost.LogMessage(new LogMessage() { Message = "Failed to parse settings file as JsonObject." });
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ExtensionHost.LogMessage(new LogMessage() { Message = ex.ToString() });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
// 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 Windows.System;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public static partial class KeyChordHelpers
|
||||
{
|
||||
public static KeyChord FromModifiers(
|
||||
bool ctrl = false,
|
||||
bool alt = false,
|
||||
bool shift = false,
|
||||
bool win = false,
|
||||
int vkey = 0,
|
||||
int scanCode = 0)
|
||||
{
|
||||
var modifiers = (ctrl ? VirtualKeyModifiers.Control : VirtualKeyModifiers.None)
|
||||
| (alt ? VirtualKeyModifiers.Menu : VirtualKeyModifiers.None)
|
||||
| (shift ? VirtualKeyModifiers.Shift : VirtualKeyModifiers.None)
|
||||
| (win ? VirtualKeyModifiers.Windows : VirtualKeyModifiers.None)
|
||||
;
|
||||
return new(modifiers, vkey, scanCode);
|
||||
}
|
||||
|
||||
public static KeyChord FromModifiers(
|
||||
bool ctrl = false,
|
||||
bool alt = false,
|
||||
bool shift = false,
|
||||
bool win = false,
|
||||
VirtualKey vkey = VirtualKey.None,
|
||||
int scanCode = 0)
|
||||
{
|
||||
return FromModifiers(ctrl, alt, shift, win, (int)vkey, scanCode);
|
||||
}
|
||||
|
||||
public static string FormatForDebug(KeyChord value)
|
||||
{
|
||||
var result = string.Empty;
|
||||
|
||||
if (value.Modifiers.HasFlag(VirtualKeyModifiers.Control))
|
||||
{
|
||||
result += "Ctrl+";
|
||||
}
|
||||
|
||||
if (value.Modifiers.HasFlag(VirtualKeyModifiers.Shift))
|
||||
{
|
||||
result += "Shift+";
|
||||
}
|
||||
|
||||
if (value.Modifiers.HasFlag(VirtualKeyModifiers.Menu))
|
||||
{
|
||||
result += "Alt+";
|
||||
}
|
||||
|
||||
result += (VirtualKey)value.Vkey;
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class ListHelpers
|
||||
{
|
||||
// Generate a score for a list item.
|
||||
public static int ScoreListItem(string query, ICommandItem listItem)
|
||||
{
|
||||
if (string.IsNullOrEmpty(query) || string.IsNullOrWhiteSpace(query))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(listItem.Title))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
var nameMatchScore = FuzzyStringMatcher.ScoreFuzzy(query, listItem.Title);
|
||||
|
||||
// var locNameMatch = StringMatcher.FuzzySearch(query, NameLocalized);
|
||||
var descriptionMatchScore = FuzzyStringMatcher.ScoreFuzzy(query, listItem.Subtitle);
|
||||
|
||||
// var executableNameMatch = StringMatcher.FuzzySearch(query, ExePath);
|
||||
// var locExecutableNameMatch = StringMatcher.FuzzySearch(query, ExecutableNameLocalized);
|
||||
// var lnkResolvedExecutableNameMatch = StringMatcher.FuzzySearch(query, LnkResolvedExecutableName);
|
||||
// var locLnkResolvedExecutableNameMatch = StringMatcher.FuzzySearch(query, LnkResolvedExecutableNameLocalized);
|
||||
// var score = new[] { nameMatch.Score, (descriptionMatch.Score - 4) / 2, executableNameMatch.Score }.Max();
|
||||
return new[] { nameMatchScore, (descriptionMatchScore - 4) / 2, 0 }.Max();
|
||||
}
|
||||
|
||||
public static IEnumerable<IListItem> FilterList(IEnumerable<IListItem> items, string query)
|
||||
{
|
||||
var scores = items
|
||||
.Select(li => new ScoredListItem() { ListItem = li, Score = ScoreListItem(query, li) })
|
||||
.Where(score => score.Score > 0)
|
||||
.OrderByDescending(score => score.Score);
|
||||
return scores
|
||||
.Select(score => score.ListItem);
|
||||
}
|
||||
|
||||
public static IEnumerable<T> FilterList<T>(IEnumerable<T> items, string query, Func<string, T, int> scoreFunction)
|
||||
{
|
||||
return FilterListWithScores<T>(items, query, scoreFunction)
|
||||
.Select(score => score.Item);
|
||||
}
|
||||
|
||||
public static IEnumerable<Scored<T>> FilterListWithScores<T>(IEnumerable<T> items, string query, Func<string, T, int> scoreFunction)
|
||||
{
|
||||
var scores = items
|
||||
.Select(li => new Scored<T>() { Item = li, Score = scoreFunction(query, li) })
|
||||
.Where(score => score.Score > 0)
|
||||
.OrderByDescending(score => score.Score);
|
||||
return scores;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Modifies the contents of `original` in-place, to match those of
|
||||
/// `newContents`. The canonical use being:
|
||||
/// ```cs
|
||||
/// ListHelpers.InPlaceUpdateList(FilteredItems, FilterList(ItemsToFilter, TextToFilterOn));
|
||||
/// ```
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Any type that can be compared for equality</typeparam>
|
||||
/// <param name="original">Collection to modify</param>
|
||||
/// <param name="newContents">The enumerable which `original` should match</param>
|
||||
public static void InPlaceUpdateList<T>(IList<T> original, IEnumerable<T> newContents)
|
||||
where T : class
|
||||
{
|
||||
InPlaceUpdateList(original, newContents, out _);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Modifies the contents of `original` in-place, to match those of
|
||||
/// `newContents`. The canonical use being:
|
||||
/// ```cs
|
||||
/// ListHelpers.InPlaceUpdateList(FilteredItems, FilterList(ItemsToFilter, TextToFilterOn));
|
||||
/// ```
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Any type that can be compared for equality</typeparam>
|
||||
/// <param name="original">Collection to modify</param>
|
||||
/// <param name="newContents">The enumerable which `original` should match</param>
|
||||
/// <param name="removedItems">List of items that were removed from the original collection</param>
|
||||
public static void InPlaceUpdateList<T>(IList<T> original, IEnumerable<T> newContents, out List<T> removedItems)
|
||||
where T : class
|
||||
{
|
||||
removedItems = [];
|
||||
|
||||
// we're not changing newContents - stash this so we don't re-evaluate it every time
|
||||
var numberOfNew = newContents.Count();
|
||||
|
||||
// Short circuit - new contents should just be empty
|
||||
if (numberOfNew == 0)
|
||||
{
|
||||
removedItems.AddRange(original);
|
||||
original.Clear();
|
||||
return;
|
||||
}
|
||||
|
||||
var i = 0;
|
||||
foreach (var newItem in newContents)
|
||||
{
|
||||
if (i >= original.Count)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
for (var j = i; j < original.Count; j++)
|
||||
{
|
||||
var og_2 = original[j];
|
||||
var areEqual_2 = og_2?.Equals(newItem) ?? false;
|
||||
if (areEqual_2)
|
||||
{
|
||||
for (var k = i; k < j; k++)
|
||||
{
|
||||
// This item from the original list was not in the new list. Remove it.
|
||||
removedItems.Add(original[i]);
|
||||
original.RemoveAt(i);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var og = original[i];
|
||||
var areEqual = og?.Equals(newItem) ?? false;
|
||||
|
||||
// Is this new item already in the list?
|
||||
if (areEqual)
|
||||
{
|
||||
// It is already in the list
|
||||
}
|
||||
else
|
||||
{
|
||||
// it isn't. Add it.
|
||||
original.Insert(i, newItem);
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
// Remove any extra trailing items from the destination
|
||||
while (original.Count > numberOfNew)
|
||||
{
|
||||
// RemoveAtEnd
|
||||
removedItems.Add(original[original.Count - 1]);
|
||||
original.RemoveAt(original.Count - 1);
|
||||
}
|
||||
|
||||
// Add any extra trailing items from the source
|
||||
if (original.Count < numberOfNew)
|
||||
{
|
||||
var remaining = newContents.Skip(original.Count);
|
||||
foreach (var item in remaining)
|
||||
{
|
||||
original.Add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct ScoredListItem
|
||||
{
|
||||
public int Score;
|
||||
public IListItem ListItem;
|
||||
}
|
||||
|
||||
public struct Scored<T>
|
||||
{
|
||||
public int Score;
|
||||
public T Item;
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class ListItem : CommandItem, IListItem
|
||||
{
|
||||
private ITag[] _tags = [];
|
||||
private IDetails? _details;
|
||||
|
||||
private string _section = string.Empty;
|
||||
private string _textToSuggest = string.Empty;
|
||||
|
||||
public virtual ITag[] Tags
|
||||
{
|
||||
get => _tags;
|
||||
set
|
||||
{
|
||||
_tags = value;
|
||||
OnPropertyChanged(nameof(Tags));
|
||||
}
|
||||
}
|
||||
|
||||
public virtual IDetails? Details
|
||||
{
|
||||
get => _details;
|
||||
set
|
||||
{
|
||||
_details = value;
|
||||
OnPropertyChanged(nameof(Details));
|
||||
}
|
||||
}
|
||||
|
||||
public virtual string Section
|
||||
{
|
||||
get => _section;
|
||||
set
|
||||
{
|
||||
_section = value;
|
||||
OnPropertyChanged(nameof(Section));
|
||||
}
|
||||
}
|
||||
|
||||
public virtual string TextToSuggest
|
||||
{
|
||||
get => _textToSuggest;
|
||||
set
|
||||
{
|
||||
_textToSuggest = value;
|
||||
OnPropertyChanged(nameof(TextToSuggest));
|
||||
}
|
||||
}
|
||||
|
||||
public ListItem(ICommand command)
|
||||
: base(command)
|
||||
{
|
||||
}
|
||||
|
||||
public ListItem(ICommandItem command)
|
||||
: base(command)
|
||||
{
|
||||
}
|
||||
|
||||
public ListItem()
|
||||
: base()
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
// 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 Windows.Foundation;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class ListPage : Page, IListPage
|
||||
{
|
||||
private string _placeholderText = string.Empty;
|
||||
private string _searchText = string.Empty;
|
||||
private bool _showDetails;
|
||||
private bool _hasMore;
|
||||
private IFilters? _filters;
|
||||
private IGridProperties? _gridProperties;
|
||||
private ICommandItem? _emptyContent;
|
||||
|
||||
public event TypedEventHandler<object, IItemsChangedEventArgs>? ItemsChanged;
|
||||
|
||||
public virtual string PlaceholderText
|
||||
{
|
||||
get => _placeholderText;
|
||||
set
|
||||
{
|
||||
_placeholderText = value;
|
||||
OnPropertyChanged(nameof(PlaceholderText));
|
||||
}
|
||||
}
|
||||
|
||||
public virtual string SearchText
|
||||
{
|
||||
get => _searchText;
|
||||
set
|
||||
{
|
||||
_searchText = value;
|
||||
OnPropertyChanged(nameof(SearchText));
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool ShowDetails
|
||||
{
|
||||
get => _showDetails;
|
||||
set
|
||||
{
|
||||
_showDetails = value;
|
||||
OnPropertyChanged(nameof(ShowDetails));
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool HasMoreItems
|
||||
{
|
||||
get => _hasMore;
|
||||
set
|
||||
{
|
||||
_hasMore = value;
|
||||
OnPropertyChanged(nameof(HasMoreItems));
|
||||
}
|
||||
}
|
||||
|
||||
public virtual IFilters? Filters
|
||||
{
|
||||
get => _filters;
|
||||
set
|
||||
{
|
||||
_filters = value;
|
||||
OnPropertyChanged(nameof(Filters));
|
||||
}
|
||||
}
|
||||
|
||||
public virtual IGridProperties? GridProperties
|
||||
{
|
||||
get => _gridProperties;
|
||||
set
|
||||
{
|
||||
_gridProperties = value;
|
||||
OnPropertyChanged(nameof(GridProperties));
|
||||
}
|
||||
}
|
||||
|
||||
public virtual ICommandItem? EmptyContent
|
||||
{
|
||||
get => _emptyContent;
|
||||
set
|
||||
{
|
||||
_emptyContent = value;
|
||||
OnPropertyChanged(nameof(EmptyContent));
|
||||
}
|
||||
}
|
||||
|
||||
public virtual IListItem[] GetItems() => [];
|
||||
|
||||
public virtual void LoadMore()
|
||||
{
|
||||
}
|
||||
|
||||
protected void RaiseItemsChanged(int totalItems = -1)
|
||||
{
|
||||
try
|
||||
{
|
||||
// TODO #181 - This is the same thing that BaseObservable has to deal with.
|
||||
ItemsChanged?.Invoke(this, new ItemsChangedEventArgs(totalItems));
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
protected void SetSearchNoUpdate(string newSearchText)
|
||||
{
|
||||
_searchText = newSearchText;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class LogMessage : BaseObservable, ILogMessage
|
||||
{
|
||||
private MessageState _messageState = MessageState.Info;
|
||||
|
||||
private string _message = string.Empty;
|
||||
|
||||
public string Message
|
||||
{
|
||||
get => _message;
|
||||
set
|
||||
{
|
||||
_message = value;
|
||||
OnPropertyChanged(nameof(Message));
|
||||
}
|
||||
}
|
||||
|
||||
public MessageState State
|
||||
{
|
||||
get => _messageState;
|
||||
set
|
||||
{
|
||||
_messageState = value;
|
||||
OnPropertyChanged(nameof(State));
|
||||
}
|
||||
}
|
||||
|
||||
public LogMessage(string message = "")
|
||||
{
|
||||
_message = message;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// 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;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace ManagedCsWin32;
|
||||
|
||||
internal static partial class Shell32
|
||||
{
|
||||
[LibraryImport("SHELL32.dll", EntryPoint = "ShellExecuteExW", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static partial bool ShellExecuteEx(ref SHELLEXECUTEINFOW lpExecInfo);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
public struct SHELLEXECUTEINFOW
|
||||
{
|
||||
public uint CbSize;
|
||||
public uint FMask;
|
||||
public IntPtr Hwnd;
|
||||
|
||||
public IntPtr LpVerb;
|
||||
public IntPtr LpFile;
|
||||
public IntPtr LpParameters;
|
||||
public IntPtr LpDirectory;
|
||||
public int Show;
|
||||
public IntPtr HInstApp;
|
||||
public IntPtr LpIDList;
|
||||
public IntPtr LpClass;
|
||||
public IntPtr HkeyClass;
|
||||
public uint DwHotKey;
|
||||
public IntPtr HIconOrMonitor;
|
||||
public IntPtr Process;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class MarkdownContent : BaseObservable, IMarkdownContent
|
||||
{
|
||||
public virtual string Body
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
OnPropertyChanged(nameof(Body));
|
||||
}
|
||||
}
|
||||
|
||||
= string.Empty;
|
||||
|
||||
public MarkdownContent()
|
||||
{
|
||||
}
|
||||
|
||||
public MarkdownContent(string body)
|
||||
{
|
||||
Body = body;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// 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;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class MatchOption
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets prefix of match char, use for highlight
|
||||
/// </summary>
|
||||
[Obsolete("this is never used")]
|
||||
public string Prefix { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets suffix of match char, use for highlight
|
||||
/// </summary>
|
||||
[Obsolete("this is never used")]
|
||||
public string Suffix { get; set; } = string.Empty;
|
||||
|
||||
public bool IgnoreCase { get; set; } = true;
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
// 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.Runtime.CompilerServices;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class MatchResult
|
||||
{
|
||||
public MatchResult(bool success, SearchPrecisionScore searchPrecision)
|
||||
{
|
||||
Success = success;
|
||||
SearchPrecision = searchPrecision;
|
||||
}
|
||||
|
||||
public MatchResult(bool success, SearchPrecisionScore searchPrecision, List<int> matchData, int rawScore)
|
||||
{
|
||||
Success = success;
|
||||
SearchPrecision = searchPrecision;
|
||||
MatchData = matchData;
|
||||
RawScore = rawScore;
|
||||
}
|
||||
|
||||
public bool Success { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the final score of the match result with search precision filters applied.
|
||||
/// </summary>
|
||||
public int Score { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The raw calculated search score without any search precision filtering applied.
|
||||
/// </summary>
|
||||
private int _rawScore;
|
||||
|
||||
public int RawScore
|
||||
{
|
||||
get => _rawScore;
|
||||
|
||||
set
|
||||
{
|
||||
_rawScore = value;
|
||||
Score = ScoreAfterSearchPrecisionFilter(_rawScore);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets matched data to highlight.
|
||||
/// </summary>
|
||||
public List<int> MatchData { get; private set; } = new();
|
||||
|
||||
public SearchPrecisionScore SearchPrecision { get; set; }
|
||||
|
||||
public bool IsSearchPrecisionScoreMet()
|
||||
{
|
||||
return IsSearchPrecisionScoreMet(_rawScore);
|
||||
}
|
||||
|
||||
private bool IsSearchPrecisionScoreMet(int rawScore)
|
||||
{
|
||||
return rawScore >= (int)SearchPrecision;
|
||||
}
|
||||
|
||||
private int ScoreAfterSearchPrecisionFilter(int rawScore)
|
||||
{
|
||||
return IsSearchPrecisionScoreMet(rawScore) ? rawScore : 0;
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class MediumGridLayout : BaseObservable, IMediumGridLayout
|
||||
{
|
||||
public virtual bool ShowTitle
|
||||
{
|
||||
get => field;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
OnPropertyChanged(nameof(ShowTitle));
|
||||
}
|
||||
}
|
||||
|
||||
= true;
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\..\..\..\Common.Dotnet.CsWinRT.props" />
|
||||
<Import Project="..\..\..\..\Common.Dotnet.AotCompatibility.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
|
||||
<WindowsSdkPackageVersion>10.0.26100.57</WindowsSdkPackageVersion>
|
||||
|
||||
<OutputPath>$(SolutionDir)$(Platform)\$(Configuration)\Microsoft.CommandPalette.Extensions.Toolkit</OutputPath>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<LangVersion>preview</LangVersion>
|
||||
<!-- MRT from windows app sdk will search for a pri file with the same name of the module before defaulting to resources.pri -->
|
||||
<ProjectPriFileName>Microsoft.CommandPalette.Extensions.Toolkit.pri</ProjectPriFileName>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>None</ResolveAssemblyWarnOrErrorOnTargetArchitectureMismatch>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(CIBuild)'=='true'">
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<DelaySign>true</DelaySign>
|
||||
<AssemblyOriginatorKeyFile>$(MSBuildThisFileDirectory)..\..\..\..\..\.pipelines\272MSSharedLibSN2048.snk</AssemblyOriginatorKeyFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<CsWinRTIncludes>Microsoft.CommandPalette.Extensions</CsWinRTIncludes>
|
||||
<CsWinRTGeneratedFilesDir>$(OutDir)</CsWinRTGeneratedFilesDir>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<ControlFlowGuard>Guard</ControlFlowGuard>
|
||||
<SpectreMitigation>Spectre</SpectreMitigation>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Windows.CsWin32">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="System.Drawing.Common" />
|
||||
<!-- This line forces the WebView2 version used by Windows App SDK to be the one we expect from Directory.Packages.props . -->
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Microsoft.CommandPalette.Extensions\Microsoft.CommandPalette.Extensions.vcxproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="$(SolutionDir)$(Platform)\$(Configuration)\Microsoft.CommandPalette.Extensions\Microsoft.CommandPalette.Extensions.winmd" Link="Microsoft.CommandPalette.Extensions.winmd" CopyToOutputDirectory="PreserveNewest" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="Properties\Resources.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Properties\Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<PublishTrimmed>True</PublishTrimmed>
|
||||
<PublishAot>True</PublishAot>
|
||||
|
||||
<!-- Suppress DynamicallyAccessedMemberTypes.PublicParameterlessConstructor in fallback code path of Windows SDK projection -->
|
||||
<WarningsNotAsErrors>IL2081;$(WarningsNotAsErrors)</WarningsNotAsErrors>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,102 @@
|
||||
// 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.Runtime.InteropServices;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
internal static partial class NativeMethods
|
||||
{
|
||||
[DllImport("shell32.dll", CharSet = CharSet.Unicode)]
|
||||
internal static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttributes, ref SHFILEINFO psfi, uint cbFileInfo, uint uFlags);
|
||||
|
||||
[DllImport("shell32.dll", CharSet = CharSet.Auto)]
|
||||
internal static extern IntPtr SHGetFileInfo(IntPtr pidl, uint dwFileAttributes, ref SHFILEINFO psfi, uint cbSizeFileInfo, uint uFlags);
|
||||
|
||||
[DllImport("shell32.dll")]
|
||||
internal static extern int SHParseDisplayName([MarshalAs(UnmanagedType.LPWStr)] string pszName, IntPtr pbc, out IntPtr ppidl, uint sfgaoIn, out uint psfgaoOut);
|
||||
|
||||
[DllImport("ole32.dll")]
|
||||
internal static extern void CoTaskMemFree(IntPtr pv);
|
||||
|
||||
[DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
|
||||
internal static extern int SHLoadIndirectString(string pszSource, System.Text.StringBuilder pszOutBuf, int cchOutBuf, IntPtr ppvReserved);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||
internal struct SHFILEINFO
|
||||
{
|
||||
#pragma warning disable SA1307 // Accessible fields should begin with upper-case letter
|
||||
public IntPtr hIcon;
|
||||
public int iIcon;
|
||||
public uint dwAttributes;
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
|
||||
public string szDisplayName;
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
|
||||
public string szTypeName;
|
||||
#pragma warning restore SA1307 // Accessible fields should begin with upper-case letter
|
||||
}
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
|
||||
internal static extern bool DestroyIcon(IntPtr hIcon);
|
||||
|
||||
[DllImport("Shell32.dll", CharSet = CharSet.Unicode)]
|
||||
internal static extern int SHGetImageList(int iImageList, ref Guid riid, out IntPtr ppv);
|
||||
|
||||
[DllImport("comctl32.dll", SetLastError = true)]
|
||||
internal static extern int ImageList_GetIcon(IntPtr himl, int i, int flags);
|
||||
|
||||
[LibraryImport("shlwapi.dll", StringMarshalling = StringMarshalling.Utf16, SetLastError = false)]
|
||||
internal static unsafe partial int AssocQueryStringW(
|
||||
AssocF flags,
|
||||
AssocStr str,
|
||||
string pszAssoc,
|
||||
string? pszExtra,
|
||||
char* pszOut,
|
||||
ref uint pcchOut);
|
||||
|
||||
// SHDefExtractIconW lets us ask for specific sizes (incl. 256)
|
||||
// nIconSize: HIWORD = large size, LOWORD = small size
|
||||
[LibraryImport("shell32.dll", StringMarshalling = StringMarshalling.Utf16, SetLastError = false)]
|
||||
internal static partial int SHDefExtractIconW(
|
||||
string pszIconFile,
|
||||
int iIndex,
|
||||
uint uFlags,
|
||||
out nint phiconLarge,
|
||||
out nint phiconSmall,
|
||||
int nIconSize);
|
||||
|
||||
[Flags]
|
||||
public enum AssocF : uint
|
||||
{
|
||||
None = 0,
|
||||
IsProtocol = 0x00001000,
|
||||
}
|
||||
|
||||
public enum AssocStr
|
||||
{
|
||||
Command = 1,
|
||||
Executable,
|
||||
FriendlyDocName,
|
||||
FriendlyAppName,
|
||||
NoOpen,
|
||||
ShellNewValue,
|
||||
DDECommand,
|
||||
DDEIfExec,
|
||||
DDEApplication,
|
||||
DDETopic,
|
||||
InfoTip,
|
||||
QuickTip,
|
||||
TileInfo,
|
||||
ContentType,
|
||||
DefaultIcon,
|
||||
ShellExtension,
|
||||
DropTarget,
|
||||
DelegateExecute,
|
||||
SupportedUriProtocols,
|
||||
ProgId,
|
||||
AppId,
|
||||
AppPublisher,
|
||||
AppIconReference, // sometimes present, but DefaultIcon is most common
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
CoImpersonateClient
|
||||
GetCurrentThread
|
||||
OpenThreadToken
|
||||
GetPackageFamilyNameFromToken
|
||||
CoRevertToSelf
|
||||
SHGetKnownFolderPath
|
||||
KNOWN_FOLDER_FLAG
|
||||
GetCurrentPackageId
|
||||
|
||||
SHOW_WINDOW_CMD
|
||||
SEE_MASK_INVOKEIDLIST
|
||||
@@ -0,0 +1,42 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class Page : Command, IPage
|
||||
{
|
||||
private bool _loading;
|
||||
private string _title = string.Empty;
|
||||
private OptionalColor _accentColor;
|
||||
|
||||
public virtual bool IsLoading
|
||||
{
|
||||
get => _loading;
|
||||
set
|
||||
{
|
||||
_loading = value;
|
||||
OnPropertyChanged(nameof(IsLoading));
|
||||
}
|
||||
}
|
||||
|
||||
public virtual string Title
|
||||
{
|
||||
get => _title;
|
||||
set
|
||||
{
|
||||
_title = value;
|
||||
OnPropertyChanged(nameof(Title));
|
||||
}
|
||||
}
|
||||
|
||||
public virtual OptionalColor AccentColor
|
||||
{
|
||||
get => _accentColor;
|
||||
set
|
||||
{
|
||||
_accentColor = value;
|
||||
OnPropertyChanged(nameof(AccentColor));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class ProgressState : BaseObservable, IProgressState
|
||||
{
|
||||
private bool _isIndeterminate;
|
||||
|
||||
private uint _progressPercent;
|
||||
|
||||
public virtual bool IsIndeterminate
|
||||
{
|
||||
get => _isIndeterminate;
|
||||
set
|
||||
{
|
||||
_isIndeterminate = value;
|
||||
OnPropertyChanged(nameof(IsIndeterminate));
|
||||
}
|
||||
}
|
||||
|
||||
public virtual uint ProgressPercent
|
||||
{
|
||||
get => _progressPercent;
|
||||
set
|
||||
{
|
||||
_progressPercent = value;
|
||||
OnPropertyChanged(nameof(ProgressPercent));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// 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 Windows.Foundation;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class PropChangedEventArgs : IPropChangedEventArgs
|
||||
{
|
||||
public string PropertyName { get; private set; }
|
||||
|
||||
public PropChangedEventArgs(string propertyName)
|
||||
{
|
||||
PropertyName = propertyName;
|
||||
}
|
||||
}
|
||||
180
src/modules/Deux/SDK/Microsoft.CommandPalette.Extensions.Toolkit/Properties/Resources.Designer.cs
generated
Normal file
180
src/modules/Deux/SDK/Microsoft.CommandPalette.Extensions.Toolkit/Properties/Resources.Designer.cs
generated
Normal file
@@ -0,0 +1,180 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit.Properties {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Resources {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Resources() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.CommandPalette.Extensions.Toolkit.Properties.Resources", typeof(Resources).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Invoke.
|
||||
/// </summary>
|
||||
internal static string AnonymousCommand_Invoke {
|
||||
get {
|
||||
return ResourceManager.GetString("AnonymousCommand_Invoke", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Copy failed ({0}). Please try again..
|
||||
/// </summary>
|
||||
internal static string copy_failed {
|
||||
get {
|
||||
return ResourceManager.GetString("copy_failed", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Copy path.
|
||||
/// </summary>
|
||||
internal static string CopyPathTextCommand_Name {
|
||||
get {
|
||||
return ResourceManager.GetString("CopyPathTextCommand_Name", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Copied path to clipboard.
|
||||
/// </summary>
|
||||
internal static string CopyPathTextCommand_Result {
|
||||
get {
|
||||
return ResourceManager.GetString("CopyPathTextCommand_Result", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Copied to clipboard.
|
||||
/// </summary>
|
||||
internal static string CopyTextCommand_CopiedToClipboard {
|
||||
get {
|
||||
return ResourceManager.GetString("CopyTextCommand_CopiedToClipboard", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Copy.
|
||||
/// </summary>
|
||||
internal static string CopyTextCommand_Copy {
|
||||
get {
|
||||
return ResourceManager.GetString("CopyTextCommand_Copy", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Open.
|
||||
/// </summary>
|
||||
internal static string OpenFileCommand_Name {
|
||||
get {
|
||||
return ResourceManager.GetString("OpenFileCommand_Name", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Open path in console.
|
||||
/// </summary>
|
||||
internal static string OpenInConsoleCommand_Name {
|
||||
get {
|
||||
return ResourceManager.GetString("OpenInConsoleCommand_Name", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Properties.
|
||||
/// </summary>
|
||||
internal static string OpenPropertiesCommand_Name {
|
||||
get {
|
||||
return ResourceManager.GetString("OpenPropertiesCommand_Name", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Open.
|
||||
/// </summary>
|
||||
internal static string OpenUrlCommand_Open {
|
||||
get {
|
||||
return ResourceManager.GetString("OpenUrlCommand_Open", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Open with.
|
||||
/// </summary>
|
||||
internal static string OpenWithCommand_Name {
|
||||
get {
|
||||
return ResourceManager.GetString("OpenWithCommand_Name", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Settings.
|
||||
/// </summary>
|
||||
internal static string Settings {
|
||||
get {
|
||||
return ResourceManager.GetString("Settings", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Show in folder.
|
||||
/// </summary>
|
||||
internal static string ShowFileInFolderCommand_ShowInFolder {
|
||||
get {
|
||||
return ResourceManager.GetString("ShowFileInFolderCommand_ShowInFolder", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="AnonymousCommand_Invoke" xml:space="preserve">
|
||||
<value>Invoke</value>
|
||||
</data>
|
||||
<data name="CopyTextCommand_Copy" xml:space="preserve">
|
||||
<value>Copy</value>
|
||||
</data>
|
||||
<data name="CopyTextCommand_CopiedToClipboard" xml:space="preserve">
|
||||
<value>Copied to clipboard</value>
|
||||
</data>
|
||||
<data name="CopyPathTextCommand_Name" xml:space="preserve">
|
||||
<value>Copy path</value>
|
||||
</data>
|
||||
<data name="CopyPathTextCommand_Result" xml:space="preserve">
|
||||
<value>Copied path to clipboard</value>
|
||||
</data>
|
||||
<data name="OpenUrlCommand_Open" xml:space="preserve">
|
||||
<value>Open</value>
|
||||
</data>
|
||||
<data name="Settings" xml:space="preserve">
|
||||
<value>Settings</value>
|
||||
</data>
|
||||
<data name="ShowFileInFolderCommand_ShowInFolder" xml:space="preserve">
|
||||
<value>Show in folder</value>
|
||||
</data>
|
||||
<data name="OpenInConsoleCommand_Name" xml:space="preserve">
|
||||
<value>Open path in console</value>
|
||||
</data>
|
||||
<data name="OpenPropertiesCommand_Name" xml:space="preserve">
|
||||
<value>Properties</value>
|
||||
</data>
|
||||
<data name="OpenFileCommand_Name" xml:space="preserve">
|
||||
<value>Open</value>
|
||||
</data>
|
||||
<data name="OpenWithCommand_Name" xml:space="preserve">
|
||||
<value>Open with</value>
|
||||
</data>
|
||||
<data name="copy_failed" xml:space="preserve">
|
||||
<value>Copy failed ({0}). Please try again.</value>
|
||||
<comment>{0} is the error message</comment>
|
||||
</data>
|
||||
</root>
|
||||
@@ -0,0 +1,12 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public enum SearchPrecisionScore
|
||||
{
|
||||
Regular = 50,
|
||||
Low = 20,
|
||||
None = 0,
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class Separator : ISeparatorContextItem, ISeparatorFilterItem
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
// 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.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public abstract class Setting<T> : ISettingsForm
|
||||
{
|
||||
private static readonly JsonSerializerOptions _jsonSerializerOptions = new() { WriteIndented = true };
|
||||
|
||||
public T? Value { get; set; }
|
||||
|
||||
public string Key { get; }
|
||||
|
||||
public bool IsRequired { get; set; }
|
||||
|
||||
public string ErrorMessage { get; set; } = string.Empty;
|
||||
|
||||
public string Label { get; set; } = string.Empty;
|
||||
|
||||
public string Description { get; set; } = string.Empty;
|
||||
|
||||
protected Setting()
|
||||
{
|
||||
Value = default;
|
||||
Key = string.Empty;
|
||||
}
|
||||
|
||||
public Setting(string key, T defaultValue)
|
||||
{
|
||||
Key = key;
|
||||
Value = defaultValue;
|
||||
}
|
||||
|
||||
public Setting(string key, string label, string description, T defaultValue)
|
||||
{
|
||||
Key = key;
|
||||
Value = defaultValue;
|
||||
Label = label;
|
||||
Description = description;
|
||||
}
|
||||
|
||||
public abstract Dictionary<string, object> ToDictionary();
|
||||
|
||||
public string ToDataIdentifier() => $"\"{Key}\": \"{Key}\"";
|
||||
|
||||
public string ToForm()
|
||||
{
|
||||
var bodyJson = JsonSerializer.Serialize(ToDictionary(), JsonSerializationContext.Default.Dictionary);
|
||||
var dataJson = $"\"{Key}\": \"{Key}\"";
|
||||
|
||||
var json = $$"""
|
||||
{
|
||||
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
|
||||
"type": "AdaptiveCard",
|
||||
"version": "1.5",
|
||||
"body": [
|
||||
{{bodyJson}}
|
||||
],
|
||||
"actions": [
|
||||
{
|
||||
"type": "Action.Submit",
|
||||
"title": "Save",
|
||||
"data": {
|
||||
{{dataJson}}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
""";
|
||||
return json;
|
||||
}
|
||||
|
||||
public abstract void Update(JsonObject payload);
|
||||
|
||||
public abstract string ToState();
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
// 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.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using Windows.Foundation;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public sealed partial class Settings : ICommandSettings
|
||||
{
|
||||
private readonly Dictionary<string, object> _settings = [];
|
||||
private static readonly JsonSerializerOptions _jsonSerializerOptions = new() { WriteIndented = true };
|
||||
|
||||
public event TypedEventHandler<object, Settings>? SettingsChanged;
|
||||
|
||||
public void Add<T>(Setting<T> s) => _settings.Add(s.Key, s);
|
||||
|
||||
public T? GetSetting<T>(string key) => _settings[key] is Setting<T> s ? s.Value : default;
|
||||
|
||||
public bool TryGetSetting<T>(string key, out T? val)
|
||||
{
|
||||
object? o;
|
||||
if (_settings.TryGetValue(key, out o))
|
||||
{
|
||||
if (o is Setting<T> s)
|
||||
{
|
||||
val = s.Value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
val = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
internal string ToFormJson()
|
||||
{
|
||||
var settings = _settings
|
||||
.Values
|
||||
.Where(s => s is ISettingsForm)
|
||||
.Select(s => s as ISettingsForm)
|
||||
.Where(s => s is not null)
|
||||
.Select(s => s!);
|
||||
|
||||
var bodies = string.Join(",", settings
|
||||
.Select(s => JsonSerializer.Serialize(s.ToDictionary(), JsonSerializationContext.Default.Dictionary)));
|
||||
|
||||
var datas = string.Join(",", settings.Select(s => s.ToDataIdentifier()));
|
||||
|
||||
var json = $$"""
|
||||
{
|
||||
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
|
||||
"type": "AdaptiveCard",
|
||||
"version": "1.5",
|
||||
"body": [
|
||||
{{bodies}}
|
||||
],
|
||||
"actions": [
|
||||
{
|
||||
"type": "Action.Submit",
|
||||
"title": "Save",
|
||||
"data": {
|
||||
{{datas}}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
""";
|
||||
return json;
|
||||
}
|
||||
|
||||
public string ToJson()
|
||||
{
|
||||
var settings = _settings
|
||||
.Values
|
||||
.Where(s => s is ISettingsForm)
|
||||
.Select(s => s as ISettingsForm)
|
||||
.Where(s => s is not null)
|
||||
.Select(s => s!);
|
||||
var content = string.Join(",\n", settings.Select(s => s.ToState()));
|
||||
return $"{{\n{content}\n}}";
|
||||
}
|
||||
|
||||
public void Update(string data)
|
||||
{
|
||||
var formInput = JsonNode.Parse(data)?.AsObject();
|
||||
if (formInput is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var key in _settings.Keys)
|
||||
{
|
||||
var value = _settings[key];
|
||||
if (value is ISettingsForm f)
|
||||
{
|
||||
f.Update(formInput);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void RaiseSettingsChanged()
|
||||
{
|
||||
var handlers = SettingsChanged;
|
||||
handlers?.Invoke(this, this);
|
||||
}
|
||||
|
||||
private sealed partial class SettingsContentPage : ContentPage
|
||||
{
|
||||
private readonly Settings _settings;
|
||||
|
||||
public override IContent[] GetContent() => _settings.ToContent();
|
||||
|
||||
public SettingsContentPage(Settings settings)
|
||||
{
|
||||
_settings = settings;
|
||||
Name = Properties.Resources.Settings;
|
||||
Icon = new IconInfo("\uE713"); // Settings icon
|
||||
|
||||
// When our settings change, make sure to let CmdPal know to
|
||||
// retrieve the new forms
|
||||
_settings.SettingsChanged += (s, e) => RaiseItemsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public IContentPage SettingsPage => new SettingsContentPage(this);
|
||||
|
||||
public IContent[] ToContent() => [new SettingsForm(this)];
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// 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.Text.Json.Nodes;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class SettingsForm : FormContent
|
||||
{
|
||||
private readonly Settings _settings;
|
||||
|
||||
internal SettingsForm(Settings settings)
|
||||
{
|
||||
_settings = settings;
|
||||
TemplateJson = _settings.ToFormJson();
|
||||
}
|
||||
|
||||
public override ICommandResult SubmitForm(string inputs, string data)
|
||||
{
|
||||
var formInput = JsonNode.Parse(inputs)?.AsObject();
|
||||
if (formInput is null)
|
||||
{
|
||||
return CommandResult.KeepOpen();
|
||||
}
|
||||
|
||||
// Re-render the current value of the settings to a card. The
|
||||
// SettingsContentPage will raise an ItemsChanged in its own
|
||||
// SettingsChange handler, so we need to be prepared to return the
|
||||
// current settings value.
|
||||
TemplateJson = _settings.ToFormJson();
|
||||
|
||||
_settings.Update(inputs);
|
||||
_settings.RaiseSettingsChanged();
|
||||
|
||||
return CommandResult.GoHome();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,281 @@
|
||||
// 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.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public static class ShellHelpers
|
||||
{
|
||||
/// <summary>
|
||||
/// These are the executable file extensions that Windows Shell recognizes. Unlike CMD/PowerShell,
|
||||
/// Shell does not use PATHEXT, but has a magic fixed list.
|
||||
/// </summary>
|
||||
public static string[] ExecutableExtensions { get; } = [".PIF", ".COM", ".EXE", ".BAT", ".CMD"];
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified file name represents an executable file
|
||||
/// by examining its extension against the known list of Windows Shell
|
||||
/// executable extensions (a fixed list that does not honor PATHEXT).
|
||||
/// </summary>
|
||||
/// <param name="fileName">The file name (with or without path) whose extension will be evaluated.</param>
|
||||
/// <returns>
|
||||
/// True if the file name has an extension that matches one of the recognized executable
|
||||
/// extensions; otherwise, false. Returns false for null, empty, or whitespace input.
|
||||
/// </returns>
|
||||
public static bool IsExecutableFile(string fileName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(fileName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var fileExtension = Path.GetExtension(fileName);
|
||||
return IsExecutableExtension(fileExtension);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the provided file extension (including the leading dot)
|
||||
/// is one of the Windows Shell recognized executable extensions.
|
||||
/// </summary>
|
||||
/// <param name="fileExtension">The file extension to test. Should include the leading dot (e.g. ".exe").</param>
|
||||
/// <returns>
|
||||
/// True if the extension matches (case-insensitive) one of the known executable
|
||||
/// extensions; false if it does not match or if the input is null/whitespace.
|
||||
/// </returns>
|
||||
public static bool IsExecutableExtension(string fileExtension)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(fileExtension))
|
||||
{
|
||||
// Shell won't execute app with a filename without an extension
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (var extension in ExecutableExtensions)
|
||||
{
|
||||
if (string.Equals(fileExtension, extension, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool OpenCommandInShell(string? path, string? pattern, string? arguments, string? workingDir = null, ShellRunAsType runAs = ShellRunAsType.None, bool runWithHiddenWindow = false)
|
||||
{
|
||||
if (string.IsNullOrEmpty(pattern))
|
||||
{
|
||||
// Log.Warn($"Trying to run OpenCommandInShell with an empty pattern. The default browser definition might have issues. Path: '${path ?? string.Empty}' ; Arguments: '${arguments ?? string.Empty}' ; Working Directory: '${workingDir ?? string.Empty}'", typeof(ShellHelpers));
|
||||
}
|
||||
else if (pattern.Contains("%1", StringComparison.Ordinal))
|
||||
{
|
||||
arguments = pattern.Replace("%1", arguments);
|
||||
}
|
||||
|
||||
return OpenInShell(path, arguments, workingDir, runAs, runWithHiddenWindow);
|
||||
}
|
||||
|
||||
public static bool OpenInShell(string? path, string? arguments = null, string? workingDir = null, ShellRunAsType runAs = ShellRunAsType.None, bool runWithHiddenWindow = false)
|
||||
{
|
||||
using var process = new Process();
|
||||
process.StartInfo.FileName = path;
|
||||
process.StartInfo.WorkingDirectory = string.IsNullOrWhiteSpace(workingDir) ? string.Empty : workingDir;
|
||||
process.StartInfo.Arguments = string.IsNullOrWhiteSpace(arguments) ? string.Empty : arguments;
|
||||
process.StartInfo.WindowStyle = runWithHiddenWindow ? ProcessWindowStyle.Hidden : ProcessWindowStyle.Normal;
|
||||
process.StartInfo.UseShellExecute = true;
|
||||
|
||||
if (runAs == ShellRunAsType.Administrator)
|
||||
{
|
||||
process.StartInfo.Verb = "RunAs";
|
||||
}
|
||||
else if (runAs == ShellRunAsType.OtherUser)
|
||||
{
|
||||
process.StartInfo.Verb = "RunAsUser";
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
process.Start();
|
||||
return true;
|
||||
}
|
||||
catch (Win32Exception)
|
||||
{
|
||||
// Log.Exception($"Unable to open {path}: {ex.Message}", ex, MethodBase.GetCurrentMethod().DeclaringType);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public enum ShellRunAsType
|
||||
{
|
||||
None,
|
||||
Administrator,
|
||||
OtherUser,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses the input string to extract the executable and its arguments.
|
||||
/// </summary>
|
||||
public static void ParseExecutableAndArgs(string input, out string executable, out string arguments)
|
||||
{
|
||||
input = input.Trim();
|
||||
executable = string.Empty;
|
||||
arguments = string.Empty;
|
||||
|
||||
if (string.IsNullOrEmpty(input))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (input.StartsWith("\"", System.StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
// Find the closing quote
|
||||
var closingQuoteIndex = input.IndexOf('\"', 1);
|
||||
if (closingQuoteIndex > 0)
|
||||
{
|
||||
executable = input.Substring(1, closingQuoteIndex - 1);
|
||||
if (closingQuoteIndex + 1 < input.Length)
|
||||
{
|
||||
arguments = input.Substring(closingQuoteIndex + 1).TrimStart();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Executable ends at first space
|
||||
var firstSpaceIndex = input.IndexOf(' ');
|
||||
if (firstSpaceIndex > 0)
|
||||
{
|
||||
executable = input.Substring(0, firstSpaceIndex);
|
||||
arguments = input[(firstSpaceIndex + 1)..].TrimStart();
|
||||
}
|
||||
else
|
||||
{
|
||||
executable = input;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a file exists somewhere in the PATH.
|
||||
/// If it exists, returns the full path to the file in the out parameter.
|
||||
/// If it does not exist, returns false and the out parameter is set to an empty string.
|
||||
/// <param name="filename">The name of the file to check.</param>
|
||||
/// <param name="fullPath">The full path to the file if it exists; otherwise an empty string.</param>
|
||||
/// <param name="token">An optional cancellation token to cancel the operation.</param>
|
||||
/// <returns>True if the file exists in the PATH; otherwise false.</returns>
|
||||
/// </summary>
|
||||
public static bool FileExistInPath(string filename, out string fullPath, CancellationToken? token = null)
|
||||
{
|
||||
fullPath = string.Empty;
|
||||
|
||||
if (File.Exists(filename))
|
||||
{
|
||||
token?.ThrowIfCancellationRequested();
|
||||
fullPath = Path.GetFullPath(filename);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
var values = Environment.GetEnvironmentVariable("PATH");
|
||||
if (values is not null)
|
||||
{
|
||||
foreach (var path in values.Split(Path.PathSeparator))
|
||||
{
|
||||
var path1 = Path.Combine(path, filename);
|
||||
if (File.Exists(path1))
|
||||
{
|
||||
fullPath = Path.GetFullPath(path1);
|
||||
return true;
|
||||
}
|
||||
|
||||
token?.ThrowIfCancellationRequested();
|
||||
|
||||
var path2 = Path.Combine(path, filename + ".exe");
|
||||
if (File.Exists(path2))
|
||||
{
|
||||
fullPath = Path.GetFullPath(path2);
|
||||
return true;
|
||||
}
|
||||
|
||||
token?.ThrowIfCancellationRequested();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TryResolveFromAppPaths(string name, [NotNullWhen(true)] out string? fullPath)
|
||||
{
|
||||
try
|
||||
{
|
||||
fullPath = TryHiveView(RegistryHive.CurrentUser, RegistryView.Registry64) ??
|
||||
TryHiveView(RegistryHive.CurrentUser, RegistryView.Registry32) ??
|
||||
TryHiveView(RegistryHive.LocalMachine, RegistryView.Registry64) ??
|
||||
TryHiveView(RegistryHive.LocalMachine, RegistryView.Registry32) ?? string.Empty;
|
||||
|
||||
return !string.IsNullOrEmpty(fullPath);
|
||||
|
||||
string? TryHiveView(RegistryHive hive, RegistryView view)
|
||||
{
|
||||
using var baseKey = RegistryKey.OpenBaseKey(hive, view);
|
||||
using var k1 = baseKey.OpenSubKey($@"SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\{name}.exe");
|
||||
var val = (k1?.GetValue(null) as string)?.Trim('"');
|
||||
if (!string.IsNullOrEmpty(val))
|
||||
{
|
||||
return val;
|
||||
}
|
||||
|
||||
// Some vendors create keys without .exe in the subkey name; check that too.
|
||||
using var k2 = baseKey.OpenSubKey($@"SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\{name}");
|
||||
return (k2?.GetValue(null) as string)?.Trim('"');
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
fullPath = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Mimics Windows Shell behavior to resolve an executable name to a full path.
|
||||
/// </summary>
|
||||
/// <param name="name"></param>
|
||||
/// <param name="fullPath"></param>
|
||||
/// <returns></returns>
|
||||
public static bool TryResolveExecutableAsShell(string name, out string fullPath)
|
||||
{
|
||||
// First check if we can find the file in the registry
|
||||
if (TryResolveFromAppPaths(name, out var path))
|
||||
{
|
||||
fullPath = path;
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the name does not have an extension, try adding common executable extensions
|
||||
// this order mimics Windows Shell behavior
|
||||
// Note: HasExtension check follows Shell behavior, but differs from the
|
||||
// Start Menu search results, which will offer file name with extensions + ".exe"
|
||||
var nameHasExtension = Path.HasExtension(name);
|
||||
if (!nameHasExtension)
|
||||
{
|
||||
foreach (var ext in ExecutableExtensions)
|
||||
{
|
||||
var nameWithExt = name + ext;
|
||||
if (FileExistInPath(nameWithExt, out fullPath))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fullPath = string.Empty;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class SmallGridLayout : BaseObservable, ISmallGridLayout
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class StatusMessage : BaseObservable, IStatusMessage
|
||||
{
|
||||
public virtual string Message
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
OnPropertyChanged(nameof(Message));
|
||||
}
|
||||
}
|
||||
|
||||
= string.Empty;
|
||||
|
||||
public virtual MessageState State
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
OnPropertyChanged(nameof(State));
|
||||
}
|
||||
}
|
||||
|
||||
= MessageState.Info;
|
||||
|
||||
public virtual IProgressState? Progress
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
OnPropertyChanged(nameof(Progress));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class Tag : BaseObservable, ITag
|
||||
{
|
||||
private OptionalColor _foreground;
|
||||
private OptionalColor _background;
|
||||
private string _text = string.Empty;
|
||||
|
||||
public virtual OptionalColor Foreground
|
||||
{
|
||||
get => _foreground;
|
||||
set
|
||||
{
|
||||
_foreground = value;
|
||||
OnPropertyChanged(nameof(Foreground));
|
||||
}
|
||||
}
|
||||
|
||||
public virtual OptionalColor Background
|
||||
{
|
||||
get => _background;
|
||||
set
|
||||
{
|
||||
_background = value;
|
||||
OnPropertyChanged(nameof(Background));
|
||||
}
|
||||
}
|
||||
|
||||
public virtual IIconInfo Icon
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
OnPropertyChanged(nameof(Icon));
|
||||
}
|
||||
}
|
||||
|
||||
= new IconInfo();
|
||||
|
||||
public virtual string Text
|
||||
{
|
||||
get => _text;
|
||||
set
|
||||
{
|
||||
_text = value;
|
||||
OnPropertyChanged(nameof(Text));
|
||||
}
|
||||
}
|
||||
|
||||
public virtual string ToolTip
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
OnPropertyChanged(nameof(ToolTip));
|
||||
}
|
||||
}
|
||||
|
||||
= string.Empty;
|
||||
|
||||
public Tag()
|
||||
{
|
||||
}
|
||||
|
||||
public Tag(string text)
|
||||
{
|
||||
_text = text;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
// 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.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class TextSetting : Setting<string>
|
||||
{
|
||||
public bool Multiline { get; set; }
|
||||
|
||||
public string Placeholder { get; set; } = string.Empty;
|
||||
|
||||
private TextSetting()
|
||||
: base()
|
||||
{
|
||||
Value = string.Empty;
|
||||
}
|
||||
|
||||
public TextSetting(string key, string defaultValue)
|
||||
: base(key, defaultValue)
|
||||
{
|
||||
}
|
||||
|
||||
public TextSetting(string key, string label, string description, string defaultValue)
|
||||
: base(key, label, description, defaultValue)
|
||||
{
|
||||
}
|
||||
|
||||
public override Dictionary<string, object> ToDictionary()
|
||||
{
|
||||
return new Dictionary<string, object>
|
||||
{
|
||||
{ "type", "Input.Text" },
|
||||
{ "title", Label },
|
||||
{ "id", Key },
|
||||
{ "label", Description },
|
||||
{ "value", Value ?? string.Empty },
|
||||
{ "isRequired", IsRequired },
|
||||
{ "errorMessage", ErrorMessage },
|
||||
{ "isMultiline", Multiline },
|
||||
{ "placeholder", Placeholder },
|
||||
};
|
||||
}
|
||||
|
||||
public static TextSetting LoadFromJson(JsonObject jsonObject) => new() { Value = jsonObject["value"]?.GetValue<string>() ?? string.Empty };
|
||||
|
||||
public override void Update(JsonObject payload)
|
||||
{
|
||||
// If the key doesn't exist in the payload, don't do anything
|
||||
if (payload[Key] is not null)
|
||||
{
|
||||
Value = payload[Key]?.GetValue<string>();
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToState() => $"\"{Key}\": {JsonSerializer.Serialize(Value, JsonSerializationContext.Default.String)}";
|
||||
}
|
||||
@@ -0,0 +1,435 @@
|
||||
// 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.Drawing;
|
||||
using System.Globalization;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using Windows.Storage;
|
||||
using Windows.Storage.FileProperties;
|
||||
using Windows.Storage.Streams;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public static class ThumbnailHelper
|
||||
{
|
||||
private static readonly string[] ImageExtensions =
|
||||
[
|
||||
".png",
|
||||
".jpg",
|
||||
".jpeg",
|
||||
".gif",
|
||||
".bmp",
|
||||
".tiff",
|
||||
".ico",
|
||||
];
|
||||
|
||||
public static async Task<IRandomAccessStream?> GetThumbnail(string path, bool jumbo = false)
|
||||
{
|
||||
var extension = Path.GetExtension(path).ToLower(CultureInfo.InvariantCulture);
|
||||
var isImage = ImageExtensions.Contains(extension);
|
||||
if (isImage)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await GetImageThumbnailAsync(path, jumbo);
|
||||
if (result is not null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignore and fall back to icon
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return await GetFileIconStream(path, jumbo);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignore and return null
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// these are windows constants and mangling them is goofy
|
||||
#pragma warning disable SA1310 // Field names should not contain underscore
|
||||
#pragma warning disable SA1306 // Field names should begin with lower-case letter
|
||||
private const uint SHGFI_ICON = 0x000000100;
|
||||
private const uint SHGFI_LARGEICON = 0x000000000;
|
||||
private const uint SHGFI_SHELLICONSIZE = 0x000000004;
|
||||
private const uint SHGFI_SYSICONINDEX = 0x000004000;
|
||||
private const uint SHGFI_PIDL = 0x000000008;
|
||||
private const int SHIL_JUMBO = 4;
|
||||
private const int ILD_TRANSPARENT = 1;
|
||||
#pragma warning restore SA1306 // Field names should begin with lower-case letter
|
||||
#pragma warning restore SA1310 // Field names should not contain underscore
|
||||
|
||||
// This will call DestroyIcon on the hIcon passed in.
|
||||
// Duplicate it if you need it again after this.
|
||||
private static MemoryStream GetMemoryStreamFromIcon(IntPtr hIcon)
|
||||
{
|
||||
var memoryStream = new MemoryStream();
|
||||
|
||||
// Ensure disposing the icon before freeing the handle
|
||||
using (var icon = Icon.FromHandle(hIcon))
|
||||
{
|
||||
icon.ToBitmap().Save(memoryStream, System.Drawing.Imaging.ImageFormat.Png);
|
||||
}
|
||||
|
||||
// Clean up the unmanaged handle without risking a use-after-free.
|
||||
NativeMethods.DestroyIcon(hIcon);
|
||||
|
||||
memoryStream.Position = 0;
|
||||
return memoryStream;
|
||||
}
|
||||
|
||||
private static async Task<IRandomAccessStream?> GetFileIconStream(string filePath, bool jumbo)
|
||||
{
|
||||
return await TryExtractUsingPIDL(filePath, jumbo)
|
||||
?? await GetFileIconStreamUsingFilePath(filePath, jumbo);
|
||||
}
|
||||
|
||||
private static async Task<IRandomAccessStream?> TryExtractUsingPIDL(string shellPath, bool jumbo)
|
||||
{
|
||||
IntPtr pidl = 0;
|
||||
try
|
||||
{
|
||||
var hr = NativeMethods.SHParseDisplayName(shellPath, IntPtr.Zero, out pidl, 0, out _);
|
||||
if (hr != 0 || pidl == IntPtr.Zero)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
nint hIcon = 0;
|
||||
if (jumbo)
|
||||
{
|
||||
hIcon = GetLargestIcon(pidl);
|
||||
}
|
||||
|
||||
if (hIcon == 0)
|
||||
{
|
||||
var shinfo = default(NativeMethods.SHFILEINFO);
|
||||
var fileInfoResult = NativeMethods.SHGetFileInfo(pidl, 0, ref shinfo, (uint)Marshal.SizeOf(shinfo), SHGFI_ICON | SHGFI_SHELLICONSIZE | SHGFI_LARGEICON | SHGFI_PIDL);
|
||||
if (fileInfoResult != IntPtr.Zero && shinfo.hIcon != IntPtr.Zero)
|
||||
{
|
||||
hIcon = shinfo.hIcon;
|
||||
}
|
||||
}
|
||||
|
||||
if (hIcon == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return await FromHIconToStream(hIcon);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (pidl != IntPtr.Zero)
|
||||
{
|
||||
NativeMethods.CoTaskMemFree(pidl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<IRandomAccessStream?> GetFileIconStreamUsingFilePath(string filePath, bool jumbo)
|
||||
{
|
||||
nint hIcon = 0;
|
||||
|
||||
// If requested, look up the Jumbo icon
|
||||
if (jumbo)
|
||||
{
|
||||
hIcon = GetLargestIcon(filePath);
|
||||
}
|
||||
|
||||
// If we didn't want the JUMBO icon, or didn't find it, fall back to
|
||||
// the normal icon lookup
|
||||
if (hIcon == 0)
|
||||
{
|
||||
var shinfo = default(NativeMethods.SHFILEINFO);
|
||||
|
||||
var hr = NativeMethods.SHGetFileInfo(filePath, 0, ref shinfo, (uint)Marshal.SizeOf(shinfo), SHGFI_ICON | SHGFI_SHELLICONSIZE);
|
||||
|
||||
if (hr == 0 || shinfo.hIcon == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
hIcon = shinfo.hIcon;
|
||||
}
|
||||
|
||||
if (hIcon == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return await FromHIconToStream(hIcon);
|
||||
}
|
||||
|
||||
private static async Task<IRandomAccessStream?> GetImageThumbnailAsync(string filePath, bool jumbo)
|
||||
{
|
||||
var file = await StorageFile.GetFileFromPathAsync(filePath);
|
||||
var thumbnail = await file.GetThumbnailAsync(
|
||||
jumbo ? ThumbnailMode.SingleItem : ThumbnailMode.ListView,
|
||||
jumbo ? 64u : 20u);
|
||||
return thumbnail;
|
||||
}
|
||||
|
||||
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore", Justification = "Win32 Naming/Private")]
|
||||
private static readonly Guid IID_IImageList = new Guid("46EB5926-582E-4017-9FDF-E8998DAA0950");
|
||||
|
||||
private static nint GetLargestIcon(string path)
|
||||
{
|
||||
var shinfo = default(NativeMethods.SHFILEINFO);
|
||||
NativeMethods.SHGetFileInfo(path, 0, ref shinfo, (uint)Marshal.SizeOf(shinfo), SHGFI_SYSICONINDEX);
|
||||
|
||||
var hIcon = IntPtr.Zero;
|
||||
var iID_IImageList = IID_IImageList;
|
||||
|
||||
if (NativeMethods.SHGetImageList(SHIL_JUMBO, ref iID_IImageList, out var imageListPtr) == 0 && imageListPtr != IntPtr.Zero)
|
||||
{
|
||||
hIcon = NativeMethods.ImageList_GetIcon(imageListPtr, shinfo.iIcon, ILD_TRANSPARENT);
|
||||
}
|
||||
|
||||
return hIcon;
|
||||
}
|
||||
|
||||
private static nint GetLargestIcon(IntPtr pidl)
|
||||
{
|
||||
var shinfo = default(NativeMethods.SHFILEINFO);
|
||||
NativeMethods.SHGetFileInfo(pidl, 0, ref shinfo, (uint)Marshal.SizeOf(shinfo), SHGFI_SYSICONINDEX | SHGFI_PIDL);
|
||||
|
||||
var hIcon = IntPtr.Zero;
|
||||
var iID_IImageList = IID_IImageList;
|
||||
|
||||
if (NativeMethods.SHGetImageList(SHIL_JUMBO, ref iID_IImageList, out var imageListPtr) == 0 && imageListPtr != IntPtr.Zero)
|
||||
{
|
||||
hIcon = NativeMethods.ImageList_GetIcon(imageListPtr, shinfo.iIcon, ILD_TRANSPARENT);
|
||||
}
|
||||
|
||||
return hIcon;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get an icon stream for a registered URI protocol (e.g. "mailto:", "http:", "steam:").
|
||||
/// </summary>
|
||||
public static async Task<IRandomAccessStream?> GetProtocolIconStream(string protocol, bool jumbo)
|
||||
{
|
||||
// 1) Ask the shell for the protocol's default icon "path,index"
|
||||
var iconRef = QueryProtocolIconReference(protocol);
|
||||
if (string.IsNullOrWhiteSpace(iconRef))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Indirect reference:
|
||||
if (iconRef.StartsWith('@'))
|
||||
{
|
||||
if (TryLoadIndirectString(iconRef, out var expanded) && !string.IsNullOrWhiteSpace(expanded))
|
||||
{
|
||||
iconRef = expanded;
|
||||
}
|
||||
}
|
||||
|
||||
// 2) Handle image files from a store app
|
||||
if (File.Exists(iconRef))
|
||||
{
|
||||
try
|
||||
{
|
||||
var file = await StorageFile.GetFileFromPathAsync(iconRef);
|
||||
var thumbnail = await file.GetThumbnailAsync(
|
||||
jumbo ? ThumbnailMode.SingleItem : ThumbnailMode.ListView,
|
||||
jumbo ? 64u : 20u);
|
||||
return thumbnail;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// 3) Parse "path,index" (index can be negative)
|
||||
if (!TryParseIconReference(iconRef, out var path, out var index))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// if it's and .exe and without a path, let's find on path:
|
||||
if (Path.GetExtension(path).Equals(".exe", StringComparison.OrdinalIgnoreCase) && !Path.IsPathRooted(path))
|
||||
{
|
||||
var paths = Environment.GetEnvironmentVariable("PATH")?.Split(';') ?? [];
|
||||
foreach (var p in paths)
|
||||
{
|
||||
var candidate = Path.Combine(p, path);
|
||||
if (File.Exists(candidate))
|
||||
{
|
||||
path = candidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3) Extract an HICON, preferably ~256px when jumbo==true
|
||||
var hIcon = ExtractIconHandle(path, index, jumbo);
|
||||
if (hIcon == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return await FromHIconToStream(hIcon);
|
||||
}
|
||||
|
||||
private static bool TryLoadIndirectString(string input, out string? output)
|
||||
{
|
||||
var outBuffer = new StringBuilder(1024);
|
||||
var hr = NativeMethods.SHLoadIndirectString(input, outBuffer, outBuffer.Capacity, IntPtr.Zero);
|
||||
if (hr == 0)
|
||||
{
|
||||
output = outBuffer.ToString();
|
||||
return !string.IsNullOrWhiteSpace(output);
|
||||
}
|
||||
|
||||
output = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static async Task<IRandomAccessStream?> FromHIconToStream(IntPtr hIcon)
|
||||
{
|
||||
var stream = new InMemoryRandomAccessStream();
|
||||
|
||||
using var memoryStream = GetMemoryStreamFromIcon(hIcon); // this will DestroyIcon hIcon
|
||||
using var outputStream = stream.GetOutputStreamAt(0);
|
||||
using var dataWriter = new DataWriter(outputStream);
|
||||
|
||||
dataWriter.WriteBytes(memoryStream.ToArray());
|
||||
await dataWriter.StoreAsync();
|
||||
await dataWriter.FlushAsync();
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
private static string? QueryProtocolIconReference(string protocol)
|
||||
{
|
||||
// First try DefaultIcon (most widely populated for protocols)
|
||||
// If you want to try AppIconReference as a fallback, you can repeat with AssocStr.AppIconReference.
|
||||
var iconReference = AssocQueryStringSafe(NativeMethods.AssocStr.DefaultIcon, protocol);
|
||||
if (!string.IsNullOrWhiteSpace(iconReference))
|
||||
{
|
||||
return iconReference;
|
||||
}
|
||||
|
||||
// Optional fallback – some registrations use AppIconReference:
|
||||
iconReference = AssocQueryStringSafe(NativeMethods.AssocStr.AppIconReference, protocol);
|
||||
return iconReference;
|
||||
|
||||
static unsafe string? AssocQueryStringSafe(NativeMethods.AssocStr what, string protocol)
|
||||
{
|
||||
uint cch = 0;
|
||||
|
||||
// First call: get required length (incl. null)
|
||||
_ = NativeMethods.AssocQueryStringW(NativeMethods.AssocF.IsProtocol, what, protocol, null, null, ref cch);
|
||||
if (cch == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Small buffers on stack; large on heap
|
||||
var span = cch <= 512 ? stackalloc char[(int)cch] : new char[(int)cch];
|
||||
|
||||
fixed (char* p = span)
|
||||
{
|
||||
var hr = NativeMethods.AssocQueryStringW(NativeMethods.AssocF.IsProtocol, what, protocol, null, p, ref cch);
|
||||
if (hr != 0 || cch == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// cch includes the null terminator; slice it off
|
||||
var len = (int)cch - 1;
|
||||
if (len < 0)
|
||||
{
|
||||
len = 0;
|
||||
}
|
||||
|
||||
return new string(span.Slice(0, len));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TryParseIconReference(string iconRef, out string path, out int index)
|
||||
{
|
||||
// Typical shapes:
|
||||
// "C:\Program Files\Outlook\OUTLOOK.EXE,-1"
|
||||
// "shell32.dll,21"
|
||||
// "\"C:\Some Path\app.dll\",-325"
|
||||
|
||||
// If there's no comma, assume ",0"
|
||||
index = 0;
|
||||
path = iconRef.Trim();
|
||||
|
||||
// Split only on the last comma so paths with commas still work
|
||||
var lastComma = path.LastIndexOf(',');
|
||||
if (lastComma >= 0)
|
||||
{
|
||||
var idxPart = path[(lastComma + 1)..].Trim();
|
||||
path = path[..lastComma].Trim();
|
||||
_ = int.TryParse(idxPart, out index);
|
||||
}
|
||||
|
||||
// Trim quotes around path
|
||||
path = path.Trim('"');
|
||||
if (path.Length > 1 && path[0] == '"' && path[^1] == '"')
|
||||
{
|
||||
path = path.Substring(1, path.Length - 2);
|
||||
}
|
||||
|
||||
// Basic sanity
|
||||
return !string.IsNullOrWhiteSpace(path);
|
||||
}
|
||||
|
||||
private static nint ExtractIconHandle(string path, int index, bool jumbo)
|
||||
{
|
||||
// Request sizes: LOWORD=small, HIWORD=large.
|
||||
// Ask for 256 when jumbo, else fall back to 32/16.
|
||||
var small = jumbo ? 256 : 16;
|
||||
var large = jumbo ? 256 : 32;
|
||||
var sizeParam = (large << 16) | (small & 0xFFFF);
|
||||
|
||||
var hr = NativeMethods.SHDefExtractIconW(path, index, 0, out var hLarge, out var hSmall, sizeParam);
|
||||
if (hr == 0 && hLarge != 0)
|
||||
{
|
||||
return hLarge;
|
||||
}
|
||||
|
||||
if (hr == 0 && hSmall != 0)
|
||||
{
|
||||
return hSmall;
|
||||
}
|
||||
|
||||
// Final fallback: try 32/16 explicitly in case the resource can’t upscale
|
||||
sizeParam = (32 << 16) | 16;
|
||||
hr = NativeMethods.SHDefExtractIconW(path, index, 0, out hLarge, out hSmall, sizeParam);
|
||||
if (hr == 0 && hLarge != 0)
|
||||
{
|
||||
return hLarge;
|
||||
}
|
||||
|
||||
if (hr == 0 && hSmall != 0)
|
||||
{
|
||||
return hSmall;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class ToastArgs : IToastArgs
|
||||
{
|
||||
public string? Message { get; set; }
|
||||
|
||||
public ICommandResult? Result { get; set; } = CommandResult.Dismiss();
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class ToastStatusMessage
|
||||
{
|
||||
private readonly Lock _showLock = new();
|
||||
private bool _shown;
|
||||
|
||||
public virtual StatusMessage Message { get; init; }
|
||||
|
||||
public virtual int Duration { get; init; } = 2500;
|
||||
|
||||
public ToastStatusMessage(StatusMessage message)
|
||||
{
|
||||
Message = message;
|
||||
}
|
||||
|
||||
public ToastStatusMessage(string text)
|
||||
{
|
||||
Message = new StatusMessage() { Message = text };
|
||||
}
|
||||
|
||||
public void Show()
|
||||
{
|
||||
lock (_showLock)
|
||||
{
|
||||
if (!_shown)
|
||||
{
|
||||
ExtensionHost.ShowStatus(Message, StatusContext.Extension);
|
||||
_ = Task.Run(() =>
|
||||
{
|
||||
Thread.Sleep(Duration);
|
||||
|
||||
lock (_showLock)
|
||||
{
|
||||
_shown = false;
|
||||
ExtensionHost.HideStatus(Message);
|
||||
}
|
||||
});
|
||||
_shown = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
// 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.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public sealed class ToggleSetting : Setting<bool>
|
||||
{
|
||||
private ToggleSetting()
|
||||
: base()
|
||||
{
|
||||
}
|
||||
|
||||
public ToggleSetting(string key, bool defaultValue)
|
||||
: base(key, defaultValue)
|
||||
{
|
||||
}
|
||||
|
||||
public ToggleSetting(string key, string label, string description, bool defaultValue)
|
||||
: base(key, label, description, defaultValue)
|
||||
{
|
||||
}
|
||||
|
||||
public override Dictionary<string, object> ToDictionary()
|
||||
{
|
||||
var items = new List<Dictionary<string, object>>();
|
||||
|
||||
if (!string.IsNullOrEmpty(Label))
|
||||
{
|
||||
items.Add(
|
||||
new()
|
||||
{
|
||||
{ "type", "TextBlock" },
|
||||
{ "text", Label },
|
||||
{ "wrap", true },
|
||||
});
|
||||
}
|
||||
|
||||
if (!(string.IsNullOrEmpty(Description) || string.Equals(Description, Label, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
items.Add(
|
||||
new()
|
||||
{
|
||||
{ "type", "TextBlock" },
|
||||
{ "text", Description },
|
||||
{ "isSubtle", true },
|
||||
{ "size", "Small" },
|
||||
{ "spacing", "Small" },
|
||||
{ "wrap", true },
|
||||
});
|
||||
}
|
||||
|
||||
return new()
|
||||
{
|
||||
{ "type", "ColumnSet" },
|
||||
{
|
||||
"columns", new List<Dictionary<string, object>>
|
||||
{
|
||||
new()
|
||||
{
|
||||
{ "type", "Column" },
|
||||
{ "width", "20px" },
|
||||
{
|
||||
"items", new List<Dictionary<string, object>>
|
||||
{
|
||||
new()
|
||||
{
|
||||
{ "type", "Input.Toggle" },
|
||||
{ "title", " " },
|
||||
{ "id", Key },
|
||||
{ "value", JsonSerializer.Serialize(Value, JsonSerializationContext.Default.Boolean) },
|
||||
{ "isRequired", IsRequired },
|
||||
{ "errorMessage", ErrorMessage },
|
||||
},
|
||||
}
|
||||
},
|
||||
{ "verticalContentAlignment", "Center" },
|
||||
},
|
||||
new()
|
||||
{
|
||||
{ "type", "Column" },
|
||||
{ "width", "stretch" },
|
||||
{ "items", items },
|
||||
{ "verticalContentAlignment", "Center" },
|
||||
},
|
||||
}
|
||||
},
|
||||
{ "spacing", "Medium" },
|
||||
};
|
||||
}
|
||||
|
||||
public static ToggleSetting LoadFromJson(JsonObject jsonObject) => new() { Value = jsonObject["value"]?.GetValue<bool>() ?? false };
|
||||
|
||||
public override void Update(JsonObject payload)
|
||||
{
|
||||
// If the key doesn't exist in the payload, don't do anything
|
||||
if (payload[Key] is not null)
|
||||
{
|
||||
// Adaptive cards returns boolean values as a string "true"/"false", cause of course.
|
||||
var strFromJson = payload[Key]?.GetValue<string>() ?? string.Empty;
|
||||
var val = strFromJson switch { "true" => true, "false" => false, _ => false };
|
||||
Value = val;
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToState()
|
||||
{
|
||||
var adaptiveCardsUsesStringsForBools = Value ? "true" : "false";
|
||||
return $"\"{Key}\": \"{adaptiveCardsUsesStringsForBools}\"";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// 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 Windows.Foundation;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
public partial class TreeContent : BaseObservable, ITreeContent
|
||||
{
|
||||
public IContent[] Children { get; set; } = [];
|
||||
|
||||
public virtual IContent? RootContent
|
||||
{
|
||||
get;
|
||||
set
|
||||
{
|
||||
field = value;
|
||||
OnPropertyChanged(nameof(RootContent));
|
||||
}
|
||||
}
|
||||
|
||||
public event TypedEventHandler<object, IItemsChangedEventArgs>? ItemsChanged;
|
||||
|
||||
public virtual IContent[] GetChildren() => Children;
|
||||
|
||||
protected void RaiseItemsChanged(int totalItems = -1)
|
||||
{
|
||||
try
|
||||
{
|
||||
// TODO #181 - This is the same thing that BaseObservable has to deal with.
|
||||
ItemsChanged?.Invoke(this, new ItemsChangedEventArgs(totalItems));
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
// 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.Runtime.InteropServices;
|
||||
using Windows.Win32;
|
||||
using Windows.Win32.Foundation;
|
||||
using Windows.Win32.UI.Shell;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1312:Variable names should begin with lower-case letter", Justification = "This file has more than a couple Windows constants in it, which don't make sense to rename")]
|
||||
public class Utilities
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to produce a path to a settings folder which your app can use.
|
||||
/// If your app is running packaged, this will return the redirected local
|
||||
/// app data path (Packages/{your_pfn}/LocalState). If not, it'll return
|
||||
/// %LOCALAPPDATA%\{settingsFolderName}.
|
||||
///
|
||||
/// Does not ensure that the directory exists. Callers should call
|
||||
/// CreateDirectory before writing settings files to this directory.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// var directory = Utilities.BaseSettingsPath("Some.Unique.String.Here");
|
||||
/// Directory.CreateDirectory(directory);
|
||||
/// </example>
|
||||
/// <param name="settingsFolderName">A fallback directory name to use
|
||||
/// inside of %LocalAppData%, in the case this app is not currently running
|
||||
/// in a package context</param>
|
||||
/// <returns>The path to a folder to use for storing settings.</returns>
|
||||
public static string BaseSettingsPath(string settingsFolderName)
|
||||
{
|
||||
// KF_FLAG_FORCE_APP_DATA_REDIRECTION, when engaged, causes SHGet... to return
|
||||
// the new AppModel paths (Packages/xxx/RoamingState, etc.) for standard path requests.
|
||||
// Using this flag allows us to avoid Windows.Storage.ApplicationData completely.
|
||||
var FOLDERID_LocalAppData = new Guid("F1B32785-6FBA-4FCF-9D55-7B8E7F157091");
|
||||
var hr = PInvoke.SHGetKnownFolderPath(
|
||||
FOLDERID_LocalAppData,
|
||||
KNOWN_FOLDER_FLAG.KF_FLAG_FORCE_APP_DATA_REDIRECTION,
|
||||
null,
|
||||
out var localAppDataFolder);
|
||||
|
||||
if (hr.Succeeded)
|
||||
{
|
||||
var basePath = new string(localAppDataFolder.ToString());
|
||||
if (!IsPackaged())
|
||||
{
|
||||
basePath = Path.Combine(basePath, settingsFolderName);
|
||||
}
|
||||
|
||||
return basePath;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw Marshal.GetExceptionForHR(hr.Value)!;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Can be used to quickly determine if this process is running with package identity.
|
||||
/// </summary>
|
||||
/// <returns>true iff the process is running with package identity</returns>
|
||||
public static bool IsPackaged()
|
||||
{
|
||||
uint bufferSize = 0;
|
||||
var bytes = Array.Empty<byte>();
|
||||
|
||||
// CsWinRT apparently won't generate this constant
|
||||
var APPMODEL_ERROR_NO_PACKAGE = (WIN32_ERROR)15700;
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* p = bytes)
|
||||
{
|
||||
// We don't actually need the package ID. We just need to know
|
||||
// if we have a package or not, and APPMODEL_ERROR_NO_PACKAGE
|
||||
// is a quick way to find out.
|
||||
var win32Error = PInvoke.GetCurrentPackageId(ref bufferSize, p);
|
||||
return win32Error != APPMODEL_ERROR_NO_PACKAGE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
// 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.ComponentModel;
|
||||
|
||||
namespace Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
/// <summary>
|
||||
/// Implements a weak event listener that allows the owner to be garbage
|
||||
/// collected if its only remaining link is an event handler.
|
||||
/// </summary>
|
||||
/// <typeparam name="TInstance">Type of instance listening for the event.</typeparam>
|
||||
/// <typeparam name="TSource">Type of source for the event.</typeparam>
|
||||
/// <typeparam name="TEventArgs">Type of event arguments for the event.</typeparam>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
internal sealed class WeakEventListener<TInstance, TSource, TEventArgs>
|
||||
where TInstance : class
|
||||
{
|
||||
/// <summary>
|
||||
/// WeakReference to the instance listening for the event.
|
||||
/// </summary>
|
||||
private readonly WeakReference<TInstance> _weakInstance;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WeakEventListener{TInstance, TSource, TEventArgs}"/> class.
|
||||
/// </summary>
|
||||
/// <param name="instance">Instance subscribing to the event.</param>
|
||||
/// <param name="onEventAction">Event handler executed when event is raised.</param>
|
||||
/// <param name="onDetachAction">Action to execute when instance was collected.</param>
|
||||
public WeakEventListener(
|
||||
TInstance instance,
|
||||
Action<TInstance, TSource, TEventArgs>? onEventAction = null,
|
||||
Action<WeakEventListener<TInstance, TSource, TEventArgs>>? onDetachAction = null)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(instance);
|
||||
|
||||
_weakInstance = new(instance);
|
||||
OnEventAction = onEventAction;
|
||||
OnDetachAction = onDetachAction;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the method to call when the event fires.
|
||||
/// </summary>
|
||||
public Action<TInstance, TSource, TEventArgs>? OnEventAction { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the method to call when detaching from the event.
|
||||
/// </summary>
|
||||
public Action<WeakEventListener<TInstance, TSource, TEventArgs>>? OnDetachAction { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Handler for the subscribed event calls OnEventAction to handle it.
|
||||
/// </summary>
|
||||
/// <param name="source">Event source.</param>
|
||||
/// <param name="eventArgs">Event arguments.</param>
|
||||
public void OnEvent(TSource source, TEventArgs eventArgs)
|
||||
{
|
||||
if (_weakInstance.TryGetTarget(out var target))
|
||||
{
|
||||
// Call registered action
|
||||
OnEventAction?.Invoke(target, source, eventArgs);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Detach from event
|
||||
Detach();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Detaches from the subscribed event.
|
||||
/// </summary>
|
||||
public void Detach()
|
||||
{
|
||||
OnDetachAction?.Invoke(this);
|
||||
OnDetachAction = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
EXPORTS
|
||||
DllCanUnloadNow = WINRT_CanUnloadNow PRIVATE
|
||||
DllGetActivationFactory = WINRT_GetActivationFactory PRIVATE
|
||||
@@ -0,0 +1,396 @@
|
||||
namespace Microsoft.CommandPalette.Extensions
|
||||
{
|
||||
[contractversion(1)]
|
||||
apicontract ExtensionsContract {}
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IExtension {
|
||||
IInspectable GetProvider(ProviderType providerType);
|
||||
void Dispose();
|
||||
};
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
enum ProviderType {
|
||||
Commands = 0,
|
||||
};
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IIconData {
|
||||
String Icon { get; };
|
||||
Windows.Storage.Streams.IRandomAccessStreamReference Data { get; };
|
||||
};
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IIconInfo {
|
||||
IIconData Light { get; };
|
||||
IIconData Dark { get; };
|
||||
};
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
struct KeyChord
|
||||
{
|
||||
Windows.System.VirtualKeyModifiers Modifiers;
|
||||
Int32 Vkey;
|
||||
Int32 ScanCode;
|
||||
};
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface INotifyPropChanged {
|
||||
event Windows.Foundation.TypedEventHandler<Object, IPropChangedEventArgs> PropChanged;
|
||||
};
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IPropChangedEventArgs {
|
||||
String PropertyName { get; };
|
||||
};
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface INotifyItemsChanged {
|
||||
event Windows.Foundation.TypedEventHandler<Object, IItemsChangedEventArgs> ItemsChanged;
|
||||
};
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IItemsChangedEventArgs {
|
||||
Int32 TotalItems { get; };
|
||||
};
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface ICommand requires INotifyPropChanged{
|
||||
String Name{ get; };
|
||||
String Id{ get; };
|
||||
IIconInfo Icon{ get; };
|
||||
}
|
||||
|
||||
enum CommandResultKind {
|
||||
Dismiss, // Reset the palette to the main page and dismiss
|
||||
GoHome, // Go back to the main page, but keep it open
|
||||
GoBack, // Go back one level
|
||||
Hide, // Keep this page open, but hide the palette.
|
||||
KeepOpen, // Do nothing.
|
||||
GoToPage, // Go to another page. GoToPageArgs will tell you where.
|
||||
ShowToast, // Display a transient message to the user
|
||||
Confirm, // Display a confirmation dialog
|
||||
};
|
||||
|
||||
enum NavigationMode {
|
||||
Push, // Push the target page onto the navigation stack
|
||||
GoBack, // Go back one page before navigating to the target page
|
||||
GoHome, // Go back to the home page before navigating to the target page
|
||||
};
|
||||
|
||||
[uuid("f9d6423b-bd5e-44bb-a204-2f5c77a72396")]
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface ICommandResultArgs{};
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface ICommandResult {
|
||||
CommandResultKind Kind { get; };
|
||||
ICommandResultArgs Args { get; };
|
||||
}
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IGoToPageArgs requires ICommandResultArgs{
|
||||
String PageId { get; };
|
||||
NavigationMode NavigationMode { get; };
|
||||
}
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IToastArgs requires ICommandResultArgs{
|
||||
String Message { get; };
|
||||
ICommandResult Result { get; };
|
||||
}
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IConfirmationArgs requires ICommandResultArgs{
|
||||
String Title { get; };
|
||||
String Description { get; };
|
||||
ICommand PrimaryCommand { get; };
|
||||
Boolean IsPrimaryCommandCritical { get; };
|
||||
}
|
||||
|
||||
// This is a "leaf" of the UI. This is something that can be "done" by the user.
|
||||
// * A ListPage
|
||||
// * the MoreCommands flyout of for a ListItem or a MarkdownPage
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IInvokableCommand requires ICommand {
|
||||
ICommandResult Invoke(Object sender);
|
||||
}
|
||||
|
||||
|
||||
[uuid("ef5db50c-d26b-4aee-9343-9f98739ab411")]
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IFilterItem {}
|
||||
|
||||
[uuid("0a923c7f-5b7b-431d-9898-3c8c841d02ed")]
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface ISeparatorFilterItem requires IFilterItem {}
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IFilter requires INotifyPropChanged, IFilterItem {
|
||||
String Id { get; };
|
||||
String Name { get; };
|
||||
IIconInfo Icon { get; };
|
||||
}
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IFilters {
|
||||
String CurrentFilterId { get; set; };
|
||||
IFilterItem[] GetFilters();
|
||||
}
|
||||
|
||||
struct Color
|
||||
{
|
||||
UInt8 R;
|
||||
UInt8 G;
|
||||
UInt8 B;
|
||||
UInt8 A;
|
||||
};
|
||||
|
||||
struct OptionalColor
|
||||
{
|
||||
Boolean HasValue;
|
||||
Microsoft.CommandPalette.Extensions.Color Color;
|
||||
};
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface ITag {
|
||||
IIconInfo Icon { get; };
|
||||
String Text { get; };
|
||||
OptionalColor Foreground { get; };
|
||||
OptionalColor Background { get; };
|
||||
String ToolTip { get; };
|
||||
};
|
||||
|
||||
[uuid("6a6dd345-37a3-4a1e-914d-4f658a4d583d")]
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IDetailsData {}
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IDetailsElement {
|
||||
String Key { get; };
|
||||
IDetailsData Data { get; };
|
||||
}
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IDetails {
|
||||
IIconInfo HeroImage { get; };
|
||||
String Title { get; };
|
||||
String Body { get; };
|
||||
IDetailsElement[] Metadata { get; };
|
||||
}
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IDetailsTags requires IDetailsData {
|
||||
ITag[] Tags { get; };
|
||||
}
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IDetailsLink requires IDetailsData {
|
||||
Windows.Foundation.Uri Link { get; };
|
||||
String Text { get; };
|
||||
}
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IDetailsCommands requires IDetailsData {
|
||||
ICommand[] Commands { get; };
|
||||
}
|
||||
[uuid("58070392-02bb-4e89-9beb-47ceb8c3d741")]
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IDetailsSeparator requires IDetailsData {}
|
||||
|
||||
enum MessageState
|
||||
{
|
||||
Info = 0,
|
||||
Success,
|
||||
Warning,
|
||||
Error,
|
||||
};
|
||||
|
||||
enum StatusContext
|
||||
{
|
||||
Page,
|
||||
Extension
|
||||
};
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IProgressState requires INotifyPropChanged
|
||||
{
|
||||
Boolean IsIndeterminate { get; };
|
||||
UInt32 ProgressPercent { get; };
|
||||
};
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IStatusMessage requires INotifyPropChanged
|
||||
{
|
||||
MessageState State { get; };
|
||||
IProgressState Progress { get; };
|
||||
String Message { get; };
|
||||
// TODO! Icon maybe? Work with design on this
|
||||
};
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface ILogMessage
|
||||
{
|
||||
MessageState State { get; };
|
||||
String Message { get; };
|
||||
};
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IExtensionHost
|
||||
{
|
||||
Windows.Foundation.IAsyncAction ShowStatus(IStatusMessage message, StatusContext context);
|
||||
Windows.Foundation.IAsyncAction HideStatus(IStatusMessage message);
|
||||
|
||||
Windows.Foundation.IAsyncAction LogMessage(ILogMessage message);
|
||||
};
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IPage requires ICommand {
|
||||
String Title { get; };
|
||||
Boolean IsLoading { get; };
|
||||
|
||||
OptionalColor AccentColor { get; };
|
||||
}
|
||||
|
||||
[uuid("c78b9851-e76b-43ee-8f76-da5ba14e69a4")]
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IContextItem {}
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface ICommandItem requires INotifyPropChanged {
|
||||
ICommand Command{ get; };
|
||||
IContextItem[] MoreCommands{ get; };
|
||||
IIconInfo Icon{ get; };
|
||||
String Title{ get; };
|
||||
String Subtitle{ get; };
|
||||
}
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface ICommandContextItem requires ICommandItem, IContextItem {
|
||||
Boolean IsCritical { get; }; // READ: "make this red"
|
||||
KeyChord RequestedShortcut { get; };
|
||||
}
|
||||
|
||||
[uuid("924a87fc-32fe-4471-9156-84b3b30275a6")]
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface ISeparatorContextItem requires IContextItem {}
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IListItem requires ICommandItem {
|
||||
ITag[] Tags{ get; };
|
||||
IDetails Details{ get; };
|
||||
String Section { get; };
|
||||
String TextToSuggest { get; };
|
||||
}
|
||||
|
||||
[uuid("50C6F080-1CBE-4CE4-B92F-DA2F116ED524")]
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IGridProperties requires INotifyPropChanged { }
|
||||
|
||||
[uuid("05914D59-6ECB-4992-9CF2-5982B5120A26")]
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface ISmallGridLayout requires IGridProperties { }
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IMediumGridLayout requires IGridProperties
|
||||
{
|
||||
Boolean ShowTitle { get; };
|
||||
}
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IGalleryGridLayout requires IGridProperties
|
||||
{
|
||||
Boolean ShowTitle { get; };
|
||||
Boolean ShowSubtitle { get; };
|
||||
}
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IListPage requires IPage, INotifyItemsChanged {
|
||||
// DevPal will be responsible for filtering the list of items, unless the
|
||||
// class implements IDynamicListPage
|
||||
String SearchText { get; };
|
||||
String PlaceholderText { get; };
|
||||
Boolean ShowDetails{ get; };
|
||||
IFilters Filters { get; };
|
||||
IGridProperties GridProperties { get; };
|
||||
Boolean HasMoreItems { get; };
|
||||
ICommandItem EmptyContent { get; };
|
||||
|
||||
IListItem[] GetItems();
|
||||
void LoadMore();
|
||||
}
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IDynamicListPage requires IListPage {
|
||||
String SearchText { set; };
|
||||
}
|
||||
|
||||
[uuid("b64def0f-8911-4afa-8f8f-042bd778d088")]
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IContent requires INotifyPropChanged {
|
||||
}
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IFormContent requires IContent {
|
||||
String TemplateJson { get; };
|
||||
String DataJson { get; };
|
||||
String StateJson { get; };
|
||||
ICommandResult SubmitForm(String inputs, String data);
|
||||
}
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IMarkdownContent requires IContent {
|
||||
String Body { get; };
|
||||
}
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface ITreeContent requires IContent, INotifyItemsChanged {
|
||||
IContent RootContent { get; };
|
||||
IContent[] GetChildren();
|
||||
}
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IContentPage requires IPage, INotifyItemsChanged {
|
||||
IContent[] GetContent();
|
||||
IDetails Details { get; };
|
||||
IContextItem[] Commands { get; };
|
||||
}
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface ICommandSettings {
|
||||
IContentPage SettingsPage { get; };
|
||||
};
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IFallbackHandler {
|
||||
void UpdateQuery(String query);
|
||||
};
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IFallbackCommandItem requires ICommandItem {
|
||||
IFallbackHandler FallbackHandler{ get; };
|
||||
String DisplayTitle { get; };
|
||||
};
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface ICommandProvider requires Windows.Foundation.IClosable, INotifyItemsChanged
|
||||
{
|
||||
String Id { get; };
|
||||
String DisplayName { get; };
|
||||
IIconInfo Icon { get; };
|
||||
ICommandSettings Settings { get; };
|
||||
Boolean Frozen { get; };
|
||||
|
||||
ICommandItem[] TopLevelCommands();
|
||||
IFallbackCommandItem[] FallbackCommands();
|
||||
|
||||
ICommand GetCommand(String id);
|
||||
|
||||
void InitializeWithHost(IExtensionHost host);
|
||||
};
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface IExtendedAttributesProvider
|
||||
{
|
||||
Windows.Foundation.Collections.IMap<String, Object> GetProperties();
|
||||
};
|
||||
|
||||
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
|
||||
interface ICommandProvider2 requires ICommandProvider
|
||||
{
|
||||
Object[] GetApiExtensionStubs();
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<PathToRoot>..\..\..\..\..\</PathToRoot>
|
||||
<WasdkNuget>$(PathToRoot)packages\Microsoft.WindowsAppSDK.1.8.250907003</WasdkNuget>
|
||||
<CppWinRTNuget>$(PathToRoot)packages\Microsoft.Windows.CppWinRT.2.0.240111.5</CppWinRTNuget>
|
||||
<WindowsSdkBuildToolsNuget>$(PathToRoot)packages\Microsoft.Windows.SDK.BuildTools.10.0.26100.4188</WindowsSdkBuildToolsNuget>
|
||||
<WebView2Nuget>$(PathToRoot)packages\Microsoft.Web.WebView2.1.0.2903.40</WebView2Nuget>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(WasdkNuget)\build\native\Microsoft.WindowsAppSDK.props" Condition="Exists('$(WasdkNuget)\build\native\Microsoft.WindowsAppSDK.props')" />
|
||||
<Import Project="$(CppWinRTNuget)\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('$(CppWinRTNuget)\build\native\Microsoft.Windows.CppWinRT.props')" />
|
||||
<Import Project="$(WindowsSdkBuildToolsNuget)\build\Microsoft.Windows.SDK.BuildTools.props" Condition="Exists('$(WindowsSdkBuildToolsNuget)\build\Microsoft.Windows.SDK.BuildTools.props')" />
|
||||
<PropertyGroup Label="Globals">
|
||||
<CppWinRTOptimized>true</CppWinRTOptimized>
|
||||
<CppWinRTRootNamespaceAutoMerge>true</CppWinRTRootNamespaceAutoMerge>
|
||||
<CppWinRTGenerateWindowsMetadata>true</CppWinRTGenerateWindowsMetadata>
|
||||
<MinimalCoreWin>true</MinimalCoreWin>
|
||||
<ProjectGuid>{7997DAD4-31D6-496B-95DB-6C028D699370}</ProjectGuid>
|
||||
<ProjectName>Microsoft.CommandPalette.Extensions</ProjectName>
|
||||
<RootNamespace>Microsoft.CommandPalette.Extensions</RootNamespace>
|
||||
<DefaultLanguage>en-US</DefaultLanguage>
|
||||
<MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion>
|
||||
<AppContainerApplication>false</AppContainerApplication>
|
||||
<ApplicationType>Windows Store</ApplicationType>
|
||||
<ApplicationTypeRevision>10.0</ApplicationTypeRevision>
|
||||
<WindowsTargetPlatformMinVersion>10.0.19041.0</WindowsTargetPlatformMinVersion>
|
||||
<WindowsTargetPlatformVersion>10.0.26100.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|ARM64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>ARM64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|ARM64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>ARM64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\Microsoft.CommandPalette.Extensions\</OutDir>
|
||||
<IntDir>obj\$(Platform)\$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<PlatformToolset Condition="'$(VisualStudioVersion)' == '16.0'">v142</PlatformToolset>
|
||||
<PlatformToolset Condition="'$(VisualStudioVersion)' == '15.0'">v141</PlatformToolset>
|
||||
<PlatformToolset Condition="'$(VisualStudioVersion)' == '14.0'">v140</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<GenerateManifest>false</GenerateManifest>
|
||||
<DesktopCompatible>true</DesktopCompatible>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
|
||||
<ClCompile>
|
||||
<!-- We use MultiThreadedDebug, rather than MultiThreadedDebugDLL, to avoid DLL dependencies on VCRUNTIME140d.dll and MSVCP140d.dll. -->
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<!-- Link statically against the runtime and STL, but link dynamically against the CRT by ignoring the static CRT
|
||||
lib and instead linking against the Universal CRT DLL import library. This "hybrid" linking mechanism is
|
||||
supported according to the CRT maintainer. Dynamic linking against the CRT makes the binaries a bit smaller
|
||||
than they would otherwise be if the CRT, runtime, and STL were all statically linked in. -->
|
||||
<IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries);libucrtd.lib</IgnoreSpecificDefaultLibraries>
|
||||
<AdditionalOptions>%(AdditionalOptions) /defaultlib:ucrtd.lib /profile /opt:ref /opt:icf</AdditionalOptions>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
|
||||
<ClCompile>
|
||||
<!-- We use MultiThreaded, rather than MultiThreadedDLL, to avoid DLL dependencies on VCRUNTIME140.dll and MSVCP140.dll. -->
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<!-- Link statically against the runtime and STL, but link dynamically against the CRT by ignoring the static CRT
|
||||
lib and instead linking against the Universal CRT DLL import library. This "hybrid" linking mechanism is
|
||||
supported according to the CRT maintainer. Dynamic linking against the CRT makes the binaries a bit smaller
|
||||
than they would otherwise be if the CRT, runtime, and STL were all statically linked in. -->
|
||||
<IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries);libucrt.lib</IgnoreSpecificDefaultLibraries>
|
||||
<AdditionalOptions>%(AdditionalOptions) /defaultlib:ucrt.lib /profile /opt:ref /opt:icf</AdditionalOptions>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<PrecompiledHeaderOutputFile>$(IntDir)pch.pch</PrecompiledHeaderOutputFile>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<LanguageStandard>stdcpp20</LanguageStandard>
|
||||
<AdditionalOptions>%(AdditionalOptions) /bigobj /Zi</AdditionalOptions>
|
||||
<PreprocessorDefinitions>_WINRT_DLL;WIN32_LEAN_AND_MEAN;WINRT_LEAN_AND_MEAN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalUsingDirectories>$(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories)</AdditionalUsingDirectories>
|
||||
<ControlFlowGuard>Guard</ControlFlowGuard>
|
||||
<SpectreMitigation>Spectre</SpectreMitigation>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateWindowsMetadata>false</GenerateWindowsMetadata>
|
||||
<ModuleDefinitionFile>Microsoft.CommandPalette.Extensions.def</ModuleDefinitionFile>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<!-- <ClCompile Include="Generated Files\module.g.cpp" /> -->
|
||||
<ClCompile Include="winrt_module.cpp" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Midl Include="Microsoft.CommandPalette.Extensions.idl" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
<None Include="Microsoft.CommandPalette.Extensions.def" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Text Include="readme.txt">
|
||||
<DeploymentContent>false</DeploymentContent>
|
||||
</Text>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="$(WindowsSdkBuildToolsNuget)\build\Microsoft.Windows.SDK.BuildTools.targets" Condition="Exists('$(WindowsSdkBuildToolsNuget)\build\Microsoft.Windows.SDK.BuildTools.targets')" />
|
||||
<Import Project="$(CppWinRTNuget)\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('$(CppWinRTNuget)\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||
<Import Project="$(WasdkNuget)\build\native\Microsoft.WindowsAppSDK.targets" Condition="Exists('$(WasdkNuget)\build\native\Microsoft.WindowsAppSDK.targets')" />
|
||||
<Import Project="$(WebView2Nuget)\build\native\Microsoft.Web.WebView2.targets" Condition="Exists('$(WebView2Nuget)\build\native\Microsoft.Web.WebView2.targets')" />
|
||||
</ImportGroup>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('$(WindowsSdkBuildToolsNuget)\build\Microsoft.Windows.SDK.BuildTools.props')" Text="$([System.String]::Format('$(ErrorText)', '$(WindowsSdkBuildToolsNuget)\build\Microsoft.Windows.SDK.BuildTools.props'))" />
|
||||
<Error Condition="!Exists('$(WindowsSdkBuildToolsNuget)\build\Microsoft.Windows.SDK.BuildTools.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(WindowsSdkBuildToolsNuget)\build\Microsoft.Windows.SDK.BuildTools.targets'))" />
|
||||
<Error Condition="!Exists('$(CppWinRTNuget)\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '$(CppWinRTNuget)\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||
<Error Condition="!Exists('$(CppWinRTNuget)\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(CppWinRTNuget)\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||
<Error Condition="!Exists('$(WasdkNuget)\build\native\Microsoft.WindowsAppSDK.props')" Text="$([System.String]::Format('$(ErrorText)', '$(WasdkNuget)\build\native\Microsoft.WindowsAppSDK.props'))" />
|
||||
<Error Condition="!Exists('$(WasdkNuget)\build\native\Microsoft.WindowsAppSDK.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(WasdkNuget)\build\native\Microsoft.WindowsAppSDK.targets'))" />
|
||||
<Error Condition="!Exists('$(WebView2Nuget)\build\native\Microsoft.Web.WebView2.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(WebView2Nuget)\build\native\Microsoft.Web.WebView2.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Web.WebView2" version="1.0.2903.40" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.240111.5" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.231216.1" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK" version="1.8.250907003" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.Base" version="1.8.250831001" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.Foundation" version="1.8.250906002" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.WinUI" version="1.8.250906003" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.Runtime" version="1.8.250907003" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.DWrite" version="1.8.25090401" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.InteractiveExperiences" version="1.8.250906004" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.Widgets" version="1.8.250904007" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK.AI" version="1.8.37" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.SDK.BuildTools" version="10.0.26100.4188" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.SDK.BuildTools.MSIX" version="1.7.20250829.1" targetFramework="native" />
|
||||
</packages>
|
||||
@@ -0,0 +1 @@
|
||||
#include "pch.h"
|
||||
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
#include <winrt/base.h>
|
||||
#include <Windows.h>
|
||||
#include <til/winrt.h>
|
||||
@@ -0,0 +1,2 @@
|
||||
The DLL that this project builds exists only to satisfy the build system, and is otherwise unused.
|
||||
This Project provides winmd to the SDK project for building projections.
|
||||
@@ -0,0 +1,112 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace til // Terminal Implementation Library. Also: "Today I Learned"
|
||||
{
|
||||
template<typename T>
|
||||
struct property
|
||||
{
|
||||
explicit constexpr property(auto&&... args) :
|
||||
_value{ std::forward<decltype(args)>(args)... } {}
|
||||
|
||||
property& operator=(const property& other) = default;
|
||||
|
||||
T operator()() const noexcept
|
||||
{
|
||||
return _value;
|
||||
}
|
||||
void operator()(auto&& arg)
|
||||
{
|
||||
_value = std::forward<decltype(arg)>(arg);
|
||||
}
|
||||
explicit operator bool() const noexcept
|
||||
{
|
||||
#ifdef WINRT_Windows_Foundation_H
|
||||
if constexpr (std::is_same_v<T, winrt::hstring>)
|
||||
{
|
||||
return !_value.empty();
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
return _value;
|
||||
}
|
||||
}
|
||||
bool operator==(const property& other) const noexcept
|
||||
{
|
||||
return _value == other._value;
|
||||
}
|
||||
bool operator!=(const property& other) const noexcept
|
||||
{
|
||||
return _value != other._value;
|
||||
}
|
||||
bool operator==(const T& other) const noexcept
|
||||
{
|
||||
return _value == other;
|
||||
}
|
||||
bool operator!=(const T& other) const noexcept
|
||||
{
|
||||
return _value != other;
|
||||
}
|
||||
|
||||
private:
|
||||
T _value;
|
||||
};
|
||||
|
||||
#ifdef WINRT_Windows_Foundation_H
|
||||
|
||||
template<typename ArgsT>
|
||||
struct event
|
||||
{
|
||||
event<ArgsT>() = default;
|
||||
winrt::event_token operator()(const ArgsT& handler) { return _handlers.add(handler); }
|
||||
void operator()(const winrt::event_token& token) { _handlers.remove(token); }
|
||||
operator bool() const noexcept { return bool(_handlers); }
|
||||
template<typename... Arg>
|
||||
void raise(auto&&... args)
|
||||
{
|
||||
_handlers(std::forward<decltype(args)>(args)...);
|
||||
}
|
||||
winrt::event<ArgsT> _handlers;
|
||||
};
|
||||
|
||||
template<typename SenderT = winrt::Windows::Foundation::IInspectable, typename ArgsT = winrt::Windows::Foundation::IInspectable>
|
||||
struct typed_event
|
||||
{
|
||||
typed_event<SenderT, ArgsT>() = default;
|
||||
winrt::event_token operator()(const winrt::Windows::Foundation::TypedEventHandler<SenderT, ArgsT>& handler) { return _handlers.add(handler); }
|
||||
void operator()(const winrt::event_token& token) { _handlers.remove(token); }
|
||||
operator bool() const noexcept { return bool(_handlers); }
|
||||
template<typename... Arg>
|
||||
void raise(Arg const&... args)
|
||||
{
|
||||
_handlers(std::forward<decltype(args)>(args)...);
|
||||
}
|
||||
winrt::event<winrt::Windows::Foundation::TypedEventHandler<SenderT, ArgsT>> _handlers;
|
||||
};
|
||||
#endif
|
||||
#ifdef WINRT_Windows_UI_Xaml_Data_H
|
||||
|
||||
using property_changed_event = til::event<winrt::Windows::UI::Xaml::Data::PropertyChangedEventHandler>;
|
||||
// Making a til::observable_property unfortunately doesn't seem feasible.
|
||||
// It's gonna just result in more macros, which no one wants.
|
||||
//
|
||||
// 1. We don't know who the sender is, or would require `this` to always be
|
||||
// the first parameter to one of these observable_property's.
|
||||
//
|
||||
// 2. We don't know what our own name is. We need to actually raise an event
|
||||
// with the name of the variable as the parameter. Only way to do that is
|
||||
// with something like
|
||||
//
|
||||
// til::observable<int> Foo(this, L"Foo", 42)
|
||||
//
|
||||
// which then implies the creation of:
|
||||
//
|
||||
// #define OBSERVABLE(type, name, ...) til::observable_property<type> name{ this, L## #name, this.PropertyChanged, __VA_ARGS__ };
|
||||
//
|
||||
// Which is just silly
|
||||
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
// TYPICALLY: This file is generated by C++/WinRT (this one was originally from
|
||||
// v2.0.240111.5), and it's included in the dll by way of "Generated
|
||||
// Files\module.g.cpp". However, our project doesn't have any activatable WinRT
|
||||
// `runtimeclass`es in it. But we need something to appease WinRT, so this is
|
||||
// the empty version of this file, without any classes in it.
|
||||
//
|
||||
// Should you need to add a WinRT runtime class to this project, think very
|
||||
// carefully about it first. If that's the only solution, then just delete this
|
||||
// file.
|
||||
|
||||
#include "pch.h"
|
||||
#include "winrt/base.h"
|
||||
|
||||
bool __stdcall winrt_can_unload_now() noexcept
|
||||
{
|
||||
if (winrt::get_module_lock())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
winrt::clear_factory_cache();
|
||||
return true;
|
||||
}
|
||||
|
||||
void* __stdcall winrt_get_activation_factory([[maybe_unused]] std::wstring_view const& name)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int32_t __stdcall WINRT_CanUnloadNow() noexcept
|
||||
{
|
||||
#ifdef _WRL_MODULE_H_
|
||||
if (!::Microsoft::WRL::Module<::Microsoft::WRL::InProc>::GetModule().Terminate())
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
return winrt_can_unload_now() ? 0 : 1;
|
||||
}
|
||||
|
||||
int32_t __stdcall WINRT_GetActivationFactory(void* classId, void** factory) noexcept try
|
||||
{
|
||||
std::wstring_view const name{ *reinterpret_cast<winrt::hstring*>(&classId) };
|
||||
*factory = winrt_get_activation_factory(name);
|
||||
|
||||
if (*factory)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef _WRL_MODULE_H_
|
||||
return ::Microsoft::WRL::Module<::Microsoft::WRL::InProc>::GetModule().GetActivationFactory(static_cast<HSTRING>(classId), reinterpret_cast<::IActivationFactory**>(factory));
|
||||
#else
|
||||
return winrt::hresult_class_not_available(name).to_abi();
|
||||
#endif
|
||||
}
|
||||
catch (...) { return winrt::to_hresult(); }
|
||||
@@ -0,0 +1,58 @@
|
||||
// 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 CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Microsoft.CommandPalette.UI.Models;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Windows.Foundation;
|
||||
|
||||
namespace Microsoft.CmdPal.UI.ViewModels;
|
||||
|
||||
public partial class AppStateModel : ObservableObject
|
||||
{
|
||||
private static string _filePath;
|
||||
|
||||
public event TypedEventHandler<AppStateModel, object?>? StateChanged;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// STATE HERE
|
||||
// Make sure that you make the setters public (JsonSerializer.Deserialize will fail silently otherwise)!
|
||||
// Make sure that any new types you add are added to JsonSerializationContext!
|
||||
public List<string> RunHistory { get; set; } = [];
|
||||
|
||||
// END STATE
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static AppStateModel()
|
||||
{
|
||||
_filePath = PersistenceService.SettingsJsonPath("state.json");
|
||||
}
|
||||
|
||||
public static AppStateModel LoadState(ILogger logger)
|
||||
{
|
||||
return PersistenceService.LoadObject<AppStateModel>(_filePath, JsonSerializationContext.Default.AppStateModel!, logger);
|
||||
}
|
||||
|
||||
public static void SaveState(AppStateModel model, ILogger logger)
|
||||
{
|
||||
try
|
||||
{
|
||||
PersistenceService.SaveObject(
|
||||
model,
|
||||
_filePath,
|
||||
JsonSerializationContext.Default.AppStateModel!,
|
||||
JsonSerializationContext.Default.AppStateModel!.Options,
|
||||
beforeWriteMutation: null,
|
||||
afterWriteCallback: m => m.StateChanged?.Invoke(m, null),
|
||||
logger);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log_SaveStateFailure(logger, _filePath, ex);
|
||||
}
|
||||
}
|
||||
|
||||
[LoggerMessage(Level = LogLevel.Error, Message = "Failed to save application state to '{filePath}'.")]
|
||||
static partial void Log_SaveStateFailure(ILogger logger, string filePath, Exception exception);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CommandPalette.UI.Models.Helpers;
|
||||
|
||||
public static class LayoutMapHelper
|
||||
{
|
||||
private static readonly global::PowerToys.Interop.LayoutMapManaged LayoutMap = new();
|
||||
|
||||
public static string GetKeyName(uint key)
|
||||
{
|
||||
return LayoutMap.GetKeyName(key);
|
||||
}
|
||||
|
||||
public static uint GetKeyValue(string key)
|
||||
{
|
||||
return LayoutMap.GetKeyValue(key);
|
||||
}
|
||||
|
||||
public static readonly uint VirtualKeyWindows = global::PowerToys.Interop.Constants.VK_WIN_BOTH;
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
// 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.Globalization;
|
||||
using System.Text;
|
||||
using System.Text.Json.Serialization;
|
||||
using Microsoft.CommandPalette.UI.Models.Helpers;
|
||||
|
||||
namespace Microsoft.CommandPalette.UI.Models;
|
||||
|
||||
public record HotkeySettings
|
||||
{
|
||||
private const int VKTAB = 0x09;
|
||||
|
||||
public HotkeySettings()
|
||||
{
|
||||
Win = false;
|
||||
Ctrl = false;
|
||||
Alt = false;
|
||||
Shift = false;
|
||||
Code = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="HotkeySettings"/> class.
|
||||
/// </summary>
|
||||
/// <param name="win">Should Windows key be used</param>
|
||||
/// <param name="ctrl">Should Ctrl key be used</param>
|
||||
/// <param name="alt">Should Alt key be used</param>
|
||||
/// <param name="shift">Should Shift key be used</param>
|
||||
/// <param name="code">Go to https://learn.microsoft.com/windows/win32/inputdev/virtual-key-codes to see list of v-keys</param>
|
||||
public HotkeySettings(bool win, bool ctrl, bool alt, bool shift, int code)
|
||||
{
|
||||
Win = win;
|
||||
Ctrl = ctrl;
|
||||
Alt = alt;
|
||||
Shift = shift;
|
||||
Code = code;
|
||||
}
|
||||
|
||||
[JsonPropertyName("win")]
|
||||
public bool Win { get; set; }
|
||||
|
||||
[JsonPropertyName("ctrl")]
|
||||
public bool Ctrl { get; set; }
|
||||
|
||||
[JsonPropertyName("alt")]
|
||||
public bool Alt { get; set; }
|
||||
|
||||
[JsonPropertyName("shift")]
|
||||
public bool Shift { get; set; }
|
||||
|
||||
[JsonPropertyName("code")]
|
||||
public int Code { get; set; }
|
||||
|
||||
// This is currently needed for FancyZones, we need to unify these two objects
|
||||
// see src\common\settings_objects.h
|
||||
[JsonPropertyName("key")]
|
||||
public string Key { get; set; } = string.Empty;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var output = new StringBuilder();
|
||||
|
||||
if (Win)
|
||||
{
|
||||
output.Append("Win + ");
|
||||
}
|
||||
|
||||
if (Ctrl)
|
||||
{
|
||||
output.Append("Ctrl + ");
|
||||
}
|
||||
|
||||
if (Alt)
|
||||
{
|
||||
output.Append("Alt + ");
|
||||
}
|
||||
|
||||
if (Shift)
|
||||
{
|
||||
output.Append("Shift + ");
|
||||
}
|
||||
|
||||
if (Code > 0)
|
||||
{
|
||||
var localKey = LayoutMapHelper.GetKeyName((uint)Code);
|
||||
output.Append(localKey);
|
||||
}
|
||||
else if (output.Length >= 2)
|
||||
{
|
||||
output.Remove(output.Length - 2, 2);
|
||||
}
|
||||
|
||||
return output.ToString();
|
||||
}
|
||||
|
||||
public List<object> GetKeysList()
|
||||
{
|
||||
var shortcutList = new List<object>();
|
||||
|
||||
if (Win)
|
||||
{
|
||||
shortcutList.Add(92); // The Windows key or button.
|
||||
}
|
||||
|
||||
if (Ctrl)
|
||||
{
|
||||
shortcutList.Add("Ctrl");
|
||||
}
|
||||
|
||||
if (Alt)
|
||||
{
|
||||
shortcutList.Add("Alt");
|
||||
}
|
||||
|
||||
if (Shift)
|
||||
{
|
||||
shortcutList.Add("Shift");
|
||||
|
||||
// shortcutList.Add(16); // The Shift key or button.
|
||||
}
|
||||
|
||||
if (Code > 0)
|
||||
{
|
||||
switch (Code)
|
||||
{
|
||||
// https://learn.microsoft.com/uwp/api/windows.system.virtualkey?view=winrt-20348
|
||||
case 38: // The Up Arrow key or button.
|
||||
case 40: // The Down Arrow key or button.
|
||||
case 37: // The Left Arrow key or button.
|
||||
case 39: // The Right Arrow key or button.
|
||||
// case 8: // The Back key or button.
|
||||
// case 13: // The Enter key or button.
|
||||
shortcutList.Add(Code);
|
||||
break;
|
||||
default:
|
||||
var localKey = LayoutMapHelper.GetKeyName((uint)Code);
|
||||
shortcutList.Add(localKey);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return shortcutList;
|
||||
}
|
||||
|
||||
public bool IsValid()
|
||||
{
|
||||
return IsAccessibleShortcut() ? false : (Alt || Ctrl || Win || Shift) && Code != 0;
|
||||
}
|
||||
|
||||
public bool IsEmpty()
|
||||
{
|
||||
return !Alt && !Ctrl && !Win && !Shift && Code == 0;
|
||||
}
|
||||
|
||||
public bool IsAccessibleShortcut()
|
||||
{
|
||||
// Shift+Tab and Tab are accessible shortcuts
|
||||
return (!Alt && !Ctrl && !Win && Shift && Code == VKTAB)
|
||||
|| (!Alt && !Ctrl && !Win && !Shift && Code == VKTAB);
|
||||
}
|
||||
|
||||
public static bool TryParseFromCmd(string cmd, out object? result)
|
||||
{
|
||||
bool win = false, ctrl = false, alt = false, shift = false;
|
||||
var code = 0;
|
||||
|
||||
var parts = cmd.Split('+');
|
||||
foreach (var part in parts)
|
||||
{
|
||||
switch (part.Trim().ToLower(CultureInfo.InvariantCulture))
|
||||
{
|
||||
case "win":
|
||||
win = true;
|
||||
break;
|
||||
case "ctrl":
|
||||
ctrl = true;
|
||||
break;
|
||||
case "alt":
|
||||
alt = true;
|
||||
break;
|
||||
case "shift":
|
||||
shift = true;
|
||||
break;
|
||||
default:
|
||||
if (!TryParseKeyCode(part, out code))
|
||||
{
|
||||
result = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
result = new HotkeySettings(win, ctrl, alt, shift, code);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryParseKeyCode(string key, out int keyCode)
|
||||
{
|
||||
// ASCII symbol
|
||||
if (key.Length == 1 && char.IsLetterOrDigit(key[0]))
|
||||
{
|
||||
keyCode = char.ToUpper(key[0], CultureInfo.InvariantCulture);
|
||||
return true;
|
||||
}
|
||||
|
||||
// VK code
|
||||
else if (key.Length == 4 && key.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return int.TryParse(key.AsSpan(2), NumberStyles.HexNumber, null, out keyCode);
|
||||
}
|
||||
|
||||
// Alias
|
||||
else
|
||||
{
|
||||
keyCode = (int)LayoutMapHelper.GetKeyValue(key);
|
||||
return keyCode != 0;
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryToCmdRepresentable(out string result)
|
||||
{
|
||||
result = ToString();
|
||||
result = result.Replace(" ", null);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// 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.Text.Json.Serialization;
|
||||
using Microsoft.CmdPal.UI.ViewModels;
|
||||
|
||||
namespace Microsoft.CommandPalette.UI.Models;
|
||||
|
||||
[JsonSerializable(typeof(float))]
|
||||
[JsonSerializable(typeof(int))]
|
||||
[JsonSerializable(typeof(string))]
|
||||
[JsonSerializable(typeof(bool))]
|
||||
[JsonSerializable(typeof(SettingsModel))]
|
||||
[JsonSerializable(typeof(WindowPosition))]
|
||||
[JsonSerializable(typeof(AppStateModel))]
|
||||
[JsonSerializable(typeof(List<string>), TypeInfoPropertyName = "StringList")]
|
||||
[JsonSerializable(typeof(Dictionary<string, object>), TypeInfoPropertyName = "Dictionary")]
|
||||
[JsonSourceGenerationOptions(UseStringEnumConverter = true, WriteIndented = true, IncludeFields = true, PropertyNameCaseInsensitive = true, AllowTrailingCommas = true)]
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:File may only contain a single type", Justification = "Just used here")]
|
||||
internal sealed partial class JsonSerializationContext : JsonSerializerContext
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// 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.
|
||||
|
||||
namespace Microsoft.CmdPal.UI.Models.Messages;
|
||||
|
||||
public record HotkeySummonMessage(string CommandId, IntPtr Hwnd)
|
||||
{
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user