Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make command palette ephemeral #8377

Merged
11 commits merged into from
Dec 9, 2020
Merged

Conversation

Don-Vito
Copy link
Contributor

@Don-Vito Don-Vito commented Nov 23, 2020

So the implementation is somewhat dirty.
The ideas was nice - add lostFocusHandler

However it broke few things:

  • In the TabSwitcher the ListItem must be focusable since otherwise
    the SingleSelectionMode behavior breaks.
    To address this I had to put the lostFocusHandler on the items as well

  • When you click the flyout, the palette loses focus,
    which makes the terminal page to set the focus on the tab, closing the flyout.
    To address this I had to ensure the tab won't get focused once the flyout is open.
    In addition, flyout should fix the focus before opening,
    otherwise alt+tab will put a focus on a tab row rather than on tab

  • I also had to close the palette if the tab order changes.
    To prevent inconsistencies.

Closes #8355

@ghost ghost added Area-UserInterface Issues pertaining to the user interface of the Console or Terminal Issue-Task It's a feature request, but it doesn't really need a major design. Product-Terminal The new Windows Terminal. labels Nov 23, 2020
@Don-Vito Don-Vito marked this pull request as draft November 23, 2020 18:47
@Don-Vito Don-Vito marked this pull request as ready for review November 29, 2020 13:36
@zadjii-msft zadjii-msft self-assigned this Nov 30, 2020
Copy link
Member

@zadjii-msft zadjii-msft left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've got a couple questions and spelling nits. I've checked out the branch and played with it and it feels fine to me, so I'm okay with this. I just want to make sure the TODOs shouldn't be "TODONEs"

}

// Method Description:
// - The purpose of this event handler is to hind the palette if it loses focus.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// - The purpose of this event handler is to hind the palette if it loses focus.
// - The purpose of this event handler is to hide the palette if it loses focus.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess I was drunk or something 😕

// We cannot do it on closing because if the window loses focus (alt+tab)
// the closing event is not fired.
// It is important to set the focus on the tab
// Sine the previous focus location might be discarded in the background,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Sine the previous focus location might be discarded in the background,
// Since the previous focus location might be discarded in the background,

// Return focus to the active control
if (auto index{ _GetFocusedTabIndex() })
// We don't want to set focus on the tab if fly-out is open as it will be closed
// TODO: consider checking we are not in the opening state, by hooking both Opening and Open events
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to address this TODO during this review?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zadjii-msft - don't think so - its highly theoretical.

// Since the focus might move within the palette we need to register this handler for all focusable children
// Right now this is achieved by explicitly registering on the root on the highlighted text controls.
// Few notes:
// - found no way to entires disable focus on list-view elements in single-item-selection mode without breaking list behavior
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// - found no way to entires disable focus on list-view elements in single-item-selection mode without breaking list behavior
// - found no way to entirely disable focus on list-view elements in single-item-selection mode without breaking list behavior

(I think?)

// Right now this is achieved by explicitly registering on the root on the highlighted text controls.
// Few notes:
// - found no way to entires disable focus on list-view elements in single-item-selection mode without breaking list behavior
// - TODO: the explicit implementation can be generalized by checking if the focused element is a descendant of palette
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to address this TODO? Are there any drawbacks from the current approach? Like, will he manually have to add this _lostFocusHandler to all the focusable UI elements in the cmdpal?

Copy link
Contributor Author

@Don-Vito Don-Vito Nov 30, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As you said, the only drawback of the current approach is that we add another field (that might get focus) we need to remember to add it here. On the other hand this solution is simple (funny stuff is that Xaml UI guys have implemented similar function but do not export it - seen it occasionally, trying to understand if we can bind the tabview in any case).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay. That's definitely annoying to remember, but hopefully we'll remember to do this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay. That's definitely annoying to remember, but hopefully we'll remember to do this.

IT IS NOW FIXED HA HA HA

@zadjii-msft zadjii-msft assigned Don-Vito and unassigned zadjii-msft Nov 30, 2020
@zadjii-msft zadjii-msft added the Needs-Author-Feedback The original author of the issue/PR needs to come back and respond to something label Nov 30, 2020
@ghost ghost removed the Needs-Author-Feedback The original author of the issue/PR needs to come back and respond to something label Nov 30, 2020
@Don-Vito
Copy link
Contributor Author

I've got a couple questions and spelling nits. I've checked out the branch and played with it and it feels fine to me, so I'm okay with this. I just want to make sure the TODOs shouldn't be "TODONEs"

@zadjii-msft - thanks for the review! And sorry for all misspellings (I guess it is an hour of the day.. or of the night.. that I start to err the way that even spellchecker cannot stop me). I am not sure if we need to implement TODOs now.. as both are somewhat hypothetical.. I can absolutely work on them if you find it is a correct timing 😊

@Don-Vito Don-Vito requested a review from zadjii-msft December 1, 2020 22:38
@zadjii-msft zadjii-msft self-assigned this Dec 1, 2020
@Don-Vito
Copy link
Contributor Author

Don-Vito commented Dec 8, 2020

@zadjii-msft - a week has passed, so bumping 😄

Copy link
Member

@zadjii-msft zadjii-msft left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm okay with this, mostly on the premise that it works well when I played with it. I really don't love having to remember to add that event handler each time manually, but we can live with it. And If we can't live with ti, then we've at least got a plan.

My only real nit is that we don't usually allow bare TODOs in the code, so I'm suggesting that we just link these to the bullet points I added in #5400. They don't really deserve a whole issue, but best to have them enumerated somewhere.

// Right now this is achieved by explicitly registering on the root on the highlighted text controls.
// Few notes:
// - found no way to entires disable focus on list-view elements in single-item-selection mode without breaking list behavior
// - TODO: the explicit implementation can be generalized by checking if the focused element is a descendant of palette
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay. That's definitely annoying to remember, but hopefully we'll remember to do this.

src/cascadia/TerminalApp/CommandPalette.cpp Outdated Show resolved Hide resolved
src/cascadia/TerminalApp/TerminalPage.cpp Outdated Show resolved Hide resolved
@zadjii-msft zadjii-msft added the Needs-Second It's a PR that needs another sign-off label Dec 8, 2020
@zadjii-msft
Copy link
Member

(I'm throwing Needs-Second on this, but I'd bet that @DHowett would want to take a look)

@ghost ghost added the Area-CmdPal Command Palette issues and features label Dec 8, 2020
Comment on lines 457 to 464
void CommandPalette::_lostFocusHandler(Windows::Foundation::IInspectable const& /* sender*/,
Windows::UI::Xaml::RoutedEventArgs const& /*args*/)
{
auto focusedElement = Input::FocusManager::GetFocusedElement(this->XamlRoot()).try_as<FrameworkElement>();
if (focusedElement && focusedElement != *this && focusedElement.Name() != L"_highlightedTextControl" && Visibility() != Visibility::Collapsed)
{
_dismissPalette();
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've seen some strange things with the Focus event that make me feel like this should be easier.

If you add a LostFocus handler on the root control, and check the sender parameter instead of GetFocusedElement, you might find that you're getting LostFocus event fired for all contained controls. (I am not certain about this, but it is a routed event!) That would make this easier: use VisualTreeHelper to walk the control's parent to see if it is us or is within us.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@DHowett - Yeah.. I have a code with VisualTreeHelper iterating the parents. This is how I started, but putting the lost focus on the root of the palette was not sufficient. Are you suggesting to put in on TerminalPage?

Copy link
Contributor Author

@Don-Vito Don-Vito Dec 8, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also from the documentation of Lost Focus Event:

Specific Windows Runtime controls may have class-based handling for the LostFocus event. If so, the control probably has an override for the method OnLostFocus. Typically the event is marked handled by the class handler, and the LostFocus event is not raised for handling by any user code handlers on that control. Controls might handle the event in order to unload a visual state that displayed a focus rectangle in response to OnGotFocus.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can check if we can do something with LosingFocus event that won't probably get swallowed the way LostFocus does

Copy link
Member

@DHowett DHowett left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking to have out the discussion about LostFocus -- not totally sure -- otherwise I like this! 😄

@ghost ghost added Needs-Author-Feedback The original author of the issue/PR needs to come back and respond to something and removed Needs-Author-Feedback The original author of the issue/PR needs to come back and respond to something labels Dec 8, 2020
@DHowett
Copy link
Member

DHowett commented Dec 8, 2020

Ah. Don't do any work to investigate that -- I don't want you to waste your effort. After we get through this spec review meeting I'll come back to tap this PR. 😄

@Don-Vito
Copy link
Contributor Author

Don-Vito commented Dec 8, 2020

Ah. Don't do any work to investigate that -- I don't want you to waste your effort. After we get through this spec review meeting I'll come back to tap this PR. 😄

Too late - already playing with this 😄

@Don-Vito
Copy link
Contributor Author

Don-Vito commented Dec 8, 2020

So.. LosingFocus seems to catch everything:

void CommandPalette::_losingFocusHandler(Windows::Foundation::IInspectable const& /*sender*/,
                                           Windows::UI::Xaml::Input::LosingFocusEventArgs const& args)
    {
        auto element = args.NewFocusedElement();
        while (element)
        {
            if (element == *this)
            {
                return;
            }

            element = winrt::Windows::UI::Xaml::Media::VisualTreeHelper::GetParent(element);
        }

        _dismissPalette();
    }

The problem I believe is that _dismissPalette, changes the visibility of the palette, that invokes a property changed handler in terminal page, that sets focus, that causes corruption 😄

@Don-Vito
Copy link
Contributor Author

Don-Vito commented Dec 8, 2020

So it seems that lostFocusHandler does capture all events (I cannot reproduce why it didn't work for me earlier), but sender is not good enough, it is always ourselves. So i am submitting something in between:

void CommandPalette::_lostFocusHandler(Windows::Foundation::IInspectable const& /*sender*/,
                                           Windows::UI::Xaml::RoutedEventArgs const& /*args*/)
    {        
        auto focusedElement = Input::FocusManager::GetFocusedElement(this->XamlRoot()).try_as<DependencyObject>();        
        while (focusedElement)
        {
            if (focusedElement == *this)
            {
                return;
            }

            focusedElement = winrt::Windows::UI::Xaml::Media::VisualTreeHelper::GetParent(focusedElement);
        }

        _dismissPalette();
    }

Comment on lines 454 to 461
while (focusedElement)
{
if (focusedElement == *this)
{
return;
}

focusedElement = winrt::Windows::UI::Xaml::Media::VisualTreeHelper::GetParent(focusedElement);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

k so just so I understand: If the thing losing focus is inside us or is us, we will return here and keep the palette open.

I don't totally understand why this works as described 😄 because if we aren't reacting to the command palette itself or any of its descendants losing focus and dismissing, when ARE we dismissing?

Copy link
Contributor Author

@Don-Vito Don-Vito Dec 9, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@DHowett - I mean you were right, the lost focus event is getting routed to us if we or one of our children loses focus. We only need to distinguish if it loses the focus in favor of some other our child or some external element.

We cannot achieve this with the sender, because it is always the element that loses focus, while we are interested in element that gets focus.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess I will issue another commit explaining this 😊

@Don-Vito Don-Vito requested a review from DHowett December 9, 2020 08:50
Copy link
Member

@DHowett DHowett left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, of course, it makes sense. Not sender. "Is the thing that now has focus inside us".

Thanks!

@DHowett DHowett added the AutoMerge Marked for automatic merge by the bot when requirements are met label Dec 9, 2020
@ghost
Copy link

ghost commented Dec 9, 2020

Hello @DHowett!

Because this pull request has the AutoMerge label, I will be glad to assist with helping to merge this pull request once all check-in policies pass.

p.s. you can customize the way I help with merging this pull request, such as holding this pull request until a specific person approves. Simply @mention me (@msftbot) and give me an instruction to get started! Learn more here.

@ghost ghost merged commit e1a8657 into microsoft:main Dec 9, 2020
@ghost
Copy link

ghost commented Jan 28, 2021

🎉Windows Terminal Preview v1.6.10272.0 has been released which incorporates this pull request.:tada:

Handy links:

@ghost ghost mentioned this pull request Jan 28, 2021
mpela81 pushed a commit to mpela81/terminal that referenced this pull request Jan 28, 2021
So the implementation is somewhat dirty.
The ideas was nice - add lostFocusHandler

However it broke few things:
* In the TabSwitcher the ListItem must be focusable since otherwise 
the SingleSelectionMode behavior breaks. 
To address this I had to put the lostFocusHandler on the items as well
 
* When you click the flyout, the palette loses focus, 
which makes the terminal page to set the focus on the tab, closing the flyout. 
To address this I had to ensure the tab won't get focused once the flyout is open.
In addition, flyout should fix the focus before opening, 
otherwise alt+tab will put a focus on a tab row rather than on tab

* I also had to close the palette if the tab order changes.
To prevent inconsistencies.

Closes microsoft#8355
ghost pushed a commit that referenced this pull request Feb 17, 2021
Fixes a regression of command palette accessibility. The regression was
introduced in #8377 by setting `IsTabStop` to false. Though the commands
would light up, the focus didn't technically get on the command, so the
screen reader would just read the text box.

## Validation Steps Performed
Opened the command palette while NVDA is active. It now reads the
commands as focus moves on them.
DHowett pushed a commit that referenced this pull request Feb 23, 2021
Fixes a regression of command palette accessibility. The regression was
introduced in #8377 by setting `IsTabStop` to false. Though the commands
would light up, the focus didn't technically get on the command, so the
screen reader would just read the text box.

Opened the command palette while NVDA is active. It now reads the
commands as focus moves on them.

(cherry picked from commit ac3e4bf)
This pull request was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-CmdPal Command Palette issues and features Area-UserInterface Issues pertaining to the user interface of the Console or Terminal AutoMerge Marked for automatic merge by the bot when requirements are met Issue-Task It's a feature request, but it doesn't really need a major design. Needs-Second It's a PR that needs another sign-off Product-Terminal The new Windows Terminal.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Close command palette on focus loss
3 participants