mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-02-24 04:00:02 +01:00
Make some things observable
This commit is contained in:
@@ -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.CmdPal.Common.Messages;
|
||||
|
||||
public partial class GetHwndMessage
|
||||
{
|
||||
public nint Hwnd { get; set; } = 0;
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
using System.Collections.ObjectModel;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Microsoft.CmdPal.Common.Messages;
|
||||
using Microsoft.CmdPal.Core.ViewModels.Messages;
|
||||
using Microsoft.CmdPal.Core.ViewModels.Models;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
@@ -17,15 +18,51 @@ namespace Microsoft.CmdPal.Core.ViewModels;
|
||||
|
||||
public abstract partial class ParameterRunViewModel : ExtensionObjectViewModel
|
||||
{
|
||||
private ExtensionObject<IParameterRun> _model;
|
||||
|
||||
internal InitializedState Initialized { get; set; } = InitializedState.Uninitialized;
|
||||
|
||||
protected bool IsInitialized => IsInErrorState || Initialized.HasFlag(InitializedState.Initialized);
|
||||
|
||||
public bool IsInErrorState => Initialized.HasFlag(InitializedState.Error);
|
||||
|
||||
internal ParameterRunViewModel(WeakReference<IPageContext> context)
|
||||
internal ParameterRunViewModel(IParameterRun model, WeakReference<IPageContext> context)
|
||||
: base(context)
|
||||
{
|
||||
_model = new(model);
|
||||
}
|
||||
|
||||
public override void InitializeProperties()
|
||||
{
|
||||
if (IsInitialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var model = _model.Unsafe;
|
||||
if (model == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
model.PropChanged += Model_PropChanged;
|
||||
}
|
||||
|
||||
private void Model_PropChanged(object sender, IPropChangedEventArgs args)
|
||||
{
|
||||
try
|
||||
{
|
||||
FetchProperty(args.PropertyName);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ShowException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void FetchProperty(string propertyName)
|
||||
{
|
||||
// Override in derived classes
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +73,7 @@ public partial class LabelRunViewModel : ParameterRunViewModel
|
||||
public string Text { get; set; } = string.Empty;
|
||||
|
||||
public LabelRunViewModel(ILabelRun labelRun, WeakReference<IPageContext> context)
|
||||
: base(context)
|
||||
: base(labelRun, context)
|
||||
{
|
||||
_model = new(labelRun);
|
||||
}
|
||||
@@ -70,7 +107,7 @@ public partial class ParameterValueRunViewModel : ParameterRunViewModel
|
||||
public bool NeedsValue { get; set; }
|
||||
|
||||
public ParameterValueRunViewModel(IParameterValueRun valueRun, WeakReference<IPageContext> context)
|
||||
: base(context)
|
||||
: base(valueRun, context)
|
||||
{
|
||||
_model = new(valueRun);
|
||||
}
|
||||
@@ -147,18 +184,31 @@ public partial class StringParameterRunViewModel : ParameterValueRunViewModel
|
||||
}
|
||||
}
|
||||
|
||||
public partial class CommandParameterRunViewModel : ParameterValueRunViewModel
|
||||
public partial class CommandParameterRunViewModel : ParameterValueRunViewModel, IDisposable
|
||||
{
|
||||
private ExtensionObject<ICommandParameterRun> _model;
|
||||
|
||||
private ListViewModel? _listViewModel;
|
||||
private CommandViewModel? _commandViewModel;
|
||||
private AppExtensionHost _extensionHost;
|
||||
|
||||
public string DisplayText { get; set; } = string.Empty;
|
||||
|
||||
public IconInfoViewModel Icon { get; set; } = new(null);
|
||||
|
||||
public CommandParameterRunViewModel(ICommandParameterRun commandRun, WeakReference<IPageContext> context)
|
||||
public string ButtonLabel => !string.IsNullOrEmpty(DisplayText) ? DisplayText : string.Empty;
|
||||
|
||||
public string SearchBoxText
|
||||
{
|
||||
get => GetSearchText();
|
||||
set => SetSearchText(value);
|
||||
}
|
||||
|
||||
public CommandParameterRunViewModel(ICommandParameterRun commandRun, WeakReference<IPageContext> context, AppExtensionHost extensionHost)
|
||||
: base(commandRun, context)
|
||||
{
|
||||
_model = new(commandRun);
|
||||
_extensionHost = extensionHost;
|
||||
}
|
||||
|
||||
public override void InitializeProperties()
|
||||
@@ -182,9 +232,71 @@ public partial class CommandParameterRunViewModel : ParameterValueRunViewModel
|
||||
Icon.InitializeProperties();
|
||||
}
|
||||
|
||||
GetHwndMessage msg = new();
|
||||
WeakReferenceMessenger.Default.Send(msg);
|
||||
var command = commandRun.GetSelectValueCommand((ulong)msg.Hwnd);
|
||||
if (command == null)
|
||||
{
|
||||
}
|
||||
else if (command is IListPage list)
|
||||
{
|
||||
if (PageContext.TryGetTarget(out var pageContext))
|
||||
{
|
||||
_listViewModel = new ListViewModel(list, pageContext.Scheduler, _extensionHost);
|
||||
_listViewModel.InitializeProperties();
|
||||
}
|
||||
}
|
||||
else if (command is IInvokableCommand invokable)
|
||||
{
|
||||
_commandViewModel = new CommandViewModel(invokable, this.PageContext);
|
||||
_commandViewModel.InitializeProperties();
|
||||
}
|
||||
|
||||
UpdateProperty(nameof(DisplayText));
|
||||
UpdateProperty(nameof(Icon));
|
||||
}
|
||||
|
||||
protected override void FetchProperty(string propertyName)
|
||||
{
|
||||
var model = this._model.Unsafe;
|
||||
if (model is null)
|
||||
{
|
||||
return; // throw?
|
||||
}
|
||||
|
||||
switch (propertyName)
|
||||
{
|
||||
case nameof(ICommandParameterRun.DisplayText):
|
||||
DisplayText = model.DisplayText;
|
||||
break;
|
||||
case nameof(ICommandParameterRun.Icon):
|
||||
Icon = new(model.Icon);
|
||||
if (Icon is not null)
|
||||
{
|
||||
Icon.InitializeProperties();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
UpdateProperty(propertyName);
|
||||
}
|
||||
|
||||
private string GetSearchText()
|
||||
{
|
||||
return _listViewModel?.SearchText ?? string.Empty;
|
||||
}
|
||||
|
||||
private void SetSearchText(string value)
|
||||
{
|
||||
_listViewModel?.SearchTextBox = value;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
GC.SuppressFinalize(this);
|
||||
_listViewModel?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public partial class ParametersPageViewModel : PageViewModel, IDisposable
|
||||
@@ -257,7 +369,7 @@ public partial class ParametersPageViewModel : PageViewModel, IDisposable
|
||||
{
|
||||
ILabelRun labelRun => new LabelRunViewModel(labelRun, PageContext),
|
||||
IStringParameterRun stringRun => new StringParameterRunViewModel(stringRun, PageContext),
|
||||
ICommandParameterRun commandRun => new CommandParameterRunViewModel(commandRun, PageContext),
|
||||
ICommandParameterRun commandRun => new CommandParameterRunViewModel(commandRun, PageContext, this.ExtensionHost),
|
||||
_ => null,
|
||||
};
|
||||
if (itemVm != null)
|
||||
|
||||
@@ -42,6 +42,19 @@
|
||||
Style="{StaticResource SearchTextBoxStyle}"
|
||||
Text="{x:Bind Text, Mode=TwoWay}" />
|
||||
</DataTemplate>
|
||||
<DataTemplate x:Key="ListParamTemplate" x:DataType="coreVm:CommandParameterRunViewModel">
|
||||
<TextBox
|
||||
MinHeight="32"
|
||||
Padding="4,2"
|
||||
VerticalAlignment="Center"
|
||||
VerticalContentAlignment="Stretch"
|
||||
BorderBrush="{ThemeResource AccentTextFillColorPrimaryBrush}"
|
||||
BorderThickness="1"
|
||||
Foreground="{ThemeResource AccentTextFillColorPrimaryBrush}"
|
||||
PlaceholderText="{x:Bind PlaceholderText, Mode=OneWay}"
|
||||
Style="{StaticResource SearchTextBoxStyle}"
|
||||
Text="{x:Bind SearchBoxText, Mode=TwoWay}" />
|
||||
</DataTemplate>
|
||||
<cpcontrols:ParameterRunTemplateSelector
|
||||
x:Key="ParameterRunTemplateSelector"
|
||||
ButtonParamTemplate="{StaticResource ButtonParamTemplate}"
|
||||
|
||||
@@ -46,6 +46,7 @@ public sealed partial class MainWindow : WindowEx,
|
||||
IRecipient<ShowWindowMessage>,
|
||||
IRecipient<HideWindowMessage>,
|
||||
IRecipient<QuitMessage>,
|
||||
IRecipient<GetHwndMessage>,
|
||||
IDisposable
|
||||
{
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore", Justification = "Stylistically, window messages are WM_")]
|
||||
@@ -87,6 +88,7 @@ public sealed partial class MainWindow : WindowEx,
|
||||
WeakReferenceMessenger.Default.Register<QuitMessage>(this);
|
||||
WeakReferenceMessenger.Default.Register<ShowWindowMessage>(this);
|
||||
WeakReferenceMessenger.Default.Register<HideWindowMessage>(this);
|
||||
WeakReferenceMessenger.Default.Register<GetHwndMessage>(this);
|
||||
|
||||
// Hide our titlebar.
|
||||
// We need to both ExtendsContentIntoTitleBar, then set the height to Collapsed
|
||||
@@ -747,4 +749,9 @@ public sealed partial class MainWindow : WindowEx,
|
||||
_localKeyboardListener.Dispose();
|
||||
DisposeAcrylic();
|
||||
}
|
||||
|
||||
public void Receive(GetHwndMessage message)
|
||||
{
|
||||
message.Hwnd = this.GetWindowHandle();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +48,38 @@ public sealed partial class SimpleParameterTest : ParametersPage
|
||||
public override IListItem Command => new ListItem(_command);
|
||||
}
|
||||
|
||||
public sealed partial class ButtonParameterTest : ParametersPage
|
||||
{
|
||||
private readonly CommandParameterRun _stringParameter;
|
||||
private readonly InvokableCommand _command;
|
||||
|
||||
public ButtonParameterTest()
|
||||
{
|
||||
Name = "Open";
|
||||
_stringParameter = new FilePickerParameterRun();
|
||||
|
||||
_command = new AnonymousCommand(() =>
|
||||
{
|
||||
var input = _stringParameter.Value?.ToString();
|
||||
var toast = new ToastStatusMessage(new StatusMessage() { Message = $"You entered: '{input}'" });
|
||||
toast.Show();
|
||||
})
|
||||
{
|
||||
Name = "Submit",
|
||||
Icon = new IconInfo("\uE724"), // Send
|
||||
Result = CommandResult.KeepOpen(),
|
||||
};
|
||||
}
|
||||
|
||||
public override IParameterRun[] Parameters => new IParameterRun[]
|
||||
{
|
||||
new LabelRun("Enter a value:"),
|
||||
_stringParameter,
|
||||
};
|
||||
|
||||
public override IListItem Command => new ListItem(_command);
|
||||
}
|
||||
|
||||
public sealed partial class CreateNoteParametersPage : ParametersPage
|
||||
{
|
||||
private readonly SelectFolderPage _selectFolderPage = new();
|
||||
|
||||
@@ -95,7 +95,7 @@ public partial class StringParameterRun : ParameterValueRun, IStringParameterRun
|
||||
}
|
||||
}
|
||||
|
||||
public partial class CommandParameterRun : ParameterValueRun
|
||||
public partial class CommandParameterRun : ParameterValueRun, ICommandParameterRun
|
||||
{
|
||||
private string? _displayText;
|
||||
|
||||
@@ -164,35 +164,25 @@ internal sealed partial class FilePickerParameterRun : CommandParameterRun
|
||||
{
|
||||
public StorageFile? File { get; private set; }
|
||||
|
||||
public override string? DisplayText { get => File != null ? File.DisplayName : "Select a file"; }
|
||||
|
||||
public FilePickerParameterRun()
|
||||
{
|
||||
var command = new FilePickerCommand();
|
||||
command.FileSelected += (s, file) =>
|
||||
{
|
||||
File = file;
|
||||
if (file != null)
|
||||
{
|
||||
Value = file;
|
||||
DisplayText = file.Name;
|
||||
|
||||
// Icon = new IconInfo("File");
|
||||
}
|
||||
else
|
||||
{
|
||||
Value = null;
|
||||
DisplayText = null;
|
||||
|
||||
// Icon = new IconInfo("File");
|
||||
}
|
||||
Value = file != null ? file : (object?)null;
|
||||
OnPropertyChanged(nameof(DisplayText));
|
||||
};
|
||||
PlaceholderText = "Select a file";
|
||||
Icon = new IconInfo("File");
|
||||
Icon = new IconInfo("\uE710"); // Add
|
||||
Command = command;
|
||||
}
|
||||
|
||||
private sealed partial class FilePickerCommand : InvokableCommand, IRequiresHostHwnd
|
||||
{
|
||||
public override IconInfo Icon => new("File");
|
||||
public override IconInfo Icon => new("\uE710"); // Add
|
||||
|
||||
public override string Name => "Pick a file";
|
||||
|
||||
|
||||
@@ -95,6 +95,11 @@ public partial class SamplesListPage : ListPage
|
||||
Title = "Sample parameters page",
|
||||
Subtitle = "A demo of a command that takes simple parameters",
|
||||
},
|
||||
new ListItem(new ButtonParameterTest())
|
||||
{
|
||||
Title = "Button parameters page",
|
||||
Subtitle = "A demo of a command that takes simple parameters",
|
||||
},
|
||||
|
||||
// Evil edge cases
|
||||
// Anything weird that might break the palette - put that in here.
|
||||
|
||||
Reference in New Issue
Block a user