Skip to content

Commit

Permalink
Add support for broadcasting to all panes in a tab (#14393)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
zadjii-msft authored Jul 19, 2023
1 parent b4042ea commit 6a10ea5
Show file tree
Hide file tree
Showing 21 changed files with 416 additions and 13 deletions.
1 change: 1 addition & 0 deletions .github/actions/spelling/expect/expect.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2132,6 +2132,7 @@ WDDMCONSOLECONTEXT
wdm
webpage
websites
websockets
wekyb
wex
wextest
Expand Down
9 changes: 9 additions & 0 deletions src/cascadia/TerminalApp/App.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,9 @@

<SolidColorBrush x:Key="SettingsUiTabBrush"
Color="#0c0c0c" />

<SolidColorBrush x:Key="BroadcastPaneBorderColor"
Color="{StaticResource SystemAccentColorDark2}" />
</ResourceDictionary>

<ResourceDictionary x:Key="Light">
Expand All @@ -190,6 +193,9 @@

<SolidColorBrush x:Key="SettingsUiTabBrush"
Color="#ffffff" />

<SolidColorBrush x:Key="BroadcastPaneBorderColor"
Color="{StaticResource SystemAccentColorLight2}" />
</ResourceDictionary>

<ResourceDictionary x:Key="HighContrast">
Expand All @@ -210,6 +216,9 @@

<StaticResource x:Key="SettingsUiTabBrush"
ResourceKey="SystemControlBackgroundBaseLowBrush" />

<SolidColorBrush x:Key="BroadcastPaneBorderColor"
Color="{StaticResource SystemColorHighlightColor}" />
</ResourceDictionary>

</ResourceDictionary.ThemeDictionaries>
Expand Down
12 changes: 12 additions & 0 deletions src/cascadia/TerminalApp/AppActionHandlers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand All @@ -1294,4 +1305,5 @@ namespace winrt::TerminalApp::implementation
}
args.Handled(true);
}

}
7 changes: 7 additions & 0 deletions src/cascadia/TerminalApp/CommandPalette.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
79 changes: 77 additions & 2 deletions src/cascadia/TerminalApp/Pane.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()
{
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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;
}
12 changes: 12 additions & 0 deletions src/cascadia/TerminalApp/Pane.h
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -142,6 +143,11 @@ class Pane : public std::enable_shared_from_this<Pane>

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:
Expand Down Expand Up @@ -251,6 +257,7 @@ class Pane : public std::enable_shared_from_this<Pane>
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();
Expand All @@ -263,6 +270,7 @@ class Pane : public std::enable_shared_from_this<Pane>
Borders _borders{ Borders::None };

bool _zoomed{ false };
bool _broadcastEnabled{ false };

winrt::Windows::Media::Playback::MediaPlayer _bellPlayer{ nullptr };
bool _bellPlayerCreated{ false };
Expand All @@ -281,6 +289,7 @@ class Pane : public std::enable_shared_from_this<Pane>
void _SetupEntranceAnimation();
void _UpdateBorders();
Borders _GetCommonBorders();
winrt::Windows::UI::Xaml::Media::SolidColorBrush _ComputeBorderColor();

bool _Resize(const winrt::Microsoft::Terminal::Settings::Model::ResizeDirection& direction);

Expand All @@ -307,6 +316,9 @@ class Pane : public std::enable_shared_from_this<Pane>
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*/);

Expand Down
6 changes: 6 additions & 0 deletions src/cascadia/TerminalApp/TabHeaderControl.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -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" />
Expand Down
33 changes: 33 additions & 0 deletions src/cascadia/TerminalApp/TerminalPage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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)
Expand Down
Loading

0 comments on commit 6a10ea5

Please sign in to comment.