Add Open in Notepad++ and static Windows shell verbs without embedding IContextMenu on right-click.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-26 14:24:00 +02:00
parent e2916aef9c
commit 66ea25993f
16 changed files with 1290 additions and 3 deletions

View File

@@ -34,6 +34,9 @@
<Button Content="Open in Cursor" MinWidth="120" Height="32"
Command="{Binding OpenSelectedInCursorCommand}"
IsEnabled="{Binding CanOpenInCursor}" Margin="0,0,8,8"/>
<Button Content="Open in Notepad++" MinWidth="140" Height="32"
Command="{Binding OpenSelectedInNotepadPlusPlusCommand}"
IsEnabled="{Binding CanOpenInCursor}" Margin="0,0,8,8"/>
<Button Content="Stage" MinWidth="72" Height="32" Click="OnStage"
IsEnabled="{Binding CanStage}" Margin="0,0,8,8"/>
<Button Content="Unstage" MinWidth="72" Height="32" Click="OnUnstage"
@@ -58,6 +61,8 @@
<MenuItem Header="View diff" Click="OnDiff" IsEnabled="{Binding CanDiff}"/>
<MenuItem Header="Open in Cursor" Command="{Binding OpenSelectedInCursorCommand}"
IsEnabled="{Binding CanOpenInCursor}"/>
<MenuItem Header="Open in Notepad++" Command="{Binding OpenSelectedInNotepadPlusPlusCommand}"
IsEnabled="{Binding CanOpenInCursor}"/>
<Separator/>
<MenuItem Header="Stage" Click="OnStage" IsEnabled="{Binding CanStage}"/>
<MenuItem Header="Unstage" Click="OnUnstage" IsEnabled="{Binding CanUnstage}"/>

View File

@@ -26,7 +26,7 @@
<Window.Resources>
<ContextMenu x:Key="FolderListContextMenu" x:Shared="false">
<MenuItem Header="Open" Click="OnCtxOpen"/>
<Separator/>
<Separator Tag="ShellVerbAnchor" Visibility="Collapsed"/>
<MenuItem Header="Cut" Command="{Binding CutCommand}"/>
<MenuItem Header="Copy" Command="{Binding CopyCommand}"/>
<MenuItem Header="Paste" Command="{Binding PasteCommand}"/>
@@ -74,6 +74,8 @@
Visibility="{Binding ShowOpenTerminal, Converter={StaticResource BoolVis}}"/>
<MenuItem Header="Open in Cursor" Command="{Binding OpenInCursorCommand}"
Visibility="{Binding ShowOpenInCursor, Converter={StaticResource BoolVis}}"/>
<MenuItem Header="Open in Notepad++" Command="{Binding OpenInNotepadPlusPlusCommand}"
Visibility="{Binding ShowOpenInNotepadPlusPlus, Converter={StaticResource BoolVis}}"/>
<MenuItem Header="Add to Favorites" Click="OnAddFavorite"
Visibility="{Binding ShowAddFavorite, Converter={StaticResource BoolVis}}"/>
<MenuItem Header="Remove from Favorites" Click="OnRemoveFavorite"
@@ -191,6 +193,8 @@
IsEnabled="{Binding ShowOpenTerminal}"/>
<MenuItem Header="Open in _Cursor" Command="{Binding OpenInCursorCommand}"
IsEnabled="{Binding ShowOpenInCursor}"/>
<MenuItem Header="Open in _Notepad++" Command="{Binding OpenInNotepadPlusPlusCommand}"
IsEnabled="{Binding ShowOpenInNotepadPlusPlus}"/>
</MenuItem>
<MenuItem Header="_Recycle Bin">
<MenuItem Header="_Open Recycle Bin" Click="OnOpenRecycleBin"/>

View File

@@ -8,6 +8,7 @@ using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
using Explorer.Domain;
using Explorer.Domain.Abstractions;
using Explorer.Presentation;
using Explorer.Presentation.ViewModels;
@@ -651,6 +652,15 @@ public partial class MainWindow : Window
if (sender is FrameworkElement { ContextMenu: { } menu })
{
menu.DataContext = DataContext;
try
{
FillShellContextVerbs(menu);
}
catch (Exception)
{
// Shell extras must not take down the Workbench menu.
}
_ = FillRunProfileMenuAsync(menu);
}
}
@@ -1364,6 +1374,66 @@ public partial class MainWindow : Window
dlg.ShowDialog();
}
private void FillShellContextVerbs(ContextMenu menu)
{
const string anchor = "ShellVerbAnchor";
const string tagPrefix = "shell:";
for (var i = menu.Items.Count - 1; i >= 0; i--)
{
if (menu.Items[i] is FrameworkElement { Tag: string tag }
&& tag.StartsWith(tagPrefix, StringComparison.Ordinal))
{
menu.Items.RemoveAt(i);
}
}
var anchorItem = menu.Items.OfType<Separator>().FirstOrDefault(item => Equals(item.Tag, anchor));
if (anchorItem is null)
{
return;
}
var verbs = Vm.ListShellContextVerbs();
anchorItem.Visibility = verbs.Count > 0 ? Visibility.Visible : Visibility.Collapsed;
if (verbs.Count == 0)
{
return;
}
var index = menu.Items.IndexOf(anchorItem) + 1;
foreach (var verb in verbs)
{
menu.Items.Insert(index++, BuildShellMenuItem(verb));
}
}
private MenuItem BuildShellMenuItem(ShellContextVerb verb)
{
var item = new MenuItem { Header = verb.Label, Tag = "shell:" + verb.Id };
if (verb.Children is { Count: > 0 })
{
foreach (var child in verb.Children)
{
item.Items.Add(BuildShellMenuItem(child));
}
return item;
}
item.Click += OnShellContextVerb;
return item;
}
private void OnShellContextVerb(object sender, RoutedEventArgs e)
{
if (sender is not MenuItem { Tag: string tag } || !tag.StartsWith("shell:", StringComparison.Ordinal))
{
return;
}
Vm.InvokeShellContextVerb(tag["shell:".Length..]);
}
private async Task FillRunProfileMenuAsync(ContextMenu menu)
{
var host = menu.Items.OfType<MenuItem>().FirstOrDefault(i => Equals(i.Tag, "RunProfileMenu"));