Skip to content

Commit 5f6a14e

Browse files
committed
A controller moving focus between tabs trips a fail-fast in WinUI's TabView::OnListViewGettingFocus. Add a GettingFocus handler on the TabViewItem that cancels that gamepad focus move before it reaches the framework handler.
1 parent 8fe6c21 commit 5f6a14e

2 files changed

Lines changed: 35 additions & 0 deletions

File tree

.github/actions/spelling/allow/microsoft.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,4 +78,5 @@ wtl
7878
wtt
7979
wttlog
8080
Xamarin
81+
XBOX
8182
xfgcheck

src/cascadia/TerminalApp/Tab.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,40 @@ namespace winrt::TerminalApp::implementation
162162
}
163163
});
164164

165+
// BODGY: Work around a fail-fast crash in WinUI 2's TabView. When a
166+
// game controller (e.g. an XBOX controller's d-pad/stick) moves focus
167+
// Up or Down between two TabViewItems, MUX's TabView::OnListViewGettingFocus
168+
// runs a GameController-only code path that calls
169+
// DispatcherQueue.TryEnqueue, which fails with E_INVALIDARG. That
170+
// unhandled error inside a GettingFocus callback makes XAML fail-fast,
171+
// and the whole window vanishes. See
172+
// microsoft-ui-xaml/blob/840c6cd4ac9d16d9aa99b24f0bd43997fe21bef8/dev/TabView/TabView.cpp#L246
173+
//
174+
// This GettingFocus handler is on the TabViewItem, which is closer to
175+
// the focus source than the TabViewListView that MUX's handler lives
176+
// on, so it runs first. Marking the event Handled - and cancelling the
177+
// focus move, which is exactly what MUX itself does for the keyboard
178+
// case - preempts MUX's broken handler. Keyboard navigation is left
179+
// untouched; MUX handles that path safely.
180+
TabViewItem().GettingFocus([](auto&&, const winrt::WUX::Input::GettingFocusEventArgs& args) {
181+
const auto direction{ args.Direction() };
182+
if (direction != winrt::WUX::Input::FocusNavigationDirection::Up &&
183+
direction != winrt::WUX::Input::FocusNavigationDirection::Down)
184+
{
185+
return;
186+
}
187+
if (args.InputDevice() != winrt::WUX::Input::FocusInputDeviceKind::GameController)
188+
{
189+
return;
190+
}
191+
if (args.OldFocusedElement().try_as<winrt::MUX::Controls::TabViewItem>() &&
192+
args.NewFocusedElement().try_as<winrt::MUX::Controls::TabViewItem>())
193+
{
194+
args.Cancel(true);
195+
args.Handled(true);
196+
}
197+
});
198+
165199
UpdateTitle();
166200
_RecalculateAndApplyTabColor();
167201
}

0 commit comments

Comments
 (0)