Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Only iterate panes one time when updating settings #10997

Merged
4 commits merged into from
Aug 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 17 additions & 33 deletions src/cascadia/TerminalApp/Pane.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1049,48 +1049,32 @@ void Pane::_FocusFirstChild()
}

// Method Description:
// - Attempts to update the settings of this pane or any children of this pane.
// * If this pane is a leaf, and our profile guid matches the parameter, then
// we'll apply the new settings to our control.
// * If we're not a leaf, we'll recurse on our children.
// - 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)
DHowett marked this conversation as resolved.
Show resolved Hide resolved
{
if (!_IsLeaf())
{
_firstChild->UpdateSettings(settings, profile);
_secondChild->UpdateSettings(settings, profile);
}
else
assert(_IsLeaf());

_profile = profile;
auto controlSettings = _control.Settings().as<TerminalSettings>();
// Update the parent of the control's settings object (and not the object itself) so
// that any overrides made by the control don't get affected by the reload
controlSettings.SetParent(settings.DefaultSettings());
auto unfocusedSettings{ settings.UnfocusedSettings() };
if (unfocusedSettings)
{
// Because this call may be coming in with a different settings tree,
// we want to map the incoming profile based on its GUID.
// Failure to find a matching profile will result in a pane holding
// a reference to a deleted profile (which is okay!).
if (profile.Guid() == _profile.Guid())
{
_profile = profile;
auto controlSettings = _control.Settings().as<TerminalSettings>();
// Update the parent of the control's settings object (and not the object itself) so
// that any overrides made by the control don't get affected by the reload
controlSettings.SetParent(settings.DefaultSettings());
auto unfocusedSettings{ settings.UnfocusedSettings() };
if (unfocusedSettings)
{
// Note: the unfocused settings needs to be entirely unchanged _except_ we need to
// set its parent to the settings object that lives in the control. This is because
// the overrides made by the control live in that settings object, so we want to make
// sure the unfocused settings inherit from that.
unfocusedSettings.SetParent(controlSettings);
}
_control.UnfocusedAppearance(unfocusedSettings);
_control.UpdateSettings();
}
// Note: the unfocused settings needs to be entirely unchanged _except_ we need to
// set its parent to the settings object that lives in the control. This is because
// the overrides made by the control live in that settings object, so we want to make
// sure the unfocused settings inherit from that.
unfocusedSettings.SetParent(controlSettings);
}
_control.UnfocusedAppearance(unfocusedSettings);
_control.UpdateSettings();
}

// Method Description:
Expand Down
8 changes: 8 additions & 0 deletions src/cascadia/TerminalApp/Pane.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,14 @@ class Pane : public std::enable_shared_from_this<Pane>
winrt::Microsoft::Terminal::Control::TermControl GetTerminalControl();
winrt::Microsoft::Terminal::Settings::Model::Profile GetFocusedProfile();

// Method Description:
// - If this is a leaf pane, return its profile.
// - If this is a branch/root pane, return nullptr.
winrt::Microsoft::Terminal::Settings::Model::Profile GetProfile() const
{
return _profile;
}

winrt::Windows::UI::Xaml::Controls::Grid GetRootElement();

bool WasLastFocused() const noexcept;
Expand Down
68 changes: 44 additions & 24 deletions src/cascadia/TerminalApp/TerminalPage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1996,36 +1996,56 @@ namespace winrt::TerminalApp::implementation
_HookupKeyBindings(_settings.ActionMap());

// Refresh UI elements
auto profiles = _settings.ActiveProfiles();
for (const auto& profile : profiles)

// 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)
{
try
// 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 });
}

for (const auto& tab : _tabs)
{
if (auto terminalTab{ _GetTerminalTabImpl(tab) })
{
auto settings{ TerminalSettings::CreateWithProfile(_settings, profile, *_bindings) };
terminalTab->UpdateSettings();

for (auto tab : _tabs)
{
if (auto terminalTab = _GetTerminalTabImpl(tab))
// 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() })
{
terminalTab->UpdateSettings(settings, 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 = TerminalSettings::CreateWithProfile(_settings, pair.first, *_bindings);
}
pane->UpdateSettings(pair.second, pair.first);
}
}
}
}
CATCH_LOG();
}

// 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.
return false;
});

// 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)
{
if (auto terminalTab = _GetTerminalTabImpl(tab))
{
// 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.
_UpdateTabIcon(*terminalTab);

// Force the TerminalTab to re-grab its currently active control's title.
Expand Down
11 changes: 4 additions & 7 deletions src/cascadia/TerminalApp/TerminalTab.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -263,16 +263,13 @@ namespace winrt::TerminalApp::implementation
}

// Method Description:
// - Attempts to update the settings of this tab's tree of panes.
// Arguments:
// - settings: The new TerminalSettingsCreateResult to apply to any matching controls
// - profile: The GUID of the profile these settings should apply to.
// - Attempts to update the settings that apply to this tab.
// - Panes are handled elsewhere, by somebody who can establish broader knowledge
// of the settings that apply to all tabs.
// Return Value:
// - <none>
void TerminalTab::UpdateSettings(const TerminalSettingsCreateResult& settings, const Profile& profile)
void TerminalTab::UpdateSettings()
DHowett marked this conversation as resolved.
Show resolved Hide resolved
{
_rootPane->UpdateSettings(settings, profile);

// The tabWidthMode may have changed, update the header control accordingly
_UpdateHeaderControlMaxWidth();
}
Expand Down
4 changes: 3 additions & 1 deletion src/cascadia/TerminalApp/TerminalTab.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ namespace winrt::TerminalApp::implementation
bool SwapPane(const winrt::Microsoft::Terminal::Settings::Model::FocusDirection& direction);
bool FocusPane(const uint32_t id);

void UpdateSettings(const Microsoft::Terminal::Settings::Model::TerminalSettingsCreateResult& settings, const winrt::Microsoft::Terminal::Settings::Model::Profile& profile);
void UpdateSettings();
winrt::fire_and_forget UpdateTitle();

void Shutdown() override;
Expand Down Expand Up @@ -90,6 +90,8 @@ namespace winrt::TerminalApp::implementation
std::shared_ptr<Pane> GetActivePane() const;
winrt::TerminalApp::TaskbarState GetCombinedTaskbarState() const;

std::shared_ptr<Pane> GetRootPane() const { return _rootPane; }

winrt::TerminalApp::TerminalTabStatus TabStatus()
{
return _tabStatus;
Expand Down