diff --git a/controls/LICENSE b/controls/LICENSE deleted file mode 100644 index 21071075c2..0000000000 --- a/controls/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ - MIT License - - Copyright (c) Microsoft Corporation. All rights reserved. - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE diff --git a/controls/dev/CommandBarFlyout/CommandBarFlyoutCommandBar.cpp b/controls/dev/CommandBarFlyout/CommandBarFlyoutCommandBar.cpp index b96d96c097..b717f75c3b 100644 --- a/controls/dev/CommandBarFlyout/CommandBarFlyoutCommandBar.cpp +++ b/controls/dev/CommandBarFlyout/CommandBarFlyoutCommandBar.cpp @@ -9,6 +9,11 @@ #include "ResourceAccessor.h" #include "TypeLogging.h" #include "Vector.h" +#include "velocity.h" +#include + +// Bug 47050253: [1.4 servicing] Insiders report that even with the latest bug fixes in the dev channel they can still see the context menu with a transparent background sometimes +#define WINAPPSDK_CHANGEID_47050253 47050253 CommandBarFlyoutCommandBar::CommandBarFlyoutCommandBar() { @@ -159,6 +164,19 @@ CommandBarFlyoutCommandBar::CommandBarFlyoutCommandBar() }); } +CommandBarFlyoutCommandBar::~CommandBarFlyoutCommandBar() +{ + if (WinAppSdk::Containment::IsChangeEnabled()) + { + // The SystemBackdrop DP has already been cleared out. Use our cached field. + if (auto systemBackdrop = m_systemBackdrop.get()) + { + systemBackdrop.OnTargetDisconnected(m_backdropLink); + systemBackdrop.OnTargetDisconnected(m_overflowPopupBackdropLink); + } + } +} + void CommandBarFlyoutCommandBar::OnApplyTemplate() { COMMANDBARFLYOUT_TRACE_INFO(*this, TRACE_MSG_METH, METH_NAME, this); @@ -1419,6 +1437,11 @@ void CommandBarFlyoutCommandBar::OnPropertyChanged(const winrt::DependencyProper oldSystemBackdrop.OnTargetDisconnected(m_overflowPopupBackdropLink); } + if (WinAppSdk::Containment::IsChangeEnabled()) + { + m_systemBackdrop = newSystemBackdrop; + } + if (newSystemBackdrop) { if (!m_backdropLink) diff --git a/controls/dev/CommandBarFlyout/CommandBarFlyoutCommandBar.h b/controls/dev/CommandBarFlyout/CommandBarFlyoutCommandBar.h index 097d14b4a7..dd2085a19d 100644 --- a/controls/dev/CommandBarFlyout/CommandBarFlyoutCommandBar.h +++ b/controls/dev/CommandBarFlyout/CommandBarFlyoutCommandBar.h @@ -19,6 +19,7 @@ class CommandBarFlyoutCommandBar : { public: CommandBarFlyoutCommandBar(); + ~CommandBarFlyoutCommandBar(); // IFrameworkElementOverrides void OnApplyTemplate(); @@ -131,6 +132,12 @@ class CommandBarFlyoutCommandBar : winrt::ContentExternalBackdropLink m_backdropLink{ nullptr }; winrt::ContentExternalBackdropLink m_overflowPopupBackdropLink{ nullptr }; + // A copy of the value in the DependencyProperty. We need to unregister with this SystemBackdrop when this + // CommandBarFlyoutCommandBar is deleted, but the DP value is already cleared by the time we get to Unloaded or the + // dtor, so we cache a copy for ourselves to use during cleanup. Another possibility is to do cleanup during Closed, + // but the app can release and delete this CommandBarFlyoutCommandBar without ever closing it. + weak_ref m_systemBackdrop{ nullptr }; + // Localized string caches. Looking these up from MRTCore is expensive, so we don't want to put the lookups in a // loop. Instead, look them up once, cache them, use the cached values, then clear the cache. The values in these // caches are only valid after CacheLocalizedStringResources and before ClearLocalizedStringResourceCache. diff --git a/dxaml/xcp/components/AccessKeys/inc/ScopeTree.h b/dxaml/xcp/components/AccessKeys/inc/ScopeTree.h index 4b47f1f52c..53a530bdc6 100644 --- a/dxaml/xcp/components/AccessKeys/inc/ScopeTree.h +++ b/dxaml/xcp/components/AccessKeys/inc/ScopeTree.h @@ -331,7 +331,7 @@ namespace AccessKeys { // We successfully found an element to be invoked, but it failed to find a valid pattern. As a result, we will give focus to the element if (!invokeResult.invokeFoundValidPattern) { - if (FocusProperties::IsFocusable(invokedElement.get())) + if (FocusProperties::IsFocusable(invokedElement.get(), false /*ignoreOffScreenPosition*/)) { const Focus::FocusMovementResult result = m_pFocusManager->SetFocusedElement( Focus::FocusMovement( diff --git a/dxaml/xcp/components/elements/UIElement.cpp b/dxaml/xcp/components/elements/UIElement.cpp index cc399c7bc5..995cefe65f 100644 --- a/dxaml/xcp/components/elements/UIElement.cpp +++ b/dxaml/xcp/components/elements/UIElement.cpp @@ -123,7 +123,8 @@ CUIElement::CUIElement(_In_ CCoreServices* core) , m_childBoundsDirty(TRUE) , m_combinedInnerBoundsDirty(TRUE) , m_outerBoundsDirty(TRUE) - , m_skipFocusSubtree() + , m_skipFocusSubtree_OffScreenPosition() + , m_skipFocusSubtree_Other() , m_contentInnerBounds() , m_childBounds() , m_combinedInnerBounds() diff --git a/dxaml/xcp/components/focus/FocusProperties/FocusProperties.cpp b/dxaml/xcp/components/focus/FocusProperties/FocusProperties.cpp index 955cf56680..19a87af533 100644 --- a/dxaml/xcp/components/focus/FocusProperties/FocusProperties.cpp +++ b/dxaml/xcp/components/focus/FocusProperties/FocusProperties.cpp @@ -109,13 +109,13 @@ bool IsEnabled(_In_ CDependencyObject* const object) //Determine if a particular DependencyObject cares to take focus. template<> -bool IsFocusable(_In_ CDependencyObject* const object) +bool IsFocusable(_In_ CDependencyObject* const object, bool ignoreOffScreenPosition) { bool isFocusable = false; xref_ptr objectAsUI; - if (SUCCEEDED(DoPointerCast(objectAsUI, object)) && objectAsUI->SkipFocusSubtree()) + if (SUCCEEDED(DoPointerCast(objectAsUI, object)) && objectAsUI->SkipFocusSubtree(ignoreOffScreenPosition)) { return false; } @@ -125,7 +125,7 @@ bool IsFocusable(_In_ CDependencyObject* const object) CControl* control = static_cast(object); ASSERT(control); - isFocusable = IsFocusable(control); + isFocusable = IsFocusable(control, ignoreOffScreenPosition); } else if (IFocusable* ifocusable = CFocusableHelper::GetIFocusableForDO(object)) { @@ -135,7 +135,7 @@ bool IsFocusable(_In_ CDependencyObject* const object) { if (objectAsUI) { - isFocusable = IsFocusable(objectAsUI.get()); + isFocusable = IsFocusable(objectAsUI.get(), ignoreOffScreenPosition); } } @@ -207,7 +207,7 @@ bool CanHaveFocusableChildren(_In_ CDependencyObject* const p child->OfTypeByIndex() || child->OfTypeByIndex()) { - if (IsFocusable(child.get()) && IsPotentialTabStop(child.get())) + if (IsFocusable(child.get(), false /*ignoreOffScreenPosition*/) && IsPotentialTabStop(child.get())) { isFocusable = true; } @@ -221,7 +221,7 @@ bool CanHaveFocusableChildren(_In_ CDependencyObject* const p } else { - if (IsFocusable(child.get())) + if (IsFocusable(child.get(), false /*ignoreOffScreenPosition*/)) { isFocusable = true; } diff --git a/dxaml/xcp/components/focus/XYFocus/inc/Focusability.h b/dxaml/xcp/components/focus/XYFocus/inc/Focusability.h index 2c7399ab56..63e79ddb47 100644 --- a/dxaml/xcp/components/focus/XYFocus/inc/Focusability.h +++ b/dxaml/xcp/components/focus/XYFocus/inc/Focusability.h @@ -10,7 +10,7 @@ namespace Focus { namespace XYFocusPrivate { template bool IsValidCandidate(_In_ Element* const element) { - const bool isFocusable = FocusProperties::IsFocusable(element); + const bool isFocusable = FocusProperties::IsFocusable(element, false /*ignoreOffScreenPosition*/); const bool isGamepadFocusCandidate = FocusProperties::IsGamepadFocusCandidate(element); const bool isRootScrollViewer = element->template OfTypeByIndex(); const bool isValidTabStop = FocusProperties::IsPotentialTabStop(element); diff --git a/dxaml/xcp/components/focus/inc/FocusProperties.h b/dxaml/xcp/components/focus/inc/FocusProperties.h index d45bb01d84..c4d9cc3641 100644 --- a/dxaml/xcp/components/focus/inc/FocusProperties.h +++ b/dxaml/xcp/components/focus/inc/FocusProperties.h @@ -37,7 +37,7 @@ namespace FocusProperties } template - bool IsFocusable(_In_ DependencyObject* const object) + bool IsFocusable(_In_ DependencyObject* const object, bool ignoreOffScreenPosition) { return object->IsFocusable() == TRUE; } @@ -92,7 +92,7 @@ namespace FocusProperties template<> bool IsVisible(_In_ CDependencyObject* const object); template<> bool AreAllAncestorsVisible(_In_ CDependencyObject* const object); template<> bool IsEnabled(_In_ CDependencyObject* const object); - template<> bool IsFocusable(_In_ CDependencyObject* const object); + template<> bool IsFocusable(_In_ CDependencyObject* const object, bool ignoreOffScreenPosition); template<> bool IsPotentialTabStop(_In_ CDependencyObject* const object); template<> bool CanHaveFocusableChildren(_In_ CDependencyObject* const parent); template<> bool IsFocusEngagementEnabled(_In_ CDependencyObject* const object); diff --git a/dxaml/xcp/components/mrt/ModernResourceProvider.cpp b/dxaml/xcp/components/mrt/ModernResourceProvider.cpp index e0662c5256..d22c037c1f 100644 --- a/dxaml/xcp/components/mrt/ModernResourceProvider.cpp +++ b/dxaml/xcp/components/mrt/ModernResourceProvider.cpp @@ -427,9 +427,43 @@ HRESULT ModernResourceProvider::UpdateLanguageAndLayoutDirectionQualifiers() // Windows components use the Windows display language, can lead to an // inconsistent language experience if WinUI 3 always uses // ApplicationLanguages.Languages. - wchar_t lpLocaleName[LOCALE_NAME_MAX_LENGTH]; - IFC_RETURN(GetUserDefaultLocaleName(lpLocaleName, LOCALE_NAME_MAX_LENGTH)); - primaryLanguageName.Attach(wrl_wrappers::HStringReference(lpLocaleName).Get()); + // We need to use GetUserDefaultUILanguage() to get the Windows display language, + // GetUserDefaultLocaleName() will return the "Regional format"/sort order instead + // if it has been set which could also be inconsistent with other OS UI. + // Example of setting the sort order: + // Win+R -> intl.cpl -> "Change sorting method" -> "Format": "German (Germany)" -> + // "Change sorting method" -> "Select the sorting method:" "Phone book (DIN)" + boolean isLocaleValid = false; + boolean isWellFormedLanguage = false; + + wrl::ComPtr languageStatics; + IFC_RETURN(wf::GetActivationFactory( + wrl_wrappers::HStringReference(RuntimeClass_Windows_Globalization_Language).Get(), + &languageStatics)); + + wchar_t lpLocaleName[LOCALE_NAME_MAX_LENGTH] = { 0 }; + isLocaleValid = GetLocaleInfo(MAKELCID(GetUserDefaultUILanguage(), SORT_DEFAULT), LOCALE_SNAME, lpLocaleName, LOCALE_NAME_MAX_LENGTH) != FALSE; + + if (isLocaleValid) + { + IFC_RETURN(languageStatics->IsWellFormed(wrl_wrappers::HStringReference(lpLocaleName).Get(), &isWellFormedLanguage)); + } + + if (isLocaleValid && isWellFormedLanguage) + { + // CopyTo() calls WindowsDuplicateString() which will make a deep copy of the + // underlying fast-pass lpLocaleName buffer, + // making primaryLanguageName safe to use outside of lpLocaleName's scope + wrl_wrappers::HStringReference(lpLocaleName).CopyTo(primaryLanguageName.GetAddressOf()); + } + else + { + // If the Windows display language isn't bcp47 compliant for some reason, fall back to the user's preferred language list. + // This could still be an inconsistent localization experience, but is the next best option. + wrl::ComPtr> languages; + IFC_RETURN(applicationLanguagesStatics->get_Languages(languages.ReleaseAndGetAddressOf())); + IFC_RETURN(languages->GetAt(0, primaryLanguageName.GetAddressOf())); + } } else { diff --git a/dxaml/xcp/core/core/elements/Control.cpp b/dxaml/xcp/core/core/elements/Control.cpp index f2383c020c..52f88715fd 100644 --- a/dxaml/xcp/core/core/elements/Control.cpp +++ b/dxaml/xcp/core/core/elements/Control.cpp @@ -14,6 +14,10 @@ #include "DXamlServices.h" #include "CVisualStateManager2.h" #include +#include + +// Bug 47229546: [1.4 servicing] The File Explorer's Details pane steals focus unexpectedly +#define WINAPPSDK_CHANGEID_47229546 47229546 // Class: CControl // @@ -1483,9 +1487,25 @@ CControl::Enabled( if (pControl == pFocusable) { bool focusUpdated = false; - // If we are trying to set focus in a changing focus event handler, we will end up leaving focus on the disabled control. - // As a result, we fail fast here. This is being tracked by Bug 9840123 - IFCFAILFAST(pControl->Focus(DirectUI::FocusState::Programmatic, false /*animateIfBringIntoView*/, &focusUpdated)); + + if (WinAppSdk::Containment::IsChangeEnabled()) + { + // In 1.4 servicing we started specifying NoActivate here. In this function, we're setting focus just to avoid the state where no + // focus is set, not because the user is interacting with it. We don't want to activate the window for this kind of situation. + IFCFAILFAST(pControl->Focus( + DirectUI::FocusState::Programmatic, + false /*animateIfBringIntoView*/, + &focusUpdated, + DirectUI::FocusNavigationDirection::None, + InputActivationBehavior::NoActivate)); + } + else + { + // If we are trying to set focus in a changing focus event handler, we will end up leaving focus on the disabled control. + // As a result, we fail fast here. This is being tracked by Bug 9840123 + IFCFAILFAST(pControl->Focus(DirectUI::FocusState::Programmatic, false /*animateIfBringIntoView*/, &focusUpdated)); + } + } } } diff --git a/dxaml/xcp/core/core/elements/Popup.cpp b/dxaml/xcp/core/core/elements/Popup.cpp index db9bde268a..9df376e1ee 100644 --- a/dxaml/xcp/core/core/elements/Popup.cpp +++ b/dxaml/xcp/core/core/elements/Popup.cpp @@ -833,7 +833,7 @@ _Check_return_ HRESULT CPopup::Close(bool forceCloseforTreeReset) if (m_pPreviousFocusWeakRef) { CDependencyObject* pPreviousFocus = m_pPreviousFocusWeakRef.lock(); - if (pPreviousFocus && FocusProperties::IsFocusable(pPreviousFocus)) + if (pPreviousFocus && FocusProperties::IsFocusable(pPreviousFocus, false /*ignoreOffScreenPosition*/)) { if (pPreviousFocus->OfTypeByIndex()) { diff --git a/dxaml/xcp/core/core/elements/uielement.cpp b/dxaml/xcp/core/core/elements/uielement.cpp index e03c807726..5f864780ed 100644 --- a/dxaml/xcp/core/core/elements/uielement.cpp +++ b/dxaml/xcp/core/core/elements/uielement.cpp @@ -1578,8 +1578,11 @@ _Check_return_ HRESULT CUIElement::LeaveImpl(_In_ CDependencyObject *pNamescopeO params.fCoercedIsEnabled = FALSE; } - // Clear the skip focus subtree flag. - m_skipFocusSubtree = FALSE; + ASSERT(IsTabNavigationWithVirtualizedItemsSupported() || !m_skipFocusSubtree_OffScreenPosition); + + // Clear the skip focus subtree flags. + m_skipFocusSubtree_OffScreenPosition = false; + m_skipFocusSubtree_Other = false; // When visual tree is being reset, (CCoreServices::ResetVisualTree) no need to coerce values/raise events. if (params.fIsLive && !params.fVisualTreeBeingReset) diff --git a/dxaml/xcp/core/dll/focusmgr.cpp b/dxaml/xcp/core/dll/focusmgr.cpp index 9266042603..9dd0dca6bf 100644 --- a/dxaml/xcp/core/dll/focusmgr.cpp +++ b/dxaml/xcp/core/dll/focusmgr.cpp @@ -19,6 +19,10 @@ #include "InitialFocusSIPSuspender.h" #include "FocusLockOverrideGuard.h" +#include + +// Bug 46833401: [1.4 Servicing] AutoSuggestBox's dropdown flickers when focus moves back to the island +#define WINAPPSDK_CHANGEID_46833401 46833401 #define E_FOCUS_ASYNCOP_INPROGRESS 64L @@ -113,7 +117,11 @@ CFocusManager::SetFocusedElement(_In_ const FocusMovement& movement) if (pFocusedElement == nullptr) { return FocusMovementResult(); } - if(!IsFocusable(pFocusedElement)) + // This is the only occurrence where ignoreOffScreenPosition==True is used. + // For compatibility reasons, an element that is still placed in a ModernCollectionBasePanel's garbage section (because it has not been arranged yet) + // can be focused through the UpdadateFocus call below. This situation can occur when app code invokes UIElement.Focus(FocusState) inside a + // ListViewBase.ContainerContentChanging event handler. + if (!IsFocusable(pFocusedElement, true /*ignoreOffScreenPosition*/)) { pFocusedElement = GetFirstFocusableElement(pFocusedElement); @@ -660,6 +668,30 @@ CFocusManager::ProcessTabStop(_In_ bool bPressedShift, _Out_ bool* bHandled) xref_ptr spNewTabStop; + auto scopeGuard = wil::scope_exit([&] + { + if (bPressedShift) + { + m_isMovingFocusToPreviousTabStop = false; + } + else + { + m_isMovingFocusToNextTabStop = false; + } + }); + + if (CUIElement::IsTabNavigationWithVirtualizedItemsSupported()) + { + if (bPressedShift) + { + m_isMovingFocusToPreviousTabStop = true; + } + else + { + m_isMovingFocusToNextTabStop = true; + } + } + // Get the new tab stoppable element. const bool queryOnly = false; IFC_RETURN(ProcessTabStopInternal(bPressedShift, queryOnly, spNewTabStop.ReleaseAndGetAddressOf())); @@ -1491,9 +1523,9 @@ CFocusManager::NotifyFocusChanged(_In_ bool bringIntoView, _In_ bool animateIfBr //------------------------------------------------------------------------ bool -CFocusManager::IsFocusable(_In_ CDependencyObject *pObject) +CFocusManager::IsFocusable(_In_ CDependencyObject *pObject, bool ignoreOffScreenPosition) { - return FocusProperties::IsFocusable(pObject); + return FocusProperties::IsFocusable(pObject, ignoreOffScreenPosition); } //------------------------------------------------------------------------ @@ -1937,7 +1969,7 @@ CFocusManager::UpdateFocus(_In_ const FocusMovement& movement) } } - ASSERT((pNewFocus == nullptr) || IsFocusable(pNewFocus)); + ASSERT((pNewFocus == nullptr) || IsFocusable(pNewFocus, true /*ignoreOffScreenPosition*/)); // Update the previous focused control pOldFocusedElement = m_pFocusedElement; // Still has reference that will be freed in Cleanup. @@ -2584,9 +2616,50 @@ CDependencyObject* CFocusManager::FindNextFocus( const bool bPressedShift = direction == DirectUI::FocusNavigationDirection::Previous; // Get the move candidate element according to next/previous navigation direction. - if (FAILED(ProcessTabStopInternal(bPressedShift, findFocusOptions.IsQueryOnly(), nextFocusedElement.ReleaseAndGetAddressOf()))) + if (CUIElement::IsTabNavigationWithVirtualizedItemsSupported()) + { + if (findFocusOptions.IsQueryOnly()) + { + if (FAILED(ProcessTabStopInternal(bPressedShift, true /*queryOnly*/, nextFocusedElement.ReleaseAndGetAddressOf()))) + { + return nullptr; + } + } + else + { + auto scopeGuard = wil::scope_exit([&] + { + if (bPressedShift) + { + m_isMovingFocusToPreviousTabStop = false; + } + else if (direction == DirectUI::FocusNavigationDirection::Next) + { + m_isMovingFocusToNextTabStop = false; + } + }); + + if (bPressedShift) + { + m_isMovingFocusToPreviousTabStop = true; + } + else if (direction == DirectUI::FocusNavigationDirection::Next) + { + m_isMovingFocusToNextTabStop = true; + } + + if (FAILED(ProcessTabStopInternal(bPressedShift, false /*queryOnly*/, nextFocusedElement.ReleaseAndGetAddressOf()))) + { + return nullptr; + } + } + } + else { - return nullptr; + if (FAILED(ProcessTabStopInternal(bPressedShift, findFocusOptions.IsQueryOnly(), nextFocusedElement.ReleaseAndGetAddressOf()))) + { + return nullptr; + } } } else @@ -3199,6 +3272,17 @@ _Check_return_ HRESULT CFocusManager::SetWindowFocus( // to the settings app and back. m_pCoreService->SetShouldReevaluateIsAnimationEnabled(true); + if(WinAppSdk::Containment::IsChangeEnabled() && + isFocused && focusedElement && ((m_contentRoot.GetInputManager().GetLastInputDeviceType() == InputDeviceType::Mouse) + || (m_contentRoot.GetInputManager().GetLastInputDeviceType() == InputDeviceType::Touch) + || (m_contentRoot.GetInputManager().GetLastInputDeviceType() == InputDeviceType::Pen))) + { + // When user is coming back to the window, we restore the focus while may be a click event going to some other control, + // so we need to skip a few rendering frames to let the click event be processed to avoid rendering focus state for a control which may be able to lose focus to the clicked control + + m_pCoreService->SkipFrames(5); + } + if (focusedElement == nullptr && isFocused) { // Find the first focusable element from the root diff --git a/dxaml/xcp/core/dll/xcpcore.cpp b/dxaml/xcp/core/dll/xcpcore.cpp index 4e7679a820..32c2c0e4d6 100644 --- a/dxaml/xcp/core/dll/xcpcore.cpp +++ b/dxaml/xcp/core/dll/xcpcore.cpp @@ -86,6 +86,9 @@ // Bug 46468883: [1.4 servicing] Explorer first frame - DesktopWindowXamlSource spends 30ms on RoGetActivationFactory #define WINAPPSDK_CHANGEID_46468883 46468883 +// Bug 46833401: [1.4 Servicing] AutoSuggestBox's dropdown flickers when focus moves back to the island +#define WINAPPSDK_CHANGEID_46833401 46833401 + #undef max using namespace RuntimeFeatureBehavior; @@ -6301,7 +6304,17 @@ CCoreServices::NWDrawTree( IFC(pTimeMgrNoRef->UpdateIATargets()); } - if (isRenderEnabled) + if( WinAppSdk::Containment::IsChangeEnabled() && isRenderEnabled && m_framesToSkip != 0) + { + m_framesToSkip--; + + ITickableFrameScheduler *pFrameScheduler = GetBrowserHost()->GetFrameScheduler(); + if (pFrameScheduler != NULL) + { + IFC(pFrameScheduler->RequestAdditionalFrame(0 /* immediate */, RequestFrameReason::LayoutCompletedNeeded)); + } + } + else if (isRenderEnabled) { bool canSubmitFrame = true; diff --git a/dxaml/xcp/core/inc/corep.h b/dxaml/xcp/core/inc/corep.h index d369775b73..4c705504a9 100644 --- a/dxaml/xcp/core/inc/corep.h +++ b/dxaml/xcp/core/inc/corep.h @@ -840,6 +840,8 @@ class CCoreServices : public IMediaServices, public IParserCoreServices bool HasActiveAnimations(); + void SkipFrames(int amountOfFramesToSkip) { m_framesToSkip = amountOfFramesToSkip; }; + // TransitionTargets can only be created during OnBegin of a DynamicTimeline void SetAllowTransitionTargetCreation(_In_ bool allow) { m_allowTransitionTargetsToBeCreated = allow; } bool IsAllowingTransitionTargetCreations() { return m_allowTransitionTargetsToBeCreated; } @@ -1733,6 +1735,7 @@ class CCoreServices : public IMediaServices, public IParserCoreServices public: XUINT32 m_uFrameNumber; + int m_framesToSkip = 0; // If the render walk encountered an error while rendering some element, it can choose to request a subsequent // frame to try rendering the element again. In that case, we don't want to submit the frame where we encountered diff --git a/dxaml/xcp/core/inc/focusmgr.h b/dxaml/xcp/core/inc/focusmgr.h index ab6428c7d2..05986da4e8 100644 --- a/dxaml/xcp/core/inc/focusmgr.h +++ b/dxaml/xcp/core/inc/focusmgr.h @@ -93,6 +93,20 @@ class CFocusManager final CDependencyObject* GetFirstFocusableElementFromRoot(_In_ bool bReverse); + bool IsMovingFocusToNextTabStop() const + { + ASSERT(CUIElement::IsTabNavigationWithVirtualizedItemsSupported()); + + return m_isMovingFocusToNextTabStop; + } + + bool IsMovingFocusToPreviousTabStop() const + { + ASSERT(CUIElement::IsTabNavigationWithVirtualizedItemsSupported()); + + return m_isMovingFocusToPreviousTabStop; + } + bool CanProcessTabStop(_In_ bool isShiftPressed); bool CanTabOutOfPlugin() { return m_bCanTabOutOfPlugin; } @@ -125,7 +139,7 @@ class CFocusManager final public: bool IsFocusedElementInPopup() { return m_pFocusedElement && GetRootOfPopupSubTree(m_pFocusedElement); } - bool IsFocusable(_In_ CDependencyObject *pObject ); + bool IsFocusable(_In_ CDependencyObject *pObject, bool ignoreOffScreenPosition = false); FocusObserver* GetFocusObserverNoRef(); @@ -291,6 +305,9 @@ class CFocusManager final bool m_isPrevFocusTextControl; + bool m_isMovingFocusToNextTabStop = false; + bool m_isMovingFocusToPreviousTabStop = false; + xref_ptr m_spEngagedControl; // focusRectManager is responsible for drawing the focus rectangle diff --git a/dxaml/xcp/core/inc/uielement.h b/dxaml/xcp/core/inc/uielement.h index 13bab9bd23..7b0e604b00 100644 --- a/dxaml/xcp/core/inc/uielement.h +++ b/dxaml/xcp/core/inc/uielement.h @@ -27,6 +27,10 @@ #include "AutomationEventsHelper.h" #include #include "xcpmath.h" +#include + +// Bug 47048404: [1.4 servicing] Support ListViewBase.TabNavigation=Local/Cycle with virtualized items +#define WINAPPSDK_CHANGEID_47048404 47048404 #undef GetFirstChild @@ -789,14 +793,26 @@ class CUIElement : public CDependencyObject return !!m_fUseLayoutRounding; } - void SetSkipFocusSubtree(bool skipFocusSubtree) + void SetSkipFocusSubtree(bool skipFocusSubtree, bool forOffScreenPosition = false) { - m_skipFocusSubtree = skipFocusSubtree; + if (forOffScreenPosition) + { + // forOffScreenPosition can only be True when ModernCollectionBasePanel::IsTabNavigationWithVirtualizedItemsSupported() returns True. + ASSERT(IsTabNavigationWithVirtualizedItemsSupported()); + m_skipFocusSubtree_OffScreenPosition = skipFocusSubtree; + } + else + { + m_skipFocusSubtree_Other = skipFocusSubtree; + } } - bool SkipFocusSubtree() const + bool SkipFocusSubtree(bool ignoreOffScreenPosition = false) const { - return m_skipFocusSubtree; + // m_skipFocusSubtree_OffScreenPosition can only be True when ModernCollectionBasePanel::IsTabNavigationWithVirtualizedItemsSupported() returns True. + ASSERT(IsTabNavigationWithVirtualizedItemsSupported() || !m_skipFocusSubtree_OffScreenPosition); + + return (!ignoreOffScreenPosition && m_skipFocusSubtree_OffScreenPosition) || m_skipFocusSubtree_Other; } // Inherited text property support @@ -3127,9 +3143,11 @@ class CUIElement : public CDependencyObject unsigned int m_combinedInnerBoundsDirty : 1; unsigned int m_outerBoundsDirty : 1; - // If true focusmgr does not set the focus on children or the element. Notice that this flag only and only - // regulates the focusmanager tab behavior. - unsigned int m_skipFocusSubtree : 1; + // If any of these flags is True, CFocusManager does not set the focus on children or the element itself. Notice that these flags only + // regulate the focus manager tab behavior. + unsigned int m_skipFocusSubtree_OffScreenPosition : 1; // Set to True when the element is placed off screen at position ModernCollectionBasePanel::GetOffScreenPosition. + unsigned int m_skipFocusSubtree_Other : 1; // Set to True to skip focus for other reasons (like the element is in a recycle pool or hidden SemanticZoom view). + // m_skipFocusSubtree_OffScreenPosition can only be set when ModernCollectionBasePanel::IsTabNavigationWithVirtualizedItemsSupported() returns True. // True iff CheckAutomaticChanges have been called for this object at least once. // It is to make sure not to call costly UIARaisePropertyChangedEvent during the @@ -3152,11 +3170,11 @@ class CUIElement : public CDependencyObject // Indicates this element has non-zero CornerRadius for any of the 4 corners, this requires a CompNode for rounded corner clipping unsigned int m_requiresCompNodeForRoundedCorners : 1; - unsigned int m_hasLayoutStorage : 1; - // ******************************************************************** // Bitfield group 3 (32 bits) // ******************************************************************** + unsigned int m_hasLayoutStorage : 1; + unsigned int m_hasTransform3D : 1; // Keeps track of if this element has a Transform3D (regardless of depth), so we can properly maintain a comp node. unsigned int m_isNonClippingSubtree : 1; @@ -3228,11 +3246,11 @@ class CUIElement : public CDependencyObject // When true, the RenderWalk avoids culling this element. Used for forcefully walking to hidden elements. Has performance implications, use with care! unsigned int m_forceNoCulling : 1; - // Indicates that this UI element allows drag-and-drop events to pass through it for the purposes of hit-testing. - unsigned int m_allowsDragAndDropPassThrough : 1; // ******************************************************************** // Bitfield group 4 (32 bits) // ******************************************************************** + // Indicates that this UI element allows drag-and-drop events to pass through it for the purposes of hit-testing. + unsigned int m_allowsDragAndDropPassThrough : 1; // Used to store the KeyboardNavigationMode enum set through TabFocusNavigation/TabNavigation. unsigned int m_eKeyboardNavigationMode : 2; @@ -3290,7 +3308,6 @@ class CUIElement : public CDependencyObject /* Remove one of these each time you use a new bit so we can keep track of when we'll pop to a new word of size, which we really should avoid - unsigned int m_unused21 : 1; unsigned int m_unused22 : 1; unsigned int m_unused23 : 1; unsigned int m_unused24 : 1; @@ -3316,6 +3333,12 @@ class CUIElement : public CDependencyObject void RaiseAccessKeyShown(_In_z_ const wchar_t* strPressedKeys); void RaiseAccessKeyHidden(); + static bool IsTabNavigationWithVirtualizedItemsSupported() + { + // Returns True to support ListviewBase.TabNavigation == KeyboardNavigationMode.Cycle and KeyboardNavigationMode.Local with virtualized items. + return WinAppSdk::Containment::IsChangeEnabled(); + } + static _Check_return_ HRESULT TabFocusNavigation( _In_ CDependencyObject *pObject, _In_ XUINT32 cArgs, diff --git a/dxaml/xcp/core/input/KeyTipManager.cpp b/dxaml/xcp/core/input/KeyTipManager.cpp index 157d68aa06..9c52ba56c8 100644 --- a/dxaml/xcp/core/input/KeyTipManager.cpp +++ b/dxaml/xcp/core/input/KeyTipManager.cpp @@ -277,7 +277,7 @@ namespace BoundsHelper { CTextElement* childAsTextElement = do_pointer_cast(child.get()); if (childAsUIElement && - FocusProperties::IsFocusable(child.get()) && + FocusProperties::IsFocusable(child.get(), false /*ignoreOffScreenPosition*/) && !child->OfTypeByIndex()) { XRECTF_RB childBounds; @@ -287,7 +287,7 @@ namespace BoundsHelper { } else if (childAsTextElement && - FocusProperties::IsFocusable(child.get())) + FocusProperties::IsFocusable(child.get(), false /*ignoreOffScreenPosition*/)) { IFC_RETURN(AddBoundsForTextElement( childAsTextElement, diff --git a/dxaml/xcp/dxaml/idl/winrt/controls/microsoft.ui.xaml.controls.controls2.idl b/dxaml/xcp/dxaml/idl/winrt/controls/microsoft.ui.xaml.controls.controls2.idl index f69593009b..08a3188a94 100644 --- a/dxaml/xcp/dxaml/idl/winrt/controls/microsoft.ui.xaml.controls.controls2.idl +++ b/dxaml/xcp/dxaml/idl/winrt/controls/microsoft.ui.xaml.controls.controls2.idl @@ -1925,9 +1925,6 @@ namespace Microsoft.UI.Xaml.Controls static Microsoft.UI.Xaml.DependencyProperty IsDefaultShadowEnabledProperty{ get; }; [contract(Microsoft.UI.Xaml.WinUIContract, 5)] - [feature(Feature_ExperimentalApi)] - [static_name("Microsoft.UI.Xaml.Controls.IMenuFlyoutPresenterStaticsFeature_ExperimentalApi")] - [interface_name("Microsoft.UI.Xaml.Controls.IMenuFlyoutPresenterFeature_ExperimentalApi")] { Microsoft.UI.Xaml.Media.SystemBackdrop SystemBackdrop; static Microsoft.UI.Xaml.DependencyProperty SystemBackdropProperty{ get; }; diff --git a/dxaml/xcp/dxaml/idl/winrt/core/microsoft.ui.xaml.coretypes2.idl b/dxaml/xcp/dxaml/idl/winrt/core/microsoft.ui.xaml.coretypes2.idl index 1fd8be13f1..c8e13c946e 100644 --- a/dxaml/xcp/dxaml/idl/winrt/core/microsoft.ui.xaml.coretypes2.idl +++ b/dxaml/xcp/dxaml/idl/winrt/core/microsoft.ui.xaml.coretypes2.idl @@ -2170,7 +2170,8 @@ namespace Microsoft.UI.Xaml.Core.Direct Popup_PlacementTarget, Popup_SystemBackdrop, FlyoutBase_SystemBackdrop, - Window_SystemBackdrop = 2464, + MenuFlyoutPresenter_SystemBackdrop, + Window_SystemBackdrop, Window_Title, }; diff --git a/dxaml/xcp/dxaml/lib/BuildTreeService_Partial.cpp b/dxaml/xcp/dxaml/lib/BuildTreeService_Partial.cpp index d1492c909e..1efebfa293 100644 --- a/dxaml/xcp/dxaml/lib/BuildTreeService_Partial.cpp +++ b/dxaml/xcp/dxaml/lib/BuildTreeService_Partial.cpp @@ -13,6 +13,11 @@ using namespace DirectUISynonyms; #define ACTIVE 0 #define SUSPENDED 1 +int BuildTreeService::ActiveWorkersCount() const +{ + return m_workers[ACTIVE].size(); +} + _Check_return_ HRESULT BuildTreeService::RegisterWork(_In_ ITreeBuilder* pTreeBuildingElement) { HRESULT hr = S_OK; diff --git a/dxaml/xcp/dxaml/lib/BuildTreeService_Partial.h b/dxaml/xcp/dxaml/lib/BuildTreeService_Partial.h index 6c00c383b2..0b3fedd768 100644 --- a/dxaml/xcp/dxaml/lib/BuildTreeService_Partial.h +++ b/dxaml/xcp/dxaml/lib/BuildTreeService_Partial.h @@ -25,6 +25,9 @@ namespace DirectUI // clear work (on shutdown) _Check_return_ HRESULT ClearWork(); + // returns the number of active workers + int ActiveWorkersCount() const; + private: // stores everyone that likes to be called // first queue: active workers, diff --git a/dxaml/xcp/dxaml/lib/FlyoutBase_partial.cpp b/dxaml/xcp/dxaml/lib/FlyoutBase_partial.cpp index 0106a942b0..0afb4d1d4e 100644 --- a/dxaml/xcp/dxaml/lib/FlyoutBase_partial.cpp +++ b/dxaml/xcp/dxaml/lib/FlyoutBase_partial.cpp @@ -49,6 +49,9 @@ using namespace std::placeholders; // Bug 46253034: [Regression] WinAppSdk 1.4.preview2: Menus don't respect RequestedTheme on their target element, showing white text on a white background #define WINAPPSDK_CHANGEID_46253034 46253034 +// Bug 46832968: [1.4 Servicing] [Cross Product] [EEAP] - "Right-click pop-up menu" hides part of the popup outside the desktop +#define WINAPPSDK_CHANGEID_46832968 46832968 + // #define DBG_FLYOUT #if defined(DBG) && defined(DBG_FLYOUT) @@ -3309,10 +3312,20 @@ _Check_return_ HRESULT FlyoutBase::UpdateTargetPosition( flowDirection, &majorPlacementMode); - FlyoutBase::PreferredJustification justification = GetJustificationFromPlacementMode(placementMode); + FlyoutBase::PreferredJustification justification{}; + + if (!WinAppSdk::Containment::IsChangeEnabled()) + { + justification = GetJustificationFromPlacementMode(placementMode); + } FlyoutBase::MajorPlacementMode originalMajorPlacementMode = majorPlacementMode; - FlyoutBase::PreferredJustification originalJustification = justification; + FlyoutBase::PreferredJustification originalJustification{}; + + if (!WinAppSdk::Containment::IsChangeEnabled()) + { + originalJustification = justification; + } // If the desired placement of the flyout is inside the exclusion area, we'll shift it in the direction of the placement direction // so that it no longer is inside that area. @@ -3423,10 +3436,13 @@ _Check_return_ HRESULT FlyoutBase::UpdateTargetPosition( majorPlacementMode = FlyoutBase::MajorPlacementMode::Left; } - // If we were justified left, we're now justified right. - if (justification == FlyoutBase::PreferredJustification::Left) + if (!WinAppSdk::Containment::IsChangeEnabled()) { - justification = FlyoutBase::PreferredJustification::Right; + // If we were justified left, we're now justified right. + if (justification == FlyoutBase::PreferredJustification::Left) + { + justification = FlyoutBase::PreferredJustification::Right; + } } } } @@ -3464,11 +3480,14 @@ _Check_return_ HRESULT FlyoutBase::UpdateTargetPosition( { majorPlacementMode = FlyoutBase::MajorPlacementMode::Right; } - - // If we were justified right, we're now justified left. - if (justification == FlyoutBase::PreferredJustification::Right) + + if (!WinAppSdk::Containment::IsChangeEnabled()) { - justification = FlyoutBase::PreferredJustification::Left; + // If we were justified right, we're now justified left. + if (justification == FlyoutBase::PreferredJustification::Right) + { + justification = FlyoutBase::PreferredJustification::Left; + } } } } @@ -3487,10 +3506,13 @@ _Check_return_ HRESULT FlyoutBase::UpdateTargetPosition( majorPlacementMode = FlyoutBase::MajorPlacementMode::Right; } - // If we were justified right, we're now justified left. - if (justification == FlyoutBase::PreferredJustification::Right) + if (!WinAppSdk::Containment::IsChangeEnabled()) { - justification = FlyoutBase::PreferredJustification::Left; + // If we were justified right, we're now justified left. + if (justification == FlyoutBase::PreferredJustification::Right) + { + justification = FlyoutBase::PreferredJustification::Left; + } } } else if (isRTL && targetPoint.X + presenterSize.Width >= availableRect.Width) @@ -3504,10 +3526,13 @@ _Check_return_ HRESULT FlyoutBase::UpdateTargetPosition( majorPlacementMode = FlyoutBase::MajorPlacementMode::Left; } - // If we were justified left, we're now justified right. - if (justification == FlyoutBase::PreferredJustification::Left) + if (!WinAppSdk::Containment::IsChangeEnabled()) { - justification = FlyoutBase::PreferredJustification::Right; + // If we were justified left, we're now justified right. + if (justification == FlyoutBase::PreferredJustification::Left) + { + justification = FlyoutBase::PreferredJustification::Right; + } } } } @@ -3524,10 +3549,13 @@ _Check_return_ HRESULT FlyoutBase::UpdateTargetPosition( majorPlacementMode = FlyoutBase::MajorPlacementMode::Bottom; } - // If we were justified bottom, we're now justified top. - if (justification == FlyoutBase::PreferredJustification::Bottom) + if (!WinAppSdk::Containment::IsChangeEnabled()) { - justification = FlyoutBase::PreferredJustification::Top; + // If we were justified bottom, we're now justified top. + if (justification == FlyoutBase::PreferredJustification::Bottom) + { + justification = FlyoutBase::PreferredJustification::Top; + } } } @@ -3568,10 +3596,13 @@ _Check_return_ HRESULT FlyoutBase::UpdateTargetPosition( majorPlacementMode = FlyoutBase::MajorPlacementMode::Top; } - // If we were justified top, we're now justified bottom. - if (justification == FlyoutBase::PreferredJustification::Top) + if (!WinAppSdk::Containment::IsChangeEnabled()) { - justification = FlyoutBase::PreferredJustification::Bottom; + // If we were justified top, we're now justified bottom. + if (justification == FlyoutBase::PreferredJustification::Top) + { + justification = FlyoutBase::PreferredJustification::Bottom; + } } } @@ -3583,42 +3614,109 @@ _Check_return_ HRESULT FlyoutBase::UpdateTargetPosition( // The above accounting may have shifted our position, so we'll account for the exclusion rect again. accountForExclusionRect(); - // Accounting for the exclusion rect is important, but keeping the popup fully in view is more important, if possible. - // If we switched sides but are still not completely in view at this point, then we'll give up, revert back to the - // original placement mode and justification, and put the popup as far in that direction as possible while still remaining in view. - if (originalMajorPlacementMode != majorPlacementMode || - originalJustification != justification) + if (WinAppSdk::Containment::IsChangeEnabled()) { - if (presenterSize.Width <= availableRect.Width && + // Accounting for the exclusion rect is important, but keeping the popup fully in view is more important, if possible. + // If we are still not completely in view at this point, we'll fall back to our original positioning and then + // nudge the popup into full view, as long as it will fit on screen. + const bool presenterIsOffScreenHorizontally = isRTL ? + (horizontalOffset - presenterSize.Width < availableRect.X || + horizontalOffset > availableRect.X + availableRect.Width) : (horizontalOffset < availableRect.X || - horizontalOffset + presenterSize.Width > availableRect.X + availableRect.Width)) + horizontalOffset + presenterSize.Width > availableRect.X + availableRect.Width); + + const bool presenterIsOffScreenVertically = + verticalOffset < availableRect.Y || + verticalOffset + presenterSize.Height > availableRect.Y + availableRect.Height; + + // First we'll put the positioning back and re-account for the exclusion rect. + if ((presenterSize.Width <= availableRect.Width && presenterIsOffScreenHorizontally) || + (presenterSize.Height <= availableRect.Height && presenterIsOffScreenVertically)) { - if (originalMajorPlacementMode == FlyoutBase::MajorPlacementMode::Left || - originalJustification == FlyoutBase::PreferredJustification::Right) + majorPlacementMode = originalMajorPlacementMode; + accountForExclusionRect(); + } + + /// Now we'll nudge the popup into view. + if (presenterSize.Width <= availableRect.Width) + { + // In RTL, the horizontal offset represents the top-right corner instead of the top-left corner, + // so we need to adjust our calculations accordingly. + if (isRTL) { - horizontalOffset = availableRect.X; + if (horizontalOffset - presenterSize.Width < availableRect.X) + { + horizontalOffset = availableRect.X + presenterSize.Width; + } + else if (horizontalOffset > availableRect.X + availableRect.Width) + { + horizontalOffset = availableRect.X + availableRect.Width; + } } else { - horizontalOffset = availableRect.X + availableRect.Width - presenterSize.Width; + if (horizontalOffset < availableRect.X) + { + horizontalOffset = availableRect.X; + } + else if (horizontalOffset + presenterSize.Width > availableRect.X + availableRect.Width) + { + horizontalOffset = availableRect.X + availableRect.Width - presenterSize.Width; + } } } - if (presenterSize.Height <= availableRect.Height && - (verticalOffset < availableRect.Y || - verticalOffset + presenterSize.Height > availableRect.Y + availableRect.Height)) + if (presenterSize.Height <= availableRect.Height) { - if (originalMajorPlacementMode == FlyoutBase::MajorPlacementMode::Top || - originalJustification == FlyoutBase::PreferredJustification::Bottom) + if (verticalOffset < availableRect.Y) { verticalOffset = availableRect.Y; } - else + else if (verticalOffset + presenterSize.Height > availableRect.Y + availableRect.Height) { verticalOffset = availableRect.Y + availableRect.Height - presenterSize.Height; } } } + else + { + // Accounting for the exclusion rect is important, but keeping the popup fully in view is more important, if possible. + // If we switched sides but are still not completely in view at this point, then we'll give up, revert back to the + // original placement mode and justification, and put the popup as far in that direction as possible while still remaining in view. + if (originalMajorPlacementMode != majorPlacementMode || + originalJustification != justification) + { + if (presenterSize.Width <= availableRect.Width && + (horizontalOffset < availableRect.X || + horizontalOffset + presenterSize.Width > availableRect.X + availableRect.Width)) + { + if (originalMajorPlacementMode == FlyoutBase::MajorPlacementMode::Left || + originalJustification == FlyoutBase::PreferredJustification::Right) + { + horizontalOffset = availableRect.X; + } + else + { + horizontalOffset = availableRect.X + availableRect.Width - presenterSize.Width; + } + } + + if (presenterSize.Height <= availableRect.Height && + (verticalOffset < availableRect.Y || + verticalOffset + presenterSize.Height > availableRect.Y + availableRect.Height)) + { + if (originalMajorPlacementMode == FlyoutBase::MajorPlacementMode::Top || + originalJustification == FlyoutBase::PreferredJustification::Bottom) + { + verticalOffset = availableRect.Y; + } + else + { + verticalOffset = availableRect.Y + availableRect.Height - presenterSize.Height; + } + } + } + } IFC_RETURN(m_tpPopup->put_HorizontalOffset(horizontalOffset)); IFC_RETURN(m_tpPopup->put_VerticalOffset(verticalOffset)); diff --git a/dxaml/xcp/dxaml/lib/FocusManager.cpp b/dxaml/xcp/dxaml/lib/FocusManager.cpp index 3cc540c44e..662b573361 100644 --- a/dxaml/xcp/dxaml/lib/FocusManager.cpp +++ b/dxaml/xcp/dxaml/lib/FocusManager.cpp @@ -399,7 +399,7 @@ _Check_return_ HRESULT FocusManagerFactory::TryFocusAsyncImpl( wrl::ComPtr spFocusAsyncOperation = TryFocusAsyncAction::CreateFocusAsyncOperation(movement.GetCorrelationId()); IFCFAILFAST(spFocusAsyncOperation.CopyTo(asyncOperation)); - if (FocusProperties::IsFocusable(static_cast(pElement)->GetHandle()) == false) + if (FocusProperties::IsFocusable(static_cast(pElement)->GetHandle(), false /*ignoreOffScreenPosition*/) == false) { // We need to start and complete the async operation since this is a no-op spFocusAsyncOperation->StartOperation(); diff --git a/dxaml/xcp/dxaml/lib/ListViewBase_Partial.cpp b/dxaml/xcp/dxaml/lib/ListViewBase_Partial.cpp index e5105e7866..aa1e0adf3f 100644 --- a/dxaml/xcp/dxaml/lib/ListViewBase_Partial.cpp +++ b/dxaml/xcp/dxaml/lib/ListViewBase_Partial.cpp @@ -52,6 +52,7 @@ ListViewBase::ListViewBase() , m_currentAutoPanVelocity() , m_lowestPhaseInQueue(-1) , m_budget(BUDGET_MANAGER_DEFAULT_LIMIT) + , m_useUnbudgetedContainerBuild(false) , m_itemsAreTabStops(TRUE) , m_groupsAreTabStops(TRUE) , m_allowDrop(FALSE) @@ -1020,16 +1021,28 @@ _Check_return_ HRESULT ListViewBase::GetFocusableElement( { HRESULT hr = S_OK; BOOLEAN shouldCustomizeTabNavigation = FALSE; + xaml_input::KeyboardNavigationMode navigationMode; *ppFocusable = nullptr; + // ShouldCustomizeTabNavigation returns whether the ItemsPanel is a ModernCollectionBasePanel or not (shouldCustomizeTabNavigation is set to True for modern panels). IFC(ShouldCustomizeTabNavigation(&shouldCustomizeTabNavigation)); + IFC(get_TabNavigation(&navigationMode)); + + const bool isUsingTabNavigationOnce = navigationMode == xaml_input::KeyboardNavigationMode::KeyboardNavigationMode_Once; + // The modern panels are not using GetFirst/GetLast mechanism for controlling tab stop navigation, but rather // ProcessTabStopOverride and ProcessCandidateTabStopOverride as this method works for all scenarios involving // enter, in-LVB and exit transitions. - if (!shouldCustomizeTabNavigation) - { + // IsTabNavigationWithVirtualizedItemsSupported()==True: + // But even for modern panels (i.e. shouldCustomizeTabNavigation == True), when the ListViewBase's TabNavigation is + // Local or Cycle, the default behavior must be overridden as the first (for isBackward==False) or last (for isBackward==True) + // item may not be realized. In those cases, that item must be realized and prepared first and selected as the focusable element. + if (!shouldCustomizeTabNavigation || (IsTabNavigationWithVirtualizedItemsSupported() && !isUsingTabNavigationOnce)) + { + const bool isUsingTabNavigationCycle = navigationMode == xaml_input::KeyboardNavigationMode::KeyboardNavigationMode_Cycle; + bool scrollIntoViewAndPrepareContainer = false; ctl::ComPtr spGroupItem; ctl::ComPtr spFirstFocusableResult; BOOLEAN isGrouping = FALSE; @@ -1041,13 +1054,32 @@ _Check_return_ HRESULT ListViewBase::GetFocusableElement( IFC(get_IsGrouping(&isGrouping)); IFC(HasFocus(&hasFocus)); + if (IsTabNavigationWithVirtualizedItemsSupported() && !isGrouping && shouldCustomizeTabNavigation && !isUsingTabNavigationOnce) + { + CFocusManager* focusManager = VisualTree::GetFocusManagerForElement(GetHandle()); + + ASSERT(focusManager); + + if ((!isBackward && focusManager->IsMovingFocusToNextTabStop()) || (isBackward && focusManager->IsMovingFocusToPreviousTabStop())) + { + // The FocusManager is processing the Tab key to actually navigate to an element (as opposed to only accessing a tab stop). + scrollIntoViewAndPrepareContainer = true; + } + } + // If neither focused item nor focused group exists, retrieve GroupIem for last focused item, if grouping. // If not grouping, get container for last focused item. // Additional check for HasFocus is necessary because focused index, etc. are set on OnGotFocus/LostFocus and this is async, // so even if the indices are set they may be slightly out of sync. Check that focus is within this ListViewBase before using the indices. // There is still a possibility that they will not be up to date if focus was rapidly changed within the ListViewBase, but that is not an important // scenario. + // IsTabNavigationWithVirtualizedItemsSupported()==True: + // Even when HasFocus is True, when TabNavigation is Cycle, the target index may have to be realized and prepared when cycling from the first to + // the last item or vice-versa. + ASSERT(IsTabNavigationWithVirtualizedItemsSupported() || !scrollIntoViewAndPrepareContainer); + if ((m_iFocusedIndex < 0 && m_focusedGroupIndex < 0) || + (isUsingTabNavigationCycle && scrollIntoViewAndPrepareContainer) || !hasFocus) { if (isGrouping) @@ -1063,12 +1095,7 @@ _Check_return_ HRESULT ListViewBase::GetFocusableElement( if (!spGroupItem) { - xaml_input::KeyboardNavigationMode navigationMode; - - IFC(get_TabNavigation(&navigationMode)); - - if (navigationMode == xaml_input::KeyboardNavigationMode::KeyboardNavigationMode_Local || - navigationMode == xaml_input::KeyboardNavigationMode::KeyboardNavigationMode_Cycle) + if (!isUsingTabNavigationOnce) { UINT itemCount = 0; @@ -1076,7 +1103,23 @@ _Check_return_ HRESULT ListViewBase::GetFocusableElement( if (itemCount > 0) { - targetFocusedIndex = (!isBackward) ? 0 : itemCount - 1; + if (IsTabNavigationWithVirtualizedItemsSupported()) + { + ASSERT(!(isUsingTabNavigationCycle && isBackward && m_iFocusedIndex == 0 && !hasFocus)); + ASSERT(!(isUsingTabNavigationCycle && !isBackward && m_iFocusedIndex == itemCount - 1 && !hasFocus)); + + if (!isUsingTabNavigationCycle || // TabNavigation is Local, tabbing into the first item or shift-tabbing into the last one. + (isUsingTabNavigationCycle && !hasFocus) || // TabNavigation is Cycle, hasFocus is False, tabbing into the first item or shift-tabbing into the last one. + (isUsingTabNavigationCycle && isBackward && m_iFocusedIndex == 0) || // TabNavigation is Cycle, hasFocus is True, alter the default behavior when shift-tabbing from the first to the last item. + (isUsingTabNavigationCycle && !isBackward && m_iFocusedIndex == itemCount - 1)) // TabNavigation is Cycle, hasFocus is True, alter the default behavior when tabbing from the last to the first item. + { + targetFocusedIndex = (!isBackward) ? 0 : itemCount - 1; + } + } + else + { + targetFocusedIndex = (!isBackward) ? 0 : itemCount - 1; + } } } else @@ -1094,6 +1137,18 @@ _Check_return_ HRESULT ListViewBase::GetFocusableElement( else if (targetFocusedIndex >= 0) { IFC(ContainerFromIndex(targetFocusedIndex, &spFirstFocusableResult)); + + if (scrollIntoViewAndPrepareContainer) + { + ASSERT(IsTabNavigationWithVirtualizedItemsSupported()); + + ctl::ComPtr itemContainerCandidate = spFirstFocusableResult; + + // Make sure the target index is realized and prepared so it can be declared focusable and tabbed into. + // Since this method brings the target index into view, it is only invoked when the FocusManager is processing + // the Tab key to actually navigate to an element (as opposed to only accessing a tab stop). + IFC(GetScrolledIntoViewAndPreparedContainer(targetFocusedIndex, itemContainerCandidate.Detach(), &spFirstFocusableResult)); + } } *ppFocusable = static_cast(spFirstFocusableResult.Detach()); @@ -1103,6 +1158,92 @@ _Check_return_ HRESULT ListViewBase::GetFocusableElement( RRETURN(hr); } +// Realizes, scrolls into view and prepares the item at the provided index so it can be tabbed into. +_Check_return_ HRESULT ListViewBase::GetScrolledIntoViewAndPreparedContainer( + int index, + _In_opt_ xaml::IDependencyObject* itemContainerCandidate, + _Outptr_result_maybenull_ xaml::IDependencyObject** itemContainer) +{ + ASSERT(IsTabNavigationWithVirtualizedItemsSupported()); + ASSERT(index >= 0); + ASSERT(itemContainer); + + bool isPositionedInGarbageSection = false; + + *itemContainer = itemContainerCandidate; + + if (*itemContainer) + { + // Check if the candidate item is currently off screen so it can be brought into view first. + ctl::ComPtr itemContainerUIE; + + if (SUCCEEDED(ctl::do_query_interface(itemContainerUIE, *itemContainer))) + { + ctl::ComPtr itemsPanel; + + IFC_RETURN(get_ItemsHost(&itemsPanel)); + + if (ctl::is(itemsPanel)) + { + isPositionedInGarbageSection = itemsPanel.Cast()->ElementIsPositionedInGarbageSection(itemContainerUIE); + } + } + } + + if (!*itemContainer || isPositionedInGarbageSection) + { + // Synchronously realize item at the provided index. + IFC_RETURN(Selector::ScrollIntoView( + index, + FALSE /*isGroupItemIndex*/, + FALSE /*isHeader*/, + FALSE /*isFooter*/, + FALSE /*isFromPublicAPI*/, + TRUE /*ensureContainerRealized*/, + FALSE /*animateIfBringIntoView*/, + xaml_controls::ScrollIntoViewAlignment::ScrollIntoViewAlignment_Default)); + + // Retrieve the item now that it was realized. + IFC_RETURN(ContainerFromIndex(index, itemContainer)); + + if (IsTabNavigationWithVirtualizedItemsSupported() && *itemContainer) + { + // This last phase makes sure the target container is fully prepared to be eligible for focus. + ctl::ComPtr buildTreeService; + + IFC_RETURN(DXamlCore::GetCurrent()->GetBuildTreeService(buildTreeService)); + + if (buildTreeService) + { + // Temporarily turning off the new container build budget to guarantee that the target item + // at the provided index is fully prepared (gets its new Content and DataContext) so that it + // can be declared focusable my the FocusManager. Otherwise the default 40ms allocation + // comes short and it may be declared ineligible for focus, not having a Content set. + m_useUnbudgetedContainerBuild = true; + + auto scopeGuard = wil::scope_exit([&] + { + m_useUnbudgetedContainerBuild = false; + }); + + bool workLeft = false; + // Allow as many BuildTrees calls as there are active workers so that the ListViewBase worker + // gets a chance to do its work as it is progressively moved into the front of the active list. + int maxBuildTreesCalls = buildTreeService->ActiveWorkersCount(); + + do + { + IFC_RETURN(buildTreeService->BuildTrees(&workLeft)); + maxBuildTreesCalls--; + } + while (maxBuildTreesCalls > 0 && workLeft); + } + } + } + + return S_OK; +} + // Shared implementation for GetFirstFocusableElementOverride and GetLastFocusableElementOverride. _Check_return_ HRESULT ListViewBase::GetFocusableElementForModernPanel( _In_ BOOLEAN isBackward, diff --git a/dxaml/xcp/dxaml/lib/ListViewBase_Partial.h b/dxaml/xcp/dxaml/lib/ListViewBase_Partial.h index 9c8a52b874..ac123525b5 100644 --- a/dxaml/xcp/dxaml/lib/ListViewBase_Partial.h +++ b/dxaml/xcp/dxaml/lib/ListViewBase_Partial.h @@ -504,6 +504,12 @@ namespace DirectUI _Check_return_ HRESULT LoadMoreItemsIfNeeded( _In_ wf::Size finalSize); + // Realizes, scrolls into view and prepares the item at the provided index so it can be tabbed into. + _Check_return_ HRESULT GetScrolledIntoViewAndPreparedContainer( + int index, + _In_opt_ xaml::IDependencyObject* itemContainerCandidate, + _Outptr_result_maybenull_ xaml::IDependencyObject** itemContainer); + // Shared implementation for GetFirstFocusableElementOverride and GetLastFocusableElementOverride. _Check_return_ HRESULT GetFocusableElementForModernPanel( _In_ BOOLEAN isBackward, @@ -652,6 +658,12 @@ namespace DirectUI return WinAppSdk::Containment::IsChangeEnabled(); } + static bool IsTabNavigationWithVirtualizedItemsSupported() + { + // Returns True to support TabNavigation == KeyboardNavigationMode.Cycle and KeyboardNavigationMode.Local with virtualized items. + return WinAppSdk::Containment::IsChangeEnabled(); + } + // Get the next state from current one in the specified direction. // The order for isBackward = FALSE is: Other -> Header-> Item -> GroupHeader -> Footer -> Other static ElementType GetElementTypeTransition( @@ -1666,6 +1678,11 @@ namespace DirectUI // once we get stl 11, we should convert to std::chrono UINT m_budget; + // exceptionally set to True when (shift) tabbing into an unrealized item to give the BuildTreeService + // enough time to prepare it to receive focus. This is specific to the Local/Cycle TabNavigation modes. + // Only set when IsTabNavigationWithVirtualizedItemsSupported() returns True. + bool m_useUnbudgetedContainerBuild; + // the last known count of containers in the incremental visualization queue. this value is used when logging // our telemetry event in the case where BuildTreeImpl cannot perform work on the busy uiThread. UINT m_lastIncrementalVisualizationContainerCount; diff --git a/dxaml/xcp/dxaml/lib/ListViewBase_Partial_ContainerPhase.cpp b/dxaml/xcp/dxaml/lib/ListViewBase_Partial_ContainerPhase.cpp index b44a21ad11..4a0e872411 100644 --- a/dxaml/xcp/dxaml/lib/ListViewBase_Partial_ContainerPhase.cpp +++ b/dxaml/xcp/dxaml/lib/ListViewBase_Partial_ContainerPhase.cpp @@ -432,6 +432,9 @@ _Check_return_ HRESULT ListViewBase::IsBuildTreeSuspendedImpl(_Out_ BOOLEAN* pRe // the async version of doWork that is being called by NWDrawTree _Check_return_ HRESULT ListViewBase::BuildTreeImpl(_Out_ BOOLEAN *workLeft) noexcept { + // m_useUnbudgetedContainerBuild only expected to be set when IsTabNavigationWithVirtualizedItemsSupported()==True + ASSERT(IsTabNavigationWithVirtualizedItemsSupported() || !m_useUnbudgetedContainerBuild); + INT timeElapsedInMS = 0; ctl::ComPtr budget; UINT containersToClearCount = 0; @@ -441,9 +444,13 @@ _Check_return_ HRESULT ListViewBase::BuildTreeImpl(_Out_ BOOLEAN *workLeft) noex *workLeft = TRUE; IFC_RETURN(DXamlCore::GetCurrent()->GetBudgetManager(budget)); - IFC_RETURN(budget->GetElapsedMilliSecondsSinceLastUITick(&timeElapsedInMS)); - if (static_cast(timeElapsedInMS) <= m_budget) + if (!m_useUnbudgetedContainerBuild) + { + IFC_RETURN(budget->GetElapsedMilliSecondsSinceLastUITick(&timeElapsedInMS)); + } + + if (static_cast(timeElapsedInMS) <= m_budget || m_useUnbudgetedContainerBuild) { IFC_RETURN(get_ItemsHost(&itemsHost)); if (itemsHost) @@ -542,7 +549,7 @@ _Check_return_ HRESULT ListViewBase::BuildTreeImpl(_Out_ BOOLEAN *workLeft) noex IFC_RETURN(ProcessCurrentPosition()); while (processingPhase != std::numeric_limits::max() && - static_cast(timeElapsedInMS) < m_budget) + (static_cast(timeElapsedInMS) < m_budget || m_useUnbudgetedContainerBuild)) { INT64 phase = 0; ctl::ComPtr container; @@ -798,8 +805,12 @@ _Check_return_ HRESULT ListViewBase::BuildTreeImpl(_Out_ BOOLEAN *workLeft) noex { IFC_RETURN(ProcessCurrentPosition()); } - // updates the time - IFC_RETURN(budget->GetElapsedMilliSecondsSinceLastUITick(&timeElapsedInMS)); + + if (!m_useUnbudgetedContainerBuild) + { + // updates the time + IFC_RETURN(budget->GetElapsedMilliSecondsSinceLastUITick(&timeElapsedInMS)); + } } if (processingPhase == std::numeric_limits::max()) diff --git a/dxaml/xcp/dxaml/lib/ModernCollectionBasePanel_IICG2_Partial.cpp b/dxaml/xcp/dxaml/lib/ModernCollectionBasePanel_IICG2_Partial.cpp index ddd55848c2..9091d37e55 100644 --- a/dxaml/xcp/dxaml/lib/ModernCollectionBasePanel_IICG2_Partial.cpp +++ b/dxaml/xcp/dxaml/lib/ModernCollectionBasePanel_IICG2_Partial.cpp @@ -374,7 +374,14 @@ _Check_return_ HRESULT ModernCollectionBasePanel::GenerateContainerAtIndexImpl(_ IFC(LinkContainerToItem(spContainer.Get(), spItem.Get())); } - SetBoundsForElement(spContainer, RectUtil::CreateRect(GetOffScreenPosition(), wf::Size())); + if (IsTabNavigationWithVirtualizedItemsSupported()) + { + SetElementEmptySizeInGarbageSection(spContainer); + } + else + { + SetBoundsForElement(spContainer, RectUtil::CreateRect(GetOffScreenPosition(), wf::Size())); + } if (spContainerFromItemTemplate && !isOwnContainer) { @@ -585,7 +592,15 @@ _Check_return_ HRESULT ModernCollectionBasePanel::GenerateHeaderAtGroupIndexImpl } SetElementIsHeader(spHeader, TRUE); - SetBoundsForElement(spHeader, RectUtil::CreateRect(GetOffScreenPosition(), wf::Size())); + + if (IsTabNavigationWithVirtualizedItemsSupported()) + { + SetElementEmptySizeInGarbageSection(spHeader); + } + else + { + SetBoundsForElement(spHeader, RectUtil::CreateRect(GetOffScreenPosition(), wf::Size())); + } // put it in the pinned containers, so that lookups will work during the Link and Prepare phases if (!foundPinnedHeader) diff --git a/dxaml/xcp/dxaml/lib/ModernCollectionBasePanel_Partial.cpp b/dxaml/xcp/dxaml/lib/ModernCollectionBasePanel_Partial.cpp index 1073d32172..fc79f51c91 100644 --- a/dxaml/xcp/dxaml/lib/ModernCollectionBasePanel_Partial.cpp +++ b/dxaml/xcp/dxaml/lib/ModernCollectionBasePanel_Partial.cpp @@ -615,7 +615,12 @@ _Check_return_ HRESULT ModernCollectionBasePanel::MeasureElementsInGarbageSectio IFC(spChildren->GetAt(garbageIndex, &spCurrentElement)); - const wf::Point position = GetGarbageElementPosition(spCurrentElement); + wf::Point position{}; + + if (!IsTabNavigationWithVirtualizedItemsSupported()) + { + position = GetGarbageElementPosition(spCurrentElement); + } xaml_controls::ElementType elementType = (GetElementIsHeader(spCurrentElement)) ? xaml_controls::ElementType_GroupHeader : xaml_controls::ElementType_ItemContainer; @@ -641,7 +646,14 @@ _Check_return_ HRESULT ModernCollectionBasePanel::MeasureElementsInGarbageSectio IFC(spCurrentElement->get_DesiredSize(&desiredSize)); - SetBoundsForElement(spCurrentElement, RectUtil::CreateRect(position, desiredSize)); + if (IsTabNavigationWithVirtualizedItemsSupported()) + { + SetElementSizeInGarbageSection(spCurrentElement, desiredSize); + } + else + { + SetBoundsForElement(spCurrentElement, RectUtil::CreateRect(position, desiredSize)); + } // Protected by Apiset : TRUE only for Vertical Orientation + Headerplacement!=Left if (m_bUseStickyHeaders) @@ -3877,6 +3889,58 @@ wf::Point ModernCollectionBasePanel::GetGarbageElementPosition(_In_ const ctl::C return result; } +/*static*/ +bool ModernCollectionBasePanel::ElementIsPositionedInGarbageSection(_In_ const ctl::ComPtr& spElement) +{ + ASSERT(IsTabNavigationWithVirtualizedItemsSupported()); + + const wf::Point offScreenPosition = GetOffScreenPosition(); + const wf::Rect bounds = GetBoundsFromElement(spElement); + const bool hasOffScreenPosition = bounds.X == offScreenPosition.X && bounds.Y == offScreenPosition.Y; + + return hasOffScreenPosition; +} + +/*static*/ +void ModernCollectionBasePanel::SetElementEmptySizeInGarbageSection(_In_ const ctl::ComPtr& spElement) +{ + ASSERT(IsTabNavigationWithVirtualizedItemsSupported()); + + SetElementSizeInGarbageSection(spElement, wf::Size()); +} + +/*static*/ +void ModernCollectionBasePanel::SetElementSizeInGarbageSection(_In_ const ctl::ComPtr& spElement, _In_ wf::Size size) +{ + ASSERT(IsTabNavigationWithVirtualizedItemsSupported()); + + UIElement* uielement = spElement.Cast(); + ASSERT(uielement); + + static_cast(uielement->GetHandle())->SetSkipFocusSubtree(true /*skipFocusSubtree*/, true /*forOffScreenPosition*/); + uielement->GetVirtualizationInformation()->SetBounds(RectUtil::CreateRect(GetOffScreenPosition(), size)); +} + +/*static*/ +void ModernCollectionBasePanel::SetBoundsForElement(_In_ const ctl::ComPtr& spElement, _In_ wf::Rect bounds) +{ + UIElement* uielement = spElement.Cast(); + ASSERT(uielement); + + if (IsTabNavigationWithVirtualizedItemsSupported()) + { +#ifdef DBG + const wf::Point offScreenPositionDbg = GetOffScreenPosition(); + const bool hasOffScreenPositionDbg = bounds.X == offScreenPositionDbg.X && bounds.Y == offScreenPositionDbg.Y; + ASSERT(!hasOffScreenPositionDbg); +#endif + + static_cast(uielement->GetHandle())->SetSkipFocusSubtree(false /*skipFocusSubtree*/, true /*forOffScreenPosition*/); + } + + uielement->GetVirtualizationInformation()->SetBounds(bounds); +} + _Check_return_ HRESULT ModernCollectionBasePanel::GetViewportSize(_Out_ wf::Size* pSize) { HRESULT hr = S_OK; diff --git a/dxaml/xcp/dxaml/lib/ModernCollectionBasePanel_Partial.h b/dxaml/xcp/dxaml/lib/ModernCollectionBasePanel_Partial.h index 5c112ee47d..6fe2c07edd 100644 --- a/dxaml/xcp/dxaml/lib/ModernCollectionBasePanel_Partial.h +++ b/dxaml/xcp/dxaml/lib/ModernCollectionBasePanel_Partial.h @@ -237,6 +237,9 @@ namespace DirectUI double AdjustViewportOffsetForDeserialization(double offset); + // Only used when IsTabNavigationWithVirtualizedItemsSupported() returns True. + static bool ElementIsPositionedInGarbageSection(_In_ const ctl::ComPtr& spElement); + protected: // The specifics of the layout is governed by a layout strategy. Subclasses like ItemsWrapGrid // tell us which strategy to use via these methods. @@ -2026,10 +2029,11 @@ namespace DirectUI return spElement.Cast()->GetVirtualizationInformation()->GetBounds(); } - static void SetBoundsForElement(_In_ const ctl::ComPtr& spElement, _In_ wf::Rect bounds) - { - spElement.Cast()->GetVirtualizationInformation()->SetBounds(bounds); - } + // Only used when IsTabNavigationWithVirtualizedItemsSupported() returns True. + static void SetElementEmptySizeInGarbageSection(_In_ const ctl::ComPtr& spElement); + static void SetElementSizeInGarbageSection(_In_ const ctl::ComPtr& spElement, _In_ wf::Size size); + + static void SetBoundsForElement(_In_ const ctl::ComPtr& spElement, _In_ wf::Rect bounds); static void SetElementIsRealized(_In_ const ctl::ComPtr& spElement, bool isRealized) { @@ -2096,6 +2100,12 @@ namespace DirectUI return spElement.Cast()->GetVirtualizationInformation()->GetIsContainerFromTemplateRoot(); } + static bool IsTabNavigationWithVirtualizedItemsSupported() + { + // Returns True to support TabNavigation == KeyboardNavigationMode.Cycle and KeyboardNavigationMode.Local with virtualized items. + return WinAppSdk::Containment::IsChangeEnabled(); + } + _Check_return_ HRESULT RegisterSpecialElementSize(_In_ xaml_controls::ElementType type, _In_ INT index, _In_ wf::Size desiredSize) { HRESULT hr = S_OK; diff --git a/dxaml/xcp/dxaml/lib/UIElement_Partial.h b/dxaml/xcp/dxaml/lib/UIElement_Partial.h index b340a8eb92..4b1a347874 100644 --- a/dxaml/xcp/dxaml/lib/UIElement_Partial.h +++ b/dxaml/xcp/dxaml/lib/UIElement_Partial.h @@ -16,12 +16,16 @@ #include "UIElement.g.h" #include "StickyHeaderWrapper.h" #include +#include // TODO: Give codegen the ability to map non-codegen types to filenames // so it can add the proper #include directives. // Until then, we'll need to pull in core headers in some of our framework partial class headers #include +// Bug 47048404: [1.4 servicing] Support ListViewBase.TabNavigation=Local/Cycle with virtualized items +#define WINAPPSDK_CHANGEID_47048404 47048404 + class CRoutedEventArgs; namespace DirectUI diff --git a/dxaml/xcp/dxaml/lib/synonyms.g.h b/dxaml/xcp/dxaml/lib/synonyms.g.h index b235539745..01c390b072 100644 --- a/dxaml/xcp/dxaml/lib/synonyms.g.h +++ b/dxaml/xcp/dxaml/lib/synonyms.g.h @@ -393,7 +393,7 @@ namespace DirectUISynonyms typedef ABI::Microsoft::UI::Xaml::Controls::IMenuFlyoutItem IMenuFlyoutItem; typedef ABI::Microsoft::UI::Xaml::Controls::IMenuFlyoutItemBase IMenuFlyoutItemBase; typedef ABI::Microsoft::UI::Xaml::Controls::IMenuFlyoutPresenter IMenuFlyoutPresenter; - typedef ABI::Microsoft::UI::Xaml::Controls::IMenuFlyoutPresenterFeature_ExperimentalApi IMenuFlyoutPresenterFeature_ExperimentalApi; + typedef ABI::Microsoft::UI::Xaml::Controls::IMenuFlyoutPresenter2 IMenuFlyoutPresenter2; typedef ABI::Microsoft::UI::Xaml::Controls::IMenuFlyoutSeparator IMenuFlyoutSeparator; typedef ABI::Microsoft::UI::Xaml::Controls::IMenuFlyoutSubItem IMenuFlyoutSubItem; typedef ABI::Microsoft::UI::Xaml::Controls::IPage IPage; diff --git a/dxaml/xcp/dxaml/lib/winrtgeneratedclasses/MenuFlyoutPresenter.g.cpp b/dxaml/xcp/dxaml/lib/winrtgeneratedclasses/MenuFlyoutPresenter.g.cpp index 0b80227944..c338a83165 100644 --- a/dxaml/xcp/dxaml/lib/winrtgeneratedclasses/MenuFlyoutPresenter.g.cpp +++ b/dxaml/xcp/dxaml/lib/winrtgeneratedclasses/MenuFlyoutPresenter.g.cpp @@ -36,12 +36,10 @@ HRESULT DirectUI::MenuFlyoutPresenterGenerated::QueryInterfaceImpl(_In_ REFIID i { *ppObject = static_cast(this); } -#if WI_IS_FEATURE_PRESENT(Feature_ExperimentalApi) - else if (InlineIsEqualGUID(iid, __uuidof(ABI::Microsoft::UI::Xaml::Controls::IMenuFlyoutPresenterFeature_ExperimentalApi)) && Feature_ExperimentalApi::IsEnabled()) + else if (InlineIsEqualGUID(iid, __uuidof(ABI::Microsoft::UI::Xaml::Controls::IMenuFlyoutPresenter2))) { - *ppObject = ctl::interface_cast(this); + *ppObject = ctl::interface_cast(this); } -#endif else { RRETURN(DirectUI::ItemsControl::QueryInterfaceImpl(iid, ppObject)); @@ -169,12 +167,10 @@ HRESULT DirectUI::MenuFlyoutPresenterFactory::QueryInterfaceImpl(_In_ REFIID iid { *ppObject = static_cast(this); } -#if WI_IS_FEATURE_PRESENT(Feature_ExperimentalApi) - else if (InlineIsEqualGUID(iid, __uuidof(ABI::Microsoft::UI::Xaml::Controls::IMenuFlyoutPresenterStaticsFeature_ExperimentalApi))) + else if (InlineIsEqualGUID(iid, __uuidof(ABI::Microsoft::UI::Xaml::Controls::IMenuFlyoutPresenterStatics2))) { - *ppObject = static_cast(this); + *ppObject = static_cast(this); } -#endif else { RRETURN(ctl::BetterAggregableCoreObjectActivationFactory::QueryInterfaceImpl(iid, ppObject)); diff --git a/dxaml/xcp/dxaml/lib/winrtgeneratedclasses/MenuFlyoutPresenter.g.h b/dxaml/xcp/dxaml/lib/winrtgeneratedclasses/MenuFlyoutPresenter.g.h index 72b4e6c7b2..256de18388 100644 --- a/dxaml/xcp/dxaml/lib/winrtgeneratedclasses/MenuFlyoutPresenter.g.h +++ b/dxaml/xcp/dxaml/lib/winrtgeneratedclasses/MenuFlyoutPresenter.g.h @@ -12,20 +12,15 @@ #pragma once #include "ItemsControl.g.h" -#include -#if WI_IS_FEATURE_PRESENT(Feature_ExperimentalApi) -#define FEATURE_EXPERIMENTALAPI_OVERRIDE override -#else -#define FEATURE_EXPERIMENTALAPI_OVERRIDE -#endif + #define __MenuFlyoutPresenter_GUID "a21c5006-9b67-4df9-829a-0ed60a5ad01e" #pragma region forwarders namespace ctl { template - class interface_forwarder< ABI::Microsoft::UI::Xaml::Controls::IMenuFlyoutPresenterFeature_ExperimentalApi, impl_type> final - : public ctl::iinspectable_forwarder_base< ABI::Microsoft::UI::Xaml::Controls::IMenuFlyoutPresenterFeature_ExperimentalApi, impl_type> + class interface_forwarder< ABI::Microsoft::UI::Xaml::Controls::IMenuFlyoutPresenter2, impl_type> final + : public ctl::iinspectable_forwarder_base< ABI::Microsoft::UI::Xaml::Controls::IMenuFlyoutPresenter2, impl_type> { impl_type* This() { return this->This_helper(); } IFACEMETHOD(get_SystemBackdrop)(_Outptr_result_maybenull_ ABI::Microsoft::UI::Xaml::Media::ISystemBackdrop** ppValue) override { return This()->get_SystemBackdrop(ppValue); } @@ -44,9 +39,7 @@ namespace DirectUI public DirectUI::ItemsControl , public ABI::Microsoft::UI::Xaml::Controls::IMenuFlyoutPresenter , public ABI::Microsoft::UI::Xaml::Controls::IMenuPresenter -#if WI_IS_FEATURE_PRESENT(Feature_ExperimentalApi) - , public ctl::forwarder_holder< ABI::Microsoft::UI::Xaml::Controls::IMenuFlyoutPresenterFeature_ExperimentalApi, MenuFlyoutPresenterGenerated > -#endif + , public ctl::forwarder_holder< ABI::Microsoft::UI::Xaml::Controls::IMenuFlyoutPresenter2, MenuFlyoutPresenterGenerated > { friend class DirectUI::MenuFlyoutPresenter; @@ -55,9 +48,7 @@ namespace DirectUI BEGIN_INTERFACE_MAP(MenuFlyoutPresenterGenerated, DirectUI::ItemsControl) INTERFACE_ENTRY(MenuFlyoutPresenterGenerated, ABI::Microsoft::UI::Xaml::Controls::IMenuFlyoutPresenter) INTERFACE_ENTRY(MenuFlyoutPresenterGenerated, ABI::Microsoft::UI::Xaml::Controls::IMenuPresenter) -#if WI_IS_FEATURE_PRESENT(Feature_ExperimentalApi) - INTERFACE_ENTRY(MenuFlyoutPresenterGenerated, ABI::Microsoft::UI::Xaml::Controls::IMenuFlyoutPresenterFeature_ExperimentalApi) -#endif + INTERFACE_ENTRY(MenuFlyoutPresenterGenerated, ABI::Microsoft::UI::Xaml::Controls::IMenuFlyoutPresenter2) END_INTERFACE_MAP(MenuFlyoutPresenterGenerated, DirectUI::ItemsControl) public: @@ -119,16 +110,12 @@ namespace DirectUI public ctl::BetterAggregableCoreObjectActivationFactory , public ABI::Microsoft::UI::Xaml::Controls::IMenuFlyoutPresenterFactory , public ABI::Microsoft::UI::Xaml::Controls::IMenuFlyoutPresenterStatics -#if WI_IS_FEATURE_PRESENT(Feature_ExperimentalApi) - , public ABI::Microsoft::UI::Xaml::Controls::IMenuFlyoutPresenterStaticsFeature_ExperimentalApi -#endif + , public ABI::Microsoft::UI::Xaml::Controls::IMenuFlyoutPresenterStatics2 { BEGIN_INTERFACE_MAP(MenuFlyoutPresenterFactory, ctl::BetterAggregableCoreObjectActivationFactory) INTERFACE_ENTRY(MenuFlyoutPresenterFactory, ABI::Microsoft::UI::Xaml::Controls::IMenuFlyoutPresenterFactory) INTERFACE_ENTRY(MenuFlyoutPresenterFactory, ABI::Microsoft::UI::Xaml::Controls::IMenuFlyoutPresenterStatics) -#if WI_IS_FEATURE_PRESENT(Feature_ExperimentalApi) - INTERFACE_ENTRY(MenuFlyoutPresenterFactory, ABI::Microsoft::UI::Xaml::Controls::IMenuFlyoutPresenterStaticsFeature_ExperimentalApi) -#endif + INTERFACE_ENTRY(MenuFlyoutPresenterFactory, ABI::Microsoft::UI::Xaml::Controls::IMenuFlyoutPresenterStatics2) END_INTERFACE_MAP(MenuFlyoutPresenterFactory, ctl::BetterAggregableCoreObjectActivationFactory) public: @@ -140,7 +127,7 @@ namespace DirectUI // Dependency properties. IFACEMETHOD(get_IsDefaultShadowEnabledProperty)(_Out_ ABI::Microsoft::UI::Xaml::IDependencyProperty** ppValue) override; - IFACEMETHOD(get_SystemBackdropProperty)(_Out_ ABI::Microsoft::UI::Xaml::IDependencyProperty** ppValue) FEATURE_EXPERIMENTALAPI_OVERRIDE; + IFACEMETHOD(get_SystemBackdropProperty)(_Out_ ABI::Microsoft::UI::Xaml::IDependencyProperty** ppValue) override; // Attached properties. diff --git a/dxaml/xcp/tools/XCPTypesAutoGen/Modules/Controls/MenuFlyout.cs b/dxaml/xcp/tools/XCPTypesAutoGen/Modules/Controls/MenuFlyout.cs index 696b6bbaff..a76343c683 100644 --- a/dxaml/xcp/tools/XCPTypesAutoGen/Modules/Controls/MenuFlyout.cs +++ b/dxaml/xcp/tools/XCPTypesAutoGen/Modules/Controls/MenuFlyout.cs @@ -283,8 +283,7 @@ public void ShowAt([Optional] Microsoft.UI.Xaml.UIElement targetElement, Windows [FrameworkTypePattern] [Implements(typeof(Microsoft.UI.Xaml.Controls.IMenuPresenter))] [Guids(ClassGuid = "a21c5006-9b67-4df9-829a-0ed60a5ad01e")] - [Platform("Feature_ExperimentalApi", typeof(Microsoft.UI.Xaml.WinUIContract), Microsoft.UI.Xaml.WinUIContract.LatestVersion)] - [Velocity(Feature = "Feature_ExperimentalApi")] + [Platform(2, typeof(Microsoft.UI.Xaml.WinUIContract), 5)] public class MenuFlyoutPresenter : Microsoft.UI.Xaml.Controls.ItemsControl { @@ -300,7 +299,7 @@ public Microsoft.UI.Xaml.Controls.Primitives.MenuFlyoutPresenterTemplateSettings public MenuFlyoutPresenter() { } [RequiresMultipleAssociationCheck] - [VelocityFeature("Feature_ExperimentalApi")] + [Version(2)] public Microsoft.UI.Xaml.Media.SystemBackdrop SystemBackdrop { get; set; } } } diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index b3cd905c92..fbe0195d3f 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -2,17 +2,17 @@ - + https://dev.azure.com/microsoft/ProjectReunion/_git/WindowsAppSDK 0584534e260a81ebf0a37c37db6faf3dfc82f078 - + https://dev.azure.com/microsoft/LiftedIXP/_git/DCPP - 17b52d8f7c1921025a2d5aeef064d74602e155e9 + aca3f84ba82804f6bbc99aedf97315da44cde57a - + https://dev.azure.com/microsoft/LiftedIXP/_git/DCPP - 17b52d8f7c1921025a2d5aeef064d74602e155e9 + aca3f84ba82804f6bbc99aedf97315da44cde57a diff --git a/eng/projectcaching.props b/eng/projectcaching.props new file mode 100644 index 0000000000..4b96e790de --- /dev/null +++ b/eng/projectcaching.props @@ -0,0 +1,54 @@ + + + $(NugetPackageDirectory)\MemoBuild.$(MemoBuildPackageVersion) + + + false + + + LocalOnly + + + AzurePipelines + $(ProjectRoot)\MemoBuildCache + + + true + + + 1 + + + + + + + $(MemoBuildIdenticalDuplicateOutputPatterns);$(NativeAssemblyPackageLocation)\Microsoft.UI.Xaml\Assets\NoiseAsset_256x256_PNG.png + + + $(MemoBuildIgnoredOutputPatterns);controls\dev\Generated\** + + + $(MemoBuildGlobalPropertiesToIgnore);MUXFinalRelease + + + $(MemoBuildGlobalPropertiesToIgnore);WinUIVersion + + \ No newline at end of file diff --git a/eng/winui-version.props b/eng/winui-version.props index af698b59ae..b304b9bd98 100644 --- a/eng/winui-version.props +++ b/eng/winui-version.props @@ -72,7 +72,7 @@ $(BeforeBuildCompileTargets);BuildWinUISourceRevisionHeader - +