Make Tab an unsealed runtimeclass (and rename it to TabBase) (#8153)

In preparation for the Settings UI, we needed to make some changes to
Tab to abstract out shared, common functionality between different types
of tab. This is the result of that work. All code references to the
settings have been removed or reverted.

Contains changes from #8053, #7802.

The messages below only make sense in the context of the Settings UI,
which this pull request does not bring in. They do, however, provide
valuable information.

From #7802 (@leonMSFT):

> This PR's goal was to add an option to the `OpenSettings` keybinding to
> open the Settings UI in a tab. In order to implement that, a couple of
> changes had to be made to `Tab`, specifically:
>
> - Introduce a tab interface named `ITab`
> - Create/Rename two new Tab classes that implement `ITab` called
>   `SettingsTab` and `TerminalTab`
>

From #8053:

> `TerminalTab` and `SettingsTab` share some implementation details. The
> close submenu introduced in #7728 is a good example of functionality
> that is consistent across all tabs. This PR transforms `ITab` from an
> interface, into an [unsealed runtime class] to de-duplicate some
> functionality. Most of the logic from `SettingsTab` was moved there
> because I expect the default behavior of a tab to resemble the
> `SettingsTab` over a `TerminalTab`.
>
> ## References
> Verified that Close submenu work was transferred over (#7728, #7961, #8010).
>
> ## Validation Steps Performed
> Check close submenu on first/last tab when multiple tabs are open.
>
> Closes #7969
>
> [unsealed runtime class]: https://docs.microsoft.com/en-us/uwp/midl-3/intro#base-classes

Co-authored-by: Carlos Zamora <carlos.zamora@microsoft.com>

Co-authored-by: Leon Liang <lelian@microsoft.com>
Co-authored-by: Carlos Zamora <carlos.zamora@microsoft.com>
This commit is contained in:
Dustin L. Howett 2020-11-04 12:15:05 -06:00 committed by GitHub
parent 26ca73b823
commit 4eeaddc583
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 673 additions and 520 deletions

View file

@ -44,6 +44,7 @@ oaidl
ocidl
otms
OUTLINETEXTMETRICW
overridable
PAGESCROLL
RETURNCMD
rfind

View file

@ -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());
});

View file

@ -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();
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();
// 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<SetColorSchemeArgs>())
{
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<TerminalSettings>();
controlSettings->ApplyColorScheme(scheme);
activeControl.UpdateSettings(*controlSettings);
args.Handled(true);
if (const auto scheme = _settings.GlobalSettings().ColorSchemes().TryLookup(realArgs.SchemeName()))
{
auto controlSettings = activeControl.Settings().as<TerminalSettings>();
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())
if (auto activeTab = _GetTerminalTabImpl(focusedTab))
{
activeTab->SetTabText(title.value());
}
else
{
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);
}

View file

@ -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"

View file

@ -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);
}
}

View file

@ -0,0 +1,148 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include <LibraryResources.h>
#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:
// - <none>
// Return Value:
// - <none>
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:
// - <none>
// 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:
// - <none>
// Return Value:
// - <none>
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<SwitchToTabArgs>().TabIndex(idx);
}
void TabBase::SetDispatch(const winrt::TerminalApp::ShortcutActionDispatch& dispatch)
{
_dispatch = dispatch;
}
}

View file

@ -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<TabBase>
{
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::Windows::Foundation::IInspectable>);
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;
};
}

View file

@ -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);
}
}

View file

@ -70,6 +70,12 @@
<ClInclude Include="MinMaxCloseControl.h">
<DependentUpon>MinMaxCloseControl.xaml</DependentUpon>
</ClInclude>
<ClInclude Include="TabBase.h">
<DependentUpon>TabBase.idl</DependentUpon>
</ClInclude>
<ClInclude Include="TerminalTab.h">
<DependentUpon>TerminalTab.idl</DependentUpon>
</ClInclude>
<ClInclude Include="TerminalPage.h">
<DependentUpon>TerminalPage.xaml</DependentUpon>
<SubType>Code</SubType>
@ -95,9 +101,6 @@
<ClInclude Include="IconPathConverter.h">
<DependentUpon>IconPathConverter.idl</DependentUpon>
</ClInclude>
<ClInclude Include="Tab.h">
<DependentUpon>Tab.idl</DependentUpon>
</ClInclude>
<ClInclude Include="Pane.h" />
<ClInclude Include="ColorHelper.h" />
<ClInclude Include="TerminalSettings.h">
@ -127,6 +130,12 @@
<ClCompile Include="MinMaxCloseControl.cpp">
<DependentUpon>MinMaxCloseControl.xaml</DependentUpon>
</ClCompile>
<ClCompile Include="TabBase.cpp">
<DependentUpon>TabBase.idl</DependentUpon>
</ClCompile>
<ClCompile Include="TerminalTab.cpp">
<DependentUpon>TerminalTab.idl</DependentUpon>
</ClCompile>
<ClCompile Include="TerminalPage.cpp">
<DependentUpon>TerminalPage.xaml</DependentUpon>
<SubType>Code</SubType>
@ -152,9 +161,6 @@
<ClCompile Include="IconPathConverter.cpp">
<DependentUpon>IconPathConverter.idl</DependentUpon>
</ClCompile>
<ClCompile Include="Tab.cpp">
<DependentUpon>Tab.idl</DependentUpon>
</ClCompile>
<ClCompile Include="Pane.cpp" />
<ClCompile Include="Pane.LayoutSizeNode.cpp" />
<ClCompile Include="ColorHelper.cpp" />
@ -197,6 +203,8 @@
<DependentUpon>MinMaxCloseControl.xaml</DependentUpon>
<SubType>Code</SubType>
</Midl>
<Midl Include="TabBase.idl" />
<Midl Include="TerminalTab.idl" />
<Midl Include="TerminalPage.idl">
<DependentUpon>TerminalPage.xaml</DependentUpon>
<SubType>Code</SubType>
@ -220,7 +228,6 @@
<Midl Include="EmptyStringVisibilityConverter.idl" />
<Midl Include="HasNestedCommandsVisibilityConverter.idl" />
<Midl Include="IconPathConverter.idl" />
<Midl Include="Tab.idl" />
<Midl Include="TerminalSettings.idl" />
</ItemGroup>
<!-- ========================= Misc Files ======================== -->

View file

@ -27,6 +27,10 @@
<ClCompile Include="TerminalSettings.cpp">
<Filter>settings</Filter>
</ClCompile>
<ClCompile Include="Jumplist.cpp" />
<ClCompile Include="Tab.cpp">
<Filter>tab</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Utils.h" />
@ -48,6 +52,10 @@
<ClInclude Include="TerminalSettings.h">
<Filter>settings</Filter>
</ClInclude>
<ClInclude Include="Jumplist.h" />
<ClInclude Include="Tab.h">
<Filter>tab</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Midl Include="AppLogic.idl">
@ -62,14 +70,13 @@
<Midl Include="ShortcutActionDispatch.idl">
<Filter>settings</Filter>
</Midl>
<Midl Include="Tab.idl">
<Filter>tab</Filter>
</Midl>
<Midl Include="IDirectKeyListener.idl" />
<Midl Include="CommandKeyChordVisibilityConverter.idl" />
<Midl Include="TerminalSettings.idl">
<Filter>settings</Filter>
</Midl>
<Midl Include="TerminalTab.idl">
<Filter>tab</Filter>
</Midl>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
@ -119,4 +126,4 @@
<Filter>app</Filter>
</ApplicationDefinition>
</ItemGroup>
</Project>
</Project>

View file

@ -42,7 +42,7 @@ namespace winrt
namespace winrt::TerminalApp::implementation
{
TerminalPage::TerminalPage() :
_tabs{ winrt::single_threaded_observable_vector<TerminalApp::Tab>() },
_tabs{ winrt::single_threaded_observable_vector<TerminalApp::TabBase>() },
_mruTabActions{ winrt::single_threaded_vector<Command>() },
_startupActions{ winrt::single_threaded_vector<ActionAndArgs>() }
{
@ -668,8 +668,10 @@ namespace winrt::TerminalApp::implementation
TermControl term{ settings, connection };
auto newTabImpl = winrt::make_self<TerminalTab>(profileGuid, term);
_MakeSwitchToTabCommand(*newTabImpl, _tabs.Size());
// Add the new tab to the list of our tabs.
auto newTabImpl = winrt::make_self<Tab>(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<TerminalSettings>(_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<TerminalSettings>(_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
// - <none>
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<Tab> 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)
if (auto terminalTab = _GetTerminalTabImpl(_tabs.GetAt(*index)))
{
// The magic value of WHEEL_PAGESCROLL indicates that we need to scroll the entire page
realRowsToScroll = _systemRowsToScroll == WHEEL_PAGESCROLL ?
focusedTab->GetActiveTerminalControl().GetViewHeight() :
_systemRowsToScroll;
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);
}
else
{
// use the custom value specified in the command
realRowsToScroll = rowsToScroll.Value();
}
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<Tab> TerminalPage::_GetStrongTabImpl(const uint32_t index) const
{
winrt::com_ptr<Tab> tabImpl;
tabImpl.copy_from(winrt::get_self<Tab>(_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<Tab> TerminalPage::_GetStrongTabImpl(const ::winrt::TerminalApp::Tab& tab) const
{
winrt::com_ptr<Tab> tabImpl;
tabImpl.copy_from(winrt::get_self<Tab>(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<TabBase>(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<TerminalTab> TerminalPage::_GetTerminalTabImpl(const TerminalApp::TabBase& tab) const
{
if (auto terminalTab = tab.try_as<TerminalApp::TerminalTab>())
{
winrt::com_ptr<TerminalTab> tabImpl;
tabImpl.copy_from(winrt::get_self<TerminalTab>(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:
// - <none>
// Return Value:
// - <none>
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:
// - <none>
// Return Value:
// - <none>
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:

View file

@ -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<TerminalApp::Tab> _tabs;
Windows::Foundation::Collections::IObservableVector<TerminalApp::TabBase> _tabs;
Windows::Foundation::Collections::IVector<winrt::Microsoft::Terminal::Settings::Model::Command> _mruTabActions;
winrt::com_ptr<Tab> _GetStrongTabImpl(const uint32_t index) const;
winrt::com_ptr<Tab> _GetStrongTabImpl(const ::winrt::TerminalApp::Tab& tab) const;
winrt::com_ptr<TerminalTab> _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<uint32_t> _GetFocusedTabIndex() const noexcept;
winrt::com_ptr<Tab> _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();

View file

@ -4,8 +4,8 @@
#include "pch.h"
#include <LibraryResources.h>
#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<Pane>(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
// - <none>
// Return Value:
// - <none>
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:
// - <none>
// 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:
// - <none>
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:
// - <none>
// 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:
// - <none>
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<GUID> Tab::GetFocusedProfile() const noexcept
std::optional<GUID> 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:
// - <none>
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:
// - <none>
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:
// - <none>
// Return Value:
// - <none>
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:
// - <none>
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
// - <none>
// 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
// - <none>
// Return Value:
// - <none>
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:
// - <none>
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:
// - <none>
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:
// - <none>
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:
// - <none>
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:
// - <none>
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
// - <none>
// Return Value:
// - <none>
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
// - <none>
// Return Value:
// - <none>
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:
// - <none>
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:
// - <none>
void Tab::_UpdateActivePane(std::shared_ptr<Pane> pane)
void TerminalTab::_UpdateActivePane(std::shared_ptr<Pane> 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
// - <none>
// Return Value:
// - <none>
void Tab::_AttachEventHandlersToPane(std::shared_ptr<Pane> pane)
void TerminalTab::_AttachEventHandlersToPane(std::shared_ptr<Pane> pane)
{
auto weakThis{ get_weak() };
@ -531,7 +492,7 @@ namespace winrt::TerminalApp::implementation
// - <none>
// Return Value:
// - <none>
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:
// - <none>
// 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:
// - <none>
// Return Value:
// - <none>
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
// - <none>
// Return Value:
// - <none>
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:
// - <none>
void Tab::_ConstructTabRenameBox(const winrt::hstring& tabText)
void TerminalTab::_ConstructTabRenameBox(const winrt::hstring& tabText)
{
if (_tabViewItem.Header().try_as<Controls::TextBox>())
if (TabViewItem().Header().try_as<Controls::TextBox>())
{
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<Controls::TextBox>() };
auto textBox{ TabViewItem().Header().try_as<Controls::TextBox>() };
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
// - <none>
// Return Value:
// - The tab's color, if any
std::optional<winrt::Windows::UI::Color> Tab::GetTabColor()
std::optional<winrt::Windows::UI::Color> TerminalTab::GetTabColor()
{
const auto currControlColor{ GetActiveTerminalControl().TabColor() };
std::optional<winrt::Windows::UI::Color> controlTabColor;
@ -859,7 +770,7 @@ namespace winrt::TerminalApp::implementation
// - color: the color the user picked for their tab
// Return Value:
// - <none>
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
// - <none>
// Return Value:
// - <none>
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:
// - <none>
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
// - <none>
// Return Value:
// - <none>
void Tab::ResetRuntimeTabColor()
void TerminalTab::ResetRuntimeTabColor()
{
_runtimeTabColor.reset();
_RecalculateAndApplyTabColor();
@ -971,7 +882,7 @@ namespace winrt::TerminalApp::implementation
// - <none>
// Return Value:
// - <none>
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
// - <none>
// Return Value:
// - <none>
void Tab::ActivateColorPicker()
void TerminalTab::ActivateColorPicker()
{
_tabColorPickup.ShowAt(_tabViewItem);
_tabColorPickup.ShowAt(TabViewItem());
}
// Method Description:
@ -1017,17 +928,17 @@ namespace winrt::TerminalApp::implementation
// - <none>
// Return Value:
// - <none>
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
// - <none>
// 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:
// - <none>
// Return Value:
// - <none>
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:
// - <none>
// Return Value:
// - <none>
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<SwitchToTabArgs>().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<winrt::Windows::UI::Color>);
DEFINE_EVENT(Tab, ColorCleared, _colorCleared, winrt::delegate<>);
DEFINE_EVENT(TerminalTab, ActivePaneChanged, _ActivePaneChangedHandlers, winrt::delegate<>);
DEFINE_EVENT(TerminalTab, ColorSelected, _colorSelected, winrt::delegate<winrt::Windows::UI::Color>);
DEFINE_EVENT(TerminalTab, ColorCleared, _colorCleared, winrt::delegate<>);
}

View file

@ -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<Tab>
struct TerminalTab : TerminalTabT<TerminalTab, TabBase>
{
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<GUID> 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::Windows::Foundation::IInspectable>);
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
DECLARE_EVENT(ActivePaneChanged, _ActivePaneChangedHandlers, winrt::delegate<>);
DECLARE_EVENT(ColorSelected, _colorSelected, winrt::delegate<winrt::Windows::UI::Color>);
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<Pane> _rootPane{ nullptr };
std::shared_ptr<Pane> _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> 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;
};
}

View file

@ -0,0 +1,11 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import "TabBase.idl";
namespace TerminalApp
{
[default_interface] runtimeclass TerminalTab : TabBase
{
}
}

View file

@ -30,7 +30,8 @@
<ClInclude Include="../TitlebarControl.h" />
<ClInclude Include="../TabRowControl.h" />
<ClInclude Include="../App.h" />
<ClInclude Include="../Tab.h" />
<ClInclude Include="../TerminalTab.h" />
<ClInclude Include="../SettingsTab.h" />
</ItemGroup>
<!-- ========================= Cpp Files ======================== -->
<ItemGroup>

View file

@ -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<args> _##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) \