Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

using Microsoft.CommandPalette.Extensions;
using Microsoft.CommandPalette.Extensions.Toolkit;
using Windows.System;
using Windows.Win32;

namespace SamplePagesExtension;

Expand Down Expand Up @@ -65,6 +67,67 @@ public override IListItem[] GetItems()
Subtitle = "and I'll take you to a page with markdown content",
Tags = [new Tag("Sample Tag")],
},

new ListItem(
new AnonymousCommand(() =>
{
var t = new ToastStatusMessage(new StatusMessage()
{
Message = "Primary command invoked",
State = MessageState.Info,
});
t.Show();
})
{
Result = CommandResult.KeepOpen(),
})
{
Title = "You can add context menu items too. Press Ctrl+k",
Subtitle = "Try pressing Ctrl+1 with me selected",
Icon = new IconInfo("\uE712"),
MoreCommands = [
new CommandContextItem(
new AnonymousCommand(() =>
{
var t = new ToastStatusMessage(new StatusMessage()
{
Message = "Secondary command invoked",
State = MessageState.Warning,
});
t.Show();
})
{
Name = "Secondary command",
Icon = new IconInfo("\uF147"), // Dial 2
Result = CommandResult.KeepOpen(),
})
{
Title = "I'm a second command",
RequestedShortcut = KeyChordHelpers.FromModifiers(ctrl: true, vkey: VirtualKey.Number1),
},
new CommandContextItem(
new AnonymousCommand(() =>
{
var t = new ToastStatusMessage(new StatusMessage()
{
Message = "Third command invoked",
State = MessageState.Error,
});
t.Show();
})
{
Name = "Do it",
Icon = new IconInfo("\uF148"), // dial 3
Result = CommandResult.KeepOpen(),
})
{
Title = "A third command too",
Icon = new IconInfo("\uF148"),
RequestedShortcut = KeyChordHelpers.FromModifiers(ctrl: true, vkey: VirtualKey.Number2),
}
],
},

new ListItem(new SendMessageCommand())
{
Title = "I send lots of messages",
Expand All @@ -91,7 +154,35 @@ public override IListItem[] GetItems()
})
{
Title = "Confirm twice before doing something",
}
},
new ListItem(
new AnonymousCommand(() =>
{
var fg = PInvoke.GetForegroundWindow();
var bufferSize = PInvoke.GetWindowTextLength(fg) + 1;
unsafe
{
fixed (char* windowNameChars = new char[bufferSize])
{
if (PInvoke.GetWindowText(fg, windowNameChars, bufferSize) == 0)
{
var emptyToast = new ToastStatusMessage(new StatusMessage() { Message = "FG Window didn't have a title", State = MessageState.Warning });
emptyToast.Show();
}

var windowName = new string(windowNameChars);
var nameToast = new ToastStatusMessage(new StatusMessage() { Message = $"FG Window is {windowName}", State = MessageState.Success });
nameToast.Show();
}
}
})
{
Result = CommandResult.KeepOpen(),
})
{
Title = "Get the name of the Foreground window",
},

];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@
<ProjectReference Include="..\..\extensionsdk\Microsoft.CommandPalette.Extensions.Toolkit\Microsoft.CommandPalette.Extensions.Toolkit.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Windows.CsWin32">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
</ItemGroup>
<!--
Defining the "Msix" ProjectCapability here allows the Single-project MSIX Packaging
Tools extension to be activated for this project even if the Windows App SDK Nuget
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,16 @@ namespace Microsoft.CmdPal.UI.ViewModels;

public partial class CommandContextItemViewModel(ICommandContextItem contextItem, WeakReference<IPageContext> context) : CommandItemViewModel(new(contextItem), context)
{
private readonly KeyChord nullKeyChord = new(0, 0, 0);

public new ExtensionObject<ICommandContextItem> Model { get; } = new(contextItem);

public bool IsCritical { get; private set; }

public KeyChord? RequestedShortcut { get; private set; }

public bool HasRequestedShortcut => RequestedShortcut != null && (RequestedShortcut.Value != nullKeyChord);

public override void InitializeProperties()
{
if (IsInitialized)
Expand All @@ -31,6 +35,9 @@ public override void InitializeProperties()
}

IsCritical = contextItem.IsCritical;

// I actually don't think this will ever actually be null, because
// KeyChord is a struct, which isn't nullable in WinRT
if (contextItem.RequestedShortcut != null)
{
RequestedShortcut = new(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,23 @@ public override void SafeCleanup()
base.SafeCleanup();
Initialized |= InitializedState.CleanedUp;
}

/// <summary>
/// Generates a mapping of key -> command item for this particular item's
/// MoreCommands. (This won't include the primary Command, but it will
/// include the secondary one). This map can be used to quickly check if a
/// shortcut key was pressed
/// </summary>
/// <returns>a dictionary of KeyChord -> Context commands, for all commands
/// that have a shortcut key set.</returns>
internal Dictionary<KeyChord, CommandContextItemViewModel> Keybindings()
{
return MoreCommands
.Where(c => c.HasRequestedShortcut)
.ToDictionary(
c => c.RequestedShortcut ?? new KeyChord(0, 0, 0),
c => c);
}
}

[Flags]
Expand Down
30 changes: 16 additions & 14 deletions src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/ListViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -128,15 +128,15 @@ private void FetchItems()

try
{
IListItem[] newItems = _model.Unsafe!.GetItems();
var newItems = _model.Unsafe!.GetItems();

// Collect all the items into new viewmodels
Collection<ListItemViewModel> newViewModels = [];

// TODO we can probably further optimize this by also keeping a
// HashSet of every ExtensionObject we currently have, and only
// building new viewmodels for the ones we haven't already built.
foreach (IListItem? item in newItems)
foreach (var item in newItems)
{
ListItemViewModel viewModel = new(item, new(this));

Expand All @@ -147,8 +147,8 @@ private void FetchItems()
}
}

IEnumerable<ListItemViewModel> firstTwenty = newViewModels.Take(20);
foreach (ListItemViewModel? item in firstTwenty)
var firstTwenty = newViewModels.Take(20);
foreach (var item in firstTwenty)
{
item?.SafeInitializeProperties();
}
Expand Down Expand Up @@ -233,7 +233,7 @@ private void InitializeItemsTask(CancellationToken ct)
iterable = Items.ToArray();
}

foreach (ListItemViewModel item in iterable)
foreach (var item in iterable)
{
ct.ThrowIfCancellationRequested();

Expand Down Expand Up @@ -266,8 +266,8 @@ private static int ScoreListItem(string query, CommandItemViewModel listItem)
return 1;
}

MatchResult nameMatch = StringMatcher.FuzzySearch(query, listItem.Title);
MatchResult descriptionMatch = StringMatcher.FuzzySearch(query, listItem.Subtitle);
var nameMatch = StringMatcher.FuzzySearch(query, listItem.Title);
var descriptionMatch = StringMatcher.FuzzySearch(query, listItem.Subtitle);
return new[] { nameMatch.Score, (descriptionMatch.Score - 4) / 2, 0 }.Max();
}

Expand All @@ -280,7 +280,7 @@ private struct ScoredListItemViewModel
// Similarly stolen from ListHelpers.FilterList
public static IEnumerable<ListItemViewModel> FilterList(IEnumerable<ListItemViewModel> items, string query)
{
IOrderedEnumerable<ScoredListItemViewModel> scores = items
var scores = items
.Where(i => !i.IsInErrorState)
.Select(li => new ScoredListItemViewModel() { ViewModel = li, Score = ScoreListItem(query, li) })
.Where(score => score.Score > 0)
Expand Down Expand Up @@ -342,6 +342,8 @@ private void UpdateSelectedItem(ListItemViewModel item)
{
WeakReferenceMessenger.Default.Send<UpdateCommandBarMessage>(new(item));

WeakReferenceMessenger.Default.Send<UpdateItemKeybindsMessage>(new(item.Keybindings()));
Comment thread Fixed

if (ShowDetails && item.HasDetails)
{
WeakReferenceMessenger.Default.Send<ShowDetailsMessage>(new(item.Details));
Expand All @@ -359,7 +361,7 @@ public override void InitializeProperties()
{
base.InitializeProperties();

IListPage? model = _model.Unsafe;
var model = _model.Unsafe;
if (model == null)
{
return; // throw?
Expand All @@ -385,7 +387,7 @@ public override void InitializeProperties()

public void LoadMoreIfNeeded()
{
IListPage? model = this._model.Unsafe;
var model = this._model.Unsafe;
if (model == null)
{
return;
Expand All @@ -412,7 +414,7 @@ protected override void FetchProperty(string propertyName)
{
base.FetchProperty(propertyName);

IListPage? model = this._model.Unsafe;
var model = this._model.Unsafe;
if (model == null)
{
return; // throw?
Expand Down Expand Up @@ -475,21 +477,21 @@ protected override void UnsafeCleanup()

lock (_listLock)
{
foreach (ListItemViewModel item in Items)
foreach (var item in Items)
{
item.SafeCleanup();
}

Items.Clear();
foreach (ListItemViewModel item in FilteredItems)
foreach (var item in FilteredItems)
{
item.SafeCleanup();
}

FilteredItems.Clear();
}

IListPage? model = _model.Unsafe;
var model = _model.Unsafe;
if (model != null)
{
model.ItemsChanged -= Model_ItemsChanged;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,12 @@ public PerformCommandMessage(ExtensionObject<ICommand> command, ExtensionObject<
Context = context.Unsafe;
}

public PerformCommandMessage(CommandContextItemViewModel contextCommand)
{
Command = contextCommand.Command.Model;
Context = contextCommand.Model.Unsafe;
}

public PerformCommandMessage(ConfirmResultViewModel vm)
{
Command = vm.PrimaryCommand.Model;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Copyright (c) Microsoft Corporation
Comment thread Fixed
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using Microsoft.CommandPalette.Extensions;

namespace Microsoft.CmdPal.UI.ViewModels.Messages;

public record UpdateItemKeybindsMessage(Dictionary<KeyChord, CommandContextItemViewModel>? Keys);
Comment thread Fixed
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,14 @@
Grid.Column="1"
VerticalAlignment="Center"
Text="{x:Bind Title, Mode=OneWay}" />
<!--<TextBlock
<TextBlock
Grid.Column="2"
Margin="16,0,0,0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Foreground="{ThemeResource MenuFlyoutItemKeyboardAcceleratorTextForeground}"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{x:Bind RequestedShortcut, Mode=OneWay, Converter={StaticResource KeyChordToStringConverter}}" />-->
Text="{x:Bind RequestedShortcut, Mode=OneWay, Converter={StaticResource KeyChordToStringConverter}}" />
</Grid>
</DataTemplate>

Expand Down
Loading