From bd2dc3af05991445bb1aaa610b2312aa1ea79c59 Mon Sep 17 00:00:00 2001 From: Mike Griese Date: Mon, 15 Sep 2025 06:15:00 -0500 Subject: [PATCH] add a photo picker --- .../Pages/ActionsTestPage.cs | 59 +++++++++++++------ .../Microsoft.CmdPal.Actions/Parameters.cs | 15 ++++- .../ext/SamplePagesExtension/Parameters.cs | 15 ++++- 3 files changed, 67 insertions(+), 22 deletions(-) diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Actions/Pages/ActionsTestPage.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Actions/Pages/ActionsTestPage.cs index 8e5f001d88..8968815aa9 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Actions/Pages/ActionsTestPage.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Actions/Pages/ActionsTestPage.cs @@ -13,6 +13,7 @@ using Windows.AI.Actions; using Windows.AI.Actions.Hosting; using Windows.ApplicationModel.Contacts; using Windows.Storage; +using Windows.Storage.Pickers; namespace Microsoft.CmdPal.Ext.Actions; @@ -42,8 +43,8 @@ internal sealed partial class ActionsTestPage : ListPage foreach (var action in actions) { var overloads = action.GetOverloads(); - var overloadsTxt = string.Join("\n\t", overloads.Select(o => o.DescriptionTemplate)); - actionsDebug += $"{action.Id}: {action.Description}\n\t{overloadsTxt}\n"; + var overloadsTxt = string.Join("\n", overloads.Select(o => $" - {o.DescriptionTemplate}")); + actionsDebug += $"* `{action.Id}`: {action.Description}\n{overloadsTxt}\n"; } Logger.LogDebug(actionsDebug); @@ -118,20 +119,7 @@ public partial class DoActionPage : ParametersPage var inputs = action.GetInputs(); foreach (var input in inputs) { - IParameterRun param = input.Kind switch - { - ActionEntityKind.None => new LabelRun(input.Name), - ActionEntityKind.Document => new FilePickerParameterRun() { PlaceholderText = input.Name }, - ActionEntityKind.File => new FilePickerParameterRun() { PlaceholderText = input.Name }, - ActionEntityKind.Photo => new FilePickerParameterRun() { PlaceholderText = input.Name }, - ActionEntityKind.Text => new StringParameterRun(input.Name), - - // ActionEntityKind.StreamingText => new CommandParameter(input.Name, input.Required, ParameterType.StreamingText), - // ActionEntityKind.RemoteFile => new CommandParameter(input.Name, input.Required, ParameterType.RemoteFile), - // ActionEntityKind.Table => new CommandParameter(input.Name, input.Required, ParameterType.Table), - ActionEntityKind.Contact => new StringParameterRun(input.Name), - _ => throw new NotSupportedException($"Unsupported action entity kind: {input.Kind}"), - }; + var param = GetParameter(input); _parameters.Add(param); if (param is ParameterValueRun p) @@ -146,6 +134,25 @@ public partial class DoActionPage : ParametersPage public override IListItem Command => new ListItem(_command) { Title = _overload.DescriptionTemplate }; public override IParameterRun[] Parameters => _parameters.ToArray(); + + internal static IParameterRun GetParameter(ActionEntityRegistrationInfo input) + { + IParameterRun param = input.Kind switch + { + ActionEntityKind.None => new LabelRun(input.Name), + ActionEntityKind.Document => new FilePickerParameterRun() { PlaceholderText = input.Name }, + ActionEntityKind.File => new FilePickerParameterRun() { PlaceholderText = input.Name }, + ActionEntityKind.Photo => new PhotoFilePicker() { PlaceholderText = input.Name }, + ActionEntityKind.Text => new StringParameterRun(input.Name), + + // ActionEntityKind.StreamingText => new CommandParameter(input.Name, input.Required, ParameterType.StreamingText), + // ActionEntityKind.RemoteFile => new CommandParameter(input.Name, input.Required, ParameterType.RemoteFile), + // ActionEntityKind.Table => new CommandParameter(input.Name, input.Required, ParameterType.Table), + ActionEntityKind.Contact => new StringParameterRun(input.Name), + _ => throw new NotSupportedException($"Unsupported action entity kind: {input.Kind}"), + }; + return param; + } } public partial class DoActionCommand : InvokableCommand @@ -232,7 +239,7 @@ public partial class DoActionCommand : InvokableCommand _actionParams = parameters; } - private ActionEntity CreateEntity(ActionEntityRegistrationInfo i, ActionEntityFactory f, object value) + private static ActionEntity CreateEntity(ActionEntityRegistrationInfo i, ActionEntityFactory f, object value) { var input = value switch { @@ -257,7 +264,7 @@ public partial class DoActionCommand : InvokableCommand return v; } - private ContactActionEntity CreateContact(string? text, ActionEntityFactory f) + private static ContactActionEntity CreateContact(string? text, ActionEntityFactory f) { var contact = new Contact(); var email = new ContactEmail(); @@ -267,6 +274,22 @@ public partial class DoActionCommand : InvokableCommand } } +internal sealed partial class PhotoFilePicker : FilePickerParameterRun +{ + protected override void ConfigureFilePicker(object? sender, FileOpenPicker picker) + { + picker.SuggestedStartLocation = PickerLocationId.PicturesLibrary; + picker.FileTypeFilter.Add(".jpg"); + picker.FileTypeFilter.Add(".jpeg"); + picker.FileTypeFilter.Add(".png"); + picker.FileTypeFilter.Add(".gif"); + picker.FileTypeFilter.Add(".bmp"); + picker.FileTypeFilter.Add(".tiff"); + picker.FileTypeFilter.Add(".webp"); + } +} + + // [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:File may only contain a single type", Justification = "meh")] // public partial class ImageParameter : CommandParameter // { diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Actions/Parameters.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Actions/Parameters.cs index 042d7b7066..65951c13d5 100644 --- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Actions/Parameters.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Actions/Parameters.cs @@ -195,7 +195,7 @@ public partial class CommandParameterRun : ParameterValueRun, ICommandParameterR } } -internal sealed partial class FilePickerParameterRun : CommandParameterRun +public partial class FilePickerParameterRun : CommandParameterRun { public StorageFile? File { get; private set; } @@ -203,6 +203,8 @@ internal sealed partial class FilePickerParameterRun : CommandParameterRun public override string? DisplayText { get => File != null ? File.DisplayName : "Select a file"; } + public Action? SetupFilePicker { get; set; } + public FilePickerParameterRun() { var command = new FilePickerCommand(); @@ -215,6 +217,7 @@ internal sealed partial class FilePickerParameterRun : CommandParameterRun OnPropertyChanged(nameof(NeedsValue)); OnPropertyChanged(nameof(DisplayText)); }; + command.RequestCustomizePicker += ConfigureFilePicker; PlaceholderText = "Select a file"; Icon = new IconInfo("\uE710"); // Add Command = command; @@ -233,6 +236,8 @@ internal sealed partial class FilePickerParameterRun : CommandParameterRun public event EventHandler? FileSelected; + public event EventHandler? RequestCustomizePicker; + private nint _hostHwnd; public void SetHostHwnd(nint hostHwnd) @@ -249,7 +254,8 @@ internal sealed partial class FilePickerParameterRun : CommandParameterRun private async void PickFileAsync() { var picker = new FileOpenPicker() { }; - picker.FileTypeFilter.Add("*"); + + RequestCustomizePicker?.Invoke(this, picker); // You need to initialize the picker with a window handle in WinUI 3 desktop apps // See https://learn.microsoft.com/en-us/windows/apps/design/controls/file-open-picker @@ -259,6 +265,11 @@ internal sealed partial class FilePickerParameterRun : CommandParameterRun FileSelected?.Invoke(this, file); } } + + protected virtual void ConfigureFilePicker(object? sender, FileOpenPicker picker) + { + picker.FileTypeFilter.Add("*"); + } } public partial class SelectParameterCommand : InvokableCommand diff --git a/src/modules/cmdpal/ext/SamplePagesExtension/Parameters.cs b/src/modules/cmdpal/ext/SamplePagesExtension/Parameters.cs index 042d7b7066..65951c13d5 100644 --- a/src/modules/cmdpal/ext/SamplePagesExtension/Parameters.cs +++ b/src/modules/cmdpal/ext/SamplePagesExtension/Parameters.cs @@ -195,7 +195,7 @@ public partial class CommandParameterRun : ParameterValueRun, ICommandParameterR } } -internal sealed partial class FilePickerParameterRun : CommandParameterRun +public partial class FilePickerParameterRun : CommandParameterRun { public StorageFile? File { get; private set; } @@ -203,6 +203,8 @@ internal sealed partial class FilePickerParameterRun : CommandParameterRun public override string? DisplayText { get => File != null ? File.DisplayName : "Select a file"; } + public Action? SetupFilePicker { get; set; } + public FilePickerParameterRun() { var command = new FilePickerCommand(); @@ -215,6 +217,7 @@ internal sealed partial class FilePickerParameterRun : CommandParameterRun OnPropertyChanged(nameof(NeedsValue)); OnPropertyChanged(nameof(DisplayText)); }; + command.RequestCustomizePicker += ConfigureFilePicker; PlaceholderText = "Select a file"; Icon = new IconInfo("\uE710"); // Add Command = command; @@ -233,6 +236,8 @@ internal sealed partial class FilePickerParameterRun : CommandParameterRun public event EventHandler? FileSelected; + public event EventHandler? RequestCustomizePicker; + private nint _hostHwnd; public void SetHostHwnd(nint hostHwnd) @@ -249,7 +254,8 @@ internal sealed partial class FilePickerParameterRun : CommandParameterRun private async void PickFileAsync() { var picker = new FileOpenPicker() { }; - picker.FileTypeFilter.Add("*"); + + RequestCustomizePicker?.Invoke(this, picker); // You need to initialize the picker with a window handle in WinUI 3 desktop apps // See https://learn.microsoft.com/en-us/windows/apps/design/controls/file-open-picker @@ -259,6 +265,11 @@ internal sealed partial class FilePickerParameterRun : CommandParameterRun FileSelected?.Invoke(this, file); } } + + protected virtual void ConfigureFilePicker(object? sender, FileOpenPicker picker) + { + picker.FileTypeFilter.Add("*"); + } } public partial class SelectParameterCommand : InvokableCommand