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

Add an animation to pane entrance/exit #7364

Merged
23 commits merged into from
Oct 9, 2020
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
ebf75ed
Move the dll project to /dll and move the lib project up
zadjii-msft Aug 20, 2020
cb468e6
Huh, this definitely creates an animation, but it's got HORRIFYING pe…
zadjii-msft Aug 20, 2020
22a8b4d
animating one split > animating 0 splits
zadjii-msft Aug 20, 2020
d7e872c
hot damn this is so C R I S P
zadjii-msft Aug 20, 2020
d472fe5
Add a bunch of code cleanup
zadjii-msft Aug 20, 2020
dcbba94
Try animating both the first and second panes
zadjii-msft Aug 21, 2020
a09ec80
Pane.cpp is ready for review, just gotta add the setting now
zadjii-msft Aug 21, 2020
522ab15
add a setting to disable animations, ready for review
zadjii-msft Aug 21, 2020
f48fbfe
Merge remote-tracking branch 'origin/master' into dev/migrie/f/panes-…
zadjii-msft Aug 21, 2020
188c969
okay bot
zadjii-msft Aug 21, 2020
facc672
Merge remote-tracking branch 'origin/master' into dev/migrie/f/panes-…
zadjii-msft Aug 26, 2020
fde5dca
holy shit it finally worked
zadjii-msft Aug 27, 2020
38ddc35
Update this to look more crisp
zadjii-msft Aug 27, 2020
ae04952
Clean up this code for review
zadjii-msft Aug 27, 2020
bcb675f
gottem
zadjii-msft Aug 27, 2020
3c7d53f
Merge remote-tracking branch 'origin/master' into dev/migrie/f/panes-…
zadjii-msft Aug 28, 2020
cddf16c
disable this animation when animations are disabled in the OS
zadjii-msft Aug 28, 2020
994d7e0
spellcheck nits
zadjii-msft Sep 29, 2020
984a309
Merge remote-tracking branch 'origin/master' into dev/migrie/f/panes-…
zadjii-msft Sep 29, 2020
d95e5c4
Merge remote-tracking branch 'origin/master' into dev/migrie/f/panes-…
zadjii-msft Oct 5, 2020
1653fa3
Some merge conflicts fromt the month I was awaty
zadjii-msft Oct 5, 2020
5f09776
Merge remote-tracking branch 'origin/master' into dev/migrie/f/panes-…
zadjii-msft Oct 7, 2020
082be75
do something to make the azp run
DHowett Oct 9, 2020
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
5 changes: 5 additions & 0 deletions doc/cascadia/profiles.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,11 @@
"description": "When set to `true`, the color and font formatting of selected text is also copied to your clipboard. When set to `false`, only plain text is copied to your clipboard. An array of specific formats can also be used. Supported array values include `html` and `rtf`. Plain text is always copied.",
"$ref": "#/definitions/CopyFormat"
},
"disableAnimations": {
"default": false,
"description": "When set to `true`, visual animations will be disabled across the application.",
zadjii-msft marked this conversation as resolved.
Show resolved Hide resolved
"type": "boolean"
},
"largePasteWarning": {
"default": true,
"description": "When set to true, trying to paste text with more than 5 KiB of characters will display a warning asking you whether to continue or not with the paste.",
Expand Down
3 changes: 3 additions & 0 deletions src/cascadia/TerminalApp/GlobalAppSettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ static constexpr std::string_view ConfirmCloseAllKey{ "confirmCloseAllTabs" };
static constexpr std::string_view SnapToGridOnResizeKey{ "snapToGridOnResize" };
static constexpr std::string_view EnableStartupTaskKey{ "startOnUserLogin" };
static constexpr std::string_view AlwaysOnTopKey{ "alwaysOnTop" };
static constexpr std::string_view DisableAnimationsKey{ "disableAnimations" };

static constexpr std::string_view DebugFeaturesKey{ "debugFeatures" };

Expand Down Expand Up @@ -180,6 +181,8 @@ void GlobalAppSettings::LayerJson(const Json::Value& json)

JsonUtils::GetValueForKey(json, AlwaysOnTopKey, _AlwaysOnTop);

JsonUtils::GetValueForKey(json, DisableAnimationsKey, _DisableAnimations);

// This is a helper lambda to get the keybindings and commands out of both
// and array of objects. We'll use this twice, once on the legacy
// `keybindings` key, and again on the newer `bindings` key.
Expand Down
1 change: 1 addition & 0 deletions src/cascadia/TerminalApp/GlobalAppSettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ class TerminalApp::GlobalAppSettings final
GETSET_PROPERTY(bool, DebugFeaturesEnabled); // default value set in constructor
GETSET_PROPERTY(bool, StartOnUserLogin, false);
GETSET_PROPERTY(bool, AlwaysOnTop, false);
GETSET_PROPERTY(bool, DisableAnimations, false);

private:
std::optional<std::wstring> _unparsedDefaultProfile;
Expand Down
143 changes: 143 additions & 0 deletions src/cascadia/TerminalApp/Pane.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ Pane::Pane(const GUID& profile, const TermControl& control, const bool lastFocus
_SetupResources();
}

// Use the unfocused border color as the pane background, so an actual color
// appears behind panes as we animate them sliding in.
_root.Background(s_unfocusedBorderBrush);

// Register an event with the control to have it inform us when it gains focus.
_gotFocusRevoker = control.GotFocus(winrt::auto_revoke, { this, &Pane::_ControlGotFocusHandler });

Expand Down Expand Up @@ -903,6 +907,143 @@ void Pane::_ApplySplitDefinitions()
}
}

// Method Description:
// - Create a pair of animations when a new control enters this pane. This
// should _ONLY_ be called in _Split, AFTER the first and second child panes
// have been set up.
void Pane::_SetupEntranceAnimation()
{
const bool splitWidth = _splitState == SplitState::Vertical;
const auto totalSize = splitWidth ? _root.ActualWidth() : _root.ActualHeight();
// If we don't have a size yet, it's likely that we're in startup, or we're
// being executed as a sequence of actions. In that case, just skip the
// animation.
if (totalSize <= 0)
{
return;
}

const auto [firstSize, secondSize] = _CalcChildrenSizes(::base::saturated_cast<float>(totalSize));

// WARNING: Don't do this! This won't work
// Duration duration{ std::chrono::milliseconds{ 200 } };
// Instead, make a duration from a TimeSpan from the time in millis
//
// 100ms was chosen because it's quick enough that it doesn't break your
// flow, but not too quick to see
Duration duration = DurationHelper::FromTimeSpan(winrt::Windows::Foundation::TimeSpan(std::chrono::milliseconds(200)));
zadjii-msft marked this conversation as resolved.
Show resolved Hide resolved

// This is safe to capture this, because it's only being called in the
// context of this method (not on another thread)
auto setupAnimation = [&](const auto& size, const bool isFirstChild) {
auto child = isFirstChild ? _firstChild : _secondChild;
auto childGrid = child->_root;
auto control = child->_control;
// 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
// * it'll animate that value using a quadratic function (like f(t) = t^2)
// * IMPORTANT! We'll manually tell the animation that "yes we know what
// we're doing, we want an animation here."
Media::Animation::DoubleAnimation animation{};
animation.Duration(duration);
if (isFirstChild)
{
// If we're animating the first pane, the size should decrease, form the
zadjii-msft marked this conversation as resolved.
Show resolved Hide resolved
// full size down to the given size.
animation.From(totalSize);
animation.To(size);
}
else
{
// Otherwise, we want to show the pane getting larger, so animate
// from 0 to the requested size.
animation.From(0.0);
animation.To(size);
}
animation.EasingFunction(Media::Animation::QuadraticEase{});
animation.EnableDependentAnimation(true);

// Now we're going to set up the Storyboard. This is a unit that uses the
// Animation from above, and actually applies it to a property.
// * we'll set it up for the same duration as the animation we have
// * Apply the animation to the grid of the new pane we're adding to the tree.
// * apply the animation to the Width or Height property.
Media::Animation::Storyboard s;
s.Duration(duration);
s.Children().Append(animation);
s.SetTarget(animation, childGrid);
s.SetTargetProperty(animation, splitWidth ? L"Width" : L"Height");

// BE TRICKY:
// We're animating the width or height of our child pane's grid.
//
// We DON'T want to change the size of the control itself, because the
// terminal has to reflow the buffer every time the control changes size. So
// what we're going to do there is manually set the control's size to how
// big we _actually know_ the control will be.
//
// We're also going to be changing alignment of our child pane and the
// control. This way, we'll be able to have the control stick to the inside
// of the child pane's grid (the side that's moving), while we also have the
// pane's grid stick to "outside" of the grid (the side that's not moving)
if (splitWidth)
{
// If we're animating the first child, then stick to the top/left of
// the parent pane, otherwise use the bottom/right. This is always
// the "outside" of the parent pane.
childGrid.HorizontalAlignment(isFirstChild ? HorizontalAlignment::Left : HorizontalAlignment::Right);
control.HorizontalAlignment(HorizontalAlignment::Left);
control.Width(isFirstChild ? totalSize : size);
}
else
{
// If we're animating the first child, then stick to the top/left of
// the parent pane, otherwise use the bottom/right. This is always
// the "outside" of the parent pane.
childGrid.VerticalAlignment(isFirstChild ? VerticalAlignment::Top : VerticalAlignment::Bottom);
control.VerticalAlignment(VerticalAlignment::Top);
control.Height(isFirstChild ? totalSize : size);
}

// Start the animation.
s.Begin();

std::weak_ptr<Pane> weakThis{ shared_from_this() };
// When the animation is completed, undo the trickiness from before, to
// restore the controls to the behavior they'd usually have.
animation.Completed([weakThis, isFirstChild, splitWidth](auto&&, auto&&) {
if (auto pane{ weakThis.lock() })
{
auto child = isFirstChild ? pane->_firstChild : pane->_secondChild;
auto childGrid = child->_root;
if (auto control = child->_control)
{
if (splitWidth)
{
control.Width(NAN);
childGrid.Width(NAN);
childGrid.HorizontalAlignment(HorizontalAlignment::Stretch);
control.HorizontalAlignment(HorizontalAlignment::Stretch);
}
else
{
control.Height(NAN);
childGrid.Height(NAN);
childGrid.VerticalAlignment(VerticalAlignment::Stretch);
control.VerticalAlignment(VerticalAlignment::Stretch);
}
}
}
});
};

// TODO: GH#7365 - animating the first child right now doesn't _really_ do
// anything. We could do better though.
setupAnimation(firstSize, true);
setupAnimation(secondSize, false);
}

// Method Description:
// - Determines whether the pane can be split
// Arguments:
Expand Down Expand Up @@ -1177,6 +1318,8 @@ std::pair<std::shared_ptr<Pane>, std::shared_ptr<Pane>> Pane::_Split(SplitState

_lastActive = false;

_SetupEntranceAnimation();

return { _firstChild, _secondChild };
}

Expand Down
1 change: 1 addition & 0 deletions src/cascadia/TerminalApp/Pane.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ class Pane : public std::enable_shared_from_this<Pane>

void _CreateRowColDefinitions();
void _ApplySplitDefinitions();
void _SetupEntranceAnimation();
void _UpdateBorders();

bool _Resize(const winrt::TerminalApp::Direction& direction);
Expand Down
5 changes: 5 additions & 0 deletions src/cascadia/TerminalApp/TerminalPage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2041,6 +2041,11 @@ namespace winrt::TerminalApp::implementation
// the alwaysOnTop setting will be lost.
_isAlwaysOnTop = _settings->GlobalSettings().AlwaysOnTop();
_alwaysOnTopChangedHandlers(*this, nullptr);

// Settings AllowDependentAnimations will affect whether animations are
// enabled application-wide, so we don't need to check it each time we
// want to create an animation.
WUX::Media::Animation::Timeline::AllowDependentAnimations(!_settings->GlobalSettings().DisableAnimations());
}

// This is a helper to aid in sorting commands by their `Name`s, alphabetically.
Expand Down
1 change: 1 addition & 0 deletions src/cascadia/TerminalApp/defaults.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"startOnUserLogin": false,
"theme": "system",
"snapToGridOnResize": true,
"disableAnimations": false,

"profiles":
[
Expand Down
1 change: 1 addition & 0 deletions src/cascadia/TerminalApp/pch.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#include <winrt/Windows.UI.Xaml.Controls.Primitives.h>
#include <winrt/Windows.UI.Xaml.Data.h>
#include <winrt/Windows.ui.xaml.media.h>
#include <winrt/Windows.UI.Xaml.Media.Animation.h>
#include <winrt/Windows.ui.xaml.input.h>
#include <winrt/Windows.UI.Xaml.Hosting.h>
#include "winrt/Windows.UI.Xaml.Markup.h"
Expand Down