-
Notifications
You must be signed in to change notification settings - Fork 683
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Resize tab view items only once the pointer has left the TabViewItem strip #2569
Changes from 5 commits
01f31c4
dd78553
85d99b9
41e2f25
4c03af4
ceb4c77
a587fd2
89b02d3
ebb74f2
d94dc69
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -80,7 +80,11 @@ void TabView::OnApplyTemplate() | |
m_addButtonColumn.set(GetTemplateChildT<winrt::ColumnDefinition>(L"AddButtonColumn", controlProtected)); | ||
m_rightContentColumn.set(GetTemplateChildT<winrt::ColumnDefinition>(L"RightContentColumn", controlProtected)); | ||
|
||
m_tabContainerGrid.set(GetTemplateChildT<winrt::Grid>(L"TabContainerGrid", controlProtected)); | ||
if (const auto& containerGrid = GetTemplateChildT<winrt::Grid>(L"TabContainerGrid", controlProtected)) | ||
{ | ||
m_tabContainerGrid.set(containerGrid); | ||
m_tabStripPointerExitedRevoker = containerGrid.PointerExited(winrt::auto_revoke, { this,&TabView::OnTabStripPointerExited }); | ||
} | ||
|
||
m_shadowReceiver.set(GetTemplateChildT<winrt::Grid>(L"ShadowReceiver", controlProtected)); | ||
|
||
|
@@ -341,6 +345,18 @@ void TabView::OnListViewLoaded(const winrt::IInspectable&, const winrt::RoutedEv | |
} | ||
} | ||
|
||
void TabView::OnTabStripPointerExited(const winrt::IInspectable& sender, const winrt::PointerRoutedEventArgs& args) | ||
{ | ||
if (updateTabWidthOnPointerLeave) | ||
{ | ||
UpdateTabWidths(); | ||
auto scopeGuard = gsl::finally([this]() | ||
{ | ||
updateTabWidthOnPointerLeave = false; | ||
}); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Scope guarded flag should be declared before UpdateTabWidths() is called. This is so that if UpdateTabWidths() throws but that exception is later handled we will still update this flag. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point, moved the UpdateTabWidths call behind the scopeguard. |
||
} | ||
} | ||
|
||
void TabView::OnScrollViewerLoaded(const winrt::IInspectable&, const winrt::RoutedEventArgs& args) | ||
{ | ||
if (auto&& scrollViewer = m_scrollViewer.get()) | ||
|
@@ -423,7 +439,7 @@ void TabView::UpdateScrollViewerDecreaseAndIncreaseButtonsViewState() | |
|
||
void TabView::OnItemsPresenterSizeChanged(const winrt::IInspectable& sender, const winrt::SizeChangedEventArgs& args) | ||
{ | ||
UpdateTabWidths(); | ||
updateTabWidthOnPointerLeave = true; | ||
UpdateScrollViewerDecreaseAndIncreaseButtonsViewState(); | ||
} | ||
|
||
|
@@ -434,42 +450,50 @@ void TabView::OnItemsChanged(winrt::IInspectable const& item) | |
m_tabItemsChangedEventSource(*this, args); | ||
|
||
int numItems = static_cast<int>(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<int32_t>(args.Index())) | ||
updateTabWidthOnPointerLeave = true; | ||
if (numItems > 0) | ||
{ | ||
// Find the closest tab to select instead. | ||
int startIndex = static_cast<int>(args.Index()); | ||
if (startIndex >= numItems) | ||
// SelectedIndex might also already be -1 | ||
auto selectedIndex = SelectedIndex(); | ||
if (selectedIndex == -1 || selectedIndex == static_cast<int32_t>(args.Index())) | ||
{ | ||
startIndex = numItems - 1; | ||
} | ||
int index = startIndex; | ||
|
||
do | ||
{ | ||
auto nextItem = ContainerFromIndex(index).as<winrt::ListViewItem>(); | ||
|
||
if (nextItem && nextItem.IsEnabled() && nextItem.Visibility() == winrt::Visibility::Visible) | ||
// Find the closest tab to select instead. | ||
int startIndex = static_cast<int>(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<winrt::ListViewItem>(); | ||
|
||
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) | ||
|
@@ -620,6 +644,7 @@ void TabView::RequestCloseTab(winrt::TabViewItem const& container) | |
internalTabViewItem->RaiseRequestClose(*args); | ||
} | ||
} | ||
UpdateTabWidths(false); | ||
} | ||
|
||
void TabView::OnScrollDecreaseClick(const winrt::IInspectable&, const winrt::RoutedEventArgs&) | ||
|
@@ -649,7 +674,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<double>::quiet_NaN(); | ||
|
||
|
@@ -742,18 +767,22 @@ void TabView::UpdateTabWidths() | |
} | ||
} | ||
|
||
for (auto item : TabItems()) | ||
|
||
if (shouldUpdateWidths) | ||
{ | ||
// Set the calculated width on each tab. | ||
auto tvi = item.try_as<winrt::TabViewItem>(); | ||
if (!tvi) | ||
for (auto item : TabItems()) | ||
{ | ||
tvi = ContainerFromItem(item).as<winrt::TabViewItem>(); | ||
} | ||
// Set the calculated width on each tab. | ||
auto tvi = item.try_as<winrt::TabViewItem>(); | ||
if (!tvi) | ||
{ | ||
tvi = ContainerFromItem(item).as<winrt::TabViewItem>(); | ||
} | ||
|
||
if (tvi) | ||
{ | ||
tvi.Width(tabWidth); | ||
if (tvi) | ||
{ | ||
tvi.Width(tabWidth); | ||
} | ||
} | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
<Page | ||
x:Class="MUXControlsTestApp.TabViewTabClosingBehaviorPage" | ||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" | ||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" | ||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" | ||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" | ||
xmlns:muxc="using:Microsoft.UI.Xaml.Controls" | ||
mc:Ignorable="d" | ||
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> | ||
<Grid> | ||
<StackPanel Orientation="Horizontal"> | ||
<Grid MaxWidth="500"> | ||
<Grid.ColumnDefinitions> | ||
<ColumnDefinition Width="Auto"/> | ||
<ColumnDefinition Width="*"/> | ||
</Grid.ColumnDefinitions> | ||
<muxc:TabView | ||
MaxWidth="500" | ||
x:Name="Tabs" | ||
TabCloseRequested="TabViewTabCloseRequested" | ||
AddTabButtonClick="AddButtonClick"> | ||
|
||
<muxc:TabViewItem Header="Tab 0" AutomationProperties.Name="Tab 0"/> | ||
<muxc:TabViewItem Header="Tab 1" AutomationProperties.Name="Tab 1"/> | ||
<muxc:TabViewItem Header="Tab 2" AutomationProperties.Name="Tab 2"/> | ||
<muxc:TabViewItem Header="Tab 3" AutomationProperties.Name="Tab 3"/> | ||
<muxc:TabViewItem Header="Tab 4" AutomationProperties.Name="Tab 4"/> | ||
<muxc:TabViewItem Header="Tab 5" AutomationProperties.Name="Tab 5"/> | ||
</muxc:TabView> | ||
</Grid> | ||
<StackPanel> | ||
<TextBlock Text="Actual width"/> | ||
<TextBlock x:Name="TabViewWidth" AutomationProperties.Name="TabViewWidth"/> | ||
<TextBlock Text="Scroll buttons status"/> | ||
<TextBlock x:Name="ScrollButtonStatus" AutomationProperties.Name="ScrollButtonStatus" /> | ||
</StackPanel> | ||
<StackPanel> | ||
<Button AutomationProperties.Name="GetActualWidthButton" | ||
Click="GetActualWidthsButton_Click" | ||
Content="Get actual TabView width"/> | ||
</StackPanel> | ||
</StackPanel> | ||
</Grid> | ||
</Page> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
a downside to this approach is that you will never set m_tabContainerGrid to null, even if a second template is later applied which doesn't have a TabContainerGrid. Seems unlikely in this case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated the code to set it to null if we get a nullptr.