TermControl: rework input and focus handling#4748
TermControl: rework input and focus handling#4748DHowett-MSFT wants to merge 3 commits intomasterfrom
Conversation
This commit changes how we handle focus and tabstop-ness in TermControl and its descendent controls. The root of the issue that #4031 set out to investigate is that TermControl hosts a bunch of other controls, but it's using event tunneling to get first dibs on every bit of keyboard input sent its way -- even for things that live inside it. This required us to hack around and detemrine whether the input was intended for someone else before eventually ignoring it or using it. I believe the _correct_ thing for us to do is to direct user keyboard input to a control that lives logically "inside" the terminal surface and have that control hold focus and process keyboard events. Herein, we introduce an empty and invisible UserControl to hold focus inside the terminal surface. Everything I've tested works great here. It's important to note that the UserControl is invisible. Unfortunately, this means that it can't receive **mouse** input. Its parent, the SwapChainPanel acting as the terminal surface, can. I've gone through and unmarked things that should not show up in the tabstop list and changed the focus parameters for things that should. Doing this lets us kill TermControl's dependency on all private details in SearchBoxControl, including the bit where we could ask "do you think you should be focused?", by letting Xaml handle focus. We now listen to more focus events and funnel focus where we think it belongs. For SearchBoxControl, that means we focus the text field and continue to select all the text in it when the SearchBoxControl gets focus. Fixes #4031. This is an alternative solution to #4723, so I reverted that fix.
650b321 to
bfdce8f
Compare
There was a problem hiding this comment.
Tab Stop Issues:
- You changed a few tab stop things. They make sense. You can tab to a SwapChainPanel. But I still can't figure out how to tab to a search box. Honestly, it's ok to just remove the search box as a tab stop because the user can still access it via the search box keybinding.
Accessibility:
- This introduces a severe regression. For some reason, the
TermControlAutomationPeeris no longer being interacted with when theTermControlgets focus. This means that we are completely unable to access our Automation Providers and extract info from the text buffer. - What's weird is that the TCAP still appears in the tree. But there's no way to navigate to it...
| // Method Description: | ||
| // - Funnels focus from TermControl to its input control | ||
| // Arguments: | ||
| // - e: the event args for the focus event | ||
| void TermControl::OnGotFocus(const RoutedEventArgs& e) | ||
| { | ||
| if (e.OriginalSource() == *this) | ||
| { | ||
| // If we're about to get focus, funnel it into our input site. | ||
| TerminalInputTarget().Focus(FocusState::Programmatic); | ||
| } | ||
| } |
There was a problem hiding this comment.
So, this means that whenever the TermControl is attempted to get focus, we redirect focus to the TerminalInputTarget right? So you can't ever accidentally give focus to the search box?
There was a problem hiding this comment.
Also, just to be clear, this is called just before the _GotFocusHandler?
Should we consolidate styles and rename this function to OnGotFocusHandler?
There was a problem hiding this comment.
OnGotFocus is a special method name in that the UserControl composition class will call it if it's implemented. It must be named this: it is not a handler, it is not bound to an event.
There was a problem hiding this comment.
If the TermControl itself attempts to receive focus (not the controls inside it, not the search box, not anything else), we redirect focus to the input target.
Information in response to first comment. |
leonMSFT
left a comment
There was a problem hiding this comment.
Just a small question around SearchBoxControl::OnGotFocus. Although it seems like Carlos noticed some concerns around accessibility, so I'll hold my approval for now.
| void SearchBoxControl::OnGotFocus(/*const IInspectable& sender,*/ Windows::UI::Xaml::RoutedEventArgs const& args) | ||
| { | ||
| if (TextBox()) | ||
| if (args.OriginalSource() == *this) |
There was a problem hiding this comment.
Just to see if I'm understanding this correctly, this condition returns true only if the SearchBoxControl directly gained focus (probably from TermControl)? I'm guessing if the user clicked on the SearchBoxControl::TextBox() or its many Toggle-able buttons, the OriginalSource would be the TextBox or the buttons, so it wouldn't satisfy this condition?
There was a problem hiding this comment.
Indeed! I wanted to maintain the behavior that we had, where the searchbox didn't re-select all its text when the textbox was re-focused, but did when we entered the control for the first time. 😄
zadjii-msft
left a comment
There was a problem hiding this comment.
This seems much more manageable than before. I trust Carlos to make sure his concerns about the accessibility actually get sorted out before he signs off
|
This one's a party of Leon, Mike, and Carlos, so I'm skipping this one intentionally. |
|
Hey I'm gonna mark this as "needs author feedback" - I'm not sure how to resolve the accessibility concerns Carlos had, or if they're still reproing, but maybe we should loop back on this, esp with #6680 |
|
This pull request has been automatically marked as stale because it has been marked as requiring author feedback but has not had any activity for 7 days. It will be closed if no further activity occurs within 7 days of this comment. |
This commit changes how we handle focus and tabstop-ness in TermControl
and its descendent controls.
The root of the issue that #4031 set out to investigate is that
TermControl hosts a bunch of other controls, but it's using event
tunneling to get first dibs on every bit of keyboard input sent its way
-- even for things that live inside it. This required us to hack around
and detemrine whether the input was intended for someone else before
eventually ignoring it or using it.
I believe the correct thing for us to do is to direct user keyboard
input to a control that lives logically "inside" the terminal surface
and have that control hold focus and process keyboard events.
Herein, we introduce an empty and invisible UserControl to hold focus
inside the terminal surface. Everything I've tested works great here.
It's important to note that the UserControl is invisible. Unfortunately,
this means that it can't receive mouse input. Its parent, the
SwapChainPanel acting as the terminal surface, can.
I've gone through and unmarked things that should not show up in the
tabstop list and changed the focus parameters for things that should.
Doing this lets us kill TermControl's dependency on all private details
in SearchBoxControl, including the bit where we could ask "do you think
you should be focused?", by letting Xaml handle focus. We now listen to
more focus events and funnel focus where we think it belongs. For
SearchBoxControl, that means we focus the text field and continue to
select all the text in it when the SearchBoxControl gets focus.
Fixes #4031.
This is an alternative solution to #4723, so I reverted that fix.
PR Checklist