Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 31 commits into from
Jul 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
c1fb748
This is a resurrection of #9222. This needs a lot of love, but it works
zadjii-msft Nov 7, 2022
1651994
Merge remote-tracking branch 'origin/main' into dev/migrie/f/broadcas…
zadjii-msft Nov 15, 2022
8862ed8
better borders
zadjii-msft Nov 15, 2022
d685c12
cleanup some bad names
zadjii-msft Nov 15, 2022
e21b6bb
that's not the right font
zadjii-msft Nov 15, 2022
8abe995
Merge remote-tracking branch 'origin/main' into dev/migrie/f/broadcas…
zadjii-msft Nov 16, 2022
c1c002a
proof of concept, this is gonna be way easier
zadjii-msft Nov 16, 2022
e29dc40
yea this is gonna work
zadjii-msft Nov 16, 2022
0136994
this is far easier
zadjii-msft Nov 16, 2022
8d7c1ea
better names
zadjii-msft Nov 16, 2022
8a89d81
other kinds of input
zadjii-msft Nov 17, 2022
1813a42
paste too
zadjii-msft Nov 17, 2022
521fdea
this is why I don't use vs
zadjii-msft Nov 17, 2022
0627f8f
etter HC
zadjii-msft Nov 17, 2022
6ac4579
Merge branch 'main' into dev/migrie/f/broadcast-resurrections
zadjii-msft Nov 22, 2022
2860b4e
this PC can't build but I bet this is right
zadjii-msft Nov 22, 2022
95dba3d
sure why not
zadjii-msft Nov 22, 2022
8894d80
another comment
zadjii-msft Nov 23, 2022
0c9821c
Migrate spelling-0.0.21 changes from main
DHowett Nov 23, 2022
20976f7
Merge branch 'main' into dev/migrie/f/broadcast-resurrections
zadjii-msft Feb 14, 2023
c1e4b7d
Merge branch 'main' into dev/migrie/f/broadcast-resurrections
zadjii-msft Apr 6, 2023
a5e66e2
Merge branch 'main' into dev/migrie/f/broadcast-resurrections
zadjii-msft May 31, 2023
fb90f25
Merge remote-tracking branch 'origin/main' into dev/migrie/f/broadcas…
zadjii-msft Jul 5, 2023
7d2e6ad
I guess I half-assed that merge before I left for leave
zadjii-msft Jul 5, 2023
2fb183a
as requested, only raise the event if broadcast is actually enabled
zadjii-msft Jul 6, 2023
bea849a
Merge remote-tracking branch 'origin/main' into dev/migrie/f/broadcas…
zadjii-msft Jul 12, 2023
1a5852d
spel
zadjii-msft Jul 12, 2023
3cdb3db
fix an edge case of adding a new pane to a broadcasting tab
zadjii-msft Jul 17, 2023
aad2b68
switch to revokers for fun and profit
zadjii-msft Jul 17, 2023
5a78021
Merge remote-tracking branch 'origin/main' into dev/migrie/f/broadcas…
zadjii-msft Jul 19, 2023
2dce177
add a comment to clarify
zadjii-msft Jul 19, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
zadjii-msft marked this conversation as resolved.
Show resolved Hide resolved
}

}
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();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Idle thought... should this not go via the same path as RawWriteString? PasteTextFromClipboard means "ask the app for the clipboard content," which could result in one ContentDialog per control (!) confirming the paste.

I'd expect this:

sequenceDiagram
    TerminalPage-->>TermControl: PasteTextFromClipboard
    TermControl-->>TerminalPage: GetTextFromClipboard
    TerminalPage-->>TermControl: Here's the text bud
    TermControl-->>Pane: SentString("here's the...")
    Pane-->>OtherTermControl: RawWriteString("here's the...")
    Pane-->>OtherTermControl: RawWriteString("here's the...")
    Pane-->>OtherTermControl: RawWriteString("here's the...")
Loading

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note from discussion: bracketed paste, make sure that works

Copy link
Member Author

@zadjii-msft zadjii-msft Feb 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. This is... less trivial.

TerminalPage::_PasteFromClipboardHandler calls the event's HandleClipboardData, which is a callback that Interactivity set up to call _core->PasteText 1 when the paste is completed. However, Core & Interactivity don't actually know anything about broadcast mode. That's all good and well.

I suppose this could be done by finagling the paste handling a bit. We'd bubble up to TermControl, it'd register the paste callback itself, and in its own handler it could decide to re-raise a event Windows.Foundation.TypedEventHandler<Object, StringSentEventArgs> PasteSent;2, which pane could listen to, and then propagate. Feels kinda extra dirty, tbh.

I don't think the dialog will pop multiple times, on account of the fact that there can only ever be one dialog. I'll have to validate that though.

Alternatively, we could do the broadcast check inside TerminalPage::_PasteFromClipboardHandler and just call WalkTree([text](){ p->Control().PasteText(); }); and plumb that through. Might be tricky to determine that though. TermControl doesn't expose the Interactivity out of it, so we couldn't compare the sender well, I suppose if we're not in broadcast mode, we could just call the event callback as before.

Thoughts?

Footnotes

  1. core->PasteText is what handles bracketed paste, so that's fine.

  2. can't be the StringSent, cause we do need to special-case pastes

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@DHowett gibe thoughts pls, so I can merge this when I get back from break for 1.19 selfhost ☺️

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We chatted last-ish week about RawWriteString taking a "StringKind" of Clipboard, Raw, Whatever. Noting that here to close the loop. :)

}
});
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
lhecker marked this conversation as resolved.
Show resolved Hide resolved
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