diff --git a/.github/actions/spell-check/dictionary/apis.txt b/.github/actions/spell-check/dictionary/apis.txt index fddc4ddb7bf..eef0f5056fa 100644 --- a/.github/actions/spell-check/dictionary/apis.txt +++ b/.github/actions/spell-check/dictionary/apis.txt @@ -44,6 +44,7 @@ oaidl ocidl otms OUTLINETEXTMETRICW +overridable PAGESCROLL RETURNCMD rfind diff --git a/src/cascadia/LocalTests_TerminalApp/TabTests.cpp b/src/cascadia/LocalTests_TerminalApp/TabTests.cpp index d32a41aee94..deff987ba7f 100644 --- a/src/cascadia/LocalTests_TerminalApp/TabTests.cpp +++ b/src/cascadia/LocalTests_TerminalApp/TabTests.cpp @@ -7,7 +7,7 @@ #include "../TerminalApp/MinMaxCloseControl.h" #include "../TerminalApp/TabRowControl.h" #include "../TerminalApp/ShortcutActionDispatch.h" -#include "../TerminalApp/Tab.h" +#include "../TerminalApp/TerminalTab.h" #include "../CppWinrtTailored.h" using namespace Microsoft::Console; @@ -250,8 +250,8 @@ namespace TerminalAppLocalTests // In the real app, this isn't a problem, but doesn't happen // reliably in the unit tests. Log::Comment(L"Ensure we set the first tab as the selected one."); - auto tab{ page->_GetStrongTabImpl(0) }; - page->_tabView.SelectedItem(tab->GetTabViewItem()); + auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0)); + page->_tabView.SelectedItem(tab->TabViewItem()); page->_UpdatedSelectedTab(0); }); VERIFY_SUCCEEDED(result); @@ -453,7 +453,7 @@ namespace TerminalAppLocalTests result = RunOnUIThread([&page]() { VERIFY_ARE_EQUAL(1u, page->_tabs.Size()); - auto tab = page->_GetStrongTabImpl(0); + auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0)); VERIFY_ARE_EQUAL(1, tab->GetLeafPaneCount()); }); VERIFY_SUCCEEDED(result); @@ -463,7 +463,7 @@ namespace TerminalAppLocalTests page->_SplitPane(SplitState::Automatic, SplitType::Duplicate, nullptr); VERIFY_ARE_EQUAL(1u, page->_tabs.Size()); - auto tab = page->_GetStrongTabImpl(0); + auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0)); VERIFY_ARE_EQUAL(2, tab->GetLeafPaneCount()); }); VERIFY_SUCCEEDED(result); @@ -481,7 +481,7 @@ namespace TerminalAppLocalTests page->_SplitPane(SplitState::Automatic, SplitType::Duplicate, nullptr); VERIFY_ARE_EQUAL(1u, page->_tabs.Size()); - auto tab = page->_GetStrongTabImpl(0); + auto tab = page->_GetTerminalTabImpl(page->_tabs.GetAt(0)); VERIFY_ARE_EQUAL(2, tab->GetLeafPaneCount(), L"We should gracefully do nothing here - the profile no longer exists."); @@ -562,7 +562,7 @@ namespace TerminalAppLocalTests ActionEventArgs eventArgs{ args }; // eventArgs.Args(args); page->_HandleSplitPane(nullptr, eventArgs); - auto firstTab = page->_GetStrongTabImpl(0); + auto firstTab = page->_GetTerminalTabImpl(0); VERIFY_ARE_EQUAL(2, firstTab->GetLeafPaneCount()); VERIFY_IS_FALSE(firstTab->IsZoomed()); @@ -573,7 +573,7 @@ namespace TerminalAppLocalTests result = RunOnUIThread([&page]() { ActionEventArgs eventArgs{}; page->_HandleTogglePaneZoom(nullptr, eventArgs); - auto firstTab = page->_GetStrongTabImpl(0); + auto firstTab = page->_GetTerminalTabImpl(0); VERIFY_ARE_EQUAL(2, firstTab->GetLeafPaneCount()); VERIFY_IS_TRUE(firstTab->IsZoomed()); }); @@ -583,7 +583,7 @@ namespace TerminalAppLocalTests result = RunOnUIThread([&page]() { ActionEventArgs eventArgs{}; page->_HandleTogglePaneZoom(nullptr, eventArgs); - auto firstTab = page->_GetStrongTabImpl(0); + auto firstTab = page->_GetTerminalTabImpl(0); VERIFY_ARE_EQUAL(2, firstTab->GetLeafPaneCount()); VERIFY_IS_FALSE(firstTab->IsZoomed()); }); @@ -600,7 +600,7 @@ namespace TerminalAppLocalTests SplitPaneArgs args{ SplitType::Duplicate }; ActionEventArgs eventArgs{ args }; page->_HandleSplitPane(nullptr, eventArgs); - auto firstTab = page->_GetStrongTabImpl(0); + auto firstTab = page->_GetTerminalTabImpl(0); VERIFY_ARE_EQUAL(2, firstTab->GetLeafPaneCount()); VERIFY_IS_FALSE(firstTab->IsZoomed()); @@ -614,7 +614,7 @@ namespace TerminalAppLocalTests page->_HandleTogglePaneZoom(nullptr, eventArgs); - auto firstTab = page->_GetStrongTabImpl(0); + auto firstTab = page->_GetTerminalTabImpl(0); VERIFY_ARE_EQUAL(2, firstTab->GetLeafPaneCount()); VERIFY_IS_TRUE(firstTab->IsZoomed()); }); @@ -628,7 +628,7 @@ namespace TerminalAppLocalTests page->_HandleMoveFocus(nullptr, eventArgs); - auto firstTab = page->_GetStrongTabImpl(0); + auto firstTab = page->_GetTerminalTabImpl(0); VERIFY_ARE_EQUAL(2, firstTab->GetLeafPaneCount()); VERIFY_IS_FALSE(firstTab->IsZoomed()); }); @@ -645,7 +645,7 @@ namespace TerminalAppLocalTests SplitPaneArgs args{ SplitType::Duplicate }; ActionEventArgs eventArgs{ args }; page->_HandleSplitPane(nullptr, eventArgs); - auto firstTab = page->_GetStrongTabImpl(0); + auto firstTab = page->_GetTerminalTabImpl(0); VERIFY_ARE_EQUAL(2, firstTab->GetLeafPaneCount()); VERIFY_IS_FALSE(firstTab->IsZoomed()); @@ -659,7 +659,7 @@ namespace TerminalAppLocalTests page->_HandleTogglePaneZoom(nullptr, eventArgs); - auto firstTab = page->_GetStrongTabImpl(0); + auto firstTab = page->_GetTerminalTabImpl(0); VERIFY_ARE_EQUAL(2, firstTab->GetLeafPaneCount()); VERIFY_IS_TRUE(firstTab->IsZoomed()); }); @@ -672,7 +672,7 @@ namespace TerminalAppLocalTests page->_HandleClosePane(nullptr, eventArgs); - auto firstTab = page->_GetStrongTabImpl(0); + auto firstTab = page->_GetTerminalTabImpl(0); VERIFY_IS_FALSE(firstTab->IsZoomed()); }); VERIFY_SUCCEEDED(result); @@ -683,7 +683,7 @@ namespace TerminalAppLocalTests Log::Comment(L"Check to ensure there's only one pane left."); result = RunOnUIThread([&page]() { - auto firstTab = page->_GetStrongTabImpl(0); + auto firstTab = page->_GetTerminalTabImpl(0); VERIFY_ARE_EQUAL(1, firstTab->GetLeafPaneCount()); VERIFY_IS_FALSE(firstTab->IsZoomed()); }); diff --git a/src/cascadia/TerminalApp/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp index e2ca19453b4..47339208cf7 100644 --- a/src/cascadia/TerminalApp/AppActionHandlers.cpp +++ b/src/cascadia/TerminalApp/AppActionHandlers.cpp @@ -130,21 +130,26 @@ namespace winrt::TerminalApp::implementation void TerminalPage::_HandleTogglePaneZoom(const IInspectable& /*sender*/, const ActionEventArgs& args) { - auto activeTab = _GetFocusedTab(); - - // Don't do anything if there's only one pane. It's already zoomed. - if (activeTab && activeTab->GetLeafPaneCount() > 1) + if (auto focusedTab = _GetFocusedTab()) { - // First thing's first, remove the current content from the UI - // tree. This is important, because we might be leaving zoom, and if - // a pane is zoomed, then it's currently in the UI tree, and should - // be removed before it's re-added in Pane::Restore - _tabContent.Children().Clear(); - - // Togging the zoom on the tab will cause the tab to inform us of - // the new root Content for this tab. - activeTab->ToggleZoom(); + if (auto activeTab = _GetTerminalTabImpl(focusedTab)) + { + // Don't do anything if there's only one pane. It's already zoomed. + if (activeTab && activeTab->GetLeafPaneCount() > 1) + { + // First thing's first, remove the current content from the UI + // tree. This is important, because we might be leaving zoom, and if + // a pane is zoomed, then it's currently in the UI tree, and should + // be removed before it's re-added in Pane::Restore + _tabContent.Children().Clear(); + + // Togging the zoom on the tab will cause the tab to inform us of + // the new root Content for this tab. + activeTab->ToggleZoom(); + } + } } + args.Handled(true); } @@ -323,16 +328,19 @@ namespace winrt::TerminalApp::implementation args.Handled(false); if (const auto& realArgs = args.ActionArgs().try_as()) { - if (auto activeTab = _GetFocusedTab()) + if (auto focusedTab = _GetFocusedTab()) { - if (auto activeControl = activeTab->GetActiveTerminalControl()) + if (auto activeTab = _GetTerminalTabImpl(focusedTab)) { - if (const auto scheme = _settings.GlobalSettings().ColorSchemes().TryLookup(realArgs.SchemeName())) + if (auto activeControl = activeTab->GetActiveTerminalControl()) { - auto controlSettings = activeControl.Settings().as(); - controlSettings->ApplyColorScheme(scheme); - activeControl.UpdateSettings(*controlSettings); - args.Handled(true); + if (const auto scheme = _settings.GlobalSettings().ColorSchemes().TryLookup(realArgs.SchemeName())) + { + auto controlSettings = activeControl.Settings().as(); + controlSettings->ApplyColorScheme(scheme); + activeControl.UpdateSettings(*controlSettings); + args.Handled(true); + } } } } @@ -352,16 +360,18 @@ namespace winrt::TerminalApp::implementation } } - auto activeTab = _GetFocusedTab(); - if (activeTab) + if (auto focusedTab = _GetFocusedTab()) { - if (tabColor.has_value()) + if (auto activeTab = _GetTerminalTabImpl(focusedTab)) { - activeTab->SetRuntimeTabColor(tabColor.value()); - } - else - { - activeTab->ResetRuntimeTabColor(); + if (tabColor.has_value()) + { + activeTab->SetRuntimeTabColor(tabColor.value()); + } + else + { + activeTab->ResetRuntimeTabColor(); + } } } args.Handled(true); @@ -370,10 +380,12 @@ namespace winrt::TerminalApp::implementation void TerminalPage::_HandleOpenTabColorPicker(const IInspectable& /*sender*/, const ActionEventArgs& args) { - auto activeTab = _GetFocusedTab(); - if (activeTab) + if (auto focusedTab = _GetFocusedTab()) { - activeTab->ActivateColorPicker(); + if (auto activeTab = _GetTerminalTabImpl(focusedTab)) + { + activeTab->ActivateColorPicker(); + } } args.Handled(true); } @@ -388,16 +400,18 @@ namespace winrt::TerminalApp::implementation title = realArgs.Title(); } - auto activeTab = _GetFocusedTab(); - if (activeTab) + if (auto focusedTab = _GetFocusedTab()) { - if (title.has_value()) - { - activeTab->SetTabText(title.value()); - } - else + if (auto activeTab = _GetTerminalTabImpl(focusedTab)) { - activeTab->ResetTabText(); + if (title.has_value()) + { + activeTab->SetTabText(title.value()); + } + else + { + activeTab->ResetTabText(); + } } } args.Handled(true); @@ -406,10 +420,12 @@ namespace winrt::TerminalApp::implementation void TerminalPage::_HandleOpenTabRenamer(const IInspectable& /*sender*/, const ActionEventArgs& args) { - auto activeTab = _GetFocusedTab(); - if (activeTab) + if (auto focusedTab = _GetFocusedTab()) { - activeTab->ActivateTabRenamer(); + if (auto activeTab = _GetTerminalTabImpl(focusedTab)) + { + activeTab->ActivateTabRenamer(); + } } args.Handled(true); } diff --git a/src/cascadia/TerminalApp/AppLogic.h b/src/cascadia/TerminalApp/AppLogic.h index 077982d8a58..34875582685 100644 --- a/src/cascadia/TerminalApp/AppLogic.h +++ b/src/cascadia/TerminalApp/AppLogic.h @@ -4,8 +4,6 @@ #pragma once #include "AppLogic.g.h" - -#include "Tab.h" #include "TerminalPage.h" #include "Jumplist.h" #include "../../cascadia/inc/cppwinrt_utils.h" diff --git a/src/cascadia/TerminalApp/Tab.idl b/src/cascadia/TerminalApp/Tab.idl deleted file mode 100644 index 7e88c587816..00000000000 --- a/src/cascadia/TerminalApp/Tab.idl +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. -import "ShortcutActionDispatch.idl"; - -namespace TerminalApp -{ - [default_interface] runtimeclass Tab : Windows.UI.Xaml.Data.INotifyPropertyChanged - { - String Title { get; }; - Windows.UI.Xaml.Controls.IconSource IconSource { get; }; - Microsoft.Terminal.Settings.Model.Command SwitchToTabCommand { get; }; - UInt32 TabViewIndex { get; }; - - Windows.UI.Xaml.UIElement Content { get; }; - - void SetDispatch(ShortcutActionDispatch dispatch); - } -} diff --git a/src/cascadia/TerminalApp/TabBase.cpp b/src/cascadia/TerminalApp/TabBase.cpp new file mode 100644 index 00000000000..84d6a8b4c4d --- /dev/null +++ b/src/cascadia/TerminalApp/TabBase.cpp @@ -0,0 +1,148 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#include "pch.h" +#include +#include "TabBase.h" +#include "TabBase.g.cpp" + +using namespace winrt; +using namespace winrt::Windows::UI::Xaml; +using namespace winrt::Windows::UI::Core; +using namespace winrt::Microsoft::Terminal::TerminalControl; +using namespace winrt::Microsoft::Terminal::Settings::Model; +using namespace winrt::Windows::System; + +namespace winrt +{ + namespace MUX = Microsoft::UI::Xaml; + namespace WUX = Windows::UI::Xaml; +} + +namespace winrt::TerminalApp::implementation +{ + WUX::FocusState TabBase::FocusState() const noexcept + { + return _focusState; + } + + // Method Description: + // - Prepares this tab for being removed from the UI hierarchy + void TabBase::Shutdown() + { + Content(nullptr); + _ClosedHandlers(nullptr, nullptr); + } + + // Method Description: + // - Creates a context menu attached to the tab. + // Currently contains elements allowing the user to close the selected tab + // Arguments: + // - + // Return Value: + // - + void TabBase::_CreateContextMenu() + { + auto weakThis{ get_weak() }; + + // Close + Controls::MenuFlyoutItem closeTabMenuItem; + Controls::FontIcon closeSymbol; + closeSymbol.FontFamily(Media::FontFamily{ L"Segoe MDL2 Assets" }); + closeSymbol.Glyph(L"\xE8BB"); + + closeTabMenuItem.Click([weakThis](auto&&, auto&&) { + if (auto tab{ weakThis.get() }) + { + tab->_ClosedHandlers(nullptr, nullptr); + } + }); + closeTabMenuItem.Text(RS_(L"TabClose")); + closeTabMenuItem.Icon(closeSymbol); + + // Build the menu + Controls::MenuFlyout newTabFlyout; + newTabFlyout.Items().Append(_CreateCloseSubMenu()); + newTabFlyout.Items().Append(closeTabMenuItem); + TabViewItem().ContextFlyout(newTabFlyout); + } + + // Method Description: + // - Creates a sub-menu containing menu items to close multiple tabs + // Arguments: + // - + // Return Value: + // - the created MenuFlyoutSubItem + Controls::MenuFlyoutSubItem TabBase::_CreateCloseSubMenu() + { + auto weakThis{ get_weak() }; + + // Close tabs after + _closeTabsAfterMenuItem.Click([weakThis](auto&&, auto&&) { + if (auto tab{ weakThis.get() }) + { + tab->_CloseTabsAfter(); + } + }); + _closeTabsAfterMenuItem.Text(RS_(L"TabCloseAfter")); + + // Close other tabs + _closeOtherTabsMenuItem.Click([weakThis](auto&&, auto&&) { + if (auto tab{ weakThis.get() }) + { + tab->_CloseOtherTabs(); + } + }); + _closeOtherTabsMenuItem.Text(RS_(L"TabCloseOther")); + + Controls::MenuFlyoutSubItem closeSubMenu; + closeSubMenu.Text(RS_(L"TabCloseSubMenu")); + closeSubMenu.Items().Append(_closeTabsAfterMenuItem); + closeSubMenu.Items().Append(_closeOtherTabsMenuItem); + + return closeSubMenu; + } + + // Method Description: + // - Enable the Close menu items based on tab index and total number of tabs + // Arguments: + // - + // Return Value: + // - + void TabBase::_EnableCloseMenuItems() + { + // close other tabs is enabled only if there are other tabs + _closeOtherTabsMenuItem.IsEnabled(TabViewNumTabs() > 1); + // close tabs after is enabled only if there are other tabs on the right + _closeTabsAfterMenuItem.IsEnabled(TabViewIndex() < TabViewNumTabs() - 1); + } + + void TabBase::_CloseTabsAfter() + { + CloseTabsAfterArgs args{ _TabViewIndex }; + ActionAndArgs closeTabsAfter{ ShortcutAction::CloseTabsAfter, args }; + + _dispatch.DoAction(closeTabsAfter); + } + + void TabBase::_CloseOtherTabs() + { + CloseOtherTabsArgs args{ _TabViewIndex }; + ActionAndArgs closeOtherTabs{ ShortcutAction::CloseOtherTabs, args }; + + _dispatch.DoAction(closeOtherTabs); + } + + void TabBase::UpdateTabViewIndex(const uint32_t idx, const uint32_t numTabs) + { + TabViewIndex(idx); + TabViewNumTabs(numTabs); + _EnableCloseMenuItems(); + SwitchToTabCommand().Action().Args().as().TabIndex(idx); + } + + void TabBase::SetDispatch(const winrt::TerminalApp::ShortcutActionDispatch& dispatch) + { + _dispatch = dispatch; + } +} diff --git a/src/cascadia/TerminalApp/TabBase.h b/src/cascadia/TerminalApp/TabBase.h new file mode 100644 index 00000000000..d4697fadcd6 --- /dev/null +++ b/src/cascadia/TerminalApp/TabBase.h @@ -0,0 +1,57 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once +#include "inc/cppwinrt_utils.h" +#include "TabBase.g.h" + +// fwdecl unittest classes +namespace TerminalAppLocalTests +{ + class TabTests; +}; + +namespace winrt::TerminalApp::implementation +{ + struct TabBase : TabBaseT + { + public: + virtual void Focus(winrt::Windows::UI::Xaml::FocusState focusState) = 0; + winrt::Windows::UI::Xaml::FocusState FocusState() const noexcept; + + virtual void Shutdown(); + void SetDispatch(const winrt::TerminalApp::ShortcutActionDispatch& dispatch); + + void UpdateTabViewIndex(const uint32_t idx, const uint32_t numTabs); + + WINRT_CALLBACK(Closed, winrt::Windows::Foundation::EventHandler); + WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); + + // The TabViewIndex is the index this Tab object resides in TerminalPage's _tabs vector. + // This is needed since Tab is going to be managing its own SwitchToTab command. + GETSET_PROPERTY(uint32_t, TabViewIndex, 0); + // The TabViewNumTabs is the number of Tab objects in TerminalPage's _tabs vector. + GETSET_PROPERTY(uint32_t, TabViewNumTabs, 0); + + OBSERVABLE_GETSET_PROPERTY(winrt::hstring, Title, _PropertyChangedHandlers); + OBSERVABLE_GETSET_PROPERTY(winrt::hstring, Icon, _PropertyChangedHandlers); + OBSERVABLE_GETSET_PROPERTY(winrt::Microsoft::Terminal::Settings::Model::Command, SwitchToTabCommand, _PropertyChangedHandlers, nullptr); + GETSET_PROPERTY(winrt::Microsoft::UI::Xaml::Controls::TabViewItem, TabViewItem, nullptr); + + OBSERVABLE_GETSET_PROPERTY(winrt::Windows::UI::Xaml::FrameworkElement, Content, _PropertyChangedHandlers, nullptr); + + protected: + winrt::Windows::UI::Xaml::FocusState _focusState{ winrt::Windows::UI::Xaml::FocusState::Unfocused }; + winrt::Windows::UI::Xaml::Controls::MenuFlyoutItem _closeOtherTabsMenuItem{}; + winrt::Windows::UI::Xaml::Controls::MenuFlyoutItem _closeTabsAfterMenuItem{}; + winrt::TerminalApp::ShortcutActionDispatch _dispatch; + + virtual void _CreateContextMenu(); + winrt::Windows::UI::Xaml::Controls::MenuFlyoutSubItem _CreateCloseSubMenu(); + void _EnableCloseMenuItems(); + void _CloseTabsAfter(); + void _CloseOtherTabs(); + + friend class ::TerminalAppLocalTests::TabTests; + }; +} diff --git a/src/cascadia/TerminalApp/TabBase.idl b/src/cascadia/TerminalApp/TabBase.idl new file mode 100644 index 00000000000..040a2559120 --- /dev/null +++ b/src/cascadia/TerminalApp/TabBase.idl @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +import "ShortcutActionDispatch.idl"; + +namespace TerminalApp +{ + unsealed runtimeclass TabBase : Windows.UI.Xaml.Data.INotifyPropertyChanged + { + String Title { get; }; + String Icon { get; }; + Microsoft.Terminal.Settings.Model.Command SwitchToTabCommand; + Microsoft.UI.Xaml.Controls.TabViewItem TabViewItem { get; }; + Windows.UI.Xaml.FrameworkElement Content { get; }; + Windows.UI.Xaml.FocusState FocusState { get; }; + + UInt32 TabViewIndex; + UInt32 TabViewNumTabs; + + overridable void Focus(Windows.UI.Xaml.FocusState focusState); + overridable void Shutdown(); + + void SetDispatch(ShortcutActionDispatch dispatch); + } +} diff --git a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj index ca38ca66b13..d8dae687c4c 100644 --- a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj +++ b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj @@ -70,6 +70,12 @@ MinMaxCloseControl.xaml + + TabBase.idl + + + TerminalTab.idl + TerminalPage.xaml Code @@ -95,9 +101,6 @@ IconPathConverter.idl - - Tab.idl - @@ -127,6 +130,12 @@ MinMaxCloseControl.xaml + + TabBase.idl + + + TerminalTab.idl + TerminalPage.xaml Code @@ -152,9 +161,6 @@ IconPathConverter.idl - - Tab.idl - @@ -197,6 +203,8 @@ MinMaxCloseControl.xaml Code + + TerminalPage.xaml Code @@ -220,7 +228,6 @@ - diff --git a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj.filters b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj.filters index 33a85d5e112..746652be67c 100644 --- a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj.filters +++ b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj.filters @@ -27,6 +27,10 @@ settings + + + tab + @@ -48,6 +52,10 @@ settings + + + tab + @@ -62,14 +70,13 @@ settings - - tab - - settings + + tab + @@ -119,4 +126,4 @@ app - + \ No newline at end of file diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 8c16c74cfdb..d61a9a9ef81 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -42,7 +42,7 @@ namespace winrt namespace winrt::TerminalApp::implementation { TerminalPage::TerminalPage() : - _tabs{ winrt::single_threaded_observable_vector() }, + _tabs{ winrt::single_threaded_observable_vector() }, _mruTabActions{ winrt::single_threaded_vector() }, _startupActions{ winrt::single_threaded_vector() } { @@ -668,8 +668,10 @@ namespace winrt::TerminalApp::implementation TermControl term{ settings, connection }; + auto newTabImpl = winrt::make_self(profileGuid, term); + _MakeSwitchToTabCommand(*newTabImpl, _tabs.Size()); + // Add the new tab to the list of our tabs. - auto newTabImpl = winrt::make_self(profileGuid, term); _tabs.Append(*newTabImpl); _mruTabActions.Append(newTabImpl->SwitchToTabCommand()); @@ -686,7 +688,8 @@ namespace winrt::TerminalApp::implementation auto weakTab = make_weak(newTabImpl); // When the tab's active pane changes, we'll want to lookup a new icon - // for it, and possibly propagate the title up to the window. + // for it. The Title change will be propagated upwards through the tab's + // PropertyChanged event handler. newTabImpl->ActivePaneChanged([weakTab, weakThis{ get_weak() }]() { auto page{ weakThis.get() }; auto tab{ weakTab.get() }; @@ -695,24 +698,12 @@ namespace winrt::TerminalApp::implementation { // Possibly update the icon of the tab. page->_UpdateTabIcon(*tab); - // Possibly update the title of the tab, window to match the newly - // focused pane. - page->_UpdateTitle(*tab); } }); - auto tabViewItem = newTabImpl->GetTabViewItem(); + auto tabViewItem = newTabImpl->TabViewItem(); _tabView.TabItems().Append(tabViewItem); - // GH#6570 - // The TabView does not apply compact sizing to items added after Compact is enabled. - // By forcibly reapplying compact sizing every time we add a new tab, we'll make sure - // that it works. - // Workaround from https://github.com/microsoft/microsoft-ui-xaml/issues/2711 - if (_tabView.TabWidthMode() == MUX::Controls::TabViewWidthMode::Compact) - { - _tabView.UpdateLayout(); - _tabView.TabWidthMode(MUX::Controls::TabViewWidthMode::Compact); - } + _ReapplyCompactTabSize(); // Set this tab's icon to the icon from the user's profile const auto profile = _settings.FindProfile(profileGuid); @@ -923,12 +914,12 @@ namespace winrt::TerminalApp::implementation // TitleChanged event. // Arguments: // - tab: the Tab to update the title for. - void TerminalPage::_UpdateTitle(const Tab& tab) + void TerminalPage::_UpdateTitle(const TerminalTab& tab) { - auto newTabTitle = tab.GetActiveTitle(); + auto newTabTitle = tab.Title(); if (_settings.GlobalSettings().ShowTitleInTitlebar() && - tab.IsFocused()) + tab.FocusState() != FocusState::Unfocused) { _titleChangeHandlers(*this, newTabTitle); } @@ -939,7 +930,7 @@ namespace winrt::TerminalApp::implementation // tab's icon to that icon. // Arguments: // - tab: the Tab to update the title for. - void TerminalPage::_UpdateTabIcon(Tab& tab) + void TerminalPage::_UpdateTabIcon(TerminalTab& tab) { const auto lastFocusedProfileOpt = tab.GetFocusedProfile(); if (lastFocusedProfileOpt.has_value()) @@ -990,30 +981,32 @@ namespace winrt::TerminalApp::implementation { if (auto index{ _GetFocusedTabIndex() }) { - try + if (auto terminalTab = _GetTerminalTabImpl(_tabs.GetAt(*index))) { - auto focusedTab = _GetStrongTabImpl(*index); - // TODO: GH#5047 - In the future, we should get the Profile of - // the focused pane, and use that to build a new instance of the - // settings so we can duplicate this tab/pane. - // - // Currently, if the profile doesn't exist anymore in our - // settings, we'll silently do nothing. - // - // In the future, it will be preferable to just duplicate the - // current control's settings, but we can't do that currently, - // because we won't be able to create a new instance of the - // connection without keeping an instance of the original Profile - // object around. - - const auto& profileGuid = focusedTab->GetFocusedProfile(); - if (profileGuid.has_value()) + try { - const auto settings{ winrt::make(_settings, profileGuid.value(), *_bindings) }; - _CreateNewTabFromSettings(profileGuid.value(), settings); + // TODO: GH#5047 - In the future, we should get the Profile of + // the focused pane, and use that to build a new instance of the + // settings so we can duplicate this tab/pane. + // + // Currently, if the profile doesn't exist anymore in our + // settings, we'll silently do nothing. + // + // In the future, it will be preferable to just duplicate the + // current control's settings, but we can't do that currently, + // because we won't be able to create a new instance of the + // connection without keeping an instance of the original Profile + // object around. + + const auto& profileGuid = terminalTab->GetFocusedProfile(); + if (profileGuid.has_value()) + { + const auto settings{ winrt::make(_settings, profileGuid.value(), *_bindings) }; + _CreateNewTabFromSettings(profileGuid.value(), settings); + } } + CATCH_LOG(); } - CATCH_LOG(); } } @@ -1040,8 +1033,8 @@ namespace winrt::TerminalApp::implementation { // Removing the tab from the collection should destroy its control and disconnect its connection, // but it doesn't always do so. The UI tree may still be holding the control and preventing its destruction. - auto tab{ _GetStrongTabImpl(tabIndex) }; - tab->Shutdown(); + auto tab{ _tabs.GetAt(tabIndex) }; + tab.Shutdown(); uint32_t mruIndex; if (_mruTabActions.IndexOf(_tabs.GetAt(tabIndex).SwitchToTabCommand(), mruIndex)) @@ -1091,8 +1084,8 @@ namespace winrt::TerminalApp::implementation // here. If we don't, then the TabView will technically not have a // selected item at all, which can make things like ClosePane not // work correctly. - auto newSelectedTab{ _GetStrongTabImpl(newSelectedIndex) }; - _tabView.SelectedItem(newSelectedTab->GetTabViewItem()); + auto newSelectedTab{ _tabs.GetAt(newSelectedIndex) }; + _tabView.SelectedItem(newSelectedTab.TabViewItem()); } // GH#5559 - If we were in the middle of a drag/drop, end it by clearing @@ -1114,7 +1107,7 @@ namespace winrt::TerminalApp::implementation // Arguments: // - term: The newly created TermControl to connect the events for // - hostingTab: The Tab that's hosting this TermControl instance - void TerminalPage::_RegisterTerminalEvents(TermControl term, Tab& hostingTab) + void TerminalPage::_RegisterTerminalEvents(TermControl term, TerminalTab& hostingTab) { // Add an event handler when the terminal's selection wants to be copied. // When the text buffer data is retrieved, we'll copy the data into the Clipboard @@ -1144,12 +1137,12 @@ namespace winrt::TerminalApp::implementation } else if (args.PropertyName() == L"Content") { - if (tab == page->_GetFocusedTab()) + if (*tab == page->_GetFocusedTab()) { page->_tabContent.Children().Clear(); page->_tabContent.Children().Append(tab->Content()); - tab->SetFocused(true); + tab->Focus(FocusState::Programmatic); } } } @@ -1160,7 +1153,7 @@ namespace winrt::TerminalApp::implementation auto page{ weakThis.get() }; auto tab{ weakTab.get() }; - if (page && tab && tab->IsFocused()) + if (page && tab && (tab->FocusState() != FocusState::Unfocused)) { page->_SetNonClientAreaColors(color); } @@ -1170,7 +1163,7 @@ namespace winrt::TerminalApp::implementation auto page{ weakThis.get() }; auto tab{ weakTab.get() }; - if (page && tab && tab->IsFocused()) + if (page && tab && (tab->FocusState() != FocusState::Unfocused)) { page->_ClearNonClientAreaColors(); } @@ -1234,8 +1227,8 @@ namespace winrt::TerminalApp::implementation { if (_startupState == StartupState::InStartup) { - auto tab{ _GetStrongTabImpl(tabIndex) }; - _tabView.SelectedItem(tab->GetTabViewItem()); + auto tab{ _tabs.GetAt(tabIndex) }; + _tabView.SelectedItem(tab.TabViewItem()); _UpdatedSelectedTab(tabIndex); } else @@ -1263,16 +1256,21 @@ namespace winrt::TerminalApp::implementation // - void TerminalPage::_UnZoomIfNeeded() { - auto activeTab = _GetFocusedTab(); - if (activeTab && activeTab->IsZoomed()) + if (auto focusedTab = _GetFocusedTab()) { - // Remove the content from the tab first, so Pane::UnZoom can - // re-attach the content to the tree w/in the pane - _tabContent.Children().Clear(); - // In ExitZoom, we'll change the Tab's Content(), triggering the - // content changed event, which will re-attach the tab's new content - // root to the tree. - activeTab->ExitZoom(); + if (auto activeTab = _GetTerminalTabImpl(focusedTab)) + { + if (activeTab->IsZoomed()) + { + // Remove the content from the tab first, so Pane::UnZoom can + // re-attach the content to the tree w/in the pane + _tabContent.Children().Clear(); + // In ExitZoom, we'll change the Tab's Content(), triggering the + // content changed event, which will re-attach the tab's new content + // root to the tree. + activeTab->ExitZoom(); + } + } } } @@ -1288,9 +1286,11 @@ namespace winrt::TerminalApp::implementation { if (auto index{ _GetFocusedTabIndex() }) { - auto focusedTab{ _GetStrongTabImpl(*index) }; - _UnZoomIfNeeded(); - focusedTab->NavigateFocus(direction); + if (auto terminalTab = _GetTerminalTabImpl(_tabs.GetAt(*index))) + { + _UnZoomIfNeeded(); + terminalTab->NavigateFocus(direction); + } } } @@ -1298,13 +1298,12 @@ namespace winrt::TerminalApp::implementation { if (auto index{ _GetFocusedTabIndex() }) { - auto focusedTab{ _GetStrongTabImpl(*index) }; - return focusedTab->GetActiveTerminalControl(); - } - else - { - return nullptr; + if (auto terminalTab = _GetTerminalTabImpl(_tabs.GetAt(*index))) + { + return terminalTab->GetActiveTerminalControl(); + } } + return nullptr; } // Method Description: @@ -1327,11 +1326,11 @@ namespace winrt::TerminalApp::implementation // Method Description: // - returns a com_ptr to the currently focused tab. This might return null, // so make sure to check the result! - winrt::com_ptr TerminalPage::_GetFocusedTab() + winrt::TerminalApp::TabBase TerminalPage::_GetFocusedTab() { if (auto index{ _GetFocusedTabIndex() }) { - return _GetStrongTabImpl(*index); + return _tabs.GetAt(*index); } return nullptr; } @@ -1356,8 +1355,8 @@ namespace winrt::TerminalApp::implementation if (auto page{ weakThis.get() }) { - auto tab{ _GetStrongTabImpl(tabIndex) }; - _tabView.SelectedItem(tab->GetTabViewItem()); + auto tabToFocus = page->_tabs.GetAt(tabIndex); + _tabView.SelectedItem(tabToFocus.TabViewItem()); } } @@ -1379,9 +1378,11 @@ namespace winrt::TerminalApp::implementation { if (auto index{ _GetFocusedTabIndex() }) { - auto focusedTab{ _GetStrongTabImpl(*index) }; - _UnZoomIfNeeded(); - focusedTab->ClosePane(); + if (auto terminalTab = _GetTerminalTabImpl(_tabs.GetAt(*index))) + { + _UnZoomIfNeeded(); + terminalTab->ClosePane(); + } } } @@ -1422,22 +1423,24 @@ namespace winrt::TerminalApp::implementation { if (auto index{ _GetFocusedTabIndex() }) { - auto focusedTab{ _GetStrongTabImpl(*index) }; - uint32_t realRowsToScroll; - if (rowsToScroll == nullptr) - { - // The magic value of WHEEL_PAGESCROLL indicates that we need to scroll the entire page - realRowsToScroll = _systemRowsToScroll == WHEEL_PAGESCROLL ? - focusedTab->GetActiveTerminalControl().GetViewHeight() : - _systemRowsToScroll; - } - else + if (auto terminalTab = _GetTerminalTabImpl(_tabs.GetAt(*index))) { - // use the custom value specified in the command - realRowsToScroll = rowsToScroll.Value(); + uint32_t realRowsToScroll; + if (rowsToScroll == nullptr) + { + // The magic value of WHEEL_PAGESCROLL indicates that we need to scroll the entire page + realRowsToScroll = _systemRowsToScroll == WHEEL_PAGESCROLL ? + terminalTab->GetActiveTerminalControl().GetViewHeight() : + _systemRowsToScroll; + } + else + { + // use the custom value specified in the command + realRowsToScroll = rowsToScroll.Value(); + } + auto scrollDelta = _ComputeScrollDelta(scrollDirection, realRowsToScroll); + terminalTab->Scroll(scrollDelta); } - auto scrollDelta = _ComputeScrollDelta(scrollDirection, realRowsToScroll); - focusedTab->Scroll(scrollDelta); } } @@ -1470,9 +1473,16 @@ namespace winrt::TerminalApp::implementation return; } + auto focusedTab = _GetTerminalTabImpl(_tabs.GetAt(*indexOpt)); + + // Do nothing if the focused tab isn't a TerminalTab + if (!focusedTab) + { + return; + } + try { - auto focusedTab = _GetStrongTabImpl(*indexOpt); TerminalApp::TerminalSettings controlSettings; GUID realGuid; bool profileFound = false; @@ -1546,9 +1556,11 @@ namespace winrt::TerminalApp::implementation { if (auto index{ _GetFocusedTabIndex() }) { - auto focusedTab{ _GetStrongTabImpl(*index) }; - _UnZoomIfNeeded(); - focusedTab->ResizePane(direction); + if (auto terminalTab = _GetTerminalTabImpl(_tabs.GetAt(*index))) + { + _UnZoomIfNeeded(); + terminalTab->ResizePane(direction); + } } } @@ -1566,11 +1578,13 @@ namespace winrt::TerminalApp::implementation return; } - const auto control = _GetActiveControl(); - const auto termHeight = control.GetViewHeight(); - auto focusedTab{ _GetStrongTabImpl(*indexOpt) }; - auto scrollDelta = _ComputeScrollDelta(scrollDirection, termHeight); - focusedTab->Scroll(scrollDelta); + if (auto terminalTab = _GetTerminalTabImpl(_tabs.GetAt(*indexOpt))) + { + const auto control = _GetActiveControl(); + const auto termHeight = control.GetViewHeight(); + auto scrollDelta = _ComputeScrollDelta(scrollDirection, termHeight); + terminalTab->Scroll(scrollDelta); + } } // Method Description: @@ -1681,8 +1695,10 @@ namespace winrt::TerminalApp::implementation { if (auto index{ _GetFocusedTabIndex() }) { - auto focusedTab{ _GetStrongTabImpl(*index) }; - return focusedTab->CalcSnappedDimension(widthOrHeight, dimension); + if (auto terminalTab = _GetTerminalTabImpl(_tabs.GetAt(*index))) + { + return terminalTab->CalcSnappedDimension(widthOrHeight, dimension); + } } } return dimension; @@ -1960,18 +1976,17 @@ namespace winrt::TerminalApp::implementation // Unfocus all the tabs. for (auto tab : _tabs) { - auto tabImpl{ _GetStrongTabImpl(tab) }; - tabImpl->SetFocused(false); + tab.Focus(FocusState::Unfocused); } if (index >= 0) { try { - auto tab{ _GetStrongTabImpl(index) }; + auto tab{ _tabs.GetAt(index) }; _tabContent.Children().Clear(); - _tabContent.Children().Append(tab->Content()); + _tabContent.Children().Append(tab.Content()); // GH#7409: If the tab switcher is open, then we _don't_ want to // automatically focus the new tab here. The tab switcher wants @@ -1985,12 +2000,12 @@ namespace winrt::TerminalApp::implementation // need to worry about focus getting lost. if (CommandPalette().Visibility() != Visibility::Visible) { - tab->SetFocused(true); + tab.Focus(FocusState::Programmatic); _UpdateMRUTab(index); } // Raise an event that our title changed - _titleChangeHandlers(*this, tab->GetActiveTitle()); + _titleChangeHandlers(*this, tab.Title()); } CATCH_LOG(); } @@ -2025,8 +2040,10 @@ namespace winrt::TerminalApp::implementation const auto newSize = e.NewSize(); for (auto tab : _tabs) { - auto tabImpl{ _GetStrongTabImpl(tab) }; - tabImpl->ResizeContent(newSize); + if (auto terminalTab = _GetTerminalTabImpl(tab)) + { + terminalTab->ResizeContent(newSize); + } } } @@ -2097,9 +2114,10 @@ namespace winrt::TerminalApp::implementation for (auto tab : _tabs) { - // Attempt to reload the settings of any panes with this profile - auto tabImpl{ _GetStrongTabImpl(tab) }; - tabImpl->UpdateSettings(settings, profileGuid); + if (auto terminalTab = _GetTerminalTabImpl(tab)) + { + terminalTab->UpdateSettings(settings, profileGuid); + } } } CATCH_LOG(); @@ -2111,11 +2129,17 @@ namespace winrt::TerminalApp::implementation // anymore, so we can't possibly update its settings. // Update the icon of the tab for the currently focused profile in that tab. + // Only do this for TerminalTabs. Other types of tabs won't have multiple panes + // and profiles so the Title and Icon will be set once and only once on init. for (auto tab : _tabs) { - auto tabImpl{ _GetStrongTabImpl(tab) }; - _UpdateTabIcon(*tabImpl); - _UpdateTitle(*tabImpl); + if (auto terminalTab = _GetTerminalTabImpl(tab)) + { + _UpdateTabIcon(*terminalTab); + + // Force the TerminalTab to re-grab its currently active control's title. + terminalTab->UpdateTitle(); + } } auto weakThis{ get_weak() }; @@ -2255,10 +2279,9 @@ namespace winrt::TerminalApp::implementation for (const auto& tab : _tabs) { - auto tabImpl{ _GetStrongTabImpl(tab) }; - if (tabImpl->GetTabViewItem().ContextFlyout()) + if (tab.TabViewItem().ContextFlyout()) { - tabImpl->GetTabViewItem().ContextFlyout().Hide(); + tab.TabViewItem().ContextFlyout().Hide(); } } } @@ -2317,32 +2340,6 @@ namespace winrt::TerminalApp::implementation _alwaysOnTopChangedHandlers(*this, nullptr); } - // Method Description: - // - Returns a com_ptr to the implementation type of the tab at the given index - // Arguments: - // - index: an unsigned integer index to a tab in _tabs - // Return Value: - // - a com_ptr to the implementation type of the Tab - winrt::com_ptr TerminalPage::_GetStrongTabImpl(const uint32_t index) const - { - winrt::com_ptr tabImpl; - tabImpl.copy_from(winrt::get_self(_tabs.GetAt(index))); - return tabImpl; - } - - // Method Description: - // - Returns a com_ptr to the implementation type of the given projected Tab - // Arguments: - // - tab: the projected type of a Tab - // Return Value: - // - a com_ptr to the implementation type of the Tab - winrt::com_ptr TerminalPage::_GetStrongTabImpl(const ::winrt::TerminalApp::Tab& tab) const - { - winrt::com_ptr tabImpl; - tabImpl.copy_from(winrt::get_self(tab)); - return tabImpl; - } - // Method Description: // - Sets the tab split button color when a new tab color is selected // Arguments: @@ -2544,7 +2541,7 @@ namespace winrt::TerminalApp::implementation // Return focus to the active control if (auto index{ _GetFocusedTabIndex() }) { - _GetStrongTabImpl(index.value())->SetFocused(true); + _tabs.GetAt(*index).Focus(FocusState::Programmatic); _UpdateMRUTab(index.value()); } } @@ -2583,10 +2580,75 @@ namespace winrt::TerminalApp::implementation const uint32_t size = _tabs.Size(); for (uint32_t i = 0; i < size; ++i) { - _GetStrongTabImpl(i)->UpdateTabViewIndex(i, size); + auto tab{ _tabs.GetAt(i) }; + auto tabImpl{ winrt::get_self(tab) }; + tabImpl->UpdateTabViewIndex(i, size); } } + // Method Description: + // - Returns a com_ptr to the implementation type of the given tab if it's a TerminalTab. + // If the tab is not a TerminalTab, returns nullptr. + // Arguments: + // - tab: the projected type of a Tab + // Return Value: + // - If the tab is a TerminalTab, a com_ptr to the implementation type. + // If the tab is not a TerminalTab, nullptr + winrt::com_ptr TerminalPage::_GetTerminalTabImpl(const TerminalApp::TabBase& tab) const + { + if (auto terminalTab = tab.try_as()) + { + winrt::com_ptr tabImpl; + tabImpl.copy_from(winrt::get_self(terminalTab)); + return tabImpl; + } + else + { + return nullptr; + } + } + + // Method Description: + // - The TabView does not apply compact sizing to items added after Compact is enabled. + // By forcibly reapplying compact sizing every time we add a new tab, we'll make sure + // that it works. + // Workaround from https://github.com/microsoft/microsoft-ui-xaml/issues/2711 + // TODO: Remove this function and its calls when ingesting the above changes. + // Arguments: + // - + // Return Value: + // - + void TerminalPage::_ReapplyCompactTabSize() + { + if (_tabView.TabWidthMode() == MUX::Controls::TabViewWidthMode::Compact) + { + _tabView.UpdateLayout(); + _tabView.TabWidthMode(MUX::Controls::TabViewWidthMode::Compact); + } + } + + // Method Description: + // - Initializes a SwitchToTab command object for this Tab instance. + // This should be done before the tab is added to the _tabs vector so that + // controls like the CmdPal that observe the vector changes can always expect + // a SwitchToTab command to be available. + // Arguments: + // - + // Return Value: + // - + void TerminalPage::_MakeSwitchToTabCommand(const TerminalApp::TabBase& tab, const uint32_t index) + { + SwitchToTabArgs args{ index }; + ActionAndArgs focusTabAction{ ShortcutAction::SwitchToTab, args }; + + Command command; + command.Action(focusTabAction); + command.Name(tab.Title()); + command.Icon(tab.Icon()); + + tab.SwitchToTabCommand(command); + } + // Method Description: // - Computes the delta for scrolling the tab's viewport. // Arguments: diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 7d29e77c928..97d87d4f78c 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -4,7 +4,7 @@ #pragma once #include "TerminalPage.g.h" -#include "Tab.h" +#include "TerminalTab.h" #include "AppKeyBindings.h" #include "TerminalSettings.h" @@ -96,10 +96,10 @@ namespace winrt::TerminalApp::implementation Microsoft::Terminal::Settings::Model::CascadiaSettings _settings{ nullptr }; - Windows::Foundation::Collections::IObservableVector _tabs; + Windows::Foundation::Collections::IObservableVector _tabs; Windows::Foundation::Collections::IVector _mruTabActions; - winrt::com_ptr _GetStrongTabImpl(const uint32_t index) const; - winrt::com_ptr _GetStrongTabImpl(const ::winrt::TerminalApp::Tab& tab) const; + winrt::com_ptr _GetTerminalTabImpl(const TerminalApp::TabBase& tab) const; + void _UpdateTabIndices(); bool _isInFocusMode{ false }; @@ -146,8 +146,8 @@ namespace winrt::TerminalApp::implementation void _HookupKeyBindings(const Microsoft::Terminal::Settings::Model::KeyMapping& keymap) noexcept; void _RegisterActionCallbacks(); - void _UpdateTitle(const Tab& tab); - void _UpdateTabIcon(Tab& tab); + void _UpdateTitle(const TerminalTab& tab); + void _UpdateTabIcon(TerminalTab& tab); void _UpdateTabView(); void _UpdateTabWidthMode(); void _UpdateCommandsForPalette(); @@ -159,7 +159,7 @@ namespace winrt::TerminalApp::implementation void _RemoveTabViewItem(const Microsoft::UI::Xaml::Controls::TabViewItem& tabViewItem); void _RemoveTabViewItemByIndex(uint32_t tabIndex); - void _RegisterTerminalEvents(Microsoft::Terminal::TerminalControl::TermControl term, Tab& hostingTab); + void _RegisterTerminalEvents(Microsoft::Terminal::TerminalControl::TermControl term, TerminalTab& hostingTab); void _SelectNextTab(const bool bMoveRight); bool _SelectTab(const uint32_t tabIndex); @@ -167,7 +167,7 @@ namespace winrt::TerminalApp::implementation winrt::Microsoft::Terminal::TerminalControl::TermControl _GetActiveControl(); std::optional _GetFocusedTabIndex() const noexcept; - winrt::com_ptr _GetFocusedTab(); + TerminalApp::TabBase _GetFocusedTab(); winrt::fire_and_forget _SetFocusedTabIndex(const uint32_t tabIndex); void _CloseFocusedTab(); void _CloseFocusedPane(); @@ -218,6 +218,12 @@ namespace winrt::TerminalApp::implementation void _UnZoomIfNeeded(); + void _OpenSettingsUI(); + + void _ReapplyCompactTabSize(); + + void _MakeSwitchToTabCommand(const TerminalApp::TabBase& tab, const uint32_t index); + static int _ComputeScrollDelta(ScrollDirection scrollDirection, const uint32_t rowsToScroll); static uint32_t _ReadSystemRowsToScroll(); diff --git a/src/cascadia/TerminalApp/Tab.cpp b/src/cascadia/TerminalApp/TerminalTab.cpp similarity index 74% rename from src/cascadia/TerminalApp/Tab.cpp rename to src/cascadia/TerminalApp/TerminalTab.cpp index 6ca57fa902d..567d48a7ba1 100644 --- a/src/cascadia/TerminalApp/Tab.cpp +++ b/src/cascadia/TerminalApp/TerminalTab.cpp @@ -4,8 +4,8 @@ #include "pch.h" #include #include "ColorPickupFlyout.h" -#include "Tab.h" -#include "Tab.g.cpp" +#include "TerminalTab.h" +#include "TerminalTab.g.cpp" #include "Utils.h" #include "ColorHelper.h" @@ -24,7 +24,7 @@ namespace winrt namespace winrt::TerminalApp::implementation { - Tab::Tab(const GUID& profile, const TermControl& control) + TerminalTab::TerminalTab(const GUID& profile, const TermControl& control) { _rootPane = std::make_shared(profile, control, true); @@ -36,7 +36,6 @@ namespace winrt::TerminalApp::implementation Content(_rootPane->GetRootElement()); _MakeTabViewItem(); - _MakeSwitchToTabCommand(); _CreateContextMenu(); } @@ -46,18 +45,18 @@ namespace winrt::TerminalApp::implementation // - // Return Value: // - - void Tab::_MakeTabViewItem() + void TerminalTab::_MakeTabViewItem() { - _tabViewItem = ::winrt::MUX::Controls::TabViewItem{}; + TabViewItem(::winrt::MUX::Controls::TabViewItem{}); - _tabViewItem.DoubleTapped([weakThis = get_weak()](auto&& /*s*/, auto&& /*e*/) { + TabViewItem().DoubleTapped([weakThis = get_weak()](auto&& /*s*/, auto&& /*e*/) { if (auto tab{ weakThis.get() }) { tab->ActivateTabRenamer(); } }); - _UpdateTitle(); + UpdateTitle(); _RecalculateAndApplyTabColor(); } @@ -72,22 +71,11 @@ namespace winrt::TerminalApp::implementation // Return Value: // - nullptr if no children were marked `_lastFocused`, else the TermControl // that was last focused. - TermControl Tab::GetActiveTerminalControl() const + TermControl TerminalTab::GetActiveTerminalControl() const { return _activePane->GetTerminalControl(); } - // Method Description: - // - Gets the TabViewItem that represents this Tab - // Arguments: - // - - // Return Value: - // - The TabViewItem that represents this Tab - winrt::MUX::Controls::TabViewItem Tab::GetTabViewItem() - { - return _tabViewItem; - } - // Method Description: // - Called after construction of a Tab object to bind event handlers to its // associated Pane and TermControl object @@ -95,39 +83,29 @@ namespace winrt::TerminalApp::implementation // - control: reference to the TermControl object to bind event to // Return Value: // - - void Tab::Initialize(const TermControl& control) + void TerminalTab::Initialize(const TermControl& control) { _BindEventHandlers(control); } - // Method Description: - // - Returns true if this is the currently focused tab. For any set of tabs, - // there should only be one tab that is marked as focused, though each tab has - // no control over the other tabs in the set. - // Arguments: - // - - // Return Value: - // - true iff this tab is focused. - bool Tab::IsFocused() const noexcept - { - return _focused; - } - // Method Description: // - Updates our focus state. If we're gaining focus, make sure to transfer // focus to the last focused terminal control in our tree of controls. // Arguments: - // - focused: our new focus state. If true, we should be focused. If false, we - // should be unfocused. + // - focused: our new focus state // Return Value: // - - void Tab::SetFocused(const bool focused) + void TerminalTab::Focus(WUX::FocusState focusState) { - _focused = focused; + _focusState = focusState; - if (_focused) + if (_focusState != FocusState::Unfocused) { - _Focus(); + auto lastFocusedControl = GetActiveTerminalControl(); + if (lastFocusedControl) + { + lastFocusedControl.Focus(_focusState); + } } } @@ -140,7 +118,7 @@ namespace winrt::TerminalApp::implementation // Return Value: // - nullopt if no children of this tab were the last control to be // focused, else the GUID of the profile of the last control to be focused - std::optional Tab::GetFocusedProfile() const noexcept + std::optional TerminalTab::GetFocusedProfile() const noexcept { return _activePane->GetFocusedProfile(); } @@ -152,7 +130,7 @@ namespace winrt::TerminalApp::implementation // - control: reference to the TermControl object to bind event to // Return Value: // - - void Tab::_BindEventHandlers(const TermControl& control) noexcept + void TerminalTab::_BindEventHandlers(const TermControl& control) noexcept { _AttachEventHandlersToPane(_rootPane); _AttachEventHandlersToControl(control); @@ -165,35 +143,18 @@ namespace winrt::TerminalApp::implementation // - profile: The GUID of the profile these settings should apply to. // Return Value: // - - void Tab::UpdateSettings(const TerminalSettings& settings, const GUID& profile) + void TerminalTab::UpdateSettings(const TerminalSettings& settings, const GUID& profile) { _rootPane->UpdateSettings(settings, profile); } - // Method Description: - // - Focus the last focused control in our tree of panes. - // Arguments: - // - - // Return Value: - // - - void Tab::_Focus() - { - _focused = true; - - auto lastFocusedControl = GetActiveTerminalControl(); - if (lastFocusedControl) - { - lastFocusedControl.Focus(FocusState::Programmatic); - } - } - // Method Description: // - Set the icon on the TabViewItem for this tab. // Arguments: // - iconPath: The new path string to use as the IconPath for our TabViewItem // Return Value: // - - winrt::fire_and_forget Tab::UpdateIcon(const winrt::hstring iconPath) + winrt::fire_and_forget TerminalTab::UpdateIcon(const winrt::hstring iconPath) { // Don't reload our icon if it hasn't changed. if (iconPath == _lastIconPath) @@ -205,13 +166,13 @@ namespace winrt::TerminalApp::implementation auto weakThis{ get_weak() }; - co_await winrt::resume_foreground(_tabViewItem.Dispatcher()); + co_await winrt::resume_foreground(TabViewItem().Dispatcher()); if (auto tab{ weakThis.get() }) { // The TabViewItem Icon needs MUX while the IconSourceElement in the CommandPalette needs WUX... - IconSource(IconPathConverter::IconSourceWUX(_lastIconPath)); - _tabViewItem.IconSource(IconPathConverter::IconSourceMUX(_lastIconPath)); + Icon(_lastIconPath); + TabViewItem().IconSource(IconPathConverter::IconSourceMUX(_lastIconPath)); // Update SwitchToTab command's icon SwitchToTabCommand().Icon(_lastIconPath); @@ -225,7 +186,7 @@ namespace winrt::TerminalApp::implementation // - // Return Value: // - the title string of the last focused terminal control in our tree. - winrt::hstring Tab::GetActiveTitle() const + winrt::hstring TerminalTab::_GetActiveTitle() const { if (!_runtimeTabText.empty()) { @@ -243,14 +204,14 @@ namespace winrt::TerminalApp::implementation // - // Return Value: // - - winrt::fire_and_forget Tab::_UpdateTitle() + winrt::fire_and_forget TerminalTab::UpdateTitle() { auto weakThis{ get_weak() }; - co_await winrt::resume_foreground(_tabViewItem.Dispatcher()); + co_await winrt::resume_foreground(TabViewItem().Dispatcher()); if (auto tab{ weakThis.get() }) { // Bubble our current tab text to anyone who's listening for changes. - Title(GetActiveTitle()); + Title(_GetActiveTitle()); // Update SwitchToTab command's name SwitchToTabCommand().Name(Title()); @@ -268,7 +229,7 @@ namespace winrt::TerminalApp::implementation // - delta: a number of lines to move the viewport relative to the current viewport. // Return Value: // - - winrt::fire_and_forget Tab::Scroll(const int delta) + winrt::fire_and_forget TerminalTab::Scroll(const int delta) { auto control = GetActiveTerminalControl(); @@ -284,7 +245,7 @@ namespace winrt::TerminalApp::implementation // - splitType: The type of split we want to create. // Return Value: // - True if the focused pane can be split. False otherwise. - bool Tab::CanSplitPane(SplitState splitType) + bool TerminalTab::CanSplitPane(SplitState splitType) { return _activePane->CanSplit(splitType); } @@ -298,7 +259,7 @@ namespace winrt::TerminalApp::implementation // - control: A TermControl to use in the new pane. // Return Value: // - - void Tab::SplitPane(SplitState splitType, const GUID& profile, TermControl& control) + void TerminalTab::SplitPane(SplitState splitType, const GUID& profile, TermControl& control) { auto [first, second] = _activePane->Split(splitType, profile, control); _activePane = first; @@ -318,7 +279,7 @@ namespace winrt::TerminalApp::implementation // Method Description: // - See Pane::CalcSnappedDimension - float Tab::CalcSnappedDimension(const bool widthOrHeight, const float dimension) const + float TerminalTab::CalcSnappedDimension(const bool widthOrHeight, const float dimension) const { return _rootPane->CalcSnappedDimension(widthOrHeight, dimension); } @@ -330,7 +291,7 @@ namespace winrt::TerminalApp::implementation // - newSize: the amount of space that the panes have to fill now. // Return Value: // - - void Tab::ResizeContent(const winrt::Windows::Foundation::Size& newSize) + void TerminalTab::ResizeContent(const winrt::Windows::Foundation::Size& newSize) { // NOTE: This _must_ be called on the root pane, so that it can propagate // throughout the entire tree. @@ -344,7 +305,7 @@ namespace winrt::TerminalApp::implementation // - direction: The direction to move the separator in. // Return Value: // - - void Tab::ResizePane(const Direction& direction) + void TerminalTab::ResizePane(const Direction& direction) { // NOTE: This _must_ be called on the root pane, so that it can propagate // throughout the entire tree. @@ -358,7 +319,7 @@ namespace winrt::TerminalApp::implementation // - direction: The direction to move the focus in. // Return Value: // - - void Tab::NavigateFocus(const Direction& direction) + void TerminalTab::NavigateFocus(const Direction& direction) { // NOTE: This _must_ be called on the root pane, so that it can propagate // throughout the entire tree. @@ -367,7 +328,7 @@ namespace winrt::TerminalApp::implementation // Method Description: // - Prepares this tab for being removed from the UI hierarchy by shutting down all active connections. - void Tab::Shutdown() + void TerminalTab::Shutdown() { _rootPane->Shutdown(); } @@ -380,21 +341,21 @@ namespace winrt::TerminalApp::implementation // - // Return Value: // - - void Tab::ClosePane() + void TerminalTab::ClosePane() { _activePane->Close(); } - void Tab::SetTabText(winrt::hstring title) + void TerminalTab::SetTabText(winrt::hstring title) { _runtimeTabText = title; - _UpdateTitle(); + UpdateTitle(); } - void Tab::ResetTabText() + void TerminalTab::ResetTabText() { _runtimeTabText = L""; - _UpdateTitle(); + UpdateTitle(); } // Method Description: @@ -404,7 +365,7 @@ namespace winrt::TerminalApp::implementation // - // Return Value: // - - void Tab::ActivateTabRenamer() + void TerminalTab::ActivateTabRenamer() { _inRename = true; _receivedKeyDown = false; @@ -421,7 +382,7 @@ namespace winrt::TerminalApp::implementation // - control: the TermControl to add events to. // Return Value: // - - void Tab::_AttachEventHandlersToControl(const TermControl& control) + void TerminalTab::_AttachEventHandlersToControl(const TermControl& control) { auto weakThis{ get_weak() }; @@ -431,7 +392,7 @@ namespace winrt::TerminalApp::implementation { // The title of the control changed, but not necessarily the title of the tab. // Set the tab's text to the active panes' text. - tab->_UpdateTitle(); + tab->UpdateTitle(); } }); @@ -468,7 +429,7 @@ namespace winrt::TerminalApp::implementation // - pane: a Pane to mark as active. // Return Value: // - - void Tab::_UpdateActivePane(std::shared_ptr pane) + void TerminalTab::_UpdateActivePane(std::shared_ptr pane) { // Clear the active state of the entire tree, and mark only the pane as active. _rootPane->ClearActive(); @@ -476,7 +437,7 @@ namespace winrt::TerminalApp::implementation _activePane->SetActive(); // Update our own title text to match the newly-active pane. - _UpdateTitle(); + UpdateTitle(); // Raise our own ActivePaneChanged event. _ActivePaneChangedHandlers(); @@ -491,7 +452,7 @@ namespace winrt::TerminalApp::implementation // - // Return Value: // - - void Tab::_AttachEventHandlersToPane(std::shared_ptr pane) + void TerminalTab::_AttachEventHandlersToPane(std::shared_ptr pane) { auto weakThis{ get_weak() }; @@ -531,7 +492,7 @@ namespace winrt::TerminalApp::implementation // - // Return Value: // - - void Tab::_CreateContextMenu() + void TerminalTab::_CreateContextMenu() { auto weakThis{ get_weak() }; @@ -605,57 +566,7 @@ namespace winrt::TerminalApp::implementation newTabFlyout.Items().Append(menuSeparator); newTabFlyout.Items().Append(_CreateCloseSubMenu()); newTabFlyout.Items().Append(closeTabMenuItem); - _tabViewItem.ContextFlyout(newTabFlyout); - } - - // Method Description: - // - Creates a sub-menu containing menu items to close multiple tabs - // Arguments: - // - - // Return Value: - // - the created MenuFlyoutSubItem - Controls::MenuFlyoutSubItem Tab::_CreateCloseSubMenu() - { - auto weakThis{ get_weak() }; - - // Close tabs after - _closeTabsAfterMenuItem.Click([weakThis](auto&&, auto&&) { - if (auto tab{ weakThis.get() }) - { - tab->_CloseTabsAfter(); - } - }); - _closeTabsAfterMenuItem.Text(RS_(L"TabCloseAfter")); - - // Close other tabs - _closeOtherTabsMenuItem.Click([weakThis](auto&&, auto&&) { - if (auto tab{ weakThis.get() }) - { - tab->_CloseOtherTabs(); - } - }); - _closeOtherTabsMenuItem.Text(RS_(L"TabCloseOther")); - - Controls::MenuFlyoutSubItem closeSubMenu; - closeSubMenu.Text(RS_(L"TabCloseSubMenu")); - closeSubMenu.Items().Append(_closeTabsAfterMenuItem); - closeSubMenu.Items().Append(_closeOtherTabsMenuItem); - - return closeSubMenu; - } - - // Method Description: - // - Enable the Close menu items based on tab index and total number of tabs - // Arguments: - // - - // Return Value: - // - - void Tab::_EnableCloseMenuItems() - { - // close other tabs is enabled only if there are other tabs - _closeOtherTabsMenuItem.IsEnabled(TabViewNumTabs() > 1); - // close tabs after is enabled only if there are other tabs on the right - _closeTabsAfterMenuItem.IsEnabled(TabViewIndex() < TabViewNumTabs() - 1); + TabViewItem().ContextFlyout(newTabFlyout); } // Method Description: @@ -670,9 +581,9 @@ namespace winrt::TerminalApp::implementation // - // Return Value: // - - void Tab::_UpdateTabHeader() + void TerminalTab::_UpdateTabHeader() { - winrt::hstring tabText{ GetActiveTitle() }; + winrt::hstring tabText{ Title() }; if (!_inRename) { @@ -690,13 +601,13 @@ namespace winrt::TerminalApp::implementation tb.Text(tabText); sp.Children().Append(tb); - _tabViewItem.Header(sp); + TabViewItem().Header(sp); } else { // If we're not currently in the process of renaming the tab, // then just set the tab's text to whatever our active title is. - _tabViewItem.Header(winrt::box_value(tabText)); + TabViewItem().Header(winrt::box_value(tabText)); } } else @@ -713,9 +624,9 @@ namespace winrt::TerminalApp::implementation // - tabText: This should be the text to initialize the rename text box with. // Return Value: // - - void Tab::_ConstructTabRenameBox(const winrt::hstring& tabText) + void TerminalTab::_ConstructTabRenameBox(const winrt::hstring& tabText) { - if (_tabViewItem.Header().try_as()) + if (TabViewItem().Header().try_as()) { return; } @@ -763,7 +674,7 @@ namespace winrt::TerminalApp::implementation { tab->_runtimeTabText = textBox.Text(); tab->_inRename = false; - tab->_UpdateTitle(); + tab->UpdateTitle(); } }); @@ -793,7 +704,7 @@ namespace winrt::TerminalApp::implementation e.Handled(true); textBox.Text(tab->_runtimeTabText); tab->_inRename = false; - tab->_UpdateTitle(); + tab->UpdateTitle(); break; } } @@ -803,7 +714,7 @@ namespace winrt::TerminalApp::implementation _tabRenameBoxLayoutUpdatedRevoker = tabTextBox.LayoutUpdated(winrt::auto_revoke, [this](auto&&, auto&&) { // Curiously, the sender for this event is null, so we have to // get the TextBox from the Tab's Header(). - auto textBox{ _tabViewItem.Header().try_as() }; + auto textBox{ TabViewItem().Header().try_as() }; if (textBox) { textBox.SelectAll(); @@ -813,7 +724,7 @@ namespace winrt::TerminalApp::implementation _tabRenameBoxLayoutUpdatedRevoker.revoke(); }); - _tabViewItem.Header(tabTextBox); + TabViewItem().Header(tabTextBox); } // Method Description: @@ -822,7 +733,7 @@ namespace winrt::TerminalApp::implementation // - // Return Value: // - The tab's color, if any - std::optional Tab::GetTabColor() + std::optional TerminalTab::GetTabColor() { const auto currControlColor{ GetActiveTerminalControl().TabColor() }; std::optional controlTabColor; @@ -859,7 +770,7 @@ namespace winrt::TerminalApp::implementation // - color: the color the user picked for their tab // Return Value: // - - void Tab::SetRuntimeTabColor(const winrt::Windows::UI::Color& color) + void TerminalTab::SetRuntimeTabColor(const winrt::Windows::UI::Color& color) { _runtimeTabColor.emplace(color); _RecalculateAndApplyTabColor(); @@ -874,11 +785,11 @@ namespace winrt::TerminalApp::implementation // - // Return Value: // - - void Tab::_RecalculateAndApplyTabColor() + void TerminalTab::_RecalculateAndApplyTabColor() { auto weakThis{ get_weak() }; - _tabViewItem.Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [weakThis]() { + TabViewItem().Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [weakThis]() { auto ptrTab = weakThis.get(); if (!ptrTab) return; @@ -906,7 +817,7 @@ namespace winrt::TerminalApp::implementation // - color: the color the user picked for their tab // Return Value: // - - void Tab::_ApplyTabColor(const winrt::Windows::UI::Color& color) + void TerminalTab::_ApplyTabColor(const winrt::Windows::UI::Color& color) { Media::SolidColorBrush selectedTabBrush{}; Media::SolidColorBrush deselectedTabBrush{}; @@ -935,15 +846,15 @@ namespace winrt::TerminalApp::implementation // currently if a tab has a custom color, a deselected state is // signified by using the same color with a bit ot transparency - _tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackgroundSelected"), selectedTabBrush); - _tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackground"), deselectedTabBrush); - _tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackgroundPointerOver"), hoverTabBrush); - _tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackgroundPressed"), selectedTabBrush); - _tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderForeground"), fontBrush); - _tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderForegroundSelected"), fontBrush); - _tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderForegroundPointerOver"), fontBrush); - _tabViewItem.Resources().Insert(winrt::box_value(L"TabViewItemHeaderForegroundPressed"), fontBrush); - _tabViewItem.Resources().Insert(winrt::box_value(L"TabViewButtonForegroundActiveTab"), fontBrush); + TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackgroundSelected"), selectedTabBrush); + TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackground"), deselectedTabBrush); + TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackgroundPointerOver"), hoverTabBrush); + TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderBackgroundPressed"), selectedTabBrush); + TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderForeground"), fontBrush); + TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderForegroundSelected"), fontBrush); + TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderForegroundPointerOver"), fontBrush); + TabViewItem().Resources().Insert(winrt::box_value(L"TabViewItemHeaderForegroundPressed"), fontBrush); + TabViewItem().Resources().Insert(winrt::box_value(L"TabViewButtonForegroundActiveTab"), fontBrush); _RefreshVisualState(); @@ -958,7 +869,7 @@ namespace winrt::TerminalApp::implementation // - // Return Value: // - - void Tab::ResetRuntimeTabColor() + void TerminalTab::ResetRuntimeTabColor() { _runtimeTabColor.reset(); _RecalculateAndApplyTabColor(); @@ -971,7 +882,7 @@ namespace winrt::TerminalApp::implementation // - // Return Value: // - - void Tab::_ClearTabBackgroundColor() + void TerminalTab::_ClearTabBackgroundColor() { winrt::hstring keys[] = { L"TabViewItemHeaderBackground", @@ -989,9 +900,9 @@ namespace winrt::TerminalApp::implementation for (auto keyString : keys) { auto key = winrt::box_value(keyString); - if (_tabViewItem.Resources().HasKey(key)) + if (TabViewItem().Resources().HasKey(key)) { - _tabViewItem.Resources().Remove(key); + TabViewItem().Resources().Remove(key); } } @@ -1005,9 +916,9 @@ namespace winrt::TerminalApp::implementation // - // Return Value: // - - void Tab::ActivateColorPicker() + void TerminalTab::ActivateColorPicker() { - _tabColorPickup.ShowAt(_tabViewItem); + _tabColorPickup.ShowAt(TabViewItem()); } // Method Description: @@ -1017,17 +928,17 @@ namespace winrt::TerminalApp::implementation // - // Return Value: // - - void Tab::_RefreshVisualState() + void TerminalTab::_RefreshVisualState() { - if (_focused) + if (_focusState != FocusState::Unfocused) { - VisualStateManager::GoToState(_tabViewItem, L"Normal", true); - VisualStateManager::GoToState(_tabViewItem, L"Selected", true); + VisualStateManager::GoToState(TabViewItem(), L"Normal", true); + VisualStateManager::GoToState(TabViewItem(), L"Selected", true); } else { - VisualStateManager::GoToState(_tabViewItem, L"Selected", true); - VisualStateManager::GoToState(_tabViewItem, L"Normal", true); + VisualStateManager::GoToState(TabViewItem(), L"Selected", true); + VisualStateManager::GoToState(TabViewItem(), L"Normal", true); } } @@ -1037,7 +948,7 @@ namespace winrt::TerminalApp::implementation // - // Return Value: // - The total number of leaf panes hosted by this tab. - int Tab::GetLeafPaneCount() const noexcept + int TerminalTab::GetLeafPaneCount() const noexcept { return _rootPane->GetLeafPaneCount(); } @@ -1052,12 +963,12 @@ namespace winrt::TerminalApp::implementation // Return Value: // - The SplitState that we should use for an `Automatic` split given // `availableSpace` - SplitState Tab::PreCalculateAutoSplit(winrt::Windows::Foundation::Size availableSpace) const + SplitState TerminalTab::PreCalculateAutoSplit(winrt::Windows::Foundation::Size availableSpace) const { return _rootPane->PreCalculateAutoSplit(_activePane, availableSpace).value_or(SplitState::Vertical); } - bool Tab::PreCalculateCanSplit(SplitState splitType, winrt::Windows::Foundation::Size availableSpace) const + bool TerminalTab::PreCalculateCanSplit(SplitState splitType, winrt::Windows::Foundation::Size availableSpace) const { return _rootPane->PreCalculateCanSplit(_activePane, splitType, availableSpace).value_or(false); } @@ -1066,13 +977,13 @@ namespace winrt::TerminalApp::implementation // - Toggle our zoom state. // * If we're not zoomed, then zoom the active pane, making it take the // full size of the tab. We'll achieve this by changing our response to - // Tab::GetRootElement, so that it'll return the zoomed pane only. + // Tab::GetTabContent, so that it'll return the zoomed pane only. // * If we're currently zoomed on a pane, un-zoom that pane. // Arguments: // - // Return Value: // - - void Tab::ToggleZoom() + void TerminalTab::ToggleZoom() { if (_zoomedPane) { @@ -1083,7 +994,7 @@ namespace winrt::TerminalApp::implementation EnterZoom(); } } - void Tab::EnterZoom() + void TerminalTab::EnterZoom() { _zoomedPane = _activePane; _rootPane->Maximize(_zoomedPane); @@ -1091,7 +1002,7 @@ namespace winrt::TerminalApp::implementation _UpdateTabHeader(); Content(_zoomedPane->GetRootElement()); } - void Tab::ExitZoom() + void TerminalTab::ExitZoom() { _rootPane->Restore(_zoomedPane); _zoomedPane = nullptr; @@ -1100,60 +1011,12 @@ namespace winrt::TerminalApp::implementation Content(_rootPane->GetRootElement()); } - bool Tab::IsZoomed() + bool TerminalTab::IsZoomed() { return _zoomedPane != nullptr; } - // Method Description: - // - Initializes a SwitchToTab command object for this Tab instance. - // Arguments: - // - - // Return Value: - // - - void Tab::_MakeSwitchToTabCommand() - { - SwitchToTabArgs args{ _TabViewIndex }; - ActionAndArgs focusTabAction{ ShortcutAction::SwitchToTab, args }; - - Command command; - command.Action(focusTabAction); - command.Name(Title()); - command.Icon(_lastIconPath); - - SwitchToTabCommand(command); - } - - void Tab::_CloseTabsAfter() - { - CloseTabsAfterArgs args{ _TabViewIndex }; - ActionAndArgs closeTabsAfter{ ShortcutAction::CloseTabsAfter, args }; - - _dispatch.DoAction(closeTabsAfter); - } - - void Tab::_CloseOtherTabs() - { - CloseOtherTabsArgs args{ _TabViewIndex }; - ActionAndArgs closeOtherTabs{ ShortcutAction::CloseOtherTabs, args }; - - _dispatch.DoAction(closeOtherTabs); - } - - void Tab::UpdateTabViewIndex(const uint32_t idx, const uint32_t numTabs) - { - TabViewIndex(idx); - TabViewNumTabs(numTabs); - _EnableCloseMenuItems(); - SwitchToTabCommand().Action().Args().as().TabIndex(idx); - } - - void Tab::SetDispatch(const winrt::TerminalApp::ShortcutActionDispatch& dispatch) - { - _dispatch = dispatch; - } - - DEFINE_EVENT(Tab, ActivePaneChanged, _ActivePaneChangedHandlers, winrt::delegate<>); - DEFINE_EVENT(Tab, ColorSelected, _colorSelected, winrt::delegate); - DEFINE_EVENT(Tab, ColorCleared, _colorCleared, winrt::delegate<>); + DEFINE_EVENT(TerminalTab, ActivePaneChanged, _ActivePaneChangedHandlers, winrt::delegate<>); + DEFINE_EVENT(TerminalTab, ColorSelected, _colorSelected, winrt::delegate); + DEFINE_EVENT(TerminalTab, ColorCleared, _colorCleared, winrt::delegate<>); } diff --git a/src/cascadia/TerminalApp/Tab.h b/src/cascadia/TerminalApp/TerminalTab.h similarity index 64% rename from src/cascadia/TerminalApp/Tab.h rename to src/cascadia/TerminalApp/TerminalTab.h index 1eb4f85a9e3..7144a961fa4 100644 --- a/src/cascadia/TerminalApp/Tab.h +++ b/src/cascadia/TerminalApp/TerminalTab.h @@ -4,7 +4,8 @@ #pragma once #include "Pane.h" #include "ColorPickupFlyout.h" -#include "Tab.g.h" +#include "TabBase.h" +#include "TerminalTab.g.h" // fwdecl unittest classes namespace TerminalAppLocalTests @@ -14,21 +15,18 @@ namespace TerminalAppLocalTests namespace winrt::TerminalApp::implementation { - struct Tab : public TabT + struct TerminalTab : TerminalTabT { public: - Tab() = delete; - Tab(const GUID& profile, const winrt::Microsoft::Terminal::TerminalControl::TermControl& control); + TerminalTab(const GUID& profile, const winrt::Microsoft::Terminal::TerminalControl::TermControl& control); // Called after construction to perform the necessary setup, which relies on weak_ptr void Initialize(const winrt::Microsoft::Terminal::TerminalControl::TermControl& control); - winrt::Microsoft::UI::Xaml::Controls::TabViewItem GetTabViewItem(); winrt::Microsoft::Terminal::TerminalControl::TermControl GetActiveTerminalControl() const; std::optional GetFocusedProfile() const noexcept; - bool IsFocused() const noexcept; - void SetFocused(const bool focused); + void Focus(winrt::Windows::UI::Xaml::FocusState focusState) override; winrt::fire_and_forget Scroll(const int delta); @@ -46,9 +44,9 @@ namespace winrt::TerminalApp::implementation void NavigateFocus(const winrt::Microsoft::Terminal::Settings::Model::Direction& direction); void UpdateSettings(const winrt::TerminalApp::TerminalSettings& settings, const GUID& profile); - winrt::hstring GetActiveTitle() const; + winrt::fire_and_forget UpdateTitle(); - void Shutdown(); + void Shutdown() override; void ClosePane(); void SetTabText(winrt::hstring title); @@ -68,28 +66,10 @@ namespace winrt::TerminalApp::implementation int GetLeafPaneCount() const noexcept; - void UpdateTabViewIndex(const uint32_t idx, const uint32_t numTabs); - - void SetDispatch(const winrt::TerminalApp::ShortcutActionDispatch& dispatch); - - WINRT_CALLBACK(Closed, winrt::Windows::Foundation::EventHandler); - WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler); DECLARE_EVENT(ActivePaneChanged, _ActivePaneChangedHandlers, winrt::delegate<>); DECLARE_EVENT(ColorSelected, _colorSelected, winrt::delegate); DECLARE_EVENT(ColorCleared, _colorCleared, winrt::delegate<>); - OBSERVABLE_GETSET_PROPERTY(winrt::hstring, Title, _PropertyChangedHandlers); - OBSERVABLE_GETSET_PROPERTY(winrt::Windows::UI::Xaml::Controls::IconSource, IconSource, _PropertyChangedHandlers, nullptr); - OBSERVABLE_GETSET_PROPERTY(winrt::Microsoft::Terminal::Settings::Model::Command, SwitchToTabCommand, _PropertyChangedHandlers, nullptr); - - // The TabViewIndex is the index this Tab object resides in TerminalPage's _tabs vector. - // This is needed since Tab is going to be managing its own SwitchToTab command. - OBSERVABLE_GETSET_PROPERTY(uint32_t, TabViewIndex, _PropertyChangedHandlers, 0); - // The TabViewNumTabs is the number of Tab objects in TerminalPage's _tabs vector. - OBSERVABLE_GETSET_PROPERTY(uint32_t, TabViewNumTabs, _PropertyChangedHandlers, 0); - - OBSERVABLE_GETSET_PROPERTY(winrt::Windows::UI::Xaml::UIElement, Content, _PropertyChangedHandlers, nullptr); - private: std::shared_ptr _rootPane{ nullptr }; std::shared_ptr _activePane{ nullptr }; @@ -101,9 +81,7 @@ namespace winrt::TerminalApp::implementation winrt::Windows::UI::Xaml::Controls::MenuFlyoutItem _closeOtherTabsMenuItem{}; winrt::Windows::UI::Xaml::Controls::MenuFlyoutItem _closeTabsAfterMenuItem{}; - bool _focused{ false }; bool _receivedKeyDown{ false }; - winrt::Microsoft::UI::Xaml::Controls::TabViewItem _tabViewItem{ nullptr }; winrt::hstring _runtimeTabText{}; bool _inRename{ false }; @@ -112,11 +90,8 @@ namespace winrt::TerminalApp::implementation winrt::TerminalApp::ShortcutActionDispatch _dispatch; void _MakeTabViewItem(); - void _Focus(); - void _CreateContextMenu(); - winrt::Windows::UI::Xaml::Controls::MenuFlyoutSubItem _CreateCloseSubMenu(); - void _EnableCloseMenuItems(); + void _CreateContextMenu() override; void _RefreshVisualState(); @@ -127,19 +102,14 @@ namespace winrt::TerminalApp::implementation void _UpdateActivePane(std::shared_ptr pane); + winrt::hstring _GetActiveTitle() const; void _UpdateTabHeader(); - winrt::fire_and_forget _UpdateTitle(); void _ConstructTabRenameBox(const winrt::hstring& tabText); void _RecalculateAndApplyTabColor(); void _ApplyTabColor(const winrt::Windows::UI::Color& color); void _ClearTabBackgroundColor(); - void _MakeSwitchToTabCommand(); - - void _CloseTabsAfter(); - void _CloseOtherTabs(); - friend class ::TerminalAppLocalTests::TabTests; }; } diff --git a/src/cascadia/TerminalApp/TerminalTab.idl b/src/cascadia/TerminalApp/TerminalTab.idl new file mode 100644 index 00000000000..7485acbf27d --- /dev/null +++ b/src/cascadia/TerminalApp/TerminalTab.idl @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import "TabBase.idl"; + +namespace TerminalApp +{ + [default_interface] runtimeclass TerminalTab : TabBase + { + } +} diff --git a/src/cascadia/TerminalApp/dll/TerminalApp.vcxproj b/src/cascadia/TerminalApp/dll/TerminalApp.vcxproj index 003a977aae6..a85b1a9ae23 100644 --- a/src/cascadia/TerminalApp/dll/TerminalApp.vcxproj +++ b/src/cascadia/TerminalApp/dll/TerminalApp.vcxproj @@ -30,7 +30,8 @@ - + + diff --git a/src/cascadia/inc/cppwinrt_utils.h b/src/cascadia/inc/cppwinrt_utils.h index a1ac58fb80a..e20dcca82db 100644 --- a/src/cascadia/inc/cppwinrt_utils.h +++ b/src/cascadia/inc/cppwinrt_utils.h @@ -82,7 +82,7 @@ public: winrt::event_token name(args const& handler) { return _##name##Handlers.add(handler); } \ void name(winrt::event_token const& token) { _##name##Handlers.remove(token); } \ \ -private: \ +protected: \ winrt::event _##name##Handlers; // This is a helper macro for both declaring the signature and body of an event @@ -128,7 +128,7 @@ private: // (like when the class is being initialized). #define OBSERVABLE_GETSET_PROPERTY(type, name, event, ...) \ public: \ - type name() { return _##name; }; \ + type name() const noexcept { return _##name; }; \ void name(const type& value) \ { \ if (_##name != value) \