From 27f775ee9e4a1484895bc2aae916dc6a9d6a8b11 Mon Sep 17 00:00:00 2001 From: Josh Johnson <1590066+GeekJosh@users.noreply.github.com> Date: Thu, 30 Jan 2025 21:53:43 +0000 Subject: [PATCH] Add the ability to show the tab bar in fullscreen (#18171) This PR allows users to enable the tab bar in fullscreen mode. A new setting; "showTabsFullscreen"; has been added which accepts a boolean value. When `true`, then the tab bar will remain visible when the terminal app is fullscreen. If the value is `false` (default), then the tab bar is hidden in fullscreen. When the tab bar is visible in fullscreen, the min/max/close controls are hidden to maintain the expected behaviour of a fullscreen app. ## Validation Steps Performed All unit tests are passing. Manually verified that when the "launchMode" setting is "fullscreen" and the "showTabsFullscreen" setting is `true`, the tab bar is visible on launch. Manually verified that changing the setting at runtime causes the tab bar to be shown/hidden immediately (if the terminal is currently fullscreen). Manually verified that the new "showTabsFullscreen" setting is honoured regardless of whether "showTabsInTitlebar" is set to `true` or `false`. Closes #11130 --- doc/cascadia/profiles.schema.json | 5 +++ src/cascadia/TerminalApp/TabManagement.cpp | 10 +++-- src/cascadia/TerminalApp/TerminalPage.cpp | 34 ++++++++++++++++ src/cascadia/TerminalApp/TerminalPage.h | 3 ++ src/cascadia/TerminalApp/TerminalWindow.cpp | 10 +++++ src/cascadia/TerminalApp/TerminalWindow.h | 2 + src/cascadia/TerminalApp/TerminalWindow.idl | 2 + src/cascadia/TerminalApp/TitlebarControl.cpp | 7 ++++ src/cascadia/TerminalApp/TitlebarControl.h | 1 + src/cascadia/TerminalApp/TitlebarControl.idl | 1 + .../GlobalAppearance.xaml | 6 +++ .../GlobalAppearanceViewModel.h | 1 + .../GlobalAppearanceViewModel.idl | 1 + .../Resources/en-US/Resources.resw | 8 ++++ .../GlobalAppSettings.idl | 1 + .../TerminalSettingsModel/MTSMSettings.h | 3 +- src/cascadia/WindowsTerminal/AppHost.cpp | 2 + src/cascadia/WindowsTerminal/IslandWindow.cpp | 5 +++ src/cascadia/WindowsTerminal/IslandWindow.h | 3 ++ .../WindowsTerminal/NonClientIslandWindow.cpp | 39 +++++++++++++++---- .../WindowsTerminal/NonClientIslandWindow.h | 2 + 21 files changed, 133 insertions(+), 13 deletions(-) diff --git a/doc/cascadia/profiles.schema.json b/doc/cascadia/profiles.schema.json index 7554302c408..315d103c537 100644 --- a/doc/cascadia/profiles.schema.json +++ b/doc/cascadia/profiles.schema.json @@ -2532,6 +2532,11 @@ "description": "When set to true, the Terminal's tab row will display a shield icon when the Terminal is running with administrator privileges", "type": "boolean" }, + "showTabsFullscreen": { + "default": false, + "description": "When set to true, tabs remain visible in fullscreen mode. When set to false, tabs will be hidden when entering fullscreen mode.", + "type": "boolean" + }, "useAcrylicInTabRow": { "default": false, "description": "When set to true, the tab row will have an acrylic material background with 50% opacity.", diff --git a/src/cascadia/TerminalApp/TabManagement.cpp b/src/cascadia/TerminalApp/TabManagement.cpp index 0eba4511a92..cf0df031750 100644 --- a/src/cascadia/TerminalApp/TabManagement.cpp +++ b/src/cascadia/TerminalApp/TabManagement.cpp @@ -243,10 +243,12 @@ namespace winrt::TerminalApp::implementation // - Handle changes in tab layout. void TerminalPage::_UpdateTabView() { - // Never show the tab row when we're fullscreen. Otherwise: - // Show tabs when there's more than 1, or the user has chosen to always - // show the tab bar. - const auto isVisible = (!_isFullscreen && !_isInFocusMode) && + // The tab row should only be visible if: + // - we're not in focus mode + // - we're not in full screen, or the user has enabled fullscreen tabs + // - there is more than one tab, or the user has chosen to always show tabs + const auto isVisible = !_isInFocusMode && + (!_isFullscreen || _showTabsFullscreen) && (_settings.GlobalSettings().ShowTabsInTitlebar() || (_tabs.Size() > 1) || _settings.GlobalSettings().AlwaysShowTabs()); diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index e73ba93193b..c71596b7080 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -269,6 +269,7 @@ namespace winrt::TerminalApp::implementation _layoutUpdatedRevoker = _tabContent.LayoutUpdated(winrt::auto_revoke, { this, &TerminalPage::_OnFirstLayout }); _isAlwaysOnTop = _settings.GlobalSettings().AlwaysOnTop(); + _showTabsFullscreen = _settings.GlobalSettings().ShowTabsFullscreen(); // DON'T set up Toasts/TeachingTips here. They should be loaded and // initialized the first time they're opened, in whatever method opens @@ -3599,6 +3600,8 @@ namespace winrt::TerminalApp::implementation _isAlwaysOnTop = _settings.GlobalSettings().AlwaysOnTop(); AlwaysOnTopChanged.raise(*this, nullptr); + _showTabsFullscreen = _settings.GlobalSettings().ShowTabsFullscreen(); + // Settings AllowDependentAnimations will affect whether animations are // enabled application-wide, so we don't need to check it each time we // want to create an animation. @@ -4023,6 +4026,37 @@ namespace winrt::TerminalApp::implementation return _isAlwaysOnTop; } + // Method Description: + // - Returns true if the tab row should be visible when we're in full screen + // state. + // Arguments: + // - + // Return Value: + // - true if the tab row should be visible in full screen state + bool TerminalPage::ShowTabsFullscreen() const + { + return _showTabsFullscreen; + } + + // Method Description: + // - Updates the visibility of the tab row when in fullscreen state. + void TerminalPage::SetShowTabsFullscreen(bool newShowTabsFullscreen) + { + if (_showTabsFullscreen == newShowTabsFullscreen) + { + return; + } + + _showTabsFullscreen = newShowTabsFullscreen; + + // if we're currently in fullscreen, update tab view to make + // sure tabs are given the correct visibility + if (_isFullscreen) + { + _UpdateTabView(); + } + } + void TerminalPage::SetFullscreen(bool newFullscreen) { if (_isFullscreen == newFullscreen) diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 23258c25095..368cb77cd8d 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -121,6 +121,8 @@ namespace winrt::TerminalApp::implementation bool FocusMode() const; bool Fullscreen() const; bool AlwaysOnTop() const; + bool ShowTabsFullscreen() const; + void SetShowTabsFullscreen(bool newShowTabsFullscreen); void SetFullscreen(bool); void SetFocusMode(const bool inFocusMode); void Maximized(bool newMaximized); @@ -228,6 +230,7 @@ namespace winrt::TerminalApp::implementation bool _isFullscreen{ false }; bool _isMaximized{ false }; bool _isAlwaysOnTop{ false }; + bool _showTabsFullscreen{ false }; std::optional _loadFromPersistedLayoutIdx{}; diff --git a/src/cascadia/TerminalApp/TerminalWindow.cpp b/src/cascadia/TerminalApp/TerminalWindow.cpp index 569b236374a..055d6123ccf 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.cpp +++ b/src/cascadia/TerminalApp/TerminalWindow.cpp @@ -289,6 +289,11 @@ namespace winrt::TerminalApp::implementation return _settings.GlobalSettings().AlwaysOnTop(); } + bool TerminalWindow::GetInitialShowTabsFullscreen() + { + return _settings.GlobalSettings().ShowTabsFullscreen(); + } + bool TerminalWindow::GetMinimizeToNotificationArea() { return _settings.GlobalSettings().MinimizeToNotificationArea(); @@ -981,6 +986,11 @@ namespace winrt::TerminalApp::implementation return _root ? _root->AlwaysOnTop() : false; } + bool TerminalWindow::ShowTabsFullscreen() const + { + return _root ? _root->ShowTabsFullscreen() : false; + } + void TerminalWindow::SetSettingsStartupArgs(const std::vector& actions) { for (const auto& action : actions) diff --git a/src/cascadia/TerminalApp/TerminalWindow.h b/src/cascadia/TerminalApp/TerminalWindow.h index 7467b64241d..eef9efaf210 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.h +++ b/src/cascadia/TerminalApp/TerminalWindow.h @@ -89,6 +89,7 @@ namespace winrt::TerminalApp::implementation bool Fullscreen() const; void Maximized(bool newMaximized); bool AlwaysOnTop() const; + bool ShowTabsFullscreen() const; bool AutoHideWindow(); void IdentifyWindow(); @@ -106,6 +107,7 @@ namespace winrt::TerminalApp::implementation Microsoft::Terminal::Settings::Model::LaunchMode GetLaunchMode(); bool GetShowTabsInTitlebar(); bool GetInitialAlwaysOnTop(); + bool GetInitialShowTabsFullscreen(); float CalcSnappedDimension(const bool widthOrHeight, const float dimension) const; Windows::UI::Xaml::UIElement GetRoot() noexcept; diff --git a/src/cascadia/TerminalApp/TerminalWindow.idl b/src/cascadia/TerminalApp/TerminalWindow.idl index f0bf2a98bd2..a8fa0c97c51 100644 --- a/src/cascadia/TerminalApp/TerminalWindow.idl +++ b/src/cascadia/TerminalApp/TerminalWindow.idl @@ -69,6 +69,7 @@ namespace TerminalApp void Maximized(Boolean newMaximized); Boolean AlwaysOnTop { get; }; Boolean AutoHideWindow { get; }; + Boolean ShowTabsFullscreen { get; }; void IdentifyWindow(); void SetPersistedLayoutIdx(UInt32 idx); @@ -82,6 +83,7 @@ namespace TerminalApp Microsoft.Terminal.Settings.Model.LaunchMode GetLaunchMode(); Boolean GetShowTabsInTitlebar(); Boolean GetInitialAlwaysOnTop(); + Boolean GetInitialShowTabsFullscreen(); Single CalcSnappedDimension(Boolean widthOrHeight, Single dimension); void TitlebarClicked(); void CloseWindow(); diff --git a/src/cascadia/TerminalApp/TitlebarControl.cpp b/src/cascadia/TerminalApp/TitlebarControl.cpp index 1debd030544..1ed82f39ca6 100644 --- a/src/cascadia/TerminalApp/TitlebarControl.cpp +++ b/src/cascadia/TerminalApp/TitlebarControl.cpp @@ -12,6 +12,8 @@ #include "TitlebarControl.g.cpp" +using namespace winrt::Windows::UI::Xaml; + namespace winrt::TerminalApp::implementation { TitlebarControl::TitlebarControl(uint64_t handle) : @@ -77,6 +79,11 @@ namespace winrt::TerminalApp::implementation } } + void TitlebarControl::FullscreenChanged(const bool fullscreen) + { + MinMaxCloseControl().Visibility(fullscreen ? Visibility::Collapsed : Visibility::Visible); + } + void TitlebarControl::_OnMaximizeOrRestore(byte flag) { POINT point1 = {}; diff --git a/src/cascadia/TerminalApp/TitlebarControl.h b/src/cascadia/TerminalApp/TitlebarControl.h index 8020ff90956..1008253ba8d 100644 --- a/src/cascadia/TerminalApp/TitlebarControl.h +++ b/src/cascadia/TerminalApp/TitlebarControl.h @@ -22,6 +22,7 @@ namespace winrt::TerminalApp::implementation void SetWindowVisualState(WindowVisualState visualState); void Root_SizeChanged(const IInspectable& sender, const Windows::UI::Xaml::SizeChangedEventArgs& e); + void FullscreenChanged(const bool fullscreen); void Minimize_Click(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::RoutedEventArgs& e); void Maximize_Click(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::RoutedEventArgs& e); diff --git a/src/cascadia/TerminalApp/TitlebarControl.idl b/src/cascadia/TerminalApp/TitlebarControl.idl index b7a06ba987e..8f934fae951 100644 --- a/src/cascadia/TerminalApp/TitlebarControl.idl +++ b/src/cascadia/TerminalApp/TitlebarControl.idl @@ -22,6 +22,7 @@ namespace TerminalApp { TitlebarControl(UInt64 parentWindowHandle); void SetWindowVisualState(WindowVisualState visualState); + void FullscreenChanged(Boolean fullscreen); void HoverButton(CaptionButton button); void PressButton(CaptionButton button); diff --git a/src/cascadia/TerminalSettingsEditor/GlobalAppearance.xaml b/src/cascadia/TerminalSettingsEditor/GlobalAppearance.xaml index f227268613d..854fd733766 100644 --- a/src/cascadia/TerminalSettingsEditor/GlobalAppearance.xaml +++ b/src/cascadia/TerminalSettingsEditor/GlobalAppearance.xaml @@ -64,6 +64,12 @@ Style="{StaticResource ToggleSwitchInExpanderStyle}" /> + + + + + Display a shield in the title bar when Windows Terminal is running as Administrator Header for a control to toggle displaying a shield in the title bar of the app. "Admin" refers to elevated sessions like "run as Admin" + + Show tabs in full screen + Header for a control to toggle if the app should show the tabs when in full screen state. + + + 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. + 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 b04073273ba..cee08a2656d 100644 --- a/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl +++ b/src/cascadia/TerminalSettingsModel/GlobalAppSettings.idl @@ -57,6 +57,7 @@ namespace Microsoft.Terminal.Settings.Model INHERITABLE_SETTING(Int32, InitialRows); INHERITABLE_SETTING(Int32, InitialCols); INHERITABLE_SETTING(Boolean, AlwaysShowTabs); + INHERITABLE_SETTING(Boolean, ShowTabsFullscreen); INHERITABLE_SETTING(NewTabPosition, NewTabPosition); INHERITABLE_SETTING(Boolean, ShowTitleInTitlebar); INHERITABLE_SETTING(Boolean, ConfirmCloseAllTabs); diff --git a/src/cascadia/TerminalSettingsModel/MTSMSettings.h b/src/cascadia/TerminalSettingsModel/MTSMSettings.h index 2faa985c5ea..070792d149b 100644 --- a/src/cascadia/TerminalSettingsModel/MTSMSettings.h +++ b/src/cascadia/TerminalSettingsModel/MTSMSettings.h @@ -68,7 +68,8 @@ Author(s): X(bool, EnableUnfocusedAcrylic, "compatibility.enableUnfocusedAcrylic", true) \ 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(hstring, SearchWebDefaultQueryUrl, "searchWebDefaultQueryUrl", L"https://www.bing.com/search?q=%22%s%22") \ + X(bool, ShowTabsFullscreen, "showTabsFullscreen", false) // Also add these settings to: // * Profile.idl diff --git a/src/cascadia/WindowsTerminal/AppHost.cpp b/src/cascadia/WindowsTerminal/AppHost.cpp index 0fd623aad61..6cdc7ff8e72 100644 --- a/src/cascadia/WindowsTerminal/AppHost.cpp +++ b/src/cascadia/WindowsTerminal/AppHost.cpp @@ -193,6 +193,7 @@ void AppHost::Initialize() _window->SetAlwaysOnTop(_windowLogic.GetInitialAlwaysOnTop()); _window->SetAutoHideWindow(_windowLogic.AutoHideWindow()); + _window->SetShowTabsFullscreen(_windowLogic.GetInitialShowTabsFullscreen()); // MORE EVENT HANDLERS HERE! // MAKE SURE THEY ARE ALL: @@ -1020,6 +1021,7 @@ void AppHost::_HandleSettingsChanged(const winrt::Windows::Foundation::IInspecta _window->SetMinimizeToNotificationAreaBehavior(_windowLogic.GetMinimizeToNotificationArea()); _window->SetAutoHideWindow(_windowLogic.AutoHideWindow()); + _window->SetShowTabsFullscreen(_windowLogic.ShowTabsFullscreen()); _updateTheme(); } diff --git a/src/cascadia/WindowsTerminal/IslandWindow.cpp b/src/cascadia/WindowsTerminal/IslandWindow.cpp index 4b965be4022..1e2ced901e5 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/IslandWindow.cpp @@ -895,6 +895,11 @@ void IslandWindow::ShowWindowChanged(const bool showOrHide) } } +void IslandWindow::SetShowTabsFullscreen(const bool newShowTabsFullscreen) +{ + _showTabsFullscreen = newShowTabsFullscreen; +} + // Method Description // - Flash the taskbar icon, indicating to the user that something needs their attention void IslandWindow::FlashTaskbar() diff --git a/src/cascadia/WindowsTerminal/IslandWindow.h b/src/cascadia/WindowsTerminal/IslandWindow.h index 47d4e38ce98..97bdd0841d3 100644 --- a/src/cascadia/WindowsTerminal/IslandWindow.h +++ b/src/cascadia/WindowsTerminal/IslandWindow.h @@ -50,6 +50,7 @@ class IslandWindow : void FullscreenChanged(const bool fullscreen); void SetAlwaysOnTop(const bool alwaysOnTop); void ShowWindowChanged(const bool showOrHide); + virtual void SetShowTabsFullscreen(const bool newShowTabsFullscreen); void FlashTaskbar(); void SetTaskbarProgress(const size_t state, const size_t progress); @@ -109,6 +110,7 @@ class IslandWindow : bool _borderless{ false }; bool _alwaysOnTop{ false }; bool _fullscreen{ false }; + bool _showTabsFullscreen{ false }; bool _fWasMaximizedBeforeFullscreen{ false }; RECT _rcWindowBeforeFullscreen{}; RECT _rcWorkBeforeFullscreen{}; @@ -116,6 +118,7 @@ class IslandWindow : virtual void _SetIsBorderless(const bool borderlessEnabled); virtual void _SetIsFullscreen(const bool fullscreenEnabled); + void _RestoreFullscreenPosition(const RECT& rcWork); void _SetFullscreenPosition(const RECT& rcMonitor, const RECT& rcWork); diff --git a/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp b/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp index 3c66105885c..414e4043f88 100644 --- a/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp +++ b/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp @@ -1129,7 +1129,7 @@ void NonClientIslandWindow::_SetIsBorderless(const bool borderlessEnabled) // Method Description: // - Enable or disable fullscreen mode. When entering fullscreen mode, we'll -// need to manually hide the entire titlebar. +// need to check whether to hide the titlebar. // - See also IslandWindow::_SetIsFullscreen, which does additional work. // Arguments: // - fullscreenEnabled: If true, we're entering fullscreen mode. If false, we're leaving. @@ -1138,10 +1138,7 @@ void NonClientIslandWindow::_SetIsBorderless(const bool borderlessEnabled) void NonClientIslandWindow::_SetIsFullscreen(const bool fullscreenEnabled) { IslandWindow::_SetIsFullscreen(fullscreenEnabled); - if (_titlebar) - { - _titlebar.Visibility(_IsTitlebarVisible() ? Visibility::Visible : Visibility::Collapsed); - } + _UpdateTitlebarVisibility(); // GH#4224 - When the auto-hide taskbar setting is enabled, then we don't // always get another window message to trigger us to remove the drag bar. // So, make sure to update the size of the drag region here, so that it @@ -1149,16 +1146,42 @@ void NonClientIslandWindow::_SetIsFullscreen(const bool fullscreenEnabled) _ResizeDragBarWindow(); } +void NonClientIslandWindow::SetShowTabsFullscreen(const bool newShowTabsFullscreen) +{ + IslandWindow::SetShowTabsFullscreen(newShowTabsFullscreen); + + // don't waste time recalculating UI elements if we're not + // in fullscreen state - this setting doesn't affect other + // window states + if (_fullscreen) + { + _UpdateTitlebarVisibility(); + } +} + +void NonClientIslandWindow::_UpdateTitlebarVisibility() +{ + if (!_titlebar) + { + return; + } + + const auto showTitlebar = _IsTitlebarVisible(); + _titlebar.Visibility(showTitlebar ? Visibility::Visible : Visibility::Collapsed); + _titlebar.FullscreenChanged(_fullscreen); +} + // Method Description: -// - Returns true if the titlebar is visible. For things like fullscreen mode, -// borderless mode (aka "focus mode"), this will return false. +// - Returns true if the titlebar is visible. For borderless mode (aka "focus mode"), +// this will return false. For fullscreen, this will return false unless the user +// has enabled fullscreen tabs. // Arguments: // - // Return Value: // - true iff the titlebar is visible bool NonClientIslandWindow::_IsTitlebarVisible() const { - return !(_fullscreen || _borderless); + return !_borderless && (!_fullscreen || _showTabsFullscreen); } void NonClientIslandWindow::SetTitlebarBackground(winrt::Windows::UI::Xaml::Media::Brush brush) diff --git a/src/cascadia/WindowsTerminal/NonClientIslandWindow.h b/src/cascadia/WindowsTerminal/NonClientIslandWindow.h index 76e093a2340..bdf64f98409 100644 --- a/src/cascadia/WindowsTerminal/NonClientIslandWindow.h +++ b/src/cascadia/WindowsTerminal/NonClientIslandWindow.h @@ -46,6 +46,7 @@ class NonClientIslandWindow : public IslandWindow void OnApplicationThemeChanged(const winrt::Windows::UI::Xaml::ElementTheme& requestedTheme) override; void SetTitlebarBackground(winrt::Windows::UI::Xaml::Media::Brush brush); + void SetShowTabsFullscreen(const bool newShowTabsFullscreen) override; virtual void UseMica(const bool newValue, const double titlebarOpacity) override; @@ -92,6 +93,7 @@ class NonClientIslandWindow : public IslandWindow void _UpdateFrameMargins() const noexcept; void _UpdateMaximizedState(); void _UpdateIslandPosition(const UINT windowWidth, const UINT windowHeight); + void _UpdateTitlebarVisibility(); struct Revokers {