Repository: microsoft/terminal (language: cpp)

Issue:
Polish `TabBase`
`TerminalPage::_RefreshUIForSettingsReload()` has a section where it iterates through each tab and updates the settings in one way or another. This can be slightly refactored by...
- add `UpdateSettings` to `TabBase`
- have the for-loop just call `UpdateSettings` on each one
- unify creating a new terminal tab and settings tab

Additional refactor work items can/will be added here as they come up.


Discussion / hints:

Fix patch:
diff --git a/src/cascadia/TerminalApp/AppActionHandlers.cpp b/src/cascadia/TerminalApp/AppActionHandlers.cpp
index 662ffc25746..22a0582dfda 100644
--- a/src/cascadia/TerminalApp/AppActionHandlers.cpp
+++ b/src/cascadia/TerminalApp/AppActionHandlers.cpp
@@ -1418,7 +1418,7 @@ namespace winrt::TerminalApp::implementation
         args.Handled(true);
     }
 
-    void TerminalPage::_HandleOpenScratchpad(const IInspectable& /*sender*/,
+    void TerminalPage::_HandleOpenScratchpad(const IInspectable& sender,
                                              const ActionEventArgs& args)
     {
         if (Feature_ScratchpadPane::IsEnabled())
@@ -1431,7 +1431,7 @@ namespace winrt::TerminalApp::implementation
             scratchPane->GetRoot().KeyDown({ this, &TerminalPage::_KeyDownHandler });
 
             const auto resultPane = std::make_shared<Pane>(*scratchPane);
-            _SplitPane(_GetFocusedTabImpl(), SplitDirection::Automatic, 0.5f, resultPane);
+            _SplitPane(_senderOrFocusedTab(sender), SplitDirection::Automatic, 0.5f, resultPane);
             args.Handled(true);
         }
     }
diff --git a/src/cascadia/TerminalApp/IPaneContent.idl b/src/cascadia/TerminalApp/IPaneContent.idl
index a26d1a05477..fa258fdbd26 100644
--- a/src/cascadia/TerminalApp/IPaneContent.idl
+++ b/src/cascadia/TerminalApp/IPaneContent.idl
@@ -19,6 +19,7 @@ namespace TerminalApp
     interface IPaneContent
     {
         Windows.UI.Xaml.FrameworkElement GetRoot();
+        void UpdateSettings(Microsoft.Terminal.Settings.Model.CascadiaSettings settings);
 
         Windows.Foundation.Size MinimumSize { get; };
 
@@ -26,6 +27,9 @@ namespace TerminalApp
         UInt64 TaskbarState { get; };
         UInt64 TaskbarProgress { get; };
         Boolean ReadOnly { get; };
+        String Icon { get; };
+        Windows.Foundation.IReference<Windows.UI.Color> TabColor { get; };
+        Windows.UI.Xaml.Media.Brush BackgroundBrush { get; };
 
         Microsoft.Terminal.Settings.Model.NewTerminalArgs GetNewTerminalArgs(BuildStartupKind kind);
 
diff --git a/src/cascadia/TerminalApp/Pane.cpp b/src/cascadia/TerminalApp/Pane.cpp
index 45950e2c776..8d159506c15 100644
--- a/src/cascadia/TerminalApp/Pane.cpp
+++ b/src/cascadia/TerminalApp/Pane.cpp
@@ -1075,6 +1075,29 @@ TermControl Pane::GetLastFocusedTerminalControl()
     return GetTerminalControl();
 }
 
+IPaneContent Pane::GetLastFocusedContent()
+{
+    if (!_IsLeaf())
+    {
+        if (_lastActive)
+        {
+            auto pane = shared_from_this();
+            while (const auto p = pane->_parentChildPath.lock())
+            {
+                if (p->_IsLeaf())
+                {
+                    return p->_content;
+                }
+                pane = p;
+            }
+            // We didn't find our child somehow, they might have closed under us.
+        }
+        return _firstChild->GetLastFocusedContent();
+    }
+
+    return _content;
+}
+
 // Method Description:
 // - Gets the TermControl of this pane. If this Pane is not a leaf this will
 //   return the nullptr;
@@ -1215,7 +1238,10 @@ void Pane::UpdateVisuals()
 void Pane::_Focus()
 {
     GotFocus.raise(shared_from_this(), FocusState::Programmatic);
-    _content.Focus(FocusState::Programmatic);
+    if (const auto& lastContent{ GetLastFocusedContent() })
+    {
+        lastContent.Focus(FocusState::Programmatic);
+    }
 }
 
 // Method Description:
@@ -1254,20 +1280,21 @@ void Pane::_FocusFirstChild()
     }
 }
 
-// Method Description:
-// - Updates the settings of this pane, presuming that it is a leaf.
-// Arguments:
-// - settings: The new TerminalSettings to apply to any matching controls
-// - profile: The profile from which these settings originated.
-// Return Value:
-// - <none>
-void Pane::UpdateSettings(const TerminalSettingsCreateResult& settings, const Profile& profile)
+void Pane::UpdateSettings(const CascadiaSettings& settings, const winrt::TerminalApp::TerminalSettingsCache& cache)
 {
-    assert(_IsLeaf());
-
-    if (const auto& terminalPane{ _getTerminalContent() })
+    if (_content)
     {
-        return terminalPane.UpdateSettings(settings, profile);
+        // We need to do a bit more work here for terminal
+        // panes. They need to know about the profile that was used for
+        // them, and about the focused/unfocused settings.
+        if (const auto& terminalPaneContent{ _content.try_as<TerminalPaneContent>() })
+        {
+            terminalPaneContent.UpdateTerminalSettings(cache);
+        }
+        else
+        {
+            _content.UpdateSettings(settings);
+        }
     }
 }
 
@@ -1893,7 +1920,7 @@ void Pane::_SetupEntranceAnimation()
         auto child = isFirstChild ? _firstChild : _secondChild;
         auto childGrid = child->_root;
         // If we are splitting a parent pane this may be null
-        auto control = child->_content.GetRoot();
+        auto control = child->_content ? child->_content.GetRoot() : nullptr;
         // Build up our animation:
         // * it'll take as long as our duration (200ms)
         // * it'll change the value of our property from 0 to secondSize
diff --git a/src/cascadia/TerminalApp/Pane.h b/src/cascadia/TerminalApp/Pane.h
index 0f4a5c907dc..f93798b91c2 100644
--- a/src/cascadia/TerminalApp/Pane.h
+++ b/src/cascadia/TerminalApp/Pane.h
@@ -73,6 +73,7 @@ class Pane : public std::enable_shared_from_this<Pane>
 
     std::shared_ptr<Pane> GetActivePane();
     winrt::Microsoft::Terminal::Control::TermControl GetLastFocusedTerminalControl();
+    winrt::TerminalApp::IPaneContent GetLastFocusedContent();
     winrt::Microsoft::Terminal::Control::TermControl GetTerminalControl() const;
     winrt::Microsoft::Terminal::Settings::Model::Profile GetFocusedProfile();
     bool IsConnectionClosed() const;
@@ -107,8 +108,7 @@ class Pane : public std::enable_shared_from_this<Pane>
     BuildStartupState BuildStartupActions(uint32_t currentId, uint32_t nextId, winrt::TerminalApp::BuildStartupKind kind);
     winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs GetTerminalArgsForPane(winrt::TerminalApp::BuildStartupKind kind) const;
 
-    void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult& settings,
-                        const winrt::Microsoft::Terminal::Settings::Model::Profile& profile);
+    void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings& settings, const winrt::TerminalApp::TerminalSettingsCache& cache);
     bool ResizePane(const winrt::Microsoft::Terminal::Settings::Model::ResizeDirection& direction);
     std::shared_ptr<Pane> NavigateDirection(const std::shared_ptr<Pane> sourcePane,
                                             const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction,
diff --git a/src/cascadia/TerminalApp/ScratchpadContent.cpp b/src/cascadia/TerminalApp/ScratchpadContent.cpp
index 0ed99203391..0f39daa1bee 100644
--- a/src/cascadia/TerminalApp/ScratchpadContent.cpp
+++ b/src/cascadia/TerminalApp/ScratchpadContent.cpp
@@ -26,6 +26,11 @@ namespace winrt::TerminalApp::implementation
         _root.Children().Append(_box);
     }
 
+    void ScratchpadContent::UpdateSettings(const CascadiaSettings& /*settings*/)
+    {
+        // Nothing to do.
+    }
+
     winrt::Windows::UI::Xaml::FrameworkElement ScratchpadContent::GetRoot()
     {
         return _root;
@@ -47,4 +52,15 @@ namespace winrt::TerminalApp::implementation
     {
         return nullptr;
     }
+
+    winrt::hstring ScratchpadContent::Icon() const
+    {
+        static constexpr std::wstring_view glyph{ L"\xe70b" }; // QuickNote
+        return winrt::hstring{ glyph };
+    }
+
+    winrt::Windows::UI::Xaml::Media::Brush ScratchpadContent::BackgroundBrush()
+    {
+        return _root.Background();
+    }
 }
diff --git a/src/cascadia/TerminalApp/ScratchpadContent.h b/src/cascadia/TerminalApp/ScratchpadContent.h
index a2b76e47934..cd01414e677 100644
--- a/src/cascadia/TerminalApp/ScratchpadContent.h
+++ b/src/cascadia/TerminalApp/ScratchpadContent.h
@@ -13,6 +13,8 @@ namespace winrt::TerminalApp::implementation
 
         winrt::Windows::UI::Xaml::FrameworkElement GetRoot();
 
+        void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings& settings);
+
         winrt::Windows::Foundation::Size MinimumSize();
 
         void Focus(winrt::Windows::UI::Xaml::FocusState reason = winrt::Windows::UI::Xaml::FocusState::Programmatic);
@@ -23,6 +25,9 @@ namespace winrt::TerminalApp::implementation
         uint64_t TaskbarState() { return 0; }
         uint64_t TaskbarProgress() { return 0; }
         bool ReadOnly() { return false; }
+        winrt::hstring Icon() const;
+        Windows::Foundation::IReference<winrt::Windows::UI::Color> TabColor() const noexcept { return nullptr; }
+        winrt::Windows::UI::Xaml::Media::Brush BackgroundBrush();
 
         til::typed_event<> ConnectionStateChanged;
         til::typed_event<IPaneContent> CloseRequested;
diff --git a/src/cascadia/TerminalApp/SettingsPaneContent.cpp b/src/cascadia/TerminalApp/SettingsPaneContent.cpp
new file mode 100644
index 00000000000..3e89c38296b
--- /dev/null
+++ b/src/cascadia/TerminalApp/SettingsPaneContent.cpp
@@ -0,0 +1,84 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT license.
+
+#include "pch.h"
+#include "SettingsPaneContent.h"
+#include "Utils.h"
+
+using namespace winrt::Windows::Foundation;
+using namespace winrt::Windows::UI::Xaml;
+using namespace winrt::Microsoft::Terminal::Settings::Model;
+
+#define ASSERT_UI_THREAD() assert(_sui.Dispatcher().HasThreadAccess())
+
+namespace winrt::TerminalApp::implementation
+{
+    SettingsPaneContent::SettingsPaneContent(CascadiaSettings settings)
+    {
+        _sui = winrt::Microsoft::Terminal::Settings::Editor::MainPage{ settings };
+
+        // Stash away the current requested theme of the app. We'll need that in
+        // _BackgroundBrush() to do a theme-aware resource lookup
+        _requestedTheme = settings.GlobalSettings().CurrentTheme().RequestedTheme();
+    }
+
+    void SettingsPaneContent::UpdateSettings(const CascadiaSettings& settings)
+    {
+        ASSERT_UI_THREAD();
+        _sui.UpdateSettings(settings);
+
+        _requestedTheme = settings.GlobalSettings().CurrentTheme().RequestedTheme();
+    }
+
+    winrt::Windows::UI::Xaml::FrameworkElement SettingsPaneContent::GetRoot()
+    {
+        return _sui;
+    }
+    winrt::Windows::Foundation::Size SettingsPaneContent::MinimumSize()
+    {
+        return { 1, 1 };
+    }
+    void SettingsPaneContent::Focus(winrt::Windows::UI::Xaml::FocusState reason)
+    {
+        if (reason != FocusState::Unfocused)
+        {
+            _sui.as<Controls::Page>().Focus(reason);
+        }
+    }
+    void SettingsPaneContent::Close()
+    {
+        CloseRequested.raise(*this, nullptr);
+    }
+
+    NewTerminalArgs SettingsPaneContent::GetNewTerminalArgs(const BuildStartupKind /*kind*/) const
+    {
+        // For now, we're doing a terrible thing in TerminalTab itself to
+        // generate an OpenSettings action manually, without asking for the pane
+        // structure.
+        return nullptr;
+    }
+
+    winrt::hstring SettingsPaneContent::Icon() const
+    {
+        // This is the Setting icon (looks like a gear)
+        static constexpr std::wstring_view glyph{ L"\xE713" };
+        return winrt::hstring{ glyph };
+    }
+
+    Windows::Foundation::IReference<winrt::Windows::UI::Color> SettingsPaneContent::TabColor() const noexcept
+    {
+        return nullptr;
+    }
+
+    winrt::Windows::UI::Xaml::Media::Brush SettingsPaneContent::BackgroundBrush()
+    {
+        // Look up the color we should use for the settings tab item from our
+        // resources. This should only be used for when "terminalBackground" is
+        // requested.
+        static const auto key = winrt::box_value(L"SettingsUiTabBrush");
+        // You can't just do a Application::Current().Resources().TryLookup
+        // lookup, cause the app theme never changes! Do the hacky version
+        // instead.
+        return ThemeLookup(Application::Current().Resources(), _requestedTheme, key).try_as<winrt::Windows::UI::Xaml::Media::Brush>();
+    }
+}
diff --git a/src/cascadia/TerminalApp/SettingsPaneContent.h b/src/cascadia/TerminalApp/SettingsPaneContent.h
new file mode 100644
index 00000000000..ee3322bf06b
--- /dev/null
+++ b/src/cascadia/TerminalApp/SettingsPaneContent.h
@@ -0,0 +1,46 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT license.
+
+#pragma once
+#include "winrt/TerminalApp.h"
+#include <LibraryResources.h>
+
+namespace winrt::TerminalApp::implementation
+{
+    class SettingsPaneContent : public winrt::implements<SettingsPaneContent, IPaneContent>
+    {
+    public:
+        SettingsPaneContent(winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings settings);
+
+        void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings& settings);
+
+        winrt::Windows::UI::Xaml::FrameworkElement GetRoot();
+        winrt::Microsoft::Terminal::Settings::Editor::MainPage SettingsUI() { return _sui; }
+
+        winrt::Windows::Foundation::Size MinimumSize();
+        void Focus(winrt::Windows::UI::Xaml::FocusState reason = winrt::Windows::UI::Xaml::FocusState::Programmatic);
+        void Close();
+        winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs GetNewTerminalArgs(const BuildStartupKind kind) const;
+
+        winrt::hstring Title() { return RS_(L"SettingsTab"); }
+        uint64_t TaskbarState() { return 0; }
+        uint64_t TaskbarProgress() { return 0; }
+        bool ReadOnly() { return false; }
+        winrt::hstring Icon() const;
+        Windows::Foundation::IReference<winrt::Windows::UI::Color> TabColor() const noexcept;
+        winrt::Windows::UI::Xaml::Media::Brush BackgroundBrush();
+
+        til::typed_event<> ConnectionStateChanged;
+        til::typed_event<IPaneContent> CloseRequested;
+        til::typed_event<IPaneContent, winrt::TerminalApp::BellEventArgs> BellRequested;
+        til::typed_event<IPaneContent> TitleChanged;
+        til::typed_event<IPaneContent> TabColorChanged;
+        til::typed_event<IPaneContent> TaskbarProgressChanged;
+        til::typed_event<IPaneContent> ReadOnlyChanged;
+        til::typed_event<IPaneContent> FocusRequested;
+
+    private:
+        winrt::Microsoft::Terminal::Settings::Editor::MainPage _sui{ nullptr };
+        winrt::Windows::UI::Xaml::ElementTheme _requestedTheme;
+    };
+}
diff --git a/src/cascadia/TerminalApp/SettingsTab.cpp b/src/cascadia/TerminalApp/SettingsTab.cpp
deleted file mode 100644
index 483f4f30475..00000000000
--- a/src/cascadia/TerminalApp/SettingsTab.cpp
+++ /dev/null
@@ -1,131 +0,0 @@
-// Copyright (c) Microsoft Corporation.
-// Licensed under the MIT license.
-
-#include "pch.h"
-#include <LibraryResources.h>
-#include "SettingsTab.h"
-#include "SettingsTab.g.cpp"
-#include "Utils.h"
-
-using namespace winrt;
-using namespace winrt::Windows::UI::Xaml;
-using namespace winrt::Windows::UI::Core;
-using namespace winrt::Microsoft::Terminal::Control;
-using namespace winrt::Microsoft::Terminal::Settings::Model;
-using namespace winrt::Microsoft::Terminal::Settings::Editor;
-using namespace winrt::Windows::System;
-
-namespace winrt
-{
-    namespace MUX = Microsoft::UI::Xaml;
-    namespace WUX = Windows::UI::Xaml;
-}
-
-#define ASSERT_UI_THREAD() assert(TabViewItem().Dispatcher().HasThreadAccess())
-
-namespace winrt::TerminalApp::implementation
-{
-    SettingsTab::SettingsTab(MainPage settingsUI,
-                             winrt::Windows::UI::Xaml::ElementTheme requestedTheme)
-    {
-        Content(settingsUI);
-        _requestedTheme = requestedTheme;
-
-        _MakeTabViewItem();
-        _CreateContextMenu();
-        _CreateIcon();
-    }
-
-    void SettingsTab::UpdateSettings(CascadiaSettings settings)
-    {
-        ASSERT_UI_THREAD();
-
-        auto settingsUI{ Content().as<MainPage>() };
-        settingsUI.UpdateSettings(settings);
-
-        // Stash away the current requested theme of the app. We'll need that in
-        // _BackgroundBrush() to do a theme-aware resource lookup
-        _requestedTheme = settings.GlobalSettings().CurrentTheme().RequestedTheme();
-    }
-
-    // Method Description:
-    // - Creates a list of actions that can be run to recreate the state of this tab
-    // Arguments:
-    // - asContent: unused. There's nothing different we need to do when
-    //   serializing the settings tab for moving to another window. If we ever
-    //   really want to support opening the SUI to a specific page, we can
-    //   re-evaluate including that arg in this action then.
-    //  Return Value:
-    // - The list of actions.
-    std::vector<ActionAndArgs> SettingsTab::BuildStartupActions(BuildStartupKind) const
-    {
-        ASSERT_UI_THREAD();
-
-        ActionAndArgs action;
-        action.Action(ShortcutAction::OpenSettings);
-        OpenSettingsArgs args{ SettingsTarget::SettingsUI };
-        action.Args(args);
-
-        return std::vector{ std::move(action) };
-    }
-
-    // Method Description:
-    // - Focus the settings UI
-    // Arguments:
-    // - focusState: The FocusState mode by which focus is to be obtained.
-    // Return Value:
-    // - <none>
-    void SettingsTab::Focus(WUX::FocusState focusState)
-    {
-        ASSERT_UI_THREAD();
-
-        _focusState = focusState;
-
-        if (_focusState != FocusState::Unfocused)
-        {
-            Content().as<WUX::Controls::Page>().Focus(focusState);
-        }
-    }
-
-    // Method Description:
-    // - Initializes a TabViewItem for this Tab instance.
-    // Arguments:
-    // - <none>
-    // Return Value:
-    // - <none>
-    void SettingsTab::_MakeTabViewItem()
-    {
-        TabBase::_MakeTabViewItem();
-
-        Title(RS_(L"SettingsTab"));
-        TabViewItem().Header(winrt::box_value(Title()));
-    }
-
-    // Method Description:
-    // - Set the icon on the TabViewItem for this tab.
-    // Arguments:
-    // - <none>
-    // Return Value:
-    // - <none>
-    void SettingsTab::_CreateIcon()
-    {
-        // This is the Setting icon (looks like a gear)
-        static constexpr std::wstring_view glyph{ L"\xE713" };
-
-        // The TabViewItem Icon needs MUX while the IconSourceElement in the CommandPalette needs WUX...
-        Icon(winrt::hstring{ glyph });
-        TabViewItem().IconSource(Microsoft::Terminal::UI::IconPathConverter::IconSourceMUX(glyph, false));
-    }
-
-    winrt::Windows::UI::Xaml::Media::Brush SettingsTab::_BackgroundBrush()
-    {
-        // Look up the color we should use for the settings tab item from our
-        // resources. This should only be used for when "terminalBackground" is
-        // requested.
-        static const auto key = winrt::box_value(L"SettingsUiTabBrush");
-        // You can't just do a Application::Current().Resources().TryLookup
-        // lookup, cause the app theme never changes! Do the hacky version
-        // instead.
-        return ThemeLookup(Application::Current().Resources(), _requestedTheme, key).try_as<winrt::Windows::UI::Xaml::Media::Brush>();
-    }
-}
diff --git a/src/cascadia/TerminalApp/SettingsTab.h b/src/cascadia/TerminalApp/SettingsTab.h
deleted file mode 100644
index e5d7df92faf..00000000000
--- a/src/cascadia/TerminalApp/SettingsTab.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/*++
-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 "TabBase.h"
-#include "SettingsTab.g.h"
-
-namespace winrt::TerminalApp::implementation
-{
-    struct SettingsTab : SettingsTabT<SettingsTab, TabBase>
-    {
-    public:
-        SettingsTab(winrt::Microsoft::Terminal::Settings::Editor::MainPage settingsUI,
-                    winrt::Windows::UI::Xaml::ElementTheme requestedTheme);
-
-        void UpdateSettings(Microsoft::Terminal::Settings::Model::CascadiaSettings settings);
-        void Focus(winrt::Windows::UI::Xaml::FocusState focusState) override;
-
-        std::vector<Microsoft::Terminal::Settings::Model::ActionAndArgs> BuildStartupActions(BuildStartupKind kind) const override;
-
-    private:
-        winrt::Windows::UI::Xaml::ElementTheme _requestedTheme;
-
-        void _MakeTabViewItem() override;
-        void _CreateIcon();
-
-        virtual winrt::Windows::UI::Xaml::Media::Brush _BackgroundBrush() override;
-    };
-}
diff --git a/src/cascadia/TerminalApp/SettingsTab.idl b/src/cascadia/TerminalApp/SettingsTab.idl
deleted file mode 100644
index deb45d4f155..00000000000
--- a/src/cascadia/TerminalApp/SettingsTab.idl
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright (c) Microsoft Corporation.
-// Licensed under the MIT license.
-
-import "TabBase.idl";
-
-namespace TerminalApp
-{
-    [default_interface] runtimeclass SettingsTab : TabBase
-    {
-        void UpdateSettings(Microsoft.Terminal.Settings.Model.CascadiaSettings settings);
-    }
-}
diff --git a/src/cascadia/TerminalApp/TabManagement.cpp b/src/cascadia/TerminalApp/TabManagement.cpp
index 8d99ac600f5..3f833bb8ba9 100644
--- a/src/cascadia/TerminalApp/TabManagement.cpp
+++ b/src/cascadia/TerminalApp/TabManagement.cpp
@@ -19,7 +19,6 @@
 #include "TabRowControl.h"
 #include "ColorHelper.h"
 #include "DebugTapConnection.h"
-#include "SettingsTab.h"
 #include "..\TerminalSettingsModel\FileUtils.h"
 
 #include <shlobj.h>
@@ -171,17 +170,8 @@ namespace winrt::TerminalApp::implementation
         auto tabViewItem = newTabImpl->TabViewItem();
         _tabView.TabItems().InsertAt(insertPosition, tabViewItem);
 
-        // Set this tab's icon to the icon from the user's profile
-        if (const auto profile{ newTabImpl->GetFocusedProfile() })
-        {
-            const auto& icon = profile.EvaluatedIcon();
-            if (!icon.empty())
-            {
-                const auto theme = _settings.GlobalSettings().CurrentTheme();
-                const auto iconStyle = (theme && theme.Tab()) ? theme.Tab().IconStyle() : IconStyle::Default;
-                newTabImpl->UpdateIcon(icon, iconStyle);
-            }
-        }
+        // Set this tab's icon to the icon from the content
+        _UpdateTabIcon(*newTabImpl);
 
         tabViewItem.PointerReleased({ this, &TerminalPage::_OnTabClick });
 
@@ -226,13 +216,15 @@ namespace winrt::TerminalApp::implementation
     // Arguments:
     // - pane: The pane to use as the root.
     // - insertPosition: Optional parameter to indicate the position of tab.
-    void TerminalPage::_CreateNewTabFromPane(std::shared_ptr<Pane> pane, uint32_t insertPosition)
+    TerminalApp::TerminalTab TerminalPage::_CreateNewTabFromPane(std::shared_ptr<Pane> pane, uint32_t insertPosition)
     {
         if (pane)
         {
             auto newTabImpl = winrt::make_self<TerminalTab>(pane);
             _InitializeTab(newTabImpl, insertPosition);
+            return *newTabImpl;
         }
+        return nullptr;
     }
 
     // Method Description:
@@ -242,11 +234,13 @@ namespace winrt::TerminalApp::implementation
     // - tab: the Tab to update the title for.
     void TerminalPage::_UpdateTabIcon(TerminalTab& tab)
     {
-        if (const auto profile = tab.GetFocusedProfile())
+        if (const auto content{ tab.GetActiveContent() })
         {
+            const auto& icon{ content.Icon() };
             const auto theme = _settings.GlobalSettings().CurrentTheme();
             const auto iconStyle = (theme && theme.Tab()) ? theme.Tab().IconStyle() : IconStyle::Default;
-            tab.UpdateIcon(profile.EvaluatedIcon(), iconStyle);
+
+            tab.UpdateIcon(icon, iconStyle);
         }
     }
 
@@ -800,14 +794,6 @@ namespace winrt::TerminalApp::implementation
                 }
             }
         }
-        else if (auto index{ _GetFocusedTabIndex() })
-        {
-            const auto tab{ _tabs.GetAt(*index) };
-            if (tab.try_as<TerminalApp::SettingsTab>())
-            {
-                _HandleCloseTabRequested(tab);
-            }
-        }
     }
 
     // Method Description:
diff --git a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj
index 827e9dcf168..d0629da8036 100644
--- a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj
+++ b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj
@@ -88,9 +88,6 @@
       <DependentUpon>PaletteItemTemplateSelector.idl</DependentUpon>
       <SubType>Code</SubType>
     </ClInclude>
-    <ClInclude Include="SettingsTab.h">
-      <DependentUpon>SettingsTab.idl</DependentUpon>
-    </ClInclude>
     <ClInclude Include="PaletteItem.h" />
     <ClInclude Include="TabBase.h">
       <DependentUpon>TabBase.idl</DependentUpon>
@@ -164,7 +161,13 @@
     <ClInclude Include="ScratchpadContent.h">
       <DependentUpon>TerminalPaneContent.idl</DependentUpon>
     </ClInclude>
+    <ClInclude Include="SettingsPaneContent.h">
+      <DependentUpon>TerminalPaneContent.idl</DependentUpon>
+    </ClInclude>
     <ClInclude Include="Toast.h" />
+    <ClInclude Include="TerminalSettingsCache.h">
+      <DependentUpon>TerminalSettingsCache.idl</DependentUpon>
+    </ClInclude>
     <ClInclude Include="SuggestionsControl.h">
       <DependentUpon>SuggestionsControl.xaml</DependentUpon>
     </ClInclude>
@@ -185,9 +188,6 @@
       <DependentUpon>PaletteItemTemplateSelector.idl</DependentUpon>
       <SubType>Code</SubType>
     </ClCompile>
-    <ClCompile Include="SettingsTab.cpp">
-      <DependentUpon>SettingsTab.idl</DependentUpon>
-    </ClCompile>
     <ClCompile Include="PaletteItem.cpp" />
     <ClCompile Include="TabBase.cpp">
       <DependentUpon>TabBase.idl</DependentUpon>
@@ -272,10 +272,16 @@
       <DependentUpon>TerminalPaneContent.idl</DependentUpon>
     </ClCompile>
     <ClCompile Include="ScratchpadContent.cpp">
-      <DependentUpon>ScratchpadContent.idl</DependentUpon>
+      <DependentUpon>TerminalPaneContent.idl</DependentUpon>
+    </ClCompile>
+    <ClCompile Include="SettingsPaneContent.cpp">
+      <DependentUpon>TerminalPaneContent.idl</DependentUpon>
     </ClCompile>
     <ClCompile Include="$(GeneratedFilesDir)module.g.cpp" />
     <ClCompile Include="Toast.cpp" />
+    <ClCompile Include="TerminalSettingsCache.cpp">
+      <DependentUpon>TerminalSettingsCache.idl</DependentUpon>
+    </ClCompile>
     <ClCompile Include="SuggestionsControl.cpp">
       <DependentUpon>SuggestionsControl.xaml</DependentUpon>
     </ClCompile>
@@ -295,7 +301,6 @@
     <Midl Include="PaletteItemTemplateSelector.idl">
       <SubType>Designer</SubType>
     </Midl>
-    <Midl Include="SettingsTab.idl" />
     <Midl Include="PaletteItem.idl" />
     <Midl Include="ShortcutActionDispatch.idl" />
     <Midl Include="AppKeyBindings.idl" />
@@ -348,6 +353,7 @@
     <Midl Include="FilteredCommand.idl" />
     <Midl Include="IPaneContent.idl" />
     <Midl Include="TerminalPaneContent.idl" />
+    <Midl Include="TerminalSettingsCache.idl" />
   </ItemGroup>
   <!-- ========================= Misc Files ======================== -->
   <ItemGroup>
diff --git a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj.filters b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj.filters
index e18881ba012..0f6468fd6b9 100644
--- a/src/cascadia/TerminalApp/TerminalAppLib.vcxproj.filters
+++ b/src/cascadia/TerminalApp/TerminalAppLib.vcxproj.filters
@@ -24,9 +24,6 @@
     <ClCompile Include="Tab.cpp">
       <Filter>tab</Filter>
     </ClCompile>
-    <ClCompile Include="SettingsTab.cpp">
-      <Filter>tab</Filter>
-    </ClCompile>
     <ClCompile Include="FilteredCommand.cpp">
       <Filter>commandPalette</Filter>
     </ClCompile>
@@ -64,9 +61,6 @@
     <ClInclude Include="Tab.h">
       <Filter>tab</Filter>
     </ClInclude>
-    <ClInclude Include="SettingsTab.h">
-      <Filter>tab</Filter>
-    </ClInclude>
     <ClInclude Include="FilteredCommand.h">
       <Filter>commandPalette</Filter>
     </ClInclude>
@@ -98,6 +92,7 @@
     <Midl Include="ShortcutActionDispatch.idl">
       <Filter>settings</Filter>
     </Midl>
+    <Midl Include="IDirectKeyListener.idl" />
     <Midl Include="SettingsTab.idl">
       <Filter>tab</Filter>
     </Midl>
diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp
index 591fc6906a0..e722a744112 100644
--- a/src/cascadia/TerminalApp/TerminalPage.cpp
+++ b/src/cascadia/TerminalApp/TerminalPage.cpp
@@ -20,7 +20,7 @@
 #include "App.h"
 #include "ColorHelper.h"
 #include "DebugTapConnection.h"
-#include "SettingsTab.h"
+#include "SettingsPaneContent.h"
 #include "TabRowControl.h"
 #include "Utils.h"
 
@@ -2336,6 +2336,12 @@ namespace winrt::TerminalApp::implementation
             }
         }
 
+        // For now, prevent splitting the _settingsTab. We can always revisit this later.
+        if (*activeTab == _settingsTab)
+        {
+            return;
+        }
+
         // If the caller is calling us with the return value of _MakePane
         // directly, it's possible that nullptr was returned, if the connections
         // was supposed to be launched in an elevated window. In that case, do
@@ -3271,50 +3277,18 @@ namespace winrt::TerminalApp::implementation
 
         // Refresh UI elements
 
-        // Mapping by GUID isn't _excellent_ because the defaults profile doesn't have a stable GUID; however,
-        // when we stabilize its guid this will become fully safe.
-        std::unordered_map<winrt::guid, std::pair<Profile, TerminalSettingsCreateResult>> profileGuidSettingsMap;
-        const auto profileDefaults{ _settings.ProfileDefaults() };
-        const auto allProfiles{ _settings.AllProfiles() };
-
-        profileGuidSettingsMap.reserve(allProfiles.Size() + 1);
-
-        // Include the Defaults profile for consideration
-        profileGuidSettingsMap.insert_or_assign(profileDefaults.Guid(), std::pair{ profileDefaults, nullptr });
-        for (const auto& newProfile : allProfiles)
-        {
-            // Avoid creating a TerminalSettings right now. They're not totally cheap, and we suspect that users with many
-            // panes may not be using all of their profiles at the same time. Lazy evaluation is king!
-            profileGuidSettingsMap.insert_or_assign(newProfile.Guid(), std::pair{ newProfile, nullptr });
-        }
+        // Recreate the TerminalSettings cache here. We'll use that as we're
+        // updating terminal panes, so that we don't have to build a _new_
+        // TerminalSettings for every profile we update - we can just look them
+        // up the previous ones we built.
+        _terminalSettingsCache = TerminalApp::TerminalSettingsCache{ _settings, *_bindings };
 
         for (const auto& tab : _tabs)
         {
             if (auto terminalTab{ _GetTerminalTabImpl(tab) })
             {
-                terminalTab->UpdateSettings();
-
-                // Manually enumerate the panes in each tab; this will let us recycle TerminalSettings
-                // objects but only have to iterate one time.
-                terminalTab->GetRootPane()->WalkTree([&](auto&& pane) {
-                    if (const auto profile{ pane->GetProfile() })
-                    {
-                        const auto found{ profileGuidSettingsMap.find(profile.Guid()) };
-                        // GH#2455: If there are any panes with controls that had been
-                        // initialized with a Profile that no longer exists in our list of
-                        // profiles, we'll leave it unmodified. The profile doesn't exist
-                        // anymore, so we can't possibly update its settings.
-                        if (found != profileGuidSettingsMap.cend())
-                        {
-                            auto& pair{ found->second };
-                            if (!pair.second)
-                            {
-                                pair.second = TerminalSettings::CreateWithProfile(_settings, pair.first, *_bindings);
-                            }
-                            pane->UpdateSettings(pair.second, pair.first);
-                        }
-                    }
-                });
+                // Let the tab know that there are new settings. It's up to each content to decide what to do with them.
+                terminalTab->UpdateSettings(_settings, _terminalSettingsCache);
 
                 // 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
@@ -3324,10 +3298,6 @@ namespace winrt::TerminalApp::implementation
                 // Force the TerminalTab to re-grab its currently active control's title.
                 terminalTab->UpdateTitle();
             }
-            else if (auto settingsTab = tab.try_as<TerminalApp::SettingsTab>())
-            {
-                settingsTab.UpdateSettings(_settings);
-            }
 
             auto tabImpl{ winrt::get_self<TabBase>(tab) };
             tabImpl->SetActionMap(_settings.ActionMap());
@@ -3889,7 +3859,10 @@ namespace winrt::TerminalApp::implementation
                 }
             }
 
-            winrt::Microsoft::Terminal::Settings::Editor::MainPage sui{ _settings };
+            // Create the SUI pane content
+            auto settingsContent{ winrt::make_self<SettingsPaneContent>(_settings) };
+            auto sui = settingsContent->SettingsUI();
+
             if (_hostingHwnd)
             {
                 sui.SetHostingWindow(reinterpret_cast<uint64_t>(*_hostingHwnd));
@@ -3905,54 +3878,9 @@ namespace winrt::TerminalApp::implementation
                 }
             });
 
-            auto newTabImpl = winrt::make_self<SettingsTab>(sui, _settings.GlobalSettings().CurrentTheme().RequestedTheme());
-
-            // Add the new tab to the list of our tabs.
-            _tabs.Append(*newTabImpl);
-            _mruTabs.Append(*newTabImpl);
-
-            newTabImpl->SetDispatch(*_actionDispatch);
-            newTabImpl->SetActionMap(_settings.ActionMap());
-
-            // Give the tab its index in the _tabs vector so it can manage its own SwitchToTab command.
-            _UpdateTabIndices();
-
-            // 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);
-
-            tabViewItem.PointerPressed({ this, &TerminalPage::_OnTabClick });
-
-            // When the tab requests close, try to close it (prompt for approval, if required)
-            newTabImpl->CloseRequested([weakTab, weakThis{ get_weak() }](auto&& /*s*/, auto&& /*e*/) {
-                auto page{ weakThis.get() };
-                auto tab{ weakTab.get() };
-
-                if (page && tab)
-                {
-                    page->_HandleCloseTabRequested(*tab);
-                }
-            });
-
-            // When the tab is closed, remove it from our list of tabs.
-            newTabImpl->Closed([weakTab, weakThis{ get_weak() }](auto&& /*s*/, auto&& /*e*/) {
-                const auto page = weakThis.get();
-                const auto tab = weakTab.get();
-
-                if (page && tab)
-                {
-                    page->_RemoveTab(*tab);
-                }
-            });
-
-            _settingsTab = *newTabImpl;
-
-            // This kicks off TabView::SelectionChanged, in response to which
-            // we'll attach the terminal's Xaml control to the Xaml root.
-            _tabView.SelectedItem(tabViewItem);
+            // Create the tab
+            auto resultPane = std::make_shared<Pane>(*settingsContent);
+            _settingsTab = _CreateNewTabFromPane(resultPane);
         }
         else
         {
@@ -4571,13 +4499,15 @@ namespace winrt::TerminalApp::implementation
         til::color bgColor = backgroundSolidBrush.Color();
 
         Media::Brush terminalBrush{ nullptr };
-        if (const auto& control{ _GetActiveControl() })
+        if (const auto tab{ _GetFocusedTabImpl() })
         {
-            terminalBrush = control.BackgroundBrush();
-        }
-        else if (const auto& settingsTab{ _GetFocusedTab().try_as<TerminalApp::SettingsTab>() })
-        {
-            terminalBrush = settingsTab.Content().try_as<Settings::Editor::MainPage>().BackgroundBrush();
+            if (const auto& pane{ tab->GetActivePane() })
+            {
+                if (const auto& lastContent{ pane->GetLastFocusedContent() })
+                {
+                    terminalBrush = lastContent.BackgroundBrush();
+                }
+            }
         }
 
         if (_settings.GlobalSettings().UseAcrylicInTabRow())
diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h
index 227f28edbb5..eece631419b 100644
--- a/src/cascadia/TerminalApp/TerminalPage.h
+++ b/src/cascadia/TerminalApp/TerminalPage.h
@@ -213,7 +213,7 @@ namespace winrt::TerminalApp::implementation
 
         void _UpdateTabIndices();
 
-        TerminalApp::SettingsTab _settingsTab{ nullptr };
+        TerminalApp::TerminalTab _settingsTab{ nullptr };
 
         bool _isInFocusMode{ false };
         bool _isFullscreen{ false };
@@ -260,6 +260,8 @@ namespace winrt::TerminalApp::implementation
 
         TerminalApp::ContentManager _manager{ nullptr };
 
+        TerminalApp::TerminalSettingsCache _terminalSettingsCache{ nullptr };
+
         struct StashedDragData
         {
             winrt::com_ptr<winrt::TerminalApp::implementation::TabBase> draggedTab{ nullptr };
@@ -291,7 +293,7 @@ namespace winrt::TerminalApp::implementation
 
         void _OpenNewTabDropdown();
         HRESULT _OpenNewTab(const Microsoft::Terminal::Settings::Model::NewTerminalArgs& newTerminalArgs);
-        void _CreateNewTabFromPane(std::shared_ptr<Pane> pane, uint32_t insertPosition = -1);
+        TerminalApp::TerminalTab _CreateNewTabFromPane(std::shared_ptr<Pane> pane, uint32_t insertPosition = -1);
 
         std::wstring _evaluatePathForCwd(std::wstring_view path);
 
diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.cpp b/src/cascadia/TerminalApp/TerminalPaneContent.cpp
index 49bbe3ff355..96a84c4560f 100644
--- a/src/cascadia/TerminalApp/TerminalPaneContent.cpp
+++ b/src/cascadia/TerminalApp/TerminalPaneContent.cpp
@@ -80,6 +80,16 @@ namespace winrt::TerminalApp::implementation
         CloseRequested.raise(*this, nullptr);
     }
 
+    winrt::hstring TerminalPaneContent::Icon() const
+    {
+        return _profile.EvaluatedIcon();
+    }
+
+    Windows::Foundation::IReference<winrt::Windows::UI::Color> TerminalPaneContent::TabColor() const noexcept
+    {
+        return _control.TabColor();
+    }
+
     NewTerminalArgs TerminalPaneContent::GetNewTerminalArgs(const BuildStartupKind kind) const
     {
         NewTerminalArgs args{};
@@ -328,11 +338,20 @@ namespace winrt::TerminalApp::implementation
         RestartTerminalRequested.raise(*this, nullptr);
     }
 
-    void TerminalPaneContent::UpdateSettings(const TerminalSettingsCreateResult& settings,
-                                             const Profile& profile)
+    void TerminalPaneContent::UpdateSettings(const CascadiaSettings& /*settings*/)
+    {
+        // Do nothing. We'll later be updated manually by
+        // UpdateTerminalSettings, which we need for profile and
+        // focused/unfocused settings.
+        assert(false); // If you hit this, you done goofed.
+    }
+
+    void TerminalPaneContent::UpdateTerminalSettings(const TerminalApp::TerminalSettingsCache& cache)
     {
-        _profile = profile;
-        _control.UpdateControlSettings(settings.DefaultSettings(), settings.UnfocusedSettings());
+        if (const auto& settings{ cache.TryLookup(_profile) })
+        {
+            _control.UpdateControlSettings(settings.DefaultSettings(), settings.UnfocusedSettings());
+        }
     }
 
     // Method Description:
@@ -344,6 +363,11 @@ namespace winrt::TerminalApp::implementation
         _isDefTermSession = true;
     }
 
+    winrt::Windows::UI::Xaml::Media::Brush TerminalPaneContent::BackgroundBrush()
+    {
+        return _control.BackgroundBrush();
+    }
+
     float TerminalPaneContent::SnapDownToGrid(const TerminalApp::PaneSnapDirection direction, const float sizeToSnap)
     {
         return _control.SnapDimensionToGrid(direction == PaneSnapDirection::Width, sizeToSnap);
diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.h b/src/cascadia/TerminalApp/TerminalPaneContent.h
index 11ad0425379..57a9a630c0f 100644
--- a/src/cascadia/TerminalApp/TerminalPaneContent.h
+++ b/src/cascadia/TerminalApp/TerminalPaneContent.h
@@ -29,8 +29,8 @@ namespace winrt::TerminalApp::implementation
 
         winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs GetNewTerminalArgs(BuildStartupKind kind) const;
 
-        void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult& settings,
-                            const winrt::Microsoft::Terminal::Settings::Model::Profile& profile);
+        void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings& settings);
+        void UpdateTerminalSettings(const TerminalApp::TerminalSettingsCache& cache);
 
         void MarkAsDefterm();
 
@@ -43,6 +43,9 @@ namespace winrt::TerminalApp::implementation
         uint64_t TaskbarState() { return _control.TaskbarState(); }
         uint64_t TaskbarProgress() { return _control.TaskbarProgress(); }
         bool ReadOnly() { return _control.ReadOnly(); }
+        winrt::hstring Icon() const;
+        Windows::Foundation::IReference<winrt::Windows::UI::Color> TabColor() const noexcept;
+        winrt::Windows::UI::Xaml::Media::Brush BackgroundBrush();
 
         float SnapDownToGrid(const TerminalApp::PaneSnapDirection direction, const float sizeToSnap);
         Windows::Foundation::Size GridUnitSize();
diff --git a/src/cascadia/TerminalApp/TerminalPaneContent.idl b/src/cascadia/TerminalApp/TerminalPaneContent.idl
index 7e04c8b836c..60c540273c6 100644
--- a/src/cascadia/TerminalApp/TerminalPaneContent.idl
+++ b/src/cascadia/TerminalApp/TerminalPaneContent.idl
@@ -2,6 +2,7 @@
 // Licensed under the MIT license.
 
 import "IPaneContent.idl";
+import "TerminalSettingsCache.idl";
 
 namespace TerminalApp
 {
@@ -9,8 +10,7 @@ namespace TerminalApp
     {
         Microsoft.Terminal.Control.TermControl GetTermControl();
 
-        void UpdateSettings(const Microsoft.Terminal.Settings.Model.TerminalSettingsCreateResult settings,
-                            const Microsoft.Terminal.Settings.Model.Profile profile);
+        void UpdateTerminalSettings(TerminalSettingsCache cache);
 
         void MarkAsDefterm();
 
diff --git a/src/cascadia/TerminalApp/TerminalSettingsCache.cpp b/src/cascadia/TerminalApp/TerminalSettingsCache.cpp
new file mode 100644
index 00000000000..6662afa9384
--- /dev/null
+++ b/src/cascadia/TerminalApp/TerminalSettingsCache.cpp
@@ -0,0 +1,57 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT license.
+
+#include "pch.h"
+#include "TerminalSettingsCache.h"
+#include "TerminalSettingsCache.g.cpp"
+
+namespace winrt
+{
+    namespace MUX = Microsoft::UI::Xaml;
+    namespace WUX = Windows::UI::Xaml;
+    namespace MTSM = Microsoft::Terminal::Settings::Model;
+}
+
+namespace winrt::TerminalApp::implementation
+{
+    TerminalSettingsCache::TerminalSettingsCache(const MTSM::CascadiaSettings& settings, const TerminalApp::AppKeyBindings& bindings) :
+        _settings{ settings },
+        _bindings{ bindings }
+    {
+        // Mapping by GUID isn't _excellent_ because the defaults profile doesn't have a stable GUID; however,
+        // when we stabilize its guid this will become fully safe.
+        const auto profileDefaults{ _settings.ProfileDefaults() };
+        const auto allProfiles{ _settings.AllProfiles() };
+
+        profileGuidSettingsMap.reserve(allProfiles.Size() + 1);
+
+        // Include the Defaults profile for consideration
+        profileGuidSettingsMap.insert_or_assign(profileDefaults.Guid(), std::pair{ profileDefaults, nullptr });
+        for (const auto& newProfile : allProfiles)
+        {
+            // Avoid creating a TerminalSettings right now. They're not totally cheap, and we suspect that users with many
+            // panes may not be using all of their profiles at the same time. Lazy evaluation is king!
+            profileGuidSettingsMap.insert_or_assign(newProfile.Guid(), std::pair{ newProfile, nullptr });
+        }
+    }
+
+    MTSM::TerminalSettingsCreateResult TerminalSettingsCache::TryLookup(const MTSM::Profile& profile)
+    {
+        const auto found{ profileGuidSettingsMap.find(profile.Guid()) };
+        // GH#2455: If there are any panes with controls that had been
+        // initialized with a Profile that no longer exists in our list of
+        // profiles, we'll leave it unmodified. The profile doesn't exist
+        // anymore, so we can't possibly update its settings.
+        if (found != profileGuidSettingsMap.cend())
+        {
+            auto& pair{ found->second };
+            if (!pair.second)
+            {
+                pair.second = MTSM::TerminalSettings::CreateWithProfile(_settings, pair.first, _bindings);
+            }
+            return pair.second;
+        }
+
+        return nullptr;
+    }
+}
diff --git a/src/cascadia/TerminalApp/TerminalSettingsCache.h b/src/cascadia/TerminalApp/TerminalSettingsCache.h
new file mode 100644
index 00000000000..054ccb4b51a
--- /dev/null
+++ b/src/cascadia/TerminalApp/TerminalSettingsCache.h
@@ -0,0 +1,37 @@
+/*++
+Copyright (c) Microsoft Corporation
+Licensed under the MIT license.
+
+Class Name:
+- TerminalSettingsCache.h
+
+Abstract:
+- This is a helper class used as we update the settings for panes. This class
+  contains a single map of guid -> TerminalSettings, so that as we update all
+  the panes during a settings reload, we only need to create a TerminalSettings
+  once per profile.
+--*/
+#pragma once
+
+#include "TerminalSettingsCache.g.h"
+#include <inc/cppwinrt_utils.h>
+
+namespace winrt::TerminalApp::implementation
+{
+    class TerminalSettingsCache : public TerminalSettingsCacheT<TerminalSettingsCache>
+    {
+    public:
+        TerminalSettingsCache(const Microsoft::Terminal::Settings::Model::CascadiaSettings& settings, const TerminalApp::AppKeyBindings& bindings);
+        Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult TryLookup(const Microsoft::Terminal::Settings::Model::Profile& profile);
+
+    private:
+        Microsoft::Terminal::Settings::Model::CascadiaSettings _settings{ nullptr };
+        TerminalApp::AppKeyBindings _bindings{ nullptr };
+        std::unordered_map<winrt::guid, std::pair<Microsoft::Terminal::Settings::Model::Profile, Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult>> profileGuidSettingsMap;
+    };
+}
+
+namespace winrt::TerminalApp::factory_implementation
+{
+    BASIC_FACTORY(TerminalSettingsCache);
+}
diff --git a/src/cascadia/TerminalApp/TerminalSettingsCache.idl b/src/cascadia/TerminalApp/TerminalSettingsCache.idl
new file mode 100644
index 00000000000..933e035a984
--- /dev/null
+++ b/src/cascadia/TerminalApp/TerminalSettingsCache.idl
@@ -0,0 +1,12 @@
+import "AppKeyBindings.idl";
+
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT license.
+namespace TerminalApp
+{
+    [default_interface] runtimeclass TerminalSettingsCache
+    {
+        TerminalSettingsCache(Microsoft.Terminal.Settings.Model.CascadiaSettings settings, AppKeyBindings bindings);
+        Microsoft.Terminal.Settings.Model.TerminalSettingsCreateResult TryLookup(Microsoft.Terminal.Settings.Model.Profile profile);
+    }
+}
diff --git a/src/cascadia/TerminalApp/TerminalTab.cpp b/src/cascadia/TerminalApp/TerminalTab.cpp
index 247601e21d1..9c99b44afaf 100644
--- a/src/cascadia/TerminalApp/TerminalTab.cpp
+++ b/src/cascadia/TerminalApp/TerminalTab.cpp
@@ -5,6 +5,7 @@
 #include <LibraryResources.h>
 #include "ColorPickupFlyout.h"
 #include "TerminalTab.h"
+#include "SettingsPaneContent.h"
 #include "TerminalTab.g.cpp"
 #include "Utils.h"
 #include "ColorHelper.h"
@@ -266,12 +267,18 @@ namespace winrt::TerminalApp::implementation
     //   of the settings that apply to all tabs.
     // Return Value:
     // - <none>
-    void TerminalTab::UpdateSettings()
+    void TerminalTab::UpdateSettings(const CascadiaSettings& settings, const TerminalApp::TerminalSettingsCache& cache)
     {
         ASSERT_UI_THREAD();
 
         // The tabWidthMode may have changed, update the header control accordingly
         _UpdateHeaderControlMaxWidth();
+
+        // Update the settings on all our panes.
+        _rootPane->WalkTree([&](auto pane) {
+            pane->UpdateSettings(settings, cache);
+            return false;
+        });
     }
 
     // Method Description:
@@ -280,7 +287,7 @@ namespace winrt::TerminalApp::implementation
     // - iconPath: The new path string to use as the IconPath for our TabViewItem
     // Return Value:
     // - <none>
-    void TerminalTab::UpdateIcon(const winrt::hstring iconPath, const winrt::Microsoft::Terminal::Settings::Model::IconStyle iconStyle)
+    void TerminalTab::UpdateIcon(const winrt::hstring& iconPath, const winrt::Microsoft::Terminal::Settings::Model::IconStyle iconStyle)
     {
         ASSERT_UI_THREAD();
 
@@ -383,7 +390,7 @@ namespace winrt::TerminalApp::implementation
             return RS_(L"MultiplePanes");
         }
         const auto activeContent = GetActiveContent();
-        return activeContent ? activeContent.Title() : L"";
+        return activeContent ? activeContent.Title() : winrt::hstring{ L"" };
     }
 
     // Method Description:
@@ -440,7 +447,30 @@ namespace winrt::TerminalApp::implementation
         // 1 for the child after the first split.
         auto state = _rootPane->BuildStartupActions(0, 1, kind);
 
+        // HORRIBLE
+        //
+        // Workaround till we know how we actually want to handle state
+        // restoring other kinda of panes. If this is a settings tab, just
+        // restore it as a settings tab. Don't bother recreating terminal args
+        // for every pane.
+        //
+        // In the future, we'll want to definitely get rid of
+        // Pane::GetTerminalArgsForPane, and somehow instead find a better way
+        // of re-creating the pane state. Probably through a combo of ResizePane
+        // actions and SetPaneOrientation actions.
+        if (const auto& settings{ _rootPane->GetContent().try_as<SettingsPaneContent>() })
+        {
+            ActionAndArgs action;
+            action.Action(ShortcutAction::OpenSettings);
+            OpenSettingsArgs args{ SettingsTarget::SettingsUI };
+            action.Args(args);
+
+            state.args = std::vector{ std::move(action) };
+        }
+        else
         {
+            state = _rootPane->BuildStartupActions(0, 1, kind);
+
             ActionAndArgs newTabAction{};
             newTabAction.Action(ShortcutAction::NewTab);
             NewTabArgs newTabArgs{ state.firstPane->GetTerminalArgsForPane(kind) };
@@ -1566,12 +1596,12 @@ namespace winrt::TerminalApp::implementation
     {
         ASSERT_UI_THREAD();
 
-        std::optional<winrt::Windows::UI::Color> controlTabColor;
-        if (const auto& control = GetActiveTerminalControl())
+        std::optional<winrt::Windows::UI::Color> contentTabColor;
+        if (const auto& content{ GetActiveContent() })
         {
-            if (const auto color = control.TabColor())
+            if (const auto color = content.TabColor())
             {
-                controlTabColor = color.Value();
+                contentTabColor = color.Value();
             }
         }
 
@@ -1581,7 +1611,7 @@ namespace winrt::TerminalApp::implementation
         // Color                |             | Set by
         // -------------------- | --          | --
         // Runtime Color        | _optional_  | Color Picker / `setTabColor` action
-        // Control Tab Color    | _optional_  | Profile's `tabColor`, or a color set by VT
+        // Content Tab Color    | _optional_  | Profile's `tabColor`, or a color set by VT (whatever the tab's content wants)
         // Theme Tab Background | _optional_  | `tab.backgroundColor` in the theme (handled in _RecalculateAndApplyTabColor)
         // Tab Default Color    | **default** | TabView in XAML
         //
@@ -1590,7 +1620,7 @@ namespace winrt::TerminalApp::implementation
         // tabview color" (and clear out any colors we've set).
 
         return til::coalesce(_runtimeTabColor,
-                             controlTabColor,
+                             contentTabColor,
                              std::optional<Windows::UI::Color>(std::nullopt));
     }
 
@@ -1629,7 +1659,7 @@ namespace winrt::TerminalApp::implementation
     winrt::Windows::UI::Xaml::Media::Brush TerminalTab::_BackgroundBrush()
     {
         Media::Brush terminalBrush{ nullptr };
-        if (const auto& c{ GetActiveTerminalControl() })
+        if (const auto& c{ GetActiveContent() })
         {
             terminalBrush = c.BackgroundBrush();
         }
diff --git a/src/cascadia/TerminalApp/TerminalTab.h b/src/cascadia/TerminalApp/TerminalTab.h
index 3ae1a3cc2fa..785d785ff31 100644
--- a/src/cascadia/TerminalApp/TerminalTab.h
+++ b/src/cascadia/TerminalApp/TerminalTab.h
@@ -42,7 +42,7 @@ namespace winrt::TerminalApp::implementation
                                                                           std::shared_ptr<Pane> newPane);
 
         void ToggleSplitOrientation();
-        void UpdateIcon(const winrt::hstring iconPath, const winrt::Microsoft::Terminal::Settings::Model::IconStyle iconStyle);
+        void UpdateIcon(const winrt::hstring& iconPath, const winrt::Microsoft::Terminal::Settings::Model::IconStyle iconStyle);
         void HideIcon(const bool hide);
 
         void ShowBellIndicator(const bool show);
@@ -58,7 +58,7 @@ namespace winrt::TerminalApp::implementation
         bool SwapPane(const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction);
         bool FocusPane(const uint32_t id);
 
-        void UpdateSettings();
+        void UpdateSettings(const winrt::Microsoft::Terminal::Settings::Model::CascadiaSettings& settings, const TerminalApp::TerminalSettingsCache& cache);
         void UpdateTitle();
 
         void Shutdown() override;
diff --git a/src/cascadia/TerminalApp/dll/TerminalApp.vcxproj b/src/cascadia/TerminalApp/dll/TerminalApp.vcxproj
index df855d79e11..483a27c9e7f 100644
--- a/src/cascadia/TerminalApp/dll/TerminalApp.vcxproj
+++ b/src/cascadia/TerminalApp/dll/TerminalApp.vcxproj
@@ -36,7 +36,6 @@
     <ClInclude Include="../TabRowControl.h" />
     <ClInclude Include="../App.h" />
     <ClInclude Include="../TerminalTab.h" />
-    <ClInclude Include="../SettingsTab.h" />
   </ItemGroup>
   <!-- ========================= Cpp Files ======================== -->
   <ItemGroup>


Test patch: