Files
terminal/src/cascadia/TerminalApp/CommandPalette.xaml
Leonard Hecker eae5fbd83d Implement custom text context menus to fix crashes (#18854)
This works around a bug in WinUI where it creates a single context
menu/flyout for text elements per thread, not per `XamlRoot`, similar to
many other areas. Since the `XamlRoot` cannot change after creation,
this means that once you've opened the flyout, you're locked into that
window (= XAML root) forever. You can't open the flyout in another
window and once you've closed that window, you can't open it anywhere at
all.

Closes #18599

* Flies out right click in the
  * About dialog 
  * Search dialog 
  * Word delimiters setting 
  * Launch size setting 
* Across two windows 

(cherry picked from commit 076746a7a6)
Service-Card-Id: PVTI_lADOAF3p4s4Axadtzgd5l-U
Service-Version: 1.23
2025-08-21 17:45:34 -05:00

373 lines
18 KiB
XML

<!--
Copyright (c) Microsoft Corporation. All rights reserved. Licensed under
the MIT License. See LICENSE in the project root for license information.
-->
<UserControl x:Class="TerminalApp.CommandPalette"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:SettingsModel="using:Microsoft.Terminal.Settings.Model"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:TerminalApp"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:mtu="using:Microsoft.Terminal.UI"
xmlns:mux="using:Microsoft.UI.Xaml.Controls"
AllowFocusOnInteraction="True"
AutomationProperties.Name="{x:Bind ControlName, Mode=OneWay}"
IsTabStop="True"
LostFocus="_lostFocusHandler"
PointerPressed="_rootPointerPressed"
PreviewKeyDown="_previewKeyDownHandler"
PreviewKeyUp="_keyUpHandler"
TabNavigation="Cycle"
mc:Ignorable="d">
<UserControl.Resources>
<ResourceDictionary>
<!-- KeyChordText styles -->
<Style x:Key="KeyChordBorderStyle"
TargetType="Border">
<Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="2" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderBrush" Value="{ThemeResource SystemControlForegroundBaseMediumBrush}" />
</Style>
<Style x:Key="KeyChordTextBlockStyle"
TargetType="TextBlock">
<Setter Property="Foreground" Value="{ThemeResource SystemControlForegroundBaseMediumBrush}" />
</Style>
<!-- ParsedCommandLineText styles -->
<Style x:Key="ParsedCommandLineBorderStyle"
TargetType="Border">
<Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="{ThemeResource ControlCornerRadius}" />
<Setter Property="Background" Value="{ThemeResource CardBackgroundFillColorDefaultBrush}" />
<Setter Property="BorderBrush" Value="{ThemeResource CardStrokeColorDefaultBrush}" />
</Style>
<DataTemplate x:Key="ListItemTemplate"
x:DataType="local:FilteredCommand">
<ListViewItem HorizontalContentAlignment="Stretch"
AutomationProperties.AcceleratorKey="{x:Bind Item.KeyChordText, Mode=OneWay}"
AutomationProperties.Name="{x:Bind Item.Name, Mode=OneWay}" />
</DataTemplate>
<DataTemplate x:Key="GeneralItemTemplate"
x:DataType="local:FilteredCommand">
<Grid HorizontalAlignment="Stretch"
ColumnSpacing="8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="16" />
<!-- icon -->
<ColumnDefinition Width="Auto" />
<!-- command label -->
<ColumnDefinition Width="*" />
<!-- key chord -->
<ColumnDefinition Width="16" />
<!-- gutter for scrollbar -->
</Grid.ColumnDefinitions>
<ContentPresenter Grid.Column="0"
Width="16"
Height="16"
Content="{x:Bind Item.ResolvedIcon, Mode=OneWay}" />
<local:HighlightedTextControl Grid.Column="1"
HorizontalAlignment="Left"
Text="{x:Bind HighlightedName, Mode=OneWay}" />
<!--
The block for the key chord is only visible
when there's actual text set as the label.
We're setting the accessibility view on the
border and text block to Raw because otherwise,
Narrator will read out the key chord. Problem is,
it already did that because it was the list item's
"AcceleratorKey". It's redundant.
-->
<Border Grid.Column="2"
Padding="2,0,2,0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
AutomationProperties.AccessibilityView="Raw"
Background="{ThemeResource FlyoutPresenterBackground}"
Style="{StaticResource KeyChordBorderStyle}"
Visibility="{x:Bind mtu:Converters.StringNotEmptyToVisibility(Item.KeyChordText), Mode=OneWay}">
<TextBlock AutomationProperties.AccessibilityView="Raw"
FontSize="12"
Style="{StaticResource KeyChordTextBlockStyle}"
Text="{x:Bind Item.KeyChordText, Mode=OneWay}" />
</Border>
</Grid>
</DataTemplate>
<DataTemplate x:Key="NestedItemTemplate"
x:DataType="local:FilteredCommand">
<Grid HorizontalAlignment="Stretch"
ColumnSpacing="8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="16" />
<!-- icon -->
<ColumnDefinition Width="Auto" />
<!-- command label -->
<ColumnDefinition Width="*" />
<!-- key chord -->
<ColumnDefinition Width="16" />
<!-- gutter for scrollbar -->
</Grid.ColumnDefinitions>
<ContentPresenter Grid.Column="0"
Width="16"
Height="16"
Content="{x:Bind Item.ResolvedIcon, Mode=OneWay}" />
<local:HighlightedTextControl Grid.Column="1"
HorizontalAlignment="Left"
Text="{x:Bind HighlightedName, Mode=OneWay}" />
<!--
The block for the key chord is only visible
when there's actual text set as the label.
We're setting the accessibility view on the
border and text block to Raw because otherwise,
Narrator will read out the key chord. Problem is,
it already did that because it was the list item's
"AcceleratorKey". It's redundant.
-->
<Border Grid.Column="2"
Padding="2,0,2,0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
AutomationProperties.AccessibilityView="Raw"
Style="{StaticResource KeyChordBorderStyle}"
Visibility="{x:Bind mtu:Converters.StringNotEmptyToVisibility(Item.KeyChordText), Mode=OneWay}">
<TextBlock AutomationProperties.AccessibilityView="Raw"
FontSize="12"
Style="{StaticResource KeyChordTextBlockStyle}"
Text="{x:Bind Item.KeyChordText, Mode=OneWay}" />
</Border>
<FontIcon Grid.Column="2"
HorizontalAlignment="Right"
FontFamily="{ThemeResource SymbolThemeFontFamily}"
FontSize="12"
Glyph="&#xE76C;" />
</Grid>
</DataTemplate>
<DataTemplate x:Key="TabItemTemplate"
x:DataType="local:FilteredCommand">
<Grid HorizontalAlignment="Stretch"
AutomationProperties.Name="{x:Bind Item.Name, Mode=OneWay}"
ColumnSpacing="8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="16" />
<!-- icon / progress -->
<ColumnDefinition Width="Auto" />
<!-- command label -->
<ColumnDefinition Width="*" />
<!-- gutter for indicators -->
<ColumnDefinition Width="Auto" />
<!-- Indicators -->
<ColumnDefinition Width="16" />
<!-- gutter for scrollbar -->
</Grid.ColumnDefinitions>
<mux:ProgressRing Grid.Column="0"
Width="15"
Height="15"
MinWidth="0"
MinHeight="0"
IsActive="{x:Bind Item.(local:TabPaletteItem.TabStatus).IsProgressRingActive, Mode=OneWay}"
IsIndeterminate="{x:Bind Item.(local:TabPaletteItem.TabStatus).IsProgressRingIndeterminate, Mode=OneWay}"
Visibility="{x:Bind Item.(local:TabPaletteItem.TabStatus).IsProgressRingActive, Mode=OneWay}"
Value="{x:Bind Item.(local:TabPaletteItem.TabStatus).ProgressValue, Mode=OneWay}" />
<ContentPresenter Grid.Column="0"
Width="16"
Height="16"
Content="{x:Bind Item.ResolvedIcon, Mode=OneWay}" />
<local:HighlightedTextControl Grid.Column="1"
HorizontalAlignment="Left"
Text="{x:Bind HighlightedName, Mode=OneWay}" />
<StackPanel Grid.Column="2"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Orientation="Horizontal">
<FontIcon Margin="0,0,8,0"
FontFamily="{ThemeResource SymbolThemeFontFamily}"
FontSize="12"
Glyph="&#xEA8F;"
Visibility="{x:Bind Item.(local:TabPaletteItem.TabStatus).BellIndicator, Mode=OneWay}" />
<FontIcon Margin="0,0,8,0"
FontFamily="{ThemeResource SymbolThemeFontFamily}"
FontSize="12"
Glyph="&#xE8A3;"
Visibility="{x:Bind Item.(local:TabPaletteItem.TabStatus).IsPaneZoomed, Mode=OneWay}" />
<FontIcon x:Name="HeaderLockIcon"
Margin="0,0,8,0"
FontFamily="{ThemeResource SymbolThemeFontFamily}"
FontSize="12"
Glyph="&#xE72E;"
Visibility="{x:Bind Item.(local:TabPaletteItem.TabStatus).IsReadOnlyActive, Mode=OneWay}" />
<FontIcon x:Name="HeaderBroadcastIcon"
Margin="0,0,8,0"
FontFamily="Segoe MDL2 Assets"
FontSize="12"
Glyph="&#xEC05;"
Visibility="{x:Bind Item.(local:TabPaletteItem.TabStatus).IsInputBroadcastActive, Mode=OneWay}" />
</StackPanel>
<Ellipse Grid.Column="4"
Width="8"
Height="8"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Fill="{x:Bind mtu:Converters.ColorToBrush(Item.(local:TabPaletteItem.TabStatus).TabColorIndicator), Mode=OneWay}" />
</Grid>
</DataTemplate>
<local:PaletteItemTemplateSelector x:Key="PaletteItemTemplateSelector"
GeneralItemTemplate="{StaticResource GeneralItemTemplate}"
NestedItemTemplate="{StaticResource NestedItemTemplate}"
TabItemTemplate="{StaticResource TabItemTemplate}" />
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="6*" />
<ColumnDefinition Width="2*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="8*" />
<RowDefinition Height="2*" />
</Grid.RowDefinitions>
<Grid x:Name="_backdrop"
Grid.Row="0"
Grid.Column="1"
Margin="8"
Padding="0,8,0,0"
HorizontalAlignment="Stretch"
VerticalAlignment="Top"
Background="{ThemeResource FlyoutPresenterBackground}"
BorderBrush="{ThemeResource FlyoutBorderThemeBrush}"
BorderThickness="{ThemeResource FlyoutBorderThemeThickness}"
CornerRadius="{ThemeResource OverlayCornerRadius}"
PointerPressed="_backdropPointerPressed"
Shadow="{StaticResource SharedShadow}"
Translation="0,0,32">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBox x:Name="_searchBox"
Grid.Row="0"
Margin="8,0,8,8"
Padding="18,8,8,8"
IsSpellCheckEnabled="False"
PlaceholderText="{x:Bind SearchBoxPlaceholderText, Mode=OneWay}"
Text=""
TextChanged="_filterTextChanged">
<TextBox.ContextFlyout>
<mtu:TextMenuFlyout />
</TextBox.ContextFlyout>
</TextBox>
<TextBlock x:Name="_prefixCharacter"
Grid.Row="0"
Margin="8,0,8,8"
Padding="8,0,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
FontSize="14"
Text="{x:Bind PrefixCharacter, Mode=OneWay}"
Visibility="{x:Bind mtu:Converters.StringNotEmptyToVisibility(PrefixCharacter), Mode=OneWay}" />
<StackPanel Grid.Row="1"
Margin="8,0,8,8"
Orientation="Horizontal"
Visibility="{x:Bind mtu:Converters.StringNotEmptyToVisibility(ParentCommandName), Mode=OneWay}">
<Button x:Name="_parentCommandBackButton"
x:Uid="ParentCommandBackButton"
VerticalAlignment="Center"
Click="_moveBackButtonClicked"
ClickMode="Press">
<FontIcon FontFamily="{ThemeResource SymbolThemeFontFamily}"
FontSize="11"
Glyph="&#xE76b;" />
</Button>
<TextBlock x:Name="_parentCommandText"
Padding="16,4"
VerticalAlignment="Center"
FontStyle="Italic"
Text="{x:Bind ParentCommandName, Mode=OneWay}" />
</StackPanel>
<Border Grid.Row="1"
Margin="8,0,8,8"
Padding="16,12"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Style="{StaticResource ParsedCommandLineBorderStyle}"
Visibility="{x:Bind mtu:Converters.StringNotEmptyToVisibility(ParsedCommandLineText), Mode=OneWay}">
<ScrollViewer MaxHeight="200"
VerticalScrollBarVisibility="Auto">
<TextBlock FontStyle="Italic"
Text="{x:Bind ParsedCommandLineText, Mode=OneWay}"
TextWrapping="Wrap" />
</ScrollViewer>
</Border>
<Border x:Name="_noMatchesText"
Grid.Row="3"
Height="36"
Margin="8,0,8,8"
Visibility="Collapsed">
<TextBlock Padding="12,0"
VerticalAlignment="Center"
FontStyle="Italic"
Text="{x:Bind NoMatchesText, Mode=OneWay}" />
</Border>
<ListView x:Name="_filteredActionsView"
Grid.Row="3"
Padding="4,-2,4,6"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
AllowDrop="False"
CanReorderItems="False"
ChoosingItemContainer="_choosingItemContainer"
ContainerContentChanging="_containerContentChanging"
IsItemClickEnabled="True"
ItemClick="_listItemClicked"
ItemsSource="{x:Bind FilteredActions}"
SelectionChanged="_listItemSelectionChanged"
SelectionMode="Single"
Style="{StaticResource NoAnimationsPlease}" />
</Grid>
</Grid>
</UserControl>