mirror of
https://github.com/microsoft/terminal.git
synced 2026-04-10 08:11:06 +00:00
Compare commits
31 Commits
dev/lhecke
...
dev/migrie
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
98dc5a519e | ||
|
|
455407ce00 | ||
|
|
22c94bf10d | ||
|
|
cd2db82515 | ||
|
|
20f5651224 | ||
|
|
173a830339 | ||
|
|
e544871893 | ||
|
|
e2cc27889a | ||
|
|
36b5759604 | ||
|
|
84a41b4504 | ||
|
|
7988d89c08 | ||
|
|
a3c4776355 | ||
|
|
4516b4b1b3 | ||
|
|
f5909d9baa | ||
|
|
8feb9098b9 | ||
|
|
d94183b897 | ||
|
|
2c66c32f97 | ||
|
|
ff5eead9ea | ||
|
|
026f342bc5 | ||
|
|
48e7348f5c | ||
|
|
e785bfc8bb | ||
|
|
985fcdbdb6 | ||
|
|
b0fa972ec1 | ||
|
|
f361b6c879 | ||
|
|
7404dc3d35 | ||
|
|
c97ac66d5d | ||
|
|
ccfc83443b | ||
|
|
0bda66fc2f | ||
|
|
d3b5533a1e | ||
|
|
1449088e80 | ||
|
|
c96799c6e9 |
1
.github/actions/spelling/allow/allow.txt
vendored
1
.github/actions/spelling/allow/allow.txt
vendored
@@ -93,6 +93,7 @@ slnt
|
||||
Sos
|
||||
ssh
|
||||
stakeholders
|
||||
sxn
|
||||
timeline
|
||||
timelines
|
||||
timestamped
|
||||
|
||||
3
.github/actions/spelling/expect/expect.txt
vendored
3
.github/actions/spelling/expect/expect.txt
vendored
@@ -311,6 +311,7 @@ CPLINFO
|
||||
cplusplus
|
||||
CPPCORECHECK
|
||||
cppcorecheckrules
|
||||
cpprest
|
||||
cpprestsdk
|
||||
cppwinrt
|
||||
CProc
|
||||
@@ -1436,6 +1437,7 @@ PPEB
|
||||
ppf
|
||||
ppguid
|
||||
ppidl
|
||||
pplx
|
||||
PPROC
|
||||
PPROCESS
|
||||
ppropvar
|
||||
@@ -2113,6 +2115,7 @@ WDDMCONSOLECONTEXT
|
||||
wdm
|
||||
webpage
|
||||
websites
|
||||
websockets
|
||||
wekyb
|
||||
wex
|
||||
wextest
|
||||
|
||||
@@ -50,6 +50,7 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
case ShortcutAction::SetColorScheme:
|
||||
case ShortcutAction::AdjustOpacity:
|
||||
case ShortcutAction::SendInput:
|
||||
{
|
||||
_RunRestorePreviews();
|
||||
break;
|
||||
@@ -140,6 +141,22 @@ namespace winrt::TerminalApp::implementation
|
||||
});
|
||||
}
|
||||
|
||||
void TerminalPage::_PreviewSendInput(const Settings::Model::SendInputArgs& args)
|
||||
{
|
||||
const auto backup = _restorePreviewFuncs.empty();
|
||||
|
||||
_ApplyToActiveControls([&](const auto& control) {
|
||||
control.PreviewInput(args.Input());
|
||||
|
||||
if (backup)
|
||||
{
|
||||
_restorePreviewFuncs.emplace_back([=]() {
|
||||
// On dismiss:
|
||||
control.PreviewInput(L"");
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
void TerminalPage::_PreviewAction(const Settings::Model::ActionAndArgs& args)
|
||||
{
|
||||
switch (args.Action())
|
||||
@@ -150,6 +167,9 @@ namespace winrt::TerminalApp::implementation
|
||||
case ShortcutAction::AdjustOpacity:
|
||||
_PreviewAdjustOpacity(args.Args().try_as<AdjustOpacityArgs>());
|
||||
break;
|
||||
case ShortcutAction::SendInput:
|
||||
_PreviewSendInput(args.Args().try_as<SendInputArgs>());
|
||||
break;
|
||||
}
|
||||
|
||||
// GH#9818 Other ideas for actions that could be preview-able:
|
||||
|
||||
@@ -225,15 +225,24 @@ namespace winrt::TerminalApp::implementation
|
||||
void CommandPalette::_selectedCommandChanged(const IInspectable& /*sender*/,
|
||||
const Windows::UI::Xaml::RoutedEventArgs& /*args*/)
|
||||
{
|
||||
const auto currentlyVisible{ Visibility() == Visibility::Visible };
|
||||
|
||||
const auto selectedCommand = _filteredActionsView().SelectedItem();
|
||||
const auto filteredCommand{ selectedCommand.try_as<winrt::TerminalApp::FilteredCommand>() };
|
||||
if (_currentMode == CommandPaletteMode::TabSwitchMode)
|
||||
{
|
||||
_switchToTab(filteredCommand);
|
||||
}
|
||||
else if (_currentMode == CommandPaletteMode::ActionMode && filteredCommand != nullptr)
|
||||
else if (_currentMode == CommandPaletteMode::ActionMode &&
|
||||
currentlyVisible)
|
||||
{
|
||||
if (const auto actionPaletteItem{ filteredCommand.Item().try_as<winrt::TerminalApp::ActionPaletteItem>() })
|
||||
// If we don't have a selected command, then end any previews we
|
||||
// might currently be showing.
|
||||
if (filteredCommand == nullptr)
|
||||
{
|
||||
_PreviewActionHandlers(*this, nullptr);
|
||||
}
|
||||
else if (const auto actionPaletteItem{ filteredCommand.Item().try_as<winrt::TerminalApp::ActionPaletteItem>() })
|
||||
{
|
||||
_PreviewActionHandlers(*this, actionPaletteItem.Command());
|
||||
}
|
||||
@@ -1083,7 +1092,9 @@ namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
std::copy(begin(commandsToFilter), end(commandsToFilter), std::back_inserter(actions));
|
||||
}
|
||||
else if (_currentMode == CommandPaletteMode::TabSearchMode || _currentMode == CommandPaletteMode::ActionMode || _currentMode == CommandPaletteMode::CommandlineMode)
|
||||
else if (_currentMode == CommandPaletteMode::TabSearchMode ||
|
||||
_currentMode == CommandPaletteMode::ActionMode ||
|
||||
_currentMode == CommandPaletteMode::CommandlineMode)
|
||||
{
|
||||
for (const auto& action : commandsToFilter)
|
||||
{
|
||||
|
||||
1090
src/cascadia/TerminalApp/SuggestionsControl.cpp
Normal file
1090
src/cascadia/TerminalApp/SuggestionsControl.cpp
Normal file
File diff suppressed because it is too large
Load Diff
135
src/cascadia/TerminalApp/SuggestionsControl.h
Normal file
135
src/cascadia/TerminalApp/SuggestionsControl.h
Normal file
@@ -0,0 +1,135 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "FilteredCommand.h"
|
||||
#include "SuggestionsControl.g.h"
|
||||
#include "AppCommandlineArgs.h"
|
||||
|
||||
// fwdecl unittest classes
|
||||
namespace TerminalAppLocalTests
|
||||
{
|
||||
class TabTests;
|
||||
};
|
||||
|
||||
namespace winrt::TerminalApp::implementation
|
||||
{
|
||||
struct SuggestionsControl : SuggestionsControlT<SuggestionsControl>
|
||||
{
|
||||
SuggestionsControl();
|
||||
|
||||
Windows::Foundation::Collections::IObservableVector<winrt::TerminalApp::FilteredCommand> FilteredActions();
|
||||
|
||||
void SetCommands(const Windows::Foundation::Collections::IVector<Microsoft::Terminal::Settings::Model::Command>& actions);
|
||||
void SetActionMap(const Microsoft::Terminal::Settings::Model::IActionMapView& actionMap);
|
||||
|
||||
bool OnDirectKeyEvent(const uint32_t vkey, const uint8_t scanCode, const bool down);
|
||||
|
||||
void SelectNextItem(const bool moveDown);
|
||||
|
||||
void ScrollPageUp();
|
||||
void ScrollPageDown();
|
||||
void ScrollToTop();
|
||||
void ScrollToBottom();
|
||||
|
||||
Windows::UI::Xaml::FrameworkElement SelectedItem();
|
||||
|
||||
TerminalApp::SuggestionsMode Mode() const;
|
||||
void Mode(TerminalApp::SuggestionsMode mode);
|
||||
void Anchor(Windows::Foundation::Point anchor, Windows::Foundation::Size space, float characterHeight);
|
||||
|
||||
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
|
||||
WINRT_OBSERVABLE_PROPERTY(winrt::hstring, NoMatchesText, _PropertyChangedHandlers);
|
||||
WINRT_OBSERVABLE_PROPERTY(winrt::hstring, SearchBoxPlaceholderText, _PropertyChangedHandlers);
|
||||
WINRT_OBSERVABLE_PROPERTY(winrt::hstring, ControlName, _PropertyChangedHandlers);
|
||||
WINRT_OBSERVABLE_PROPERTY(winrt::hstring, ParentCommandName, _PropertyChangedHandlers);
|
||||
WINRT_OBSERVABLE_PROPERTY(winrt::hstring, ParsedCommandLineText, _PropertyChangedHandlers);
|
||||
|
||||
TYPED_EVENT(SwitchToTabRequested, winrt::TerminalApp::SuggestionsControl, winrt::TerminalApp::TabBase);
|
||||
TYPED_EVENT(CommandLineExecutionRequested, winrt::TerminalApp::SuggestionsControl, winrt::hstring);
|
||||
TYPED_EVENT(DispatchCommandRequested, winrt::TerminalApp::SuggestionsControl, Microsoft::Terminal::Settings::Model::Command);
|
||||
TYPED_EVENT(PreviewAction, Windows::Foundation::IInspectable, Microsoft::Terminal::Settings::Model::Command);
|
||||
|
||||
private:
|
||||
friend struct SuggestionsControlT<SuggestionsControl>; // for Xaml to bind events
|
||||
|
||||
Windows::Foundation::Collections::IVector<winrt::TerminalApp::FilteredCommand> _allCommands{ nullptr };
|
||||
Windows::Foundation::Collections::IVector<winrt::TerminalApp::FilteredCommand> _currentNestedCommands{ nullptr };
|
||||
Windows::Foundation::Collections::IObservableVector<winrt::TerminalApp::FilteredCommand> _filteredActions{ nullptr };
|
||||
Windows::Foundation::Collections::IVector<winrt::TerminalApp::FilteredCommand> _nestedActionStack{ nullptr };
|
||||
|
||||
Windows::Foundation::Collections::IVector<winrt::TerminalApp::FilteredCommand> _commandsToFilter();
|
||||
|
||||
TerminalApp::SuggestionsMode _mode{ TerminalApp::SuggestionsMode::Palette };
|
||||
TerminalApp::SuggestionsDirection _direction{ TerminalApp::SuggestionsDirection::TopDown };
|
||||
|
||||
bool _lastFilterTextWasEmpty{ true };
|
||||
Windows::Foundation::Point _anchor;
|
||||
Windows::Foundation::Size _space;
|
||||
|
||||
void _filterTextChanged(const Windows::Foundation::IInspectable& sender,
|
||||
const Windows::UI::Xaml::RoutedEventArgs& args);
|
||||
void _previewKeyDownHandler(const Windows::Foundation::IInspectable& sender,
|
||||
const Windows::UI::Xaml::Input::KeyRoutedEventArgs& e);
|
||||
|
||||
void _keyUpHandler(const Windows::Foundation::IInspectable& sender,
|
||||
const Windows::UI::Xaml::Input::KeyRoutedEventArgs& e);
|
||||
|
||||
void _selectedCommandChanged(const Windows::Foundation::IInspectable& sender,
|
||||
const Windows::UI::Xaml::RoutedEventArgs& args);
|
||||
|
||||
void _updateUIForStackChange();
|
||||
|
||||
void _rootPointerPressed(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& e);
|
||||
|
||||
void _lostFocusHandler(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs& args);
|
||||
|
||||
void _backdropPointerPressed(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Input::PointerRoutedEventArgs& e);
|
||||
|
||||
void _listItemClicked(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Controls::ItemClickEventArgs& e);
|
||||
|
||||
void _listItemSelectionChanged(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::Controls::SelectionChangedEventArgs& e);
|
||||
|
||||
void _moveBackButtonClicked(const Windows::Foundation::IInspectable& sender, const Windows::UI::Xaml::RoutedEventArgs&);
|
||||
|
||||
void _updateFilteredActions();
|
||||
|
||||
void _updateCurrentNestedCommands(const winrt::Microsoft::Terminal::Settings::Model::Command& parentCommand);
|
||||
|
||||
std::vector<winrt::TerminalApp::FilteredCommand> _collectFilteredActions();
|
||||
|
||||
void _close();
|
||||
|
||||
void _switchToMode();
|
||||
|
||||
void _setDirection(TerminalApp::SuggestionsDirection direction);
|
||||
|
||||
std::wstring _getTrimmedInput();
|
||||
|
||||
Microsoft::Terminal::Settings::Model::IActionMapView _actionMap{ nullptr };
|
||||
|
||||
winrt::Windows::UI::Xaml::Controls::ListView::SizeChanged_revoker _sizeChangedRevoker;
|
||||
|
||||
void _dispatchCommand(const winrt::TerminalApp::FilteredCommand& command);
|
||||
static std::optional<winrt::TerminalApp::FilteredCommand> _buildCommandLineCommand(const winrt::hstring& commandLine);
|
||||
|
||||
void _dismissPalette();
|
||||
|
||||
void _scrollToIndex(uint32_t index);
|
||||
uint32_t _getNumVisibleItems();
|
||||
|
||||
void _choosingItemContainer(const Windows::UI::Xaml::Controls::ListViewBase& sender, const Windows::UI::Xaml::Controls::ChoosingItemContainerEventArgs& args);
|
||||
void _containerContentChanging(const Windows::UI::Xaml::Controls::ListViewBase& sender, const Windows::UI::Xaml::Controls::ContainerContentChangingEventArgs& args);
|
||||
winrt::TerminalApp::PaletteItemTemplateSelector _itemTemplateSelector{ nullptr };
|
||||
std::unordered_map<Windows::UI::Xaml::DataTemplate, std::unordered_set<Windows::UI::Xaml::Controls::Primitives::SelectorItem>> _listViewItemsCache;
|
||||
Windows::UI::Xaml::DataTemplate _listItemTemplate;
|
||||
|
||||
friend class TerminalAppLocalTests::TabTests;
|
||||
};
|
||||
}
|
||||
|
||||
namespace winrt::TerminalApp::factory_implementation
|
||||
{
|
||||
BASIC_FACTORY(SuggestionsControl);
|
||||
}
|
||||
49
src/cascadia/TerminalApp/SuggestionsControl.idl
Normal file
49
src/cascadia/TerminalApp/SuggestionsControl.idl
Normal file
@@ -0,0 +1,49 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import "TabBase.idl";
|
||||
import "IDirectKeyListener.idl";
|
||||
import "HighlightedTextControl.idl";
|
||||
import "FilteredCommand.idl";
|
||||
|
||||
namespace TerminalApp
|
||||
{
|
||||
|
||||
enum SuggestionsMode
|
||||
{
|
||||
Palette = 0,
|
||||
Menu,
|
||||
// Inline,
|
||||
};
|
||||
|
||||
enum SuggestionsDirection {
|
||||
TopDown,
|
||||
BottomUp
|
||||
};
|
||||
|
||||
[default_interface] runtimeclass SuggestionsControl : Windows.UI.Xaml.Controls.UserControl, Windows.UI.Xaml.Data.INotifyPropertyChanged, IDirectKeyListener
|
||||
{
|
||||
SuggestionsControl();
|
||||
|
||||
String NoMatchesText { get; };
|
||||
String SearchBoxPlaceholderText { get; };
|
||||
String ControlName { get; };
|
||||
String ParentCommandName { get; };
|
||||
String ParsedCommandLineText { get; };
|
||||
|
||||
Windows.UI.Xaml.FrameworkElement SelectedItem { get; };
|
||||
|
||||
Windows.Foundation.Collections.IObservableVector<FilteredCommand> FilteredActions { get; };
|
||||
|
||||
SuggestionsMode Mode { get; set; };
|
||||
|
||||
void SetCommands(Windows.Foundation.Collections.IVector<Microsoft.Terminal.Settings.Model.Command> actions);
|
||||
void SetActionMap(Microsoft.Terminal.Settings.Model.IActionMapView actionMap);
|
||||
void SelectNextItem(Boolean moveDown);
|
||||
|
||||
void Anchor(Windows.Foundation.Point anchor, Windows.Foundation.Size space, Single characterHeight);
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<SuggestionsControl, Microsoft.Terminal.Settings.Model.Command> DispatchCommandRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Microsoft.Terminal.Settings.Model.Command> PreviewAction;
|
||||
}
|
||||
}
|
||||
328
src/cascadia/TerminalApp/SuggestionsControl.xaml
Normal file
328
src/cascadia/TerminalApp/SuggestionsControl.xaml
Normal file
@@ -0,0 +1,328 @@
|
||||
<!--
|
||||
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.SuggestionsControl"
|
||||
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:model="using:Microsoft.Terminal.Settings.Model"
|
||||
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>
|
||||
<!-- This creates an instance of our CommandKeyChordVisibilityConverter we can reference below -->
|
||||
<local:EmptyStringVisibilityConverter x:Key="CommandKeyChordVisibilityConverter" />
|
||||
<local:EmptyStringVisibilityConverter x:Key="ParsedCommandLineTextVisibilityConverter" />
|
||||
<local:EmptyStringVisibilityConverter x:Key="ParentCommandVisibilityConverter" />
|
||||
<model:IconPathConverter x:Key="IconSourceConverter" />
|
||||
|
||||
<DataTemplate x:Key="ListItemTemplate"
|
||||
x:DataType="local:FilteredCommand">
|
||||
<ListViewItem Height="32"
|
||||
MinHeight="0"
|
||||
Padding="16,0,12,0"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
AutomationProperties.AcceleratorKey="{x:Bind Item.KeyChordText, Mode=OneWay}"
|
||||
AutomationProperties.Name="{x:Bind Item.Name, Mode=OneWay}"
|
||||
FontSize="12" />
|
||||
</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}" />
|
||||
|
||||
</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. See
|
||||
CommandKeyChordVisibilityConverter for details.
|
||||
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="{ThemeResource KeyChordBorderStyle}"
|
||||
Visibility="{x:Bind Item.KeyChordText, Mode=OneWay, Converter={StaticResource CommandKeyChordVisibilityConverter}}">
|
||||
|
||||
<TextBlock AutomationProperties.AccessibilityView="Raw"
|
||||
FontSize="12"
|
||||
Style="{ThemeResource KeyChordTextBlockStyle}"
|
||||
Text="{x:Bind Item.KeyChordText, Mode=OneWay}" />
|
||||
</Border>
|
||||
|
||||
<FontIcon Grid.Column="2"
|
||||
HorizontalAlignment="Right"
|
||||
FontFamily="{ThemeResource SymbolThemeFontFamily}"
|
||||
FontSize="12"
|
||||
Glyph="" />
|
||||
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
|
||||
<local:PaletteItemTemplateSelector x:Key="PaletteItemTemplateSelector"
|
||||
GeneralItemTemplate="{StaticResource GeneralItemTemplate}"
|
||||
NestedItemTemplate="{StaticResource NestedItemTemplate}" />
|
||||
|
||||
<ResourceDictionary.ThemeDictionaries>
|
||||
<ResourceDictionary x:Key="Dark">
|
||||
|
||||
<!-- 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>
|
||||
<Style x:Key="ParsedCommandLineTextBlockStyle"
|
||||
TargetType="TextBlock">
|
||||
<Setter Property="Foreground" Value="{ThemeResource SystemControlForegroundBaseMediumBrush}" />
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary x:Key="Light">
|
||||
|
||||
<!-- 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>
|
||||
<Style x:Key="ParsedCommandLineTextBlockStyle"
|
||||
TargetType="TextBlock">
|
||||
<Setter Property="Foreground" Value="{ThemeResource SystemControlForegroundBaseMediumBrush}" />
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary x:Key="HighContrast">
|
||||
|
||||
<!-- KeyChordText styles (use XAML defaults for High Contrast theme) -->
|
||||
<Style x:Key="KeyChordBorderStyle"
|
||||
TargetType="Border" />
|
||||
<Style x:Key="KeyChordTextBlockStyle"
|
||||
TargetType="TextBlock" />
|
||||
|
||||
<!-- ParsedCommandLineText styles (use XAML defaults for High Contrast theme) -->
|
||||
<Style x:Key="ParsedCommandLineBorderStyle"
|
||||
TargetType="Border" />
|
||||
<Style x:Key="ParsedCommandLineTextBlockStyle"
|
||||
TargetType="TextBlock" />
|
||||
</ResourceDictionary>
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
</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.RowSpan="2"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="3"
|
||||
MaxWidth="300"
|
||||
MaxHeight="300"
|
||||
Margin="0"
|
||||
Padding="0,8,0,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
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" />
|
||||
<!-- Top-down _searchBox -->
|
||||
<RowDefinition Height="Auto" />
|
||||
<!-- Top-down ParentCommandName -->
|
||||
<RowDefinition Height="Auto" />
|
||||
<!-- Top-down UNUSED???????? -->
|
||||
<RowDefinition Height="*" />
|
||||
<!-- _filteredActionsView -->
|
||||
<RowDefinition Height="Auto" />
|
||||
<!-- bottom-up _searchBox -->
|
||||
</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"
|
||||
Visibility="Collapsed" />
|
||||
|
||||
<StackPanel Grid.Row="1"
|
||||
Margin="8,0,8,8"
|
||||
Orientation="Horizontal"
|
||||
Visibility="{x:Bind ParentCommandName, Mode=OneWay, Converter={StaticResource ParentCommandVisibilityConverter}}">
|
||||
|
||||
<Button x:Name="_parentCommandBackButton"
|
||||
x:Uid="ParentCommandBackButton"
|
||||
VerticalAlignment="Center"
|
||||
Click="_moveBackButtonClicked"
|
||||
ClickMode="Press">
|
||||
<FontIcon FontFamily="{ThemeResource SymbolThemeFontFamily}"
|
||||
FontSize="11"
|
||||
Glyph="" />
|
||||
</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="{ThemeResource ParsedCommandLineBorderStyle}"
|
||||
Visibility="{x:Bind ParsedCommandLineText, Mode=OneWay, Converter={StaticResource ParsedCommandLineTextVisibilityConverter}}">
|
||||
|
||||
<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" />
|
||||
|
||||
</Grid>
|
||||
|
||||
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -60,6 +60,9 @@
|
||||
<Page Include="CommandPalette.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="SuggestionsControl.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<!-- ========================= Headers ======================== -->
|
||||
<ItemGroup>
|
||||
@@ -139,6 +142,9 @@
|
||||
<DependentUpon>AppLogic.idl</DependentUpon>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Toast.h" />
|
||||
<ClInclude Include="SuggestionsControl.h">
|
||||
<DependentUpon>SuggestionsControl.xaml</DependentUpon>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<!-- ========================= Cpp Files ======================== -->
|
||||
<ItemGroup>
|
||||
@@ -233,6 +239,9 @@
|
||||
</ClCompile>
|
||||
<ClCompile Include="$(GeneratedFilesDir)module.g.cpp" />
|
||||
<ClCompile Include="Toast.cpp" />
|
||||
<ClCompile Include="SuggestionsControl.cpp">
|
||||
<DependentUpon>SuggestionsControl.xaml</DependentUpon>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<!-- ========================= idl Files ======================== -->
|
||||
<ItemGroup>
|
||||
@@ -292,6 +301,10 @@
|
||||
<DependentUpon>CommandPalette.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Midl>
|
||||
<Midl Include="SuggestionsControl.idl">
|
||||
<DependentUpon>SuggestionsControl.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Midl>
|
||||
<Midl Include="FilteredCommand.idl" />
|
||||
<Midl Include="EmptyStringVisibilityConverter.idl" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -287,6 +287,20 @@ namespace winrt::TerminalApp::implementation
|
||||
CommandPalette().SwitchToTabRequested({ this, &TerminalPage::_OnSwitchToTabRequested });
|
||||
CommandPalette().PreviewAction({ this, &TerminalPage::_PreviewActionHandler });
|
||||
|
||||
{
|
||||
const auto& sxnUi{ SuggestionsUI() };
|
||||
// sxnUi.PositionManually(Windows::Foundation::Point{ 0, 0 }, Windows::Foundation::Size{ 200, 300 });
|
||||
sxnUi.RegisterPropertyChangedCallback(UIElement::VisibilityProperty(), [this](auto&&, auto&&) {
|
||||
if (SuggestionsUI().Visibility() == Visibility::Collapsed)
|
||||
{
|
||||
// SuggestionsPopup().IsOpen(false);
|
||||
_FocusActiveControl(nullptr, nullptr);
|
||||
}
|
||||
});
|
||||
sxnUi.DispatchCommandRequested({ this, &TerminalPage::_OnDispatchCommandRequested });
|
||||
sxnUi.PreviewAction({ this, &TerminalPage::_PreviewActionHandler });
|
||||
}
|
||||
|
||||
// Settings AllowDependentAnimations will affect whether animations are
|
||||
// enabled application-wide, so we don't need to check it each time we
|
||||
// want to create an animation.
|
||||
@@ -1469,7 +1483,13 @@ namespace winrt::TerminalApp::implementation
|
||||
return;
|
||||
}
|
||||
|
||||
if (const auto p = CommandPalette(); p.Visibility() == Visibility::Visible && cmd.ActionAndArgs().Action() != ShortcutAction::ToggleCommandPalette)
|
||||
if (const auto p = CommandPalette(); p.Visibility() == Visibility::Visible &&
|
||||
cmd.ActionAndArgs().Action() != ShortcutAction::ToggleCommandPalette)
|
||||
{
|
||||
p.Visibility(Visibility::Collapsed);
|
||||
}
|
||||
if (const auto p = SuggestionsUI(); p.Visibility() == Visibility::Visible &&
|
||||
cmd.ActionAndArgs().Action() != ShortcutAction::ToggleCommandPalette)
|
||||
{
|
||||
p.Visibility(Visibility::Collapsed);
|
||||
}
|
||||
@@ -1660,6 +1680,8 @@ namespace winrt::TerminalApp::implementation
|
||||
});
|
||||
|
||||
term.ShowWindowChanged({ get_weak(), &TerminalPage::_ShowWindowChangedHandler });
|
||||
|
||||
term.MenuChanged({ get_weak(), &TerminalPage::_ControlMenuChangedHandler });
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
@@ -4452,4 +4474,74 @@ namespace winrt::TerminalApp::implementation
|
||||
_activated = activated;
|
||||
_updateThemeColors();
|
||||
}
|
||||
|
||||
winrt::fire_and_forget TerminalPage::_ControlMenuChangedHandler(const IInspectable /*sender*/,
|
||||
const MenuChangedEventArgs args)
|
||||
{
|
||||
if constexpr (!Feature_ShellCompletions::IsEnabled())
|
||||
{
|
||||
co_return;
|
||||
}
|
||||
auto weakThis{ get_weak() };
|
||||
co_await winrt::resume_background();
|
||||
if (const auto& page{ weakThis.get() })
|
||||
{
|
||||
// `this` is safe to use
|
||||
|
||||
// Parse the json
|
||||
try
|
||||
{
|
||||
auto commandsCollection = Command::ParsePowerShellMenuComplete(args.MenuJson(),
|
||||
args.ReplacementLength());
|
||||
|
||||
// Open the Suggestions UI with the commands from the control
|
||||
_OpenSuggestions(commandsCollection, SuggestionsMode::Menu);
|
||||
}
|
||||
CATCH_LOG();
|
||||
}
|
||||
}
|
||||
|
||||
winrt::fire_and_forget TerminalPage::_OpenSuggestions(IVector<Command> commandsCollection,
|
||||
winrt::TerminalApp::SuggestionsMode mode)
|
||||
{
|
||||
if (commandsCollection == nullptr)
|
||||
{
|
||||
co_return;
|
||||
}
|
||||
if (commandsCollection.Size() == 0)
|
||||
{
|
||||
SuggestionsUI().Visibility(Visibility::Collapsed);
|
||||
|
||||
co_return;
|
||||
}
|
||||
auto weakThis{ get_weak() };
|
||||
co_await wil::resume_foreground(Dispatcher(), CoreDispatcherPriority::Normal);
|
||||
const auto& page{ weakThis.get() };
|
||||
if (!page)
|
||||
{
|
||||
co_return;
|
||||
}
|
||||
// page is now keeping `this` alive to use safely
|
||||
const auto& control{ _GetActiveControl() };
|
||||
if (!control)
|
||||
{
|
||||
co_return;
|
||||
}
|
||||
const auto& sxnUi{ SuggestionsUI() };
|
||||
|
||||
sxnUi.Mode(mode);
|
||||
sxnUi.SetCommands(commandsCollection);
|
||||
sxnUi.Visibility(commandsCollection.Size() > 0 ? Visibility::Visible : Visibility::Collapsed);
|
||||
|
||||
const auto characterSize{ control.CharacterDimensions() };
|
||||
// This is in control-relative space. We'll need to convert it to page-relative space.
|
||||
const til::point cursorPos{ control.CursorPositionInDips() };
|
||||
const auto controlTransform = control.TransformToVisual(this->Root());
|
||||
const til::point controlOrigin{ til::math::rounding, controlTransform.TransformPoint(Windows::Foundation::Point{ 0, 0 }) };
|
||||
const til::point realCursorPos = controlOrigin + cursorPos;
|
||||
const til::size windowDimensions{ til::math::rounding, ActualWidth(), ActualHeight() };
|
||||
|
||||
sxnUi.Anchor(realCursorPos.to_winrt_point(), windowDimensions.to_winrt_size(), characterSize.Height);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -429,6 +429,8 @@ namespace winrt::TerminalApp::implementation
|
||||
void _RunRestorePreviews();
|
||||
void _PreviewColorScheme(const Microsoft::Terminal::Settings::Model::SetColorSchemeArgs& args);
|
||||
void _PreviewAdjustOpacity(const Microsoft::Terminal::Settings::Model::AdjustOpacityArgs& args);
|
||||
void _PreviewSendInput(const Microsoft::Terminal::Settings::Model::SendInputArgs& args);
|
||||
|
||||
winrt::Microsoft::Terminal::Settings::Model::ActionAndArgs _lastPreviewedAction{ nullptr };
|
||||
std::vector<std::function<void()>> _restorePreviewFuncs{};
|
||||
|
||||
@@ -460,6 +462,9 @@ namespace winrt::TerminalApp::implementation
|
||||
void _updateThemeColors();
|
||||
void _updateTabCloseButton(const winrt::Microsoft::UI::Xaml::Controls::TabViewItem& tabViewItem);
|
||||
|
||||
winrt::fire_and_forget _ControlMenuChangedHandler(const winrt::Windows::Foundation::IInspectable sender, const winrt::Microsoft::Terminal::Control::MenuChangedEventArgs args);
|
||||
winrt::fire_and_forget _OpenSuggestions(Windows::Foundation::Collections::IVector<winrt::Microsoft::Terminal::Settings::Model::Command> commandsCollection, winrt::TerminalApp::SuggestionsMode mode);
|
||||
|
||||
winrt::fire_and_forget _ShowWindowChangedHandler(const IInspectable sender, const winrt::Microsoft::Terminal::Control::ShowWindowArgs args);
|
||||
|
||||
#pragma region ActionHandlers
|
||||
|
||||
@@ -86,11 +86,11 @@
|
||||
ContentDialog a Row, I believe it's assigned Row 0 by default. So,
|
||||
when the dialog gets opened, the dialog seemingly causes a giant
|
||||
hole to appear in the body of the app.
|
||||
|
||||
|
||||
Assigning all the dialogs to Row 2 (where the rest of the content
|
||||
is) makes the "hole" appear in the same space as the rest of the
|
||||
TabContent, fixing the issue.
|
||||
|
||||
|
||||
Note that the actual content in a content dialog gets parented to
|
||||
the PopupRoot, so it actually always appeared in the correct place, it's
|
||||
just this weird hole that appeared in Row 0.
|
||||
@@ -195,6 +195,13 @@
|
||||
PreviewKeyDown="_KeyDownHandler"
|
||||
Visibility="Collapsed" />
|
||||
|
||||
<local:SuggestionsControl x:Name="SuggestionsUI"
|
||||
Grid.Row="2"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Top"
|
||||
PreviewKeyDown="_KeyDownHandler"
|
||||
Visibility="Collapsed" />
|
||||
|
||||
<!--
|
||||
A TeachingTip with IsLightDismissEnabled="True" will immediately
|
||||
dismiss itself if the window is unfocused (In Xaml Islands). This is
|
||||
|
||||
@@ -125,6 +125,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
auto pfnPlayMidiNote = std::bind(&ControlCore::_terminalPlayMidiNote, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
|
||||
_terminal->SetPlayMidiNoteCallback(pfnPlayMidiNote);
|
||||
|
||||
auto pfnMenuChanged = std::bind(&ControlCore::_terminalMenuChanged, this, std::placeholders::_1, std::placeholders::_2);
|
||||
_terminal->MenuChangedCallback(pfnMenuChanged);
|
||||
|
||||
// MSFT 33353327: Initialize the renderer in the ctor instead of Initialize().
|
||||
// We need the renderer to be ready to accept new engines before the SwapChainPanel is ready to go.
|
||||
// If we wait, a screen reader may try to get the AutomationPeer (aka the UIA Engine), and we won't be able to attach
|
||||
@@ -2097,6 +2100,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
}
|
||||
}
|
||||
|
||||
void ControlCore::_terminalMenuChanged(std::wstring_view menuJson,
|
||||
int32_t replaceLength)
|
||||
{
|
||||
auto args = winrt::make_self<MenuChangedEventArgs>(winrt::hstring{ menuJson },
|
||||
replaceLength);
|
||||
_MenuChangedHandlers(*this, *args);
|
||||
}
|
||||
|
||||
void ControlCore::ColorSelection(const Control::SelectionColor& fg, const Control::SelectionColor& bg, Core::MatchMode matchMode)
|
||||
{
|
||||
if (HasSelection())
|
||||
|
||||
@@ -232,6 +232,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
TYPED_EVENT(ShowWindowChanged, IInspectable, Control::ShowWindowArgs);
|
||||
TYPED_EVENT(UpdateSelectionMarkers, IInspectable, Control::UpdateSelectionMarkersEventArgs);
|
||||
TYPED_EVENT(OpenHyperlink, IInspectable, Control::OpenHyperlinkEventArgs);
|
||||
TYPED_EVENT(MenuChanged, IInspectable, Control::MenuChangedEventArgs);
|
||||
|
||||
TYPED_EVENT(CloseTerminalRequested, IInspectable, IInspectable);
|
||||
// clang-format on
|
||||
|
||||
@@ -307,6 +309,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
void _terminalPlayMidiNote(const int noteNumber,
|
||||
const int velocity,
|
||||
const std::chrono::microseconds duration);
|
||||
|
||||
void _terminalMenuChanged(std::wstring_view menuJson, int32_t replaceLength);
|
||||
|
||||
#pragma endregion
|
||||
|
||||
MidiAudio _midiAudio;
|
||||
|
||||
@@ -162,5 +162,7 @@ namespace Microsoft.Terminal.Control
|
||||
event Windows.Foundation.TypedEventHandler<Object, UpdateSelectionMarkersEventArgs> UpdateSelectionMarkers;
|
||||
event Windows.Foundation.TypedEventHandler<Object, OpenHyperlinkEventArgs> OpenHyperlink;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> CloseTerminalRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, MenuChangedEventArgs> MenuChanged;
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@@ -14,3 +14,4 @@
|
||||
#include "FoundResultsArgs.g.cpp"
|
||||
#include "ShowWindowArgs.g.cpp"
|
||||
#include "UpdateSelectionMarkersEventArgs.g.cpp"
|
||||
#include "MenuChangedEventArgs.g.cpp"
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "FoundResultsArgs.g.h"
|
||||
#include "ShowWindowArgs.g.h"
|
||||
#include "UpdateSelectionMarkersEventArgs.g.h"
|
||||
#include "MenuChangedEventArgs.g.h"
|
||||
|
||||
namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
{
|
||||
@@ -169,4 +170,17 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
|
||||
WINRT_PROPERTY(bool, ClearMarkers, false);
|
||||
};
|
||||
|
||||
struct MenuChangedEventArgs : public MenuChangedEventArgsT<MenuChangedEventArgs>
|
||||
{
|
||||
public:
|
||||
MenuChangedEventArgs(const winrt::hstring menuJson, const int32_t replaceLength) :
|
||||
_MenuJson(menuJson),
|
||||
_ReplacementLength(replaceLength)
|
||||
{
|
||||
}
|
||||
|
||||
WINRT_PROPERTY(winrt::hstring, MenuJson, L"");
|
||||
WINRT_PROPERTY(int32_t, ReplacementLength, 0);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -83,4 +83,10 @@ namespace Microsoft.Terminal.Control
|
||||
{
|
||||
Boolean ClearMarkers { get; };
|
||||
}
|
||||
|
||||
runtimeclass MenuChangedEventArgs
|
||||
{
|
||||
String MenuJson { get; };
|
||||
Int32 ReplacementLength { get; };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -437,4 +437,26 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
void TSFInputControl::_formatUpdatingHandler(CoreTextEditContext sender, const CoreTextFormatUpdatingEventArgs& /*args*/)
|
||||
{
|
||||
}
|
||||
|
||||
void TSFInputControl::ManuallyDisplayText(const winrt::hstring& text)
|
||||
{
|
||||
_focused = !text.empty();
|
||||
Canvas().Visibility(text.empty() ? Visibility::Collapsed : Visibility::Visible);
|
||||
|
||||
_inputBuffer.clear();
|
||||
_activeTextStart = 0;
|
||||
_inComposition = false;
|
||||
|
||||
// HACK trim off leading DEL chars.
|
||||
std::wstring_view view{ text.c_str() };
|
||||
const auto strBegin = view.find_first_not_of(L"\x7f");
|
||||
if (strBegin != std::wstring::npos)
|
||||
{
|
||||
view = view.substr(strBegin * 2);
|
||||
}
|
||||
|
||||
TextBlock().Text(winrt::hstring{ view });
|
||||
TextBlock().UpdateLayout();
|
||||
TryRedrawCanvas();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
void ClearBuffer();
|
||||
void TryRedrawCanvas();
|
||||
|
||||
void ManuallyDisplayText(const winrt::hstring& text);
|
||||
|
||||
void Close();
|
||||
|
||||
// -------------------------------- WinRT Events ---------------------------------
|
||||
|
||||
@@ -31,6 +31,9 @@ namespace Microsoft.Terminal.Control
|
||||
void ClearBuffer();
|
||||
void TryRedrawCanvas();
|
||||
|
||||
void ManuallyDisplayText(String text);
|
||||
|
||||
|
||||
void Close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -388,6 +388,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
// - <none>
|
||||
void TermControl::SendInput(const winrt::hstring& wstr)
|
||||
{
|
||||
PreviewInput(L"");
|
||||
_core.SendInput(wstr);
|
||||
}
|
||||
void TermControl::ClearBuffer(Control::ClearBufferType clearType)
|
||||
@@ -3157,6 +3158,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
return _core.OwningHwnd();
|
||||
}
|
||||
|
||||
void TermControl::PreviewInput(const winrt::hstring& text)
|
||||
{
|
||||
TSFInputControl().ManuallyDisplayText(text);
|
||||
}
|
||||
|
||||
void TermControl::AddMark(const Control::ScrollMark& mark)
|
||||
{
|
||||
_core.AddMark(mark);
|
||||
@@ -3174,4 +3180,27 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
{
|
||||
_core.ColorSelection(fg, bg, matchMode);
|
||||
}
|
||||
|
||||
// Returns the text cursor's position relative to our origin, in DIPs.
|
||||
Microsoft::Terminal::Core::Point TermControl::CursorPositionInDips()
|
||||
{
|
||||
const til::point cursorPos{ _core.CursorPosition() };
|
||||
|
||||
const til::size fontSize{ til::math::flooring, CharacterDimensions() };
|
||||
|
||||
// Convert text buffer cursor position to client coordinate position
|
||||
// within the window. This point is in _pixels_
|
||||
const til::point clientCursorPos{ cursorPos * fontSize };
|
||||
|
||||
// Get scale factor for view
|
||||
const double scaleFactor = DisplayInformation::GetForCurrentView().RawPixelsPerViewPixel();
|
||||
|
||||
const til::point clientCursorInDips{ til::math::flooring, clientCursorPos.x / scaleFactor, clientCursorPos.y / scaleFactor };
|
||||
|
||||
auto padding{ GetPadding() };
|
||||
til::point relativeToOrigin{ til::math::flooring,
|
||||
clientCursorInDips.x + padding.Left,
|
||||
clientCursorInDips.y + padding.Top };
|
||||
return relativeToOrigin.to_core_point();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
Windows::Foundation::Size MinimumSize();
|
||||
float SnapDimensionToGrid(const bool widthOrHeight, const float dimension);
|
||||
|
||||
void PreviewInput(const winrt::hstring& text);
|
||||
|
||||
Microsoft::Terminal::Core::Point CursorPositionInDips();
|
||||
|
||||
void WindowVisibilityChanged(const bool showOrHide);
|
||||
|
||||
void ColorSelection(Control::SelectionColor fg, Control::SelectionColor bg, Core::MatchMode matchMode);
|
||||
@@ -150,6 +154,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
|
||||
PROJECTED_FORWARDED_TYPED_EVENT(ShowWindowChanged, IInspectable, Control::ShowWindowArgs, _core, ShowWindowChanged);
|
||||
PROJECTED_FORWARDED_TYPED_EVENT(CloseTerminalRequested, IInspectable, IInspectable, _core, CloseTerminalRequested);
|
||||
|
||||
PROJECTED_FORWARDED_TYPED_EVENT(MenuChanged , IInspectable, Control::MenuChangedEventArgs, _core, MenuChanged);
|
||||
|
||||
PROJECTED_FORWARDED_TYPED_EVENT(PasteFromClipboard, IInspectable, Control::PasteFromClipboardEventArgs, _interactivity, PasteFromClipboard);
|
||||
|
||||
TYPED_EVENT(OpenHyperlink, IInspectable, Control::OpenHyperlinkEventArgs);
|
||||
|
||||
@@ -44,6 +44,7 @@ namespace Microsoft.Terminal.Control
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> TabColorChanged;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> ReadOnlyChanged;
|
||||
event Windows.Foundation.TypedEventHandler<Object, Object> FocusFollowMouseRequested;
|
||||
event Windows.Foundation.TypedEventHandler<Object, MenuChangedEventArgs> MenuChanged;
|
||||
|
||||
event Windows.Foundation.TypedEventHandler<TermControl, Windows.UI.Xaml.RoutedEventArgs> Initialized;
|
||||
// This is an event handler forwarder for the underlying connection.
|
||||
@@ -97,8 +98,13 @@ namespace Microsoft.Terminal.Control
|
||||
// opacity set by the settings should call this instead.
|
||||
Double BackgroundOpacity { get; };
|
||||
|
||||
void PreviewInput(String text);
|
||||
|
||||
Windows.UI.Xaml.Media.Brush BackgroundBrush { get; };
|
||||
|
||||
void ColorSelection(SelectionColor fg, SelectionColor bg, Microsoft.Terminal.Core.MatchMode matchMode);
|
||||
|
||||
Microsoft.Terminal.Core.Point CursorPositionInDips { get; };
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1411,6 +1411,11 @@ const size_t Microsoft::Terminal::Core::Terminal::GetTaskbarProgress() const noe
|
||||
return _taskbarProgress;
|
||||
}
|
||||
|
||||
void Microsoft::Terminal::Core::Terminal::MenuChangedCallback(std::function<void(std::wstring_view, int32_t)> pfn) noexcept
|
||||
{
|
||||
_pfnMenuChanged.swap(pfn);
|
||||
}
|
||||
|
||||
Scheme Terminal::GetColorScheme() const
|
||||
{
|
||||
Scheme s;
|
||||
|
||||
@@ -140,6 +140,9 @@ public:
|
||||
bool IsConsolePty() const noexcept override;
|
||||
bool IsVtInputEnabled() const noexcept override;
|
||||
void NotifyAccessibilityChange(const til::rect& changedRect) noexcept override;
|
||||
|
||||
void InvokeMenu(std::wstring_view menuJson, int32_t replaceLength) override;
|
||||
|
||||
#pragma endregion
|
||||
|
||||
void ClearMark();
|
||||
@@ -213,6 +216,7 @@ public:
|
||||
void TaskbarProgressChangedCallback(std::function<void()> pfn) noexcept;
|
||||
void SetShowWindowCallback(std::function<void(bool)> pfn) noexcept;
|
||||
void SetPlayMidiNoteCallback(std::function<void(const int, const int, const std::chrono::microseconds)> pfn) noexcept;
|
||||
void MenuChangedCallback(std::function<void(std::wstring_view, int32_t)> pfn) noexcept;
|
||||
|
||||
void SetCursorOn(const bool isOn);
|
||||
bool IsCursorBlinkingAllowed() const noexcept;
|
||||
@@ -310,6 +314,7 @@ private:
|
||||
std::function<void()> _pfnTaskbarProgressChanged;
|
||||
std::function<void(bool)> _pfnShowWindowChanged;
|
||||
std::function<void(const int, const int, const std::chrono::microseconds)> _pfnPlayMidiNote;
|
||||
std::function<void(std::wstring_view, int32_t)> _pfnMenuChanged;
|
||||
|
||||
RenderSettings _renderSettings;
|
||||
std::unique_ptr<::Microsoft::Console::VirtualTerminal::StateMachine> _stateMachine;
|
||||
|
||||
@@ -467,3 +467,10 @@ void Terminal::NotifyAccessibilityChange(const til::rect& /*changedRect*/) noexc
|
||||
{
|
||||
// This is only needed in conhost. Terminal handles accessibility in another way.
|
||||
}
|
||||
void Terminal::InvokeMenu(std::wstring_view menuJson, int32_t replaceLength)
|
||||
{
|
||||
if (_pfnMenuChanged)
|
||||
{
|
||||
_pfnMenuChanged(menuJson, replaceLength);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -750,6 +750,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::factory_implementation
|
||||
BASIC_FACTORY(RenameTabArgs);
|
||||
BASIC_FACTORY(SwapPaneArgs);
|
||||
BASIC_FACTORY(SplitPaneArgs);
|
||||
BASIC_FACTORY(SendInputArgs);
|
||||
BASIC_FACTORY(SetFocusModeArgs);
|
||||
BASIC_FACTORY(SetFullScreenArgs);
|
||||
BASIC_FACTORY(SetMaximizedArgs);
|
||||
|
||||
@@ -192,6 +192,8 @@ namespace Microsoft.Terminal.Settings.Model
|
||||
|
||||
[default_interface] runtimeclass SendInputArgs : IActionArgs
|
||||
{
|
||||
SendInputArgs(String input);
|
||||
|
||||
String Input { get; };
|
||||
};
|
||||
|
||||
|
||||
@@ -635,4 +635,98 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
|
||||
return newCommands;
|
||||
}
|
||||
|
||||
winrt::Windows::Foundation::Collections::IVector<Model::Command> Command::ParsePowerShellMenuComplete(winrt::hstring json, int32_t replaceLength)
|
||||
{
|
||||
if (json.empty())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
auto data = winrt::to_string(json);
|
||||
|
||||
std::string errs;
|
||||
static std::unique_ptr<Json::CharReader> reader{ Json::CharReaderBuilder::CharReaderBuilder().newCharReader() };
|
||||
Json::Value root;
|
||||
if (!reader->parse(data.data(), data.data() + data.size(), &root, &errs))
|
||||
{
|
||||
throw winrt::hresult_error(WEB_E_INVALID_JSON_STRING, winrt::to_hstring(errs));
|
||||
}
|
||||
|
||||
auto result = winrt::single_threaded_vector<Model::Command>();
|
||||
|
||||
auto backspaces = std::wstring(::base::saturated_cast<size_t>(replaceLength), L'\x7f');
|
||||
|
||||
const auto parseElement = [&](const auto& element) {
|
||||
winrt::hstring completionText;
|
||||
winrt::hstring listText;
|
||||
JsonUtils::GetValueForKey(element, "CompletionText", completionText);
|
||||
JsonUtils::GetValueForKey(element, "ListItemText", listText);
|
||||
|
||||
Model::SendInputArgs args{ winrt::hstring{ fmt::format(L"{}{}", backspaces, completionText.c_str()) } };
|
||||
Model::ActionAndArgs actionAndArgs{ ShortcutAction::SendInput, args };
|
||||
|
||||
auto c = winrt::make_self<Command>();
|
||||
c->_name = listText;
|
||||
c->_ActionAndArgs = actionAndArgs;
|
||||
|
||||
// Try to assign a sensible icon based on the result type. These are
|
||||
// roughly chosen to align with the icons in
|
||||
// https://github.com/PowerShell/PowerShellEditorServices/pull/1738
|
||||
// as best as possible.
|
||||
if (const auto resultType{ JsonUtils::GetValueForKey<int>(element, "ResultType") })
|
||||
{
|
||||
// PowerShell completion result -> Segoe Fluent icon value & name
|
||||
switch (resultType)
|
||||
{
|
||||
case 1: // History -> 0xe81c History
|
||||
c->_iconPath = L"\ue81c";
|
||||
break;
|
||||
case 2: // Command -> 0xecaa AppIconDefault
|
||||
c->_iconPath = L"\uecaa";
|
||||
break;
|
||||
case 3: // ProviderItem -> 0xe8e4 AlignLeft
|
||||
c->_iconPath = L"\ue8e4";
|
||||
break;
|
||||
case 4: // ProviderContainer -> 0xe838 FolderOpen
|
||||
c->_iconPath = L"\ue838";
|
||||
break;
|
||||
case 5: // Property -> 0xe7c1 Flag
|
||||
c->_iconPath = L"\ue7c1";
|
||||
break;
|
||||
case 6: // Method -> 0xecaa AppIconDefault
|
||||
c->_iconPath = L"\uecaa";
|
||||
break;
|
||||
case 7: // ParameterName -> 0xe7c1 Flag
|
||||
c->_iconPath = L"\ue7c1";
|
||||
break;
|
||||
case 8: // ParameterValue -> 0xf000 KnowledgeArticle
|
||||
c->_iconPath = L"\uf000";
|
||||
break;
|
||||
case 10: // Namespace -> 0xe943 Code
|
||||
c->_iconPath = L"\ue943";
|
||||
break;
|
||||
case 13: // DynamicKeyword -> 0xe945 LightningBolt
|
||||
c->_iconPath = L"\ue945";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
result.Append(*c);
|
||||
};
|
||||
|
||||
if (root.isArray())
|
||||
{
|
||||
// If we got a whole array of suggestions, parse each one.
|
||||
for (const auto& element : root)
|
||||
{
|
||||
parseElement(element);
|
||||
}
|
||||
}
|
||||
else if (root.isObject())
|
||||
{
|
||||
// If we instead only got a single element back, just parse the root element.
|
||||
parseElement(root);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,6 +67,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
|
||||
hstring IconPath() const noexcept;
|
||||
void IconPath(const hstring& val);
|
||||
|
||||
static Windows::Foundation::Collections::IVector<Model::Command> ParsePowerShellMenuComplete(winrt::hstring json, int32_t replaceLength);
|
||||
|
||||
winrt::Windows::UI::Xaml::Data::INotifyPropertyChanged::PropertyChanged_revoker propertyChangedRevoker;
|
||||
|
||||
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
|
||||
|
||||
@@ -47,5 +47,8 @@ namespace Microsoft.Terminal.Settings.Model
|
||||
Windows.Foundation.Collections.IVectorView<Profile> profiles,
|
||||
Windows.Foundation.Collections.IVectorView<ColorScheme> schemes,
|
||||
Windows.Foundation.Collections.IVector<SettingsLoadWarnings> warnings);
|
||||
|
||||
static IVector<Command> ParsePowerShellMenuComplete(String json, Int32 replaceLength);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,4 +155,14 @@
|
||||
</alwaysEnabledBrandingTokens>
|
||||
</feature>
|
||||
|
||||
<feature>
|
||||
<name>Feature_ShellCompletions</name>
|
||||
<description>An experimental escape sequence for client applications to request the Terminal display a list of suggestions.</description>
|
||||
<id>3121</id>
|
||||
<stage>AlwaysDisabled</stage>
|
||||
<alwaysEnabledBrandingTokens>
|
||||
<brandingToken>Dev</brandingToken>
|
||||
</alwaysEnabledBrandingTokens>
|
||||
</feature>
|
||||
|
||||
</featureStaging>
|
||||
|
||||
@@ -493,3 +493,7 @@ void ConhostInternalGetSet::MarkCommandFinish(std::optional<unsigned int> /*erro
|
||||
{
|
||||
// Not implemented for conhost.
|
||||
}
|
||||
void ConhostInternalGetSet::InvokeMenu(std::wstring_view /*menuJson*/, int32_t /*replaceLength*/)
|
||||
{
|
||||
// Not implemented for conhost.
|
||||
}
|
||||
|
||||
@@ -80,6 +80,8 @@ public:
|
||||
void MarkOutputStart() override;
|
||||
void MarkCommandFinish(std::optional<unsigned int> error) override;
|
||||
|
||||
void InvokeMenu(std::wstring_view menuJson, int32_t replaceLength) override;
|
||||
|
||||
private:
|
||||
Microsoft::Console::IIoProvider& _io;
|
||||
bool _bracketedPasteMode{ false };
|
||||
|
||||
@@ -131,6 +131,8 @@ public:
|
||||
|
||||
virtual bool DoFinalTermAction(const std::wstring_view string) = 0;
|
||||
|
||||
virtual bool DoXtermJsAction(const std::wstring_view string) = 0;
|
||||
|
||||
virtual StringHandler DownloadDRCS(const VTInt fontNumber,
|
||||
const VTParameter startChar,
|
||||
const DispatchTypes::DrcsEraseControl eraseControl,
|
||||
|
||||
@@ -82,5 +82,7 @@ namespace Microsoft::Console::VirtualTerminal
|
||||
virtual void MarkCommandStart() = 0;
|
||||
virtual void MarkOutputStart() = 0;
|
||||
virtual void MarkCommandFinish(std::optional<unsigned int> error) = 0;
|
||||
|
||||
virtual void InvokeMenu(std::wstring_view menuJson, int32_t replaceLength) = 0;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3132,6 +3132,73 @@ bool AdaptDispatch::DoFinalTermAction(const std::wstring_view string)
|
||||
// modify the state of that mark as we go.
|
||||
return false;
|
||||
}
|
||||
// Method Description:
|
||||
// - Performs a XtermJs action
|
||||
// - Ascribes to the ITermDispatch interface
|
||||
// - Currently, the actions we support are:
|
||||
// * Completions: An experimental protocol for passing shell completion
|
||||
// information from the shell to the terminal. This sequence is still under
|
||||
// active development, and subject to change.
|
||||
// - Not actually used in conhost
|
||||
// Arguments:
|
||||
// - string: contains the parameters that define which action we do
|
||||
// Return Value:
|
||||
// - false in conhost, true for the SetMark action, otherwise false.
|
||||
bool AdaptDispatch::DoXtermJsAction(const std::wstring_view string)
|
||||
{
|
||||
// This is not implemented in conhost.
|
||||
if (_api.IsConsolePty())
|
||||
{
|
||||
// Flush the frame manually, to make sure marks end up on the right line, like the alt buffer sequence.
|
||||
_renderer.TriggerFlush(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
if constexpr (!Feature_ShellCompletions::IsEnabled())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto parts = Utils::SplitString(string, L';');
|
||||
|
||||
if (parts.size() < 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto action = til::at(parts, 0);
|
||||
|
||||
if (action == L"Completions")
|
||||
{
|
||||
// The structure of the message is as follows:
|
||||
// `e]633;
|
||||
// 0: Completions;
|
||||
// 1: $($completions.ReplacementIndex);
|
||||
// 2: $($completions.ReplacementLength);
|
||||
// 3: $($cursorIndex);
|
||||
// 4: $completions.CompletionMatches | ConvertTo-Json
|
||||
unsigned int replacementIndex = 0;
|
||||
unsigned int replacementLength = 0;
|
||||
unsigned int cursorIndex = 0;
|
||||
|
||||
bool succeeded = (parts.size() >= 2) &&
|
||||
(Utils::StringToUint(til::at(parts, 1), replacementIndex));
|
||||
succeeded &= (parts.size() >= 3) &&
|
||||
(Utils::StringToUint(til::at(parts, 2), replacementLength));
|
||||
succeeded &= (parts.size() >= 4) &&
|
||||
(Utils::StringToUint(til::at(parts, 3), cursorIndex));
|
||||
|
||||
// VsCode is using cursorIndex and replacementIndex, but we aren't currently.
|
||||
if (succeeded)
|
||||
{
|
||||
_api.InvokeMenu(parts.size() < 5 ? L"" : til::at(parts, 4),
|
||||
static_cast<int32_t>(replacementLength));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Method Description:
|
||||
// - DECDLD - Downloads one or more characters of a dynamically redefinable
|
||||
|
||||
@@ -132,6 +132,8 @@ namespace Microsoft::Console::VirtualTerminal
|
||||
|
||||
bool DoFinalTermAction(const std::wstring_view string) override;
|
||||
|
||||
bool DoXtermJsAction(const std::wstring_view string) override;
|
||||
|
||||
StringHandler DownloadDRCS(const VTInt fontNumber,
|
||||
const VTParameter startChar,
|
||||
const DispatchTypes::DrcsEraseControl eraseControl,
|
||||
|
||||
@@ -922,6 +922,11 @@ bool OutputStateMachineEngine::ActionOscDispatch(const wchar_t /*wch*/,
|
||||
success = _dispatch->DoFinalTermAction(string);
|
||||
break;
|
||||
}
|
||||
case OscActionCodes::XtermJsAction:
|
||||
{
|
||||
success = _dispatch->DoXtermJsAction(string);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// If no functions to call, overall dispatch was a failure.
|
||||
success = false;
|
||||
|
||||
@@ -208,6 +208,7 @@ namespace Microsoft::Console::VirtualTerminal
|
||||
ResetBackgroundColor = 111, // Not implemented
|
||||
ResetCursorColor = 112,
|
||||
FinalTermAction = 133,
|
||||
XtermJsAction = 633,
|
||||
ITerm2Action = 1337,
|
||||
};
|
||||
|
||||
|
||||
5
src/tools/scratch/README.md
Normal file
5
src/tools/scratch/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
## Narrator Buddy
|
||||
|
||||
This is a sample of how we might implement Narrator Buddy. This was an internal tool for taking what narrator would read aloud, and logging it to the console.
|
||||
|
||||
Currently this builds as a scratch project in the Terminal solution. It's provided as a reference implementation for how a real narrator buddy might be implemented in the future.
|
||||
@@ -9,6 +9,7 @@
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
</PropertyGroup>
|
||||
<Import Project="..\..\common.build.pre.props" />
|
||||
<Import Project="..\..\common.nugetversions.props" />
|
||||
<ItemGroup>
|
||||
<ClCompile Include="main.cpp" />
|
||||
</ItemGroup>
|
||||
@@ -32,4 +33,5 @@
|
||||
<!-- Careful reordering these. Some default props (contained in these files) are order sensitive. -->
|
||||
<Import Project="..\..\common.build.post.props" />
|
||||
<Import Project="..\..\common.build.tests.props" />
|
||||
<Import Project="..\..\common.nugetversions.targets" />
|
||||
</Project>
|
||||
|
||||
@@ -2,9 +2,162 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
#include <windows.h>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <thread>
|
||||
#include <sstream>
|
||||
|
||||
// This wmain exists for help in writing scratch programs while debugging.
|
||||
int __cdecl wmain(int /*argc*/, WCHAR* /*argv[]*/)
|
||||
#include <wrl.h>
|
||||
namespace wrl = Microsoft::WRL;
|
||||
|
||||
#include <wil\result.h>
|
||||
#include <wil\resource.h>
|
||||
#include <wil\com.h>
|
||||
|
||||
#include <evntcons.h>
|
||||
#include <evntrace.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
wil::unique_event g_stopEvent{ wil::EventOptions::None };
|
||||
|
||||
} // namespace anonymous
|
||||
|
||||
void WINAPI ProcessEtwEvent(_In_ PEVENT_RECORD rawEvent)
|
||||
{
|
||||
// This is the task id from srh.man (with the same name).
|
||||
constexpr auto InitiateSpeaking = 5;
|
||||
|
||||
auto data = rawEvent->UserData;
|
||||
auto dataLen = rawEvent->UserDataLength;
|
||||
auto processId = rawEvent->EventHeader.ProcessId;
|
||||
processId;
|
||||
auto task = rawEvent->EventHeader.EventDescriptor.Task;
|
||||
|
||||
if (task == InitiateSpeaking)
|
||||
{
|
||||
// The payload first has an int32 representing the channel we're writing to. This is basically always writing to the
|
||||
// default channel, so just skip over those bytes... the rest is the string payload we want to speak,
|
||||
// as a null terminated string.
|
||||
if (dataLen <= 4)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto stringPayloadSize = (dataLen - 4) / sizeof(wchar_t);
|
||||
// We don't need the null terminator, because wstring intends to handle that on its own.
|
||||
const auto stringLen = stringPayloadSize - 1;
|
||||
const auto payload = std::wstring(reinterpret_cast<wchar_t*>(static_cast<char*>(data) + 4), stringLen);
|
||||
|
||||
// wprintf(L"[Narrator pid=%d]: %s\n", processId, payload.c_str());
|
||||
wprintf(L"%s\n", payload.c_str());
|
||||
fflush(stdout);
|
||||
|
||||
if (payload == L"Exiting Narrator")
|
||||
{
|
||||
g_stopEvent.SetEvent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int __cdecl wmain(int argc, wchar_t* argv[])
|
||||
{
|
||||
wprintf(L"Welcome to Narrator Buddy. Start up Narrator to start logging narrator output.\n");
|
||||
wprintf(L" Press ^C to exit\n");
|
||||
wprintf(L" If you start Narrator and don't see any output, make sure there's no leaked ETW traces with:\n\n");
|
||||
wprintf(L" logman query -ets\n\n");
|
||||
|
||||
const bool runForever = (argc == 2) &&
|
||||
std::wstring{ argv[1] } == L"-forever";
|
||||
|
||||
GUID sessionGuid;
|
||||
FAIL_FAST_IF_FAILED(::CoCreateGuid(&sessionGuid));
|
||||
|
||||
std::array<wchar_t, 64> traceSessionName{};
|
||||
FAIL_FAST_IF_FAILED(StringCchPrintf(traceSessionName.data(), static_cast<DWORD>(traceSessionName.size()), L"NarratorTraceSession_%d", ::GetCurrentProcessId()));
|
||||
|
||||
unsigned int traceSessionNameBytes = static_cast<unsigned int>((wcslen(traceSessionName.data()) + 1) * sizeof(wchar_t));
|
||||
|
||||
// Now, to get tracing. Most settings below are defaults from MSDN, except where noted.
|
||||
// First, set up a session (StartTrace) - which requires a PROPERTIES struct. This has to have
|
||||
// the session name after it in the same block of memory...
|
||||
const unsigned int propertiesByteSize = sizeof(EVENT_TRACE_PROPERTIES) + traceSessionNameBytes;
|
||||
std::vector<char> eventTracePropertiesBuffer(propertiesByteSize);
|
||||
auto properties = reinterpret_cast<EVENT_TRACE_PROPERTIES*>(eventTracePropertiesBuffer.data());
|
||||
|
||||
// Set up properties struct for a real-time session...
|
||||
properties->Wnode.BufferSize = propertiesByteSize;
|
||||
properties->Wnode.Guid = sessionGuid;
|
||||
properties->Wnode.ClientContext = 1;
|
||||
properties->Wnode.Flags = WNODE_FLAG_TRACED_GUID;
|
||||
properties->LogFileMode = EVENT_TRACE_REAL_TIME_MODE | EVENT_TRACE_USE_PAGED_MEMORY;
|
||||
properties->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES);
|
||||
properties->FlushTimer = 1;
|
||||
// Finally, copy the session name...
|
||||
memcpy(properties + 1, traceSessionName.data(), traceSessionNameBytes);
|
||||
|
||||
std::thread traceThread;
|
||||
auto joinTraceThread = wil::scope_exit([&]() {
|
||||
if (traceThread.joinable())
|
||||
{
|
||||
traceThread.join();
|
||||
}
|
||||
});
|
||||
|
||||
TRACEHANDLE session{};
|
||||
const auto rc = ::StartTrace(&session, traceSessionName.data(), properties);
|
||||
FAIL_FAST_IF(rc != ERROR_SUCCESS);
|
||||
auto stopTrace = wil::scope_exit([&]() {
|
||||
EVENT_TRACE_PROPERTIES properties{};
|
||||
properties.Wnode.BufferSize = sizeof(properties);
|
||||
properties.Wnode.Guid = sessionGuid;
|
||||
properties.Wnode.Flags = WNODE_FLAG_TRACED_GUID;
|
||||
|
||||
::ControlTrace(session, nullptr, &properties, EVENT_TRACE_CONTROL_STOP);
|
||||
});
|
||||
|
||||
constexpr GUID narratorProviderGuid = { 0x835b79e2, 0xe76a, 0x44c4, 0x98, 0x85, 0x26, 0xad, 0x12, 0x2d, 0x3b, 0x4d };
|
||||
auto hr = ::EnableTrace(TRUE /* enable */, 0 /* enableFlag */, TRACE_LEVEL_VERBOSE, &narratorProviderGuid, session);
|
||||
if (hr == ERROR_NO_SYSTEM_RESOURCES)
|
||||
{
|
||||
wprintf(L"Looks like you ran out of ETW trace slots. You'll need to free one up\n");
|
||||
return hr;
|
||||
}
|
||||
|
||||
FAIL_FAST_IF(ERROR_SUCCESS != hr);
|
||||
auto disableTrace = wil::scope_exit([&]() {
|
||||
::EnableTrace(FALSE /* enable */,
|
||||
0 /* enableFlag */,
|
||||
TRACE_LEVEL_VERBOSE,
|
||||
&narratorProviderGuid,
|
||||
session);
|
||||
});
|
||||
|
||||
// Finally, start listening (OpenTrace/ProcessTrace/CloseTrace)...
|
||||
EVENT_TRACE_LOGFILE trace{};
|
||||
trace.LoggerName = traceSessionName.data();
|
||||
trace.ProcessTraceMode = PROCESS_TRACE_MODE_EVENT_RECORD | PROCESS_TRACE_MODE_REAL_TIME;
|
||||
trace.EventRecordCallback = ProcessEtwEvent;
|
||||
|
||||
using unique_tracehandle = wil::unique_any<TRACEHANDLE, decltype(::CloseTrace), ::CloseTrace>;
|
||||
unique_tracehandle traceHandle{ ::OpenTrace(&trace) };
|
||||
|
||||
// Since the actual call to ProcessTrace blocks while it's working,
|
||||
// we spin up a separate thread to do that.
|
||||
traceThread = std::thread([traceHandle(std::move(traceHandle))]() mutable {
|
||||
::ProcessTrace(traceHandle.addressof(), 1 /* handleCount */, nullptr /* startTime */, nullptr /* endTime */);
|
||||
});
|
||||
|
||||
if (runForever)
|
||||
{
|
||||
::Sleep(INFINITE);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_stopEvent.wait(INFINITE);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user