Skip to content

Allow tab reordering in elevated windows via pointer-driven drag#20278

Open
jreverett wants to merge 1 commit into
microsoft:mainfrom
jreverett:elevated-tab-reorder
Open

Allow tab reordering in elevated windows via pointer-driven drag#20278
jreverett wants to merge 1 commit into
microsoft:mainfrom
jreverett:elevated-tab-reorder

Conversation

@jreverett

@jreverett jreverett commented May 30, 2026

Copy link
Copy Markdown

Summary of the Pull Request

Enables reordering tabs by dragging them when Windows Terminal is running elevated (as administrator). Today, dragging a tab in an elevated window does nothing, because the OS modern drag/drop service can't operate across integrity levels, so the TabView's drag/reorder is disabled. This adds a small, self-contained, pointer-driven reorder that only activates when the native drag/drop path is unavailable.

References and Relevant Issues

Addresses the in-strip tab-reorder portion of #6661.

This intentionally does not attempt to restore tab tear-out (dragging a tab out into its own window). Tear-out genuinely depends on the OS drag/drop service that is unavailable to elevated processes, so it remains disabled when elevated. The scope here is in-strip reordering only.

Note for maintainers: I see this area is marked External-Blocked-WinUI3. I've kept this deliberately small — fully gated behind !CanDragDrop(), additive only (no existing lines changed), reusing the existing _TryMoveTab primitive, and trivially removable once a WinUI 3 migration restores native elevated drag/drop. It's intended as a low-risk stopgap for the current System XAML + WinUI 2 stack while that's pending, not a competing direction. Happy to adjust the approach based on your guidance.

Detailed Description of the Pull Request / Additional comments

Background

When Terminal runs elevated, Utils::CanUwpDragDrop() returns false (it already special-cases a fully UAC-disabled session, where there is no integrity separation and native drag still works). TerminalPage propagates this through CanDragDrop() and sets TabView.CanReorderTabs / CanDragTabs accordingly. So in a normal elevated session both are false and tabs can't be dragged at all. This is the long-standing workaround for the cross-integrity-level drag/drop crash.

Approach

Rather than re-enabling the native (crashing) path, this drives reorder directly from raw pointer events — the same idea as the existing Move tab forward/backward actions, which already reorder without the drag/drop service:

  • Each TabViewItem gets a PointerMoved handler (_OnTabPointerMoved), registered only when CanDragDrop() is false, so non-elevated sessions are completely unaffected (the handler isn't even attached).
  • On a left-button drag past a small threshold, it captures the pointer and runs a custom reorder:
    • The dragged tab follows the pointer via a TranslateTransform, clamped to the tab-strip bounds so it can't slide off-screen.
    • Sibling tabs shift aside to preview the drop location.
    • The actual reorder is committed once on release (PointerCaptureLost) using the existing _TryMoveTab primitive.
  • Left-button PointerPressed is consumed by TabViewItem for selection, so the drag begins from PointerMoved rather than PointerPressed. Single-click selection and middle-click-close are unaffected.

Why this is safe

  • It never invokes the OS drag/drop service, so the original cross-integrity crash cannot recur. CanReorderTabs / CanDragTabs stay false when elevated.
  • It is gated entirely on !CanDragDrop(), so it is inert wherever native drag already works.
  • The diff is additive only (no existing lines changed) and reuses existing reorder logic (_TryMoveTab).

Validation Steps Performed

Demo — reordering tabs in an elevated window:

MoveTabsTerminalAdmin.mp4

Built and deployed the dev package and tested in an elevated window:

  • Dragging a tab horizontally reorders it; the dragged tab follows the cursor and siblings shift to preview the drop.
  • Dragging past the first/last tab clamps at the strip edge instead of sliding out of view.
  • Single left-click still selects a tab; middle-click still closes a tab.
  • Opening new tabs works (no crash).

In a non-elevated window:

  • Tab drag/reorder and tear-out continue to use the native path, unchanged (the handler isn't registered when CanDragDrop() is true).

Automated tests: none added. This is a pointer- and layout-geometry-driven interaction (TransformToVisual, ActualWidth, pointer capture) that the TAEF LocalTests_TerminalApp host can't meaningfully exercise (no live visual layout), and the suite has no existing drag/reorder tests to extend. Validated manually as above.

PR Checklist

  • Closes #xxx — partially addresses Figure out how to handle drag-drop in an elevated session #6661 (in-strip reorder only; not tear-out)
  • Tests added/passed — see note above; interaction not unit-testable in TAEF
  • Documentation updated — n/a (no new setting/action/UI surface)
  • Schema updated (if necessary) — n/a

When Terminal runs elevated the OS modern drag/drop service is unavailable across integrity levels, so CanDragDrop() is false and the TabView's CanReorderTabs/CanDragTabs are disabled. As a result tabs cannot be rearranged by dragging in an elevated window.

This adds a pointer-driven reorder that activates only when CanDragDrop() is false, leaving the native drag/drop path untouched otherwise. Each TabViewItem gets a PointerMoved handler that, on a left-button drag, captures the pointer, floats the dragged tab via a TranslateTransform, slides sibling tabs aside to preview the drop, and commits the move once on release using the existing _TryMoveTab primitive (the same one behind the Move Tab actions). The dragged tab is clamped to the tab strip bounds.

Tearing a tab out into a separate window still requires the OS drag/drop service and remains unavailable when elevated.

Addresses microsoft#6661.
@jreverett

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

@jreverett jreverett marked this pull request as ready for review May 30, 2026 00:27
@carlos-zamora

Copy link
Copy Markdown
Member

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

@carlos-zamora

Copy link
Copy Markdown
Member

Tested locally. It works! I noticed a subtle difference in elevated sessions though. You can only move along the rail and tab tear-off is not supported. I think that's fine though, but wanted to make note of it.

@jreverett

Copy link
Copy Markdown
Author

Tested locally. It works! I noticed a subtle difference in elevated sessions though. You can only move along the rail and tab tear-off is not supported. I think that's fine though, but wanted to make note of it.

Great! Yes lack of tear-off is noted in the PR description, I couldn't find a way around that and I think that really will need to wait for WinUI3 integration to be supported. The reordering along rails is what is really needed though (at least in my opinion), the current behaviour of having to right click and reorder the tabs gets annoying quickly

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants