From 01f31c45f2b18476978c7d6b1d606de1d6cd0bfd Mon Sep 17 00:00:00 2001 From: Marcel Wagner Date: Thu, 28 May 2020 10:13:51 +0200 Subject: [PATCH 01/10] Add initial behavior --- dev/TabView/TabView.cpp | 73 ++++++++++++++++++++++++++--------------- dev/TabView/TabView.h | 7 +++- 2 files changed, 53 insertions(+), 27 deletions(-) diff --git a/dev/TabView/TabView.cpp b/dev/TabView/TabView.cpp index 3bc3565eb6..8caef531ff 100644 --- a/dev/TabView/TabView.cpp +++ b/dev/TabView/TabView.cpp @@ -89,6 +89,7 @@ void TabView::OnApplyTemplate() if (listView) { m_listViewLoadedRevoker = listView.Loaded(winrt::auto_revoke, { this, &TabView::OnListViewLoaded }); + m_listViewPointerExitedRevoker = listView.PointerExited(winrt::auto_revoke, { this,&TabView::OnListViewPointerExited }); m_listViewSelectionChangedRevoker = listView.SelectionChanged(winrt::auto_revoke, { this, &TabView::OnListViewSelectionChanged }); m_listViewDragItemsStartingRevoker = listView.DragItemsStarting(winrt::auto_revoke, { this, &TabView::OnListViewDragItemsStarting }); @@ -341,6 +342,18 @@ void TabView::OnListViewLoaded(const winrt::IInspectable&, const winrt::RoutedEv } } +void TabView::OnListViewPointerExited(const winrt::IInspectable& sender, const winrt::PointerRoutedEventArgs& args) +{ + if (updateTabWidthOnPointerLeave) + { + UpdateTabWidths(); + auto scopeGuard = gsl::finally([this]() + { + updateTabWidthOnPointerLeave = false; + }); + } +} + void TabView::OnScrollViewerLoaded(const winrt::IInspectable&, const winrt::RoutedEventArgs& args) { if (auto&& scrollViewer = m_scrollViewer.get()) @@ -423,7 +436,7 @@ void TabView::UpdateScrollViewerDecreaseAndIncreaseButtonsViewState() void TabView::OnItemsPresenterSizeChanged(const winrt::IInspectable& sender, const winrt::SizeChangedEventArgs& args) { - UpdateTabWidths(); + updateTabWidthOnPointerLeave = true; UpdateScrollViewerDecreaseAndIncreaseButtonsViewState(); } @@ -434,42 +447,50 @@ void TabView::OnItemsChanged(winrt::IInspectable const& item) m_tabItemsChangedEventSource(*this, args); int numItems = static_cast(TabItems().Size()); - if (args.CollectionChange() == winrt::CollectionChange::ItemRemoved && numItems > 0) + + if (args.CollectionChange() == winrt::CollectionChange::ItemRemoved) { - // SelectedIndex might also already be -1 - auto selectedIndex = SelectedIndex(); - if (selectedIndex == -1 || selectedIndex == static_cast(args.Index())) + updateTabWidthOnPointerLeave = true; + if (numItems > 0) { - // Find the closest tab to select instead. - int startIndex = static_cast(args.Index()); - if (startIndex >= numItems) - { - startIndex = numItems - 1; - } - int index = startIndex; - - do + // SelectedIndex might also already be -1 + auto selectedIndex = SelectedIndex(); + if (selectedIndex == -1 || selectedIndex == static_cast(args.Index())) { - auto nextItem = ContainerFromIndex(index).as(); - - if (nextItem && nextItem.IsEnabled() && nextItem.Visibility() == winrt::Visibility::Visible) + // Find the closest tab to select instead. + int startIndex = static_cast(args.Index()); + if (startIndex >= numItems) { - SelectedItem(TabItems().GetAt(index)); - break; + startIndex = numItems - 1; } + int index = startIndex; - // try the next item - index++; - if (index >= numItems) + do { - index = 0; - } - } while (index != startIndex); + auto nextItem = ContainerFromIndex(index).as(); + + if (nextItem && nextItem.IsEnabled() && nextItem.Visibility() == winrt::Visibility::Visible) + { + SelectedItem(TabItems().GetAt(index)); + break; + } + + // try the next item + index++; + if (index >= numItems) + { + index = 0; + } + } while (index != startIndex); + } } } + else + { + UpdateTabWidths(); + } } - UpdateTabWidths(); } void TabView::OnListViewSelectionChanged(const winrt::IInspectable& sender, const winrt::SelectionChangedEventArgs& args) diff --git a/dev/TabView/TabView.h b/dev/TabView/TabView.h index 0a81378f19..8a533fa550 100644 --- a/dev/TabView/TabView.h +++ b/dev/TabView/TabView.h @@ -89,7 +89,7 @@ class TabView : // IFrameworkElement void OnApplyTemplate(); - winrt::Size MeasureOverride(winrt::Size const& availableSize); + winrt::Size MeasureOverride(winrt::Size const& availableSize); // IUIElement winrt::AutomationPeer OnCreateAutomationPeer(); @@ -127,6 +127,7 @@ class TabView : void OnItemsPresenterSizeChanged(const winrt::IInspectable& sender, const winrt::SizeChangedEventArgs& args); void OnListViewLoaded(const winrt::IInspectable& sender, const winrt::RoutedEventArgs& args); + void OnListViewPointerExited(const winrt::IInspectable& sender, const winrt::PointerRoutedEventArgs& args); void OnListViewSelectionChanged(const winrt::IInspectable& sender, const winrt::SelectionChangedEventArgs& args); void OnListViewDragItemsStarting(const winrt::IInspectable& sender, const winrt::DragItemsStartingEventArgs& args); @@ -152,6 +153,9 @@ class TabView : int GetItemCount(); + bool updateTabWidthOnPointerLeave{ false }; + + winrt::TabViewItem FindTabViewItemFromDragItem(const winrt::IInspectable& item); tracker_ref m_leftContentColumn{ this }; @@ -172,6 +176,7 @@ class TabView : tracker_ref m_shadowReceiver{ this }; winrt::ListView::Loaded_revoker m_listViewLoadedRevoker{}; + winrt::ListView::PointerExited_revoker m_listViewPointerExitedRevoker{}; winrt::Selector::SelectionChanged_revoker m_listViewSelectionChangedRevoker{}; winrt::UIElement::GettingFocus_revoker m_listViewGettingFocusRevoker{}; From dd78553afe7c3578b9bb9c48bc85ba55ec46f5d6 Mon Sep 17 00:00:00 2001 From: Marcel Wagner Date: Fri, 29 May 2020 12:49:32 +0200 Subject: [PATCH 02/10] Hide scroll buttons when they are not needed anymore --- dev/TabView/TabView.cpp | 25 +++++++++++++++---------- dev/TabView/TabView.h | 2 +- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/dev/TabView/TabView.cpp b/dev/TabView/TabView.cpp index 8caef531ff..4ad722edc3 100644 --- a/dev/TabView/TabView.cpp +++ b/dev/TabView/TabView.cpp @@ -641,6 +641,7 @@ void TabView::RequestCloseTab(winrt::TabViewItem const& container) internalTabViewItem->RaiseRequestClose(*args); } } + UpdateTabWidths(false); } void TabView::OnScrollDecreaseClick(const winrt::IInspectable&, const winrt::RoutedEventArgs&) @@ -670,7 +671,7 @@ winrt::Size TabView::MeasureOverride(winrt::Size const& availableSize) return __super::MeasureOverride(availableSize); } -void TabView::UpdateTabWidths() +void TabView::UpdateTabWidths(bool shouldUpdateWidths) { double tabWidth = std::numeric_limits::quiet_NaN(); @@ -763,18 +764,22 @@ void TabView::UpdateTabWidths() } } - for (auto item : TabItems()) + + if (shouldUpdateWidths) { - // Set the calculated width on each tab. - auto tvi = item.try_as(); - if (!tvi) + for (auto item : TabItems()) { - tvi = ContainerFromItem(item).as(); - } + // Set the calculated width on each tab. + auto tvi = item.try_as(); + if (!tvi) + { + tvi = ContainerFromItem(item).as(); + } - if (tvi) - { - tvi.Width(tabWidth); + if (tvi) + { + tvi.Width(tabWidth); + } } } } diff --git a/dev/TabView/TabView.h b/dev/TabView/TabView.h index 8a533fa550..9c18d49823 100644 --- a/dev/TabView/TabView.h +++ b/dev/TabView/TabView.h @@ -145,7 +145,7 @@ class TabView : void UpdateSelectedItem(); void UpdateSelectedIndex(); - void UpdateTabWidths(); + void UpdateTabWidths(bool shouldUpdateWidths=true); void UpdateScrollViewerDecreaseAndIncreaseButtonsViewState(); From 85d99b96b8a224fc2cf973404bd048da4f6c62d6 Mon Sep 17 00:00:00 2001 From: Marcel Wagner Date: Fri, 29 May 2020 14:56:24 +0200 Subject: [PATCH 03/10] Add test --- dev/TabView/InteractionTests/TabViewTests.cs | 58 +++++++++++++++ dev/TabView/TestUI/TabViewPage.xaml | 7 +- dev/TabView/TestUI/TabViewPage.xaml.cs | 8 ++- .../TestUI/TabViewTabClosingBehaviorPage.xaml | 44 ++++++++++++ .../TabViewTabClosingBehaviorPage.xaml.cs | 72 +++++++++++++++++++ dev/TabView/TestUI/TabView_TestUI.projitems | 7 ++ 6 files changed, 194 insertions(+), 2 deletions(-) create mode 100644 dev/TabView/TestUI/TabViewTabClosingBehaviorPage.xaml create mode 100644 dev/TabView/TestUI/TabViewTabClosingBehaviorPage.xaml.cs diff --git a/dev/TabView/InteractionTests/TabViewTests.cs b/dev/TabView/InteractionTests/TabViewTests.cs index 8cb28a8792..00689936cd 100644 --- a/dev/TabView/InteractionTests/TabViewTests.cs +++ b/dev/TabView/InteractionTests/TabViewTests.cs @@ -647,6 +647,64 @@ public void VerifyTabViewItemHeaderForegroundResource() } } + [TestMethod] + public void VerifySizingBehaviorOnTabClose() + { + int pixelTolerance = 10; + + using (var setup = new TestSetupHelper(new[] { "TabView Tests", "TabViewTabClosingBehaviorButton" })) + { + + Log.Comment("Verifying sizing behavior when closing a tab"); + CloseTabAndVerifyWidth("Tab 1", 500,true); + + CloseTabAndVerifyWidth("Tab 2", 500, true); + + // Scroll buttons are not visible after closing this tab. + // However the TabCloseRequested event get's raised before the scroll buttons dissappear. + // This results in every call essentially verifiying the scroll button state of the previous tab closing. + CloseTabAndVerifyWidth("Tab 3", 443, false); + + CloseTabAndVerifyWidth("Tab 4", 343, false); + + Log.Comment("Leaving the pointer exited area"); + var readTabViewWidthButton = new Button(FindElement.ByName("GetActualWidthButton")); + readTabViewWidthButton.Click(); + Wait.ForIdle(); + + readTabViewWidthButton.Click(); + Wait.ForIdle(); + + Log.Comment("Verify correct TabView width"); + Verify.IsTrue(Math.Abs(GetActualTabViewWidth() - 500) < pixelTolerance); + } + + void CloseTabAndVerifyWidth(string tabName, int expectedValue, bool expectedScrollButtonVisible) + { + Log.Comment("Closing tab:" + tabName); + FindCloseButton(FindElement.ByName(tabName)).Click(); + Wait.ForIdle(); + Log.Comment("Verifying TabView width"); + Verify.IsTrue(Math.Abs(GetActualTabViewWidth() - expectedValue) < pixelTolerance); + if(expectedScrollButtonVisible) + { + Verify.AreEqual("true;true;", FindElement.ByName("ScrollButtonStatus").GetText()); + } + else + { + Verify.AreEqual("false;false;", FindElement.ByName("ScrollButtonStatus").GetText()); + } + + } + + double GetActualTabViewWidth() + { + var tabviewWidth = new TextBlock(FindElement.ByName("TabViewWidth")); + + return Double.Parse(tabviewWidth.GetText()); + } + } + public void PressButtonAndVerifyText(String buttonName, String textBlockName, String expectedText) { Button button = FindElement.ByName - + + diff --git a/dev/TabView/TestUI/TabViewPage.xaml.cs b/dev/TabView/TestUI/TabViewPage.xaml.cs index 8e0a5f652d..a9861bd8e5 100644 --- a/dev/TabView/TestUI/TabViewPage.xaml.cs +++ b/dev/TabView/TestUI/TabViewPage.xaml.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. See LICENSE in the project root for license information. using System; @@ -394,6 +394,12 @@ private void TabViewSizingPageButton_Click(object sender, RoutedEventArgs e) this.Frame.Navigate(typeof(TabViewSizingPage)); } + + private void TabViewTabClosingBehaviorButton_Click(object sender, RoutedEventArgs e) + { + this.Frame.Navigate(typeof(TabViewTabClosingBehaviorPage)); + } + private void ShortLongTextButton_Click(object sender, RoutedEventArgs e) { FirstTab.Header = "s"; diff --git a/dev/TabView/TestUI/TabViewTabClosingBehaviorPage.xaml b/dev/TabView/TestUI/TabViewTabClosingBehaviorPage.xaml new file mode 100644 index 0000000000..f39f3a7f7d --- /dev/null +++ b/dev/TabView/TestUI/TabViewTabClosingBehaviorPage.xaml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + +