diff --git a/src/cascadia/TerminalApp/Pane.cpp b/src/cascadia/TerminalApp/Pane.cpp index 8183ba064e9..0d0fe1a71b1 100644 --- a/src/cascadia/TerminalApp/Pane.cpp +++ b/src/cascadia/TerminalApp/Pane.cpp @@ -1384,6 +1384,18 @@ Profile Pane::GetFocusedProfile() return lastFocused ? lastFocused->_profile : nullptr; } +// Method Description: +// - Returns true if the connection state of this pane is closed. If this Pane is not a leaf this will +// return false. +// Arguments: +// - +// Return Value: +// - true if the connection state of this Pane is closed. +bool Pane::IsConnectionClosed() const +{ + return _control && _control.ConnectionState() >= ConnectionState::Closed; +} + // Method Description: // - Returns true if this pane was the last pane to be focused in a tree of panes. // Arguments: diff --git a/src/cascadia/TerminalApp/Pane.h b/src/cascadia/TerminalApp/Pane.h index 6920a90b341..7c04bcabc09 100644 --- a/src/cascadia/TerminalApp/Pane.h +++ b/src/cascadia/TerminalApp/Pane.h @@ -75,6 +75,7 @@ class Pane : public std::enable_shared_from_this winrt::Microsoft::Terminal::Control::TermControl GetLastFocusedTerminalControl(); winrt::Microsoft::Terminal::Control::TermControl GetTerminalControl(); winrt::Microsoft::Terminal::Settings::Model::Profile GetFocusedProfile(); + bool IsConnectionClosed() const; // Method Description: // - If this is a leaf pane, return its profile. diff --git a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw index df28d046daf..71b0692708d 100644 --- a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw @@ -891,4 +891,10 @@ If set, the command will be appended to the profile's default command instead of replacing it. + + Restart Connection + + + Restart the active pane connection + diff --git a/src/cascadia/TerminalApp/TabHeaderControl.xaml b/src/cascadia/TerminalApp/TabHeaderControl.xaml index 5fde709ecbf..3c5c7f7f053 100644 --- a/src/cascadia/TerminalApp/TabHeaderControl.xaml +++ b/src/cascadia/TerminalApp/TabHeaderControl.xaml @@ -43,6 +43,12 @@ FontSize="12" Glyph="" Visibility="{x:Bind TabStatus.IsReadOnlyActive, Mode=OneWay}" /> + () }; if (!menu) @@ -4864,6 +4863,14 @@ namespace winrt::TerminalApp::implementation makeItem(RS_(L"PaneClose"), L"\xE89F", ActionAndArgs{ ShortcutAction::ClosePane, nullptr }); } + if (const auto pane{ _GetFocusedTabImpl()->GetActivePane() }) + { + if (pane->IsConnectionClosed()) + { + makeItem(RS_(L"RestartConnectionText"), L"\xE72C", ActionAndArgs{ ShortcutAction::RestartConnection, nullptr }); + } + } + if (withSelection) { makeItem(RS_(L"SearchWebText"), L"\xF6FA", ActionAndArgs{ ShortcutAction::SearchForText, nullptr }); diff --git a/src/cascadia/TerminalApp/TerminalTab.cpp b/src/cascadia/TerminalApp/TerminalTab.cpp index 864ff6aad4e..e8b7a595af3 100644 --- a/src/cascadia/TerminalApp/TerminalTab.cpp +++ b/src/cascadia/TerminalApp/TerminalTab.cpp @@ -15,6 +15,7 @@ using namespace winrt; using namespace winrt::Windows::UI::Xaml; using namespace winrt::Windows::UI::Core; using namespace winrt::Microsoft::Terminal::Control; +using namespace winrt::Microsoft::Terminal::TerminalConnection; using namespace winrt::Microsoft::Terminal::Settings::Model; using namespace winrt::Microsoft::UI::Xaml::Controls; using namespace winrt::Windows::System; @@ -35,6 +36,7 @@ namespace winrt::TerminalApp::implementation _activePane = nullptr; _closePaneMenuItem.Visibility(WUX::Visibility::Collapsed); + _restartConnectionMenuItem.Visibility(WUX::Visibility::Collapsed); auto firstId = _nextPaneId; @@ -890,6 +892,7 @@ namespace winrt::TerminalApp::implementation control.TitleChanged(events.titleToken); control.TabColorChanged(events.colorToken); control.SetTaskbarProgress(events.taskbarToken); + control.ConnectionStateChanged(events.stateToken); control.ReadOnlyChanged(events.readOnlyToken); control.FocusFollowMouseRequested(events.focusToken); @@ -949,6 +952,14 @@ namespace winrt::TerminalApp::implementation } }); + events.stateToken = control.ConnectionStateChanged([dispatcher, weakThis](auto&&, auto&&) -> winrt::fire_and_forget { + co_await wil::resume_foreground(dispatcher); + if (auto tab{ weakThis.get() }) + { + tab->_UpdateConnectionClosedState(); + } + }); + events.readOnlyToken = control.ReadOnlyChanged([dispatcher, weakThis](auto&&, auto&&) -> winrt::fire_and_forget { co_await wil::resume_foreground(dispatcher); if (auto tab{ weakThis.get() }) @@ -1054,6 +1065,40 @@ namespace winrt::TerminalApp::implementation _TaskbarProgressChangedHandlers(nullptr, nullptr); } + // Method Description: + // - Set an indicator on the tab if any pane is in a closed connection state. + // - Show/hide the Restart Connection context menu entry depending on active pane's state. + // Arguments: + // - + // Return Value: + // - + void TerminalTab::_UpdateConnectionClosedState() + { + ASSERT_UI_THREAD(); + + if (_rootPane) + { + const bool isClosed = _rootPane->WalkTree([&](const auto& p) { + return p->IsConnectionClosed(); + }); + + _tabStatus.IsConnectionClosed(isClosed); + } + + if (_activePane) + { + _restartConnectionMenuItem.Visibility(_activePane->IsConnectionClosed() ? + WUX::Visibility::Visible : + WUX::Visibility::Collapsed); + } + } + + void TerminalTab::_RestartActivePaneConnection() + { + ActionAndArgs restartConnection{ ShortcutAction::RestartConnection, nullptr }; + _dispatch.DoAction(*this, restartConnection); + } + // Method Description: // - Mark the given pane as the active pane in this tab. All other panes // will be marked as inactive. We'll also update our own UI state to @@ -1072,6 +1117,7 @@ namespace winrt::TerminalApp::implementation // Update our own title text to match the newly-active pane. UpdateTitle(); _UpdateProgressState(); + _UpdateConnectionClosedState(); // We need to move the pane to the top of our mru list // If its already somewhere in the list, remove it first @@ -1393,6 +1439,28 @@ namespace winrt::TerminalApp::implementation Automation::AutomationProperties::SetHelpText(findMenuItem, findToolTip); } + Controls::MenuFlyoutItem restartConnectionMenuItem = _restartConnectionMenuItem; + { + // "Restart Connection" + Controls::FontIcon restartConnectionSymbol; + restartConnectionSymbol.FontFamily(Media::FontFamily{ L"Segoe Fluent Icons, Segoe MDL2 Assets" }); + restartConnectionSymbol.Glyph(L"\xE72C"); + + restartConnectionMenuItem.Click([weakThis](auto&&, auto&&) { + if (auto tab{ weakThis.get() }) + { + tab->_RestartActivePaneConnection(); + } + }); + restartConnectionMenuItem.Text(RS_(L"RestartConnectionText")); + restartConnectionMenuItem.Icon(restartConnectionSymbol); + + const auto restartConnectionToolTip = RS_(L"RestartConnectionToolTip"); + + WUX::Controls::ToolTipService::SetToolTip(restartConnectionMenuItem, box_value(restartConnectionToolTip)); + Automation::AutomationProperties::SetHelpText(restartConnectionMenuItem, restartConnectionToolTip); + } + // Build the menu Controls::MenuFlyout contextMenuFlyout; Controls::MenuFlyoutSeparator menuSeparator; @@ -1403,6 +1471,7 @@ namespace winrt::TerminalApp::implementation contextMenuFlyout.Items().Append(moveTabToNewWindowMenuItem); contextMenuFlyout.Items().Append(exportTabMenuItem); contextMenuFlyout.Items().Append(findMenuItem); + contextMenuFlyout.Items().Append(restartConnectionMenuItem); contextMenuFlyout.Items().Append(menuSeparator); // GH#5750 - When the context menu is dismissed with ESC, toss the focus diff --git a/src/cascadia/TerminalApp/TerminalTab.h b/src/cascadia/TerminalApp/TerminalTab.h index 72ec8d8bac2..12c4f79a643 100644 --- a/src/cascadia/TerminalApp/TerminalTab.h +++ b/src/cascadia/TerminalApp/TerminalTab.h @@ -109,6 +109,7 @@ namespace winrt::TerminalApp::implementation std::shared_ptr _zoomedPane{ nullptr }; Windows::UI::Xaml::Controls::MenuFlyoutItem _closePaneMenuItem; + Windows::UI::Xaml::Controls::MenuFlyoutItem _restartConnectionMenuItem; winrt::hstring _lastIconPath{}; std::optional _runtimeTabColor{}; @@ -125,6 +126,7 @@ namespace winrt::TerminalApp::implementation winrt::event_token titleToken; winrt::event_token colorToken; winrt::event_token taskbarToken; + winrt::event_token stateToken; winrt::event_token readOnlyToken; winrt::event_token focusToken; @@ -171,6 +173,9 @@ namespace winrt::TerminalApp::implementation void _UpdateProgressState(); + void _UpdateConnectionClosedState(); + void _RestartActivePaneConnection(); + void _DuplicateTab(); virtual winrt::Windows::UI::Xaml::Media::Brush _BackgroundBrush() override; diff --git a/src/cascadia/TerminalApp/TerminalTabStatus.h b/src/cascadia/TerminalApp/TerminalTabStatus.h index 95080935a38..838a909df22 100644 --- a/src/cascadia/TerminalApp/TerminalTabStatus.h +++ b/src/cascadia/TerminalApp/TerminalTabStatus.h @@ -12,6 +12,7 @@ namespace winrt::TerminalApp::implementation TerminalTabStatus() = default; WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); + WINRT_OBSERVABLE_PROPERTY(bool, IsConnectionClosed, _PropertyChangedHandlers); WINRT_OBSERVABLE_PROPERTY(bool, IsPaneZoomed, _PropertyChangedHandlers); WINRT_OBSERVABLE_PROPERTY(bool, IsProgressRingActive, _PropertyChangedHandlers); WINRT_OBSERVABLE_PROPERTY(bool, IsProgressRingIndeterminate, _PropertyChangedHandlers); diff --git a/src/cascadia/TerminalApp/TerminalTabStatus.idl b/src/cascadia/TerminalApp/TerminalTabStatus.idl index 57f4e05fa1a..5dc69ffe59b 100644 --- a/src/cascadia/TerminalApp/TerminalTabStatus.idl +++ b/src/cascadia/TerminalApp/TerminalTabStatus.idl @@ -7,6 +7,7 @@ namespace TerminalApp { TerminalTabStatus(); + Boolean IsConnectionClosed { get; set; }; Boolean IsPaneZoomed { get; set; }; Boolean IsProgressRingActive { get; set; }; Boolean IsProgressRingIndeterminate { get; set; };