From f56157d1a5c0e1dbcd75896bb7709329d3765cfc Mon Sep 17 00:00:00 2001 From: Stephen L Peters Date: Mon, 17 May 2021 21:41:21 -0700 Subject: [PATCH] Update CommandBarFlyout close and open animations. (#4988) * Change CommandBarFlyout's Opening and Closing storyboards to opacity animations from clip animations. * Disable transitions for Visual state changes made while the CommandBarFlyout is opening, we only want to fade in while opening. * Fix Test failure. * Remove the shadow animation with the new opacity animation as it is not required. * Fix shadow issues, turn off drop shadows for V1 styles. * Fix test control CBF test failures * respond to feedback * remove the primary shadow when closing all animation types. --- dev/CommandBarFlyout/CommandBarFlyout.cpp | 63 +++++--- .../CommandBarFlyoutCommandBar.cpp | 142 ++++++++++++------ .../CommandBarFlyoutCommandBar.h | 19 ++- .../CommandBarFlyout_themeresources.xaml | 128 +++++++--------- .../CommandBarFlyout_themeresources_v1.xaml | 11 ++ dev/CommandBarFlyout/TextCommandBarFlyout.cpp | 13 +- dev/Generated/TreeViewItem.properties.cpp | 2 +- ...sableAnimationsStylesOutsideStore_rs2.xaml | 28 ---- 8 files changed, 236 insertions(+), 170 deletions(-) diff --git a/dev/CommandBarFlyout/CommandBarFlyout.cpp b/dev/CommandBarFlyout/CommandBarFlyout.cpp index d3833fdae7..b5203d3d1f 100644 --- a/dev/CommandBarFlyout/CommandBarFlyout.cpp +++ b/dev/CommandBarFlyout/CommandBarFlyout.cpp @@ -127,22 +127,41 @@ CommandBarFlyout::CommandBarFlyout() if (auto commandBar = m_commandBar.get()) { - // If we don't have IFlyoutBase5 available, then we assume a standard show mode. - if (!thisAsFlyoutBase5 || thisAsFlyoutBase5.ShowMode() == winrt::FlyoutShowMode::Standard) - { - commandBar.IsOpen(true); - } - + // If we are in AlwaysExpanded mode then we want to make sure we open in Standard ShowMode + // Otherwise the timing on the creation of the two drops shows is such that the primary items + // draw their shadow on top of the secondary items. // When CommandBarFlyout is in AlwaysOpen state, don't show the overflow button if (AlwaysExpanded()) { - commandBar.IsOpen(true); commandBar.OverflowButtonVisibility(winrt::Windows::UI::Xaml::Controls::CommandBarOverflowButtonVisibility::Collapsed); + if (thisAsFlyoutBase5) + { + thisAsFlyoutBase5.ShowMode(winrt::FlyoutShowMode::Standard); + } } else { commandBar.OverflowButtonVisibility(winrt::Windows::UI::Xaml::Controls::CommandBarOverflowButtonVisibility::Auto); } + SharedHelpers::QueueCallbackForCompositionRendering( + [strongThis = get_strong(), thisAsFlyoutBase5, commandBar] + { + if (auto const commandBarFlyoutCommandBar = winrt::get_self(commandBar)) + { + auto const scopeGuard = gsl::finally([commandBarFlyoutCommandBar]() + { + commandBarFlyoutCommandBar->m_commandBarFlyoutIsOpening = false; + }); + commandBarFlyoutCommandBar->m_commandBarFlyoutIsOpening = true; + + // If we don't have IFlyoutBase5 available, then we assume a standard show mode. + if (!thisAsFlyoutBase5 || thisAsFlyoutBase5.ShowMode() == winrt::FlyoutShowMode::Standard) + { + commandBar.IsOpen(true); + } + } + } + ); } if (m_primaryCommands.Size() > 0) @@ -170,6 +189,9 @@ CommandBarFlyout::CommandBarFlyout() { if (auto commandBar = winrt::get_self(m_commandBar.get())) { + // We are not able to fade this shadow out with the V2 default opacity closing animition. + // Additionally we drop shadows doing play well with the clip animation of the V1 style. + // So we need to remove it in all cases. RemoveDropShadow(); if (!m_isClosingAfterCloseAnimation && commandBar->HasCloseAnimation()) @@ -184,20 +206,22 @@ CommandBarFlyout::CommandBarFlyout() m_isClosingAfterCloseAnimation = false; }); } + else + { + // If we don't have an animation, close the command bar and thus it's subflyouts. + commandBar->IsOpen(false); + } - // Close commandbar and thus other associated flyouts - commandBar->IsOpen(false); - - //CommandBarFlyoutCommandBar.Closed will be called when - //clicking the more (...) button, we clear the translations - //here - commandBar->ClearShadow(); + //Drop shadows do not play nicely with clip animations, if we are using both, clear the shadow + if (SharedHelpers::Is21H1OrHigher() && commandBar->OpenAnimationKind() == CommandBarFlyoutOpenCloseAnimationKind::Clip) + { + commandBar->ClearShadow(); + } } } }); - // If we didn't close the CommandBar in the Closing event, we want to do it here, - // in order to ensure that we're always starting from a known state when opening the flyout. + // Close the CommandBar in order to ensure that we're always starting from a known state when opening the flyout. Closed({ [this](auto const&, auto const&) { @@ -271,10 +295,7 @@ winrt::Control CommandBarFlyout::CreatePresenter() if (SharedHelpers::Is21H1OrHigher()) { - // Since DropShadows don't play well with the entrance animation for the presenter, - // we'll need to fade it in. This name helps us locate the element to set the fade in - // flag in the OS code. - presenter.Name(L"DropShadowFadeInTarget"); + commandBar->SetPresenter(presenter); // We'll need to remove the presenter's drop shadow on the commandBar's Opening/Closing // because we need it to disappear during its expand/shrink animation when the Overflow is opened. @@ -290,7 +311,7 @@ winrt::Control CommandBarFlyout::CreatePresenter() // We'll only need to do the mid-animation remove/add when the "..." button is // pressed to open/close the overflow. This means we shouldn't do it for AlwaysExpanded // and if there's nothing in the overflow. - if (!AlwaysExpanded() && m_secondaryCommands.Size() > 0) + if (m_secondaryCommands.Size() > 0) { RemoveDropShadow(); } diff --git a/dev/CommandBarFlyout/CommandBarFlyoutCommandBar.cpp b/dev/CommandBarFlyout/CommandBarFlyoutCommandBar.cpp index 4cd7cffd99..9b2e8066ad 100644 --- a/dev/CommandBarFlyout/CommandBarFlyoutCommandBar.cpp +++ b/dev/CommandBarFlyout/CommandBarFlyoutCommandBar.cpp @@ -19,7 +19,7 @@ CommandBarFlyoutCommandBar::CommandBarFlyoutCommandBar() { COMMANDBARFLYOUT_TRACE_INFO(*this, TRACE_MSG_METH, METH_NAME, this); - UpdateUI(); + UpdateUI(!m_commandBarFlyoutIsOpening); // Programmatically focus the first primary command if any, else programmatically focus the first secondary command if any. auto commands = PrimaryCommands().Size() > 0 ? PrimaryCommands() : (SecondaryCommands().Size() > 0 ? SecondaryCommands() : nullptr); @@ -67,7 +67,7 @@ CommandBarFlyoutCommandBar::CommandBarFlyoutCommandBar() { COMMANDBARFLYOUT_TRACE_VERBOSE(*this, TRACE_MSG_METH, METH_NAME, this); - UpdateUI(); + UpdateUI(!m_commandBarFlyoutIsOpening); } }); @@ -108,7 +108,7 @@ CommandBarFlyoutCommandBar::CommandBarFlyoutCommandBar() COMMANDBARFLYOUT_TRACE_VERBOSE(*this, TRACE_MSG_METH, METH_NAME, this); UpdateFlowsFromAndFlowsTo(); - UpdateUI(); + UpdateUI(!m_commandBarFlyoutIsOpening); }); // Since we own these vectors, we don't need to cache the event tokens - @@ -120,7 +120,7 @@ CommandBarFlyoutCommandBar::CommandBarFlyoutCommandBar() COMMANDBARFLYOUT_TRACE_VERBOSE(*this, TRACE_MSG_METH, METH_NAME, this); UpdateFlowsFromAndFlowsTo(); - UpdateUI(); + UpdateUI(!m_commandBarFlyoutIsOpening); } }); @@ -131,7 +131,7 @@ CommandBarFlyoutCommandBar::CommandBarFlyoutCommandBar() m_secondaryItemsRootSized = false; UpdateFlowsFromAndFlowsTo(); - UpdateUI(); + UpdateUI(!m_commandBarFlyoutIsOpening); } }); } @@ -158,8 +158,24 @@ void CommandBarFlyoutCommandBar::OnApplyTemplate() m_overflowPopup.set(GetTemplateChildT(L"OverflowPopup", thisAsControlProtected)); m_secondaryItemsRoot.set(GetTemplateChildT(L"OverflowContentRoot", thisAsControlProtected)); m_moreButton.set(GetTemplateChildT(L"MoreButton", thisAsControlProtected)); - m_openingStoryboard.set(GetTemplateChildT(L"OpeningStoryboard", thisAsControlProtected)); - m_closingStoryboard.set(GetTemplateChildT(L"ClosingStoryboard", thisAsControlProtected)); + m_openingStoryboard.set([this, thisAsControlProtected]() + { + if (auto const opacityStoryBoard = GetTemplateChildT(L"OpeningOpacityStoryboard", thisAsControlProtected)) + { + m_openAnimationKind = CommandBarFlyoutOpenCloseAnimationKind::Opacity; + return opacityStoryBoard; + } + m_openAnimationKind = CommandBarFlyoutOpenCloseAnimationKind::Clip; + return GetTemplateChildT(L"OpeningStoryboard", thisAsControlProtected); + }()); + m_closingStoryboard.set([this, thisAsControlProtected]() + { + if (auto const opacityStoryBoard = GetTemplateChildT(L"ClosingOpacityStoryboard", thisAsControlProtected)) + { + return opacityStoryBoard; + } + return GetTemplateChildT(L"ClosingStoryboard", thisAsControlProtected); + }()); if (auto& overflowPopup = m_overflowPopup.get()) { @@ -195,6 +211,7 @@ void CommandBarFlyoutCommandBar::OnApplyTemplate() AttachEventHandlers(); UpdateFlowsFromAndFlowsTo(); UpdateUI(false /* useTransitions */); + SetPresenterName(m_flyoutPresenter.get()); } void CommandBarFlyoutCommandBar::SetOwningFlyout( @@ -228,7 +245,7 @@ void CommandBarFlyoutCommandBar::AttachEventHandlers() [this](auto const&, auto const&) { m_secondaryItemsRootSized = true; - UpdateUI(); + UpdateUI(!m_commandBarFlyoutIsOpening); } }); @@ -534,7 +551,7 @@ void CommandBarFlyoutCommandBar::UpdateUI( UpdateTemplateSettings(); UpdateVisualState(useTransitions); - UpdateShadow(); + UpdateProjectedShadow(); } void CommandBarFlyoutCommandBar::UpdateVisualState( @@ -617,15 +634,18 @@ void CommandBarFlyoutCommandBar::UpdateVisualState( { if (shouldExpandUp) { + winrt::VisualStateManager::GoToState(*this, L"NoOuterOverflowContentRootShadow", useTransitions); winrt::VisualStateManager::GoToState(*this, L"ExpandedUpWithPrimaryCommands", useTransitions); } else { + winrt::VisualStateManager::GoToState(*this, L"OuterOverflowContentRootShadow", useTransitions); winrt::VisualStateManager::GoToState(*this, L"ExpandedDownWithPrimaryCommands", useTransitions); } } else { + winrt::VisualStateManager::GoToState(*this, L"OuterOverflowContentRootShadow", useTransitions); if (shouldExpandUp) { winrt::VisualStateManager::GoToState(*this, L"ExpandedUpWithoutPrimaryCommands", useTransitions); @@ -638,6 +658,7 @@ void CommandBarFlyoutCommandBar::UpdateVisualState( } else { + winrt::VisualStateManager::GoToState(*this, L"NoOuterOverflowContentRootShadow", useTransitions); winrt::VisualStateManager::GoToState(*this, L"Default", useTransitions); winrt::VisualStateManager::GoToState(*this, L"Collapsed", useTransitions); } @@ -1181,62 +1202,93 @@ void CommandBarFlyoutCommandBar::EnsureTabStopUniqueness( } } -void CommandBarFlyoutCommandBar::UpdateShadow() +void CommandBarFlyoutCommandBar::UpdateProjectedShadow() { - if (PrimaryCommands().Size() > 0) + if (SharedHelpers::IsThemeShadowAvailable() && !SharedHelpers::Is21H1OrHigher()) { - AddShadow(); + if (PrimaryCommands().Size() > 0) + { + AddProjectedShadow(); + } + else if (PrimaryCommands().Size() == 0) + { + ClearProjectedShadow(); + } } - else if (PrimaryCommands().Size() == 0) +} + +void CommandBarFlyoutCommandBar::AddProjectedShadow() +{ + //This logic applies to projected shadows, which are the default on < 21H1. + //See additional notes in CommandBarFlyout::CreatePresenter(). + //Apply Shadow on the Grid named "ContentRoot", this is the first element below + //the clip animation of the commandBar. This guarantees that shadow respects the + //animation + winrt::IControlProtected thisAsControlProtected = *this; + auto grid = GetTemplateChildT(L"ContentRoot", thisAsControlProtected); + + if (winrt::IUIElement10 grid_uiElement10 = grid) { - ClearShadow(); + if (!grid_uiElement10.Shadow()) + { + winrt::Windows::UI::Xaml::Media::ThemeShadow shadow; + grid_uiElement10.Shadow(shadow); + + const auto translation = winrt::float3{ grid.Translation().x, grid.Translation().y, 32.0f }; + grid.Translation(translation); + } } } -void CommandBarFlyoutCommandBar::AddShadow() +void CommandBarFlyoutCommandBar::ClearProjectedShadow() { - if (SharedHelpers::IsThemeShadowAvailable() && !SharedHelpers::Is21H1OrHigher()) + // This logic applies to projected shadows, which are the default on < 21H1. + // See additional notes in CommandBarFlyout::CreatePresenter(). + winrt::IControlProtected thisAsControlProtected = *this; + auto grid = GetTemplateChildT(L"ContentRoot", thisAsControlProtected); + if (winrt::IUIElement10 grid_uiElement10 = grid) { - //This logic applies to projected shadows, which are the default on < 21H1. - //See additional notes in CommandBarFlyout::CreatePresenter(). - //Apply Shadow on the Grid named "ContentRoot", this is the first element below - //the clip animation of the commandBar. This guarantees that shadow respects the - //animation - winrt::IControlProtected thisAsControlProtected = *this; - auto grid = GetTemplateChildT(L"ContentRoot", thisAsControlProtected); - - if (winrt::IUIElement10 grid_uiElement10 = grid) + if (grid_uiElement10.Shadow()) { - if (!grid_uiElement10.Shadow()) - { - winrt::Windows::UI::Xaml::Media::ThemeShadow shadow; - grid_uiElement10.Shadow(shadow); + grid_uiElement10.Shadow(nullptr); - const auto translation = winrt::float3{ grid.Translation().x, grid.Translation().y, 32.0f }; - grid.Translation(translation); - } + //Undo the elevation + const auto translation = winrt::float3{ grid.Translation().x, grid.Translation().y, 0.0f }; + grid.Translation(translation); } } } + void CommandBarFlyoutCommandBar::ClearShadow() { if (SharedHelpers::IsThemeShadowAvailable() && !SharedHelpers::Is21H1OrHigher()) { - // This logic applies to projected shadows, which are the default on < 21H1. - // See additional notes in CommandBarFlyout::CreatePresenter(). - winrt::IControlProtected thisAsControlProtected = *this; - auto grid = GetTemplateChildT(L"ContentRoot", thisAsControlProtected); - if (winrt::IUIElement10 grid_uiElement10 = grid) - { - if (grid_uiElement10.Shadow()) - { - grid_uiElement10.Shadow(nullptr); + ClearProjectedShadow(); + } + winrt::VisualStateManager::GoToState(*this, L"NoOuterOverflowContentRootShadow", true/*useTransitions*/); +} - //Undo the elevation - const auto translation = winrt::float3{ grid.Translation().x, grid.Translation().y, 0.0f }; - grid.Translation(translation); - } + +void CommandBarFlyoutCommandBar::SetPresenter(winrt::FlyoutPresenter const& presenter) +{ + m_flyoutPresenter = winrt::make_weak(presenter); +} + +void CommandBarFlyoutCommandBar::SetPresenterName(winrt::FlyoutPresenter const& presenter) +{ + // Since DropShadows don't play well with clip entrance animations for the presenter, + // we'll need to fade it in. This name helps us locate the element to set the fade in + // flag in the OS code. + if (presenter) + { + if (OpenAnimationKind() == CommandBarFlyoutOpenCloseAnimationKind::Clip) + { + presenter.Name(L"DropShadowFadeInTarget"); + } + else + { + presenter.Name(L""); } } } diff --git a/dev/CommandBarFlyout/CommandBarFlyoutCommandBar.h b/dev/CommandBarFlyout/CommandBarFlyoutCommandBar.h index 53b473b256..6f200d0598 100644 --- a/dev/CommandBarFlyout/CommandBarFlyoutCommandBar.h +++ b/dev/CommandBarFlyout/CommandBarFlyoutCommandBar.h @@ -7,6 +7,12 @@ #include "CommandBarFlyoutCommandBar.properties.h" #include "CommandBarFlyoutTrace.h" +enum class CommandBarFlyoutOpenCloseAnimationKind +{ + Opacity, + Clip +}; + class CommandBarFlyoutCommandBar : public ReferenceTracker, public CommandBarFlyoutCommandBarProperties @@ -20,6 +26,7 @@ class CommandBarFlyoutCommandBar : void SetOwningFlyout(winrt::CommandBarFlyout const& owningFlyout); bool HasOpenAnimation(); + CommandBarFlyoutOpenCloseAnimationKind OpenAnimationKind() { return m_openAnimationKind; }; void PlayOpenAnimation(); bool HasCloseAnimation(); bool HasSecondaryOpenCloseAnimations(); @@ -28,10 +35,13 @@ class CommandBarFlyoutCommandBar : void BindOwningFlyoutPresenterToCornerRadius(); void ClearShadow(); + void SetPresenter(winrt::FlyoutPresenter const& presenter); // IControlOverrides / IControlOverridesHelper void OnKeyDown(winrt::KeyRoutedEventArgs const& args); + bool m_commandBarFlyoutIsOpening{ false }; + private: void AttachEventHandlers(); void DetachEventHandlers(); @@ -43,6 +53,8 @@ class CommandBarFlyoutCommandBar : void EnsureAutomationSetCountAndPosition(); void EnsureFocusedPrimaryCommand(); + void SetPresenterName(winrt::FlyoutPresenter const& presenter); + static bool IsControlFocusable( winrt::Control const& control, bool checkTabStop); @@ -63,8 +75,9 @@ class CommandBarFlyoutCommandBar : winrt::IObservableVector const& commands, winrt::Control const& moreButton); - void AddShadow(); - void UpdateShadow(); + void AddProjectedShadow(); + void UpdateProjectedShadow(); + void ClearProjectedShadow(); tracker_ref m_primaryItemsRoot{ this }; tracker_ref m_overflowPopup{ this }; @@ -84,6 +97,8 @@ class CommandBarFlyoutCommandBar : tracker_ref m_currentPrimaryItemsEndElement{ this }; tracker_ref m_currentSecondaryItemsStartElement{ this }; + CommandBarFlyoutOpenCloseAnimationKind m_openAnimationKind{ CommandBarFlyoutOpenCloseAnimationKind::Clip }; + weak_ref m_flyoutPresenter{}; tracker_ref m_openingStoryboard{ this }; tracker_ref m_closingStoryboard{ this }; winrt::Storyboard::Completed_revoker m_openingStoryboardCompletedRevoker{}; diff --git a/dev/CommandBarFlyout/CommandBarFlyout_themeresources.xaml b/dev/CommandBarFlyout/CommandBarFlyout_themeresources.xaml index 4d1c7e339d..d0c7249250 100644 --- a/dev/CommandBarFlyout/CommandBarFlyout_themeresources.xaml +++ b/dev/CommandBarFlyout/CommandBarFlyout_themeresources.xaml @@ -762,39 +762,30 @@ - - - - - - - - - + + + - - - - - - - - - - - - - - - - + + + + @@ -864,7 +855,7 @@ - + @@ -938,7 +929,7 @@ - + @@ -1041,10 +1032,10 @@ - + - + @@ -1069,10 +1060,10 @@ - + - + @@ -1102,11 +1093,11 @@ - + - + @@ -1138,11 +1129,11 @@ - + - + @@ -1161,8 +1152,8 @@ - - + + @@ -1174,10 +1165,9 @@ - - + + - @@ -1188,10 +1178,9 @@ - - + + - @@ -1202,14 +1191,21 @@ - - + + - + + + + + + + + - - - - - - - - + and another that we can animate. --> @@ -1282,17 +1271,12 @@ + - - - - - - - - + and another that we can animate. --> diff --git a/dev/CommandBarFlyout/CommandBarFlyout_themeresources_v1.xaml b/dev/CommandBarFlyout/CommandBarFlyout_themeresources_v1.xaml index dd25f948b1..8d8b3dd8a9 100644 --- a/dev/CommandBarFlyout/CommandBarFlyout_themeresources_v1.xaml +++ b/dev/CommandBarFlyout/CommandBarFlyout_themeresources_v1.xaml @@ -9,6 +9,7 @@ xmlns:contract7Present="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractPresent(Windows.Foundation.UniversalApiContract,7)" xmlns:contract7NotPresent="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractNotPresent(Windows.Foundation.UniversalApiContract,7)" xmlns:contract8Present="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractPresent(Windows.Foundation.UniversalApiContract,8)" + xmlns:contract12Present="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractPresent(Windows.Foundation.UniversalApiContract,12)" xmlns:contract14NotPresent="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractNotPresent(Windows.Foundation.UniversalApiContract,14)"> @@ -32,6 +33,8 @@ + +