Skip to content

Commit

Permalink
Avoid animations during startup (#15204)
Browse files Browse the repository at this point in the history
This fixes 3 sources for animations:
* `TabView`'s `EntranceThemeTransition` causes tabs to slowly slide in
  from the bottom. Removing the transition requires you to override the
  entire list of transitions obviously, which is a global change. Nice.
  Am I glad I don't need to deal with the complexity of CSS. /s
* `TabBase`, `SettingsTab` and `TerminalTab` were using a lot of
  coroutines with `resume_foreground` even though almost none of the
  functions are called from background tabs in the first place. This
  caused us to miss the initial XAML drawing pass, which resulted in
  animations when the tab icons would asynchronously pop into existence.
  It also appears as if `resume_foreground`, etc. have a very high CPU
  cost attached, which surprises me absolutely not at all given WinRT.

The improvement is difficult to quantify because the run to run
variation is very high. But it seems like this shaves about 10% off
of the ~500ms startup delay on my PC depending on how you measure it.

Part of #5907

## PR Checklist
* It starts when it should ✅
* It doesn't "exit" when it shouldn't ✅
  (Scrolling, Settings reload, Bell `\a`, Progress `\e]9;4;2;80\e\\`)
  • Loading branch information
lhecker committed Apr 20, 2023
1 parent da0a6d4 commit 35b9e75
Show file tree
Hide file tree
Showing 9 changed files with 235 additions and 169 deletions.
19 changes: 18 additions & 1 deletion src/cascadia/TerminalApp/App.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
<Application x:Class="TerminalApp.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:TA="using:TerminalApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:TerminalApp"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:primitives="using:Microsoft.UI.Xaml.Controls.Primitives"
mc:Ignorable="d">
<!--
If you want to prove this works, then add `RequestedTheme="Light"` to
Expand Down Expand Up @@ -48,6 +48,23 @@
<Thickness x:Key="TabViewHeaderPadding">9,0,5,0</Thickness>
<Thickness x:Key="TabViewItemBorderThickness">1,1,1,0</Thickness>

<!--
Disable the EntranceThemeTransition for our muxc:TabView, which would slowly slide in the tabs
while the window opens. The difference is especially noticeable if window fade-in transitions are
disabled system-wide. On my system this shaves off about 10% of the startup cost and looks better.
-->
<Style TargetType="primitives:TabViewListView">
<Setter Property="ItemContainerTransitions">
<Setter.Value>
<TransitionCollection>
<AddDeleteThemeTransition />
<ContentThemeTransition />
<ReorderThemeTransition />
</TransitionCollection>
</Setter.Value>
</Setter>
</Style>

<!-- Shadow that can be used by any control. -->
<ThemeShadow x:Name="SharedShadow" />

Expand Down
26 changes: 14 additions & 12 deletions src/cascadia/TerminalApp/SettingsTab.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ namespace winrt
namespace WUX = Windows::UI::Xaml;
}

#define ASSERT_UI_THREAD() assert(TabViewItem().Dispatcher().HasThreadAccess())

namespace winrt::TerminalApp::implementation
{
SettingsTab::SettingsTab(MainPage settingsUI,
Expand All @@ -36,6 +38,8 @@ namespace winrt::TerminalApp::implementation

void SettingsTab::UpdateSettings(CascadiaSettings settings)
{
ASSERT_UI_THREAD();

auto settingsUI{ Content().as<MainPage>() };
settingsUI.UpdateSettings(settings);

Expand All @@ -55,6 +59,8 @@ namespace winrt::TerminalApp::implementation
// - The list of actions.
std::vector<ActionAndArgs> SettingsTab::BuildStartupActions(const bool /*asContent*/) const
{
ASSERT_UI_THREAD();

ActionAndArgs action;
action.Action(ShortcutAction::OpenSettings);
OpenSettingsArgs args{ SettingsTarget::SettingsUI };
Expand All @@ -71,6 +77,8 @@ namespace winrt::TerminalApp::implementation
// - <none>
void SettingsTab::Focus(WUX::FocusState focusState)
{
ASSERT_UI_THREAD();

_focusState = focusState;

if (_focusState != FocusState::Unfocused)
Expand Down Expand Up @@ -99,20 +107,14 @@ namespace winrt::TerminalApp::implementation
// - <none>
// Return Value:
// - <none>
winrt::fire_and_forget SettingsTab::_CreateIcon()
void SettingsTab::_CreateIcon()
{
auto weakThis{ get_weak() };
// This is the Setting icon (looks like a gear)
static constexpr std::wstring_view glyph{ L"\xE713" };

co_await wil::resume_foreground(TabViewItem().Dispatcher());

if (auto tab{ weakThis.get() })
{
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...
Icon(glyph);
TabViewItem().IconSource(IconPathConverter::IconSourceMUX(glyph));
}
// The TabViewItem Icon needs MUX while the IconSourceElement in the CommandPalette needs WUX...
Icon(winrt::hstring{ glyph });
TabViewItem().IconSource(IconPathConverter::IconSourceMUX(glyph));
}

winrt::Windows::UI::Xaml::Media::Brush SettingsTab::_BackgroundBrush()
Expand Down
2 changes: 1 addition & 1 deletion src/cascadia/TerminalApp/SettingsTab.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ namespace winrt::TerminalApp::implementation
winrt::Windows::UI::Xaml::ElementTheme _requestedTheme;

void _MakeTabViewItem() override;
winrt::fire_and_forget _CreateIcon();
void _CreateIcon();

virtual winrt::Windows::UI::Xaml::Media::Brush _BackgroundBrush() override;
};
Expand Down
90 changes: 42 additions & 48 deletions src/cascadia/TerminalApp/TabBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ namespace winrt
namespace WUX = Windows::UI::Xaml;
}

#define ASSERT_UI_THREAD() assert(TabViewItem().Dispatcher().HasThreadAccess())

namespace winrt::TerminalApp::implementation
{
WUX::FocusState TabBase::FocusState() const noexcept
Expand All @@ -32,6 +34,8 @@ namespace winrt::TerminalApp::implementation
// - Prepares this tab for being removed from the UI hierarchy
void TabBase::Shutdown()
{
ASSERT_UI_THREAD();

Content(nullptr);
_ClosedHandlers(nullptr, nullptr);
}
Expand Down Expand Up @@ -159,6 +163,8 @@ namespace winrt::TerminalApp::implementation

void TabBase::UpdateTabViewIndex(const uint32_t idx, const uint32_t numTabs)
{
ASSERT_UI_THREAD();

TabViewIndex(idx);
TabViewNumTabs(numTabs);
_EnableCloseMenuItems();
Expand All @@ -167,11 +173,15 @@ namespace winrt::TerminalApp::implementation

void TabBase::SetDispatch(const winrt::TerminalApp::ShortcutActionDispatch& dispatch)
{
ASSERT_UI_THREAD();

_dispatch = dispatch;
}

void TabBase::SetActionMap(const Microsoft::Terminal::Settings::Model::IActionMapView& actionMap)
{
ASSERT_UI_THREAD();

_actionMap = actionMap;
_UpdateSwitchToTabKeyChord();
}
Expand All @@ -183,26 +193,18 @@ namespace winrt::TerminalApp::implementation
// - keyChord - string representation of the key chord that switches to the current tab
// Return Value:
// - <none>
winrt::fire_and_forget TabBase::_UpdateSwitchToTabKeyChord()
void TabBase::_UpdateSwitchToTabKeyChord()
{
const auto keyChord = _actionMap ? _actionMap.GetKeyBindingForAction(ShortcutAction::SwitchToTab, SwitchToTabArgs{ _TabViewIndex }) : nullptr;
const auto keyChordText = keyChord ? KeyChordSerialization::ToString(keyChord) : L"";

if (_keyChord == keyChordText)
{
co_return;
return;
}

_keyChord = keyChordText;

auto weakThis{ get_weak() };

co_await wil::resume_foreground(TabViewItem().Dispatcher());

if (auto tab{ weakThis.get() })
{
_UpdateToolTip();
}
_UpdateToolTip();
}

// Method Description:
Expand Down Expand Up @@ -281,13 +283,17 @@ namespace winrt::TerminalApp::implementation

std::optional<winrt::Windows::UI::Color> TabBase::GetTabColor()
{
ASSERT_UI_THREAD();

return std::nullopt;
}

void TabBase::ThemeColor(const winrt::Microsoft::Terminal::Settings::Model::ThemeColor& focused,
const winrt::Microsoft::Terminal::Settings::Model::ThemeColor& unfocused,
const til::color& tabRowColor)
{
ASSERT_UI_THREAD();

_themeColor = focused;
_unfocusedThemeColor = unfocused;
_tabRowColor = tabRowColor;
Expand All @@ -305,49 +311,37 @@ namespace winrt::TerminalApp::implementation
// - <none>
void TabBase::_RecalculateAndApplyTabColor()
{
auto weakThis{ get_weak() };

TabViewItem().Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [weakThis]() {
auto ptrTab = weakThis.get();
if (!ptrTab)
{
return;
}

auto tab{ ptrTab };
// GetTabColor will return the color set by the color picker, or the
// color specified in the profile. If neither of those were set,
// then look to _themeColor to see if there's a value there.
// Otherwise, clear our color, falling back to the TabView defaults.
const auto currentColor = GetTabColor();
if (currentColor.has_value())
{
_ApplyTabColorOnUIThread(currentColor.value());
}
else if (_themeColor != nullptr)
{
// Safely get the active control's brush.
const Media::Brush terminalBrush{ _BackgroundBrush() };

// GetTabColor will return the color set by the color picker, or the
// color specified in the profile. If neither of those were set,
// then look to _themeColor to see if there's a value there.
// Otherwise, clear our color, falling back to the TabView defaults.
const auto currentColor = tab->GetTabColor();
if (currentColor.has_value())
if (const auto themeBrush{ _themeColor.Evaluate(Application::Current().Resources(), terminalBrush, false) })
{
tab->_ApplyTabColorOnUIThread(currentColor.value());
}
else if (tab->_themeColor != nullptr)
{
// Safely get the active control's brush.
const Media::Brush terminalBrush{ tab->_BackgroundBrush() };

if (const auto themeBrush{ tab->_themeColor.Evaluate(Application::Current().Resources(), terminalBrush, false) })
{
// ThemeColor.Evaluate will get us a Brush (because the
// TermControl could have an acrylic BG, for example). Take
// that brush, and get the color out of it. We don't really
// want to have the tab items themselves be acrylic.
tab->_ApplyTabColorOnUIThread(til::color{ ThemeColor::ColorFromBrush(themeBrush) });
}
else
{
tab->_ClearTabBackgroundColor();
}
// ThemeColor.Evaluate will get us a Brush (because the
// TermControl could have an acrylic BG, for example). Take
// that brush, and get the color out of it. We don't really
// want to have the tab items themselves be acrylic.
_ApplyTabColorOnUIThread(til::color{ ThemeColor::ColorFromBrush(themeBrush) });
}
else
{
tab->_ClearTabBackgroundColor();
_ClearTabBackgroundColor();
}
});
}
else
{
_ClearTabBackgroundColor();
}
}

// Method Description:
Expand Down
2 changes: 1 addition & 1 deletion src/cascadia/TerminalApp/TabBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ namespace winrt::TerminalApp::implementation
void _EnableCloseMenuItems();
void _CloseTabsAfter();
void _CloseOtherTabs();
winrt::fire_and_forget _UpdateSwitchToTabKeyChord();
void _UpdateSwitchToTabKeyChord();
void _UpdateToolTip();

void _RecalculateAndApplyTabColor();
Expand Down
Loading

0 comments on commit 35b9e75

Please sign in to comment.