Avoid animations during startup (#15204)

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\\`)
This commit is contained in:
Leonard Hecker
2023-04-20 14:31:44 +02:00
committed by GitHub
parent da0a6d468a
commit 35b9e75574
9 changed files with 234 additions and 168 deletions

View File

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

View File

@@ -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,
@@ -36,6 +38,8 @@ namespace winrt::TerminalApp::implementation
void SettingsTab::UpdateSettings(CascadiaSettings settings)
{
ASSERT_UI_THREAD();
auto settingsUI{ Content().as<MainPage>() };
settingsUI.UpdateSettings(settings);
@@ -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 };
@@ -71,6 +77,8 @@ namespace winrt::TerminalApp::implementation
// - <none>
void SettingsTab::Focus(WUX::FocusState focusState)
{
ASSERT_UI_THREAD();
_focusState = focusState;
if (_focusState != FocusState::Unfocused)
@@ -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()

View File

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

View File

@@ -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
@@ -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);
}
@@ -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();
@@ -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();
}
@@ -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:
@@ -281,6 +283,8 @@ namespace winrt::TerminalApp::implementation
std::optional<winrt::Windows::UI::Color> TabBase::GetTabColor()
{
ASSERT_UI_THREAD();
return std::nullopt;
}
@@ -288,6 +292,8 @@ namespace winrt::TerminalApp::implementation
const winrt::Microsoft::Terminal::Settings::Model::ThemeColor& unfocused,
const til::color& tabRowColor)
{
ASSERT_UI_THREAD();
_themeColor = focused;
_unfocusedThemeColor = unfocused;
_tabRowColor = tabRowColor;
@@ -305,49 +311,37 @@ namespace winrt::TerminalApp::implementation
// - <none>
void TabBase::_RecalculateAndApplyTabColor()
{
auto weakThis{ get_weak() };
// 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() };
TabViewItem().Dispatcher().RunAsync(CoreDispatcherPriority::Normal, [weakThis]() {
auto ptrTab = weakThis.get();
if (!ptrTab)
if (const auto themeBrush{ _themeColor.Evaluate(Application::Current().Resources(), terminalBrush, false) })
{
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 = tab->GetTabColor();
if (currentColor.has_value())
{
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:

View File

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

View File

@@ -24,6 +24,8 @@ namespace winrt
namespace WUX = Windows::UI::Xaml;
}
#define ASSERT_UI_THREAD() assert(TabViewItem().Dispatcher().HasThreadAccess())
namespace winrt::TerminalApp::implementation
{
TerminalTab::TerminalTab(std::shared_ptr<Pane> rootPane)
@@ -143,30 +145,23 @@ namespace winrt::TerminalApp::implementation
_RecalculateAndApplyTabColor();
}
winrt::fire_and_forget TerminalTab::_UpdateHeaderControlMaxWidth()
void TerminalTab::_UpdateHeaderControlMaxWidth()
{
auto weakThis{ get_weak() };
co_await wil::resume_foreground(TabViewItem().Dispatcher());
if (auto tab{ weakThis.get() })
try
{
try
// Make sure to try/catch this, because the LocalTests won't be
// able to use this helper.
const auto settings{ winrt::TerminalApp::implementation::AppLogic::CurrentAppSettings() };
if (settings.GlobalSettings().TabWidthMode() == winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode::SizeToContent)
{
// Make sure to try/catch this, because the LocalTests won't be
// able to use this helper.
const auto settings{ winrt::TerminalApp::implementation::AppLogic::CurrentAppSettings() };
if (settings.GlobalSettings().TabWidthMode() == winrt::Microsoft::UI::Xaml::Controls::TabViewWidthMode::SizeToContent)
{
tab->_headerControl.RenamerMaxWidth(HeaderRenameBoxWidthTitleLength);
}
else
{
tab->_headerControl.RenamerMaxWidth(HeaderRenameBoxWidthDefault);
}
_headerControl.RenamerMaxWidth(HeaderRenameBoxWidthTitleLength);
}
else
{
_headerControl.RenamerMaxWidth(HeaderRenameBoxWidthDefault);
}
CATCH_LOG()
}
CATCH_LOG()
}
// Method Description:
@@ -182,6 +177,8 @@ namespace winrt::TerminalApp::implementation
// that was last focused.
TermControl TerminalTab::GetActiveTerminalControl() const
{
ASSERT_UI_THREAD();
if (_activePane)
{
return _activePane->GetLastFocusedTerminalControl();
@@ -198,6 +195,8 @@ namespace winrt::TerminalApp::implementation
// - <none>
void TerminalTab::Initialize()
{
ASSERT_UI_THREAD();
_rootPane->WalkTree([&](std::shared_ptr<Pane> pane) {
// Attach event handlers to each new pane
_AttachEventHandlersToPane(pane);
@@ -217,6 +216,8 @@ namespace winrt::TerminalApp::implementation
// - <none>
void TerminalTab::Focus(WUX::FocusState focusState)
{
ASSERT_UI_THREAD();
_focusState = focusState;
if (_focusState != FocusState::Unfocused)
@@ -249,6 +250,8 @@ namespace winrt::TerminalApp::implementation
// focused, else the GUID of the profile of the last control to be focused
Profile TerminalTab::GetFocusedProfile() const noexcept
{
ASSERT_UI_THREAD();
return _activePane->GetFocusedProfile();
}
@@ -260,6 +263,8 @@ namespace winrt::TerminalApp::implementation
// - <none>
void TerminalTab::UpdateSettings()
{
ASSERT_UI_THREAD();
// The tabWidthMode may have changed, update the header control accordingly
_UpdateHeaderControlMaxWidth();
}
@@ -270,12 +275,14 @@ namespace winrt::TerminalApp::implementation
// - iconPath: The new path string to use as the IconPath for our TabViewItem
// Return Value:
// - <none>
winrt::fire_and_forget TerminalTab::UpdateIcon(const winrt::hstring iconPath)
void TerminalTab::UpdateIcon(const winrt::hstring iconPath)
{
ASSERT_UI_THREAD();
// Don't reload our icon if it hasn't changed.
if (iconPath == _lastIconPath)
{
co_return;
return;
}
_lastIconPath = iconPath;
@@ -284,19 +291,12 @@ namespace winrt::TerminalApp::implementation
// for when we show the icon again)
if (_iconHidden)
{
co_return;
return;
}
auto weakThis{ get_weak() };
co_await wil::resume_foreground(TabViewItem().Dispatcher());
if (auto tab{ weakThis.get() })
{
// The TabViewItem Icon needs MUX while the IconSourceElement in the CommandPalette needs WUX...
Icon(_lastIconPath);
TabViewItem().IconSource(IconPathConverter::IconSourceMUX(_lastIconPath));
}
// The TabViewItem Icon needs MUX while the IconSourceElement in the CommandPalette needs WUX...
Icon(_lastIconPath);
TabViewItem().IconSource(IconPathConverter::IconSourceMUX(_lastIconPath));
}
// Method Description:
@@ -304,28 +304,23 @@ namespace winrt::TerminalApp::implementation
// - Used when we want to show the progress ring, which should replace the icon
// Arguments:
// - hide: if true, we hide the icon; if false, we show the icon
winrt::fire_and_forget TerminalTab::HideIcon(const bool hide)
void TerminalTab::HideIcon(const bool hide)
{
auto weakThis{ get_weak() };
ASSERT_UI_THREAD();
co_await wil::resume_foreground(TabViewItem().Dispatcher());
if (auto tab{ weakThis.get() })
if (_iconHidden != hide)
{
if (tab->_iconHidden != hide)
if (hide)
{
if (hide)
{
Icon({});
TabViewItem().IconSource(IconSource{ nullptr });
}
else
{
Icon(_lastIconPath);
TabViewItem().IconSource(IconPathConverter::IconSourceMUX(_lastIconPath));
}
tab->_iconHidden = hide;
Icon({});
TabViewItem().IconSource(IconSource{ nullptr });
}
else
{
Icon(_lastIconPath);
TabViewItem().IconSource(IconPathConverter::IconSourceMUX(_lastIconPath));
}
_iconHidden = hide;
}
}
@@ -333,37 +328,27 @@ namespace winrt::TerminalApp::implementation
// - Hide or show the bell indicator in the tab header
// Arguments:
// - show: if true, we show the indicator; if false, we hide the indicator
winrt::fire_and_forget TerminalTab::ShowBellIndicator(const bool show)
void TerminalTab::ShowBellIndicator(const bool show)
{
auto weakThis{ get_weak() };
ASSERT_UI_THREAD();
co_await wil::resume_foreground(TabViewItem().Dispatcher());
if (auto tab{ weakThis.get() })
{
_tabStatus.BellIndicator(show);
}
_tabStatus.BellIndicator(show);
}
// Method Description:
// - Activates the timer for the bell indicator in the tab
// - Called if a bell raised when the tab already has focus
winrt::fire_and_forget TerminalTab::ActivateBellIndicatorTimer()
void TerminalTab::ActivateBellIndicatorTimer()
{
auto weakThis{ get_weak() };
ASSERT_UI_THREAD();
co_await wil::resume_foreground(TabViewItem().Dispatcher());
if (auto tab{ weakThis.get() })
if (!_bellIndicatorTimer.has_value())
{
if (!tab->_bellIndicatorTimer.has_value())
{
DispatcherTimer bellIndicatorTimer;
bellIndicatorTimer.Interval(std::chrono::milliseconds(2000));
bellIndicatorTimer.Tick({ get_weak(), &TerminalTab::_BellIndicatorTimerTick });
bellIndicatorTimer.Start();
tab->_bellIndicatorTimer.emplace(std::move(bellIndicatorTimer));
}
DispatcherTimer bellIndicatorTimer;
bellIndicatorTimer.Interval(std::chrono::milliseconds(2000));
bellIndicatorTimer.Tick({ get_weak(), &TerminalTab::_BellIndicatorTimerTick });
bellIndicatorTimer.Start();
_bellIndicatorTimer.emplace(std::move(bellIndicatorTimer));
}
}
@@ -396,21 +381,18 @@ namespace winrt::TerminalApp::implementation
// - <none>
// Return Value:
// - <none>
winrt::fire_and_forget TerminalTab::UpdateTitle()
void TerminalTab::UpdateTitle()
{
auto weakThis{ get_weak() };
co_await wil::resume_foreground(TabViewItem().Dispatcher());
if (auto tab{ weakThis.get() })
{
const auto activeTitle = _GetActiveTitle();
// Bubble our current tab text to anyone who's listening for changes.
Title(activeTitle);
ASSERT_UI_THREAD();
// Update the control to reflect the changed title
_headerControl.Title(activeTitle);
Automation::AutomationProperties::SetName(tab->TabViewItem(), activeTitle);
_UpdateToolTip();
}
const auto activeTitle = _GetActiveTitle();
// Bubble our current tab text to anyone who's listening for changes.
Title(activeTitle);
// Update the control to reflect the changed title
_headerControl.Title(activeTitle);
Automation::AutomationProperties::SetName(TabViewItem(), activeTitle);
_UpdateToolTip();
}
// Method Description:
@@ -421,12 +403,11 @@ namespace winrt::TerminalApp::implementation
// - delta: a number of lines to move the viewport relative to the current viewport.
// Return Value:
// - <none>
winrt::fire_and_forget TerminalTab::Scroll(const int delta)
void TerminalTab::Scroll(const int delta)
{
ASSERT_UI_THREAD();
auto control = GetActiveTerminalControl();
co_await wil::resume_foreground(control.Dispatcher());
const auto currentOffset = control.ScrollOffset();
control.ScrollViewport(::base::ClampAdd(currentOffset, delta));
}
@@ -440,6 +421,8 @@ namespace winrt::TerminalApp::implementation
// - A vector of commands
std::vector<ActionAndArgs> TerminalTab::BuildStartupActions(const bool asContent) const
{
ASSERT_UI_THREAD();
// Give initial ids (0 for the child created with this tab,
// 1 for the child after the first split.
auto state = _rootPane->BuildStartupActions(0, 1, asContent);
@@ -513,6 +496,8 @@ namespace winrt::TerminalApp::implementation
const float splitSize,
std::shared_ptr<Pane> pane)
{
ASSERT_UI_THREAD();
// Add the new event handlers to the new pane(s)
// and update their ids.
pane->WalkTree([&](auto p) {
@@ -560,6 +545,8 @@ namespace winrt::TerminalApp::implementation
// - The removed pane, if the remove succeeded.
std::shared_ptr<Pane> TerminalTab::DetachPane()
{
ASSERT_UI_THREAD();
// if we only have one pane, or the focused pane is the root, remove it
// entirely and close this tab
if (_rootPane == _activePane)
@@ -587,6 +574,8 @@ namespace winrt::TerminalApp::implementation
// - The root pane.
std::shared_ptr<Pane> TerminalTab::DetachRoot()
{
ASSERT_UI_THREAD();
// remove the closed event handler since we are closing the tab
// manually.
_rootPane->Closed(_rootClosedToken);
@@ -613,6 +602,8 @@ namespace winrt::TerminalApp::implementation
// - <none>
void TerminalTab::AttachPane(std::shared_ptr<Pane> pane)
{
ASSERT_UI_THREAD();
// Add the new event handlers to the new pane(s)
// and update their ids.
pane->WalkTree([&](auto p) {
@@ -658,6 +649,8 @@ namespace winrt::TerminalApp::implementation
// - <none>
void TerminalTab::AttachColorPicker(TerminalApp::ColorPickupFlyout& colorPicker)
{
ASSERT_UI_THREAD();
auto weakThis{ get_weak() };
_tabColorPickup = colorPicker;
@@ -696,6 +689,8 @@ namespace winrt::TerminalApp::implementation
// - <none>
void TerminalTab::ToggleSplitOrientation()
{
ASSERT_UI_THREAD();
_rootPane->ToggleSplitOrientation();
}
@@ -703,6 +698,8 @@ namespace winrt::TerminalApp::implementation
// - See Pane::CalcSnappedDimension
float TerminalTab::CalcSnappedDimension(const bool widthOrHeight, const float dimension) const
{
ASSERT_UI_THREAD();
return _rootPane->CalcSnappedDimension(widthOrHeight, dimension);
}
@@ -715,6 +712,8 @@ namespace winrt::TerminalApp::implementation
// - <none>
void TerminalTab::ResizePane(const ResizeDirection& direction)
{
ASSERT_UI_THREAD();
// NOTE: This _must_ be called on the root pane, so that it can propagate
// throughout the entire tree.
_rootPane->ResizePane(direction);
@@ -730,6 +729,8 @@ namespace winrt::TerminalApp::implementation
// to the terminal when no other panes are present (GH#6219)
bool TerminalTab::NavigateFocus(const FocusDirection& direction)
{
ASSERT_UI_THREAD();
// NOTE: This _must_ be called on the root pane, so that it can propagate
// throughout the entire tree.
if (const auto newFocus = _rootPane->NavigateDirection(_activePane, direction, _mruPanes))
@@ -760,6 +761,8 @@ namespace winrt::TerminalApp::implementation
// - true if two panes were swapped.
bool TerminalTab::SwapPane(const FocusDirection& direction)
{
ASSERT_UI_THREAD();
// You cannot swap panes with the parent/child pane because of the
// circular reference.
if (direction == FocusDirection::Parent || direction == FocusDirection::Child)
@@ -783,6 +786,8 @@ namespace winrt::TerminalApp::implementation
bool TerminalTab::FocusPane(const uint32_t id)
{
ASSERT_UI_THREAD();
if (_rootPane == nullptr)
{
return false;
@@ -797,6 +802,8 @@ namespace winrt::TerminalApp::implementation
// - Prepares this tab for being removed from the UI hierarchy by shutting down all active connections.
void TerminalTab::Shutdown()
{
ASSERT_UI_THREAD();
if (_rootPane)
{
_rootPane->Shutdown();
@@ -813,22 +820,30 @@ namespace winrt::TerminalApp::implementation
// - <none>
void TerminalTab::ClosePane()
{
ASSERT_UI_THREAD();
_activePane->Close();
}
void TerminalTab::SetTabText(winrt::hstring title)
{
ASSERT_UI_THREAD();
_runtimeTabText = title;
UpdateTitle();
}
winrt::hstring TerminalTab::GetTabText() const
{
ASSERT_UI_THREAD();
return _runtimeTabText;
}
void TerminalTab::ResetTabText()
{
ASSERT_UI_THREAD();
_runtimeTabText = L"";
UpdateTitle();
}
@@ -842,6 +857,8 @@ namespace winrt::TerminalApp::implementation
// - <none>
void TerminalTab::ActivateTabRenamer()
{
ASSERT_UI_THREAD();
_headerControl.BeginRename();
}
@@ -889,7 +906,8 @@ namespace winrt::TerminalApp::implementation
auto dispatcher = TabViewItem().Dispatcher();
ControlEventTokens events{};
events.titleToken = control.TitleChanged([weakThis](auto&&, auto&&) {
events.titleToken = control.TitleChanged([dispatcher, weakThis](auto&&, auto&&) -> winrt::fire_and_forget {
co_await wil::resume_foreground(dispatcher);
// Check if Tab's lifetime has expired
if (auto tab{ weakThis.get() })
{
@@ -899,7 +917,8 @@ namespace winrt::TerminalApp::implementation
}
});
events.colorToken = control.TabColorChanged([weakThis](auto&&, auto&&) {
events.colorToken = control.TabColorChanged([dispatcher, weakThis](auto&&, auto&&) -> winrt::fire_and_forget {
co_await wil::resume_foreground(dispatcher);
if (auto tab{ weakThis.get() })
{
// The control's tabColor changed, but it is not necessarily the
@@ -918,14 +937,16 @@ namespace winrt::TerminalApp::implementation
}
});
events.readOnlyToken = control.ReadOnlyChanged([weakThis](auto&&, auto&&) {
events.readOnlyToken = control.ReadOnlyChanged([dispatcher, weakThis](auto&&, auto&&) -> winrt::fire_and_forget {
co_await wil::resume_foreground(dispatcher);
if (auto tab{ weakThis.get() })
{
tab->_RecalculateAndApplyReadOnly();
}
});
events.focusToken = control.FocusFollowMouseRequested([weakThis](auto&& sender, auto&&) {
events.focusToken = control.FocusFollowMouseRequested([dispatcher, weakThis](auto&& sender, auto&&) -> winrt::fire_and_forget {
co_await wil::resume_foreground(dispatcher);
if (const auto tab{ weakThis.get() })
{
if (tab->_focusState != FocusState::Unfocused)
@@ -955,6 +976,8 @@ namespace winrt::TerminalApp::implementation
// progress percentage of all our panes.
winrt::TerminalApp::TaskbarState TerminalTab::GetCombinedTaskbarState() const
{
ASSERT_UI_THREAD();
std::vector<winrt::TerminalApp::TaskbarState> states;
if (_rootPane)
{
@@ -1114,13 +1137,11 @@ namespace winrt::TerminalApp::implementation
// Add a Closed event handler to the Pane. If the pane closes out from
// underneath us, and it's zoomed, we want to be able to make sure to
// update our state accordingly to un-zoom that pane. See GH#7252.
auto closedToken = pane->Closed([weakThis, weakPane](auto&& /*s*/, auto&& /*e*/) -> winrt::fire_and_forget {
auto closedToken = pane->Closed([weakThis, weakPane](auto&& /*s*/, auto&& /*e*/) {
if (auto tab{ weakThis.get() })
{
if (tab->_zoomedPane)
{
co_await wil::resume_foreground(tab->Content().Dispatcher());
tab->Content(tab->_rootPane->GetRootElement());
tab->ExitZoom();
}
@@ -1133,7 +1154,6 @@ namespace winrt::TerminalApp::implementation
// did not actually change. Triggering
if (pane != tab->_activePane && !tab->_activePane->_IsLeaf())
{
co_await wil::resume_foreground(tab->Content().Dispatcher());
tab->_UpdateActivePane(tab->_activePane);
}
@@ -1387,6 +1407,8 @@ namespace winrt::TerminalApp::implementation
// - The tab's color, if any
std::optional<winrt::Windows::UI::Color> TerminalTab::GetTabColor()
{
ASSERT_UI_THREAD();
std::optional<winrt::Windows::UI::Color> controlTabColor;
if (const auto& control = GetActiveTerminalControl())
{
@@ -1425,6 +1447,8 @@ namespace winrt::TerminalApp::implementation
// - <none>
void TerminalTab::SetRuntimeTabColor(const winrt::Windows::UI::Color& color)
{
ASSERT_UI_THREAD();
_runtimeTabColor.emplace(color);
_RecalculateAndApplyTabColor();
}
@@ -1439,6 +1463,8 @@ namespace winrt::TerminalApp::implementation
// - <none>
void TerminalTab::ResetRuntimeTabColor()
{
ASSERT_UI_THREAD();
_runtimeTabColor.reset();
_RecalculateAndApplyTabColor();
}
@@ -1462,6 +1488,8 @@ namespace winrt::TerminalApp::implementation
// - <none>
void TerminalTab::RequestColorPicker()
{
ASSERT_UI_THREAD();
_ColorPickerRequestedHandlers();
}
@@ -1473,6 +1501,8 @@ namespace winrt::TerminalApp::implementation
// - The total number of leaf panes hosted by this tab.
int TerminalTab::GetLeafPaneCount() const noexcept
{
ASSERT_UI_THREAD();
return _rootPane->GetLeafPaneCount();
}
@@ -1490,6 +1520,8 @@ namespace winrt::TerminalApp::implementation
const float splitSize,
winrt::Windows::Foundation::Size availableSpace) const
{
ASSERT_UI_THREAD();
return _rootPane->PreCalculateCanSplit(_activePane, splitType, splitSize, availableSpace).value_or(std::nullopt);
}
@@ -1501,6 +1533,8 @@ namespace winrt::TerminalApp::implementation
// - <none>
void TerminalTab::UpdateZoom(std::shared_ptr<Pane> newFocus)
{
ASSERT_UI_THREAD();
// clear the existing content so the old zoomed pane can be added back to the root tree
Content(nullptr);
_rootPane->Restore(_zoomedPane);
@@ -1521,6 +1555,8 @@ namespace winrt::TerminalApp::implementation
// - <none>
void TerminalTab::ToggleZoom()
{
ASSERT_UI_THREAD();
if (_zoomedPane)
{
ExitZoom();
@@ -1533,6 +1569,8 @@ namespace winrt::TerminalApp::implementation
void TerminalTab::EnterZoom()
{
ASSERT_UI_THREAD();
// Clear the content first, because with parent focusing it is possible
// to zoom the root pane, but setting the content will not trigger the
// property changed event since it is the same and you would end up with
@@ -1546,6 +1584,8 @@ namespace winrt::TerminalApp::implementation
}
void TerminalTab::ExitZoom()
{
ASSERT_UI_THREAD();
Content(nullptr);
_rootPane->Restore(_zoomedPane);
_zoomedPane = nullptr;
@@ -1556,6 +1596,8 @@ namespace winrt::TerminalApp::implementation
bool TerminalTab::IsZoomed()
{
ASSERT_UI_THREAD();
return _zoomedPane != nullptr;
}
@@ -1565,6 +1607,8 @@ namespace winrt::TerminalApp::implementation
// the same read-only status.
void TerminalTab::TogglePaneReadOnly()
{
ASSERT_UI_THREAD();
auto hasReadOnly = false;
auto allReadOnly = true;
_activePane->WalkTree([&](const auto& p) {
@@ -1642,6 +1686,8 @@ namespace winrt::TerminalApp::implementation
std::shared_ptr<Pane> TerminalTab::GetActivePane() const
{
ASSERT_UI_THREAD();
return _activePane;
}

View File

@@ -28,7 +28,7 @@ namespace winrt::TerminalApp::implementation
void Focus(winrt::Windows::UI::Xaml::FocusState focusState) override;
winrt::fire_and_forget Scroll(const int delta);
void Scroll(const int delta);
std::shared_ptr<Pane> DetachRoot();
std::shared_ptr<Pane> DetachPane();
@@ -41,11 +41,11 @@ namespace winrt::TerminalApp::implementation
std::shared_ptr<Pane> newPane);
void ToggleSplitOrientation();
winrt::fire_and_forget UpdateIcon(const winrt::hstring iconPath);
winrt::fire_and_forget HideIcon(const bool hide);
void UpdateIcon(const winrt::hstring iconPath);
void HideIcon(const bool hide);
winrt::fire_and_forget ShowBellIndicator(const bool show);
winrt::fire_and_forget ActivateBellIndicatorTimer();
void ShowBellIndicator(const bool show);
void ActivateBellIndicatorTimer();
float CalcSnappedDimension(const bool widthOrHeight, const float dimension) const;
std::optional<winrt::Microsoft::Terminal::Settings::Model::SplitDirection> PreCalculateCanSplit(winrt::Microsoft::Terminal::Settings::Model::SplitDirection splitType,
@@ -58,7 +58,7 @@ namespace winrt::TerminalApp::implementation
bool FocusPane(const uint32_t id);
void UpdateSettings();
winrt::fire_and_forget UpdateTitle();
void UpdateTitle();
void Shutdown() override;
void ClosePane();
@@ -155,7 +155,7 @@ namespace winrt::TerminalApp::implementation
void _MakeTabViewItem() override;
winrt::fire_and_forget _UpdateHeaderControlMaxWidth();
void _UpdateHeaderControlMaxWidth();
void _CreateContextMenu() override;
virtual winrt::hstring _CreateToolTipTitle() override;

View File

@@ -961,11 +961,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
auto bufferHeight = _core.BufferHeight();
ScrollBar().Maximum(bufferHeight - bufferHeight);
ScrollBar().Maximum(0);
ScrollBar().Minimum(0);
ScrollBar().Value(0);
ScrollBar().ViewportSize(bufferHeight);
ScrollBar().LargeChange(std::max(bufferHeight - 1, 0)); // scroll one "screenful" at a time when the scroll bar is clicked
ScrollBar().LargeChange(bufferHeight); // scroll one "screenful" at a time when the scroll bar is clicked
// Set up blinking cursor
int blinkTime = GetCaretBlinkTime();

View File

@@ -72,14 +72,21 @@
latest ControlsV2 version of the template can be found at:
https://github.com/microsoft/microsoft-ui-xaml/blob/main/dev/CommonStyles/ScrollBar_themeresources.xaml#L218
We're also removing the corner radius, cause that should be flush
Additionally we have:
* removed the corner radius, because that should be flush
with the top of the window above the TermControl.
* set ScrollBarExpandBeginTime to 0 so that the scrollbar, fades in instantly when it's expanded.
This makes it feel much better with cursors compared to the questionable standard 400ms delay in
the Win11-style WinUI ScrollView. If you also have the "Always show scrollbars" setting enabled in
the settings app (do it if you haven't already), it avoids any and all animations during startup which
makes the app start feel noticeably better and also shaves off another ~167ms of our "busy time".
We're also planning on making this adjustable in the future
(GH#9218), where we might need this anyways.
-->
<x:Double x:Key="ScrollBarSize">16</x:Double>
<x:String x:Key="ScrollBarExpandBeginTime">0</x:String>
<Style x:Key="ForkedScrollbarTemplate"
TargetType="ScrollBar">