Repository: microsoft/terminal (language: cpp)

Issue:
[WCAG 2.2] [Windows Terminal - Multiple Tab]: No single pointer alternative is provided to move the tabs.
### Windows Terminal version

1.22.2362.0

### Windows build number

27695.1000

### Other Software

**Test Environment:**
OS: Windows 11 Version Dev (OS Build 27695.1000)
App: Windows Terminal Preview

### Steps to reproduce

**Repro steps:**
1. Open the 'windows terminal' application.
2. Open Multiple tabs and navigate among the tabs.
3. Now, verify whether any single pointer alternative is provided to move the tabs or not.
5. Observe the issue.

**User experience:**
Users who struggle with performing dragging movements will feel difficulty to move tabs, if there is no single pointer alternative is provided to perform the action.

**WCAG Reference Link:**
[Understanding Success Criterion 2.5.7: Dragging Movements | WAI | W3C](https://www.w3.org/WAI/WCAG22/Understanding/dragging-movements)

**Attachment:**
[No single pointer alternative is provided to move the tabs.zip](https://github.com/user-attachments/files/16958526/No.single.pointer.alternative.is.provided.to.move.the.tabs.zip)

### Expected Behavior

A single pointer alternative mechanism should be provided to the users to move the tabs.

### Actual Behavior

User can be able to move the tabs here & there by using mouse, but no alternative method is available for the user to perform action using single pointer.


Discussion / hints:
We've found some similar issues:


- #16717 , similarity score: 87%
  

If any of the above are duplicates, please consider closing this issue out and adding additional context in the original issue.

> Note: You can give me feedback by 👍 or 👎 this comment.
This is a /dup of #16717.

Please refer to the discussion in that thread for further details.
Hi! We've identified this issue as a duplicate of another one that already exists on this Issue Tracker. This specific instance is being closed in favor of tracking the concern over on the referenced thread. Thanks for your report!
<!-- Policy app identification https://img.shields.io/static/v1?label=PullRequestIssueManagement. -->
@carlos-zamora,
Having keyboard shortcut to move the tabs cannot be consider as a single pointer alternative.

Suggestion: A button can be defined in a context menu of tabs using which users can move the tab.
Is there precedent for something like this in Edge?

Expanding out "Move tab to new window" to "Move Tab > {To new window, To the left, To the right, ...}" as a nested menu might be an option here

Fix patch:
diff --git a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw
index 26823366401..217f4e88879 100644
--- a/src/cascadia/TerminalApp/Resources/en-US/Resources.resw
+++ b/src/cascadia/TerminalApp/Resources/en-US/Resources.resw
@@ -193,6 +193,9 @@
   <data name="TabCloseSubMenu" xml:space="preserve">
     <value>Close</value>
   </data>
+  <data name="TabMoveSubMenu" xml:space="preserve">
+    <value>Move tab</value>
+  </data>
   <data name="TabCloseAfter" xml:space="preserve">
     <value>Close tabs to the right</value>
   </data>
@@ -941,4 +944,10 @@
   <data name="CloseSnippetsPaneButton.[using:Windows.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
     <value>Close pane</value>
   </data>
-</root>
+  <data name="TabMoveLeft" xml:space="preserve">
+    <value>Move left</value>
+  </data>
+  <data name="TabMoveRight" xml:space="preserve">
+    <value>Move right</value>
+  </data>
+</root>
\ No newline at end of file
diff --git a/src/cascadia/TerminalApp/TabBase.cpp b/src/cascadia/TerminalApp/TabBase.cpp
index 2d5e8da4433..0cedab37d0a 100644
--- a/src/cascadia/TerminalApp/TabBase.cpp
+++ b/src/cascadia/TerminalApp/TabBase.cpp
@@ -7,6 +7,7 @@
 #include "TabBase.g.cpp"
 #include "Utils.h"
 #include "ColorHelper.h"
+#include "../inc/WindowingBehavior.h"
 
 using namespace winrt;
 using namespace winrt::Windows::UI::Xaml;
@@ -56,10 +57,72 @@ namespace winrt::TerminalApp::implementation
                 tab->RequestFocusActiveControl.raise();
             }
         });
+        _AppendMoveMenuItems(contextMenuFlyout);
         _AppendCloseMenuItems(contextMenuFlyout);
         TabViewItem().ContextFlyout(contextMenuFlyout);
     }
 
+    void TabBase::_AppendMoveMenuItems(winrt::Windows::UI::Xaml::Controls::MenuFlyout flyout)
+    {
+        auto weakThis{ get_weak() };
+
+        // Move to new window
+        {
+            Controls::FontIcon moveTabToNewWindowTabSymbol;
+            moveTabToNewWindowTabSymbol.FontFamily(Media::FontFamily{ L"Segoe Fluent Icons, Segoe MDL2 Assets" });
+            moveTabToNewWindowTabSymbol.Glyph(L"\xE8A7");
+
+            _moveToNewWindowMenuItem.Click([weakThis](auto&&, auto&&) {
+                if (auto tab{ weakThis.get() })
+                {
+                    MoveTabArgs args{ winrt::to_hstring(NewWindow), MoveTabDirection::Forward };
+                    ActionAndArgs actionAndArgs{ ShortcutAction::MoveTab, args };
+                    tab->_dispatch.DoAction(*tab, actionAndArgs);
+                }
+            });
+            _moveToNewWindowMenuItem.Text(RS_(L"MoveTabToNewWindowText"));
+            _moveToNewWindowMenuItem.Icon(moveTabToNewWindowTabSymbol);
+
+            const auto moveTabToNewWindowToolTip = RS_(L"MoveTabToNewWindowToolTip");
+            WUX::Controls::ToolTipService::SetToolTip(_moveToNewWindowMenuItem, box_value(moveTabToNewWindowToolTip));
+            Automation::AutomationProperties::SetHelpText(_moveToNewWindowMenuItem, moveTabToNewWindowToolTip);
+        }
+
+        // Move left
+        {
+            _moveLeftMenuItem.Click([weakThis](auto&&, auto&&) {
+                if (auto tab{ weakThis.get() })
+                {
+                    MoveTabArgs args{ hstring{}, MoveTabDirection::Backward };
+                    ActionAndArgs actionAndArgs{ ShortcutAction::MoveTab, args };
+                    tab->_dispatch.DoAction(*tab, actionAndArgs);
+                }
+            });
+            _moveLeftMenuItem.Text(RS_(L"TabMoveLeft"));
+        }
+
+        // Move right
+        {
+            _moveRightMenuItem.Click([weakThis](auto&&, auto&&) {
+                if (auto tab{ weakThis.get() })
+                {
+                    MoveTabArgs args{ hstring{}, MoveTabDirection::Forward };
+                    ActionAndArgs actionAndArgs{ ShortcutAction::MoveTab, args };
+                    tab->_dispatch.DoAction(*tab, actionAndArgs);
+                }
+            });
+            _moveRightMenuItem.Text(RS_(L"TabMoveRight"));
+        }
+
+        // Create a sub-menu for our extended move tab items.
+        Controls::MenuFlyoutSubItem moveSubMenu;
+        moveSubMenu.Text(RS_(L"TabMoveSubMenu"));
+        moveSubMenu.Items().Append(_moveToNewWindowMenuItem);
+        moveSubMenu.Items().Append(_moveRightMenuItem);
+        moveSubMenu.Items().Append(_moveLeftMenuItem);
+        flyout.Items().Append(moveSubMenu);
+    }
+
     // Method Description:
     // - Append the close menu items to the context menu flyout
     // Arguments:
@@ -75,7 +138,9 @@ namespace winrt::TerminalApp::implementation
         _closeTabsAfterMenuItem.Click([weakThis](auto&&, auto&&) {
             if (auto tab{ weakThis.get() })
             {
-                tab->_CloseTabsAfter();
+                CloseTabsAfterArgs args{ tab->_TabViewIndex };
+                ActionAndArgs closeTabsAfter{ ShortcutAction::CloseTabsAfter, args };
+                tab->_dispatch.DoAction(*tab, closeTabsAfter);
             }
         });
         _closeTabsAfterMenuItem.Text(RS_(L"TabCloseAfter"));
@@ -88,7 +153,9 @@ namespace winrt::TerminalApp::implementation
         _closeOtherTabsMenuItem.Click([weakThis](auto&&, auto&&) {
             if (auto tab{ weakThis.get() })
             {
-                tab->_CloseOtherTabs();
+                CloseOtherTabsArgs args{ tab->_TabViewIndex };
+                ActionAndArgs closeOtherTabs{ ShortcutAction::CloseOtherTabs, args };
+                tab->_dispatch.DoAction(*tab, closeOtherTabs);
             }
         });
         _closeOtherTabsMenuItem.Text(RS_(L"TabCloseOther"));
@@ -129,33 +196,27 @@ namespace winrt::TerminalApp::implementation
     }
 
     // Method Description:
-    // - Enable the Close menu items based on tab index and total number of tabs
+    // - Enable menu items based on tab index and total number of tabs
     // Arguments:
     // - <none>
     // Return Value:
     // - <none>
-    void TabBase::_EnableCloseMenuItems()
+    void TabBase::_EnableMenuItems()
     {
-        // close other tabs is enabled only if there are other tabs
-        _closeOtherTabsMenuItem.IsEnabled(TabViewNumTabs() > 1);
-        // close tabs after is enabled only if there are other tabs on the right
-        _closeTabsAfterMenuItem.IsEnabled(TabViewIndex() < TabViewNumTabs() - 1);
-    }
+        const auto tabIndex = TabViewIndex();
+        const auto numOfTabs = TabViewNumTabs();
 
-    void TabBase::_CloseTabsAfter()
-    {
-        CloseTabsAfterArgs args{ _TabViewIndex };
-        ActionAndArgs closeTabsAfter{ ShortcutAction::CloseTabsAfter, args };
+        // enabled if there are other tabs
+        _closeOtherTabsMenuItem.IsEnabled(numOfTabs > 1);
 
-        _dispatch.DoAction(closeTabsAfter);
-    }
+        // enabled if there are other tabs on the right
+        _closeTabsAfterMenuItem.IsEnabled(tabIndex < numOfTabs - 1);
 
-    void TabBase::_CloseOtherTabs()
-    {
-        CloseOtherTabsArgs args{ _TabViewIndex };
-        ActionAndArgs closeOtherTabs{ ShortcutAction::CloseOtherTabs, args };
+        // enabled if not left-most tab
+        _moveLeftMenuItem.IsEnabled(tabIndex > 0);
 
-        _dispatch.DoAction(closeOtherTabs);
+        // enabled if not last tab
+        _moveRightMenuItem.IsEnabled(tabIndex < numOfTabs - 1);
     }
 
     void TabBase::UpdateTabViewIndex(const uint32_t idx, const uint32_t numTabs)
@@ -164,7 +225,7 @@ namespace winrt::TerminalApp::implementation
 
         TabViewIndex(idx);
         TabViewNumTabs(numTabs);
-        _EnableCloseMenuItems();
+        _EnableMenuItems();
         _UpdateSwitchToTabKeyChord();
     }
 
diff --git a/src/cascadia/TerminalApp/TabBase.h b/src/cascadia/TerminalApp/TabBase.h
index 9a538550b8d..aa4928c669b 100644
--- a/src/cascadia/TerminalApp/TabBase.h
+++ b/src/cascadia/TerminalApp/TabBase.h
@@ -54,6 +54,9 @@ namespace winrt::TerminalApp::implementation
         winrt::Windows::UI::Xaml::FocusState _focusState{ winrt::Windows::UI::Xaml::FocusState::Unfocused };
         winrt::Windows::UI::Xaml::Controls::MenuFlyoutItem _closeOtherTabsMenuItem{};
         winrt::Windows::UI::Xaml::Controls::MenuFlyoutItem _closeTabsAfterMenuItem{};
+        winrt::Windows::UI::Xaml::Controls::MenuFlyoutItem _moveToNewWindowMenuItem{};
+        winrt::Windows::UI::Xaml::Controls::MenuFlyoutItem _moveRightMenuItem{};
+        winrt::Windows::UI::Xaml::Controls::MenuFlyoutItem _moveLeftMenuItem{};
         winrt::TerminalApp::ShortcutActionDispatch _dispatch;
         Microsoft::Terminal::Settings::Model::IActionMapView _actionMap{ nullptr };
         winrt::hstring _keyChord{};
@@ -69,10 +72,9 @@ namespace winrt::TerminalApp::implementation
 
         virtual void _MakeTabViewItem();
 
+        void _AppendMoveMenuItems(winrt::Windows::UI::Xaml::Controls::MenuFlyout flyout);
         winrt::Windows::UI::Xaml::Controls::MenuFlyoutSubItem _AppendCloseMenuItems(winrt::Windows::UI::Xaml::Controls::MenuFlyout flyout);
-        void _EnableCloseMenuItems();
-        void _CloseTabsAfter();
-        void _CloseOtherTabs();
+        void _EnableMenuItems();
         void _UpdateSwitchToTabKeyChord();
         void _UpdateToolTip();
 
diff --git a/src/cascadia/TerminalApp/TerminalTab.cpp b/src/cascadia/TerminalApp/TerminalTab.cpp
index 0a01e83d13e..e81e452708e 100644
--- a/src/cascadia/TerminalApp/TerminalTab.cpp
+++ b/src/cascadia/TerminalApp/TerminalTab.cpp
@@ -10,7 +10,6 @@
 #include "Utils.h"
 #include "ColorHelper.h"
 #include "AppLogic.h"
-#include "../inc/WindowingBehavior.h"
 
 using namespace winrt;
 using namespace winrt::Windows::UI::Xaml;
@@ -1443,23 +1442,6 @@ namespace winrt::TerminalApp::implementation
             Automation::AutomationProperties::SetHelpText(splitTabMenuItem, splitTabToolTip);
         }
 
-        Controls::MenuFlyoutItem moveTabToNewWindowMenuItem;
-        {
-            // "Move tab to new window"
-            Controls::FontIcon moveTabToNewWindowTabSymbol;
-            moveTabToNewWindowTabSymbol.FontFamily(Media::FontFamily{ L"Segoe Fluent Icons, Segoe MDL2 Assets" });
-            moveTabToNewWindowTabSymbol.Glyph(L"\xE8A7");
-
-            moveTabToNewWindowMenuItem.Click({ get_weak(), &TerminalTab::_moveTabToNewWindowClicked });
-            moveTabToNewWindowMenuItem.Text(RS_(L"MoveTabToNewWindowText"));
-            moveTabToNewWindowMenuItem.Icon(moveTabToNewWindowTabSymbol);
-
-            const auto moveTabToNewWindowToolTip = RS_(L"MoveTabToNewWindowToolTip");
-
-            WUX::Controls::ToolTipService::SetToolTip(moveTabToNewWindowMenuItem, box_value(moveTabToNewWindowToolTip));
-            Automation::AutomationProperties::SetHelpText(moveTabToNewWindowMenuItem, moveTabToNewWindowToolTip);
-        }
-
         Controls::MenuFlyoutItem closePaneMenuItem = _closePaneMenuItem;
         {
             // "Close pane"
@@ -1535,12 +1517,15 @@ namespace winrt::TerminalApp::implementation
         contextMenuFlyout.Items().Append(renameTabMenuItem);
         contextMenuFlyout.Items().Append(duplicateTabMenuItem);
         contextMenuFlyout.Items().Append(splitTabMenuItem);
-        contextMenuFlyout.Items().Append(moveTabToNewWindowMenuItem);
+        _AppendMoveMenuItems(contextMenuFlyout);
         contextMenuFlyout.Items().Append(exportTabMenuItem);
         contextMenuFlyout.Items().Append(findMenuItem);
         contextMenuFlyout.Items().Append(restartConnectionMenuItem);
         contextMenuFlyout.Items().Append(menuSeparator);
 
+        auto closeSubMenu = _AppendCloseMenuItems(contextMenuFlyout);
+        closeSubMenu.Items().Append(closePaneMenuItem);
+
         // GH#5750 - When the context menu is dismissed with ESC, toss the focus
         // back to our control.
         contextMenuFlyout.Closed([weakThis](auto&&, auto&&) {
@@ -1560,8 +1545,6 @@ namespace winrt::TerminalApp::implementation
                 }
             }
         });
-        auto closeSubMenu = _AppendCloseMenuItems(contextMenuFlyout);
-        closeSubMenu.Items().Append(closePaneMenuItem);
 
         TabViewItem().ContextFlyout(contextMenuFlyout);
     }
@@ -2001,13 +1984,6 @@ namespace winrt::TerminalApp::implementation
         actionAndArgs.Action(ShortcutAction::ExportBuffer);
         _dispatch.DoAction(*this, actionAndArgs);
     }
-    void TerminalTab::_moveTabToNewWindowClicked(const winrt::Windows::Foundation::IInspectable& /* sender */,
-                                                 const winrt::Windows::UI::Xaml::RoutedEventArgs& /* args */)
-    {
-        MoveTabArgs args{ winrt::to_hstring(NewWindow), MoveTabDirection::Forward };
-        ActionAndArgs actionAndArgs{ ShortcutAction::MoveTab, args };
-        _dispatch.DoAction(*this, actionAndArgs);
-    }
     void TerminalTab::_findClicked(const winrt::Windows::Foundation::IInspectable& /* sender */,
                                    const winrt::Windows::UI::Xaml::RoutedEventArgs& /* args */)
     {
diff --git a/src/cascadia/TerminalApp/TerminalTab.h b/src/cascadia/TerminalApp/TerminalTab.h
index f38895a3da6..5a2f67b295d 100644
--- a/src/cascadia/TerminalApp/TerminalTab.h
+++ b/src/cascadia/TerminalApp/TerminalTab.h
@@ -196,7 +196,6 @@ namespace winrt::TerminalApp::implementation
         void _splitTabClicked(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::RoutedEventArgs& e);
         void _closePaneClicked(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::RoutedEventArgs& e);
         void _exportTextClicked(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::RoutedEventArgs& e);
-        void _moveTabToNewWindowClicked(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::RoutedEventArgs& e);
         void _findClicked(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::UI::Xaml::RoutedEventArgs& e);
 
         void _bubbleRestartTerminalRequested(TerminalApp::TerminalPaneContent sender, const winrt::Windows::Foundation::IInspectable& args);


Test patch: