mirror of
https://github.com/microsoft/terminal.git
synced 2025-12-11 04:38:24 -06:00
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:
parent
b4042eaaf0
commit
6a10ea5a7e
1
.github/actions/spelling/expect/expect.txt
vendored
1
.github/actions/spelling/expect/expect.txt
vendored
@ -2132,6 +2132,7 @@ WDDMCONSOLECONTEXT
|
||||
wdm
|
||||
webpage
|
||||
websites
|
||||
websockets
|
||||
wekyb
|
||||
wex
|
||||
wextest
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -205,6 +205,13 @@
|
||||
Glyph=""
|
||||
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=""
|
||||
Visibility="{x:Bind Item.(local:TabPaletteItem.TabStatus).IsInputBroadcastActive, Mode=OneWay}" />
|
||||
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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*/);
|
||||
|
||||
|
||||
@ -43,6 +43,12 @@
|
||||
FontSize="12"
|
||||
Glyph=""
|
||||
Visibility="{x:Bind TabStatus.IsReadOnlyActive, Mode=OneWay}" />
|
||||
<FontIcon x:Name="HeaderBroadcastIcon"
|
||||
Margin="0,0,8,0"
|
||||
FontFamily="{ThemeResource SymbolThemeFontFamily}"
|
||||
FontSize="12"
|
||||
Glyph=""
|
||||
Visibility="{x:Bind TabStatus.IsInputBroadcastActive, Mode=OneWay}" />
|
||||
<TextBlock x:Name="HeaderTextBlock"
|
||||
Text="{x:Bind Title, Mode=OneWay}"
|
||||
Visibility="Visible" />
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
@ -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);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -13,5 +13,6 @@ namespace TerminalApp
|
||||
Boolean BellIndicator { get; set; };
|
||||
UInt32 ProgressValue { get; set; };
|
||||
Boolean IsReadOnlyActive { get; set; };
|
||||
Boolean IsInputBroadcastActive { get; set; };
|
||||
}
|
||||
}
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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; };
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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();
|
||||
|
||||
|
||||
@ -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") },
|
||||
};
|
||||
}();
|
||||
|
||||
|
||||
@ -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) \
|
||||
|
||||
@ -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>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user