Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions controls/dev/TabView/InteractionTests/TabViewTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -781,6 +781,56 @@ public void VerifyDragStartedCalledOnItemDrag()
}
}

[TestMethod]
[TestProperty("Description", "Verifies that dragging a tab shows the drag visual when the TabView allows dragging.")]
public void VerifyTabEntersDragVisualStateWhenDraggingIsEnabled()
{
using (var setup = new TestSetupHelper("TabView Tests"))
{
FindElement.ByName<Button>("ResetFirstTabDragDropVisualStateLogButton").InvokeAndWait();

InputHelper.MouseDragDistance(FindElement.ByName("FirstTab"), 200, Direction.East, 4000);

FindElement.ByName<Button>("GetFirstTabDragDropVisualStateButton").InvokeAndWait();

var visitedStates = FindElement.ByName<TextBlock>("FirstTabDragDropVisualStateLogTextBlock").DocumentText;
Verify.IsTrue(visitedStates.Contains("DragDropVisualVisible"),
$"The dragged tab should enter the DragDropVisualVisible state. Visited states: '{visitedStates}'.");
}
}

[TestMethod]
[TestProperty("Description", "Verifies that a tab neither enters nor gets stuck in the drag visual state when the TabView disallows dragging.")]
[TestProperty("RegressionBug", "10749")]
public void VerifyTabDoesNotEnterDragVisualStateWhenDraggingIsDisabled()
{
using (var setup = new TestSetupHelper("TabView Tests"))
{
Log.Comment("Disallow both reordering and dragging of tabs.");
FindElement.ByName<CheckBox>("CanDragTabsCheckBox").Uncheck();
FindElement.ByName<CheckBox>("CanReorderTabsCheckBox").Uncheck();

FindElement.ByName<Button>("ResetFirstTabDragDropVisualStateLogButton").InvokeAndWait();

Log.Comment("Drag the first tab away from the tab strip and release the pointer outside of it.");
var firstTab = FindElement.ByName("FirstTab");
InputHelper.LeftMouseButtonDown(firstTab);
InputHelper.MoveMouse(firstTab, 40, 0);
InputHelper.MoveMouse(firstTab, 300, 200);
InputHelper.LeftMouseButtonUp();

FindElement.ByName<Button>("GetFirstTabDragDropVisualStateButton").InvokeAndWait();

var visitedStates = FindElement.ByName<TextBlock>("FirstTabDragDropVisualStateLogTextBlock").DocumentText;
Verify.IsFalse(visitedStates.Contains("DragDropVisualVisible"),
$"The tab should never enter the DragDropVisualVisible state while the TabView disallows dragging. Visited states: '{visitedStates}'.");

var currentState = FindElement.ByName<TextBlock>("FirstTabDragDropVisualStateTextBlock").DocumentText;
Verify.AreNotEqual("DragDropVisualVisible", currentState,
"The tab should not be left in the drag visual state.");
}
}

[TestMethod]
public void ScrollButtonToolTipTest()
{
Expand Down
18 changes: 18 additions & 0 deletions controls/dev/TabView/TabViewItem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -485,8 +485,26 @@ bool TabViewItem::ShouldStartDrag(winrt::PointerRoutedEventArgs const& args)
m_dragPointerId == args.Pointer().PointerId();
}

// Mirrors the conditions under which ListViewBaseItem captures the pointer to detect a drag gesture.
// Without that capture this item stops receiving pointer events once the pointer leaves its bounds, so it
// would never see the PointerReleased that takes it back out of the drag visual state.
bool TabViewItem::IsDraggingAllowed()
{
if (const auto tabView = GetParentTabView())
{
return tabView.CanDragTabs() || tabView.CanReorderTabs() || CanDrag();
}

return true;
}

void TabViewItem::BeginCheckingForDrag(uint32_t const& pointerId)
{
if (!IsDraggingAllowed())
{
return;
}

m_dragPointerId = pointerId;
m_isCheckingforDrag = true;
}
Expand Down
1 change: 1 addition & 0 deletions controls/dev/TabView/TabViewItem.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ class TabViewItem :

void UpdateSelectedBackgroundPathTranslateTransform();

bool IsDraggingAllowed();
void BeginCheckingForDrag(const uint32_t& pointerId);
void StopCheckingForDrag(const uint32_t& pointerId);

Expand Down
11 changes: 11 additions & 0 deletions controls/dev/TabView/TestUI/TabViewPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
<CheckBox x:Name="HandleTabItemCloseRequestedCheckBox" AutomationProperties.Name="HandleTabItemCloseRequestedCheckBox" Content="Handle TabViewItem close"/>
<CheckBox x:Name="IsAddButtonVisibleCheckBox" AutomationProperties.Name="IsAddButtonVisibleCheckBox" Content="Add button visible" IsChecked="{x:Bind Tabs.IsAddTabButtonVisible, Mode=TwoWay}"/>
<CheckBox x:Name="IsDisabledTabVisibleCheckBox" AutomationProperties.Name="IsDisabledTabVisibleCheckBox" Checked="IsDisabledTabVisibleCheckBox_CheckChanged" Unchecked="IsDisabledTabVisibleCheckBox_CheckChanged" Content="Disabled tab visible" IsChecked="True"/>
<CheckBox x:Name="CanDragTabsCheckBox" AutomationProperties.Name="CanDragTabsCheckBox" Content="CanDragTabs" IsChecked="{x:Bind Tabs.CanDragTabs, Mode=TwoWay}"/>
<CheckBox x:Name="CanReorderTabsCheckBox" AutomationProperties.Name="CanReorderTabsCheckBox" Content="CanReorderTabs" IsChecked="{x:Bind Tabs.CanReorderTabs, Mode=TwoWay}"/>
<StackPanel Orientation="Horizontal" Margin="0,0,0,8">
<TextBlock VerticalAlignment="Center">Tab Width:</TextBlock>
<ComboBox x:Name="TabWidthComboBox" AutomationProperties.Name="TabWidthComboBox" Margin="4,0,0,0" SelectedIndex="0" SelectionChanged="TabWidthComboBox_SelectionChanged">
Expand Down Expand Up @@ -125,6 +127,15 @@
<TextBlock x:Name="TabsItemChangedEventArgsIndexTextBlock" AutomationProperties.Name="TabsItemChangedEventArgsIndexTextBlock" Margin="4,0,0,0" Text="" />
</StackPanel>

<StackPanel Orientation="Horizontal" Margin="0,0,0,8">
<Button x:Name="ResetFirstTabDragDropVisualStateLogButton" AutomationProperties.Name="ResetFirstTabDragDropVisualStateLogButton" Content="Reset FirstTab drag state" Click="ResetFirstTabDragDropVisualStateLogButton_Click"/>
<Button x:Name="GetFirstTabDragDropVisualStateButton" AutomationProperties.Name="GetFirstTabDragDropVisualStateButton" Content="Get FirstTab drag state" Margin="4,0,0,0" Click="GetFirstTabDragDropVisualStateButton_Click"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,0,0,8">
<TextBlock x:Name="FirstTabDragDropVisualStateTextBlock" AutomationProperties.Name="FirstTabDragDropVisualStateTextBlock" Margin="4,0,0,0" Text=""/>
<TextBlock x:Name="FirstTabDragDropVisualStateLogTextBlock" AutomationProperties.Name="FirstTabDragDropVisualStateLogTextBlock" Margin="4,0,0,0" Text=""/>
</StackPanel>

<TextBlock x:Name="TabDraggedResultsTextBlock" AutomationProperties.Name="TabDraggedResultsTextBlock" />
</StackPanel>
</ScrollViewer>
Expand Down
54 changes: 54 additions & 0 deletions controls/dev/TabView/TestUI/TabViewPage.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using Microsoft.UI.Xaml.Automation;
using Microsoft.UI.Xaml.Shapes;
using System.Reflection;
using System.Collections.Generic;

using System.Collections.ObjectModel;
using Windows.Devices.PointOfService;
Expand All @@ -33,6 +34,8 @@ public sealed partial class TabViewPage : TestPage
{
int _newTabNumber = 1;
SymbolIconSource _iconSource;
VisualStateGroup firstTabDragDropVisualStateGroup;
readonly List<string> firstTabDragDropVisualStateLog = new List<string>();

public TabViewPage()
{
Expand Down Expand Up @@ -175,6 +178,57 @@ public void GetTab1ToolTipButton_Click(object sender, RoutedEventArgs e)
GetToolTipStringForUIElement(SecondTab, Tab1ToolTipTextBlock);
}

public void ResetFirstTabDragDropVisualStateLogButton_Click(object sender, RoutedEventArgs e)
{
if (firstTabDragDropVisualStateGroup == null)
{
firstTabDragDropVisualStateGroup = FindDragDropVisualStateGroup(FirstTab);

if (firstTabDragDropVisualStateGroup != null)
{
firstTabDragDropVisualStateGroup.CurrentStateChanged +=
(s, args) => firstTabDragDropVisualStateLog.Add(args.NewState?.Name ?? "none");
}
}

firstTabDragDropVisualStateLog.Clear();
FirstTabDragDropVisualStateTextBlock.Text = string.Empty;
FirstTabDragDropVisualStateLogTextBlock.Text = string.Empty;
}

public void GetFirstTabDragDropVisualStateButton_Click(object sender, RoutedEventArgs e)
{
FirstTabDragDropVisualStateTextBlock.Text = firstTabDragDropVisualStateGroup?.CurrentState?.Name ?? "none";
FirstTabDragDropVisualStateLogTextBlock.Text = string.Join(",", firstTabDragDropVisualStateLog);
}

// The drag/drop visual state group in the TabViewItem template is unnamed, so find it by the states it holds.
private static VisualStateGroup FindDragDropVisualStateGroup(TabViewItem tabViewItem)
{
if (VisualTreeHelper.GetChildrenCount(tabViewItem) == 0)
{
return null;
}

if (VisualTreeHelper.GetChild(tabViewItem, 0) is not FrameworkElement templateRoot)
{
return null;
}

foreach (var group in VisualStateManager.GetVisualStateGroups(templateRoot))
{
foreach (var state in group.States)
{
if (state.Name == "DragDropVisualVisible")
{
return group;
}
}
}

return null;
}

public void GetToolTipStringForUIElement(UIElement item, TextBlock textBlock)
{
var tooltip = ToolTipService.GetToolTip(item);
Expand Down