Skip to content

Commit

Permalink
Porting NavigationView Fixes (#8776)
Browse files Browse the repository at this point in the history
* ported product changes from 7842816

* ported product changes from 8042221

* ported over tests

* fix build error

* fix build error

* minor const update

* Fix NVI DataTemplate

* ported PR 8727895

* fix previous port

* added const

* fixed tests
  • Loading branch information
ojhad authored Aug 24, 2023
1 parent 73990b0 commit 9f7c129
Show file tree
Hide file tree
Showing 6 changed files with 613 additions and 19 deletions.
158 changes: 148 additions & 10 deletions dev/NavigationView/NavigationView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1260,6 +1260,18 @@ void NavigationView::OnRepeaterElementPrepared(const winrt::ItemsRepeater& ir, c
winrt::get_self<NavigationViewItem>(nvi)->PropagateDepthToChildren(childDepth);

SetNavigationViewItemRevokers(nvi);

auto item = MenuItemFromContainer(nvi);

if (SelectedItem() == item && IsVisible(nvi))
{
if (m_isSelectionChangedPending && m_pendingSelectionChangedItem)
{
MUX_ASSERT(m_pendingSelectionChangedItem == item);
}

m_selectedItemLayoutUpdatedRevoker = nvi.LayoutUpdated(winrt::auto_revoke, { this, &NavigationView::OnSelectedItemLayoutUpdated });
}
}
}
}
Expand Down Expand Up @@ -2116,7 +2128,7 @@ void NavigationView::AnimateSelectionChanged(const winrt::IInspectable& nextItem
strongThis->OnAnimationComplete(sender, args);
});
}
else
else if (prevIndicator != nextIndicator)
{
// if all else fails, or if animations are turned off, attempt to correctly set the positions and opacities of the indicators.
ResetElementAnimationProperties(prevIndicator, 0.0f);
Expand Down Expand Up @@ -2311,6 +2323,7 @@ winrt::UIElement NavigationView::FindSelectionIndicator(const winrt::IInspectabl
// Indicator was not found, so maybe the layout hasn't updated yet.
// So let's do that now.
container.UpdateLayout();
container.ApplyTemplate();
return winrt::get_self<NavigationViewItem>(container)->GetSelectionIndicator();
}
}
Expand All @@ -2323,9 +2336,17 @@ void NavigationView::RaiseSelectionChangedEvent(winrt::IInspectable const& nextI
auto eventArgs = winrt::make_self<NavigationViewSelectionChangedEventArgs>();
eventArgs->SelectedItem(nextItem);
eventArgs->IsSettingsSelected(isSettingsItem);
if (auto container = NavigationViewItemBaseOrSettingsContentFromData(nextItem))
if (nextItem)
{
eventArgs->SelectedItemContainer(container);
if (auto container = NavigationViewItemBaseOrSettingsContentFromData(nextItem))
{
eventArgs->SelectedItemContainer(container);
}
else if (container = GetContainerForIndexPath(m_selectionModel.SelectedIndex(), false /* lastVisible */, true /* forceRealize */))
{
MUX_ASSERT(MenuItemFromContainer(container) == nextItem);
eventArgs->SelectedItemContainer(container);
}
}
eventArgs->RecommendedNavigationTransitionInfo(CreateNavigationTransitionInfo(recommendedDirection));
m_selectionChangedEventSource(*this, *eventArgs);
Expand Down Expand Up @@ -2385,6 +2406,29 @@ void NavigationView::ChangeSelection(const winrt::IInspectable& prevItem, const
UnselectPrevItem(prevItem, nextItem);
ChangeSelectStatusForItem(nextItem, true /*selected*/);

winrt::IndexPath indexPath{ nullptr };

if (auto container = NavigationViewItemBaseOrSettingsContentFromData(nextItem))
{
indexPath = GetIndexPathForContainer(container);
}
else
{
indexPath = GetIndexPathOfItem(nextItem);
}

if (indexPath && indexPath.GetSize() > 0)
{
// The SelectedItem property has already been updated. So we want to block any logic from executing
// in the SelectionModel selection changed callback.
auto scopeGuard = gsl::finally([this]()
{
m_shouldIgnoreNextSelectionChange = false;
});
m_shouldIgnoreNextSelectionChange = true;
UpdateSelectionModelSelection(indexPath);
}

{
auto scopeGuard = gsl::finally([this]()
{
Expand All @@ -2410,16 +2454,56 @@ void NavigationView::ChangeSelection(const winrt::IInspectable& prevItem, const
}
}

RaiseSelectionChangedEvent(nextItem, isSettingsItem, recommendedDirection);
AnimateSelectionChanged(nextItem);

// If this item has an associated container, we'll raise the SelectionChanged event on it immediately.
if (auto const nvi = NavigationViewItemOrSettingsContentFromData(nextItem))
{
AnimateSelectionChanged(nvi);
RaiseSelectionChangedEvent(nextItem, isSettingsItem, recommendedDirection);
ClosePaneIfNeccessaryAfterItemIsClicked(nvi);
}
else
{
// Otherwise, we'll wait until a container gets realized for this item and raise it then.
m_isSelectionChangedPending = true;
m_pendingSelectionChangedItem = nextItem;
m_pendingSelectionChangedDirection = recommendedDirection;

auto completePendingSelectionChange = [weakThis{ get_weak() }]()
{
if (auto strongThis = weakThis.get())
{
strongThis->CompletePendingSelectionChange();
}
};

SharedHelpers::ScheduleActionAfterWait(completePendingSelectionChange, 100);
}
}
}

void NavigationView::CompletePendingSelectionChange()
{
// It may be the case that this item is in a collapsed repeater, in which case
// no container will be realized for it. We'll assume that this this is the case
// if the UI thread has fallen idle without any SelectionChanged being raised.
// In this case, we'll raise the SelectionChanged at that time, as otherwise it'll never be raised.
if (m_isSelectionChangedPending)
{
AnimateSelectionChanged(FindLowestLevelContainerToDisplaySelectionIndicator());

m_isSelectionChangedPending = false;

auto const item = m_pendingSelectionChangedItem;
auto const direction = m_pendingSelectionChangedDirection;

m_pendingSelectionChangedItem = nullptr;
m_pendingSelectionChangedDirection = NavigationRecommendedTransitionDirection::Default;

RaiseSelectionChangedEvent(item, IsSettingsItem(item), direction);
}
}


void NavigationView::UpdateSelectionModelSelection(const winrt::IndexPath& ip)
{
auto const prevIndexPath = m_selectionModel.SelectedIndex();
Expand Down Expand Up @@ -5106,6 +5190,11 @@ winrt::IndexPath NavigationView::SearchEntireTreeForIndexPath(const winrt::Navig
}
}
}
else
{
// We found an unrealized child, so we'll want to manually realize and search if we don't find the item.
areChildrenRealized = false;
}
}
}
}
Expand Down Expand Up @@ -5310,7 +5399,7 @@ winrt::UIElement NavigationView::GetContainerForIndex(int index, bool inFooter)
return nullptr;
}

winrt::NavigationViewItemBase NavigationView::GetContainerForIndexPath(const winrt::IndexPath& ip, bool lastVisible)
winrt::NavigationViewItemBase NavigationView::GetContainerForIndexPath(const winrt::IndexPath& ip, bool lastVisible, bool forceRealize)
{
if (ip && ip.GetSize() > 0)
{
Expand All @@ -5332,14 +5421,14 @@ winrt::NavigationViewItemBase NavigationView::GetContainerForIndexPath(const win
// This will return nullptr if requesting children containers of
// items in the primary list, or unrealized items in the overflow popup.
// However this should not happen.
return GetContainerForIndexPath(container, ip, lastVisible);
return GetContainerForIndexPath(container, ip, lastVisible, forceRealize);
}
}
return nullptr;
}


winrt::NavigationViewItemBase NavigationView::GetContainerForIndexPath(const winrt::UIElement& firstContainer, const winrt::IndexPath& ip, bool lastVisible)
winrt::NavigationViewItemBase NavigationView::GetContainerForIndexPath(const winrt::UIElement& firstContainer, const winrt::IndexPath& ip, bool lastVisible, bool forceRealize)
{
auto container = firstContainer;
if (ip.GetSize() > 2)
Expand All @@ -5356,7 +5445,8 @@ winrt::NavigationViewItemBase NavigationView::GetContainerForIndexPath(const win

if (auto const nviRepeater = winrt::get_self<NavigationViewItem>(nvi)->GetRepeater())
{
if (auto const nextContainer = nviRepeater.TryGetElement(ip.GetAt(i)))
auto const index = ip.GetAt(i);
if (auto const nextContainer = forceRealize ? nviRepeater.GetOrCreateElement(index) : nviRepeater.TryGetElement(index))
{
container = nextContainer;
succeededGettingNextContainer = true;
Expand Down Expand Up @@ -5668,3 +5758,51 @@ bool NavigationView::IsTopLevelItem(const winrt::NavigationViewItemBase& nvib)
{
return IsRootItemsRepeater(GetParentItemsRepeaterForContainer(nvib));
}

bool NavigationView::IsVisible(const winrt::DependencyObject& obj)
{
// We'll go up the visual tree until we find this NavigationView.
// If everything up the tree was visible, then this object was visible.
winrt::DependencyObject current = obj;
winrt::NavigationView navView = *this;

while (current && current != navView)
{
if (auto currentAsUIE = current.try_as<winrt::UIElement>())
{
if (currentAsUIE.Visibility() != winrt::Visibility::Visible)
{
return false;
}
}

current = winrt::VisualTreeHelper::GetParent(current);
}

// If we found this NavigationView, then this is both in the visual tree and visible.
// Otherwise, it's not in the visual tree, and thus is not visible.
return current == navView;
}

void NavigationView::OnSelectedItemLayoutUpdated(const winrt::IInspectable& sender, const winrt::IInspectable&)
{
if (m_isSelectionChangedPending)
{
m_isSelectionChangedPending = false;

auto const item = m_pendingSelectionChangedItem;
auto const direction = m_pendingSelectionChangedDirection;

m_pendingSelectionChangedItem = nullptr;
m_pendingSelectionChangedDirection = NavigationRecommendedTransitionDirection::Default;

m_selectedItemLayoutUpdatedRevoker.revoke();

if (auto const nvi = NavigationViewItemOrSettingsContentFromData(item))
{
AnimateSelectionChanged(nvi);
}

RaiseSelectionChangedEvent(item, IsSettingsItem(item), direction);
}
}
15 changes: 13 additions & 2 deletions dev/NavigationView/NavigationView.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,15 +103,16 @@ class NavigationView :
void OnSelectionModelChildrenRequested(const winrt::SelectionModel& selectionModel, const winrt::SelectionModelChildrenRequestedEventArgs& e);
void OnSelectedItemPropertyChanged(winrt::DependencyPropertyChangedEventArgs const& args);
void ChangeSelection(const winrt::IInspectable& prevItem, const winrt::IInspectable& nextItem);
void CompletePendingSelectionChange();
void UpdateSelectionModelSelection(const winrt::IndexPath& ip);

// Item/container info functions
int GetIndexFromItem(const winrt::ItemsRepeater& ir, const winrt::IInspectable& data);
static winrt::IInspectable GetItemFromIndex(const winrt::ItemsRepeater& ir, int index);
winrt::IndexPath GetIndexPathOfItem(const winrt::IInspectable& data);
winrt::UIElement GetContainerForIndex(int index, bool inFooter);
winrt::NavigationViewItemBase GetContainerForIndexPath(const winrt::IndexPath& ip, bool lastVisible = false);
winrt::NavigationViewItemBase GetContainerForIndexPath(const winrt::UIElement& firstContainer, const winrt::IndexPath& ip, bool lastVisible);
winrt::NavigationViewItemBase GetContainerForIndexPath(const winrt::IndexPath& ip, bool lastVisible = false, bool forceRealize = false);
winrt::NavigationViewItemBase GetContainerForIndexPath(const winrt::UIElement& firstContainer, const winrt::IndexPath& ip, bool lastVisible, bool forceRealize);
winrt::IInspectable GetChildrenForItemInIndexPath(const winrt::IndexPath& ip, bool forceRealize = false);
winrt::IInspectable GetChildrenForItemInIndexPath(const winrt::UIElement& firstContainer, const winrt::IndexPath& ip, bool forceRealize = false);
winrt::UIElement SearchEntireTreeForContainer(const winrt::ItemsRepeater& rootRepeater, const winrt::IInspectable& data);
Expand Down Expand Up @@ -319,6 +320,8 @@ class NavigationView :

void OnBackButtonClicked(const winrt::IInspectable& sender, const winrt::RoutedEventArgs& args);

void OnSelectedItemLayoutUpdated(const winrt::IInspectable& sender, const winrt::IInspectable& obj);

bool IsOverlay();
bool IsLightDismissible();
bool ShouldShowBackButton();
Expand All @@ -341,6 +344,8 @@ class NavigationView :
void UnsetDropShadow();
void ShadowCasterEaseOutStoryboard_Completed(const winrt::Grid& shadowCaster);

bool IsVisible(const winrt::DependencyObject& obj);

com_ptr<NavigationViewItemsFactory> m_navigationViewItemsFactory{ nullptr };

// Visual components
Expand Down Expand Up @@ -454,6 +459,8 @@ class NavigationView :

winrt::Storyboard::Completed_revoker m_shadowCasterEaseOutStoryboardRevoker{};

winrt::NavigationViewItem::LayoutUpdated_revoker m_selectedItemLayoutUpdatedRevoker{};

bool m_wasForceClosed{ false };
bool m_isClosedCompact{ false };
bool m_blockNextClosingEvent{ false };
Expand All @@ -467,6 +474,10 @@ class NavigationView :
winrt::ItemsSourceView m_menuItemsSource{ nullptr };
winrt::ItemsSourceView m_footerItemsSource{ nullptr };

bool m_isSelectionChangedPending{ false };
winrt::IInspectable m_pendingSelectionChangedItem{ nullptr };
NavigationRecommendedTransitionDirection m_pendingSelectionChangedDirection{ NavigationRecommendedTransitionDirection::Default };

bool m_appliedTemplate{ false };

// Identifies whenever a call is the result of OnApplyTemplate
Expand Down
3 changes: 3 additions & 0 deletions dev/NavigationView/NavigationViewItem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,8 @@ void NavigationViewItem::OnLoaded(winrt::IInspectable const&, winrt::RoutedEvent
{
PrepNavigationViewItem(splitView);
}

UpdateVisualStateForChevron();
}

void NavigationViewItem::UpdateRepeaterItemsSource()
Expand Down Expand Up @@ -751,6 +753,7 @@ NavigationViewItemPresenter* NavigationViewItem::GetPresenter() const
if (m_navigationViewItemPresenter)
{
presenter = winrt::get_self<NavigationViewItemPresenter>(m_navigationViewItemPresenter.get());
presenter->ApplyTemplate();
}
return presenter;
}
Expand Down
Loading

0 comments on commit 9f7c129

Please sign in to comment.