Add support for broadcasting to all panes in a tab (#14393)

Resurrection of #9222. 
Spec draft in #9365.

Consensus from community feedback is that the whole of that spec is
_nice to have_, but what REALLY matters is just broadcasting to all the
panes in a tab. So, in the interest of best serving our community, I'm
pushing this out as the initial implementation, before we figure out the
rest of design. Regardless of how we choose to implement the rest of the
features detailed in the spec, the UX for this part of the feature
remains the same.

This PR adds a new action: `toggleBroadcastInput`. Performing this
action starts broadcasting to all panes in this tab. Keystrokes in one
pane will be sent to all panes in the tab.

An icon in the tab is used to indicate when this mode is active.
Furthermore, the borders of all panes will be highlighted with
`SystemAccentColorDark2`/`SystemAccentColorLight2` (depending on the
theme), to indicate they're also active.

* [x] Closes #2634. 
  - (we should lick a reserved thread for follow-ups)

Co-authored-by: Don-Vito khvitaly@gmail.com
This commit is contained in:
Mike Griese 2023-07-19 10:45:25 -05:00 committed by GitHub
parent b4042eaaf0
commit 6a10ea5a7e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 416 additions and 13 deletions

View File

@ -2132,6 +2132,7 @@ WDDMCONSOLECONTEXT
wdm
webpage
websites
websockets
wekyb
wex
wextest

View File

@ -174,6 +174,9 @@
<SolidColorBrush x:Key="SettingsUiTabBrush"
Color="#0c0c0c" />
<SolidColorBrush x:Key="BroadcastPaneBorderColor"
Color="{StaticResource SystemAccentColorDark2}" />
</ResourceDictionary>
<ResourceDictionary x:Key="Light">
@ -190,6 +193,9 @@
<SolidColorBrush x:Key="SettingsUiTabBrush"
Color="#ffffff" />
<SolidColorBrush x:Key="BroadcastPaneBorderColor"
Color="{StaticResource SystemAccentColorLight2}" />
</ResourceDictionary>
<ResourceDictionary x:Key="HighContrast">
@ -210,6 +216,9 @@
<StaticResource x:Key="SettingsUiTabBrush"
ResourceKey="SystemControlBackgroundBaseLowBrush" />
<SolidColorBrush x:Key="BroadcastPaneBorderColor"
Color="{StaticResource SystemColorHighlightColor}" />
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>

View File

@ -1272,6 +1272,17 @@ namespace winrt::TerminalApp::implementation
}
}
void TerminalPage::_HandleToggleBroadcastInput(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (const auto activeTab{ _GetFocusedTabImpl() })
{
activeTab->ToggleBroadcastInput();
args.Handled(true);
}
// If the focused tab wasn't a TerminalTab, then leave handled=false
}
void TerminalPage::_HandleRestartConnection(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
@ -1294,4 +1305,5 @@ namespace winrt::TerminalApp::implementation
}
args.Handled(true);
}
}

View File

@ -205,6 +205,13 @@
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>
</Grid>
</DataTemplate>

View File

@ -108,6 +108,7 @@ void Pane::_setupControlEvents()
_controlEvents._WarningBell = _control.WarningBell(winrt::auto_revoke, { this, &Pane::_ControlWarningBellHandler });
_controlEvents._CloseTerminalRequested = _control.CloseTerminalRequested(winrt::auto_revoke, { this, &Pane::_CloseTerminalRequestedHandler });
_controlEvents._RestartTerminalRequested = _control.RestartTerminalRequested(winrt::auto_revoke, { this, &Pane::_RestartTerminalRequestedHandler });
_controlEvents._ReadOnlyChanged = _control.ReadOnlyChanged(winrt::auto_revoke, { this, &Pane::_ControlReadOnlyChangedHandler });
}
void Pane::_removeControlEvents()
{
@ -1434,8 +1435,9 @@ void Pane::UpdateVisuals()
{
_UpdateBorders();
}
_borderFirst.BorderBrush(_lastActive ? _themeResources.focusedBorderBrush : _themeResources.unfocusedBorderBrush);
_borderSecond.BorderBrush(_lastActive ? _themeResources.focusedBorderBrush : _themeResources.unfocusedBorderBrush);
const auto& brush{ _ComputeBorderColor() };
_borderFirst.BorderBrush(brush);
_borderSecond.BorderBrush(brush);
}
// Method Description:
@ -3177,3 +3179,76 @@ void Pane::CollectTaskbarStates(std::vector<winrt::TerminalApp::TaskbarState>& s
_secondChild->CollectTaskbarStates(states);
}
}
void Pane::EnableBroadcast(bool enabled)
{
if (_IsLeaf())
{
_broadcastEnabled = enabled;
UpdateVisuals();
}
else
{
_firstChild->EnableBroadcast(enabled);
_secondChild->EnableBroadcast(enabled);
}
}
void Pane::BroadcastKey(const winrt::Microsoft::Terminal::Control::TermControl& sourceControl,
const WORD vkey,
const WORD scanCode,
const winrt::Microsoft::Terminal::Core::ControlKeyStates modifiers,
const bool keyDown)
{
WalkTree([&](const auto& pane) {
if (pane->_IsLeaf() && pane->_control != sourceControl && !pane->_control.ReadOnly())
{
pane->_control.RawWriteKeyEvent(vkey, scanCode, modifiers, keyDown);
}
});
}
void Pane::BroadcastChar(const winrt::Microsoft::Terminal::Control::TermControl& sourceControl,
const wchar_t character,
const WORD scanCode,
const winrt::Microsoft::Terminal::Core::ControlKeyStates modifiers)
{
WalkTree([&](const auto& pane) {
if (pane->_IsLeaf() && pane->_control != sourceControl && !pane->_control.ReadOnly())
{
pane->_control.RawWriteChar(character, scanCode, modifiers);
}
});
}
void Pane::BroadcastString(const winrt::Microsoft::Terminal::Control::TermControl& sourceControl,
const winrt::hstring& text)
{
WalkTree([&](const auto& pane) {
if (pane->_IsLeaf() && pane->_control != sourceControl && !pane->_control.ReadOnly())
{
pane->_control.RawWriteString(text);
}
});
}
void Pane::_ControlReadOnlyChangedHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/,
const winrt::Windows::Foundation::IInspectable& /*e*/)
{
UpdateVisuals();
}
winrt::Windows::UI::Xaml::Media::SolidColorBrush Pane::_ComputeBorderColor()
{
if (_lastActive)
{
return _themeResources.focusedBorderBrush;
}
if (_broadcastEnabled && (_IsLeaf() && !_control.ReadOnly()))
{
return _themeResources.broadcastBorderBrush;
}
return _themeResources.unfocusedBorderBrush;
}

View File

@ -55,6 +55,7 @@ struct PaneResources
{
winrt::Windows::UI::Xaml::Media::SolidColorBrush focusedBorderBrush{ nullptr };
winrt::Windows::UI::Xaml::Media::SolidColorBrush unfocusedBorderBrush{ nullptr };
winrt::Windows::UI::Xaml::Media::SolidColorBrush broadcastBorderBrush{ nullptr };
};
class Pane : public std::enable_shared_from_this<Pane>
@ -142,6 +143,11 @@ public:
bool ContainsReadOnly() const;
void EnableBroadcast(bool enabled);
void BroadcastKey(const winrt::Microsoft::Terminal::Control::TermControl& sourceControl, const WORD vkey, const WORD scanCode, const winrt::Microsoft::Terminal::Core::ControlKeyStates modifiers, const bool keyDown);
void BroadcastChar(const winrt::Microsoft::Terminal::Control::TermControl& sourceControl, const wchar_t vkey, const WORD scanCode, const winrt::Microsoft::Terminal::Core::ControlKeyStates modifiers);
void BroadcastString(const winrt::Microsoft::Terminal::Control::TermControl& sourceControl, const winrt::hstring& text);
void UpdateResources(const PaneResources& resources);
// Method Description:
@ -251,6 +257,7 @@ private:
winrt::Microsoft::Terminal::Control::TermControl::WarningBell_revoker _WarningBell;
winrt::Microsoft::Terminal::Control::TermControl::CloseTerminalRequested_revoker _CloseTerminalRequested;
winrt::Microsoft::Terminal::Control::TermControl::RestartTerminalRequested_revoker _RestartTerminalRequested;
winrt::Microsoft::Terminal::Control::TermControl::ReadOnlyChanged_revoker _ReadOnlyChanged;
} _controlEvents;
void _setupControlEvents();
void _removeControlEvents();
@ -263,6 +270,7 @@ private:
Borders _borders{ Borders::None };
bool _zoomed{ false };
bool _broadcastEnabled{ false };
winrt::Windows::Media::Playback::MediaPlayer _bellPlayer{ nullptr };
bool _bellPlayerCreated{ false };
@ -281,6 +289,7 @@ private:
void _SetupEntranceAnimation();
void _UpdateBorders();
Borders _GetCommonBorders();
winrt::Windows::UI::Xaml::Media::SolidColorBrush _ComputeBorderColor();
bool _Resize(const winrt::Microsoft::Terminal::Settings::Model::ResizeDirection& direction);
@ -307,6 +316,9 @@ private:
const winrt::Windows::UI::Xaml::RoutedEventArgs& e);
void _ControlLostFocusHandler(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Windows::UI::Xaml::RoutedEventArgs& e);
void _ControlReadOnlyChangedHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& e);
void _CloseTerminalRequestedHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& /*args*/);
void _RestartTerminalRequestedHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& /*args*/);

View File

@ -43,6 +43,12 @@
FontSize="12"
Glyph="&#xE72E;"
Visibility="{x:Bind TabStatus.IsReadOnlyActive, Mode=OneWay}" />
<FontIcon x:Name="HeaderBroadcastIcon"
Margin="0,0,8,0"
FontFamily="{ThemeResource SymbolThemeFontFamily}"
FontSize="12"
Glyph="&#xEC05;"
Visibility="{x:Bind TabStatus.IsInputBroadcastActive, Mode=OneWay}" />
<TextBlock x:Name="HeaderTextBlock"
Text="{x:Bind Title, Mode=OneWay}"
Visibility="Visible" />

View File

@ -2787,6 +2787,24 @@ namespace winrt::TerminalApp::implementation
// - Paste text from the Windows Clipboard to the focused terminal
void TerminalPage::_PasteText()
{
// First, check if we're in broadcast input mode. If so, let's tell all
// the controls to paste.
if (const auto& tab{ _GetFocusedTabImpl() })
{
if (tab->TabStatus().IsInputBroadcastActive())
{
tab->GetRootPane()->WalkTree([](auto&& pane) {
if (auto control = pane->GetTerminalControl())
{
control.PasteTextFromClipboard();
}
});
return;
}
}
// The focused tab wasn't in broadcast mode. No matter. Just ask the
// current one to paste.
if (const auto& control{ _GetActiveControl() })
{
control.PasteTextFromClipboard();
@ -4570,6 +4588,21 @@ namespace winrt::TerminalApp::implementation
// will eat focus.
_paneResources.unfocusedBorderBrush = SolidColorBrush{ Colors::Black() };
}
const auto broadcastColorKey = winrt::box_value(L"BroadcastPaneBorderColor");
if (res.HasKey(broadcastColorKey))
{
// MAKE SURE TO USE ThemeLookup
auto obj = ThemeLookup(res, requestedTheme, broadcastColorKey);
_paneResources.broadcastBorderBrush = obj.try_as<winrt::Windows::UI::Xaml::Media::SolidColorBrush>();
}
else
{
// DON'T use Transparent here - if it's "Transparent", then it won't
// be able to hittest for clicks, and then clicking on the border
// will eat focus.
_paneResources.broadcastBorderBrush = SolidColorBrush{ Colors::Black() };
}
}
void TerminalPage::WindowActivated(const bool activated)

View File

@ -512,6 +512,8 @@ namespace winrt::TerminalApp::implementation
}
return false;
});
pane->EnableBroadcast(_tabStatus.IsInputBroadcastActive());
// Make sure to take the ID before calling Split() - Split() will clear out the active pane's ID
const auto activePaneId = _activePane->Id();
// Depending on which direction will be split, the new pane can be
@ -620,7 +622,7 @@ namespace winrt::TerminalApp::implementation
_nextPaneId++;
}
});
pane->EnableBroadcast(_tabStatus.IsInputBroadcastActive());
// pass the old id to the new child
const auto previousId = _activePane->Id();
@ -890,6 +892,10 @@ namespace winrt::TerminalApp::implementation
control.ReadOnlyChanged(events.readOnlyToken);
control.FocusFollowMouseRequested(events.focusToken);
events.KeySent.revoke();
events.CharSent.revoke();
events.StringSent.revoke();
_controlEvents.erase(paneId);
}
}
@ -964,7 +970,12 @@ namespace winrt::TerminalApp::implementation
}
});
_controlEvents[paneId] = events;
if (_tabStatus.IsInputBroadcastActive())
{
_addBroadcastHandlers(control, events);
}
_controlEvents[paneId] = std::move(events);
}
// Method Description:
@ -1763,4 +1774,88 @@ namespace winrt::TerminalApp::implementation
return Title();
}
// Method Description:
// - Toggle broadcasting input to all the panes in this tab.
void TerminalTab::ToggleBroadcastInput()
{
const bool newIsBroadcasting = !_tabStatus.IsInputBroadcastActive();
_tabStatus.IsInputBroadcastActive(newIsBroadcasting);
_rootPane->EnableBroadcast(newIsBroadcasting);
auto weakThis{ get_weak() };
// When we change the state of broadcasting, add or remove event
// handlers appropriately, so that controls won't be propagating events
// needlessly if no one is listening.
_rootPane->WalkTree([&](const auto& p) {
const auto paneId = p->Id();
if (!paneId.has_value())
{
return;
}
if (const auto& control{ p->GetTerminalControl() })
{
auto it = _controlEvents.find(*paneId);
if (it != _controlEvents.end())
{
auto& events = it->second;
// Always clear out old ones, just in case.
events.KeySent.revoke();
events.CharSent.revoke();
events.StringSent.revoke();
if (newIsBroadcasting)
{
_addBroadcastHandlers(control, events);
}
}
}
});
}
void TerminalTab::_addBroadcastHandlers(const TermControl& termControl, ControlEventTokens& events)
{
auto weakThis{ get_weak() };
// ADD EVENT HANDLERS HERE
events.KeySent = termControl.KeySent(winrt::auto_revoke, [weakThis](auto&& sender, auto&& e) {
if (const auto tab{ weakThis.get() })
{
if (tab->_tabStatus.IsInputBroadcastActive())
{
tab->_rootPane->BroadcastKey(sender.try_as<TermControl>(),
e.VKey(),
e.ScanCode(),
e.Modifiers(),
e.KeyDown());
}
}
});
events.CharSent = termControl.CharSent(winrt::auto_revoke, [weakThis](auto&& sender, auto&& e) {
if (const auto tab{ weakThis.get() })
{
if (tab->_tabStatus.IsInputBroadcastActive())
{
tab->_rootPane->BroadcastChar(sender.try_as<TermControl>(),
e.Character(),
e.ScanCode(),
e.Modifiers());
}
}
});
events.StringSent = termControl.StringSent(winrt::auto_revoke, [weakThis](auto&& sender, auto&& e) {
if (const auto tab{ weakThis.get() })
{
if (tab->_tabStatus.IsInputBroadcastActive())
{
tab->_rootPane->BroadcastString(sender.try_as<TermControl>(),
e.Text());
}
}
});
}
}

View File

@ -85,6 +85,7 @@ namespace winrt::TerminalApp::implementation
void TogglePaneReadOnly();
void SetPaneReadOnly(const bool readOnlyState);
void ToggleBroadcastInput();
std::shared_ptr<Pane> GetActivePane() const;
winrt::TerminalApp::TaskbarState GetCombinedTaskbarState() const;
@ -133,6 +134,10 @@ namespace winrt::TerminalApp::implementation
winrt::event_token taskbarToken;
winrt::event_token readOnlyToken;
winrt::event_token focusToken;
winrt::Microsoft::Terminal::Control::TermControl::KeySent_revoker KeySent;
winrt::Microsoft::Terminal::Control::TermControl::CharSent_revoker CharSent;
winrt::Microsoft::Terminal::Control::TermControl::StringSent_revoker StringSent;
};
std::unordered_map<uint32_t, ControlEventTokens> _controlEvents;
@ -179,6 +184,8 @@ namespace winrt::TerminalApp::implementation
virtual winrt::Windows::UI::Xaml::Media::Brush _BackgroundBrush() override;
void _addBroadcastHandlers(const winrt::Microsoft::Terminal::Control::TermControl& control, ControlEventTokens& events);
friend class ::TerminalAppLocalTests::TabTests;
};
}

View File

@ -18,6 +18,7 @@ namespace winrt::TerminalApp::implementation
WINRT_OBSERVABLE_PROPERTY(bool, BellIndicator, _PropertyChangedHandlers);
WINRT_OBSERVABLE_PROPERTY(bool, IsReadOnlyActive, _PropertyChangedHandlers);
WINRT_OBSERVABLE_PROPERTY(uint32_t, ProgressValue, _PropertyChangedHandlers);
WINRT_OBSERVABLE_PROPERTY(bool, IsInputBroadcastActive, _PropertyChangedHandlers);
};
}

View File

@ -13,5 +13,6 @@ namespace TerminalApp
Boolean BellIndicator { get; set; };
UInt32 ProgressValue { get; set; };
Boolean IsReadOnlyActive { get; set; };
Boolean IsInputBroadcastActive { get; set; };
}
}

View File

@ -15,3 +15,6 @@
#include "FoundResultsArgs.g.cpp"
#include "ShowWindowArgs.g.cpp"
#include "UpdateSelectionMarkersEventArgs.g.cpp"
#include "KeySentEventArgs.g.cpp"
#include "CharSentEventArgs.g.cpp"
#include "StringSentEventArgs.g.cpp"

View File

@ -15,6 +15,9 @@
#include "FoundResultsArgs.g.h"
#include "ShowWindowArgs.g.h"
#include "UpdateSelectionMarkersEventArgs.g.h"
#include "KeySentEventArgs.g.h"
#include "CharSentEventArgs.g.h"
#include "StringSentEventArgs.g.h"
namespace winrt::Microsoft::Terminal::Control::implementation
{
@ -179,6 +182,43 @@ namespace winrt::Microsoft::Terminal::Control::implementation
WINRT_PROPERTY(bool, ClearMarkers, false);
};
struct KeySentEventArgs : public KeySentEventArgsT<KeySentEventArgs>
{
public:
KeySentEventArgs(const WORD vkey, const WORD scanCode, const winrt::Microsoft::Terminal::Core::ControlKeyStates modifiers, const bool keyDown) :
_VKey(vkey),
_ScanCode(scanCode),
_Modifiers(modifiers),
_KeyDown(keyDown) {}
WINRT_PROPERTY(WORD, VKey);
WINRT_PROPERTY(WORD, ScanCode);
WINRT_PROPERTY(winrt::Microsoft::Terminal::Core::ControlKeyStates, Modifiers);
WINRT_PROPERTY(bool, KeyDown, false);
};
struct CharSentEventArgs : public CharSentEventArgsT<CharSentEventArgs>
{
public:
CharSentEventArgs(const wchar_t character, const WORD scanCode, const winrt::Microsoft::Terminal::Core::ControlKeyStates modifiers) :
_Character(character),
_ScanCode(scanCode),
_Modifiers(modifiers) {}
WINRT_PROPERTY(wchar_t, Character);
WINRT_PROPERTY(WORD, ScanCode);
WINRT_PROPERTY(winrt::Microsoft::Terminal::Core::ControlKeyStates, Modifiers);
};
struct StringSentEventArgs : public StringSentEventArgsT<StringSentEventArgs>
{
public:
StringSentEventArgs(const winrt::hstring& text) :
_Text(text) {}
WINRT_PROPERTY(winrt::hstring, Text);
};
}
namespace winrt::Microsoft::Terminal::Control::factory_implementation

View File

@ -89,4 +89,24 @@ namespace Microsoft.Terminal.Control
{
Boolean ClearMarkers { get; };
}
runtimeclass KeySentEventArgs
{
UInt16 VKey { get; };
UInt16 ScanCode { get; };
Microsoft.Terminal.Core.ControlKeyStates Modifiers { get; };
Boolean KeyDown { get; };
}
runtimeclass CharSentEventArgs
{
Char Character { get; };
UInt16 ScanCode { get; };
Microsoft.Terminal.Core.ControlKeyStates Modifiers { get; };
}
runtimeclass StringSentEventArgs
{
String Text { get; };
}
}

View File

@ -524,7 +524,13 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - <none>
void TermControl::SendInput(const winrt::hstring& wstr)
{
_core.SendInput(wstr);
// only broadcast if there's an actual listener. Saves the overhead of some object creation.
if (_StringSentHandlers)
{
_StringSentHandlers(*this, winrt::make<StringSentEventArgs>(wstr));
}
RawWriteString(wstr);
}
void TermControl::ClearBuffer(Control::ClearBufferType clearType)
{
@ -1083,10 +1089,31 @@ namespace winrt::Microsoft::Terminal::Control::implementation
modifiers |= ControlKeyStates::EnhancedKey;
}
const auto handled = _core.SendCharEvent(ch, scanCode, modifiers);
// Broadcast the character to all listeners
// only broadcast if there's an actual listener. Saves the overhead of some object creation.
if (_CharSentHandlers)
{
auto charSentArgs = winrt::make<CharSentEventArgs>(ch, scanCode, modifiers);
_CharSentHandlers(*this, charSentArgs);
}
const auto handled = RawWriteChar(ch, scanCode, modifiers);
e.Handled(handled);
}
bool TermControl::RawWriteChar(const wchar_t character,
const WORD scanCode,
const winrt::Microsoft::Terminal::Core::ControlKeyStates modifiers)
{
return _core.SendCharEvent(character, scanCode, modifiers);
}
void TermControl::RawWriteString(const winrt::hstring& text)
{
_core.SendInput(text);
}
// Method Description:
// - Manually handles key events for certain keys that can't be passed to us
// normally. Namely, the keys we're concerned with are F7 down and Alt up.
@ -1333,6 +1360,22 @@ namespace winrt::Microsoft::Terminal::Control::implementation
const WORD scanCode,
const ControlKeyStates modifiers,
const bool keyDown)
{
// Broadcast the key to all listeners
// only broadcast if there's an actual listener. Saves the overhead of some object creation.
if (_KeySentHandlers)
{
auto keySentArgs = winrt::make<KeySentEventArgs>(vkey, scanCode, modifiers, keyDown);
_KeySentHandlers(*this, keySentArgs);
}
return RawWriteKeyEvent(vkey, scanCode, modifiers, keyDown);
}
bool TermControl::RawWriteKeyEvent(const WORD vkey,
const WORD scanCode,
const winrt::Microsoft::Terminal::Core::ControlKeyStates modifiers,
const bool keyDown)
{
const auto window = CoreWindow::GetForCurrentThread();
@ -2587,7 +2630,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return;
}
_core.SendInput(text);
// SendInput will take care of broadcasting for us.
SendInput(text);
}
// Method Description:
@ -2666,7 +2710,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
try
{
auto link{ co_await e.DataView().GetApplicationLinkAsync() };
_core.PasteText(link.AbsoluteUri());
_pasteTextWithBroadcast(link.AbsoluteUri());
}
CATCH_LOG();
}
@ -2675,7 +2719,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
try
{
auto link{ co_await e.DataView().GetWebLinkAsync() };
_core.PasteText(link.AbsoluteUri());
_pasteTextWithBroadcast(link.AbsoluteUri());
}
CATCH_LOG();
}
@ -2684,7 +2728,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
try
{
auto text{ co_await e.DataView().GetTextAsync() };
_core.PasteText(text);
_pasteTextWithBroadcast(text);
}
CATCH_LOG();
}
@ -2812,11 +2856,27 @@ namespace winrt::Microsoft::Terminal::Control::implementation
allPathsString += fullPath;
}
_core.PasteText(winrt::hstring{ allPathsString });
_pasteTextWithBroadcast(winrt::hstring{ allPathsString });
}
}
}
// Method Description:
// - Paste this text, and raise a StringSent, to potentially broadcast this
// text to other controls in the app. For certain interactions, like
// drag/dropping a file, we want to act like we "pasted" the text (even if
// the text didn't come from the clipboard). This lets those interactions
// broadcast as well.
void TermControl::_pasteTextWithBroadcast(const winrt::hstring& text)
{
// only broadcast if there's an actual listener. Saves the overhead of some object creation.
if (_StringSentHandlers)
{
_StringSentHandlers(*this, winrt::make<StringSentEventArgs>(text));
}
_core.PasteText(text);
}
// Method Description:
// - Handle the DragOver event. We'll signal that the drag operation we
// support is the "copy" operation, and we'll also customize the

View File

@ -146,6 +146,10 @@ namespace winrt::Microsoft::Terminal::Control::implementation
void AdjustOpacity(const double opacity, const bool relative);
bool RawWriteKeyEvent(const WORD vkey, const WORD scanCode, const winrt::Microsoft::Terminal::Core::ControlKeyStates modifiers, const bool keyDown);
bool RawWriteChar(const wchar_t character, const WORD scanCode, const winrt::Microsoft::Terminal::Core::ControlKeyStates modifiers);
void RawWriteString(const winrt::hstring& text);
void ShowContextMenu();
void Detach();
@ -180,7 +184,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
TYPED_EVENT(FocusFollowMouseRequested, IInspectable, IInspectable);
TYPED_EVENT(Initialized, Control::TermControl, Windows::UI::Xaml::RoutedEventArgs);
TYPED_EVENT(WarningBell, IInspectable, IInspectable);
TYPED_EVENT(KeySent, IInspectable, Control::KeySentEventArgs);
TYPED_EVENT(CharSent, IInspectable, Control::CharSentEventArgs);
TYPED_EVENT(StringSent, IInspectable, Control::StringSentEventArgs);
// clang-format on
WINRT_OBSERVABLE_PROPERTY(winrt::Windows::UI::Xaml::Media::Brush, BackgroundBrush, _PropertyChangedHandlers, nullptr);
@ -355,6 +361,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
til::point _toPosInDips(const Core::Point terminalCellPos);
void _throttledUpdateScrollbar(const ScrollBarUpdate& update);
void _pasteTextWithBroadcast(const winrt::hstring& text);
void _contextMenuHandler(IInspectable sender, Control::ContextMenuRequestedEventArgs args);
void _showContextMenuAt(const til::point& controlRelativePos);

View File

@ -53,6 +53,9 @@ 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, KeySentEventArgs> KeySent;
event Windows.Foundation.TypedEventHandler<Object, CharSentEventArgs> CharSent;
event Windows.Foundation.TypedEventHandler<Object, StringSentEventArgs> StringSent;
Microsoft.UI.Xaml.Controls.CommandBarFlyout ContextMenu { get; };
Microsoft.UI.Xaml.Controls.CommandBarFlyout SelectionContextMenu { get; };
@ -94,6 +97,9 @@ namespace Microsoft.Terminal.Control
void ToggleShaderEffects();
void SendInput(String input);
Boolean RawWriteKeyEvent(UInt16 vkey, UInt16 scanCode, Microsoft.Terminal.Core.ControlKeyStates modifiers, Boolean keyDown);
Boolean RawWriteChar(Char character, UInt16 scanCode, Microsoft.Terminal.Core.ControlKeyStates modifiers);
void RawWriteString(String text);
void BellLightOn();

View File

@ -93,6 +93,7 @@ static constexpr std::string_view ColorSelectionKey{ "experimental.colorSelectio
static constexpr std::string_view ShowContextMenuKey{ "showContextMenu" };
static constexpr std::string_view ExpandSelectionToWordKey{ "expandSelectionToWord" };
static constexpr std::string_view RestartConnectionKey{ "restartConnection" };
static constexpr std::string_view ToggleBroadcastInputKey{ "toggleBroadcastInput" };
static constexpr std::string_view ActionKey{ "action" };
@ -424,6 +425,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
{ ShortcutAction::ShowContextMenu, RS_(L"ShowContextMenuCommandKey") },
{ ShortcutAction::ExpandSelectionToWord, RS_(L"ExpandSelectionToWordCommandKey") },
{ ShortcutAction::RestartConnection, RS_(L"RestartConnectionKey") },
{ ShortcutAction::ToggleBroadcastInput, RS_(L"ToggleBroadcastInputCommandKey") },
};
}();

View File

@ -106,7 +106,8 @@
ON_ALL_ACTIONS(ShowContextMenu) \
ON_ALL_ACTIONS(ExpandSelectionToWord) \
ON_ALL_ACTIONS(CloseOtherPanes) \
ON_ALL_ACTIONS(RestartConnection)
ON_ALL_ACTIONS(RestartConnection) \
ON_ALL_ACTIONS(ToggleBroadcastInput)
#define ALL_SHORTCUT_ACTIONS_WITH_ARGS \
ON_ALL_ACTIONS_WITH_ARGS(AdjustFontSize) \

View File

@ -684,6 +684,10 @@
<data name="CloseOtherPanesCommandKey" xml:space="preserve">
<value>Close all other panes</value>
</data>
<data name="ToggleBroadcastInputCommandKey" xml:space="preserve">
<value>Toggle broadcast input to all panes</value>
<comment>When enabled, input will go to all panes in this tab simultaneously</comment>
</data>
<data name="RestartConnectionKey" xml:space="preserve">
<value>Restart connection</value>
</data>