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 9 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
9 changes: 9 additions & 0 deletions src/cascadia/TerminalApp/App.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,9 @@

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

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

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

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

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

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

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

<SolidColorBrush x:Key="BroadcastPaneBorderColor"
Color="{StaticResource SystemAccentColorLight2}" />
zadjii-msft marked this conversation as resolved.
Show resolved Hide resolved
</ResourceDictionary>

</ResourceDictionary.ThemeDictionaries>
Expand Down
11 changes: 11 additions & 0 deletions src/cascadia/TerminalApp/AppActionHandlers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1182,4 +1182,15 @@ namespace winrt::TerminalApp::implementation
args.Handled(handled);
}
}
void TerminalPage::_HandleToggleBroadcastInput(const IInspectable& /*sender*/,
const ActionEventArgs& args)
{
if (const auto activeTab{ _GetFocusedTabImpl() })
{
activeTab->ToggleBroadcastInput();
}

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 @@ -204,6 +204,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
98 changes: 95 additions & 3 deletions src/cascadia/TerminalApp/Pane.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ static const Duration AnimationDuration = DurationHelper::FromTimeSpan(winrt::Wi

winrt::Windows::UI::Xaml::Media::SolidColorBrush Pane::s_focusedBorderBrush = { nullptr };
winrt::Windows::UI::Xaml::Media::SolidColorBrush Pane::s_unfocusedBorderBrush = { nullptr };
winrt::Windows::UI::Xaml::Media::SolidColorBrush Pane::s_broadcastBorderBrush = { nullptr };

Pane::Pane(const Profile& profile, const TermControl& control, const bool lastFocused) :
_control{ control },
Expand All @@ -48,7 +49,7 @@ Pane::Pane(const Profile& profile, const TermControl& control, const bool lastFo
// On the first Pane's creation, lookup resources we'll use to theme the
// Pane, including the brushed to use for the focused/unfocused border
// color.
if (s_focusedBorderBrush == nullptr || s_unfocusedBorderBrush == nullptr)
if (s_focusedBorderBrush == nullptr || s_unfocusedBorderBrush == nullptr || s_broadcastBorderBrush == nullptr)
{
_SetupResources();
}
Expand Down Expand Up @@ -1381,8 +1382,8 @@ void Pane::UpdateVisuals()
{
_UpdateBorders();
}
_borderFirst.BorderBrush(_lastActive ? s_focusedBorderBrush : s_unfocusedBorderBrush);
_borderSecond.BorderBrush(_lastActive ? s_focusedBorderBrush : s_unfocusedBorderBrush);
_borderFirst.BorderBrush(_ComputeBorderColor());
_borderSecond.BorderBrush(_ComputeBorderColor());
}

// Method Description:
Expand Down Expand Up @@ -1586,6 +1587,7 @@ void Pane::_CloseChild(const bool closeFirst, const bool isDetaching)
// Add our new event handler before revoking the old one.
_connectionStateChangedToken = _control.ConnectionStateChanged({ this, &Pane::_ControlConnectionStateChangedHandler });
_warningBellToken = _control.WarningBell({ this, &Pane::_ControlWarningBellHandler });
_readOnlyChangedToken = _control.ReadOnlyChanged({ this, &Pane::_ControlReadOnlyChangedHandler });

// Revoke the old event handlers. Remove both the handlers for the panes
// themselves closing, and remove their handlers for their controls
Expand All @@ -1601,6 +1603,7 @@ void Pane::_CloseChild(const bool closeFirst, const bool isDetaching)
{
p->_control.ConnectionStateChanged(p->_connectionStateChangedToken);
p->_control.WarningBell(p->_warningBellToken);
p->_control.ReadOnlyChanged(p->_readOnlyChangedToken);
}
});
}
Expand All @@ -1609,6 +1612,7 @@ void Pane::_CloseChild(const bool closeFirst, const bool isDetaching)
remainingChild->Closed(remainingChildClosedToken);
remainingChild->_control.ConnectionStateChanged(remainingChild->_connectionStateChangedToken);
remainingChild->_control.WarningBell(remainingChild->_warningBellToken);
remainingChild->_control.ReadOnlyChanged(remainingChild->_readOnlyChangedToken);

// If we or either of our children was focused, we want to take that
// focus from them.
Expand Down Expand Up @@ -1690,6 +1694,7 @@ void Pane::_CloseChild(const bool closeFirst, const bool isDetaching)
{
p->_control.ConnectionStateChanged(p->_connectionStateChangedToken);
p->_control.WarningBell(p->_warningBellToken);
p->_control.ReadOnlyChanged(p->_readOnlyChangedToken);
}
});
}
Expand Down Expand Up @@ -3114,6 +3119,20 @@ void Pane::_SetupResources()
// will eat focus.
s_unfocusedBorderBrush = SolidColorBrush{ Colors::Black() };
}

const auto broadcastColorKey = winrt::box_value(L"BroadcastPaneBorderColor");
if (res.HasKey(broadcastColorKey))
{
winrt::Windows::Foundation::IInspectable obj = res.Lookup(broadcastColorKey);
s_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.
s_broadcastBorderBrush = SolidColorBrush{ Colors::Black() };
}
}

int Pane::GetLeafPaneCount() const noexcept
Expand Down Expand Up @@ -3159,3 +3178,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)
{
if (_IsLeaf())
{
if (_control != sourceControl && !_control.ReadOnly())
{
_control.BroadcastKeyEvent(vkey, scanCode, modifiers, keyDown);
}
}
else
{
_firstChild->BroadcastKey(sourceControl, vkey, scanCode, modifiers, keyDown);
_secondChild->BroadcastKey(sourceControl, 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)
{
if (_IsLeaf())
{
if (_control != sourceControl && !_control.ReadOnly())
{
_control.BroadcastChar(character, scanCode, modifiers);
}
}
else
{
_firstChild->BroadcastChar(sourceControl, character, scanCode, modifiers);
_secondChild->BroadcastChar(sourceControl, character, scanCode, modifiers);
}
zadjii-msft marked this conversation as resolved.
Show resolved Hide resolved
}

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 s_focusedBorderBrush;
}

if (_broadcastEnabled)
{
return s_broadcastBorderBrush;
}
zadjii-msft marked this conversation as resolved.
Show resolved Hide resolved

return s_unfocusedBorderBrush;
}
9 changes: 9 additions & 0 deletions src/cascadia/TerminalApp/Pane.h
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,10 @@ 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);

// Method Description:
// - A helper method for ad-hoc recursion on a pane tree. Walks the pane
// tree, calling a function on each pane in a depth-first pattern.
Expand Down Expand Up @@ -217,6 +221,7 @@ class Pane : public std::enable_shared_from_this<Pane>
winrt::Windows::UI::Xaml::Controls::Border _borderSecond{};
static winrt::Windows::UI::Xaml::Media::SolidColorBrush s_focusedBorderBrush;
static winrt::Windows::UI::Xaml::Media::SolidColorBrush s_unfocusedBorderBrush;
static winrt::Windows::UI::Xaml::Media::SolidColorBrush s_broadcastBorderBrush;

#pragma region Properties that need to be transferred between child / parent panes upon splitting / closing
std::shared_ptr<Pane> _firstChild{ nullptr };
Expand All @@ -237,6 +242,7 @@ class Pane : public std::enable_shared_from_this<Pane>
winrt::event_token _firstClosedToken{ 0 };
winrt::event_token _secondClosedToken{ 0 };
winrt::event_token _warningBellToken{ 0 };
winrt::event_token _readOnlyChangedToken{ 0 };

winrt::Windows::UI::Xaml::UIElement::GotFocus_revoker _gotFocusRevoker;
winrt::Windows::UI::Xaml::UIElement::LostFocus_revoker _lostFocusRevoker;
Expand All @@ -246,6 +252,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 };
winrt::Windows::Media::Playback::MediaPlayer::MediaEnded_revoker _mediaEndedRevoker;
Expand All @@ -264,6 +271,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 @@ -290,6 +298,7 @@ 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);

std::pair<float, float> _CalcChildrenSizes(const float fullSize) const;
SnapChildrenSizeResult _CalcSnappedChildrenSizes(const bool widthOrHeight, const float fullSize) const;
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
40 changes: 39 additions & 1 deletion src/cascadia/TerminalApp/TerminalTab.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,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
Expand Down Expand Up @@ -624,7 +626,7 @@ namespace winrt::TerminalApp::implementation
_nextPaneId++;
}
});

pane->EnableBroadcast(_tabStatus.IsInputBroadcastActive());
Copy link
Member

Choose a reason for hiding this comment

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

this is the scary for me - will a pane moved into a different tab continue broadcasting to the old tab? we don't do any event hookups or unhooks here.

Copy link
Member Author

Choose a reason for hiding this comment

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

Nope, works like a charm

Copy link
Member

Choose a reason for hiding this comment

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

Huh! Can you walk me through why it works?
As a reviewer I couldn't see the machinery that kept it safe. I'd rather know that we know the code is safe than that we tested and found the behavior safe 😄

Copy link
Member Author

Choose a reason for hiding this comment

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

Okay, I know this is gonna cause you physical pain to read:

  • TerminalPage::_HandleMovePane
    • TerminalPage::_MovePane
      • TerminalTab::DetachPane
        • Pane::DetachPane
          • this._DetachedHandlers -> handled in a lambda in TerminalTab::_AttachEventHandlersToPane
            • revokes all our per-pane event handlers
            • TerminalTab::_DetachEventHandlersFromControl
              • revokes per-control handlers (including these)
      • TerminalTab::AttachPane
        • reattaches them to the new tab.

// pass the old id to the new child
const auto previousId = _activePane->Id();

Expand Down Expand Up @@ -863,6 +865,8 @@ namespace winrt::TerminalApp::implementation
control.SetTaskbarProgress(events.taskbarToken);
control.ReadOnlyChanged(events.readOnlyToken);
control.FocusFollowMouseRequested(events.focusToken);
control.KeySent(events.keySentToken);
control.CharSent(events.charSentToken);

_controlEvents.erase(paneId);
}
Expand Down Expand Up @@ -934,6 +938,32 @@ namespace winrt::TerminalApp::implementation
}
});

events.keySentToken = control.KeySent([weakThis](auto&& sender, auto&& e) {
if (const auto tab{ weakThis.get() })
{
if (tab->_tabStatus.IsInputBroadcastActive())
{
if (const auto termControl{ sender.try_as<winrt::Microsoft::Terminal::Control::TermControl>() })
{
tab->_rootPane->BroadcastKey(termControl, e.VKey(), e.ScanCode(), e.Modifiers(), e.KeyDown());
}
}
}
});

events.charSentToken = control.CharSent([weakThis](auto&& sender, auto&& e) {
if (const auto tab{ weakThis.get() })
{
if (tab->_tabStatus.IsInputBroadcastActive())
{
if (const auto termControl{ sender.try_as<winrt::Microsoft::Terminal::Control::TermControl>() })
{
tab->_rootPane->BroadcastChar(termControl, e.Character(), e.ScanCode(), e.Modifiers());
}
}
}
});

_controlEvents[paneId] = events;
}

Expand Down Expand Up @@ -1629,4 +1659,12 @@ namespace winrt::TerminalApp::implementation

return Title();
}

// Method Description:
// - Toggle read-only mode on the active pane
zadjii-msft marked this conversation as resolved.
Show resolved Hide resolved
void TerminalTab::ToggleBroadcastInput()
{
_tabStatus.IsInputBroadcastActive(!_tabStatus.IsInputBroadcastActive());
_rootPane->EnableBroadcast(_tabStatus.IsInputBroadcastActive());
}
}
3 changes: 3 additions & 0 deletions src/cascadia/TerminalApp/TerminalTab.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ namespace winrt::TerminalApp::implementation
int GetLeafPaneCount() const noexcept;

void TogglePaneReadOnly();
void ToggleBroadcastInput();
std::shared_ptr<Pane> GetActivePane() const;
winrt::TerminalApp::TaskbarState GetCombinedTaskbarState() const;

Expand Down Expand Up @@ -128,6 +129,8 @@ namespace winrt::TerminalApp::implementation
winrt::event_token taskbarToken;
winrt::event_token readOnlyToken;
winrt::event_token focusToken;
winrt::event_token keySentToken;
winrt::event_token charSentToken;
};
std::unordered_map<uint32_t, ControlEventTokens> _controlEvents;

Expand Down
1 change: 1 addition & 0 deletions src/cascadia/TerminalApp/TerminalTabStatus.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
};
}

Expand Down
1 change: 1 addition & 0 deletions src/cascadia/TerminalApp/TerminalTabStatus.idl
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@ namespace TerminalApp
Boolean BellIndicator { get; set; };
UInt32 ProgressValue { get; set; };
Boolean IsReadOnlyActive { get; set; };
Boolean IsInputBroadcastActive { get; set; };
}
}
2 changes: 2 additions & 0 deletions src/cascadia/TerminalControl/ControlCore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_sendInputToConnection(wstr);
}

// Method Description:
// - Broadcasts this char to any registered CharSent handlers
zadjii-msft marked this conversation as resolved.
Show resolved Hide resolved
bool ControlCore::SendCharEvent(const wchar_t ch,
const WORD scanCode,
const ::Microsoft::Terminal::Core::ControlKeyStates modifiers)
Expand Down
Loading