diff --git a/.github/actions/spell-check/dictionary/apis.txt b/.github/actions/spell-check/dictionary/apis.txt
index 969b90c3460..092ed76889f 100644
--- a/.github/actions/spell-check/dictionary/apis.txt
+++ b/.github/actions/spell-check/dictionary/apis.txt
@@ -24,6 +24,7 @@ IExplorer
IMap
IObject
IStorage
+ITab
llabs
LCID
lround
diff --git a/OpenConsole.sln b/OpenConsole.sln
index c033e78249d..54f0b9c95f5 100644
--- a/OpenConsole.sln
+++ b/OpenConsole.sln
@@ -2010,7 +2010,8 @@ Global
{CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}.AuditMode|x86.Build.0 = Release|Win32
{CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}.AuditMode|x86.Deploy.0 = Release|Win32
{CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}.Debug|Any CPU.ActiveCfg = Debug|Win32
- {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}.Debug|ARM64.ActiveCfg = Debug|Win32
+ {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}.Debug|ARM64.Build.0 = Debug|ARM64
{CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}.Debug|DotNet_x64Test.ActiveCfg = Debug|Win32
{CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}.Debug|DotNet_x86Test.ActiveCfg = Debug|Win32
{CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}.Debug|x64.ActiveCfg = Debug|x64
@@ -2020,7 +2021,8 @@ Global
{CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}.Debug|x86.Build.0 = Debug|Win32
{CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}.Debug|x86.Deploy.0 = Debug|Win32
{CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}.Release|Any CPU.ActiveCfg = Release|Win32
- {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}.Release|ARM64.ActiveCfg = Release|Win32
+ {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}.Release|ARM64.ActiveCfg = Release|ARM64
+ {CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}.Release|ARM64.Build.0 = Release|ARM64
{CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}.Release|DotNet_x64Test.ActiveCfg = Release|Win32
{CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}.Release|DotNet_x86Test.ActiveCfg = Release|Win32
{CA5CAD1A-0B5E-45C3-96A8-BB496BFE4E32}.Release|x64.ActiveCfg = Release|x64
diff --git a/src/cascadia/LocalTests_TerminalApp/TabTests.cpp b/src/cascadia/LocalTests_TerminalApp/TabTests.cpp
index 4b98b1a9552..9707269e828 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"
#include "JsonTestClass.h"
@@ -246,8 +246,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);
@@ -469,7 +469,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);
@@ -479,7 +479,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);
@@ -497,7 +497,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.");
diff --git a/src/cascadia/LocalTests_TerminalApp/TerminalApp.LocalTests.vcxproj b/src/cascadia/LocalTests_TerminalApp/TerminalApp.LocalTests.vcxproj
index 67952867d7d..eded639c75e 100644
--- a/src/cascadia/LocalTests_TerminalApp/TerminalApp.LocalTests.vcxproj
+++ b/src/cascadia/LocalTests_TerminalApp/TerminalApp.LocalTests.vcxproj
@@ -79,6 +79,7 @@
+
diff --git a/src/cascadia/LocalTests_TerminalApp/TestHostApp/TestHostApp.vcxproj b/src/cascadia/LocalTests_TerminalApp/TestHostApp/TestHostApp.vcxproj
index b6d85e9ae04..b9c3b869689 100644
--- a/src/cascadia/LocalTests_TerminalApp/TestHostApp/TestHostApp.vcxproj
+++ b/src/cascadia/LocalTests_TerminalApp/TestHostApp/TestHostApp.vcxproj
@@ -94,6 +94,8 @@
{CA5CAD1A-C46D-4588-B1C0-40F31AE9100B}
+
+
diff --git a/src/cascadia/TerminalApp/ActionArgs.idl b/src/cascadia/TerminalApp/ActionArgs.idl
index 51ecf282941..3bb355aa4ca 100644
--- a/src/cascadia/TerminalApp/ActionArgs.idl
+++ b/src/cascadia/TerminalApp/ActionArgs.idl
@@ -42,7 +42,8 @@ namespace TerminalApp
{
SettingsFile = 0,
DefaultsFile,
- AllFiles
+ AllFiles,
+ SettingsUI
};
[default_interface] runtimeclass NewTerminalArgs {
diff --git a/src/cascadia/TerminalApp/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp
index 6d2e765c3a1..74bcdddceda 100644
--- a/src/cascadia/TerminalApp/AppActionHandlers.cpp
+++ b/src/cascadia/TerminalApp/AppActionHandlers.cpp
@@ -121,22 +121,27 @@ namespace winrt::TerminalApp::implementation
void TerminalPage::_HandleTogglePaneZoom(const IInspectable& /*sender*/,
const TerminalApp::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();
- activeTab->ToggleZoom();
+ activeTab->ToggleZoom();
- // Update the selected tab, to trigger us to re-add the tab's GetRootElement to the UI tree
- _UpdatedSelectedTab(_tabView.SelectedIndex());
+ // Update the selected tab, to trigger us to re-add the tab's tab content to the UI tree
+ _UpdatedSelectedTab(_tabView.SelectedIndex());
+ }
+ }
}
+
args.Handled(true);
}
@@ -315,16 +320,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);
+ }
}
}
}
@@ -344,16 +352,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);
@@ -362,10 +372,12 @@ namespace winrt::TerminalApp::implementation
void TerminalPage::_HandleOpenTabColorPicker(const IInspectable& /*sender*/,
const TerminalApp::ActionEventArgs& args)
{
- auto activeTab = _GetFocusedTab();
- if (activeTab)
+ if (auto focusedTab = _GetFocusedTab())
{
- activeTab->ActivateColorPicker();
+ if (auto activeTab = _GetTerminalTabImpl(focusedTab))
+ {
+ activeTab->ActivateColorPicker();
+ }
}
args.Handled(true);
}
@@ -380,16 +392,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);
diff --git a/src/cascadia/TerminalApp/AppLogic.h b/src/cascadia/TerminalApp/AppLogic.h
index 192f7330ce3..aaf2425e602 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 "CascadiaSettings.h"
#include "TerminalPage.h"
#include "Jumplist.h"
diff --git a/src/cascadia/TerminalApp/CommandPalette.cpp b/src/cascadia/TerminalApp/CommandPalette.cpp
index 82fb48ed9a7..111b6c066d0 100644
--- a/src/cascadia/TerminalApp/CommandPalette.cpp
+++ b/src/cascadia/TerminalApp/CommandPalette.cpp
@@ -966,7 +966,7 @@ namespace winrt::TerminalApp::implementation
// -
void CommandPalette::OnTabsChanged(const IInspectable& s, const IVectorChangedEventArgs& e)
{
- if (auto tabList = s.try_as>())
+ if (auto tabList = s.try_as>())
{
auto idx = e.Index();
auto changedEvent = e.CollectionChange();
diff --git a/src/cascadia/TerminalApp/ITab.idl b/src/cascadia/TerminalApp/ITab.idl
new file mode 100644
index 00000000000..9087b20110e
--- /dev/null
+++ b/src/cascadia/TerminalApp/ITab.idl
@@ -0,0 +1,20 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT license.
+
+import "Command.idl";
+
+namespace TerminalApp
+{
+ interface ITab
+ {
+ String Title { get; };
+ Windows.UI.Xaml.Controls.IconSource IconSource { get; };
+ Command SwitchToTabCommand;
+ Microsoft.UI.Xaml.Controls.TabViewItem TabViewItem { get; };
+ Windows.UI.Xaml.FrameworkElement Content { get; };
+ Windows.UI.Xaml.FocusState FocusState { get; };
+
+ void Focus(Windows.UI.Xaml.FocusState focusState);
+ void Shutdown();
+ }
+}
diff --git a/src/cascadia/TerminalApp/SettingsTab.cpp b/src/cascadia/TerminalApp/SettingsTab.cpp
new file mode 100644
index 00000000000..864e0be7b81
--- /dev/null
+++ b/src/cascadia/TerminalApp/SettingsTab.cpp
@@ -0,0 +1,135 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT license.
+
+#include "pch.h"
+#include
+#include "SettingsTab.h"
+#include "SettingsTab.g.cpp"
+#include "Utils.h"
+#include "ActionAndArgs.h"
+#include "ActionArgs.h"
+
+using namespace winrt;
+using namespace winrt::Windows::UI::Xaml;
+using namespace winrt::Windows::UI::Core;
+using namespace winrt::Microsoft::Terminal::TerminalControl;
+using namespace winrt::Windows::System;
+
+namespace winrt
+{
+ namespace MUX = Microsoft::UI::Xaml;
+ namespace WUX = Windows::UI::Xaml;
+}
+
+namespace winrt::TerminalApp::implementation
+{
+ SettingsTab::SettingsTab()
+ {
+ Content(winrt::Microsoft::Terminal::Settings::Editor::MainPage());
+
+ _MakeTabViewItem();
+ _CreateContextMenu();
+ _CreateIcon();
+ }
+
+ // Method Description:
+ // - Initializes a TabViewItem for this Tab instance.
+ // Arguments:
+ // -
+ // Return Value:
+ // -
+ void SettingsTab::_MakeTabViewItem()
+ {
+ TabViewItem(::winrt::MUX::Controls::TabViewItem{});
+ TabViewItem().Header(winrt::box_value(Title()));
+ }
+
+ // Method Description:
+ // - Focus the settings UI
+ // Arguments:
+ // - focusState: The FocusState mode by which focus is to be obtained.
+ // Return Value:
+ // -
+ void SettingsTab::Focus(WUX::FocusState focusState)
+ {
+ _focusState = focusState;
+
+ if (_focusState != FocusState::Unfocused)
+ {
+ Content().Focus(focusState);
+ }
+ }
+
+ WUX::FocusState SettingsTab::FocusState() const noexcept
+ {
+ return _focusState;
+ }
+
+ // Method Description:
+ // - Set the icon on the TabViewItem for this tab.
+ // Arguments:
+ // -
+ // Return Value:
+ // -
+ winrt::fire_and_forget SettingsTab::_CreateIcon()
+ {
+ auto weakThis{ get_weak() };
+
+ co_await winrt::resume_foreground(TabViewItem().Dispatcher());
+
+ if (auto tab{ weakThis.get() })
+ {
+ auto fontFamily = winrt::WUX::Media::FontFamily(L"Segoe MDL2 Assets");
+ auto glyph = L"\xE713"; // This is the Setting icon (looks like a gear)
+
+ // The TabViewItem Icon needs MUX while the IconSourceElement in the CommandPalette needs WUX...
+ IconSource(GetFontIcon(fontFamily, 12, glyph));
+ TabViewItem().IconSource(GetFontIcon(fontFamily, 12, glyph));
+
+ // Update SwitchToTab command's icon
+ SwitchToTabCommand().IconSource(GetFontIcon(fontFamily, 12, glyph));
+ }
+ }
+
+ // Method Description:
+ // - Prepares this tab for being removed from the UI hierarchy
+ void SettingsTab::Shutdown()
+ {
+ // TODO: Does/Will the settings UI need some shutdown procedures?
+ 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 SettingsTab::_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;
+ Controls::MenuFlyoutSeparator menuSeparator;
+ newTabFlyout.Items().Append(closeTabMenuItem);
+ TabViewItem().ContextFlyout(newTabFlyout);
+ }
+}
diff --git a/src/cascadia/TerminalApp/SettingsTab.h b/src/cascadia/TerminalApp/SettingsTab.h
new file mode 100644
index 00000000000..678b6bd71ac
--- /dev/null
+++ b/src/cascadia/TerminalApp/SettingsTab.h
@@ -0,0 +1,51 @@
+/*++
+Copyright (c) Microsoft Corporation
+Licensed under the MIT license.
+
+Module Name:
+- SettingsTab.h
+
+Abstract:
+- The SettingsTab is a tab whose content is a Settings UI control. They can
+ coexist in a TabView with all other types of tabs, like the TerminalTab.
+ There should only be at most one SettingsTab open at any given time.
+
+Author(s):
+- Leon Liang - October 2020
+
+--*/
+
+#pragma once
+#include "SettingsTab.g.h"
+#include
+#include
+#include "../../cascadia/inc/cppwinrt_utils.h"
+
+namespace winrt::TerminalApp::implementation
+{
+ struct SettingsTab : SettingsTabT
+ {
+ public:
+ SettingsTab();
+
+ void Focus(winrt::Windows::UI::Xaml::FocusState focusState);
+ winrt::Windows::UI::Xaml::FocusState FocusState() const noexcept;
+
+ void Shutdown();
+
+ WINRT_CALLBACK(Closed, winrt::Windows::Foundation::EventHandler);
+
+ GETSET_PROPERTY(winrt::hstring, Title, L"Settings");
+ GETSET_PROPERTY(winrt::Windows::UI::Xaml::Controls::IconSource, IconSource, nullptr);
+ GETSET_PROPERTY(winrt::TerminalApp::Command, SwitchToTabCommand, nullptr);
+ GETSET_PROPERTY(winrt::Microsoft::UI::Xaml::Controls::TabViewItem, TabViewItem, nullptr);
+ GETSET_PROPERTY(winrt::Windows::UI::Xaml::Controls::Page, Content, nullptr);
+
+ private:
+ winrt::Windows::UI::Xaml::FocusState _focusState{ winrt::Windows::UI::Xaml::FocusState::Unfocused };
+
+ void _MakeTabViewItem();
+ void _CreateContextMenu();
+ winrt::fire_and_forget _CreateIcon();
+ };
+}
diff --git a/src/cascadia/TerminalApp/SettingsTab.idl b/src/cascadia/TerminalApp/SettingsTab.idl
new file mode 100644
index 00000000000..bea4f29ef65
--- /dev/null
+++ b/src/cascadia/TerminalApp/SettingsTab.idl
@@ -0,0 +1,11 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT license.
+
+import "ITab.idl";
+
+namespace TerminalApp
+{
+ [default_interface] runtimeclass SettingsTab : ITab
+ {
+ }
+}
diff --git a/src/cascadia/TerminalApp/Tab.idl b/src/cascadia/TerminalApp/Tab.idl
deleted file mode 100644
index ceb52a55f8f..00000000000
--- a/src/cascadia/TerminalApp/Tab.idl
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright (c) Microsoft Corporation.
-// Licensed under the MIT license.
-
-import "Command.idl";
-
-namespace TerminalApp
-{
- [default_interface] runtimeclass Tab : Windows.UI.Xaml.Data.INotifyPropertyChanged
- {
- String Title { get; };
- Windows.UI.Xaml.Controls.IconSource IconSource { get; };
- Command SwitchToTabCommand { get; };
- UInt32 TabViewIndex { get; };
- }
-}
diff --git a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj
index ade5c951528..0d65f30d1aa 100644
--- a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj
+++ b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj
@@ -76,6 +76,12 @@
MinMaxCloseControl.xaml
+
+ SettingsTab.idl
+
+
+ TerminalTab.idl
+
TerminalPage.xaml
Code
@@ -101,9 +107,6 @@
HasNestedCommandsVisibilityConverter.idl
-
- Tab.idl
-
ColorScheme.idl
@@ -167,6 +170,12 @@
MinMaxCloseControl.xaml
+
+ SettingsTab.idl
+
+
+ TerminalTab.idl
+
TerminalPage.xaml
Code
@@ -192,9 +201,6 @@
HasNestedCommandsVisibilityConverter.idl
-
- Tab.idl
-
ColorScheme.idl
@@ -268,6 +274,7 @@
App.xaml
+
@@ -277,6 +284,8 @@
MinMaxCloseControl.xaml
Code
+
+
TerminalPage.xaml
Code
@@ -300,7 +309,6 @@
-
@@ -425,4 +433,4 @@
-
+
\ No newline at end of file
diff --git a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj.filters b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj.filters
index e8949cbdfe6..1ee756f16ad 100644
--- a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj.filters
+++ b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj.filters
@@ -63,6 +63,13 @@
settings
+
+
+ tab
+
+
+ tab
+
@@ -123,6 +130,13 @@
settings
+
+
+ tab
+
+
+ tab
+
@@ -140,14 +154,10 @@
settings
-
- tab
-
commandPalette
-
settings
@@ -160,6 +170,19 @@
settings
+
+
+
+
+
+ tab
+
+
+ tab
+
+
+ tab
+
@@ -215,4 +238,4 @@
app
-
+
\ No newline at end of file
diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp
index 8dc7142a3f0..7e914ef33ac 100644
--- a/src/cascadia/TerminalApp/TerminalPage.cpp
+++ b/src/cascadia/TerminalApp/TerminalPage.cpp
@@ -20,6 +20,7 @@
#include "TabRowControl.h"
#include "ColorHelper.h"
#include "DebugTapConnection.h"
+#include "SettingsTab.h"
using namespace winrt;
using namespace winrt::Windows::Foundation::Collections;
@@ -45,7 +46,7 @@ namespace winrt
namespace winrt::TerminalApp::implementation
{
TerminalPage::TerminalPage() :
- _tabs{ winrt::single_threaded_observable_vector() },
+ _tabs{ winrt::single_threaded_observable_vector() },
_startupActions{ winrt::single_threaded_vector() }
{
InitializeComponent();
@@ -684,13 +685,12 @@ 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);
- // Give the tab its index in the _tabs vector so it can manage its own SwitchToTab command.
- newTabImpl->UpdateTabViewIndex(_tabs.Size() - 1);
-
// Hookup our event handlers to the new terminal
_RegisterTerminalEvents(term, *newTabImpl);
@@ -699,7 +699,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() };
@@ -708,24 +709,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);
@@ -948,12 +937,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);
}
@@ -964,7 +953,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())
@@ -1015,30 +1004,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();
}
}
@@ -1065,8 +1056,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();
_tabs.RemoveAt(tabIndex);
_tabView.TabItems().RemoveAt(tabIndex);
@@ -1109,8 +1100,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
@@ -1132,7 +1123,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
@@ -1168,7 +1159,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);
}
@@ -1178,7 +1169,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();
}
@@ -1237,8 +1228,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
@@ -1266,15 +1257,20 @@ 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();
- activeTab->ExitZoom();
- // Re-attach the tab's content to the UI tree.
- _tabContent.Children().Append(activeTab->GetRootElement());
+ 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();
+ activeTab->ExitZoom();
+ // Re-attach the tab's content to the UI tree.
+ _tabContent.Children().Append(activeTab->Content());
+ }
+ }
}
}
@@ -1290,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);
+ }
}
}
@@ -1300,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:
@@ -1329,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()
+ ITab TerminalPage::_GetFocusedTab()
{
if (auto index{ _GetFocusedTabIndex() })
{
- return _GetStrongTabImpl(*index);
+ return _tabs.GetAt(*index);
}
return nullptr;
}
@@ -1358,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());
}
}
@@ -1381,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();
+ }
}
}
@@ -1423,8 +1422,10 @@ namespace winrt::TerminalApp::implementation
{
if (auto index{ _GetFocusedTabIndex() })
{
- auto focusedTab{ _GetStrongTabImpl(*index) };
- focusedTab->Scroll(delta);
+ if (auto terminalTab = _GetTerminalTabImpl(_tabs.GetAt(*index)))
+ {
+ terminalTab->Scroll(delta);
+ }
}
}
@@ -1457,9 +1458,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;
@@ -1533,9 +1541,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);
+ }
}
}
@@ -1556,11 +1566,13 @@ namespace winrt::TerminalApp::implementation
return;
}
- delta = std::clamp(delta, -1, 1);
- const auto control = _GetActiveControl();
- const auto termHeight = control.GetViewHeight();
- auto focusedTab{ _GetStrongTabImpl(*indexOpt) };
- focusedTab->Scroll(termHeight * delta);
+ if (auto terminalTab = _GetTerminalTabImpl(_tabs.GetAt(*indexOpt)))
+ {
+ delta = std::clamp(delta, -1, 1);
+ const auto control = _GetActiveControl();
+ const auto termHeight = control.GetViewHeight();
+ terminalTab->Scroll(termHeight * delta);
+ }
}
// Method Description:
@@ -1671,8 +1683,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;
@@ -1870,32 +1884,39 @@ namespace winrt::TerminalApp::implementation
// a background thread, as to not hang/crash the UI thread.
fire_and_forget TerminalPage::_LaunchSettings(const SettingsTarget target)
{
- // This will switch the execution of the function to a background (not
- // UI) thread. This is IMPORTANT, because the Windows.Storage API's
- // (used for retrieving the path to the file) will crash on the UI
- // thread, because the main thread is a STA.
- co_await winrt::resume_background();
+ if (target == SettingsTarget::SettingsUI)
+ {
+ _OpenSettingsUI();
+ }
+ else
+ {
+ // This will switch the execution of the function to a background (not
+ // UI) thread. This is IMPORTANT, because the Windows.Storage API's
+ // (used for retrieving the path to the file) will crash on the UI
+ // thread, because the main thread is a STA.
+ co_await winrt::resume_background();
+
+ auto openFile = [](const auto& filePath) {
+ HINSTANCE res = ShellExecute(nullptr, nullptr, filePath.c_str(), nullptr, nullptr, SW_SHOW);
+ if (static_cast(reinterpret_cast(res)) <= 32)
+ {
+ ShellExecute(nullptr, nullptr, L"notepad", filePath.c_str(), nullptr, SW_SHOW);
+ }
+ };
- auto openFile = [](const auto& filePath) {
- HINSTANCE res = ShellExecute(nullptr, nullptr, filePath.c_str(), nullptr, nullptr, SW_SHOW);
- if (static_cast(reinterpret_cast(res)) <= 32)
+ switch (target)
{
- ShellExecute(nullptr, nullptr, L"notepad", filePath.c_str(), nullptr, SW_SHOW);
+ case SettingsTarget::DefaultsFile:
+ openFile(CascadiaSettings::GetDefaultSettingsPath());
+ break;
+ case SettingsTarget::SettingsFile:
+ openFile(CascadiaSettings::GetSettingsPath());
+ break;
+ case SettingsTarget::AllFiles:
+ openFile(CascadiaSettings::GetDefaultSettingsPath());
+ openFile(CascadiaSettings::GetSettingsPath());
+ break;
}
- };
-
- switch (target)
- {
- case SettingsTarget::DefaultsFile:
- openFile(CascadiaSettings::GetDefaultSettingsPath());
- break;
- case SettingsTarget::SettingsFile:
- openFile(CascadiaSettings::GetSettingsPath());
- break;
- case SettingsTarget::AllFiles:
- openFile(CascadiaSettings::GetDefaultSettingsPath());
- openFile(CascadiaSettings::GetSettingsPath());
- break;
}
}
@@ -1954,23 +1975,22 @@ 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->GetRootElement());
+ _tabContent.Children().Append(tab.Content());
- tab->SetFocused(true);
+ tab.Focus(FocusState::Programmatic);
// Raise an event that our title changed
- _titleChangeHandlers(*this, tab->GetActiveTitle());
+ _titleChangeHandlers(*this, tab.Title());
}
CATCH_LOG();
}
@@ -2005,8 +2025,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);
+ }
}
}
@@ -2063,9 +2085,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();
@@ -2077,11 +2100,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() };
@@ -2270,32 +2299,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:
@@ -2497,7 +2500,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);
}
}
@@ -2534,10 +2537,128 @@ namespace winrt::TerminalApp::implementation
{
for (uint32_t i = 0; i < _tabs.Size(); ++i)
{
- _GetStrongTabImpl(i)->UpdateTabViewIndex(i);
+ auto command = _tabs.GetAt(i).SwitchToTabCommand();
+ command.Action().Args().as()->TabIndex(i);
+ }
+ }
+
+ // Method Description:
+ // - Creates a settings UI tab and focuses it. If there's already a settings UI tab open,
+ // just focus the existing one.
+ // Arguments:
+ // -
+ // Return Value:
+ // -
+ void TerminalPage::_OpenSettingsUI()
+ {
+ // If we're holding the settings tab's switch command, don't create a new one, switch to the existing one.
+ if (!_switchToSettingsCommand)
+ {
+ auto newTabImpl = winrt::make_self();
+ _MakeSwitchToTabCommand(*newTabImpl, _tabs.Size());
+
+ // Add the new tab to the list of our tabs.
+ _tabs.Append(*newTabImpl);
+
+ // Don't capture a strong ref to the tab. If the tab is removed as this
+ // is called, we don't really care anymore about handling the event.
+ auto weakTab = make_weak(newTabImpl);
+
+ auto tabViewItem = newTabImpl->TabViewItem();
+ _tabView.TabItems().Append(tabViewItem);
+
+ _ReapplyCompactTabSize();
+
+ tabViewItem.PointerPressed({ this, &TerminalPage::_OnTabClick });
+
+ // When the tab is closed, remove it from our list of tabs.
+ newTabImpl->Closed([tabViewItem, weakThis{ get_weak() }](auto&& /*s*/, auto&& /*e*/) {
+ if (auto page{ weakThis.get() })
+ {
+ page->_switchToSettingsCommand = nullptr;
+ page->_RemoveOnCloseRoutine(tabViewItem, page);
+ }
+ });
+
+ _switchToSettingsCommand = newTabImpl->SwitchToTabCommand();
+
+ // This kicks off TabView::SelectionChanged, in response to which
+ // we'll attach the terminal's Xaml control to the Xaml root.
+ _tabView.SelectedItem(tabViewItem);
+ }
+ else
+ {
+ _actionDispatch->DoAction(_switchToSettingsCommand.Action());
+ }
+ }
+
+ // 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 ITab& 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 ITab& tab, const uint32_t index)
+ {
+ auto focusTabAction = winrt::make_self();
+ auto args = winrt::make_self();
+ args->TabIndex(index);
+
+ focusTabAction->Action(ShortcutAction::SwitchToTab);
+ focusTabAction->Args(*args);
+
+ winrt::TerminalApp::Command command;
+ command.Action(*focusTabAction);
+ command.Name(tab.Title());
+ command.IconSource(tab.IconSource());
+
+ tab.SwitchToTabCommand(command);
+ }
+
// -------------------------------- WinRT Events ---------------------------------
// Winrt events need a method for adding a callback to the event and removing the callback.
// These macros will define them both for you.
diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h
index 540921c0c23..5a7d69dc87c 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 "CascadiaSettings.h"
#include "Profile.h"
#include "AppKeyBindings.h"
@@ -90,11 +90,12 @@ namespace winrt::TerminalApp::implementation
TerminalApp::CascadiaSettings _settings{ nullptr };
- Windows::Foundation::Collections::IObservableVector _tabs;
- winrt::com_ptr _GetStrongTabImpl(const uint32_t index) const;
- winrt::com_ptr _GetStrongTabImpl(const ::winrt::TerminalApp::Tab& tab) const;
+ Windows::Foundation::Collections::IObservableVector _tabs;
+ winrt::com_ptr _GetTerminalTabImpl(const ITab& tab) const;
void _UpdateTabIndices();
+ winrt::TerminalApp::Command _switchToSettingsCommand{ nullptr };
+
bool _isInFocusMode{ false };
bool _isFullscreen{ false };
bool _isAlwaysOnTop{ false };
@@ -135,8 +136,8 @@ namespace winrt::TerminalApp::implementation
void _HookupKeyBindings(const TerminalApp::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();
@@ -148,7 +149,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);
@@ -156,7 +157,7 @@ namespace winrt::TerminalApp::implementation
winrt::Microsoft::Terminal::TerminalControl::TermControl _GetActiveControl();
std::optional _GetFocusedTabIndex() const noexcept;
- winrt::com_ptr _GetFocusedTab();
+ ITab _GetFocusedTab();
winrt::fire_and_forget _SetFocusedTabIndex(const uint32_t tabIndex);
void _CloseFocusedTab();
void _CloseFocusedPane();
@@ -207,6 +208,12 @@ namespace winrt::TerminalApp::implementation
void _UnZoomIfNeeded();
+ void _OpenSettingsUI();
+
+ void _ReapplyCompactTabSize();
+
+ void _MakeSwitchToTabCommand(const ITab& tab, const uint32_t index);
+
#pragma region ActionHandlers
// These are all defined in AppActionHandlers.cpp
void _HandleOpenNewTabDropdown(const IInspectable& sender, const TerminalApp::ActionEventArgs& args);
diff --git a/src/cascadia/TerminalApp/TerminalSettingsSerializationHelpers.h b/src/cascadia/TerminalApp/TerminalSettingsSerializationHelpers.h
index 3a3eb4f5ffb..5992ff560b8 100644
--- a/src/cascadia/TerminalApp/TerminalSettingsSerializationHelpers.h
+++ b/src/cascadia/TerminalApp/TerminalSettingsSerializationHelpers.h
@@ -320,10 +320,11 @@ JSON_ENUM_MAPPER(::winrt::TerminalApp::SplitType)
JSON_ENUM_MAPPER(::winrt::TerminalApp::SettingsTarget)
{
- JSON_MAPPINGS(3) = {
+ JSON_MAPPINGS(4) = {
pair_type{ "settingsFile", ValueType::SettingsFile },
pair_type{ "defaultsFile", ValueType::DefaultsFile },
pair_type{ "allFiles", ValueType::AllFiles },
+ pair_type{ "settingsUI", ValueType::SettingsUI },
};
};
diff --git a/src/cascadia/TerminalApp/Tab.cpp b/src/cascadia/TerminalApp/TerminalTab.cpp
similarity index 78%
rename from src/cascadia/TerminalApp/Tab.cpp
rename to src/cascadia/TerminalApp/TerminalTab.cpp
index f7d33efd754..903d17edb28 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"
#include "ActionAndArgs.h"
@@ -25,7 +25,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
_activePane = _rootPane;
_MakeTabViewItem();
- _MakeSwitchToTabCommand();
}
// Method Description:
@@ -45,11 +44,11 @@ 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->_inRename = true;
@@ -57,7 +56,7 @@ namespace winrt::TerminalApp::implementation
}
});
- _UpdateTitle();
+ UpdateTitle();
_RecalculateAndApplyTabColor();
}
@@ -67,7 +66,7 @@ namespace winrt::TerminalApp::implementation
// -
// Return Value:
// - The UIElement acting as root of the Tab's root pane.
- UIElement Tab::GetRootElement()
+ FrameworkElement TerminalTab::Content()
{
if (_zoomedPane)
{
@@ -90,22 +89,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 and to create the context menu of
@@ -114,40 +102,44 @@ 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);
_CreateContextMenu();
}
// Method Description:
- // - Returns true if this is the currently focused tab. For any set of tabs,
+ // - Returns the focus state of this Tab. Unfocused means this tab is not focused,
+ // and any other FocusState means that this tab is focused. 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
+ // - A FocusState enum value
+ WUX::FocusState TerminalTab::FocusState() const noexcept
{
- return _focused;
+ return _focusState;
}
// 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);
+ }
}
}
@@ -160,7 +152,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();
}
@@ -172,7 +164,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);
@@ -185,35 +177,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)
@@ -225,13 +200,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(GetColoredIcon(_lastIconPath));
- _tabViewItem.IconSource(GetColoredIcon(_lastIconPath));
+ TabViewItem().IconSource(GetColoredIcon(_lastIconPath));
// Update SwitchToTab command's icon
SwitchToTabCommand().IconSource(IconSource());
@@ -245,7 +220,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())
{
@@ -263,14 +238,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());
@@ -288,7 +263,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();
@@ -304,7 +279,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(winrt::TerminalApp::SplitState splitType)
+ bool TerminalTab::CanSplitPane(winrt::TerminalApp::SplitState splitType)
{
return _activePane->CanSplit(splitType);
}
@@ -318,7 +293,7 @@ namespace winrt::TerminalApp::implementation
// - control: A TermControl to use in the new pane.
// Return Value:
// -
- void Tab::SplitPane(winrt::TerminalApp::SplitState splitType, const GUID& profile, TermControl& control)
+ void TerminalTab::SplitPane(winrt::TerminalApp::SplitState splitType, const GUID& profile, TermControl& control)
{
auto [first, second] = _activePane->Split(splitType, profile, control);
_activePane = first;
@@ -338,7 +313,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);
}
@@ -350,7 +325,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.
@@ -364,7 +339,7 @@ namespace winrt::TerminalApp::implementation
// - direction: The direction to move the separator in.
// Return Value:
// -
- void Tab::ResizePane(const winrt::TerminalApp::Direction& direction)
+ void TerminalTab::ResizePane(const winrt::TerminalApp::Direction& direction)
{
// NOTE: This _must_ be called on the root pane, so that it can propagate
// throughout the entire tree.
@@ -378,7 +353,7 @@ namespace winrt::TerminalApp::implementation
// - direction: The direction to move the focus in.
// Return Value:
// -
- void Tab::NavigateFocus(const winrt::TerminalApp::Direction& direction)
+ void TerminalTab::NavigateFocus(const winrt::TerminalApp::Direction& direction)
{
// NOTE: This _must_ be called on the root pane, so that it can propagate
// throughout the entire tree.
@@ -387,7 +362,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();
}
@@ -400,21 +375,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:
@@ -427,7 +402,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() };
@@ -437,7 +412,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();
}
});
@@ -474,7 +449,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();
@@ -482,7 +457,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();
@@ -497,7 +472,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() };
@@ -521,7 +496,7 @@ namespace winrt::TerminalApp::implementation
// -
// Return Value:
// -
- void Tab::_CreateContextMenu()
+ void TerminalTab::_CreateContextMenu()
{
auto weakThis{ get_weak() };
@@ -595,7 +570,7 @@ namespace winrt::TerminalApp::implementation
newTabFlyout.Items().Append(renameTabMenuItem);
newTabFlyout.Items().Append(menuSeparator);
newTabFlyout.Items().Append(closeTabMenuItem);
- _tabViewItem.ContextFlyout(newTabFlyout);
+ TabViewItem().ContextFlyout(newTabFlyout);
}
// Method Description:
@@ -610,9 +585,9 @@ namespace winrt::TerminalApp::implementation
// -
// Return Value:
// -
- void Tab::_UpdateTabHeader()
+ void TerminalTab::_UpdateTabHeader()
{
- winrt::hstring tabText{ GetActiveTitle() };
+ winrt::hstring tabText{ Title() };
if (!_inRename)
{
@@ -630,13 +605,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
@@ -653,9 +628,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;
}
@@ -703,7 +678,7 @@ namespace winrt::TerminalApp::implementation
{
tab->_runtimeTabText = textBox.Text();
tab->_inRename = false;
- tab->_UpdateTitle();
+ tab->UpdateTitle();
}
});
@@ -725,7 +700,7 @@ namespace winrt::TerminalApp::implementation
e.Handled(true);
textBox.Text(tab->_runtimeTabText);
tab->_inRename = false;
- tab->_UpdateTitle();
+ tab->UpdateTitle();
break;
}
}
@@ -735,7 +710,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();
@@ -745,7 +720,7 @@ namespace winrt::TerminalApp::implementation
_tabRenameBoxLayoutUpdatedRevoker.revoke();
});
- _tabViewItem.Header(tabTextBox);
+ TabViewItem().Header(tabTextBox);
}
// Method Description:
@@ -754,7 +729,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;
@@ -791,7 +766,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();
@@ -806,11 +781,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;
@@ -838,7 +813,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{};
@@ -867,15 +842,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();
@@ -890,7 +865,7 @@ namespace winrt::TerminalApp::implementation
// -
// Return Value:
// -
- void Tab::ResetRuntimeTabColor()
+ void TerminalTab::ResetRuntimeTabColor()
{
_runtimeTabColor.reset();
_RecalculateAndApplyTabColor();
@@ -903,7 +878,7 @@ namespace winrt::TerminalApp::implementation
// -
// Return Value:
// -
- void Tab::_ClearTabBackgroundColor()
+ void TerminalTab::_ClearTabBackgroundColor()
{
winrt::hstring keys[] = {
L"TabViewItemHeaderBackground",
@@ -921,9 +896,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);
}
}
@@ -937,9 +912,9 @@ namespace winrt::TerminalApp::implementation
// -
// Return Value:
// -
- void Tab::ActivateColorPicker()
+ void TerminalTab::ActivateColorPicker()
{
- _tabColorPickup.ShowAt(_tabViewItem);
+ _tabColorPickup.ShowAt(TabViewItem());
}
// Method Description:
@@ -949,17 +924,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);
}
}
@@ -969,7 +944,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();
}
@@ -984,12 +959,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);
}
@@ -998,13 +973,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)
{
@@ -1015,14 +990,14 @@ namespace winrt::TerminalApp::implementation
EnterZoom();
}
}
- void Tab::EnterZoom()
+ void TerminalTab::EnterZoom()
{
_zoomedPane = _activePane;
_rootPane->Maximize(_zoomedPane);
// Update the tab header to show the magnifying glass
_UpdateTabHeader();
}
- void Tab::ExitZoom()
+ void TerminalTab::ExitZoom()
{
_rootPane->Restore(_zoomedPane);
_zoomedPane = nullptr;
@@ -1030,41 +1005,12 @@ namespace winrt::TerminalApp::implementation
_UpdateTabHeader();
}
- 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()
- {
- auto focusTabAction = winrt::make_self();
- auto args = winrt::make_self();
- args->TabIndex(_TabViewIndex);
-
- focusTabAction->Action(ShortcutAction::SwitchToTab);
- focusTabAction->Args(*args);
-
- winrt::TerminalApp::Command command;
- command.Action(*focusTabAction);
- command.Name(Title());
- command.IconSource(IconSource());
-
- SwitchToTabCommand(command);
- }
-
- void Tab::UpdateTabViewIndex(const uint32_t idx)
- {
- TabViewIndex(idx);
- SwitchToTabCommand().Action().Args().as()->TabIndex(idx);
- }
-
- 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 81%
rename from src/cascadia/TerminalApp/Tab.h
rename to src/cascadia/TerminalApp/TerminalTab.h
index 3b52488f5d0..e63c5d0e15b 100644
--- a/src/cascadia/TerminalApp/Tab.h
+++ b/src/cascadia/TerminalApp/TerminalTab.h
@@ -4,7 +4,7 @@
#pragma once
#include "Pane.h"
#include "ColorPickupFlyout.h"
-#include "Tab.g.h"
+#include "TerminalTab.g.h"
// fwdecl unittest classes
namespace TerminalAppLocalTests
@@ -14,22 +14,21 @@ 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() = delete;
+ 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::Windows::UI::Xaml::UIElement GetRootElement();
+ winrt::Windows::UI::Xaml::FrameworkElement Content();
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);
+ winrt::Windows::UI::Xaml::FocusState FocusState() const noexcept;
winrt::fire_and_forget Scroll(const int delta);
@@ -47,7 +46,7 @@ namespace winrt::TerminalApp::implementation
void NavigateFocus(const winrt::TerminalApp::Direction& direction);
void UpdateSettings(const winrt::TerminalApp::TerminalSettings& settings, const GUID& profile);
- winrt::hstring GetActiveTitle() const;
+ winrt::fire_and_forget UpdateTitle();
void Shutdown();
void ClosePane();
@@ -68,8 +67,6 @@ namespace winrt::TerminalApp::implementation
int GetLeafPaneCount() const noexcept;
- void UpdateTabViewIndex(const uint32_t idx);
-
WINRT_CALLBACK(Closed, winrt::Windows::Foundation::EventHandler);
WINRT_CALLBACK(PropertyChanged, Windows::UI::Xaml::Data::PropertyChangedEventHandler);
DECLARE_EVENT(ActivePaneChanged, _ActivePaneChangedHandlers, winrt::delegate<>);
@@ -80,9 +77,7 @@ namespace winrt::TerminalApp::implementation
OBSERVABLE_GETSET_PROPERTY(winrt::Windows::UI::Xaml::Controls::IconSource, IconSource, _PropertyChangedHandlers, nullptr);
OBSERVABLE_GETSET_PROPERTY(winrt::TerminalApp::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);
+ GETSET_PROPERTY(winrt::Microsoft::UI::Xaml::Controls::TabViewItem, TabViewItem, nullptr);
private:
std::shared_ptr _rootPane{ nullptr };
@@ -93,7 +88,7 @@ namespace winrt::TerminalApp::implementation
std::optional _themeTabColor{};
std::optional _runtimeTabColor{};
- bool _focused{ false };
+ winrt::Windows::UI::Xaml::FocusState _focusState{ winrt::Windows::UI::Xaml::FocusState::Unfocused };
winrt::Microsoft::UI::Xaml::Controls::TabViewItem _tabViewItem{ nullptr };
winrt::hstring _runtimeTabText{};
@@ -113,16 +108,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();
-
friend class ::TerminalAppLocalTests::TabTests;
};
}
diff --git a/src/cascadia/TerminalApp/TerminalTab.idl b/src/cascadia/TerminalApp/TerminalTab.idl
new file mode 100644
index 00000000000..a721365683f
--- /dev/null
+++ b/src/cascadia/TerminalApp/TerminalTab.idl
@@ -0,0 +1,11 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT license.
+
+import "ITab.idl";
+
+namespace TerminalApp
+{
+ [default_interface] runtimeclass TerminalTab : ITab
+ {
+ }
+}
diff --git a/src/cascadia/TerminalApp/Utils.h b/src/cascadia/TerminalApp/Utils.h
index 91915804139..64d560af2d7 100644
--- a/src/cascadia/TerminalApp/Utils.h
+++ b/src/cascadia/TerminalApp/Utils.h
@@ -82,6 +82,23 @@ namespace Microsoft::TerminalApp::details
{
using type = winrt::Windows::UI::Xaml::Controls::BitmapIconSource;
};
+
+ template
+ struct FontIconSource
+ {
+ };
+
+ template<>
+ struct FontIconSource
+ {
+ using type = winrt::Microsoft::UI::Xaml::Controls::FontIconSource;
+ };
+
+ template<>
+ struct FontIconSource
+ {
+ using type = winrt::Windows::UI::Xaml::Controls::FontIconSource;
+ };
}
// Method Description:
@@ -116,6 +133,28 @@ TIconSource GetColoredIcon(const winrt::hstring& path)
return nullptr;
}
+// TODO: GH#1564 SUI polish - Dedupe with Command's icon handler
+template
+TIconSource GetFontIcon(const winrt::Windows::UI::Xaml::Media::FontFamily& fontFamily,
+ const double fontSize,
+ const winrt::hstring glyph)
+{
+ if (!glyph.empty())
+ {
+ try
+ {
+ ::Microsoft::TerminalApp::details::FontIconSource::type iconSource;
+ iconSource.FontFamily(fontFamily);
+ iconSource.FontSize(fontSize);
+ iconSource.Glyph(glyph);
+ return iconSource;
+ }
+ CATCH_LOG();
+ }
+
+ return nullptr;
+}
+
std::wstring VisualizeControlCodes(std::wstring str) noexcept;
inline std::wstring VisualizeControlCodes(std::wstring_view str) noexcept
diff --git a/src/cascadia/TerminalApp/dll/TerminalApp.vcxproj b/src/cascadia/TerminalApp/dll/TerminalApp.vcxproj
index 6cf1ed00dee..32ebc347566 100644
--- a/src/cascadia/TerminalApp/dll/TerminalApp.vcxproj
+++ b/src/cascadia/TerminalApp/dll/TerminalApp.vcxproj
@@ -36,7 +36,8 @@
-
+
+
diff --git a/src/cascadia/TerminalSettingsEditor/MainPage.xaml b/src/cascadia/TerminalSettingsEditor/MainPage.xaml
index 120028331da..bb9ad8d6ea3 100644
--- a/src/cascadia/TerminalSettingsEditor/MainPage.xaml
+++ b/src/cascadia/TerminalSettingsEditor/MainPage.xaml
@@ -1,6 +1,6 @@
-
Margin="20">
-
+
diff --git a/src/cascadia/inc/cppwinrt_utils.h b/src/cascadia/inc/cppwinrt_utils.h
index 72f0526d3ca..32e1783efc8 100644
--- a/src/cascadia/inc/cppwinrt_utils.h
+++ b/src/cascadia/inc/cppwinrt_utils.h
@@ -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) \