diff --git a/src/cascadia/TerminalApp/Pane.cpp b/src/cascadia/TerminalApp/Pane.cpp index 43849b21d2..6f3d1759b6 100644 --- a/src/cascadia/TerminalApp/Pane.cpp +++ b/src/cascadia/TerminalApp/Pane.cpp @@ -31,10 +31,14 @@ Pane::Pane(IPaneContent content, const bool lastFocused) : _lastActive{ lastFocused } { _setPaneContent(std::move(content)); - _root.Children().Append(_borderFirst); + _CreatePaneHeader(); const auto& control{ _content.GetRoot() }; - _borderFirst.Child(control); + + // Set up leaf layout: header in _root row 0, content in _borderFirst row 1. + // The TermControl stays as the direct child of _borderFirst (no Grid wrapper) + // so the SwapChainPanel renders correctly. + _SetupLeafLayout(control); // Register an event with the control to have it inform us when it gains focus. if (control) @@ -1232,6 +1236,12 @@ void Pane::UpdateVisuals() const auto& brush{ _ComputeBorderColor() }; _borderFirst.BorderBrush(brush); _borderSecond.BorderBrush(brush); + + // Update pane header color to match focus state + if (_paneHeaderBorder && _paneHeaderBorder.Visibility() == winrt::Windows::UI::Xaml::Visibility::Visible) + { + _paneHeaderBorder.Background(brush); + } } // Method Description: @@ -1454,9 +1464,9 @@ void Pane::_CloseChild(const bool closeFirst) _root.RowDefinitions().Clear(); // Reattach the TermControl to our grid. - _root.Children().Append(_borderFirst); + _CreatePaneHeader(); const auto& control{ _content.GetRoot() }; - _borderFirst.Child(control); + _SetupLeafLayout(control); // Make sure to set our _splitState before focusing the control. If you // fail to do this, when the tab handles the GotFocus event and asks us @@ -1759,7 +1769,92 @@ void Pane::_setPaneContent(IPaneContent content) } // Method Description: -// - Sets up row/column definitions for this pane. There are three total +// - Creates the pane header UI elements (title bar shown above the content). +// The header is initially collapsed and only shown via ShowPaneHeaders(). +void Pane::_CreatePaneHeader() +{ + namespace WUX = winrt::Windows::UI::Xaml; + + _paneHeaderText = Controls::TextBlock{}; + _paneHeaderText.FontSize(12); + _paneHeaderText.Padding({ 8, 2, 8, 2 }); + _paneHeaderText.IsTextSelectionEnabled(false); + _paneHeaderText.TextTrimming(WUX::TextTrimming::CharacterEllipsis); + if (_content) + { + _paneHeaderText.Text(_content.Title()); + _titleChangedRevoker = _content.TitleChanged(winrt::auto_revoke, [this](auto&&, auto&&) { + _paneHeaderBorder.Dispatcher().RunAsync( + winrt::Windows::UI::Core::CoreDispatcherPriority::Normal, + [this]() { + if (_content && _paneHeaderText) + { + _paneHeaderText.Text(_content.Title()); + } + }); + }); + } + + _paneHeaderBorder = Controls::Border{}; + _paneHeaderBorder.Padding({ 0, 0, 0, 0 }); + _paneHeaderBorder.Child(_paneHeaderText); + _paneHeaderBorder.Visibility(WUX::Visibility::Collapsed); +} + +// Method Description: +// - Sets up the leaf pane layout in _root: a header row (auto-sized) and a +// content row (star-sized). The TermControl stays as the direct child of +// _borderFirst so the SwapChainPanel renders correctly. +void Pane::_SetupLeafLayout(const winrt::Windows::UI::Xaml::UIElement& control) +{ + auto headerRow = Controls::RowDefinition{}; + headerRow.Height(GridLengthHelper::Auto()); + auto contentRow = Controls::RowDefinition{}; + contentRow.Height(GridLengthHelper::FromValueAndType(1, GridUnitType::Star)); + _root.RowDefinitions().Append(headerRow); + _root.RowDefinitions().Append(contentRow); + + Controls::Grid::SetRow(_paneHeaderBorder, 0); + Controls::Grid::SetRow(_borderFirst, 1); + + _root.Children().Append(_paneHeaderBorder); + _root.Children().Append(_borderFirst); + + if (control) + { + _borderFirst.Child(control); + } +} + +// Method Description: +// - Show or hide the pane header title bar on all leaf panes in the tree. +// Called by Tab when the number of panes changes. +void Pane::ShowPaneHeaders(bool show) +{ + if (_IsLeaf()) + { + if (_paneHeaderBorder) + { + namespace WUX = winrt::Windows::UI::Xaml; + _paneHeaderBorder.Visibility(show ? WUX::Visibility::Visible : WUX::Visibility::Collapsed); + + if (show) + { + const auto& brush = _ComputeBorderColor(); + _paneHeaderBorder.Background(brush); + _paneHeaderText.Foreground(winrt::Windows::UI::Xaml::Media::SolidColorBrush(winrt::Windows::UI::Colors::White())); + } + } + } + else + { + _firstChild->ShowPaneHeaders(show); + _secondChild->ShowPaneHeaders(show); + } +} + +// Method Description: +// - Sets up row/column definitions for this pane.There are three total // row/cols. The middle one is for the separator. The first and third are for // each of the child panes, and are given a size in pixels, based off the // available space, and the percent of the space they respectively consume, @@ -2325,6 +2420,10 @@ std::pair, std::shared_ptr> Pane::_Split(SplitDirect _root.RowDefinitions().Clear(); _CreateRowColDefinitions(); + // Reset Grid.Row on _borderFirst — it may have been set to row 1 in the + // leaf layout (header=row0, content=row1). + Controls::Grid::SetRow(_borderFirst, 0); + _borderFirst.Child(_firstChild->GetRootElement()); _borderSecond.Child(_secondChild->GetRootElement()); diff --git a/src/cascadia/TerminalApp/Pane.h b/src/cascadia/TerminalApp/Pane.h index ecc81fad82..ae61334e42 100644 --- a/src/cascadia/TerminalApp/Pane.h +++ b/src/cascadia/TerminalApp/Pane.h @@ -150,6 +150,7 @@ public: bool ContainsReadOnly() const; void EnableBroadcast(bool enabled); + void ShowPaneHeaders(bool show); 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); @@ -235,6 +236,11 @@ private: winrt::Windows::UI::Xaml::Controls::Border _borderFirst{}; winrt::Windows::UI::Xaml::Controls::Border _borderSecond{}; + // Per-pane title header (visible when there are split panes) + winrt::Windows::UI::Xaml::Controls::Border _paneHeaderBorder{ nullptr }; + winrt::Windows::UI::Xaml::Controls::TextBlock _paneHeaderText{ nullptr }; + winrt::TerminalApp::IPaneContent::TitleChanged_revoker _titleChangedRevoker; + PaneResources _themeResources; #pragma region Properties that need to be transferred between child / parent panes upon splitting / closing @@ -266,6 +272,8 @@ private: void _SetupChildCloseHandlers(); winrt::TerminalApp::IPaneContent _takePaneContent(); void _setPaneContent(winrt::TerminalApp::IPaneContent content); + void _CreatePaneHeader(); + void _SetupLeafLayout(const winrt::Windows::UI::Xaml::UIElement& control); bool _HasChild(const std::shared_ptr child); winrt::TerminalApp::TerminalPaneContent _getTerminalContent() const; diff --git a/src/cascadia/TerminalApp/Tab.cpp b/src/cascadia/TerminalApp/Tab.cpp index da4d0896ae..bbef32c786 100644 --- a/src/cascadia/TerminalApp/Tab.cpp +++ b/src/cascadia/TerminalApp/Tab.cpp @@ -361,6 +361,10 @@ namespace winrt::TerminalApp::implementation // The tabWidthMode may have changed, update the header control accordingly _UpdateHeaderControlMaxWidth(); + // Refresh pane header visibility based on the current setting + const auto showHeaders = settings.GlobalSettings().ShowPaneHeaders() && _rootPane->GetLeafPaneCount() > 1; + _rootPane->ShowPaneHeaders(showHeaders); + // Update the settings on all our panes. _rootPane->WalkTree([&](const auto& pane) { pane->UpdateSettings(settings); @@ -646,6 +650,14 @@ namespace winrt::TerminalApp::implementation // After split, Close Pane Menu Item should be visible _closePaneMenuItem.Visibility(WUX::Visibility::Visible); + // Show pane headers now that we have multiple panes (if the setting is enabled) + try + { + const auto settings{ winrt::TerminalApp::implementation::AppLogic::CurrentAppSettings() }; + _rootPane->ShowPaneHeaders(settings.GlobalSettings().ShowPaneHeaders()); + } + CATCH_LOG(); + // The active pane has an id if it is a leaf if (activePaneId) { @@ -1337,6 +1349,7 @@ namespace winrt::TerminalApp::implementation if (_rootPane->GetLeafPaneCount() == 1) { _closePaneMenuItem.Visibility(WUX::Visibility::Collapsed); + _rootPane->ShowPaneHeaders(false); } _RecalculateAndApplyReadOnly(); diff --git a/src/cascadia/TerminalSettingsEditor/GlobalAppearance.xaml b/src/cascadia/TerminalSettingsEditor/GlobalAppearance.xaml index 690c1961b0..eba747dca6 100644 --- a/src/cascadia/TerminalSettingsEditor/GlobalAppearance.xaml +++ b/src/cascadia/TerminalSettingsEditor/GlobalAppearance.xaml @@ -68,6 +68,13 @@ + + + + + diff --git a/src/cascadia/TerminalSettingsEditor/GlobalAppearanceViewModel.h b/src/cascadia/TerminalSettingsEditor/GlobalAppearanceViewModel.h index 108636a747..dc758cae66 100644 --- a/src/cascadia/TerminalSettingsEditor/GlobalAppearanceViewModel.h +++ b/src/cascadia/TerminalSettingsEditor/GlobalAppearanceViewModel.h @@ -33,6 +33,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, AlwaysShowTabs); PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, ShowTabsFullscreen); + PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, ShowPaneHeaders); PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, ShowTabsInTitlebar); PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, UseAcrylicInTabRow); PERMANENT_OBSERVABLE_PROJECTED_SETTING(_GlobalSettings, ShowTitleInTitlebar); diff --git a/src/cascadia/TerminalSettingsEditor/GlobalAppearanceViewModel.idl b/src/cascadia/TerminalSettingsEditor/GlobalAppearanceViewModel.idl index fb75021608..4a10edc406 100644 --- a/src/cascadia/TerminalSettingsEditor/GlobalAppearanceViewModel.idl +++ b/src/cascadia/TerminalSettingsEditor/GlobalAppearanceViewModel.idl @@ -27,6 +27,7 @@ namespace Microsoft.Terminal.Settings.Editor PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, AlwaysShowTabs); PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, ShowTabsFullscreen); + PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, ShowPaneHeaders); PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, ShowTabsInTitlebar); PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, UseAcrylicInTabRow); PERMANENT_OBSERVABLE_PROJECTED_SETTING(Boolean, ShowTitleInTitlebar); diff --git a/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw b/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw index 48e1862f5a..ba111788c9 100644 --- a/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw +++ b/src/cascadia/TerminalSettingsEditor/Resources/en-US/Resources.resw @@ -2685,6 +2685,14 @@ When enabled, the tab bar will be visible when the app is full screen. A description for what the "show tabs in full screen" setting does. + + Show pane title headers + Header for a control to toggle if the app should show title headers above each pane when multiple panes are open. + + + When enabled, a title header is shown above each pane when multiple panes are open. + A description for what the "show pane headers" setting does. Presented near "Globals_ShowPaneHeaders.Header". + Path translation Name for a control to select how file and directory paths are translated. diff --git a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl index 33d92ed8b8..222370b9f0 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl @@ -66,6 +66,7 @@ namespace Microsoft.Terminal.Settings.Model INHERITABLE_SETTING(Int32, InitialCols); INHERITABLE_SETTING(Boolean, AlwaysShowTabs); INHERITABLE_SETTING(Boolean, ShowTabsFullscreen); + INHERITABLE_SETTING(Boolean, ShowPaneHeaders); INHERITABLE_SETTING(NewTabPosition, NewTabPosition); INHERITABLE_SETTING(Boolean, ShowTitleInTitlebar); INHERITABLE_SETTING(ConfirmOnClose, ConfirmOnClose); diff --git a/src/cascadia/TerminalSettingsModel/MTSMSettings.h b/src/cascadia/TerminalSettingsModel/MTSMSettings.h index 948fb46169..4d0da505b7 100644 --- a/src/cascadia/TerminalSettingsModel/MTSMSettings.h +++ b/src/cascadia/TerminalSettingsModel/MTSMSettings.h @@ -72,7 +72,8 @@ Author(s): X(winrt::Windows::Foundation::Collections::IVector, NewTabMenu, "newTabMenu", winrt::single_threaded_vector({ Model::RemainingProfilesEntry{} })) \ X(bool, AllowHeadless, "compatibility.allowHeadless", false) \ X(hstring, SearchWebDefaultQueryUrl, "searchWebDefaultQueryUrl", L"https://www.bing.com/search?q=%22%s%22") \ - X(bool, ShowTabsFullscreen, "showTabsFullscreen", false) + X(bool, ShowTabsFullscreen, "showTabsFullscreen", false) \ + X(bool, ShowPaneHeaders, "showPaneHeaders", true) // Also add these settings to: // * Profile.idl